开发环境:
IDE:MKD 5.30
开发板:RA-Eco-RA4M2
MCU:R7FA4M2AD3CFP
按键 GPIO 端口有两个方案可以选择,一是采用上拉输入模式,因为按键在没按下的时候,是默认为高电平的,采且内部上拉模式正好符合这个要求。第二个方案是直接采用浮空输入模式,因为按照硬件威廉希尔官方网站 图,在芯片外部接了上拉电阻,其实就没必要再配置成内部上拉输入模式了,因为在外部上拉与内部上拉效果是一样的。
RA-Eco-RA4M2开发板采用的外部上拉模式。
首先使用Renesas RA Smart Configurator软件配置按键的Pin。
打开Renesas RA Smart Configurator软件。将连接到按键的 IO 引脚的“Mode” 属性配置为“Input Mode”。
后点右上角的 “Generate Project Content” 图标,让软件根据我们的设置自动生成配置代码即可。
按键检测的程序很贱,不断检测Pin是否按下,首先通过 R_IOPORT_Open 函数初始化配置GPIO引脚,之后使用 R_IOPORT_PinRead 函数来获取当前引脚的电平的状态。
完整代码请参附件,这里只贴出核心代码。
GPIO已经统一初始化了,因此这里就不需要再次初始化了,关于GPIO的初始化可以看前面的章节,笔者已经深入剖析过了。
/**
* @brief 检测是否有按键按下
* @param key_ioport_ctrl, key_pin, key_value:
* @retval 按键的状态
* @arg KEY_ON:按键按下
* @arg KEY_OFF:按键没按下
*/
bsp_io_level_t Key_Scan(ioport_ctrl_t * key_ioport_ctrl, bsp_io_port_pin_t key_pin, bsp_io_level_t *key_value)
{
/*检测是否有按键按下 */
if(R_IOPORT_PinRead(key_ioport_ctrl, key_pin, key_value) == KEY_ON )
{
/*延时消抖*/
Key_Delay(100);
if(R_IOPORT_PinRead(key_ioport_ctrl, key_pin, key_value) == KEY_ON )
{
/*等待按键释放 */
while(R_IOPORT_PinRead(key_ioport_ctrl, key_pin, key_value) == KEY_OFF );
return KEY_ON;
}
else
return KEY_OFF;
}
else
return KEY_OFF;
}
相信延时消抖的原理大家在学习其他单片机时就已经了解了,本函数的功能就是扫描输入参数中指定的引脚,检测其电平变化,并作延时消抖处理,最终对按键消息进行确认。
hal_entry函数代码如下:
void hal_entry(void)
{
bsp_io_level_t key_value_005;
bsp_io_level_t key_value_006;
SysTick_Init(); //初始化系统时钟
/* TODO: add your own code here */
while(1)
{
Key_Scan(&g_ioport_ctrl, GPIO_KEY1, &key_value_005);
Key_Scan(&g_ioport_ctrl, GPIO_KEY2, &key_value_006);
if( key_value_005 == KEY_ON ) //扫描判断按键
{
R_IOPORT_PinWrite(&g_ioport_ctrl, GPIO_LED1, BSP_IO_LEVEL_HIGH);
R_IOPORT_PinWrite(&g_ioport_ctrl, GPIO_LED2, BSP_IO_LEVEL_HIGH);
R_IOPORT_PinWrite(&g_ioport_ctrl, GPIO_LED3, BSP_IO_LEVEL_HIGH);
}
if( key_value_006 == KEY_ON ) //扫描判断按键
{
R_IOPORT_PinWrite(&g_ioport_ctrl, GPIO_LED1, BSP_IO_LEVEL_LOW);
R_IOPORT_PinWrite(&g_ioport_ctrl, GPIO_LED2, BSP_IO_LEVEL_LOW);
R_IOPORT_PinWrite(&g_ioport_ctrl, GPIO_LED3, BSP_IO_LEVEL_LOW);
}
}
#if BSP_TZ_SECURE_BUILD
/* Enter non-secure code */
R_BSP_NonSecureEnter();
#endif
这里两个按键都用上,一个用于关灯,一个用于开灯。
IRQ(Interrupt ReQuest)就是指中断请求,通过 GPIO 检测输入脉冲,引起中断事件,打断原来的代码执行流程,进入到中断服务函数中进行处理,处理完后再返回到中断之前的代码中执行。
Cortex-M内核具有强大的异常响应系统,它把能够打断当前代码执行流程的事件分为异常(exception)和中断(interrupt),并把它们用一个表管理起来,编号为 0 ~ 15 的称为内核异常,而 16 以上的则称为外部中断(外是相对内核而言),这个表就称为中断向量表。
而RA4M2对这个表重新进行了编排,把编号从 –3 至 6 的中断向量定义为系统异常,编号为负的内核异常不能被设置优先级,如复位(Reset)、不可屏蔽中断 (NMI)、硬错误(Hardfault)。从编号16开始的为外部中断,这些中断的优先级都是可以自行设置的。详细的 RA4M2中断向量表见下表。
RA4M2的中断如此之多,配置起来并不容易,因此我们需要一个强大而方便的中断控制器 NVIC (Nested Vectored Interrupt Controller)。NVIC 是属于 Cortex 内核的器件,不可屏蔽中断 (NMI)和外部中断都由它来处理,而SysTick不是由 NVIC 来控制的。
可以在固件库core_cm33.h看到关于NVIC的结构与相关的函数。
以上就是NVIC的主要函数。
的所有 I/O 端口都可以配置为 EXTI 中断模式,用来捕捉外部信号,可以配置为下降沿中断、上升沿中断和上升下降沿中断这三种模式。
RA4M2的所有 GPIO 都引入到EIRQ外部中断线上,使得所有的 GPIO 都能作为外部中断的输入源。GPIO 与 EIRQ的连接方式见图Figure 2‑2。
观察Figure 2‑2可知,GPIO接到了IRQx上。ICU通过IELSRn.IELS[8:0]从外围功能中断或外部引脚中断选择事件源输入。接受的中断源将ELSRn.IR设置为1,并向NVIC发送中断请求。外部引脚中断请求通过以下任一方式检测:
●边沿(下降沿、上升沿或上升沿和下降沿)
●中断信号的电平(低电平)。
设置IRQCRi.IRQMD[1:0]位以选择RQi引脚的检测模式。对于与外围模块相关的中断源,事件必须在中断发生之前被NVIC接受并被CPU接受。
EIRQ最普通的应用就是接上一个按键,设置为下降沿触发,用中断来检测按键。
GPIO的中断事件表如下图所示。
打开Renesas RA Smart Configurator软件。将按键的的Pin为配置为中断模式。
然后配置外部中断模块。
配置参数如下所示:
配置完成之后点右上角的“Generate Project Content” 按钮,让软件自动生成配置代码即可
要想实现外部引脚中断,主要步骤如下:
1.配置IO端口设置
2.将RQCRi..FLTEN位(i=0到15)清零(禁用数字滤波器)
3.设置给定IRQCRi寄存器(i=0到15)的IRQMD[1:0]位以选择检测的意义。
4.设置FCLKSEL[1:O]位和IRQCRi寄存器的FLTEN位。
5.选择IRQ引脚如下:
●如果IRQ引脚用于CPU中断请求,请将IELSRn.IELS[8:O]位和IELSRn.DTCE位设置为0
●如果IRQ引脚用于DTC激活,请将IELSRn.IELS[8:O]位和IELSRn.DTCE位设置为1。
●如果IRQ引脚用于激活DMAC,设置DELSRn.DELS[8:O]位。
首先初始化按键的GPIO引脚,这部分代码在FSP已经做过了,因此只需要打开相应IQR即可。
/**
* [url=home.php?mod=space&uid=2666770]@Brief[/url] IRQ 初始化函数
* [url=home.php?mod=space&uid=3142012]@param[/url] None
* @retval None
*/
void Key_IRQ_Init(void)
{
/* 初始化外部中断 */
R_ICU_ExternalIrqOpen(g_sw1.p_ctrl, g_sw1.p_cfg);
R_ICU_ExternalIrqEnable(g_sw1.p_ctrl);
R_ICU_ExternalIrqOpen(g_sw2.p_ctrl, g_sw2.p_cfg);
R_ICU_ExternalIrqEnable(g_sw2.p_ctrl);
}
然后就是中断回调函数的编写。
/**
* @brief SW1回调函数
* @param p_args
* @retval None
*/
void SW1_Callback(external_irq_callback_args_t *p_args)
{
(void) p_args;
R_IOPORT_PinWrite(&g_ioport_ctrl, GPIO_LED1, BSP_IO_LEVEL_HIGH);
R_IOPORT_PinWrite(&g_ioport_ctrl, GPIO_LED2, BSP_IO_LEVEL_HIGH);
R_IOPORT_PinWrite(&g_ioport_ctrl, GPIO_LED3, BSP_IO_LEVEL_HIGH);
}
/**
* @brief SW2回调函数
* @param p_args
* @retval None
*/
void SW2_Callback(external_irq_callback_args_t *p_args)
{
(void) p_args;
R_IOPORT_PinWrite(&g_ioport_ctrl, GPIO_LED1, BSP_IO_LEVEL_LOW);
R_IOPORT_PinWrite(&g_ioport_ctrl, GPIO_LED2, BSP_IO_LEVEL_LOW);
R_IOPORT_PinWrite(&g_ioport_ctrl, GPIO_LED3, BSP_IO_LEVEL_LOW);
}
代码很简单,一个负责打开LED,一个负责关闭LED。
最后看看hal_entry函数。
void hal_entry(void)
{
SysTick_Init(); //初始化系统时钟
Key_IRQ_Init();
/* TODO: add your own code here */
while(1)
{
Delay_ms(500);
}
#if BSP_TZ_SECURE_BUILD
/* Enter non-secure code */
R_BSP_NonSecureEnter();
#endif
}
代码很简单,就不赘述了。
编译好程序后,下载到板子上,不管是普通方式还是中断方式,当按在按键SW1时,LED亮,当按在按键SW2时,LED灭。
更多回帖