STM32F407标准库作USB Host CDC 与广和通 mc665 AT通信

本文详细指导了STM32新手如何移植官方USBHostCDC例程,包括下载官方库、修改头文件、消除编译错误、配置中断处理和接口描述符,以实现基本的串口通信功能。
摘要由CSDN通过智能技术生成

开局叠甲:纯新手第一次弄,有哪里做错了的,欢迎指正

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接收到就会调用上边说的那个函数了,这时候就可以打印出来,至于停止接收,目前还没摸索出来怎么用。

  • 8
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
STM32F407标准库中的USB CDC (Communication Device Class)用于在STM32微控制器和计算机之间建立虚拟的串行通信连接。USB CDC支持基于文本和二进制的数据传输,可以在计算机上使用终端模拟器或其他串口通信工具来与STM32F407进行通信。 使用USB CDC,可以实现STM32F407与计算机之间的实时双向通信。在STM32中,需要通过配置USB外设为CDC设备,并实现相应的回调函数来处理来自计算机的命令和数据。STM32F407USB CDC库提供了相关的函数和数据结构,以简化配置和操的过程。 首先,需要在工程中添加USB CDC库文件,并在代码中包含对应的头文件。接着,需要配置相应的GPIO和时钟以启用USB的功能。然后,通过调用库函数配置USB外设为CDC设备并进行初始化。 在USB CDC设备初始化完成后,可以通过调用相关函数来处理USB连接和传输的事件。例如,可以通过设置回调函数来处理接收到的数据或将数据发送到计算机。可以使用库函数来读取并处理计算机发送的数据并出响应。 当STM32F407与计算机建立了USB CDC连接后,可以使用终端模拟器或其他串口通信工具来与STM32进行通信。可以通过发送和接收文本或二进制数据来实现双向通信。 在开发过程中,需要仔细配置和处理USB CDC相关的设置和事件,以确保正常的通信和数据传输。同时,也需要了解USB CDC的协议规范以便正确地配置和操USB外设。 总之,STM32F407标准库中的USB CDC功能提供了一种方便的方式,在STM32微控制器和计算机之间建立虚拟的串行通信连接。通过适当的配置和操,可以实现实时的双向通信
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值