将TeenyUSB加入到CubeMX生成的工程中


ST为其旗下的STM32系列芯片提供了图形界面的初始化工具STM32CubeMX,使用这个工具能够以可视化的方式完成引脚分配,时钟配置,并生成相应的代码,能够大大提高工作效率。TeenyUSB是一个简化的USB协议栈。下面将介绍如何在CubeMX生成的工程中加入TeenyUSB协议栈。这里以STM32F723E-Discovery开发板为例来说明。

打开CubeMX后新建一个工程,选择芯片型号为STM32F723IEK6

配置RCC时钟源

这里的STM32F723E开发板使用的是外置晶振,如下图:

配置USB模块

这里将FS和HS都配置为Device Only模式,由于TeenyUSB会设置USB的时钟、相关IO以及工作模式,因此这选Device Only或Host Only都可以。

配置USB相关中间件

这里为FS和HS模块选择中间件,因为后面会用TeenyUSB替换官方的协议栈,这里随便选一个就行。

配置时钟树

STM32F723E开发板使用的是外置25MHz的晶振,这里只测试USB相关的功能,只需要关心USB时钟。时钟树配置如下:

生成代码

生成的代码结构如下图,图中箭头指向的是官方USB协议栈的相关文件,在添加TeenyUSB协议栈文件前需要删除。

增加TeenyUSB协议栈文件

增加TeenyUSB协议栈的文件

teeny_usb.c                                    TeenyUSB的标准USB请求处理文件,在这个文件中处理USB标准请求。

stm32_otg_init.c                             OTG设备的初始化代码,在这个文件中初始化USB相关的IO以及USB模块,处理中断。

teeny_usb_stm32_otg_device.c    TeenyUSB中OTG模块设备模式下的处理代码

teeny_usb_desc.c                          TeenyDT自动生成的描述符文件,这里采用的是custom_bulk示例中的文件

修改工程配置

在宏定义中增加NO_HOST,表示不需要主机相关代码

在包含目录中增加下面的路径

TeenyUSB头文件所在路径

增加开发板相关配置文件的路径,STM32F723E的开发板配置文件示例在这里

增加端点定义文件路径,这里采用的是custom_bulk示例中的端点定义

删除不需要的文件

TeenyUSB使用了官方USB库中的头文件,官方USB库需要一个usbd_conf.h文件来定义USB模块的一些配置。TeenyUSB为所有的设备提供了一个通用的配置文件,在usb_stack/inc目录中。为了使用这个通用的USB设备配置文件,需要将CubeMX自动生成的usbd_conf.h文件删除。

删除不需要的代码

CubeMX会自动生成USB相关中断处理函数,TeenyUSB会在stm32_otg_init.c文件中处理中断,需要将CubeMX中自动生成的USB中断处理函数删除。

在main函数中,删除初始化HAL库初始化USB设备的MX_USB_DEVICE_Init函数。

在main.c中增加相关功能

在生成代码的main.c文件中,增加如下代码:

/* USER CODE BEGIN Includes */
#include "teeny_usb.h"
#include "string.h"
/* USER CODE END Includes */
...

/* USER CODE BEGIN 0 */

#define  TX_EP   PCD_ENDP1
#define  RX_EP   PCD_ENDP2

uint8_t buf[4096];
__IO uint32_t data_cnt = 0;

// if data tx done, set rx valid again
void tusb_on_tx_done(tusb_device_t* dev, uint8_t EPn)
{
  if(EPn == TX_EP){
    tusb_set_rx_valid(dev, RX_EP);
  }
}

int tusb_on_rx_done(tusb_device_t* dev, uint8_t EPn, const void* data, uint16_t len)
{
  if(EPn == RX_EP){
    data_cnt = len;
    return len;
  }
  return 0;
}

void tusb_reconfig(tusb_device_t* dev)
{
  // call the BULK device init macro
  BULK_TUSB_INIT(dev);
  // setup recv buffer for rx end point
  tusb_set_recv_buffer(dev, RX_EP, buf, sizeof(buf));
  // enable rx ep after buffer set
  tusb_set_rx_valid(dev, RX_EP);
}

void tusb_delay_ms(uint32_t ms)
{
   HAL_Delay(ms);
}

/* USER CODE END 0 */
...

  /* USER CODE BEGIN 2 */
  tusb_device_t* dev = tusb_get_device(TEST_APP_USB_CORE);
  tusb_open_device(dev);
  /* USER CODE END 2 */
...

  /* USER CODE BEGIN WHILE */
  while (1)
  {
    if(data_cnt){
      // every data plus 1 and echo back
      for(int i=0;i<data_cnt;i++){
        buf[i]++;
      }
      tusb_send_data(dev, TX_EP, buf, data_cnt, TUSB_TXF_ZLP);
      data_cnt = 0; 
    }
  /* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
  }

在“USER CODE Includes”代码块中,增加了相关的头文件

在“USER CODE 0”代码块中,实现了TeenyUSB中的一些回调函数

在“USER CODE 2”代码块中,打开了一个USB设备,TEST_APP_USB_CORE的定义在STM32F723E开发板的board_config.h文件中

在“USER CODE WHILE”代码块中,将接收到的数据逐字节+1后发回主机

上面的代码来自TeenyUSB协议栈的Custom Bulk示例,这个示例实现了一个自定义USB Bulk设备,在Win8以后的系统上可以不用安装驱动程序。

 

访问 www.tusb.org 获取更多关于TeenyUSB协议栈的资料。