STM32系列单片机在进入main函数前都在干些什么?

最新更新时间:2022-03-02来源: eefocus关键字:STM32系列  单片机  main函数 手机看文章 扫描二维码
随时随地手机看文章

在刚开始学习单片机的时候,一直以为程序启动后就直接进入到了main函数,但是随着学习的深入才发现,程序在进入main函数前其实还要干好多事情。现在就来分析一下,STM32系列单片机程序在进入main函数前都在干些什么?

单片机上电后,程序首先跳转到地址0处,此时主堆栈指针MSP的初值也为0。然后单片机产生了复位信号,主堆栈指针加1,由于单片机内核为32位,所以地址增加一位,实际上是增加了32位,也就是增加了4个字节。此时MSP指针就指向了复位向量。而Cortex-M内核处理器的向量表可以重新定位,所以此时程序就会跳转到复位向量重新映射的地址处。

通过上面的两个图可以看到,通过复位向量的重映射后,MSP指针就会跳转到复位向量处,在STM32系列单片机中,复位向量的函数通常在启动文件startup_stm32f10x_xx.s中实现。


下来接着看一下复位函数都实现了哪些功能。

可以看到复位函数首先获取到了main()函数和系统初始化函数SystemInit()的地址,然后跳转到系统初始化函数中SystemInit()中,接着就会跳转到main函数中。


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

}

         在系统复位函数SystemInit()中,主要是复位各个寄存器,将寄存器值设置位默认值,最后调用时钟设置函数 SetSysClock()对系统时钟进行设置。


static void SetSysClock( void )

{

#ifdef SYSCLK_FREQ_HSE

    SetSysClockToHSE();

#elif defined SYSCLK_FREQ_24MHz

    SetSysClockTo24();

#elif defined SYSCLK_FREQ_36MHz

    SetSysClockTo36();

#elif defined SYSCLK_FREQ_48MHz

    SetSysClockTo48();

#elif defined SYSCLK_FREQ_56MHz

    SetSysClockTo56();

#elif defined SYSCLK_FREQ_72MHz

    SetSysClockTo72();

#endif

 

    /* If none of the define above is enabled, the HSI is used as System clock

       source (default after reset) */

}

系统设置函数,根据不同的晶振和单片机型号,选择相应频率的时钟进行设置。


static void SetSysClockTo72( void )

{

    __IO uint32_t StartUpCounter = 0, HSEStatus = 0;

 

    /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/

    /* Enable HSE */

    RCC->CR |= ( ( uint32_t )RCC_CR_HSEON );

 

    /* Wait till HSE is ready and if Time out is reached exit */

    do

    {

        HSEStatus = RCC->CR & RCC_CR_HSERDY;

        StartUpCounter++;

    }

    while( ( HSEStatus == 0 ) && ( StartUpCounter != HSE_STARTUP_TIMEOUT ) );

 

    if ( ( RCC->CR & RCC_CR_HSERDY ) != RESET )

    {

        HSEStatus = ( uint32_t )0x01;

    }

    else

    {

        HSEStatus = ( uint32_t )0x00;

    }

 

    if ( HSEStatus == ( uint32_t )0x01 )

    {

        /* Enable Prefetch Buffer */

        FLASH->ACR |= FLASH_ACR_PRFTBE;

 

        /* Flash 2 wait state */

        FLASH->ACR &= ( uint32_t )( ( uint32_t )~FLASH_ACR_LATENCY );

        FLASH->ACR |= ( uint32_t )FLASH_ACR_LATENCY_2;

 

 

        /* HCLK = SYSCLK */

        RCC->CFGR |= ( uint32_t )RCC_CFGR_HPRE_DIV1;

 

        /* PCLK2 = HCLK */

        RCC->CFGR |= ( uint32_t )RCC_CFGR_PPRE2_DIV1;

 

        /* PCLK1 = HCLK */

        RCC->CFGR |= ( uint32_t )RCC_CFGR_PPRE1_DIV2;

 

#ifdef STM32F10X_CL

        /* Configure PLLs ------------------------------------------------------*/

        /* PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */

        /* PREDIV1 configuration: PREDIV1CLK = PLL2 / 5 = 8 MHz */

 

        RCC->CFGR2 &= ( uint32_t )~( RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL |

                                     RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC );

        RCC->CFGR2 |= ( uint32_t )( RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL8 |

                                    RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV5 );

 

        /* Enable PLL2 */

        RCC->CR |= RCC_CR_PLL2ON;

        /* Wait till PLL2 is ready */

        while( ( RCC->CR & RCC_CR_PLL2RDY ) == 0 )

        {

        }

 

 

        /* PLL configuration: PLLCLK = PREDIV1 * 9 = 72 MHz */

        RCC->CFGR &= ( uint32_t )~( RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL );

        RCC->CFGR |= ( uint32_t )( RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 |

                                   RCC_CFGR_PLLMULL9 );

#else

        /*  PLL configuration: PLLCLK = HSE * 9 = 72 MHz */

        RCC->CFGR &= ( uint32_t )( ( uint32_t )~( RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |

                                   RCC_CFGR_PLLMULL ) );

        RCC->CFGR |= ( uint32_t )( RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9 );

#endif /* STM32F10X_CL */

 

        /* Enable PLL */

        RCC->CR |= RCC_CR_PLLON;

 

        /* Wait till PLL is ready */

        while( ( RCC->CR & RCC_CR_PLLRDY ) == 0 )

        {

        }

 

        /* Select PLL as system clock source */

        RCC->CFGR &= ( uint32_t )( ( uint32_t )~( RCC_CFGR_SW ) );

        RCC->CFGR |= ( uint32_t )RCC_CFGR_SW_PLL;

 

        /* Wait till PLL is used as system clock source */

        while ( ( RCC->CFGR & ( uint32_t )RCC_CFGR_SWS ) != ( uint32_t )0x08 )

        {

        }

    }

    else

    {

        /* If HSE fails to start-up, the application will have wrong clock

             configuration. User can add here some code to deal with this error */

    }

}

#endif


在系统时钟设置函数中,设置锁相环,及各个时钟的频率。


关于时钟的具体设置,可以参考下面这张图。

通过上面的分析,可以知道,程序在进入main之前执行流程为

这里大概分析了单片机在进入main函数之前,大概都做了哪些工作,对于具体的启动文件代码没做分析。对于启动文件startup_stm32f10x_hd.s的详细分析,在另一篇文章中进行说明。

关键字:STM32系列  单片机  main函数 编辑:什么鱼 引用地址:STM32系列单片机在进入main函数前都在干些什么?

上一篇:STM32系列单片机向量表和向量表重新定位
下一篇:STM32单片机启动文件startup_stm32f10x_hd.s详解

推荐阅读

5V供电CAN器件和3.3V供电MCU之间的通讯连接
目前市场上最常用的CAN通讯接口器件大多都是采用5V供电,而大部分的MCU供电电压却从5V降低到了3.3V供电,这样就会造成5VCAN通讯接口器件和3.3VMCU进行通讯时的接口电平不一致问题,本文针对这种应用提出几种5V供电CAN器件和3.3V供电MCU之间的连接方式,并给出了川土微电子产品的具体应用案例。CAN器件概述和MCU之间的连接CAN器件和MCU之间是通过RXD和TXD进行连接的,MCU发送的数据到CAN器件TXD后,由CAN收发器转换成CAN的隐性和显性电平发送到CAN总线,在接收数据时,CAN总线上的隐性和显性电平通过CAN收发器转换成逻辑电平由RXD输出到MCU。以川土微电子的CA-IF1051S/HS为例,对于5
发表于 2023-01-19
5V供电CAN器件和3.3V供电<font color='red'>MCU</font>之间的通讯连接
用汇编语言做一个看门狗测试
用STC的MCU的IO方式控制74HC595驱动8位数码管。; 用户可以修改宏来选择时钟频率.; 显示效果为: 显示秒计数, 5秒后不喂狗, 等复位.Fosc_KHZ EQU 22118 ;22118KHZSTACK_POIRTER EQU 0D0H ; 堆栈开始地址DIS_DOT EQU 020HDIS_BLACK EQU 010HDIS_ EQU 011HAUXR DATA 08EHP4 DATA 0C0HP5 DATA 0C8HP0M1 DATA 0x93 ;P0M0 DATA 0x94 ;P1M1 DATA 0x91 ;P1M0 DATA 0x92 ;P2M1 DATA 0x95 ;P2M0 DATA 0x96 ;P3M1
发表于 2023-01-13
用AT89C51单片机显示倒计时程序
;可设定时间的倒计时定时器,可选择5/15/20/30/35/45/50分钟倒计时;倒计时时间由四位拨码开关的2/3/4位来控制,;第2位表示5分钟,第3位表示15分钟,第4位表示30分钟,;通过不同的组合可以产生5/15/20/30/35/45/50分钟倒计时;P1.0口的外接的发光二极管为状态LED,定时未开始时LED常亮,定时过程中LED闪烁;K1为开始按钮,K2为停止按钮适用STM8S/STM8L/STM8A N76E003 脱机编程器/烧录器/下载器/SP_00【包邮】m.tb.cn/h.UlXVKiOa_bit equ 20h ;数码管个位数存放内存位置b_bit equ 21h ;数码管十位数存放内存位置temp eq
发表于 2023-01-13
基于S3C2440芯片和单片机设计压装数据采集系统的设计
引言随着经济和社会的发展,我国的工业水平和信息技术水平也得到了飞速发展。其中工业中最为常见的零部件组装和装备压装监测设备也得到了不断的改进。压装的过程其实就是按规定的技术要求将零部件进行组培和连接,使之成为半成品或者成品的工艺过程。如图1所示,就是将两个零部件进行过盈无键组装配合,使之牢固结合在一起。工业中很多机械设备都是通过这种压装方式组合到一起的,包括火车轮对、轴承、汽车发动机、变速器、底盘等关键部件。压装的质量决定了以后产品的使用质量和人民的生命财产都息息相关。我们知道压装质量的评判标准,主要是根据压装过程中压力和位移的变化曲线来确定的。而压力位移曲线的获取这就需要由良好的运行稳定的数据采集系统来提供。基于此,本文提出一种基于
发表于 2023-01-13
基于S3C2440芯片和<font color='red'>单片机</font>设计压装数据采集系统的设计
八位单片机的程序优化12项注意事项
1、采用短变量一个提高代码效率的最基本的方式就是减小变量的长度。使用 C 编程时,我们都习惯于对循环控制变量使用 int 类型,这对 8 位的单片机来说是一种极大的浪费,你应该仔细考虑你所声明的变量值可能的范围,然后选择合适的变量类型,很明显,经常使用的变量应该是unsigned char,只占用一个字节。2、使用无符号类型为什么要使用无符号类型呢?原因是8051不支持符号运算,程序中也不要使用含有带符号变量的外部代码,除了根据变量长度来选择变量类型外,你还要考虑是否变量是否会用于负数的场合。如果你的程序中可以不需要负数那么把变量都定义成无符号类型的。3、避免使用浮点指针在 8 位操作系统上使用 32 位浮点数是得不偿失的。你可以这
发表于 2023-01-13
单片机程序该如何优化?
对程序进行优化,通常是指优化程序代码或程序执行速度。优化代码和优化速度实际上是一个予盾的统一。一般是优化了代码的尺寸,就会带来执行时间的增加;如果优化了程序的执行速度,通常会带来代码增加的副作用。很难鱼与熊掌兼得,只能在设计时掌握一个平衡点。一、程序结构的优化1、程序的书写结构虽然书写格式并不会影响生成的代码质量,但是在实际编写程序时还是应该尊循一定的书写规则,一个书写清晰、明了的程序,有利于以后的维护。在书写程序时,特别是对于While、for、do…while、if…else、switch…case 等语句或这些语句嵌套组合时,应采用“缩格”的书写形式。2、标识符程序中使用的用户标识符除要遵循标识符的命名规则以外,一般不要用代数
发表于 2023-01-13

推荐帖子

初学者,请教各位一个很蹊跷的问题
#include<MSP430x14x.h> voidmain(void) { inti; intarray[]={1,2,3,4,5}; intl=1; while(1) { switch(l) { case1:i=array[l];break; case2:i=array[l];break; default:break;
jackhui 微控制器 MCU
DSP5509建工程和下程序
刚碰dsp,用ccs4.2给5509建立一个工程,编译无错误,但是loadprogram报错: 55xx:FileLoader:Dataverificationfailedataddress0x00FFFF00Pleaseverifytargetmemoryandmemorymap. Errorfoundduringdataverification. Ensurethelinkercommandfilematchesthememory
水沉威尼斯 DSP 与 ARM 处理器
做了个播放器
做了个播放器
shower.xu DIY/开源硬件专区
[TI首届低功耗设计大赛]+开始要调试了!
[TI首届低功耗设计大赛]+开始要调试了! 在焊接时,发现了不少问题啊! 感觉焊接还是有难度啊! 正面 反面 手持 [TI首届低功耗设计大赛]+开始要调试了!
蓝雨夜 微控制器 MCU
求救:本本没有串、并口,大侠们提供一个USB下载线制作资料或地址吧(用于AT89S52单片机的)。
我的本本只有USB口,我买了个USB转串口的线是可以用的,但转并口的线不能用。 现在想自做一个USB下载线或串口下载线,但网上资料全是并口的资料,还求前辈们给个资料或地址。 注:是用于AT89S52芯片的ISP下载线。求救:本本没有串、并口,大侠们提供一个USB下载线制作资料或地址吧(用于AT89S52单片机的)。
xhwang2003 嵌入式系统
电流、电压采样有哪些不同?
电流、电压采样有哪些不同?电流、电压采样都是通过AD测量采样电阻上的压差吗?都是串联?看起来都是一样的原理 电流、电压采样有哪些不同?
QWE4562009 电路观察室
小广播
设计资源 培训 开发板 精华推荐

何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2023 EEWORLD.com.cn, Inc. All rights reserved