STM32技术--存储器映射及位带

以下以STM32F407ZGT6为例说明。 

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亮了。

posted @ 2021-02-22 13:41  温柔的熊  阅读(791)  评论(0编辑  收藏  举报