很久沒有記筆記了。今天要記點東西,不然以后又忘記了。
隨着時代的發展,現在的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
