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; }
浙公网安备 33010602011771号