以前にもシリアルEEPROMをラズパイPicoに接続したことがありましたがI2C接続でMicroPython利用でした。今回は「3線式」EEPROM、結構昔からあるタイプ、をC/C++SDKで接続してみます。「その1」はソフトウエア制御であります。簡単だろ~と思っていたら、お約束の落とし穴にハマりました。新年そうそう。
※「Pico三昧」投稿順 index はこちら
(末尾に実験に使用したソース全文を掲げました。なお、ソースのビルドとデバッグの母艦としてRaspberry Pi 4 model Bを使用しております。)
3線式シリアルEEPROM S93C46
さて今回接続してみるEEPROMは、エイブリック(元のSIIセミコンダクタ)製S93C46であります(エイブリック社についての関連記事はこちら。)エイブリック社の製品ページは以下です。
3ワイヤシリアルEEPROM S-93C46C/56C/66C/76C/86C
ただし、現物は秋月電子通商殿の以下のページから購入したので、エイブリックと名前が変更される前のものかもしれません。
なお、この「3線式」シリアルEEPROM、オリジナルはフェアチャイルド(現オン・セミコンダクタ)じゃないかと思います。また「3線式」のインタフェース、MICROWIREと名前がついているのですが、そこそこ古い規格のようです。インタフェースはSPIによく似ています(ちょっと工夫すればSPI接続できるかも。)しかし、微妙にSPIとは異なる部分があります。この規格のオリジンはどうもナショナル・セミコンダクタ(TIに買収された)みたいです。規格の説明資料が以下に。
AN-452 MICROWIRE Serial Interface
歴史があるだけに、ちょっと調べただけで買収された会社名が次々と現れてまいりまして、諸行無常を感じさせます。
さて、S93C46は、1Kビット(バイトじゃありません)のEEPROMです。1ワード16ビットx64ワードの容量的にはごくごく小さい不揮発メモリです。普通のFlashとの違いは百万回の書き換えができること。頻繁に書き換える設定値などを電源断の期間でも覚えておくために使うのが「ありがち」な使い方かと思います。
Raspberry Pi Picoとの接続
さて、ラズパイPicoとの接続は以下のようにしました(冒頭のアイキャッチ画像ではDO端子にプルアップ抵抗が付いているのですが、DOのアクティブ期間を知りたくてつけたものです。機能的には不要です。)
3線式と言いつつも、CS(チップ・セレクト)信号があるので合計4端子を使っています。
Project作成
C/C++SDKでのプロジェクトの作成は、例によって
Raspberry Pi Pico Project Generator
を使わせていただきました。GUIモードでの設定画面が以下に(末尾に生成したCMakeLists.txtを添付しました。)
今回は、GPIOをソフトで制御してEEPROMの読み書きテストしようということなので、特別な設定は不要でした。自動生成したCMakeLists.txtのままでOK。
動作確認
末尾に添付したソフトの原型で、「動くはず」だったのですが、動きませんでした。書き込みしているのに書き込みできてない、読み出すと初期値のまま、という感じの症状です。なぜ?と?が2つくらいついたところでデータシートを良く読んで判明しました。
S93C46はRESET時、書き込み保護状態でスタートする
これです。なにか暴走でもあって、虎の子の大事な設定値が飛ばないように簡単な鍵をかけているのです。最初に書いたとき、この「鍵」に気づいておらず、ちゃんと書き込みできている波形の筈なのに書き込めませんでした(当然です。)添付コードの中に後から付け加えた以下の関数に
controlS93C46(bool en)
true を渡せば書き込みできる状態となり、falseを渡せば書き込み不可となります。
添付の実験コードの書き込み時の波形を Analog Discovery2のロジアナモードで観察したものがこちら。
そして読み出し時(上記書き込みとはアドレスが対応していないです)の波形が以下に。
簡単なインタフェースなので、動けば何と言うこともないですが、動かないと焦ります。まあ、データシートをちゃんと読んでいれば問題なかったのですが。読む前にコードを書いて動かしてみる粗忽者です。
さて、実際にVSCode(PC上からラズパイ4にリモートログイン)からデバッガ(ラズパイ4からラズパイPicoへのSWDデバッグ)を使って動作確認している様子が以下です。
書き込みと同じアドレスに対応する読み出しが完了したところで、ブレークポイントで止まって、読み書きの値を比べているところです。
書いたものがそのまま読めるというだけのことです。まったくスペクタクルは無いですが、動いているぞなもし。
Pico三昧(4) Pico C/C++ SDKで74HC595接続、PIO制御編2直列 へ戻る
Pico三昧(6) Pico C/C++ SDKでS93C46接続その2、PIO制御 へ進む
実験に使用したCソース全文
// S93C46 Software Control #include <stdio.h> #include "pico/stdlib.h" // S93C46 PINS #define SK (18) #define DI (19) #define DO (20) #define CS (21) #define MAX_ADR (64) #define WAITV (5) void enableS93C46() { gpio_put(CS, true); //CS=HIGH, ENABLE } void disableS93C46() { gpio_put(CS, false); //CS=LOW, DISABLE } void setupS93C46() { gpio_init(CS); gpio_set_dir(CS, GPIO_OUT); disableS93C46(); gpio_init(SK); gpio_set_dir(SK, GPIO_OUT); gpio_put(SK, false); gpio_init(DI); gpio_set_dir(DI, GPIO_OUT); gpio_put(DI, false); gpio_init(DO); gpio_set_dir(DO, GPIO_IN); } void sendBit(bool dat) { gpio_put(SK, false); gpio_put(DI, dat); sleep_us(WAITV); gpio_put(SK, true); sleep_us(WAITV); } void startBit() { sendBit(true); } void readInst() { sendBit(true); sendBit(false); } void writeInst() { sendBit(false); sendBit(true); } void sendAdr(uint8_t adr) { adr &= 0x3F; adr <<= 2; for (int i=0; i<6; i++) { if ((adr & 0x80) != 0) { sendBit(true); } else { sendBit(false); } adr <<= 1; } } void sendData(uint16_t dat) { for (int i=0; i<16; i++) { if ((dat & 0x8000) != 0) { sendBit(true); } else { sendBit(false); } dat <<= 1; } } void getData(uint16_t *datptr) { bool pin; uint16_t temp = 0; for (int i=0; i<16; i++) { temp <<= 1; gpio_put(SK, false); sleep_us(WAITV); gpio_put(SK, true); sleep_us(WAITV); pin = gpio_get(DO); if (pin) { temp |= 1; } } *datptr = temp; } void writeS93C46(uint8_t adr, uint16_t dat) { enableS93C46(); startBit(); writeInst(); sendAdr(adr); sendData(dat); disableS93C46(); } void readS93C46(uint8_t adr, uint16_t *datptr) { enableS93C46(); startBit(); readInst(); sendAdr(adr); getData(datptr); disableS93C46(); } void controlS93C46(bool en) { enableS93C46(); startBit(); sendBit(false); sendBit(false); if (en) { sendBit(true); sendBit(true); } else { sendBit(false); sendBit(false); } sendBit(false); sendBit(false); sendBit(false); sendBit(false); disableS93C46(); } int main() { uint16_t result; uint16_t dat = 5; uint8_t adr = 0; stdio_init_all(); puts("S93C46 Software Read/Write TEST 00."); setupS93C46(); controlS93C46(true); for (int i=0; i<100; i++) { printf("0x%02x = 0x%04x\r\n", adr, dat); writeS93C46(adr, dat); sleep_ms(1000); result = 0; readS93C46(adr, &result); printf("0x%02x = 0x%04x\r\n", adr, result); sleep_ms(1000); adr++; if (adr >= MAX_ADR) adr = 0; dat <<= 1; if (dat == 0) dat = 5; } controlS93C46(false); return 0; }
実験に使用したCMakeLists.txt
# Generated Cmake Pico project file cmake_minimum_required(VERSION 3.13) set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 17) # initalize pico_sdk from installed location # (note this can come from environment, CMake cache etc) set(PICO_SDK_PATH "/home/pi/pico/pico-sdk") # Pull in Raspberry Pi Pico SDK (must be before project) include(pico_sdk_import.cmake) project(S93C46 C CXX ASM) # Initialise the Raspberry Pi Pico SDK pico_sdk_init() # Add executable. Default name is the project name, version 0.1 add_executable(S93C46 S93C46.c ) pico_set_program_name(S93C46 "S93C46") pico_set_program_version(S93C46 "0.1") pico_enable_stdio_uart(S93C46 0) pico_enable_stdio_usb(S93C46 1) # Add the standard library to the build target_link_libraries(S93C46 pico_stdlib) pico_add_extra_outputs(S93C46)