概述
文章目录
- 1.FreeRTOS的简介和定位
- 定位
- 配置
- 2.FreeRTOS的主要内容
- 延时
- 队列
- 信号量
- 通知
- 3.FreeRTOS的常见用法
- 4.FreeRTOS的注意事项
- 优先级
- 内存大小
- 初始化
- 常见错误
1.FreeRTOS的简介和定位
定位
- 嵌入式操作系统
一个免费,轻量级的操作系统,可以使代码框架清晰,更具有层次感。 - 任务调度器内核—双向链表
链表内核使任务查询速度加快,使用上理解为队列的架构。 - 任务操作和任务间通信方式多样
挂起,延时,队列,信号量,通知…
配置
配置步骤
1.CubeMX的参数配置
2.任务创建
3.初始化及启动
- 配置前首先需要将单片机的主定时器配置为普通定时器,即Timer1,Timer2…,(默认为Systick,CubeMX会警告)
1.CubeMX的配置
(1)选择接口标准
(2)配置任务最大优先级
(3)使能固定延时
2.任务创建
----以创建陀螺仪(IMU)任务为例
(1)定义变量及函数声明
/* 任务句柄 */
TaskHandle_t DeviceIMU_Handle;
/* 任务执行函数 */
void Device_IMU(void *arg);
(2)任务函数定义
/**
*@brief IMU任务
*/
void Device_IMU(void *arg)
{
/* Cache for Task */
/* Pre-Load for task */
/* Infinite loop */
for(;;)
{
/* Code */
}
}
(3)任务创建
void Service_Devices_Init(void)
{
xTaskCreate(Device_IMU, /* 任务函数 */
"Dev.IMU", /* 任务名称 */
Large_Stack_Size, /* 堆栈大小 */
NULL, /* 任务参数 */
PriorityHigh, /* 优先级 */
&DeviceIMU_Handle); /* 任务句柄 */
}
3.启动任务调度器
(1)任务初始化
// 该任务为CubeMX自动生成,不需要用户创建,只要填充内容即可。
void StartDefaultTask(void const * argument)
{
/* USER CODE BEGIN StartDefaultTask */
/* Infinite loop */
for(;;)
{
/* 初始化任务 */
Task_Init();
/* 删除任务 */
vTaskDelete(defaultTaskHandle);
}
/* USER CODE END StartDefaultTask */
}
(2)启动任务调度器(自动生成)
2.FreeRTOS的主要内容
延时
(1)DelayUntil
- 延时时间包括了任务执行时间,严格控制时序,但是要保证任务执行时间小于延时时间。
void DelayUntil_Task(void *arg)
{
/* Cache for Task */
TickType_t xLastWakeTime_t = xTaskGetTickCount();
TickType_t _xTicksToWait = pdMS_TO_TICKS(1);
/* Pre-Load for task */
/* Infinite loop */
for(;;)
{
vTaskDelayUntil(&xLastWakeTime_t, _xTicksToWait);
/* Code */
}
}
(2)Delay
- 延时时间不包括任务执行时间,任务执行结束后,经过设定的延时时间,再到下一个任务。
void Delay_Task(void *arg)
{
/* Cache for Task */
TickType_t _xTicksToWait = pdMS_TO_TICKS(1);
/* Pre-Load for task */
/* Infinite loop */
for(;;)
{
vTaskDelay(_xTicksToWait);
/* Code */
}
}
队列
----以串口接收为例
(1)结构体定义
typedef struct
{
uint8_t port_num;
uint16_t len;
void* address;
} USART_COB;
(2)存储队列定义和初始化
QueueHandle_t USART_RxPort; //全局变量
USART_RxPort = xQueueCreate(4,sizeof(USART_COB));
(3)定义中断回调函数
uint32_t User_UART1_RxCpltCallback(uint8_t* Recv_Data, uint16_t ReceiveLen)
{
static USART_COB Usart_RxCOB;
//Send To UART Receive Queue
if(USART_RxPort != NULL)
{
Usart_RxCOB.port_num = 1;
Usart_RxCOB.len = ReceiveLen;
Usart_RxCOB.address = Recv_Data;
xQueueSendFromISR(USART_RxPort,&Usart_RxCOB,0);
}
return 0;
}
(4)定义处理函数
void Task_UsartRecieve(void *arg)
{
USART_COB Usart_RxCOB;
for(;;)
{
/* Usart Recevice Port */
if(xQueueReceive(USART_RxPort,&Usart_RxCOB,portMAX_DELAY) == pdPASS)
{
/* Code */
}
}
信号量
----以二值信号量为例
(1)信号量定义和初始化
SemaphoreHandle_t SemaphoreHandle; //全局变量
SemaphoreHandle = xSemaphoreCreateBinary();
(2)定义信号量发送函数
void SemaphoreTx_Task(void *arg)
{
/* Cache for Task */
TickType_t xLastWakeTime_t = xTaskGetTickCount();
TickType_t _xTicksToWait = pdMS_TO_TICKS(1);
/* Pre-Load for task */
/* Infinite loop */
for(;;)
{
/* 数据更新 */
data.Update();
/* 发送二值信号量,表示数据更新 */
xSemaphoreGive(SemaphoreHandle);
/* Pass control to the next task */
vTaskDelayUntil(&xLastWakeTime_t,_xTicksToWait);
}
}
(3)定义信号量接收函数
void SemaphoreRx_Task(void *arg)
{
/* 任务等待变量 */
BaseType_t wait = pdFALSE;
/* Infinite loop */
for(;;)
{
/* 等待数据更新 */
wait = xSemaphoreTake(SemaphoreHandle, portMAX_DELAY);
if(wait == pdTRUE)
{
/* Code */
}
}
通知
(1)定义通知发送函数
void NotifyTx_Task(void *arg)
{
/* Cache for Task */
TickType_t xLastWakeTime_t = xTaskGetTickCount();
TickType_t _xTicksToWait = pdMS_TO_TICKS(1);
/* Pre-Load for task */
/* Infinite loop */
for(;;)
{
/* Code */
/* 发送通知 */
xTaskNotifyGive(NotifyRx_Handle);
/* Pass control to the next task */
vTaskDelayUntil(&xLastWakeTime_t,_xTicksToWait);
}
}
(2)定义通知接收函数
void NotifyRx_Task(void *arg)
{
/* Cache for Task */
/* Pre-Load for task */
/* Infinite loop */
for(;;)
{
/* 等待通知 */
ulTaskNotifyTake(pdTRUE,portMAX_DELAY);
/* Code */
}
}
3.FreeRTOS的常见用法
实战:电机驱动
1.电机以固定频率f1反馈数据给单片机。
2.单片机解算数据包,更新电机数据。
3.单片机以固定频率f2计算控制输出,并发送给电机。
实现流程图
实现源码(只给出框架部分)
(1)定义中断回调函数
void CAN1_CallBack(CAN_RxBuffer *CAN_RxMessage)
{
static CAN_COB CAN_RxCOB;
/* 数据标准化处理 */
Convert_Data(CAN_RxMessage,&CAN_RxCOB);
/* 发送数据到队列中 */
if(CAN1_RxPort != NULL)
xQueueSendFromISR(CAN1_RxPort,&CAN_RxCOB,0);
}
(2)定义接收处理任务
void Task_CAN1Receive(void *arg)
{
/* Cache for Task */
CAN_COB CAN_RxMsg;
/* Pre-Load for task */
/* Infinite loop */
for(;;)
{
/* 等待CAN数据包更新 */
if(xQueueReceive(CAN1_RxPort,&CAN_RxMsg,portMAX_DELAY) == pdPASS)
{
/* 更新电机数据 */
Update_Motor_Data(&CAN_RxMsg);
}
}
}
(3)定义控制任务
void Task_GimabalCtrl(void *arg)
{
/* Cache for Task */
TickType_t xLastWakeTime_t = xTaskGetTickCount();
TickType_t _xTicksToWait = pdMS_TO_TICKS(1);
CAN_COB CAN_TxMsg;
/* Pre-Load for task */
/* Infinite loop */
for(;;)
{
/* 固定延时时间 */
vTaskDelayUntil(&xLastWakeTime_t, _xTicksToWait);
/* 计算电机的控制输出 */
Motor_Ctrl(&CAN_TxMsg);
/* 发送数据到发送队列 */
xQueueSend(can1_port, &CAN_TxMsg, 0);
}
}
(4)定义数据发送任务
void Task_CAN1Transmit(void *arg)
{
/* Cache for Task */
CAN_COB CAN_TxMsg;
/* Pre-Load for task */
/* Infinite loop */
for(;;)
{
/* 等待发送的数据包更新 */
if(xQueueReceive(CAN1_TxPort,&CAN_TxMsg,portMAX_DELAY) == pdPASS)
{
/* 发送数据给电机 */
CANx_SendData(&hcan1,CAN_TxMsg.ID,CAN_TxMsg.Data,CAN_TxMsg.DLC);
}
}
}
4.FreeRTOS的注意事项
优先级
原则:
1.轮询任务优先级低,等待任务优先级高。
2.轮询任务中,需严格控制时序的任务优先级相对较高。
内存大小
原则:
1.评估任务所需的内存量来分配内存。内存过小会进硬件中断。
内存过大会浪费内存,且增加不必要的任务执行时间。2.对大部分情况而言,内存宜大不宜小。
初始化
原则:
1.通知接收任务要先于通知发送任务创建。
初始化方式:
1.在任务调度器开启前创建任务: 初始化函数放在main函数MX_FREERTOS_Init()前面。
2.在任务调度器开启后创建任务: 初始化函数放在 StartDefaultTask任务中。
常见错误
硬件错误
1.检查内存分配大小
2.检查是否使用了未初始化的外设
某个任务不执行
1.检查优先级配置
2.检查内存分配大小
最后
以上就是害怕篮球为你收集整理的FreeRTOS学习1.FreeRTOS的简介和定位2.FreeRTOS的主要内容3.FreeRTOS的常见用法4.FreeRTOS的注意事项的全部内容,希望文章能够帮你解决FreeRTOS学习1.FreeRTOS的简介和定位2.FreeRTOS的主要内容3.FreeRTOS的常见用法4.FreeRTOS的注意事项所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复