一、什么是FreeRTOS?
FreeRTOS 是 RTOS 系统的一种,FreeRTOS 十分的小巧,可以在资源有限的微控制器中运行;
1、 FreeRTOS是免费的。
2、许多其他半导体厂商产品的 SDK 包就使用 FreeRTOS 作为其操作系统,尤其是 WIFI、
蓝牙这些带协议栈的芯片或模块。
3、FreeRTOS 被移植到了很多不同的微处理器上。
4、FreeRTOS 的内核支持抢占式,合作式和时间片调度。
5、FreeRTOS 系统简单、小巧、易用,通常情况下内核占用 4k-9k 字节的空间。
二、FreeRTOS移植
1、创建基础文件,添加FreeRTOS源码
portable 文件夹,需要留下 keil、MemMang 和 RVDS这三个文件夹:
最后创建基础工程,新建分组 FreeRTOS_CORE 和 FreeRTOS_PORTABLE:
2、修改SYSTEM文件
①修改sys.h文件
在 sys.h 文件里面用宏 SYSTEM_SUPPORT_OS 来定义是否使用 OS,我们使用了 FreeRTOS
cpp
#define SYSTEM_SUPPORT_OS 1 //定义系统文件夹是否支持 OS
②修改usart.c文件
添加 FreeRTOS.h 头文件:
cpp
#include "FreeRTOS.h" //os 使用
使用 FreeRTOS 不需要OSIntEnter()和 OSIntExit()中断函数,将对应的代码删除:
cpp
void USART1_IRQHandler(void)
{
u8 Res;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
Res =USART_ReceiveData(USART1);
if((USART_RX_STA&0x8000)==0)
{
if(USART_RX_STA&0x4000)
{
if(Res!=0x0a)USART_RX_STA=0;
else USART_RX_STA|=0x8000;
}
else
{
if(Res==0x0d)USART_RX_STA|=0x4000;
else
{
USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
USART_RX_STA++;
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;
}
}
}
}
}
3、修改delay.c文件
cpp
//systick 中断服务函数,使用 OS 时用到
void SysTick_Handler(void)
{
if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
{
xPortSysTickHandler();
}
cpp
//初始化延迟函数
//SYSTICK 的时钟固定为 AHB 时钟,基础例程里面 SYSTICK 时钟频率为 AHB/8
//SYSCLK:系统时钟频率
{ void delay_init()
u32 reload;
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);//选择外部时钟 HCLK
fac_us=SystemCoreClock/1000000;
//不论是否使用 OS,fac_us 都需要使用
reload=SystemCoreClock/1000000;
//每秒钟的计数次数 单位为 M
reload*=1000000/configTICK_RATE_HZ;
//根据 configTICK_RATE_HZ 设定溢出
//时间 reload 为 24 位寄存器,最大值:
//16777216,在 72M 下,约合 0.233s 左右
fac_ms=1000/configTICK_RATE_HZ;
//代表 OS 可以延时的最少单位
SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk; //开启 SYSTICK 中断
SysTick->LOAD=reload;
//每 1/configTICK_RATE_HZ 秒中断
//一次
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启 SYSTICK
}
修改延时函数:
cpp
//延时 nus
//nus:要延时的 us 数.
//nus:0~204522252(最大值即 2^32/fac_us@fac_us=168)
void delay_us(u32 nus)
{
u32 ticks;
u32 told,tnow,tcnt=0;
u32 reload=SysTick->LOAD; //LOAD 的值
ticks=nus*fac_us;//需要的节拍数
told=SysTick->VAL;//刚进入时的计数器值
while(1)
{
tnow=SysTick->VAL;
if(tnow!=told)
{
//这里注意一下 SYSTICK 是一个递减的计数器就可以了.
if(tnow<told)tcnt+=told-tnow;
else tcnt+=reload-tnow+told;
told=tnow;
if(tcnt>=ticks)break;//时间超过/等于要延迟的时间,则退出.
}
};
}
/延时 nms,会引起任务调度
//nms:要延时的 ms 数
//nms:0~65535
void delay_ms(u32 nms)
{
if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
{
if(nms>=fac_ms)//延时的时间大于 OS 的最少时间周期
{
vTaskDelay(nms/fac_ms);//FreeRTOS 延时
}
nms%=fac_ms;//OS 已经无法提供这么小的延时了,
//采用普通方式延时
}
delay_us((u32)(nms*1000));//普通方式延时
}
//延时 nms,不会引起任务调度
//nms:要延时的 ms 数
void delay_xms(u32 nms)
{
u32 i;
for(i=0;i<nms;i++) delay_us(1000);
}
修改完编译。
三、关于FreeRTOS 任务
RTOS 系统的核心就是任务管理。
1、创建任务
使用函数 xTaskCreate()或 xTaskCreateStatic()来创建任务,
任务模版:
cpp
void vATaskFunction(void *pvParameters)
{
for( ; ; )
{
--任务应用程序--
vTaskDelay();
}
/* 不 能 从 任 务 函 数 中 返 回 或 者 退 出 , 从 任 务 函 数 中 返 回 或 退 出 的 话 就 会 调 用
configASSERT(),前提是你定义了 configASSERT()。如果一定要从任务函数中退出的话那一定
要调用函数 vTaskDelete(NULL)来删除此任务。*/
vTaskDelete(NULL);
}
常用函数:
1、xTaxkCreate()函数
cpp
BaseType_t xTaskCreate(
TaskFunction_t pxTaskCode,
const char * const pcName,
const uint16_t usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask )
参数:
pxTaskCode:任务函数。
pcName:任务名字,一般用于追踪和调试,任务名字长度不能超过。
configMAX_TASK_NAME_LEN。
usStackDepth:任务堆栈大小,注意实际申请到的堆栈是 usStackDepth 的 4 倍。其中空闲任
务的任务堆栈大小为 configMINIMAL_STACK_SIZE。
pvParameters:传递给任务函数的参数。
uxPriotiry:任务优先级,范围 0~ configMAX_PRIORITIES-1。
pxCreatedTask: 任务句柄,任务创建成功以后会返回此任务的任务句柄,这个句柄其实就是
任务的任务堆栈。此参数就用来保存这个任务句柄。其他 API 函数可能会使用 到这个句柄。
返回值:
pdPASS: 任务创建成功。
errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY: 任务创建失败,因为堆内存不足!
2、xTaskCreateStatic()函数
cpp
TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode,
const char * const pcName,
const uint32_t ulStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
StackType_t * const puxStackBuffer,
StaticTask_t * const pxTaskBuffer )
参数:
pxTaskCode:任务函数。
pcName:任务名字,一般用于追踪和调试,任务名字长度不能超过。
configMAX_TASK_NAME_LEN。
usStackDepth:任务堆栈大小,由于本函数是静态方法创建任务,所以任务堆栈由用户给出,
一般是个数组,此参数就是这个数组的大小。
pvParameters:传递给任务函数的参数。
uxPriotiry:任务优先级,范围 0~ configMAX_PRIORITIES-1。
puxStackBuffer: 任务堆栈,一般为数组,数组类型要为 StackType_t 类型。
pxTaskBuffer:任务控制块。
返回值:
NULL:
任务创建失败,puxStackBuffer 或 pxTaskBuffer 为 NULL 的时候会导致这个错误的发生。
其他值:
任务创建成功,返回任务的任务句柄。
3、 xTaskCreateRestricted()函数
cpp
BaseType_t xTaskCreateRestricted( const TaskParameters_t * const pxTaskDefinition,
TaskHandle_t * pxCreatedTask )
参数:
pxTaskDefinition:指向一个结构体 TaskParameters_t,这个结构体描述了任务的任务函数、
堆栈大小、优先级等。此结构体在文件 task.h 中有定义。
pxCreatedTask:任务句柄。
返回值:
pdPASS:任务创建成功。
其他值:任务未创建成功,很有可能是因为 FreeRTOS 的堆太小了
4、 vTaskDelete()函数
vTaskDelete( TaskHandle_t xTaskToDelete )
参数:
xTaskToDelete:要删除的任务的任务句柄。
返回值:
无
四、FreeRTOS列表和列表项
1、什么是列表?
列表概念上和链表很相似,列表用来跟踪FreeRTOS中的任务。与列表相关的全部东西都在文件list.c 和 list.h 中。结构体如下:
cpp
typedef struct xLIST
{
listFIRST_LIST_INTEGRITY_CHECK_VALUE
configLIST_VOLATILE UBaseType_t uxNumberOfItems;
ListItem_t * configLIST_VOLATILE pxIndex;
MiniListItem_t xListEnd;
listSECOND_LIST_INTEGRITY_CHECK_VALUE
} List_t;
2、列表项
列表项就是存放在列表中的项目,FreeRTOS 提供了两种列表项:列表项和迷你列表项。
cpp
struct xLIST_ITEM
{
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE
configLIST_VOLATILE TickType_t xItemValue;
struct xLIST_ITEM * configLIST_VOLATILE pxNext;
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;
void * pvOwner;
void * configLIST_VOLATILE pvContainer;
listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE
};
typedef struct xLIST_ITEM ListItem_t;
3、迷你列表项
cpp
struct xMINI_LIST_ITEM
{
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE
configLIST_VOLATILE TickType_t xItemValue;
struct xLIST_ITEM * configLIST_VOLATILE pxNext;
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;
};
typedef struct xMINI_LIST_ITEM MiniListItem_t;
其他初始化看管方文档。