Linux 下串口编程入门 Android串口通信开发参考

串口简介

串行口是计算机一种常用的接口,具有连接线少,通讯简单,得到广泛的使用。常用的串口是 RS-232-C 接口(又称 EIA RS-232-C)它是在 1970 年由美国电子工业协会(EIA)联合贝尔系统、 调制解调器厂家及计算机终端生产厂家共同制定的用于串行通讯的标准。它的全名是"数据终端设备(DTE)和数据通讯设备(DCE)之间串行二进制数据交换接口技术标准"该标准规定采用一个 25 个脚的 DB25 连接器,对连接器的每个引脚的信号内容加以规定,还对各种信号的电平加以规定。传输距离在码元畸变小于 4% 的情况下,传输电缆长度应为 50 英尺。

Linux 操作系统从一开始就对串行口提供了很好的支持,本文就 Linux 下的串行口通讯编程进行简单的介绍,如果要非常深入了解,建议看看本文所参考的 《Serial Programming Guide for POSIX Operating Systems》

计算机串口的引脚说明

序号信号名称符号流向功能

2发送数据TXDDTE→DCEDTE发送串行数据

3接收数据RXDDTE←DCEDTE 接收串行数据

4请求发送RTSDTE→DCEDTE 请求 DCE 将线路切换到发送方式

5允许发送CTSDTE←DCEDCE 告诉 DTE 线路已接通可以发送数据

6数据设备准备好DSRDTE←DCEDCE 准备好

7信号地     信号公共地

8载波检测DCDDTE←DCE表示 DCE 接收到远程载波

20数据终端准备好DTRDTE→DCEDTE 准备好

22振铃指示RIDTE←DCE表示 DCE 与线路接通,出现振铃

串口操作

串口操作需要的头文件

#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 下串口文件是位于 /dev 下的

串口一 为 /dev/ttyS0

串口二 为 /dev/ttyS1

打开串口是通过使用标准的文件打开函数操作:

int fd;

/*以读写方式打开串口*/

fd = open( "/dev/ttyS0", O_RDWR);

if (-1 == fd){

/* 不能打开串口一*/

perror(" 提示错误!");

}

设置串口

最基本的设置串口包括波特率设置,效验位和停止位设置。

串口的设置主要是设置 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(fd1, TCSANOW, &Opt); 

      if  (status != 0) {       

        perror("tcsetattr fd1"); 

        return;    

      }   

      tcflush(fd,TCIOFLUSH);  

    } 

  }

}

效验位和停止位的设置:

无效验8位Option.c_cflag &= ~PARENB; 

Option.c_cflag &= ~CSTOPB; 

Option.c_cflag &= ~CSIZE; 

Option.c_cflag |= ~CS8;

奇效验(Odd)7位Option.c_cflag |= ~PARENB; 

Option.c_cflag &= ~PARODD; 

Option.c_cflag &= ~CSTOPB; 

Option.c_cflag &= ~CSIZE; 

Option.c_cflag |= ~CS7;

偶效验(Even)7位Option.c_cflag &= ~PARENB; 

Option.c_cflag |= ~PARODD; 

Option.c_cflag &= ~CSTOPB; 

Option.c_cflag &= ~CSIZE; 

Option.c_cflag |= ~CS7;

Space效验7位Option.c_cflag &= ~PARENB; 

Option.c_cflag &= ~CSTOPB; 

Option.c_cflag &= &~CSIZE; 

Option.c_cflag |= CS8;

设置效验的函数:

/**

*@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); 

}

需要注意的是:

如果不是开发终端之类的,只是串口传输数据,而不需要串口来处理,那么使用原始模式(Raw Mode)方式来通讯,设置方式如下:

options.c_lflag  &= ~(ICANON | ECHO | ECHOE | ISIG);  /*Input*/

options.c_oflag  &= ~OPOST;   /*Output*/

读写串口

设置好串口之后,读写串口就很容易了,把串口当作文件读写就是。

发送数据 1char  buffer[1024];int    Length;int    nByte;nByte = write(fd, buffer ,Length)

读取串口数据

使用文件操作read函数读取,如果设置为原始模式(Raw Mode)传输数据,那么read函数返回的字符数是实际串口收到的字符数。

可以使用操作文件的函数来实现异步读取,如fcntl,或者select等来操作。

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

关闭串口

关闭串口就是关闭文件。

1close(fd);

例子

下面是一个简单的读取串口数据的例子,使用了上面定义的一些函数和头文件

/**********************************************************************

代码说明:使用串口二测试的,发送的数据是字符,

但是没有发送字符串结束符号,所以接收到后,后面加上了结束符号。

我测试使用的是单片机发送数据到第二个串口,测试通过。

**********************************************************************/

#define FALSE  -1

#define TRUE   0

/*********************************************************************/

int OpenDev(char *Dev)

{

    int fd = open( Dev, O_RDWR );         //| O_NOCTTY | O_NDELAY  

    if (-1 == fd)  

    {          

        perror("Can't Open Serial Port");

        return -1;     

    }  

    else   

        return fd;

}

int main(int argc, char **argv){

    int fd;

    int nread;

    char buff[512];

    char *dev  = "/dev/ttyS1"; //串口二

    fd = OpenDev(dev);

    set_speed(fd,19200);

    if (set_Parity(fd,8,1,'N') == FALSE)  {

        printf("Set Parity Error\n");

        exit (0);

    }

while (1) //循环读取数据

{  

    while((nread = read(fd, buff, 512))>0)

    {

        printf("\nLen %d\n",nread);

        buff[nread+1] = '\0';  

        printf( "\n%s", buff);  

    }

}

    //close(fd); 

    // exit (0);

}

Serial Programming Guide for POSIX Operating Systems

Linux 的源代码

代码下载: 代码

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,100评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,862评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,993评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,309评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,303评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,421评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,830评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,501评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,689评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,506评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,564评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,286评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,826评论 3 305
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,875评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,114评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,705评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,269评论 2 341

推荐阅读更多精彩内容