单片机串口通讯

介绍

串口是一种应用十分广泛的通讯接口,串口成本低、容易使用、通信线路简单,可实现两个设备的互相通信。
单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信,极大的扩展了单片机的应用范围,增强了单片机系统的硬件实力。
51 单片机内部自带 UART Universal Asynchronous Receiver Transmitter ,通用异步收发器),可实现单片机的串口通信。

我们软件里自带有串口助手 ,一定要注意串口必须和我们电脑连接单片机的串口相同

 硬件电路

简单双向串口通信有两根通信线(发送端 TXD 和接收端 RXD
TXD RXD 要交叉连接
当只需单向的数据传输时,可以直接一根通信线
当电平标准不一致时,需要加电平转换芯片

电平标准

电平标准是数据 1 和数据 0 的表达方式,是传输线缆中人为规定的电压与数据的对应关系,串口常用的电平标准有如下三种:
TTL 电平: +5V 表示 1 0V 表示 0
RS232 电平: -3~-15V 表示 1 +3~+15V 表示 0
RS485 电平:两线压差 +2~+6V 表示 1 -2~-6V 表示 0 (差分信号)

接口以及引脚定义

RTS、CTS,DRS--->流控制

常见通信接口比较

相关术语

全双工:通信双方可以在同一时刻互相传输数据
半双工:通信双方可以互相传输数据,但必须分时复用一根数据线
单工:通信只能有一方发送到另一方,不能反向传输
异步:通信双方各自约定通信速率
同步:通信双方靠一根时钟线来约定通信速率
总线:连接各个设备的数据传输线路(类似于一条马路,把路边各住户连接起来,使住户可以相互交流)

单片机中的UART

STC89C52 1 个UART
STC89C52 的UART有四种工作模式:

  模式0:同步移位寄存器

  模式18UART,波特率可变(常用)

  模式29UART,波特率固定

  模式39UART,波特率可变

 串口参数及时序图

波特率:串口通信的速率(发送和接收各数据位的间隔时间)
检验位:用于数据验证
停止位:用于数据帧间隔

从低位开始发

串口模式图

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

串口和中断系统

串口相关寄存器 

PCON的前两个寄存器是和串口有关的 

实现通过串口向计算机发送数据

要使用到Delay.c

先了解每一个寄存器所对应的功能(对照着手册),再进行配置:

SM0:我们使用的是八位寄存器,不需要帧错误检测, 不需要管;

SM0、SM1:我们需要用到常用的方式1,则SM0=0,SM1=1;

 SM2:不用管;

REN:需要单片机接受数据时给1,不需要就给0;

TB8、RB8:无关,不用管;

TI、RI:中断标志位,给0;

SM0,SM1,REN,TB8,RB8,Ti,RI=0100 0000=0x40

SCON = 0x40;

PCON = 0;

 此过程不需要配置中断,则不管它,默认是不开启

选择波特率

需要参考计时器模块的代码

    TMOD &= 0xF0;		
	TMOD |= 0x01;		
	TL0 = 0x66;		
	TH0 = 0xFC;		
	TF0 = 0;		
	TR0 = 1;		
    ET0 = 1;
	EA = 1;
	PT0 = 0;

串口只能使用定时器1,但是代码是配置的定时器0,所以需要稍加修改

而且串口传输速度很快,不能用先前的16位定时器模式(0~65535),得用更精准的双8位自动重装(0~255)

则M1、M0分别给1、0 ,则:

    TMOD &= 0x0F;		
	TMOD |= 0x20;

给TH0和TL0赋初值需要计算波特率,用到软件STC,设置如下

得到: 

void UartInit(void)        //4800bps@11.0592MHz
{
    PCON |= 0x80;        //使能波特率倍速位SMOD
    SCON = 0x50;        //8位数据,可变波特率
    AUXR &= 0xBF;        //定时器1时钟为Fosc/12,即12T
    AUXR &= 0xFE;        //串口1选择定时器1为波特率发生器
    TMOD &= 0x0F;        //清除定时器1模式位
    TMOD |= 0x20;        //设定定时器1为8位自动重装方式
    TL1 = 0xF4;        //设定定时初值
    TH1 = 0xF4;        //设定定时器重装值
    ET1 = 0;        //禁止定时器1中断
    TR1 = 1;        //启动定时器1
}

上下对比: 上(AUXR也需要删掉,因为SCT系列没有这个功能)

void UART_Init(void)
{
    SCON = 0x40;
    PCON =0;        //赋予了0x80

    TMOD &= 0xF0;		//0x0F
	TMOD |= 0x01;		//0x20,设置定时器模式
	TL0 = 0x66;		    //设定定时初值,计算波特率得来,0xF3
	TH0 = 0xFC;		    //0xF3
	TF0 = 0;		    //禁止定时器1中断,设为1且这句话以后的都删去(设定中断器的语句)
	TR0 = 1;		
    ET0 = 1;
	EA = 1;
	PT0 = 0;
}

得最终结果

void UART_Init(void)//4800bps@11.0592MHz
{
	SCON = 0x40;
	PCON |= 0x80;
	
	TMOD &= 0x0F;		
	TMOD |= 0x20;		//给定时器1设为8位自动重装方式
	TL1 = 0xF4;		 //设定定时初值
	TH1 = 0xF4;	     //设定定时器重装值
	ET1 = 0;		//禁止计时器1中断
	TR1 = 1; 		//启动定时器1
}

void main(void)
{
	UART_Init();//初始化串口和定时器1
	while(1)
	{
		
	}
}

此时,下载到单片机中是没有任何现象的
需要建立一个发送字节的函数

void UART_SendByte(unsigned char Byte)
{
	SBUF = Byte;
	while(TI==0);//检测是否完成发送
	TI = 0;		 //发送完成后立即复位
}

整体:

#include <REGX52.H>
#include "Delay.h"

void UART_Init(void)//4800bps@11.0592MHz
{
	SCON = 0x40;
	PCON |= 0x80;
	
	TMOD &= 0x0F;		
	TMOD |= 0x20;		
	TL1 = 0xF4;		
	TH1 = 0xF4;	
	ET1 = 0;		//½ûÖ¹¶¨Ê±Æ÷1ÖжÏ
	TR1 = 1; 		//Æô¶¯¶¨Ê±Æ÷1
}

void UART_SendByte(unsigned char Byte)
{
	SBUF = Byte;
	while(TI==0);//¼ì²âÊÇ·ñÍê³É·¢ËÍ
	TI = 0;		 //·¢ËÍÍê³ÉºóÁ¢Âí¸´Î»
}

void main(void)
{
	UART_Init();
	UART_SendByte(0x11);
	while(1)
	{
		
	}
}

 编译后得:

设置:打开串口,波特率:4800;校验位:无校验;停止位:1位。

然后连接开发板,每按一次复位键都会传输一次Byte给开发板。

 注意:

当我们这么写代码时:

void main(void)
{
	UART_Init();
	while(1)
	{
		UART_SendByte(0x66);
	}
}

然后打开串口,会出现BUG

并不是66,而是96,这是因为晶振的误差,可以通过延时来解决问题

void main(void)
{
	UART_Init();
	while(1)
	{
		UART_SendByte(0x66);
		Delay(1);
	}
}

每隔一秒发送一个递增的数

UART.c

#include <REGX52.H>

/**
  * @brief  ´®¿Ú³õʼ»¯
  * @param  ÎÞ
  * @retval ÎÞ
  */

void UART_Init(void)//4800bps@11.0592MHz
{
	SCON = 0x40;
	PCON |= 0x80;
	
	TMOD &= 0x0F;		
	TMOD |= 0x20;		
	TL1 = 0xF4;		
	TH1 = 0xF4;	
	ET1 = 0;		//½ûÖ¹¶¨Ê±Æ÷1ÖжÏ
	TR1 = 1; 		//Æô¶¯¶¨Ê±Æ÷1
}

/**
  * @brief  ´®¿Ú·¢ËÍÒ»¸ö×Ö½Ú
  * @param  ×Ö½Ú
  * @retval ÎÞ
  */

void UART_SendByte(unsigned char Byte)
{
	SBUF = Byte;
	while(TI==0);//¼ì²âÊÇ·ñÍê³É·¢ËÍ
	TI = 0;		 //·¢ËÍÍê³ÉºóÁ¢Âí¸´Î»
}

 main.c

#include <REGX52.H>
#include "Delay.h"
#include "UART.h"

unsigned char Sec = 0;

void main(void)
{
	UART_Init();
	while(1)
	{
		UART_SendByte(Sec);
		Sec++;
		Delay(1000);
	}
}

 按下复位键可以从0开始

电脑通过串口控制LED

复制UART的头文件和c文件

接受电脑数据需要中断器判断,当接受到数据后,申请中断,检测出数据,所以要打开串口的中断器,并不是打开定时器1的中断器。

因为要用电脑送给单片机,所以REN要设置为1,

其他不变,则SM0,SM1,REN,TB8,RB8,Ti,RI=0101 0000=0x50

SCON = 0x50;

 中断器设置:

EA = 1;ES = 1;

 根据中断查询次序号(下图)

 串口中断号是4;则可得中断服务子函数:

void UART_Routine() interrupt 4
{
    
}

最终我们可得修改后的UART.c

#include <REGX52.H>

void UART_Init(void)//4800bps@11.0592MHz
{
	SCON = 0x50;
	PCON |= 0x80;
	
	TMOD &= 0x0F;		
	TMOD |= 0x20;		
	TL1 = 0xF4;		
	TH1 = 0xF4;	
	//禁止定时器1中断
    ET1 = 0;		
	//启动定时器1
    TR1 = 1; 		
	//启动串口中断器
    EA = 1;
	ES = 1;			
}


void UART_SendByte(unsigned char Byte)
{
	SBUF = Byte;
	while(TI==0);
	TI = 0;		 
}

main.c

#include <REGX52.H>
#include "Delay.h"
#include "UART.h"

unsigned char Sec = 0;

void main(void)
{
	UART_Init();
	while(1)
	{
		
	}
}

void UART_Rountine() interrupt 4
{
	
}

我们还可以编写一个建议代码来验证

main.c:

#include <REGX52.H>
#include "Delay.h"
#include "UART.h"

unsigned char Sec = 0;

void main(void)
{
	UART_Init();
	while(1)
	{
		
	}
}

void UART_Rountine() interrupt 4
{
	P2 = 0x00;
}

随机发送一个数据,

会使LED灯全部亮起 

 

但是这不能证明是串口接收了数据才使LED亮起,所以要加一个判断(因为也有可能是串口发送数据导致中断,因为发送中断标志位和接收中断标志位是使用同一个通道的)

 

 仅对主函数.c文件中的中断函数做修改

void UART_Rountine() interrupt 4
{
	if(RI==1)
	{
		P2 = SBUF;//SBUF是串口接收到的数据
		RI = 0;//让串口还能继续接受数据
	}
}

 当我们发送0xf0时

可得结果: 

 因为有些板子LED方向不一样,可以给P2加上取反符号。

再继续实现把数据发送到板子中,这就要运用到之前所编写的UART_SendByte()函数,

#include <REGX52.H>
#include "Delay.h"
#include "UART.h"

unsigned char Sec = 0;

void main(void)
{
	UART_Init();
	while(1)
	{
		
	}
}

void UART_Rountine() interrupt 4
{
	if(RI==1)
	{
		P2 = ~SBUF;
		UART_SendByte(SBUF);
		RI = 0;
	}
}

特别注意:UART_SendByte()函数不能同时出现在中断函数和主函数中(函数的重录),这会导致一些BUG。

正常结果如图

 

 最终结果:

UART.c

#include <REGX52.H>


void UART_Init(void)//4800bps@11.0592MHz
{
	SCON = 0x50;
	PCON |= 0x80;
	
	TMOD &= 0x0F;		
	TMOD |= 0x20;		
	TL1 = 0xF4;		
	TH1 = 0xF4;	
	ET1 = 0;		//½ûÖ¹¶¨Ê±Æ÷1ÖжÏ
	TR1 = 1; 		//Æô¶¯¶¨Ê±Æ÷1
	EA = 1;
	ES = 1;			//Æô¶¯´®¿ÚÖжÏÆ÷
}


void UART_SendByte(unsigned char Byte)
{
	SBUF = Byte;
	while(TI==0);//¼ì²âÊÇ·ñÍê³É·¢ËÍ
	TI = 0;		 //·¢ËÍÍê³ÉºóÁ¢Âí¸´Î»
}

/**
  * @brief  串口中断函数模板
  * @param  
  * @retval 
  */

/*void UART_Rountine() interrupt 4
{
	if(RI==1)
	{
		
		RI = 0;
	}
}*/


main.c

#include <REGX52.H>
#include "Delay.h"
#include "UART.h"

unsigned char Sec = 0;

void main(void)
{
	UART_Init();
	while(1)
	{
		
	}
}

void UART_Rountine() interrupt 4
{
	if(RI==1)
	{
		P2 = SBUF;
		UART_SendByte(SBUF);
		RI = 0;
	}
}

拓展:

波特率的计算(12MHz)

 TL1 = 0xF3 = 243(十进制),而计数器(8位重装模式)每到256就溢出一次

256 - 243 = 13,即可得计数器每记13个数就溢出一次

即13us,即T1溢出频率即为1/13us = 0.07692MHz

因为我们设置SMOD=1,

到发送控制器T1只用/16,0.07692 / 16 =0.00480769MHz = 4807.69Hz;

当SMOD=0时,波特率=4807.69/2;

数据显示模式

HEX 模式 / 十六进制模式 / 二进制模式:以原始数据的形式显示
文本模式 / 字符模式:以原始数据编码后的形式显示

编码,即ASCII码

当我们HEX模式给串口输入0x30时,HEX模式下接手即为30

HEX模式发送30时,文本模式接收则会为0

这其实是对照ASCII码的

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C51单片机串口通讯是将单片机与其他设备(如电脑、其他单片机等)之间进行数据传输的一种方式。C51单片机内置了一个串口通讯模块,可以实现串口通讯功能。 C51单片机串口通讯的具体步骤如下: 1. 配置串口通讯的波特率、校验位、数据位等参数。 2. 初始化串口通讯模块。 3. 发送数据:将需要发送的数据存储在串口发送缓冲区中,当串口发送缓冲区为空时,将数据发送出去。 4. 接收数据:当有数据到达串口接收缓冲区时,从串口接收缓冲区中读取数据。 C51单片机串口通讯的具体实现可以参考以下代码示例: ```c #include <reg52.h> #include <stdio.h> // 定义波特率为9600 #define BAUDRATE 9600 // 定义计算波特率的预分频值 #define PRESCALER (65536 - (11059200 / 12 / BAUDRATE)) // 初始化串口通讯模块 void InitUART() { // 设置波特率 TMOD &= 0x0F; TMOD |= 0x20; TH1 = PRESCALER / 256; TL1 = PRESCALER % 256; // 启用串口通讯模块 TR1 = 1; // 设置串口通讯模式 SCON = 0x50; } // 发送一个字符 void SendChar(char c) { SBUF = c; while (TI == 0) ; TI = 0; } // 发送一个字符串 void SendString(char *s) { while (*s != '\0') { SendChar(*s); s++; } } // 接收一个字符 char ReceiveChar() { while (RI == 0) ; RI = 0; return SBUF; } // 主函数 void main() { char data; InitUART(); SendString("Hello, world!\r\n"); while (1) { data = ReceiveChar(); SendChar(data); } } ``` 以上代码实现了C51单片机与电脑之间的串口通讯,可以通过终端软件(如SecureCRT、TeraTerm等)进行测试和调试。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值