Closed68

ラズパイ入門記録(出力編)

(love cat)(love cat)

多分そのうち飽きて、またいつか始めるのでその時のために備忘

(love cat)(love cat)
sudo snap install rpi-imager

マイクロSDへのOSの書き込みやwifi設定はrpi-imagerで行った。
簡単、便利。

(love cat)(love cat)

初めて知ったワード
ブレッドボード:電子装置のプロトタイピングの構築基盤。差し込んだりするだけで簡単電子工作。
GPIO:General-Purpose Input/Output。拡張ボードでブレッドボードに引き出せる。
GPIO拡張ボード:GPIOを拡張したボード。各ピンに名前がついており、覚えられない。言語やライブラリによって各ピンの呼び方が違うみたい。

(love cat)(love cat)

まず220Ωの抵抗器を陽極(LEDの長いピン)に接続し、それから抵抗器を3.3 Vの電源に接続し、LEDの陰極(短いピン)をRaspberry PiのGPIO17に接続する。したがって、LEDをオンにするには、GPIO17を低(0V)レベルにする必要がある。

いきなり全然わからない。

(love cat)(love cat)

LEDは、長いピンが正極に接続され、もう一方が負極に接続されている場合に点灯する。直接電源に接続すると破損の危険があるので、160Ω以上の抵抗を直列に接続すること。

(love cat)(love cat)

Rustでの操作にはrppalというのがいいのだろうか。
現在の最新バージョンは0.13.1。
ピンの指定はBCM。

(love cat)(love cat)

Rustで操作するのは隙あらばRust使って練習していきたい気分なだけでそれ以上の理由はない。

(love cat)(love cat)

PWM:パルス幅変調。デジタル手段でアナログ結果を取得するための技術。信号がオンである時間とオフである時間を長くしたり短くしたりする方式。

RGB LEDで遊ぶ。RGB LEDの色は輝度によって変化し、輝度はPWMで調整可能。
Raspberry PiのハードウェアPWM出力用チャネルが1つだけなので、3つのチャネルを必要とするRGB LEDの制御は本来難しいが、softPwmライブラリを使えばできるらしい。

(love cat)(love cat)

これがLチカか。楽しい。

pub fn rgb_led() -> Result<(), Box<dyn Error>> {
    const COLOR_FLAGS: [u64; 7] = [0b000, 0b100, 0b010, 0b001, 0b110, 0b101, 0b011];
    const GPIO_LED_RED: u8 = 17;
    const GPIO_LED_GREEN: u8 = 18;
    const GPIO_LED_BLUE: u8 = 27;

    const DURATION: u64 = 100 * 100;

    let mut pin1 = Gpio::new()?.get(GPIO_LED_RED)?.into_output();
    let mut pin2 = Gpio::new()?.get(GPIO_LED_GREEN)?.into_output();
    let mut pin3 = Gpio::new()?.get(GPIO_LED_BLUE)?.into_output();

    let light_led = |pin: &mut OutputPin, palse_width: u64| {
        pin.set_pwm(
            Duration::from_micros(DURATION),
            Duration::from_micros(palse_width),
        )
    };
    for flags in COLOR_FLAGS {
        light_led(&mut pin1, (flags & 0b100) * 100)?;
        light_led(&mut pin2, (flags & 0b010) * 100)?;
        light_led(&mut pin3, (flags & 0b001) * 100)?;
        thread::sleep(Duration::from_millis(500));
    }
    Ok(())
}

(love cat)(love cat)

74HC595について簡単に理解したい。
https://synapse.kyoto/glossary/74hc595/page001.html

(love cat)(love cat)
pub fn segment7() -> Result<(), Box<dyn Error>> {
    let turn_high_and_low = |pin: &mut OutputPin, duration: Duration| {
        pin.set_high();
        thread::sleep(duration);
        pin.set_low();
    };
    const SEG_CODE: [u8; 16] = [
        0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79,
        0x71,
    ];

    const GPO17: u8 = 17;
    const GPO18: u8 = 18;
    const GPO27: u8 = 27;

    let mut pin_sdi = Gpio::new()?.get(GPO17)?.into_output();
    let mut pin_rclk = Gpio::new()?.get(GPO18)?.into_output();
    let mut pin_srclk = Gpio::new()?.get(GPO27)?.into_output();

    pin_sdi.set_low();
    pin_rclk.set_low();
    pin_srclk.set_low();

    for code in SEG_CODE {
        for i in 0..8 {
            if 0x80 & (code << i) != 0 {
                pin_sdi.set_high();
            } else {
                pin_sdi.set_low();
            }
            turn_high_and_low(&mut pin_srclk, Duration::from_millis(1));
        }
        turn_high_and_low(&mut pin_rclk, Duration::from_millis(1000));
    }
    pin_sdi.set_low();
    for _ in 0..8 {
        turn_high_and_low(&mut pin_srclk, Duration::from_millis(1));
    }
    turn_high_and_low(&mut pin_rclk, Duration::from_millis(0));
    Ok(())
}
(love cat)(love cat)

次は4桁の7セグメントディスプレイをチカチカさせる。
今更だがアノードコモンとカソードコモンについてググる。
アノードはLEDのプラス側(足が長い方)、カソードはLEDのマイナス側。
つまりアノードコモンはプラス側を共有し、マイナス側の電流をLOWにすることで制御する。
カソードコモンはその逆。

(love cat)(love cat)

ピンの定数とturn_high_and_low関数(もっとマトモな名前があるはず)は外に追い出した。
マニュアルのサンプルコードと同様、タイマーを使ってカウントアップしていくつもり。
timer使うの初めてなのでとりあえずちゃんとカウントアップしていくか練習。
guard関数とかはtimerのドキュメントのほぼ丸写し。

pub fn four_digit_segment7() -> Result<(), Box<dyn Error>> {
    let timer = timer::Timer::new();
    let count = Arc::new(Mutex::new(0));

    let guard = {
        let count = count.clone();
        timer.schedule_repeating(chrono::Duration::seconds(1), move || {
            *count.lock().unwrap() += 1;
        })
    };

    let seg_code: [u8; 10] = [0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90];

    let mut pin_sdi = Gpio::new()?.get(GPIO24)?.into_output();
    let mut pin_rclk = Gpio::new()?.get(GPIO23)?.into_output();
    let mut pin_srclk = Gpio::new()?.get(GPIO18)?.into_output();

    let mut place_pins: [OutputPin; 4] = [
        Gpio::new()?.get(SPIMOSI)?.into_output(),
        Gpio::new()?.get(GPIO22)?.into_output(),
        Gpio::new()?.get(GPIO27)?.into_output(),
        Gpio::new()?.get(GPIO17)?.into_output(),
    ];

    for p in &mut place_pins {
        p.set_high();
    }

    for code in seg_code {
        for i in 0..8 {
            if 0x80 & (code << i) != 0 {
                pin_sdi.set_high();
            } else {
                pin_sdi.set_low();
            }
            turn_high_and_low(&mut pin_srclk, Duration::from_millis(1));
        }
        turn_high_and_low(&mut pin_rclk, Duration::from_millis(1000));
        println!("{:?}", count);
    }
    pin_sdi.set_high();
    for _ in 0..8 {
        turn_high_and_low(&mut pin_srclk, Duration::from_millis(1));
    }
    turn_high_and_low(&mut pin_rclk, Duration::from_millis(0));

    Ok(())
}
(love cat)(love cat)

1桁目に2、2桁目に1、3桁目に0、4桁目は3、みたいな感じでそれぞれの桁に表示したい数字を一瞬だけ光らせる操作を高速に行うことによって全部の桁がずっと光っているように見せる。
見ていて楽しいのは100msで1カウントアップぐらいかなあ。

fn clear_display(sdi: &mut OutputPin, rclk: &mut OutputPin, srclk: &mut OutputPin, is_anode: bool) {
    if is_anode {
        sdi.set_low();
    } else {
        sdi.set_high();
    }
    for _ in 0..8 {
        turn_high_and_low(srclk, Duration::from_millis(0));
    }
    turn_high_and_low(rclk, Duration::from_millis(0));
}

fn hc595_shift(sdi: &mut OutputPin, rclk: &mut OutputPin, srclk: &mut OutputPin, code: u8) {
    for i in 0..8 {
        if 0x80 & (code << i) != 0 {
            sdi.set_high();
        } else {
            sdi.set_low();
        }
        turn_high_and_low(srclk, Duration::from_millis(0));
    }
    turn_high_and_low(rclk, Duration::from_millis(0));
}

pub fn four_digit_segment7() -> Result<(), Box<dyn Error>> {
    let timer = timer::Timer::new();
    let count = Arc::new(Mutex::new(0));

    let guard = {
        let count = count.clone();
        timer.schedule_repeating(chrono::Duration::milliseconds(100), move || {
            *count.lock().unwrap() += 1;
        })
    };

    let seg_code: [u8; 10] = [0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90];

    let mut pin_sdi = Gpio::new()?.get(GPIO24)?.into_output();
    let mut pin_rclk = Gpio::new()?.get(GPIO23)?.into_output();
    let mut pin_srclk = Gpio::new()?.get(GPIO18)?.into_output();

    let mut place_pins: [OutputPin; 4] = [
        Gpio::new()?.get(SPIMOSI)?.into_output(),
        Gpio::new()?.get(GPIO22)?.into_output(),
        Gpio::new()?.get(GPIO27)?.into_output(),
        Gpio::new()?.get(GPIO17)?.into_output(),
    ];

    let mut pick_digit = |digit: usize| {
        for p in &mut place_pins {
            p.set_low();
        }
        place_pins[digit].set_high();
    };

    let mut light_1digit = |count: usize, digit: usize| {
        let base: i32 = 10;
        clear_display(&mut pin_sdi, &mut pin_rclk, &mut pin_srclk, false);
        pick_digit(digit);
        hc595_shift(
            &mut pin_sdi,
            &mut pin_rclk,
            &mut pin_srclk,
            seg_code[count / (base.pow(digit as u32) as usize) % 10],
        );
    };

    while *count.lock().unwrap() < 10000 {
        light_1digit(*count.lock().unwrap(), 0);
        light_1digit(*count.lock().unwrap(), 1);
        light_1digit(*count.lock().unwrap(), 2);
        light_1digit(*count.lock().unwrap(), 3);
    }
    clear_display(&mut pin_sdi, &mut pin_rclk, &mut pin_srclk, false);
    drop(guard);
    Ok(())
}
(love cat)(love cat)

次はLEDドットマトリクス。
https://docs.sunfounder.com/projects/davinci-kit/ja/latest/1.1.6_led_dot_matrix.html
光らせるだけなのはちょっと飽きてきたが、74HC595を2つ使ったりよい練習になるだろう。

(love cat)(love cat)

抵抗はあったほうが良さそうな気がする。ちゃんとした根拠はないが。
それとも8本並列になるから電流恐るるに足らず、ということなのだろうか。
とりあえずサンプルの回路に気の向くまま抵抗を配置し、光り具合で考えることにする。

(love cat)(love cat)

求めていた情報だ。
https://eman-physics.net/circuit/led.html

もっと多くの LED を点灯させたい場合には並列にするしかないわけだが,この時,並列する線の一個一個に抵抗を付ける必要がある.豆電球とは違って,並列した LED の全てに電流が均等に分かれてくれないからだ.

(love cat)(love cat)

これまで入力1つに対して220Ωの抵抗1個とLED1個で光らせたりしていたわけで、1個1個につけなくても良い気がする。

(love cat)(love cat)

GPIO17、GPIO18、GPIO27からの入力にそれぞれ220Ωの抵抗を付けてみた。
サンプルプログラムでの動作は確認できた。特に抵抗の有無で明るさが変わったりはしていない気がする。
まあ…回路についてはとりあえずいいか。
どう光らせているのか理解しRustで書く。

(love cat)(love cat)

冷静になってみると74HC595の制御に抵抗があっても意味ない気がする。
じゃあやっぱ8個抵抗を付けるかというと面倒…。
VCCやGNDに付けたらOKだったりするか?
今のところ壊れないで光ってくれているので今後理解したいこととしておく。

(love cat)(love cat)

「1つ目の74HC595の最後の出力が2つ目の74HC595のSDIなの頭こんがらがるな~」
など考えてこんがらがっていたけど、単純に1つ目で溢れたやつが2つ目にシフトしていくだけか。

(love cat)(love cat)

というわけでサンプルコードのRust版。ほぼそのままだ。

fn hc595_in(sdi: &mut OutputPin, srclk: &mut OutputPin, code: u8) {
    for i in 0..8 {
        if 0x80 & (code << i) != 0 {
            sdi.set_high();
        } else {
            sdi.set_low();
        }
        turn_high_and_low(srclk, Duration::from_millis(0));
    }
}

fn hc595_out(rclk: &mut OutputPin) {
    turn_high_and_low(rclk, Duration::from_millis(0));
}

pub fn light_led_dot_matrix() -> Result<(), Box<dyn Error>> {
    let code_h: [u8; 20] = [
        0x01, 0xff, 0x80, 0xff, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0xff, 0xff, 0xff,
        0xff, 0xff, 0xff, 0xff, 0xff,
    ];
    let code_l: [u8; 20] = [
        0x00, 0x7f, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfd, 0xfb,
        0xf7, 0xef, 0xdf, 0xbf, 0x7f,
    ];
    let mut pin_sdi = Gpio::new()?.get(GPIO17)?.into_output();
    let mut pin_rclk = Gpio::new()?.get(GPIO18)?.into_output();
    let mut pin_srclk = Gpio::new()?.get(GPIO27)?.into_output();
    loop {
        for i in 0..code_h.len() {
            hc595_in(&mut pin_sdi, &mut pin_srclk, code_l[i]);
            hc595_in(&mut pin_sdi, &mut pin_srclk, code_h[i]);
            hc595_out(&mut pin_rclk);
            thread::sleep(Duration::from_millis(100));
        }
        for i in (0..code_h.len()).rev() {
            hc595_in(&mut pin_sdi, &mut pin_srclk, code_l[i]);
            hc595_in(&mut pin_sdi, &mut pin_srclk, code_h[i]);
            hc595_out(&mut pin_rclk);
            thread::sleep(Duration::from_millis(100));
        }
    }
    Ok(())
}
(love cat)(love cat)

ctrlcで消灯して終了するように。

pub fn light_led_dot_matrix() -> Result<(), Box<dyn Error>> {
    let code_h: [u8; 20] = [
        0x01, 0xff, 0x80, 0xff, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0xff, 0xff, 0xff,
        0xff, 0xff, 0xff, 0xff, 0xff,
    ];
    let code_l: [u8; 20] = [
        0x00, 0x7f, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfd, 0xfb,
        0xf7, 0xef, 0xdf, 0xbf, 0x7f,
    ];
    let mut pin_sdi = Gpio::new()?.get(GPIO17)?.into_output();
    let mut pin_rclk = Gpio::new()?.get(GPIO18)?.into_output();
    let mut pin_srclk = Gpio::new()?.get(GPIO27)?.into_output();

    let running = Arc::new(AtomicBool::new(true));
    let r = running.clone();
    ctrlc::set_handler(move || {
        r.store(false, Ordering::SeqCst);
    })
    .expect("Error setting Ctrl-C handler");
    while running.load(Ordering::SeqCst) {
        for i in 0..code_h.len() {
            hc595_in(&mut pin_sdi, &mut pin_srclk, code_l[i]);
            hc595_in(&mut pin_sdi, &mut pin_srclk, code_h[i]);
            hc595_out(&mut pin_rclk);
            thread::sleep(Duration::from_millis(100));
        }
        for i in (0..code_h.len()).rev() {
            hc595_in(&mut pin_sdi, &mut pin_srclk, code_l[i]);
            hc595_in(&mut pin_sdi, &mut pin_srclk, code_h[i]);
            hc595_out(&mut pin_rclk);
            thread::sleep(Duration::from_millis(100));
        }
    }
    clear_display(&mut pin_sdi, &mut pin_rclk, &mut pin_srclk, true);
    Ok(())
}
(love cat)(love cat)

次はLCD1602とやらを光らせる。まずはI2Cってなんぞやってところからかな。
https://docs.sunfounder.com/projects/davinci-kit/ja/latest/1.1.7_i2c_lcd1602.html

(love cat)(love cat)

とりあえずサンプルプログラム動かそうとしたが動かない。
raspi-configでi2cは有効にしたし、/boot/config.txtに"dtparam=i2c_arm=on"とあるのを確認し再起動した。
"i2cdetect -y 1"で0x27が有効になってるのも確認した。
配線は楽だからいつでも戻ってこれる。ここは後回し。

(love cat)(love cat)

次、アクティブブザー。
アクティブブザーは通電すれば自力で音を出せるところがパッシブブザーと違うらしい。
https://docs.sunfounder.com/projects/davinci-kit/ja/latest/1.2.1_active_buzzer.html

(love cat)(love cat)

トランジスタが回路に登場。
今回使うのはPNP型で、NPN型というのもあるそう。
NとかPは何かというと、P型半導体とN型半導体のこと。
N型半導体には負電荷を持つ自由電子があり、電圧をかけると電流が流れる。
P型半導体は電子の空席、正孔があり、電圧をかけると正孔に電子が流れて正孔が移動し電流が流れる。

(love cat)(love cat)

https://deviceplus.jp/mc-general/tidbits-of-electronics-01/

トランジスタの端子は部品の型番が刻印されている面(今回使用する「2SC1740S」の場合は「C1740」と記載されている)を手前にした状態が左右の向きの基準となります。電子工作でよく使われるトランジスタはこの向きで左の足からエミッタ(E)・コレクタ(C)・ベース(B)の順番に並んでいるものが多いです。この「E・C・B」の順番は語呂合わせで「え・く・ぼ」と呼ばれており、データシートを確認する手間を省きたいときはこのようにして端子の順番を見ることがよくあります。

可愛い。覚えやすい。

(love cat)(love cat)

アクティブブザー、猫がビックリするのであまり遊ばないほうが良さそう。
ピピピピ鳴らすだけのコード。

pub fn beep_active_buzzer() -> Result<(), Box<dyn Error>> {
    let mut beep_pin = Gpio::new()?.get(GPIO17)?.into_output();

    let running = Arc::new(AtomicBool::new(true));
    let r = running.clone();
    ctrlc::set_handler(move || {
        r.store(false, Ordering::SeqCst);
    })
    .expect("Error setting Ctrl-C handler");

    while running.load(Ordering::SeqCst) {
        beep_pin.set_low();
        thread::sleep(Duration::from_millis(100));
        beep_pin.set_high();
        thread::sleep(Duration::from_millis(10));
    }
    beep_pin.set_high();
    Ok(())
}
(love cat)(love cat)

サンプルコードで用意されている周波数の音を順番に鳴らす。
set_pwm_frequencyの第2引数はデューティーサイクル、今回は音量と考えて良さそう。

pub fn beep_passive_buzzer() -> Result<(), Box<dyn Error>> {
    // Frequency of Bass tone in C major
    const CL: [f64; 8] = [0.0, 131.0, 147.0, 165.0, 175.0, 196.0, 211.0, 248.0];
    // Frequency of Midrange tone in C major
    const CM: [f64; 8] = [0.0, 262.0, 294.0, 330.0, 350.0, 393.0, 441.0, 495.0];
    // Frequency of Treble tone in C major
    const CH: [f64; 8] = [0.0, 525.0, 589.0, 661.0, 700.0, 786.0, 882.0, 990.0];

    // Retrieve the GPIO pin and configure it as an output.
    let mut beep_pin = Gpio::new()?.get(GPIO17)?.into_output();

    for freq in CL {
        beep_pin.set_pwm_frequency(freq, 0.1)?;
        thread::sleep(Duration::from_millis(500));
    }
    for freq in CM {
        beep_pin.set_pwm_frequency(freq, 0.1)?;
        thread::sleep(Duration::from_millis(500));
    }
    for freq in CH {
        beep_pin.set_pwm_frequency(freq, 0.1)?;
        thread::sleep(Duration::from_millis(500));
    }

    Ok(())
}
(love cat)(love cat)

我ながら暇だなって思った。

pub fn beep_passive_buzzer() -> Result<(), Box<dyn Error>> {
    struct CDEFGAB {
        c: f64,
        d: f64,
        e: f64,
        f: f64,
        g: f64,
        a: f64,
        b: f64,
    }
    const M_TONE: CDEFGAB = CDEFGAB {
        c: 261.626,
        d: 293.665,
        e: 329.628,
        f: 349.228,
        g: 391.995,
        a: 440.0,
        b: 493.883,
    };
    const H_TONE: CDEFGAB = CDEFGAB {
        c: 523.251,
        d: 587.33,
        e: 659.255,
        f: 698.456,
        g: 783.991,
        a: 880.0,
        b: 987.767,
    };

    let mut beep_pin = Gpio::new()?.get(GPIO17)?.into_output();

    let mut beep = |tone: f64, duty_cycle: f64, millis: u64| {
        let _ = beep_pin.set_pwm_frequency(tone, duty_cycle);
        thread::sleep(Duration::from_millis(millis));
    };

    for i in 0..3 {
        // ときめく
        beep(M_TONE.b, 0.1, 500);
        beep(H_TONE.d, 0.1, 500);
        beep(M_TONE.a, 0.1, 500);
        beep(M_TONE.b, 0.1, 500);
        // こころの
        beep(M_TONE.a, 0.1, 500);
        beep(H_TONE.d, 0.1, 500);
        beep(H_TONE.d, 0.1, 480);
        beep(H_TONE.d, 0.0, 20);
        beep(H_TONE.e, 0.1, 500);
        if i == 0 {
            // もーしょん
            beep(739.938, 0.1, 500);
            beep(H_TONE.d, 0.1, 500);
            beep(M_TONE.b, 0.1, 500);
            beep(M_TONE.a, 0.1, 480);
            beep(M_TONE.a, 0.0, 20);
            // が
            beep(M_TONE.a, 0.1, 500);
            beep(M_TONE.a, 0.0, 1500);
        } else if i == 1 {
            // やまない
            beep(H_TONE.d, 0.1, 500);
            beep(H_TONE.e, 0.1, 500);
            beep(H_TONE.a, 0.1, 750);
            beep(H_TONE.a, 0.0, 250);
            // の
            beep(H_TONE.d, 0.1, 1000);
            beep(H_TONE.d, 0.0, 1000);
        } else {
            // プログラ
            beep(739.938, 0.1, 500);
            beep(H_TONE.d, 0.1, 500);
            beep(M_TONE.b, 0.1, 500);
            beep(M_TONE.a, 0.1, 480);
            beep(H_TONE.a, 0.0, 20);
            // ム しりた
            beep(M_TONE.a, 0.1, 480);
            beep(H_TONE.a, 0.0, 20);
            beep(M_TONE.a, 0.0, 500);
            beep(M_TONE.a, 0.1, 250);
            beep(M_TONE.b, 0.1, 250);
            beep(M_TONE.b, 0.0, 250);
            beep(H_TONE.d, 0.1, 250);
            // い しりた
            beep(H_TONE.d, 0.1, 500);
            beep(H_TONE.d, 0.0, 500);
            beep(M_TONE.a, 0.1, 250);
            beep(M_TONE.b, 0.1, 250);
            beep(M_TONE.b, 0.0, 250);
            beep(H_TONE.d, 0.1, 250);
            // い ねえもっと
            beep(H_TONE.d, 0.1, 250);
            beep(M_TONE.b, 0.0, 250);
            beep(M_TONE.a, 0.0, 500);
            beep(739.938, 0.1, 500);
            beep(H_TONE.d, 0.1, 500);
            // つきあって
            beep(H_TONE.d, 0.1, 500);
            beep(M_TONE.a, 0.1, 500);
            beep(H_TONE.e, 0.1, 750);
            beep(H_TONE.e, 0.08, 250);
            beep(H_TONE.d, 0.1, 1000);
        }
    }
    beep(H_TONE.d, 0.0, 1000);
    for i in 0..4 {
        // ダンスロボットダンス
        beep(H_TONE.d, 0.1, 200);
        beep(H_TONE.d, 0.0, 550);
        beep(H_TONE.d, 0.1, 230);
        beep(H_TONE.d, 0.01, 20);
        beep(H_TONE.d, 0.1, 200);
        beep(H_TONE.d, 0.0, 300);
        beep(554.365, 0.1, 250);
        beep(H_TONE.d, 0.1, 250);
        if i == 0 || i == 2 {
            beep(M_TONE.a, 0.0, 500);
            beep(M_TONE.a, 0.1, 500);
            beep(739.938, 0.1, 500);
            beep(H_TONE.d, 0.1, 450);
            beep(H_TONE.d, 0.0, 50);
        } else if i == 1 {
            beep(M_TONE.a, 0.0, 500);
            beep(H_TONE.d, 0.1, 500);
            beep(H_TONE.e, 0.1, 230);
            beep(H_TONE.e, 0.05, 20);
            beep(H_TONE.d, 0.1, 450);
            beep(H_TONE.d, 0.0, 50);
        } else {
            beep(H_TONE.d, 0.0, 500);
            beep(H_TONE.d, 0.1, 500);
            beep(H_TONE.a, 0.1, 250);
            beep(H_TONE.a, 0.0, 250);
            beep(H_TONE.d, 0.1, 500);
        }
    }
    Ok(())
}
(love cat)(love cat)
(love cat)(love cat)

L293Dは、高電圧と高電流のチップで統合された4チャネルモータードライバーである。

モーターを動かすための便利グッズ。

(love cat)(love cat)

おそらくL293Dのおかげで簡単に回すことができる。

pub fn motor() -> Result<(), Box<dyn Error>> {
    let mut pin_en1 = Gpio::new()?.get(GPIO22)?.into_output();
    let mut pin_1a = Gpio::new()?.get(GPIO27)?.into_output();
    let mut pin_2a = Gpio::new()?.get(GPIO17)?.into_output();

    pin_en1.set_high();
    pin_1a.set_high();
    pin_2a.set_low();
    thread::sleep(Duration::from_secs(3));
    pin_2a.set_high();
    thread::sleep(Duration::from_secs(3));
    pin_1a.set_low();
    thread::sleep(Duration::from_secs(3));
    Ok(())
}
(love cat)(love cat)

次はサーボ。
配線もコードも別に難しくなさそうなので、どういうところに使われてるものなのかとかを見たいところ。
https://docs.sunfounder.com/projects/davinci-kit/ja/latest/1.3.2_servo.html

(love cat)(love cat)

例えば、自動車製造工場で稼働する産業用ロボットは、部品をピッキングしたり、溶接したり、塗装したり、常に同じ動作を正確に繰り返しながら、大量の自動車を作り出しています。ロボットに内蔵されているサーボモータに指示を出すと、決められた位置や速度や回転力(トルク)で忠実に動いてくれます。
そのため、いまやサーボモータは、超高速や超精密な制御を行う産業機械の構成要素として、必要不可欠なものになっています。例えば、前出の産業用ロボットはもちろん、工作機械、電子部品の実装装置、半導体・液晶製造装置、射出成形機、ラベル包装機、プレス機械、医療機器など、様々な利用シーンで大活躍しているのです。

https://www.fujielectric.co.jp/products/column/servo/servo_01.html

(love cat)(love cat)

サーボはパルス幅変調は20ミリ秒に1パルスを期待している。
パルスの長さで角度が操作でき、1.5msで90度。
0.5msが最小で0度。2.5msが最大で180度。

(love cat)(love cat)

ご家庭で使うとしたらパッと思いつくのはスマートロックとか、あとは固定の猫カメラの向きかえるとかに使えそう?

(love cat)(love cat)
pub fn servomotor() -> Result<(), Box<dyn Error>> {
    const PERIOD_MS: u64 = 20;
    const PULSE_MIN_US: u64 = 500;
    const PULSE_NEUTRAL_US: u64 = 1500;
    const PULSE_MAX_US: u64 = 2500;

    let mut pin_servo = Gpio::new()?.get(GPIO18)?.into_output();
    for i in PULSE_NEUTRAL_US..PULSE_MAX_US {
        pin_servo.set_pwm(Duration::from_millis(PERIOD_MS), Duration::from_micros(i))?;
        thread::sleep(Duration::from_millis(10));
    }
    for i in PULSE_MIN_US..PULSE_NEUTRAL_US {
        pin_servo.set_pwm(Duration::from_millis(PERIOD_MS), Duration::from_micros(i))?;
        thread::sleep(Duration::from_millis(10));
    }
    Ok(())
}
(love cat)(love cat)

次はステッピングモーター。
何がどうなって動くのかなどを理解できる気がしないが、多分youtube探したらわかりやすい解説あるんじゃないかな。
https://docs.sunfounder.com/projects/davinci-kit/ja/latest/1.3.3_stepper_motor.html

(love cat)(love cat)

ドライバーボードというものを使って操作する。
IN1からIN4に入力をするのだが…ワイヤを接続するようなピン(?)がない。
キットの説明書の写真とか見るに普通に付いてるはずなので加工漏れか?
トホホ~。
探して買う。

(love cat)(love cat)

届いたのでやっていく。
1相励磁: 電流を流す配線を1つづつ切り替えて回す方法。常に1本にしか電流を流さないので低電力だが切り替わりが発生するのでガタガタうるさい。
2相励磁: 電流を1本目に流してる途中で2本目にも流し、2本目の途中で3本目にも流し…とする方法。当然電力はone phase onより使うがパワフルでなめらか静か。
1-2相励磁: 1相励磁と2相励磁を交互に繰り返す方式。回転のスピードは前の2つの半分になるが更になめらか。

(love cat)(love cat)

というわけでまずは1相励磁。

pub fn stepper_motor() -> Result<(), Box<dyn Error>> {
    let mut pins = [
        Gpio::new()?.get(GPIO18)?.into_output(),
        Gpio::new()?.get(GPIO23)?.into_output(),
        Gpio::new()?.get(GPIO24)?.into_output(),
        Gpio::new()?.get(GPIO25)?.into_output(),
    ];
    let one_step = [
        [true, false, false, false],
        [false, true, false, false],
        [false, false, true, false],
        [false, false, false, true],
    ];

    loop {
        for step in one_step {
            for i in 0..4 {
                if step[i] {
                    pins[i].set_high();
                } else {
                    pins[i].set_low();
                }
                thread::sleep(Duration::from_micros(1000));
            }
        }
    }
}
(love cat)(love cat)

2相励磁。さっきのコードだとsleepの長さが500マイクロ秒だと高い音を立てるだけで回らなくなったけどこっちなら回る。
滑らかさはよくわからない。

pub fn stepper_motor() -> Result<(), Box<dyn Error>> {
    let mut pins = [
        Gpio::new()?.get(GPIO18)?.into_output(),
        Gpio::new()?.get(GPIO23)?.into_output(),
        Gpio::new()?.get(GPIO24)?.into_output(),
        Gpio::new()?.get(GPIO25)?.into_output(),
    ];
    let two_step = [
        [true, true, false, false],
        [false, true, true, false],
        [false, false, true, true],
        [true, false, false, true],
    ];

    loop {
        for step in two_step {
            for i in 0..4 {
                if step[i] {
                    pins[i].set_high();
                } else {
                    pins[i].set_low();
                }
                thread::sleep(Duration::from_micros(1000));
            }
        }
    }
}
(love cat)(love cat)

1-2相励磁。

let half_step = [
        [true, false, false, false],
        [true, true, false, false],
        [false, true, false, false],
        [false, true, true, false],
        [false, false, true, false],
        [false, false, true, true],
        [false, false, false, true],
        [true, false, false, true],
    ];
(love cat)(love cat)

入力編、最後はリレー。
じっくり理解しよう。
https://docs.sunfounder.com/projects/davinci-kit/ja/latest/1.3.4_relay.html

(love cat)(love cat)

ダイオード:電流を一方向にしか流れないようにするもの(ざっくり)。
アノードとカソードを持ち、順方向にちゃんと繋がないと電流が流れづらい。
オームの法則に従わず、一定以上の電圧で一気に電流が流れるようになる。
つまり当たり前だが発光ダイオードと同じ性質。ただしあんまり光らない。
わざと逆方向に繋いで使うという技もあるらしい。

(love cat)(love cat)

リレー:電流が流れる→電磁力が発生する→その影響で他の回路が繋がる

(love cat)(love cat)

回路図のとおりにつなげた気がするが、思った通りに動かない。
最初GPIO17をlowにする(LEDがつく)→GPIO17をhighにする(LEDがついたまま)
最初GPIO17をhighにする(LEDがつかない)→GPIO17をlowにする(LEDがつく)→GPIO17をhighにする(LEDがついたまま)

(love cat)(love cat)

トランジスタを変えたら普通に動いた。
トランジスタが壊れてるとかではなく、NPNじゃなくてPNP使ってた。
無駄に悩んだおかげでリレーの回路については理解できた気がする。

(love cat)(love cat)

なんでダイオードが回路にあるのかはわかってないな。
色々試してるときもわからないから置いてなかったが、今無くても動いている。

このスクラップは2022/10/08にクローズされました