This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[参考译文] DK-TM4C123G:SD 卡引导加载程序、不稳定的行为和 FatFS

Guru**** 2329980 points
请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/575392/dk-tm4c123g-sd-card-bootloader-erratic-behaviors-with-fatfs

器件型号:DK-TM4C123G

在多次尝试修改 boot_serial 工程并使用汇编启动代码提供的失败后; 虽然看到了许多奇怪的行为,例如使用编译器优化级别更改行为、奇怪的 sp 和 LR 地址,但更改 GPIO 强制更新分配会在以后的另一个点中断程序,有时变量/指针只是在单步执行语句后不设置--

这是我最近尝试创建尽可能简单且易于理解的 SD 卡引导加载程序。

这在理论上似乎是正确的、也应该对其他人有用...  

启动脚本没有什么特别之处、只是通过 Keil 提供的脚本。 fatfs 文件是包含在 TivaWare 中的文件。  

使用此构建:

 当我单步执行 f_mount (0、&fatf)时、我看到 SP 已更改为0x1fff60;  

然后、我将以下内容移至全局区域:

FATFSFatFs;
FILFileObject;
FRESULTfResult; 

这样、就不会使用栈来分配它们、而只是为了查看发生了什么、因为此数据不应首先导致栈溢出或任何问题。 我看不到不稳定的堆栈指针。 但在某些任意点、lr 设置 为0xfffffff9、程序进入 HardFault_Handler。  

我真的无法以任何方式解释这种行为。

实际上、app_start_address 0x2800是使用另一个程序使用 boot_demo2进行刷写的。 我能够从 boot_demo2跳回引导加载程序... 但出于某种原因进入 fatfs 函数、会发生奇怪的情况。 我知道问题不在于 fatfs 代码、它可以处理其他项目。  

//
//
//// bl_main.c -文件保存引导加载程序的主控制循环。
//
//版权所有(c) 2006-2016 Texas Instruments Incorporated。 保留所有权利。
//软件许可协议
//
//德州仪器(TI)提供此软件仅供
和//仅供 TI 的微控制器产品使用。 软件归
// TI 和/或其供应商所有,并受适用的版权
//法律保护。 您不能将此软件与"病毒"开源
//软件组合在一起以形成更大的程序。
//
//此软件按“原样”提供,且存在所有故障。
//对于

本软件,不作任何明示、暗示或法定的保证,包括但不限于对适销性和适用性的暗示保证//特定用途。 在任何
//情况下、TI 不对任何
原因造成的特殊、意外或必然//损害负责。
//
//这是 Tiva 固件开发包版本2.1.3.156的一部分。
////
*****************
//
//
//转换为 SD 引导加载程序并使用引导演示1和2进行测试
//
//*********

#include 
#include 
#include "inc/hw_gpio.h"
#include "iptit/hw_nvic.h"
#include "inc/hw_sysctl.h"
#include
"inc/hw_memmap.h"

#include "fatfs/conf/ff.h"
#include "fatfs/src="/src/ffio.io"
#include "#/driverc/#include "#/drivers/fs/fs/notes/src/#/ffic/now/now/notes/sr.pnings.c/#/now/notes/snings./#/#/#/notes/snow/nings./#/#/#/ff/now/nings.c/now/nings./#/#/#/#/notes/sr.pnings.c/nings.c/now/notes/snings./#/#/#/#/ffics/notes/snings.






//
//确保应用程序的起始地址位于闪存页边界
//
//*********
#if (APP_START_ADDRESS &(FLASH_PAGE_SIZE - 1)
)#ERROR 错误:APP_START_ADDRESS 必须是 FLASH_PAGE_SIZE 字节的倍数!
#endif

//*********
//
//确保闪存保留空间是闪存页的倍数。
////
*****************
#if (FLASH_RSVD_SPACE &(FLASH_PAGE_SIZE - 1))
#ERROR 错误:FLASH_RSVD_SPACE 必须是 FLASH_PAGE_SIZE 字节的倍数!
#endif

//*********
//
//应用程序的起始地址。 这必须是1024
//字节的倍数(使其与页边界对齐)。 在
//这个位置应该有一个矢量表、而矢量表(位于
SRAM 中的堆栈、位于闪存中的复位矢量)的感知有效性被用作
应用程序映像的//有效性的指示。
//
//启动加载程序的闪存映像不得大于此值。
//
//取决于:无
//不包括:无
//要求:无
//
//*********
#define APP_START_ADDRESS 0x2800

//*********
//
//应用程序查找其异常向量表的地址。
//这必须是1024字节的倍数(使其与页//
边界对齐)。 通常、应用程序将从其矢量表开始、
//此值应设置为 APP_START_ADDRESS。 提供此选项的目的
是//满足从外部存储器运行的应用程序的需要,
NVIC 可能无法访问这些应用程序(矢量表偏移寄存器仅为30位
//长)。
//
//取决于:无
//不包括:无
//要求:无
//
//*********
#define VTABLE vstart_address 0x2800

//*********
//
//闪存中单个可擦除页的大小。 这必须是电源
//为2。
//
//取决于:无
//不包括:无
//要求:无
//
//*********
#define FLASH_PAGE_SIZE 0x00000400

//*********
//
//要启用 GPIO 模块以检查强制更新。 这将
//成为 SYSCTL_RCGC2_GPIOx 值之一,其中"x"将替换为端口
//名称(如 B)。 "x"的值应与
// forced_update_port 的"x"的值匹配。
//
//取决于:enable_update_check
//不包括:none
// requss: none
//
//*********
//#define Forced_update_Periph sysctl_RCGC2_GPIOB
#define Forced_update_Periph 0x00000800 //对于 GPIO//

*********
//
// GPIO 端口检查强制更新。 这将是
// GPIO_Portx_BASE 值之一,其中"x"替换为端口名称(如
// B)。 "x"的值应与
// Forced_update_Periph 的"x"的值匹配。
//
//取决于:enable_update_check
//不包括:none
// requss: none
//
//*********
#define Forced_update_port GPIO_PORTM_BASE

//*********
//
//检查强制更新的引脚。 这是一个介于0和7之间的值。
//
//取决于:enable_update_check
//不包括:none
// requss: none
//
//*********
#define Forced_update_pin 0

//*****************
//
//导致强制更新的 GPIO 引脚的极性。 如果
引脚应该为低电平、这个值//应该为0;如果引脚应该为高电平、这个值应该为1。
//
//取决于:enable_update_check
//不包括:none
// requss: none
//
//*********
#define Forced_update_polarity 0

//*********
//
//这为强制
//更新中使用的 GPIO 引脚启用弱上拉或下拉。
只应//定义其中一个 forced_update_wpu 或 forced_update_wpd、如果不需要弱上拉或下拉、则不应这样做。
//
//取决于:enable_update_check
//不包括:none
// requss: none
//
//*********
#define Forced_update_WPU
//#define Forced_update_WPD

//*********
//
//这使得能够使用 GPIO_LOCK 机制来配置
//受保护的 GPIO 引脚(例如 JTAG 引脚)。 如果未定义此值、
//将不使用锁定机制。 此
//功能的唯一合法值是 Fury 器件的 GPIO_LOCK_KEY 和不
支持此功能的 Sandstorm 器件以外的所有//其他器件的 GPIO_LOCK_KEY_DD。
//
//取决于:enable_update_check
//不包括:none
// requss: none
//
//*********
//#define forced_update_key GPIO_LOCK_KEY
//#define Forced_update_key GPIO_LOCK_KEY_DD



#define delay (n) ROM_SysCtlDelay ((ROM_SysCtlClockGet ()/3000000)*n)


//extern void CallApplication ();

uint32_t g_ui32Forced = 0;

//*********
//
//! 检查 GPIO 是否有强制更新。
//!
//! 此函数检查 GPIO 的状态以确定是否正在进行更新
//! 已申请。
//!
//! 如果正在请求更新、则返回非零值、并且为零
//! 否则。
////
*****************
uint32_t
CheckGPIOForceUpdate (void)
{
//
//启用所需的 GPIO 模块。
//
HWREG (SYSCTL_RCGCGPIO)|= Forced_update_Periph;

//
//在访问外设之前等待一段时间。
//
延迟(3);

#ifdef Forced_update_key
//
//解锁 GPIO 访问。
//
HWREG (Forced_update_port + GPIO_lock)= Forced_update_key;
HWREG (Forced_update_port + GPIO_CR)= 1 << Forced_update_PIN;
#endif

//
//启用用于查看是否正在请求更新的引脚。
//
HWREG (Forced_update_port + GPIO_DEN)|= 1 << Forced_update_PIN;
#ifdef Forced_update_WPU
//
//设置输出驱动强度。
//
HWREG (Forced_update_port + GPIO_DR2R)|= 1 << Forced_update_PIN;

//
//启用弱上拉。
//
HWREG (Forced_update_port + GPIO_PUR)|= 1 << Forced_update_PIN;

//
//确保该引脚的模拟模式选择寄存器是清零的。
//
HWREG (Forced_update_port + GPIO_AMSEL)&=~(1 << Forced_update_pin);
#endif
#ifdef Forced_update_wpd
//
//设置输出驱动强度。
//
HWREG (Forced_update_port + GPIO_DR2R)|= 1 << Forced_update_PIN;

//
//启用弱下拉。
//
HWREG (Forced_update_port + GPIO_PDR)|= 1 << Forced_update_PIN;

//
//确保该引脚的模拟模式选择寄存器是清零的。
//该寄存器只出现在 DustDevil 类(及更高版本)设备中,但是
//是对 Sandstorm 和 Fury 级设备的无害写入。
//
HWREG (Forced_update_port + GPIO_AMSEL)&=~(1 << Forced_update_PIN);
#endif

#ifdef Forced_update_key
//
//解锁 GPIO 访问。
//
HWREG (Forced_update_port + GPIO_lock)= Forced_update_key;
HWREG (Forced_update_port + GPIO_CR)= 0;
#endif

//
//在读取引脚之前等待一段时间。
//
延迟(1000);

//
//检查引脚以查看是否正在请求更新。
//
if (HWREG (Forced_update_port +(1 <<(Forced_update_PIN + 2)))=
(Forced_update_polarity << Forced_update_PIN)
{
//
//请记住这是强制更新。
//
G_ui32 Forced = 1;

返回(1);
}

//
//未请求更新,因此返回0。
//
退货(0);
}


//*********
//
//! 检查是否需要或正在请求更新。
//!
//! 此函数检测是否请求更新或是否没有
//! 当前位于微控制器上的有效代码。 这用于告诉
//! 是否进入更新模式。
//!
//! 如果需要更新或正在
进行//、\返回返回非零值! 而不是0。
////
*****************
uint32_t
CheckForceUpdate (void)
{

uint32_t * pui32App;

//
//查看第一个位置是0xffffffffff 还是不是这样的位置
//看起来像堆栈指针,或者第二个位置是0xffffffff 或
//看起来不像复位矢量的内容。
//
pui32App =(uint32_t *) app_start_address;
if ((pui32App[0]= 0xffffffff)||
((pui32App[0]& 0xFF00000)!= 0x20000000)||
(pui32App[1]=0xffffffff)||
((pui32App[1]和0xfff00001)!= 0x00000001))
{
返回(1);
}

//
//如果配置了简单的 GPIO 检查,则确定是否强制执行
//更新。
//
return (CheckGPIOForceUpdate());
}

//*********
//
//! 配置微控制器。
//!
//! 此函数用于配置微控制器的外设和 GPIO、
//! 准备供引导加载程序使用。 已经//
!的接口 因为更新端口将被配置、自动波特率将为
//! 必要时执行。
//!
//! \无返回。
////
*****************
void
ConfigureDevice (void)
{
//
//由于指定了晶振频率,因此启用主振荡器
//并从它为处理器计时。
//
// HWREG (SYSCTL_RCC)&&~(SYSCTL_RCC_MOSCDIS);
//延迟(524288);
// HWREG (SYSCTL_RCC)=((HWREG (SYSCTL_RCC)&~(SYSCTL_RCC_OSCSRC_M)))|
// SYSCTL_RCC_OSCSRC_MAIN);
//ROM_SysCtlClockSet (SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_INT | SYSCTL_MAIN_OSC_DIS);//为 ROM_SysCtlDelay 启用

int nStatus;

HWREG (SYSCTL_RCGCGPIO)&=~Forced_update_Periph;

ROM_SysCtlClockSet (SYSCTL_USE_OSC | SYSCTL_OSC_INT);
nStatus = ROM_SysCtlClockGet ();

//
//为100Hz 中断配置 SysTick。 FatFs 驱动程序需要10ms
//勾选。
//
ROM_SysTickPeriodSet (ROM_SysCtlClockGet ()/ 100);
ROM_SysTickEnable();
ROM_SysTickIntEnable();

ROM_IntMasterEnable();
}//


*********
//
//! 此函数对所选端口执行更新。
//!
//! 此函数由引导加载程序直接调用或作为
//! 应用程序更新请求的结果。
//!
//! 返回但不返回。
////
*****************
void
Updater (void)
{
FATFSFatFs;
FILFileObject;
FRESULTfResult;

uint32_t addr、count、 data、temp;


fResult = f_mount (0、&fatf);
//fResult = pf_mount (&fatf);

if (fResult = FR_OK)
{
fResult = f_open (&FileObject、"boot2.bin"、FA_read);
//fResult = pf_open ("boot2.bin");

//temp = 0;
//do
//
fResult = pf_open ("boot2.bin");//

fresult

=
/+
!/fresult = temp =/f_start_addr (针对文件启动);/fif_file=/f <+!/fr =/f 文件启动);/fr =/f 文件启动!(针对文件启动) ADDR += FLASH_PAGE_SIZE)
//for (addr = app_start_address;addr < app_start_address + fatfs.fsize;addr += flash_page_size)
{
//
擦除此块。
//
rom_FlashErase (addr);
}

for (addr = app_start_address;addr < app_start_address + FileObject.fsize;addr += 4)
// for (addr = app_start_address;addr < app_start_address + fatdr.fsize;adfs += 4)
{
//
//////将

数据写入到闪存中(4个字节
)、写入数据(4)、写入数据(4)、写入数据(4)、写入数据(4个字节)、数据(4)、写入数据(4个数据)、数据(4个数据) 4、&count);
ROM_FlashProgram (&data、addr、4);
}
}
//

HWREG (NVIC_APINT)=(NVIC_APINT_VECTKEY | NVIC_APINT_SYSRESETREQ);

while (1);
}

//*********
//
//这是该 SysTick 中断的处理程序。 FatFs 需要一个计时器节拍
//每10ms 用于内部计时。
////
*****************
void
SysTickHandler (void)
{
//
//调用 FatFs tick 计时器。
//
Disk_timerproC();
}


void SVC_Handler (void)
{
ConfigureDevice();
Updater ();
while (1);
}

int
main (void)
{
uint32_t t = 213;

t = ROM_SysCtlClockGet ();

if (CheckForceUpdate())
{
ConfigureDevice();
Updater ();
}
else
{
ROM_SysTickIntDisable();
ROM_SysTickDisable();

HWREG (NVIC_DIS0)= 0xffffffff;
HWREG (NVIC_DIS1)= 0xffffffff;

(*(void (*)(void))(*(uint32_t *)(app_start_address+4))))();
}

while (1);
}

//*********
//
//关闭 Doxygen 组。
//! @}
//
//*********

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    您好 Scott

    您是否更新了 VTABLE 寄存器以指向要使用的新中断矢量表的应用程序基址?
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    很好的收获 我确实纠正了这一点、但我认为至少现在不会造成问题。 因为当程序跳转到 APP_ADDRESS 并跳回到引导加载程序时、VTABLE 会保持为0。 我想、如果实际应用程序运行中断、这会导致问题。  

    出于某种原因,f_mount ()中的行“RFS = FatFs[vol];//获取当前的 FS 对象*/”会导致 RFS 设置为某些0x80000地址,并且程序稍后在 “if (FS){”处出现硬故障  
    这些似乎是任意的线...

    但是、我确实注意到、我之前尝试使用 PeteFS 时、实际上由于使用堆栈空间定义函数内的 FATFS 结构而出现了一些问题。 将其移到外部似乎已解决问题。

    这很奇怪、因为我在这里找到的示例工作正常、它在一个函数内定义了 FatFs: github.com/.../Tiva-SD-Card-Boot-Loader

    不管怎样,我现在都在工作。 任何人都可以尝试它

    //
    //
    //// bl_main.c -文件保存引导加载程序的主控制循环。
    //
    //版权所有(c) 2006-2016 Texas Instruments Incorporated。 保留所有权利。
    //软件许可协议
    //
    //德州仪器(TI)提供此软件仅供
    和//仅供 TI 的微控制器产品使用。 软件归
    // TI 和/或其供应商所有,并受适用的版权
    //法律保护。 您不能将此软件与"病毒"开源
    //软件组合在一起以形成更大的程序。
    //
    //此软件按“原样”提供,且存在所有故障。
    //对于
    
    本软件,不作任何明示、暗示或法定的保证,包括但不限于对适销性和适用性的暗示保证//特定用途。 在任何
    //情况下、TI 不对任何
    原因造成的特殊、意外或必然//损害负责。
    //
    //这是 Tiva 固件开发包版本2.1.3.156的一部分。
    ////
    *****************
    //
    //
    //转换为 SD 引导加载程序并使用引导演示1和2进行测试
    //
    //*********
    
    #include 
    #include 
    #include "inc/hw_gpio.h"
    #include "iptit/hw_NVIC.h"
    #include "inc/hw_sysctl.h"
    #include "inc/hw_types.h"
    #include "inc/hw_memmap.h"
    
    //#include "fatfs/src="/src/ff.h"
    
    #include "fatfs/iptc/now/driver3*
    #include "level/drive/src/包含#ffic/#defic/#deficl"
    
    
    
    
    
    //
    //确保应用程序的起始地址位于闪存页边界
    //
    //*********
    #if (APP_START_ADDRESS &(FLASH_PAGE_SIZE - 1)
    )#ERROR 错误:APP_START_ADDRESS 必须是 FLASH_PAGE_SIZE 字节的倍数!
    #endif
    
    //*********
    //
    //确保闪存保留空间是闪存页的倍数。
    ////
    *****************
    #if (FLASH_RSVD_SPACE &(FLASH_PAGE_SIZE - 1))
    #ERROR 错误:FLASH_RSVD_SPACE 必须是 FLASH_PAGE_SIZE 字节的倍数!
    #endif
    
    //*********
    //
    //应用程序的起始地址。 这必须是1024
    //字节的倍数(使其与页边界对齐)。 在
    //这个位置应该有一个矢量表、而矢量表(位于
    SRAM 中的堆栈、位于闪存中的复位矢量)的感知有效性被用作
    应用程序映像的//有效性的指示。
    //
    //启动加载程序的闪存映像不得大于此值。
    //
    //取决于:无
    //不包括:无
    //要求:无
    //
    //*********
    #define APP_START_ADDRESS 0x2800
    
    //*********
    //
    //应用程序查找其异常向量表的地址。
    //这必须是1024字节的倍数(使其与页//
    边界对齐)。 通常、应用程序将从其矢量表开始、
    //此值应设置为 APP_START_ADDRESS。 提供此选项的目的
    是//满足从外部存储器运行的应用程序的需要,
    NVIC 可能无法访问这些应用程序(矢量表偏移寄存器仅为30位
    //长)。
    //
    //取决于:无
    //不包括:无
    //要求:无
    //
    //*********
    #define VTABLE vstart_address 0x2800
    
    //*********
    //
    //闪存中单个可擦除页的大小。 这必须是电源
    //为2。
    //
    //取决于:无
    //不包括:无
    //要求:无
    //
    //*********
    #define FLASH_PAGE_SIZE 0x00000400
    
    //*********
    //
    //要启用 GPIO 模块以检查强制更新。 这将
    //成为 SYSCTL_RCGC2_GPIOx 值之一,其中"x"将替换为端口
    //名称(如 B)。 "x"的值应与
    // forced_update_port 的"x"的值匹配。
    //
    //取决于:enable_update_check
    //不包括:none
    // requss: none
    //
    //*********
    //#define Forced_update_Periph sysctl_RCGC2_GPIOB
    #define Forced_update_Periph 0x00000800 //对于 GPIO//
    
    *********
    //
    // GPIO 端口检查强制更新。 这将是
    // GPIO_Portx_BASE 值之一,其中"x"替换为端口名称(如
    // B)。 "x"的值应与
    // Forced_update_Periph 的"x"的值匹配。
    //
    //取决于:enable_update_check
    //不包括:none
    // requss: none
    //
    //*********
    #define Forced_update_port GPIO_PORTM_BASE
    
    //*********
    //
    //检查强制更新的引脚。 这是一个介于0和7之间的值。
    //
    //取决于:enable_update_check
    //不包括:none
    // requss: none
    //
    //*********
    #define Forced_update_pin 0
    
    //*****************
    //
    //导致强制更新的 GPIO 引脚的极性。 如果
    引脚应该为低电平、这个值//应该为0;如果引脚应该为高电平、这个值应该为1。
    //
    //取决于:enable_update_check
    //不包括:none
    // requss: none
    //
    //*********
    #define Forced_update_polarity 0
    
    //*********
    //
    //这为强制
    //更新中使用的 GPIO 引脚启用弱上拉或下拉。
    只应//定义其中一个 forced_update_wpu 或 forced_update_wpd、如果不需要弱上拉或下拉、则不应这样做。
    //
    //取决于:enable_update_check
    //不包括:none
    // requss: none
    //
    //*********
    #define Forced_update_WPU
    //#define Forced_update_WPD
    
    //*********
    //
    //这使得能够使用 GPIO_LOCK 机制来配置
    //受保护的 GPIO 引脚(例如 JTAG 引脚)。 如果未定义此值、
    //将不使用锁定机制。 此
    //功能的唯一合法值是 Fury 器件的 GPIO_LOCK_KEY 和不
    支持此功能的 Sandstorm 器件以外的所有//其他器件的 GPIO_LOCK_KEY_DD。
    //
    //取决于:enable_update_check
    //不包括:none
    // requss: none
    //
    //*********
    //#define forced_update_key GPIO_LOCK_KEY
    //#define Forced_update_key GPIO_LOCK_KEY_DD
    
    
    
    #define delay (n) ROM_SysCtlDelay ((ROM_SysCtlClockGet ()/3000000)*n)
    
    
    //extern void CallApplication ();
    
    uint32_t g_ui32Forced = 0;
    
    FATFSFatFs;
    //FILFileObject;
    
    //*********
    //
    //! 检查 GPIO 是否有强制更新。
    //!
    //! 此函数检查 GPIO 的状态以确定是否正在进行更新
    //! 已申请。
    //!
    //! 如果正在请求更新、则返回非零值、并且为零
    //! 否则。
    ////
    *****************
    uint32_t
    CheckGPIOForceUpdate (void)
    {
    //
    //启用所需的 GPIO 模块。
    //
    HWREG (SYSCTL_RCGCGPIO)|= Forced_update_Periph;
    
    //
    //在访问外设之前等待一段时间。
    //
    延迟(3);
    
    #ifdef Forced_update_key
    //
    //解锁 GPIO 访问。
    //
    HWREG (Forced_update_port + GPIO_lock)= Forced_update_key;
    HWREG (Forced_update_port + GPIO_CR)= 1 << Forced_update_PIN;
    #endif
    
    //
    //启用用于查看是否正在请求更新的引脚。
    //
    HWREG (Forced_update_port + GPIO_DEN)|= 1 << Forced_update_PIN;
    #ifdef Forced_update_WPU
    //
    //设置输出驱动强度。
    //
    HWREG (Forced_update_port + GPIO_DR2R)|= 1 << Forced_update_PIN;
    
    //
    //启用弱上拉。
    //
    HWREG (Forced_update_port + GPIO_PUR)|= 1 << Forced_update_PIN;
    
    //
    //确保该引脚的模拟模式选择寄存器是清零的。
    //
    HWREG (Forced_update_port + GPIO_AMSEL)&=~(1 << Forced_update_pin);
    #endif
    #ifdef Forced_update_wpd
    //
    //设置输出驱动强度。
    //
    HWREG (Forced_update_port + GPIO_DR2R)|= 1 << Forced_update_PIN;
    
    //
    //启用弱下拉。
    //
    HWREG (Forced_update_port + GPIO_PDR)|= 1 << Forced_update_PIN;
    
    //
    //确保该引脚的模拟模式选择寄存器是清零的。
    //该寄存器只出现在 DustDevil 类(及更高版本)设备中,但是
    //是对 Sandstorm 和 Fury 级设备的无害写入。
    //
    HWREG (Forced_update_port + GPIO_AMSEL)&=~(1 << Forced_update_PIN);
    #endif
    
    #ifdef Forced_update_key
    //
    //解锁 GPIO 访问。
    //
    HWREG (Forced_update_port + GPIO_lock)= Forced_update_key;
    HWREG (Forced_update_port + GPIO_CR)= 0;
    #endif
    
    //
    //在读取引脚之前等待一段时间。
    //
    延迟(1000);
    
    //
    //检查引脚以查看是否正在请求更新。
    //
    if (HWREG (Forced_update_port +(1 <<(Forced_update_PIN + 2)))=
    (Forced_update_polarity << Forced_update_PIN)
    {
    //
    //请记住这是强制更新。
    //
    G_ui32 Forced = 1;
    
    返回(1);
    }
    
    //
    //未请求更新,因此返回0。
    //
    退货(0);
    }
    
    
    //*********
    //
    //! 检查是否需要或正在请求更新。
    //!
    //! 此函数检测是否请求更新或是否没有
    //! 当前位于微控制器上的有效代码。 这用于告诉
    //! 是否进入更新模式。
    //!
    //! 如果需要更新或正在
    进行//、\返回返回非零值! 而不是0。
    ////
    *****************
    uint32_t
    CheckForceUpdate (void)
    {
    
    uint32_t * pui32App;
    
    //
    //查看第一个位置是0xffffffffff 还是不是这样的位置
    //看起来像堆栈指针,或者第二个位置是0xffffffff 或
    //看起来不像复位矢量的内容。
    //
    pui32App =(uint32_t *) app_start_address;
    if ((pui32App[0]= 0xffffffff)||
    ((pui32App[0]& 0xFF00000)!= 0x20000000)||
    (pui32App[1]=0xffffffff)||
    ((pui32App[1]和0xfff00001)!= 0x00000001))
    {
    返回(1);
    }
    
    //
    //如果配置了简单的 GPIO 检查,则确定是否强制执行
    //更新。
    //
    return (CheckGPIOForceUpdate());
    }
    
    //*********
    //
    //! 配置微控制器。
    //!
    //! 此函数用于配置微控制器的外设和 GPIO、
    //! 准备供引导加载程序使用。 已经//
    !的接口 因为更新端口将被配置、自动波特率将为
    //! 必要时执行。
    //!
    //! \无返回。
    ////
    *****************
    void
    ConfigureDevice (void)
    {
    //
    //由于指定了晶振频率,因此启用主振荡器
    //并从它为处理器计时。
    //
    // HWREG (SYSCTL_RCC)&&~(SYSCTL_RCC_MOSCDIS);
    //延迟(524288);
    // HWREG (SYSCTL_RCC)=((HWREG (SYSCTL_RCC)&~(SYSCTL_RCC_OSCSRC_M)))|
    // SYSCTL_RCC_OSCSRC_MAIN);
    
    HWREG (SYSCTL_RCGCGPIO)&&=~Forced_update_Periph;
    
    // ROM_SysCtlClockSet (SYSCTL_USE_OSC | SYSCL_INT);//启用 ROM_SysCtlDelay
    
    //
    ////////////配置100Hz 中断。 FatFS 驱动程序需要一
    个10ms //// tick。
    //
    ////// rom_SysTickPeriodSet (ROM_SysCtlClockGet ()/ 100);
    // ROM_SysTickEnable ();
    // ROM_SysTickIntEnable ();
    // ROM_SysIntEnable
    ();
    // ROM_IntMasterEnable ();}
    
    
    //*************
    //
    //! 此函数对所选端口执行更新。
    //!
    //! 此函数由引导加载程序直接调用或作为
    //! 应用程序更新请求的结果。
    //!
    //! 返回但不返回。
    ////
    *****************
    void
    Updater (void)
    {
    FRESULTfResult;
    
    uint32_t addr、count、data;
    
    //fResult = f_mount (0、 FatFs);
    fResult = pf_mount (&FatFs);
    
    if (fResult = FR_OK)
    {
    //fResult = f_open (&FileObject、"boot2.bin"、FA_read);
    fAddr = pf_open ("boot2.bin");
    
    if (fResult = f_open="boot_file"="fr_start_address"、f_addr//addr_start_addr/
    
    addr/ addr./addr_resum_addr/ addr_addr/ addr_addr/ addr_start_addr/ addr_addr_addr/ addr_addr =/addr_addr_addr_addr =/addr_addr_addr./addr_addr./addr_addr =/add
    
    
    
    
    //
    rom_FlashErase (addr);
    }
    
    //for (addr = app_start_address;addr < app_start_address + FileObject.fsize;addr += 4)
    for (addr = app_start_address;addr < app_start_address + Fatdr.fsize;adfs += 4)
    {
    //
    //////将
    
    数据写入到闪存/
    数据、写入4个字节(4)、写入数据、写入4个数据、写入到数据、写入数据、写入4个数据(4个数据字节) 4、&count);
    ROM_FlashProgram (&data、addr、4);
    }
    }
    //
    
    HWREG (NVIC_APINT)=(NVIC_APINT_VECTKEY | NVIC_APINT_SYSRESETREQ);
    
    while (1);
    }
    
    //*********
    ////
    ////这是该 SysTick 中断的处理程序。 FatFs 需要一个计时器节拍
    ////每10ms 用于内部计时。
    ///////////*************
    
    //void
    //SysTick 处理程序(void)
    //{
    //
    //////////////调用 FatFs tick 计时器。
    ////
    //// disk_timerproc ();
    ////
    
    
    // void SVC_Handler (void)
    //{
    //HWREG (NVIC_vtable)= 0;
    // HWREG (NVIC_en0)= 0xffffffff;
    // HWREG (NVIC_EN1)= Forceffff;
    //ConfigureDevice ()=
    
    
    
    
    
    
    
    //(//
    
    
    
    ()//(//)/INT32);//(//(//)/uLockUpdater (//);//(//(//)/iD/(/t/i20/(/i20/()//()//(//))/iD/(/iD/(/i20/)/i20/(//)/iD/(/iD/)/iD/)/iD/(/iD/(/iD
    
    
    //Updater ();
    //else
    
    //{
    // rom_SysTickIntDisable ();
    // rom_SysTickDisable ();
    //rom_IntMasterDisable ();
    //
    HWREG (NVIC_DIS0)= 0xFFFF;
    // HWREG (NVIC_DIS1)= 0xFFFF;//
    
    HWREG (NVIC_DIS1)=//(*)
    
    //(void)////(*)/*(/*)/*(void (/*
    
    
    )/*)/*(/*)/*(////_start/*(void)/*(/*)/*(//*)//*(///////////////////////////////////////////////_
    
    
    
    //
    //关闭 Doxygen 组。
    //! @}
    //
    //*********
    

    启动脚本是

    ;<<<使用上下文菜单中的配置向导>>>
    ;*********
    ;
    ; startup_rvmdk.S -用于 Keil uVision 的启动代码。
    ;
    版权所有(c) 2013-2015 Texas Instruments Incorporated。 保留所有权利。
    ;软件许可协议
    ;
    ;德州仪器(TI)提供此软件仅供和
    使用;仅供 TI 的微控制器产品使用。 该软件归
    ;TI 和/或其供应商所有,并受适用的版权
    ;法律的保护。 您不能将此软件与“病毒”开源
    软件结合使用,以形成更大的程序。
    ;
    ;此软件按“原样”提供,且存在所有缺陷。
    ;没有任何明示、暗示或法定的保证,包括: 但
    不限于对适销性和适用性的暗示保证
    ;特定用途适用于本软件。 在任何
    情况下、TI 都不应对因
    任何原因造成的特殊、意外或必然的损害负责。
    ;
    ;这是 DK-TM4C123G 固件包的版本2.1.2.111的一部分。
    ;
    
    
    ;包含 bl_config.inc
    ;_STACK_SIZE equ 128;0x00000100;48
    _app_start_address equ 0x2800
    _vtable_start_address equ 0x2800
    
    ;*********
    ;
    ;通常从适当的 C 获得的几个定义
    ;头文件,但必须在此处手动提供,因为 Keil 编译器没有
    ;没有通过 C 预处理器传递汇编源代码的机制。
    ;
    
    ;sysctl_RESC equ 0x400fe05c
    ;sysctl_RESC_MOSCFAIL equ 0x00010000
    NVIC_vtable equ 0xe000ed08
    
    ;*********
    ;
    堆栈大小(以字节为单位)<0x0-0xFFFFFFFF:8>
    ;
    *********
    堆栈 EQU 0x00000400
    
    ;*********
    ;
    堆大小(以字节为单位)<0x0-0xFFFFFFFF:8>
    ;
    *********
    堆 EQU 0x00000000
    
    ;*********
    ;
    ;为堆栈分配空间。
    ;
    
    区域堆栈、NOINIT、READWRITE、ALIGN=3
    StackMem
    太空堆栈
    __initial;
    
    *********
    ;
    ;为堆分配空间。
    ;
    
    区域堆、NOINIT、READWRITE、align=3
    _heap_base
    HeapMem
    空间堆
    _heap_limit
    
    ;*********
    ;
    指示此文件中的代码保留堆栈的8字节对齐。
    ;
    
    PRESERVE8
    
    
    ;
    ;将代码放入复位代码部分。
    ;
    
    区域复位、代码、只读
    拇指
    
    
    ;
    ;应用程序使用的中断处理程序的外部声明。
    ;
    
    ; 外部 SysTickHandler
    
    ;*********
    ;
    ;向量表。
    ;
    
    导出__Vectors
    __Vectors
    DCD StackMem +堆栈 ;栈顶
    DCD RESET_Handler ;重置处理程序
    DCD NmiSR ; NMI 处理程序
    DCD FaultISR ;硬故障处理程序
    DCD IntDefaultHandler ;MPU 故障处理程序
    DCD IntDefaultHandler ;总线故障处理程序
    DCD IntDefaultHandler ;用法故障处理程序
    DCD 0 ;保留
    DCD 0 ;保留
    DCD 0 ;保留
    DCD 0 ;保留
    DCD UpdateHandler ; SVCall 处理程序
    DCD IntDefaultHandler ;调试监视器处理程序
    DCD 0 ;保留
    DCD IntDefaultHandler ; PendSV 处理程序
    DCD IntDefaultHandler ; SysTick 处理程序
    DCD IntDefaultHandler ;GPIO 端口 A
    DCD IntDefaultHandler ;GPIO 端口 B
    DCD IntDefaultHandler ;GPIO 端口 C
    DCD IntDefaultHandler ;GPIO 端口 D
    DCD IntDefaultHandler ;GPIO 端口 E
    DCD IntDefaultHandler ;UART0 Rx 和 Tx
    DCD IntDefaultHandler ;UART1 Rx 和 Tx
    DCD IntDefaultHandler ;SSI0 Rx 和 Tx
    DCD IntDefaultHandler ; I2C0主机和从机
    DCD IntDefaultHandler ;PWM 故障
    DCD IntDefaultHandler ; PWM 发生器0
    DCD IntDefaultHandler ;PWM 发生器1
    DCD IntDefaultHandler ;PWM 发生器2
    DCD IntDefaultHandler ;正交编码器0
    DCD IntDefaultHandler ;ADC 序列0
    DCD IntDefaultHandler ;ADC 序列1
    DCD IntDefaultHandler ;ADC 序列2
    DCD IntDefaultHandler ;ADC 序列3.
    DCD IntDefaultHandler ;看门狗定时器
    DCD IntDefaultHandler ; Timer 0子定时器 A
    DCD IntDefaultHandler ; Timer 0子定时器 B
    DCD IntDefaultHandler ; Timer 1子定时器 A
    DCD IntDefaultHandler ; Timer 1子定时器 B
    DCD IntDefaultHandler ; Timer 2子定时器 A
    DCD IntDefaultHandler ; Timer 2子定时器 B
    DCD IntDefaultHandler ;模拟比较器0
    DCD IntDefaultHandler ;模拟比较器1
    DCD IntDefaultHandler ;模拟比较器2.
    DCD IntDefaultHandler ;系统控制(PLL、OSC、BO)
    DCD IntDefaultHandler ; Flash 控制
    DCD IntDefaultHandler ;GPIO 端口 F
    DCD IntDefaultHandler ;GPIO 端口 G
    DCD IntDefaultHandler ;GPIO 端口 H
    DCD IntDefaultHandler ;UART2 Rx 和 Tx
    DCD IntDefaultHandler ;SSI1 Rx 和 Tx
    DCD IntDefaultHandler ; Timer 3子计时器 A
    DCD IntDefaultHandler ; Timer 3子定时器 B
    DCD IntDefaultHandler ; I2C1主设备和从设备
    DCD IntDefaultHandler ;正交编码器1
    DCD IntDefaultHandler ;CAN0
    DCD IntDefaultHandler ;CAN1
    DCD 0 ;保留
    DCD 0 ;保留
    DCD IntDefaultHandler ;休眠
    DCD IntDefaultHandler ;USB0
    DCD IntDefaultHandler ;PWM 发生器3.
    DCD IntDefaultHandler ;UDMA 软件传输
    DCD IntDefaultHandler ; UDMA 错误
    DCD IntDefaultHandler ;ADC1序列0
    DCD IntDefaultHandler ;ADC1序列1
    DCD IntDefaultHandler ;ADC1序列2.
    DCD IntDefaultHandler ;ADC1序列3.
    DCD 0 ;保留
    DCD 0 ;保留
    DCD IntDefaultHandler ;GPIO 端口 J
    DCD IntDefaultHandler ;GPIO 端口 K
    DCD IntDefaultHandler ;GPIO 端口 L
    DCD IntDefaultHandler ;SSI2 Rx 和 Tx
    DCD IntDefaultHandler ;SSI3 Rx 和 Tx
    DCD IntDefaultHandler ;UART3 Rx 和 Tx
    DCD IntDefaultHandler ;UART4 Rx 和 Tx
    DCD IntDefaultHandler ;UART5 Rx 和 Tx
    DCD IntDefaultHandler ;UART6 Rx 和 Tx
    DCD IntDefaultHandler ;UART7 Rx 和 Tx
    DCD 0 ;保留
    DCD 0 ;保留
    DCD 0 ;保留
    DCD 0 ;保留
    DCD IntDefaultHandler ; I2C2主设备和从设备
    DCD IntDefaultHandler ; I2C3主设备和从设备
    DCD IntDefaultHandler ;计时器4子计时器 A
    DCD IntDefaultHandler ;定时器4子定时器 B
    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 0 ;保留
    DCD 0 ;保留
    DCD 0 ;保留
    DCD IntDefaultHandler ; Timer 5子计时器 A
    DCD IntDefaultHandler ; Timer 5子定时器 B
    DCD IntDefaultHandler ;宽定时器0子定时器 A
    DCD IntDefaultHandler ;宽定时器0子定时器 B
    DCD IntDefaultHandler ;宽定时器1子定时器 A
    DCD IntDefaultHandler ;宽定时器1子定时器 B
    DCD IntDefaultHandler ;宽定时器2子定时器 A
    DCD IntDefaultHandler ;宽定时器2子定时器 B
    DCD IntDefaultHandler ;宽定时器3子定时器 A
    DCD IntDefaultHandler ;宽定时器3子定时器 B
    DCD IntDefaultHandler ;宽定时器4子定时器 A
    DCD IntDefaultHandler ;宽定时器4子定时器 B
    DCD IntDefaultHandler ;宽定时器5子定时器 A
    DCD IntDefaultHandler ;宽定时器5子定时器 B
    DCD IntDefaultHandler ;FPU
    DCD 0 ;保留
    DCD 0 ;保留
    DCD IntDefaultHandler ; I2C4主设备和从属设备
    DCD IntDefaultHandler ; I2C5主设备和从设备
    DCD IntDefaultHandler ;GPIO 端口 M
    DCD IntDefaultHandler ;GPIO 端口 N
    DCD IntDefaultHandler ;正交编码器2.
    DCD 0 ;保留
    DCD 0 ;保留
    DCD IntDefaultHandler ;GPIO 端口 P (摘要或 P0)
    DCD IntDefaultHandler ;GPIO 端口 P1
    DCD IntDefaultHandler ;GPIO 端口 P2
    DCD IntDefaultHandler ;GPIO 端口 P3
    DCD IntDefaultHandler ;GPIO 端口 P4
    DCD IntDefaultHandler ;GPIO 端口 P5
    DCD IntDefaultHandler ;GPIO 端口 P6
    DCD IntDefaultHandler ;GPIO 端口 P7
    DCD IntDefaultHandler ;GPIO 端口 Q (摘要或 Q0)
    DCD IntDefaultHandler ;GPIO 端口 Q1
    DCD IntDefaultHandler ;GPIO 端口 Q2
    DCD IntDefaultHandler ;GPIO 端口 Q3
    DCD IntDefaultHandler ;GPIO 端口 Q4
    DCD IntDefaultHandler ;GPIO 端口 Q5
    DCD IntDefaultHandler ;GPIO 端口 Q6
    DCD IntDefaultHandler ;GPIO 端口 Q7
    DCD IntDefaultHandler ;GPIO 端口 R
    DCD IntDefaultHandler ;GPIO 端口 S
    DCD IntDefaultHandler ;PWM 1发生器0
    DCD IntDefaultHandler ;PWM 1发生器1
    DCD IntDefaultHandler ;PWM 1发生器2
    DCD IntDefaultHandler ;PWM 1发生器3
    DCD IntDefaultHandler ;PWM 1故障
    
    ;*********
    ;
    ;这是处理器首次开始执行
    时调用的代码;在复位事件之后。
    ;
    
    ;导出 Reset_Handler
    ; Reset_Handler
    ;
    ;启用浮点单元。 必须在此处执行此操作才能处理
    ;main()使用浮点且函数 prologueue 保存的情况
    ;浮点寄存器(如果浮点不是浮点则会出现故障)
    ;;已启用)。 使用的浮点单元的任何配置
    ;DriverLib API 必须在浮点单元之前在此处完成
    ;正在启用。
    ;
    ;;请注意,这不使用 DriverLib,因为它可能不包含在内
    ;在本项目中。
    ;
    ;MOVW R0、#0xED88
    ;MOVT R0、#0xE000
    ; LDR R1、[R0]
    ;ORR R1、#0x00F00000
    ;STR R1、[R0]
    
    ;
    ;;调用 C 库处理启动的足够点。 这将复制
    ;从闪存到 SRAM 的.data 段初始化程序,并且零填充
    ;.bss 段。
    ;
    ;导入__main
    ;B _main
    
    ;*********
    ;
    ;这是当处理器接收到 NMI 时被调用的代码。 这样
    ;只需通过
    调试器进入无限循环,保持系统状态以供检查。
    ;
    
    NmiSR
    B NmiSR
    
    
    ;
    ;这是处理器收到故障
    ;中断时调用的代码。 这只是进入一个无限循环,保留系统状态
    ;供调试器检查。
    ;
    
    FaultISR
    B FaultISR
    
    ;*********
    ;
    ;这是当处理器收到意外
    的;中断时被调用的代码。 这只是进入一个无限循环,保留系统状态
    ;供调试器检查。
    ;
    
    IntDefaultHandler
    B IntDefaultHandler
    
    
    
    ;*********
    ;
    ;通过将引导加载程序从闪存复制到 SRAM,零来初始化处理器
    ;填充.bss 段,并将矢量表移动到
    ;SRAM 的开头。 返回地址被修改为指向引导
    加载程序的 SRAM 副本,而不是闪存副本,从而导致现在在
    ; SRAM 中分支到该副本。
    ;
    
    导出 ProcessorInit
    ProcessorInit
    ;
    ;将代码映像从闪存复制到 SRAM。
    ;
    MOV r0、#0x0000
    MOV R1、#0x0000
    MOVt R1、#0x2000
    import || Image$SRAM$Z$$Base||
    LDR R2、=||Image$SRAM$Z$$$Base||
    COPY_LOOP
    LDR r3、[r0]、#4
    str R3、[R1]、#4
    CMP R1、R2
    BLT copy_loop
    
    ;
    零填充.bss 段。
    ;
    MOV r0、#0x0000
    导入|Image$SRAM$Z$$LIMI$|LDR
    R2、=||Image$SRAM$Z$$LIMIT||
    zero_loop
    str R0、[R1]、#4
    CMP R1、R2
    BLT zero_loop
    
    ;
    ;将矢量表指针设置为 SRAM 的开头。
    ;
    movw r0、#(NVIC_vtable & 0xFFFF)
    movt r0、#(NVIC_vtable >> 16)
    MOVs R1、#0x0000
    movt R1、#0x2000
    str r1,[r0]
    
    ;
    ;返回给呼叫者。
    ;
    BX LR
    
    
    ;
    ;复位处理程序,在处理器启动时调用。
    ;
    
    导出 Reset_Handler
    Reset_Handler
    
    ;
    启用浮点单元。 如果有任何情况,则必须在此处执行此操作
    ;后面的 C 函数使用浮点。 请注意,某些工具链将
    ;即使没有显式浮点
    数据类型正在使用,也会将 FPU 寄存器用于常规工作区。
    ;
    movw r0、#0xED88
    movt r0、#0xE000
    ldr R1、[r0]
    Orr R1、#0x00F00000
    str r1、[r0]
    
    ;
    初始化处理器。
    ;
    BL ProcessorInit
    
    ;
    ;分支到复位处理程序的 SRAM 副本。
    ;
    LDR PC,=Reset_Handler_in_SRAM
    
    
    ;*********
    ;
    ;更新处理程序,在应用程序需要时调用
    ;启动更新。
    ;
    
    UpdateHandler
    ;
    初始化处理器。
    ;
    BL ProcessorInit
    
    ;
    ;分支到更新处理程序的 SRAM 副本。
    ;
    LDR PC,=UpdateHandler_in_SRAM
    
    
    
    
    ;*********
    ;
    ;确保此段的末尾对齐。
    ;
    
    align
    
    
    ;
    ;正常代码部分中用于初始化堆和堆栈的一些代码。
    ;
    
    Area |.text|,code,readonly
    
    Reset_Handler_in_SRAM
    ;
    查看是否应执行更新。
    ;
    导入 CheckForceUpdate
    BL CheckForceUpdate
    CBZ R0、CallApplication
    
    ;
    ;配置微控制器。
    ;
    EnterBootLoader
    导入 ConfigureDevice
    BL ConfigureDevice
    
    ;
    ;分支到更新处理程序。
    ;
    导入更新程序
    b Updater
    
    ;
    这是允许从引导启动应用程序的第二个符号
    ;加载程序链接器可能不喜欢感知的跳转。
    ;
    导出 StartApplication
    StartApplication
    ;
    ;通过其矢量表中的复位处理程序调用应用程序。 加载
    应用程序矢量表的;地址。
    ;
    CallApplication
    ;
    ;必要时将应用程序的矢量表复制到目标地址。
    ;请注意,错误的引导加载程序配置可能导致此情况
    ;代码已损坏! 将 vtable_start_address 设置为0x20000000 (开始
    ;SRAM)是安全的,因为这将使用引导加载程序
    已用于其矢量表的相同内存。 如果
    要使用其它地址,则必须非常小心。
    ;
    如果(_app_start_address!=_vtable_start_address)
    movw r0、#(_vtable_start_address 和0xFFFF)
    如果(_table_start_address > 0xFFFF)
    movt r0、#(_vtable_start_address >> 16)
    endif
    movw R1、#(_app_start_address > 0xFFFF);#(>0xt_start_endif
    
    )
    
    
    
    计算向量表的结束地址,假定它具有
    ;最大可能的向量数。 我们不知道应用程序已
    填充了多少;因此这是最安全的方法,但如果
    应用程序表小于最大值,则可能会复制一些非矢量数据。
    ;
    movw R2,#(70 * 4)
    添加 R2、R2、r0
    VectorCopyLoop
    LDR r3、[R1]、#4
    str R3、[r0]、#4
    CMP R0、R2
    BLT VectorCopyLoop
    endif
    
    ;
    ;将矢量表地址设置为应用程序的开头。
    ;
    movw r0、#(_vtable_start_address & 0xFFFF)
    if (_table_start_address > 0xFFFF)
    movt r0、#(_vtable_start_address >> 16)
    endif
    movw R1、#(NVIC_vtable & 0xFFFF)
    movt R1、#(NVIC_vstr > 16)
    R0,[R1]
    
    ;
    从应用程序的矢量表加载栈指针。
    ;
    如果(_app_start_address!=_vtable_start_address)
    movw r0、#(_app_start_address 和0xFFFF)
    if (_app_start_address > 0xFFFF)
    movt r0、#(_app_start_address >> 16)
    endif
    endif
    ldr SP,[r0]
    
    ;
    从应用程序的矢量表加载初始 PC 并分支到
    ;应用程序的入口点。
    ;
    LDR R0、[r0、#4]
    BX R0
    
    
    ;
    ;更新处理程序,在应用程序需要时调用
    ;启动更新。
    ;
    
    UpdateHandler_in_SRAM
    ;
    ;从向量表加载栈指针。
    ;
    MOV r0、#0x0000
    LDR SP、[r0]
    
    b 更新程序
    
    
    ;;;;NMI
    处理程序。
    ;
    ;;
    ;如果 :def:_enable_MOSCFAIL_handler
    ;NmiSR_in_SRAM
    ;
    ;;;;;恢复堆栈帧。
    ;;;mov
    LR、R12
    ;STM SP、{R4-r11}
    
    ;
    ;;;;;保存链接寄存器。
    ;;;mov
    R9、LR
    
    ;
    ;;;;;调用用户提供的低级硬件初始化函数
    ;;如果提供。
    ;;;;IF
    :def:_BL_HW_INIT_FN_Hook
    ;BL _BL_HW_init_fn_hook
    ;endif
    
    ;
    ;;;;查看是否应执行更新。
    ;;;;BL
    CheckForceUpdate
    ;CBZ R0,EnterApplication
    
    ;
    ;;;;;清除 RESC 中的 MOSCFAIL 位。
    ;;;movw
    r0,#(sysctl_RESC & 0xFFFFFF);movt
    r0,#(sysctl_RESC>16)
    ;LDR r1、[r0]
    ;BIC R1、R1、#sysctl_RESC_MOSCFAIL
    ;str r1、[r0]
    
    ;
    ;;;;;修复堆栈上的 PC,以便绕过引导引脚检查
    ;;;(因为已经执行了此检查)。
    ;;;LDR
    R0,=EnterBootLoader
    ;BIC R0、#0x00000001
    ;str R0、[sp、#0x18]
    
    ;
    ;;;;;从 NMI 处理程序返回。 然后,这将开始执行
    ;;引导加载程序。
    ;;;;BX
    R9
    
    ;;
    ;;;;;恢复连接寄存器。
    ;;;;EnterApplication
    
    ;mov lr、r9
    
    ;
    ;;;;;;必要时将应用程序的矢量表复制到目标地址。
    ;;请注意,错误的引导加载程序配置可能导致
    ;;损坏代码! 将 vtable_start_address 设置为0x20000000 (start
    ;;of SRAM)是安全的,因为这将使用引导加载程序
    ;;已用于其矢量表的相同内存。 如果
    要使用其它地址,则必须非常小心。
    ;;;if
    (_app_start_address!=_vtable_start_address)
    ;movw r0、#(_vtable_start_address & 0xFFFF)
    if (_vtable_start_address > 0xFFFF)
    ;motivt r0、#(_vtable_start_address >> 16)
    ;endif
    
    
    > movw_start_address (<0xFFFF);#_app_start_address (#_endif);
    
    
    
    计算向量表的结束地址,假定它具有
    ;;最大可能的向量数。 我们不知道应用程序有多少
    ;;已填充、因此这是最安全的方法、尽管它可能复制一些非
    ;;如果应用程序表小于最大值、则向量数据。
    ;;;movw
    R2、#(70 * 4)
    添加 R2、R2、r0
    ;VectorCopyLoop2
    ;LDR r3、[R1]、#4
    ;str r3、[r0]、#4
    ;cmp R0、R2
    ;blt VectorCopyLoop2;endif
    
    
    ;;;
    ;设置应用程序的矢量表起始地址。 通常这是
    ;;应用程序起始地址,但在某些情况下,应用程序可能会重新定位
    ;;因此我们不能假定这两个地址是相等的。
    ;;;MOVw
    r0、#(_vtable_start_address & 0xFFFF)
    ;if (_vtable_start_address > 0xFFFF)
    
    
    
    
    str movt r0、#(_vtable_start_address >> 16);endif;movw R1、#(NVIC_vtable & 0xFFFF) movt r1、NVIC > 16 R0、[R1]
    
    ;
    ;;;;;从引导加载程序的堆栈中删除 NMI 堆栈框。
    ;;;ldmia
    sp,{r4-r11}
    
    ;;;;;;;
    获取应用程序的堆栈指针。
    ;;; if
    (_app_start_address!=_vtable_start_address)
    ;movw r0、#(_app_start_address & 0xFFFF)
    ;if (_app_start_address > 0xFFFF)
    ;movt r0、#(_app_start_address >> 16)
    endif
    ;
    ldr SP、[r0、#0x00]
    
    ;
    ;;;;;将 NMI 堆栈帧的返回地址修复为
    ;;应用程序的复位处理程序。
    ;;;LDR
    R10、[r0、#0x04]
    ;BIC R10、#0x00000001
    
    ;
    ;;;;;;将 NMI 堆栈框存储到应用程序的堆栈中。
    ;;;;stmdb
    sp!、{R4-r11}
    
    ;;;;;
    分支到应用程序的 NMI 处理程序。
    ;;;LDR
    R0、[r0、#0x08]
    ;bx R0
    ;endif
    
    
    
    ;*********
    ;
    ; C 库启动代码用于定义栈的预期函数
    ;和堆内存位置。 对于启动代码的 C 库版本
    ;提供此函数,以便 C 库初始化代码可以找到
    ;堆栈和堆的位置。
    ;
    
    如果:DEF:__MICROLIB
    导出_initial sp
    导出_heap_base
    导出_heap_limit
    其他
    导入__use_two_region_memory
    导出__user_initial_stackheap
    __user_initial_stackheap
    LDR R0 = HeapMem
    LDR R1、=(StackMem +堆栈)
    LDR R2、=(HeapMem +堆)
    LDR R3、=StackMem
    BX LR
    endif
    
    ;*********
    ;
    ;确保此段的末尾对齐。
    ;
    
    align
    
    
    ;
    ;告诉汇编器我们已完成。
    ;
    
    结束
    

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    您好、Scott、
    如果没有太多麻烦、您可以与我分享该项目。 我对如何从零创建一个有点迷失。
    提前感谢
    安德烈斯
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    当然。 其中有一些额外的文件、但应将项目设置为正常工作。  

    e2e.ti.com/.../6607.Bootloader.zip