我是靠谱客的博主 灵巧棒球,最近开发中收集的这篇文章主要介绍Linux下uart_opt结构体使用,嵌入式Linux系统uart串口编程详解及实例分析,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

近来在一个项目开发中,在一个新的硬件平台下的linux系统,使用uart串口进行通讯,结果通讯不畅。代码是以前在其他硬件平台下验证完全没问题的代码,为什么会出问题呢?经过各方面查资料,最终定位为uart串口初始化的问题。在linux系统下,串口的初始化比较复杂,需要设置的东西比较多,如果有一些默认的配置与硬件和应用程序不匹配,而又没有重新配置,就会导致通讯失败的情况。经过对linux系统下串口初始化的进行了整理梳理,最终解决了问题。记录这批位置可以为其他小伙伴提供参考。

一、termios结构体

termios是在POSIX规范中定义的标准接口,表示终端设备,包括虚拟终端、串口等。串口通过termios进行配置。本文的内容,基本是基于这个这个结构体进行介绍,它是决定串口是否设置成功的关键。

这个结构体的定义如下:

struct termios

{

unsigned short c_iflag; // 输入模式标志

unsigned short c_oflag; // 输出模式标志

unsigned short c_cflag; // 控制模式标志

unsigned short c_lflag; // 本地模式标志

unsigned char c_line; // 线路规程

unsigned char c_cc[NCC]; // 控制特性

speed_t c_ispeed; // 输入速度

speed_t c_ospeed; // 输出速度

}

在这个结构体中包含了串口设置的绝大部分标志位,下面我们对这个结构体中的元素一一进行分析。

1、输入模式标志c_iflag

c_iflag成员负责控制串口输入数据的处理,下表是c_iflag的部分可用标志

标志

说明

INPCK

打开输入奇偶校验

IGNPAR

忽略奇偶错字符

PARMRK

标记奇偶错

ISTRIP

剥除字符第8位

IXON

启用/停止输出控制流起作用

IXOFF

启用/停止输入控制流起作用

IGNBRK

忽略BREAK条件

INLCR

将输入的NL转换为CR

IGNCR

忽略CR

ICRNL

将输入的CR转换为NL

设置输入校验

当c_cflag成员的PARENB(奇偶校验)选项启用时,c_iflag的也应启用奇偶校验选项。操作方法是启用INPCK和ISTRIP选项:

options.c_iflag |= (INPCK | ISTRIP);

如果关闭奇偶校验,则使用如下方法:

options.c_iflag &= ~(INPCK | ISTRIP);

设置软件流控制

使用软件流控制是启用IXON、IXOFF和IXANY选项:

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

相反,要禁用软件流控制是禁止上面的选项:

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

输入字符转换

linux系统可以将输入字符转换为其他字符,这个功能如果不注意可能会导致程序,比如,ICRNL这个标志位的作用是将输入的CR转换为NL ,如果要启用这个功能,则使用下面的方法:

options.c_iflag |= ICRNL;

如果要禁用这个功能,则使用下面的方法:

options.c_iflag &= ~ICRNL;

2、输入模式标志c_oflag

c_oflag成员管理输出模式,下表所示是c_oflag成员的部分选项标志。

标志

说明

BSDLY

退格延迟屏蔽

CMSPAR

标志或空奇偶性

CRDLY

CR延迟屏蔽

FFDLY

换页延迟屏蔽

OCRNL

将输出的CR转换为NL

OFDEL

填充符为DEL,否则为NULL

OFILL

对于延迟使用填充符

OLCUC

将输出的小写字符转换为大写字符

ONLCR

将NL转换为CR-NL

ONLRET

NL执行CR功能

ONOCR

在0列不输出CR

OPOST

执行输出处理

OXTABS

将制表符扩充为空格

启用输出处理

与c_iflag标志类似,串口的输出也会将一些字符进行转换,如果想使能这些转换,需要启用输出处理。启用输出处理需要在c_oflag成员中启用OPOST选项,其操作方法如下:

options.c_oflag |= OPOST;

使用原始输出

使用原始输出,就是禁用输出处理,使数据能不经过处理、过滤地完整地输出到串口接口。当OPOST被禁止,c_oflag其它选项也被忽略,其操作方法如下:

options.c_oflag &= ~OPOST;

3、控制模式标志c_cflag

c_cflag成员控制着波特率、数据位、奇偶校验、停止位以及流控制,下表列出了c_cflag可用的部分选项。

标志

说明

标志

说明

CBAUD

波特率位屏蔽

CSIZE

数据位屏蔽

B0

0位/秒(挂起)

CS5

5位数据位

B110

100位/秒

CS6

6位数据位

B134

134位/秒

CS7

7位数据位

B1200

1200位/秒

CS8

8位数据位

B2400

2400位/秒

CSTOPB

2位停止位,否则为1位

B4800

4800位/秒

CREAD

启动接收

B9600

9600位/秒

PARENB

进行奇偶校验

B19200

19200位/秒

PARODD

奇校验,否则为偶校验

B57600

57600位/秒

HUPCL

最后关闭时断开

B115200

115200位/秒

CLOCAL

忽略调制调解器状态行

B460800

460800位/秒

c_cflag成员的CREAD和CLOCAL选项通常是要启用的,这两个选项使驱动程序启动接收字符装置,同时忽略串口信号线的状态。

4、本地模式标志c_lflag

本地标志c_lflag控制着串口驱动程序如何管理输入的字符,下表所示是c_lflag的部分可用标志。

标志

说明

ISIG

启用终端产生的信号

ICANON

启用规范输入

XCASE

规范大/小写表示

ECHO

进行回送

ECHOE

可见擦除字符

ECHOK

回送kill符

ECHONL

回送NL

NOFLSH

在中断或退出键后禁用刷清

IEXTEN

启用扩充的输入字符处理

ECHOCTL

回送控制字符为^(char)

ECHOPRT

硬拷贝的可见擦除方式

ECHOKE

Kill的可见擦除

PENDIN

重新打印未决输入

TOSTOP

对于后台输出发送SIGTTOU

选择规范模式

规范模式是行处理的。调用read读取串口数据时,每次返回一行数据。当选择规范模式时,需要启用ICANON、ECHO和ECHOE选项:

options.c_lflag |= (ICANON | ECHO | ECHOE);

当串口设备作为用户终端时,通常要把串口设备配置成规范模式。

选择原始模式

在原始模式下,串口输入数据是不经过处理的,在串口接口接收的数据被完整保留。要使串口设备工作在原始模式,需要关闭ICANON、ECHO、ECHOE和ISIG选项,其操作方法如下:

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

5、控制特性c_cc[NCC]

c_cc数组的长度是NCC,一般介于15-20之间。c_cc数组的每个成员的下标都用一个宏表示,下表列出了c_cc的部分下标标志名及其对应说明。

标 志

说 明

VINTR

中断

VQUIT

退出

VERASE

擦除

VEOF

行结束

VEOL

行结束

VMIN

需读取的最小字节数

VTIME

与“VMIN”配合使用,是指限定的传输或等待的最长时间

二、实例分析

下面通过一个实例来分析如何使用嵌入式linux的串口,在这个实例中,包含三个文件,分别为:main.cpp、uart.cpp、uart.h。这个实例的作用是通过串口接收数据,并将接收的每个数据加1后再通过串口返回到发送方。

1、uart.h

uart.h的代码如下所示:

#ifndef UART_H

#define UART_H

#include #include #include #include #include #include #include #include #include #define UART1_DEV "/dev/ttyTHS2"

#define UART2_DEV "/dev/ttyTHS1"

#define UART1 1

#define UART2 2

#define BAUDRATE1 B115200

#define BAUDRATE2 B115200

using namespace std;

class Uart {

public :

~Uart();

Uart();

int uartOpen(int port, int flag, speed_t buadrate);

int uartWrite(int fd, char *data, int num);

int uartRead(int fd, char *data, int num);

void uartClose(int fd);

private:

int uartInit(int fd,speed_t buadrate);

static int uart1_only_fd;

static int uart2_only_fd;

static bool uart1_inited_flag;

static bool uart2_inited_flag;

};

#endif // UART_H

在这个头文件中,定义了若干变量,和基本的串口操作函数。

2、uart.cpp

uart.cpp的代码如下所示:

#include "uart.h"

int Uart::uart1_only_fd = 0;

int Uart::uart2_only_fd = 0;

bool Uart::uart1_inited_flag = false;

bool Uart::uart2_inited_flag = false;

Uart::Uart()

{

}

int Uart::uartInit(int fd,speed_t buadrate)

{

struct termios opt;

tcgetattr(fd,&opt);

cfsetispeed(&opt,buadrate);

cfsetospeed(&opt,buadrate);

opt.c_cflag |= CLOCAL | CREAD;

opt.c_cflag &= ~CRTSCTS;

opt.c_cflag &= ~CSIZE;

opt.c_cflag |= CS8;

opt.c_cflag &= ~PARENB;

opt.c_cflag &= ~CSTOPB;

opt.c_iflag &= ~INPCK;

opt.c_iflag &= ~(ICRNL|BRKINT|ISTRIP);

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

opt.c_oflag &= ~OPOST;

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

tcflush(fd,TCIFLUSH);

if (tcsetattr(fd,TCSANOW,&opt) != 0)

{

return -1;

}

return 0;

}

int Uart::uartOpen(int port, int flag, speed_t buadrate)

{

if(port == UART1)

{

if(uart1_inited_flag == false)

{

uart1_only_fd = open(UART1_DEV, flag);

if(uart1_only_fd < 0)

{

return -1;

}

else

{

uartInit(uart1_only_fd,buadrate);

uart1_inited_flag = true;

return uart1_only_fd;

}

}

else

{

return uart1_only_fd;

}

}

else if(port == UART2)

{

if(uart2_inited_flag == false)

{

uart2_only_fd = open(UART2_DEV, flag);

if(uart2_only_fd < 0)

{

return -1;

}

else

{

uartInit(uart2_only_fd,buadrate);

uart2_inited_flag = true;

return uart2_only_fd;

}

}

else

{

return uart2_only_fd;

}

}

return -1;

}

int Uart::uartWrite(int fd, char *data , int num)

{

int ret = -1;

ret = write(fd, data, num);

return ret;

}

int Uart::uartRead(int fd, char *data, int num)

{

int ret = -1;

int i=0;

char buf[1024] = {0};

if(num > 1024)

{

ret = read(fd, buf, 1024);

}

else

{

ret = read(fd, buf, num);

}

for (i=0;i0)

close(fd);

}

Uart::~Uart()

{

}

在这个文件中,int Uart::uartInit(int fd,speed_t buadrate)为串口初始化函数,在这个函数中,首先通过tcgetattr(fd,&opt);函数将操作系统原来的串口配置结构体读出来,并且在这个结构体的基础上进行配置。这样做的好处是在原有配置的基础上进行修改,成功率比较高,如果在一个全新的结构体上修改,很容易导致失败。

读取配置结构体之后,在下面的代码中对结构体的成员进行配置。之后调用tcflush(fd,TCIFLUSH);函数来清空输入队列。再之后调用tcsetattr(fd,TCSANOW,&opt)函数来配置串口。

在这个文件中,int Uart::uartOpen(int port, int flag, speed_t buadrate)函数的作用是开启串口,在函数里调用uartInit()函数来初始化串口,并返回串口对应的文件描述符。

int Uart::uartWrite(int fd, char *data , int num)为串口输出函数,int  Uart::uartRead(int fd, char *data, int num)为读取串口的函数,void Uart::uartClose(int fd)为关闭串口的函数。

3、main.cpp

main.cpp中调用uart.cpp中的函数实现串口的接收和发送,代码如下:

#include "uart.h"

#include #include #include int main(void)

{

Uart uart;

int uart1_fd;

int num;

char rbuff[200];

uart1_fd = uart.uartOpen(UART1,O_RDWR |O_NONBLOCK | O_NOCTTY | O_NDELAY,BAUDRATE1);

if(uart1_fd < 0)

cout<0)

{

for(int i = 0;i < num;i++)

{

rbuff[i] = rbuff[i]+1;

}

uart.uartWrite(uart1_fd,rbuff,num);

}

}

return 0;

}

最后

以上就是灵巧棒球为你收集整理的Linux下uart_opt结构体使用,嵌入式Linux系统uart串口编程详解及实例分析的全部内容,希望文章能够帮你解决Linux下uart_opt结构体使用,嵌入式Linux系统uart串口编程详解及实例分析所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(50)

评论列表共有 0 条评论

立即
投稿
返回
顶部