STM32是32位的芯片,意味着有从0x00000000~0xFFFFFFFF 4G也就是32位的寻址空间,在设计芯片的时候,采用的是ARM所设计的架构,ST(意法半导体)公司在基于ARM的对芯片添加了自己的外设。
注意:这里是一个地址空间存放一个字节,即1Byte
ARM规定:
Vendor-specific memory 511MB 存放特定厂商的代码
Private peripheral bus 1M 物理总线
External device 1.0GB 片上外设不够用时,外扩在此区域
Extermal RAM 1.0GB RAM空间不够用时,外扩在此区域
peripheral 0.5GB 外设都在此区域 如 GPIO口,定时器等等
SRAM 0.5GB 用来存放运行时的数据
Code(Flash) 0.5GB 用来存放相应的代码和数据
ST公司又根据ARM的划分添加了相应的外设
以GPIO口为例:我们操作GPIO口实际上是操作地址为0x40020000~0x400223FF的寄存器
ST的封装如下:
GPIO_TypeDef * 是一个结构体指针类型,而这个结构体的地址为各个端口号的基地址,
地址计算为:PERIPH_BASE设备的基地址加上0x00020000的偏移地址为AHB1的地址,AHB1的地址加上各个端口的偏移地址即为各个GPIO口的基地址,再加上每个端口上寄存器的偏移量就得到了该寄存器的地址。
重写GPIOF的寄存器
#define GPIOF_BASEADDR 0x40021400 #define rGPIOF_MODER *(uint32_t *)(GPIOF_BASEADDR+0x00) #define rGPIOF_OTYPER *(uint32_t *)(GPIOF_BASEADDR+0x04) #define rGPIOF_OSPEEDR *(uint32_t *)(GPIOF_BASEADDR+0x08) #define rGPIOF_PUPDR *(uint32_t *)(GPIOF_BASEADDR+0x0C) void LED_myCOnfig(void) { RCC->AHB1ENR |= (1<<5); rGPIOF_MODER &=~ (3<<12); rGPIOF_MODER |= (1<<12); rGPIOF_OTYPER &=~ (1<<6); rGPIOF_OSPEEDR &=~ (3<<12); rGPIOF_OSPEEDR |= (2<<12); rGPIOF_PUPDR &=~ (3<<12); }
位带:
在stm32中因为是对寄存器的位操作,如果我们想要像51单片机那样类似p1=1的位变量操作,就要使用ARM在设计内核架构时留的位带操作。
什么是位带?
位带是基于ARM提供的位段机制,将位操作变为字操作的一种操作机制。
只有0x20000000~0x200FFFFF和0x40000000~0x400FFFFF这两部分有位段的映射,其中0x20000000~0x200FFFFF和0x40000000~0x400FFFFF这两个1MB的空间叫做位段区,0x22000000~0x23FFFFFF和0x42000000~0x43FFFFFF这两部分叫做别名区,位段区的每一个位(1bit)都对应别名区的每一个字(4Byte),我们向别名区相应的字中写入0或1就是向对应的位段区写入0或1.
而位带的使用方法是根据位段区的相应位计算出别名区的地址,然后再通过指针对该地址进行操作即可。
以PF6为例 GPIOF的基地址为 0x40021414
计算出GPIOF的基地址相对于位段基地址偏移量 -- 字节
0x40021414 - 0x40000000 = 0x21414
再乘以8转换成GPIOF端口的第六个管脚偏移多少位
0x21414 * 8 + 6
计算别名区的偏移量
(0x21414 * 8 + 6)*4
这里因为是位段区每偏移一位,别名区偏移4个字节,所以得出来的值要乘以4来转换成偏移的字节数
别名区的地址 --偏移量 + 基地址
0x42000000 + (0x21414 * 8 + 6)*4
换算的公式如下:
位段区addr bit
别名区基地址:
(Adrr &0xF0000000) +0x2000000
别名区的偏移量:
((Addr & 0xFFFFF)*8 + bit)*4
别名区地址:
(Adrr &0xF0000000) +0x2000000 +( (Addr & 0xFFFFF)*8 + bit)*4
#include "stm32f4xx.h" //先计算别名区对应的地址 #define BITBAND(addr,bit) (((addr&0xf0000000)+0x2000000)+((addr&0xfffff)*8+bit)*4) //操作别名区 #define MEM_ADDR(addr,bit) *(volatile unsigned int *)BITBAND(addr,bit) //ODR #define PAout(bit) MEM_ADDR((unsigned int)&GPIOA->ODR,bit) #define PBout(bit) MEM_ADDR((unsigned int)&GPIOB->ODR,bit) #define PCout(bit) MEM_ADDR((unsigned int)&GPIOC->ODR,bit) #define PDout(bit) MEM_ADDR((unsigned int)&GPIOD->ODR,bit) #define PEout(bit) MEM_ADDR((unsigned int)&GPIOE->ODR,bit) #define PFout(bit) MEM_ADDR((unsigned int)&GPIOF->ODR,bit) #define PGout(bit) MEM_ADDR((unsigned int)&GPIOG->ODR,bit) #define PHout(bit) MEM_ADDR((unsigned int)&GPIOG->ODR,bit) //IDR #define PAin(bit) MEM_ADDR((unsigned int)&GPIOA->IDR,bit) #define PBin(bit) MEM_ADDR((unsigned int)&GPIOB->IDR,bit) #define PCin(bit) MEM_ADDR((unsigned int)&GPIOC->IDR,bit) #define PDin(bit) MEM_ADDR((unsigned int)&GPIOD->IDR,bit) #define PEin(bit) MEM_ADDR((unsigned int)&GPIOE->IDR,bit) #define PFin(bit) MEM_ADDR((unsigned int)&GPIOF->IDR,bit) #define PGin(bit) MEM_ADDR((unsigned int)&GPIOG->IDR,bit) #define PHin(bit) MEM_ADDR((unsigned int)&GPIOG->IDR,bit)
这样,在操作时就可以直接将PF(6)=0使led亮了。
想要一起学习的需要资料的小伙伴,或者有疑问的小伙伴都可以联系我共同学习,交流QQ:1459059585