单片机定时器驱动蜂鸣器、LED数码管应用实例及串口通信教程
目录
文章目录
一、利用T1控制发出1kHz的音频信号
二、 LED数码管秒表的制作
1.LED数码管
2.使用定时器控制LED数码管
三、LCD时钟的设计
1.LCD1602介绍
2.使用定时器设计LCD时钟
四、串口通信原理及实例
1.通信方式
(1)按照数据传送方式可分为串行通信和并行通信;
(2)按照通信的数据同步方式,可分为异步通信和同步通信;
(3)按照数据的传输方向又可分为单工、半双工和全双工通信;
2.串行控制寄存器SCON
3.电源控制寄存器PCON
4.单片机与PC通信
5.双单片机通信
总结
一、利用T1控制发出1kHz的音频信号
定时器/计数器T0由特殊功能寄存器TH0、TL0构成,T1由特殊功能寄存器TH1、TL1构成。
图1 定时器/计数器结构图
要求:定时器/计数器T0由特殊功能寄存器TH0、TL0构成,T1由特殊功能寄存器TH1、TL1构成。
系统时钟为12MHz。方波音频信号周期1ms,因此T1的定时中断时间为0.5 ms,进入中断服务程序后,对P1.7求反。
先计算T1初值,系统时钟为12MHz,则机器周期为1µs。1kHz音频信号周期为1ms,要定时计数的脉冲数为a。则T1初值:
TH1=(65 536 −a) /256;
TL1=(65 536 −a) %256;
具体代码如下:
#include<reg51.h> //包含头文件
sbit sound=P1^7; //将sound位定义为P1.7脚
#define f1(a) (65536-a)/256 //定义装入定时器高8位时间常数
#define f2(a) (65536-a)%256 //定义装入定时器低8位时间常数
unsigned int i=500;
unsigned int j=0;
void main(void)
{
EA=1; //开总中断.
ET1=1; //允许定时器T1中断 .
TMOD=0x10; //TMOD=0001 000B,使用T1的方式1定时 TH1=f1(i); //给T1高8位赋初值.
TL1=f2(i); //给T1低8位赋初值.
TR1=1; //启动T1
while(1)
{ //循环等待
i=460;
while(j<2000);
j=0;
i=360;
while(j<2000);
j=0;
}
}
void T1(void) interrupt 3 using 0 //定时器T1中断函数
{
TR1= 0; //关闭T1
sound=~sound; //P1.7输出求反
TH1=f1(i); //T1的高8位重新赋初值.
TL1=f2(i); //T1的低8位重新赋初值.
j++;
TR1=1; //启动定时器T1
}
仿真效果图如下:
二、 LED数码管秒表的制作
1.LED数码管
LED数码管的显示原理是利用LED的发光特性,通过控制LED的亮灭来显示不同的数字或字符。它由多个LED灯组成,通常为七个LED灯,排列成数字“8”的形状。每个LED灯代表一个数字段,分别为a、b、c、d、e、f、g段,其中a、b、c、d、e、f、g段分别对应数字“0”到“9”中的不同段。
LED数码管的控制方式有两种,一种是共阳极控制,一种是共阴极控制。共阳极控制是指将所有LED灯的阳极连接在一起,通过控制各个阴极的接通情况来控制LED数码管的亮灭;共阴极控制则是将所有LED灯的阴极连接在一起,通过控制各个阳极的接通情况来控制LED数码管的亮灭。
2.使用定时器控制LED数码管
要求:用2位数码管显示计时时间,最小计时单位为“百毫秒”,计时范围0.1~9.9s。当第1次按一下计时功能键时,秒表开始计时并显示;第2次按一下计时功能键时,停止计时,将计时的时间值送到数码管显示;如果计时到9.9s,将重新开始从0计时;第3次按一下计时功能键,秒表清0。再次按一下计时功能键,则重复上述计时过程。
代码如下:
#include<reg51.h> //头文件
unsigned char code discode1[]= {0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,0x87,0xff,0xef};
//数码管显示0~9的段码表, 带小数点
unsigned char code discode2[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
//数码管显示0~9的段码表,不带小数点
unsigned char timer=0; //timer记录中断次数
unsigned char second; //second储存秒
unsigned char key=0; //key记录按键次数
main() //主函数
{
TMOD=0x01; //定时器T0方式1定时
ET0=1; //允许定时器T0中断
EA=1; //总中断允许
second=0; //设初始值
P0=discode1[second/10]; //显示秒位0
P2=discode2[second%10]; //显示0.1s位0
while(1) //循环
{
if((P3&0x80)==0x00) //当按键被按下时
{
key++; //按键次数加1
switch(key) //根据按键次数分三种情况
{
case 1: //第一次按下为启动秒表计时
TH0=0xee; //向TH0写入初值的高8位
TL0=0x00; //向TL0写入初值的低8位,定时5ms
TR0=1; //启动定时器T0
break;
case 2: //按下两次暂定秒表
TR0=0; //关闭定时器T0
break;
case 3: //按下3次秒表清0
key=0; //按键次数清
second=0; //秒表清0
P0=discode1[second/10]; //显示秒位0 P2=discode2[second%10]; //显示0.1s位0
break;
}
while((P3&0x80)==0x00); //如果按键时间过长在此循环
}
}
}
void int_T0() interrupt 1 using 0 //定时器T0中断函数
{
TR0=0; //停止计时,执行以下操作(会带来计时误差)
TH0=0xee; //向TH0写入初值的高8位
TL0=0x00; //向TL0写入初值的低8位,定时5ms
timer++; //记录中断次数
if (timer==20) //中断20次,共计时20*5ms=100ms=0.1s
{
timer=0; //中断次数清0
second++; //加0.1s
P0=discode1[second/10]; //根据计时,即时显示秒位 P2=discode2[second%10]; //根据计时,即时显示0.1s位 }
if(second==99) //当计时到9.9s时
{
TR0=0; //停止计时
second=0; //秒数清0
key=2; //按键数置2,当再次按下按键时, //key++,即key=3,秒表清0复原
}
else //计时不到9.9s时
{
TR0=1; //启动定时器继续计时
}
}
三、LCD时钟的设计
1.LCD1602介绍
LCD ( Liquid Crystal Display 的简称)液晶显示器。能够同时显示16×2,32个字符,是一种专门用来显示字母、数字、符号等的点阵型液晶模块。
LCD1602液晶显示器是广泛使用的一种字符型液晶显示模块。它是由字符型液晶显示屏(LCD)、控制驱动主电路HD44780及其扩展驱动电路HD44100,以及少量电阻、电容元件和结构件等装配在PCB板上而组成。该显示屏的优点是耗电量低、体积小、辐射低。
LCD1602主要用来显示数字、字母、图形以及少量自定义字符。可以显示2行16个字符,拥有16个引脚,其中8位数据总线D0-D7,和RS、R/W、EN三个控制端口,工作电压为5V,并且带有字符对比度调节V0和背光源AK。
2.使用定时器设计LCD时钟
将T0定时时间定为50ms,采用中断方式进行溢出次数累计,满20次,则秒计数变量second加1;若秒计满60,则分计数变量minute加1,同时将秒计数变量second清0;若分钟计满60,则小时计数变量hour加1;若小时计数变量满24,则将小时计数变量hour清0。
#include<reg51.h>
#include<lcd1602.h>
#define uchar unsigned char
#define uint unsigned int
uchar int_time; //定义中断次数计数变量
uchar second; //秒计数变量
uchar minute; //分钟计数变量
uchar hour; //小时计数变量
uchar code date[]=" H.I.T. CHINA "; //LCD第1行显示的内容
uchar code time[]=" TIME 23:59:55 "; //LCD第2行显示的内容
uchar second=55,minute=59,hour=23;
void clock_init()
{
uchar i,j;
for(i=0;i<16;i++)
{
write_data(date[i]);
}
write_com(0x80+0x40);
TMOD=0x01; //设置定时器T0为方式1定时
EA=1; // 总中断开
ET0=1; // 允许T0中断
TH0=(65536-46483)/256; //给T0装初值
TL0=(65536-46483)%256;
TR0=1;
int_time=0; //中断次数、秒、分、时单元清0
second=55;
minute=59;
hour=23;
while(1)
{
clock_write(second ,minute, hour);
}
}
void T0_interserve(void) interrupt 1 using 1 //T0中断服务子程序
{
int_time++; //中断次数加1
if(int_time==20) //若中断次数计满20次
{
int_time=0; //中断次数变量清0
second++; //秒计数变量加 1
}
if(second==60) //若计满60s
{
second=0; //秒计数变量清0
minute ++; //分计数变量加 1
}
if(minute==60) //若计满60分
{
minute=0; //分计数变量清0
hour ++; //小时计数变量加1
}
if(hour==24)
{
hour=0; //小时计数计满24,将小时计数变量清0
}
TH0=(65536-46083)/256; //定时器T0重新赋值
TL0=(65536-46083)%256;
}
四、串口通信原理及实例
1.通信方式
(1)按照数据传送方式可分为串行通信和并行通信;
并行通信:数据的各位同时进行发送或接收的通信方式。优点是速率高。缺点是需要的传输线多,成本高,只适合近距离的数据通信。
串行通信:一位一位的按顺序的进行发送或接收的通信方式。优点是需要的传输线少,成本低。缺点是传输的速率慢,适合远距离的数据通信。
(2)按照通信的数据同步方式,可分为异步通信和同步通信;
同步通信:发送端在发送串行数据的同时,提供一个时钟信号,并按照一定的约定(例如:在时钟信号的上升沿的时候,将数据发送出去)发送数据,接收端根据发送端提供的时钟信号,以及大家的约定,接收数据。如:I2C、SPI等有时钟信号的协议,都属于这种通信方式。
异步通信: 接收方并不知道数据什么时候会到达,收发双方可以有各自自己的时钟。发送方发送的时间间隔可以不均,接收方是在数据的起始位和停止位的帮助下实现信息同步的。
(3)按照数据的传输方向又可分为单工、半双工和全双工通信;
单工通信:在单工通信中,通信的信道是单向的,发送端与接收端也是固定的,即发送端只能发送信息,不能接收;接收端只能接收信息,不能发送。基于这种情况,数据信号从一端传送到另外一端,信号流是单方向的。
半双工通信:半双工通信可以实现双向的通信,但不能在两个方向上同时进行,必须轮流交替地进行。这种工作方式下,发送端可以转变为接收端;相应地,接收端也可以转变为发送端。但是在同一个时刻,信息只能在一个方向上传输。因此,也可以将半双工通信理解为一种切换方向的单工通信。
全双工通信:全双工通信允许数据同时在两个方向上传输,又称为双向同时通信,即通信的双方可以同时发送和接收数据。
2.串行控制寄存器SCON
SCON寄存器是用于选择串行通信的工作方式,和某些控制功能。其格式如下:
如上图所示,常用位只有SM0,SM1,REN,TI和RI。
SM0和SM1,用确定串口UART的工作模式,通过软件置位SM0和SM1,确定出了四种组合,对应着四种UART工作模式,在不同模式下,串口的功能和波特率都不相同。
REN,允许/禁止穿行接收控制位。由软件置位REN,REN=1为允许串行接收状态,可启动串行接收器RXD,开始接收信息;软件复位REN,即REN=0,则禁止接收;
TI: 发送中断请求标志位。在方式0,当串行发送数据第8位结束时,由内部硬件自动置位,即TI=1,向主机请求中断,响应中断后必须用软件复位,即TI=0。在其他方式中,则在停止位开始发送时由内部硬件置位,必须用软件复位;
RI:接收中断请求标志位。在方式0,当串行接收到第8位结束时由内部硬件自动置位RI=1,向主机请求中断,响应中断后必须用软件复位,即RI=0。在其他方式中,串行接收到停止位的中间时刻由内部硬件置位,即RI=1,必须由软件复位,即RI=0。
3.电源控制寄存器PCON
字节地址为87H,不能位寻址。仅最高位SMOD与串口有关
SMOD位:波特率选择位。
当用软件置位SMOD,即SMOD=1,则使波特率加倍;
SMOD=0,则各工作方式的波特率不加倍不变。复位时默认SMOD=0。
4.单片机与PC通信
要求:将单片机串口与笔记本电脑串口模块相连,单片机每隔2秒发送“Hello C51”,笔记本电脑用串口助手软件接收。 如果串口助手发送字符“0" 给单片机,则单片机停止发送; 如果单片机收到“1”,则继续每隔2秒发送“Hello C51”。
代码如下:
#include <REGX52.H>
#include "stdio.h"
unsigned char ch;
unsigned char Flag=1;
void Delay(unsigned int xms) //@11.0592MHz
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
void UartInit(void) //9600bps@11.0592MHz
{
PCON &= 0x7F; //波特率不倍速
SCON = 0x50; //8位数据,可变波特率
TMOD &= 0x0F; //清除定时器1模式位
TMOD |= 0x20; //设定定时器1为8位自动重装方式
TL1 = 0xFD; //设定定时初值
TH1 = 0xFD; //设定定时器重装值
ET1 = 0; //禁止定时器1中断
TR1 = 1; //启动定时器1
EA=1;
ES=1;
}
void UartSend()
{
TI=1;
puts("Hello C51");
while(!TI);
TI=0;
Delay(2000);
}
void main()
{
UartInit();
while(1)
{
if(Flag==1)UartSend();
}
}
//串口中断函数模板
void UART_Routine() interrupt 4 //串口中断
{
if(RI==1)
{
RI=0;
ch=SBUF;
if(ch=='1')Flag=1;
if(ch=='0')Flag=0;
}
}
实现效果如下:
5.双单片机通信
要求:如图8-30,甲、乙两单片机进行方式3(方式2)串行通信。甲机把控制8个流水灯点亮的数据发送给乙机并点亮其P1口的8个LED。方式3比方式1多了1个可编程位TB8,该位一般作奇偶校验位。乙机接收到的8位二进制数据有可能出错,需进行奇偶校验,其方法是将乙机的RB8和PSW的奇偶校验位P进行比较,如果相同,接收数据;否则拒绝接收。
代码如下:
//甲机发送
#include <REGX52.H>
sbit T_P=PSW^0;
unsigned char code Tab[8]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};//流水灯程序
void Send(unsigned char dat)
{
TB8=T_P;
SBUF=dat;
while(TI==0);
TI=0;
}
void Delay(unsigned char xms) //@11.0592MHz
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
void main()
{
unsigned char i;
TMOD=0x20;
SCON=0xc0;
PCON&=0x7f;
TH1=0xfd;
TL1=0xfd;
TR1=1;
while(1)
{
for(i=0;i<8;i++)
{
Send(Tab[i]);
Delay(200);//约200ms发送数据
}
}
}
//乙机接收
#include <REGX52.H>
sbit R_P=PSW^0;
unsigned char Receive()//接收一字节数据
{
unsigned char dat;
while(RI==0);//检测RI,RI=0,未接收完
RI=0; //接收数据完成RI手动清0
ACC=SBUF; //将接收缓冲器的数据存于ACC
if(RB8=R_P) //只有偶检验成功才能往下执行,接收数据
{
dat=ACC; //将ACC数据存于dat
return dat; //将接收的数据返回
}
}
void main()
{
TMOD=0x20; //设置定时器为方式2,8位自动重载
SCON=0xd0; //串口为方式3,允许接收REN=1
PCON&=0x7f; //波特率不加倍
TH1=0xfd; //波特率9600
TL1=0xfd;
TR1=1;
//REN=1;
while(1)
{
P2=Receive(); //将接收的数据送至P2口显示
}
}
仿真结果如下:
总结
经过此次实验对51单片机的串口通信以及定时器的应用了解的更加深入,增加了动手实践的经验,是一次很好的锻炼。
参考链接:
51单片机–串口通信(详细教程)_51单片机串口通信-CSDN博客
作者:2201_75518371