GPIO的使用是我们最常用的外设操作之一,本示例使用扩展MCU的io来闪烁LED。展示了如何将GPIO引脚配置为输出,然后打开和关闭它们,控制开发板上的的三色灯进行闪烁。
在开始上手GPIO的操作前我们先来学习一下Zephyr中对于GPIO物理电平和逻辑电平的配置。
物理电平(physical leve):一般情况下我们将0当做gpio的低电平,1当做gpio的高电平,这是物理电平。
逻辑电平(logic level):在软件上为提高业务代码的可移植性或易读性,我们会引入逻辑电平,即自行定义0和1对应的电平状态。
在{SDK}\.sdk\zephyr\include\zephyr\dt-bindings\gpio\gpio.h
中用两个宏flag表示逻辑电平对应的物理电平:
/** GPIO pin is active (has logical value '1') in low state. */
#define GPIO_ACTIVE_LOW (1 << 0)
/** GPIO pin is active (has logical value '1') in high state. */
#define GPIO_ACTIVE_HIGH (0 << 0)
在Zephyr开发中通过gpio_pin_config(struct device *port, gpio_pin_t pin, gpio_flags_t flags)
接口的flags标志对GPIO进行逻辑电平的模式配置,根据宏flag的定义,GPIO逻辑电平和物理电平的对应关系如下:
当GPIO被配置为GPIO_ACTIVE_LOW时:GPIO上出现低电平时表示逻辑电平1
物理电平(gpio leve) | 逻辑电平(gpio logic level) |
---|---|
0 | 1 |
1 | 0 |
当GPIO被配置为GPIO_ACTIVE_HIGH时:GPIO上出现高电平时表示逻辑电平1
物理电平(gpio leve) | 逻辑电平(gpio logic level) |
---|---|
0 | 0 |
1 | 1 |
当GPIO配置时GPIO_ACTIVE_LOW和GPIO_ACTIVE_HIGH都没设置,将会默认使用GPIO_ACTIVE_HIGH,也就是物理电平和逻辑电平一致。
了解 GPIO 逻辑电平和物理电平的对应关系对本章节后续的 GPIO 引脚的配置和读写操作很有帮助,GPIO 逻辑电平的读和写操作接口都会依赖配置的 GPIO_ACTIVE_LOW、GPIO_ACTIVE_HIGH 标志。
flag | 说明 |
---|---|
GPIO_INPUT | 启用GPIO为输入 |
GPIO_OUTPUT | 启用GPIO为输出,不改变输出状态 |
GPIO_DISCONNECTED | 输入和输出都配置为禁用 |
GPIO_OUTPUT_LOW | 将GPIO配置为输出并将其初始化为低 |
GPIO_OUTPUT_HIGH | 将GPIO配置为输出并将其初始化为高 |
GPIO_OUTPUT_INACTIVE | 将GPIO配置为输出并将其初始化为逻辑电平0 |
GPIO_OUTPUT_ACTIVE | 将GPIO配置为输出并将其初始化为逻辑电平1 |
中断配置标志(GPIO_INT_*)
用来指定作为输入的GPIO引脚以何种方式触发中断。中断可由PIN脚的物理电平或逻辑水平触发。GPIO中断的触发也会根据配置的GPIO_ACTIVE_LOW、GPIO_ACTIVE_HIGH标志来判断。
flag | 说明 |
---|---|
GPIO_INT_DISABLE | 禁用 GPIO 管脚中断 |
GPIO_INT_EDGE_RISING | 将GPIO中断配置为引脚上升沿触发 |
GPIO_INT_EDGE_FALLING | 将 GPIO 中断配置为在引脚下降沿触发 |
GPIO_INT_EDGE_BOTH | 将GPIO中断配置为在引脚上升或下降时触发 |
GPIO_INT_LEVEL_LOW | 将GPIO中断配置为引脚物理电平拉低并保持 |
GPIO_INT_LEVEL_HIGH | 将GPIO中断配置为引脚物理电平拉高并保持 |
GPIO_INT_EDGE_TO_INACTIVE | 将GPIO中断配置为引脚变为低电平时触发 |
GPIO_INT_EDGE_TO_ACTIVE | 将GPIO中断配置为引脚变高电平时触发 |
GPIO_INT_LEVEL_INACTIVE | 将GPIO中断配置为管脚逻辑电平0时触发 |
GPIO_INT_LEVEL_ACTIVE | 将GPIO中断配置为管脚逻辑电平1时触发 |
flag配置对应的内容可以在{SDK}\.sdk\include\zephyr\dt-bindings\gpio\gpio.h
中查看。
状态 | 属性 |
---|---|
GPIO_PULL_UP | 引脚上拉 |
GPIO_PULL_DOWN | 引脚下拉 |
int gpio_pin_configure(const struct device *port, gpio_pin_t pin, gpio_flags_t flags)
参数说明
字段 | 说明 |
---|---|
port | 指向GPIO设备实例的指针 |
pin | 要配置的引脚编号 |
flags | 引脚配置的标志: GPIO 输入/输出配置标志, GPIO 引脚驱动标志, GPIO 引脚偏置标志 |
int gpio_pin_configure_dt(const struct gpio_dt_spec *spec, gpio_flags_t extra_flags)
参数说明
字段 | 说明 |
---|---|
spec | GPIO配置信息结构体,从设备树配置获取 |
extra_flags | GPIO 标志(输入/输出等) |
int gpio_pin_interrupt_configure(const struct device *port, gpio_pin_t pin, gpio_flags_t flags)
参数说明
字段 | 说明 |
---|---|
port | 指向GPIO设备实例的指针 |
pin | 要配置的引脚编号 |
flags | 由GPIO_ INT_ * 定义的中断配置标志 |
int gpio_pin_interrupt_configure_dt(const struct gpio_dt_spec *spec, gpio_flags_t flags)
参数说明
字段 | 说明 |
---|---|
spec | GPIO配置信息结构体,从设备树配置获取 |
flags | 中断配置标志 |
gpio_dt_spec 为 GPIO 的设备树配置结构体,通过 GPIO_DT_SPEC_GET_OR() 接口从设备树获取 GPIO 的配置信息,结构体内容如下:
struct gpio_dt_spec { const struct device *port;//GPIO的设备实例 gpio_pin_t pin;//GPIO pin脚 gpio_dt_flags_t dt_flags;//pin脚的功能标志 }
写gpio逻辑电平操作需要参考GPIO_ACTIVE_LOW或GPIO_ACTIVE_HIGH配置下逻辑电平和物理电平的对应关系,例如当GPIO配置为GPIO_ACTIVE_LOW时,设置逻辑电平为1则GPIO输出低电平。当GPIO配置为GPIO_ACTIVE_HIGH时,设置逻辑电平1则GPIO输出高电平。
int gpio_pin_set(const struct device *port, gpio_pin_t pin, int value)
参数说明
字段 | 说明 |
---|---|
port | 指向GPIO设备实例的指针 |
pin | 要配置的引脚编号 |
value | 分配给引脚的值 |
给GPIO写物理电平不需要参考GPIO_ACTIVE_LOW或GPIO_ACTIVE_HIGH配置。
int gpio_pin_set_raw(const struct device *port, gpio_pin_t pin, int value)
参数说明
字段 | 说明 |
---|---|
port | 指向GPIO设备实例的指针 |
pin | 要配置的引脚编号 |
value | 分配给引脚的值 |
int gpio_pin_set_dt(const struct gpio_dt_spec *spec, int value)
参数说明
字段 | 说明 |
---|---|
spec | GPIO配置信息结构体,从设备树配置获取 |
value | 分配给引脚的值 |
如果 GPIO 配置为 GPIO_ACTIVE_HIGH,则 gpio_pin_set() 函数等效于gpio_pin_set_raw()。
int gpio_pin_get(const struct device *port, gpio_pin_t pin)
获取GPIO逻辑电平,同时考虑到 GPIO_ACTIVE_LOW、GPIO_ACTIVE_HIGH标志。如果将GPIO配置为GPIO_ACTIVE_HIGH,则GPIO为物理低电平时通过该接口获取的逻辑电平为0。如果将GPIO配置为GPIO_ACTIVE_LOW,则GPIO为物理低电平时通过接口过去的逻辑电平为1。
参数说明
字段 | 说明 |
---|---|
port | 指向GPIO设备实例的指针 |
pin | 引脚编号 |
int gpio_pin_get_dt(const struct gpio_dt_spec *spec)
参数说明
字段 | 说明 |
---|---|
spec | GPIO配置信息结构体,从设备树配置获取 |
int gpio_pin_get_raw(const struct device *port, gpio_pin_t pin)
参数说明
字段 | 说明 |
---|---|
port | 指向GPIO设备实例的指针 |
pin | 引脚编号 |
如果 GPIO 配置为 GPIO_ACTIVE_HIGH,则 gpio_pin_get() 等效于 gpio_pin_get_raw()。
更多GPIO API接口请看Zephyr官网GPIO Driver APIs描述。
SDK 中提供了 exmcu_gpio_led 的示例。
{SDK}\.sdk\csk\samples\driver\exmcu_gpio_led
使用扩展MCU的io来闪烁LED。展示了如何将GPIO引脚配置为输出,然后打开和关闭它们,控制开发板上的的三色灯进行闪烁。
适用开发板:大模型开发套件
编译版型:csk6_duomotai_devkit
在 SDK 根目录(duomotai_ap
)下可通过执行以下指令进行对该示例工程的编译:
lisa zep build -b csk6_duomotai_devkit .sdk/csk/samples/driver/exmcu_gpio_led/ -p
编译完成后,编译产物二进制文件位于 build\zephyr\zephyr.bin
使用 Type-C 数据线连接开发套件的 DAP_USB
接口,选中以下其中一种方式对固件进行烧录:
cskburn desktop
是一款聆思推出的桌面烧录工具,在下载并安装 cskburn desktop 烧录工具后,双击图标运行软件:
1.点击串口下拉框,选择连接开发套件后识别到的串口编号;
2.将编译输出的.bin
文件拖拽进烧录区域;
3.点击开始烧录,等待烧录完成。
若您已按照 《环境搭建》 教程完成开发环境的安装,可在编译完成后执行 lisa zep exec cskburn
指令完成烧录。
lisa zep exec cskburn -s \\.\COMxx -C 6 -b 1500000 0x000000 --verify-all .\build\zephyr\zephyr.bin
请将命令行中的的 COMx 替换为开发套件在 PC 上对应的串口号(可通过设备管理器查看)。例如:
COM3
。
lisa zep exec cskburn -s PORT -C 6 0x000000 --verify-all ./build/zephyr/zephyr.bin -b 1500000
请将命令行中的 PORT 替换为开发套件连接在 PC 上对应的串口号。例如:
/dev/ttyUSB0
。
烧录完成后,可看LED灯的闪烁变化。
以下代码与注释已省略一部分非关键接口代码,主要呈现示例的主业务流程与主要接口的使用。
CONFIG_GPIO=y
CONFIG_GPIO_CSK6_CH32V003=y
CONFIG_LOG=y
CONFIG_LOG_PRINTK=y
CONFIG_LOG_MODE_IMMEDIATE=y
1.验证 GPIO 端口是否已准备就绪
gpio_is_ready_dt()
2.基于设备树节点的配置来设置特定的 GPIO 引脚
gpio_pin_configure_dt()
3.循环中切换 GPIO 引脚的状态
gpio_pin_toggle_dt()
按下开发板复位按钮,运行程序,观察LED灯的闪烁变化。
int main(void)
{
int ret;
if (!gpio_is_ready_dt(&led_r)) {
return 0;
}
if (!gpio_is_ready_dt(&led_g)) {
return 0;
}
if (!gpio_is_ready_dt(&led_b)) {
return 0;
}
ret = gpio_pin_configure_dt(&led_r, GPIO_OUTPUT_ACTIVE);
if (ret < 0) {
return 0;
}
ret = gpio_pin_configure_dt(&led_g, GPIO_OUTPUT_ACTIVE);
if (ret < 0) {
return 0;
}
ret = gpio_pin_configure_dt(&led_b, GPIO_OUTPUT_ACTIVE);
if (ret < 0) {
return 0;
}
while (1) {
ret = gpio_pin_toggle_dt(&led_r);
if (ret < 0) {
return 0;
}
k_msleep(SLEEP_TIME_MS);
ret = gpio_pin_toggle_dt(&led_g);
if (ret < 0) {
return 0;
}
k_msleep(SLEEP_TIME_MS);
ret = gpio_pin_toggle_dt(&led_b);
if (ret < 0) {
return 0;
}
k_msleep(SLEEP_TIME_MS);
}
return 0;
}