您的位置 首页 软件

简析STM32的发动进程

简析STM32的启动过程-当前的嵌入式应用程序开发过程里,C语言已成为了绝大部分场合的最佳选择。如此一来main函数似乎成为了理所当然的起点——因为C程序往往从main函数开始执行

  当时的嵌入式应用程序开发进程里,C言语已成为了绝大部分场合的最佳挑选。如此一来main函数好像成为了天经地义的起点——由于C程序往往从main函数开端履行。但一个常常会被疏忽的问题是:微控制器(单片机)上电后,是怎么寻找到并履行main函数的呢?很显然微控制器无法从硬件上定位main函数的进口地址,由于运用C言语作为开发言语后,变量/函数的地址便由编译器在编译时自行分配,这样一来main函数的进口地址在微控制器的内部存储空间中不再是必定不变的。信任读者都能够答复这个问题,答案或许迥然不同,但必定都有个关键词,叫“发动文件”,用英文单词来描绘是“Bootloader”。

  不管功用高低,结构简繁,价格贵贱,每一种微控制器(处理器)都有必要有发动文件,发动文件的效果便是担任履行微控制器从“复位”到“开端履行main函数”中心这段时刻(称为发动进程)一切必要进行的作业。最为常见的51,AVR或MSP430等微控制器当然也有对应发动文件,但开发环境往往主动完整地供给了这个发动文件,不需求开发人员再行干涉发动进程,只需求从main函数开端进行应用程序的规划即可。

  关于“发动形式”

  论题转到STM32微控制器,不管是keil uvision4仍是IAR EWARM开发环境,ST公司都供给了现成的直接可用的发动文件,程序开发人员能够直接引证发动文件后直接进行C应用程序的开发。这样能大大减小开发人员从其它微控制器渠道跳转至STM32渠道,也降低了习惯STM32微控制器的难度(关于上一代ARM的当家花旦ARM9,发动文件往往是第一道难啃却又无法跨越的坎)。 相关于ARM上一代的干流ARM7/ARM9内核架构,新一代Cortex内核架构的发动办法有了比较大的改变。ARM7/ARM9内核的控制器在复位后,CPU会从存储空间的必定地址0x000000取出第一条指令履行复位中止服务程序的办法发动,即固定了复位后的开端地址为0x000000(PC =0x000000)一起中止向量表的方位并不是固定的。而Cortex-M3内核则正好相反,有3种状况:

  1、 经过boot引脚设置能够将中止向量表定坐落SRAM区,即开端地址为0x2000000,一起复位后PC指针坐落0x2000000处;

  2、 经过boot引脚设置能够将中止向量表定坐落FLASH区,即开端地址为0x8000000,一起复位后PC指针坐落0x8000000处;

  3、 经过boot引脚设置能够将中止向量表定坐落内置Bootloader区,本文不对这种状况做论说;

  Cortex-M3内核规则,开端地址有必要寄存堆顶指针,而第二个地址则有必要寄存复位中止进口向量地址,这样在Cortex-M3内核复位后,会主动从开端地址的下一个32位空间取出复位中止进口向量,跳转履行复位中止服务程序。比照ARM7/ARM9内核,Cortex-M3内核则是固定了中止向量表的方位而开端地址是可改变的。

  细说STM32的发动进程

  下面就从ST的发动文件说起,由于库中的startup_stm32f10x_md.s与编译环境有关,所以针对的是库中的

  STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\TrueSTUDIO途径下的文件进行剖析。

  system_stm32f10x.c

  SystemInit():在“startup_stm32f10x_xx.s”文件中被调用,功用是设置体系时钟(包含时钟源,PLL系数,AHB/APBx的预分频系数还有 flash的设定),这个函数会在体系复位之后首要被履行。文件中默许的一些设置:答应HSE(外部时钟),FLASH(答应预取缓冲区,设置2个等候周 期),PLL系数为9,敞开PLL并挑选PLL输出作为时钟源(SYSCLK),后续设置SYSCLK = HCLK = APB2 = 72MHz,APB1 = HCLK/2 = 36MHz,HCLK即AHB的时钟。

  SystemCoreClockUpdate():在体系时钟HCLK改变的时分调用,以更新一个全局变量SystemCoreClock,这个变量能够为应用程序运用,有必要确保正确。默许不调用这个函数,由于SystemCoreClock默许被设置为设定的频率了(72MHz)

  别的,下面这种设置寄存器的办法值得学习,先用位名铲除相应的位,再进行设置,例如:

  #define RCC_CFGR_PLLSRC ((uint32_t)0x00010000) /*!《 PLL entry clock source */

  #define RCC_CFGR_PLLXTPRE ((uint32_t)0x00020000) /*!《 HSE divider for PLL entry */

  #define RCC_CFGR_PLLMULL ((uint32_t)0x003C0000) /*!《 PLLMUL[3:0] bits (PLL mulTIplicaTIon factor) */

  #define RCC_CFGR_PLLSRC_HSE ((uint32_t)0x00010000) /*!《 HSE clock selected as PLL entry clock source */

  #define RCC_CFGR_PLLMULL9 ((uint32_t)0x001C0000) /*!《 PLL input clock*9 */

  /* 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);

  startup_stm32f10x_md.s:(针对F103RBT6为中容量的产品)

  这个文件里边首要界说了复位中止(复位进口矢量被硬件固定在地址0x0000_0004)的处理函数:Reset_Handler,它的效果便是将保存于flash中的初始化数据复制到sram中,调用上面提到的SystemInit来初始化时钟,接着跳转到main履行。

  接着界说了Default_Handler, 这个是作为其他一切中止的默许处理函数,效果便是死循环,所以你假设敞开了某个中止,请依照这儿边的中止函数名给它写中止处理函数,例如串口中止处理函数名是 USART1_IRQHandler,你开了串口中止,假如不重写USART1_IRQHandler,就默许履行Default_Handler,死循环了。而假如你有重写,那么中止向量表中的处理函数的地址就会更新为你自己写的那个函数的地址了。为什么会这样呢?由于此文件的结尾用了相似这样的句子:

  .weak USART1_IRQHandler

  .thumb_set USART1_IRQHandler,Default_Handler

  它给中止处理函数供给了弱(weak)别号(Default_Handler),假如不重写,中止了默许履行Default_Handler,假如重写了,由于是弱别号,所以会被你写的同名函数掩盖。

  最终界说了一个中止向量表的段(.secTIon .isr_vector,“a”,%progbits),这个表将会放置在0x0000 0000那里,第二个字(0x0000 0004)是复位向量,复位之后从这地址所指的函数履行。

  那么,怎么确保.isr_vector这个段将放在flash的最开端(0x08000000)呢?这就需求链接脚本了,即咱们用的那个stm32_flash.ld文件了,检查一下就知道了,里边先界说了仓库的地址:

  _estack

  /* Highest address of the user mode stack */

  _estack = 0x20005000; /* end of 20K RAM */

  接着界说了堆和栈的巨细:

  /* Generate a link error if heap and stack don‘t fit into RAM */

  _Min_Heap_Size = 0; /* required amount of heap */

  _Min_Stack_Size = 0x100; /* required amount of stack */

  接着声明晰各个内存的区域(界说了几个代表某个线性空间的姓名,如下面的FLASH):

  /* Specify the memory areas */

  MEMORY

  {

  FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 128K

  RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K

  MEMORY_B1 (rx) : ORIGIN = 0x60000000, LENGTH = 0K }

  接着下面再介绍上面这三个姓名里边都放了什么东西,首要是FLASH的:

  /* Define output sections */

  SECTIONS

  {

  /* The startup code goes first into FLASH */

  .isr_vector :

  {

  。 = ALIGN(4); KEEP(*(.isr_vector)) /* Startup code */

  。 = ALIGN(4); } 》FLASH

  /* The program code and other data goes into FLASH */

  .text :

  {

  。 = ALIGN(4); *(.text) /* .text sections (code) */

  *(.text*) /* .text* sections (code) */

  *(.rodata) /* .rodata sections (constants, strings, etc.) */

  *(.rodata*) /* .rodata* sections (constants, strings, etc.) */

  *(.glue_7) /* glue arm to thumb code */

  *(.glue_7t) /* glue thumb to arm code */

  KEEP (*(.init))

  KEEP (*(.fini))

  。 = ALIGN(4); _etext = 。; /* define a global symbols at end of code */

  } 》FLASH

  能够看到startup_stm32f10x_md.s中界说的这个段.isr_vector的确放在了最最初的方位。

  可是咱们前面说复位向量在0x0000 0004,现在其地址是在flash中,所以地址是0x0800 0004,这个怎么回事呢?本来,stm32能够经过boot0、boot1引脚的装备将flash映射到0x0000 0000处。详细可参阅stm32的用户手册2.4节。

  从主闪存存储器发动(BOOT0 = 0,BOOT1 = X):主闪存存储器被映射到发动空间(0x0000 0000),但仍然能够在它原有的地址(0x0800 0000)拜访它,即闪存存储器的内容能够在两个地址区域拜访,0x0000 0000或0x0800 0000。

  至此,整个STM32的发动进程以及程序结构都能够比较明晰了。

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/qianrushi/ruanjian/183042.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部