C/C++开发,异步串行接口(uart)通信示例

目录

一、构建简化版串口库

二、uart接口

三、UART接口调用测试


一、构建简化版串口库

        一直以来在串口通信方面都很信任libctb,其对serial/gpib通信支持win/linux,的确好用,但最近实现arm的串口通信时发现ctb还是有点复杂,因此想到构建一个相对简单的串口通信接口,主要依赖于linux的termios.h,termios 结构是在POSIX规范中定义的标准接口,通过设置termios类型的数据结构中的值和函数调用,就可以对终端接口进行控制。更多描述在命令行中man termios 查看。

        下面给出本人实践过的接口全源码及测试样例,希望对大家有所帮助。

二、uart接口

uart_linux.h

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#ifndef _UARTC_H_
#define _UARTC_H_
/***********************************************************************
  *Copyright 2020-04-06, pyfree
  *
  *File Name       : uart_linux.h
  *File Mark       : 
  *Summary         : 
  *linux uart data gather
  *
  *Current Version : 1.00
  *Author          : pyfree
  *FinishDate      :
  *
  *Replace Version :
  *Author          :
  *FinishDate      :

 ************************************************************************/
#include <termios.h>

namespace pyfree
{
	/*
	* 打开串口
	* @param fd {int } 文件指针
	* @param name {char* } 串口名,如"/dev/ttyS*"
	* @return {int} 返回<1,异常;返回>0,为fd
	*/
	int uart_open(int &fd,const char *name);
	/*
	* 串口配置
	* @param fd {int } 文件指针
	* @param baude {int } 波特率
	* @param c_flow {int } 流标识
	* @param bits {int } 数据位
	* @param parity {char } 奇偶位
	* @param stop {int } 停止位
	* @return {int}  返回=-1,异常;返回=0,成功,为读取大小
	*/
	int uart_config(int fd,int baude,int c_flow, int bits, char parity, int stop);
	/*
	* 串口读取数据
	* @param fd {int } 文件指针
	* @param r_buf {char* } 缓存指针
	* @param lenth {int } 缓存大小
	* @param time_out_ms {等待时间 } 毫秒,默认1000
	* @return {int} 返回<=0,异常;返回>0,成功
	*/
	int uart_read(int fd, char *r_buf, int lenth, int time_out_ms=1000);
	/*
	* 串口写入数据
	* @param fd {int } 文件指针
	* @param r_buf {char* } 内容指针
	* @param lenth {int } 大小
	* @return {int} 返回<=0,异常;返回>0,成功,为写入大小
	*/
	int uart_write(int fd, char *r_buf, int lenth, int time_out_ms=1000);
	/*
	*用于清空输入、输出缓冲区 
	* @param fd {int } 文件指针
	* @param model {int } 模式,有三种取值  TCIFLUSH(用于清空输入缓冲区) TCOFLUSH(用于清空输出缓冲区) TCIOFLUSH(用于清空输入输出缓冲区)
	* @return {int} 
	*/
	int uart_clear(int fd,int model);
	/*
	* 关闭串口
	* @param fd {int } 文件指针
	* @return {int} 
	*/
	int uart_close(int fd);
};

#endif

uart_linux.cpp

#include "uart_unix.h"

#include <stdio.h>
#include <fcntl.h>
#include <assert.h>
#include <termios.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <errno.h>

int pyfree::uart_open(int &fd,const char *name)
{
    //检测串口路径是否存在
    assert(name);								
    //以读写形式、不将此终端作为此进程的终端控制器、非阻塞的形式打开串口
    fd = open(name,O_RDWR|O_NOCTTY|O_NDELAY);	
    if(fd == -1)
    {
        char e_buf[64]={0};
        sprintf(e_buf,"uart open %s failed!",name);
        perror(e_buf);
        return -1;
    }
    //设置串口非阻塞,因为这里是以非阻塞形式打开的,第三个参数为0,返回值:成功返回0,失败返回-1,失败原因存入errno
    if(fcntl(fd,F_SETFL,0)<0)						
    {
        perror("fcntl failed!");
        return -1;
    }
    return fd;
};

int pyfree::uart_config(int fd,int baude,int c_flow, int bits, char parity, int stop)
{
    struct termios uart;
    //用于获取termios结构体属性。成功返回0,失败返回非0
    if(tcgetattr(fd,&uart)!=0)
    {
        perror("tcgetattr failed!");
        return -1;
    }

    switch(baude)
    {
    case 4800:
        cfsetispeed(&uart,B4800);//设置输入波特率
        cfsetospeed(&uart,B4800);//设置输出波特率
        break;
    case 9600:
        cfsetispeed(&uart,B9600);
        cfsetospeed(&uart,B9600);
        break;
    case 19200:
        cfsetispeed(&uart,B19200);
        cfsetospeed(&uart,B19200);
        break;
    case 38400:
        cfsetispeed(&uart,B38400);
        cfsetospeed(&uart,B38400);
        break;
    default:
        fprintf(stderr,"Unknown baude!");
        return -1;
    }
    switch(c_flow)
    {
    case 'N':
    case 'n':
        uart.c_cflag &= ~CRTSCTS;//不进行硬件流控制
        break;
    case 'H':
    case 'h':
        uart.c_cflag |= CRTSCTS;//进行硬件流控制
        break;
    case 'S':
    case 's':
        uart.c_cflag |= (IXON | IXOFF | IXANY);//进行软件流控制
        break;
    default:
        fprintf(stderr,"Unknown c_cflag");
        return -1;
    }
    switch(bits)
    {
    case 5:
        uart.c_cflag &= ~CSIZE;//屏蔽其他标志位
        uart.c_cflag |= CS5;//数据位为5位
        break;
    case 6:
        uart.c_cflag &= ~CSIZE;
        uart.c_cflag |= CS6;
        break;
    case 7:
        uart.c_cflag &= ~CSIZE;
        uart.c_cflag |= CS7;
        break;
    case 8:
        uart.c_cflag &= ~CSIZE;
        uart.c_cflag |= CS8;
      break;
    default:
        fprintf(stderr,"Unknown bits!");
        return -1;
    }
    switch(parity)
    {
    case 'n':
    case 'N':
        uart.c_cflag &= ~PARENB;//PARENB:产生奇偶校验
        uart.c_cflag &= ~INPCK;//INPCK:使奇偶校验起作用
        break;
    case 's':
    case 'S':
        uart.c_cflag &= ~PARENB;
        uart.c_cflag &= ~CSTOPB;//使用两位停止位
        break;
    case 'o':
    case 'O':
        uart.c_cflag |= PARENB;
        uart.c_cflag |= PARODD;//使用奇校验
        uart.c_cflag |= INPCK;
        uart.c_cflag |= ISTRIP;//使字符串剥离第八个字符,即校验位
        break;
    case 'e':
    case 'E':
        uart.c_cflag |= PARENB;
        uart.c_cflag &= ~PARODD;//非奇校验,即偶校验
        uart.c_cflag |= INPCK;
        uart.c_cflag |= ISTRIP;
        break;
    default:
        fprintf(stderr,"Unknown parity!\n");
        return -1;
    }
    switch(stop)
    {
    case 1:
        uart.c_cflag &= ~CSTOPB;//CSTOPB:使用两位停止位
        break;
    case 2:
        uart.c_cflag |= CSTOPB;
        break;
    default:
        fprintf(stderr,"Unknown stop!\n");
        return -1;
    }

    uart.c_oflag &= ~OPOST;//OPOST:表示数据经过处理后输出
    if(tcsetattr(fd,TCSANOW,&uart)<0)//激活配置,失败返回-1
    {
        return -1;
    }

    uart.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG );//使串口工作在原始模式下
    uart.c_cc[VTIME] = 0;//设置等待时间为0
    uart.c_cc[VMIN] = 1;//设置最小接受字符为1
    tcflush(fd,TCIFLUSH);//清空输入缓冲区
    if(tcsetattr(fd,TCSANOW,&uart)<0)//激活配置
    {
        perror("tcgetattr failed!");
        return -1;
    }
    return 0;
}

int pyfree::uart_read(int fd, char *r_buf, int lenth, int time_out_ms/*=1000*/)
{
    fd_set rfds;
    struct timeval time;
    ssize_t cnt = 0;
    /*将读文件描述符加入描述符集合*/
    FD_ZERO(&rfds);
    FD_SET(fd,&rfds);
    /*设置超时为*/
    time.tv_sec = (time_t)(time_out_ms/1000);
    time.tv_usec = (long)1000*(time_out_ms%1000);
    /*实现多路IO*/
    int ret = select(fd+1, &rfds ,NULL, NULL, &time);
    switch (ret) {
    case -1:
        fprintf(stderr,"select error!\n");
        break;
    case 0:
        fprintf(stderr, "time over!\n");
        break;
    default:
        cnt = read(fd, r_buf, lenth);
        if(cnt == -1)
        {
            fprintf(stderr, "read failed!\n");
            return -1;
        }
        return cnt;
    }
    return ret;
}

int pyfree::uart_write(int fd, char *r_buf, int lenth, int time_out_ms/*=1000*/)//串口写入数据
{
    fd_set rfds;
    struct timeval time;
    ssize_t cnt = 0;
    /*将读文件描述符加入描述符集合*/
    FD_ZERO(&rfds);
    FD_SET(fd,&rfds);
    /*设置超时为*/
    time.tv_sec = (time_out_ms/1000);
    time.tv_usec = 1000*(time_out_ms%1000);
    /*实现多路IO*/
    int ret = select(fd+1, &rfds ,NULL, NULL, &time);
    switch (ret) {
    case -1:
        fprintf(stderr,"select error!\n");
        break;
    case 0:
        fprintf(stderr, "time over!\n");
        break;
    default:
        cnt = write(fd, r_buf, lenth);
        if(cnt!=lenth)
        {
            fprintf(stderr, "write failed!\n");
            return -1;
        }
        return cnt;
    }
    return ret;
}

int pyfree::uart_clear(int fd,int model)
{
    assert(fd);
    int ret = tcflush(fd,model);
    if(ret<0){
        fprintf(stderr, "tcflush failed!\n");
    }
    return ret;
}

int pyfree::uart_close(int fd)
{
    assert(fd);//assert先检查文件描述符是否存在
    close(fd);
    return 0;
}


三、UART接口调用测试

        demo_test,如果想在虚拟的linux系统测试,有无实物设备,方案选择如下:

采用"VSPD虚拟串口.zip"串口工具安装并创建一个串口对COM4<->COM5  

采用"sscom.exe"工具模拟设备端,打开并设置其端口选择COM5,19200 8 N 1  ,其支持定时发送

(如果没有这两款工具,可以去我个人空间下载:pyfree-IotEdge: c++开发的一套物联网边缘服务系统,当前主要包含采集和调度两个子系统,采集子系统用于设备的供电、态势、IT资源等信息采集,支持串口、网口、蓝牙等采集接口,以及采集数据的分析、中转、级联;调度子系统通过可定制的定期、定时、轮询、条件、启停等任务策略以及可视化监控终端实现设备态势的自动化调度与运维 - Gitee.com

        主机win系统,安装VMware,在该工具创建虚拟机,安装linux系统,在虚拟机关闭时,进入虚拟机设置页面,添加串行端口,指定使用物理串行端口,选择COM4(前提需要创建虚拟串口对)  则对应虚拟linux系统/dev/ttyS0

#include "uart_unix.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
	int fd;
	int ret;
	char r_buf[256];
	bzero(r_buf,256);
	fd = pyfree::uart_open(fd, "/dev/ttyS0");//选择的是/dev/ttyS0串口
	if(fd == -1)
	{
		fprintf(stderr,"open failed!\n");
		exit(EXIT_FAILURE);
	}
	fprintf(stderr,"open success!\n");
	if(pyfree::uart_config(fd,19200,'N',8,'N',1) == -1)
	{
		fprintf(stderr,"configure failed!\n");
		exit(EXIT_FAILURE);
	}
	fprintf(stderr,"config success!\n");
	pyfree::uart_clear(fd,TCIOFLUSH);
	while (1) {
		ret = pyfree::uart_read(fd,r_buf,256,1000);                
		if(ret == -1)
		{
			fprintf(stderr, "uart_read failed!\n");
			exit(EXIT_FAILURE);
		}
		if(ret>0){
			printf("buf:%s\n", r_buf);
			// ret = pyfree::uart_write(fd,r_buf,strlen(r_buf));   
			// if(ret == -1)
			// {
			// 	fprintf(stderr, "uart_write failed!\n");
			// 	exit(EXIT_FAILURE);
			// }
		}
	}

	ret = pyfree::uart_close(fd);
	if(ret == -1)
	{
		fprintf(stderr, "close failed!\n");
		exit(EXIT_FAILURE);
	}

	exit(EXIT_SUCCESS);
}
香橙派(Orange Pi)是一款基于Linux的单板计算机,常用于嵌入式系统开发UART(Universal Asynchronous Receiver/Transmitter,通用异步收发器)串口通信是其常见的通信接口之一,特别是在C++中。下面是一个简单的示例代码片段,展示了如何使用C++在香橙派上进行UART串口通信: ```cpp #include <iostream> #include <unistd.h> // for usleep() #include <fcntl.h> // for open, O_NOCTTY, etc. #include <termios.h> // for termios structure and related functions #define BAUDRATE 9600 #define DEV_NAME "/dev/ttyUSB0" // 将此处替换为你的UART设备路径 int main() { int uart_fd = -1; struct termios options; // 打开串口设备 uart_fd = open(DEV_NAME, O_RDWR | O_NOCTTY | O_NDELAY); if (uart_fd == -1) { std::cerr << "Failed to open UART device: " << strerror(errno) << std::endl; return 1; } // 设置波特率和其他参数 tcgetattr(uart_fd, &options); // 获取当前设置 cfsetispeed(&options, B9600); // 设置波特率为9600 bps cfsetospeed(&options, B9600); options.c_cflag &= ~PARENB; // 清除 parity bit options.c_cflag &= ~CSTOPB; // 清除停止位 options.c_cflag &= ~CSIZE; // 清除数据位大小,这里默认为8位 options.c_cflag |= CS8; // 设置为8位数据位 options.c_iflag &= ~(IXON | IXOFF | IXANY); // 关闭DTR和RTS流控 options.c_oflag &= ~OPOST; // 关闭发送缓冲区 options.c_lflag &= ~(ICANON | ECHO | ISIG); // 关闭回显、非阻塞输入等 tcflush(uart_fd, TCIFLUSH); // 清空输入缓冲区 tcsetattr(uart_fd, TCSANOW, &options); // 发送数据 char data[] = "Hello, UART!"; write(uart_fd, data, strlen(data)); // 接收数据(如果需要) char received[100]; read(uart_fd, received, sizeof(received)); std::cout << "Received: " << received << std::endl; close(uart_fd); return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

py_free-物联智能

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

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

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

打赏作者

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

抵扣说明:

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

余额充值