在Linux下对设备的操作方法与对文件的操作方法是一样的,因此对串口的读写就可以使用简单的read()、write()函数来完成,所不同的是只是需要对串口的其他参数进行其他配置,本文实现的是宿主机实现写功能,目标机实现读功能,采用单工方式的串口通信,下面针对我个人的mini2440开发板简单介绍下串口应用开发的步骤。
笔者的操作系统 Ubuntu 10.10; 使用USB转串口:/dev/ttyUSB0; 开发板mini2440,开发板串口:/dev/ttySAC0 (电脑中串口名称不一定一样)
1.打开串口,这里使用函数open_port.c实现。
2.设置串口参数,这里使用函数set_com_config.c实现。
3.读写串口,宿主机上是com_writer.c;目标机上是com_reader_arm9.c
以下再分别展开介绍上面步骤的详细子步骤:
1.打开串口函数open_port.c实现步骤:
(1)利用open()函数打开:
1)针对宿主机的串口打开方法:
fd = open("/dev/ttyUSB0",O_RDWR | O_NOCTTY | O_NDELAY);
2)针对目标机的串口打开方法:
fd = open("/dev/ttySAC0",O_RDWR | O_NOCTTY | O_NDELAY);
(2)利用fcntl()函数恢复串口的为阻塞状态,用于等待串口数据的读入:
fcntl(fd, F_SETFL, 0)
(3)利用isatty()函数测试打开的文件描述符是否链接到一个终端设备,以进一步确认串口是否正确打开:
isatty(STDIN_FILENO);
(4)打开宿主机的串口程序open_port.c如下所示:
/*打开宿主机串口函数*/ #define MAX_COM_NUM 3 int open_port(int com_port) { int fd; //#if (COM_TYPE == GNR_COM) /*use general Serial port*/ // char *dev[] = {"/dev/ttyS0","/dev/ttyS1","/dev/ttyS2"}; //#else /*use USB turn Serial port*/ char *dev[] = {"/dev/ttyUSB0","/dev/ttyUSB1","/dev/ttyUSB2"}; //使用USB转串口。ps:用预处理没能成功选择,望指教?? //#endif if((com_port < 0) || (com_port > MAX_COM_NUM)) return -1; /*打开串口*/ fd = open(dev[com_port - 1], O_RDWR | O_NOCTTY | O_NDELAY); if(fd < 0) { perror("open serial port"); return -1; } /*恢复串口为阻塞状态*/ if(fcntl(fd, F_SETFL, 0) < 0) { perror("fcntl F_SETFL\n"); return -1; } /*测试是否为终端设备*/ if(isatty(STDIN_FILENO) == 0) { perror("standard input is not a terminal device"); return -1; } return fd; }
(5)打开目标机(mini2440开发板)串口程序open_port_arm9.c(不同的就只是在char *dev[]那里):
/*打开开发板串口程序open_port_arm9.c (不同的就只是在char *dev[]那里)*/ #define MAX_COM_NUM 3 int open_port(int com_port) { int fd; //#if (COM_TYPE == GNR_COM) /*use general Serial port*/ // char *dev[] = {"/dev/ttyS0","/dev/ttyS1","/dev/ttyS2"}; //#else /*use USB turn Serial port*/ // char *dev[] = {"/dev/ttyUSB0","/dev/ttyUSB1","/dev/ttyUSB2"}; char *dev[] = {"/dev/ttySAC0","/dev/SAC1","/dev/SAC2"}; //#endif if((com_port < 0) || (com_port > MAX_COM_NUM)) return -1; /*打开串口*/ fd = open(dev[com_port - 1], O_RDWR | O_NOCTTY | O_NDELAY); if(fd < 0) { perror("open serial port"); return -1; } /*恢复串口为阻塞状态*/ if(fcntl(fd, F_SETFL, 0) < 0) { perror("fcntl F_SETFL\n"); return -1; } /*测试是否为终端设备*/ if(isatty(STDIN_FILENO) == 0) { perror("standard input is not a terminal device"); return -1; } return fd; }
2.设置串口参数,这里使用函数set_com_config.c实现。
(1)串口的设置主要是设置struct termios结构体的各成员值,如下所示:
#include<termios.h> struct termios { unsigned short c_iflag; /*输入模式标志*/ unsigned short c_oflag; /*输出模式标志*/ unsigned short c_cflag; /*控制模式标志*/ unsigned short c_lflag; /*本地模式标志*/ unsigned char c_line; /*线路规程*/ unsigned char c_cc[NCC]; /*控制特性*/ speed_t c_ispeed; /*输入速度*/ speed_t c_ospeed; /*输出速度*/ };
(2)set_com_config.c程序如下:
int set_com_config(int fd, int baud_rate, \ int data_bits, char parity, int stop_bits) { struct termios new_cfg,old_cfg; int speed; /*步骤一:保存原先串口配置*/ if(tcgetattr(fd, &old_cfg) != 0) { perror("tcgetattr"); return -1; } new_cfg = old_cfg; /*步骤二:激活选项*/ cfmakeraw(&new_cfg); //config to raw mode /*步骤三:设置字符大小*/ new_cfg.c_cflag &= ~CSIZE; //set mask /*步骤四:设置波特率*/ switch(baud_rate) { case 2400: speed = B2400; break; case 4800: speed = B4800; break; case 9600: speed = B9600; break; case 19200: speed = B19200; break; case 38400: speed = B38400; break; default: case 115200: speed = B115200; break; } cfsetispeed(&new_cfg, speed); cfsetospeed(&new_cfg, speed); /*步骤五:设置数据位*/ switch(data_bits) { case 7: new_cfg.c_cflag |= CS7; break; default: case 8: new_cfg.c_cflag |= CS8; break; } /*步骤六:设置奇偶校验位*/ switch(parity) { default: case 'n': case 'N': new_cfg.c_cflag &= ~PARENB; new_cfg.c_cflag &= ~INPCK; break; case 'o': case 'O': new_cfg.c_cflag |= (PARODD | PARENB); new_cfg.c_cflag |= INPCK; break; case 'e': case 'E': new_cfg.c_cflag |= PARENB; new_cfg.c_cflag &= ~PARODD; new_cfg.c_cflag |= INPCK; break; case 's': //as no parity case 'S': new_cfg.c_cflag &= ~PARENB; new_cfg.c_cflag &= ~CSTOPB; break; }//end of 'switch parity' /*步骤七:设置停止位*/ switch (stop_bits) { default: case 1: new_cfg.c_cflag &= ~CSTOPB; break; case 2: new_cfg.c_cflag |= CSTOPB; break; }//end of 'switch stop_bits' /*步骤八:设置最少字符和等待时间*/ new_cfg.c_cc[VTIME] = 0; new_cfg.c_cc[VMIN] = 1; /*步骤九:处理未接收的字符*/ tcflush(fd,TCIFLUSH); /*步骤十:激活新配置*/ if((tcsetattr(fd, TCSANOW, &new_cfg)) != 0) { perror("tcsetattr"); return -1; } return 0; }
3.读写串口,宿主机上是com_writer.c;目标机上是com_reader_arm9.c
(1)宿主机的写程序com_writer.c程序如下:
/*com_writer.c*/ #include<stdio.h> #include<stdlib.h> #include<string.h> #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include<unistd.h> #include<errno.h> #include<termios.h> #include"open_port.c" #include"set_com_config.c" #define HOST_COM_PORT 1 /*使用1表示PC机的串口1*/ #define BUFFER_SIZE 30 /*最大缓存大小*/ int main(void) { int fd; char buff[BUFFER_SIZE]; if((fd = open_port(HOST_COM_PORT)) < 0) /*打开串口*/ { perror("open_port"); return 1; } if(set_com_config(fd, 115200, 8, 'N', 1) < 0) { perror("set_com_config"); return 1; } do { printf("Input some words(enter 'quit' to exit):"); memset(buff, 0, BUFFER_SIZE); if(fgets(buff, BUFFER_SIZE, stdin) == NULL) { perror("fgets"); break; } write(fd,buff,strlen(buff)); } while(strncmp(buff,"quit",4)); close(fd); return 0; }
(2)目标机读串口程序com_reader_arm9.c:
/*com_reader.c*/ #include<stdio.h> #include<stdlib.h> #include<string.h> #include<sys/types.h> #include<sys/stat.h> #include<errno.h> #include<unistd.h> #include<termios.h> #include<fcntl.h> #include"open_port_arm9.c" #include"set_com_config.c" #define BUFFER_SIZE 30 /*最大缓存区*/ #define TARGET_COM_PORT 1 /*用1来表示目标机上的串口1*/ int main(void) { int fd; char buff[BUFFER_SIZE]; if((fd = open_port(TARGET_COM_PORT)) < 0) /*打开串口*/ { perror("open_port"); return 1; } if(set_com_config(fd, 115200, 8, 'N', 1) < 0) /*配置串口*/ { perror("set_com_config"); return 1; } do { memset(buff, 0, BUFFER_SIZE); if(read(fd, buff, BUFFER_SIZE) > 0) printf("The received words are : %s", buff); } while(strncmp(buff, "quit", 4)); close(fd); return 0; }
最后,进行将com_writer.c用gcc编译: gcc -g com_writer.c -o com_writer
将com_reader_arm9.c用arm-linux-gcc交叉编译:arm-linux-gcc -g com_reader_arm9.c -o com_reader_arm9 并且将其NFS或者其他方法放在开发板上,然后打开。
将串口线直连,这里我是通过终端用telnet登陆到开发板,然后用nfs挂载文件到开发板,留下串口用来通信(之前用kermit利用串口相连通信收到数据的时候会有异常,且不能正常退出,可能是互相占用的问题),再打开com_reader_arm9程序。接着在宿主机上运行com_writer,再里面输入字符,开发板上面就会收到。如下图所示:
宿主机打开com_writer
目标机打开com_reader_arm9
最后,我想总结下我弄这个程序所遇到的一些问题,希望在我以后知识提高的时候可以解决掉或者各位现在提供些参考意见。
问题1:能不能只用一个open_port.c函数就直接选择USB转串口、普通PC串口、开发板串口的选择?之前按照书中的条件编译#if #else #endif没能实现,默认选择普通PC串口,所以源程序中我注释掉了。
问题2:以上两图中,对于com_writer写入的第一条语句时候com_reader_arm9并不能读出,只能从第二条开始。之后我试着把new_cfg.c_cc[VMIN] = 1改成"=0”后再进行编译连接,反倒在目标板上用com_reader_arm9不能读出任何来自宿主机的写信息了。
问题3:在第一次成功实现串口单工通信后,用quit退出后,想要再次实现串口通信确不行了,得要开发板重启后才能再次进行串口通信。
本文中的程序参考了书籍《嵌入式Linux应用程序开发标准教程(第2版)》
原创文章,欢迎转载,转载请注明:blog.csdn.net/jjzhoujun2010
作者:Dream Fly