MCU寄存器的位运算方式(读写+拼接字节数据)

乐活   2025-02-07 21:57   内蒙古  

我一直想把嵌入式学到精通,学到现在终于是有点感觉了。MCU永远离不开一个个寄存器的bit。而且对于数据来讲也是字节的流转。

我觉得嵌入式精通第一课应该是位运算。

我们对寄存器的操作其实就是两个:读,写。

读取寄存器.提取特定位: 获取寄存器中我们感兴趣的位的状态。
方法:将寄存器值和一个位掩码做与运算。位掩码中,只有我们感兴趣的位为1,其他位为0。这样,与运算的结果就只保留了我们感兴趣的位。

这个东西就像勺子,把我们感兴趣的东西挖走

假设我们要判断一个寄存器的第5位是否为1,位掩码为00100000。将寄存器值和这个掩码做与运算,如果结果的第5位为1,说明原寄存器的第5位也为1。

记忆:看见&就是读取位。

 将提取出的位与一个常数比较。如果相等,说明硬件处于某种状态。

读取到ADC就绪,开始读取

写入寄存器.清除特定位(将寄存器中某一位的值清零)

清除其实是复杂的

最关键一步就是这个取反

将寄存器值和一个取反的位掩码做与运算。取反的位掩码中,我们想要清零的位为0,其他位为1。这样,与运算的结果就会将要清零的位清零。

自己来算一下,我们目标是清除3位置的1.
第三步的时候是读取,可以看到3位置上面是1.
接着对掩码取反,然后与运算。
最后的结构就是第三位为0了。
我们可以这样记,清除的时候,先读取,然后再清楚。也就是多了一个步骤。
上面是读位和清位,下面就是写位。使用或运算|

记住这个开关的样子

就好像是开关的竖

设置特定位:将寄存器中某一位的值设置为1。
方法:将寄存器值和一个位掩码做或运算。位掩码中,我们想要设置为1的位为1,其他位为0。这样,或运算的结果就会将要设置的位设置为1。

置位,直接|

清除的步骤多,要与运算和掩码翻转

读取位为清除步骤的一半儿

请把这个刻在骨子里面。

我们还有一个是这样的

寄存器的操作是连续的,如果这个寄存器很大,我们一位一位的就不好了。可以先都清了,然后直接把值放进去。

它就是一个复合操作:
  1. (~Mask) 取反,生成一个 仅 Mask 指定位为 0,其余位为 1 的掩码
  2. Reg & (~Mask) 先清零 Mask 指定的位
  3. | (Value) 再用 Value 赋值

看这个ADC的看门狗功能


|是或运算,有1为1,所以也就是像加法。
通过 |(按位或)操作,将这两个掩码组合成一个掩码,表示要操作的位区域。

在这里

分了俩半了,各16位

看见了新的运算符号

ADC_WatchdogStruct->ADC_WatchdogVth << 16:ADC_WatchdogStruct->ADC_WatchdogVth 是存储上阈值的变量。
左移 16 位将上阈值移到 AWDTR 寄存器的高 16 位位置(VTH 占用高 16 位)。
ADC_WatchdogStruct->ADC_WatchdogVtl:ADC_WatchdogStruct->ADC_WatchdogVtl 是存储下阈值的变量。
它直接放在 AWDTR 寄存器的低 16 位(VTL 占用低 16 位)。

我们假设这个值是这样的

上阈值左移 16 位:
ADC_WatchdogStruct->ADC_WatchdogVth << 16 // 结果:0x12340000
下阈值(保持原位不动):
ADC_WatchdogStruct->ADC_WatchdogVtl // 结果:0x5678
组合两个值:
(0x12340000 | 0x5678) // 结果:0x12345678
最终,0x12345678 就是写入 AWDTR 寄存器的值。
总结一下:我们很多时候是要多字节操作寄存器的,这里的例子比较极端。
还有一类是从寄存器读取数据来拼接的。

ADS1115

要发送两个字节的数据,先搞个数组:

regData[0] = data >> 8;
将 16 位的 data 右移 8 位,提取高 8 位数据,并将其存储在 regData[0] 中。
regData[1] = data & 0xFF;
将 16 位的 data 与 0xFF 进行与运算,提取低 8 位数据,并将其存储在 regData[1] 中。这样,regData 数组就包含了要写入的 16 位数据的两个字节。
0xFF 的二进制表示为 11111111,即所有位都为 1。
与 0xFF 进行与运算相当于保留 data 的低 8 位,而高位部分由于与 0 相与,结果都为 0。
由于 0xFF 的高 8 位都是 0,(没1肯定算不出来)所以与 data 的高 8 位进行与运算时,结果必定为 0。而 data 的低 8 位与 0xFF 的低 8 位进行与运算,则保留了原来的值。

最后发个数组出去

通过这两个位运算操作,我们可以将一个 16 位的无符号整数拆分成两个 8 位的无符号整数,分别存储在 regData[0] 和 regData[1] 中。这在处理多字节数据时非常常见。

假设 data 的值为 0x1234(二进制为 0001 0010 0011 0100),那么:
regData[0] = data >> 8; 
regData[0] 的值为 0x12(二进制为 0001 0010)。
regData[1] = data & 0xFF; 
regData[1] 的值为 0x34(二进制为 0011 0100
这就是嵌入式,这就是底层。

从MPU6050看传感器原始数据的处理方式-位运算   以往的位运算。

云深之无迹
纵是相见,亦如不见,潇湘泪雨,执念何苦。
 最新文章