蓝桥杯单片机学习笔记:串口通信原理与应用详解

目录

一、原理部分

1、什么是串行通信

(1)并行通信与串行通信

(2)串行通信的制式

(3)串行通信的主要方式 

 2、配置串口

(1)SCON和PCON :串行口1的控制寄存器

(2)SBUF:串行口数据缓冲寄存器 

(3)AUXR:辅助寄存器​编辑

(4)ES、PS :与串行口1中断相关的寄存器

(5)波特率设置 

 3、串口框架编写

二、程序案例


一、原理部分

1、什么是串行通信

(1)并行通信与串行通信

微控制器与外部设备的数据通信,根据连线结构和传送方式的不同,可以分为两种:

并行通信和串行通信。

并行通信:数据的各位同时发送与接收,每个数据位使用一条导线,这种方式传输快,但是需要多条导线进行信号传输。

串行通信:数据一位一位地按照顺序发送与接收。这种方式仅需要一根导线,但是传输较慢

串行通信有SPI、IIC、UART等多种,最常见最通用的是UART,大多数情况下,串口通信指的是UART。  

(2)串行通信的制式

串行通信的制式有:单工、半双工、全双工三种

单工: 单工制式下,通信线的一端为发送器,一端为接收器,数据只能按照一个固定的方向传送;半双工:半双工制式下,系统的每个通信设备都由一个发送器和一个接收器组成,因而数据能从A站传送到B站,也可以从B站传送到A站,但是不能同时在两个方向上传送;

全双工:全双工方式下,系统的每端都有发送器和接收器,可以同时发送和接收,即数据可以在两个方向上同时传送。

(3)串行通信的主要方式 

串行通信的主要方式有两种:同步和异步

同步串行通信:使用同一个时钟,以数据块为单位传输数据

异步串行通信:每个设备有自己的时钟信号,通信双方的波特率(串口每秒钟传输的位数)需要保持一致,以字符为单位进行数据帧传送,依次传输一个帧。 

 2、配置串口

配置前需要了解一下相关寄存器

(1)SCON和PCON :串行口1的控制寄存器

SCON: 串行控制寄存器 (可位寻址)

SM0/FE:

        当PCON寄存器中的SMODO/PCON.6位为1时,该位用于顿错误检测。当检测到一个无效停止位时,通过UART接收器设置该位。它必须由软件清零。

        当PCON寄存器中的SMODO/PCON.6位为0时,该位和SM1一起指定串行通信的工作方式,如下表所示。 

一般使用方式1,即SM0=0,SM1=1

SM2: 允许方式2或方式3多机通信控制位。暂时不用理,置0即可。

REN:介许/禁止串行接收控制位。

        由软件置位REN,即REN=1为允许串行接收状态,可启动串行接收器RxD,开始接收信息。

        软件复位REN,即REN=0,则禁止接收

TB8、RB8:奇偶校验位,方式1不用用到,置0即可。

TI:发送中断请求标志位。

        在方式0,当串行发送数据第8位结束时,由内部硬件自动置位.即TI=1,向主机请求中断,响应中断后TI必须用软件清零,即TI=0。

        在其他方式中,则在停止位开始发送时由内部硬件置位,即TI=1,响应中断后TI必须用软件清零。
RI:接收中断请求标志位。

        在方式0,当串行接收到第8位结束时由内部硬件自动置位RI=1,向主机请求中断,响应中断后RI必须用软件清零,即RI=0。

        在其他方式中,串行接收到停止位的中间时刻由内部硬件置位,即RI=1,向CPU发中断申请,响应中断后RI必须由软件清零

使用方式1,则需要REN置1,剩余置零即可,则加上前面的SM0=0,SM1=1,SCON应该设置为:SCON=0x50

至于TI以及RI:

        当一帧发送完成,内部硬件自动置位TI,即TI=1,请求中断处理;

        当接收完一帧信息时,内部硬件自动置位RI,即RI=1,请求中断处理。

        由于TI和RI以“或逻辑”关系向主机请求中断,所以主机响应中断时事先并不知道是TI还是RI请求的中断,必须在中断服务程序中查询TI和RI进行判别,然后分别处理。因此,两个中断请求标志位均不能由硬件自动置位,必须通过软件清0,否则将出现一次请求多次响应的错误。

 PCON:电源控制寄存器 (不可位寻址)

SMOD:波特率选择位。

        当用软件置位SMOD,即SMOD=1,则使串行通信方式1、2、3的波特率加倍;

        SMOD=0,则各工作方式的波特率加倍。复位时SMOD=0。

这个寄存器只需要用到SMOD一位,默认置0;可以不用理。

(2)SBUF:串行口数据缓冲寄存器 

STC15系列单片机的串行口1缓冲寄存器(SBUF)的地址是99H,实际是2个缓冲器,一个是

发送寄存器,一个是接收寄存器,在物理结构上是完全独立的,字节地址均为99H,通过读/写指令区区分:

        串行发送时,CPU向SBUF写入数据,此时99H表示发送缓存SBUF。

        串行接收时:CPU从SBUF读取数据,此时99H表示接收缓存SBUF。
数据发送:把数据扔进SBUF后,内核会自动将数据发送,内容发送完毕,TI标志位置1。

数据接收:内核从串口接收到一个完整数据后,会将RI标志置1,用户用SBUF读取即可。

        由于接收通道内设有输入移位寄存器和SBUF缓冲器,从而能使一帧接收完将数据由移位寄存器装入SBUF后,可立即开始接收下一帧信息,主机应在该顿接收结束前从SBUF缓冲器中将数据取走,否则前一帧数据将丢失。SBUF以并行方式送往内部数据总线。 

(3)AUXR:辅助寄存器

T1x12: 定时器1速度控制位
                0,定时器1是传统8051速度,12分频;
                1,定时器1的速度是传统8051的12倍,不分频

如果UART1/串口1用T1作为波特率发生器,则由T1x12决定UART1/串口是12T还是1T

S1ST2: 串口1(UART1) 选择定时器2作波特率发生器的控制位

        0,选择定时器1作为串口1(UART1)的波特率发生器

        1,选择定时器2作为串口1(UART1)的波特率发生器,此时定时器1得到释放,可以作独立定时器使用

AUXR需要用到的为以上两位,这里选择12分频,以及定时器1作为波特率发生器

则AUXR设置为:

AUXR=0x00;

头文件“reg52.h”没有定义AUXR,因此需要添加:

sfr AUXR=0x8e;

才可以使用AUXR寄存器。

(4)ES、PS :与串行口1中断相关的寄存器

IE: 中断允许寄存器 (可位寻址)

EA: CPU的总中断允许控制位

        EA=1,CPU开放中断,
        EA=0,CPU屏蔽所有的中断申请。

        EA的作用是使中断允许形成多级控制。即各中断源首先受EA控制:其次还受各中断源自己的中断允许控制位控制。
ES: 串行口中断允许位

        ES=1,允许串行口中断

        ES=0,禁止串行口中断 

则IE应设置为:

EA=1;

ES=1;

 IP:中断优先级控制寄存器低 (可位寻址)

 PS :串行口1中断优先级控制位。

        当PS=0时,串行口1中断为最低优先级中断(优先级0)

        当PS=1时,串行口1中断为最高优先级中断(优先级1)

(5)波特率设置 

 此外,还需要配置一下波特率,我们使用的是定时器1做串口波特率发生器,因而需要设置定时器1的TH1以及TL1(设置值可参考下标),以及定时器的工作模式(8位自动重装载模式),观看我前面写的文章——定时器原理及其应用,则定时器的配置形式为:

TMOD=0x20;
TH1=0xfd;

TL1=0xfd;

TR1=1;

 3、串口框架编写

串口设计使用中,一般需要编写三个函数:串口初始化函数、字节发送函数及串口中断服务函数

(1)初始化函数:

①配置工作模式,对TMOD寄存器编程,TMOD=0x20(8位自动重装载模式)
②计算计数初值,对THx和TLx寄存器进行赋值(波特率9600,设置THx=0xfd,TLx=0xfd)

③启动定时器,即TR0或TR1置1
④设置SCON,为0x50

⑤设置AUXR,为0x00
⑥使能串口中断,ES=1

⑦使能总中断,即EA =1

(2)中断服务函数(串口1中断号为4):

①对接收还是发送进行判断,即RI以及TI是否为1

②若是接收,则将SBUF中的数据取走(定义变量接收),将RI清零

③相关逻辑处理

(3)字节发送函数:

①将数据赋给SBUF

②等待数据发送,此时TI=0,使用while等待至TI=1

③将TI清零

 

另外,特别注意需要在前面加上:

sfr AUXR=0x8e;

才可以使用AUXR。

二、程序案例

例子:采用8位的UART模式,波特率为9600BPS,建立数据传输通道,完成以下任务:

1、系统上电后关闭蜂鸣器继电器灯设备

2、初始化发送字符串“Welcom to FSZ system!”

3、上位机发送以下命令并产生相应响应

#include "reg52.h"

sfr AUXR=0x8e;//定义寄存器

/*****************************************************************
*@Function: SelectHc573     //
*@Description:锁存器选择     //
*@Input: channel:通道选择//
*@Output:无 //
*@Return: 无 //
*@Others: 无 //
/*****************************************************************/
void SelectHc573(unsigned char channel)
{
	switch(channel)
	{
		case 4:
			P2=P2&0x1f|0x80;
		break;
		case 5:
			P2=P2&0x1f|0xa0;
		break;
		case 6:
			P2=P2&0x1f|0xc0;
		break;
		case 7:
			P2=P2&0x1f|0xe0;
		break;
		case 0:
			P2=P2&0x1f|0x00;
		break;
	}
}

/*****************************************************************
*@Function:InitUart      //
*@Description: 串口初始化    //
*@Input: 无//
*@Output:无 //
*@Return: 无 //
*@Others: 无 //
/*****************************************************************/
void InitUart()
{
	TMOD=0x20;//设置定时器模式
	TH1=0xfd;//设置定时器初始值
	TL1=0xfd;
	TR1=1;//使能时钟
	
	SCON=0x50;//设置串口模式
	AUXR=0x00;//配置AUXR
	ES=1;//使能串口中断
	EA=1;//使能总中断
}

unsigned char command=0x00;//用于接收串口的数据

/*****************************************************************
*@Function: ServiceUart     //
*@Description:中断服务函数     //
*@Input:无 //
*@Output: 无//
*@Return: 无 //
*@Others: 无 //
/*****************************************************************/
void ServiceUart () interrupt 4
{
	if(RI==1)//如果RI=1(接收到数据)
	{
		command=SBUF;//将接收的数据赋值给command
		RI=0;//将接收标志位RI清零
	}
}

/*****************************************************************
*@Function: SendByte     //
*@Description:通过串口发送一个字节     //
*@Input:dat:发送的字节 //
*@Output:无 //
*@Return:无  //
*@Others:无  //
/*****************************************************************/
void SendByte(unsigned char dat)
{
	SBUF=dat;//将dat数据赋给SBUF,将数据发送出去
	while(TI==0);//等待数据发送
	TI=0;//将发送标志位清零
}

/*****************************************************************
*@Function: SendString     //
*@Description: 字符串发送函数    //
*@Input: *str:发送的字符串首地址//
*@Output:无 //
*@Return: 无 //
*@Others:无  //
/*****************************************************************/
void SendString(unsigned char *str)
{
	while(*str!='\0')//当字符不为空时,继续发送
	{
		SendByte(*str++);//发送后指针str加1,指向下一个字节
	}

}

/*****************************************************************
*@Function:Working      //
*@Description: 对串口接收的数据进行处理    //
*@Input: 无//
*@Output:无 //
*@Return: 无 //
*@Others: 无 //
/*****************************************************************/
void Working()
{
	if(command!=0x00)//如果command不为0x00(即接收到数据)
	{
		switch(command&0xf0)//对数据进行判断
		{
			case 0xa0://若为0xa0
				P0=(P0|0x0f)&(~command|0xf0);//将command取反后取出后四位(将前四位置1),P0的低四位置1,两者与上,即保持P0的高四位,将command的低四位覆盖P0低四位
				SelectHc573(4);//转换锁存器
				command=0x00;//将command清零
				break;
			case 0xb0://若为0xb0
				P0=(P0|0xf0)&(~command<<4|0x0f);//将command取反后左移四位,再取出前四位(将后四位置1),P0的前四位置1,两者与上,即保持P0的低四位,将command的高四位覆盖P0高四位
				SelectHc573(4);//转换锁存器
				command=0x00;//将command清零
				break;
			case 0xc0://若是0xc0
				SendString("The system is runing\r\n");//发送字符串The system is runing\r\n
				command=0x00;//将command清零
				break;
		}
	}
}

/*****************************************************************
*@Function: InitSystem     //
*@Description:初始化,关闭蜂鸣器,LED灯以及继电器     //
*@Input:无 //
*@Output:无 //
*@Return: 无 //
*@Others:无  //
/*****************************************************************/
void InitSystem()
{
	SelectHc573(5);
	P0=0x00;
	SelectHc573(4);
	P0=0xff;
}

void main()
{
	InitSystem();
	InitUart();
	SendString("Welcom to FSZ system!\r\n");//发送字符串“Welcom to FSZ system!\r\n”
	while(1)
	{
		Working();
	}
}

蓝桥杯的学习笔记持续更新中~

要是文章有帮助的话,就点赞收藏关注一下啦!

感谢大家的观看

欢迎大家提出问题并指正~

物联沃分享整理
物联沃-IOTWORD物联网 » 蓝桥杯单片机学习笔记:串口通信原理与应用详解

发表回复