一、串口简介

rs232是三芯通信,即DB9的第2引脚RXD(接收)、第3引脚TXD(发送数据)、第5引脚DG(信号地),rs232是三芯通信,485是两芯通讯的,RS-232串口线 通常 是 DB9--DB9 的 串口通信线,9芯RS-485数据线 是 双绞线或者屏蔽双绞线,232传输距离较近,485传输距离比较远,485是单工(向)通讯,232是双工(向)的。

串口编程都没有区别,都是按照RS232编程的,计算机没有485接口,需要用一个232转485的转换器就可以了。

-------------------------------------------------------

二、串口操作

在linux下编写终端程序时,有规范模式,非规范模式(原始模式特殊的非规范模式)之分;如不用于终端,而是用在像串口这种情况下,一般设置其为原始模式。

1、操作步骤

1、打开串口

2、配置串口:对串口的 波特率、数据位、停止位、校验码、等进行设置。

3、读写串口

4、关闭串口

2、读写原理 

串口的读写像正常读取其他文件一样使用read()、write()   进行读写即可。但是我在用read()函数从串口接收指定的数量的字符时,发现往往接收到的实际字符数,都与指定的不同。

深层原因:

一般而言,串口的读写模式有直接模式和缓存模式之分。

直接模式:串口的读写都是单字节的,也就是说read/write每次只能操作一个字节;

缓存模式:顾名思义,就是先讲收/发的字节缓存到串口内存中然后再操作,绝大部份串口芯片都支持缓存模式,缓存模式一般同时支持中断聚合和超时机制,也就是说在有数据时,当缓存满或者超时时间到时,都会触发读/写中断操作。

例如,本人在Ubuntu14.04下用一个usb转232来充当串口测试读写时发现一次可以read的字节数总不是我期望的字节数,也进一步证明了当我们读取串口的时候是读取的缓存,而每次读取的时候缓存收到的字节数是不固定的。

总之,通讯过程中无法保证一次发送的数据肯定是一次接收的,所以必须写代码来一次一次的接收,直到接收满足预定的为止,在此过程中可使用select/poll来避免超时接收。

-------------------------------------------------------

三、串口编程

1.关于阻塞和非阻塞模式

简单的串口编程,一般设置成阻塞模式,便可以了。但是在大多数应用场合,把串口设置成阻塞模式是很不实用的,如read()时,如果没有数据发来,这程序一直会阻塞在这里(除非用多线程)。

因此一般把其设置为非阻塞模式。一般是需要用串口读取指定长度的数据,但是read函数实际读取的数据长度,往往会与指定的不同,所以必须自己编写一个读写N字节数据的函数:很快想到用个循环,但是循环中必须有:即使一直没有收到指定长度的数据但在一定时间后也必须跳出循环”的机制,否则就与阻塞模式的没有区别了(也就是让函数一直等,等到指定长度数据接收为止)。

而非阻塞I/O使我们的操作要么成功,要么立即返回错误,不被阻塞。

对于一个给定的描述符两种方法对其指定非阻塞I/O:

(1)调用open获得描述符,并指定O_NONBLOCK标志

(2)对已经打开的文件描述符,调用fcntl,打开O_NONBLOCK文件状态标志。

[C/C++ code]

int flags,s为描述符

flags = fcntl( s, F_GETFL, 0 ) )

fcntl( s, F_SETFL, flags | O_NONBLOCK )

-------------------------------------------------------

2.关键代码解析

最基本的串口设置包括波特率、数据位、校验位和停止位设置,且串口设置主要使用termios.h头文件中定义的termios结构,如下:

struct termios

{

tcflag_t  c_iflag;   //输入模式标志

tcflag_t  c_oflag;  //输出模式标志

tcflag_t  c_cflag;  //控制模式标志

tcflag_t  c_lflag;   //本地模式标志

cc_t   c_line;        //line discipline

cc_t   c_cc[NCC]; //control characters

}

1)打开串口

int uart_open(int fd, char* port)
{// 1.以读写方式打开串口fd = open(port, O_RDWR|O_NOCTTY|O_NDELAY);         if (-1 == fd) goto ERR_LABLE_OPEN;// 2.恢复串口的阻塞状态if(fcntl(fd, F_SETFL, 0) < 0) goto ERR_LABLE_OPEN;  //非阻塞:FNDELAY// 3.测试是否为终端设备if(0 == isatty(STDIN_FILENO)) goto ERR_LABLE_OPEN;return fd;
ERR_LABLE_OPEN:perror("uart_open");return -1;
}

说明:

O_RDWR   :指定串口为读写模式。

O_NOCTTY:告诉Unix这个程序不想成为“控制终端”控制的程序,不说明这个标志的话,任何输入都会影响你的程序。

O_NDELAY :告诉Unix这个程序不关心DCD信号线状态,即其他端口是否运行,不说明这个标志的话,该程序就会在DCD信号线为低电平时停止

2)设置串口

int uart_set(int fd,int speed,int databits,int stopbits,int parity,int flow_ctrl)
{int i=0;int speed_val[] = {115200, 38400, 19200, 9600, 4800, 2400, 1200, 300};int speed_arr[] = {B115200, B38400, B19200, B9600, B4800, B2400, B1200, B300};struct termios options;// 1.获取串口属性。/*tcgetattr(fd,&options)得到与fd指向对象的相关参数,并存于options中,该函数还可测试串口配置是否正确,是否可用等。返回值 成功 0 失败-1.*/if ( 0!=tcgetattr( fd,&options) ) goto ERR_LABLE_SET;// 2.设置串口I/O波特率for (i= 0; i < sizeof(speed_arr) / sizeof(int); i++){if (speed == speed_val[i]){cfsetispeed(&options, speed_arr[i]);cfsetospeed(&options, speed_arr[i]);}}// 3.修改控制模式,保证终端程序不会占用串口options.c_cflag |= CLOCAL;// 4.修改控制模式,保证能从串口读取输入数据options.c_cflag |= CREAD;// 5.设置数据流控制(适用于收发波特率不同时)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;default:fprintf(stderr,"Unsupported ctrl_flow type!\n");goto ERR_LABLE_SET;}// 6.设置有效数据位数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");goto ERR_LABLE_SET;}// 7.设置校验位switch (parity){case 'n':case 'N': //无奇偶校验位options.c_cflag &= ~PARENB; //清除校验使能options.c_iflag &= ~INPCK;  //不使能奇偶校验检测break;case 'o':case 'O': //设置为奇校验options.c_cflag |= PARENB;options.c_cflag |= PARODD;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");goto ERR_LABLE_SET;}// 8.设置停止位switch (stopbits){case 1:options.c_cflag &= ~CSTOPB; break;case 2:options.c_cflag |=  CSTOPB; break;default:fprintf(stderr,"Unsupported stop bits\n");goto ERR_LABLE_SET;}// 9.修改输出模式,原始数据通讯(raw)options.c_oflag &= ~OPOST;options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);// 10.防止串口把回车和换行当成同一个字符options.c_iflag &= ~(INLCR | ICRNL | IGNCR);options.c_oflag &= ~(ONLCR | OCRNL);// 12.如果在传输XON和XOFF字符时传不过去,需把软件流控制屏蔽options.c_iflag &= ~(IXON | IXOFF | IXANY);// 13.设置等待时间和最小接收字符options.c_cc[VTIME] = 0;    /* 读取一个字符等待1*(1/10)s */options.c_cc[VMIN]  = 1;    /* 满足读取功能的最低字元接收个数*/// 14.如果发生数据溢出,则刷新收到的数据但是不读tcflush(fd,TCIFLUSH);// 15.激活配置 (将修改后的termios数据设置到串口配置中)if (tcsetattr(fd,TCSANOW,&options) != 0) goto ERR_LABLE_SET;return 0;ERR_LABLE_SET:perror("uart_set");return -1;
}

说明:

a.在第四步中我们看到一些比较特殊的设置,下面简述一下他们的作用。

c_cc数组的VSTART和VSTOP元素被设定成DC1和DC3,代表ASCII标准的XON和XOFF字符,如果在传输这两个字符的时候就传不过去,需要把软件流控制屏蔽,即:

options.c_iflag &= ~(IXON | IXOFF | IXANY);

b.有时候,在用write发送数据时没有键入回车,信息就发送不出去,这主要是因为我们在输入输出时是按照规范模式接收到回车或换行才发送,而更多情况下我们是不必键入回车或换行的。此时应转换到行方式输入,不经处理直接发送,设置如下:

options.c_oflag &= ~OPOST;

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

c.还存在这样的情况:发送字符0X0d的时候,往往接收端得到的字符是0X0a,原因是因为在串口设置中c_iflag和c_oflag中存在从NL-CR和CR-NL的映射,即串口能把回车和换行当成同一个字符,可以进行如下设置屏蔽之:

options.c_iflag &= ~(INLCR | ICRNL | IGNCR);

options.c_oflag &= ~(ONLCR | OCRNL);

d、linux 下串口编程VTIME和VMIN的设置

1、只有设置为阻塞时这两个参数才有效,且仅针对于读操作。

VTIME 定义要求等待的时间量(取值不能大于cc_t<unsigned char>))。

VMIN定义了要求等待的最小字节数, 这个字节数可以是0(立即返回)。

options.c_cc[VTIME]  = X;   //设置从获取到Y个字节后开始计时的超时返回时间

options.c_cc[VMIN]    = Y;   //设置要求等待的最小字节数

在原始模式下对read()函数的影响:

1、X  = 0; Y != 0    read()只在读取了Y个字节的数据或者收到一个终止信号时才返回;

2、X != 0; Y  = 0    即使没有数据可以读取,read()等待X时间量(X*0.1s)后返回;

3、X != 0; Y != 0    收到至少Y个数据或者超时时间量够X*0.1S后read()返回;

4、X  =0 ;  Y = 0    即使读取不到任何数据,函数read也会立即返回(相当于非阻塞)。

2、示例:

通过串口连ID卡读卡器,要求读串口至少收6个字节数据立即(VTIME=0)返回,可以将串口的上述两个设置项设置如下:

options.c_cc[VTIME] = 0;

options.c_cc[VMIN]   = 6;

3)读写串口

直接用read()函数和write()函数把串口当做文件来读写即可。

int nwByte = 0;

int nrByte  = 0;

nwByte = write(fd, buffer, length);

nrByte  = read(fd, buffer, len);

读取数据方式如下,原始数据模式下每个read函数将返回实际串口收到的字符数,如果串口中没有字符可用,回叫将会阻塞直到以下几种情况:a.有字符进入;

b.一个间隔计时器失效;

c.错误发送。

在打开串口成功后,使用fcntl(fd, F_SETFL, FNDELAY)语句,可以使read函数立即返回而不阻塞。FNDELAY选项使read函数在串口无字符时立即返回且为0。

注意:

设置为原始模式传输数据的话,read函数返回的字符数是实际串口收到的字符数。Linux下直接用read读串口可能会造成堵塞,或者数据读出错误,此时可使用fcntl或者select等函数实现异步读取。用select先查询com口,再用read去读就可以避免上述错误。

4)关闭串口

串口作为文件来处理,所以一般的关闭文件函数即可:

close(fd);

5)其他参数

c_iflag:输出模式标志,控制终端输入方式,具体参数如表1所示。 表1 c_iflag参数

键 值 说 明
IGNBRK 忽略BREAK键输入
BRKINT 如果设置了IGNBRK,BREAK键输入将被忽略
IGNPAR 忽略奇偶校验错误
PARMRK 标识奇偶校验错误
INPCK 允许输入奇偶校验
ISTRIP 去除字符的第8个比特
INLCR 将输入的NL(换行)转换成CR(回车)
IGNCR 忽略输入的回车
ICRNL 将输入的回车转化成换行(如果IGNCR未设置的情况下)
IUCLC 将输入的大写字符转换成小写字符(非POSIX)
IXON 允许输出时对XON/XOFF流进行控制
IXANY 输入任何字符将重启停止的输出
IXOFF 允许输入时对XON/XOFF流进行控制
IMAXBEL 当输入队列满的时候开始响铃

c_oflag:输出模式标志,控制终端输出方式,具体参数如表2所示。 表2 c_oflag参数

键 值 说 明
OPOST 处理后输出
OLCUC 将输入的小写字符转换成大写字符(非POSIX)
ONLCR 将输入的NL(换行)转换成CR(回车)及NL(换行)
OCRNL 将输入的CR(回车)转换成NL(换行)
ONOCR 第一行不输出回车符
ONLRET 不输出回车符
OFILL 发送填充字符以延迟终端输出
OFDEL 以ASCII码的DEL作为填充字符,如果未设置该参数,填充字符为NUL
NLDLY 换行输出延时,可以取NL0(不延迟)或NL1(延迟0.1s)
CRDLY 回车延迟,取值范围为:CR0、CR1、CR2和 CR3
TABDLY 水平制表符输出延迟,取值范围为:TAB0、TAB1、TAB2和TAB3
BSDLY 空格输出延迟,可以取BS0或BS1
VTDLY 垂直制表符输出延迟,可以取VT0或VT1
FFDLY 换页延迟,可以取FF0或FF1

c_cflag:控制模式标志,指定终端硬件控制信息,具体参数如表3所示。 表3 c_cflag参数

键 值 说 明
CBAUD 波特率(4+1位)(非POSIX)
CBAUDEX 附加波特率(1位)(非POSIX)
CSIZE 字符长度,取值范围为CS5、CS6、CS7或CS8
CSTOPB 设置两个停止位
CREAD 使用接收器
PARENB 使用奇偶校验
PARODD 对输入使用奇偶校验,对输出使用偶校验
HUPCL 关闭设备时挂起
CLOCAL 忽略调制解调器线路状态
CRTSCTS 使用RTS/CTS流控制

c_lflag:本地模式标志,控制终端编辑功能,具体参数如表4所示。 表4 c_lflag参数

键 值 说 明
ISIG 当输入INTR、QUIT、SUSP或DSUSP时,产生相应的信号
ICANON 使用标准输入模式
XCASE 在ICANON和XCASE同时设置的情况下,终端只使用大写。
ECHO 显示输入字符
ECHOE 如果ICANON同时设置,ERASE将删除输入的字符
ECHOK 如果ICANON同时设置,KILL将删除当前行
ECHONL 如果ICANON同时设置,即使ECHO没有设置依然显示换行符
ECHOPRT 如果ECHO和ICANON同时设置,将删除打印出的字符(非POSIX)
TOSTOP 向后台输出发送SIGTTOU信号

c_cc[NCCS]:控制字符,用于保存终端驱动程序中的特殊字符,如输入结束符等。c_cc中定义了如表5所示的控制字符。 表5 c_cc支持的控制字符

说 明 说 明
VINTR Interrupt字符 VEOL 附加的End-of-file字符
VQUIT Quit字符 VTIME 非规范模式读取时的超时时间
VERASE Erase字符 VSTOP Stop字符
VKILL Kill字符 VSTART Start字符
VEOF End-of-file字符 VSUSP Suspend字符
VMIN 非规范模式读取时的最小字符数    

四、相关函数

1、tcflush

tcflush函数刷清(扔掉)输入缓存(终端驱动法度已接管到,但用户法度尚未读)或输出缓存(用户法度已经写,但尚未发送).

int tcflush(int filedes,int quene)
    quene数该当是下列三个常数之一:
    *TCIFLUSH  刷清输入队列
    *TCOFLUSH  刷清输出队列
    *TCIOFLUSH 刷清输入、输出队列

例如:tcflush(fd,TCIFLUSH);

在打开串口后,串口其实已经可以开始读取数据了 ,这段时间用户如果没有读取,将保存在缓冲区里,如果用户不想要开始的一段数据,或者发现缓冲区数据有误,可以使用这个函数清空缓冲

tcflush(fd, TCIFLUSH);

sleep(2);

ret = uart_recv(fd, RecvBuf, 10, 0*1000);

这样,在sleep之前发的数据都被清空了。

2、多多交流,共同进步,欢迎提出问题~~~

【嵌入式linux】Linux串口通信相关推荐

  1. Linux (Android) 串口通信教程

    Demo功能 在学习Linux的串口通信程序之前,我们先来看看Demo的效果,这样比先来一大堆串口知识介绍更加有学习动力,毕竟是能运行的.本文章不会讲解串口的原理,如何接线等,只会讲解在Linux(A ...

  2. 【嵌入式】STM32串口通信

    [嵌入式]STM32串口通信 一.串口通信协议 1.串口通信简介 2.串口通信原理 二.RS232通信协议 1.RS232协议简介 2.机械规约 3.电气规约 三.STM32的USART串口通信(查询 ...

  3. Linux c语言虚拟串口,利用socat和cutecom实现Linux虚拟串口通信

    (1)打开终端,输入 sudo apt-get install socat 等待socat安装完成- (2)输入 socat -d -d pty,raw,echo=0 pty,raw,echo=0 我 ...

  4. linux下串口通信程序,关于Linux下串口通信的一点心得

    1. 打开串口 与其他的关于设备编程的方法一样,在 Linux 下,操作.控制串口也是通过操作起设备文件进行的.在 Linux 下,串口的设备文件是 /dev/ttyS0 或 /dev/ttyS1 等 ...

  5. (二)树莓派Linux环境串口通信编程--AT指令集的发送和接收

    文章目录 一.前言 二.要了解的知识 2.1 termios结构体 2.2 tcflush() 2.3 cfsetispeed()与cfsetospeed() 三.流程图设计与代码实现 serial_ ...

  6. 【嵌入式基础】串口通信

    目录: 1. 前言 2. 基本概念 2.1 波特率 2.2 起始位 2.3 数据位 2.4 校验位 2.5 停止位 2.6 空闲位 3. 工作模式 3.1 单工模式 3.2 半双工模式 3.3 全双工 ...

  7. 【嵌入式】蓝牙串口通信透传模块(HC-08)的使用

    一 使用蓝牙透传模块简介 HC-08 蓝牙串口通信模块是新一代的基于 Bluetooth Specification V4.0 BLE 蓝牙协议的数传模块.无线工作频段为 2.4GHz ISM,调制方 ...

  8. 【嵌入式学习-STM32F103-USART串口通信】

    目录 1.串口通信协议(简介+软硬件规则) 2.STM32内部的USART外设 3.USART基本结构(江科大简化) 4.串口发送代码 4-1 基本流程 4-2 整体代码 4-2-1 main.c 4 ...

  9. Arm Linux平台串口通信,二进制数据流

    项目需要通过串口传输音频数据,由于音频数据包含不可显示的字节,属于二进制数据流,因此需要对linux端的串口读取程序设定一些参数.完整的串口配置代码如下: int fd = open("/d ...

  10. linux下串口通信详解,Linux操作系统下的串口通信学习笔记

    http://www.diybl.com/ 2008-7-5 网络 点击: [ 评论 ] - - 文章搜索:     [点击打包该文章] [本站开通在线QQ讨论群] CBAUDEX (不属于POSIX ...

最新文章

  1. 10种常用降维算法源代码(python)
  2. 在.Net如何制作自定义的快捷方式(转)
  3. MS SQL 2000 分配权限
  4. iphone全部机型_iPhone 12 销量或创 iPhone 6 以来最高|iphone|郭明錤
  5. 递归算法,JavaScript实现
  6. Swoole 2019 :化繁为简、破茧成蝶
  7. 北京交通大学计算机学院篮球,院际杯篮球赛|男篮小组赛第四轮战报
  8. win8.1 安装.NET Framework3.5
  9. AngularJS transclude 理解及例子
  10. bootdo图片上传
  11. 读书笔记之财报就像一本故事书
  12. 灰度思维,黑白决策(上)
  13. 外贸型网站建设需要多少钱
  14. 网站备案后服务器到期,域名备案后服务器到期
  15. 苹果手机扬声器没声音怎么办_苹果手机没有声音处理办法 !
  16. 程序员debug三大定律
  17. Xeam Visual Installer白金版,Xeam Visual Installer完整用户体验
  18. 用Matlab的.m脚本文件处理实验室数据
  19. Liunx磁盘管理——LVM
  20. 约瑟夫问题的理解与解决(丢手帕问题)

热门文章

  1. 家长对奥数产生的3个误解,你中招了吗?
  2. 人脸检测算法理解之mtcnn
  3. Python—序列化模块
  4. C语言实现各类string函数
  5. [Xcode 实际操作]八、网络与多线程-(25)实现ShareSdk的社会化分享功能
  6. 大学一年之后竟落到如此地步。。。开学前的挣扎
  7. 计算机视觉基础(六)——图像边缘检测
  8. 计算机网络的五层协议
  9. 考研界公认十大难考专业,你的专业上榜了吗?
  10. jQuery 兄弟元素选择器