RP2040: how to use FreeRTOS with TinyUSB? - Page 1
Forum section: electronics / general

author: mblinov
author_rank: Contributor
timestamp: March 12, 2023, 12:01:00 pm
content: Hi all,Quite basic question but the answer eludes me. How to sanely use FreeRTOS with the TinyUSB that is shipped withpico-sdk?Sopico-sdkcontains TinyUSB as part of its distribution. If you clonepico-sdk, you will find it underpico-sdk\lib\tinyusb.However, here's the catch: If we look inpico-sdk\lib\tinyusb\hw\bsp\rp2040\family.cmake(note it is a git submodule), which is the file that specifies compilation options for the RP2040 MCU, we find thatCFG_TUSB_OSis hardcoded toOPT_OS_PICO:Code:target_compile_definitions(tinyusb_common_base INTERFACECFG_TUSB_MCU=OPT_MCU_RP2040CFG_TUSB_OS=OPT_OS_PICO#CFG_TUSB_DEBUG=${TINYUSB_DEBUG_LEVEL})Which is fine for all normal purposes. However, I'd like to use FreeRTOS in my app, and consequently (atleast, it would seem to me) that I need to set this to beOPT_OS_FREERTOSin place ofOPT_OS_PICO. Unfortunately, it does not seem to be a configurable option (for the time being I can ofcourse change it directly in the sdk source code.)Now what I *could* do instead is supply my owntusb_config.hfile, and overrideCFG_TUSB_OSin there instead. I can confirm that the compiler picks up my header file, because I see lots of warnings likeCode:warning: "CFG_TUSB_OS" redefined46 | #define CFG_TUSB_OS OPT_OS_FREERTOS|Which is "good" since it implies it's picking up my option (but also not good because it's clearly not the right way to go about it!)However the build fails, I think because it picks it up my header file "too late" so to speak. I getunknown type nameandimplicit declaration of functionerrors, which suggests that some object files are being compiled withOPT_OS_PICOwhile others withOPT_OS_FREERTOS, and consequently some C files are trying to use symbols which haven't been compiled.I want to know, what is the "correct" way of doing what I'm trying to do? Obviously its trivial to modify the SDK directly, but I really don't want to drag a slightly-custompico-sdktogether with my project.I seem to have some kind of faint memory of seeing FreeRTOS <-> "Pico OS" interop, atleast as far as the various synchronization primitives go, which would mean that what I'm trying to do is completely needless. But I can't remember where I got this impression from.

author: mblinov
author_rank: Contributor
timestamp: March 12, 2023, 12:42:41 pm
content: Ok, so actually its not quite as simple as just changing thepico-sdk\lib\tinyusb\hw\bsp\rp2040\family.cmakefile. I still get the sameunknown type nameandimplicit declaration of functionerrors, but they are *not* due to some hand-wavy inclusion error.I neglected to post these errors in the OP, here they are below:Code:[4/5] Building C object [...long path...]/pico-sdk/src/rp2_common/pico_stdio_usb/stdio_usb.c.objFAILED: [...long path...]/pico-sdk/src/rp2_common/pico_stdio_usb/stdio_usb.c.obj[...trimmed content...][...long path...]/pico-sdk/src/rp2_common/pico_stdio_usb/stdio_usb.c:38:8: error: unknown type name 'critical_section_t'38 | static critical_section_t one_shot_timer_crit_sec;| ^~~~~~~~~~~~~~~~~~[...long path...]/pico-sdk/src/rp2_common/pico_stdio_usb/stdio_usb.c: In function 'timer_task':[...long path...]/pico-sdk/src/rp2_common/pico_stdio_usb/stdio_usb.c:49:9: warning: implicit declaration of function 'critical_section_is_initialized' [-Wimplicit-function-declaration]49 | if (critical_section_is_initialized(&one_shot_timer_crit_sec)) {| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~[...long path...]/pico-sdk/src/rp2_common/pico_stdio_usb/stdio_usb.c:50:9: warning: implicit declaration of function 'critical_section_enter_blocking' [-Wimplicit-function-declaration]50 | critical_section_enter_blocking(&one_shot_timer_crit_sec);| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~[...long path...]/pico-sdk/src/rp2_common/pico_stdio_usb/stdio_usb.c:52:9: warning: implicit declaration of function 'critical_section_exit' [-Wimplicit-function-declaration]52 | critical_section_exit(&one_shot_timer_crit_sec);| ^~~~~~~~~~~~~~~~~~~~~[...long path...]/pico-sdk/src/rp2_common/pico_stdio_usb/stdio_usb.c: In function 'stdio_usb_init':[...long path...]/pico-sdk/src/rp2_common/pico_stdio_usb/stdio_usb.c:213:9: warning: implicit declaration of function 'critical_section_init_with_lock_num' [-Wimplicit-function-declaration]213 | critical_section_init_with_lock_num(&one_shot_timer_crit_sec, next_striped_spin_lock_num());| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ninja: build stopped: subcommand failed.Now, if I undo all my changes (that is, letCFG_TUSB_OS=OPT_OS_PICO) and look at who includes who, the following comes up:If we selectCFG_TUSB_OS=OPT_OS_PICO(the default), then the following inclusion chain occurs:Code:pico-sdk\src\rp2_common\pico_stdio_usb\stdio_usb.c:- pico-sdk\lib\tinyusb\src\tusb.h- pico-sdk\lib\tinyusb\src\osal\osal.h(here it is decided who to include based on OS)- pico-sdk\lib\tinyusb\src\osal\osal_pico.h- pico-sdk\src\common\pico_sync\include\pico\critical_section.h(Here, `struct critical_section` is defined (among others.))- pico-sdk\src\rp2_common\pico_stdio_usb\include\pico\stdio_usb.hHowever, if we selectCFG_TUSB_OS=OPT_OS_FREERTOS, then the following inclusion chain occurs:Code:pico-sdk\src\rp2_common\pico_stdio_usb\stdio_usb.c:- pico-sdk\lib\tinyusb\src\tusb.h- pico-sdk\lib\tinyusb\src\osal\osal.h(here it is decided who to include based on OS)- pico-sdk\lib\tinyusb\src\osal\osal_freertos.h- pico-sdk\src\rp2_common\pico_stdio_usb\include\pico\stdio_usb.hWe never did includecritical_section.h, and as a consequence the build fails, since thestdio_usb.cfile is implemented using those primitives.I'm inclined to suggest that this is an oversight: The printing logic (stdio_usb.c) is a *consumer* of the TinyUSB library, not an integral part of it. By that logic, it should not depend *at all* on whether TinyUSB itself is targetting FreeRTOS, or usingpico-sdkprimitives, or whatever.Infact the fix is trivial: We simply need to includecritical_section.hdirectly fromstdio_usb.c:Code:diff --git a/src/rp2_common/pico_stdio_usb/stdio_usb.c b/src/rp2_common/pico_stdio_usb/stdio_usb.cindex 32f1f33..c91af99 100644--- a/src/rp2_common/pico_stdio_usb/stdio_usb.c+++ b/src/rp2_common/pico_stdio_usb/stdio_usb.c@@ -16,6 +16,7 @@#include "pico/time.h"#include "pico/stdio/driver.h"#include "pico/mutex.h"+#include "pico/critical_section.h"#include "hardware/irq.h"#include "device/usbd_pvt.h" // for usbd_defer_func

author: dobsonr741
author_rank: Frequent Contributor
timestamp: March 12, 2023, 03:16:17 pm
content: Perhaps worthy of a pull request?

author: mblinov
author_rank: Contributor
timestamp: March 12, 2023, 06:08:27 pm
content: Quote from: dobsonr741 on March 12, 2023, 03:16:17 pmTo be honest I'm still not sure if what I'm trying to do isn't completely insane.I just came across the following text in theRP2040 SDK manual:In any case: Continuing from my previous post... Unfortunately I don't remember the details anymore, I think what bit me was that if you want to calltud_taskandboard_init, you need to add to your CMake project the TinyUSB link targets, e.g.:Code:target_link_libraries(${CMAKE_PROJECT_NAME} pico_stdlib tinyusb_device tinyusb_board FreeRTOS-Kernel FreeRTOS-Kernel-Heap3 pico_unique_id)And as explained in the manual above, linking againsttinyusb_deviceandtinyusb_boardactually has a consequence on codegen: There is code in the pico-sdk library that detects you're explicitly linking against TinyUSB, and will turn off the stdio-USB printing logic (or some of it.)Basically there is really quite intricate interplay between printf, TinyUSB, and the largerpico-sdklibrary. Consequently if you want to do anything that isn't absolutely out-the-box standard printf-over-USB, it seems best to create a completely custom TinyUSB endpoint from scratch, and use that.You're not completely left out in the cold though, as the stdio subsystem provides a function calledstdio_set_driver_enabled, which lets you provide astdio_driver_tstructure.I then copied the filespico-sdk\src\rp2_common\pico_stdio_usb\stdio_usb.candpico-sdk\src\rp2_common\pico_stdio_usb\stdio_usb_descriptors.c, modified slightly to work standalone, and had essentially a CDC endpoint that behaved exactly likeprintf, only I can now set the manufacturer string, manage it from FreeRTOS, etc.

author: rapzak
author_rank: Newbie
timestamp: March 24, 2023, 07:47:46 pm
content: Hi,Have you found any better/easy solution to this problem?I am now in same boart.../Kasper

author: mblinov
author_rank: Contributor
timestamp: March 25, 2023, 04:41:15 pm
content: Unfortunately not. I did open a discussion in theTinyUSB github repo, so there is some upstream visibility into this problem.

author: rapzak
author_rank: Newbie
timestamp: March 26, 2023, 09:37:36 pm
content: Hi,Thanks for the update.I think the Pico SDK could have and option for this input in the cmake file like these:# create map/bin/hex file etc.pico_add_extra_outputs(${NAME})# enable usb output, disable uart outputpico_enable_stdio_usb(${NAME} 0)pico_enable_stdio_uart(${NAME} 0)/KasperQuote from: mblinov on March 25, 2023, 04:41:15 pm

author: rapzak
author_rank: Newbie
timestamp: March 26, 2023, 09:41:53 pm
content: https://github.com/raspberrypi/pico-sdk/issues/1326

author: westfw
author_rank: Super Contributor
timestamp: March 27, 2023, 05:30:26 am
content: I would have thought that "tinyUSB" was a sort of "full stack" USB implementation designed for systems that don't use an RTOS, so that if you want to use FreeRTOS on an RP2040, you'd have to find a FreeRTOS USB stack. Probably that would rely on lower-level USB hardware drivers, and use the FreeRTOS scheduling primitives for the more complicated levels, instead of whatever mechanisms TinyUB uses to implement multiple IO streams in a taskless environment. (perhaps not. It seems that FreeRTOS is lower level and doesn't have a standardized USB abstraction.)

author: SiliconWizard
author_rank: Super Contributor
timestamp: March 27, 2023, 06:31:15 pm
content: Quote from: westfw on March 27, 2023, 05:30:26 amTinyUSB supports no OS, FreeRTOS, RT-Thread and Mynewt. The best bet is not to think, but to look at the project.https://github.com/hathach/tinyusbThe problem here is that TinyUSB's FreeRTOS support for the RP2040 is not quite there yet. It's not a limitation of TinyUSB, just something that is not fully ready yet.