C51单片机时钟中断及主要问题一. 中断一般概念51单片机一共设有5个中断源. 引起CPU中断根源, 成为中断源. 中断嵌套, 也即单片机在处理一个中断时又发生了一个中断, 单片机中断当前中断程序, 转而去处理新的中断程序. 中断优先级, 通过中断优先级寄存器设置.1. 中断源及其默认优先级52单片机共有6个中断源, 如下:符号名称产生条件INT0 外部中断0 由P3.2端口线引入, 低电平或下降沿引起INT1 外部中断1 由P3.3端口线引入, 低电平或下降沿引起T0 定时器/计数器0 由T0计数器计满回零引起T1 定时器/计数器1 由T1计数器计满回零引起T2 定时器/计数器2 由T2计数器计满回零引起TI/RI 串行口中断串行端口完成一帧字符发送/接受后引起其中, T2是52单片机独有的.其默认中断优先级别如下:中断源默认优先级别序号(C语言用) 入口地址(汇编)INT0 最高0 0003HT0 第2 1 000BHINT1 第3 2 0013HT1 第4 3 001BHTI/RI 第5 4 0023HT2 最低 5 002BH2.中断控制中的特殊功能寄存器SFR(1). 中断允许寄存器IE(Interrupt Enable)位序号位符号位地址说明D7(高位) EA 0xAF 全局中断允许位D6 -- -- 无效位D5 ET2 0xAD Timer2中断允许位D4 ES 0xAC 串行口中断允许位D3 ET1 0xAB Timer1中断允许位D2 EX1 0xAA 外部中断1中断允许位D1 ET0 0xA9 Timer0中断允许位D0(低位) EX0 0xA8 外部中断0中断允许位备注对于上述所有中断允许位:1: 打开该中断0: 关闭该中断(2). 中断优先级寄存器IP(Interrupt Priority)位序号位符号位地址说明D7(高位) -- -- 无效位D6 -- -- 无效位D5 -- -- 无效位D4 PS 0xBC 串行口中断优先级控制位D3 PT1 0xBB Timer1中断优先级控制位D2 PX1 0xBA 外部中断1中断优先级控制位D1 PT0 0xB9 Timer0中断优先级控制位D0(低位) PX0 0xB8 外部中断0中断优先级控制位备注对于上述所有中断优先级控制位:1: 设置对应的中断为高优先级0: 设置对应的中断为低优先级二. 单片机的定时器中断定时器/计数器实质是一个16位的加1计数器, 由高8位(THx)和低8位(TLx)两个寄存器组成. TMOD是定时器/计数器的工作方式寄存器, 确定工作方式和功能; TCON是控制寄存器, 控制T0, T1的启动和停止及设置溢出标志.1. 定时器/计数器工作方式寄存器TMOD定时器/计数器工作方式寄存器在特殊功能寄存器中, 字节地址为0x89, 不能位寻址, TMOD 用来确定定时器的工作方式及功能选择. 单片机复位时TMOD全部被清0. 各位的含义如下:位序号控制位符号说明D7(高位) Timer1 GA TE 门控制位. 0表示Timer启动与停止仅受TCON寄存器TRx(x=0, 1)控制1表示Timer启动与停止由TCON寄存器中TRx(x=0,1)和外部中断引脚(INT0或INT1)上的电平状态共同控制D6 C/T___定时器和计数器模式选择位, 0:定时器, 1:计数器D5 M1 工作方式选择位. 见下表.D4 M0D3 Timer0 GA TED2 C/T___D1 M1D0(低位) M0备注M1 M0 工作方式0 0 方式0, 为13位定时器/计数器0 1 方式1, 为16位定时器/计数器1 0 方式2, 8位初值自动重装的8位定时器/计数器1 1 方式3, 仅适用于T0, 分成两个8位计数器, T1停止计数2. 定时器/计数器控制寄存器TCON位序号位符号位地址说明D7(高位) TF1 0x8F Timer1溢出标志位. 计满溢出后, 由硬件使TF1置1,并且申请中断. 进入中断处理程序后, 由硬件自动清0. 注意: 使用定时器中断, 该位不需要人为操作, 但如果使用软件查询方式的话, 必须使用软件清0.D6 TR1 0x8E Timer1运行控制位. 由软件清0关闭定时器 1. 当GA TE=1, 且INT1为高电平是, TR1置1启动定时器1; 当GA TE=0时, TR1置1启动定时器1.D5 TF0 0x8D Timer0溢出标志位. 其它同TF1.D4 TR0 0x8C Timer0运行控制位. 其它同TR1.D3 IE1 0x8B 外部中断1请求标志D2 IT1 0x8A 外部中断1触发方式选择位D1 IE0 0x89 外部中断0请求标志D0(低位) IT0 0x88 外部中断0触发方式选择位备注三、中断服务程序的C语言写法1、中断响应条件●中断源有中断请求;●此中断源的中断允许位为1;●CPU 开中断(即EA=1)。
以上三条同时满足时,CPU 才有可能响应中断2、2、C51的中断函数格式如下:void func_name() interrupt 中断号[using 工作组]{//中断服务程序内容}●中断号: 指单片机中几个中断源的序号. 是编译器识别不同中断的唯一符号.5个中断源排序:0代表外部中断0中断;1代表定时器/计数器0中断;2代表外部中断1中断;3代表定时器/计数器1中断;4代表串口中断。
●工作组: 可选部分. 指这个中断函数使用单片机内存中4组工作寄存器中的哪一组, C51编译器在编译时会自动分配工作组, 故通常省略.3、3、P3.2 口的外部中断EA=1; //开总中断EX0=1; //开外部中断0//IT0=1; 当IT=1 时位跳变沿外部中断触发方式//当IT=0 时为电平的外部中断触发方式TCON=0x01; 这是对寄存器将IT 置1,和IT0=1 的效果是一样的。
因为TCON 的地址是可以被8 整除,所以可以对其进行位操作4、4、定时/计数器应用举例初始化程序应完成如下工作:●对TMOD 赋值,以确定T0 和T1 的工作方式。
●计算初值,并将其写入TH0、TL0 或TH1、TL1。
●中断方式时,则对IE 赋值,开放中断。
●使TR0 或TR1 置位,启动定时计数器定时或计数。
定时器T0 中断的初始化TMOD=0x01; \\设定工作方式为16 位定时器TH0=(65536-50000)/256; \\赋定时器T0 高四的值为50MS(对256 求模)TL0=(65536-50000)%256; \\赋定时器T0 低四的值为50MS(对256 求余)EA=1; \\开总中断ET0=1; \\开定时器T0 中断TR0=1; \\启动定时器T0当定时器时间到产生中断就自动跳到以下中断函数,中断函数不需要做任何声明;void timer0( ) interrupt 1{TH0=(65536-50000)/256; \\重装初值TL0=(65536-50000)%256; \\重装初值\\往下写CPU 要处理的事情就OK 了}四. 补充1. 普通51系列单片机存储空间资源分配情况空间名称地址范围说明DA TA D:00H~7FH 片内RAM直接寻址区BDA TA D:20H~2FH 片内RAM位寻址区IDA TA I:00H~FFH 片内RAM间接寻址区XDA TA X:0000H~FFFFH 64K片外RAM数据区CODE C:0000H~FFFFH 64K片内外ROM代码区BANK0~BANK31 B0:0000H~FFFFH::B31:0000H~FFFFH分组代码区,最大可扩展32X64KB ROM五、C51应注意的问题C51的中断函数的格式为:void FuncIr(void) interrupt x [using y]以下是一些分析:一、中断函数是一个特殊的函数,没有参数,也没有返回值;但是程序中允不允许使用return呢?答案是允许的,不过只能用"return;",不能用"return(z);";用在一些需要快速返回的地方,对应的汇编会有多个ret语句,相对效率会高一些。
二、using的用法,using可以修饰任何函数,不过建议只用来修饰中断函数;简单的说,“using”会指定工作寄存器组,由于中断函数一般都是比较紧急的事情,有时一条语句都会斤斤计较,所以使用using切换寄存器组可以省去一些压栈的动作,由于51只有两级中断,同级中断不能被打断,因此,我们可以同级中断设成同样的寄存器组,从某种意义上来说,有一组寄存器是多余的。
同时建议中断函数应该使用using这个关键字。
三、中断中调用函数,首先要讨论中断函数中调用函数的必要性,有些情况中断中调用函数还是必要的,这个时候是不是该调用函数,其实和普通函数差不多,首先是这个函数如果调用多次,或者要带一些参数什么的就更加必要的;假如只调用一次且无参数无返回的函数要直接写,因为如果用函数,至少会增加CALL 和RET两条语句,实际调试发现:当你程序比较复杂时,你将那部分单独拉出来做成函数,可能代码和时间都会更好(/bbs/moredata.asp?id=19732&syid=654577#aa)。
四、中断中调用的函数最好不要被中断外的其它函数调用,因为会出现“重复调用”的警告,有时这种调用是很致命的,有人说这个函数可以用reentrant 来修饰,是的,的确可以这样解决,不过不建议这么做,也许这样会跟你减少很多堆栈空间,并且整个程序的优化要差很多,建议出现这种情况就把这个函数写两遍,分成两个函数分别调用。
五,中断调用了函数,会出现一些莫名其妙的问题,一些数据不对。
其实一般是因为汇编中使用了绝对寄存器引起的(/bbs/moredata.asp?id=19732&syid=654114),有人说中断函数使用那个寄存器组,被中断调用的函数就使用哪个寄存器组,我认为这样不好:这样会增加额外的消耗,使用using会增加一下语句:PUSH PSWMOV PSW, #XX....POP PSW更重要的是,使用using的函数不能有返回值,这是致命伤推荐的方法有两种:1、使用“#pragma NOAREGS”禁止使用绝对寄存器2、使用“#pragme RB(x)”来指定本文件的工作寄存器组六、一般说来,要求中断函数尽可能的短,但也有特殊情况,有些前/后台的系统中,就会把很多相对重要的事情放到定时中断(这个定时中断类似实时操作系统中的时钟节拍)去做,而且程序很长。
中断函数也是一个函数而已,只要系统有必要,可以做一些看似不合理的事情,该出手时就出手,就像goto语句一样。