美文网首页
FreeRTOS学习笔记(12)——按键中断

FreeRTOS学习笔记(12)——按键中断

作者: Leung_ManWah | 来源:发表于2021-01-08 11:19 被阅读0次

    一、简介

    在 FreeRTOS 下实现按键中断可以有两种方法:

    • 通过事件的触发和等待:可以实现一对多多对多的同步。即一个任务可以等待多个事件的发生;可以是任意一个事件发生时唤醒任务进行事件处理;也可以是几个事件都发生后才唤醒任务进行事件处理。同样,也可以是多个任务同步多个事件。
    • 通过任务通知: FreeRTOS v8.2.0 以上版本。优点是解除阻塞的任务要快 45%,并且节省 RAM 内存空间。缺点是只能够一对一

    二、通过事件

    2.1 要点

    1. 创建 EXTI 外部中断,配置 NVIC 中断优先级分组与 FreeRTOS 相同,即 4
    2. 触发 EXTI 外部中断,在中断服务函数中使用事件组置位函数 xEventGroupSetBitsFromISR()
    3. 使用等待事件函数 xEventGroupWaitBits(),任务阻塞直到等待的事件发生。

    2.2 实验

    2.2.1 board_gpi.c

    /*********************************************************************
     * INCLUDES
     */
    #include "board_gpi.h"
    
    static void nvicConfig(void);
    
    /*********************************************************************
     * PUBLIC FUNCTIONS
     */
    /**
     @brief 按键驱动初始化
     @param 无
     @return 无
    */
    void Board_KeyInit(void)
    {
        GPIO_InitTypeDef GPIO_InitStructure;
        EXTI_InitTypeDef EXTI_InitStructure;
        
        RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK, ENABLE);                  // 开启按键端口的时钟
    //  RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK | KEY2_GPIO_CLK, ENABLE);  // 开启按键端口的时钟
        
        nvicConfig();                                                   //  配置NVIC中断
        
        /*--------------------- KEY1 配置 ---------------------*/
        GPIO_InitStructure.GPIO_Pin = KEY1_GPIO_PIN;                    // 选择按键的引脚 
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;           // 设置按键的引脚为浮空输入 
        GPIO_Init(KEY1_GPIO_PORT, &GPIO_InitStructure);                 // 使用结构体初始化按键
        
        GPIO_EXTILineConfig(KEY1_INT_EXTI_PORTSOURCE, \
                            KEY1_INT_EXTI_PINSOURCE);
        EXTI_InitStructure.EXTI_Line = KEY1_INT_EXTI_LINE;              // 选择EXTI的信号源
        EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;             // EXTI为中断模式
        EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;          // 上升沿中断
        EXTI_InitStructure.EXTI_LineCmd = ENABLE;                       // 使能中断
        EXTI_Init(&EXTI_InitStructure);
        
        /*--------------------- KEY2 配置 ---------------------*/
    //    GPIO_InitStructure.GPIO_Pin = KEY2_GPIO_PIN;                    // 选择按键的引脚  
    //    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;           // 设置按键的引脚为浮空输入  
    //    GPIO_Init(KEY2_GPIO_PORT, &GPIO_InitStructure);                 // 使用结构体初始化按键 
    //    
    //    GPIO_EXTILineConfig(KEY2_INT_EXTI_PORTSOURCE, \
    //                        KEY2_INT_EXTI_PINSOURCE);
    //    EXTI_InitStructure.EXTI_Line = KEY2_INT_EXTI_LINE;              // 选择EXTI的信号源
    //    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;             // EXTI为中断模式
    //    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;          // 上升沿中断
    //    EXTI_InitStructure.EXTI_LineCmd = ENABLE;                       // 使能中断
    //    EXTI_Init(&EXTI_InitStructure);
    }
    
    /**
     @brief 检测是否有按键按下
     @param keyNum -[in] 按键编号
     @return KEY_OFF(没按下按键)、KEY_ON(按下按键)
    */
    uint8_t Board_KeyScan(uint8_t keyNum)
    {           
        switch(keyNum)
        {
            case KEY1:
            {
                // 检测是否有按键按下
                if(GPIO_ReadInputDataBit(KEY1_GPIO_PORT, KEY1_GPIO_PIN) == KEY_ON)  
                {    
                    // 等待按键释放
                    while(GPIO_ReadInputDataBit(KEY1_GPIO_PORT, KEY1_GPIO_PIN) == KEY_ON)
                    {                               
                        return KEY_ON;
                    }                    
                }
                else
                {
                    return KEY_OFF;
                }
            }
            break;
            
    //        case KEY2:
    //        {
    //            // 检测是否有按键按下
    //            if(GPIO_ReadInputDataBit(KEY2_GPIO_PORT, KEY2_GPIO_PIN) == KEY_ON)  
    //            {  
    //                // 等待按键释放
    //                while(GPIO_ReadInputDataBit(KEY2_GPIO_PORT, KEY2_GPIO_PIN) == KEY_ON)
    //                {                               
    //                    return KEY_ON;
    //                }                    
    //            }
    //            else
    //            {
    //                return KEY_OFF;
    //            }
    //        }
    //        break;
            
            default:
                break;
        }   
        return 0;
    }
    
    
    /*********************************************************************
     * LOCAL FUNCTIONS
     */
    /**
     @brief 配置嵌套向量中断控制器NVIC
     @param 无
     @return 无
    */
    static void nvicConfig(void)
    {
        NVIC_InitTypeDef NVIC_InitStructure;
    
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);                 // 嵌套向量中断控制器组选择
    
        NVIC_InitStructure.NVIC_IRQChannel = KEY1_INT_EXTI_IRQ;         // 配置按键1为中断源
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 7;       // 抢断优先级
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;              // 子优先级
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                 // 使能中断
        NVIC_Init(&NVIC_InitStructure);                                 // 初始化配置NVIC
        
    //    NVIC_InitStructure.NVIC_IRQChannel = KEY2_INT_EXTI_IRQ;     // 配置按键2为中断源,其他使用上面相关配置
        NVIC_Init(&NVIC_InitStructure);
    }
    
    /****************************************************END OF FILE****************************************************/
    

    2.2.2 board_gpi.h

    #ifndef _BOARD_GPI_H_
    #define _BOARD_GPI_H_
    
    /*********************************************************************
     * INCLUDES
     */
    #include "stm32f10x.h"
    
    /*********************************************************************
     * DEFINITIONS
     */
    // 按键1
    #define KEY1_GPIO_CLK               (RCC_APB2Periph_GPIOD | RCC_APB2Periph_AFIO)
    #define KEY1_GPIO_PORT              GPIOD              
    #define KEY1_GPIO_PIN               GPIO_Pin_6
    #define KEY1_INT_EXTI_PORTSOURCE    GPIO_PortSourceGPIOD
    #define KEY1_INT_EXTI_PINSOURCE     GPIO_PinSource6
    #define KEY1_INT_EXTI_LINE          EXTI_Line6
    #define KEY1_INT_EXTI_IRQ           EXTI9_5_IRQn
    #define KEY1_IRQHandler             EXTI9_5_IRQHandler
    
    // 按键2
    #define KEY2_GPIO_CLK               (RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO)
    #define KEY2_GPIO_PORT              GPIOC          
    #define KEY2_GPIO_PIN               GPIO_Pin_13
    #define KEY2_INT_EXTI_PORTSOURCE    GPIO_PortSourceGPIOC
    #define KEY2_INT_EXTI_PINSOURCE     GPIO_PinSource13
    #define KEY2_INT_EXTI_LINE          EXTI_Line13
    #define KEY2_INT_EXTI_IRQ           EXTI15_10_IRQn
    #define KEY2_IRQHandler             EXTI15_10_IRQHandler
    
    #define KEY_ON                      1
    #define KEY_OFF                     0
    
    #define KEY1                        0x01
    #define KEY2                        0x02
    
    #define KEY1_EVENT                  (0x01 << 0)             // 设置事件掩码的位0
    #define KEY2_EVENT                  (0x01 << 1)             // 设置事件掩码的位1
    
    /*********************************************************************
     * API FUNCTIONS
     */
    void  Board_KeyInit(void);
    uint8_t Board_KeyScan(uint8_t keyNum);
    
    #endif /* _BOARD_GPI_H_ */
    

    2.2.3 stm32f10x_it.c

    #include "stm32f10x_it.h"
    //FreeRTOS使用         
    #include "FreeRTOS.h"                    
    #include "task.h" 
    #include "event_groups.h"
    
    #include "board_gpi.h"
    
    /**
      * @brief  This function handles SysTick Handler.
      * @param  None
      * @retval None
      */
    extern void xPortSysTickHandler(void);
    // systick中断服务函数
    void SysTick_Handler(void)
    {   
    #if (INCLUDE_xTaskGetSchedulerState == 1)
        if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
        {
    #endif  /* INCLUDE_xTaskGetSchedulerState */  
            xPortSysTickHandler();
    #if (INCLUDE_xTaskGetSchedulerState == 1)
        }
    #endif  /* INCLUDE_xTaskGetSchedulerState */
    }
    
    extern EventGroupHandle_t g_eventHandle;
    
    void KEY1_IRQHandler(void)
    {
        BaseType_t xHigherPriorityTaskWoken;
        uint32_t ulReturn;
        /* 进入临界段,临界段可以嵌套 */
        ulReturn = taskENTER_CRITICAL_FROM_ISR();
        
        if(EXTI_GetITStatus(KEY1_INT_EXTI_LINE) != RESET) 
        {
            xEventGroupSetBitsFromISR(g_eventHandle, KEY1_EVENT, &xHigherPriorityTaskWoken);
            
            // 如果需要的话进行一次任务切换
            portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
            
            // 清除中断标志位
            EXTI_ClearITPendingBit(KEY1_INT_EXTI_LINE);
        }
        
        /* 退出临界段 */
        taskEXIT_CRITICAL_FROM_ISR(ulReturn);
    }
    
    void KEY2_IRQHandler(void)
    {
        BaseType_t xHigherPriorityTaskWoken;
        uint32_t ulReturn;
        /* 进入临界段,临界段可以嵌套 */
        ulReturn = taskENTER_CRITICAL_FROM_ISR();
        
        if(EXTI_GetITStatus(KEY2_INT_EXTI_LINE) != RESET) 
        {
            xEventGroupSetBitsFromISR(g_eventHandle, KEY2_EVENT, &xHigherPriorityTaskWoken);
            
            // 如果需要的话进行一次任务切换
            portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
            
            // 清除中断标志位
            EXTI_ClearITPendingBit(KEY2_INT_EXTI_LINE);
        }
        
        /* 退出临界段 */
        taskEXIT_CRITICAL_FROM_ISR(ulReturn);
    }
    

    2.2.4 main.c

    /*********************************************************************
     * INCLUDES
     */
    // FreeRTOS
    #include "FreeRTOS.h"
    #include "task.h"
    #include "event_groups.h"
    
    /****************** 内核对象句柄 ******************/
    /*
     * 信号量,消息队列,事件标志组,软件定时器这些都属于内核的对象,要想使用这些内核
     * 对象,必须先创建,创建成功之后会返回一个相应的句柄。实际上就是一个指针,后续我
     * 们就可以通过这个句柄操作这些内核对象。
     *
     * 内核对象说白了就是一种全局的数据结构,通过这些数据结构我们可以实现任务间的通信,
     * 任务间的事件同步等各种功能。至于这些功能的实现我们是通过调用这些内核对象的函数
     * 来完成的
     * 
     */
    EventGroupHandle_t g_eventHandle = NULL;
    
    /**
     @brief 按键检测任务主体
     @param 无
     @return 无
    */
    static void keyTask(void *pvParameters)
    {
        EventBits_t rEvent;                                                 // 定义一个事件接收变量
        
        while(1)                                                            // 任务都是一个无限循环,不能返回
        {   
            rEvent = xEventGroupWaitBits(g_eventHandle,                     // 事件对象句柄
                                            KEY1_EVENT,                     // 接收任务感兴趣的事件
                                            pdTRUE,                         // 退出时清除事件位
                                            pdTRUE,                         // 满足感兴趣的所有事件
                                            portMAX_DELAY);                 // 指定超时事件,一直等
            
            if((rEvent & KEY1_EVENT) == KEY1_EVENT)
            {
                 vTaskDelay(50);                                             // 消抖
    
                if(Board_KeyScan(KEY1) == KEY_ON)
                {
                    // 应用程序             
                }
            }
            else
            {
                printf("Event error!\n"); 
            }
        }
    }
    

    三、通过任务通知

    3.1 要点

    1. 创建 EXTI 外部中断,配置 NVIC 中断优先级分组与 FreeRTOS 相同,即 4
    2. 触发 EXTI 外部中断,在中断服务函数中使用任务通知发送函数 xTaskNotifyFromISR()
    3. 使用等待通知函数 xTaskNotifyWait(),任务阻塞直到等待的通知到达。

    注意:要在 FreeRTOS v8.2.0 以上版本

    3.2 实验

    3.2.1 board_gpi.c

    /*********************************************************************
     * INCLUDES
     */
    #include "board_gpi.h"
    
    static void nvicConfig(void);
    
    /*********************************************************************
     * PUBLIC FUNCTIONS
     */
    /**
     @brief 按键驱动初始化
     @param 无
     @return 无
    */
    void Board_KeyInit(void)
    {
        GPIO_InitTypeDef GPIO_InitStructure;
        EXTI_InitTypeDef EXTI_InitStructure;
        
        RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK, ENABLE);                  // 开启按键端口的时钟
    //  RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK | KEY2_GPIO_CLK, ENABLE);  // 开启按键端口的时钟
        
        nvicConfig();                                                   //  配置NVIC中断
        
        /*--------------------- KEY1 配置 ---------------------*/
        GPIO_InitStructure.GPIO_Pin = KEY1_GPIO_PIN;                    // 选择按键的引脚 
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;           // 设置按键的引脚为浮空输入 
        GPIO_Init(KEY1_GPIO_PORT, &GPIO_InitStructure);                 // 使用结构体初始化按键
        
        GPIO_EXTILineConfig(KEY1_INT_EXTI_PORTSOURCE, \
                            KEY1_INT_EXTI_PINSOURCE);
        EXTI_InitStructure.EXTI_Line = KEY1_INT_EXTI_LINE;              // 选择EXTI的信号源
        EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;             // EXTI为中断模式
        EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;          // 上升沿中断
        EXTI_InitStructure.EXTI_LineCmd = ENABLE;                       // 使能中断
        EXTI_Init(&EXTI_InitStructure);
        
        /*--------------------- KEY2 配置 ---------------------*/
    //    GPIO_InitStructure.GPIO_Pin = KEY2_GPIO_PIN;                    // 选择按键的引脚  
    //    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;           // 设置按键的引脚为浮空输入  
    //    GPIO_Init(KEY2_GPIO_PORT, &GPIO_InitStructure);                 // 使用结构体初始化按键 
    //    
    //    GPIO_EXTILineConfig(KEY2_INT_EXTI_PORTSOURCE, \
    //                        KEY2_INT_EXTI_PINSOURCE);
    //    EXTI_InitStructure.EXTI_Line = KEY2_INT_EXTI_LINE;              // 选择EXTI的信号源
    //    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;             // EXTI为中断模式
    //    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;          // 上升沿中断
    //    EXTI_InitStructure.EXTI_LineCmd = ENABLE;                       // 使能中断
    //    EXTI_Init(&EXTI_InitStructure);
    }
    
    /**
     @brief 检测是否有按键按下
     @param keyNum -[in] 按键编号
     @return KEY_OFF(没按下按键)、KEY_ON(按下按键)
    */
    uint8_t Board_KeyScan(uint8_t keyNum)
    {           
        switch(keyNum)
        {
            case KEY1:
            {
                // 检测是否有按键按下
                if(GPIO_ReadInputDataBit(KEY1_GPIO_PORT, KEY1_GPIO_PIN) == KEY_ON)  
                {    
                    // 等待按键释放
                    while(GPIO_ReadInputDataBit(KEY1_GPIO_PORT, KEY1_GPIO_PIN) == KEY_ON)
                    {                               
                        return KEY_ON;
                    }                    
                }
                else
                {
                    return KEY_OFF;
                }
            }
            break;
            
    //        case KEY2:
    //        {
    //            // 检测是否有按键按下
    //            if(GPIO_ReadInputDataBit(KEY2_GPIO_PORT, KEY2_GPIO_PIN) == KEY_ON)  
    //            {  
    //                // 等待按键释放
    //                while(GPIO_ReadInputDataBit(KEY2_GPIO_PORT, KEY2_GPIO_PIN) == KEY_ON)
    //                {                               
    //                    return KEY_ON;
    //                }                    
    //            }
    //            else
    //            {
    //                return KEY_OFF;
    //            }
    //        }
    //        break;
            
            default:
                break;
        }   
        return 0;
    }
    
    
    /*********************************************************************
     * LOCAL FUNCTIONS
     */
    /**
     @brief 配置嵌套向量中断控制器NVIC
     @param 无
     @return 无
    */
    static void nvicConfig(void)
    {
        NVIC_InitTypeDef NVIC_InitStructure;
    
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);                 // 嵌套向量中断控制器组选择
    
        NVIC_InitStructure.NVIC_IRQChannel = KEY1_INT_EXTI_IRQ;         // 配置按键1为中断源
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 7;       // 抢断优先级
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;              // 子优先级
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                 // 使能中断
        NVIC_Init(&NVIC_InitStructure);                                 // 初始化配置NVIC
        
    //    NVIC_InitStructure.NVIC_IRQChannel = KEY2_INT_EXTI_IRQ;     // 配置按键2为中断源,其他使用上面相关配置
        NVIC_Init(&NVIC_InitStructure);
    }
    
    /****************************************************END OF FILE****************************************************/
    

    3.2.2 board_gpi.h

    #ifndef _BOARD_GPI_H_
    #define _BOARD_GPI_H_
    
    /*********************************************************************
     * INCLUDES
     */
    #include "stm32f10x.h"
    
    /*********************************************************************
     * DEFINITIONS
     */
    // 按键1
    #define KEY1_GPIO_CLK               (RCC_APB2Periph_GPIOD | RCC_APB2Periph_AFIO)
    #define KEY1_GPIO_PORT              GPIOD              
    #define KEY1_GPIO_PIN               GPIO_Pin_6
    #define KEY1_INT_EXTI_PORTSOURCE    GPIO_PortSourceGPIOD
    #define KEY1_INT_EXTI_PINSOURCE     GPIO_PinSource6
    #define KEY1_INT_EXTI_LINE          EXTI_Line6
    #define KEY1_INT_EXTI_IRQ           EXTI9_5_IRQn
    #define KEY1_IRQHandler             EXTI9_5_IRQHandler
    
    // 按键2
    #define KEY2_GPIO_CLK               (RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO)
    #define KEY2_GPIO_PORT              GPIOC          
    #define KEY2_GPIO_PIN               GPIO_Pin_13
    #define KEY2_INT_EXTI_PORTSOURCE    GPIO_PortSourceGPIOC
    #define KEY2_INT_EXTI_PINSOURCE     GPIO_PinSource13
    #define KEY2_INT_EXTI_LINE          EXTI_Line13
    #define KEY2_INT_EXTI_IRQ           EXTI15_10_IRQn
    #define KEY2_IRQHandler             EXTI15_10_IRQHandler
    
    #define KEY_ON                      1
    #define KEY_OFF                     0
    
    #define KEY1                        0x01
    #define KEY2                        0x02
    
    #define KEY1_EVENT                  (0x01 << 0)             // 设置事件掩码的位0
    #define KEY2_EVENT                  (0x01 << 1)             // 设置事件掩码的位1
    
    /*********************************************************************
     * API FUNCTIONS
     */
    void  Board_KeyInit(void);
    uint8_t Board_KeyScan(uint8_t keyNum);
    
    #endif /* _BOARD_GPI_H_ */
    

    3.2.3 stm32f10x_it.c

    #include "stm32f10x_it.h"
    //FreeRTOS使用         
    #include "FreeRTOS.h"                    
    #include "task.h" 
    #include "event_groups.h"
    
    #include "board_gpi.h"
    
    /**
      * @brief  This function handles SysTick Handler.
      * @param  None
      * @retval None
      */
    extern void xPortSysTickHandler(void);
    // systick中断服务函数
    void SysTick_Handler(void)
    {   
    #if (INCLUDE_xTaskGetSchedulerState == 1)
        if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
        {
    #endif  /* INCLUDE_xTaskGetSchedulerState */  
            xPortSysTickHandler();
    #if (INCLUDE_xTaskGetSchedulerState == 1)
        }
    #endif  /* INCLUDE_xTaskGetSchedulerState */
    }
    
    extern EventGroupHandle_t g_eventHandle;
    
    void KEY1_IRQHandler(void)
    {
        BaseType_t xHigherPriorityTaskWoken;
        uint32_t ulReturn;
        /* 进入临界段,临界段可以嵌套 */
        ulReturn = taskENTER_CRITICAL_FROM_ISR();
        
        if(EXTI_GetITStatus(KEY1_INT_EXTI_LINE) != RESET) 
        {
            xTaskNotifyFromISR(g_keyTaskHandle, KEY1_EVENT, eSetBits, &xHigherPriorityTaskWoken);
            
            // 如果需要的话进行一次任务切换
            portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
            
            // 清除中断标志位
            EXTI_ClearITPendingBit(KEY1_INT_EXTI_LINE);
        }
        
        /* 退出临界段 */
        taskEXIT_CRITICAL_FROM_ISR(ulReturn);
    }
    

    3.3.4 main.c

    注意包含 limits.h

    /*********************************************************************
     * INCLUDES
     */
    // FreeRTOS
    #include "FreeRTOS.h"
    #include "task.h"
    #include "event_groups.h"
    #include "limits.h"
    
    TaskHandle_t g_keyTaskHandle = NULL;                             // 按键检测任务句柄
    
    /**
     @brief 按键检测任务主体
     @param 无
     @return 无
    */
    static void keyTask(void *pvParameters)
    {
        uint32_t ulInterruptStatus;
    
        while(1)                                                            // 任务都是一个无限循环,不能返回
        {   
            // 等待任务通知,无限期阻塞(没有超时,所以没有必要检查函数返回值)
            xTaskNotifyWait(0x00,                     // 在进入的时候不清除通知值的任何位
                            ULONG_MAX,                // 在退出的时候复位通知值为0
                            &ulInterruptStatus,       // 任务通知值传递到变量
                            portMAX_DELAY);           // 指定超时事件,一直等
            
            if((ulInterruptStatus & KEY1_EVENT) == KEY1_EVENT)
            {
                 vTaskDelay(50);                                             // 消抖
    
                if(Board_KeyScan(KEY1) == KEY_ON)
                {
                    // 应用程序             
                }
            }
            else
            {
                printf("Event error!\n"); 
            }
        }
    }
    

    • 由 Leung 写于 2021 年 1 月 8 日

    相关文章

      网友评论

          本文标题:FreeRTOS学习笔记(12)——按键中断

          本文链接:https://www.haomeiwen.com/subject/qdxooktx.html