Use STM32CubeMX software to generate USB_HOST_CDC driver ME909s-821ap (4G communication module)

1. Test platform:
MCU: STM32F429IGT6
Tool: STM32CubeMX software
Compilation software: MDK

2. Configuration steps
(1). Open the STM32CubeMX software, create a new project file, and generate a serial port 1 routine without an operating system. The routine for generating a serial port will not be described in detail here.
(2). Since it is necessary to send AT commands to the communication module when testing the communication module, here we connect the serial port 1 to the computer, and issue the AT command through the computer serial port software. After the serial port 1 receives the interrupt and receives the data, the AT command will be sent through the USB. The interface is forwarded to the 4G communication module. Since the AT commands all end with 0x0D 0x0A, when the serial port receives 0x0A, the data reception is completed, and then the data is forwarded to the 4G communication module.
(3). Write the serial port 1 receiving interrupt function to realize that after the serial port 1 receives the data ending with 0x0A, it will return the data to the serial port software on the computer. Since the serial port 1 receiving interrupt is not enabled in the project generated by STM32CubeMX, add it here.
__HAL_UART_ENABLE_IT(&huart1,UART_IT_RXNE);
insert image description here
Add serial port 1 receiving and sending functions
///Redirect c library function printf to USART1

int fputc(int ch, FILE *f)
{
    
    
		unsigned char bCh=0;
		bCh=ch;
		HAL_UART_Transmit(&huart1,&bCh,1,10);
		return (ch);
}

unsigned char TxdData[UART_BUF_LEN];
unsigned char bRxdFinishFlag=0;
unsigned char RxdData[UART_BUF_LEN];
unsigned char bRxLen=0;
unsigned char UsbRxdData[USB_BUF_LEN];

void Test_USART_TXRX(void)
{
    
    
	if(1==bRxdFinishFlag)
	{
    
    
		bRxdFinishFlag=0;
		HAL_UART_Transmit(&huart1,RxdData,bRxLen,100);
	}
}

insert image description here

void USART1_IRQHandler(void)
{
    
    
  /* USER CODE BEGIN USART1_IRQn 0 */
	uint8_t  bData=0;
	static uint8_t bCnt=0;
	
	if(__HAL_UART_GET_FLAG( &huart1, UART_FLAG_RXNE ) != RESET)
	{
    
    		
    bData=( uint8_t)READ_REG(huart1.Instance->DR);
		RxdData[bCnt++]=bData;
		if(RxdData[bCnt-1]==0x0A)
		{
    
    
			bRxLen=bCnt;
			bCnt=0;
			bRxdFinishFlag=1;
		}   
	}
  /* USER CODE END USART1_IRQn 0 */
//  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 1 */
}

insert image description here
(4). Test the receiving and sending of serial port 1. The computer serial port 1 software sends AT commands ending in 0x0A, and serial port 1 can receive the issued commands.
insert image description here
(5). Configure USB_OTG. Since the pins of USB on the circuit board are PB14 and PB15, USB_OTG_HS needs to be configured here. Since there is no external Phy on the circuit board, it is configured as internal FS Phy, Internal FS Phy. Under the option Host Only, configure the interrupt at the same time.
insert image description here
(6). Configure USB_HOST. Since the 4G communication module is a CDC device, configure it here as Class For HS IP and select Communication Host Class (Virtual Port Com). Because after the ME909s module is connected, there are 5 Interfaces under the configuration descriptor, and there are at most 3 Endpoints (hereinafter referred to as Ep) in the Interface. So USBH_MAX_NUM_ENDPOINTS needs to be configured >3, USBH_MAX_NUM_INTERFACES needs to be configured >5.
insert image description here
(7). After the code is generated, modify the classcode. Since the classcode customized by the ME909s manufacturer is 0xFF, modify the USB_CDC_CLASS macro definition to 0xFF.
#define USB_CDC_CLASS 0x02
#define USB_CDC_CLASS 0xFF //Manufacturer's custom classcode
insert image description here
(8). Modify the USBH_CDC_InterfaceInit function in usbh_cdc.c. Since ME909s uses interface 0, the interface here is 0, interface = USBH_FindInterfaceIndex(phost, 0, 0 ); if it is an EC20 communication module, it is interface 2, interface = USBH_FindInterfaceIndex(phost, 2, 0);

static USBH_StatusTypeDef USBH_CDC_InterfaceInit(USBH_HandleTypeDef *phost)
{
    
    
 
	USBH_StatusTypeDef status = USBH_FAIL;
	uint8_t interface;
	CDC_HandleTypeDef *CDC_Handle;
 
	// 默认系统配置标准CDC接口
//  interface = USBH_FindInterface(phost,
//  															 USB_CDC_CLASS,
//                                 ABSTRACT_CONTROL_MODEL,
//                                 COMMON_AT_COMMAND);
 
	/**
	 * 注:
	 * cubemx生成的例程中,标准的CDC类设备,1个配置描述符中需要2接口
	 * 其中一个为Communication Interface Class, 该接口需要一个方向为in的Ep
	 * 另外一个为Data Interface Class, 该接口需要一个方向为in的Ep和一个方向为out的Ep
	 * 所以USBH_CDC_InterfaceInit函数,调用了两次USBH_FindInterface函数
	 * 查找两个匹配的Interface, 分别进行配置
	 *
	 * USB-TTL串口工具,debug状态下查询设备描述符结构体中,只有一个接口
	 * 但是该接口拥有3个Ep, 2个方向为in, 1个方向为out.
	 * 由此猜测,串口工具并没有将Interface分开
	 * 经测试, Communication Interface使用的Ep为2, Data Interface使用Ep0,1
	 *
	 * Ec20模块,可以读到5个Interface,但是只有Interface 1 2 3 有3个Ep,0 和 4 只有2个Ep
	 * 经测试,接口AT指令的串口Interface为 2.
	 * Interface 2中,Communication Interface使用的Ep为0
	 * Data Interface使用的Ep为1 和 2
	 */
 
	// USB-TTL串口工具接口配置
//	interface = USBH_FindInterface(phost,
//	USER_USB_CDC_CLASS,
//	DIRECT_LINE_CONTROL_MODEL, 02);
	// 移远4G模块接口配置
	interface = USBH_FindInterfaceIndex(phost, 0, 0);
 
	if (interface == 0xFFU) /* No Valid Interface */
	{
    
    
		USBH_DbgLog("Cannot Find the interface for Communication Interface Class.",
				phost->pActiveClass->Name);
	}
	else
	{
    
    
		USBH_SelectInterface(phost, interface);
		phost->pActiveClass->pData = (CDC_HandleTypeDef*) USBH_malloc(
				sizeof(CDC_HandleTypeDef));
		CDC_Handle = (CDC_HandleTypeDef*) phost->pActiveClass->pData;
 
		/*Collect the notification endpoint address and length*/
		if (phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].bEndpointAddress
				& 0x80U)
		{
    
    
			CDC_Handle->CommItf.NotifEp =
					phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].bEndpointAddress;
			CDC_Handle->CommItf.NotifEpSize =
					phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].wMaxPacketSize;
		}
 
		/*Allocate the length for host channel number in*/
		CDC_Handle->CommItf.NotifPipe = USBH_AllocPipe(phost,
				CDC_Handle->CommItf.NotifEp);
 
		/* Open pipe for Notification endpoint */
		USBH_OpenPipe(phost, CDC_Handle->CommItf.NotifPipe,
				CDC_Handle->CommItf.NotifEp, phost->device.address, phost->device.speed,
				USB_EP_TYPE_INTR, CDC_Handle->CommItf.NotifEpSize);
 
		USBH_LL_SetToggle(phost, CDC_Handle->CommItf.NotifPipe, 0U);
 
		// 默认系统配置标准CDC接口
//		interface = USBH_FindInterface(phost,
//		DATA_INTERFACE_CLASS_CODE,
//		RESERVED,
//		NO_CLASS_SPECIFIC_PROTOCOL_CODE);
 
		if (interface == 0xFFU) /* No Valid Interface */
		{
    
    
			USBH_DbgLog("Cannot Find the interface for Data Interface Class.",
					phost->pActiveClass->Name);
		}
		else
		{
    
    
			/*Collect the class specific endpoint address and length*/
			if (phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[2].bEndpointAddress
					& 0x80U)
			{
    
    
				CDC_Handle->DataItf.InEp =
						phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[2].bEndpointAddress;
				CDC_Handle->DataItf.InEpSize =
						phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[2].wMaxPacketSize;
			}
			else
			{
    
    
				CDC_Handle->DataItf.OutEp =
						phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[2].bEndpointAddress;
				CDC_Handle->DataItf.OutEpSize =
						phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[2].wMaxPacketSize;
			}
 
			if (phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[1].bEndpointAddress
					& 0x80U)
			{
    
    
				CDC_Handle->DataItf.InEp =
						phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[1].bEndpointAddress;
				CDC_Handle->DataItf.InEpSize =
						phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[1].wMaxPacketSize;
			}
			else
			{
    
    
				CDC_Handle->DataItf.OutEp =
						phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[1].bEndpointAddress;
				CDC_Handle->DataItf.OutEpSize =
						phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[1].wMaxPacketSize;
			}
 
			/*Allocate the length for host channel number out*/
			CDC_Handle->DataItf.OutPipe = USBH_AllocPipe(phost,
					CDC_Handle->DataItf.OutEp);
 
			/*Allocate the length for host channel number in*/
			CDC_Handle->DataItf.InPipe = USBH_AllocPipe(phost,
					CDC_Handle->DataItf.InEp);
 
			/* Open channel for OUT endpoint */
			USBH_OpenPipe(phost, CDC_Handle->DataItf.OutPipe,
					CDC_Handle->DataItf.OutEp, phost->device.address, phost->device.speed,
					USB_EP_TYPE_BULK, CDC_Handle->DataItf.OutEpSize);
			/* Open channel for IN endpoint */
			USBH_OpenPipe(phost, CDC_Handle->DataItf.InPipe, CDC_Handle->DataItf.InEp,
					phost->device.address, phost->device.speed,
					USB_EP_TYPE_BULK, CDC_Handle->DataItf.InEpSize);
 
			CDC_Handle->state = CDC_IDLE_STATE;
 
			USBH_LL_SetToggle(phost, CDC_Handle->DataItf.OutPipe, 0U);
			USBH_LL_SetToggle(phost, CDC_Handle->DataItf.InPipe, 0U);
			status = USBH_OK;
		}
	}
	return status;
}

(9). Modify the USBH_CDC_ClassRequest function. After testing, it is found that this function does not need to be modified, and the original function can be used.

static USBH_StatusTypeDef USBH_CDC_ClassRequest(USBH_HandleTypeDef *phost)
{
    
    
	USBH_StatusTypeDef status = USBH_FAIL;
	CDC_HandleTypeDef *CDC_Handle =
			(CDC_HandleTypeDef*) phost->pActiveClass->pData;
//	/*Issue the get line coding request*/
//	status = GetLineCoding(phost, &CDC_Handle->LineCoding);
	CDC_Handle->data_rx_state = CDC_IDLE;
	CDC_Handle->data_tx_state = CDC_IDLE;
	CDC_Handle->LineCoding.b.bCharFormat = 0;
	CDC_Handle->LineCoding.b.bDataBits = 8;
	CDC_Handle->LineCoding.b.bParityType = 0;
	CDC_Handle->LineCoding.b.dwDTERate = 115200;
	status = USBH_OK;
	if (status == USBH_OK)
	{
    
    
		phost->pUser(phost, HOST_USER_CLASS_ACTIVE);
	}
	return status;
}

insert image description here
(10). Modify the MX_USB_HOST_Process(void) function in the usb_host.c file

void MX_USB_HOST_Process(void)
{
    
    
	CDC_HandleTypeDef *CDC_Handle = hUsbHostHS.pActiveClass->pData;
  /* USB Host Background task */
  USBH_Process(&hUsbHostHS);
  if (hUsbHostHS.gState == HOST_CLASS)
    {
    
    
    	if (CDC_Handle->data_rx_state == CDC_IDLE)
    	{
    
    
    		USBH_CDC_Receive(&hUsbHostHS, UsbRxdData, USB_BUF_LEN);
    	}
    }
}

insert image description here
(11) Redefine the USBH_CDC_ReceiveCallback function in the .usb_host.c file

void USBH_CDC_ReceiveCallback(USBH_HandleTypeDef *phost)
{
    
    
	unsigned char len_rec=0;
	
	len_rec = USBH_CDC_GetLastReceivedDataSize(phost);
	HAL_UART_Transmit(&huart1,UsbRxdData,len_rec,100);
}

insert image description here
(12). Modify the USBH_UsrLog macro definition

#define USBH_UsrLog(...) do {
      
      	\
                            printf("USBH_UsrLog: ") ; \
                            printf(__VA_ARGS__); \
                            printf("\n"); \
} while (0)	

insert image description here
(13). Forward the data received by serial port 1 to the 4G communication module through USB.

void Test_USART_TXRX(void)
{
    
    
	if(1==bRxdFinishFlag)
	{
    
    
		bRxdFinishFlag=0;
	//	HAL_UART_Transmit(&huart1,RxdData,bRxLen,100);
		USBH_CDC_Transmit(&hUsbHostHS, (uint8_t *)RxdData, bRxLen);
	}
}

insert image description here
(14). Send the AT command through the serial port software, and you can receive the correct reply.
insert image description here
(15). For the version with freertos operating system, you need to add the MX_USB_HOST_Process() function in the USB task.
insert image description here
(16). Special attention, test the USB_HOST_CDC with FreeRtos to drive the 4G communication module. After disconnecting the 4G communication module and replugging the 4G communication module, the 4G communication module can be enumerated, but there is no response when sending AT commands. After testing, it is because the USBH_CDC_Receive(&hUsbHostFS, UsbRxdData, USB_BUF_LEN) function is not executed after re-plugging. Debugging found that the CDC_Handle->data_rx_state variable is not equal to CDC_RECEIVE_DATA_WAIT after re-plugging. In order to solve this problem, in After the 4G module is successfully enumerated, reset the value of CDC_Handle->data_rx_state to CDC_IDLE. Add RESET_USB_CDC() at USBH_UsrLog(“%s class started.”, phost->pActiveClass->Name);
insert image description here

void RESET_USB_CDC(void)
{
    
    
	CDC_HandleTypeDef *CDC_Handle = hUsbHostFS.pActiveClass->pData;
	CDC_Handle->data_rx_state = CDC_IDLE;
}

insert image description here

Guess you like

Origin blog.csdn.net/qizhi321123/article/details/126622434