#今回用意したもの
SPRESENSEメインボード[CXD5602PWBMAIN1]
https://www.switch-science.com/catalog/3900/
B-stem PDA01 SPRESENSE用機能拡張ボード
https://www.switch-science.com/catalog/5687/
今回は、単純にスイッチが欲しかっただけで、何でも良かったんだけど、
この基板は、スイッチが付いていたので、これを使いました。
#ブログ(元ネタ)
この記事は、以下のブログに過去に投稿したもののまとめです。
SubCoreでGPIOを制御する@SDK その1
http://spresense.livedoor.blog/archives/24401112.html
SubCoreでGPIOを制御する@SDK その2
http://spresense.livedoor.blog/archives/24409556.html
SubCoreでGPIOを制御する@SDK その3
http://spresense.livedoor.blog/archives/24417303.html
#はじめに
他のアプリケーションを実行している中、ある信号線が立ち上がったら、8us以内にある信号線を落とさなければいけなかったので、SubCoreでのGPIO制御をしてみることにしました。
SubCore側のコードで、スイッチの押下を検知し、LEDを点灯させることポーリングで行えば、
8us以内にGPIOのINからOUTへの反応が可能になります。
#サンプルプログラム
SubCoreを使うということで、Spresense SDKの中のサンプル
をいじって、スイッチの検知&LED点灯を行います。
#スイッチのGPIO
PDA01の回路図を見ると、
https://p-art.net/wp2/wp-content/uploads/2019/11/PDA01_kairozu.pdf
スイッチ5番が SPR_PWM3 につながっています。
ちなみに、Spresenseの拡張ボードのGPIOをみてみると
https://developer.sony.com/develop/spresense/docs/introduction_ja.html
D03につながっていることがわかります。
#LEDのGPIO
worker側にはLEDのユーティリティーもないので、LEDとGPIOの関係から調査します。
Spresense の Mainボードの回路図 を見ると、
https://github.com/sonydevworld/spresense-hw-design-files/raw/master/CXD5602PWBMAIN1/schematics/CXD5602PWBMAIN1_schematics.pdf
どれでも良かったんですが、
SPR_I2S1_LRCK
につながっているので、取り急ぎ、これを叩くことにします。
#GPIOの叩き方
現時点で、workerからGPIOを叩くユーティリティーもないので、ここは、コードを読み解いて実装します。
GPIOの制御は、
https://developer.sony.com/develop/spresense/docs/sdk_tutorials_ja.html#_system_gpiotool
こちらを読むとわかります。
board_gpio_config
で、pinの設定をして、
board_gpio_read
board_gpio_write
で、RWします。
MainCoreでの普通の使い方は、チュートリアルでわかります。
今回はこの中身を見て、レジスタアクセスの仕方を調べます。
spresense/nuttx/arch/arm/src/cxd56xx/cxd56_gpioif.c
の中を見ていくと、
cxd56_gpio_write と cxd56_gpio_read
によって、Read/Write がされており、その中の
regaddr と regval とが、レジスタアドレスと生値だとわかります。
#WokerでのGPIOの叩き方
workerでは、アドレスに直接アクセスするしかないので、
SPR_PWM3 と SPR_I2S1_LRCK へのアドレスが必要です。
spresense/nuttx/arch/arm/src/cxd56xx/hardware/cxd5602_topreg.h
をみると、
#define CXD56_TOPREG_GP_PWM3 (CXD56_TOPREG_BASE + 0x20c0)
#define CXD56_TOPREG_GP_I2S1_LRCK (CXD56_TOPREG_BASE + 0x216c)
だとわかります。
またこのレジスタの叩き方は、下記のLSIのユーザマニュアルに書いてあります。
https://sony-semicon.co.jp/products/smart-sensing/spresense/assets/images/CXD5602_user_manual.pdf
読み出すときは、最下位ビット を、書き込むときは、下位から9ビット目 を叩くことがわかりました。
GPIOの叩き方が分かったと同時に、アドレス空間を見る限り、どのコアでも同一にIOを制御できそうだということが分かったので、SubCoreでの制御が可能だと言ことがわかります。
#MainCoreプログラム
上記で書いたように
を使って確認します。このサンプルは、特にチュートリアルはないようなので、
Readmeを参考に動作させてみます。
https://github.com/sonydevworld/spresense/tree/master/examples/asmp/
このサンプルの詳しい説明は、今回の目的から外れるのでしないのですが、
> ./tools/config.py examples/asmp
でconfigでbuildすると、SubCoreのバイナリもRomdiskになって読み込めます。
その後、ロードして実行すると、MultiCore間で、
message queue、mutexのテストをして終了するサンプルです。
今回、これらの通信テストをせずに、スイッチ5を叩くとLED2が点灯するコードをSubCoreに書いて実行できればOKです。
まず、pinのConfigrationをMainCoreで行います。
examples/asmp/asmp_main.c ※Master側のMain Loop
に、pin制御をするためのincludeをします。
#include <arch/board/board.h>
#include <arch/chip/pins.h>
また、
int main(int argc, FAR char *argv[])
の中の 310行目
(void) run_worker(fullpath);
の前に、inputとoutputのpinを設定します。
今回は、inputをPIN_PWM3、outputをPIN_I2S1_LRCKとしたので、
board_gpio_config(PIN_PWM3, 0, true, false, PIN_PULLUP);
board_gpio_config(PIN_I2S1_LRCK, 0, false, false, PIN_FLOAT);
(void) run_worker(fullpath);
を追加します。
これで、pinのconfigができました。
※PDA01のスイッチは、開放が1、押下が0なので、pullupにしています。
#SubCoreプログラム
次にSubCore側で、inputされたら、直ちにoutputするコードを書きます。
examples/asmp/worker/hello/hello.c ※SubCore側のMain Loop
このファイルの
int main(void)
の前に、
レジスタ定義を追記します。
#define CXD56_SYS_MIRROR 0x04000000
#define CXD56_TOPREG_BASE (CXD56_SYS_MIRROR + 0x00100000)
#define CXD56_TOPREG_GP_PWM3 (CXD56_TOPREG_BASE + 0x20c0)
#define CXD56_TOPREG_GP_I2S1_LRCK (CXD56_TOPREG_BASE + 0x216c)
※諸々includeパスが通ってない場所なので、定義を直接書いてしまっています。
これで、Mapped IO のレジスタアドレスが定義できたので、
int main(void)
の先頭に、
volatile uint32_t* iadr = (uint32_t*)CXD56_TOPREG_GP_PWM3;
volatile uint32_t* oadr = (uint32_t*)CXD56_TOPREG_GP_I2S1_LRCK;
while(1){
if( (*iadr & 0x01) == 0){
break;
}
}
*oadr = 0x100;
while(1); /* exit代わり */
を記述することで、SubCore側で、スイッチ5の検出をするとLEDが点灯を点灯させるコードになりました。
#動作結果
動かすとこんな感じになります。オシロで見ても8us以内に反応できています!
※動画無くしちゃった…。