***************
Getting Started
***************

Add TinyUSB to your project
---------------------------

To incorporate tinyusb to your project

* Copy or ``git submodule`` this repo into your project in a subfolder. Let's say it is ``your_project/tinyusb``
* Add all the ``.c`` in the ``tinyusb/src`` folder to your project
* Add ``your_project/tinyusb/src`` to your include path. Also make sure your current include path also contains the configuration file ``tusb_config.h``.
* Make sure all required macros are all defined properly in ``tusb_config.h`` (configure file in demo application 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 configure for all boards).
* If you use the device stack, make sure you have created/modified usb descriptors for your own need. Ultimately you need to implement all **tud descriptor** callbacks for the stack to work.
* Add ``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's callbacks.
* If you don't use any RTOSes at all, you need to continuously and/or periodically call ``tud_task()``/``tuh_task()`` function. 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 it first according to its manufacturer guide.

Dependencies
^^^^^^^^^^^^

The hardware code is located in ``hw/bsp`` folder, and is organized by family/boards. e.g raspberry_pi_pico is located in ``hw/bsp/rp2040/boards/raspberry_pi_pico`` where ``FAMILY=rp2040`` and ``BOARD=raspberry_pi_pico``. Before building, we firstly need to download dependencies such as: MCU low-level peripheral driver and external libraries e.g FreeRTOS (required by some examples). We can do that by either ways:

1. Run ``tools/get_deps.py {FAMILY}`` script to download all dependencies for a family as follow. Note: For TinyUSB developer 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 folder as follow.

.. 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 system 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.
