前回、マルチコアの両方にGPIO割り込みを入れてみましたが、割り込み受付後のタイマ処理が上手く行きませんでした。暫定対策したものの、実はそれもダメダメでした。今回はタイマ処理が上手く行かなかった理由を調査。対策打って「安定して動いている」ように「今のところ」見えているところまでやってみました。継続試験中。
※「鳥なき里のマイコン屋」投稿順Indexはこちら
前回のおさらいをしておくと、意図したコードは以下のようでした。
-
- ラズパイPicoのコア0にはGPIO18番から割り込み入れる
- 上記の割り込みハンドラ内で、GPIO20番に接続しているLEDを点灯。また、1秒後にLEDを消灯するようにタイマアラームその0を仕掛ける。
- しかけたタイマアラームその0が発動したら、LEDを消灯させ、GPIO18番からの割り込みを再度許可する
- ラズパイPicoのコア1にはGPIO19番から割り込み入れる
- 上記の割り込みハンドラ内で、GPIO21番に接続しているLEDを点灯。また、1秒後にLEDを消灯するようにタイマアラームその1を仕掛ける。
- しかけたタイマアラームその1が発動したら、LEDを消灯させ、GPIO19番からの割り込みを再度許可する
しかし、どうもタイマアラームその1が意図したようには動かず、しかたなく「やっつけ」で禁断の「暫定対策」をしてみたあげくに傷を広げた、というのがここまでの状況でした。
タイマアラームその1が動作しなかった理由
タイマアラームは、2つハンドリングすることは可能な筈で、実際、その0,その1とも発動していました。しかし、
タイマアラームはコア0で処理されていました
デフォルトのアラーム・プールというものはコア0で初期化されており、他のコアからも仕掛けることはできるものの、実際にアラーム処理がされるときはコア0で処理されているようでした。このため、コア1で消灯するのではなく、コア0で消灯していました。すると消灯に引き続く割り込みの再設定はコア0で行われてしまいます。結果、コア1の割り込みは不許可となり、本来コア1でハンドリングする筈のGPIO19番の割り込みが、コア0に送られてしまっていました。ここには、割り込み受付時にGPIOの番号を見ないことがある、というドキュメントに書かれた問題が横たわっているようでした。
対策
コア1側のタイマアラームがコア1で処理されれば、意図通りの動作となる筈なので、
コア1用のアラーム・プールを増設
してみることにいたしました。デフォルトと増設のアラーム・プールはAPIの名前や引数から異なりますが、いたしかたありませぬ。コア1側でアラーム・プールを増設し、コア1の割り込みハンドラ内ではそちらの消灯アラームを使うことにいたしました。アラームに関するAPIについてのドキュメントは以下に
Raspberry Pi Pico SDK Documentation alarm
対策の結果
対策の結果、前回みられた、コア1側のLED(青)が点灯したまま消灯しない、といった問題は消えたようです。また、コア0とコア1の両方のスイッチを同時押ししても、どちらも点灯、そして1秒後に消灯と意図どおりの動きをみせています。修正いらい6時間ほど動かし続けており、気が付くたびにスイッチを押してみているのですが、問題は出ておりませぬ。
標準出力への動作状況報告の様子は以下に。
本当に、これで良いのかね。まあ、前回よりは進歩したのでいいか。適当な。
鳥なき里のマイコン屋(146) ラズパイPico、C/C++SDK、マルチコアで割り込み1 へ戻る
鳥なき里のマイコン屋(148) SAMD21マイコン搭載、Seeeduino XIAO到着 へ進む
実験に使用したRaspberry Pi Pico用の C ソースコード
#include <stdio.h> #include "pico/stdlib.h" #include "pico/multicore.h" #include "pico/time.h" #include "hardware/gpio.h" #include "hardware/irq.h" #define EXT_SW0 (18) #define EXT_SW1 (19) #define EXT_LED0 (20) #define EXT_LED1 (21) alarm_pool_t* core1Alarm; int64_t alarm_callback0(alarm_id_t id, void *user_data) { cancel_alarm(id); gpio_put(EXT_LED0, false); gpio_set_irq_enabled(EXT_SW0, GPIO_IRQ_EDGE_FALL, true); printf("alarm0: %d\r\n", sio_hw->cpuid); } int64_t alarm_callback1(alarm_id_t id, void *user_data) { alarm_pool_cancel_alarm(core1Alarm, id); gpio_put(EXT_LED1, false); gpio_set_irq_enabled(EXT_SW1, GPIO_IRQ_EDGE_FALL, true); printf("alarm1: %d\r\n", sio_hw->cpuid); } gpio_irq_callback_t gpio_callback0(uint gpio, uint32_t event) { gpio_set_irq_enabled(EXT_SW0, GPIO_IRQ_EDGE_FALL, false); printf("CORE 0 INTR!\r\n"); if (gpio == EXT_SW0) { gpio_put(EXT_LED0, true); add_alarm_in_ms(1000, alarm_callback0, NULL, false); } else { printf("CORE 0 received unexpected INTR.\r\n"); } } gpio_irq_callback_t gpio_callback1(uint gpio, uint32_t event) { gpio_set_irq_enabled(EXT_SW1, GPIO_IRQ_EDGE_FALL, false); printf("CORE 1 INTR!\r\n"); if (gpio == EXT_SW1) { gpio_put(EXT_LED1, true); alarm_pool_add_alarm_in_ms(core1Alarm, 1000, alarm_callback1, NULL, false); } else { printf("CORE 1 received unexpected INTR.\r\n"); } } void core1_task() { int core1Counter = 0; core1Alarm = alarm_pool_create(1, 4); gpio_init(EXT_SW1); gpio_init(EXT_LED1); gpio_set_dir(EXT_LED1, GPIO_OUT); gpio_set_dir(EXT_SW1, GPIO_IN); gpio_put(EXT_LED1, false); gpio_set_irq_enabled_with_callback(EXT_SW1, GPIO_IRQ_EDGE_FALL, true, gpio_callback1); while (true) { printf("CORE1: %d\r\n", ++core1Counter); sleep_ms(7777); } } int main() { int loopCounter = 0; stdio_init_all(); gpio_init(EXT_SW0); gpio_init(EXT_LED0); gpio_set_dir(EXT_LED0, GPIO_OUT); gpio_set_dir(EXT_SW0, GPIO_IN); gpio_put(EXT_LED0, false); gpio_set_irq_enabled_with_callback(EXT_SW0, GPIO_IRQ_EDGE_FALL, true, gpio_callback0); multicore_launch_core1(core1_task); while (true) { printf("CORE0: %d\r\n", ++loopCounter); sleep_ms(5000); } puts("NEVER!"); 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(multINTR 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(multINTR multINTR.c ) pico_set_program_name(multINTR "multINTR") pico_set_program_version(multINTR "0.1") pico_enable_stdio_uart(multINTR 0) pico_enable_stdio_usb(multINTR 1) # Add the standard library to the build target_link_libraries(multINTR pico_stdlib pico_time pico_multicore) pico_add_extra_outputs(multINTR)