中断管理实验是在 FreeRTOS 中创建了两个任务分别获取信号量与消息队列,并且定义了两个按键 KEY1 与 KEY2 的触发方式为中断触发,其触发的中断服务函数则跟裸机一样,在中断触发的时候通过消息队列将消息传递给任务, 任务接收到消息就将信息通过串口调试助手显示出来。而且中断管理实验也实现了一个串口的 DMA 传输+空闲中断功能,当串口接收完不定长的数据之后产生一个空闲中断,在中断中将信号量传递给任务, 任务在收到信号量的时候将串口的数据读取出来并且在串口调试助手中回显。
/*
*************************************************************************
* 包含的头文件
*************************************************************************
*/
/* FreeRTOS头文件 */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
/* 开发板硬件bsp头文件 */
#include "bsp_led.h"
#include "bsp_usart.h"
#include "bsp_key.h"
#include "bsp_exti.h"
/* 标准库头文件 */
#include <string.h>
/**************************** 任务句柄 ********************************/
/*
* 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄
* 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么
* 这个句柄可以为NULL。
*/
static TaskHandle_t AppTaskCreate_Handle = NULL;/* 创建任务句柄 */
static TaskHandle_t LED_Task_Handle = NULL;/* LED任务句柄 */
static TaskHandle_t KEY_Task_Handle = NULL;/* KEY任务句柄 */
/********************************** 内核对象句柄 *********************************/
/*
* 信号量,消息队列,事件标志组,软件定时器这些都属于内核的对象,要想使用这些内核
* 对象,必须先创建,创建成功之后会返回一个相应的句柄。实际上就是一个指针,后续我
* 们就可以通过这个句柄操作这些内核对象。
*
* 内核对象说白了就是一种全局的数据结构,通过这些数据结构我们可以实现任务间的通信,
* 任务间的事件同步等各种功能。至于这些功能的实现我们是通过调用这些内核对象的函数
* 来完成的
*
*/
QueueHandle_t Test_Queue = NULL;
SemaphoreHandle_t BinarySem_Handle = NULL;
/******************************* 全局变量声明 ************************************/
/*
* 当我们在写应用程序的时候,可能需要用到一些全局变量。
*/
extern char Usart_Rx_Buf[USART_RBUFF_SIZE];
/******************************* 宏定义 ************************************/
/*
* 当我们在写应用程序的时候,可能需要用到一些宏定义。
*/
#define QUEUE_LEN 4 /* 队列的长度,最大可包含多少个消息 */
#define QUEUE_SIZE 4 /* 队列中每个消息大小(字节) */
/*
*************************************************************************
* 函数声明
*************************************************************************
*/
static void AppTaskCreate(void);/* 用于创建任务 */
static void LED_Task(void *pvParameters);/* LED_Task任务实现 */
static void KEY_Task(void *pvParameters);/* KEY_Task任务实现 */
static void BSP_Init(void);/* 用于初始化板载相关资源 */
/*****************************************************************
* @brief 主函数
* @param 无
* @retval 无
* @note 第一步:开发板硬件初始化
第二步:创建APP应用任务
第三步:启动FreeRTOS,开始多任务调度
****************************************************************/
int main(void)
{
BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
/* 开发板硬件初始化 */
BSP_Init();
printf("这是一个[野火]-STM32全系列开发板-FreeRTOS中断管理实验!\n");
printf("按下KEY1 | KEY2触发中断!\n");
printf("串口发送数据触发中断,任务处理数据!\n");
/* 创建AppTaskCreate任务 */
xReturn = xTaskCreate((TaskFunction_t)AppTaskCreate, /* 任务入口函数 */
(const char *)"AppTaskCreate", /* 任务名字 */
(uint16_t)512, /* 任务栈大小 */
(void *)NULL, /* 任务入口函数参数 */
(UBaseType_t)1, /* 任务的优先级 */
(TaskHandle_t *)&AppTaskCreate_Handle); /* 任务控制块指针 */
/* 启动任务调度 */
if (pdPASS == xReturn)
vTaskStartScheduler(); /* 启动任务,开启调度 */
else
return -1;
//
while (1); /* 正常不会执行到这里 */
}
/***********************************************************************
* @ 函数名 : AppTaskCreate
* @ 功能说明: 为了方便管理,所有的任务创建函数都放在这个函数里面
* @ 参数 : 无
* @ 返回值 : 无
**********************************************************************/
static void AppTaskCreate(void)
{
BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
taskENTER_CRITICAL(); //进入临界区
/* 创建Test_Queue */
Test_Queue = xQueueCreate((UBaseType_t) QUEUE_LEN, /* 消息队列的长度 */
(UBaseType_t) QUEUE_SIZE); /* 消息的大小 */
if (NULL != Test_Queue)
printf("Test_Queue消息队列创建成功!\n");
/* 创建 BinarySem */
BinarySem_Handle = xSemaphoreCreateBinary();
if (NULL != BinarySem_Handle)
printf("BinarySem_Handle二值信号量创建成功!\n");
/* 创建LED_Task任务 */
xReturn = xTaskCreate((TaskFunction_t)LED_Task, /* 任务入口函数 */
(const char *)"LED_Task", /* 任务名字 */
(uint16_t)512, /* 任务栈大小 */
(void *)NULL, /* 任务入口函数参数 */
(UBaseType_t)2, /* 任务的优先级 */
(TaskHandle_t *)&LED_Task_Handle); /* 任务控制块指针 */
if (pdPASS == xReturn)
printf("创建LED_Task任务成功!\n");
/* 创建KEY_Task任务 */
xReturn = xTaskCreate((TaskFunction_t)KEY_Task, /* 任务入口函数 */
(const char *)"KEY_Task", /* 任务名字 */
(uint16_t)512, /* 任务栈大小 */
(void *)NULL, /* 任务入口函数参数 */
(UBaseType_t)3, /* 任务的优先级 */
(TaskHandle_t *)&KEY_Task_Handle); /* 任务控制块指针 */
if (pdPASS == xReturn)
printf("创建KEY_Task任务成功!\n");
vTaskDelete(AppTaskCreate_Handle); //删除AppTaskCreate任务
taskEXIT_CRITICAL(); //退出临界区
}
/**********************************************************************
* @ 函数名 : LED_Task
* @ 功能说明: LED_Task任务主体
* @ 参数 :
* @ 返回值 : 无
********************************************************************/
static void LED_Task(void *parameter)
{
BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
uint32_t r_queue; /* 定义一个接收消息的变量 */
while (1)
{
/* 队列读取(接收),等待时间为一直等待 */
xReturn = xQueueReceive(Test_Queue, /* 消息队列的句柄 */
&r_queue, /* 发送的消息内容 */
portMAX_DELAY); /* 等待时间 一直等 */
if (pdPASS == xReturn)
{
printf("触发中断的是 KEY%d !\n", r_queue);
}
else
{
printf("数据接收出错\n");
}
LED1_TOGGLE;
}
}
/**********************************************************************
* @ 函数名 : LED_Task
* @ 功能说明: LED_Task任务主体
* @ 参数 :
* @ 返回值 : 无
********************************************************************/
static void KEY_Task(void *parameter)
{
BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
while (1)
{
//获取二值信号量 xSemaphore,没获取到则一直等待
xReturn = xSemaphoreTake(BinarySem_Handle,/* 二值信号量句柄 */
portMAX_DELAY); /* 等待时间 */
if (pdPASS == xReturn)
{
printf("收到数据:%s\n", Usart_Rx_Buf);
memset(Usart_Rx_Buf, 0, USART_RBUFF_SIZE); /* 清零 */
LED2_TOGGLE;
}
}
}
/***********************************************************************
* @ 函数名 : BSP_Init
* @ 功能说明: 板级外设初始化,所有板子上的初始化均可放在这个函数里面
* @ 参数 :
* @ 返回值 : 无
*********************************************************************/
static void BSP_Init(void)
{
/*
* STM32中断优先级分组为4,即4bit都用来表示抢占优先级,范围为:0~15
* 优先级分组只需要分组一次即可,以后如果有其他的任务需要用到中断,
* 都统一用这个优先级分组,千万不要再分组,切忌。
*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
/* LED 初始化 */
LED_GPIO_Config();
/* DMA初始化 */
USARTx_DMA_Config();
/* 串口初始化 */
USART_Config();
/* 按键初始化 */
Key_GPIO_Config();
/* 按键初始化 */
EXTI_Key_Config();
}
/********************************END OF FILE****************************/
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x_it.h"
/* FreeRTOS头文件 */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
/* 开发板硬件bsp头文件 */
#include "bsp_led.h"
#include "bsp_usart.h"
#include "bsp_key.h"
#include "bsp_exti.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 QueueHandle_t Test_Queue;
extern SemaphoreHandle_t BinarySem_Handle;
static uint32_t send_data1 = 1;
static uint32_t send_data2 = 2;
/*********************************************************************************
* @ 函数名 : KEY1_IRQHandler
* @ 功能说明: 中断服务函数
* @ 参数 : 无
* @ 返回值 : 无
********************************************************************************/
void KEY1_IRQHandler(void)
{
BaseType_t pxHigherPriorityTaskWoken;
//确保是否产生了EXTI Line中断
uint32_t ulReturn;
/* 进入临界段,临界段可以嵌套 */
ulReturn = taskENTER_CRITICAL_FROM_ISR();
if (EXTI_GetITStatus(KEY1_INT_EXTI_LINE) != RESET)
{
/* 将数据写入(发送)到队列中,等待时间为 0 */
xQueueSendFromISR(Test_Queue, /* 消息队列的句柄 */
&send_data1,/* 发送的消息内容 */
&pxHigherPriorityTaskWoken);
//如果需要的话进行一次任务切换
portYIELD_FROM_ISR(pxHigherPriorityTaskWoken);
//清除中断标志位
EXTI_ClearITPendingBit(KEY1_INT_EXTI_LINE);
}
/* 退出临界段 */
taskEXIT_CRITICAL_FROM_ISR(ulReturn);
}
/*********************************************************************************
* @ 函数名 : KEY1_IRQHandler
* @ 功能说明: 中断服务函数
* @ 参数 : 无
* @ 返回值 : 无
********************************************************************************/
void KEY2_IRQHandler(void)
{
BaseType_t pxHigherPriorityTaskWoken;
uint32_t ulReturn;
/* 进入临界段,临界段可以嵌套 */
ulReturn = taskENTER_CRITICAL_FROM_ISR();
//确保是否产生了EXTI Line中断
if (EXTI_GetITStatus(KEY2_INT_EXTI_LINE) != RESET)
{
/* 将数据写入(发送)到队列中,等待时间为 0 */
xQueueSendFromISR(Test_Queue, /* 消息队列的句柄 */
&send_data2,/* 发送的消息内容 */
&pxHigherPriorityTaskWoken);
//如果需要的话进行一次任务切换
portYIELD_FROM_ISR(pxHigherPriorityTaskWoken);
//清除中断标志位
EXTI_ClearITPendingBit(KEY2_INT_EXTI_LINE);
}
/* 退出临界段 */
taskEXIT_CRITICAL_FROM_ISR(ulReturn);
}
/*********************************************************************************
* @ 函数名 : DEBUG_USART_IRQHandler
* @ 功能说明: 串口中断服务函数
* @ 参数 : 无
* @ 返回值 : 无
********************************************************************************/
void DEBUG_USART_IRQHandler(void)
{
uint32_t ulReturn;
/* 进入临界段,临界段可以嵌套 */
ulReturn = taskENTER_CRITICAL_FROM_ISR();
if (USART_GetITStatus(DEBUG_USARTx, USART_IT_IDLE) != RESET)
{
Uart_DMA_Rx_Data(); /* 释放一个信号量,表示数据已接收 */
USART_ReceiveData(DEBUG_USARTx); /* 清除标志位 */
}
/* 退出临界段 */
taskEXIT_CRITICAL_FROM_ISR(ulReturn);
}