||
把串口设备由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已经定义了TIOCGRS485、TIOCSRS485 的常量了;
只有这个函数没有定义的常量,才会调用default中的ret = uport->ops->ioctl(uport, cmd, arg);自定义ioctl函数 ,也就8250_core.c的ioctl函数;
把上边2个case的内容一屏蔽;
重新编译内核。。。。。应以程序还是报告"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()那么RS485的RTS引脚不就申请成功了么;
按照这样修改,重新编译内核,RS485串口RST引脚注册成功,ioctl TIOCSRS485 操作通过,RS485串口运行正常。
QQ交流群:761781147