記事の概要
nRF connect SDKを用いてGPIO制御をする際、デバイスツリーを使用する方法が推奨されています。
その方法については Nordic DevAcademyで詳しく解説されています。
ですが、本記事では、デバイスツリーを使用せずにGPIO制御する方法を紹介したいと思います。
本記事では nRF54L15の開発ボードを使用します。(無線機能を使用しないので認証については問題ありません。)
自分用のメモ程度に書くので私にとって自明なことは説明を省略しますが、もし分かりにくいところがあれば説明を追加するので、お気軽にコメントください
nRF54L15のGPIO
nRF54l15のGPIOはGPIO0、GPIO1、GPIO2の3つがあり、合計35個のGPIO端子が存在します。
- GPIO0(P0)
- P0.00 ~ P0.06 の7ピン(0番~6番)
- GPIO1(P1)
- P1.00 ~ P1.16 の17ピン(0番~16番)
- GPIO2(P2)
- P2.00 ~ P2.10 の11ピン(0番~10番
ピン番号は GPIO1については P1.nならば 32+nになります。
ピン番号は GPIO1については P2.nならば 64+nになります。
ただし、GPIO2グループはGPIOTEや割り込みに対応しておらず、通常のGPIO入出力としてのみ利用できます。
よってウェイクアップやピンセンス機能はないので、割込み制御で使用することはできません。
デバイス有効化
デバイスは以下のように有効化します。
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
#define GPIO0_NODE DT_NODELABEL(gpio0)
#define GPIO1_NODE DT_NODELABEL(gpio1)
#define GPIO2_NODE DT_NODELABEL(gpio2)
void some_function(void) {
const struct device *gpio0_dev = DEVICE_DT_GET(GPIO0_NODE);
if (!device_is_ready(gpio0_dev)) {
printk("Error:gpio_dev is NULL\n");
return;
}
const struct device *gpio1_dev = DEVICE_DT_GET(GPIO1_NODE);
if (!device_is_ready(gpio1_dev)) {
printk("Error:gpio_dev is NULL\n");
return;
}
const struct device *gpio2_dev = DEVICE_DT_GET(GPIO2_NODE);
if (!device_is_ready(gpio2_dev)) {
printk("Error:gpio_dev is NULL\n");
return;
}
出力
関数gpio_pin_configure
で出力端子設定にして、関数gpio_pin_set
やgpio_pin_toggle
でHigh/Low出力にします。
各関数の詳細は説明せず、実際に開発ボードで出力できるサンプル(必要なインクルードファイルの一部は省略)を以下に例示します。
関数の使い方は以下のサンプルを見れば分かると思いますが、不明点がありましたらお気軽にご質問ください。
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
#define LED0 (9) // P2.09 = 64+9 = 73
#define LED1 (10) // P1.10 = 32+10 = 42
#define LED2 (7) // P2.07 = 64+7 = 71
#define LED3 (14) // P1.14 = 32+14 = 46
#define GPIO0_NODE DT_NODELABEL(gpio0)
#define GPIO1_NODE DT_NODELABEL(gpio1)
#define GPIO2_NODE DT_NODELABEL(gpio2)
void gpio_output(void)
{
const struct device *gpio_dev[3] = { DEVICE_DT_GET(GPIO0_NODE), DEVICE_DT_GET(GPIO1_NODE), DEVICE_DT_GET(GPIO2_NODE) } ;
if (!device_is_ready(gpio_dev[0])) {
printk("Error:gpio_dev is NULL\n");
return;
}
if (!device_is_ready(gpio_dev[1])) {
printk("Error:gpio_dev is NULL\n");
return;
}
if (!device_is_ready(gpio_dev[2])) {
printk("Error:gpio_dev is NULL\n");
return;
}
gpio_pin_set(gpio_dev[2], LED0, 1);
gpio_pin_configure(gpio_dev[2], LED0, GPIO_OUTPUT_ACTIVE);
gpio_pin_set(gpio_dev[1], LED1, 1);
gpio_pin_configure(gpio_dev[1], LED1, GPIO_OUTPUT_ACTIVE);
gpio_pin_set(gpio_dev[2], LED2, 1);
gpio_pin_configure(gpio_dev[2], LED2, GPIO_OUTPUT_ACTIVE);
gpio_pin_set(gpio_dev[1], LED3, 1);
gpio_pin_configure(gpio_dev[1], LED3, GPIO_OUTPUT_ACTIVE);
for (int i=0; i<4; i++) {
printk("LED Debug=%d\n", i);
k_msleep(500);
gpio_pin_toggle(gpio_dev[2], LED0);
gpio_pin_toggle(gpio_dev[1], LED1);
gpio_pin_toggle(gpio_dev[2], LED2);
gpio_pin_toggle(gpio_dev[1], LED3);
}
}
入力と割込み
関数gpio_pin_configure
で入力端子設定にして、関数gpio_pin_get
で入力状態を取得します
割込みは、インスタンスをgpio_callback
で定義し、関数gpio_init_callback
で設定し、gpio_add_callback
で割込み時に呼ばれる割込み処理関数を登録します。
割込み設定では、デバイスにGPIO0とGPIO1のどちらを使用しているかを区別するようにしてください。
割込み関数では、引数に割込み用入力端子の入力状態データpins
がありますが、これは割り込み用入力端子の値のみをマスクしたデータなのでご注意ください。
GPIOn全体の端子状態を知りたい場合は、関数gpio_port_get_raw
を使用してください。
関数の使い方は以下のサンプルを見れば分かると思いますが、不明点がありましたらお気軽にご質問ください。
また、以下のサンプルはチャタリング対策しておらず、実用には耐えないのでご注意ください。
実際の製品に応用する際は、割込み処理内ではタイマー起動だけを行い、タイマー割込み処理で入力端子が割込み発生時と一致していることを再確認してから、実行したい割込み処理を行うようにしてください。
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
#define LED0 (9) // P2.09 = 64+9 = 73
#define LED1 (10) // P1.10 = 32+10 = 42
#define LED2 (7) // P2.07 = 64+7 = 71
#define LED3 (14) // P1.14 = 32+14 = 46
#define Button0 (13) // P1.13 = 32+13 = 45
#define Button1 (9) // P1.09 = 32+9 = 41
#define Button2 (8) // P1.08 = 32+8 = 40
#define Button3 (4) // P0.04 = 0+4 = 4
#define GPIO0_NODE DT_NODELABEL(gpio0)
#define GPIO1_NODE DT_NODELABEL(gpio1)
#define GPIO2_NODE DT_NODELABEL(gpio2)
static struct gpio_callback button_cb_data;
static struct gpio_callback button_cb_data2;
void button_pressed(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
{
const struct device *gpio0_dev = DEVICE_DT_GET(GPIO0_NODE);
if (!device_is_ready(gpio0_dev)) {
printk("Error:gpio_dev is NULL\n");
return;
}
const struct device *gpio1_dev = DEVICE_DT_GET(GPIO1_NODE);
if (!device_is_ready(gpio1_dev)) {
printk("Error:gpio_dev is NULL\n");
return;
}
/* Manually get pin information test */
uint32_t get_pins;
uint32_t get_pins_buffer[3];
gpio_port_get_raw(dev, &get_pins);
gpio_port_get_raw(gpio0_dev, &get_pins_buffer[0]);
gpio_port_get_raw(gpio1_dev, &get_pins_buffer[1]);
printk("pins=%d, get_pins=%d, get_pins_buffer=%d, %d, %d \n", pins, get_pins, get_pins_buffer[0], get_pins_buffer[1], get_pins_buffer[2]);
uint32_t masked_pins = 0;
int val = gpio_pin_get(dev, pins);
printk("Input Interrupt, pin=%d, val=%d\n", pins, val);
if (pins & 1 << Button0) {
masked_pins = get_pins & (1 << Button0);
printk("Button0, masked_pins=%d\n", masked_pins);
if (val <= 0) {
gpio_pin_toggle(gpio2_dev, LED0);
}
} else if (pins & (1 << Button1)) {
masked_pins = get_pins & (1 << Button1);
printk("Button1, masked_pins=%d\n", masked_pins);
if (val <= 0) {
gpio_pin_toggle(gpio1_dev, LED1);
}
} else if (pins & (1 << Button2)) {
masked_pins = get_pins & (1 << Button2);
printk("Button2, masked_pins=%d\n", masked_pins);
if (val <= 0) {
gpio_pin_toggle(gpio2_dev, LED2);
}
} else if (pins & (1 << Button3)) {
masked_pins = get_pins & (1 << Button3);
printk("Button3, masked_pins=%d\n", masked_pins);
if (val <= 0) {
gpio_pin_toggle(gpio1_dev, LED3);
}
} else {
//
}
}
void gpio_input(void)
{
const struct device *gpio0_dev = DEVICE_DT_GET(GPIO0_NODE);
if (!device_is_ready(gpio0_dev)) {
printk("Error:gpio_dev is NULL\n");
return;
}
const struct device *gpio1_dev = DEVICE_DT_GET(GPIO1_NODE);
if (!device_is_ready(gpio1_dev)) {
printk("Error:gpio_dev is NULL\n");
return;
}
// Input + Pull-up + Active-low
gpio_pin_configure(gpio1_dev, Button0, GPIO_INPUT | GPIO_PULL_UP);
gpio_pin_configure(gpio1_dev, Button1, GPIO_INPUT | GPIO_PULL_UP);
gpio_pin_configure(gpio1_dev, Button2, GPIO_INPUT | GPIO_PULL_UP);
gpio_pin_configure(gpio0_dev, Button3, GPIO_INPUT | GPIO_PULL_UP);
// Interrupt setting (falling edge)
gpio_pin_interrupt_configure(gpio1_dev, Button0, GPIO_INT_EDGE_TO_ACTIVE);
gpio_pin_interrupt_configure(gpio1_dev, Button1, GPIO_INT_EDGE_TO_ACTIVE);
gpio_pin_interrupt_configure(gpio1_dev, Button2, GPIO_INT_EDGE_TO_ACTIVE);
gpio_pin_interrupt_configure(gpio0_dev, Button3, GPIO_INT_EDGE_TO_ACTIVE);
// Register callback
gpio_init_callback(&button_cb_data, button_pressed, BIT(Button0) | BIT(Button1) | BIT(Button2));
gpio_add_callback(gpio1_dev, &button_cb_data);
gpio_init_callback(&button_cb_data2, button_pressed, BIT(Button3));
gpio_add_callback(gpio0_dev, &button_cb_data2);
}