问题陈述
在微控制器上开发其他设备时,我遇到了需要连续记录大量数据的情况。 该设备必须将包含时间戳和每秒100次的六个ADC测量值的数据集保存到SD卡中(我们将此数据集称为数据包),然后以图形形式在计算机上分析此数据。 同时还需要将数据写入SD卡,并通过UART传输数据。 由于存在大量数据,因此该数据应占用的空间尽可能少。 同时,有必要以某种方式分离这些数据包,因为数据以连续流的形式进入。 通过互联网翻遍没什么好,我没有找到,所以决定为它创建我自己的协议和库。
然后他出现了-分组流协议(PSP1N)
由于某些考虑,决定了以下内容:在协议中,将以N个字节组成的数据包传输数据,其中每个字节的第一位分配给数据包同步的起始位的符号(因此称为协议名称),其余的七个位分配给数据。 数据的顺序和大小是事先已知的。
一个例子:
我们为时间戳分配32位,为ADC测量分配60位(6个通道,每个通道10位),总计92位。 由于您可以在一个字节中传输7位有用数据,因此数据包将由14个字节组成(92位/ 7位= 13.14 ...向上舍入为14)。 14字节中有112位信息,其中92位有用数据的起始位属性占用了14位,还有6位未使用的位(我们可以在其中写入更多信息,但为简单起见,我们将不使用它们)。

第7位是起始位的符号(表示数据包的开始),其中6,5,4,3,2,1,0是数据位。
接收方还知道它接收到一个14字节的数据包,其中第一个字节的第一位是起始位(1)(在其余字节中,起始位是0),然后在数据位中依次是时间戳的32位,然后是ADC测量编号的10位。 1,然后是ADC#2的10位,依此类推...
同样,也会根据协议写入SD卡并从中进行读取。 总共,对于一天在SD卡上记录的信息,我们获得115.4 MB的信息(14字节x每秒100次测量x 3600秒x 24小时)。
这样的数据结构仍然很方便,因为将来我们可以从文件中的任何位置选择数据块,然后以图形的形式显示它们,从而不会将整个文件加载到RAM(可能达到数十GB)中。 通过加载必要的程序包,我们还可以方便地滚动显示这些图形。

是时候开始微控制器的软件实现了
我们用C ++编写用于微控制器的库。
为了方便起见,请创建一个类:
class PSP { public: void init(byte startBit, byte* arrayByte, byte sizeArrayByte); void pushData(byte sizeBit, uint32_t value); byte* popData(); protected: byte startBit;
使用初始化方法,我认为一切都很清楚:
void PSP::init(byte startBit, byte* arrayByte, byte sizeArrayByte) { this->startBit = startBit; this->arrayByte = arrayByte; this->sizeArrayByte = sizeArrayByte; }
添加数据的方法更加复杂,这里,通过狡猾的按位操作,我们将数据放置在字节数组中。
void PSP::pushData(byte sizeBit, uint32_t value) { byte free; byte y; int remBit = 0;
获取数据包字节数组的方法:
byte* PSP::popData() { position = 0;
最后,还有一些辅助功能:
总结一下
完成工作的结果是,用于流数据PSP1N的紧凑协议和可从GitHub下载的现成库在这里诞生了。 在此存储库中,您将找到:
- ExampleColsolePSP1N / C#库的使用示例
- PSP1N_CPP /包含用于处理协议的PSP库及其在Arduino上使用的示例
- PSP1N_CSHARP / .NET的协议库
为了演示协议的操作,您可以将草图刷入Arduino,然后在计算机上的ExampleSerialPortRead示例中,通过COM端口从微控制器接收数据。 在那里,这些数据被解密并显示在控制台应用程序中。 我将再说一次用C#编写的用于接收方的库。
测试控制台:

更新(03/31/19):更改了编码和解码算法