
Credits:
Optimistic / Inspirational by Mixaund | https://mixaund.bandcamp.com
Music promoted by https://www.free-stock-music.com
Credits:
Five stars: http://www.pngall.com/?p=39082
The definition of universal serial bus (USB) and the motivation behind USB development.
The history of USB development and USB versions.
The most common USB cables and connectors.
A quick look on the inner-structure of the specified cable in USB 2.0.
The main features of USB protocol regardless of the USB version.
Demonstration of the USB star topology and the meaning of USB function.
Explanation about the history of different power supply specifications for USB devices.
Explanation about the principle of smart chargers.
Explore how USB uses differential signaling with the D+ and D- lines to encode differential zero and one, focusing on full-speed devices and noise immunity.
Understand how differential J and K states form USB data, and how signals like SE0, detached, idle, and reset, suspend, and resume govern bus behavior across full-speed devices.
Explore timing tolerance in USB, including how breakpoints disrupt USB protocol, plus 0.25% per bus state, and why hardware handles timing rather than software.
Identify USB 2.0 speed by noting host pull-downs on D+ and D− data lines, and device pull-ups on D− for low speed or D+ for full/high speed.
Explore bit stuffing as the insertion of non-information bits to maintain clock sync on the USB bus, inserting a zero after six consecutive ones to prevent long idle states.
Learn how non-return-to-zero inverted (nrzi) encoding toggles the bus state on zeros and leaves ones unchanged, and how bit stuffing and hardware-managed nrzi encoding keep usb signaling in sync.
Discover how USB host controllers manage low-level bus operations and pull-downs and pull-ups, while software drivers support HCI, OHCI, EHCI, and xHCI interfaces across devices.
Explain how frames, marked by the start of frame signal every one millisecond in full-speed USB, allocate minimum portions to each device, balancing camera, keyboard, and mouse bandwidth.
Explore USB endpoints, their direction, in and out endpoints, and endpoint zero, with practical examples of sensor data streams and host–device configuration.
Here you can find a PDF file that contains the slides of some of the important upcoming lectures that you may need frequently to be opened next to you while watching the videos (e.g. to be able to compare the transaction types without switching between the videos).
Understand how USB transmits data in packets, with packet structure, endpoint-based size decisions, and signaling from sync to end, including bit stuffing and least significant bit first.
Explore USB behind the scenes by examining packet types and fields, including start of frame, token, data, and handshake packets, with PID, addresses, endpoints, CRC checks, and synchronization.
A usb transaction is a complete host-device data exchange, typically comprising a token packet, a data packet, and a handshake packet.
Understand USB packet identifiers (PIDs) and the PID field, outlining token, data, and handshake packets, their in and out directions, and how a PID value indicates packet type.
Explore token packets—in, out, and setup—that start every USB transaction. Learn how the host reads from or writes to the device and how setup begins control transfers on endpoint zero.
Transmit data through USB data packets by alternating data zero and data one packets, enabling error checking; payloads may be zero length, with zero length packets signaling specific meanings.
Handshake packets report the status of the previous data packet, with Ack, not acknowledged, and stall as three options; some endpoints, like endpoint zero, stall on not supported requests.
Learn how usb device addressing works as the host assigns a unique address during enumeration. Start with the default address zero and understand why reconnecting may change the device address.
Explore usb bus polling, where the physical layer samples a buffered data stream instead of continuous monitoring, and interrupt transfers are implemented by polling.
Explore how USB is host driven, where the host triggers all transactions with out and setup tokens, and the host requests device data using in tokens.
Usb is host driven; the host sends tokens and data to the device and requests data back with in tokens, while the device acknowledges or retries as needed.
Understand the four USB transfer types—interrupt, bulk, isochronous, and control—and how endpoints, including the control endpoint zero, perform transfers. The host learns endpoint configuration and descriptors during device enumeration.
Understand USB interrupt transfers: periodic, latency-guaranteed data using in/out token and handshake packets for mice, keyboards, and joysticks, with up to 64 bytes full-speed or 1024 bytes high-speed payloads.
Learn how bulk transfer operates as a non periodic, on-demand USB transfer that enables read/write to storage devices, guarantees error-free delivery with handshake, and uses host and device token exchanges.
Explore isochronous transfer, a periodic, guaranteed-bandwidth USB transfer with no handshaking, used for webcam and microphone streams; max payload is 1023 bytes full speed, 1024 high speed.
Explore how USB control transfer enables device enumeration and configuration through endpoint zero, detailing setup, data, and status stages and their handshaking for error-free transmission.
Explore usb control transfers in three stages—setup, data (optional), and status—where setup uses a setup token, data may include multiple in or out transactions, and status uses a zero-length packet.
Allocate bus bandwidth for each endpoint during enumeration to guarantee periodic transfers up to 90% for full speed and 80% for high speed, then control and bulk transfers.
Learn how to download, install, and set up stm32 cube ide on Windows, including license acceptance, extracting the installer, and selecting default options and debugging drivers.
Create a new stm32 cube ide project for the usb device targeting stm32 f429 discovery board, with executable project and writing your own hardware access in C (no cube libraries).
Learn how CMSIS header files map each register and bit, and SIMS macros simplify bare-metal STM32 firmware development.
Delete the auto-generated files syscalls.c and system.c from the source folder to keep the USB behind the scenes project simple, after reviewing their contents.
Master the art of logging with SWO to debug USB firmware, redirect printf output to SWO after soldering the bridge, override the underscore_write weak symbol, and build modular logging helpers.
Organize a logging helper for hid firmware by adding include and source helpers and implementing logger c and h with error, information, and debug levels, and set system_log_level in system_init.
Explore two ways to configure the system clock with an interactive STM32 Cube MK tool, then configure the clock by registers and build a custom hardware access layer.
Create a temporary stm32 cube mx project to explore clock configuration for usb device development, enable usb otg hs, select device only, and validate clocks with an mco output.
Configure the microcontroller clock to feed a fixed 48 MHz USB clock, using an 8 MHz external oscillator, HSE, PLL multipliers/dividers, and prescalers, with CubeMX guiding the calculations.
Derive a 72 MHz system clock and a 48 MHz USB clock from 8 MHz using M, N, Q, and P dividers, via the system clock multiplexer and AHB prescaler.
Explain how to configure MCO one source mux to output the PLL clock on PA8, routing USB and system clocks, and verify a 36 MHz signal on the MCO1 pin.
Configure the system clock in system init by including the stm32f4xx header and stdint, and implement a static configure clock function to set the 72 MHz core clock.
Use CMSIS bit operation macros to set, clear, and modify bits in registers. Include standard and ARM header files, then configure the system clock and verify by compiling.
Configure flash memory read latency as wait states before raising the CPU clock to 72 MHz, by modifying the flash ACR latency field using standard macros and datasheet guidance.
Use the fld2val macro to convert a value into a field with proper bit shifting and masking, then examine the first register modification and USB-related code glimpses.
Enable the high speed external clock (hse) by setting RCC_CR bit 16, then poll the ready bit until stable, using the set_bit and read_bit macros to manipulate RCC registers.
Enable the module via cr register, configure pll with pl cfg r using m, n, p, q prescalers, wait for the ready bit, then switch clock to pll.
Configure the APB prescaler to two by writing four to the clock configuration register, while HP prescaler and APB prescaler two stay at the default one; verify the clock switch.
Disable the high-speed internal oscillator to save power by clearing the HSC bit in the CR register after switching the system clock to use the HSC as clock source.
Fix two PLL configuration traps by recognizing a macro as a mask, choosing the correct shift method, and correcting enable-before-configuration per datasheet; verify clock frequency with an oscilloscope.
Flash the program to the microcontroller after updating the core clock to 72 MHz in debugging configuration, then enable logging to verify correct clocking and the infinite loop is stable.
Configure mco1 to output clock on pin a8 by selecting the clock source, setting prescaler to two, enabling gpio a, and setting pin a8 to alternate function with medium speed.
Verify the clock frequency with an oscilloscope, flash the program to the microcontroller, and validate a 36 MHz MCO clock via the system init and startup assembly.
Discover a three-layer USB firmware architecture—driver layer, framework (protocol-specific) layer, and application layer—balancing hardware access, host interactions, endpoints, and FIFO buffer management.
Create driver and framework header and source files, include stm32 f4 headers, and access usb register regions by offsetting the base address using sims structures.
Initialize usb pins by enabling the gpio b clock and configuring pins b14 and b15 to alternate function 12 for usb differential data, with b13 floating for bus sensing pin.
Apply practical tips to master usb firmware development by understanding usb basics, endpoints, and transfer modes, reading the whole usb chapter, and building the workflow from registers and interrupts.
Skim the USB chapter to identify key registers and configuration options, then implement USB core initialization by setting turnaround time, device speed, and unmasking device interrupts.
Initialize the USB core by enabling its clock, setting turnaround to nine, forcing device mode with the embedded PHY, selecting the embedded layer, choosing full-speed, and enabling Vbus sensing.
Unmask the usb core main interrupts from the global mask, clear pending interrupts via the interrupt status register, then write 0xff and finally unmask the usb global interrupt.
Use two firmware functions to connect or disconnect the USB device by setting the software disconnect bit in the device control register and toggling the power down bit for transceivers.
Initialize the USB device core and GPIO pins, connect to the bus, and test on a Raspberry Pi to show Linux recognizes a full-speed USB device.
Use a global interrupt status handler to read the USB core interrupts register, unmask interrupts during core init, route to the correct ISR, and clear flags by setting bits.
Handle the usb reset signal and distinguish it from other resets. Configure endpoint settings, data fifo memory, and setup packet caching for host communication.
Implement the USB reset handler in the driver layer, manage all endpoints including endpoint zero, define endpoint_count as six, and restore the device to its default USB state.
Configure endpoint zero in the driver layer, unmask in and out interrupts, and set max packet size. Activate endpoint zero and clear nak bit using inline functions for endpoint registers.
Implement a dedicated function to configure in endpoints, specifying the endpoint number, type (bulk, interrupt, isochronous, or control), max packet size, and data pid, with corresponding FIFOs setup.
Master the reconfiguration of USB endpoints by configuring in and out endpoints, masking interrupts, clearing flags, disabling ongoing transmissions, and reactivating endpoints while keeping endpoint zero always active.
Explore argument validation within the USB HID driver abstraction and learn to document code with doxygen, balancing hardware specific checks with forward progress in the driver layer.
Configure the usb fifo memory: assign a fifo per active endpoint, share the rcs fifo for outs, with start at zero and depth 10 setup locations and max packet size.
Implement three functions to configure fifo sizes, converting bytes to 32-bit words and auto-adjusting start offsets, with direct access for fifo zero and array access for others.
Configure the fifos while configuring endpoints, set endpoint zero fifo size to full-speed 64-byte packet size, align the fifo with endpoints, assign fifo numbers, and clear before writing to finish.
Demonstrate two new functions that flush the endpoint fifo via the usb reset register, using the fifo number and flush bit for precise cleanup.
Access and manipulate USB fifos by pushing data to the top and popping from the bottom, using an inline function to compute the per-endpoint fifo address.
Reconfigure all endpoints and their fifos after a USB reset, unmask transfer completion interrupts for in and out endpoints in the core initialization, and log the reset handling.
Handle the speed enumeration done interrupt by configuring endpoint zero for an eight-byte packet on a full-speed USB device, then clear the interrupt after logging and invoking the handler.
Implement the rx fifo not-empty interrupt handler by popping the status word, decoding endpoint and byte count, and routing setup versus out data packets.
Understand how setup and out transfer statuses from the fifo may appear without data, and how to re-enable endpoints after transfers in USB HID firmware.
Describe how to pop data from the RxFIFO in a USB HID firmware, using a shared FIFO, per-endpoint pointers, and 32-bit word reads with final-byte realignment to reconstruct packets.
Push data into a txfifo for an in endpoint by implementing the write packet function, configuring transfer size, packet count, and clearing stall before enabling transmission.
Resolve compilation errors by including the USB standards header, then reorder functions so definitions appear before use, enabling the project to compile successfully.
Define USB driver type as a structure of function pointers and expose a constant USB driver instance as the interface between the driver and framework layers, with internal functions static.
Implement the USB framework layer by polling the USB bus and firmware interrupt flags, calling the driver's global interrupt status handler in a tight loop to respond to bus changes.
Define USB events type in framework layer as a structure of function pointers to handle reset, setup data, out data, in transfer, and USB pulled, triggered by driver layer.
Define USB device states from default to addressed to configured, cover enumeration and set address, and note the optional suspended state; detach is not defined by the USB spec.
Define and differentiate USB control transfer stages—setup, data in/out, and status—and explain termination via zero-length data packets and data-in zero/idle transitions in firmware.
Define a usb_device structure in header to store device state, default address, endpoint zero control transfer stage, current configuration, and data buffers; declare a usb_events variable in the driver header.
Initialize the USB device by passing and storing a USB device pointer in a static global variable, then set an out buffer in main.c and compile.
Understand the usb reset handler during device enumeration, reset data size and configuration, set endpoint zero to setup stage, and initialize the device address to zero via the driver layer.
Implement set device address function in the USB driver, write the address to the device configuration register's address field, expose it via a pointer in the driver structure, and compile.
Define a USB events instance, assign the USB reset handler, and trigger on USB reset received to invoke the framework and driver handlers in this HID firmware workflow.
Implement the setup data received handler for endpoint zero, read the setup data from the fifo, and route it to a process request function to analyze host USB requests.
Explore the usb request structure used in setup transactions, detailing the eight-byte data packet and the fields bmRequestType, bRequest, wValue, wIndex, and wLength.
Explore USB standard device requests, focusing on get descriptor, set address, and set configuration. Understand how request type, b request, w value, w index, and w length govern data stage.
Define a USB request structure and masks to simplify parsing host requests, casting the buffer to the USB standard structure and exposing direction, type, and recipient fields.
Cast the incoming buffer to a USB request, route standard device requests via a dedicated handler, and start with get descriptor to access device descriptors.
investigate the host's first usb request by examining the get device descriptor transaction, showing an eight-byte setup with bm request 0x80, bRequest 6, descriptor type 1, length 64.
Define the USB standard device descriptor and other descriptor types, explain get descriptor requests, and show how host requests specify descriptor types using the value byte.
Define the usb device descriptor in a single header file and explain key fields such as bLength, bDescriptorType, usb version, device class per interface, and vendor/product IDs.
Define standard USB request macros in the USB standards header and handle get descriptor requests by extracting the descriptor type from the w value field.
Identify and fix a variable after case error in a switch by inserting a semicolon or wrapping the case in braces, then log the get descriptor request and recompile.
Write a packet only when the endpoint is empty to avoid overwriting data in USB control transfers. The lecture explains endpoint zero, host fetch, and multi-packet transfers.
Implement the function to process the control transfer stage and handle the USB pulled event to manage setup and data in stages, including get device descriptor, in a bus-polling loop.
Write data to endpoint zero in the in data stage, using the maximum packet size, then wait in the data-in idle sub stage until the host reads.
Define and attach the in transfer completed handler to USB events, check the in data buffer, and resume the in data stage to send the next packet to endpoint zero.
Learn to send the last USB control transfer packet by applying a min between remaining data and max packet size, and handle zero-length packets and data in zero sub stages.
Process the out-status stage by switching back to setup after the host signals completion with a zero-length packet; the physical layer auto-handles reception and data is queued in the fifo.
Assign the on out transfer completed handler to the USB event instance and call the process standard device request inside the process request function, then compile the project successfully.
Watch the first successful host–device USB communication, as the device replies to the get device descriptor and moves to the set address phase, verified in syslog and Wireshark.
Observe Wireshark capturing a get device descriptor request: the host asks 64 bytes, the device provides an 18-byte descriptor, and the endpoint zero max packet size drives a reset.
Set address requests are handled in the outer switch with no data stage; read W value, update device address via the driver, then switch to in status stage.
The lecture demonstrates handling the IN-status stage in USB control transfers by sending a zero-length packet to the host on endpoint zero and transitioning to the setup stage.
Demonstrate viewing a USB set address request and its immediate response in Wireshark, alongside capturing device descriptor traffic and debugging USB timeouts during firmware development.
View the full get device descriptor response and confirm the device descriptor contents, including the vendor ID, product ID, and version, after the set address exchange with the host.
View how a get configuration descriptor request appears in Wireshark, why the host receives no response yet, and how the configuration descriptor describes interfaces, endpoints, and power.
Understand how a configuration descriptor ties interfaces and endpoints. See how the host requests a configuration and the device replies with the configuration and its specific interface and endpoint descriptors.
Define a placeholder USB configuration descriptor and assemble a configuration descriptor combination for host requests. Build a structure that aggregates multiple descriptors for a single configuration.
Inline the USB configuration descriptor within the configuration combination, using constants for global structures and removing separate definitions to ensure the C code compiles.
Handle get configuration descriptor requests by sending the configuration descriptor plus attached descriptors, selecting the correct configuration by index, and switching to the data in stage for host transfer.
The host first requests the nine-byte configuration descriptor header, then uses its total length to learn the full size of the configuration descriptor combination and fetches the complete descriptor.
Process the USB set configuration request by recording the chosen configuration index, switch the device state from addressed to configured, and advance the control transfer to the status stage.
Define a use-case specific USB configuration function that configures endpoints according to the host's selected configuration descriptor, handles set configuration, and prepares the device for enumeration.
Explore usb device classes and how peripherals like mice interact with hosts, and implement a simple hid mouse firmware that moves the cursor periodically, with buttons and position sensor enhancements.
Write the USB configuration descriptor for a mouse HID device, defining the configuration, interfaces, and endpoints, calculating the total length, and setting attributes, max power, and wake capability.
Define the Usb endpoint descriptor for the mouse, selecting endpoint three as an in interrupt endpoint with a 64-byte max packet size and a 50-frame interval.
Add the USB ID descriptor for HID, create a dedicated ID standards header, link to an ID report descriptor, and clarify descriptor versus report descriptor to prepare testing.
The lecture explains HID report descriptors and the HID report structure by building a simple mouse report with X and Y deltas and three buttons.
Describe HID buttons by setting the usage page to button, defining min/max and logical ranges, and specifying report size, count, input as absolute, with padding for compatibility.
Describe the HID x and y axes on the generic desktop page, set logical min and max, and configure an 8-bit, single input report with relative delta values for axes.
Compact and optimize a HID report descriptor by grouping desktop mouse usages for x and y, increasing the count to two, and enclosing data in application and physical collections.
Diagnose usb behind the scenes firmware by packing usb descriptors to prevent alignment padding, fix nine-byte configuration descriptor length, and verify correct descriptor sets with Wireshark against the Linux host.
Learn to handle set idle and HID report descriptor requests in USB HID firmware after a set configuration, with a configuration descriptor including interface, endpoint, and ID descriptors.
Explore handling class-specific HID requests by implementing process class interface request for set idle, and transitioning the control transfer to status to acknowledge the host.
Implement handling of standard USB interface requests, including get report descriptor, to deliver HID report data via a control transfer and endpoint three.
Instantiate a mouse report, fill the x and y values, and write it to endpoint three after a successful transfer using the in transfer completed handler.
Burn this project into the microcontroller, debug on Linux, and verify the usb mouse is recognized and moves the cursor with a bare-metal usb driver.
Explore Windows-specific HID descriptor ordering in USB devices and fix cross-platform compatibility by placing the HID descriptor before the endpoint, guided by Linux debugging with syslog and Wireshark.
Have you ever wanted to develop your own device that can be connected to a computer using USB? Are you familiar with using USB <-> UART adapters but want to take your USB knowledge and understanding to the next ultimate level? If yes, then this course is absolutely for you!
I made the whole content of slides and code by myself after a lot of preparation and fine-tuning to give you the best experience you can find today online to learn and understand USB protocol and framework in theory and in practice.
Your journey with me in this course should save you from any frustration that could happen when you try to learn or understand USB from any other online source. USB is for most a mystery and a very complex protocol, and most engineers try to avoid it or at least try to convert it to other simpler protocols. However, after taking this course, you will be able to be a confident native USB engineer, and you will be able to develop your first bare-metal USB device with me without using any library, which will give you full control over the powers of USB. Even if you want in your career to use a USB library, taking this course will give you a full understanding of what is going on behind the scenes, and will allow you to be able to develop and fine-tune and USB device you want, because, after the end of this course, I would expect from you to have a full understanding of the concept of USB and how it works.
In this course, we will:
- Start with some theoretical information about USB.
- Understand the essential details of USB protocol.
- Take our time to understand how to configure the clock of any embedded system correctly.
- Implement a bare-metal USB firmware for ARM Cortex M4 based microcontroller (STM32F4xx family).
- Implement a bare-metal USB framework.
- Develop our own USB HID mouse from zero to fully functional!
Of course, the source code of the project we are going to develop together in this course will be available for you to download. You may use it as a template (fully or partially) for your projects in the future.
This course is in its early stages and some new additional content will be added or enhanced if necessary frequently. Nevertheless, the current content is full and sufficient to get a fully functional USB human interface device.
Happy engineering! See you inside the course!