中断,在学单片机的时候就没有学好,这次接触到210的中断体系,直接蒙圈了。。前天下午老师简单的讲了一下,他的每句话拆开都可以听得懂,联系起来就傻了,,而且懂了与会写差距还远着呢。当天下午所有人都是萎靡的状态(不懂,不会写极其郁闷),以为参照裸机开发指南可以边写会慢慢理解,结果越写越是打击。晚上没有回去,一直想体验下在教室通宵的感觉,除了蚊子比较多、床不好睡、网速依旧渣、睡得太累,好吧,好像并没有什么优点。
虽然说是通宵了,但是几乎什么都没有学到,反而把自己搞得劳累。得不偿失!早上去领了免费包子,吃完回教室,大家在吐槽寝室那边“习俗”的事和程序的事,得到老师的承诺说下午会带着写程序,整个早上也没有怎么瞎折腾了,随便看看。
前几天写好的makefile今天打开居然无法使用,编译到最后连接的时候有段错误!!(致命一击),重新把程序审视了一遍,什么都没有发现。请教老师也没有找出来,可能是ld程序出错了,后来重启了几次依旧如此,不想再纠结了,直接做中断了。
6410和210的中断体系增加了向量中断的概念,相比2440,复杂程度乘法增加的。部分理解如下:
四个向量中断:每个分别存放32个中断源,注意是中断”源“,不是单个中断,理解这点很重要,因为每个中断“源”里面包含了若干中断
其中外部中断全部在第一个中断向量中,中断“源”是16,,,16中断“源”包含了EXT_INT[16] ~ [31]号中断,这里主要讨论16--19和24--28号的外部按键中断
由于存在中断“源”和具体中断的区别,所以分别有两套寄存器控制着,这一点也是非常重要的,操作“源”就可以操作里面的中断,里面具体的哪一号中断还要 另外控制。呵呵,现在只能理解部分,以后要好好加深。
中断处理函数和中断服务程序:(起这样两个名字的人有没有考虑过初学者的感受。。。)处理一个中断需要两级跳转,首先是跳转到中断处理函数(irq_handle和irq_handler 这两个函数真心不晓得是哪个),中断处理函数负责入栈、出栈等操作外,还负责跳转到中断服务程序(isr_key),在中断服务函数判断产生了具体哪个中断 (不是哪个中断“源”),然后执行相应的操作。
关于注册地址:中断处理函数需要注册到异常向量表的中断异常地址上,实现中断异常的跳转。
中断服务程序需要注册到相应的中断“源”地址上,
因为两者都是通过注册地址来调用的,所以都不需要直接动手调用函数
/******************************************************************************************************************************************************************************************************************/ 具体程序如下:
#include "stdio.h"
// 1.设置按键
#define GPH2CON *((volatile unsigned int *)0xE0200C40)
#define GPH3CON *((volatile unsigned int *)0xE0200C60)
//中断状态寄存器,可以通过该寄存器知道中断是否发生
#define VIC0IRQSTATUS *((volatile unsigned int *)0xF2000000)
//中断选择
#define VIC0INTSELECT *((volatile unsigned int *)0xF200000C)
//中断使能寄存器
#define VIC0INTENABLE *((volatile unsigned int *)0xF2000010)
//禁止所有中断
#define VIC0INTENCLEAR *((volatile unsigned int *)0xF2000014)
//存放中断处理函数的地址
//VICAddress:向量地址寄存器.当IRQ中断产生时,
//此寄存器保存当前激活的中断向量的地址.
#define VIC0ADDR *((volatile unsigned int *)0xF2000F00)
//存放中断服务程序的地址
#define VIC0VECTADDR16 *((volatile unsigned int *)0xF2000140)
//#define VIC0_BASE (0xF2000000)
//#define VIC0EINT16ADDR ( *((volatile unsigned long *)(VIC0_BASE + 0x100 + 64)) )
//设置中断为下降沿触发
#define EXT_INT_2_CON *((volatile unsigned int *)0xE0200E08)
#define EXT_INT_3_CON *((volatile unsigned int *)0xE0200E0C)
//不屏蔽中断
#define EXT_INT_2_MASK *((volatile unsigned int *)0xE0200F08)
#define EXT_INT_3_MASK *((volatile unsigned int *)0xE0200F0C)
//记录中断,有中断发生相应位就会置1,初始化时相应位清零
//可以用来检测具体哪一个中断是否发生
//VIC0IRQSTATUS是检测是否有中断产生(总体与个体的关系)
#define EXT_INT_2_PEND *((volatile unsigned int *)0xE0200F48)
#define EXT_INT_3_PEND *((volatile unsigned int *)0xE0200F4C)
//中断向量表
#define _Exception_Vector 0xD0037400
#define pExceptionRESET ( *((volatile unsigned long *)(_Exception_Vector + 0x0)) )
#define pExceptionUNDEF ( *((volatile unsigned long *)(_Exception_Vector + 0x4)) )
#define pExceptionSWI ( *((volatile unsigned long *)(_Exception_Vector + 0x8)) )
#define pExceptionPABORT ( *((volatile unsigned long *)(_Exception_Vector + 0xc)) )
#define pExceptionDABORT ( *((volatile unsigned long *)(_Exception_Vector + 0x10)) )
#define pExceptionRESERVED ( *((volatile unsigned long *)(_Exception_Vector + 0x14)) )
#define pExceptionIRQ ( *((volatile unsigned long *)(_Exception_Vector + 0x18)) )
#define pExceptionFIQ ( *((volatile unsigned long *)(_Exception_Vector + 0x1c)) )
void IRQ_handle(void); //用来现场保护和恢复,并且跳转到中断处理函数,不用调用
void enable_irq(void); //使能cpsr的总中断
void disable_irq(void); //关闭cpsr的总中断
void key_isq(void);
void delay(void);
/************************************************************************************************/
int init_interrupt_reg()
{
//printf("here1\r\n");
//禁止所有中断
VIC0INTENCLEAR = 0xffffffff;
//设置成普通中断模式
VIC0INTSELECT = 0x0;
//清除中断地址
VIC0ADDR = 0x0;
return 0;
}
int init_gpio(void)
{
//printf("here3\r\n");
//设置GPH2 0~3为中断方式
//GPH2CON &= 0xffff0000;
GPH2CON |= 0x0000ffff;
GPH3CON |= 0x0000ffff;
//设置外部中断为下降沿触发
EXT_INT_2_CON &= 0xffff8888; //保留位不清零
EXT_INT_2_CON |= 0x00002222;
EXT_INT_3_CON &= 0xffff8888; //保留位不清零
EXT_INT_3_CON |= 0x00002222;
//打开外部中断16--19
EXT_INT_2_MASK &= 0xfffffff0;
//打开外部中断24--27
EXT_INT_3_MASK &= 0xfffffff0;
//通过清除中断记录,清除外部中断,写1清零
EXT_INT_2_PEND = 0xff;
EXT_INT_3_PEND = 0xff;
//使能16号外部中断源
VIC0INTENABLE |= (0x1<<16);
return 0;
}
int set_isr_addr(void)
{
//printf("here2\r\n");
//注册中断服务子函数到相应的中断响应地址
//每个源都有一个特有的地址,16号中断源
VIC0VECTADDR16 = (unsigned long)key_isq;
//注册中断处理函数到中断异常
pExceptionIRQ = (unsigned long)IRQ_handle;
return 0;
}
//编写中断服务子函数
void key_isq(void)
{
//printf("here6\r\n");
delay();//消抖
unsigned int reg = 0;
unsigned int reg1 = 0;
reg = EXT_INT_2_PEND;//有中断产生时相应位就会有记录为1
reg1 = EXT_INT_3_PEND; // 25--28的中断记录
//判断16--19号外部中断产生 了哪一个
if((reg & 0x01)==0x01)
{
//记录位为1,有中断,为EINT16
printf("key1(EINT16) down\r\n");
}
else if(reg & (0x01<<1))
{
//记录位为1,有中断,为EINT17
printf("key2(EINT17) down\r\n");
}
else if(reg & (0x01<<2))
{
//记录位为1,有中断,为EINT18
printf("key3(EINT18) down\r\n");
}
else if(reg & (0x01<<3))
{
//记录位为1,有中断,为EINT19
printf("key4(EINT19) down\r\n");
}
//判断25--28号外部中断产生 了哪一个
if((reg1 & 0x01)==0x01)
{
//记录位为1,有中断,为EINT19
printf("key5(EINT24) down\r\n");
}
else if(reg1 & (0x01<<1))
{
//记录位为1,有中断,为EINT19
printf("key6(EINT25) down\r\n");
}
else if(reg1 & (0x01<<2))
{
//记录位为1,有中断,为EINT19
printf("key7(EINT26) down\r\n");
}
else if(reg1 & (0x01<<3))
{
//记录位为1,有中断,为EINT19
printf("key8(EINT27) down\r\n");
}
VIC0ADDR =0x0; //随便写入一个值表示中断服务子函数结束
EXT_INT_2_PEND = 0xf; //通过写1去清零16--19相应的记录位
EXT_INT_3_PEND = 0xf; //通过写1去清零25--28相应的记录位
}
typedef void funp(void);//定义了funp的函数类型,记得加;号
//中断处理函数
void irq_handler(void)
{
//printf("here5\r\n");
unsigned int reg = 0;
void (*isr)(void);
reg = VIC0IRQSTATUS;
if(reg !=0)
{
//有中断产生
isr = (funp*)VIC0ADDR;//类型强转,读取中断向量的地址执行中断服务子程序
}
(*isr)();
}
/**************************************************************************************/
void init_exint(void)
{
disable_irq();
init_interrupt_reg();
set_isr_addr();
//irq_handler(void);由IRQ_handle()函数调用到
//中断处理函数和中断服务程序不用注册,
//key_isq(void);由于已经安装了地址,有中断产生时自然会调用
init_gpio();
enable_irq();
}
、*********************************************************************************************************************************************************************/
吐槽下博客园的程序文本格式。。。
虽然说程序写完了,准确的说是抄完了,现在让我一个写还是没有把握写出来,毕竟那么多寄存器不是那么好找的,更别说用了。在老师程序的基础上,完成了全部八个外部按键中断,
还有一点是要注意的是,由于裸机是没有标准库的,所以要想很好的调试程序,在裸机上面移植了网上的printf函数,只要把自己写的程序最后组装成一个函数,然后直接把组装好的函数扔进printf的主函数即可。
记得要把源代码赋值到printf的目录,直接加到项目工程是不能编译的,,,加进去之后还有把目录里面的加载到工程里面,否则修改不到。。。(这种错误,智商捉急啊。。)