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. 系统镜像下的调试
-
image.ub加载起来之后, 看不到
/dev/ttyUL0
设备
需要添加字符驱动, 在linux镜像当中
-
开发版的串口和电脑对连接, 利用串口助手调试
在国产片子上面调试碰见如下问题:
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核文档查看, 比如以下是写寄存器的值和对应的操作
代码如下:
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; }
本文来自博客园踩坑狭,作者:韩若明瞳,转载请注明原文链接:https://www.cnblogs.com/han-guang-xue/p/17190415.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· AI 智能体引爆开源社区「GitHub 热点速览」
2021-03-08 Linux和windows 双网卡配置其中一个网卡访问固定的IP