第5章 中断服务程序设计.docx
- 文档编号:30131138
- 上传时间:2023-08-05
- 格式:DOCX
- 页数:20
- 大小:138.59KB
第5章 中断服务程序设计.docx
《第5章 中断服务程序设计.docx》由会员分享,可在线阅读,更多相关《第5章 中断服务程序设计.docx(20页珍藏版)》请在冰豆网上搜索。
第5章中断服务程序设计
第5章中断服务程序设计
中断服务程序(ISR)是嵌入式应用系统获取各种事件的基本手段,而“事件”是实时性问题的讨论基础和时间计算的起点。
ISR的设计质量直接影响到系统的实时性指标和操作系统的工作效率。
只要没有关中断,中断服务程序可以中断任何任务的运行,可将中断服务程序可成比最高优先级(0级)还高的“任务”。
5.1中断优先级安排原则
中断源是系统及时获取异步事件的主要手段,其优先级安排原则如下:
●紧迫性:
触发中断的事件允许耽误的时间越短,设定的中断优先级就越高。
●关键性:
触发中断的事件越关键(重要),设定的中断优先级就越高。
●频繁性:
触发中断的事件发生越频繁,设定的中断优先级就越高。
●快捷性:
ISR处理越快捷(耗时短),设定的中断优先级就越高。
中断服务程的功能应尽量简单,只要将获取的异步事件通信给关联任务,后续处理由关联任务完成。
5.2不受操作系统管理的中断服务程序
正常情况下,ISR应受操作系统的管理,因很多任务是靠ISR触发的。
但在两种情况下ISR不受操作系统管理:
①没有必要;②操作系统没有对该ISR进行管理。
实时操作系统uC/OS-Ⅱ移植到ARM7体系的CPU上时,没有对FIQ进行处理,即FIQ是不受操作系统管理的。
选用FIQ来响应实时性要求最高的高速采样操作是一个有效措施,保护现场的工作量很小(FIQ专有的8个寄存器不需要保护)。
在工程模板的系统启动文件Startup.s中,已经把汇编代码部分处理好,用户只需要用C语言编写快速中断服务函数FIQ_Exception()即可,不需考虑保护现场和恢复现场的问题。
程序:
Startup.s中队FIQ的处理
Reset;异常向量表
LDRPC,ResetAddr;跳转到复位入口地址
LDRPC,UndefinedAddr
LDRPC,SWI_Addr;跳转到软件中断入口地址
LDRPC,PrefetchAddr
LDRPC,DataAbortAddr
DCD0xb9205f80
LDRPC,[PC,#-0xff0];跳转到向量中断入口地址(向量中断控制器)
LDRPC,FIQ_Addr;跳转到快速中断入口地址
ResetAddrDCDResetInit
UndefinedAddrDCDUndefined
SWI_AddrDCDSoftwareInterrupt
PrefetchAddrDCDPrefetchAbort
NouseDCD0
IRQ_AddrDCD0
FIQ_AddrDCDFIQ_Handler;快速中断服务程序入口地址
FIQ_Handler;快速中断服务程序
STMFDSP!
{R0-R3,LR};保护现场
HLFIQ_Exception;调用C语言编写的快速中断服务函数
LDMFDSP!
{R0-R3,LR};恢复现场
SUBSPC,LR,#4;中断返回
由于没有操作系统介入,FIQ的ISR无法与关联任务进行通信,所获取的信息不能及时得到关联任务的处理,故只能以原始形式保存在一个缓冲区内,等待以后进行离线处理。
典型的例子是高速数据采集系统。
由于使用FIQ方式进行采样,其ISR不受操作系统管理,所以只能用“使能中断源”和“关闭中断源”来控制采样过程。
这是需要设置一个采样任务来控制采样过程。
其代码结构如下程序。
程序:
高速采样任务函数结构
voidTaskSamp(void*pdata)//高速采样任务函数
{
进行相关设置;
while
(1)//无限循环
{等待启动信号’
进行准备工作;
使能采样中断;
等待结束信号;
停止采样中断;
数据预处理;
输出采样数据块;
}
}
实验:
将定时器1设置为FIQ,在FIQ中进行快速采样。
采样过程由采样任务进行控制,而采样任务本身由操作者通过按键进行控制,每次采样过程进行200次连续采样,采样周期为50us。
高速采样的程序流程图如图5-1所示,程序代码如下。
(a)按键任务(b)采样任务(c)FIQ
图5-1高速采样的程序流程图
程序:
高速采样示例
#include“config.h”//文件config.h包含了includes.h和一些系统配置文件
#defineKEY(1<<20)//P0.20为按键控制I/0
#defineTaskStk100//定义任务堆栈长度
OS_STKTaskKeyStk[TaskStk]//定义按键任务的堆栈
OS_STKTaskSampStk[TaskStk];//定义采样任务的堆栈
voidTaskKey(void*pdata);//声明按键任务,因为这段代码在创建它的主函数//后面
voidTaskSamp(void*pdata);//声明采样任务,因为这段代码在创建它的按键任务//函数的后面
INT16USamp[200];//保存采样结果的数组
INT8Ucount=0;//采样次数计数器
voidShow(INT16U*a,INT16Un)//显示波形函数
{
INT16UI;
GUI_ClearSCR();//清屏
for(i=0;i GUI_Point(i,240-a[i]*/300/3000,RED);//高度240点相当于3000mV } intmain(void)//将main()函数设置为整型是为了防止编译警告 { OSInit(); OSTaskCreat(TaskKey,(void*)0,&TaskKeyStk[TaskStk-1],4);//创建按键任务 OSStart(); return0; } voidTaskKey(void*pdata)//按键任务 { pdata=pdata; TargetInit();//系统电路初始化 GUI_Initalize();//初始化LCD(液晶屏) PINSEL1=0x00400000;//设置P0.27连接到AINO //进行ADC模块设置,其中x< ADCR=(1<<0)|//SEL=1,选择通道0 ((Fpclk/1000000-1)<<8)|//即转换时钟为1Mhz (0<<16)|//BURST=0.软件控制转换操作 (0<<17)|//CLKS=0,使用11clock转换 (1<<21)|//PDN=1,正常工作模式(非掉电转换模式) (0<<22)|//TEST1: 0=00,正常工作模式(非测试模式) (1<<24)|//START=1,直接启动ADC转换 (0<<27)|//EDGE=0,引脚下降沿触发转换 T1IR=0xffffffff;//复位中断源 T1TC=0x00;//初始化定时器1 T1PR=0x00;//设置定时器1的分频器(不分频) T1TCR=0x01;//使能定时器1 T1MCR=0x03;//匹配时产生中断并复位定时器1 T1MR0=Fpclk/20000;//定时时间为50us VICIntSelect=1<<5;//T1设置为快速中断 while (1) { OSTimeDly (2);//延时 if((IO0PIN&KEY)! =0)continue;//未按键,再延时 else//按下按键 { while(IO0PIN&KEY)==0)//等待按键释放 { IO0SET=KEY; OSTimerDly (1);//延时 } OSTaskCreate(TaskSamp,(void*)0,&TaskSamp[TaskStk-1],2);//创建采样任务 } } } voidTaskSamp(void*pdata)//高速采样任务 { INT8Ui;//临时变量 INT32UTemp;//临时变量 pdata=pdata; count=0;//初始化采样计数器 VICIntEnable=1<<5;//打开定时器1的FIQ,开始采样 while (1);//等待采样结束 { OS_ENTER_CRITICAL();//关中断 i=count;//查询当前完成的采样次数 OS_EXIT_CRITICAL();//开中断 if(i>=200)break;//完成预定采样次数,结束查询 OSTimeDly (1);//未完成预定采样次数,延时一个时钟节拍继续//查询 } for(i=0;i<200;i++)//将采样数据进行预处理,使数据以mV为单位 { Temp=3000*Samp[i];//参考电压为3000mV Samp[i]=(INT16U)(TEMP>>16); } Show(Samp,200);//显示采样信号的波形 OSTaskDel(OS_PRIO_SELF);//删除自己 } voidFIQ_Exception(void)//快速中断服务函数 { INT32UADC_Data; T1IR=0x01;//清除中断源 VICVectAddr=0;//通知中断控制器 ADC_Data=ADDR;//通过读取ADC结果清除DONE标志位 ADCR=(ADCR&0Xffffff00)|0x01|(1<<24);//切换通道并进行第一次转换 while(ADDR&0x80000000)==0);//等待转换结束 ADCR=ADCR|(1<<24);//再次启动转换 while(ADDR&0x80000000)==0);//等待转换结束 Samp[count]=(INT16U)(ADDR&0x0000FFFF);//读取并保存转换结果 count++;//调整采样计数器 if(count==200)VICIntEnClr=1<<5;//完成采样次数,关闭FIQ 5.3受操作系统管理的中断服务程序 ●中断服务程序的结构 受实时操作系统管理的ISR与不受实时操作系统管理的ISR有很大区别,体现在以下3个阶段。 1入中断: 除了保护现场外,还需要调用“进入中断”服务函数。 2行功能代码: 完成ISR的实质功能的代码外,还包含了对系统通信服务函数的调用,使关联任务得到同步信号或Ⅱ数据,从而进入就绪状态,但在ISR中不容许调用延时函数和可能被挂起系统服务函数。 3退出中断: 执行“退出中断”流程。 必须将ISR中与具体功能无关的代码剥离出来,作为实时操作系统内核的一部分,提供给实时操作系统的用户。 实时操作系统uc/OS-Ⅱ移植到ARM7体系的CPU上时,这部分代码用一个汇编宏实现(移植文件IRQ.inc),并提供C语言接口,用户只需要用C语言编写ISR的功能代码即可。 ●中断句柄 为了使用ISR的汇编宏,每个受操作系统管理的ISR都必须按汇编宏要求的格式,在文件IRQ.s的尾部添加中断句柄: XXXX_HandlerHANDLERXXXX_Exception 其中: ◇XXXX_Handler是ISR的起始地址,即汇编宏的起始地址作为中断向量地址使用。 ◇HANDLER是句柄关键词 ◇XXXX_Exception是用户用C语言编写的功能函数名,该函数供汇编调用。 例: 使用定时器1作为一个中断源,如下中断句柄: Timer1_HanderHANDLERTimer1_Exception ●配置和初始化中断源 在一个中断源开始工作前,需配置和初始化中断源,使其按预定的参数和方式工作。 中断源工作参数配置: 每个中断源都有不同的工作参数,必须在该中断源开始工作之前按实际需要配置好。 以定时器1中断为例,让定时器1产生周期为1ms的中断的参数配置如下程序。 程序: 配置中断源定时器1 TITR=0xffffffff;//复位中断源 TITC=0x00;//初始化定时/计数器1 T1PR=0x00;//设置定时器1的分频器(不分频) T1TCR=0x01;//使能定时/计数器1 T1MCR=0x03;//匹配时产生中断并复位定时/计数器 T1MR0=Fpclk/1000;//匹配值为1ms 程序: 配置向量中断控制器 externvoidXXXX_Handler(void);//声明中断源XXXX的中断服务函数ISR VICVectAddrY=(INT32U)XXXX_Handler;//将ISR入口地址填入向量寄存器Y VICVecCntlY=(0x20|x);//向量中断方式,通道号为X 程序: 控制中断源的工作 VICInEnable=1< VICIntEnClr=1< 程序: 为定时器1配置向量中断控制器 externvoidTimer1_Handler(void);//声明定时器1的中断服务函数(ISR) VICVectAddr2=(INT32U)Timer1_Handler;//将ISR入口地址填入向量寄存器2 VICVecCntl2=(0x20|0x05);//向量中断方式,中断源为定时器1 程序: 控制定时器1的中断(其一) VICInEnable=1<<5;//使能定时器1产生中断 VICIntEnClr=1<<5;//禁止定时器1产生中断 ●设计与关联任务的通信手段 ISR的主要功能是响应异步事件,改异步事件将触发一系列操作。 ISR设计的基本原则是尽可能简短,以便其他异步事件也能够得到及时响应。 异步事件包含数据信息时就需要进行数据采集(如A/D转换),在ISR中进行数据采集可以获得最好的数据质量(及时并准确),但增加了ISR的负担,使ISR代码加长。 ISR与关联任务的通信方式有两种基本类型: 信号(信号量)型和数据(消息)型。 到底使用哪种方式,需要根据实际情况来决定。 ◇触发ISR的事件不包含数据: 不需要对事件进行信息采集(如A/D转换)。 例如报警信号触发了外部中断,该ISR只需要触发关联任务即可,后续各项操作由各个关联任务完成。 在这种情况下,ISR使用信号量与关联任务进行通信。 ◇触发ISR的事件是包含数据的低频事件: 将数据采集的工作在关联任务中完成,产生的时刻延迟与采样周期相比可以忽略不计,对采样数据的质量没有什么影响。 在这种情况下,ISR使用信号量与关联任务进行通信,从而简化了ISR。 一个示例如程序清单L5-10所示。 ◇触发ISR的事件是包含数据的中高频事件: 数据采集的工作放在关联任务中完成时,产生的时刻延误与采样周期相比不能忽略不计,对采样数据的质量有可察觉的影响。 在这种情况下,数据采集的工作应该放在ISR中完成,由ISR使用消息邮箱与关联任务进行通信。 关联任务从消息邮箱中得到消息的数据,并完成后续处理工作。 一个示例如程序清单L5-11所示。 ◇触发ISR的事件是包含数据的非周期“高频”事件: 对于非周期“高频”事件,其最短事件间隔可能小于一个事件数据处理的耗时,如果使用消息邮箱进行通信,就可能出现数据丢失现象。 在这种消息下,数据采集的工作应该放在ISR中完成,由ISR使用具有数据缓冲功能的消息队列和关联任务进行通信。 关联任务从消息队列中得到消息的数据,并完成后续处理工作。 ●编写中断服务程序的功能函数 程序: ISR的C函数结构 voidXXXX_Exception(void)//由汇编宏调用的C语言函数 { OS_ENTER_CRITICAL();//关中断 清除中断源; 通知中断控制器中断结束; OS_EXIT_CRITICAL();//开中断 用户中断处理代码; } 用户编写的C函数中除了功能代码外,首先要完成“清楚中断源”和“通知中断控制器中断结束”的工作。 这部分安排在临界代码端(不容许中断嵌套),以保证顺利完成。 例子为一个采样周期为10ms的低速数据采集程序。 采样周期由定时器1来控制,使用信号量与采样任务来进行通信,采样操作在采样任务中完成,采样过程由按键启动,采样数据保存在全局变量数组中。 由于定时器1的中断为IRQ,所以必须为其添加中断句柄,并按程序清单L5-5对向量中断控制器进行配置。 (a)按键任务(b)采样任务(c)ISR 图5-2低速采样的程序流程图 程序: 低速采样示例 #include”config.h”//文件config.h包含了includes.h和一些系统 //配置文件 #defineKEY(1<<20)//P0.20为按键控制I/O #defineTaskStk100//定义任务堆栈长度 OS_STKTaskKeyStk[TaskStk];//定义按键任务的堆栈 OS_STKTaskSampStk[TaskStk];//定义采样任务的堆栈 voidTaskKey(void*pdata);//声明按键任务,因为这段代码在主函数的后面 voidTaskSamp(void*pdata);//声明低速采样任务,因为这段代码在按键任务 //函数的后面 OS_EVENT*Sem;//定义信号量指针 INT16USamp[200];//定义保存采样结果的数组 VoidShow(INT16U*a,INT16Un)//显示波形函数 { INT16Ui; GUI_ClearSCR();//清屏 for(i=0;i GUI_Point(i,240-a[i]*240/3000,RED);//高度240点相当于3000mV } intmain(void)//将main()函数设置为整形是为了防止编译警告 { OSInit(); OSTaskCreate(TaskKey,(void*)0,&TaskKeyStk[TaskStk-1],4);//创建按键任务 OSStart(); return0; } voidTaskKey(void*pdata)//按键任务 { pdata=pdata; TargetInit();//系统电路初始化 GUI_Initialize();//初始化LCM(液晶屏) PINSEL1=0x00400000;//设置P0.27连接到AINO /*进行ADC模块设置,其中x< ADCR=(1<<0)//SEL=1,选择通道0 ((Fpclk/1000000-1)<<8)//即转换时钟为1MHz (0<<16)//BURST=0,软件控制转换操作 (0<<17)//CLKS=0,使用11clock转换 (1<<21)//PDN=1,正常工作模式(非掉电转换模式) (0<<22)//TEST1: 0=00正常工作模式(非测试模式) (1<<24)//START=1直接启动ADC转换 (0<<27);//EDGE=0,引脚下降沿触发转换 T1IR=0xffffffff;//复位中断源 T1TC=0x00;//初始化定时器1 T1PR=0x00;//设置定时器1的分频器(不分频) T1TCR=0x01;//使能定时器1 T1MCR=0x03;//匹配时产生中断并复位定时器1 T1MRO=Fpclk/100;//定时时间为10ms Sem=OSSemCreate(0);//创建信号量 while (1) { OSTimeDly (2);//延时 if((IO0PIN&KEY! =0)continue;//未按键,再延时 else//按下按键 { While((IO0PIN&KEY)==0)//等待按键释放 { IO0SET=KEY; OSTiimeDly (1);//延时 } OSTaskCreate(TaskSamp,(void*)0,&TaskSampStk[TaskStk-1],2);//创建采样任务 } } } voidTaskSamp(void*pdata)//低速采样任务 { INT8Ui,err; INT32UTemp;//临时变量 pdata=pdata; VICIntEnable=1<<5;//打开定时器1的中断,开始采样 for(i=0;i<200;i++)//采样200次,数据以mV为单位 { OSSemPend(Sem,0,&err);//等待信号量 Temp=ADDR;//通过读取ADC结果清除DONE标志位 ADCR=(ADCR&0xFFFFFF00)|0x01|(1<<24);//进行第一次转换 while((ADDR&0x80000000)==0);//等待转换结束 ADCR=ADCR|(1<<24);//再次启动转换 while((ADDR&0x80000000)==0);//等待转换结束 Temp=ADDR;//读取转换结果 Temp=3000*(Temp&0x0000ffff);//参考电源为3000mV Samp[i]=(INT16U)(Temp>>16);//保存采样结果 } ViCIntEnClr=1<<5;//禁止定时器1产生中断 Show(Samp,200);//显示采样信号的波形 OSTaskDel(OS_PRIO_SELF);//删除自己 } voidTimer1_Exception(void)//T1中断服务函数 { OS_ENTER_CRITICAL();//关中断 T1IR=0x01;//清除中断源 VICVectAddr=0;//通知中断控制器中断结束 OS_EXIT_CRITICAL();//开中断 OSSemPost(Sem);//发送信号量 例子为一个采样周期为200us的中速数据采集程序,采样周期由定时器1来控制,采样操作在定时器1的ISR中完成,使用消息邮箱与采样任务进行通信,采样过程由按键启动,采样数据保存在全局数组中。 由于定时器1的中断为IRQ,所以必须为其添加中断句柄,并按程序清单L5-5对向量中断控制器进行配置。 中速采样的流程图如图5-3所示,程序代码如程序清单L5-11所示。 程序: 中速采样示例 #include”config.h”//文件config.h包含了includes.h和一些系统 //配置文件 #defineKEY(1<<20)//P0.20为按键控制I/O #defineTaskStk100//定义任务堆栈长度 OS_STKTaskKeyStk[TaskStk];//定义按键任务的堆栈 OS_STKTaskSampStk[TaskStk];//定义采样任务的堆栈 voidTaskKey(void*pdata);//按键任务 voidTaskSamp(void*pdata);//中速采样任务 OS_EVENT*Mybox;//定义消息邮箱指针 INT16USamp[200];
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 第5章 中断服务程序设计 中断 服务 程序设计