目录
原理图
由于RK3399的外设资源有限,才有了USB转串的芯片(XR21V1414IM48)。
XR21V1414IM48简介
XR21V1414IM48框架图如下所示
通过数据手册知道
-
USB Vendor ID is 0x04E2,
-
USB Product ID is 0x1414 。
这写ID好都存在EEPROM中,通过IIC接口读取。
然后通过6组GPIOS来配置串口的属性。
在内核的源码中进行配置和初始化
在kernel/drivers/usb/serial目录中:
xr_usb_serial_common.c //公共接口
xr_usb_serial_common.h //公共的宏定义
xr_usb_serial_hal.c //硬件初始化
xr_usb_serial_ioctl.h //控制接口初始化
通过配置config进行编译和加载。
进入arm开发板的命令终端后,可以通过lsusb命令看到USB的设备号和产品号,则证明XR这个驱动芯片识别成功。
root@linaro-alip:~# lsusb
Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 006 Device 005: ID 093a:2510 Pixart Imaging, Inc. Optical Mouse
Bus 006 Device 004: ID 05e3:0608 Genesys Logic, Inc. Hub
Bus 006 Device 003: ID 04e2:1414 Exar Corp.
Bus 006 Device 002: ID 0424:2514 Standard Microsystems Corp. USB 2.0 Hub
Bus 006 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 007 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 005 Device 002: ID 0bda:d723 Realtek Semiconductor Corp.
Bus 005 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
root@linaro-alip:~#
在设备节点中可以看到初始化成功的串口。
[root@localhost ~]# ls /dev/ttyXRUSB*
ttyXRUSB0 ttyXRUSB1 ttyXRUSB2 ttyXRUSB3
[root@localhost ~]#
重点代码
函数入口初始化
/*
* Init / exit.
*/
static int __init xr_usb_serial_init(void)
{
int retval;
xr_usb_serial_tty_driver = alloc_tty_driver(XR_USB_SERIAL_TTY_MINORS);
if (!xr_usb_serial_tty_driver)
return -ENOMEM;
xr_usb_serial_tty_driver->driver_name = "xr_usb_serial",
xr_usb_serial_tty_driver->name = "ttyXRUSB",
xr_usb_serial_tty_driver->major = XR_USB_SERIAL_TTY_MAJOR,
xr_usb_serial_tty_driver->minor_start = 0,
xr_usb_serial_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
xr_usb_serial_tty_driver->subtype = SERIAL_TYPE_NORMAL,
xr_usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
xr_usb_serial_tty_driver->init_termios = tty_std_termios;
xr_usb_serial_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD |
HUPCL | CLOCAL;
tty_set_operations(xr_usb_serial_tty_driver, &xr_usb_serial_ops);
retval = tty_register_driver(xr_usb_serial_tty_driver);
if (retval) {
put_tty_driver(xr_usb_serial_tty_driver);
return retval;
}
retval = usb_register(&xr_usb_serial_driver);
if (retval) {
tty_unregister_driver(xr_usb_serial_tty_driver);
put_tty_driver(xr_usb_serial_tty_driver);
return retval;
}
printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");
return 0;
}
tty串口操作集
/*
* TTY driver structures.
*/
static const struct tty_operations xr_usb_serial_ops = {
.install = xr_usb_serial_tty_install,
.open = xr_usb_serial_tty_open,
.close = xr_usb_serial_tty_close,
.cleanup = xr_usb_serial_tty_cleanup,
.hangup = xr_usb_serial_tty_hangup,
.write = xr_usb_serial_tty_write,
.write_room = xr_usb_serial_tty_write_room,
.ioctl = xr_usb_serial_tty_ioctl,
.throttle = xr_usb_serial_tty_throttle,
.unthrottle = xr_usb_serial_tty_unthrottle,
.chars_in_buffer = xr_usb_serial_tty_chars_in_buffer,
.break_ctl = xr_usb_serial_tty_break_ctl,
.set_termios = xr_usb_serial_tty_set_termios,
.tiocmget = xr_usb_serial_tty_tiocmget,
.tiocmset = xr_usb_serial_tty_tiocmset,
};
USB转串初始化
//USB转串初始化
static int xr_usb_serial_tty_install(struct tty_driver *driver, struct tty_struct *tty)
{
struct xr_usb_serial *xr_usb_serial;
int retval;
dev_dbg(tty->dev, "%s\n", __func__);
//添加节点
xr_usb_serial = xr_usb_serial_get_by_index(tty->index);
if (!xr_usb_serial)
return -ENODEV;
//初始化节点
retval = tty_standard_install(driver, tty);
if (retval)
goto error_init_termios;
tty->driver_data = xr_usb_serial;
return 0;
error_init_termios:
tty_port_put(&xr_usb_serial->port);
return retval;
}
设备ID
/*
* USB driver structure.
*/
static const struct usb_device_id xr_usb_serial_ids[] = {
{ USB_DEVICE(0x04e2, 0x1410)},
{ USB_DEVICE(0x04e2, 0x1411)},
{ USB_DEVICE(0x04e2, 0x1412)},
{ USB_DEVICE(0x04e2, 0x1414)}, //XR21V1414IM48芯片
{ USB_DEVICE(0x04e2, 0x1420)},
{ USB_DEVICE(0x04e2, 0x1421)},
{ USB_DEVICE(0x04e2, 0x1422)},
{ USB_DEVICE(0x04e2, 0x1424)},
{ USB_DEVICE(0x04e2, 0x1400)},
{ USB_DEVICE(0x04e2, 0x1401)},
{ USB_DEVICE(0x04e2, 0x1402)},
{ USB_DEVICE(0x04e2, 0x1403)},
{ }
};
测试代码
头文件
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<termios.h>
#include<errno.h>
宏定义
#define FALSE -1
#define TRUE 0
//串口设备名字
static char *serial_name[]={"/dev/ttyXRUSB0","/dev/ttyXRUSB1",
"/dev/ttyXRUSB2","/dev/ttyXRUSB3"};
int speed_arr[] = { B38400, B19200, B9600, B4800, B2400, B1200, B300,B38400, B19200, B9600, B4800, B2400, B1200, B300, };
int name_arr[] = {38400, 19200, 9600, 4800, 2400, 1200, 300, 38400, 19200, 9600, 4800, 2400, 1200, 300, };
输入参数提示
//信息提示
void printf_prompt(void)
{
printf("0:ttyXRUSB0,1:ttyXRUSB1,2:ttyXRUSB2,3:ttyXRUSB3\n");
printf("please input the serial port for writing:");
}
打开设备
/*************************************************
*打开设备
*************************************************/
int OpenDev(char *Dev)
{
int fd = open( Dev, O_RDWR | O_NOCTTY | O_NDELAY); //| O_NOCTTY | O_NDELAY
if (fd <0){
perror("Can't Open Serial Port");
exit(EXIT_FAILURE);
}
return fd;
}
设置波特率
//设置波特率
void set_speed(int fd, int speed)
{
int i;
int status;
struct termios Opt;
tcgetattr(fd, &Opt);
for ( i= 0; i < sizeof(speed_arr) / sizeof(int); i++)
{
if (speed == name_arr[i])
{
tcflush(fd, TCIOFLUSH);
cfsetispeed(&Opt, speed_arr[i]);
cfsetospeed(&Opt, speed_arr[i]);
status = tcsetattr(fd, TCSANOW, &Opt);
if (status != 0)
{
perror("tcsetattr fd1");
return;
}
tcflush(fd,TCIOFLUSH);
}
}
}
配置数据位、停止位、校验位
//配置参数
int set_Parity(int fd,int databits,int stopbits,int parity)
{
struct termios options;
if ( tcgetattr( fd,&options) != 0)
{
perror("SetupSerial 1");
return(FALSE);
}
options.c_cflag &= ~CSIZE;
//数据位
switch (databits)
{
case 7:
options.c_cflag |= CS7;
break;
case 8:
options.c_cflag |= CS8;
break;
default:
fprintf(stderr,"Unsupported data size\n");
return (FALSE);
}
//校验位
switch (parity)
{
case 'n':
case 'N':
options.c_cflag &= ~PARENB; /* Clear parity enable */
options.c_iflag &= ~INPCK; /* Enable parity checking */
break;
case 'o':
case 'O':
options.c_cflag |= (PARODD | PARENB);
options.c_iflag |= INPCK; /* Disnable parity checking */
break;
case 'e':
case 'E':
options.c_cflag |= PARENB; /* Enable parity */
options.c_cflag &= ~PARODD;
options.c_iflag |= INPCK; /* Disnable parity checking */
break;
case 'S':
case 's': /*as no parity*/
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
break;
default:
fprintf(stderr,"Unsupported parity\n");
return (FALSE);
}
//停止位
switch (stopbits)
{
case 1:
options.c_cflag &= ~CSTOPB;
break;
case 2:
options.c_cflag |= CSTOPB;
break;
default:
fprintf(stderr,"Unsupported stop bits\n");
return (FALSE);
}
/* Set input parity option */
if (parity != 'n')
options.c_iflag |= INPCK;
//清空缓冲
tcflush(fd,TCIFLUSH);
options.c_cc[VTIME] = 150;
options.c_cc[VMIN] = 0; /* Update the options and do it NOW */
if (tcsetattr(fd,TCSANOW,&options) != 0)
{
perror("SetupSerial 3");
return (FALSE);
}
return (TRUE);
}
主函数
//主函数,
int main(void)
{
int fd,i;
//提示信息
printf_prompt();
scanf("%d",&i);
//打开串口
fd = OpenDev(serial_name[i]);
printf("This program updates last time at %s %s\n",__TIME__,__DATE__);
printf("STDIO COM1\n");
//设置波特率
set_speed(fd,115200);
//设置数据位、停止位、校验位
if (set_Parity(fd,8,1,'N') == FALSE) {
printf("Set Parity Error\n");
exit (0);
}
//写数据
char buf[] = "helloworld";
write(fd,&buf,26);
char buff[512];
int nread;
while(1)
{
//读数据
if((nread = read(fd, buff, 512))>0)
{
printf("\nLen: %d\n",nread);
buff[nread+1] = '\0';
printf("%s",buff);
//写数据
write(fd,&buf,26);
}
}
close(fd);
return 0;
}
Makefile文件
#--------------------------------- /* execute file(s) */
TESTFILE = uart_test
#--------------------------------- /* object file(s) */
SRCFILE = uart_test.c
#--------------------------------- /* header file(s) */
TESTFILE_H =
CROSS = aarch64-linux-gnu-
CC = $(CROSS)gcc
AS = $(CROSS)as
LD = $(CROSS)ld
CFLAGS += -O2 -Wall
all: $(TESTFILE)
$(TESTFILE): $(SRCFILE) $(TESTFILE_H) Makefile
$(CC) $(CFLAGS) -o $@ $@.c -static
clean:
rm -f $(TESTFILE) *.o
测试代码
编译源码
在ubuntu中,编译测试文件
make
得到串口测试文件uart_test文件。
执行测试程序
chmod 777 uart_test
./uart_test
实验现象
串口发送的字符串,会打印出来。