STM32F4_RS485、RS232

目录

1. 485简介

2. 串口UART存在的问题

3. RS232协议

4. RS485协议

6. 硬件分析

7. 实验程序

7.1 main.c

7.2 RS485.c

7.3 RS485.h


RS232的高电平1的逻辑为-5V~-15V,低电平0的逻辑为+5V~+15V。高电平和TTL的0~5V不兼容,传输的距离也不够长。

1. 485简介

        485(一般称作RS485)是隶属于OSI模型物理层的电气特性,规定为2线,半双工模式,多点通信。其电气特性和RS232不太一样,RS485是用缆线两端的电压差值来传递信号。RS485仅仅规定了接收端和发送端的电气特性。没有规定或推荐任何数据协议。

特点:

  1.         接口电平低,不容易损坏芯片。这一点相对于RS232接口的高电平来说,是非常大的优点。                                                                                                                     
  2.         RS485的电气特性:逻辑 “1” 以两线间的电压差+(2~6)V表示;逻辑 “0” 以两线间的电压差-(2~6)V表示。接口电平低,不易损坏接口电路的芯片,并且该电平与TTL电平兼容,可以方便与TTL电路连接
  3.         传输速率高。10米时,RS485的数据最高传输速率可达35Mbps,在1200m时,传输速度可达100Kbps。
  4.         抗干扰能力强。RS485接口采用平衡驱动器和差分接收器的组合,抗共模干扰能力强,也可以说是抗噪声干扰性好。
  5.         传输距离远,支持节点多。RS485总线最长可以传输1200m以上,一般最大支持32个节点。

总结:

        RS232 和 RS485 并不是和 IIC、SPI 协议一样,是一个单独的总线协议,RS232 和 RS485 都是建立在电气层面的,他们的实现都需要依赖于串口

2. 串口UART存在的问题

        这里为什么要说明串口 UART 存在的问题呢? 是因为一般在进行项目时,通常很少使用串口通信,就是因为串口存在诸多问题。也正是因为串口存在的这诸多问题,所以才有 RS485 通讯存在的意义。

电气接口不统一(致命):

        1. UART只是对信号的时序进行了定义,而未定义接口的电气特性。(电气特性:打个比方,我们买了个家具,不管买的哪个厂家的,回到家之后都可以通过地线、火线、零线接到自家的插座上使用,这是因为插座的尺寸是全国统一标准规定的,而 UART 就没有对这一特性进行规定,导致不同器件之间通信变得复杂化)

        2. UART通信时一般使用处理器提供的电平,即TTL电平,但不同处理器使用的电平是存在差异的,所以不同处理器使用 UART 通信时一般不能直接相连。(打个比方,我想使用51单片机和STM32单片机通信,51单片机处理器提供的电平0~5V,STM32处理器提供的电平是0~3.3V,如果两者之间想要进行通信,必须经过转电平处理才可以)

        3. UART没有规定不同器件连接时连接器的标准,所以不同器件之间通过UART通信时连接很不方便。

STM32F4_RS485、RS232_第1张图片

抗干扰能力差:

        UART一般直接使用TTL信号来表示 0 和 1 ,但TTL信号的抗干扰能力较差,数据在传输过程中很容易出错。

通信距离极短:

        因为TTL信号的抗干扰能力较差,所以其通信距离也很短,一般只能用于一个电路板上的两个不同芯片之间的通信。

3. RS232协议

        RS232协议是1970年美国电子工业协会 EIA 联合贝尔系统、调制解调器厂家、计算机终端生产厂家共同制定的用于串行通信的标准(从上面介绍的UART的缺点来看,串口通信最致命的缺点就是没有统一的标准,导致不同器件之间的通讯变得异常的复杂,而 RS232 和 RS485 就是这样的一个标准,应用在串口之上,使得串口通信具有一定的标准)

        该标准规定采用一个标准的连接器,标准中对连接器的每个引脚的作用都加以规定,同时也对信号的电平加以规定。

STM32F4_RS485、RS232_第2张图片

接口:

        该标准规定采用一个 25 引脚的 DB-25 连接器,标准中对连接器的每个引脚的信号内容加以规定。还对各种信号的电平加以规定;后来1BM的PC机将RS232简化成了 DB-9 连接器,再后来成了事实的标准(就像我们家用的三头插座一样,火线、零线、地线);现在工业控制的RS-232接口一般只使用RXD、TXD、GND三条线(STM32开发板中的原理图上也是如此)。

信号:

        RS232标准规定逻辑 “1” 的电平为-5V到-15V,逻辑 “0” 的电平为+5V到+15V,选择该电气标准的目的在于提高抗干扰能力,增大通信距离,其传送的距离一般可达15m。

STM32F4_RS485、RS232_第3张图片

STM32F4_RS485、RS232_第4张图片

C1+:倍增器电荷泵电容器的正极。

V+:充电泵产生的+5.5V。

C1-:倍增器电荷泵电容器的负极。

C2+:反转电荷泵电容器的正极。

C2-:反转电荷泵电容器的负极。

V-:-5.5V由电荷泵产生。

DOUT2:RS-232驱动输出。

RIN2:RS-232接收输入。

VCC:+3V~+5.5V供给电圧。

GND:地。

DOUT1:RS-232驱动输出。

RIN1:RS-232接收输入。

ROUT1:RS-232接收输出。

DIN1:RS-232驱动输入。

DIN2:RS-232驱动输入。

ROUT2:RS-232接收输出。

RS232存在的问题:

        1. 接口的信号电平较高(-15V~+15V),易损坏接口电路芯片,又因为与TTL电平不兼容,所以需要使用电平转换芯片才能与TTL电路连接。

        2. 通信速度较低,不适合用于那些高速通信的场合。        

        3. 易产生共模干扰,抗噪声干扰性弱。

        4. 传输距离较短,一般情况只有15m。

最后强调一点:程序编程方面 RS232 和串口没有任何区别,只是在硬件方面做出了一些改善。

4. RS485协议

        RS485协议是由电信行业协会和电子工业联盟定义;使用该标准的通信网络能在远距离条件下(1500m)以及电子噪声大的环境下有效传输信号。该标准允许连接多个收发器,即具有多站能力,这样可以利用单一的 RS485 接口方便的建立起一个设备网络。

        像串口 UART 和 RS232 是点对点通信;点对点的意思就是说只能实现两个设备之间的收发数据;

        而RS485是可以建立起设备通信网络的,主机可以像下图一样连接多个 RS485 设备进行通信;在理想情况下,RS485需要两个终端匹配电阻(一般在总线的起始和终止端加,也就是主机和最后一个设备上加,匹配电阻通常情况下是120Ω,如下图所示),电阻的阻值等于传输电缆的特性阻抗。

        至于为什么要加这两个终端匹配电阻,是因为如果不加的话,当所有RS485设备都静止或者没有能量的时候就会产生噪音。并且可能在线移的过程中导致数据传输错误。

STM32F4_RS485、RS232_第5张图片

信号:

        RS485标准规定采用差分信号进行数据传输,两线间的电压差为+2V~+6V表示逻辑 “1” ,两线间的电压差-2V到-6V表示逻辑 “0” 。使用差分信号能有效地减少噪声信号的干扰,延长通信距离,RS485 的通信距离可以达到1500m;RS485 接口信号的电平比 RS232 降低了,所以不易损坏接口电路的芯片,且该电平与TTL电平信号兼容,可以方便与TTL电路连接。

拓展

        1. 什么是差分信号?

        官方点说:差分信号是用一个数值来表示两个物理量之间的差异。简单来说就是:通常我们通信的信号都是通过一根线来表示高低电平1 或者 0 的,但是差分信号是通过 2 根线互铰在一起传达的(如下图),更精确的说是2根线传达一个信号,这个信号可以是0,也可以是1(不像我们平时通信,1 根线传 0,另一根线传 1)。通过这两根线传达的电平高低差异来定义逻辑1 和逻辑 0 。 

STM32F4_RS485、RS232_第6张图片

STM32F4_RS485、RS232_第7张图片

        2.  为什么RS485可以通过差分信号明显的降低噪声干扰?

        其实在我们日常生活中,差分信号的应用还是很多的。比方说我们家用的网线,细心的可以发现,其中就是通过两根线互铰在一起的。

        之所以能明显的降低噪声干扰,是因为噪声是确确实实存在的,我们无法直接去掉这种干扰,可以反过来这样的考虑,既然无法处理这种干扰,那不妨就让他干扰,因为差分信号是用两根线进行传递一个信号的,所以噪音只要干扰,一定是同时干扰我的两根线的,而且干扰我的两根线的程度一定是相同的,这样一来,逻辑1 还是逻辑 1 ,逻辑0 还是逻辑 0 。没有实质上的影响。

STM32F4_RS485、RS232_第8张图片

接口:

        RS485采用两线制(RS422采用四线制),两线制就是说 RS485 设备通过两根线连接到主机上即可实现通信,这种接线方式为总线式拓扑结构,在同一总线上可以同时存在多个节点。也就是可以连接多个 RS485 设备,实现组成一个通信网络。

        因为采用的是两线制,数据的发送和接收都是要使用这对差分信号线,(两根线是互铰在一起的,发送的时候同时需要这两根线,接收的时候也同时需要这两根线)发送和接收不能同时进行(所以虽然是两根线,但是不能一根用来发送,另外一根用来接收),所以只能采用半双工的方式进行,编程时需要进行格外的处理。

RS485主从机通信:

        通过对 IIC 和 SPI 的学习,我们知道 IIC 通信是通过主机寻址的方式进行的,对应的每个 IIC 从机都有一个确定的地址,主机想要和哪个从机进行通信,找到对应从机的地址即可进行通信。SPI 通信时主机上对应多个 SPI 片选信号线 CS ,有的地方也称作 SS、NSS,实际上是一个意思,都是片选信号线,每个 SPI 从机上都有独立的片选信号线,主机想要和特定的从机通信时,只需要将对应从机的片选信号线置 0 即可,结束通讯时只需要将片选信号线置 1 即可。那么 RS485 呢

        事实上,RS485的主从机通讯并没有明确的规定,这完全由我们编程的程序来确定,我们可以效仿 IIC ,给每个从机写一个确定的地址,通过寻址的方式来确定主从机通讯。当然,也可以是其他的方式。

电平转换:

        虽然很多处理器都会集成 UART 控制器,但处理器产生的信号一般都是TTL信号并不是符合 RS485 标准的信号,所以一般我们还需要在处理器外部去添加电路将TTL信号转换成差分信号。RS485用到的TTL信号转换成差分信号的芯片就是SP3485

STM32F4_RS485、RS232_第9张图片

RO:接收输出引脚。

RE:接收输出使能。(接收使能信号低电平有效)

DE:驱动输出使能。(发送使能信号高电平有效)

DI:驱动输入使能。

GND:地。

A:总线接口,用于连接485总线。 接线的时候A对A,B对B。

B:总线接口,用于连接485总线。

VCC:该芯片支持3.3V供电。

RS485的优势:

        1. 接口信号的电平值较低,不易损坏接口电路芯片,且与TTL电平兼容,可以方便的与TTL电路连接。

        2. 通信速度快。

        3. 抗噪声干扰性强。

        4. 传输距离远(1500m)。

        5. 可以实现多节点网络。

总结:

        举个例子来说明本节中 RS485 和 RS232 的用处。

        我们从A地去往B地。串口UART的方式就是保证谁先去,谁先谁后的顺序,其他的一概不管。而RS485和RS232在串口的基础上强调了怎么去,比方说我们是坐火车、高铁还是飞机的方式,具体什么方式还需要我们来选择,选择不同的方式会大大的减少出行的时间。

6. 硬件分析

STM32F4_RS485、RS232_第10张图片

        上图是 STM32F4 的 RS485 的相关引脚连接图。其中 SP3485 芯片中的 RE 和 DE 引脚分别为接收使能引脚和发送使能引脚。通过 RS485_RE 连接到芯片的 PG8 引脚上,使能控制SP3485 的收发,当 PG8=0,输出低电平,RE接收使能有效,为接收模式;当 PG8=1,输出高电平,DE 发送使能有效,为发送模式

        这里需要注意,PA2,PA3 和 ETH_MDIO 和 PWM_DAC 有共用 IO,所以在使用的时候,注意 分时复用,不能同时使用。另外 RS485_RE 信号,也和 NRF_IRQ 共用 PG8,所以他们也不可 以同时使用,只能分时复用。

        电阻 R38 和 R40 是两个偏置电阻,是用来保证总线空闲时,A B 压差不定,引起逻辑错乱,可能出现乱码的情况。

        C71 是电源滤波电容,保证芯片 SP3485 正常稳定的运行,过滤掉其他噪声的干扰。

STM32F4_RS485、RS232_第11张图片

        本次实验需要将两个开发板上的 P9排针 的跳线帽按上图进行连接;

        最后,我们用 2 根导线将两个开发板 RS485 端子的 A 和 A,B 和 B 连接起来。这里注意不 要接反了(A 接 B),接反了会导致通讯异常!!

7. 实验程序

        本实验将实现两个开发板之间的RS485通讯,KEY0控制发送,当按下开发板的KEY0按键时,就会发送5个数据给另外一个开发板,在两个开发板上分别显示发送的值和接收到的值。

7.1 main.c

#include "stm32f4xx.h"                 
#include "delay.h"
#include "usart.h"
#include "LED.h"
#include "lcd.h"
#include "Key.h"
#include "usmart.h"
#include "RS485.h"

//LCD状态设置函数
void led_set(u8 sta)//只要工程目录下有usmart调试函数,主函数就必须调用这两个函数
{
	LED1=sta;
}
//函数参数调用测试函数
void test_fun(void(*ledset)(u8),u8 sta)
{
	led_set(sta);
}

int main(void)
{
	u8 key;
	u8 i=0;
	u8 t=0;
	u8 CNT=0; //CNT是一个累加数,一旦KEY0按下,就以这个数位基准连续发送5个数据
	u8 RS485Buf[5];
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
	delay_init(168);
	uart_init(115200); //切记:不初始化延迟函数和串口是不能用LCD的
	
	LED_Init();
	LCD_Init();
	Key_Init();
	RS485_Init(9600);  //初始化RS485,设置波特率为9600
	POINT_COLOR=RED;
	LCD_ShowString(30,50,200,16,16,"Explorer STM32F4");
	LCD_ShowString(30,70,200,16,16,"RS485 TEXT");
	LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");
	LCD_ShowString(30,110,200,16,16,"2023/06/08");
	LCD_ShowString(30,130,200,16,16,"KEY0:Send"); //显示提示信息,按KEY0发送
	
	POINT_COLOR=BLUE;
	LCD_ShowString(30,150,200,16,16,"Count:");  //显示当前计数值
	LCD_ShowString(30,170,200,16,16,"Send Data:");  //提示发送的数据
	LCD_ShowString(30,210,200,16,16,"Receive Data:");  //提示接收到的数据
	
	while(1)
	{
		key=KEY_Scan(0); //获取Key按键扫描函数
		if(key==1)  //KEY0按下,显示发送一位数据
		{
			for(i=0;i<5;i++)
			{
				//因为定义的RS485Buf[5];所以需要通过循环来填充发送缓冲区,每填充一个数据,下一次的填充就需要在本次的数据位基础之上进行填充,所以就需要一个累加数CNT
				RS485Buf[i]=CNT+i;//CNT是一个累加数,一单按键按下,就以CNT为基准进行数据发送
				LCD_ShowxNum(30+i*32,190,RS485Buf[i],3,16,0x80);//显示每一次发送缓冲区收到的数据
				//LCD是通过x y坐标来表示某一确切位置的
				//30+i*32,:30是起始x的坐标,一个坐标放一个字节,一个字节是8位,4*8=32,所以每按键发送一个字节,下一次按键发送的字节和前一个字节有4位的间距,也可以理解为两个字符间保持间距
				//显示的数字是:RS485Buf[i]  数字占3位  
				//0x80表示1000 0000  表示填充0
			}
			RS485_Send_Data(RS485Buf,5); //按键发送,一次发送5个字节
		}
		//通过上面的if语句,按键已经发送了数据,那么紧接着就需要接收这些数据字节
		RS485_Receive_Data(RS485Buf,&key); //调用接收数据函数,接收的地址就是RS485Buf,接收的字节长度就是&key,&key表示按键按了多少次,
        //因为每按一次发送的字节长度是确定的,那么按了多少次后整个字节长度len也就是确定的
		if(key)//只要key为真,就表示接收到了字节
		{
			if(key>5)//因为定义的接收缓存区最大值就是5
				key=5;
			for(i=0;i

7.2 RS485.c

#include "stm32f4xx.h"              
#include "RS485.h"
#include "delay.h"


//因为RS485是半双工通讯方式,所以发送和接收是不能同时进行的,这时候就要通过函数来定义什么时候发送,什么时候接收了

//如果使能了串口接收功能
#if EN_USART2_RX   // #if 和 #endif 是同时使用的函数,表示如果定义了if后面的语句,才会进入循环中。

u8 RS485_RX_BUF[64];//接收缓冲区,最大可以储存64个字节
u8 RS485_RX_CNT=0;  //接收到的数据长度

void USART2_IRQHandler(void) //表示如果使能了串口的接收功能,就会执行串口2的中断接收配置
	//该中断服务函数表示接收来自485总线的数据,将其存放在RS485_RX_BUF里面。
{
	u8 res;
	if(USART_GetITStatus(USART2,USART_IT_RXNE)!=RESET)//获取中断状态位,判断是否接收到了数据
		//通过以往学习,我们知道状态寄存器的TXE位是发送缓冲区的标志位,当该位为0时,表示发送缓冲区非空;该位为1时,表示发送缓冲区为空,也就表示可以发送下一个数据了
	//状态寄存器的RXNE位是接收缓冲区的标志位,当该位为0时,表示接收缓冲区非空,暂时还不能接收下一个数据;该位为1时,表示接收缓冲区为空,可以接收了
	{
		res=USART_ReceiveData(USART2);  //通过调用串口接收函数,把从串口2接收到的数据赋给res
		if(RS485_RX_CNT<64)  //因为定义的接收缓冲区是静态定义的,所以有其接收的最大值限制,如果大于64个字节,那么这个空间是不够存储的
		{
			RS485_RX_BUF[RS485_RX_CNT]=res;  //把接收的数据res赋给接收缓冲区,占用字节长度为RS485_RX_CNT,也就是发送的这一位数据的字节长度
			RS485_RX_CNT++;//我们知道通过串口接收数据是一位一位传的,所以需要启动循环,一位一位的传数据,
			//每传一位,接收到的数据长度就加1,因为下一次存放数据需要建立在上一次存放数据的基础之上。
		}
	}
}
#endif
//初始化RS485 
//初始化IO口 和 串口2
//bound:波特率
void RS485_Init(u32 bound)
{
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);  //使能GPIOA时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);   //使能USART2时钟
	
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource2,GPIO_AF_USART2); //GPIOA2复用为串口2
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource3,GPIO_AF_USART2); //GPIOA3复用为串口2
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF;
	GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_2 | GPIO_Pin_3;
	GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_UP;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_100MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_OUT; //设置为推挽输出
	GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_8;
	GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_UP;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_100MHz;
	GPIO_Init(GPIOG,&GPIO_InitStructure);
	
	//串口2初始化
	USART_InitTypeDef USART_InitStructure;
	USART_InitStructure.USART_BaudRate=bound; //设置波特率
	USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;  //无硬件数据流控制
	USART_InitStructure.USART_Mode=USART_Mode_Rx | USART_Mode_Tx; //模式为串口收发模式
	USART_InitStructure.USART_Parity=USART_Parity_No;  //无奇偶校验位
	USART_InitStructure.USART_StopBits=USART_StopBits_1;  //1个停止位
	USART_InitStructure.USART_WordLength=USART_WordLength_8b;  //字长为8位
	USART_Init(USART2,&USART_InitStructure);
	
	USART_Cmd(USART2,ENABLE); //使能串口2
	
	USART_ClearFlag(USART2,USART_FLAG_TC);  //清除串口2中断标志位
	
#if EN_USART2_RX //表示使能串口2的接收模式
                 //如果该 #if EN_USART2_RX 成立,那么就会进入串口2的中断服务程序,既然用到了中断,那么必须使能中断优先级NVIC

	USART_ITConfig(USART2,USART_IT_RXNE,ENABLE); //串口中断命令,USART_IT_RXNE是串口状态寄存器的接收缓存区的标志位,该函数表示开启接收中断
	
	//串口2中断优先级配置
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel=USART2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3; //抢占优先级3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=3;  //子优先级3
	NVIC_Init(&NVIC_InitStructure);
	
#endif

	RS485_TX_EN=0;   //初始化情况下默认引脚PG8是接收模式,然后通过两个判断函数判断是否进入接收中断函数
}

//RS485发送len个字节
//BufAddress:发送区首地址
//len:发送的字节数,因为定义接收缓冲区的大小为64个字节,所以发送的字节数原则上不大于64
void RS485_Send_Data(u8 *BufAddress,u8 len)
{
	u8 t;
	RS485_TX_EN=1;  //设置为发送模式
	for(t=0;t

7.3 RS485.h

#ifndef _RS485__H_
#define _RS485__H_

extern u8 RS485_RX_BUF[64]; //接收缓冲区,定义最大可以存放64个字节
extern u8 RS485_RX_CNT;     //接收到的数据长度

#define EN_USART2_RX 1   //0:不接受  1:接收
#define RS485_TX_EN PGout(8) //485模式控制,RS485芯片的RE引脚是低电平有效,DE引脚是高电平有效
							//所以低电平0表示接收使能;高电平1表示发送使能;

void RS485_Init(u32 bound);
void RS485_Send_Data(u8 *BufAddress,u8 len);
void RS485_Receive_Data(u8 *BufAddress,u8 *len);

#endif

        注:本节介绍的 485 总线时通过串口控制收发的,我们只需要将 P9 的跳线帽稍作改变(跳线帽COM2_RX接PA2_TX,COM2_TX接PA2_RX),该实验就变成了一个 RS232 串口通信实验了,通过对接两个开发板的 RS232 接口,即可得到同样的实验现象!!!

你可能感兴趣的:(STM32,stm32,单片机,嵌入式硬件)