linux 串口调试工具和源码

一、主要功能

1.实现了非标波特率设置功能。

2.实现简单测试服务端和客户端。

3.实现一个简单通讯协议。

二、串口源码

cpp 复制代码
#include <stdio.h> //标准输入输出.printf().perror()
#include <fcntl.h> //设备文件操作 open()
#include <sys/types.h> //设备文件操作 open()
#include <sys/stat.h> //设备文件操作 open()
#include <errno.h>
#include <string.h> //内存空间操作 memset()
#include <unistd.h> //读写函数 read()
#include <sys/select.h> //阻塞函数 select() 好像不要也行?
#include <sys/ioctl.h>
#include <asm/termbits.h>
//#include <linux/termios.h>
//int ioctl(int d, int request, ...);

#define COM2 "/dev/ttyS2"
#define BUFF_MAX_SIZE 1*1024*1024
#define LOG_ERROR(fmt, ...) fprintf(stderr, "[%s:%d]:" fmt "\n", __FUNCTION__, __LINE__, ##__VA_ARGS__);

//#define _DEBUG
#ifdef _DEBUG
#define LOG_DEBUG(fmt, ...) fprintf(stdout, "[%s:%d]:" fmt "\n", __FUNCTION__, __LINE__, ##__VA_ARGS__);
#else
#define LOG_DEBUG(fmt, ...)
#endif

int tcgetattr2(int fd, struct termios2 *termios_p)
{
    struct termios2 termios;

   if((ioctl(fd, TCGETS2, &termios))<0){
        LOG_ERROR("faild to get the termios2:%s",strerror(errno));
        return -1;
    }
    memcpy(termios_p, &termios, sizeof(termios));
    return 0;
}

int tcsetattr2(int fd, int optional_actions, const struct termios2 *termios_p)
{
    struct termios2 termios;
    memcpy( &termios, termios_p, sizeof(termios));

   if((ioctl(fd, TCSETS2, &termios))<0){
        LOG_ERROR("faild to set the termios2:%s",strerror(errno));
        return -1;
    }
    return 0;
}

int tcflush2(int fd, int queue_selector)
{
  return ioctl (fd, TCFLSH, queue_selector);
}

int set_serial(int fd, int baudrate, int data, int parity, int stop)
{
    struct termios2 struct_serial; //创建串口属性结构体
    tcgetattr2(fd, &struct_serial); //获取当前串口属性

    struct_serial.c_cflag &= ~CBAUD;
    struct_serial.c_cflag |= BOTHER; //0x00001000;
    struct_serial.c_ispeed = baudrate;
    struct_serial.c_ospeed = baudrate;

    struct_serial.c_cflag &= ~CSIZE; //初始化数据位为默认值
    switch(data) {//设置数据位
        case 7:
        struct_serial.c_cflag |= CS7; //7位
        break;
        case 8:
        struct_serial.c_cflag |= CS8; //8位
        break;
        default:
        LOG_ERROR("set data %d error.", data);
        return -1;
    }
    
    switch(parity) {//设置校验方式
        case 0: //无校验
        struct_serial.c_cflag &= ~PARENB; //关闭校验
        struct_serial.c_iflag &= ~INPCK; //关闭输入校验
        break;
        case 1: //奇校验
        struct_serial.c_cflag |= PARENB; //打开校验
        struct_serial.c_iflag |= INPCK; //打开输入校验
        struct_serial.c_cflag |= PARODD; //设为奇校验
        break;    
        case 2: //偶校验
        struct_serial.c_cflag |= PARENB; //打开校验
        struct_serial.c_iflag |= INPCK; //打开输入校验
        struct_serial.c_cflag &= ~PARODD; //设为奇校验
        break;
        default:
        LOG_ERROR("set parity %d error.", parity);
        return -1;
    }
    
    switch(stop) {//设置停止位
        case 1: //1位
        struct_serial.c_cflag &= ~CSTOPB;
        break;
        case 2: //2位
        struct_serial.c_cflag |= CSTOPB;
        break;    
        default:
        LOG_ERROR("set stop %d error.", stop);
        return -1;
    }

    //下列为一些配置,具体作用尚不明确
    struct_serial.c_cc[VTIME] = 0; //超时时间.阻塞读操作中不再有数据到来的等待时间.单位取100ms.0无限
    struct_serial.c_cc[VMIN] = 0; //单次接收最小字节数.阻塞读操作中若读取到的数据小于该数值则会等待
    struct_serial.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); //非规范模式
    struct_serial.c_oflag &= ~OPOST;//原始数据输出,二进制通讯一定要设置,否则协议会被驱动插入其他数据
    struct_serial.c_cflag &= ~CRTSCTS;//关闭硬件流控制
    
    struct_serial.c_lflag  &= ~(ICANON | ECHO | ECHOE | ISIG); //这个是串口回显.若不配置则导致向Linux发什么回什么   
    tcflush2(fd, TCIOFLUSH); //配置前立刻清空一次缓冲区
    if(-1 == tcsetattr2(fd, TCSANOW, &struct_serial)){
        LOG_ERROR("set_data_stop_parity tcsetattr error"); //若出错附带打印错误信息
        return -1;
    }
    return 0;
}

int open_serialport()
{
    int fd = open(COM2, O_RDWR | O_NOCTTY ); //| O_NDELAY
    if (fd < 0) {
        LOG_ERROR("failed to open serial port. error:%d, msg:%s", errno, strerror(errno));
        return -1;
    }
    if (set_serial(fd, 5000000, 8, 0, 1)) {
        LOG_ERROR("failed to set serial port attr.");
        return -1;
    }
    return fd;
}

int reset_serialport(int* fd)
{
    if (*fd >=0)
        close(*fd);
    *fd = open_serialport();
    if (*fd < 0) {
        LOG_ERROR("failed to open serialport. error:%d, msg:%s", errno, strerror(errno));
        return -1;
    }
    return 0;
}

int recv_select(int fd, int timeout) //设置阻塞
{
    int res = -1;
    struct timeval select_time; //超时时间结构体

    select_time.tv_sec = timeout; //以s为单位
    select_time.tv_usec = 0; //微妙部分设为0
    fd_set set; //句柄映射数组
    FD_ZERO(&set); //初始化
    FD_SET(fd, &set); //建立句柄和数组的联系
    res = select(fd+1, &set, NULL, NULL, &select_time); //阻塞等待fd文件的可读变化 
    return res;
}

int recv_data(int fd, unsigned char* buff, int len) //接收数据
{
    int index=0, nread = -1;
    int size = len;
    do {
        nread = read(fd, buff+index, size); //读取数据.这里200仅供参考.基本达不到
        if(nread < 0) {
            if (errno == EAGAIN) {
                sleep(1);
                LOG_DEBUG("read eagain.");
                continue;
            }
            LOG_ERROR("failed to write data. error:%d, msg:%s", errno, strerror(errno));
            return -1;
        }
        size -= nread;
        index += nread;
    }while(size > 0 && index < len);
    return len;
}

int send_data(int fd, unsigned char* buff, int len)
{
    int index=0, nwrite= -1;
    int size = len;
    do {
        nwrite = write(fd, buff+index, size);
        if (nwrite < 0) {
            if (errno == EAGAIN) {
                LOG_DEBUG("wirte eagain.");
                sleep(1);
                continue;
            }
            LOG_ERROR("failed to write data. error:%d, msg:%s", errno, strerror(errno));
            return -1;
        }
        size -= nwrite;
        index += nwrite;
    }while(size > 0 && index < len);
    return len;
}

void handle_any(int fd, int cmd)
{
    unsigned char response_data[] = {0xA5, 0x5A, 0x00, 0x06, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00};
    response_data[4] = cmd >> 8 & 0xff;
    response_data[5] = cmd & 0xff;

    if (send_data(fd, response_data, sizeof(response_data)) != sizeof(response_data)){
        LOG_ERROR("reponse cmd0 error");
    }
}

void handle_cmd1(int fd, unsigned char *buff, unsigned short cmd_len)
{
    unsigned char response_data[] = {0xA5, 0x5A, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00};

    for (unsigned short i=0; i < cmd_len; ++i) {
        if (buff[4+i] != 0xAA) {
            LOG_ERROR("recived error cmd1 data");
            response_data[7] = 1;
            break;
        }
    }

    LOG_DEBUG("server handle cmd1. cmd_len:%u, buff:[%X %X %X %X]", 
        cmd_len, buff[0], buff[1], buff[2], buff[3]);
    if (send_data(fd, response_data, sizeof(response_data)) != sizeof(response_data)){
        LOG_ERROR("reponse cmd1 error");
    }
}

void handle_cmd2(int fd, unsigned char *buff)
{
    unsigned int len = 0;
    unsigned char response_data[BUFF_MAX_SIZE] = {0};
    unsigned short count = buff[0] * 256 + buff[1];
    char * msg = response_data + 6;

    LOG_DEBUG("server handle cmd2: count:%u buff:[%X %X %X %X]", 
            count, buff[0], buff[1], buff[2], buff[3]);

    sprintf(msg, "reponse serail port test mesage number: %u", count);
    len = strlen(msg) + 5;
    response_data[0] = 0xA5;
    response_data[1] = 0x5A;
    response_data[2] = ((len >> 8) & 0xff);
    response_data[3] = (len & 0xff);
    response_data[4] = 0x00;
    response_data[5] = 0x02;
    msg[len - 1] = 0;
    msg[len - 2] = 0;
    msg[len - 3] = 0;
    len += 4;

    if (send_data(fd, response_data, len) != len){
        LOG_ERROR("reponse cmd2 error");
    }
}

int run_server()
{
    int fd = -1;
    unsigned int len, cmd;
    unsigned char *cmd_data;
    unsigned char buff[BUFF_MAX_SIZE];

    if (reset_serialport(&fd)) {
        return -1;
    }

    while (1) {
        memset(buff, 0, BUFF_MAX_SIZE);
        if (recv_data(fd, buff, 4) != 4) {
            LOG_ERROR("recv data header error.");
            return -1;
        }

        if (buff[0] != 0xA5 || buff[1] != 0x5A) {
            LOG_ERROR("recv data header error magic:%X %X", buff[0], buff[1]);
            tcflush2(fd, TCIOFLUSH);
            sleep(1);
            continue;
        }
        
        len = buff[2] * 256 + buff[3];
        if (recv_data(fd, buff+4, len) != len) {
            LOG_ERROR("recv data content error.");
            return -1;
        }

        cmd = buff[4] * 256 + buff[5];
        LOG_DEBUG("server handle cmd: %u len:%u buff:[%X %X %X %X %X %X %X %X %X %X]", 
            cmd, len, buff[0], buff[1],buff[2],buff[3],buff[4],buff[5],buff[6],buff[7],buff[8],buff[9]);
        cmd_data = buff + 6;
        switch (cmd) {
        case 0x01:
            handle_cmd1(fd, cmd_data, len - 8);
            break;
        case 0x02:
            handle_cmd2(fd, cmd_data);
            break;
        default:
            handle_any(fd, cmd);
            break;
        }
    }
    close(fd);
    return 0;
}

int run_client()
{
    int fd = -1;
    unsigned int len, cmd;
    unsigned short count = 0;
    unsigned char cmd2_data[] = {0xA5, 0x5A, 0x00, 0x06, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00};
    unsigned char buff[BUFF_MAX_SIZE];

    if (reset_serialport(&fd)) {
        return -1;
    }

    while (1) {
        cmd2_data[6] = ((count >> 8) & 0xff);
        cmd2_data[7] = (count & 0xff);

        if (send_data(fd, cmd2_data, sizeof(cmd2_data)) != sizeof(cmd2_data)) {
            LOG_ERROR("send cmd2 data error.");
            return -1;
        }

        len = recv_select(fd, 3);
        if (len < 0) {
            LOG_ERROR("send cmd2 data error.");
            return -1;
        }
        if (len == 0) {
            LOG_DEBUG("send cmd2 data timeout. count:%u, data:[%X %X %X %X %X %X %X %X %X %X]", 
                count, cmd2_data[0], cmd2_data[1], cmd2_data[2], cmd2_data[3], cmd2_data[4], 
                cmd2_data[5], cmd2_data[6], cmd2_data[7], cmd2_data[8], cmd2_data[9]);
            tcflush2(fd, TCIOFLUSH);
            sleep(1);
            continue;
        }

        memset(buff, 0, BUFF_MAX_SIZE);
        if (recv_data(fd, buff, 4) != 4) {
            LOG_ERROR("recv data header error.");
            return -1;
        }

        if (buff[0] != 0xA5 || buff[1] != 0x5A) {
            LOG_ERROR("recv data header error magic:%X %X", buff[0], buff[1]);
            tcflush2(fd, TCIOFLUSH);
            sleep(1);
            continue;
        }
        
        len = buff[2] * 256 + buff[3];
        if (recv_data(fd, buff+4, len) != len) {
            LOG_ERROR("recv data content error.");
            return -1;
        }
        cmd = buff[4] * 256 + buff[5];
        LOG_DEBUG("client handle cmd: %u len:%u buff:[%X %X %X %X %X %X]", 
            cmd, len, buff[0], buff[1],buff[2],buff[3],buff[4],buff[5]);
        if (cmd != 0x02) {
            LOG_ERROR("recv error cmd:%u.", cmd);
            continue;
        }

        count++;
        //LOG_DEBUG("SERIALPORT READ TEST COUNT:%d", count);
        printf("recv message:%s\n", buff + 6);
    }
    close(fd);
    return 0;
}

int run_client1()
{
    int fd = -1;
    unsigned int len, cmd, reponse;
    unsigned int cmd1_len = 50*1024 + 2 + 2;
    unsigned char cmd1_data[BUFF_MAX_SIZE];
    unsigned char buff[BUFF_MAX_SIZE];

    if (reset_serialport(&fd)) {
        return -1;
    }

    memset(cmd1_data, 0xAA, BUFF_MAX_SIZE);
    cmd1_data[0] = 0xA5;
    cmd1_data[1] = 0x5A;
    cmd1_data[2] = (cmd1_len >> 8) & 0xFF;
    cmd1_data[3] = cmd1_len & 0xFF;;
    cmd1_data[4] = 0;
    cmd1_data[5] = 0x1;
    cmd1_len += 4;
    cmd1_data[cmd1_len - 1] = 0;
    cmd1_data[cmd1_len - 2] = 0;
    
    while (1) {

        if (send_data(fd, cmd1_data, cmd1_len) != cmd1_len) {
            LOG_ERROR("send cmd1 data error.");
            return -1;
        }

        len = recv_select(fd, 3);
        if (len < 0) {
            LOG_ERROR("send cmd1 data error.");
            return -1;
        }
        if (len == 0) {
            LOG_DEBUG("send cmd1 data timeout. data:[%X %X %X %X %X %X %X %X %X %X]", 
                cmd1_data[0], cmd1_data[1], cmd1_data[2], cmd1_data[3], cmd1_data[4], 
                cmd1_data[5], cmd1_data[6], cmd1_data[7], cmd1_data[8], cmd1_data[9]);
            tcflush2(fd, TCIOFLUSH);
            sleep(1);
            continue;
        }

        memset(buff, 0, BUFF_MAX_SIZE);
        if (recv_data(fd, buff, 4) != 4) {
            LOG_ERROR("recv data header error.");
            return -1;
        }

        if (buff[0] != 0xA5 || buff[1] != 0x5A) {
            LOG_ERROR("recv data header error magic:%X %X", buff[0], buff[1]);
            tcflush2(fd, TCIOFLUSH);
            sleep(1);
            continue;
        }
        
        len = buff[2] * 256 + buff[3];
        if (recv_data(fd, buff+4, len) != len) {
            LOG_ERROR("recv data content error.");
            return -1;
        }
        cmd = buff[4] * 256 + buff[5];
        LOG_DEBUG("client handle cmd: %u len:%u buff:[%X %X %X %X %X %X]", 
            cmd, len, buff[0], buff[1],buff[2],buff[3],buff[4],buff[5]);
        if (cmd != 0x01) {
            LOG_ERROR("recv error cmd:%u.", cmd);
            continue;
        }

        reponse = buff[6] * 256 + buff[7];
        if (reponse) {
            printf("cmd1 response message fail. error code:%d\n", reponse);
        } else {
            printf("cmd1 response message ok.\n");
        }
    }
    close(fd);
    return 0;
}

int main(int argc, const char* argv[])
{
    if( argc < 2) {
        printf("mode error.\n\t usage: ./%s mode(0-server, 1-client)\n", argv[0]);
        return -1;
    }

    if (!strncasecmp(argv[1], "0", 1)) {
        printf("run serial port server.\n");
        run_server();
    } else if (!strncasecmp(argv[1], "1", 1)) {
        printf("run serial port client.\n");
        run_client();
    } else {
        printf("run serial port client2.\n");
        run_client1();
    }
    return 0;
}
相关推荐
朝九晚五ฺ1 小时前
【Linux探索学习】第十四弹——进程优先级:深入理解操作系统中的进程优先级
linux·运维·学习
自由的dream1 小时前
Linux的桌面
linux
xiaozhiwise1 小时前
Makefile 之 自动化变量
linux
Swift社区2 小时前
LeetCode - #139 单词拆分
算法·leetcode·职场和发展
Kkooe2 小时前
GitLab|数据迁移
运维·服务器·git
Kent_J_Truman2 小时前
greater<>() 、less<>()及运算符 < 重载在排序和堆中的使用
算法
久醉不在酒2 小时前
MySQL数据库运维及集群搭建
运维·数据库·mysql
IT 青年3 小时前
数据结构 (1)基本概念和术语
数据结构·算法
Dong雨3 小时前
力扣hot100-->栈/单调栈
算法·leetcode·职场和发展