FreeRTOS使用示例
任务创建与删除
#define START_TASK_PRIO 1 //任务优先级 (1)
#define START_STK_SIZE 128 //任务堆栈大小 (2)
TaskHandle_t StartTask_Handler; //任务句柄 (3)
void start_task(void *pvParameters);//任务函数 (4)
#define TASK1_TASK_PRIO 2 //任务优先级
#define TASK1_STK_SIZE 128 //任务堆栈大小
TaskHandle_t Task1Task_Handler; //任务句柄
void task1_task(void *pvParameters);//任务函数
#define TASK2_TASK_PRIO 3 //任务优先级
#define TASK2_STK_SIZE 128 //任务堆栈大小
TaskHandle_t Task2Task_Handler; //任务句柄
void task2_task(void *pvParameters);//任务函数
int main(void)
{
//创建开始任务
xTaskCreate((TaskFunction_t )start_task, //任务函数 (1)
(const char* )"start_task", //任务名称
(uint16_t )START_STK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )START_TASK_PRIO,//任务优先级
(TaskHandle_t* )&StartTask_Handler);//任务句柄
vTaskStartScheduler(); //开启任务调度 (2)
}
//开始任务任务函数
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
//创建 TASK1 任务
xTaskCreate((TaskFunction_t )task1_task,
(const char* )"task1_task",
(uint16_t )TASK1_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK1_TASK_PRIO,
(TaskHandle_t* )&Task1Task_Handler);
//创建 TASK2 任务
xTaskCreate((TaskFunction_t )task2_task,
(const char* )"task2_task",
(uint16_t )TASK2_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK2_TASK_PRIO,
(TaskHandle_t* )&Task2Task_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务 (2)
taskEXIT_CRITICAL(); //退出临界区
}
//task1 任务函数
void task1_task(void *pvParameters)
{
u8 task1_num=0;
while(1){
task1_num++; //任务执 1 行次数加 1 注意 task1_num1 加到 255 的时候会清零!!
if(task1_num==5) {
vTaskDelete(Task2Task_Handler);//任务 1 执行 5 次删除任务 2
}
vTaskDelay(1000); //延时 1s,也就是 1000 个时钟节拍
}
}
//task2 任务函数
void task2_task(void *pvParameters)
{
u8 task2_num=0;
while(1){
task2_num++; //任务 2 执行次数加 1 注意 task1_num2 加到 255 的时候会清零!!
vTaskDelay(1000); //延时 1s,也就是 1000 个时钟节拍
}
}
任务挂起与恢复
#define START_TASK_PRIO 1 //任务优先级
#define START_STK_SIZE 128 //任务堆栈大小
TaskHandle_t StartTask_Handler; //任务句柄
void start_task(void *pvParameters); //任务函数
#define KEY_TASK_PRIO 2 //任务优先级
#define KEY_STK_SIZE 128 //任务堆栈大小
TaskHandle_t KeyTask_Handler; //任务句柄
void key_task(void *pvParameters); //任务函数
#define TASK1_TASK_PRIO 3 //任务优先级
#define TASK1_STK_SIZE 128 //任务堆栈大小
TaskHandle_t Task1Task_Handler; //任务句柄
void task1_task(void *pvParameters); //任务函数
#define TASK2_TASK_PRIO 4 //任务优先级
#define TASK2_STK_SIZE 128 //任务堆栈大小
TaskHandle_t Task2Task_Handler; //任务句柄
void task2_task(void *pvParameters);//任务函数
int main(void)
{
//创建开始任务
xTaskCreate((TaskFunction_t )start_task, //任务函数 (1)
(const char* )"start_task", //任务名称
(uint16_t )START_STK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )START_TASK_PRIO,//任务优先级
(TaskHandle_t* )&StartTask_Handler);//任务句柄
vTaskStartScheduler(); //开启任务调度 (2)
}
//开始任务任务函数
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
//创建 KEY 任务
xTaskCreate((TaskFunction_t )key_task,
(const char* )"key_task",
(uint16_t )KEY_STK_SIZE,
(void* )NULL,
(UBaseType_t )KEY_TASK_PRIO,
(TaskHandle_t* )&KeyTask_Handler);
//创建 TASK1 任务
xTaskCreate((TaskFunction_t )task1_task,
(const char* )"task1_task",
(uint16_t )TASK1_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK1_TASK_PRIO,
(TaskHandle_t* )&Task1Task_Handler);
//创建 TASK2 任务
xTaskCreate((TaskFunction_t )task2_task,
(const char* )"task2_task",
(uint16_t )TASK2_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK2_TASK_PRIO,
(TaskHandle_t* )&Task2Task_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
void key_task(void *pvParameters)
{
u8 key;
while(1){
key=KEY_Scan(0);
switch(key){
case WKUP_PRES:
vTaskSuspend(Task1Task_Handler);//挂起任务 1
break;
case KEY1_PRES:
vTaskResume(Task1Task_Handler);//恢复任务 1
break;
case KEY2_PRES:
vTaskSuspend(Task2Task_Handler);//挂起任务 2
break;
}
vTaskDelay(10); //延时 10ms
}
}
//task1 任务函数
void task1_task(void *pvParameters)
{
u8 task1_num=0;
while(1){
task1_num++; //任务执 1 行次数加 1 注意 task1_num1 加到 255 的时候会清零!!
printf("任务 1 已经执行:%d 次\r\n",task1_num);
vTaskDelay(1000); //延时 1s,也就是 1000 个时钟节拍
}
}
//task2 任务函数
void task2_task(void *pvParameters)
{
u8 task2_num=0;
while(1){
task2_num++; //任务 2 执行次数加 1 注意 task1_num2 加到 255 的时候会清零!!
printf("任务 2 已经执行:%d 次\r\n",task2_num);
vTaskDelay(1000); //延时 1s,也就是 1000 个时钟节拍
}
}
消息队列
- 应用:类似二值或者计数型信号量(两种也是队列实现)。即一个任务或者中断连续往一个队列发送5包数据;另一个任务就可以延后处理数据按5次调用取出数据来用。
#define START_TASK_PRIO 1 //任务优先级
#define START_STK_SIZE 256 //任务堆栈大小
TaskHandle_t StartTask_Handler; //任务句柄
void start_task(void *pvParameters); //任务函数
#define TASK1_TASK_PRIO 2 //任务优先级
#define TASK1_STK_SIZE 256 //任务堆栈大小
TaskHandle_t Task1Task_Handler; //任务句柄
void task1_task(void *pvParameters);//任务函数
#define KEYPROCESS_TASK_PRIO 3 //任务优先级
#define KEYPROCESS_STK_SIZE 256 //任务堆栈大小
TaskHandle_t Keyprocess_Handler; //任务句柄
void Keyprocess_task(void *pvParameters); //任务函数
//按键消息队列的数量
#define KEYMSG_Q_NUM 1 //按键消息队列的数量 (1)
#define MESSAGE_Q_NUM 4 //发送数据的消息队列的数量 (2)
QueueHandle_t Key_Queue; //按键值消息队列句柄
QueueHandle_t Message_Queue; //信息队列句柄
int main(void)
{
//创建开始任务
}
//开始任务任务函数
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
//创建消息 Key_Queue
Key_Queue=xQueueCreate(KEYMSG_Q_NUM,sizeof(u8)); (1)
//创建消息 Message_Queue,队列项长度是串口接收缓冲区长度
Message_Queue=xQueueCreate(MESSAGE_Q_NUM,USART_REC_LEN); (2)
//创建 TASK1 任务
xTaskCreate((TaskFunction_t )task1_task,
(const char* )"task1_task",
(uint16_t )TASK1_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK1_TASK_PRIO,
(TaskHandle_t* )&Task1Task_Handler);
//创建 TASK2 任务
xTaskCreate((TaskFunction_t )Keyprocess_task,
(const char* )"keyprocess_task",
(uint16_t )KEYPROCESS_STK_SIZE,
(void* )NULL,
(UBaseType_t )KEYPROCESS_TASK_PRIO,
(TaskHandle_t* )&Keyprocess_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
void task1_task(void *pvParameters)
{
BaseType_t err;
while(1)
{
u8 key=KEY_Scan(0);
if((Key_Queue!=0)&&(key)){ //消息队列 Key_Queue 创建成功,并且按键被按下
err=xQueueSend(Key_Queue,&key,10);
if(err==errQUEUE_FULL) {
printf("队列 Key_Queue 已满,数据发送失败!\r\n");
}
}
vTaskDelay(10); //延时 10ms,也就是 10 个时钟节拍
}
}
//Keyprocess_task 函数
void Keyprocess_task(void *pvParameters)
{
u8 num,key,beepsta=1;
while(1)
{
if(Key_Queue!=0){
if(xQueueReceive(Key_Queue,&key,portMAX_DELAY)){//请求消息 Key_Queue
switch(key){
case WKUP_PRES: //KEY_UP 控制 LED1
break;
case KEY2_PRES: //KEY2 控制蜂鸣器
break;
case KEY0_PRES: //KEY0 刷新 LCD 背景
break;
}
}
}
vTaskDelay(10); //延时 10ms,也就是 10 个时钟节拍
}
}
//串口 1 中断服务程序
void USART1_IRQHandler(void)
{
......
if(USART_RX_STA&0x8000) {//向队列发送接收到的数据
//向队列中发送数据
xQueueSendFromISR(Message_Queue,USART_RX_BUF,&xHigherPriorityTaskWoken);
//如果需要的话进行一次任务切换
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
//回调函数,定时器中断服务函数调用
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
u8 *buffer;
BaseType_t xTaskWokenByReceive=pdFALSE;
BaseType_t err;
if(Message_Queue!=0){
.......
//请求消息 Message_Queue
err=xQueueReceiveFromISR(Message_Queue,buffer,&xTaskWokenByReceive);
if(err==pdTRUE){ //接收到消息
disp_str(buffer); //在 LCD 上显示接收到的消息 (6)
}
}
//如果需要的话进行一次任务切换
portYIELD_FROM_ISR(xTaskWokenByReceive);
}
二值信号量
- 用途:【1】共享资源访问;【2】用于任务同步
//二值信号量句柄
SemaphoreHandle_t BinarySemaphore;//二值信号量句柄
int main(void)
{
//创建开始任务
}
//开始任务任务函数
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
//创建二值信号量
BinarySemaphore=xSemaphoreCreateBinary();
//创建 TASK1 任务
xTaskCreate((TaskFunction_t )task1_task,
(const char* )"task1_task",
(uint16_t )TASK1_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK1_TASK_PRIO,
(TaskHandle_t* )&Task1Task_Handler);
//创建 TASK2 任务
xTaskCreate((TaskFunction_t )DataProcess_task,
(const char* )"keyprocess_task",
(uint16_t )DATAPROCESS_STK_SIZE,
(void* )NULL,
(UBaseType_t )DATAPROCESS_TASK_PRIO,
(TaskHandle_t* )&DataProcess_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
void task1_task(void *pvParameters)
{
BaseType_t err;
while(1)
{
vTaskDelay(500); //延时 500ms,也就是 500 个时钟节拍
}
}
//DataProcess_task 函数
void DataProcess_task(void *pvParameters)
{
while(1)
{
if(BinarySemaphore!=NULL){
err=xSemaphoreTake(BinarySemaphore,portMAX_DELAY);//获取信号量 (1)
if(err==pdTRUE){ //获取信号量成功
//处理数据
}else if(err==pdFALSE){
vTaskDelay(10); //延时 10ms,也就是 10 个时钟节拍
}
}
}
}
//串口 1 中断服务程序
void USART1_IRQHandler(void)
{
BaseType_t xHigherPriorityTaskWoken;
//接收到数据,并且二值信号量有效
if((USART_RX_STA&0x8000)&&(BinarySemaphore!=NULL)){
xSemaphoreGiveFromISR(BinarySemaphore,&xHigherPriorityTaskWoken);//释放二值信号量
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);//如果需要的话进行一次任务切换
}
}
计数型信号量
- 应用:类似按键连续按下5次,其它任务同步延后按5次逐个完成对应的工作(而不是指响应最后一次按下,每次按下都响应,只是响应时间往后延时完成)
//计数型信号量句柄
SemaphoreHandle_t CountSemaphore;//计数型信号量
int main(void)
{
//创建开始任务
}
//开始任务任务函数
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
//创建计数型信号量,计数型信号量计数最大值设置为255,初始值设置为0。
CountSemaphore=xSemaphoreCreateCounting(255,0);
//创建释放信号量任务
xTaskCreate((TaskFunction_t )SemapGive_task,
(const char* )"semapgive_task",
(uint16_t )SEMAPGIVE_STK_SIZE,
(void* )NULL,
(UBaseType_t )SEMAPGIVE_TASK_PRIO,
(TaskHandle_t* )&SemapGiveTask_Handler);
//创建获取信号量任务
xTaskCreate((TaskFunction_t )SemapTake_task,
(const char* )"semaptake_task",
(uint16_t )SEMAPTAKE_STK_SIZE,
(void* )NULL,
(UBaseType_t )SEMAPTAKE_TASK_PRIO,
(TaskHandle_t* )&SemapTakeTask_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
//释放计数型信号量任务函数
void SemapGive_task(void *pvParameters)
{
u8 semavalue;
BaseType_t err;
while(1){
key=KEY_Scan(0);
if(CountSemaphore!=NULL) {//计数型信号量创建成功
switch(key){
case WKUP_PRES:
err=xSemaphoreGive(CountSemaphore);//释放计数型信号量
if(err==pdFALSE){//信号量释放失败
}
semavalue=uxSemaphoreGetCount(CountSemaphore);//获取计数型信号量值
break;
}
}
vTaskDelay(10);//延时 10ms,也就是 10 个时钟节拍
}
}
//获取计数型信号量任务函数
void SemapTake_task(void *pvParameters)
{
u8 semavalue;
while(1){
xSemaphoreTake(CountSemaphore,portMAX_DELAY); //等待数值信号量 (5)
semavalue=uxSemaphoreGetCount(CountSemaphore); //获取数值信号量值 (6)
//打印或者显示信号量的值semavalue
vTaskDelay(1000); //延时 1s,也就是 1000 个时钟节拍
}
}
互斥信号量
- 当一个低优先级任务和一个高优先级任务同时使用同一个信号量,且低优先级的任务延时阻塞比较久才释放信号量!
- 而系统中还有其他中等优先级任务时。如果低优先级任务获得了信号量,那么高优先级的任务就会处于等待状态。
- 但是,中等优先级的任务可以打断低优先级任务而先于高优先级任务运行(此时高优先级的任务在等待信号量 ,所以不能运行),这是就出现了优先级翻转的现象。
SemaphoreHandle_t MutexSemaphore;//创建互斥信号量
int main(void)
{
//创建开始任务
}
//开始任务任务函数
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
//创建互斥信号量
MutexSemaphore=xSemaphoreCreateMutex(); (1)
//创建高优先级任务
xTaskCreate((TaskFunction_t )high_task,
(const char* )"high_task",
(uint16_t )HIGH_STK_SIZE,
(void* )NULL,
(UBaseType_t )HIGH_TASK_PRIO,
(TaskHandle_t* )&HighTask_Handler);
//创建中等优先级任务
xTaskCreate((TaskFunction_t )middle_task,
(const char* )"middle_task",
(uint16_t )MIDDLE_STK_SIZE,
(void* )NULL,
(UBaseType_t )MIDDLE_TASK_PRIO,
(TaskHandle_t* )&MiddleTask_Handler);
//创建低优先级任务
xTaskCreate((TaskFunction_t )low_task,
(const char* )"low_task",
(uint16_t )LOW_STK_SIZE,
(void* )NULL,
(UBaseType_t )LOW_TASK_PRIO,
(TaskHandle_t* )&LowTask_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
//高优先级任务的任务函数
void high_task(void *pvParameters)
{
while(1){
vTaskDelay(500); //延时 500ms,也就是 500 个时钟节拍
xSemaphoreTake(MutexSemaphore,portMAX_DELAY); //获取互斥信号量
printf("high task Running!\r\n");
xSemaphoreGive(MutexSemaphore); //释放信号量
vTaskDelay(500); //延时 500ms,也就是 500 个时钟节拍
}
}
//中等优先级任务的任务函数
void middle_task(void *pvParameters)
{
while(1){
printf("middle task Running!\r\n");
vTaskDelay(1000); //延时 1s,也就是 1000 个时钟节拍
}
}
//低优先级任务的任务函数
void low_task(void *pvParameters)
{
static u32 times;
while(1){
xSemaphoreTake(MutexSemaphore,portMAX_DELAY); //获取互斥信号量
printf("low task Running!\r\n");
for(times=0;times<20000000;times++){ //模拟低优先级任务占用互斥信号量
taskYIELD(); //发起任务调度
}
xSemaphoreGive(MutexSemaphore); //释放互斥信号量
vTaskDelay(1000); //延时 1s,也就是 1000 个时钟节拍
}
}
事件标志组
- 类似裸跑时一个全局flag变量,对应的一个或者多个bit被置起来,就同步做另一个事情
- 事件标志组是在不同任务间置起标志位(发送),由其中一个任务判断哪些标志位被置起来而同步做某个事件。
EventGroupHandle_t EventGroupHandler; //事件标志组句柄
#define EVENTBIT_0 (1<<0) //事件位
#define EVENTBIT_1 (1<<1)
#define EVENTBIT_2 (1<<2)
#define EVENTBIT_ALL (EVENTBIT_0|EVENTBIT_1|EVENTBIT_2)
int main(void)
{
//创建开始任务
}
//开始任务任务函数
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
//创建事件标志组
EventGroupHandler=xEventGroupCreate(); //创建事件标志组 (1)
//创建设置事件位的任务
xTaskCreate((TaskFunction_t )eventsetbit_task,
(const char* )"eventsetbit_task",
(uint16_t )EVENTSETBIT_STK_SIZE,
(void* )NULL,
(UBaseType_t )EVENTSETBIT_TASK_PRIO,
(TaskHandle_t* )&EventSetBit_Handler);
//创建事件标志组处理任务
xTaskCreate((TaskFunction_t )eventgroup_task,
(const char* )"eventgroup_task",
(uint16_t )EVENTGROUP_STK_SIZE,
(void* )NULL,
(UBaseType_t )EVENTGROUP_TASK_PRIO,
(TaskHandle_t* )&EventGroupTask_Handler);
//创建事件标志组查询任务
xTaskCreate((TaskFunction_t )eventquery_task,
(const char* )"eventquery_task",
(uint16_t )EVENTQUERY_STK_SIZE,
(void* )NULL,
(UBaseType_t )EVENTQUERY_TASK_PRIO,
(TaskHandle_t* )&EventQueryTask_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
//设置事件位的任务
void eventsetbit_task(void *pvParameters)
{
u8 key;
while(1){
if(EventGroupHandler!=NULL){
key=KEY_Scan(0);
switch(key){
case KEY1_PRES:
xEventGroupSetBits(EventGroupHandler,EVENTBIT_1);
break;
case KEY2_PRES:
xEventGroupSetBits(EventGroupHandler,EVENTBIT_2);
break;
}
}
vTaskDelay(10); //延时 10ms,也就是 10 个时钟节拍
}
}
//事件标志组处理任务
void eventgroup_task(void *pvParameters)
{
EventBits_t EventValue;
while(1){
if(EventGroupHandler!=NULL){
//等待事件组中的相应事件位
EventValue=xEventGroupWaitBits((EventGroupHandle_t )EventGroupHandler, (4)
(EventBits_t ) EVENTBIT_ALL,
(BaseType_t )pdTRUE,
(BaseType_t )pdTRUE,
(TickType_t )portMAX_DELAY);
printf("事件标志组的值:%d\r\n",EventValue);
}else{
vTaskDelay(10); //延时 10ms,也就是 10 个时钟节拍
}
}
}
//事件查询任务
void eventquery_task(void *pvParameters)
{
EventBits_t NewValue,LastValue;
while(1){
if(EventGroupHandler!=NULL){
NewValue=xEventGroupGetBits(EventGroupHandler); //获取事件组的 (5)
if(NewValue!=LastValue){
LastValue=NewValue;
printf("事件标志组的值:%d\r\n",NewValue);
}
}
vTaskDelay(50); //延时 50ms,也就是 50 个时钟节拍
}
}
//中断服务函数
void EXTI3_IRQHandler(void)
{
BaseType_t Result,xHigherPriorityTaskWoken;
delay_xms(50); //消抖
if(KEY0==0){
Result=xEventGroupSetBitsFromISR(EventGroupHandler,EVENTBIT_0,\ (1)
&xHigherPriorityTaskWoken);
if(Result!=pdFAIL){
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_3); //清除中断标志位
}
任务通知模拟二值信号量
//DataProcess_task 函数
void DataProcess_task(void *pvParameters)
{
u32 NotifyValue;
while(1){
//第一个参数设置为 pdTRUE。模拟二值信号量,所以需要在获取任务通知以后将任务通知值清零
NotifyValue=ulTaskNotifyTake(pdTRUE,portMAX_DELAY); //获取任务通知 (1)
if(NotifyValue==1){ //清零之前的任务通知值为 1,说明任务通知有效 (2)
//处理串口中断发送过来的数据,调用全局buffer
}else {
vTaskDelay(10); //延时 10ms,也就是 10 个时钟节拍
}
}
}
extern TaskHandle_t DataProcess_Handler;; //接收任务通知的任务句柄
//串口 1 中断服务程序
void USART1_IRQHandler(void)
{
BaseType_t xHigherPriorityTaskWoken;
......//接收数据处理
//接收到数据,并且接收任务通知的任务有效
if((USART_RX_STA&0x8000)&&(DataProcess_Handler!=NULL)){
vTaskNotifyGiveFromISR(DataProcess_Handler,&xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);//如果需要的话进行一次任务切换
}
}
任务通知模拟计数型信号量
//释放计数型信号量任务函数
void SemapGive_task(void *pvParameters)
{
while(1){
if(SemapTakeTask_Handler!=NULL) {
switch(KEY_Scan(0)){
case WKUP_PRES:
xTaskNotifyGive(SemapTakeTask_Handler);//发送任务通知
break;
}
}
vTaskDelay(10); //延时10ms,也就是 10 个时钟节拍
}
}
//获取计数型信号量任务函数
void SemapTake_task(void *pvParameters)
{
uint32_t NotifyValue;
while(1){
NotifyValue=ulTaskNotifyTake(pdFALSE,portMAX_DELAY);//获取任务通知
//NotifyValue 要减一才是当前的任务通知值
LCD_ShowxNum(166,111,NotifyValue-1,3,16,0); //显示当前任务通知值
vTaskDelay(1000); //延时 1s,也就是 1000 个时钟节拍
}
}
任务通知模拟消息邮箱
int main(void)
{
//创建开始任务
}
//开始任务任务函数
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
//创建 TASK1 任务
xTaskCreate((TaskFunction_t )task1_task,
(const char* )"task1_task",
(uint16_t )TASK1_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK1_TASK_PRIO,
(TaskHandle_t* )&Task1Task_Handler);
//创建按键处理任务
xTaskCreate((TaskFunction_t )Keyprocess_task,
(const char* )"keyprocess_task",
(uint16_t )KEYPROCESS_STK_SIZE,
(void* )NULL,
(UBaseType_t )KEYPROCESS_TASK_PRIO,
(TaskHandle_t* )&Keyprocess_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
//task1 任务函数
void task1_task(void *pvParameters)
{
u8 key,i=0;
BaseType_t err;
while(1){
key=KEY_Scan(0); //扫描按键
if((Keyprocess_Handler!=NULL)&&(key)) {
err=xTaskNotify((TaskHandle_t )Keyprocess_Handler, //任务句柄
(uint32_t )key, //任务通知值
(eNotifyAction )eSetValueWithOverwrite); //覆写的方式
if(err==pdFAIL){
printf("任务通知发送失败\r\n");
}
}
vTaskDelay(10); //延时 10ms,也就是 10 个时钟节拍
}
}
//Keyprocess_task 函数
void Keyprocess_task(void *pvParameters)
{
uint32_t NotifyValue;
BaseType_t err;
while(1){
err=xTaskNotifyWait((uint32_t )0x00, //进入函数的时候不清除任务 bit (2)
(uint32_t )ULONG_MAX,//退出函数的时候清除所有的 bit
(uint32_t* )&NotifyValue, //保存任务通知值
(TickType_t )portMAX_DELAY); //阻塞时间
if(err==pdTRUE){ //获取任务通知成功
switch((u8)NotifyValue){
case WKUP_PRES: //KEY_UP 控制 LED1
break;
case KEY2_PRES: //KEY2 控制蜂鸣器
break;
case KEY0_PRES: //KEY0 刷新 LCD 背景
break;
}
}
}
}
任务通知模拟事件标志组
int main(void)
{
//创建开始任务
}
//开始任务任务函数
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
//创建设置事件位的任务
xTaskCreate((TaskFunction_t )eventsetbit_task,
(const char* )"eventsetbit_task",
(uint16_t )EVENTSETBIT_STK_SIZE,
(void* )NULL,
(UBaseType_t )EVENTSETBIT_TASK_PRIO,
(TaskHandle_t* )&EventSetBit_Handler);
//创建事件标志组处理任务
xTaskCreate((TaskFunction_t )eventgroup_task,
(const char* )"eventgroup_task",
(uint16_t )EVENTGROUP_STK_SIZE,
(void* )NULL,
(UBaseType_t )EVENTGROUP_TASK_PRIO,
(TaskHandle_t* )&EventGroupTask_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
//设置事件位的任务
void eventsetbit_task(void *pvParameters)
{
while(1){
if(EventGroupTask_Handler!=NULL){
switch(KEY_Scan(0)){
case KEY1_PRES:
xTaskNotify((TaskHandle_t )EventGroupTask_Handler,
(uint32_t )EVENTBIT_1,
(eNotifyAction )eSetBits);
break;
case KEY2_PRES:
xTaskNotify((TaskHandle_t )EventGroupTask_Handler,
(uint32_t )EVENTBIT_2,
(eNotifyAction )eSetBits);
break;
}
}
vTaskDelay(10); //延时 10ms,也就是 10 个时钟节拍
}
}
//事件标志组处理任务
void eventgroup_task(void *pvParameters)
{
u8 num=0,enevtvalue;
static u8 event0flag,event1flag,event2flag;
uint32_t NotifyValue;
BaseType_t err;
while(1){
err=xTaskNotifyWait((uint32_t )0x00, // 获取任务通知值,进入函数的时候不清除任务 bit (3)
(uint32_t )ULONG_MAX, //退出函数的时候清除所有的 bit
(uint32_t* )&NotifyValue, //保存任务通知值
(TickType_t )portMAX_DELAY);//阻塞时间
if(err==pdPASS){ //任务通知获取成功
if((NotifyValue&EVENTBIT_0)!=0){//事件 0 发生 (4)
event0flag=1;
}else if((NotifyValue&EVENTBIT_1)!=0){//事件 1 发生
event1flag=1;
}else if((NotifyValue&EVENTBIT_2)!=0){//事件 2 发生
event2flag=1;
}
enevtvalue=event0flag|(event1flag<<1)|(event2flag<<2); //模拟事件标志组值(5)
printf("任务通知值为:%d\r\n",enevtvalue);
if((event0flag==1)&&(event1flag==1)&&(event2flag==1)){//三个事件都同时发生
//处理同时发生的同步事件
event0flag=0; //标志清零
event1flag=0;
event2flag=0;
}
}else{
printf("任务通知获取失败\r\n");
}
}
}
//事件标志组句柄
extern TaskHandle_t EventGroupTask_Handler;
//中断服务函数
void EXTI3_IRQHandler(void)
{
BaseType_t xHigherPriorityTaskWoken;
delay_xms(50); //消抖
if(KEY0==0){
xTaskNotifyFromISR((TaskHandle_t )EventGroupTask_Handler, //任务句柄
(uint32_t )EVENTBIT_0, //要更新的 bit
(eNotifyAction )eSetBits, //更新指定的 bit
(BaseType_t* )xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_3); //清除中断标志位
}
UCOS使用示例
信号量使用
信号量访问共享资源区/
OS_SEMMY_SEM; //定义一个信号量,用于访问共享资源
OSSemCreate ((OS_SEM* )&MY_SEM, //创建信号量,指向信号量
(CPU_CHAR* )"MY_SEM", //信号量名字
(OS_SEM_CTR )1, //信号量值为1,可以理解开始有个任务就可以请求到信号量
(OS_ERR* )&err);//错误码
void task1_task(void *p_arg)
{
OS_ERR err;
u8 task1_str[]="First task Running!";
//请求信号量,参数2:0为死等;参数3:表示信号量无效任务挂起等待信号量;参数4:时间戳
OSSemPend(&MY_SEM,0,OS_OPT_PEND_BLOCKING,0,&err);
memcpy(share_resource,task1_str,sizeof(task1_str));//向共享资源区拷贝数据
delay_ms(200);
printf("%s\r\n",share_resource); //串口输出共享资源区数据
OSSemPost(&MY_SEM,OS_OPT_POST_1,&err); //发送信号量
}
void task2_task(void *p_arg)
{
OS_ERR err;
u8 task2_str[]="Second task Running!";
OSSemPend(&MY_SEM,0,OS_OPT_PEND_BLOCKING,0,&err); //请求信号量(3)
memcpy(share_resource,task2_str,sizeof(task2_str));//向共享资源区拷贝数据
delay_ms(200);
printf("%s\r\n",share_resource); //串口输出共享资源区数据
//OS_OPT_POST_1表示向信号量优先级高的任务发送信号量
OSSemPost(&MY_SEM,OS_OPT_POST_1,&err);//发送信号量
}
/信号量用于任务同步实验
OS_SEM SYNC_SEM; //定义一个信号量,用于任务同步
OSSemCreate ((OS_SEM* )&SYNC_SEM,//创建信号量,指向信号量
(CPU_CHAR* )"SYNC_SEM",//信号量名字
(OS_SEM_CTR )0, //信号量值为0
(OS_ERR* )&err); //错误码
void task1_task(void *p_arg)
{
OS_ERR err;
if(KEY_Scan(0)==WKUP_PRES){
OSSemPost(&SYNC_SEM,OS_OPT_POST_1,&err);//发送信号量
LCD_ShowxNum(150,111,SYNC_SEM.Ctr,3,16,0); //显示信号量值
}
}
void task2_task(void *p_arg)
{
OS_ERR err;
//OS_OPT_PEND_BLOCKING:表示信号量无效任务挂起等待信号量
//如是OS_OPT_POST_ALL 向等待该信号量的所有任务发送信号量。
OSSemPend(&SYNC_SEM,0,OS_OPT_PEND_BLOCKING,0,&err); //请求信号量
LCD_ShowxNum(150,111,SYNC_SEM.Ctr,3,16,0); //显示信号量值
OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err);//延时1s
}
内建信号量
//内建信号量///
void task1_task(void *p_arg)
{
OS_ERR err;
if(KEY_Scan(0) == WKUP_PRES){
//OS_OPT_POST_NONE:不指定特定的选项
OSTaskSemPost(&Task2_TaskTCB,OS_OPT_POST_NONE,&err);//使用系统内建信号量向任务task2发送信号量
LCD_ShowxNum(150,111,Task2_TaskTCB.SemCtr,3,16,0); //显示信号量值
}
}
void task2_task(void *p_arg)
{
OS_ERR err;
//参数1:超时时间,0为一直等待信号量;2:信号量被占用则挂起等待;3:时间戳
OSTaskSemPend(0,OS_OPT_PEND_BLOCKING, 0,&err);//请求任务内建的信号量
LCD_ShowxNum(150,111,Task2_TaskTCB.SemCtr,3,16,0);//显示任务内建信号量值
}
消息传递
消息队列相关函数
消息队列//
#define KEYMSG_Q_NUM 1 //按键消息队列的数量
#define DATAMSG_Q_NUM 4 //发送数据的消息队列的数量
OS_Q KEY_Msg; //定义一个消息队列,用于按键消息传递,模拟消息邮箱
OS_Q DATA_Msg; //定义一个消息队列,用于发送数据
void start_task(void *p_arg)
{//......
OSQCreate ( (OS_Q* )&KEY_Msg,//指向一个消息队列
(CPU_CHAR* )"KEY Msg",//消息队列名称
(OS_MSG_QTY )KEYMSG_Q_NUM, //消息队列长度,这里设置为 1
(OS_ERR* )&err); //错误码
//创建消息队列 DATA_Msg
OSQCreate ( (OS_Q* )&DATA_Msg, //指向一个消息队列
(CPU_CHAR* )"DATA Msg",//消息队列的名称
(OS_MSG_QTY )DATAMSG_Q_NUM,//消息队列的个数这里是4
(OS_ERR* )&err);//错误码
}
void tmr1_callback(void *p_tmr,void *p_arg)
{//......
sprintf((char*)pbuf,"ALIENTEK %d",msg_num);
//发送消息
OSQPost((OS_Q* )&DATA_Msg,//指向一个消息队列
(void* )pbuf, //指向要发送的内容void指针
(OS_MSG_SIZE )10, //要发送的消息大小,单位字节
(OS_OPT )OS_OPT_POST_FIFO,//发送消息操作类型,这里表示发送消息报错队列尾部
(OS_ERR* )&err); //错误码
}
void main_task(void *p_arg)
{
u8 key = KEY_Scan(0); //扫描按键
//发送消息
OSQPost((OS_Q* )&KEY_Msg,
(void* )&key,
(OS_MSG_SIZE )1,
(OS_OPT )OS_OPT_POST_FIFO,
(OS_ERR* &err);
u8 msgq_remain_size = DATA_Msg.MsgQ.NbrEntriesSize-DATA_Msg.MsgQ.NbrEntries;//消息队列剩余大小
sprintf((char*)p,"Total Size:%d",DATA_Msg.MsgQ.NbrEntriesSize);//显示 DATA_Msg 消息队列总的大小
}
void Keyprocess_task(void *p_arg)
{
u8 num;
u8 *key;
OS_MSG_SIZE size;
OS_ERR err;
key=OSQPend((OS_Q* )&KEY_Msg, //指向一个消息队列,整个函数反回的是指针消息数据
(OS_TICK )0, //指定时间没有接收到数据任务就被唤醒,0一直等
(OS_OPT )OS_OPT_PEND_BLOCKING,//一直等,直到接收到消息
(OS_MSG_SIZE* )&size,//接收消息的字节长度
(CPU_TS* )0,//指向一个时间戳
(OS_ERR* )&err);//错误码
}
void msgdis_task(void *p_arg)
{
u8 *p;
OS_MSG_SIZE size;
OS_ERR err;
p=OSQPend( (OS_Q* )&DATA_Msg,
(OS_TICK )0,
(OS_OPT )OS_OPT_PEND_BLOCKING,
(OS_MSG_SIZE* )&size;
(CPU_TS* )0,
(OS_ERR* )&err);
LCD_ShowString(5,270,100,16,16,p);
}
任务内建消息队列
#define TASK_Q_NUM 4 //任务内建消息队列的长度
void tmr1_callback(void *p_tmr,void *p_arg)
{//......
sprintf((char*)pbuf,"ALIENTEK %d",msg_num);
OSTaskQPost((OS_TCB* )&Msgdis_TaskTCB, //向任务msgdis_task发送消息
(void* )pbuf, //指向要发送的内容void指针
(OS_MSG_SIZE )10, //指定要发送消息的大小
(OS_OPT )OS_OPT_POST_FIFO,//发送消息报错在队列末尾
(OS_ERR* )&err);//错误码
}
void msgdis_task(void *p_arg)
{//......
u8 *p;
OS_MSG_SIZE size;
OS_ERR err;
p=OSTaskQPend((OS_TICK )0, //超时时间没有接收到数据任务就被唤醒
(OS_OPT )OS_OPT_PEND_BLOCKING, //一直等待,直到接收到消息
(OS_MSG_SIZE* )&size, //消息的大小
(CPU_TS* )0, //时间戳
(OS_ERR* )&err ); //错误码
LCD_ShowString(40,270,100,16,16,p);//P为接收到的数据指针
}
事件标志组
事件标志组//
#define KEY0_FLAG 0x01
#define KEY1_FLAG 0x02
#define KEYFLAGS_VALUE 0X00
OS_FLAG_GRP EventFlags; //定义一个事件标志组
void start_task(void *p_arg)
{//......
OSFlagCreate((OS_FLAG_GRP* )&EventFlags, //指向事件标志组
(CPU_CHAR* )"Event Flags", //名字
(OS_FLAGS )KEYFLAGS_VALUE, //事件标志组初始值
(OS_ERR* )&err); //错误码
}
//向事件标志组 EventFlags 发送标志
void main_task(void *p_arg)
{//......
//按下按键1发送
flags_num=OSFlagPost((OS_FLAG_GRP*)&EventFlags,//指向事件标志组
(OS_FLAGS )KEY0_FLAG,//决定哪些位清零和置位
(OS_OPT )OS_OPT_POST_FLAG_SET,//对位进行置位操作,也可清零
(OS_ERR* )&err);//返回错误码
//按下按键2发送
//向事件标志组 EventFlags 发送标志
flags_num=OSFlagPost((OS_FLAG_GRP*)&EventFlags,
(OS_FLAGS )KEY1_FLAG,
(OS_OPT )OS_OPT_POST_FLAG_SET,
(OS_ERR* )&err);
}
void flagsprocess_task(void *p_arg)
{//......
OS_ERR err;
//等待事件标志组
OSFlagPend((OS_FLAG_GRP* )&EventFlags, //指向事件标准组
(OS_FLAGS )KEY0_FLAG+KEY1_FLAG,//等待 bit0和bit1时,值就为 0X03。
(OS_TICK )0,//等待超时时间,为0则一直等待下去
(OS_OPT )OS_OPT_PEND_FLAG_SET_ALL+\//多种配置模式:当前配置为等待所有位
OS_OPT_PEND_FLAG_CONSUME,//保留事件标志的状态
(CPU_TS* )0,//时间戳
(OS_ERR* )&err);//返回错误码
printf("事件标志组 EventFlags 的值:%d\r\n",EventFlags.Flags);
}