单片机在main()函数之前是怎么运行

这次我来记录下单片机在main()函数之前是怎么运行的

在这是别人帖子上的图

STM32上电或者复位后,代码区始终从0x00000000开始,其实就是将存储空间的地址映射到0x00000000中。三种启动模式如下:

·从主闪存存储器启动,将主Flash地址0x08000000映射到0x00000000,这样代码启动之后就相当于从0x08000000开始。主闪存存储器是STM32内置的Flash,作为芯片内置的Flash,是正常的工作模式。一般我们使用JTAG或者SWD模式下载程序时,就是下载到这个里面,重启后也直接从这启动程序。一般我们都用这种模式

·从系统存储器启动。首先控制BOOT0、BOOT1管脚,复位后,STM32与上述两种方式类似,从系统存储器地址0x1FFF F000开始执行代码。系统存储器是芯片内部一块特定的区域,芯片出厂时在这个区域预置了一段Bootloader,就是通常说的ISP程序。这个区域的内容在芯片出厂后没有人能够修改或擦除,即它是一个ROM区。启动的程序功能由厂家设置。系统存储器存储的其实就是STM32自带的bootloader代码。

·从内置SRAM启动,将SRAM地址0x20000000映射到0x00000000,这样代码启动之后就相当于从0x20000000开始。内置SRAM,也就是STM32的内存,既然是SRAM,自然也就没有程序存储的能力了,这个模式一般用于程序调试。假如我只修改了代码中一个小小的地方,然后就需要重新擦除整个Flash,比较的费时,可以考虑从这个模式启动代码,用于快速的程序调试,等程序调试完成后,在将程序下载到SRAM中。
用户可以通过设置BOOT1和BOOT0引脚的状态,来选择在复位后的启动模式。STM32三种启动模式对应的存储介质均是芯片内置的,如下图:

这是别人帖子上的图

  1. 单片机上电后,程序首先跳转到地址0处,此时主堆栈指针MSP的初值也为0。然后单片机产生了复位信号,主堆栈指针加1,由于单片机内核为32位,所以地址增加一位,实际上是增加了32位,也就是增加了4个字节。此时MSP指针就指向了复位向量。而Cortex-M内核处理器的向量表可以重新定位,所以此时程序就会跳转到复位向量重新映射的地址处。
    2,。通过复位向量的重映射后,MSP指针就会跳转到复位向量处.。这里我理解复位向量的地方就是Reset_Handler
    如果时候stm32F1系列单片机中,复位向量的函数通常在启动文件startup_stm32f10x_xx.s中实现。这是别人帖子上的图
    3.我们可以看到首先进入Reset_Handle
    复位函数首先获取到了main()函数和系统初始化函数SystemInit()的地址,然后跳转到系统初始化函数中SystemInit()中,接着就会跳转到main函数中。
    SystemInit里面就是各种时钟配置了,之后就会main()函数了。
    别人说的更透彻
    (https://blog.csdn.net/qq_20222919/article/details/109304589)
    (https://baijiahao.baidu.com/s?id=1730596918670556770&wfr=spider&for=pc)
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
单片机中,可以使用定时器来计算函数运行时间。定时器可以定时中断CPU的执行,当定时器中断发生时,可以记录下当前的时间,然后在函数执行结束时再次记录当前时间,两个时间之差即为函数运行时间。 以下是一个示例代码,用于计算函数执行时间: ```c #include <reg51.h> #define FREQ_OSCILLATOR 11059200UL // 单片机振荡器频率 #define TIMER_PRESCALER 12 // 定时器分频系数 unsigned long start_time; // 函数开始时间 unsigned long end_time; // 函数结束时间 // 定时器中断服务程序 void timer_isr() interrupt 1 { static unsigned int count = 0; // 计数器,用于计算定时器中断的次数 TH0 = (65536 - (FREQ_OSCILLATOR / TIMER_PRESCALER / 1000)); // 重载计数器,定时1ms TL0 = (65536 - (FREQ_OSCILLATOR / TIMER_PRESCALER / 1000)); if (++count == 1000) { // 每隔1s输出一次定时器中断次数 count = 0; putchar('.'); } } // 需要计算运行时间的函数 void my_function() { // 执行某些操作 } int main() { TMOD = 0x01; // 定时器0工作在模式1,16位定时器/计数器 TH0 = (65536 - (FREQ_OSCILLATOR / TIMER_PRESCALER / 1000)); // 重载计数器,定时1ms TL0 = (65536 - (FREQ_OSCILLATOR / TIMER_PRESCALER / 1000)); TR0 = 1; // 启动定时器0 ET0 = 1; // 允许定时器0中断 start_time = (unsigned long) TH0 * 256 + TL0; // 记录函数开始时间 my_function(); // 执行需要计算运行时间的函数 end_time = (unsigned long) TH0 * 256 + TL0; // 记录函数结束时间 TR0 = 0; // 停止定时器0 ET0 = 0; // 禁止定时器0中断 printf("\nFunction execution time: %lu ms\n", end_time - start_time); return 0; } ``` 在上述代码中,我们使用定时器0来定时中断CPU的执行,每隔1ms就会触发一次定时器中断。在每次中断中,我们重载定时器0的计数器,使其再次定时1ms,并输出一个点号。在主函数中,我们先记录下函数开始执行的时间,然后调用需要计算运行时间的函数。在函数执行完成后,我们再次记录下当前时间作为函数结束时间。最后,我们停止定时器0并禁止定时器中断,计算出函数执行时间,并将其输出。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值