完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
1个回答
|
|
NRF52832的程序升级,即DFU,有通过无线方式(OTA)升级,也有通过UART,USB等硬件接口进行升级,目前资料最多的是通过无线方式进行升级,大家可以参考“青风带你学蓝牙”、“[艾克姆科技]nRF52832开发指南”等系列资料。
本篇文章主要记录利用UART和自定义协议如何进行设备的固件升级。对于UART,相信大家都很清楚,那么什么叫自定义协议升级呢?就是指传输新固件的时候用自己定义的协议,不用管NRF官方的的传输协议。 本文记录的方法是在支持OTA升级(Dual Bank Flash)的基础上修改的,使用此方法前可先了解OTA方式升级,所以使用此方法即可保证OTA不受影响,又能利用有线方式进行升级。此方法还有一个用处就是,如果用UART搭载4G模块,便可实现远程升级。 1.得到新固件 使用OTA升级时,我们会用boot.hex、SDK.hex、APP.hex以及一些命令去生成一个压缩包文件,然后用nRF Connect手机App升级时,选择用此压缩包进行升级。当我们用解压工具解压此压缩包是会得到三个文件:app.bin;app.dat;manifest.json。没错,app.bin就是我们需要传输的新固件。 2.传输新固件(远程传输) 我们得到新固件app.bin后,可以利用串口去传输它到NRF52832中,为防止传输过程中出现数据丢包和数据错误的情况,最好自己定义一个协议,将新固件打包传输,在NRF52832中按照协议解析,当解析出错时,进行固件重传。 如何进行远程传输?可用4G模块或者其他可远程传输数据的模块,连接到NRF52832上,例如使用4G模块EC20,将EC20用串口UART和NRF52832相连,EC20连接到你自己的远程服务器上,在服务器上开一个TCPserver,服务器上使用TCP发送固件,EC20接收到后通过串口将固件透传给NRF52832。 我们可以在应用程序中或者boot loader中添加传输新固件的代码。 3.存储新固件 NRF52832使用OTA升级时,NRF52832应用程序存储区分为bank0、bank1两个区,bank0存储的是当前正在运行的固件,bank1存储的是新固件。通过调试发现bank0的起始地址是固定的0x26000,bank1的起始地址是=bank0的起始地址+bank0的app大小并页对齐,boot loader中也提供了相应的计算函数: uint32_t nrf_dfu_bank1_start_addr(void) { uint32_t bank0_addr = nrf_dfu_bank0_start_addr; return ALIGN_TO_PAGE(bank0_addr + s_dfu_settings.bank_0.image_size); } 4.使新固件骗过BootLoader,让BootLoader认为新固件有效 我们将新固件已经存到了bank1区域,但是bank0才是当前程序运行区域,也就是还需要将程序copy到bank0,在OTA的bootloader中,已经有这部分代码了,只是要让BootLoader检测到bank1的新固件合法有效才会去执行这步操作。 怎么让BootLoader认为我们bank1的新固件合法有效呢?其中最重要的就是s_dfu_settings这个结构体,它的值是在BootLoader启动时,从flash中获取并通过校验的,对应BootLoader中的函数是 void nrf_dfu_settings_reinit(void) 所以,我们要使bank1中的固件能通过BootLoader的校验,就需要更改s_dfu_settings的值,它是一个结构体变量,包含了bank0、bank1存储的固件大小,CRC校验,以及自身数据的校验值等等,当BootLoader检测到s_dfu_settings的检验数据不正确时,还有一个备份s_dfu_settings数据的flash区,会从备份区域给s_dfu_settings赋值并更新原s_dfu_settings存储区的值。 接下来就是如何更改s_dfu_settings?更改s_dfu_settings数据,可以在BootLoader从flash读取到数据以后再更改,也可以在读取之前去将要更改的数据存到相应flash中(如果此段flash可以用app读写,则用此方法实现升级可以不用修改BootLoader代码,完全由APP实现)。 为区分OTA和UART升级,在UART传输完成后,需要设置一个标志位,标志位符合UART升级事件再执行修改s_dfu_settings的操作。修改数据在“void nrf_dfu_settings_reinit(void)”函数中完成。 修改s_dfu_settings数据: 1>s_dfu_settings.progressv.update_start_address 新固件的起始存储地址,对于我而言固定为0x3E000,也可以利用上面的函数去计算; 2>s_dfu_settings.bank_1.image_size 新固件大小; 3>s_dfu_settings.bank_1.image_crc 新固件CRC检验值,此值可用BootLoader提供的现有函数"crc32_compute()"计算而得; 4>s_dfu_settings.bank_1.bank_code 更改为“NRF_DFU_BANK_VALID_APP”,表示有效的APP; 5>s_dfu_settings.crc “s_dfu_settings“的校验值,因为我们已经把s_dfu_settings中的数据改了,所以CRC就和之前不同,应该重新计算后赋值,利用BootLoader现有的函数”settings_crc_get“去计算此值; 6>s_dfu_settings.boot_validation_app.bytes 将s_dfu_settings.bank_1.image_crc复制给它; 7>修改完成后,还需要把这些数据更新到相应flash中,以便下次重启后通过检验,用到的函数是“”nrf_dfu_settings_write"; 8>修改备份s_dfu_settings区域数据有效性判断条件,如果有效,则会把备份区的数据重新赋值给s_dfu_settings,以上工作就做了无用功,所以判断用于区分OTA和UART升级的标志位,在UART升级时,不要用备份区数据去覆盖已经修改过的s_dfu_settings数据; 9>清除用于区分OTA和UART升级的标志位。 我修改后的代码展示 void nrf_dfu_settings_reinit(void) { bool settings_valid = settings_crc_ok();//校验flash中的s_dfu_settings数据 bool settings_backup_valid = settings_backup_crc_ok();//校验flash中备份的s_dfu_settings数据 if (settings_valid)//如果flash中的s_dfu_settings数据校验有效 { NRF_LOG_DEBUG("Using settings page."); memcpy(&s_dfu_settings, m_dfu_settings_buffer, sizeof(nrf_dfu_settings_t));//将flash中的数据复制给s_dfu_settings /**************UART升级更改s_dfu_settings数据***************/ uint32_t new_firmware_init=0x69000;//app中新固件传输完成,我将标志位和新固件大小写到此flash uint8_t new_firmware_flag[8]={0}; nrf_dfu_flash_read(new_firmware_init, new_firmware_flag, 8);//读取标志位,次函数自己根据原有的读flash修改的,BootLoader中没有,自己稍微修改一下或者用原来的,功能没有区别,只是为了更方便使用 if(new_firmware_flag[0]==0x01) { s_dfu_settings.progress.update_start_address = 0x3E000;//bank1起始位置 s_dfu_settings.bank1.image_size = ;/*新固件大小*/ s_dfu_settings.bank1.image_crc = crc32_compute((uint8_t*)(s_dfu_settings.progress.update_start_address),s_dfu_settings.bank1.image_size,NULL);//计算新固件crc s_dfu_settings.bank1.bank_code = NRF_DFU_BANK_VALID_APP;//新固件有效 s_dfu_settings.crc = settings_crc_get(&s_dfu_settings);//重新计算s_dfu_settings的crc memcpy(s_dfu_settings.boot_validation_app.bytes, &s_dfu_settings.bank1.image_crc, 4);//也是存储新固件crc的位置 ret_code_t err_code = nrf_dfu_settings_write(NULL);//将s_dfu_settings的值更新到flash中 if(err_code != NRF_SUCCESS) return; nrf_dfu_flash_erase(new_firmware_init, 1, NULL);//清除UART升级标志位 } else if (settings_backup_valid)//如果flash中备份的数据有效 /***********************************************************/ { NRF_LOG_DEBUG("Copying forbidden parts from backup page."); settings_forbidden_parts_copy_from_backup((uint8_t *)&s_dfu_settings); } } /*以下程序未修改,此处展示时省略*/ } 到此,升级需要修改的代码就完成了。更改s_dfu_settings数据后,BootLoader检测带新固件有效就会去从bank1 copy新固件到bank0,并自动更新其他相关的数据和写flash,比如备份s_dfu_settings数据的flash会自动被跟新,不需要我们去更改。然后BootLoader跳转到app中,此时的app就是升级后的app。 本文重点是第4步,理解之后就能运用了。如果能用蓝牙去广播新固件,用此方法是不是可以实现批量升级?如果可以用蓝牙广播固件,丢包、传输速度又是问题,如何提高升级的效率? |
|
|
|
只有小组成员才能发言,加入小组>>
3278 浏览 9 评论
2955 浏览 16 评论
3455 浏览 1 评论
8987 浏览 16 评论
4050 浏览 18 评论
1102浏览 3评论
570浏览 2评论
const uint16_t Tab[10]={0}; const uint16_t *p; p = Tab;//报错是怎么回事?
568浏览 2评论
用NUC131单片机UART3作为打印口,但printf没有输出东西是什么原因?
2301浏览 2评论
NUC980DK61YC启动随机性出现Err-DDR是为什么?
1857浏览 2评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-22 08:53 , Processed in 1.063287 second(s), Total 82, Slave 60 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号