GPIO的使用是我们最常用的外设操作之一,本章节主要介绍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 中提供了 GPIO 的示例。
{SDK}\.sdk\csk\samples\driver\gpio
控制 GPIO-A1引脚,每1s翻转一次,通过示波器可以看到翻转的波形。
适用开发板:大模型开发套件
编译版型:csk6_duomotai_devkit
在 SDK 根目录(duomotai_ap
)下可通过执行以下指令进行对该示例工程的编译:
lisa zep build -b csk6_duomotai_devkit .sdk\csk\samples\driver\gpio -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
。
烧录完成后,使用数据线连接开发套件的CSK_USB
接口进行供电:
通过示波器连接GPIO-A1
引脚可以看到翻转的波形。电压表检测效果如下:
以下代码与注释已省略一部分非关键接口代码,主要呈现示例的主业务流程与主要接口的使用。
// 获取与别名 gpioa1 相关联的设备树节点
#define GPIOA_1_NODE DT_ALIAS(gpioa1)
// 从设备树中获取 gpioa1 设备
static const struct gpio_dt_spec gpioa1 = GPIO_DT_SPEC_GET(GPIOA_1_NODE, gpios);
static int gpio_a1_enable(void)
{
gpio_pin_set_dt(&gpioa1, 0);
return 0;
}
static int gpio_a1_disenable(void)
{
gpio_pin_set_dt(&gpioa1, 1);
return 0;
}
int main(void)
{
printk("GPIO sample application\n");
if (gpioa1.port == NULL) {
printk("Failed to get GPIOA1 device\n");
return -ENODEV;
}
// 配置 gpioa1 为输出模式并设置为活跃状态
gpio_pin_configure_dt(&gpioa1, GPIO_OUTPUT_ACTIVE);
// 交替调用 gpio_a1_enable 和 gpio_a1_disenable 函数,使 gpioa1 引脚的电平每秒钟切换一次
while (1) {
gpio_a1_enable();
k_msleep(1000);
gpio_a1_disenable();
k_msleep(1000);
}
return 0;
}