ARM中的异常中断是如何实现进入中断程序的,比如如何进入
swi(0x00) void SwiHandle1(int Handle);其实没有函数体,执行这个语句后就自动把Handle的值赋给了R0,接着执行下面的代码。__swi(0x00)是软件中断,0为软中断指令中的24位立即数,但是通过R0寄存器来传递参数具体的函数体,当然是要在swi的中断处理程序中去找了可以在复位时的异常向量表里面找到swi中断服务程序的入口地址。
SWI 执行的流程是,先进入异常中断向量表,然后跳到向量地址处,接着一小段汇编 *** 作,把功能号读入到一个寄存器中,然后 switch 判断这个功能号是多少,接着跳转到对应的终端服务程序,如果函数有参数,则根据ATPCS规则进行参数的传递;如只有一个参数,则用 R0 来传递,超过4个参数,超出的部分用堆栈来传递。
__swi是ADS编译器的关键字,用它做前缀可以声明一个软中断调用,格式为:
__swi(功能号) 返回值 名称 (参数列表)
功能号:即软中断指令中的24位立即数,软中断号
名 称:即调用软中断时用于描述软中断的函数名称
参 数:软中断函数的参数,根据ATPCS规则,如果软中断函数有不超过4个参数时,通过R0~R3传递,超过4个参数时用堆栈来传递。
__swi(0x00) void SwiHandle1(int Handle)。其中0x00为软中断功能号(软中断号);软中断函数名称为SwiHandle1;只有一个参数,则使用R0来传递;函数没有返回值。紧接着这句代码的是定义了4个宏,分别表示禁能IRQ函数、使能IRQ函数、禁能FIQ函数、使能IFQ函数,其实调用的软中断函数是一样的,只是参数不同而已。例如在用户程序中调用“IRQEnable( );”时,处理器会产生软中断。位于启动代码中的那些是软中断处理函数,当发生软中断时,PC被强制指向0x00000008,这个地址中存放的是软中断异常的处理函数的地址,所以程序会跳转至标号“SoftwareInterrupt ”处执行。SoftwareInterrupt 函数的功能是判断R0的值(R0的值为软中断函数传递过来的参数)是否小于4,如果小于4则跳转至标号“SwiFunction”执行,如果不是则函数返回。SwiFunction函数是一个散转函数,它的功能是根据R0的值跳转至对应的函数处执行,即如果参数为1,则函数会跳转至IRQEnable处执行,将IRQ中断使能。
本文件SWIs位于ARM Executable Image for LPC2294工程模板中,故不考虑SWI触发前为Thumb态;SWI异常一旦触发,内核硬件完成:
♂ 进入Supervisor模式;
♂ 拷贝CPSR至SPSR_svc
♂ 拷贝异常返回地址至LR_svc
♂ 将0x00000008装入PC
因此,当触发SWI软中断前内核处于Supervisor模式,SPSR_svc、LR_svc中的值将被破坏;
3、SWI指令编码中自带24bit数据作为软中断号(swi_num),因此可通过取SWI指令编码获取软中断号;LDR r0,[lr,#-4]就是这样;
4、SWI_Exception_Function函数一般采用C编码(也可汇编),采用C编码可直接套用switch根据swi_nun软中断号切换,SWI_Exception_Function函数的编制是灵活的,比如可以为带参或不带参函数;
5、一个SWI调用允许带1~4个字型参数和1~4个字型返回值,触发SWI调用时四个参数依次保存在R0~R3中,返回值也
中断服务程序:处理器处理“急件”,可理解为是一种服务,是通过执行事先编好的某个特定的程序来完成的,这种处理“急件”的程序被称为——中断服务程序。
当中央处理器正在处理内部数据时,外界发生了紧急情况,要求CPU暂停当前的工作转去处理这个紧急事件。处理完毕后,再回到原来被中断的地址,继续原来的工作,这样的过程称为中断。实现这一功能的部件称为中断系统,申请CPU中断的请求源称为中断源,单片机的中断系统一般允许多个中断源,当多个中断源同时向CPU请求中断时,就存在一个中断优先权的问题。通常根据中断源的优先级别,优先处理最紧急事件的中断请求源,即最先响应级别最高的中断请求。
使用 Windows media player控件 或 PlaySoundAPI函数,结合 MsgBox() 函数均可。
例如:使用 Windows media player控件 结合 MsgBox() 函数
①在窗体上放一个Windows media player控件,假设其名称为WindowsMediaPlayer1。(如果不想让其出现在窗体上,将WindowsMediaPlayer1的visible属性设为“False”即可)
②在你的程序需要原地等待并持续播放声音报警的地方插入下列几句代码:
WindowsMediaPlayer1URL = "F:\_MP3\草原的微香mp3" '指定声音文件
WindowsMediaPlayer1Controlsplay '持续播放声音文件报警
K = MsgBox("请按 确定按钮 停止声音报警,继续程序运行。", vbOKOnly, " 提示") ' *** 作提示
WindowsMediaPlayer1Controlsstop '关闭播放声音文件,停止报警
//GPIO中断实验,按键KEY1接上拉电阻,通过PC3做为中断输入,DS1(PF10)做为中断指示,中断一次,亮灭状态改变一次。
#include "stm32f10xh"
u8 count=0;//软件延时寄存器。
//函数声明
void Delay_ms(vu16 nCount); //ms级延时函数声明。
void Delay_10us(vu16 nCount);//10us级延时函数声明。
void GPIO_PC_Init(void);
void GPIO_PF_Init(void);
void EXTI_Line3_Init(void);
void NVIC_Configuration(void);
//初始化子程序
void GPIO_PC_Init(void)//PC3初始化子程序
{
GPIO_InitTypeDef GPIO_InitStructure; //声明一个名为GPIO_InitStructure的结构体,其原型由GPIO_InitTypeDef确定。只能放在一个函数的最前面。
GPIO_InitStructureGPIO_Pin = GPIO_Pin_3;//选择待初始化的端口号
GPIO_InitStructureGPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入模式(因为外部有上拉电阻)。
GPIO_InitStructureGPIO_Speed = GPIO_Speed_2MHz;//管脚速度设定。
GPIO_Init(GPIOC,&GPIO_InitStructure);//用以上参数初始化GPIOC。
GPIO_EXTILineConfig(GPIO_PortSourceGPIOC, GPIO_PinSource3);//指定用作外部中断线路的管脚。
}
void GPIO_PF_Init(void)//PC3初始化子程序
{
GPIO_InitTypeDef GPIO_InitStructure; //声明一个名为GPIO_InitStructure的结构体,其原型由GPIO_InitTypeDef确定。只能放在一个函数的最前面。
GPIO_InitStructureGPIO_Pin = GPIO_Pin_10;
GPIO_InitStructureGPIO_Mode = GPIO_Mode_Out_OD;//开漏输出模式。
GPIO_InitStructureGPIO_Speed = GPIO_Speed_10MHz;//管脚速度设定。
GPIO_Init(GPIOF,&GPIO_InitStructure);//用以上参数初始化GPIOF。
}
void EXTI_Line3_Init(void)//中断线PC3的配置。
{
EXTI_InitTypeDef EXTI_InitStructure;//声明一个名为EXTI_InitStructure的结构体,其原型由EXTI_InitTypeDef确定。只能放在一个函数的最前面。
EXTI_InitStructureEXTI_Line=EXTI_Line3;//选择待初始化的外部中断端口
EXTI_InitStructureEXTI_Mode=EXTI_Mode_Interrupt;//设置EXTI线路为中断请求。
EXTI_InitStructureEXTI_Trigger=EXTI_Trigger_Falling;//设置输入线路中断请求为下降沿触发。
EXTI_InitStructureEXTI_LineCmd=ENABLE;//中断线使能。
EXTI_Init(&EXTI_InitStructure);//用以上参数初始化外部中断。
}
void NVIC_Configuration(void)//NVIC嵌套向量中断控制器配置。
{
NVIC_InitTypeDef NVIC_InitStructure;//声明一个名为NVIC_InitStructure的结构体,其原型由NVIC_InitTypeDef确定。
NVIC_InitStructureNVIC_IRQChannel=EXTI3_IRQn;//指定要配置的中断源。
NVIC_InitStructureNVIC_IRQChannelPreemptionPriority=0;//设置先占优先级
NVIC_InitStructureNVIC_IRQChannelSubPriority=1;//设置从优先级。
NVIC_InitStructureNVIC_IRQChannelCmd=ENABLE;//使能中断
NVIC_Init(&NVIC_InitStructure);//用以上参数初始化中断控制器。
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);//设置优先级分组位长度
}
int main(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOF|RCC_APB2Periph_AFIO, ENABLE);//开启GPIOC,GPIOF,AFIO(复用时钟)的时钟。
//要配置stm32的事件输出/外部中断/重映射的时候就必须开启复用时钟
GPIO_PC_Init();
GPIO_PF_Init();
EXTI_Line3_Init();
NVIC_Configuration();
while(1);//等待中断。
}
void EXTI3_IRQHandler(void)//外部中断线3的中断服务程序。
{
if(EXTI_GetITStatus(EXTI_Line3)==SET);//检测指定线路的中断请求是否产生。
{
Delay_ms(10);//去抖延时
while(GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_3)==0);//等待按键释放
GPIO_WriteBit(GPIOF, GPIO_Pin_10, (BitAction)(1-GPIO_ReadOutputDataBit(GPIOF, GPIO_Pin_10)));//对GPIOF_10脚输出取反。
Delay_ms(100);
}
EXTI_ClearFlag(EXTI_Line3);//与EXTI_ClearITPendingBit(EXTI_Line3);等效 清除中断标注后,下次中断才能进入。
}
/
函数功能:1ms延时,8M晶振
/
void Delay_ms(vu16 nCount)
{
vu16 i;
for(; nCount >0; nCount--)
for(i=10276;i!=0;i--);
}
/
函数功能:10us延时,8M晶振
/
void Delay_10us(vu16 nCount)
{
vu16 i;
for(; nCount >0; nCount--)
for(i=74;i!=0;i--);
}
只要将相应的端口改为你自己的端口就OK了。
如果是用C去写的话,像汇编一样,开中断就行了。位置一般就在MAIN 之前的。
//晶振频率221184MHz
#include<at89x52h>
#define TIMER0H 0x4c
#define TIMER0L 0x00
#define TIMER0_RUN TR0=1
#define SECOND_OVERFLOW 40
#define SEG_PORT P0
#define DISPLAY_DIG1 P1&=0xf0;P1|=0x01
unsigned char g_CurrentDigit=0; //当前显示的数字
void timer() interrupt 1
{
static unsigned char s_Count = 0;
TH0 = TIMER0H; //重置定时器初值
TL0 = TIMER0L;
//每次进入中断服务程序,TH0和 TL0 的值都
TIMER0_RUN; //定时器运行,开始下一
if(s_Count != SECOND_OVERFLOW)
{ //未到整秒,把 sCount 值加1
s_Count++;
}
else
{ //到整秒,s_Count归 0,更新把当前显示
s_Count = 0;
if(g_CurrentDigit != 9)
{
g_CurrentDigit++;
}
else
{
g_CurrentDigit = 0;
}
}
return;
}
void Initial(void) //初始化
{
IE = 0x82; //仅允许Timer0 中断
TMOD = 0x01; //Timer0 使用工作方式 1(16位) ,定时器
TH0 = TIMER0H; //设置定时器初值
TL0 = TIMER0L;
TIMER0_RUN; //定时器开始运行
DISPLAY_DIG1;
}
void main()
{
unsigned char code SEG_CODE[]
= {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
Initial();
while(1)
{
SEG_PORT = SEG_CODE[g_CurrentDigit]; //显示当前的数字
//当timer0溢出时,单片机响应 timer0 中断,调用 timer 函数,
//每40 次调用当前显示的数字加 1
}
}
程序分析:
程序中主程序做的事只是在死循环中反复显示当前的数字,每产生一次中断,程序就跳
转到中断服务函数 timer()中进行相应的更新。
这里中断服务函数 timer()有别于普通 C函数的地方是在声明中多了“interrupt 1” ,说明
这个函数是中断号为 1的中断服务函数。各个中断对应的中断号如表 38所示。
这个程序需要初始化的东西比较多, 我们把这些初始化语句都放在了初始化函数 Initial()
中,这也是程序初始化很常见的做法。我们还第一次用到了静态变量和全局变量。全局变量
是中断处理函数与外界程序进行参数传递的唯一途径,因此在单片机程序中全局变量的使
用频率要比普通的 C 语言程序高。尽管如此,由于全局变量的使用会影响程序的结构化,
所以在可以不使用全局变量的地方,还是要避免使用全局变量。在程序中,为了把全局变
量与静态变量跟普通变量区别开来,我们在变量前分别加了小写 g_和小写 s_以示区别。
IE 寄存器中的使能位和C中的中断号 中断源
0 外部中断0
1 定时器0溢出
2 外部中断1
3 定时器1溢出
4 串行口中断
5
定时器2溢出(仅在S52、
C52中有此中断源)
好了,以上是我找的一个实例,希望对你有帮助!
给你上个较完整的程序,可以直接在KEIL中运行并观察输出
/
功能:
本程序在12M晶振模式下,通过定时器中断精确实现数字时钟计时 *** 作,并在KEIL
中实现输出。时、分、秒的变化在定时中断里处理。
说明:
因采用工作方式2,自动装入初值,所以此程序计时很精确,只是在KEIL中模拟输
出显示的变化速度很快,这点可不理会
/
#include
#include
#define
TEST
//此行用于KEIL输出显示,如果不需要显示可将其删除
typedef
unsigned
char
uchar;
typedef
unsigned
int
uint;
#define
TH0TL0_INIT
(256-250)
//定时器8位自动装入模式下寄存器初值,025ms中断一次
char
cHour;
//时
char
cMin;
//分
char
cSec;
//秒
uint
iCount;
//秒计数,计数达到4000时1s,4000025ms
=1000ms
=
1s
bit
bSecChanged;
//秒发生变化标志,每秒送一次输出显示,送显完成后清0,提高主程序效率
//==============================================================================
//T0定时器中断服务程序,12M晶振下每025ms产生中断,本程序执行一次
//==============================================================================
void
Timer0()
interrupt
1
{
iCount++;
//秒计数值+1
if(iCount==4000)
{//时间计数达到1S
iCount
=
0;
//重新开始下一秒计数
cSec++;
//时钟:秒+1
bSecChanged
=
1;
//置秒发生变化标志
if(cSec==60)
{//计够60s
cSec
=
0;
//重新开始下一分计数
cMin++;
//时钟:分+1
}
if(cMin==60)
{//计够60分钟
cMin
=
0;
//重新开始下一小时计数
cHour++;
//时钟:小时+1
}
if(cHour==24)
{//计够24小时
cHour
=
0;
//重新开始第二天计数
}
}
}
//==============================================================================
//主程序
//==============================================================================
void
main()
{
uchar
outstr[10];
//输出字符串,我的编译器可能有问题,直接输出有错
TMOD
=
0X02;//工作方式2,8位自动重装计时模式
TH0
=
TH0TL0_INIT;
//025ms中断一次
TL0
=
TH0TL0_INIT;
//025ms中断一次
#ifdef
TEST
SCON
=
0x50;
/
SCON:
mode
1,
8-bit
UART,
enable
rcvr
/
TMOD
|=
0x20;
/
TMOD:
timer
1,
mode
2,
8-bit
reload
/
TH1
=
221;
/
TH1:
reload
value
for
1200
baud
@
16MHz
/
TR1
=
1;
/
TR1:
timer
1
run
/
TI
=
1;
/
TI:
set
TI
to
send
first
char
of
UART
/
#endif
cHour
=
0;
//时
cMin
=
0;
//分
cSec
=
0;
//秒
iCount
=
0;
//秒计数
bSecChanged
=
0;
outstr[2]
=
':';
//时分分隔符
outstr[5]
=
':';
//分秒分隔符
outstr[8]
=
0;
//字符串结束符
EA=1;
//开总中断
ET0=1;
//允许T0中断
TR0=1;
//启动T0
while(1)
{
if(bSecChanged==1)
{//秒发生变化,将时间值转换为可显示字符串准备送显示
bSecChanged
=
0;//清除标志,节省CPU资源
outstr[0]
=
cHour/10
+
0x30;
//将秒转换为ASCII码
outstr[1]
=
cHour%10
+
0x30;
outstr[3]
=
cMin/10
+
0x30;
//将分转换为ASCII码
outstr[4]
=
cMin%10
+
0x30;
outstr[6]
=
cSec/10
+
0x30;
//将小时转换为ASCII码
outstr[7]
=
cSec%10
+
0x30;
#ifdef
TEST
printf("
%s\r",outstr);
//在KEIL中显示时钟
#endif
}
}
}
以上就是关于ARM中的异常中断是如何实现进入中断程序的,比如如何进入...全部的内容,包括:ARM中的异常中断是如何实现进入中断程序的,比如如何进入...、什么是C语言的中断程序作用是什么、VB怎么实现中断等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)