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、PLLXTPREHSE分频器作为PLL输入 (HSE divider for PLL entry) (属于RCC_CFGR

2、PLLMULPLL倍频系数 (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个可屏蔽中断通道(不包含16Cortex™-M3的中断线,16个可编程的优先等级(使用了4位中断优先级)

        STM32 将中断分为 5 个组,组 0~4 。该分组的设置是由 SCB->AIRCR 寄存器的 bit10~8 来定义的。具体的分配关系如表 4.5.1 所示:

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

SETENAs: xE000_E100 – 0xE000_E11C ; CLRENAs:0xE000E180 - 0xE000_E19C

SETPENDs:0xE000_E200 – 0xE000_E21C ; CLRPENDs:0xE000E280 - 0xE000_E29C

  ------------------------------------------------------------------------------------------------------------------------------

        中断和事件的区别:

        事件机制提供了一个完全有硬件自动完成的触发到产生结果的通道,不要软件的参与,降低了CPU的负荷,节省了中断资源,提高了响应速度(硬件总快于软件),是利用硬件来提升CPU芯片处理事件能力的一个有效方法。中断信号需要经过cpu的处理,然后调用中断处理函数进行处理。
        

这里先只讲解外部GPIO的中断,外设的中断后面再讲。

STM32F103 19 个外部中断为:
        线 0~15 :对应外部 IO 口的输入中断。
        线 16 :连接到 PVD 输出。
        线 17 :连接到 RTC 闹钟事件。
        线 18 :连接到 USB 唤醒事件。

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   */

上面比较重要的就是AHBAPB1APB2SysTick_BASE几个总线的地址的地址是比较重要的。

总线名称总线基地址
APB10x4000 0000
APB20x4001 0000
AHB0x4002 0000

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/151690.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++基础——基础语法

1 注释 C支持单行注释和多行注释。 单行注释 // 注释内容单行注释直到改行末尾&#xff0c;可以与代码放在同一行&#xff0c;在代码后面注释 多行注释 /* 注释内容 */包含在其中的都会被注释 2 变量 变量的作用是给指定的内存空间起名&#xff0c;方便操作这段内存。变…

哈希应用之位图

文章目录 1.位图概念2.面试题引入3.代码解决[配注释]4.位图应用4.1找到100亿个整数里只出现一次的整数4.2找两个分别有100亿个整数的文件的交集[只有1G内存]1.法一[使用于数据量<42亿]2.法二[适用于数据量大>42亿]3.在一个有100亿个int的文件中找到出现次数不超过2次的所…

AI伦理:如何确保人工智能的公平与透明

文章目录 什么是AI伦理&#xff1f;AI公平性AI透明性 为什么AI公平性和透明性重要&#xff1f;确保AI公平性的方法1. 数据收集和准备2. 算法和模型3. 解释和可解释性4. 持续监测 确保AI透明性的方法1. 记录决策2. 可解释性工具3. 用户教育 AI伦理的挑战和未来结论 &#x1f389…

STM32MP157汇编流水灯

.text .global _start _start: /* 使能GPIOE、GPIOF寄存器 RCC_MP_AHB4ENSETR * 基地址: 0x50000000 偏移地址: 0xA28 0x50000A28* RCC_MP_AHB4ENSETR[4]->1 RCC_MP_AHB4ENSETR[5]->1*/ LDR R0,0x50000A28LDR R1,[R0]ORR R1,R1,#(0x1<<4)STR R1,[R0]LDR R0,0x…

C++ 学习系列 -- std::list

一 std::list 介绍 list 是 c 中的序列式容器&#xff0c;其实现是双向链表&#xff0c;每个元素都有两个指针&#xff0c;分别指向前一个节点与后一个节点 链表与数组都是计算机常用的内存数据结构&#xff0c;与数组连续内存空间不一样的地方在于&#xff0c;链表的空间是不…

【Java 进阶篇】HTML块级元素详解

HTML&#xff08;Hypertext Markup Language&#xff09;是用于创建网页的标记语言。在HTML中&#xff0c;元素被分为块级元素和内联元素两种主要类型。块级元素通常用于构建网页的结构&#xff0c;而内联元素则嵌套在块级元素内&#xff0c;用于添加文本和其他内容。本文将重点…

异常:找不到匹配的key exchange算法

目录 问题描述原因分析解决方案 问题描述 PC 操作系统&#xff1a;Windows 10 企业版 LTSC PC 异常软件&#xff1a;XshellPortable 4(Build 0127) PC 正常软件&#xff1a;PuTTY Release 0.74、MobaXterm_Personal_23.1 服务器操作系统&#xff1a;OpenEuler 22.03 (LTS-SP2)…

Ubuntu 22.04 安装系统 手动分区 针对只有一块硬盘 lvm 单独分出/home

自动安装的信息 参考自动安装时产生的分区信息 rootyeqiang-MS-7B23:~# fdisk /dev/sdb -l Disk /dev/sdb&#xff1a;894.25 GiB&#xff0c;960197124096 字节&#xff0c;1875385008 个扇区 Disk model: INTEL SSDSC2KB96 单元&#xff1a;扇区 / 1 * 512 512 字节 扇区大…

phpstudy本地域名伪静态

环境&#xff1a;WNMP(Windows10 Nginx1.15.11 MySQL5.7.26 【PHP 7.4.3 (cli) (built: Feb 18 2020 17:29:57) ( NTS Visual C 2017 x64 ) 】) 使用PhpStudy配置本地域名后&#xff0c;设置伪静态&#xff0c;这样在Web端打开网站就不需要输入index.php了&#xff0c;很简单…

架构方法、模型、范式、治理

从架构方法、模型、范式、治理等四个方面介绍架构的概念和方法论、典型业务场景下的架构范式、不同架构的治理特点这3个方面的内容

Pycharm操作git仓库 合并等

菜单 Git CommitPushUpdate ProjectPullFetchMergreRebase 查询 查询分支 查询本地所有分支 # 查询本地分支 git branch# 查询远程分支 git branch -rPycharm查看当前分支 步骤&#xff1a; Git->Branches 哈喽&#xff0c;大家好&#xff0c;我是[有勇气的牛排]&…

ELK集群 日志中心集群

ES&#xff1a;用来日志存储 Logstash:用来日志的搜集&#xff0c;进行日志格式转换并且传送给别人&#xff08;转发&#xff09; Kibana:主要用于日志的展示和分析 kafka Filebeat:搜集文件数据 es-1 本地解析 vi /etc/hosts scp /etc/hosts es-2:/etc/hosts scp /etc…

视频转GIF:快速生成有趣的动态图片

随着社交媒体的快速发展&#xff0c;GIF动态图片已经成为了人们表达情感、分享生活片段的重要方式。将视频片段转换成GIF动态图片&#xff0c;可以让人们更好地分享和表达自己的情感&#xff0c;也可以让一些有趣的瞬间变得更加生动有趣。本文将介绍如何将视频快速转换成GIF动态…

微信小程序wxs标签 在wxml文件中编写JavaScript逻辑

PC端开发 可以在界面中编写JavaScript脚本 vue/react这些框架更是形成了一种常态 因为模板引擎和jsx语法本身就都是在js中的 我们小程序中其实也有类似的奇妙写法 不过先声明 这东西不是很强大 我们可以先写一个案例代码 wxml代码参考 <view><wxs module"wordSt…

Mac 点击桌面 出现黑边框 解决

1、桌面黑框效果 2、解决&#xff1a;设置为 仅在台前调度中

数据结构-顺序存储二叉树

文章目录 目录 文章目录 前言 一 . 什么是顺序存储二叉树 二 . 模拟实现 前序遍历 总结 前言 大家好,今天给大家讲一下顺序存储二叉树 一 . 什么是顺序存储二叉树 顺序存储二叉树是一种将二叉树的节点按照从上到下、从左到右的顺序存储在数组中的方法。具体来说&#xff0c;顺…

文件操作 和 IO - 详解

一&#xff0c;认识文件 1.1 树形结构组织和目录 文件是对于"硬盘"数据的一种抽象&#xff0c;在一台计算机上&#xff0c;有非常多的文件&#xff0c;这些文件是通过 "文件系统" 来进行组织的&#xff0c;本质上就是通过 "目录"(文件夹) 这样…

WebGoat 靶场 JWT tokens 四 五 七关通关教程

文章目录 webGoat靶场第 四 关 修改投票数第五关第七关 你购买书&#xff0c;让Tom用户付钱 webGoat靶场 越权漏洞 将webgoat-server-8.1.0.jar复制到kali虚拟机中 sudo java -jar webgoat-server-8.1.0.jar --server.port8888解释&#xff1a; java&#xff1a;这是用于执行…

MySQL命令行中文乱码问题

MySQL命令行中文乱码问题&#xff1a; 命令行界面默认字符集是gbk&#xff0c;若字符集不匹配会中文乱码或无法插入中文。 解决办法&#xff1a;执行set names gbk; 验证&#xff1a; 执行命令show variables like ‘char%’;查看默认字符集。 创建数据库设置字符集utf8&…

2023旅游产业内容营销洞察报告:如何升级经营模式,适配社媒新链路

2023年我国旅游业强劲复苏&#xff0c;上半年旅游消费增长显著&#xff0c;政府出台一系列文旅扶持政策后&#xff0c;旅游业也在积极寻求数字化转型的升级方式。 上半年以旅游消费为代表的服务业对经济的增长贡献率超过60%&#xff0c;旅游企业普遍实现经营好转&#xff0c;企…