【stm32多种系列bootloader项目纪实】

(工作纪实,有想法多交流!谢谢)

一、项目背景

公司用到了stm32g030c8t6、stm32g030f6p6和stm32f407,原本想着直接在网上找好现成的能用的就行,但是又说后面可能要用stm32g4,那还是自己写一个更利于后期拓展,而且要是后期实际使用中出了什么问题便于第一时间维护,自己懂才是最好的。

二、项目分析

对于bootloader这一块其实原理很简单:
①先写一个bootloader下载到flash,用于接收上位机的bin数据写入到指定的app地址后跳转。
②做一个上位机,这里我先用labview做了一个串口通讯bootloader。

三、项目详细过程

1、因为上位机只是个串口通讯主要发送bin文件,所以这里就不详细记录了,只是测试阶段测试bootloader功能是否正常的,本身也没做太复杂,要等后面看同事们具体讨论全部的上位机功能后再认真做一下,其实太懒的话你用串口助手都行sscom。

2、芯片的flash分区为bootloader+app,也就是两个工程文件,这一块概念网上讲的大佬很多,去找一个看看就懂了。重要的有一点,因为是两个工程,从bootloader区跳转到app区起始地址,所以要在app的工程中配置正确的起始地址和中断向量偏移(测试项目中用的g030,app起始地址从0x08003000开始写入)
①配置app的起始地址,在魔术棒里,因为我用的是f6p6太小了只有32Kflash所以我给app留的大小是0x5000(20K)
在这里插入图片描述
②还需要配置下中断向量偏移,一般是在system_stm32g0xx.c文件中,直接用cubemx生成的文件中是没有这个宏定义的,加上宏定义后改下offset就好了在这里插入图片描述

3、弄好了上位机和APP后开始写bootloader,首先用stm32cubemx配置了stm32g030f6p6,只包含485和一个gpio(指定一个led常亮证明现在是处于bootloader代码区)。在这里插入图片描述
从bootloader的代码部分出现了各种问题:
①在网上抄了现成的跳转app的代码,每一条都抄的很有原理就是不能跳转;
②简单的bootloader无非就是接收bin、写入flash和跳转app,但是用的HAL_FLASH_Program写入异常。

四、问题解决

解决问题一:为什么app不能成功跳转?
明明就是在网上找到的访问量还是很高的例程,但是为什么抄过来jumpapp就是不能成功跳转呢?后面发现对于红色方框圈起来的这个if判断条件,网上大佬们写的有这样的:if((((__IO uint32_t)ulAddr_App) & 0x2FFE0000) == 0x20000000)
还有这样的:if(((__IO uint32_t)ulAddr_App) == 0x20002000)无奈的是都无法成功跳转,因为我确实第一次见这个“东西”根本不知道这个代表的具体含义,但因为是上班又不想因为研究这个东西耽误时间。好在咱们还有debug这个神器,单步走一走发现这个(*(__IO uint32_t*) FLASH_APP_ADDR)指向的栈顶地址对于不同内存大小的app是不同的,总归是不会大于ram的最大值(具体最大值多少看具体用的哪款芯片),这里做下判断是好的,防止乱跑,到这里这个问题就算解决了可以成功跳转了。在这里插入图片描述解决问题二:不同的芯片用的都是HAL库HAL_FLASH_Program,g030能写flash,f407不能写?
其实这里才是整个bootloader项目不同芯片的不同之处,回想一下第一步做的上位机串口通讯发送bin文件,与什么芯片无关。第二步配置app起始地址和中断向量偏移,也完全一样,可能就是不同芯片定义的文件位置不同。第三步,编写bootloader的流程图,判断、接收、处理和跳转,我最初以为不同芯片都是HAL_FLASH_Program,乍一眼没啥不同,所以不同芯片bootloader的不同之处应该就是stm32cubemx配置的那些底层文件的不同,但是等我开始着手写入flash准备看成果时,写不进去HAL_FLASH_Program返回错误。
在这里插入图片描述
也是这里我测试了好久,我们可以将flash的操作分为:擦除、读取、写入。其中读取最简单,我们想要获取哪个地址的内存数据只需要返回哪个地址指针就好了,用debug都可以直接访问内存的
在这里插入图片描述
那发现了这个擦除和写入不同芯片是存在差别的,这里搞通了以后着实给我爽了一下,完整的stmflash_write函数应该包括:
①判断写入地址是否合理(比如我是从0x08003000开始写入app,其实这里还要加一个是否大于最大值的判断)

if(WriteAddr < FLASH_DATA_ADDR_MIN || WriteAddr % 4)  return;

②解锁

HAL_FLASH_Unlock(); 

③擦除

if(STMFLASH_ReadWord(addrx)!=0XFFFFFFFF)	//有非0XFFFFFFFF的地方,要擦除这个扇区
			{   
				FlashEraseInit.TypeErase = FLASH_TYPEERASE_PAGES;    //擦除类型,页擦除 
				FlashEraseInit.Page = STM32G0_GetFlashSector(addrx);      //从哪页开始擦除
				FlashEraseInit.NbPages = 1;                          //一次只擦除一页
				//SEGGER_RTT_printf(0,"Flash Erase page is %d\r\n",FlashEraseInit.Page);
				if(HAL_FLASHEx_Erase(&FlashEraseInit, &PageError) != HAL_OK) 
				{
					//SEGGER_RTT_printf(0,"Flash Erase err\r\n");
					break;      //发生错误了	
				}
				FLASH_WaitForLastOperation(FLASH_WAITETIME);            //等待上次操作完成
			}else {
				addrx += 4;
			}

这里不同芯片的不同之处在哪呢?找下这个FlashEraseInit结构体就可以知道,这个是g030f6p6的结构体
在这里插入图片描述
这个是f407的结构体,名字不一样而已,原理相同,所以这里就不同芯片记得在hal_flash_ex.h里面看一下,这是第一个不同之处
在这里插入图片描述
④写入

while(WriteAddr < endaddr)									//写数据,这里通过while循环实现从开始地址到结束地址的flash写入,每一次循环固定双字8字节写入,若一次写入一页那就是需要2048即
			//2048/8=256次,总结就是调用一次就擦一页写一页,就算后面app代码很大有20K那也就循环操作个10页,麻烦但难度不大
		{
			if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, WriteAddr, *(uint64_t*)pBuffer) != HAL_OK)//写入数据,字WORD是32bit,双字DOUBLEWORD是64bit
			{
				break;												//写入异常
			}
			WriteAddr += 8;     //地址加8
			pBuffer += 1;       //buff传进来的是64位的,所以这里+1 便是8个字节
		}  

这里开始真没想太多因为g030和f407这个函数定义的参数是一样的,在g030我就直接把接收的bin数据合并成64bit一组一组写入flash一切正常,但是我用同样的代码在f407上合并64bit就写不进去,这里咱们知道是有四种类型写入的,字节8bit、半字16bit,字32bit和双字64bit。DOUBLEWORD写不进去我就试试WORD,然后发现在f407上32bit的数据可以正常写入,到这里就算bootloader从g030到f407移植成功了,具体为什么64bit写不正常我没细研究先交任务完成绩效,后面再搞。

HAL_StatusTypeDef  HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint64_t Data);

⑤上锁

HAL_FLASH_Lock(); 

多系列bootloader初期功能任务完成,撒花!!!

  • 21
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值