mapleft的笔记 https://bbs.21ic.com/?41444 [收藏] [复制] [RSS]

日志

AM3352 串口升级为8250模式后,RS485模式失效及处理

已有 1256 次阅读2020-7-14 09:36 |个人分类:工蚁手记|系统分类:兴趣爱好

AM3352原来使用为TI官方提供的OMAP  serial port 串口驱动,使用一段时间后发现,这个串口是么有启动用DMA 模式发送的, 尽管你在设备树种声明可DNA通道,
这有啥问题呢,就是发送串口数据时,例如发送100个字节,大部分的情况下,这100个字节是连续发送出去的,但是遇到系统繁忙时,数据就不是连续发送了,中间会插入5-10ms的延时,这个时间是不确定的(由内核处理的事务多少而定),看了下OMAP  serial port 串口驱动代码。串口发送机制大概就是把数据分成若干帧(帧长度为FIFO大小)写满串口的FIFO ,然后FIFO空闲时中断再填充下一帧数据,直到数据发送完成。 这个方案遇到系统繁忙时,FIFO中断被延后就会出现数据发送不连续的问题。

然后有大神告知8250串口默认都是开启DMA模式的,便试试;

其实升级8250串口很简单,都不用几分钟就升级完成了

把串口设备由OMAP serial 改为8250 串口即可,见上图;

编译内核,更新内核。需要说明的是之后/dev/下的串口驱动名字会变成ttyS0,ttyS1。。。等,不再是原来的ttyO1,ttyO2;

所以需要把/etc目录下的inittab文件的控制台初始化语句改动掉

# Put a getty on the serial port

ttyS0::respawn:/sbin/getty -L  ttyS0 115200 vt100 # GENERIC_SERIAL


之后我们测试AM3352的串口,果然串口发送数据不连续的问题解决了。。。。  开心中。。。。;

正准备划水时,测试发现RS485串口不工作了,RST引脚一直为高电平,正常你妹呀;

RS485功能原来用omap serial port 是好的呀,这下可好了,把锅补出个大洞了。

后来查代码得知,8250的串口驱动压根就没有RS485的相关部分代码,必须自己添加


按照外网的连接对8250_core.c RS485
补丁

http://lkml.iu.edu/hypermail/linux/kernel/1407.1/01890.html

 然后编译内核,下载运行。
然而你要的RS485通讯功能并没有出现

应用程序初始化RS485串口函数打印出异常:printf("ioctl TIOCSRS485 error.\r\n");

哎。。。。免费的确实是最贵的;又开始填坑之类旅。。。

 先看应用程序的异常打印代码:


if (ioctl (fd, TIOCSRS485, &rs485conf) <
0)   
//将填充的结构体写入到文件描述符中

       {

              /* Error handling.*/

              printf("ioctl TIOCSRS485
error.\r\n"
);

       }

 

ioctl函数引起的,我们就去8250_core.c哪里看看

 static int serial8250_ioctl(struct
uart_port *port, unsigned int cmd,

                   unsigned
long arg)

{

         struct
serial_rs485 rs485conf;

         struct
uart_8250_port *up;

 up
= container_of(port, struct uart_8250_port, port);

 if
(!up->rs485_config)

     
        return -ENOIOCTLCMD;
 

         switch
(cmd)

         {

         case
TIOCSRS485:

                   if
(!gpio_is_valid(up->rts_gpio))

                            return
-ENODEV;

                   if
(copy_from_user(&rs485conf, (void __user *) arg,

                                               sizeof(rs485conf)))

                            return
-EFAULT;

 

                   serial8250_config_rs485(port,
&rs485conf);

                   break;

 

         case
TIOCGRS485:

                   if
(!gpio_is_valid(up->rts_gpio))

                            return
-ENODEV;

                   if
(copy_to_user((void __user *) arg, &up->rs485,

                                               sizeof(rs485conf)))

                            return
-EFAULT;

                   break;

 

         default:

                   return
-ENOIOCTLCMD;

         }

         return
0;

}

 

 

通过prink函数打印,得出:ioctl 写入的cmd值并没有等于
TIOCSRS485

反复检测应用程序,没有错呀;(应用程序基于omap-serial.c模式时,RS-485可是工作得好好的呢)

真是百思不得骑姐;

 

后来在
drivers\tty\serial\ serial_core.c找到串口真正的ioctl入口函数

 

/*

 *
Called via sys_ioctl.  We can use
spin_lock_irq() here.

 */

static int

uart_ioctl(struct tty_struct *tty, unsigned
int cmd,

            unsigned long arg)

{

         struct
uart_state *state = tty->driver_data;

         struct
tty_port *port = &state->port;

         void
__user *uarg = (void __user *)arg;

         int
ret = -ENOIOCTLCMD;

 

 

         /*

          * These ioctls don't rely on the hardware to
be present.

          */

         switch
(cmd) {

         case
TIOCGSERIAL:

                   ret
= uart_get_info_user(port, uarg);

                   break;

 

         case
TIOCSSERIAL:

                   down_write(&tty->termios_rwsem);

                   ret
= uart_set_info_user(tty, state, uarg);

                   up_write(&tty->termios_rwsem);

                   break;

 

         case
TIOCSERCONFIG:

                   down_write(&tty->termios_rwsem);

                   ret
= uart_do_autoconfig(tty, state);

                   up_write(&tty->termios_rwsem);

                   break;

 

         case
TIOCSERGWILD: /* obsolete */

         case
TIOCSERSWILD: /* obsolete */

                   ret
= 0;

                   break;

         }

 

         if
(ret != -ENOIOCTLCMD)

                   goto
out;

 

         if
(tty->flags & (1 << TTY_IO_ERROR)) {

                   ret
= -EIO;

                   goto
out;

         }

 

         /*

          * The following should only be used when
hardware is present.

          */

         switch
(cmd) {

         case
TIOCMIWAIT:

                   ret
= uart_wait_modem_status(state, arg);

                   break;

         }

 

         if
(ret != -ENOIOCTLCMD)

                   goto
out;

 

         mutex_lock(&port->mutex);

 

         if
(tty->flags & (1 << TTY_IO_ERROR)) {

                   ret
= -EIO;

                   goto
out_up;

         }

 

         /*

          * All these rely on hardware being present and
need to be

          * protected against the tty being hung up.

          */

 

         switch
(cmd) {

         case
TIOCSERGETLSR: /* Get line status register */

                   ret
= uart_get_lsr_info(tty, state, uarg);

                   break;

 

         case
TIOCGRS485:

                   ret
= uart_get_rs485_config(state->uart_port, uarg);

                   break;

 

         case
TIOCSRS485:

                   ret
= uart_set_rs485_config(state->uart_port, uarg);

                   break;

         default:
{

                   struct
uart_port *uport = state->uart_port;

                   if
(uport->ops->ioctl)

                            ret
= uport->ops->ioctl(uport, cmd, arg);

                   break;

         }

         }

out_up:

         mutex_unlock(&port->mutex);

out:

         return
ret;

}

原来这个函数中的case已经定义了TIOCGRS485TIOCSRS485 的常量了;

只有这个函数没有定义的常量,才会调用default中的ret = uport->ops->ioctl(uport, cmd, arg);自定义ioctl函数 ,也就8250_core.cioctl函数;

把上边2case的内容一屏蔽;
重新编译内核。。。。。应以程序还是报告"ioctl TIOCSRS485 error.

 

再回去8250_core.c中看serial8250_ioctl() 的代码,
通过打印报告得出

         if
(!gpio_is_valid(up->rts_gpio))

                            return
-ENODEV;

函数判断RS-485 rts引脚无效,然后就退出了,看来解决了rts引脚注册的问题,就能解决问题了;

 

RS-485补丁中8250_core.c中有个serial8250_probe_rs485()函数, 负责根据设备树的配置信息去申请对应的gpio,来控制rts引脚

 

int serial8250_probe_rs485(struct
uart_8250_port *up,

                   struct
device *dev)

{

         struct
serial_rs485 *rs485conf = &up->rs485;

         struct
device_node *np = dev->of_node;

         u32
rs485_delay[2];

         enum
of_gpio_flags flags;

         int
ret;

 

         rs485conf->flags
= 0;

         if
(!np)

                   return
0;

 

         /*
check for tx enable gpio */

         up->rts_gpio
= of_get_named_gpio_flags(np, "rts-gpio", 0, &flags);

         if
(up->rts_gpio == -EPROBE_DEFER)

                   return
-EPROBE_DEFER;

         if
(!gpio_is_valid(up->rts_gpio))

                   return
0;

 

         ret
= devm_gpio_request(dev, up->rts_gpio, "serial_rts");

         if
(ret < 0)

                   return
ret;

         ret
= gpio_direction_output(up->rts_gpio,

                            flags
& SER_RS485_RTS_AFTER_SEND);

         if
(ret < 0)

                   return
ret;

 

         up->rts_gpio_valid
= true;

 

         if
(of_property_read_bool(np, "rs485-rts-active-high"))

                   rs485conf->flags
|= SER_RS485_RTS_ON_SEND;

         else

                   rs485conf->flags
|= SER_RS485_RTS_AFTER_SEND;

 

         if
(of_property_read_u32_array(np, "rs485-rts-delay",

                                     rs485_delay,
2) == 0) {

                   rs485conf->delay_rts_before_send
= rs485_delay[0];

                   rs485conf->delay_rts_after_send
= rs485_delay[1];

         }

 

         if
(of_property_read_bool(np, "rs485-rx-during-tx"))

                   rs485conf->flags
|= SER_RS485_RX_DURING_TX;

 

         if
(of_property_read_bool(np, "linux,rs485-enabled-at-boot-time"))

                   rs485conf->flags
|= SER_RS485_ENABLED;

 

         return
0;

}

EXPORT_SYMBOL_GPL(serial8250_probe_rs485);

 

奇怪的是在内核的源码中,居然没有找到调用这个函数的地方,
而且这个函数还是被声明为:EXPORT_SYMBOL_GPL的呢

 

分析串口相关的代码,理清串口注册的流程如下:

Drivers\tty\serola\8250_omap.c  负责把AM335X 的串口注册为8250模式串口

 

8250_omap.c 函数中

static int omap8250_probe(struct
platform_device *pdev)

 

有这一行函数:

         ret
= serial8250_register_8250_port(&up);

 

 

serial8250_register_8250_port()函数位于8250_core.c中,为核心函数

这个函数就把AM3352的串口注册为 8250模式,

后来想serial8250_probe_rs485()函数不是声明为:EXPORT_SYMBOL_GPL 外部调用的函数么?

那我们在serial8250_register_8250_port()函数前面调用下serial8250_probe_rs485()那么RS485RTS引脚不就申请成功了么;

按照这样修改,重新编译内核,RS485串口RST引脚注册成功,ioctl TIOCSRS485 操作通过,RS485串口运行正常。

QQ交流群:761781147



路过

鸡蛋

鲜花

握手

雷人

评论 (0 个评论)