blj28

导航

Modbus协议中将bit位组合到字中或者将字中的比特位映射到byte位中

 

 

#include <stdio.h>
#include <stdint.h>
#include <string.h>

/**
* @brief 将字节数组按小端序打包到 uint16_t 数组中
* @param rsp 源字节数组(uint8_t)
* @param offset 起始偏移(从第几个字节开始取)
* @param nb 需要处理的比特位数(决定实际使用多少字节)
* @param dest 目标 uint16_t 数组(调用者保证空间足够)
*/
void pack_bytes_to_uint16(const uint8_t *rsp, int offset, int nb, uint16_t *dest)
{
  // 计算需要多少个字节来容纳 nb 个比特(向上取整)
  int byte_count = (nb + 7) / 8; // 更简洁的写法

  // 先处理完整的两个字节对(每对组成一个 uint16_t)
  int pairs = byte_count / 2; // 完整对的数量
  int src_idx = offset;
  int dst_idx = 0;

  for (int i = 0; i < pairs; i++) {
  // 小端:低字节在前,高字节在后左移8位
    dest[dst_idx++] = rsp[src_idx] | (rsp[src_idx + 1] << 8);
    src_idx += 2;
  }

  // 如果剩余一个字节(byte_count 为奇数),单独处理
  if (byte_count % 2 == 1) {
    dest[dst_idx] = rsp[src_idx]; // 高字节自动补 0
  }
}

void pack_bits(const uint16_t *src, int nbits, uint8_t *dst)
{
    int dst_len = (nbits + 7) / 8;
    memset(dst, 0, dst_len);
    for (int i = 0; i < nbits; i++) {
        int src_idx = i / 16;
        int src_bit = i % 16;
        int bit_val = (src[src_idx] >> src_bit) & 1;
        int dst_idx = i / 8;
        int dst_bit = i % 8;
        if (bit_val) {
            dst[dst_idx] |= (1 << dst_bit);
        }
    }
}

/**
 * @brief 高效的 bit 打包(一次构造一个字节)
 */
void pack_bits_fast(const uint16_t *src, int nbits, uint8_t *dst)
{
    int dst_len = (nbits + 7) / 8;
    int bit_pos = 0;          // 当前处理的全局 bit 索引
    for (int i = 0; i < dst_len; i++) {
        uint8_t byte = 0;
        // 构造当前字节的 8 个 bit
        for (int j = 0; j < 8; j++) {
            if (bit_pos >= nbits) {
                // 剩余的 bit 不足 8 个,高位补 0
                break;
            }
            int src_idx = bit_pos / 16;
            int src_bit = bit_pos % 16;
            int bit_val = (src[src_idx] >> src_bit) & 1;
            if (bit_val) {
                byte |= (1 << j);
            }
            bit_pos++;
        }
        dst[i] = byte;
    }
}

/**
 * @brief 将 uint16_t 数组按小端字节序拆分为 uint8_t 数组
 * @param src    源 uint16_t 数组
 * @param nbits  需要处理的 bit 总数(通常为 16 的倍数,若不是则处理剩余位)
 * @param dst    目标 uint8_t 数组(长度应 ≥ (nbits+7)/8)
 */
void pack_bits_fast2(const uint16_t *src, int nbits, uint8_t *dst)
{
    int full_words = nbits / 16;          // 完整处理的 16 位字个数
    int remaining_bits = nbits % 16;      // 剩余不足 16 位的 bit 数
    int i;

    // 1. 快速处理完整的 16 位字(每个字拆成两个字节)
    for (i = 0; i < full_words; i++) {
        uint16_t word = src[i];
        dst[2 * i]     = word & 0xFF;      // 低字节
        dst[2 * i + 1] = (word >> 8) & 0xFF; // 高字节
    }

    // 2. 处理剩余位(若存在)
    if (remaining_bits > 0) {
        uint16_t word = src[full_words];
        int byte_idx = 2 * full_words;     // 当前写入位置

        if (remaining_bits <= 8) {
            // 剩余位只够填一个字节,只取低 remaining_bits 位
            dst[byte_idx] = word & ((1 << remaining_bits) - 1);
        } else {
            // 剩余位超过 8 位,填满一个字节,再填第二个字节的高位
            dst[byte_idx]     = word & 0xFF;               // 第一个字节取低 8 位
            dst[byte_idx + 1] = (word >> 8) & ((1 << (remaining_bits - 8)) - 1);
        }
    }
}


void print_bits(uint8_t *data, int len)
{
    for (int i = 0; i < len; i++) {
        // 从最高位(bit7)到最低位(bit0)打印
        for (int j = 7; j >= 0; j--) {
            printf("%d", (data[i] >> j) & 1);
        }
        printf(" %x  ",data[i]);
    }
    printf("\n");
}

void print_bits2(uint8_t *data, int len)
{

    for (int i = 0; i < len; i++) {
        for (int j = 0; j < 8; j++) {
            printf("%d", (data[i] >> j) & 1);
        }
        printf(" ");
    }
    printf("\n");
}


int main(void *afgv)
{
    // 示例:源数组有 2 个 uint16_t,共 32 位,但我们只取前 20 位
    uint16_t src[4] = {
        0x1234,  // 二进制: 0001 0010 0011 0100(低到高:0,0,1,0,1,1,0,0,0,1,0,0,1,0,0,0)
        0x5678,   // 二进制: 0101 0110 0111 1000
        0x9876,
        0x5432
    };
    int nbits = 21;
    uint8_t dst[3];  // 20 位需要 3 个字节 (20/8=2余4)

    pack_bits_fast(src, nbits, dst);

    printf("源数组 (十六进制): 0x%04X, 0x%04X\n", src[0], src[1]);
    printf("打包后的字节 (低位在前): ");
    print_bits(dst, 3);

    // 验证:手动计算前 20 位(低到高)
    // src[0] 低16位: 0x1234 -> 位序: 0,0,1,0,1,1,0,0,0,1,0,0,1,0,0,0
    // src[1] 低4位: 0x5678的低4位是 0x8 -> 1000 (bit0=0,bit1=0,bit2=0,bit3=1)
    // 拼成 20 位: 前8位: 0,0,1,0,1,1,0,0 = 0x34? 实际是 0b00101100 -> 0x2C? 等等,我们按低位在前,所以第一个字节对应 bit0-7,应为 0b00101100 = 0x2C,但这里用print_bits显示的是每个字节的位序,低位在前,所以显示符合。

    return 0;
}

 

posted on 2026-09-07 10:20  bailinjun  阅读(3)  评论(0)    收藏  举报