STM32 USB HID IAP升级

 

找了网上大量的资料,最后发现这个东西人家还出售源码。又不是什么算法级的东西,实在理解不了。

至于为什么要用HID,不用官方的DFU,因为驱动呀,DFU识别USB的时候还是要装驱动,客户你永远理解不了他的水平,所以研发需要cover住所有case.

我是在STM32F4的平台上做的,Cubemax配置工程。以前反感这个UI配置,现在这个东西BUG少了,以前定义一个局部的结构体变量都不初始化。下面是配置图,注意RCC配置USB的时钟。STM32 USB HID IAP升级_第1张图片

STM32 USB HID IAP升级_第2张图片

STM32 USB HID IAP升级_第3张图片

  CubeMax配置完生成代码打开,ST已经给你弄好代码框架,你只需要修改设备描述符,传输的字节,轮询的时间,中断回调函数接收。

__ALIGN_BEGIN static uint8_t CUSTOM_HID_ReportDesc_FS[USBD_CUSTOM_HID_REPORT_DESC_SIZE] __ALIGN_END =
{
  /* USER CODE BEGIN 0 */
		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,0x40, // 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,0x40, // REPORT_COUNT (64Byte)
    0x91,0x02, // OUTPUT (Data,Var,Abs)
  /* USER CODE END 0 */
  0xC0    /*     END_COLLECTION	             */
};

相关的宏修改 #define USBD_CUSTOM_HID_REPORT_DESC_SIZE     33U//2

#define CUSTOM_HID_EPIN_ADDR                 0x81U
#define CUSTOM_HID_EPIN_SIZE                 0x40U      //64字节

#define CUSTOM_HID_EPOUT_ADDR                0x01U
#define CUSTOM_HID_EPOUT_SIZE                0x40U

#define CUSTOM_HID_FS_BINTERVAL            0x01U//1ms轮询

这个是USB的中断接收函数,根据USB设备的ID来接收字节,库生成的时候只能接收2个字节的,

当我们改成0x40,就能接收64个字节,USB HID一包只能64个字节

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
};

static int8_t CUSTOM_HID_OutDulBuf_FS (uint8_t* DulBuf)
{

    return (0);
}

重点关注这个函数,这个函数是完成中断接收缓存的,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);
	
  USBD_LL_PrepareReceive(pdev, CUSTOM_HID_EPOUT_ADDR , hhid->Report_buf,
                         USBD_CUSTOMHID_OUTREPORT_BUF_SIZE);
	
  
	
  return USBD_OK;
}

自此,USB HID64字节包的接收我们就完成,关于HID的发送,官方声明了一个static函数,注释掉了,调用发送就好了

有些宏定义的查找,不需要去翻文件了,编译然后goto definition

/*
static int8_t USBD_CUSTOM_HID_SendReport_FS(uint8_t *report, uint16_t len)
{
  return USBD_CUSTOM_HID_SendReport(&hUsbDeviceFS, report, len);
}
*/

上面的工作已经完成了USB HID的双向通信。重点来,IAP升级,我之前用官方的串口IAP升级做过,用的Ymodem协议传输,波特率921600,Ymodem我们可以使用secureCRT去进行升级传输,官方库的下位机Ymodem已经做的很好了,也有防错机制。现在我们用USB传输,64字节一包,尴尬的是没有上位机去升级,网上或许有下载,但是人家有握手协议,以及包协议,所以基本不能用。由于没有上位机开发经验,甚至一度下载了QT Crator准备开发,后面思考了下还是偷懒了,我用BUS Hound去抓包人家上位机传输的USB格式,关键是这个USB上位机只负责发,没有握手协议。还是被我下载到了。

STM32 USB HID IAP升级_第4张图片

当你的板子下载程序USB HID识别成功之后,输入VID 0483  PID 5750 连接打开设备。

下面是stm32 APP端的bin文件输出了,在app里面重新映射中断向量表,以及代码flash段,我的是Loader 0x4000 16K

所以APP的开始地址是0x08004000 然后在工程选项下的C++栏 生成bin文件选项 记得勾选After build。

生成app端的bin文件之后,需要想的是,这个USB上位机虽然不握手,只负责发送,但是人家到底有没有发送格式呢,这就需要我们对比它发送的文件,以及我们bin文件的内容。所以用到了visual studio2015 和 Bus hound两个工具,嵌入式工程师必备吧,工具的使用是人类的进化的标志。STM32 USB HID IAP升级_第5张图片

咳咳,暴露了我双显示器的配置。 对比发现这个USB上位机就是发送的bin文件,发送完成最后发了一个空包,全是0,这就好办了,空包作为传输结束标志。

下面的函数就是缓冲接收64字节包的代码,这只是demo,在中断回调里面缓冲,并且写了flash,道理上是不合理的哈。

但是loder又不需要多任务处理,干就完事了。调试的时候思路卡了一会在flash烧写的地方,一个是需要先擦除app位置的flash,另一个flash的API需要32位对齐,所以需要64/4,其它就没啥了

__IO uint32_t flashdestination=APPLICATION_ADDRESS;
__IO uint32_t transfer_error=0;
static int8_t CUSTOM_HID_OutDulBuf_FS (uint8_t* DulBuf)
{
	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;
	
	if(j<64&&FLASH_If_Write(flashdestination, (uint32_t*)IAP_buff, (uint32_t)64/4)== FLASHIF_OK)
  {
     flashdestination+=(uint32_t)64;
  }
	else 
    transfer_error++;

	return (0);
}

再上传一个loader跳转到APP,APP跳转到Loader的代码

loader跳转到APP __set_FAULTMASK(1);//关闭所用中断 至于考虑APP是否使用了RTOS,加上__set_CONTROL(0); 吧

void LoderToApp(void)
{
	if (((*(__IO uint32_t*)APPLICATION_ADDRESS) & 0x2FFE0000 ) == 0x20000000)//栈顶地址检查
	{
		HAL_RCC_DeInit(); //时钟失能
		HAL_DeInit();		  //外设失能
		__set_FAULTMASK(1);//关闭所用中断
		JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4);//跳转到APP地址
		JumpToApplication = (pFunction) JumpAddress;
		__set_CONTROL(0);  //RTOS指针使能
		SCB->VTOR = FLASH_BASE |0x4000;//中断向量表重映射
		__set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS);//MSP指针跳转 
		JumpToApplication();
	}
}
void AppToLoder(void)
{
	  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_RESET);
   __set_FAULTMASK(1);
	  HAL_NVIC_SystemReset();
}

APP跳转到Loader就简单点吧HAL_NVIC_SystemReset();干脆高效

 

致敬开源

         --骚年追梦

 

上位机:https://download.csdn.net/download/u014803614/11603107

下位机:https://download.csdn.net/download/u014803614/11603069

 

 

 

 

 

你可能感兴趣的:(stm32)