微控制器(单片机)上电后,是如何寻找到并执行主函数的呢?很显然微控制器无法从硬件上定位主函数的入口地址,因为使用Ç语言作为开发语言后,变量/函数的地址便由编译器在编译时自行分配,这样一来主函数的入口地址在微控制器的内部存储空间中不再是绝对不变的。相信读者都可以回答这个问题,答案也许大同小异,但肯定都有个关键词,叫“启动文件”,用英文单词来描述是“引导程序”。启动文件的作用便是负责执行微控制器从“复位”到“开始执行主函数”中间这段时间(称为启动过程)所必须进行的工作
    .Cortex-M3内核规定,起始地址必须存放堆顶指针,而第二个地址则必须存放复位中断入口向量地址,这样在Cortex-M3的的内核复位后,会自动从起始地址的下一个32位空间取出复位中断入口向量,跳转执行复位中断服务程序。对比ARM7 / 一个RM9内核,Cortex-M3内核则是固定了中断向量表的位置而起始地址是可变化的。

<----------------------- - ------------------------------分割线----------------- - ------------------------------------>
    我所用到的芯片是GD32,GD32是我国兆易创新公司生产的完全兼容STM32系列的Cortex-M3处理器,具有几大亮点: 1,高主频108MHz。性能提升30%以上,可超频到120MHz  2,Flash零等待.STM32的72MHz需要两个等待,其实兆易创新公司本来就是做闪存起家的,具有gFlash专利

3,采用ARM Cortex-M3新内核R2p1 .STM32采用R1p1,带有一些缺陷 4,性价比高.GD32比对应的STM32芯片一般便宜20%,某些芯片便宜30%以上

一下是相对于STM32的对比:可以参考文档:GD32与STM32区别点击打开链接

看完这个文档,对于我们新手来说,一定是一头雾水,那么接下来我们来细细研究一下他的启动文件的的的Startup.s。

<------------------------------------------------- -----分割线------------------------------------------- ----------->

一,启动文件的作用
 (关于启动代码的作用,前面已经提到过了,这里再啰嗦一下)
(1)初始堆栈指针SP;
(2)初始化程序计数器指针PC;
(3)设置堆,栈的大小;
(4)设置异常向量表的入口地址;
(5)配置外部SRAM作为数据存储器(这个由用户配置,一般的开发板可没有外部SRAM);
(6)设置Ç库的分支入口__main最终用来调用主函数);
(7)在3.5版的启动文件还调用了system_stm32f10x.c文件中的SystemIni()函数配置系统时钟。

二,启动代码详解

1,栈 - 栈

Stack_Size EQU 0x400 AREA STACK,NOINIT,READWRITE,ALIGN = 3
Stack_Mem空间Stack_Size
__initial_sp

分配名为STACK,不初始化,可读可写,8(2 ^ 3)字节对齐的1KB空间。

栈:局部变量,函数形参等栈的大小不能超过内部SRAM大小。

AREA:汇编一个新的代码段或者数据段

堆栈段名,任意命名; NOINIT表示不初始化;

READWRITE可读可写;

ALIGN = 3(2 ^ 3 = 8字节对齐)。

__initial_sp紧挨了空间放置,表示栈的结束地址,栈是从高往低生长,结束地址就是栈顶地址。

&Stack_Size EQU 0x400定义了栈的大小,EQU相当于汇编中的宏定义,堆栈备选0x400,即16 ^ 2 * 4 = 1024字节

&Stack_Mem SPACE Stack_Size分配连续的Stack_Size字节的存储单元并初始化为0

&__ initial_sp标号,表示堆栈顶部位置

2,堆

Heap_Size等于0x400
AREA HEAP,NOINIT,READWRITE,ALIGN = 3
__heap_base
Heap_Mem空间Heap_Size
__heap_limit

分配名为HEAP,不初始化,可读可写,8(2 ^ 3)字节对齐的512字节空间.__ heap_base的堆的起始地址,__ heap_limit堆的结束地址。堆由低向生长。动态分配内存用到堆

&Heap_Size EQU 0x400定义了堆的大小,大小和栈相同为1K

&__ heap_base的标号,代表堆栈底部地址__heap_limit标号,代表堆栈限制地址

&  PRESERVE8指示编译器8字节对齐.PRESERVE8指令指定当前文件保存堆栈八字节对齐。它设置PRES8编译属性以通知链接器。链接器检查要求堆栈八字节对齐的任何代码是否仅由保持堆栈八字节对齐的代码直接或间接地调用。

&拇指表示后面指令兼容THUMB指令.THUBM是ARM以前的指令集,16位,现在的Cortex-M系列的都使用拇指2指令集,拇指2是32位的,兼容16位和32位的指令,是拇指的超级。

3,定位中断向量表

; 带有例外ISR地址的向量表条目区域重置,数据,READONLYEXPORT __VectorsEXPORT __Vectors_EndEXPORT __Vectors_Size

__Vectors DCD __initial_sp; 堆栈顶部DCD Reset_Handler; 矢量编号1,复位处理程序DCD NMI_Handler; 矢量数字2,NMI处理程序DCD HardFault_Handler; 矢量编号3,硬故障处理程序DCD MemManage_Handler; 矢量编号4,MPU故障处理程序DCD BusFault_Handler; 矢量编号5,总线故障处理程序DCD UsageFault_Handler; 向量编号6,使用错误处理程序DCD 0; 保留的DCD 0; 保留的DCD 0; 保留的DCD 0; 保留的DCD SVC_Handler; 矢量编号11,SVCall处理程序DCD DebugMon_Handler; 矢量编号12,调试监视器处理程序DCD 0; 保留的DCD PendSV_Handler; 矢量编号14,PendSV处理程序DCD SysTick_Handler; 矢量编号15,SysTick处理程序
; 外部中断DCD WWDG_IRQHandler; 矢量数16,窗口看门狗DCD LVD_IRQHandler; 矢量编号17,通过EXTI线路检测的LVDDCD RTC_IRQHandler; 矢量编号18,RTC通过EXTI线DCD FMC_IRQHandler; 矢量数字19,FMCDCD RCC_IRQHandler; 矢量编号20,RCCDCD EXTI0_1_IRQHandler; 矢量编号21,EXTI线0和EXTI线1DCD EXTI2_3_IRQHandler; 矢量编号22,EXTI线2和EXTI线3DCD EXTI4_15_IRQHandler; 矢量编号23,EXTI Line 4到EXTI Line 15DCD TSI_IRQHandler; 矢量编号24,TSIDCD DMA1_Channel1_IRQHandler; 向量编号25,DMA1通道1DCD DMA1_Channel2_3_IRQHandler; 向量编号26,DMA1通道2和DMA1通道3DCD DMA1_Channel4_5_IRQHandler; 向量编号27,DMA1通道4和DMA1通道5DCD ADC1_CMP_IRQHandler; 矢量编号28,ADC1和比较器1-2DCD TIMER1_BRK_UP_TRG_COM_IRQHandler; 向量编号29,TIMER1中断,更新,触发器和换向DCD TIMER1_CC_IRQHandler; 向量编号30,TIMER1捕获比较DCD TIMER2_IRQHandler; 矢量编号31,TIMER2DCD TIMER3_IRQHandler; 矢量数32,TIMER3DCD TIMER6_DAC_IRQHandler; 矢量编号33,TIMER6和DACDCD 0; 保留的DCD TIMER14_IRQHandler; 矢量编号35,TIMER14DCD TIMER15_IRQHandler; 矢量编号36,TIMER15DCD TIMER16_IRQHandler; 矢量编号37,TIMER16DCD TIMER17_IRQHandler; 矢量编号38,TIMER17DCD I2C1_EV_IRQHandler; 向量编号39,I2C1事件DCD I2C2_EV_IRQHandler; 向量编号40,I2C2事件DCD SPI1_IRQHandler; 矢量编号41,SPI1DCD SPI2_IRQHandler; 矢量编号42,SPI2DCD USART1_IRQHandler; 矢量数43,USART1DCD USART2_IRQHandler; 矢量编号44,USART2DCD 0; 保留的DCD CEC_IRQHandler; 矢量编号46,CECDCD 0; 保留的DCD I2C1_ER_IRQHandler; 向量编号48,I2C1错误DCD 0; 保留的DCD I2C2_ER_IRQHandler; 向量编号50,I2C2错误DCD I2C3_EV_IRQHandler; 向量编号51,I2C3事件DCD I2C3_ER_IRQHandler; 向量编号52,I2C3错误DCD USB_FS_LP_IRQHandler; 矢量编号53,USB FS LPDCD USB_FS_HP_IRQHandler; 矢量编号54,USB FS HPDCD 0; 保留的DCD 0; 保留的DCD 0; 保留的DCD USBWakeUp_IRQHandler; 矢量数字58,USB唤醒DCD CAN1_TX_IRQHandler; 向量编号59,CAN1 TXDCD CAN1_RX0_IRQHandler; 向量编号60,CAN1 RX0DCD CAN1_RX1_IRQHandler; 向量编号61,CAN1 RX1DCD CAN1_SCE_IRQHandler; 矢量编号62,CAN1 SCE     DCD LCD_IRQHandler; 矢量编号63,LCDDCD DMA1_Channel6_7_IRQHandler; 向量编号64,DMA1通道6和通道7DCD 0; 保留的DCD 0; 保留的DCD SPI3_IRQHandler; 矢量编号67,SPI3DCD 0; 保留的DCD 0; 保留的DCD 0; 保留的DCD 0; 保留的DCD 0; 保留的DCD 0; 保留的DCD 0; 保留的DCD 0; 保留的DCD 0; 保留的DCD 0; 保留的DCD 0; 保留的DCD 0; 保留的DCD 0; 保留的DCD 0; 保留的DCD 0; 保留的DCD 0; 保留的DCD 0; 保留的DCD 0; 保留的DCD CAN2_TX_IRQHandler; 矢量编号86,CAN2 TX DCD CAN2_RX0_IRQHandler; 向量编号87,CAN2 RX0 DCD CAN2_RX1_IRQHandler; 向量编号88,CAN2 RX1 DCD CAN2_SCE_IRQHandler; 矢量编号89,CAN2 SCE
空间0x5A
__Vectors_End
__Vectors_Size EQU __Vectors_End  -  __Vectors

&   定义一个名为RESET,可读的数据段。并声明__Vectors,__ Vectors_End和__Vectors_Size这三个标号可被外部的文件使用。

&_Vectors为向量表起始地址,__ Vectors_End为向量表结束地址,两个相减即可算出向量表大小。

&向量表从FLASH的0地址开始放置,以4个字节为一个单位,地址0存放的是栈顶地址,0X04存放的是复位程序的地址,以此类推。从代码上看,向量表中存放的都是中断服务函数的函数名,可我们知道ç语言中的函数名就是一个地址。

4,复位程序

AREA | .text |,CODE,READONLY

&定义了一个名为的.text,可读的代码段

; 重置处理程序
Reset_Handler PROC EXPORT Reset_Handler [弱] IMPORT __main IMPORT System_Init   LDR R0,= System_Init BLX R0 LDR R0,= __ main BX R0 ENDP

SystemInit()函数初始系统时钟,然后调用C库函数_main。复位中断(复位入口矢量被硬件固定在地址0x0000_0004)的处理函数:复位子程序是系统上电后第一个执行的程序,Reset_Handler,它的作用就是将保存于闪存中的初始化数据复制到SRAM中,调用上面说到的SystemInit来初始化时钟,接着跳转到主执行。

__main()是编译系统提供的一个函数,负责完成库函数的初始化和初始化应用程序执行环境,最后自动跳转到主()

5,终端服务子程序

NMI_Handler PROC EXPORT NMI_Handler [弱] B. ENDP 

HardFault_Handler PROC EXPORT HardFault_Handler [弱] B. ENDP 

MemManage_Handler PROC EXPORT MemManage_Handler [WEAK] B. ENDP

&启动文件里面已经帮我们写好所有中断的中断服务函数,跟我们平时写的中断服务函数不一样的就是这些函数都是空的,真正的中断复服务程序需要我们在外部的Ç文件里面重新实现,这里只是提前占了一个位置而已。如果我们在使用某个外设的时候,开启了某个中断,但是又忘记编写配套的中断服务程序或者函数名写错,那当中断来临的时,程序就会跳转到启动文件预先写好的空的中断服务程序中,并且在这个空函数中无线循环,即程序就死在这里。

&B:“”跳到一个,表示无限循环.B表示将程序流程转移到另一个程序中

&Default_Handler,这个是作为其他所有中断的默认处理函数,作用就是死循环,所以你假如开启了某个中断,请按照这里面的中断函数名给它写中断处理函数,例如串口中断处理函数名是USART1_IRQHandler ,你开了串口中断,如果不重写USART1_IRQHandler,就默认执行Default_Handler,死循环了。而如果你有重写,那么中断向量表中的处理函数的地址就会更新为你自己写的那个函数的地址了。

如图6所示,用户堆栈初始化

                ALIGN 

; ******************* *******************************
; 用户堆栈和堆初始化
; ******************* ****************************** 如果:DEF:__ MICROLIB EXPORT __initial_sp EXPORT __heap_base EXPORT __heap_limit 其他导入__use_two_region_memory EXPORT __user_initial_stackheap
__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
 ALIGN
 万一
 结束

&ALIGN:对指令或者数据存放的地址进行对齐,后面会跟一个立即数缺省表示4字节对齐。

&判断是否定义了__MICROLIB,如果定义了则赋予标号__initial_sp(栈顶地址),__ heap_base(堆起始地址),__ heap_limit(堆结束地址)全局属性,可供外部文件调用。如果没有定义的__MICROLIB)则使用默认的ç库,然后初始化用户堆栈大小,这部分有ç库函数__main来完成。

&__ user_initial_stackheap,此处是初始化两区的堆栈空间,堆是从低到高的增长,堆栈是由高向低生长的,两个是互相独立的数据段,并且不能交叉使用。

&LDR R0,= HeapMem //保存堆始地址

LDR R1,=(StackMem + Stack)//保存栈的大小

LDR R2,=(HeapMem + Heap)//保存堆的大小

LDR R3,= StackMem //保存栈顶指针

至此,启动文件就完成了执行微控制器从“复位”到“开始执行主函数”中间这段时间(称为启动过程)所必须进行的工作。定义了主要的入口,生命了很多中断

以上仅为个人学习整理资料,由不足的地方还请提出批评

GD32 startup.s相关推荐

  1. GD32调试卡在startup启动文件

    程序一直停在LDR R0=SystemInit,不能进入main()函数 是因为GD32在执行程序的时候,不是直接进Main函数,会优先进入system配置函数里面去,需要system_gd32xxx ...

  2. GD32 MDK创建工程GD32F407工程模板

    GD32 MDK创建工程GD32F407工程模板 一.材料准备 1.到兆易官网下载固件库和支持包.GD32F4xx Firmware Library GD32F4xx ADD-ON 2.解压文件,安装 ...

  3. 【IAP】STM32和GD32的IAP原理分析、教程、资料整理

    文章目录 前言 一.什么是IAP? 二.IAP执行原理(以STM32F10X为例) 2.1 STM32F10X的储存器映像 2.2 正常上电的运行流程 2.3 加入IAP后的Bootloader运行流 ...

  4. 国产单片机GD32系列开坑,带你零死角玩转GD32 第二章

    [开坑国产单片机GD32系列,带你零死角玩转GD32] 第二章 GD32开发环境的搭建,常用资料的获取,以及点亮LED! 目录 [开坑国产单片机GD32系列,带你零死角玩转GD32] 第二章 GD32 ...

  5. Keil5新建GD32裸机程序工程模板

    新建Keil5工程(以GD32C013CB为例) 文件目录 当管理一个工程时,需要将工程中的各种文件分类组织保存好,所以需要在新建工程时提前新建好文件夹,如下图: Docs:该文件夹用于保存该工程所需 ...

  6. 史上最详细的gd32时钟频率设置

    在调试GD32芯片时遇到了时钟配置错误的问题,仔细研究文档和网上资料,认真调试之后做了以下总结,希望能帮到同样有类似困惑的你. ** 1.认识时钟 HXTAL:高速外部时钟: LXTAL:低速外部时钟 ...

  7. GD32学习笔记1(高难度工程,点亮一个LED灯)

    系列文章目录 第一章 GD32学习笔记1(高难度工程,点亮一个LED灯) 文章目录 系列文章目录 前言 一.工作流程 二.新建工程的准备工作 三.新建工程 四.工程目录管理 五.代码实现 1.初始化 ...

  8. 替代STM32的GD32,替代KEIL的Eclipse配置---连载1

    前言 GD32替代STM32原因: (1)前段时间stm32系列芯片涨价厉害,只能用国产替代,管脚兼容的并且做的不错的只有兆易创新的GD32: (2)国产化是个趋势,最好在stm32禁止之前替代掉,符 ...

  9. tomcat报 Context [] startup failed due toprevious errors

    今天同事在实施项目中,遇到一个问题,就是项目布到tomcat服务器上了,却总是找不到访问路径.tomcat报 Context [] startup failed due toprevious erro ...

  10. STARTUP报错:ORA-00205: error in identifying control file, check alert log for more info

    问题描述 startup nomount没有异常 startup mount则出现异常,说明控制文件出错

最新文章

  1. linux网络编程-posix信号量与互斥锁(39)
  2. 总结C#保留小数位数
  3. DataGrip 2019.2.5 —— 中文列名(字段名)(columns name)乱码问题解决方案
  4. shell中$*和$@ 两个都区别
  5. java如何实现多继承
  6. ocs 2007技巧:查看存档服务记录的消息内容
  7. c语言switch scanf语句,C语言中scanf函数与switch语句
  8. ubuntu sun-java-jdk(zhuan)
  9. anaconda老版本下载的方法
  10. linux zip命令收藏
  11. 使用HTML5构建iOS原生APP(2)
  12. 牛逼!支付宝 App架构
  13. Python 实现Mac 屏幕截图
  14. CSS设置字体和文字样式
  15. 解决接口慢的几种方法
  16. 【啃书】《智能优化算法及其MATLAB实例》例6.1基本粒子群算法进行sphere函数寻优
  17. 腾讯云域名如何绑定ip地址
  18. 分布式查询工具-Apache Drill
  19. 不成熟的梦想家 (未熟DREAMER)
  20. npm下载包,npm常用包下载记录,npm包大全

热门文章

  1. 反编译 APK 的基本步骤
  2. matlab uicontrol 居中,MATLAB有关uicontrol的问题~
  3. Quartz的CronTrigger
  4. 简单有限元分析python实现——二维1单元4节点刚度矩阵求解然后得到4个节点的位移和力
  5. 大学生计算机竞赛试题,大学生计算机基础知识竞赛题库_大学生计算机基础知识竞赛试题附答案.docx...
  6. 《深入浅出WPF》-刘铁猛学习笔记——XAML
  7. ZBrush、Maya和3dmax的区别?
  8. Win8驱动的兼容性问题
  9. D7000、60D、K5、E5的详细对比评价(转)_我是亲民_新浪博客
  10. TunesKit Video Cutter for mac(视频分割编辑器)