Linux下串口编程基础

串口知识
串行接口 (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 Get
if (n < 0)
                                fputs("write() of 4 bytes failed!\n", stderr);

                }
        }
    //close(fd);
    //exit(0);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值