1 /* 2 ************************************************************************************************* 3 * uC/OS-II实时控制内核 4 * 主要的包含文件 5 * 时钟管理项 6 * 文 件: OS_TIME.C 时钟管理代码 7 * 作 者: Jean J. Labrosse 8 * 中文注解: 钟常慰 zhongcw @ 126.com 整理:lin-credible 译注版本:1.0 请尊重原版内容 9 ********************************************************************************************************* 10 */ 11 12 #ifndef OS_MASTER_FILE //是否已定义OS_MASTER_FILE主文件 13 #include "includes.h" //包含"includes.h"文件,部分C语言头文件的汇总打包文件 14 #endif //定义结束 15 16 /* 17 ********************************************************************************************************* 18 * 将一个任务延时若干个时钟节拍(DELAY TASK 'n' TICKS (n from 0 to 65535)) 19 * 20 * 描述: 将一个任务延时若干个时钟节拍。如果延时时间大于0, 系统将立即进行任务调度. 延时时间的长度 21 * 可从0到65535个时钟节拍。延时时间0表示不进行延时,函数将立即返回调用者。延时的具体时间依 22 * 赖于系统每秒钟有多少时钟节拍(由文件SO_CFG.H中的常量OS_TICKS_PER_SEC设定)。 23 * 24 * 附加:调用该函数会使uC/OS-ii进行一次任务调度,并且执行下一个优先级最高的就绪态任务。任务调用 25 * OSTimeDly()后,一旦规定的时间期满或者有其它的任务通过调用OSTimeDlyResume()取消了延时, 26 * 它就会马上进入就绪状态。注意,只有当该任务在所有就绪任务中具有最高的优先级时,它才会立即 27 * 运行。 28 * 参数: ticks 为要延时的时钟节拍数。(一个1 到65535之间的数) 29 * 30 * 返回: 无 31 * 注意: 注意到延时时间0表示不进行延时操作,而立即返回调用者. 为了确保设定的延时时间,建议用户设定 32 * 的时钟节拍数加1。例如,希望延时10个时钟节拍,可设定参数为11。 33 ********************************************************************************************************* 34 */ 35 void OSTimeDly (INT16U ticks) //任务延时函数(时钟节拍数) 36 { 37 #if OS_CRITICAL_METHOD == 3 //中断函数被设定为模式3 38 OS_CPU_SR cpu_sr; 39 #endif 40 41 42 if (ticks > 0) { //如果延时设定为0值,表示不想对任务延时,返回调用任务 43 OS_ENTER_CRITICAL(); //关闭中断 44 if ((OSRdyTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0) { 45 OSRdyGrp &= ~OSTCBCur->OSTCBBitY; 46 } 47 //非0值会使得任务延时函数OSTimeDly()将当前任务从就绪表中移除 48 OSTCBCur->OSTCBDly = ticks; //接着,这个延时节拍数会被保存在当前任务的OS_TCB中 49 OS_EXIT_CRITICAL(); //打开中断 50 OS_Sched(); //既然任务已经不再处于就绪状态,(任务调度) 51 //任务调度程序会执行下一个优先级最高的就绪任务。 52 } 53 } 54 /*$PAGE*/ 55 /* 56 ********************************************************************************************************* 57 * 将一个任务延时若干时间(DELAY TASK FOR SPECIFIED TIME) 58 * 59 * 描述: 将一个任务延时若干时间。延时的单位是小时、分、秒、毫秒。所以使用OSTimeDlyHMSM()比OSTimeDly() 60 * 更方便。调用OSTimeDlyHMSM()后,如果延时时间不为0,系统将立即进行任务调度。 61 * 62 * 参数: hours 为延时小时数,范围从0-255. (max. is 255) 63 * minutes 为延时分钟数,范围从0-59. (max. 59) 64 * seconds 为延时秒数,范围从0-59. (max. 59) 65 * milli 为延时毫秒数,范围从0-999. (max. 999) 66 * 需要说明的是,延时操作函数都是以时钟节拍为为单位的。实际的延时时间是时钟节拍的整数倍。例如系统 67 * 每次时钟节拍间隔是10ms,如果设定延时为5ms,将不产生任何延时操作,而设定延时15ms,实际的延时是 68 * 两个时钟节拍,也就是20ms。 69 * 70 * 附加:调用OSTimeDlyHMSM()函数也会使uC/OS-ii进行一次任务调度,并且执行下一个优先级最高的就绪态任务。 71 * 任务调用OSTimeDlyHMSM()后,一旦规定的时间期满或者有其它的任务通过调用OSTimeDlyResume()取消了延 72 * 时(恢复延时的任务OSTimeDlyResume()),它就会马上处于就绪态。同样,只有当该任务在所有就绪态任务 73 * 中具有最高的优先级时,它才会立即运行。 74 * 75 * 返回: OS_NO_ERR 函数调用成功; 76 * OS_TIME_INVALID_MINUTES 参数错误,分钟数大于59; 77 * OS_TIME_INVALID_SECONDS 参数错误,秒数大于59; 78 * OS_TIME_INVALID_MS 参数错误,毫秒数大于999; 79 * OS_TIME_ZERO_DLY 四个参数全为0. 80 * 81 * 注意: OSTimeDlyHMSM(0,0,0,0)表示不进行延时操作, 而立即返回调用者. 另外, 如果延时总时间超过65535个 82 * 时钟节拍,将不能用OSTimeDlyResume()函数终止延时并唤醒任务。 83 ********************************************************************************************************* 84 */ 85 86 #if OS_TIME_DLY_HMSM_EN > 0 //允许生成OSTimeDlyHMSM() 函数代码 87 INT8U OSTimeDlyHMSM (INT8U hours, INT8U minutes, INT8U seconds, INT16U milli) 88 { //将一个任务延时若干时间(设定时、分、秒、毫秒) 89 INT32U ticks; //定义节拍数 90 INT16U loops; //定义循环次数 91 //当设定值大于0值(是否时有效值) 92 if (hours > 0 || minutes > 0 || seconds > 0 || milli > 0) { 93 if (minutes > 59) { //如果分钟>59,则返回参数错误,分钟数大于59; 94 return (OS_TIME_INVALID_MINUTES); 95 } 96 if (seconds > 59) { //如果秒>59,则返回参数错误,秒数大于59; 97 return (OS_TIME_INVALID_SECONDS); 98 } 99 if (milli > 999) { //如果毫秒>999,则返回参数错误,毫秒数大于999; 100 return (OS_TIME_INVALID_MILLI); 101 } 102 /* 103 * 因为uC/OS-ii只知道节拍,所以节拍总数是从指定的时间中计算出来的。很明显,程序清单中的程序并不是十分 104 * 有效的。笔者只是用这种方法告诉大家一个公式,这样用户就可以知道怎样计算总的节拍数了。真正有意义的只 105 * 是OS_TICKS_PER_SEC。下段程序决定了最接近需要延迟的时间的时钟节拍总数。500/OS_TICKS_PER_SECOND的值 106 * 基本上与0.5个节拍对应的毫秒数相同。例如,若将时钟频率(OS_TICKS_PER_SEC)设置成100Hz(10ms),4ms的延时 107 * 不会产生任何延时!而5ms的延时就等于延时10ms。 108 */ 109 //计算程序输入的总的节拍数 110 ticks = ((INT32U)hours * 3600L + (INT32U)minutes * 60L + (INT32U)seconds) * OS_TICKS_PER_SEC 111 + OS_TICKS_PER_SEC * ((INT32U)milli + 500L / OS_TICKS_PER_SEC) / 1000L; 112 /* 113 * uC/OS-ii支持的延时最长为65,535个节拍。要想支持更长时间的延时,OSTimeDlyHMSM()确定了用户想延时多少 114 * 次超过65,535个节拍的数目和剩下的节拍数。例如,若OS_TICKS_PER_SEC的值为100,用户想延时15分钟,则 115 * OSTimeDlyHMSM()会延时15x60x100=90,000个时钟。这个延时会被分割成两次32,768个节拍的延时(因为用户只能 116 * 延时65,535个节拍而不是65536个节拍)和一次24,464个节拍的延时。在这种情况下,OSTimeDlyHMSM()首先考虑 117 * 剩下的节拍,然后是超过65,535的节拍数(即两个32,768个节拍延时)。 118 */ 119 loops = (INT16U)(ticks / 65536L); //计算得商得倍数(多少个65536 时钟节拍) 120 ticks = ticks % 65536L; //计算得余数 121 OSTimeDly((INT16U)ticks); //先作余数清除 122 while (loops > 0) { //如果节拍数超过65536 个时钟节拍 123 OSTimeDly(32768); //执行两次延时,实现共65536 个时钟节拍 124 OSTimeDly(32768); 125 loops--; //继续减1,直到为0 126 } 127 return (OS_NO_ERR); //返回(函数调用成功) 128 } 129 return (OS_TIME_ZERO_DLY); //返回(四个参数全为0) 130 } 131 #endif 132 /*$PAGE*/ 133 /* 134 ********************************************************************************************************* 135 * 唤醒一个用OSTimeDly()或OSTimeDlyHMSM()函数延时的任务(RESUME A DELAYED TASK) 136 * 137 * 描述: 唤醒一个用OSTimeDly()或OSTimeDlyHMSM()函数延时的任务 138 * OSTimeDlyResume()函数不能唤醒一个用OSTimeDlyHMSM()延时,且延时时间总计超过65535个时钟节拍的 139 * 任务。例如,如果系统时钟为100Hz,OSTimeDlyResume()不能唤醒延时OSTimeDlyHMSM(0,10,55,350) 140 * 或更长时间的任务。 141 * (OSTimeDlyHMSM(0,10,55,350)共延时[10 minutes * 60 + (55+0.35)seconds ] * 100 = 65,535次时 142 * 钟节拍---译者注) 143 * 144 * 参数: prio 为指定要唤醒任务的优先级 145 * 146 * 返回: OS_NO_ERR 函数调用成功 147 * OS_PRIO_INVALID 参数指定的优先级大于OS_LOWEST_PRIO 148 * OS_TIME_NOT_DLY 要唤醒的任务不在延时状态 149 * OS_TASK_NOT_EXIST 指定的任务不存在 150 * 151 * 注意:用户不应该用OSTimeDlyResume()去唤醒一个设置了等待超时操作,并且正在等待事件发生的任务。操作的 152 * 结果是使该任务结束等待,除非的确希望这么做。 153 ********************************************************************************************************* 154 * uC/OS-ii允许用户结束延时正处于延时期的任务。延时的任务可以不等待延时期满,而是通过其它任务取 155 * 消延时来使自己处于就绪态。这可以通过调用OSTimeDlyResume()和指定要恢复的任务的优先级来完常慰 156 * 实际上,OSTimeDlyResume()也可以唤醒正在等待事件(参看任务间的通讯和同步)的任务,虽然这一点并 157 * 没有提到过。在这种情况下,等待事件发生的任务会考虑是否终止等待事件。 158 * OSTimeDlyResume()的代码如程序,它首先要确保指定的任务优先级有效。接着,OSTimeDlyResume()要确 159 * 认要结束延时的任务是确实存在的。如果任务存在,OSTimeDlyResume()会检验任务是否在等待延时期满。 160 * 只要OS_TCB域中的OSTCBDly包含非0值就表明任务正在等待延时期满,因为任务调用了OSTimeDly(), 161 * OSTimeDlyHMSM()或其它在第六章中所描述的PEND函数。然后延时就可以通过强制命令OSTCBDly为0来取消 162 * 。延时的任务有可能已被挂起了,这样的话,任务只有在没有被挂起的情况下才能处于就绪状态。当上面 163 * 的条件都满足后,任务就会被放在就绪表中。这时,OSTimeDlyResume()会调用任务调度程序来看被恢复 164 * 的任务是否拥有比当前任务更高的优先级。这会导致任务的切换。 165 ********************************************************************************************************* 166 */ 167 168 #if OS_TIME_DLY_RESUME_EN > 0 //允许生成OSTimeDlyResume() 函数代码 169 INT8U OSTimeDlyResume (INT8U prio) //唤醒一个用OSTimeDly()或OSTimeDlyHMSM()函数的任务(优先级) 170 { 171 #if OS_CRITICAL_METHOD == 3 //中断函数被设定为模式3 172 OS_CPU_SR cpu_sr; 173 #endif 174 OS_TCB *ptcb; //定义任务控制块优先级表变量 175 176 177 if (prio >= OS_LOWEST_PRIO) { //当任务指针大于等于最低(大)优先级时,确保优先级有效 178 return (OS_PRIO_INVALID); //返回(参数指定的优先级大于OS_LOWEST_PRIO) 179 } 180 OS_ENTER_CRITICAL(); //关闭中断 181 ptcb = (OS_TCB *)OSTCBPrioTbl[prio]; //ptcb = 任务控制块优先级表当前优先级 182 if (ptcb != (OS_TCB *)0) { //确保要结束的延时的任务是确实存在的 183 if (ptcb->OSTCBDly != 0) { //如果任务存在,程序会检验任务是否在等待延时期满,只要任务 184 //控制块的.OSTCBDly域非0值,就表明任务正在等待延时期满,因 185 //为任务调用了OSTimeDly()、OSTimeDlyHMSM()或其它的pend函数 186 ptcb->OSTCBDly = 0; //通过使 .OSTCBDly为0而取消延时 187 if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == 0x00) { 188 //延时的任务有可能已被挂起,然而任务在没有被挂起的情况下, 189 //才能处于就绪态 190 OSRdyGrp |= ptcb->OSTCBBitY; 191 OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX; 192 //当上面的条件都满足时,任务就会被放在就绪表中 193 OS_EXIT_CRITICAL(); //打开中断 194 OS_Sched(); //任务调度程序会执行下一个优先级最高的就绪任务(任务调度) 195 } else { //不然 196 OS_EXIT_CRITICAL(); //打开中断 197 } 198 return (OS_NO_ERR); //函数调用成功 199 } else { //否则 200 OS_EXIT_CRITICAL(); //打开中断 201 return (OS_TIME_NOT_DLY); //返回(要唤醒的任务不在延时状态) 202 } 203 } 204 OS_EXIT_CRITICAL(); //打开中断 205 return (OS_TASK_NOT_EXIST); //返回(指定的任务不存在) 206 } 207 #endif 208 /*$PAGE*/ 209 /* 210 ********************************************************************************************************* 211 * 获取当前系统时钟数值(GET CURRENT SYSTEM TIME) 212 * 213 * 描述: 获取当前系统时钟数值。系统时钟是一个32位的计数器,记录系统上电后或时钟重新设置后的时钟计数。 214 * 215 * 附加:无论时钟节拍何时发生,uC/OS-ii都会将一个32位的计数器加1。这个计数器在用户调用OSStart()初始 216 * 化多任务和4,294,967,295个节拍执行完一遍的时候从0开始计数。在时钟节拍的频率等于100Hz的时候, 217 * 这个32位的计数器每隔497天就重新开始计数。用户可以通过调用OSTimeGet()来获得该计数器的当前值。 218 * 也可以通过调用OSTimeSet()来改变该计数器的值。OSTimeGet()和OSTimeSet()两个函数的代码如程序。 219 * 注意,在访问OSTime的时候中断是关掉的。这是因为在大多数8位处理器上增加和拷贝一个32位的数都需 220 * 要数条指令,这些指令一般都需要一次执行完毕,而不能被中断等因素打断 221 * 222 * 参数: 无 223 * 224 * 返回: 当前时钟计数(时钟节拍数) 225 ********************************************************************************************************* 226 */ 227 228 #if OS_TIME_GET_SET_EN > 0 //允许生成OSTimeGet() 函数代码 229 INT32U OSTimeGet (void) //获取当前系统时钟数值 230 { 231 #if OS_CRITICAL_METHOD == 3 //中断函数被设定为模式3 232 OS_CPU_SR cpu_sr; 233 #endif 234 INT32U ticks; //定义节拍数 235 236 237 OS_ENTER_CRITICAL(); //关闭中断 238 ticks = OSTime; //获取当前系统时钟数值 239 OS_EXIT_CRITICAL(); //打开中断 240 return (ticks); //返回系统时钟数值 241 } 242 #endif 243 244 /* 245 ********************************************************************************************************* 246 * 设置当前系统时钟数值(SET SYSTEM CLOCK) 247 * 248 * 描述: 设置当前系统时钟数值。系统时钟是一个32位的计数器, 记录系统上电后或时钟重新设置后的时钟计数. 249 * 250 * 参数: ticks 要设置的时钟数,单位是时钟节拍数. 251 * 252 * 返回: 无 253 ********************************************************************************************************* 254 */ 255 256 #if OS_TIME_GET_SET_EN > 0 //允许生成OSTimeSet() 函数代码 257 void OSTimeSet (INT32U ticks) //设置当前系统时钟数值 258 { 259 #if OS_CRITICAL_METHOD == 3 //中断函数被设定为模式3 260 OS_CPU_SR cpu_sr; 261 #endif 262 263 264 OS_ENTER_CRITICAL(); //关闭中断 265 OSTime = ticks; //设置当前系统时钟数值为多少个节拍数 266 OS_EXIT_CRITICAL(); //打开中断 267 } 268 #endif 269