STM32 H7系列学习笔记:使用CubeMX配置Bootloader应用程序的详解
cubemx版本6.11.1。基于U盘的bootloader
目录
一、bootloader
二、app
一、bootloader
使用cubeMX配置(这里就不描述MPU,时钟等配置了)主要是配置USBH_MSC并使用FatFs作为文件管理系统。
并且配置了硬件CRC校验
然后进入MDK中,其中g_JumpInit变量,包括跳转函数是参考安富莱 的程序的,请参考实战技能分享,一劳永逸的解决BOOT跳转APP失败问题,含MDK AC5,AC6和IAR_哔哩哔哩_bilibili
int main(void)
{
/* USER CODE BEGIN 1 */
if (g_JumpInit == 0xAA553344) /* 软件复位后再进入APP,提供一个干净的CPU环境给APP */
{
JumpToApp(); /* 去执行APP程序 */
}
else
{
/* 用户可以自己加处理 */
}
/* USER CODE END 1 */
/* MPU Configuration--------------------------------------------------------*/
MPU_Config();
/* Enable the CPU Cache */
/* Enable I-Cache---------------------------------------------------------*/
SCB_EnableICache();
/* Enable D-Cache---------------------------------------------------------*/
SCB_EnableDCache();
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_USART2_UART_Init();
MX_FATFS_Init();
MX_CRC_Init();
MX_USB_HOST_Init();
MX_TIM4_Init();
/* USER CODE BEGIN 2 */
bsp_InitTimer();
bootloader_event();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
MX_USB_HOST_Process();
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
主要是增加了bsp_InitTimer() 这个函数是软件定时器初始化,方便后面做计时。
bootloader_event()是主要的函数,下面的循环while(1)执行不到。
void bootloader_event(void)
{
printf("\nUSB enum...\n");
bsp_StartTimer(0,4000);
while(1)
{
MX_USB_HOST_Process();
if(Appli_state == APPLICATION_READY) break;
if(bsp_CheckTimer(0) == 1)
{
printf("U disk not attached");
g_JumpInit = 0xAA553344;
NVIC_SystemReset(); /* 复位CPU */
}
}
printf("USB enum success!\n");
while(1)
{
LoadFirmware();
BootHexCrcVeriy();
g_JumpInit = 0xAA553344;//此处请参考安富莱,一劳永逸bootloader跳转
NVIC_SystemReset(); /* 复位CPU */
}
}
bootloader_event()这个函数首先是进行usb的背景任务,如果经过4秒也没用u盘设备连接,就跳出bootloader进入app。连接之后,首先加载固件函数LoadFirmware();
static void LoadFirmware(void)
{
FRESULT result;
float FinishPecent;
uint32_t bw;
// char filepath[64];
uint32_t SectorCount = 0;
uint32_t SectorRemain = 0;
uint32_t i = 0;
uint8_t ucState;
uint32_t Count = 0;
uint32_t TotalSize = 0;
uint32_t ver;
/* 第1步:挂载文件系统 ***************************************************************/
result = f_mount(&fl, path, 1);
if (result != FR_OK)
{
printf("mount fail! res: %d\n", result);
}
/* 第2步:打开文件 ***************************************************************/
// sprintf(filepath, "%sapp.bin", path);
result = f_open(&fsrc, filepath, FA_OPEN_EXISTING | FA_READ);
if (result != FR_OK)
{
printf("Don't Find File : CL_firmware.bin\r\n");
g_JumpInit = 0xAA553344;
NVIC_SystemReset(); /* 复位CPU */
return;
}
/* 第2步:获取文件大小和APP固件版本 ***********************************************/
f_stat(filepath, &fno);
/* 打印文件大小, 最大4G */
printf("APP Size: %d\r\n", (int)fno.fsize);
uwAppSize = fno.fsize;
/* 读取固件大小 */
f_lseek(&fsrc, 28);
f_read(&fsrc, &ver, sizeof(ver), &bw);
f_lseek(&fsrc, 0);
printf("APP firmware version:V%X.%02X\r\n", ver >> 8, ver & 0xFF);
/* 第4步:扇区擦除 **************************************************************/
SectorCount = fno.fsize/(128*1024);
SectorRemain = fno.fsize%(128*1024);
for(i = 0; i < SectorCount; i++)
{
//printf("go to clear sector = %08x\r\n", AppAddr + i*128*1024);
bsp_EraseCpuFlash((uint32_t)(AppAddr + i*128*1024));
}
if(SectorRemain)
{
printf("App address = %08x\r\n", AppAddr + i*128*1024);
bsp_EraseCpuFlash((uint32_t)(AppAddr + i*128*1024));
}
/* 第5步:将APP固件写入到内部Flash ********************************************/
for(;;)
{
/* 读取一个扇区的数据到buf */
result = f_read(&fsrc, &tempbuf, sizeof(tempbuf), &bw);
/* 读取出错或者读取完毕,退出 */
if ((result != FR_OK)||bw == 0)
{
printf("APP load firmware done\r\n");
printf("=================================================\r\n");
break;
}
/* 编程内部Flash */
TotalSize += bw;
ucState = bsp_WriteCpuFlash((uint32_t)(AppAddr + Count*sizeof(tempbuf)), (uint8_t *)tempbuf, bw);
/* 如果返回非0,表示编程失败 */
if(ucState != 0)
{
printf("clear fail\r\n");
break;
}
/* 显示复制进度 */
Count = Count + 1;
FinishPecent = (float)(TotalSize) / fno.fsize;
printf("programing: %02d%%\r\n", (uint8_t)(FinishPecent*100));
}
/* 关闭文件*/
f_close(&fsrc);
/* 卸载文件系统 */
f_mount(NULL, path, 1);
}
这里几个变量path 以及filepath是我在文件中定义好的全局变量,因为一般对于升级文件都是固定的名字比如,“0:/app.bin”。此处AppAddr是根据自己情况来,比如app开始地址是0x0804 0000,那么AppAddr就是这个值(此时偏移量就是0x0004 0000,这个值需要记住,后面还要用)。
再是校验函数BootHexCrcVeriy()
static void BootHexCrcVeriy(void)
{
/* 读取bin文件的CRC */
uwExpectedCRCValue = *(__IO uint32_t *)(AppAddr + uwAppSize - 4);
/* 计算是否与硬件CRC一致 */
uwCRCValue = HAL_CRC_Calculate(&hcrc, (uint32_t *)(AppAddr), uwAppSize/4 - 1);
printf("Actual firmware crc = 0x%x\r\n", uwExpectedCRCValue);
printf("Calculated firmware crc = 0x%x\r\n", uwCRCValue);
if (uwCRCValue != uwExpectedCRCValue)
{
printf("Crc fail!\r\n");
uint32_t SectorCount = 0;
uint32_t SectorRemain = 0;
SectorCount = fno.fsize/(128*1024);
SectorRemain = fno.fsize%(128*1024);
int i;
for(i = 0; i < SectorCount; i++)
{
//printf("go to clear sector = %08x\r\n", AppAddr + i*128*1024);
bsp_EraseCpuFlash((uint32_t)(AppAddr + i*128*1024));
}
if(SectorRemain)
{
printf("App address = %08x\r\n", AppAddr + i*128*1024);
bsp_EraseCpuFlash((uint32_t)(AppAddr + i*128*1024));
}
Error_Handler();
}
else
{
FRESULT res;
res = f_mount(&fl, path, 1);
if (res != FR_OK)
{
printf("mount fail! res: %d\n", res);
}
else
{
printf("jump to APP\r\n");
res = f_rename(filepath,newname);
printf("result : %d",res);
/* 卸载文件系统 */
f_mount(NULL, path, 1);
}
}
printf("=================================================\r\n");
}
这里如果校验不通过重新擦写flash(其实这么做有点蠢。)。如果校验通过会更改文件名。
FatFs中,如果要更改文件名,该文件不可处于打开状态!
最后进行软件复位,在跳转到app。
二、app
在已有的app程序上,
在system_stm32h7xx.c中
#define USER_VECT_TAB_ADDRESS加入这句话。
将VECT_TAB_OFFSET设置为0x0004 0000。
如果自己写了分散加载文件
就设置成这样
如果不想自己修改分散文件,就修改target这边
关于app怎么生成bin文件请参考安富莱的视频教程
BSP视频教程第18期:基于NAND,eMMC,SD卡和U盘的BootLoader实战,带CRC完整性校验_哔哩哔哩_bilibili
到这基本上就结束了。
作者:做只小小jet