4、GPIO基础编程:GPIO模块初始化、输入输出模式配置、中断触发与回调函数,实战:按键控制LED
好,咱们今天聊聊GPIO。这玩意儿可以说是嵌入式开发里最基础、也最常用的外设了。你想想看,一个芯片要跟外界打交道,总得有个“手脚”吧?GPIO就是干这个的。
我个人习惯,拿到一个新开发板,第一件事就是点灯。别笑,这真不是闹着玩的。点灯能帮你快速验证硬件连接、开发环境、下载调试链路是不是通的。要是灯都不亮,后面写再多代码也是白搭。
4.1 GPIO模块初始化
在TI的无线连接芯片上,GPIO的初始化其实挺讲究的。不像有些单片机,上电默认就是普通IO。TI的芯片,很多引脚是复用的——它可能同时是GPIO、也可能是I2C、SPI、UART,甚至是RF相关的引脚。
所以第一步,你得告诉芯片:“嘿,这个脚,我要当普通IO用。”
初始化流程大致分三步:
- 使能GPIO模块时钟——没电怎么干活?
- 配置引脚功能为GPIO——从复用功能里“抢”过来。
- 设置方向(输入/输出)——你是要听,还是要说?
代码大概长这样:
// 使能GPIO时钟
PRCMGPIOClockEnable();
// 配置引脚为GPIO功能
PinTypeGPIO(PIN_LED, PIN_MODE_0);
// 设置方向为输出
GPIODirModeSet(GPIOA_BASE, PIN_LED, GPIO_DIR_MODE_OUT);
嗯,这里要注意:PinTypeGPIO这个函数,不同型号的TI芯片名字可能不一样。有的叫IOCPinTypeGPIO,有的直接操作寄存器。我建议你养成好习惯,每次都用TI的驱动库函数,别自己手撕寄存器——除非你特别清楚自己在干什么。
4.2 输入输出模式配置
GPIO的方向配置,说白了就是决定这个脚是“说话”还是“听话”。
输出模式:芯片往外送电平。高电平就是3.3V(或者1.8V,看你的VDD),低电平就是GND。用来控制LED、继电器、蜂鸣器这些。
输入模式:芯片读取外部电平。比如检测按键有没有按下、传感器输出是高是低。
配置代码很简单:
// 配置为输出
GPIODirModeSet(GPIOA_BASE, PIN_LED, GPIO_DIR_MODE_OUT);
// 配置为输入
GPIODirModeSet(GPIOA_BASE, PIN_KEY, GPIO_DIR_MODE_IN);
但这里有个坑——上拉和下拉。输入模式下,如果外部引脚悬空(什么都没接),电平是不确定的。你读到的值可能是0,也可能是1,完全看心情。
解决办法?加个上拉电阻或者下拉电阻。TI的芯片内部一般都集成了这个功能,不用你外接。
// 使能内部上拉
GPIOPadConfigSet(GPIOA_BASE, PIN_KEY, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU);
我曾经在一个项目里,按键没配置上拉,结果按键没按下时,程序老是误触发。折腾了两天,最后发现是内部上拉没使能。你说冤不冤?
| 配置选项 | 说明 | 适用场景 |
|---|---|---|
GPIO_PIN_TYPE_STD |
标准推挽输出 / 高阻输入 | 输出驱动LED,输入有外部上拉 |
GPIO_PIN_TYPE_STD_WPU |
标准输出 / 带上拉输入 | 按键输入(最常用) |
GPIO_PIN_TYPE_STD_WPD |
标准输出 / 带下拉输入 | 某些传感器,默认低电平 |
GPIO_PIN_TYPE_OD |
开漏输出 | I2C总线、电平转换 |
4.3 中断触发与回调函数
轮询读按键?可以,但太浪费CPU了。你想想看,CPU一直在那问:“按键按了吗?按了吗?按了吗?”——啥正事都干不了。
更好的办法是中断。按键按下那一刻,硬件自动通知CPU:“嘿,别忙了,这边有事!”
TI芯片的GPIO中断配置,一般分这几步:
- 配置中断触发方式——上升沿、下降沿、还是双边沿?
- 注册中断服务函数——也就是回调函数,告诉CPU中断来了该干啥。
- 使能中断——打开总开关。
代码示例:
// 配置下降沿触发(按键按下时,电平从高变低)
GPIOIntTypeSet(GPIOA_BASE, PIN_KEY, GPIO_FALLING_EDGE);
// 注册中断回调函数
GPIOIntRegister(GPIOA_BASE, PIN_KEY, KeyPressCallback);
// 使能中断
GPIOIntEnable(GPIOA_BASE, PIN_KEY);
回调函数长这样:
void KeyPressCallback(void)
{
// 清除中断标志——这一步千万别忘!
GPIOIntClear(GPIOA_BASE, PIN_KEY);
// 处理按键逻辑
// 比如翻转LED
GPIOPinWrite(GPIOA_BASE, PIN_LED,
~GPIOPinRead(GPIOA_BASE, PIN_LED));
}
另外,中断回调函数里不要做耗时操作。比如别在里面加延时、别做复杂的计算。原则是:快进快出。你可以在回调里设一个标志位,然后在主循环里处理具体逻辑。
4.4 实战:按键控制LED
好,理论说完了,咱们来点实际的。写一个完整的例子:按一下按键,LED状态翻转。
硬件连接:
- LED接在PA0(输出)
- 按键接在PA1(输入,内部上拉)
完整代码:
#include <ti/drivers/GPIO.h>
#include <ti/drivers/Power.h>
// 引脚定义
#define PIN_LED 0
#define PIN_KEY 1
// 全局标志
volatile uint8_t g_keyPressed = 0;
// 按键中断回调
void KeyPressCallback(uint_least8_t index)
{
// 清除中断
GPIOIntClear(PIN_KEY);
// 设标志
g_keyPressed = 1;
}
void main(void)
{
// 初始化
Power_init();
GPIO_init();
// 配置LED为输出,初始低电平
GPIO_setConfig(PIN_LED, GPIO_CFG_OUTPUT);
GPIO_write(PIN_LED, 0);
// 配置按键为输入,内部上拉,下降沿中断
GPIO_setConfig(PIN_KEY, GPIO_CFG_IN_PU | GPIO_CFG_IN_INT_FALLING);
GPIO_setCallback(PIN_KEY, KeyPressCallback);
GPIO_enableInt(PIN_KEY);
while(1)
{
if(g_keyPressed)
{
g_keyPressed = 0;
// 翻转LED
uint8_t state = GPIO_read(PIN_LED);
GPIO_write(PIN_LED, !state);
}
// 其他任务...
// 休眠或低功耗模式
Power_sleep();
}
}
好了,这一章的内容就这些。GPIO看似简单,但它是所有外设的基础。你把这个玩熟了,后面学I2C、SPI、UART都会轻松很多。下一章咱们聊聊定时器——那个东西更有意思。