
前回、マルチコアの両方に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)
