六 linux串口编程
一.结构体
1.termios
struct termios {tcflag_t c_iflag;/* 输入模式标志 */tcflag_t c_oflag;/* 输出模式标志 */tcflag_t c_cflag;/* 控制模式标志 */tcflag_t c_lflag;/* 本地模式标志 */cc_t c_line;/* 线路规程 */cc_t c_cc[NCCS];/* 控制字符 */};二.定义
1.类型定义
typedef unsigned charcc_t;typedef unsigned intspeed_t;typedef unsigned inttcflag_t;
2.c_cc
#define VINTR 0//中断 Ctrl-C#define VQUIT 1//退出 Ctrl-Z#define VERASE 2//擦除 空格BS#define VKILL 3//删行 Ctrl-U#define VEOF 4//文件结尾 Ctrl-D#define VTIME 5//等待时间#define VMIN 6//读取的最小字符数#define VSWTC 7//#define VSTART 8//软件流开始#define VSTOP 9//软件流停止#define VSUSP 10//#define VEOL 11//行结尾 回车CR#define VREPRINT 12//#define VDISCARD 13//#define VWERASE 14//#define VLNEXT 15//#define VEOL2 16//第二行结尾 进行LF
3.c_iflag
#define IGNBRK0000001//忽略任何中断条件#define BRKINT0000002//传送SIGINT,当检测到中断#define IGNPAR0000004//忽略校验错误#define PARMRK0000010//标记校验错误#define INPCK0000020//校验有效#define ISTRIP0000040//剥离校验位#define INLCR0000100//映射NL到CR#define IGNCR0000200//忽略CR#define ICRNL0000400//映射CR到NL#define IUCLC0001000//映射大写到小写#define IXON0002000//软件流控制输出作用#define IXANY0004000//允许任何字符再次启动流#define IXOFF0010000//软件流控制输入作用#define IMAXBEL0020000//在输入线上回显BEL过长#define IUTF80040000
4.c_oflag
#define OPOST0000001//输出后处理(不设置=行式输出)#define OLCUC0000002//映射小写到大写#define ONLCR0000004//映射NL到CR-NL#define OCRNL0000010//映射CR到NL#define ONOCR0000020//#define ONLRET0000040//NL实现CR功能#define OFILL0000100//对于延时用填充字符#define OFDEL0000200//填充字符DEL#define NLDLY0000400//在行之间,需要掩蔽延时#define NL00000000//NL没有延时#define NL10000400//在换行后,新的输出前延时100ms#define CRDLY0003000//回车到左边,需要掩蔽的延时#define CR00000000//CR没有延时#define CR10001000//CR后的延时,依赖于当前的列位置#define CR20002000//CR后延时100ms#define CR30003000//CR后延时150ms#define TABDLY0014000//TAB后需要延时#define TAB00000000//TAB后没有延时#define TAB10004000//TAB后的延时根据当前的列位置#define TAB20010000//TAB后延时100ms#define TAB30014000//TAB扩展为空格#define XTABS0014000//#define BSDLY0020000//BS后需要延时掩蔽#define BS00000000//BS后无延时#define BS10020000//BS后延时50ms#define VTDLY0040000//VT后需要延时掩蔽#define VT00000000//VT后无延时#define VT10040000//VT后延时2秒#define FFDLY0100000//FF后需要延时掩蔽#define FF00000000//FF后无延时#define FF10100000//FF后延时2秒
5.c_cflag
#define CBAUD0010017//波特率掩码#define B00000000//挂起#define B500000001//50 b/s#define B750000002//75 b/s#define B1100000003//110 b/s#define B1340000004//134.5 b/s#define B1500000005//150 b/s#define B2000000006//200 b/s#define B3000000007//300 b/s#define B6000000010//600 b/s#define B12000000011//1200 b/s#define B18000000012//1800 b/s#define B24000000013//2400 b/s#define B48000000014//4800 b/s#define B96000000015//9600 b/s#define B192000000016//19200 b/s#define B384000000017//38400 b/s#define EXTA B19200//外部时钟A#define EXTB B38400//外部时钟B#define CSIZE0000060//位数据位掩码#define CS50000000//5位数据位#define CS60000020//6位数据位#define CS70000040//7位数据位#define CS80000060//8位数据位#define CSTOPB0000100//2位停止位#define CREAD0000200//接收有效#define PARENB0000400//校验位有效#define PARODD0001000//奇偶校验#define HUPCL0002000//最后关闭后,挂起#define CLOCAL0004000//本地线-不 改变端口的"拥有者"#define CBAUDEX 0010000//#define BOTHER 0010000//#define B57600 0010001//57600 b/s#define B115200 0010002//115200 b/s#define B230400 0010003//230400 b/s#define B460800 0010004//460800 b/s#define B500000 0010005//500000 b/s#define B576000 0010006//576000 b/s#define B921600 0010007//921600 b/s#define B1000000 0010010//1000000 b/s#define B1152000 0010011//1152000 b/s#define B1500000 0010012//1500000 b/s#define B2000000 0010013//2000000 b/s#define B2500000 0010014//2500000 b/s#define B3000000 0010015//3000000 b/s#define B3500000 0010016//3500000 b/s#define B4000000 0010017//4000000 b/s#define CIBAUD002003600000//输入波特率#define CMSPAR 010000000000/* mark or space (stick) parity */#define CRTSCTS020000000000/* flow control */#define IBSHIFT 16
6.c_lflag
#define ISIG0000001//使SIGINTR,SIGSUSP,SIGDSUSP,SIGQUIT等信号作用#define ICANON0000002//设定规范canonical(或行式raw)#define XCASE0000004//map uppercase\lowercase(废除)#define ECHO0000010//回显输入字符#define ECHOE0000020//回显擦除字符 BS-SP-BS#define ECHOK0000040//在删除字符后,回显NL#define ECHONL0000100//回显NL#define NOFLSH0000200//不在中断后退出字符后刷新输入缓冲#define TOSTOP0000400//传送SIGTTOU信号作为背景#define ECHOCTL0001000//以^char方式回显控制字符和以~?方式显示删除字符#define ECHOPRT0002000//回显提示有删除字符#define ECHOKE0004000//BS-SP-BS整行,在有行删除时#define FLUSHO0010000//刷新输出#define PENDIN0040000//在下次读或输入时,将未决字符重打#define IEXTEN0100000#define EXTPROC0200000
7.其他
/* tcflow() and TCXONC use these */#defineTCOOFF0#defineTCOON1#defineTCIOFF2#defineTCION3/* tcflush() and TCFLSH use these */#defineTCIFLUSH0#defineTCOFLUSH1#defineTCIOFLUSH2/* tcsetattr 使用到这些参数*/#defineTCSANOW0//立即改变,不等待数据结束#defineTCSADRAIN1//等待直到所有的都传完#defineTCSAFLUSH2//刷新输入输出缓冲,然后改变
三.API函数
1.基本操作
打开串口 O_NOCTTY表示不是系统终端,这样就不会受键盘Ctrl-C等操作影响
fd = open("/dev/ttyXXX", O_RDWR | O_NOCTTY | O_NDELAY);写
write(unsigned int fd,const char __user * buf,size_t count)
读
fcntl(unsigned int fd,F_SETFL,0)
关闭串口
close(unsigned int fd)
2.其他
int tcgetattr(int fildes, struct termios *termios_p);//获取串口参数int tcsetattr(int fildes, int optional_actions,const struct termios *termios_p);//设置串口参数speed_t cfgetispeed(const struct termios *termios_p);//获取输入波特率int cfsetispeed(struct termios *termios_p, speed_t speed);//设置输入波特率speed_t cfgetospeed(const struct termios *termios_p);//获取输出波特率int cfsetospeed(struct termios *termios_p, speed_tspeed);//设置输出波特率int tcflow(int fildes, int action);//软件流控制int tcflush(int fildes, int queue_selector);//丢弃要写入引用的对象,但是尚未传输的数据,或者收到但是尚未读取的数据int tcdrain(int fildes);//等待直到所有写入int tcsendbreak(int fildes, int duration);//传送连续的0值比特流,持续一段时间
四.例子
网上找来个好的例子
/************************Copyright(c)******************************* ** 西安邮电学院 ** graduate school ** XNMS项目组 ** WebSite :blog.csdn.net/tigerjb **------------------------------------------FileInfo------------------------------------------------------- ** File name: main.c ** Last modified Date: 2011-01-31 ** Last Version: 1.0 ** Descriptions: **------------------------------------------------------------------ ** Created by: 冀博 ** Created date: 2011-01-31 ** Version: 1.0 ** Descriptions: The original version **------------------------------------------------------------------ ** Modified by: ** Modified date: ** Version: ** Descriptions: *******************************************************************/ //串口相关的头文件 #include<stdio.h> /*标准输入输出定义*/ #include<stdlib.h> /*标准函数库定义*/ #include<unistd.h> /*Unix 标准函数定义*/ #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> /*文件控制定义*/ #include<termios.h> /*PPSIX 终端控制定义*/ #include<errno.h> /*错误号定义*/ #include<string.h> //宏定义 #define FALSE -1 #define TRUE 0 /******************************************************************* * 名称: UART0_Open * 功能: 打开串口并返回串口设备文件描述 * 入口参数: fd :文件描述符 port :串口号(ttyS0,ttyS1,ttyS2) * 出口参数: 正确返回为1,错误返回为0 *******************************************************************/ int UART0_Open(int fd,char* port) { fd = open( port, O_RDWR|O_NOCTTY|O_NDELAY); if(FALSE == fd){ perror("Can't Open Serial Port"); return(FALSE); } //恢复串口为阻塞状态 if(fcntl(fd, F_SETFL, 0) < 0){ printf("fcntl failed!\n"); return(FALSE); } else{ printf("fcntl=%d\n",fcntl(fd, F_SETFL,0)); } //测试是否为终端设备 if(0 == isatty(STDIN_FILENO)){ printf("standard input is not a terminal device\n"); return(FALSE); } else{ printf("isatty success!\n"); } printf("fd->open=%d\n",fd); return fd; } /******************************************************************* * 名称: UART0_Close * 功能: 关闭串口并返回串口设备文件描述 * 入口参数: fd :文件描述符 port :串口号(ttyS0,ttyS1,ttyS2) * 出口参数: void *******************************************************************/ void UART0_Close(int fd) { close(fd); } /******************************************************************* * 名称: UART0_Set * 功能: 设置串口数据位,停止位和效验位 * 入口参数:fd 串口文件描述符 * speed 串口速度 * flow_ctrl 数据流控制 0不使用 1硬件流控制 2软件流控制* databits 数据位 取值为 7 或者8 * stopbits 停止位 取值为 1 或者2 * parity 效验类型 取值为N,E,O,,S *出口参数: 正确返回为1,错误返回为0 *******************************************************************/ int UART0_Set(int fd,int speed,int flow_ctrl,int databits,int stopbits,int parity) { int i; int status; int speed_arr[] = { B115200, B19200, B9600, B4800, B2400, B1200, B300}; int name_arr[] = {115200, 19200, 9600, 4800, 2400, 1200, 300}; struct termios options; /*tcgetattr(fd,&options)得到与fd指向对象的相关参数,并将其保存于options,该函数还可以测试配置是否正确,该串口是否可用等。若调用成功,函数返回值为0,若调用失败,函数返回值为1*/ if (tcgetattr( fd,&options)!= 0){ perror("SetupSerial 1"); return(FALSE); } //设置串口输入波特率和输出波特率 for ( i= 0; i < sizeof(speed_arr) / sizeof(int); i++){ if(speed == name_arr[i]){ cfsetispeed(&options, speed_arr[i]); cfsetospeed(&options, speed_arr[i]); } } //修改控制模式,保证程序不会占用串口 options.c_cflag |= CLOCAL; //修改控制模式,使得能够从串口中读取输入数据 options.c_cflag |= CREAD; //设置数据流控制 switch(flow_ctrl) { case 0://不使用流控制 options.c_cflag &= ~CRTSCTS; break; case 1://使用硬件流控制 options.c_cflag |= CRTSCTS; break; case 2://使用软件流控制 options.c_cflag |= IXON | IXOFF | IXANY; break; } //设置数据位 //屏蔽其他标志位 options.c_cflag &= ~CSIZE; switch (databits) { case 5: options.c_cflag |= CS5; break; case 6: options.c_cflag |= CS6; break; case 7: options.c_cflag |= CS7; break; case 8: options.c_cflag |= CS8; break; default: fprintf(stderr,"Unsupported data size\n"); return (FALSE); } //设置校验位 switch (parity) { case 'n': case 'N': //无奇偶校验位。 options.c_cflag &= ~PARENB; options.c_iflag &= ~INPCK; break; case 'o': case 'O'://设置为奇校验 options.c_cflag |= (PARODD | PARENB); options.c_iflag |= INPCK; break; case 'e': case 'E'://设置为偶校验 options.c_cflag |= PARENB; options.c_cflag &= ~PARODD; options.c_iflag |= INPCK; break; case 's': case 'S': //设置为空格 options.c_cflag &= ~PARENB; options.c_cflag &= ~CSTOPB; break; default: fprintf(stderr,"Unsupported parity\n"); return (FALSE); } // 设置停止位 switch (stopbits) { case 1: options.c_cflag &= ~CSTOPB; break; case 2: options.c_cflag |= CSTOPB; break; default: fprintf(stderr,"Unsupported stop bits\n"); return (FALSE); } //修改输出模式,原始数据输出 options.c_oflag &= ~OPOST; options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);//我加的 //options.c_lflag &= ~(ISIG | ICANON); //设置等待时间和最小接收字符 options.c_cc[VTIME] = 1; /* 读取一个字符等待1*(1/10)s */ options.c_cc[VMIN] = 1; /* 读取字符的最少个数为1 */ //如果发生数据溢出,接收数据,但是不再读取 刷新收到的数据但是不读 tcflush(fd,TCIFLUSH); //激活配置 (将修改后的termios数据设置到串口中) if (tcsetattr(fd,TCSANOW,&options) != 0){ perror("com set error!\n"); return (FALSE); } return (TRUE); } /******************************************************************* * 名称:UART0_Init() * 功能:串口初始化 * 入口参数:fd: 文件描述符 *speed : 串口速度 *flow_ctrl 数据流控制 *databits 数据位 取值为 7 或者8 *stopbits 停止位 取值为 1 或者2 *parity 效验类型 取值为N,E,O,,S * * 出口参数: 正确返回为1,错误返回为0 *******************************************************************/ int UART0_Init(int fd, int speed,int flow_ctrl,int databits,int stopbits,int parity) { int err; //设置串口数据帧格式 if (UART0_Set(fd,19200,0,8,1,'N') == FALSE){ return FALSE; } else{ return TRUE; } } /******************************************************************* * 名称:UART0_Recv * 功能:接收串口数据 * 入口参数:fd:文件描述符 *rcv_buf:接收串口中数据存入rcv_buf缓冲区中 *data_len:一帧数据的长度 * 出口参数:正确返回为1,错误返回为0 *******************************************************************/ int UART0_Recv(int fd, char *rcv_buf,int data_len) { int len,fs_sel; fd_set fs_read; struct timeval time; FD_ZERO(&fs_read); FD_SET(fd,&fs_read); time.tv_sec = 10; time.tv_usec = 0; //使用select实现串口的多路通信 fs_sel = select(fd+1,&fs_read,NULL,NULL,&time); if(fs_sel){ len = read(fd,rcv_buf,data_len); printf("I am right!(version1.2) len = %d fs_sel = %d\n",len,fs_sel); return len; } else{ printf("Sorry,I am wrong!"); return FALSE; } } /******************************************************************** * 名称:UART0_Send * 功能:发送数据 * 入口参数:fd:文件描述符 *send_buf :存放串口发送数据 *data_len :一帧数据的个数 * 出口参数:正确返回为1,错误返回为0 *******************************************************************/ int UART0_Send(int fd, char *send_buf,int data_len) { int len = 0; len = write(fd,send_buf,data_len); if (len == data_len ) { return len; } else { tcflush(fd,TCOFLUSH); return FALSE; } } int main(int argc, char **argv) { int fd;//文件描述符 int err;//返回调用函数的状态 int len; int i; char rcv_buf[100]; char send_buf[20]="tiger john"; if(argc != 3){ printf("Usage: %s /dev/ttySn 0(send data)/1 (receive data) \n",argv[0]); return FALSE; } fd = UART0_Open(fd,argv[1]); //打开串口,返回文件描述符 do{ err = UART0_Init(fd,19200,0,8,1,'N'); printf("Set Port Exactly!\n"); }while(FALSE == err || FALSE == fd); if(0 == strcmp(argv[2],"0")){ for(i = 0;i < 10;i++){ len = UART0_Send(fd,send_buf,10); if(len > 0) printf(" %d send data successful\n",i); else printf("send data failed!\n"); sleep(2); } UART0_Close(fd); } else{ while (1){ //循环读取数据 len = UART0_Recv(fd, rcv_buf,9); if(len > 0){ rcv_buf[len] = '\0'; printf("receive data is %s\n",rcv_buf); printf("len = %d\n",len); } else{ printf("cannot receive data\n"); } sleep(2); } UART0_Close(fd); } } /********************************************************************* End Of File *********************************************************************/
必须的表
