stm32的时钟、中断的配置(针对寄存器),一些基础知识
一、学习参考资料
(1)正点原子的寄存器源码。
(2)STM32F103最小系统板开发指南-寄存器版本_V1.1(正点)
(3)STM32F103最小系统板开发指南-库函数版本_V1.1(正点)
(4)Cortex-M3权威指南(中文)
(5)STM32中文参考手册_V10
(6)stm32cubemx可视化时钟树配置
(7)其他博主文章
本文主要以stm32f1系列单片机为研究对象,从寄存器层面对时钟树的配置、中断优先级的配置进行阐述。
二、stm32官方bsp库寄存器封装的基本方式
一般按照连续的寄存器地址使用结构体指针的形式封装,将寄存器按照连续的顺序定义变量在结构体中,然后直接将利用结构体指针指向结构体第一个定义的首地址的位置。
typedef struct
{__I uint32_t CPUID; /*!< Offset: 0x00 CPU ID Base Register */__IO uint32_t ICSR; /*!< Offset: 0x04 Interrupt Control State Register */__IO uint32_t VTOR; /*!< Offset: 0x08 Vector Table Offset Register */__IO uint32_t AIRCR; /*!< Offset: 0x0C Application Interrupt / Reset Control Register */__IO uint32_t SCR; /*!< Offset: 0x10 System Control Register */__IO uint32_t CCR; /*!< Offset: 0x14 Configuration Control Register */__IO uint8_t SHP[12]; /*!< Offset: 0x18 System Handlers Priority Registers (4-7, 8-11, 12-15) */__IO uint32_t SHCSR; /*!< Offset: 0x24 System Handler Control and State Register */__IO uint32_t CFSR; /*!< Offset: 0x28 Configurable Fault Status Register */__IO uint32_t HFSR; /*!< Offset: 0x2C Hard Fault Status Register */__IO uint32_t DFSR; /*!< Offset: 0x30 Debug Fault Status Register */__IO uint32_t MMFAR; /*!< Offset: 0x34 Mem Manage Address Register */__IO uint32_t BFAR; /*!< Offset: 0x38 Bus Fault Address Register */__IO uint32_t AFSR; /*!< Offset: 0x3C Auxiliary Fault Status Register */__I uint32_t PFR[2]; /*!< Offset: 0x40 Processor Feature Register */ // 如果占用两个字节,就直接定义为数组的形式__I uint32_t DFR; /*!< Offset: 0x48 Debug Feature Register */__I uint32_t ADR; /*!< Offset: 0x4C Auxiliary Feature Register */__I uint32_t MMFR[4]; /*!< Offset: 0x50 Memory Model Feature Register */__I uint32_t ISAR[5]; /*!< Offset: 0x60 ISA Feature Register */
} SCB_Type;
#define SCB ((SCB_Type *) SCB_BASE) /*!< SCB configuration struct */
上面定义之后,对寄存器的操作就变成了“SCB->VTOR &= data(寄存器对应位置零),SCB->VTOR |= data(寄存器对应位置一)”。上面有些寄存器并不是32位的,占用位数比较多的可以采用定义数据的方式。
--------------------------------------------------------------------------------------------------------------------------------
除了上面大范围的定义寄存器的形式,对于操作某些单个寄存器的时候,可以采用寄存器地址强制转化的方式。转化为指针“ unsigned volatile int * ”。
#define RCC_BASE_MY (uint32_t)0x40021000 //基地址
#define RCC_CR_MY (uint32_t)0x00000000 //偏移地址
#define RCC_CFGR_MY (uint32_t)0x00000004
#define RCC_CIR_MY (uint32_t)0x00000008
#define RCC_APB2RSTR_MY (uint32_t)0x0000000c#define MY_RCC_CR *((unsigned volatile int*)(RCC_BASE_MY+RCC_CR_MY))
#define MY_RCC_CFGR *((unsigned volatile int*)(RCC_BASE_MY+RCC_CFGR_MY))
#define MY_RCC_CIR *((unsigned volatile int*)(RCC_BASE_MY+RCC_CIR_MY))
#define MY_RCC_APB2RSTR *((unsigned volatile int*)(RCC_BASE_MY+RCC_APB2RSTR_MY))
#define MY_FLASH_ACR *((unsigned volatile int*)0x40022000) //要查m4内核手册
此时对寄存器置位和复位的操作为:“ MY_RCC_CR |= (uint32_t)(1<<24) ”和“ MY_RCC_CFGR &= ~(uint32_t)(3<<11) ”。使用上面的方式就可以单独对某个寄存器进行操作。
------------------------------------------------------------------------------------------------------------------------------
stm32底层代码的知识点补充:
底层配置代码经常使用“assert_param()”函数判断用户的选择是否正确,如果不正确就会调用相应的错误处理函数进行处理。
#define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__))
//利用上面定义的assert_param(expr),判断expr是否正确(也就是用户的选择参数是否正确,来确定是否调用assert_failed函数,assert_failed函数需要从写。)
/* Exported functions ------------------------------------------------------- */void assert_failed(uint8_t* file, uint32_t line);
#else#define assert_param(expr) ((void)0)
#endif /* USE_FULL_ASSERT */#endif /* __STM32F10x_CONF_H */
#define __I volatile const /*!< defines 'read only' permissions */#define __O volatile /*!< defines 'write only' permissions */#define __IO volatile /*!< defines 'read / write' permissions */
__I :输入口(read only)。既然是输入,那么寄存器的值就随时会外部修改,那就不能进行优化, 每次都要重新从寄存器中读取。也不能写,即只读,不然就不是输入而是输出了。
__O :输出口(write only),也不能进行优化,不然你连续两次输出相同值,编译器认为没改变, 就忽略了后面那一次输出,假如外部在两次输出中间修改了值,那就影响输出
__IO:输入输出口(read/write),同上
为什么加下划线?
原因是:避免命名冲突,一般宏定义都是大写,但因为这里的字母比较少,所以再添加下划线来区分。这样一般都可以避免命名冲突问题,因为很少人这样命名,这样命名的人肯定知道这些是有什么用的。经常写大工程时,都会发现老是命名冲突,要不是全局变量冲突,要不就是宏定义冲突,所以我们要尽量避免这些问题,不然出问题了都不知道问题在哪里。
三、stm32f103zet6的时钟树讲解
对单片机的时钟树进行配置的时候,可以结合stm32cubemx的图形化配置进行理解。

如上图所示,总线时钟需要配置的位置为“1、2、3、4、5、6”。对时钟配置的寄存器主要有下图的几个,下面将针对需要配置位置的寄存器进行讲解。
对应外设时钟的“复位”(RCC_APB1RSTR、RCC_APB2RSTR)和“使能和失能”(RCC_APB1ENR、RCC_APB2ENR)分别对应两个寄存器。

1、PLLXTPRE:HSE分频器作为PLL输入 (HSE divider for PLL entry) (属于RCC_CFGR)

2、PLLMUL:PLL倍频系数 (PLL multiplication factor)(属于RCC_CFGR)

3、SW[1:0]:系统时钟切换 (System clock switch) (属于RCC_CFGR)

4、HPRE[3:0]: AHB预分频 (AHB Prescaler)(属于RCC_CFGR)

5、PPRE1[2:0]:低速APB预分频(APB1) (APB low-speed prescaler (APB1))(属于RCC_CFGR)

6、PPRE2[2:0]:高速APB预分频(APB2) (APB high-speed prescaler (APB2))属于RCC_CFGR)

配置的时钟和总线的开启寄存器是RCC_CR寄存器 。
注意:时钟配置的时候要从后向前配置,在相应时钟和总线开启之后,从“6”的位置向前进行配置。
四、系统时钟寄存器
typedef struct
{__IO uint32_t CR;__IO uint32_t CFGR;__IO uint32_t CIR;__IO uint32_t APB2RSTR;__IO uint32_t APB1RSTR;__IO uint32_t AHBENR;__IO uint32_t APB2ENR;__IO uint32_t APB1ENR;__IO uint32_t BDCR;__IO uint32_t CSR;#ifdef STM32F10X_CL __IO uint32_t AHBRSTR;__IO uint32_t CFGR2;
#endif /* STM32F10X_CL */ #if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL) uint32_t RESERVED0;__IO uint32_t CFGR2;
#endif /* STM32F10X_LD_VL || STM32F10X_MD_VL || STM32F10X_HD_VL */
} RCC_TypeDef;
#define RCC ((RCC_TypeDef *) RCC_BASE)
对系统时钟树的配置使用上述的底层地址定义即可。
下面是正点时钟树的配置代码,其中中断优先级的配置后面会讲:
//不能在这里执行所有外设复位!否则至少引起串口不工作.
//把所有时钟寄存器复位
void MYRCC_DeInit(void)
{ RCC->APB1RSTR = 0x00000000;//复位结束 RCC->APB2RSTR = 0x00000000; RCC->AHBENR = 0x00000014; //睡眠模式闪存和SRAM时钟使能.其他关闭. RCC->APB2ENR = 0x00000000; //外设时钟关闭. RCC->APB1ENR = 0x00000000; RCC->CR |= 0x00000001; //使能内部高速时钟HSION RCC->CFGR &= 0xF8FF0000; //复位SW[1:0],HPRE[3:0],PPRE1[2:0],PPRE2[2:0],ADCPRE[1:0],MCO[2:0] RCC->CR &= 0xFEF6FFFF; //复位HSEON,CSSON,PLLONRCC->CR &= 0xFFFBFFFF; //复位HSEBYP RCC->CFGR &= 0xFF80FFFF; //复位PLLSRC, PLLXTPRE, PLLMUL[3:0] and USBPRE RCC->CIR = 0x00000000; //关闭所有中断 //配置向量表
#ifdef VECT_TAB_RAM/* 向量表的配置和中断优先级的配置后面都会讲 */MY_NVIC_SetVectorTable(0x20000000, 0x0);
#else MY_NVIC_SetVectorTable(0x08000000,0x0);
#endif
}
//系统时钟初始化函数
//pll:选择的倍频数,从2开始,最大值为16
void Stm32_Clock_Init(u8 PLL)
{unsigned char temp=0; MYRCC_DeInit(); //复位并配置向量表RCC->CR|=0x00010000; //外部高速时钟使能HSEONwhile(!(RCC->CR>>17)); //等待外部时钟就绪RCC->CFGR=0X00000400; //APB1=DIV2;APB2=DIV1;AHB=DIV1;PLL-=2; //抵消2个单位(因为是从2开始的,设置0就是2)RCC->CFGR|=PLL<<18; //设置PLL值 2~16RCC->CFGR|=1<<16; //PLLSRC ON FLASH->ACR|=0x32; //FLASH 2个延时周期RCC->CR|=0x01000000; //PLLONwhile(!(RCC->CR>>25)); //等待PLL锁定RCC->CFGR|=0x00000002; //PLL作为系统时钟 while(temp!=0x02) //等待PLL作为系统时钟设置成功{ temp=RCC->CFGR>>2;temp&=0x03;}
}
五、中断向量表地址的配置
这里需要有stm32内存的框架知识,后面有对这部分知识的详细的讲解,不懂得可以先看后面,这里为了衔接前面的代码,先介绍中断向量表地址的配置。
stm32的内存中,对于程序员最重要的是“SRAM(0x2000 0000~0x2000 FFFF)”和“Flash(0x0800 0000~0x0808 0000)”两个位置的内存。

向量表的首地址可以设置在SRAM区或者Flash区域。下面是stm32F4的向量表设置的形式,STM32F4 在复位后,先从 0X08000004 地址取出复位中断向量的地址,并
跳转到复位中断服务程序,如图标号①所示;在复位中断服务程序执行完之后,会跳转到我们
的 main 函数,如图标号②所示;而我们的 main 函数一般都是一个死循环,在 main 函数执行过
程中,如果收到中断请求(发生重中断),此时 STM32F4 强制将 PC 指针指回中断向量表处,
如图标号③所示;然后,根据中断源进入相应的中断服务程序,如图标号④所示;在执行完中
断服务程序以后,程序再次返回 main 函数执行,如图标号⑤所示。

中断项链表的配置的寄存器在“Cortex-M3权威指南(中文)”文档中可以找到(113页)。

下面是底层硬件地址结构体封装和正点项链表地址设置代码。
/** @addtogroup CMSIS_CM3_SCB CMSIS CM3 SCBmemory mapped structure for System Control Block (SCB)@{*/
typedef struct
{__I uint32_t CPUID; /*!< Offset: 0x00 CPU ID Base Register */__IO uint32_t ICSR; /*!< Offset: 0x04 Interrupt Control State Register */__IO uint32_t VTOR; /*!< Offset: 0x08 Vector Table Offset Register */__IO uint32_t AIRCR; /*!< Offset: 0x0C Application Interrupt / Reset Control Register */__IO uint32_t SCR; /*!< Offset: 0x10 System Control Register */__IO uint32_t CCR; /*!< Offset: 0x14 Configuration Control Register */__IO uint8_t SHP[12]; /*!< Offset: 0x18 System Handlers Priority Registers (4-7, 8-11, 12-15) */__IO uint32_t SHCSR; /*!< Offset: 0x24 System Handler Control and State Register */__IO uint32_t CFSR; /*!< Offset: 0x28 Configurable Fault Status Register */__IO uint32_t HFSR; /*!< Offset: 0x2C Hard Fault Status Register */__IO uint32_t DFSR; /*!< Offset: 0x30 Debug Fault Status Register */__IO uint32_t MMFAR; /*!< Offset: 0x34 Mem Manage Address Register */__IO uint32_t BFAR; /*!< Offset: 0x38 Bus Fault Address Register */__IO uint32_t AFSR; /*!< Offset: 0x3C Auxiliary Fault Status Register */__I uint32_t PFR[2]; /*!< Offset: 0x40 Processor Feature Register */__I uint32_t DFR; /*!< Offset: 0x48 Debug Feature Register */__I uint32_t ADR; /*!< Offset: 0x4C Auxiliary Feature Register */__I uint32_t MMFR[4]; /*!< Offset: 0x50 Memory Model Feature Register */__I uint32_t ISAR[5]; /*!< Offset: 0x60 ISA Feature Register */
} SCB_Type;
#define SCB ((SCB_Type *) SCB_BASE) /*!< SCB configuration struct */
//设置向量表偏移地址
//NVIC_VectTab:基址
//Offset:偏移量
void MY_NVIC_SetVectorTable(u32 NVIC_VectTab, u32 Offset)
{ SCB->VTOR = NVIC_VectTab|(Offset & (u32)0x1FFFFF80);//设置NVIC的向量表偏移寄存器//用于标识向量表是在CODE区还是在RAM区
}
六、中断的优先级分组、优先级设置、外部中断的配置
6.1、stm32中断相关基础知识
中断使用的时候不允许中断嵌套。
对于stm32f10xxx系列单片机而言,有68个可屏蔽中断通道(不包含16个Cortex™-M3的中断线,16个可编程的优先等级(使用了4位中断优先级);



中断的使能的相关寄存器信息在“Cortex-M3权威指南(中文)”资料里面看,里面有中断使能相关的寄存器,中断只能的寄存器挂载在“SysTick_BASE”系统时钟总线上。


------------------------------------------------------------------------------------------------------------------------------
中断和事件的区别:
事件机制提供了一个完全有硬件自动完成的触发到产生结果的通道,不要软件的参与,降低了CPU的负荷,节省了中断资源,提高了响应速度(硬件总快于软件),是利用硬件来提升CPU芯片处理事件能力的一个有效方法。中断信号需要经过cpu的处理,然后调用中断处理函数进行处理。
这里先只讲解外部GPIO的中断,外设的中断后面再讲。
stm32的外部中断是按pin号进行分级管理的,共有16个管理器。

stm32的系统时钟和中断相关的操作的寄存器需要参考“Cortex-M3权威指南(中文)”和“STM32中文参考手册_V10”关于中断向量这些的定义是Cortex-M3相关,stm32官方直接使用的,基本外设这些寄存器是stm32官方根据需求自己定义的,所以对时钟和中断的配置需要参考两个的文档。
6.2、中断项链表偏移地址的配置以及分组设置(不是必须的,自己代码可以步配置,使用默认的)
使用到的寄存器如下所示,来自“Cortex-M3权威指南(中文)”。


下面是正点地中断向量表地址配置代码:
//设置向量表偏移地址
//NVIC_VectTab:基址
//Offset:偏移量
void MY_NVIC_SetVectorTable(u32 NVIC_VectTab, u32 Offset)
{ SCB->VTOR = NVIC_VectTab|(Offset & (u32)0x1FFFFF80);//设置NVIC的向量表偏移寄存器//用于标识向量表是在CODE区还是在RAM区
}
根据6.1中地前两个图可以知道如何设置寄存器地分组,下面是正点的中断优先级分组的代码:
//设置NVIC分组
//NVIC_Group:NVIC分组 0~4 总共5组
void MY_NVIC_PriorityGroupConfig(u8 NVIC_Group)
{ u32 temp,temp1; temp1=(~NVIC_Group)&0x07;//取后三位,分组数与位的设置正好是取反的关系temp1<<=8; //移位到寄存器AIRCR中断优先级分组配置位置temp=SCB->AIRCR; //读取先前的设置temp&=0X0000F8FF; //清空先前分组temp|=0X05FA0000; //写入钥匙,根据手册,改变寄存器必须写入钥匙temp|=temp1; SCB->AIRCR=temp; //设置分组
}
七、stm32内存架构
stm32的内存架构是非常重要的,有助于理解stm32的寄存器的配置,程序员经常关心和使用的内存区域主要有SRAM和Flash两个区域。
7.1、内存的基础知识点
RAM:随机存取存储器(英语:Random Access Memory,缩写:RAM),也叫主存,是与CPU直接交换数据的内部存储器。
ROM:(只读内存(Read-Only Memory)简称)英文简称ROM。ROM所存数据,一般是装入整机前事先写好的,整机工作过程中只能读出,而不像随机存储器那样能快速地、方便地加以改写。
内存有ROM(掉电不丢失,例如Flash)和RAM(掉电丢失,例如SRAM),下面是关于两种内存的介绍:

7.1.1、Flash
通过上图我们可以知道,FLASH属于 非易失性存储器:
FLASH又称为闪存,不仅具备电子可擦除可编程(EEPROM)的性能,还不会断电丢失数据同时可以快速读取数据,U盘和MP3里用的就是这种存储器。在以前的嵌入式芯片中,存储设备一直使用ROM(EPROM),随着技术的进步,现在嵌入式中基本都是FLASH,用作存储Bootloader以及操作系统或者程序代码或者直接当硬盘使用(U盘)。
Flash 主要有两种NOR Flash和NADN Flash:
NOR Flash的读取和我们常见的SDRAM的读取是一样,用户可以直接运行装载在NOR FLASH里面的代码,这样可以减少SRAM的容量从而节约了成本,可以随机读写。
NAND Flash没有采取内存的随机读取技术,它的读取是以一次读取一块的形式来进行的,通常是一次读取512个字节,采用这种技术的Flash比较廉价。用户不能直接运行NAND Flash上的代码,因此好多使用NAND Flash的开发板除了使用NAND Flah以外,还作上了一块小的NOR Flash来运行启动代码。
STM32单片机内部的FLASH为 NOR FLASH。
Flash 相对容量大,掉电数据不丢失,主要用来存储 代码,以及一些掉电不丢失的用户数据。
7.1.2、RAM
RAM 属于易失性存储器:
RAM随机存储器(Random Access Memory)表示既可以从中读取数据,也可以写入数据。当机器电源关闭时,存于其中的数据就会丢失。比如电脑的内存条。
RAM有两大类,一种称为静态RAM(Static RAM/SRAM),SRAM速度非常快,是目前读写最快的存储设备了,但是它也非常昂贵,所以只在要求很苛刻的地方使用,譬如CPU的一级缓冲,二级缓冲。另一种称为动态RAM(Dynamic RAM/DRAM),DRAM保留数据的时间很短,速度也比SRAM慢,不过它还是比任何的ROM都要快,但从价格上来说DRAM相比SRAM要便宜很多,计算机内存就是DRAM的。
为什么需要RAM,因为相对FlASH而言,RAM的速度快很多,所有数据在FLASH里面读取太慢了,为了加快速度,就把一些需要和CPU交换的数据读到RAM里来执行(注意这里不是全部数据,只是一部分需要的数据,这个在后面介绍STM32的内存管理中会提到)。
STM32单片机内部的 RAM 为 SRAM。
RAM相对容量小,速度快,掉电数据丢失,其作用是用来存取各种动态的输入输出数据、中间计算结果以及与外部存储器交换的数据和暂存数据。
总结:stm32的RAM内存为SRAM,ROM为Flash。
stm32的内存管理分为两部分,一部分是关于内核的,stm32参照Cortex-M内核进行内存映射地址的设置。另一部分是stm32根据不同单片机进行的内存地址的映射,两种地址设置的方式都是非常重要的,下面将对其进行张开叙述。
7.2、Cortex-M3的存储器映射
Cortex-M3的存储器映射是stm32单片机存储区映射的基本框架和标准。stm32单片机可以在此标准下进行关于自己的存储器映射的扩展。

存储器映射 是用 地址来表示 对象,因为Cortex-M3是32位的单片机,因此其PC指针可以指向2^32=4G的地址空间,也就是图中的 0x00000000到0xFFFFFFFF的区间,也就是将程序存储器、数据存储器、寄存器和输入输出端口被组织在同一个4GB的线性地址空间内,数据字节以小端格式存放在存储器中。
注意:上面介绍的内存的地址分布架构,只要单片机采用上面的架构,就需要遵守上面的地址规定,区域的首地址是不能改变的,不同的单片机使用的内存的大小是不一样的,首地址不能改变,后面不适用的映射地址可以保留。
前三个区域对于程序员来说是比较重要的。
7.3、stm32的存储器映射
STM32存储器映射表(选用的是STM32F103VE的,不同的型号Flash 和 SRAM 的地址空间不同,起始地址都是一样的),stm32地栈和堆的位置所需要的内存都在“ 64K大小的SRAM”的位置:

那么我们所需要分析的STM32 内存,就是图中 0X0800 0000开始的 Flash 部分 和 0x2000 0000 开始的SRAM部分,这里还要介绍一个和Flash模块相关的部分,从0X0800 0000开始的 Flash 部分,可以对其进行划分为:主存储块、信息块、闪存存储器接口寄存器。
STM32的Flash,严格说,应该是Flash模块。该Flash模块包括:Flash主存储区(Main memory)、Flash信息区(Informationblock),以及Flash存储接口寄存器区(Flash memory interface)。
主存储器,该部分用来存放代码和数据常数(如加const类型的数据)。对于大容量产品,其被划分为256页,每页2K,小容量和中容量产品则每页只有1K字节。主存储起的起始地址为0X08000000,B0、B1都接GND的时候,就从0X08000000开始运行代码。
信息块,该部分分为2个部分,其中启动程序代码,是用来存储ST自带的启动程序,用于下载,当B0接3.3V,B1接GND时,运行的就这部分代码,用户选择字节,则一般用于配置保护等功能。
闪存储器块,该部分用于控制闪存储器读取等,是整个闪存储器的控制机构。
对于主存储器和信息块的写入有内嵌的闪存编程管理;编程与擦除的高压由内部产生。
在执行闪存写操作时,任何对闪存的读操作都会锁定总线,在写完成后才能正确进行,在进行读取或擦除操作时,不能进行代码或者数据的读取操作。
7.4、stm32的代码中内存管理
STM32 的内存管理起始就是对0X0800 0000 开始的 Flash 部分 和 0x2000 0000 开始的 SRAM 部分使用管理。根据程序中的数据存储可以划分为6个存储数据段和3种存储属性区。

上面几个6个存储数据段和3种存储属性区,将从代码的层次进行分析。

(1)Code:
程序代码部分。
.text 段
放在ROM里面,就是Flash,需要占用flash空间
(2)RO-data
(Read Only)只读数据
程序定义的常量,只读数据,字符串常量(const修饰的)
.constdata 段
放在flash里面,需要占用flash空间
(3)RW-data
(Read Write)可读可写数据
已经初始化的全局变量和静态变量(就是static修饰的变量);
.data 段
需要在 RAM里面运行,但是起初需要保存在 Flash里面,程序运行后复制到 RAM里面运行,需要占用Flash空间。
(4)ZI-data
(Zero Initialize)未初始化的全局变量和静态变量,以及初始化为0的变量;
.BSS段
ZI的数据全部是0,没必要开始就包含,只要程序运行之前将ZI数据所在的区域(RAM里面)一律清 0,不占用Flash,运行时候占用RAM。
heap 和 stack 其实也属于 ZI,只不过他不是程序编译就能确定大小的,必须在运行中才会有大小,而是是变化的,因为RAM掉电丢失,所以 RW-data 数据也得下载到ROM(flash) 中,在运行的时候复制到 RAM中运行,如下图所示(图中的地址也是错的,应该是从0x0800 0000 开始):
由上我们得知:
程序占用 Flash = Code + RO data + RW data
程序运行时候占用 RAM = RW data + ZI data。
Code + RO data + RW data 的大小也是生成的 bin 文件的大小


在 startup_stm32fxxx.s 中我们可以看到关于 Stack_Size 和 Heap_Size的定义,图中的定义就是规定本程序中 栈 的大小为 1K, 堆的大小为 0.5K。
经过keil编译过后的文件会生成“test.map”的文件,这个文件中有编译之后的栈和堆的位置,但是这些都是不固定的,所以用户自己使用这段内存的时候,需要看堆的地址,也可以直接使用malloc进行堆上数据的申请,但是这种方式容易形成碎片内存的现象(例如刚开始申请了2,之后有申请了3,释放前面申请的2之后,在申请3的时候,前面的连续内存2就不能使用了,这样连续进行小容量的malloc申请,就会形成很对如前面没用的2的碎片内存。最后造成系统内存减少。)
7.5、stm32启动方式

第一种启动方式是最常用的用户FLASH启动,正常工作就在这种模式下,STM32的FLASH可以擦出10万次,所以不用担心芯片哪天会被擦爆!
第二种启动方式是系统存储器启动方式,即我们常说的串口下载方式(ISP),不建议使用这种,速度比较慢。STM32 中自带的BootLoader就是在这种启动方式中,如果出现程序硬件错误的话可以切换BOOT0/1到该模式下重新烧写Flash即可恢复正常。
第三种启动方式是STM32内嵌的SRAM启动。该模式用于调试。 用jlink在线仿真,则是下载到SRAM中。
以上三种启动方式我们都很熟悉,但是他的究竟是如何实现的呢?
我们先来看看《Cortex-M3权威指南》关于CM3复位后的动作:

当选择相应的启动方式时,对应的存储器空间被映射到启动空间(0x00000000)。
从闪存存储器启动:主闪存存储器被映射到启动空间(0x0000 0000) ,也就是0x08000000被映射到0x00000000。
从内嵌SRAM启动 :SRAM起始地址 0x2000 0000 被映射到0x00000000。
从系统存储器启动:系统存储器被映射到启动空间(0x0000 0000),也就是0x1FFF F000被映射到0x00000000。
(为什么是0x1FFF F000 可以查阅上文中的 2.2小节 STM32 的存储器映射分析,STM32互联型产品这个地址不一样,此地址由ST官方写入了一段BootLoader代码,可以通过官方BootLoader升级MCU固件,无法修改)。
参考链接:
STM32的内存管理相关(内存架构,内存管理,map文件分析)_stm32f103 malloc_矜辰所致的博客-CSDN博客
深入理解STM32内存管理_行稳方能走远的博客-CSDN博客
八、stm32时钟树基础总线地址
stm32对基本外设的管理都是通过总线进行管理的,外设的配置寄存器的操作形式为“基础地址+偏移地址”,其中的基础地址就是总线的地址。



stm32f1系列的基础地址的映射分为两部分,有部分是片上外设(eripheral_memory_map)如上图中“A”的位置。另一个映射区域为Cortex-M3的内核寄存器(/* Memory mapping of Cortex-M3 Hardware */)如图中的“B”的位置。在stm32f1的代码文件中,两种寄存器的定义分别在“stm32f10x.h”和“core_cm3.h”的代码文件里面。
下面是“stm32f10x.h”代码中对片上外设(eripheral_memory_map)基础总线地址的定义。
/** @addtogroup Peripheral_memory_map* @{*/#define FLASH_BASE ((uint32_t)0x08000000) /*!< FLASH base address in the alias region */
#define SRAM_BASE ((uint32_t)0x20000000) /*!< SRAM base address in the alias region */
#define PERIPH_BASE ((uint32_t)0x40000000) /*!< Peripheral base address in the alias region */#define SRAM_BB_BASE ((uint32_t)0x22000000) /*!< SRAM base address in the bit-band region */
#define PERIPH_BB_BASE ((uint32_t)0x42000000) /*!< Peripheral base address in the bit-band region */#define FSMC_R_BASE ((uint32_t)0xA0000000) /*!< FSMC registers base address *//*!< Peripheral memory map */
#define APB1PERIPH_BASE PERIPH_BASE
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
#define AHBPERIPH_BASE (PERIPH_BASE + 0x20000)#define TIM2_BASE (APB1PERIPH_BASE + 0x0000)
#define TIM3_BASE (APB1PERIPH_BASE + 0x0400)
#define TIM4_BASE (APB1PERIPH_BASE + 0x0800)
#define TIM5_BASE (APB1PERIPH_BASE + 0x0C00)
#define TIM6_BASE (APB1PERIPH_BASE + 0x1000)
#define TIM7_BASE (APB1PERIPH_BASE + 0x1400)
#define TIM12_BASE (APB1PERIPH_BASE + 0x1800)
.................等
下面是“core_cm3.h”代码文件中对Cortex-M3的内核寄存器(/* Memory mapping of Cortex-M3 Hardware */)的定义的情况。
/* Memory mapping of Cortex-M3 Hardware */
#define SCS_BASE (0xE000E000) /*!< System Control Space Base Address */
#define ITM_BASE (0xE0000000) /*!< ITM Base Address */
#define CoreDebug_BASE (0xE000EDF0) /*!< Core Debug Base Address */
#define SysTick_BASE (SCS_BASE + 0x0010) /*!< SysTick Base Address */
#define NVIC_BASE (SCS_BASE + 0x0100) /*!< NVIC Base Address */
#define SCB_BASE (SCS_BASE + 0x0D00) /*!< System Control Block Base Address */#define InterruptType ((InterruptType_Type *) SCS_BASE) /*!< Interrupt Type Register */
#define SCB ((SCB_Type *) SCB_BASE) /*!< SCB configuration struct */
#define SysTick ((SysTick_Type *) SysTick_BASE) /*!< SysTick configuration struct */
#define NVIC ((NVIC_Type *) NVIC_BASE) /*!< NVIC configuration struct */
#define ITM ((ITM_Type *) ITM_BASE) /*!< ITM configuration struct */
#define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE) /*!< Core Debug configuration struct */
上面比较重要的就是AHB、APB1、APB2、SysTick_BASE几个总线的地址的地址是比较重要的。
| 总线名称 | 总线基地址 |
|---|---|
| APB1 | 0x4000 0000 |
| APB2 | 0x4001 0000 |
| AHB | 0x4002 0000 |
相关文章:
stm32的时钟、中断的配置(针对寄存器),一些基础知识
一、学习参考资料 (1)正点原子的寄存器源码。 (2)STM32F103最小系统板开发指南-寄存器版本_V1.1(正点) (3)STM32F103最小系统板开发指南-库函数版本_V1.1(正点࿰…...
Vue14 监视属性简写
监视属性简写 当监视属性只有handler时,可以使用简写 <!DOCTYPE html> <html><head><meta charset"UTF-8" /><title>天气案例_监视属性_简写</title><!-- 引入Vue --><script type"text/javascript&…...
基于docker+Keepalived+Haproxy高可用前后的分离技术
基于dockerKeepalivedHaproxy高可用前后端分离技术 架构图 服务名docker-ip地址docker-keepalived-vip-iphaproxy-01docker-ip自动分配 未指定ip192.168.31.252haproxy-02docker-ip自动分配 未指定ip192.168.31.253 安装haproxy 宿主机ip 192.168.31.254 宿主机keepalived虚…...
安装配置deep learning开发环境
1. 下载安装anacondahttps://www.anaconda.com/download-success vim ~/.condarcchannels: - bioconda - https://mirrors.ustc.edu.cn/anaconda/pkgs/main/ - https://mirrors.ustc.edu.cn/anaconda/cloud/conda-forge/ - https://mirrors.tuna.tsinghua.edu.cn/anaco…...
Docker基础(CentOS 7)
参考资料 hub.docker.com 查看docker官方仓库,需要梯子 Docker命令大全 黑马程序员docker实操教程 (黑马讲的真的不错 容器与虚拟机 安装 yum install -y docker Docker服务命令 启动服务 systemctl start docker停止服务 systemctl stop docker重启…...
HTTP的基本格式
HTTP/HTTPS HTTPhttp的协议格式 HTTP 应用层,一方面是需要自定义协议,一方面也会用到一些现成的协议. HTTP协议,就是最常用到的应用层协议. 使用浏览器,打开网站,使用手机app,加载数据,这些过程大概率都是HTTP来支持的 HTTP是一个超文本传输协议, 文本>字符串 超文本>除…...
Qt元对象系统 day5
Qt元对象系统 day5 内存管理 QObject以对象树的形式组织起来,当为一个对象创建子对象时,子对象回自动添加到父对象的children()列表中。父对象拥有子对象所有权,比如父对象可以在自己的析构函数中删除它的孩子对象。使用findChild()或findC…...
【audio】alsa pcm音频路径
文章目录 AML方案音频路径分析dump alsa pcm各个音频路径的原始音频流数据 AML方案音频路径分析 一个Audio Patch用来表示一个或多个source端到一个或多个sink端。这个是从代码的注释翻译来的,大家可以把它比作大坝,可以有好几个入水口和出水口…...
NLP - 数据预处理 - 文本按句子进行切分
NLP - 数据预处理 - 文本按句子进行切分 文章目录 NLP - 数据预处理 - 文本按句子进行切分一、前言二、环境配置1、安装nltk库2、下载punkt分句器 三、运行程序四、额外补充 一、前言 在学习对数据训练的预处理的时候遇到了一个问题,就是如何将文本按句子切分&#…...
【轻松玩转MacOS】常用软件篇
引言 在本篇文章中,我将介绍如何安装和使用一些常用的软件,如Safari浏览器、邮件、日历、地图等。让我们一起来看看吧! 一、Safari浏览器 Safari是MacOS自带的浏览器,具有简洁、快速、安全的特点。 以下是一些Safari浏览器的使…...
Akshare简记
文章目录 基本信息安装Anaconda安装(推荐)Anaconda设置AKShare安装使用AKShare更新数据接口一览数据字典用例Hello WorldMFI指标SMA指标BOLL线指标股市新闻情绪判断市场情绪指标ARBR条件选股回测配对交易策略日线策略计算相近产品基本信息 线上文档:...
Jmeter常用断言之断言持续时间简介
Duration Assertion:断言持续时间。 断言持续时间通常用于做性能测试,一般用于检查HTTP请求的响应时间是否超过预期值。而这个响应时间是性能测试中常关注的一个性能指标。 一、添加断言方式 根据需要可在【测试计划】、【线程组】、【线程请求】下添加…...
C/C++/VS2022/指针/数组 调试出现debug
这个情况就很难受,编译没错,但是运行出现问题了,如果点击中止(重试、忽略)下一次运行还是会出现,看了显示的大致意思是在数组arry上出现了什么错误,经过检查发现,原来是数组在数入时…...
【设计模式】使用原型模式完成业务中“各种O”的转换
文章目录 1.原型模式概述2.浅拷贝与深拷贝2.1.浅拷贝的实现方式2.2.深拷贝的实现方式 3.结语 1.原型模式概述 原型模式是一种非常简单易懂的模型,在书上的定义是这样的: Specify the kinds of objects to create using a prototypical instance,and cre…...
[C++ 网络协议] IOCP(Input Output Completion Port)
1.什么是IOCP IOCP(Input Output Completion Port)输入输出完成端口。其实就是基于重叠I/O的一种改进的模型。 重叠I/O具有缺点:重复调用非阻塞模式的accpet函数和以进入alertablewait状态为目的的SleepEx函数会影响程序性能。 而IOCP提供…...
R实现地图相关图形绘制
大家好,我是带我去滑雪! 地图相关图形绘制具有许多优点,这些优点使其在各种领域和应用中非常有用。例如:地图相关图形提供了一种直观的方式来可视化数据,使数据更容易理解和分析。通过地图,可以看到数据的空…...
【Jmeter】性能测试脚本开发——性能测试环境准备、Jmeter脚本编写和执行
文章目录 一、常用的Jmeter元件二、性能测试环境准备三、编写Jmeter脚本四、执行测试脚本 一、常用的Jmeter元件 取样器-HTTP请求 作用:发送HTTP请求配置原件-HTTP请求默认值 作用:设置HTTP请求的默认参数配置原件-用户定义的变量 作用:定义…...
看好你家电视盒的后门!数千个Android电视盒感染了与欺诈相关的危险恶意软件
如果你从Android电视盒获得流媒体修复程序,则你的设备可能会被恶意软件所感染,这些恶意软件能够进行广告欺诈、创建假帐户,并通过悄悄地将你的数据转移到中国的服务器来销售对家庭网络的访问。 根据本周的一份新报告,网络安全公司…...
LeetCode 1251. 平均售价
题目链接:1251. 平均售价 题目描述 表:Prices Column NameTypeproduct_idintstart_datedateend_datedatepriceint (product_id,start_date,end_date) 是 prices 表的主键(具有唯一值的列的组合)。 price…...
TypeScript 笔记:String 字符串
1 对象属性 length 返回字符串的长度 2 对象方法 charAt() 返回在指定位置的字符 charCodeAt() 返回在指定的位置的字符的 Unicode 编码 concat 连接两个或更多的字符串 indexOf 返回某个指定的字符串值在字符串中首次出现的位置 lastIndexOf 从后向前搜索字符串&…...
Flask RESTful 示例
目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题: 下面创建一个简单的Flask RESTful API示例。首先,我们需要创建环境,安装必要的依赖,然后…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序
一、开发环境准备 工具安装: 下载安装DevEco Studio 4.0(支持HarmonyOS 5)配置HarmonyOS SDK 5.0确保Node.js版本≥14 项目初始化: ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...
浅谈不同二分算法的查找情况
二分算法原理比较简单,但是实际的算法模板却有很多,这一切都源于二分查找问题中的复杂情况和二分算法的边界处理,以下是博主对一些二分算法查找的情况分析。 需要说明的是,以下二分算法都是基于有序序列为升序有序的情况…...
python执行测试用例,allure报乱码且未成功生成报告
allure执行测试用例时显示乱码:‘allure’ �����ڲ����ⲿ���Ҳ���ǿ�&am…...
九天毕昇深度学习平台 | 如何安装库?
pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子: 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...
【生成模型】视频生成论文调研
工作清单 上游应用方向:控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...
Python 训练营打卡 Day 47
注意力热力图可视化 在day 46代码的基础上,对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...
通过MicroSip配置自己的freeswitch服务器进行调试记录
之前用docker安装的freeswitch的,启动是正常的, 但用下面的Microsip连接不上 主要原因有可能一下几个 1、通过下面命令可以看 [rootlocalhost default]# docker exec -it freeswitch fs_cli -x "sofia status profile internal"Name …...
Leetcode33( 搜索旋转排序数组)
题目表述 整数数组 nums 按升序排列,数组中的值 互不相同 。 在传递给函数之前,nums 在预先未知的某个下标 k(0 < k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...
