Electronics > Microcontrollers
RP2040: how to use FreeRTOS with TinyUSB?
mblinov:
Hi all,
Quite basic question but the answer eludes me. How to sanely use FreeRTOS with the TinyUSB that is shipped with pico-sdk?
So pico-sdk contains TinyUSB as part of its distribution. If you clone pico-sdk, you will find it under pico-sdk\lib\tinyusb.
However, here's the catch: If we look in pico-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 that CFG_TUSB_OS is hardcoded to OPT_OS_PICO:
--- Code: --- target_compile_definitions(tinyusb_common_base INTERFACE
CFG_TUSB_MCU=OPT_MCU_RP2040
CFG_TUSB_OS=OPT_OS_PICO
#CFG_TUSB_DEBUG=${TINYUSB_DEBUG_LEVEL}
)
--- End code ---
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 be OPT_OS_FREERTOS in place of OPT_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 own tusb_config.h file, and override CFG_TUSB_OS in there instead. I can confirm that the compiler picks up my header file, because I see lots of warnings like
--- Code: ---warning: "CFG_TUSB_OS" redefined
46 | #define CFG_TUSB_OS OPT_OS_FREERTOS
|
--- End code ---
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 get unknown type name and implicit declaration of function errors, which suggests that some object files are being compiled with OPT_OS_PICO while others with OPT_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-custom pico-sdk together 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.
mblinov:
Ok, so actually its not quite as simple as just changing the pico-sdk\lib\tinyusb\hw\bsp\rp2040\family.cmake file. I still get the same unknown type name and implicit declaration of function errors, 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.obj
FAILED: [...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.
--- End code ---
Now, if I undo all my changes (that is, let CFG_TUSB_OS=OPT_OS_PICO) and look at who includes who, the following comes up:
If we select CFG_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.h
--- End code ---
However, if we select CFG_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.h
--- End code ---
We never did include critical_section.h, and as a consequence the build fails, since the stdio_usb.c file 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 using pico-sdk primitives, or whatever.
Infact the fix is trivial: We simply need to include critical_section.h directly from stdio_usb.c:
--- Code: ---diff --git a/src/rp2_common/pico_stdio_usb/stdio_usb.c b/src/rp2_common/pico_stdio_usb/stdio_usb.c
index 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
--- End code ---
dobsonr741:
Perhaps worthy of a pull request?
mblinov:
--- Quote from: dobsonr741 on March 12, 2023, 03:16:17 pm ---Perhaps worthy of a pull request?
--- End quote ---
To 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 the RP2040 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 call tud_task and board_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)
--- End code ---
And as explained in the manual above, linking against tinyusb_device and tinyusb_board actually 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 larger pico-sdk library. 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 called stdio_set_driver_enabled, which lets you provide a stdio_driver_t structure.
I then copied the files pico-sdk\src\rp2_common\pico_stdio_usb\stdio_usb.c and pico-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 like printf, only I can now set the manufacturer string, manage it from FreeRTOS, etc.
rapzak:
Hi,
Have you found any better/easy solution to this problem?
I am now in same boart...
/Kasper
Navigation
[0] Message Index
[#] Next page
Go to full version