【STM32】485接口原理

发布于:2025-07-26 ⋅ 阅读:(18) ⋅ 点赞:(0)

485 通信实验

这篇文章是对 RS485通信 的原理、硬件连接、接口芯片(SP3485)、总线结构等都有详尽的说明。我们在此处进行清晰有条理的讲解整理,便于学习和实验操作。

在了解485接口通信原理之前,我们先复习一下串口

串口是一种接口标准,它规定了接口的电气标准,简单说只是物理层的一个标准。没有规定接口插件电缆以及使用的协议,所以只要我们使用的接口插件电缆符合串口标准就可以在实际中灵活使用,在串口接口标准上使用各种协议进行通讯及设备控制。


典型的串行通讯标准是RS232RS485,它们定义了电压,阻抗等,但不对软件协议给予定义。

RS232接口缺陷:
(1)接口的信号电平值较高(+/-12V),易损坏接口电路的芯片。 
(2)传输速率较低,在异步传输时,波特率为20Kbps。
(3)接口使用一根信号线和一根信号返回线而构成共地的传输形式,这种共地传输容易产生共模干扰,所以抗噪声干扰性弱。
(4)传输距离有限,最大传输距离标准值为50英尺,实际上也只能用在50米左右。 

🔹 第一部分:RS485 接口原理

RS485 是什么?

485(一般称作RS485/EIA-485)是隶属于OSI模型物理层的电气特性规定为2线,半双工,多点通信的标准。它的电气特性和RS-232大不一样。用缆线两端的电压差值来表示传递信号。RS485仅仅规定了接受端和发送端的电气特性。它没有规定或推荐任何数据协议。

简单点说,RS485 是一种用于串行通信的物理层标准,主要特性包括:

  • 差分传输(A/B 两线电压差)
  • 半双工通信(收发不能同时)
  • 多点通信(支持多个节点)
  • 传输距离远(可达 1200 米)
  • 抗干扰能力强
📏 RS485 与 RS232 对比简表:
特性 RS232 RS485
信号类型 单端信号 差分信号
电平 ±12V ±2~6V
接口形式 点对点 多点总线
通信距离 < 50 米 > 1200 米
抗干扰性
通信方式 全双工 半双工
RS485的特点包括:

接口电平低,不易损坏芯片。 RS485的电气特性:逻辑“1” 以两线间的电压差为+(2~6)V 表示;逻辑“0” 以两线间的电压差为-(2~6)V 表示。接口信号电平比RS232降低了,不易损坏接口电路的芯片。
传输速率高。 10米时,RS485的数据最高传输速率可达35Mbps,在1200m时,传输速度可达100Kbps。
抗干扰能力强。 RS485接口是采用平衡驱动器和差分接收器的组合,抗共模干扰能力增强,即抗噪声干扰性好。
传输距离远,支持节点多。 RS485总线最长可以传输1200m以上(速率≤100Kbps)一般最大支持32个节点,如果使用特制的485芯片,可以达到128个或者256个节点,最大的可以支持到400个节点。


🔹 第二部分:RS485 硬件结构与连接

RS485推荐使用在点对点网络中,线型,总线型,不能是星型,环型网络。理想情况下RS485需要2个匹配电阻,其阻值要求等于传输电缆的特性阻抗(一般为120Ω)。没有特性阻抗的话,当所有的设备都静止或者没有能量的时候就会产生噪声,而且线移需要双端的电压差。没有终接电阻的话,会使得较快速的发送端产生多个数据信号的边缘,导致数据传输出错。485推荐的连接方式:

总线拓扑结构图:
在这里插入图片描述
总线型拓扑
A连A,B连B
首尾两端需加终端电阻(120Ω) → 抑制反射
避免星型/分支结构 → 增加干扰、波形反射

√ 在上面的连接中,如果需要添加匹配电阻,我们一般在总线的起止端加入,也就是主机和设备4上面各加一个120Ω的匹配电阻。

收发器SP3485

SP3485 收发器结构图:
在这里插入图片描述
图中A、B总线接口,用于连接485总线。RO是接收输出端,DI是发送数据收入端,RE是接收使能信号(低电平有效),DE是发送使能信号(高电平有效)。

引脚 名称 功能说明
1 RO 接收输出(接到 MCU RX)
2 RE 接收使能(低有效)
3 DE 发送使能(高有效)
4 DI 发送输入(接到 MCU TX)
6/7 A/B 差分数据线
8/5 VCC / GND 电源和地

✅ 控制方式(DE / RE):
发送模式:DE = 1, RE = 1(关闭接收)
接收模式:DE = 0, RE = 0(开启接收)
可将 DE 和 RE 连接到同一IO口控制

RS485 实际硬件原理图

注意:
1)R14和R17是两个偏置电阻,用来保证总线空闲时,AB之间的电压差都会大约200mV,避免总线空闲时压差不定逻辑混乱。
2)两个485接口连接,A连接A,B连接B。

在这里插入图片描述
💡 说明:

  • MCU 使用 USART2 (PA2/PA3) 进行串口通信
  • PD7 控制 DE/RE 引脚,实现收发模式切换
  • SP3485 芯片将 TTL 串口电平转换为 RS485 差分信号
  • R14/R17:偏置电阻(保持总线空闲时有稳定电压)
  • R20(120Ω):终端匹配电阻(抑制反射)

🔹 第三部分:实验程序讲解要点

🧱 1. 初始化串口(USART)
void USART2_Init(void) {
    // 配置PA2(TX),PA3(RX),波特率等
}
🧱 2. 初始化控制引脚(DE / RE)
void RS485_DIR_Init(void) {
    // PD7 设置为输出模式
}
🧱 3. 控制收发方向的宏定义
#define RS485_TX_EN()  GPIO_SetBits(GPIOD, GPIO_Pin_7)  // 发
#define RS485_RX_EN()  GPIO_ResetBits(GPIOD, GPIO_Pin_7) // 收
🧱 4. 发送函数
void RS485_Send(uint8_t *data, uint16_t len) {
    RS485_TX_EN(); // 切换到发送模式
    for (int i = 0; i < len; i++) {
        USART_SendData(USART2, data[i]);
        while (USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);
    }
    RS485_RX_EN(); // 切换回接收
}
🧱 5. 接收处理

可以使用中断 / 查询方式接收数据
接收模式时要保证 DE = 0,RE = 0

注意事项:

  1. 发送和接收需要控制方向切换(半双工)
  2. 匹配电阻必须加在总线两端(主机 + 末端设备)
  3. 偏置电阻保证空闲时有稳定电平差
  4. A对A、B对B连接,切勿反接
  5. 如果多个设备通信,要采用 协议约定主从通信(如Modbus

🔹第四部分: 实际代码案例(RS485接口通信数据收发)

示例一:

验完整代码(标准库实现)(模块化)
因为在 Keil 软件中,函数设计是模块划分的(代码整理):

实验目的:使用RS485接口通信进行数据的收发。

📄 rs485.h

#ifndef __RS485_H
#define __RS485_H			 
#include "sys.h"	 								  
							  
//////////////////////////////////////////////////////////////////////////////////
	  		  	
extern u8 RS485_RX_BUF[64]; 		//接收缓冲,最大64个字节
extern u8 RS485_RX_CNT;   			//接收到的数据长度

//模式控制
#define RS485_TX_EN		PDout(7)	//485模式控制.0,接收;1,发送.
//如果想串口中断接收,请不要注释以下宏定义
#define EN_USART2_RX 	1			//0,不接收;1,接收.




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


#endif	   

📄 rs485.c

#include "sys.h"		    
#include "rs485.h"	 
#include "delay.h"


#ifdef EN_USART2_RX   	//如果使能了接收


//接收缓存区 	
u8 RS485_RX_BUF[64];  	//接收缓冲,最大64个字节.
//接收到的数据长度
u8 RS485_RX_CNT=0;   		  
  
void USART2_IRQHandler(void)
{
	u8 res;	    
 
 	if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //接收到数据
	{	 
	 			 
		res =USART_ReceiveData(USART2); 	//读取接收到的数据
		if(RS485_RX_CNT<64)
		{
			RS485_RX_BUF[RS485_RX_CNT]=res;		//记录接收到的值
			RS485_RX_CNT++;						//接收数据增加1 
		} 
	}  											 
} 
#endif										 
//初始化IO 串口2
//pclk1:PCLK1时钟频率(Mhz)
//bound:波特率	  
void RS485_Init(u32 bound)
{  
  GPIO_InitTypeDef GPIO_InitStructure;
  USART_InitTypeDef USART_InitStructure;
 	NVIC_InitTypeDef NVIC_InitStructure;
 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOD, ENABLE);//使能GPIOA,D时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);//使能USART2时钟
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;				 //PD7端口配置
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOD, &GPIO_InitStructure);
 
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;	//PA2
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽
  GPIO_Init(GPIOA, &GPIO_InitStructure);
   
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;//PA3
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
  GPIO_Init(GPIOA, &GPIO_InitStructure);  

	RCC_APB1PeriphResetCmd(RCC_APB1Periph_USART2,ENABLE);//复位串口2
	RCC_APB1PeriphResetCmd(RCC_APB1Periph_USART2,DISABLE);//停止复位
 
	
 #ifdef EN_USART2_RX		  	//如果使能了接收
	USART_InitStructure.USART_BaudRate = bound;//波特率设置
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位数据长度
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;///奇偶校验位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//收发模式

  USART_Init(USART2, &USART_InitStructure); ; //初始化串口
  
	NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; //使能串口2中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; //先占优先级2级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级2级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
	NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
 
  USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//开启中断
   
  USART_Cmd(USART2, ENABLE);                    //使能串口 

 #endif

  RS485_TX_EN = 0;			//默认为接收模式
 
}

//RS485发送len个字节.
//buf:发送区首地址
//len:发送的字节数(为了和本代码的接收匹配,这里建议不要超过64个字节)
void RS485_Send_Data(u8 *buf,u8 len)
{
	u8 t;
	RS485_TX_EN=1;			//设置为发送模式
  	for(t=0;t<len;t++)		//循环发送数据
	{		   
		while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);	  
		USART_SendData(USART2,buf[t]);
	}	 
 
	while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);		
	RS485_RX_CNT=0;	  
	RS485_TX_EN=0;				//设置为接收模式	
}
//RS485查询接收到的数据
//buf:接收缓存首地址
//len:读到的数据长度
void RS485_Receive_Data(u8 *buf,u8 *len)
{
	u8 rxlen=RS485_RX_CNT;
	u8 i=0;
	*len=0;				//默认为0
	delay_ms(10);		//等待10ms,连续超过10ms没有接收到一个数据,则认为接收结束
	if(rxlen==RS485_RX_CNT&&rxlen)//接收到了数据,且接收完成了
	{
		for(i=0;i<rxlen;i++)
		{
			buf[i]=RS485_RX_BUF[i];	
		}		
		*len=RS485_RX_CNT;	//记录本次数据长度
		RS485_RX_CNT=0;		//清零
	}
}

📄 key.h

#ifndef __KEY_H
#define __KEY_H	 
#include "sys.h"
							  
//////////////////////////////////////////////////////////////////////////////////   	 


//#define KEY0 PEin(4)   	//PE4
//#define KEY1 PEin(3)	//PE3 
//#define WK_UP PAin(0)	//PA0  WK_UP

#define KEY0  GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_1)//读取按键0
//#define KEY1  GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)//读取按键1
#define WK_UP   GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)//读取按键3(WK_UP) 

 

#define KEY0_PRES 	1	//KEY0按下
#define KEY1_PRES	  2	//KEY1按下
#define WKUP_PRES   3	//KEY_UP按下(即WK_UP/KEY_UP)


void KEY_Init(void);//IO初始化
u8 KEY_Scan(u8);  	//按键扫描函数		


#endif

📄 key.c

#include "stm32f10x.h"
#include "key.h"
#include "sys.h" 
#include "delay.h"

								    
//按键初始化函数
void KEY_Init(void) //IO初始化
{ 
 	GPIO_InitTypeDef GPIO_InitStructure;
 
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC,ENABLE);//使能PORTA,PORTE时钟

	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_1;//KEY0-KEY1
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入
 	GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化GPIOE4,3

	//初始化 WK_UP-->GPIOA.0	  下拉输入
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0设置成输入,默认下拉	  
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.0

}


//按键处理函数
//返回按键值
//mode:0,不支持连续按;1,支持连续按;
//0,没有任何按键按下
//1,KEY0按下
//2,KEY1按下
//3,KEY3按下 WK_UP
//注意此函数有响应优先级,KEY0>KEY1>KEY_UP!!
u8 KEY_Scan(u8 mode)
{	 
	static u8 key_up=1;//按键按松开标志
	if(mode)key_up=1;  //支持连按		  
	if(key_up&&(KEY0 == 0 || WK_UP == 1))
	{
		delay_ms(10);//去抖动 
		key_up=0;
		if(KEY0==0)return KEY0_PRES;
		else if(KEY0 == 0)return KEY0_PRES;
		else if(WK_UP == 0)return WKUP_PRES;
	}else if(KEY0==1 && WK_UP==1)key_up=1; 	    
 	return 0;// 无按键按下
}

📄 main.c

#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"	 
#include "rs485.h"

 				 	
 int main(void)
 {	 
	u8 key;
	u8 i=0,t=0;
	u8 cnt=0;
	u8 rs485buf[5]; 
	 
	delay_init();	    	 //延时函数初始化	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
	uart_init(115200);	 	//串口初始化为115200
	LED_Init();		  		//初始化与LED连接的硬件接口
	KEY_Init();				//按键初始化		 	 
	RS485_Init(9600);	//初始化RS485      通信距离越短,收发的波特率就可以越高,反之通信距离越长,收发波特率就越低
	 
	printf("RS485 TEST !\r\nKEY0:Send \r\n");

	while(1)
	{
		key=KEY_Scan(0);
		if( WK_UP == 1 )//KEY0按下,发送一次数据
		{
			printf("Send Data: ");
			for(i=0;i<5;i++)
			{
				rs485buf[i]=cnt+i;//填充发送缓冲区
				
				printf(" %02X",rs485buf[i] );
 			}
			printf("\r\n");
			RS485_Send_Data(rs485buf,5);//发送5个字节 									   
		}		 
		RS485_Receive_Data(rs485buf,&key);
		if(key)//接收到有数据
		{
			printf("Receive Data: ");
			if(key>5)key=5;//最大是5个数据.
 			for(i=0;i<key;i++)
			{
				printf(" %02X",rs485buf[i]);
			}
			printf("\r\n");
 		}
		t++; 
		delay_ms(10);
		if(t==20)
		{
			LED0=!LED0;//提示系统正在运行	
			t=0;
			cnt++;
		}		   
	} 
}

示例二:Modbus 通信模块(一主多从)(Modbus协议)

代码目标实现:

基于 Modbus RTU 协议 的从机通信程序,使用 USART 接口实现串口通信,并支持 功能码 0x03(读保持寄存器) 和 0x06(写单个寄存器),其中也包含了 CRC 校验函数。

在这里插入图片描述
Modbus协议 适用于工业控制。

📄 modbus.h

#ifndef _MODBUS_H  //宏定义,定义文件名称
#define _MODBUS_H

/*---------------------------头文件引用--------------------------------------*/
#include "stm32f10x.h"

extern u8 Ram_try[10];
extern u8 Modbus_try[20];
extern u8 uart2_gs;// UART2接收数据指针

void crc_check(unsigned char *ptr,unsigned int len);
void SendData_Modbus(u8 len);
void GetData_Modbus(void);
void UART1_Send_Byte(u8 byte);




#endif //定义文件名称结束

📄 modbus.c

#include "modbus.h"
#include "delay.h"
#include "usart.h"
#include "string.h"

u8 L_CRC=0;
u8 H_CRC=0;
u8 Ram_try[10];
u8 Modbus_try[20];
u8 uart2_gs=0;// UART2接收数据指针

void crc_check(unsigned char *ptr,unsigned int len)
{
  unsigned long wcrc=0XFFFF;//预置16位crc寄存器,初值全部为1
  unsigned char temp;//定义中间变量
  int i=0,j=0;//定义计数
  for(i=0;i<len;i++)//循环计算每个数据
  {
    temp=*ptr&0X00FF;//将八位数据与crc寄存器亦或
    ptr++;//指针地址增加,指向下个数据
    wcrc^=temp;//将数据存入crc寄存器
    for(j=0;j<8;j++)//循环计算数据的
    {
      if(wcrc&0X0001)//判断右移出的是不是1,如果是1则与多项式进行异或。
      {
        wcrc>>=1;//先将数据右移一位
        wcrc^=0XA001;//与上面的多项式进行异或
      }
      else//如果不是1,则直接移出
      {
        wcrc>>=1;//直接移出
      }
    }
  }
  temp=wcrc;//crc的值
  L_CRC=wcrc;//crc的低八位
  H_CRC=wcrc>>8;//crc的高八位
}

//Modbus数据发送
//例:主机发送[1][03][00][6B][00][03][CRC低][CRC高]
//例:从机发送[1][03][06][02][2B][00][00][00][64][CRC低][CRC高]
/*
uart3_try[0]=0x01
uart3_try[1]=0x03
uart3_try[2]=0x06
*/
void GetData_Modbus(void)
{
    u16 num=0;
    u16 Address=0;
    u8 i=0;
    
	if(U1RX_ILDE_FLAG == 1)	//串口已经接收完成数据
	{
    if(Modbus_try[0]==0x01)//判断设备ID号(1~247)
    {
        crc_check(Modbus_try,6);// crc 校验
        if(L_CRC==Modbus_try[6] && H_CRC==Modbus_try[7])
        {
            Address=Modbus_try[2];
            Address=Address<<8|Modbus_try[3];
                
                
            if(Modbus_try[1]==0x03)//读操作
            {
                num=Modbus_try[4];
                num=num<<8|Modbus_try[5];
                Modbus_try[2]=num*2;
                
                for(i=0;i<Modbus_try[2];i++)
                {
                    Modbus_try[3+i]=Ram_try[Address+i];
                }
              
                crc_check(Modbus_try,3+Modbus_try[2]);// crc 校验
                Modbus_try[3+Modbus_try[2]]=L_CRC;
                Modbus_try[4+Modbus_try[2]]=H_CRC;
                
                SendData_Modbus(5+Modbus_try[2]);//发送数据
            }
            else if(Modbus_try[1]==0x06)//写操作
            {
              //[1][06][00][02][00][03][CRC低][CRC高]
                switch(Address)
                {
                  case 0:
                  {
                        Ram_try[0]=Modbus_try[4];
                        Ram_try[1]=Modbus_try[5];
										
												if((Modbus_try[4]<<8 | Modbus_try[5] ) == 0x0001)
													GPIO_ResetBits(GPIOB,GPIO_Pin_7);
												else
													GPIO_SetBits(GPIOB,GPIO_Pin_7);
                  }
                  break;
                  case 1:
                  {
                        Ram_try[2]=Modbus_try[4];
                        Ram_try[3]=Modbus_try[5];
										
												if((Modbus_try[4]<<8 | Modbus_try[5] ) == 0x0001)
													GPIO_ResetBits(GPIOB,GPIO_Pin_8);
												else
													GPIO_SetBits(GPIOB,GPIO_Pin_8);
                  }
                  break;
									case 2:
                  {
                        if(Modbus_try[5]==0)
                        {
//                            GPIO_ResetBits(GPIOB,GPIO_Pin_5);
                        }
                        else if(Modbus_try[5]==1)
                        {
//                            GPIO_SetBits(GPIOB,GPIO_Pin_5);
                        }
                  }
                  break;
                }
                SendData_Modbus(8);//发送数据
            }
        }
    }
					//3、情况缓存区:将接收数据清空,标志位清除,方便下次接收
			
					U1RX_ILDE_FLAG = 0;
					U1RX_LEN = 0;
					memset(Modbus_try,0,U1RX_MAX_LEN);
	}
}

//Modbus数据接收
void SendData_Modbus(u8 len)
{
        u8 i=0;
        
//        GPIO_SetBits(GPIOE,GPIO_Pin_5);//485准备发送数据
        delay_ms(2);//等待485控制端稳定
        
        for(i=0;i<len;i++)//发送所有接收到的数据
        {
            UART1_Send_Byte(Modbus_try[i]);
        }
        delay_ms(2);//等待数据发送完成
//        GPIO_ResetBits(GPIOE,GPIO_Pin_5);//切换至接收状态
}

void UART1_Send_Byte(u8 byte)
{
		USART_ClearFlag( USART1, USART_FLAG_TC);                        //清除发送完成标志位
    USART_SendData(USART1,byte);
		while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);
}

📄 main.c

/**
  ******************************************************************************
  * @file    Project/STM32F10x_StdPeriph_Template/main.c 
  * @author  MCD Application Team
  * @version V3.5.0
  * @date    08-April-2011
  * @brief   Main program body
  ******************************************************************************
  * @attention
  *
  * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
  * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
  * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
  *
  * <h2><center>&copy; COPYRIGHT 2011 STMicroelectronics</center></h2>
  ******************************************************************************
  */  

/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#include <stdio.h>

#include "string.h"

#include "gpio.h"
#include "delay.h"
#include "stdbool.h"
#include "exti.h"
#include "usart.h"
#include "modbus.h"



int main(void)
{
//	uint32_t RunCount = 0;
	
	NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2 );  //使用分组 2
	MX_GPIO_Init();
	delay_Init(); 
//	EXTI0_Init(2, 2);  //抢占优先级为2 响应优先级为 2 
	MX_USART1_Init(9600);
	USART1_NVIC_Init(1, 1);

//	printf("System Init OK! \r\n");

	while(1)
	{
		GetData_Modbus();

	}
}


/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/

在这里插入图片描述

Modbus RTU 帧结构

在这里插入图片描述
✅ 主机请求帧(读保持寄存器):

[设备地址][功能码][起始地址高][起始地址低][寄存器数高][寄存器数低][CRC][CRC]
[01]       [03]     [00]     [6B]      [00]       [03]       [CRC]

✅ 从机响应帧:

[设备地址][功能码][数据字节数][数据1][数据1]...[CRCL][CRCH]

在这里插入图片描述

RS485 是一种稳定、高抗干扰、长距离的通信方式,广泛应用于工业控制、仪表、智能电网等场景。通过 USART + SP3485 可以实现基于 STM32 的 485 通信实验。如果说RS232 简单点对点;那么,RS485 则为差分长距离;CAN 差分 + 协议智能总线。这些通信方式都可以用于串口通信,但差分通信(RS485/CAN) 更适合工业场景,特别是在噪声干扰强、距离长、设备多的环境中。

以上,欢迎有从事同行业的电子信息工程、互联网通信、嵌入式开发的朋友共同探讨与提问,我可以提供实战演示或模板库。希望内容能够对你产生帮助!


网站公告

今日签到

点亮在社区的每一天
去签到