Faster analogread and floating point calculations on STM32?

I was working on a project with a nucelo f303k8 and trying to get audio in and out via analogread and analogout at 32khz via timer interrupt. I was using an array as a short delay line for an audio effect.
I suspected the 303 might not be able to keep up, but even removing a couple float calculations for filtering and lowering the interrupt to 100uS it would work for a minute and then eventually become unresponsive.

I did get it kind of working on a nucleo F401re, but had to use pwm out. The standard pwm library doesn’t really have the resolution for it though.
I got it competently working with a nucleo f446re.
This is just bare bones though and I’m concerned about moving forward so I did some testing using a 1,000,000 for loop running the same instruction over.

partial code:

        a = timer.read_us();
        for( int i = 0; i < 1000000; i++) {
            tempFloat = input;
        }
        c= timer.read_us();
        c= c - emptyLoopTime;
        tempFloat = (c-a) / 1000000.0f;
        printf("filter aRead uS %f", tempFloat);
        tempFloat = tempFloat *64;
        printf("1 aRead clock ticks %f", tempFloat);
        printf("1M aRead %d \n", c-a);

emptyLoopTime is from an empty for loop to subtract the loop overhead.

I found a single analog read on the 303 was taking about 680 clock cycles or 10.3 uS.
on the 401 it was about 453 cycles or 5.4 uS.
time difference betwee read_u16() and float was minimal.
AnalogOut took about 2 uS, but that’s totally manageable.
This float calculation took about the same amount of time. (float not double)

 lpfBuffer = lpfBuffer + ((input - lpfBuffer) * .45f);

(I also tried this on mbed studio and everything was significantly significantly slower)

I believe analogread is slow because mbed waits for the ADC to stabilize. I assume this is because there are a lot more ADC pins than internal ADCs and switching between them too quickly would cause poor read quality. Is this right?

I tried looking into DMA, but I think it’s over my head. I know there are libraries for other boards for faster analogread, but couldn’t find much info regarding STM32s.

I knew the ADC wouldn’t be super fast though. the float calculation was more surprising as I was hoping to add more math into the loops. I thought the FPU was enabled on both of these boards. Should I be doing my math differently?

Any suggestions or explanations would be appreciated.
Thanks

I wonder if different compiler optimization settings make any difference.

Also, I think you can configure the ADC clock and sample rate in the HAL file.

[quote]
I wonder if different compiler optimization settings make any difference.

Also, I think you can configure the ADC clock and sample rate in the HAL file. [/quote]

This is deep down somewhere in the mbed library, right? I thought that could not be edited in the online compiler.
I’ve not used mbed studio much. I use the 303k8 a lot and I don’t think it’s supported.

I think you can configure the ADC with this file. Not sure if you can do that with Online compiler. But I imagine Studio lets you do that. (I only use CLI.)

Maybe Online compiler uses the release profile and Studio uses the debug profile or something. I am pretty sure you can change the compiler optimization settings with Studio. You just modify json files and specify which one to use.

https://os.mbed.com/docs/mbed-os/v6.0/program-setup/build-profiles-and-rules.html

Here is an example of how optimization can make a difference.

Hello Brian,

Another way how to speed up analog read is to use STM’s HAL functions and DMA. For example, in the barcode reader program the analog read is carried out at about 428.5 kHz speed. To build the program I applied a method described here.

Best regards,

Zoltan

I’m pretty sure the mbed library is locked away with the online compiler. In older versions I think you could use mbed-dev.h (or something like that) but I tried that for something else a while back it didn’t work. I don’t think it’s supported any longer.

I get compilers aren’t all the same. I was under the impression that studio was supposed to be an offline version of mbed online. I only recently started using it and there are certainly more features. I’m still exploring them.

Thank you. I’ll check out the links later tonight.

After a deep dive in the STM HAL manual I did use it with timer1 on another project. I was curious about using DMA, but it’s not something I’ve tried before and didn’t know where to start. Thanks.

I was curious about using DMA, but it’s not something I’ve tried before and didn’t know where to start.

With the STM32CubeIDE it’s quite easy :

  • Open the .ioc file and on the Pinout & Configuration tab open Categories > Analog and select the ADC controller you’d like to use.

  • After selecting the input channel (e.g. IN0) you can configure it as needed (In this case to run in continuous mode.)

  • Select the DMA Settings tab and click on the Add button.

  • Select ADC1 for DMA Request.

  • Set up the DMA transfer direction from peripheral (ADC) to Memory (SRAM), automatic SRAM memory address increment after each transfer and data width.

  • On the NVIC Settings tab enable global interrupt for the DMA1 channel1.

  • Save the .ioc file and let STM32CubeIDE generate the code.

  • Open the main.c file and implement your code to start ADC conversion (e.g. on timer TIM3 Channel 1 tick event) and the code to be executed when the ADC conversion is finished/complete. For example:

...
uint16_t            data[DATA_LEN];
volatile uint8_t    adcDmaStarted = 0;
volatile uint8_t    adcDataAvailable = 0;
...
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef* htim)
{
    if ((htim->Instance == TIM3) && (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)) {
        if (!adcDmaStarted) {
            adcDmaStarted = 1;
            HAL_ADC_Start_DMA(&hadc1, (uint32_t*)data, DATA_LEN);
        }
    }
}

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
    adcDataAvailable = 1;
}
...
int main(void)
{
...
    while (1) {
        if (adcDataAvailable) {
#ifdef __MBED__
            printf("Data available:\r\n");
            for (int i = 3660; i < (3660 + 50); i++) {
                printf("data[%d] = %d\r\n", i, data[i]);
            }
#endif
            adcDataAvailable = 0;
            adcDmaStarted = 0;
        }

#ifdef __MBED__
        led1 = !led1;
        printf("blink\r\n");
        wait_ms(1000);
#endif
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    }
...
}

Best regards,

Zoltan