task notification是更高级的IPC。
可以替代sem,queue, event group等。
TN是一种更高效率的IPC方式。
每个task都有一个task notification vector,TNV。32bits。
+++++++++++++++++++++++++++++++++++++++++++
使用TNV模拟二值信号量。
void DataProcess_task(void *pvParameters)
{
u8 len=0;
u8 CommandValue=COMMANDERR;
u32 NotifyValue;
u8 *CommandStr;
POINT_COLOR=BLUE;
for(;;)
{
NotifyValue=ulTaskNotifyTake(pdTRUE,portMAX_DELAY); //获取任务通知
if(NotifyValue==1) //清零之前的任务通知值为1,说明任务通知有效
{
len=USART_RX_STA&0x3fff; //得到此次接收到的数据长度
CommandStr=mymalloc(SRAMIN,len+1); //申请内存
sprintf((char*)CommandStr,"%s",USART_RX_BUF);
CommandStr[len]='\0'; //加上字符串结尾符号
LowerToCap(CommandStr,len); //将字符串转换为大写
CommandValue=CommandProcess(CommandStr); //命令解析
if(CommandValue!=COMMANDERR)
{
LCD_Fill(10,90,210,110,WHITE); //清除显示区域
LCD_ShowString(10,90,200,16,16,CommandStr); //在LCD上显示命令
printf("命令为:%s\r\n",CommandStr);
switch(CommandValue) //处理命令
{
case LED1ON:
LED1=0;
break;
case LED1OFF:
LED1=1;
break;
case BEEPON:
BEEP=1;
break;
case BEEPOFF:
BEEP=0;
break;
}
}
else
{
printf("无效的命令,请重新输入!!\r\n");
}
USART_RX_STA=0;
memset(USART_RX_BUF,0,USART_REC_LEN); //串口接收缓冲区清零
myfree(SRAMIN,CommandStr); //释放内存
}
else
{
vTaskDelay(10); //延时10ms,也就是10个时钟节拍
}
}
}
任务主体的第一句,就是take,作为进程同步。
extern TaskHandle_t DataProcess_Handler;; //接收任务通知的任务句柄
void USART1_IRQHandler(void) //串口1中断服务程序
{
u8 Res;
BaseType_t xHigherPriorityTaskWoken;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
Res =USART_ReceiveData(USART1);//(USART1->DR); //读取接收到的数据
if((USART_RX_STA&0x8000)==0)//接收未完成
{
if(USART_RX_STA&0x4000)//接收到了0x0d
{
if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
else USART_RX_STA|=0x8000; //接收完成了
}
else //还没收到0X0D
{
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;//接收数据错误,重新开始接收
}
}
}
}
//发送任务通知
if((USART_RX_STA&0x8000)&&(DataProcess_Handler!=NULL))//接收到数据,并且接收任务通知的任务有效
{
vTaskNotifyGiveFromISR(DataProcess_Handler,&xHigherPriorityTaskWoken);//发送任务通知
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);//如果需要的话进行一次任务切换
}
}
在ISR中,调用give,向对应的TCB发出一个TN。
++++++++++++++++++++++++++++++++++++++++++++++++++++
使用TNV模拟计数信号量。
//释放计数型信号量任务函数
void SemapGive_task(void *pvParameters)
{
u8 key,i=0;
for(;;)
{
key=KEY_Scan(0);
switch(key)
{
case WKUP_PRES:
xTaskNotifyGive(SemapTakeTask_Handler);//发送任务通知
break;
}
i++;
if(i==50)
{
i=0;
LED0=!LED0;
}
vTaskDelay(10); //延时10ms,也就是10个时钟节拍
}
}
这是前台任务,是一个输入任务,在任务中,扫描按键,根据按键状态,调用give这个API,向对应TCB发出TN。
//获取计数型信号量任务函数
void SemapTake_task(void *pvParameters)
{
u8 num;
uint32_t NotifyValue;
for(;;)
{
NotifyValue=ulTaskNotifyTake(pdFALSE,portMAX_DELAY);//获取任务通知
num++;
LCD_ShowxNum(166,111,NotifyValue-1,3,16,0); //显示当前任务通知值
LCD_Fill(6,131,233,313,lcd_discolor[num%14]); //刷屏
LED1=!LED1;
}
}
这是后台任务,是一个产出任务。在任务中,第一句就是进程同步,当TN有效时,解除阻塞。然后继续执行。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++
使用TNV模拟msgbox。
//task1任务函数
void task1_task(void *pvParameters)
{
u8 key,i=0;
BaseType_t err;
for(;;)
{
key=KEY_Scan(0); //扫描按键
err=xTaskNotify((TaskHandle_t )Keyprocess_Handler, //接收任务通知的任务句柄
(uint32_t )key, //任务通知值
(eNotifyAction )eSetValueWithOverwrite); //覆写的方式发送任务通知
if(err==pdFAIL)
{
printf("任务通知发送失败\r\n");
}
i++;
if(i==50)
{
i=0;
LED0=!LED0;
}
vTaskDelay(10); //延时10ms,也就是10个时钟节拍
}
}
这是一个前台任务,一个输入任务,将获得的按键状态值,强制转换成一个u32的值,赋值给对应的TCB中TNV。
//Keyprocess_task函数
void Keyprocess_task(void *pvParameters)
{
u8 num;
uint32_t NotifyValue;
BaseType_t err;
for(;;)
{
err=xTaskNotifyWait((uint32_t )0x00, //进入函数的时候不清除任务bit
(uint32_t )ULONG_MAX, //退出函数的时候清除所有的bit
(uint32_t* )&NotifyValue, //保存任务通知值
(TickType_t )portMAX_DELAY); //阻塞时间
if(err==pdTRUE) //获取任务通知成功
{
switch((u8)NotifyValue)
{
case WKUP_PRES: //KEY_UP控制LED1
LED1=!LED1;
break;
case KEY2_PRES: //KEY2控制蜂鸣器
BEEP=!BEEP;
break;
case KEY0_PRES: //KEY0刷新LCD背景
num++;
LCD_Fill(6,126,233,313,lcd_discolor[num%14]);
break;
}
}
}
}
这是一个后台任务,一个产出任务。在任务中,第一句就是进程同步。当TNV中存在没有被取走的有效数据时,将数据拷贝到指定的buffer中,并将任务解除阻塞。这里,buffer就是任务的栈变量NotifyValue。
++++++++++++++++++++++++++++++++++++++++
使用TNV模拟EG。
void eventsetbit_task(void *pvParameters)
{
u8 key,i;
for(;;)
{
key=KEY_Scan(0);
switch(key)
{
case KEY1_PRES:
xTaskNotify((TaskHandle_t )EventGroupTask_Handler,//接收任务通知的任务句柄
(uint32_t )EVENTBIT_1, //要更新的bit
(eNotifyAction )eSetBits); //更新指定的bit
break;
case KEY2_PRES:
xTaskNotify((TaskHandle_t )EventGroupTask_Handler,//接收任务通知的任务句柄
(uint32_t )EVENTBIT_2, //要更新的bit
(eNotifyAction )eSetBits); //更新指定的bit
break;
}
i++;
if(i==50)
{
i=0;
LED0=!LED0;
}
vTaskDelay(10); //延时10ms,也就是10个时钟节拍
}
}
这是一个前台任务,一个输入任务,根据获得的按键状态值,映射到对应的mask,并将mask强制转换成一个u32的值,形成一个EG,赋值给对应的TCB中TNV。
void eventgroup_task(void *pvParameters)
{
u8 num=0,enevtvalue;
static u8 event0flag,event1flag,event2flag;
uint32_t NotifyValue;
BaseType_t err;
for(;;)
{
err=xTaskNotifyWait((uint32_t )0x00, //进入函数的时候不清除任务bit
(uint32_t )ULONG_MAX, //退出函数的时候清除所有的bit
(uint32_t* )&NotifyValue, //保存任务通知值
(TickType_t )portMAX_DELAY); //阻塞时间
if(err==pdPASS) //任务通知获取成功
{
if((NotifyValue&EVENTBIT_0)!=0) //事件0发生
{
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); //模拟事件标志组值
printf("任务通知值为:%d\r\n",enevtvalue);
LCD_ShowxNum(174,110,enevtvalue,1,16,0); //在LCD上显示当前事件值
if((event0flag==1)&&(event1flag==1)&&(event2flag==1)) //三个事件都同时发生
{
num++;
LED1=!LED1;
LCD_Fill(6,131,233,313,lcd_discolor[num%14]);
event0flag=0; //标志清零
event1flag=0;
event2flag=0;
}
}
else
{
printf("任务通知获取失败\r\n");
}
}
}
这是一个后台任务,一个产出任务。在任务中,第一句就是进程同步。当TNV中存在没有被取走的有效数据时,将数据拷贝到指定的buffer中,并将任务解除阻塞。这里,buffer就是任务的栈变量NotifyValue。
TNV取出的数据,就是模拟EG的值。
extern TaskHandle_t EventGroupTask_Handler;
void EXTI4_IRQHandler(void)
{
BaseType_t xHigherPriorityTaskWoken;
delay_xms(20); //消抖
if(KEY0==0)
{
xTaskNotifyFromISR((TaskHandle_t )EventGroupTask_Handler, //任务句柄
(uint32_t )EVENTBIT_0, //要更新的bit
(eNotifyAction )eSetBits, //更新指定的bit
(BaseType_t* )xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
EXTI_ClearITPendingBit(EXTI_Line4);//清除LINE4上的中断标志位
}
在前台ISR中,根据获得的按键状态值,映射到对应的mask,并将mask强制转换成一个u32的值,形成一个EG,赋值给对应的TCB中TNV。