STM32F103ZET6 - USB_HID -2 (IAP升级)

*STM32F103ZET6 - USB_HID -2 (IAP升级)
前言:
记录下碰到的坑!!

  1. HID升级完成后, 有查看0X0801 0000 Memory内容,和bin文件是相同的; 按键触发跳转至APP, STM32正点原子精英版上的LED只亮后就死掉; 将连接电脑的USB断开后能正常跳转.
    ------------ 说明USB有干扰,在跳转前加入了关闭USB功能后,正常跳转;
USBD_Stop(&hUsbDeviceFS);// 重点: 此处需关闭USB功能, 需要时再打开.

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

  1. STM32CubeMX生成代码过程如下:

    1>. 打开SWD - debug调试模式, STM32CubeMX生成的代码默认是没打开debug调试模式的 STM32F103ZET6 - USB_HID -2 (IAP升级)_第1张图片
    2>. 外部时钟配置, HSE选择为外部晶振STM32F103ZET6 - USB_HID -2 (IAP升级)_第2张图片
    3>. CLOCK Configuration 配置如下: 选择HSE,PLL倍频为9, USB分频为1.5=48MSTM32F103ZET6 - USB_HID -2 (IAP升级)_第3张图片 4>. GPIO口配置, 配置LED0,LED1和KEY0脚位
    STM32F103ZET6 - USB_HID -2 (IAP升级)_第4张图片
    STM32F103ZET6 - USB_HID -2 (IAP升级)_第5张图片
    5>. USB功能选中STM32F103ZET6 - USB_HID -2 (IAP升级)_第6张图片
    6>. USB_DEVICE --> 选择HID功能 . 参数设置保持默认不变.
    STM32F103ZET6 - USB_HID -2 (IAP升级)_第7张图片
    STM32F103ZET6 - USB_HID -2 (IAP升级)_第8张图片
    7>. 中断配置保持默认.
    STM32F103ZET6 - USB_HID -2 (IAP升级)_第9张图片
    8>. 生成代码配置STM32F103ZET6 - USB_HID -2 (IAP升级)_第10张图片

点击生成代码,然后烧录默认代码,连上电脑可在电脑的设备管理器中可以看到人全学输入设备中可以看到USB输入设备,但是有个叹号:
STM32F103ZET6 - USB_HID -2 (IAP升级)_第11张图片
二、接下来我们将要在生成的HID框架上修改代码, 主要有修改设备描述符,传输的字节,轮询的时间,中断回调函数接收( usbd_custom_hid_if.c,usbd_conf.h,usbd_customhid.h)

  1. 在 usbd_custom_hid_if.c 中修改设备描述符函数 CUSTOM_HID_ReportDesc_FS。
/** @defgroup USBD_CUSTOM_HID_Private_Variables USBD_CUSTOM_HID_Private_Variables
  * @brief Private variables.
  * @{
  */

/** Usb HID report descriptor. */
//设备描述符
__ALIGN_BEGIN static uint8_t CUSTOM_HID_ReportDesc_FS[USBD_CUSTOM_HID_REPORT_DESC_SIZE] __ALIGN_END =
{
  /* USER CODE BEGIN 0 */
//  0x00,
	0x05, 0x8c, /* USAGE_PAGE (ST Page) */
    0x09, 0x01, /* USAGE (Demo Kit) */
    0xa1, 0x01, /* COLLECTION (Application) */
 
    // The Input report
    0x09,0x03, // USAGE ID - Vendor defined
    0x15,0x00, // LOGICAL_MINIMUM (0)
    0x26,0x00, 0xFF, // LOGICAL_MAXIMUM (255)
    0x75,0x08, // REPORT_SIZE (8bit)
    0x95,CUSTOM_HID_EPIN_SIZE, // REPORT_COUNT (64Byte)
    0x81,0x02, // INPUT (Data,Var,Abs)
 
    // The Output report
    0x09,0x04, // USAGE ID - Vendor defined
    0x15,0x00, // LOGICAL_MINIMUM (0)
    0x26,0x00,0xFF, // LOGICAL_MAXIMUM (255)
    0x75,0x08, // REPORT_SIZE (8bit)
    0x95,CUSTOM_HID_EPOUT_SIZE, // REPORT_COUNT (64Byte)
    0x91,0x02, // OUTPUT (Data,Var,Abs)
  /* USER CODE END 0 */
  0xC0    /*     END_COLLECTION	             */
};
  1. 在usbd_conf.h 中修改设备描述符USBD_CUSTOM_HID_REPORT_DESC_SIZE的大小 ,原为2个字节. 现根据之前的修改代 码改为33个字节.
    USBD_CUSTOMHID_OUTREPORT_BUF_SIZE 发送BUF改为64字节,原为2个字节
/** @defgroup USBD_CONF_Exported_Defines USBD_CONF_Exported_Defines
  * @brief Defines for configuration of the Usb device.
  * @{
  */

/*---------- -----------*/
#define USBD_MAX_NUM_INTERFACES     1
/*---------- -----------*/
#define USBD_MAX_NUM_CONFIGURATION     1
/*---------- -----------*/
#define USBD_MAX_STR_DESC_SIZ     512
/*---------- -----------*/
#define USBD_DEBUG_LEVEL     0
/*---------- -----------*/
#define USBD_SELF_POWERED     1
/*---------- -----------*/
#define USBD_CUSTOMHID_OUTREPORT_BUF_SIZE     64//2 /* 64字节 */
/*---------- -----------*/
#define USBD_CUSTOM_HID_REPORT_DESC_SIZE     33U//2 /* 设备描述符大小 */
/*---------- -----------*/
#define CUSTOM_HID_FS_BINTERVAL     0x5
  1. 在 usbd_customhid.h 中修改 CUSTOM_HID_EPIN_SIZE,CUSTOM_HID_EPOUT_SIZE,和轮循时间CUSTOM_HID_HS_BINTERVAL

/** @defgroup USBD_CUSTOM_HID_Exported_Defines
  * @{
  */
#define CUSTOM_HID_EPIN_ADDR                 0x81U
#define CUSTOM_HID_EPIN_SIZE                 0x40U//0x02U  /* 64字节 */
/*这个是USB的中断接收函数,根据USB设备的ID来接收字节,库生成的时候只能接收2个字节的,
当我们改成0x40,就能接收64个字节,USB HID一包只能64个字节*/

#define CUSTOM_HID_EPOUT_ADDR                0x01U
#define CUSTOM_HID_EPOUT_SIZE                0x40U//0x02U  /* 64字节 */

#define USB_CUSTOM_HID_CONFIG_DESC_SIZ       41U
#define USB_CUSTOM_HID_DESC_SIZ              9U

#ifndef CUSTOM_HID_HS_BINTERVAL
#define CUSTOM_HID_HS_BINTERVAL            0x01U//0x05U //1ms轮询
#endif /* CUSTOM_HID_HS_BINTERVAL */

至些代码修改完成, 这时重新编译。 然后烧录代码后, 插上USB,电脑会正确识别为HID设备, 这时没有了感叹号。
STM32F103ZET6 - USB_HID -2 (IAP升级)_第12张图片
三、添加USB中断接收内代码和写FLASH代码:
主要需要以下文件 usbd_custom_hid_if.c;

  1. 首先在文件前面包含flash.h头文件
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "usbd_custom_hid_if.h"

/* USER CODE BEGIN INCLUDE */
#include "flash.h"
/* USER CODE END INCLUDE */
  1. 在usbd_custom_hid_if.c 中 根据USB HID库里面的接收思路,我们可以单独定义一个接收64字节的函数 CUSTOM_HID_OutDulBuf_FS;
USBD_CUSTOM_HID_ItfTypeDef USBD_CustomHID_fops_FS =
{
  CUSTOM_HID_ReportDesc_FS,	/* 设备描述符 */
  CUSTOM_HID_Init_FS,	
  CUSTOM_HID_DeInit_FS,
  CUSTOM_HID_OutEvent_FS,  /* 中断接收函数 */
  CUSTOM_HID_OutDulBuf_FS  /* add - 定义一个接收64字节的函数 */ //add
};

在上面usbd_customhid.h 中,我们将CUSTOM_HID_EPIN_SIZE改成0x40,就是从原接收2个字节改到了能接收64个字节,现在USB HID一包只能64个字节;

//USB的中断接收函数
static int8_t CUSTOM_HID_OutEvent_FS(uint8_t event_idx, uint8_t state)
{
/* USER CODE BEGIN 6 /
return (USBD_OK);
/
USER CODE END 6 */
}

梳理USB HID库里面的接收思路,我们可以单独定义一个接收64字节的函数 CUSTOM_HID_OutDulBuf_FS

USBD_CUSTOM_HID_ItfTypeDef USBD_CustomHID_fops_FS =
{
  CUSTOM_HID_ReportDesc_FS,	/* 设备描述符 */
  CUSTOM_HID_Init_FS,	
  CUSTOM_HID_DeInit_FS,
  CUSTOM_HID_OutEvent_FS,  /* 中断接收函数 */
  CUSTOM_HID_OutDulBuf_FS  /* add - 定义一个接收64字节的函数 */ //add
};
static int8_t CUSTOM_HID_OutDulBuf_FS (uint8_t* DulBuf)
{

    return (0);
}

代码如下;

/** @defgroup USBD_CUSTOM_HID_Private_FunctionPrototypes USBD_CUSTOM_HID_Private_FunctionPrototypes
  * @brief Private functions declaration.
  * @{
  */

static int8_t CUSTOM_HID_Init_FS(void);
static int8_t CUSTOM_HID_DeInit_FS(void);
static int8_t CUSTOM_HID_OutEvent_FS(uint8_t event_idx, uint8_t state);/* 中断接收函数 */
static int8_t CUSTOM_HID_OutDulBuf_FS(uint8_t* DulBuf);//add /* add - 定义一个接收64字节的函数 */

/**
  * @}
  */

USBD_CUSTOM_HID_ItfTypeDef USBD_CustomHID_fops_FS =
{
  CUSTOM_HID_ReportDesc_FS,	/* 设备描述符 */
  CUSTOM_HID_Init_FS,	
  CUSTOM_HID_DeInit_FS,
  CUSTOM_HID_OutEvent_FS,  /* 中断接收函数 */
	CUSTOM_HID_OutDulBuf_FS  /* add - 定义一个接收64字节的函数 */ //add
};

/** @defgroup USBD_CUSTOM_HID_Private_Functions USBD_CUSTOM_HID_Private_Functions
  * @brief Private functions.
  * @{
  */

/* Private functions ---------------------------------------------------------*/
__IO uint32_t flashdestination = DEVICE_INFO_ADDRESS;
__IO uint32_t transfer_error=0;
unsigned char IAP_buff[64]={0};
uint8_t USB_Received_Flag=0;
static int8_t CUSTOM_HID_OutDulBuf_FS(uint8_t* DulBuf)//add /* add - 定义一个接收64字节的函数 */
{
	static uint8_t eraseAPPFlag = 0;	//APP页擦除标志
	unsigned char j=0;
	//memcpy(IAP_buff,DulBuf,64);//缓存64字节数据
	for(unsigned char i=0;i<64;i++)
	{
	 IAP_buff[i]=DulBuf[i];
	 if(IAP_buff[i]==0x00)
			j++;
	}
	if(j==64)
	{
		USB_Received_Flag=1;
		eraseAPPFlag = 0;	//APP页擦除标志复位
		flashdestination = DEVICE_INFO_ADDRESS;//地址复位
	}
	
//	if(j<64&&FLASH_If_Write(flashdestination, (uint32_t*)IAP_buff, (uint32_t)64/4)== FLASHIF_OK)
	if(j<64)	
  {
		if(eraseAPPFlag == 0)
		{
			eraseAPPFlag = 1;	//APP页擦除标志
			
			//擦除APP所有的空间
			IAP_FlashErase( DEVICE_INFO_ADDRESS );
		}
		//IAP写FLASH, 不用擦除和读动作, 只需要顺序写
		IAP_FlashWrite(flashdestination, IAP_buff, 64 );
//		FlashWriteBuff_Word(flashdestination, IAP_buff, 64);
		flashdestination+=(uint32_t)64;
  }
	else 
    transfer_error++;
	
    return (0);
}
  1. 在usbd_customhid.c 修改中断接收缓存:

重点关注这个函数,这个函数是完成中断接收缓存的,Report_buf[],我们插入
((USBD_CUSTOM_HID_ItfTypeDef *)pdev->pUserData)->OutDulBuf(hhid->Report_buf);完成64字节包的接收
//这个函数是完成中断接收缓存的
static uint8_t USBD_CUSTOM_HID_DataOut(USBD_HandleTypeDef *pdev,
uint8_t epnum)
{

USBD_CUSTOM_HID_HandleTypeDef *hhid = (USBD_CUSTOM_HID_HandleTypeDef *)pdev->pClassData;

((USBD_CUSTOM_HID_ItfTypeDef *)pdev->pUserData)->OutEvent(hhid->Report_buf[0],
hhid->Report_buf[1]);

((USBD_CUSTOM_HID_ItfTypeDef *)pdev->pUserData)->OutDulBuf(hhid->Report_buf); //add
USBD_LL_PrepareReceive(pdev, CUSTOM_HID_EPOUT_ADDR, hhid->Report_buf,
USBD_CUSTOMHID_OUTREPORT_BUF_SIZE);

return USBD_OK;
}
代码如下:

/**
  * @brief  USBD_CUSTOM_HID_DataOut
  *         handle data OUT Stage
  * @param  pdev: device instance
  * @param  epnum: endpoint index
  * @retval status
  */
//这个函数是完成中断接收缓存的
static uint8_t  USBD_CUSTOM_HID_DataOut(USBD_HandleTypeDef *pdev,
                                        uint8_t epnum)
{

  USBD_CUSTOM_HID_HandleTypeDef     *hhid = (USBD_CUSTOM_HID_HandleTypeDef *)pdev->pClassData;

  ((USBD_CUSTOM_HID_ItfTypeDef *)pdev->pUserData)->OutEvent(hhid->Report_buf[0],
                                                            hhid->Report_buf[1]);

  ((USBD_CUSTOM_HID_ItfTypeDef *)pdev->pUserData)->OutDulBuf(hhid->Report_buf);	//add
	USBD_LL_PrepareReceive(pdev, CUSTOM_HID_EPOUT_ADDR, hhid->Report_buf,
                         USBD_CUSTOMHID_OUTREPORT_BUF_SIZE);

  return USBD_OK;
}
  1. 在 usbd_customhid.h 中结构体上添加新增加的接收函数定义:
typedef struct _USBD_CUSTOM_HID_Itf
{
  uint8_t                  *pReport;
  int8_t (* Init)(void);
  int8_t (* DeInit)(void);
  int8_t (* OutEvent)(uint8_t event_idx, uint8_t state);
	int8_t (* OutDulBuf)(uint8_t* DulBuf);	//add

} USBD_CUSTOM_HID_ItfTypeDef;
  1. 在 usbd_desc.c 中可以修改VID,PID等参数
/** @defgroup USBD_DESC_Private_Defines USBD_DESC_Private_Defines
  * @brief Private defines.
  * @{
  */

#define USBD_VID     1155	//VID = 0x0483
#define USBD_LANGID_STRING     1033
#define USBD_MANUFACTURER_STRING     "STMicroelectronics"
#define USBD_PID_FS     22352		//PID = 0x5750
#define USBD_PRODUCT_STRING_FS     "STM32 Custom Human interface"
#define USBD_CONFIGURATION_STRING_FS     "Custom HID Config"
#define USBD_INTERFACE_STRING_FS     "Custom HID Interface"
  1. 在main.c 中添加头文件和 跳转至APP代码
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usb_device.h"
#include "gpio.h"
#include "usbd_customhid.h"
#include "usbd_custom_hid_if.h"
#include "flash.h"
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
extern USBD_HandleTypeDef hUsbDeviceFS;

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

pFunction JumpToApplication;
uint32_t JumpAddress;
/* USER CODE END 0 */
int main(void)
{
  /* USER CODE BEGIN 1 */
	uint32_t preticks;
	uint8_t ledTime;
//	uint8_t report[64] = {1,2,3,4,5,6,7,8};
  /* USER CODE END 1 */
  

  /* MCU Configuration--------------------------------------------------------*/
	__set_FAULTMASK(0);//开启所有中断
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

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

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USB_DEVICE_Init();
  /* USER CODE BEGIN 2 */

	//test
//	HAL_FLASH_Unlock();	// 解锁
//	FLASH_PageErase( 0x08010000 );
//	HAL_FLASH_Lock();  // 上锁
//	FlashWriteBuff_Word(0x08010000, TestWrite_buff, 4);
//	FlashReadBuff(0x08010000, TestRead_buff, 4);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
		
    /* USER CODE BEGIN 3 */
//		if(HAL_GetTick() - preticks > 1000)
//		{
//			preticks = HAL_GetTick();
////			USBD_CUSTOM_HID_SendReport(&hUsbDeviceFS, report, 64);
//			USBD_CUSTOM_HID_SendReport_FS(report, 64);
//		}
		if(HAL_GetTick() - preticks > 10)
		{
			preticks = HAL_GetTick();

			if(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin) != GPIO_PIN_SET)
			{
				if(((*(__IO uint32_t*)0x08010000) & 0x2FFE0000) == 0x20000000)
				{
					USBD_Stop(&hUsbDeviceFS);// 重点: 此处需关闭USB功能, 需要时再打开.
					HAL_RCC_DeInit(); //时钟失能
					HAL_DeInit();		  //外设失能
					__set_FAULTMASK(1);//关闭所用中断
					
					JumpAddress = *(__IO uint32_t*)(DEVICE_INFO_ADDRESS +4);//跳转到APP地址
					JumpToApplication = (pFunction) JumpAddress;
						
					__set_MSP(*(__IO uint32_t*)DEVICE_INFO_ADDRESS);//MSP指针跳转 
					JumpToApplication();
				}
			}
			
			//LED0闪烁
			if(++ledTime > 25)
			{
				ledTime = 0;
				HAL_GPIO_TogglePin(LED0_GPIO_Port,LED0_Pin);
			}
		}
  }
  /* USER CODE END 3 */
}

四、生成APP的bin文件
用keil编写一个闪灯的APP程序,点击魔术棒设置程序起始地址
STM32F103ZET6 - USB_HID -2 (IAP升级)_第13张图片
STM32F103ZET6 - USB_HID -2 (IAP升级)_第14张图片
另外,在APP程序的还要进行中断向量表的重映射,只要更改system_stm32fxx.c为以下配置即可。
STM32F103ZET6 - USB_HID -2 (IAP升级)_第15张图片
主循环while内写个简单的灯闪烁代码
STM32F103ZET6 - USB_HID -2 (IAP升级)_第16张图片
*注意: 在Main程序初始化前,需要先打开所有中断, __set_FAULTMASK(0);//开启所有中断
因为在BOOTLOADER中跳转前将所有中断都关闭了;

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
	__set_FAULTMASK(0);//开启所有中断
  /* USER CODE END 1 */
  

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

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

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
		HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
		HAL_Delay(500);
		HAL_GPIO_TogglePin(LED0_GPIO_Port, LED0_Pin);
		HAL_Delay(500);
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

附:APP生成.bin文件配置
C:\Keil_v5\ARM\ARMCC\bin\fromelf.exe --bin -o .\ledtest\ledtest.bin .\ledtest\ledtest.axf
STM32F103ZET6 - USB_HID -2 (IAP升级)_第17张图片
这样就成功生成了app.bin文件;

五、测试代码, 使用USB_IAP工具,此工具在下文会有下载链接地址。
此工具无单包的首,尾校验, 每包传输64字节数据; 只有在传输完成后,最后一包添加全为0的64字节的单包数据;
STM32F103ZET6 - USB_HID -2 (IAP升级)_第18张图片
打开USB_HID_IAP上位机后,填入VID,和PID; 然后打开设备,再选择APP的bin文件,最后点击发送就OK;

至此USB-HID_IAP 的APP和BootLoader程序的设计和测试就已经完成。

USB_HID_IAP 上位机工具下载地址: https://download.csdn.net/download/mimo6086/12405634
flash驱动代码下载地址: https://download.csdn.net/download/mimo6086/12089123
BootLoader代码下载地址: https://download.csdn.net/download/mimo6086/12264213

你可能感兴趣的:(STM32F103ZET6 - USB_HID -2 (IAP升级))