51单片机中断系统编程

一.外部中断

1.编程思想

  • 中断准备:中断初始化函数打开中断开关 ,选择中断传输方式
  • 中断处理:为了便于观察,让我们知道单片机进入中断处理函数,在这里我们选择打开流水灯
  • 电路搭建:由于P3^3引脚不便直接接地,我们把P3^3P3^7连接起来,用P3^7来控制P3^3的电平变化

2.实践代码

#include <reg52.h>
#define uint unsigned int
#define uchar unsigned char
sbit key_s2=P3^0;
sbit flag=P3^7;void delay(uint z)
{uint x,y;for(x = z; x > 0; x--)for(y = 114; y > 0 ; y--); 		
} 
//外部中断1初始化
void clInit()
{EA = 1;	//开总中断EX1 = 1;//开外部中断1IT1 = 1;//外部中断1下降沿触发	
}void main()//main函数自身会循环
{clInit();//外部中断1初始化	while(1){if(key_s2 == 0)//判断S2是否被按下{delay(20);//按键消抖if(key_s2 == 0){flag = 1;flag = 0;//产生下降沿while(!key_s2);//松手检测}	}}	
}//外部中断1中断服务程序
void cml() interrupt 2
{P1 = ~P1;
}  

注意,为了确保时钟信号有两个时钟周期,按键时请自然一些。

二.定时器

1.了解

51单片机有2个16位定时器/计数器:定时器0 (T0为P3.4)和定时器1 (T1为P3.5)

这里所说定时/计数器是因为它有两种功能,既能定时又能计数。

当工作在定时模式时,每经过一个机器周期,内部的16位计数寄存器的值就会加1。当这个寄存器装满时溢出,我们可以算出工作在定时模式时最高单次定时时间为65535*1.085us=时间 (单位us)

当工作在计数器模式时,T0(P3.4引脚)T1 (P3.5引脚) 每来一个脉冲计数寄存器加1。

定时器作用:定时计数器可以用于精确事件定时,PWM脉宽调制,波形发生,信号时序测量的方面。

2.使用步骤

定时/计数器使用步骤:

  • 1.启动定时/计数器(通过TCON控制器)
  • 2.设置定时/计数器工作模式(通过TMOD控制器)
  • 3.查询定时。计数器是否一处(读TCONTF位)

3.了解TMOD控制器

​ 定时和计数功能由特殊功能寄存器TMOD的控制位C/T进行选择,TMOD寄存器的各位信息如下表所列。可以看出,2个定时/计数器有4种操作模式,通过TMODM1M0选择。2个定时/计数器的模式0、1和2都相同,模式3不同,各模式下的功能如下所述。

在这里插入图片描述

该图选自官方STC89C52芯片手册,其中要注意的三个点我已经标出:

  • 不可位寻址:我们在编写程序时不能再像之前一样令某位直接为0或1。
  • 复位值:也就是默认值,如果我们什么也不改的情况下其值全部为0
  • M1M0:选择0、1这种模式。

三.定时器编程

#include <reg52.h>
#define uchar unsigned char
#define uint unsigned intsbit DU = P2^6;  //数码管段选
sbit WE = P2^7;  //数码管位选//共阴数码管段选表0-9
uchar  code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};//毫秒级延时函数
void delay(uint z)
{uint x,y;for(x = z; x > 0; x--)for(y = 114; y > 0 ; y--); 		
} void display(uchar i)
{uchar bai, shi, ge;bai = i / 100; shi = i % 100 / 10;	ge  = i % 10;//第一位数码管  		P0 = 0XFF;//清除断码WE = 1;//打开位选锁存器P0 = 0XFE; //1111 1110WE = 0;//锁存位选数据DU = 1;//打开段选锁存器P0 = tabel[bai];//DU = 0;//锁存段选数据delay(5);//第二位数码管P0 = 0XFF;//清除断码WE = 1;//打开位选锁存器P0 = 0XFD; //1111 1101WE = 0;//锁存位选数据DU = 1;//打开段选锁存器P0 = tabel[shi];//DU = 0;//锁存段选数据delay(5);//第三位数码管P0 = 0XFF;//清除断码WE = 1;//打开位选锁存器P0 = 0XFB; //1111 1011WE = 0;//锁存位选数据DU = 1;//打开段选锁存器P0 = tabel[ge];//DU = 0;//锁存段选数据delay(5);
}//定时器初始化程序
void timeclInit()
{TR0 = 1;//启动定时器0TMOD = 0x01; //定时器工作模式选择//假设定时50ms,初值通过计算而来TH0=0x4b;TL0=0xfd;
}void main()
{uchar msec,sec;timeclInit();while(1){if(TF0 == 1)   //判断是否溢出,溢出代表50ms完毕{TF0 = 0;   //软件清零TH0 = 0x4b;TL0 = 0xfd; //定时50msmsec++;if(msec==20)  //20个50ms促进秒加1{msec=0;sec++;}}display(sec);if(sec>10)sec=0;     //秒清零}
}

四.计数器

1.编程思路

  • 结合上面的定时器,把一个定时/计数器作为定时器使用,一个作为计数器使用
  • 计数器显示标示:使用LED1作为每加1的标志,需要把单片机的P1.0P3.4进行连接

2.实践代码

#include <reg52.h>
#define uchar unsigned char
#define uint unsigned intsbit DU = P2^6;  //数码管段选
sbit WE = P2^7;  //数码管位选
sbit LED1 = P1^0;//共阴数码管段选表0-9
uchar  code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};//毫秒级延时函数
void delay(uint z)
{uint x,y;for(x = z; x > 0; x--)for(y = 114; y > 0 ; y--); 		
} //数码管显示函数
void display(uchar i)
{uchar bai, shi, ge;bai = i / 100; shi = i % 100 / 10;	ge  = i % 10;//第一位数码管  		P0 = 0XFF;//清除断码WE = 1;//打开位选锁存器P0 = 0XFE; //1111 1110WE = 0;//锁存位选数据DU = 1;//打开段选锁存器P0 = tabel[bai];//DU = 0;//锁存段选数据delay(5);//第二位数码管P0 = 0XFF;//清除断码WE = 1;//打开位选锁存器P0 = 0XFD; //1111 1101WE = 0;//锁存位选数据DU = 1;//打开段选锁存器P0 = tabel[shi];//DU = 0;//锁存段选数据delay(5);//第三位数码管P0 = 0XFF;//清除断码WE = 1;//打开位选锁存器P0 = 0XFB; //1111 1011WE = 0;//锁存位选数据DU = 1;//打开段选锁存器P0 = tabel[ge];//DU = 0;//锁存段选数据delay(5);
}//计数器T0初始化函数
void countclInit()
{TR0 = 1;TMOD |= 0x05;TH0= 0;TL0= 0;
}//定时器T1初始化程序
void timeclInit()
{TR1 = 1;//启动定时器0TMOD |= 0x10; //定时器工作模式选择//假设定时50ms,初值通过计算而来TH1=(65535 - 46082)/256;TL1=(65535 - 46082)%256;
}void main()
{uchar msec,sec;countclInit();timeclInit();while(1){if(TF1 == 1)   //判断是否溢出,溢出代表50ms完毕{TF1 = 0;   //软件清零TH1=(65535 - 46082)/256;TL1=(65535 - 46082)%256;msec++;if(msec==5)  //20个50ms促进秒加1{msec=0;LED1 = ~LED1;}}display(TL0);}
}

五.定时/计数器中断

1.理论补充

之前我们使用定时器和计数器使用的都是查询的方式(通过都TF0TF1的状态),而现在我们要使用的是中断的方式,通过之前的外部中断,我们同样需要打开中断开关进行初始化。

2.基础使用

#include <reg52.h>
#define uchar unsigned char
#define uint unsigned intsbit DU = P2^6;  //数码管段选
sbit WE = P2^7;  //数码管位选
uchar msec,sec;//共阴数码管段选表0-9
uchar  code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};//毫秒级延时函数
void delay(uint z)
{uint x,y;for(x = z; x > 0; x--)for(y = 114; y > 0 ; y--); 		
} void display(uchar i)
{uchar bai, shi, ge;bai = i / 100; shi = i % 100 / 10;	ge  = i % 10;//第一位数码管  		P0 = 0XFF;//清除断码WE = 1;//打开位选锁存器P0 = 0XFE; //1111 1110WE = 0;//锁存位选数据DU = 1;//打开段选锁存器P0 = tabel[bai];//DU = 0;//锁存段选数据delay(5);//第二位数码管P0 = 0XFF;//清除断码WE = 1;//打开位选锁存器P0 = 0XFD; //1111 1101WE = 0;//锁存位选数据DU = 1;//打开段选锁存器P0 = tabel[shi];//DU = 0;//锁存段选数据delay(5);//第三位数码管P0 = 0XFF;//清除断码WE = 1;//打开位选锁存器P0 = 0XFB; //1111 1011WE = 0;//锁存位选数据DU = 1;//打开段选锁存器P0 = tabel[ge];//DU = 0;//锁存段选数据delay(5);
}//定时器初始化程序
void timer0Init()
{EA = 1;//打开总中断ET0 = 1;//打开对应中断开关TR0 = 1;//启动定时器0TMOD = 0x01; //定时器工作模式选择//假设定时50ms,初值通过计算而来TH0=0x4b;TL0=0xfd;
}//中断函数
void timer0() interrupt 1
{TH0=0x4b;TL0=0xfd;msec=msec+1;if(msec==20)  //20个50ms促进秒加1{msec=0;sec++;}
}void main()
{timer0Init();while(1){display(sec);}
}

该部分同样实现的是秒表的功能,只不过使用的是中断方式的硬件清零。

3.与数码管相结合

之前的独立键盘与数码管结合,已经在独立键盘做过介绍,但是在那里有一个问题,就是每次按下独立按键时,当且只有松开时数码管才会加1,而如果你松开慢点,就会发现按下时显示的只有一位数字,这是为什么呢?因为独立键盘的延时缘故,只有在松手检测完成后才会加1,而我们想要的效果是只要你按下无论是否松开,都会加1。所以这里要用到定时器与独立键盘和数码管相结合。

#include <reg52.h>
#define uchar unsigned char
#define uint unsigned intsbit DU = P2^6;  //数码管段选
sbit WE = P2^7;  //数码管位选
sbit key_s2 = P3^0; //定义独立键盘S2的IO口
sbit key_s3 = P3^1; //定义独立键盘S2的IO口
uchar num = 0;//共阴数码管段选表0-9
//数码管段选表
uchar code tabel[]={ 0x3F,  //"0"0x06,  //"1"0x5B,  //"2"0x4F,  //"3"0x66,  //"4"0x6D,  //"5"0x7D,  //"6"0x07,  //"7"0x7F,  //"8"0x6F,  //"9"0x77,  //"A"0x7C,  //"B"0x39,  //"C"0x5E,  //"D"0x79,  //"E"0x71,  //"F"0x76,  //"H"0x38,  //"L"0x37,  //"n"0x3E,  //"u"0x73,  //"P"0x5C,  //"o"0x40,  //"-"0x00,  //熄灭0x00  //自定义};
uchar code SMGwei[] = {0xfe, 0xfd, 0xfb};//毫秒级延时函数
void delay(uint z)
{uint x,y;for(x = z; x > 0; x--)for(y = 114; y > 0 ; y--); 		
} void display(uchar i)
{static uchar wei; 		P0 = 0XFF;//清除断码WE = 1;//打开位选锁存器P0 = SMGwei[wei];WE = 0;//锁存位选数据switch(wei){case 0: DU = 1; P0 = tabel[i / 100]; DU = 0; break;case 1: DU = 1; P0 = tabel[i % 100 / 10]; DU = 0; break;	case 2: DU = 1; P0 = tabel[i % 10]; DU = 0; break;		}wei++;if(wei == 3)wei = 0;
}//定时器初始化程序
void timer0Init()
{EA = 1;//打开总中断ET0 = 1;//打开对应中断开关TR0 = 1;//启动定时器0TMOD = 0x01; //定时器工作模式选择//假设定时5ms,初值通过计算而来TH0=0xED;TL0=0xED;
}//定时器0中断函数
void timer0() interrupt 1
{TH0 = 0xED;TL0 = 0xFF; //定时5msdisplay(num); //数码管显示函数	
} void main()//main函数自身会循环
{	timer0Init();//定时器0初始化while(1){if(key_s2 == 0)//判断S2是否被按下{delay(20);//按键消抖if(key_s2 == 0){if(num != 120)num++;while(!key_s2);//松手检测}	}if(key_s3 == 0)//判断S3是否被按下{delay(20);//按键消抖if(key_s3 == 0){if(num > 0)num--;while(!key_s3);//松手检测}	}}	
} 

注意这里的display()函数,由于原来的display()函数采用的是一位一位的表示,中间用5ms延时来消除其余辉效果,但是如果使用定时器后,每个5ms来显示num的值,其值直接给3个对应的数码管赋值,而不需要延时函数,所以我们显示一个值相当于调用display函数三次,而即使原来的display()函数去掉5ms延时任然显示一个数才调用一次,这就是二者的区别。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/415749.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

用LED数码显示器循环显示数字0~9

#include<reg51.h> // 包含51单片机寄存器定义的头文件 /************************************************** 函数功能&#xff1a;延时函数&#xff0c;延时一段时间 ***************************************************/ void delay(void) { unsigned …

C语言总结十二:文件操作详细总结

在操作系统中&#xff0c;为了统一对各种硬件的操作&#xff0c;简化接口&#xff0c;不同的硬件设备也都被看成一个文件。对这些文件的操作&#xff0c;等同于对磁盘上普通文件的操作。我们不去探讨硬件设备是如何被映射成文件的&#xff0c;把任意 I/O 设备&#xff0c;转换成…

Doris配置外表以及多个Hive外表的配置

1.场景分析 以Clickhouse、Doris、Starrocks等为代表的mpp分析数据库正在快速的兴起&#xff0c;以其高效查询、跨库整合能力收到广大技术人员的喜爱。本文主要浅显介绍下作者在使用Doris时&#xff0c;通过建立catlog进行跨库查询。 废话不多少&#xff0c;直接上代码 2.相关…

使用Python+pygame实现贪吃蛇小游戏

使用Pythonpygame贪吃蛇小游戏 使用第三方库pygame&#xff0c;关于Python中pygame游戏模块的安装使用可见 https://blog.csdn.net/cnds123/article/details/119514520 给出两种实现。 第一种 运行效果如下&#xff1a; 游戏源码如下&#xff1a; import pygame import sy…

HCIA-HarmonyOS设备开发认证-序

序 最近涉及到HarmonyOS鸿蒙系统设备开发&#xff0c;在网络上已经有很多相关资料&#xff0c;视频教程&#xff0c;我也移植了公司的一个stm32G474板卡&#xff0c;运行LiteOS-m L0系统。 一面看资料一面移植&#xff0c;遇到不少坑&#xff0c;当看到运行的LOGO时&#xff0…

openpose之使用摄像头检测并输出到json文件

编程如画&#xff0c;我是panda&#xff01; 前言 之前给大家分享了如何搭建openpose环境&#xff0c;并进行了测试案例&#xff0c;但是如果要使用摄像头的话&#xff0c;还需要修改一下运行文件&#xff0c;并且这次会教大家如何输出到json文件 。 如果环境还没有搭建好&am…

三层架构——工业控制领域简单理解

前言闲话 工业领域对好滴软件架构的需求不高&#xff0c;但不意味着可以用纯面向过程式编程解决问题&#xff0c;这样后期维护必将大乱。 曾经和一位从业30年的老电气工程师交流工业控制编程&#xff1a; 我问&#xff1a;为啥富士康这些大厂以前的机器都不联网&#xff1f;&…

C. Partitioning the Array

C. Partitioning the Array 题目 解题思路 对于两个数 x 、 y x、y x、y,如果 x x x mod m m m ≡ y y y mod m m m&#xff0c;则有 ( y − x y - x y−x) ≡ 0 (mod m m m)&#xff0c;则 m m m 是 (y - x) 的因数&#xff0c;所有因数的最大公约数非 1 则是一种方案…

Win10 打开文件突然鼠标变成一个蓝色大圈卡住点不了也打不开文件,重启电脑也是这样

环境: Win10 专业版 加密客户端环境 问题描述: Win10 打开桌面word文件突然鼠标变成一个蓝色大圈卡住点不了也打不开文件,重启电脑也是这样,只有蓝色圈变大没有鼠标指针出现圈卡着不会动,和那些有鼠标箭头加小蓝色圈不一样 解决方案: 某网上查看的,还是要自己排查…

spring boot mybatis plus mapper如何自动注册到spring bean容器

##Import(AutoConfiguredMapperScannerRegistrar.class) ##注册MapperScannerConfigurer ##MapperScannerConfigurer.postProcessBeanDefinitionRegistry方法扫描注册mapper ##找到mapper候选者 ##过滤mapper 类 候选者 ##BeanDefinitionHolder注册到spring 容器

(2023版)斯坦福CS231n学习笔记:DL与CV教程 (56) | 卷积神经网络

前言 &#x1f4da; 笔记专栏&#xff1a;斯坦福CS231N&#xff1a;面向视觉识别的卷积神经网络&#xff08;23&#xff09;&#x1f517; 课程链接&#xff1a;https://www.bilibili.com/video/BV1xV411R7i5&#x1f4bb; CS231n: 深度学习计算机视觉&#xff08;2017&#xf…

带你学C语言-指针(4)

目录 ​编辑 ⚾0.前言 &#x1f3c0;1.回调函数 ⚽2.qsort &#x1f3c9;2.1 qsort函数的模拟实现 &#x1f3be;3.sizeof与strlen对比 &#x1f3be;4.结束语 ⚾0.前言 言C之言&#xff0c;聊C之识&#xff0c;以C会友&#xff0c;共向远方。各位CSDN的各位你们好啊&…