nRF51822 看门狗和OTA (无线升级功能)的尴尬笔记
很久没有记笔记了。今天要记点东西,不然以后又忘记了。
随着时代的发展,现在的SDK已经是13.0了。蓝牙5.0也就来了。废话就少说了,记笔记吧。
两年前搞过nRF51822 的无线升级功能,那时候用的还是 SDK5.20,直接用hex镜像进行升级的。后来的SDK就不再是hex,要用zip了,现在还不清楚具体做APP时候的用法。以后再说,先用官方提供了nRF_ToolBox能升级就很不错了。
现在用了新的SDK,试了很长一段时间,各种各样的文档都过了,各种各样的方法都试过了,就是不行,真特么尴尬。后来今天又试了,到官方论坛去搜各种各样的情况,尝试,到底还是有点眉目了。
SDK9.0的DFU例子,在..\nRF51_SDK_9.0.0_2e23562\examples\dfu\bootloader\pca10028\dual_bank_ble_s110 这个路径,我修改了一点点程序,
1.修改 bootloader_settings.c 文件下的这一行
uint8_t m_boot_settings[CODE_PAGE_SIZE] __attribute__((at(BOOTLOADER_SETTINGS_ADDRESS))) __attribute__((used))
为
uint8_t m_boot_settings[CODE_PAGE_SIZE] __attribute__((at(BOOTLOADER_SETTINGS_ADDRESS))) __attribute__((used)) = {BANK_VALID_APP};
没有这个,程序用一些手段烧进去了它就一直是bootloader那运行,进不了application,具体的原因查看下下面的这个链接,官方论坛网友提的一个问题和别人的解答:
https://devzone.nordicsemi.com/question/2304/device-is-always-in-bootloader-mode/
2.修改main函数:
1 int main(void) 2 { 3 uint32_t err_code; 4 5 // bool dfu_start = false; 6 bool app_reset = (NRF_POWER->GPREGRET == BOOTLOADER_DFU_START); 7 8 #ifdef UART_DEBUG 9 uart_init(); 10 M_LOG("\r\n[Boot]Uart Init OK.\r\n"); 11 #endif 12 13 if (app_reset) 14 { 15 M_LOG("[Boot]in DFU Mode...\r\n"); 16 NRF_POWER->GPREGRET = 0; 17 } 18 19 // leds_init(); 20 21 // This check ensures that the defined fields in the bootloader corresponds with actual 22 // setting in the nRF51 chip. 23 APP_ERROR_CHECK_BOOL(*((uint32_t *)NRF_UICR_BOOT_START_ADDRESS) == BOOTLOADER_REGION_START); 24 APP_ERROR_CHECK_BOOL(NRF_FICR->CODEPAGESIZE == CODE_PAGE_SIZE); 25 26 // Initialize. 27 timers_init(); 28 29 err_code = app_timer_create( &feed_wd_timer_id, APP_TIMER_MODE_REPEATED, timer_index_feed_wd ); 30 APP_ERROR_CHECK(err_code); 31 32 #if 0 33 buttons_init(); 34 #endif 35 36 (void)bootloader_init(); 37 #if 0 38 if (bootloader_dfu_sd_in_progress()) 39 { 40 // nrf_gpio_pin_clear(UPDATE_IN_PROGRESS_LED); 41 42 err_code = bootloader_dfu_sd_update_continue(); 43 APP_ERROR_CHECK(err_code); 44 45 ble_stack_init(!app_reset); 46 scheduler_init(); 47 48 err_code = bootloader_dfu_sd_update_finalize(); 49 APP_ERROR_CHECK(err_code); 50 51 // nrf_gpio_pin_set(UPDATE_IN_PROGRESS_LED); 52 } 53 else 54 #endif 55 { 56 // If stack is present then continue initialization of bootloader. 57 ble_stack_init(true);//!app_reset);// 58 scheduler_init(); 59 M_LOG("[Boot]ble_stack_init OK...\r\n"); 60 61 } 62 #if 0 63 dfu_start = app_reset; 64 dfu_start |= ((nrf_gpio_pin_read(BOOTLOADER_BUTTON) == 0) ? true: false); 65 66 if (dfu_start || (!bootloader_app_is_valid(DFU_BANK_0_REGION_START))) 67 #else 68 if(app_reset) 69 #endif 70 { 71 // nrf_gpio_pin_clear(UPDATE_IN_PROGRESS_LED); 72 73 err_code = app_timer_start(feed_wd_timer_id, APP_TIMER_TICKS(1000,APP_TIMER_PRESCALER), NULL); 74 APP_ERROR_CHECK(err_code); 75 76 77 // Initiate an update of the firmware. 78 err_code = bootloader_dfu_start(); 79 APP_ERROR_CHECK(err_code); 80 81 // nrf_gpio_pin_set(UPDATE_IN_PROGRESS_LED); 82 } 83 84 if (bootloader_app_is_valid(DFU_BANK_0_REGION_START) && !bootloader_dfu_sd_in_progress()) 85 { 86 87 (void)app_timer_stop( feed_wd_timer_id ); 88 M_LOG("[Boot]bootloader_app_start...\r\n"); 89 // Select a bank region to use as application region. 90 // @note: Only applications running from DFU_BANK_0_REGION_START is supported. 91 bootloader_app_start(DFU_BANK_0_REGION_START); 92 } 93 NVIC_SystemReset(); 94 }
err_code = app_timer_create( &feed_wd_timer_id, APP_TIMER_MODE_REPEATED, timer_index_feed_wd );
APP_ERROR_CHECK(err_code);
err_code = app_timer_start(feed_wd_timer_id, APP_TIMER_TICKS(1000,APP_TIMER_PRESCALER), NULL);
APP_ERROR_CHECK(err_code);
(void)app_timer_stop( feed_wd_timer_id );
这个定时器就是解决今天尴尬的关键!
3.话说当 application 触发进入 OTA 模式很简单,只需要在加入以下代码即可:
1 void ota_mode_entry(void) 2 { 3 sd_softdevice_disable(); 4 NRF_POWER->GPREGRET = 0xB1; 5 sd_softdevice_enable(NRF_CLOCK_LFCLKSRC_RC_250_PPM_8000MS_CALIBRATION,NULL); 6 NVIC_SystemReset(); 7 }
但是,但是!!!!蛋蛋是!!!
如果你的 application 启动了 看门狗, 当软件复位的时候,看门狗是不会停止的,它还在工作,当你不喂它,很显然它就狂吠你,导致重启。
这时候重启很显然就没办法再进行升级了,而是又进入了application。
nRF51822 的看门狗很奇怪,它一旦起来了,就不能软件关闭了,而且调用NVIC_SystemReset() 不会致使它的寄存器清除,它还在跑。
解决的办法只能是在 bootloader 程序加喂狗程序咯。论坛上有人直接在 for(;;)循环里面加喂狗代码,但是并不十分管用,因为有个低功耗休眠函数会阻塞程序,导致喂不到狗,
不知道论坛上的那些大神是怎么想的,我也懒得去研究他们的办法和解释,
所以只能加个软件定时器,1秒喂一次,就当是把它关闭了吧。下面是这个软件定时器的回调:
1 static app_timer_id_t feed_wd_timer_id; 2 3 static void timer_index_feed_wd( void *p_context ) 4 { 5 //feed the dog 6 NRF_WDT->RR[0] = WDT_RR_RR_Reload; 7 }
记得还要吧timers_init()里的第二个参数调整加1
#define APP_TIMER_MAX_TIMERS 4// 3
OK了,蛋蛋疼的OTA和看门狗之间的尴尬就化解了。
其他打包 zip 和烧录的 工作,就交给 帮助文档吧。
速度搜索一下 你的电脑,找到 How to generate the INIT file for DFU.pdf 文档。
用到的工具是: 1.hex2bin.exe 2.nrfutil.exe
下面是命令行脚本的内容,这样就可以生成 zip 了。
.\hex2bin.exe app.hex
.\nrfutil.exe dfu genpkg app.zip --application app.bin --application-version 0xffffffff --dev-revision 0xffff --dev-type 0xffff --sd-req 0x0064