利用STM32CubeMX软件生成USB_HOST_CDC驱动ME909s-821ap(4G通信模块)

一、测试平台:
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 函数,经过测试发现,当使用ME909通信模块时,可以不用修改此函数,用原函数即可。当使用EC20通信模块时,必须要修改此函数,否则将导致无法发送AT指令。

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;
}

在这里插入图片描述

  • 5
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: STM32CubeMX软件是一款用于生成STM32微控制器应用程序的图形化设计工具。使用它可以快速配置STM32的硬件,并生成符合STM32标准的C/C++代码,以及相关的配置文件。要生成STM32的SPI驱动程序,首先需要在STM32CubeMX软件中配置SPI的硬件,然后生成C/C++代码并编译,最后将编译好的文件链接到相应的应用程序中即可。 ### 回答2: STM32CubeMX软件是针对ST微电子的STM32系列微控制器的开发工具。它提供了一个图形化的界面,使得开发人员可以轻松地生成C代码的启动文件和初始化配置,以及相关外设驱动程序。 要使用STM32CubeMX软件生成STM32的SPI驱动程序,可以按照以下步骤进行操作: 1. 下载和安装STM32CubeMX软件,并启动它。 2. 在工程窗口中选择要使用的STM32微控制器型号,并创建一个新项目。 3. 在配置栏中选择"GPIO"选项卡,启用所需的SPI引脚。 4. 在配置栏中选择"RCC"选项卡,配置系统时钟和外设时钟使能。 5. 在配置栏中选择"SPI"选项卡,并启用SPI外设,并进行相关配置,如主从模式、传输速率、数据位数等。 6. 在NVIC配置中启用SPI外设中断(如果需要)。 7. 点击"生成代码"按钮,STM32CubeMX生成相关的C代码并自动保存。 8. 在生成的代码中,可以找到SPI初始化函数,例如"HAL_SPI_Init()",在主函数中调用此函数进行SPI外设的初始化。 9. 通过适当的SPI发送和接收函数调用,可以在应用程序中使用SPI外设进行通信。 总结来说,使用STM32CubeMX软件生成STM32的SPI驱动程序的步骤包括选择目标微控制器型号、配置SPI引脚和参数、生成代码并保存、初始化SPI外设,并在应用程序中使用SPI函数进行通信。这个工具可以大大简化SPI驱动程序的生成过程,提高开发效率。 ### 回答3: 使用STM32CubeMX软件生成STM32的SPI驱动程序很简单。下面是具体的步骤: 1. 下载并安装STM32CubeMX软件,并打开软件。 2. 在软件界面的左侧,选择需要使用的STM32系列微控制器。 3. 在"Pinout & Configuration"选项卡中,选择需要使用的SPI接口。如果需要使用多个SPI接口,可以在"Peripherals"列表中选择并启用多个SPI接口。 4. 在"Pinout & Configuration"选项卡中,为SPI接口选择引脚。单击需要选择的引脚,在弹出的菜单中选择对应的引脚功能。确保引脚选择符合硬件连接的需求。 5. 在"Configuration"选项卡中,设置SPI的各种参数。这些参数包括时钟极性、时钟相位、数据位长度、数据传输模式等。如果需要使用硬件NSS信号,也可以在这里设置。 6. 在"Initialization"选项卡中,选择SPI的初始化方法。可以选择使用HAL库还是LL库初始化SPI。 7. 在"Preparation"选项卡中,可以进行其他相关设置,比如中断优先级等。 8. 确认所有的设置后,单击界面下方的"GENERATE CODE"按钮,生成工程代码。 9. 打开生成的工程代码,找到SPI驱动程序所在的文件。通常情况下,SPI驱动程序会在"main.c"或者其他自动生成的文件中。 10. 根据生成的代码,调用对应的函数来控制SPI接口进行数据传输。通常情况下,生成的代码已经包含了SPI的初始化和数据传输的相关函数。 总之,使用STM32CubeMX软件生成STM32的SPI驱动程序只需要进行简单的配置和参数设置,然后点击生成代码即可。生成的代码中已经包含了SPI的初始化和数据传输函数,可以直接调用和使用。使用这些生成的函数,可以轻松地控制STM32的SPI接口进行数据传输。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值