从c++中的串行端口读取不同包大小的数据流的最佳方式

Best way to read data streams with different package sizes from serial port in c++

本文关键字:数据流 包大小 最佳 方式 读取 c++ 串行端口      更新时间:2023-10-16

我正在研究ATMEL传感器板(加速度计和陀螺仪)的固件,并试图在Ubuntu的平台上读取数据。

目前的固件是这样的:

Ubuntu发送一个字符"D",作为响应,固件发回20字节的数据,以"\n"结尾,然后Ubuntu使用serialport_read_until(fd,buff,'\n'),并假设buff[0]为字节零,依此类推。获取频率为200hz。但使用这种方法有时我会收到损坏的价值观,而且效果不好。此外,ubuntu中还有许多"无法在串行端口上写入"错误。

我从ATMEL中找到了一个固件的示例代码,在那里,数据以不同的包发送,并且连续地(无需等待计算机要求),结构如下:

    void adv_data_send_3(uint8_t stream_num, uint32_t timestamp,
        int32_t value0, int32_t value1, int32_t value2)
{
    /* Define packet format with 3 data fields */
    struct {
        adv_data_start_t start;       /* Starting fields of packet */
        adv_data_field_t field [3];   /* 3 data fields */
        adv_data_end_t end;           /* Ending fields of packet */
    } packet;
    /* Construct packet */
    packet.start.header1 = ADV_PKT_HEADER_1;
    packet.start.header2 = ADV_PKT_HEADER_2;
    packet.start.length  = cpu_to_le16(sizeof(packet));
    packet.start.type    = ADV_PKT_DATA;
    packet.start.stream_num = stream_num;
    packet.start.time_stamp = cpu_to_le32(timestamp);
    packet.field[0].value = cpu_to_le32(value0);
    packet.field[1].value = cpu_to_le32(value1);
    packet.field[2].value = cpu_to_le32(value2);
    packet.end.crc = 0x00;  /* Not used */
    packet.end.mark = ADV_PKT_END;
    /* Write packet */
    adv_write_buf((uint8_t *)&packet, sizeof(packet));
}

但我不知道如何连续读取以上述结构发送的数据。

如果这是一个琐碎的问题,我很抱歉。我不是程序员,但我需要解决这个问题,在搜索了几天后,我找不到一个解决方案(我能理解!)。

我在linux中使用的读取功能:

int serialport_read_until(int fd, unsigned char* buf, char until){
char b[1];
int i=0;
do { 
    int n = read(fd, b, 1);  // read a char at a time
    if( n==-1) return -1;    // couldn't read
    if( n==0 ) {
        usleep( 1 * 1000 ); // wait 1 msec try again
        continue;
    }
    buf[i] = b[0]; i++;
} while( b[0] != until );
buf[i] = 0;  // null terminate the string
return 0;}

新的阅读功能:

    // Read the header part
adv_data_start_t start;
serial_read_buf(fd, reinterpret_cast<uint8_t*>(&start), sizeof(start));
// Create a buffer for the data and the end marker
std::vector<uint8_t> data_and_end(start.length - sizeof(start));
// Read the data and end marker
serial_read_buf(fd, data_and_end.data(), data_and_end.size());
// Iterate over the data
size_t num_data_fields = (data_and_end.size() - sizeof(adv_data_end_t)) / sizeof(adv_data_field_t);
adv_data_field_t* fields = reinterpret_cast<adv_data_field_t*>(data_and_end.data());

for (size_t i = 0; i < num_data_fields; i++)
    std::cout << "Field #" << (i + 1) << " = " << fields[i].value << 'n';

从固件发送的数据包:

typedef struct {
uint8_t         header1;        // header bytes - always 0xFF5A
uint8_t         header2;        // header bytes - always 0xFF5A
uint16_t        length;         // packet length (bytes)
uint32_t        time_stamp;     // time stamp (tick count)
    } adv_data_start_t;

typedef struct {
int32_t         value;          // data field value (3 VALUES)
} adv_data_field_t;

 typedef struct {
uint8_t         crc;            // 8-bit checksum
uint8_t         mark;           // 1-byte end-of-packet marker
uint16_t         mark2;           // 2-byte end-of-packet marker (Added to avoid data structure alignment problem)
 } adv_data_end_t;

数据包的长度在数据包的"头"中,所以一次读取头字段(start结构),第二次读取数据和末尾。

如果所有数据包的startend部分都相同(我猜是这样),那么您可以很容易地计算出第二次读取后的数据字段数量。


类似这样的东西:

// Read the header part
adv_data_start_t start;
adv_read_buf(reinterpret_cast<uint8_t*>(&start), sizeof(start));
// Create a buffer for the data and the end marker
std::vector<uint8_t> data_and_end(start.length - sizeof(start));
// Read the data and end marker
adv_read_buf(data_and_end.data(), data_and_end.size());
// Iterate over the data
size_t num_data_fields = (data_and_end.size() - sizeof(adv_data_end_t)) / sizeof(adv_data_field_t);
adv_data_end_t* fields = reinterpret_cast<adv_data_end_t*>(data_and_end.data());
for (size_t i = 0; i < num_data_fields; i++)
    std::cout << "Field #" << (i + 1) << " = " << fields[i] << 'n';

可能的read_buf实现:

// Read `bufsize` bytes into `buffer` from a file descriptor
// Will block until `bufsize` bytes has been read
// Returns -1 on error, or `bufsize` on success
int serial_read_buf(int fd, uint8_t* buffer, const size_t bufsize)
{
    uint8_t* current = buffer;
    size_t remaining = bufsize
    while (remaining > 0)
    {
        ssize_t ret = read(fd, current, remaining);
        if (ret == -1)
            return -1;  // Error
        else if (ret == 0)
        {
            // Note: For some descriptors, this means end-of-file or
            //       connection closed.
            usleep(1000);
        }
        else
        {
            current += ret;  // Advance read-point in buffer
            remaining -= ret;  // Less data remaining to read
        }
    }
    return bufsize;
}