FreeRTOS和UCOS操作系统使用笔记

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);//挂起任务 1break;case KEY1_PRES:vTaskResume(Task1Task_Handler);//恢复任务 1break;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_QueueKey_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_Queueswitch(key){case WKUP_PRES: //KEY_UP 控制 LED1break;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_Queueerr=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 控制 LED1break;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_MsgOSQCreate ( (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);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1475348.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

vue3 学习 之 vue3使用

为什么要学习vue3呢&#xff1f; vue2.0也是现在比较稳定的一个版本&#xff0c;社区还有周边都比较完善&#xff0c;如果不是非必要其实我们不需要着急直接升级到vue3.0; 那为什么还要学习&#xff0c;主要是还是为了了解一下vue3.0相较于2.0的优势和特性&#xff0c;方便之后…

跳转控制语句—break和continue

break语句我本人只在switch语句和循环语句中遇见&#xff0c;continue则只在循环语句中遇见&#xff1b; 下面我来记录一下&#xff0c;它俩的不同之处&#xff1a; 1.break 相比之下&#xff0c;break是比较简单的&#xff0c;就是跳出循环体&#xff0c;执行循环体下方的代…

刷题之删除有序数组中的重复项(leetcode)

删除有序数组中的重复项 这题简单题&#xff0c;双指针&#xff0c;一个指针记录未重复的数的个数&#xff0c;另一个记录遍历的位置。 以下是简单模拟&#xff0c;可以优化&#xff1a; class Solution { public:int removeDuplicates(vector<int>& nums) {int l0…

智慧地产视觉监控系统开源了,系统采用多种优化技术,提高系统的响应速度和资源利用率

智慧地产视觉监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒&#xff0c;省去繁琐重复的适配流程&#xff0c;实现芯片、算法、应用的全流程组合&#xff0c;从而大大减少企业级应用约95%的开发成本。用户只需在界面上…

YOLOv5改进 | 注意力机制 | 结合静态和动态上下文信息的注意力机制【全网独家】

秋招面试专栏推荐 &#xff1a;深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转 &#x1f4a1;&#x1f4a1;&#x1f4a1;本专栏所有程序均经过测试&#xff0c;可成功执行&#x1f4a1;&#x1f4a1;&#x1f4a1; 专栏目录 &#xff1a; 《YOLOv5入门 改…

环信IM实现小米、oppo推送详细步骤

本文教大家集成环信IM后如何实现小米、oppo推送。 一、小米推送 步骤一、在小米开放平台创建应用。 在 小米开放平台 创建应用&#xff0c;开启推送服务。详见小米官方网站的 推送服务接入指南。 步骤二、上传推送证书。 注册完成后&#xff0c;需要在环信即时通讯云控制台…

「Java开发指南」如何用MyEclipse完成Spring Web Flow 2.0搭建?

本教程将引导您完成Spring Web Flow的软件组件生成&#xff0c;这是Spring的一个项目&#xff0c;用于简化Web应用程序的开发。虽然Spring Web Flow与Spring MVC兼容&#xff0c;但Spring Web Flow使用流而不是控制器来实现应用程序的Web层。在本教程中&#xff0c;您将学习如何…

Python前沿技术:机器学习与人工智能

Python前沿技术&#xff1a;机器学习与人工智能 一、引言 随着科技的飞速发展&#xff0c;机器学习和人工智能&#xff08;AI&#xff09;已经成为了计算机科学领域的热门话题。Python作为一门易学易用且功能强大的编程语言&#xff0c;已经成为了这两个领域的首选语言之一。本…

LibreOffice的国内镜像安装地址和node.js国内快速下载网站

文章目录 1、LibreOffice1.1、LibreOffice在application-conf.yml中的配置2、node.js 1、LibreOffice 国内镜像包网址&#xff1a;https://mirrors.cloud.tencent.com/libreoffice/libreoffice/ 1.1、LibreOffice在application-conf.yml中的配置 jodconverter:local:enable…

如何处理 PostgreSQL 中由于索引过多导致的性能下降问题?

文章目录 一、索引过多导致性能下降的原因二、识别过多索引导致的性能问题&#xff08;一&#xff09;监控数据库性能指标&#xff08;二&#xff09;检查索引使用情况&#xff08;三&#xff09;分析查询计划 三、解决方案&#xff08;一&#xff09;删除不必要的索引&#xf…

家里老人能操作的电视直播软件,目前能用的免费看直播的电视软件app,适合电视和手机使用!

2024年许多能看电视直播的软件都不能用了&#xff0c;家里的老人也不会手机投屏&#xff0c;平时什么娱乐都没有了&#xff0c;这真的太不方便了。 很多老人并不喜欢去买一个广电的机顶盒&#xff0c;或者花钱拉有线电视。 现在的电视大多数都是智能电视&#xff0c;所以许多电…

数据防泄密软件精选|6款好用的数据防泄漏软件强推

某科技公司会议室&#xff0c;CEO张总、CIO李总、信息安全主管王经理正围绕最近发生的一起数据泄露事件展开讨论。 张总&#xff08;忧虑&#xff09;: 大家&#xff0c;这次的数据泄露事件对我们来说是个沉重的打击。客户信息的外泄不仅损害了我们的信誉&#xff0c;还可能面…

lora/lycoris

Stable Diffusion 训练指南 (LyCORIS) | Coding HuskyStable Diffusion 文字生成图片的教程已经很多了。这篇文章是讲解如何用 Kohya Trainer 在 Google Colab 上训练一个 LyCORIS 模型。在读之前希望你已经至少玩过 Stable Diffusion。https://ericfu.me/stable-diffusion-fin…

2.硬盘和内存区别

2.2 磁盘比内存慢几万倍&#xff1f; 存储器方面的设备&#xff0c;分类比较多&#xff0c;那我们肯定不能只买一种存储器&#xff0c;比如你除了要买内存&#xff0c;还要买硬盘&#xff0c;而针对硬盘我们还可以选择是固态硬盘还是机械硬盘。 相信大家都知道内存和硬盘都属…

常见的自动化工具开发必备的源代码!

随着科技的飞速发展&#xff0c;自动化工具已经成为我们日常工作中不可或缺的一部分&#xff0c;自动化工具不仅极大地提高了工作效率&#xff0c;还降低了人为错误的可能性。 然而&#xff0c;要想开发出高效、稳定的自动化工具&#xff0c;掌握一些常见的源代码技巧是至关重…

列式存储和行式存储

列式存储(Columnar or column-based)是相对于传统关系型数据库的行式存储(Row-basedstorage)来说的&#xff0c;简单来说两者的区别就是如何组织表。 原文&#xff1a; Row-based storage stores atable in a sequence of rows. Column-based storage storesa table in …

方圆资源网,方圆资源官网

在当今这个信息化高速发展的时代&#xff0c;方圆资源网络已成为推动社会进步、促进经济发展的重要力量。方圆资源网不仅汇聚了海量的信息资源&#xff0c;更为我们提供了一个高效、便捷的信息交流平台。本文旨在详细介绍资源网的概念、特点、功能以及其在现代社会中的重要意义…

C#写的winform项目无法打包发布?谈谈思路

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

单片机学习(17)--AD/DA

AD/DA 16.1AD/DA的基础知识1.AD/DA介绍2.硬件电路模型3.硬件电路4.运算放大器5.运放电路6.DA原理6.AD原理7.AD/DA性能指标8.XPT2046 16.2AD模数转换&DA数模转换1.AD模数转换&#xff08;1&#xff09;工程目录&#xff08;2&#xff09;main.c函数&#xff08;3&#xff09…

Springboot实战:AI大模型+亮数据代理助力短视频时代

目录 前言1.如何入门亮数据1.1、注册登录1.2、注册账号1.3、登录1.4、购买静态住宅代理1.5、展示购买的代理 2. 使用Springboot、AI大模型构建系统2.1 使用Springboot、AI大模型构建爬虫2.2、在Springboot项目添加工具 3、编写代码&#xff0c;爬取视频素材3.1、代码里使用代理…