嵌入式实时操作系统的设计与开发

时钟管理

在RTOS中,时钟具有非常重要的作用,通过时钟可实现延时任务、周期性触发任务执行、任务有限等待的计时。

大多数嵌入式系统有两种时钟源,分别为实时时钟RTC(Real-Time Clock)和定时器/计数器。

实时时钟一般是靠电池供电,即使系统断电,也可以维持日期和时间。由于实时时钟独立于操作系统,因此也被称为硬件时钟,它为整个系统提供一个时间标准。

嵌入式处理器通常集成了多个定时器或计数器,实时内核需要一个定时器作为系统时钟,并由内核控制系统时钟工作。

一般而言,实时时钟是系统时钟的基准,实时内核通过读取实时时钟来初始化系统时钟,此后,二者保持同步运行,共同维持系统时钟。
因此,系统时钟只有当系统运行起来以后才有效,并且由实时内核完全控制。

定时器一般由晶体振荡器提供周期信号源,并通过程序对其计数寄存器进行设置,让其产生固定周期的脉冲,每次脉冲的产生都触发一个时钟中断,时钟中断的频率既是系统的心跳,也称为时基或Tick,Tick的大小决定了整个系统的时间粒度。

晶体振荡器提供周期信号源,它通过Bus总线连接到CPU核上,开发人员设定计数寄存器的初始值,随后,每一个晶体振荡器输入信号都会导致该值增加,当计数器溢出时,就产生一个输出脉冲(Pulse),输出脉冲可以用来触发CPU核上的一个中断。

输出脉冲是RTOS的硬件基础,因为它将送到中断控制器上,产生中断信号,触发时钟中断,由时钟中断服务程序维持系统时钟的正常工作。

实时内核的时间管理以系统时钟为基础,通过Tick处理程序来实现。
定时器产生中断后,RTOS将响应并执行器中断服务程序,并在中断服务程序中调用Tick处理函数,它作为实时内核的一部分,与具体的定时器/计数器无关,由系统时钟中断服务程序调用,使内核具有对不同定时器/计数器的适应性。

在内核层初始化中,重要的一步就是通过acoral_intr_attach()将时钟中断服务程序与Ticks处理程序进行绑定。

这样,每当定时器产生一个输出脉冲(Pulse),输出脉冲就向CPU发出一个时钟中断,找到内核层对应的时钟中断号,最终将执行该中断号对应的服务程序,即Ticks处理函数aCoral_Ticks_entry()。

内核时钟管理的绝大部分工作都在aCoral_Ticks_entry()进行的,如线程延迟操作time_delay_deal()、超时处理timeout_delay_deal()、与调度策略相关的操作(如时间片轮转调度)等。

如果任务采用时间片轮转调度,则需要再Ticks处理程序中对当前正在运行的任务已执行的时间进行"+1"操作。执行完该操作后,如果任务的已执行时间同任务时间片相等,则表示任务使用完一个时间片的执行时间,需要通过acoral_sched()触发重调度。

如果开发人员在线程中调用acoral_delay_thread()对线程进行延迟操作,则会让当前运行线程从运行(Running)状态切换到挂起(Suspend)状态,并将其挂载到一个等待队列"acoral_list_t waiting",这里的等待队列也称为时间等待链,用来存放需要延迟处理的任务。

接下来每当定时器产生一个Tick,Tick处理函数aCoral_Ticks_entry()的time_delay_deal()需要对时间等待链中线程的剩余等待时间进行"减1"操作,如果某个线程的剩余等待时间被减到了0,则将该线程从等待队列中移出,挂载到就绪队列,通过acoral_sched()触发重调度。

如,开发人员用acoral_delay_thread()将线程A、B、C、D分别延迟。

通常情况下,每当定时器产生一个Tick,time_delay_deal()会对时间等待链中的每一个结点进行"减1"操作。

若时间等待链的结点数越多,时钟中断的Tick处理函数的计算开销就比较大,从而降低系统性能。

为了提高系统性能,减小计算开销,可采用差分时间等待链来描述延迟队列。

队列中某个结点的值是相对于前一个结点的时间差。

当采用查分时间等待链后,每当时钟中断产生一个Tick,只需对队列头部结点进行"减1"操作,当减到0时,就将其从等待链中取下,后续结点将成为新的头部,并且被激活。
等待链其它结点的值保持不变,无须对每一个结点进行"减1"操作,这样可减小计算开销。

如果有新线程要进行延迟操作,需要往差分时间等待链中插入新的结点,如线程E要延迟7Ticks。

只需要在线程B(3+2)和C(3+2+5)中插入线程E即可。再修改结点C的值即可(10-7)。

c 复制代码
acoral_list_t time_delay_queue; //线程延时队列,调用线程delay相关函数的线程都会被加到这个队列上,等待一段具体时间后重新被唤醒。

acoral_list_t timeout_queue; //aCoral获取资源(互斥量)超时等待队列,即在timeout时间内获取即成功,否则超时失败。
void time_delay_deal()
{
	acoral_list_t *tmp,*tmp1,*head;
	acoral_thread_t *thread;
	head = &time_delay_queue; //获取时间等待链的头部
	if(acoral_list_empty(head))
		return;
	thread = list_entry(head->next,acoral_thread_t,waiting); //获取时间等待链头结点对应的TCB地址
	thread->delay--; //对时间等待链头结点对应线程delay成员的剩余等待时间
	for(tmp=head->next;tmp!=head;)
	{
		if(thread->delay > 0) //如果头结点线程delay成员大于0,则退出
			break;
		//delay=0,线程延迟结束
		tmp1 = tmp>next;
		acoral_list_del(&thread->waiting); //将该线程从时间等待链中删除
		tmp = tmp1;
		thread->state &= ~ACORAL_THREAD_STATE_DELAY; //将线程切换为就绪态	
		acoral_rdy_thread(thread); //将其挂到就绪队列上
	}
}

waiting成员主要用来将线程结构挂到相应链表队列,是一个双向链表结构。

c 复制代码
struct acoral_list{
	struct acoral_list *next,*prev;
};

以就绪队列ready为例,当用户调用了acoral_rdy_thread或acoral_resume_thread接口时,就会将线程挂到就绪队列acoral_ready_queue上。

c 复制代码
acoral_list_t policy_list;
void acoral_policy_delay_deal()
{
	acoral_list_t *tmp,*head;
	acoral_sched_policy_t *policy_ctrl;
	head = &policy_list;
	tmp = head;
	for(tmp=head->next;tmp!=head;tmp=tmp->next)
	{
		policy_ctrl = list_entry(tmp,acoral_sched_policy_list,list);
		if(policy_ctrl->delay_deal!=NULL)
			policy_ctrl->delay_deal();
	}
}
c 复制代码
void period_policy_init(void)
{
	acoral_init_list(&period_delay_queue);
	period_policy.type=ACORAL_SCHED_POLICY_PERIOD;
	period_policy.policy_thread_init=period_policy_thread_init;
	period_policy.policy_thread_release=period_policy_thread_release;
	period_policy.delay_deal=period_delay_deal;
	period_policy.name="period";
	acoral_register_sched_policy(&period_policy);
}
c 复制代码
typedef struct{
	unsigned int time; //线程周期,单位为ms
	void (*route)(void *args); //线程函数
	void *args; //线程函数的参数
}period_private_data_t; //周期线程私有数据块,用于保存自己的周期、函数体,以便在新周期重新挂载线程时使用。
c 复制代码
acoral_list_t period_delay_queue; //周期线程专用延时队列,只要是周期线程,就会被挂载到这个队列上,
void period_delay_deal()
{
	acoral_list_t *tmp,*tmp1,*head;
	acoral_thread_t *thread;
	period_private_data_t * private_data;
	head = &period_delay_queue;
	if(acoral_list_empty(head))
		return;
	thread = list_entry(head->next,acoral_thread_t,waiting);
	thread->delay--;
	for(tmp=head->next;tmp!=head;){
		thread = list_entry(tmp,acoral_thread_t,waiting);
		if(thread->delay > 0)
			break;
		private->data = thread->private_data;
		tmp1 = tmp->next; //保存下一个周期延时队列上的结点
		acoral_list_del(&thread->waiting);
		tmp = tmp1;
		if(thread->state & ACORAL_THREAD_STATE_SUSPEND){
			thread->stack = (unsigned int *)((char *)thread->stack_buttom+thread->stack_size - 4);
			HAL_STACK_INIT(&thread->stack,private_data->route,period_thread_exit,private_data->args);
			acoral_rdy_thread(thread);
		}
		period_thread_delay(thread,private_data->time);
	}
}
相关推荐
深圳市青牛科技实业有限公司12 分钟前
【青牛科技】应用方案|D2587A高压大电流DC-DC
人工智能·科技·单片机·嵌入式硬件·机器人·安防监控
Mr.谢尔比1 小时前
电赛入门之软件stm32keil+cubemx
stm32·单片机·嵌入式硬件·mcu·信息与通信·信号处理
LightningJie1 小时前
STM32中ARR(自动重装寄存器)为什么要减1
stm32·单片机·嵌入式硬件
西瓜籽@2 小时前
STM32——毕设基于单片机的多功能节能窗控制系统
stm32·单片机·课程设计
远翔调光芯片^138287988724 小时前
远翔升压恒流芯片FP7209X与FP7209M什么区别?做以下应用市场摄影补光灯、便携灯、智能家居(调光)市场、太阳能、车灯、洗墙灯、舞台灯必看!
科技·单片机·智能家居·能源
极客小张5 小时前
基于STM32的智能充电桩:集成RTOS、MQTT与SQLite的先进管理系统设计思路
stm32·单片机·嵌入式硬件·mqtt·sqlite·毕业设计·智能充电桩
m0_739312878 小时前
【STM32】项目实战——OV7725/OV2604摄像头颜色识别检测(开源)
stm32·单片机·嵌入式硬件
嵌入式小章8 小时前
基于STM32的实时时钟(RTC)教学
stm32·嵌入式硬件·实时音视频
TeYiToKu8 小时前
笔记整理—linux驱动开发部分(9)framebuffer驱动框架
linux·c语言·arm开发·驱动开发·笔记·嵌入式硬件·arm
基极向上的三极管9 小时前
【AD】3-4 在原理图中放置元件
嵌入式硬件