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

Joseph Halfmoon

前回はシリアルEEPROM、I2C接続でした。今回はシリアルSRAM、SPI接続であります。前回はMicroPythonからのI2C制御、釈然としない部分ありだったのですが、今回のSPIは明朗会計なんであります。ほとんど何も考えずに接続すれば動作してしまう感じ。落差大きいです。

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

ラズパイPicoに接続するデバイスは、前回に続きマイクロチップ社製であります。23LC512、512Kビット(64Kバイト)品です。流石にこの容量になってくると高速とは言えないI2Cバス接続は辛い。SPI接続であります。まずはデータシートへのリンクを貼り付けておきます。

23LC512 2.5-5.5V 512Kb SPI Serial SRAM

このデバイス、8ピン小パッケージのデバイスでありますが、通常のSPIモード(データ線は入出力独立各1ビット)に加えて、QSPIモード(データ線は入出力共用4ビット幅)にも対応しています。またクロック周波数は最大20MHzです。電源落としたら記憶が飛んでしまうSRAMですが、その代わり前回のEEPROMなどと異なり書き換え回数の上限などなく、また書き込み中は待たされるようなことなどありません。頻繁に書き換えるデータの一時保存などには重宝するんじゃないかと思います。

ラズパイPicoは、ハードウエアのSPIを2チャンネル搭載しています。PIOのSMをSPI用にプログラムするという手もありますが、今回は折角なのでハードウエアIPの方を使わせていただきます。RP2040のデータシートを見ると、このSPIは、Arm社のIPライブラリである PrimeCellであるようです。型番PL022。

今回は2個の搭載SPIのうち、SPI0の方をSPIモードで1MHzという低速設定で使ってみたいと思います。MicroPythonでの読み書きテストプログラムの全文は末尾にあります。

以下に今回実験した回路を示します。例によってラズパイPicoは端子アサインの自由度が高いです。必ずしもGP2からGP5に接続しなければならないわけでも無いのです。手元のブレッドボード上接続しやすかったのでココにした、というだけのものです。シンプルな接続で64Kバイト分のメモリが接続できて良い感じです。

RPiPico_23LC512_schematic現物の接続の様子を以下に掲げました。前回のI2CシリアルEEPROMも同じ8ピンのデバイスでしたが、プルアップなど無い分、回路はスッキリした印象です。一応、波形をとれるようにピンたてたのですが、波形を見るまでもなく動いてしまったので、見てません。

RPiPico_23LC512DUTI2Cの時と同様、ラズパイPicoのMicroPython SDKのドキュメントには、サンプルプログラムが書いてありますが、メソッドの説明がありません。「だいたい」該当する説明文書(日本語)は以下のもので良いのではないかと思うので、URLを掲げておきます。

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

ここでポイントは、「CSバー」信号は別途ソフト制御に任されているということです。SPIのハードでは制御してくれません。しかし、その方がナンボか良かったです。下手にハード制御されると、デバイス固有の読み書きシーケンスに合致しない可能性もあり、です。ソフトでCSをロウ(アクティブ)にした後、必要な順番でプリミティブなWriteとReadシーケンスを並べ、最後にCSをハイ(インアクティブ)に戻す、というやり方で読み書きともバッチリじゃないかと思います。

末尾のサンプルプログラムでは、まず動作モードの設定レジスタを読み出し(変更する場合には書き込まないとならないですが、今回はデフォルト値でそのまま動作させています)て確認、そのあと0x1000番地からアスキーコードで’0′, ‘1’, ‘2’, ‘3’と4バイト書き込み、そして読み出して表示というシーケンスです。

以下に動作させた結果を示します。

Mode Register = 0x40
bytearray(b'1234')

モードレジスタが0x40というのは、「シーケンシャルモード」になっていることを示しています。このチップは、バイトとか、32バイトページとか、でも読み書きできるのですが、シーケンシャルモードであると、最初に先頭アドレスを与えた後、何バイトでも(実メモリは64Kバイト。上限超えるとラップ。)読みでも書きでも続けられます。これだけあればええじゃないか、と思えるモードです。なおMicroPython上の入出力データは、I2Cの時と同様、以下の型の入れ物に入れておく必要があります。

  • 書き込み元は bytes型(変更不可)で良い
  • 読み込み先はbytearray型(変更可能)でなければならない

今回は、特にトラブルこともなく動作してしまいました。次回は再びラズパイPicoの特徴であるPIOのSMを使って、異なるインタフェースに接続してみる予定。

MicroPython的午睡(17) ラズパイPico、I2CでシリアルEEPROM接続 へ戻る

MicroPython的午睡(19) ラズパイPico、「普通の」SRAM接続に手こずる へ進む

SPI接続シリアルSRAM READ/WRITE テストプログラム
import time
from machine import Pin, SPI

spi0 = SPI(0, 100_000, sck=Pin(2), mosi=Pin(3), miso=Pin(4))
cs0 = Pin(5, Pin.OUT)
cs0.value(1)

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

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

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

result = rdmr()
print("Mode Register = 0x{0:02x}".format(result[0]))
writeSEQ(0x1000, [0x31, 0x32, 0x33, 0x34])
result = readSEQ(0x1000, 4)
print(str(result))