【Silicon Labs BG22-EK4108A 蓝牙开发评测】+升级FW后的bootloader

北方   2022-1-14 10:21 楼主

1. 升级FW之后,整个板子就清净了,清净到下载的应用程序都不执行了。blinky这样的应用程序下载之后就不再有console的输出。这个是肿么了?

大致是这个逻辑,通常的应用开发,在reset之后就跳转到代码启动地址。这个也是原生代码开发,如应用keil等开发通常会遇到的。但是在simplityStudio这样的独立生态开发,采用了另外的模式,就是首先搞一个bootloader,再由bootloader启动应用程序。这个就是本帖所遇到的问题。

先说解决方法,再demo中找到bootloader程序,然后选择一个适合自己的下载,这次选择的是bootloader UART,编译下载,

1.PNG

顺利解决问题,前面下载的blink可以正确运行的。

捕获.PNG

2. Bootloader是不是没有用?

基本上可以很确定的给出这个问题的答案,那就是非常有必要。

除非是使用8位机,只有8k的flash,执行简单玩具级别的功能,事实上现在的玩具逻辑也复杂起来了,那么用bootloader没什么效果。

一个良好的bootloader,就像计算机的bios一样,起到自检,硬件定义,简单内存管理等功能,通过开放的接口,在没有复杂代码和外部工具的情况下,执行设备和系统的调试和管理,通常还有自动升级软件的功能。

即使不是大的系统,也要用c语言搞一个自己的bootloader,这样在跨平台,在不同的芯片上开发的时候,都可以先弄bootloader,然后再加载其他应用程序。

如果,是开发一个需要升级的产品,那么,bootloader是绝对的需要。

 

3. Bootloader的实现。

Boot loader需要选择起始引导位置,本代码用的是uart,

捕获.PNG 如simplicity可以看到,用spi,i2c都可以,在有外部flash的情况下,还需要访问spi。推荐采用uart就足够的。

进入bootloader需要使用GPIO的button按键,定义函数如下,

bool gpio_enterBootloader(void)
{
  bool pressed;

#if defined(CMU_HFBUSCLKEN0_GPIO)
  // Enable GPIO clock
  CMU->HFBUSCLKEN0 |= CMU_HFBUSCLKEN0_GPIO;
#endif
#if defined(_CMU_CLKEN0_MASK)
  // Enable GPIO clock
  CMU->CLKEN0_SET = CMU_CLKEN0_GPIO;
#endif

  // Since the button may have decoupling caps, they may not be charged
  // after a power-on and could give a false positive result. To avoid
  // this issue, drive the output as an output for a short time to charge
  // them up as quickly as possible.
  GPIO_PinModeSet(BSP_BTL_BUTTON_PORT,
                  BSP_BTL_BUTTON_PIN,
                  gpioModePushPull,
                  BTL_GPIO_ACTIVATION_POLARITY);
  for (volatile int i = 0; i < 100; i++) {
    // Do nothing
  }

  // Reconfigure as an input with pull(up|down) to read the button state
  GPIO_PinModeSet(BSP_BTL_BUTTON_PORT,
                  BSP_BTL_BUTTON_PIN,
                  gpioModeInputPull,
                  BTL_GPIO_ACTIVATION_POLARITY);

  // We have to delay again here so that if the button is depressed the
  // cap has time to discharge again after being charged up by the above delay
  for (volatile int i = 0; i < 500; i++) {
    // Do nothing
  }

  pressed = GPIO_PinInGet(BSP_BTL_BUTTON_PORT, BSP_BTL_BUTTON_PIN)
            != BTL_GPIO_ACTIVATION_POLARITY;

  // Disable GPIO pin
  GPIO_PinModeSet(BSP_BTL_BUTTON_PORT,
                  BSP_BTL_BUTTON_PIN,
                  gpioModeDisabled,
                  BTL_GPIO_ACTIVATION_POLARITY);

#if defined(CMU_HFBUSCLKEN0_GPIO)
  // Disable GPIO clock
  CMU->HFBUSCLKEN0 &= ~CMU_HFBUSCLKEN0_GPIO;
#endif

  return pressed;
}

4. 应用的起始地址,在不同的产品系列如下,启动后访问0x0h,如果没有bootloader,那么在0x6000h的应用代码就无法访问到。

1.PNG

启动顺序如下,

1.PNG

进入页面,先按下btn0,然后rst就进入bootloader页面,这里啥也没显示,然后按照手册访问uart_dft.exe 就可以完成对应的功能,

1.PNG 整个过程比较完整,而且也在不停升级优化,更详细资料需要认真读代码。整个编译效率很高,小于2k。

 

5  整个代码包括uart初始化,gpio初始化,dft,crc和sha256加密等过程。完整核心代码供参考。

#include "config/btl_config.h"
#include "api/btl_interface.h"

#include "core/btl_core.h"
#include "core/btl_reset.h"
#include "core/btl_parse.h"
#include "core/btl_bootload.h"
#include "core/btl_upgrade.h"

#include "plugin/debug/btl_debug.h"

#ifdef BTL_PLUGIN_GPIO_ACTIVATION
#include "plugin/gpio/gpio-activation/btl_gpio_activation.h"
#endif

#ifdef BTL_PLUGIN_EZSP_GPIO_ACTIVATION
#include "plugin/gpio/ezsp-gpio-activation/btl_ezsp_gpio_activation.h"
#endif

#ifdef BOOTLOADER_SUPPORT_STORAGE
#include "plugin/storage/btl_storage.h"
#include "plugin/storage/bootloadinfo/btl_storage_bootloadinfo.h"
#endif

#ifdef BOOTLOADER_SUPPORT_COMMUNICATION
#include "plugin/communication/btl_communication.h"
#endif

#include "em_device.h"
#include "em_cmu.h"
#include "em_gpio.h"
#include "em_chip.h"

#if defined(__GNUC__)
#define ROM_END_SIZE 0
extern const size_t __rom_end__;
#elif defined(__ICCARM__)
#define ROM_END_SIZE 4
const size_t __rom_end__ @ "ROM_SIZE";
#endif

// --------------------------------
// Local function declarations
__STATIC_INLINE bool enterBootloader(void);
SL_NORETURN static void bootToApp(uint32_t);

#if defined(BOOTLOADER_WRITE_DISABLE)
__STATIC_INLINE void lockBootloaderArea(void)
{
  // Disable write access to bootloader.
  // Prevents application from touching the bootloader.
#if defined(_MSC_PAGELOCK0_MASK)
#if defined(CRYPTOACC_PRESENT)
  CMU->CLKEN1_SET = CMU_CLKEN1_MSC;
#endif
  for (uint32_t i = (BTL_FIRST_STAGE_BASE / FLASH_PAGE_SIZE);
       i < ((BTL_MAIN_STAGE_MAX_SIZE + BTL_FIRST_STAGE_SIZE) / FLASH_PAGE_SIZE);
       i++) {
    MSC->PAGELOCK0_SET = (0x1 << i);
  }
#if defined(CRYPTOACC_PRESENT)
  CMU->CLKEN1_CLR = CMU_CLKEN1_MSC;
#endif
#elif defined(MSC_BOOTLOADERCTRL_BLWDIS)
  MSC->BOOTLOADERCTRL |= MSC_BOOTLOADERCTRL_BLWDIS;
#else
  // Do nothing
#endif
}
#endif

void HardFault_Handler(void)
{
  BTL_DEBUG_PRINTLN("Fault          ");
  reset_resetWithReason(BOOTLOADER_RESET_REASON_FATAL);
}

// Main Bootloader implementation

int main(void)
{
  int32_t ret = BOOTLOADER_ERROR_STORAGE_BOOTLOAD;

  CHIP_Init();

  // Enabling HFXO will add a hefty code size penalty (~1k)
  // CMU_HFXOInit_TypeDef hfxoInit = CMU_HFXOINIT_DEFAULT;
  // CMU_HFXOInit(&hfxoInit);
  // CMU_ClockSelectSet(cmuClock_HF, cmuSelect_HFXO);
  // CMU_OscillatorEnable(cmuOsc_HFRCO, false, false);

  BTL_DEBUG_PRINTLN("BTL entry");

#if defined(EMU_CMD_EM01VSCALE2) && defined(EMU_STATUS_VSCALEBUSY)
  // Device supports voltage scaling, and the bootloader may have been entered
  // with a downscaled voltage. Scale voltage up to allow flash programming.
  EMU->CMD = EMU_CMD_EM01VSCALE2;
  while (EMU->STATUS & EMU_STATUS_VSCALEBUSY) {
    // Do nothing
  }
#endif

  btl_init();

#ifdef BOOTLOADER_SUPPORT_STORAGE
  if (!reset_resetCounterEnabled()) {
    // Storage bootloaders might use part of the reason signature as a counter,
    // so only invalidate the signature when the counter is not in use.
    reset_invalidateResetReason();
  }
#else
  reset_invalidateResetReason();
#endif

#ifdef BOOTLOADER_SUPPORT_STORAGE
  // If the bootloader supports storage, first attempt to apply an existing
  // image from storage.
  ret = storage_main();

  if (ret == BOOTLOADER_OK) {
    // Firmware upgrade from storage successful. Disable the reset counter
    // and return to application
    if (reset_resetCounterEnabled()) {
      reset_disableResetCounter();
    }
    reset_resetWithReason(BOOTLOADER_RESET_REASON_GO);
  } else {
    if (!reset_resetCounterEnabled()) {
      // Start counting the number of consecutive resets after the first reset
      reset_enableResetCounter();
    }

    // Stop after three consecutive resets (the first one counts as 0)
    if (reset_getResetCounter() >= 2) {
      // If the system is not able to recover from a fault like BADAPP or
      // BADIMAGE, wait in a busy loop to ease reflashing and debugging.
      BTL_DEBUG_PRINTLN("Reset loop detected. Stopping...");
      reset_disableResetCounter();
      while (1) {
        // Wait...
      }
    } else {
      reset_incrementResetCounter();
    }

    // Wait a short while (approx. 500 ms) before continuing.
    // This allows other operations to complete before the reset.
    for (volatile int i = 800000; i > 0; i--) {
      // Do nothing
    }
  }
#endif

#ifdef BOOTLOADER_SUPPORT_COMMUNICATION
  communication_init();

  ret = communication_start();
  if (ret != BOOTLOADER_OK) {
    reset_resetWithReason(BOOTLOADER_RESET_REASON_FATAL);
  }

  ret = communication_main();
  BTL_DEBUG_PRINT("Protocol returned ");
  BTL_DEBUG_PRINT_WORD_HEX(ret);
  BTL_DEBUG_PRINT_LF();

  communication_shutdown();

  if ((ret == BOOTLOADER_OK)
      || (ret == BOOTLOADER_ERROR_COMMUNICATION_DONE)) {
    reset_resetWithReason(BOOTLOADER_RESET_REASON_GO);
  }
#endif // BOOTLOADER_SUPPORT_COMMUNICATION

  // An error occurred in storage or communication, and a firmware upgrade
  // was not performed
  if (0
#ifdef BOOTLOADER_SUPPORT_COMMUNICATION
      || (ret == BOOTLOADER_ERROR_COMMUNICATION_IMAGE_ERROR)
      || (ret == BOOTLOADER_ERROR_COMMUNICATION_TIMEOUT)
#endif
#ifdef BOOTLOADER_SUPPORT_STORAGE
      || (ret == BOOTLOADER_ERROR_STORAGE_BOOTLOAD)
#endif
      ) {
    reset_resetWithReason(BOOTLOADER_RESET_REASON_BADIMAGE);
  } else {
    reset_resetWithReason(BOOTLOADER_RESET_REASON_FATAL);
  }

  return 0;
}

#ifdef BOOTLOADER_SUPPORT_STORAGE
extern const BootloaderStorageFunctions_t storageFunctions;
#endif

const MainBootloaderTable_t mainStageTable = {
  {
    .type = BOOTLOADER_MAGIC_MAIN,
    .layout = BOOTLOADER_HEADER_VERSION_MAIN,
    .version = BOOTLOADER_VERSION_MAIN
  },
  // Bootloader size is the relative address of the end variable plus 4 for the
  // CRC
  .size = ((uint32_t)&__rom_end__) - BTL_MAIN_STAGE_BASE + ROM_END_SIZE + 4,
  .startOfAppSpace = (BareBootTable_t *)(BTL_APPLICATION_BASE),
  .endOfAppSpace = (void *)(BTL_APPLICATION_BASE + BTL_APP_SPACE_SIZE),
  .capabilities = (0
#ifdef BOOTLOADER_ENFORCE_SIGNED_UPGRADE
                   | BOOTLOADER_CAPABILITY_ENFORCE_UPGRADE_SIGNATURE
#endif
#ifdef BOOTLOADER_ENFORCE_ENCRYPTED_UPGRADE
                   | BOOTLOADER_CAPABILITY_ENFORCE_UPGRADE_ENCRYPTION
#endif
#ifdef BOOTLOADER_ENFORCE_SECURE_BOOT
                   | BOOTLOADER_CAPABILITY_ENFORCE_SECURE_BOOT
#endif
#ifdef BOOTLOADER_SUPPORT_CERTIFICATES
                   | BOOTLOADER_CAPABILITY_ENFORCE_CERTIFICATE_SECURE_BOOT
#endif
#ifdef BOOTLOADER_ROLLBACK_PROTECTION
                   | BOOTLOADER_CAPABILITY_ROLLBACK_PROTECTION
#endif
                   | BOOTLOADER_CAPABILITY_BOOTLOADER_UPGRADE
                   | BOOTLOADER_CAPABILITY_EBL
                   | BOOTLOADER_CAPABILITY_EBL_SIGNATURE
#if !defined(BTL_PARSER_NO_SUPPORT_ENCRYPTION)
                   | BOOTLOADER_CAPABILITY_EBL_ENCRYPTION
#endif
#ifdef BOOTLOADER_SUPPORT_STORAGE
                   | BOOTLOADER_CAPABILITY_STORAGE
#endif
#ifdef BOOTLOADER_SUPPORT_COMMUNICATION
                   | BOOTLOADER_CAPABILITY_COMMUNICATION
#endif
                   ),
  .init = &btl_init,
  .deinit = &btl_deinit,
  .verifyApplication = &bootload_verifyApplication,
  .initParser = &core_initParser,
  .parseBuffer = &core_parseBuffer,
#ifdef BOOTLOADER_SUPPORT_STORAGE
  .storage = &storageFunctions,
#else
  .storage = NULL,
#endif
  .parseImageInfo = core_parseImageInfo,
  .parserContextSize = core_parserContextSize,
#ifdef BOOTLOADER_ROLLBACK_PROTECTION
  .remainingApplicationUpgrades = &bootload_remainingApplicationUpgrades
#else
  .remainingApplicationUpgrades = NULL
#endif
};

#if defined(BOOTLOADER_SUPPORT_CERTIFICATES)
const ApplicationCertificate_t sl_app_certificate = {
  .structVersion = APPLICATION_CERTIFICATE_VERSION,
  .flags = { 0U },
  .key = { 0U },
  .version = 0,
  .signature = { 0U },
};
#endif

const ApplicationProperties_t sl_app_properties = {
  .magic = APPLICATION_PROPERTIES_MAGIC,
  .structVersion = APPLICATION_PROPERTIES_VERSION,
  .signatureType = APPLICATION_SIGNATURE_NONE,
  .signatureLocation = ((uint32_t)&__rom_end__) - BTL_MAIN_STAGE_BASE + ROM_END_SIZE,
  .app = {
    .type = APPLICATION_TYPE_BOOTLOADER,
    .version = BOOTLOADER_VERSION_MAIN,
    .capabilities = 0UL,
    .productId = { 0U },
  },
#if defined(BOOTLOADER_SUPPORT_CERTIFICATES)
  // If certificate based boot chain is enabled, the bootloader binary will be provided with
  // a certificate that does not contain any key.
  // A valid certificate needs to be injected to the bootloader images using Simplicity Commander.
  // Simplicity Commander will replace this certificate.
  .cert = (ApplicationCertificate_t *)&sl_app_certificate,
#else
  .cert = NULL,
#endif
  .longTokenSectionAddress = NULL,
};

/**
 * This function gets executed before ANYTHING got initialized.
 * So, no using global variables here!
 */
void SystemInit2(void)
{
  // Initialize debug before first debug print
  BTL_DEBUG_INIT();

  // Assumption: We should enter the app
  volatile bool enterApp = true;
  // Assumption: The app should be verified
  volatile bool verifyApp = true;

  // Check if we came from EM4. If any other bit than the EM4 bit it set, we
  // can't know whether this was really an EM4 reset, and we need to do further
  // checking.
#if defined(RMU_RSTCAUSE_EM4RST) && defined(APPLICATION_VERIFICATION_SKIP_EM4_RST)
  if (RMU->RSTCAUSE == RMU_RSTCAUSE_EM4RST) {
    // We came from EM4, app doesn't need to be verified
    verifyApp = false;
  } else if (enterBootloader()) {
    // We want to enter the bootloader, app doesn't need to be verified
    enterApp = false;
    verifyApp = false;
  }
#else
  if (enterBootloader()) {
    // We want to enter the bootloader, app doesn't need to be verified
    enterApp = false;
    verifyApp = false;
  }
#endif
  uint32_t startOfAppSpace = (uint32_t)mainStageTable.startOfAppSpace;

  // Sanity check application program counter
  uint32_t pc = *(uint32_t *)(startOfAppSpace + 4);
  if (pc == 0xFFFFFFFF) {
    // Sanity check failed; enter the bootloader
    reset_setResetReason(BOOTLOADER_RESET_REASON_BADAPP);
    enterApp = false;
    verifyApp = false;
  }

  // App should be verified
  if (verifyApp) {
    // If app verification fails, enter bootloader instead
    enterApp = bootload_verifyApplication(startOfAppSpace);
    if (!enterApp) {
      reset_setResetReason(BOOTLOADER_RESET_REASON_BADAPP);
    }
  }

#if defined(BOOTLOADER_ROLLBACK_PROTECTION)
  // Clean the stored application versions if requested with a magic.
  // The magic is only written when a bootloader upgrade is triggered.
  bootload_removeStoredApplicationVersions();

  if (enterApp) {
    enterApp = bootload_storeApplicationVersion(startOfAppSpace);
  }
#endif

  if (enterApp) {
    BTL_DEBUG_PRINTLN("Enter app");
    BTL_DEBUG_PRINT_LF();

#if defined(BOOTLOADER_SUPPORT_STORAGE)
    // Disable the reset counter if we're booting (back) into the application
    if (reset_resetCounterEnabled()) {
      reset_disableResetCounter();
    }
#endif

#if defined(BOOTLOADER_WRITE_DISABLE)
    lockBootloaderArea();
#endif

#if defined(BOOTLOADER_ENFORCE_SECURE_BOOT) && defined(APPLICATION_WRITE_DISABLE)
    // The neccessary check of valid signature pointer for application at startOfAppSpace
    // is already done in bootload_verifyApplication.
    bootload_lockApplicationArea(startOfAppSpace, 0);
#endif

    // Set vector table to application's table
    SCB->VTOR = startOfAppSpace;

    bootToApp(startOfAppSpace);
  }
  // Enter bootloader
}

/**
 * Jump to app
 */
SL_NORETURN static void bootToApp(uint32_t startOfAppSpace)
{
  (void)startOfAppSpace;

  // Load SP and PC of application
  __ASM("mov r0, %0       \n" // Load address of SP into R0
        "ldr r1, [r0]     \n" // Load SP into R1
        "msr msp, r1      \n" // Set MSP
        "msr psp, r1      \n" // Set PSP
        "ldr r0, [r0, #4] \n" // Load PC into R0
        "mov pc, r0       \n" // Set PC
        :: "r" (startOfAppSpace) : "r0", "r1");

  while (1) {
    // Do nothing
  }
}

/**
 * Check whether we should enter the bootloader
 *
 * @return True if the bootloader should be entered
 */
__STATIC_INLINE bool enterBootloader(void)
{
// *INDENT-OFF*
#if defined(EMU_RSTCAUSE_SYSREQ)
  if (EMU->RSTCAUSE & EMU_RSTCAUSE_SYSREQ) {
#else
  if (RMU->RSTCAUSE & RMU_RSTCAUSE_SYSREQRST) {
#endif
    // Check if we were asked to run the bootloader...
    switch (reset_classifyReset()) {
      case BOOTLOADER_RESET_REASON_BOOTLOAD:
      case BOOTLOADER_RESET_REASON_FORCE:
      case BOOTLOADER_RESET_REASON_UPGRADE:
      case BOOTLOADER_RESET_REASON_BADAPP:
        // Asked to go into bootload mode
        return true;
      default:
        break;
    }
  }
// *INDENT-ON*

#ifdef BTL_PLUGIN_GPIO_ACTIVATION
  if (gpio_enterBootloader()) {
    // GPIO pin state signals bootloader entry
    return true;
  }
#endif

#ifdef BTL_PLUGIN_EZSP_GPIO_ACTIVATION
  if (ezsp_gpio_enterBootloader()) {
    // GPIO pin state signals bootloader entry
    return true;
  }
#endif

  return false;
}

bool bootloader_enforceSecureBoot(void)
{
#ifdef BOOTLOADER_ENFORCE_SECURE_BOOT
  return true;
#else
  return false;
#endif
}

 

4. bootloader的主要作用,就是加载应用程序。本例中使用的gecko bootloader1.2版本相当的完整。

 

 

本帖最后由 北方 于 2022-1-14 10:55 编辑

回复评论 (1)

bootload做的很不错,可以学习学习

点赞  2022-1-15 09:13
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复