メモ:rp-hal(RustでRaspberry Pi Picoをプログラミングするやつ)でPIOでシフトレジスタIC(74HC595)を使う
すでに詳しくブログを書かれている方がいるので、書くほどでもないと思いつつメモ。
74HC595 ピンと配線
TC74HC595のデータシートはこれ。他にもTIが出してるSN74HC595、Unisonicが出してるU74HC595(秋月で売ってる)とかがあるらしい。
コントロール用のピンは5つあるけど、出力はつねに出てていいのでピン13はGNDに(Lowだと出力が有効になる)、シフトレジスタのデータをクリアしたいときは新しくデータを入れればいいだけなのでピン10はVccに(Lowだとクリアされてしまう)。なので、以下のような配線にして実質3つだけをコントロールすればいい。
┌─────v─────┐ 1 │ │ 16 2 │ │ 15 3 │ │ 14 Input <------------ GPIO2 4 │ │ 13 Output enable <----- GND 5 │ │ 12 Ratch <------------ GPIO3 6 │ │ 11 Clock <------------ GPIO4 7 │ │ 10 Clear <------------- Vcc 8 │ │ 9 └───────────┘
実装
- pin 11(クロック)は入力のたびに LOW→HIGH に変化させる
- pin 12(ラッチ)は出力が’揃ったら(つまり8回シフトしたら)LOW→HIGHに変化させる
という決まった作業があるので、ここにPIOを使う。 OSRにデータを送って、それを1bitずつpin 14(入力)にセットしつつクロックとラッチを動かしたい。
Rustのコード
こんな感じのはず。ちなみに、OSRのシフト方向は、PIOのプログラムをビルドするときにout_shift_direction()
で設定できる。
ちなみに、今回は、デフォルト(左)のままにして、データを送るときに左詰めにする(32ビットに8ビットのデータを入れるので、24ビット分シフトさせて詰める)感じでやってる。
これは、595と7セグLEDをそういう順番でつないじゃったので考えるのがめんどくさくてそうしてるけど、効率でいうと↑のブログでやってるように右シフトでやるのが正しいのかも。ビットシフトしなくていいので。
// OSRにデータを送る fn pio_shift_out<T: ValidStateMachine>(tx: &mut Tx<T>, data: u32) { tx.write(data); }
let pin1: hal::gpio::Pin<_, hal::gpio::FunctionPio0> = pins.gpio2.into_mode(); let _pin2: hal::gpio::Pin<_, hal::gpio::FunctionPio0> = pins.gpio3.into_mode(); let _pin3: hal::gpio::Pin<_, hal::gpio::FunctionPio0> = pins.gpio4.into_mode(); let pin1_id = pin1.id().num; let (mut sm, _, mut tx) = PIOBuilder::from_program(installed) .out_pins(pin1_id, 1) .side_set_pin_base(pin1_id + 1) .build(sm0); sm.set_pindirs([ (pin1_id, hal::pio::PinDir::Output), (pin1_id + 1, hal::pio::PinDir::Output), (pin1_id + 2, hal::pio::PinDir::Output), ]); let _sm = sm.start(); let mut level = 0_u8; loop { info!("{}", level); pio_shift_out(&mut tx, (level as u32) << 24); level = (level + 1) % 128; // 7ビットしか使ってないので127(7ビットの最大値)を超えたら0にリセットする delay.delay_ms(100); }
PIOのコード
基本的には↑のブログに載っていた通り。
0b10
のピンはクロックで、out
でピン14の状態を更新するたびにHIGHにする。
0b01
のピンはラッチで、8ビット取り込むごとにHIGHにする。
.program shift_register .side_set 2 pull block side 0b00 set x, 7 side 0b00 loop: out pins, 1 side 0b00 jmp x-- loop side 0b10 ; set clock pin to HIGH to tick down nop side 0b01 ; set ratch pin to HIGH to update the output of the IC