Initial import of firmware and host projects
This commit is contained in:
6
KeyBorad/KeyBorad.slnx
Normal file
6
KeyBorad/KeyBorad.slnx
Normal file
@@ -0,0 +1,6 @@
|
||||
<Solution>
|
||||
<Configurations>
|
||||
<Platform Name="x64" />
|
||||
</Configurations>
|
||||
<Project Path="KeyBorad/KeyBorad.vcxproj" Id="175bed2a-ed45-4ddc-a67b-7c4042190634" />
|
||||
</Solution>
|
||||
121
KeyBorad/KeyBorad/COM/Com_Cdc.h
Normal file
121
KeyBorad/KeyBorad/COM/Com_Cdc.h
Normal file
@@ -0,0 +1,121 @@
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QByteArray>
|
||||
#include <QtCore/QtGlobal>
|
||||
|
||||
struct Packet_len
|
||||
{
|
||||
static const quint8 Com_Len_UnKnow = 0;
|
||||
static const quint8 Com_Len_HelloReq = 1;
|
||||
static const quint8 Com_Len_HelloRsp = 9;
|
||||
static const quint8 Com_Len_Bitmap = 29;
|
||||
static const quint8 Com_Len_FunctionKeyEvent = 3;
|
||||
static const quint8 Com_Len_LedState = 1;
|
||||
static const quint8 Com_Len_TimeSync = 16;
|
||||
static const quint8 Com_Len_ThemeRgb = 3;
|
||||
static const quint8 Com_Len_Ack = 1;
|
||||
static const quint8 Com_Len_Error = 2;
|
||||
};
|
||||
|
||||
enum Packet_Type : quint8
|
||||
{
|
||||
Com_Type_UnKnow = 0x00,
|
||||
|
||||
Com_Type_HelloReq = 0x01,
|
||||
Com_Type_HelloRsp = 0x02,
|
||||
|
||||
Com_Type_Bitmap = 0x10,
|
||||
|
||||
Com_Type_FunctionKeyEvent = 0x20,
|
||||
Com_Type_LedState = 0x21,
|
||||
|
||||
Com_Type_TimeSync = 0x30,
|
||||
Com_Type_ThemeRgb = 0x31,
|
||||
|
||||
Com_Type_Ack = 0x7E,
|
||||
Com_Type_Error = 0x7F
|
||||
};
|
||||
|
||||
enum Key_Action : quint8
|
||||
{
|
||||
Key_Action_Release = 0x00,
|
||||
Key_Action_Press = 0x01
|
||||
};
|
||||
|
||||
enum Error_Code : quint8
|
||||
{
|
||||
Error_Code_None = 0x00,
|
||||
Error_Code_UnknownType = 0x01,
|
||||
Error_Code_InvalidLength = 0x02,
|
||||
Error_Code_InvalidParam = 0x03,
|
||||
Error_Code_NotReady = 0x04
|
||||
};
|
||||
|
||||
struct Packet
|
||||
{
|
||||
quint8 Com_Packet_Head1 = 0xAA;
|
||||
quint8 Com_Packet_Head2 = 0x55;
|
||||
|
||||
quint8 len = Packet_len::Com_Len_UnKnow;
|
||||
Packet_Type type = Com_Type_UnKnow;
|
||||
|
||||
QByteArray data;
|
||||
};
|
||||
|
||||
struct Packet_HelloReq
|
||||
{
|
||||
quint8 ProtocolVersion = 0x01;
|
||||
};
|
||||
|
||||
struct Packet_HelloRsp
|
||||
{
|
||||
quint8 ProtocolVersion = 0x01;
|
||||
quint16 VendorId = 0;
|
||||
quint16 ProductId = 0;
|
||||
quint8 FirmwareMajor = 0;
|
||||
quint8 FirmwareMinor = 0;
|
||||
quint16 CapabilityFlags = 0;
|
||||
};
|
||||
|
||||
struct Packet_Bitmap
|
||||
{
|
||||
QByteArray UsageBitmap = QByteArray(Packet_len::Com_Len_Bitmap, 0);
|
||||
};
|
||||
|
||||
struct Packet_FunctionKeyEvent
|
||||
{
|
||||
quint16 Usage = 0;
|
||||
quint8 Action = Key_Action_Release;
|
||||
};
|
||||
|
||||
struct Packet_LedState
|
||||
{
|
||||
quint8 LedMask = 0;
|
||||
};
|
||||
|
||||
struct Packet_TimeSync
|
||||
{
|
||||
quint8 Version = 1;
|
||||
quint8 Flags = 0;
|
||||
quint16 TimezoneMin = 0;
|
||||
quint64 UtcMs = 0;
|
||||
quint32 AccuracyMs = 0;
|
||||
};
|
||||
|
||||
struct Packet_ThemeRgb
|
||||
{
|
||||
quint8 Red = 0;
|
||||
quint8 Green = 0;
|
||||
quint8 Blue = 0;
|
||||
};
|
||||
|
||||
struct Packet_Ack
|
||||
{
|
||||
quint8 AckedType = 0;
|
||||
};
|
||||
|
||||
struct Packet_Error
|
||||
{
|
||||
quint8 ErrorType = 0;
|
||||
quint8 ErrorCode = Error_Code_None;
|
||||
};
|
||||
198
KeyBorad/KeyBorad/COM/Com_CdcDecode.cpp
Normal file
198
KeyBorad/KeyBorad/COM/Com_CdcDecode.cpp
Normal file
@@ -0,0 +1,198 @@
|
||||
#include "COM/Com_CdcDecode.h"
|
||||
#include "COM/Com_CdcEncode.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
constexpr quint8 COM_CDC_CONST_PACKET_HEAD1 = 0xAA;
|
||||
constexpr quint8 COM_CDC_CONST_PACKET_HEAD2 = 0x55;
|
||||
constexpr int COM_CDC_CONST_FRAME_OVERHEAD = 5;
|
||||
constexpr int COM_CDC_CONST_MAX_PAYLOAD_LENGTH = 64;
|
||||
|
||||
bool Com_Cdc_Func_IsKnownLengthValid(Packet_Type Type, quint8 DataLength)
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case Com_Type_UnKnow:
|
||||
return DataLength == Packet_len::Com_Len_UnKnow;
|
||||
|
||||
case Com_Type_HelloReq:
|
||||
return DataLength == Packet_len::Com_Len_HelloReq;
|
||||
|
||||
case Com_Type_HelloRsp:
|
||||
return DataLength == Packet_len::Com_Len_HelloRsp;
|
||||
|
||||
case Com_Type_Bitmap:
|
||||
return DataLength == Packet_len::Com_Len_Bitmap;
|
||||
|
||||
case Com_Type_FunctionKeyEvent:
|
||||
return DataLength == Packet_len::Com_Len_FunctionKeyEvent;
|
||||
|
||||
case Com_Type_LedState:
|
||||
return DataLength == Packet_len::Com_Len_LedState;
|
||||
|
||||
case Com_Type_TimeSync:
|
||||
return DataLength == Packet_len::Com_Len_TimeSync;
|
||||
|
||||
case Com_Type_ThemeRgb:
|
||||
return DataLength == Packet_len::Com_Len_ThemeRgb;
|
||||
|
||||
case Com_Type_Ack:
|
||||
return DataLength == Packet_len::Com_Len_Ack;
|
||||
|
||||
case Com_Type_Error:
|
||||
return DataLength == Packet_len::Com_Len_Error;
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool Com_Cdc_Func_ParseFrameAtStart(const QByteArray& ByteArray, Packet* p_Packet)
|
||||
{
|
||||
if ((p_Packet == nullptr) || (ByteArray.size() < COM_CDC_CONST_FRAME_OVERHEAD))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (static_cast<quint8>(ByteArray.at(0)) != COM_CDC_CONST_PACKET_HEAD1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (static_cast<quint8>(ByteArray.at(1)) != COM_CDC_CONST_PACKET_HEAD2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const quint8 DataLength = static_cast<quint8>(ByteArray.at(2));
|
||||
if (DataLength > COM_CDC_CONST_MAX_PAYLOAD_LENGTH)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const Packet_Type Type =
|
||||
static_cast<Packet_Type>(static_cast<quint8>(ByteArray.at(3)));
|
||||
if (!Com_Cdc_Func_IsKnownLengthValid(Type, DataLength))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const int FrameLength = COM_CDC_CONST_FRAME_OVERHEAD + static_cast<int>(DataLength);
|
||||
if (ByteArray.size() != FrameLength)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const QByteArray Body = ByteArray.left(FrameLength - 1);
|
||||
const quint8 Checksum = Com_Cdc_Func_CalcChecksum(Body);
|
||||
const quint8 ChecksumRx = static_cast<quint8>(ByteArray.at(FrameLength - 1));
|
||||
if (Checksum != ChecksumRx)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
p_Packet->Com_Packet_Head1 = COM_CDC_CONST_PACKET_HEAD1;
|
||||
p_Packet->Com_Packet_Head2 = COM_CDC_CONST_PACKET_HEAD2;
|
||||
p_Packet->len = DataLength;
|
||||
p_Packet->type = Type;
|
||||
p_Packet->data = ByteArray.mid(4, DataLength);
|
||||
return true;
|
||||
}
|
||||
|
||||
int Com_Cdc_Func_FindHead(const QByteArray& ByteArray)
|
||||
{
|
||||
for (int Index = 0; Index + 1 < ByteArray.size(); ++Index)
|
||||
{
|
||||
if ((static_cast<quint8>(ByteArray.at(Index)) == COM_CDC_CONST_PACKET_HEAD1) &&
|
||||
(static_cast<quint8>(ByteArray.at(Index + 1)) == COM_CDC_CONST_PACKET_HEAD2))
|
||||
{
|
||||
return Index;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Com_Cdc_Func_KeepTailForNextScan(QByteArray* p_Buffer)
|
||||
{
|
||||
if (!p_Buffer->isEmpty() &&
|
||||
(static_cast<quint8>(p_Buffer->at(p_Buffer->size() - 1)) == COM_CDC_CONST_PACKET_HEAD1))
|
||||
{
|
||||
*p_Buffer = QByteArray(1, static_cast<char>(COM_CDC_CONST_PACKET_HEAD1));
|
||||
return;
|
||||
}
|
||||
|
||||
p_Buffer->clear();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool Com_Cdc_Func_ParseFrame(const QByteArray& ByteArray, Packet* p_Packet)
|
||||
{
|
||||
return Com_Cdc_Func_ParseFrameAtStart(ByteArray, p_Packet);
|
||||
}
|
||||
|
||||
bool Com_Cdc_Func_TryTakeFrame(QByteArray* p_Buffer, Packet* p_Packet)
|
||||
{
|
||||
if ((p_Buffer == nullptr) || (p_Packet == nullptr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (p_Buffer->size() < 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const int HeadIndex = Com_Cdc_Func_FindHead(*p_Buffer);
|
||||
if (HeadIndex < 0)
|
||||
{
|
||||
Com_Cdc_Func_KeepTailForNextScan(p_Buffer);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (HeadIndex > 0)
|
||||
{
|
||||
p_Buffer->remove(0, HeadIndex);
|
||||
}
|
||||
|
||||
if (p_Buffer->size() < 4)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const quint8 DataLength = static_cast<quint8>(p_Buffer->at(2));
|
||||
if (DataLength > COM_CDC_CONST_MAX_PAYLOAD_LENGTH)
|
||||
{
|
||||
p_Buffer->remove(0, 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
const Packet_Type Type =
|
||||
static_cast<Packet_Type>(static_cast<quint8>(p_Buffer->at(3)));
|
||||
if (!Com_Cdc_Func_IsKnownLengthValid(Type, DataLength))
|
||||
{
|
||||
p_Buffer->remove(0, 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
const int FrameLength = COM_CDC_CONST_FRAME_OVERHEAD + static_cast<int>(DataLength);
|
||||
if (p_Buffer->size() < FrameLength)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const QByteArray FrameBytes = p_Buffer->left(FrameLength);
|
||||
if (!Com_Cdc_Func_ParseFrameAtStart(FrameBytes, p_Packet))
|
||||
{
|
||||
p_Buffer->remove(0, 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
p_Buffer->remove(0, FrameLength);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
8
KeyBorad/KeyBorad/COM/Com_CdcDecode.h
Normal file
8
KeyBorad/KeyBorad/COM/Com_CdcDecode.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QByteArray>
|
||||
|
||||
#include "COM/Com_Cdc.h"
|
||||
|
||||
bool Com_Cdc_Func_ParseFrame(const QByteArray& ByteArray, Packet* p_Packet);
|
||||
bool Com_Cdc_Func_TryTakeFrame(QByteArray* p_Buffer, Packet* p_Packet);
|
||||
159
KeyBorad/KeyBorad/COM/Com_CdcEncode.cpp
Normal file
159
KeyBorad/KeyBorad/COM/Com_CdcEncode.cpp
Normal file
@@ -0,0 +1,159 @@
|
||||
#include "COM/Com_CdcEncode.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
Packet Com_Cdc_Func_MakePacket(Packet_Type Type, const QByteArray& Data)
|
||||
{
|
||||
Packet PacketData;
|
||||
PacketData.type = Type;
|
||||
PacketData.len = static_cast<quint8>(Data.size());
|
||||
PacketData.data = Data;
|
||||
return PacketData;
|
||||
}
|
||||
|
||||
void Com_Cdc_Func_WriteLe16(QByteArray* p_Data, int Offset, quint16 Value)
|
||||
{
|
||||
(*p_Data)[Offset] = static_cast<char>(Value & 0x00FFU);
|
||||
(*p_Data)[Offset + 1] = static_cast<char>((Value >> 8) & 0x00FFU);
|
||||
}
|
||||
|
||||
void Com_Cdc_Func_WriteLe32(QByteArray* p_Data, int Offset, quint32 Value)
|
||||
{
|
||||
for (int Index = 0; Index < 4; ++Index)
|
||||
{
|
||||
(*p_Data)[Offset + Index] = static_cast<char>((Value >> (Index * 8)) & 0xFFU);
|
||||
}
|
||||
}
|
||||
|
||||
void Com_Cdc_Func_WriteLe64(QByteArray* p_Data, int Offset, quint64 Value)
|
||||
{
|
||||
for (int Index = 0; Index < 8; ++Index)
|
||||
{
|
||||
(*p_Data)[Offset + Index] = static_cast<char>((Value >> (Index * 8)) & 0xFFULL);
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray Com_Cdc_Func_FitPayload(const QByteArray& Data, int ExpectedLength)
|
||||
{
|
||||
QByteArray Payload = Data.left(ExpectedLength);
|
||||
if (Payload.size() < ExpectedLength)
|
||||
{
|
||||
Payload.append(ExpectedLength - Payload.size(), 0);
|
||||
}
|
||||
return Payload;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
quint8 Com_Cdc_Func_CalcChecksum(const QByteArray& ByteArray)
|
||||
{
|
||||
quint8 Checksum = 0;
|
||||
|
||||
for (int Index = 0; Index < ByteArray.size(); ++Index)
|
||||
{
|
||||
const quint8 ByteValue = static_cast<quint8>(ByteArray.at(Index));
|
||||
Checksum = static_cast<quint8>(Checksum ^ ByteValue);
|
||||
}
|
||||
|
||||
return Checksum;
|
||||
}
|
||||
|
||||
QByteArray Com_Cdc_Func_BuildFrame(const Packet& PacketData)
|
||||
{
|
||||
if (PacketData.data.size() > 0xFF)
|
||||
{
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
QByteArray ByteArray;
|
||||
const quint8 DataLength = static_cast<quint8>(PacketData.data.size());
|
||||
|
||||
ByteArray.append(static_cast<char>(PacketData.Com_Packet_Head1));
|
||||
ByteArray.append(static_cast<char>(PacketData.Com_Packet_Head2));
|
||||
ByteArray.append(static_cast<char>(DataLength));
|
||||
ByteArray.append(static_cast<char>(PacketData.type));
|
||||
ByteArray.append(PacketData.data);
|
||||
|
||||
const quint8 Checksum = Com_Cdc_Func_CalcChecksum(ByteArray);
|
||||
ByteArray.append(static_cast<char>(Checksum));
|
||||
return ByteArray;
|
||||
}
|
||||
|
||||
QByteArray Com_Cdc_Func_BuildHelloReq(const Packet_HelloReq& PacketData)
|
||||
{
|
||||
QByteArray Payload(Packet_len::Com_Len_HelloReq, 0);
|
||||
Payload[0] = static_cast<char>(PacketData.ProtocolVersion);
|
||||
return Com_Cdc_Func_BuildFrame(Com_Cdc_Func_MakePacket(Com_Type_HelloReq, Payload));
|
||||
}
|
||||
|
||||
QByteArray Com_Cdc_Func_BuildHelloRsp(const Packet_HelloRsp& PacketData)
|
||||
{
|
||||
QByteArray Payload(Packet_len::Com_Len_HelloRsp, 0);
|
||||
Payload[0] = static_cast<char>(PacketData.ProtocolVersion);
|
||||
Com_Cdc_Func_WriteLe16(&Payload, 1, PacketData.VendorId);
|
||||
Com_Cdc_Func_WriteLe16(&Payload, 3, PacketData.ProductId);
|
||||
Payload[5] = static_cast<char>(PacketData.FirmwareMajor);
|
||||
Payload[6] = static_cast<char>(PacketData.FirmwareMinor);
|
||||
Com_Cdc_Func_WriteLe16(&Payload, 7, PacketData.CapabilityFlags);
|
||||
return Com_Cdc_Func_BuildFrame(Com_Cdc_Func_MakePacket(Com_Type_HelloRsp, Payload));
|
||||
}
|
||||
|
||||
QByteArray Com_Cdc_Func_BuildBitmap(const Packet_Bitmap& PacketData)
|
||||
{
|
||||
return Com_Cdc_Func_BuildFrame(
|
||||
Com_Cdc_Func_MakePacket(
|
||||
Com_Type_Bitmap,
|
||||
Com_Cdc_Func_FitPayload(PacketData.UsageBitmap, Packet_len::Com_Len_Bitmap)));
|
||||
}
|
||||
|
||||
QByteArray Com_Cdc_Func_BuildFunctionKeyEvent(const Packet_FunctionKeyEvent& PacketData)
|
||||
{
|
||||
QByteArray Payload(Packet_len::Com_Len_FunctionKeyEvent, 0);
|
||||
Com_Cdc_Func_WriteLe16(&Payload, 0, PacketData.Usage);
|
||||
Payload[2] = static_cast<char>(PacketData.Action);
|
||||
return Com_Cdc_Func_BuildFrame(
|
||||
Com_Cdc_Func_MakePacket(Com_Type_FunctionKeyEvent, Payload));
|
||||
}
|
||||
|
||||
QByteArray Com_Cdc_Func_BuildLedState(const Packet_LedState& PacketData)
|
||||
{
|
||||
QByteArray Payload(Packet_len::Com_Len_LedState, 0);
|
||||
Payload[0] = static_cast<char>(PacketData.LedMask);
|
||||
return Com_Cdc_Func_BuildFrame(Com_Cdc_Func_MakePacket(Com_Type_LedState, Payload));
|
||||
}
|
||||
|
||||
QByteArray Com_Cdc_Func_BuildTimeSync(const Packet_TimeSync& PacketData)
|
||||
{
|
||||
QByteArray Payload(Packet_len::Com_Len_TimeSync, 0);
|
||||
Payload[0] = static_cast<char>(PacketData.Version);
|
||||
Payload[1] = static_cast<char>(PacketData.Flags);
|
||||
Com_Cdc_Func_WriteLe16(&Payload, 2, PacketData.TimezoneMin);
|
||||
Com_Cdc_Func_WriteLe64(&Payload, 4, PacketData.UtcMs);
|
||||
Com_Cdc_Func_WriteLe32(&Payload, 12, PacketData.AccuracyMs);
|
||||
return Com_Cdc_Func_BuildFrame(Com_Cdc_Func_MakePacket(Com_Type_TimeSync, Payload));
|
||||
}
|
||||
|
||||
QByteArray Com_Cdc_Func_BuildThemeRgb(const Packet_ThemeRgb& PacketData)
|
||||
{
|
||||
QByteArray Payload(Packet_len::Com_Len_ThemeRgb, 0);
|
||||
Payload[0] = static_cast<char>(PacketData.Red);
|
||||
Payload[1] = static_cast<char>(PacketData.Green);
|
||||
Payload[2] = static_cast<char>(PacketData.Blue);
|
||||
return Com_Cdc_Func_BuildFrame(Com_Cdc_Func_MakePacket(Com_Type_ThemeRgb, Payload));
|
||||
}
|
||||
|
||||
QByteArray Com_Cdc_Func_BuildAck(const Packet_Ack& PacketData)
|
||||
{
|
||||
QByteArray Payload(Packet_len::Com_Len_Ack, 0);
|
||||
Payload[0] = static_cast<char>(PacketData.AckedType);
|
||||
return Com_Cdc_Func_BuildFrame(Com_Cdc_Func_MakePacket(Com_Type_Ack, Payload));
|
||||
}
|
||||
|
||||
QByteArray Com_Cdc_Func_BuildError(const Packet_Error& PacketData)
|
||||
{
|
||||
QByteArray Payload(Packet_len::Com_Len_Error, 0);
|
||||
Payload[0] = static_cast<char>(PacketData.ErrorType);
|
||||
Payload[1] = static_cast<char>(PacketData.ErrorCode);
|
||||
return Com_Cdc_Func_BuildFrame(Com_Cdc_Func_MakePacket(Com_Type_Error, Payload));
|
||||
}
|
||||
19
KeyBorad/KeyBorad/COM/Com_CdcEncode.h
Normal file
19
KeyBorad/KeyBorad/COM/Com_CdcEncode.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QByteArray>
|
||||
|
||||
#include "COM/Com_Cdc.h"
|
||||
|
||||
quint8 Com_Cdc_Func_CalcChecksum(const QByteArray& ByteArray);
|
||||
|
||||
QByteArray Com_Cdc_Func_BuildFrame(const Packet& PacketData);
|
||||
|
||||
QByteArray Com_Cdc_Func_BuildHelloReq(const Packet_HelloReq& PacketData);
|
||||
QByteArray Com_Cdc_Func_BuildHelloRsp(const Packet_HelloRsp& PacketData);
|
||||
QByteArray Com_Cdc_Func_BuildBitmap(const Packet_Bitmap& PacketData);
|
||||
QByteArray Com_Cdc_Func_BuildFunctionKeyEvent(const Packet_FunctionKeyEvent& PacketData);
|
||||
QByteArray Com_Cdc_Func_BuildLedState(const Packet_LedState& PacketData);
|
||||
QByteArray Com_Cdc_Func_BuildTimeSync(const Packet_TimeSync& PacketData);
|
||||
QByteArray Com_Cdc_Func_BuildThemeRgb(const Packet_ThemeRgb& PacketData);
|
||||
QByteArray Com_Cdc_Func_BuildAck(const Packet_Ack& PacketData);
|
||||
QByteArray Com_Cdc_Func_BuildError(const Packet_Error& PacketData);
|
||||
227
KeyBorad/KeyBorad/DRI/Dri_Cdc.cpp
Normal file
227
KeyBorad/KeyBorad/DRI/Dri_Cdc.cpp
Normal file
@@ -0,0 +1,227 @@
|
||||
#include "DRI/Dri_Cdc.h"
|
||||
|
||||
#include "COM/Com_Cdc.h"
|
||||
#include "COM/Com_CdcDecode.h"
|
||||
#include "COM/Com_CdcEncode.h"
|
||||
|
||||
#include <QtCore/QElapsedTimer>
|
||||
#include <QtCore/QtGlobal>
|
||||
#include <QtSerialPort/QSerialPort>
|
||||
#include <QtSerialPort/QSerialPortInfo>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
bool Dri_Cdc_Func_TryReadFrame(
|
||||
QSerialPort* p_Serial,
|
||||
QByteArray* p_ReadBuffer,
|
||||
int TimeoutMs,
|
||||
Packet* p_Packet)
|
||||
{
|
||||
if ((p_Serial == nullptr) || (p_ReadBuffer == nullptr) || (p_Packet == nullptr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Com_Cdc_Func_TryTakeFrame(p_ReadBuffer, p_Packet))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
QElapsedTimer Timer;
|
||||
Timer.start();
|
||||
while (Timer.elapsed() < TimeoutMs)
|
||||
{
|
||||
const int RemainingMs = TimeoutMs - static_cast<int>(Timer.elapsed());
|
||||
const int WaitMs = qMin(20, RemainingMs);
|
||||
if (WaitMs <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (!p_Serial->waitForReadyRead(WaitMs))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const QByteArray ReadBytes = p_Serial->readAll();
|
||||
if (!ReadBytes.isEmpty())
|
||||
{
|
||||
p_ReadBuffer->append(ReadBytes);
|
||||
}
|
||||
|
||||
if (Com_Cdc_Func_TryTakeFrame(p_ReadBuffer, p_Packet))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
QVector<Dri_Cdc_Struct_PortInfo> Dri_Cdc_Enum()
|
||||
{
|
||||
QVector<Dri_Cdc_Struct_PortInfo> PortList;
|
||||
|
||||
const QList<QSerialPortInfo> InfoList = QSerialPortInfo::availablePorts();
|
||||
for (int Index = 0; Index < InfoList.size(); ++Index)
|
||||
{
|
||||
const QSerialPortInfo& Info = InfoList.at(Index);
|
||||
|
||||
Dri_Cdc_Struct_PortInfo PortInfo;
|
||||
PortInfo.PortName = Info.portName();
|
||||
PortInfo.Description = Info.description();
|
||||
PortInfo.Manufacturer = Info.manufacturer();
|
||||
PortList.append(PortInfo);
|
||||
}
|
||||
|
||||
return PortList;
|
||||
}
|
||||
|
||||
void Dri_Cdc_Deinit(Dri_Cdc_Struct_Port* p_Port)
|
||||
{
|
||||
if (p_Port->p_Port != nullptr)
|
||||
{
|
||||
if (p_Port->p_Port->isOpen())
|
||||
{
|
||||
p_Port->p_Port->close();
|
||||
}
|
||||
delete p_Port->p_Port;
|
||||
}
|
||||
|
||||
*p_Port = Dri_Cdc_Struct_Port();
|
||||
}
|
||||
|
||||
bool Dri_Cdc_Init(
|
||||
Dri_Cdc_Struct_Port* p_Port,
|
||||
const Dri_Cdc_Struct_InitConfig& Config)
|
||||
{
|
||||
Dri_Cdc_Deinit(p_Port);
|
||||
|
||||
const QVector<Dri_Cdc_Struct_PortInfo> PortList = Dri_Cdc_Enum();
|
||||
if (PortList.isEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Packet_HelloReq HelloReq;
|
||||
const QByteArray HelloReqFrame = Com_Cdc_Func_BuildHelloReq(HelloReq);
|
||||
|
||||
for (int Index = 0; Index < PortList.size(); ++Index)
|
||||
{
|
||||
const Dri_Cdc_Struct_PortInfo& PortInfo = PortList.at(Index);
|
||||
|
||||
QSerialPort* p_Serial = new QSerialPort();
|
||||
p_Serial->setPortName(PortInfo.PortName);
|
||||
p_Serial->setBaudRate(Config.BaudRate);
|
||||
p_Serial->setDataBits(QSerialPort::Data8);
|
||||
p_Serial->setParity(QSerialPort::NoParity);
|
||||
p_Serial->setStopBits(QSerialPort::OneStop);
|
||||
p_Serial->setFlowControl(QSerialPort::NoFlowControl);
|
||||
|
||||
if (!p_Serial->open(QIODevice::ReadWrite))
|
||||
{
|
||||
delete p_Serial;
|
||||
continue;
|
||||
}
|
||||
|
||||
p_Serial->readAll();
|
||||
|
||||
if (p_Serial->write(HelloReqFrame) != HelloReqFrame.size())
|
||||
{
|
||||
p_Serial->close();
|
||||
delete p_Serial;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!p_Serial->waitForBytesWritten(Config.HandshakeTimeoutMs))
|
||||
{
|
||||
p_Serial->close();
|
||||
delete p_Serial;
|
||||
continue;
|
||||
}
|
||||
|
||||
QByteArray HandshakeBuffer;
|
||||
QElapsedTimer Timer;
|
||||
Timer.start();
|
||||
while (Timer.elapsed() < Config.HandshakeTimeoutMs)
|
||||
{
|
||||
Packet PacketData;
|
||||
const int RemainingMs =
|
||||
Config.HandshakeTimeoutMs - static_cast<int>(Timer.elapsed());
|
||||
if (!Dri_Cdc_Func_TryReadFrame(
|
||||
p_Serial,
|
||||
&HandshakeBuffer,
|
||||
qMin(20, RemainingMs),
|
||||
&PacketData))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (PacketData.type == Com_Type_HelloRsp)
|
||||
{
|
||||
p_Port->p_Port = p_Serial;
|
||||
p_Port->IsOpened = true;
|
||||
p_Port->PortName = PortInfo.PortName;
|
||||
p_Port->ReadBuffer = HandshakeBuffer;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
p_Serial->close();
|
||||
delete p_Serial;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Dri_Cdc_Read(
|
||||
Dri_Cdc_Struct_Port* p_Port,
|
||||
QByteArray& ByteArray)
|
||||
{
|
||||
ByteArray.clear();
|
||||
|
||||
if (!p_Port->IsOpened || (p_Port->p_Port == nullptr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Packet PacketData;
|
||||
if (Com_Cdc_Func_TryTakeFrame(&p_Port->ReadBuffer, &PacketData) ||
|
||||
Dri_Cdc_Func_TryReadFrame(p_Port->p_Port, &p_Port->ReadBuffer, 50, &PacketData))
|
||||
{
|
||||
ByteArray = Com_Cdc_Func_BuildFrame(PacketData);
|
||||
return !ByteArray.isEmpty();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Dri_Cdc_Write(
|
||||
Dri_Cdc_Struct_Port* p_Port,
|
||||
const QByteArray& ByteArray)
|
||||
{
|
||||
if (!p_Port->IsOpened || (p_Port->p_Port == nullptr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ByteArray.isEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (p_Port->p_Port->write(ByteArray) != ByteArray.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!p_Port->p_Port->waitForBytesWritten(200))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
40
KeyBorad/KeyBorad/DRI/Dri_Cdc.h
Normal file
40
KeyBorad/KeyBorad/DRI/Dri_Cdc.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QByteArray>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QVector>
|
||||
|
||||
class QSerialPort;
|
||||
|
||||
struct Dri_Cdc_Struct_PortInfo
|
||||
{
|
||||
QString PortName;
|
||||
QString Description;
|
||||
QString Manufacturer;
|
||||
};
|
||||
|
||||
struct Dri_Cdc_Struct_InitConfig
|
||||
{
|
||||
qint32 BaudRate = 115200;
|
||||
int HandshakeTimeoutMs = 200;
|
||||
};
|
||||
|
||||
struct Dri_Cdc_Struct_Port
|
||||
{
|
||||
QSerialPort* p_Port = nullptr;
|
||||
bool IsOpened = false;
|
||||
QString PortName;
|
||||
QByteArray ReadBuffer;
|
||||
};
|
||||
|
||||
QVector<Dri_Cdc_Struct_PortInfo> Dri_Cdc_Enum();
|
||||
void Dri_Cdc_Deinit(Dri_Cdc_Struct_Port* p_Port);
|
||||
bool Dri_Cdc_Init(
|
||||
Dri_Cdc_Struct_Port* p_Port,
|
||||
const Dri_Cdc_Struct_InitConfig& Config);
|
||||
bool Dri_Cdc_Read(
|
||||
Dri_Cdc_Struct_Port* p_Port,
|
||||
QByteArray& ByteArray);
|
||||
bool Dri_Cdc_Write(
|
||||
Dri_Cdc_Struct_Port* p_Port,
|
||||
const QByteArray& ByteArray);
|
||||
4
KeyBorad/KeyBorad/KeyBorad.qrc
Normal file
4
KeyBorad/KeyBorad/KeyBorad.qrc
Normal file
@@ -0,0 +1,4 @@
|
||||
<RCC>
|
||||
<qresource prefix="KeyBorad">
|
||||
</qresource>
|
||||
</RCC>
|
||||
26
KeyBorad/KeyBorad/KeyBorad.ui
Normal file
26
KeyBorad/KeyBorad/KeyBorad.ui
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<UI version="4.0" >
|
||||
<class>KeyBoradClass</class>
|
||||
<widget class="QWidget" name="KeyBoradClass" >
|
||||
<property name="objectName" >
|
||||
<string notr="true">KeyBoradClass</string>
|
||||
</property>
|
||||
<property name="geometry" >
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>600</width>
|
||||
<height>400</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle" >
|
||||
<string>KeyBorad</string>
|
||||
</property>$centralwidget$
|
||||
</widget>
|
||||
<layoutDefault spacing="6" margin="11" />
|
||||
<pixmapfunction></pixmapfunction>
|
||||
<resources>
|
||||
<include location="KeyBorad.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</UI>
|
||||
112
KeyBorad/KeyBorad/KeyBorad.vcxproj
Normal file
112
KeyBorad/KeyBorad/KeyBorad.vcxproj
Normal file
@@ -0,0 +1,112 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="18.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{175BED2A-ED45-4DDC-A67B-7C4042190634}</ProjectGuid>
|
||||
<Keyword>QtVS_v304</Keyword>
|
||||
<WindowsTargetPlatformVersion Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">10.0</WindowsTargetPlatformVersion>
|
||||
<WindowsTargetPlatformVersion Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">10.0</WindowsTargetPlatformVersion>
|
||||
<QtMsBuild Condition="'$(QtMsBuild)'=='' OR !Exists('$(QtMsBuild)\qt.targets')">$(MSBuildProjectDirectory)\QtMsBuild</QtMsBuild>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<PlatformToolset>v145</PlatformToolset>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<PlatformToolset>v145</PlatformToolset>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Condition="Exists('$(QtMsBuild)\qt_defaults.props')">
|
||||
<Import Project="$(QtMsBuild)\qt_defaults.props" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'" Label="QtSettings">
|
||||
<QtInstall>D:\App\Qt\5.13.1\msvc2015_64</QtInstall>
|
||||
<QtModules>core;serialport</QtModules>
|
||||
<QtBuildConfig>debug</QtBuildConfig>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'" Label="QtSettings">
|
||||
<QtInstall>D:\App\Qt\5.13.1\msvc2015_64</QtInstall>
|
||||
<QtModules>core;serialport</QtModules>
|
||||
<QtBuildConfig>release</QtBuildConfig>
|
||||
</PropertyGroup>
|
||||
<Target Name="QtMsBuildNotFound" BeforeTargets="CustomBuild;ClCompile" Condition="!Exists('$(QtMsBuild)\qt.targets') or !Exists('$(QtMsBuild)\qt.props')">
|
||||
<Message Importance="High" Text="QtMsBuild: could not locate qt.targets, qt.props; project may not build correctly." />
|
||||
</Target>
|
||||
<ImportGroup Label="ExtensionSettings" />
|
||||
<ImportGroup Label="Shared" />
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="$(QtMsBuild)\Qt.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="$(QtMsBuild)\Qt.props" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'" Label="Configuration">
|
||||
<ClCompile>
|
||||
<AdditionalOptions>/utf-8 %(AdditionalOptions)</AdditionalOptions>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'" Label="Configuration">
|
||||
<ClCompile>
|
||||
<AdditionalOptions>/utf-8 %(AdditionalOptions)</AdditionalOptions>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="COM\Com_Cdc.h" />
|
||||
<ClCompile Include="COM\Com_CdcEncode.cpp" />
|
||||
<ClInclude Include="COM\Com_CdcEncode.h" />
|
||||
<ClCompile Include="COM\Com_CdcDecode.cpp" />
|
||||
<ClInclude Include="COM\Com_CdcDecode.h" />
|
||||
<ClCompile Include="DRI\Dri_Cdc.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClInclude Include="DRI\Dri_Cdc.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Condition="Exists('$(QtMsBuild)\qt.targets')">
|
||||
<Import Project="$(QtMsBuild)\qt.targets" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
51
KeyBorad/KeyBorad/KeyBorad.vcxproj.filters
Normal file
51
KeyBorad/KeyBorad/KeyBorad.vcxproj.filters
Normal file
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>qml;cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>qrc;rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Form Files">
|
||||
<UniqueIdentifier>{99349809-55BA-4b9d-BF79-8FDBB0286EB3}</UniqueIdentifier>
|
||||
<Extensions>ui</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Translation Files">
|
||||
<UniqueIdentifier>{639EADAA-A684-42e4-A9AD-28FC9BCB8F7C}</UniqueIdentifier>
|
||||
<Extensions>ts</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="COM\Com_Cdc.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="COM\Com_CdcEncode.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClInclude Include="COM\Com_CdcEncode.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="COM\Com_CdcDecode.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClInclude Include="COM\Com_CdcDecode.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="DRI\Dri_Cdc.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="main.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClInclude Include="DRI\Dri_Cdc.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
19
KeyBorad/KeyBorad/main.cpp
Normal file
19
KeyBorad/KeyBorad/main.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
#include "DRI/Dri_Cdc.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
Dri_Cdc_Struct_Port Port;
|
||||
Dri_Cdc_Struct_InitConfig Config;
|
||||
Config.BaudRate = 115200;
|
||||
|
||||
const bool IsInitOk = Dri_Cdc_Init(&Port, Config);
|
||||
qDebug() << "Dri_Cdc_Init =" << IsInitOk;
|
||||
|
||||
Dri_Cdc_Deinit(&Port);
|
||||
return 0;
|
||||
}
|
||||
305
KeyBorad/PROTOBUF_ALIGN_STEPS.md
Normal file
305
KeyBorad/PROTOBUF_ALIGN_STEPS.md
Normal file
@@ -0,0 +1,305 @@
|
||||
# KeyBorad Protobuf 对齐步骤
|
||||
|
||||
这份步骤是按当前工程目录写的,目标是:
|
||||
|
||||
1. 保留当前 CDC 传输帧: `AA 55 + len + type + data + checksum`
|
||||
2. 把 `data` 改成 protobuf 二进制载荷
|
||||
3. 上位机使用 protobuf C++
|
||||
4. 下位机使用 nanopb
|
||||
|
||||
## 1. 先确定边界
|
||||
|
||||
当前工程里三层职责不要打乱:
|
||||
|
||||
- `KeyBorad\KeyBorad\DRI\Dri_Cdc.*`
|
||||
作用: 串口枚举、握手、收发字节流
|
||||
- `KeyBorad\KeyBorad\COM\Com_CdcEncode.*`
|
||||
作用: 把一帧 CDC 包组出来
|
||||
- `KeyBorad\KeyBorad\COM\Com_CdcDecode.*`
|
||||
作用: 从 CDC 字节流缓存里拆出一帧
|
||||
|
||||
引入 protobuf 以后,建议职责变成:
|
||||
|
||||
- `DRI`
|
||||
只管串口
|
||||
- `COM`
|
||||
只管外层帧
|
||||
- `proto`
|
||||
只管 `data` 的结构定义
|
||||
|
||||
也就是说:
|
||||
|
||||
- `type` 继续保留在外层帧里
|
||||
- `data` 改为 protobuf 序列化结果
|
||||
- `len` 改为 protobuf 载荷实际长度
|
||||
|
||||
## 2. 当前已经准备好的文件
|
||||
|
||||
本次已经加了两份协议源文件:
|
||||
|
||||
- `KeyBorad\proto\keyboard.proto`
|
||||
- `KeyBorad\proto\keyboard.options`
|
||||
|
||||
其中:
|
||||
|
||||
- `keyboard.proto`
|
||||
定义了 `HelloReqPayload / HelloRspPayload / BitmapPayload / FunctionKeyEventPayload / TimeSyncPayload` 等消息
|
||||
- `keyboard.options`
|
||||
给 `nanopb` 的 `bytes usage_bitmap` 指定了 `max_size:29`
|
||||
|
||||
## 3. 下载工具
|
||||
|
||||
### 上位机 protobuf
|
||||
|
||||
按 protobuf 官方文档,`protoc` 用 `--cpp_out` 生成 C++ 代码。
|
||||
|
||||
你需要准备:
|
||||
|
||||
- `protoc.exe`
|
||||
- protobuf C++ runtime 头文件和库
|
||||
|
||||
官方参考:
|
||||
|
||||
- protobuf C++ generated code guide
|
||||
- protobuf releases / protoc binary
|
||||
|
||||
### 下位机 nanopb
|
||||
|
||||
你需要准备:
|
||||
|
||||
- `nanopb` runtime
|
||||
`pb.h`, `pb_common.c/h`, `pb_encode.c/h`, `pb_decode.c/h`
|
||||
- `nanopb_generator.py`
|
||||
|
||||
官方参考:
|
||||
|
||||
- nanopb overview
|
||||
- nanopb generator reference
|
||||
|
||||
## 4. 生成代码
|
||||
|
||||
### 4.1 生成上位机 C++ 代码
|
||||
|
||||
在工程根目录 `C:\Users\lst\Desktop\动态链接库版本\20260320_new_keyboard` 下执行:
|
||||
|
||||
```powershell
|
||||
New-Item -ItemType Directory -Force -Path KeyBorad\generated\cpp | Out-Null
|
||||
protoc --proto_path=KeyBorad\proto --cpp_out=KeyBorad\generated\cpp KeyBorad\proto\keyboard.proto
|
||||
```
|
||||
|
||||
预期生成:
|
||||
|
||||
- `KeyBorad\generated\cpp\keyboard.pb.h`
|
||||
- `KeyBorad\generated\cpp\keyboard.pb.cc`
|
||||
|
||||
### 4.2 生成下位机 nanopb 代码
|
||||
|
||||
如果你是 nanopb 源码目录:
|
||||
|
||||
```powershell
|
||||
New-Item -ItemType Directory -Force -Path KeyBorad\generated\nanopb | Out-Null
|
||||
python third_party\nanopb\generator\nanopb_generator.py --output-dir=KeyBorad\generated\nanopb --strip-path KeyBorad\proto\keyboard.proto
|
||||
```
|
||||
|
||||
如果你用的是 nanopb 二进制包,自带 generator,也可以直接换成对应可执行文件。
|
||||
|
||||
预期生成:
|
||||
|
||||
- `KeyBorad\generated\nanopb\keyboard.pb.h`
|
||||
- `KeyBorad\generated\nanopb\keyboard.pb.c`
|
||||
|
||||
## 5. 先做一处关键改造
|
||||
|
||||
### 必改: 不再把 payload 长度当成固定值
|
||||
|
||||
一旦 `data` 改成 protobuf,`HelloRsp / TimeSync / Ack` 的 `data` 长度就不一定再等于今天的固定字节数。
|
||||
|
||||
所以迁移时要先把 `COM` 层规则改成:
|
||||
|
||||
- `len` 表示 protobuf 载荷实际字节数
|
||||
- `Com_CdcDecode` 不再用 `type -> 固定 len` 做强校验
|
||||
- 改成:
|
||||
- 校验帧头
|
||||
- 校验 `len`
|
||||
- 校验 checksum
|
||||
- 可选校验某个 `type` 的最大长度,不再校验固定长度
|
||||
|
||||
建议做法:
|
||||
|
||||
- 先保留 `Packet_Type`
|
||||
- 删除或弱化 `Packet_len` 在解码期的“硬匹配”
|
||||
- `Packet_len` 只保留给旧协议兼容或做上限参考
|
||||
|
||||
## 6. 上位机接入方式
|
||||
|
||||
### 6.1 在 VCXPROJ 里加入生成文件
|
||||
|
||||
把下面文件加入 `KeyBorad\KeyBorad\KeyBorad.vcxproj`:
|
||||
|
||||
- `..\generated\cpp\keyboard.pb.cc`
|
||||
- `..\generated\cpp\keyboard.pb.h`
|
||||
|
||||
并加 include 目录:
|
||||
|
||||
- `KeyBorad\generated\cpp`
|
||||
- protobuf runtime include 目录
|
||||
|
||||
再链接 protobuf C++ runtime 库。
|
||||
|
||||
### 6.2 改造原则
|
||||
|
||||
不要再在 `Com_Cdc.h` 里维护两份字段和 `data`。
|
||||
|
||||
建议改成两层:
|
||||
|
||||
- 原始帧:
|
||||
`Packet`
|
||||
- protobuf payload:
|
||||
`keyboard::cdc::HelloReqPayload` 等生成类
|
||||
|
||||
### 6.3 发送流程
|
||||
|
||||
以 `HelloReq` 为例:
|
||||
|
||||
1. 创建 `keyboard::cdc::HelloReqPayload`
|
||||
2. 填字段
|
||||
3. `SerializeToString()` / `SerializeToArray()`
|
||||
4. 把序列化结果塞进 `Packet.data`
|
||||
5. `Packet.type = Com_Type_HelloReq`
|
||||
6. 用 `Com_Cdc_Func_BuildFrame()` 打外层包
|
||||
|
||||
伪代码:
|
||||
|
||||
```cpp
|
||||
keyboard::cdc::HelloReqPayload payload;
|
||||
payload.set_protocol_version(1);
|
||||
|
||||
std::string bytes;
|
||||
payload.SerializeToString(&bytes);
|
||||
|
||||
Packet frame;
|
||||
frame.type = Com_Type_HelloReq;
|
||||
frame.data = QByteArray(bytes.data(), static_cast<int>(bytes.size()));
|
||||
|
||||
QByteArray tx = Com_Cdc_Func_BuildFrame(frame);
|
||||
```
|
||||
|
||||
### 6.4 接收流程
|
||||
|
||||
1. `Dri_Cdc_Read()` 读到完整 CDC 帧
|
||||
2. `Com_Cdc_Func_ParseFrame()` 拿到 `Packet`
|
||||
3. 根据 `Packet.type` 选择 protobuf 消息类型
|
||||
4. 对 `Packet.data` 做 `ParseFromArray()`
|
||||
|
||||
伪代码:
|
||||
|
||||
```cpp
|
||||
Packet frame;
|
||||
if (!Com_Cdc_Func_ParseFrame(frameBytes, &frame))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (frame.type == Com_Type_HelloRsp)
|
||||
{
|
||||
keyboard::cdc::HelloRspPayload payload;
|
||||
if (!payload.ParseFromArray(frame.data.constData(), frame.data.size()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 7. 下位机接入方式
|
||||
|
||||
下位机不要碰外层 CDC 帧格式,直接沿用:
|
||||
|
||||
- 帧头
|
||||
- 长度
|
||||
- type
|
||||
- checksum
|
||||
|
||||
只把 `type` 对应的 `data` 改成 nanopb 编解码。
|
||||
|
||||
建议流程:
|
||||
|
||||
1. 串口 ISR / DMA 收满一帧
|
||||
2. 校验 `AA 55 / len / checksum`
|
||||
3. 根据 `type` 选 `keyboard.pb.h` 里的消息描述符
|
||||
4. `pb_decode()` 解 protobuf 载荷
|
||||
5. 业务处理
|
||||
6. 回复时 `pb_encode()` 出 payload,再挂回外层 CDC 帧
|
||||
|
||||
## 8. 推荐迁移顺序
|
||||
|
||||
建议按下面顺序,不要一次全换:
|
||||
|
||||
1. 只提交 `.proto` 和 `.options`
|
||||
2. 上位机先接 `HelloReq / HelloRsp`
|
||||
3. 下位机把 `HelloReq / HelloRsp` 改成 nanopb
|
||||
4. 确认握手通了,再迁移 `Ack / Error`
|
||||
5. 然后迁移 `TimeSync / ThemeRgb`
|
||||
6. 最后迁移 `Bitmap / FunctionKeyEvent`
|
||||
|
||||
原因:
|
||||
|
||||
- `HelloReq / HelloRsp` 字段简单,最适合先打通生成链路
|
||||
- `Bitmap` 有 `bytes[29]`,更适合在 `nanopb` 选项验证完后再切
|
||||
|
||||
## 9. 你当前 COM 文件还能起什么作用
|
||||
|
||||
即使引入 protobuf,当前 `COM` 层仍然非常有价值:
|
||||
|
||||
- `Com_CdcEncode`
|
||||
继续负责外层帧打包
|
||||
- `Com_CdcDecode`
|
||||
继续负责流式拆包
|
||||
- `Packet_Type`
|
||||
继续作为外层快速分发 ID
|
||||
|
||||
真正被 protobuf 替代的是:
|
||||
|
||||
- 旧的 `data` 手写字段布局
|
||||
- 手工小端拼 payload 的代码
|
||||
|
||||
一句话:
|
||||
|
||||
- `COM` 继续保留
|
||||
- 但 `COM` 不再定义业务 payload 字段
|
||||
- payload 交给 `.proto`
|
||||
|
||||
## 10. 一套最小可复现命令
|
||||
|
||||
假设你已经装好 `protoc` 和 `nanopb`,最小步骤就是:
|
||||
|
||||
```powershell
|
||||
Set-Location C:\Users\lst\Desktop\动态链接库版本\20260320_new_keyboard
|
||||
|
||||
New-Item -ItemType Directory -Force -Path KeyBorad\generated\cpp | Out-Null
|
||||
protoc --proto_path=KeyBorad\proto --cpp_out=KeyBorad\generated\cpp KeyBorad\proto\keyboard.proto
|
||||
|
||||
New-Item -ItemType Directory -Force -Path KeyBorad\generated\nanopb | Out-Null
|
||||
python third_party\nanopb\generator\nanopb_generator.py --output-dir=KeyBorad\generated\nanopb --strip-path KeyBorad\proto\keyboard.proto
|
||||
```
|
||||
|
||||
然后做两边接入:
|
||||
|
||||
1. 上位机 `vcxproj` 加入 `keyboard.pb.cc`
|
||||
2. 下位机工程加入 `keyboard.pb.c`
|
||||
3. 把 `HelloReq / HelloRsp` 的 `data` 改为 protobuf payload
|
||||
4. 把 `Com_CdcDecode` 的固定长度校验去掉
|
||||
|
||||
## 11. 最后一个关键建议
|
||||
|
||||
如果你只是想“共享结构定义”,那就:
|
||||
|
||||
- 保留外层 `type`
|
||||
- 每个 `type` 对应一个 protobuf message
|
||||
|
||||
不要一开始就把外层 `type` 也塞进 protobuf `oneof` 里。
|
||||
|
||||
原因很简单:
|
||||
|
||||
- 外层 `type` 对调试串口抓包很直观
|
||||
- 下位机分发也更省
|
||||
- 迁移成本最小
|
||||
1
KeyBorad/proto/keyboard.options
Normal file
1
KeyBorad/proto/keyboard.options
Normal file
@@ -0,0 +1 @@
|
||||
BitmapPayload.usage_bitmap max_size:29
|
||||
83
KeyBorad/proto/keyboard.proto
Normal file
83
KeyBorad/proto/keyboard.proto
Normal file
@@ -0,0 +1,83 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package keyboard.cdc;
|
||||
|
||||
// The AA55 CDC frame still lives outside protobuf:
|
||||
// [head1][head2][len][type][data][checksum]
|
||||
// `data` is the serialized protobuf payload for the matching `type`.
|
||||
// Keep the outer `type` field for fast dispatch and debugging.
|
||||
|
||||
enum CdcPacketType {
|
||||
CDC_PACKET_TYPE_UNKNOWN = 0;
|
||||
CDC_PACKET_TYPE_HELLO_REQ = 1;
|
||||
CDC_PACKET_TYPE_HELLO_RSP = 2;
|
||||
CDC_PACKET_TYPE_BITMAP = 16;
|
||||
CDC_PACKET_TYPE_FUNCTION_KEY_EVENT = 32;
|
||||
CDC_PACKET_TYPE_LED_STATE = 33;
|
||||
CDC_PACKET_TYPE_TIME_SYNC = 48;
|
||||
CDC_PACKET_TYPE_THEME_RGB = 49;
|
||||
CDC_PACKET_TYPE_ACK = 126;
|
||||
CDC_PACKET_TYPE_ERROR = 127;
|
||||
}
|
||||
|
||||
enum KeyAction {
|
||||
KEY_ACTION_RELEASE = 0;
|
||||
KEY_ACTION_PRESS = 1;
|
||||
}
|
||||
|
||||
enum ErrorCode {
|
||||
ERROR_CODE_NONE = 0;
|
||||
ERROR_CODE_UNKNOWN_TYPE = 1;
|
||||
ERROR_CODE_INVALID_LENGTH = 2;
|
||||
ERROR_CODE_INVALID_PARAM = 3;
|
||||
ERROR_CODE_NOT_READY = 4;
|
||||
}
|
||||
|
||||
message HelloReqPayload {
|
||||
uint32 protocol_version = 1;
|
||||
}
|
||||
|
||||
message HelloRspPayload {
|
||||
uint32 protocol_version = 1;
|
||||
uint32 vendor_id = 2;
|
||||
uint32 product_id = 3;
|
||||
uint32 firmware_major = 4;
|
||||
uint32 firmware_minor = 5;
|
||||
uint32 capability_flags = 6;
|
||||
}
|
||||
|
||||
message BitmapPayload {
|
||||
bytes usage_bitmap = 1;
|
||||
}
|
||||
|
||||
message FunctionKeyEventPayload {
|
||||
uint32 usage = 1;
|
||||
KeyAction action = 2;
|
||||
}
|
||||
|
||||
message LedStatePayload {
|
||||
uint32 led_mask = 1;
|
||||
}
|
||||
|
||||
message TimeSyncPayload {
|
||||
uint32 version = 1;
|
||||
uint32 flags = 2;
|
||||
sint32 timezone_min = 3;
|
||||
fixed64 utc_ms = 4;
|
||||
fixed32 accuracy_ms = 5;
|
||||
}
|
||||
|
||||
message ThemeRgbPayload {
|
||||
uint32 red = 1;
|
||||
uint32 green = 2;
|
||||
uint32 blue = 3;
|
||||
}
|
||||
|
||||
message AckPayload {
|
||||
uint32 acked_type = 1;
|
||||
}
|
||||
|
||||
message ErrorPayload {
|
||||
uint32 error_type = 1;
|
||||
ErrorCode error_code = 2;
|
||||
}
|
||||
Reference in New Issue
Block a user