ラズパイPicoのPIO(Programable IO)、前回は任意のデータの8ビットパラレル出力を行ってみました。今回は、前回の8ビット出力に加えて、4ビットのパラレル入力を追加してみたいと思います。実験用に外付け回路を追加。でもまだまだPIOアセンブラは数行、極めてシンプルであります。
※「鳥なき里のマイコン屋」投稿順Indexはこちら
(末尾に実験に使用したソース類を掲げました。ラズパイPico用のツールチェーンはRaspberry Pi 4 —Raspberry Pi OS 32bit—上のものを使用し、PCからVS Codeでリモート接続して作業しています。なお、今回使用のソース類は前回のもののチョイ直しです。)
実験用の外付け回路
8ビットのパラレル出力、4ビットのパラレル入力を試験するにあたって外付けしたのは、以下のデバイスであります。
お馴染み、標準ロジックIC 74HC00 です。2入力のNAND回路を4回路搭載。14ピンのICです。接続図を先頭のアイキャッチ画像に貼り付けました。
-
- ラズパイPicoのGP2からGP9までの8本をNANDゲートの入力に接続
- NANDゲートの出力4本をラズパイPicoのGP10からGP13に接続
という具合。GP2からの8本にデータを出力すれば下から2ビットづつのNANDを取った結果がGP10からの4本に返ってきます。回路は極めて簡単、分かり易い思うのですが、いまどき流行らないパラレル接続、たった8ビットでも配線多いな。波形をとるための準備もあって、実際は配線類が醜いです。こんな感じ。
PIOアセンブラ本体部分
ソース全文を末尾に掲げましたが、肝心のPIO命令部分は以下だけです。
.wrap_target pull block out pins, 8 [8] in pins, 4 push block .wrap
-
- pull block でCPUからTx FIFOに書き込まれたデータをOSRに読み出す。FIFOが空なら待つ。なお、デフォルトでブロッキングだが、ここでは明示的に block と指示。
- out pins, 8 [8] で出力用にマップされているpins(端子)にOSRから8ビット分のデータを出力する。そのあと8サイクルの「待ち」を入れる。
- in pins, 4 で入力用のマップされているpins(端子)からISRより4ビット分のデータを入力する
- push block でRx FIFOにISRの内容を書き出す。FIFOが満杯なら待つ。なお、デフォルトでブロッキングだが、ここでは明示的に block と指示。
折角のPIOなのですが、この処理自体はCPUがFIFOを読み書きしてくれるタイミング依存です。意味的にはソフトウエアでGPIO操作をするのと大差ありません。CPUのタイミングに依存しないコードはまた次回かな。
PIOの初期化関数
static inline void pio002_program_init(PIO pio, uint sm, uint offset, uint opin, uint ipin) { pio_sm_config c = pio002_program_get_default_config(offset); sm_config_set_out_pins(&c, opin, 8); sm_config_set_out_shift(&c, true, false, 0); // shift_right, no-autopull sm_config_set_in_pins(&c, ipin); sm_config_set_in_shift(&c, false, false, 0); // shift_left, no-autopush for (uint pidx=opin; pidx < (opin + 8); pidx++) { pio_gpio_init(pio, pidx); } for (uint pidx=ipin; pidx < (ipin + 4); pidx++) { pio_gpio_init(pio, pidx); gpio_pull_up(pidx); } pio_sm_set_consecutive_pindirs(pio, sm, opin, 8, true); pio_sm_set_consecutive_pindirs(pio, sm, ipin, 4, false); sm_config_set_clkdiv(&c, 128); pio_sm_init(pio, sm, offset, &c); pio_sm_set_enabled(pio, sm, true); }
前回からすると少し行数増えました。MicroPythonだとこのあたりを1行で指定できるので楽なのですが、C/C++では個別に設定しないとなりませぬ、メンドイ。入出力の端子を設定するのが主な作業です。今回、ツボにハマったのが、入出力の「シフト」の指定です。FIFOへのPUSH/POPは符号なし32ビット値ですが、今回とりあつかっているのは出力8ビット、入力4ビットです。LSBが右にあるとして、それら入出力の値は右詰めでおきたいです。
-
- 出力の場合は、OSRに右詰でおいて右シフトすれば、GP2から順番にデータがアサインできました。
- 入力の場合は、ISRに左シフトで読み込めば、右詰めデータになり、そのときLSBがGP10になりました。
バカなので、最初、ぼんやりと入出力とも右シフト指定してしまったので、そのときは入力ビットがMSB側に入ってしまいました。下のビットをいくら見てもゼロ入力、おかしい入力できてないとアセリました。ピンとMSB、LSBの対応関係をちゃんと確かめろよ、自分。
なお、上記コードではpio_gpio_init()関数で出力だけでなく、入力端子までPIOに向けていますが、ドキュメントを読む限り入力時の指定は不要です。ここは念のため。また入力端子にプルアップまで指定してますが、外付け回路は7400なので不必要です。これまた惰性で念のため。
実行結果
stdioに入出力をダンプした結果がこちら。testPatternの方が出力した8ビット値(HEX)、Resultの方が入力した4ビット値(HEX下一桁有効。)
Start PIO002. testPattern: 00 Result: 0f testPattern: 01 Result: 0f testPattern: 02 Result: 0f testPattern: 03 Result: 0e testPattern: 04 Result: 0f ~途中略~ testPattern: fe Result: 01 testPattern: ff Result: 00 End of Execution.
最初のところオール0出力で、結果は f と入力オール1、期待と一致。testPattern: 03にて、ようやく下2ビット出力0b11となったところで、Result: eと下1ビット入力0となりました。ちゃんとNAND動作してるようです。当然か。
一応、パラレル入出力できましたが、今回のPIO SMはCPUを待って動いているので、PIOのご利益があまりないです。次はPIOを勝手に走らせて、CPUのタイミングには依存しないようにしてみたいと思います。
鳥なき里のマイコン屋(135) ラズパイPico、C/C++SDKでPIO、多ビット出力 に戻る
鳥なき里のマイコン屋(137) ラズパイPico、C/C++SDKでPIO、サイド出力 に進む
実験に使用した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(pio002 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(pio002 pio002.c ) pico_set_program_name(pio002 "pio002") pico_set_program_version(pio002 "0.1") pico_enable_stdio_uart(pio002 1) pico_enable_stdio_usb(pio002 0) pico_generate_pio_header(pio002 ${CMAKE_CURRENT_LIST_DIR}/pio002.pio) # Add the standard library to the build target_link_libraries(pio002 pico_stdlib) # Add any user requested libraries target_link_libraries(pio002 hardware_pio ) pico_add_extra_outputs(pio002)
実験に使用した PIO アセンブラソース
; PIO002 ; GPIO output/input test .program pio002 .wrap_target pull block out pins, 8 [8] in pins, 4 push block .wrap % c-sdk { #include "hardware/gpio.h" static inline void pio002_program_init(PIO pio, uint sm, uint offset, uint opin, uint ipin) { pio_sm_config c = pio002_program_get_default_config(offset); sm_config_set_out_pins(&c, opin, 8); sm_config_set_out_shift(&c, true, false, 0); // shift_right, no-autopull sm_config_set_in_pins(&c, ipin); sm_config_set_in_shift(&c, false, false, 0); // shift_left, no-autopush for (uint pidx=opin; pidx < (opin + 8); pidx++) { pio_gpio_init(pio, pidx); } for (uint pidx=ipin; pidx < (ipin + 4); pidx++) { pio_gpio_init(pio, pidx); gpio_pull_up(pidx); } pio_sm_set_consecutive_pindirs(pio, sm, opin, 8, true); pio_sm_set_consecutive_pindirs(pio, sm, ipin, 4, false); sm_config_set_clkdiv(&c, 128); pio_sm_init(pio, sm, offset, &c); pio_sm_set_enabled(pio, sm, true); } static inline void pio002_program_stop(PIO pio, uint sm) { pio_sm_set_enabled(pio, sm, false); } %}
実験に使用したCソースコード
#include <stdio.h> #include "pico/stdlib.h" #include "hardware/gpio.h" #include "hardware/pio.h" #include "pio002.pio.h" #define OPINS (2) #define IPINS (10) int main() { uint32_t result; int testPat=0; stdio_init_all(); printf("Start PIO002.\r\n"); for (int i=OPINS; i<(OPINS+8); i++) { gpio_init(i); gpio_set_dir(i, GPIO_OUT); gpio_put(i, 1); } PIO pio = pio0; uint sm = pio_claim_unused_sm(pio, true); pio_sm_restart(pio, sm); uint offset = pio_add_program(pio, &pio002_program); pio002_program_init(pio, sm, offset, OPINS, IPINS); while(testPat < 256) { printf("testPattern: %02x\r\n", testPat); pio_sm_put(pio, sm, testPat++); result = pio_sm_get_blocking(pio, sm); printf("Result: %02x\r\n", (result & 0xFF)); sleep_ms(2000); } pio002_program_stop(pio, sm); printf("End of Execution.\r\n"); return 0; }