博客
关于我
从STM32的位带操作重谈嵌入式中寻址与对齐的理解
阅读量:574 次
发布时间:2019-03-11

本文共 2160 字,大约阅读时间需要 7 分钟。

转载自:http://www.cnblogs.com/apollius/archive/2012/12/02/2797604.html

觉得这篇文章很不错,普及知识:

【@.1 从位带操作开始】

初接触STM32的人可能会花不少时间来理解位带操作(bit banding)的原理与步骤。位带操作允许编程人员以字的单位读/写单个bit位。想想我们平时对于一个bit位的操作就如下面代码所示:

```c@---> PIN0 |= (1 << 3);@---> PIN0 &= ~(1 << 5);```

虽然这段代码看起来只有一行,但实际上做了很多工作。例如,第一行代码首先读取当前PIN0的值并存储到缓存区,然后将1左移三位放入缓存区,接着进行“或”操作,更新PIN0的值。汇编语言中这些步骤可能不那么明显,但这些工作都是必然要做的。

对于位带操作,STM32将每个bit位重新映射到一个单独的地址,只需对这个新地址进行写操作,就可以自动将原PIN0对应位设置或清零。例如,假设PIN0的第3bit位重新映射的地址用变量PIN0BIT3表示,那么上述操作可以写成:

```c@---> PIN0BIT3 = 1; // 等同于PIN0 |= (1 << 3)```

这一行代码通过写入PIN0BIT3所在的地址,确保了PIN0的第3bit位被置1。这样操作比之前简单快捷,处理速度也更快。

接下来,我们自然会想知道PIN0第三bit位重新映射的地址在哪里?位带操作的地址重映射是否有限制?原地址和重映射地址之间有什么转换公式?

我们可以参考STM32官方手册。STM32F1系列及其编程参考书中都有关于位带操作的详细说明,包括地址重映射的计算公式。

【@.2 字,字节,半字与寻址方式】

先回顾一些基本概念。在计算机中,二进制的单位包括比特(bit)、字节(byte)和半字(half-word)。这些概念直接关系到内存寻址方式和数据存储方式。

比特(bit)是计算机中最基本的单位,每个比特只能有0或1两个值。字节(byte)是8个比特的集合,常用来衡量存储单元的大小。半字(half-word)则是16个比特或32个比特的集合,具体取决于处理器的架构。每个字可以对应一个寄存器的大小,如ARM处理器的32位寄存器就需要4个字节(32bit)来存储。

在ARM架构中,数据和指令是以字(word)为单位存储和访问的。每个寄存器占用4个字节(32bit)的空间,这样多个寄存器可以连续存放到物理地址中,提高访问效率。例如,GPIO寄存器通常会被定义为连续的32bit值,确保快速访问和操作。

【@.3 字节对齐,字节序 Endianness】

在计算机中,字节对齐和字节序(endianness)是数据存储和访问的关键问题。字节对齐确保数据在内存中的存储位置是正确的,字节序决定了多字节数据在内存中的排列顺序。

以GPIO寄存器为例,假设地址0xE0028000存储了一个32bit的值。该地址的实际存储内容可能是0xDD, 0xCC, 0xBB, 0xAA等四个字节组成的值。读取时,我们需要明确字节序是大端(big-endian)还是小端(little-endian)。大端的高位数存放在地址的低位,小端则相反。ARM架构通常使用小端字节序,这在读取和写入寄存器时非常重要,确保数据的正确性和一致性。

【@.4 再看位带操作】

位带操作的核心原理在于将单个bit位映射到一个独立的地址。STM32中的位带操作区(bit band region)允许每个bit位扩展到一个32bit的别名地址(bit band alias)。这样,每个bit位的操作都可以通过简单的读写操作完成,极大提升了操作效率。

位带操作区的实现涉及到内存的物理地址重映射。编程参考书中提到,允许位带操作的区域有两处:一个是SRAM区,另一个是Peripheral区(外设区),每处均有1MB的空间。通过地址重映射,每个bit位被扩展到一个32bit的别名地址,实现快速的单bit位操作。

例如,在GPIO应用中,通过宏定义可以轻松实现位带操作。定义如下:

```c#define BITBAND(addr, bitnum) ((addr & 0xF0000000) + 0x2000000 + ((addr & 0xFFFFF) << 5) + (bitnum << 2))#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))#define GPIOA_ODR_Addr (GPIOA_BASE + 12) // 0x4001080C#define PAout(n) BIT_ADDR(GPIOA_ODR_Addr, n) // Output```

使用时,只需调用PAout函数传入bit位数即可实现对相应PIN的控制:

```c@---> PAout(4) = 1;```

这种方法简洁高效,充分利用了位带操作的优势,实现了对GPIO引脚状态的快速控制。

你可能感兴趣的文章
设计模式 - 2) 策略模式
查看>>
SpringBoot使用RedisTemplate简单操作Redis的五种数据类型
查看>>
国标流媒体服务器以ROOT身份运行提示“permission denide”报错解决
查看>>
国标流媒体服务器在linux系统运行提示fork/exec ……/redis/redis-server错误解决方案
查看>>
国标GB28181协议视频推流平台EasyGBD在Linux下编译报“UINT64_C在此作用领域中尚未声明”错误
查看>>
安防摄像机网页无插件直播方案EasyNVR关于接口调用出现401 Unauthorized问题的解决方法
查看>>
【视频教程】EasyNVR如何将老版本的EasyNVR的数据迁移到4.0.0以上版本
查看>>
qt中转到槽后如何取消信号与槽关联
查看>>
qt问题记录-spin box与double spin box
查看>>
移动端事件
查看>>
css 图片按比例缩放
查看>>
小程序form表单里面buton点击事件失效
查看>>
微信小程序placeholder设置自定义样式
查看>>
安装npm install --save vue-scroller失败
查看>>
es6 引用数组,数组发生改变 (es6 引用类型的数据引用的时候怎么不改变原始数据)
查看>>
spring-day01
查看>>
spring的值注入与组件扫描
查看>>
【leetcode】349. 两个数组的交集(intersection-of-two-arrays)(哈希)[简单]
查看>>
C#跨窗体程序调用方法的具体操作
查看>>
C#中创建Android项目
查看>>