单片机定时器与串口通信

本节资料

链接:https://pan.baidu.com/s/1Pt1WPQLeBlm7mY6KYvI2Pg
提取码:t5l3

在Proteus和普中51单片机上完成下列程序:

一、实验一

1.实验要求

课件 【例7-4】利用中断发出1Khz的方波信号,驱动蜂鸣器鸣叫。
【例7-4】利用T1的中断控制P1.7引脚输出频率为1kHz方波音频信号,驱动蜂鸣器发声。系统时钟为12MHz。方波音频信号周期1ms,因此T1的定时中断时间为0.5 ms,进入中断服务程序后,对P1.7求反。

 先计算T1初值,系统时钟为12MHz,则机器周期为1µs。1kHz音频信号周期为1ms,要定时计数的脉冲数为a。则T1初值:
   
      TH1=(65536 −a) /256;	
      TL1=(65536 −a) %256。

2.Proteus原理图

在这里插入图片描述

3.代码部分

#include<reg51.h>
sbit sound=P2^5;   
#define f1(a) (65536-a)/256
#define f2(a) (65536-a)%256
unsigned int i=500;
unsigned int j=0;
void t1 (void) interrupt 3 using 0
{
	TR1=0;
	sound=~sound;
	TH1=f1(i);
	TL1=f2(i);
	j++;
	TR1=1;
}	

void main(void)
{
	EA=1;
	ET1=1;
	TMOD=0X10;
	TH1=f1(i);
	TL1=f2(i);
	TR1=1;
	while(1)
	{
    i=460;
		while(j<2000);
		j=0;
		i=360;
		while(j<2000);
		j=0;
	}	
}

4.实验现象

Week7

蜂鸣器鸣叫。

二、实验二

1.实验要求

课件【例7-5】LED数码管秒表的制作。用2位数码管显示计时时间,最小计时单位为“百毫秒”,计时范围0.1~9.9s。当第1次按一下计时功能键时,秒表开始计时并显示;第2次按一下计时功能键时,停止计时,将计时的时间值送到数码管显示;如果计时到9.9s,将重新开始从0计时;第3次按一下计时功能键,秒表清0。再次按一下计时功能键,则重复上述计时过程。

2.Proteus原理图

Week7_test2_01

3.代码部分

#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;			//向THO写入初值的高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;							//向THO写入初值的高8位
	TL0=0x00;							//向TLO写入初值的低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;			//启动定时器继续计时
	}
}

4.实验现象

Week7_test2_02

三、实验三

1.实验要求

【例7-7】使用定时器实现一个LCD显示时钟。采用LCD 1602,具体见第5章的绍。LCD时钟的原理电路见图7-22。最小计时单位是秒,如何获得1s的定时?可将T0定时时间定为50ms,采用中断方式进行澄出次数累计,满20次,则秒计数变量second加1;若秒计满60,则分计数变量minute加1,同时将秒计数变量second清0;若分钟计满60,则小时计数变量hour加1;若小时计数变量满24,则将小时计数变量hour清0。

2.Proteus原理图

在这里插入图片描述

3.代码部分

先将定时器以及各计数变量设定完毕,然后调用时间显示子程疗序。秒计时由TO中断服务子程序来实现。
参考程序如下:
#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);
for(j=0;j<16;j++)
	{
		write_data(time]);
	}
}

void clock_write( uint s, uint m, uint h)
{
	 write_sfm(0x47,h);
	 write_sfm(0x4a,m);
	 write_sfm(0x4d,s);
}
void main()
{
	init1602();		//LCD初始化
	clock_init();	//时钟初始化
	TMOD=0x01;		//设置定时器TO为方式1定时
	EA=1;			//总中断开
	ET0=1;			//允许TO中断
	TH0=(65536-46483)/256;	//给TO装初值
	TLO=(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
lITO中断服务子程序
{
	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)
I若计满60{
	minute=0;		//分计数变量清0
	hour ++;		//小时计数变量加1
}
if(hour==24)
{
	hour=0;			//小时计数计满24,将小时计数变量清0
}
	TH0=(65536-46083)/256;		//定时器T0重新赋值
	TL0=(65536-46083)%256;
}
执行上述程序仿真运行,就会在LCD上显示实时时间。


4.实验现象

在这里插入图片描述

四、串口通信原理

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。

五、实验四

1.实验要求

【例8-6】(甲乙两个单片机串口通信)在实物实验时,如果不能找到两个普中单片机,用笔记本电脑的串口助手程序代替其中一个单片机,实现课件上描述的主要功能。
如图8-30,甲、乙两单片机进行方式3(或方式2)串行通信。甲机把控制8个流水灯点亮的数据发送给乙机并点亮其P1的8个LED。方式3比方式1多了1个可编程位TB8,该位一般作奇偶校验位。乙机接收到的8位二进制数据有可能出错,需进行奇偶校验,其方法是将乙机的RB8和PSW的奇偶校验位P进行比较,如果相同,接收数据。否则拒绝接收。
本例使用了一个虚拟终端来观察甲机串口发出的数据。

2.Proteus原理图

在这里插入图片描述

3.代码部分

//甲机发送程序
#include <reg51.h>
sbit p=PSW^0;		//P位为PSW寄存器的第0位,即奇偶校验位
unsigned char Tab[8]= {0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};	//控制流水灯显示数据数组,为全局变量

void Send(unsigned char dat)		//发送1字节数据的函数
{
	TB8=p;		//将偶校验位作为第9位数据发送
	SBUF=dat;
	while(TI==0);	//检测TI,TI=0,未发送完
	;				//空操作
	TI=0;			// 1字节发送完,TI清0
}

void delay (void)		//延时约200ms的函数
{
	unsigned char m,n;
	for(m=0;m<250;m++)
	for(n=0;n<250;n++) ;
}

void main(void)		//主函数
{
	unsigned char i ;
	TMOD=0x20;		//设置定时器T1为方式2
	SCON=0xc0;		//设置串口为方式3
	PCON=0x00;		//SMOD=0
	TH1=0xfd;		//给T1赋初值,波特率设置为9600	
	TL1=0xfd;
	TR1=1;			//启动定时器T1
	while(1)
	{
		for(i=0;i<8;i++)
		{
			Send (Tab[i]);
			delay() ;	//大约200ms发送一次数据
		}
	}
}

//乙机接收程序
#include <reg51.h>
sbit p=PSW^0;	//P位为PSW寄存器的第0位,即奇偶校验位

unsigned char Receive(void)		//接收1字节数据的函数
{
	unsigned char dat;
	while(RI==0) ;		//检测RI,RI=0,未接收完,则循环等待;
	;
	RI=0;				//己接收一帧数据,将RI清0
	ACC=SBUF;			//将接收缓冲器的数据存于ACC
	if(RB8==p)			//只有偶校验成功才能往下执行,接收数据
	{
		dat=ACC;			//将接收缓冲器的数据存于dat
		return dat;			//将接收的数据返回
	}
}

void main(void)	//主函数
{
	TMOD=0x20;		//设置定时器T1为方式2
	SCON=0xd0;		//设置串口为方式3,允许接收REN=1
	PCON=0x00;		// SMOD=0
	TH1=0xfd;		//给定时器T1赋初值,波特率为9600
	TL1=0xfd;
	TR1=1;			//接通定时器T1
	REN=1;			//允许接收

	while(1)
	{
		P1= Receive( );		//将接收到的数据送P1口显示
	}
}


六、实验五

1.实验要求

将单片机串口与笔记本电脑串口模块相连。单片机每隔2秒发送“Hello C51”,笔记本电脑用串口助手软件接收。 如果串口助手发送字符“0" 给单片机,则单片机停止发送; 如果单片机收到“1”,则继续每隔2秒发送“Hello C51”。

2.代码部分

#include<reg51.h>
#define uint unsigned int
#define uchar unsigned char
uint p=0;//默认设置状态0
void delay_2s()//延时函数
{
	int i,j; 
	for(i=0;i<21800;i++)
     {
        for(j=10;j>0;j--);
       }
}
void uart_sendbyte(uchar byte)//发送字符
{
	SBUF=byte;
	while(TI==0);
	TI=0;
}
void helloc51()
{
			uart_sendbyte('h');
			uart_sendbyte('e');
			uart_sendbyte('l');
			uart_sendbyte('l');
			uart_sendbyte('o');
			uart_sendbyte('c');
			uart_sendbyte('5');
			uart_sendbyte('1');
			uart_sendbyte(' ');
}
void uart_init(uchar baud)//uart初始化
{
	TMOD|=0X20;
	SCON=0X50; 
	PCON=0X80; 
	TH1=baud; 
	TL1=baud;
	ES=1;
	EA=1;
	TR1=1;
}
void uart() interrupt 4
{
	if(RI==1)//读取串口发送的信息
{
	uchar rec_data;
	rec_data=SBUF;
	RI=0;
	if(rec_data==0)//发送0,设置为模式0
	{
		p=0;
	}
	else if(rec_data==1)//发送1,设置为模式1
	{
		p=1;
	}
	
}
}
void main()
{
	uart_init(0XFA);
	while(1)
{
	if(p==0)//状态0,不做操作
	{
	while(p==0)
	{
		
	}
	}
	else if(p==1)//状态1,循环输出hello c51并且延时2s
	{
		while(p==1)
		{
			helloc51();
			delay_2s();
		}
	}
}
}

3.实验现象

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

七、心得体会

学到串口,对于单片机得学习更深入了一步,感觉有点难了,得自己课外多加学习、多在板子上练习才行。

八、参考链接

1.https://blog.csdn.net/2201_75518371/article/details/137928488?spm=1001.2014.3001.5502
2.https://blog.csdn.net/m0_74327085/article/details/138001316?spm=1001.2014.3001.5502#1_5

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值