1.前言

通用异步收发传输器(Universal Asynchronous Receiver/Transmitter,通常称作UART) 是一种串行异步收发协议,应用十分广泛。UART工作原理是将数据的二进制位一位一位的进行传输,在UART通讯协议中信号线上的状态位高电平代表’1’,低电平代表’0’。

2.通信协议

UART帧格式如下:

  • 空闲位:
    UART协议规定,当总线处于空闲状态时信号线的状态为‘1’即高电平
  • 起始位:
    开始进行数据传输时发送方要先发出一个低电平’0’来表示传输字符的开始。因为空闲位一直是高电平所以开始第一次通讯时先发送一个明显区别于空闲状态的信号即为低电平。
  • 数据位:
    起始位之后就是要传输的数据,数据可以是5,6,7,8,9位,构成一个字符,一般都是8位。先发送最低位最后发送最高位。
  • 奇偶校验位:
    数据位传送完成后,要进行奇偶校验,校验位其实是调整个数,串口校验分几种方式:
    1.无校验(no parity)
    2.奇校验(odd parity):如果数据位中’1’的数目是偶数,则校验位为’1’,如果’1’的数目是奇数,校验位为’0’。
    3.偶校验(even parity):如果数据为中’1’的数目是偶数,则校验位为’0’,如果为奇数,校验位为’1’。
    4.mark parity:校验位始终为1
    5.space parity:校验位始终为0
  • 停止位:
    数据结束标志,可以是1位,1.5位,2位的高电平。
  • 波特率:
    数据传输速率使用波特率来表示,单位bps(bits per second),常见的波特率9600bps,115200bps等等,其他标准的波特率是1200,2400,4800,19200,38400,57600。举个例子,如果串口波特率设置为9600bps,那么传输一个比特需要的时间是1/9600≈104.2us。

3.基本操作

在LINUX编程中需要通过UART通信,对其基本操作包括:打开UART设备、设置基本属性、读写IO、关闭UART设备。

针号      功能              缩写1      数据载波检测         DCD2      接收数据            RXD3      发送数据            TXD4      数据端准备           DTR5      信号地              GND6      数据设备准备好        DSR7      请求发送             RTS8      清除发送             CTS9      振铃提示             DELL
其中RXD和TXD与GND比较常用,DTR和RTS这种在加有流控的串口设备上回使用

3.1 打开串口

fd = open("/dev/ttyS0", O_RDWR|O_NOCTTY|O_NDELAY);
  • O_NOCTTY:表示程序不会成为这个端口上的“控制终端”,若不这样做的话,所有的输入会影响到你的进程,如键盘Ctrl+C中止信号可停止进程。
  • O_NDELAY:表示程序并不关心DCD信号线的状态,即不关心端口另一端是否已经连接。

3.2 基本属性

设置串口属性包括基本的波特率,校验位,停止位等,其中最重要的一个结构体 struct termios ,每个选项都是16位数,包含了串口全部属性。

#include <termios.h>
struct termios{tcflag_t  c_iflag;      //输入模式标志tcflag_t  c_oflag;      //输出模式标志tcflag_t  c_cflag;      //控制选项tcflag_t  c_lflag;      //行选项cc_t      c_cc[NCCS];   //控制字符
};

(1) c_iflag:输入模式标志,控制终端输入方式

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

(2) c_oflag:输出模式标志,控制终端输出方式

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

(3) c_cflag:控制模式标志,指定终端硬件控制信息

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

(4) c_lflag:本地模式标志,控制终端编辑功能

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

(5) c_cc[NCCS]:控制字符,用于保存终端驱动程序中的特殊字符
只有在本地模式标志c_lflag中设置了IEXITEN时,POSIX没有定义的控制字符才能在Linux中使用。每个控制字符都对应一个按键组合(^C和^H等)。 VMIN和VTIME这两个控制字符除外,它们不对应控制符。这两个控制字符只在原始模式下才有效。

    键 值             说 明c_cc[VMIN]      原始模式(非标准模式)读的最小字符数c_cc[VTIME]     原始模式(非标准模式)读时的延时,以十分之一秒为单位c_cc[VINTR]     默认对应的控制符是^C,作用是清空输入和输出队列的数据并且向tty设备的前台进程组中的每一个程序发送一个SIGINT信号,对SIGINT信号没有定义处理程序的进程会马上退出。c_cc[VQUIT]     默认对应的控制符是^/,作用是清空输入和输出队列的数据并向tty设备的前台进程组中的每一个程序发送一个SIGQUIT信号,对SIGQUIT 信号没有定义处理程序的进程会马上退出。c_cc[verase]    默认对应的控制符是^H或^?,作用是在标准模式下,删除本行前一个字符,该字符在原始模式下没有作用。c_cc[VKILL]     默认对应的控制符是^U,在标准模式下,删除整行字符,该字符在原始模式下没有作用。c_cc[VEOF]      默认对应的控制符是^D,在标准模式下,使用read()返回0,标志一个文件结束。c_cc[VSTOP]     默认对应的控制字符是^S,作用是使用tty设备暂停输出直到接收到VSTART控制字符。或者,如果设备了IXANY,则等收到任何字符就开始输出。c_cc[VSTART]    默认对应的控制字符是^Q,作用是重新开始被暂停的tty设备的输出。c_cc[VSUSP]     默认对应的控制字符是^Z,使当前的前台进程接收到一个SIGTSTP信号。c_cc[VEOL]c_cc[VEOL2]     在标准模式下,这两个下标在行的末尾加上一个换行符('/n'),标志一个行的结束,从而使用缓冲区中的数据被发送,并开始新的一行。POSIX中没有定义VEOL2。c_cc[VREPRINT]  默认对应的控制符是^R,在标准模式下,如果设置了本地模式标志ECHO,使用VERPRINT对应的控制符和换行符在本地显示,并且重新打印当前缓冲区中的字符。POSIX中没有定义VERPRINT。c_cc[VWERASE]   默认对应的控制字符是^W,在标准模式下,删除缓冲区末端的所有空格符,然后删除与之相邻的非空格符,从而起到在一行中删除前一个单词的效果。 POSIX中没有定义VWERASE。c_cc[VLNEXT]    默认对应的控制符是^V,作用是让下一个字符原封不动地进入缓冲区。如果要让^V字符进入缓冲区,需要按两下^V。POSIX中没有定义 VLNEXT。

3.3 操作函数

(1)获取属性

int tcgetattr(int fd, struct termios *termios_p);

一般的在设置属性之前先读出原来的串口信息保存前,因为有些参数我们不用修改使用原值就行。
(2)设置属性

int tcsetattr(int fd, int optional_actions,const struct termios *termios_p);

功能:设置与终端相关的参数 (除非需要底层支持却无法满足),使用termios_p 引用的termios 结构。optional_actions指定了什么时候改变会起作用,具体见下。

  • TCSANOW:改变立即发生
  • TCSADRAIN:改变在所有写入fd 的输出都被传输后生效。这个函数应当用于修改影响输出的参数时使用(当前输出完成时将值改变)
  • TCSAFLUSH :改变在所有写入fd 引用的对象的输出都被传输后生效,所有已接受但未读入的输入都在改变发生前丢弃(同TCSADRAIN,但会舍弃当前所有值)

(3)等待所有输出都被传输

int tcdrain(int fd);

(4)刷新IO

int tcflush(int fd, int queue_selector);

功能:丢弃要写入引用的对象,对象由queue_selector 选择:

  • TCIFLUSH:刷新收到的数据但是不读
  • TCOFLUSH:刷新写入的数据但是不传送
  • TCIOFLUSH:同时刷新收到的数据但是不读,并且刷新写入的数据但是不传送

(5)获取输入速度

speed_t cfgetispeed(const struct termios *termios_p);

(6)获取输出速度

speed_t cfgetospeed(const struct termios *termios_p);

3.4 关闭串口

close(fd);

4 代码演示

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>int set_opt(int,int,int,char,int);
int uart_send(int fd,void *buf, int len);
int uart_recv_timeout(void *buf, int len, int timeout_ms);
void main()
{int fd,ret,i=10;char *uart3 = "/dev/ttySAC3";char *buffer = "hello world!\n";printf("\r\nitop4412 uart3 writetest start\r\n");if((fd = open(uart3, O_RDWR | O_NOCTTY | O_NDELAY)) < 0){printf("open %s is failed",uart3);}else{printf("open %s is success\n",uart3);set_opt(fd, 115200, 8, 'N', 1); while(i--){ret = uart_send(fd,buffer, strlen(buffer));if(ret < 0)printf("write failed\n");else{printf("wr_static is %d\n",ret);}sleep(1);}}close(fd);
}int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop)
{struct termios newtio,oldtio;/*获取原有串口配置*/if  ( tcgetattr( fd,&oldtio)  !=  0) { perror("SetupSerial 1");return -1;}memset( &newtio, 0, sizeof(newtio) );/*CREAD 开启串行数据接收,CLOCAL并打开本地连接模式*/newtio.c_cflag  |=  CLOCAL | CREAD;/*设置数据位*/newtio.c_cflag &= ~CSIZE;switch( nBits ){case 7:newtio.c_cflag |= CS7;break;case 8:newtio.c_cflag |= CS8;break;}/* 设置奇偶校验位 */switch( nEvent ){case 'O':newtio.c_cflag |= PARENB;newtio.c_cflag |= PARODD;newtio.c_iflag |= (INPCK | ISTRIP);break;case 'E': newtio.c_iflag |= (INPCK | ISTRIP);newtio.c_cflag |= PARENB;newtio.c_cflag &= ~PARODD;break;case 'N':  newtio.c_cflag &= ~PARENB;break;}/* 设置波特率 */switch( nSpeed ){case 2400:cfsetispeed(&newtio, B2400);cfsetospeed(&newtio, B2400);break;case 4800:cfsetispeed(&newtio, B4800);cfsetospeed(&newtio, B4800);break;case 9600:cfsetispeed(&newtio, B9600);cfsetospeed(&newtio, B9600);break;case 115200:cfsetispeed(&newtio, B115200);cfsetospeed(&newtio, B115200);break;case 460800:cfsetispeed(&newtio, B460800);cfsetospeed(&newtio, B460800);break;default:cfsetispeed(&newtio, B9600);cfsetospeed(&newtio, B9600);break;}/*设置停止位*/if( nStop == 1 )/*设置停止位;若停止位为1,则清除CSTOPB,若停止位为2,则激活CSTOPB*/newtio.c_cflag &=  ~CSTOPB;/*默认为一位停止位; */else if ( nStop == 2 )newtio.c_cflag |=  CSTOPB;/*设置最少字符和等待时间,对于接收字符和等待时间没有特别的要求时*/newtio.c_cc[VTIME]  = 0;/*非规范模式读取时的超时时间;*/newtio.c_cc[VMIN] = 0;/*非规范模式读取时的最小字符数*//*tcflush清空终端未完成的输入/输出请求及数据;TCIFLUSH表示清空正收到的数据,且不读取出来 */tcflush(fd,TCIFLUSH);if((tcsetattr(fd,TCSANOW,&newtio))!=0){perror("com set error");return -1;}
//  printf("set done!\n\r");return 0;
}int uart_send(int fd,void *buf, int len)
{int ret = 0;int count = 0;tcflush(fd, TCIFLUSH);while (len > 0) {ret = write(fd, (char*)buf + count, len);if (ret < 1) {break;}count += ret;len = len - ret;}return count;
}int uart_recv_timeout(void *buf, int len, int timeout_ms)
{int ret;size_t  rsum = 0;ret = 0;fd_set rset;struct timeval t;while (rsum < len){t.tv_sec = timeout_ms/1000;t.tv_usec = (timeout_ms - t.tv_sec*1000)*1000;FD_ZERO(&rset);FD_SET(uart_fd, &rset);ret = select(uart_fd+1, &rset, NULL, NULL, timeout);if (ret <= 0) {if (ret == 0) {//timeoutreturn -1;}if (errno == EINTR) {// 信号中断continue;}return -errno;} else{ret = read(uart_fd, (char *)buf + rsum, len - rsum);if (ret < 0){return ret;}else{rsum += ret;}}}return rsum;
}

加入讨论

参考博文

  1. Linux串口UART编程–C语言

【Linux应用】串口UART编程相关推荐

  1. ARM架构与编程(基于I.MX6ULL): 串口UART编程(七)

    文章目录 1.硬件知识 URAT硬件介绍 1.1.串口的硬件介绍 1.2.串口的参数 1.3.串口电平 1.4 串口内部结构 2. IMX6ULL UART操作 2.1. 串口编程步骤 2.1.1. ...

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

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

  3. Linux串口应用编程

    目录 Demo 串口应用编程介绍 终端Terminal 串口应用编程(配置.读取.写入) struct termios 结构体配置 输入模式: c_iflag 输出模式: c_oflag 控制模式: ...

  4. Linux下C语言串口应用编程,Linux下串口C语言编程

    Linux下串口C语言编程 (5页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 9.9 积分 串口操作代码#include #include #inclu ...

  5. 【嵌入式Linux】嵌入式Linux应用开发基础知识之串口应用编程

    文章目录 前言 1.ARM芯片是如何使用串口发送/接收数据的 2. TTY体系中设备节点的差别 做个小实验 3.TTY驱动程序框架 4.在STM32MP157上做串口实验的准备工作 4.1.使能设备树 ...

  6. Linux应用基础——串口应用编程

    串口应用编程 串口介绍 扩展-- TTY体系 TTY简介 TTY系统框架 代码部分 基本配置部分 GPS模块的应用 GPS模块数据格式 GPS代码 基于平台:IMX6ULL PRO开发板 串口介绍 串 ...

  7. 【SSD20X平台Linux应用开发】串口应用编程

    1. 串口的作用 UART:通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),简称串口. 调试:移植u-boot.内核.应用程序时,主要使用 ...

  8. Linux uart寄存器读写,Linux下读写UART串口的代码

    Linux下读写UART串口的代码,从IBM Developer network上拿来的东西,操作比較的复杂,就直接跳过了,好在代码能用,记录一下- 两个实用的函数- /** *@brief 设置串口 ...

  9. linux实验串行端口程序设计,Linux下串口编程心得(转)

    最近一段时间,需要完成项目中关于Linux下使用串口的一个部分,现在开帖记录过程点滴. 项目的要求是这样的,Qt应用程序主要完成数据采集和发送功能,一开始在google中海搜关键字"Qt串口 ...

最新文章

  1. 二分图匹配匈牙利算法DFS实现
  2. 经常使用的eclipse插件
  3. opentracing
  4. 3分钟入门python_3分钟学完Python,直接从入门到精通「史上最强干货库」
  5. 发表自我的windows live writer
  6. 想成为BAT中的一员,你总要去学点什么(二)
  7. .NET Core全新的配置管理[共9篇]
  8. Tomcat就是这么简单
  9. querydsl动态 sql_Spring data jpa 复杂动态查询方式总结
  10. 让您的数据库服务器与您对话:直接从SQL Agent Jobs发送电子邮件
  11. 【图像加密】基于matlab GUI彩色图像加密解密(带面板)【含Matlab源码 1231期】
  12. AI 图像识别项目从入门到上线
  13. Denise Perfect Room for mac(混响插件)
  14. 8/3 MATLAB绘制正态分布概率密度函数(normpdf)图形
  15. 计算机控制器cu的功能是,控制器cu的功能是
  16. 2021牛客寒假算法基础集训营1-E-三棱锥之刻-(计算几何)
  17. bzoj 1269 editor
  18. c语言迷宫问题程序流程图,c语言程序设计 迷宫问题
  19. 我的世界服务器被无限循环怎么办,我的世界无限循环装置制作方法
  20. Windows配置互联网访问检测服务器-IspSrv

热门文章

  1. C语言实现单链表逆置算法
  2. 2023年全国最新保安员精选真题及答案16
  3. 简单好用的容器监控工具-Weave scope
  4. 脚本小子_什么是数据处理
  5. 安卓NumberPicker数字选择器用法
  6. 2022年安徽省技术创新示范企业奖励补贴标准以及申报条件(附合肥市各地区奖补标准)
  7. php 字符串函数 教程_最全的php字符串处理函数
  8. jsp mysql简单仓库信息管理系统_基于jsp+mysql的JSP简单学生信息管理系统
  9. 袋鼠云高教行业数字化转型方案,推进数字化技术和学校教育教学深度融合
  10. java面试基础问题答不上来怎么办,快来看鸭~