祝各位道友念头通达
GitHub Gitee 语雀 打赏

petalinux的AXI_uart 调试

FMQL汇总文档中有示例代码, 可以先跑dome试试, 以下是自己没有跑dome, 自己踩的坑[😂]

1. 前期工作

  • 逻辑那块添加并设置AXI_uart IP核, 并设置好引脚约束
  • 生成对应的hdf文件,其中设备树:
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 = <&misc_clk_0>;
			compatible = "xlnx,axi-uartlite-2.0", "xlnx,xps-uartlite-1.00.a";
			current-speed = <19200>;
			device_type = "serial";
			interrupt-names = "interrupt";
			interrupt-parent = <&intc>;
			interrupts = <0 29 1>;
			port-number = <0>;
			reg = <0x42c00000 0x10000>;
			xlnx,baudrate = <0x4b00>;
			xlnx,data-bits = <0x8>;
			xlnx,odd-parity = <0x1>;
			xlnx,s-axi-aclk-freq-hz-d = "100.0";
			xlnx,use-parity = <0x1>;
		};
		...
  • 根据对应的设备树和bit文件制作镜像和uboot等

2. 系统镜像下的调试

  1. image.ub加载起来之后, 看不到 /dev/ttyUL0 设备
    需要添加字符驱动, 在linux镜像当中
    image

  2. 开发版的串口和电脑对连接, 利用串口助手调试

在国产片子上面调试碰见如下问题:
2.1 在petalinux 中使用 stty 设置对应的奇偶校验和波特率(波特率设置肯能没啥用)

  • 在对串口进行 cat /dev/ttyUL0 监听的时候,串口助手发送一个字节的数据, 回收到同样的一个字节的数据, 并且 cat 的设备没有输出;如果不对设备 cat,串口助手发送数据就不会接收到任何数据; 但是对串口 echo /dev/ttyUL0发数据,串口助手能却够收到

上述现象能够说明,串口硬件功能和逻辑端基本没问题, 然后从 软件层面找问题
回显问题是串口回显功能开启, 可以使用 stty -F /dev/ttyUL0 -echo 关闭串口回显
cat不到数据,echo向串口助手发送数据,串口助手能收到数据,是系统对设备的数据接收参数设置的不对,这块跟底层驱动对设备树解析有关系,暂时怀疑因为兼容性问题,系统在对设备加载的时候并没有将pl设备树中串口信息参数解析出来,进口的应该不会有这个问题,暂时没有搞明白国产的串口驱动怎么弄的,所以该解决方法就是利用 stty 设置对应的波特率,数据位、奇偶校验、停止位。

2.2 使用open和 read 函数操作

  • 在进口的片子上试是正常的, 在国产片子碰见问题如下
  • 使用open, read和write函数对串口读写,write出去的数据,串口助手能够收到,read不到串口助手你发过来的数据, 串口助手发送的数据回返回

上述现象和2.1 中的一样, 就算使用 stty 设置了也是一样, 所以在代码中,需要通过 struct termio 设置的才起作用
可以使用 tcgetattr(fd, &options); 获取默认配置, 如果默认配置对不上,回收不到数据

#include <termios.h>
int init_serial_device(int baud, int fd)
{
   int ret;
   struct termios options;
   int fd = open("/dev/ttyUL0", O_RDWR | O_NOCTTY);     /* 读写权限,非阻塞 */
   ret = tcgetattr(fd, &options);
   if (-1 == ret)
       return -1;


   options.c_cflag &= ~CSIZE;    //屏蔽其他标志
   options.c_cflag |= CS8;        //将数据位修改为8bit
   options.c_cflag |= (PARODD|PARENB);
   options.c_cflag &= ~CSTOPB; // 设置一位停止位;


   options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
   cfsetispeed(&options, baud);
   cfsetospeed(&options, baud);
   options.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
   options.c_cc[VTIME] = 10; // 超时时间 单位 100ms,open设置 O_NOCTTY,可以设置read一次的最大延时时间
   options.c_cc[VMIN] = 0;
   ret = tcsetattr(fd, TCSANOW, &options);
   if (-1 == ret)
       return -1;
   return fd;
}

2.3 直接操作串口寄存器地址实现收发,利用mmap,这个就不需要基于linux系统架构了,和裸跑的代码差不多

  • 需要注意的点是对寄存器如何操作,需要查看对应IP核的手册,这里使用AXI_uartlite,IP核
  • 还有ip核为 AXI UART 16550,也能够支持zynq器件
  • IP核文档查看, 比如以下是写寄存器的值和对应的操作
    image

代码如下:

    int fd = 0;
    if((fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1)
    ...
    MAP_uart_addr[0] = mmap(0, AXI_uart_Len, PROT_READ | PROT_WRITE, MAP_SHARED, fd,uart_addr[0]);
    if(MAP_uart_addr[0] == (void *) -1)
    {
    	printf("mmap error !\r\n");
        close(fd);
        return 0;
    }

存疑: pl到ps然后再到系统镜像, 系统如何将物理地址一步一步映射到内存的

struct termio 设置参数介绍

https://blog.csdn.net/vevenlcf/article/details/51096122

stty 指令参数

利用 stty -a -F /dev/ttyS0 查看串口所有属性值

特殊字符:
 * dsusp 字符   每当输入刷新时会发送一个用于终端阻塞信号的字符
   eof  字符    表示文件末尾而发送的字符(用于终止输入)
   eol  字符    为表示行尾而发送的字符
 * eol2 字符    为表示行尾而发送的另一个可选字符
   erase 字符   擦除前一个输入文字的字符
   intr 字符    用于发送中断信号的字符
   kill 字符    用于擦除当前终端行的字符
 * lnext 字符   用于输入下一个引用文字的字符
   quit 字符    用于发送退出信号的字符
 * rprnt 字符   用于重绘当前行的字符
   start 字符   在停止后重新开启输出的字符
   stop 字符    停止输出的字符
   susp 字符    发送终端阻断信号的字符
 * swtch 字符   在不同的shell 层次间切换的字符
 * werase 字符  擦除前一个输入的单词的字符
 
特殊设置:
   N            设置输入输出速度为N 波特
 * cols N       统治内核终端上有N 栏
 * columns N    等于cols N
   ispeed N     设置输入速度为N 波特
 * line N       设置行约束规则为N
   min N        和 -icanon 配合使用,设置每次一完整读入的最小字符数为<N>
   ospeed N     设置输出速度为N 波特
 * rows N       向内核通告此终端有N 行
 * size         根据内核信息输出当前终端的行数和列数
   speed        输出终端速度(单位为波特)
   time N       和-icanon 配合使用,设置读取超时为N 个十分之一秒
 
控制设置:
   [-]clocal    禁用调制解调器控制信号
   [-]cread     允许接收输入
 * [-]crtscts   启用RTS/CTS 握手
   csN          设置字符大小为N 位,N 的范围为5 到8
   [-]cstopb    每个字符使用2 位停止位 (要恢复成1 位配合"-"即可)
   [-]hup       当最后一个进程关闭标准终端后发送挂起信号
   [-]hupcl     等于[-]hup
   [-]parenb    对输出生成奇偶校验位并等待输入的奇偶校验位
   [-]parodd    设置校验位为奇数 (配合"-"则为偶数)
 
输入设置:
   [-]brkint    任务中断会触发中断信号
   [-]icrnl     将回车转换为换行符
   [-]ignbrk    忽略中断字符
   [-]igncr     忽略回车
   [-]ignpar    忽略含有奇偶不对称错误的字符
 * [-]imaxbel   发出终端响铃但不刷新字符的完整输入缓冲
   [-]inlcr     将换行符转换为回车
   [-]inpck     启用输入奇偶性校验
   [-]istrip    剥除输入字符的高8 位比特
 * [-]iutf8     假定输入字符都是UTF-8 编码
 * [-]iuclc     将大写字母转换为小写
 * [-]ixany     使得任何字符都会重启输出,不仅仅是起始字符
   [-]ixoff     启用开始/停止字符传送
   [-]ixon      启用XON/XOFF 流控制
   [-]parmrk    标记奇偶校验错误 (结合255-0 字符序列)
   [-]tandem    等于[-]ixoff
 
输出设置:
 * bsN          退格延迟的风格,N 的值为0 至1
 * crN          回车延迟的风格,N 的值为0 至3
 * ffN          换页延迟的风格,N 的值为0 至1
 * nlN          换行延迟的风格,N 的值为0 至1
 * [-]ocrnl     将回车转换为换行符
 * [-]ofdel     使用删除字符代替空字符作填充
 * [-]ofill     延迟时使用字符填充代替定时器同步
 * [-]olcuc     转换小写字母为大写
 * [-]onlcr     将换行符转换为回车
 * [-]onlret    使得换行符的行为表现和回车相同
 * [-]onocr     不在第一列输出回车
   [-]opost     后续进程输出
 * tabN         水平制表符延迟的风格,N 的值为0 至3
 * tabs         等于tab0
 * -tabs        等于tab3
 * vtN          垂直制表符延迟的风格,N 的值为0 至1
 
本地设置:
   [-]crterase  擦除字符回显为退格符
 * crtkill      依照echoprt 和echoe 的设置清除所有行
 * -crtkill     依照echoctl 和echol 的设置清除所有行
 * [-]ctlecho   在头字符中输出控制符号("^c")
   [-]echo      回显输入字符
 * [-]echoctl   等于[-]ctlecho
   [-]echoe     等于[-]crterase
   [-]echok     在每清除一个字符后输出一次换行
 * [-]echoke    等于[-]crtkill 意义相同
   [-]echonl    即使没有回显任何其它字符也输出换行
 * [-]echoprt   在"\"和"/"之间向后显示擦除的字符
   [-]icanon    启用erase、kill、werase 和rprnt 等特殊字符
   [-]iexten    允许POSIX 标准以外的特殊字符
   [-]isig      启用interrupt、quit和suspend 等特殊字符
   [-]noflsh    在interrupt 和 quit 特殊字符后禁止刷新
 * [-]prterase  等于[-]echoprt
 * [-]tostop    中止尝试向终端写入数据的后台任务
 * [-]xcase     和icanon 配合使用,用转义符"\"退出大写状态
 
综合设置:
 * [-]LCASE     等于[-]lcase
   cbreak       等于-icanon
   -cbreak      等于icanon
   cooked       等于brkint ignpar istrip icrnl ixon opost isig icanon eof eol 等的默认值
   -cooked      等于-raw
   crt          等于echoe echoctl echoke
   dec          等于echoe echoctl echoke -ixany intr ^c erase 0177 kill ^u
 * [-]decctlq   等于[-]ixany
   ek           清除所有字符,将它们回溯为默认值
   evenp        等于parenb -parodd cs7
   -evenp       等于-parenb cs8
 * [-]lcase     等于xcase iuclc olcuc
   litout       等于-parenb -istrip -opost cs8
   -litout      等于parenb istrip opost cs7
   nl           等于-icrnl -onlcr
   -nl          等于icrnl -inlcr -igncr onlcr -ocrnl -onlret
   oddp         等于parenb parodd cs7
   -oddp        等于-parenb cs8
   [-]parity    等于[-]evenp
   pass8        等于-parenb -istrip cs8
   -pass8       等于parenb istrip cs7
   raw          等于-ignbrk -brkint -ignpar -parmrk -inpck -istrip
                 -inlcr -igncr -icrnl -ixon -ixoff -iuclc -ixany
                 -imaxbel -opost -isig -icanon -xcase min 1 time 0
   -raw         等于cooked
   sane         等于cread -ignbrk brkint -inlcr -igncr icrnl -iutf8
                -ixoff -iuclc -ixany imaxbel opost -olcuc -ocrnl onlcr
                -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
                isig icanon iexten echo echoe echok -echonl -noflsh
                -xcase -tostop -echoprt echoctl echoke,
                所有特殊字符均使用默认值

直接操作寄存器的代码

/* Canonical definitions for peripheral AXI_UARTLITE_0 */
#define XPAR_UARTLITE_0_DEVICE_ID XPAR_AXI_UARTLITE_0_DEVICE_ID
#define XPAR_UARTLITE_0_BASEADDR 0x42c00000
#define XPAR_UARTLITE_0_HIGHADDR 0xA0001FFF
#define XPAR_UARTLITE_0_BAUDRATE 115200
#define XPAR_UARTLITE_0_USE_PARITY 0
#define XPAR_UARTLITE_0_ODD_PARITY 0
#define XPAR_UARTLITE_0_DATA_BITS 8

/* Canonical definitions for peripheral AXI_UARTLITE_1 */
#define XPAR_UARTLITE_1_DEVICE_ID XPAR_AXI_UARTLITE_1_DEVICE_ID
#define XPAR_UARTLITE_1_BASEADDR 0xA0002000
#define XPAR_UARTLITE_1_HIGHADDR 0xA0002FFF
#define XPAR_UARTLITE_1_BAUDRATE 115200
#define XPAR_UARTLITE_1_USE_PARITY 0
#define XPAR_UARTLITE_1_ODD_PARITY 0
#define XPAR_UARTLITE_1_DATA_BITS 8

/* UART Lite register offsets */
#define XUL_RX_FIFO_OFFSET		0	/* receive FIFO, read only */
#define XUL_TX_FIFO_OFFSET		4	/* transmit FIFO, write only */
#define XUL_STATUS_REG_OFFSET		8	/* status register, read only */
#define XUL_CONTROL_REG_OFFSET		12	/* control reg, write only */

/* Control Register bit positions */

#define XUL_CR_ENABLE_INTR		0x10	/* enable interrupt */
#define XUL_CR_FIFO_RX_RESET		0x02	/* reset receive FIFO */
#define XUL_CR_FIFO_TX_RESET		0x01	/* reset transmit FIFO */

/* Status Register bit positions */

#define XUL_SR_PARITY_ERROR		0x80
#define XUL_SR_FRAMING_ERROR		0x40
#define XUL_SR_OVERRUN_ERROR		0x20
#define XUL_SR_INTR_ENABLED		0x10	/* interrupt enabled */
#define XUL_SR_TX_FIFO_FULL		0x08	/* transmit FIFO full */
#define XUL_SR_TX_FIFO_EMPTY		0x04	/* transmit FIFO empty */
#define XUL_SR_RX_FIFO_FULL		0x02	/* receive FIFO full */
#define XUL_SR_RX_FIFO_VALID_DATA	0x01	/* data in receive FIFO */

/*
 * axi_uart.c
 *
 *  Created on: 2022��10��26��
 *      Author: limou
 */

#include "axi_uart.h"
#include <sys/mman.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


#define UART_NUM 6
#define AXI_uart_Len 4096

#define XUartLite_ReadReg(BaseAddress, RegOffset) \
		Xil_In32((BaseAddress) + (RegOffset))

#define XUartLite_WriteReg(BaseAddress, RegOffset, Data) \
		Xil_Out32((BaseAddress) + (RegOffset), (unsigned int)(Data))

unsigned int uart_addr[UART_NUM] = {XPAR_UARTLITE_0_BASEADDR,0,0,0,0,0};
void * MAP_uart_addr[UART_NUM] = {0,0,0,0,0,0};

void printHex1(unsigned char * rbuf, int len, int uint, char *name) {
    int cnt = 0;
    int index = 0;
    printf("\n%s", name);
    for(cnt = 0; cnt < len; cnt++) {
        if(cnt%uint == 0) {
            index++;
            printf("\n ==> %02d: ", index);
        }
        printf("%02x ", rbuf[cnt]);
    }
    printf("\n");
}

//write
static unsigned int Xil_Out32(unsigned long long Addr, unsigned int Value)
{
	volatile unsigned int *LocalAddr = (volatile unsigned int *)Addr;
	*LocalAddr = Value;
	return 0;
}

//read
static unsigned int Xil_In32(unsigned long long Addr)
{
	return *(volatile unsigned int *)Addr;
}


unsigned int Axi_Uart_Send(unsigned int num,unsigned char *SendBuf,unsigned int NumByte)
{

	unsigned int SendCount = 0;
	unsigned int StatusRegister;
	unsigned int IntrEnableStatus;
	unsigned int send_timeout_cnt = 0;

	//get status
	StatusRegister = XUartLite_ReadReg(MAP_uart_addr[num],XUL_STATUS_REG_OFFSET);

	XUartLite_WriteReg(MAP_uart_addr[num],XUL_CONTROL_REG_OFFSET, 0);

	IntrEnableStatus = StatusRegister;

	//send buffer
	while ((SendCount < NumByte))
	{
		if((StatusRegister & XUL_SR_TX_FIFO_FULL) == 0)
		{
			XUartLite_WriteReg(MAP_uart_addr[num],XUL_TX_FIFO_OFFSET,SendBuf[SendCount]);

			SendCount++;
		}
		else
		{
			send_timeout_cnt++;
			if(send_timeout_cnt>20000)
			{
				//				SendCount = NumByte;
				printf("SendCount=%d\n",SendCount);
				break;
			}
		}

		StatusRegister = XUartLite_ReadReg(MAP_uart_addr[num],XUL_STATUS_REG_OFFSET);
	}

	IntrEnableStatus &= XUL_CR_ENABLE_INTR;

	XUartLite_WriteReg(MAP_uart_addr[num], XUL_CONTROL_REG_OFFSET,IntrEnableStatus);

	return SendCount;
}

unsigned int Axi_Uart_Recv(unsigned int num,unsigned char* RecvBuf,unsigned int NumByte)
{
	unsigned int RecvCount = 0;
	unsigned int StatusRegister;
	unsigned int StatusRegisterVal;
	unsigned int recv_timeout_cnt = 0;

	StatusRegisterVal = XUartLite_ReadReg(MAP_uart_addr[num],XUL_STATUS_REG_OFFSET);

	XUartLite_WriteReg(MAP_uart_addr[num],XUL_CONTROL_REG_OFFSET, 0);

	while (RecvCount < NumByte)
	{

		StatusRegister = XUartLite_ReadReg(MAP_uart_addr[num],XUL_STATUS_REG_OFFSET);


		if (StatusRegister & XUL_SR_RX_FIFO_VALID_DATA)
		{
			RecvBuf[RecvCount++]=XUartLite_ReadReg(MAP_uart_addr[num],XUL_RX_FIFO_OFFSET);

		}
		else
		{
#if 1
			recv_timeout_cnt++;
			if(recv_timeout_cnt>20000)
			{
				//				SendCount = NumByte;

				if(RecvCount != 0) {
					printf("RecvCount=%d\n",RecvCount);
					printHex1(RecvBuf, RecvCount, RecvCount, "RecvBuf");
				}

				break;
			}
#endif		
			//#if 1
			//
			//			break;
			//
			//#endif	
		}
	}
	StatusRegisterVal &= XUL_CR_ENABLE_INTR;
	XUartLite_WriteReg(MAP_uart_addr[num],XUL_CONTROL_REG_OFFSET, StatusRegisterVal);

	return RecvCount;
}



void uart_init() 
{

	int fd = 0;
    if((fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1)
    {
    	printf("open error !\r\n");
        return 0;
    }
  /* Map one page */ //���ں˿ռ�ӳ�䵽�û��ռ�
    MAP_uart_addr[0] = mmap(0, AXI_uart_Len, PROT_READ | PROT_WRITE, MAP_SHARED, fd,uart_addr[0]);
    if(MAP_uart_addr[0] == (void *) -1)
    {
    	printf("mmap error !\r\n");
        close(fd);
        return 0;
    }

}

void uart_test()
{

	unsigned int err = 0;
	unsigned int len = 10;
	unsigned char sendbuff1[len];
	unsigned char sendbuff2[len];
	unsigned char recvbuff[len];
	unsigned int i =0;

	uart_init();

	for(i=0;i<len;i++)
	{
		sendbuff1[i] = i;
	}

	for(i=0;i<len;i++)
	{
		sendbuff2[i] = ~i;
	}
	// while(1)
	// {

	// 	Axi_Uart_Send(0,sendbuff1,len);
	// 	Axi_Uart_Recv(0,recvbuff,len);
	// 	for(i=0;i<len;i++)
	// 	{
	// 		if(recvbuff[i] != sendbuff1[i])
	// 		{
	// 			printf("check failed !\n");
	// 		}
	// 	}


	// }
	Axi_Uart_Send(0,sendbuff1,len);
	printHex1(sendbuff1, len, len, "SendBuf");
	while(1)
	Axi_Uart_Recv(0,recvbuff,len);
	// for(i=0;i<len;i++)
	// {
	// 	if(recvbuff[i] != sendbuff1[i])
	// 	{
	// 		printf("check failed !\n");
	// 	}
	// }

}


int main(int argc, char const *argv[])
{
	uart_test();
	return 0;
}
posted @ 2023-03-08 01:49  韩若明瞳  阅读(1123)  评论(0编辑  收藏  举报