STM32 HAL庫之USB

前言

stm32f1xx_hal_msp.c 文件定義了兩個函數 HAL_MspInit 和 HAL_MspDeInit。這兩個函數分別被文件 stm32f1xx_hal.c 中的 HAL_Init 和 HAL_DeInit 所調用。HAL_MspInit 函數的主要作用是進行 MCU相關的硬件初始化操作。例如我們要初始化某些硬件,我們可以硬件相關的初始化配置 寫在HAL_MspDeinit 函數中。這樣的話,在系統啓動後調用了 HAL_Init 之後,會自動調用硬件初始化函數。
實際上,我們在工程模板中直接刪掉 stm32f1xx_hal_msp.c 文件也不會對程序運行產生任何影響。

1.函數組成

在這裏插入圖片描述
main.c裏面僅包含一個USB設備函數初始化函數 MX_USB_DEVICE_Init(),在程序開始時調用。
usbd_cdc_interface.c爲USB的CDC類應用層文件,裏面包含虛擬串口的接收,發送和控制等函數。
usb_desc.c 包含USB的描述符,以及USB枚舉處理等函數。
usb_conf.c 爲USB管腳配置文件,包含引USB引腳初始化以及參數設置,中斷回調函數等。

2.初始化

定義USB結構體句柄
STM32的標準庫中,句柄是一種特殊的指針,通常指向結構體!

USBD_HandleTypeDef hUsbDeviceFS

在HAL庫中,USBD初始化結構體變量,我們要定義爲全局變量。所以說上述代碼放在函數外邊。句柄用於管理進程例程之間的共享數據資源,查看結構體USBD_HandleTypeDef成員:

typedef struct _USBD_HandleTypeDef
{
  uint8_t                 id;
  uint32_t                dev_config;
  uint32_t                dev_default_config;
  uint32_t                dev_config_status; 
  USBD_SpeedTypeDef       dev_speed; 
  USBD_EndpointTypeDef    ep_in[15];
  USBD_EndpointTypeDef    ep_out[15];  
  uint32_t                ep0_state;  
  uint32_t                ep0_data_len;     
  uint8_t                 dev_state;
  uint8_t                 dev_old_state;
  uint8_t                 dev_address;
  uint8_t                 dev_connection_status;  
  uint8_t                 dev_test_mode;
  uint32_t                dev_remote_wakeup;

  USBD_SetupReqTypedef    request;
  USBD_DescriptorsTypeDef *pDesc;
  USBD_ClassTypeDef       *pClass;
  void                    *pClassData;  
  void                    *pUserData;    
  void                    *pData;    
} USBD_HandleTypeDef;

聲明用戶自定義變量

//usbd_cdc_if.c
#define APP_RX_DATA_SIZE  2048
#define APP_TX_DATA_SIZE  2048
//通過USB接收的數據存儲在此緩衝區中
uint8_t UserRxBufferFS[APP_RX_DATA_SIZE];
//通過USB CDC發送的數據存儲在此緩衝區中
uint8_t UserTxBufferFS[APP_TX_DATA_SIZE];

USB初始化

//usb_device.c 
void MX_USB_DEVICE_Init(void)
{
	if (USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS) != USBD_OK)
	{
		Error_Handler();
	}
	if (USBD_RegisterClass(&hUsbDeviceFS, &USBD_CDC) != USBD_OK)
	{
		Error_Handler();
	}
	if (USBD_CDC_RegisterInterface(&hUsbDeviceFS, &USBD_Interface_fops_FS) != USBD_OK)
	{
		Error_Handler();
	}
	if (USBD_Start(&hUsbDeviceFS) != USBD_OK)
	{
		Error_Handler();
	}
}

初始化函數第三步如下:

uint8_t  USBD_CDC_RegisterInterface  (USBD_HandleTypeDef   *pdev, USBD_CDC_ItfTypeDef *fops)
{
  uint8_t  ret = USBD_FAIL;
  
  if(fops != NULL)
  {
    pdev->pUserData= fops;
    ret = USBD_OK;    
  }
  
  return ret;
}

USBD_CDC_ItfTypeDef結構體定義如下:
有四個成員,分別是四個函數指針

typedef struct _USBD_CDC_Itf
{
  int8_t (* Init)          (void);
  int8_t (* DeInit)        (void);
  int8_t (* Control)       (uint8_t, uint8_t * , uint16_t);   
  int8_t (* Receive)       (uint8_t *, uint32_t *);  

}USBD_CDC_ItfTypeDef;
USBD_CDC_ItfTypeDef USBD_Interface_fops_FS = 
{
  CDC_Init_FS,
  CDC_DeInit_FS,
  CDC_Control_FS,  
  CDC_Receive_FS
};
  • int8_t CDC_Init_FS(void)
    初始化CDC媒體底層,設置了收發Buffer
static int8_t CDC_Init_FS(void)
{
  /* Set Application Buffers */
  USBD_CDC_SetTxBuffer(&hUsbDeviceFS, UserTxBufferFS, 0);
  USBD_CDC_SetRxBuffer(&hUsbDeviceFS, UserRxBufferFS);
  return (USBD_OK);
}
  • static int8_t CDC_Control_FS (uint8_t cmd, uint8_t* pbuf, uint16_t length)
    CDC控制命令處理,列舉了主機有可能向設備發送的一些命令。沒有具體的處理過程,需要用戶自己編寫。其中包括串口參數的設置,要做串口轉USB通信的話需要修改這裏。只是爲了用USB與PC通信則不用管這裏。每個命令具體的意思需要查詢CDC類手冊。
static int8_t CDC_Control_FS  (uint8_t cmd, uint8_t* pbuf, uint16_t length)
{ 
  switch (cmd)
  {
  case CDC_SEND_ENCAPSULATED_COMMAND:
 
    break;
  case CDC_GET_ENCAPSULATED_RESPONSE:
 
    break;
  case CDC_SET_COMM_FEATURE:
 
    break;
  case CDC_GET_COMM_FEATURE:

    break;
  case CDC_CLEAR_COMM_FEATURE:

    break;
  case CDC_SET_LINE_CODING:   
    
    break;
  case CDC_GET_LINE_CODING:     

    break;
  case CDC_SET_CONTROL_LINE_STATE:

    break;
  case CDC_SEND_BREAK:
 
    break;    
  default:
  
    break;
  }

  return (USBD_OK);
}

  • int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
    虛擬串口接收函數,Buf爲接收緩存。這個緩存實際上就是CDC_Init_FS()中設置的UserRxBufferFS[]數組。這個全局數組的定義在usbd_cdc_if.c文件中。Len爲接收到數據的長度。這個變量不是全局的,需要用戶聲明變量把這個傳出去。
static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
	USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
	USBD_CDC_ReceivePacket(&hUsbDeviceFS);
	return (USBD_OK);
}

注意:CDC_Receive_FS()是接收函數。這個函數不需要調用。直接在函數中添加代碼把接受到的數據和數據長度複製到自己定義的接收緩存。

usbd_cdc_if.c中CDC_Transmit_FS()是發送函數。要發送時調用這個函數,需要傳入待發送數據的指針和長度。

//處理數據函數
void HandleReceiveData (uint8_t* Buf, uint32_t *Len)
{
    CDC_Transmit_FS(Buf,(uint16_t)Len);
}

參考

參考:
https://www.jianshu.com/p/82f277d0fe2b
https://www.stmcu.com.cn/Designresource/design_resource_detail
https://blog.csdn.net/xuzhexing/article/details/90137754
https://blog.csdn.net/king_jie0210/article/details/76713938

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章