一:RS485知识分享:
485(一般称作 RS485/EIA-485)隶属于 OSI模型物理层,是串行通讯的一种。电气特性规定为2线,半双工,多点通信的类型。它的电气特性和 RS-232 大不一样。用缆线两端的电压差值来表示传递信号。RS485 仅仅规定了接受端和发送端的电气特性。它没有规定或推荐任何数据协议。
RS485 的特点包括:
1:接口电平低,不易损坏芯片。RS485 的电气特性:逻辑“1”以两线间的电压差为+(2~6)V 表1,示;逻辑“0”以两线间的电压差为-(2~6)V 表示。接口信号电平比 RS232 降低了,不易损坏接口电路的芯片,且该电平与 TIL 电平兼容,可方便与 TIL 电路连接。
2:传输速率高。10 米时,RS485 的数据最高传输速率可达 35Mbps,在 1200m 时,传输速度可达 100Kbps。
3:抗干扰能力强。RS485 接口是采用平衡驱动器和差分接收器的组合,抗共模干扰能力增强。即抗噪声干扰性好。
传输距离远,支持节点多。RS485 总线最长可以传输 1200m 左右,更远的距离则需要中继4,传输设备支持但这时(速率≤100Kbps)才能稳定传输,一般最大支持32个节点,如果使用特制的 485 芯片,可以达到 128 个或者 256 个节点,最大的可以支持到 400 个节点。RS485 推荐使用在点对点网络中,比如:线型,总线型网络等,而不能是星型,环型网络。理想情况下 RS485 需要2个终端匹配电阻,其阻值要求等于传输电缆的特性阻抗(一般为 1202 )。没有特性阻抗的话,当所有的设备都静止或者没有能量的时候就会产生噪声,而且线移需要双端的电压差。没有终接电阻的话,会使得较快速的发送端产生多个数据信号的边缘,导致数据传输出错。485 推荐的一主多从连接方式如图下所示:
在上面的连接中,如果需要添加匹配电阻,我们一般在总线的起止端加入,也就是主机和设备4上面各加一个 120Ω的匹配电阻。
由于 RS485 具有传输距离远、传输速度快、支持节点多和抗干扰能力更强等特点,所以RS485有很广泛的应用。实际多设备时收发器有范围为-7V到+12V的共模电压,为了稳定传输,也有使用3线的布线方式,即在原有的 A、B两线上多增加一条地线。(4线制使用全双工通讯方式,这种也叫 RS422,由于布线的难度和通讯局限,相对使用得比较少)。
二:硬件配置如下所示:
三:STM32 cube MX 软件配置:
四:软件代码编写:
4.1 发送函数
/**************************************************************************************************** * 函数名称: void Send_Data(u8 *buf,u8 len) * 入口参数:u8 *buf u8 len * 返回 值:无 * 功能说明:串口发送数据 * buf:发送区首地址 * len:发送的字节数(为了和本代码的接收匹配,这里建议不要超过64个字节) ***************************************************************************************************/ void Send_Data(uint8_t *buf,uint8_t len) { RS485_TX_EN; //设置为发送模式 HAL_UART_Transmit(&huart2, &buf[0], 8, 100); USART_RX_CNT=0; RS485_TX_EN; //设置为接收模式 }
4.2 读取RS485 返回的数据
/**************************************************************************************************** * 函数名称:u8 UartRead(u8 *buf, u8 len) * 入口参数:u8 *buf u8 len * 返回 值:u8 * 功能说明:计算接收的数据长度,并且将数据放到*buf数组中 ***************************************************************************************************/ uint8_t UartRead(uint8_t *buf, uint8_t len) { uint8_t i; if(len > USART_RX_CNT) //指定读取长度大于实际接收到的数据长度时 { len = USART_RX_CNT; //读取长度设置为实际接收到的数据长度 } for(i = 0;i < len;i ++) //拷贝接收到的数据到接收指针中 { *buf = RecBuffer2[i]; //将数据复制到buf中 buf ++; } USART_RX_CNT=0; //接收计数器清零 return len; //返回实际读取长度 }
4.3 超时判断函数:
/**************************************************************************************************** * 函数名称:void UartRxMonitor(u8 ms) * 入口参数:u8 ms * 返回 值:无 * 功能说明:在定时器中调用,用于监控数据接收 ***************************************************************************************************/ void UartRxMonitor(uint8_t ms) { static uint8_t USART_RX_BKP = 0; //定义USART2_RC_BKP暂时存储诗句长度与实际长度比较 static uint8_t idletmr = 0; //定义监控时间 if(USART_RX_CNT > 0) //接收计数器大于零时,监控总线空闲时间 { if(USART_RX_BKP != USART_RX_CNT) //接收计数器改变,即刚接收到数据时,清零空闲计时 { USART_RX_BKP = USART_RX_CNT; //赋值操作,将实际长度给USART2_RX_BKP idletmr = 0; //将监控时间清零 } else //接收计数器未改变,即总线空闲时,累计空闲时间 { //如果在一帧数据完成之前有超过3.5个字节时间的停顿,接收设备将刷新当前的消息并假定下一个字节是一个新的数据帧的开始 if(idletmr < 3) //空闲时间小于3ms时,持续累加 { idletmr += ms; if(idletmr >= 3) //空闲时间达到3ms时,即判定为1帧接收完毕 { From_Flag = 1; //设置命令到达标志,帧接收完毕标志 } } } } else { USART_RX_BKP = 0; } }
4.4 写入函数如下所示:
/**************************************************************************************************** * 函数名称:void RS485_RW_Opr(u8 ucAddr,u8 ucCmd,u16 ucReg,u16 uiDate) * 入口参数:u8 ucAddr,u8 ucCmd,u16 ucReg,u16 uiDate * ucAddr:从机地址 * ucCmd :功能码 03->读 06->写 * ucReg :寄存器地址 * uiDate:写操作即是发送的数据 读操作即是读取数据个数 * 返回 值:无 * 功能说明:485读写操作函数 ***************************************************************************************************/ void RS485_RW_Opr(uint8_t ucAddr,uint8_t ucCmd,uint16_t ucReg,uint16_t uiDate) { unsigned int crc; unsigned char crcl; unsigned char crch; unsigned char ucBuf[8]; ucBuf[0] = ucAddr; /* 从机地址 */ ucBuf[1] = ucCmd; /* 命令 06 写 03 读 */ ucBuf[2] = ucReg >> 8; /* 寄存器地址高位 */ ucBuf[3] = ucReg; /* 寄存器地址低位 */ ucBuf[4] = uiDate >> 8; /* 数据高8位 */ ucBuf[5] = uiDate; /* 数据低8位 */ crc = GetCRC16(ucBuf,6); /* 计算CRC校验值 */ crch = crc >> 8; /* crc高位 */ crcl = crc & 0xFF; /* crc低位 */ ucBuf[6] = crch; /* 校验高8位 */ ucBuf[7] = crcl; /* 校验低8位 */ Send_Data(ucBuf,8); /* 发送数据 */ RS485Busy = 1; /* 发送完成后 忙标志置位 */ }
4.5 接收串口返回的数据如下所示:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart->Instance == USART1) /* 如果是串口1 */ { Usart2_DEAL_RX_Buf[USART_RX_CNT] = RecBuffer2[0]; //记录接收到的值 USART_RX_CNT ++; //接收数据增加1 HAL_UART_Receive_IT(&huart1, (uint8_t *)RecBuffer2, 1); } }
五:实物验证:
5.1 在定时器任务中添加读取寄存器命令:
RS485_RW_Opr(0x01,0x03,00,3); //读取外设功能
5.2 测试数据截图: