在多次尝试修改 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 组。 //! @} // //*********