Electronics > Microcontrollers

RP2040: how to use FreeRTOS with TinyUSB?

(1/2) > >>

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

There was an error while thanking
Thanking...
Go to full version
Powered by SMFPacks Advanced Attachments Uploader Mod