skaiuijing

前言

前面已经说过了,Sparrow采用的是时间触发系统的设计,本章将会讲解时钟触发相关的算法。

重新回顾一下时间触发系统的定义:

时间触发系统的任务调度基于定时器中断,适用于周期性任务和确定性要求高的场景,如控制系统。它的内部有一个时钟,每隔一定时间间隔就会触发一次时间中断,每次时钟中断时,调度器决定是否需要切换任务。

systick时钟

systick时钟是为RTOS而设计的的一个中断时钟,它使得RTOS在不同架构之间的移植性难度大大减小。

image-20241029170450390

在前面的文章中,笔者曾经调试过Sparrow来了解RTOS内部的中断使用,当时得出的结论是:SysTick中断会在一定时间间隔响应,然后触发PendSV中断产生上下文切换:

image-20241029170253759

systick中断用法如下:

image-20241029171016883

如何使用时钟

定时上下文切换

参考上面的程序,我们可以先设置SysTick时钟,让它在一定时间间隔响应,然后在SysTick中断中加入PendSV中断的触发函数,这样就能达到定时上下文切换的目的。

除此之外,SysTick本身就是一个定时器,RTOS中需要定时的地方太多了,我们必须高效利用它的定时功能。

延时阻塞态

利用延时的时间

在Sparrow中,任务有就绪态、阻塞态、挂起态三种状态,任何任务只能有一种状态。延时就是一种阻塞态,表示它在等待某个事情的发生。

让我们想一想,在非RTOS的环境中,我们的延时一般都是空等,但是在RTOS环境中,任务被分解为线程的形式,每个线程尽量与其他线程并行执行,那么,我们是不是可以在其他线程延时等待时,把它移除就绪表,然后把时间让给其他线程执行呢?等时间一到,再把它加入就绪表。

这当然是可行的。

如何设计算法?

1
2
3
4
5
6
graph LR
Aa(时钟计时提供时间基准)
Ab(就绪表)--延时则加入延时表-->Ac(任务一)-->Bc(任务二)
Sb(延时表)--定期检查延时结束时间_时间到就加入就绪表-->Sc(任务A_延时结束时间)-->Sp(任务B_延时结束时间)


如图所示,我们需要一张延时表,当每个任务延时时,这个任务会被踢出就绪表,延时表会记录它延时到期的时间,每一次SysTIck中断时,时间基数就会加1,然后我们可以先比较延时表中的任务是否到时间,如果到时间,那么就从延时表删除,加入就绪表,如果没到,就继续等。

如何解决溢出的问题?

我们知道计算机中一个数的大小是有上限的,比如uint_32,最大只能到2^32 - 1。如果发生了溢出那怎么办呢?

笔者提供两种思路:

1.设置更大的计数单位

2.使用两个表进行维护。

当然,Sparrow使用的是后一种。

设置更大的计数单位

请读者想一想,我们每天都是24小时轮转,但我们是怎么表示时间的呢?是通过年、月、日来进行记录,也就是说,SysTick的计数也可以这样干,每溢出一次,前面的计数单位就加一,这样就能记录事情发生的先后了。

但是假设时间非常漫长,那么单片机会不断的设置更大的计数单位,从秒、分、时、日、月,一直到年,每一个数字都需要内存来存储,这样的方法,缺点是内存并不固定。

当然,这是考虑时间永久的情况下,实际生活中,1s计数1000次,约等于2^10,一分钟约等于计数2^16,一小时约等于2^22,三天约等于2^28,48天约等于2^32。

既然溢出一次需要48天,那么只要我们使用两个计数单位,那么上限不就是48*2^32天了?理论上就可以计数到产品损坏了。

使用两个表进行维护

正常情况下,任务的延时加上当前时间最多溢出一次,例如1 + 2^32 -1 会溢出一次,虽然1 + 2^32 - 1 + 2^32会溢出两次 ,但是这个时间段太长了,太离谱了,根本不可能被使用。既然最多溢出一次,那么,只要多一个表就可以解决溢出的问题。

1
2
3
4
graph LR

Aa(记录没有溢出的延时表)-->Ab(记录的时间)-->Ac(记录的时间)
Ba(记录溢出的延时表)-->Bb(记录的时间)-->Bc(记录的时间)

当时间溢出发生时,由于此时记录没有溢出的延时表的任务肯定已经完成了延时,也就是说,这张表现在是空的。

此时只有溢出的延时表上的延时需要完成,当溢出再次发生时,我们又需要一张表难道我们要重新申请一块内存来存储表吗?

显然,完全不需要,因为没有溢出的延时表是空的,我们可以重复利用它。现在,之前溢出的延时表变成了未溢出的延时表,之前未溢出的延时表被重新利用,拿来记录现在溢出的延时任务!

使用两个表进行维护,这就是Sparrow的延时策略。

总结

先讲解了时间触发型RTOS的设计,然后讲解SysTick时钟,它通常作为RTOS的定时器。为了充分利用定时器,引入延时阻塞的概念,解决了裸机中常见的延时空等待的问题。