STM32HAL----USB升级固件(DFU)

       早就听说stm32可以利用USB升级固件,脱离各种Link。趁有空搜了下相关资料,然后利用stm32CubeMX实现了这个功能。

参考了这个地址的资料:http://www.stm32cube.com/question/500

       上面这个链接写得很详细,所以不再赘述,直接上重点以及注意点。

 

一、bootloader地址设置

STM32HAL----USB升级固件(DFU)_第1张图片

在CubeMX里面,有两个参数要设置好,以上画框部分。

一个是APP的默认地址。

另一个是DFU接口。用来识别我们bootloader存放起始地址以及占用内存大小,还有内部FLASH的扇区划分方式。

因为我的MCU是STM32F407VET6,所以改成上图。bootloader占用第一扇区16KB空间。016Ka,其中“a”代表只读,"g"读写。

二、APP地址设置

APP像往常一样编写以及编译程序,需要改动的地址有两个。

1、中断向量表:“#define VECT_TAB_OFFSET  0x4000”

     位于:“system_stm32f4xx.c ”,因为我bootloader占用了16KB,计算可知偏移量应为 “0x4000”

2、MDK魔术棒

三、bootloader占用内存

         尝试着能不能缩减bootloader占用的内存,将RCC以及GPIO由HAL库换成LL库,生成的程序空间可以小约2KB。

但RCC使用LL库,发现生成的程序,USB设备无法枚举成功。如果GPIO使用LL库,RCC为HAL库,发现USB插头插上

PC后,需要较久时间才能反应过来,而且DFU下载程序时候的反应也变慢了。

         所以,如果对内存占用较敏感,可能不适合用CubeMX。需要自己到ST官网下载DFU库修改,寄存器操作RCC以及GPIO。

 

注意点:

      1、刚开始,发现功能能实现,但每次由DFU状态转到APP之后,复位MCU想要烧写另外的APP。发现PC上USB设备不会重新枚举,每次烧写都要重新插拔。后来查阅USB的资料,发现可以利用“D+”引脚使PC重新枚举USB。资料地址:https://blog.csdn.net/u011279649/article/details/41779767查看里面的第一个步骤。

      1、帖子的代码里有一处地方需修改,在“MEM_If_Erase_FS(uint32_t Add)”函数里,“pEraseInit.NbSectors = 9;”此处应改为“pEraseInit.NbSectors = 1;”。这个函数,当一个地址被传入,擦除地址所在扇区。我试过改成1后,烧写200多KB程序正常运行。证明修改后市没问题的。

将程序修改如下,复位MCU后PC会重新枚举设备:

void MX_GPIO_Init(void)
{

  GPIO_InitTypeDef GPIO_InitStruct;

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOE_CLK_ENABLE();
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();

  /*Configure GPIO pin : PtPin */
  GPIO_InitStruct.Pin = KEY1_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  HAL_GPIO_Init(KEY1_GPIO_Port, &GPIO_InitStruct);

  /*拉低USB D+ 引脚 使PC重新枚举USB设备 */
  GPIO_InitStruct.Pin = GPIO_PIN_12;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
	
  HAL_GPIO_WritePin(GPIOA,GPIO_PIN_12,GPIO_PIN_RESET);
}

主函数:

int main(void)
{

  HAL_Init();

  SystemClock_Config();

  MX_GPIO_Init();
  HAL_Delay(1);  //延时1MS等待USB稳定
	
     if(HAL_GPIO_ReadPin( KEY1_GPIO_Port , KEY1_Pin ) == GPIO_PIN_RESET)
    {
		MX_USB_DEVICE_Init();
		while(1);
    }
		
    /* Test if user code is programmed starting from USBD_DFU_APP_DEFAULT_ADD address */
	if(((*(__IO uint32_t*)USBD_DFU_APP_DEFAULT_ADD) & 0x2FFE0000 ) == 0x20000000)
	{
		/* 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();
	}
    

  while (1)
  {


  }

}

在“void MX_GPIO_Init(void)”中拉低了PA12,也就是“D+”,然后初始化USB时 “D+”恢复高电平,PC识别设备。

我生成了2个APP,APP1使D2闪烁,APP2使D3闪烁。

STM32HAL----USB升级固件(DFU)_第2张图片

STM32HAL----USB升级固件(DFU)_第3张图片

STM32HAL----USB升级固件(DFU)_第4张图片

 

四、按键控制,烧写APP到不同地址。

目的是,按下不同按键,DFU烧写APP到不同地址,然后开机时按不同按键,运行不同的APP。

需要修改几处地方:

1、"usbd_conf.h" : "extern  uint32_t  APPAddrDevi;"

                               “#define USBD_DFU_APP_DEFAULT_ADD     (0x08004000 + APPAddrDevi) ”

2、"main.c":申请一个全局变量“uint32_t APPAddrDevi = 0;”

主函数修改如下:

int main(void)
{

  HAL_Init();

  SystemClock_Config();

  MX_GPIO_Init();
  HAL_Delay(1);  //延时1MS等待USB稳定

  if(HAL_GPIO_ReadPin( KEY1_GPIO_Port , KEY1_Pin ) == GPIO_PIN_RESET)
  {
          if(HAL_GPIO_ReadPin( KEY0_GPIO_Port , KEY0_Pin ) == GPIO_PIN_RESET) APPAddrDevi = 0x8000;
	  MX_USB_DEVICE_Init();
	  while(1);
  }
		
  if(HAL_GPIO_ReadPin( KEY0_GPIO_Port , KEY0_Pin ) == GPIO_PIN_RESET) APPAddrDevi = 0x8000;
		
  /* Test if user code is programmed starting from USBD_DFU_APP_DEFAULT_ADD address */
  if(((*(__IO uint32_t*)USBD_DFU_APP_DEFAULT_ADD) & 0x2FFE0000 ) == 0x20000000)
  {
	  /* 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();
  }
    
 
  while (1)
  {

 
  }

}

相应的,APP2魔术棒程序起始地址应设为“0x800C000”,大小为“0x74000”,

中断向量表:“#define VECT_TAB_OFFSET  0xC000”

 

开机按住KEY1跟KEY0,然后放开KEY1,APP会烧写到“0x800C000”这个地址。如果只按住KEY1不安KEY0,则会烧写到“0x8004000”这个地址

开机不按按键,运行“0x8004000”地址的APP,按下KEY0,运行“0x800C000”地址的APP。

你可能感兴趣的:(STM32,USB)