element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
Raspberry Pi
  • Products
  • More
Raspberry Pi
Blog FreeRTOS Stream and Message Buffer on Raspberry Pico: specialised to handle interrupt-to-task and multi-core messages
  • Blog
  • Forum
  • Documents
  • Quiz
  • Events
  • Polls
  • Files
  • Members
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
Join Raspberry Pi to participate - click to join for free!
Featured Articles
Announcing Pi
Technical Specifications
Raspberry Pi FAQs
Win a Pi
GPIO Pinout
Raspberry Pi Wishlist
Comparison Chart
Quiz
  • Share
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: Jan Cumps
  • Date Created: 21 Sep 2024 6:13 PM Date Created
  • Views 1427 views
  • Likes 9 likes
  • Comments 10 comments
Related
Recommended
  • raspberry
  • pico
  • smp
  • freertos

FreeRTOS Stream and Message Buffer on Raspberry Pico: specialised to handle interrupt-to-task and multi-core messages

Jan Cumps
Jan Cumps
21 Sep 2024

The Stream (and Message) Buffer is one of the more recent additions to FreeRTOS. A construct that's useful in specific (yet commons) situations:

  • moving data from an interrupt (e.g.: UART TX interrupt) to an RTOS task for processing
  • in a multi-core setting (like my test here on Pico's dual cores) to send data from a task running on one core to a task on the other core.

quotes from FreeRTOS:

"Unlike most other FreeRTOS communications primitives, stream buffers are optimised for single reader single writer scenarios, such as passing data from an interrupt service routine to a task, or from one microcontroller core to another on a dual core CPU."

"basic and light weight core to core communication"

The goal of this post is to get a demo working on Raspberry Pico. On the 2 ARM cores. In a later post I 'll visit the details.

image
the two ARM Cortex cores in action

The Raspberry Pico RP2040 controller has 2 ARM cores. Most projects on the internet use one core, and let the other sleep.
FreeRTOS SMP allows you to run code on both cores in a scalable and simple way. 

This mechanism is lighter and has less concurrency guards compared to the other FreeRTOS mechanisms. That's on purpose. If you use it for what it is intended for, these guards are unnecessary overhead.

There's a good document that explains principles and use. And an example that has a lot of comments that explain the intrinsics. The example isn't part of the Pico port, but all sources are available. It took me some time to get it to build. I've attached my project at the end of this post.

Get a recent FreeRTOS on your development machine. Minimum version V10.0.0. You can get it by executing

git clone https://github.com/FreeRTOS/FreeRTOS.git --recursive

Then create an environment variable FREERTOS_KERNEL_PATH that point to the FreeRTOS/FreeRTOS/Source subdirectory. You can do that in your OS settings, or in your IDE.
In VSCode, I set it in the Extensions - > CMake -> CMake Tools -> Environment

image

In VSCode, to use the project, expand the attached zip file, and add the extracted dir to your workspace. For other IDEs, and from the command line, this will most likely work too, if you have CMake.
As long as you are able to build one of the Raspberry Pico examples, you will be able to build this one too, if you set that variable.

image

All necessary changes to the CMake and source files are part of my project. You don't need to change FreeRTOS code. You can build and compile (or use the .ul2 binary that's included in my zip, although that doesn't do anything you can see from the outside. You need a debugger to understand what's happening).

Project: bufferedqueue_20240921.zip

  • Sign in to reply
  • shabaz
    shabaz 9 months ago in reply to embeddedguy

    It's been known in such situations for an I2C device to go faulty, and hang a product which is trying to communicate with one of its chips, simply because it can't continue running any more until the I2C bus is fixed (even if thing that was being accessed was trivial; could be an I2C temperature sensor that went faulty, and you'd rather flash a warning LED, than completely hang the product.

    Mutex/semaphores are fundamental, they are the lowest-level building-block to build easier-to-use mechanisms on top of. 

    For larger apps it can be good to move the risk away, by not using them natively (which gives the coder the burden), and, instead, allowing support libraries, frameworks, etc., to handle that low-level, and instead provide higher-level services for your code, that doesn't carry as much risk.

    No-one is suggesting there's no place for those low-level methods; I think the point of the blog is to mention another method that you'd use where it makes sense (and it's a very safe and intuitive method, makes it attractive to developers since it encourages simpler tasks all safely communicating as required, without a risk of accidentally starving important tasks).

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • embeddedguy
    embeddedguy 9 months ago in reply to shabaz

    Yes, both mutex and semaphores are quite low level and actually it makes sense. Imagine a situation where one is implementing hardware driver for I2C bus, there a task that is doing transaction need to run for a time when transmission is happening. Then only it can give back the control to other tasks. In such situation it is very important not to switch task until something really urgent needs to be executed like watchdog timer or low power related interrupts.,

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • shabaz
    shabaz 9 months ago in reply to Jan Cumps

    It's very neat. And this sort of thing wasn't all that easy with embedded systems in the past, when OS's only offered more basic mechanisms. 

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • Jan Cumps
    Jan Cumps 9 months ago in reply to shabaz

    I've been playing around with Python, trying to do a similar thing, with tasks I needed being: (1) receiving data, (2) processing it, and (3) sending the results back out. That's three tasks that can be mentally placed in a line.

    I used as many "stream-like" connections as I could (in my case, I used queues), i.e., that would be two in this situation, to connect each task to the next in the chain. This is simple to visualize; everything is decoupled, and each task can be small and easy to maintain if the application grows in functionality.

    That is exactly what these FreeRTOS stream buffers are made for. In your case, you'd have two of them, one from (1) to (2), the second from (2) to (3)

    The receiving jobs don't use CPU, and don't poll to see if there is data available. They are inactive. They get activated by the queue and scheduler, once there is data ready for them to pick up.

    xStreamBufferReceive( pxStreamBuffers->xEchoServerBuffer, ( void * ) pcStringReceived, xSendLength, portMAX_DELAY );

    Because there is only one job writing, and one job reading - and there is that decoupling stream buffer between them - there is no need to mutex / semaphore / protect / block.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • shabaz
    shabaz 9 months ago in reply to embeddedguy

    Probably the best way to see it is that mutex or semaphores are quite low-level and need caution when used since the onus is on the coders to still handle the data, and those synchronization methods are just a hammer-like tool to help in the implementation.

    The difficulty remains with the developer in correctly identifying all their critical sections and not messing that up. There are enough ugly situations (like other tasks being starved of CPU all because of one task consuming the locked resource and not releasing it) that then the pre-burned developer will try to minimize usage, leading to more elaborate code just to avoid use, and that becomes hard to work with too.

    Generally with more higher-level mechanisms, the hammer usage is left more to the OS (or whatever library or middleware is implementing the mechanism, since sometimes it's not part of the OS) to use when needed, so the developer is a lot more free to write simpler code, and liberally use the mechanisms everywhere.

    I've been playing around with Python, trying to do a similar thing, with tasks I needed being: (1) receiving data, (2) processing it, and (3) sending the results back out. That's three tasks that can be mentally placed in a line.

    I used as many "stream-like" connections as I could (in my case, I used queues), i.e., that would be two in this situation, to connect each task to the next in the chain. This is simple to visualize; everything is decoupled, and each task can be small and easy to maintain if the application grows in functionality.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
>
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube