专栏首页緣來來來51单片机的中断及其使用方法

51单片机的中断及其使用方法

什么是中断?

比如说我正在厨房用煤气烧一壶水,这样就只能守在厨房里,苦苦等着水开——如果水溢出来浇灭了煤气,有可能就要发生一场灾难了。

门外忽然又传来了铿锵有力的歌声,我最喜欢的天龙八部要开演了,听着水壶发出“咕嘟咕嘟”的声音,我清楚:除非等到水开,否则没有我享受人生的时候。

这个场景跟中断有什么关系呢?

在这个场景中,我是唯一具有处理能力的主体,不管是烧水、还是看电视,同一个时间点上我只能干一件事情。但是,在我专心致志干一件事情时,总有许多或紧迫或不紧迫的事情突然出现在面前,都需要去关注,有些还需要我停下手头的工作马上去处理。只有在处理完之后,方能回头完成先前的任务。

中断机制不仅赋予了我处理意外情况的能力,如果我能充分发挥这个机制的妙用,就可以“同时”完成多个任务了。

事实上烧水需要10分钟完成,但是提下水壶和关煤气我只要几秒钟就可以完成。为了这几秒,我需要在厨房等候10分钟。如果使用闹钟定时10分钟,10分钟一到,闹钟就会提醒我该去关煤气了,那么我就可以去安心看电视了。

实际上就是用了闹钟这样一个中断信号来提示我完成提水壶和关煤气的任务。

中断优先级的说明

  • 当设置为默认中断固有优先级时: 当几个中断同时发生时,则先处理中断优先级高的中断程序,在处理任意中断期间发生中断,都不会响应。
  • 当配置了中断优先级,即抢占优先级 同时发生中断,优先级高的先响应,在处理任意中断时,发生同级别或低级的中断,则不响应,发生优先级更高的中断时,则先处理高优先级中断,处理完毕,再回来处理当前中断。
  • 当设置为默认固有中断优先级时: 假设“水开”默认优先级高于“门铃”优先级,当先听到“水开”则先处理“水开”这件事,当先听到“门铃”则先处理“门铃”这件事。只有当两件事同时发生时,则会先去处理“水开”,再处理“门铃”。
  • 当人为配置了抢占中断优先级 配置“水开”为高优先级,则当“水开”事件发生时,直接处理“水开”,在处理“水开”的过程中,即使“门铃”响了,也不会去理会。当“门铃”响了,正在走向门口时,这个时候,“水开”事件又发生了,那么从门口转向,先去处理“水开”,处理好了后,再回头到门口,处理“门铃”事件。

在51单片机中使用中断

51单片机中断源

51单片机共有6个中断源,分别如下:

INT0——外部中断0,由P3_2端口引入,低电平或者下降沿引起;中断级别最高;C语言使用序号为0;

T0——定时器/计数器0,由T0计数器计数回零引起;中断级别第二;C语言使用序号为1;

INT1——外部中断1,由P3_3端口引入,低电平或者下降沿引起;中断级别第三;C语言使用序号为2;

T1——定时器/计数器1,由T1计数器计数回零引起;中断级别第四;C语言使用序号为3;

TI/RI——串行口中断,由串行口完成一帧字符发送/接受后引起;中断级别第五;C语言使用序号为4;

T2——定时器/计数器2,由T2计数器计数回零引起;中断级别第六;C语言使用序号为5;

51单片机中断的例子

下面先简单写一个不用中断实行的数码管秒表程序,定时器的使用可以参考我的这边文章: (51单片机定时器使用)[https://www.fkomm.cn/article/2018/9/19/45.html]

# include <8052.h>

# define ADDR0 P1_0
# define ADDR1 P1_1
# define ADDR2 P1_2
# define ADDR3 P1_3
# define ENLED P1_4

unsigned char __code LedChar[] = { //数码管显示字符转换表
    0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
    0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
};
unsigned char LedBuff[6] = {  //数码管显示缓冲区,初值0xFF确保启动时都不亮
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};

void main() {

 unsigned char i = 0;    //动态扫描的索引
    unsigned int  cnt = 0;  //记录T1中断次数
    unsigned long sec = 0;  //记录经过的秒数

  ENLED = 0;
  ADDR3 = 1;

  TMOD = 0x01;        //设置T1为模式1
  TH1 = 0xCF;         //为T1赋初值0xCF67,为1ms
 TL1 = 0x67;
 TR1 = 1;          //启动T1

  while(1) {        
    if (TF1 == 1) {     //判断T1是否溢出

      TF1 = 0;      //T1溢出后,清零中断标志,并重新赋值
      TH1 = 0xCF;
     TL1 = 0x67;
     TR1 = 1;

      cnt ++;       //计数值自加1

      if (cnt >= 1000) {  //判断T1溢出是否达到1000次
       cnt = 0;    //达到1000次后计数值清零
       sec ++;     //秒计数自加1

        /*以下代码将sec按十进制位从低到高依次提取并转为数码管显示字符*/
       LedBuff[0] = LedChar[sec%10];
       LedBuff[1] = LedChar[sec/10%10];
        LedBuff[2] = LedChar[sec/100%10];
       LedBuff[3] = LedChar[sec/1000%10];
                LedBuff[4] = LedChar[sec/10000%10];
                LedBuff[5] = LedChar[sec/100000%10];
     }
   }

    P0 = 0xFF;        //数码管消隐
   /*以下代码完成数码管动态扫描刷新*/
   switch(i) {
     case 0: ADDR2=0; ADDR1=0; ADDR0=0; i++; P0=LedBuff[0]; break;
            case 1: ADDR2=0; ADDR1=0; ADDR0=1; i++; P0=LedBuff[1]; break;
            case 2: ADDR2=0; ADDR1=1; ADDR0=0; i++; P0=LedBuff[2]; break;
            case 3: ADDR2=0; ADDR1=1; ADDR0=1; i++; P0=LedBuff[3]; break;
            case 4: ADDR2=1; ADDR1=0; ADDR0=0; i++; P0=LedBuff[4]; break;
            case 5: ADDR2=1; ADDR1=0; ADDR0=1; i=0; P0=LedBuff[5]; break;
            default: break;
   }
 }
}

下面我们将使用中断来实现

# include <8052.h>


# define ADDR0 P1_0
# define ADDR1 P1_1
# define ADDR2 P1_2
# define ADDR3 P1_3
# define ENLED P1_4

unsigned char __code LedChar[] = {  //数码管显示字符转换表
    0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
    0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
};
unsigned char LedBuff[6] = {  //数码管显示缓冲区,初值0xFF确保启动时都不亮
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};

unsigned char i = 0;    //动态扫描的索引
unsigned char flag1s = 0;//1秒定时标志
unsigned int  cnt = 0;  //记录T1中断次数

void main() {
    unsigned long sec = 0;//记录经过的秒数

  EA = 1;           //使能总中断
 ENLED = 0;        //使能U3
  ADDR3 = 1;        //因为需要动态改变ADDR0-2的值,所以不需要再初始化了
  TMOD = 0x10;      //设置T1为模式1
  TH1  = 0xFC;      //为T1赋初值0xFC67,定时1ms
  TL1  = 0x67;
  ET1  = 1;         //使能T1中断
  TR1  = 1;         //启动T1

  while(1) {
    if (flag1s == 1) { //判断1秒定时标志

     flag1s = 0;   //1秒定时标志清零
      sec ++;       //秒计数自减1

      /*以下代码将sec按十进制位从低到高依次提取并转为数码管显示字符*/
     LedBuff[0] = LedChar[sec%10];
     LedBuff[1] = LedChar[sec/10%10];
      LedBuff[2] = LedChar[sec/100%10];
     LedBuff[3] = LedChar[sec/1000%10];
            LedBuff[4] = LedChar[sec/10000%10];
            LedBuff[5] = LedChar[sec/100000%10];
   }
 }
}

/* 定时器1中断服务函数 */
void InterruptTime1() __interrupt 3 {

 TH1 = 0xCF;       //重新加载初值
  TL1 = 0x67;

 cnt++;          //中断次数计数值加1

 if (cnt >= 1000) {    //中断1000次即1秒
        cnt = 0;          //清零计数值以重新开始下1秒计时
        flag1s = 1;      //设置1秒定时标志为1
    }

 /*以下代码完成数码管动态扫描刷新*/
    P0 = 0xFF;   //显示消隐

  switch(i) {
   case 0: ADDR2=0; ADDR1=0; ADDR0=0; i++; P0=LedBuff[0]; break;
        case 1: ADDR2=0; ADDR1=0; ADDR0=1; i++; P0=LedBuff[1]; break;
        case 2: ADDR2=0; ADDR1=1; ADDR0=0; i++; P0=LedBuff[2]; break;
        case 3: ADDR2=0; ADDR1=1; ADDR0=1; i++; P0=LedBuff[3]; break;
        case 4: ADDR2=1; ADDR1=0; ADDR0=0; i++; P0=LedBuff[4]; break;
        case 5: ADDR2=1; ADDR1=0; ADDR0=1; i=0; P0=LedBuff[5]; break;
        default: break;
 }
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 基础知识 | 每日一面(7)

    小林:它可以用作一种格式上的提示表明函数的定义可能在另一个源文件中, 但在extern int f();和int f();之间并没有实质的区别。

    闫小林
  • C语言生成固定范围的随机数

    本文为仙士可原创文章,转载无需和我联系,但请注明来自仙士可博客www.php20.cn

    仙士可
  • 95个可见字符生成的6位密码词典有多大?

    -rw-r--r-- 1 root root4.7TApr 14 07:41 /data3/ccc.txt

    我爱你的一诺
  • linux 科普

    为了进一步强化大型主机的功能,让主机的资源可以提供更多的使用者来利用,所以在1964年, 由AT&A公司的贝尔实验室(Bell)、麻省理工学院(MIT)及奇异公...

    suwanbin
  • 栈的基本实现(更新中)

    参考着严蔚敏的《数据结构(C语言版)》,用自己拿渣的可怜的C语言做了一下午的实现。。。也没能写出来几个。。。就很菜(气哭)。。。

    李志伟
  • 现代化 C++ 开发工具 CLion 从入门到精通

    ? 作者:allsochen ,腾讯 PCG 后台开发工程师 这就是 CLion 几年前写过一篇 eclipse C++ 的文章、现在还提 eclipse 的...

    腾讯技术工程官方号
  • 全国计算机二级C语言 考试大纲(2018年版)

    4. 在Visual C++集成环境下,能够编写简单的C程序,并具有基本的纠错和调试程序的能力。

    用户6755376
  • php的各种 I/O流 以及用法

    或许有人看到这个会懵逼,这是什么东东?这有啥用?这咋用?我是谁?我在哪?我要去往何处?

    仙士可
  • 简述 C语言 有和 C++ 的基本区别,你真的懂吗?(新手面试必学)

    c++:有命名空间:using namespace std(可以防止函数出现相同的情况)

    诸葛青云
  • 【算法随记六】一段Matlab版本的Total Variation(TV)去噪算法的C语言翻译。

      最近看到一篇文章讲IMAGE DECOMPOSITION,里面提到了将图像分为Texture layer和Structure layer,测试了很多方法,对...

    用户1138785

扫码关注云+社区

领取腾讯云代金券