前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >MCU寄存器的位运算方式(读写+拼接字节数据)

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

作者头像
云深无际
发布2025-02-08 14:08:52
发布2025-02-08 14:08:52
5300
代码可运行
举报
文章被收录于专栏:云深之无迹云深之无迹
运行总次数:0
代码可运行

我一直想把嵌入式学到精通,学到现在终于是有点感觉了。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 位:

代码语言:javascript
代码运行次数:0
复制
ADC_WatchdogStruct->ADC_WatchdogVth << 16 // 结果:0x12340000

下阈值(保持原位不动):

代码语言:javascript
代码运行次数:0
复制
ADC_WatchdogStruct->ADC_WatchdogVtl // 结果:0x5678

组合两个值:

代码语言:javascript
代码运行次数:0
复制
(0x12340000 | 0x5678) // 结果:0x12345678

最终,0x12345678 就是写入 AWDTR 寄存器的值。

总结一下:我们很多时候是要多字节操作寄存器的,这里的例子比较极端。

还有一类是从寄存器读取数据来拼接的。

ADS1115

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

代码语言:javascript
代码运行次数:0
复制
regData[0] = data >> 8;

将 16 位的 data 右移 8 位,提取高 8 位数据,并将其存储在 regData[0] 中。

代码语言:javascript
代码运行次数: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),那么:

代码语言:javascript
代码运行次数:0
复制
regData[0] = data >> 8; 

regData[0] 的值为 0x12(二进制为 0001 0010)。

代码语言:javascript
代码运行次数:0
复制
regData[1] = data & 0xFF; 

regData[1] 的值为 0x34(二进制为 0011 0100)

这就是嵌入式,这就是底层。

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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-02-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 云深之无迹 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档