问题原因:
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