很久前写了个类BootLoader的东西,后来最近因为公司的需要所以做成了BootLoader,目前已实现成功,并且除去很多之前的BUG,目前很稳定,趁现在杀毒的时候就随便写写,以后慢慢补充。不断更新中,写得很乱以后再整理。。。高手们请给点意见再改进改进,毕竟偶的上位机水平实在烂。我的目的是让想做BL的朋友能通过本篇文章减少开发时间,本贴目前只给Mcuzone的会员看,当然mcu123兄可以过段时间转贴,不要怪我偏心啊,嘻嘻,所以暂时没考虑移到其他组或者贴到其他的BBS,请勿转贴谢谢合作!

硬件:EasyARM2200 软件:ADS1.2 Borland C++Builder 6

BootLoader下位机分两部分,一为对内外部FLASH的擦除,读写操作,另一部分为与上位机的通讯协议问题了。首先讲的是第一部分,操作外部FLASH很容易了,这里就不说了,操作内部FLASH呢,对于LPCXXXX来说注意几点,主要是用IAP操作,在操作IAP时不要开中断,不过我整个程序都没用中断,然后就是对PLL的操作,把PLL关了,操作内部FLASH时注意256K还是128K类型,看手册就能知道了,主要看一下扇区的大小,因为不是完全对等的,在分散加载处把RAM的底部改为STACKS 0x40003FE0 UNINIT 非必需的,注意看手册上写的,要求写入数据地址要对齐,因为我不想特意指定地址,用数组方式但又不一定是对齐,很简单的办法就是

#define mem_align( x ) ((INT8U *)((((INT32U)(x)) + 3) & ~0x3))用这个宏来处理,把数据的BUF多开个+3就行了,这样取出来的指针位置就是对齐的了,然后把REMEP设置为内部0x40000000的地址处,这样方便的好处,无论你在内部FLASH还是外部FLASH,还是内部RAM都一样能正常操作,我已测试过,并且还有一个好处就是当用中断的话用这种方法你的程序就算不是从0x0000000开始也无所谓,我现在做的是从FLASH将BL调到内部RAM运行,然后又可以通过BL引导应用程序。其实前期做下位机时花的时间较长的就是对IAP的操作了,还有就是项目工程注意把C语言工程里的ARM/Thumb勾找上,我也快记不来了,到时翻翻再补上,现在是凭记忆写的。然后就是跳转处理,一个方法就是在汇编里将PC指向你要跳的地址,二是把这个跳转设置为变量,这样通过上位机来引导任何地址,当然先得有个默认的,我设置为0x80000000,用宏来处理,这样改头文件就行了,剩下的就是通讯时的处理,我当时没注意出现一个BUG,大量数据通讯到一半时如果把线取了就会发生下位机假死,解决办法很简单,做一个超时处理就OK了,其实我因为一是当时没把IAP处理好,二是因为想代码小所以没用UCOSII,用UCOSII的话更容易。我用的是查询方式接收串口数据,如果开个收发缓冲区用中断方式的话借鉴OSView的方式效率会更高些,但我不想再动原来的结构就只好将就了。

前面的TargetInit因为初始化了PLL我不想改它就加入

PLLCON = PLLCON & 0xFD; PLLFEED = 0xaa; PLLFEED = 0x55; while((PLLSTAT & (1 << 9)) != 0);

把PLL关了就是了这样时间也非常短

现在开始讲讲通讯协议,我用的格式为

HEAD_1 HEAD_2 CMD LEN_16Bit DATA CRC16Bit END_Flag

格式就是上面这样了,至于下位机接收处理很简单,做成状态机,入口函数在状态机里面做个超时返回指针void RecvComChar(INT8U Ch, INT16U *pTimeOutVal)这个主要返回给Ch = GetCh(TimeOutVal, &RecvOK);当接收有效时才能执行接收状态机处理函数,接收超时的话直接复位状态机,这样就可以做成一个超时处理,但启动检测时我分开两个来做,因为正常程序在接收出错或者其他情况错误时要将超时设置为0,意思是一直等,而启动检测时就不要做成这样了,一出错就退出不给超时一直等的机会,通讯时注意大小端问题,我一直都用的是小端方式传输,所以接为发送都要按相同的顺序来处理。

我的做法是上位机不停的发启动检测命令,这个包里面带有密码,下位机接收到正确命令及密码后验证,然后再回应上位机,在发送密码及密码校验时用异或处理,这样就不用进行明文密码传输了,当然也可用其他加密方式。下位机上电启动时检测是在一定的时间内接收上位机包,如果是正确包的话检查密码,成功后进入BL,如果其中有一步没对的话就直接引导用户代码。

写得有点乱喔,到时再改了,说说上位机吧,上位机我用的是CComPort的C++类,有超时接收功能,这样写握手程序很方便,因为协议是自己定的,接收长度自己心里有底,超时未收到返回-1,收到后检查长度,这里当时我没注意一个BUG,就是没检测此包的头尾是否正确,导致CB在CRC检验时无反应,程序就死在那里了。解决办法是检查头1,头2是否OK,检查接收到的最后一个字节是否为END_Flag,如果OK才返回,要不然就是无效包,头跟尾都不对还搞个毛喔。

这上面这层就是封包拆包处理了,因为我应答也做成相同的包,先返回下位机收到命令应答包,然后下位机再返回操作成功与否包,包含本次操作是否成功,这样有个好处就是你写命令很快,但下位机不要等到操作完以后再返回,因为如果你下位机操作用上一分钟的话就不是等烦了,但这是极端,我觉得有好处也有坏处,但相信一点,通讯所花的时间大多在你的波特率问题而不是我多了一个应答命令问题。把这做好后就是多线程处理了,做一个当前操作模式的变量,因为多线程时只同时一个命令对串口操作,所以在那个while(1)里做一个bRunning来看是否正在运行命令,如果运行就的话就忙,通知用户再等等吧。通过操作模式来识别你操作什么,如写内部还是写外部FLASH,是读内部还是外部FLASH等,忘了说一个技术点,就是假设用户要中止怎么办?

int CFlashCore::CheckEventKill(void) { if(::WaitForSingleObject(m_hEventKill, 0) != WAIT_TIMEOUT) { return -1; } return 0; }

这样的办法是检测是否用户发送中止当前操作的功能,这个函数可以加在很多地方,然后就是分仅是中止呢还是杀了该线程,处理办法很简单,就是做个变量,做两个函数,一个只是中止而已,一个是杀,区别在于一个在运行时收到KILL事件就复位Event,而另一个就退出线程序循环。在每个命令运行前要检测串口是否打开,未打开就直接挂起就OK了,忘了说了,执行完一个命令后都要挂起,因为下次再执行时设置命令模式再唤醒线程就OK了,当时第二版时考虑做成通用的就封装类方式处理,CComPort类作为CFlashCore类的一个成员变量,CFlashCore类作为CFlashThread类的一个成员,本考虑用QT写的,这样就可以在WIN,LINUX下用,但没时间了,现在除了串口超时处理外其他的改很简单了,都是C++的,因为要考虑到LINUX下也能达到同样的效果,没时间研究了,也是导致通讯协议很复杂麻烦的原因,因为没时间改了,所以一直加,越加越不顺眼,要想再弄得更好的话只能再改协议。在CFlashCore开始我为了做成DLL所以用消息来通讯,只要共享主窗口的句柄就OK了,主窗口到时接收消息来处理,我现在做的是连传输的进度条也做成消息,实验证明很方便,我的信息输出函数是void CFlashCore::OutputString(AnsiString str, int color)这样有个好处,到时接收到的字符串消息可以还知道color这样显示出来是错误就显示红色,要不绿色等。

补充:当时本考虑过启动检测采用像PHILIPS的ISP那种简单的'?'来识别,当然后面有同步等处理,LPC的ISP功能我也实现了,是先做了ISP才做BL的,后来考虑这样不好,如果当目标板在运行用户程序时不小心重启时这时正好串口来了个'?'不就误进BL了,所以还是做成用像上面那个通讯格式加上密码,加上CRC16校验这样就把误入的机率降得很低了,还有当时一直无法正确设置PC跳转到指定的内部FLASH,但跳到内部RAM,外部FLASH的任何问题都没问题,过了很久后我请教了铁匠这个问题,在他的指点下成功实现了,后来我把我以前写的程序调出来看,一样的没区别呀,到现在还没搞懂怎么回事。

对于用户应用程序如果在外部FLASH的0x80000000开始的话不用怎么修改,而假如你的BL放在内部FLASH,而用户程序放在第二个块开始,或者放在外部FLASH的第二个块开始又如何办呢?将分散加载地址改为你指定的地址,当然要对齐,一般我是按块地址来分,然后将启动REMAP改为映射到内部RAM,将你的异常向量表拷到0x40000000去,这样就OK了,我测试过,在内部FLASH跟外部FLASH都有用户程序,BL默认引导外部FLASH的用户代码,我可通过上位机改变BL引导地址,让它引导内部FLASH的用户代码。

补充:对于超时重试的处理,假设在读FLASH过程中出现通讯出错或者数据检验未通过可以用重试方式来处理,但目前我的BL还未做此功能,这个功能主要由上位机来处理。


图片点击可在新窗口打开查看


图片点击可在新窗口打开查看

对于高手来说这个贴子对他没用的,对于没做过的朋友又想试一下的话我觉得有点点帮助

,原来用ISP烧写100多K的二进制代码要好像50多秒钟,换成我写的软件因为是只传二进制快多了,只要15秒左右吧,大家都知道EmLoad吧http://www.segger.com/emload.html,当时实在搞不到它的源代码,只好自己写一个,上位机现在还没实现那种查看16进制的功能,用VC的控件的话太麻烦,只好不弄了,现在好像很多功能跟他实现的差不多了

http://www.mcuzone.com/bbs/dispbbs.asp?boardID=16&ID=2000&page=1可以把这里的OSView下下来参考一下,不过我写这个BL的时候还没拿到这个代码,所以只把它的代码看懂,而没有去学着去做,毕竟已经稳定的东西就不想再做大动作了

文档在12月26号发给铁匠看了,他说写得很乱,简单问题复杂化了,不建议这样做

说明:这个文章是一年前写的了,一直放在mcuzone的会员区里,这两天才开的BLOG所以就贴到这边来了,可能现在也没什么人对ARM7的BL感兴趣了,现在精力花在LINUX上面