Linux串口应用编程详解(Serial)
Posted Mculover666
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux串口应用编程详解(Serial)相关的知识,希望对你有一定的参考价值。
一、串口设备节点
二、访问串口
1. 打开串口
#include <stdio.h> /* Standard input/output definitions */
#include <string.h> /* String function definitions */
#include <unistd.h> /* UNIX standard function definitions */
#include <fcntl.h> /* File control definitions */
#include <errno.h> /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */
/*
* 'open_port()' - Open serial port 1.
*
* Returns the file descriptor on success or -1 on error.
*/
int open_port(void)
int fd; /* File descriptor for the port */
fd = open("/dev/ttyf1", O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1)
/*
* Could not open the port.
*/
perror("open_port: Unable to open /dev/ttyf1 - ");
else
fcntl(fd, F_SETFL, 0);
return (fd);
额外的设置标志:
O_NOCTTY
:告诉内核这个程序不想作为控制终端。如果不指定该标志,任何输入都会影响程序(比如键盘中止信号等)。O_NDELAY
:告诉内核这个程序不关注DCD信号线的状态。如果不指定该标志,当DCD信号线是空电压值的时候,程序将会进入睡眠。
2. 发送数据
n = write(fd, "ATZ\\r\\n", 5);
if (n < 0)
fputs("write() of 5 bytes failed!\\n", stderr);
write函数写完后会返回发送的字节数,如果发生错误会返回-1.
3. 接收数据
(1)在原始数据模式操作串口时,使用read函数即可。
成功读取时,返回串口输入buffer中实际可用的字符数量,当串口输入buffer中没有可用字符时,会引发堵塞直到新的字符到来。
read函数也可以设置直接返回,当读取不到可用字符时,立即返回0:
fcntl(fd, F_SETFL, FNDELAY);
如果想要恢复read函数的堵塞机制,可以再次设置:
fcntl(fd, F_SETFL, 0);
4. 关闭串口
close(fd);
三、设置串口(重点)
1. POSIX Terminal 接口
大多数系统都支持POSIX terminal(serial) 接口来改变串口参数,比如波特率,数据长度等。
首先包含头文件:
#include <termios.h>
该文件中和 POSIX 控制函数一样,定义了终端控制结构体Termios:
typedef unsigned char cc_t;
typedef unsigned int speed_t;
typedef unsigned int tcflag_t;
#define NCCS 32
struct termios
tcflag_t c_iflag; /* input mode flags */
tcflag_t c_oflag; /* output mode flags */
tcflag_t c_cflag; /* control mode flags */
tcflag_t c_lflag; /* local mode flags */
cc_t c_line; /* line discipline */
cc_t c_cc[NCCS]; /* control characters */
speed_t c_ispeed; /* input speed */
speed_t c_ospeed; /* output speed */
#define _HAVE_STRUCT_TERMIOS_C_ISPEED 1
#define _HAVE_STRUCT_TERMIOS_C_OSPEED 1
;
其中每项成员的意义如下:
成员 | 意义 |
---|---|
c_cflag | 控制设置 |
c_lflag | 本地设置 |
c_iflag | 输入设置 |
c_oflag | 输出设置 |
c_cc | 控制字符 |
c_ispeed | 输入速率 |
c_ospeed | 输出速率 |
两个最重要的 POSIX 函数是 tcgetattr
和 tcsetattr
,获取参数和设置参数,原型如下:
/* Put the state of FD into *TERMIOS_P. */
extern int tcgetattr (int __fd, struct termios *__termios_p) __THROW;
/* Set the state of FD to *TERMIOS_P.
Values for OPTIONAL_ACTIONS (TCSA*) are in <bits/termios.h>. */
extern int tcsetattr (int __fd, int __optional_actions,
const struct termios *__termios_p) __THROW;
tcsetattr 在设置参数时可选择的标志如下:
/* tcsetattr uses these */
#define TCSANOW 0
#define TCSADRAIN 1
#define TCSAFLUSH 2
- TCSANOW:立即设置,不用等数据完成
- TCSADRAIN:等到所有数据都发送完成再设置
- TCSAFLUSH:刷新输入和输出buffer并且完成设置
2. 控制设置(Control Options)
c_cflag成员控制波特率,数据位、校验位、停止位的宽度,和硬件流控。
2.1. 可选常量
这些设置选项支持的值都已经被定义为常量,如下。
(1)波特率
#define B0 0000000 /* hang up */
#define B50 0000001
#define B75 0000002
#define B110 0000003
#define B134 0000004
#define B150 0000005
#define B200 0000006
#define B300 0000007
#define B600 0000010
#define B1200 0000011
#define B1800 0000012
#define B2400 0000013
#define B4800 0000014
#define B9600 0000015
#define B19200 0000016
#define B38400 0000017
#define B57600 0010001
#define B115200 0010002
#define B230400 0010003
#define B460800 0010004
#define B500000 0010005
#define B576000 0010006
#define B921600 0010007
#define B1000000 0010010
#define B1152000 0010011
#define B1500000 0010012
#define B2000000 0010013
#define B2500000 0010014
#define B3000000 0010015
#define B3500000 0010016
#define B4000000 0010017
#define __MAX_BAUD B4000000
(2)数据位
#define CS5 0000000
#define CS6 0000020
#define CS7 0000040
#define CS8 0000060
(3)停止位
//设置该值则为2个停止位,不设置则为1个停止位
#define CSTOPB 0000100
(4)校验位
//使能校验位
#define PARENB 0000400
//设置该值则使用奇校验,否则使用偶校验
#define PARODD 0001000
2.2. 建议使能的标志
还有两个选项应该一直被启用:
#define CREAD 0000200
#define CLOCAL 0004000
这两个设置确保你的程序不会变为端口的所有者而受到影响,并且串口驱动程序将会速度传入的数据。
在设置 c_cflag 成员的时候,一定不能直接赋值,要使用位操作来设置或者清除对应的位。
2.3. 设置示例
(1)设置波特率为115200
struct termios options;
/*
* Get the current options for the port...
*/
tcgetattr(fd, &options);
/*
* Set the baud rates to 115200...
*/
cfsetispeed(&options, B115200);
cfsetospeed(&options, B115200);
/*
* Enable the receiver and set local mode...
*/
options.c_cflag |= (CLOCAL | CREAD);
/*
* Set the new options for the port...
*/
tcsetattr(fd, TCSANOW, &options)
(2)设置数据位为8位
options.c_cflag &= ~CSIZE; /* Mask the character size bits */
options.c_cflag |= CS8; /* Select 8 data bits */
(3)设置无校验位
options.c_cflag &= ~PARENB
options.c_cflag &= ~CSTOPB
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
(6)设置无硬件流控
options.c_cflag &= ~CNEW_RTSCTS;
3. 本地设置(Local Options)
c_lflag 成员控制输入的字符如何被串口驱动管理,一般用来设置是否使用原始数据模式即可。
开启原始数据模式:
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
四、串口回传示例
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
#include <termios.h>
#include <string.h>
/* 115200, 8, N, 1 */
int uart_setup(int fd)
struct termios options;
// 获取原有串口配置
if (tcgetattr(fd, &options) < 0)
return -1;
// 修改控制模式,保证程序不会占用串口
options.c_cflag |= CLOCAL;
// 修改控制模式,能够从串口读取数据
options.c_cflag |= CREAD;
// 不使用流控制
options.c_cflag &= ~CRTSCTS;
// 设置数据位
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
// 设置奇偶校验位
options.c_cflag &= ~PARENB;
options.c_iflag &= ~INPCK;
// 设置停止位
options.c_cflag &= ~CSTOPB;
// 设置最少字符和等待时间
options.c_cc[VMIN] = 1; // 读数据的最小字节数
options.c_cc[VTIME] = 0; //等待第1个数据,单位是10s
// 修改输出模式,原始数据输出
options.c_oflag &= ~OPOST;
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
// 设置波特率
cfsetispeed(&options, B115200);
cfsetospeed(&options, B115200);
// 清空终端未完成的数据
tcflush(fd, TCIFLUSH);
// 设置新属性
if(tcsetattr(fd, TCSANOW, &options) < 0)
return -1;
return 0;
int main(int argc, char *argv[])
int fd;
int ret;
char ch;
if (argc != 2)
printf("usage: ./test_uart [device]\\n");
return -1;
/* 打开串口 */
fd = open(argv[1], O_RDWR | O_NOCTTY | O_NDELAY);
if (fd < 0)
printf("open dev fail!\\n");
return -1;
else
fcntl(fd, F_SETFL, 0);
/* 设置串口 */
ret = uart_setup(fd);
if (ret < 0)
printf("uart setup fail!\\n");
close(fd);
return -1;
/* 串口回传实验 */
while (1)
scanf("%c", &ch);
ret = write(fd, &ch, 1);
printf("write [%c] , ret is %d!\\r\\n", ch, ret);
ret = read(fd, &ch, 1);
if (ret < 1)
printf("read fail, ret is %d\\r\\n", ret);
else
printf("recv a char:[0x%02x][%c]\\r\\n", ch, ch);
close(fd);
编译:
arm-linux-gnueabihf-gcc uart_app.c -o uart_app
在开发板上执行:
![](https://image.cha138.com/20220417/ec7b06a5cdde4d0ebbd9a23ecfb9fea2.jpg)
![](https://image.cha138.com/20220417/55dca4bdfab249f1a9ea836481551269.jpg)
以上是关于Linux串口应用编程详解(Serial)的主要内容,如果未能解决你的问题,请参考以下文章
boost asio串口编程问题:serial_port如何重新打开串口?