• (51单片机)第三章-数码管显示原理及应用实现-中断


     

    3.4 中断概念

            中断使得单片机具有对外部或内部随机发生的时间实时处理的能力,是单片机最重要的功能之一。51单片机内部有一共5个中断源,这里介绍的是其中一种情况——定时器中断

      

            引起CPU中断的根源称为中断源,中断源向CPU提出中断申请,CPU暂时中断原来的事A,转而去处理事B,处理完毕事B后,再回到原来被中断的地方(即断点),称为中断返回。实现上述中断功能的部件称为中断系统中断机构)。

            中断的开启与关闭、设置启用哪一个中断都是由单片机内部的一些特殊寄存器来决定。

            中断嵌套 中断优先级 中断允许寄存器IE 中断优先级寄存器IP

            52单片机一共有6个中断源,它们的符号、名称及产生的条件分别解释如下:

            (1)INTO-外部中断0,由P3.2端口线引入,低电平或下降沿引起;

            (2)INT1-外部中断1,由P3.3端口线引入,低电平或下降沿引起;

            (2)T0——定时器/计数器0中断,由T0计数器计满回零引起;

            (3)T1——定时器/计数器1中断,由T1计数器计满回零引起;

            (4)T2——定时器/计数器2中断,由T2计数器计满回零引起;

            (5)TI/RI-串行口中断,串行端口完成一帧字符发送/接收后引起

            默认的中断级别

            中断允许寄存器IE

            中断允许寄存器用于设定各个中断源的打开和关闭。IE在特殊功能寄存器中,字节地址A8H,位地址(由低位到高位)分别是A8H~AFH,该寄存器可以进行位寻址,即可对该寄存器的每一位进行单独操作,单片机复位时IE全部被清0。定义表:

            中断优先级寄存器

            中断优先级寄存器在特殊功能寄存器中,字节地址为B8H,位地址(由低位到高位)分别是B8H~BFH。IP用来设定各个中断源属于两级中断中的哪一级。该寄存器可以进行位寻址,即可对该寄存器的每一位进行单独操作。单片机复位时,IP全部被清0。定义表:

             1:高优先级中断;0:低优先级中断

            PS—串行口中断;PT1—定时器/计数器1;PX1—外部中断1;PT0—定时器/计时器0;PX0—外部中断0。

            在51单片机系列中,高优先级中断能够打断低优先级中断以形成中断嵌套,同级中断之间,或低级对高级中断则不能形成中断嵌套。若几个同级中断同时向CPU请求中断响应,在没有设置中断优先级的情况下,按照默认中断级别,在设置中断优先级后,则按照设置顺序确定相应的先后顺序。 

    3.5 单片机的定时中断

            51单片机内部共有2个16位可编程的定时器/计数器,即定时器T0和定时器T1。52单片机内部多一个T2定时器/计数器。既有定时功能又有计数功能。通过设置与它们相关的特殊功能寄存器可以选择启用定时功能或技术功能。

            注意,定时器是单片机内部一个独立的硬件部分,与CPU和晶振通过内部某些控制线连接并相互作用,CPU一旦设置开启定时功能后,定时器便会在晶振的作用下自动开始计时,当定时器的计数器装满后,会产生中断。

            定时器/计数器的实质1计数器(16位),由高8位和低8为两个寄存机组成。TMOD是定时器/计时器的工作方式寄存器,确定工作方式和功能;TCON是控制寄存器,控制T0、T1的启动和停止以及设置溢出标志。下图为定时器/计数器结构框图:

            加1计数器的输入计数脉冲有两个来源:(1)有系统时钟振荡器输出脉冲经12分频后送来;(2)T0或T1引脚输入的外部脉冲源,每来一个脉冲计数器加1,当计数器加到全1时,再输入一个脉冲就使计数器归零,且计数器的溢出使TCON寄存器中TF0或TF1置1,向CPU发出中断请求(定时器/计数器中断允许时),表示时间已到或计数值已满。由溢出时计数器的值减去计数初值才是加1计数器的计数值。

            设置为定时器模式时,加1计数器是对内部机器周期计数(1个机器周期等于12个振荡周期,即计数频率为晶振频率的1/12)。计数值N乘以机器周期T。就是定时时间t;设置为计数器模式时,外部事件计数脉冲由T0或T1引脚输入到计数器。在每个机器周期的 S5P2期间采样T0、T1引脚电平。当某周期采样到一高电平输入,而下一周期又采样到一低电平时,则计数器加1,更新的计数值在下一个机器周期的 S3P1 期间装入计数器。由于检测一个从 1~0的下降沿需要2个机器周期,因此要求被采样的电平至少要维持一个机器周期当晶振频率为12MHz时,最高计数频率不超过 1/2MHz,即计数脉冲的周期要大于2μs

            寄存器

            单片机在使用定时器或计数器功能时,通常需要设置两个与定时器有关的寄存器:定时器/计数器工作方式寄存器 TMOD与定时器/计数器控制寄存器 TCON。

            定时器/计数器工作方式寄存器TMOD

            定时器/计数器工作方式寄存器在特殊功能寄存器中,字节地址为89H,不能位寻址,TMOD 用来确定定时器的工作方式及功能选择。单片机复位时TMOD 全部被清 0。定义表:

            如表所示,TMOD的高4位用于设置定时器1,低四位用于设定定时器0,对应的4位含义如下:

            (1)GATE—门控制位:

               1)GATE=0,定时器/计数器启动与停止仅受TCON寄存器中TRX(X=0,1)来控制

              2)GATE=1,定时器/计数器启动与停滞由TCON寄存器中TRX(X=0,1)和外部中断引脚(INT0或INT1)上的电平状态共同控制

            (2)C/ $ \overline{\text{T}} $—定时器模式合计数器模式选择位:0-定时器;1-计时器

            (3)M1M0——工作方式选择位(4种,见下表):

            定时器/计数器工作控制寄存器TCON

            定时器/计数器控制寄存器在特殊功能寄存器中,字节地址为88H,位地址(由低位到高位)分别是88H~8FH,该寄存器可进行位寻址。TCON寄存器用来控制定时器的启、停,标志定时器溢出和中断情况。单片机复位时 TCON 全部被清 0。其中,TF1、TR1、TF0和 TR0 位用于定时器/计数器:IE1、IT1、IE0和IT0位用于外部中断。定义表如下:

            (1)TF1—定时器1溢出标志位

            当定时器1计满溢出时,由硬件使TF1置1,并且申请中断。进入中断服务程序后,由硬件自动清0。需要注意的是,若使用定时器中断则该位无需人为操作,但若使用软件查询方式的话,当查询到该位置1后,就需要用软件清0。

            (2)TR1—定时器1运行控制位

            由软件清0关闭定时器1。当GATE=1,且INT1为高电平时,TR1置1启动定时器1;当GATE=0时,TR1置1启动定时器1。

            (3)TF0—定时器0溢出标志位 同TF1

            (4)TR0—定时器0运行控制位 同TR1

            (5)IE1—外部中断1请求标志

               1)当IT1=0时为电平触发方式,每个机器周期的S5P2采样INT1引脚,若INT1脚为低电平,则置1,否则IE1清0;

               2)当IT1=1时,INT1为跳变沿触发方式,当第一个机器周期采样到INT1为低电平时,则IE1置1。IE1=1,表示外部中断1正在向CPU申请中断。当CPU响应中断。当CPU响应中断,转向中断服务程序时,该位由硬件清0。

            (6)IT1—外部中断1触发方式选择位

                1)IT1=0:电平触发方式,引脚INT1上低电平有效

                2)IT1=1:跳变沿触发方式,引脚INT1的电平从高到低的负跳变有效

            (7)IE0—外部中断0请求标志 同IE0

            (8)IT0—外部中断0触发方式选择位 同IT1

            补充:SCON的中断标志

            (1)RI(SCON.0),串行口接收中断标志位。当允许串行口接收数据时,每接收完一个串行帧,由硬件置位RI。注意,RI必须由软件清除。

            (2)TI(SCON.1),串行口发送中断标志位。当CPU将一个发送数据写入串行口发送缓冲器时,就启动了发送过程。每发送完一个串行帧,由硬件置位TI。CPU响应中断时,不能自动清除TI,TI必须由软件清除。

            工作方式

            定时器0的工作方式——1:16位定时器:

            方式1的计数位数是16位,对于T0,TL0为低8位,TH0为高8位,组成了16位加1计数器,逻辑结果框图如下所示:

            分析逻辑图:GATE=0,TR=0时,TL0便在机器周期的作用下开始加1计数,当TL0装满后向TH0进位,知道把TH0也装满,此时计时器溢出,置TF0为1,向CPU申请中断,接下来CPU进行中断处理。在这种情况下,只要TR0为1,那么计数就不会停止。8位定时器、13位定时器工作方式大同小异。

            定时器初值:定时器一旦启动,它便在原来的数值上开始加1计数。若在程序开始时没有设置TR0和TR1,它们的默认值都是0。时钟频率为12MHz,12个周期为一个机器周期,那么此时机器周期为1μs,计满TH0和TL0需要216-1个数,共计65536μs,约为65.5ms。若需要定时50ms,则需要给TH0和TL0装一个初值,在初值基础上计50000个数后定时器溢出,刚好50ms中断一次——65536-50000=15536,15536/256=60(装入TH0)余176(装入TL0)

            总结定时器初值的计算方法:当定时器的方式1进行计数时,设机器周期为Tcy,定时器产生一次中断的时间为t,那么需要计数的个数为N=t/Tcy,装入THX和TLX的数分别为:THX=(65536-N)/256TLX=(65536-N)%256

        对于TX-1C,时钟频率11.0592MHz,机器周期约为1.08507μs,若t=50ms,则N=50000/1.08507≈46080,于是有THX=76,TL0=0

            教材中常用的方法是(50ms)THX=(65536-45872)/256; TLX=(65536-45872)%256;似乎在实际使用中更加准确。

    中断服务程序写法:

            写单片机程序时,在程序开始处需要对定时器及中断寄存器做初始化设置,通常定时器初始化过程如下:

            (1)对TMOD赋值,以确定T0和T1的工作方式;

            (2)计算初值,并将初值,并将初值写入TH0、TL0或TH1、TL1;

            (3)中断方式时,则对IE赋值,开放中断;

            (4)使TR0或TR1置位,启动定时器/计数器定时或计数

            注意:一般在中断服务程序中不要写过多的语句,防止该程序的代码执行时间超过中断循环时间,出现丢失累计情况。一般遵循能在主程序中完成的功能就不在中断函数中写的原则。

            补充:方式0——N=13(此为TH8位,TL5位)

            方式0为13位计数,由TL0的低5位(高3位未用)和TH0的8位组成。TL0的低5位溢出时向TH0进位,TH0溢出时,置位TCON中的TF0标志,向CPU发出中断请求。计数初值计算的公式为:

    $ X=2^{13}-N $

     

            示例 

            示例1: 使第一个发光二极管以1s(50ms X 20次)闪烁

    1. #include
    2. #define uchar unsigned char
    3. #define uint unsigned int
    4. sbit led1=P1^0;
    5. sbit beep=P2^3;
    6. uint num;
    7. void main()
    8. {
    9. TMOD=0x01;//设置定时器0的工作方式为1(M1M0=01)
    10. TH0=76; //装初值
    11. TL0=0; //装初值
    12. EA=1;//打开总中断
    13. ET0=1;//打开定时器0中断
    14. TR0=1;//启动定时器0
    15. num=0;
    16. led1=1;
    17. beep=1;
    18. while(1)//程序停止在这里等待中断发生
    19. {
    20. if(num==20) //加了20次说明1s时间到
    21. {
    22. num=0;//num清零之后可以重新计数
    23. led1=~led1;//让发光二极管状态取反!
    24. beep=~beep;
    25. }
    26. }
    27. }
    28. void T0_time() interrupt 1 //中断程序
    29. {
    30. TH0=76; //重装初值
    31. TL0=0; //重装初值
    32. num++;//num每加一次直至num==20
    33. }

            示例2: 数码管前两位59s循环计时

    1. #include
    2. #define uchar unsigned char
    3. #define uint unsigned int
    4. sbit dula=P2^6; //声明U1锁存器的锁存端
    5. sbit wela=P2^7; //声明U2锁存器的锁存端
    6. sbit led1=P1^0; //第一位发光二极管(0亮1灭)
    7. sbit beep=P2^3; //声明蜂鸣器引脚(0响1灭)
    8. uchar dula_num,wela_num;
    9. uchar num,num_timer0,num_timer1,tens,units;
    10. uchar code dula_table[]={
    11. 0x3f,0x06,0x5b,0x4f, // 0,1,2,3
    12. 0x66,0x6d,0x7d,0x07, //4,5,6,7
    13. 0x7f,0x6f,0x77,0x7c, //8,9,10,11
    14. 0x39,0x5e,0x79,0x71 //12,13,14,15
    15. };
    16. uchar code wela_table[]=
    17. {
    18. 0xdf,0xef,0xf7,0xfb,0xfd,0xfe //从右向左数第一到六位
    19. };
    20. void main()
    21. {
    22. void delayxms(uint xms);
    23. void display(uchar,uchar);
    24. dula=0; //初始化,段选置0
    25. wela=0; //初始化,片选置0
    26. led1=1; //初始化,二极管灯灭
    27. beep=1; //初始化,蜂鸣器不响
    28. TMOD=0x10;//设置定时器0和1的工作方式为1(0001 0001)
    29. TH1=(65536-45872)/256; //装初值
    30. TL1=(65536-45872)%256; //装初值
    31. EA=1;//打开总中断
    32. ET1=1;//打开定时器1中断
    33. TR1=1;//启动定时器1
    34. num=0;
    35. num_timer1=0;
    36. tens=0;
    37. units=0;
    38. while(1)
    39. {
    40. display(tens,units);
    41. }
    42. }
    43. void delayxms(uint xms)
    44. {
    45. uint x,y;
    46. for(x=xms;x>0;x--)
    47. for(y=124;y>0;y--);
    48. }
    49. void display(uchar tens,uchar units)//显示子函数
    50. {
    51. wela=1; //打开U2锁存端
    52. P0=wela_table[1]; //送入U2锁存端
    53. wela=0; //关闭U2锁存端
    54. P0=0xc0; //消影,防止P0残留电位信号干扰段选
    55. dula=1; //打开U1锁存端
    56. P0=dula_table[tens]; //送入段选信号
    57. dula=0; //关闭U1锁存端
    58. P0=0xff; //消影,防止P0残留电位信号干扰片选
    59. delayxms(1);
    60. wela=1; //打开U2锁存端
    61. P0=wela_table[0]; //送入U2锁存端
    62. wela=0; //关闭U2锁存端
    63. P0=0xc0; //消影,防止P0残留电位信号干扰段选
    64. dula=1; //打开U1锁存端
    65. P0=dula_table[units]; //送入段选信号
    66. dula=0; //关闭U1锁存端
    67. P0=0xff; //消影,防止P0残留电位信号干扰片选
    68. delayxms(1);
    69. }
    70. void T1_time() interrupt 3 //中断程序
    71. {
    72. TH1=(65536-45872)/256; //装初值
    73. TL1=(65536-45872)%256; //装初值
    74. num_timer1++;
    75. if(num_timer1%10==0)
    76. {
    77. led1=~led1;
    78. beep=~beep;
    79. }
    80. if(num_timer1==20)
    81. {
    82. num_timer1=0;
    83. num++;
    84. if(num==60) num=0;
    85. tens=num/10;
    86. units=num%10;
    87. }
    88. }

    参考资料: 

    [1] 郭天祥. 新概念51单片机C语言教程:入门、提高、开发、拓展全攻略[M]. 北京: 电子工业出版社, 2009. 

  • 相关阅读:
    extern、struct等关键字
    MySQL查询语法
    cookie加密8
    将windows文件夹挂载到linux机器
    Mysql 安全加固经验总结
    shell脚本之免交互
    面试被问了几百遍的 IoC 和 AOP ,还在傻傻搞不清楚?
    在校生学习生涯总结(2022)
    Windows Service Wrapper 一个将可执行文件封装为windows服务的工具
    Node.js vs. Spring Boot:Hello World 性能对决,谁更快一点?
  • 原文地址:https://blog.csdn.net/qq_46369644/article/details/136524819