4. GPIO与引脚复用配置实战

在RA FSP平台中,GPIO并不是一个简单的“置高置低”外设,而是由IOPORT模块统一管理引脚方向、电平、复用功能与中断输入。理解IOPORT的API结构,是完成按键输入与LED输出交互程序的基础。本节以RA4M2(Cortex-M33,100引脚LQFP)为例,围绕“LED输出 + 按键输入 + 外部中断”这一典型任务,讲解从引脚规划到代码落地的完整过程。

一、IOPORT模块的API层次

FSP的IOPORT模块对外提供三层能力:引脚控制层(方向、电平、上下拉)、复用功能层(PSEL/PMR/PODR等寄存器映射)、中断输入层(IRQ与ICU联动)。常用API如下:

  • R_IOPORT_PinCfg():按ioport_pin_cfg_t结构一次性配置方向、上下拉、驱动能力、初始电平。
  • R_IOPORT_PinWrite() / R_IOPORT_PinRead():写/读单个引脚电平,返回fsp_err_t。
  • R_IOPORT_PortWrite() / R_IOPORT_PortRead():按端口(如PORT1、PORT4)批量操作,适合并口LED或矩阵按键。
  • R_IOPORT_PinsCfg():批量配置多个引脚,常用于初始化阶段。
  • R_IOPORT_Open():打开IOPORT实例,通常在hal_entry()中由FSP自动调用。

需要特别注意的是:引脚复用功能(Peripheral Function)的切换并不通过IOPORT API完成,而是在FSP Configurator的“Pins”视图中配置,生成代码写入PmnPFS寄存器(PSEL、PMR、PODR、PCR等)。IOPORT API只负责“通用IO”语义下的读写与方向控制。若某引脚已被配置为SCI、SPI或AGT等外设功能,再调用R_IOPORT_PinWrite()不会改变其复用输出,必须先在FSP中把该引脚改回GPIO模式。

二、引脚规划与FSP配置

假设硬件连接如下(示例参数,实际以原理图为准):

功能引脚端口/位方向上下拉初始电平
LED1(红)P405PORT4.5输出无高(灭)
LED2(绿)P404PORT4.4输出无高(灭)
KEY1P105PORT1.5输入上拉—
KEY2(中断)P106PORT1.6输入上拉—

在FSP Configurator中依次完成:Stacks → New Stack → Input → External IRQ,选择IRQ通道(如IRQ6对应P106);在Pins视图中把P405、P404设为GPIO Output,P105、P106设为GPIO Input并勾选Pull-up。生成代码后,hal_data.c中会出现g_ioport实例与g_external_irq6实例。

RA4M2 GPIO / 引脚复用 / 外部中断 信号链 RA4M2 MCU 内部 IOPORT 模块 PmnPFS: PSEL / PMR / PODR / PCR ICU 中断控制单元 IRQ6 → NVIC → ISR 应用层 R_IOPORT_PinWrite / PinRead FSP Configurator Pins 视图 → 复用功能 Stacks → External IRQ 生成 hal_data.c 引脚 P405 P404 P105 P106 复用功能 由 PSEL 决定 GPIO 模式 PMR=0 外部器件 LED1(红) 低电平点亮 LED2(绿) 低电平点亮 KEY1 按下接地,查询读取 KEY2 按下接地,触发IRQ6 IRQ6 触发 → ISR 回调 注:LED 低电平点亮;按键按下为低电平;P106 复用为 IRQ6 输入

三、LED输出:从寄存器到API

LED接在P405、P404,低电平点亮。初始化时通过R_IOPORT_PinCfg()把两脚设为输出、初始高电平:

ioport_pin_cfg_t led_cfg = {
    .pin     = BSP_IO_PORT_04_PIN_05,
    .dir     = IOPORT_DIRECTION_OUTPUT,
    .pull    = IOPORT_CFG_PULL_UP_OFF,
    .drive   = IOPORT_CFG_DRIVE_MID,
    .level   = IOPORT_LEVEL_HIGH
};
R_IOPORT_PinCfg(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_05, &led_cfg);
R_IOPORT_PinCfg(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_04, &led_cfg);

点亮LED1只需R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_05, BSP_IO_LEVEL_LOW);。若使用R_IOPORT_PortWrite(),可一次写整个PORT4,但要注意不要覆盖同端口其他已配置引脚的状态,通常配合读-改-写或使用位掩码。

四、按键输入与消抖

KEY1接P105,上拉输入,按下为低。查询方式读取:

bsp_io_level_t level;
R_IOPORT_PinRead(&g_ioport_ctrl, BSP_IO_PORT_01_PIN_05, &level);
if (BSP_IO_LEVEL_LOW == level) { /* 按键按下 */ }

机械按键存在5~20 ms抖动,直接在主循环判断会误触发。推荐做法是:检测到低电平后延时10 ms再读一次,两次一致才确认;或者用AGT定时器每5 ms采样一次,连续3次为低才判定按下。示例中采用R_BSP_SoftwareDelay(10, BSP_DELAY_UNITS_MILLISECONDS)做简单消抖,实际产品建议用定时器状态机。

五、外部中断配置

KEY2接P106并复用为IRQ6。FSP中配置External IRQ属性:Trigger选择Falling(下降沿,按下瞬间触发),Callback填入key2_isr,优先级设为12(低于系统节拍)。生成代码后,中断服务函数中应尽量短小,只置标志位,主循环再处理:

volatile bool g_key2_pressed = false;

void key2_isr(external_irq_callback_args_t *p_args) {
    (void)p_args;
    g_key2_pressed = true;
}

/* 主循环 */
if (g_key2_pressed) {
    g_key2_pressed = false;
    R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_04, BSP_IO_LEVEL_LOW);
    R_BSP_SoftwareDelay(200, BSP_DELAY_UNITS_MILLISECONDS);
    R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_04, BSP_IO_LEVEL_HIGH);
}
若中断触发后无法再次进入,检查是否在ISR中调用了R_ICU_ExternalIrqEnable()重新使能,或确认FSP中IRQ的“Digital Filter”是否开启。RA的ICU支持数字滤波,可有效抑制按键毛刺。

六、交互程序闭环

把上述片段组合:上电后LED1、LED2灭;按下KEY1,LED1点亮并保持;按下KEY2,LED2闪烁一次。判断依据是:用万用表或示波器观察P405/P404电平翻转,或在调试器中查看g_key2_pressed变量与IRQ6计数。若LED不亮,先确认P405是否被其他外设复用(PSEL≠0),再检查R_IOPORT_Open()是否成功返回FSP_SUCCESS。

常见错误:在FSP Pins视图中把P106配置为“GPIO Input”后,又去Stacks中添加External IRQ,两者会冲突。正确顺序是先添加External IRQ并选择P106,FSP会自动把该引脚设为IRQ功能,无需再手动设为GPIO。