《基于嵌入式实时操作系统的程序设计(4).ppt》由会员分享,可在线阅读,更多相关《基于嵌入式实时操作系统的程序设计(4).ppt(57页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、第第5章章中断服务程序设计中断服务程序设计 中断服务程序(ISR)是嵌入式应用系统获取各种事件的基本手段,而“事件”是实时性问 题的讨论基础和时间计算的起点。ISR的设计质量直接影响到系统的实时性指标和操作系统的工作效率。5.1 中断优先级安排中断优先级安排为不同的中断服务程序安排不同的优先级,在允许中断嵌套的情况下,最高优先级的中断总是能够得到及时响应。5.1.1 中断的优先级资源中断的优先级资源中断的优先级资源就是CPU的中断系统。以ARM7体系的CPU为例,最多可以有32个中断资源。每个具体的中断源可以将其设定为FIQ,使其具有最高优先级,但FIQ最好是分配给唯一的中断源,否则就失去意义
2、;也可以设定为向量IRQ,使其具有中等优先级,但向量IRQ的总数不能超过16个,这些中断源优先级的高低按向量编号从0(最高)到15(最低)排序;如果中断源的个数超过17个,则剩余的中断源只能设定为非向量IRQ,其优先级最低。操作系统本身必须使用一个定时器中断源来作为系统节拍中断,它是操作系统工作的基础。只要没有关闭中断,中断服务程序可以中断任何任务的运行,故可以将中断服务程序看成比最高优先级(0级)的任务还要优先的“任务”。5.1.2 中断优先级安排原则中断优先级安排原则中断源是系统及时获取异步事件的主要手段,其优先级安排原则如下:紧迫性:触发中断的事件允许耽误的时间越短,设定的中断优先级就越
3、高。例如脉冲峰值数据采样时耽误的时间越短,采样结果就越真实。紧迫性为最高原则。关键性:触发中断的事件越关键(重要),设定的中断优先级就越高。频繁性:触发中断的事件发生越频繁,设定的中断优先级就越高。频繁事件的间隔时间比较短,如不及时处理有可能遗漏。快捷性:在前三项条件相近时,ISR处理越快捷(耗时短),设定的中断优先级就越高。在发生中断嵌套时,耗时短的ISR嵌套在耗时长的ISR里时对耗时长的ISR的完成时间影响不明显,而耗时长的ISR嵌套在耗时短的ISR里时对耗时短的ISR的完成时间影响很明显。中断服务程序的功能应该尽量简单,只要将获取的异步事件通信给关联任务即可,后续处理交由关联任务完成。5
4、.2 不受操作系统管理的中断服务不受操作系统管理的中断服务程序程序在正常情况下,ISR应该接受操作系统的管理,因为很多任务是靠ISR触发的。但在两种情况下ISR不受操作系统管理:一种情况是没有必要;另一种情况是操作系统根本就没有对该ISR进行管理。某些控制系统需要在掉电时将各种现场动态数据保存起来,以便下次上电时恢复原样。在这类系统中配备了掉电检测单元,在电源电压开始下降时及时触发掉电中断(配备最高优先 级),在掉电ISR中将各种现场动态数据保存起来,然后使系统进入掉电状态,只有再次复位时系统才能开始重新运行。由于掉电ISR运行之后系统不再运行任何程序,故掉电ISR没有必要受操作系统管理。实时
5、操作系统c/os-II移植到ARM7体系的CPU上时,没有对FIQ进行处理,即 FIQ是不受操作系统管理的。选用FIQ来响应实时性要求最高的高速采样操作是一个有效措施,保护现场的工作量很小(FIQ专有的8个寄存器不需要保护)。在工程模板的系统启动文件Startups中,已经把汇编代码部分处理好了。用户只需要用C语言编写快速中断服务函数FIQ_Exception()即可,不需要考虑保护现场和恢复现场的问题。由于没有操作系统介入,FIQ的lSR无法与关联任务进行通信,所获取的信息不能及时得到关联任务的处理,故只能以原始形式保存在一个缓冲区内,等待以后进行离线处理。其典型的例子是高速数据采集系统。5
6、.3 受操作系统管理的中断服务程序受操作系统管理的中断服务程序 实时操作系统/C/OS-II移植到ARM7体系的CPU上时,对IRQ进行了管理,其ISR代码的编写必须遵循一定的规则。5.3.1 中断服务程序的结构中断服务程序的结构 受实时操作系统管理的ISR与不受实时操作系统管理的ISR有很大区别,体现在以下3个时段。(1)进入中断:除了保护现场外,还需要调用“进入中断”服务函数,用来通知实时操作系统,使实时操作系统掌握当前中断的嵌套深度。(2)运行功能代码:在这部分代码中,除了完成本ISR的实质功能的代码外,还包含了对系统通信服务函数的调用,实现与其他任务进行通信的功能。正是这种通信功能,使
7、关联任务得到同步信号或数据,从而进入就绪状态。但在ISR中不允许调用延时函数和可能被挂起的系统服务函数。(3)退出中断:必须执行实时操作系统规定的“退出中断”流程。首先根据中断嵌套深度判断被本次中断所中断的代码是“任务级”还是“中断级”,如果是“中断级”,则返回被中断的较低级的ISR;如果被中断的代码是“任务级”且不允许进行任务调度,则返回被中断的任务。如果允许进行任务调度,且出现了比被中断的任务的优先级更高的就绪任务,便进行任务切换;否则还是返回被中断的任务。在中断返回时,如果没有进行任务切换,则恢复中断进入时保护的现场;如果进行了任务切换,则现场被恢复成切换后的任务的现场(即这个任务在被挂
8、起时的现场)。受实时操作系统管理的ISR具有相同的结构,其进入中断和退出中断的处理流程完全相同,且与具体的CPU结构有关。作为实时操作系统的用户,如果要编写全部ISR代码,势必触动实时操作系统的内核,这是非常危险的。为此,必须将ISR中与具体功能无关的代码剥离出来,作为实时操作系统内核的一部分,提供给实时操作系统的用户。实时操作系统C/OS-移植到ARM7体系的CPU上时,这部分代码用一个汇编宏实现(移植文件IRQ.inc),并提供C语言接口,用户只需要用C语言编写ISR的功能代码部分即可。5.3.2中断句柄中断句柄为了使用ISR的汇编宏,每个受操作系统管理的ISR都必须按汇编宏要求的格式,在
9、文件IRQ.S的尾部添加中断句柄:XXXX_Handler HANDLER XXXX_Excetion其中:XXXX_Handler是ISR的起始地址,即汇编宏的起始地址,在初始化向量中断控制器时作为中断向量地址使用。用户按实际中断源来命名,即把其中的xxxx换为具体的中断源名称。HANDLER是句柄关键词,必须大写,不能有错。XXXX_Exception是用户用C语言编写的功能函数名,该函数供汇编宏调用。用户可按实际中断源来命名,即把其中的xxxx换为具体的中断源名称。例如,我们要使用定时器1作为一个中断源,用来控制某个周期性操作,则需要在文件IRQ.S的尾部添加如下中断句柄:Timer1_
10、Handler HANDLER Timer1_Exception 5.3.3 配置和初始化中断源配置和初始化中断源在一个中断源开始工作之前,需要配置和初始化中断源,使它接预定的参数和方式工作。这部分工作通常在系统上电硬件初始化时完成,也可以在某个任务运行中完成。中断源工作参数配置:每个中断源都有不同的工作参数,必须在该中断源开始工作之前按实际需要配置好。以定时器1中断为例,让定时器1产生周期为1 ms的中断的参数配置如程序清单L5-4所示。程序清单L5-4 配置中断源定时器1T1IR=0 xffffffff;/复位中断源T1TC=0 x00;/初始化定时计数器1T1PR=0 x0;/设置定时器
11、1的分频器(不分频)T1TCR=0 x01;/使能定时/计数器1T1MCR=0 x03;/匹配时产生中断并复位定时/计数器T1MR0=(Fpclk1000);/匹配值为1ms向量中断控制器配置:为了使中断信号和对应的ISR联系起来,还必须对向量中断控制器进行配置。对于通道号为X的中断源xxxx,如果配置中断优先级为Y,则需要在向量中断控制器的初始化函数里添加如程序清单L5-5所示代码。程序清单L5-5 配置向量中断控制器extern void XXXX_Handler(void);/声明中断源xxxx的中断服务函数ISRVICVecAddrrY=(INT32U)XXXX_Handler;/将I
12、SR入口地址填入向量寄存器YVICVectCntlY=(0 x20|x);/向量中断方式,通道号为X在运行过程中,随时可用如程序清单L5-6所示代码来启动和关闭通道号为X的中断源。程序清单L5-6 控制中断源的工作 VICIntEnable=1X;/使能中断源X产生中断 VIClntEnClr=1x;/禁止中断源x产生中断 以定时器1(通道号x=5)中断为例,配置优先级Y=2时需要将如程序清单L5-7所示代码加入向量中断控制器的初始化函数里。程序清单L5-7 为定时器1配置向量中断控制器 extern void Timer1_Handler(void);/声明定时器1的中断服务函数(ISR)V
13、ICVectAddr2=(INT32U)Timerl_Handler;/将ISR入口地址填入向量寄存器2 VICVectCntl2=(0 x20|0 x05);/向量中断方式,中断源为定时器1在运行过程中,随时可用如程序清单L5-8所示代码来启动和关闭定时器1的中断源:程序清单L5-8 控制定时器1的中断VICInrEnable =15;/使能定时器1产生中断 VICIntEnClr =15;/禁止定时器1产生中断5.3.4 设计与关联任务的通信手段设计与关联任务的通信手段ISR的主要功能是响应异步事件,该异步事件将触发一系列操作。ISR设计的基本原则是尽可能简短,以便其他异步事件也能够得到及
14、时响应。异步事件包含数据信息时就需要进行数据采集(如A/D转换),在ISR中进行数据采集可以获得最好的数据质量(及时并准确),但增加了ISR的负担,使ISR代码加长。在关联任务 中进行数据采集时,可以最大限度地减轻ISR的负担,简化ISR的代码,但也有可能影响数据 采集的质量(采集时刻有一些延误)。ISR与关联任务的通信方式有两种基本类型:信号(信号量)型和数据(消息)型。当使用信号量进行通信时,ISR只完成发送信号量的工作,表示事件已经发生,通过信号量的同步功能触发关联任务,所有具体工作均由关联任务完成;当使用数据进行通信时,ISR需要完成对异步事件的信息采集工作,然后使用消息邮箱(或消息队
15、列)将数据发送给关联任务,由关联任务完成后续数据处理工作。到底使用哪种方式,需要根据实际情况来决定。触发ISR的事件不包含数据:不需要对事件进行信息采集(如A/D转换)。例如报警信号触发了外部中断,该ISR只需要触发关联任务即可,后续各项操作由各个关联任务完成。在这种情况下,ISR使用信号量与关联任务进行通信。触发ISR的事件是包含数据的低频事件:将数据采集的工作放在关联任务中完成,产生的时刻延误与采样周期相比可以忽略不计,对采样数据的质量没有什么影响。在这种情况下,ISR使用信号量与关联任务进行通信,从而简化了ISR。一个示例如程序清单L5-10所示。触发ISR的事件是包含数据的中高频事件:
16、数据采集的工作放在关联任务中完成时,产生的时刻延误与采样周期相比不能忽略不计,对采样数据的质量有可察觉的影响。在这种情况下,数据采集的工作应该放在ISR中完成,由ISR使用消息邮箱与关联任务进行通信。关联任务从消息邮箱中得到消息的数据,并完成后续处理工作。一个示例如程序清单L5-11所示。触发ISR的事件是包含数据的非周期“高频”事件:对于非周期“高频”事件,其最短事件间隔可能小于一个事件数据处理的耗时,如果使用消息邮箱进行通信,就可能出现数据丢失现象。在这种情况下,数据采集的工作应该放在ISR中完成,由ISR使用具有数据缓冲功能的消息队列与关联任务进行通信。关联任务从消息队列中得到消息的数据
17、,并完成后续处理工作。一个示例见第10章的程序清单L10-3。5.3.5 编写中断服务程序的功能函数编写中断服务程序的功能函数用户中断服务程序的C语言函数部分结构如程序清单L5-9所示。程序清单L5-9 ISR的C函数结构void XXXX_Exception(void)/由汇编宏调用的c语言函数 OS_ENTER_CRITICAL();/关中断清除中断源;通知中断控制器中断结束;OS_EXIT_ CRITICAL();/开中断 用户中断处理代码;在移植操作系统时编写的汇编宏会加入到所有受管理的ISR中,因此,该汇编宏本身不能包含具体的中断源信息,涉及具体中断源的操作代码必须由用户完成,故用户
18、编写的C函数中除了功能代码外,首先要完成“清除中断源”和“通知中断控制器中断结束”的工作。这部分工作安排在临界代码端(不允许中断嵌套),以保证顺利完成。当执行到“清除中断源”时,本次中断已经获得响应,故应该将中断源清除;否则,ISR结束后有可能再次响应,造成一次事件多次响应的后果。如果中断源为外部电位信号,则必须配合硬件手段进行清除。如果中断源为芯片内部某功能部件,则可通过相应代码进行清除。以定时器1中断为例,当使用TIMR0进行匹配中断时,代码T1IR=0 x01就可以完成清除中断 标志的工作。“通知中断控制器中断结束”由代码VICVectAddr=0来完成。执行这条代码后,中断控制器开始准
19、备下一次的中断工作。“用户中断处理代码”为ISR的功能代码,内容由用户根据需要编写,原则是尽可能简洁、高效,不允许调用任何可能使自己“挂起”的系统服务函数。下面的例子为一个采样周期为10 ms的低速数据采集程序。采样周期由定时器1来控制,使用信号量与采样任务进行通信,采样操作在采样任务中完成,采样过程由按键启动,采样数据保存在全局数组中。由于定时器1的中断为IRQ,所以必须为其添加中断句柄,并按程序清单L5-5对向量中断控制器进行配置。低速采样的程序流程图如图5-2所示程序清单L5-10 低速采样示例#includeconfig.h#define KEY(120)/P020为按键控制I/O#d
20、efine TaskStk 100 /定义任务堆栈长度OS_ STK TaskKeyStkTaskStk;/定义按键任务的堆栈OS_STK TaskSampStkTaskstk;/定义采样任务的堆栈void TaskKey(void*pdata);/声明按键任务,因为这段代码在主函数的后面void TaskSamp(void*pdata);/声明低速采样任务,因为这段代码在按键任务函数的后面OS_EVENT*Sem;/定义信号量指针INT16U Samp200;/定义保存采样结果的数组void Show(INT16U*a,INT16Un)/显示波形函数INT16U i;GUI_ClearSCR
21、();/清屏for(i=0;in;i+)/显示波形 GUI_Point(i,240-ai*240/3000,RED);/高度 240点相当于 3000 mVint main(void)/将main()函数设置为整型是为了防止编译警告OSInit();OSTaskCreate(TaskKey,(void*)0,&TaskKeyStkTaskStk-l,4);/创建按键任务OSStart();Return 0;void TaskKey(void*pdata)/按键任务pdata=pdata;TargetInit();/系统电路初始化GUI_Initialize();/初始化LCM(液晶屏)PINS
22、ELl=0 x00400000;/设置P0.27连接到AIN0*进行ADC模块设置,其中xn表示第n位设置为x(若x超过一位,则向高位顺延)*ADCR=(10)|/SEL=l,选择通道0(Fpclk1000000-1)8)|/即转换时钟为1MHz(016)|/BURST=0,软件控制转换操作 (017)|/CLKS=0,使用11clock转换(121)|/PDN=1,正常工作模式(非掉电转化模式)(022)|/TEST1:0=00,正常工作模式(非测试模式)(124)|/START=1,直接启动ADC转换 (027);|/EDGE=0,引脚下降沿触发转换T1IR=0 xffffffff;/复位
23、中断源T1TC=0 x00;/初始化定时器1 T1PR=0 x00;/设置定时器1的分频器(不分频)T1TCR=0 x01;/使能定时器1T1MCR=0 x03;/匹配时产生中断并复位定时器1 T1MR0=Fpclk/100;/定时时间为10msSem=OSSemCreate(0);/创建信号量 while(1)OSTimeDly(2);/延时if(IO0PINKEY)!=0)continue;/未按键,再延时else /按下按键 while(IO0PIN&KEY)=0)/等待按键释放IO0SET=KEY;OSTimeDly(1);/延时 OSTaskCreate(TaskSamp,(void
24、*)0,&TaskSampStkTaskStk-1,2);/创建采样任务void TaskSamp(void*pata)/低速采样任务INT8U i,err;INT32U Temp;/临时变量pdata=pdata;VICIntEnable=15;/打开定时器1的中断,开始采样for(i=0;i200;i+)/采样200 次;数据以mV为单位OSSemPend(Sem,0,&err);/等待信号量 Temp=ADDR;通过读取ADC结果清除DONE标志位ADCR =(ADCR&0 xFFFFFF00)|0 x01|(124);/进行第一次转换while(ADDR&0 x80000000)=0)
25、;/等待转换结束ADCR=ADCR|(116);/保存采样结果VICIntEnClr=15;/禁止定时器1产生中断show(Samp,200);/显示采样信号的波形OSTaskDel(OS_PRIO_SELF);/删除自己void Timer1_Exception(void)/T1中断服务函数OS_ENTER_CRITICAL();/关中断TlIR=0 x01;/清除中断源 VICVetAddr=0;/通知中断控制器中断结束 OS_EXIT_CRITICAL();/开中断 OSSemPost(Sem);/发送信号量 下面的例子为一个采样周期为200 s的中速数据采集程序,采样周期由定时器1来控
26、制,采样操作在定时器1的ISR中完成,使用消息邮箱与采样任务进行通信,采样过程由按键启动,采样数据保存在全局数组中。由于定时器1的中断为IRQ,所以必须为其添加中断句柄,并按程序清单L5-5对向量中断控制器进行配置。中速采样的程序流程图如图5 3所示,程序代码如程序清单L5-11所示。#includeconfig.h /文件config.h包含了includes.h和一些系统配置文件#define KEY (120)/PO20为按键控制I/O#define TaskStk 100 /定义任务堆栈长度OS_STK TaskKeyStkTaskStk;/定义按键任务的堆栈OS_STK TaskSa
27、mpStkTaskStk;/定义采样任务的堆栈void TaskKey(void*pdata);/按键任务void TaskSamp(void*pdata);/中速采样任务OS_EVENT*Mybox;/定义消息邮箱指针INT16U Samp200;/定义保存采样结果的数组void Show(INT16U*a,INT16U n)/显示波形函数INTl6U i;GUI_ClearSCR();/清屏 for(i=0,in;i+)/显示波形 GUI_Point(i,240-ai*240/3000,RED);高度240点相当于 3000mVint_ main(void).OSInit();OSTask
28、Create(TaskKey,(void*)O,&TaskKeyStkTaskStk-l,4);创建按键任务OSStart();return 0;void TaskKey(void*pdata)按键任务pdata=pdata;TargetInit();系统电路初始化GUI_Initialize();/初始化LCM(液晶屏)PINSEL1=0 x00400000;PINSEL1=0 x00400000;/设置P0.27连接到AIN0*进行ADC模块设置,其中xn表示第n位设置为x(若x超过一位,则向高位顺延)*/ADCR=(10)|/SEL=1,选择通道0(Fpclk/1000000-1)8)|
29、即转换时钟为1MHz(0l6)|BURST=0,软件控制转换操作(0l7)|/CLKS=0,使用11clock转换(l21)|/PDN=l,正常工作模式(非掉电转换摸式)(022)|/TESTl:0=00,正常工作模式(非测试模式)(124)|START=1,直接启动ADC转换(027);EDGE=0,引脚下降沿触发转换T1IR=0 xffffffff;/复位中断源T1TC=0 x00;/初始化定时器1T1PR=0 x00;/设置定时器1的分频器(不分频)T1TCR=0 x01;/使能定时器1 T1MCR=0 x03;/匹配时产生中断并复位定时器1T1MR0=Fpclk5000;/定时时间为2
30、00s while(1)OSTimeDly(2);/延时if(IO0PIN&KEY)!=0)continue;未按键,再延时else /按下按键while(IO0PIN&KEY)=0)等待按键释放IO0SET=KEY;OSTimeDly(l);/延时OSTaskCreate(TaskSamp,(void*)0,&TaskSampStkTaskStk-l,2);创建采样任务void TaskSamp void*pdata)/中速采样任务INT8U i,err;pdata=pdata;Mybox=OSMboxCreate(void*)0);/创建消息邮箱VICIntEnable=15;/打开定时器
31、1的中断,开始采样for(i=0;i200;i+)/采样200次,数据以mV为单位Sampi=*(INT16U*)OSMboxPend(Mybox,0,&err);/获取并保存转换结果 VICIntEnClr=15;/禁止定时器1产生中断 OSMboxDel(Mybox,OS_DEL_ALWAYS,&err);/删除消息邮箱 Show(Samp,200);/显示采样信号的波形OSTaskDel(OS_PRIO_SELF);/删除自己void Timer1_Exception(void)/Tl中断服务函数static INT32U Temp;/静态变量OS_ENTER_CRITICAL();/关中断T1IR=0 x01;/清除中断源VICVectAddr=0;/通知中断控制器中断结束 Temp=ADDR;/通过读取ADC结果清除DONE标志位ADCR=(ADCR&0 xFFFFFF00)|0 x01|(124);/进行第一次转换while(ADDR&0 x80000000)=0);/等待转换结束ADCR=ADCR|(1 16;/参考电源为3000 mVOS_EXIT_CRITICAL();/开中断OSMboxPost(Mybox,(void*)&Temp);/发送以mV为单位的采样结果
限制150内