C++ 串口通信 Windiws API CRC 循环冗余校验

适用于Windiws平台的上位机通信,利用windows API

可以获取下位机指定类型设备数量, 完整的rcr校验算法,灵活设计AP

当谈到嵌入式系统和串口通信时,C++是一个强大的选择。在这篇博客中,我们将探讨如何使用C++与嵌入式设备进行串口通信,以及如何构建一个简单的串口通信应用程序。

串口通信是一种用于在计算机和外部设备之间传输数据的通信方式。在嵌入式系统中,串口通信通常用于与传感器、执行器和其他外围设备进行通信。

本文介绍了如何使用C++与嵌入式设备进行串口通信,并提供了一个简单的示例应用程序。通过这种方式,您可以轻松地与嵌入式系统进行通信,并控制其行为。串口通信是嵌入式系统中常用的通信方式之一,将有助于您开发更加强大和灵活的嵌入式应用程序。

步骤1:包含必要的头文件

#include "serialport.h"

#include <iostream>

步骤2:初始化串口

SerialPort *d = nullptr;
void Motor_Init() {
    d = new SerialPort();
    // 设置串口参数,如波特率、数据位、校验位等
    d->setBaudRate(SerialPort::Baud115200);
    d->setDataBits(SerialPort::Data8);
    d->setParity(SerialPort::NoParity);
    d->setStopBits(SerialPort::OneStop);
    d->setFlowControl(SerialPort::NoFlowControl);
}

 步骤3:打开串口并发送/接收数据

bool Motor_Open(int index, const signalCount &count, const signalLock &lock = nullptr, void *context = nullptr) {
    // 串口打开及数据处理逻辑
}

bool Motor_SetParam(int index, unsigned char step) {
    // 向下位机发送指令
}

bool Motor_GetParam(int index, bool *lock) {
    // 从下位机获取数据
}

 步骤4:关闭串口

void Motor_UnInit() { delete d; }

 如何定义使用:

        示例应用程序

#include "serialport.h"
#include <iostream>

SerialPort *d = nullptr;

// 定义回调函数
typedef void(*signalCount)(float, void *);

typedef void(*signalLock)(bool, void *);

static inline bool crc16_XMODEM(const unsigned char *data, unsigned char *cs) {
    unsigned short crc = SerialPort::crc16Check(data, 4, 0x1021, 0x0000, 0x0000, false, false);
    unsigned char high = (crc >> 8) & 0xFF;
    unsigned char low = crc & 0xFF;

    if (*cs == high && *(cs + 1) == low)return true;
    *cs = high;
    *(cs + 1) = low;
    return false;
}

// 初始库
void Motor_Init() {
    if(d) return;
    d = new SerialPort();
    d->setBaudRate(SerialPort::Baud115200);
    d->setDataBits(SerialPort::Data8);
    d->setParity(SerialPort::NoParity);
    d->setStopBits(SerialPort::OneStop);
    d->setFlowControl(SerialPort::NoFlowControl);
}

// 反初始
void Motor_UnInit() { delete d; }

/*!
 *  确认 0,4,7 是和下位机相同的才被认可是查找的设备
 *  这个取决于和下位机的协议 如果协议认为 1,2 这里 verify便是1,2
 * @return 有效设备数量
 */
int Motor_Count() {
    static unsigned char define[8] = {0x8e, 0x01, 0x7b, 0x00, 0x0b, 0x1f, 0xae, 0x8e};
    return d->serialCount(define, 8, {0, 4, 7});
}

// 获取串口号
int Motor_Port(int index) { return d->serialPort(index); }

// 串口设备是否打开
bool Motor_IsOpen(int index) { return d->serialIsOpen(index); }

bool Motor_Open(int index, const signalCount &count, const signalLock &lock = nullptr, void *context = nullptr) {
    // 下位机有数据上来时会触发这个函数
    return d->serialOpen(index, [count, lock, context](int port, unsigned char *data) {
        // crc 校验
        if (crc16_XMODEM(data, &data[4])) {
            switch (data[2]) {
                case 0x7b:
                    if (count)
                        count(0x7b, context);
                    break;
                case 0x7c:
                    if (lock)lock(true, context);
                    break;
                default:
                    break;
            }
        }
    }, 8);
}

/*!
 *  向下位机发送指令
 *  这里crc 校验 写入数据不需要下位机的数据 传递 nullptr,只需要对 第0,1,7 是相同的证明下位机收到了这个指令。
 * @param index
 * @param step
 * @return
 */
bool Motor_SetParam(int index, unsigned char step) {
    unsigned char define[8] = {0x8e, 0x01, step, 0x00, 0x00, 0x00, 0x00, 0x00};
    crc16_XMODEM(define, &define[4]);
    return d->serialWrite(index, define, 8, nullptr, {0, 1, 7});
}

/*!
 *  向下位机设备发送指令
 *  crc 校验 需要下位机数据 传递buffer
 * @param index
 * @param lock
 * @return
 */
bool Motor_GetParam(int index, bool *lock) {
    static unsigned char define[8] = {0x8e, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
    unsigned char buffer[8];
    bool result = d->serialWrite(index, define, 8, buffer, {0, 1, 7});
    if (result && crc16_XMODEM(buffer, &buffer[4]))*lock = static_cast<bool >(buffer[2]);
    return result;
}

int main() {

    Motor_Init();
    int count = Motor_Count();

    for (int i = 0; i < count; ++i) {
        std::cout << "Index(" << i << ")" << "-Port(COM" << Motor_Port(i) << ")" << std::endl;
    }
    // 打开索引第0个设备
    int serial_index = 0;

    if (Motor_IsOpen(serial_index)) {
        std::cout << "设备已打开" << std::endl;
        return 0;
    }
    if (Motor_Open(serial_index,
                   [](float count, void *context) {
                       // 如果是C++ 将上下文转成你的对象。注意这里的上下文取决于你传递的上下文指针
                       //  auto *instance = static_cast<MyClass *>(context);
                       // instance->MyFunction(count);
                       std::cout << "count" << count << std::endl;
                   },
                   [](bool lock, void *context) {
                       std::cout << "lock" << lock << std::endl;
                   }, nullptr)//这里传递上下文 如果你是C++ 传递this 指针
            ) {
        std::cout << "设备打开成功!" << std::endl;
    } else {
        std::cout << "设备打开失败!" << std::endl;
        return 0;
    }
    if (Motor_SetParam(serial_index, 0x04)) {
        std::cout << "写入参数成功!" << std::endl;
    } else {
        std::cout << "写入参数失败!" << std::endl;
    }
    bool lock;
    if (Motor_GetParam(serial_index, &lock)) {
        std::cout << "读取参数成功!" << lock << std::endl;
    } else {
        std::cout << "读取参数失败!" << std::endl;
    }
    Motor_UnInit();
    return 0;
}

//serialport.h

#ifndef SOFTPETAL_SERIALPORT_H
#define SOFTPETAL_SERIALPORT_H

#define S_D(Class) \
        private:  \
            Class(const Class &) = delete;\
            Class &operator=(const Class &) = delete;\
        friend class Class##Private ; \
        Class##Private * const d;

#define S_Q(Class) \
        private:     \
            Class##Private(const Class##Private &) = delete; \
            Class##Private(Class##Private &&) = delete;\
            Class##Private &operator=(const Class##Private &) = delete; \
            Class##Private& operator=(Class##Private&&)= delete;\
        friend Class;\
        Class * const q;

#include <functional>

using callbackFunction = std::function<void(int, unsigned char *)>;

class SerialPort {
S_D(SerialPort)
public:
//波特率
    enum BaudRate {
        Baud110 = 110,
        Baud300 = 300,
        Baud600 = 600,
        Baud1200 = 1200,
        Baud2400 = 2400,
        Baud4800 = 4800,
        Baud9600 = 9600,
        Baud14400 = 14400,
        Baud19200 = 19200,
        Baud38400 = 38400,
        Baud56000 = 56000,
        Baud57600 = 57600,
        Baud115200 = 115200,
        Baud128000 = 128000,
        Baud256000 = 256000,
    };
//数据位
    enum DataBits {
        Data5 = 5,
        Data6 = 6,
        Data7 = 7,
        Data8 = 8,
    };
//校验位
    enum Parity {
        NoParity,
        OddParity,
        EvenParity,
        MarkParity,
        SpaceParity,
    };
//停止位
    enum StopBits {
        OneStop,
        OneAndHalfStop,
        TwoStop,
    };
//流控制
    enum FlowControl {
        NoFlowControl,
        HardwareControl,
        SoftwareControl
    };

    /**
     * 计算给定数据的CRC校验值。
     *
     * @param data 指向需要进行CRC校验的数据的指针。
     * @param len 需要进行CRC校验的数据长度。
     * @param polynomial CRC的多项式,用于生成CRC的检查值。
     * @param initial CRC的初始值。
     * @param finalXor CRC计算结束时与之进行异或的值。
     * @param input 指示数据是否是按位输入。如果为true,则数据按位输入;如果为false,则数据按字节输入。
     * @param result 指示CRC的计算结果是否按位返回。如果为true,则结果按位返回;如果为false,则结果按字节返回。
     * @return 返回计算得到的CRC校验值。
     */
    static unsigned char crc8Check(const unsigned char *data, int len,
                                   unsigned char polynomial,
                                   unsigned char initial,
                                   unsigned char finalXor,
                                   bool input, bool result);

    static unsigned short crc16Check(const unsigned char *data, int len,
                                     unsigned short polynomial,
                                     unsigned short initial,
                                     unsigned short finalXor,
                                     bool input, bool result);

    static unsigned int crc32Check(const unsigned char *data, int len,
                                   unsigned int polynomial,
                                   unsigned int initial,
                                   unsigned int finalXor,
                                   bool input, bool result);

#if 0
    static inline bool crc16_XMODEM(const unsigned char *data, unsigned char *cs, unsigned char len) {
        unsigned short crc = crc16Check(data, len, 0x1021, 0x0000, 0x0000, false, false);
        unsigned char high = (crc >> 8) & 0xFF;
        unsigned char low = crc & 0xFF;

        if (*cs == high && *(cs + 1) == low)return true;
        *cs = high;
        *(cs + 1) = low;
        return false;
    }
#endif

public:
    explicit SerialPort();


    ~SerialPort();

    void setBaudRate(BaudRate baud);

    void setDataBits(DataBits data);

    void setParity(Parity parity);

    void setStopBits(StopBits stop);

    void setFlowControl(FlowControl flow);

    /*!
     * 获取指定设备数量
     * @param data 握手数据
     * @param len 数据长度
     * @param verify 对接确认位 比如下位机 第4个和第5个要和 data 相同 才被认可是要查找的设备
     * @param time 发送data后等待下位机回复的时间
     * @return 有效数量
     */
    int serialCount(unsigned char *data, int len,
                    const std::vector<int> &verify = std::vector<int>(),
                    unsigned long time = 30);
    // 获取串口号
    [[nodiscard]] int serialPort(int index) const;
    // 串口是否打开
    [[nodiscard]] bool serialIsOpen(int index) const;
    /*!
     *  打开设备
     * @param index 按照serialCount() 获得的索引打开
     * @param callback 回调函数 打开后下位机有数据会通过这个函数告知
     * @param len 下位机发送的长度
     * @return 是否打开成功
     */
    bool serialOpen(int index, const callbackFunction &callback, int len);
    // 关闭串口
    void serialClose(int index);
    /*!
     * 写入数据
     * @param index 按照serialCount() 获得的索引写入
     * @param order 需要写入的数据指令
     * @param len 数据长度
     * @param buffer 写入后需要下位机的回复,如果不需要 传递 nullptr
     * @param verify 对接确认位 比如下位机 第4个和第5个要和 data 相同 才被认可是下位机确认收到
     * @param reissue 如果没有得到下位机的回复会重发。默认重发3次
     * @param time 发送data后等待下位机恢复时间
     * @return 是否写入成功
     */
    bool serialWrite(int index,
                     unsigned char *order,
                     int len,
                     unsigned char *buffer,
                     const std::vector<int> &verify = std::vector<int>(),
                     int reissue = 3,
                     unsigned long time = 30);

};

/*!
 * crc 校验表
    unsigned char data[] = {0xee, 0x01, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b};

    printf("CRC8 -> %02x\n", SerialPort::crc8Check(data, 22, 0x07, 0x00, 0x00, false, false));
    printf("CRC16_XMODEM -> %02x\n", SerialPort::crc16Check(data, 22, 0x1021, 0x0000, 0x0000, false, false));
    printf("CRC32_MPEG2 -> %02x\n", SerialPort::crc32Check(data, 22, 0x04C11DB7, 0xFFFFFFFF, 0x00000000, false, false));

    Name                 Polynomial Initial    FinalXor   Input Result
    CRC8                 0x07       0x00       0x00       false          false
    CRC8_SAE_J1850       0x1D       0xFF       0xFF       false          false
    CRC8_SAE_J1850_ZERO  0x1D       0x00       0x00       false          false
    CRC8_8H2F            0x2F       0xFF       0xFF       false          false
    CRC8_CDMA2000        0x9B       0xFF       0x00       false          false
    CRC8_DARC            0x39       0x00       0x00       true           true
    CRC8_DVB_S2          0xD5       0x00       0x00       false          false
    CRC8_EBU             0x1D       0xFF       0x00       true           true
    CRC8_ICODE           0x1D       0xFD       0x00       false          false
    CRC8_ITU             0x07       0x00       0x55       false          false
    CRC8_MAXIM           0x31       0x00       0x00       true           true
    CRC8_ROHC            0x07       0xFF       0x00       true           true
    CRC8_WCDMA           0x9B       0x00       0x00       true           true
    CRC16_CCIT_ZERO      0x1021     0x0000     0x0000     false          false
    CRC16_ARC            0x8005     0x0000     0x0000     true           true
    CRC16_AUG_CCITT      0x1021     0x1D0F     0x0000     false          false
    CRC16_BUYPASS        0x8005     0x0000     0x0000     false          false
    CRC16_CCITT_FALSE    0x1021     0xFFFF     0x0000     false          false
    CRC16_CDMA2000       0xC867     0xFFFF     0x0000     false          false
    CRC16_DDS_110        0x8005     0x800D     0x0000     false          false
    CRC16_DECT_R         0x0589     0x0000     0x0001     false          false
    CRC16_DECT_X         0x0589     0x0000     0x0000     false          false
    CRC16_DNP            0x3D65     0x0000     0xFFFF     true           true
    CRC16_EN_13757       0x3D65     0x0000     0xFFFF     false          false
    CRC16_GENIBUS        0x1021     0xFFFF     0xFFFF     false          false
    CRC16_MAXIM          0x8005     0x0000     0xFFFF     true           true
    CRC16_MCRF4XX        0x1021     0xFFFF     0x0000     true           true
    CRC16_RIELLO         0x1021     0xB2AA     0x0000     true           true
    CRC16_T10_DIF        0x8BB7     0x0000     0x0000     false          false
    CRC16_TELEDISK       0xA097     0x0000     0x0000     false          false
    CRC16_TMS37157       0x1021     0x89EC     0x0000     true           true
    CRC16_USB            0x8005     0xFFFF     0xFFFF     true           true
    CRC16_A              0x1021     0xC6C6     0x0000     true           true
    CRC16_KERMIT         0x1021     0x0000     0x0000     true           true
    CRC16_MODBUS         0x8005     0xFFFF     0x0000     true           true
    CRC16_X_25           0x1021     0xFFFF     0xFFFF     true           true
    CRC16_XMODEM         0x1021     0x0000     0x0000     false          false
    CRC32                0x04C11DB7 0xFFFFFFFF 0xFFFFFFFF true           true
    CRC32_BZIP2          0x04C11DB7 0xFFFFFFFF 0xFFFFFFFF false          false
    CRC32_C              0x1EDC6F41 0xFFFFFFFF 0xFFFFFFFF true           true
    CRC32_D              0xA833982B 0xFFFFFFFF 0xFFFFFFFF true           true
    CRC32_MPEG2          0x04C11DB7 0xFFFFFFFF 0x00000000 false          false
    CRC32_POSIX          0x04C11DB7 0x00000000 0xFFFFFFFF false          false
    CRC32_Q              0x814141AB 0x00000000 0x00000000 false          false
    CRC32_JAMCRC         0x04C11DB7 0xFFFFFFFF 0x00000000 true           true
    CRC32_XFER           0x000000AF 0x00000000 0x00000000 false          false
 */
#endif //SOFTPETAL_SERIALPORT_H

// serialport.cpp
#include "serialport.h"
#include <windows.h>
#include <tchar.h>
#include <thread>
#include <iostream>
#include <algorithm>
#include <mutex>
#include <cassert>

class HandleInfo {
    // 串口号
    const int m_HandlePort;
    // 串口句柄
    HANDLE m_Handle{nullptr};
    // 串口是否打开
    bool m_HandleOpen{false};
    // 回调函数
    callbackFunction m_FuncPtr = nullptr;

    std::thread m_Thread;
    bool m_KillThread{false};
    // 线程锁
    std::mutex m_ThreadMutex;


public:
    explicit HandleInfo(int port) : m_HandlePort(port) {};

    ~HandleInfo() { handleClose(); }

    HandleInfo() = delete;

    HandleInfo(const HandleInfo &) = delete;

    HandleInfo(HandleInfo &&) = delete;

    HandleInfo &operator=(const HandleInfo &) = delete;

    HandleInfo &operator=(HandleInfo &&) = delete;

    [[nodiscard]] inline bool handleIsOpen() const { return (m_Handle && m_HandleOpen); }

    [[nodiscard]] int handlePort() const { return m_HandlePort; }

    inline bool handleOpen(HANDLE handle, const callbackFunction &callback, int len) {
        handleClose();
        if (handle) {
            m_FuncPtr = callback;
            m_Handle = handle;
            m_HandleOpen = true;
            m_KillThread = true;
            m_Thread = std::thread(&HandleInfo::processEvent, this, len);
        }
        return handleIsOpen();
    }

    inline bool handleWrite(unsigned char *order,
                            int len,
                            unsigned char *buffer,
                            const std::vector<int> &verify = std::vector<int>(),
                            int reissue = 3,
                            unsigned long time = 30) {
        if (reissue < 1)return false;
        m_ThreadMutex.lock();
        if (handleWrite(m_Handle, order, len)) {
            auto *mandate = new unsigned char[len];
            std::this_thread::sleep_for(std::chrono::milliseconds(time));
            if (handleRead(m_Handle, mandate, len)) {
                bool isVerify = true;
                for (int v: verify) {
                    if (mandate[v] != order[v]) {
                        isVerify = false;
                        break;
                    }
                }
                if (isVerify) {
                    m_ThreadMutex.unlock();
                    if (buffer != nullptr)memcpy(buffer, mandate, len);
                    delete[] mandate;
                    return true;
                }
            }
            delete[] mandate;
        }
        m_ThreadMutex.unlock();
        return handleWrite(order, len, buffer, verify, reissue - 1, time);
    }

    inline void handleClose() {
        m_KillThread = false;
        if (m_Thread.joinable())m_Thread.join();
        CloseHandle(m_Handle);
        m_Handle = nullptr;
        m_HandleOpen = false;
    }

    void processEvent(int len) {
        auto *buffer = new unsigned char[len];
        while (m_KillThread) {
            std::this_thread::sleep_for(std::chrono::milliseconds(1));
            m_ThreadMutex.lock();
            handleRead(m_Handle, buffer, len);
            m_ThreadMutex.unlock();
            // 如果收到的是空数据不发送 否则触发回调函数
            if (buffer[0] == 0x00)continue;
            m_FuncPtr(m_HandlePort, buffer);
        }
        delete[] buffer;
    }


    static inline HANDLE handleOpen(const TCHAR *name,
                                    SerialPort::BaudRate baud,
                                    SerialPort::DataBits data,
                                    SerialPort::Parity parity,
                                    SerialPort::StopBits stop,
                                    SerialPort::FlowControl flow) {
        HANDLE handle = CreateFile(
                name,//串口名
                GENERIC_READ | GENERIC_WRITE, // 读写
                0, //独占方式,串口不支持共享
                nullptr, // 安全属性指针,默认值为NULL
                OPEN_EXISTING, //打开现有的串口文件
                0, //0:同步方式,FILE_FLAG_OVERLAPPED:异步方式
                nullptr);//用于复制文件句柄,默认值为NULL,对串口而言该参数必须置为NULL
        if (handle == INVALID_HANDLE_VALUE) return nullptr;

        // 配置参数
        DCB dcbSerialParams = {0};

        dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
        dcbSerialParams.BaudRate = baud; // 波特率
        dcbSerialParams.ByteSize = data; // 每个字节使用8位
        dcbSerialParams.Parity = parity;      // 无奇偶校验
        dcbSerialParams.StopBits = stop;  // 1个停止位
        dcbSerialParams.fDtrControl = flow; // 控制流

        if (!SetCommState(handle, &dcbSerialParams)) return nullptr;

        // 超时处理,单位:毫秒
        //总超时=时间系数×读或写的字符数+时间常量
        COMMTIMEOUTS TimeOuts;
        TimeOuts.ReadIntervalTimeout = 1; //指定在接收到连续字符之间的最大时间间隔(毫秒),当此时间间隔过去而没有接收到新数据时,读操作将返回。
        TimeOuts.ReadTotalTimeoutMultiplier = 1; //指定每个字符的等待时间的倍率。实际超时时间为该倍率乘以接收到的字符数乘以 ReadTotalTimeoutConstant 的值。
        TimeOuts.ReadTotalTimeoutConstant = 1; //指定在读操作期间的固定超时时间。如果在读取每个字符之间的时间间隔内没有接收到新数据,则此时间将开始递增
        TimeOuts.WriteTotalTimeoutMultiplier = 1; //指定每个字符的等待时间的倍率。实际超时时间为该倍率乘以要发送的字符数乘以 WriteTotalTimeoutConstant 的值
        TimeOuts.WriteTotalTimeoutConstant = 1; //指定在写操作期间的固定超时时间。
        if (!SetCommTimeouts(handle, &TimeOuts)) return nullptr;

        PurgeComm(handle, PURGE_TXCLEAR | PURGE_RXCLEAR);//清空串口缓冲区

        return handle;
    }

    static inline bool handleWrite(HANDLE handle, unsigned char *data, int len) {
        DWORD bytes;
        return WriteFile(handle, data, len, &bytes, nullptr);
    }

    static inline bool handleRead(HANDLE hSerial, unsigned char *data, int len) {
        DWORD bytes;
        std::memset(data, 0, len);
        return ReadFile(hSerial, // 串口句柄
                        data, // 数据缓冲区地址
                        len, // 要读取的最大字节数
                        &bytes, // DWORD*,用于接收成功读取的字节数
                        nullptr); // NULL为同步发送,OVERLAPPED*为异步发送
    }

    static inline unsigned char reverse8(unsigned char data) {
        unsigned char temp = 0;
        for (int i = 0; i < 8; i++) temp |= ((data >> i) & 0x01) << (7 - i);
        return temp;
    };

    static inline unsigned short reverse16(unsigned short data) {
        unsigned short temp = 0;
        for (int i = 0; i < 16; i++) temp |= ((data >> i) & 0x01) << (15 - i);
        return temp;
    };

    static inline unsigned int reverse32(unsigned int data) {
        unsigned int temp = 0;
        for (int i = 0; i < 32; i++) temp |= ((data >> i) & 0x01) << (31 - i);
        return temp;
    };


};

class SerialPortPrivate {

S_Q(SerialPort)

    SerialPort::BaudRate m_BaudRate{SerialPort::Baud115200};
    SerialPort::DataBits m_DataBits{SerialPort::Data8};
    SerialPort::Parity m_Parity{SerialPort::NoParity};
    SerialPort::StopBits m_StopBits{SerialPort::OneStop};
    SerialPort::FlowControl m_FlowControl{SerialPort::NoFlowControl};

    std::vector<HandleInfo *> m_HandleInfos;

    explicit SerialPortPrivate(SerialPort *ptr) : q(ptr) {}

    [[nodiscard]] inline HandleInfo *instances(int index) const {
        assert(index < m_HandleInfos.size() && "Index out of bounds");
        if (index < m_HandleInfos.size())return m_HandleInfos[index];
        return nullptr;
    };
};


unsigned char SerialPort::crc8Check(const unsigned char *data, int len, unsigned char polynomial, unsigned char initial,
                                    unsigned char finalXor, bool input, bool result) {
    unsigned char temp;
    for (; len > 0; len--) {
        temp = *data++;
        if (input) temp = HandleInfo::reverse8(temp);
        initial ^= temp;
        for (int i = 0; i < 8; i++)
            initial = (initial & 0x80) ? (initial << 1) ^ polynomial : initial << 1;
    }
    if (result) initial = HandleInfo::reverse8(initial);

    return initial ^ finalXor;
}

unsigned short
SerialPort::crc16Check(const unsigned char *data, int len, unsigned short polynomial, unsigned short initial,
                       unsigned short finalXor, bool input, bool result) {
    unsigned char temp;
    for (; len > 0; len--) {
        temp = *data++;
        if (input) temp = HandleInfo::reverse8(temp);
        initial ^= (temp << 8);
        for (int i = 0; i < 8; i++)
            initial = (initial & 0x8000) ? (initial << 1) ^ polynomial : initial << 1;
    }
    if (result) initial = HandleInfo::reverse16(initial);

    return initial ^ finalXor;
}

unsigned int SerialPort::crc32Check(const unsigned char *data, int len, unsigned int polynomial, unsigned int initial,
                                    unsigned int finalXor, bool input, bool result) {
    unsigned char temp;
    for (; len > 0; len--) {
        temp = *data++;
        if (input) temp = HandleInfo::reverse8(temp);
        initial ^= (temp << 24);
        for (int i = 0; i < 8; i++) initial = (initial & 0x80000000) ? (initial << 1) ^ polynomial : initial << 1;
    }
    if (result) initial = HandleInfo::reverse32(initial);
    return initial ^ finalXor;
}

SerialPort::SerialPort() : d(new SerialPortPrivate(this)) {}

SerialPort::~SerialPort() {
    for (auto &handle: d->m_HandleInfos)delete handle;
    delete d;
}

void SerialPort::setBaudRate(SerialPort::BaudRate baud) { d->m_BaudRate = baud; }

void SerialPort::setDataBits(SerialPort::DataBits data) { d->m_DataBits = data; }

void SerialPort::setParity(SerialPort::Parity parity) { d->m_Parity = parity; }

void SerialPort::setStopBits(SerialPort::StopBits stop) { d->m_StopBits = stop; }

void SerialPort::setFlowControl(SerialPort::FlowControl flow) { d->m_FlowControl = flow; }


int SerialPort::serialCount(unsigned char *data, int len, const std::vector<int> &verify, unsigned long time) {
    auto *buffer = new unsigned char[len];
    std::vector<HandleInfo *> closed;

    for (int i = 1; i < 256; i++) {
        TCHAR name[256];
        _stprintf_s(name, 256, _T(R"(\\.\COM%d)"), i);
        HANDLE handle = HandleInfo::handleOpen(name,
                                               d->m_BaudRate,
                                               d->m_DataBits,
                                               d->m_Parity,
                                               d->m_StopBits,
                                               d->m_FlowControl);
        if (handle) {
            if (HandleInfo::handleWrite(handle, data, len)) {
                std::this_thread::sleep_for(std::chrono::milliseconds(time));
                if (HandleInfo::handleRead(handle, buffer, len)) {
                    bool isVerify = true;
                    for (int v: verify) {
                        if (buffer[v] != data[v]) {
                            isVerify = false;
                            break;
                        }
                    }
                    if (isVerify) {
#ifdef _DEBUG
                        std::wcout << name << "\t";
                        for (int di = 0; di < len; ++di) printf("%02x ", buffer[di]);
                        printf("\n");
#endif
                        closed.push_back(new HandleInfo(i));
                    }
                }
            }
            CloseHandle(handle);
        }
    }
    delete[]buffer;

    for (auto &info: d->m_HandleInfos) {
        if (info->handleIsOpen()) closed.push_back(info);
        else delete info;
    }

    std::sort(closed.begin(), closed.end(),
              [](HandleInfo *a, HandleInfo *b) {
                  return a->handlePort() < b->handlePort();
              });
    d->m_HandleInfos = closed;
    return static_cast<int>(closed.size());
}


int SerialPort::serialPort(int index) const {
    HandleInfo *d_ptr = d->instances(index);
    if (d_ptr)return d_ptr->handlePort();
    return 0;
}

bool SerialPort::serialIsOpen(int index) const {
    HandleInfo *d_ptr = d->instances(index);
    if (d_ptr)return d_ptr->handleIsOpen();
    return false;
}

bool SerialPort::serialOpen(int index, const callbackFunction &callback, int len) {
    HandleInfo *d_ptr = d->instances(index);
    bool result = false;
    if (d_ptr) {
        TCHAR name[256];
        _stprintf_s(name, 256, _T(R"(\\.\COM%d)"), d_ptr->handlePort());
        HANDLE handle = HandleInfo::handleOpen(name,
                                               d->m_BaudRate,
                                               d->m_DataBits,
                                               d->m_Parity,
                                               d->m_StopBits,
                                               d->m_FlowControl);
        result = d_ptr->handleOpen(handle, callback, len);
    }
    return result;
}

void SerialPort::serialClose(int index) {
    HandleInfo *d_ptr = d->instances(index);
    if (d_ptr) d_ptr->handleClose();
}

bool SerialPort::serialWrite(int index, unsigned char *order, int len,
                             unsigned char *buffer, const std::vector<int> &verify,
                             int reissue, unsigned long time) {
    bool result = false;
    HandleInfo *d_ptr = d->instances(index);
    if (d_ptr) result = d_ptr->handleWrite(order, len, buffer, verify, reissue, time);
    return result;
}

  • 13
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: CRC循环冗余校验)是一种常用的错误检测技术。CRC主要通过附加一个固定长度的校验码到数据中,来检测数据在传输过程中是否发生了错误。在CRC中,校验码是通过对数据进行除法运算得到的。 具体而言,给定一个二进制的数据块D,CRC会附加一个n位的校验码C到数据末尾,形成一个新的数据块D'C。校验码C被设计为使得D'C能够被一个特定的生成多项式G整除,如果在传输过程中发生了错误,这个多项式无法整除D'C,因此可以通过检查余数来判断是否发生了错误。 CRC的关键是选择适当的生成多项式G。常见的生成多项式包括CRC-8、CRC-16和CRC-32等。不同的生成多项式会产生不同长度的校验码,长度越长,检测到错误的可能性越高。 在进行CRC运算时,接收方会将接收到的数据块D'C除以生成多项式G。如果余数为0,则表明没有错误;如果余数不为0,则表明发生了错误。在这种情况下,接收方可以向发送方请求重新发送数据,以确保数据的正确性。 总而言之,CRC是一种通过附加校验码到数据中的方法,可以有效地检测传输过程中的错误。通过选择适当的生成多项式,可以提高CRC的检测能力。 ### 回答2: CRC循环冗余校验)是一种常见的错误检测技术,用于检测数据传输过程中是否发生了错误。它使用多项式除法的原理,将输入数据与生成多项式进行模2除法运算,得到校验码,然后将校验码附加到原始数据中进行传输,接收方再次进行CRC运算并检查校验码是否匹配,从而判断数据是否出现错误。 CRC算法主要基于封闭性和循环性的原则,它将传输的数据看作二进制数,使用一个生成多项式进行除法运算。该生成多项式在CRC算法中是固定的,不同的生成多项式对应不同的CRC校验码。一般情况下,生成多项式选取低于数据位数的最高次项为1,其余项为0。 CRC过程中,首先需要进行数据的比特填充,即将数据位数按照生成多项式的次数进行扩展,扩展的位数是生成多项式的位数减1。然后进行模2除法运算,逐位比较输入数据和生成多项式的对应位,若两位相同,则结果为0,否则为1。运算结束后得到CRC校验码,然后将其附加到原始数据后面,发送给接收方。 接收方收到数据后,同样经过除法运算得到接收到的校验码。如果接收到的校验码与发送方计算的校验码相同,说明数据传输过程中没有发生错误。如果两个校验码不匹配,则表示数据存在错误,需要重传或进行其他处理。 CRC循环冗余校验是一种简单、高效且广泛应用的错误检测技术,它能够检测发生在数据传输过程中的大多数错误,并且具有较低的错误漏检率和错误检测率。在网络通信、存储系统、计算机硬件等领域广泛应用,能够确保数据的完整性和可靠性。 ### 回答3: CRC循环冗余校验)是一种常用的数据校验方法。在计算机通信和存储中,为了确保数据的完整性和准确性,我们经常需要对数据进行校验。CRC是通过对数据进行除法运算来实现的。 CRC校验的基本原理是,将数据看作二进制多项式,然后利用除法运算来计算其余数。具体步骤如下: 1. 将待校验的数据表示为二进制形式,并在最高位补充k个0,其中k为CRC校验码的位数。 2. 选择一个固定的生成多项式G(x),作为除数。该多项式的系数即为CRC校验码的系数。 3. 将生成多项式G(x)左移k位,与待校验数据相异或。 4. 重复步骤3,直到所有数据位都被处理完。 5. 最后所得结果就是校验码,可以将其附加在原始数据后面发送。 6. 接收方根据接收到的数据,再次进行CRC校验。如果余数为0,则认为数据没有出错;反之,则认为数据出错。 CRC可以提供较高的校验效率和误码检测能力,常被应用于计算机网络、存储器、传感器等领域。它的优点是计算简单、校验速度快,并且能够检测到多种错误,包括单比特差错、双比特差错和突发差错等。 总而言之,CRC循环冗余校验是一种常用的数据校验方法,通过除法运算来计算校验码,并能够高效地检测数据的错误。它在通信和存储领域发挥着重要作用,确保了数据的完整性和可靠性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值