Android UART串口通信总结

  • UART串口通信概念
  • 数据结构
  • termios作用与设置
  • linux下的open/close/read/write 函数使用

UART串口通信概念

通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),通常称作UART,是一种异步收发传输器,UART作为异步串口通信协议的一种,工作原理是将传输数据的每个字符一位接一位地传输。

数据结构

起始位 (逻辑0):表示传输字符的开始

数据位 :数据位的个数可以设置为4,5,6,7,8,构成一个字符。

奇偶校验位: 数据位加上奇偶校验位后,逻辑“1”的数量为偶数则为偶校验。为奇数则为奇校验。在串口通信中一种简单的检错方式。有四种检错方式:偶、奇、高和低。当然没有校验位也是可以的。对于偶和奇校验的情况,串口会设置校验位(数据位后面的一位),用一个值确保传输的数据有偶个或者奇个逻辑高位。例如,如果数据是011,那么对于偶校验,校验位为0,保证逻辑高的位数是偶数个。如果是奇校验,校验位为1,这样就有3个逻辑高位。高位和低位不是真正的检查数据,简单置位逻辑高或者逻辑低校验。这样使得接收设备能够知道一个位的状态,有机会判断是否有噪声干扰了通信或者是否传输和接收数据是否不同步。

停止位:它是一帧数据的结束标志。用于表示单个包的最后一位。典型的值为1,1.5和2位。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。

空闲位:没有数据传输时,为逻辑“1“

传输方向:数据从高位(msb)开始传输还是从低位(lsb)开始传输

这里写图片描述

termios作用与设置

函数描述了用于控制异步通信端口的通用终端接口。参见Linux API termios描述

引用头文件

#include "termios.h"

结构体:

struct termios{
    unsigned short c_iflag;   /* 输入模式标志*/
    unsigned short c_oflag;   /* 输出模式标志*/
    unsigned short c_cflag;   /* 控制模式标志*/
    unsigned short c_lflag;   /* 区域模式标志或本地模式标志或局部模式*/
    unsigned char  c_line;    /* 行控制line discipline */
    unsigned char  c_cc[NCCS]; /* 控制字符特性*/
};

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 当输入队列满的时候开始响铃
IUTF9 当输入是UTF8时,能够允许字符擦除在加工模式下正确执行

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:控制模式标志,指定终端硬件控制信息

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

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]:控制字符,用于保存终端驱动程序中的特殊字符,如输入结束符等

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

注意:控制符VTIME和VMIN之间有复杂的关系。VTIME定义要求等待的时间(百毫米,通常是unsigned char变量),而VMIN定义了要求等待的最小字节数(相比之下,read函数的第三个参数指定了要求读的最大字节数)。
如果VTIME=0,VMIN=要求等待读取的最小字节数,read必须在读取了VMIN个字节的数据或者收到一个信号才会返回。
如果VTIME=时间量,VMIN=0,不管能否读取到数据,read也要等待VTIME的时间量。
如果VTIME=时间量,VMIN=要求等待读取的最小字节数,那么将从read读取第一个字节的数据时开始计时,并会在读取到VMIN个字节或者VTIME时间后返回。
如果VTIME=0,VMIN=0,不管能否读取到数据,read都会立即返回
​ ​
tcsetattr函数用于设置终端的相关参数。

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

参数fd为打开的终端文件描述符
参数optional_actions用于控制修改起作用的时间,而结构体termios_p中保存了要修改的参数optional_actions可以取如下的值:

参数 参数说明
TCSANOW 不等数据传输完毕就立即改变属性。
TCSADRAIN 等待所有数据传输结束才改变属性。
TCSAFLUSH 清空输入输出缓冲区才改变属性。
返回值 参数说明
EBADF 非法的文件描述符。
EINTR tcsetattr函数调用被信号中断。
EINVAL 参数optional_actions使用了非法值,或参数termios中使用了非法值。
ENCTTY 非终端的文件描述符。

tcgetattr获取与fd终端关联的termios

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

tcsetattr重新设置与fd终端关联的termios

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

设置波特率

int cfsetispeed(struct termios *termios_p, speed_t speed); //输入波特率
int cfsetospeed(struct termios *termios_p, speed_t speed); //输出波特率

常见配置:
8位数据位、无校验位:
c_cflag &= ~PARENB;
c_cflag &= ~CSTOPB;
c_cflag &= ~CSIZE;
c_cflag |= CS8;

7位数据位、奇校验:
c_cflag |= PARENB;
c_cflag |= PARODD;
c_cflag &= ~CSTOPB;
c_cflag &= ~CSIZE;
c_cflag |= CS7;

7位数据位、偶校验:
c_cflag |= PARENB;
c_cflag &= ~PARODD;
c_cflag &= ~CSTOPB;
c_cflag &= ~CSIZE;
c_cflag |= CS7;

7位数据位、Space校验:
c_cflag &= ~PARENB;
c_cflag &= ~CSTOPB;
c_cflag &= ~CSIZE;
c_cflag |= CS7;

//8位数据位,无校验位,波特率115200
char *path = "/dev/ttyHSL1"
termios termios_p;
int fd = open(path, O_RDWR | O_NOCTTY);
tcflush(fd, TCIOFLUSH);
tcgetattr(fd, &termios_p);
termios_p.c_cflag &= ~PARENB;
termios_p.c_cflag &= ~CSTOPB;
termios_p.c_cflag &= ~CSIZE;
termios_p.c_cflag |= CS8;
cfsetispeed(&termios_p, B115200);
cfsetospeed(&termios_p, B115200);
tcsetattr(fd, &termios_p);

linux下的open/close/read/write 函数使用

#include  
#include  
int open(const char *path, int oflag, ... ); 

#include 
ssize_t read(int fd, void *buf, size_t count);

#include  
ssize_t write(int fd, const void *buf, size_t count);   

#include 
int close(int fd);

一个简单的应用文本读写:

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

struct {
   int fd;
} global_v;

void open_t();
void read_t();
void write_t();
void close_t();

int main(){
   open_t();
   write_t();
   read_t();
   close_t();
   return 0;
}

void open_t(){
    char *file_path = "D:/log.txt";
    int fd = open(file_path, O_RDWR | O_APPEND);
    if(fd > 0){
        global_v.fd = fd;
    }
}

void read_t(){
    if(global_v.fd < 0){
        return;
    }

    int ret = -1;
    char buf[256];
    printf("read_t len = %d\n", sizeof(buf));
    while((ret = read(global_v.fd, &buf, sizeof(buf))) > 0){
        for(int i = 0; i < ret; i ++){
            printf("%c ", buf[i]);
        }
    }
}

void write_t(){
    if(global_v.fd < 0){
        return;
    }

    char *write_buf = "testbuf\n";
    int ret = write(global_v.fd, write_buf, strlen(write_buf));
    printf("write = %d\n", ret); 
}

void close_t(){
    if(global_v.fd < 0){
        return;
    }
    close(global_v.fd);
}

你可能感兴趣的:(Android UART串口通信总结)