前言
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