STM32CUBEMX_自研BOOT升级程序
前言:
这件事情源自公司一个产品已经开发完成并对外售卖了,只预留了一个USB口用于给单片机升级,但是有个比较坑的点就是,没有预留触发升级的按钮(使用USB插入时产生的5V也可以做一个电平指示),不改硬件的情况下只能通过软件想办法,在APP程序中操作片flash区域,写一个标志位,然后设备重启后会读flash的标志位,从而启动进入DFU模式进行升级,思路是这么个思路,但是不好好考虑其中的逻辑,设备很容易变成砖,最终只能返厂维修了,踩坑过程细节不讲了,直接上经过实践的代码。
1、boot程序配置:
注意:
USBD_DFU_APP_DEFAULT_ADD:设置为0x08004000,表示boot跳转的APP程序地址,也限定了boot程序不能超过16Kb
USBD_DFU_MEDIA Interface:设置为@Internal Flash /0x08000000/16001Ka,48001Kg,表示前16K给boot程序,后48K给APP程序
配置完成就可以生成工程了,然后我们稍作修改就可以了
2、架构框图
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();
}
}