热度 13
2014-8-28 09:30
1358 次阅读|
7 个评论
Stm32 学习第二天 今天对为位带操作进行了浅显的学习。 我对位带操作的理解是,通过位带操作我们可以向操作 51 单片机一样,直接控制一个 IO 口的输出,或者得到一个 IO 口的输入。在 51 中我们经常这么写程序: PA0 = 1; 在 51 单片机中以上语句实现了将 PA0 口的输出置 1 。 在 stm32 的应用程序中,利用位带操作,我们也可以达到上述的效果。我们最容易想到的就是通过 GPIO 的管教来单独控制每盏 LED 的点亮和熄灭。另一方面,也对操作串行接口器件提供了很大的方便(典型的如 74HC165 之类的)。当然位带操作不仅 作 用于 此。 我们知道在芯片的内存中,一个地址对应这一个字节,也就是 8 个比特的数据。比如说,我们可以查看 stm32 手册得 GPIOx_CRL 的寄存器偏移地址是 00h ,它有 32 个比特,紧接着它的下一个寄存器 GPIOx_CRH 的偏移地址是 04h ,也就是说 GPIOx_CRL 的 32 比特占据了 00h 到 04h 之间的 4 个地址( 00h , 01h , 02h , 03h )。也就是一个地址, 8 个比特的数据。 位带操作所想实现的功能是,将这每个比特用 4 个地址来寻址,我们知道 4 个地址原本对应的是 32 比特的数据。也就是将 1 个比特将膨胀为 32 比特。必须说明的是膨胀后的 32 比特数据中只有最低位是有效的,也就是原始的那一比特数据对应的是膨胀 后的 32 比 特数据的最低位。膨胀前的原始地址和膨胀后的地址具有一一对应的关系。通过这样一种地址的映射将之前每个字节中的 8 个比特剥离分散开来,以便于我们对每一个比特进行控制。我们知道在 GPIO 中每一个比特对应这一个 IO 管脚,这样我们就能 方便的对 一个管脚进行控制。我觉的形象的来说就是给每一个比特重新起了个别名。 在 stm32 中,不是每块内存区域都能进行位带操作的。支持位带操作的内存区范围是: 0x2000_0000 ‐ 0x200F_FFFF ( SRAM 区中的最低 1MB ) 0x4000_0000 ‐ 0x400F_FFFF (片上外设区中的最低 1MB ) 对于 SRAM 位带区的某个比特,记它所在字节地址为 A, 位序号 n(0=n=31), 则该比特在别名区的地址为: AliasAddr = 0x22000000 + (A – 0x20000000)*32 + n*4 对于片上外设位带区的某个比特,记它所在字节的地址为 A, 位序号为 n(0=n=31) ,则该比特在别名区的地址为: AliasAddr = 0x42000000 + (A – 0x40000000)*32 + n*4 对以上公式,比如对片上外设这个公式来说,我的理解是: 1. 膨胀后的映射地址起点从 0x42000000 开始 2. 用 (A – 0x40000000) 将会得到相对于起始地址的偏移量 3. 乘以 32 的原因是:以前 8 个比特用一个地址来表示,现在一个比特用 4 个地址来表示,所以以前一个地址表示的数据,现在要用 32 个地址来表示,所以乘以 32 4. 从 3 可以知道,我们乘以 32 后,我们得到了这个字节第 0 位比特膨胀后的地址,那么要得到第 n 位的,就加上 n*4 ,乘以 4 的原因是一个比特用四个地址来表示。 5. n 的取值范围是 0 到 31 是因为片上外设的寄存器都是 32 位的( 0 ~ 31 ) 那么对 SRAM 映射公式的理解类似。 通过上述的位带映射后,我们对映射后的地址进行操作,也就是对原始的地 址进行操作了。并且可以对单个比特进行单独的操作。 那么相对与普通对单个比特的操作,位带操作除了方便之外还有什么优势,那就是在效率方面,位带操作的效率更高。 比如,欲设置地址 0x20000000 中的比特 2 为 1 ,使用位带操作和不实用位带操作的汇编代码如下: ( 膨胀后的地址为 0x22000008) 1. 不使用位带操作的汇编代码: LDR R0 , =0x20000000 ; 设置地址 LDR R1 , ; 读取数据到寄存器 ORR.W R1 , #0x04 ; 改变第 2 比特的值 STR R1 , ; 将数据写回原来的地址 2. 使用位带操作的汇编代码: LDR R0 , =0x22000008 ; 设置地址(此为膨胀后的地址) MOV R1 , #1 ; 设置数据 STR R1 , ; 向地址写入数据 从上述代码中可以看出,位带操作的汇编指令更精简,效率更高。究其原因,我的理解是,使用位带操作少了定位到具体哪一位的操作,我们只需要向某个地址写入数据即可,而不需要确定写入到哪一位上,因为位带映射之后,我们每一个比特都对 应一 个地址。 位带操作还能用来简化跳转的判断。当跳转依据是某个位的时候,比如我们根据一个管脚的输入是高还是低,来决定我们之后的操作。以前我们必须这么做: 1. 读取整个寄存器 2. 掩蔽不需要的位 3. 比较并跳转 现在只需: 1. 从位带别名区(即膨胀后的地址)读取状态位 2. 比较并跳转 从以上可以看出,位带操作的效率更高,究其原因,还是因为缺少了对特定位的屏蔽和提取的操作。 以上,就是我对 stm32 的位带操作的理解,有什么理解不到位的地方,请大家指证,希望和大家多多交流。至于C语言的具体实现,出于字数的限制,我讲在下一篇文章来介绍。