Linux下串口编程基础

来源:互联网 发布:php默认编码方式 编辑:程序博客网 时间:2024/06/06 20:30

串口知识
串行接口 (SerialInterface) 是指数据一位一位地顺序传送,其特点是通信线路简单,只要一对传输线就可以实现双向通信(可以直接利用电话线作为传输线),从而大大降低了成本,特别适用于远距离通信,但传送速度较慢。
1. 波特率
表示每秒传输的比特数,串口通信的双方必须保持一致才能通信数据位,若波特率为115200,它表示什么呢?
对于发送断,即每秒钟发送115200bit。
对于接收端,115200波特率意味着串口通信在数据线上的采样率为115200Hz.
2. 数据位
这是衡量通信中实际数据位的参数。当计算机发送一个信息包,实际的数据不会是8位的,标准的值是5、6、7和8位。如何设置取决于你想传送的信息。比如,标准的ASCII码是0~127(7位)。扩展的ASCII码是0~255(8位)。如果数据使用简单的文本(标准 ASCII码),那么每个数据包使用7位数据。每个包是指一个字节,包括开始/停止位,数据位和奇偶校验位。由于实际数据位取决于通信协议的选取,术语“包”指任何通信的情况。7位或8位数据中不仅仅是数据,还包括开始/停止位,数据位以及奇偶校验位等
3. 停止位
用于表示单个包的最后一位。典型的值为1,1.5和2位。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。
4. 奇偶校验
一般奇偶校验位的应用是由硬件完成的,软件只需要在初始化的时候对MCU的串口外设设置一下
奇偶校验的形式一般是在数据位后面跟一个奇偶校验位,如果数据位是8位的,那么加上校验位就是9位数据;如果数据位是7位的,那么加上校验位就是8位数据,校验位和数据之间没有任何意义上的关联,它不是数据的一部分,它只是关心数据中的“1”的个数是否为奇数(奇校验),或者偶数(偶校验)。奇校验,奇校验就是控制器在发送的一个字节或者一个数据帧里面含有的“1”的个数进行奇数个的修正调整,这里采用8位数据位+1位奇校验位的形式举个简单的例子,A发送数据0x35到B,后面紧跟一个奇校验位X, 0x35的二进制 = 0011 0101,可以看出8个数据位中一共有4个‘1’,那此时硬件会根据“1”的个数来设置X,这里会将奇校验位X调整为“1”,为什么呢? 因为数据位的“1”的个数是偶数,而我们用的是奇校验,所以为了达到有奇数个“1”的目的,调整奇偶校验位X为“1”,那么这9个位发出去就有奇数个“1”啦,当B接收的时候,B的硬件同样会对接收到“数据+X”中“1”的个数进行计数判断,如果是奇数个“1”,则认为数据接收正确,否则认为数据错误。而当A发送数据0x25( 0010 0101)到B的时候,则X应该就是“0”了,因为要保证发送出去的数据位+X位一共有奇数个“1”。
串口编程
串口编程的步骤:

a) 打开串口
这里是串口操作需要的一些头文件

#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>      /*错误号定义*/

Linux下一切皆文件,所以串口操作也是对文件进行操作的
Linux的串口文件位于 /dev下的

/dev/ttyS0     /* 串口0  *//dev/ttyS1          /* 串口1  */

这里我们通过标准的文件打开操作尝试打开串口 1
在这之前先建立一个 .c 文件 这里是 uatr2.c

#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>      /*错误号定义*/int main(){  int fd;  /*以读写方式打开串口*/  fd = open( "/dev/ttyS1", O_RDWR);  if (-1 == fd)  /* 不能打开串口一*/    perror(" 提示错误!");  else    printf("success\n");  close(fd);}

编译 .c 文件

gcc -o uart2.o uart2.c

运行

./uart2.o

这里输出success 说明串口能成功打开

调用open()函数来代开串口设备,对于串口的打开操作,必须使用O_NOCTTY参数。
O_NOCTTY:表示打开的是一个终端设备,程序不会成为该端口的控制终端。如果不使用此标志,任务一个输入(eg:键盘中止信号等)都将影响进程。
O_NDELAY:表示不关心DCD信号线所处的状态(端口的另一端是否激活或者停止)。

1、 串口波特率设置
串口的设置主要是设置 struct termios 结构体的各成员值。

struct termio{        unsigned short  c_iflag;                 /* 输入模式标志 */                unsigned short  c_oflag;                 /* 输出模式标志 */                unsigned short  c_cflag;                 /* 控制模式标志*/                unsigned short  c_lflag;                 /* local mode flags */                unsigned char  c_line;                 /* line discipline */                unsigned char  c_cc[NCC];    /* control characters */};

设置这个结构体很复杂,这里就只说说常见的一些设置:
波特率设置
下面是修改波特率的代码:

struct  termios Opt;tcgetattr(fd, &Opt);cfsetispeed(&Opt,B19200);     /*设置为19200Bps*/cfsetospeed(&Opt,B19200);tcsetattr(fd,TCANOW,&Opt);

设置波特率的例子函数:

/***@brief  设置串口通信速率*@param  fd     类型 int  打开串口的文件句柄*@param  speed  类型 int  串口速度*@return  void*/int speed_arr[] = { B38400, B19200, B9600, B4800, B2400, B1200, B300,          B38400, B19200, B9600, B4800, B2400, B1200, B300, };int name_arr[] = {38400,  19200,  9600,  4800,  2400,  1200,  300, 38400,            19200,  9600, 4800, 2400, 1200,  300, };void set_speed(int fd, int speed){  int   i;  int   status;  struct termios   Opt;  tcgetattr(fd, &Opt);  for ( i= 0;  i < sizeof(speed_arr) / sizeof(int);  i++) {    if  (speed == name_arr[i]) {           tcflush(fd, TCIOFLUSH);           cfsetispeed(&Opt, speed_arr[i]);        cfsetospeed(&Opt, speed_arr[i]);         status = tcsetattr(fd, TCSANOW, &Opt);        if  (status != 0) {                perror("tcsetattr fd1");          return;           }         tcflush(fd,TCIOFLUSH);       }    }}

3、校验位和停止位的设置
设置效验的函数:

/***@brief   设置串口数据位,停止位和效验位*@param  fd     类型  int  打开的串口文件句柄*@param  databits 类型  int 数据位   取值 为 7 或者8*@param  stopbits 类型  int 停止位   取值为 1 或者2*@param  parity  类型  int  效验类型 取值为N,E,O,,S*/int set_Parity(int fd,int databits,int stopbits,int parity){        struct termios options;        if  ( tcgetattr( fd,&options)  !=  0) {                perror("SetupSerial 1");                     return(FALSE);          }        options.c_cflag &= ~CSIZE;        switch (databits) /*设置数据位数*/        {           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;   /* Clear parity enable */                options.c_iflag &= ~INPCK;     /* Enable parity checking */                break;          case 'o':           case 'O':                     options.c_cflag |= (PARODD | PARENB); /* 设置为奇效验*/                  options.c_iflag |= INPCK;             /* Disnable parity checking */                break;          case 'e':          case 'E':                   options.c_cflag |= PARENB;     /* Enable parity */                   options.c_cflag &= ~PARODD;   /* 转换为偶效验*/                     options.c_iflag |= INPCK;       /* Disnable parity checking */                break;        case 'S':        case 's':  /*as no parity*/               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);}/* Set input parity option */if (parity != 'n')           options.c_iflag |= INPCK;tcflush(fd,TCIFLUSH);options.c_cc[VTIME] = 150; /* 设置超时15 seconds*/   options.c_cc[VMIN] = 0; /* Update the options and do it NOW */if (tcsetattr(fd,TCSANOW,&options) != 0)   {        perror("SetupSerial 3");           return (FALSE);  }return (TRUE);  }

4、 读写串口
设置好串口之后,读写串口就很容易了,把串口当作文件读写就是。
发送数据

char  buffer[1024];int    Length;int    nByte;nByte = write(fd, buffer ,Length);

读取串口数据
使用文件操作read函数读取,如果设置为原始模式(Raw Mode)传输数据,那么read函数返回的字符数是实际串口收到的字符数。
可以使用操作文件的函数来实现异步读取,如fcntl,或者select等来操作。

char  buff[1024];int    Len;int  readByte = read(fd,buff,Len);

5、 关闭串口
关闭串口就是关闭文件。
close(fd);

下面是串口1的一个简单的读取与发送的例子

#include     <stdio.h>      /*标准输入输出定义*/#include     <stdlib.h>     /*标准函数库定义*/#include     <unistd.h>     /*Unix标准函数定义*/#include     <sys/types.h>  /**/#include     <sys/types.h>  /**/#include     <sys/stat.h>   /**/#include     <fcntl.h>      /*文件控制定义*/#include     <termios.h>    /*PPSIX终端控制定义*/#include     <errno.h>      /*错误号定义*/#define FALSE 0#define TRUE 1/***@brief  设置串口通信速率*@param  fd     类型 int  打开串口的文件句柄*@param  speed  类型 int  串口速度*@return  void*/int speed_arr[] = { B38400, B19200, B9600, B4800, B2400, B1200, B300,            B38400, B19200, B9600, B4800, B2400, B1200, B300, };int name_arr[] = {38400,  19200,  9600,  4800,  2400,  1200,  300,            38400,  19200,  9600, 4800, 2400, 1200,  300, };void set_speed(int fd, int speed){  int   i;  int   status;  struct termios   Opt;  tcgetattr(fd, &Opt);  for ( i= 0;  i < sizeof(speed_arr) / sizeof(int);  i++)   {        if  (speed == name_arr[i])        {            tcflush(fd, TCIOFLUSH);        cfsetispeed(&Opt, speed_arr[i]);        cfsetospeed(&Opt, speed_arr[i]);        status = tcsetattr(fd, TCSANOW, &Opt);        if  (status != 0)            perror("tcsetattr fd1");        return;        }   tcflush(fd,TCIOFLUSH);   }}/**{int     fd = open( Dev, O_RDWR | O_NOCTTY | O_NDELAY);         //| O_NOCTTY | O_NDELAY        if (-1 == fd)                { /*设置数据位数*/                        perror("Can't Open Serial Port");                        return -1;                }        else        return fd;}/***@breif         main()*/int main(int argc, char **argv){        int fd , n;        int nread;        char buff[512];        char *dev ="/dev/ttyS1";        fd = OpenDev(dev);        if (fd>0)    set_speed(fd,19200);        else                {                printf("Can't Open Serial Port!\n");                exit(0);                }  if (set_Parity(fd,8,1,'N')== FALSE)  {    printf("Set Parity Error\n");    exit(1);  }  while(1)        {                while((nread = read(fd,buff,512))>0)                {                printf("\nLen %d\n",nread);                buff[nread+1]='\0';                printf("\n%s",buff);                n = write(fd, "I get\r", 4)        //如果收到数据则向串口发送I Getif (n < 0)                                fputs("write() of 4 bytes failed!\n", stderr);                }        }    //close(fd);    //exit(0);}
0 0