Linux串口通信编程
2015-07-25 11:16
579 查看
转载自:http://blog.chinaunix.net/uid-11582448-id-745506.html
串口是计算机上一种非常通用设备通信的协议,常用PC机上包含的是RS232规格的串口,具有连接线少,通讯简单,得到广泛的使用。
串口是计算机上一种非常通用设备通信的协议,常用PC机上包含的是RS232规格的串口,具有连接线少,通讯简单,得到广泛的使用。
Linux对所有设备的访问是通过设备文件来进行的,串口也是这样,为了访问串口,只需打开其设备文件即可操作串口设备。在linux系统下面,每一个串口设备都有设备文件与其关联,设备文件位于系统的/dev目录下面。如linux下的/ttyS0,/ttyS1分别表示的是串口1和串口2。
在串口编程中,比较重要的是串口的设置,我们要设置的部分包括:波特率,数据位,停止位,奇偶校验位;要注意的是,每台机器的串口默认设置可能是不同的,如果你没设置这些,仅仅按照默认设置进行发送数据,很可能出现n多异想不到而又查不出来的情况。
1) 设置波特率
#include <termios.h> #include <unistd.h> int cfsetispeed(struct termios *termios_p, speed_t speed); int cfsetospeed(struct termios *termios_p, speed_t speed); |
2) 设置属性:奇偶校验位、数据位、停止位。主要设置中的termios结构体即可:
#define NCCS 19 struct termios { tcflag_t c_iflag; /* input mode flags */ tcflag_t c_oflag; /* output mode flags */ tcflag_t c_cflag; /* control mode flags */ tcflag_t c_lflag; /* local mode flags */ cc_t c_line; /* line discipline */ cc_t c_cc[NCCS]; /* control characters */ }; |
有相应的函数供获取和设置属性:
int tcgetattr(int fd, struct termios *termios_p); int tcsetattr(int fd, int optional_actions, struct termios *termios_p); |
3) 打开、关闭和读写串口。串口作为设备文件,可以直接用文件描述符来进行操作。
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int open(const char *pathname, int flags); #include <unistd.h> int close(int fd); ssize_t write(int fd, const void *buf, size_t count); ssize_t read(int fd, void *buf, size_t count); |
网上的一个例子:
/*串口设备无论是在工控领域,还是在嵌入式设备领域,应用都非常广泛。而串口编程也就显得必不可少。 偶然的一次机会,需要使用串口,而且操作系统还要求是Linux,因此,趁着这次机会,综合别人的代码, 进行了一次整理和封装。具体的封装格式为C代码,这样做是为了很好的移植性,使它可以在C和C++环境下, 都可以编译和使用。代码的头文件如下: */ /////////////////////////////////////////////////////////////////////////////// //filename:stty.h #ifndef __STTY_H__ #define __STTY_H__ //包含头文件 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <termios.h> #include <errno.h> #include <pthread.h> // // 串口设备信息结构 typedef struct tty_info_t { int fd; // 串口设备ID pthread_mutex_t mt; // 线程同步互斥对象 char name[24]; // 串口设备名称,例:"/dev/ttyS0" struct termios ntm; // 新的串口设备选项 struct termios otm; // 旧的串口设备选项 } TTY_INFO; // // 串口操作函数 TTY_INFO *readyTTY(int id); int setTTYSpeed(TTY_INFO *ptty, int speed); int setTTYParity(TTY_INFO *ptty,int databits,int parity,int stopbits); int cleanTTY(TTY_INFO *ptty); int sendnTTY(TTY_INFO *ptty,char *pbuf,int size); int recvnTTY(TTY_INFO *ptty,char *pbuf,int size); int lockTTY(TTY_INFO *ptty); int unlockTTY(TTY_INFO *ptty); #endif /*从头文件中的函数定义不难看出,函数的功能,使用过程如下: (1) 打开串口设备,调用函数setTTYSpeed(); (2) 设置串口读写的波特率,调用函数setTTYSpeed(); (3) 设置串口的属性,包括停止位、校验位、数据位等,调用函数setTTYParity(); (4) 向串口写入数据,调用函数sendnTTY(); (5) 从串口读出数据,调用函数recvnTTY(); (6) 操作完成后,需要调用函数cleanTTY()来释放申请的串口信息接口; 其中,lockTTY()和unlockTTY()是为了能够在多线程中使用。在读写操作的前后,需要锁定和释放串口资源。 具体的使用方法,在代码实现的原文件中,main()函数中进行了演示。下面就是源代码文件: */ //////////////////////////////////////////////////////////////////////////////// //stty.c #include <stdio.h> #include <sys/ioctl.h> #include "stty.h" /////////////////////////////////////////////////////////////////////////////// // 初始化串口设备并进行原有设置的保存 TTY_INFO *readyTTY(int id) { TTY_INFO *ptty; ptty = (TTY_INFO *)malloc(sizeof(TTY_INFO)); if(ptty == NULL) return NULL; memset(ptty,0,sizeof(TTY_INFO)); pthread_mutex_init(&ptty->mt,NULL); sprintf(ptty->name,"/dev/ttyS%d",id); // // 打开并且设置串口 ptty->fd = open(ptty->name, O_RDWR | O_NOCTTY |O_NDELAY); if (ptty->fd <0) { free(ptty); return NULL; } // // 取得并且保存原来的设置 tcgetattr(ptty->fd,&ptty->otm); return ptty; } /////////////////////////////////////////////////////////////////////////////// // 清理串口设备资源 int cleanTTY(TTY_INFO *ptty) { // // 关闭打开的串口设备 if(ptty->fd>0) { tcsetattr(ptty->fd,TCSANOW,&ptty->otm); close(ptty->fd); ptty->fd = -1; free(ptty); ptty = NULL; } return 0; } /////////////////////////////////////////////////////////////////////////////// // 设置串口通信速率 // ptty 参数类型(TTY_INFO *),已经初始化的串口设备信息结构指针 // speed 参数类型(int),用来设置串口的波特率 // return 返回值类型(int),函数执行成功返回零值,否则返回大于零的值 /////////////////////////////////////////////////////////////////////////////// int setTTYSpeed(TTY_INFO *ptty, int speed) { int i; // // 进行新的串口设置,数据位为8位 bzero(&ptty->ntm, sizeof(ptty->ntm)); tcgetattr(ptty->fd,&ptty->ntm); ptty->ntm.c_cflag = /*CS8 |*/ CLOCAL | CREAD; switch(speed) { case 300: ptty->ntm.c_cflag |= B300; break; case 1200: ptty->ntm.c_cflag |= B1200; break; case 2400: ptty->ntm.c_cflag |= B2400; break; case 4800: ptty->ntm.c_cflag |= B4800; break; case 9600: ptty->ntm.c_cflag |= B9600; break; case 19200: ptty->ntm.c_cflag |= B19200; break; case 38400: ptty->ntm.c_cflag |= B38400; break; case 115200: ptty->ntm.c_cflag |= B115200; break; } ptty->ntm.c_iflag = IGNPAR; ptty->ntm.c_oflag = 0; // // tcflush(ptty->fd, TCIFLUSH); tcsetattr(ptty->fd,TCSANOW,&ptty->ntm); // // return 0; } // 设置串口数据位,停止位和效验位 // ptty 参数类型(TTY_INFO *),已经初始化的串口设备信息结构指针 // databits 参数类型(int), 数据位,取值为7或者8 // stopbits 参数类型(int),停止位,取值为1或者2 // parity 参数类型(int),效验类型 取值为N,E,O,,S // return 返回值类型(int),函数执行成功返回零值,否则返回大于零的值 /////////////////////////////////////////////////////////////////////////////// int setTTYParity(TTY_INFO *ptty,int databits,int parity,int stopbits) { // // 取得串口设置 if( tcgetattr(ptty->fd,&ptty->ntm) != 0) { printf("SetupSerial [%s]\n",ptty->name); return 1; } bzero(&ptty->ntm, sizeof(ptty->ntm)); ptty->ntm.c_cflag = CS8 | CLOCAL | CREAD; ptty->ntm.c_iflag = IGNPAR; ptty->ntm.c_oflag = 0; // // 设置串口的各种参数 ptty->ntm.c_cflag &= ~CSIZE; switch (databits) { //设置数据位数 case 7: ptty->ntm.c_cflag |= CS7; break; case 8: ptty->ntm.c_cflag |= CS8; break; default: printf("Unsupported data size\n"); return 5; } // // switch (parity) { // 设置奇偶校验位数 case n: case N: ptty->ntm.c_cflag &= ~PARENB; /* Clear parity enable */ ptty->ntm.c_iflag &= ~INPCK; /* Enable parity checking */ break; case o: case O: ptty->ntm.c_cflag |= (PARODD|PARENB); /* 设置为奇效验*/ ptty->ntm.c_iflag |= INPCK; /* Disnable parity checking */ break; case e: case E: ptty->ntm.c_cflag |= PARENB; /* Enable parity */ ptty->ntm.c_cflag &= ~PARODD; /* 转换为偶效验*/ ptty->ntm.c_iflag |= INPCK; /* Disnable parity checking */ break; case S: case s: /*as no parity*/ ptty->ntm.c_cflag &= ~PARENB; ptty->ntm.c_cflag &= ~CSTOPB; break; default: printf("Unsupported parity\n"); return 2; } // // 设置停止位 switch (stopbits) { case 1: ptty->ntm.c_cflag &= ~CSTOPB; break; case 2: ptty->ntm.c_cflag |= CSTOPB; break; default: printf("Unsupported stop bits\n"); return 3; } // // ptty->ntm.c_lflag = 0; ptty->ntm.c_cc[VTIME] = 0; // inter-character timer unused ptty->ntm.c_cc[VMIN] = 1; // blocking read until 1 chars received tcflush(ptty->fd, TCIFLUSH); if (tcsetattr(ptty->fd,TCSANOW,&ptty->ntm) != 0) { printf("SetupSerial \n"); return 4; } return 0; } int recvnTTY(TTY_INFO *ptty,char *pbuf,int size) { int ret,left,bytes; left = size; while(left>0) { ret = 0; bytes = 0; pthread_mutex_lock(&ptty->mt); ioctl(ptty->fd, FIONREAD, &bytes); if(bytes>0) { ret = read(ptty->fd,pbuf,left); } pthread_mutex_unlock(&ptty->mt); if(ret >0) { left -= ret; pbuf += ret; } usleep(100); } return size - left; } int sendnTTY(TTY_INFO *ptty,char *pbuf,int size) { int ret,nleft; char *ptmp; ret = 0; nleft = size; ptmp = pbuf; while(nleft>0) { pthread_mutex_lock(&ptty->mt); ret = write(ptty->fd,ptmp,nleft); pthread_mutex_unlock(&ptty->mt); if(ret >0) { nleft -= ret; ptmp += ret; } //usleep(100); } return size - nleft; } int lockTTY(TTY_INFO *ptty) { if(ptty->fd < 0) { return 1; } return flock(ptty->fd,LOCK_EX); } int unlockTTY(TTY_INFO *ptty) { if(ptty->fd < 0) { return 1; } return flock(ptty->fd,LOCK_UN); } #ifdef LEAF_TTY_TEST /////////////////////////////////////////////////////////////////////////////// // 接口测试 int main(int argc,char **argv) { TTY_INFO *ptty; int nbyte,idx; unsigned char cc[16]; ptty = readyTTY(0); if(ptty == NULL) { printf("readyTTY(0) error\n"); return 1; } // // lockTTY(ptty); if(setTTYSpeed(ptty,9600)>0) { printf("setTTYSpeed() error\n"); return -1; } if(setTTYParity(ptty,8,N,1)>0) { printf("setTTYParity() error\n"); return -1; } // idx = 0; while(1) { cc[0] = 0xFA; sendnTTY(ptty,&cc[0],1); nbyte = recvnTTY(ptty,cc,1); printf("%d:%02X\n",idx++,cc[0]); } cleanTTY(ptty); } #endif |
参考: http://hi.baidu.com/szimshan/blog/item/103f34dcb7c4b6a6cd1166aa.html http://blog.chinaunix.net/u/19671/showart_194522.html
相关文章推荐
- Linux多线程编程(不限Linux)
- 使用rsync同步Linux数据到Windows
- CentOS 6.x搭建Open***实现双IDC互联
- centos7 安装R和Rstudio客户端
- Linux之GTK系列教程
- 大概CentOS7 安装LAMP的步骤
- linux c 获取当前运行进程总数
- Linux中的BSS段、数据段、堆栈段和代码段
- linux下ok6410驱动开发环境搭建基本教程
- Linux下的JDK安装与卸载
- 如何判断Linux的tar.gz是二进制文件还是源码文件
- linux和window下mkdir函数
- Linux——tar包安装
- linux mysql 操作命令
- DHCP情况下,Linux如何查看当前网关
- linux下为分区设置卷标
- Linux下关闭开启防火墙
- 关闭Linux的ICMP
- CentOS下的软件包管理--yum篇
- linux 命令总结