51单片机串口通讯原理深入理解及程序源码,使用单片机IO模拟串口功能扩展串口-----day8

本文详细介绍了51单片机的串口通讯原理,包括电平分类如TTL、CMOS和RS232,以及电平转换芯片如CH340T、MAX232的使用。此外,讨论了串行通信与并行通信的区别,UART的工作模式,以及波特率的计算。还提供了UART初始化和数据发送的示例代码,强调了波特率加倍和数据采样的重要性。
摘要由CSDN通过智能技术生成

51单片机串口通讯原理及程序源码-----day8
1.电平分类:
1、 TTL: Vcc: 5V;
2、 LVTTL : Vcc: 3.3V等
3、 CMOS: Vcc: 5V;
4、 LVCMOS : Vcc: 3.3V等
5、 RS232标准: 1: -3V ~ -15V 0 : 3V ~ 15V
所以计算机与单片机之间通讯时需要加电平转换芯片CH340T 、 MAX232。
MAX232 供电电压5V
MAX3232 供电电压3.3V
在这里插入图片描述
在这里插入图片描述

DB9实物介绍:
在这里插入图片描述
引脚:
1、 载波检测(DCD)
2、 接收数据(RXD)
3、 发送数据(TXD)
4、 数据终端准备好(DTR)
5、 信号地线(SG)
6、 数据准备好(DSR)
7、 请求发送(RTS)
8、 清除发送(CTS)
9、 振铃提示(RI)

2.通信分类:
(1)并行通信通常是将数据字节的各位用多条数据线同时进行传送 。
并行通信控制简单、 传输速度快; 由于传输线较多, 长距离传送时成本高且接收方的各位同时接收存在困难
在这里插入图片描述
(2)串行通信是将数据字节分成一位一位的形式在一条传输线上逐个地传送。串行通信的特点: 传输线少, 长距离传送时成本低, 且可以利用电话网等现成的设备, 但数据的传送控制比并行通信复杂。
在这里插入图片描述
异步通信的特点: 不要求收发双方时钟的严格一致, 实现容易, 设备开销较小, 但每个字符要附加2~3位用于起止位, 各帧之间还有间隔, 因此传输效率不高。
在这里插入图片描述
2.UART的四种模式:
模式0:
模式1: 以TXD为例, 平时没数据时TXD为高电平, 来了数据, 一位起始位0, 八位数据位( 一个字节) , 一位停止位1。
模式2和3: 原理和模式1一样, 只是添加了一位奇偶校验位( 防止通信出错) 。,奇校验在D7后补充一位0或者1根据前面1的个数来补充,如果是奇校验且D0-D7之间1的个数为偶数则补1,如果是偶校验且D0-D7之间个数为奇数则补1.

3.单位: 波特率 = 位/字符× 字符/秒 = 位/秒。 波特率: 就是发送一位数据的速率, 即发送一个数据的持续时间 = 1/baud;
常用串口波特率:
300、 600、 1200、 2400、 4800、 9600、19200 ……115200;
SBUF:串口数据缓冲寄存器, 发送数据时, 只要将数据送入SBUF, 则单片机自动发送数据,接收数据时, 自动将数据接收至SBUF。
SCON串口控制寄存器
在这里插入图片描述
SM1与SM2为模式0时,波特率为时钟频率/12,比特率固定。
模式2与模式3时9位数据多一位奇偶校验位。
模式1为串口常用模式。
SM2: 使能模式2和3中的多机通信功能。 通常不使用。
REN使能串口接收。 由软件置1, 则允许串口接收数据; 由软件清零, 则禁止串口接收数据。
TB8,RB8: 方式2和方式3中的校验位, 分为四种方式A.偶校验;B.奇校验;C.强制为0;D.强制为1 。
TI: 发送中断标志, 在发送停止位时由硬件置1。必须通过软件才能清零。
RI: 接收中断标志, 接收停止位的中间时刻由硬件置1, 必须通过软件清零。
定时器TMOD:
模式2: 自动装载8位计数器。 主要应用在串口波特率发生器
在这里插入图片描述
SMOD:波特率选择位。当用软件置位SMOD,即SMOD=1,则使串行通信方式1、2、3的波特率加倍;SMOD=0,则各工作方式的波特率不加倍。复位时SMOD=0。
一般选择不加倍,所以SMOD为0,SYSclk是单片机时钟,也就是晶振的频率,11.0592MHz,运算时要转化为基本单位Hz,即11059200Hz
定时器工作模式是8位自动重装载,TH1和TL1赋的初值一样。
定时器1使用自动重装模式, 即模式2。8位自动重装载,所以使用的是2^8=256
TH1 = TL1 = 256 - 11059200/(12 * 32 *9600)
TH1=TL1=0xFD;
实现步骤:
1、 将定时器1置为自动重装模式。
2、 将串口设置为方式1。
3、 根据公式计算出定时器1的初值。
4、 打开定时器1, 打开串口允许接受。
发送字符:

uint8 Buf[]="how are you!\n";
 
 void delay(uint16 n)
{
	while (n--);
}

/*
 * UART初始化
 * 波特率:9600
*/
void UART_init(void)
{
    SCON = 0x50;        // 10位uart,允许串行接受

    TMOD = 0x20;        // 定时器1工作在方式2(自动重装)
    TH1 = 0xFD;
    TL1 = 0xFD;

    TR1 = 1;
}

/*
 * UART 发送一字节
*/
void UART_send_byte(uint8 dat)
{
	SBUF = dat;
	while (TI == 0);
	TI = 0;
}

/*
 * UART 发送字符串
*/
void UART_send_string(uint8 *buf)
{
	while (*buf != '\0')
	{
		UART_send_byte(*buf++);
	}
}

void main()
{
	UART_init();	
	while (1)
	{
		UART_send_string(Buf);
		delay(20000);
	}

}

void main()
{
    unsigned char i;
    EA = 1;            //使能总中断
    ConfigTimer0(1);   //配置T0定时1ms
    ConfigUART(9600);  //配置波特率为9600
    
    while (1)
    {  
	    //将接收字节在数码管上以十六进制形式显示出来
	    disbuf[0] = ucDataOneTab[RxdByte >> 4];
        disbuf[1] = ucDataOneTab[RxdByte & 0x0F];
       
		for (i = 0; i < 8; i++ )
	    {
           SendData(disbuf[i], ucDataTwoTab[i]);   
		   Delay1ms(1);
        }	  
    }
}
/* 串口配置函数,baud-通信波特率 */
void ConfigUART(unsigned int baud)
{
    SCON  = 0x50;  //配置串口为模式1
    TMOD &= 0x0F;  //清零T1的控制位
    TMOD |= 0x20;  //配置T1为模式2
    TH1 = 256 - (11059200/12/32)/baud;  //计算T1重载值
    TL1 = TH1;     //初值等于重载值
    ET1 = 0;       //禁止T1中断
    ES  = 1;       //使能串口中断
    TR1 = 1;       //启动T1
}
/* UART中断服务函数 */
void InterruptUART() interrupt 4
{
    if (RI)  //接收到字节
    {
        RI = 0;          //手动清零接收中断标志位
        RxdByte = SBUF;  //接收到的数据保存到接收字节变量中
        SBUF = (RxdByte>>0x04)+(RxdByte&0x0f);  //接收到的数据的高位低位相加返回
                         //用以提示用户输入的信息是否已正确接收
    }
    if (TI)  //字节发送完毕
    {
        TI = 0;  //手动清零发送中断标志位
    }
}

超级简单,不扩展了。。。。
ASCII表:
在这里插入图片描述
知识点一:
PCON只有第七位与串口通信波特率有关, 置1可以加倍串口通信波特率。 PCON |= 0x80; ====> 9600*2=19200
知识点二:
关于UART数据读取的知识点: CPU读取RXD数据时, 采样(读取数据)速度为波特率的16倍。 在第7、 8、 9计数状态时, 采样此时的RXD数值, 取值为三个采样值的多数(即至少2次)作为读取的数值。 波特率9600为例:
7、8、9中有2个1则为高电平,有两个0则为低电平。
在这里插入图片描述
对于起始位来说, 下跳沿开始读数, 但会通过7、 8、 9的采样来确定这个数是不是0, 如果不是, 则复位接收电路。
在这里插入图片描述
用单片机IO口模拟串口
如果需要两个串口, 但我们知道一般的51系列D单片机只提供一个串口, 那么另一个串口只能靠
普通IO口模拟。
置1或0分别代表高低电平, 也就是串口通信中所说的, 如起始位用低电平, 则将其置0停止位为高电平, 则将其置1, 各种数据位则根据情况置1或置0。

在这里插入图片描述
设波特率为9600,则发送一位bit数据需要1/9600=104us
设计思路:数据发送时使用定时器判断IO的电平翻转。
1/9600 = (65536-x)12/11059200
x =65536 -(11059200/9600
12)
x = 65440 = 0xFFA0
实测在140us需要根据实际进行调节。
在这里插入图片描述
实际调整完后X=0xFFBB接近于104us
在这里插入图片描述
//发送部分

#include <reg52.h>
#define uchar unsigned char  
#define uint unsigned int 

uchar Send_Value[1] = {0x59};  

void Send_Data(bit i)
{
	 if(i != 0) TXD = 1;	
	 else TXD = 0;
	 TR0 = 1;			//开启定时
	 while(!TF0);		//判断是否到达发送结束时间104us
	 TR0 = 0;			//停止定时器0计数
	 TH0 = 0xFF;		//重赋初值
	 TL0 = 0xBB;
	 TF0 = 0;			//清定时器溢出中断
}


main()
{
	  uchar a = 0;
	  uchar i;
	  uint j;
	  TMOD |= 0x01;   
	  TH0 = 0xFF;  //定时初值
	  TL0 = 0xBB;
	  TXD = 1;		//将发送端置高
	  while(1)
	  {
		  TF0 = 0;//清定时器溢出
		  Send_Data(0);//发送起始位
		  a = Send_Value[0];
		  for(i=0;i<8;i++)	//依次发送8位数据
		  {
		  	 Send_Data((bit)(a&0x01));//强制类型转换先发送最低位
		  	 a = a>>1;					//向右移动一位
		  }
		  Send_Data(1);				 //停止位
		  for(j=0;j<50000;j++);		//延时发送
		  
 	  }
}

接收部分:
串口发送数据时起始位为低电平。
源代码部分:
接收一个ASCII ,将接收的数据再发送出去。

#include <reg52.h>

typedef unsigned char uint8;
typedef unsigned int uint16;

sbit RXD2 = P3^0;
sbit TXD2 = P3^1;

#define MCLK		11059200UL
#define BAUD_RATE	9600UL

#define WAIT_TIME()	
do
{
   while(!TF0);
   TF0 = 0;
}while(0)


/*
 * 定时器初始化
*/
void timer0_init(void)
{
	TMOD &= 0xF0;
	TMOD |= 0x02;                //定时器0,方式2
	TH0 = 256 - MCLK/(12*BAUD_RATE);
	TL0 = TH0;
	TF0 = 0;
	TR0 = 0;
}

/*
 * UART发送
*/
void UART_send_byte(uint8 dat)
{
	uint8 len=8;
	
	// 1. start bit
	TL0 = TH0;
	TR0 = 1;
	TXD2 = 0;
	WAIT_TIME();
	// 2. DAT_LEN bit data
	while (len--)
	{
		TXD2 = (bit)(dat & 0x01);     //先发送低位
		WAIT_TIME();
		dat = dat>>1;
	}
	// 3. stop bit
	TXD2 = 1;
	WAIT_TIME();
	TR0=0;
}

/*
 * UART接收
*/
uint8 UART_rev_byte(void)
{
    uint8 len=8, dat=0;

	while (RXD2);

	// 1. jump start bit
	TR0 = 1;
	TL0 = 256 - MCLK/(12*BAUD_RATE)/2; //调到起始位的中间7/8/9位进行采集 1/2的9600 19200
    WAIT_TIME();	   //起始位结束
	WAIT_TIME();  	  //调到起始位的中间7/8/9位进行采集

    // 2. receive DAT_LEN bit data
    while (len--)
    {
        dat >>= 1;
        if (RXD2) 
			dat  |= 0x80;
		WAIT_TIME();
    }

	// 3. wait stop bit
	WAIT_TIME();
	TR0 = 0; 

    return dat;
}



main()
{
	uint8 a;

	timer0_init();

	while (1)
	{

		UART_send_byte('i');
		UART_send_byte('n');
		UART_send_byte('p');
		UART_send_byte('u');
		UART_send_byte('t');
		UART_send_byte(':');

		a = UART_rev_byte();
		UART_send_byte(a);
		
		UART_send_byte('\r');
		UART_send_byte('\n');
	}
}
外部中断模拟串口,波特率不能超过65536 实验测试:发送57600可以正常,但接收只能 <= 38400 #include "Uart_EXT0.h" #include "MAIN.h" #define FOCS 22114800ul bit Over; bit bRxflag; unsigned char IEN0_NOW,IEN1_NOW; //中断临时变量 unsigned char idata bRxstate=0; //接收状态 unsigned char idata tmp_Len=0; //缓存数组下标 unsigned char idata bRxlen; //接收字节数 unsigned char xdata EX_buf[64]; //接收存放区 /*************************************************** baud = 56000 接收一字节 =178.6us ,接收会出现错误 baud = 38400 接收一字节 =260.4us //快速接收都容易出现错误 baud = 19200 接收一字节 =520us | baud = 14400 接收一字节 =694.5us | baud = 9600 接收一字节 =1041.6us | baud = 4800 接收一字节 =2.083ms 接收过长,定时器MODE2无法满足 //主机发送的数据最好加上校验字 发送:最大57600,发送还能正常。 ****************************************************/ void Time0_Uart(unsigned int baud) { TMOD &= 0XF0; TMOD |= 0X02; //使用定时器0方式2,使用方式1则2400波特也能有 TH0 = 256-(FOCS/12)/baud; TL0 = TH0; IP0 |= 0x02; //设置为最高优先级 IP1 |= 0x02; TR0 = 1; ET0 = 1; EA = 1; } void Time0_isr() interrupt 1 { //方式1则重装值 Over = 1; } /* 发送数据,1起始位,8数据,1停止位 发送波特率实验57600都不会错误,大量发送数据待测试 */ void Uart_set(unsigned char dat) { unsigned char i; Over = 0; TL0 = TH0; //防止发送数据开始时不知道TL0是多少 T_uart = 0; //起始位 while(Over==0); Over = 0; for(i=0;i>= 1; } // Over = 0; //数据位清零了 T_uart = 1; //停止位,此后如果没有数据则一直为高 while(Over==0); } unsigned char get_Uart() { unsigned char I=0,get_dat=0; Over = 0; TL0 = TH0; //重新赋值,防止出错 while(Over==0); Over = 0; for(I=0;I>= 1; if(Get_ex == 1) get_dat |= 0x80; else get_dat &= 0x7f; while(Over==0); Over = 0; } if(Get_ex == 1) { Over = 0; } return get_dat; } void EXuart_isr() interrupt 0 { unsigned char _chr; IEN0_NOW = IEN0; //进入接收1字节,关闭中断 IEN1_NOW = IEN1; IEN0 = 0X82; IEN1 = 0; _chr=get_Uart(); switch(bRxstate) { case 0: if(_chr==0x55) { bRxstate=1; } else bRxstate=0; break; case 1: if(_chr==0xaa) { bRxstate=2; } else bRxstate=0; break; case 2: //取得数据长度 bRxlen=_chr; bRxstate=3; break; case 3: EX_buf[tmp_Len]=_chr; tmp_Len++; if(tmp_Len==bRxlen) { bRxstate=0; tmp_Len=0; bRxflag=1; //一帧数据接收完毕,置位完成标志位 } break; default: break; } IE0 = 0; //清外部0中断标志,防止下次接收不到 IEN0 = IEN0_NOW; IEN1 = IEN1_NOW; } void main() { Time0_Uart(9600); EX_uart(); if(bRxflag==1) { bRxflag = 0; for(i=0;i<10;i++) RevBuffer[i] = EX_buf[i]; T0Uart_TX(10,RevBuffer); } }
很高兴为您解答关于51单片机串口通信的问题。下面是关于51单片机串口通信模板的详细介绍以及通信实例。 1. 51单片机串口通信模板 51单片机与外部设备进行串口通信,需要设置串口的一些参数,包括波特率、数据位、校验位和停止位等。以下是51单片机串口通信模板的代码: ```c #include <reg52.h> #include <stdio.h> #define FOSC 11059200L #define BAUD 9600 typedef unsigned char BYTE; typedef unsigned int WORD; sbit LED = P1^0; void UART_Init(); void SendData(BYTE dat); void SendString(char *s); void main() { UART_Init(); while(1) { LED = ~LED; SendString("Hello, world!\r\n"); delay(1000); } } void UART_Init() { TMOD &= 0x0F; TMOD |= 0x20; TH1 = TL1 = -(FOSC/12/32/BAUD); TR1 = 1; SCON = 0x50; ES = 1; EA = 1; } void SendData(BYTE dat) { SBUF = dat; while(TI == 0); TI = 0; } void SendString(char *s) { while(*s) { SendData(*s++); } } ``` 在上面的代码中,我们定义了一个LED灯,在主函数中通过串口发送“Hello, world!”字符串,并控制LED灯闪烁。在UART_Init()函数中,我们设置了串口的一些参数,包括波特率、数据位、校验位和停止位等。 2. 51单片机串口通信实例 以下是一个简单的51单片机串口通信实例,它通过串口接收PC机发送的数据,并将其原封不动地发送回去: ```c #include <reg52.h> #include <stdio.h> #define FOSC 11059200L #define BAUD 9600 typedef unsigned char BYTE; typedef unsigned int WORD; sbit LED = P1^0; void UART_Init(); void SendData(BYTE dat); void SendString(char *s); void main() { BYTE c; UART_Init(); while(1) { if(RI) { c = SBUF; RI = 0; SendData(c); LED = ~LED; } } } void UART_Init() { TMOD &= 0x0F; TMOD |= 0x20; TH1 = TL1 = -(FOSC/12/32/BAUD); TR1 = 1; SCON = 0x50; ES = 1; EA = 1; } void SendData(BYTE dat) { SBUF = dat; while(TI == 0); TI = 0; } void SendString(char *s) { while(*s) { SendData(*s++); } } ``` 在上面的代码中,我们定义了一个LED灯,在主函数中通过串口接收PC机发送的数据,并将其原封不动地发送回去。同时,每当接收到数据时,LED灯会闪烁一次。 以上就是关于51单片机串口通信模板和通信实例的介绍,希望能对您有所帮助。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

从小白到大师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值