🚥
Rubyのコードで理解するシフトレジスタ(74HC595)の挙動
シフトレジスタは容量1バイトのストレージ
class Storage
attr_writer :input
attr_reader :memory
def initialize
@values = Array.new(8) { 0 }
end
def shift
@values = [@input, *@values].take(@values.size)
end
def commit
@memory = @values.clone
end
def to_i
[@memory.join].pack("B*").ord
end
def inspect
"%08b (%d)" % [to_i, to_i]
end
end
だと思っとけばOK
一度に書き込めるのは1ビットだけ
S = Storage.new
S.input = 1
S.shift
S.commit
S.memory # => [1, 0, 0, 0, 0, 0, 0, 0]
shift するたびに input を配列に入れる
commit で memory に反映する
続けて 0 を入れる
S.input = 0
S.shift
S.commit
S.memory # => [0, 1, 0, 0, 0, 0, 0, 0]
[1, ...]
がずれて [0, 1, ...]
となった
8回繰り返す
170を保存する例
8.times do |i|
S.input = 170[i]
S.shift
S.commit
S.to_i # => 32, 144, 72, 164, 82, 169, 84, 170
end
S # => 10101010 (170)
8個分詰めてやっと1バイト完成する
シフト中の状態は隠したい
8.times do |i|
S.input = 123[i]
S.shift
end
S # => 10101010 (170)
S.commit
S # => 01111011 (123)
shift 後に commit するのをやめる
そして終わったタイミングでのみ commit する
そうすれば一瞬で切り替わったように見える[1]
つまりシフトレジスタを参照している側からは1バイト毎に来ているように見える
8回ループするのが面倒
def shift_out(value)
8.times do |i|
S.input = value[i]
S.shift
end
end
そこだけメソッド化しておく
これで1バイトが1行で送れるようになった
shift_out(204)
S # => 01111011 (123)
S.commit
S # => 11001100 (204)
メリット
1ビットずつ溜めることで1バイト分に見えるようになった
本来1バイトを送るのにデータ用のGPIOが8本いるがこれなら1本で済む
デメリット
速度を犠牲にしている
とはいえ超速いし次のデータを用意してから切り替えているので遅さを体感するのは難しい
実際のコードとの対応
input = 1
digitalWrite(SER, 1);
shift
digitalWrite(SRCLK, LOW);
digitalWrite(SRCLK, HIGH);
shift_out(170)
shiftOut(SER, SRCLK, LSBFIRST, 170);
commit
digitalWrite(RCLK, LOW);
digitalWrite(RCLK, HIGH);
Discussion