昨天的文章中介绍F4系列单片机的内部Flash读写,包括之前文章中介绍了FatFS文件系统读写U盘的操作。本篇文章就是将两者结合,实现F4系列单片机程序的U盘升级。
首先对内部Flash空间进行划分,前128K用于存储BootLoader程序,后面的空间用于存储App程序。定义如下:
#define IAP_SIZE ((uint32_t)0x20000) /* 128Kbytes as IAP size */
#define APPLICATIONADDRESS ((uint32_t)0x08020000) /* User start code space */
#define APPLICATIONSIZE ((uint32_t)0xE0000)
之所以分配了128K给BootLoader程序,是因为这部分程序中移植了FatFs文件系统和emWin程序,程序比较大。当然emWin程序不是必须的,这里只是通过液晶屏来显示升级进度等信息。
上电后先运行BootLoader程序,可以通过多种方式来判断是否需要进行程序升级,比如标志位、按键、触摸屏状态等。如果需要程序升级,则读取U盘中的.bin文件,然后写入到APPLICATIONADDRESS地址。如果不需要升级,则直接跳转到App程序运行。
程序升级部分代码如下:
int writeNewProgram()
{
int rtn;
int i;
int isFinish = 0;
FIL fil;
int sectorFlag[12];//用于标记一下此sect是否已经擦除
for( i=0; i<12; i++ )
sectorFlag[i] = 0;
GUI_DispStringAt("start write new program", 0, y);
y+=16;
HAL_FLASH_Unlock();//解锁flash
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR);
rtn = f_open(&fil, filename, FA_READ );//打开程序文件
if(rtn)
{
sprintf(str,"open %s error:%d\r\n",filename,rtn);
GUI_DispStringAt(str, 0, y);
y+=16;
}
else
{
sprintf(str,"open %s sucess\r\n",filename);
GUI_DispStringAt(str, 0, y);
y+=16;
}
uint32_t startaddress = APPLICATIONADDRESS;
uint32_t endaddress = APPLICATIONADDRESS + APPLICATIONSIZE;
GUI_DispStringAt("start program", 0, y);
y+=16;
while( startaddress < endaddress && isFinish == 0 )
{
rtn = f_read(&fil, buf, BUF_SIZE, (UINT*)&bytesread);//读取128K数据
if(rtn)
{
sprintf(str,"read address 0x%X error:%d",startaddress,rtn);
GUI_DispStringAt(str, 0, y);
break;
}
else
{
sprintf(str,"read address 0x%X sucess! rbytes=0x%X",startaddress,bytesread);
GUI_DispStringAt(str, 0, y);
}
y+=16;
for( i=0; i<bytesread; i+=4 )
{
int sect = GetSectorFromAddress(startaddress);
if( sect >= 0 )
{
if( sectorFlag[sect] == 0 )//0表示此sect还没有被擦除
{
sectorFlag[sect] = 1;
FLASH_Erase_Sector(sect,FLASH_VOLTAGE_RANGE_3);
sprintf(str,"erase sector %d",sect);
GUI_DispStringAt(str, 0, y);
}
}
uint32_t *p;
p = (uint32_t *)&buf[i];
rtn = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,startaddress,*p);//写入Flash
if( rtn != 0 )
{
isFinish = 1;
sprintf(str,"HAL_FLASH_Program Err:%X",startaddress);
GUI_DispStringAt(str, 0, y);
break;
}
startaddress +=4;
}
if( bytesread < BUF_SIZE )
{
isFinish = 1;
}
}
y+=16;
rtn = f_close(&fil);
if(rtn)
{
sprintf(str,"close %s error:%d",filename,rtn);
GUI_DispStringAt(str, 0, y);
y+=16;
}
else
{
sprintf(str,"close %s sucess",filename);
GUI_DispStringAt(str, 0, y);
y+=16;
}
HAL_FLASH_Lock();
GUI_DispStringAt("end write program", 0, y);
y+=16;
return isFinish;
}
程序流程如下:解锁Flash后,打开U盘中的程序文件,然后每次读取128K数据(128K正好是一个Sector的大小,F429单片机RAM也足够大,所以可以每次读取较多的数据,效率更高),然后擦除Sector,再写入。直到所有数据写入完成再对Flash上锁。之后再跳转到App程序运行即可。
跳转部分代码如下:
typedef void (*pFunction)(void);
pFunction Jump_To_Application;
uint32_t JumpAddress;
void jumpToApp()
{
if (((*(__IO uint32_t*)APPLICATIONADDRESS) & 0x2FFE0000 ) == 0x20000000)
{
/* Jump to user application */
JumpAddress = *(__IO uint32_t*) (APPLICATIONADDRESS + 4);
Jump_To_Application = (pFunction) JumpAddress;
/* Initialize user application's Stack Pointer */
__set_MSP(*(__IO uint32_t*) APPLICATIONADDRESS);
Jump_To_Application();
}
}
实际运行效果:
视频中升级之前对单片机内的程序进行了备份,再进行写入。可以看到,500多KB的程序升级用了几秒钟,速度还是可以的。
总结
以上介绍的内容只是实现了基本的程序升级功能,实际应用中可能还需要对数据进行校验、加密等操作。
推荐阅读:
欢迎关注公众号"嵌入式技术开发",大家可以后台给我留言沟通交流。如果觉得该公众号对你有所帮助,也欢迎推荐分享给其他人。