1、STM32F405RGT6芯片结构
ARM Cortex-M4 MCU,具有1 MB Flash、168 MHz CPU和ART加速器
数据手册下载链接:
https://www.st.com/resource/en/datasheet/stm32f405rg.pdf
官方链接:
https://www.st.com/zh/microcontrollers-microprocessors/stm32f405rg.html#tools-software
2、Flash
stm32f405内部flah是1M(1024K)大小,其结构划分如图所示:
STM32F4 的闪存模块由:主存储器、系统存储器、 OPT 区域和选项字节等 4 部分组成:
主存储器:该部分用来存放代码和数据常数(如 const 类型的数据),分为 12 个大小不同的扇区,主存储器的起始地址是 0X08000000。
系统存储器:这个区主要用来存放 STM32F4 的 bootloader 代码,此代码是出厂的时候就固化在芯片内部了,比例用串口下载程序时的bootload(ISP下载)。
OTP 区域:即一次性可编程区域,一次性的,写完一次,永远不能擦除。
选项字节:用于配置读保护、 BOR 级别、软件/硬件看门狗以及器件处于待机或停止模式下的复位,相当于一些寄存器位。
在执行闪存写操作时,任何对闪存的读操作都会锁住总线,在写操作完成后读操作才能正确地进行;既在进行写或擦除操作时,不能进行代码或数据的读取操作。所以在写的时候有必要写FLASH_DataCacheCmd(DISABLE);来禁止数据缓存,写完后再打开。
flash擦除后每个地址块的数据是0xff,擦除就是将地址内的数据转变为0xff,写入数据就是将有关位置0;
3、RAM, ROM
这里所说的内存,是指RAM,RAM包括SRAM,DRAM等。
这里所说的flash,指的是用作为ROM的存储器,保存代码与常量数据。
栈的生长方向:指的是入栈方向,从高地址向低地址生长叫做向下生长,或逆向生长;反过来就叫向上生长,或正向生长。STM32的栈是向下生长。
(1)栈区(stack):由编译器自动分配和释放,存放函数的参数与返回值、局部变量等,其操作方式类似于数据结构中的栈。
(2)堆区(heap):由程序员分配管理。
(3)全局区与静态区(data):存储全局变量和静态变量,其中初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。
(4)文字常量区 [constdata(常量)]:常量字符串存放。
(5)程序代码区 [main()]:存放程序代码。
0X20000000是RAM开始地址。可以看到各段的分布。从低地址到高地址,分别为:data段,bss段,heap段,stack段。包含对应上面所说的(1)(2)(3)。
0X08000000是ROM(flash)开始地址。可以看到constdata(常量),和函数代码,对应上面的(4)(5);
其中,data指的是初始化不为0的全局或静态(static)变量。bss指的是没有初始化,或者初始化为0的全局或静态变量。
全局与静态变量的初始值,是需要保存下来,其基本可以分为三大类,一种是等于0的,一种是不等于0,还有一种是没有初始值的。
这些都是需要记录下来保存在镜像文件里面,再烧录到单片机的flash里(当然非要写到ram也可以)。为了节省空间,就把等于0的变量和没有初始值的变量归为一类,都当做初始值等于0的变量处理。因为这些变量的初始值都为0,所以记录也方便,节省不少空间。比如定义一个500字节的全局数组,要是初始值已经定义,那么镜像文件也需要相应大小的空间记录下来。但是如果全部为0,或者没有定义初始值。那么,从原理上来说,只需记录数组长度,赋予BSS“属性”。
这样分类,可以节省镜像文件中的空间大小。
4、ISP,IAP
ISP(In-System Programming)在系统可编程,指电路板上的空白器件可以写入最终用户代码, 而不需要从电路板上取下器件,已经编程的器件也可以用ISP方式擦除或再编程。
IAP(In-Application Programming) 指MCU可以在系统中获取新代码并对自己重新编程,即可用程序来改变程序。
ISP技术的优势是不需要编程器就可以进行单片机的实验和开发,单片机芯片可以直接焊接到电路板上,调试结束即成成品,免去了调试时由于频繁地插入取出芯片对芯片和电路板带来的不便。比如可以通过串口给32下载程序。
IAP技术是从结构上将Flash存储器映射为两个存储体,当运行一个存储体上的用户程序时,可对另一个存储体重新编程,之后将程序从一个存储体转向另一个。
IAP的编写流程
由Bootloader负责检测SD卡中是否有固件更新所需的BIN文件,或者通过SPI、CAN、以太网等方式获取BIN文件。
如果获取到所需要的BIN文件,则开始复制文件更新固件,更新结束后跳转到指定的地址开始执行最新的程序。
#define FLASH_OLDAPP_ADDR 0x08010000//FLASH_Sector_4
#define FLASH_APP_ADDR 0x08080000//应用程序起始地址128Kb
typedef void (*pFunction)(void);
pFunction Jump_To_Application;
void execute_app()
{
uint32_t JumpAddress = 0;
uint32_t Jumpadd = FLASH_OLDAPP_ADDR;
USART_DeInit(USART1);
if (upgrade_flag == 1)//1111-升级完成
{
Jumpadd = FLASH_APP_ADDR;
}
JumpAddress = *(__IO uint32_t *)(Jumpadd + 4);//用户代码区第二个字存储维新程序起始地址
Jump_To_Application = (pFunction)JumpAddress;
__set_MSP(*(__IO uint32_t *)Jumpadd); //初始化APP堆栈指针(用户代码区的第一个字用于存储栈顶地址)
Jump_To_Application(); //设置PC指针为新程序复位中断函数的地址
}
RAM:随机存取器,内存
ROM :只读存储器,flash,保存代码,立即数,常数,常数字符串。
1024 kB ROM (flash), 4 kB、192 kB RAM
静态存储区的大小是,192k
程序启动
程序启动后,将首先从“中断向量表”取出复位中断向量执行复位中断程序完成启动,当复位中断程序运行完成后才跳转到main函数。由此可见,在最后一步的设计中需要根据存放APP程序的起始地址以及中断向量表来设置栈顶地址,并获取复位中断地址跳转到复位中断程序。
内置Flash的分配情况大致如下:
STM32Fx有一个中断向量表,这个中断向量表存放代码开始部分的后4个字节处(即0x08000004),代码开始的4个字节存放的是栈顶地址。
当发生中断后程序通过查找该表得到相应的中断服务程序入口,然后跳到相应的中断服务程序中执行。
1)上电后从0x08000004处取出复位中断向量的地址,然后跳转到复位中断程序入口。
2)执行结束后跳转到main函数。
3)在执行main函数的过程中发生中断,则STM32强制将PC指针指回中断向量表处。
4)从中断向量表中找到相应的中断函数入口地址,跳转到相应的中断服务函数
5)执行完中断服务函数后再返回到main函数中来。
IAP方案,程序启动
在内置的Flash里面添加一个BootLoader程序,BootLoader程序和user application各有一个中断向量表,假设BootLoader程序占用的空间为N+M字节,则程序的走向应该如下图所示:
#define
#define 是个宏,宏本质就是个定义一个符号
宏与定义变量没有关系,更说不上是放在ROM还是RAM 中了。
#define a 0 的意思是:用符号a表示0。
宏定义 #define只是在预编译时简单的文字替换,其实并没有定义任何的变量。
#define a 1
uchar i;
main()
{i=a;}
其实在预编译以后,程序就成了{i=1;}程序里面根本就没有a。
猜测sizeof得到的并不是a,是宏定义下的那个东西的长度。因为sizeof(a),预编译后就是sizeof(1)。
typedef
在编程中使用typedef目的一般有两个:
1) 一个是给变量一个易记且意义明确的新名字
typedef long byte_4;//给已知数据类型long起个新名字,叫byte_4。
typedef signed char int8_t;
typedef signed short int int16_t;
typedef signed int int32_t;
2) 一个是简化一些比较复杂的类型声明
typedef struct tagStruct
{
int iNum;
long Length;
} Struct;
这个typedef和结构体,结合使用的方法,有以下两个操作:
- 定义一个新的结构类型
struct tagMyStruct
{
int iNum;
long lLength;
};
- typedef为这个新的结构起了一个名字,叫MyStruct。
typedef struct tagMyStruct MyStruct;
定义结构体指针,使用以下方式:
struct tagNode
{
char *pItem;
struct tagNode *pNext;
};
typedef struct tagNode *pNode;