开局叠甲:纯新手第一次弄,有哪里做错了的,欢迎指正
1.关于STM32的USB的原理讲述的文章多了去了,有需要的可以去了解一下先,本文主要是讲一下移植官方例程。
2.首先,需要去官网下载一个官方标准驱动库,https://www.st.com/en/embedded-software/stsw-stm32046.html
下载完之后,主要关注这两个文件夹 Libraries 和 Project。顾名思义,Libraries主要就是驱动文件之类的了,Project就是官方例程。打开 USB_Host_Examples/CDC 的keil工程,然后把下图框出来的文件复制到自己的工程路径,并添加到自己的工程,记得把头文件也添加进去。(我也不清楚是不是框的所有都要用上)
3.打开魔术棒,C/C++,Define 处添加 USE_USB_OTG_FS。这时候,就可以编译程序了,然后就是看到一堆报错。好了,搞不定,收工(狗头)。
4.接下来就是消除报错了,只需进行一点点非常简单的增删查改就可以了
- 搜索 #include "lcd_log.h" 并删除,官方开发板的屏幕相关的,我用不上
- 搜索 #include "usbh_fs.h" 并删除,文件系统相关的
- 搜索 #include "usbh_data.h" 并删除,好像存的是屏幕用到的图像数据
- 在 usbh_usr.c 添加 #include "stdio.h",然后把LCD有关的处理掉,可把屏幕显示的消息换成printf
- usbh_usr.c 这个函数USBH_USR_UserInput(),直接返回USBH_USR_RESP_OK
- usbh_usr.c 这个函数CDC_OutputData(),在USB接收到数据之后会调用这个函数,可以在这里打印消息
do{
注释代码
}while(usbh_usr.c 有 error)
好了,跳出循环可以继续了!
- usb_bsp.c 的这个函数 USB_OTG_BSP_Init(),我只用到了DP、DM两根线,所以把其他没用到的引脚初始化注释掉了。然后下边还有USB中断配置跟TIM2配置(用作USB_delay),可以不改。
- usb_bsp.c 屏蔽power脚的报错,有个power脚的宏定义,我没用上,我直接注释了
5.然后就是打开 usbh_cdc_core.h
把这3个宏改成0xff,反正广和通的是0xff,所以具体的要看自己的设备修改一下
6.打开usb_conf.h ,做以下修改
7.在 main.c 添加以下代码
#include "usb_bsp.h"
#include "usbh_core.h"
#include "usbh_usr.h"
#include "usbh_cdc_core.h"
#ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED
#if defined ( __ICCARM__ ) /* !< IAR Compiler */
#pragma data_alignment=4
#endif
#endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */
__ALIGN_BEGIN USB_OTG_CORE_HANDLE USB_OTG_Core __ALIGN_END;
#ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED
#if defined ( __ICCARM__ ) /* !< IAR Compiler */
#pragma data_alignment=4
#endif
#endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */
__ALIGN_BEGIN USBH_HOST USB_Host __ALIGN_END;
然后在main()里添加USB初始化函数
USBH_Init(&USB_OTG_Core,USB_OTG_FS_CORE_ID,&USB_Host, &CDC_cb, &USR_Callbacks);
这里建议等从机上电,USB初始化完之后,再调用这个初始化函数。
然后,就在主循环里添加状态机函数USBH_Process(&USB_OTG_Core, &USB_Host);就是处理USB的各种事情。
8.改了这么久了,编译下载看看。哎!识别到厂商信息了,结束收工。
当然还没有,但是离成功就差一步了...吧?(狗头)
9.打开 stm32f4xx_it.c 添加以下代码,自己找个顺眼的地方添加。
#include "usb_bsp.h"
#include "usbh_usr.h"
#include "usb_hcd_int.h"
#include "usbh_core.h"
extern USB_OTG_CORE_HANDLE USB_OTG_Core;
extern USBH_HOST USB_Host;
extern void USB_OTG_BSP_TimerIRQ(void);
/**
* @brief EXTI1_IRQHandler
* This function handles External line 1 interrupt request.
* @param None
* @retval None
*/
void EXTI1_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line1) != RESET)
{
/* USB输出电流异常中断 */
USB_Host.usr_cb->OverCurrentDetected();
EXTI_ClearITPendingBit(EXTI_Line1);
}
}
/**
* @brief TIM2_IRQHandler
* This function handles Timer2 Handler.
* @param None
* @retval None
*/
void TIM2_IRQHandler(void)
{
/* 官方例程用的TIM2 */
USB_OTG_BSP_TimerIRQ();
}
/**
* @brief OTG_FS_IRQHandler
* This function handles USB-On-The-Go FS global interrupt request.
* requests.
* @param None
* @retval None
*/
#ifdef USE_USB_OTG_FS
void OTG_FS_IRQHandler(void)
#else
void OTG_HS_IRQHandler(void)
#endif
{
USBH_OTG_ISR_Handler(&USB_OTG_Core);
}
10.然后就是在 stm32f4xx_it.h 里声明啦。
11.打开 usbh_conf.h ,作以下修改,广和通的mc665有5个接口描述符,这里(可把模块接上电脑,安装USB虚拟串口驱动,就可以看到每个接口的信息了)
至于端点描述符,好像说是可以有0-15共16个,但是广和通这个好像只用到3个,所以我改成5,根据自己的需求来。
12.打开 usbh_cdc_core.c,找到下边这个函数
/**
* @brief CDC_InterfaceInit
* The function init the CDC class.
* @param pdev: Selected device
* @param hdev: Selected device property
* @retval USBH_Status :Response for USB CDC driver intialization
*/
static USBH_Status CDC_InterfaceInit ( USB_OTG_CORE_HANDLE *pdev,
void *phost)
{
USBH_HOST *pphost = phost;
USBH_Status status = USBH_OK ;
#define AT_ITF_NUM 3 //AT1 Port MI_03 AT2 Port MI_04 都可以
#define COMMITF_EP_NUM 1 //最大端点数以内都可以
#define DATAITF_EP_IN_NUM 0 //0号端点,方向为IN
#define DATAITF_EP_OUT_NUM 1 //1号端点,方向为OUT
/* 具体哪个接口哪个端点,可以把模块连上电脑看一下 */
/**
* 注:
* cubemx生成的例程中,标准的CDC类设备,1个配置描述符中需要2接口
* 其中一个为Communication Interface Class(即CommItf), 该接口需要一个方向为in的Ep
* 另外一个为Data Interface Class(即DataItf), 该接口需要一个方向为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
*
* (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
* (2)有方4G模块
* commitf使用interface2-EP0; dataitf使用interface4-EP0和EP1;
*/
/* Communication Interface */
//mc665x任何一个接口描述符bInterfaceClass都是0xff,bInterfaceSubClass和bInterfaceProtocol都是0x00
//所以只要知道是哪个接口哪个端点了,直接配就行
if(pphost->device_prop.Itf_Desc[COMMITF_EP_NUM].bInterfaceClass == COMMUNICATION_DEVICE_CLASS_CODE
/*
&& (pphost->device_prop.Itf_Desc[COMMITF_EP_NUM].bInterfaceSubClass == ABSTRACT_CONTROL_MODEL)
&& (pphost->device_prop.Itf_Desc[COMMITF_EP_NUM].bInterfaceProtocol == COMMON_AT_COMMAND)
*/)
{
/*Collect the notification endpoint address and length
收集通知端点地址和长度,在本例程这个好像不重要,有没有都行*/
CDC_Machine.CDC_CommItf.ep_addr = pphost->device_prop.Ep_Desc[COMMITF_EP_NUM][0].bEndpointAddress;
CDC_Machine.CDC_CommItf.length = pphost->device_prop.Ep_Desc[COMMITF_EP_NUM][0].wMaxPacketSize;
if(pphost->device_prop.Ep_Desc[COMMITF_EP_NUM][0].bEndpointAddress & 0x80)
{
CDC_Machine.CDC_CommItf.notificationEp =\
(pphost->device_prop.Ep_Desc[COMMITF_EP_NUM][0].bEndpointAddress);
}
/*Allocate the length for host channel number in*/
CDC_Machine.CDC_CommItf.hc_num_in = USBH_Alloc_Channel(pdev,
CDC_Machine.CDC_CommItf.notificationEp );
/* Open channel for IN endpoint */
USBH_Open_Channel (pdev,
CDC_Machine.CDC_CommItf.hc_num_in,
pphost->device_prop.address,
pphost->device_prop.speed,
EP_TYPE_INTR,
CDC_Machine.CDC_CommItf.length);
}
else
{
printf("Not Supported notification endpoint\r\n");
}
/* Data Interface */
if((pphost->device_prop.Itf_Desc[AT_ITF_NUM].bInterfaceClass == DATA_INTERFACE_CLASS_CODE)&& \
(pphost->device_prop.Itf_Desc[AT_ITF_NUM].bInterfaceSubClass == RESERVED) && \
(pphost->device_prop.Itf_Desc[AT_ITF_NUM].bInterfaceProtocol == NO_CLASS_SPECIFIC_PROTOCOL_CODE))
{
/*Collect the class specific endpoint address and length
收集类特定端点地址和长度*/
CDC_Machine.CDC_DataItf.ep_addr = pphost->device_prop.Ep_Desc[AT_ITF_NUM][0].bEndpointAddress;
CDC_Machine.CDC_DataItf.length = pphost->device_prop.Ep_Desc[AT_ITF_NUM][0].wMaxPacketSize;
if(pphost->device_prop.Ep_Desc[AT_ITF_NUM][DATAITF_EP_IN_NUM].bEndpointAddress & 0x80)
{
CDC_Machine.CDC_DataItf.cdcInEp = (pphost->device_prop.Ep_Desc[AT_ITF_NUM][DATAITF_EP_IN_NUM].bEndpointAddress);
}
else
{
CDC_Machine.CDC_DataItf.cdcOutEp = (pphost->device_prop.Ep_Desc[AT_ITF_NUM][DATAITF_EP_IN_NUM].bEndpointAddress);
}
if(pphost->device_prop.Ep_Desc[AT_ITF_NUM][DATAITF_EP_OUT_NUM].bEndpointAddress & 0x80)
{
CDC_Machine.CDC_DataItf.cdcInEp = (pphost->device_prop.Ep_Desc[AT_ITF_NUM][DATAITF_EP_OUT_NUM].bEndpointAddress);
}
else
{
CDC_Machine.CDC_DataItf.cdcOutEp = (pphost->device_prop.Ep_Desc[AT_ITF_NUM][DATAITF_EP_OUT_NUM].bEndpointAddress);
}
/*Allocate the length for host channel number out*/
CDC_Machine.CDC_DataItf.hc_num_out = USBH_Alloc_Channel(pdev,
CDC_Machine.CDC_DataItf.cdcOutEp);
/*Allocate the length for host channel number in*/
CDC_Machine.CDC_DataItf.hc_num_in = USBH_Alloc_Channel(pdev,
CDC_Machine.CDC_DataItf.cdcInEp);
/* Open channel for OUT endpoint */
USBH_Open_Channel (pdev,
CDC_Machine.CDC_DataItf.hc_num_out,
pphost->device_prop.address,
pphost->device_prop.speed,
EP_TYPE_BULK,
CDC_Machine.CDC_DataItf.length);
/* Open channel for IN endpoint */
USBH_Open_Channel (pdev,
CDC_Machine.CDC_DataItf.hc_num_in,
pphost->device_prop.address,
pphost->device_prop.speed,
EP_TYPE_BULK,
CDC_Machine.CDC_DataItf.length);
/*Initilise the Tx/Rx Params*/
CDC_InitTxRxParam();
/*Initialize the class specific request with "GET_LINE_CODING"*/
CDC_ReqState = CDC_GET_LINE_CODING_RQUEST ;
}
else
{
printf("Not Supported data endpoint\r\n");
}
return status;
}
通知端点我试下来不重要,有没有都行,知道用到他的时候给他一个IN端点就可以了。至于IN、OUT端点的区分,就是看端点地址,最高位是1的是IN端点,最高位是0的是OUT端点。
每个设备只有一个设备描述符,可以有多个接口描述符,每个接口描述符下又有多个端点描述符。
标准库版本,接口描述符是一个数组,端点描述符又是另一个二维数组,其中 行 就是对应的接口号。(不知道为啥不直接放到接口描述符里边)
13.最后,打开 usbh_cdc_core.c ,这个函数CDC_ClassRequest() 的返回值直接改成USBH_OK,不修改这个会一直断开重连。这个函数好像就是获取跟配置串口4要素的,但是好像并不重要,在电脑串口助手就可以试一下,4要素随便改,但是都能正常通信。
好了,到这就真的是移植完了,之后就是消除警告,把那些没用的代码删掉(当然不删也行)
发送跟接收在 usbh_cdc_core.c 里,先发送数据,然后调用开始接收,当USB接收到就会调用上边说的那个函数了,这时候就可以打印出来,至于停止接收,目前还没摸索出来怎么用。