笔者博客链接:蜡笔小新没有博客
希望可以和志同道合的朋友多交流!
STM32中的位带操作: 名字为位带操作,实际上是对位的操作,位操作就是可以单独的对一个比特位读和写,这个在 51 单片机中非常常见。 51 单片机中通过关键字 sbit 来实现位定义, STM32 没有这样的关键字,而是通过访问位带别名区来实现。STM32 的全部寄存器都可以通过访问位带别名区的方式来达到访问原始寄存器比特位的效果,这比 51 单片机强大很多。因为 51 单片机里面并不是所有的寄存器都是可以比特位操作,有些寄存器还是得字节操作,比如 SBUF。
51单片机中的位操作:
51单片机中可以对寄存器实现单个位的操作,靠的就是关键字sbit
,如
sbit led=P1^0; led=1;
就可实现对P1.0位置1的效果。
为什么STM32不推崇直接进行位操作?
本人认为STM32是32位MCU,一次处理32位数据,所以一次只处理一位的数据未必大材小用了,除非特殊情况,否则都以32位处理。
如何处理STM32中要对某一位进行操作时的情况?
要知道STM32中采用库函数编程,所以有很多的对位操作的任务都用具体的函数来完成,而这些函数都已经做好了我们只需要知道怎么用就行。但我们仍然可以自己实现位操作,这种神操作就是位带操作 。
位带区与位带区别名:
在 STM32 中,有两个地方实现了位带,一个是 SRAM 区的最低 1MB 空间,令一个是外设区最低 1MB 空间。这两个 1MB 的空间除了可以像正常的 RAM 一样操作外,他们还有自己的位带别名区,位带别名区把这 1MB 的空间的==每一个位膨胀成32 位 (要知道 STM32 的系统总线是 32 位的,按照 4 个字节访问的时候是最快的,所以膨胀成 4 个字节来访问是最高效的。)==,当访问位带别名区的这些字时,就可以达到访问位带区某个比特位的目的。
位带区就是就是可以进行位带操作的寄存器的映射地址。
位带区别名可以理解为将位带区每一个位都膨胀32倍(用一个字节代表一个位,以便于32位MCU操作)后的地址。
如何实现位带操作?
要进行位带操作需要知道被操作的位的地址,因为SRAM和外设中都可以位带操作,所以形式上可以将位带操作归纳为俩个公式。
对于位带区的某个比特,记它所在字节的地址为 A,位序号为 n(0<=n<=7),则其在位带区别名地址为:
外设:AliasAddr= =0x42000000+ (A-0x40000000)*8*4 +n*4
SRAM:AliasAddr= =0x22000000+ (A-0x20000000)*8*4 +n*4
==用外设解释公式:0X42000000 是外设位带别名区的起始地址, 0x40000000 是外设位带区的起始地址,(A-0x40000000)表示该比特前面有多少个字节,一个字节有 8 位,所以8,一个位膨胀后是 4 个字节,所以4, n 表示该比特在 A 地址的序号,因为一个位经过膨胀后是四个字节,所以也*4。==
当然,也可以将俩个公式合二为一:
// 把“位带地址+位序号”转换成别名地址的宏
AliasAdd = (addr & 0xF0000000)+0x02000000+((addr &0x00FFFFFF)<<5)+(bitnum<<2)
知晓了位带区别名的地址,然后将此地址转换为指针类型就可以通过位带操作对原始的为进行操作。
附上野火的位带操作代码:
#include "stm32f10x.h"
#include "./led/bsp_led.h"
#define PCout(n) (*(unsigned int*)(((GPIOC_BASE+0x0c) & 0xF0000000)+0x02000000+(((GPIOC_BASE+0x0c) &0x000FFFFF)<<5)+(n<<2)))
#define PBout(n) (*(unsigned int*)(((GPIOB_BASE+0x0c) & 0xF0000000)+0x02000000+(((GPIOB_BASE+0x0c) &0x000FFFFF)<<5)+(n<<2)))
void delay(uint32_t count)
{
for(; count!=0; count--);
}
int main(void)
{
LED_GPIO_Config(); //LED初始化函数
while(1)
{
PCout(2) = 1;
//GPIO_SetBits(LED1_GPIO_PORT, LED1_GPIO_PIN);
delay(0xfffff);
PCout(2) = 0;
//GPIO_ResetBits(LED1_GPIO_PORT, LED1_GPIO_PIN);
delay(0xfffff);
PCout(3) = 1;
//GPIO_SetBits(LED2_GPIO_PORT, LED2_GPIO_PIN);
delay(0xfffff);
PCout(3) = 0;
//GPIO_ResetBits(LED2_GPIO_PORT, LED2_GPIO_PIN);
delay(0xfffff);
}
代码简述:用宏定义的方法来操作GPIOC中的ODR寄存器和IDR寄存器中的某一位来实现led灯的亮灭
#define PCout(n) (*(unsigned int*)(((GPIOC_BASE+0x0c) & 0xF0000000)+0x02000000+(((GPIOC_BASE+0x0c) &0x000FFFFF)<<5)+(n<<2)))
文章部分参考:【野火®】《零死角玩转STM32》