TA的每日心情 | 奋斗 6 小时前 |
---|
签到天数: 174 天 [LV.7]常住居民III
荣誉版主
- 积分
- 2351
|
楼主 |
发表于 2023-7-27 16:56:46
|
显示全部楼层
本帖最后由 CosyOS 于 2023-8-3 01:32 编辑
CosyOS - 定时中断与定时查询
我们知道,MCU都有定时器和定时器中断,当定时器溢出时,CPU可以响应中断并调用相应的定时器中断服务程序。
然而,硬件定时器的数量是有限的,在较复杂的应用中往往是不够用的。所以,CosyOS提供了软件定时器,以弥补硬件定时器数量的不足,
同时也支持“定时服务”功能,来模拟定时器中断。
软件定时器
CosyOS的软件定时器分为三类,延时定时器、定时中断定时器、定时查询定时器。
延时定时器:每个任务各有一个,用来在此任务中实现阻塞延时或超时计数。
定时中断定时器:每个定时中断任务/钩子绑定一个定时中断定时器,至多可以有64个,定时器ID从0开始,用户通过调用API进行定时操作后,定时器将自动开始计数。
定时查询定时器:每个定时查询任务/钩子绑定一个定时查询定时器,至多可以有64个,定时器ID从0开始,用户通过调用API进行定时操作后,定时器将自动开始计数。
定时中断定时器、定时查询定时器,相当于是硬件定时器;
定时中断任务/钩子、定时查询任务/钩子,相当于是定时器中断服务程序。
MCU:
调用
中断响应 ——> 定时器中断服务程序
CosyOS:
调用
系统滴答 ——> 定时中断/查询钩子
恢复
系统滴答 ——> 定时中断/查询任务
定时中断任务/钩子:由用户设定定时时间,当定时器溢出时,系统将自动恢复/调用与其绑定的任务/钩子并自动重复定时(如果您已开启定时器的自动重装载)。
定时查询任务/钩子:由用户设定定时时间,当定时器溢出后,系统在每个滴答周期都会查询用户定义的事件,若事件为真,系统将自动恢复/调用与其绑定的任务/钩子并自动重复定时(如果您已开启定时器的自动重装载)。
系统初始化后,所有定时查询定时器的值均为零,相当于已经溢出,系统已经开始查询用户定义的事件了。
任务与钩子的区别:由于定时中断钩子、定时查询钩子都是在SysTick_Handler中被调用,所以只有极为精简的代码才适合创建钩子,耗时长的代码应创建为任务。再有,钩子相当于比任务具有更高的优先级,可更及时的被执行。
应用示例
定时查询任务应用于串口发送,定时中断任务应用于串口接收解析(裸机时一般用定时器中断),
下面以串口4为例加以说明(通讯数据为字符串):
/* 声明标志组:UART4_Send_Flag */
uExternFlagGroup
(
UART4_Send_Flag,
uDefFlagBit(f1);
uDefFlagBit(f2);
uDefFlagBit(f3);
uDefFlagBit(f4);
uDefVoidBits(4);
);
/* 创建标志组:UART4_Send_Flag */
uCreateFlagGroup(UART4_Send_Flag) = {0};
/* UART4中断 */
void UART4_Isr(void) interrupt 18 using 2
{
if(S4CON & S4TI)
{
S4CON &= ~S4TI;
if(UART4.Send.Temp[UART4.Send.i]) S4BUF = UART4.Send.Temp[UART4.Send.i++];
else
{
UART4.Send.i = 0;
iTimQry_ms(tmid_uart4_send_task, 50, 4); // UART4发送分包间隔50ms,4为svid
}
}
if(S4CON & S4RI)
{
S4CON &= ~S4RI;
if(UART4.Recv.i < sizeof_UART4_Recv_Temp-1) UART4.Recv.Temp[UART4.Recv.i++] = S4BUF;
iTimInt_ms(tmid_uart4_recv_task, 25, 5); // 25ms分包提前解析,外部设备发送至串口4的分包间隔要大于25ms+解析时间,5为svid
}
}
/*
* UART4发送任务
* 定时器ID:tmid_uart4_send_task
* 定时查询事件:滴答中查询标志组(UART4_Send_Flag)
* 定时器自动重装载:禁用
* 任务名称:uart4_send_task
* 任务优先级:200
* 任务栈size:0
* 安全运行时:0
* 私信参数:0
* 按自定义的发送优先级依次查询各标志位,为真则清除标志位并发送对应数据。
*/
uCreateTask_TimQry(tmid_uart4_send_task, tQueryFlagGroup(UART4_Send_Flag), false, uart4_send_task, 200, 0, 0, 0)
{
if(UART4_Send_Flag.f1)
{
uClearFlagBit(UART4_Send_Flag, f1);
/* 串口发送对应数据 */
}
else if(UART4_Send_Flag.f2)
{
uClearFlagBit(UART4_Send_Flag, f2);
/* 串口发送对应数据 */
}
else if(UART4_Send_Flag.f3)
{
uClearFlagBit(UART4_Send_Flag, f3);
/* 串口发送对应数据 */
}
else if(UART4_Send_Flag.f4)
{
uClearFlagBit(UART4_Send_Flag, f4);
/* 串口发送对应数据 */
}
uSuspendTasking;
uEndTasking;
}
/*
* UART4接收解析任务
* 定时器ID:tmid_uart4_recv_task
* 定时器自动重装载:禁用
* 任务名称:uart4_recv_task
* 任务优先级:210
* 任务栈size:0
* 安全运行时:0
* 私信参数:0
*/
uCreateTask_TimInt(tmid_uart4_recv_task, false, uart4_recv_task, 210, 0, 0, 0)
{
UART4.Recv.Temp[UART4.Recv.i] = '\0';
UART4_RECV_ANALYSIS(); // 调用解析函数
UART4.Recv.i = 0;
uSuspendTasking;
uEndTasking;
}
0、UART4发送用定时查询任务就实现了发送分包间隔时间最短为50ms,当有发送标志位为真时,系统将自动恢复任务。
1、UART4接收解析任务的优先级一定要高,并高于UART4发送任务。而后,在各发送处设置对应的标志位(并缓存数据,如果需要的话)。
2、当然对于本示例,还可有另外一个方法,就是双方都发送字符串尾0,接收中断时查0,为0就解析,就不需要定时中断任务了。
3、这个发送方法适用于复杂应用,如在多个中断或任务中都有相应的发送项,这时就需要在一个任务中统一处理各发送项,而不能在各处直接发送。
4、所有系统任务、定时中断任务、定时查询任务均由CosyOS自动启动,无需用户启动。
定时中断任务/钩子、定时查询任务/钩子的应用不仅限于此,在于自己发挥。总之,它们都是软件定时器中断,当你需要定时器中断时就可以考虑它们了。
本示例也充分展示了CosyOS事件标志组的应用,相较传统RTOS,其易用性不可描述。
|
|