跳转到内容

GPIO

F101 的 GPIO 和引脚复用(pinctrl)在同一个 PIO 模块里:6 个 bank(PA…PF),每个 bank 32 根线,每个 bank 一个中断。Zephyr 里把每个 bank 做成一个 GPIO 设备(gpioa…gpiof),接口是标准的 <zephyr/drivers/gpio.h>,驱动是 drivers/gpio/gpio_sunxi.c。

项目内容
设备gpioa、gpiob、gpioc、gpiod、gpioe、gpiof,每个 bank 32 个引脚
方向输入、输出,可选内部上拉或下拉
中断上升沿、下降沿、双沿、高电平、低电平
不支持开漏、开源输出(-ENOTSUP),gpio_pin_get_config

GPIO 涉及三个文件,从下到上依次是:

文件作用
dts/bindings/gpio/allwinner,sunxi-gpio.yaml绑定:规定 allwinner,sunxi-gpio 节点的属性
dts/riscv/allwinner/sun252i_f101.dtsiSoC 设备树:6 个 bank 的节点,默认都是 okay
boards/yuzukihd/yuzukineko/yuzukineko.dts板级设备树:用 gpio-leds、cd-gpios 这类属性引用具体的引脚

下面是 PB bank 的节点,其余 5 个 bank 只有寄存器地址、allwinner,bank 和中断号不同:

gpiob: gpio@2000030 {
/* 匹配驱动 gpio_sunxi.c 里的 DT_DRV_COMPAT allwinner_sunxi_gpio */
compatible = "allwinner,sunxi-gpio";
/* 这个 bank 的寄存器:基址 0x02000030,长度 0x30 */
reg = <0x02000030 0x30>;
/* PIO 寄存器布局类型。F1xx 系列用类型 0:bank 间隔 0x30 字节,中断寄存器在 0x200 之后 */
allwinner,pio-hw-type = <ALLWINNER_PIO_HW_TYPE0>;
/* bank 号:PA = 0,PB = 1,……,PF = 5。驱动靠它算出这个 bank 的中断寄存器位置 */
allwinner,bank = <1>;
/* 这是一个 GPIO 控制器,引用它的 gpios 属性有 2 个格子:引脚号、标志 */
gpio-controller;
#gpio-cells = <2>;
/* 引脚个数 */
ngpios = <32>;
/* 这个 bank 的中断:PLIC 的 69 号,优先级 1 */
interrupts = <69 1>;
interrupt-parent = <&plic>;
status = "okay";
};

六个 bank 的差别:

节点标签寄存器allwinner,bankPLIC 中断号
gpio@2000000gpioa0x02000000067
gpio@2000030gpiob0x02000030169
gpio@2000060gpioc0x02000060271
gpio@2000090gpiod0x02000090373
gpio@20000c0gpioe0x020000c0475
gpio@20000f0gpiof0x020000f0577

板级文件用 GPIO 的地方有两处。一处是 LED,另一处是 TF 卡的插卡检测:

/ {
aliases {
/* 应用里用 DT_ALIAS(led0) 取到 LED */
led0 = &led0;
};
leds {
compatible = "gpio-leds";
led0: led_0 {
/* &gpiod:PD bank;5:第 5 根线,即 PD5;高电平点亮 */
gpios = <&gpiod 5 GPIO_ACTIVE_HIGH>;
};
};
};
&smhc0 {
/* PF6 是 TF 卡插卡检测,低电平表示有卡,所以是 GPIO_ACTIVE_LOW */
cd-gpios = <&gpiof 6 GPIO_ACTIVE_LOW>;
};

gpios = <&gpiod 5 GPIO_ACTIVE_HIGH> 里 &gpiod 后面的两个数就是 #gpio-cells = <2> 规定的“引脚号”和“标志”。标志可以用的位在 include/zephyr/dt-bindings/gpio/gpio.h:GPIO_ACTIVE_LOW、GPIO_PULL_UP、GPIO_PULL_DOWN 等。

引脚不用作普通 GPIO,而是给外设用时,由 pinctrl 设定功能。yuzukineko.dts 里每个外设都有一组 pinctrl-0:

&pio {
/* UART1 控制台:PB00 是 TX,PB01 是 RX,复用功能 4 */
uart1_pins: uart1_pins {
uart1_tx_rx {
pinmux = <ALLWINNER_PINMUX(ALLWINNER_PIO_PB, 0, 4)
ALLWINNER_PINMUX(ALLWINNER_PIO_PB, 1, 4)>;
};
};
};
&uart1 {
pinctrl-0 = <&uart1_pins>;
pinctrl-names = "default";
};

ALLWINNER_PINMUX(bank, 引脚号, 复用功能) 的复用功能:0 是输入,1 是输出,2…15 是外设功能,14 是外部中断。每个引脚各功能是什么,见引脚复用表。可以写在同一个组里的属性:

属性含义
pinmux引脚和复用功能,每个格子一个引脚
bias-pull-up打开内部上拉
bias-pull-down打开内部下拉
bias-disable关闭上下拉
allwinner,drive-level驱动能力,0…3,不写就保持复位值
# Copyright (c) 2026 Yuzuki Tsuru
# SPDX-License-Identifier: Apache-2.0
description: |
Allwinner sunxi PIO GPIO controller.
Each DT gpio node covers one PIO bank (PA..PF), identified by the
'allwinner,bank' property. The 'allwinner,pio-hw-type' property selects
the PIO register layout used by the driver (see
include/zephyr/drivers/pinctrl/pinctrl_sunxi.h). Bank interrupts are
exposed as interrupt specifiers.
compatible: "allwinner,sunxi-gpio"
include: [gpio-controller.yaml, base.yaml]
properties:
reg:
required: true
interrupts:
required: true
allwinner,pio-hw-type:
required: true
type: int
description: |
PIO hardware layout type. See
include/zephyr/drivers/pinctrl/pinctrl_sunxi.h for the register
layout description of each type.
allwinner,bank:
required: true
type: int
description: |
PIO bank number of this controller (PA = 0, PB = 1, ...).
"#gpio-cells":
const: 2
gpio-cells:
- pin
- flags
属性必填含义
reg是这个 bank 的寄存器
interrupts是这个 bank 的中断
allwinner,pio-hw-type是PIO 寄存器布局,F101 是 ALLWINNER_PIO_HW_TYPE0
allwinner,bank是bank 号,PA = 0 … PF = 5
ngpios来自 gpio-controller.yaml引脚个数,32
#gpio-cells是,固定为 2引脚号、标志
选项默认含义
CONFIG_GPIO板级 defconfig 里是 y打开 GPIO 子系统。自己的应用里要写在 prj.conf
CONFIG_GPIO_SUNXIy(依赖 DT_HAS_ALLWINNER_SUNXI_GPIO_ENABLED)编译这个驱动,不用手动设置
CONFIG_GPIO_INIT_PRIORITYZephyr 默认GPIO 驱动的初始化优先级

下面每个接口一个小标题:原型、驱动的行为、一个完整的示例函数和它在板子上的输出。示例来自文末的完整演示程序,里面的 err() 把返回值转成名字。接口里的“电平”都是逻辑电平:设备树里写了 GPIO_ACTIVE_LOW 的引脚,gpio_pin_set_dt(spec, 1) 输出的是低电平。带 _raw 的接口不看这个标志。

演示用到 3 个引脚:

引脚位置用途
PD5板上的 LED(led0)输出
PB2排针上悬空的引脚输出,用 gpio_port_* 操作
PB3排针上悬空的引脚输入,用内部上下拉改变电平,触发中断

PB2 和 PB3 在演示的 app.overlay 里通过 zephyr,user 节点引用。这是应用自己给引脚取名字的标准办法,不用改板级文件。

int gpio_pin_configure_dt(const struct gpio_dt_spec *spec, gpio_flags_t extra_flags);

配置方向、初始电平和上下拉。

标志驱动的行为
GPIO_INPUT复用功能设为输入
GPIO_OUTPUT、GPIO_OUTPUT_INACTIVE、GPIO_OUTPUT_ACTIVE复用功能设为输出,并按初始电平先写数据寄存器再切成输出,输出口不会有毛刺
GPIO_PULL_UP、GPIO_PULL_DOWN打开对应的内部上拉或下拉;两个都不写则关闭上下拉
GPIO_OPEN_DRAIN、GPIO_OPEN_SOURCE返回 -ENOTSUP
/* gpio_pin_configure_dt: direction, initial level and pull resistor */
static void demo_pin_configure_dt(void)
{
int ret;
ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_INACTIVE);
printk("[configure] PD5 output, low = %s\n", err(ret));
ret = gpio_pin_configure_dt(&in, GPIO_INPUT | GPIO_PULL_UP);
printk("[configure] PB3 input with pull up = %s\n", err(ret));
ret = gpio_pin_configure_dt(&in, GPIO_INPUT | GPIO_PULL_DOWN);
printk("[configure] PB3 input with pull down = %s\n", err(ret));
ret = gpio_pin_configure_dt(&out, GPIO_OUTPUT | GPIO_OPEN_DRAIN);
printk("[configure] PB2 open drain output = %s\n", err(ret));
ret = gpio_pin_configure_dt(&out, GPIO_OUTPUT | GPIO_OPEN_SOURCE);
printk("[configure] PB2 open source output = %s\n", err(ret));
}
[configure] PD5 output, low = 0
[configure] PB3 input with pull up = 0
[configure] PB3 input with pull down = 0
[configure] PB2 open drain output = -ENOTSUP
[configure] PB2 open source output = -ENOTSUP
int gpio_pin_set_dt(const struct gpio_dt_spec *spec, int value);

设置输出的逻辑电平,1 是有效电平。

/* gpio_pin_set_dt: the logical level of an output */
static void demo_pin_set_dt(void)
{
int ret;
gpio_pin_configure_dt(&led, GPIO_OUTPUT_INACTIVE);
ret = gpio_pin_set_dt(&led, 1);
printk("[set] gpio_pin_set_dt(PD5, 1) = %s, register bit %d\n", err(ret),
gpio_pin_get_raw(led.port, led.pin));
ret = gpio_pin_set_dt(&led, 0);
printk("[set] gpio_pin_set_dt(PD5, 0) = %s, register bit %d\n", err(ret),
gpio_pin_get_raw(led.port, led.pin));
}
[set] gpio_pin_set_dt(PD5, 1) = 0, register bit 1
[set] gpio_pin_set_dt(PD5, 0) = 0, register bit 0
int gpio_pin_toggle_dt(const struct gpio_dt_spec *spec);

把输出的电平翻转。

/* gpio_pin_toggle_dt: invert an output; six calls make the LED blink three times */
static void demo_pin_toggle_dt(void)
{
int ret = gpio_pin_toggle_dt(&led);
printk("[toggle] gpio_pin_toggle_dt(PD5) = %s, register bit %d\n", err(ret),
gpio_pin_get_raw(led.port, led.pin));
for (int i = 0; i < 5; i++) {
k_msleep(100);
gpio_pin_toggle_dt(&led);
}
printk("[toggle] blinked the LED 3 times\n");
}
[toggle] gpio_pin_toggle_dt(PD5) = 0, register bit 1
[toggle] blinked the LED 3 times

最后一行对应 LED 闪烁 3 次。

int gpio_pin_get_dt(const struct gpio_dt_spec *spec);

读引脚的逻辑电平。输出引脚读回的是写进去的值,不是引脚上的实际电平。

/* gpio_pin_get_dt: the logical level of a pin. For an output pin it is the level that was written. */
static void demo_pin_get_dt(void)
{
int level;
gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
level = gpio_pin_get_dt(&led);
printk("[get] PD5 written active, gpio_pin_get_dt = %d\n", level);
gpio_pin_configure_dt(&led, GPIO_OUTPUT_INACTIVE);
level = gpio_pin_get_dt(&led);
printk("[get] PD5 written inactive, gpio_pin_get_dt = %d\n", level);
}
[get] PD5 written active, gpio_pin_get_dt = 1
[get] PD5 written inactive, gpio_pin_get_dt = 0
int gpio_pin_get_raw(const struct device *port, gpio_pin_t pin);

读引脚上的实际电平,不看 GPIO_ACTIVE_LOW。PB3 悬空,所以打开上拉读到 1,打开下拉读到 0,这也是后面中断演示制造边沿的办法。

/* gpio_pin_get_raw: the level of the pin, not looking at GPIO_ACTIVE_LOW. PB3 is not connected,
* so the pull resistor decides.
*/
static void demo_pin_get_raw(void)
{
gpio_pin_configure_dt(&in, GPIO_INPUT | GPIO_PULL_UP);
k_msleep(1);
printk("[get_raw] PB3 with pull up = %d\n", gpio_pin_get_raw(in.port, in.pin));
gpio_pin_configure_dt(&in, GPIO_INPUT | GPIO_PULL_DOWN);
k_msleep(1);
printk("[get_raw] PB3 with pull down = %d\n", gpio_pin_get_raw(in.port, in.pin));
}
[get_raw] PB3 with pull up = 1
[get_raw] PB3 with pull down = 0
int gpio_port_get_raw(const struct device *port, gpio_port_value_t *value);

一次读整个 bank 的 32 根线,value 的每一位对应一个引脚。

/* gpio_port_get_raw: all 32 pins of the bank at once */
static void demo_port_get_raw(void)
{
gpio_port_value_t v;
int ret;
gpio_pin_configure_dt(&in, GPIO_INPUT | GPIO_PULL_UP);
k_msleep(1);
ret = gpio_port_get_raw(gpiob, &v);
printk("[port_get] gpio_port_get_raw(PB) = %s, PB2 %d, PB3 %d\n", err(ret),
!!(v & BIT(out.pin)), !!(v & BIT(in.pin)));
}
[port_get] gpio_port_get_raw(PB) = 0, PB2 0, PB3 1
int gpio_port_set_bits_raw(const struct device *port, gpio_port_pins_t pins);

把掩码里的引脚都拉高,一次寄存器写入完成。

/* gpio_port_set_bits_raw: drive the pins of the mask high */
static void demo_port_set_bits_raw(void)
{
gpio_port_value_t v;
int ret;
gpio_pin_configure_dt(&out, GPIO_OUTPUT_INACTIVE);
ret = gpio_port_set_bits_raw(gpiob, BIT(out.pin));
gpio_port_get_raw(gpiob, &v);
printk("[set_bits] gpio_port_set_bits_raw(PB2) = %s, PB2 %d\n", err(ret), !!(v & BIT(out.pin)));
}
[set_bits] gpio_port_set_bits_raw(PB2) = 0, PB2 1
int gpio_port_clear_bits_raw(const struct device *port, gpio_port_pins_t pins);

把掩码里的引脚都拉低。

/* gpio_port_clear_bits_raw: drive the pins of the mask low */
static void demo_port_clear_bits_raw(void)
{
gpio_port_value_t v;
int ret = gpio_port_clear_bits_raw(gpiob, BIT(out.pin));
gpio_port_get_raw(gpiob, &v);
printk("[clear_bits] gpio_port_clear_bits_raw(PB2) = %s, PB2 %d\n", err(ret), !!(v & BIT(out.pin)));
}
[clear_bits] gpio_port_clear_bits_raw(PB2) = 0, PB2 0
int gpio_port_toggle_bits(const struct device *port, gpio_port_pins_t pins);

把掩码里的引脚都翻转。

/* gpio_port_toggle_bits: invert the pins of the mask */
static void demo_port_toggle_bits(void)
{
gpio_port_value_t v;
int ret = gpio_port_toggle_bits(gpiob, BIT(out.pin));
gpio_port_get_raw(gpiob, &v);
printk("[toggle_bits] gpio_port_toggle_bits(PB2) = %s, PB2 %d\n", err(ret), !!(v & BIT(out.pin)));
}
[toggle_bits] gpio_port_toggle_bits(PB2) = 0, PB2 1
int gpio_port_set_masked_raw(const struct device *port, gpio_port_pins_t mask, gpio_port_value_t value);

掩码里的引脚设成 value 里对应的位,其余引脚不变。

/* gpio_port_set_masked_raw: set the pins of the mask to the matching bits of value, leave the others */
static void demo_port_set_masked_raw(void)
{
gpio_port_value_t v;
int ret = gpio_port_set_masked_raw(gpiob, BIT(out.pin), 0);
gpio_port_get_raw(gpiob, &v);
printk("[set_masked] gpio_port_set_masked_raw(PB2, 0) = %s, PB2 %d\n", err(ret),
!!(v & BIT(out.pin)));
}
[set_masked] gpio_port_set_masked_raw(PB2, 0) = 0, PB2 0
void gpio_init_callback(struct gpio_callback *callback, gpio_callback_handler_t handler, gpio_port_pins_t pin_mask);

把处理函数和它关心的引脚掩码绑在一个 struct gpio_callback 上。回调在 PLIC 中断里执行,pins 参数是这个 bank 上本次待处理的所有引脚的位掩码,回调里不要做耗时的事。下面三个演示共用的变量和回调函数:

static struct gpio_callback cb;
static volatile int fired;
static volatile uint32_t last_pins;
static void on_edge(const struct device *port, struct gpio_callback *callback, uint32_t pins)
{
fired++;
last_pins = pins;
}
/* gpio_init_callback: bind a handler and the mask of the pins it wants */
static void demo_init_callback(void)
{
gpio_init_callback(&cb, on_edge, BIT(in.pin));
printk("[init_callback] handler for pin mask 0x%x\n", (unsigned int)cb.pin_mask);
}
[init_callback] handler for pin mask 0x8
int gpio_add_callback(const struct device *port, struct gpio_callback *callback);

把回调注册到 bank 上。

/* gpio_add_callback: register it with the bank */
static void demo_add_callback(void)
{
int ret = gpio_add_callback(in.port, &cb);
printk("[add_callback] gpio_add_callback = %s\n", err(ret));
}
[add_callback] gpio_add_callback = 0
int gpio_pin_interrupt_configure_dt(const struct gpio_dt_spec *spec, gpio_flags_t flags);
flags驱动的行为
GPIO_INT_EDGE_RISING、GPIO_INT_EDGE_FALLING、GPIO_INT_EDGE_BOTH边沿触发
GPIO_INT_LEVEL_HIGH、GPIO_INT_LEVEL_LOW电平触发
GPIO_INT_DISABLE关掉这个引脚的中断;如果引脚还在中断功能上,把复用功能恢复为打开中断前的值

演示:PB3 先下拉,装载上升沿;改成上拉制造一个上升沿,回调触发 1 次;改回下拉是下降沿,不是装载的上升沿,回调次数是 0。

/* gpio_pin_interrupt_configure_dt: arm the pin. The pull resistor is part of the pin configuration,
* so after every gpio_pin_configure the interrupt is armed again.
*/
static void demo_pin_interrupt_configure_dt(void)
{
int ret;
gpio_pin_configure_dt(&in, GPIO_INPUT | GPIO_PULL_DOWN);
k_msleep(1);
ret = gpio_pin_interrupt_configure_dt(&in, GPIO_INT_EDGE_RISING);
printk("[interrupt] arm EDGE_RISING = %s\n", err(ret));
fired = 0;
gpio_pin_configure_dt(&in, GPIO_INPUT | GPIO_PULL_UP);
gpio_pin_interrupt_configure_dt(&in, GPIO_INT_EDGE_RISING);
k_msleep(20);
printk("[interrupt] pull up after arming rising : %d callback(s), pins 0x%x\n", fired, last_pins);
fired = 0;
gpio_pin_configure_dt(&in, GPIO_INPUT | GPIO_PULL_DOWN);
gpio_pin_interrupt_configure_dt(&in, GPIO_INT_EDGE_RISING);
k_msleep(20);
printk("[interrupt] pull down after arming rising : %d callback(s)\n", fired);
ret = gpio_pin_interrupt_configure_dt(&in, GPIO_INT_DISABLE);
printk("[interrupt] GPIO_INT_DISABLE = %s\n", err(ret));
}
[interrupt] arm EDGE_RISING = 0
[interrupt] pull up after arming rising : 1 callback(s), pins 0x8
[interrupt] pull down after arming rising : 0 callback(s)
[interrupt] GPIO_INT_DISABLE = 0

pins 0x8 就是 PB3(第 3 位)。

int gpio_get_pending_int(const struct device *dev);

读这个 bank 的中断状态寄存器,每个引脚一位。中断处理函数会清掉已经处理的位,所以没有待处理的中断时读到 0。

/* gpio_get_pending_int: the interrupt status register of the bank, one bit per pin */
static void demo_get_pending_int(void)
{
printk("[pending] gpio_get_pending_int(PB) = 0x%x\n", gpio_get_pending_int(gpiob));
}
[pending] gpio_get_pending_int(PB) = 0x0
int gpio_remove_callback(const struct device *port, struct gpio_callback *callback);

把回调从 bank 上取下来。

/* gpio_remove_callback: take the handler out again */
static void demo_remove_callback(void)
{
int ret = gpio_remove_callback(in.port, &cb);
printk("[remove_callback] gpio_remove_callback = %s\n", err(ret));
}
[remove_callback] gpio_remove_callback = 0

上面所有示例和日志都来自这一个程序。建一个目录,放进下面的文件,就可以像其他 Zephyr 应用一样编译。

gpio_api/
├── CMakeLists.txt
├── prj.conf
├── app.overlay
└── src/
└── main.c
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(gpio_api)
target_sources(app PRIVATE src/main.c)
CONFIG_GPIO=y
CONFIG_PRINTK=y
CONFIG_SHELL=n
CONFIG_DEVMEM_SHELL=n
CONFIG_LOG_PRINTK=n
CONFIG_MAIN_STACK_SIZE=4096

CONFIG_MAIN_STACK_SIZE=4096 不能省:默认的主线程栈只有 1024 字节,printk 就会把它用完,程序在显示初始化的时候就会因为 Instruction Access fault 停下来。

/* PB2 and PB3 are free pins of the board (the UART1 flow control signals, not used) */
/ {
zephyr,user {
out-gpios = <&gpiob 2 GPIO_ACTIVE_HIGH>;
in-gpios = <&gpiob 3 GPIO_ACTIVE_HIGH>;
};
};
/*
* Calls the functions of <zephyr/drivers/gpio.h> that the sunxi GPIO driver
* implements, one function per call, and prints what each call returns.
*/
#include <errno.h>
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
/* PD5, the LED of the board (alias led0 in yuzukineko.dts) */
static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios);
/* PB2 and PB3, from the zephyr,user node of app.overlay */
static const struct gpio_dt_spec out = GPIO_DT_SPEC_GET(DT_PATH(zephyr_user), out_gpios);
static const struct gpio_dt_spec in = GPIO_DT_SPEC_GET(DT_PATH(zephyr_user), in_gpios);
static const struct device *const gpiob = DEVICE_DT_GET(DT_NODELABEL(gpiob));
static const char *err(int ret)
{
switch (ret) {
case 0: return "0";
case -ENOTSUP: return "-ENOTSUP";
case -EINVAL: return "-EINVAL";
case -EIO: return "-EIO";
default: return "?";
}
}
/* gpio_pin_configure_dt: direction, initial level and pull resistor */
static void demo_pin_configure_dt(void)
{
int ret;
ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_INACTIVE);
printk("[configure] PD5 output, low = %s\n", err(ret));
ret = gpio_pin_configure_dt(&in, GPIO_INPUT | GPIO_PULL_UP);
printk("[configure] PB3 input with pull up = %s\n", err(ret));
ret = gpio_pin_configure_dt(&in, GPIO_INPUT | GPIO_PULL_DOWN);
printk("[configure] PB3 input with pull down = %s\n", err(ret));
ret = gpio_pin_configure_dt(&out, GPIO_OUTPUT | GPIO_OPEN_DRAIN);
printk("[configure] PB2 open drain output = %s\n", err(ret));
ret = gpio_pin_configure_dt(&out, GPIO_OUTPUT | GPIO_OPEN_SOURCE);
printk("[configure] PB2 open source output = %s\n", err(ret));
}
/* gpio_pin_set_dt: the logical level of an output */
static void demo_pin_set_dt(void)
{
int ret;
gpio_pin_configure_dt(&led, GPIO_OUTPUT_INACTIVE);
ret = gpio_pin_set_dt(&led, 1);
printk("[set] gpio_pin_set_dt(PD5, 1) = %s, register bit %d\n", err(ret),
gpio_pin_get_raw(led.port, led.pin));
ret = gpio_pin_set_dt(&led, 0);
printk("[set] gpio_pin_set_dt(PD5, 0) = %s, register bit %d\n", err(ret),
gpio_pin_get_raw(led.port, led.pin));
}
/* gpio_pin_toggle_dt: invert an output; six calls make the LED blink three times */
static void demo_pin_toggle_dt(void)
{
int ret = gpio_pin_toggle_dt(&led);
printk("[toggle] gpio_pin_toggle_dt(PD5) = %s, register bit %d\n", err(ret),
gpio_pin_get_raw(led.port, led.pin));
for (int i = 0; i < 5; i++) {
k_msleep(100);
gpio_pin_toggle_dt(&led);
}
printk("[toggle] blinked the LED 3 times\n");
}
/* gpio_pin_get_dt: the logical level of a pin. For an output pin it is the level that was written. */
static void demo_pin_get_dt(void)
{
int level;
gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
level = gpio_pin_get_dt(&led);
printk("[get] PD5 written active, gpio_pin_get_dt = %d\n", level);
gpio_pin_configure_dt(&led, GPIO_OUTPUT_INACTIVE);
level = gpio_pin_get_dt(&led);
printk("[get] PD5 written inactive, gpio_pin_get_dt = %d\n", level);
}
/* gpio_pin_get_raw: the level of the pin, not looking at GPIO_ACTIVE_LOW. PB3 is not connected,
* so the pull resistor decides.
*/
static void demo_pin_get_raw(void)
{
gpio_pin_configure_dt(&in, GPIO_INPUT | GPIO_PULL_UP);
k_msleep(1);
printk("[get_raw] PB3 with pull up = %d\n", gpio_pin_get_raw(in.port, in.pin));
gpio_pin_configure_dt(&in, GPIO_INPUT | GPIO_PULL_DOWN);
k_msleep(1);
printk("[get_raw] PB3 with pull down = %d\n", gpio_pin_get_raw(in.port, in.pin));
}
/* gpio_port_get_raw: all 32 pins of the bank at once */
static void demo_port_get_raw(void)
{
gpio_port_value_t v;
int ret;
gpio_pin_configure_dt(&in, GPIO_INPUT | GPIO_PULL_UP);
k_msleep(1);
ret = gpio_port_get_raw(gpiob, &v);
printk("[port_get] gpio_port_get_raw(PB) = %s, PB2 %d, PB3 %d\n", err(ret),
!!(v & BIT(out.pin)), !!(v & BIT(in.pin)));
}
/* gpio_port_set_bits_raw: drive the pins of the mask high */
static void demo_port_set_bits_raw(void)
{
gpio_port_value_t v;
int ret;
gpio_pin_configure_dt(&out, GPIO_OUTPUT_INACTIVE);
ret = gpio_port_set_bits_raw(gpiob, BIT(out.pin));
gpio_port_get_raw(gpiob, &v);
printk("[set_bits] gpio_port_set_bits_raw(PB2) = %s, PB2 %d\n", err(ret), !!(v & BIT(out.pin)));
}
/* gpio_port_clear_bits_raw: drive the pins of the mask low */
static void demo_port_clear_bits_raw(void)
{
gpio_port_value_t v;
int ret = gpio_port_clear_bits_raw(gpiob, BIT(out.pin));
gpio_port_get_raw(gpiob, &v);
printk("[clear_bits] gpio_port_clear_bits_raw(PB2) = %s, PB2 %d\n", err(ret), !!(v & BIT(out.pin)));
}
/* gpio_port_toggle_bits: invert the pins of the mask */
static void demo_port_toggle_bits(void)
{
gpio_port_value_t v;
int ret = gpio_port_toggle_bits(gpiob, BIT(out.pin));
gpio_port_get_raw(gpiob, &v);
printk("[toggle_bits] gpio_port_toggle_bits(PB2) = %s, PB2 %d\n", err(ret), !!(v & BIT(out.pin)));
}
/* gpio_port_set_masked_raw: set the pins of the mask to the matching bits of value, leave the others */
static void demo_port_set_masked_raw(void)
{
gpio_port_value_t v;
int ret = gpio_port_set_masked_raw(gpiob, BIT(out.pin), 0);
gpio_port_get_raw(gpiob, &v);
printk("[set_masked] gpio_port_set_masked_raw(PB2, 0) = %s, PB2 %d\n", err(ret),
!!(v & BIT(out.pin)));
}
/* interrupts. The edge is made by switching the pull resistor of the unconnected pin PB3. */
static struct gpio_callback cb;
static volatile int fired;
static volatile uint32_t last_pins;
static void on_edge(const struct device *port, struct gpio_callback *callback, uint32_t pins)
{
fired++;
last_pins = pins;
}
/* gpio_init_callback: bind a handler and the mask of the pins it wants */
static void demo_init_callback(void)
{
gpio_init_callback(&cb, on_edge, BIT(in.pin));
printk("[init_callback] handler for pin mask 0x%x\n", (unsigned int)cb.pin_mask);
}
/* gpio_add_callback: register it with the bank */
static void demo_add_callback(void)
{
int ret = gpio_add_callback(in.port, &cb);
printk("[add_callback] gpio_add_callback = %s\n", err(ret));
}
/* gpio_pin_interrupt_configure_dt: arm the pin. The pull resistor is part of the pin configuration,
* so after every gpio_pin_configure the interrupt is armed again.
*/
static void demo_pin_interrupt_configure_dt(void)
{
int ret;
gpio_pin_configure_dt(&in, GPIO_INPUT | GPIO_PULL_DOWN);
k_msleep(1);
ret = gpio_pin_interrupt_configure_dt(&in, GPIO_INT_EDGE_RISING);
printk("[interrupt] arm EDGE_RISING = %s\n", err(ret));
fired = 0;
gpio_pin_configure_dt(&in, GPIO_INPUT | GPIO_PULL_UP);
gpio_pin_interrupt_configure_dt(&in, GPIO_INT_EDGE_RISING);
k_msleep(20);
printk("[interrupt] pull up after arming rising : %d callback(s), pins 0x%x\n", fired, last_pins);
fired = 0;
gpio_pin_configure_dt(&in, GPIO_INPUT | GPIO_PULL_DOWN);
gpio_pin_interrupt_configure_dt(&in, GPIO_INT_EDGE_RISING);
k_msleep(20);
printk("[interrupt] pull down after arming rising : %d callback(s)\n", fired);
ret = gpio_pin_interrupt_configure_dt(&in, GPIO_INT_DISABLE);
printk("[interrupt] GPIO_INT_DISABLE = %s\n", err(ret));
}
/* gpio_get_pending_int: the interrupt status register of the bank, one bit per pin */
static void demo_get_pending_int(void)
{
printk("[pending] gpio_get_pending_int(PB) = 0x%x\n", gpio_get_pending_int(gpiob));
}
/* gpio_remove_callback: take the handler out again */
static void demo_remove_callback(void)
{
int ret = gpio_remove_callback(in.port, &cb);
printk("[remove_callback] gpio_remove_callback = %s\n", err(ret));
}
int main(void)
{
if (!gpio_is_ready_dt(&led) || !gpio_is_ready_dt(&out) || !gpio_is_ready_dt(&in)) {
printk("gpio not ready\n");
return 0;
}
demo_pin_configure_dt();
demo_pin_set_dt();
demo_pin_toggle_dt();
demo_pin_get_dt();
demo_pin_get_raw();
demo_port_get_raw();
demo_port_set_bits_raw();
demo_port_clear_bits_raw();
demo_port_toggle_bits();
demo_port_set_masked_raw();
demo_init_callback();
demo_add_callback();
demo_pin_interrupt_configure_dt();
demo_get_pending_int();
demo_remove_callback();
printk("gpio_api done\n");
return 0;
}
终端窗口
. ~/f101-zephyr/env.sh
cd ~/f101-zephyr
west build -b yuzukineko -d build/gpio-api gpio_api

下载运行的方法见编译与运行。控制台 UART1 上的完整输出:

[configure] PD5 output, low = 0
[configure] PB3 input with pull up = 0
[configure] PB3 input with pull down = 0
[configure] PB2 open drain output = -ENOTSUP
[configure] PB2 open source output = -ENOTSUP
[set] gpio_pin_set_dt(PD5, 1) = 0, register bit 1
[set] gpio_pin_set_dt(PD5, 0) = 0, register bit 0
[toggle] gpio_pin_toggle_dt(PD5) = 0, register bit 1
[toggle] blinked the LED 3 times
[get] PD5 written active, gpio_pin_get_dt = 1
[get] PD5 written inactive, gpio_pin_get_dt = 0
[get_raw] PB3 with pull up = 1
[get_raw] PB3 with pull down = 0
[port_get] gpio_port_get_raw(PB) = 0, PB2 0, PB3 1
[set_bits] gpio_port_set_bits_raw(PB2) = 0, PB2 1
[clear_bits] gpio_port_clear_bits_raw(PB2) = 0, PB2 0
[toggle_bits] gpio_port_toggle_bits(PB2) = 0, PB2 1
[set_masked] gpio_port_set_masked_raw(PB2, 0) = 0, PB2 0
[init_callback] handler for pin mask 0x8
[add_callback] gpio_add_callback = 0
[interrupt] arm EDGE_RISING = 0
[interrupt] pull up after arming rising : 1 callback(s), pins 0x8
[interrupt] pull down after arming rising : 0 callback(s)
[interrupt] GPIO_INT_DISABLE = 0
[pending] gpio_get_pending_int(PB) = 0x0
[remove_callback] gpio_remove_callback = 0
gpio_api done