Pico三昧(6) Pico C/C++SDKでS93C46接続その2、PIO制御

Joseph Halfmoon

前回は3線式シリアルE2PROM S93C46をソフトウエア制御で読み書きしてみました。今回は、ラズパイPicoの特長であるPIO(Programmable IO)を使って同じことを行ってみます。PIOステートマシンのアセンブラを書くのも大分慣れた気もしますが、最後は波形を見ないと安心できないんだ,、これが。

(末尾に実験に使用したソース全文を掲げました。なお、ソースのビルドとデバッグの母艦としてRaspberry Pi 4 model Bを使用しております。)

PIO使用のプロジェクトの作成

例によって Raspberry Pi Pico Project Generator を使えば、PIO使用のプロジェクトの作成は簡単です。以下GUIモードの設定画面では、

Library Options >> PIO interface

にチェックを入れればOKっと。

ProjS93C46PIO
上記で、作成される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が逆転するので、名前がよじれるのは致し方ありません。

RPiPico_S93C46_PIO_Schematic
PIOステートマシンのプログラミング

ソースコード全文を末尾に掲げましたが、今回作成したPIOステートマシンのコードは、

リードもライトも、ついでに書き許可、不許可も1個で全部OK

な、ズボラなコードとしました。そのためPIOにputするデータは

  1. ストップビット1ビット(常に1)
  2. コマンドビット2ビット
  3. アドレス6ビット
  4. 書き込みデータ16ビット(読み出しや書き込み許可時にはオール0でよい)
  5. 埋め草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アセンブラのデバッグの友。

Result23線式シリアルE2PROMの読み書き、多分OKかな。

Pico三昧(5) Pico C/C++ SDKでS93C46接続その1、ソフト制御 へ戻る

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)