STM32CUBEMX_自研BOOT升级程序

STM32CUBEMX_自研BOOT升级程序

前言:
这件事情源自公司一个产品已经开发完成并对外售卖了,只预留了一个USB口用于给单片机升级,但是有个比较坑的点就是,没有预留触发升级的按钮(使用USB插入时产生的5V也可以做一个电平指示),不改硬件的情况下只能通过软件想办法,在APP程序中操作片flash区域,写一个标志位,然后设备重启后会读flash的标志位,从而启动进入DFU模式进行升级,思路是这么个思路,但是不好好考虑其中的逻辑,设备很容易变成砖,最终只能返厂维修了,踩坑过程细节不讲了,直接上经过实践的代码。

1、boot程序配置:

STM32CUBEMX_自研BOOT升级程序_第1张图片
STM32CUBEMX_自研BOOT升级程序_第2张图片
STM32CUBEMX_自研BOOT升级程序_第3张图片
STM32CUBEMX_自研BOOT升级程序_第4张图片
注意:
USBD_DFU_APP_DEFAULT_ADD:设置为0x08004000,表示boot跳转的APP程序地址,也限定了boot程序不能超过16Kb
USBD_DFU_MEDIA Interface:设置为@Internal Flash /0x08000000/16001Ka,48001Kg,表示前16K给boot程序,后48K给APP程序
STM32CUBEMX_自研BOOT升级程序_第5张图片
配置完成就可以生成工程了,然后我们稍作修改就可以了

2、架构框图

STM32CUBEMX_自研BOOT升级程序_第6张图片

3、boot程序源码的逻辑展示
#include "main.h"
#include "usb_device.h"
#include "gpio.h"
#include "usbd_dfu.h"
#include "usbd_ctlreq.h"

typedef  void (*pFunction)(void);

void SystemClock_Config(void);

extern USBD_HandleTypeDef hUsbDeviceFS;
#define FLASH_UPDATE_ADDR_x  0x0800F800	//STM32F103C8t6的flash空间中0x0800F800地址比较靠后一般使用不到

//读数据
unsigned short My_FLASH_R(unsigned int add)	//参数1:32位flash地址,返回值:16位数据
{
	unsigned short a;
	a = *(unsigned short*)(add);			//从指定页的addr地址开始读
	return a;
}

//写入数据
void My_FLASH_W(unsigned int add,unsigned short dat)		//参数1:32位flash地址,参数2:16位数据
{
	//RCC_HSICmd(ENABLE);									//打开HSI时钟
	HAL_FLASH_Unlock();  									//解锁flash编程擦除控制器
	FLASH_EraseInitTypeDef My_Flash;						//声明结构体
	My_Flash.TypeErase = FLASH_TYPEERASE_PAGES;  			//只做擦除操作
	My_Flash.PageAddress = add;  							//要擦除的地址
	My_Flash.NbPages = 1;                        			//要擦除的页数
				
	uint32_t PageError = 0;                    				//出现错误,则会保存出错的地址
	HAL_FLASHEx_Erase(&My_Flash, &PageError);  				//擦除flash
	
	HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, add, dat);//对flash进行写入
	HAL_FLASH_Lock();    									//锁定flash编程擦除控制器
}

//FLASH擦除
void My_FLASH_C(unsigned int add)
{
	//RCC_HSICmd(ENABLE); 									//打开HSI时钟
	HAL_FLASH_Unlock();  									//解锁flash编程擦除控制器
	FLASH_EraseInitTypeDef My_Flash;						//声明结构体
	My_Flash.TypeErase = FLASH_TYPEERASE_PAGES;  			//只做擦除操作
	My_Flash.PageAddress = add;  							//要擦除的地址
	My_Flash.NbPages = 1;                        			//要擦除的页数
				
	uint32_t PageError = 0;                    				//出现错误,则会保存出错的地址
	HAL_FLASHEx_Erase(&My_Flash, &PageError);  				//擦除flash
	
	HAL_FLASH_Lock();    									//锁定flash编程擦除控制器
}

int main(void)
{
	pFunction JumpToApplication;
	uint32_t JumpAddress;

	HAL_Init();

	SystemClock_Config();

	unsigned short update_flag = My_FLASH_R(FLASH_UPDATE_ADDR_x);
	if(update_flag != 0x5555)  //检测到升级标志位
	{
		/* Test if user code is programmed starting from address 0x08007000 */
//		if (((*(__IO uint32_t *) USBD_DFU_APP_DEFAULT_ADD) & 0x2FFFB000) == 0x20000000)
//		{
			/* Jump to user application */
			JumpAddress = *(__IO uint32_t *) (USBD_DFU_APP_DEFAULT_ADD + 4);//跳转APP程序
			JumpToApplication = (pFunction) JumpAddress;

			/* Initialize user application's Stack Pointer */
			__set_MSP(*(__IO uint32_t *) USBD_DFU_APP_DEFAULT_ADD);
			JumpToApplication();
//		}
	}

  MX_USB_DEVICE_Init();//进入DFU模式

  while (1)
  {
  		//可以接入一个指示灯来指示正在DUF模式状态,还是已经退出DFU状态
		HAL_Delay(30000);			//进入DFU模式必须在30S之内升级DFU完毕,30S之后不管有没有升级完毕都会去跳转APP
		USBD_Stop(&hUsbDeviceFS);	//关闭USB

//升级完成后自动跳转到APP开始运行
//		/* Test if user code is programmed starting from address 0x08007000 */
//		if (((*(__IO uint32_t *) USBD_DFU_APP_DEFAULT_ADD) & 0x2FFFB000) == 0x20000000)//跳转APP程序
//		{
			/* Jump to user application */
			JumpAddress = *(__IO uint32_t *) (USBD_DFU_APP_DEFAULT_ADD + 4);
			JumpToApplication = (pFunction) JumpAddress;

			/* Initialize user application's Stack Pointer */
			__set_MSP(*(__IO uint32_t *) USBD_DFU_APP_DEFAULT_ADD);
			JumpToApplication();
//		}
  }
}

4、APP程序源码逻辑


/*************************标准库关于FLASH的操作*******************************
//FLASH写入函数
void FLASH_C(u32 add)//参数1:32位flash地址,参数2:16位数据
{
	 RCC_HSICmd(ENABLE); 																	//打开HSI时钟
	 FLASH_Unlock();  																		//解锁flash编程擦除控制器
     FLASH_ClearFlag(FLASH_FLAG_BSY|FLASH_FLAG_EOP|FLASH_FLAG_PGERR|FLASH_FLAG_WRPRTERR);	//清除标志位
     FLASH_ErasePage(add);     																//擦除flash
     FLASH_ClearFlag(FLASH_FLAG_BSY|FLASH_FLAG_EOP|FLASH_FLAG_PGERR|FLASH_FLAG_WRPRTERR);	//清除标志位
     FLASH_Lock();    																		//锁定flash编程擦除控制器
}

//FLASH写入函数
void FLASH_W(u32 add,u16 dat)//参数1:32位flash地址,参数2:16位数据
{
	RCC_HSICmd(ENABLE); 																	//打开HSI时钟
	FLASH_Unlock();  																		//解锁flash编程擦除控制器
	FLASH_ClearFlag(FLASH_FLAG_BSY|FLASH_FLAG_EOP|FLASH_FLAG_PGERR|FLASH_FLAG_WRPRTERR);	//清除标志位
	FLASH_ErasePage(add);     																//擦除flash
	FLASH_ProgramHalfWord(add,dat); 														//从指定页的地址开始写
	FLASH_ClearFlag(FLASH_FLAG_BSY|FLASH_FLAG_EOP|FLASH_FLAG_PGERR|FLASH_FLAG_WRPRTERR);	//清除标志位
	FLASH_Lock();    																		//锁定flash编程擦除控制器
}

//FLASH读出数据
u16 FLASH_R(u32 add)//参数1:32位flash地址
{
	u16 a;
    a = *(u16*)(add);
	return a;
}
*************************标准库关于FLASH的操作*******************************/

/*************************HAL库关于FLASH的操作*******************************
#define FLASH_UPDATE_ADDR_x  0x0800F800	//STM32F103C8t6的flash空间中0x0800F800地址比较靠后一般使用不到

//读数据
unsigned short My_FLASH_R(unsigned int add)	//参数1:32位flash地址,返回值:16位数据
{
	unsigned short a;
	a = *(unsigned short*)(add);			//从指定页的addr地址开始读
	return a;
}

//写入数据
void My_FLASH_W(unsigned int add,unsigned short dat)		//参数1:32位flash地址,参数2:16位数据
{
	//RCC_HSICmd(ENABLE);									//打开HSI时钟
	HAL_FLASH_Unlock();  									//解锁flash编程擦除控制器
	FLASH_EraseInitTypeDef My_Flash;						//声明结构体
	My_Flash.TypeErase = FLASH_TYPEERASE_PAGES;  			//只做擦除操作
	My_Flash.PageAddress = add;  							//要擦除的地址
	My_Flash.NbPages = 1;                        			//要擦除的页数
				
	uint32_t PageError = 0;                    				//出现错误,则会保存出错的地址
	HAL_FLASHEx_Erase(&My_Flash, &PageError);  				//擦除flash
	
	HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, add, dat);//对flash进行写入
	HAL_FLASH_Lock();    									//锁定flash编程擦除控制器
}

//FLASH擦除
void My_FLASH_C(unsigned int add)
{
	//RCC_HSICmd(ENABLE); 									//打开HSI时钟
	HAL_FLASH_Unlock();  									//解锁flash编程擦除控制器
	FLASH_EraseInitTypeDef My_Flash;						//声明结构体
	My_Flash.TypeErase = FLASH_TYPEERASE_PAGES;  			//只做擦除操作
	My_Flash.PageAddress = add;  							//要擦除的地址
	My_Flash.NbPages = 1;                        			//要擦除的页数
				
	uint32_t PageError = 0;                    				//出现错误,则会保存出错的地址
	HAL_FLASHEx_Erase(&My_Flash, &PageError);  				//擦除flash
	
	HAL_FLASH_Lock();    									//锁定flash编程擦除控制器
}
*************************HAL库关于FLASH的操作*******************************/

void usb_recv(void)//USB数据解析
{
	//解析USB的指令数据
	//如果指令是控制升级的,就把该标志位置1
	DFU_STM32_FLAG = 1;
}

void update_stm32(void)//置标志位
{
	if(DFU_STM32_FLAG)
	{
		DFU_STM32_FLAG = 0;
		FLASH_W(0x0800F800,0x5555);
		if(FLASH_R(0x0800F800) == 0x5555)
		{
			USB_SendACK();//回复上位机
		}
	}
}

int main(void)
{	 
	SCB->VTOR = FLASH_BASE | 0x4000;

	if(FLASH_R(0x0800F800) == 0x5555)
	{
		delayms(20);
		FLASH_C(0x0800F800);
	}
	while (1)
	{
		system_run();//APP的系统程序
		delay(50);
		update_stm32();
	}
}

你可能感兴趣的:(STMCUBE_IDE,stm32,嵌入式硬件,单片机)