当前位置: 首页 > news >正文

单片机原理及应用:定时器/计数器综合应用

本文是《单片机原理及应用》专栏中的最后一篇文章,笔者以编译器的安装配置——51单片机简介——LED和数码管外设——开关和按键控制功能切换——外部中断系统——定时器与计数器为知识大纲,介绍了C语言编程控制51单片机的入门教程。作为收尾,我们将一同学习三个定时器/计数器的实际应用,不同于之间灯光和按键的组合,在这三个应用中都加入了其他外设,更复杂也更有创意,希望大家对单片机的学习也能兴致盎然、不断进取。

定时器控制P1.0引脚产生1ms方波

首先选用系统时钟为12MHz的单片机, 要在P1.0上产生周期为2ms的方波,定时器应产生1ms的定时中断,定时时间到则在中断服务程序中对P1.0求反。在本案例我们使用定时器T0,方式1定时中断,GATE不起作用。此外对中断标志位采用查询法,查询法不经过中断程序,是最简单的I/O方式,数据在CPU和外设之间的传送完全靠计算机程序控制,外设和CPU之间是串行工作,CPU效率低。由于查询法效率低于中断函数,已经被逐渐淘汰,笔者此处仅作为知识补充。

#include <reg51.h>	  	
sbit P1_0=P1^0;			
void main(void)			
{TMOD=0x01;		 //设置T0为定时器工作方式1TR0=1;			 //允许T0中断while(1)		{TH0=0xfc;		 //赋初值,计数1000即溢出:X=65536-1000TL0=0x18;do{}while(!TF0); //使用查询法,TF0为0原地循环,为1则T0溢出,往下执行P1_0=!P1_0;		 //P1.0状态求反,高低电平转换TF0=0;			 //定时器T0溢出标志位TF0清零}
}

仿真如图所示,为了确认输出波形,在proteus中添加了虚拟示波器,添加方式是点击左侧菜单栏虚拟仪器模式,在弹出的窗口中选择OSCILLIOSCOPE即可。

另外请大家注意,左侧按键属于复位电路,对方波输出无影响。启动仿真后即可看到示波器图像,示波器右侧还有各类参数,大家可以自行调整。

 定时器控制发出1KHz音频

第二个应用是用定时器T1的中断控制P1.7引脚输出频率为1kHz方波音频信号,驱动蜂鸣器发声。单片机系统时钟为12MHz,音频信号周期1ms,因此T1的定时中断时间为0.5 ms,进入中断服务程序后,对P1.7求反。原理与应用一大致相同,区别在代码构架中采用了中断法,且在while循环中多次更改定时器初值。这几个应用或多或少都采用了与众不同的设计思路,更贴合实际使用场景。

#include<reg51.h>  			
sbit sound=P1^7;  			    //将蜂鸣器连接在P1.7引脚上
#define f1(a) (65536-a)/256		//宏定义输入计数值即按表达式输出初值
#define f2(a) (65536-a)%256    	
unsigned int i=500; 
unsigned int j=0; 
void main(void)
{EA=1;                  	//开启总中断允许.ET1=1;                	//允许定时器T1中断 TMOD=0x10; 		        //T设置定时器1工作方式1 TH1=f1(i);      		//初始化计数值i得到初值,此时i为500TL1=f2(i);      	TR1=1;                 	//启动定时器1while(1)  {   /*i=500; 				//可以改变频率达到不同的声音效果while(j<2000);j=0;i=1000; while(j<2000);j=0;i=3000; while(j<2000);j=0;*/					//返回第一种频率}}void T1_int(void) interrupt 3 using 0	//定时器T1中断服务函数
{TH1=f1(i);   		//重新赋初值.TL1=f2(i);   		sound=~sound; 	    //对P1.7输出求反,形成方波j++;				//用于记录中断次数,也就是记录时间
}

仿真如下,为了更直观地看到频率的变化,在蜂鸣器旁并联了一个示波器,虽然会降低幅值,但是不影响频率。

定时器控制蜂鸣器发出1KHz音频

 蜂鸣器声音较大,大家在观看视频演示时需要注意调节音量。

定时器测量INT1引脚正脉冲宽度

第三个应用是计数器测量INT1引脚正脉冲宽度,利用门控位GATE1可使T1启动计数受INT1控制:当GATE1=1,TR1=1时,只有INT1引脚输入高电平时,T1才被允许计数。利用该功能,可测量引脚正脉冲宽度,并在6位数码管上以机器周期数显示,能通过旋转信号源旋钮调整频率。

在中断处理上再次使用了查询法,另外需要注意INT1作为外部中断1的引脚,并未像之前我们介绍的那样设置TCON用于外部中断允许。作为最后一个应用,可能有些难以理解,大家了解相关原理内容就好。

#include<reg51.h>
#define uint unsigned int
#define uchar unsigned char
sbit P3_3=P3^3;			//读取INT1引脚电平
uchar count_high; 		//定义计数变量,用来读取TH0
uchar count_low;		//定义计数变量,用来读取TL0
uint num;
uchar shiwan, wan, qian, bai, shi, ge;	  //定义各数码管显示数位
uchar flag;				//控制刷新频率
uchar code table[]={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=110;y>0;y--);
}
void display(uint a,uint b,uint c,uint d,uint e,uint f){P2=0xfe; 	P0=table[f];	delay(2);		  //数码管段选,位选,消影P2=0xfd;	P0=table[e];	delay(2);		  //六个数位对应留个双管P2=0xfb;	P0=table[d];	delay(2);P2=0xf7;	P0=table[c];	delay(2);P2=0xef;	P0=table[b];	delay(2);P2=0xdf;	P0=table[a];	delay(2);
}
void read_count() 			//读取定时寄存器的内容
{do{count_high=TH1;		 //读取高字节count_low=TL1;		 //读取低字节}while (count_high!=TH1);//读取完成后继续往下执行	num=count_high*256+count_low; //将16位数据恢复为十进制数字}
void main( )
{while(1){flag=0;TMOD=0x90;		//设置定时器T1工作方式1,门控位为1,当INT1为高电平时定时器才开始工作TH1=0;			//计数初值为零TL1=0;while(P3_3==1); 	//等待INT1变为低电平TR1=1;		        //如果INT1为低电平,启动定时器器T1,此时未真正开始计数while(P3_3==0); 	//等待INT1变为高电平,变高后T1真正开始计数while(P3_3==1); 	//等待INT1变为低电平,变低后T1停止计数TR1=0;read_count(); 		//调用函数,读取定时寄存器内容的函数shiwan=num/100000;	//数据分割,每一位数码管显示对应数位wan=num%100000/10000;qian=num%10000/1000;bai=num%1000/100;shi=num%100/10;ge=num%10;while(flag!=100)	//运行100次显示数值后再读取下一次数值 			{flag++;display(ge,shi,bai,qian,wan,shiwan); //调用display函数,传入相应参数}}
}

仿真如下,为避免接线混乱,采用了之前介绍的总线接法;P0端口使用了上拉电阻,此时端口为准双向口,不存在高阻抗的悬浮状态;和示波器一样,信号发生器也在虚拟仪器界面,选择SIGNAL GENERATOR即可。

 

大家可以更改信号发生器的参数,包括频率、电压和波形,观察数码管示数的变化,详细的操作教程可以参考51单片机数字频率计开发

定时器测量INT1引脚正脉冲宽度


到此为止,这个专栏就算圆满落幕了,这部分单片机课程笔者也是学习了一月有余,对于非电子专业的同学来说这些知识足够了。以后如果有进一步的机会,或许会重启这个专栏。总之感谢陪伴我一同学习的每一个人,祝愿大家在新的一年里都能百尺竿头更进一步,谢谢大家,我们下个专栏再见。

相关文章:

  • BioTech - 蛋白质结构、核酸结构、小分子构象的预测
  • npm run dev,vite 配置 ip 访问
  • 怎么把身份证压缩到200k以下?一分钟教你如图片压缩
  • 人工智能技术的应用
  • 重生奇迹mu敏弓加点攻略
  • PHP 正则表达式(PCRE)
  • 新一代通信协议 - Socket.D
  • javascript编程求三个数中最大的数
  • Pandas实战100例 | 案例 43: 数据排序
  • EtherCAT主站SOEM -- 16 --Qt-Soem通过界面按键控制电机转圈圈PV模式
  • 在开发时如何决定使⽤哪种数据类型?
  • 基于网络爬虫的租房数据分析系统
  • 简单谈一谈pytorch中混合精度训练(torch.cuda.amp)的功效及命令行参数解析器的使用
  • Spring整理-Spring Bean的作用域
  • 【Mybatis系列】Mybatis空值关联
  • 实现windows 窗体的自己画,网上摘抄的,学习了
  • android高仿小视频、应用锁、3种存储库、QQ小红点动画、仿支付宝图表等源码...
  • Codepen 每日精选(2018-3-25)
  • ES6 学习笔记(一)let,const和解构赋值
  • IIS 10 PHP CGI 设置 PHP_INI_SCAN_DIR
  • JavaScript对象详解
  • miaov-React 最佳入门
  • Python学习之路16-使用API
  • quasar-framework cnodejs社区
  • SegmentFault 2015 Top Rank
  • 排序算法学习笔记
  • 前端性能优化——回流与重绘
  • 如何选择开源的机器学习框架?
  • 使用 Xcode 的 Target 区分开发和生产环境
  • 我建了一个叫Hello World的项目
  • 详解移动APP与web APP的区别
  • 扩展资源服务器解决oauth2 性能瓶颈
  • 蚂蚁金服CTO程立:真正的技术革命才刚刚开始
  • ​flutter 代码混淆
  • ​VRRP 虚拟路由冗余协议(华为)
  • #图像处理
  • $NOIp2018$劝退记
  • (01)ORB-SLAM2源码无死角解析-(56) 闭环线程→计算Sim3:理论推导(1)求解s,t
  • (07)Hive——窗口函数详解
  • (a /b)*c的值
  • (done) ROC曲线 和 AUC值 分别是什么?
  • (阿里云万网)-域名注册购买实名流程
  • (初研) Sentence-embedding fine-tune notebook
  • (带教程)商业版SEO关键词按天计费系统:关键词排名优化、代理服务、手机自适应及搭建教程
  • (翻译)terry crowley: 写给程序员
  • (仿QQ聊天消息列表加载)wp7 listbox 列表项逐一加载的一种实现方式,以及加入渐显动画...
  • (分布式缓存)Redis分片集群
  • (附源码)计算机毕业设计SSM基于健身房管理系统
  • (剑指Offer)面试题41:和为s的连续正数序列
  • (九十四)函数和二维数组
  • (十二)springboot实战——SSE服务推送事件案例实现
  • (转) SpringBoot:使用spring-boot-devtools进行热部署以及不生效的问题解决
  • (转)四层和七层负载均衡的区别
  • .form文件_SSM框架文件上传篇
  • .net websocket 获取http登录的用户_如何解密浏览器的登录密码?获取浏览器内用户信息?...