diff options
Diffstat (limited to 'docs/tutorials')
| -rw-r--r-- | docs/tutorials/first_device.rst | 147 | ||||
| -rw-r--r-- | docs/tutorials/first_host.rst | 160 | ||||
| -rw-r--r-- | docs/tutorials/getting_started.rst | 293 | ||||
| -rw-r--r-- | docs/tutorials/index.rst | 12 |
4 files changed, 612 insertions, 0 deletions
diff --git a/docs/tutorials/first_device.rst b/docs/tutorials/first_device.rst new file mode 100644 index 000000000..9fac2df49 --- /dev/null +++ b/docs/tutorials/first_device.rst @@ -0,0 +1,147 @@ +********************* +Your First USB Device +********************* + +This tutorial walks you through creating a simple USB CDC (serial) device using TinyUSB. By the end, you'll have a working USB device that appears as a serial port on your computer. + +Prerequisites +============= + +* Completed :doc:`getting_started` tutorial +* Development board with USB device capability (e.g., STM32F4 Discovery, Raspberry Pi Pico) +* Basic understanding of C programming + +Understanding USB Device Basics +=============================== + +A USB device needs three key components: + +1. **USB Descriptors**: Tell the host what kind of device this is +2. **Class Implementation**: Handle USB class-specific requests (CDC, HID, etc.) +3. **Application Logic**: Your main application code + +Step 1: Choose Your Starting Point +================================== + +We'll start with the ``cdc_msc`` example as it's the most commonly used and well-tested. + +.. code-block:: bash + + cd examples/device/cdc_msc + +This example implements both CDC (virtual serial port) and MSC (mass storage) classes. + +Step 2: Understand the Code Structure +===================================== + +Key files in the example: + +* ``main.c`` - Main application loop and board initialization +* ``usb_descriptors.c`` - USB device descriptors +* ``tusb_config.h`` - TinyUSB stack configuration + +**Main Loop Pattern**: + +.. code-block:: c + + int main(void) { + board_init(); + tusb_init(); + + while (1) { + tud_task(); // TinyUSB device task + cdc_task(); // Application-specific CDC handling + } + } + +**Device Task**: ``tud_task()`` must be called regularly to handle USB events and maintain the connection. + +Step 3: Build and Test +====================== + +.. code-block:: bash + + # Fetch dependencies for your board family + python ../../../tools/get_deps.py stm32f4 # Replace with your family + + # Build for your board + make BOARD=stm32f407disco all + + # Flash to device + make BOARD=stm32f407disco flash + +**Expected Result**: After flashing, connect the USB port to your computer. You should see: + +* A new serial port device (e.g., ``/dev/ttyACM0`` on Linux, ``COMx`` on Windows) +* A small mass storage device + +Step 4: Customize for Your Needs +================================= + +**Simplify to CDC-only**: + +1. In ``tusb_config.h``, disable MSC: + +.. code-block:: c + + #define CFG_TUD_MSC 0 // Disable Mass Storage + +2. Remove MSC-related code from ``main.c`` and ``usb_descriptors.c`` + +**Modify Device Information**: + +In ``usb_descriptors.c``: + +.. code-block:: c + + tusb_desc_device_t const desc_device = { + .idVendor = 0xCafe, // Your vendor ID + .idProduct = 0x4000, // Your product ID + .bcdDevice = 0x0100, // Device version + // ... other fields + }; + +**Add Application Logic**: + +In the CDC task function, add your serial communication logic: + +.. code-block:: c + + void cdc_task(void) { + if (tud_cdc_available()) { + uint8_t buf[64]; + uint32_t count = tud_cdc_read(buf, sizeof(buf)); + + // Echo back what was received + tud_cdc_write(buf, count); + tud_cdc_write_flush(); + } + } + +Common Issues and Solutions +=========================== + +**Device Not Recognized**: + +* Check USB cable (must support data, not just power) +* Verify descriptors are valid using ``LOG=2`` build option +* Ensure ``tud_task()`` is called regularly in main loop + +**Build Errors**: + +* Missing dependencies: Run ``python tools/get_deps.py FAMILY`` +* Wrong board name: Check ``hw/bsp/FAMILY/boards/`` for valid names +* Compiler issues: Install ``gcc-arm-none-eabi`` + +**Runtime Issues**: + +* Hard faults: Check stack size in linker script +* USB not working: Verify clock configuration and USB pin setup +* Serial data corruption: Ensure proper flow control in CDC implementation + +Next Steps +========== + +* Learn about other device classes in :doc:`../reference/usb_classes` +* Understand advanced integration in :doc:`../guides/integration` +* Explore TinyUSB architecture in :doc:`../explanation/architecture`
\ No newline at end of file diff --git a/docs/tutorials/first_host.rst b/docs/tutorials/first_host.rst new file mode 100644 index 000000000..5b406ad34 --- /dev/null +++ b/docs/tutorials/first_host.rst @@ -0,0 +1,160 @@ +****************** +Your First USB Host +****************** + +This tutorial guides you through creating a simple USB host application that can connect to and communicate with USB devices. + +Prerequisites +============= + +* Completed :doc:`getting_started` and :doc:`first_device` tutorials +* Development board with USB host capability (e.g., STM32F4 Discovery with USB-A connector) +* USB device to test with (USB drive, mouse, keyboard, or CDC device) + +Understanding USB Host Basics +============================= + +A USB host application needs: + +1. **Device Enumeration**: Detect and configure connected devices +2. **Class Drivers**: Handle communication with specific device types +3. **Application Logic**: Process data from/to the connected devices + +Step 1: Start with an Example +============================= + +Use the ``cdc_msc_hid`` host example: + +.. code-block:: bash + + cd examples/host/cdc_msc_hid + +This example can communicate with CDC (serial), MSC (storage), and HID (keyboard/mouse) devices. + +Step 2: Understand the Code Structure +===================================== + +Key components: + +* ``main.c`` - Main loop and device event handling +* Host callbacks - Functions called when devices connect/disconnect +* Class-specific handlers - Process data from different device types + +**Main Loop Pattern**: + +.. code-block:: c + + int main(void) { + board_init(); + tusb_init(); + + while (1) { + tuh_task(); // TinyUSB host task + // Handle connected devices + } + } + +**Connection Events**: TinyUSB calls your callbacks when devices connect: + +.. code-block:: c + + void tuh_mount_cb(uint8_t dev_addr) { + printf("Device connected, address = %d\\n", dev_addr); + } + + void tuh_umount_cb(uint8_t dev_addr) { + printf("Device disconnected, address = %d\\n", dev_addr); + } + +Step 3: Build and Test +====================== + +.. code-block:: bash + + # Fetch dependencies + python ../../../tools/get_deps.py stm32f4 + + # Build + make BOARD=stm32f407disco all + + # Flash + make BOARD=stm32f407disco flash + +**Testing**: Connect different USB devices and observe the output via serial console. + +Step 4: Handle Specific Device Types +==================================== + +**Mass Storage (USB Drive)**: + +.. code-block:: c + + void tuh_msc_mount_cb(uint8_t dev_addr) { + printf("USB Drive mounted\\n"); + // Read/write files + } + +**HID Devices (Keyboard/Mouse)**: + +.. code-block:: c + + void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, + uint8_t const* desc_report, uint16_t desc_len) { + uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance); + if (itf_protocol == HID_ITF_PROTOCOL_KEYBOARD) { + printf("Keyboard connected\\n"); + } + } + +**CDC Devices (Serial)**: + +.. code-block:: c + + void tuh_cdc_mount_cb(uint8_t idx) { + printf("CDC device mounted\\n"); + // Configure serial settings + tuh_cdc_set_baudrate(idx, 115200, NULL, 0); + } + +Common Issues and Solutions +=========================== + +**No Device Detection**: + +* Check power supply - host mode requires more power than device mode +* Verify USB connector wiring and type (USB-A for host vs USB micro/C for device) +* Enable logging with ``LOG=2`` to see enumeration process + +**Enumeration Failures**: + +* Some devices need more time - increase timeouts +* Check USB hub support if using a hub +* Verify device is USB 2.0 compatible (USB 3.0 devices should work in USB 2.0 mode) + +**Class Driver Issues**: + +* Not all devices follow standards perfectly - may need custom handling +* Check device descriptors with USB analyzer tools +* Some composite devices may not be fully supported + +Hardware Considerations +======================= + +**Power Requirements**: + +* Host mode typically requires external power or powered USB hub +* Check board documentation for power limitations +* Some boards need jumper changes to enable host power + +**Pin Configuration**: + +* Host and device modes often use different USB connectors/pins +* Verify board supports host mode on your chosen port +* Check if OTG (On-The-Go) configuration is needed + +Next Steps +========== + +* Learn about supported USB classes in :doc:`../reference/usb_classes` +* Understand advanced integration in :doc:`../guides/integration` +* Explore TinyUSB architecture in :doc:`../explanation/architecture`
\ No newline at end of file diff --git a/docs/tutorials/getting_started.rst b/docs/tutorials/getting_started.rst new file mode 100644 index 000000000..7853a9cc0 --- /dev/null +++ b/docs/tutorials/getting_started.rst @@ -0,0 +1,293 @@ +*************** +Getting Started +*************** + +This tutorial will guide you through setting up TinyUSB for your first project. We'll cover the basic integration steps and build your first example. + +Add TinyUSB to your project +--------------------------- + +To incorporate TinyUSB into your project: + +* Copy or ``git submodule`` this repository into your project in a subfolder. Let's say it is ``your_project/tinyusb`` +* Add all the ``.c`` files in the ``tinyusb/src`` folder to your project +* Add ``your_project/tinyusb/src`` to your include path. Also make sure your current include path contains the configuration file ``tusb_config.h``. +* Make sure all required macros are defined properly in ``tusb_config.h`` (the configuration file in demo applications is sufficient, but you need to add a few more such as ``CFG_TUSB_MCU``, ``CFG_TUSB_OS`` since they are passed by make/cmake to maintain a unique configuration for all boards). +* If you use the device stack, make sure you have created/modified USB descriptors for your own needs. Ultimately you need to implement all **tud descriptor** callbacks for the stack to work. +* Add a ``tusb_init(rhport, role)`` call to your reset initialization code. +* Call ``tusb_int_handler(rhport, in_isr)`` in your USB IRQ handler +* Implement all enabled classes' callbacks. +* If you don't use any RTOS at all, you need to continuously and/or periodically call the ``tud_task()``/``tuh_task()`` functions. All of the callbacks and functionality are handled and invoked within the call of that task runner. + +.. code-block:: c + + int main(void) { + tusb_rhport_init_t dev_init = { + .role = TUSB_ROLE_DEVICE, + .speed = TUSB_SPEED_AUTO + }; + tusb_init(0, &dev_init); // initialize device stack on roothub port 0 + + tusb_rhport_init_t host_init = { + .role = TUSB_ROLE_HOST, + .speed = TUSB_SPEED_AUTO + }; + tusb_init(1, &host_init); // initialize host stack on roothub port 1 + + while(1) { // the mainloop + your_application_code(); + tud_task(); // device task + tuh_task(); // host task + } + } + + void USB0_IRQHandler(void) { + tusb_int_handler(0, true); + } + + void USB1_IRQHandler(void) { + tusb_int_handler(1, true); + } + +Examples +-------- + +For your convenience, TinyUSB contains a handful of examples for both host and device with/without RTOS to quickly test the functionality as well as demonstrate how API should be used. Most examples will work on most of :doc:`the supported boards <boards>`. Firstly we need to ``git clone`` if not already + +.. code-block:: bash + + $ git clone https://github.com/hathach/tinyusb tinyusb + $ cd tinyusb + +Some ports will also require a port-specific SDK (e.g. RP2040) or binary (e.g. Sony Spresense) to build examples. They are out of scope for TinyUSB, you should download/install them first according to the manufacturer's guide. + +Dependencies +^^^^^^^^^^^^ + +The hardware code is located in the ``hw/bsp`` folder, and is organized by family/boards. For example, raspberry_pi_pico is located in ``hw/bsp/rp2040/boards/raspberry_pi_pico`` where ``FAMILY=rp2040`` and ``BOARD=raspberry_pi_pico``. Before building, we first need to download dependencies such as: MCU low-level peripheral drivers and external libraries like FreeRTOS (required by some examples). We can do this in either of two ways: + +1. Run the ``tools/get_deps.py {FAMILY}`` script to download all dependencies for a family as follows. Note: For TinyUSB developers to download all dependencies, use FAMILY=all. + +.. code-block:: bash + + $ python tools/get_deps.py rp2040 + +2. Or run the ``get-deps`` target in one of the example folders as follows. + +.. code-block:: bash + + $ cd examples/device/cdc_msc + $ make BOARD=feather_nrf52840_express get-deps + +You only need to do this once per family. Check out :doc:`complete list of dependencies and their designated path here <dependencies>` + +Build Examples +^^^^^^^^^^^^^^ + +Examples support make and cmake build systems for most MCUs, however some MCU families such as Espressif or RP2040 only support cmake. First change directory to an example folder. + +.. code-block:: bash + + $ cd examples/device/cdc_msc + +Then compile with make or cmake + +.. code-block:: bash + + $ # make + $ make BOARD=feather_nrf52840_express all + + $ # cmake + $ mkdir build && cd build + $ cmake -DBOARD=raspberry_pi_pico .. + $ make + +To list all available targets with cmake + +.. code-block:: bash + + $ cmake --build . --target help + +Note: some examples especially those that uses Vendor class (e.g webUSB) may requires udev permission on Linux (and/or macOS) to access usb device. It depends on your OS distro, typically copy ``99-tinyusb.rules`` and reload your udev is good to go + +.. code-block:: bash + + $ cp examples/device/99-tinyusb.rules /etc/udev/rules.d/ + $ sudo udevadm control --reload-rules && sudo udevadm trigger + +RootHub Port Selection +~~~~~~~~~~~~~~~~~~~~~~ + +If a board has several ports, one port is chosen by default in the individual board.mk file. Use option ``RHPORT_DEVICE=x`` or ``RHPORT_HOST=x`` To choose another port. For example to select the HS port of a STM32F746Disco board, use: + +.. code-block:: bash + + $ make BOARD=stm32f746disco RHPORT_DEVICE=1 all + + $ cmake -DBOARD=stm32f746disco -DRHPORT_DEVICE=1 .. + +Port Speed +~~~~~~~~~~ + +A MCU can support multiple operational speed. By default, the example build system will use the fastest supported on the board. Use option ``RHPORT_DEVICE_SPEED=OPT_MODE_FULL/HIGH_SPEED/`` or ``RHPORT_HOST_SPEED=OPT_MODE_FULL/HIGH_SPEED/`` e.g To force F723 operate at full instead of default high speed + +.. code-block:: bash + + $ make BOARD=stm32f746disco RHPORT_DEVICE_SPEED=OPT_MODE_FULL_SPEED all + + $ cmake -DBOARD=stm32f746disco -DRHPORT_DEVICE_SPEED=OPT_MODE_FULL_SPEED .. + +Size Analysis +~~~~~~~~~~~~~ + +First install `linkermap tool <https://github.com/hathach/linkermap>`_ then ``linkermap`` target can be used to analyze code size. You may want to compile with ``NO_LTO=1`` since ``-flto`` merges code across ``.o`` files and make it difficult to analyze. + +.. code-block:: bash + + $ make BOARD=feather_nrf52840_express NO_LTO=1 all linkermap + +Debug +^^^^^ + +To compile for debugging add ``DEBUG=1``\ , for example + +.. code-block:: bash + + $ make BOARD=feather_nrf52840_express DEBUG=1 all + + $ cmake -DBOARD=feather_nrf52840_express -DCMAKE_BUILD_TYPE=Debug .. + +Log +~~~ + +Should you have an issue running example and/or submitting an bug report. You could enable TinyUSB built-in debug logging with optional ``LOG=``. ``LOG=1`` will only print out error message, ``LOG=2`` print more information with on-going events. ``LOG=3`` or higher is not used yet. + +.. code-block:: bash + + $ make BOARD=feather_nrf52840_express LOG=2 all + + $ cmake -DBOARD=feather_nrf52840_express -DLOG=2 .. + +Logger +~~~~~~ + +By default log message is printed via on-board UART which is slow and take lots of CPU time comparing to USB speed. If your board support on-board/external debugger, it would be more efficient to use it for logging. There are 2 protocols: + + +* `LOGGER=rtt`: use `Segger RTT protocol <https://www.segger.com/products/debug-probes/j-link/technology/about-real-time-transfer/>`_ + + * Cons: requires jlink as the debugger. + * Pros: work with most if not all MCUs + * Software viewer is JLink RTT Viewer/Client/Logger which is bundled with JLink driver package. + +* ``LOGGER=swo`` : Use dedicated SWO pin of ARM Cortex SWD debug header. + + * Cons: only work with ARM Cortex MCUs minus M0 + * Pros: should be compatible with more debugger that support SWO. + * Software viewer should be provided along with your debugger driver. + +.. code-block:: bash + + $ make BOARD=feather_nrf52840_express LOG=2 LOGGER=rtt all + $ make BOARD=feather_nrf52840_express LOG=2 LOGGER=swo all + + $ cmake -DBOARD=feather_nrf52840_express -DLOG=2 -DLOGGER=rtt .. + $ cmake -DBOARD=feather_nrf52840_express -DLOG=2 -DLOGGER=swo .. + +Flash +^^^^^ + +``flash`` target will use the default on-board debugger (jlink/cmsisdap/stlink/dfu) to flash the binary, please install those support software in advance. Some board use bootloader/DFU via serial which is required to pass to make command + +.. code-block:: bash + + $ make BOARD=feather_nrf52840_express flash + $ make SERIAL=/dev/ttyACM0 BOARD=feather_nrf52840_express flash + +Since jlink/openocd can be used with most of the boards, there is also ``flash-jlink/openocd`` (make) and ``EXAMPLE-jlink/openocd`` target for your convenience. Note for stm32 board with stlink, you can use ``flash-stlink`` target as well. + +.. code-block:: bash + + $ make BOARD=feather_nrf52840_express flash-jlink + $ make BOARD=feather_nrf52840_express flash-openocd + + $ cmake --build . --target cdc_msc-jlink + $ cmake --build . --target cdc_msc-openocd + +Some board use uf2 bootloader for drag & drop in to mass storage device, uf2 can be generated with ``uf2`` target + +.. code-block:: bash + + $ make BOARD=feather_nrf52840_express all uf2 + + $ cmake --build . --target cdc_msc-uf2 + +IAR Support +^^^^^^^^^^^ + +Use project connection +~~~~~~~~~~~~~~~~~~~~~~ + +IAR Project Connection files are provided to import TinyUSB stack into your project. + +* A buildable project of your MCU need to be created in advance. + + * Take example of STM32F0: + + - You need ``stm32l0xx.h``, ``startup_stm32f0xx.s``, ``system_stm32f0xx.c``. + + - ``STM32L0xx_HAL_Driver`` is only needed to run examples, TinyUSB stack itself doesn't rely on MCU's SDKs. + +* Open ``Tools -> Configure Custom Argument Variables`` (Switch to ``Global`` tab if you want to do it for all your projects) + Click ``New Group ...``, name it to ``TUSB``, Click ``Add Variable ...``, name it to ``TUSB_DIR``, change it's value to the path of your TinyUSB stack, + for example ``C:\\tinyusb`` + +**Import stack only** + +Open ``Project -> Add project Connection ...``, click ``OK``, choose ``tinyusb\\tools\\iar_template.ipcf``. + +**Run examples** + +1. Run ``iar_gen.py`` to generate .ipcf files of examples: + + .. code-block:: + + > cd C:\tinyusb\tools + > python iar_gen.py + +2. Open ``Project -> Add project Connection ...``, click ``OK``, choose ``tinyusb\\examples\\(.ipcf of example)``. + For example ``C:\\tinyusb\\examples\\device\\cdc_msc\\iar_cdc_msc.ipcf`` + +Native CMake support +~~~~~~~~~~~~~~~~~~~~ + +With 9.50.1 release, IAR added experimental native CMake support (strangely not mentioned in public release note). Now it's possible to import CMakeLists.txt then build and debug as a normal project. + +Following these steps: + +1. Add IAR compiler binary path to system ``PATH`` environment variable, such as ``C:\Program Files\IAR Systems\Embedded Workbench 9.2\arm\bin``. +2. Create new project in IAR, in Tool chain dropdown menu, choose CMake for Arm then Import ``CMakeLists.txt`` from chosen example directory. +3. Set up board option in ``Option - CMake/CMSIS-TOOLBOX - CMake``, for example ``-DBOARD=stm32f439nucleo -DTOOLCHAIN=iar``, **Uncheck 'Override tools in env'**. +4. (For debug only) Choose correct CPU model in ``Option - General Options - Target``, to profit register and memory view. + +Common Issues and Solutions +--------------------------- + +**Build Errors** + +* **"arm-none-eabi-gcc: command not found"**: Install ARM GCC toolchain: ``sudo apt-get install gcc-arm-none-eabi`` +* **"Board 'X' not found"**: Check available boards in ``hw/bsp/FAMILY/boards/`` or run ``python tools/build.py -l`` +* **Missing dependencies**: Run ``python tools/get_deps.py FAMILY`` where FAMILY matches your board + +**Runtime Issues** + +* **Device not recognized**: Check USB descriptors implementation and ``tusb_config.h`` settings +* **Enumeration failure**: Enable logging with ``LOG=2`` and check for USB protocol errors +* **Hard faults/crashes**: Verify interrupt handler setup and stack size allocation + +Next Steps +---------- + +* Try the :doc:`first_device` tutorial to implement a simple USB device +* Read about :doc:`../guides/integration` for production projects +* Check :doc:`../reference/boards` for board-specific information diff --git a/docs/tutorials/index.rst b/docs/tutorials/index.rst new file mode 100644 index 000000000..6cf8f6ded --- /dev/null +++ b/docs/tutorials/index.rst @@ -0,0 +1,12 @@ +********* +Tutorials +********* + +Step-by-step learning guides for TinyUSB development. + +.. toctree:: + :maxdepth: 2 + + getting_started + first_device + first_host
\ No newline at end of file |
