前言
最近需要实现IMX6DL的RS-485 9 Bit模式,在网上和手册中得到了一些帮助,最终还是成功的调出了第九位,下面分享一下经验。
源码分析
-
在IMX6参考手册的UART章节下有关于9-bit RS-485 Mode的详细介绍,当9位模式使能时,发送和接收数据第9位为1表示地址,为0表示数据,要使能并控制9位模式,需要操作以下寄存器中的相关位。
-
源码中我修改了driver\tty\serial\imx.c以及driver\tty\serial\serial_core.c,前者给后者提供接口,后者实现应用层的接口调用。
在serial_core.c 中uart_ioctrl下添加了两个操作命令,一个用来初始化9位模式,另外一个用来单独发送地址数据。具体实现是在imx.c下做的,所以这两个函数只调用Imx.c下实现的函数接口:
3.在imx.c中具体实现imx_rs485_setmode和imx_rs485_setaddr,发送地址数据前需要将TXB8拉高,然后我采用轮询的方式来发送地址数据:
static int imx_rs485_setmode(struct uart_port *port)
{
unsigned long temp;
if (port->rs485.flags & SER_RS485_ENABLED) {
/* 9-bit data or Multidrop Mode (RS-485) Enable */
temp = readl(port->membase + UMCR);
temp |= UMCR_MDEN;
writel(temp, port->membase + UMCR);
/* Enable parity generator and checker && 8-bit transmit and receive character length*/
temp = readl(port->membase + UCR2);
temp |= UCR2_PREN | UCR2_WS;
writel(temp, port->membase + UCR2);
}
return 0;
}
static int imx_rs485_setaddr(struct uart_port *port,
unsigned char __user *rs485_addr)
{
unsigned long temp;
unsigned char addr;
if (copy_from_user(&addr, rs485_addr, sizeof(*rs485_addr)))
return -EFAULT;
if (port->rs485.flags & SER_RS485_ENABLED) {
/* 发送地址前需要将TXB8置高 */
temp = readl(port->membase + UMCR);
temp |= UMCR_TXB8;
writel(temp, port->membase + UMCR);
/* 轮询方式发送地址数据 这个函数就是源码中的imx_poll_put_char函数 */
imx_rs485_put_char(port, addr);
/* 发送完后将TXB8拉低 */
temp = readl(port->membase + UMCR);
temp &= ~UMCR_TXB8;
writel(temp, port->membase + UMCR);
}
Return 0;
}
- 发送数据的话,只需要在imx_start_tx函数中,在数据写入缓冲区前将TXB8拉低即可。
应用程序
应用程序中需要通过Ioctrl TIOCSRS485使能RS485,TIOCSMODE初始化9位模式,TIOCSADDR传输地址数据。
rs485conf.flags |= SER_RS485_ENABLED;
ioctl(fd, TIOCSRS485, &rs485conf);
ioctl(fd, TIOCSMODE, NULL);
ioctl(fd, TIOCSADDR, &addr)