问题原因:

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

NORDIC 烧录BLE协议栈后不能用JLINK仿真bootloader问题及修改方案的更多相关文章

  1. 深入浅出低功耗蓝牙(BLE)协议栈

    深入浅出低功耗蓝牙(BLE)协议栈 BLE协议栈为什么要分层?怎么理解蓝牙"连接"?如果蓝牙协议只有ATT没有GATT会发生什么? 协议栈框架 一般而言,我们把某个协议的实现代码称 ...

  2. TI BLE协议栈软件框架分析

    看源代码的时候,一般都是从整个代码的入口处开始,TI  BLE 协议栈源码也不例外.它的入口main()函数就是整个程序的入口,由系统上电时自动调用. 它主要做了以下几件事情: (一)底层硬件初始化配 ...

  3. BLE协议栈及传统蓝牙协议栈对比图

    1. BLE协议栈的层次图如下: 主机控制接口层: 为主机和控制器之间提供标准通信接口 逻辑链路控制及自适应协议层: 为上层提供数据封装服务 安全管理层: 定义配对和密钥分配方式,为协议栈其他层与另一 ...

  4. 深入浅出讲解低功耗蓝牙(BLE)协议栈

    详解BLE连接建立过程https://www.cnblogs.com/iini/p/8972635.html 详解BLE 空中包格式—兼BLE Link layer协议解析https://www.cn ...

  5. 蓝牙BLE: 蓝牙(BLE)协议栈

    蓝牙协议是通信协议的一种,一般而言,我们把某个协议的实现代码称为协议栈(protocol stack),BLE协议栈就是实现低功耗蓝牙协议的代码,理解和掌握BLE协议是实现BLE协议栈的前提.当前的蓝 ...

  6. CentOS 7合盖后黑屏但不进入睡眠模式修改

    CentOS 7合盖后黑屏但不进入睡眠模式修改 systemd 能够处理某些电源相关的 ACPI事件,你可以通过从 /etc/systemd/logind.conf 以下选项进行配置: HandleP ...

  7. 【转】TI蓝牙BLE 协议栈代码学习

    BLE就是低功率蓝牙.要着重了解两种设备: dual-mode双模设备:简单说就是向下兼容. single-mode单模设备:仅仅支持BLE.   关于开发主要讲的是单模设备,它可以只靠纽扣电池即可持 ...

  8. TI CC2541 BLE协议栈蓝牙MAC 地址

    在Flash中有一块只读区域,从地址0x780E开始,蓝牙的MAC以小端方式存放在里面. 在TI的Peripheral例程里面,添加一个特征值,只读属性,6字节长度(蓝牙MAC长度为48-bit,6字 ...

  9. 解决 Orange Pi 烧录完系统后剩余可用空间过少的问题

    输入命令 df -ha 这图是拿别人的 看到系统才使用3.2g,内存卡有16g,不可能满的. 执行命令,加上sudo,防止权限不够: sudo fs_resize 如果上面那个不行的话,试试这个命令( ...

随机推荐

  1. iOS摄像头和相册(转)

    iOS摄像头和相册iOS 获取图片有三种方法1. 直接调用摄像头拍照 2. 从相册中选择 3. 从图库中选择 UIImagePickerController 是系统提供的用来获取图片和视频的接口: 用 ...

  2. sonar:windows重启sonar

    登录后操作

  3. json 格式化输出

    C#格式化JSON字符串 很多时候我们需要将json字符串以 {     "status": 1,     "sum": 9 }这种方式显示,而从服务端取回来的 ...

  4. 并查集与最小生成树Kruskal算法

    一.什么是并查集 在计算机科学中,并查集是一种树型的数据结构,用于处理一些不交集的合并及查询问题.有一个联合-查找算法(union-find algorithm)定义了两个用于次数据结构的操作: Fi ...

  5. NVIDIA显卡电源不足

    NVIDIA显卡 Ubuntu16.04安装驱动后出现问题:Unable to determine the device handle for GPUXXX 安装NVIDIA驱动后输入:nvidia- ...

  6. 【数据库开发】Redis数据库服务器启动配置

    Redis简介 redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset(sor ...

  7. 【miscellaneous】关于gst ffmpeg插件的安装心得

    1 一直通过软件源不能将ffmpeg插件进行安装 2 下载源码编译一直失败 说是需要依赖bad插件 3 bad插件安装不上在ubuntu下 解决办法: 应该直接查找ffmpeg插件的安装办法,不是只有 ...

  8. 最新 拉卡拉java校招面经 (含整理过的面试题大全)

    从6月到10月,经过4个月努力和坚持,自己有幸拿到了网易雷火.京东.去哪儿.拉卡拉等10家互联网公司的校招Offer,因为某些自身原因最终选择了拉卡拉.6.7月主要是做系统复习.项目复盘.LeetCo ...

  9. js删除array数组中元素

    for(var i = 0; i < array.length; i++) { if(-1 != array[i]) { array.splice(i, 1); i--; } } 上面if就是判 ...

  10. nginx 进程管理-信号

    进程结构:一个master进程和多个子进程. 子进程分两类:一种是 Worker 进程,另一种是 Cache 相关的进程. master进程:管理 Worker 进程,发送信号. 接收信号: TERM ...