Cortex-M3单片机的IAP在线升级上位机和下位机

mac2025-02-10  14

最近有个项目要做在线升级功能,我也是第一次做,把学习的过程总结下,希望能够帮助到

其他人吧。本篇博客主要介绍两个部分,下位机和上位机。

首先说下要实现功能:

1、上位机能够把APP的bin文件烧写进下位机并运行;

2、下位机能够在运行APP程序时候,通过指令跳转到Boot区运行,进行程序在线升级;

3、上电首先运行BOOT区程序,然后能够判定目前APP区是否有程序,然后跳转至APP区运行;

       否则运行boot区程序,等待程序少写;

4、考虑到APP程序可能由于其它原因导致不能正常工作,要求上电后时候,虽然APP区有程序,

     仍然可以升级程序;

一、BOOT程序

1、思路:

      1)分为两个区,一个BOOT程序区,一个BOOT data 配置区(也可以用外面的非易失性存储器替代);

      2)上电根据BOOT DATA区参数来判定APP区是否有程序写入,然后计算APP区的CRC是否一致;如果

             一致则APP区程序有效;否则无效;

      3)跳转之前可以做几个秒级延时,或者判定某个引脚的状态,从而判定是否转到APP区运行,防止把APP

           区烧坏,boot也不起作用;

      4)在APP运行期间,可以通过升级指令跳转至boot区,这个时候,可以通过修改BOOT data 配置区数据,

            来告诉程序,现在要做在线升级了。

2、具体实现: 

       1)Flash的读、写、擦除这些都是官方库提供的,没啥可说的。注意读写的字节类型,擦除时候注意块擦除

              的块大小;

        2)跳转,在跳转之前,一定要把终端全部关闭;程序存储的首地址为栈顶指针;程序第二个地址存储的为程序

             执行地址;代码如下:

typedef  void iapfun(void);                //¶¨ÒåÒ»¸öº¯ÊýÀàÐ͵IJÎÊý.

bool jump_app(void) { iapfun *ptJumpToApp = NULL; __disable_irq(); //¹Ø±ÕËùÓÐÖÐ¶Ë ptJumpToApp=(iapfun*)(*((vu32*)(APP_ADDR+4))); //Óû§´úÂëÇøµÚ¶þ¸ö×ÖΪ³ÌÐò¿ªÊ¼µØÖ·(¸´Î»µØÖ·) __set_MSP(*((vu32*)APP_ADDR)); //³õʼ»¯APP¶ÑÕ»Ö¸Õë(Óû§´úÂëÇøµÄµÚÒ»¸ö×ÖÓÃÓÚ´æ·ÅÕ»¶¥µØÖ·) ptJumpToApp(); //Ìøתµ½APP my_delay_ms(1000); __enable_irq(); TRACE_DEBUG("jump_app error\r\n"); return false; }

       3)跳转到boot函数,类似;

二、APP程序

1、思路:

        1)正常运行APP,当收到在线升级程序指令,则修改BOOT data 配置区数据,跳转到BOOT区运行;

2、实现:

       1)程序运行之前,初始化时钟;SystemInit();

       2)中断地址重映射(如果不用中断,则可以省略;初始化其它功能,运行即可;(说明,这里的地址为存储地址,不是程                 序运行的首地址);

static void nvic_set(void) { uint32_t wAdd = 0; //wAdd = *((uint32_t*)(APP_ADDR+4)); wAdd = APP_ADDR; wAdd -= 0x08000000; //Æ«ÒÆÁ¿ NVIC_SetVectorTable(NVIC_VectTab_FLASH, wAdd); //IAPʹÄÜ }

       3)编译时候,把起始地址修改为存储地址,如下图:

三、问题

1、boot里面的看门狗时间要>FLASH擦除时间(我的单片机擦除APP大概4S,因此为关闭了看门狗);

2、APP程序里面如果打开了看门狗,则要求如下:

      1)要么boot看门狗打开,要求跳转之前清狗,跳转时间+boot初始化时间<看门狗时间;

      2)要么boot放到起始位置,收到跳转指令后复位;

四、上位机(基于MFC程序)

1、获得文件路径

void MyUpProgram::OnClickedButtouOpen() { // TODO: 在此添加控件通知处理程序代码 //CString gReadFilePathName; //CFileDialog fileDlg(true, _T("mp3"), _T("*.mp3"), OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, _T("mp3 Files (*.mp3)|*.mp3|wav File(*.wav)|*.wav|All File (*.*)|*.*||"), NULL); CFileDialog fileDlg(true, _T("bin"), _T("*.bin"), OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, _T("bin Files (*.bin)|*.BIN||"), NULL); if (fileDlg.DoModal() == IDOK) //弹出对话框 { gReadFilePathName = fileDlg.GetPathName();//得到完整的文件名和目录名拓展名 GetDlgItem(IDC_RICHEDIT_SHOW)->SetWindowText(gReadFilePathName);//将路径显示 CString filename = fileDlg.GetFileName(); (GetDlgItem(IDC_BUTTON_UP))->EnableWindow(TRUE); } }

效果:

2、打开文件

包含如下头文件:

#include <iostream> #include <fstream> #include <string> using namespace std;

函数介绍:

ifstream               ifs;

ifs.open(gReadFilePathName,ios::binary);//前面是文件完成路径,后面是以二进制形式打开

ifs.is_open()判定打开是否成功

ifs.seekg((wPage-1)* sizeof(chBuffer),ios::beg);//把光标设置为基于文件起始位置多少处读取数据

ifs.read((char*)chBuffer,sizeof(chBuffer));//读取数据

ifs.gcount()//实际读出的个数

ifs.close()//关闭打开的文件

效果:

 

最新回复(0)