06【基础学习】串口通信

1、基础知识

1.1、通信

设备与设备之间需要通信时,例如单片机与单片机,单片机与模块等,通信又分为2种方式:串行通信与并行通信。

①并行通信:

😀传输原理:数据各个位同时传输。
😀优点:速度快
😀缺点:占用引脚资源多

在这里插入图片描述
如上图所示:假设两个设备之间传输的是一个8位的数据,上图就是并行通信连接方式,需要八根线,两个设备各占八个IO口,每一个IO口对应数据的一位

②串行通信:

😀传输原理:数据按位顺序传输。
😀优点:占用引脚资源少
😀缺点:速度相对较慢

在这里插入图片描述
如上图所示:同样还是传输8位的数据,只需要一根数据线就可以实现数据的传送。另一根是共地线

1.2、串行通信方式分类

①按照传输方向分:

😀单工:数据传输只支持数据在一个方向上传输
😀半双工:允许数据在两个方向上传输,但是,在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信
😀全双工:允许数据同时在两个方向上传输,因此,全双工通信是两个单工通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力

在这里插入图片描述
②按照是否有时钟线分:

😀有时钟线:同步通信
例如:SPI,I2C等通信方式
😀无时钟线:异步通信
例如:UART通信方式

在这里插入图片描述

1.3、数据帧格式

串口接收和发送的数据是以数据帧进行传输的。那什么是数据帧喃?

🤑数据帧 = 起始位 + 数据位 + 校验位 + 停止位
😀起始位:低电平0
😀数据位:即发送/接收到的数据内容(可配置为8位/9位)
😀校验位:奇校验/偶校验(可配置为无校验/奇校验/偶校验)
【注意】若配置了校验位,则校验位占据数据位的最后一位
😀停止位:高电平1(可配置为0.5/1/1.5/2位)

在这里插入图片描述
①起始位:
串口总线在空闲时,总线上的电平为高电平1。所以需要使用起始位为低电平0来打破这个平衡,代表将有数据进行传输。
②数据位:
起始位后面紧跟着就是数据位,数据位可配置为8位(一个字节),也可以配置为9位
③校验位:
校验位可配置为奇校验和偶校验。当配置为奇校验时,若有效数据位中1的个数为偶数,则校验位写入1;若有效数据位中1的个数为奇数,则校验位写入0

在这里插入图片描述
校验位占据了数据位的一位。当配置为8位数据位+1位校验位 = 7位有效数据位 + 1位校验位。 当配置为9位数据位+1位校验位 = 8位有效数据位 + 1位校验位
最常用的情况是:8位数据位 + 0位校验位 or 9位数据位 + 1位校验位

④停止位:
在数据帧的末尾会有一位停止位(高电平1),将总线电平拉高,代表数据传输完成。

1.4、波特率

①同步通信:
同步通信时,可以使用时钟线让发送设备和接收设备处理数据时达到同步状态,这样使接收设备能够准确无误的接收到数据。如下图所示:
在这里插入图片描述
②异步通信:
而异步通信没有时钟线,那接收设备是怎样准确无误的获取到哦发送设备发送的数据喃?
答案:接收设备和发送设备使用相同的波特率

波特率:1s发送码元的个数。在计算机中使用的是二进制,则0和1就代表为一个码元。所以波特率就代表1s发送0/1的个数。
9600波特率 = 1s传输了9600个数据位,则传输一个数据位的速度 = 1s / 9600

在这里插入图片描述

1.5、硬件流控(不常用)

硬件流控的作用主要是防止传输的数据丢失,当传输一个数据帧时,等待接收方返回一个反馈信号。如果反馈的是一个低电平,代表接收方可以继续接收数据,然后发送方继续发送数据。
在这里插入图片描述

2、串口UART

UART是使用异步全双工通信方式(即同一时间段既能发送数据,又能接受数据,有2个数据线:Tx和Rx,低位先行)。其UART的通信接口如下图所示:
在这里插入图片描述
如图所示:串口拥有TX(发送数据引脚)和RX(接收数据引脚),2个设备通过串口接收/发送数据时,这2个引脚需要交替连接。

2.1、寄存器简介

在这里插入图片描述

2.2、串口内部基本结构

在这里插入图片描述

2.3、工作方式和波特率

在这里插入图片描述

综上:若想将串口在方式1的波特率配置为9600,则应该如何设置?

在这里插入图片描述

3、实验

3.1、串口发送

3.1.1、发送一个字节

实验要求:使用轮询方式让单片机通过串口向外发送一个字节的数据

创建工程名为:20_UART

在这里插入图片描述

①UART.c文件的代码如下:

#include "UART.h"

/**
 * 串口初始化
 * 参数:波特率
 */
void UART_Init(unsigned int Baud)
{
	/* 串口寄存器的配置 */
	 PCON &= 0x3F;	//PCON = 00xx xxxx
	 SCON &= 0x0F;
	 SCON |= 0x50;	//SCON = 0101 xxxx
	
	/* 配置T1的寄存器 */
	TMOD &= 0x0F;
	TMOD |= 0x20;	//TOMD = 0010 xxxx:T1定时器为8位自动重装载
	TH1 = 256 - (28800/Baud);//配置波特率
	TL1 = 256 - (28800/Baud);//配置波特率
	ET1 = 0;		//关闭T1溢出中断
	TR1 = 1;		//使能T1
}

②UART.h文件的代码如下:

#ifndef __UART_H
#define __UART_H
#include <REGX52.H>	//包含51头文件,里面全是寄存器地址

void UART_Init(unsigned int Baud);

#endif

③mian.c文件的代码如下:

#include "UART.h"
#include "Delay.h"
#include "Timer.h"

void main(void)
{
	UART_Init(9600);//9600波特率
	Time0_Intrrupt_Init();	//定时器T0的初始化,Delay_ms需要
	
	while(1)
	{
		SBUF = 0x88;//给SBUF写入一个字节数据0x88	
		while(!TI);	//等待数据发送完成(未完成TI = 0,完成TI = 1)
		TI = 0;		//手动清0
		Delay_ms(500);
	}
}

在这里插入图片描述

3.1.2、发送多个字节

实验要求:
①使用轮询方式让单片机通过串口向外发送一个字节的数据
②使用轮询方式让单片机通过串口向外发送多个字节的数据
③使用轮询方式让单片机通过串口向外发送字符串
④使用模块化编程

复制工程20_UART,粘贴改名为:21_UART_Send

①UART.c文件的代码如下:

#include "UART.h"

/**
 * 串口初始化
 * 参数:波特率
 */
void UART_Init(unsigned int Baud)
{
	/* 串口寄存器的配置 */
	 PCON &= 0x3F;	//PCON = 00xx xxxx
	 SCON &= 0x0F;
	 SCON |= 0x50;	//SCON = 0101 xxxx
	
	/* 配置T1的寄存器 */
	TMOD &= 0x0F;
	TMOD |= 0x20;	//TOMD = 0010 xxxx:8位自动重装载
	TH1 = 256 - (28800/Baud);//配置波特率
	TL1 = 256 - (28800/Baud);//配置波特率
	ET1 = 0;		//关闭T1溢出中断
	TR1 = 1;		//使能T1
}

/**
 * 发送一个字节的数据
 */
void Send_Char(unsigned char ch)
{
	SBUF = ch;	//给SBUF写入一个字节数据
	while(!TI);	//等待数据发送完成(未完成TI = 0,完成TI = 1)
	TI = 0;		//手动清0
}

/**
 * 发送多个字节的数据
 */
void Send_Array(unsigned char* Array,unsigned char Len)
{
	unsigned char i;
	for(i = 0; i<Len; i++)
	{
		Send_Char(*(Array+i));
	}
}

/**
 * 发送字符串
 */
void Send_String(unsigned char* str)
{
	while(*str != '\0')
	{
		Send_Char(*str++);
	}
}

②UART.h文件的代码如下:

#ifndef __UART_H
#define __UART_H
#include <REGX52.H>	//包含51头文件,里面全是寄存器地址

void UART_Init(unsigned int Baud);
void Send_Char(unsigned char ch);//发送一个字节的数据
void Send_Array(unsigned char* Array,unsigned char Len);//发送多个字节的数据
void Send_String(unsigned char* str);//发送字符串

#endif

③mian.c文件的代码如下:

#include "UART.h"
#include "Delay.h"
#include "Timer.h"

static unsigned char Arr[] = {0x10,0x20,0x30,0x40,0x50};
void main(void)
{
	UART_Init(9600);//9600波特率
	Time0_Intrrupt_Init();
	
	while(1)
	{
		Send_Char(0xAA);//发送一个字节数据AA
		Delay_ms(500);
		
		Send_Array(Arr,sizeof(Arr)/sizeof(Arr[0]));//发送多个字节数据
		Delay_ms(500);
	}
}

在这里插入图片描述

3.1.3、printf重定向

实验要求: printf函数的重定向

如下为printf函数的使用

#include <stdio.h>

void mian(void) 
{
  char a = 1;
  int b  = 12365;
  long c = 0x7FFFFFFF;

  unsigned char x = 'A';
  unsigned int y  = 54321;
  unsigned long z = 0x4A6F6E00;

  float f = 10.0;
  float g = 22.95;

  char buf [] = "Test String";
  char *p = buf;

  printf ("char %bd int %d long %ld\n",a,b,c);
  printf ("Uchar %bu Uint %u Ulong %lu\n",x,y,z);
  printf ("xchar %bx xint %x xlong %lx\n",x,y,z);
  printf ("String %s is at address %p\n",buf,p);
  printf ("%f != %g\n", f, g);
  printf ("%*f != %*g\n", (int)8, f, (int)8, g);
}

①printf重定向的代码如下:

#include "UART.h"

/**
 * 串口初始化
 * 参数:波特率
 */
void UART_Init(unsigned int Baud)
{
	/* 串口寄存器的配置 */
	 PCON &= 0x3F;	//PCON = 00xx xxxx
	 SCON &= 0x0F;
	 SCON |= 0x50;	//SCON = 0101 xxxx
	
	/* 配置T1的寄存器 */
	TMOD &= 0x0F;
	TMOD |= 0x20;	//TOMD = 0010 xxxx
	TH1 = 256 - (28800/Baud);//配置波特率
	TL1 = 256 - (28800/Baud);//配置波特率
	ET1 = 0;		//关闭T1溢出中断
	TR1 = 1;		//使能T1
}

/**
 * 发送一个字节的数据
 */
void Send_Char(unsigned char ch)
{
	SBUF = ch;	//给SBUF写入一个字节数据
	while(!TI);	//等待数据发送完成(未完成TI = 0,完成TI = 1)
	TI = 0;		//手动清0
}

/**
 * 对printf函数进行重定向
 */
char putchar(char ch)
{
	Send_Char(ch);
    return ch;
}

②UART.h文件的代码如下:

#ifndef __UART_H
#define __UART_H
#include <REGX52.H>	//包含51头文件,里面全是寄存器地址
#include <stdio.h>

void UART_Init(unsigned int Baud);
void Send_Char(unsigned char ch);//发送一个字节的数据
void Send_Array(unsigned char* Array,unsigned char Len);//发送多个字节的数据
void Send_String(unsigned char* str);//发送字符串
char putchar(char ch);//printf()重定向

#endif

③mian.c文件的代码如下:

#include "UART.h"
#include "Delay.h"
#include "Timer.h"

static unsigned char Arr[] = {0x10,0x20,0x30,0x40,0x50};
void main(void)
{
	UART_Init(9600);//9600波特率
	Time0_Intrrupt_Init();
	
	while(1)
	{
//		Send_Char(0xAA);//发送一个字节数据AA
//		Delay_ms(500);
//		
//		Send_Array(Arr,sizeof(Arr)/sizeof(Arr[0]));//发送多个字节数据
//		Delay_ms(500);
		printf("nihao\r\n");
		printf("%bd\r\n",88);
		printf("%d\r\n",12365);
		printf("%f\r\n",3.14);
		Delay_ms(500);
	}
}

在这里插入图片描述

3.2、串口接收

使用VSPD软件模拟串口,软件安装包:链接: link
在这里插入图片描述
电路连接和串口的配置如下图所示:
在这里插入图片描述

3.2.1、接收一个字节

实验要求: 使用轮询方式接收一个字节的数据

复制工程21_UART_Send,粘贴改名为:22_UART_Send_Receive

①UART.c文件的代码如下:

#include "UART.h"

/**
 * 串口初始化
 * 参数:波特率
 */
void UART_Init(unsigned int Baud)
{
	/* 串口寄存器的配置 */
	 PCON &= 0x3F;	//PCON = 00xx xxxx
	 SCON &= 0x0F;
	 SCON |= 0x50;	//SCON = 0101 xxxx
	
	/* 配置T1的寄存器 */
	TMOD &= 0x0F;
	TMOD |= 0x20;	//TOMD = 0010 xxxx:8位自动重装载
	TH1 = 256 - (28800/Baud);//配置波特率
	TL1 = 256 - (28800/Baud);//配置波特率
	ET1 = 0;		//关闭T1溢出中断
	TR1 = 1;		//使能T1
}

/**
 * 发送一个字节的数据
 */
void Send_Char(unsigned char ch)
{
	SBUF = ch;	//给SBUF写入一个字节数据
	while(!TI);	//等待数据发送完成(未完成TI = 0,完成TI = 1)
	TI = 0;		//手动清0
}

/**
 * 发送多个字节的数据
 */
void Send_Array(unsigned char* Array,unsigned char Len)
{
	unsigned char i;
	for(i = 0; i<Len; i++)
	{
		Send_Char(*(Array+i));
	}
}

/**
 * 发送字符串
 */
void Send_String(unsigned char* str)
{
	while(*str != '\0')
	{
		Send_Char(*str++);
	}
}

/**
 * 对printf函数进行重定向
 */
char putchar(char ch)
{
	Send_Char(ch);
    return ch;
}

②UART.h文件的代码如下:

#ifndef __UART_H
#define __UART_H
#include <REGX52.H>	//包含51头文件,里面全是寄存器地址
#include <stdio.h>

void UART_Init(unsigned int Baud);
void Send_Char(unsigned char ch);//发送一个字节的数据
void Send_Array(unsigned char* Array,unsigned char Len);//发送多个字节的数据
void Send_String(unsigned char* str);//发送字符串
char putchar(char ch);//printf()重定向

#endif

③mian.c文件的代码如下:

#include "UART.h"

void main(void)
{
	unsigned char Data = 0;		//一个字节的数据缓冲区
	UART_Init(9600);			//9600波特率
	
	while(1)
	{
		if(RI)					//RI = 1,SBUF里面接收到数据
		{
			Data = SBUF;		//对数据进行处理
			RI = 0;
			Send_Char(Data);	//将数据发送出去
		}
	}
}

在这里插入图片描述

3.2.2、串口中断接收

实验要求: 使用中断方式接收一个字节数据

复制工程22_UART_Send_Receive,粘贴改名为:23_UART_Send_Receive_Interrupt

①UART.c文件的代码如下:

#include "UART.h"

/**
 * 串口初始化
 * 参数:波特率
 */
void UART_Init(unsigned int Baud)
{
	/* 串口寄存器的配置 */
	 PCON &= 0x3F;	//PCON = 00xx xxxx
	 SCON &= 0x0F;
	 SCON |= 0x50;	//SCON = 0101 xxxx
	
	/* 配置T1的寄存器 */
	TMOD &= 0x0F;
	TMOD |= 0x20;	//TOMD = 0010 xxxx:8位自动重装载
	TH1 = 256 - (28800/Baud);//配置波特率
	TL1 = 256 - (28800/Baud);//配置波特率
	ET1 = 0;		//关闭T1溢出中断
	TR1 = 1;		//使能T1
	
	/* 使能串口中断 */
	ES = 1;			//使能串口中断
	EA = 1;			//使能中断总开关
}

/**
 * 发送一个字节的数据
 */
void Send_Char(unsigned char ch)
{
	SBUF = ch;	//给SBUF写入一个字节数据
	while(!TI);	//等待数据发送完成(未完成TI = 0,完成TI = 1)
	TI = 0;		//手动清0
}

/**
 * 发送多个字节的数据
 */
void Send_Array(unsigned char* Array,unsigned char Len)
{
	unsigned char i;
	for(i = 0; i<Len; i++)
	{
		Send_Char(*(Array+i));
	}
}

/**
 * 发送字符串
 */
void Send_String(unsigned char* str)
{
	while(*str != '\0')
	{
		Send_Char(*str++);
	}
}

/**
 * 对printf函数进行重定向
 */
char putchar(char ch)
{
	Send_Char(ch);
    return ch;
}


/***********中断服务函数*************/
/**
 * 中断服务函数
 */
unsigned char Receive_Data = 0;
unsigned char Receive_Flag = 0;
void UART_Routine(void) interrupt 4
{
	/* 若是发送完成中断:TI = 1*/
	if(TI)
	{
		//一般情况下不使用你中断进行发送数据
	}
	
	/* 若接收数据完成中断 RI = 1*/
	if(RI)
	{
		RI = 0;
		Receive_Data = SBUF;//对数据进行处理
		Receive_Flag = 1;	//标志位置1
	}
}

②UART.h文件的代码如下:

#ifndef __UART_H
#define __UART_H
#include <REGX52.H>	//包含51头文件,里面全是寄存器地址
#include <stdio.h>

void UART_Init(unsigned int Baud);
void Send_Char(unsigned char ch);//发送一个字节的数据
void Send_Array(unsigned char* Array,unsigned char Len);//发送多个字节的数据
void Send_String(unsigned char* str);//发送字符串
char putchar(char ch);//printf()重定向

extern unsigned char Receive_Data;
extern unsigned char Receive_Flag;

#endif

③mian.c文件的代码如下:

#include "UART.h"

void main(void)
{
	UART_Init(9600);			//9600波特率
	
	while(1)
	{
		if(Receive_Flag)			//Receive_Flag = 1,数据处理完成
		{
			Receive_Flag = 0;
			Send_Char(Receive_Data);//将数据发送出去
		}
	}
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值