了解更多关于bootloader 的C语言实现,请加我QQ: 1273623966 (验证信息请填 bootloader),欢迎咨询或定制bootloader(在线升级程序)。

  PIC32MZ 的 flash memory 支持live update, 这是个全新的特性,在之前的所有PIC不管是8位还是16位的单片机上面都没有这个特性。我写过很多PIC 8位和16位单片机的bootloader,但这些bootloader都是传统的bootloader。传统的bootloader在更新程序时,需要先进入BOOTLOADER模式,BOOTLOADER模式完全独立于用户程序,只做更新应用程序的活。 今天要介绍的我最新完成的live update bootloader, 它是用户程序的一部分,它在烧写新的用户程序时,仍然可以侦测外部信号,及时响应外部输入,反馈外部请求。也就是用户程序还是在运行,新的用户程序烧写完成后,可以对新的用户程序做CRC验证,如果验证OK后,才跳转到新的用户程序。如果整个更新的过程出现异常的话,可以不跳转,继续运行旧的用户程序,基本不影响用户的使用。总之live update bootloader和传统的bootloader比较,更新用户程序更可靠,更新程序时也不影响用户的使用,用户体验更好。

我是利用业余时间完成这个PIC32MZ live update bootloader,过程时断时续,碰到的难题是一个接一个。例如boot flash 烧写不对,boot sequence不可以烧写,live update 更新新程序时,MCU直接卡死等等。虽然这些问题最后都一一解决了,但它确实是我用时最久一个bootloader。碰到的困难越多,最后的成就感也越大。所以live update bootloader 是我的encrypted bootloader之后最喜欢的一款bootloader。

我试着讲下PIC32MZ live update的原理,从memory map 讲起。PIC32MZ 的memory map有点复杂,要想做bootloader,必须搞清楚memory map. PIC32 的program flash( 一般用户程序的存储区域)地址分为虚拟地址和物理地址,CPU取指令是按照虚拟地址去取,在bootloader中擦除或烧写的地址是用物理地址。所以我们的live update bootloader 烧写时主要操作的是物理地址。以我用的PIC32MZ2048ECH144为例,它的program flash的物理地址范围是0x1d000000-0x1d1fffff. 这个区域又分为两个空间大小相同的bank, PFM Bank1和PFM Bank2. 芯片启动后,执行完startup后就自动跳到0x1d000000开始的的区域(PFM lower mapped region), 当NVMCONbits.SWAP = 0时(默认情况), PFM Bank1映射到0x1d000000开始的区域,  这时执行的就是PFM Bank1中的程序,当NVMCONbits.SWAP = 1时,PFM Bank2映射到0x1d000000开始的区域, 芯片启动后,执行完startup后自动跳到0x1d000000开始的区域(PFM lower mapped region), 这时执行的就是PFM Bank2中的程序。live update就是利用这个特性实现无缝更新应用程序。

接着具体讲解下我的PIC32MZ2048ECH live update bootloader是如何实现的,以及如何验证的。整个实现和验证的过程中,我写了3个程序。其中两个是带有live udate功能的APP1和APP2, APP1和APP2 是使用同一个linker文件build的。先烧录APP1到芯片的0x1d000000开始区域,运行在PFM Bank1空间(此时,NVMCONbits.SWAP=0)。这个时候上位机发送APP2的hex文件过来,APP1接收了APP2的hex文件,烧写到PFM Bank2的空间(0x1d100000开始)。烧写完成后,将BF2SEQ0的值修改成比BF1SEQ0大,修改后,如果期望芯片重启后,运行PFM Bank2的程序,需要修改NVMCONbits.SWAP的值,但是由于NVMCONbits.SWAP的值不可以在运行的PFM的程序中需改,所以我写了第三个程序BOOT0, BOOT0是运行在Boot flash, 重启后执行完startup后,芯片先运行BOOT0的程序,在BOOT0的程序中判断BF2SEQ0和BF1SEQ0的值,如果BF1SEQ0的值大于等于BF2SEQ0,就将NVMCONbits.SWAP清零,然后跳转到PFM Bank1, 如果BF2SEQ0大于BF1SEQ0,就将NVMCONbits.SWAP置1。然后跳转到PFM Bank2。 就这样我的live update bootloader就实现了。

live update bootloader 的烧写部分和传统的bootloader是一样的,所以我就不多讲,具体也可以参考我之前的博文,http://www.cnblogs.com/geekygeek/p/hyperbootloader_pic32.html 和 http://www.cnblogs.com/geekygeek/p/pic32_serialbootloader.html。接下来讲live update的实现中两个比较重要的函数。第一个重要的是修改BOOT sequence的函数SwapBootPanels。刚才有讲到在live update APP 更新完新程序后,会将BF2SEQ0更新成比BF1SEQ0的值还大的一个值,这里要注意的是修改BF2SEQ0的值时不能用word write。 我是使用quad word write。下面是代码。(借鉴了Microchip forums网友aschen0866的不少代码,非常感谢他的帮助)。

#define UBA_BFSEQ_PA        0x1FC2FFF0

void SwapBootPanels(void)
{
printf("Boot Flash Swapping...\r\n");
CoreT_DelayMs(10);
UINT32 bf1seq0;
UINT32 bf2seq0;
bf1seq0 = BF1SEQ0;
bf2seq0 = BF2SEQ0;
UINT32 new_bfseq = GenNewFSEQ(bf1seq0, bf2seq0);
printf("New Sequence = 0x%08X\n\r", new_bfseq);
unsigned int s[4];
s[0] = new_bfseq;
s[1] = new_bfseq;
s[2] = new_bfseq;
s[3] = new_bfseq; unsigned int status = NVMWriteQuadWord(UBA_BFSEQ_PA,s);
if (status != 0)
{
printf("New BFMSEQ Write Failed\n\r");
}
} UINT32 GenNewFSEQ(UINT32 seq1, UINT32 seq2)
{
DWORD_VAL temp1, temp2, new_seq;
BOOL valid1, valid2; temp1.Val = seq1;
temp2.Val = seq2; valid1 = FALSE;
valid2 = FALSE; if ((temp1.word.LW + temp1.word.HW) == 0xFFFF)
{
valid1 = TRUE;
} if ((temp2.word.LW + temp2.word.HW) == 0xFFFF)
{
valid2 = TRUE;
} if (valid1 == TRUE && valid2 == TRUE)
{
if (temp1.word.LW >= temp2.word.LW)
{
new_seq.word.LW = temp1.word.LW + 1;
}
else
{
new_seq.word.LW = temp2.word.LW + 1;
}
}
else if (valid1 == TRUE)
{
new_seq.word.LW = temp1.word.LW + 1;
}
else if (valid2 == TRUE)
{
new_seq.word.LW = temp2.word.LW + 1;
}
else
{
new_seq.word.LW = 0;
} new_seq.word.HW = 0xFFFF - new_seq.word.LW; return new_seq.Val;
}

  第二个重要的是修改NVMCONbits.SWAP的函数,之前有讲过,调用这个函数的程序需要运行在boot flash。 代码如下。

#define USER_APP_BASE_ADDRESS 0xBD000000

void SwapFlashPanels(void)
{
printf("Jump...\r\n"); if (LowerBootAliasIsBootBank2())
{
if (NVMCONbits.SWAP == 0)
{
DisableINT();
NVMCONbits.WREN = 0;
NVMKEY = 0xAA996655;
NVMKEY = 0x556699AA;
NVMCONSET = 0x80;
}
}
else
{
if (NVMCONbits.SWAP == 1)
{
DisableINT();
NVMCONbits.WREN = 0;
NVMKEY = 0xAA996655;
NVMKEY = 0x556699AA;
NVMCONCLR = 0x80;
}
}
(*((void(*)(void))USER_APP_BASE_ADDRESS))();
} int LowerBootAliasIsBootBank2(void)
{
UINT32 s1 = BF1SEQ0;
UINT32 s2 = BF2SEQ0;
if (GetCurrentBFMbank(s1,s2) == 2)
{
return 1;
}
else
{
return 0;
}
} UINT32 GetCurrentBFMbank(UINT32 seq1, UINT32 seq2)
{
DWORD_VAL temp1, temp2;
BOOL valid1, valid2;
UINT32 cur_bank = 0; temp1.Val = seq1;
temp2.Val = seq2; valid1 = FALSE;
valid2 = FALSE; if ((temp1.word.LW + temp1.word.HW) == 0xFFFF)
{
valid1 = TRUE;
} if ((temp2.word.LW + temp2.word.HW) == 0xFFFF)
{
valid2 = TRUE;
} if (valid1 == TRUE && valid2 == TRUE)
{
if (temp1.word.LW >= temp2.word.LW)
{
cur_bank = 1;
}
else
{
cur_bank = 2;
}
}
else if (valid1 == TRUE)
{
cur_bank = 1;
}
else if (valid2 == TRUE)
{
cur_bank = 2;
}
else
{
cur_bank = 1;
} return cur_bank;
}

我的这个PIC32MZ live update bootloader是用的Uart接口,而网口和USB的live update bootloader还在计划中。

PIC32MZ Live update bootloader的更多相关文章

  1. 采用TCP协议的PIC32MZ ethernet bootloader

    了解更多关于bootloader 的C语言实现,请加我QQ: 1273623966 (验证信息请填 bootloader),欢迎咨询或定制bootloader(在线升级程序). 趁热打铁,在上一PIC ...

  2. PIC32MZ 通过USB在线升级 -- USB HID bootloader

    了解 bootloader 的实现, 请加QQ: 1273623966(验证填bootloader); 欢迎咨询或定制bootloader; 我的博客主页 www.cnblogs.com/geekyg ...

  3. PIC32MZ 通过U盘在线升级 -- USB Host bootloader

    了解bootloader的实现,请加QQ: 1273623966(验证填bootloader); 欢迎咨询或定制bootloader; 我的博客主页 www.cnblogs.com/geekygeek ...

  4. PIC24 通过USB在线升级 -- USB CDC bootloader

    了解bootloader的实现,请加QQ: 1273623966 (验证填bootloader):欢迎咨询或定制bootloader:我的博客主页www.cnblogs.com/geekygeek 今 ...

  5. 树莓派4b通过外接ssd硬盘启动系统失败的排查和解决

    树莓派4b通过外接ssd硬盘启动系统失败,症状: 屏幕卡在黑屏或提示 mmc1:Controller never released inhibit bit(s).... 先说如何设置硬盘启动,后面是解 ...

  6. 采用UDP协议的PIC32MZ ethernet bootloader

    了解更多关于bootloader 的C语言实现,请加我QQ: 1273623966 (验证信息请填 bootloader),欢迎咨询或定制bootloader(在线升级程序). 经过千辛万苦,今天终于 ...

  7. PIC32MZ 通过USB在线升级 -- USB CDC bootloader

    了解bootloader 的实现,请加QQ: 1273623966 (验证填 bootloader):欢迎咨询或定制bootloader:我的博客主页www.cnblogs.com/geekygeek ...

  8. Xmodem Bootloader

    了解更多关于bootloader 的C语言实现,请加我QQ: 1273623966 (验证信息请填 bootloader),欢迎咨询或定制bootloader(在线升级程序). 多年前玩Cisco交换 ...

  9. 自己用C语言写PIC32 serial bootloader

    了解更多关于bootloader 的C语言实现,请加我QQ: 1273623966 (验证信息请填 bootloader),欢迎咨询或定制bootloader(在线升级程序). 从15年12月份以来我 ...

随机推荐

  1. STM32F4时钟配置分析

    //学习STM32F4的过程中关于时钟上面讲的比较好 特地转发与大家分享 STM32F4时钟设置分析 原文博客链接:http://blog.csdn.net/jdh99,作者:jdh,转载请注明. 环 ...

  2. 对本地Solr服务器添加IK中文分词器实现全文检索功能

    在上一篇随笔中我们提到schema.xml中<field/>元素标签的配置,该标签中有四个属性,分别是name.type.indexed与stored,这篇随笔将讲述通过设置type属性的 ...

  3. WPF - 监听判断键盘组合键的按下

    对于键盘事件PreviewKeyDown.PreviewKeyUp.KeyDown.KeyUp,在其中检查当次事件是哪个按键触发的很简单,只需要判断KeyEventArgs类型的事件参数e的Key属性 ...

  4. 每天一个Linux命令(06)--rmdir命令

    终于忙完了公司的事,可以安静的充充电了. 今天学习一下Linux中命令:rmdir 命令,rmdir是常用的命令,该命令的功能是删除空目录,一个目录被删除之前必须是空的.(注意,rm -r dir 命 ...

  5. 【java基础之jdk源码】集合类

    最近在整理JAVA 基础知识,从jdk源码入手,今天就jdk中 java.util包下集合类进行理解 先看图 从类图结构可以了解 java.util包下的2个大类: 1.Collecton:可以理解为 ...

  6. Selenium 使用过程遇到问题随笔

    最近正在学习Selenium,自学是比较难的,也很感谢网络环境中,各位大大的博文帮助. 也希望在此能够记录一下从小白学习使用selenium测试的过程,也希望能对别人有所帮助. 关于环境部署,以及入门 ...

  7. Android--多线程之Handler 前言

    前言 Android的消息传递机制是另外一种形式的“事件处理”,这种机制主要是为了解决Android应用中多线程的问题,在Android中不 允许Activity新启动的线程访问该Activity里的 ...

  8. JavaWeb与Asp.net工作原理比较分析

    一.概述 不管是什么语言开发的web应用程序,都是在解决一个问题,那就是用户输入url怎么把对应的页面响应出来,如何通过url映射到响应的类,由于自己做asp.net的时间也不短了,还算是对asp.n ...

  9. 一个好用的几乎没有Callback的Android异步库

    android-async-task 这是一个Android平台处理复杂异步任务的库 (https://github.com/gplibs/android-async-task) 1. 安装方法 gr ...

  10. 常见的html面试题

    1.doctype作用?标准模式和兼容模式各有什么区别? (1).<doctype>声明位于文档第一行,在<html>标签之前.用于告知浏览器的解析器以什么样的标准解析该文档. ...