STM32F103ZET6 - USB_DFU 升级

STM32F103ZET6 - USB_DFU 升级

前言:
记录下碰到的坑!!

  1. 仿真时发现读按键状态函数HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin),无论是判断等于1还是0,仿真都能进入; 纠结很久,然后查看GPIOx_IDR寄存器, 发现没问题,按键按下时该位为0,不按为1; 经查找发现是IDE优化等级问题,STM32CubeMX生成的代码默认优化等级为O3;
    以下KEIL编译器【C语言编译选项优化等级说明】,参考 https://blog.csdn.net/jiangchao3392/article/details/80267198
    -O0 Minimum optimization. Turns off most optimizations.It gives the best possible debug view and the lowest level of optimization. (近乎不优化,用于调试代码。出现代码行不能设置断点可如此设置试试。)
    -O1 Restrictedoptimization. Removes unused inline functions and unused static functions.Turns off optimizations that seriously degrade the debug view. Ifused with --debug, this option gives a satisfactorydebug view with good code density.
    (部分优化。 移除未调用的内联函数和静态函数,关闭debug窗口优化,此状态也能用于调试)
    -O2 Highoptimization. If used with–debug, the debug viewmight be less satisfactory because the mapping of object code tosource code is not always clear.This is the default optimization level.
    (默认优化等级。如果处于debug状态,部分代码行将不能被调试,具体做了什么优化好像没说)
    -O3 Maximumoptimization.-O3 performs the same optimizationsas-O2 however the balance between space and timeoptimizations in the generated code is more heavily weighted towardsspace or time compared with-O2. That is:
  2. DFU升级成功后, 有查看0X0801 0000 Memory内容,和bin文件是相同的; 按键触发跳转至APP, STM32正点原子精英版上的LED只亮后就死掉; 将连接电脑的USB断开后能正常跳转.
    ------------ 说明USB有干扰,在跳转前加入了关闭USB功能后,正常跳转;
USBD_Stop(&hUsbDeviceFS);// 重点: 此处需关闭USB功能, 需要时再打开.

示例详解:  (参考 : https://blog.csdn.net/yhl_sophia/article/details/84337048)
	基于硬件平台: STM32F103ZET6正点原子的精英板, 使用stm32cubemx 工具自动产生的配置工程,使用KEIL5编译代码。

   1.  STM32CubeMX生成代码过程如下:
       
       1>.  打开SWD - debug调试模式, STM32CubeMX生成的代码默认是没打开debug调试模式的 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20191222213429503.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21pbW82MDg2,size_16,color_FFFFFF,t_70)
       2>.  外部时钟配置, HSE选择为外部晶振![在这里插入图片描述](https://img-blog.csdnimg.cn/20191212190806689.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21pbW82MDg2,size_16,color_FFFFFF,t_70)
            3>.   CLOCK Configuration 配置如下: 选择HSE,PLL倍频为9, USB分频为1.5=48M![在这里插入图片描述](https://img-blog.csdnimg.cn/20191212191124166.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21pbW82MDg2,size_16,color_FFFFFF,t_70)
         4>.   USB功能选中![在这里USB插入图片描述](https://img-blog.csdnimg.cn/2019121219145053.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21pbW82MDg2,size_16,color_FFFFFF,t_70)
5>.  USB_DEVICE : 
     APP地址设置为:0X801000
     DFU接口: @Internal Flash   /0x08000000/32 * 02Ka,224 * 02Kg
     因为我的MCU是STM32F103ZET6,1个扇区为2k, bootloader占64KB空间,APP占448K。32 * 02Ka,其中“a”代表只读,224 * 02Kg,其中"g"读写。![在这里插入图片描述](https://img-blog.csdnimg.cn/20191212192444586.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21pbW82MDg2,size_16,color_FFFFFF,t_70)
6>.  生成代码配置![在这里插入图片描述](https://img-blog.csdnimg.cn/20191222213919754.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21pbW82MDg2,size_16,color_FFFFFF,t_70)



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

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
 

/* Private functions ---------------------------------------------------------*/
/**

  • @brief Memory initialization routine.
  • @retval USBD_OK if operation is successful, MAL_FAIL else.
    /
    //解锁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
    /
    //上锁FLASH
    uint16_t MEM_If_DeInit_FS(void)
    {
    /
    USER CODE BEGIN 1 /
    HAL_FLASH_Lock();
    return (USBD_OK);
    /
    USER CODE END 1 */
    }
 2、Flash的擦除操作,这里擦除的是存放APP程序的整个空间
 

/**

  • @brief Erase sector.
  • @param Add: Address of sector to be erased.
  • @retval 0 if operation is successful, MAL_FAIL else.
    */

#define USBD_DFU_APP_END_ADD 0x08080000//512K
#define USBD_DFU_APP_DEFAULT_ADD 0x08010000
#define FLASH_PAGE_SIZE 0x800U //2k

//Flash的擦除操作,这里擦除的是存放APP程序的整个空间
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操作

/**

  • @brief Memory write routine.

  • @param src: Pointer to the source buffer. Address to be written to.

  • @param dest: Pointer to the destination buffer.

  • @param Len: Number of data to be written (in bytes).

  • @retval USBD_OK if operation is successful, MAL_FAIL else.
    */
    //写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 {
    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操作

/**

  • @brief Memory read routine.

  • @param src: Pointer to the source buffer. Address to be written to.

  • @param dest: Pointer to the destination buffer.

  • @param Len: Number of data to be read (in bytes).

  • @retval Pointer to the physical address where data should be read.
    */
    //读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 {
    dest[i] = psrc++;
    }
    return (uint8_t
    )(dest);;
    // return (uint8_t*)(USBD_OK);
    /* USER CODE END 4 */
    }

5、获取Flash状态

/**

  • @brief Get status routine
  • @param Add: Address to be read from
  • @param Cmd: Number of data to be read (in bytes)
  • @param buffer: used for returning the time necessary for a program or an erase operation
  • @retval USBD_OK if operation is successful
    */

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

//获取Flash状态
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程序入口指针和存放入口地址的变量

//首先定义APP程序入口指针和存放入口地址的变量
typedef void (*pFunction)(void);

pFunction JumpToApplication;
uint32_t JumpAddress;

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

if(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin) != 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();
		 }
	}
点击魔术棒设置程序起始地址,和大小
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191212194230555.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21pbW82MDg2,size_16,color_FFFFFF,t_70)
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191212194300178.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21pbW82MDg2,size_16,color_FFFFFF,t_70)
至此,BootLoader程序就编写好了,接下来是制作APP程序

二、用keil编写一个闪灯的APP程序,点击魔术棒设置程序起始地址
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191212194356343.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21pbW82MDg2,size_16,color_FFFFFF,t_70)
![在这里插入图片描述](https://img-blog.csdnimg.cn/2019121219442063.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21pbW82MDg2,size_16,color_FFFFFF,t_70)
另外,在APP程序的还要进行中断向量表的重映射,只要更改system_stm32fxx.c为以下配置即可。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191212194543355.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21pbW82MDg2,size_16,color_FFFFFF,t_70)
主循环while内写个简单的灯闪烁代码
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191212194702860.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21pbW82MDg2,size_16,color_FFFFFF,t_70)
附:APP生成.bin文件配置
C:\Keil_v5\ARM\ARMCC\bin\fromelf.exe   --bin -o   .\ledtest\ledtest.bin .\ledtest\ledtest.axf
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191212194838646.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21pbW82MDg2,size_16,color_FFFFFF,t_70)
编译生成app.hex文件,接下来需要将hex文件转换为DFU文件,去ST官网上搜索DFuse,下载stsw-stm32080这个代号的压缩包,里面是DFuse工具。
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文件。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191212195050222.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21pbW82MDg2,size_16,color_FFFFFF,t_70)


 之后打开DFuse软件,可以看到软件检测到了我们的USB DFU设备,Choose刚刚生成的DFU文件,点击Upgrade进行升级。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191212195218421.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21pbW82MDg2,size_16,color_FFFFFF,t_70)
至此,USB_DFU程序完成, 切换USB_232供电后, 按下KEY0,程序会跳至APP运行,此时两个LED将会以500ms间隔闪烁.

你可能感兴趣的:(STM32F103ZET6 - USB_DFU 升级)