MicroPython的午睡(88) STM32版、SPI接続のSRAM読み書きテスト

Joseph Halfmoon

前回までにTimerは何とか制御できそうな気がしてきたので、今回からインタフェース回路に進みます。最初はSPIです。シリアルだけれどもそこそこ高速な転送速度を使えるインタフェースです。STM32版のMicroPythonの場合、ソフトウエア的には2系統のモジュールでSPIにアクセスできます。別に同じハードなんだけれども。

※「MicroPython的午睡」投稿順 Indexはこちら

SPI接続のSRAMを接続してみる

STM32版MicroPythonからSPIを使用してみる実験に、今回はSPI接続のSRAMを使ってみることにいたしました。SRAMの特定のアドレスにテキトーなデータを書き込んだのち読み出して、同じものが読めればOKっという目論見。

接続するSRAMは既に何度も使っている米マイクロチップ社の以下のチップです。

23LC512 2.5-5.5V 512Kb SPI Serial SRAM

さてSTM32との接続ですが、STM32版のMicroPythonはソフトウエアSPIもサポートしているので、ぶっちゃけほとんどの端子をSPI接続に使えるようですが、やはり速度性能等を考えるとハードウエアのSPIを使った方が良いです。今回はSTM32上でSPI1と呼ばれているハードウエアSPIを使用してみます。

SPIの機能端子と、STM32チップ上での端子名、そしてNucleo-F401REボード上のArduino互換ピンソケットでの端子名の表が以下に。

SPI端子 STM32端子名 Arduino互換ピンソケット端子名
SPI1_SCLK PA_5 D13
SPI1_MISO PA_6 D12
SPI1_MOSI PA_7 D11
SPI1_SSEL PA_4 A2

後でみますが、使用するMicroPythonモジュールによっては、SPI1_SSELはソフト制御にできるので、上記端子でなくても良いと思われます。

さて、Nucleo-F401REボードに23LC512を接続した実験用の回路が以下に(現物の様子は冒頭のアイキャッチ画像に。)STM32SPISRAM_Schematic

2つのモジュール

さてSTM32版のMicroPythonでは、2系統のモジュールでSPIをサポートしています。といって制御対象は同じハードウエアなので、冗長と言えば冗長。多分、pyboardとの互換性をとるか、他ボードとの互換性の高い方法をとるかという配慮かと。

まずpyboard互換のクラスは以下のようです。import pybすればhelpを見れます。helpPybSPI

こちらを使用した場合の制御方法は以下のページに記されてます。

class SPI — a controller-driven serial protocol

上記方法は、SPIのCS端子なども所定の端子を使って制御してくれたりしてくれてSTM32のハード密着型でSTM32特有の機能を使える一方、キメウチのハードウエア制御のみなので多少柔軟性にかけるじゃないか、と思われます。

もう一つが machineモジュール内のSPI機能を使う方式です。こちらも import machineすれば helpを見れます。helpMachineSPI

pybとは関数セットなどもかなり異なっていることがわかります。こちらについてのマニュアルページは以下で良いかと思います。

クラス SPI — シリアルペリフェラルインタフェース バスプロトコル(コントローラ側)

こちらの方が、他機種のMicroPythonと互換性をとったりするにはよろしいように思われるのですが、同じソースが無修正でそのまま走るかどうかは微妙です。今回、大分以前にやってみたRaspberry Pi Picoボード用のMicroPython上で動かした以下の回のMicroPythonソースを持ってきたのですが、SPIの初期化のところはPico用の記述の端子名を変更した程度ではダメでした。

MicroPython的午睡(18) ラズパイPico、SPIでシリアルSRAM接続

読み書きは同じようにできたので、ま、いいか。

今回実験に使用したMicroPythonスクリプト

以下に示すスクリプトは、ラズパイPicoのMicroPython用だったものを小改造したものであります。キメウチのSPI‐SRAMアドレスにキメウチのデータを書いて、読んでみるだけのもの。速度は控えめの100kHzです(ブレッドボードのグダグダ配線だし。)SPIなのでもっと早くしてもいいかな~。

#STM32: SPI1 SRAM 23LC512 R/W test
#SPI1_SCLK	PA_5	D13
#SPI1_MISO	PA_6	D12
#SPI1_MOSI	PA_7	D11
#SPI1_SSEL	PA_4	A2
import time
from machine import Pin, SPI

spi1 = SPI(1, 100_000)
cs1 = Pin(machine.Pin.board.A2, Pin.OUT)
cs1.value(1)

def rdmr():
    buf = bytearray(1)
    command = bytes([0x05])
    cs1.value(0)
    spi1.write(command)
    spi1.readinto(buf)
    cs1.value(1)
    return buf

def writeSEQ(memadr, datlis):
    command = bytes([0x02, ((memadr >> 8) & 0xFF), (memadr & 0xFF)])
    dat = bytes([(x & 0xFF) for x in datlis])
    cs1.value(0)
    spi1.write(command)
    spi1.write(dat)
    cs1.value(1)

def readSEQ(memadr, datlen):
    buf = bytearray(datlen)
    command = bytes([0x03, ((memadr >> 8) & 0xFF), (memadr & 0xFF)])
    cs1.value(0)
    spi1.write(command)
    spi1.readinto(buf)
    cs1.value(1)
    return buf

def main():
    print("STM32F401RE SPI1 SRAM R/W Test.")      
    result = rdmr()
    print("Mode Register = 0x{0:02x}".format(result[0]))
    memADR = 0x1000
    memDAT = [0x31, 0x32, 0x33, 0x34]
    while True:
        writeSEQ(memADR, memDAT)
        result = readSEQ(memADR, 4)
        print("ADR=0x{0:04x} {1}".format(memADR, str(result)))
        if memADR == 0x1000:
            memADR = 0x1004
            memDAT = [0x35, 0x36, 0x37, 0x38]
        else:
            memADR = 0x1000
            memDAT = [0x31, 0x32, 0x33, 0x34]
        pyb.LED(1).on()
        time.sleep(1)
        pyb.LED(1).off()
        time.sleep(1)

if __name__ == "__main__":
    main()
動作結果

動作結果がほれこのように。

STM32SPISRAM_TestResult

アドレス0x1000番地からアスキーコードで 1234、次の0x1004番地には5678と書かれておるようです。予定通りの動作っす。

MicroPython的午睡(87) STM32版、入力キャプチャ割り込み受けで出力コンペア へ戻る

MicroPython的午睡(89) STM32版、I2C接続EEPROM読み書きテスト へ進む