3. GPIO驱动与板级引脚映射
在RA系列MCU中,GPIO并不是一个单一外设,而是由PORT外设统一管理的一组寄存器阵列。每个物理引脚都对应PORT模块中的若干位,通过写寄存器来设定方向、读取电平、控制输出、选择复用功能。理解PORT的寄存器结构,是后续用FSP配置引脚、写按键与LED驱动、以及把代码引脚号和原理图网络对应起来的基础。
一、RA的PORT外设寄存器结构
RA的PORT外设按端口分组,例如P0、P1、P4、P5等,每个端口通常有16个引脚(Px00~Px15)。每个端口对应一组寄存器,关键寄存器包括:
- PCNTR1:包含PDR(方向寄存器)和PODR(输出数据寄存器)。PDR某位为1表示输出,为0表示输入;PODR某位决定输出引脚的电平。
- PCNTR2:包含PIDR(输入数据寄存器)和EIDR(事件输入数据寄存器),用于读取引脚当前电平。
- PCNTR3:包含PORR(输出置位)和POSR(输出复位),用于原子性地置位/复位输出,避免读改写竞争。
- PCNTR4:包含EORR和EOSR,用于事件输出置位/复位。
- PmnPFS:每个引脚一个引脚功能选择寄存器,控制复用功能、上下拉、驱动能力、输入缓冲使能等。
以P4端口为例,P400对应P4PFS0,P401对应P4PFS1,依次类推。PFS寄存器中的PSEL字段选择该引脚作为GPIO还是外设功能(如SCI、SPI、AGT等);PMR位为1表示外设模式,为0表示GPIO模式;PCR位控制上拉;PDR位控制方向;NCODR控制开漏;ISEL控制输入方式。这些位组合决定了引脚的实际行为。
二、FSP中的pin配置与引脚复用
在e² studio中使用FSP配置器时,引脚配置集中在Pins标签页。每个引脚可以设置为“GPIO”或某个外设信号。配置器会自动生成pin_data.c和hal_data.c中的初始化代码,并在pin_data.c里生成g_bsp_pin_cfg数组,描述每个引脚的PFS值。
例如,把P405配置为输出、初始低电平、无上拉,配置器会生成类似下面的结构:
/* P405 */
.pin = (bsp_io_port_pin_t) BSP_IO_PORT_04_PIN_05,
.pin_cfg = (uint32_t) IOPORT_CFG_PORT_DIRECTION_OUTPUT
| IOPORT_CFG_PORT_OUTPUT_LOW,
如果P405要复用为SCI9的TXD,则配置器会把它归到SCI9的引脚组,并在g_bsp_pin_cfg中写入对应的PSEL值。FSP在R_BSP_PinCfg()中遍历该数组,把每个引脚的PFS寄存器写成配置值。因此,FSP的pin配置本质上是把图形化选择翻译成PFS寄存器值,理解这一点后,手工修改或排查引脚问题就有据可依。
三、按键输入与LED输出驱动
用FSP的IOPORT模块驱动GPIO非常直接。LED输出通常用R_IOPORT_PinWrite(),按键输入用R_IOPORT_PinRead()。下面是一个按键控制LED的示例,假设LED接P405,按键接P404,按键按下为低电平:
#include "hal_data.h"
void key_led_demo(void)
{
bsp_io_level_t key_level;
/* 读取按键引脚 P404 */
R_IOPORT_PinRead(&g_ioport_ctrl,
BSP_IO_PORT_04_PIN_04,
&key_level);
if (BSP_IO_LEVEL_LOW == key_level)
{
/* 按键按下,点亮 LED P405 */
R_IOPORT_PinWrite(&g_ioport_ctrl,
BSP_IO_PORT_04_PIN_05,
BSP_IO_LEVEL_HIGH);
}
else
{
R_IOPORT_PinWrite(&g_ioport_ctrl,
BSP_IO_PORT_04_PIN_05,
BSP_IO_LEVEL_LOW);
}
}
这段代码在轮询方式下工作。实际项目中,按键需要消抖:可以在检测到低电平后延时10~20 ms再读一次,或者用定时器中断周期采样。LED若为低电平点亮,则输出逻辑要反过来。FSP的R_IOPORT_PinWrite()内部会操作PODR或PORR/POSR,具体取决于底层实现,但对外表现为设置引脚输出电平。
对于需要频繁翻转的LED,可以用R_IOPORT_PinWrite()配合软件延时,也可以使用AGT或GPT定时器产生周期中断,在中断里翻转引脚。若使用R_IOPORT_PinWrite(),注意它可能涉及读改写,在多任务或中断中操作同一端口的不同引脚时,建议使用R_IOPORT_PinWrite()的原子版本或直接写PORR/POSR。
四、板级引脚定义与原理图对照
板级适配的核心工作之一,是把原理图上的网络名和MCU引脚号对应起来,并形成一份可维护的引脚定义表。通常做法是在项目中建立一个board_pins.h,用宏把“功能名”映射到FSP的引脚枚举:
#define LED1_PIN BSP_IO_PORT_04_PIN_05
#define LED2_PIN BSP_IO_PORT_04_PIN_06
#define KEY1_PIN BSP_IO_PORT_04_PIN_04
#define KEY2_PIN BSP_IO_PORT_04_PIN_07
这样,应用层只使用LED1_PIN,不直接写端口号。当硬件改版、引脚调整时,只需修改board_pins.h和FSP配置,应用代码不动。对照原理图时,要确认三件事:网络名对应的MCU引脚号、该引脚在FSP中是否已配置为GPIO、以及外部电路是否有上下拉或驱动反相。例如原理图上LED阳极接3.3 V、阴极经限流电阻接P405,则P405输出低电平点亮,代码中就要写BSP_IO_LEVEL_LOW为点亮。
上图把PORT寄存器、FSP配置和板级映射三者的关系串了起来:PFS寄存器是引脚行为的最终落点,FSP配置器负责把图形化选择翻译成PFS值,而board_pins.h负责把功能名与具体引脚枚举解耦。实际调试时,如果LED不亮,可以按这个顺序排查:先确认原理图网络名和引脚号是否对应,再确认FSP中该引脚是否配置为输出,然后确认代码中使用的宏是否指向正确引脚,最后用万用表或示波器测量引脚电平。
R_IOPORT_PinCfg()可以在运行时重新配置引脚,但频繁调用会影响实时性。总结来说,RA的GPIO驱动围绕PORT寄存器展开,FSP把寄存器操作封装成配置器和IOPORT API,板级适配则通过引脚定义头文件把硬件和软件连接起来。掌握这三层关系,就能在原理图、FSP配置和C代码之间快速定位问题,也能在硬件改版时以最小代价完成迁移。