瑞萨单片机论坛
直播中

ouxiaolong

10年用户 470经验值
擅长:嵌入式技术 光电显示
私信 关注
[经验]

【RA4M2设计挑战赛】智能家居助手之按键

开发环境

IDE:MKD 5.30

开发板:RA-Eco-RA4M2

MCU:R7FA4M2AD3CFP

1 普通方式

1.1 普通方式工作原理

按键 GPIO 端口有两个方案可以选择,一是采用上拉输入模式,因为按键在没按下的时候,是默认为高电平的,采且内部上拉模式正好符合这个要求。第二个方案是直接采用浮空输入模式,因为按照硬件电路图,在芯片外部接了上拉电阻,其实就没必要再配置成内部上拉输入模式了,因为在外部上拉与内部上拉效果是一样的。

1677249504924xgfdfvdm1l

RA-Eco-RA4M2开发板采用的外部上拉模式。

1.2 普通方式实现

首先使用Renesas RA Smart Configurator软件配置按键的Pin。

1.2.1 FSP配置

打开Renesas RA Smart Configurator软件。将连接到按键的 IO 引脚的“Mode” 属性配置为“Input Mode”。

1677249505553b0dz41pi3r

后点右上角的 “Generate Project Content” 图标,让软件根据我们的设置自动生成配置代码即可。

1.2.2 按键轮询方式实现

按键检测的程序很贱,不断检测Pin是否按下,首先通过 R_IOPORT_Open 函数初始化配置GPIO引脚,之后使用 R_IOPORT_PinRead 函数来获取当前引脚的电平的状态。

完整代码请参附件,这里只贴出核心代码。

  • GPIO 初始化配置

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;
}

相信延时消抖的原理大家在学习其他单片机时就已经了解了,本函数的功能就是扫描输入参数中指定的引脚,检测其电平变化,并作延时消抖处理,最终对按键消息进行确认。

  • 利用R_IOPORT_PinRead() 读取输入数据,若从相应引脚读取的数据等于 0(KEY_ON),低电平,表明可能有按键按下,调用延时函数。否则返回 KEY_OFF,表示按键没有被按下。
  • 延时之后再次利用R_IOPORT_PinRead()读取输入数据,若依然为低电平,表明确实有按键被按下了。否则返回 KEY_OFF,表示按键没有被按下。
  • 循环调用R_IOPORT_PinRead()一直检测按键的电平,直至按键被释放,被释放后,返回表示按键被按下的标志 KEY_ON。以上是按键消抖的流程,调用了一个库函数 R_IOPORT_PinRead ()。输入参数为要读取的端口、引脚,返回引脚的输入电平状态,高电平为 1,低电平为 0。

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

这里两个按键都用上,一个用于关灯,一个用于开灯。

2 EIRQ方式

2.1 EIRQ的工作原理

IRQ(Interrupt ReQuest)就是指中断请求,通过 GPIO 检测输入脉冲,引起中断事件,打断原来的代码执行流程,进入到中断服务函数中进行处理,处理完后再返回到中断之前的代码中执行。

  • RA4M2的中断和异常

Cortex-M内核具有强大的异常响应系统,它把能够打断当前代码执行流程的事件分为异常(exception)和中断(interrupt),并把它们用一个表管理起来,编号为 0 ~ 15 的称为内核异常,而 16 以上的则称为外部中断(外是相对内核而言),这个表就称为中断向量表。

而RA4M2对这个表重新进行了编排,把编号从 –3 至 6 的中断向量定义为系统异常,编号为负的内核异常不能被设置优先级,如复位(Reset)、不可屏蔽中断 (NMI)、硬错误(Hardfault)。从编号16开始的为外部中断,这些中断的优先级都是可以自行设置的。详细的 RA4M2中断向量表见下表。

16772495059504ye2pghw3e

  • NVIC 中断控制器

RA4M2的中断如此之多,配置起来并不容易,因此我们需要一个强大而方便的中断控制器 NVIC (Nested Vectored Interrupt Controller)。NVIC 是属于 Cortex 内核的器件,不可屏蔽中断 (NMI)和外部中断都由它来处理,而SysTick不是由 NVIC 来控制的。

1677249506332bw2j560a3j

可以在固件库core_cm33.h看到关于NVIC的结构与相关的函数。

1677249506685vgsfoe99dn

以上就是NVIC的主要函数。

的所有 I/O 端口都可以配置为 EXTI 中断模式,用来捕捉外部信号,可以配置为下降沿中断、上升沿中断和上升下降沿中断这三种模式。

  • EIRQ外部中断

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最普通的应用就是接上一个按键,设置为下降沿触发,用中断来检测按键。

1677249507108mrgar9t14u

GPIO的中断事件表如下图所示。

1677249507467j8nfe3sxnj

2.2 EIRQ方式实现

2.2.1 FSP配置

打开Renesas RA Smart Configurator软件。将按键的的Pin为配置为中断模式。

1677249507782aj95w7bbsk

然后配置外部中断模块。

1677249508177tnjgsde599

配置参数如下所示:

1677249508518qneuadbdxd

配置完成之后点右上角的“Generate Project Content” 按钮,让软件自动生成配置代码即可

2.2.2 按键中断方式实现

要想实现外部引脚中断,主要步骤如下:

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
}


代码很简单,就不赘述了。

3 实验现象

编译好程序后,下载到板子上,不管是普通方式还是中断方式,当按在按键SW1时,LED亮,当按在按键SW2时,LED灭。

更多回帖

发帖
×
20
完善资料,
赚取积分