软件环境:vivado 2017.4 硬件平台:XC7Z020
工程还是之前那篇的那个工程,今天说下怎么把axi_uartlite加到linux下,并使用起来。
工程编译完成,生成设备树以后,看眼设备树pl.dtsi,确定是如下的样子。
/ {
amba_pl: amba_pl {
#address-cells = <1>;
#size-cells = <1>;
compatible = "simple-bus";
ranges ;
axi_uartlite_0: serial@42c00000 {
clock-names = "s_axi_aclk";
clocks = <&clkc 15>;
compatible = "xlnx,xps-uartlite-1.00.a";
current-speed = <115200>;
device_type = "serial";
interrupt-names = "interrupt";
interrupt-parent = <&intc>;
interrupts = <0 29 1>;
port-number = <0>;
reg = <0x42c00000 0x10000>;
xlnx,baudrate = <0x1c200>;
xlnx,data-bits = <0x8>;
xlnx,odd-parity = <0x0>;
xlnx,s-axi-aclk-freq-hz-d = "50.0";
xlnx,use-parity = <0x0>;
};
};
};
这里面的xInx,baudrate、xInx,data-bits、xInx,odd-parity和vivado中axi_uartlite设置的参数是保持一致的,和上篇说过的一样,如果要调整波特率、数据位数、奇偶校验等参数,在vivado中就必须重新设置并生成bitstream。
linux下axi_uartlite使用的驱动是 xlnx,xps-uartlite-1.00.a,在编译内核的时候,需要按路径依次打开如下选项。Device Drivers---Character devices---Serial drivers。
添加对xilinx uartlite的支持,然后就可以保存,编译了。
哦对了,在做设备树的时候,一定注意一下调试串口,默认的好像是uartlite,把这里要改回去,不然kernel就从uartlite打印了。
系统跑起来以后,在/dev下就能看到axi_uartlite设备了,名字叫ttyUL1。
应用层代码如下。
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
int uart_configure(int fd,int nSpeed, int nBits, char nEvent, int nStop)
{
struct termios newtio,oldtio;
if ( tcgetattr( fd,&oldtio) != 0) {
perror("SetupSerial 1");
return -1;
}
bzero( &newtio, sizeof( newtio ) );
newtio.c_cflag |= CLOCAL | CREAD;
newtio.c_cflag &= ~CSIZE;
switch( nBits )
{
case 7:
newtio.c_cflag |= CS7;
break;
case 8:
newtio.c_cflag |= CS8;
break;
}
switch( nEvent )
{
case 'O':
newtio.c_cflag |= PARENB;
newtio.c_cflag |= PARODD;
newtio.c_iflag |= (INPCK | ISTRIP);
break;
case 'E':
newtio.c_iflag |= (INPCK | ISTRIP);
newtio.c_cflag |= PARENB;
newtio.c_cflag &= ~PARODD;
break;
case 'N':
newtio.c_cflag &= ~PARENB;
break;
}
switch( nSpeed )
{
case 2400:
cfsetispeed(&newtio, B2400);
cfsetospeed(&newtio, B2400);
break;
case 4800:
cfsetispeed(&newtio, B4800);
cfsetospeed(&newtio, B4800);
break;
case 9600:
cfsetispeed(&newtio, B9600);
cfsetospeed(&newtio, B9600);
break;
case 115200:
cfsetispeed(&newtio, B115200);
cfsetospeed(&newtio, B115200);
break;
case 460800:
cfsetispeed(&newtio, B460800);
cfsetospeed(&newtio, B460800);
break;
default:
cfsetispeed(&newtio, B9600);
cfsetospeed(&newtio, B9600);
break;
}
if( nStop == 1 )
newtio.c_cflag &= ~CSTOPB;
else if ( nStop == 2 )
newtio.c_cflag |= CSTOPB;
newtio.c_cc[VTIME] = 0;
newtio.c_cc[VMIN] = 0;
tcflush(fd,TCIFLUSH);
if((tcsetattr(fd,TCSANOW,&newtio))!=0)
{
perror("com set error");
return -1;
}
return 0;
}
void main()
{
int fd,recv_cnt;
char *uart_pl = "/dev/ttyUL1";
char buffer[1500];
char *uart_out = "start pl uart application\r\n";
memset(buffer, 0, sizeof(buffer));
fd = open(uart_pl, O_RDWR|O_NOCTTY);
if(fd < 0)
printf("open %s is failed",uart_pl);
uart_configure(fd, 115200, 8, 'N', 1);
write(fd,uart_out, strlen(uart_out));
while(1){
recv_cnt = read(fd, buffer, 1500);
if(recv_cnt > 0)
{
buffer[recv_cnt+1] = '\0';
write(fd,buffer,strlen(buffer));
memset(buffer, 0, strlen(buffer));
recv_cnt = 0;
}
}
}
串口助手测试,一切OK。
最后呢,备注两点:
1.按照道理呢, 代码中open(uart_pl, O_RDWR|O_NOCTTY)没加O_NONBLOCK,应该是阻塞模式,也就是说在串口没接收到东西的时候,会一直停在read(fd, buffer, 1500)这里,但是我在read下面加调试打印后,可以不断打印出来,说明就没有阻塞住,这个我得再琢磨琢磨是啥子问题。
2.由于使用的是pl端的axi_uart模块,所以如果要改串口的波特率等参数,不能只从操作系统应用代码中改波特率,那样是改不成功的,vivado中axi_uart模块参数必须与应用代码中设置的波特率一直才可以正常收发,这也是比较不方便的一点。