STM32 HAL学习(七)USB DFU升级BootLoader

DFU(Device Firmware Upgrade),即固件升级,STM32提供有专门的USB通信协议实现DFU升级BootLoader

我用的开发板是STM32F070,支持DFU升级,故用来做做实验,下面对整个过程做个记录。

首先是使用STM32Cube MX使能USB外设,并在Class for FS IP中选择Download Firmware Class(DFU),我这里通过判断PA2引脚电平决定进入DFU模式还是APP模式,因此PA2引脚配置成输入模式。

之后配置DFU设备的相关参数 

红色方框中的两个参数

第一个USBD_DFU_APP_DEFAULT_ADD即是APP程序的开始地址,而从0x08000000开始到0x08007000的空间则是留给BootLoader 。

第二个USBD_DFU_MEDIA Interface是向DFUse软件描述单片机内部Flash分配的情况,@Internal Flash是Flash的名称,0x08000000是Flash的起始地址,09*2Ka表示前面9个页的空间,a表示只读,因此用来存放BootLoader,2k表示每页大小为2k(这个具体要查看芯片手册来设定);g则表示可读可写,故用来存放APP程序。

点击生成代码,在keil中开始编辑BootLoader代码,用户要编辑的主要是usbd_dfu.c文件,在这个文件中提供了Flash的初始化、反初始化、擦除、编程、读取以及获取状态几个函数接口,我们要做的就是实现这些函数的具体操作。

扫描二维码关注公众号,回复: 4921134 查看本文章
static uint16_t MEM_If_Init_FS(void);
static uint16_t MEM_If_Erase_FS(uint32_t Add);
static uint16_t MEM_If_Write_FS(uint8_t *src, uint8_t *dest, uint32_t Len);
static uint8_t *MEM_If_Read_FS(uint8_t *src, uint8_t *dest, uint32_t Len);
static uint16_t MEM_If_DeInit_FS(void);
static uint16_t MEM_If_GetStatus_FS(uint32_t Add, uint8_t Cmd, uint8_t *buffer);

 1、Init和Deinit里面主要是解锁Flash和上锁Flash

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

/**
  * @brief  De-Initializes Memory
  * @retval USBD_OK if operation is successful, MAL_FAIL else
  */
uint16_t MEM_If_DeInit_FS(void)
{
  /* USER CODE BEGIN 1 */
	HAL_FLASH_Lock();
  return (USBD_OK);
  /* USER CODE END 1 */
}

 2、Flash的擦除操作,这里擦除的是存放APP程序的整个空间

#define USBD_DFU_APP_END_ADD        0x0801F800
#define USBD_DFU_APP_DEFAULT_ADD    0x08007000
#define FLASH_PAGE_SIZE             0x800U       //2k

uint16_t MEM_If_Erase_FS(uint32_t Add)
{
  /* USER CODE BEGIN 2 */
        uint32_t NbOfPages = 0;
	uint32_t PageError = 0;
	FLASH_EraseInitTypeDef pEraseInit;
	
	NbOfPages = (USBD_DFU_APP_END_ADD - USBD_DFU_APP_DEFAULT_ADD)/FLASH_PAGE_SIZE;
	
	pEraseInit.TypeErase = FLASH_TYPEERASE_PAGES;
	pEraseInit.PageAddress = USBD_DFU_APP_DEFAULT_ADD;
	pEraseInit.NbPages = NbOfPages;      //erase all pages of APP
	
	if(HAL_FLASHEx_Erase(&pEraseInit,&PageError)!= HAL_OK)
		return 1;
  return (USBD_OK);
  /* USER CODE END 2 */
}

3、写Flash操作

uint16_t MEM_If_Write_FS(uint8_t *src, uint8_t *dest, uint32_t Len)
{
  /* USER CODE BEGIN 3 */
	uint32_t i =0;
	
	for(i=0;i<Len;i+=4)
	{
		if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,(uint32_t)(dest+i),*(uint32_t*)(src+i))== HAL_OK)
		{
			/*Check the written value*/
			if(*(uint32_t*)(src+i) != *(uint32_t*)(dest+i))
			{
				/*Flash content doesn't match SRAM content*/
				return 2;
			}
		}
		else
		{
			return 1;
		}
	}
  return (USBD_OK);
  /* USER CODE END 3 */
}

4、读Flash操作

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 */
	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 */
}

5、获取Flash状态

//这个只是一个擦除或编程时间的设定
#define FLASH_ERASE_TIME            (uint16_t)50 
#define FLASH_PROGRAM_TIME           (uint16_t)50


uint16_t MEM_If_GetStatus_FS(uint32_t Add, uint8_t Cmd, uint8_t *buffer)
{
  /* USER CODE BEGIN 5 */
  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);
  /* USER CODE END 5 */
}

usbd_dfu_if.c文件编写完成,之后就是main函数中的操作

首先定义APP程序入口指针和存放入口地址的变量

typedef void (*pFunction)(void);

 pFunction JumpToApplication;
 uint32_t JumpAddress;

读取PA2引脚电平决定是否进入APP以及判断APP程序入口地址是否存在或正确

if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_2) != GPIO_PIN_SET)
{
  if(((*(__IO uint32_t*)USBD_DFU_APP_DEFAULT_ADD) & 0x2FFE0000) == 0x20000000)
  {
	JumpAddress = *(__IO uint32_t*)(USBD_DFU_APP_DEFAULT_ADD +4);
	JumpToApplication = (pFunction) JumpAddress;
			
	__set_MSP(*(__IO uint32_t*)USBD_DFU_APP_DEFAULT_ADD);
	JumpToApplication();
   }
}


MX_USB_DEVICE_Init(); 

至此,BootLoader程序就编写好了,接下来是制作APP程序

用keil编写一个闪灯的APP程序,点击魔术棒设置程序起始地址

另外,在APP程序的main函数中还要进行中断向量表的重映射 

#define APPLICATION_ADDRESS   (uint32_t)0x08007000
#if (defined ( __CC_ARM )) 
__IO uint32_t VectorTable[48] __attribute__((at(0x20000000))); 
#elif (defined (__ICCARM__)) 
#pragma location = 0x20000000
__no_init __IO uint32_t VectorTable[48];
#elif defined ( __GNUC__ )
__IO uint32_t VectorTable[48] __attribute__((section(".RAMVectorTable"))); 
#endif

  
int main(void)	
{
 /* USER CODE BEGIN Init */

  for(i=0;i<48;i++)
	{
		VectorTable[i] = *(__IO uint32_t*)(APPLICATION_ADDRESS + (i<<2));
	}
	__HAL_RCC_SYSCFG_CLK_ENABLE();
	
	__HAL_SYSCFG_REMAPMEMORY_SRAM();
	
  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();
}

编译生成app.hex文件,接下来需要将hex文件转换为DFU文件,去ST官网上搜索DFuse,下载stsw-stm32080这个代号的压缩包,里面是DFuse工具。需要注意,安装完这个DFuse工具后打开软件并不能检测到DFU设备,因为还要安装一个驱动。这个驱动在DFUse安装路径下,例如我的是:C:\Program Files (x86)\STMicroelectronics\Software\DfuSe v3.0.6\Bin\Driver\Win7\x64,找到对应的路径根据自己电脑的版本安装dpinst_amd64.exe。

DFUse软件自带有将hex文件转换成DFU文件的工具 DFU File Manager,在电脑开始菜单中搜索dfu即可找到这个软件,运行选择“I Want to GENERATE  a DFU file from s19,HEX or BIN files ”,进入以下界面,这里的Target ID有不同的值,其中0代表片内Flash;1代表外部Flash;2代表外部Nor Flash。因此我们这里选择0,点击S19 or Hex选择hex文件即可生成DFU文件。

 之后打开DFuse软件,可以看到软件检测到了我们的USB DFU设备,Choose刚刚生成的DFU文件,点击Upgrade进行升级。

将PA2接低电平,reset单片机,闪灯程序正常执行,证明跳转APP成功,DFU升级完成。 

猜你喜欢

转载自blog.csdn.net/yhl_sophia/article/details/84337048