写在前面
嵌入式当中,使用例如i2c
、spi
等外设时,需要对寄存器中的某些连续位进行写入操作,而不影响其他位,本文来探讨如何实现。
需求
现在有数据要求写入到寄存器,从第 3 位开始,连续写入 4 位的数据。写入的数据为:1100
,我们假设寄存器原始数据为:0110 1100
。
我们定义从第 3 位开始为:uint8_t bits_start = 0x3;
。
写入 4 位数据定义为:uint8_t bits_len = 0x4;
。
寄存器原始数据为:uint8_t data = 0b01101100;
,即:uint8_t data = 0x6C;
。
准备写入的数据为:uint8_t bits_data = 0b1100;
。
注:这里我们在描述寄存器的位时,所用的方式是不同的,注意不要混淆。
- 使用“第 N 位”时,我们从 1 开始计算比特位。
- 使用
bitN
时,我们从 0 开始计算比特位。
例如:
- 自然语言索引二进制比特位。
. 0. 1. 1. 0. 1. 1. 0. 0
第八位 第七位 第六位 第五位 第四位 第三位 第二位 第一位
- 用
BitN
来索引二进制比特位。
. 0 1 1 0. 1 1 0 0
bitN. 7 6 5 4 3 2 1 0
实现
获取掩码
因为我们需要用掩码来对寄存器原始数据相应的位被清除为 0,这里,相应的位是:从第 3 位开始,写入 4 位数据,即:从bit2
到bit5
。
用bits_len
来获取掩码
uint8_t mask = ((1 << bits_len) - 1);
这里 bits_len = 4
让 1 向左移 4 位:
1 << 4. ->. 1 0000,补全 8 位为:0001 0000
将 mask 减去 1:
二进制 0001 0000 转换为十六进制为:0x10
0x10 减去 1 为:0x10 - 0x1 = 0xF
0xF 转换为二进制为: 1111
通过上面代码我们可以获取掩码为:
1111
这里我们得到了长度为bits_len
的掩码。接下来我们将对他移动到对应比特写入的位置。
将掩码 mask 左移 bits_start - 1
位
已知bits_start = 3
,所以需要将 mask
左移3 - 1 = 2
位。
将mask
左移2
位:
0000 1111
0011 1100
我们得到~mask = 0011 1100
。我们已经将掩码移动到了合适的位置,接下来我们将对掩码取反,目的是清除寄存器原始数据中需要修改的比特位。
将掩码mask
取反
~ 0011 1100 =. 1100 0011
我们得到了最终掩码,现在可以用它来清除寄存器原始数据中需要清除的位。
清除写入位
data = 0110 1100
mask = 1100 0011
我们对他们进行按位与计算:
. 0110 1100 (data)
& 1100 0011 (mask)
-------------------
. 0100 0000
解释:
可以看到,寄存器的原始数据为: 0 1 1 0 1 1 0 0 . ^.^ ^.^
我们需要从第 3 位开始写入,连续写 4 位。正如我标记的一样。然而,我们既然需要重新写入这些位的数据,就要清除当前位原来的数据对吧。
0 1 0 0 0 0 0 0
可以看到,我们清除掉相应的位之后,得到的结果和 用掩码来处理 的结果一样。
我们已经得到了处理好的寄存器数据,接下来只需要将bits_data
数据整合到data
中就可以了。
bits_data
左移
我们已知bits_data = 1100
。现在将其左移到用掩码清空的位置。
uint8_t bits_data = 1100;
我们将其左移 (bits_start - 1) 个位
1100 << (bits_start - 1) ->. 1100 << 2 -> 0011 0000
我们得到数据有效位和掩码处理过的位相对应的二进制数据。接下来只需要简单的将bits_data
和data
按位或操作即可。
合并数据
. 0011 0000. (bits_data)
| 0100 0000. (data)
-------------------------
. 0111 0000
我们得到了最终数据,可知直接将其写入到寄存器中,而不对其他位产生影响。
总结
data = 0110 1100. 从寄存器读出来的数据
bits_start = 0x3 从第三位开始
bits_len = 0x4 写入四位的数据
bits_data = 1100 将要写入的数据
data: 0 1 1 0 1 1 0 0
^ ^ ^ ^
第六位 第五位 第四位 第三位
bits_data: 1. 1. 0. 0. 用 bits_data 替换相应位
得:
0111 0000
代码示例
#include <stdint.h>
#include <stdio.h>
void print_binary(uint8_t value) {
char binary_str[10] = {0};
int index = 0;
for (int i = 7; i >= 0; i--) {
binary_str[index++] = (value & (1 << i)) ? '1' : '0';
if (i == 4) {
binary_str[index++] = ' ';
}
}
printf("%s\n", binary_str);
}
void write_bits(uint8_t bits_start, uint8_t bits_len, uint8_t bits_data) {
uint8_t mask = 0x00;
uint8_t final_data = 0x00;
uint8_t data = 0b01101100; // 0x6C
mask = ~(((1 << bits_len) - 1) << (bits_start - 1)); // 取掩码
data &= mask; // 清零写入位
bits_data <<= (bits_start - 1); // 写入数据移动到写入位
bits_data |= data; // 合并数据
printf("bits_data: \t");
print_binary(bits_data);
}
int main(int argc, char *argv[]) {
uint8_t bits_start = 3;
uint8_t bits_len = 4;
uint8_t bits_data = 0b1100; // 0xC
write_bits(bits_start, bits_len, bits_data);
return 0;
}