RS485

一、简介

RS485 通信速率快,最大传输速度可以达到 10Mb/s 以上。

RS485 内部的物理结构,采用的是平衡驱动器和差分接收器的组合,抗干扰能力也大大增加,即抗噪声干扰性好。

传输距离最远可以达到 1200 米左右,但是它的传输速率和传输距离是成反比的,只有在 100Kb/s 以下的传输速度,才能达到最大的通信距离,如果需要传输更远距离可以使用中继。

传输距离远,支持节点多。RS485 总线最长可以传输 1200m 以上(速率≤100Kbps)一般最大支持 32 个节点,如果使用特制的 485 芯片,可以达到 128 个或者 256 个节点,最大的可以支持到 400 个节点。

RS485 的接口非常简单,与 RS232 所使用的 MAX232 是类似的,只需要一个 RS485转换器,就可以直接与单片机的 UART 串口连接起来,并且使用完全相同的异步串行通信协议。但是由于 RS485 是差分通信,因此接收数据和发送数据是不能同时进行的,也就是说它是一种半双工通信

RS485 采用两根通信线,通常用 A 和 B 或者 D+和 D-来表示。逻辑“1”以两线之间的电压差为+(0.2~6)V 表示,逻辑“0”以两线间的电压差为-(0.2~6)V 来表示,是一种典型的差分通信。

RS485一主多从:

通常情况下RS485需要接2个匹配电阻,其阻值要求等于传输电缆的特性阻抗(一般为120Ω)。

二、MODBUS-RTU

(1)简介

   MODBUS协议是Modicon公司发表的一种串行通信协议,属于OSI模型中应用层的协议,现广泛应用于工业控制领域,它的主要特点是免费开放、支持多种电气接口(如RS-232、RS-485),传输介质可以是双绞线、光纤、无线等。

通信的发起端只能是主机,从机负责响应主机的请求,也就是说从机的通信都是被动的,它不会主动对主机发起通信;(主机没有地址

主机对从机发起通信的模式有两种:

①单播模式:主机指定特定的子机地址(1~247),子机接到主机的请求后,向主机返回一个报文作为响应;

②广播模式:主机向所有子机发送请求,广播地址为0,当主机向0号地址发数据包的时候,每一个从机设备都会收到数据包,子机接收到主机的广播命令后不需要返回报文作为响应。

(2)MODBUS通信方式

①基于串口的Modbus-ASCII :ASCII模式使用文本格式的数据,其中每个字节都表示一个字符。在ASCII模式下,Modbus协议使用ASCII码来表示数据。ASCII模式通常用于短距离的串行通信,例如在同一个局域网内的设备之间进行通信。

②基于串口的Modbus-RTU :每个 8 位字节含有两个 4 位十六进制字符,这种模式的主要优点是较高的数据密度,在相同的波特率下比 ASCII 模式有更高的吞吐率,每个报文必须以连续的字符流传送,通常采用CRC-16_Modbus校验算法。

③基于网口的Modbus-TCP: Modbus-TCP基于TCP/IP协议。

在应用当中首先要确认的就是使用哪个通信方式工作,主从机必须工作在同一种模式下,且其它串行参数也要设置为相同,如波特率等;在嵌入式工业领域中最常用的还是RTU模式,下面就着重以RTU来解析。

(3)报文格式

子节点地址对应子机地址,功能代码下文会解释,简单理解为配置读寄存器还是写寄存器。

帧最大为256字节,每个字节为11位,传输顺序为从最低有效位开始(即起始位开始);

如果设置为奇/偶校验则为(起始位1bit+8bit数据+1bit奇偶校验位+1bit停止位);

如果设置为无校验则为(起始位1bit+8bit数据+2bit停止位)。

帧格式:

(4)RTU报文帧时序要求:

报文帧间至少为 3.5 个字符时间的空闲间隔:

整个报文帧必须以连续的字符流发送,如果两个字符之间的空闲间隔大于 1.5 个字符时间,则报文帧被认为不完整应该被接收节点丢弃。

(5)RTU功能码

Modbus-RTU协议不支持Modbus协议中的功能码01和02,因为它们使用了不同的数据结构来表示输入和线圈状态。此外,Modbus-RTU协议还支持一些额外的功能码,用于读取和写入保持寄存器,以便在通信过程中保持某些状态信息。这里仅介绍0x03、0x06、0x10。

1、读多个寄存器功能码0x03
使用该功能码读取保持寄存器连续块的内容;

寄存器地址由2个字节表示(范围为0x0000~0xFFFF);

寄存器数量也由2个字节表示(范围为0~0x7d)。

示例:

主机读取子机地址为01,寄存器地址为0x0001为起始的连续10个寄存器值(以下为十六进制数)

主机请求帧:01  03  00  01  00  0A  CRC_L  CRC_H(即为子机地址+功能码+寄存器地址高字节+寄存器地址低字节+寄存器数量高字节+寄存器数量低字节+CRC低字节+CRC高字节);

子机响应帧:01 03  06  00  02  00  04  00  06(即为子机地址+功能码+读取的字节数+DATA1_H+DATA1_L+DATA2_H+DATA2_L+DATA3_H+DATA3_L+CRC低字节+CRC高字节)。

2、 写单个寄存器功能码0x06
使用该功能码向单个寄存器写入值;

寄存器地址由2个字节表示(范围为0x0000~0xFFFF);

寄存器数量也由2个字节表示(范围为0x0000~0xFFFF)。

示例:

主机向子机地址为01,寄存器地址为0x0001写入0x03值(以下为十六进制数)

主机请求帧:01  06  00  01  00 03  CRC_L  CRC_H(即为子机地址+功能码+寄存器地址高字节+寄存器地址低字节+写入值高字节+写入值低字节+CRC_L_CRC_H);

子机响应帧:01  06  00  01  00 03  CRC_L  CRC_H(即为子机地址+功能码+输出寄存器地址高字节+输出寄存器地址低字节+输出写入值高字节+输出写入值低字节+CRC_L_CRC_H)。

3、写多个寄存器值0x10
使用该功能码写连续寄存器块(1 至约 120 个寄存器);

同上面两个功能码同理,就不一一介绍了

CRC校验:
基于循环冗余校验 (CRC - Cyclical RedundancyChecking) 算法的错误检验域;

CRC 检验是对整个报文帧内容的校验,即使没有奇偶校验位,也要执行此检验;

CRC 的值由主机端根据算法计算而来,它由两个字节组成,低字节在前,高字节为报文发送的最后一个字节;从机接受到报文后也会去计算CRC值,计算结果与主机发送过来的CRC值比较,相等即为校验通过,传输的数据无误。

三、历程

#include "rs485.h"
#include "stm32f10x.h"
#include "SystemTick.h"
#include <stdio.h>

void RS485_USART3_Init(void)
{
	GPIO_InitTypeDef GPIO_InitUsart3Struct;//引脚初始化定义
	USART_InitTypeDef USART3_InitStruct;	//串口初始化的定义
	NVIC_InitTypeDef NVIC_USART3_InitStruct;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);
	
	USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
	
	//485 RE DE 引脚的控制
	GPIO_InitUsart3Struct.GPIO_Mode  = GPIO_Mode_Out_PP;
	GPIO_InitUsart3Struct.GPIO_Pin   = GPIO_Pin_0;
	GPIO_InitUsart3Struct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitUsart3Struct);
	
	//Pb10(usart3_tx)
	GPIO_InitUsart3Struct.GPIO_Mode  = GPIO_Mode_AF_PP;
	GPIO_InitUsart3Struct.GPIO_Pin	 = GPIO_Pin_10;
	GPIO_InitUsart3Struct.GPIO_Speed = GPIO_Speed_10MHz;
	GPIO_Init(GPIOB, &GPIO_InitUsart3Struct);
	
	//Pb11(usart3_rx)
	GPIO_InitUsart3Struct.GPIO_Mode  = GPIO_Mode_IN_FLOATING;
	GPIO_InitUsart3Struct.GPIO_Pin   = GPIO_Pin_11;
	GPIO_Init(GPIOB, &GPIO_InitUsart3Struct);
	
	USART3_InitStruct.USART_BaudRate			= 115200;
	USART3_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART3_InitStruct.USART_Mode				= USART_Mode_Rx | USART_Mode_Tx;
	USART3_InitStruct.USART_Parity				= USART_Parity_No;
	USART3_InitStruct.USART_StopBits			= USART_StopBits_1;
	USART3_InitStruct.USART_WordLength			= USART_WordLength_8b;
	USART_Init(USART3, &USART3_InitStruct);
	USART_Cmd(USART3, ENABLE);
	
	NVIC_USART3_InitStruct.NVIC_IRQChannel					 = USART3_IRQn;
	NVIC_USART3_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;
	NVIC_USART3_InitStruct.NVIC_IRQChannelSubPriority		 = 2;
	NVIC_USART3_InitStruct.NVIC_IRQChannelCmd				 = ENABLE;

	NVIC_Init(&NVIC_USART3_InitStruct);
	
	RX_ENABLE;					//485初始化成接收使能

}
 

//发送字符RS485不能直接调用
void RS485_SendByte(USART_TypeDef* USARTx, uint16_t Data)
{
	USART_SendData(USARTx,Data);  
	//等待发送数据寄存器中的数据被取走
	while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);//等待高电平,数据发送完是高电平
}

//发送字符串
void RS485_SendString(USART_TypeDef* USARTx, char *str)
{
	TX_ENABLE;
	uint16_t i = 0;
	do
	{
		RS485_SendByte(USARTx, *(str+i));
		i++;
		
	}while(*(str+i) != '\0');
	//等待发送移位寄存器(为空)
	while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);//等待高电平,数据发送完是高电平
	RX_ENABLE;
	
}




//串口三RS485
void USART3_IRQHandler(void)
{
//	u8 res; 
	static char tmp; 
	if(USART_GetITStatus(USART3, USART_IT_RXNE) ==SET )//接收到串口的数据
	{
		USART_ClearITPendingBit(USART3, USART_IT_RXNE);
		tmp = USART_ReceiveData(USART3);
		USART_SendData(USART1, tmp);
		while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);//等待发送完成
	}
}

int  main()
{
	initSysTick();
	usart_init();
	RS485_USART3_Init();
	INIT_LED();
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	printf("这是一个RS485实验\r\n");
	ms_delay(1000);
	//USARTSsendStr(USART3, "485");//该函数可以发送但是串口3接收不到数据,这是485的半双工的原因(有两个开发板可以使用一个发送 另一个接收)
	ms_delay(1000);

	
	while(1)
	{

		
		ms_delay(1000);
		ms_delay(1000);

	}
		 
   
}

引用:https://blog.csdn.net/weixin_49576307/article/details/132231422

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值