2 Star 4 Fork 3

hu_submodule/linux_serial

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
serial.c 15.04 KB
一键复制 编辑 原始数据 按行查看 历史
huenrong 提交于 2021-12-07 15:22 . 1. 修改serial.c文件中定义
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673
/**
* @file : serial.c
* @brief : Linux下串口驱动源文件
* @author : huenrong ([email protected])
* @date : 2021-03-26 20:51:12
* @author : huenrong
*
* @copyright : Copyright (c) 2021 胡恩荣
*
*/
#include "./serial.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
#include <string.h>
#include <pthread.h>
#include <linux/serial.h>
#include <sys/ioctl.h>
// 串口名最大长度
#define SERIAL_NAME_MAX_LEN 15
// 串口最大数量
#define SERIAL_MAX_NUM 10
// 记录串口信息
typedef struct
{
// 串口名
char serial_name[SERIAL_NAME_MAX_LEN];
// 串口文件描述符
int serial_fd;
// 串口互斥锁
pthread_mutex_t serial_mutex;
} serial_info_t;
// 记录串口信息索引
static uint8_t g_serial_info_index = 0;
// 记录串口信息
static serial_info_t g_serial_info[SERIAL_MAX_NUM];
/**
* @brief 查找指定串口的信息
* @param info : 输出参数, 查找到的串口信息
* @param serial_name: 输入参数, 待查找的串口名
* @return 成功: 0
* 失败: -1
*/
static int find_serial_info(serial_info_t *info, const char *serial_name)
{
int ret = -1;
if ((!info) || (!serial_name))
{
return -1;
}
for (uint8_t i = 0; i < g_serial_info_index; i++)
{
ret = memcmp(g_serial_info[i].serial_name,
serial_name, strlen(serial_name));
if (0 == ret)
{
memcpy(info, &g_serial_info[i], sizeof(serial_info_t));
return 0;
}
}
return -1;
}
/**
* @brief 设置串口标准波特率
* @param options : 输出参数, 串口属性
* @param baud_rate: 输入参数, 波特率
* @return 成功: 0
* 失败: -1
*/
static int set_serial_std_baud_rate(struct termios *options,
const serial_baud_rate_e baud_rate)
{
if (!options)
{
return -1;
}
// 设置输入波特率
if (0 != cfsetispeed(options, baud_rate))
{
return -1;
}
// 设置输出波特率
if (0 != cfsetospeed(options, baud_rate))
{
return -1;
}
return 0;
}
/**
* @brief 设置串口特殊波特率
* @param options : 输出参数, 串口属性
* @param fd : 输入参数, 文件描述符
* @param baud_rate: 输入参数, 波特率
* @return 成功: 0
* 失败: -1
*/
static int set_serial_special_baud_rate(struct termios *options, const int fd,
const int baud_rate)
{
struct serial_struct serial = {0};
if (!options)
{
return -1;
}
// 设置波特率为38400
if (0 != cfsetispeed(options, B38400))
{
return -1;
}
if (0 != cfsetospeed(options, B38400))
{
return -1;
}
if (0 != ioctl(fd, TIOCGSERIAL, &serial))
{
return -1;
}
// 设置标志位和系数
serial.flags = ASYNC_SPD_CUST;
serial.custom_divisor = (serial.baud_base / baud_rate);
if (0 != ioctl(fd, TIOCSSERIAL, &serial))
{
return -1;
}
return 0;
}
/**
* @brief 设置串口数据位
* @param options : 输出参数, 串口属性
* @param data_bit: 输入参数, 数据位
* @return 成功: 0
* 失败: -1
*/
static int set_serial_data_bit(struct termios *options,
const serial_data_bit_e data_bit)
{
if (!options)
{
return -1;
}
options->c_cflag &= ~CSIZE;
options->c_cflag |= data_bit;
return 0;
}
/**
* @brief 设置串口奇偶检验位
* @param options : 输出参数, 串口属性
* @param parity_bit: 输入参数, 奇偶检验位, 默认为无校验'n'或'N'
* @return 成功: 0
* 失败: -1
*/
static int set_serial_parity_bit(struct termios *options,
const serial_parity_bit_e parity_bit)
{
if (!options)
{
return -1;
}
switch (parity_bit)
{
// 无校验
case E_SERIAL_PARITY_BIT_N:
{
options->c_cflag &= ~PARENB;
options->c_iflag &= ~INPCK;
break;
}
// 奇校验
case E_SERIAL_PARITY_BIT_O:
{
options->c_cflag |= (PARODD | PARENB);
options->c_iflag |= INPCK;
break;
}
// 偶校验
case E_SERIAL_PARITY_BIT_E:
{
options->c_cflag |= PARENB;
options->c_cflag &= ~PARODD;
options->c_iflag |= INPCK;
break;
}
// 默认为无校验
default:
{
options->c_cflag &= ~PARENB;
options->c_iflag &= ~INPCK;
break;
}
}
return 0;
}
/**
* @brief 设置串口停止位
* @param options : 输出参数, 串口属性
* @param stop_bit: 输入参数, 停止位, 默认为1位停止位
* @return 成功: 0
* 失败: -1
*/
static int set_serial_stop_bit(struct termios *options,
const serial_stop_bit_e stop_bit)
{
if (!options)
{
return -1;
}
switch (stop_bit)
{
// 1位停止位
case E_SERIAL_STOP_BIT_1:
{
options->c_cflag &= ~CSTOPB;
break;
}
// 2位停止位
case E_SERIAL_STOP_BIT_2:
{
options->c_cflag |= CSTOPB;
break;
}
// 默认为1位停止位
default:
{
options->c_cflag &= ~CSTOPB;
break;
}
}
return 0;
}
/**
* @brief 打开串口
* @param serial_name : 输入参数, 串口名(例: /dev/ttyS1)
* @param std_baud_rate : 输入参数, 标准波特率(若使用特殊波特率必须填SERIAL_BAUD_RATE_SPECIAL)
* @param special_baud_rate: 输入参数, 特殊波特率(不使用填任意值)
* @param data_bit : 输入参数, 数据位
* @param parity_bit : 输入参数, 奇偶检验位, 默认为无校验'n'或'N'
* @param stop_bit : 输入参数, 停止位, 默认为1位停止位
* @return 成功: 0
* 失败: -1
*/
int open_serial(const char *serial_name,
const serial_baud_rate_e std_baud_rate,
const int special_baud_rate,
const serial_data_bit_e data_bit,
const serial_parity_bit_e parity_bit,
const serial_stop_bit_e stop_bit)
{
int fd = -1;
int ret = -1;
// 串口信息
serial_info_t info = {0};
// 串口属性
struct termios options = {0};
if (!serial_name)
{
return -1;
}
// 超过支持的串口数量, 直接返回错误
if (g_serial_info_index > SERIAL_MAX_NUM)
{
return -1;
}
// 串口已打开, 先关闭串口
ret = find_serial_info(&info, serial_name);
if (0 == ret)
{
// 关闭串口
close_serial(info.serial_name);
}
// 打开串口
fd = open(serial_name, O_RDWR | O_NOCTTY | O_NDELAY);
if (fd < 0)
{
return -1;
}
// 获取终端属性
if (0 != tcgetattr(fd, &options))
{
close(fd);
return -1;
}
// 设置波特率
if (E_SERIAL_BAUD_RATE_SPECIAL != std_baud_rate)
{
if (0 != set_serial_std_baud_rate(&options, std_baud_rate))
{
close(fd);
return -1;
}
}
else
{
if (0 != set_serial_special_baud_rate(&options, fd, special_baud_rate))
{
close(fd);
return -1;
}
}
// 设置数据位
if (0 != set_serial_data_bit(&options, data_bit))
{
close(fd);
return -1;
}
// 设置奇偶检验位
if (0 != set_serial_parity_bit(&options, parity_bit))
{
close(fd);
return -1;
}
// 设置停止位
if (0 != set_serial_stop_bit(&options, stop_bit))
{
close(fd);
return -1;
}
// 一般必设置的标志
options.c_cflag |= (CLOCAL | CREAD);
options.c_oflag &= ~(OPOST);
options.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
options.c_iflag &= ~(ICRNL | INLCR | IGNCR | IXON | IXOFF | IXANY);
// 清空输入输出缓冲区
if (0 != tcflush(fd, TCIOFLUSH))
{
close(fd);
return -1;
}
// 设置最小接收字符数和超时时间
// 当MIN=0, TIME=0时, 如果有数据可用, 则read最多返回所要求的字节数,
// 如果无数据可用, 则read立即返回0
options.c_cc[VMIN] = 0;
options.c_cc[VTIME] = 0;
// 设置终端属性
if (0 != tcsetattr(fd, TCSANOW, &options))
{
close(fd);
return -1;
}
// 记录串口信息
memcpy(g_serial_info[g_serial_info_index].serial_name,
serial_name, strlen(serial_name));
g_serial_info[g_serial_info_index].serial_fd = fd;
// 初始化互斥锁
pthread_mutex_init(&g_serial_info[g_serial_info_index].serial_mutex, NULL);
g_serial_info_index++;
return 0;
}
/**
* @brief 关闭串口
* @param serial_name: 输入参数, 串口名(例: /dev/ttyS1)
* @return 成功: 0
* 失败: -1
*/
int close_serial(const char *serial_name)
{
int ret = -1;
for (uint8_t i = 0; i < g_serial_info_index; i++)
{
ret = memcmp(g_serial_info[i].serial_name,
serial_name, strlen(serial_name));
// 当前串口已打开
if (0 == ret)
{
// 关闭串口
ret = close(g_serial_info[i].serial_fd);
if (ret < 0)
{
return -1;
}
// 清空串口信息
g_serial_info[i].serial_fd = -1;
memset(g_serial_info[i].serial_name, 0, SERIAL_NAME_MAX_LEN);
// 销毁互斥锁
pthread_mutex_destroy(&g_serial_info[i].serial_mutex);
// 将串口信息放到数组最前面
memcpy(&g_serial_info[i], &g_serial_info[i + 1],
(sizeof(serial_info_t) * (SERIAL_MAX_NUM - i - 1)));
(g_serial_info_index > 0) ? g_serial_info_index-- : 0;
return 0;
}
}
return 0;
}
/**
* @brief 清空串口输入缓存
* @param serial_name: 输入参数, 串口名(例: /dev/ttyS1)
* @return 成功: 0
* 失败: -1
*/
int serial_flush_input_data(const char *serial_name)
{
int ret = -1;
serial_info_t info; // 串口信息
// 串口未打开, 直接返回成功
ret = find_serial_info(&info, serial_name);
if (0 != ret)
{
return 0;
}
ret = tcflush(info.serial_fd, TCIFLUSH);
return ret;
}
/**
* @brief 清空串口输出缓存
* @param serial_name: 输入参数, 串口名(例: /dev/ttyS1)
* @return 成功: 0
* 失败: -1
*/
int serial_flush_output_data(const char *serial_name)
{
int ret = -1;
serial_info_t info; // 串口信息
// 串口未打开, 直接返回成功
ret = find_serial_info(&info, serial_name);
if (0 != ret)
{
return 0;
}
ret = tcflush(info.serial_fd, TCOFLUSH);
return ret;
}
/**
* @brief 清空串口输入和输出缓存
* @param serial_name: 输入参数, 串口名(例: /dev/ttyS1)
* @return 成功: 0
* 失败: -1
*/
int serial_flush_both_data(const char *serial_name)
{
int ret = -1;
serial_info_t info; // 串口信息
// 串口未打开, 直接返回成功
ret = find_serial_info(&info, serial_name);
if (0 != ret)
{
return 0;
}
ret = tcflush(info.serial_fd, TCIOFLUSH);
return ret;
}
/**
* @brief 串口发送数据
* @param serial_name : 输入参数, 串口名(例: /dev/ttyS1)
* @param send_data : 输入参数, 发送数据
* @param send_data_len: 输入参数, 发送数据长度
* @return 成功: 0
* 失败: -1
*/
int serial_write_data(const char *serial_name, const uint8_t *send_data,
const uint32_t send_data_len)
{
int ret = -1;
serial_info_t info; // 串口信息
// 串口未打开, 直接返回失败
ret = find_serial_info(&info, serial_name);
if (0 != ret)
{
return -1;
}
// 加锁
pthread_mutex_lock(&info.serial_mutex);
ret = write(info.serial_fd, send_data, send_data_len);
if (send_data_len != ret)
{
// 解锁
pthread_mutex_unlock(&info.serial_mutex);
return -1;
}
// 解锁
pthread_mutex_unlock(&info.serial_mutex);
return 0;
}
/**
* @brief 串口接收数据
* @param recv_data : 输出参数, 接收数据
* @param serial_name : 输入参数, 串口名(例: /dev/ttyS1)
* @param recv_data_len: 输入参数, 接收数据长度
* @param timeout: : 输入参数, 接收超时(单位:ms)
* @return 成功: 实际接收数据长度
* 失败: -1
*/
int serial_read_data(uint8_t *recv_data, const char *serial_name,
const uint32_t recv_data_len, uint32_t timeout)
{
int ret = -1;
nfds_t nfds = 1; // 指定fds数组中的项目数
struct pollfd fds[1]; // 指定要监视的文件描述符集
int total_data_len = 0; // 已读取数据长度
int remain_data_len = 0; // 未读取数据长度
serial_info_t info; // 串口信息
// 串口未打开, 直接返回失败
ret = find_serial_info(&info, serial_name);
if (0 != ret)
{
return -1;
}
memset(recv_data, 0, recv_data_len);
remain_data_len = recv_data_len;
// 加锁
pthread_mutex_lock(&info.serial_mutex);
while (1)
{
// 设置需要监听的文件描述符
memset(fds, 0, sizeof(fds));
fds[0].fd = info.serial_fd;
fds[0].events = POLLIN;
ret = poll(fds, nfds, timeout);
// 返回负值, 发生错误
if (ret < 0)
{
// 解锁
pthread_mutex_unlock(&info.serial_mutex);
return -1;
}
// 返回0, 超时
else if (0 == ret)
{
// 如果超时后, 已读取数据长度大于0, 返回实际接收数据长度
if (total_data_len > 0)
{
// 解锁
pthread_mutex_unlock(&info.serial_mutex);
return total_data_len;
}
// 解锁
pthread_mutex_unlock(&info.serial_mutex);
return -1;
}
// 返回值大于0, 成功
else
{
// 判断是否是期望的返回
if (fds[0].revents & POLLIN)
{
// 从文件起始位置开始读数据
lseek(fds[0].fd, 0, SEEK_SET);
ret = read(fds[0].fd, &recv_data[total_data_len],
remain_data_len);
if (ret < 0)
{
// 解锁
pthread_mutex_unlock(&info.serial_mutex);
return -1;
}
// 计算已读取数据长度
total_data_len += ret;
// 计算剩余需要读取长度
remain_data_len = (recv_data_len - total_data_len);
// 读取完毕
if (total_data_len == recv_data_len)
{
break;
}
}
}
}
// 解锁
pthread_mutex_unlock(&info.serial_mutex);
return total_data_len;
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/hu_git_submodule/linux_serial.git
[email protected]:hu_git_submodule/linux_serial.git
hu_git_submodule
linux_serial
linux_serial
master

搜索帮助