对于单片机做的产品,要实现在线升级,单片机内部一般是两段代码,一个是bootloader程序,一个是用户app程序,bootloader程序主要就是实现app升级的程序,它是单片机上电后首次运行的程序,app程序就是实现产品功能的程序。
对于nRF51822来说,稍微有点特殊,但是本质原理也是一样的,nRF51822芯片内部有段SoftDevice的程序,它是芯片上电后首次运行的程序,不过这段程序不负责程序升级,它是Nordic官方提供的蓝牙协议栈程序,当然它也具备一点bootloader的功能,也就是说,芯片上电后,它会判断芯片内部是否有bootloader代码(bootloader代码位置固定,所以它能判断出是否有合法的bootloader程序),若有bootloader程序则会跳转到bootloader程序执行,若没有bootloader程序而只检测到了用户的app程序,那么就直接跳转到app程序运行,当然这个bootloader程序是我们自己写的代码,只是它存储的位置和app程序是不一样的。
bootloader程序差不多也只做两件事情,1:控制程序的跳转,比如跳转到app程序;2:实现app程序升级;
当然bootloader是我们自己写的,你要实现更多的功能也是可以的,但是最基本的功能就是这两个,bootloader运行后,检测直接跳转到app的条件是否满足(app程序合法,相关标志位合法),若满足就直接跳转到app执行app程序,若不满足就继续执行bootloader,等待升级的相关操作命令;
![](http://www.embed-net.com/data/attachment/forum/201507/08/171829kcmc5kyzkc7wadca.jpg)
手机端DFU功能简介:
单片机段程序准备好了,当然就需要手机端程序来实现固件的升级了,主要工作流程如下:
1,扫描蓝牙设备,连接蓝牙设备,查询蓝牙设备是否支持DFU服务;
2,若支持DFU服务,那么将本地的固件通过蓝牙发送出去;
3,数据发送完毕,升级固件成功
nRF51822程序加载顺序
1)SoftDevice加载;
2)SoftDevice初始化蓝牙协议栈;
3)SoftDevice检查0x10001014处是否保存有有效的BootLoader地址, 如果不是跳转到0x14000处的APP执行,流程结束.
4)SoftDevice跳转到BootLoader
5)BootLoader进行他的业务处理工作;
6)BootLoader跳转到0x14000处的APP执行,流程结束.
下面介绍百度手环的成个代码关于OTA的工作流程。
1:SoftDevice加载;
2:SoftDevice初始化蓝牙协议栈;
3:我们已经有 Bootloader了所以他检查Bootloader是不是合理
代码: APP_ERROR_CHECK_BOOL(NRF_UICR->CLENR0 == CODE_REGION_1_START);//检测BOOTLOADER是否存在
4:检查Bootloader标志位if (should_enter_bootloader || (wdt_reset & 0x02))
if进入OTA模式 err_code = bootloader_dfu_start();//进入OTA里面完成:蓝牙连接,BIN传到WSAP.
等待完成进入。
然后进入APP(是否设置标志位都进入)
//跳到DFU_BANK_0_REGION_START(0x00014000) application程序存放的首地址跳过去就不会往下运行了
bootloader_app_start(DFU_BANK_0_REGION_START);
5:进入APP后如果设置标志位,然后复位循环以上的。
注意:Bootloader和Application还有SoftDevice是三个独立的代码,
其中SoftDevice是官方提供的HEX文件,(S110、S120或者S130等蓝牙协议)不可修改。
代码分析
这时Application工程中的触发OTA模式
/**********************************************************************
* User to reset the MCU & enter OTA
***********************************************************************/
void trigger_ota_mode(void)
{
sd_power_gpregret_set(0x01);//设置一个保留的寄存器为0X01(作为启动OTA的标志)
//reset the system and start OTA
NVIC_SystemReset();//复位进入OTA
}
然后再BOOTLOADER里判断标志位。
uint8_t should_enter_bootloader = NRF_POWER->GPREGRET & 0xff;
uint16_t wdt_reset = (NRF_POWER->RESETREAS) & (0xFFFF);
if (should_enter_bootloader || (wdt_reset & 0x02))
满足进入OTA,不满足跳转Application。
简单的说就是:
设置标志位》判断》进入》运行》跳转到APP。
下面是BOOTLOAER mian完整代码
int main(void)
{
uint32_t err_code;
wdt_stop(); //stop wdt whether it started or not
leds_init();
// This check ensures that the defined fields in the bootloader corresponds with actual
// setting in the nRF51 chip.
APP_ERROR_CHECK_BOOL(NRF_UICR->CLENR0 == CODE_REGION_1_START);//检测BOOTLOADER是否存在
//检查BOOTLOADER的地址是不是0X03C800
APP_ERROR_CHECK_BOOL(*((uint32_t *)NRF_UICR_BOOT_START_ADDRESS) == BOOTLOADER_REGION_START);
APP_ERROR_CHECK_BOOL(NRF_FICR->CODEPAGESIZE == CODE_PAGE_SIZE);
// Initialize.
timers_init();
//进入 OTA的条件, application程序中 sd_power_gpregret_set(0x01);就是设置这个 NRF_POWER->GPREGRET
uint8_t should_enter_bootloader = NRF_POWER->GPREGRET & 0xff;
uint16_t wdt_reset = (NRF_POWER->RESETREAS) & (0xFFFF);
if (should_enter_bootloader || (wdt_reset & 0x02))
{
nrf_gpio_pin_set(LED1);
// Initiate an update of the firmware.
err_code = bootloader_dfu_start();//进入OTA里面完成:蓝牙连接,BIN传到WSAP.
APP_ERROR_CHECK(err_code);
nrf_gpio_pin_clear(LED1);
}
//exit from DFU unit so just Start application
//进入OTA条件不满足进入application
{
//set retain register
NRF_POWER->GPREGRET = 0x00;//清0
//clear AIRCR register
NRF_POWER->RESETREAS = 0x04;
if(wdt_reset & 0x02) {
NRF_POWER->RESETREAS = 0x02;
}
// Select a bank region to use as application region.
// @note: Only applications running from DFU_BANK_0_REGION_START is supported.
//跳到DFU_BANK_0_REGION_START(0x00014000) application程序存放的首地址跳过去就不会往下运行了
bootloader_app_start(DFU_BANK_0_REGION_START);
}
leds_off();
//comes here should reenter DFU
//这里重新进入OTA应该是如果bootloader_app_start(DFU_BANK_0_REGION_START);失败
NRF_POWER->GPREGRET = 0x01;
NVIC_SystemReset();//复位重新进入OTA因为NRF_POWER->GPREGRET = 0x01;
}