NORDIC 燒錄BLE協議棧后不能用JLINK仿真bootloader問題及修改方案


問題原因:

bootloader的程序區域是0X78000~0X7E000

但是在bootloader程序中定義了0X0FF8與0XFFC位置處的數據,此數據與BLE協議棧沖突,BLE協議棧的flash范圍是0~0X25FFF,所以燒錄協議棧后不能用JLINK仿真

具體在bootloader中的代碼為:

/** @brief Location (in the flash memory) of the bootloader address. */
#define MBR_BOOTLOADER_ADDR      (0xFF8)
/** @brief Location (in UICR) of the bootloader address. */
#define MBR_UICR_BOOTLOADER_ADDR (&(NRF_UICR->NRFFW[0]))
/** @brief Location (in the flash memory) of the address of the MBR parameter page. */
#define MBR_PARAM_PAGE_ADDR      (0xFFC)
 
    uint32_t const m_uicr_mbr_params_page_address
        __attribute__((at(NRF_UICR_MBR_PARAMS_PAGE_ADDRESS))) = NRF_MBR_PARAMS_PAGE_ADDRESS;
 
    uint32_t  m_uicr_bootloader_start_address __attribute__((at(NRF_UICR_BOOTLOADER_START_ADDRESS)))
                                                    = BOOTLOADER_START_ADDR;
 
代碼中使用__attribute__強制定義了數據在flash中的存儲位置,所以燒錄不成功
bootloader的hex文件部分內容:

 

 修改bootloader:

1、為了能用JLINK燒錄APP后能從升級程序正常跳轉到APP程序,修改bootloader,屏蔽APP_CRC校驗功能改為檢查APP首地址是否為0X20000000

//    else if (!boot_validate(&s_dfu_settings.boot_validation_app, nrf_dfu_bank0_start_addr(), s_dfu_settings.bank_0.image_size, do_crc))//DEBUG_D
//    {
//        NRF_LOG_WARNING("Boot validation failed. App is invalid.");
//        return false;
//    }
 else if(((*(volatile uint32_t*)0x26000)&0x2FFE0000) != 0x20000000) //DEBUG_D
 {
  NRF_LOG_WARNING("Boot validation failed. App is invalid.");
  return false;
 }

修改bank0區的驗證條件if (s_dfu_settings.bank_0.bank_code != NRF_DFU_BANK_VALID_APP)為:

if (s_dfu_settings.bank_0.bank_code != NRF_DFU_BANK_VALID_APP && s_dfu_settings.bank_0.image_size != 0 && s_dfu_settings.bank_0.image_crc != 0)

2、為了能夠使用JLINK調試bootloader'程序,需要做如下修該:

修改bootloader中
// uint32_t const m_uicr_mbr_params_page_address
// __attribute__((at(NRF_UICR_MBR_PARAMS_PAGE_ADDRESS))) = NRF_MBR_PARAMS_PAGE_ADDRESS;為
uint32_t m_uicr_mbr_params_page_address = NRF_MBR_PARAMS_PAGE_ADDRESS;

// uint32_t m_uicr_bootloader_start_address __attribute__((at(NRF_UICR_BOOTLOADER_START_ADDRESS))) = BOOTLOADER_START_ADDR;為
uint32_t m_uicr_bootloader_start_address = BOOTLOADER_START_ADDR;
取消使用__attribute__對flash存儲位置的強制定義,因為數據被定義在flash位置0XFF8與0XFFC區,與SD協議棧重合,導致bootloader代碼燒錄不進去,不能在線調試

3、為了能夠使用雙備份升級以及升級過程中藍牙突然斷開導致的升級中斷,之前的APP能夠繼續使用,作如下修改:

    if (NRF_DFU_SETTINGS_COMPATIBILITY_MODE && !NRF_DFU_IN_APP && (s_dfu_settings.settings_version == 1))
    {
        NRF_LOG_INFO("Old settings page detected. Upgrading info.");
        // Old version. Translate.
        memcpy(&s_dfu_settings.peer_data, (uint8_t *)&s_dfu_settings + DFU_SETTINGS_BOND_DATA_OFFSET_V1, NRF_DFU_PEER_DATA_LEN);
        memcpy(&s_dfu_settings.adv_name,  (uint8_t *)&s_dfu_settings + DFU_SETTINGS_ADV_NAME_OFFSET_V1,  NRF_DFU_ADV_NAME_LEN);
        // Initialize with defaults.
        s_dfu_settings.boot_validation_softdevice.type = NO_VALIDATION;
        s_dfu_settings.boot_validation_app.type        = VALIDATE_CRC;
        s_dfu_settings.boot_validation_bootloader.type = NO_VALIDATION;
        memcpy(s_dfu_settings.boot_validation_app.bytes, &s_dfu_settings.bank_0.image_crc, sizeof(uint32_t));
        s_dfu_settings.settings_version = NRF_DFU_SETTINGS_VERSION;
    }
 
//升級程序中需要加入的代碼
 #define BANK1_BAKEUP_START_ADDR  0X53000 //備份程序起始地址
 #define BNAK0_APP_START_ADDR  0X26000 //應用程序起始地址
 s_dfu_settings.bank_0.image_crc = 0;
 s_dfu_settings.bank_0.image_size = BANK1_BAKEUP_START_ADDR - BNAK0_APP_START_ADDR;
 s_dfu_settings.write_offset = 0;
 
    err_code = nrf_dfu_settings_write_and_backup(NULL);
    if (err_code != NRF_SUCCESS)
    {
        NRF_LOG_ERROR("nrf_dfu_settings_write_and_backup() failed with error: %x", err_code);
        return NRF_ERROR_INTERNAL;
    }
4、在boot跳轉到APP的函數中屏蔽掉對APP的代碼保護,因為APP到備份區之間有一片flash用存儲掉電保持的數據,如果不屏蔽代碼保護會導致寫flash出錯,血的教訓啊,具體修改如下:
void nrf_bootloader_app_start_final(uint32_t vector_table_addr)
{
    ret_code_t ret_val;
    // Protect MBR & bootloader code and params pages.
    if (NRF_BOOTLOADER_READ_PROTECT)
    {
        ret_val = nrf_bootloader_flash_protect(0, MBR_SIZE, NRF_BOOTLOADER_READ_PROTECT);
    }
    // Size of the flash area to protect.
    uint32_t area_size;
    area_size = BOOTLOADER_SIZE + NRF_MBR_PARAMS_PAGE_SIZE;
    ret_val = nrf_bootloader_flash_protect(BOOTLOADER_START_ADDR,
                                           area_size,
                                           NRF_BOOTLOADER_READ_PROTECT);
    if (!NRF_BOOTLOADER_READ_PROTECT && (ret_val != NRF_SUCCESS))
    {
        NRF_LOG_ERROR("Could not protect bootloader and settings pages, 0x%x.", ret_val);
    }
//屏蔽對APP程序區的代碼保護
//    ret_val = nrf_bootloader_flash_protect(0,
//                                           nrf_dfu_bank0_start_addr() + s_dfu_settings.bank_0.image_size,
//                                           false);
    if (!NRF_BOOTLOADER_READ_PROTECT && (ret_val != NRF_SUCCESS))
    {
        NRF_LOG_ERROR("Could not protect SoftDevice and application, 0x%x.", ret_val);
    }
    // Run application
    app_start(vector_table_addr);
}

APP跳轉到bootloader方法:

進入bootloader條件:

1、APP校驗不通過(if (!app_is_valid(crc_on_valid_app_required())))

2、升級按鍵按下(if (NRF_BL_DFU_ENTER_METHOD_BUTTON && (nrf_gpio_pin_read(NRF_BL_DFU_ENTER_METHOD_BUTTON_PIN) == 0)))

3、復位引腳按下(if (NRF_BL_DFU_ENTER_METHOD_PINRESET && (NRF_POWER->RESETREAS & POWER_RESETREAS_RESETPIN_Msk)))

4、NRF_POWER_GPREGRET寄存器置1(if (NRF_BL_DFU_ENTER_METHOD_GPREGRET && (nrf_power_gpregret_get() & BOOTLOADER_DFU_START)))

NRF_POWER_GPREGRET寄存器是一個保持寄存器,在軟復位的情況下自動保持,外部復位時清除。

5、使能無按鈕模式且enter_buttonless_dfu置1(if (NRF_BL_DFU_ENTER_METHOD_BUTTONLESS && (s_dfu_settings.enter_buttonless_dfu == 1)))

應用程序使用第四種方法進入BOOT,當需要從應用程序跳轉到bootloader時,可以參考Buttonless中處理的方式來做:

uint32_t ble_dfu_buttonless_bootloader_start_finalize(void)
{
    uint32_t err_code;
    NRF_LOG_DEBUG("In ble_dfu_buttonless_bootloader_start_finalize\r\n");
    err_code = sd_power_gpregret_clr(0, 0xffffffff);
    VERIFY_SUCCESS(err_code);
    err_code = sd_power_gpregret_set(0, BOOTLOADER_DFU_START);
    VERIFY_SUCCESS(err_code);
    // Indicate that the Secure DFU bootloader will be entered
    m_dfu.evt_handler(BLE_DFU_EVT_BOOTLOADER_ENTER);
    // Signal that DFU mode is to be enter to the power management module
    nrf_pwr_mgmt_shutdown(NRF_PWR_MGMT_SHUTDOWN_GOTO_DFU);
    return NRF_SUCCESS;
}
簡化后的代碼為:
void EnterDFU(void)
{
  #define BOOTLOADER_DFU_GPREGRET_MASK            (0xB0)         
  #define BOOTLOADER_DFU_START_BIT_MASK           (0x01)     
  #define BOOTLOADER_DFU_START        (BOOTLOADER_DFU_GPREGRET_MASK | BOOTLOADER_DFU_START_BIT_MASK)
  sd_power_gpregret_clr(0,0xffffffff);
  sd_power_gpregret_set(BOOTLOADER_DFU_START);
  nrf_pwr_mgmt_shutdown(NRF_PWR_MGMT_SHUTDOWN_GOTO_DFU);
  NVIC_SystemReset();
}
特別注意:, 根據官方的答復,寫GPREGRET寄存器之前,需要先Clear
 
參考文章:https://blog.csdn.net/musicalspace/article/details/87863744


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM