🚥
Rubyのコードで理解するシフトレジスタの基本動作
シフトレジスタは容量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
一度に書き込めるのは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 = value
digitalWrite(SER, value);
shift
digitalWrite(SRCLK, LOW);
digitalWrite(SRCLK, HIGH);
shift_out(value)
shiftOut(SER, SRCLK, LSBFIRST, value);
commit
digitalWrite(RCLK, LOW);
digitalWrite(RCLK, HIGH);
Discussion