STM32 USB 基础

USB协议简介

简介

USB采用差分串行输入数据,在USB2.0以及之前的版本中,总共包括四个引脚,包括供电端,GOUND,以及数据的D+端和D-端。

USB支持的设备有很多种,常见的包括音频接口,打印机,人机交互设备,下文中的举例主要以人机交互设备(HID)为主

总线信号

信号功能引脚电平
J、K状态用于表示一般逻辑高低J: D+=1, D-=0 K:D+=1, D-=0
Reset设置到未配置状态D+=0,D-=0, 持续10ms
Idle数据发送前后状态电平和J一致
Sync同步信号3次KJ状态切换,后跟随2位时间的K状态
Resume从Idle中恢复
SOP开始状态从Idle切换到k状态
EOP结束状态持续2位时间D+=0, D-=1,后跟随1位时间的J状态

传输包结构

USB传输有清晰的层次结构。一次传输包括多个Transaction,一个Transaction包括多Packet

一个Packet由四部分组成:

SOPSYNCContentEOP
起始信号同步内容结束

包分为四类,命令 (Token) 、Packet 帧首 (Start of Frame) 、Packet 数据 (Data) 、Packet 握手 (Handshake) Packet

其中的SOP、SYNC和EOP的功能在总线信号中已经粗略介绍过

其中的Content分为多个部件,Content的结构一般如下,各个Content包含内容部件会有所不同

PID地址帧号数据校验
包类型目标地址,由8位设备地址和4位端点地址累计发送帧CRC

PID将包分为令牌、数据、握手以及特殊包等。

一个Transaction总是由主机发起,定义四种传输类型:中断传输,等时传输,控制传输和批量传输。注意传输都是由主机发起的,中断传输实际上是周期性向外设发起传输

描述符

当USB设置插入主机时,主机会去尝试获取各个描述符,主要包括设备描述符、配置描述符、接口描述符,相当于设备的初始化过程

设备描述符,主要用于描述厂家,设备版本等

配置描述符,主要用于接口数量和功率设置

接口描述符,主要用于设置接口编号和接口类

端点描述符,主要设置每个端点的地址速度

报告描述符,针对某些设备的设置描述

STM32 USB 库函数

设备描述符

在源文件usbd_desc.c中记录了设备描述符的描述数组

可以通过Cubemx修改字符串的设置

// 各类的描述符和字符串
USBD_DescriptorsTypeDef FS_Desc =
{
  USBD_FS_DeviceDescriptor //设备描述符
, USBD_FS_LangIDStrDescriptor // 字符串的语言ID 
, USBD_FS_ManufacturerStrDescriptor // 厂商字符串
, USBD_FS_ProductStrDescriptor // 产品字符串
, USBD_FS_SerialStrDescriptor // 序列字符串
, USBD_FS_ConfigStrDescriptor // 设置字符串
, USBD_FS_InterfaceStrDescriptor //接口字符串
};

以USB_HID 为例,STM32库函数中在接口描述符码中定义为0x00,表示设备用途在类描述符中定义而非设备描述符中

在usbd_hid*.c中记录了其他的描述符

USBD_ClassTypeDef  USBD_HID =
{
  USBD_HID_Init,    //初始化函数指针
  USBD_HID_DeInit,  //恢复复位函数指针
  USBD_HID_Setup,   
  NULL, /*EP0_TxSent*/
  NULL, /*EP0_RxReady*/
  USBD_HID_DataIn, /*DataIn*/
  NULL, /*DataOut*/
  NULL, /*SOF */
  NULL,
  NULL,
    /*
        不同速度下的其他各类描述符
    */
  USBD_HID_GetHSCfgDesc,
  USBD_HID_GetFSCfgDesc,
  USBD_HID_GetOtherSpeedCfgDesc,
  USBD_HID_GetDeviceQualifierDesc,
};

报告描述符

USB的HID设备通过报告描述符确定通信的格式,在设备的初始化的过程中完成。

报告描述符由通用Item组成,Item分为两种,在HID中主要使用Short Item

Item在起始的1字节中描述了后方数据的长度,Item的类型,Item各个类型的分类标签

Item类型分为三种,包括Main类,Global类、Local类,不同Item描述的数据作用范围不同,这部分可以由生成工具自动生成

在usbd_hid.c中定义了报告描述符

   //自动生成的代码中,没有数据的注释
   //在参考中,有报告描述符的详细解释,我简单介绍下操作的逻辑结构
static uint8_t HID_MOUSE_ReportDesc[HID_MOUSE_REPORT_DESC_SIZE]={ 
    //描述用途
    //创建集合{
        //集合用途
        //创建数据集合{
            //数据说明
            //}
        //}
}

数据说明中说明了每次传输的各个字节对应位的用处,默认第一个数据的前三位为三个按钮的状态,第二三四个数据分别表示xy和滚轮偏移量。

设置完成后,每次USB发送报告只需要安装报告描述符写好的规定的数据传输即可。

使用函数USBD_HID_SendReport实现发送数据

修改主函数实现如下

#include "main.h"
#include "usb_device.h"
#include "usbd_hid.h"
extern USBD_HandleTypeDef hUsbDeviceFS;
​
void SystemClock_Config(void);
int main(void)
{
    int8_t buff[4]={0};
​
    HAL_Init();
​
    SystemClock_Config();
    MX_USB_DEVICE_Init();
​
    while(1){
        //鼠标左键
        buff[0] = 0x01;
        USBD_HID_SendReport(&hUsbDeviceFS, buff, sizeof(buff));
        HAL_Delay(10);
        buff[0] = 0x00;
        USBD_HID_SendReport(&hUsbDeviceFS, buff, sizeof(buff));
        HAL_Delay(100);
        //鼠标左移
        buff[1] = -100;
        USBD_HID_SendReport(&hUsbDeviceFS, buff, sizeof(buff));
        HAL_Delay(1000);
        buff[1] = 0;
    }
​
}
// 时钟设置应该按照各个芯片的时钟树设置,本实验使用的是STM32F3DISCOVERY
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
​
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USB|RCC_PERIPHCLK_USART1
                              |RCC_PERIPHCLK_TIM8;
  PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2;
  PeriphClkInit.USBClockSelection = RCC_USBCLKSOURCE_PLL_DIV1_5;
  PeriphClkInit.Tim8ClockSelection = RCC_TIM8CLK_HCLK;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
  {
    Error_Handler();
  }
}

参考

STMCU USB2.0协议详解

USB协议详解第10讲(USB描述符-报告描述符)一个早起的程序员的博客-CSDN博客usb报告描述符

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值