我一直想把嵌入式学到精通,学到现在终于是有点感觉了。MCU永远离不开一个个寄存器的bit。而且对于数据来讲也是字节的流转。
我觉得嵌入式精通第一课应该是位运算。
我们对寄存器的操作其实就是两个:读,写。
读取寄存器.提取特定位: 获取寄存器中我们感兴趣的位的状态。方法:将寄存器值和一个位掩码做与运算。位掩码中,只有我们感兴趣的位为1,其他位为0。这样,与运算的结果就只保留了我们感兴趣的位。
这个东西就像勺子,把我们感兴趣的东西挖走
假设我们要判断一个寄存器的第5位是否为1,位掩码为00100000。将寄存器值和这个掩码做与运算,如果结果的第5位为1,说明原寄存器的第5位也为1。记忆:看见&就是读取位。
将提取出的位与一个常数比较。如果相等,说明硬件处于某种状态。
读取到ADC就绪,开始读取
写入寄存器.清除特定位(将寄存器中某一位的值清零):
清除其实是复杂的

最关键一步就是这个取反
将寄存器值和一个取反的位掩码做与运算。取反的位掩码中,我们想要清零的位为0,其他位为1。这样,与运算的结果就会将要清零的位清零。
我们可以这样记,清除的时候,先读取,然后再清楚。也就是多了一个步骤。
记住这个开关的样子

就好像是开关的竖
方法:将寄存器值和一个位掩码做或运算。位掩码中,我们想要设置为1的位为1,其他位为0。这样,或运算的结果就会将要设置的位设置为1。
置位,直接|
清除的步骤多,要与运算和掩码翻转
读取位为清除步骤的一半儿
请把这个刻在骨子里面。

我们还有一个是这样的
寄存器的操作是连续的,如果这个寄存器很大,我们一位一位的就不好了。可以先都清了,然后直接把值放进去。
- (~Mask) 取反,生成一个 仅 Mask 指定位为 0,其余位为 1 的掩码
- Reg & (~Mask) 先清零 Mask 指定的位

看这个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 位)。
我们假设这个值是这样的
ADC_WatchdogStruct->ADC_WatchdogVth << 16
ADC_WatchdogStruct->ADC_WatchdogVtl
最终,0x12345678 就是写入 AWDTR 寄存器的值。总结一下:我们很多时候是要多字节操作寄存器的,这里的例子比较极端。
ADS1115
要发送两个字节的数据,先搞个数组:
将 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] 的值为 0x12(二进制为 0001 0010)。regData[1] = data & 0xFF;
regData[1] 的值为 0x34(二进制为 0011 0100)从MPU6050看传感器原始数据的处理方式-位运算 以往的位运算。