在上面的 LFU 演示中,使用了两个应用程序映像。一个在闪存组 0 上运行,另一个在闪存组 1 上运行。BANK0_FLASH 构建配置被视为“旧”或“参考”固件,而 BANK1_FLASH 构建配置被视为“新”固件。这两个应用程序在其他方面都是相同的。它们之间没有源代码差异;但是,新固件有 25 个已定义和已初始化的新浮点变量。这两个应用程序通过 TIDM-DC-DC-BUCK 解决方案工程的两个构建配置来实现 – 即 BANK0_FLASH 和 BANK1_FLASH。顾名思义,BANK0_FLASH 从闪存组 0 执行,BANK1_FLASH 从闪存组 1 执行。这两个构建配置共享相同的源文件,但包含不同的链接器命令文件。此外,在代码中的不同位置,宏“#ifdef BANK0”和“ifdef BANK1”控制执行,它们运行相同的控制循环 ISR 和后台任务。
以下是控制循环在 CPU 上运行时的简要 LFU 流程。
- 器件复位时,执行从默认引导至闪存入口点 0x80000 处开始,这是组选择逻辑函数所在的位置。此函数检查任一闪存组或这两个闪存组中是否存在有效的应用程序,如果存在一个,则选择较新的版本并转移到该地址(0x8EFF0 或 0x9EFF0)。这些是各个应用程序的 code_start 位置,执行从这里进入 C 运行时初始化例程 (_c_int00),并进入相应应用程序的 main()。如果这两个闪存组都不包含有效的应用程序,则执行将等待主机完成自动波特率锁定,并等待主机通过 SCI 发送映像。
- 用户通过 Windows 命令提示符调用 LFU 命令“8 Live DFU”。
- 目标器件在 SCI 接收中断 ISR 中接收命令 ID“0x700”。
- 在 main() 中,在后台循环中调用函数 BUCK_LFU_runLFU()。当命令 ID 与“0x700”匹配时,SCI 中断被禁用,执行分支到自定义引导加载程序中 Live DFU (liveDFU()) 函数的地址。如果组 0 中的应用程序正在执行,则分支到组 0 中的自定义引导加载程序(地址 0x81000)。如果组 1 中的应用程序正在执行,则分支到组 1 中的自定义引导加载程序(地址 0x91000)。
自定义引导加载程序中的 liveDFU() 从主机接收应用程序映像并将其编程到闪存中。完成后,执行取决于是否定义了宏 LFU_WITH_RESET。如果是,则看门狗配置为生成复位信号,然后启用,因此发生器件复位。如果未定义宏,则执行分支到新应用程序映像的 LFU 入口点。这是组 0 的 0x8EFF8 和组 1 的 0x9EFF8。这与常规的闪存启动入口点不同。
- 函数 c_int_lfu() 位于组 0 上的 0x8eff8 和组 1 上的 0x9eff8。该函数可在不复位器件的情况下执行 LFU 切换。在该函数中:
- 调用编译器的 LFU 初始化例程 (__TI_auto_init_warm())。这将初始化已指示为需要初始化的任何变量。因此,它将初始化在 BANK1_Flash 构建配置中定义的 25 个新浮点变量。
- 设置一个标志以指示 LFU 正在进行中。在 F28004x 上,这是使用软件变量 lfuSwitch 实现的。在 F28003x 上,这是通过使用 LFU_setLFUCPU() 设置 LFUConfig SysCtl 寄存器的 LFU.CPU 位实现的。
- 调用 main()
在 main() 中,初始化进程取决于 LFU 是否正在进行中。这是通过访问 F28003x 上的 LFUConfig SysCtl 寄存器的 LFU.CPU 位或 F28004x 上的 lfuSwitch 实现的。如果该值为 0,则初始化会像发生器件复位一样进行。
- 如果该值不为 0,则执行有限初始化。首先,执行 init_lfu()。该函数将代码从闪存复制到 RAM,对应于用户已指示需要从 RAM 运行的程序代码。接下来,在 F28003x 上,它使用 Shadow_Interrupt_Register() 更新停用的 PIE 中断向量表。在 F28004x 上,中断向量表交换硬件特性不可用,因此不执行该特性。在 F28003x 上,还会更新一组停用的函数指针。在 F28004x 上,RAM 块交换硬件特性不可用,因此不执行该特性。
- 接下来,将变量 lfuSwitch_start 设置为 Lfu_switch_wait_for_isr。执行在此等待,直到下一个控制循环 ISR 开始执行,其中 lfuSwitch_start 从 Lfu_switch_wait_for_isr 移到 Lfu_switch_ready_to_switch。这有助于将 LFU 切换同步到控制循环 ISR 的末端,从而最大限度地利用控制循环中断之间的空闲时间。
- 当执行继续时,将发生 LFU 切换步骤。首先,禁用全局中断。在 F28003x 上,执行 PIE 中断向量表交换和 RAM 块交换。在 F28004x 上,每个使用的 PIE 中断向量都需要在此处单独更新。同样,每个函数指针都需要在此处单独更新。然后执行 C28x CPU 端堆栈指针初始化,并重新启用全局中断。这表示 LFU 切换结束。
- 在短时间内禁用全局中断。如果在此期间发生外设中断,它将继续保持锁定状态,并在重新启用全局中断时中断 CPU。这样做是为了避免在以下情况(不太可能发生)下出现不可预测的行为:发生中断,以及在更新向量表之时访问此向量表。
- 图 3-9 显示了另一个 LFU 用例,其中将在固件升级期间更新控制循环参数。在实践中,这可以使用补偿设计器实时完成,这里提供了这方面的用例进行说明。这对应于 buck_F28004x_lfu_controlloop 工程。在此工程中,BANK0_FLASH 构建配置包含对应于较小增益 (Kdc = 4000) 的系数。BANK1_FLASH 构建配置包含对应于较大增益 Kdc=38904 的系数(请参阅 buck.h 中的函数 BUCK_initControlLoopGlobals())。当启用有源负载时,这会导致 BANK0_FLASH 编译配置的瞬态性能较差,而 BANK1_FLASH 编译配置的瞬态性能更好。如果器件已经包含与 buck_f28004x_lfu 工程(或甚至 CLA)对应的应用程序文件,则可以使用与上一节相同的 LFU 命令运行此更新,但与此工程对应的已更新的 .txt 名称除外,如表 3-1 所示。在 buck_f28004x_lfu_controlloop 工程中,在器件复位时(而不是在 LFU 切换之后)在 main() 中启用有源负载,因此在运行 LFU 后复位器件非常重要,可为执行此初始化提供便利。器件复位只需执行一次。
然后,用户可以使用 controlloop 工程可执行文件,在不复位器件的情况下执行其他 LFU 更新。
注: 目前,仅在 F28004x 上创建启用了有源负载的 Controlloop 示例。