メモ: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