STM32系统构架
当你拿到一款芯片后,怎样可以快速了解到它具有哪些功能?了解单片机的系统架构是使用款芯片的基础,这些信息在芯片手册上都会有详细的说明,并且它们会被安排在最前面的章节,目的就是让用户最开始就能够关注到该款芯片具有哪些功能,特点。
STM32F103系统结构
从上图我们可以了解到STM32F103系列的主要系统组成单元:
CPU核:使用ARM Cortex-M3内核;
存储器:
STM32F103系列微控制器采用经典的哈佛架构,程序存储和数据存储采用独立的存储器空间;
ICode:指令代码,Flash存储器用于存储程序;
DCode:数据代码,SRAM用于存储数据;
外设:
GPIO;
定时器;
ADC;
通信接口:SPI、I2C、USART;
其他接口:USB、CAN、RTC、DMA、SDIO、FSMC等;
总线架构:
AHB总线:高速总线,连接核心与部分外设。
APB1/APB2总线:连接外设,时钟较AHB低。
芯片存储器映射
从上面的系统结构图中我们很容易就能看出内核与存储单元的关系,但更多的细节芯片手册上并没有给出。对于51系列单片机单片机,因为其内核和存储单元都比较简单管理起来也非常容易,但STM32芯片内部复杂程度都比51高几个数量级,所以需要对存储单元有特定的划分管理,把它称为存储器映射,这些知识在51单片机中是不会涉及的,这一部分内容在ARM公司的内核手册上才会有详细的讲解。我们都知道了STM32F103系列单片机采用的是的ARM Cortex-M3内核,所以要了解这部分知识需要查看Cortex-M3内核资料。
上图是Cortex-M3内核资料中对给内核芯片预定义的存储器映射说明,接下来我们通过STM32F103系列单片机来逐步学习。
STM32单片机存储器映射
STM32单片机存储空间包含了用于程序代码和数据存储的系统存储器区域,以及用于外设控制的外设寄存器区域。为了能够直观而有效地操作这些内存资源,STM32采用存储器映射的方式进行内存管理。什么是存储器映射?其基本思想是将不同的内存区域按逻辑功能映射到单片机的线性地址空间,从而实现通过访问地址来操作存储单元的目的。
要高效地使用STM32单片机,正确理解和使用其存储器映射方式是必要的。以下详细介绍STM32内存映射的组成结构、典型布局以及编程方法,帮助初学者建立对存储器映射的整体认知,为后续STM32编程开发奠定基础。
STM32存储器映射概述
STM32单片机的内存映射可以分为两大部分:系统存储器映射区域和外设寄存器映射区域。
系统存储器区域主要包括:
SRAM:用于存放数据,包括全局变量、堆栈等
Flash:用于存放程序代码
外设寄存器区域则映射了各外设控制需要的寄存器,通过访问这些寄存器可以配置外设工作模式或者获取其工作状态。
整个内存映射采用线性连续的地址从低向高排布,从0x00000000开始,每个内存区按照功能和大小顺序映射。STM32内存空间的总大小依型号不同有所区别,较大的可以达到GB级别。
典型存储器映射布局
下面我们以STM32F103系列为例,介绍其典型的存储器映射布局。
Flash memory: 0x08000000 ~ 0x0807FFFF (512KB)
该区域存储了程序代码。起始地址固定为0x08000000,大小根据芯片容量不同而不同。
SRAM: 0x20000000 ~ 0x2000FFFF (64KB)
该区域用于存储运行时的数据,如全局变量、堆栈等。起始地址固定为0x20000000。
外设寄存器: 0x40000000 ~ 0x4000FFFF
用于映射控制外设的各种控制寄存器,可以通过访问这些地址来配置外设。
AHB外设: 0x40020000 ~ 0x4002FFFF
包含了连接在AHB总线上的外设,如GPIO、DMA等的控制寄存器。
APB1外设: 0x40000000 ~ 0x4000FFFF
包含连接在APB1总线上的外设,如TIMER2、UART2等。
APB2外设: 0x40010000 ~ 0x400FFFFF
包含连接在APB2总线上的外设,如TIMER1、SPI1等。
通过上述内存布局可知,STM32内存映射有明确的逻辑划分,不同区域存放不同的数据或寄存器,各外设也按照所在总线分组排布。用户可以通过编程手册找到每个外设寄存器的确切地址。
基于存储器映射的外设寄存器编程
知道了各外设寄存器的地址映射后,我们就可以通过直接读写地址来操作外设,例如:
GPIOA_CRH = 0x40010800; //GPIOA控制寄存器地址
*(volatile uint32_t *)GPIOA_CRH |= 0x01; //配置PA8为输出模式
但是直接地址操作不够优雅和灵活,更好的方法是利用硬件抽象层(HAL)提供的函数来访问寄存器,例如:
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = GPIO_PIN_8;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL库会根据传入的GPIO端口和引脚参数,自动计算出对应的寄存器地址,从而实现配置GPIO的功能。
这种基于内存映射的编程方式,为用户屏蔽了底层寄存器操作的细节,提供了简单易用的外设驱动接口。
存储器映射工作原理
那么STM32是如何实现内存映射,将不同区域的存储单元映射到连续的线性地址空间中的呢?
主要是通过地址译码器根据地址某些Bit位的组合,对地址进行解码,选通出需要访问的目标存储区域的信号,从而将访问请求导向到正确的目的地。
例如,地址的31-24bit用于区分代码区域和外设区域,0-15bit含有偏移量;地址译码器根据地址Bit位组合,将访问请求定向到与之对应的存储单元上。
内存保护机制
为了数据安全,STM32还增加了存储区域写入保护机制。
主要手段是利用内存保护单元(MPU)进行存储区域访问控制。MPU可以进行区域划分,配置每个区域的访问权限,从而实现对区域读写操作的保护,防止程序异常修改关键存储内容。
结语
本文介绍了STM32存储器映射的基本结构、典型布局及相关原理,并讲解了基于存储器映射访问外设寄存器的编程方法。存储器映射方式抽象并简化了STM32的庞大内存空间,有助于用户直观而高效地使用这块“位带区”。希望本文能帮助读者建立STM32存储器映射的概念框架,并为后续编程项目提供基础理论支持。关于“位带”操作,我将会在以后的文章中做单独进行介绍。使用“位带”操作可以让我们的代码更高效,这也是每一位STM32单片机开发者不可或缺的技能。