bootloader编写——MCU固件升级系列2(STM32)

本系列将从升级流程、boot代码编写、APP代码编写以及固件打包来介绍,硬件选用STM32F407ZGT6(手里只有),来完成这系列教程。

前言

开发STM32固件升级并编写Bootloader时,需要注意以下几个关键点:

  1. 熟悉硬件和数据手册:在开发过程中,确保充分理解STM32微控制器的特性和功能。阅读相关数据手册,了解其内存布局、外设接口以及其他重要信息。

  2. 选择合适的通信接口:根据项目需求选择合适的通信接口进行固件升级,如串口、I2C、SPI、USB等。确保所选接口可以与外部设备(如PC)正常通信。(后续会使用CAN UART)

  3. 定义固件升级协议:设计一个简单且可靠的通信协议,用于在Bootloader和外部设备之间传输数据。协议应包括命令、地址、数据长度、数据包校验等信息。

  4. 保留足够的Bootloader空间:为Bootloader预留足够的程序存储空间。Bootloader的大小可能会随着功能的增加而增大,因此预留一定的余量非常重要。

  5. 安全和鲁棒性:确保Bootloader代码具有良好的异常处理和错误检测能力。避免因意外情况导致的设备损坏或不可恢复状态。

  6. 可扩展性:在设计Bootloader时考虑到未来可能的功能扩展。保持代码结构清晰,易于维护和升级。

  7. 测试与验证:在实际硬件上对Bootloader进行充分测试,确保其下载、擦除、写入等操作的正确性和稳定性。

遵循以上关键点,在开发过程中保持耐心和细致,能有效编写出一个可靠、高效的STM32 Bootloader。

编写Bootloader程序

在编写前我需要确定几个地方:

  • bootloader

    • 确定bootloader存放地址 0x08000000
    • 配置bootloader中断向量表
    • 实现串口或USB等通信接口 UART
    • 编写flash擦除、编程函数
    • 确定应用程序存放地址 0x08000000 + Boot_size + PARAM_SIZE
    • 跳转到应用程序入口 跳转指令
  • 注意事项

    • 确认芯片型号和数据手册,了解芯片的Flash大小和布局
    • 确定应用程序和bootloader的存放地址
    • 确定bootloader的触发方式,如按键触发、超时触发等
    • bootloader需要配置中断向量表,以便跳转到应用程序时正确执行
    • bootloader需要实现串口或USB等通信接口,以便与上位机进行通信
    • bootloader需要编写flash擦除、编程函数,以便将应用程序下载到Flash中
    • bootloader需要检查应用程序的合法性,如校验和、签名等
    • bootloader需要跳转到应用程序入口,启动应用程序

在MCU中,bootloader主要作用引导进入APP1程序,检测升级标注位是否需要将备份APP2覆盖到APP1中(升级新程序)或者接收升级包进行升级(可以是CAN、UART通信或者读取SD卡获取升级包)。bootloader程序尽量保持简洁,不需要用到资源统统去掉(很容易出现问题,往往初始化的外设资源,最好都去初始化(保留原来的状态)),保证小巧、稳定和扩展性。
需要实现的功能:flash擦除接口和通信接口,主要这两个,还需要添加状态和标注位。

Flash读写和擦除

Flash 功能接口

u32 STMFLASH_ReadWord(u32 faddr)
{
	return *(vu32*)faddr; 
}  

uint16_t STMFLASH_GetFlashSector(u32 addr)
{
	if(addr<ADDR_FLASH_SECTOR_1)return FLASH_Sector_0;
	else if(addr<ADDR_FLASH_SECTOR_2)return FLASH_Sector_1;
	else if(addr<ADDR_FLASH_SECTOR_3)return FLASH_Sector_2;
	else if(addr<ADDR_FLASH_SECTOR_4)return FLASH_Sector_3;
	else if(addr<ADDR_FLASH_SECTOR_5)return FLASH_Sector_4;
	else if(addr<ADDR_FLASH_SECTOR_6)return FLASH_Sector_5;
	else if(addr<ADDR_FLASH_SECTOR_7)return FLASH_Sector_6;
	else if(addr<ADDR_FLASH_SECTOR_8)return FLASH_Sector_7;
	else if(addr<ADDR_FLASH_SECTOR_9)return FLASH_Sector_8;
	else if(addr<ADDR_FLASH_SECTOR_10)return FLASH_Sector_9;
	else if(addr<ADDR_FLASH_SECTOR_11)return FLASH_Sector_10; 
	return FLASH_Sector_11;	
}

void STMFLASH_Write(u32 WriteAddr,u32 *pBuffer,u32 NumToWrite)	
{ 
  FLASH_Status status = FLASH_COMPLETE;
	u32 addrx=0;
	u32 endaddr=0;	
  if(WriteAddr<STM32_FLASH_BASE||WriteAddr%4)return;	//非法地址
	FLASH_Unlock();									//解锁 
  FLASH_DataCacheCmd(DISABLE);//FLASH擦除期间,必须禁止数据缓存
 		
	addrx=WriteAddr;				//写入的起始地址
	endaddr=WriteAddr+NumToWrite*4;	//写入的结束地址
	if(status==FLASH_COMPLETE)
	{
		while(WriteAddr<endaddr)//写数据
		{
			if(FLASH_ProgramWord(WriteAddr,*pBuffer)!=FLASH_COMPLETE)//写入数据
			{ 
				break;	//写入异常
			}
			WriteAddr+=4;
			pBuffer++;
		} 
	}
  FLASH_DataCacheCmd(ENABLE);	//FLASH擦除结束,开启数据缓存
	FLASH_Lock();//上锁
} 

void STMFLASH_Read(u32 ReadAddr,u32 *pBuffer,u32 NumToRead)   	
{
	u32 i;
	for(i=0;i<NumToRead;i++)
	{
		pBuffer[i]=STMFLASH_ReadWord(ReadAddr);//读取4个字节.
		ReadAddr+=4;//偏移4个字节.	
	}
}

实现在SD卡寻找APP程序,进行升级。

	FIL fnew;                         /* 文件对象 */
	FRESULT res_sd = FR_OK;                   /* 文件操作结果 */
	UINT fnum;                        /* 文件成功读写数量 */
	BYTE ReadBuffer[512]={0};        /* 读缓冲区 */
	
	u8 checknum = 0;
	int writeSum = 0;
 	while(SD_Init())
	{

	}
 	exfuns_init();									 
  f_mount(fs[0],"0:",1); 					
	Flash_Erase(APP1_ADDRESS);
	while(1)
	{
		t++; 
		res_sd = f_open(&fnew, "0:APP2.bin", FA_OPEN_EXISTING | FA_READ);
		if(res_sd == FR_OK)//有升级文件
		{
			printf("open bootloader\r\n");
			fnum = 1;
			do
			{
				res_sd = f_read(&fnew, ReadBuffer, sizeof(ReadBuffer), &fnum);
				if(res_sd == FR_OK)
				{
						printf("##");
						if(fnum!=0)
						STMFLASH_Write(APP1_ADDRESS+writeSum,(u32*)ReadBuffer,fnum/4);
						writeSum += fnum;
						for(int i = 0 ; i < fnum;i++)
						{
							checknum ^= ReadBuffer[i];
							printf("0x%2x ",ReadBuffer[i]);
						}
						printf("\r\n");
						memset(ReadBuffer,0,sizeof(ReadBuffer));
				}
				else
				{
						
						printf("read file failth(%d)\n", res_sd);
				}
			}while(fnum > 0);
			/* 不再读,关闭文件 */
        f_close(&fnew);
				u8 readBuf[4];
				u8 readCheckCrc = 0;
				for(int i = 0;i < writeSum ; i++)
				{
					STMFLASH_Read(APP1_ADDRESS+i*4,(u32*)readBuf,1);
					for(int j = 0;j<4;j++)
					readCheckCrc ^= readBuf[j];
				}
				printf("boot jump app\r\n");
				if(readCheckCrc == checknum && readCheckCrc!=0)
				{
					printf("check sum success jump APP1\r\n");
					//跳转
					jump_to_app();
				}
				else
				{
					printf("check sum Failth\r\n");
				}
				while(1);
				
		}
		else
		{
			printf("not found bootloader \r\n");
		}
		LED0=!LED0;  //状态灯  bootloader状态
	}

上面还需要进行优化,需要添加一些标志位可以在一个扇区或者在备份寄存器标记,来做升级标记。

跳转指令 重点

#define  APP1_ADDRESS  0x8020000
__asm void start_app(uint32_t r0_msp, uint32_t r1_pc)
{
    MOV SP, R0    //R0的数值   其实就是参数r0_msp
    BX R1             //R1               其实就是r1_pc
}
void jump_to_app(void)
{
	start_app((*(uint32_t *)(APP1_ADDRESS)),(*(uint32_t *)(APP1_ADDRESS + 4)));
}

在实际操作过程中,遇到跳转,没能正常运行APP。
我进行分析,排除问题,把问题范围缩小,可能会出现的问题:1、Flash读写接口有问题 2、FAT32文件系统读取数据有问题。
看前面的代码,我都加了日志信息输出,把文件系统读到的数据输出,和真实的bin文件内容对比,对比问题是正确,说明问题出现在Flash擦除。
看下面的截图,左边是都是FF没做写入前做擦除,右边是擦除后再写,是写入成功的。
写入前要先做擦除处理。
文章来源地址https://uudwc.com/A/vmZN

原文地址:https://blog.csdn.net/weixin_43746325/article/details/130460161

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请联系站长进行投诉反馈,一经查实,立即删除!

h
上一篇 2023年06月13日 02:28
第12章 STM32+BH1750光照传感器+OLED模块显示环境光照强度
下一篇 2023年06月13日 02:28