STM32复习(四)--- RS485串口通讯

一、RS485通讯协议简介

        RS485(也称为 EIA-485TIA-485)是一种常用于工业和通信领域的串行通信标准。它定义了一个电气特性规范,用于在多点网络中进行数据传输,特别适用于需要长距离、高速传输和抗干扰的应用场景。RS485标准常用于工业自动化、传感器、智能仪表等系统中。

特点:

        1.差分信号传输

        RS485使用差分信号进行数据传输。它由两根信号线(A和B)组成:

  • A线(+)B线(-) 之间的电压差传输数据。
  • 当电流在这两根线之间的方向发生变化时,接收端会读取这种变化并解码为数据位。

        相比单端信号,差分信号对噪声的抗干扰能力更强,因为噪声通常影响两条线的相同程度,因此它们之间的电压差不会受到噪声的显著影响。

        2.多点通信
        RS485支持多达32个设备(发送器和接收器总和)连接在同一总线上。这使得它非常适合于多节点的网络应用。

        3.长距离传输
        RS485的通信距离可以达到1200米(约4000英尺),这取决于传输速率和电缆质量。在低速率下(如9600bps),可以实现更长的传输距离。

        4.高传输速率
        RS485支持较高的传输速率(如10 Mbps),尽管速度和距离通常是相互制约的。

         5.抗干扰能力强
        由于使用差分信号,RS485能在电磁干扰(EMI)较强的环境下稳定工作,因此常用于工厂、自动化设备和恶劣环境下的通信。

RS-485与RS-232通讯协议的特性对比:  

通讯标准信号线通讯方向电平标准通讯距离通讯节点数
RS232TXD,RXD,GND全双工

逻辑1:+3V ~ +15V

逻辑0:-15V ~ -3V

100米以内只有两个节点
RS485差分线AB半双工

逻辑1:+2V ~ +6V

逻辑0:-6V ~ -2V

1200米支持多个节点,支持多个主设备,任意节点间可以相互通讯

差分信号线具有很强的干扰能力,特别适合应用于电磁环境复杂的工业控制环境中,RS-485协议主要是把RS-232的信号改进成差分信号,从而大大提高了抗干扰特性

二、 RS485通讯实验

 本实验我们采用土壤传感器,RS485转TTL模块以及STM32F103C8T6最小系统板来实现。

1.土壤传感器

以下是土壤传感器的部分参数:

这个协议内容用到了ModBus-Rtu,感兴趣的小伙伴可以看一下这个https://blog.youkuaiyun.com/as480133937/article/details/123197782

2.RS485转TTL模块

实验原理:
485通讯实质上就是软件层的串口通讯,再加上物理层上的485芯片,将TTL信号转化为差分信号进行传输,对待上就按照串口通讯就行。
编程思想:
DE: 1 发送使能;0发送禁止
RE: 0 接收使能;1接收禁止

一般将DE和RE连在一起,这样就是高电平发送,低电平接收

通过单片机串口2的TXD(PA2)将数据发送出去,RXD(PA3)进行接收数据,然后通过串口1打印接收到的数据 。

由于是通过485协议发送数据,每次发送前要对485传输方式设置为发送模式,完成后要改为接收模式,由于是半双工,发送完要有一定的延时,确保数据不会丢失;

 3.接线图

4、程序代码

Usart.h

#ifndef __USART_H__
#define __USART_H__

extern uint8_t Serial_TxPacket[];
extern uint8_t Serial_RxPacket[];

void my_Usart_Init2(void);
void my_Usart_Init(void);
void My_Usart_Send_Byte(USART_TypeDef* USARTx, uint16_t Data);  //发送一个字节
void My_Usart_SendArray(uint8_t *Array,uint16_t Length);  //发送一个数组(用串口2)
void My_Usart_SendArray2(uint8_t *Array,uint16_t Length); //发送一个数组(用串口1)
void My_Usart_Send_String(USART_TypeDef* USARTx, char * str); //发送字符串
uint8_t Serial_GetRxFlag(void);  //实现读后自动清除的功能

#endif

Usart.c

#include "stm32f10x.h"
#include "Usart.h"
#include "stdio.h"
#include "RS485.h"

uint8_t Serial_TxPacket[8];
uint8_t Serial_RxPacket[12];
uint8_t Serial_RxFlag;

void my_Usart_Init2(void)   //初始化串口2
{
  GPIO_InitTypeDef gpio_initstruct;
	USART_InitTypeDef usart_initstruct;
	NVIC_InitTypeDef nvic_initstruct;
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);
	
//Tx  PA2
	gpio_initstruct.GPIO_Mode  = GPIO_Mode_AF_PP;//发送数据固定为复用推挽输出
	gpio_initstruct.GPIO_Pin   = GPIO_Pin_2;
	gpio_initstruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&gpio_initstruct);
//Rx  PA3	
	gpio_initstruct.GPIO_Mode = GPIO_Mode_IPU;//接收数据为浮空输入或上拉输入
	gpio_initstruct.GPIO_Pin  = GPIO_Pin_3;
	GPIO_Init(GPIOA,&gpio_initstruct);
	
	usart_initstruct.USART_BaudRate = 9600;
	usart_initstruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//不使用流控
	usart_initstruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	usart_initstruct.USART_Parity = USART_Parity_No;//不使用校验位
	usart_initstruct.USART_StopBits = USART_StopBits_1;//一位停止位
	usart_initstruct.USART_WordLength = USART_WordLength_8b;//因为不使用校验位所以字节长度为八个
	USART_Init(USART2,&usart_initstruct);
	
	
	USART_ITConfig(USART2,USART_IT_RXNE,ENABLE);
	
	nvic_initstruct.NVIC_IRQChannel = USART2_IRQn;
	nvic_initstruct.NVIC_IRQChannelPreemptionPriority = 2;
	nvic_initstruct.NVIC_IRQChannelSubPriority = 2;
	nvic_initstruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&nvic_initstruct);
	
	USART_Cmd(USART2,ENABLE);//一定记得配置完后使能USART2外设
}


void my_Usart_Init(void)    //初始化串口1
{
	GPIO_InitTypeDef gpio_initstruct;
	USART_InitTypeDef usart_initstruct;
	NVIC_InitTypeDef nvic_initstruct;
		
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1,ENABLE);
	
	
//Tx  PA9
	gpio_initstruct.GPIO_Mode  = GPIO_Mode_AF_PP;//发送数据固定为复用推挽输出
	gpio_initstruct.GPIO_Pin   = GPIO_Pin_9;
	gpio_initstruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&gpio_initstruct);
//Rx  PA10	
	gpio_initstruct.GPIO_Mode = GPIO_Mode_IPU;//接收数据为浮空输入或上拉输入
	gpio_initstruct.GPIO_Pin  = GPIO_Pin_10;
	gpio_initstruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&gpio_initstruct);
	
	usart_initstruct.USART_BaudRate = 115200;
	usart_initstruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//不使用流控
	usart_initstruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	usart_initstruct.USART_Parity = USART_Parity_No;//不使用校验位
	usart_initstruct.USART_StopBits = USART_StopBits_1;//一位停止位
	usart_initstruct.USART_WordLength = USART_WordLength_8b;//因为不使用校验位所以字节长度为八个
	USART_Init(USART1,&usart_initstruct);
	
	USART_Cmd(USART1,ENABLE);//一定记得配置完后使能USART1外设
	USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
	
	nvic_initstruct.NVIC_IRQChannel = USART1_IRQn;
	nvic_initstruct.NVIC_IRQChannelPreemptionPriority = 1;
	nvic_initstruct.NVIC_IRQChannelSubPriority = 1;
	nvic_initstruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&nvic_initstruct);
	
}

//发送一个字节
void My_Usart_Send_Byte(USART_TypeDef* USARTx, uint16_t Data)
{
  USART_SendData(USARTx, Data);
	while(USART_GetFlagStatus(USARTx,USART_FLAG_TXE) == RESET);//‘发送数据寄存器空’等于0,即说明寄存器中还有数据,因此需要留在while循环中

}
//发送一个数组
void My_Usart_SendArray(uint8_t *Array,uint16_t Length)
{
	uint16_t i;
	for(i=0;i<Length;i++)
	{
		My_Usart_Send_Byte(USART2,Array[i]);
	}
}
//发送一个数组
void My_Usart_SendArray2(uint8_t *Array,uint16_t Length)
{
	uint16_t i;
	for(i=0;i<Length;i++)
	{
		My_Usart_Send_Byte(USART1,Array[i]);
	}
}
//发送一个字符串
void My_Usart_Send_String(USART_TypeDef* USARTx, char * str)
{
	uint16_t i=0;
	do
	{
		
		My_Usart_Send_Byte(USARTx,*(str+i));//逐个字符发送
		i++;
	
	}while(*(str+i) != '\0');// 直到遇到字符串结束符
	while(USART_GetFlagStatus(USARTx,USART_FLAG_TC) == RESET);

}

uint8_t Serial_GetRxFlag(void)//实现读后自动清除的功能
{
	if(Serial_RxFlag == 1)
	{
		Serial_RxFlag=0;
		return 1;
	}
	return 0;
}

int fputc(int ch,FILE*f)//对printf函数重定向
{
	My_Usart_Send_Byte(USART1,ch);
	return ch;
}

void USART1_IRQHandler(void)//中断函数名称看开始文件
{
	if(USART_GetITStatus(USART1,USART_IT_RXNE) == SET)
	{

		USART_ClearITPendingBit(USART1,USART_IT_RXNE);//清除标志位,如果读取DR是可以自动清除的
	}
}

void USART2_IRQHandler()
{
	if(USART_GetITStatus(USART2,USART_IT_RXNE) == SET)
	{
		static uint8_t RxState = 0;
		static uint8_t pRxState = 0;
		
		uint8_t RxData = USART_ReceiveData(USART2);
		
		if(RxState == 0)
		{
			if(RxData==0x01) //如果地址是0x01再接收其他返回的数据并存在Serial_RxPacket数组里
			{
				RxState=1;
				pRxState=0;
			}
		}
		else if(RxState == 1)
		{
			Serial_RxPacket[pRxState]=RxData;
			
			pRxState ++;
			
			if(pRxState>=12)
			{
					RxState=0;
					Serial_RxFlag=1;
			}
		}

		USART_ClearITPendingBit(USART2,USART_IT_RXNE);//清除标志位,如果读取DR是可以自动清除的
	}
}

这样我们就把发送接收数据的串口2和打印数据的串口1配置好了,接下来配置控制RS485发送还是接收的PA7

RS485.h

#ifndef __RS485_H__
#define __RS485_H__

void RS485_Init(void);

#endif

RS485.c

#include "stm32f10x.h"
#include "RS485.h"

void RS485_Init(void)
{
	GPIO_InitTypeDef gpio_initstruct;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	gpio_initstruct.GPIO_Mode  = GPIO_Mode_Out_PP;
	gpio_initstruct.GPIO_Pin   = GPIO_Pin_7;       
	gpio_initstruct.GPIO_Speed = GPIO_Speed_50MHz;
	
	GPIO_Init(GPIOA,&gpio_initstruct);

}

main.c

#include "stm32f10x.h" // Device header
#include "stdio.h"
#include "dht11.h"
#include "delay.h"
#include "Usart.h"
#include "RS485.h"
#include "relay.h"
#include "OLED.h"

int main(void)
{
	
	Relay_Init();
	my_Usart_Init();
	my_Usart_Init2();
	RS485_Init();
	
	Serial_TxPacket[0]=0x01;
	Serial_TxPacket[1]=0x03;
	Serial_TxPacket[2]=0x00;
	Serial_TxPacket[3]=0x00;
	Serial_TxPacket[4]=0x00;
	Serial_TxPacket[5]=0x04;
	Serial_TxPacket[6]=0x44;
	Serial_TxPacket[7]=0x09;
	
	
	u8 tem1=0;
	u8 hum1=0;
	
	float temp=0;
	float humi=0;
	 u16  EC=0;
	float PH=0;
	
	
	while(1)
	{
		delay_s(5); //5s采集一次数据
		GPIO_SetBits(GPIOA,GPIO_Pin_7); //给高电平,设置为发送模式
		My_Usart_SendArray(Serial_TxPacket,8); //将询问帧发给土壤传感器
		delay_ms(1); //发送完延时1ms保证数据不会丢失
		GPIO_ResetBits(GPIOA,GPIO_Pin_7); //给低电平,设置为接收模式,接收土壤传感器返回的数据
		if(Serial_GetRxFlag() == 1) //如果返回的数据满足中断函数的要求将会返回1
		{
			temp  = ((float)(Serial_RxPacket[4]*256 + Serial_RxPacket[5]))/10.0;
			humi  = ((float)(Serial_RxPacket[2]*256 + Serial_RxPacket[3]))/10.0;
			 EC   = Serial_RxPacket[6]*256 + Serial_RxPacket[7];
			 PH   = ((float)(Serial_RxPacket[8]*256 + Serial_RxPacket[9]))/10.0;
			printf("\r\ntemp: %.1f \r\nhumi: %.1f \r\nEC: %d \r\nPH: %.1f \r\n",temp,humi,EC,PH);
			
		} //打印收到的各个数据
		
            
            if(humi <= 30)
			{
				GPIO_ResetBits(GPIOA,GPIO_Pin_0);
			}
			else
			{
				GPIO_SetBits(GPIOA,GPIO_Pin_0);
			}
		
		//这个后续可以加一个继电器来控制水泵的开关,如果湿度过低就打开水泵,这里就先不加了
		
		
		
	}
}

三、串口助手显示 

这个Airtemp和Airhumi是我测试的空气温度和湿度,可以不看

<think>好的,我现在需要帮助用户查找关于RS485转TTL模块的信息,包括介绍、购买和使用建议。首先,我得先理解用户的需求。用户可能正在开发一个项目,需要将RS485信号转换为TTL电平,以便微控制器(比如Arduino或STM32)能够处理这些信号。他们可能对如何正确接线、模块的选择以及使用中的注意事项有疑问。首先,我应该回顾已有的引用内容,看看有哪些信息可以利用。引用1提到了接线方式和注意事项,引用2说明了模块之间的连接方法以及RE和DE引脚的控制,引用3涉及Modbus协议的应用,引用4则提供了STM32控制RS485的代码示例。这些信息对于回答用户的问题应该很有帮助。 接下来,我需要整理这些信息,分门别类地呈现给用户。介绍部分需要解释模块的基本功能和应用场景,比如工业控制、传感器通信等。购买建议应该包括电压匹配、芯片品牌、隔离保护、通信速率和接口类型等关键因素。使用部分则需要详细说明接线步骤、收发控制、软件配置和调试技巧。用户可能对如何选择适合自己项目的模块不太清楚,所以购买建议要具体,比如推荐3.3V或5V系统,选择Maxim或TI的芯片,是否需要隔离。同时,使用中的常见问题,比如接线错误导致的通信失败,收发模式切换不及时,或者波特率设置不匹配,都需要提到解决方案。另外,用户可能对代码实现感兴趣,特别是如何用微控制器控制RE/DE引脚,以及如何处理半双工通信。引用4中的STM32代码示例可以作为一个很好的参考,说明如何切换发送和接收模式,并确保数据完整。还需要考虑用户的实际操作中可能遇到的问题,比如信号干扰、长距离传输的稳定性,是否需要终端电阻等。这些细节虽然引用中没有直接提到,但根据RS485的一般知识,应该补充进去,帮助用户全面了解。最后,生成的相关问题需要围绕用户可能进一步探索的方向,比如具体型号推荐、故障排除、协议实现等,这样用户可以根据自己的需求继续提问。总结来说,我需要将技术信息结构化,用清晰的中文分点说明,确保用户能够一步步理解如何选择、连接和使用RS485转TTL模块,同时提供实际代码示例和常见问题解决方法,确保回答实用且全面。</think>### RS485转TTL模块介绍、购买与使用指南 #### **一、模块功能与适用场景** RS485转TTL模块用于将工业设备中常见的$RS485$差分信号(抗干扰能力强,适合远距离通信)转换为$TTL$电平信号($0$-$5V$或$0$-$3.3V$),以便微控制器(如STM32、ESP32、Arduino)直接处理。其典型应用包括: - 工业传感器数据采集(如温度、压力传感器) - Modbus RTU协议通信[^3][^4] - 长距离($1200$米以内)设备组网 --- #### **二、购买建议** 1. **电压匹配** - 若主控为$5V$系统(如Arduino Uno),选择支持$5V$供电的模块; - 若主控为$3.3V$系统(如ESP32),需选$3.3V$兼容模块[^2]。 2. **芯片选择** - 推荐采用**Maxim MAX485**或**TI SN65HVD72**芯片的模块,稳定性高、抗干扰强。 3. **隔离保护** - 若用于工业环境,选择带**光耦隔离**和TVS保护的模块,可防浪涌和地线干扰。 4. **通信速率** - 常规模块支持$0$-$10Mbps$速率,需根据项目需求选择(例如Modbus常用$9600$-$115200$bps[^3])。 5. **接口类型** - 选配带接线端子或杜邦线接口的模块,便于快速连接。 --- #### **三、接线与使用步骤** ##### **1. 接线方式** - **模块与RS485设备连接**(以A/B线制为例): $$ \begin{aligned} \text{模块A端} &\to \text{设备A+} \\ \text{模块B端} &\to \text{设备B-} \\ \text{GND} &\to \text{共地(防电位差干扰)} \end{aligned} $$ 注意:多个设备需**总线式并联**,避免星型连接[^1][^2]。 - **模块与MCU连接**: ```plaintext TTL_TX → MCU_RX TTL_RX → MCU_TX VCC → 3.3V/5V GND → GND RE/DE → MCU_GPIO(收发控制引脚) ``` ##### **2. 收发模式控制** RS485为半双工通信,需通过**RE(接收使能)**和**DE(发送使能)**引脚切换状态: - **接收模式**:RE=低电平,DE=低电平 - **发送模式**:RE=高电平,DE=高电平 代码示例(STM32):
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值