ZephyrRTOSでSTM32マイコン再入門~タイマ割り込みでLED点滅制御~
はじめに
これまでWindows上にZephyrRTOSの開発環境を構築し、NucleoF103RBボードを用いて学習を進めてきた。
今回は、タイマ割り込みを使ってNucleoF103RBのLEDを点滅制御するサンプルを作成し、タイマ動作の仕組みの理解に取り組みたい。
コード
今回はmain.cの実装に加えて、zephyr/prj.confの編集、そしてzephyr/boards/nucleo_f103rb.overlay のデバイスツリー定義が必要である。
main.cのコード全体
#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/counter.h>
#define LED_NODE DT_ALIAS(led0)
static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED_NODE, gpios);
#define TIMER_NODE DT_NODELABEL(counter3)
const struct device *counter_dev = DEVICE_DT_GET(TIMER_NODE);
static bool led_on = false;
static bool flag_tim = false;
static void timer_callback(const struct device *dev, uint8_t chan_id,
uint32_t ticks, void *user_data)
{
ARG_UNUSED(dev);
ARG_UNUSED(chan_id);
ARG_UNUSED(ticks);
ARG_UNUSED(user_data);
flag_tim = true;
struct counter_alarm_cfg alarm_cfg = {
.flags = 0,
.ticks = counter_us_to_ticks(counter_dev, 500 * 1000), // 500ms
.callback = timer_callback,
.user_data = NULL,
};
counter_set_channel_alarm(counter_dev, 0, &alarm_cfg);
}
int main(void)
{
if (!device_is_ready(counter_dev)) {
printk("Counter device not ready\n");
return -1;
}
if (!gpio_is_ready_dt(&led)) {
printk("LED device not ready\n");
return -1;
}
printk("Debug start\n");
gpio_pin_configure_dt(&led, GPIO_OUTPUT_INACTIVE);
counter_start(counter_dev);
struct counter_alarm_cfg alarm_cfg = {
.flags = 0,
.ticks = counter_us_to_ticks(counter_dev, 500 * 1000), // 500ms
.callback = timer_callback,
.user_data = NULL,
};
counter_set_channel_alarm(counter_dev, 0, &alarm_cfg);
while (1) {
if (flag_tim == true){
flag_tim = false;
led_on = ! led_on;
gpio_pin_set_dt(&led, led_on);
}
k_sleep(K_MSEC(10));
}
return 0;
}
CONFIG_COUNTER=y
&timers3 {
status = "okay";
st,prescaler = <10000>;
counter3: counter {
compatible = "st,stm32-counter";
status = "okay";
};
};
zephyrで用意されるデバイスツリーに対して独自の定義を追加する場合は[ボード名].overlayという名前のファイルを作成し、そこに追記する。そしてzephyr/boardsディレクトリに配置することでビルド時に自動的に読み込まれる。
詳細は以下のドキュメントを参照されたい。
デバイスツリーでのタイマ設定
#define TIMER_NODE DT_NODELABEL(counter3)
const struct device *counter_dev = DEVICE_DT_GET(TIMER_NODE);
nucleo_f103rb.overlayで定義したcounter3ノードを参照している。
プリスケーラを10000に設定しているため、タイマのクロック周波数は72MHz/10000+1≒7.2kHzとなる。
タイマ割り込みコールバック関数
static bool flag_tim = false;
static void timer_callback(const struct device *dev, uint8_t chan_id,
uint32_t ticks, void *user_data)
{
ARG_UNUSED(dev);
ARG_UNUSED(chan_id);
ARG_UNUSED(ticks);
ARG_UNUSED(user_data);
flag_tim = true;
struct counter_alarm_cfg alarm_cfg = {
.flags = 0,
.ticks = counter_us_to_ticks(counter_dev, 500 * 1000), // 500ms
.callback = timer_callback,
.user_data = NULL,
};
counter_set_channel_alarm(counter_dev, 0, &alarm_cfg);
}
タイマー割り込みが発生すると呼び出されるコールバック関数を定義している。
割り込みハンドラ内では極力処理を軽くするため、フラグを立てるだけにしている。
また、今回のプログラムでは繰り返し割り込みを発生させたいので、コールバック関数内で再度カウンタのアラームを設定している。
メイン関数内での初期化処理
counter_start(counter_dev);
struct counter_alarm_cfg alarm_cfg = {
.flags = 0,
.ticks = counter_us_to_ticks(counter_dev, 500 * 1000), // 500ms
.callback = timer_callback,
.user_data = NULL,
};
counter_set_channel_alarm(counter_dev, 0, &alarm_cfg);
メイン関数内では、まずカウンタをスタートさせている。
そしてタイマー割り込みを発生させるためのアラームを設定している。
フラグのポーリング
while (1) {
if (flag_tim == true){
flag_tim = false;
led_on = ! led_on;
gpio_pin_set_dt(&led, led_on);
}
k_sleep(K_MSEC(10));
}
メインループ内でタイマー割り込みコールバック関数で立てられたフラグをポーリングしている。
フラグが立っていたらLEDの点灯/消灯を切り替えている。
おわりに
以上でZephyrRTOSを用いたSTM32マイコンのタイマ割り込みによるLED点滅制御の解説を終わる。
タイマ割り込みの仕組みを理解する一助となれば幸いである。
Discussion