【问题标题】:Read Bytes off a Serial Device (and make sense of them??)从串行设备上读取字节(并理解它们??)
【发布时间】:2012-01-27 16:02:29
【问题描述】:

我正在努力弄清楚如何从串行设备中读取字节,检查校验和,然后将它们转换为我可以实际读取的内容。

我有一个“应该”向我发送各种消息的设备,每个消息都以字节 $83 开始,以字节 $84 结束。倒数第二个字节应该是一个校验和,由 XORign 所有其他值一起生成并进行比较。

返回的实际值应该是字母数字,但我无法确定数据的头部或尾部。我对 C++ 很陌生——我确信这没有帮助。

我已经阅读了几本关于串行编程的指南,但我迷路了。

谁能帮助我、链接我或教我如何从串行设备读取字节,观察 83 美元和 84 美元,然后理解其间的数据?

这里是每条消息的格式:

$FF byte Destination Address
$10 byte Message Length 16 Bytes
$37 byte Message Type
$00 byte Message subtype
BankAngle int -179 to +180
PitchAngle int -90 to +90
YawAngle int -179 to +180
Slip sint -50 to +50
GForce fps 0 to 6G
MISC byte Mode bits
Heading word 0 to 359 
N/A not used
Voltage byte input voltage

这一切都来自 MGL SP-4 AHRS,为了便于使用,我的目标是 Linux 系统,特别是 Ubuntu。我正在使用 GCC 编译器结束 Eclipse CDT 进行开发。

我迷路的地方 我可以将数据读入缓冲区,但之后我对 C++ 的精通不足以理解它,因为它不是 ASCII。我有兴趣学习我需要知道的东西,但我不知道我需要知道什么。

我有 Perl / Java 背景。

【问题讨论】:

  • 如果有人可以向您展示代码以“理解”字节流,作为这个问题的答案,我会感到惊讶。
  • 好的,那么串行协议是什么?你有什么代码(TX/RX)?
  • 您使用的是什么编译器和操作系统?您是否检查过您的设备已连接并使用串行终端仿真器发送预期数据?
  • 你在哪里迷路了?听起来您正在从串行端口获取数据流。您可以在该数据流中发布数据吗?一个常见的错误是波特率、奇偶校验、停止位等错误,因此也要仔细检查这些内容。
  • 消息格式是基于字节的。一旦将字节读入缓冲区(即打开串行设备并调用 read 函数将字节流读入unsigned char 数组),就可以像这样将字节打印为十六进制:for(int i = 0; i < bytes_in_buffer; ++i) { printf("%02x ", buffer[i]); } printf("\n");跨度>

标签: c++ serial-port device


【解决方案1】:

实现这一点将完全取决于您所针对的操作系统和平台。由于您提到的设备在一般用例中安装在飞机内部,因此我假设您的目标不是 Windows 平台,而更有可能是 Linux 或嵌入式系统。有许多资源可用于在此类平台上执行串行 I/O(例如:Serial Programming HOW-TO),您应该查看这些资源。此外,根据设备安装手册(大约在页面中间的here 提供)中的建议,您应该“请参阅 SP-4 OEM 手册以了解消息格式和消息类型选择。”我怀疑您将从该文档中获得最相关和最有用的信息。您可能需要检查制造商是否为您的平台提供了 API,因为这将使您无需实现实际的通信例程。

理解数据而言,一旦您可以从串行接口读取字节,您就可以利用structs 和unions 使访问数据对程序员更友好.对于您提供的粗略消息大纲,这样的内容可能是合适的:

struct _message
{
    uint8_t  DestinationAddress;
    uint8_t  MessageLength;
    uint8_t  MessageType;
    uint8_t  MessageSubtype;
    int32_t  BankAngle; //assuming an int is 32 bits
    int32_t  PitchAngle;
    int32_t  YawAngle;
    sint_t   Slip; //not sure what a 'sint' is
    fps_t    GForce; //likewise 'fps'
    uint8_t  MISC;
    uint16_t Heading; //assuming a word is 16 bits
    uint8_t  Unused[UNUSED_BYTES]; //however many there are
    uintt_t  Voltage;
}

struct myMessage
{
    union
    {
        char raw[MAX_MESSAGE_SIZE]; //sizeof(largest possible message)
        struct _message message;
    }
}

这样,如果您要声明struct myMessage serialData;,您可以将您的消息读入serialData.raw,然后方便地访问其成员(例如serialData.message.DestinationAddress)。

编辑:针对您的编辑,我将提供一个示例,说明如何理解您的数据。此示例假设您只需要担心一种消息类型,但它可以轻松扩展到其他类型。

struct myMessage serialData;
memcpy(serialData.raw, serialDataBuffer, MAX_MESSAGE_SIZE); //copy data from your buffer
if(serialData.message.MessageType == SOME_MESSAGE_TYPE)
{
    //you have usable data here.
    printf("I am a SOME_MESSAGE!\n");
}

现在,假设这些整数类型真的只对数据传输有用,您需要将这些位转换为“可用数据”。假设这些字段之一实际上是编码的浮点数。一种常见的方案是选择位权重(有时也称为分辨率)。我不知道这是否直接适用于您的设备,或者它是否是真正的值,但为了讨论,我们假设YawAngle 字段的分辨率为0.00014 degrees/bit。例如,要将消息 (serialData.message.YawAngle) 中的值从其uint32_t 值转换为double,您可以这样做:

double YawAngleValue = 0.00014 * serialData.message.YawAngle;

...仅此而已。 OEM 手册应该告诉您数据是如何编码的,并且您应该能够从那里弄清楚如何对其进行解码。

现在,假设您要处理两种消息类型。我已经向您展示过的那个,以及理论上的CRITICAL_BITS 消息。要使用我列出的方案添加该类型,您首先要定义 CRITICAL_BITS 结构(可能如下):

struct _critical_bits
{
    uint8_t  DestinationAddress;
    uint8_t  MessageLength;
    uint8_t  MessageType;
    uint8_t  MessageSubtype;
    uint32_t SomeCriticalData;
}

...然后将其添加到 struct myMessage 定义中,如下所示:

struct myMessage
{
    union
    {
        char raw[MAX_MESSAGE_SIZE]; //sizeof(largest possible message)
        struct _message message;
        struct _critical_bits critical_message;
    }
}

...然后您可以像访问其他字段一样访问SomeCriticalData

if(serialData.message.MessageType == CRITICAL_MESSAGE_TYPE)
{
    uint32_t critical_bits = serialData.critical_message.SomeCriticalData;
}

您可以通过阅读structs 找到更多关于其工作原理的信息。请记住,struct myMessage 类型的实例一次只会包含一组有意义的数据。更简单地说,如果serialData 包含CRITICAL_MESSAGE_TYPE 数据,那么serialData.critical_message 中的数据是有效的,但serialData.message 不是——即使语言不会阻止您在请求时访问该数据。

编辑:再举一个例子;要使用您指定的算法计算消息的校验和,您可能需要这样的东西(假设您已经知道消息完全在缓冲区内):

uint8_t calculate_checksum(struct myMessage *data)
{
    uint8_t number_bytes = data->message.MessageLength;
    uint8_t checksum = 0;
    int i;
    for(i=0; i<number_bytes; ++i)
    {
        //this performs a XOR with checksum and the byte
        //in the message at offset i
        checksum ^= data->raw[i];
    }
    return checksum;
}

您可能需要针对未包含的字节调整该函数,检查以确保 data != NULL 等,但它应该可以帮助您入门。

【讨论】:

    猜你喜欢
    • 2018-07-17
    • 1970-01-01
    • 2021-01-11
    • 1970-01-01
    • 1970-01-01
    • 2015-05-27
    • 1970-01-01
    • 2013-03-22
    • 1970-01-01
    相关资源
    最近更新 更多