我一直想把嵌入式学到精通,学到现在终于是有点感觉了。MCU永远离不开一个个寄存器的bit。而且对于数据来讲也是字节的流转。
我觉得嵌入式精通第一课应该是位运算。
我们对寄存器的操作其实就是两个:读,写。
读取寄存器.提取特定位: 获取寄存器中我们感兴趣的位的状态。
方法:将寄存器值和一个位掩码做与运算。位掩码中,只有我们感兴趣的位为1,其他位为0。这样,与运算的结果就只保留了我们感兴趣的位。
这个东西就像勺子,把我们感兴趣的东西挖走
假设我们要判断一个寄存器的第5位是否为1,位掩码为00100000。将寄存器值和这个掩码做与运算,如果结果的第5位为1,说明原寄存器的第5位也为1。
记忆:看见&就是读取位。
将提取出的位与一个常数比较。如果相等,说明硬件处于某种状态。
读取到ADC就绪,开始读取
写入寄存器.清除特定位(将寄存器中某一位的值清零):
清除其实是复杂的
最关键一步就是这个取反
将寄存器值和一个取反的位掩码做与运算。取反的位掩码中,我们想要清零的位为0,其他位为1。这样,与运算的结果就会将要清零的位清零。
自己来算一下,我们目标是清除3位置的1.
第三步的时候是读取,可以看到3位置上面是1.
接着对掩码取反,然后与运算。
最后的结构就是第三位为0了。
我们可以这样记,清除的时候,先读取,然后再清楚。也就是多了一个步骤。
上面是读位和清位,下面就是写位。使用或运算|
记住这个开关的样子
就好像是开关的竖
设置特定位:将寄存器中某一位的值设置为1。
方法:将寄存器值和一个位掩码做或运算。位掩码中,我们想要设置为1的位为1,其他位为0。这样,或运算的结果就会将要设置的位设置为1。
置位,直接|
清除的步骤多,要与运算和掩码翻转
读取位为清除步骤的一半儿
请把这个刻在骨子里面。
我们还有一个是这样的
寄存器的操作是连续的,如果这个寄存器很大,我们一位一位的就不好了。可以先都清了,然后直接把值放进去。
它就是一个复合操作:
看这个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看传感器原始数据的处理方式-位运算 以往的位运算。