一、测试平台: MCU:STM32F429IGT6 工具:STM32CubeMX软件 编译软件:MDK
二、配置步骤 (1).打开STM32CubeMX软件,创建新的工程文件,先生成一个不带操作系统的串口1例程,生成串口的例程这里不再详细介绍。 (2).由于测试通信模块时需要给通信模块发送AT命令,所以在这里我们将串口1接上电脑,通过电脑串口软件下发AT命令,串口1接收中断收到数据之后将AT命令通过USB接口转发给4G通信模块。由于AT命令都是以0x0D 0x0A结束,所以当串口收到0x0A时,则所以数据接收完成,之后将数据转发给4G通信模块。 (3).编写串口1接收中断函数,实现串口1收到以0x0A结尾的数据之后,将数据回传到电脑端串口软件中。由于STM32CubeMX生成的工程中没有开启串口1接收中断,在这里添加上。 __HAL_UART_ENABLE_IT(&huart1,UART_IT_RXNE); 添加串口1接收和发送的函数 ///重定向c库函数printf到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); } } 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 */ } (4).测试串口1的接收和发送,电脑串口1软件下发0x0A结尾的AT命令,串口1可以接收到下发的命令。 (5).配置USB_OTG,由于电路板上的USB引出来的管脚是PB14,PB15,所以在这里需要配置USB_OTG_HS,由于电路板上没有外接Phy,所以这里配置为内部的FS Phy,Internal FS Phy选项下Host Only,同时配置中断。 (6).配置USB_HOST,由于4G通信模块是CDC设备,所以在这里配置为Class For HS IP项下选择Communication Host Class(Virtual Port Com)。因为ME909s模块连接后,配置描述符下一共有5个 Interface , 并且 Interface 中至多有3个Endpoint(下面简称Ep)。所以 USBH_MAX_NUM_ENDPOINTS 需配置 >3 , USBH_MAX_NUM_INTERFACES 需配置 >5.。 (7).生成代码之后,修改classcode,由于ME909s厂商自定义的classcode是0xFF,所以将USB_CDC_CLASS宏定义修改为0xFF。 #define USB_CDC_CLASS 0x02 #define USB_CDC_CLASS 0xFF //厂商自定义classcode (8).修改usbh_cdc.c 中的 USBH_CDC_InterfaceInit 函数,由于ME909s用的是接口0,所以这里接口为0,interface = USBH_FindInterfaceIndex(phost, 0, 0);若为EC20通信模块,则为接口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).修改 USBH_CDC_ClassRequest 函数,经过测试发现,此函数可以不用修改,用原函数即可。 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; } (10).修改usb_host.c文件中的MX_USB_HOST_Process(void)函数 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); } } } (11).usb_host.c文件中重定义 USBH_CDC_ReceiveCallback 函数 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);
} (12).修改USBH_UsrLog宏定义 #define USBH_UsrLog(…) do { printf(“USBH_UsrLog: “) ; printf(VA_ARGS); printf(”\n”); } while (0) (13).将串口1收到的数据通过USB转发给4G通信模块。 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); } } (14).通过串口软件下发AT命令,可以收到正确的回复。 (15).若为带freertos操作系统的版本,则需要在USB任务中添加MX_USB_HOST_Process()函数。 (16).特别注意,测试带有FreeRtos的USB_HOST_CDC驱动4G通信模块,当断开4G通信模块之后,重新插上4G通信模块,可以枚举到4G通信模块,但是发送AT命令无回应。经测试完毕,是因为重新插上之后USBH_CDC_Receive(&hUsbHostFS, UsbRxdData, USB_BUF_LEN)函数没有执行到,Debug调试发现是因为重新插上之后CDC_Handle->data_rx_state变量不等于,而等于CDC_RECEIVE_DATA_WAIT,为了解决这个问题,在4G模块枚举成功之后,重置CDC_Handle->data_rx_state值为CDC_IDLE。在USBH_UsrLog(“%s class started.”, phost->pActiveClass->Name);处添加RESET_USB_CDC(); void RESET_USB_CDC(void) { CDC_HandleTypeDef *CDC_Handle = hUsbHostFS.pActiveClass->pData; CDC_Handle->data_rx_state = CDC_IDLE; }
|