element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • About Us
  • 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 Guard ADC peripheral access with a MUTEX for the Multi-core FreeRTOS SMP Project with the Raspberry Pi Pico
  • 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: 29 Sep 2022 8:18 PM Date Created
  • Views 2766 views
  • Likes 9 likes
  • Comments 3 comments
Related
Recommended
  • raspberry
  • pico_freertos_smp
  • pico
  • smp
  • freertos

Guard ADC peripheral access with a MUTEX for the Multi-core FreeRTOS SMP Project with the Raspberry Pi Pico

Jan Cumps
Jan Cumps
29 Sep 2022
Guard ADC peripheral access with a MUTEX for the Multi-core FreeRTOS SMP Project with the Raspberry Pi Pico

FreeRTOS Symmetric Multiprocessing (SMP) is a recent version of the RTOS that can schedule tasks across multiple controller cores. It's currently in test phase, and they have a version for the RP2040. In this blog post, I protect the ADC peripheral in a multi-threaded environment..
image
image source: keil
Prerequisite is that you are able to run the project from the previous post.

Mutex to reserve a shared resource

Certain CPU resources should not be tampered with, while you are using them. And you shouldn't touch them when another task is using them. Some examples are serial ports, ADC blocks, GPIO pins. You do not want that a task changes the baud rate or CS status of your SPI, while you are exchanging data in another task. In RTOSses, this is managed via semaphores.

A semaphore is like a token you can grab. If you have the token, you can use the resource. Then you return the token. In our case, we only want to give one token, so that only one task at the time can use our ADC. This single token style is called a binary semaphore. There is one (if the resource is free), or there isn't one (if some other task grabbed the token and is using the ADC). In FreeRTOS, a binary semaphore is called a MUTEX (mutual exclusive).  That's the one I'll be using here to reserve the ADC peripheral.

Define the mutex

Before you can use a mutex, you have to create it. I've done that in the main().

// ...
#include "semphr.h"

// handles
static SemaphoreHandle_t xADCMutex;

int main(void)
{
    /* Configure the hardware ready to run the demo. */
    prvSetupHardware();

    xTaskCreate(prvBlinkTask, "blink", configMINIMAL_STACK_SIZE, NULL, mainBLINK_TASK_PRIORITY, NULL);
    xTaskCreate(prvTemperatureTask, "temperature", configMINIMAL_STACK_SIZE, NULL, mainTEMPERATURE_TASK_PRIORITY, NULL);
    /* Create a mutex type semaphore. */
    xADCMutex = xSemaphoreCreateMutex();

    /* Start the tasks and timer running. */
    vTaskStartScheduler();
    
    // ...

Use the mutex

Then, in any code that changes the state, or relies on the state of the ADC, I try to grab the mutex first. That will only succeed if no one else uses the ADC. I 've defined a maximum wait (forever). My code will wait - block without taking CPU ticks - until the ADC is free. Another option is to give up after a timeout. Or to increase task priority each time the mutex grab failed, ...

static void prvTemperatureTask(void *pvParameters)
{
    (void)pvParameters;
    TickType_t xNextWakeTime;

    /* Initialise xNextWakeTime - this only needs to be done once. */
    xNextWakeTime = xTaskGetTickCount();

    /* Enable onboard temperature sensor */
    adc_set_temp_sensor_enabled(true);
    for (;;)
    {
        xSemaphoreTake(xADCMutex, portMAX_DELAY);

        // switch to the temperature mux if needed
        if (adc_get_selected_input() != 4)
        {
            adc_select_input(4);
        }
        float temperature = read_onboard_temperature(TEMPERATURE_UNITS);
        xSemaphoreGive(xADCMutex);
        
        printf("Onboard temperature = %.02f %c\n", temperature, TEMPERATURE_UNITS);
        xTaskDelayUntil(&xNextWakeTime, mainTEMPERATURE_TASK_FREQUENCY_MS);
    }
}

You can see that I try to make the surface between grabbing the mutex and giving it back, as small as possible. I use it just before setting the channel. And I return it after reading. That's the critical part of the execution where I want to be sure that the ADC settings aren't changed.

c++:

In object-oriented RTOSes, mutexes and semaphores are often objects that you just have to instantiate in the context where you need to have access. You don't need to call methods or clean up. Their constructor will try to grab the token, and their destructor will give it back, once you exit the context. 

In the current project, there is no competition from another task, so I can't prove that it works. But in the next blog I'll create a competitor for the ADC: a task that reads a voltage. We can then run the example with and without mutex protection, and experience the mayhem of not protecting a resource.

critical area:

FreeRTOS also has the concept of critical code areas. Those are parts of the code that should run uninterrupted (atomic).
Typically, all other processing, and interrupts, that can interfere with the critical part are suspended. That is OK for a very short piece of code. I don't think it is appropriate for logic that takes significant time, such as peripheral interactions. Semaphores fit that bill better.

Show All Blog Posts

  • Sign in to reply
  • Jan Cumps
    Jan Cumps over 2 years ago in reply to DAB

    > I am not sure sharing an ADC is a good idea unless you have a matrix of the inputs so you can select a separate channel each time.

    That is the case here.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • DAB
    DAB over 2 years ago

    It looks like a simple mutex implementation.

    I am not sure sharing an ADC is a good idea unless you have a matrix of the inputs so you can select a separate channel each time.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • shabaz
    shabaz over 2 years ago

    An example (maybe not realistic depending on the underlying behavior, but it's a nice simple way of representing), what happens if no semaphore is used : )

    Here the critical section of code is the print statement.

    image

    • 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