做嵌入式开发的无论软件还是硬件,应该经常听见这样一个概念“485协议”,但是呢,去查资料又发现好多人说“485要跑modbus协议”,姑且不论modbus是什么,协议上跑协议,有点晕。到底是怎么回事呢,请往下看。
485是电气特性规定为2线,半双工,多点通信的的标准,它的电气特性和RS232不太一样,用缆线两端的电压差值来表示传递信号,RS485仅仅规定那个了接收端和发送端的电气特性,它没有规定或推荐任何数据协议(注意这句话:仅仅规定了特性,没规定协议)。
RS485特点:
1.接口电平低,不易损坏芯片,逻辑“1” :VA-VB>+200mv;逻辑“0”:VA-VB<-200mv;|VA-VB|<200mv,总线电平不确定(网上有些资料叙述错误,误人子弟,大家可以网上搜一款485芯片,对照手册来确定逻辑电平和电压差的关系)
,接口电平比RS232降低了。
2.传输速率高,10 米时, RS485 的数据最高传输速率可达 35Mbps,在 1200m 时,传输速度可达 100Kbps。
3.抗干扰能力强,RS485 接口是采用平衡驱动器和差分接收器的组合,抗共模干扰能力增强,即抗噪声干扰性好。
4.传输距离远,支持节点多, RS485 总线最长可以传输 1200m 以上(速率≤100Kbps)一般最大支持 32 个节点,如果使用特制的 485 芯片,可以达到 128 个或者 256 个节点,最大的可以支持到 400 个节点。
注意:
1.485推荐使用在线型,总线型网络,而不能是星型,环形网络(牵扯到信号反射,造成干扰),2.传输距离比较远的情况下RS485需要2个终端匹配电阻,其阻值要求等于传输电缆的特性阻抗120欧姆。(485通讯硬件设计注意事项比较多,稍有不慎就可能对通信造成很大的干扰,要多查资料)
3.具体使用的时候,使用相应的485芯片作为收发器,比如SP3485,max485等
去网上搜modbus,关于该协议的介绍可能一大推,其实就一句话:modbus定义了一种数据帧格式:帧头---地址---功能码---数据---CRC校验
,注意:协议是一种很灵活的东西,目的是定义数据通讯的格式,上面这个是标准的modbus通讯协议,具体应用的时候可以根据实际需要进行裁剪,比如加个帧尾,比如换成其他的校验方式。
所以,大家现在应该对485和modbus的关系有所认识了吧,我们可以把485总线单纯地理解为硬件通路,它具有自己的电气特性,所有的设备都可以挂在上面,每个设备有唯一的地址,和串口通信不同的是由于485有专门的控制收发引脚,所以代码里面每次发送前后都要对该控制引脚进行控制。此外软件上注意延时(电平稳定);modbus其实可以理解为硬件公路上的车,它本身就是个软件协议,规定上位机和下位机数据以什么样式进行传输。
下面是我用VISIO画的基本框图
引脚介绍
RX MCU接收管脚
TX MCU发送管脚
IO MCU控制485收发的管脚
RO 属于485芯片的发送管脚
DI 属于485芯片的接收管脚
CTL 是485芯片的控制管脚(实际上是485两个控制管脚接在一起,当IO输出低电平,mcu接收数据,当IO输出高电平,mcu发送数据)
我们做了一个485批量测试软件,测试板上放了16个mcu,通过485协议,PC端读取每个模组的测试数据,基本思路如下
1.通过测试板硬件,固定测试板每个模组的地址,然后由mcu去读各自的Address
#define RS485_TX_EN PAout(8) //485收发控制.0,接收;1,发送.
uint8_t Address[5]; //由4个IO口读取电平,拼成一个address
uint8_t RS485_address; //存放模组真正地址
void Get_Address() //获取模组地址
{
//D4 PA10
//D3 PA0
//D2 PA1
//D1 PA2
//D0 PA3
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
Address[0] = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_3);
Address[1] = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2);
Address[2] = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1);
Address[3] = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0);
Address[4] = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_9); //最开始是25的模组测试板,16个可取消这个IO口
RS485_address = ((~Address[4])&0x01)<<4 | Address[3]<<3 | Address[2]<<2 | Address[1]<<1 | Address[0];
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //将PA8配置成收发控制管脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
RS485_TX_EN = 0; //默认为收
}
2.配置串口接收中断
将模组配置成串口中断模式,接收PC端发的指令,然后在中断中执行一些操作,将数据上传到PC
基本协议如下
PC指令
0x19 0x20 address 0x69
0x19 0x20 address 0x89
注:完整指令一共四个字节0x19 0x20是帧头 address是模组的地址,0x69和0x89都是功能码对应不同的操作
3.串口中断函数
uint8_t gRxbuf[4] = {0}; //define the array to store received data
void USART1_IRQHandler(void)
{
uint8_t k,data = 0;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
data = USART_ReceiveData(USART1);
if(data == 0x19)
gRxbuf[0] = data;
else if(data == 0x20)
gRxbuf[1] = data;
else if(data < 0x19)
gRxbuf[2] = data;
else if(data > 0x20)
gRxbuf[3] = data;
if(gRxbuf[3] != 0) //当此判断条件成立时,表示接收到一条完整的指令
{
if ((gRxbuf[0] == 0x19)&&(gRxbuf[1] == 0x20)&&(gRxbuf[2] == RS485_address)) //确保指令正确性
{
if(0x69 == gRxbuf[3]) //对应当功能码为0x69时执行的操作
{
/***************其余操作*********************************/
RS485_TX_EN = 1; //给485控制管脚高电平,发送数据
delay_ms(1); //延时1ms等485切换控制管脚的电平稳定
/***************发送操作*******************/
delay_ms(1); //延时1ms等485切换控制管脚的电平稳定
RS485_TX_EN = 0;
gRxbuf[0] = 0; //处理完毕将存储指令的数组清空复位
gRxbuf[1] = 0;
gRxbuf[2] = 0;
gRxbuf[3] = 0;
}
if(0x89 == gRxbuf[3]) //对应当功能码为0x89时执行的操作
{
/***************其余操作*********************************/
RS485_TX_EN = 1;
delay_ms(1);
/***************发送操作*******************/
delay_ms(5);
RS485_TX_EN = 0;
gRxbuf[0] = 0;
gRxbuf[1] = 0;
gRxbuf[2] = 0;
gRxbuf[3] = 0;
}
}
else //当功能码既不是0x69也不是0x89时,不做任何操作
{
gRxbuf[0] = 0;
gRxbuf[1] = 0;
gRxbuf[2] = 0;
gRxbuf[3] = 0;
}
}
}
}
此处说一下地址的作用:比如PC向总线发了一个测试指令0x19 0x20 0x00 0x69
,485总线上的所有设备都可以收到这四个字节,然后通过软件编写,485设备会将接收到的指令的地址码即0x00和它们自己的地址做比较,当一样时知道是发给自己的,当不一样时不做任何回应,从而实现访问485总线的多点通信。