第4章 GPIO编程实战:GPIO模式配置、寄存器操作与LED闪烁实验

各位同学,欢迎来到第四章。前面我们聊了开发环境搭建和C语言基础,今天终于要上手操作硬件了。

GPIO,全称是General Purpose Input Output,通用输入输出口。说白了,它就是芯片和外界沟通的“手脚”。你想让LED亮?GPIO输出高电平。你想检测按键是否按下?GPIO读取电平状态。你想让外部事件通知CPU?GPIO中断。

我个人习惯把GPIO编程分成三个层次:模式配置、电平操作、中断响应。今天我们就一个一个来啃。

4.1 GPIO模式配置:输入、输出与中断

MTK平台的GPIO,每个引脚都可以独立配置模式。你想想看,一个引脚既能当输入,又能当输出,还能当中断源,靠的就是寄存器里的模式位。

常见的模式有这几种:

模式 用途 典型场景
GPIO_MODE_OUTPUT 输出模式 控制LED、蜂鸣器
GPIO_MODE_INPUT 输入模式 读取按键、传感器状态
GPIO_MODE_INT 中断模式 检测外部事件触发
GPIO_MODE_AF 复用功能 UART、I2C等外设

嗯,这里要注意:配置模式之前,一定要先使能GPIO时钟。我在项目中遇到过好几次,明明代码写对了,LED就是不亮,查了半天才发现是时钟没开。这个坑,你们一定要记住。

4.2 寄存器操作:直接操控硬件

MTK的GPIO寄存器,通常分为几大类:

  • GPIO_DIR:方向寄存器,0是输入,1是输出
  • GPIO_DOUT:数据输出寄存器,写1高电平,写0低电平
  • GPIO_DIN:数据输入寄存器,读取引脚当前电平
  • GPIO_PULLEN:上下拉使能寄存器
  • GPIO_PULLSEL:上下拉选择寄存器

直接操作寄存器,说白了就是往特定内存地址写值。MTK平台通常把寄存器映射到某个基地址上,然后通过偏移量访问。

举个例子,假设GPIO基地址是0x10001000,GPIO0的DIR寄存器偏移是0x00,DOUT偏移是0x04:

#define GPIO_BASE       0x10001000
#define GPIO_DIR        (*(volatile unsigned int *)(GPIO_BASE + 0x00))
#define GPIO_DOUT       (*(volatile unsigned int *)(GPIO_BASE + 0x04))

// 设置GPIO0为输出模式
GPIO_DIR |= (1 << 0);

// GPIO0输出高电平
GPIO_DOUT |= (1 << 0);

// GPIO0输出低电平
GPIO_DOUT &= ~(1 << 0);
警告:操作寄存器时,一定要用 volatile 关键字。否则编译器优化后,你的写操作可能被“吃掉”。我曾经因为这个bug,整整调了两天。

4.3 LED闪烁实验:从理论到实践

好了,理论讲完了,我们来做一个经典的LED闪烁实验。这个实验虽然简单,但包含了GPIO编程的完整流程。

实验目标:让板载LED以1秒为周期闪烁。

硬件连接:LED正极接GPIO0,负极接地(串联一个330Ω电阻)。

代码实现:

#include "mtk_gpio.h"

// 延时函数,粗略延时
void delay_ms(unsigned int ms)
{
    volatile unsigned int i, j;
    for(i = 0; i < ms; i++)
        for(j = 0; j < 1000; j++);
}

int main(void)
{
    // 1. 使能GPIO时钟
    mtk_gpio_clock_enable(GPIO0);
    
    // 2. 配置GPIO0为输出模式
    mtk_gpio_set_mode(GPIO0, GPIO_MODE_OUTPUT);
    
    // 3. 主循环:LED闪烁
    while(1)
    {
        mtk_gpio_set_high(GPIO0);   // LED亮
        delay_ms(500);              // 延时500ms
        
        mtk_gpio_set_low(GPIO0);    // LED灭
        delay_ms(500);              // 延时500ms
    }
    
    return 0;
}
小技巧:实际项目中,延时函数不要用这种空循环。CPU空转太浪费了。更好的做法是用硬件定时器,或者RTOS的延时函数。这个我们后面章节会讲。

4.4 中断模式配置:让GPIO“主动”通知你

轮询方式读取GPIO,CPU一直在“问”引脚状态,效率很低。中断模式就不一样了——引脚电平变化时,硬件自动触发中断,CPU再去处理。

配置中断的步骤:

  1. 配置GPIO为输入模式
  2. 设置中断触发方式(上升沿、下降沿、双边沿、高电平、低电平)
  3. 使能GPIO中断
  4. 编写中断服务函数(ISR)

代码示例:

// 配置GPIO1为下降沿触发中断
mtk_gpio_set_mode(GPIO1, GPIO_MODE_INPUT);
mtk_gpio_set_int_type(GPIO1, GPIO_INT_FALLING_EDGE);
mtk_gpio_enable_int(GPIO1);

// 中断服务函数
void GPIO1_IRQHandler(void)
{
    // 清除中断标志
    mtk_gpio_clear_int(GPIO1);
    
    // 处理中断事件
    // 比如:记录按键按下次数
    key_press_count++;
}

重点:中断服务函数里,一定要先清除中断标志,再处理业务逻辑。否则会反复进入中断,造成死循环。我刚开始做中断时,就犯过这个错误,CPU直接卡死。

4.5 避坑指南:我踩过的那些GPIO坑

做GPIO开发这么多年,我总结了一些常见问题,分享给你们:

  • 上下拉配置不对:输入模式下,如果引脚浮空,电平会不稳定。记得配置上拉或下拉。
  • 中断优先级:多个中断同时触发时,优先级低的可能被“饿死”。合理分配优先级。
  • 去抖处理:机械按键按下时,会有抖动。硬件加电容,软件加延时去抖。
  • 驱动能力:GPIO输出电流有限,驱动大负载要加三极管或MOS管。

我曾经在一个项目中,GPIO直接驱动继电器,结果芯片烧了。后来才知道,继电器线圈需要几百毫安电流,GPIO根本扛不住。嗯,这个教训很深刻。

4.6 本章小结

今天我们做了三件事:

  1. 学会了GPIO三种模式的配置方法
  2. 掌握了直接操作寄存器的技巧
  3. 完成了LED闪烁实验,并了解了中断配置

下一章,我们会深入定时器编程。到时候,LED闪烁就不用空循环延时了,用硬件定时器精确控制。敬请期待。

记住:纸上得来终觉浅,绝知此事要躬行。 拿起你的开发板,把今天的代码跑一遍。遇到问题,欢迎在课程群里交流。