1、发现设备

1)首先查看串口驱动是否存在:

lsmod | grep usbserial

若不存在,则可通过下述方式安装驱动:

sudo apt-get install setserial

2)插入串口设备,可在/dev/目录下查看到串口设备的存在,串口设备文件的一般格式为:

/dev/ttyUSBx(x表示0,1,...)

同时,可通过下述发现串口设别是否已经成功接入:

dmesg | grep ttyUSB*

3)以程序的形式发现串口设备

函数定义:

/* 获取可用的串口设备 */
static int GetAvailableUartDev(char (*strDevList)[MAX_DEV_NAME_LENS], size_t u32MaxItem, const char* strCmd)
{
	char buf[MAX_DEV_NAME_LENS] = {0};
	char tmp[MAX_DEV_NAME_LENS] = {0};
	FILE *pf = NULL;
	int s32Size = 0;
	
	/* 创建管道用来执行命令 */
	pf = popen(strCmd, "r");
	if(NULL == pf)
	{
	    perror("It's failed to open the shell of cmd");
		return -1;
	}
 
	/* 分行获取数据 */
	while(fgets(buf, sizeof(buf), pf) && s32Size < u32MaxItem)
	{	
	    sscanf(buf, "%s\n", tmp);   /* 用以剔除末尾的回车 */
	    memcpy(strDevList[s32Size], tmp, sizeof(tmp));
	    s32Size++;
	}
	
	/* 关闭管道 */
	pclose(pf);
	
	/* 打印获取到的数据 */
	printf("Device lists as follow: (%d items)\n", s32Size);
	for(int i = 0; i < s32Size; ++i)
	{
	    printf("%d, %s\n", i, strDevList[i]);
	}
	
	return s32Size;
}

上述方法通过系统函数popen,执行shell命令,得以将结果返回。传入的shell命令为:

ls /dev/ttyUSB* -l | awk '{print $NF}' | sort

 

2、串口通信

uart.c文件

#include "uart.h"
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <stdlib.h>
#include <pthread.h>
#include <signal.h>

UART_CTRL pstCtrl = {0};

/* 信号的注册函数 */
void CleanUpFunc(int sig_num)
{
    if(pstCtrl.fd != -1)
    {
        close(pstCtrl.fd);
    }
    printf("Program exit via signal function!\n");
    exit(1);
}

/* 设置串口的属性,包括波特率,数据位数,奇偶校验位,停止位 */
static int SetUartAttr(int fd, int nSpeed, int nBits, char nEvent, int nStop)
{
    struct termios newtio;
    bzero(&newtio, sizeof(newtio));
    newtio.c_cflag |= CLOCAL | CREAD;   //本地连接,接收使能
    newtio.c_cflag &= ~CSIZE;           //屏蔽数据位
    
    /* 设置数据位数 */
    switch( nBits )
    {
        case 7:
            newtio.c_cflag |= CS7;
            break;
        case 8:
            newtio.c_cflag |= CS8;
            break;
    }
    
    /* 设置奇偶校验位 */
    switch( nEvent )
    {
        case 'O':   /* 奇数校验 */
            newtio.c_cflag |= PARENB;               //有校验
            newtio.c_cflag |= PARODD;               //奇校验
            newtio.c_iflag |= (INPCK | ISTRIP);
            break;
        case 'E':   /* 偶数校验 */
            newtio.c_iflag |= (INPCK | ISTRIP);
            newtio.c_cflag |= PARENB;               //有校验
            newtio.c_cflag &= ~PARODD;              //偶校验
            break;
        case 'N':   /* 无校验 */
            newtio.c_cflag &= ~PARENB;              //无校验
            break;
    }
    
    /* 设置波特率 */
    switch( nSpeed )
    {
        case 2400:
            cfsetispeed(&newtio, B2400);
            cfsetospeed(&newtio, B2400);
            break;
        case 4800:
            cfsetispeed(&newtio, B4800);
            cfsetospeed(&newtio, B4800);
            break;
        case 9600:
            cfsetispeed(&newtio, B9600);
            cfsetospeed(&newtio, B9600);
            break;
        case 115200:
            cfsetispeed(&newtio, B115200);
            cfsetospeed(&newtio, B115200);
            break;
        default:
            cfsetispeed(&newtio, B9600);
            cfsetospeed(&newtio, B9600);
            break;
    }
    
    /* 设置停止位 */
    if( nStop == 1 )
    {
        newtio.c_cflag &= ~CSTOPB;
    }        
    else if ( nStop == 2 )
    {
        newtio.c_cflag |= CSTOPB;
    }
        
    newtio.c_cc[VTIME] = 0;      // 等待时间,单位百毫秒 (读)
    newtio.c_cc[VMIN] = 0;       // 最小字节数 (读)
    tcflush(fd, TCIFLUSH);       // 刷清输入队列,即将配置信息立即刷入系统
    if((tcsetattr(fd, TCSANOW, &newtio)) != 0)  // TCSANOW立即生效
    {
        perror("com set error");
        return -1;
    }

    printf("SetUartAttr exec ok!\n");
    return 0;
}

/* 打开串口设备 */
static int OpenUartDev(char* strDevFile)
{
    int fd = 0;
    int ret = 0;
    
    printf("Opening device: \"%s\"\n", strDevFile);

    /* 打开设备文件 */
    fd = open(strDevFile, O_RDWR|O_NOCTTY|O_NDELAY);
    if (-1 == fd)
    {
        perror("Can't Open Serial Port");
        return -1;
    }
 
    /* 设置文件描述符的文件状态为0,即复位文件状态 */
    ret = fcntl(fd, F_SETFL, 0);
    if(ret < 0)
    {
        printf("fcntl failed!\n");
    }

    /* 判断文件描述符(标准输入)是否是为终端 */
    ret = isatty(STDIN_FILENO);
    if(ret == 0)
    {
        printf("Standard input is not a terminal device\n");
    }

    printf("Open device ok, fd=%d, dev=%s\n", fd, strDevFile);
    return fd;
}

/* 获取可用的串口设备 */
static int GetAvailableUartDev(char (*strDevList)[64], size_t u32MaxItem, const char* strCmd)
{
	char buf[MAX_DEV_NAME_LENS] = {0};
	char tmp[MAX_DEV_NAME_LENS] = {0};
	FILE *pf = NULL;
	int s32Size = 0;
	
	/* 创建管道用来执行命令 */
	pf = popen(strCmd, "r");
	if(NULL == pf)
	{
	    perror("It's failed to open the shell of cmd");
		return -1;
	}
 
	/* 分行获取数据 */
	while(fgets(buf, sizeof(buf), pf) && s32Size < u32MaxItem)
	{	
	    sscanf(buf, "%s\n", tmp);   /* 用以剔除末尾的回车 */
	    memcpy(strDevList[s32Size], tmp, sizeof(tmp));
	    s32Size++;
	}
	
	/* 关闭管道 */
	pclose(pf);
	
	/* 打印获取到的数据 */
	printf("Device lists as follow: (%d items)\n", s32Size);
	for(int i = 0; i < s32Size; ++i)
	{
	    printf("%d, %s\n", i, strDevList[i]);
	}
	
	return s32Size;
}

/* 串口初始化 */
int UartInit(int nSpeed, int nBits, char nEvent, int nStop)
{
    int ret = -1;

      /* 注册信号,用于回应"Ctrl+c" */
    signal(SIGINT, CleanUpFunc);
    
    /* 获取可用串口 */
    pstCtrl.u32DevNums = GetAvailableUartDev(pstCtrl.strDevList, MAX_DEV_NUMS, "ls /dev/ttyUSB* -l | awk '{print $NF}' | sort");
    if(pstCtrl.u32DevNums <= 0)
    {
        printf("There isn't any available device be found!\n");
        return -1;
    }
    
    /* 打开串口 */
    pstCtrl.fd = OpenUartDev(pstCtrl.strDevList[0]);
    if(pstCtrl.fd < 0)
    {
        perror("open_port error");
        return -2;
    }
    
    /* 设置串口属性,包括波特率、数据位数、奇偶校验、停止位等 */
    ret = SetUartAttr(pstCtrl.fd, nSpeed, nBits, nEvent, nStop);
    if(ret < 0)
    {
        perror("SetUartAttr error");
        return -3;
    }

    printf("UartInit exec ok!\n");
    return 0;
}

/* 串口发送数据 */
int UartSendData(const void *buf, size_t size)
{
    if(NULL == buf || 0 == size)
    {
        printf("The send data is error occured! buf=%p, size=%ld", buf, size);
        return -1;
    }

    write(pstCtrl.fd, buf, size);
    return 0;
}

/* 串口接收数据 */
size_t UartRecvData(void *buf, size_t size)
{
    if(NULL == buf || 0 == size)
    {
        printf("The send data is error occured! buf=%p, size=%lu", buf, size);
        return -1;
    }

    ssize_t s64RecvSize = 0;
    s64RecvSize = read(pstCtrl.fd, buf, size);
    return s64RecvSize;
}

uart.h文件

#ifndef _UART_H_
#define _UART_H_

#include <stdio.h>


#define MAX_DEV_NAME_LENS (64)  /* 每个设备的名字最大长度 */
#define MAX_DEV_NUMS      (64)  /* 一次最大可以扫描到的设备数量 */

typedef struct _UART_CTRL_
{
    int fd;     //串口设备的文件描述符
    int u32DevNums;     //扫描到的串口设备的数量
    char strDevList[MAX_DEV_NUMS][MAX_DEV_NAME_LENS];   //设备列表,包含扫描到的所有串口设备
}UART_CTRL;

/* 串口初始化,需设置串口的波特率,数据位数,奇偶校验位,停止位 */
int UartInit(int nSpeed, int nBits, char nEvent, int nStop);

/* 串口发送数据 */
int UartSendData(const void *buf, size_t size);

/* 串口接收数据 */
size_t UartRecvData(void *buf, size_t size);


#endif

3、对端收发

对端可以直接使用scanf从串口上接收数据,使用printf将数据发送到串口。

注意:当在对端使用scanf接收来自串口的数据时,Linux发送端应在数据末尾加上换行符'\n'。

4、参考资料

1)linux串口通信编程 - Malphite - 博客园 (cnblogs.com)

2)linux下的串口通信_u013485792的专栏-CSDN博客_linux 串口通信

3)linux下串口通信与管理 - ssooking - 博客园 (cnblogs.com)

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐