昨天的文章中介绍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的程序升级用了几秒钟,速度还是可以的。
总结
以上介绍的内容只是实现了基本的程序升级功能,实际应用中可能还需要对数据进行校验、加密等操作。
推荐阅读:
欢迎关注公众号"嵌入式技术开发",大家可以后台给我留言沟通交流。如果觉得该公众号对你有所帮助,也欢迎推荐分享给其他人。
转载:https://blog.csdn.net/zhang062061/article/details/114317576