单片机定时器与串口通信的学习与应用

目录

一、蜂鸣器

(一)蜂鸣器介绍

(二)蜂鸣器驱动

1、有源蜂鸣器驱动:

2、无源蜂鸣器驱动:

(三)实际应用

1、题目要求:

2、Proteus电路原理图:

3、代码实现:

4、Proteus仿真:

5、开发板实现:

二、LED数码管秒表

(一)数码管原理介绍:

1、简单介绍:

2、LED数码管的分类:

3、按内部发光二极管单元的连接方式分类:

4、数码管段码表:

(二)实际应用:

1、题目要求:

2、Proteus电路原理图:

3、代码实现:

4、Proteus仿真:

三、LCD1602显示时钟

(一)LCD1602模块简介

(二)LCD1602驱动

1、写数据/指令时序

2、指令集

3、LCD初始化

4、其余常用LCD1602代码:

(三)实际应用:

1、题目要求:

2、Proteus电路原理图:

3、代码实现:

4、Proteus仿真:

5、开发板实现:

四、串口通信

(一)串口简介

1、串口通信的功能

2、单片机串口结构

(二)串行通信口的控制寄存器

1、串行口控制寄存器SCON

2、电源控制寄存器PCON

3、配置中断

​(三)实际应用

1、题目一:

(1)题目要求:

(2)代码实现:

(3)串口助手效果:

2、题目二:

(1)题目要求:

(2)Proteus电路原理图:

(3)代码实现:

(4)Proteus仿真:


一、蜂鸣器

(一)蜂鸣器介绍

蜂鸣器是一种将电信号转换为声音信号的器件,常用来产生设备的按键音、报警音等提示信号,按照驱动方式可以分为如下两种:

1、有源蜂鸣器:内部自带振荡源,将正负极接上直流电压即可持续发声,频率固定。

包括如下种类蜂鸣器:

(1)声音模块蜂鸣器:

  • 工作原理:集成了声音芯片和振膜的组件,通过控制芯片内部的声音文件或音调来产生声音。
  • 特点:可以通过简单的控制方式实现多种声音效果,适合于需要播放特定声音的应用,如警报、音乐等。

(2)电磁式蜂鸣器:

  • 工作原理:利用电磁感应原理产生声音,通过交变电流在线圈产生磁场,使得振膜振动产生声音。
  • 特点:声音相对较大,适合于需要较高音量的应用,但相对于压电蜂鸣器,体积稍大、功耗稍高。

2、无源蜂鸣器:内部不带振荡源,需要控制器提供振荡脉冲才可发声,调整提供振荡脉冲的频率,可发出不同频率的声音。

包括如下种类蜂鸣器:

(1)压电蜂鸣器:

  • 工作原理:利用压电效应产生声音,当施加电压时,压电材料会收缩或膨胀,导致振动产生声音。
  • 特点:体积小,功耗低,响应速度快,适合于需要高频率、短促声音的应用。

(2)压电陶瓷蜂鸣器:

  • 工作原理:结合了压电和陶瓷材料的特性,利用压电效应产生振动,从而产生声音。
  • 特点:结合了压电蜂鸣器和陶瓷蜂鸣器的优点,具有较高的效率和响应速度。

3、有源蜂鸣器和无源蜂鸣器的区别:

有源蜂鸣器和无源蜂鸣器的区别在于是否有源,这个源并不指的是电源而是指震荡源,也就是说有源蜂鸣器内部自带震荡源,所以只要一通电就会发出声音,而无源蜂鸣器内部不带震荡源,所以用直流信号无法令其鸣叫,必须使用交流信号。

(二)蜂鸣器驱动

1、有源蜂鸣器驱动:

有源蜂鸣器不需要外部给予激励源,只需要直接接入直流电源即可自动发出声音(声音频率相对固定),它的发声工作原理是直流电源经过振荡系统的放大取样电路在谐振装置的作用下产生声音信号,原理如下图所示:

2、无源蜂鸣器驱动:

无源蜂鸣器内部没有激励源,只要给予它一个一定频率的方波信号,就能让蜂鸣器的振动装置起振,从而实现无源蜂鸣器发声。同时由于输入的方波频率不同,发出的声音也不同。原理如下图所示:

当我们使用无源蜂鸣器时,如果我们使用直流电源,它不会发出声音。只有交流电源才能发出声音。我们可以通过改变交流电的频率来发出相应的声音。被动蜂鸣器可以通过改变频率来改变其音调,因为没有内部振动源,所以被动蜂鸣器具有声音频率可控的特点,可以演奏音乐。

无源蜂鸣器频率计算:

 公式:频率 = 1/周期,即f = 1/T,二者互为倒数。其中f(频率)的单位为赫兹Hz,T(周期)的单位是秒s。一般我们通过频率来求得周期,比如1KHz对应的周期就是0.001s就是1ms。在实验中我们可以通过延时函数来实现一个周期内输出相同时间的高电平和低电平模拟方波信号将这个信号输给无源蜂鸣器使其发声。

(三)实际应用

1、题目要求:

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

2、Proteus电路原理图:

3、代码实现:

#include <REGX51.H>

sbit sound = P3^5;

void main()
{
	EA=1;               //开总中断.
  	ET1=1;              //允许定时器T1中断         .
   	TMOD=0x10; 			//TMOD=0001 000B,使用T1的方式1定时    	
	TH1=0xFE;      			
   	TL1=0x33;      		//设置定时器1的初值为0xFE33,使定时器每次溢出所需的时间为500微秒。
   	TR1=1;              //启动T1
	while(1)
	{
	}
}

void Timer1_Riutine(void)  interrupt 3
{
	
	sound = ~sound;
	TH1=0xFE;      			
   	TL1=0x33;

}

4、Proteus仿真:

5、开发板实现:

例7-4板子

二、LED数码管秒表

(一)数码管原理介绍:

1、简单介绍:

LED数码管由多个发光二极管封装在一起组成“8”字型的器件,引线已在内部连接完成,只需要引出它的各个笔画以及公共电极。数码管实际上是由七个发光管组成八字形构成的,加上一个小数点加上8个。这些段分别由字母a,b,c,d,e,f,g,dp来表示。电路图如下图所示:

2、LED数码管的分类:

按显示段数分:分为七段、八段、九段、十四段和十六段

七段:七段数码管由7个LED构成;八段:八段数码管比七段多了一个小数点;

九段:九段数码管由9个LED构成;十四段:十四段数码管由14个LED构成;

十六段:十六段数码管由16个LED构成

3、按内部发光二极管单元的连接方式分类:

在proteus元器件库中,共阳数码管为7SEG-MPX1-CA,共阴数码管为7SEG-MPX1-CC。

(1)共阳极:

当LED另一端接入电源的时候,与另一端产生电势差因此会有电流从正极流到GND,最后会亮灯

当LED另一端接入地电源的时候,则不会产生电势差也就不会亮灯。

(2)共阴极:

当LED另一端接入电源的时候,不会产生电势差因此不会亮灯。

当LED另一端接地的时候,会产生电势差,电流会从电源端流经LED到地端,会亮灯。

4、数码管段码表:

将数码管的8个段当成8个LED小灯来控制,即a、b、c、d. e、f、g、dp- -共8个LED小灯。如果点亮b和c这两个LED小灯,也就是数码管的a,b,c,d,e,f段,其他的所有的段都熄灭的话,就可以让数码管显示出一个数字0,以共阳极数码管为例,二进制数字为0b11000000,十六进制为0xC0。同理可得其他数字多对应的数码管的真值,如下表:

共阳极数码管段码表:

字符01234567
数值0xC00xF90xA40xB00x990x920x820xF8
字符89ABCDEF
数值0x800x900x880x830xC60xA10x860x8E

共阴极数码管的段码表:

字符01234567
数值0x3F0x060x5B0x4F0x660x6D0x7D0x07
字符89ABCDEF
数值0x7F0x6F0x770x7C0x390x5E0x790x71

(二)实际应用:

1、题目要求:

用2位数码管显示计时时间,最小计时单位为“百毫秒”,计时范围0.1~9.9s。当第1次按一下计时功能键时,秒表开始计时并显示;第2次按一下计时功能键时,停止计时,将计时的时间值送到数码管显示;如果计时到9.9s,将重新开始从0计时;第3次按一下计时功能键,秒表清0。再次按一下计时功能键,则重复上述计时过程。    本秒表应用定时器模式,计时范围0.1~9.9s。此外还涉及如何编写控制LED数码管显示的程序。

2、Proteus电路原理图:

3、代码实现:

#include <REGX51.H>
typedef unsigned int uint;
typedef unsigned char uchar;

uchar discode1[]={0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,0x87,0xff,0xef};//第一个
uchar discode2[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};//第二个

uchar timer = 0;//中断次数
uchar second;//秒数
uchar key = 0;//按键次数

sbit keyif = P3^7;//定义按键引脚

void Delay1ms(unsigned int xms)		//@12.000MHz
{
	unsigned char i, j;
	while(xms)
	{
		i = 2;
		j = 239;
		do
		{
			while (--j);
		} while (--i);
		xms--;
	}
}//延时函数,按键消抖

void main()
{
	TMOD = 0x01;
	ET0 = 1;
	EA = 1;
	second = 0;
	P0=discode1[second/10];   		//显示秒位0
	P2=discode2[second%10];   		//显示0.1s位0
	while(1)
	{
	  if(keyif == 0)
	  {
		Delay1ms(5);
		  if(keyif == 0)
		  {
			key++;
			switch(key)
			{
				case 1:
				TH0 = 0xEE;
				TL0 = 0x00;
				TR0 = 1;
				break;
				case 2:
				timer = 0;
				TR0 = 0;
				break;
				case 3:
				key = 0;
				second = 0;
				P0 = discode1[0];
				P2 = discode2[0];
				break;	
			}
			while(keyif == 0); //如果按键时间过长在此循环

			}
		}		  
	}
}

void timer0() interrupt 1
{
	TR0 = 0;
	timer++;
	if(timer == 20)
	{
		second++;
		P0=discode1[second/10]; 	//根据计时,即时显示秒位		
		P2=discode2[second%10]; 	//根据计时,即时显示0.1s位
		timer = 0;
	}
	if(second == 99)//计时到达9.9s
	{
		TR0 = 0;//停止计时
		second = 0;//秒数清零
		key = 2;//停止计时
	}
	else
	{
		TR0 = 1;//继续计时
	}

}

4、Proteus仿真:

三、LCD1602显示时钟

(一)LCD1602模块简介

  • LCD1602(Liquid Crystal Display)液晶显示屏是一种字符型液晶显示模块,可以显示ASCII码的标准字符和其它的一些内置特殊字符,还可以有8个自定义字符
  • 显示容量:16×2个字符,每个字符为5*7点阵

LCD1602引脚接线:

各个引脚的作用如下:

其中:

  • RS是寄存选择,高电平选择数据寄存器,低电平选择指令寄存器。
  • RW为读写选择,高电平进行读操作,低电平进行写操作。
  • E端为使能端,后面与时序联系在一起。

LCD1602内部结构框图:

1、DDRAM(数据显示区)

2、CGROM(字模库)

(二)LCD1602驱动

1、写数据/指令时序

我们来分析一下时序图,当我们要写指令的时候,RS置为低电平,RW置为低电平,EN置为低电平,然后将指令数据送到数据口D0~D7,延时tsp1,让1602准备接收数据,这时候将EN拉高,产生一个上升沿,这时候指令就开始写入LCD,延时一段时间,将EN置低电平。

当我们要写数据的时候,RS置为高电平,RW置为低电平,EN置为低电平,然后将指令数据送到数据口D0~D7,延时tsp1,让1602准备接收数据,这时候将EN拉高,产生一个上升沿,这时候数据就开始写入LCD,延时一段时间,将EN置低电平。

写指令的步骤:

Step1:将RS置0;
Step2:将RW置0;
Step3:将指令(Command)写入LCD_DataPort
Step4:将EN置1;
Step5:延时1ms;
Step6:将EN置0;
Step7:延时1ms;

写指令代码:

void LCD_WriteCommand(unsigned char Command)//LCD写命令
{
	LCD_RS=0;
	LCD_RW=0;
	LCD_DataPort=Command;
	LCD_EN=1;
	LCD_Delay();
	LCD_EN=0;
	LCD_Delay();
}

写数据的步骤:

Step1:将RS置0;
Step2:将RW置0;
Step3:将数据(Data)写入LCD_DataPort
Step4:将EN置1;
Step5:延时1ms;
Step6:将EN置0;
Step7:延时1ms;

写数据代码:

void LCD_WriteData(unsigned char Data)//LCD写数据
{
	LCD_RS=1;
	LCD_RW=0;
	LCD_DataPort=Data;
	LCD_EN=1;
	LCD_Delay();
	LCD_EN=0;
	LCD_Delay();
}

2、指令集

常用指令:

(1)清显示,指令码01H

功能:

  • 光标复位到地址00H位置,
  • LCD显示DDRAM的内容全部写入” “的ASCII码20H

(2)显示开关控制

功能:

  • D(Dispaly):控制整体的显示开与关,高电平表示开显示屏,低电平表示关显示屏
  • C(Cursor):控制光标的开与关,高电平表示有光标,低电平表示无光标
  • B(Blink):控制光标是否闪烁,高电平闪烁,低电平不闪烁

(3)功能设置命令

  • DL:DL=1代表数据长度为8位,DL=0代表数据长度为4位
  • N:低电平时只有一行可以显示,高电平时两行都可以显示,
  • F:低电平时一个字符大小为5X7的点阵字符,高电平时一个字符大小为5X10的点阵字符。

3、LCD初始化

初始化:     发送指令0x38    //八位数据接口,两行显示,5*7点阵     

                   发送指令0x0C    //显示开,光标关,闪烁关     

                   发送指令0x06    //数据读写操作后,光标自动加一,画面不动     

                   发送指令0x01    //清屏

代码:

void LCD_Init()
{
	LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
	LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
	LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
	LCD_WriteCommand(0x01);//光标复位,清屏
}

4、其余常用LCD1602代码:

void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)//LCD显示字符
{
	LCD_SetCursor(Line,Column);
	LCD_WriteData(Char);
}

void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)//LCD显示字符串
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=0;String[i]!='\0';i++)
	{
		LCD_WriteData(String[i]);
	}
}

int LCD_Pow(int X,int Y)
{
	unsigned char i;
	int Result=1;
	for(i=0;i<Y;i++)
	{
		Result*=X;
	}
	return Result;
}

void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)//LCD显示数字
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
	}
}

(三)实际应用:

1、题目要求:

使用定时器实现一个LCD显示时钟。采用LCD1602,最小计时单位是秒,如何获得1s的定时?可将T0定时时间定为50ms,采用中断方式进行溢出次数累计,满20次,则秒计数变量second加1;若秒计满60,则分计数变量minute加1,同时将秒计数变量second清0;若分钟计满60,则小时计数变量hour加1;若小时计数变量满24,则将小时计数变量hour清0。

2、Proteus电路原理图:

3、代码实现:

#include <REGX51.H>


typedef unsigned char uchar;
typedef unsigned int uint;

uchar Hour=23,Min=59,Sec=55;

sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P3

void LCD_Delay()
{
	unsigned char i, j;

	i = 2;
	j = 239;
	do
	{
		while (--j);
	} while (--i);
}

void LCD_WriteCommand(unsigned char Command)//LCD写命令
{
	LCD_RS=0;
	LCD_RW=0;
	LCD_DataPort=Command;
	LCD_EN=1;
	LCD_Delay();
	LCD_EN=0;
	LCD_Delay();
}

void LCD_WriteData(unsigned char Data)//LCD写数据
{
	LCD_RS=1;
	LCD_RW=0;
	LCD_DataPort=Data;
	LCD_EN=1;
	LCD_Delay();
	LCD_EN=0;
	LCD_Delay();
}

void LCD_SetCursor(unsigned char Line,unsigned char Column)//LCD设置光标位置
{
	if(Line==1)
	{
		LCD_WriteCommand(0x80|(Column-1));
	}
	else if(Line==2)
	{
		LCD_WriteCommand(0x80|(Column-1+0x40));
	}
}

void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)//LCD显示字符
{
	LCD_SetCursor(Line,Column);
	LCD_WriteData(Char);
}

void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)//LCD显示字符串
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=0;String[i]!='\0';i++)
	{
		LCD_WriteData(String[i]);
	}
}

int LCD_Pow(int X,int Y)
{
	unsigned char i;
	int Result=1;
	for(i=0;i<Y;i++)
	{
		Result*=X;
	}
	return Result;
}

void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)//LCD显示数字
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
	}
}

void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
{
	unsigned char i;
	unsigned int Number1;
	LCD_SetCursor(Line,Column);
	if(Number>=0)
	{
		LCD_WriteData('+');
		Number1=Number;
	}
	else
	{
		LCD_WriteData('-');
		Number1=-Number;
	}
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');
	}
}

void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i,SingleNumber;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		SingleNumber=Number/LCD_Pow(16,i-1)%16;
		if(SingleNumber<10)
		{
			LCD_WriteData(SingleNumber+'0');
		}
		else
		{
			LCD_WriteData(SingleNumber-10+'A');
		}
	}
}

void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');
	}
}

void LCD_Init()
{
	LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
	LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
	LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
	LCD_WriteCommand(0x01);//光标复位,清屏
}

void Timer0_Init()		//定时器初始化
{
	TMOD |= 0x01;		//设置定时器模式
	TL0 = 0x18;		//设置定时初值,与通过计算的得到有一定误差。
	TH0 = 0xFC;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	
	ET0=1;
	EA=1;//将中断打通即令ET0=1。
}

void main()
{
	LCD_Init();
	Timer0_Init();
	LCD_ShowString(1,1,"  :  :  ");
	while(1)
	{
		LCD_ShowNum(1,1,Hour,2);
		LCD_ShowNum(1,4,Min,2);
		LCD_ShowNum(1,7,Sec,2);    
	}
}

void Timer0_Routine() interrupt 1//定时器0的中断程序
{
		static unsigned int T0Count;//静态变量,让局部变量变为全局变量。
		TL0 = 0x18;		
		TH0 = 0xFC;//重新赋值,计时器重新开始运行。
		T0Count++;
		if(T0Count>=1000)//定时为1s
		{
			T0Count=0;
			Sec++;
			if(Sec>=60)
			{
				Sec=0;
				Min++;
				if(Min>=60)
				{
					Min=0;
					Hour++;
					if(Hour>=24)
					{
						Hour=0;
					}
				}
			}
		}//每隔1s。
		
}//中断函数

4、Proteus仿真:

5、开发板实现:

7-7

四、串口通信

(一)串口简介

串口是一种应用十分广泛的通讯接口,串口成本低、容易使用、通信线路简单,可实现两个设备的互相通信。

单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信,极大的扩展了单片机的应用范围,增强了单片机系统的硬件实力。

51单片机内部自带UART(Universal Asynchronous Receiver Transmitter,通用异步收发器),可实现单片机的串口通信。

1、串口通信的功能

数据通信:

数据通信通常是指单片机与单片机之间,或者单片机与其他设备之间的信息交换。通常把数据通信分为如下两种:

(1)并行通信:数据的各位同时进行发送或接收的通信方式。优点是速率高。缺点是需要的传输线多,成本高,只适合近距离的数据通信。

(2)串行通信:一位一位的按顺序的进行发送或接收的通信方式。优点是需要的传输线少,成本低。缺点是传输的速率慢,适合远距离的数据通信。

2、单片机串口结构

结构图:

(1)两个数据缓冲器:SBUF,串口数据缓存寄存器,物理上是两个独立的寄存器,但占用相同的地址。写操作时,写入的是发送寄存器,读操作时,读出的是接收寄存器。

(2)定时器1产生波特率, 串口一般使用定时器1,模式2,八位自动重装模式,来产生溢出率,从而产生波特率。而且在配置定时器相关的寄存器时不用配置定时器中断,只是使用定时器1来产生波特率的功能

(3)移位寄存器,在接受控制器的控制下,将输入的数据逐位移入接收SBUF。

(4)串行控制寄存器SCON,SCON的功能是控制串行通信口的工作方式以及工作的状态。

(二)串行通信口的控制寄存器

1、串行口控制寄存器SCON

STC89C51RC/RD+系列单片机的串行口设有两个控制寄存器:串行控制寄存器SCON和波特率选择特殊功能寄存器PCON。
串行控制寄存器SCON用于选择串行通信的工作方式和某些控制功能。其格式如下:SCON:串行控制寄存器(可位寻址)

通过配置SCON寄存器来对串行通信的工作模式进行控制。
(1)SM0/FE:当PCON寄存器中的SMODO/PCON.6位为1时,该位用于帧错误检测。当检测到一个SMO/FE:无效停止位时,通过UART接收器设置该位。它必须由软件清零。当PCON寄存器中的SMOD0/PCON.6位为0时,该位和SM1一起指定串行通信的工作方式,如下表所示。

(2)REN:允许/禁止串行接收控制位。由软件置位REN,即REN=1为允许串行接收状态,可启动串行接收器RD,开始接收信息。软件复位REN,即REN-0,则禁止接收。

(3)TI:发送中断请求标志位。在方式0,当串行发送数据第8位结束时,由内部硬件自动置位,即TI=1,向主机请求中断,响应中断后必须用软件复位,即TI=0。在其他方式中,则在停止位开始发送时由内部硬件置位,必须用软件复位。

(4)RI:接收中断请求标志位。在方式0,当串行接收到第8位结束时由内部硬件自动置位RI=1,向主机请求中断,响应中断后必须用软件复位,即RI=0。在其他方式中,串行接收到停止位的中间时刻由内部硬件置位,即RI=1(例外情况见SM2说明),必须由软件复位,即RI=0。

2、电源控制寄存器PCON

配置PCON寄存器

(1)SMOD:波特率选择位。当用软件置位SMOD,即SMOD=1,则使串行通信方式1、2、3的波特率加倍;SMOD=0,则各工作方式的波特率加倍。复位时SMOD=0。

(2)SMOD0:帧错误检测有效控制位。当SMOD0=1,SCON寄存器中的SMO/FE位用于FE(帧错误检测)功能;当SMOD0=0,SCON寄存器中的SMOFE位用于SM0功能,和SM1一起指定串行口的工作方式。复位时SMOD0=0。

3、配置中断


(三)实际应用

1、题目一:

(1)题目要求:

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

(2)代码实现:
#include <REGX51.H>
#include "stdio.h"

unsigned char a;
unsigned char Flag=1;
void Delay1ms(unsigned int xms)		//@12.000MHz
{
	unsigned char i, j;
	while(xms)
	{
		i = 2;
		j = 239;
		do
		{
			while (--j);
		} while (--i);
		xms--;
	}
}
void UartInit(void)		//9600bps@12.000MHz
{
	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;
		Delay1ms(2000);
}

void main()
{
	UartInit();
	while(1)
	{
		if(Flag==1)
		UartSend();
	}	
}
 
 
//串口中断函数模板
void UART_Routine()	interrupt 4 //串口中断
{
	if(RI==1)
	{
		RI=0;
		a=SBUF;
		if(a=='1')Flag=1;
		if(a=='0')Flag=0;
	}
}
(3)串口助手效果:

c51

2、题目二:

(1)题目要求:

甲、乙两单片机进行 方式3(或方式2)串行通信。甲机把控制8个流水灯点亮的数据发送给乙机并点亮其P1口的8个LED。方式3比方式1多了1个可编程位TB8,该位一般作奇偶校验位。乙机接收到的8位二进制数据有可能出错,需进行奇偶校验,其方法是将乙机的RB8和PSW的奇偶校验位P进行比较,如果相同,接收数据;否则拒绝接收。

(2)Proteus电路原理图:

(3)代码实现:

甲机:

#include <REGX51.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 Delay1ms(unsigned int xms)		//@12.000MHz
{
	unsigned char i, j;
	while(xms)
	{
		i = 2;
		j = 239;
		do
		{
			while (--j);
		} while (--i);
		xms--;
	}
}
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]);
			Delay1ms(200);
		}
	}
}

乙机:

#include <REGX51.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;  
	SCON=0xd0;	
	PCON&=0x7f;
	TH1=0xfd;	
	TL1=0xfd;
	TR1=1;
	while(1)
	{
		 P2=Receive();	
	}
}
(4)Proteus仿真:

参考博客链接:【常用传感器】LCD1602液晶工作原理详解及例程代码_当rs为低电平,r/w为高电平时-CSDN博客

  • 19
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
52单片机可以使用定时器2来实现串口通信。下面是一种常见的实现方式: 1. 首先,需要设定时器2的工作模式。在52单片机中,定时器2可以选择作为波特率发生器来生成串口通信所需的波特率。设定时器2的工作模式,可以使用T2CON寄存器进行配。 例如,下面的代码将定时器2设为8位自动重装载模式,使用定时器2作为波特率发生器: ```c T2CON = 0x34; // 设T2CON寄存器,启用定时器2,选择8位自动重装载模式 ``` 2. 接下来,需要设波特率。波特率决定了串口通信数据传输速率。根据串口通信的要求,选择合适的波特率,并使用RCAP2H和RCAP2L寄存器设波特率发生器的重装载值。重装载值的计算公式为:重装载值 = (定时器2的时钟频率) / (波特率 x 32) - 1。 例如,下面的代码将波特率设为9600: ```c RCAP2H = 0xFF; // 设重装载值的高字节 RCAP2L = 0xDC; // 设重装载值的低字节 ``` 3. 然后,需要使能串口通信。使用SCON寄存器进行配,设串口通信的工作模式和使能位。 例如,下面的代码将串口通信为8位数据位、1个停止位、无奇偶校验,并使能串口: ```c SCON = 0x50; // 设SCON寄存器,选择8位数据位、1个停止位、无奇偶校验 ``` 4. 最后,启动定时器2。使用TR2位进行控制,将其设为1即可启动定时器2。 例如,下面的代码启动定时器2: ```c TR2 = 1; // 启动定时器2 ``` 通过以上步骤,你就可以在52单片机中使用定时器2实现串口通信了。当定时器2每次溢出时,会产生一个中断,你可以在中断服务函数中处理串口通信数据和接收操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值