目录
前言
- 目前不涉及驱动源码
参考
1. 实战分析
1.1 开发步骤
- 获取串口设备路径
- 打开设备文件
- 配置串口
- 对该设备文件进行读写,相当于对该串口设备进行读写,即通信
- 关闭设备文件
以下代码段默认从 附件-最终串口测试源码 中摘取
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 用于获取、设置终端设备的参数
- 包括波特率、数据位数、校验位等
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;
}