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

news2024/11/26 17:26:21

中断系统结构

在RTOS中,中断是与具体硬件平台关联度最大的部分,为了实现高可移植性、可配置性,中断子系统依照aCoral的整体结构来设计,划分为HAL(硬件抽象层)和内核层。
在HAL层先将各种中断汇拢,对于硬件相关的公共部分进行前序处理,然后在内核层根据中断源进行后序处理。

HAL层的处理将复位、未定义指令、软中断、预取指终止、数据终止、快速中断等异常的异常号交给special_entry进行相应的专门处理。
而将普通中断IRQ的异常都赋值给HAL_INTR_ENTRY,HAL_INTR_ENTRY根据中断控制器产生的中断号做一些必要的公共中断,如压栈、临时关中断(如果硬件没有自动关中断)、读取中断向量号等,然后跳转到hal_all_entry执行,交内核层处理,当内核层处理完毕后,中断处理程序调用acoral_intr_exit进行中断退出操作。

中断初始化

在中断能够正确响应并处理以前,必须做相关初始化工作,中断初始化是aCoral内核各模块初始化(acoral_module_init())过程中的首要工作,由acoral_intr_sys_init()完成。

/*中断初始化函数*/
void acoral_intr_sys_init()
{
	acoral_u32 i;
	/*关中断*/
	acoral_intr_disable();
	/*中断嵌套标志初始化*/
	HAL_INTR_NESTING_INIT();
	/*中断底层初始化函数*/
	HAL_INTR_INIT();
	/*对于每个中断,设置默认的服务处理程序,然后屏蔽该中断*/
	for(i = HAL_INTR_MIN; i <= HAL_INTR_MAX; i++){
		acoral_set_intr_enter(i,hal_intr_ack);
		acoral_set_intr_exit(i,NULL);
		acoral_set_intr_mask(i,hal_intr_mask);
		acoral_set_intr_unmask(i,hal_intr_unmask);
		intr_table[i].isr = acoral_default_isr;
		intr_table[i].type = ACORAL_COMM_INTR'
		acoral_intr_mask(i);
	}
	/*特殊中断初始化*/
	HAL_INTR_SPECIAL();
}

在中断初始化时,先要将中断关闭,否则可能会出现异常,然后是中断嵌套标志初始化。
然后进行中断底层硬件初始化,这是针对ARM S3C2440所做的初始化。
根据以上代码,中断的初始化过程也分为HAL层初始化和内核层初始化,其中,acoral_intr_disable()和以“HAL”打头的是HAL层初始化,剩下的是内核层初始化。

  1. HAL层初始化是针对具体硬件平台的中断特性做相关设置。acoral_intr_disable()属于HAL层初始化,因为中断关闭是与具体硬件平台相关的,且最终实现是HAL_INTR_DISABLE()。接下来的HAL_INTR_NESTING_INIT()是通过定义hal_intr_nesting_init_com()来实现的。
/*中断嵌套初始化*/
void hal_intr_nesting_init_comm(){
	acoral_u32 i;
	intr_nesting=0;
}

然后通过HAL_INTR_INIT()完成中断优先级、屏蔽寄存器及中断向量表私有函数等初始化。

void hal_intr_init(){
	acoral_u32 i;
	rPRIOORITY = 0x00000000; // 使用默认的固定的优先级
	rINTMOD = 0x00000000; //所有中断均为IRQ中断
	rEINTMSK = 0xffffffff; // 屏蔽所有的外部中断
	rINTMSK = 0xffffffff; //屏蔽所有中断
}

通过设置中断控制器的相关寄存器来确定优先级、类型,屏蔽所有中断等。
关中断是将ARM处理器程序当前状态寄存器的第6为置为1,此时ARM成为关中断模式,不会对任何IRQ做出响应。而屏蔽所有中断是设置中断控制器的屏幕寄存器每一位都置为1,用于屏蔽所有中断,用户可以通过改变屏幕寄存器的某些位来屏蔽某些中断,不至于关闭所有中断。

  1. 内核层初始化
    内核层的中断初始化设置内核层的中断向量表。
    中断进入时的操作由hal_intr_ack实现,这里主要是清除中断Pending位。由于第4号中断到第7号中断复用了中断号4,第8号中断到第23号中断复用了中断号5,所以中断进入时操作除了设置寄存器INTPND外,还需设置寄存器EINTPND。
void hal_intr_ack(acoral_u32 vector){

        if((vector>3) && (vector<8)){
               rEINTPND &= ~(1<<vector);
               vector = 4;
           }
        else if((vector>7) && (vector<24)){
               rEINTPND &= ~(1<<vector);
               vector = 5;
           }
        else if(vector > 23)
               vector -= 18;
   
        
        rSRCPND = 1<<vector; //清除SRCPND寄存器
		rINTPND = 1<<vector; //清除INTPND寄存器		
}

中断屏蔽时的操作由hal_intr_mask实现

oid hal_intr_mask(acoral_vector vector){

         if((vector>3) && (vector<8)){
               rEINTMSK |=(1<<vector);
               vector = 4;
           }
         else if((vector>7) && (vector<24)){
               rEINTMSK |=(1<<vector);
               vector = 5;
           }
         else if(vector > 23)
               vector -= 18; 

         rINTMSK |= (1<<vector); //屏蔽中断
}

通过向中断屏蔽寄存器(INTMSK)某位写入1来屏蔽相应中断。
通过向中断屏蔽寄存器(INTMSK)某位写入0来打开相应中断。

void hal_intr_unmask(acoral_vector vector){
          if((vector>3) && (vector<8)){
               rEINTMSK &=~(1<<vector);
               vector = 4;
           }
         else if((vector>7) && (vector<24)){
               rEINTMSK &=~(1<<vector);
               vector = 5;
           }
         else if(vector > 23)
               vector -= 18; 

         rINTMSK &=~(1<<vector);			/*开启中断*/
}

时钟中断实例

Ticks由中断触发产生,每隔一定时间就会触发一次时钟中断,用来计时,线程的延时函数就要利用Ticks时钟。
当开发板完成启动和aCoral的加载后,系统会通过代码进入acoral_start(),时钟初始化是acoral_start()的重点工作之一。

  1. 时钟HAL层初始化主要是设置Ticks时钟中断相关的寄存器,主要涉及时钟模式、时钟计数值、时钟开启等寄存器操作。
void hal_ticks_init(){
  	rTCON = rTCON & (~0xf) ;	/* clear manual update bit, stop Timer0*/
    rTCFG0 &= 0xFFFF00;
 	rTCFG0 |= 0xF9;				/* prescaler等于249*/
 	rTCFG1 &= ~0x0000F;   
 	rTCFG1 |= 0x2;			   /*divider等于8,则设置定时器4的时钟频率为25kHz*/

   	rTCNTB0 = PCLK /(8*(249+1)*ACORAL_TICKS_PER_SEC);
   	rTCON = rTCON & (~0xf) |0x02;              	/* updata*/
	rTCON = rTCON & (~0xf) |0x09; 			/* 启动定时器0*/
}

  1. 时钟内核层初始化。内核层初始化是给时钟中断重新设定ISRs。在之前初始化中,为每个中断设定了默认的中断服务程序acoral_default_isr()。
    时钟的内核层初始化由acoral_ticks_init()完成,首先将Ticks的初始值设为0,然后重新设置Ticks的ISRs。
void acoral_ticks_init(){
	ticks = 0;
	acoral_intr_attach(HAL_TICKS_INTR,acoral_ticks_entry);// 注册Ticks的处理函数
	HAL_TICKS_INIT();
	acoral_intr_unmask(HAL_TICKS_INTR);
	return;
}
#define HAL_TICKS_INTR 28 //时钟中断号,对于定时器0的中断
#define HAL_TICKS_INIT() hal_ticks_init()
/*将服务函数isr绑定到中断向量Vector,返回0表示成功*/
acoral_32 acoral_intr_attach(acoral_vector vector, void (*isr)(acoral_vector)){
	if(intr_table[vector].type != ACORAL_RT_INTR){
		intr_table[vector].isr = isr;
	}else{
		HAL_INTR_ATTACH(vector,isr);
	}
	return 0;
}

时钟管理

在RTOS中,时钟具有非常重要的作用,通过时钟可实现延时任务、周期性触发任务执行、任务有限等待的计时、软定时器的定时管理、确认超时以及与时间相关的调度操作。

大多数嵌入式系统有两种时钟源,分别为实时时钟RTC(Real-Time Clock)和定时器/计数器。
实时时钟一般靠电池供电,即使系统断电,也可以维持日期和时间。由于实时时钟独立于操作系统,因此也被称为硬件时钟,它为整个系统提供一个时间标准。
嵌入式处理器还集成了多个定时器和计数器,实时内核需要一个定时器作为系统时钟,并由内核控制系统时钟工作,系统时钟的最小粒度是由应用和操作系统的特点决定的。

在不同RTOS中,实时时钟和系统时钟之间的关系是不一样的,实时时钟和系统时钟之间的关系也决定了RTOS的时钟运行机制。一般而言,实时时钟是系统时钟的基准,实时内核通过读取实时时钟来初始化系统时钟,此后,两者保持同步运行,共同维持系统时间。
因此,系统时钟并不是真正意义上的时钟,只有当系统运行起来以后才有效,并且由实时内核完全控制。

嵌入式系统的时钟源的选择

根据硬件的不同,可以是专门的硬件定时器,也可以是来自AC交流电的50/60Hz信号频率。
定时器一般是由晶体振荡器提供周期信号源,并通过程序对其计数寄存器进行设置,让其产生固定周期的脉冲,而每次脉冲的产生都将触发一个时钟中断,时钟中断的频率既是系统的心跳,也称为时基或Tick,Tick的大小决定了整个系统的时间粒度。

对于RTOS,时钟心跳频率一般为10~100次/秒,甚至更高。
在这里插入图片描述
这是一个简单的定时器/计数器示意图,晶体振荡器提供周期信号源,它通过总线连接到CPU核上,开发人员编程设计计数寄存器Counter的初始值,随后,每一个晶体振荡器的输入信号都会导致该值增加,当计数寄存器溢出时,就产生一个输出脉冲(Pulse),输出脉冲可以用来触发CPU核上的一个中断,输出脉冲是RTOS时钟的硬件基础,因为它将送到中断控制器上,产生中断信号,触发时钟中断,由时钟中断服务程序维持系统时钟的正常工作。

实时内核的时间管理以系统时钟为基础,通过Tick处理程序来实现,定时器产生中断后,RTOS将响应并执行其中断服务程序,在中断服务程序中调用Tick处理函数。

在内核层初始化中,重要的一步就是通过acoral_intr_attach()将时钟中断服务程序与Ticks处理程序进行绑定。
每当定时器产生一个输出脉冲,输出脉冲就向CPU核发出一个时钟中断。经过HAL层的中断处理后,就进入内核层的中断响应,找到内核层对应的时钟中断号,最终将执行该中断号对应的服务程序,即acoral_ticks_entry()。
RTOS内核时钟管理的绝大部分都是在acoral_ticks_entry()进行的,如线程延迟操作time_delay_deal()、超时处理timeout_delay_deal()、与调度策略相关的操作等。

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

如果开发人员在线程中调用acoral_delay_thread()对线程进行延迟操作,则acoral_delay_thread()会将当前运行线程从运行状态(Running)切换到挂起状态(Suspend),并将其挂载到一个等待队列“acoral_list_t waiting”,这里的等待队列也被称为时间等待链,用它来存放需要延迟处理的任务。
接下来,每当定时器产生一个Tick,Tick处理函数acoral_ticks_entry()的time_delay_deal()对时间等待链中线程的剩余等待时间进行“减一”操作,如果某个线程的剩余等待时间被减到0,则将该线程从等待队列中移出,挂载到就绪队列,并通过acoral_sched()触发重调度。

例如,开发人员用acoral_delay_thread()将线程A、线程B、线程C、线程D分别延时3、5、10和14个Ticks。
通常情况下,每当定时器产生一个Tick,time_delay_deal()会对时间等待链中的每一个结点进行“减一”操作。若时间等待链的结点数越多,时钟中断的Tick处理函数的计算开销就比较大,从而降低系统性能。
所以采用差分时间队列来描述延迟队列,队列中某个结点的值是相对于前一个结点的时间差,因此线程B所在的结点为2,该值是相对于线程A的相对值。
采用差分时间等待链后,每当时钟中断产生一个Tick,只需对队列头部结点进行“减一”操作,当减到0时,就从等待链中取下,后续结点将成为新的头部,并且被激活。
该过程中,等待链其它结点的值保持不变,无须对每一个结点进行“减一”操作,这样可减小计算开销。

差分时间等待队列时RTOS采用的一种性能优化技术,实现比较简单。

void time_delay_deal(){
	acoral_list_t *tmp,*tmp1,*head;
	acoral_thread_t *thread;
	head = &time_delay_queue.head;
	if(acoral_list_empty(head)){
		return;
	}
	thread = list_entry(head->next,acoral_thread_t,waiting);
	thread->delay--;
	for(tmp=head->next;rmp!=head;){
		thread=list_entry(tmp,acoral_thread_t,waiting);
		if(thread->delay>0){
			break;
		}
		tmp1=tmp->next;
		acoral_list_del(&thread->waiting);
		tmp=tmp1;
		thread->state&=~ACORAL_THREAD_STATE_DELAY; //将1位清零
		acoral_rdy_thread(thread);
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/153757.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第三周周二1.10

-A 添加规则 -I 插入 -F 清空 -L 查看 -p 调整默认规则 -D 删除规则 dport : -j ACCEPT DROP REJECT LOG /var/log/messages -n 以数字的形式显示结果 -v 详细信息 -x 精确的 -line-number 行号 删除&#xff1a;指…

2023 年你应该知道的 10 个开源项目

精心策划的 2023 年 GitHub 上最有趣的开发工具和项目列表。1.NetBeansNetBeans 是一个开源的集成开发环境&#xff0c;因其支持多种编程语言和平台而受到开发人员的欢迎。动图2.OpenCVOpenCV 是一个用于图像和视频处理的开源计算机视觉库。它广泛用于对象检测、面部识别和机器…

嬴图 | 走进 Ultipa Manager 之 高可视化

Ultipa Manager是嬴图数据库管理系统基于网页端的应用。自2019年至今&#xff0c;已迭代最新至v4.2版本。本系列&#xff0c;笔者将分三篇文章&#xff0c;引导大家走进嬴图之Ultipa Manager&#xff0c;详细了解其高可视化、数据科学家工具箱、便捷的数据迁移3大亮点功能&…

分享91个NET源码,总有一款适合您

NET源码 分享91个NET源码&#xff0c;总有一款适合您 91个NET源码下载链接&#xff1a;https://pan.baidu.com/s/1dqb9XgiiVfsVkq-wqKt3Kg?pwd275d 提取码&#xff1a;275d 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&…

mybatis插入mysql数据库PersistenceException 数据库连接超时

mybatis插入mysql部分数据失败mybatis插入mysql数据库PersistenceException1、异常堆栈信息&#xff1a;2、问题原因&#xff1a;3、问题排查3.1 查看数据库连接信息3.2 问题解决3.2.1 Spring项目可以在配置文件里面设置3.2.2 修改conn改成局部变量mybatis插入mysql数据库Persi…

python 语法(空行、变量、if条件控制、循环语句)编码示例

文章目录前言python 语法(空行、变量、if条件控制、循环语句)编码示例1 空行&#xff0c;在python中空行也是代码2 单个变量赋值、多个变量赋值3 if条件控制4 循环语句4.1 while循环4.2 for 循环前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff…

C++程序的编译与运行

C和C语言类似&#xff0c;也要经过编译和链接后才能运行。 下图是 C/C 代码生成可执行文件的过程&#xff1a; C源文件的后缀 C语言源文件的后缀非常统一&#xff0c;在不同的编译器下都是.c。C 源文件的后缀则有些混乱&#xff0c;不同的编译器支持不同的后缀&#xff0c;下…

新应用——设备巡检管理,系统化更具稳定性

对于生产制造型企业来说&#xff0c;随着企业逐步发展&#xff0c;产量增大&#xff0c;生产设备的稳定性和安全性就成为企业经济效益的关键。设备巡检是有效保证设备安全和稳定的重要环节。通过设备巡检&#xff0c;定期掌握各台设备的运行情况及周围环境的变化&#xff0c;尽…

算法竞赛百日——快速排序 - 分治

本文已收录于专栏 &#x1f332;《百日算法竞赛》&#x1f332; 目录 本文已收录于专栏 &#x1f332;《百日算法竞赛》&#x1f332; 快速排序 解题思路 ​ 思路分析&#xff1a; 模拟&#xff1a; AC_Code 二分查找 用二分法求 平方根 二分模板 快速排序 给定你…

纯前端导出Excel并修改样式

之前写过一篇前端导出&#xff1a;Vue实现导出功能&#xff08;无后端配合&#xff09; &#xff0c;但是当时没考虑到样式的问题&#xff0c;后来要求导出的Excel单元格的样式也需要调整&#xff0c;尤其是宽度&#xff0c;第一想到的就是xlsx-style这个包&#xff0c;之前也没…

Python基础篇---生成器和模块

生成器对象 生成器对象其本质是自定义迭代器&#xff0c;就是需要我们自己写代码产生的迭代器。 生成器对象也是节省存储空间的 特性与迭代器对象一致。 def index():print(第一次输出)yield 1print(第二次输出)yield 2 1 2 3 4 5 6 7 8 9 10 当我们没有加括号调用之前&…

学习JavaScript,有哪些好的博客或者网站推荐?

前言 特意制作了一个Js整体的学习路线以及相关知识点博客讲解以及各类网站学习的资源整合&#xff0c;希望对你有帮助哈~ 废话少说&#xff0c;我们直接进入正题&#xff0c;先放上路线导图&#xff1a; 部分重要知识点 基础性知识 声明变量 null 和 undefined 详解JavaS…

code warrior IDE win10 64位安装保姆级教程

以下安装步骤是在win10 64位系统运行程序默认安装在C盘第1步&#xff1a;第2步&#xff1a;将PE_Plugin.dll文件复制到com文件夹中&#xff0c;路径如下&#xff1a;C:\Program Files(x86)\Freescale\CodeWarrior for Microcontrollers V6.3\bin\plugins\com第3步&#xff1a;以…

Blender里的三种绑定 (三)骨骼

文章目录Blender里的三种绑定.骨骼.骨骼基础.骨骼的父子关系.挤出骨骼.细分骨骼.骨骼分层.骨骼分组.骨骼约束.骨骼被遮挡.对称骨骼.为模型绑定骨骼.刚体物体&#xff0c;只跟随骨骼的移动&#xff0c;不随骨骼移动发生形变&#xff0c;如机械.软体物体&#xff0c;跟随骨骼的移…

手写RPC框架第7版-框架容错性相关设计

源代码地址&#xff1a;https://github.com/lhj502819/IRpc/tree/v8 系列文章&#xff1a; 注册中心模块实现路由模块实现序列化模块实现过滤器模块实现自定义SPI机制增加框架的扩展性的设计与实现基于线程和队列提升框架并发处理能力框架容错性相关设计通过SpringBoot-Starte…

数据结构:关于空间复杂度的例题计算

1、计算冒泡排序的空间复杂度 答案&#xff1a;该程序空间复杂度为O(1)。 解析&#xff1a;该程序在栈空间所申请的临时变量空间只有三个&#xff0c;也就是看成常数个&#xff0c;所以是O(1)。如下图所示 2、动态开辟N个数的数组空间复杂度 答案&#xff1a;该程序空间复杂度…

【UE4 第一人称射击游戏】31-更好的UI界面

素材资料地址&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1epyD62jpOZg-o4NjWEjiyg密码&#xff1a;jlhr上一篇&#xff1a;【UE4 第一人称射击游戏】30-简单的任务提示功能本篇效果&#xff1a;步骤&#xff1a;在UE中新建一个“HUD_Export”文件夹&#xff0c;将所…

《码出高效:java开发手册》七 - 并发与多线程

前言 现代CPU运算速度以百亿计&#xff0c;家用计算机和操作系统也是数十进程&#xff0c;数百线程&#xff0c;程序相应也需要采用多线程和并发的技术 并发和并行&#xff1a;并发是指某个时间段&#xff0c;多任务处理&#xff1b;并行是指同时处理多任务的能力&#xff1b;…

接口测试项目实战与经典面试题解析,挑战 BAT 大厂必会!

近年来&#xff0c;接口测试技术体系已在各大互联网企业落地普及&#xff0c;各种新接口框架不断涌现&#xff0c;业界也形成了不少成熟方案和成功案例。当前 BAT 大厂在招聘测试人员时&#xff0c;接口测试技能和项目经验是必考重点&#xff0c;直接影响到职级评定和薪资水平&…

2023/1/10 Vue学习笔记6 - 路由基本使用

1 路由的简介-router 1、路由就是一组key-value的对应关系。 2、多个路由&#xff0c;需要经过路由器的管理。 SPA (single page web application&#xff09;应用 - 单页面web应用 {"key":"/class","value":"班级组件" }1.vue-rout…