CRSF协议负载段数据解析

crsf负载数据每个通道是11位,需要从中摘取出各通道的数据,在以往的做法中通常使用位运算实现。如

ch1 = (buf[0] | buf[1] << 8) & 0x07FF;
ch2 = (buf[1] >> 3 | buf[2] << 5) & 0x07FF;
ch3 = (buf[2] >> 6 | buf[3] << 2 | buf[4] << 10) & 0x07FF;
ch4 = (buf[4] >> 1 | buf[5] << 7) & 0x07FF;

不太优雅。

该仓库的CRSF解析中发现了使用位域强转的方法,很不错。

#define PACKED __attribute__((packed))
typedef struct crsf_header_s
{
    uint8_t device_addr; // from crsf_addr_e
    uint8_t frame_size;  // counts size after this byte, so it must be the payload size + 2 (type and crc)
    uint8_t type;        // from crsf_frame_type_e
    uint8_t data[CRSF_MAX_PAYLOAD_LEN + 1];
} PACKED crsf_header_t;

typedef struct crsf_channels_s
{
    unsigned int ch0 : 11;
    unsigned int ch1 : 11;
    unsigned int ch2 : 11;
    unsigned int ch3 : 11;
    unsigned int ch4 : 11;
    unsigned int ch5 : 11;
    unsigned int ch6 : 11;
    unsigned int ch7 : 11;
    unsigned int ch8 : 11;
    unsigned int ch9 : 11;
    unsigned int ch10 : 11;
    unsigned int ch11 : 11;
    unsigned int ch12 : 11;
    unsigned int ch13 : 11;
    unsigned int ch14 : 11;
    unsigned int ch15 : 11;
} PACKED crsf_channels_t;

crsf_channels_t* ch = (crsf_channels_t*)&(p->data);

_channels[0] = ch->ch0;
_channels[1] = ch->ch1;
_channels[2] = ch->ch2;
_channels[3] = ch->ch3;
_channels[4] = ch->ch4;
_channels[5] = ch->ch5;
_channels[6] = ch->ch6;
_channels[7] = ch->ch7;
_channels[8] = ch->ch8;
_channels[9] = ch->ch9;
_channels[10] = ch->ch10;
_channels[11] = ch->ch11;
_channels[12] = ch->ch12;
_channels[13] = ch->ch13;
_channels[14] = ch->ch14;
_channels[15] = ch->ch15;

但是我是在Visual Studio中开发的,查找了挺久,发现__attribute__((packed))的语法替换的#pragma pack(1)并不能取消struct中位域的对齐,无奈这个优雅的方法用不上了。

于是想自己实现多个通道的解析,但是不想写成手动位运算的方式,故自动实现。

思路就是把所有的数据排起来,找到对应通道所在的位置,然后用0x7FF(11位全是1)的mask把数据取下来,再移位得到通道数据。以下代码

void CrsfSerial::packetChannelsPacked(const crsf_header_t *p)
{
   
    for (int i = 0; i < 16; ++i)
    {
        uint16_t start = (i  * 11) / 8 ;
        uint8_t startBit = (i * 11) % 8;
        uint32_t mask = 0x7ff << startBit;
        uint32_t data = *((uint32_t*) (p->data + start));
        _channels[i] = (data & mask) >> startBit;
    }

    ...
}

其中p->data + start取出对应通道数据最低位所在缓存数组的地址,然后把该数组地址强转成(uint32_t*)类型数组的地址,然后取出元素得到了包含通道数据的uint32数据,然后再将mask移位,取与,再移回来即可。

也可以用联合来做数据转换

typedef union 
{
    uint8_t datain[4];
    uint32_t dataout;
}intExchange;

void CrsfSerial::packetChannelsPacked(const crsf_header_t *p)
{
    intExchange aaa{};
    for (int i = 0; i < 16; ++i)
    {
        uint16_t start = (i  * 11) / 8 ;
        uint8_t startBit = (i * 11) % 8;
        uint32_t mask = 0x7ff << startBit;

        memcpy(aaa.datain, p->data + start, 4);
        uint32_t data = aaa.dataout;
       
        _channels[i] = (data & mask) >> startBit;
    }

    ...
}

至此得到了所有通道的数据,可以开始快乐的做映射了~

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值