STM32-RS485通信软硬件实现
OS:Windows 64
Development kit:MDK5.14
IDE:UV4
MCU:STM32F103C8T6/VET6
AD:Altium Designer 18.0.12
1、RS485简介
RS-485又名TIA-485-A, ANSI/TIA/EIA-485或TIA/EIA-485。
RS485是一个定义平衡数字多点系统中的驱动器和接收器的电气特性的标准,该标准由电信行业协会和电子工业联盟定义。使用该标准的数字通信网络能在远距离条件下以及电子噪声大的环境下有效传输信号。RS-485使得廉价本地网络以及多支路通信链路的配置成为可能。RS485接口组成的半双工网络,一般是两线制(以前有四线制接法,只能实现点对点的通信方式,现很少采用),多采用屏蔽双绞线传输。这种接线方式为总线式拓扑结构在同一总线上最多可以挂接32个结点。在RS485通信网络中一般采用的是主从通信方式,即一个主机带多个从机。很多情况下,连接RS-485通信链路时只是简单地用一对双绞线将各个接口的“A”、“B”端连接起来。RS485接口连接器采用DB-9的9芯插头座,与智能终端RS485接口采用DB-9(孔),与键盘连接的键盘接口RS485采用DB-9(针)。
在低速、短距离、无干扰的场合可以采用普通的双绞线,反之,在高速、长线传输时,则必须采用阻抗匹配(一般为120Ω)的RS485专用电缆(STP-120Ω(用于RS485 & CAN)一对18AWG),而在干扰恶劣的环境下还应采用铠装型双绞屏蔽电缆(ASTP-120Ω(用于RS485 & CAN)一对18AWG)。
2、RS485特性
- RS-485的电气特性:逻辑“0”以两线间的电压差为+(2—6)V表示;逻辑“1”以两线间的电压差为-(2—6)V表示。接口信号电平比RS-232降低了,就不易损坏接口电路的芯片,且该电平与TTL电平兼容,可方便与TTL电路连接
- RS-485的数据最高传输速率为10Mbps
- RS-485接口是采用平衡驱动器和差分接收器的组合,抗共模干扰能力增强,即抗噪声干扰性好
- RS-485接口的最大传输距离标准值为4000英尺(约1219米),实际上可达3000英尺,另外RS-232接口在总线上只允许连接1个收发器,即单站能力。而RS-485接口在总线上是允许连接多达128个收发器。即具有多站能力,这样用户可以利用单一的RS-485接口方便地建立起设备网络。
3、RS485通信硬件实现
博主使用的485芯片为MAX3485,实现半双工通信。应用电路如下图:
R6为120欧的阻抗匹配电阻,如果长距离通信的话,一定要在最后一个节点接上这一个电阻;但是短距离通信的话焊上R6反而出错(博主在实验中发现,焊上匹配电阻后,主从节点通信异常,调试发现大量的00字节在自动收发),因此建议大家先不要焊上,但是在电路设计时保留。RXD485、TXD485分别接控制芯片的USART1_RX、USART1_TX(串口号可自行选择,这里使用串口1),此外;485C接芯片的PA4引脚(随意选择)用以切换485的通信状态。J1、J2为两个JST接口,方便485通信线路的连接,由于是从节点因而留出两个。以下为其余连接电路:
4、RS485通信软件实现
1 #include "sys.h"
2 #include "stdio.h"
3
4 #define USART1_RX_LEN 50 //接收最大字节
5 #define USART1_TX_LEN 50 //发送最大字节
6 #define RS485_TX_EN PAout(4)
7
8
9 extern u8 USART1_RX_Buf[USART1_RX_LEN]; //接收缓冲
10 extern u8 USART1_TX_Buf[USART1_TX_LEN]; //发送缓冲
11 extern u8 USART1_RX_Data_Len; //实际接收数据字节长度
12 extern u8 USART1_TX_Data_Len; //待发送数据字节长度
13 extern u8 USART1_RX_Flag; //是否收到数据
14
15 void RS485_Config(u32 bound);
16 void USART1_IRQHandler(void);
17 void RS485_Send_Data(u8 *buf,u8 len);
1 #include "sys.h"
2 #include "delay.h"
3 #include "rs485.h"
4
5 u8 USART1_RX_Buf[USART1_RX_LEN]; //接收缓冲
6 u8 USART1_TX_Buf[USART1_TX_LEN]; //发送缓冲
7 u8 USART1_RX_Data_Len = 0; //实际接收数据字节长度
8 u8 USART1_TX_Data_Len = 0; //待发送数据字节长度
9 u8 USART1_RX_Flag = 0; //串口1是否接收完数据
10
11 void USART1_IRQHandler(void)
12 {
13 u8 res;
14 if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收到数据
15 {
16 res =USART_ReceiveData(USART1); //读取接收到的数据
17 if(USART1_RX_Data_Len<USART1_RX_LEN)
18 {
19 USART1_RX_Buf[USART1_RX_Data_Len]=res; //记录接收到的值
20 USART1_RX_Data_Len++; //接收数据增加 1
21 }
22 USART1_RX_Flag=1; //串口1接收到数据
23 }
24 }
25
26 void RS485_Config(u32 bound)
27 {
28 GPIO_InitTypeDef GPIO_InitStructure;
29 USART_InitTypeDef USART_InitStructure;
30 NVIC_InitTypeDef NVIC_InitStructure;
31
32 /*********************配置串口1**************************/
33
34 /* config USART1 clock */
35 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 , ENABLE);
36 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);
37
38 /* USART1 GPIO config */
39 /* Configure USART1 Tx (PA.02) as alternate function push-pull *///TX
40 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
41 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
42 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
43 GPIO_Init(GPIOA, &GPIO_InitStructure);
44 /* Configure USART1 Rx (PA.03) as input floating *///RX
45 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
46 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
47 GPIO_Init(GPIOA, &GPIO_InitStructure);
48
49
50
51
52 /* USART1 mode config */
53
54 USART_InitStructure.USART_BaudRate = bound;
55 USART_InitStructure.USART_WordLength = USART_WordLength_8b;
56 USART_InitStructure.USART_StopBits = USART_StopBits_1;
57 USART_InitStructure.USART_Parity = USART_Parity_No ;
58 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
59 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
60 USART_Init(USART1, &USART_InitStructure);
61
62 /* USART1 接收中断 */
63 NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //使能串口 2 中断
64 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; //先占优先级 3 级
65 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //从优先级 2级
66 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
67 NVIC_Init(&NVIC_InitStructure);//初始化 NVIC 寄存器
68
69 USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //开启中断
70 USART_Cmd(USART1, ENABLE); //使能串口
71
72 //USART_ClearFlag(USART1, USART_FLAG_TC);//清发送完成标志
73
74
75 /**********************配置485控制口*********************/
76
77 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
78 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出 ,PA4,485Ctr
79 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
80 GPIO_Init(GPIOA, &GPIO_InitStructure);
81 GPIO_ResetBits(GPIOA, GPIO_Pin_4); //设置为接收模式,默认接收
82
83 }
84
85 void RS485_Send_Data(u8 *buf,u8 len) //发送完改为接收
86 {
87 u8 t;
88 RS485_TX_EN=1;//设置为发送模式
89 for(t=0;t<len;t++)
90 {
91 while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
92 USART_SendData(USART1,buf[t]);
93 }
94 while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
95
96 USART1_RX_Data_Len=0;
97 RS485_TX_EN=0;//设置为接收模式
98 }
通过以上代码我么们就能通过STM32的串口资源实现485的正常通信了。注意事项:
- 初始化串口:RX设置为浮空输入、TX设置为复用推挽输出
- 因为是从节点,默认为接收模式,485C初始化为低电平;主节点则相反。可根据需要修改
- 每次发送或接收时都应切换通信状态