前回は、Raspberry Pi PicoのProgramable IO(PIO)が可能性無限大、みたいな話を書きましたが、オシロで波形を観察したにとどまりました。今回はその威力を実地に確かめるべく、前々回の7セグLED4桁駆動回路に適用してみます。一撃で任意の波形を作れるPIOがあると本当に楽だ。
※「MicroPython的午睡」投稿順 Indexはこちら
まずは、テスト時の様子を撮ったアイキャッチ画像を御覧ください。前回と同じじゃないか、ということなかれ、であります。Digilent AnalogDicovery2の接続
- 前回はオシロ
- 今回はロジアナ
AnalogDiscovery2の2現象のオシロでは見たい信号を取り切れなくなり、ロジアナ機能を使った、ということであります。それでとらえた波形はこちらです。
Name欄に74HC595の端子名を書いて置きました。上から
- DS、シリアル入力データ端子。シフトINするデータを載せる
- OE#、アウトプットイネーブル。Lowのとき74HC595の出力がドライブされる
- ST_CP、シフトレジスタから出力(ストレージ)レジスタへのラッチ
- SH_CP、シフトレジスタへのクロック
であります。まあOE#信号は常にイネーブルでも良いのですが、殊更に制御してみました。上記から分かることは、最低でも4端子を制御しないと74HC595は所望の動作をしてくれない、ということであります。前回はデータ1端子、クロック1端子を制御しただけした。さて複数端子を制御する方法は? Raspberry Pi Pico Python SDK をくまなく真面目?に読んでいたら分かります(本当か?)結論から書きましょう。
PIOは、ある端子から始まる連続した「端子群」を1度に操作できる
のであります。これは、データの出力に使用するOUT命令であっても、今回クロックなど制御端子の操作に使っている sideset 機能であっても同様です。今回は、制御端子をGP19番から「並び」の3端子、GP19, GP20, GP21 に割り当てています。そこで以下のようなパラメータをSM(ステートマシン)に教えてやれば、GP19番からの並びの端子を一気に操作してくれます。とっても楽。
sideset_base=Pin(19)
4桁7セグLED駆動プログラムの改造
前々回の回路構成に戻し、前々回のMicroPythonプログラムをPIO利用のコードで書き換えてみます。ソースコード全文は末尾にあります。まず、嬉しいのが、
前々回ソフトウエアでパルス幅を確保するのに使ったソフトループ
が無くせた、ということであります。当然、信号をON/OFFするための処理やら、シフトするための処理などCPUパワーを使ってソフトで始末していた部分は、SM側に「オフロード」されています。前々回のsend8bitData() 関数と比べるとその差が明らかです。今回は、パターンを反転し(セグメント端子がカソード側なので)、データをSMに渡すだけになっています。
SM自体の起動は、最初に1回
sm.active(1)
でおしまいです。SMはFIFOにデータが来るのをずっと「待って」おり、put()関数でデータが書きこまれるとPIOアセンブラで書かれたコード ”send74HC595″ を実行します。この関数自体はMicroPythonの中に埋め込まれていますが、メインのArmプロセッサが実行するわけでなく、実行はSMによります。
こうしてSMは 「74HC595 専用のインタフェース回路」として動作しつづけます。
MicroPython的午睡(15) ラズパイPico、プログラマブルIOの威力 へ戻る
MicroPython的午睡(17) ラズパイPico、I2CでシリアルEEPROM接続 へ進む
74HC595制御をPIOで行った7セグLED4桁表示プログラム
from rp2 import PIO, asm_pio from machine import Pin, Timer import time LED7 = [0] * 10 # ABCDEFGP LED7[0] = (0b11111100) LED7[1] = (0b01100000) LED7[2] = (0b11011010) LED7[3] = (0b11110010) LED7[4] = (0b01100110) LED7[5] = (0b10110110) LED7[6] = (0b10111110) LED7[7] = (0b11100000) LED7[8] = (0b11111110) LED7[9] = (0b11110110) datIdx = 0 datPat = [0] * 4 dig = [None] * 4 dig[0] = Pin(2, Pin.OUT) dig[1] = Pin(3, Pin.OUT) dig[2] = Pin(4, Pin.OUT) dig[3] = Pin(5, Pin.OUT) for idx in range(4): dig[idx].value(1) hc595DS = Pin(22, Pin.OUT) hc595OEb = Pin(21, Pin.OUT) hc595ST = Pin(20, Pin.OUT) hc595SH = Pin(19, Pin.OUT) hc595MRb = Pin(18, Pin.OUT) hc595DS.value(0) hc595OEb.value(1) hc595ST.value(0) hc595SH.value(0) hc595MRb.value(1) def send8bitData(dat): tmp = ~dat & 0xFF sm.put(tmp) @asm_pio(sideset_init=(rp2.PIO.OUT_LOW,rp2.PIO.OUT_LOW,rp2.PIO.OUT_LOW), out_init=rp2.PIO.OUT_LOW, out_shiftdir=PIO.SHIFT_RIGHT) def send74HC595(): pull() set(x, 7).side(0x4) [1] label("loop") out(pins, 1) [1] nop().side(0x5) [1] nop().side(0x4) [1] jmp(x_dec, "loop") nop().side(0x6) [1] nop().side(0x4) [1] nop().side(0x0) def display1digit(timer): global datIdx dig[datIdx].value(1) datIdx = (datIdx + 1) if datIdx < 3 else 0 send8bitData(datPat[datIdx]) dig[datIdx].value(0) sm = rp2.StateMachine(0, send74HC595, freq=400000, sideset_base=Pin(19), out_base=Pin(22)) sm.active(1) tim = Timer() tim.init(freq=250, mode=Timer.PERIODIC, callback=display1digit) for cnt in range(9999): datPat[3] = LED7[cnt % 10] datPat[2] = LED7[(cnt // 10) % 10] datPat[1] = LED7[(cnt // 100) % 10] datPat[0] = LED7[(cnt // 1000) % 10] time.sleep(3.0) sm.active(0)