Joseph Halfmoon
ラズパイPicoのPIO(Programable IO)、前回は任意のデータの8ビットパラレル出力 を行ってみました。今回は、前回の8ビット出力に加えて、4ビットのパラレル入力を追加してみたいと思います。実験用に外付け回路を追加。でもまだまだPIOアセンブラは数行、極めてシンプルであります。
※「鳥なき里のマイコン屋」投稿順Indexはこちら
(末尾に実験に使用したソース類を掲げました。ラズパイPico用のツールチェーンはRaspberry Pi 4 —Raspberry Pi OS 32bit—上のものを使用し、PCからVS Codeでリモート接続して作業しています。なお、今回使用のソース類は前回のもののチョイ直しです。)
実験用の外付け回路
8ビットのパラレル出力、4ビットのパラレル入力を試験するにあたって外付けしたのは、以下のデバイスであります。
東芝デバイス&ストレージ社 TC74HC00AP
お馴染み、標準ロジック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
.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 ) ;
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_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_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_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
# 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
pico_add_extra_outputs(pio002)
# 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)
# 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 アセンブラソース
#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) ;
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) ;
; 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);
}
%}
; 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 "hardware/gpio.h"
#include "hardware/pio.h"
printf ( "Start PIO002.\r\n" ) ;
for ( int i=OPINS; i <( OPINS+8 ) ; i++ ) {
gpio_set_dir ( i, GPIO_OUT ) ;
uint sm = pio_claim_unused_sm ( pio, true ) ;
uint offset = pio_add_program ( pio, &pio002_program ) ;
pio002_program_init ( pio, sm, offset, OPINS, IPINS ) ;
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 )) ;
pio002_program_stop ( pio, sm ) ;
printf ( "End of Execution.\r\n" ) ;
#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;
}
#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;
}