【单片机】12-串口通信和RS485

1.通信有关的常见概念

区分:串口,COM口,UART,USART_usart和串口区别-CSDN博客

串口、COM口、UART口, TTL、RS-232、RS-485区别详解-CSDN博客

1.什么是通信

(1)人和人之间的通信:说话,写信

(2)人和计算机之间的通信:按键,显示器,鼠标

(3)计算机和计算机之间的通信

2.通信的关键

(1)事先约定

(2)基本的信息单元

(3)有效信息的编码,传输和解码

3.通信的专业性概念

(1)同步和异步:同一个步调

(2)单工,半双工,全双工:数据传输方向

(3)并行和串口:并行(多根线),串行(单根线)

(4)电平信号【传输近,易受干扰】和差分信号【传输远,不易受干扰】:电平是通过高低电平进行区分,差分是相对的

2.串行通信

1.串口通信基础

(1)一种特定的通信协议【电平通信】

(2)串行通信,串口通信,UART,USART

(3)串口通信的特点:异步,串行,全双工

2.串行通信的主要用途

(1)早期:计算机之间短距离通信 (1.5米内),完备通信机制

(2)现在:CPU之间近距离通信【CPU和周边芯片】,调试信息输入输出,非完备通信

3.串行通信的工作方式

(1)3根线(GND,RxD【接收线】,TxD【发送线】---recive ,transmit)---不完备通信

或者9根线(DB9)-完备通信

(2)发送方有发送位移寄存器,接收方有接收移位寄存器

(3)数据在发送方和接收方的CPU中都以字节为单位整字节处理

(4)数据在通信线上以位为单位逐个bit的传输

4.串行通信的主要概念

传输都是一帧一帧的发

1.起始位

开始之前要发送一个起始位

2.数据位

比如我们传输"A",转换为ASCII就是8位二进制,这8位就叫做8位数据位【有效数据】

数据位要双方沟通确定的

3.奇偶校验位

判断传输过程中是否出错

4.停止位(帧)

判断是否结束

5.波特率

串行通信的速度

一秒钟传输多少给bit位,发送方和接收方必须波特率设置为一样【波特率越小,传输速度慢,抗干扰能力越强】

6.流控

速率协商,现在一般要禁用掉

3.51单片机的串行通信

1.基础概念

SoC:把CPU以及其他功能集成到一个芯片上

(1)串行通信功能是SoC的一个(内部)外设提供的,与CPU无关【CPU=运算器+控制器】--CPU本身无法通信

(2)各种不同的SoC的串行通信大同小异【内部差不多,编程时候可能不同】

(3)串行通信经常作为主控SoC与其他外部芯片之间的通信接口**【串行通信==SoC与外部其他芯片的通信】**

2.STC51单片机的串行通信简介

4.STC51的串行通信相关寄存器

1.总体浏览

2.SCON:串行控制寄存器

3.SBUF:串行口缓存寄存器

4.PCON:电源控制寄存器

5.IE:中断允许寄存器

6.串口发送时的软硬件协作方式

(1)查询方式。硬盘在发送完一帧数据后会将一个标志位置位(标志位本来是0),软件需要不断读取这个标志位的值来判断硬件是否完成了发送(如果读出来的是0就表示硬件还在发还在发还没完还在忙,所以我们就不能认为硬件发完了,所以就不能给硬件安排下一帧数据的发送;如果读出来的是1则说明硬件已经发完了上一帧数据,这时候软件就应该给硬件在给一帧数据去发送)

(2)因为串口发送完这个事件对CPU来说是个异步事件(因为不知道什么时候发送消息),所以这里查询方式来处理和之前讲过的查询方式处理按键是非常类似的。

(3)常见情况下:串口发送会使用查询方式,串口接收会使用中断方式【因为不知道什么时候会接收到信息,使用中断才不会过度销毁CPU】

7.波特率加倍

PCON中的SMOD

所谓波特率加倍,就是正常计算出的波特率假设是2400,那么SMOD=1时则实际波特率就是4800;当SMOD=0时不加倍,也就是2400还是2400

5.STC51的串行通信实战

1.硬件接线分析

(1)目标:将PC机和51单片机通过串口连接起来

(2)PC机的串口情况:台式机串口,笔记本USB转串口

(3)开发板原理图分析

2.接线方案

1.使用板载CH340

什么都不用动,默认就是使用这个,最简单最省事,最推荐

2.使用DB9接口USB转串口线

用DB9接口的USB转串口线

注意:跳线帽接到DB9一侧

3.使用TTL接口USB转串口线

只接三根线:TxD,RxD,GND

4.总结

3.使用板载CH340进行串口实践

1.接线+下载程序

2.查看设备管理器确定COM号

3.方法一:使用普中下载软件自带的串口助手监视

4.方法二:使用第三方串口助手软件监视

5.方法三:使用SecureCRT软件监视

4.使用DB9接口USB转串口线

1.接线+下载程序

2.注意对下载程序的影响

此时无法进行程序下载

3.使用各种方式进行监视

5.串口初始化

cpp 复制代码
//串口初始化函数
//预设一个串口条件:8位数据位,1停止位,0校验位,波特率4800
//初始化的主要工作就是去设置相关的寄存器
void uart_init(void){
	
	SCON=0x50; //串口工作在模式1(8位串口),允许接收
	PCON=0x80;   //波特率加倍,意思是本来需要波特率4800,等一下计算时按照2400去计算就好
	
}

6.波特率计算

SMOD---》判断是否进行波特率加倍【如果加倍则为1,不加倍则为0】

查看数据手册中"串行通信中波特率的设置"

接着查看定时/计数器1的工作方式2的寄存器设置情况

cpp 复制代码
TMOD=0x20;  //设置T1为模式2
cpp 复制代码
TR1=1;        //开启T1让它开始工作

我们刚刚在上面计算出TH1=243;,所以进行设置

cpp 复制代码
TH1=243;
TL1=243;   //8位自动重装,意思就是TH1用完了下一个周期TL1会自动重装到TH1中



7.串口发送字符【单个字符】

注意点:51单片机不一样,要先发送在检验有没有在发送

其他MCU都是先检验在发送

如果为【0】则表示在忙,如果为【1】则表示发送结束

cpp 复制代码
//串口发送函数,发送一个字节
void uart_send_byte(unsigned char c){
	
	//【第一步】发送一个字节
	SBUF=c;
	//【第二步】先确认串口发送部分没有在忙
	while(!TI);//TI=0,表示在忙
	//【第三步】软件复位TI标志位---数据手册要求的
	TI=0;
}

8.测试一下

1.波特率不加倍

cpp 复制代码
#include<reg51.h>


//函数声明
void uart_init(void);
void uart_send_byte(unsigned char c);
void delay();



void main(){
	
	//第一步:初始化好串口到正确的状态
	uart_init();
	
	
	while(1){//为了调试方便,让A循环发送,才好监视
		
		//第二步:通过串口发送信息出去
		uart_send_byte('A');
		delay();
	}
	
	
}



void delay(){
	unsigned char i,j;
	for(i=0;i<100;i++){
		for(j=0;j<200;j++);
	}
}



//串口初始化函数
//预设一个串口条件:8位数据位,1停止位,0校验位,波特率4800
//初始化的主要工作就是去设置相关的寄存器
void uart_init(void){
	
	//波特率不加倍的例子
	
	SCON=0x50; //串口工作在模式1(8位串口),允许接收
	PCON=0x00;   //波特率不加倍
	
	//通信波特率相关的设置
	//此处我们使用【方式1】---对应数据手册
	
	TMOD=0x20;  //设置T1为模式2
	
	TR1=1;	//开启T1让它开始工作
	
	TH1=249;
	TL1=249;    //8位自动重装,意思就是TH1用完了下一个周期TL1会自动重装到TH1中
	
	
	//中断初始化
	ES=1;//串口中断初始化
	EA=1;//整个中断初始化
	

}


//串口发送函数,发送一个字节
void uart_send_byte(unsigned char c){
	
	//【第一步】发送一个字节
	SBUF=c;
	//【第二步】先确认串口发送部分没有在忙
	while(!TI);//TI=0,表示在忙
	//【第三步】软件复位TI标志位---数据手册要求的
	TI=0;
}

因为我们算出来的TH1和TL1=6.5,所以精确度可能会受到影响,我们将其设置为250 或者 249则结果都会输出乱码

2.换一个波特率:9600

我们前面算出TH1=6.5【这里我们的波特率为4800】,所以如果为9600则我们应该将【6.5/2=3.25】才是我们9600的结果

cpp 复制代码
//波特率为9600

	SCON=0x50; //串口工作在模式1(8位串口),允许接收
	PCON=0x00;   //波特率不加倍
	
	//通信波特率相关的设置
	//此处我们使用【方式1】---对应数据手册
	
	TMOD=0x20;  //设置T1为模式2
	
	TR1=1;	//开启T1让它开始工作
	
	TH1=253;
	TL1=253;    //8位自动重装,意思就是TH1用完了下一个周期TL1会自动重装到TH1中
	
	
	//中断初始化
	ES=1;//串口中断初始化
	EA=1;//整个中断初始化

9.串口发送字符串【多个字符】

cpp 复制代码
#include<reg51.h>


//函数声明
void uart_init(void);
void uart_send_byte(unsigned char c);
void delay();
void uart_send_string(unsigned char *str);



void main(){
	
	//第一步:初始化好串口到正确的状态
	uart_init();

	while(1){
			//发送字符串,也可以发送中文
		uart_send_string("abcdefg");
		delay();
	}
	
}



void delay(){
	unsigned char i,j;
	for(i=0;i<100;i++){
		for(j=0;j<200;j++);
	}
}



//串口初始化函数
//预设一个串口条件:8位数据位,1停止位,0校验位,波特率4800
//初始化的主要工作就是去设置相关的寄存器
void uart_init(void){

//波特率加倍的例子
	
	SCON=0x50; //串口工作在模式1(8位串口),允许接收
	PCON=0x80;   //波特率加倍,意思是本来需要波特率4800,等一下计算时按照2400去计算就好
	
	//通信波特率相关的设置
	//此处我们使用【方式1】---对应数据手册
	
	TMOD=0x20;  //设置T1为模式2
	
	TR1=1;	//开启T1让它开始工作
	
	TH1=243;
	TL1=243;    //8位自动重装,意思就是TH1用完了下一个周期TL1会自动重装到TH1中
	
	
	//中断初始化
	ES=1;//串口中断初始化
	EA=1;//整个中断初始化

}


//串口发送函数,发送一个字节【单个字节】
void uart_send_byte(unsigned char c){
	
	//【第一步】发送一个字节
	SBUF=c;
	//【第二步】先确认串口发送部分没有在忙
	while(!TI);//TI=0,表示在忙
	//【第三步】软件复位TI标志位---数据手册要求的
	TI=0;
}


//发送字符串【多个字符】
void uart_send_string(unsigned char *str)
{
	while (*str != '\0')
	{
		uart_send_byte(*str);		// 发送1个字符
		str++;						// 指针指向下一个字符
	}
}

10.串口接收函数编写

因为我们在程序执行过程中如果要接收PC机传输过来的数据,则表示程序要进行中断,则要进行中断处理。

cpp 复制代码
void uart_isr(void) interrupt 4 using 1{
}
cpp 复制代码
#include<reg51.h>


//函数声明
void uart_init(void);
void uart_send_byte(unsigned char c);
void delay();
void uart_send_string(unsigned char *str);



void main(){
	
	//第一步:初始化好串口到正确的状态
	uart_init();

	uart_send_string("串口回环测试\n");
	while(1);
}



void delay(){
	unsigned char i,j;
	for(i=0;i<100;i++){
		for(j=0;j<200;j++);
	}
}



//串口初始化函数
//预设一个串口条件:8位数据位,1停止位,0校验位,波特率4800
//初始化的主要工作就是去设置相关的寄存器
void uart_init(void){

//波特率加倍的例子
	
	SCON=0x50; //串口工作在模式1(8位串口),允许接收
	PCON=0x80;   //波特率加倍,意思是本来需要波特率4800,等一下计算时按照2400去计算就好
	
	//通信波特率相关的设置
	//此处我们使用【方式1】---对应数据手册
	
	TMOD=0x20;  //设置T1为模式2
	
	TR1=1;	//开启T1让它开始工作
	
	TH1=243;
	TL1=243;    //8位自动重装,意思就是TH1用完了下一个周期TL1会自动重装到TH1中
	
	
	//中断初始化
	ES=1;//串口中断初始化
	EA=1;//整个中断初始化
	

}


//串口发送函数,发送一个字节【单个字节】
void uart_send_byte(unsigned char c){
	
	//【第一步】发送一个字节
	SBUF=c;
	//【第二步】先确认串口发送部分没有在忙
	while(!TI);//TI=0,表示在忙
	//【第三步】软件复位TI标志位---数据手册要求的
	TI=0;
}


//发送字符串【多个字符】
void uart_send_string(unsigned char *str)
{
	while (*str != '\0')
	{
		uart_send_byte(*str);		// 发送1个字符
		str++;						// 指针指向下一个字符
	}
}

//中断处理程序
void uart_isr(void) interrupt 4 using 1
{
	unsigned char tmp;
	if(RI){
		tmp=SBUF;   //读取SBUF,其实就是读出串口接收到的1个字节
		//RI:串行口1接收中断标志
		RI=0;
	}
	//自此已经读到了PC发给单片机的一个字节,但是单片机没有显示器无法显示
	//我们用最简单的方法,就是直接回发
	uart_send_byte(tmp);
}

6.RS485

1.UART的缺点:传输距离受限

(1)理论上RS232不超过15米--电脑的COM端口【DB9】

(2)理论上TTL电平通信距离更短---TTL是在单片机上使用

(3)实际上几百米也有人宣称做到了,但是稳定性不能保证

(4)波特率越高通信距离越短【速度越快,通信距离越短】

2.远距离传输

(1)提高电压标准

(2)提高通信线抗干扰能力,降低阻抗

(3)使用差分信号--抗干扰能力强【RS485/RS422】

3.RS485(RS422)

(1)最大通信距离1200多米,最快通信速率10Mbps,距离和速度成反比(USB接口,网线)

(2)差分信号负逻辑【5V代表---0 -3V代表----1】

(3)更远距离可以加中继器【中继器---多个485的节点连接起来--放大】

(4)半双工【如果要实现全双工,则使用4根线】

(5)RS485只提高物理层通信能力,不提供数据层协议,需要用户自定义,或者使用标准协议如MODBUS协议。

4.MAX485

MAX485就是相当于UART与RS485之间的信号转换

(1)CPU本身只会提供URAT接口,而不会提供RS485接口。CPU根本不认识RS485

(2)RS485使用场景:CPUA-->UART转RS485------>远距离通信----->RS485转UART---->CPUB

(3)对RS485的理解:RS485是纯硬件实现的,硬件芯片如MAX485来管理的,根本不涉及软件编程。软件工程师只关注串口,只通过串口将数据发送出去或者接收回来即可。UART转485和485转UART对CPU来说是透明的。

相关推荐
国科安芯4 小时前
航天器多路并联大功率电源系统设计与ASP4644均流特性分析
单片机·嵌入式硬件·fpga开发·安全性测试
茯苓gao6 小时前
嵌入式开发笔记:CANopen相关移位运算与通信协议术语详解
网络·嵌入式硬件·学习·信息与通信
梁朝辉9 小时前
STM32H750VBT6中ADCINP与INN什么区别
stm32·单片机·嵌入式硬件
省四收割者10 小时前
一文详解信号完整性(1)
python·嵌入式硬件·数学建模·信息与通信·信号处理·智能硬件
崇山峻岭之间11 小时前
单片机无感FOC驱动之ADC
单片机·嵌入式硬件
Ligocious11 小时前
1.点亮一颗小小的LED
单片机·嵌入式硬件
KaifuZeng15 小时前
通信与接口协议面试八、CAN通信
单片机·嵌入式硬件·面试·通信与接口协议
智源单片机设计16 小时前
基于单片机的直流电机双闭环调速系统设计
单片机·嵌入式硬件
KaifuZeng1 天前
通信与接口协议面试一、基本概念
嵌入式硬件·通信与接口协议
wuyk5551 天前
24. C 语言模块化:不是拆几个.c 文件那么简单
c语言·开发语言·stm32·单片机