天天看點

STM32 USB HID IAP更新

找了網上大量的資料,最後發現這個東西人家還出售源碼。又不是什麼算法級的東西,實在了解不了。

至于為什麼要用HID,不用官方的DFU,因為驅動呀,DFU識别USB的時候還是要裝驅動,客戶你永遠了解不了他的水準,是以研發需要cover住所有case.

我是在STM32F4的平台上做的,Cubemax配置工程。以前反感這個UI配置,現在這個東西BUG少了,以前定義一個局部的結構體變量都不初始化。下面是配置圖,注意RCC配置USB的時鐘。

STM32 USB HID IAP更新
STM32 USB HID IAP更新
STM32 USB HID IAP更新

  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)

{

  return (USBD_OK);

}

梳理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

上面的工作已經完成了USB HID的雙向通信。重點來,IAP更新,我之前用官方的序列槽IAP更新做過,用的Ymodem協定傳輸,波特率921600,Ymodem我們可以使用secureCRT去進行更新傳輸,官方庫的下位機Ymodem已經做的很好了,也有防錯機制。現在我們用USB傳輸,64位元組一包,尴尬的是沒有上位機去更新,網上或許有下載下傳,但是人家有握手協定,以及包協定,是以基本不能用。由于沒有上位機開發經驗,甚至一度下載下傳了QT Crator準備開發,後面思考了下還是偷懶了,我用BUS Hound去抓包人家上位機傳輸的USB格式,關鍵是這個USB上位機隻負責發,沒有握手協定。還是被我下載下傳到了。

STM32 USB HID IAP更新

當你的闆子下載下傳程式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更新

咳咳,暴露了我雙顯示器的配置。 對比發現這個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

繼續閱讀