当前位置:   article > 正文

51单片机:串口通信_51单片机串口通信的接收与发送

51单片机串口通信的接收与发送


前言

51单片机串口通信原理及代码,实现单片机与PC的交互

一、前置知识了解

数据传输方式分类:

  1. 串行通信:使用一条数据线,将数据一位一位传输,每个数据占据一个固定的时间长度。特点:传输线少,长时间传输成本低,但传输控制比并行复杂。
  2. 并行通信:将数据字节的各位用多条数据线同时进行传输。特点:控制简单,速度快,但长距离传输时间成本高且接受方接受困难,扛干扰能力差。

数据同步方式分类

  1. 异步通信: 发送与接受设备使用各自的时钟控制数据的发送和接受过程。双方时钟不一定一致,但通信时钟应尽可能保持一致。特点: 不要求双方时钟严格一致,实现简单,设备开销小,但传输效率低。
  2. 同步通信: 要建立发送和接受双方的时钟直接控制,使双方达到完全同步。分为外同步和自同步。特点: 双方时钟应严格一致,控制复杂,开销大,但传输效率高。

数据传输方向分类

  1. 单工通信: 数据传输只能沿一个方向
  2. 半双工通信: 数据可双向传输,但须分时进行
  3. 全双工通信: 数据可以同时双向传输。

通信速率

  1. 比特率: 每秒钟传输二进制代码的位数,单位:位/秒。如:每秒传输240字符,每个字符含10位,则其比特率为:2400bps
  2. 波特率: 当定义若干位为一个码元,以2400bps为例,定义每四位为一码元,则其波特率为 2400 / 4 = 600 2400/4=600 2400/4=600

二、51单片机寄存器

1. 串口控制寄存器SCON

76543210
字节地址:98HSM0SM1SM2RENTB8RB8TIRISCON

REN: 允许串行接收位。 R E N = 1 REN=1 REN=1启用串口接收数据,反之禁止。

RB8: 在方式2或3中(见下表),为接收数据的第9位,作为奇偶校验位或地址帧/数据帧的标志位。在方式1时,若 S M 2 = 0 SM2=0 SM2=0,则RB8是接收到的停止位。

RI: 接收中断标志位。 在方式0中,接收到第8位数据结束时,在其他方式中,接收到停止位时,硬件置1,发出中断申请,须在中断程序中软件清0,取消此中断。

SM2: 多机通信控制位,主要用于方式2和3(见下表)。 S M 2 = 1 SM2=1 SM2=1时,启用多机通信;接收的第九位数据赋值给RB8,当 R B 8 = 0 RB8=0 RB8=0,不激活RI(即 R I = 0 RI=0 RI=0),收到的数据丢弃;当 R B 8 = 1 RB8=1 RB8=1,激活RI(即 R I RI RI硬件置1),产生中断从SBUF中读取数据。当 S M 2 = 0 SM2=0 SM2=0,不管RB8为0或1,均可读取其中数据。

TB8: 方式0和1中未用到。在方式2和3中,是发送数据的第9位,可以软件定义其作用,作为校验位或标志位。

TI: 发送中断标志位。 在方式0中,发送第8位数据结束时,在其他方式中,发送停止位时,硬件置1,发出中断申请,须在中断程序中软件清0,取消此中断。

SM0和SM1为工作方式选择位

SM0SM1方式说明波特率
000移位寄存器 f o s c / 12 f_{osc}/12 fosc/12
01110位异步收发器(8位数据)可变
10211位一步收发器(9位数据) f o s c / 64 f_{osc}/64 fosc/64 f o s c / 32 f_{osc}/32 fosc/32
11311位异步收发器(9位数据)可变

f o s c f_{osc} fosc为外部晶振频率

2. 电源控制寄存器PCON

76543210
字节地址:97HSMODPCON

SMOD: 波特率倍增位。 在串口方式1、2、3(见工作方式选择表)时,波特率和SMOD有关。 S M O D = 1 SMOD=1 SMOD=1,波特率提高一倍,复位时置0。

如何理解波特率,及其倍增位,以全双工异步通信为例,如下图所示。
在这里插入图片描述
图中结构可以看出为全双工通信,如果为异步通信(即接收端与发送端双方的时钟由各自控制),为了保证设备正常工作,需要尽可能让双方时钟保持一致;如果为同步通信,则发送端除了控制自己的时钟,同时还要控制接收端的时钟,使双方时钟保持一致。
在这里插入图片描述
举例说明,当发送端与接收端同步时(即双方波特率比值为1:1),发送端发送一个a,接收端接收一个a。

当发送端与接收端时钟不同步时(例如双方波特率比值为2:1)。发送端发送一个a,接收端接收到两个a。这就是双方时钟不一致(即波特率不同)产生的现象。

无论同步通信还是异步通信都要使双方时钟保持一致,区别只在于同步时钟是由谁控制。至于为什么要进行波特率倍增,大概可能估计是由于硬件限制,考虑到最大波特率也无法进行正常通信的情况。

三、工作方式选择

  1. 接收到的数据和需要发送的数据存储在两段相互独立的内存中,但两段内存对外都命名为SBUF,因此对发送数据和接收数据的处理只需对SBUF处理即可。
  2. 无论是工作在哪种工作模式,在输入数据到单片机时,均须使SCON寄存器中的REN置1。
  3. 除方式0外,其余RXD均为数据接收引脚,TXD均为数据发送引脚

方式0

串口为同步移位寄存器的输入输出方式。主要用于扩展并行输入或输出口。数据由RXD(P3.0)引脚输入或输出,同步位移脉冲由TXD(P3.1)引脚输出。例如外部设备须并行输出数据到单片机,但单片机没有多余的引脚,则可以使用该引脚通信功能,与74HC595扩展的区别在于需要使用TXD引脚同步双方时钟。发送和接收均为8位数据,从低至高位依次发送。

输出时序图


先将数据写入SBUF,从低至高依次输出数据,在第八位结束时,硬件置TI=1(详见SCON中TI的描述),产生中断结束发送数据,后续还需软件置0。

输入时序图

在这里插入图片描述
输入前先将输入中断RI软件置0,输入完毕后RI硬件置1(详见SCON中RI描述),产生中断,结束输入。

方式1

一帧数据由1位起始位,8位数据位,1位停止位组成。

输出时序图

在这里插入图片描述
接收到停止位时,TI硬件置1(详见SCON中TI描述),产生中断,结束输出。

输入时序图


接收到起始位开始输入,至接收到停止位时,前八数据为存入SBUF,停止位进入RB8,RI硬件置1,产生中断,结束输入。

上述为SM2=1,多机通信中的场景。在SM2=0,单机通信中,接收到停止位即产生中断,结束输入。

方式2、3

数据格式
在这里插入图片描述
当输出时,数据第九位为TB8,为输出的标志位或校验位。
当输入时,数据第九位位RB8,为输入的标志位或校验位。

输出时序图

在这里插入图片描述

在这里插入图片描述
如上图所示,当移位寄存器检测到上种情况时,TI硬件置1,产生中断终止输出。

输入时序图

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

四、串口通信代码

波特率计算使用提供的波特率计算软件即可。电路图不太能看懂就省略了。
功能:

  1. 由于显示设备限制,将PC发送的数据返回给PC以代替单片机上的数据显示。
  2. 通过按键矩阵,可输入a~p字符,按下独立按键K3即可将单片机的数据发送给PC。

这是简单的设备之间的通信,后续单片机如果有合适的显示设备,或许会完善使用按键矩阵和独立按键实现拼音9键,实现单片机和PC的英文交互。

时延函数 delay.h

#ifndef _DELAY_H__
#define _DELAY_H__
/*10us=1,时延为10us*/
void delay_10us(unsigned int _10us){
	while(_10us--){}
}
/*1ms=1,时延为1ms*/
void delay_1ms(unsigned int _1ms){
	 _1ms*=110;
	 while(_1ms--){
	 }
}
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

中断初始化函数 interrupt_utils.h

#ifndef _INTERRUPT_H_
#define _INTERRUPT_H_
#include "reg52.h"
/*中断初始化*/
/*外部中断0*/
void Int0_Init(const unsigned char *mode){//外部中断0的触发方式,0低电平触发,1下降沿触发
	EA=1;//总中断允许位
	EX0=1;//外部中断0允许位
	IT0=*mode;	
}
/*外部中断1*/
void Int1_Init(const unsigned char *mode){//外部中断1的触发方式,0低电平触发,1下降沿触发
	EA=1;//总中断允许位
	EX1=1;//外部中断0允许位
	IT1=*mode;	
}
void Timer0_Init(const unsigned char *mode ,const unsigned char *HighVal , const unsigned char *LowVal ){
	EA=1;//打开中断总允许位
	ET0=1;//打开T0中断允许位
	TR0=1;//定时/计数器中断0开始工作
	TMOD |= *mode;//设置T0工作模式
	TH0 = *HighVal; //高八位寄存器初值
	TL0 = *LowVal;  //第八位寄存器初值
} 
void Timer1_Init(const unsigned char *mode ,const unsigned char *HighVal , const unsigned char *LowVal ){
	EA=1;//打开中断总允许位
	ET1=1;//打开T0中断允许位
	TR1=1;//定时/计数器中断0开始工作
	TMOD |= *mode;//设置T0工作模式
	TH1 = *HighVal; //高八位寄存器初值
	TL1 = *LowVal;  //第八位寄存器初值
} 
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

串口初始化及相关功能函数 uart_init.h

#ifndef __UART_INIT_H__
#define __UART_INIT_H__
#include "interrupt_utils.h"
unsigned char data_buffer[10];//存储接收的数据
unsigned char Send_buffer[10];//要发送的数据
unsigned char Num=0;
unsigned char Send_Num=0;
/*设置串口寄存器参数
		1.设置串口寄存器
		2.设置波特率参数
*/
void Set_Uart( unsigned char *Uart_Reg_mode ,  unsigned char *Baud_mode){
	SCON = *Uart_Reg_mode;
	PCON |= *Baud_mode;
}
/*串口初始化函数*/
void Uart_Init(unsigned char T1_Mode , unsigned char Uart_Reg_mode , 
				 unsigned char Baud_mode ,  unsigned char Init_Val ){
	  
	  Set_Uart(&Uart_Reg_mode,  &Baud_mode);
	  Timer1_Init(&T1_Mode ,&Init_Val , &Init_Val );
	  ES=1;	//打开串口中断允许位
	  ET1=0;//关闭定时器中断1的允许位	  
}
/*串口发送一个字符*/
void Uart_Send_byte(unsigned char dat){
	SBUF = dat;
	while(!TI);//发出中断,TI硬件置1,跳出循环
	TI=0;//重置,取消本次中断	
	
}
/*串口发送一串字符*/
void Uart_Send_String(){
	unsigned char i;
	for(i=0 ; i!=Send_Num;++i){
		Uart_Send_byte(Send_buffer[i]);
	}
	Send_Num=0;
}
/*串口接受数据(不超过10位)*/
void UART_Routine( ) interrupt 4{  
  	RI=0;		//将接收中断标志清0 
	if(Num>9){	 //超出缓存大小,覆盖从头开始存储
		Num=0;
	}	
	data_buffer[Num++] = SBUF;		
	/*由于单片机暂无合适的显示设备,将接收到的数据返回给电脑*/
	Uart_Send_byte(data_buffer[Num-1]);
}
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

矩阵按键扫描函数 scan_button.h

#ifndef __SCAN_BUTTON_H__
#define __SCAN_BUTTON_H__
/*扫描矩阵按键*/
#include "reg52.h"
#include "delay.h"
typedef unsigned int uint;
typedef unsigned char uchar;

sbit line0 = P1^7;
sbit line1 = P1^6;
sbit line2 = P1^5;
sbit line3 = P1^4;
sbit col0 = P1^3;
sbit col1 = P1^2;
sbit col2 = P1^1;
sbit col3 = P1^0;

char  Table16[4][4] = {{'a','b','c','d'},
						{'e','f','g','h'},
						{'i','j','k','l'},
						{'m','n','o','p'}
						} ;
/*确定行*/
uint get_line(void){
	uint retVal=4;
	col0=0;
	col1=0;
	col2=0;
	col3=0;
	if(!line0) retVal=0;
	if(!line1) retVal=1;
	if(!line2) retVal=2;
	if(!line3) retVal=3;
	col0=1;
	col1=1;
	col2=1;
	col3=1;
	return retVal;
}
/*确定列*/
uint get_col(void){
	uint retVal=4;
	line0=0;
	line1=0;
	line2=0;
	line3=0;
	if(!col0) retVal=0;
	if(!col1) retVal=1;
	if(!col2) retVal=2;
	if(!col3) retVal=3;
	line0=1;
	line1=1;
	line2=1;
	line3=1;
	return retVal;
}
/*监测按键,并返回按键值*/
char Scan_Button(){
	uint row;
	uint col;
	delay_10us(6000);
	row = get_line();
	col = get_col();
	delay_10us(6000);
	if(row!=4&&col!=4)
		return Table16[row][col];
	return 0;
}	
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69

主函数

#include "Uart_Init.h"
#include "Scan_Button.h"
/*
	1.由于暂时无合适的显示设备,所以将单片机接收到的信息返回给电脑显示。
	2.可以通过矩阵键盘输入信息,按独立按键k3进行发送
*/
void main(){
   Int0_Init(0);//外部中断0初始化
   Uart_Init(0x20,0x50,0x80,0xFA);//串口中断初始化
   while(1){
	  char cha;
	  cha=Scan_Button();
	  if(cha)
	  	Send_buffer[Send_Num++] = cha;
   }
}
void Int0_Rountine() interrupt 0{ //按键矩阵输入完毕,按独立按键K3中断发送
	Uart_Send_String();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/神奇cpp/article/detail/855123
推荐阅读
相关标签
  

闽ICP备14008679号