nRF51822 看门狗和OTA (无线升级功能)的尴尬笔记

很久没有记笔记了。今天要记点东西,不然以后又忘记了。

随着时代的发展,现在的SDK已经是13.0了。蓝牙5.0也就来了。废话就少说了,记笔记吧。

两年前搞过nRF51822 的无线升级功能,那时候用的还是 SDK5.20,直接用hex镜像进行升级的。后来的SDK就不再是hex,要用zip了,现在还不清楚具体做APP时候的用法。以后再说,先用官方提供了nRF_ToolBox能升级就很不错了。

现在用了新的SDK,试了很长一段时间,各种各样的文档都过了,各种各样的方法都试过了,就是不行,真特么尴尬。后来今天又试了,到官方论坛去搜各种各样的情况,尝试,到底还是有点眉目了。

SDK9.0的DFU例子,在.. RF51_SDK_9.0.0_2e23562examplesdfuootloaderpca10028dual_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("
[Boot]Uart Init OK.
");
11 #endif
12 
13     if (app_reset)
14     {
15         M_LOG("[Boot]in DFU Mode...
");
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...
");
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...
");
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 }
View Code
里面除了屏蔽很多行代码,还多了几行代码,是某个软件定时器的,创建、开启和关闭。
 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
. rfutil.exe dfu genpkg app.zip --application app.bin --application-version 0xffffffff --dev-revision 0xffff --dev-type 0xffff --sd-req 0x0064

原文地址:https://www.cnblogs.com/ceibacity/p/7028234.html