在嵌入式项目中,UART接口的使用频率很高,多种模块(2G通信模组、蓝牙模块,等等)都会通过UART接口与主控MCU相连。本文将梳理UART接口调试流程,为调试工作提供参考,解决调试过程中可能遇到的问题,快速完成UART接口调试,实现模块功能。
首先查看原理图确认串口号,比如UART1,同时查看该串口引脚是否有复用功能,比如用作SD卡接口。如果有复用,需要在内核配置中取消复用功能的选项。然后选中串口功能,大致的路径为
Device Drivers > Character devices > Serial drivers
不同厂家提供的BSP有细微差别,具体路径视情况而定。在内核根目录输入make menuconfig打开内核配置界面,选中UART1。以nuc970 为例,配置如下:
$ make menuconfig
编译运行内核,如果UART1驱动加载成功会在/dev目录下产生相应UART设备节点。以高通mdm9x07为例:
系统启动之后在/dev下有两个ttyHSL设备节点:ttyHSL0、ttyHSL1。
$ cat /PRoc/tty/driver/msm_serial_hsl
输入该命令可以显示设备节点详细信息,其中通过地址(0x078B3000)与data sheet对比即可确认UART1对应的设备节点为/dev/ttyHSL1。 同时通过who命令可查看当前终端tty信息。
$ who root ttyHSL0 00:00 Jan 6 00:02:00
发现/dev/ttyHSL0为调试串口。 如果UART设备节点未产生,可在其相应驱动程序xx_probe函数中添加打印,查看xx_probe函数是否被调用,进一步查找原因。
如果成功产生了UART设备节点,可通过软件回环测试确认UART驱动程序功能是否正常。测试步骤:
进入loopback文件所在目录:
$ cd /sys/kernel/debug/msm_serial_hsl
将loopback.1值设置为1,打开UART1回环测试。
$ echo 1 > loopback.1
回环测试程序代码:
#include<stdio.h>#include<stdlib.h>#include<string.h>#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>#include<unistd.h>#include<termios.h>#include<string.h>int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop){ struct termios newtio,oldtio; if( tcgetattr( fd,&oldtio) != 0) { perror("tcgetattr error"); 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("set error"); return -1; } return 0;}int main(int argc,char *argv[]){ int fd,ret_set,ret_read,ret; char buf_read[100]; char tty[20]="/dev/"; if(4 == argc) { strcat(tty,argv[1]); fd = open(tty, O_RDWR); if(fd == -1) { printf("Open %s failed! Exit!/n",tty); exit(1); } printf("open %s successfully!/n",tty); ret_set = set_opt(fd, atoi(argv[2]), 8, 'N', 1); if (ret_set == -1) { printf("Set %s failed! Exit!/n",tty); exit(1); } printf("Set %s successfully!/n",tty); printf("Baud rate: %s/n",argv[2]); printf("Data: %s/n",argv[3]); while (1) { memset(buf_read, 0, sizeof(buf_read)); ret = write(fd, argv[3], 100); if( ret > 0){ printf("Write data: %s/n",argv[3]); }else{ printf("Write data failed! Exit!/n"); exit(1); } ret_read = read(fd, buf_read, 100); if(ret_read > 0){ printf("Read data: %s/n/n", buf_read); } sleep(3); } close(fd); }else{ printf("Usage: uart [tty node] [baud rate] [data]/n"); printf(" Sample: uart ttyHSL1 115200 test/n"); } return 0;}测试程序github地址:https://github.com/IOT-er/uart
交叉编译得到执行文件uart:
$ arm-linux-gcc -g uart.c -o uart
运行uart进行回环测试: 可以看到UART1成功收到了发送的 hello字符。
软件回环测试通过之后,将UART1的RX、TX管脚短接,关闭软件回环,使用uart程序进行自收发测试。 关闭软件回环:
$ echo 0 > loopback.1
此时有可能出现的问题是TX管脚无信号输出,确认硬件线路无误后最可能的原因是TX管脚被其他功能占用。 如果管脚信号测试通过,则串口功能基本调试成功。此方法的优点是无需上位机串口助手的配合,在串口模块到位之前提前完成接口调试工作。
新闻热点
疑难解答