(五)Betaflight 移植到keil

目录

5.1 环境搭建

5.1.1 MDK安装:

5.1.2 芯片Pack包安装:

5.1.3 J-LINK OB:

5.1.4 另外使用Jlink调试过程中可能出现如下报错:

5.1.5 官方例程试验

5.2 移植前准备

5.2.1 AT32官方资料简介

5.2.2 Keil文件类型

5.2.3 options for target(目标选项) 与 Manage Project(管理项目)

5.3 AT32VMT7在Keil下的启动流程

5.3.1 AT32存储器资源

1. 前存储空间 0x0000_0000~0x1FFF_FFFF(512MB)

    (1.1)CODE区 0x0000_0000~0x07F F_FFFF(128MB)

    (1.2) Flash 0x0800_0000~0x0F FF_FFFF(128MB),这个芯片只用到~0x083E FFFF(4032KB,即3M)

    (1.3) SRAM1 映射区 0x1000_0000~0x1000_FFFF(64KB)

    (1.4)Bootloader flash 0x1FFF_0000~0x1FFF_BFFF(48KB)

    (1.5)User system data ? 0x1FFF_C000~0x1FFF_CFFF(4KB)

2. SRAM 0x2000_0000~0x200F_FFFF(1024KB)

3. 外设空间 0x4000 0000~0xDFFF FFFF(2GB)

5.3.2 硬件启动流程

5.3.2 软件启动流程

(1)STACK与HEAP段的定义

(2)定义中断向量表

5.3.3 反汇编验证


Betaflight的体系相当庞大包括一套系统调用算法、各类飞行控制及飞行辅助算法、完整的地面通信体系、完整的外部设备接口系统等,而由于Betaflight源码是在Linux环境下进行编译的,arm-none-eabi-gcc交叉编译器与Keil自带的ARMCC编译器也有许多不同,具体体现在:

1.  Linux下支持的一些Linux C语言库函数,Keil下可能不支持,如静态断言等。

2. 两者的汇编语法不同,导致.s汇编启动文件也有出入。

3. Keil下是没有.ld链接脚本之类的文件的,导致其中定义的变量不能使用。

综上所述,Betaflight移植到Keil的第一步便是解决由于编译环境不同引起的一系列问题,本篇的目的是:

1. 解决Linux与Keil环境不同引起的一系列编译方面的问题。

2. 移植Betaflight下所有的lib文件与src中关于系统调用算法有关的文件到Keil中并正常编译运行,具体包括系统调用初始化与系统调用扫描。

5.1 环境搭建

5.1.1 MDK安装:

安装过程参考 /2、参考资料/STM32F1开发指南(精英版)-寄存器版本_V1.2文档。软件包位于 /5、软件资料/MDK5

5.1.2 芯片Pack包安装:

参考 \6、AT32官方资料\(1)教程\(1)AT32F435_437固件库BSP&Pack应用指南.pdf , pack包位于\6、AT32官方资料\Pack包_下载器Keil5_AT32MCU_AddOn_V2.1.9.zip

5.1.3 J-LINK OB

对软件的下载与调试这里使用J-LINK OB(与普通J-link功能相同,但价格低的多),淘宝链接:

https://detail.tmall.com/item.htm?abbucket=19&id=670538147682&ns=1&spm=a21n57.1.0.0.63a4523ctkXFEb&skuId=5015440542446

Jlink驱动安装,参考 /5、软件资料/J-LINK OB全套资料

5.1.4 另外使用Jlink调试过程中可能出现如下报错:

错误的提示:

其错误提示为:Encountered an improper argument(翻译过来就是遇到不恰当的争论)

错误原因

错误原因其实是因为我们在调试完结束时候,有断点(红色圆点)还没有去掉,所以我们一点击停止调试之后,keil就会马上弹出这个错误,然后你就会发现你的keil关不掉了。。。,别问我怎么知道的,因为我自己崩了不下二三十次,试了keil的两个不同版本都会出现这种情况(当然不是所有版本都这样,因为本人只试了两个版本),直到我发现只有把断点(红色圆点)全部去掉,关调试的时候才不会弹出这种提示和导致keil崩。新版本的Keil可能已经解决该问题。

解决方法:

如果想要规避这种情况的话,在你调试结束时候记得检查是否有断点没有去掉!!!重要的事说三次~

同时,这里跟你说一下,如果已经弹出如上图所示提示,keil可能已经崩了,正常是关不了了,我基本上是通过任务管理器强行关掉的。然后再打开工程就好,并且你再调试时候那个没有去掉的红点依然在,记得这次把点去了就好。

参考:解决调试时候出现的“Encountered an improper argument”错误-CSDN博客

5.1.5 官方例程试验

其实AT32官方是有给移植好的例程的,只是与其他软件应用相混杂,不太好用。这里可以用官方例程来试试环境是否搭建成功啦。打开目录

6、AT32官方资料\BSP_板级支持包_AT32F435\AT32F435_437_Firmware_Library_V2.1.2\project\at_start_f435\templates\mdk_v5

下的keil工程文件,编译、设置Debug为Jlink,由于AT32飞控Led与AT32官方start开发板的led使用了相同的引脚,下载成功后飞控的两个led灯会呈流水灯一样闪烁。

另外,当使用AT32 keil工程设置Debug为jlink时可能会出现如下提示:

该提示表示在Jlink驱动中查找不到AT32F435VMT7这个设备,这里先点击Yes来手动选择芯片设备。

然后会出现如下界面:

这里 Cotex-M4 -> OK。

至于具体原因,来看下AT32飞控参数:

CPU: AT32VMT7

主频: 288MHz

引脚: 100 Pin(“V”)

Flash: 4032 KB(“M”)

RAM: 384 KB

内核:32位ARM® Cortex®-M4内核

故选择Cotex-M4这个广义设备。

5.2 移植前准备

5.2.1 AT32官方资料简介

AT32的官方资料者可以在https://www.arterytek.com/cn/product/AT32F435.jsp#Resource 中下载。

这里清月将下载的资料都放在了6、AT32官方资料 中,其中ISP工具要介绍一下,这是一个程序下载软件,

有UI界面(6、AT32官方资料\Tool_工具\Artery_ISP_Programmer_V2.0.08\Artery ISP Programmer_V2.0.08 下的ArteryISPProgrammer.exe)

与 命令行版本(G:\Practice\(2)Linux飞控\(4)作品\基于AT32的Betaflight飞行控制器\6、AT32官方资料\Tool_工具\Artery_ISP_Console_Win_V3.0.05\Artery_ISP_Console_Win_V3.0.05 中的 AT32_ISP_Console.exe)

命令行版本要使用.bat批处理文件命令进行控制,在文件目录下DFU_download.bat等三个*download.bat文件就量分别使用usb_dfu、uart、I2C来下载程序用的。可以放在工程文件夹下使用,使用时先按住BOOT键上电启动进行Bootloader下载模式,然后双击DFU_download.bat即可,之后移植的AT32工程将使用到ISP。

注意,要保证DFU_download.bat中的程序下载文件地址与工程生成的.bin文件一致。ISP使用的命令参考:

6、AT32官方资料\Tool_工具\Artery_ISP_Console_Win_V3.0.05\Document

5.2.2 Keil文件类型

来科普一些概念,Keil 即是一家公司名称,也是一款软件名称。Keil 有几个出名的软件(IDE),包括 MDK、 C51。而µVision(或uVision)是一种开发环境,µVision5是其第5个版本。相关具体信息参考:

Keil科普教程 | Keil C51 和 MDK 的区别 - strongerHuang的文章 - 知乎

Keil科普教程 | Keil C51 和 MDK 的区别 - 知乎

(1)工程文件

(这类文件不能删除) *.uvprojx:µVision5工程文件

(2)工程选项配置文件

(这类文件不能删除) *.uvoptx:µVision5工程选项配置文件

(3)项目界面布局文件

(这类文件可以删除) *.uvguix[.user-name]:µVision5项目界面布局文件。删除之后,重新打开工程,界面布局会恢复到默认布局。如Demo.uvguix.Administrator。

其中[.user-name]为计算机的用户名,若使用联想电脑,默认为.lenovo,如文件:template.uvguix.lenovo

(4)删除过程文件.bat

.bat为winbows批处理文件,是一种调用DOS命令的可执行文件,可以看作一个小软件,一般双击运行来删除编译过程产生的文件。

(5)EventRecorderStub.scvd

EventRecorderStub:事件记录器存根(可能记录了一些用户操作之类的信息)

(6)JLinkSettings.ini

是对Jlink进行一些配置的文件。

INI文件格式是某些平台或软件上的配置文件的非正式标准,以节(section)和键(key)构成,常用于微软Windows操作系统中。这种配置文件的文件扩展名多为INI

5.2.3 options for target(目标选项) 与 Manage Project(管理项目)

Keil使用经年,仍然搞不懂“魔术棒”?来看看下面的整理

这玩意,官方名称options for target(目标选项),其中有许多关于工程编译调试的重要选项,可以说工程编译报错时的种种问题都可以在其中找到答案。而其中繁杂的选项学过Makefile的同学可能有点眼熟(了解Makefile,参考 2、参考资料\【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.81.pdf 中的3.3 Makefile 基础)。

(1)Device

     选择芯片类型:ARM等蓝色标号代表厂家,选中芯片,右侧方框会显示芯片主频、内核等关键信息

(2)Target

ROM是一种只读存储器,在老版单片机中使用。Flash则是一种可读可写的存储介质。两者都看成 一种电脑硬盘就可以,这里对ROM的设置实际就是设置Flash。设置Flash起始地址与大小(size),以及是否从该地址开始启动程序(startup)。

查看6、AT32官方资料\AT32F435 参考手册(寄存器).pdf 中的2.2、2.3节,发现Flash、RAM的起始地址与图中相同,大小4024K、384K分别乘1024并变化为16进制的字节数3F 0000、6 0000。与图中默认的有些出入,实测修改后没有问题。这里注意Start地址与手册中一样,因为芯片设计是从该ROM地址启动程序的。

(3)Output

(4)Listing

(5)User

(6)C/C++

注意勾选上C99 Mode选项,选项选上后变量可以定义在函数任意位置。这往往成为一个坑,拿找到的例程编译总是报错时可以看看这里。

C99 Mode具体参考:

https://blog.csdn.net/qq_20017379/article/details/122761158

(7)Asm

(8)Linker

(9)Debug

(10)Utilities

官方资料中有mdk5工程

参考……

去除无用部分,简化后工程在……

注意 jlink选择、前宏定义

5.3 AT32VMT7在Keil下的启动流程

5.3.1 AT32存储器资源

在介绍AT32硬件启动流程前,要先来熟悉一下这个芯片的存储器资源,也就是在程序里常能看到的0X2000 0000等类似的地址数值。“6、AT32官方资料\AT32F435 参考手册(寄存器).pdf”文档中的“2 存储器资源”章节详细说明了相关内容。

AT32F435/437存储器资源主要结构如下图:

这里要涉及到了一些计算机学科方面的概念,包括“映射”、“物理存储器”、“虚拟存储器”,作者本人大学专业机械,如有不符还望指正。

以“淘宝”上买一株腊梅花来进行比喻,你在淘宝上看到的这株腊梅她不是真的腊梅,真的腊梅可能是距你手中“淘宝”手里之外的北京。那么“虚拟存储器”代表的便是你在“淘宝”上看到的腊梅,而“物理存储器”则指代身在北京正在生长的腊梅。而所谓映射就可以比喻为“淘宝”。

虽然“淘宝”上的腊梅不能触碰到,但是在“淘宝”上你却可以远程操纵真实的腊梅。比如让快递把她送到某处。你也可以在“淘宝”上了解她的详细信息。映射也具有相同的作用。对虚拟存储器的操作会对应施加在物理存储器上。

然后来介绍存储资源地址从低到高对应的主要部分:

1. 前存储空间 0x0000_0000~0x1FFF_FFFF(512MB)

手册没有命名这一段地址空间,这里先称其为“前存储空间”,该存储空间中主要包含如下区域:

    (1.1)CODE区 0x0000_0000~0x07F F_FFFF(128MB)

这是一段与芯片启动息息相关的地址区域,在芯片上电启动或重启时,会根据BOOT0、BOOT1引脚的高低电平将其他物理存储器的地址映射到此处。

具体方式为:

当{BOOT1,BOOT0}=00/10 时,将 0x0800_0000处的Flash映射到0x0000_0000处。

当{BOOT1,BOOT0}=01 时,将 0x2000_0000处的SRAM映射到0x0000_0000处。

当{BOOT1,BOOT0}=11 时,将 0x2000_0000处的SRAM映射到0x0000_0000处。

    (1.2) Flash 0x0800_0000~0x0F FF_FFFF(128MB),这个芯片只用到~0x083E FFFF(4032KB,即3M)

为物理Flash所在的位置,Flash即快闪存储器,简称闪存,是一种存储器种类,可以看作芯片的硬盘。

    (1.3) SRAM1 映射区 0x1000_0000~0x1000_FFFF(64KB)

为虚拟的SRAM,SRAM全称静态随机存取存储器(Static Random-Access Memory,SRAM)是随机存取存储器的一种,可以看作芯片的内存。

是将0x2000_0000处的物理SRAM1映射到这里的,访问这部分区域,实际就是访问0x2000_0000处的SRAM1

    (1.4)Bootloader flash 0x1FFF_0000~0x1FFF_BFFF(48KB)

为物理Flash,存储着AT官方编写的Bootloader程序,这部分Flash可读不可写。

    (1.5)User system data ? 0x1FFF_C000~0x1FFF_CFFF(4KB)

2. SRAM 0x2000_0000~0x200F_FFFF(1024KB)

 为物理SRAM,分为SRAM1、SRAM2

    SRAM1 ~0x2000_FFFF(64KB)

    SRAM2 这个芯片只用到了0x2001_0000~0x2005_FFFF(320KB)

3. 外设空间 0x4000 0000~0xDFFF FFFF(2GB)

C语言工程中对寄存器的操作实际上就是对一段存储区域进行读写,与对硬盘、内存的操作方式并无本质区别。

    APB1总线 ~0x4000 FFFF(64KB)

    APB2总线 0x4001 0000~0x4001 FFFF(64KB)

    AHB1 0x4002 3000~0xA000 2FFF(1GB)

    AHB总线矩阵 0x4002 0000~0xC000 0000(2GB-1MB)

5.3.2 硬件启动流程

“6、AT32官方资料\AT32F435 参考手册(寄存器).pdf”文档中的“1.1.6 复位流程”章节详细说明了相关内容。

(1)CODE区映射

在 AT32F435/437 中,当上电启动或复位时,可以将主闪存存储器(位于0x0800_0000的Flash)、启动程序存储器(位于0x1FFF_0000的Bootloader flash)或片上 SRAM(位于0x2000_0000的SRAM) 这三块存储器重映射到0x0000_0000~0x07FF_FFFF 的 CODE 区,由 BOOT1 和 BOOT0 管脚来设定 将哪块存储器映射到CODE区。

具体为:

当{BOOT1,BOOT0}=00/10 时,将 0x0800_0000处的Flash映射到0x0000_0000处。

当{BOOT1,BOOT0}=01 时,将 0x2000_0000处的SRAM映射到0x0000_0000处。

当{BOOT1,BOOT0}=11 时,将 0x2000_0000处的SRAM映射到0x0000_0000处。

(2)主栈指针(MSP)与 程序计数器(PC)初始值读取

当映射完成后,处理器会从 CODE 区中读出前两个字。即:

从地址 0x0000_0000 处取出主栈指针(MSP)的初始值。

从地址 0x0000_0004 处取出程序计数器(PC)的初始值,处理器要求PC的初始值的LSB(最低有效位,即二进制下最低位的值) 必须是 1

具体流程如图:

图 5-1  MSP及PC初始化的一个范例

假设{BOOT1,BOOT0},即将 0x0800_0000处的Flash映射到0x0000_0000处,访问0x0000_0000 + k即是访问地址0x0800_0000 + k

32单片机常常是以32位即4个字节为单位来进行存储器操作的,这里同时假设0x0800_0000~0x0800_0003这四个字节存储的值是0x2000_8000,0x0800_0004~0x0800_0007四个字节的值是0x0000_0101

那么当映射完成后:

1. 处理器就会从地址 0x0000_0000 处取出主栈指针(MSP)的初始值0x2000_8000。因为此时对0x0000_0000取值实际就是取0x0800_0000处的数据。

关于主栈指针(MSP),作者的理解MSP就是指向栈地高地址处的指针,而“栈”是一段芯片中的有特殊用途的内存。什么特殊作用呢,作用是当进行函数嵌套调用、中断嵌套调用时保存现场与当前程序位置。

一个子函数执行完成后会使用MSP找到要返回的上一级函数的程序位置,并恢复调用这个子函数前的一些内核寄存器组的值(注意不是外设的寄存器)。

栈所使用内存的大小是有限的(一般在汇编程序中设置大小与位置),所以不能无限的向下调用子函数,不然就寄了。

2. 之后处理器从地址 0x0000_0004 处取出程序计数器(PC)的初始值0x0000_0100。这里注意,赋给程序计数器(PC)的值是不带最低有效位LSB的。

关于程序计数器(PC),这里可以简单的理解为程序计数器(PC)所对应地址处的值,就是接下来要运行的程序(即一个32位的二进制指令)。但是实际上程序计数器(PC)对应的指令会在2个指令周期后才被执行,这就涉及ARM架构取码、译码、执行指令流水线设计了。

这里由于0x0800_0000处的Flash映射到了CODE区,所以(PC)实际指向了地址0x0800_0100处的程序。

3. 之后依次执行编写好的程序

上述操作完成后就是正常的执行程序了。其实 主栈指针(MSP)与 程序计数器(PC)所取的初始值也属于我们编写好的程序的一部分,只不过其通常在汇编.s文件中定义。

5.3.2 软件启动流程

谈完成了一枚芯片在物理层面如何启动,接下来看看实际在Keil中编写的程序是如何运行的。

首先,要明确一点,芯片软件层面最底层的程序是二进制指令,二进制指令中的第个位都由事先定义好的协议来表明不同的操作,而这种底层的指令操作类型是有限的,32单片机一般也就几十种。协议所规定的指令集合在一起,就称为一种“指令集”,AT32所使用的Cotex-M4内核使用的就是Thumb指令集。据了解,这个指令集最初是16位的,后来发展出了其他32位的指令,32单片机中的“32”指的就是指令集的位数,也是处理器一次性处理数据的位数。

但我们当然不会一个个0101地去写程序,而是使用指令集助记符,即“汇编语言”。汇编算是嵌入式中使用的最底层的编程语言了,其本质其实就是将二进制的指令用字符来进行代替,所以常表现为流水依次执行的结构。由于汇编与二进制指令是一一对应的关系,所以不同的指令集对应的汇编语言也不同(一般命令作用、数量都不同)。随着发展,汇编语言也会加入一些“伪操作”,相当于是一个汇编指令集合而成的函数,用来完成常用的功能。

综上所述,软件由汇编文件开始运行,即src\main\startup\startup_at32f435_437.s文件。ARMCC编译器是Keil5软件带有的ARM编译器,ARMCC编译器的伪指令是用于告诉ARMCC编译器如何进行汇编的指令。下表是Keil中常用的伪指令,其他ARM汇编语法参考“2、参考资料\【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.81.pdf”中的“第七章 ARM 汇编基础”

伪指令助记符

格式

功能说明

AREA

AREA 段名 属性1,属性2…

定义一个代码段或者数据段

ENTRY

ENTRY

汇编程序的入口点

END

END

汇编程序的程序的结尾

EQU

常量名 EQU 常量数值

定义一个常量,相当于c语言中的 define

EXPORT

EXPORT 标号

声明一个全局的标号,可以在其他文件使用声明的标号

IMPORT

IMPORT 标号

通知编译器要使用一个在其他文件中定义的标号,相当于c语言中的 extern

DCD

DCD 表达式

用于分配一片连续的字存储单元并用伪指令中指定的表达式初始化,表达式可以是程序标号或数字表达式

PROC

PROC

表示一个汇编子程序的开始。相当于C语言中函数开始。

ENDP

ENDP

表示一个汇编子程序的结束。相当于C语言中函数结束。

ALIGN

编译器对指令或者数据的存放地址进行对齐,一般需要跟一个立即
数,缺省表示 4 字节对齐。要注意的是:这个不是 ARM 的指令,是
编译器的,这里放在一起只是为了方便。  ALIGN=3 表示8(2^3)字节对齐

(1)STACK与HEAP段的定义

14~32行,这一部分定义了两个代码段,似乎这部分段的属性初设置成了不初始化,所以并未被编译到输出文件的开头。。作者至今没搞懂代码段相关的内容,以及Keil汇编执行的流程,因为根据作者了解汇编程序也是有入口的(类似C中的main函数)而不是简单的依次往下。

(2)定义中断向量表

__Vectors       DCD     __initial_sp                 
                DCD     Reset_Handler                                     
                DCD     NMI_Handler
                DCD     HardFault_Handler  
                DCD     MemManage_Handler    
                 ……

              

这里假设为一般地向芯片Flash的0x0800_0000处下载程序

41~173定义中断向量表,实际上就是使用DCD伪指令依次从存储器的开头(即0x0800_0000)依次将每个字(一个字=四个字节)赋值,赋的值为每个中断所对应中断服务函数所有的存储器所在的地址。其中__initial_sp较为特殊,即之前所说的主栈指针(MSP)的初始值,似乎被“提前”定义好了,实测为200006f8,指向一个SRAM地址。由于栈向下增长,在栈中存储的数据会依次占用地址0x2000 06f8、0x2000 06f4、0x2000 06f0……。

其他的中断部分则依次存储对应中断函数所在的地址,Reset_Handler、NMI_Handler……。比如Reset_Handler对应的中断函数位于0x080002ac地址处的Flash,那么0x08000004地址处的值就是0x080002ac,也是程序计数器(PC)的初值。

(3)启动时调用的第一个函数

根据上一节“硬件件启动流程”可知,程序计数器(PC)初始值所指向地址处的代码是所有软件执行的入口。如果是从主Flash启动,那么程序计数器(PC)初始值就是Flash 0x08000004地址上存储的数据,根所汇编文件可知这个数据就是Reset_Handler函数所在程序的地址,即Reset_Handler是芯片启动时调用的第一个函数。

Reset_Handler是一个汇编函数,定义在startup_at32f435_437.s文件的180~189行。

; Reset handler

Reset_Handler   PROC   ;表示一个汇编子程序的开始。相当于C语言中函数开始。

                EXPORT  Reset_Handler                       [WEAK];声明一个全局的标号,可以在其他文件使用声明的标号

                IMPORT  __main   ;通知编译器要使用一个在其他文件中定义的标号,相当于c语言中的 extern

                IMPORT  SystemInit   ;执行SystemInit函数并返回

                LDR     R0, =SystemInit

                BLX     R0

                LDR     R0, =__main   ;执行main函数,不返回? “X”表示切换指令集

                BX      R0

                ENDP   ;表示一个汇编子程序的结束。相当于C语言中函数结束。

可以看出Reset_Handler函数中主要是声明与调用了SystemInit与__main函数,其中调用后者时就“一去不复返”了。

这两个函数终于是C语言函数了,SystemInit是AT官方提供的一个系统初始化函数,定义在“src\main\startup\system_at32f435_437.c”的70~112行。主要功能时初始化时钟与设置中断向量表位置。

/**

  * @brief  setup the microcontroller system

  *         initialize the flash interface.

  * @note   this function should be used only after reset.

  * @param  none

  * @retval none

  */

void SystemInit (void)

{

//  initialiseMemorySections();



#if defined (__FPU_USED) && (__FPU_USED == 1U)

  SCB->CPACR |= ((3U << 10U * 2U) |         /* set cp10 full access */

                 (3U << 11U * 2U)  );       /* set cp11 full access */

#endif



  /* reset the crm clock configuration to the default reset state(for debug purpose) */

  /* set hicken bit */

  CRM->ctrl_bit.hicken = TRUE; /* 使能 内部高速振荡器时钟 HICK */



  /* wait hick stable */

  while(CRM->ctrl_bit.hickstbl != SET); /* 等待 内部调整时钟 稳定 */



  /* hick used as system clock */

  CRM->cfg_bit.sclksel = CRM_SCLK_HICK; /* 系统时钟(SCLK)时钟源选择,内部调整时钟 */



  /* wait sclk switch status */

  while(CRM->cfg_bit.sclksts != CRM_SCLK_HICK); /* 等待 系统时钟(SCLK)设置完成 */



  /* reset cfg register, include sclk switch, ahbdiv, apb1div, apb2div, adcdiv, clkout bits */

  CRM->cfg = 0; /* 复位时钟配置寄存器 */



  /* reset hexten, hextbyps, cfden and pllen bits */

  CRM->ctrl &= ~(0x010D0000U); /* PLL使能、时钟失效检测使能、HEXT 旁路使能、HEXT 使能 */



  /* reset pllms pllns pllfr pllrcs bits */

  CRM->pllcfg = 0x00033002U; /* 设置PLL时钟配置寄存器, HICK为时钟源,输出频率:=8M/2*384/8=192M */



  /* reset clkout[3], usbbufs, hickdiv, clkoutdiv */

  CRM->misc1 = 0;  /* 设置额外寄存器 */



  /* disable all interrupts enable and clear pending bits  */

  CRM->clkint = 0x009F0000U; /* 禁用所有中断,并清除所有挂起的位  */



#ifdef VECT_TAB_SRAM

  SCB->VTOR = SRAM_BASE  | VECT_TAB_OFFSET;  /* vector table relocation in internal sram. */

#else /* 设置中断向量表位置为FLASH_BASE | VECT_TAB_OFFSET,即0x08000000  */

  SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET;  /* vector table relocation in internal flash. */

#endif

}

__main函数则是C标准库中定义的一个函数,该函数最终会调用工程中的main函数,也就是我们所熟悉的C编程部分了。

注意SystemInit (void)函数与之后BF源码初始化中的 init(void) -》systemInit();所调用的systemInit() 不是一个函数(开头字母大小写不同)。

BF Linux环境源码的汇编文件的入口函数也是Reset_Handler,但其内容与Keil中有所出入。如下:

Reset_Handler:

/* custom init */

  ldr   sp, =_estack      /* set stack pointer 设置栈指针*/



  bl persistentObjectInit                       /*保护数据初始化*/

  bl checkForBootLoaderRequest    /*进行一些关于BootLoader支持的操作*/



/* Copy the data segment initializers from flash to SRAM */

  movs  r1, #0

  b  LoopCopyDataInit

可以看出其调用了src/main/drivers/persistent.c中的persistentObjectInit 函数、

src/main/drivers/system_at32f43x.c中的checkForBootLoaderRequest函数

以及LoopCopyDataInit

前两个都属于BF源码,而LoopCopyDataInit属于汇编函数,经过一系列子函数调用,

-》LoopCopyDataInit (77)

-》LoopFillZerobss (84)

-》LoopFillZerofastram_bss (103)

最终在LoopFillZerofastram_bss下调用C语言的main函数

Linux环境与Keil环境下汇编文件的不同在之后的BF移植中要注意。

5.3.3 反汇编验证

在Keil“魔术棒”设置界面的User可以设置扩展应用,如图勾选“Run#2”,并在其后输入 fromelf --text -a -c --output ./Listings/template.dis ./obj/template.axf 即可编译完成后在Listings/文件夹中生成反汇编文件template.dis

反汇编文件是根据输出文件反向转换而来的汇编文件,相当于将C语言、汇编伪指令都解码成了最底层的汇编指令,并给出其所在存储器的具体地址。

打开反汇编文件(可以使用“笔记本”),在开关有如下指令:

  $d.realdata

    RESET

    __Vectors

        存储地址         数据值     ? 对应汇编指令  指令参数

        0x08000000:    200006f8    ...     DCD    536872696

        0x08000004:    080002ad    ....    DCD    134218413

        ……

可以看到,在主Flash的开头0x08000000处储存着的两个值,就是芯片启动时主栈指针(MSP)与 程序计数器(PC)的初始值。0x08000004处数据值为080002ad,也就是要去执行080002ac(舍去LSB)地址处的程序。

向下找到080002ac地址处,发现全是汇编文件中定义的Reset_Handler函数。然后就是依次调用C函数与进入到C工程中了。

 Reset_Handler

        0x080002ac:    4809        .H      LDR      r0,[pc,#36] ; [0x80002d4] = 0x8000671

        0x080002ae:    4780        .G      BLX      r0

        0x080002b0:    4809        .H      LDR      r0,[pc,#36] ; [0x80002d8] = 0x800020d

        0x080002b2:    4700        .G      BX       r0

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值