• STM32存储器映射和寄存器映射


    存储器映射

      对于Cortex-M3来讲,有一块4G大小的存储器空间。存储器映射指的是芯片厂商为这个空间分配地址的操作。这4G空间被均匀地划分为8个大小为512MB的存储块(block),并且每个块都各具特色。下面主要介绍Block1~Block2。

    Block0

      Block0的地址范围为0x0000_0000~0x1FFF_FFFF。它被设计用来存放代码程序,其中主要有FLASH、SYSTEM MEMORY和OPTION BYTES:

        FLASH:起始地址为0x0800_0000,存放用户程序和掉电保存数据。FLASH容量从16k到512k不等,以STM32F10x8系列为例,8代表FLASH容量为64k,所以结束地址就为0x0801_FFFF。

        SYSTEM MEMORY:系统存储器,存放了BootLoader程序,禁止用户改动,该程序主要用于串口下载。

        OPTION BYTES:选项字节,可以配置读写保护、看门狗等。

      这里简单说一下地址分配。整块4G存储器开始地址标为0x0000_0000,结束地址为0xFFFF_FFFF,地址表示采用了十六进制,一共8*4=32bit,而2^32刚好就是4G。存储器的基本单元是一个字节,每个地址都对应这样一个单元,因此用32位地址来表示,其容量刚好就是4GB。同理,根据Block0的起止地址,也可以计算得到其容量为512MB。

    Block1

      Block1的地址范围为0x2000_0000~0x3FFF_FFFF。其中0x2000_0000~0x2000_FFFF被划为SRAM,主要是用于程序运行的堆栈开销。

    Block2

      Block2的地址范围为0x4000_0000~0x5FFF_FFFF。这块空间被充分用作外设寄存器。根据总线不同,将外设分为三大块,第一块是APB1总线外设,起始地址为0x4000_0000;第二块是APB2总线外设,起始地址为0x4001_0000;最后一块为AHB总线外设,起始地址为0x4001_8000。

    寄存器映射

      寄存器映射主要针对于Block2。这种映射不同于存储器映射的分配地址操作,而是在程序中对具有特定功能的内存单元进行命名的过程,每个内存单元都是四个字节,称作寄存器。例如GPIOA外设有个寄存器地址范围为0x4001_0800~0x4001_0803,该寄存器是用来配置GPIOA部分端口工作模式的,因此被映射为GPIOA_CRL。由此可知,这种映射方便了对寄存器的访问操作,因为如果每次访问寄存器都要查地址范围的话是很痛苦的。

    GPIOA_CRL

      如何实现这种映射的呢?以GPIOA_CRL为例,其映射地址 = 外设总基地址(块基地址)+ 总线相对于外设总基地址的偏移 + 具体外设基地址相对于总线基地址的偏移 + 寄存器相对于具体外设基地址的偏移。

      外设总基地址恰好就是Block2的起始地址0x4000_0000;

      GPIO属于APB2总线外设,因此查阅芯片手册如下图所示,我们其实直接可以得到APB2起始地址和GPIOA的起始地址,但是程序中一般不这么做,而是以偏移量来表示层次关系。从图中可计算到总线相对于外设总基地址的偏移为0x1_0000,GPIOA相对于APB2基地址的偏移为0x800。

        

      再查阅GPIO的寄存器组,如下图所示。可以得到CRL寄存器相对于GPIO基地址偏移为0x00。综上GPIOA_CRL的基地址为:0x4000_0000+0x1_0000+0x800+0x00。

        

    程序中地址映射

      STM32固件库中,有个头文件叫stm32f10x.h,其中就定义了寄存器的映射,部分代码如下:

      外设基地址PERIPH_BASE:

    #define PERIPH_BASE           ((uint32_t)0x40000000)

      总线基地址,在外设基地址上加上偏移:

    #define APB1PERIPH_BASE       PERIPH_BASE
    #define APB2PERIPH_BASE       (PERIPH_BASE + 0x10000)
    #define AHBPERIPH_BASE        (PERIPH_BASE + 0x20000)

      

      GPIO外设基地址,在APB2总线基地址上加上偏移:

    #define GPIOA_BASE            (APB2PERIPH_BASE + 0x0800)
    #define GPIOB_BASE            (APB2PERIPH_BASE + 0x0C00)
    #define GPIOC_BASE            (APB2PERIPH_BASE + 0x1000)
    #define GPIOD_BASE            (APB2PERIPH_BASE + 0x1400)
    #define GPIOE_BASE            (APB2PERIPH_BASE + 0x1800)
    #define GPIOF_BASE            (APB2PERIPH_BASE + 0x1C00)
    #define GPIOG_BASE            (APB2PERIPH_BASE + 0x2000)

      定义GPIO外设结构体,因为结构体成员在内存中是连续的,这种形式与寄存器组非常类似,所以用结构体能够很好的管理寄存器:

    typedef struct
    {
      __IO uint32_t CRL;
      __IO uint32_t CRH;
      __IO uint32_t IDR;
      __IO uint32_t ODR;
      __IO uint32_t BSRR;
      __IO uint32_t BRR;
      __IO uint32_t LCKR;
    } GPIO_TypeDef;

      定义GPIOA结构体指针,因为单单定义GPIO外设结构体,并不能确定其内存地址,因此用指针将其绑定到GPIOA外设基地址:

    #define GPIOA               ((GPIO_TypeDef *) GPIOA_BASE)

      访问寄存器方式的对比,映射访问明显更为直观:

    //直接的地址访问
    *(unsigned int *)(0x4001_0800)|= 0x0001;
    //映射访问
    GPIOA->CRL |= 0x0001;

      

      

  • 相关阅读:
    Spark源码走读4——Scheduler
    Spark源码走读3——Job Runtime
    Spark源码走读2——Spark Submit
    Spark源码走读1——RDD
    Tachyon源码解读一:master部分
    VS2008中MFC界面编程Caption中文全是乱码的解决办法
    程序猿也爱学英语(上),有图有真相
    C++程序员必看书单
    如何将CString转换成WCHAR
    Windows 语音识别编程
  • 原文地址:https://www.cnblogs.com/kensporger/p/12347547.html
Copyright © 2020-2023  润新知