linux下实现RS485驱动,消除DE引脚抖动
创始人
2025-05-29 19:48:18

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

linux下实现RS485驱动,消除DE引脚抖动


前言

RS485驱动其实就是串口驱动外加一个GPIO驱动,GPIO驱动很好实现,但是RS485最大的问题在于DE引脚拉高拉低的时候,带来的延时,会影响到数据的收发,本文主要介绍6M串口下,几种收发影响。


一、linux下高速串口的实现

本文使用的串口波特率是6M串口,因为使用场景比较固定,所以就把波特率写死了,动态调整也是同一个思路。

linux下的串口波特率的定义

目前我使用的是linux 4.9.113版本,linux定义串口波特率的位置是

/** Routine which returns the baud rate of the tty** Note that the baud_table needs to be kept in sync with the* include/asm/termbits.h file.*/
static const speed_t baud_table[] = {0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,9600, 19200, 38400, 57600, 115200, 230400, 460800,
#ifdef __sparc__76800, 153600, 307200, 614400, 921600
#else500000, 576000, 921600, 1000000, 1152000, 1500000, 2000000,2500000, 3000000, 3500000, 6000000
#endif
};#ifndef __sparc__
static const tcflag_t baud_bits[] = {B0, B50, B75, B110, B134, B150, B200, B300, B600,B1200, B1800, B2400, B4800, B9600, B19200, B38400,B57600, B115200, B230400, B460800, B500000, B576000,B921600, B1000000, B1152000, B1500000, B2000000, B2500000,B3000000, B3500000, B6000000
};
#else
static const tcflag_t baud_bits[] = {B0, B50, B75, B110, B134, B150, B200, B300, B600,B1200, B1800, B2400, B4800, B9600, B19200, B38400,B57600, B115200, B230400, B460800, B76800, B153600,B307200, B614400, B921600
};
#endif

按照上述数组,我把4M对应的改为自己的6M波特率,我是用的是ARM64架构,头文件修改的路径是:

linux/include/uapi/asm-generic/termbits.h。。。。。。。。。。
#define   B460800 0010004
#define   B500000 0010005
#define   B576000 0010006
#define   B921600 0010007
#define  B1000000 0010010
#define  B1152000 0010011
#define  B1500000 0010012
#define  B2000000 0010013
#define  B2500000 0010014
#define  B3000000 0010015
#define  B6000000 0010017  //这里是修改的地方
。。。。。

下面修改相关程序,在sdk里面找到你自己底层的uart驱动程序,修改

baud = uart_get_baud_rate(port, termios, old, 9600, 6100000);

这个函数是设置波特率的函数,限制了波特率的最大和最小值,当然有的sdk驱动里面不一定调用该函数,所以需要自己排查一下,有的话就把最大值修改一下。

做完以上工作后驱动就可以编译通过了,但是,在实际编译应用程序的时候,还有可能报错,因为应用程序的库链接是和你的交叉编译环境有关,所以用户需要手动到自己对应编译环境下,修改termbits.h,这里有个技巧,如果用户找不到自己的环境路径,就把linux/include/uapi/asm-generic/termbits.h这个路径链家进去,编译器会提示冲突的(哈哈)

二、使用GPIO驱动实现DE的控制引脚

1.GPIO驱动的实现

GPIO驱动的实现网上教程很多,这里就不直接贴代码了:
首先需要修改设备树,在修改自己的GPIO驱动,gpiod_direction_output或者gpiod_set_value设置的值是逻辑值,不一定是物理值。


使用这个方式适合读写响应比较慢的场景,不适合速度快的场景

2. 在串口驱动里面加GPIO驱动

首先每一个串口包含两个FIFO,一个是发送FIFO,一个是接收FIFO,而且每一个FIFO的深度是可以调节的(用具体的寄存器),驱动里面默认配置是32字节的FIFO深度,这里一般在probe里面会设置FIFO的深度,这个决定了中断的触发方式。

需要注意的是一般来说SDK里面所有的串口,都是共有一个串口驱动的,所以我们在修改这个驱动前一定要特别注意,用能力级区分开RS485驱动和一般的驱动。

下面我们使用最简单的区分方式,使用串口的ID区分。比如串口四是RS485,可以使用pdev->id==4来判断,该方法写死了,不具备通用性,不建议照搬(会被主管骂死)

static int meson_uart_probe(struct platform_device *pdev)
{struct resource *res_mem, *res_irq;struct uart_port *port;struct meson_uart_port *mup;struct clk *clk;const void *prop;int ret = 0;if (pdev->dev.of_node)pdev->id = of_alias_get_id(pdev->dev.of_node, "serial");if (pdev->id < 0 || pdev->id >= AML_UART_PORT_MAX)return -EINVAL;res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);if (!res_mem)return -ENODEV;res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);if (!res_irq)return -ENODEV;if (meson_ports[pdev->id]) {dev_err(&pdev->dev, "port %d already allocated\n", pdev->id);return -EBUSY;}mup = devm_kzalloc(&pdev->dev,sizeof(struct meson_uart_port), GFP_KERNEL);if (!mup)return -ENOMEM;
#if 1if (pdev->id == 4){gpio_de = gpiod_get(&pdev->dev, "ys485de", 0);if (IS_ERR(gpio_de)){printk("gpiod get error %d \n", pdev->id);}else{printk("gpiod get success %d \n", pdev->id);}}	
#endif//spin_lock_init(&mup->wr_lock);port = &mup->port;
#ifdef CONFIG_AMLOGIC_CLKclk = devm_clk_get(&pdev->dev, "clk_gate");if (IS_ERR(clk)) {pr_err("%s: clock gate not found\n", dev_name(&pdev->dev));/* return PTR_ERR(clk); */} else {ret = clk_prepare_enable(clk);if (ret) {pr_err("uart: clock failed to prepare+enable: %d\n",ret);clk_put(clk);/* return ret; */}}clk = devm_clk_get(&pdev->dev, "clk_uart");if (IS_ERR(clk)) {pr_err("%s: clock source not found\n", dev_name(&pdev->dev));/* return PTR_ERR(clk); */}if (!IS_ERR(clk))port->uartclk = clk_get_rate(clk);
#endifport->fifosize = 64;prop = of_get_property(pdev->dev.of_node, "fifosize", NULL);if (prop)port->fifosize = of_read_ulong(prop, 1);if (!xtal_tick_en) {prop = of_get_property(pdev->dev.of_node, "xtal_tick_en", NULL);if (prop) {xtal_tick_en = of_read_ulong(prop, 1);if (xtal_tick_en == 1)xtal_tick_en = 0;}}port->iotype = UPIO_MEM;port->mapbase = res_mem->start;port->irq = res_irq->start;port->flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP | UPF_LOW_LATENCY;port->dev = &pdev->dev;port->line = pdev->id;port->type = PORT_MESON;port->x_char = 0;port->ops = &meson_uart_ops;meson_ports[pdev->id] = mup;platform_set_drvdata(pdev, port);if (of_get_property(pdev->dev.of_node, "pinctrl-names", NULL)) {mup->p = devm_pinctrl_get_select_default(&pdev->dev);/* if (!mup->p) *//* return -1; */}ret = uart_add_one_port(&meson_uart_driver, port);if (ret)meson_ports[pdev->id] = NULL;prop = of_get_property(pdev->dev.of_node, "support-sysrq", NULL);if (prop)support_sysrq = of_read_ulong(prop, 1);return ret;
} corresponding to the given hw number for this chip.

在串口的读写函数比较复杂,这里不在赘述,可以参考下面几个博客的内容,这里只说方法。

https://blog.csdn.net/Luckiers/article/details/123577836
https://blog.csdn.net/lizuobin2/article/details/51773305
https://blog.csdn.net/sharecode/article/details/9196591

串口的open函数,对应于,struct uart_ops结构体的 .startup回调函数

static struct gpio_desc *gpio_de = NULL;
struct tasklet_struct   stGpioTasklet;static int meson_uart_startup(struct uart_port *port)
{u32 val;int ret = 0;val = readl(port->membase + AML_UART_CONTROL);val |= (AML_UART_RX_RST | AML_UART_TX_RST | AML_UART_CLR_ERR);writel(val, port->membase + AML_UART_CONTROL);val &= ~(AML_UART_RX_RST | AML_UART_TX_RST | AML_UART_CLR_ERR);writel(val, port->membase + AML_UART_CONTROL);val |= (AML_UART_RX_EN | AML_UART_TX_EN);writel(val, port->membase + AML_UART_CONTROL);val |= (AML_UART_RX_INT_EN | AML_UART_TX_INT_EN);val &= ~UART_CTS_EN;writel(val, port->membase + AML_UART_CONTROL);
#if 1if (port->line == 4){gpiod_direction_output(gpio_de, 0);tasklet_init(&stGpioTasklet, gpio_485_do_tasklet, (unsigned long)port);}
#endifreturn ret;
}void gpio_485_do_tasklet(unsigned long param)
{struct uart_port *port;port = (struct uart_port *)param;while (meson_uart_tx_empty(port) != TIOCSER_TEMT){;}	gpiod_set_value(gpio_de, 0);
}

struct uart_ops结构体里面.start_tx的回调函数实现了write的函数

static void meson_uart_start_tx(struct uart_port *port)
{struct circ_buf *xmit = &port->state->xmit;unsigned int ch;struct meson_uart_port *mup = to_meson_port(port);unsigned long flags;spin_lock_irqsave(&mup->wr_lock, flags);if (port->line == 4){gpiod_set_value(gpio_de, 1);}while (!uart_circ_empty(xmit)) {if (!(readl(port->membase + AML_UART_STATUS) &AML_UART_TX_FULL)) {ch = xmit->buf[xmit->tail];writel(ch, port->membase + AML_UART_WFIFO);xmit->tail = (xmit->tail + 1) & (SERIAL_XMIT_SIZE - 1);port->icount.tx++;} elsebreak;}#if 0if (port->line == 4){while (meson_uart_tx_empty(port) != TIOCSER_TEMT){;}gpiod_set_value(gpio_de, 0);}
#endifspin_unlock_irqrestore(&mup->wr_lock, flags);
}

需要解释一下,当meson_uart_start_tx(struct uart_port *port)函数里面把GPIO拉高以后,函数结尾
#if 0 … #endif 里面的部分是不可以直接拉低的,因为uart_circ_empty里面为空后,硬件层面可能没有吧数据发送完,会出现发送不及时的情况,如下图:在这里插入图片描述
在EO后面应该还有数据,但是我已经无法继续写入。

总结

到这里基本上就可以实现us级别的延时,如果您还想延时更加精确点,可以做的改善有:

  1. 使用实时内核
  2. 串口中断绑定到一个核上面
  3. 提高时钟
    如果上述都不可以,那你就用USB转串口吧,哈哈~

相关内容

热门资讯

电子拣货标签3代系统简介 CK_Label_v3 一、产品参数  1. 电池供电版 产品型号 CK_Label_v3 尺...
2023新车上牌费用是多少?上... 今天给各位分享2023新车上牌费用是多少?上牌照需要多少钱的知识,其中也会对2022年上牌进行解释,...
荣事达竟然是美的集团旗下品牌?... 今天给各位分享荣事达竟然是美的集团旗下品牌?!的知识,其中也会对荣事达美的合并了吗进行解释,如果能碰...
火车站次查询(火车站查询车次)... 本篇文章极速百科给大家谈谈火车站次查询,以及火车站查询车次对应的知识点,希望对各位有所帮助,不要忘了...
分集水器的详细使用方法,学会供... 本篇文章极速百科给大家谈谈分集水器的详细使用方法,学会供暖不会再有办法,以及分集水器构造详图对应的知...
c++ error:cross... 最近在写代码的时候,碰到了 crosses initialization of ......
机器学习模型的性能评估方法 动动发财的小手,点个赞吧! 部署模型后,监控其性能对于确保...
俄罗斯土地面积多少平方公里(世... 本篇文章极速百科给大家谈谈俄罗斯土地面积多少平方公里,以及世界领土最大的三个国家对应的知识点,希望对...
ysl83是什么颜色 极速百科... ysl83是什么颜色目录ysl83是什么颜色ysl83是什么颜色圣罗兰纯魅唇膏86号适合黄皮女生素颜...
信用贷款怎么申请,个人信用贷款... 信用贷款怎么申请目录信用贷款怎么申请个人信用贷款怎么贷如何办理个人信用贷款?个人信用贷款怎么贷信用贷...
泾县有哪些景点,泾县十大必去景... 泾县有哪些景点目录泾县有哪些景点泾县十大必去景点泾县旅游景点泾县景点泾县有哪些景点 泾县位于安...
Leveraging Sali... Leveraging Saliency in Single-Stage Multi-Label Co...
ES-数据建模 数据模型是描述现实世界某种现象或者状态的物理抽象,比如我们之前用FSA来描述周老师的一...
进击的巨人大结局,进击的巨人漫... 进击的巨人大结局目录进击的巨人大结局进击的巨人漫画最终结局漫画《进击的巨人》大结局是什么?进击的巨人...
免单活动是什么意思 极速百科网... 免单活动是什么意思目录免单活动是什么意思免单活动是什么意思告诉我,免单是什么意思免单活动是什么意思免...
meld是手机里的什么,mel... meld是什么意思? meld是什么意思?作为全球唯一的标识,作用相当于我们每个人的。扩展资料:查找...
中华什么多奇志(中华什么多奇志... 本篇文章极速百科给大家谈谈中华什么多奇志,以及中华什么多奇志不爱红装爱武装英文对应的知识点,希望对各...
Linux C++实现进程间通... 基本知识 基本知识介绍参考:https://mp.weixin.qq.com/s/oS...
2023年ACM竞赛班 202...  目录 瞎编乱造第一题 瞎编乱造第二题 瞎编乱造第三题 瞎编乱造第四题 瞎编乱造第五题 不是很想编了...
windows安装包管理工具C... Chocolatey介绍Chocolatey 通过使用通用打包格式来管理 Windows 软件的各个...
解决:centos7如何解决网... 遇到此类问题可能会有多重解决方法,需要一个一个的去排除。 1、查看自己的网络设置是不是...
百克特1103是什么意思,请问... 百克特1103是什么意思目录百克特1103是什么意思请问,白克特1101和1103是什么病毒扫地机器...
无话不谈彼此陪伴什么意思,无话... 无话不谈彼此陪伴什么意思目录无话不谈彼此陪伴什么意思无话不谈是什么意思无话可说和无话不谈有什么区别?...
58同城企业认证的注册号是什么... 58同城企业认证的注册号是什么目录58同城企业认证的注册号是什么企业认证里的人注册号是什么我在58同...
草字头的字(草字头的字和什么有... 本篇文章极速百科给大家谈谈草字头的字,以及草字头的字和什么有关一年级对应的知识点,希望对各位有所帮助...
CentOS8提高篇2:Cen... 一、下载安装包 到wps官网下载linux版本的安装包, 根据自己的linux是 32...
这款开源管理文档管理系统兼容P... demo软件园每日更新资源,请看到最后就能获取你想要的: 1.《计算机网络实验教程》课后答案   ...
SpringBoot学习笔记(... 测试 加载测试专用属性 在启动测试环境时可以通过properties参数设置测试环境专用的属性 &#...
东方明珠什么时候开灯,东方明珠... 东方明珠什么时候开灯目录东方明珠什么时候开灯东方明珠几点开灯东方明珠几点亮灯?东方明珠几点开灯东方明...
一亩等于几分地一分地多少平方,... 一亩等于几分地一分地多少平方目录一亩等于几分地一分地多少平方一亩几分地?一分几个平方一分地多少平方一...