rs485接口具有良好的抗噪声干扰性能、长传输距离和多站能力等特点,使其成为工业控制的首选串行接口。嵌入式系统中也广泛采用rs485接口作为设备控制的串行接口。rs485采用两线差分的接线方式进行串口数据的传输。由于发送和接收都是采用这两根差分线进行,因此它是半双工工作模式。基于rs485的特点,分别讲述了通过硬件方式和软件方式来实现rs485发送和接收方向的切换,重点解决了dm8168嵌入式平台上软件实现rs485方向切换的功能。
rs485总线是工业应用中非常成熟的技术,是现代通信技术的工业标准之一。rs485总线用于多站互连十分方便,用一对双绞线即可实现,采用平衡发送和差分接收,即在发送端驱动器将ttl电平信号转换成差分信号输出,在接收端接收器将差分信号变成ttl电平,因此具有抗共模干扰的能力。根据rs485标准,传送数据速率达100 kb/s时通信距离可达1200 m。
rs485在嵌入式系统中的应用非常广泛。嵌入式系统可以通过rs485接口来控制终端设备。由于rs485是半双工模式,因此发送和接收的方向切换需要我们的关注和研究。如果方向切换方式选择不好可能会导致rs485驱动能力下降、软件执行效率下降,甚至导致系统异常等问题。
本文分别给出硬件实现rs485方向切换和软件实现rs485方向切换两种方式。两种方式各有优点,硬件方式控制起来比较简单。软件方式的驱动能力更好,但是和嵌入式平台关系比较密切,不同的平台都需要调试和验证。
1 硬件方式控制rs485方向
图1所示为硬件控制rs485的电路图。电路中使用2n7002lt1g mos场效晶体管把uart_txd_485这个mcu输出的rs485发送信号逻辑取反后送给rs485芯片的re/de pin脚。控制的原理是,当uart_txd_485输出低电平时rs485芯片的de使能;输出高电平时re使能。默认情况下uart_txd_485是高电平,rs485芯片处于接收状态。发送数据时,uart_txd_485上面有高低电平信号变化,低电平信号通过rs485芯片sp3072eenl/tr直接输出,高电平信号通过外部上下拉电阻来控制。
这种方法的优点是控制简单,软件不需要做额外的工作,控制rs485像控制rs232一样。但是这种方法的缺点是驱动能力可能不足,由于这种控制方法没有完全发挥出rs485驱动芯片自身的驱动能力,输出信号依赖于外部上下拉电阻,因此在复杂环境下,譬如很多负载需要控制时,就会存在驱动能力不足的问题。但是在一些简单的环境或者软件实现较复杂的平台下,使用这种方法还是切实可行的。
图1 硬件控制rs485电路图
2 软件方式控制rs485方向
2.1 驱动能力分析
在复杂的rs485控制环境下,用上面介绍的硬件方式来控制rs485的方向会存在比较突出的驱动能力不足的问题。修改上述控制方法,将ttl这一侧的2线控制改为3线控制,就是将收发控制信号不用当前的/txd来控制,而从主控分出一根gpio线来控制收发。
按照输出电流计算,3线控制方式相对用2线控制的总线上下拉作为输出的方式,其驱动能力提高了25~50倍(不同厂家不同型号有差异),如果辅以终端电阻灵活配置的措施,rs485的驱动能力将完全不是问题。表1是两种控制方式驱动能力的对比。
2.2 软硬件环境
图2 软件控制方法中的硬件设计
软件控制方法采用图2的硬件设计,图中很突出的修改是使用mcu的gpio来控制re和de.rs485芯片的供电采用5 v供电,提高驱动能力。rs485芯片的re和de控制使用mcu的gpio输出高低电平来控制。简单来说就是,在rs485进行数据传输时,通过gpio来控制传输方向。这里采用的mcu是ti公司的dm8168处理器来实现软件的rs485切换功能。软件版本使用uboot2010.06和linux2.6.37。用软件来实现rs485的收发,尽量要保证执行效率;要达到上面的目的就需要对串口驱动进行调试,使用串口驱动用到的软件资源和串口控制器本身的硬件资源来实现rs485的控制。
表1 软件和硬件控制方式驱动能力的对比
2.3 uboot代码修改
需要修改的文件:
① board/ti/ti8168/evm.c
② drivers/serial/ns16550.c
③ include/configs/ti8168_evm.h
ti8168_evm.h文件中增加切换宏定义:
#define config_rs485_dir_sw 1
evm.c文件中增加切换函数:
void rs485_dir_sw(int rs485_dir){
if (rs485_dir ==0)
_raw_writel(rs485_dir_mask, ti81xx_gpio1_cleardataout);
else
_raw_writel(rs485_dir_mask, ti81xx_gpio1_setdataout);
}
s16550.c串口驱动文件中增加rs485方向控制:
void ns16550_putc(ns16550_t com_port, char c){
#ifdef config_rs485_dir_sw
rs485_dir_sw(1);
#endif
……//此处代码省略
#ifdef config_rs485_dir_sw
while((serial_in(&com_port-》lsr) & uart_lsr_temt) == 0)
rs485_dir_sw(0);
#endif
}
其中uart_lsr_temt表示发送buf和移位寄存器为空。默认情况下rs485是接收状态,一旦要发送数据,就把rs485切换为发送状态。发送完数据后,等待发送buf和移位寄存器为空,然后切换回接收状态,这里无需使用timeout。
2.4 linux代码修改
需要修改的文件:
① arch/arm/machomap2/bordti8168evm.c
② drivers/serial/omapserial.c
③ include/linux/serial_core.h
serial_core.h文件,uart_port结构体中增加set_rs485_direction函数指针,用于执行rs485的方向切换void (*set_rs485_direction)(int rs485_dir);原本考虑在uart_ops结构体中增加的,但是这个结构体是常量类型,对它不作改动,因此加到了uart_port结构体中。在该文件中添加相关宏定义和函数指针类型用于函数注册:
#define set_rs485_rx0
#define set_rs485_tx1
typedef void (*set_rs485_direction_t)(int rs485_dir);//用于函数注册
omapserial.c文件主要做了如下几点改动:
① 添加omap_rs485_dir_fun全局的函数指针。
static set_rs485_direction_t omap_rs485_dir_fun[omap_max_hsuart_ports]={null, null, null, null, null, null}
② 外部驱动利用omap_rs485_dir_fun_reg注册函数对omap_rs485_dir_fun进行赋值。
void omap_rs485_dir_fun_reg(int port_num, set_rs485_direction_t rs485_dir_fun){
if (port_num》=omap_max_hsuart_ports)
printk(kern_err “%s, port_num error max is %d, but %d \\n”, __function__, omap_max_hsuart_ports-1, port_num);
omap_rs485_dir_fun[port_num]= rs485_dir_fun;
}
export_symbol(omap_rs485_dir_fun_reg);
③ serial_omap_probe函数中对控制程序中用到的up-》port.set_rs485_direction进行赋值。
up-》port.set_rs485_direction= omap_rs485_dir_fun[pdev-》id];
④ 默认情况下rs485处于接收状态。
⑤ serial_omap_enable_ier_thri函数中把rs485切换为发送状态。
static incline void serial_omap_enable_ier_thri(struct uart_omap_port *up){
if (!(up-》ier & uart_ier_thri)) {
/* rs485 dir change to tx */
if (up-》port.set_rs485_direction != null)
up-》port.set_rs485_direction(set_rs485_tx);
……//此处代码省略
}
}
⑥ serial_omap_stop_tx函数中把rs485切换为接收状态。
static void serial_omap_stop_tx(struct uart_omap_port *port){
……//此处代码省略
if (up-》ier & uart_ier_thri) {
up-》ier &= ~uart_ier_thri;
serial_out(up, uart_ier, up-》ier);
/* rs485 dir change to rx */
if (port-》set_rs485_direction != null)
port-》set_rs485_direction(set_rs485_rx);
}
}
⑦ transmit_chars更改一下,原先的代码是当没有更多的字符要发送(环形缓冲为空)时需要关闭发送中断,这时串口控制器发送buf和移位寄存器中还是有数据的,这些数据串口控制器自动发送完成后才算结束,由于已经关闭了发送中断,因此发送结束后就没有中断产生了。但是rs485切换方向需要等到完全发送完成后才能进行。因此对transmit_chars函数做了修改。调用serial_omap_stop_tx函数前判断发送buf和移位寄存器是否为空,如果为空就可以切换方向了。简而言之,延后了发送中断的关闭时间。
static void transmit_chars(struct uart_omap_port *up){
……//此处代码省略
if (uart_circ_empty(xmit) || uart_tx_stopped(&up-》port)) {
if (up-》port.ops-》tx_empty(&up-》port)==0)
return;//added for last transmit
serial_omap_stop_tx(&up-》port);
return;
}
……//此处代码省略
if (uart_circ_empty(xmit)) {
if (up-》port.ops-》tx_empty(&up-》port)==0)
return;//added for last transmit
serial_omap_stop_tx(&up-》port);
}
}
⑧ arch/arm/machomap2/boardti8168evm.c文件在ti8168_evm_init函数中调用omap_rs485_dir_fun_reg函数注册rs485切换函数。
2.5 实验结果分析
上述软件修改有如下几个优点:不增加硬件开销;不增加和使用任何硬件资源;不增加软件开销;不影响软件执行效率;硬件控制是电信号控制,方向切换和tx绑定;软件控制是整个发送缓冲区完成发送后再进行方向切换,控制实现上更加合理。
对软件切换rs485做了基本的测试,情况如下:
① 控制台操作。整个启动打印信息正常。uboot和kernel下控制效果和硬件控制一样,可以很流畅地进行命令的输入和回显,串口终端增加输入字符间的延时后可以进行配置的粘贴。内核在115 200和38 400下分别进行测试ok。
② 内核下加大负责进行大数据量的发送。增加负载,开多个ping包进程(产生大量中断)、nand flash的操作、cpu占有率接近100%条件下,通过rs485输出大量数据,没有乱码,校验ok。
③ 极高的实时性。
由于本文给出的软件实现方式是基于linux内核实现的,因此很好地保证了方向控制的实时性。实际结果显示,dm8168数据发送完成到产生方向控制信号之间的时间在25 μs左右,几乎可以忽略不计。而有些设计在用户空间使用应用程序进行方向切换的方法会导致20 ms以上的延时,导致了一系列异常问题的产生。
结语
本文详细描述了rs485方向控制的硬件和软件两种实现方式。两种控制方式各有特点,硬件控制方式实现简单,不需要软件干预,对软件而言rs485串口收发就像rs232一样简单。软件控制方式可以极大地提高整个rs485线路的驱动能力,本文给出的基于linux内核的控制方法又很好地保证了rs485方向切换的实时性,满足了实用性要求。这两种方式在很多场合已经得到了很好的应用和验证。特别是软件实现方式,可以扩展到更多的应用场合,譬如复杂的多主、多从的rs485使用环境,软件控制可以根据自己的需求来实现整个rs485线路不同的数据流向,可以规避某个设备对rs485链路上异常信号的干扰,给实际应用带来了很多的便利性。
矿用漏泄电缆的主要发展趋势都有哪些
DC/DC微型模块稳压器LTM4615的性能特点及功能应用
巧用LM324运放搭建电压跟随器
大联大推出基于安森美NCP1655 STB电竞桌机电源解决方案
厦门天马持续保持LTPS智能机面板出货市占率全球第一
嵌入式系统应用中实现RS485的方向切换
网络监控涉及到哪一些风险
Samplify推出新版Prism 压缩/解压缩技术
如何使用TensorFlow进行大规模和分布式的QML模拟
多功能食品安全检测仪的功能
什么是电源完整性PI?影响电源完整性的原因有哪些?
交换机中光口是如何配置的
linux安装网卡驱动教程
骁龙810对比805:新一代系统芯片有何提升?
3d光学轮廓仪干什么用的?
基本电能质量测量方法和常见问题原因
联合电子首款24V 商用车自动变速箱控制器批量下线
2045年半导体行业会是什么样?
锁定今晚!为大家带来一场干货满满的星火1号线上培训课程,敬请期待!
OPPO 陈明永:竞争不是你死我活, “科技为人以善天下”