51单片机中断系统编程

news2024/9/27 19:19:11

一.外部中断

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;//开外部中断1
	IT1 = 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 int
	
sbit 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 1110
	WE = 0;//锁存位选数据
	
	DU = 1;//打开段选锁存器
	P0 = tabel[bai];//
	DU = 0;//锁存段选数据
	delay(5);

	//第二位数码管
	P0 = 0XFF;//清除断码
	WE = 1;//打开位选锁存器
	P0 = 0XFD; //1111 1101
	WE = 0;//锁存位选数据
	
	DU = 1;//打开段选锁存器
	P0 = tabel[shi];//
	DU = 0;//锁存段选数据
	delay(5);

	//第三位数码管
	P0 = 0XFF;//清除断码
	WE = 1;//打开位选锁存器
	P0 = 0XFB; //1111 1011
	WE = 0;//锁存位选数据
	
	DU = 1;//打开段选锁存器
	P0 = tabel[ge];//
	DU = 0;//锁存段选数据
	delay(5);
}

//定时器初始化程序
void timeclInit()
{
	TR0 = 1;//启动定时器0
	TMOD = 0x01; //定时器工作模式选择
	
	//假设定时50ms,初值通过计算而来
	TH0=0x4b;
	TL0=0xfd;
}

void main()
{
	uchar msec,sec;
	timeclInit();
	while(1)
	{
		if(TF0 == 1)   //判断是否溢出,溢出代表50ms完毕
		{
			TF0 = 0;   //软件清零
			TH0 = 0x4b;
			TL0 = 0xfd; //定时50ms
			msec++;
			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 int
	
sbit 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 1110
	WE = 0;//锁存位选数据
	
	DU = 1;//打开段选锁存器
	P0 = tabel[bai];//
	DU = 0;//锁存段选数据
	delay(5);

	//第二位数码管
	P0 = 0XFF;//清除断码
	WE = 1;//打开位选锁存器
	P0 = 0XFD; //1111 1101
	WE = 0;//锁存位选数据
	
	DU = 1;//打开段选锁存器
	P0 = tabel[shi];//
	DU = 0;//锁存段选数据
	delay(5);

	//第三位数码管
	P0 = 0XFF;//清除断码
	WE = 1;//打开位选锁存器
	P0 = 0XFB; //1111 1011
	WE = 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;//启动定时器0
	TMOD |= 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 int
	
sbit 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 1110
	WE = 0;//锁存位选数据
	
	DU = 1;//打开段选锁存器
	P0 = tabel[bai];//
	DU = 0;//锁存段选数据
	delay(5);

	//第二位数码管
	P0 = 0XFF;//清除断码
	WE = 1;//打开位选锁存器
	P0 = 0XFD; //1111 1101
	WE = 0;//锁存位选数据
	
	DU = 1;//打开段选锁存器
	P0 = tabel[shi];//
	DU = 0;//锁存段选数据
	delay(5);

	//第三位数码管
	P0 = 0XFF;//清除断码
	WE = 1;//打开位选锁存器
	P0 = 0XFB; //1111 1011
	WE = 0;//锁存位选数据
	
	DU = 1;//打开段选锁存器
	P0 = tabel[ge];//
	DU = 0;//锁存段选数据
	delay(5);
}

//定时器初始化程序
void timer0Init()
{
	EA = 1;//打开总中断
	ET0 = 1;//打开对应中断开关
	TR0 = 1;//启动定时器0
	TMOD = 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 int
	
sbit 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;//启动定时器0
	TMOD = 0x01; //定时器工作模式选择
	
	//假设定时5ms,初值通过计算而来
	TH0=0xED;
	TL0=0xED;
}

//定时器0中断函数
void timer0() interrupt 1
{
	TH0 = 0xED;
	TL0 = 0xFF; //定时5ms
	display(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.coloradmin.cn/o/1396769.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C# ObjectArx 绘制表格并设置单元格合并

第一行默认是标题&#xff0c;可设置行【RowType】进行设置类型 Document doc Application.DocumentManager.MdiActiveDocument;using (Transaction tr doc.TransactionManager.StartOpenCloseTransaction()){BlockTable bt tr.GetObject(doc.Database.BlockTableId, OpenMo…

vue列表飞入效果

效果 实现代码 <template><div><button click"add">添加</button><TransitionGroup name"list" tag"ul"><div class"list-item" v-for"item in items" :key"item.id">{{ i…

conda 安装, 配置以及使用

文章目录 1. 安装2. 配置2.1 如何配置2.2 快速设置取消自动进入 base 环境conda 添加清华源pip 添加清华源pip 更新为最新版本 3. 使用 conda 是 python 的环境管理工具包&#xff0c;非常好用&#xff0c;特别是 miniconda 相对于 conda 不需要安装其他的工具&#xff0c;而且…

从临床和科研场景分析ChatGPT在医疗健康领域的应用可行性

2023年4月发表在Journal Medical Systems的文献《Evaluating the Feasibility of ChatGPT in Healthcare: An Analysis of Multiple Clinical and Research Scenarios》&#xff08;评估 ChatGPT 在医疗健康领域的可行性&#xff1a;对多种临床和研究场景的分析&#xff09;介绍…

Rust-泄漏

在C中&#xff0c;如果引用计数智能指针出现了循环引用&#xff0c;就会导致内存泄漏。而Rust中也一样存在引用计数智能指针Rc,那么Rust中是否可能制造出内存泄漏呢? 内存泄漏 首先&#xff0c;我们设计一个Node类型&#xff0c;它里面包含一个指针&#xff0c;可以指向其他…

Python的OpenCV模块实现图像转换素描图简单示例

一、示例代码&#xff1a; import cv2img cv2.imread(/home/lijiang/图片/ubuntu_wallpaper/Einstein_2.jpg) # 读取图像grey cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 灰度 invert cv2.bitwise_not(grey)blur_img cv2.GaussianBlur(invert…

Postgresql数据库死锁

死锁报错1 UPDATE deadlock_example SET value value 1 WHERE id 1ERROR: deadlock detected DETAIL: Process 95 waits for ShareLock on transaction 3553457; blocked by process 187. Process 187 waits for ShareLock on transaction 3553458; blocked by process 95.…

串联RLC电路中的增益

在串联RLC电路中&#xff0c;增益又可以叫放大倍数&#xff0c;用M或者G表示 电压增益就是输出电压比输入电压&#xff0c;MVo/Vin 在串联RLC中&#xff0c;VoIr*Rac,VinIr*(sLr1/sCrRac) MRac/(sLr1/sCrRac) 当输入频率fs时&#xff0c;fr为谐振频率 输入频率0<fs<…

贯通用友T+与企企,引领企业数字化转型新篇章!

客户介绍&#xff1a; 某科技有限公司是一家专注于高端芯片研发和制造的高科技企业。自成立以来&#xff0c;该企业始终坚持以科技创新为核心&#xff0c;致力于为全球客户提供高性能、高品质的芯片解决方案。公司拥有一支由业内资深专家和优秀工程师组成的研发团队&#xff0…

带你学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的各位你们好啊&…

selenium处理下拉框

当想要爬取的数据由下拉框来选择时&#xff0c;应该如何处理&#xff1f; 页面如下&#xff1a; 目的获得电影的详细信息&#xff0c;包括票房&#xff0c;上映日期等。 代码如下&#xff1a; from selenium import webdriver from selenium.webdriver.support.select impor…

28个炫酷的CSS特效动画示例(含源代码)

CSS是网页的三驾马车之一&#xff0c;是对页面布局的总管家&#xff0c;2024年了&#xff0c;这里列出28个超级炫酷的CSS动画示例&#xff0c;让您的网站更加炫目多彩。 文章目录 1. 涌动的弹簧效果2. 超逼真的3D篮球弹跳&#xff0c;含挤压弹起模态3. 鼠标放div上&#xff0c;…

linux磁盘,分区,挂载等等

1. 修改磁盘分区的标签 例如&#xff1a;733be18b-7baf-d84c-879d-ca3db465f179太长了&#xff0c;修改一下。 linuxchenxiao:/media/linux/733be18b-7baf-d84c-879d-ca3db465f179$ 先 sudo blkid sudo blkid 找到你想修改的UUID(唯一标识符) /dev/sda1: UUID"733be…

VBA_MF系列技术资料1-315

MF系列VBA技术资料 为了让广大学员在VBA编程中有切实可行的思路及有效的提高自己的编程技巧&#xff0c;我参考大量的资料&#xff0c;并结合自己的经验总结了这份MF系列VBA技术综合资料&#xff0c;而且开放源码&#xff08;MF04除外&#xff09;&#xff0c;其中MF01-04属于…

【LeetCode热题100】【子串】滑动窗口最大值

题目 给你一个整数数组 nums&#xff0c;有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回 滑动窗口中的最大值 。 示例 1&#xff1a; 输入&#xff1a;nums [1,3,-1,-3,5,3,6,7], …

华尔街日报:中国加密货币交易“非法却盛行”,VPN翻墙、微信找币商、线下面交……

《华尔街日报》戏谑地称&#xff0c;中国的投资者曾经是加密货币交易的主导力量&#xff0c;人民币是用于交易比特币最受欢迎的法定货币。而现在&#xff0c;中国的币圈投资者正努力规避政府对加密货币交易的严格规定。 事实上&#xff0c;在过去几年里&#xff0c;中国大陆与加…

基于springboot+vue的旅游网站系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目背景…

各模块的实现

注册模块&#xff1a; 注册使用手机号发送验证码注册的方式&#xff0c;使用的是阿里云的短信发送服务&#xff0c;然后进行认证&#xff0c;有个60s的时间&#xff0c;可以存到redis中&#xff0c;key是手机号&#xff0c;value是验证码。 使用Spring自带的BCryptPasswordEn…

什么是OSPF?为什么需要OSPF?OSPF基础概念

什么是OSPF&#xff1f; 开放式最短路径优先OSPF&#xff08;Open Shortest Path First&#xff09;是IETF组织开发的一个基于链路状态的内部网关协议&#xff08;Interior Gateway Protocol&#xff09;。 目前针对IPv4协议使用的是OSPF Version 2&#xff08;RFC2328&#x…

【图像分类】【深度学习】【轻量级网络】【Pytorch版本】EfficientNet_V1模型算法详解

【图像分类】【深度学习】【轻量级网络】【Pytorch版本】EfficientNet_V1模型算法详解 文章目录 【图像分类】【深度学习】【轻量级网络】【Pytorch版本】EfficientNet_V1模型算法详解前言EfficientNet_V1讲解问题辨析(Problem Formulation)缩放尺寸(Scaling Dimensions)复合缩…