stm32代码_stm32 启动代码 实现|C语言

本文深入探讨STM32启动代码细节,包括ld脚本配置、中断向量表设置及初始化过程。通过手动实现启动代码,揭示数据段(.data)与未初始化数据段(.bss)如何在RAM中正确初始化。

前面写过一篇文章,STM32 启动代码分析 。这个里面的ld .*.s 都是自动生成的,为了弄清原理自己手动实现一个

先上三个代码。stm32.ld, start.s, startup.c

bfcb79ac226870962415ea2aa298594b.png

stm32.ld

e93a2484d9f46fd1442b6f0fef51f917.png

start.s

407dcf0153d18eb29dc2be99c658d05f.png

startup.c

5d17faf954061e9aa7197636a9eac056.png

startup.c2

先看看stm32.ld 连接脚本。

248cbaa8f4b10ebad3454db5bf3e4ec5.png

第1行,设置连接脚本的entry 函数为Reset_hander。也可以删除。

2--100行 如上篇所说。 MEMORY为我的板子memory 布局状况。两个RAM,一个FLASH,然后把estack 定义在第一个RAM的最后的位置。

e95a50032dfc8360090efbe433ba2cf8.png

16到48 行,分别指定了连接的三个段text,data, bss,注意的是37行,把 data段 在程序运行时的VMA地址放在的RAM上,然后用AT指定的加载地址在flash上,意思就是在编译的时候生成的elf/bin/hex.这段的数据是放在了flash的地址空间上, 然后在程序在运行时,这段是放在RAM中。

具体初始化,看startup.c代码的init_runtime,也是和前篇的汇编代码(CopyDataInit、LoopCopyDataInit)功能一样的,把data数据初始化, 这个三个段在地址空间都是4字节对齐。

先用binutils 中size 工具看一下

b5d86c4c42c9f81e58d413b4dbfc12b8.png

目前这段代码data,bss段都是size都是0,也就是没有任何的数据,改改代码加一些验证。

d9de9e9b3e097c554ebe51c7c91d2c9c.png

定义了几个变量,再来看一下。

180dd1f96fef3defbd1bbc9cd30f54b9.png

现在长度变了,和定义的变量实际大小有点不匹配,4(uint32_t global1)+1(char global_s)+1(char global_t)+1(char global_m) =7 考虑对齐变成了8个。同理un_init_var1,un_init_var2也是一样。

在用nm 工具看一下5个变量的分布情况,global1就被放在RAM的最开始的位置0x20000000,这是整个程序的第一个全局的数据,接下来就是char 数据分别为4,5,6的偏移位置。

BSS段数据按照连接脚本是紧挨着的data段的,也可以定义到偏移的一段的位置 。 这个BSS是从0x20000008开始的,因为对齐所以是0x20000008出开始的。

9b01c18b98dc3d8c0afd30827f663e44.png

在用arm-none-eabi-readelf -a hello.elf 看一下 section

6f28bc9288e8c3a4e6f271b525ae631e.png

第一个section VA 为0x8000000,PA也是这个,FLG是R,E(R只读,E可执行)

第一个section VA 为0x200000000,PA0x80025A,FLG是R,E(运行时fRAM中,段数据放在flash上)

再来看看中断向量表。

d55426dc5bfb48e593ba2c21d364f4d5.png

__attribute((section(".isr_vector"))) 指定isr_vectors数组放在.isr_vector section中。

ce64fc46f3faef2afe639e7a4b782032.png

连接脚本指定这接放在.text 段中最开始的地方,然后再是各程序的(*.text).

这个数组的第一个元素是SP的位置,第二个就是系统Reset_handler 处理函数的位置。

连接脚本指定了_estack为0x20000000+96k == 0x20018000

9b67edce7a79436aea141c23f5ccd940.png

在来看一下isr_vectors数组和Reset_hander 放在哪位置。

76f5f1b4c115c2440414b8566d2acc82.png

反汇编一下arm-none-eabi-objdump -S hello.elf

07c79c113c1f15eeb4d53144fdd61338.png

数组的第一个元素00 80 01 20 ,因为是小端模式,所以就是 0x20018000 正好是SP的地址,这个没问题。第二个元素是Reset_hander 中断处理函数.41 02 00 08 0x08000241,这个是reset_hander处理函数的地址,也是没问题。这个函数地址竟然没四字节对齐,只要第二个元素指向的是reset_handler函数就行,先忽略。

7b2aec0d6e8f63dec9c50c2840763596.png

Reset_handler 处理 设置SP,跳到 init_runtime. 看看汇编代码有没有可以指定对齐的伪指令。上面的问题就解释了。

3b1fcec005e62b9f78512df01a210ac8.png

init_runtime 比较简单,就是把flash(LVM)处存放的.data段的数据在RAM(VMA)中重新初始化, .bss段用0填充,为了验证特意改成0x22初始化。 startup.c代码中定义的那些变量global1,global_s,un_init_var等在编译连接时就指定变量VMA地址在RAM中(连接脚本指定),所以这个阶段在那些要访问这些变量的指令是不能运行的,需等它初始化完成,为啥这么做,逻辑也简单。.data 因为你的程序给了初始值。.bss段因为定了变量,未初始化,但是可能会用到。

fc736257b05358ef3a2b7305f236e4ab.png

下载调试:openocd+st_link

332759e7ea02d57d9bf91dde0b36b6ef.png

1,下载,2,加载符号表,3,Reset_handler,init_runtime,SystemInit 三个函数加上断点

11923700f0dbe328d7cff2aae5f03db1.png

c 运行,首先第一个是Reset_handler断点了,i r 看了一下寄存器。SP其实已经初始化了,这个是向量表第一个元素的值,这个之前也有粉丝朋友说 reset_handler 不需要ldr sp,=_estack,看来是不需要的,然后N下条指令,这个就是ldr sp,=_estack。重新又把sp给初始化了一次。

继续执行断点到init_runtime开始处

4a864364232d4b87aa84ac12e072ac01.png

结合这个图

72ec8217361bc0dd4687dcb10ffc463f.png

这个时候在init_runtime未执行前先看一下 RAM中0x20000000的值及flash 中的值。

RAM 中是随机值,flash 0x00001234 , 0x73,0x74,0x6d分别为s,t,m三个字母ascii码值。

2a3c88ae2ea7ffd7316a3302f23fee34.png

程序继续c 到sysetm_init 在来看看RAM的情况。这个时候两个循环是跑完了,在检查一下RAM中的变量值。

781b294daf0e25d1ca90e5ff9373b8c6.png

红色框框为RAM变量(global1,global_s,un_init_var等)的值,说明RAM中这些变量已成功初始化,可以对照变量地址检查一下。

15f8ac6da6acd8126cdc80d2132ac363.png

启动代码的第一部分 这个先写到这里 ,后面去实现system_init,去做一下stm32 板子时钟初始化的动作。 可能很多人觉得有start.s 就不是C代码了,这个不要去纠结,主要是C代码本身没法设置SP。

如果把init_runtime 函数改成如下,再把start.S 去掉就是一个“”纯C“”的代码了

21c7d8a7d64cb2ced178ca4625ff1b3e.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值