FreeRTOS *** 作系统例程(8):消息队列

FreeRTOS *** 作系统例程(8):消息队列,第1张

安富莱电子 www.armfly.com

安富莱_STM32-V5开发板_FreeRTOS教程(V1.0)

static QueueHandle_t xQueue1 = NULL

static QueueHandle_t xQueue2 = NULL

typedef struct Msg

{

uint8_t ucMessageID

uint16_t usData[2]

uint32_t ulData[2]

}MSG_T

MSG_T g_tMsg

/*

*********************************************************************************************************

*  函 数 名: AppObjCreate

*  功能说明: 创建任务通信机制

*  形 参: 无

*  返 回 值: 无

*********************************************************************************************************

*/

static void AppObjCreate (void)

{

/* 创建 10 个 uint8_t 型消息队列 */

xQueue1 = xQueueCreate(10, sizeof(uint8_t))

if( xQueue1 == 0 )

{

/* 没有创建成功,用户可以在这里加入创建失败的处理机制 */

}

/* 创建 10 个存储指针变量的消息队列,由于 CM3/CM4 内核是 32 位机,一个指针变量占用 4 个字节 */

xQueue2 = xQueueCreate(10, sizeof(struct Msg *))

if( xQueue2 == 0 )

{

/* 没有创建成功,用户可以在这里加入创建失败的处理机制 */

}

}

//初始化定义

uint8_t ucCount = 0

/* while循环里实现向 xQueue2 发送数据 */

ucCount++

/* 向消息队列发数据,如果消息队列满了,等待 10 个时钟节拍 */

if( xQueueSend(xQueue1,

                          (void *) &ucCount,

                          (TickType_t)10) != pdPASS )

{

/* 发送失败,即使等待了 10 个时钟节拍 */

printf("向 xQueue1 发送数据失败,即使等待了 10 个时钟节拍\r\n")

}

else

{

/* 发送成功 */

printf("向 xQueue1 发送数据成功\r\n")

}

//初始化定义

MSG_T *ptMsg

/* 初始化结构体指针 */

ptMsg = &g_tMsg

/* 初始化数组 */

ptMsg->ucMessageID = 0

ptMsg->ulData[0] = 0

ptMsg->usData[0] = 0

/* while循环里实现向 xQueue2 发送数据 */

ptMsg->ucMessageID++

ptMsg->ulData[0]++

ptMsg->usData[0]++

/* 使用消息队列实现指针变量的传递 */

if(xQueueSend(xQueue2, /* 消息队列句柄 */

                         (void *) &ptMsg, /* 发送结构体指针变量 ptMsg 的地址 */

                         (TickType_t)10) != pdPASS )

{

/* 发送失败,即使等待了 10 个时钟节拍 */

printf("向 xQueue2 发送数据失败,即使等待了 10 个时钟节拍\r\n")

}

else

{

/* 发送成功 */

printf("向 xQueue2 发送数据成功\r\n")

}

static QueueHandle_t xQueue1 = NULL

/*

*********************************************************************************************************

*  函 数 名: vTaskMsgPro

*  功能说明: 使用函数 xQueueReceive 接收任务 vTaskTaskUserIF 发送的消息队列数据(xQueue1)

*  形 参: pvParameters 是在创建该任务时传递的形参

*  返 回 值: 无

* 优 先 级: 3

*********************************************************************************************************

*/

static void vTaskMsgPro(void *pvParameters)

{

BaseType_t xResult

const TickType_t xMaxBlockTime = pdMS_TO_TICKS(300)/* 设置最大等待时间为 300ms */

uint8_t ucQueueMsgValue

while(1)

{

xResult = xQueueReceive(xQueue1, /* 消息队列句柄 */

                                           (void *)&ucQueueMsgValue, /* 存储接收到的数据到变量 ucQueueMsgValue 中 */

                                           (TickType_t)xMaxBlockTime)/* 设置阻塞时间 */

if(xResult == pdPASS)

{

/* 成功接收,并通过串口将数据打印出来 */

printf("接收到消息队列数据 ucQueueMsgValue = %d\r\n", ucQueueMsgValue)

}

else

{

/* 超时 */

bsp_LedToggle(1)

bsp_LedToggle(4)

}

}

}

/*

*********************************************************************************************************

*  函 数 名: vTaskLED

*  功能说明: 使用函数 xQueueReceive 接收任务 vTaskTaskUserIF 发送的消息队列数据(xQueue2)

*  形 参: pvParameters 是在创建该任务时传递的形参

*  返 回 值: 无

* 优 先 级: 2

*********************************************************************************************************

*/

static void vTaskLED(void *pvParameters)

{

MSG_T *ptMsg

BaseType_t xResult

const TickType_t xMaxBlockTime = pdMS_TO_TICKS(200)/* 设置最大等待时间为 200ms */

while(1)

{

xResult = xQueueReceive(xQueue2, /* 消息队列句柄 */

                                          (void *)&ptMsg, /* 这里获取的是结构体的地址 */

                                          (TickType_t)xMaxBlockTime)/* 设置阻塞时间 */

if(xResult == pdPASS)

{

/* 成功接收,并通过串口将数据打印出来 */

printf("接收到消息队列数据 ptMsg->ucMessageID = %d\r\n", ptMsg->ucMessageID)

printf("接收到消息队列数据 ptMsg->ulData[0] = %d\r\n", ptMsg->ulData[0])

printf("接收到消息队列数据 ptMsg->usData[0] = %d\r\n", ptMsg->usData[0])

}

else

{

/* 超时 */

bsp_LedToggle(2)

bsp_LedToggle(3)

}

}

}

/*按键或什么外部触发启动定时器*/

printf("启动单次定时器中断,50ms 后在定时器中断给任务 vTaskMsgPro 发送消息\r\n")

bsp_StartHardTimer(1 ,50000, (void *)TIM_CallBack1)

printf("启动单次定时器中断,50ms 后在定时器中断给任务 vTaskLED 发送消息\r\n")

bsp_StartHardTimer(2 ,50000, (void *)TIM_CallBack2)

/*

*********************************************************************************************************

*  函 数 名: TIM_CallBack1 和 TIM_CallBack2

*  功能说明: 定时器中断的回调函数,此函数被 bsp_StartHardTimer 所调用。

*  形 参: 无

*  返 回 值: 无

*********************************************************************************************************

*/

static uint32_t g_uiCount = 0/* 设置为全局静态变量,方便数据更新 */

static void TIM_CallBack1(void)

{

BaseType_t xResult

BaseType_t xHigherPriorityTaskWoken = pdFALSE

/* 中断消息处理,此处省略 */

……

g_uiCount++

/* 向消息队列发数据 */

xQueueSendFromISR(xQueue1,

                                    (void *)&g_uiCount,

                                    &xHigherPriorityTaskWoken)

/* 如果 xHigherPriorityTaskWoken = pdTRUE,那么退出中断后切到当前最高优先级任务执行 */

portYIELD_FROM_ISR(xHigherPriorityTaskWoken)

}

static void TIM_CallBack2 (void)

{

MSG_T *ptMsg

BaseType_t xHigherPriorityTaskWoken = pdFALSE

/* 中断消息处理,此处省略 */

……

/* 初始化结构体指针 */

ptMsg = &g_tMsg

/* 初始化数组 */

ptMsg->ucMessageID++

ptMsg->ulData[0]++

ptMsg->usData[0]++

/* 向消息队列发数据 */

xQueueSendFromISR(xQueue2,

                                    (void *)&ptMsg,

                                    &xHigherPriorityTaskWoken)

/* 如果 xHigherPriorityTaskWoken = pdTRUE,那么退出中断后切到当前最高优先级任务执行 */

portYIELD_FROM_ISR(xHigherPriorityTaskWoken)

}

方法/步骤

嵌入式 *** 作系统有分时 *** 作系统和实时 *** 作系统,如果 *** 作系统能够使计算机系统及时响应外部事件请求,并能控制所有实时设备和实时任务协调运行,且能在一个规定的时间内完成对事件的处理,怎么这种系统称为实时 *** 作系统。

如果系统必须在极其严格的时间内完成的任务叫做硬件的实时 *** 作系统,如果不是很严格的话就是软件的实时 *** 作系统。

前往官网下载最新版的FreeRTOS系统,然后解压缩到本地。有两个文件夹,FreeRTOS文件夹里面是 *** 作系统内核,FreeRTOS-Plus里面是一些中间件如文件系统,网络协议栈等。

值得一提的是,FreeRTOS的教学用书和API参考手册电子版均已免费提供,建议在现在安装FreeRTOS的同时也一并下载到本地,以供后期学习查阅。

FreeRTOS的主要特点如下:

1. 支持抢占式调度,合作式调度和时间片调度

2. 具有低功耗模式,称为tickless模式

3. FreeRTOS-MPU支持M3/M4/M7内核的MPU(内存保护单元)

4. 典型的内核使用大小在4k~9k

5. 支持消息队列、二值信号量、计数信号量、递归信号量和互斥信号量,可用于任务与任务之间的消息传递和同步,任务与终端间的消息传递和同步

6. 任务数量不限,任务优先级数量不限

7. 高效的软件定时器,不需要损耗额外的CPU时间,除非需要执行定时器任务

8. 任务间直接的消息传递,速度较快

9. FreeRTOS的队列是其它通信和同步机制的基础

移植FreeRTOS之前,原有的工程(比如跑马灯,越简单越好)中不能有SysTick、PendSV和SVC三个系统中断的使用,因为FreeRTOS系统要使用这三个中断。

1. 准备好简单工程的模板

2. 在工程模板中创建FreeRTOS文件夹,并将解压后源码FreeRTOS文件夹中Source目录下的所有内容复制进来

3. 在user目录下需要手动窗件FreeRTOSConfig.h的配置文件,也可以从官方demo中拿来修改后使用,比如从下载的源码目录下的FreeRTOS/Demo/CORTEX_M4F_STM32F407ZG-SK中进行拷贝

4. 将源码文件添加到MDK的工程目录中,其中heap_4.c文件路径Source/portable/MemMang,port.c的路径FreeRTOS/Source/portable/RVDS/ARM_CM4F,这是因为我们使用的M451是CM4F内核的

在工程中添加新的头文件搜索路径:

.\FreeRTOS\include

.\FreeRTOS\portable\RVDS\ARM_CM4F

打开FreeRTOSConfig.h配置文件,根据自己硬件配置进行修改

1. 首先将文件开头的__ICCARM__修改为__CC_ARM,即把编译器从IAR改为RealView

2. 将以下宏配置为0

configUSE_IDLE_HOOK

configUSE_TICK_HOOK

configCHECK_FOR_STACK_OVERFLOW

configUSE_MALLOC_FAILED_HOOK

3. #define configUSE_PREEMPTION 1 将会使能抢占式调度器

4. #define configCPU_CLOCK_HZ ( SystemCoreClock )设置系统主频(M451的系统主频为72MHz)

5. #define configTICK_RATE_HZ ( ( TickType_t ) 1000 )设置系统节拍为1kHz,即1ms

6. #define configMAX_PRIORITIES ( 5 )定义可供用户使用的最大优先级数为5,那么用户可以使用的优先级号是0,1,2,3,4

6. #define configTOTAL_HEAP_SIZE ( ( size_t ) ( 30 * 1024 ) )定义堆大小,FreeRTOS内核,用户动态申请内存,任务栈等都需要这个空间

4. 将工程整体重新编译一次,这样FreeRTOS基本移植结束了

编写测试程序来验证一致的FreeRTOS是否已经可以使用

1. 在main.c中添加一下几个头文件

#include "FreeRTOS.h"

#include "task.h"

#include "queue.h"

#include "croutine.h"

2. 在main函数的开头,禁止全局中断(除了NMI和HardFault),具体方法是:__set_PRIMASK(1)这样做的好处是可以防止执行的中断服务程序中有FreeRTOS的API函数,保证系统正常启动,不受别的中断影响。在port.c中的函数prvStartFirstTask中会重新开启全局中断

3. 创建任务AppTaskCreate()

4. 启动调度,开始执行任务vTaskStartScheduler()

烧写程序,运行,可以从串口看到正确的输出信息


欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/yw/11538078.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-16
下一篇 2023-05-16

发表评论

登录后才能评论

评论列表(0条)

保存