スレッド
Zephyr OSは基本のスレッドキューとして
・システムキュー
・Main()キュー
の2つのスレッドキューを標準で持っています。つまり、2つまでのワークハンドラーを並列に実行できます。また、この二つのスレッドは優先順位が高いのでほとんどズレることもありません。
NCSというよりはZephyr
スレッドなのでnRF Connect SDKの話というよりはZephyrの話になるのですが、nRF Connect SDKを使うためにはZephyrのことも知っておかないといけないので一応NCSの話題ということにしておきます(笑)
実装
実際に実装するにあたっては、評価ボード上の4つのLEDをそれぞれのスレッドで個別に動かしてみようと思います。ちゃんとタイムスライスで切り替わっている(当たり前)が確認できます。
なお、このサンプルはスレッドの使い方としては決してよい書き方ではないですが、スレッドというのはこうやって動くんだということを実感するためにあえてwhile句を使って実装しています。シングルタスクOSだとwhile句の無限ループからは絶対に出られないので切り替わっていることが分かるからです。
/*
* Copyright (c) 2016 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
# include <kernel_structs.h>
# include <zephyr.h>
# include <device.h>
# include <devicetree.h>
# include <drivers/gpio.h>
/* 1000 msec = 1 sec */
# define SLEEP_TIME_MS 1000
/* The devicetree node identifier for the "led0" alias. */
# define LED0_NODE DT_ALIAS(led0)
# define LED1_NODE DT_ALIAS(led1)
# define LED2_NODE DT_ALIAS(led2)
# define LED3_NODE DT_ALIAS(led3)
# if DT_NODE_HAS_STATUS(LED0_NODE, okay)
# define LED0 DT_GPIO_LABEL(LED0_NODE, gpios)
# define PIN0 DT_GPIO_PIN(LED0_NODE, gpios)
# define FLAGS0 DT_GPIO_FLAGS(LED0_NODE, gpios)
# else
/* A build error here means your board isn't set up to blink an LED. */
# error "Unsupported board: led0 devicetree alias is not defined"
# define LED0 ""
# define PIN0 0
# define FLAGS0 0
# endif
# if DT_NODE_HAS_STATUS(LED1_NODE, okay)
# define LED1 DT_GPIO_LABEL(LED1_NODE, gpios)
# define PIN1 DT_GPIO_PIN(LED1_NODE, gpios)
# define FLAGS1 DT_GPIO_FLAGS(LED1_NODE, gpios)
# else
/* A build error here means your board isn't set up to blink an LED. */
# error "Unsupported board: led0 devicetree alias is not defined"
# define LED1 ""
# define PIN1 0
# define FLAGS1 0
# endif
# if DT_NODE_HAS_STATUS(LED2_NODE, okay)
# define LED2 DT_GPIO_LABEL(LED2_NODE, gpios)
# define PIN2 DT_GPIO_PIN(LED2_NODE, gpios)
# define FLAGS2 DT_GPIO_FLAGS(LED2_NODE, gpios)
# else
/* A build error here means your board isn't set up to blink an LED. */
# error "Unsupported board: led0 devicetree alias is not defined"
# define LED2 ""
# define PIN2 0
# define FLAGS2 0
# endif
# if DT_NODE_HAS_STATUS(LED3_NODE, okay)
# define LED3 DT_GPIO_LABEL(LED3_NODE, gpios)
# define PIN3 DT_GPIO_PIN(LED3_NODE, gpios)
# define FLAGS3 DT_GPIO_FLAGS(LED3_NODE, gpios)
# else
/* A build error here means your board isn't set up to blink an LED. */
# error "Unsupported board: led0 devicetree alias is not defined"
# define LED3 ""
# define PIN3 0
# define FLAGS3 0
# endif
K_THREAD_STACK_DEFINE(application_stack_area1, 1024);
K_THREAD_STACK_DEFINE(application_stack_area2, 1024);
static struct k_work_q user_queue1, user_queue2;
const struct device *dev_led[4];
void my_work_handler1(struct k_work *work)
{
while (1)
{
gpio_pin_set(dev_led[1], PIN1, 1);
k_msleep(200);
gpio_pin_set(dev_led[1], PIN1, 0);
k_msleep(1800);
}
}
K_WORK_DEFINE(my_work1, my_work_handler1);
void my_work_handler2(struct k_work *work)
{
while (1)
{
gpio_pin_set(dev_led[2], PIN2, 1);
k_msleep(500);
gpio_pin_set(dev_led[2], PIN2, 0);
k_msleep(500);
}
}
K_WORK_DEFINE(my_work2, my_work_handler2);
void my_work_handler3(struct k_work *work)
{
while (1)
{
gpio_pin_set(dev_led[3], PIN3, 1);
k_msleep(800);
gpio_pin_set(dev_led[3], PIN3, 0);
k_msleep(800);
}
}
K_WORK_DEFINE(my_work3, my_work_handler3);
void main(void)
{
bool led_is_on = true;
int ret;
dev_led[0] = device_get_binding(LED0);
dev_led[1] = device_get_binding(LED1);
dev_led[2] = device_get_binding(LED2);
dev_led[3] = device_get_binding(LED3);
if ((dev_led[0] == NULL) || (dev_led[1] == NULL) || (dev_led[2] == NULL) || (dev_led[3] == NULL))
{
printk("LEDs not found");
return;
}
ret = gpio_pin_configure(dev_led[0], PIN0, GPIO_OUTPUT_ACTIVE | FLAGS0);
ret += gpio_pin_configure(dev_led[1], PIN1, GPIO_OUTPUT_ACTIVE | FLAGS1);
ret += gpio_pin_configure(dev_led[2], PIN2, GPIO_OUTPUT_ACTIVE | FLAGS2);
ret += gpio_pin_configure(dev_led[3], PIN3, GPIO_OUTPUT_ACTIVE | FLAGS3);
if (ret < 0)
{
return;
}
k_work_queue_start(&user_queue1, application_stack_area1, K_THREAD_STACK_SIZEOF(application_stack_area1), K_PRIO_PREEMPT(6), NULL);
k_work_queue_start(&user_queue2, application_stack_area2, K_THREAD_STACK_SIZEOF(application_stack_area2), K_PRIO_PREEMPT(6), NULL);
k_work_submit(&my_work1);
k_work_submit_to_queue(&user_queue1, &my_work2);
k_work_submit_to_queue(&user_queue2, &my_work3);
while (1)
{
gpio_pin_set(dev_led[0], PIN0, (int)led_is_on);
led_is_on = !led_is_on;
k_msleep(SLEEP_TIME_MS);
}
}
このサンプルに関してはprj.confの記載は不要です。
Appendix
ユーザースレッドは割り込みプライオリティが低いため、長時間動かしていると徐々にずれてきます。