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 = ;
EX1 = ;
PX1 = ;
EA = ;
   (如果不用AJMP $那种方式等待中断,通过查询IE0/IE1的状态,我们应该这样写)
LOOP:
JB IE0, EVENT_OCCUR:
LJMP LOOP
EVENT_OCCUR:
;这里相当于可以是中断处理程序
CLR IE0 ;记得清掉IE0的状态
LJMP LOOP
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和TL1来看)。一般当我们设置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 = ;
TR0 = ;
EA = ;

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 = ;
  EX1 = ;
  ET1 = ;
  ES = ;
  EA = ;   return ;
}
 
 
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, #, 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,#,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 = , BtnPushedTimes = ; void ButtonPushed()interrupt using //中断1(定时器0中断),使用寄存器组0
{
BtnPushedTimes++;
updateDigitalNumber(digitalNumsSet[BtnPushedTimes]); TH0 = TH0_Start;
TL0 = TL0_Start;
} void EventOccur()interrupt using //中断3(定时器1中断),使用寄存器组1
{
eventOccurTimes++;
if(eventOccurTimes == FinalOuccr)
{
BtnPushedTimes = ;
updateDigitalNumber(digitalNumsSet[BtnPushedTimes]);
eventOccurTimes = ;
}
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 = ;
ET1 = ;
EA = ; while(); return ;
} void updateDigitalNumber(unsigned char const digitalNum)
{
P2 = digitalNum;
}
 
  仿真以及代码下载:http://pan.baidu.com/s/1gfsTeoF
 
 
 
 

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

  1. 单片微机原理P0:80C51结构原理

    本来我真的不想让51的东西出现在我的博客上的,因为51这种东西真的太low了,学了最多就所谓的垃圾科创利用一下,但是想一下这门课我也要考试,还是写一点东西顺便放博客上吧. 这一系列主要参考<单片 ...

  2. 单片微机原理P1:80C51指令系统和编程方法

    0. 寻址方式 寻址方式在汇编中是很重要的,汇编所有的操作都是和和内存或者寄存器打交道的,在80C51里面一共7种寻址方式.   1. 立即寻址: 这个没什么好说的,就是往寄存器或者内存里面写立即数, ...

  3. 十天学会单片机Day1点亮数码管(数码管、外部中断、定时器中断)

    1.引脚定义 P3口各引脚第二功能定义 标号 引脚 第二功能 说明 P3.0 10 RXD 串行输入口 P3.1 11 TXD 串行输出口 P3.2 12 INT0(上划线) 外部中断0 P3.3 1 ...

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

    外部拓展其实是个相对来说很好玩的章节,可以真正开始用单片机写程序了,比较重要的是外部存储器拓展,81C55拓展,矩阵键盘,动态显示,DAC和ADC.   0. IO接口电路概念与存储器拓展 1. 为什 ...

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

    0. 串口通讯 0. 串口通讯的数据传输方式:单工(单向传输数据),半双工(非同时双向传输),全双工(同时,双向传输) 1. 根据通信方式的不同又分为同步通讯和异步通讯. 同步通讯:所有设备都使用同一 ...

  6. ATmega8仿真——外部中断的学习

    前面我们学习了ATmega8的I/O口作为通用数字输入/输出口来用时对LED数码管控制和扫描按键的应用: 但ATmega8多数的I/O口都是复用口,除了作为通用数字I/O使用,还有其第二功能,这里我们 ...

  7. STM32 外部中断详解(原理+配置代码)

    本文介绍了STM32基于标准外设库的外部中断配置,以及基于参考手册如何更加寄存器配置外部中断 文章目录 1 前言 2 STM32的外部中断 3 中断服务函数的映射关系 4 外部中断的配置 5 寄存器的 ...

  8. Cortex-M3学习日志(三)-- 外部中断0

    无论是哪款单片机应该都有对应的中断的功能,中断在嵌入式系统的地位毋庸置疑.LPC1768微处理器包括4个外部中断,分别是EINT0.EINT1.EINT2.EINT3对应的引脚分别是P2.10~P2. ...

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

    这是一篇阐述怎样对嵌入式SOC外部中断进行控制编程的方法论文章.希望读者理解本篇文章后.能够具备对市场上全部已经面世和将来面世的嵌入式芯片的外部中断进行控制编程的能力. 笔者原创的技术分享一直都恪守下 ...

随机推荐

  1. Python的字符串操作和Unicode

    字符串类型 str:Unicode字符串.采用''或者r''构造的字符串均为str,单引号可以用双引号或者三引号来代替.无论用哪种方式进行制定,在Python内部存储时没有区别. bytes:二进制字 ...

  2. 一个简单的php分页类代码(转载)

    入门级php分页类 原文地址:http://www.xfcodes.com/php/fenye/3608.htm 时间:2015-12-16 20:52:00来源:网络 php分页类. 复制代码代码如 ...

  3. eclipse中mavean的使用配置

    eclipse-jee-neon-R-win32 maven-3.3.9 JDK  jdk-8u101-windows-i586 eclipse中配置mavean的步骤就不说了,网上很多教程,也很简单 ...

  4. Firefly 性能测试 报告

    原地址:http://bbs.gameres.com/thread_223724.html Firefly 性能测试 主要考虑点 网络IO的并发 进程间通信压力 数据读写压力 测试机配置: 操作系统 ...

  5. 10个基于 Ruby on Rails 构建的顶级站点

    本文系国内 ITOM 行业领军企业 OneAPM 工程师翻译整理自 Raviraj Hegde 的文章 Top Sites Built with Ruby on Rails. 就其本身而言,Ruby ...

  6. Uva 12361 File Retrieval 后缀数组+并查集

    题意:有F个单词,1 <= F <=60 , 长度<=10^4, 每次可以输入一个字符串,所有包含该字串的单词会形成一个集合. 问最多能形成多少个不同的集合.集合不能为空. 分析:用 ...

  7. D-Bus,kdbus和Binder

    http://blog.sina.com.cn/s/blog_4af327e10101irie.html 材料来自:The unveiling of kdbus 和 Kdbus Details .后一 ...

  8. WCF - Overview

    WCF stands for Windows Communication Foundation. The elementary feature of WCF is interoperability. ...

  9. sql 不同server間寫入數據

    select * from sys.servers sp_dropserver @server =N'' sp_dropserver '' ,'droplogins' EXEC master.dbo. ...

  10. BZOJ_1008_[HNOI2008]_越狱_(简单组合数学+快速幂)

    描述 http://www.lydsy.com/JudgeOnline/problem.php?id=1008 监狱有连续编号为1...N的N个房间,每个房间关押一个犯人,有M种宗教,每个犯人可能信仰 ...