Uploaded by Giovanni Varasano

STM32-USB

advertisement
STM32
USB Device
Development Guide
Translated by Giovanni Varasano on 3/10/2023.
1/109
For any questions or suggestions, please send a feedback here: faq.tusb.org
or contact the orignal chinese author via email: admin@xtoolbox.org
Table of contents STM32 USB device development
guide................................................ ................................................................. ....................... 1 1 2
Foreword........................................ ................................................................. ......................................
........................... .. 1 1.1 Origin of this
article................................................ ................................................................. ................................ 1
1.2 Introduction to
USB........................................ ................................................................. ................................... 1 1.3
Reading
Guide................................................ ................................................................. ...................................
1 USB basic
concepts................................................ ................................................................. .............................3
2.1 Physical
connection........................................ ................................................................. ..................................
3 2.1.1 D+/Dsignal........................................ ................................................................. .............................3 2.1.2
ID signal................................................ ................................................................. .............................
4 2.2 Basic USB
Operations........................................ ................................................................. .............................5
2.2.1 Hot plug and
reset........................................ ................................................................. ........................ 5 2.2.2 Set
Address Assignment ............................................. .................................................5 2.2.3
Configuration device
Configuration ............................................. ................................................................. 9 2.2.4 Power
management................................................ ................................................................. .........................
... 9 2.2.5 Device request processing/device enumeration (Request Processing) ..................................
........... 9 2.2.6 Requests related to interface
type........................................ ................................................................. ........ 9 2.2.7 Summary of
basic operations........................................ ................................................................. .............10 2.3
USB data
stream................................................ ................................................................. .............................10
2.3.1 Basic
transactions................................................ ................................................................. ..........................
..10 2.3.2 Control transfer ............................................. ............................................. 11 2.3.3
Isochronous transfer ............................................. .................................. 14 2.3.4 Interrupt
transfer................................................ .................................................14 2.3.5 Bulk
transfer ............................................. ................................................................. . 15 2.4 Transfer
Complete Condition ........................................ .............15 2.4.1 Expected data
length........................................ ................................................................. .............15 2.4.2 Some
2/109
examples of transfer
completion........................................ ................................................................. ...... 16 2.5 Standard
Device Request ........................................ ............................17 2.5.1 Setup data
structure................................................ ................................................................. .............17 2.5.2
Set Address ............................................. ................................................................. .... twenty one
2.5.3 Get Descriptor ........................................ ............................................. twenty one
2.5.4 Get Status........................................ ................................................................. ....... twenty one
2.5.5 Read/set configuration, read/set interface...................... ........................................ twenty two
2.5.6 Set/Clear Feature ........................................ ................................... twenty three 2.6 USB data
flow control................................................ ................................................................. .........................
twenty three 2.6.1 2.7 3 Flow control for synchronization
endpoints........................................ ................................................................. ............ twenty three
Device-side development and debugging
steps........................................ ................................................................. ............25 2.7.1 USB
module core configuration...................................... ................................................................. ...........
25 2.7.2 Endpoint 0 OUT direction
configuration...................................... ................................................................. ..... 25 2.7.3
Endpoint 0 IN direction
configuration...................................... ................................................................. ........... 25 2.7.4
Device
enumeration................................................ ................................................................. .........................
....26 2.7.5 Driver
installation................................................ ................................................................. ...........................
.27 2.7.6 Using the
device................................................ ................................................................. ............................27
USB Standard
Descriptor................................................ ................................................................. ............................
28 3.1 Device Descriptor ........................................ .............................................28 3.2
Configuration Descriptor Config
Descriptor ........................................ .................................................. 29 3.3 Device
interface................................................ ................................................................. .............................2
9 3.4 Interface Association Descriptor ........................................ ........... 29 3.5 Interface
Descriptor ........................................ .................................... 30 3.6 Endpoint
Descriptor ........................................ ........................................30 3.6.1 High speed high bandwidth
endpoint High speed high bandwidth endpoint........................ 30 3.6.2 Periodic
Endpoint................................................ ............................................ 31 3.6.3 Non-Periodic Endpoint
........................................ .............................31 3.7 Device Qualifier
Descriptor ............................................. ......................... 32 3.8 Other Speed Configuration
Descriptor ........................................ ........ 32 3.9 Character descriptor String
Descriptor ........................................ ................................................. 32 3.9.1 3.10 Special character
3/109
descriptors in WCID devices........................................ ............................. 32 Descriptor tool
TeenyDT ........................................ ................................................................. ...... 33 3.10.1
TeenyDT command line mode...................................... ................................................................. .....
33 3.10.2 Device
descriptor................................................ ................................................................. .............................
33 3.10.3 Character
descriptors................................................ ................................................................. ............................
.34 3.10.4 Configuration
descriptor................................................ ................................................................. .............................
36 3.10.5 Interface
descriptor................................................ ................................................................. .............................
37
3.10.6 Endpoint
descriptor................................................ ................................................................. .............................
38 3.10.7 Interface Association Descriptor
IAD ........................................ ................................................................. ...... 39 3.10.8 Function
descriptor................................................ ................................................................. .............................
39 3.10.9 Device descriptor
example................................................ ................................................................. ............ 40 3.10.10
3.11 4 TeenyDT graphical interface
mode................................................ .................................................41 Descriptor
summary................................................ ................................................................. ............................4
3 STM32 USB FS
module................................................ ................................................................. .............................44
4.1 USB core initialization
operations................................................ ................................................................. ........ 44 4.1.1
Clock
settings................................................ ................................................................. ............................44
4.1.2 IO
settings................................................ ................................................................. .............................46
4.1.3 Interrupt
settings................................................ ................................................................. ............................47
4.1.4 USB module
settings................................................ ................................................................. .............48 4.1.5
Core initialization in TeenyUSB........................................ ............................................. 48 4.1.6 Core
initialization in HAL library................................ ................................................................. .. 50 4.2
USB Endpoint Register
Operation........................................ ................................................................. ........ 50 4.2.1 Can only
be cleared (rc_w0) ........................................ ................................................................. ........ 51 4.2.2
Write 1 flip(t) ............................................. ................................................................. ............. 51
4/109
4.2.3 Read-only and read-write (r,
rw)................................ ................................................................. ........ 52 4.2.4 USB endpoint
register configuration in TeenyUSB...................................... .............................52 4.2.5 Endpoint
register configuration in HAL library........................................ ............................................ 53 4.3
PMA
operation................................................ ................................................................. .............................
53 4.3.1 USB module accesses
PMA................................................ ................................................................. .........54 4.3.2
Application access to PMA ........................................ ................................................................. ........
54 4.3.3 USB FS PMA
operation........................................ ................................................................. ....... 55 4.3.4 PMA
address and cache length configuration................................ ................................................. 57 4.3.5
PMA data reading and
writing...................................... ................................................................. ............. 58 4.4 Endpoint
initialization................................................ ................................................................. .........................
...61 4.4.1 TeenyUSB endpoint
initialization................................................ ................................................................. .. 61 4.4.2 HAL
library endpoint
initialization................................................ ................................................................. ........... 63 4.5
Responding to the Reset event and initializing the
endpoint................................. ................................................. 63 4.5.1 TeenyUSB library Reset event
processing and endpoint initialization...................................... ............ 64 4.5.2 HAL library Reset
event processing and endpoint initialization................................ ........................ 65
4.6 4.6.1 Basic transactions and data
transmission................................ ................................................................. ...... 68 4.6.2 Data
transmission processing
flow...................................... ................................................................. ........... 69 4.6.3 IN
transmission
processing...................................... ................................................................. ........................ 69 4.6.4
OUT transmission
processing...................................... ................................................................. .............70 4.6.5
SETUP
processing................................................ ................................................................. ........................
72 4.6.6 Double-buffered
endpoints........................................ ................................................................. .............................73
4.6.7 BULK double buffer and
FIFO...................................... ................................................................. ........ 76 4.7 5 Endpoint data
processing................................................ ................................................................. ........................
68 Summary of USB FS
usage................................................ ................................................................. .............76 4.7.1
5/109
Ordinary
functions................................................ ................................................................. ............................7
7 4.7.2 Callback
function................................................ ................................................................. ............................77
STM32 OTG module device mode...................................... .................................................................
........... 79 5.1 USB core initialization
operations................................................ ................................................................. ........... 80 5.1.1
Clock
settings................................................ ................................................................. ........................ 80
5.1.2 IO
settings................................................ ................................................................. ........................ 81
5.1.3 Interrupt
settings................................................ ................................................................. ........................ 82
5.1.4 USB module
settings................................................ ................................................................. ............. 82 5.1.5
Summary of core initialization
operations........................................ ................................................................. ..... 83 5.2 USB
endpoint register
operation................................................ ................................................................. ............ 83 5.3
FIFO
operation................................................ ................................................................. ........................ 84
5.3.1 FIFO
initialization................................................ ................................................................. .........................
84 5.3.2 Reading FIFO
data........................................ ................................................................. ............. 86 5.3.3 Writing
FIFO data........................................ ................................................................. ............. 86 5.3.4 FIFO
initialization of OTG_FS and OTG_HS...................................... .............................88 5.3.5 FIFO
summary................................................ ................................................................. .............................8
8 5.4 Endpoint
initialization................................................ ................................................................. .........................
....88 5.5 Responding to the Reset event and initializing the
endpoint............................. .................................................90 5.5.1 5.6 TeenyUSB library Reset
event handling and endpoint initialization........................................ .............91 Endpoint data
processing................................................ ................................................................. ............................
92 5.6.1 OUT endpoint data
processing...................................... ................................................................. .........93 5.6.2 IN
endpoint data
processing................................................ ................................................................. .............95 5.7
DMA
6/109
operations................................................ ................................................................. ............................
.98
5.7.1 OUT endpoint
DMA ........................................ ................................................................. .............98 5.7.2 DMA
processing of SETUP packet........................................ ................................................................. ...
99 5.7.3 IN endpoint
DMA ........................................ ................................................................. ......................... 99 5.8 6
USB OTG usage
summary................................................ ................................................................. .............99 5.8.1
Ordinary
functions................................................ ................................................................. ............................1
00 5.8.2 Callback
function................................................ ................................................................. ............................10
0 5.8.3 Comparison between USB FS and OTG
devices...................................... ................................................. 101 USB device
example................................................ ................................................................. .............................1
03 6.1 TeenyUSB protocol stack usage
instructions................................................ ..................................................103 6.1.1 6.2 TeenyUSB
file structure................................................ ................................................................. . 103 Custom
USB device........................................ ................................................................. .............104 6.2.1
Determine device
functionality........................................ ................................................................. ............106 6.2.2
Descriptor
design................................................ ................................................................. .............................106
6.2.3 Initializing device endpoints........................................ .................................................................
........ 108 6.2.4 Device type related request
processing........................................ ................................................................. 109 6.2.5 Equipment
function implementation........................................ ................................................................. ............
109 6.2.6 Device
driver................................................ ................................................................. ............................112
6.2.7 Functional
test................................................ ................................................................. ............................114
6.2.8 WCID
device................................................ ................................................................. .............................115
6.3 CDC serial
device................................................ ................................................................. .............................119
6.3.1 Determine device
functionality........................................ ................................................................. ............119 6.3.2
Descriptor
design................................................ ................................................................. .............................119
7/109
6.3.3 Initializing device endpoints........................................ .................................................................
........ 123 6.3.4 Device type related request
processing........................................ ................................................................. 124 6.3.5 Equipment
function
implementation...................................... ................................................................. ............126 6.3.6
Device
driver................................................ ................................................................. ............................126
6.3.7 Functional
test................................................ ................................................................. ............................128
6.3.8 Multi-serial port composite
equipment...................................... ................................................................. ............128 6.4 Mass
Storage Device MSC ........................................ ................................................................. .........132
6.4.1 Determine device
functionality........................................ ................................................................. ............132 6.4.2
Descriptor
design................................................ ................................................................. ......................... 132
6.4.3 Initializing device endpoints........................................ .................................................................
........ 133
6.4.4 Device type related request
processing........................................ ................................................................. 133 6.4.5 Equipment
function implementation........................................ ................................................................. ............
134 6.4.6 Device
driver................................................ ................................................................. ............................139
6.4.7 Functional
testing................................................ ................................................................. ............................139
6.5 7 Custom HID
devices........................................ ................................................................. .............140 6.5.1
Determine device functionality................................ ................................................................. ............
140 6.5.2 Descriptor
design................................................ ................................................................. ..................140 6.5.3
Initializing device endpoints........................................ ................................................................. ........
143 6.5.4 Device type related request
processing........................................ ................................................................. 143 6.5.5 Equipment
function implementation...................................... ................................................................. ............
145 6.5.6 Device
driver................................................ ................................................................. ............................145
6.5.7 Functional
testing................................................ ................................................................. ............................145
STM32 OTG module host
mode...................................... ................................................................. ........ 147 7.1 Initialization
8/109
of host mode...................................... ................................................................. .............147 7.2 Host
port operation................................................ ................................................................. .............147
7.3 FIFO in host
mode ............................................. ................................................................. .............148 7.4
Request Queue in the host ........................................ .................................. 148 7.5 Host
channel........................................ ................................................................. ............................149
7.5.1 8 9 Channel data sending and
receiving........................................ ................................................................. .............149 USB host
development................................................ ................................................................. ........................
.....151 8.1 Host without protocol
stack........................................ ................................................................. ....................... 151 8.2
Hosts that support keyboard, mouse and
HUB...................................... .................................................153 8.2.1 Enumeration process of
general devices...................................... ................................................................. ..... 153 8.2.2
Keyboard and
mouse........................................ ................................................................. ..................155 8.2.3 HUB
processing................................................ ................................................................. ............................
.159 8.2.4 Keyboard and mouse data
processing................................ ................................................................. .... 161 related
resources................................................ ................................................................. ..............................
.......... 162 9.1 STM32
chip................................................ ................................................................. .............................162
9.1.1 9.2 Correspondence between STM32 models and USB
modules........................................ ............................162 USB supporting software for
PC...................................... ................................................................. ............162 9.2.1 Qt
library................................................ ................................................................. .............................
163 9.2.2 XToolbox
application................................................ ................................................................. ..... 163
9.2.3 libusb
library................................................ ................................................................. .............................
163 9.2.4 libwdi driver installation
project...................................... ................................................................. .... 163 9.2.5 WinUSB
library................................................ ................................................................. .............................
164 9.2.6 QLibUsb
library................................................ ................................................................. .............164 9.3 USB
Documentation
Resources................................................ ................................................................. ........................16
4 9.3.1 References mentioned in this
article........................................ ................................................................. .. 164
9/109
10/109
0 Preface
TeenyUSB is a lightweight USB protocol stack for STM32 chips. The initial main purpose of this
project was to solve the problem of insufficient chip code space. The code generated with CubeMX
is relatively large, and the chip resources selected are very limited, so the idea of implementing a
lightweight USB came up.
The project address is: https://github.com/xtoolbox/TeenyUSB. Or email the author directly at
admin&xtoolbox.org and replace the ampersand in the email with @.
Source code hosting address: code.tusb.org, https://github.com/xtoolbox/teenyusb
Currently supported device routines
Custom USB device Custom USB device uses Bulk transmission, and the driver is automatically
installed in the form of WCID on Windows without manual installation.
F723Dap is based on STM32F723's DAPLink and supports both V1 and V2 data transmission
modes.
Composite device: Composite device that supports USB serial port, U disk, custom HID and
WinUSB Bulk transmission
Currently supported host routines
HUB+U disk+keyboard and mouse supports U disk keyboard and mouse and HUB-cascaded host
demo.
Dual-role device based on rt-thread. The master-slave mode automatically switches according to the
connection. It supports RNDIS master-slave mode, U disk master-slave mode and automatic
mounting, keyboard master-slave mode, mouse slave mode, etc.
Related tools
TeenyDT descriptor generation tool
USB descriptor automatic generation tool uses lua format to write descriptors and automatically
generates descriptors and initialization codes in TeenyDT and libOpenCM3 formats. Descriptor
examples and online trials http://dt.tusb.org or http://dt1.tusb.org.
PC testing tools
11/109
A universal USB device testing tool that supports serial port testing and HID device testing.
Common USB devices support bulk transfer, interrupt transfer and synchronous transfer.
1 Introduction
1.1 Origin of this article
ST has written a standard library and a HAL library for the USB module of the STM32 series
microcontrollers. The standard library only supports relatively some of the earliest chips are no
longer maintained; the HAL library supports a full range of chips, and the HAL library can
cooperate with CubeMX to automatically Generate initialization code. The USB part in the HAL
library, in order to adapt to various application scenarios, performs the core operations of the USB
module encapsulated layer by layer. The author is working on a project where the size of the code
that can be used is limited, so I am looking for a better solution. A simplified USB library that does
not necessarily have many functions, but can meet the needs.
There are two simplified versions of USB libraries for STM32 on github.
One is a simplified version of HID Bootloader based on STM32 Project, one is the USB library
project of STM32. The former is for bootloader, focusing on the optimization of code space; the
author's goal for the latter is to make an open source USB protocol stack, focusing on the integrity
of the protocol. Both projects also have some flaws: simplification The version of the Bootloader
project does not implement more USB device classes, and the latter project cannot support multiple
USB modules at the same time. block of chips. In order to solve these two problems, the author
implemented a simplified version of the protocol stack TeenyUSB based on STM32.
This article is mainly based on TeenyUSB to introduce the actual development process of USB
devices. Some contents in the article will be compared and analyzed at the same time. ST official
HAL library and TeenyUSB allow users of ST official HAL library to understand the
implementation of HAL library. All TeenyUSB source code, routines, and related tools are available
at www.tusb.org.
1.2 USB Introduction
USB is the abbreviation of Universal Serial Bus. USB bus has the characteristics of simple
connection and wide range of use. Currently on the market Most of the microcontrollers have
integrated USB modules, which can easily communicate with the host computer. Here we mainly
introduce ST company USB module for STM32 series microcontrollers. The basic functions of
USB modules from various manufacturers are similar. If you are familiar with one It will be easier
to draw parallels later. In embedded development, USB has always been a relatively complex
content. On the one hand, inside the microcontroller, the USB module itself is very complicated and
involves many registers; on the other hand, in addition to the development of microcontrollers, USB
also involves PC-side hosts. In terms of software development, PC-side software is more
12/109
complicated to debug because it is closely related to the hardware. TeenyUSB provides embedded
protocol while discussing the stack, PC-side testing software is also provided to facilitate testing
during the development process. This article mainly refers to the STM32 series chip reference
manual and the "USB2.0 Specification", and mentions some concepts and names.
In order to avoid errors in understanding, the original Chinese text and the English original text in
the reference materials will be used to describe the words.
1.3 Reading Guide
The following chapters 2-5 are about introducing some basic knowledge of USB devices. The
writing is quite verbose. I hope to start running the code first. Readers who have come here can
jump directly to Chapter 6 USB Device Development and perform practical operations with the
code at www.tusb.org do.
Chapter 2 mainly introduces some basic concepts in USB, The introduction is explained with the
code of TeenyUSB. Readers who are familiar with the basic concepts of USB can skip this chapter.
For those who are familiar with the concepts but don’t know how to start with the code Readers can
take a look at this chapter and perhaps find ways to convert concepts into code.
Readers familiar with standard descriptions may skip this. For those who are familiar with
descriptors but find them cumbersome to write People, you can just read the content of this section
3.10 Descriptor Tool TeenyDT, and you may be able to find a way to simplify the descriptor writing
work. method.
Chapter 4 introduces the USB FS module of STM32 and the corresponding code implementation.
The USB FS module is mainly used in STM32F0, STM32F1 series, STM32F3 series chips. FS
module is also called USB module in ST official, ST The USB module has two versions, USB and
USB+. The two versions are not very different and will be introduced together.
Chapter 5 introduces some functions of the STM32 OTG module device and the corresponding
code implementation. OTG module main Used in STM32F2, STM32F4, STM32F7 series chips.
OTG is divided into two versions: FS and HS. HS is Divided into built-in phy and external phy
versions. The basic operations of different OTG versions are similar and will be introduced
together.
Chapter 6 introduces the actual USB device development, mainly introducing custom USB devices,
CDC serial port devices, MSC large Development and use of four types of devices: capacity storage
devices and custom HID devices.
Chapter 7 introduces the host function of the STM32 OTG module. This part is very brief and only
covers the framework of the host part. It was sorted out without going into some details.
13/109
Chapter 8 introduces a simple USB host implementation, including the simplest keyboard, mouse
and HUB functions. host phase The relevant content is very brief. The main purpose of introducing
the host is to look at the implementation of the device from a different perspective.
Chapter 9 introduces some external resources related to the TeenyUSB project, such as the Qt
library and libusb used in testing the software Library, libwdi project used to drive INF file
generation, WinUSB, a universal USB device driver on Windows.
2 USB basic concepts
Before introducing the STM32 USB module, here are some basic concepts in the USB system.
These basic concepts helps us understand the design of USB modules. This article does not
completely cover all aspects of USB, but only introduces some contents closely related to USB
module operation. When introducing these basic concepts, the author will base on his own
understanding and the relevant concepts are explained in detail with specific implementations in the
code. Complete USB content can be found in the reference documentation
"Universal Serial Bus Specification 2.0"
, the "USB 2.0 Specification" in the following article also refers to this reference material.
This article does not cover the underlying USB signals, but mainly explains the design of USB
devices from the perspective of chip usage.
underlying signal
The processing is handled internally by the chip USB module, and the user layer generally does not
operate specific underlying signals. underlying signal
The detailed description is in the "Chapter 8 Protocol Layer" chapter of the "USB 2.0
Specification".
2.1 Physical connection
The USB device is connected to the host via a USB cable, for Low Speed (LS)
, full speed (FS)
, high speed (HS) equipment,
D+ and D- are data lines. For OTG devices, there is also an ID line to identify whether the device is
currently working in host mode or in slave mode. Therefore, the USB FS module of STM32
requires two signal lines, D+ and D-. In addition to the OTG module in addition to D+ and Dsignals, if necessary, supports dual role device (Dual Role Device, DRD) integrating master and
slave and still need an ID wire. This article does not cover Super Speed's 3.0 devices.
2.1.1 D+/D- signal
D+/D- signal is also called DP/DM (Data Plus/Data Minus) signal. The USB host transmits data
through D+/D- signal.
14/109
The pull-up resistor is used to identify the speed of USB devices. When D+ has a 1.5K pull-up
resistor, it is identified as a FS/HS device. D-has 1.5K
When the resistor is pulled up, it is recognized as an LS device. When there is no pull-up resistor,
the host considers the device to be disconnected. Therefore it is possible
Control the connection status of the device by controlling whether the pull-up resistor is connected
or not.
Some chips have built-in 1.5K pull-up resistors, and the status of the pull-up resistors can be
controlled through registers to achieve software control.
Connection Status. The high-speed (HS) device is in the full-speed (FS) state at startup, and the D+
pull-up is maintained to complete the high-speed device emulation.
After lifting the configuration, disconnect the pull-up resistor and enter the current-driven highspeed mode.
Some chips do not have a built-in 1.5K pull-up resistor and require an external pull-up resistor. If
you need to support software control connection status
state, an additional pin is needed to control the on and off of the pull-up resistor. When the STM32
USB module is turned off
After one clock, the USB D+/D- signal pin will work in GPIO mode. After the USB clock is turned
off, set the D+ pin to
out of mode and sets the D+ pin low. At this time, the USB host detects that both D+/D- are low
level, and will think that the device
Ready to disconnect. It can be simulated on a chip without a built-in pull-up resistor by setting D+
as an IO output and driving it low.
It has the effect of disconnection, which saves an IO that controls the state of the pull-up resistor.
The following is the D+ pin processing code of the TeenyUSB protocol stack on the F0 chip:
void tusb_open_device(tusb_device_t* dev){
...
#ifdef INTERNAL_PULLUP
// USE the interal pull up resistor
GetUSB(dev)->BCDR|=USB_BCDR_DPPU;
#else
RCC->AHBENR |= RCC_AHBENR_GPIOAEN;
// PA12 = In float
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR12;
GPIOA->MODER &= ~GPIO_MODER_MODER12;
#endif
...
}
void tusb_close_device(tusb_device_t* dev){
#ifdef INTERNAL_PULLUP
// disable interal pull up resistor
GetUSB(dev)->BCDR&=~USB_BCDR_DPPU;
15/109
#else
// PA12 = PushPull = 0
GPIOA->OTYPER |= GPIO_OTYPER_OT_12;
GPIOA->MODER &= ~GPIO_MODER_MODER12;
GPIOA->MODER |= GPIO_MODER_MODER12_0;
GPIOA->OSPEEDR |= GPIO_OSPEEDR_OSPEEDR12;
GPIOA->BRR = GPIO_BRR_BR_12;
#endif
...
}
}
If you use the chip's internal pull-up resistor, you only need to control the status bit of the pull-up
resistor; if you do not use the built-in pull-up resistor, you only need to control the status bit of the
pull-up resistor.
The pull-up resistor simulates the on-off effect by controlling the IO signal. During analog
connection, set the D+ pin (PA12 pin) to the input state.
state, then the external pull-up resistor will pull the pin signal high; then turn on the USB clock,
then the USB module will take over the pin.
When intending to disconnect, first turn off the USB clock and return the D+ pin to GPIO mode;
then configure D+ to output mode and set it to low level.
At this time, the USB host detects that D+ is low level and will consider the device to be
disconnected.
2.1.2 ID signal
There is an additional ID signal in the OTG device, and the OTG device switches working modes
through the ID signal. When ID is not connected oe when floating, it works in B mode, that is,
device mode. When ID is grounded, it works in A mode, that is, host mode. ID's
The connection method can be determined via USB cable. When the cable's ID is grounded, the
USB device operates in A mode, so this ID grounded cable is also called the A cable. Likewise with
the B cable. If using type-c cable, ID signal connection on the CC1/CC2 pin of type-c, when pulled
down to ground, the device operates in B mode, and when floating, it operates in A mode.
2.1.2.1 Host Negotiation Protocol (HNP)
Does the OTG device connected to the A cable necessarily work in host mode? OTG devices can
work on both master and slave machines mode, if they both support the host negotiation protocol,
hosts can be switched by setting a feature request after connecting. Do it specifically
The method is that device B reports an OTG descriptor to device A during the enumeration phase. A
device according to the content in the OTG descriptor,
Learn whether device B supports HNP. If the B device supports it, when the A device does not need
to actively control the bus, send a
Set Feature request to switch the working mode of B device. After device A finishes sending the
request, it releases the bus and exits.
After exiting the master mode, device B obtains bus control and waits for device A to connect in
slave mode. After A device is connected, start The enumeration process after swapping roles.
16/109
For OTG devices, A mode should be said to be the mode that initially works on the host, and B
mode is the mode that initially works on the device.
prepared mode. After the initial state, the host devices can convert each other as needed, and
perform flexible switching according to actual needs.
Transformation (On The Go)
.
2.2 Basic USB operations
This section introduces some basic USB operations related to the chip USB module.
2.2.1 Hot plug and reset
When a device is plugged into a port on a USB host, the host replicates the device's USB module
through the USB bus.
bit, the device after reset has the following characteristics:
1. Respond to data at the default address, which is address 0
2. The device is in Not Configured state
3. The device cannot be suspended (Not Suspended)
After the device receives the reset signal from the bus, it needs to set its address to 0, clear the
configuration status, and clear the hang-up state start status. The operation of configuring the device
is to set the endpoint based on the current configuration information, and the operation of clearing
the configuration is exactly the opposite.
Come, clear the endpoint configuration content and restore it to the default state. If the device has
only one configuration, it can be
Complete the configuration of all endpoints. If the device has multiple configurations, only
endpoint 0 can be configured at reset, which is used to receive enumeration and
Request to set the address; wait until the device receives the configuration request, and then
initialize other endpoints according to the actual configuration. Configuration phase
For details, see 3, 2, 3 Configuring Devices. TeenyUSB only considers the case where the device
has only one configuration, so in the device USB
All endpoints are configured on reset.
2.2.2 Address Assignment
When the host resets the device, the host will set the slave device address. This address is unique in
the USB tree structure.
of. Before setting the address, the host will initiate a read device descriptor request through the
default address (ie 0 address).
Get the maximum packet length of device endpoint 0 through the device descriptor.
Setting the address is a control transmission without data. The control transmission without data
needs to be completed through two packets of data transmission.
(See 2.3.2.3 No data control transmission for details)
. One is the Setup packet, from the host to the device; the other is the status return packet, sent by
the device to the host. When the device receives the set address command, the device address is still
0. If the device immediately sets the location at this time
17/109
address and takes effect, then the status packets sent by the device later will be sent through the
newly set address, so that from the host side,
This time the address setting did not receive Status IN and the address setting failed. Therefore, the
device needs to send the status packet successfully before it can
The new address takes effect. The USB_FS module address of STM32 takes effect immediately
after it is set, so this type of USB module needs to be installed on the device
The address can only be set after the status packet is sent successfully. The OTG module of STM32
is controlled by the internal logic of the module. In the status
The new address will not take effect until it is successfully sent, so modules such as OTG can be set
directly after receiving the Setup package to set the address.
new address.
The following figure shows the processing flow of setting address requests between TeenyUSB and
HAL libraries in different USB modules:
Source code for the process in TeenyUSB that handles set address requests.
18/109
2.2.2.1 HAL library STM32F1 series set address request
The code that handles setting address requests is as follows. The calling process is from the first
function to the last function:
static void USBD_SetAddress(USBD_HandleTypeDef *pdev,
USBD_SetupReqTypedef *req)
{
...
dev_addr = (uint8_t)(req->wValue) & 0x7F;
...
pdev->dev_address = dev_addr;
USBD_LL_SetUSBAddress(pdev, dev_addr);
...
}
USBD_StatusTypeDef USBD_LL_SetUSBAddress(USBD_HandleTypeDef *pdev,
uint8_t dev_addr){
...
hal_status = HAL_PCD_SetAddress(pdev->pData, dev_addr);
...
}
HAL_StatusTypeDef HAL_PCD_SetAddress(PCD_HandleTypeDef *hpcd, uint8_t address){
...
hpcd->USB_Address = address;
USB_SetDevAddress(hpcd->Instance, address);
...
}
HAL_StatusTypeDef USB_SetDevAddress (USB_TypeDef *USBx, uint8_t address){
if(address == 0) {
/* set device address and enable function */
USBx->DADDR = USB_DADDR_EF;
}
return HAL_OK;
}
static HAL_StatusTypeDef PCD_EP_ISR_Handler(PCD_HandleTypeDef *hpcd){
...
if (epindex == 0){
...
if((hpcd->USB_Address > 0U)&& ( ep->xfer_len == 0U)){
hpcd->Instance->DADDR = (hpcd->USB_Address | USB_DADDR_EF);
hpcd->USB_Address = 0U;
}
...
}
By analyzing the above code, the device calls USBD_SetAddress in sequence after receiving the set
address request. These four functions are USBD_LL_SetUSBAddress, HAL_PCD_SetAddress and
USB_SetDevAddress. And in the most In the USB_SetDevAddress function after Go to set address.
19/109
In the previous HAL_PCD_SetAddress function, the received address was stored in hpcd's In the
USB_Address field, this field is processed in the PCD_EP_ISR_Handler function.
PCD_EP_ISR_Handler is the endpoint event processing function, which is called when the
endpoint data is successfully sent or received. letter from Judging from the judgment conditions in
the number, when the endpoint event is IN, the endpoint number is 0 and the transmission length is
0, it will be based on The contents of the USB_Address field are used to set the address register of
the USB module. An IN event of length 0 at endpoint 0 is It is an event that Status IN in control
transmission is sent successfully (see 2.3.2 Control Transmission for details) . This feature of ST's
HAL library The management method is to set the USB address to the register to take effect only
after the status returns successfully.
2.2.2.2 HAL library STM32F7 series set address request
The code in the HAL library that handles setting address requests is as follows:
HAL_StatusTypeDef
USB_SetDevAddress (USB_OTG_GlobalTypeDef *USBx, uint8_t address){
uint32_t USBx_BASE = (uint32_t)USBx;
USBx_DEVICE->DCFG &= ~ (USB_OTG_DCFG_DAD);
USBx_DEVICE->DCFG |= ((uint32_t)address << 4) & USB_OTG_DCFG_DAD;
return HAL_OK;
}
The setting address request of the F7 series is similar to that of the F1 series. Only the different
parts are listed above, and other processing procedures are the same. same. Different parts appear in
the usb_ll library. In the F7 series, the USB_SetDevAddress function directly converts the received
The address is set to the corresponding register. This is because in the OTG module on the F7 chip,
the address will be in the next Status IN will only take effect after it is sent successfully, so there is
no need to wait until Stauts IN is sent successfully before setting it in the code.
2.2.2.3 TeenyUSB Set Address Request
The code that handles the set address request is as follows:
static void tusb_standard_request(tusb_device_t* dev,
tusb_setup_packet* setup_req){
...
case USB_REQ_SET_ADDRESS:
dev->addr = LO_BYTE(setup_req->wValue);
#if defined(USB_OTG_FS) || defined(USB_OTG_HS)
tusb_set_addr(dev);
#else
dev->ep0_tx_done = tusb_set_addr;
#endif
...
}
#if defined(USB_OTG_FS) || defined(USB_OTG_HS)
20/109
static void tusb_set_addr (tusb_device_t* dev){
USB_OTG_GlobalTypeDef *USBx = dev->handle;
USBx_DEVICE->DCFG &= ~ (USB_OTG_DCFG_DAD);
USBx_DEVICE->DCFG |= (dev->addr << 4) & USB_OTG_DCFG_DAD ;
}
#else
// Callback function for set address setup
static void tusb_set_addr(tusb_device_t* dev){
if (dev->addr){
GetUSB(dev)->DADDR = (dev->addr | USB_DADDR_EF);
dev->addr = 0;
}
}
#endif
void tusb_ep_handler(tusb_device_t* dev, uint8_t EPn){
...
if ( (EP & USB_EP_CTR_TX)) { // something transmitted
if(EPn == 0 && dev->ep0_tx_done){
// invoke status transmitted call back for ep0
dev->ep0_tx_done(dev);
dev->ep0_tx_done = 0;
}
TUSB_CLEAR_TX_CTR(GetUSB(dev), EPn, EP);
tusb_send_data_done(dev, EPn, EP);
}
...
}
In the tusb_standard_request function of TeenyUSB, after receiving the set address request,
according to the USB module , choose different processing methods. If it is an OTG device, directly
call the tusb_set_addr function to set the address into the register; if it is an FS device, hang the
tusb_set_addr function on the ep0_tx_done callback function.
When the data transfer of endpoint 0 is completed, the tusb_set_addr function registered on
ep0_tx_done is called.
Complete address settings.
2.2.3 Configuration device Configuration
In the "USB Specification", the device must be configured before it can be used. When the device
receives the configuration device (Set Configuration) request, the device selects a configuration
based on the configuration value in the current request. Generally, a device only has a configuration,
so after receiving the configuration device request, the current configuration is simply recorded for
future reading.
The Get Configuration request returns data.
After the configuration is determined, each interface (Interface) in the configuration can also have
multiple different interface configurations.
Set Interface request for selection. After receiving the set interface request, determine the interface
used by the device, according to
The endpoint descriptor in the interface descriptor initializes the endpoint.
configuration.
For USB devices with only one configuration and only one setting for the interface, you can use the
21/109
The endpoints reached are configured once. Because subsequent Set Configuration and Set
Interface requests will not change the endpoint
usage.
Therefore, the TeenyUSB library does not follow the requirements of the USB specification after
receiving the configuration (Set Configuration) request, but configure all endpoints directly after
receiving the reset event. official
The party's HAL library only initializes endpoint 0 during Reset, and completes the initialization of
other endpoints in subsequent set configuration requests.
2.2.4 Power management
If it is a bus-powered device, the required power needs to be declared in the configuration
descriptor. If set The device supports Remote Wake Up
, also needs to be specified in the configuration descriptor. The host can obtain status by
(Get Status) Requests to read the current power status of the device. Requesting to set the device via
Set Feature is
Does it support remote wake-up? For details, see 2.5.4 Read Status Request.
2.2.5 Device request processing/device enumeration (Request Processing)
Device request processing is also called device enumeration.
At this stage, the host will obtain the device through a series of control transmission commands.
equipment information and configure the device. This stage is the most critical and complex
processing stage of USB devices, which will be discussed later.
Dedicate a separate section (2.5 Standard Equipment Requests) for detailed instructions.
2.2.6 Requests related to interface type
Different interface types have some type-related requests. For example, the HID device's Read
Report Descriptor (Get Report Descriptor) request, CDC device Set/Get Line Coding request.
After successful processing of these requests, the device can work normally.
2.2.7 Summary of basic operations
When the USB device is connected to the USB cable, the host first resets the device so that the USB
module of the device works into a known state. After reset, the host first reads the first 8 bytes of
the device descriptor and obtains the device endpoint 0, the maximum packet length, then set the
device address, and start sending control transmission commands to enumerate the device. After
enumeration is complete, the host can communicate with the device. The host side will install the
corresponding driver for the device during the enumeration process.
Power on the device
Device reset
Get the first 8 bytes of the device descriptor
Device reset (optional operation)
Set device address
22/109
Get device descriptors and other descriptors
Configure device
Configure the interface (optional operation)
Configure device properties (optional)
Set the device according to the device interface type
(interface type related)
The device is working properly
Table 1 - Basic operating sequence of equipment
2.3 USB data stream
USB data transmission consists of basic transactions (Transaction), and above the basic
transactions, there is Control (Control) , Interrupt , batch (Bulk) , four data transmission modes of
synchronous (Isochronous). These four transmission modes are introduced below.
2.3.1 Basic transactions
USB data transfer transactions are initiated by the host. A complete transaction includes the Token
Stage. , Data Stage and Acknowledge Stage . Instructed by the host during the token phase It
indicates the type of transmitted data. The data stage transmits data or status. After the data stage is
the response stage, which is used to determine the data. Whether sent successfully. As shown below:
IMAGE
Basic data transfer transactions (excluding synchronization transactions)
The device can only respond with ACK after receiving the Setup transaction, so the Setup
transmission can only be successful. device received
After the Out transaction, if it can be received, respond with ACK; if it cannot be received, such as
the cache is not ready, respond with NAK;
If the transmitted data cannot be processed correctly, you can respond with STALL. So the Out
transaction has Out Data, Out NAK,
Out Stall three states. IN transactions are similar to OUT transactions, except that the status of IN
transactions is returned in the data phase.
There is no response phase for IN and OUT transactions transmitted synchronously.
In batch transmission and control transmission of high-speed equipment, according to the above
figure, whether the OUT transaction is successful or not needs to wait until the response phase.
Only then did we know that if the response is NAK, the content sent by the host in the data phase
will not be received by the slave, which wastes this part of the bandwidth.
Width. In order to save bus bandwidth, in addition to IN and OUT transactions, high-speed devices
also have PING transactions to check whether the endpoint is
Not Ready responds with NYET status if the endpoint is not ready. When should PING packets be
sent for high-speed equipment?
23/109
Inquiry status, when to directly initiate the OUT transaction? The USB protocol provides a less than
perfect solution to this problem. Details
See 3.6.3 Aperiodic Endpoints.
When introducing different types of transmission types later, a simplified representation will be
used to describe a transaction. A simplified representation package
Containing the three stages of token, data and response, a complete transmission consists of
multiple basic transactions.
The three stages of token, data and response also have lower-level data structures and processing
methods, which are not introduced in this article.
The lower-level data structure is in the reference document "USB 2.0 Specification" 8.4
24/109
2.3.2 Control transfer
Control transfer is the most basic and important transfer of USB, and all devices will support this
transfer mode. Equipment Configuration, commands, and status are all passed through control
transfer. Control transmission is initiated by the host. The host first transmits a Setup package to the
device, the Setup package contains the type of control transfer and other parameter information.
After the device receives the Setup package, first parse the Setup package and perform subsequent
operations based on the contents of the Setup package.
There are 3 control transfer types, respectively for: Write data control transfer (Control Write),
read data control transmission (Control Read) and no data control transmission (No-Data Control)
.
Control transfer data flow (from USB2.0 specification)
Control transfer will return three statuses, completed, error and processing. See table below:
Control transfer status return (from USB2.0 specification)
In USB communication, status is sent from the device to the host, and different transmission types
handle the status differently.
If the control transfer device cannot handle it, the device sets both the IN and OUT directions of
endpoint 0 to the STALL state at the same time.
Since the Setup package is not affected by the endpoint status and can always be sent successfully,
endpoint 0 is set to the STALL state and it will not affect subsequent Setup packet transmission. The
25/109
device starts a new control transmission processing instance after receiving a new Setup packet.
Process, and set the status of the endpoint based on this processing result.
In the write control transfer, after the device successfully receives all data, it sends a packet with a
packet length of 0 to tell the host the data
Transfer successful.
During the read control transfer, the device tells the host that the data transfer was successful
through an ACK response. This ACK response refers to
Yes: After the host reads the correct data, it will send a status packet with a length of 0 to the device,
and the device receives this status packet.
After receiving the status packet, the response ACK indicates successful transmission, the response
NAK indicates it is being processed, and the response STALL indicates an error.
2.3.2.1 Write data control transmission Control Write
The host initiates a write data control transfer, first sends a Setup packet to the device, then starts
reading endpoint 0 of the device, and
Send subsequent data to device endpoint 0. At this time, there is no data in the IN direction of
endpoint 0 of the device, and the host will receive a NAK message.
number, indicating that the species is being processed. After the device receives the Setup packet
from the host, the OUT direction of endpoint 0 is generally controlled by the hardware.
Control will change to the NAK state and subsequent data sent by the host will block.
The device parses the received Setup packet and obtains the length information. Prepare to receive
according to the length information in the Setup packet
Collect the cache and set the OUT0 endpoint to the VALID state after the cache is ready. At this
time, the data sent by the host can be correct
sent to the device. After the device receives the host data, it processes the data. After successful
processing, it sends a data length of 0 packet (i.e. status packet) to the host. After receiving the
status packet, the host completes the write data control transmission. If the device does not support
Or the data is incorrect and the endpoint is set to STALL state. The host performs corresponding
processing after receiving the STALL status.
How to interpret the above figure: When analyzing the Out direction or Setup data, the host's
processing is in front of the transaction block, and the device The processing is behind the
transaction block; when analyzing the data in the In direction, the host's processing is behind the
transaction block, and the device's processing before the transaction block. Gray IDLE means that
the host will not send out token packets to request data at this stage. It can be analyzed during
analysis. neglect. The process after decomposing the above figure is as follows, from left to right:
Write control transfer process decomposition
2.3.2.2 Read data control transmission Control Read
The host initiates a read data control transfer, first sends a Setup packet to the device, and then starts
reading endpoint 0 of the device. this
26/109
At that time, there is no data in the IN direction of endpoint 0 of the device, and the host will
receive a NAK signal, indicating that the data is being processed. Equipment collection
After receiving the Setup packet from the host, the OUT direction of endpoint 0 will generally be
controlled by hardware and change to the NAK state. The host sends
Subsequent status packets sent will be blocked.
The device parses the received Setup packet and obtains the length information. According to the
length information and content in the Setup package
Start sending data in the IN direction through endpoint 0, and the host starts receiving data in the IN
direction. The device can send data during
After completion or when starting data transmission, set the OUT direction of endpoint 0 to the
VALID state to receive the status of the host.
status package.
After receiving all data packets, the host sends a status packet with a length of 0 to complete this
read data control transmission. if
The device is not supported or the data is incorrect. Set the endpoint to STALL state.
2.3.2.3 No-data Control Transmission
The required data is included in the Setup package, so no subsequent data transfer phase is required.
The host has sent the Setup packet After that, the device processes it according to the Setup content
and successfully sends a status packet with a packet length of 0. If it fails, the endpoint is set to
STALL. State.
No data control transfer process
2.3.2.4 Control transmission endpoint settings
The default endpoint 0 of the USB device is the control transmission endpoint, and the maximum
packet length of the FS device endpoint 0 can be 8, 16, 32, 64 Bytes, the maximum packet length of
HS device is 64 bytes. In order to be compatible with both FS and HS devices, TeenyUSB sets
endpoint 0 by default The size is set to 64 bytes. When the device is connected to the host, the host
will send a Setup packet to read the device descriptor and Set the length of the data to be retrieved
to 8. The first 8 bytes of the device descriptor contain the maximum packet length of endpoint 0,
and the host parses The first 8 bytes of content get the maximum packet length of endpoint 0,
Subsequent control transfers are transmitted at the actual endpoint 0 maximum packet length. The
Setup command that controls the transfer will ignore the actual status of the endpoint and transfer it
to the device, so the Setup command that controls the transfer Stages always succeed. When the
Setup transmission is successful, the OUT direction of device endpoint 0 will automatically switch
to the NAK state. When writing data to control transmission, subsequent data sent by the host will
be blocked until the device sets the OUT direction of endpoint 0. Set to VALID state. In this way,
the device has sufficient time to parse the contents of the Setup packet and transmit data for
subsequent control stages. The segment is ready to be cached.
2.3.3 Isochronous transfer
Synchronous transmission is suitable for situations where the data transmission frequency is fixed
and errors can be tolerated. Such as audio signals, camera vision frequency signal. This type of data
27/109
requires real-time performance and can tolerate errors. For FS devices, the maximum length of an
ISO packet is 1023 Bytes, for HS devices, the maximum packet length is 1024 bytes. In the highbandwidth endpoint of HS devices, one frame can deliver up to There are three more packets with
the maximum packet length, that is, a maximum of 3072 bytes of data can be transmitted in one
frame.
2.3.4 Interrupt transfer
Interrupt transmission performs data transmission at a fixed frequency. USB host side based on the
endpoint descriptor reported by the device (Endpoint Descriptor) information to allocate bandwidth
for the interrupt transmission endpoint. As with isochronous transmission, on the HS device In
high-bandwidth endpoints, a frame can be transmitted up to three times. Synchronous transmission
and interrupt transmission are sometimes called periodic together. Periodic Transfer . For periodic
transmission, the host will allocate bandwidth based on the reported frequency information.
2.3.5 Bulk transfer
Bulk transfers are similar to interrupt transfers, except that batch transfers occupy all available
bandwidth on the bus.
Therefore in batch transfers, the time interval field is not used in the quantity endpoint descriptor.
In high-speed devices, the time interval field is used to indicate to the host a number of NAKs in the
frame.
2.4 Transfer Complete Condition Transfer Complete
Condition
In control transfer, interrupt transfer and batch transfer, the concept of transfer completion (Transfer
Complete) is introduced.
The completion of the transmission indicates that the data that needs to be transmitted this time has
been sent, and the receiver can proceed with subsequent processing. The control transfer can be
seen as a batch transmission with a Setup packet as the precursor. In USB, a transfer task will be
broken down into multiple ones.
A basic transaction, such as an IN transfer, consists of multiple IN transactions. There is no
requirement in the USB protocol to send data when transmitting data.
Send the data length that needs to be transmitted this time, so the USB protocol uses the maximum
packet length and the data length of each basic transaction to determine the total length of a data
read.
The endpoint descriptor of the USB device contains the maximum packet length (Max Packet Size
or MPS) information of the endpoint.
The USB endpoint can only transmit data with the maximum packet length at one time. If the data
exceeds the maximum packet length, it will be divided into many multiple packets for transmission.
During packet transmission, the length of the previous data packets is the maximum packet length,
and only the last packet of data is transmitted according to the actual length. when transmitting
When the data meets the completion conditions, it indicates that a data transmission process has
been completed.
There are two conditions for transfer completion:
28/109
1. When the data received by the device or host is not the maximum packet length, it means that the
current transmission is completed and subsequent processing can be performed.
This situation is also called receiving a short packet (Short Packet);
2. When the data received by the device or host is consistent with the expected data length (expect
length), indicating that the current transmission is completed,Can be processed later;
The first point is easier to understand. When a transmission is to be completed, send data whose
data length is not the maximum packet length, that is,Yes, there are two situations:
a. If the data length is not divisible by the maximum packet length, the last packet will naturally be
smaller than the maximum packet length, triggering the transmission completion bar.
pieces;
b. If the data length is divisible by the maximum packet length, and the last packet is equal to the
maximum packet length, another packet with a length of Zero Length Packet to trigger the
transmission completion condition.
2.4.1 Expected data length
Regarding point 2, what is the expected data length?
On the USB host on the PC side, taking libusb as an example, in order to receive data from the USB
device, you need to create a
When creating a transfer, a piece of memory will be allocated. The size of this memory is the
expected number of USB hosts.
According to the length. After the transfer in libusb is created, it is submitted to the background for
monitoring, and the callback is called when a transfer is completed.
Function gets data.
On a USB device, a piece of memory is also used to receive endpoint data. The size of this memory
is the size of the USB device.
When the data sent by the host meets the transfer completion condition, the handler on the USB
device side will receive the expected data length of the end.
The data is handed over to the application layer for processing.
Whether on the host or on the device, in some special cases the expected data length is usually an
integer multiple of the maximum packet length.
Under this condition, the expected data length can be equal to the maximum packet length, so that a
transmission will be completed when each packet of data is received.
2.4.2 Some examples of transfer completion
If the sender is sending data, add a 0 packet for the data packet that is evenly divisible by the
maximum packet length. In this situation, There will always be a short packet (length less than the
maximum packet length or length 0). In this way, no matter what the other party’s expected length
is, the transmission completion conditions can be correctly triggered, as shown below:
If the sender sends a packet that is evenly divisible by the maximum packet length, it does not add 0
packets. In this case, transmission is possible will hang, as shown below:
29/109
No short packet transmission - hang
In the above figure, the sender sends a piece of data whose length is exactly 4 times the maximum
packet length, and the following piece of data is smaller than the length of the receiver.
The expected length, and the receiver did not receive the short packet, the transfer did not complete
and hung. In this case, there are two ways to deal with it:
One is to process the received data through timeout, and then reset the receive cache. Such as profit
Use SOF interrupt to periodically check whether there is data in the received buffer. If there is, it
will be processed regardless of whether a short packet is received.
reason.
The other is that the receiver sets the expected length to the maximum packet length, so that each
packet of data will trigger the transmission completion condition, that is
So that the transmission can be completed normally without short packets, as shown in the
following figure:
No short packet transmission - normal
These two processing strategies for non-short packet transmission will be explained in more detail
in the subsequent CDC serial port routines.
2.5 Standard Device Request
Standard device requests are delivered through the control transfer of endpoint 0. Control transfers
are initiated by the host. The host first sends a request to the device.
Send a Setup packet to notify the device of the requested type and parameters. After the device
determines based on the content of the received Setup packet
continued action. In TeenyUSB, the code for handling standard device requests is in the teeny_usb.c
file. When the endpoint receives
After arriving at the Setup package, use the tusb_setup_handler function to select the processing
method based on the contents of the Setup package.
2.5.1 Setup data structure
Standard requests are transmitted through control transmission. The definition of the Setup packet
data structure marking the device request is shown in the following table:
Table 3 Setup data format (table comes from USB2.0 specification)
30/109
Setup data is transmitted in the Setup packet of control transmission, the direction is from host to
device, and the length is 8 bytes. There will only be one control transfer at a time between the host
and the device's default channel (endpoint 0), so when designing the device-side code, an 8-byte
cache can be reserved for the device to save the Setup packet on the current endpoint 0.
TeenyUSB Medium Code that defines the structure type of the Setup package:
typedef struct _tusb_setup_packet{
uint8_t bmRequestType;
uint8_t bRequest;
uint16_t wValue;
uint16_t wIndex;
uint16_t wLength;
} tusb_setup_packet;
Different request types have different meanings of each field. The following table shows the
meaning of each field in standard device requests:
Table 4 Standard device request (table from USB2.0 specification)
31/109
In the above table, some bmRequestType items have multiple lines of content, indicating that this
type of request can have multiple recipients. See Table 2 for the definition of Setup data format.
When the receiver is an interface or endpoint, specify the interface number through the wIndex field
or the endpoint number.
The following is the main function for processing the Setup package in TeenyUSB:
// Setup packet handler
void tusb_setup_handler(tusb_device_t* dev) {
tusb_setup_packet *setup_req = &dev->setup;
if( (setup_req->bmRequestType & USB_REQ_TYPE_MASK) ==
USB_REQ_TYPE_CLASS){
tusb_class_request(dev, setup_req);
#if defined(HAS_WCID)
}else if((setup_req->bmRequestType & USB_REQ_TYPE_MASK) ==
USB_REQ_TYPE_VENDOR){
tusb_vendor_request(dev, setup_req);
32/109
#endif
}else{
tusb_standard_request(dev, setup_req);
}
}
By parsing the value in the bmRequestType field, determine the type of the current Setup package
and call the corresponding receive or's processing function. When the type is a standard request, the
tusb_standard_request function is called for further processing. This section mainly explains the
processing method in the tusb_standard_request function.
The function implementation is as follows:
// Standard request process function
static void tusb_standard_request(tusb_device_t* dev, tusb_setup_packet*
setup_req) {
switch (setup_req->bRequest) {
case USB_REQ_SET_ADDRESS:
dev->addr = LO_BYTE(setup_req->wValue);
#if defined(USB_OTG_FS) || defined(USB_OTG_HS)
tusb_otg_set_addr(dev);
#else
dev->ep0_tx_done = tusb_fs_set_addr;
#endif
tusb_send_data(dev, 0, 0, 0);
break;
case USB_REQ_GET_DESCRIPTOR:
tusb_get_descriptor(dev, setup_req);
break;
case USB_REQ_GET_STATUS:
tusb_send_data(dev, 0, (uint16_t *) &dev->status, 2);
break;
// Only support one configuration, so just save and return the config
value
case USB_REQ_GET_CONFIGURATION:
tusb_send_data(dev, 0, (uint16_t *) &dev->config, 1);
break;
case USB_REQ_SET_CONFIGURATION:
dev->config = LO_BYTE(setup_req->wValue);
tusb_send_data(dev, 0, 0, 0);
break;
// Only support one alt setting, so just save and return the alt
value
case USB_REQ_GET_INTERFACE:
tusb_send_data(dev, 0, &dev->alt_cfg, 1);
break;
case USB_REQ_SET_INTERFACE:
dev->alt_cfg = LO_BYTE(setup_req->wValue);
tusb_send_data(dev, 0, 0, 0);
break;
case USB_REQ_SET_FEATURE:
if(setup_req->wValue == USB_FEATURE_REMOTE_WAKEUP){
dev->remote_wakeup = 1;
tusb_send_data(dev, 0, 0, 0);
33/109
break;
}
// otherwise fall to default
case USB_REQ_CLEAR_FEATURE:
if(setup_req->wValue == USB_FEATURE_REMOTE_WAKEUP){
dev->remote_wakeup = 0;
tusb_send_data(dev, 0, 0, 0);
break;
}
// otherwise fall to default
default:
// Error condition, stall ep0
STALL_EP0(dev);
break;
}
}
In the above function, except for reading the descriptor (Get Descriptor), other requests are within
this function
Simple processing is done and the status is returned.
2.5.2 Set Address Set Address
After receiving this request, the device sets the address of the device. This address is generally set
in the relevant register of the USB module.
middle. Different chips have different setting methods, which are detailed in the section 2.2.2
Setting the Address.
2.5.3 Get Descriptor
Depending on the parameters, various descriptors of the device are read. The descriptor is the core
content of the USB protocol. Each device
Each function and feature is described through descriptors. The first byte of the descriptor is the
descriptor length, followed by the description
Descriptor type. Generally, the host will first send a short read descriptor command to get the
previous part of the descriptor. According to
This part of the content gets the length of the entire descriptor, and then sends the complete read
descriptor command to get the complete descriptor.
content.
The high 8 bits of the wValue field in the Setup package represent the descriptor type, and the low 8
bits represent the descriptor index. If one
Each device has multiple configuration descriptors, and the required configuration descriptor is
obtained through the index value. TeenyUSB does not support multiple configurations
device, so the index of the descriptor is not detected when obtaining the configuration descriptor,
and the configuration description of the current device is directly returned.
symbol.
2.5.4 Get Status
Read a 2-byte long Status data. In the standard request, the Status data only defines bit0 to indicate
whether it is self-supplied.
34/109
Power, bit1 indicates whether remote wake-up is supported, and other bits default to 0.
In the TeenyUSB protocol stack, Status data comes from the attribute field of the configuration
descriptor, and is set according to the attribute field
Status value. In the teeny_usb_desc.c file, set the value defined by the DEV_STATUS macro
according to the configuration descriptor,
As shown in the following code:
// Power status
#define BULK_DEV_STATUS0 USB_CONFIG_SELF_POWERED
#define BULK_DEV_STATUS1 USB_CONFIG_REMOTE_WAKEUP
#define BULK_DEV_STATUS ((BULK_DEV_STATUS0) |(BULK_DEV_STATUS1) )
In the device initialization function, assign the value of DEV_STATUS to the status field of the
device structure, and then set
#define BULK_TUSB_INIT_DEVICE(dev) \
do{\
/* Init device features */
\
memset(dev, 0, TUSB_DEVICE_SIZE); \
dev->status = BULK_DEV_STATUS;\
dev->descriptors = &BULK_descriptors;\
}while(0)
The device returns to the host after receiving the read status request.
The read status requests supported by USB devices are shown in the following table:
state
receiver
wValue
data bits
Data bit meaning
Bit0
0: bus powered
1: Self-powered
Device status
Device
0
Bit1
0:Support remote wake-up
1: Does not support remote wake-up
Interface status
Endpoint status
Interface
Endpoint
0
0
Bit2-Bit15
Reserved, default is 0
Bit0-Bit15
Reserved, default is 0
Bit0
1: Endpoint paused
0: The endpoint is working normally
Bit1-Bit15
Reserved, default is 0
Table 5 Read status request
2.5.5 Read/set configuration, read/set interface
In the TeenyUSB protocol stack management, the Get/Set Configuration and Get/Set Interface
requests are not treated specially.
management, the received parameters are saved in the request to set parameters, and the set
parameters are saved in the request to read parameters.
35/109
This is because TeenyUSB only supports one configuration, and only one type per interface
(Interface)
set up. There is no endpoint setting after receiving the setting configuration request because the
endpoint is set when the device is reset.
0 parameters will set all endpoints together.
According to standard practice, when the device receives the USB reset signal, only the endpoint 0
parameters are configured to send and receive control transmissions.
data. After receiving the configuration request, the device selects the current configuration based on
the parameters and selects the default interface for the interface in the current configuration.
Confirm the configuration and set the corresponding endpoint according to the parameters of the
default interface configuration. After the device receives the request to set the interface, it checks
the new interface
Is the port setting valid?
If it is valid to configure the endpoint according to the newly set interface, return ACK;
If the new interface configuration is invalid,
Return STALL.
If multiple configurations are supported, after the device receives the Set Configuration request, it
will reset the terminal according to the selected configuration.
point.
If multiple interface configurations are supported, when receiving a request to configure an
interface, you need to first determine which interface the current request is sent to.
interface, and then reset the endpoint parameters of the interface according to the new interface
configuration.
2.5.6 Set/Clear Feature
Set clear characteristics. The wValue field indicates the content of the characteristics to be
set/cleared. TeenyUSB only supports the remote wakeup feature. Other feature requests will trigger
an error and return STALL state. The complete feature set is shown in the table below:
characteristic
receiver
wValue Remark
Stop endpoint
endpoint
0
Remote wake up
equipment 1
Turn on/off the remote wake-up function of the device
test
equipment 2
Used when testing
BEquipment HNP
equipment 3
Only valid for OTG devices
A device HNP
equipment 4
Only valid for OTG devices
Table 6 Set/Clear Feature Request
Set all endpoints to STALL mode
2.6 USB data flow control
In the design of any communication protocol, data flow control is a very important issue. When the
data of both communicating parties
36/109
When the processing rates are inconsistent, flow control is required to avoid data overflow and loss.
USB is a half-duplex communication protocol.
The transfer of data is completely controlled by the host. Therefore, the flow control on the host
side is relatively simple. When the data exceeds the processing capacity of the host,
The host can no longer initiate transfers. For the device side, when the data exceeds the device's
processing capability, the device passes NAK
The response method refuses to receive data from the host. The exception is the Setup packet in the
control transfer. The Setup packet cannot be
Rejected, the reception will be successful in any case, so the device-side code will prepare a
separate buffer for the Setup package when designing.
live. For subsequent data packets in the Setup package, the host and device can perform flow
control processing based on actual conditions.
Flow control of data sent from the host to the device: The host sends data to the device through an
OUT transaction. When the data exceeds the device's
When processing capabilities, the device puts the OUT endpoint into the NAK state. After that, the
host's OUT transaction will receive a NAK response and proceed
Enter the waiting state. When the device can continue to receive, the OUT endpoint is set to the
VALID state.
Flow control of data sent from the device to the host: Taking libusb as an example, when the data
received by the host exceeds its processing capacity, the host
The machine can temporarily not submit (without calling libusb_submit_transfer) the completed
transfer, so that the USB bus will not
A read data command will be initiated. If the device cannot receive the command to read data, the
data to be sent will remain in the cache. this
This process can be understood as the host actively reading device data. The host will initiate an IN
transaction only when it has sufficient processing capabilities. Therefore
The response phase of the IN transaction only has ACK and no other status.
2.6.1 Flow control of synchronization endpoints
Synchronous endpoint flow control in USB is different from other types of transfers. Synchronous
transfers have no concept of transfer completion, nor
The concept of flow control. For example, in the data stream of a sound card, the sound is real-time.
Just because the data exceeds the processing capabilities of one party, it cannot
Stop data without processing. Therefore, the synchronization endpoint only has Enable and Disable
states, and there is no such state as NAK. when
When the endpoint is in the Enable state, it means that the channel is successfully established and
data can be sent; when the endpoint is in the Disable state,
Indicates that the channel is disconnected and no more data can be sent. If the data of the
synchronization endpoint exceeds the processing capacity, just discard the current data.
In the data sending completion interrupt processing function of the endpoint, the PMA description
table address used by the current application is obtained.
Immediately sets the send data length to 0. Later, subsequent processing will be performed based on
the length of the remaining data at the endpoint. if there is not
More data needs to be transferred, the sent data length will remain at 0, and a packet with a length
of 0 will be sent.
37/109
In this processing method, regardless of whether the ISO IN endpoint has data, the host and device
will frequently access the data.
IN In the interrupt of transfer completion. When designing the ISO endpoint in actual applications,
the endpoint will be configured according to the actual bit rate of transmitted data.
Focus on the maximum packet length and transmission frequency, so that the rate of transmitted
data is consistent with the rate of data generation, and try not to "idle"
.
The data of the ISO OUT endpoint is sent from the host to the device. The host will start the
transmission only when there is data to be sent. Device side
Keep the endpoint available, waiting for data from the host.
2.7 Device-side development and debugging steps
Based on the previous basic operation process, here is a summary of the debugging process when
developing USB devices.
2.7.1 USB module core configuration
When developing a USB device, when the device is connected to the host and can receive a reset
command from the host, it means that the USB module is at least
Already working, the USB core register configuration is basically correct. If the reset command
cannot be received, it means that there may be
These problems: incorrect core register configuration, incorrect pin mode, incorrect clock
configuration, incorrect hardware wiring, etc.
2.7.2 Endpoint 0 OUT direction configuration
Furthermore, if a control transmission for reading the device descriptor can be received, it means
that the OUT direction of endpoint 0 has been configured.
correct. Due to the particularity of control transmission, endpoint 0 must be able to correctly receive
the control transmission command from the host under any circumstances.
In the Setup package, generally the chip will control the settings of endpoint 0 on the hardware, and
it cannot be configured by the user. Therefore this
The step will usually succeed. If it fails, you should check whether the relevant configuration of the
endpoint 0 OUT direction is correct.
The data immediately following the Setup token contains the contents of the Setup, if it can be read
from the receive buffer of endpoint 0
to the correct Setup command,
This means that the receive cache of endpoint 0 is set successfully.
At this time, according to the received Setup
The package contents are ready for subsequent operations. If only the Setup flag is received, but the
contents of the Setup packet are not received correctly, say
There is a problem with the receive cache configuration of endpoint 0. Focus on checking the cache
configuration of the endpoint.
2.7.3 Endpoint 0 IN direction configuration
The first control transfer request is generally to obtain the first 8 bytes of the device descriptor.
After receiving this request, the device can be
38/109
The first 8 bytes of the descriptor are sent out through endpoint 0 IN. If the sending is successful,
the host will then send the set address ask. If it fails, the host will reset and send the request to read
the device descriptor again. On failure, primarily checks endpoint 0
Whether the configuration of the IN direction is correct and whether the cache settings are correct.
If this step is successful, endpoint 0 has been implemented.
After the processing capacity is restored, subsequent data can be processed. If the current data is not
ready, you can send it to the synchronization endpoint.
Send a packet of length 0. In the IN transaction of the synchronization endpoint, if the device data is
not ready, data with a length of 0 is sent. same
In the OUT transaction of the step endpoint, if the host data is not ready, the OUT transaction will
not be initiated.
2.6.1.1 Synchronous endpoint flow control method in TeenyUSB
In TeenyUSB, ISO data in the IN direction, if the device is not ready, send a sync of length 0 Bag.
Therefore, the following processing method is adopted in the PC side code
if(xfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS){
if(xfer->actual_length > 0){
struct libusb_iso_packet_descriptor * desc = xfer->iso_packet_desc;
// inplace copy make buffer data continue
unsigned char * buf_actual_data = xfer->buffer;
unsigned char * buf_packet_data = xfer->buffer;
for(int i=0; i< xfer->num_iso_packets; i++){
if(desc->status == LIBUSB_TRANSFER_COMPLETED){
if(buf_actual_data != buf_packet_data){
memcpy(buf_actual_data, buf_packet_data,
desc->actual_length);
}
}
buf_actual_data += desc->actual_length;
buf_packet_data += desc->length;
desc++;
}
int real_length = buf_actual_data - xfer->buffer;
if(real_length > 0){
emit ep->m_parent.epDataReady( ep→m_info.bEndpointAddress,
QByteArray((char *)xfer->buffer, real_length));
}
}
}
The above code is written based on libusb and first checks the endpoint type and actual data length.
In synchronous transmission, this
The actual truly transmitted length in is not the received data length, but the buffer size submitted to
the lower-level communication driver.
The data length is in the iso_packet_desc data structure.
Extract the real length of the data from iso_packet_desc,
39/109
and piece the data together. If the length of the last actually received data is 0, no processing will be
performed. If it is not 0, trigger
epDataReady signal. So when the device data is not ready, the data length of the ISO endpoint send
direction can be set to
0. Here is the code for handling the ISO endpoint in the device:
void tusb_send_data_done(tusb_device_t* dev, uint8_t EPn){
...
else if( IS_ISO() ){
ep->tx_pushed = 0;
pma = (EP & USB_EP_DTOG_TX) ? PMA_TX0(dev, EPn) : PMA_TX1(dev, EPn);
pma->cnt = 0;
}
...
if(ep->tx_size || (EPn == 0 &&
ep->tx_last_size == GetInMaxPacket(dev, EPn)) ){
copy_tx(dev, ep, pma, ep->tx_buf, ep->tx_size,
GetInMaxPacket(dev, Epn));
PCD_SET_EP_TX_STATUS(GetUSB(dev), EPn, USB_EP_TX_VALID);
return;
}
...
}
In the data sending completion interrupt processing function of the endpoint, the PMA description
table address used by the current application is obtained.
Immediately sets the send data length to 0. Later, subsequent processing will be performed based on
the length of the remaining data at the endpoint. if there is not
More data needs to be transferred, the sent data length will remain at 0, and a packet with a length
of 0 will be sent.
In this processing method, regardless of whether the ISO IN endpoint has data, the host and device
will frequently access the data.
IN In the interrupt of transfer completion. When designing the ISO endpoint in actual applications,
the endpoint will be configured according to the actual bit rate of transmitted data.
Focus on the maximum packet length and transmission frequency, so that the rate of transmitted
data is consistent with the rate of data generation, and try not to "idle"
.
The data of the ISO OUT endpoint is sent from the host to the device. The host will start the
transmission only when there is data to be sent. Device side
Keep the endpoint available, waiting for data from the host.
2.7 Device-side development and debugging steps
Based on the previous basic operation process, here is a summary of the debugging process when
developing USB devices.
2.7.1 USB module core configuration
When developing a USB device, when the device is connected to the host and can receive a reset
command from the host, it means that the USB module is at least
40/109
Already working, the USB core register configuration is basically correct. If the reset command
cannot be received, it means that there may be
These problems: incorrect core register configuration, incorrect pin mode, incorrect clock
configuration, incorrect hardware wiring, etc.
2.7.2 Endpoint 0 OUT direction configuration
Furthermore, if a control transmission for reading the device descriptor can be received, it means
that the OUT direction of endpoint 0 has been configured.
correct. Due to the particularity of control transmission, endpoint 0 must be able to correctly receive
the control transmission command from the host under any circumstances.
In the Setup package, generally the chip will control the settings of endpoint 0 on the hardware, and
it cannot be configured by the user. Therefore this
The step will usually succeed. If it fails, you should check whether the relevant configuration of the
endpoint 0 OUT direction is correct.
The data immediately following the Setup token contains the contents of the Setup, if it can be read
from the receive buffer of endpoint 0
to the correct Setup command,
This means that the receive cache of endpoint 0 is set successfully.
At this time, according to the received Setup
The package contents are ready for subsequent operations. If only the Setup flag is received, but the
contents of the Setup packet are not received correctly, say
There is a problem with the receive cache configuration of endpoint 0. Focus on checking the cache
configuration of the endpoint.
2.7.3 Endpoint 0 IN direction configuration
The first control transfer request is generally to obtain the first 8 bytes of the device descriptor.
After receiving this request, the device can be
The first 8 bytes of the descriptor are sent out through endpoint 0 IN. If the sending is successful,
the host will then send the set address
ask. If it fails, the host will reset and send the request to read the device descriptor again. On failure,
primarily checks endpoint 0
Whether the configuration of the IN direction is correct and whether the cache settings are correct.
If this step is successful, endpoint 0 has been implemented.
Transmitting and receiving single data. Subsequent control transfers will deliver more than 8 bytes
of the packet, possibly exceeding the endpoint 0 maximum
If there is a problem with long packet processing, there will also be problems when enumerating
other information later.
2.7.4 Device enumeration
After the basic sending and receiving of endpoint 0 is successful, the enumeration process of
debugging the device begins.
Debugging devices during enumeration
Whether all requests are processed correctly. If a request is not processed correctly, you can see
information about the request that went wrong. For example, comment out the code that handles the
setting address request part of the device, and then connect it to the computer.
41/109
You can see the message that the address failed to be set in the device manager. If you comment out
the code that sends the configuration descriptor, you can see the message that obtaining the
descriptor failed in the device manager.
Failed to get descriptor
When an error occurs that fails to obtain the descriptor, mainly check whether there is any problem
with the data receiving and sending of endpoint 0.
Is the setting of the cache configuration correct ?
In devices such as STM32F0 and STM32F1 where the address is set to take effect immediately, if
the status transmission is not completed after setting the address, an error "Failed to set address
request" will appear, as shown below:
Set address request failed
When the request to set the address fails, mainly check whether there is any problem with the logic
of setting the address. Going to this step explains the previous step.
The step of obtaining the device descriptor has been successful.
42/109
2.7.5 Driver installation
After successful enumeration, the operating system will install the driver for the device. If it is a
standard USB device, such as a keyboard and mouse
Wait, the system will automatically install the driver; if it is a user-defined device, you need to
install the driver provided by the user.
Only after the driver is successfully installed can the user communicate with the device through the
application. During the driver installation process, the driver will also
The device performs some configurations. For example, the serial port driver will set and read the
encoding method of the device. If these requests are not processed correctly,
Even if you don't understand, you will make mistakes.
2.7.6 Using equipment
If it is a standard USB device, after the driver is successfully installed, it can be tested with common
software. For example: CDC
For serial port devices, after the driver is successfully installed, you can use the serial port test tool
to test; for U disk devices, the driver installation
After the installation is successful, you can see the drive letter in the system and perform operations.
If it is a customized device, the path to the device is usually obtained through SetupAPI on
Windows, using
The CreateFile function opens the device through the path to obtain the device handle, and finally
operates the device through the handle. in operation
If an error occurs during the process, get the specific error code through the GetLastError function,
and then analyze the cause of the error.
For devices supported by libusb, you can also use the API provided by libusb to operate. First pass
list_devices
Get the USB devices mounted on the bus, then filter the devices based on VID and PID, and finally
open them with the open function
Turn on the device to operate.
43/109
3 USB standard descriptor
USB descriptors are the most basic content of USB devices, and all information about the device is
reflected through descriptors. This chapter mainly introduces the function of each descriptor. Before
communicating user business data between the host and the device, the device-related descriptor is
first obtained. According to the content of the descriptor, mount the driver and determine how the
device is accessed. The descriptor also contains information about all endpoints used by the device,
through the descriptor can also determine the USB module's endpoint usage. The descriptor
structure in the USB device is shown in the figure below:
USB descriptor structure
The device type in the device descriptor in the figure above is composed of three fields, namely
bDeviceClass, bDeviceSubClass and bDeviceProtocol.
When the interface type is mentioned in the following text, it is also represented by xxxClass,It
consists of three fields: xxxSubClass and xxxProtocol.
At this time, it is abbreviated as "type is (a, b, c)" formula, where a represents Class, b represents
SubClass, and c represents Protocol.
This chapter does not introduce the contents of each standard descriptor field by field. The specific
meaning of each field of the descriptor is in "USB2.0
There is a detailed description in section 9.6 Standard USB Descriptor Definitions in the
Specification. The last section of this chapter 3.10 Descriptor Tool will introduce how to use the
description tool provided by TeenyUSB (TeenyDT) to generate descriptors.
Each field of the descriptor will be introduced in detail there.
3.1 Device Descriptor Device Descriptor
The device descriptor describes some basic information of the device, including USB version
number, device type, endpoint 0 maximum package length, device VID/PID, device version
number, manufacturer name, device name, serial number, and number of configuration descriptors.
The Windows system performs driver matching based on the VID and PID of the device. If the
device has multiple interfaces, it will also match the driver based on the interface number to match.
The VID in the official product needs to be applied to the USB organization, and the PID is defined
by the manufacturer.
This article is mainly about to explain the working principle of USB devices. The generated devices
will not be launched as official products, so they are used in the DEMO program.
VID 0x0483 in the ST company's USB library.
3.2 Configuration Descriptor Config Descriptor
A device can have multiple configuration descriptors. The configuration descriptor consists of two
parts. The first part is the configuration descriptor.
44/109
Basic information, including the number of interfaces, configuration number, configuration name,
and power attribute information in the current configuration. back face
Contains multiple function module descriptors. The previous basic information and the following
functional description together constitute the configuration descriptor.
The descriptor content is different for different functions, so the complete configuration descriptor
is a variable-length data structure, and the complete length is determined by the basic information.
in the wTotalLength field of the message. Under normal circumstances, the host first reads the basic
information of the configuration descriptor, and based on the basic information
Calculate the total length and read the complete configuration descriptor.
3.3 Device interface
The basic information of the configuration descriptor is followed by the descriptor related to the
device interface. A configuration can have multiple interfaces. An interface can have multiple
interface descriptors, and the first interface descriptor in the interface represents the current
interface. When there is an IAD descriptor, the type of the IAD descriptor represents the type of the
current interface.
When the device has multiple interfaces, the device descriptor needs to be set to type (0, 0, 0),
which indicates that the type of device is given by interface to determine.
On Windows, the system installs drivers for each device interface of the device. When performing
driver matching:
1. If the device has only one interface, the driver will match according to the device. The device
path is VID_xxxx&PID_yyyy form, where xxxx is the device VID and yyyy is the device PID.
2. If the device has multiple interfaces, the driver will match each interface. At this time, the path of
the device is VID_xxxx&PID_yyyy&MI_zz format, zz is the interface number.
3.4 Interface Association Descriptor
If a device function needs to be described by multiple interfaces, these interfaces need to be
(Interface Association Descriptor) descriptors are combined. But when there is only one function in
a configuration, even if this function requires multiple interface, and does not use IAD descriptors.
Once there is an IAD descriptor in the configuration descriptor, the device's type must be set to type
(0xEF, 0x02, 0x01). The conditions for using the IAD descriptor are as shown in the figure below:
Conditions of use of IAD
Device Interface (IAD not required)
Interface descriptor www.tusb.org
When an IAD descriptor exists, the type of the device interface is determined by the type of the
IAD descriptor.
45/109
3.5 Interface Descriptor
A device interface consists of one or more interface descriptors. The type of the first interface
descriptor in the device interface represents
Indicates the type of the current interface. The interface descriptor contains interface number,
interface type, number of endpoints, and interface name information. If the interface supports
multiple configurations, and different interface configurations are distinguished by the
bAlternateSetting field. According to the interface descriptor
Different types, there will be type-related descriptors after the interface descriptor. Such as HID
descriptor of HID device, CDC device Function Descriptor
.
3.6 Endpoint Descriptor Endpoint Descriptor
In a USB device, an endpoint is the basic unit of communication between the host and the device.
A USB device no matter how many, no matter how complex there are, the endpoints are the ones
that ultimately communicate with the host. Configuring the device and setting the interface request
end target are both to determine the endpoint configuration used by the current device.
On the USB bus, the source of a piece of data can be uniquely determined through the device
address and endpoint address. The device address is assigned by the host when it is connected, and
the endpoint address within the device is determined by the device itself during design. It's kind of
like an IP network IP address and port number in , the IP address is assigned by the service provider,
and the port number is determined by the service content. By IP address and port slogan, you can
get the services you need.
The endpoint number consists of 8 bits of data.
The highest bit bit7 indicates the direction of the data. When the highest bit is 1, it indicates that this
is a IN endpoint; when the highest bit is 0, it means this is an OUT endpoint. When discussing data
direction in USB, we always talk about the host described perspective. IN represents the data sent
from the device to the host, and OUT represents the data sent from the host to the device.
USB devices report endpoint information through endpoint descriptors.
This information includes:
1.Endpoint number and data transmission direction
2.Transmission type
3.Transmission frequency
4.Maximum package length
3.6.1 High speed high bandwidth endpoint
In HS devices, synchronization endpoints and interrupt endpoints can be set as high-speed highbandwidth endpoints, so that data can be transmitted in one frame.
To deliver multi-packet data, Bit11 and Bit12 in the wMaxPacketSize field of the endpoint
descriptor indicate the number of packets transmitted in one frame.
0 indicates an ordinary endpoint, which only transmits one packet;
1 means 2 packets in one frame,
2 means 3 packets in one frame.
46/109
See Reference Document 1, Section 5.9 for more information.
When a frame is transmitted only once, the maximum packet length can be any value less than or
equal to 1024; when a frame is transmitted twice, the maximum packet length is 513 to 1024 bytes;
when a frame is transmitted three times, the maximum packet length is 683 to 1024 bytes.
Why don't batch endpoints need to be set to high-bandwidth mode?
Because the batch endpoint itself will occupy all the remaining bandwidth. Periodic endpoints
(Periodic Endpoints) such as interrupt endpoints and synchronization endpoints will only initiate
one call in a frame.
Transmissions, through high-bandwidth related descriptors, can break this limit and increase the
number of data transmissions in a frame.
3.6.2 Periodic Endpoint
Periodic endpoints in USB refer to endpoints whose type is interrupt transmission or synchronous
transmission. Such endpoints have the fastest scheduling cycle.
Period is transmitted once per frame. USB communication is carried out frame by frame, and
multiple types of transmission can be carried out in one frame.
For FS devices, one frame has a cycle of 1ms; for high-speed devices, one frame has a cycle of
125us. interrupt transfer and the maximum transmission frequency of synchronous transmission is
once per frame.
For FS devices, the transmission frequency bInterval field indicates the transmission frequency, the
unit is 1ms, when bInterval=4 it indicates transmission every 4ms.
For HS equipment, it indicates the transmission frequency, the unit is 125us, bInterval=8 means
x125us=1ms, transmit once every 1ms, when bInterval=1, transmit once every 125us.
3.6.3 Non-Periodic Endpoint
Corresponding to the periodic endpoint, it is the non-periodic endpoint (Non-Periodic Endpoint)
Control endpoints and batches endpoints are all aperiodic endpoints. Transmissions from aperiodic
endpoints consume any remaining bandwidth on the bus.
In FS devices,the bInterval field for aperiodic endpoints has no meaning.
In aperiodic endpoints in HS devices, bInterval represents the NAK frequency in a frame. When
bInterval is 0, means never NAK. In fact, this bInterval is of no use in aperiodic endpoints. Just
imagine if a high-speed device's BULK OUT endpoint has bInterval set to 0, the specification
states that this endpoint never NAKs.
If the device really cannot continue to receive data at this time, it will still NAK the host's OUT
transaction. The author's understanding
Yes, the NAK frequency here is more like a promise. There is a PING mechanism in high-speed
devices. When the host sees this promise
Finally, it is considered that the device will ACK subsequent packets after NAK a certain number of
times. This way the host receives a certain number of NAKs
Afterwards, no more PING packet will be sent, but an OUT transaction packet will be directly
initiated. For example: when the host sees that the device is committed to one frame at most
47/109
NAK 10 times, when the host PINGs 10 times and is NAKed, the host will directly send an OUT
packet on the 11th time. In fact
In use, can the device really guarantee success in the 11th time? If not, the bandwidth of the host's
OUT transaction phase
It will still be wasted, so it is more cost-effective to continue sending PING packets. Therefore, I
think this is aimed at aperiodic endpoints of high-speed equipment.
The design of bInterval is of little use, so it is more cost-effective to always use PING packet
detection.
OUT transaction package and IN transaction package As shown in the figure above, in the OUT
transaction, the host will first send the OUT token and data, and then wait for the device to respond.
If the device does not If the device is ready, the data phase will waste bandwidth, so the PING
mechanism is introduced to detect whether the device is ready. Just start the OUT transaction and
send data. The IN transaction token package is followed by the device's data or the device's
response. If the device does not When ready, ACK can be returned during the data phase without
wasting bandwidth. Therefore, the PING mechanism is only used in non-periodic of OUT is
transmitting.
3.7 Device Qualifier Descriptor
Device modification descriptors are used to obtain device modification descriptors when a device
can operate at different speeds.
For example, there is a high-speed device and a full-speed device. Their VID, PID and device
version numbers are the same. First connect
If you enter a high-speed device, the system will "remember" that its speed is high. After
unplugging it, connect it to a full-speed device, because it
Their identifiers are the same, the system will think that this is the same device working at different
speeds, and will request a device modification description.
symbol. Although two devices with different speeds are used as an example here, from the
perspective of the host side, this works with one device.
It's the same at both rates. Conversely, if you connect to full speed first and then high speed, the
same will happen.
There is no separate device modification descriptor in TeenyUSB, but it is automatically generated
through device descriptors.
Device descriptor and device modification descriptor
3.8 Other Speed Configuration Descriptor
Other speed configuration descriptors are used in conjunction with device modification descriptors
and are used when working at other speeds, in addition to describing
Descriptor type, the meaning format of other fields is completely consistent with the configuration
descriptor. TeenyUSB has no separate additional speed configurations
descriptor, directly based on other speed configuration descriptors generated by the device
descriptor.
48/109
In TeenyUSB, other speed configuration functions are enabled through the
SUPPORT_OTHER_SPEED macro. After enabling, a
The block descriptor cache is used to temporarily store other speed configuration descriptors
generated based on the configuration descriptor.
3.9 Character descriptor String Descriptor
Characters in USB are in Unicode format, and one character occupies 2 bytes. For ASCII
characters, the lower 8 bits are
ASCII value, the upper 8 bits are 0. The character descriptor does not end with 0, but the character
is described in the first byte of the character descriptor.
The total length of characters. Device, configuration, and interface descriptors all contain a field
describing the name. This field is an index.
value. 0 means no character, other values represent character indices. The character descriptor with
index 0 represents the language ID of the character.
(language ID) list, a device can support multiple language IDs. When reading a character descriptor,
the requested wIndex
The field represents the language ID, and the corresponding character descriptor is returned based
on the language ID. TeenyUSB does not support multiple languages, so in
wIndex is not processed when obtaining characters.
3.9.1 Special character descriptors in WCID devices
In Windows systems, there is a USB device called WCID. The driver of this type of device is not
based on VID/PID.
Matching is done based on Compatible ID. The advantage of this is that as long as the WCID is the
same
Devices, even if they are produced by different manufacturers, can use the same driver. In order to
support WCID, the device needs to respond to
A special character descriptor is requested, and a special character descriptor is returned. Windows
system will use this word
Parameters in the descriptor, initiate a manufacturer-defined request to obtain the WCID of the
device. After obtaining the WCID, according to the WCID
Perform driver matching and installation. For a detailed introduction to WCID devices, see section
6.2.8 WCID Devices.
3.10 Descriptor Tool TeenyDT
Several standard descriptors were introduced earlier in this chapter. This section will introduce the
specific meaning of each field in the standard descriptor, and also introduce
Introduce how to use the descriptor tool TeenyDT to generate descriptors. The descriptor tool
includes generating descriptors, generating endpoint initialization
It has three functions: code and driver generation. This section only introduces the function of
generating descriptors. TeenyDT can be used in two ways
To generate descriptors, one is the command line method and the other is the graphical interface
(GUI) method. Command line mode through a lua
49/109
format input file to define the descriptor, and TeenyDT generates a C language version of the
descriptor based on the input file. Automatic generated
The descriptor is in C language format and can be used in TeenyUSB or other USB protocol stacks.
3.10.1 TeenyDT command line mode
The input file in TeenyDT command line mode is written in lua format. The command line mode
requires the executable of lua5.3
program to run the script. Put the directory where the lua executable program is located into the
system environment variable, and then enter the TeenyDT directory
Record and execute the lua gen_descriptor.lua command, the following results will appear:
TeenyDT command line information
The above will prompt that there must be an input file, as well as the optional number of endpoints
and memory size. The memory size here
Refers to the dedicated memory size of the USB FS module. The OTG module does not need to set
the USB dedicated memory size, it will be automatically generated
1280 bytes of OTG_FS module initialization code, and 4096 bytes of OTG_HS module
initialization code. for no
Different chip models have different endpoint numbers and memory sizes. The default number of
endpoints is 7, and the default memory size is 1024. About terminal
The role of the number of points and memory size will be explained in detail later when USB
devices are introduced.
3.10.2 Device descriptor
The meaning of each field of the device descriptor is shown in the following table:
Field
meaning
How TeenyDT handles it
bLength
Descriptor length
Automatic generated
bDescriptorType
Descriptor type, fixed value
Automatic generated
bcdUSB
USB version
Default is 0x200, 2.0
bDeviceClass
Equipment type
Default is 0, specified by the interface
bDeviceSubClass
Device subtype
Default is 0, specified by the interface
bDevicePrototol
Device protocol
Default is 0, specified by the interface
bMaxPacketSize
Endpoint 0 maximum packet length
Default is 64
idVendor
Manufacturer ID
Default is 0x0483
idProduct
Device ID
Default is 0x1234
bcdDevice
Device version
Default is 0x0100, 1.0
iManufacture
Manufacturer name index
Default is 0, no manufacturer name
iProduct
Device name index
Default is 0, no device name
iSerial
serial number index
Default is 0, no serial number
bNumOfConfiguration
Configuration quantity
Automatically generated based on configuration quantity
50/109
Table 7 Device Descriptor
In the above table, TeenyDT has predefined default values for most of the descriptors. Some
descriptors can be modified according to the above.
The values calculated below do not need to be specified, so a lot of content can be omitted when
writing the TeenyDT input file. The following figure is a comparison of the generated C language
device descriptor and TeenyDT's device descriptor:
version of the device descriptor, on the left is the device descriptor in TeenyDT format.
The TeenyDT version descriptor in the example above does not specify content for bcdUSB and
bcdDevice. Default bcdUSB is 0x200, bcdDevice is 0x100, bcdUSB and bcdDevice in the C code
generated on the right adopt
Default values are 0x200 and 0x100 respectively. All fields of the device descriptor in TeenyDT can
be automatically generated or have a default value, so a simplest version of TeenyDT can write
nothing in the device descriptor.
The C language version of iManufacture, iProduct and iSerial fields in the above picture are based
on the ones on the left
strManufacture, strProducr and strSerial are automatically generated. See below for how strings are
processed in TeenyDT.
3.10.3 Character descriptors
The manufacturer's name, product name and serial number are usually added to the device
descriptor.
Character descriptors in TeenyDT are automatically generated based on the required string.
The specific method is that in the TeenyDT version of the descriptor, the character description will
be required the place of talisman changed from iXXX to strXXX, and attach the actual content of
the characters.
As shown in the example above, iManufacture, iProduct and iSerial are replaced by strManufacture,
strProduct and strSerial respectively, and the generated descriptor will automatically generate
corresponding character descriptors and set iManufacture, iProduct, and iSerial to the corresponding
character descriptions
character index. The following is a device descriptor containing a string, and the remaining fields
are default values:
51/109
return Device {
strManufacture = "TeenyUSB",
strProduct
= "TeenyUSB demo",
strSerial
= "123456",
}
The content of the automatically generated character descriptor is as follows, and the character
content in the middle of the character descriptor is omitted:
// Strings
#define STRING_DESCRIPTOR0_STR
"\x09\x04"
#define STRING_DESCRIPTOR0_SIZE
(4)
WEAK __ALIGN_BEGIN const uint8_t StringDescriptor0 [4] __ALIGN_END = {
0x04,
/* bLength */
USB_STRING_DESCRIPTOR_TYPE,
/* bDescriptorType */
0x09, 0x04,
/* wLangID0 */
};
#define STRING_DESCRIPTOR1_STR
"TeenyUSB"
#define STRING_DESCRIPTOR1_SIZE
(18)
WEAK __ALIGN_BEGIN const uint8_t StringDescriptor1 [18] __ALIGN_END = {
0x12, /* bLength */
USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType */
'T', 0x00, /* wcChar0 */
...
'B', 0x00, /* wcChar7 */
};
#define STRING_DESCRIPTOR2_STR "TeenyUSB demo"
#define STRING_DESCRIPTOR2_SIZE
(28)
WEAK __ALIGN_BEGIN const uint8_t StringDescriptor2 [28] __ALIGN_END = {
0x1c, /* bLength */
USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType */
'T', 0x00, /* wcChar0 */
...
'o', 0x00, /* wcChar12 */
};
#define STRING_DESCRIPTOR3_STR
#define STRING_DESCRIPTOR3_SIZE
WEAK __ALIGN_BEGIN const uint8_t
"123456"
(14)
StringDescriptor3 [14] __ALIGN_END = {
0x0e, /* bLength */
USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType */
'1', 0x00, /* wcChar0 */
...
'6', 0x00,
/* wcChar5 */
};
#define STRING_DESCRIPTOR4_STR
#define STRING_DESCRIPTOR4_SIZE
WEAK __ALIGN_BEGIN const uint8_t
52/109
"TeenyUSB demo interface"
(48)
StringDescriptor4 [48] __ALIGN_END = {
0x30, /* bLength */
USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType */
'T', 0x00, /* wcChar0 */
...
'e', 0x00,
/* wcChar22 */
};
const uint8_t* const StringDescriptors[STRING_COUNT] = {
StringDescriptor0,
StringDescriptor1,
StringDescriptor2,
StringDescriptor3,
StringDescriptor4,
};
The descriptor is a list of language IDs (Language IDs), indicating the language used by this device
character descriptor. Via Setup wIndex field to select character descriptors for different language
IDs. TeenyUSB only supports one language, so generated here the LangID has only one language
ID. Different index character descriptors are automatically generated for different character contents
later. These character descriptors are combined in order into an array called StringDescriptors.
3.10.3.1 Chinese character descriptor
Chinese character descriptors are the same as English, because USB character descriptors use
Unicode encoding format, only make sure that the encoding format of the input file is Unicode to
generate correct character descriptors containing Chinese characters.
The following figure is an example of a character descriptor containing Chinese characters. You
need to pay attention to the encoding format of the file on the left:
Chinese character descriptor
3.10.4 Configuration descriptor
The meaning of each field in the basic content of the configuration descriptor is as follows:
Field
meaning
How TeenyDT handles it
bLength
Descriptor length
Automatic generated
bDescriptorType
Descriptor type, fixed value
Automatic generated
wTotalLength
Configuration descriptor total length Automatically generated based on interface content
bNumInterface
Number of interfaces
Automatically generated according to the actual situation of the
interface
bConfigurationValue Current configuration value
Automatically generated based on current configuration location
iConfiguration
Configuration name index
Default is 0
bmAttributes
bit7 is always 1
Default is 0x80, bus powered, cannot be woken up remotely
53/109
bit6
1-self-powered; 0-bus powered
bit5
1-remote wake-up; 0-cannot remotely wake up
bit0-4 is always 0
bMaxPower
power status
Maximum current consumption, used when powered by bus
Default is 100, gets maximum 200mA current from bus
Table 8 Basic content of configuration descriptor
In order to facilitate writing the bmAttributes field of the configuration descriptor, TeenyDT defines
a Two auxiliary fields, SelfPower and RemoteWakeup. If you want to support self-power supply, set
SelfPower to true, if you want to support remote wakeup, set Remotewakeup to true. One bus
powered, maximum current 100mA, the configuration descriptor that supports remote wake-up is
described in TeenyDT as follows:
Config {
SelfPower = false,
RemoteWakeup = true,
bMaxPower = 50,
}
The generated C language content is as follows:
// Configs
#define CONFIG_DESCRIPTOR_SIZE (9)
__ALIGN_BEGIN const uint8_t ConfigDescriptor [9] __ALIGN_END = {
0x09, /* bLength */
USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType */
0x09, 0x00, /* wTotalLength */
0x00, /* bNumInterfaces */
0x01, /* bConfigurationValue */
0x00, /* iConfiguration */
0xa0, /* bmAttributes */
0x32, /* bMaxPower */
};
3.10.5 Interface descriptor
The interface descriptor follows the basic part of the configuration descriptor, and its content is
defined as follows:
Field
meaning
How TeenyDT handles it
bLength
Descriptor length
Automatic generated
bDescriptorType
Descriptor type, fixed value
Automatic generated
bInterfaceNumber
Interface ID, starting from 0
Automatically generated based on the number of interfaces
bAlternateSetting
Current interface value
Default is 0
bNumEndpoints
Number of interface endpoints
Automatically generated based on actual endpoint configuration
bInterfaceClass
Interface Type
Default is 0xFF, user-defined type
54/109
bInterfaceSubClass
Interface subtype
Default is 0xFF, user-defined subtype
bInterfaceProtocol
Interface Protocol
Default is 0
iInterface
Interface name index
Default is 0, no name
Table 9 Interface descriptor
All fields of the interface descriptor in TeenyDT have default values or are automatically generated,
a custom interface type
You don’t have to write anything. Structurally, interface descriptors belong to configuration
descriptors, and one configuration descriptor can contain multiple
interface descriptor. After the interface descriptor is a type-related descriptor, usually a function
descriptor (Function Descriptor).
Then there is the endpoint descriptor, which is used to describe the communication endpoints that
the current interface needs to use.
The type, subtype, and protocol fields of the interface define the functions of the interface. The
USB organization predefines the interfaces of some devices.
Mouth type. The different types of interface descriptors are explained in detail when specific device
types are introduced.
3.10.6 Endpoint descriptor
The endpoint descriptor is at the end of the interface descriptor, and its number is specified in the
interface descriptor. The content is as follows:
Table 10 endpoint descriptor
Field
meaning
How TeenyDT handles it
bLength
Descriptor length
Automatic generated
bDescriptorType
Descriptor type, fixed value
Automatic generated
bEndpointAddres endpoint address
s
Generated based on user configuration
bit0-3 endpoint number
bit4-6 reserved, always 0
bit7 endpoint direction, 1-IN 0-OUT endpoint type
bmAttributes
Generated based on user configuration
it0-10 maximum packet length
it11-12 Definition of the number of transmission times in one frame
it13-15 reserved, always 0
wMaxPacketSize Maximum package length
The default is 64, transmitted once per
frame
bInterval
Default is 1, fastest
Reporting frequency
There are two ways to define interface descriptors in TeenyDT. One is to use the form of "field =
value", which is the same as the interface descriptor. The descriptor is the same as the configuration
descriptor; there is also a form of initialization parameters, where the configuration parameters are
55/109
passed like function parameters. Gives the endpoint descriptor generator. The endpoint definition in
parameter mode looks relatively concise, as shown in the following figure:
Two different styles of endpoint definition
3.10.7 Interface Association Descriptor IAD
When an interface requires multiple interface descriptors, interface union descriptors need to be
used to combine multiple interfaces.
IAD descriptor Field
meaning
How TeenyDT handles it
bLength
Descriptor length
Automatic generated
bDescriptorType
Descriptor type, fixed value
bFirstInterface
first interface ID
Automatically generated based on the first interface in
IAD
bInterfaceCount
Number of interfaces
Automatically generated based on the number of
interfaces in IAD
bFunctionClass
Function type
Automatically generated based on the first interface
bFunctionSubClass
Functional subtype
Automatically generated based on the first interface
bFunctionProtocol
functional protocol
Automatically generated based on the first interface
iFunction
Function name index
Default is 0, no name
Table 11
TeenyDT will generate an IAD descriptor based on the actual situation of the device interface. If the
device has only one interface, this
Even if the interface requires multiple interface descriptors, no IAD descriptors are generated. If the
device has multiple interfaces and at least
An interface requires an IAD descriptor, then TeenyDT will set the device type to (0xEF,0x02,0x01)
and generate
IAD descriptor.
56/109
3.10.8 Function descriptor
Some special interface types need to specify function descriptors to describe some special functions
of the interface. Such as CDC
A serial port requires functional descriptors to describe the operations it can handle.
Table 12
function descriptor Field
meaning
How TeenyDT handles it
bLength
Descriptor length
Automatic generated
bDescriptorType
Descriptor type, fixed value
Automatic generated
bDescriptorSubtype
Descriptor subtype, function related Specify when using
bFuncData0
Functional data
Specify when using
bFuncData1
Functional data
Specify when using
…
…
...
In order to facilitate the use of function descriptors, TeenyDT defines two auxiliary fields, alias and
varData.
Give the function descriptor an alias in the generated code comments.
varData is some key-value pairs (Key-Value) .
When using function-related data, the data will be annotated with key.
A function descriptor is defined as follows:
57/109
STM32 USB FS module
The USB FS module is mainly used on STM32F0, STM32F103, STM32F3 series chips, and on
different chips
There are slight differences in operation depending on the version of the FS module.
Comparison chart of FS modules of F1 and F0 series (from STM32 reference manual) As can be
seen from the above figure, the application software passes through the APB (this article refers to
both APB1 and APB as APB) bus. To communicate with the USB module, registers and packet
buffer memory are accessed through the APB wrapper. ask. When both the APB and the USB
module access the packet cache, the Arbiter determines who can access it. In ST Officer In the
party's firmware library, packet buffer memory is also called Packet Memory Area, that is, PMA.
PMA will be used in the future. to refer to this area.
4.1 USB core initialization operation
The STM32 USB FS module requires some initial configuration before use. This section introduces
this part. USB function
The initialization part of the code is implemented in the TeenyUSB protocol stack stm32f0_init.c
and stm32f1_init.c files. clock
The code for the initialization part is implemented in the system_stm32f0xx.c and
system_stm32f10x.c files.
58/109
4.1.1 Clock settings
In the official HAL library, the clock configuration code is in the SystemClock_Config function,
which is automatically configured by CubeMX.
dynamically generated. TeenyUSB adopts the approach of the old version library, using the
system_init.c file, in the SystemInit function
Configure the clock. The clock configuration in the HAL library has a timeout detection
mechanism, which requires systick to be used together, so HAL
The library needs to configure systick before calling other library functions. TeenyUSB's clock
configuration code also comes from
The code automatically generated by CubeMX removes the timeout waiting part.
4.1.1.1 F1 series chip clock settings
The USB FS module requires a 48MHz clock to work. The following figure is the USB clock
relationship tree in the F1 series chip.
F1 Series USB Clock (from STM32 Reference Manual)
As you can see in the above figure, the USB clock comes from PLLCLK, and PLLCLK can only be
divided by 1 or 1.5.
This requires that PLLCLK can only be 72MHz or 48MHz. To use the USB function on F1 series
chips, you need to turn on
PLL function, set the PLL parameters according to the frequency of the external crystal oscillator,
so that PLLCLK is 72MHz or 48MHz. Then
Turn on the USB clock and the corresponding IO clock. In the SystemInit function, configure the
main clock as PLL, 72MHz, TeenyUSB
59/109
Turn on the USB clock in the stm32f1_init.c file initialization code. The frequency division defaults
to 1.5, so there is no divider configuration here.
frequency coefficient. If the PLL clock is 48MHz, the frequency division coefficient needs to be
configured to 1.
4.1.1.2 F0 series chip clock settings
The following figure is the USB clock relationship tree in the F0 series chip:
F0 series USB clock (from STM32 reference manual) There are two sources of USB clock in the F0
chip, one is PLLCLK and the other is HSI48. If PLLCLK is used, Then the PLL clock can only be
configured to 48MHz. If using HSI48, the CRS of HSI48 needs to be configured to use USB SOF
package to calibrate. The USB clock configuration of the F0 chip in TeenyUSB is as follows:
60/109
#if defined USB_CLOCK_SOURCE_CRS
RCC->CFGR3 &= ~RCC_CFGR3_USBSW;
RCC->APB1ENR |= RCC_APB1ENR_CRSEN;
CRS->CFGR &= ~CRS_CFGR_SYNCSRC;
CRS->CFGR |= RCC_CRS_SYNC_SOURCE_USB;
CRS->CR |= (CRS_CR_AUTOTRIMEN | CRS_CR_CEN);
#else
// otherwise use pll clk
RCC->CFGR3 |= RCC_CFGR3_USBSW_PLLCLK;
#endif
Choose whether to use the PLL clock or the HSI48 clock according to the definition. If you use the
HSI48 clock, turn on the CRS function.
And set the CRS source to USB. When using HSI48, the chip does not need an external crystal
oscillator, which can reduce device costs.
4.1.2 IO settings
USB FS module communication requires two signals, D+ and D-. In F1 and F0 series chips, when
the USB clock is turned on,
The IO corresponding to D+ and D- will be automatically taken over by the USB module. D+ in the
F1 chip does not have an internal pull-up, so it requires
Connect an external 1.5K pull-up resistor to 3.3V. D+ in the F0 chip contains an internal pull-up
resistor and can be passed
register to set. In the F1 chip, TeenyUSB does not add an additional pull-up resistor control pin for
D+, but Simulate disconnection by setting the D+ pin low. code show as below:
// PA_12 output mode: OD = 0
GPIOA->CRH |= GPIO_CRH_CNF12_0;
GPIOA->CRH &= (~GPIO_CRH_CNF12_1);
GPIOA->CRH |= GPIO_CRH_MODE12;// PA_12 set as: Output mode, max speed 50 MHz.
GPIOA->BRR = GPIO_BRR_BR12;
In the F0 chip, TeenyUSB chooses to use the built-in pull-up resistor or IO simulation to achieve
disconnection according to the configuration. Function. code show as below:
#ifdef USB_FS_INTERNAL_PULLUP
// disable interal pull up resistor
GetUSB(dev)->BCDR&=~USB_BCDR_DPPU;
#else
// PA12 = PushPull = 0
GPIOA->OTYPER |= GPIO_OTYPER_OT_12;
GPIOA->MODER &= ~GPIO_MODER_MODER12;
GPIOA->MODER |= GPIO_MODER_MODER12_0;
GPIOA->OSPEEDR |= GPIO_OSPEEDR_OSPEEDR12;
GPIOA->BRR = GPIO_BRR_BR_12;
#endif
61/109
62/109
5 STM32 OTG module device mode
The STM32 OTG module has two versions: FS and HS. HS is divided into built-in phy and external
phy versions. These different versions of the OTG module operate similarly. The OTG module
block diagram is as follows:
OTG FS module block diagram
OTG HS module block diagram
As you can see from the picture above, both OTG FS and OTG HS modules have FIFOs to cache
USB data. And OTG HS module has both the AHB master interface and the AHB slave interface.
This is because the OTG HS module has DMA capabilities and can enable to move data between
USB FIFO and Memory through the PHB bus. What USB OTG actually means is the ability to
63/109
switch the roles of host and device during use. In the STM32 OTG module, when working in device
mode, use a set of registers; when working in host mode, use another set of registers. This chapter
only introduces OTG working in device mode module operation, the USB device mentioned later in
this chapter is the OTG module working in device mode.
5.1 USB core initialization operation
The USB device initialization operations of different chips are implemented in the stm32fxxx_init.c
file, and the interrupt processing of OTG devices implemented in teeny_usb_stm32_otg_device.c
file. This section describes how to initialize OTG working in device mode module. After
initialization is complete, the device is able to receive a reset signal on the USB bus.
5.1.1 Clock settings
The relevant clock of the OTG module is shown in the figure below:
STM32 partial clock tree
There are three USB-related clocks in the picture above, USB & RNG clock, USB OTG HS clock,
USB HS ULPI clock.
USB & RNG clock is the clock for the OTG FS module, coming from PLLQ or PLLSAI.
USB OTG HS clock is the clock for the built-in OTG high-speed phy, which comes from the
dedicated PLL1 for the built-in high-speed phy and PLL2, PLL1 in turn derived from HSE.
Therefore, when using the built-in high-speed phy, you need to choose a suitable HSE bell.
The USB HS ULPI clock is a clock from an external phy. This clock is provided to the chip
internally by the external phy.
The ULPI interface is used. When different phys are used to configure different clocks, the clockrelated configuration code is as follows:
64/109
static void tusb_otg_core_init(tusb_core_t* core){
USB_OTG_GlobalTypeDef* USBx = GetUSB(core);
if(GetUSB(core) == USB_OTG_FS){
#if defined(OTG_FS_EMBEDDED_PHY)
__HAL_RCC_USB_OTG_FS_CLK_ENABLE();
#endif
}else if(GetUSB(core) == USB_OTG_HS){
#if defined(USB_HS_PHYC)
__HAL_RCC_OTGPHYC_CLK_ENABLE();
#endif
__HAL_RCC_USB_OTG_HS_CLK_ENABLE();
__HAL_RCC_USB_OTG_HS_ULPI_CLK_ENABLE();
...
}
...
}
Depending on the module, choose to open different clocks.
5.1.2 IO settings
Different modules use different IO. The code is as follows:
static void tusb_setup_otg_fs_io(void){
/**USB_OTG_FS GPIO Configuration
PA8 ------> USB_OTG_FS_SOF
PA9 ------> USB_OTG_FS_VBUS
PA10 ------> USB_OTG_FS_ID
PA11 ------> USB_OTG_FS_DM
PA12 ------> USB_OTG_FS_DP
*/
__HAL_RCC_GPIOA_CLK_ENABLE();
set_io_af(GPIOA, 11, GPIO_AF10_OTG_FS);
set_io_af(GPIOA, 12, GPIO_AF10_OTG_FS);
}
#if defined(USB_HS_PHYC)
// Setup IO for OTG_HS core with embedded HS phy
static void tusb_setup_otg_hs_io(void){
__HAL_RCC_GPIOB_CLK_ENABLE();
set_io_af(GPIOB, 14, GPIO_AF12_OTG_HS_FS);
set_io_af(GPIOB, 15, GPIO_AF12_OTG_HS_FS);
}
#endif
...
#elif defined(OTG_HS_EXTERNAL_PHY)
OTG_HS_ULPI_IO_CLK_ENABLE();
set_io_af_mode( OTG_HS_ULPI_D0 );
set_io_af_mode( OTG_HS_ULPI_D1 );
set_io_af_mode( OTG_HS_ULPI_D2 );
set_io_af_mode( OTG_HS_ULPI_D3 );
set_io_af_mode( OTG_HS_ULPI_D4 );
set_io_af_mode( OTG_HS_ULPI_D5 );
set_io_af_mode( OTG_HS_ULPI_D6 );
65/109
set_io_af_mode(
set_io_af_mode(
set_io_af_mode(
set_io_af_mode(
set_io_af_mode(
#endif
OTG_HS_ULPI_D7 );
OTG_HS_ULPI_DIR );
OTG_HS_ULPI_STP );
OTG_HS_ULPI_NXT );
OTG_HS_ULPI_CK );
For the initialization of the above ULPI part IO, ULPI will use different IO on different boards. It
needs to be adjusted according to the board.
Actual situations define these macros.
5.1.3 Interrupt settings
The interrupt setting is relatively simple. Just open the corresponding interrupt according to
different modules. The code is as follows:
static void tusb_otg_core_init(tusb_core_t* core){
USB_OTG_GlobalTypeDef* USBx = GetUSB(core);
if(GetUSB(core) == USB_OTG_FS){
...
NVIC_SetPriority(OTG_FS_IRQn, 0);
NVIC_EnableIRQ(OTG_FS_IRQn);
...
}else if(GetUSB(core) == USB_OTG_HS){
...
NVIC_SetPriority(OTG_HS_IRQn, 0);
NVIC_EnableIRQ(OTG_HS_IRQn);
...
}
}
The endpoint 1 dedicated interrupt of the high-speed OTG module is not used in TeenyUSB, and no
corresponding initialization is done here.
5.1.4 USB module settings
The configuration of the STM32 OTG module includes two parts.
One part is OTG Core related settings, regardless of both device mode and host mode need to be
configured, and the other part is configured according to the actual working conditions of the OTG
module.
In TeenyUSB, the initialization work of the OTG module core part is completed in the
tusb_otg_core_init function, and the device part initialization work is completed in the
tusb_init_otg_device function, the code is as follows:
void tusb_open_device(tusb_device_t* dev){
USB_OTG_GlobalTypeDef* USBx = GetUSB(dev);
tusb_otg_core_init((tusb_core_t*) dev);
USBx->GUSBCFG &= ~(USB_OTG_GUSBCFG_FHMOD | USB_OTG_GUSBCFG_FDMOD);
USBx->GUSBCFG |= USB_OTG_GUSBCFG_FDMOD;
tusb_init_otg_device(dev);
}
66/109
5.1.5 Summary of core initialization operations
After completing the core initialization of the device mode, when the USB interface of the device is
connected to the host, the OTG mode can be triggered.
Block interruption. All subsequent operations are handled in the interrupt function of the OTG
module.
5.2 USB endpoint register operation
In the STM32 OTG module, the USB IN endpoint and OUT endpoint use different register groups,
and the HAL library
The OTG device endpoint register is defined as follows:
/**
* @brief USB_OTG_IN_Endpoint-Specific_Register
*/
typedef struct{
__IO uint32_t DIEPCTL; /*!< dev IN Endpoint Control Reg 900h + (ep_num * 20h) + 00h */
uint32_t Reserved04; /*!< Reserved 900h + (ep_num * 20h) + 04h */
__IO uint32_t DIEPINT; /*!< dev IN Endpoint Itr Reg 900h + (ep_num * 20h) + 08h */
uint32_t Reserved0C; /*!< Reserved 900h + (ep_num * 20h) + 0Ch */
__IO uint32_t DIEPTSIZ; /*!< IN Endpoint Txfer Size 900h + (ep_num * 20h) + 10h */
__IO uint32_t DIEPDMA; /*!< IN Endpoint DMA Address Reg 900h + (ep_num * 20h) + 14h */
__IO uint32_t DTXFSTS; /*!< IN Endpoint Tx FIFO Status Reg 900h + (ep_num * 20h) + 18h */
uint32_t Reserved18; /*!< Reserved
900h+(ep_num*20h)+1Ch-900h+ (ep_num * 20h) + 1Ch */
} USB_OTG_INEndpointTypeDef;
/**
* @brief USB_OTG_OUT_Endpoint-Specific_Registers
*/
typedef struct{
__IO uint32_t DOEPCTL; /*!< dev OUT Endpoint Control Reg B00h + (ep_num * 20h) + 00h */
uint32_t Reserved04; /*!< Reserved B00h + (ep_num * 20h) + 04h */
__IO uint32_t DOEPINT; /*!< dev OUT Endpoint Itr Reg B00h + (ep_num * 20h) + 08h */
uint32_t Reserved0C; /*!< Reserved
B00h + (ep_num * 20h) + 0Ch */
__IO uint32_t DOEPTSIZ; /*!< dev OUT Endpoint Txfer Size B00h + (ep_num * 20h) + 10h */
__IO uint32_t DOEPDMA; B00h + (ep_num * 20h) + 14h */
/*!< dev OUT Endpoint DMA Address
uint32_t Reserved18[2]; /*!< Reserved B00h + (ep_num * 20h) + 18h - B00h + (ep_num * 20h) +
1Ch */
} USB_OTG_OUTEndpointTypeDef;
The corresponding endpoint register address can be obtained through the USBx_INEP and
USBx_OUTEP macros in the HAL library. initial
When configuring the endpoint, configure the endpoint type, maximum packet length, FIFO
number and other information through the DxEPCTL register. Use endpoint to pass
67/109
When transmitting data, configure the size of the data to be sent, the number of packets and other
information through the DxEPTSIZ register, and then configure
DxEPCTL starts data transfer. Endpoint initialization and data sending will be introduced in detail
below.
5.3 FIFO operation
In the OTG module of STM32, endpoint data is exchanged between the application and the OTG
module through FIFO.
FIFO access in device mode is shown in the figure below:
OTG FIFO block diagram (from STM32 specification book)
As can be seen in the above figure, each IN (TX) endpoint has an independent FIFO, and all OUT
(RX) endpoints share a
Use a FIFO. Therefore, when initializing the IN endpoint, you need to configure the FIFO number
for the IN endpoint.
5.3.1 FIFO initialization
The size and starting address of the FIFO need to be configured before use. For the RX FIFO, the
starting address is fixed at 0. TeenyUSB
68/109
The FIFO configuration code is as follows:
#define SET_TX_FIFO(dev, bEpNum, addr, size) \
do{\
if(bEpNum == 0){\
GetUSB(dev)->DIEPTXF0_HNPTXFSIZ = (uint32_t)(((uint32_t)( (size)/4) << 16) | ( (addr)/4));\
}else{\
/*avoid the compiler warning */\
GetUSB(dev)->DIEPTXF[bEpNum==0?0: bEpNum- 1] = \
(uint32_t)(((uint32_t)( (size)/4) << 16) |( (addr)/4));\
}\
}while(0)
#define SET_RX_FIFO(dev, addr, size) \
do{\
GetUSB(dev)->GRXFSIZ = (size/4);\
}while(0)
The TX FIFO is in a fixed location, and the TX FIFO registers of other endpoints are in the
DIEPTXF array.
middle. The address of the RX FIFO is fixed, so only the size is configured. The size unit in the
FIFO register is 4 bytes. So divide size by 4 before writing it to the register.
TeenyDT can generate the FIFO of the OTG module based on the contents of the descriptor
The address and size are as follows:
// Endpoint define for OTG core
#define BULK_OTG_MAX_OUT_SIZE (64)
#define BULK_OTG_CONTROL_EP_NUM (1)
#define BULK_OTG_OUT_EP_NUM (1)
// RX FIFO size / 4 > (CONTROL_EP_NUM * 5 + 8) +
// MAX_OUT_SIZE / 4 + 1) + (OUT_EP_NUM*2) + 1 = 33
#define BULK_OTG_RX_FIFO_SIZE (256)
#define BULK_OTG_RX_FIFO_ADDR (0)
// Sum of IN ep max packet size is 128
// Remain Fifo size is 768 in bytes, Rx Used 256 bytes
#define BULK_EP0_TX_FIFO_ADDR (256)
#define BULK_EP0_TX_FIFO_SIZE (BULK_EP0_TX_SIZE * 6)
#define BULK_EP1_TX_FIFO_ADDR (640)
#define BULK_EP1_TX_FIFO_SIZE (BULK_EP1_TX_SIZE * 6)
The address and size of the FIFO are automatically generated based on the contents of the
descriptor, and these values are passed through SET_TX_FIFO and SET_RX_FIFO macro
configured into the FIFO register to complete the FIFO configuration.
In order to avoid the uncertainty of the status of the data in the FIFO, the FIFO is flushed before
using it. Code as follows:
void flush_rx(USB_OTG_GlobalTypeDef *USBx){
USBx->GRSTCTL = USB_OTG_GRSTCTL_RXFFLSH;
while ((USBx->GRSTCTL & USB_OTG_GRSTCTL_RXFFLSH) == USB_OTG_GRSTCTL_RXFFLSH);
}
void flush_tx(USB_OTG_GlobalTypeDef *USBx, uint32_t num){
USBx->GRSTCTL = ( USB_OTG_GRSTCTL_TXFFLSH |(uint32_t)( num << 6));
while ((USBx->GRSTCTL & USB_OTG_GRSTCTL_TXFFLSH) == USB_OTG_GRSTCTL_TXFFLSH);
}
69/109
When the TX FIFO number is 0x10, all TX FIFOs will be flushed
5.3.2 Reading FIFO data
All OUT (RX) endpoints share a FIFO. When there is data in the RX FIFO, the RXFLVL interrupt
will be triggered.
The data in the FIFO is processed in this interrupt. code show as below:
void tusb_otg_device_handler(tusb_device_t* dev){
...
if(INTR() & USB_OTG_GINTSTS_RXFLVL){
USB_MASK_INTERRUPT(USBx, USB_OTG_GINTSTS_RXFLVL);
{
uint32_t sts = USBx->GRXSTSP;
uint8_t EPn = sts & USB_OTG_GRXSTSP_EPNUM;
uint32_t len = (sts & USB_OTG_GRXSTSP_BCNT) >> 4;
if(((sts & USB_OTG_GRXSTSP_PKTSTS) >> 17) ==
STS_DATA_UPDT){
tusb_ep_data* ep = &dev->Ep[EPn];
if(ep->rx_count<ep->rx_size && ep->rx_buf){
// copy data packet
tusb_otg_read_data(USBx, ep->rx_buf + ep->rx_count, len);
ep->rx_count += len;
}else{
// drop the data because no memory to handle them
tusb_otg_read_data(USBx,0, len);
}
}else if(((sts & USB_OTG_GRXSTSP_PKTSTS) >> 17) ==
STS_SETUP_UPDT){
// copy setup packet
tusb_otg_read_data(USBx, &dev->setup, len);
}
}
USB_UNMASK_INTERRUPT(USBx, USB_OTG_GINTSTS_RXFLVL);
}
...
}
After detecting the RX FIFO interrupt, first obtain the data type in the FIFO through the GRXSTSP
register, such as data endpoint
number, whether it is a SETUP package. Then read the data in the FIFO through
tusb_otg_read_data. When reading FIFO
When the data in the endpoint is entered, the contents of the register corresponding to the endpoint
will also change accordingly. If the data in the FIFO meets the completion conditions, also
The data transfer completion interrupt will be triggered. For example, the data read is less than the
maximum packet length, or the data has reached the total transmission length.
70/109
For detailed description of transfer completion conditions, see section 2.4 Transfer Completion
Conditions.
5.3.3 Writing FIFO data
The IN (TX) endpoints each have their own TX FIFO, the FIFO is configured through the
TXFNUM field in the DIEPCTL register
Number. TeenyUSB sets the FIFO number and endpoint number to be the same to facilitate
subsequent operations. When there is no data in the TX FIFO
, the endpoint's TXFE interrupt is triggered. In the TXFE interrupt, the data to be sent is written into
the FIFO corresponding to the endpoint.
code show as below:
void tusb_otg_device_handler(tusb_device_t* dev){
...
if( ((epint & USB_OTG_DIEPINT_TXFE) == USB_OTG_DIEPINT_TXFE) &&
(USBx_DEVICE->DIEPEMPMSK &
(1 << ep)) ){
tusb_fifo_empty(dev, ep);
}
...
}
// when fifo empty call this function
void tusb_fifo_empty(tusb_device_t* dev, uint8_t EPn){
tusb_ep_data* ep = &dev->Ep[EPn];
PCD_TypeDef* USBx = GetUSB(dev);
USB_OTG_INEndpointTypeDef* epin = USBx_INEP(EPn);
uint32_t xfer_size = epin->DIEPTSIZ & USB_OTG_DIEPTSIZ_XFRSIZ;
uint32_t maxpacket = GetInMaxPacket(dev, EPn);
uint32_t fifo_len = epin->DTXFSTS & USB_OTG_DTXFSTS_INEPTFSAV;
const uint8_t* src = (const uint8_t*)ep->tx_buf;
uint32_t len32b;
// round transfer size to max packet boundary
if(xfer_size > fifo_len*4){
xfer_size = (fifo_len*4 / maxpacket) * maxpacket;
}
len32b = (xfer_size+3) /4;
// push data to fifo
while(len32b){
USBx_DFIFO(EPn) = *((__packed uint32_t *)src);
src+=4;
len32b--;
}
// adjust the dat buffer
ep->tx_buf = src;
//calculate last packet size
ep->tx_last_size = xfer_size ? (xfer_size-1)%maxpacket+1 : 0;
//if( xfer_size == 0){
71/109
if( (epin->DIEPTSIZ & USB_OTG_DIEPTSIZ_XFRSIZ) == 0 ){
// this xfer complete, so no need the fifo empty interrupt
USBx_DEVICE->DIEPEMPMSK &= ~(1ul<<EPn);
}
return;
}
When a TXFE interrupt is detected, the tusb_fifo_empty function is called for processing. The data
that the endpoint needs to send
length and FIFO free space to calculate the length of data that can currently be transferred to the
FIFO. Then write this data to
in TX FIFO. When writing TX FIFO data, the OTG module subtracts the value in the DIEPTSIZ
register of the corresponding endpoint.
If DIEPTSIZ is 0, it means that there is no data to be sent, and the TXFE interrupt is masked. When
the data in the FIFO passes through the USB
87www.tusb.org
When the transfer is completed, the transfer complete interrupt of the IN endpoint is triggered.
5.3.4 FIFO initialization of OTG_FS and OTG_HS
There are two types of STM32 OTG modules: FS and HS. The HS module supports the DMA
function. When DMA is enabled,
There are some requirements for FIFO setup. In order to cope with the FIFO settings of these two
different devices, TeenyUSB will be FS respectively.
and HS modules generate different FIFO initialization code.
5.3.5 FIFO summary
Through FIFO, the OTG module masks out some details of the endpoint. For the OUT endpoint, the
program only needs to read the RX
The data in the FIFO is written to the corresponding endpoint cache. For IN endpoints, the program
only needs to interrupt in the TX FIFO empty,
Write data from the endpoint cache to the FIFO.
5.4 Endpoint initialization
The endpoint needs to be configured with type, maximum packet length, FIFO number, etc. before
use. The OTG module in TeenyUSB
The endpoint initialization code is as follows:
#define init_ep_tx(dev, EPn, type, mps) \
do{ \
PCD_TypeDef *USBx = GetUSB(dev); \
uint32_t maxpacket = mps; \
USB_OTG_INEndpointTypeDef* INEp = USBx_INEP(EPn); \
if(USBx == USB_OTG_FS && EPn == 0){ \
maxpacket = __CLZ(maxpacket) - 25;
\
}
\
USBx_DEVICE->DAINTMSK |=
( (USB_OTG_DAINTMSK_IEPM) & ( (1 << (EPn))) ); \
72/109
INEp->DIEPCTL |= ((maxpacket & USB_OTG_DIEPCTL_MPSIZ ) | (type << 18 ) | \
((EPn) << 22 ) | (USB_OTG_DIEPCTL_SD0PID_SEVNFRM) |
(USB_OTG_DIEPCTL_USBAEP));
\
}while(0)
#define init_ep_rx(dev, EPn, type, mps) \
do{ \
PCD_TypeDef *USBx = GetUSB(dev); \
uint32_t maxpacket = mps; \
if(EPn == 0){ \
maxpacket = __CLZ(maxpacket) - 25;
\
}
\
USBx_DEVICE->DAINTMSK |=
( (USB_OTG_DAINTMSK_OEPM) & ( (1 << (EPn))<<16 ) );
USBx_OUTEP(EPn)->DOEPCTL |= ((maxpacket & USB_OTG_DOEPCTL_MPSIZ ) | (type <<
18 ) |
(USB_OTG_DOEPCTL_SD0PID_SEVNFRM) | (USB_OTG_DIEPCTL_USBAEP));
}while(0)
For endpoint 0, the maximum packet length is initialized differently, everything else is the same.
When initializing the endpoint, at the same time
The interrupt mask bit of the corresponding endpoint is also turned on. TeenyDT will generate
endpoint initialization code based on the descriptor content, OTG
The endpoint initialization code of the module is as follows:
// PMA buffer reserved for buffer description table
#define BULK_USB_BUF_START
(BULK_EP_BUF_DESC_TABLE_SIZE *
BULK_EP_NUM)
// EndPoints 0 defines
#define BULK_EP0_RX_SIZE (64)
#define BULK_EP0_RX_ADDR (BULK_USB_BUF_START + 0)
#define BULK_EP0_TX_SIZE (64)
#define BULK_EP0_TX_ADDR (BULK_USB_BUF_START + 64)
#define BULK_EP0_TYPE USB_EP_CONTROL
// EndPoints 1 defines
#define BULK_EP1_TX_SIZE (64)
#define BULK_EP1_TX0_ADDR (BULK_USB_BUF_START + 128)
#define BULK_EP1_TX1_ADDR (BULK_USB_BUF_START + 192)
#define BULK_EP1_TYPE USB_EP_BULK
// EndPoints 2 defines
#define BULK_EP2_RX_SIZE (64)
#define BULK_EP2_RX0_ADDR (BULK_USB_BUF_START + 256)
#define BULK_EP2_RX1_ADDR (BULK_USB_BUF_START + 320)
73/109
#define BULK_EP2_TYPE USB_EP_BULK
// Endpoint define for OTG core
#define BULK_OTG_MAX_OUT_SIZE (64)
#define BULK_OTG_CONTROL_EP_NUM (1)
#define BULK_OTG_OUT_EP_NUM (1)
// RX FIFO size / 4 > (CONTROL_EP_NUM * 5 + 8) +
(MAX_OUT_SIZE / 4 + 1) + (OUT_EP_NUM*2) + 1 = 33
#define BULK_OTG_RX_FIFO_SIZE (256)
#define BULK_OTG_RX_FIFO_ADDR (0)
// Sum of IN ep max packet size is 128
// Remain Fifo size is 768 in bytes, Rx Used 256 bytes
#define BULK_EP0_TX_FIFO_ADDR (256)
#define BULK_EP0_TX_FIFO_SIZE (BULK_EP0_TX_SIZE * 6)
#define BULK_EP1_TX_FIFO_ADDR (640)
#define BULK_EP1_TX_FIFO_SIZE (BULK_EP1_TX_SIZE * 6)
// EndPoints init function for USB OTG core
#define BULK_TUSB_INIT_EP_OTG(dev) \
do{\
SET_RX_FIFO(dev, BULK_OTG_RX_FIFO_ADDR, BULK_OTG_RX_FIFO_SIZE);
\
/* Init ep0 */ \
INIT_EP_Tx(dev, PCD_ENDP0, BULK_EP0_TYPE, BULK_EP0_TX_SIZE);
\
SET_TX_FIFO(dev, PCD_ENDP0, BULK_EP0_TX_FIFO_ADDR,
BULK_EP0_TX_FIFO_SIZE);
INIT_EP_Rx(dev, PCD_ENDP0, BULK_EP0_TYPE, BULK_EP0_RX_SIZE);
\
\
/* Init ep1 */ \
INIT_EP_Tx(dev, PCD_ENDP1, BULK_EP1_TYPE, BULK_EP1_TX_SIZE);
\
SET_TX_FIFO(dev, PCD_ENDP1, BULK_EP1_TX_FIFO_ADDR,
BULK_EP1_TX_FIFO_SIZE);
\
/* Init ep2 */ \
INIT_EP_Rx(dev, PCD_ENDP2, BULK_EP2_TYPE, BULK_EP2_RX_SIZE);
\
}while(0)
5.5 Respond to Reset event and initialization endpoint
After the TeenyUSB protocol stack receives the Reset event, it sets the device address to 0 and reinitializes all endpoints.
According to the USB specification, only endpoint 0 should be initialized and set in the Set
Configuration Request.
74/109
Set other endpoints. TeenyUSB takes a simplified approach and configures all endpoints. See
Configuring Devices for the reason.
section content. After Reset, the device can communicate with the host through the default address
0. Based on the previous reasons, the initial
ization endpoint and Reset processing are introduced together.
The process of TeenyUSB and the HAL library handling the Reset event and configuring the
endpoint is as shown in the figure below:
Endpoint configuration
The above figure contains the endpoint initialization process of the USB FS module. The following
describes the code for initializing the endpoint in TeenyUSB.
Code, readers who are familiar with code can skip the rest of this section.
5.5.1 TeenyUSB library Reset event processing and endpoint initialization
75/109
In the OTG module, the Reset event is divided into two parts for processing, one part is processed
in the USBRST interrupt, and the other part is processed in the USBRST interrupt.
The points are handled in the ENUMDNE interrupt. code show as below:
void tusb_otg_device_handler(tusb_device_t* dev){
...
if(INTR() & USB_OTG_GINTSTS_USBRST ){
uint32_t i;
USBx_DEVICE->DCTL &= ~USB_OTG_DCTL_RWUSIG;
flush_tx(USBx, 0x10);
for (i = 0; i < MAX_EP_NUM ; i++){
USBx_INEP(i)->DIEPINT = 0xFF;
USBx_INEP(i)->DIEPCTL &= ~USB_OTG_DIEPCTL_STALL;
USBx_INEP(i)->DIEPCTL |= USB_OTG_DIEPCTL_EPDIS;
USBx_OUTEP(i)->DOEPINT = 0xFF;
USBx_OUTEP(i)->DOEPCTL &= ~USB_OTG_DOEPCTL_STALL;
USBx_OUTEP(i)->DOEPCTL |= USB_OTG_DOEPCTL_EPDIS;
}
USBx_DEVICE->DAINT = 0xFFFFFFFF;
USBx_DEVICE->DAINTMSK |= 0x10001;
{
USBx_DEVICE->DOEPMSK |= (USB_OTG_DOEPMSK_STUPM |
USB_OTG_DOEPMSK_XFRCM |
USB_OTG_DOEPMSK_EPDM);
USBx_DEVICE->DIEPMSK |= (USB_OTG_DIEPMSK_TOM | USB_OTG_DIEPMSK_XFRCM |
USB_OTG_DIEPMSK_EPDM);
}
/* Set Default Address to 0 */
USBx_DEVICE->DCFG &= ~USB_OTG_DCFG_DAD;
USBx->GINTSTS = USB_OTG_GINTSTS_USBRST;
}
/* Handle Enumeration done Interrupt */
if(INTR() & USB_OTG_GINTSTS_ENUMDNE ){
tusb_reconfig(dev);
USBx->GUSBCFG &= ~USB_OTG_GUSBCFG_TRDT;
switch(USBx_DEVICE->DSTS & USB_OTG_DSTS_ENUMSPD){
case DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ:
USBx->GUSBCFG |= (uint32_t)((USBD_HS_TRDT_VALUE << 10) &
USB_OTG_GUSBCFG_TRDT);
break;
case DSTS_ENUMSPD_LS_PHY_6MHZ:
USBx_INEP(0)->DIEPCTL |= 3; // force ep0 packet size to 8 when in LS mode
case DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ:
case DSTS_ENUMSPD_FS_PHY_48MHZ:
USBx->GUSBCFG |= (uint32_t)((0x6 << 10) & USB_OTG_GUSBCFG_TRDT);
break;
76/109
}
/* setup EP0 to receive SETUP packets */
tusb_otg_device_prepare_setup(dev);
USBx_DEVICE->DCTL |= USB_OTG_DCTL_CGINAK;
USBx->GINTSTS = USB_OTG_GINTSTS_ENUMDNE;
}
...
}
In the RESET interrupt, all endpoints are reinitialized and the device address is set to the default
address 0.
In the ENUMDNE interrupt, call the tusb_reconfig callback function to configure the endpoint and
FIFO. Starter
Setup packet received on point 0.
5.6 Endpoint data processing
Unlike the USB FS module, data processing in the OTG module is based on transmission. In the
USB FS module, a
This transaction will trigger a CTR interrupt. In the OTG module, a complete transfer triggers the
XFRC interrupt.
Perform endpoint data processing during interruption. Triggered when the transmitted data has short
packets or the transmitted data length is the same as the preset
Transfer completion conditions. For detailed transfer completion conditions, see 2.4 Transfer
Completion Conditions.
Data transmission in the OTG module is carried out through FIFO. Data is received in the RXFLVL
interrupt and transmitted in the TXFE interrupt.
Send data. Due to the existence of FIFO, the processing of endpoint transmission events and data
movement operations are separated. OTG module
The endpoint data processing flow is shown in the figure below:
77/109
TeenyUSB OTG device mode endpoint data handling
As you can see from the picture above,
Data processing is in the tusb_otg_read_data and tusb_fifo_empty functions respectively.
The processing of transmission completion is independent of data processing.
As you can also see in the above figure, endpoint 0 is an exception and has a special processing
flow. This is because endpoint 0 transmits data
Registers are different from others in that they can process limited data. Here, the transmission
length of endpoint 0 is set to the maximum packet length,
In this way, a complete interrupt will be triggered if a packet of data is not sent. In the completion
interrupt, based on the size of the remaining data, decide whether
Do you need to continue transmitting data?
5.6.1 OUT endpoint data processing
The OUT endpoint data processing code is as follows:
if(INTR() & USB_OTG_GINTSTS_OEPINT){
uint32_t ep_intr = ((USBx_DEVICE->DAINT & USBx_DEVICE->DAINTMSK)) >> 16;
uint8_t EPn = 0;
while(ep_intr){
if(EPn >= MAX_EP_NUM){
break;
}
if (ep_intr & 0x1){
uint32_t epint = USBx_OUTEP(EPn)->DOEPINT;
tusb_ep_data* ep = &dev->Ep[EPn];
if(( epint & USB_OTG_DOEPINT_XFRC) == USB_OTG_DOEPINT_XFRC){
78/109
uint32_t maxpacket = GetOutMaxPacket(dev, EPn);
if(EPn == 0){
if(ep->rx_count == 0 || ep->rx_count >= ep->rx_size || ep->rx_count % maxpacket){
if(dev->ep0_rx_done){
dev->ep0_rx_done(dev);
dev->ep0_rx_done = 0;
}
ep->rx_buf = 0;
// prepare ep0 to recv next setup packet
tusb_otg_device_prepare_setup(dev);
}else{
ep->rx_buf += ep->rx_count;
ep->rx_size -= ep->rx_count;
tusb_set_rx_valid(dev, EPn);
}
}else{
if(tusb_on_rx_done(dev, EPn, ep->rx_buf, ep->rx_count) == 0){
ep->rx_count = 0;
tusb_set_rx_valid(dev, EPn);
}else{
ep->rx_count = ep->rx_size;
}
}
}
// Read the DOEPINT again, to make sure the SETUP flag is set
epint = USBx_OUTEP(EPn)->DOEPINT;
if(( epint & USB_OTG_DOEPINT_STUP) == USB_OTG_DOEPINT_STUP){
ep->rx_buf = 0;
tusb_setup_handler(dev);
if(ep->rx_buf){
// rx_buf is not null, means setup need write some data
tusb_set_rx_valid(dev, EPn);
}else{
// otherwise prepare recv setup packet again
tusb_otg_device_prepare_setup(dev);
}
}
// clear all interrupt flags
USBx_OUTEP(EPn)->DOEPINT = epint;
}
ep_intr>>=1;
EPn+=1;
}
}
The outermost layer is the following loop structure:
if(INTR() & USB_OTG_GINTSTS_OEPINT){
79/109
uint32_t ep_intr = ((USBx_DEVICE->DAINT & USBx_DEVICE->DAINTMSK)) >> 16;
uint8_t EPn = 0;
while(ep_intr){
if(EPn >= MAX_EP_NUM){
break;
}
...
ep_intr>>=1;
EPn+=1;
}
By using the flag bit, you can know which endpoint has an interrupt. Then according to the
corresponding register of the endpoint, the interrupt output is obtained
specific reasons for the birth.
Endpoint 0 processing method, if the receiving buffer is full or the current packet is a short packet,
it means that the transmission is completed and the registration is executed.
function on ep0_rx_done and restart receiving SETUP packets. Otherwise, adjust the cache position
and continue to receive the next packet.
data. If it is another endpoint, call the tusb_on_rx_done callback function directly. If it is a SETUP
package, execute
tusb_setup_handler, executes the SETUP processing flow.
Data movement of the OUT endpoint is performed in FIFO-related processing functions, see 5.3.2
Reading FIFO data.
In TeenyUSB, the OUT endpoint is enabled through the tusb_set_rx_valid function. The function
implementation code is as follows:
void tusb_set_rx_valid(tusb_device_t* dev, uint8_t EPn){
PCD_TypeDef* USBx = GetUSB(dev);
USB_OTG_OUTEndpointTypeDef* epout = USBx_OUTEP(EPn);
tusb_ep_data* ep = &dev->Ep[EPn];
uint32_t maxpacket = GetOutMaxPacket(dev, EPn);
uint32_t pktCnt;
uint32_t len = ep->rx_size;
ep->rx_count = 0;
if(EPn == 0){
// EP0 always recv 1 packet
if(len > maxpacket){
len = maxpacket;
}
}
pktCnt = (((len + maxpacket - 1) / maxpacket));
if(pktCnt == 0){
// avoid zero packet count, used to send ZLP(zero length packet)
pktCnt = 1;
}
if(len) len = pktCnt * maxpacket;
80/109
// clear and set the EPT size field
epout->DOEPTSIZ =
(pktCnt<<19) | len;
if(USBx->GAHBCFG & USB_OTG_GAHBCFG_DMAEN){
epout->DOEPDMA = (uint32_t)ep->rx_buf;
}
if(ISO_EP && ((epout->DOEPCTL & USB_OTG_DOEPCTL_EPTYP)
==
((USB_EP_ISOCHRONOUS)<<(USB_OTG_DOEPCTL_EPTYP_Pos))) ){
if ((USBx_DEVICE->DSTS & ( 1 << 8 )) == 0){
epout->DOEPCTL |= USB_OTG_DOEPCTL_SODDFRM;
}else{
epout->DOEPCTL |= USB_OTG_DOEPCTL_SD0PID_SEVNFRM;
}
}
epout->DOEPCTL |= (USB_OTG_DOEPCTL_CNAK | USB_OTG_DOEPCTL_EPENA);
}
Compared with the tusb_set_rx_valid function in the USB FS module, this function is much more
complicated. This is because OTG
The module will automatically determine whether the transmission is completed based on the total
length of the transmission and the maximum packet length of the endpoint, so during initialization,
the first thing to do is
First configure the cache length.
5.6.2 IN endpoint data processing
The IN endpoint interrupt data processing code is as follows:
USB_OTG_INEndpointTypeDef* epin = USBx_INEP(ep);
uint32_t epint = epin->DIEPINT;
// Xfer complete interrupt handler
if(epint & USB_OTG_DIEPINT_XFRC){
USBx_DEVICE->DIEPEMPMSK &= ~(0x1ul << ep);
epin->DIEPINT = USB_OTG_DIEPINT_XFRC;
tusb_send_data_done(dev, ep);
}
// FIFO empty interrupt handler
if( ((epint & USB_OTG_DIEPINT_TXFE) == USB_OTG_DIEPINT_TXFE) &&
(USBx_DEVICE->DIEPEMPMSK &
(1 << ep)) ){
tusb_fifo_empty(dev, ep);
}
The code for the outer loop is not written here. If the transfer is completed, call the
tusb_send_data_done function.
Since each IN endpoint has an independent FIFO, FIFO-related events also need to be handled in
each IN endpoint. Tx
81/109
For applications, FIFO can only write data.
Therefore, when the IN endpoint needs to send data, open the EMPTY of the TX FIFO
Interrupt, so that data can be written to the FIFO after an interrupt is generated. For details of the
TX FIFO processing function, see 5.3.3 Writing
FIFO data. The IN endpoint data transmission completion processing function is as follows:
// called by the ep data interrupt handler when last packet tranfer done
void tusb_send_data_done(tusb_device_t* dev, uint8_t EPn){
PCD_TypeDef* USBx = GetUSB(dev);
tusb_ep_data* ep = &dev->Ep[EPn];
uint32_t maxpacket = get_max_in_packet_size(USBx, EPn);
//track(EPn, ep->tx_size, 3, ep->tx_last_size);
if(EPn == 0){
if(ep->tx_size){
tusb_send_data(dev, EPn, ep->tx_buf, ep->tx_size);
}else if(ep->tx_last_size == maxpacket){
// Send a ZLP
tusb_send_data(dev, EPn, ep->tx_buf, 0);
}else if(dev->ep0_tx_done){
// invoke status transmitted call back for ep0
dev->ep0_tx_done(dev);
dev->ep0_tx_done = 0;
}
}else{
// clear the fifo empty mask
tusb_on_tx_done(dev, EPn);
}
}
If it is endpoint 0, call it again based on the remaining data size in the cache or the packet length of
the last packet of data.
tusb_send_data sends data. If it is another endpoint, call the tusb_on_tx_done callback function
directly.
For non-0 endpoints, the transmission completion processing in the OTG module is very simple.
The corresponding function is called according to the interrupt signal.
Just call back the function.
The data sending function code of the IN endpoint is as follows:
int tusb_send_data(tusb_device_t* dev, uint8_t EPn, const void* data, uint16_t len){
PCD_TypeDef* USBx = GetUSB(dev);
tusb_ep_data* ep = &dev->Ep[EPn];
uint32_t maxpacket = GetInMaxPacket(dev, Epn);
uint32_t pktCnt;
uint32_t total_len = len;
USB_OTG_INEndpointTypeDef* epin = USBx_INEP(EPn);
ep->tx_buf = (const uint8_t*)data;
82/109
if(epin->DIEPCTL & USB_OTG_DIEPCTL_EPENA){
return -1;
}
if(EPn == 0){
// EP0 always send 1 packet
if(len > maxpacket){
len = maxpacket;
}
}
// set remain size of tx buffer, current tx size is saved in the DIEPTSIZ register
ep->tx_size = total_len - len;
// calculate last packet size
ep->tx_last_size = len ? (len-1) % maxpacket + 1 : 0;
pktCnt = (((len + maxpacket - 1) / maxpacket)<<19);
if(pktCnt == 0){
// avoid zero packet count, used to send ZLP(zero length packet)
pktCnt = 1<<19;
}
// clear and set the EPT size field
epin->DIEPTSIZ =
pktCnt| len;
if(USBx->GAHBCFG & USB_OTG_GAHBCFG_DMAEN){
epin->DIEPDMA = (uint32_t)ep->tx_buf;
}else{
if(len > 0){
USBx_DEVICE->DIEPEMPMSK |= (1 << EPn);
}
}
// do not fill data here, data will be filled in the empty interrupt
//copy_data_to_fifo(dev, ep, EPn, data, len, total_len);
epin->DIEPCTL |= (USB_OTG_DIEPCTL_CNAK | USB_OTG_DIEPCTL_EPENA);
return 0;
}
This function mainly calculates the data packet length, sets the number of packets and data length,
and then starts the endpoint. right
For endpoint 0, the maximum amount of data of the maximum packet length can be sent at one
time. If DMA is not enabled and the packet length is not 0, enable the
The FIFO EMPTY interrupt of the point is moved to the FIFO in the EMPTY interrupt. Data
migration can also be performed here.
If the remaining space of the FIFO is large enough and all data is successfully moved, the FIFO
EMPTY interrupt does not need to be turned on. exist
The host code of the HAL library is to move data in the sending function, but the code of the host
part of the HAL library goes another way.
At one extreme, it only moves data in the sending function and does not activate the EMPTY
interrupt to move the remaining data.
83/109
5.7 DMA operations
The high-speed OTG module has the DMA function, and the data movement can be completed by
the DMA controller of the OTG module.
become. The OTG DMA of some chips cannot move data from Flash. For static data such as
descriptors, it is necessary to
It must be moved to RAM before sending. All descriptors are defined as RAM data in the HAL
library, so
This problem does not exist.
5.7.1 OUT endpoint DMA
When DMA is not enabled, the data reception of the OUT endpoint is completed in the RXFLVL
interrupt. After DMA is enabled, no
RXFLVL interrupt to move data, the code is as follows:
if(USBx->GAHBCFG & USB_OTG_GAHBCFG_DMAEN){
// If DMA enabled, setup the threshold value
USBx_DEVICE->DTHRCTL = (USB_OTG_DTHRCTL_TXTHRLEN_6 |
USB_OTG_DTHRCTL_RXTHRLEN_6);
USBx_DEVICE->DTHRCTL |= (USB_OTG_DTHRCTL_RXTHREN |
USB_OTG_DTHRCTL_ISOTHREN |
USB_OTG_DTHRCTL_NONISOTHREN);
}else{
USBx->GINTMSK |= USB_OTG_GINTMSK_RXFLVLM;
}
When DMA is not enabled, the actual length of the received data is updated in the RXFLVL
interrupt. After DMA is enabled, until the data
The application will not be notified until the transfer is completed, so the actual received data length
is calculated in the transfer completion interrupt. code show as below:
uint32_t maxpacket = GetOutMaxPacket(dev, EPn);
if(USBx->GAHBCFG & USB_OTG_GAHBCFG_DMAEN){
// Calculate recv data length from the XFRSIZ field
uint32_t total_xfer_size;
if(EPn == 0){
total_xfer_size = maxpacket;
}else{
total_xfer_size = ((ep->rx_size + maxpacket - 1) / maxpacket) * maxpacket;
}
// DMA enabled, recv data count is total_xfer_size minus transfer remain length
ep->rx_count += total_xfer_size - (USBx_OUTEP(EPn)->DOEPTSIZ &
USB_OTG_DOEPTSIZ_XFRSIZ);
}
For endpoint 0, the total length of data that needs to be transmitted is the maximum packet length.
For other endpoints, the total length of data that needs to be transmitted is
84/109
The degree is the buffer length rounded to the maximum packet length. The XFRSIZ field in
DOEPTSIZ is set to need to be passed during initialization.
The total length of the input data will be automatically decremented after receiving the data. At this
point, the XFRSIZ field indicates that the total length remains
The remaining number of bytes. Actual received data length = total length - remaining length.
There is another problem here. When the SETUP packet is received, the SETUP packet is only 8
bytes, but the largest packet is used here.
length is calculated, so the actual length calculated by the SETUP package will be incorrect. But it
doesn't matter here, SETUP package starts
It ends up being 8 bytes, and the length calculated here is not used.
98www.tusb.org
5.7.2 DMA processing of SETUP packet
When DMA is not started, the SETUP packet is processed in the RXFLVL interrupt and is
processed according to the data tag in the FIFO.
log, copying the SETUP package to the device's SETUP cache. Regardless of the status of the
endpoint, the SETUP transfer will always succeed.
Therefore, when DMA is not started, there is no need to worry about the reception of SETUP
packets.
After enabling DMA, the SETUP packet will be placed in the address pointed to by the DOEPDMA
register of endpoint 0, and the DMA transfer
The value of DOEPDMA will be automatically updated after input, so the value in DOEPDMA
needs to be updated at the appropriate time to ensure
Correct reception of SETUP packet. TeenyUSB sets SETUP reception through the
tusb_otg_device_prepare_setup function
cached address.
5.7.3 IN endpoint DMA
DMA data processing of the IN endpoint is relatively simple. If DMA is enabled, you only need to
write the cache address to the DMA address.
Just use the register, the code is as follows:
int tusb_send_data(tusb_device_t* dev, uint8_t EPn, const void* data, uint16_t len){
...
if(USBx->GAHBCFG & USB_OTG_GAHBCFG_DMAEN){
epin->DIEPDMA = (uint32_t)ep->tx_buf;
}else{
if(len > 0){
USBx_DEVICE->DIEPEMPMSK |= (1 << EPn);
}
}
...
}
5.8 USB OTG usage summary
The usage process of USB OTG devices in TeenyUSB is as shown in the figure below:
85/109
USB OTG device mode usage process
The gray part in the above picture is the API provided by TeenyUSB. There are two types of APIs,
one is ordinary functions and the other is callbacks.
function. In TeenyUSB, the development of USB devices is completed through the abovementioned ordinary functions and callback functions. Specifically
For the USB device development process, see Chapter 6 USB Device Development.
5.8.1 Ordinary functions
Ordinary functions are actively called by the application, and their definitions are shown in the
following table:
Table 22
TeenyUSB common functions
Ordinary function
tusb_get_device
tusb_open_device
tusb_close_device
tusb_send_data
86/109
tusb_set_rx_buffer
tusb_set_rx_valid
illustrate
Obtain the device. Before starting to use the USB device, call this function to obtain the device pair.
Object, used by subsequent functions, not shown in the above figure
Open the USB module and set the device to connected state
Turn off the USB module and set the device to the disconnected state, which is not shown in the
picture above.
send data
Set the receive cache and start the endpoint sending function
Start the endpoint receiving function according to the content set in tusb_set_rx_buffer
Initialize the endpoint
5.8.2 Callback function
The callback function is called when a specific event occurs. The callback function in TeenyUSB is
of weak type and is implemented in the application.
Now calls the version in the application, or the default version if the application does not implement
it. The callback function definition is shown in the table below:
Table 23
TeenyUSB callback function
Callback
tusb_reconfig
tusb_class_request
tusb_on_rx_done
tusb_on_tx_done
tusb_delay_ms
illustrate
Called when a Reset interrupt is received. All endpoints are initialized in this function.
Initialize all descriptors of the device
Debugging when a device class request is received, handle the specific device type in this function
Related requests. Returning 0 indicates that the request requires the protocol stack to continue
processing. Return 1 table
Indicates that the request has been processed and does not require protocol stack processing.
Called when the OUT transfer is completed. This function returns 0 to indicate that the data
processing in the cache is completed.
, the cache can receive subsequent packets. This function returns non-0, indicating cached data
Not processed, reception is pending, call tusb_set_rx_va when the data is processed
lid re-enables endpoint reception
Called when the IN endpoint transmission is completed, indicating that the data transmission is
completed
Delay function in milliseconds, implemented by the application according to the platform
5.8.3 Comparison between USB FS and OTG devices
5.8.3.1 Endpoint data processing
87/109
USB FS module endpoint data processing
88/109
OTG module endpoint data processing
As can be seen in the above two figures, the USB FS and OTG processing flows are basically
similar. The difference is that in USB FS
In the tusb_send_data_done function and tusb_recv_data function, it is not only necessary to
determine whether the transmission is completed, but also
Data migration.
In the OTG module,
Data migration is handled in tusb_fifo_empty and tusb_org_read_data respectively.
These two functions will not determine whether the transfer is completed, but only move data.
When the high-speed OTG module enables DMA,
The two functions tusb_fifo_empty and tusb_org_read_data can also be omitted.
89/109
6 USB device examples
6.1 TeenyUSB protocol stack usage instructions
When designing a specific USB device, the general process is: first plan the function of the device,
determine the device descriptor, and implement
Processing of device type-related requests and processing of device business data. When using
TeenyUSB, the following design can be used
process:
6.1.1 TeenyUSB file structure
The TeenyUSB protocol stack code is in the usb_stack directory. The function description of each
file is as follows:
Table 24
TeenyUSB file structure and functions
Directory File Name usb_stack/inc teeny_usb.h usb_stack/inc usbd_conf.h usb_stack/inc
teeny_usb_platform.h usb_stack/inc stm32_otg_platform.h usb_stack/inc stm32_fs_platform.h
usb_stack/src teeny_usb.c usb_stack/src teeny_usb_stm32
_fs_device.c STM32 USB FS module operation implementation file
usb_stack/src teeny_usb_stm32
_otg_device.c STM32 OTG module device mode basic operation implementation file
usb_stack/src stm32f_otg_init.c STM32 chip OTG module initialization related operation
implementation file, in this article
Implement IO initialization, USB module initialization, interrupt entry processing, etc. in the
software
usb_stack/src stm32f_fs_init.c
Development board related directory startup_stm32f<xxx>.s
Development board related directories system_stm32f<xxx>.c
Development board related directory stm32f<xx>_hal_conf.h
Application related directory teeny_usb_init.h
Application related directories
Application related directories
Application related directory teeny_usb_desc.c
<xxx>_desc.lua
<xxx>_class.c
illustrate
TeenyUSB protocol stack data structure definition and API declaration file, use
Include this file before TeenyUSB protocol stack
HAL library USB protocol stack configuration file, including teeny_usb_ini here
t.h file
90/109
Chip platform definition selection file, when a new supported chip needs to be added
, modify this file and add chip platform definition
OTG platform definition file, in which the relative configuration of chip registers is defined.
Close operations, such as setting endpoint type, setting data length, etc. Applies to S
TM32F105/107/2xx/4xx/7xx and other chips with OTG module
FS platform definition file, in which the relative configuration of chip registers is defined.
Close operations, such as setting endpoint type, setting data length, etc. Applies to S
TM32F/0xx/103/3xx and other chips with USB FS module
USB Device Standard Request Implementation Document
STM32 chip FS module initialization related operation implementation file, in this file
Implement IO initialization, USB module initialization, interrupt entry processing, etc.
Chip related startup files
Chip related clock configuration file
Chip-related HAL library function definition file
The endpoint initialization header file generated by TeenyDT is in usbd_conf.h
Include
Descriptor file generated by TeenyDT
Descriptor definition file in TeenyDT format
Implementation files for device type related requests
The TeenyUSB register definition part adopts the definition of the HAL library, so the TeenyUSB
project needs to rely on the HAL library
And the STM32 USB device library (STM32_USB_Device_Library). When using the HAL library,
you need to use
stm32fxxx_hal_conf.h file to define the functionality of the HAL library. When using the HAL
version of the USB library, you need
The usbd_confg.h file defines the functionality of the USB library. In TeenyUSB, endpoint related
definitions are automatically
Generated in the teeny_usb_init.h file, so add teeny_usb_init.h to the usbd_confg.h file. exist
Add the relevant paths of the HAL library and USB library to the project path.
6.2 Custom USB device
User-defined devices are the simplest device type and the most complex device type. Simple, it is
the opposite
For the device, the descriptor used by the custom device type is the simplest, the device structure is
also simple, and there is no need to deal with device-related
Special Requests. Complex, because for the PC side, drivers need to be developed for customized
devices, and driver development is very complicated.
petty. But since we start with customizing the device, we must first simplify the complex. Later in
this section we will find a way to omit the driver.
In the writing process, it is best not to even write the INF file, so that it can truly be plug-and-play.
Complete code for custom USB devices
The code is in the demo/custom_bulk directory. The custom USB device project structure is as
follows:
91/109
7 STM32 OTG module host mode
The STM32 OTG module can also work in host mode. When working in host mode, a set of devices
are used.
Different registers to operate. Unlike endpoints on devices, hosts introduce the concept of channels.
Every channel is equivalently, it can be configured as IN or OUT as needed, and the channel can be
recycled when no longer needed.
This article gives a brief introduction only for the host model
7.1 Initialization of host mode
The core part initialization is the same as the device mode, and the clock, interrupt, and IO settings
are the same as the device mode. The difference lies within
After the core reset is successful, the host mode initialization code is called. The code is as follows:
void tusb_open_host(tusb_host_t* host){
USB_OTG_GlobalTypeDef* USBx = GetUSB(host);
tusb_otg_core_init((tusb_core_t*) host);
// Force to host mode
USBx->GUSBCFG &= ~(USB_OTG_GUSBCFG_FHMOD | USB_OTG_GUSBCFG_FDMOD);
USBx->GUSBCFG |= USB_OTG_GUSBCFG_FHMOD;
tusb_init_otg_host(host);
}
There is a force to xxx mode code for the initialization of the host and device. In fact, there is no
need to force the setting here.
Set to a specific mode, distinguished by ID. When an ID change is detected, initialization is
performed based on the device's new role.
7.2 Host port operations
After the host kernel initialization is completed, open PORT and wait for device connection. In the
HAL library, PORT is defined as
Port0, I guess this kernel may support multiple ports, similar to a Hub. When the device is
connected, the status of the port changes as shown below:
92/109
Host port status migration diagram
When the port is in the Enabled state, the device can be operated. At this time,
the device has completed the reset. After the reset, the device address is 0,
the host accesses the device through the 0 address, first sets the address, and
then uses the new address to enumerate the device.
7.3 Host mode FIFO
The host FIFO structure is shown in the figure below:
OTG host FIFO structure (picture from STM32 specification book) There are three
FIFOs in master mode, one Rx FIFO for all IN channels and one periodic Tx FIFO
for OUT channels of type Interrupt and Sync, an aperiodic Tx FIFO for OUT of
type Control and Bulk aisle. Since the FIFO mode of the host is fixed, the
allocation of FIFO space and address is completed during initialization. Please
note that What is interesting is that after the kernel is reset, the FIFO
address allocation needs to wait for a period of time before the setting is
successful. The reference for this time is Implementation of HAL library code,
the specific reason is unknown.
93/109
7.4 Request Queue in the host
According to the chip manual, in addition to the data FIFO, the STM32 OTG host
also has a Reqeust Queue
The request queue has a depth of 8, one for each periodic channel and one for
aperiodic channels. When sending to FIFO
Before sending data, you need to check whether there is free space in the FIFO
and also check whether there is free space in the request queue. one
Each basic transaction will occupy a request queue. For IN/OUT transfers, a
transfer consists of multiple transactions.
For the relationship between transfer and transaction, see the section 2.4
Transfer Completion Conditions.
For example, a 1024-byte transfer requires at least 16 transactions on an
endpoint with a maximum packet length of 64 bytes. Right now
Even if the FIFO has 1024 bytes of free space, it cannot be completely
transferred. Because the STM32 OTG module can only handle at most
8 request queues. At this time, it is necessary to calculate the amount of data
that can be transmitted based on the FIFO space and queue space, and write it
into the FIFO
, and the remaining data is processed in NPTXFE or PTXFE. If DMA is enabled,
this part of the processing will be done by OTG
The module is completed automatically.
7.5 Host channel
The host’s channel characteristics register is defined as follows:
STM32 channel characteristics register (picture from STM32 specification book)
As you can see, the channel can define information such as device address,
channel type, endpoint number, endpoint direction, maximum packet length, etc.
In device mode, you cannot configure a device address for each endpoint because
the device has only one address.
Under host mode, different device addresses can be configured for each channel.
This is because the host mode channel can be accessed through the HUB by
different devices.
The LSDEV field in the above figure indicates whether the channel is a low-speed
channel. This low speed is not only the current STM32 host side interface speed,
but refers to the interface speed of the target device. For example, the STM32
host interface is connected to a full-speed HUB.
A low-speed keyboard is connected to the full-speed HUB. If you want to transmit
data to this low-speed keyboard, you need to change the channel characteristics.
The speed of the register is set to low speed.
94/109
7.5.1 Channel data sending and receiving
The host channel data processing flow is shown in the figure below:
Host channel data processing flow
The API description provided by TeenyUSB is shown in the table below:
Ordinary function
Description
tusb_get_host
Obtain the host. Before starting to use the USB host, call this function to obtain the host pair.Object,
used by subsequent functions, not shown in the above figure
tusb_open_host
Turn on the USB host and set the host to ready state
tusb_close_host
Turn off the USB module and set the host to the off state, which is not shown in the above picture.
tusb_host_port_reset
reset port
tusb_pipe_open
open a channel
tusb_pipe_close
close a channel
tusb_pipe_setup
Send SETUP packet using channel
tusb_pipe_xfer_data
Use channels to transfer data
tusb_pipe_wait
Waiting for channel data transmission, you can set the timeout, if the timeout is 0 , then return
immediately after checking the channel status Callback
tusb_host_port_changed This callback function is called when the port status changes
tusb_on_channel_event
This callback function is called when the channel status changes
Table 30 TeenyUSB host mode interface description
95/109
8 USB host development
The basic data transmission of the host is very simple. The main difficulty reason of the host lies in
the development of the protocol stack and the compatibility with various devices.
Two examples will be used below to illustrate the main development methods on the host side.
These examples only implement some of the functions of the device.
It can only demonstrate how the host works and cannot be used in actual projects. The compatibility
with the device is also very poor and may only works on some specific devices.
There is not much fault tolerance mechanism in the code. In order to ensure better compatibility, the
host protocol stack tolerance requires a lot of fault tolerance processing.
8.1 Host without protocol stack
Use the front custom USB device as a slave and then control it directly from the host.
The operation process of an Host without protocol stack is very simple. First use the control channel
to obtain the device descriptor, then send data through the OUT channel, and then use the IN
channel to receive the data returned by the device. The complete code is in the demo/host_raw
directory.
The main function processing code is as follows:
int main(void){
uint8_t speed;
tusb_host_t* host = tusb_get_host(TEST_APP_USB_CORE);
HOST_PORT_POWER_ON();
tusb_open_host(host);
while(1){
if(state == TUSB_HOST_PORT_CONNECTED){
state = TUSB_HOST_PORT_DUMMY;
// reset port0
tusb_port_set_reset(host, 0, 1);
tusb_delay_ms(100);
// release port0
tusb_port_set_reset(host, 0, 0);
tusb_delay_ms(100);
speed = tusb_port_get_speed(host, 0);
// Open Control pipe
tusb_pipe_open(host, &pipe_ctrl_in, 0, 0x80, EP_TYPE_CTRL,
EP_MPS, speed);
tusb_pipe_open(host, &pipe_ctrl_out, 0, 0x00, EP_TYPE_CTRL,
EP_MPS, speed);
// Get device descriptor
tusb_pipe_setup(&pipe_ctrl_out, &setup);
tusb_pipe_wait(&pipe_ctrl_out, 0xffffffff);
// Prepare buffer to recv device descriptor
tusb_pipe_xfer_data(&pipe_ctrl_in, device_descriptor,
sizeof(device_descriptor));
tusb_pipe_wait(&pipe_ctrl_in, 0xffffffff);
// Send status packet
96/109
tusb_pipe_xfer_data(&pipe_ctrl_out, 0, 0);
tusb_pipe_wait(&pipe_ctrl_out, 0xffffffff);
// Send test data
tusb_pipe_open(host, &pipe_write, 0, WRITE_EP, EP_TYPE_BULK,
EP_MPS, speed);
tusb_pipe_xfer_data(&pipe_write, test_data,
sizeof(test_data));
tusb_pipe_wait(&pipe_write, 0xffffffff);
// Recv test data
tusb_pipe_open(host, &pipe_read, 0, READ_EP, EP_TYPE_BULK,
EP_MPS, speed);
tusb_pipe_xfer_data(&pipe_read, buf, sizeof(buf));
tusb_pipe_wait(&pipe_read, 0xffffffff);
}else if(state == TUSB_HOST_PORT_DISCONNECTED){
state = TUSB_HOST_PORT_DUMMY;
// Close all pipe when device disconnected
tusb_pipe_close(&pipe_ctrl_in);
tusb_pipe_close(&pipe_ctrl_out);
tusb_pipe_close(&pipe_write);
tusb_pipe_close(&pipe_read);
}
}
}
When the device is successfully connected, reset the device. The formal approach
is to wait for the port to become Enabled. Here
Just a brief wait. Then open a control channel with address 0 and endpoints 0x80
and 0x00. these two
Each channel corresponds to the control endpoint of the device. Since the
address is 0 after the device is reset, address 0 is used.
Then send a SETUP packet to obtain the device descriptor through the OUT
channel, which is a read data control transfer.
Therefore it is followed by an IN transfer to receive the device descriptor.
According to the processing flow of read data control transmission, receive
After success, a status packet with a packet length of 0 is sent through OUT
transmission.
Next, open a BULK OUT channel with the endpoint number WRITE_EP and write test
data to it. Then hit again
Open a BULK IN channel with the endpoint number READ_EP and read back test data
to it. This completes the test.
Although such a host looks simple, it can indeed communicate with the device.
Here is the processing of device requests
And a demonstration of data processing, the actual device will be much more
complicated than this. The standard process is as follows:
1. You need to set the device address, which is not done here because we always
use address 0 for communication.
2. Read the configuration and obtain the endpoint usage information. We didn’t do
it here because the equipment was developed by ourselves,
We all know what endpoint parameters are used, so it can be used without reading
the configuration descriptor.
3. Configure the device, which is not done here. Because when our device is
reset, all endpoints will be
It starts initialization, so it can be used without configuration.
97/109
4. Type-related request processing is not available here. If it is another
device, please perform type-related processing.
Ask for processing.
5. Operate the device. Under normal circumstances, if the device has an IN
endpoint, the host should
Content, open a channel for the device, and then maintain the channel. Here we
do not change IN after receiving the data.
When the channel is enabled again, a normal host should keep the IN channel
valid. Enable the channel again after the transfer is complete.
8.2 Hosts that support keyboard, mouse and HUB
This section will briefly introduce the working methods of keyboard, mouse and
HUB. The complete code is in the demo/host_input directory
middle. The function implemented by this routine is to update the status of the
Caps Lock light when the Caps Lock key of the keyboard is pressed; when Num
When Lock is pressed, updates the state of the Num Lock light. If the keyboard
and mouse are connected to the development board through the HUB, except
In addition to the keyboard being able to control the LED lights on the
keyboard, the mouse can also control the status of the keyboard LED lights. When
the left mouse button is clicked
When, the status of Num Lock is updated; when the right mouse button is clicked,
the status of Caps Lock is updated.
8.2.1 Enumeration process of general devices
The device enumeration process is as follows:
Power on the device
Device reset
Get the first 8 bytes of the device descriptor Device reset (optional operation)
Set device address
Get device descriptors and other descriptors
Configure device
Configure the interface (optional operation)
Configure device properties (optional)
Set the device according to the device interface type (interface type related)
The device is working properly
Table 31 - Device enumeration process
For detailed instructions on the device enumeration process, see 2.2 Basic USB Operations. In the
example host_loop function, check Test the port status. When the port is in the Enabled state, call
the enum_device function to start device enumeration. The code is as follows:
98/109
void host_loop(tusb_host_t* host){
if( host->state == TUSB_HOST_PORT_CONNECTED){
// reset port
tusb_host_port_reset(host, 0, 1);
delay_ms(50);
tusb_host_port_reset(host, 0, 0);
delay_ms(100);
}else if( host->state == TUSB_HOST_PORT_ENABLED ){
if(!root){
root = new_device();
root->is_root = 1;
if(enum_device(host, root) != 0){
free_device(root);
root = 0;
}
}
}else if( host->state == TUSB_HOST_PORT_DISCONNECTED ){
if(root){
free_device(root);
root = 0;
}
}
}
In the enum_device function, devices are enumerated according to the process in
the table above. Part of the code is as follows:
int enum_device(tusb_host_t* host, usb_device_t* device){
static uint8_t init = 0;
static uint8_t addr = 1;
uint32_t retry = 0;
device->host = host;
if(!init){
if( tusb_pipe_open(host,
&def_ctrl.ctrl_out, 0, 0x00, EP_TYPE_CTRL, 8) == 0
&& tusb_pipe_open(host,
&def_ctrl.ctrl_in, 0, 0x80, EP_TYPE_CTRL, 8) == 0){
init = 1;
}
}
retry = GetDeviceDesc(&def_ctrl, device->device_desc, 8) == 0;
device->addr = addr;
addr++;
if(SetAddress(&def_ctrl, device->addr) != 0){
return -1;
}
if( tusb_pipe_open(host, &device->ctrl_pipe.ctrl_in,
device->addr, 0x80, EP_TYPE_CTRL,device->device_desc[7]) == 0
&& tusb_pipe_open(host, &device->ctrl_pipe.ctrl_out,
device->addr, 0x00, EP_TYPE_CTRL,device->device_desc[7]) == 0){
}else{
// fail to open control pipe
goto dev_fail;
99/109
}
// Get config descriptor header
if(GetConfigDesc(&device->ctrl_pipe, device->config_desc, 9) != 0){
goto dev_fail;
}
device->config_len = *(uint16_t*)(device->config_desc+2);
// get total config descriptor
retry = GetConfigDesc(&device->ctrl_pipe, device->config_desc,
device->config_len) == 0;
// Set config to 0
if(SetConfig(&device->ctrl_pipe) != 0){
goto dev_fail;
}
if(init_interface(device) != 0){
goto dev_fail;
}
return 0;
dev_fail:
return -1;
}
First use the channel on the default address to obtain the first 8 bytes of the
device descriptor and set the address. After setting the address successfully,
set the
Open two new control channels on the device, and the subsequent enumeration
process is completed using the new control channels.
Then get the configuration descriptor, set the configuration, and enter the
processing function related to the interface type after success. Type-related
processing
The management code is as follows:
int init_interface(usb_device_t* device){
USBH_CfgDescTypeDef* cfg = (USBH_CfgDescTypeDef*)device->config_desc;
if(cfg->bNumInterfaces>0){
if(cfg->Itf_Desc[0].bInterfaceClass == USB_HUB_CLASS){
return init_hub(device);
}else if(cfg->Itf_Desc[0].bInterfaceClass == USB_HID_CLASS){
return init_hid(device);
}else{
// un-supported class
goto itf_fail;
}
}
return 0;
itf_fail:
return -1;
}
This example only supports two types of devices: HID and HUB.
100/109
8.2.2 Keyboard and mouse
Both the keyboard and the mouse are HID devices. The data format of the HID
device is described by the report descriptor, which is not included here.
The report descriptor is parsed and processed. Because the keyboard and mouse
have a special protocol called Boot Protocol,
This protocol is designed to allow keyboard and mouse to run on hosts with very
limited resources.
For example, in the computer's BIOS system.
It is wasteful to parse the report descriptor in this case, so the USB
organization designed the Boot protocol for keyboard and mouse.
The report descriptor under this protocol is fixed and does not need to be
parsed.
Under the Boot protocol, the keyboard report descriptor:
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x06, // USAGE (Keyboard)
0xa1, 0x01, // COLLECTION (Application)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)
0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x08, // REPORT_COUNT (8)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x08, // REPORT_SIZE (8)
0x81, 0x03, // INPUT (Cnst,Var,Abs)
0x95, 0x05, // REPORT_COUNT (5)
0x75, 0x01, // REPORT_SIZE (1)
0x05, 0x08, // USAGE_PAGE (LEDs)
0x19, 0x01, // USAGE_MINIMUM (Num Lock)
0x29, 0x05, // USAGE_MAXIMUM (Kana)
0x91, 0x02, // OUTPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x03, // REPORT_SIZE (3)
0x91, 0x03, // OUTPUT (Cnst,Var,Abs)
0x95, 0x06, // REPORT_COUNT (6)
0x75, 0x08, // REPORT_SIZE (8)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x65, // LOGICAL_MAXIMUM (101)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
0x81, 0x00, // INPUT (Data,Ary,Abs)
0xc0 // END_COLLECTION
According to the above descriptor, the input data format of the keyboard is that
the lower 6 bits of the first byte are modifier keys, expressed bit by bit, and
the 2 bytes are used as placeholders. Bytes 3 to 8 are key codes, and each byte
represents a key. There is also an output here The data is only one byte, the
101/109
lower 5 bits are the status of the keyboard LED light, and the upper 3 bits are
used for placeholders. Under the BOOT protocol, the mouse report descriptor:
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x02, // USAGE (Mouse)
0xa1, 0x01, // COLLECTION (Application)
0x09, 0x01, // USAGE (Pointer)
0xa1, 0x00, // COLLECTION (Physical)
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, 0x03, // USAGE_MAXIMUM (Button 3)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x95, 0x03, // REPORT_COUNT (3)
0x75, 0x01, // REPORT_SIZE (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x05, // REPORT_SIZE (5)
0x81, 0x03, // INPUT (Cnst,Var,Abs)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x30, // USAGE (X)
0x09, 0x31, // USAGE (Y)
0x15, 0x81, // LOGICAL_MINIMUM (-127)
0x25, 0x7f, // LOGICAL_MAXIMUM (127)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x02, // REPORT_COUNT (2)
0x81, 0x06, // INPUT (Data,Var,Rel)
0xc0, // 0xc0 // END_COLLECTION
END_COLLECTION
According to the above descriptor, the data format of the mouse: the lower 3
bits of the 1st byte are buttons, and the upper 5 bits are used as placeholders;
the 2nd byte is the x offset; byte 3 is the y offset. For the keyboard and mouse,
there is an IN endpoint used to send data to the host, and the keyboard also has
an Out data through HID device Set Output Report request to achieve, HID device
class initialization processing code is as follows:
int init_hid(usb_device_t* device){
USBH_CfgDescTypeDef* cfg = (USBH_CfgDescTypeDef*)device->config_desc;
device->itf.handle = 0;
if(cfg->Itf_Desc[0].bInterfaceProtocol == HID_KEYBRD_BOOT_CODE){
device->itf.handle = kbd_handle;
}else if(cfg->Itf_Desc[0].bInterfaceProtocol == HID_MOUSE_BOOT_CODE){
device->itf.handle = mouse_handle;
device->itf.in_len = 4; // boot mouse report length is
}
// TODO: parse the interfce descriptor to get the endpoint descritptor
memcpy(cfg->Itf_Desc[0].Ep_Desc, (uint8_t*)cfg->Itf_Desc[0].Ep_Desc + 9,
14);
if(device->itf.handle){
if(open_ep(device) != 0){
102/109
goto hid_fail;
}
device->itf.deinit = close_ep;
if(SetBootProtocol(&device->ctrl_pipe) != 0){
goto hid_fail;
}
tusb_pipe_xfer_data(&device->itf.data_in,
device->itf.in_buf, device->itf.in_len);
}
return 0;
hid_fail:
return -1;
}
Set the processing function according to the protocol, parse the descriptor, and
obtain the endpoint descriptor. There is no analysis here, just draw the
endpoint directly
descriptor is copied to the front, skipping the HID descriptor. The formal
approach is to parse the descriptor and then open the device endpoint. this
It does not consider the situation where the device has multiple interfaces, nor
the situation where the number of endpoints is greater than 2. After
successfully opening the endpoint, send
Send a SetBootProtocol request to make the device work under the Boot protocol.
The data processing code for keyboard and mouse is also very simple. The code is
as follows:
void mouse_handle(usb_device_t* device){
static __IO uint32_t tick = 0;
channel_state_t state = tusb_pipe_wait(&device->itf.data_in, 0);
if(state == TUSB_CS_TRANSFER_COMPLETE) {
// get mouse data
if(device->itf.in_buf[0] & 1){
// left key down, toggle num lock
kbd_led ^= LED_NUM_LOCK;
}
if(device->itf.in_buf[0] & 2){
// right key down, toggle caps lock
kbd_led ^= LED_CAPS_LOCK;
}
tusb_pipe_xfer_data(&device->itf.data_in,
device->itf.in_buf, device->itf.in_len);
}else if(state != TUSB_CS_XFER_ONGOING){
tick++;
if(tick>2000){
tick = 0;
tusb_pipe_xfer_data(&device->itf.data_in,
device->itf.in_buf, device->itf.in_len);
}
}
}
void kbd_handle(usb_device_t* device){
static __IO uint32_t tick = 0;
103/109
channel_state_t state = tusb_pipe_wait(&device->itf.data_in, 0);
if(state == TUSB_CS_TRANSFER_COMPLETE) {
// get key board data
if(device->itf.in_buf[2] == KEY_NUM_LOCK){
// Num Lock
kbd_led ^= LED_NUM_LOCK;
}
if(device->itf.in_buf[2] == KEY_CAPS_LOCK){
// Caps lock
kbd_led ^= LED_CAPS_LOCK;
}
tusb_pipe_xfer_data(&device->itf.data_in,
device->itf.in_buf, device->itf.in_len);
}else if(state != TUSB_CS_XFER_ONGOING){
tick++;
if(tick>2000){
tick = 0;
tusb_pipe_xfer_data(&device->itf.data_in,
device->itf.in_buf, device->itf.in_len);
}
}
}
After the mouse receives valid data, it sets the button LED status according to
the left and right buttons. After the keyboard receives valid data, it will Set
the button LED status according to the first button value in the data. Both
keyboard and mouse have a tick count, here is to let the transfer wait
Restart after some time. The formal approach should be to determine the interval
for transmission initiation based on the value of bInterval in the endpoint
descriptor. separated. 8.2.3 HUB processing HUB is also a USB device. It has an
address, can transmit data, has a device descriptor, a configuration descriptor,
and HUB descriptor. HUB has an interrupt IN endpoint, which is used to notify
the host of port changes. The host reports according to HUB data, determine
which port has changed, and then perform operations on this port. The host needs
to communicate with the HUB through Device communication, the process is as
follows:
After the host completes HUB enumeration and setup, monitor HUB data. When the
port changes, the HUB will report to the host
104/109
Report data. The host determines which port has changed based on the data
content. Obtain the current status of the port through GetStatus request
state. If the port status is CONNECT, send a reset request to the specified port.
After the reset is completed, the port status becomes
ENABLE, then the enumeration process begins. The enumeration process with and
without HUB is the same. It should be noted that,
The HUB will report the speed information of the port. When enumerating and
transmitting data, the speed when the device is connected to the HUB must be
used.
After the device enumeration is successful, hang the device on the Child node of
the HUB device. The HUB processing code is as follows:
void hub_handle(usb_device_t* device){
channel_state_t state = tusb_pipe_wait(&device->itf.data_in, 0);
if(state == TUSB_CS_TRANSFER_COMPLETE){
uint8_t port_state = device->itf.in_buf[0];
uint8_t port = 0;
while(port_state){
// some port changed
if(port_state & 1){
// handle port change here
if (GetHUBPortStatus(&device->ctrl_pipe, port, &port_status,
sizeof(port_status)) == 0){
// Get port status success
if(port_status.wPortStatus.PORT_POWER) {
// port powered
uint8_t r = 0;
if(port_status.wPortChange.C_PORT_CONNECTION){
r += ClearHUBPort(&device->ctrl_pipe, port, HUB_FEATURE_SEL_C_PORT_CONNECTION);
}
if(port_status.wPortChange.C_PORT_ENABLE){
r += ClearHUBPort(&device->ctrl_pipe, port, HUB_FEATURE_SEL_C_PORT_ENABLE);
}
if(port_status.wPortChange.C_PORT_SUSPEND){
r += ClearHUBPort(&device->ctrl_pipe, port, HUB_FEATURE_SEL_C_PORT_SUSPEND);
}
if(port_status.wPortChange.C_PORT_OVER_CURRENT){
r += ClearHUBPort(&device->ctrl_pipe, port,
HUB_FEATURE_SEL_C_PORT_OVER_CURRENT);
}
if(port_status.wPortChange.C_PORT_RESET){
r += ClearHUBPort(&device->ctrl_pipe, port, HUB_FEATURE_SEL_C_PORT_RESET);
}
if(port_status.wPortStatus.PORT_CONNECTION){
if(port_status.wPortStatus.PORT_ENABLE){
// Enable means reset done
usb_device_t* dev = new_device();
if(dev){
dev->is_root = 0;
dev->is_low_speed = port_status.wPortStatus.PORT_LOW_SPEED;
if(enum_device(device->host, dev) == 0){
usb_device_t* child = device->children[port-1];
if(child){
105/109
free_device(child);
}
device->children[port-1] = dev;
}else{
free_device(dev);
}
}
}else{
// not enable, need a reset
r += SetHUBPort(&device->ctrl_pipe, port, HUB_FEATURE_SEL_PORT_RESET);
}
}else{
usb_device_t* child = device->children[port-1];
if(child){
free_device(child);
}
device->children[port-1] = 0;
}
}
}
}
port++;
port_state >>= 1;
}
}else if(state == TUSB_CS_STALL){
// TODO: clear interface feature
}
if(state != TUSB_CS_XFER_ONGOING){
tusb_pipe_xfer_data(&device->itf.data_in, device->itf.in_buf, device>itf.in_len);
}
for(int i=0;i<MAX_CHILD;i++){
usb_device_t* child = device->children[i];
if(child && child->itf.handle){
child->itf.handle(child);
}
}
}
By running this routine, you can directly connect the keyboard or mouse when
there is no HUB. When there is a HUB, you can connect it through the HUB.
Connect multiple keyboards and mice. In theory, HUB can also be connected to
HUB. However, there is no tracing in this sample code.
When descriptors are parsed, the reading frequency of some data may not meet the
device requirements. If there are too many devices, there will be compatibility
issues.
8.2.4 Keyboard and mouse data processing
This routine detects the left and right mouse button presses in the mouse
processing function, and updates the value of kbd_led if pressed.
In the keyboard processing function, detect whether the Caps Lock and NumLock of
the key are pressed, and update kbd_led if they are pressed.
106/109
value. In the main loop, if it is detected that the value of kbd_led has
changed, call the set_kbd_led function to set the
Set the device's LED light. The code of the set_kbd_led function is as follows:
void set_kbd_led(usb_device_t* cur_dev){
if(cur_dev){
USBH_CfgDescTypeDef* cfg = (USBH_CfgDescTypeDef*)cur_dev->config_desc;
if( cfg->Itf_Desc[0].bInterfaceClass == USB_HID_CLASS
&& cfg->Itf_Desc[0].bInterfaceProtocol == HID_KEYBRD_BOOT_CODE ){
//SetBootProtocol(&cur_dev->ctrl_pipe);
SetReport(&cur_dev->ctrl_pipe, &last_led, 1);
}
for(int i=0;i<MAX_CHILD;i++){
set_kbd_led(cur_dev->children[i]);
}
}
}
Each device in the sample code has its own control channel, which is also
wasteful. In actual use, you can apply for a pass as needed
Channel, release it after use, saving host channel resources. For the host, the
IN channel needs to listen for data from the device.
It needs to be valid all the time, and the OUT channel will only be used when
data is needed. You can apply again when needed. Do it more flexibly
The method is to make a channel pool, apply from the "pool" when needed, and
return it to the pool immediately after use.
107/109
9 Related Resources
9.1 STM32 chip
9.1.1 Correspondence between STM32 models and USB modules
STM32 USB modules are divided into two categories: FS and OTG. The FS module can
only be used as a USB slave and is configured in
M0 cores and M3 core chips in general. The OTG module can be used as both a host
and a slave. OTG is further divided into
There are two major categories: OTG_FS and OTG_HS. The operations of OTG_FS and
_OTG_HS are basically similar. OTG_HS supports DMA and
OTG_FS is not supported. The following table shows the USB module versions
supported by different chip series. Among them, the STM32F723 series
and STM32F730Z8 and STM32F730I8 with built-in high-speed USB PHY.
STM32 chip series
Kernel
STM32 chip USB module
USB_FS
OTG_FS
OTG_HS
STM32F07x/04x
M0
yes
no
no
STM32F103
M3
yes
no
no
STM32F105/107
M3
no
yes
no
STM32F2x5/2x7
M3
no
yes
yes
STM32F3xx
M4
yes
no
no
STM32F4x1/4x2/4x3
M4
no
yes
no
STM32F4x5/4x6/4x7/
4x9
M4
no
yes
yes
STM32F7xx
M7
no
yes
yes
STM32H7xx
M7
no
yes
yes
Table 33
Differences between different USB modules
USB_FS
OTG_FS
OTG_HS
interactive mode
dedicated memory
FIFO
FIFO
cache size
512-1024
1280 bytes
4096 bytes
USB slave device
support
support
support
USB host device
not support
support
support
PHY
built-in
built-in
Built-in/External
Number of endpoints 8
4-6 pieces
6-9 pcs
DMA
not support
support
not support
108/109
109/109
Download