1
完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
1. 引言
看到这篇文章的应该都是做嵌入式的,都不是新手,可能大家都上手过一些片子,也开发过项目。 用下来感觉如何? MCU的门槛是很低的,现在的网上资料一大堆,课程满天飞,很多人都可以快速上手,厂家给的SDK也相对完善,可以说这部分很简单。 在这种情况下,只要你懂C语言和一些简单的外设原理,对着demo你就能开发。 在这个基础上,怎么样更深一步,真正的从开发中学到东西?而不是单纯的会抄demo而已? 从我站的不高的角度来分析,我觉得要深入思考一下现象下的本质,一些原理性的东西,底层的东西。 第一篇,先从stm32 系统的启动开始。 2. 程序的入口不是main 最开始学的时候以为main就是所有程序的入口,是大家约好的,后来接触了6410/2440这些,就会有疑惑,为啥stm32不用启动代码,再后来知道这些都是keil和st帮我们做了,很多东西其实我们就一眼带过了,便捷是便捷了,导致的原因可能就是很久都不清楚真正的原因。 那这一篇文章的目标就是: 能想象出一个芯片上电之后的样子,是怎么样跑的,PC怎么动的,堆栈怎么分的,内存是怎么样子下面我们用stm32f103c8t6,来真实的跑一遍。 3. 系统启动的流程 3.1 上电取址 刚上电,cotex-m3的内核默认会去0x0地址取出栈指针地址,然后偏移4个字节取出跳转地址。 在Cotex m3 权威指南中有介绍。 这里有个地方要注意:
如图,可以看出0x8000004的数据是0x80001ED,把0x80001ED填入PC,实际运行的是0x80001EC的代码,为什么不相等? PC 中的数据最低两位并不代表真实的取址地址。ARM中使用最低一位来判断这条指令是 ARM 指令还是 Thumb 指令,若最低位为 0,代表 ARM 指令;若最低位为 1,代表 Thumb 指令。在 Cortex-M/R 内核中,并不支持 ARM 模式,若强行切换到 ARM 模式会引发一个 Hard Fault。3.2 地址映射 看到这里可能有朋友不理解,我们的代码明明是load到0x8000000地址的,为什么这里是从0x0开始运行的。 这里,其实是ST做了一个地址的映射,根据boot0,boot1的组合,把0x8000000(flash)映射到了0x0。这其实也就是我们熟悉的启动mode的选择。 STM32把从0x00000000到0x0005FFFF的区域作为启动空间(boot space)的别名区。 referenc manual这里可以看出,ST可以把0x8000000或者片内固化启动代码的地方映射为0。 关于RAM启动,即0x20000000,我看到两种说法, 一种是,0x2000 0000 无法映射到0x0,所以要重设中断向量表。 另一种说法是,0x2000 0000 映射到了0x0,但是在启动后断开了,所以无法通过0x0的地址去访问RAM。 这部分我有点疑惑,后面又专门做了这方面的验证:stm32深入思考(2) 之 RAM启动 引用一段在别的帖子里看到的: 所有的处理器PC指针复位值都会是0,所以一定会在0地址开始执行代码。这部分是ST做的,这样,系统上电就会直接到0x8000000,也就是我们下载了bin的地方。 3.3 bin是什么 bin是什么这里就不细说了,就是镜像文件,还有和hex的区别关系什么的,大家应该都清楚,简而言之,就是bin是啥样,flash就是啥样,一模一样的。所以做IAP升级的时候只能升级bin格式进去。 3.2我们说到,系统会从0x800 0000取MSP,从0x8000004取PC的初始值。 而我们的bin就是直接load到0x8000000的,也就是说,编译的东西,放在最前面的是什么? 查生成文件的排列顺序,一般就是直接看链接文件的,但是我在keil里面有找到.ld/.lds文件这些,有点晕,后来在输出文件夹找到一个sct文件,打开是这样的。 ; ************************************************************* ; *** Scatter-Loading Description File generated by uVision *** ; ************************************************************* LR_IROM1 0x08000000 0x00010000 { ; load region size_region ER_IROM1 0x08000000 0x00010000 { ; load address = execution address *.o (RESET, +First) *(InRoot$$Sections) .ANY (+RO) } RW_IRAM1 0x20000000 0x00004000 { ; RW data .ANY (+RW +ZI) } } 这里rom和ram的配置,和我在option里设置的是一样的。 但是这里我们要注意一句话,就是: ** *.o (RESET, +First) ** 把RESET段放在第一个。 OK,那找到了,RESET段在哪? 搜索一下代码,在start.s (startup_stm32f10x_hd.s)里。 这一段就是DCD了一些空间,放在最上面的第一二句: __Vectors DCD __initial_sp ; DCD Reset_Handler ; 就是0x8000000和0x8000004位置的东西。 可以看出,正是一个是MSP(__initial_sp),一个是系统入口( Reset_Handler装入PC) 3.4 start.s文件 这个文件是启动文件,是st公司提供的,根据不同的容量,有startup_stm32f10x_cl.s、startup_stm32f10x_hd.s、startup_stm32f10x_hd_vl.s。。。等等。 内部具体源码的分析,在CSDN有太多太多了,一抓一大把,这里就不细细描述了,直接拿我们要的东西。
AREA RESET, DATA, READONLY ;复位段,只包含数据,只读 ...
Reset_Handler PROC EXPORT Reset_Handler [WEAK] IMPORT __main IMPORT SystemInit LDR R0, =SystemInit ; 装载寄存器指令 BLX R0 ; 带链接的跳转,切换指令集 LDR R0, =__main BX R0 ; 切换指令集,main函数不返回 ENDP
_user_initial_stackheap ; 此处是初始化两区的堆栈空间,堆是从由低到高的增长,栈是由高向低生长的,两个是互相独立的数据段,并不能交叉使用。 LDR R0, = Heap_Mem LDR R1, = (Stack_Mem + Stack_Size) LDR R2, = (Heap_Mem + Heap_Size) LDR R3, = Stack_Mem BX LR 3.5 Reset_Handler start.s里的Reset_Handler 是和流程有关的,系统上电,PC取到Reset_Handler入口,跳转过来,然后运行,看代码。 ; Reset handler Reset_Handler PROC EXPORT Reset_Handler [WEAK] IMPORT __main IMPORT SystemInit LDR R0, =SystemInit BLX R0 LDR R0, =__main BX R0 ENDP 很明显看出,系统是调用了SystemInit 和 __main两个函数。 3.6 SystemInit SystemInit 是在system_stm32f10x.c中, 看代码 /** * @brief Setup the microcontroller system * Initialize the Embedded Flash Interface, the PLL and update the * SystemCoreClock variable. * @note This function should be used only after reset. * @param None * @retval None */ void SystemInit (void) { /* Reset the RCC clock configuration to the default reset state(for debug purpose) */ /* Set HSION bit */ RCC->CR |= (uint32_t)0x00000001; /* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */ #ifndef STM32F10X_CL RCC->CFGR &= (uint32_t)0xF8FF0000; #else RCC->CFGR &= (uint32_t)0xF0FF0000; #endif /* STM32F10X_CL */ /* Reset HSEON, CSSON and PLLON bits */ RCC->CR &= (uint32_t)0xFEF6FFFF; /* Reset HSEBYP bit */ RCC->CR &= (uint32_t)0xFFFBFFFF; /* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */ RCC->CFGR &= (uint32_t)0xFF80FFFF; #ifdef STM32F10X_CL /* Reset PLL2ON and PLL3ON bits */ RCC->CR &= (uint32_t)0xEBFFFFFF; /* Disable all interrupts and clear pending bits */ RCC->CIR = 0x00FF0000; /* Reset CFGR2 register */ RCC->CFGR2 = 0x00000000; #elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL) /* Disable all interrupts and clear pending bits */ RCC->CIR = 0x009F0000; /* Reset CFGR2 register */ RCC->CFGR2 = 0x00000000; #else /* Disable all interrupts and clear pending bits */ RCC->CIR = 0x009F0000; #endif /* STM32F10X_CL */ #if defined (STM32F10X_HD) || (defined STM32F10X_XL) || (defined STM32F10X_HD_VL) #ifdef DATA_IN_ExtSRAM SystemInit_ExtMemCtl(); #endif /* DATA_IN_ExtSRAM */ #endif /* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */ /* Configure the Flash Latency cycles and enable prefetch buffer */ SetSysClock(); #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 } 主要做了两个事,
__main这个函数我单步跟踪没有跟进去,暂时看不到是怎么实现的,只能看下汇编: 观察R0寄存器,说明__main就是在0x8000131这里。 找到对应的位置,可以看到跳转的是__scatterload函数。 为什么不是131而是130,和之前用0x80001ED跳转 0x80001EC的道理一样。 抄一张经典图 3.8 __main之__scatterload 这个函数看介绍,作用是: 负责把RW/RO输出段从装载域地址复制到运行域地址,并完成了ZI运行域的初始化工作。这个函数我没找到源码,但是综合流程,应该就是把flash中bin文件的RW/ZI段等一些运行中需要修改的内容拷贝到RAM,同时把ZI段(我理解就是BSS + heap + stack)清零。 3.9 __main之__rt_entry 负责初始化堆栈,完成库函数的初始化,最后自动跳转向main()函数 3.10 main OK,到达目的地,成功抵达main函数。 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1771 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1619 浏览 1 评论
1070 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
724 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1673 浏览 2 评论
1936浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
729浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
569浏览 3评论
594浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
552浏览 3评论
小黑屋| 手机版| Archiver| 德赢Vwin官网 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-22 18:15 , Processed in 0.851995 second(s), Total 77, Slave 61 queries .
Powered by 德赢Vwin官网 网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
德赢Vwin官网 观察
版权所有 © 湖南华秋数字科技有限公司
德赢Vwin官网 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号