sp3485在rk3288上的应用

落爺英雄遲暮 提交于 2019-12-05 20:31:02

2019-11-27

关键字:485串口通信


 

SP3485 是一款半双工的遵循RS485与RS422通信协议的传输芯片。

 

SP3485的芯片封装如下图所示:

其中 1 脚、4 脚分别接 rk3288 的 RX 脚与 TX 脚。

 

第 2 脚、第 3 脚为收发控制位,通常这两个脚都是接同一个电平信号的,因为通过芯片封装图可知这两个脚是互为取反设计的。当给这两个脚高电平时芯片处于“发送”模式,当给它们低电平时则处于“接收”模式。这款芯片的收发控制既可以通过软件来控制电平高低以切换收发模式,也可以直接通过硬件电路来自动切换收发模式。二者的区别在于对收发模式切换延时的要求不同。使用软件控制模式切换存在一定的延时,极限大约在 100us 左右,这个延时时长其实已经能应对大多数场景下的通信了,但还是有极少数高速通信场景接受不了这个延时时长。这种情况下就得考虑硬件电路自动切换收发模式了。如何通过硬件自动切换收发模式呢?网上有一篇文章:

http://www.elecfans.com/dianlutu/app/20180117617635_2.html

 

本篇文章记述的是通过软件来切换收发模式的方式。

 

上图第 6 脚、第 7 脚是差分信号输出引脚。因为 485 通信必须要有两条差分信号线才能进行通信,而这款芯片又仅有一组输出引脚,因此这在硬件上就限制了这款芯片只能是半双工通信模式的芯片。

 

本篇文章记述的是通过软件控制收发模式切换的方式,在笔者的开发板里,它的DE、RE脚所接的引脚为:GPIO7_B7

 

它的 RX、TX 接到了 UART1 中:

 

在 rk3288 中,共有 5 个 UART 串口,它们通常在 /dev 目录下以 ttyS* 的方式注册成串口驱动设备:

 

UART2 默认是被配置成调试串口,它是不能被当作普通串口来用的。当然,具体要开放多少个通信串口可以在 dts 中去配置。总而言之,笔者手里的环境就是通过 ttyS1 来收发 485 通信数据的。

 

既然已经知道是通过 rk 的普通串口 UART1 来做 485 通信的,那我们直接去改动 rk3288 的串口驱动程序就好了。

 

rk3288 的普通串口默认是不支持 485 通信的,因此我们这里需要自己去改一下驱动代码。笔者手里的开发板串口驱动对应的源码文件为:

./kernel/driver/tty/serial/rk_serial.c

 

rk3288 的串口驱动默认是有“回显”功能的,我们需要将它的回显功能去掉,就是在注册 uart 驱动以后修改一下驱动结构体,如下图所示:

 

然后由于我们只将 UART1 用作 485 通信,因此需要在 probe() 函数中将连接着 sp3485 收发模式控制位的引脚给申请成为普通 GPIO 口:

 

上图所示代码中的宏 "GPIO_485_EN" 指的是前面我们提到的那个 GPIO7_B7 引脚,这个引脚在驱动代码里必须转换成整型数字来表示,它在这里的值是 239,计算方式为:7 * 32 + 15 = 239。详细的计算方式可以参阅笔者的另一篇博文:RK平台如何计算GPIO脚编号

 

在将模式控制引脚设置为GPIO输出模式后还将默认电平拉为了低电平,即上电就是“接收”模式。

 

为了实现收发功能,我们必须要在适当的时机拉低或置高收发模式控制位电平。由于笔者这边是将控制位默认拉低电平的,因此这个适当的时机就是在 rk3288 芯片的 UART1 即将要发送数据时将控制位电平置高,在 rk3288 芯片的 UART1 将最后一位数据发送完毕后再将控制位电平拉低。

 

怎么来实现这个目的呢?

 

rk3288 串口的数据收发是通过中断来做的。在笔者的 rk_serial.c 源码中,有两个函数被指定为串口数据的收发中断处理函数:

 

因为前面已经将这个控制位引脚设置为普通输出型 GPIO 口了,所以在一进入 transmit_chars() 函数时就将引脚电平置高,并延时 100us 以等待电平完成拉高操作:

 

需要强调的是,transmit_chars() 函数是中断函数,是不能延时太久的,不然系统会报 BUG。

 

另外,由于串口的通信速率通常比较慢,如 9600bps, 11520bps 等,当发送的数据较长时就会需要比较多的时间。而我们拉低控制位的逻辑就是通过查询串口的中断标志寄存器,等它发送完成后就拉。而我们又不能在中断函数里等,所以需要一个工作队列来帮忙。在串口通信进入到 start_up() 阶段时初始化工作队列,在 transmit_chars() 函数即将跑完时推一个工作队列入栈,在队列函数中做死等操作。

 

在这里笔者选择 tasklet 队列。

 

rk3288 在驱动代码中将串口设备描述为结构体 struct uart_rk_port,我们需要在这个结构体中添加一个 tasklet 变量:

 

在 start_up() 函数中初始化 tasklet 队列:

 

在 transmit_chars() 结束前推入这个 tasklet 队列:

 

队列响应函数中啥事也不干,就查询相关标志位寄存器并死等它的结束,并在查询到相关标志位完成以后立即将控制位引脚电平拉低:

 

这里必须强调一点:上图的 while() 代码块中不要加任何延时函数,最好不要加任何语句。不然的话极有可能会将 cpu 完全抢占导致内核崩溃。

 

这种方式可以满足大多数场景下的 485 通信需求。

 

不过仍然要注意的是,这种软件切换 sp3485 收发模式的方式,在串口发送完最后一位数据以后,仍然会有约 100us 的延时 GPIO_485_EN 脚的电平才会被拉低。换句话说就是 sp3485 在发送完成以后,至少要间隔 100us 才能接收数据。

 

要想缩短这一时间间隔还得通过前面提到的硬件控制自动切换收发模式的方式。

 


 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!