μC/OSIII学习day10
介绍
任务信号量,任务消息队列
# 任务信号量
# 任务信号量的基本概念
- μC/OS提供任务信号量这个功能,每个任务都有一个32位(用户可以自定义位宽,我们使用32位的CPU,此处就是32位)的信号量值SemCtr,这个信号量值是在任务控制块中包含的,是任务独有的一个信号量通知值,在大多数情况下,任务信号量可以替代内核对象的二值信号量,计数信号量等(而非内核对象信号量,如非特别说明,本章中的信号量都指的是内核对象信号量.前面所讲的信号量是单独的内核对象,是独立于任务存在的;本章要讲述的任务信号量是任务特有的属性,紧紧依赖于一个特定任务).
- 相对于前面使用μC/OS内核通信的资源,必须创建二进制信号量,计数信号量等情况,使用任务信号量显然更灵活.因为使用任务信号量比通过内核对象信号量通信方式解除阻塞的任务的速度要快,并且更加节省RAM内存空间,任务信号量的使用无需单独创建信号量.
- 通过对任务信号量的合理使用,可以在一定场合下替代μC/OS的信号量,用户只需向任务内部的信号量发送一个信号而不用通过外部的信号量进行发送,这样子处理就会很方便并且更加高效,当然,凡事都有利弊,不然的话μC/OS还要内核的IPC通信机制干嘛,任务信号量虽然处理更快,RAM开销更小,但也有限制:只能有一个任务接收任务信号量,因为必须指定接收信号量的任务,才能正确发送信号量;而内核对象的信号量则没有这个限制,用户在释放信号量,可以采用广播的方式,让所有等待信号量的任务都获取到信号量.
- 在实际任务间的通信中,一个或多个任务发送一个信号量给另一个任务是非常常见的,而一个任务给多个任务发送信号量的情况相对比较少.这种情况就很适合采用任务信号量进行传递信号,如果任务信号量可以满足设计需求,那么尽量不要使用普通信号量,这样子设计的系统会更加高效.
- 任务信号量的运作机制与普通信号量一样,没什么差别.
# 任务信号量的函数接口讲解
函数名称 | 函数作用 |
---|---|
OSTaskSemPost() | 释放任务信号量 |
OSTaskSemPend() | 获取一个任务信号 |
任务信号量释放函数OSTaskSemPost()
释放任务信号量,虽然只有拥有任务信号量的任务才可以等待该任务信号量,但是其他所有的任务或者中断都可以向该任务释放信号量.
参数 含义 *p_tcb 目标任务TCB opt 选项 *p_err 返回错误类型 选项 功能 OS_OPT_POST_NONE 没有选项 OS_OPT_POST_NO_SCHED 不调用调度程序 错误类型 含义 OS_ERR_INT_Q_FULL 延迟中断后队列已满 OS_ERR_OPT_INVALID 指定的选项无效 OS_ERR_OS_NOT_RUNNING uC/OS尚未运行 OS_ERR_SEM_OVF 信号量计数溢出 OS_ERR_STATE_INVALID 任务处于无效状态 OS_ERR_NONE 无错误 OS_SEM_CTR OSTaskSemPost(OS_TCB *p_tcb, //目标任务 OS_OPT opt, //选项 OS_ERR *p_err){ OS_SEM_CTR ctr; CPU_TS ts; #ifdef OS_SAFETY_CRITICAL//如果启用(默认禁用)了安全检测 if (p_err == (OS_ERR *)0) //如果 p_err 为空 { OS_SAFETY_CRITICAL_EXCEPTION(); //执行安全检测异常函数 return ((OS_SEM_CTR)0); //返回 0(有错误),停止执行 } #endif #if OS_CFG_ARG_CHK_EN > 0u//如果启用(默认启用)了参数检测功能 switch (opt) //根据选项分类处理 { case OS_OPT_POST_NONE: //如果选项在预期之内 case OS_OPT_POST_NO_SCHED: break; //跳出 default: //如果选项超出预期 *p_err = OS_ERR_OPT_INVALID; //错误类型为"选项非法" return ((OS_SEM_CTR)0u); //返回 0(有错误),停止执行 } #endif ts = OS_TS_GET(); //获取时间戳 #if OS_CFG_ISR_POST_DEFERRED_EN > 0u//如果启用了中断延迟发布 if (OSIntNestingCtr > (OS_NESTING_CTR)0) //如果该函数是在中断中被调用 { OS_IntQPost((OS_OBJ_TYPE)OS_OBJ_TYPE_TASK_SIGNAL, //将该信号量发布到中断消息队列 (void *)p_tcb, (void *)0, (OS_MSG_SIZE)0, (OS_FLAGS )0, (OS_OPT )0, (CPU_TS )ts, (OS_ERR *)p_err); return ((OS_SEM_CTR)0); //返回 0(尚未发布) } #endif //调用OS_TaskSemPost()函数将信号量发布到任务中 ctr = OS_TaskSemPost(p_tcb, //将信号量按照普通方式处理 opt, ts, p_err); return (ctr); //返回信号的当前计数值 }
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
48
49
50
51
52释放任务信号量函数的使用实例.
OSTaskSemPost((OS_TCB *)&AppTaskPendTCB, //目标任务 (OS_OPT )OS_OPT_POST_NONE, //没选项要求 (OS_ERR *)&err);
1
2
3
4在释放任务信号量的时候,系统首先判断目标任务的状态,只有处于等待状态并且等待的是任务信号量那就调用
OS_Post()
函数让等待的任务就绪(如果内核对象信号量的话,还会让任务脱离等待列表),所以任务信号量的操作是非常高效的;如果没有处于等待状态或者等待的不是任务信号量,那就直接将任务控制块的元素SemCtr
加1.最后返回任务信号量计数值.其实,不管是否启用了中断延迟发布,最终都是调用
OS_TaskSemPost()
函数进行释放任务信号量.只是启用了中断延迟发布的释放过程会比较曲折,中间会有许多插曲,这是中断管理范畴的内容,留到后面再作介绍.在OS_TaskSemPost()
函数中,又会调用OS_Post()
函数释放内核对象.OS_Post()
函数是一个底层的释放(发布)函数,它不仅仅用来释放(发布)任务信号量,还可以释放信号量,互斥信号量,消息队列,事件标志组或任务消息队列.
获取任务信号量函数OSTaskSemPend()
用于获取一个任务信号量,参数中没有指定某个任务去获取信号量,实际上就是当前运行任务获取它自己拥有的任务信号量.
参数 含义 timeout 等待超时时间 opt 选项 *p_ts 时间戳 *p_err 返回错误类型 选项 功能 OS_OPT_PEND_BLOCKING 等待 OS_OPT_PEND_NON_BLOCKING 不等待 错误类型 含义 OS_ERR_PEND_ABORT 获取信号量已中止 OS_ERR_OPT_INVALID 指定选项无效 OS_ERR_OS_NOT_RUNNING uC/OS尚未运行 OS_ERR_PEND_ISR 从ISR调用了此函数 OS_ERR_PEND_WOULD_BLOCK 任务处于无效状态 OS_ERR_SCHED_LOCKED 调度程序已锁定 OS_ERR_STATUS_INVALID 状态无效 OS_ERR_TIMEOUT 超时 OS_ERR_NONE 无错误 OS_SEM_CTR OSTaskSemPend (OS_TICK timeout, //等待超时时间 OS_OPT opt, //选项 CPU_TS *p_ts, //返回时间戳 OS_ERR *p_err){ OS_SEM_CTR ctr; CPU_SR_ALLOC(); //使用到临界段(在关/开中断时)时必须用到该宏 #ifdef OS_SAFETY_CRITICAL//如果启用了安全检测 if (p_err == (OS_ERR *)0) //如果错误类型实参为空 { OS_SAFETY_CRITICAL_EXCEPTION(); //执行安全检测异常函数 return ((OS_SEM_CTR)0); //返回 0(有错误),停止执行 } #endif #if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u//如果启用了中断中非法调用检测 if (OSIntNestingCtr > (OS_NESTING_CTR)0) //如果该函数在中断中被调用 { *p_err = OS_ERR_PEND_ISR; //返回错误类型为"在中断中等待" return ((OS_SEM_CTR)0); //返回 0(有错误),停止执行 } #endif #if OS_CFG_ARG_CHK_EN > 0u//如果启用了参数检测 switch (opt) //根据选项分类处理 { case OS_OPT_PEND_BLOCKING: //如果选项在预期内 case OS_OPT_PEND_NON_BLOCKING: break; //直接跳出 default: //如果选项超出预期 *p_err = OS_ERR_OPT_INVALID; //错误类型为"选项非法" return ((OS_SEM_CTR)0); //返回 0(有错误),停止执行 } #endif if (p_ts != (CPU_TS *)0) //如果 p_ts 非空 { *p_ts = (CPU_TS )0; //清零(初始化)p_ts } CPU_CRITICAL_ENTER(); //关中断 if (OSTCBCurPtr->SemCtr > (OS_SEM_CTR)0) //如果任务信号量当前可用 { OSTCBCurPtr->SemCtr--; //信号量计数器减 1 ctr = OSTCBCurPtr->SemCtr; //获取信号量的当前计数值 if (p_ts != (CPU_TS *)0) //如果 p_ts 非空 { *p_ts = OSTCBCurPtr->TS; //返回信号量被发布的时间戳 } #if OS_CFG_TASK_PROFILE_EN > 0u OSTCBCurPtr->SemPendTime = OS_TS_GET() - OSTCBCurPtr->TS; //更新任务等待 if (OSTCBCurPtr->SemPendTimeMax < OSTCBCurPtr->SemPendTime) //任务信号量的 { OSTCBCurPtr->SemPendTimeMax = OSTCBCurPtr->SemPendTime; //最长时间记录. }//如果启用任务统计的宏,计算任务信号量从被提交到获取所用时间及最大时间 #endif CPU_CRITICAL_EXIT(); //开中断 *p_err = OS_ERR_NONE; //错误类型为"无错误" return (ctr); //返回信号量的当前计数值 } /* 如果任务信号量当前不可用 */ if ((opt & OS_OPT_PEND_NON_BLOCKING) != (OS_OPT)0) //如果选择了不阻塞任务 { CPU_CRITICAL_EXIT(); //开中断 *p_err = OS_ERR_PEND_WOULD_BLOCK; //错误类型为"缺乏阻塞" return ((OS_SEM_CTR)0); //返回 0(有错误),停止执行 } else {//如果选择了阻塞任务 if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) //如果调度器被锁 { CPU_CRITICAL_EXIT(); //开中断 *p_err = OS_ERR_SCHED_LOCKED;//错误类型为"调度器被锁" return ((OS_SEM_CTR)0); //返回 0(有错误),停止执行 } } /* 如果调度器未被锁 */ OS_CRITICAL_ENTER_CPU_EXIT(); //锁调度器,重开中断 OS_Pend((OS_PEND_DATA *)0, //阻塞任务,等待信号量. (OS_PEND_OBJ *)0, //不需插入等待列表. (OS_STATE )OS_TASK_PEND_ON_TASK_SEM, (OS_TICK )timeout); OS_CRITICAL_EXIT_NO_SCHED(); //开调度器(无调度) OSSched(); //调度任务 /* 任务获得信号量后得以继续运行 */ CPU_CRITICAL_ENTER(); //关中断 switch (OSTCBCurPtr->PendStatus) //根据任务的等待状态分类处理 { case OS_STATUS_PEND_OK: //如果任务成功获得信号量 if (p_ts != (CPU_TS *)0) //返回信号量被发布的时间戳 { *p_ts = OSTCBCurPtr->TS; #if OS_CFG_TASK_PROFILE_EN > 0u//更新最长等待时间记录 OSTCBCurPtr->SemPendTime = OS_TS_GET() - OSTCBCurPtr->TS; if (OSTCBCurPtr->SemPendTimeMax < OSTCBCurPtr->SemPendTime) { OSTCBCurPtr->SemPendTimeMax = OSTCBCurPtr->SemPendTime; } #endif } *p_err = OS_ERR_NONE; //错误类型为"无错误" break; //跳出 case OS_STATUS_PEND_ABORT: //如果等待被中止 if (p_ts != (CPU_TS *)0) //返回被终止时的时间戳 { *p_ts = OSTCBCurPtr->TS; } *p_err = OS_ERR_PEND_ABORT; //错误类型为"等待被中止" break; //跳出 case OS_STATUS_PEND_TIMEOUT: //如果等待超时 if (p_ts != (CPU_TS *)0) //返回时间戳为 0 { *p_ts = (CPU_TS )0; } *p_err = OS_ERR_TIMEOUT; //错误类型为"等待超时" break; //跳出 default: //如果等待状态超出预期 *p_err = OS_ERR_STATUS_INVALID; //错误类型为"状态非法" break; //跳出 } ctr = OSTCBCurPtr->SemCtr; //获取信号量的当前计数值 CPU_CRITICAL_EXIT(); //开中断 return (ctr); //返回信号量的当前计数值 }
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124获取任务信号量函数的使用实例.
OSTaskSemPend ((OS_TICK )0, //无期限等待 (OS_OPT )OS_OPT_PEND_BLOCKING, //如果信号量不可用就等待 (CPU_TS *)&ts, //获取信号量被发布的时间戳 (OS_ERR *)&err);
1
2
3
4
5在调用该函数的时候,系统先判断任务信号量是否可用,即检查任务信号量的计数值是否大于0,如果大于 0,即表示可用,这个时候获取信号量,即将计数值减1后直接返回.如果信号量不可用,且当调度器没有被锁住时,用户希望在任务信号量不可用的时候进行阻塞任务以等待任务信号量可用,那么系统就会调用
OS_Pend()
函数将任务脱离就绪列表,如果用户有指定超时时间,系统还要将该任务插入节拍列表.注意:此处系统并没有将任务插入等待列表.然后切换任务,处于就绪列表中最高优先级的任务通过任务调度获得CPU使用权,等到出现任务信号量被释放,任务等待任务信号量被强制停止,等待超时等情况,任务会从阻塞中恢复,等待任务信号量的任务重新获得CPU使用权,返回相关错误代码和任务信号量计数值,用户可以根据返回的错误知道任务退出等待状态的情况.
# 任务信号量实验
# 任务信号量代替二值信号量
任务通知代替消息队列是在μC/OS中创建了两个任务,其中一个任务是用于接收任务信号量,另一个任务发送任务信号量.两个任务独立运行,发送任务信号量的任务是通过检测按键的按下情况发送,等待任务在任务信号量中没有可用的信号量之前就一直等待,获取到信号量以后就继续执行,这样子是为了代替二值信号量,任务同步成功则继续执行,然后在串口调试助手里将运行信息打印出来.
定义任务空间栈的大小以及任务栈数组,任务控制块和优先级.
定义任务函数
任务启动函数编写
结果现象
# 任务信号量代替计数信号量
任务通知代替计数信号量是基于计数信号量实验修改而来,模拟停车场工作运行.并且在μC/OS中创建了两个任务:一个是获取信号量任务,一个是发送信号量任务,两个任务独立运行.按下WKUP按键计数信号量+1,每秒计数信号量-1.
定义任务空间栈的大小以及任务栈数组,任务控制块和优先级.
定义任务函数
任务启动函数编写
结果现象
# 任务消息队列
# 任务消息队列的基本概念
任务消息队列跟任务信号量一样,均隶属于某一个特定任务,不需单独创建,任务在则任务消息
队列在,只有该任务才可以获取(接收)这个任务消息队列的消息,其他任务只能给这个任务消
息队列发送消息,却不能获取.任务消息队列与前面讲解的(普通)消息队列极其相似,只是任
务消息队列已隶属于一个特定任务,所以它不具有等待列表,在操作的过程中省去了等待任务插
入和移除列表的动作,所以工作原理相对更简单一点,效率也比较高一些.
通过对任务消息队列的合理使用,可以在一定场合下替代μC/OS的消息队列,用户只需向任务内
部的消息队列发送一个消息而不用通过外部的消息队列进行发送,这样子处理就会很方便并且
更加高效,当然,凡事都有利弊,任务消息队列虽然处理更快,RAM开销更小,但也有限制:只
能指定消息发送的对象,有且只有一个任务接收消息;而内核对象的消息队列则没有这个限制,
用户在发送消息的时候,可以采用广播消息的方式,让所有等待该消息的任务都获取到消息.
在实际任务间的通信中,一个或多个任务发送一个消息给另一个任务是非常常见的,而一个任务
给多个任务发送消息的情况相对比较少,前者就很适合采用任务消息队列进行传递消息,如果任
务消息队列可以满足设计需求,那么尽量不要使用普通消息队列,这样子设计的系统会更加高
效.
消息队列(内核对象)是用结构体OS_Q来管理的,包含了管理消息的元素MsgQ和管理等待列
表的元素PendList等.而任务消息队列的结构体成员变量就少了PendList,因为等待任务消息队
列只有拥有任务消息队列本身的任务才可以进行获取,故任务消息队列不需要等待列表的相关
数据结构,
想要使用任务消息队列,就必须将
OS_CFG_TASK_Q_EN
宏定义配置为1,该宏定义位于os_cfg.h文件中.任务消息队列数据结构
struct os_msg_q{ OS_MSG *InPtr; OS_MSG *OutPtr; //*InPtr和*OutPtr是任务消息队列中进出消息指针 OS_MSG_QTY NbrEntriesSize; //任务消息队列中最大可用的消息个数,在创建任务的时候由用户指定这个值的大小 OS_MSG_QTY NbrEntries; //记录任务消息队列中当前的消息个数,每当发送一个消息到任务消息队列的时候,若任务没有在等待该消息,那么新发送的消息被插入任务消息队列后此值加1,NbrEntries的大小不能超过NbrEntriesSize OS_MSG_QTY NbrEntriesMax; //记录任务消息队列最多的时候拥有的消息个数 };
1
2
3
4
5
6
7
8
# 任务消息队列的函数
函数名称 | 函数作用 |
---|---|
OStaskQPost() | 发送任务消息队列 |
OSTaskQpend() | 获取任务消息队列 |
任务消息队列发送函数OSTaskQPost()
发送任务消息队列,参数中有指向消息要发送给的任务控制块的指针,任何任务都可以发送消息给拥有任务消息队列的任务(任务在被创建的时候,要设置参数 q_size大于 0).
参数 含义 *p_tcb 目标任务TCB *p_void 消息内容地址 msg_size 消息长度 opt 选项 *p_err 返回错误类型 选项 功能 OS_OPT_POST_FIFO 先进先出 OS_OPT_POST_LIFO 后进先出 OS_OPT_POST_NO_SCHED 不在发布后运行调度程序 错误类型 含义 OS_ERR_INT_Q_FULL 延迟中断后队列已满 OS_ERR_MSG_POOL_EMPTY 延迟中断后队列已满 OS_ERR_OPT_INVALID 栈已空 OS_ERR_OS_NOT_RUNNING uC/OS尚未运行 OS_ERR_Q_MAX 消息队列已满 OS_ERR_STATE_INVALID 任务无效 OS_ERR_NONE 无错误 #if OS_CFG_TASK_Q_EN > 0u //如果启用了任务消息队列 void OSTaskQPost(OS_TCB *p_tcb, //目标任务 void *p_void, //消息内容地址 OS_MSG_SIZE msg_size, //消息长度 OS_OPT opt, //选项 OS_ERR *p_err){ CPU_TS ts; #ifdef OS_SAFETY_CRITICAL//如果启用(默认禁用)了安全检测 if (p_err == (OS_ERR *)0) //如果错误类型实参为空 { OS_SAFETY_CRITICAL_EXCEPTION(); //执行安全检测异常函数 return; //返回,停止执行 } #endif #if OS_CFG_ARG_CHK_EN > 0u//如果启用了参数检测 switch (opt) //根据选项分类处理 { case OS_OPT_POST_FIFO: //如果选项在预期内 case OS_OPT_POST_LIFO: case OS_OPT_POST_FIFO | OS_OPT_POST_NO_SCHED: case OS_OPT_POST_LIFO | OS_OPT_POST_NO_SCHED: break; //直接跳出 default: //如果选项超出预期 *p_err = OS_ERR_OPT_INVALID; //错误类型为"选项非法" return; //返回,停止执行 } #endif ts = OS_TS_GET(); //获取时间戳 #if OS_CFG_ISR_POST_DEFERRED_EN > 0u//如果启用了中断延迟发布 if (OSIntNestingCtr > (OS_NESTING_CTR)0) //如果该函数在中断中被调用 { OS_IntQPost((OS_OBJ_TYPE)OS_OBJ_TYPE_TASK_MSG, //将消息先发布到中断消息队列 (void *)p_tcb, (void *)p_void, (OS_MSG_SIZE)msg_size, (OS_FLAGS )0, (OS_OPT )opt, (CPU_TS )ts, (OS_ERR *)p_err); return; //返回 } #endif OS_TaskQPost(p_tcb, //将消息直接发布 p_void, msg_size, opt, ts, p_err); } #endif
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
48
49
50
51
52
53
54
55
56任务消息队列获取函数OSTaskQPend()
于获取一个任务消息队列,函数的参数中没有指定哪个任务获取任务消息,实际上就是当前执行的任务,当任务调用了这个函数就表明这个任务需要获取任务消息.
参数 含义 timeout 等待期限 opt 选项 *p_msg_size 返回消息长度 *p_ts 返回时间戳 *p_err 返回错误类型的指针 选项 功能 OS_OPT_PEND_BLOCKING 等待 OS_OPT_PEND_NON_BLOCKING 不等待 错误类型 含义 OS_ERR_OPT_INVALID 选项无效 OS_ERR_OS_NOT_RUNNING uC/OS尚未运行 OS_ERR_PEND_ABORT 获取信号量已终止 OS_ERR_PEND_ISR 从ISR调用了此函数 OS_ERR_PEND_WOULD_BLOCK 任务处于无效状态 OS_ERR_PTR_INVALID p_msg_size为空 OS_ERR_SCHED_LOCKED 调度任务已锁定 OS_ERR_TIMEOUT 超时 OS_ERR_NONE 无错误 #if OS_CFG_TASK_Q_EN > 0u//如果启用了任务消息队列 void *OSTaskQPend(OS_TICK timeout, //等待期限(单位:时钟节拍) OS_OPT opt, //选项 OS_MSG_SIZE *p_msg_size, //返回消息长度 CPU_TS *p_ts, //返回时间戳 OS_ERR *p_err){ OS_MSG_Q *p_msg_q; void *p_void; CPU_SR_ALLOC(); //使用到临界段(在关/开中断时)时必须用到该宏 #ifdef OS_SAFETY_CRITICAL//如果启用(默认禁用)了安全检测 if (p_err == (OS_ERR *)0) //如果错误类型实参为空 { OS_SAFETY_CRITICAL_EXCEPTION(); //执行安全检测异常函数 return ((void *)0); //返回 0(有错误),停止执行 } #endif #if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u//如果启用了中断中非法调用检测 if (OSIntNestingCtr > (OS_NESTING_CTR)0) //如果该函数在中断中被调用 { *p_err = OS_ERR_PEND_ISR; //错误类型为"在中断中中止等待" return ((void *)0); //返回 0(有错误),停止执行 } #endif #if OS_CFG_ARG_CHK_EN > 0u//如果启用了参数检测 if (p_msg_size == (OS_MSG_SIZE *)0) //如果 p_msg_size 为空 { *p_err = OS_ERR_PTR_INVALID; //错误类型为"指针不可用" return ((void *)0); //返回 0(有错误),停止执行 } switch (opt) //根据选项分类处理 { case OS_OPT_PEND_BLOCKING: //如果选项在预期内 case OS_OPT_PEND_NON_BLOCKING: break; //直接跳出 default: //如果选项超出预期 *p_err = OS_ERR_OPT_INVALID; //错误类型为"选项非法" return ((void *)0); //返回 0(有错误),停止执行 } #endif if (p_ts != (CPU_TS *)0) //如果 p_ts 非空 { *p_ts = (CPU_TS )0; //初始化(清零)p_ts,待用于返回时间戳 } CPU_CRITICAL_ENTER(); //关中断 p_msg_q = &OSTCBCurPtr->MsgQ;//获取当前任务的消息队列 p_void = OS_MsgQGet(p_msg_q, //从队列里获取一个消息 p_msg_size, p_ts, p_err); if (*p_err == OS_ERR_NONE) //如果获取消息成功 { #if OS_CFG_TASK_PROFILE_EN > 0u if (p_ts != (CPU_TS *)0) { OSTCBCurPtr->MsgQPendTime = OS_TS_GET() - *p_ts; if (OSTCBCurPtr->MsgQPendTimeMax < OSTCBCurPtr->MsgQPendTime) { OSTCBCurPtr->MsgQPendTimeMax = OSTCBCurPtr->MsgQPendTime; } } #endif CPU_CRITICAL_EXIT(); //开中断 return (p_void); //返回消息内容 } /* 如果获取消息不成功(队列里没有消息) */ if ((opt & OS_OPT_PEND_NON_BLOCKING) != (OS_OPT)0) //如果选择了不阻塞任务 { *p_err = OS_ERR_PEND_WOULD_BLOCK; //错误类型为"缺乏阻塞" CPU_CRITICAL_EXIT(); //开中断 return ((void *)0); //返回 0(有错误),停止执行 } else {//如果选择了阻塞任务 if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) //如果调度器被锁 { CPU_CRITICAL_EXIT(); //开中断 *p_err = OS_ERR_SCHED_LOCKED; //错误类型为"调度器被锁" return ((void *)0); //返回 0(有错误),停止执行 } } /* 如果调度器未被锁 */ OS_CRITICAL_ENTER_CPU_EXIT(); //锁调度器,重开中断 OS_Pend((OS_PEND_DATA *)0, //阻塞当前任务,等待消息 (OS_PEND_OBJ *)0, (OS_STATE )OS_TASK_PEND_ON_TASK_Q, (OS_TICK )timeout); OS_CRITICAL_EXIT_NO_SCHED(); //解锁调度器(无调度) OSSched(); //调度任务 /* 当前任务(获得消息队列的消息)得以继续运行 */ CPU_CRITICAL_ENTER(); //关中断 switch (OSTCBCurPtr->PendStatus) //根据任务的等待状态分类处理 { case OS_STATUS_PEND_OK: //如果任务已成功获得消息 p_void = OSTCBCurPtr->MsgPtr; //提取消息内容地址 *p_msg_size = OSTCBCurPtr->MsgSize; //提取消息长度 if (p_ts != (CPU_TS *)0) //如果 p_ts 非空 { *p_ts = OSTCBCurPtr->TS; //获取任务等到消息时的时间戳 #if OS_CFG_TASK_PROFILE_EN > 0u OSTCBCurPtr->MsgQPendTime = OS_TS_GET() - OSTCBCurPtr->TS; if (OSTCBCurPtr->MsgQPendTimeMax < OSTCBCurPtr->MsgQPendTime) { OSTCBCurPtr->MsgQPendTimeMax = OSTCBCurPtr->MsgQPendTime; } #endif } *p_err = OS_ERR_NONE; //错误类型为"无错误" break; //跳出 case OS_STATUS_PEND_ABORT: //如果等待被中止 p_void = (void *)0; //返回消息内容为空 *p_msg_size = (OS_MSG_SIZE)0; //返回消息大小为 0 if (p_ts != (CPU_TS *)0) //如果 p_ts 非空 { *p_ts = (CPU_TS )0; //清零 p_ts } *p_err = OS_ERR_PEND_ABORT; //错误类型为"等待被中止" break; //跳出 case OS_STATUS_PEND_TIMEOUT: //如果等待超时 default: //或者任务状态超出预 p_void = (void *)0; //返回消息内容为空 *p_msg_size = (OS_MSG_SIZE)0; //返回消息大小为 0 if (p_ts != (CPU_TS *)0) //如果 p_ts 非空 { *p_ts = OSTCBCurPtr->TS; } *p_err = OS_ERR_TIMEOUT; //错误类为"等待超时" break; //跳出 } CPU_CRITICAL_EXIT(); //开中断 return (p_void); //返回消息内容地址 } #endif
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# 任务消息队列实验
任务通知代替消息队列是在μC/OS中创建了两个任务,其中一个任务是用于接收任务消息,另一个任务发送任务消息.两个任务独立运行,发送消息任务每秒发送一次任务消息,接收任务在就一直在等待消息,一旦获取到消息通知就通过串口发送出来.
定义任务空间栈的大小以及任务栈数组,任务控制块和优先级.
定义任务函数
任务启动函数编写
结果现象