前回は3線式シリアルE2PROM S93C46をソフトウエア制御で読み書きしてみました。今回は、ラズパイPicoの特長であるPIO(Programmable IO)を使って同じことを行ってみます。PIOステートマシンのアセンブラを書くのも大分慣れた気もしますが、最後は波形を見ないと安心できないんだ,、これが。
※「Pico三昧」投稿順 index はこちら
(末尾に実験に使用したソース全文を掲げました。なお、ソースのビルドとデバッグの母艦としてRaspberry Pi 4 model Bを使用しております。)
PIO使用のプロジェクトの作成
例によって Raspberry Pi Pico Project Generator を使えば、PIO使用のプロジェクトの作成は簡単です。以下GUIモードの設定画面では、
Library Options >> PIO interface
にチェックを入れればOKっと。
上記で、作成されるCMakeLists.txtの全文を末尾に掲げました。PIOを使用する場合、これまた「例によって」手動で一文を加えないとなりません。
pico_generate_pio_header(S93C46PIO ${CMAKE_CURRENT_LIST_DIR}/S93C46PIO.pio)
上記は、PIOアセンブラのソースから、PIOアセンブラのコードをCから呼び出すためのヘッダファイルを生成するためのものです。この際、ヘッダファイルを自力で書くのであれば不要かもしれません。しかし、私は自分でさらから書いたことがありませぬ。お任せでお楽な方が好き。
ラズパイPicoとの接続
ラズパイPicoとS93C46の接続は前回と同じ、と行きたかったのですが、PIOアセンブラを書いていて気が変わり、以下のように修正いたしました。SK(クロック)とCS(チップセレクト)の2信号をまとめて PIO の side 信号として扱いたかったためです。前回の設定だと、SKとCSが隣り合っていなかったです。隣り合うように変更しました。そして
-
- S93C46のDI端子が、PIOのout信号
- S93C46のDO端子が、PIOのin信号
となります。S93C46からみた時のIN/OUTとPIOからみたときのIN/OUTが逆転するので、名前がよじれるのは致し方ありません。
PIOステートマシンのプログラミング
ソースコード全文を末尾に掲げましたが、今回作成したPIOステートマシンのコードは、
リードもライトも、ついでに書き許可、不許可も1個で全部OK
な、ズボラなコードとしました。そのためPIOにputするデータは
-
- ストップビット1ビット(常に1)
- コマンドビット2ビット
- アドレス6ビット
- 書き込みデータ16ビット(読み出しや書き込み許可時にはオール0でよい)
- 埋め草7ビット
の合計32ビットを上記の順にMSBから詰めたものを渡すこととしました。上記の操作はCで書けば、以下の例(ライト時のコマンドビット含む例)のように書けるので、まあ許せる範囲か、というお手盛りです。
0xA0000000 | (adr << 23) | (dat << 7);
そして、入出力で独立なデータ線がある3線式ならではの
-
- 常に出力データ16ビットをDI端子へ送り出す(ただしコマンドがリードであればS93C46はデータ線を無視するので問題ない)
- 常にDO端子の値16ビットを読み取って戻してくる(ただしコマンドがリード以外であれば読み取った値を捨てればよい)
という割り切りです。ただし、書き込みや書き込み許可時にも読み取り結果を「捨てる」ためにgetしないとなりません(PIOステートマシンのFIFOを空けるためのダミー読み出し。)
ズボラなコードのお陰でPIOステートマシンのコードは非常に簡単となりました。
どちらかというと、PIOアセンブラよりも、PIOとのインタフェースを設定するためのAPIの方がこんがらがります。APIのマニュアルページへのリンクは以下に。
SDK Documentation >> sm_config
SDK Documentation >> hardware_pio
動作確認
書き込んだテストデータがそのまま読めるというだけの確認ですが、以下のように動作確認できました。お約束どおり以下のような対応関係のとれた読み書きができるようになる前に問題発覚しており、波形みてPIOアセンブラのコードに手を入れてます。オシロスコープはPIOアセンブラのデバッグの友。
Pico三昧(5) Pico C/C++ SDKでS93C46接続その1、ソフト制御 へ戻る
Pico三昧(7) Pico C/C++ SDKでinterp その1、popとpeek へ進む
PIOステートマシン、アセンブラソース、”S93C46PIO.pio”
; S93C46PIO ; SK (18) ... side-set pin ; CS (19) ... side-set pin ; DI (20) ... OUT pin ; DO (21) ... IN pin ; Write/Read .program S93C46PIO .side_set 2 .wrap_target pull block side 0x0 set x, 24 side 0x2 loopw: out pins, 1 side 0x2 nop side 0x3 in pins, 1 side 0x3 jmp x-- loopw side 0x2 push block side 0x0 .wrap % c-sdk { #include "hardware/gpio.h" static inline void S93C46PIO_program_init(PIO pio, uint sm, uint offset, uint opin, uint ipin, uint spin) { pio_sm_config c = S93C46PIO_program_get_default_config(offset); sm_config_set_out_pins(&c, opin, 1); sm_config_set_in_pins(&c, ipin); sm_config_set_sideset_pins(&c, spin); sm_config_set_out_shift(&c, false, false, 0); // shift_left, no-autopull sm_config_set_in_shift(&c, false, false, 0); // shift_left, no-autopull pio_gpio_init(pio, opin); pio_gpio_init(pio, ipin); pio_gpio_init(pio, spin); pio_gpio_init(pio, spin+1); pio_sm_set_consecutive_pindirs(pio, sm, opin, 1, true); pio_sm_set_consecutive_pindirs(pio, sm, ipin, 1, false); pio_sm_set_consecutive_pindirs(pio, sm, spin, 2, true); sm_config_set_clkdiv(&c, 128); pio_sm_init(pio, sm, offset, &c); pio_sm_set_enabled(pio, sm, true); } static inline void S93C46PIO_program_stop(PIO pio, uint sm) { pio_sm_set_enabled(pio, sm, false); } %}
Cソース全文 “S93C46PIO.c”
// S93C46 PIO Control #include <stdio.h> #include "pico/stdlib.h" #include "hardware/pio.h" #include "S93C46PIO.pio.h" // S93C46 PINS #define SK (18) #define CS (19) #define DI (20) #define DO (21) #define MAX_ADR (64) int main() { uint32_t result; uint32_t dat = 0xF005; uint32_t adr = 0x1E; uint32_t temp = 0; stdio_init_all(); puts("S93C46 PIO Read/Write TEST 00."); PIO pio = pio0; uint sm = pio_claim_unused_sm(pio, true); pio_sm_restart(pio, sm); uint offset = pio_add_program(pio, &S93C46PIO_program); S93C46PIO_program_init(pio, sm, offset, DI, DO, SK); pio_sm_put(pio, sm, 0x98000000); // EWEN result = pio_sm_get_blocking(pio, sm); //Dummy Read for (int i=0; i<100; i++) { temp = 0xA0000000 | (adr << 23) | (dat << 7); printf("WRITE: 0x%08x ADR=0x%02x DAT=0x%04x\r\n", temp, adr, dat); pio_sm_put(pio, sm, temp); result = pio_sm_get_blocking(pio, sm); //Dummy Read sleep_ms(1000); temp = 0xC0000000 | (adr << 23) | (dat << 7); pio_sm_put(pio, sm, temp); result = (0xFFFF & pio_sm_get_blocking(pio, sm)); //Data Read printf("READ: 0x%08x DAT=0x%04x\r\n", temp, result); sleep_ms(1000); adr++; if (adr >= MAX_ADR) adr = 0; dat = 0xFFFF & (dat << 1); if (dat == 0) dat = 0xF005; } pio_sm_put(pio, sm, 0x80000000); // EWDS result = pio_sm_get_blocking(pio, sm); //Dummy Read 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(S93C46PIO 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(S93C46PIO S93C46PIO.c ) pico_set_program_name(S93C46PIO "S93C46PIO") pico_set_program_version(S93C46PIO "0.1") pico_enable_stdio_uart(S93C46PIO 0) pico_enable_stdio_usb(S93C46PIO 1) pico_generate_pio_header(S93C46PIO ${CMAKE_CURRENT_LIST_DIR}/S93C46PIO.pio) # Add the standard library to the build target_link_libraries(S93C46PIO pico_stdlib) # Add any user requested libraries target_link_libraries(S93C46PIO hardware_pio ) pico_add_extra_outputs(S93C46PIO)