12. 任务消息队列¶
12.1. 任务消息队列的基本概念¶
任务消息队列跟任务信号量一样,均隶属于某一个特定任务,不需单独创建,任务在则任务消息队列在,只有该任务才可以获取(接收)这个任务消息队列的消息,其他任务只能给这个任务消息队列发送消息,却不能获取。任务消息队列与前面讲解的(普通)消息队列极其相似,只是任务消息队列已隶属于一个特定任务,所以它不具有等待 列表,在操作的过程中省去了等待任务插入和移除列表的动作,所以工作原理相对更简单一点,效率也比较高一些。
注意:本书所提的“消息队列”,若无特别说明,均指前面的(普通)消息队列(属于内核对象),而非任务消息队列。
通过对任务消息队列的合理使用,可以在一定场合下替代uCOS的消息队列,用户只需向任务内部的消息队列发送一个消息而不用通过外部的消息队列进行发送,这样子处理就会很方便并且更加高效,当然,凡事都有利弊,任务消息队列虽然处理更快,RAM开销更小,但也有限制:只能指定消息发送的对象,有且只有一个任务接收消息 ;而内核对象的消息队列则没有这个限制,用户在发送消息的时候,可以采用广播消息的方式,让所有等待该消息的任务都获取到消息。
在实际任务间的通信中,一个或多个任务发送一个消息给另一个任务是非常常见的,而一个任务给多个任务发送消息的情况相对比较少,前者就很适合采用任务消息队列进行传递消息,如果任务消息队列可以满足设计需求,那么尽量不要使用普通消息队列,这样子设计的系统会更加高效。
(内核对象)消息队列是用结构体OS_Q来管理的,包含了管理消息的元素 MsgQ 和管理等待列表的元素 PendList 等。而任务消息队列的结构体成员变量就少了PendList,因为等待任务消息队列只有拥有任务消息队列本身的任务才可以进行获取,故任务消息队列不需要等待列表的相关数据结构,具体见 代码清单26-1。
注意:想要使用任务消息队列,就必须将OS_CFG_TASK_Q_EN宏定义配置为1,该宏定义位于os_cfg.h文件中。
1 2 3 4 5 6 7 8 | struct os_msg_q
{
OS_MSG *InPtr; (1)
OS_MSG *OutPtr; (2)
OS_MSG_QTY NbrEntriesSize; (3)
OS_MSG_QTY NbrEntries; (4)
OS_MSG_QTY NbrEntriesMax; (5)
};
|
任务消息队列的运作机制与普通消息队列一样,没什么差别。
12.2. 任务消息队列的函数接口讲解¶
12.2.1. 任务消息队列发送函数OSTaskQPost()¶
函数 OSTaskQPost()用来发送任务消息队列,参数中有指向消息要发送给的任务控制块的指针,任何任务都可以发送消息给拥有任务消息队列的任务(任务在被创建的时候,要设置参数 q_size 大于 0), 其源码具体见 代码清单26-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 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 | #if OS_CFG_TASK_Q_EN > 0u //如果使能了任务消息队列
void OSTaskQPost (OS_TCB *p_tcb, (1) //目标任务
void *p_void, (2) //消息内容地址
OS_MSG_SIZE msg_size, (3) //消息长度
OS_OPT opt, (4) //选项
OS_ERR *p_err) (5) //返回错误类型
{
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); (6)
return; //返回
}
#endif
OS_TaskQPost(p_tcb, //将消息直接发布
p_void,
msg_size,
opt,
ts,
p_err); (7)
}
#endif
|
代码清单26-2 (1):目标任务。
代码清单26-2 (2):任务消息内容指针。
代码清单26-2 (3):任务消息的大小。
代码清单26-2 (4):发送的选项。
代码清单26-2 (5):用于保存返回的错误类型。
代码清单26-2 (6):如果使能了中断延迟发布,并且如果该函数在中断中被调用,就先将消息先发布到中断消息队列。
代码清单26-2 (7):调用OS_TaskQPost()函数将消息直接发送,其源码具体见 代码清单26-3。
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 | #if OS_CFG_TASK_Q_EN > 0u//如果使能了任务消息队列
void OS_TaskQPost (OS_TCB *p_tcb, //目标任务
void *p_void, //消息内容地址
OS_MSG_SIZE msg_size, //消息长度
OS_OPT opt, //选项
CPU_TS ts, //时间戳
OS_ERR *p_err) //返回错误类型
{
CPU_SR_ALLOC(); //使用到临界段(在关/开中断时)时必需该宏,该宏声明和
//定义一个局部变量,用于保存关中断前的 CPU 状态寄存器
// SR(临界段关中断只需保存SR),开中断时将该值还原。
OS_CRITICAL_ENTER(); //进入临界段
if (p_tcb == (OS_TCB *)0) (1)//如果 p_tcb 为空
{
p_tcb = OSTCBCurPtr; //目标任务为自身
}
*p_err = OS_ERR_NONE; //错误类型为“无错误”
switch (p_tcb->TaskState) (2)//根据任务状态分类处理
{
case OS_TASK_STATE_RDY: //如果目标任务没等待状态
case OS_TASK_STATE_DLY:
case OS_TASK_STATE_SUSPENDED:
case OS_TASK_STATE_DLY_SUSPENDED:
OS_MsgQPut(&p_tcb->MsgQ, //把消息放入任务消息队列
p_void,
msg_size,
opt,
ts,
p_err); (3)
OS_CRITICAL_EXIT(); //退出临界段
break; //跳出
case OS_TASK_STATE_PEND: //如果目标任务有等待状态
case OS_TASK_STATE_PEND_TIMEOUT:
case OS_TASK_STATE_PEND_SUSPENDED:
case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:
if (p_tcb->PendOn == OS_TASK_PEND_ON_TASK_Q) //如果等的是任务消息队列
{
OS_Post((OS_PEND_OBJ *)0, //把消息发布给目标任务
p_tcb,
p_void,
msg_size,
ts); (4)
OS_CRITICAL_EXIT_NO_SCHED(); //退出临界段(无调度)
if ((opt & OS_OPT_POST_NO_SCHED) == (OS_OPT)0u) //如果要调度任务
{
OSSched(); //调度任务
}
}
else(5)//如果没在等待任务消息队列
{
OS_MsgQPut(&p_tcb->MsgQ, //把消息放入任务消息队列
p_void,
msg_size,
opt,
ts,
p_err);
OS_CRITICAL_EXIT(); //退出临界段
}
break; //跳出
default: (6)//如果状态超出预期
OS_CRITICAL_EXIT(); //退出临界段
*p_err = OS_ERR_STATE_INVALID; //错误类型为“状态非法”
break; //跳出
}
}
#endif
|
代码清单26-3 (1):如果目标任务为空,则表示将任务消息释放给自己,那么p_tcb就指向当前任务。
代码清单26-3 (2):根据任务状态分类处理。
代码清单26-3 (3):如果目标任务没等待状态,就调用OS_MsgQPut()函数将消息放入队列中,执行完毕就退出,OS_MsgQPut()源码具体见代码清单20‑13。
代码清单26-3 (4):如果目标任务有等待状态,那就看看是不是在等待任务消息队列,如果是的话,调用OS_Post()函数把任务消息发送给目标任务,其源码具体见代码清单20‑14。
代码清单26-3 (5):如果任务并不是在等待任务消息队列,那么调用OS_MsgQPut()函数将消息放入任务消息队列中即可。
代码清单26-3 (6):如果状态超出预期,返回错误类型为“状态非法”的错误代码。
任务消息队列的发送过程是跟消息队列发送过程差不多,先检查目标任务的状态,如果该任务刚刚好在等待任务消息队列的消息,那么直接让任务脱离等待状态即可。如果任务没有在等待任务消息队列的消息,那么就将消息插入到要发送消息的任务消息队列。
任务消息队列发送函数的使用实例具体见 代码清单26-4。
1 2 3 4 5 6 7 8 9 | OS_ERR err;
/* 发布消息到任务 AppTaskPend */
OSTaskQPost ((OS_TCB *)&AppTaskPendTCB, //目标任务的控制块
(void *)"YeHuo uCOS-III", //消息内容
(OS_MSG_SIZE )sizeof ( "YeHuo uCOS-III" ), //消息长度
(OS_OPT )OS_OPT_POST_FIFO,
//发布到任务消息队列的入口端
(OS_ERR *)&err); //返回错误类型
|
12.2.2. 任务消息队列获取函数OSTaskQPend()¶
与OSTaskQPost()任务消息队列发送函数相对应,OSTaskQPend()函数用于获取一个任务消息队列,函数的参数中没有指定哪个任务获取任务消息,实际上就是当前执行的任务,当任务调用了这个函数就表明这个任务需要获取任务消息,OSTaskQPend()源码具体见 代码清单26_5_。
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 141 142 143 144 145 146 | #if OS_CFG_TASK_Q_EN > 0u//如果使能了任务消息队列
void *OSTaskQPend (OS_TICK timeout, (1)//等待期限(单位:时钟节拍)
OS_OPT opt, (2) //选项
OS_MSG_SIZE *p_msg_size, (3) //返回消息长度
CPU_TS *p_ts, (4) //返回时间戳
OS_ERR *p_err) (5) //返回错误类型
{
OS_MSG_Q *p_msg_q;
void *p_void;
CPU_SR_ALLOC(); //使用到临界段(在关/开中断时)时必需该宏,该宏声明和
//定义一个局部变量,用于保存关中断前的 CPU 状态寄存器
// SR(临界段关中断只需保存SR),开中断时将该值还原。
#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; (6)//获取当前任务的消息队列
p_void = OS_MsgQGet(p_msg_q, //从队列里获取一个消息
p_msg_size,
p_ts,
p_err); (7)
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); //返回消息内容
}
/* 如果获取消息不成功(队列里没有消息) */ (8)
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(9)//如果选择了堵塞任务
{
if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) //如果调度器被锁
{
CPU_CRITICAL_EXIT(); //开中断
*p_err = OS_ERR_SCHED_LOCKED; //错误类型为“调度器被锁”
return ((void *)0); //返回0(有错误),停止执行
}
}
/* 如果调度器未被锁 */
OS_CRITICAL_ENTER_CPU_EXIT(); (10)//锁调度器,重开中断
OS_Pend((OS_PEND_DATA *)0, (11)//阻塞当前任务,等待消息
(OS_PEND_OBJ *)0,
(OS_STATE )OS_TASK_PEND_ON_TASK_Q,
(OS_TICK )timeout);
OS_CRITICAL_EXIT_NO_SCHED(); //解锁调度器(无调度)
OSSched(); (12)//调度任务
/* 当前任务(获得消息队列的消息)得以继续运行 */
CPU_CRITICAL_ENTER(); (13)//关中断
switch (OSTCBCurPtr->PendStatus) //根据任务的等待状态分类处理
{
case OS_STATUS_PEND_OK: (14)//如果任务已成功获得消息
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: (15)//如果等待被中止
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: (16)//如果等待超时,
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); (17)//返回消息内容地址
}
#endif
|
代码清单26-5 (1):指定超时时间(单位:时钟节拍)。
代码清单26-5 (2):获取任务消息队列的选项。
代码清单26-5 (3):返回消息大小。
代码清单26-5 (4):返回时间戳。
代码清单26-5 (5):返回错误类型。
代码清单26-5 (6):获取当前任务的消息队列保存在p_msg_q变量中。
代码清单26-5 (7):调用OS_MsgQGet()函数从消息队列获取一个消息,其源码具体见代码清单20‑17,如果获取消息成功,则返回指向消息的指针。
代码清单26-5 (8):如果获取消息不成功(任务消息队列里没有消息),并且如果用户选择了不阻塞任务,那么返回错误类型为“缺乏阻塞”的错误代码,然后退出。
代码清单26-5 (9):如果选择了堵塞任务,先判断一下调度器是否被锁,如果被锁了也就不能继续执行。
代码清单26-5 (10):如果调度器未被锁,系统会锁调度器,重开中断。
代码清单26-5 (11):调用OS_Pend()函数将当前任务脱离就绪列表,并根据用户指定的阻塞时间插入到节拍列表,但是不会插入队列等待列表,然后打开调度器,但不进行调度,OS_Pend()源码具体见代码清单20‑18。
代码清单26-5 (12):进行一次任务调度。
代码清单26-5 (13):程序能执行到这里,就说明大体上有两种情况,要么是任务获取到消息了;任务还没获取到消息(任务没获取到消息的情况有很多种),无论是哪种情况,都先把中断关掉再说,然后根据当前运行任务的等待状态分类处理。
代码清单26-5 (14):如果任务状态是OS_STATUS_PEND_OK,则表示任务获取到消息了,那么就从任务控制块中提取消息,这是因为在发送消息给任务的时候,会将消息放入任务控制块的MsgPtr成员变量中,然后继续提取消息大小,如果p_ts非空,记录获取任务等到消息时的时间戳,返 回错误类型为“无错误”的错误代码,跳出switch语句。
代码清单26-5 (15):如果任务在等待(阻塞)重被中止,则返回消息内容为空,返回消息大小为0,返回错误类型为“等待被中止”的错误代码,跳出switch语句。
代码清单26-5 (16):如果任务等待(阻塞)超时,说明等待的时间过去了,任务也没获取到消息,则返回消息内容为空,返回消息大小为0,返回错误类型为“等待超时”的错误代码,跳出switch语句。
代码清单26-5 (17):打开中断,返回消息内容。
12.3. 任务消息队列实验¶
任务通知代替消息队列是在UCOS中创建了两个任务,其中一个任务是用于接收任务消息,另一个任务发送任务消息。两个任务独立运行,发送消息任务每秒发送一次任务消息,接收任务在就一直在等待消息,一旦获取到消息通知就把消息打印在串口调试助手里,具体见 代码清单26-6。
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 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 | /*
***********************************************************************
* @file main.c
* @author fire
* @version V1.0
* @date 2019-xx-xx
* @brief 任务消息队列
***********************************************************************
* @attention
*
* 实验平台:野火 i.MXRT1052开发板
* 论坛 :http://www.firebbs.cn
* 淘宝 :http://firestm32.taobao.com
*
***********************************************************************
*/
/*
*************************************************************************
* 包含的文件
*************************************************************************
*/
//ucosiii系统相关
#include <cpu.h>
#include <lib_mem.h>
#include <os.h>
#include <bsp_os.h>
#include <bsp_clk.h>
#include <bsp_int.h>
#include"os_app_hooks.h"
#include"app_cfg.h"
//板级驱动
#include"bsp.h"
#include"fsl_debug_console.h"
#include"board.h"
#include"pin_mux.h"
#include"clock_config.h"
/*
*************************************************************************
* LOCAL DEFINES
*************************************************************************
*/
/*
*************************************************************************
* 任务控制块TCB
*************************************************************************
*/
static OS_TCB AppTaskStartTCB;//任务控制块
static OS_TCB AppTaskPostTCB;
static OS_TCB AppTaskPendTCB;
/*
************************************************************************
* 任务堆栈
************************************************************************
*/
static CPU_STK AppTaskStartStk[APP_TASK_START_STK_SIZE];//任务堆栈
static CPU_STK AppTaskPostStk [ APP_TASK_POST_STK_SIZE ];
static CPU_STK AppTaskPendStk [ APP_TASK_PEND_STK_SIZE ];
/*
************************************************************************
* 函数原型
************************************************************************
*/
static void AppTaskStart (void *p_arg);//任务函数声明
static void AppTaskPost ( void * p_arg );
static void AppTaskPend ( void * p_arg );
/*
************************************************************************
* 函数名 : main
* 描述 : 标准的C函数入口
* 形参 : 无
* 返回值 : 无
************************************************************************
*/
int main (void)
{
OS_ERR err;
BSP_ClkInit();/* 初始化系统时钟 */
BSP_IntInit();/* 初始化RAM中断向量表. */
BSP_OS_TickInit();/* 初始化内核计时器 */
Mem_Init();/* 初始化内存管理模块 */
CPU_IntDis();/* 禁用所有中断 */
CPU_Init();/* 初始化uC/CPU相关 */
OSInit(&err);/*初始化uC / OS-III */
if (err != OS_ERR_NONE) {
while (1);
}
App_OS_SetAllHooks();//设置所有应用程序钩子函数
/* 创建起始任务 */
OSTaskCreate((OS_TCB *)&AppTaskStartTCB,//任务控制块地址
(CPU_CHAR *)"App Task Start",//任务名称
(OS_TASK_PTR ) AppTaskStart,//任务函数
(void *) 0,//传递给任务函数(形参p_arg)的实参
(OS_PRIO ) APP_TASK_START_PRIO,//任务的优先级
(CPU_STK *)&AppTaskStartStk[0],//任务堆栈的基地址
(CPU_STK_SIZE) APP_TASK_START_STK_SIZE / 10,//任务堆栈空
下1/10时限制其增长
(CPU_STK_SIZE) APP_TASK_START_STK_SIZE,//任务堆栈空间(单
sizeof(CPU_STK))
(OS_MSG_QTY ) 5u,//任务可接收的最大消息数
(OS_TICK ) 0u,//任务的时间片节拍数(0表默认值
fg_TickRate_Hz/10)
(void *) 0,//任务扩展(0表不扩展)
(OS_OPT )(OS_OPT_TASK_STK_CHK |
OPT_TASK_STK_CLR), //任务选项
(OS_ERR *)&err);
if (err != OS_ERR_NONE) {
while (1);
}
OSStart(&err);//启动多任务管理(交由uC/OS-III控制)
while (DEF_ON) {
;
}
}
/*
*************************************************************************
************
* 函数名:AppTaskStart
* 描述 : 这是一个启动任务,在多任务系统启动后,必须初始化滴答计数器(在 BSP_Init
现)。
* 形参 : p_arg 是OSTaskCreate()在创建该任务时传递过来的形参。
* 返回值 : 无
* 注意 : 1) 第一行代码 (void)p_arg; 是为了防止编译器报错,因为形参p_arg并没有
*************************************************************************
************
*/
static void AppTaskStart (void *p_arg)
{
OS_ERR err;
(void)p_arg;
OS_TRACE_INIT();//初始化uC / OS-III跟踪记录器
BSP_OS_TickEnable();//启用滴答计时器和中断
BSP_Init();//板级初始化
#if OS_CFG_STAT_TASK_EN > 0u
OSStatTaskCPUUsageInit(&err);//无需任务运行即可计算CPU容量
#endif
#ifdef CPU_CFG_INT_DIS_MEAS_EN
CPU_IntDisMeasMaxCurReset();
#endif
/* 创建 AppTaskPost 任务 */
OSTaskCreate((OS_TCB *)&AppTaskPostTCB,//任务控制块地址
(CPU_CHAR *)"App Task Post",//任务名称
(OS_TASK_PTR ) AppTaskPost,//任务函数
(void *) 0,//传递给任务函数(形参p_arg)的实参
(OS_PRIO ) APP_TASK_POST_PRIO,//任务的优先级
(CPU_STK *)&AppTaskPostStk[0],//任务堆栈的基地址
(CPU_STK_SIZE) APP_TASK_POST_STK_SIZE / 10,//任务堆栈空间剩下1/10时
其增长
(CPU_STK_SIZE) APP_TASK_POST_STK_SIZE,//任务堆栈空间(单位:
of(CPU_STK))
(OS_MSG_QTY ) 5u,//任务可接收的最大消息数
(OS_TICK ) 0u,//任务的时间片节拍数(0表默认值OSCfg_TickRate_Hz/10)
(void *) 0,//任务扩展(0表不扩展)
(OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR), //任务
(OS_ERR *)&err);//返回错误类型
/* 创建 AppTaskPend 任务 */
OSTaskCreate((OS_TCB *)&AppTaskPendTCB,//任务控制块地址
(CPU_CHAR *)"App Task Pend",//任务名称
(OS_TASK_PTR ) AppTaskPend,//任务函数
(void *) 0,//传递给任务函数(形参p_arg)的实参
(OS_PRIO ) APP_TASK_PEND_PRIO,//任务的优先级
(CPU_STK *)&AppTaskPendStk[0],//任务堆栈的基地址
(CPU_STK_SIZE) APP_TASK_PEND_STK_SIZE / 10,//任务堆栈空间剩下1/10时
其增长
(CPU_STK_SIZE) APP_TASK_PEND_STK_SIZE,//任务堆栈空间(单位:
of(CPU_STK))
(OS_MSG_QTY ) 5u,//任务可接收的最大消息数
(OS_TICK ) 0u,//任务的时间片节拍数(0表默认值OSCfg_TickRate_Hz/10)
(void *) 0,//任务扩展(0表不扩展)
(OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR), //任务
(OS_ERR *)&err);//返回错误类型
OSTaskDel ( & AppTaskStartTCB, & err );//删除起始任务本身,该任务不再运行
}
/*
*************************************************************************
********
* POST TASK
*************************************************************************
********
*/
static void AppTaskPost ( void * p_arg )
{
OS_ERR err;
(void)p_arg;
while (DEF_TRUE) {//任务体
/* 发送消息到任务 AppTaskPend */
OSTaskQPost ((OS_TCB *)&AppTaskPendTCB,//目标任务的控制块
(void *)"Fire uC/OS-III",//消息内容
(OS_MSG_SIZE )sizeof ( "Fire uC/OS-III" ), //消息
(OS_OPT )OS_OPT_POST_FIFO,//发送到任务消息队列的
端
(OS_ERR *)&err);//返回错误类型
OSTimeDlyHMSM ( 0, 0, 1, 0, OS_OPT_TIME_DLY, & err );//每20ms发
次
}
}
/*
*************************************************************************
**********
* PEND TASK
*************************************************************************
**********
*/
static void AppTaskPend ( void * p_arg )
{
OS_ERR err;
OS_MSG_SIZE msg_size;
CPU_TS ts;
CPU_INT32U cpu_clk_freq;
char * pMsg;
(void)p_arg;
cpu_clk_freq = BSP_ClkFreqGet(kCLOCK_CpuClk);//获取CPU时钟,时间戳是以
钟计数
while (DEF_TRUE) {//任务体
/* 阻塞任务,等待任务消息 */
pMsg = OSTaskQPend ((OS_TICK )0,//无期限等待
(OS_OPT )OS_OPT_PEND_BLOCKING, //没有
就阻塞任务
(OS_MSG_SIZE *)&msg_size,//返回消息长度
(CPU_TS *)&ts,//返回消息被发送的时间戳
(OS_ERR *)&err);//返回错误类型
ts = OS_TS_GET() - ts;//计算消息从发送到被接收的时间差
LED1_TOGGLE;//切换LED1的亮灭状态
PRINTF ( "\r\n接收到的消息的内容为:%s,长度是:%d字节。",
pMsg, msg_size );
PRINTF ( "\r\n任务消息从被发送到被接收的时间差是%dus\r\n",
ts / ( cpu_clk_freq / 1000000 ) );
}
}
|