目录
RS232的高电平1的逻辑为-5V~-15V,低电平0的逻辑为+5V~+15V。高电平和TTL的0~5V不兼容,传输的距离也不够长。
1. 485简介
485(一般称作RS485)是隶属于OSI模型物理层的电气特性,规定为2线,半双工模式,多点通信。其电气特性和RS232不太一样,RS485是用缆线两端的电压差值来传递信号。RS485仅仅规定了接收端和发送端的电气特性。没有规定或推荐任何数据协议。
特点:
- 接口电平低,不容易损坏芯片。这一点相对于RS232接口的高电平来说,是非常大的优点。
- RS485的电气特性:逻辑 “1” 以两线间的电压差+(2~6)V表示;逻辑 “0” 以两线间的电压差-(2~6)V表示。接口电平低,不易损坏接口电路的芯片,并且该电平与TTL电平兼容,可以方便与TTL电路连接。
- 传输速率高。10米时,RS485的数据最高传输速率可达35Mbps,在1200m时,传输速度可达100Kbps。
- 抗干扰能力强。RS485接口采用平衡驱动器和差分接收器的组合,抗共模干扰能力强,也可以说是抗噪声干扰性好。
- 传输距离远,支持节点多。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通信时连接很不方便。
抗干扰能力差:
UART一般直接使用TTL信号来表示 0 和 1 ,但TTL信号的抗干扰能力较差,数据在传输过程中很容易出错。
通信距离极短:
因为TTL信号的抗干扰能力较差,所以其通信距离也很短,一般只能用于一个电路板上的两个不同芯片之间的通信。
3. RS232协议
RS232协议是1970年美国电子工业协会 EIA 联合贝尔系统、调制解调器厂家、计算机终端生产厂家共同制定的用于串行通信的标准(从上面介绍的UART的缺点来看,串口通信最致命的缺点就是没有统一的标准,导致不同器件之间的通讯变得异常的复杂,而 RS232 和 RS485 就是这样的一个标准,应用在串口之上,使得串口通信具有一定的标准)。
该标准规定采用一个标准的连接器,标准中对连接器的每个引脚的作用都加以规定,同时也对信号的电平加以规定。
接口:
该标准规定采用一个 25 引脚的 DB-25 连接器,标准中对连接器的每个引脚的信号内容加以规定。还对各种信号的电平加以规定;后来1BM的PC机将RS232简化成了 DB-9 连接器,再后来成了事实的标准(就像我们家用的三头插座一样,火线、零线、地线);现在工业控制的RS-232接口一般只使用RXD、TXD、GND三条线(STM32开发板中的原理图上也是如此)。
信号:
RS232标准规定逻辑 “1” 的电平为-5V到-15V,逻辑 “0” 的电平为+5V到+15V,选择该电气标准的目的在于提高抗干扰能力,增大通信距离,其传送的距离一般可达15m。
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设备都静止或者没有能量的时候就会产生噪音。并且可能在线移的过程中导致数据传输错误。
信号:
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 。
2. 为什么RS485可以通过差分信号明显的降低噪声干扰?
其实在我们日常生活中,差分信号的应用还是很多的。比方说我们家用的网线,细心的可以发现,其中就是通过两根线互铰在一起的。
之所以能明显的降低噪声干扰,是因为噪声是确确实实存在的,我们无法直接去掉这种干扰,可以反过来这样的考虑,既然无法处理这种干扰,那不妨就让他干扰,因为差分信号是用两根线进行传递一个信号的,所以噪音只要干扰,一定是同时干扰我的两根线的,而且干扰我的两根线的程度一定是相同的,这样一来,逻辑1 还是逻辑 1 ,逻辑0 还是逻辑 0 。没有实质上的影响。
接口:
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。
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 的相关引脚连接图。其中 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 正常稳定的运行,过滤掉其他噪声的干扰。
本次实验需要将两个开发板上的 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<key;i++)
{
LCD_ShowxNum(30+i*32,230,RS485Buf[i],3,16,0x80); //既然接收到了数据,那么就显示接收到的数据
//显示接收到的数据的格式和发送缓存区显示数据的设置格式相同
}
}
t++;//通过t++的值来使得LED0不断闪烁,表示程序一直在运行,类似于计数器的功能
delay_ms(10);
if(t==20)
{
LED0=!LED0;
t=0;
CNT++;
LCD_ShowxNum(30+48,150,CNT,3,16,0x80);//显示数据Count的变化
//30+48=30+6*8:30是起始的坐标,一个字节占8位,6代表Count:所占的字节,所以30+6*8,150,正好可以使得显示出来的CNT值位于Count:后面,这是一个排列规整性的问题
}
}
}
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<len;t++)
{
while(USART_GetFlagStatus(USART2,USART_FLAG_TC)==RESET); //获取中断发送的标志位
//只要该串口一直在发送,那么标志位就一直是0,一旦跳出循环,那么就意味着标志位是1了。
USART_SendData(USART2,BufAddress[t]); //一旦跳出上述while循环,表示发送上一位数据停止,可以发送下一位数据了
}
while(USART_GetFlagStatus(USART2,USART_FLAG_TC)==RESET); //等待发送完毕
//一旦所有的数据发送完毕,就将接收到的数据长度清零
//因为所有接收到的数据都已经发送了,所以在上述程序执行完以后,将模式设置为接收模式
RS485_RX_CNT=0;
RS485_TX_EN=0; //设置为接收模式
}
//RS485查询接收到的数据
//BufAddress:接收缓存首地址
//len:读到的数据长度
void RS485_Receive_Data(u8 *BufAddress,u8 *len)
{
u8 RXLen=RS485_RX_CNT; //定义一个变量RXLen等于接收到的字节长
//因为是静态存储,所以接收数据前首先要定义一个能够接收数据的最大字节长度,这里我们设置字节长度恰好等于另外一个单片机接收的字节长
//这样在两个单片机通讯的过程中,既能最大化的节省空间,又能实现数据的接收和发送
u8 i;
*len=0; //默认接收的字节长度为0
delay_ms(10); //等待10ms,连续超过10ms没有接收到一个数据,则认为接收结束
if(RXLen==RS485_RX_CNT&&RXLen)//RXLen为真,表示接收到了数据;RXLen==RS485_RX_CNT表示接收完成了
{
for(i=0;i<RXLen;i++)
{
BufAddress[i]=RS485_RX_BUF[i]; //把串口接收到的数据根据数组下角标循环依次给到接收缓存区的数组
}
*len=RS485_RX_CNT; //记录本次数据的长度
RS485_RX_CNT=0; //清零,提前记录好,然后在清零,方便串口继续接收下一轮的数据
}
}
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 接口,即可得到同样的实验现象!!!