抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

任务通知

任务通知是一种任务间通信的手段,比使用信号量更节省资源,解除阻塞时间更快

  1. 无需创建队列
  2. 更节省RAM空间
  3. 发送通知任务几种模式:通知未读,不覆盖通知值;直接覆盖通知值;设置通知位的一个或多位,当作事件组;递增通知值,当做计数信号量。
  4. 由于必须指定接收通知的任务,只能有一个任务接收通知。
  5. 只有等待通知的任务可以被阻塞,发送通知的任务不会因为发送失败进入阻塞态。

任务通知运行机制

1
2
//开启任务通知功能,默认开启
#define configUSE_TASK_NOTIFICATIONS 1
  1. 随任务创建而初始化
  2. 任务通知和队列消息的运行机制一致

任务通知常见函数

  1. xTaskGenericNotify()​​发送任务通知,是任务通知的原型
  2. xTaskNotifyGive()​​调用任务通知,并将任务通知值加1,可以作为二值信号量和计数信号量的低配版,更加轻量化,原型xTaskNotify()​​
  3. xTaskNotifyGiveFromISR()​​中断调用任务通知
  4. xTaskNotify()​​用于任务直接向另一个任务发送一个事件
  5. xTaskNotifyFromISR()​​ 中断中使用,原型为xTaskGenericNotifyFromISR()​​
  6. xTaskGenericNotifyFromISR()​​通用中断发送任务通知
  7. xTaskNotifyAndQuery()​​和xTaskNotify()​​功能类似,附加了回传接收任务的通知值,若不需要回传则等同于xTaskNotify()​​
  8. xTaskNotifyAndQueryFromISR()​​中断用
  9. ulTaskNotifyTake()​可作为轻量级获取二值信号量和计数信号量的实现,用于自身句柄的调用
  10. xTaskNotifyWait()​可以用于实现全功能的等待任务通知,更轻量化

代替消息队列

设计3个任务,2个任务接收消息通知来控制LED​,1个任务通过2个按钮来发送消息通知

接收、发送通知句柄,创建一个引用,并不是创建一个真实的消息通知

1
2
3
4
5
6
//任务通知句柄
static TaskHandle_t Receive1_Task_Handle = NULL;
static TaskHandle_t Receive2_Task_Handle = NULL;
static TaskHandle_t Send_Task_Handle = NULL;

#define USE_CHAR 0 //测试字符串配置1,测试变量配置0

创建一个真实的句柄

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//Receive1_Task任务
xReturn =xTaskCreate(Receive1_Task,"Receive1_Task",512,NULL,2,(TaskHandle_t *)&Receive1_Task_Handle);
if (xReturn==pdPASS)
{
printf("创建Receive1_Task任务成功\r\n");
}

//Receive2_Task任务
xReturn = xTaskCreate(Receive2_Task,"Receive2_Task",512,NULL,3,(TaskHandle_t *)&Receive2_Task_Handle);
if (xReturn==pdPASS)
{
printf("创建Receive2_Task任务成功\r\n");
}

//Receive2_Task任务
xReturn = xTaskCreate(Send_Task,"Send_Task",512,NULL,4,(TaskHandle_t *)&Send_Task_Handle);
if (xReturn==pdPASS)
{
printf("创建Send_Task任务成功\r\n");
}

接收通知任务1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
static void Receive1_Task(void *paramter)
{
BaseType_t xReturn = pdTRUE;
#if USE_CHAR
char *r_char;
#else
uint32_t r_num;
#endif
while (1)
{
//获取任务通知,没获取到则等待
xReturn = xTaskNotifyWait(0x0, //进入函数的时候不清除任务Bit
ULONG_MAX, //退出函数的时候清除所以的Bit
#if USE_CHAR
(uint32_t *)&r_char, //保存任务通知值
#else
&r_num, //保存任务通知值
#endif
portMAX_DELAY); //阻塞时间
if(xReturn==pdTRUE)
#if USE_CHAR
printf("Receive1_Task 任务通知%s \r\n",r_char);
#else
printf("Receive1_Task 任务通知%d \r\n",r_num);
#endif
LED0_Turn();
}
}

接收通知任务2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
static void Receive2_Task(void *paramter)
{
BaseType_t xReturn = pdTRUE;
#if USE_CHAR
char *r_char;
#else
uint32_t r_num;
#endif
while (1)
{
//获取任务通知,没获取到则一直等待
xReturn = xTaskNotifyWait(0x0,
ULONG_MAX,
#if USE_CHAR
(uint32_t *)&r_char,
#else
&r_num,
#endif
portMAX_DELAY);

if(xReturn == pdTRUE)
{
#if USE_CHAR
printf("Receive2_Task 任务通知%s \r\n",r_char);
#else
printf("Receive2_Task 任务通知%d \r\n",r_num);
#endif
LED7_Turn();
}
}
}

发送通知任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
static void Send_Task(void *paramter)
{
BaseType_t xReturn=pdTRUE;
#if USE_CHAR
char test_str1[]="this is mail test 1"; //消息1
char test_str1[]="this is mail test 2"; //消息2
#else
uint32_t send1=1;
uint32_t send2=2;
#endif
while (1)
{
//KEY1按下
if (Key_BTN(BTN1)==KEY_ON)
{
xReturn = xTaskNotify(Receive1_Task_Handle, //任务句柄
#if USE_CHAR
(uint32_t)&test_str1, //发送数据,最大4字节
#else
send1, //发送数据,最大4字节
#endif
eSetValueWithOverwrite); //覆盖当前通知

if(xReturn==pdTRUE)
{
printf("Receive1_Task_Handle 任务通知释放成功\r\n");
}
}

//KEY2按下
if (Key_BTN(BTN2)==KEY_ON)
{
xReturn = xTaskNotify(Receive2_Task_Handle,
#if USE_CHAR
(uint32_t)&test_str2,
#else
send2,
#endif
eSetValueWithOverwrite);
if (xReturn==pdTRUE)
{
printf("Receive2_Task_Handle 任务通知释放成功\r\n");
}
}
vTaskDelay(20);
}
}

按下BTN1​后,通信接收任务1,LED0​亮,按下BTN2​后,通信接收任务2,LED7​亮

代替二值信号量

设计3个任务,2个任务接收消息通知来控制LED​,1个任务通过2个按钮来发送消息通知,按钮控制释放信号量句柄,注意ulTaskNotifyTake​的使用,在自身句柄对应的Task​中调用

前置和代替消息队列相同

接收通知任务1

1
2
3
4
5
6
7
8
9
10
static void Receive1_Task(void *paramter)
{
while (1)
{
//获取任务通知,没有则一直等待
ulTaskNotifyTake(pdTRUE,portMAX_DELAY);
printf("Receive1_Task 任务通知获取成功\r\n");
LED0_Turn();
}
}

接收通知任务2

1
2
3
4
5
6
7
8
9
static void Receive2_Task(void *paramter)
{
while (1)
{
ulTaskNotifyTake(pdTRUE,portMAX_DELAY);
printf("Receive2_Task 任务通知获取成功\r\n");
LED7_Turn();
}
}

发送通知任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
static void Send_Task(void *paramter)
{
BaseType_t xReturn=pdPASS;
while (1)
{
if (Key_BTN(BTN1)==KEY_ON)
{
xReturn = xTaskNotifyGive(Receive1_Task_Handle);
if (xReturn==pdTRUE)
{
printf("Receive1_Task 任务释放成功\r\n");
}

}
if (Key_BTN(BTN2)==KEY_ON)
{
xReturn = xTaskNotifyGive(Receive2_Task_Handle);
if (xReturn == pdTRUE)
{
printf("Receive2_Task 任务释放成功\r\n");
}

}
vTaskDelay(20);
}

}

按下BTN1​后,通信接收任务1,LED0​亮,按下BTN2​后,通信接收任务2,LED7​亮

代替计数信号量

创建2个任务,1个用于等待通知,申请车位。另一个用于释放信号量,释放车位。

接收、发送通知句柄,创建一个引用,并不是创建一个真实的消息通知

1
2
3
//任务通知-计数信号量替代
static TaskHandle_t Take_Task_Handle=NULL;
static TaskHandle_t Give_Task_Handle=NULL;

申请车位

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static void Take_Task(void *paramter)
{
uint32_t take_num=pdPASS;
while (1)
{
if(Key_BTN(BTN1)==KEY_ON)
{
//没获取到,就一直等待
take_num=ulTaskNotifyTake(pdFALSE,0);
if (take_num>0)
{
printf("成功申请到车位,当前车位%d\r\n",take_num-1);
}else
{
printf("KEY1按下,无多余车位。请按下KEY2释放车位\r\n");
}
}
}
}

释放车位

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static void Give_Task(void *paramter)
{
BaseType_t xReturn = pdPASS;
while (1)
{
if (Key_BTN(BTN2)==KEY_ON)
{
xReturn = xTaskNotifyGive(Take_Task_Handle);
if (xReturn==pdPASS)
{
printf("KEY2按下,释放一个车位\r\n");
}

}
vTaskDelay(20);

}
}

按下KEY1​,无车位;按下KEY2​2次释放2个车位,再按下KEY1​申请2个车位

代替事件组

创建2个任务,一个任务用于监听事件,一个任务用于控制BTN1​和BTN2​来触发事件

事件组句柄,创建一个引用,并不是创建一个真实的事件组

1
2
3
4
5
6
7
8
//事件组代替
static TaskHandle_t LED7_Task_Handle=NULL;
static TaskHandle_t KEY_Task_Handle=NULL;

//事件句柄
static EventGroupHandle_t Event_Handle=NULL;

#define KEY1_EVENT (0x01 << 0) //设置掩码的位0

创建一个真实的事件组

1
2
3
4
5
Event_Handle = xEventGroupCreate();
if (Event_Handle!=NULL)
{
printf("事件组句柄创建成功\r\n");
}

LED灯任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
static void LED7_Task(void *paramter)
{
uint32_t r_event=0;
uint32_t last_event=0;
BaseType_t xReturn=pdPASS;
while(1)
{
xReturn = xTaskNotifyWait(0x0, //进去函数不清除任务Bit
ULONG_MAX, //退出函数清除所有bit
&r_event, //保存任务通知值
portMAX_DELAY); //阻塞时间
if (xReturn == pdPASS)
{
last_event |= r_event;
if (last_event == (KEY1_EVENT|KEY2_EVENT))
{
last_event=0;
printf("KEY1和KEY2都按下\r\n");
LED7_Turn();
}else
{
last_event=r_event;
}

}
}
}

KEY任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static void KEY_Task(void *paramter)
{
while(1)
{
if( Key_BTN(BTN1) == 0 )
{/* K1 被按下 */
printf("K1 被按下\r\n");
xTaskNotify(LED7_Task_Handle, //任务句柄
KEY1_EVENT, //需要触发的事件
eSetBits); //设置通知的值
}
if( Key_BTN(BTN2) == 0 )
{/* K2 被按下 */
printf("K2 被按下\r\n");
xTaskNotify(LED7_Task_Handle,
KEY2_EVENT,
eSetBits);
}
vTaskDelay(20);
}
}

按下BTN1​,设置事件1,按下BTN2​,设置事件2,触发LED​事件