文章目录
- 一、基础知识点
- 二、代码讲解
- 三、结果演示
- 四、代码下载
一、基础知识点
[FreeRTOS 基础知识] 互斥访问与回环队列 概念
[FreeRTOS 内部实现] 互斥访问与回环队列
[FreeRTOS 内部实现] 创建任务 xTaskCreate函数解析
本实验是基于STM32F103开发移植FreeRTOS实时操作系统,实现多任务同时读写队列数据操作。
使用工具:Keil、串口工具
二、代码讲解
1、使用xQueueCreate函数 创建队列。
// 路径:项目\Core\Src\freertos.c
// 全局变量
QueueHandle_t g_xQueue;
/* 创建队列,长度5,数据大小4个字节 */
g_xQueue = xQueueCreate(5, sizeof(int32_t));
2、使用osThreadCreate创建三个任务
两个任务Sender1、Sender2负责将数据写入队列,一个任务Seceiver从队列中取出数据。
// 路径:项目\Core\Src\freertos.c
// 全局变量
osThreadId Sender1_Handle;
osThreadId Sender2_Handle;
osThreadId Seceiver_Handle;
if ( g_xQueue != NULL )
{
// 创建两个任务,传入参数100,200
osThreadDef(Sender1, vSendTask, osPriorityNormal, 0, 1000);
Sender1_Handle = osThreadCreate(osThread(Sender1), (void *)100);
if( Sender1_Handle != NULL )
{
printf("Succeeded in creating Sender1_Handle Queue. Procedure!\n\r");
}
else
{
printf("Fail in creating Sender1_Handle Queue. Procedure!\n\r");
}
osThreadDef(Sender2, vSendTask, osPriorityNormal, 0, 100);
Sender2_Handle = osThreadCreate(osThread(Sender2), (uint32_t *)200);
if( Sender2_Handle != NULL )
{
printf("Succeeded in creating Sender2_Handle Queue. Procedure!\n\r");
}
else
{
printf("Fail in creating Sender2_Handle Queue. Procedure!\n\r");
}
osThreadDef(Seceiver, vSeceiverTask, osPriorityHigh, 0, 1000);
Seceiver_Handle = osThreadCreate(osThread(Seceiver), NULL);
if( Seceiver_Handle != NULL )
{
printf("Succeeded in creating Seceiver_Handle Queue. Procedure!\n\r");
}
else
{
printf("Fail in creating Seceiver_Handle Queue. Procedure!\n\r");
}
}
函数中通过osThreadDef 宏构建osThreadDef_t 结构体,名称os_thread_def_##name(## 表示字符拼接),结构体成员包括 :#name 任务名称;thread 任务处理函数;priority 任务优先级;instances 实例; stacksz 栈大小;
#define osThreadDef(name, thread, priority, instances, stacksz) \
const osThreadDef_t os_thread_def_##name = \
{ #name, (thread), (priority), (instances), (stacksz), NULL, NULL }
将构建的osThreadDef_t 结构体传入osThreadCreate函数中,实际调用xTaskCreate函数创建任务。
osThreadId osThreadCreate (const osThreadDef_t *thread_def, void *argument)
{
TaskHandle_t handle;
if (xTaskCreate((TaskFunction_t)thread_def->pthread,(const portCHAR *)thread_def->name,
thread_def->stacksize, argument, makeFreeRtosPriority(thread_def->tpriority),
&handle) != pdPASS) {
return NULL;
}
return handle;
}
注:Sender1、Sender2任务函数共用一个。
3、Sender1、Sender2 任务函数vSendTask 实现
Sender1、Sender2负责将数据写入队列。由于两个任务使用一个任务处理函数,因此在处理函数内部要先区分当前运行的是哪任务,这里使用任务句柄:每个任务在创建时都会返回一个任务句柄( TaskHandle_t ),这个句柄可以用来唯一标识一个任务。可以在任务函数中使用 xTaskGetCurrentTaskHandle() 函数获取当前任务的句柄,然后与已知的任务句柄进行比较。
pcTaskGetTaskName() 函数来获取当前任务的名称。这个名称是在任务创建时指定的,因此可以用来区分不同的任务。
void vSendTask(void const * argument)
{
/* USER CODE BEGIN StartDefaultTask */
BaseType_t xReturn = pdPASS; /* 定义一个创建信息返回值,默认为pdPASS */
int32_t lValueToSend;
/* Infinite loop */
for(;;)
{
/* 根据任务句柄执行不同的任务逻辑,也可以使用参数传入的方式区分任务 */
TaskHandle_t xTaskHandle = xTaskGetCurrentTaskHandle();
/* 获取当前任务名称 */
const char *pcTaskName = pcTaskGetName(xTaskHandle);
lValueToSend = ( int32_t ) argument;
xReturn = xQueueSend( g_xQueue, &lValueToSend, 0 );
if (pdPASS == xReturn)
printf("DWB Sent SUCCESS ---> %s Message %d sent successfully! \n\r",pcTaskName ,(uint32_t)argument);
else
printf("DWB Sent FAIL ---> %s Message %d sent Fail! \n\r",pcTaskName, (uint32_t)argument);
vTaskDelay(pdMS_TO_TICKS(600)); // 延时600ms
}
}
在这个示例中, 使用 xTaskGetCurrentTaskHandle() 来获取当前任务的句柄,然后调用 pcTaskGetTaskName() 来获取任务名称,并将其打印出来。这样,每次任务执行时,都会打印出当前是哪个任务在运行。
注, pcTaskGetTaskName() 函数需要FreeRTOS的配置宏 configUSE_TRACE_FACILITY 被定义为1,以便启用任务跟踪和任务名称的功能。如果你的FreeRTOS配置没有启用这个宏,你需要先在FreeRTOSConfig.h中定义它。
多个任务使用一个任务函数,在任务函数中还可以使用下面几个方法实现
- 使用任务优先级:如果每个任务的优先级不同,可以在任务函数中通过 uxTaskPriorityGet(NULL) 获取当前任务的优先级,然后根据优先级执行不同的逻辑。
- 使用全局变量:可以定义一个全局变量数组,每个元素对应一个任务的状态或标识。在任务函数中,根据任务的某种标识访问对应的全局变量。
- 使用静态变量:在任务函数内部定义静态变量,每个任务实例都会有自己的静态变量副本,可以用来存储任务特定的信息。
- 使用事件组:如果任务需要根据事件来执行不同的逻辑,可以使用事件组( EventGroupHandle_t )来区分不同的事件,并在任务函数中根据事件执行相应的操作。
- 使用任务通知:通过发送和接收任务通知( vTaskNotifyGive() 和 ulTaskNotifyTake() ),任务可以在接收到特定通知时执行不同的逻辑。
4、Seceiver任务函数 vSeceiverTask实现
任务Seceiver从队列中取出数据。
void vSeceiverTask(void const * argument)
{
BaseType_t xReturn = pdTRUE; /* 定义一个创建信息返回值,默认为pdTRUE */
uint32_t r_queue; /* 定义一个接收消息的变量 */
const TickType_t xTicksToWait = pdMS_TO_TICKS( 100UL );
for(;;)
{
xReturn = xQueueReceive( g_xQueue, &r_queue, xTicksToWait); // 获取队列值
if (pdTRUE == xReturn)
printf("DWB Receive SUCCESS ---> The data received is %d. \n",(uint32_t)r_queue);
else
printf("DWB Receive FAIL ---> Data reception error, error code :%ld. \n\r",xReturn);
vTaskDelay(pdMS_TO_TICKS(500)); // 延时500ms
}
}
三、结果演示
通过串口工具查看,任务读写队列情况
四、代码下载
[FreeRTOS ] 互斥访问与回环队列 功能应用