Skip to content

由 爱笑 贡献,Ai-Thinker 进行整理

【Ai-WB2入门篇】GPIO使用

Ai-WB2(BL602)的GLB(Global Register)是芯片通用全局设定模块,主要包含了时钟管理、复位管理、总线管理、内存管理以及GPIO管理等功能。 本文将介绍如何使用Ai-WB2的GPIO管理功能,包含GPIO输入、GPI0输出以及GPIO中断。 一:GPIO介绍 通用型之输入输出(General-purpose inputoutput,通常称为GPIO),GPIO管理功能提供GPIO控制寄存器,实现软件对 GPIO 属性的配置,使用户能够方便地操作 GPIO。每个GPIO可以配置为输入、输出和可选功能三种模式。在每个模式下(除模拟可选功能),提供设置上拉,下拉,浮空三种端口状态,此外GPIO还提供中断功能,可以配置为上升沿触发,下降沿触发或者高电平/低电平触发。

Ai-WB2的 GPI0主要特点如下:

📜 点击展开完整代码
c
**·** 可以配置为普通输入输出功能,该模式下可以设定上拉,下拉或者浮空输入输出
**·** 可以配置为可选功能,搭配外设功能使用,该模式下亦可以设定上拉,下拉,在使用模拟功能时,必须设置为浮空
**·** 可以设置驱动能力,以提供更大的输出电流
**·** 可以设置施密特触发器功能,提供简单硬件防抖功能

每个GPIO可以通过软件配置为:

📜 点击展开完整代码
c
**·** 高阻输入
**·** 上拉输入
**·** 下拉输入
**·** 上拉中断输入
**·** 下拉中断输入
**·** 高阻中断输入
**·** 上拉输出
**·** 下拉输出
**·** 高阻输出
**·** 模拟输入可选功能
**·** 模拟输出可选功能
**·** 数字可选功能

GPIO的功能框图如下:

GPIO可以设定的功能包括:

📜 点击展开完整代码
c
**·** Flash/QSPI:设定GPIO为QSPI功能,可以连接Flash,作为程序存储/运行介质
**·** SPI:设定GPIO为SPI功能
**·** 12C:设定GPI0为I2C功能
**·** UART:设定GPIO为UART功能
**·** PWM:设定GPIO为PWM功能
**·** ANA:设定GPI0为Analog功能
**·** SWGPIO:设定GPI0为通用I0功能
**·** JTAG:设定GPIO为JTAG功能

每个GPIO基本上都可以选择上述可选功能,当选择某个可选功能时,GPI0与对应的功能信号如下表所示:

在上述表格中,当选择UARI功能时,只是选择JUARI的一个信号,开没有指定该引脚的县体切能比是UARX认是UARLRX),认需要通过UART SIGX SEL(X=0-7)进一步选择具体的UART信号及对应的功能。每一个UART SIGX SEL可以选择的信号包括:

📜 点击展开完整代码
c
**·** 0:UARTO RTS
**·** 1:UARTO CTS
**·** 2:UARTO TXD
**·** 3:UARTO RXD
**·** 4:UART1 RTS
**·** 5:UART1 CTS
**·** 6:UART1 TXD
**·** 7:UART1 RXD

以GPIO0为例,当fun sel选择UART的时候,GPIO0选择的是UART_SIG0,在默认情况下UART_SIGO_SEL的值是0,也就是UARTO_RTS,即GPIO是UARTO_RTS功能。如果应用程序想把GPIO作为UART1_TXD.那只要把UART_SIGO_SEL设置为6,那么GPIO0的功能就是UART1_TXD。

GPIO输出设置 通过设定func_sel为SWGPIO,GPIO可以作为普通GPIO的输入输出,将IE设置为0,OE设置为1,就可以将GPIO配置为输出功能,输出的数值通过GPIO_O寄存器组设定。当GPIO_O对应bit设置为0时,GPIO输出低电平,当GPIO_O对应bit设置为1时,GPIO输出高电平。可以通过DRV控制位设置输出能力。

GPIO输入设置 通过设定func_sel为SWGPIO,将IE设置为1,0E设置为0,就可以将GPIO配置为输入功能,可以通过SMT控制位设置是否使能施密特触发器,通过PD,PU控制位设置上拉下拉属性。外部输入的数值,可以通过读取GPIO_I寄存器对应的bit获取到。

GPIO可选功能设置 通过设定func_sel为对应的外设功能,可以实现GPIO与外设的连接,实现外设的输入输出,从GPIO的基础功能框图可以看出,当选择可选功能时,需要将IE设置为1,OE设置为0,也就是断开普通GPIO的输出控制功能。这样,对于固定输入功能的外设,外设的OE信号始终为0,从而实现输入功能;对于固定输出的外设,其OE信号始终为1,从而实现输出是被外设控制,而此时的输入信号就是输出信号但是不会被正在输出的外设采集;当外设既需要输入又要输出时,通过控制外设OE信号就可以实现输入输出。

GPI0中断设置 要使用GPIO的中断功能,需要先将GPIO设置为输入模式,中断触发模式通过GPIO_INT_MODE_SET寄存器组进行设定。可以设定的中断模式包括:

📜 点击展开完整代码
c
**·** 上升沿触发中断
**·** 下降沿触发中断
**·** 高电平触发中断
**·** 低电平触发中断

每个GPIO都可以设定为中断功能,是否使能某个GPIO中断可以通过GPIO_INT_MASK寄存器进行设定,中断产生时,在中断函数中可以通过GPIO_ INT_STAT寄存器获取到产生中断的GPIO引脚号,同时可以通过GPIO_INT_CLR清除掉对应的中断信号。 二:GPIO管理相关API介绍 BL602的GPIO管理相关的底层定义,在如下文件中定义: bl iot sdk/components/platform/soc/bl602/b1602 std/bl602 std/StdDriver/inc/bl602 gpio.h 该文件主要定义了BL602的GPIO编号、GPIO模式以及GPIO功能。 BL602的GPIO管理相关高级API定义,在如下文件中定义 bl iot sdk/components/platform/hosal/bl602 hal/bl gpio.h 该文件定义了GPIO的底层操作函数。

📜 点击展开完整代码
c
typedef struct _gpio_ctx_desc {
struct _gpio_ctx_desc *next;
void (*gpio_handler)(void *);
void *arg;
uint8_t gpioPin;
uint8_t intCtrlMod;
uint8_t intTrgMod;
} gpio_ctx_t;
int bl_gpio_enable_output(uint8_t pin, uint8_t pullup, uint8_t pulldown); // 设置GPIO为输出
int bl_gpio_enable_input(uint8_t pin, uint8_t pullup, uint8_t pulldown); // 设置GPIO为输入
int bl_gpio_output_set(uint8_t pin, uint8_t value); // 设置GPIO电平值,0表示低电平;1表示高电平
int bl_gpio_input_get(uint8_t pin, uint8_t *value); // 读取GPIO电平值,0表示低电平;1表示高电平
int bl_gpio_input_get_value(uint8_t pin);// 读取GPIO电平值,0表示低电平;1表示高电平
int bl_gpio_int_clear(uint8_t gpioPin,uint8_t intClear);// 清除中断标志
void bl_gpio_intmask(uint8_t gpiopin, uint8_t mask); // 中断掩码
void bl_set_gpio_intmod(uint8_t gpioPin, uint8_t intCtrlMod, uint8_t intTrgMod); // 设置中断模式
void bl_gpio_register(gpio_ctx_t *pstnode); // 注册gpio

同时在bl iot sdk的系统抽象硬件层中进一步封装定义,如下: bl iot sdk/components/platform/hosal/include/hosal gpio.h 该文件定义了在RTOS级对GPIO操作的硬件抽象层操作

📜 点击展开完整代码
c
typedef struct hosal_gpio_ctx {
struct hosal_gpio_ctx *next;
hosal_gpio_irq_handler_t handle;
void *arg;
uint8_t pin;
uint8_t intCtrlMod;
uint8_t intTrigMod;
}hosal_gpio_ctx_t;
typedef struct {
uint8_t        port;         /**< @brief gpio port */
hosal_gpio_config_t  config; /**< @brief gpio config */
void          *priv;         /**< @brief priv data */
} hosal_gpio_dev_t;
int hosal_gpio_init(hosal_gpio_dev_t *gpio);
int hosal_gpio_output_set(hosal_gpio_dev_t *gpio, uint8_t value);
int hosal_gpio_input_get(hosal_gpio_dev_t *gpio, uint8_t *value);
int hosal_gpio_irq_set(hosal_gpio_dev_t *gpio, hosal_gpio_irq_trigger_t trigger_type, hosal_gpio_irq_handler_t handler, void *arg);
int hosal_gpio_irq_mask(hosal_gpio_dev_t *gpio, uint8_t mask);
int hosal_gpio_finalize(hosal_gpio_dev_t *gpio);

三:硬件准备 本次示例将使用到如下硬件:

📜 点击展开完整代码
c
**·** Ai-WB2-32S开发板
**·** RGB LED
**·** 470欧姆电阻
**·** 按键
**·** 连接线
**·** 面板包板
**四:软件准备**

在前面的文章中,我们详细介绍了如何搭建Ai-WB2的开发环境: 五:代码实现 下面将以LED和按键输入为例,介绍如何使用Ai-WB2的GPIO输入、输出以及中断功能。 1. GPIO输出 示例代码如下

📜 点击展开完整代码
c
#include <stdio.h>
#include <string.h>
#include <FreeRTOS.h>
#include <task.h>
#include <bl_gpio.h>
#define GPIO_LED_RED   0
#define GPIO_LED_GREEN  1
#define GPIO_LED_BLUE   2
void blink_task(void *param){
uint8_t r_value = 1;
uint8_t g_value = 1;
uint8_t b_value = 1;
bl_gpio_enable_output(GPIO_LED_RED, 0, 0);
bl_gpio_enable_output(GPIO_LED_GREEN, 0, 0);
bl_gpio_enable_output(GPIO_LED_BLUE, 0, 0);
while(1) {
bl_gpio_output_set(GPIO_LED_RED, r_value);
r_value = !r_value;
vTaskDelay(200);
bl_gpio_output_set(GPIO_LED_GREEN, g_value);
g_value = !g_value;
vTaskDelay(200);
bl_gpio_output_set(GPIO_LED_BLUE, b_value);
b_value = !b_value;
vTaskDelay(200);
}
}
void main(void)
{
xTaskCreate(blink_task, "blink_task", 1024, NULL, 15, NULL);
}

在示例代码中,我们定义了一个FreeRTOS任务blin_ktask,该任务执行LED的闪烁任务。在 blink_task 中,通过bl_gpio_enable_output 设置GPIO为输出模式,然后通过调用 bl_gpio_output_set 设置引脚的电平值。 2. GPIO输入 示例代码如下

📜 点击展开完整代码
c
#include <stdio.h>
#include <string.h>
#include <FreeRTOS.h>
#include <task.h>
#include <bl_gpio.h>
#include <stdio.h>
#include <hosal_gpio.h>
#include <hosal_dma.h>
#include <blog.h>
#include <stdbool.h>
#define LED_PIN 0
#define KEY_PIN 3
#define TAG "gpio_exti"
static bool led_state = false;
static hosal_gpio_dev_t led;
static hosal_gpio_dev_t key;
static int32_t counter = 0;
static void init_led(void) {
led.port = LED_PIN;
led.config = OUTPUT_OPEN_DRAIN_NO_PULL;
hosal_gpio_init(&led);
hosal_gpio_output_set(&led,0);
printf("led inited\r\n");
}
static void init_key() {
key.port = KEY_PIN;
key.config = INPUT_PULL_UP;
hosal_gpio_init(&key);
printf("key inited\r\n");
}
void key_task(void* params) {
printf("key task started\r\n");
static uint8_t key_state = 0;
while (true) {
if (hosal_gpio_input_get(&key, &key_state) == 0) {
if (key_state) {
vTaskDelay(10 / portTICK_PERIOD_MS);
if (hosal_gpio_input_get(&key, &key_state) == 0) {
if (key_state) {
hosal_gpio_output_set(&led, 1);
printf("key pressed\r\n");
}
else {
hosal_gpio_output_set(&led, 0);
}
}
}
}
vTaskDelay(1 / portTICK_PERIOD_MS);
}
}
void main(void) {
init_led();
init_key();
xTaskCreate(key_task, "key_task", 1024, NULL, 15, NULL);
}

在示例代码中,我们使用了RTOS抽象层API。 首先,定义 hosal_gpio_devt实例,用于配置LED引脚和按键引脚。然后调用 hosal_gpio_init 函数初始化引脚配置,接通过调用hosal_gpio_input_get 函数来读取按键引脚的按键值,然后根据按键的电平值来通过调用 hosal_gpio_output_set 函数实现。设置LED引用的电平值. 3. GPIO中断 示例代码如下

📜 点击展开完整代码
c
#include <stdio.h>
#include <string.h>
#include <FreeRTOS.h>
#include <task.h>
#include <bl_gpio.h>
#include <stdio.h>
#include <hosal_gpio.h>
#include <hosal_dma.h>
#include <blog.h>
#include <stdbool.h>
#define LED_PIN 3
#define KEY_PIN 4
#define TAG "gpio_exti"
static bool led_state = false;
static hosal_gpio_dev_t led;
static hosal_gpio_dev_t key;
static int32_t counter = 0;
static void init_led(void) {
led.port = LED_PIN;
led.config = OUTPUT_OPEN_DRAIN_NO_PULL;
hosal_gpio_init(&led);
printf("led inited\r\n");
}
static void key_irq_callback(void* arg) {
printf("key pressed:%d\r\n",counter++);
led_state = !led_state;
hosal_gpio_output_set(&led, led_state);
}
static void init_key() {
key.port = KEY_PIN;
key.config = INPUT_PULL_UP;
hosal_gpio_init(&key);
hosal_gpio_irq_set(&key, HOSAL_IRQ_TRIG_NEG_PULSE, key_irq_callback, NULL);
printf("key inited\r\n");
}
void main(void) {
init_led();
init_key();
}

在示例代码中,LED引脚和按键引脚初始化与前面GPIO输入的步骤一样。在按键初引脚始化完成之后,通过调用 hosal_gpio_irq_set 来实现按键中断配置。

Released under the MIT License. Build Time 2026-09-11 14:52:23