How to use a Ticker callback to sample an AnalogIn (ADC) at a given sample rate?

I can’t seem to find an example which details the appropriate technique for sampling an ADC at a regular interval via the Ticker class.

I am trying to detect the frequency of a sine wave, and so far I have something like this:

void sampleCallback() {
    currVal = adc.read();
   // do something 
}

Ticker ticker;

int main () {
  ticker.attach_us(&sampleCallback, 125);
  while(1) {
    // handle stuff...
  }
}

I have read through the docs quite a bit, and it seems like reading an ADC is not “ISR” safe. I also read that it would be best to push the non-ISR safe code to another thread with a Semaphore?

I could really use a bit of guidance here. My program kinda works, but one of my pointers randomly gets changed every time I use this ticker class like this.

Hello Scott,

According to the Mbed documentation about Default timeouts the Mbed RTOS API has made the choice of defaulting to 0 timeout (no wait) for the producer methods, and osWaitForever (infinite wait) for the consumer methods.

A typical scenario for a producer could be a peripheral (e.g. a Ticker) triggering an interrupt to notify an event; in the corresponding interrupt service routine you cannot wait (this would deadlock the entire system).

To support many various targets the AnalogIn::read() member function calls an analog HAL function analogin_read(analogin_t *obj). The implementation of this function is target dependent. But in each case it:

  • configures the ADC channel
  • starts ADC conversion
  • waits for the conversion to be completed

Since these steps are quite time consuming the AnalogIn::read() function cannot be used as a producer (and called in ISR).

The fastest way to read an analog input is to utilize the hardware abstraction layer of the given target MCU. For example as in this project. This usually allows to:

  • configure the ADC channel only once and use it for all measurements
  • start ADC conversion only once and use a continuous conversion mode
  • utilize direct memory access to transfer the data from the ADC to the SRAM

Have a look also at this thread.

1 Like

I looked at your BarcodeReader project and am very curious how you went about integrating CubeMX generated code with MBED. I have always assumed MBED does a ton of initialization before the main() function gets called - so wouldn’t inserting CubeMX code really mess with the MBED initialization stuff? How do you know what HAL functions/methods to avoid calling?

Someone should make a tutorial on the topic of integrating CubeMX code with MBED!

Actually Mbed doesn’t perform too mutch initialization before calling the main function. The most important seems to be system clock configuration and Systick initialization. All Mbed API drivers are based/depend on that. That’s why when creating an STM32CubeIDE project with the aim to be imported into Mbed we have to match the same clock configuration as used by Mbed for the given target MCU. This allows as to remove the system clock configuration from the main function inserted by the STM32CubeIDE.

Initialization of Mbed API drivers isn’t done before calling the main function but in the associated constructor when a driver’s object is instantiated. That’s why we can remove also the HAL_Init() function from the main function inserted by STM32CubeIDE in order to reset all peripherals, initialize the Flash interface and the Systick.

Mbed does not prevent us from calling any HAL function. Actually, if we are familiar with the HAL of the given target MCU calling HAL functions directly can improve the program performance by striping away additional layers inserted by Mbed in order to make the related API target-independent.

thanks for the reply :+1:

This is good to know. Do you know where I can find the system clock configuration for MBED? Would it be in a cubeMX file somewhere? Not sure where to look in the repo for that.

It is nice to know that MBED only initializes via a constructor.

Knowing this, it makes it much easier to comprehend trying to implement some cube generated code into my program.

If I come up with a solution to my problem I’ll post it here

For the STM targets you can find it in the system_clock.c file. The actual path depends on the seleted target. For example, in case of the NUCLEO_F103RB the full path is as follows:

mbed-os/targets/TARGET_STM/TARGET_STM32F1/TARGET_STM32F103xB/system_clock.c

As I already said it’s important to match the same configuration in the STM32CubeIDE. This assures that the components implemented with STM32CubeIDE will work correctly also after moving the code to Mbed. The configuration in STM32CubeIDE is done using the graphical user interface:

  • Open the *.ioc file and in the Pinout & Configuration > Categories open the System Core > RCC page. Here you can select/check the source of the High Speed Clock (HSE) and Low Speed Clock (LSE).
  • Open the Clock Configuration tab. Here you should set the same configuration (Input frequency, PLL Source Mux, PLLM, PLLSAIM, …) as found in Mbed’s system_clock.c file. In the BarcodeReader you can find an example of Clock Configuration diagram matching the settings implemented by Mbed in the system_clock.c file for the STM32F103RB (see above the full path).

If you’d like to implement also some TIMER driven code in STM32CubeIDE then it’s important to avoid the TIMER used by Mbed for the us_ticker. Again, this is target dependent. For the STM32F103RB you can figure it out in the file

mbed-os/targets/TARGET_STM/TARGET_STM32F1/us_ticker_data.h

According to that, in case of the STM32F103RB, we can use in our code any TIMER except the TIM4.

1 Like

So I went ahead and coded up an TIM->ADC->DMA driver to work along side MBED, and it kind of works and it kind of doesn’t work.

I wrote up a StackExchange question to try and debug it, but I thought I would post it here as well.

I have a feeling it has something to do with the MBED RTOS, because the same driver works perfectly in a non-MBED environment (Pure CubeMX generated code).

Do you have any idea why I am having issues running this code alongside MBED?

Really stumped here.

  • I haven’t used DMA in Mbed OS 6 yet. (Apart of this case, but that was on an LPC1768).

  • As the issue could be caused by the RTOS, I would recommend to try the Mbed OS 6 bare metal profile (Mbed OS 6 without RTOS).

  • You can also try to add a "platform.callback-nontrivial": true entry to the mbed_app.json file.
    For example:

{
    "target_overrides": {
        "*": {
            "platform.callback-nontrivial": true
        }
    }
}

Yeah no luck - thanks for the suggestions though. The next thing I will try is a fresh bare metal mbed project. Maybe something in my application is causing the issue which I cannot see.