Page 1 of 1
STM32G030 how to use DMA to read ADC faster?
Posted: Thu Feb 13, 2025 11:27 am
by dusan82
I need to read ADC faster than analogRead can, so I need to use DMA. Here is my code. It doesn't work it prints all zeros:
Code: Select all
#include "stm32g0xx_hal.h"
ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;
uint32_t buffer[50];
void init_adc_to_use_dma() {
// Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = ENABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 2;
hadc1.Init.DMAContinuousRequests = ENABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
if (HAL_ADC_Init(&hadc1) != HAL_OK) {
Serial.println("some error 1");
}
// Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = ADC_CHANNEL_16;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_12CYCLES_5; // stm32g0xx_hal_adc.h has all possible values
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) {
Serial.println("some error 2");
}
// dma?
__HAL_RCC_DMA1_CLK_ENABLE();
HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
delay(500);
HAL_ADC_Start_DMA(&hadc1,(uint32_t*) &buffer, 50);
Serial.println("done");
}
void setup() {
Serial.begin(115200);
delay(500);
init_adc_to_use_dma();
}
void loop() {
delay(1000);
for (int i = 0; i < 50; i++) {
Serial.print(i);
Serial.print(' ');
Serial.println(buffer[i]);
}
}
Any idea how to fix it? Thanks.
Re: STM32G030 how to use DMA to read ADC faster?
Posted: Thu Feb 13, 2025 11:55 am
by ag123
I think you may be missing out some codes that configure the DMA.
That need to be setup as well and linked to the ADC HAL configs.
Re: STM32G030 how to use DMA to read ADC faster?
Posted: Thu Feb 13, 2025 12:28 pm
by dusan82
Here is updated code but still all zeros:
Code: Select all
#include "stm32g0xx_hal.h"
ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;
uint32_t buffer[50];
void init_adc_to_use_dma() {
// Initialize the ADC
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = ENABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 2;
hadc1.Init.DMAContinuousRequests = ENABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
if (HAL_ADC_Init(&hadc1) != HAL_OK) {
Serial.println("some error 1");
}
// Configure ADC channel
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = ADC_CHANNEL_16;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_12CYCLES_5;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) {
Serial.println("some error 2");
}
// Initialize the DMA
__HAL_RCC_DMA1_CLK_ENABLE();
hdma_adc1.Instance = DMA1_Channel1;
hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
hdma_adc1.Init.Mode = DMA_CIRCULAR;
hdma_adc1.Init.Priority = DMA_PRIORITY_LOW;
if (HAL_DMA_Init(&hdma_adc1) != HAL_OK) {
Serial.println("some error 3");
}
__HAL_LINKDMA(&hadc1, DMA_Handle, hdma_adc1);
// Enable the DMA interrupt
HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
// Start ADC with DMA
delay(500);
if (HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&buffer, 50) != HAL_OK) {
Serial.println("some error 4");
}
Serial.println("done");
}
void setup() {
pinMode(PA0, INPUT);
Serial.begin(115200);
delay(500);
init_adc_to_use_dma();
}
void loop() {
delay(1000);
for (int i = 0; i < 50; i++) {
Serial.print(i);
Serial.print(' ');
Serial.println(buffer[i]);
}
}
void DMA1_Channel1_IRQHandler(void) {
Serial.println("DMA1_Channel1_IRQHandler");
HAL_DMA_IRQHandler(&hdma_adc1);
}
Re: STM32G030 how to use DMA to read ADC faster?
Posted: Thu Feb 13, 2025 6:02 pm
by ag123
not too sure,
ADC channel 16 seemed to be on PB12
https://www.st.com/resource/en/datashee ... g030c6.pdf
it may not be there on some (smaller) chips.
And you may need to configure the pins for analog input, e.g. try
you can also try to read the internal temperature sensor, I think it may be on ADC ch 12, check the ref manual. There is also the vref that you can read on a different channel.
https://www.st.com/resource/en/referenc ... ronics.pdf
and you also need to enable the temperature sensor to read it.
Re: STM32G030 how to use DMA to read ADC faster?
Posted: Thu Feb 13, 2025 6:43 pm
by fpiSTM
Add extern "C" before the handler and so not call Serial inside.
Code: Select all
extern "C" void DMA1_Channel1_IRQHandler(void) {
HAL_DMA_IRQHandler(&hdma_adc1);
}
Re: STM32G030 how to use DMA to read ADC faster?
Posted: Thu Feb 13, 2025 6:51 pm
by dusan82
- I added pinMode(*, INPUT_ANALOG) to all 16 adc pins just to be sure and set voltage there to approx 2V
- I added extern C
- still nothing, all zeroes
- if I comment out init_adc_to_use_dma(); and use analogRead(PA0) or any other pin it shows some value, but if I call init_adc_to_use_dma() even analogRead starts returning 0
Code: Select all
#include "stm32g0xx_hal.h"
ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;
uint32_t buffer[50];
void init_adc_to_use_dma() {
// Initialize the ADC
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = ENABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 2;
hadc1.Init.DMAContinuousRequests = ENABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
if (HAL_ADC_Init(&hadc1) != HAL_OK) {
Serial.println("some error 1");
}
// Configure ADC channel
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = ADC_CHANNEL_16;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_12CYCLES_5;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) {
Serial.println("some error 2");
}
// Initialize the DMA
__HAL_RCC_DMA1_CLK_ENABLE();
hdma_adc1.Instance = DMA1_Channel1;
hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
hdma_adc1.Init.Mode = DMA_CIRCULAR;
hdma_adc1.Init.Priority = DMA_PRIORITY_LOW;
if (HAL_DMA_Init(&hdma_adc1) != HAL_OK) {
Serial.println("some error 3");
}
__HAL_LINKDMA(&hadc1, DMA_Handle, hdma_adc1);
// Enable the DMA interrupt
HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
// Start ADC with DMA
delay(500);
if (HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&buffer, 50) != HAL_OK) {
Serial.println("some error 4");
}
Serial.println("done");
}
void setup() {
pinMode(PA0, INPUT);
Serial.begin(115200);
delay(500);
pinMode(PA0, INPUT_ANALOG);
pinMode(PA1, INPUT_ANALOG);
pinMode(PA2, INPUT_ANALOG);
pinMode(PA3, INPUT_ANALOG);
pinMode(PA4, INPUT_ANALOG);
pinMode(PA5, INPUT_ANALOG);
pinMode(PA6, INPUT_ANALOG);
pinMode(PA7, INPUT_ANALOG);
pinMode(PB0, INPUT_ANALOG);
pinMode(PB1, INPUT_ANALOG);
pinMode(PB2, INPUT_ANALOG);
pinMode(PB10, INPUT_ANALOG);
pinMode(PB11, INPUT_ANALOG);
pinMode(PB12, INPUT_ANALOG);
init_adc_to_use_dma();
}
void loop() {
delay(1000);
for (int i = 0; i < 50; i++) {
Serial.print(i);
Serial.print(' ');
Serial.println(buffer[i]);
}
}
extern "C" void DMA1_Channel1_IRQHandler(void) {
HAL_DMA_IRQHandler(&hdma_adc1);
}
Re: STM32G030 how to use DMA to read ADC faster?
Posted: Fri Feb 14, 2025 2:26 pm
by dusan82
I continued working on it, most examples on internet were for F103 so I switched to F103 and eventually made it work, here is code that reads 2000 samples at 400kHz on F103 using DMA. The HAL_ADC_ConvCpltCallback is not triggered so I just put delay(100) there. After pressing "enter" in serial console it captures 2000 samples and print them to output.
Code: Select all
// Read 12bit ADC samples from pin PA0 of STM32F103C8T6 (blue pill) at 400kHz using DMA
#include "stm32f1xx.h"
#include "stm32f1xx_hal_def.h"
// number of samples
#define BUF_SIZE 2000
uint16_t buffer[BUF_SIZE];
ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;
void MX_ADC1_Init(void) {
// Configure ADC1
hadc1.Instance = ADC1;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 1;
if (HAL_ADC_Init(&hadc1) != HAL_OK) {
Serial.println("HAL_ADC_Init() failed!");
return;
}
// Configure Regular Channel
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_13CYCLES_5;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK){
Serial.println("HAL_ADC_ConfigChannel() failed!");
return;
}
// Configure DMA for ADC1
__HAL_RCC_DMA1_CLK_ENABLE();
hdma_adc1.Instance = DMA1_Channel1;
hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_adc1.Init.Mode = DMA_NORMAL; //DMA_CIRCULAR;
hdma_adc1.Init.Priority = DMA_PRIORITY_LOW;
if (HAL_DMA_Init(&hdma_adc1) != HAL_OK) {
Serial.println("HAL_DMA_Init() failed!");
return;
}
// Link DMA to ADC
__HAL_LINKDMA(&hadc1, DMA_Handle, hdma_adc1);
}
extern "C" void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
// This should be called at the end of the conversion but it's not
// for now I know how long it takes to measure samples so I just wait
Serial.println("adc conversion completed");
if (hadc->Instance == ADC1) {
Serial.println("adc1 conversion completed");
}
}
void analogReadFastA0(uint16_t* buffer, int size) {
// read samples into buffer
// Stop ADC and DMA after reading the first sample
HAL_ADC_Stop_DMA(&hadc1);
// Clear the buffer
for (int i = 0; i < size; i++) {
buffer[i] = 0;
}
// Restart ADC and DMA for the next sample and entire buffer
if (HAL_ADC_Start_DMA(&hadc1, (uint32_t*)buffer, BUF_SIZE) != HAL_OK) {
Serial.println("HAL_ADC_Start_DMA() failed!");
return;
}
}
void setup() {
Serial.begin(115200);
pinMode(PA0, INPUT_ANALOG);
MX_ADC1_Init();
Serial.println("Press enter to read one block");
}
void loop() {
if (Serial.available()) {
int c = Serial.read();
if (c == 13) {
analogReadFastA0(&buffer[0], BUF_SIZE);
delay(100);
for (int i = 0; i < BUF_SIZE; i += 5) { // I'm writing only every 5th sample to fit serial plotter
Serial.println(buffer[i]);
}
}
}
}
Re: STM32G030 how to use DMA to read ADC faster?
Posted: Fri Feb 14, 2025 4:44 pm
by fpiSTM
Have you seen this:
viewtopic.php?t=110