完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
在接触ARM Cortex M嵌入式开发的初期,很多人直接使用芯片厂商提供的IDE进行编程。新建程序后,开发者只需在main()函数中插入自己的程序。IDE会自动生成其他所需的文件,其中就包括了在main()函数之前的初始化步骤。但是很多时候厂商提供的驱动并不能满足开发者的需求或者厂商的驱动过于复杂。这时候就要求开发者自己深入了解ARM Cortex M架构的基本初始化过程来进行自定义开发。
以STM32F41和STM32CubeIDE2为例,如果我们让main()为空循环。最终IDE生成的程序也有700字节。那这700字节的程序都做了些什么呢? 以STM32F41和STM32CubeIDE2为例,如果我们让main()为空循环。最终IDE生成的程序也有700字节。那这700字节的程序都做了些什么呢? #include int main(void) { while(1) { } } 编译输出 arm-none-eabi-size stm32_mini.elf text data bss dec hex filename 724 12 1572 2308 904 stm32_mini.elf 内存分布(Memory Layout) 在讨论初始化之前先要简单介绍一下程序在MCU片上闪存(Flash)存储方式。一般基于ARM Cortex M架构的MCU都会有片上闪存。这个闪存会有一个由芯片厂商自己定义分配好的地址。例如STM32F4片上闪存起始地址为0x08000000。编译好的程序通过调试接口(JTAG或SWD)烧录入闪存。闪存地址起始处存放的是中断向量表(Interrupt Vector Table)。中断向量表每行为32bits的地址,总长度由芯片中断数决定。ARM Cortex M架构规定了中断向量表前16行的内容。第一行为入口函数地址。第二行为栈指针地址。 结合上面STM32F4的例子,我们可以读出闪存地址0x08000000的数据。 0x08000000: 0x08000208 如果我们打开编译器生成的stm32_mini.map文件我们可以看到在地址0x08000208处存放的是Reset_Handler()也就是我们要寻找的入口函数。 .text.Reset_Handler 0x0000000008000208 0x50 Core/Startup/startup_stm32f401retx.o 0x0000000008000208 Reset_Handler 上电复位 MCU硬件默认中断向量表(Interrupt Vector Table)存于闪存起始地址。上电或MCU复位后,MCU把第一行入口函数地址载入寻址寄存器(PC),第二行栈指针地址载入栈指针寄存器(SP)。然后开始执行入口函数。 入口函数(Reset_Handler) 入口函数的目标是简单高效,迅速完成初始化后跳入用户程序。所以其一般都是由汇编语言写成。每个厂商提供的入口函数会略有不同,但是基本上都会有以下这些功能: (设置SP寄存器) 初始化DATA区域 清零BSS区域 执行系统初始化函数SystemInit() 跳入主函数main() 设置SP寄存器一般由硬件完成所以很多入口函数中都会省略这一步。 剩下的步骤我们以ARM CMSIS库示例程序startup_ARMCM4.S为样本逐一进行分析。 初始化DATA区域 DATA区域用于存储需要初始化的全局或者静态变量。在程序运行时这些变量存储在内存RAM中预定的地址范围内。MCU在上电后,RAM中内容为无意义的随机数据。程序需要从闪存中把初始化数据拷贝到RAM里对全局变量和静态变量进行初始化。如果不进行初始化,在第一次使用这些变量时,变量内容为垃圾数据会导致系统无法正常运行或崩溃。 startup_ARMCM4.S line 174 ~ line 192为单一DATA区域的初始化程序。__data_start__ 和__data_end__为DATA区域在RAM中的起始和终止地址。__etext为DATA区域在闪存中的地址。 /* Single section scheme. * * The ranges of copy from/to are specified by following symbols * __etext: LMA of start of the section to copy from. Usually end of text * __data_start__: VMA of start of the section to copy to * __data_end__: VMA of end of the section to copy to * * All addresses must be aligned to 4 bytes boundary. */ ldr r1, =__etext ldr r2, =__data_start__ ldr r3, =__data_end__ .L_loop1: cmp r2, r3 ittt lt ldrlt r0, [r1], #4 strlt r0, [r2], #4 blt .L_loop1 清零BSS区域 BSS区域用于存储无需初始化或者初始化为零的全局或者静态变量。与DATA区域相同,这些变量存储与内存RAM中。如果不对BSS区域清零,在第一次使用BSS区域内的变量时,程序会读出垃圾数据然后导致系统崩溃。 startup_ARMCM4.S line 233 ~ line 249为单一BSS区域的初始化程序。__bss_start__ 和__bss_end__为BSS区域在RAM中的起始和终止地址。 /* Single BSS section scheme. * * The BSS section is specified by following symbols * __bss_start__: start of the BSS section. * __bss_end__: end of the BSS section. * * Both addresses must be aligned to 4 bytes boundary. */ ldr r1, =__bss_start__ ldr r2, =__bss_end__ movs r0, 0 .L_loop3: cmp r1, r2 itt lt strlt r0, [r1], #4 blt .L_loop3 系统初始化(SystemInit) 完成了全局变量清零和初始化就可以开始执行系统初始化函数了。系统初始化函数一般对硬件进行最基本的初始化。例如初始化时钟设置(clock),看门狗(watchdog),中断向量表位移设置。具体功能由芯片需求决定,开发者也可以加入自定义的功能。 #ifndef __NO_SYSTEM_INIT bl SystemInit #endif STM32CubeIDE生成的STM32F4的SystemInit()函数。 void SystemInit(void) { /* FPU settings ------------------------------------------------------------*/ #if (__FPU_PRESENT == 1) && (__FPU_USED == 1) SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */ #endif #if defined (DATA_IN_ExtSRAM) || defined (DATA_IN_ExtSDRAM) SystemInit_ExtMemCtl(); #endif /* DATA_IN_ExtSRAM || DATA_IN_ExtSDRAM */ /* Configure the Vector Table location add offset address ------------------*/ #ifdef VECT_TAB_SRAM SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */ #else SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */ #endif } 跳入主函数 示例程序中,系统初始化后会执行_start函数。这里_start可以被替代为开发者可以自定义任何函数名。如果#define __START main,则跳入主函数main()。在GCC编译器里,如果没有定义__START,_start函数为gcc标准库自带的初始化函数。这个初始化函数结束后会跳入main()。 #ifndef __START #define __START _start #endif bl __START 以上这些初始化程序适用于所有基于ARM Cortex M架构的MCU。只有SystemInit()是根据芯片特点进行单独编写的。 结语 了解ARM Cortex M架构的最基本初始化步骤有助于 实现可移植代码。通用的初始化程序可以在所有ARM Cortex M架构MCU上运行。 在需要对代码进行裁剪时有效的去除非必须的系统初始化代码。 开发者也可以修改厂商提供的驱动来实现更多自定义功能。 |
|
|
|
|
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1627 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1550 浏览 1 评论
984 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
688 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1601 浏览 2 评论
1867浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
650浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
518浏览 3评论
536浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
506浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-24 19:19 , Processed in 0.726391 second(s), Total 78, Slave 62 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号