[FreeRTOS 功能应用] 互斥访问与回环队列 功能应用


一、基础知识点

[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中定义它。

多个任务使用一个任务函数,在任务函数中还可以使用下面几个方法实现

  1. 使用任务优先级:如果每个任务的优先级不同,可以在任务函数中通过 uxTaskPriorityGet(NULL) 获取当前任务的优先级,然后根据优先级执行不同的逻辑。
  2. 使用全局变量:可以定义一个全局变量数组,每个元素对应一个任务的状态或标识。在任务函数中,根据任务的某种标识访问对应的全局变量。
  3. 使用静态变量:在任务函数内部定义静态变量,每个任务实例都会有自己的静态变量副本,可以用来存储任务特定的信息。
  4. 使用事件组:如果任务需要根据事件来执行不同的逻辑,可以使用事件组( EventGroupHandle_t )来区分不同的事件,并在任务函数中根据事件执行相应的操作。
  5. 使用任务通知:通过发送和接收任务通知( 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 ] 互斥访问与回环队列 功能应用

  • 33
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bazinga bingo

您的鼓励就是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值