前言

  • 目前不涉及驱动源码

参考

1. 实战分析

1.1 开发步骤

  1. 获取串口设备路径
  2. 打开设备文件
  3. 配置串口
  4. 对该设备文件进行读写,相当于对该串口设备进行读写,即通信
  5. 关闭设备文件

以下代码段默认从 附件-最终串口测试源码 中摘取

1.1.1 获取串口设备路径

  • 使用数组或者宏定义在相关文件前面定义默认串口路径,方便修改,源码段如下:
/* 不同的设备,不同的路径 */
const char def_uart_path[] = "/dev/ttymxc2"  // 默认串口路径(备用)
  • 串口路径优先从传入参数中获取,如果参数中没有传入,便使用 def_uart_path 默认路径
/* 终端设备选择 */
if(argc > 1)
    path = argv[1];
else
    path = (char *)def_uart_path;

1.1.2 打开设备文件

  • 获取设备句柄,如果获取失败,便结束
/* 打开终端 */
fd = open(path, O_RDWR);
if(fd < 0){
    printf("[%s] open err!", path);
    return 0;
}

1.1.3 配置串口

  • 定义一个结构体 termios 用于获取、设置终端设备的参数
    1. 包括波特率、数据位数、校验位等
termios 结构体
  • 成员值作用,推荐先看官方手册,看不懂再看本笔记中文表格
  • termios 结构体定义在编译链接工具的头文件默认路径中的bits文件夹中
    • 如如下源码来自 /usr/arm-linux-gnueabihf/include/bits/termios.h
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
  };
1. c_iflag 输入模式标志
  • 用于控制如何对串口输入的字符进行处理
2. c_oflag 输出模式标志
  • 用于控制串口的输出模式
3. c_cflag 控制模式标志
  • 用于控制串口的基本参数,如数据位、停止位等,常用配置见下表,特别地,c_cflag结构体成员还包含了波特率的参数
4. c_lflag 本地模式标志
  • 主要用于控制驱动程序与用户的交互,在串口通信中,实际上用不到该成员变量
5. c_cc[NCCS] 控制字符
  • 该数组包含了终端的所有特殊字符,可以修改特殊字符对应的键值(Ctrl+C产生的^C,ASCII码为0x03)
  • 仅列出常用的
6. c_ispeed和c_ospeed 波特率
  • 注意以 0 开头的数字在是 C语言 的 8进制 数字形式
#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	000001

#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
分析
  • 以上只是介绍了 termios 结构体,在编写代码时,我们使用相关 api 去配置该结构体从而配置串口
  • api 接口推荐先看本文推荐链接,不懂再看本文
// api 如下
#include <termios.h>
#include <unistd.h>

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

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

int tcsendbreak(int fd, int duration);

int tcdrain(int fd);

int tcflush(int fd, int queue_selector);

int tcflow(int fd, int action);

void cfmakeraw(struct termios *termios_p);

speed_t cfgetispeed(const struct termios *termios_p);

speed_t cfgetospeed(const struct termios *termios_p);

int cfsetispeed(struct termios *termios_p, speed_t speed);

int cfsetospeed(struct termios *termios_p, speed_t speed);

int cfsetspeed(struct termios *termios_p, speed_t speed);
  • 清空接收缓冲区,获取串口参数,配置,更新配置
/* 定义串口结构体 */
struct termios opt;
/* 清空串口接收缓冲区 */
tcflush(fd, TCIOFLUSH);
/* 获取串口参数 */
tcgetattr(fd, &opt);
/* 设置输入、输入波特率 */
cfsetospeed(&opt, B9600);
cfsetispeed(&opt, B9600);
/* 设置数据位数 */
opt.c_cflag &= ~CSIZE;
opt.c_cflag |= CS8;
/* 校验位 */
opt.c_cflag &= ~PARENB;
opt.c_iflag &= ~INPCK;
/* 设置停止位 */
opt.c_cflag &= ~CSTOPB;
/* 更新配置 */
tcsetattr(fd, TCSANOW, &opt);

1.1.4 串口收发测试

  • 串口收发测试就是对该串口进行读写
 /* 发送测试 */
write(fd, bufW, strlen(bufW));
/* 接收测试 */
res = read(fd, bufR, 512);
if(res > 0){
    printf("receive data is %s", bufR);
}

1.1.5 关闭设备文件

  • 程序正常结束,不要忘记关闭设备文件
/* 关闭文件 */
close(fd);

附件

最终串口测试源码

/** @file main.c
* @brief 串口测试文件-系统调用版
* @details 详细说明
* @author lzm
* @date 2020-11-23 19:18:20
* @version v1.0
* @copyright Copyright By lizhuming, All Rights Reserved
*
**********************************************************
* @LOG 修改日志:
**********************************************************
*/
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <termios.h>
#include <string.h>
#include <sys/ioctl.h>
/* 不同的设备,不同的路径 */
const char def_uart_path[] = "/dev/ttymxc2"; // 默认串口路径(备用)
/**
* @brief 主函数
* @param 无
* @retval 无
*/
int main(int argc, char *argv[])
{
    int fd;
    int res;
    char *path;
    char bufW[512] = "This is sys uart test!\n";
    char bufR[512];
    /* 终端设备选择 */
    if(argc > 1)
        path = argv[1];
    else
        path = (char *)def_uart_path;
    /* 打开终端 */
    fd = open(path, O_RDWR);
    if(fd < 0){
        printf("[%s] open err!", path);
        return 0;
    }
    /* 定义串口结构体 */
    struct termios opt;
    /* 清空串口接收缓冲区 */
    tcflush(fd, TCIOFLUSH);
    /* 获取串口参数 */
    tcgetattr(fd, &opt);
    /* 设置输入、输入波特率 */
    cfsetospeed(&opt, B9600);
    cfsetispeed(&opt, B9600);
    /* 设置数据位数 */
    opt.c_cflag &= ~CSIZE;
    opt.c_cflag |= CS8;
    /* 校验位 */
    opt.c_cflag &= ~PARENB;
    opt.c_iflag &= ~INPCK;
    /* 设置停止位 */
    opt.c_cflag &= ~CSTOPB;
    /* 更新配置 */
    tcsetattr(fd, TCSANOW, &opt);
    do{
        /* 发送测试 */
        write(fd, bufW, strlen(bufW));
        /* 接收测试 */
        res = read(fd, bufR, 512);
        if(res > 0){
            printf("receive data is %s", bufR);
        }
    }while(res >= 0);
    /* 读取错误 */
    printf("read error, res = %d", res);
    /* 关闭文件 */
    close(fd);
    return 0;
}
11-28 10:11
查看更多