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 函数是 tcgetattrtcsetattr,获取参数和设置参数,原型如下:

/* 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

在开发板上执行:

  • 9
    点赞
  • 69
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
### 回答1: Linux串口应用编程是指在Linux操作系统下对串口进行编程实现数据收发、控制等功能的过程。通过串口应用编程,可以实现与外设的数据交换、通信等功能,并可以将其应用于自动化控制、数据采集、远程控制等领域。 ### 回答2: Linux串口应用编程是指在Linux系统下,使用串口进行通讯的开发应用串口是一种常见的计算机通讯接口,可以用来连接不同设备,如打印机、传感器、单片机等,并进行数据传输。在Linux下,串口通讯可以通过串口设备文件进行访问。 Linux提供了很多接口用于串口编程,比如termios和ioctl两个函数族。其中,termios函数用于设置串口参数,包括波特率、数据位、停止位和校验位等,这些参数在串口通讯中十分重要。ioctl函数族则用于控制串口的I/O操作,比如发送数据、读取数据、清空缓冲区等。 使用Linux进行串口编程的几个关键步骤包括: 1.打开串口设备文件,通过open()函数打开设备文件,比如/dev/ttyS0。 2.设置串口参数,使用termios函数族中的tcgetattr()和tcsetattr()函数进行串口参数的设置,比如串口波特率、数据位、停止位、校验位等。 3.进行串口通讯,可以使用I/O函数族中的read()和write()函数进行数据的读取和发送等操作,也可以使用select()函数实现多路复用控制。 4.关闭串口设备文件,使用close()函数关闭串口设备文件。 除了上述基本步骤外,还可以考虑使用Linux提供的串口编程库,例如serial、libserialport等,来简化程序编写和开发,提高开发效率。 总之,Linux串口应用编程是一种十分重要的开发应用,通过熟练掌握相关的函数库和接口,可以实现串口与其他设备之间的数据交换与通讯。 ### 回答3: Linux操作系统自带了强大的串口驱动,支持多种串口通信协议,如RS-232、RS-485和RS-422等。Linux串口编程是一种高效而灵活的方式,可用于开发各种应用软件,如数据采集、远程监测、机器控制等,相比于其他操作系统,Linux提供的串口编程功能更为完善、稳定和安全,因而被广泛应用于各个领域。 首先,我们需要在Linux操作系统中选择一个串口设备,通常是/dev/tty*,其中*为设备号。然后,我们需要使用系统调用函数打开串口设备,如open()函数,配置串口通信参数(如波特率、数据位、停止位、校验位),设置串口读写缓冲区等,接着就可以开始进行串口通信了。 对于串口读写操作,可以使用read()和write()函数进行数据收发,也可以使用readline()函数从串口中读取一行数据。此外,还可以使用ioctl()函数实现更加复杂的控制操作,如设置流控制、使用DTR、RTS等控制信号。 在进行Linux串口编程时,需要注意一些常见的错误和异常情况,如串口通信超时、校验错误、数据损失等,这些问题通常需要通过调试和优化程序代码来解决。另外,还需要注意串口设备的权限设置和用户身份验证,以确保程序的安全性和稳定性。 总之,Linux串口编程是一种重要而广泛应用的技术,掌握了Linux串口编程技能,可以为我们带来许多便利和发展机会。在进行Linux串口编程之前,需要先学习和了解一些基础知识和操作技巧,通过实践和经验积累,不断优化和完善程序,才能实现更加高效和可靠的串口通信功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mculover666

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值