单片微机原理P2:80C51外部中断与定时器系统

0. 外部中断



  书上的废话当然是很多的了,对于中断我想大家应该早就有一个很直观的认识,就是“设置断点,执行外部外码,然后返回断点”这样的三个过程。中断给系统提供了一个良好的响应模式。当然了,响应中断的时候记得保护现场,这是写汇编的良好习惯。

  80C51一共是5个中断源,这五个中断源分别是外部中断0,1定时器中断0,1,串口中断。

1. 我们现在先来看外部中断:

一般开外部中断分为4个步骤(不用查询的方式的话):

1. 设置触发方式(IT0/IT1)

2. 开启外部中断(EX0/EX1)

3. 设定优先级(IP寄存器)

4. 开启总中断(EA)

查询方式只是多了一步看IE的值而已.

  代码(汇编代码)

SETB IT1
SETB EX1
SETB PX1 ;设定外部中断1为高级中断
SETB EA

  (C51代码)

IT1 = 1;
EX1 = 1;
PX1 = 1;
EA = 1;

2. 外部中断相关寄存器位:

  

IE0/IE1和IT0/IT1在TCON寄存器(88H),而EX0/EX1,EA在IE寄存器(A8H)

  IT0/IT1是设定外部中断0,1的触发方式的。比如当IT0为0时,是低电平触发,当INT0引脚(P3.2)为低电平时,IE置1,撤销的办法只有将外部输入的低电平变为高电平。当是下降沿触发时(INT0 = 1),则由硬件自动清0。

  IE0/IE1是中断请求位,当有中断请求的时候,这个位就会被置1,可以通过查询的方式来确定是否有中断的响应(闲的蛋疼的时候可以尝试下,就是简单的论询方式而已,JB判断一下即可)。

注意如果设定IT0/IT1为低电平触发的时候一定要注意中断在引脚维持的时间,设置不好将引发玄学。如果引脚低电平维持时间太短,那么中断可能不会被响应,如果太长,有可能让中断处理程序执行完了都还没变回高,使得中断被一直响应。(低电平触发软件/硬件修改IE0/IE1是无效的)

  (PS:当然这种情况可以引入锁存器来解决,只要锁住INT0的信号为低电平就可以了,比如书上采用了D触发器,把外部中断接入D触发器的时钟断,D触发器的输入口接地,Q端接INT0,然后随便拿一个P口置D触发器异步置位端,这样一旦有中断响应,D触发器的输出为0,然后中断处理程序给对应的异步置位端1的信号,这样就可以保证在整个中断处理程序之内INT0的引脚都是1(撤销IE),中断处理程序结束后再把异步置位端为0,以便接受其他中断)。

  但是如果IT0/IT1为下降沿触发的时候就不会这样,如果处理器在两个机器周期扫描到INT0引脚的电平先后为高电平和低电平,那么就会设置中断标志位为1,直到中断被响应之前都不会被撤销,一旦中断响应后,硬件将自动将IE0/IE1置0。

  

  

  外部中断优先级的优先位设置也是在IP位中的(Interrupt Priority),在普通的80C51中,只能设定两个中断优先级(当然前面我们已经说了新型的可以达到4个了),中断优先级的自然顺序是规定好了的,是按照外部中断0,定时器中断0,外部中断1,定时器中断1,串口中断的顺序来定的。(在C51里面这些interrupt分别是0-4)在80C51中,中断是可以嵌套的,也就是遵循高级中断可以打断低级中断,同级中断不可以重入这样的规则,在80C51中,中断嵌套最大层数为两层。)

  3. 关于中断响应时间:

  处理器收到中断请求后,下一个机器周期是否转去执行中断服务子程序,还受到以下影响:

  ① 若当前机器周期不是处理器正在执行的指令的最后一个机器周期,则需要等到指令执行完成。

  ② 若正在执行RETI指令或者是其他读写与中断有关的寄存器IE、IP的指令,则需要在执行完该指令后,再执行一条指令,然后再转入中断服务子程序。

  ③ 中断返回后至少执行一条指令后才能响应新的中断。

  中断申请到执行第一条中断服务程序的最短时间是3个机器周期(优先权扫描1机器周期,LCALL指令2个机器周期)。若遇到不是执行指令的最后一个机器周期和正在执行RET、RETI或任何访问IE或IP寄存器指令时,则需要最长的等待时间不超过8个机器周期(3个最短周期,和5个最长等待周期。)

1. 定时器/计数器中断



  定时器和计数器中断也是掌握怎么开的步骤就可以了,中断处理程序打的写法除了地址不一样其他没什么不一样。

  开定时器和计数器要有6个步骤:

1. 设定TMOD(确定计数还是计时,确定什么方式)

2. 设定定时or计数时间

3. 打开ET0/ET1

4. 打开TR0/TR1

5. 设定优先级(IP寄存器)

6. 开启总中断(EA)

  定时器/计数器中断在但单片机上的中断的引脚是P3.4(T0),P3.5(T1),当选定内部时钟时,对应的机器周期脉冲为f0sc/12(即对应晶振的12分频)。

  当定时器/计数器作为计数器时,其最大频率是晶振的24分频(Fosc / 24)。(因为采集一个下降沿需要两个机器周期)。

  当定时器/计数器作为定时器时,其最大频率是晶振的12分频(Fosc / 12)。周期T = 12*(1/f)

  1. 定时器/计数器相关寄存器:

  

  

  

  

  

  TH0/TL0,TH1/TL1这四个寄存器都是不能位寻址的,代表的是定时器/计数器0,定时器/计数器1的当前值,当我们开启定时器/计数器时前要设定他们的初值。

TCON这个寄存器是上面提过的,现在我们来看属于定时器/计数器的那一部分,TR1/TR0是开启定时器/计数器的标志位,TF1/TF0是标识计数器/定时器满的标志位(当TH0/TL0的内容达到2^x - 1的时候(x代表的x位的计数器/定时器)),这个时候TF1/TF0就置1(和外部中断一样,我们也可以用查询这个位的状态方式来代替中断处理程序的方式)。

  IE位的ET1和ET0是允许定时/计数器标志位,不要和TR0/TR1搞混了。

  TMOD是一个不能位寻址的寄存器,只能整体操作。TMOD分为两个部分,其中高四位是对应设置T1,低四位对应设置的是T0。

  GATE位是计数器门控制位:当GATE = 0时,只要启动TR0/TR1定时器就开始工作。当GATE = 1时,还需要一个外加条件即INT1/INT0为0时才会启动计数。

  C/T位:当C/T = 0,为定时器,当C/T = 1,为计数器。

  M1M0位:设定定时器/计数器打的工作方式(一共四种)

  设定了工作方式后需要做的就是根据工作方式来设定T0/T1的初值了,只要记住这个公式就可以了:

  其中X的值取决于这是多少位的计数器or定时器,比如如果设定工作方式0,那么x应该为13

  说下几个比较值得注意的坑:

1. 对于方式0,设定其初值的时候一定要记得其13位数的安排是TH0/TH1 8位,TL0/TL1 5位(而不是反过来)。

比如,80C51的工作时钟为6MHZ,定时时间为800us,使用定时器0工作方式0,如何设定初值

  所以TH0的值是0F3H,TL0的值是10H(而不是TH0 = 1EH,TL0 = 70H)

  2. 除了方式2可以自动重装外(如果采用了定时器2的工作方式,对应定时器/计数器TH和TL一定要设定一样),其他方式当中断响应以后一定要给对应定时器/计数器TH和TL设定新的值,不然不会响应下一次中断。

3. 方式3是一个比较特殊的定时方式,要注意只有T0才能设定为方式3,T1设定为方式3的时候会停止计数。

  

  我们可以看到如果设定为方式3的时候相当于把TH0和TL0拆成两个单独的8位计数器,其中TL0占用TR0和TF0,TH0占用TR1和TR0,TL0作为一个单独的8位计数器/定时器和其他定时器/计数器没有区别(只是是8位而已)。但是TH0不一样,由于它没有C/T位和GATE位控制,所以它只能作为一个定时器。

  在T0工作在方式3的时候,T1可以工作在方式0,1,2(由于TR1和TF1被TH0占用,所以想要停止T1工作只能把它的工作方式设定为工作方式3,同时也不能查询TF1的状态来看触发次数,只能直接查看TH1和TH0来看)。一般当我们设置T0为方式3时,T1会设置为方式2(自动填装),以方便串口的发送。

  那怎么打开T?比如我现在要打开定时器/计数器0,以工作方式1工作,定时为10ms(计算可知T0初值为0EC78H)

  (汇编代码)

MOV TMOD, #00000001B
MOV TH0, #0ECH
MOV TL0, #78H
SETB ET0
SETB TR0
SETB EA

(C51)

TMOD = 0x01;
TH0 = 0xEC;
TL0 = 0x78;
ET0 = 1;
TR0 = 1;
EA = 1;

2. 一些例子



  1. 编程实现INT1为高级中断,下降沿触发,T0设为低优先级中断,串行口设定为高优先级中断,其他中断禁止

  (汇编)

    ORG 0000H
    LJMP MAIN
    ORG 0100H
MAIN:
    MOV IP, #014H    ; PS:PT1:PX1:PT0:PX0 = 10100(INT1高级中断,串口高级中断)
    SETB IT1            ;外部中断下降沿触发
    SETB EX1            ;允许外部中断1
    SETB ET1            ;允许定时器中断1
    SETB ES            ;打开串口中断
    SETB EA
END                ;写汇编程序千万不要忘记写END

(C51)

int main()
{
  IP = 0x14;
  IT1 = 1;
  EX1 = 1;
  ET1 = 1;
  ES = 1;
  EA = 1;

  return 0;
}

2. 80C51单片机晶振频率为6MHZ,要求定时为10ms,定时器0工作在方式0,1,2时,定时器初值应该设置为多少?要求用16进制表示:

解:

  方式0:2^13 - (6*10^6 * 10 *10^-3 )/12 = 3192 -> TH0(063H) TL0(018H)(注意TH0放高8位,TL0放低5位)。

  方式1:2^16 - (6*10^6 * 10 *10^-3 )/12 = 60536 -> TH0(0ECH) TL0(078H)

  方式2:2^8 - (6*10^6 * 10 *10^-3 )/12 < 0 (溢出不能设置)

3. 用定时器/计数器T0产生时钟,使连接P1口的8盏灯循环点亮(1s一次),用中断方式编写

(这一题定时为1s太长了,需要我们拓展,我们可以定时个10ms然后定100次就可以了,用循环队列的思想即可完成任务)

    ORG 0000H
    LJMP MAIN
    ORG 000BH
    LJMP EVENT_OCCUR
    ORG 0100H
MAIN:
    MOV R0, #00H    ;设定队列初值0

    MOV P1, #01H
    MOV TMOD, #00000001B
    MOV TH0, #0ECH
    MOV TL0, #78H
    SETB ET0
    SETB TR0
    SETB EA
    AJMP $
EVENT_OCCUR:
    CLR EA
    PUSH ACC     ;保护现场,虽然在这一题没必要
    INC R0
    CJNE R0, #100, NEXT_EVENT
    RL A
    MOV P1, A
    MOV R0, #00H

    NEXT_EVENT:
        MOV TH0, #0ECH    ;一定要记得重设初值
        MOV TL0, #78H

    POP ACC        ;恢复现场
    RETI
END

  

  4. 测手速:统计两秒内按下按钮的次数,显示在数码管上,并且2S后让数码管清0

  (汇编代码)

_CODE_SEGMENT:
    ORG 0000H
    LJMP START
    ORG 000BH
    LJMP BUTTON_HASED_PUSHED
    ORG 001BH
    LJMP EVENTLOOP_OCCUR
    ORG 0100H
START:
    ;crystal oscillator frequency is 12MHZ

    ;Register 0 is uesd to log the number pushing actions
    MOV R0,#00H

    ;register 1 is uesd to log the microsecond event times
    MOV R1,#00H

    MOV TH0,#0FFH
    MOV TL0,#0FFH

    MOV TH1,#0D8H
    MOV TL1,#0F0H

    MOV TMOD,#00010101B

    ;we must make time interrupt 1 is the advance interrupt
    MOV IP,#02H

    SETB ET0
    SETB TR0

    SETB ET1
    SETB TR1

    SETB EA
    LCALL DISPLAY_DIGITAL_NUM

    AJMP $
BUTTON_HASED_PUSHED:
    INC R0
    UPDATE_DIGITL_NUM:
    LCALL DISPLAY_DIGITAL_NUM

    MOV TH0,#0FFH
    MOV TL0,#0FFH

    RETI
EVENTLOOP_OCCUR:
    PUSH ACC

    INC R1
    CJNE R1,#200,NEXT_EVENT

    MOV R0,#00H
    LCALL DISPLAY_DIGITAL_NUM
    MOV R1,#00H

    NEXT_EVENT:
    MOV TH1,#0D8H
    MOV TL1,#0F0H

    POP ACC
    RETI
DISPLAY_DIGITAL_NUM:
    CLR EA
    PUSH ACC

    MOV A, R0
    MOV DPTR, #DIGITAL_NUM
    MOVC A, @A + DPTR
    MOV P2, A

    POP ACC
    SETB EA
    RET
_DATA_SEGMENT:
DIGITAL_NUM:
    DB 0C0H, 0F9H, 0A4H,0B0H,99H,92H,82H,0F8H,00H,90H
    DB 88H, 83H, 0C6H, 0A1H, 86H, 8EH
END

(C51代码)

#include<reg51.h>
#define FinalOuccr 200

unsigned char const digitalNumsSet[]
                    = {0xC0, 0xF9, 0xA4, 0xB0,
                       0x99, 0x92, 0x82, 0xF8,
                       0x00, 0x90, 0x88, 0x83,
                       0xC6, 0xA1, 0x86, 0x8E};

enum StarterTime{ TH0_Start = 0xFF,
                  TL0_Start = 0xFF,
                  TH1_Start = 0xD8,
                  TL1_Start = 0xF0};

//-----------------------------------------------------
void updateDigitalNumber(unsigned char const digitalNum);

static int eventOccurTimes = 0, BtnPushedTimes = 0;

void ButtonPushed()interrupt 1 using 0//中断1(定时器0中断),使用寄存器组0
{
    BtnPushedTimes++;
    updateDigitalNumber(digitalNumsSet[BtnPushedTimes]);

    TH0 = TH0_Start;
    TL0 = TL0_Start;
}

void EventOccur()interrupt 3 using 1//中断3(定时器1中断),使用寄存器组1
{
    eventOccurTimes++;
    if(eventOccurTimes == FinalOuccr)
    {
        BtnPushedTimes = 0;
        updateDigitalNumber(digitalNumsSet[BtnPushedTimes]);
        eventOccurTimes = 0;
    }
    TH1 = TH1_Start;
    TL1 = TL1_Start;
}

int main()
{
    TH0 = TH0_Start;
    TL0 = TL0_Start;

    TH1 = TH1_Start;
    TL1 = TL1_Start;

    TMOD = 0x15;
    TCON = 0x50;
    IP = 0X02;
    ET0 = 1;
    ET1 = 1;
    EA = 1;

    while(1);

    return 0;
}

void updateDigitalNumber(unsigned char const digitalNum)
{
    P2 = digitalNum;
}

  仿真以及代码下载:http://pan.baidu.com/s/1gfsTeoF

时间: 2024-12-09 22:47:30

单片微机原理P2:80C51外部中断与定时器系统的相关文章

单片微机原理P3:80C51外部拓展系统

外部拓展其实是个相对来说很好玩的章节,可以真正开始用单片机写程序了,比较重要的是外部存储器拓展,81C55拓展,矩阵键盘,动态显示,DAC和ADC. 0. IO接口电路概念与存储器拓展 1. 为什么需要IO电路?:1. 协调计算机与外设的速度的差异 2. 输入/输出过程中的状态信号 3. 解决计算机信号与外设信号之间不一致 2. IO传送方式三种:1. 无条件传送(灯,DAC),2. 查询,3. 中断(ADC). 3. DMA存储方式(直接传输数据不通过CPU(不需要CPU),这种方式实际上已经

单片微机原理P4:80C51串口与串行总线拓展

0. 串口通讯 0. 串口通讯的数据传输方式:单工(单向传输数据),半双工(非同时双向传输),全双工(同时,双向传输) 1. 根据通信方式的不同又分为同步通讯和异步通讯. 同步通讯:所有设备都使用同一个时钟,称为同步时钟.在数据传送时,以若干个数据字符(称为数据块)为单位进行传输,每个数据块包括同步字符.数据块和校验字符CRC. 异步通信是指在串行通信中,接收设备和发送设备有各自的时钟信号,异步通信以字符为单位进行数据传送,不过通信中这些时钟频率必须保持一致. 2. 波特率和比特率 波特率是每秒

jz2440裸板开发之:外部中断

实验目的:   利用外部中断的方式,来实现点亮对应的LED 实验原理:中断的最大好处就是让CPU避免了采用查询的方式来处理中断处理程序要干的事,中断的三个必要元素:中断源.中断控制器.中断处理函数.在arm9上有七种异常, (这里把重启也包括在内,另外还有一个reversed,加起来应该有八个,只是这个中断向量地址没有用而已).当中断发生时,CPU会自动跳到中断向量地址处执行程序,由于每个中断向量都只有4字节的地址空间,所以我们经常在此处放一个跳转语句.上面CPU的自动跳转是硬件自动完成的,我们

51单片机第五弹---外部中断

写了半天掉线了... 不写了上定义... 什么叫中断 中断是指CPU在执行当前程序的过程中,由于某种随机出现的外设请求或CPU内部的异常事件,使CPU暂停正在执行的程序而转去 执行相应的服务处理程序:当服务处理程序运行完毕后,CPU再返回到暂停处继续执行原来的程序. 51单片机的中断 80c51提供有5个中断源,分别为:2个外部中断,2个定时/计数器中断,1个串口发送/接收中断.并且具有2个中断优先级,可以实现2级中断服务程序嵌套. 首先要弄中断设置 ,中断原理图如下: 以 设置INT0为例:I

嵌入式外部中断控制编程方法论—比較CC2541(51核)和S5PV210(ARM核)

这是一篇阐述怎样对嵌入式SOC外部中断进行控制编程的方法论文章.希望读者理解本篇文章后.能够具备对市场上全部已经面世和将来面世的嵌入式芯片的外部中断进行控制编程的能力. 笔者原创的技术分享一直都恪守下面原则: 从需求的角度去理解嵌入式各种软件和硬件模块的作用和组成.并从芯片系统设计的角度去阐述怎样进行控制编程. 前者对于理解复杂的系统(如linux的各个子系统)是非常有效的:后者所讲的是代表一个芯片设计project师的视觉,芯片模块由他负责设计,他对于该模块的控制编程自然是最有发言权的. 笔者

LPC1768外部中断与GPIO中断

LPC1768的外部中断严格来说只有四个,分别是EINT0,EINT1,EINT2,EINT3,技术手册上有如下说明 控制这四个外部中断靠以下寄存器 这三个寄存器的0 1 2 3位分别代表中断的0 1 2 3,EXTINT寄存器表示中断是否发生,在发生中断的时候该寄存器会置位,可以通过写1清零,EXTMODE寄存器表示触发模式,有电平触发和变化沿触发两种,EXTPOLAR与EXTMODE,在电平触发模式下,决定高电平还是低电平触发,在变化沿触发的情况下决定上升沿还是下降沿触发 这三个中断分别相关

STM32学习笔记(九) 外部中断,待机模式和事件唤醒

学会知识只需要不段的积累和提高,但是如何将知识系统的讲解出来就需要深入的认知和系统的了解.外部中断和事件学习难度并不高,不过涉及到STM32的电源控制部分,还是值得认真了解的,在本文中我将以实际代码为例详细讲解这些内容,希望对每一个阅读者有帮助. 1.外部中断 如果已经学习了SysTick系统时钟滴答实验,掌握了Cortex-M3中断的相关知识,那么外部中断也是比较好理解的,和SysTick中断一样,外部中断也是当有信号触发时,如果中断屏蔽寄存器允许触发,就会产生中断,这时CPU查找中断向量表,

LPC1788的外部中断和GPIO中断

首先是gpio中断,这一点和1768不同,1768使用的中断时和eint3共用中断通道,到了1788,专门为gpio开辟了中断 #ifndef __JOYPAD_H_ #define __JOYPAD_H_ #include "sys.h" #include "delay.h" #define JOYPAD_A        0X01 #define JOYPAD_B        0X02 #define JOYPAD_C        0X03 #define

外部中断0/1实现点亮LED代码

1 #include <reg52.h> 2 #include <stdio.h> 3 sbit LED = P2^0; 4 //初始化函数 5 void init(){ 6 //打开外部中断1的分开关 7 EX1=1; 8 //打开外部中断总开关 9 EA=1; 10 //外部中断1的标志位,标志着外部中断在响应中断 11 IT1=1; 12 } 13 //主函数 14 void main(void){ 15 //调用初始化函数 16 init(); 17 /*兜底循环,因为主函