
前回は、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)

