利用STM32CubeMX软件生成USB_DEVICE_DFU升级程序

一、测试平台:
MCU:STM32F429NIH6
工具:STM32CubeMX软件
编译软件:MDK

二、配置步骤
(1).打开STM32CubeMX软件,创建新的工程文件,先生成一个的串口的收发例程,需要实现将串口收到的数据发送的出来,生成串口的例程这里不再详细介绍。
(2).配置USB_OTG_FS,由于电路板上USB管脚连接的是PA11和PA12,所以这里配置USB_OTG_FS,Mode项配置Device Only,同时开启中断。
在这里插入图片描述
(3).配置USB_DEVICE,Class For FS IP项选择 Download Firmware Update Class(DFU) ,USBD_DFU_XFER_SIZE代表每次传输的最大字节数,USBD_DFU_APP_DEFAULT_ADD代表升级程序的起始地址。

在这里插入图片描述
USBD_DFU_APP_DEFAULT_ADD设置
此处设置的值应为你升级下载存放的起始地址,对于字段中APP字眼切勿认为需要填写APP程序的起始地址,其实它的作用就是触发DFU传输升级时告诉你应该擦除哪片区域地址,因为大多数DFU功能都在应用在Bootloader工程中,相对于Bootloader,在运行DFU升级,应该简单直接(直接擦除或写入APP区域地址的数据,而不是在APP中运行DFU升级,不能直接擦除APP自身代码,而是中转一下,下载到下载区或者其他不干涉APP程序代码的区域)。

USBD_DFU_MEDIA Interface
描述的含义:0x08000000 为起始地址。“a”代表的是Read-only,“g”代表Read/Write/Erase。也就是说,“a”所指明的区域应该为Bootloader 的空间不可擦除或者修改,“g”所指明的区别为用户代码空间。大小由前面的数字决定,乘号“”前面的为Sector 的个数,后面的为Sector 的大小,这里的意思就是从0x08000000 开始,前面3 个Sector(每个Sector 为16k 字节)为Read-only,后面1 个Sector(每个Sector 为16k 字节)为Read/Write/Erase,等等。
例如:
“@Internal Flash /0x08000000/120
128Ba,1416*128Bg”
含义:
从0x08000000 开始,前面120 个Sector(每个Sector 为128 字节)为Read-only,后
面1416 个Sector(每个Sector 为128 字节)为Read/Write/Erase。

@Internal Flash /0x08000000/03016Ka,01016Kg,01064Kg,07128Kg,04016Kg,01064Kg,07128Kg
含义:从0x08000000 开始,
03
016Ka代表3个16k字节的Read-only区域。
01016Kg代表1个16k字节的Read/Write/Erase。
01
064Kg代表1个64k字节的Read/Write/Erase。
07128Kg代表7个128k字节的Read/Write/Erase。
04
016Kg代表4个16k字节的Read/Write/Erase。
01064Kg代表1个64k字节的Read/Write/Erase。
07
128Kg代表7个128k字节的Read/Write/Erase。

(4).在CubeMX配置完成后,完善以下接口,位于usbd_dfu_if.c
uint16_t MEM_If_Init_FS(void)
uint16_t MEM_If_Erase_FS(uint32_t Add)
uint16_t MEM_If_Write_FS(uint8_t *src, uint8_t *dest, uint32_t Len)
uint8_t *MEM_If_Read_FS(uint8_t *src, uint8_t *dest, uint32_t Len)
uint16_t MEM_If_GetStatus_FS (uint32_t Add, uint8_t Cmd, uint8_t *buffer)

MEM_If_Init_FS
该函数的作用是初始化FLASH,将FLASH解锁,并将所有的标志位清零,以便后续的写入动作。

uint16_t MEM_If_Init_FS(void)
{
    
    
	HAL_FLASH_Unlock();
	__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR |
                           FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR);
  /* USER CODE BEGIN 0 */
  return (USBD_OK);
  /* USER CODE END 0 */
}

在这里插入图片描述
MEM_If_DeInit_FS
该函数与MEM_If_Init_FS的函数相对,将FLASH重新上锁,禁止对FLASH的操作

uint16_t MEM_If_DeInit_FS(void)
{
    
    
	HAL_FLASH_Lock();
  /* USER CODE BEGIN 1 */
  return (USBD_OK);
  /* USER CODE END 1 */
}

在这里插入图片描述
GetSector
局部函数,初始代码中不含该函数,主要作用是返回用户起始区域的Sector地址,STM32F429内部FLASH根据芯片手册一共有2个块,每块是12个扇区,每块中前4个为16K,第5个为64K,其余7个为128K。

#define	ADDR_FLASH_SECTOR_0			0x08000000
#define	ADDR_FLASH_SECTOR_1			0x08004000
#define	ADDR_FLASH_SECTOR_2			0x08008000
#define	ADDR_FLASH_SECTOR_3			0x0800C000
#define	ADDR_FLASH_SECTOR_4			0x08010000
#define	ADDR_FLASH_SECTOR_5			0x08020000
#define	ADDR_FLASH_SECTOR_6			0x08040000
#define	ADDR_FLASH_SECTOR_7			0x08060000
#define	ADDR_FLASH_SECTOR_8			0x08080000
#define	ADDR_FLASH_SECTOR_9			0x080A0000
#define	ADDR_FLASH_SECTOR_10		0x080C0000
#define	ADDR_FLASH_SECTOR_11		0x080E0000
#define	ADDR_FLASH_SECTOR_12		0x08100000
#define	ADDR_FLASH_SECTOR_13		0x08104000
#define	ADDR_FLASH_SECTOR_14		0x08108000
#define	ADDR_FLASH_SECTOR_15		0x0810C000
#define	ADDR_FLASH_SECTOR_16		0x08110000
#define	ADDR_FLASH_SECTOR_17		0x08120000
#define	ADDR_FLASH_SECTOR_18		0x08140000
#define	ADDR_FLASH_SECTOR_19		0x08160000
#define	ADDR_FLASH_SECTOR_20		0x08180000
#define	ADDR_FLASH_SECTOR_21		0x081A0000
#define	ADDR_FLASH_SECTOR_22		0x081C0000
#define	ADDR_FLASH_SECTOR_23		0x081E0000

static uint32_t GetSector(uint32_t Address)
{
    
    
	uint32_t sector = 0;
	if((Address < ADDR_FLASH_SECTOR_1)&&(Address >= ADDR_FLASH_SECTOR_0))
	{
    
    
		sector = FLASH_SECTOR_0;
	}
	else if((Address < ADDR_FLASH_SECTOR_2)&&(Address >= ADDR_FLASH_SECTOR_1))
	{
    
    
		sector = FLASH_SECTOR_1;
	}
	else if((Address < ADDR_FLASH_SECTOR_3)&&(Address >= ADDR_FLASH_SECTOR_2))
	{
    
    
		sector = FLASH_SECTOR_2;
	}
	else if((Address < ADDR_FLASH_SECTOR_4)&&(Address >= ADDR_FLASH_SECTOR_3))
	{
    
    
		sector = FLASH_SECTOR_3;
	}
	else if((Address < ADDR_FLASH_SECTOR_5)&&(Address >= ADDR_FLASH_SECTOR_4))
	{
    
    
		sector = FLASH_SECTOR_4;
	}
	else if((Address < ADDR_FLASH_SECTOR_6)&&(Address >= ADDR_FLASH_SECTOR_5))
	{
    
    
		sector = FLASH_SECTOR_5;
	}
	else if((Address < ADDR_FLASH_SECTOR_7)&&(Address >= ADDR_FLASH_SECTOR_6))
	{
    
    
		sector = FLASH_SECTOR_6;
	}
	else if((Address < ADDR_FLASH_SECTOR_8)&&(Address >= ADDR_FLASH_SECTOR_7))
	{
    
    
		sector = FLASH_SECTOR_7;
	}
	else if((Address < ADDR_FLASH_SECTOR_9)&&(Address >= ADDR_FLASH_SECTOR_8))
	{
    
    
		sector = FLASH_SECTOR_8;
	}
	else if((Address < ADDR_FLASH_SECTOR_10)&&(Address >= ADDR_FLASH_SECTOR_9))
	{
    
    
		sector = FLASH_SECTOR_9;
	}
	else if((Address < ADDR_FLASH_SECTOR_11)&&(Address >= ADDR_FLASH_SECTOR_10))
	{
    
    
		sector = FLASH_SECTOR_10;
	}
	else if((Address < ADDR_FLASH_SECTOR_12)&&(Address >= ADDR_FLASH_SECTOR_11))
	{
    
    
		sector = FLASH_SECTOR_11;
	}
	else if((Address < ADDR_FLASH_SECTOR_13)&&(Address >= ADDR_FLASH_SECTOR_12))
	{
    
    
		sector = FLASH_SECTOR_12;
	}
	else if((Address < ADDR_FLASH_SECTOR_14)&&(Address >= ADDR_FLASH_SECTOR_13))
	{
    
    
		sector = FLASH_SECTOR_13;
	}
	else if((Address < ADDR_FLASH_SECTOR_15)&&(Address >= ADDR_FLASH_SECTOR_14))
	{
    
    
		sector = FLASH_SECTOR_14;
	}
	else if((Address < ADDR_FLASH_SECTOR_16)&&(Address >= ADDR_FLASH_SECTOR_15))
	{
    
    
		sector = FLASH_SECTOR_15;
	}
	else if((Address < ADDR_FLASH_SECTOR_17)&&(Address >= ADDR_FLASH_SECTOR_16))
	{
    
    
		sector = FLASH_SECTOR_16;
	}
	else if((Address < ADDR_FLASH_SECTOR_18)&&(Address >= ADDR_FLASH_SECTOR_17))
	{
    
    
		sector = FLASH_SECTOR_17;
	}
	else if((Address < ADDR_FLASH_SECTOR_19)&&(Address >= ADDR_FLASH_SECTOR_18))
	{
    
    
		sector = FLASH_SECTOR_18;
	}
	else if((Address < ADDR_FLASH_SECTOR_20)&&(Address >= ADDR_FLASH_SECTOR_19))
	{
    
    
		sector = FLASH_SECTOR_19;
	}
	else if((Address < ADDR_FLASH_SECTOR_21)&&(Address >= ADDR_FLASH_SECTOR_20))
	{
    
    
		sector = FLASH_SECTOR_20;
	}
	else if((Address < ADDR_FLASH_SECTOR_22)&&(Address >= ADDR_FLASH_SECTOR_21))
	{
    
    
		sector = FLASH_SECTOR_21;
	}
	else if((Address < ADDR_FLASH_SECTOR_23)&&(Address >= ADDR_FLASH_SECTOR_22))
	{
    
    
		sector = FLASH_SECTOR_22;
	}
	else 
	{
    
    
		sector = FLASH_SECTOR_23;
	}
	return sector;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
MEM_If_Erase_FS
该函数实现的功能为擦除APP区域的数据,HAL库的初始化封装格式,定义了擦除方式为按照sector进行擦除

uint16_t MEM_If_Erase_FS(uint32_t Add)
{
    
    
	uint32_t UserStartSector;
	uint32_t SectorError;
	FLASH_EraseInitTypeDef pEraseInit;
	MEM_If_Init_FS();
	UserStartSector = GetSector(Add);
	pEraseInit.TypeErase = TYPEERASE_SECTORS;
	pEraseInit.Sector = UserStartSector;
	pEraseInit.NbSectors = 3;
	pEraseInit.VoltageRange = VOLTAGE_RANGE_3;
	if(HAL_FLASHEx_Erase(&pEraseInit,&SectorError)!=HAL_OK)
	{
    
    
		return (USBD_FAIL);
	}	
  return (USBD_OK);
  /* USER CODE END 2 */
}

在这里插入图片描述
MEM_If_Write_FS
Flash写入的接口函数,将USB接收到的FLASH数据写入到FLASH中,也是比较关键的函数接口,以word为单位进行烧写

uint16_t MEM_If_Write_FS(uint8_t *src, uint8_t *dest, uint32_t Len)
{
    
    
  /* USER CODE BEGIN 3 */
  UNUSED(src);
  UNUSED(dest);
  UNUSED(Len);
	
	uint32_t i = 0;	
	
	for(i = 0; i < Len; i = i + 4)
	{
    
    
		if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,(uint32_t)(dest + i),*(uint32_t *)(src + i)) == HAL_OK)
		{
    
    
			if(*(uint32_t *)(src + i) != *(uint32_t *)(dest + i))
			{
    
    
				return 2;
			}
		}
		else
		{
    
    
			return 1;
		}
	}	

  return (USBD_OK);
  /* USER CODE END 3 */
}

在这里插入图片描述
MEM_If_Read_FS
读取指定地址的数据 到目标数组中,并返回数组地址

uint8_t *MEM_If_Read_FS(uint8_t *src, uint8_t *dest, uint32_t Len)
{
    
    
  /* Return a valid address to avoid HardFault */
  /* USER CODE BEGIN 4 */
  UNUSED(src);
  UNUSED(dest);
  UNUSED(Len);
	
	uint32_t i = 0;
	uint8_t *psrc = src;
	for( i = 0; i < Len ; i++ )
	{
    
    
		dest[i] = *psrc++;
	}
	return (uint8_t *)(dest);


//  return (uint8_t*)(USBD_OK);
  /* USER CODE END 4 */
}

在这里插入图片描述
MEM_If_GetStatus_FS
返回状态

uint16_t MEM_If_GetStatus_FS (uint32_t Add, uint8_t Cmd, uint8_t *buffer)
{
    
    
	uint16_t FLASH_PROGRAM_TIME = 50;
	uint16_t FLASH_ERASE_TIME = 50;
	switch (Cmd)
	{
    
    
		case DFU_MEDIA_PROGRAM:
			buffer[1] = (uint8_t)FLASH_PROGRAM_TIME;
			buffer[2] = (uint8_t)(FLASH_PROGRAM_TIME << 8);
			buffer[3] = 0;
		break;
		case DFU_MEDIA_ERASE:
		default:
			buffer[1] = (uint8_t)FLASH_ERASE_TIME;
			buffer[2] = (uint8_t)(FLASH_ERASE_TIME << 8);
			buffer[3] = 0;
	break;
	}                             
  return  (USBD_OK);
}

(4).增加跳转APP的Bootloader程序

void JumpToBootloader(void)
{
    
    
	uint32_t i=0;
	void (*SysMemBootJump)(void);        /* 声明一个函数指针 */
//	__IO uint32_t BootAddr = 0x1FF09800; /* STM32F4的系统BootLoader地址 */
	__IO uint32_t BootAddr = USBD_DFU_APP_DEFAULT_ADD;
	/* 关闭全局中断 */
	__set_PRIMASK(1);//关总中断

	/* 关闭滴答定时器,复位到默认值 */
	SysTick->CTRL = 0;
	SysTick->LOAD = 0;
	SysTick->VAL = 0;

	/* 设置所有时钟到默认状态,使用HSI时钟 */
	HAL_RCC_DeInit();

	/* 关闭所有中断,清除所有中断挂起标志 */
	for (i = 0; i < 8; i++)
	{
    
    
		NVIC->ICER[i]=0xFFFFFFFF;
		NVIC->ICPR[i]=0xFFFFFFFF;
	}    

	/* 使能全局中断 */
	__set_PRIMASK(0);//开总中断

	/* 跳转到系统BootLoader,首地址是MSP,地址+4是复位中断服务程序地址 */
	SysMemBootJump = (void (*)(void)) (*((uint32_t *) (BootAddr + 4)));

	/* 设置主堆栈指针 */
	__set_MSP(*(uint32_t *)BootAddr);

	/* 在RTOS工程,这条语句很重要,设置为特权级模式,使用MSP指针 */
	__set_CONTROL(0);

	/* 跳转到系统BootLoader */
	SysMemBootJump(); 

	/* 跳转成功的话,不会执行到这里,用户可以在这里添加代码 */
	while (1)
	{
    
    

	}
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qizhi321123/article/details/127263064