Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1247120
  • 博文数量: 105
  • 博客积分: 127
  • 博客等级: 入伍新兵
  • 技术积分: 962
  • 用 户 组: 普通用户
  • 注册时间: 2011-01-29 15:22
文章分类

全部博文(105)

文章存档

2021年(1)

2019年(3)

2018年(1)

2017年(11)

2016年(47)

2015年(32)

2014年(4)

2012年(6)

我的朋友

分类: 嵌入式

2015-06-10 16:46:03

这周突然需要调试 RS485 方向控制的问题,最开始想想应该问题不大,就加个GPIO口控制就好了,或者用UART RTS来作方向控制。当然很多事情都是想得简单,做起来的时候各种意外。

MX28串口的RTS 管脚无法正常使用,打开流控,PIN脚配置OK ,寄存器配置OK,然RTS管脚一直岿然不动。无果,找FAE,反馈也等于没有反馈。

GPIO 飞线进行测试。应用层操作串口发送前后,分别控制GPIO口高低。惊喜又来了,GPIO口操作有时候会慢好几十个毫秒。应用层也查到tcdrain 这个函数来进行确认是否发送完成,发现
 这个函数最大也有可能需要
20多个毫秒。

以上种种故事,说明了原来的想法不行。就想着GPIO口在串口驱动中去操作。正题也就开始了。首先声明一下,最终这种方法是实现了,但也不是想的那么简单,道路是曲折的。

1,首先参考了如下做法:

http://kuafu80.blog.163.com/blog/static/122647180201431625820150/

修改完成后测试,发现xxx_start_tx , xxx_stop_tx 这两个函数在我所用的ARM 上,当发送数据的时候,只调用了xxx_start_tx ,xxx_stop_tx 是没有调用的。这样该方法也就寿终正寝了。

2,开始发送已经能实现提前使能了,但发送完不能及时关闭。在驱动中查找发送完时的地方,然后添加GPIO口操作。因为串口驱动采用中断方式。


点击(此处)折叠或打开

  1. static void mxs_auart_start_tx(struct uart_port *u)
  2. {
  3.     struct mxs_auart_port *s = to_auart_port(u);

  4.     /* enable transmitter */
  5.     __raw_writel(BM_UARTAPP_CTRL2_TXE, u->membase +                                         HW_UARTAPP_CTRL2_SET);

  6.     mxs_auart_tx_chars(s);
  7. }


  8. static irqreturn_t mxs_auart_irq_handle(int irq, void *context)
  9. {
  10. ...................
  11. //发送相关
  12.     if (istat & BM_UARTAPP_INTR_TXIS) {
  13.         mxs_auart_tx_chars(s);
  14.         istat &= ~BM_UARTAPP_INTR_TXIS;
  15.     }
  16. ......................
  17. }



  18. static inline void mxs_auart_tx_chars(struct mxs_auart_port *s)
  19. {
  20.     struct circ_buf *xmit = &s->port.info->xmit;
  21. ..........................................
  22. //中断方式发送相关
  23.     while (!(__raw_readl(s->port.membase + HW_UARTAPP_STAT) &
  24.          BM_UARTAPP_STAT_TXFF)) {
  25.         if (s->port.x_char) {
  26.             __raw_writel(s->port.x_char,
  27.                  s->port.membase + HW_UARTAPP_DATA);
  28.             s->port.x_char = 0;
  29.             continue;
  30.         }
  31.         if (!uart_circ_empty(xmit) && !uart_tx_stopped(&s->port)) {
  32.             __raw_writel(xmit->buf[xmit->tail],
  33.                  s->port.membase + HW_UARTAPP_DATA);
  34.             xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
  35.             if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
  36.                 uart_write_wakeup(&s->port);
  37.         } else
  38.             break;
  39.     }
  40.     if (uart_circ_empty(&(s->port.info->xmit)))
  41. {
  42.         __raw_writel(BM_UARTAPP_INTR_TXIEN,
  43.              s->port.membase + HW_UARTAPP_INTR_CLR);
  44. tasklet_hi_schedule(&my_task);

  45. }
  46.     else
  47.         __raw_writel(BM_UARTAPP_INTR_TXIEN,
  48.              s->port.membase + HW_UARTAPP_INTR_SET);

  49.     if (uart_tx_stopped(&s->port))
  50.         mxs_auart_stop_tx(&s->port);
  51. }

到了这里,原本一切应该变得很简单,然而当然很多事情都是想得简单,做起来的时候各种意外。CPU 里面内部自带的串口功能没有FIFO为空或者TIMOUT 的中断。只有一个发送中断。

分析源码发现uart_circ_empty 是判断发送队列是否为空的宏。在tx_chars 中会调用 ,tx_chars 在 start_tx 中会调用,在irq_handle 会被调用。一切很明朗了,就在uart_circ_empty函数中调用 gpio_drection_output 就好   了。当然很多事情都是想得简单,做起来的时候各种意外。

uart_circ_empty函数中调用 gpio_drection_output 后,出现了一种情况,发现数据还没有发送完,GPIO口已经拉低了。具体原因是uart_circ_empty 中判断是不是已经加载完到了FIFO,实际并没有实时发送出去,晚了几   个毫秒。这样也满足不了要求。

怎么办啊,怎么办啊?无法知道串口什么时候才把数据完全丢出去,该CPU串口功能中有一个BUSY位是用于轮询模式查看是否发送完成的标志。天无绝人之路,在uart_circ_empty中一直查看BUSY,等待其发完。实验   测试结果,表示好坑爹啊。可能在中断中,轮询16+8=24 个字节的时间。这对于系统来说 ,要了其老命,没法用。这时候想到了中断的下半部操作。自然就是工作队列,任务队列,软中断等。这里就只能引出下一   篇了,linux 中断底半部机制的差异。

揭晓一下最终做的结果,是这样的。startup 相当于open 。打开的时候初始一个任务队列,然后uart_circ_empty中调用。工作队列还真不行,tasklet_hi_schedule 只有这个能达到所需要的实时性。但这里好像会损失CPU        的性能。


点击(此处)折叠或打开

  1. 759 static int mxs_auart_startup(struct uart_port *u)
  2.  760 {
  3.  761 struct mxs_auart_port *s = to_auart_port(u);    `
  4.  762 my_wq_arg = to_auart_port(u) ;
  5.  763
  6.  764 tasklet_init(&my_task,my_wq_func,(unsigned long) s);
  7.  765 ...........
  8.  xxx }

阅读(13137) | 评论(7) | 转发(0) |
给主人留下些什么吧!~~

leibornsean2021-03-08 22:28:31

ispsubb:不要好意,一直没有留意到你的留言,看起来你的问题是已经解决了。 已经贴在这里面了啊,主要就是tasklet ,你可以查看一下我的另一篇,rs485引发的血案。

楼主,您好,中断下半部中也是要判断判断数据是否发送完毕的吗?

回复 | 举报

leibornsean2021-03-08 22:28:24

ispsubb:不要好意,一直没有留意到你的留言,看起来你的问题是已经解决了。 已经贴在这里面了啊,主要就是tasklet ,你可以查看一下我的另一篇,rs485引发的血案。

楼主,您好,中断下半部中也是要判断判断数据是否发送完毕的吗?

回复 | 举报

ispsubb2015-12-04 08:57:25

mzy73962:博主 能把改好的代码发过来么,研究一下。早先也是改了一下驱动,但是发生的情况和一样,没有考虑用到队列去改,想参考一下博主的代码,谢谢

不要好意,一直没有留意到你的留言,看起来你的问题是已经解决了。 已经贴在这里面了啊,主要就是tasklet ,你可以查看一下我的另一篇,rs485引发的血案。

回复 | 举报

ispsubb2015-12-04 08:55:18

mzy73962:按照 博主的方法改成功了,发送结束是后会延时大概1.2ms,请问博主cpu性能是怎么能测试,和轮询16+8=24 个字节的时间是怎么算出来的,谢谢博主

CPU性能这个不太明白你的意思 ,16+8是因为FIFO的原因。FIFO为16,uart_circ_empty你跟一下这个。出现16+8这种情况,只可能在第一次,因为他会预装16个字节到FIFO

回复 | 举报

ispsubb2015-12-04 08:55:18

mzy73962:按照 博主的方法改成功了,发送结束是后会延时大概1.2ms,请问博主cpu性能是怎么能测试,和轮询16+8=24 个字节的时间是怎么算出来的,谢谢博主

CPU性能这个不太明白你的意思 ,16+8是因为FIFO的原因。FIFO为16,uart_circ_empty你跟一下这个。出现16+8这种情况,只可能在第一次,因为他会预装16个字节到FIFO

回复 | 举报