嵌入式:FreeRTOS的使用(未完)
为了方便与UCOS对比,顺序按照UCOS那篇编写。
0、一些移植、系统相关
1、框架写法(个人习惯相关)
1-1、main 函数里创建一个开始任务
int main(void) { 初始化外设 xTaskCreate(); //创建开始任务 vTaskStartScheduler(); //开启任务调度 }
1-2、开始任务里,创建我们要运行的多个任务
void start_task(void *pvParameters) { taskENTER_CRITICAL(); //进入临界区 xTaskCreate(); //创建任务 1 xTaskCreate(); //创建任务 2 xTaskCreate(); //创建任务 3 vTaskDelete(StartTask_Handler); //删除开始任务 taskEXIT_CRITICAL(); //退出临界区 }
2、任务创建、挂起、删除
2-0、相关配置
#define configSUPPORT_DYNAMIC_ALLOCATION 1 //支持动态内存申请 //#define configSUPPORT_STATIC_ALLOCATION 1 //支持静态内存申请 #define configTOTAL_HEAP_SIZE ((size_t)(20*1024)) //系统所有总的堆大小,heap_x.h需要,动态申请
2-1、任务创建(动态)
//==================任务创建宏定义,便于修改================== #define START_TASK_PRIO 1 //任务优先级 #define START_STK_SIZE 256 //任务堆栈大小 TaskHandle_t StartTask_Handler; //任务句柄 void start_task(void *pvParameters); //任务函数 //==================任务创建函数================== xTaskCreate((TaskFunction_t )start_task, //任务函数 (const char* )"start_task", //任务名称 (uint16_t )START_STK_SIZE, //任务堆栈大小 (void* )NULL, //传递给任务函数的参数 (UBaseType_t )START_TASK_PRIO, //任务优先级 (TaskHandle_t* )&StartTask_Handler); //任务句柄
2-2、任务挂起
vTaskSuspend(Task1Task_Handler); //挂起任务1
2-3、任务解挂
2-3-1、任务内任务解挂
vTaskResume(Task1Task_Handler); //恢复任务1
2-3-2、中断内任务解挂
BaseType_t YieldRequired; YieldRequired=xTaskResumeFromISR(Task1Task_Handler); //恢复任务1 portYIELD_FROM_ISR(YieldRequired); //判断是否需要调度到恢复的任务
2-4、任务删除
vTaskDelete(Task1Task_Handler); //删除任务1
2-5、任务创建(静态)
2-5-1、静态任务创建
//==================任务创建宏定义,便于修改================== #define START_TASK_PRIO 1 //任务优先级 #define START_STK_SIZE 128 //任务堆栈大小 StackType_t StartTaskStack[START_STK_SIZE]; //任务堆栈 StaticTask_t StartTaskTCB; //任务控制块 TaskHandle_t StartTask_Handler; //任务句柄 void start_task(void *pvParameters); //任务函数 //==================任务创建函数================== StartTask_Handler=xTaskCreateStatic((TaskFunction_t )start_task, //任务函数 (const char* )"start_task", //任务名称 (uint32_t )START_STK_SIZE, //任务堆栈大小 (void* )NULL, //传递给任务函数的参数 (UBaseType_t )START_TASK_PRIO, //任务优先级 (StackType_t* )StartTaskStack, //任务堆栈 (StaticTask_t* )&StartTaskTCB); //任务控制块
2-5-2、静态创建任务还需实现 空闲任务、定时任务
//==================任务创建宏定义,便于修改================== static StackType_t IdleTaskStack[configMINIMAL_STACK_SIZE]; //空闲任务任务堆栈 static StaticTask_t IdleTaskTCB; //空闲任务控制块 static StackType_t TimerTaskStack[configTIMER_TASK_STACK_DEPTH]; //定时器服务任务堆栈 static StaticTask_t TimerTaskTCB; //定时器服务任务控制块 //==================任务创建函数================== //获取空闲任务地任务堆栈和任务控制块内存 void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize) { *ppxIdleTaskTCBBuffer=&IdleTaskTCB; *ppxIdleTaskStackBuffer=IdleTaskStack; *pulIdleTaskStackSize=configMINIMAL_STACK_SIZE; } //获取定时器服务任务的任务堆栈和任务控制块内存 void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer, StackType_t **ppxTimerTaskStackBuffer, uint32_t *pulTimerTaskStackSize) { *ppxTimerTaskTCBBuffer=&TimerTaskTCB; *ppxTimerTaskStackBuffer=TimerTaskStack; *pulTimerTaskStackSize=configTIMER_TASK_STACK_DEPTH; }
3、时间片轮转
3-0、相关配置(时间片片长 即 1/Tick中断频率)
#define configUSE_PREEMPTION 1 //1使用抢占式内核,0使用协程 #define configUSE_TIME_SLICING 1 //1使能时间片调度(默认式使能的) #define configTICK_RATE_HZ (20) //时钟节拍频率,20HZ = 50ms
3-1、两个任务优先级相等
#define TASK1_TASK_PRIO 2 #define TASK1_STK_SIZE 128 TaskHandle_t Task1Task_Handler; void task1_task(void *pvParameters); #define TASK2_TASK_PRIO 2 #define TASK2_STK_SIZE 128 TaskHandle_t Task2Task_Handler; void task2_task(void *pvParameters);
4、钩子函数。
4-0、相关配置
#define configUSE_IDLE_HOOK 1 //1,使用空闲钩子;0,不使用 #define configUSE_TICK_HOOK 1 //1,使用时间片钩子;0,不使用
4-1、自己实现钩子函数
void vApplicationIdleHook( void ); //空闲钩子函数 void vApplicationTickHook( void ); //时钟节拍钩子函数
5、软件定时器
5-1、软件定时器创建
//==============定时器结构体、函数声明============== TimerHandle_t MyTimer_Handle; //定时器句柄 void ReloadCallback(TimerHandle_t xTimer); //定时器回调函数 //==============定时器创建============== MyTimer_Handle=xTimerCreate((const char* )"ReloadTimer", //定时器名称 (TickType_t )1000, //周期1s(1000个时钟节拍) (UBaseType_t )pdTRUE, //周期模式 (void* )1, //定时器ID (TimerCallbackFunction_t)ReloadCallback); //定时器回调函数
5-2、定时器“中断服务函数”,回调函数
void ReloadCallback(TimerHandle_t xTimer) { //do something }
5-3、定时器开启(也有复位效果)
xTimerStart(MyTimer_Handle,0); //开启定时器
5-4、定时器停止
xTimerStop(MyTimer_Handle,0); //关闭定时器
5-5、定时器复位
xTimerReset(MyTimer_Handle, 0)
6、消息队列
6-1、消息队列创建
//================消息队列宏定义================ #define MESSAGE_Q_NUM 4 //发送数据的消息队列的数量 #define MESSAGE_Q_ITEM_NUM 200 //每个消息的空间大小 QueueHandle_t Message_Queue; //信息队列句柄 //================消息队列创建================ Message_Queue=xQueueCreate(MESSAGE_Q_NUM,MESSAGE_Q_ITEM_NUM);
6-2、消息发送
6-2-1、任务内消息发送
u8 sendData[MESSAGE_Q_ITEM_NUM]; BaseType_t err; err=xQueueSend(Message_Queue,&senddata,10); //10为发送等待时间,有可能队列已满,err = errQUEUE_FULL 或 err = pdPASS
6-2-2、中断内消息发送
u8 sendData[MESSAGE_Q_ITEM_NUM]; BaseType_t xHigherPriorityTaskWoken; xQueueSendFromISR(Message_Queue,sendData,&xHigherPriorityTaskWoken); //向队列中发送数据,返回值,依然是 满了或Pass,第三个参数是判断高优先级接受到队列后,退出中断,是否需要调度 portYIELD_FROM_ISR(xHigherPriorityTaskWoken);//如果需要的话进行一次任务切换
6-3、消息接收
6-3-1、任务内消息接收
u8 *receiveData; xQueueReceive(Message_Queue,receiveData,portMAX_DELAY) //返回值为pdPASS 或 errQUEUE_EMPTY,这里等待时间用了portMAX_DELAY阻塞,所以不用再判断了。
6-3-2、中断内消息接收
u8 *receiveData; err=xQueueReceiveFromISR(Message_Queue,receiveData,&xTaskWokenByReceive); //向队列中接受数据,返回值, FAIL或Pass,第三个参数是判断高优先级接受到队列后,退出中断,是否需要调度 portYIELD_FROM_ISR(xTaskWokenByReceive);//如果需要的话进行一次任务切换
6-4、队列剩余大小
u8 remain_size; //消息队列剩余大小 remain_size=uxQueueSpacesAvailable(Message_Queue); //得到队列剩余大小
6-5、队列使用大小
u8 used_size; //消息队列使用大小 used_size=uxQueueMessagesWaiting(Message_Queue); //得到队列使用大小
7、二值信号量(还有静态创建函数、中断内接受/发送函数,基本同上,不再重复)
7-1、二值信号量创建
SemaphoreHandle_t BinarySemaphore; //二值信号量句柄 BinarySemaphore=xSemaphoreCreateBinary(); //创建二值信号量
7-2、二值信号量等待
BaseType_t err; err = xSemaphoreTake(BinarySemaphore,portMAX_DELAY); //获取信号量
7-3、二值信号量发送
BaseType_t err; err = xSemaphoreGive(BinarySemaphore); //释放二值信号量
8、计数信号量(还有静态创建函数、中断内接受/发送函数,基本同上,不再重复)
8-1、计数信号量创建
SemaphoreHandle_t CountSemaphore;//计数型信号量 CountSemaphore=xSemaphoreCreateCounting(255,0); //创建计数型信号量,最大计数和初始化计数,参数没改动的话,为long,所以最大值可以设计为不止255
8-2、计数信号量等待
UBaseType_t semavalue; xSemaphoreTake(CountSemaphore,portMAX_DELAY); //等待数值信号量,阻塞 semavalue=uxSemaphoreGetCount(CountSemaphore); //获取数值信号量值
8-3、计数信号量发送
BaseType_t err; err=xSemaphoreGive(CountSemaphore);//释放计数型信号量
9、互斥信号量(还有静态创建函数,在中断不能用互斥信号量)
9-1、互斥信号量创建
SemaphoreHandle_t MutexSemaphore; //互斥信号量 MutexSemaphore=xSemaphoreCreateMutex(); //创建互斥信号量
9-2、互斥信号量等待
xSemaphoreTake(MutexSemaphore,portMAX_DELAY); //获取互斥信号量,因为是阻塞,也就不需要查看什么返回值
9-3、互斥信号量发送
xSemaphoreGive(MutexSemaphore); //释放互斥信号量
10、递归互斥信号量(还有静态创建函数,在中断不能用递归互斥信号量)
10-1、递归互斥信号量创建
SemaphoreHandle_t RecursiveMutex; RecursiveMutex = xSemaphoreCreateRecursiveMutex(); //创建递归互斥信号量
10-2、递归互斥信号量等待
xSemaphoreTakeRecursive(RecursiveMutex,10); //10为等待节拍
10-3、递归互斥信号量发送
xSemaphoreGiveRecursive(RecursiveMutex); //发送递归互斥信号量
11、事件标记组(还有静态创建函数、中断内接受/发送函数,基本同上,不再重复)
11-1、事件标记组创建
//例子:3个事件 #define EVENTBIT_0 (1<<0) #define EVENTBIT_1 (1<<1) #define EVENTBIT_2 (1<<2) #define EVENTBIT_ALL (EVENTBIT_0|EVENTBIT_1|EVENTBIT_2) EventGroupHandle_t EventGroupHandler; //事件标志组句柄 EventGroupHandler=xEventGroupCreate(); //创建事件标志组
11-2、事件标记组置位
xEventGroupSetBits(EventGroupHandler,EVENTBIT_1); //事件1置位
11-3、事件标记组清除置位
xEventGroupClearBits(EventGroupHandler,EVENTBIT_1); //事件1清除
11-4、事件标记组值获取
EventBits_t NewValue; NewValue = xEventGroupGetBits(EventGroupHandler); //获取事件组的
11-5、事件标记位组等待
EventValue=xEventGroupWaitBits((EventGroupHandle_t )EventGroupHandler, //句柄 (EventBits_t )EVENTBIT_ALL, //标志位 (BaseType_t )pdTRUE, //获取成功后 清除 (BaseType_t )pdTRUE, //等待所有标志位 置位 (TickType_t )portMAX_DELAY); //阻塞
12、内存管理
12-1、内存申请
u8 *buffer; buffer=pvPortMalloc(30); //申请内存,30个字节
12-2、内存释放
vPortFree(buffer); //释放内存
12-3、获取内存剩余空间
u32 freeSize; freeSize = xPortGetFreeHeapSize(); //获取剩余内存大小
13、通知
13-1、通知事件发送(++)
xTaskNotifyGive(task_Handler); //给task_Handler发送个通知
13-2、通知事件获取(--,并根据参数是否清0)
u32 NotifyValue; NotifyValue=ulTaskNotifyTake(pdTRUE,portMAX_DELAY); //获取任务通知,参数1,读完清0,参数2阻塞
13-3、通知值发送
13-3-1、通知值发送(设置通知值,可发送一个数据)
u8 data; BaseType_t err; err=xTaskNotify((TaskHandle_t )Task_Handler, //接收任务通知的任务句柄 (uint32_t )data, //任务通知值 (eNotifyAction )eSetValueWithOverwrite); //覆写的方式发送任务通知
13-3-2、通知值发送(设置通知值,可做标记位组)
#define EVENTBIT_1 (1<<1) xTaskNotify((TaskHandle_t )Task_Handler, //接收任务通知的任务句柄 (uint32_t )EVENTBIT_1, //要更新的bit (eNotifyAction )eSetBits); //更新指定的bit
13-4、通知值获取(获取通知值,并判断是否需要清0)
BaseType_t err; uint32_t NotifyValue; err=xTaskNotifyWait((uint32_t )0x00, //进入函数,没有接受到通知,不清除任何bit (uint32_t )ULONG_MAX, //退出函数,接受到通知,清除所有(0xffffffffUL)位的bit, (uint32_t* )&NotifyValue, //保存任务通知值 (TickType_t )portMAX_DELAY); //阻塞时间
================================================ 为了好与UCOS的文章对比,这些内容也放下面================================================
1、中断
1-0、相关配置
#ifdef __NVIC_PRIO_BITS #define configPRIO_BITS __NVIC_PRIO_BITS //STM32库自带 #else #define configPRIO_BITS 4 //STM32提供4Bit的中断优先级 #endif #define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15 //中断最低优先级 #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5 //系统可管理的最高中断优先级 #define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) //内核中断优先级,用来配置上下文切换、时钟节拍优先级,因为STM32优先级寄存器用高4位,所以要左移 #define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) //用来屏蔽中断的阈值,左移原因同上 #define xPortPendSVHandler PendSV_Handler #define vPortSVCHandler SVC_Handler
1-1、中断处理
无需特殊处理
2、临界段处理
2-1、任务内临界段处理
taskEXIT_CRITICAL(); //任务处理 taskEXIT_CRITICAL();
2-2、中断内临界段处理
taskENTER_CRITICAL_FROM_ISR(); //中断内处理 taskEXIT_CRITICAL_FROM_ISR();
3、引起调度函数
3-1、延时
vTaskDelay(1000); //延时1000个时钟节拍,也就是1s