cancel
Showing results for 
Search instead for 
Did you mean: 

ADC DMA Doesnt Read Properly [Need Help and Advice]

dios_kuri
Associate III

Hi, so previously on other post I've been getting trouble with Poll Conversion of ADC using STM32F4 Discovery Board. Therefore i decided to use DMA instead.

I made the program using STM32CUBEMX and Keil uVision to read 5 different channels. All data then transmitted via USART. 

The problem now is my array (uint16_t adcvalarr[5])  only got the 1st index filled with value. Shown below is the result of transmitted data from Watch window and printf debug window.  

Watch Window: 

watch1.JPG

Debug printf viewer (from left to right: iteration number, array[0], array[1], array[2], array[3], array[4])

dma array output.JPG

I have also tried using potentiometer to alter ADC values of each channel but none of it seems to change the value in the array either. Is my ADC fried up or is it just something wrong with my code?

My main.c code is too long so i'll try to put it in the comment instead

===================================================================================

Thanks in advance! Any kind of info/insight will help me greatly.

 

 

13 REPLIES 13
dios_kuri
Associate III

Here's some main parts of my main.c code: 

Transmit Data usingUSART

void transmit_data(double_t ii, double_t jj, double_t kk, double_t ll, double_t mm, uint16_t num)
{
sprintf(txData,"%d, %f , %f , %f , %f , %f \n", num, ii, jj, kk, ll, mm);
HAL_UART_Transmit(&huart3, (uint8_t *)txData, strlen(txData), 1000);
++n;
}

ARRAY to hold each CH ADC reading value

uint16_t adcvalarr[5]={0};
// This variable calculate the array length.
// In our case, array size in 5
int adc_channel_count = sizeof(adcvalarr)/sizeof(adcvalarr[0]);
// This flag will help to detect
// the DMA conversion completed or not
uint8_t adc_conv_complete_flag = 0;

 

main and while loop

int main(void)
{

/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
SystemClock_Config();

/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_ADC1_Init();
MX_I2C1_Init();
MX_I2S3_Init();
MX_SPI1_Init();
MX_USB_HOST_Init();
MX_TIM3_Init();
MX_USART3_UART_Init();
MX_TIM4_Init();
MX_DMA_Init();
MX_TIM9_Init();

/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start(&htim3);

// TIM3 CCR3 and CCR2 initialization
TIM3->CCR3 = 0;
TIM3->CCR2 = 0;

//BLINK LED AT STARTUP
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_13, 1);
HAL_Delay(300);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_13, GPIO_PIN_RESET);
HAL_Delay(1000);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_13, GPIO_PIN_SET);

HAL_Delay(500);
n=0;
int i=0;
/* USER CODE END 2 */

while (1)
{
MX_USB_HOST_Process();

// Initialize the DMA conversion
HAL_ADC_Start_DMA(&hadc1, (uint32_t *) adcvalarr , adc_channel_count);

// when adc_conv_complete_flag is set to 1,
// that means DMA conversion is completed
if(adc_conv_complete_flag == 1)
{
transmit_data(adcvalarr[0],adcvalarr[1],adcvalarr[2],adcvalarr[3],adcvalarr[4], n);
printf("%d, %d, %d, %d, %d, %d \n", n,adcvalarr[0],adcvalarr[1],adcvalarr[2],adcvalarr[3],adcvalarr[4]);

// adc_conv_complete_flag variable is set to 0, because,
// we alert this flag variable for new DMA conversion completion
adc_conv_complete_flag = 0;

//BLINK LED AFTER TRANSMITTING DATA
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, 1);
HAL_Delay(50);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_RESET);
HAL_Delay(100);
}
HAL_Delay(100);

} //end while
/* USER CODE END 3 */
}

 

The ADC initialization

static void MX_ADC1_Init(void)
{
ADC_ChannelConfTypeDef sConfig = {0};

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 = 5;
hadc1.Init.DMAContinuousRequests = ENABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}

sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_15CYCLES;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}

sConfig.Channel = ADC_CHANNEL_2;
sConfig.Rank = 2;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}

sConfig.Channel = ADC_CHANNEL_3;
sConfig.Rank = 3;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}

sConfig.Channel = ADC_CHANNEL_14;
sConfig.Rank = 4;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}

sConfig.Channel = ADC_CHANNEL_15;
sConfig.Rank = 5;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
}

DMA Setting

static void MX_DMA_Init(void)
{
__HAL_RCC_DMA2_CLK_ENABLE();
/* DMA interrupt init */
/* DMA2_Stream0_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
}

The Conversion Callback if ADC completed it

//TO CHECK IF ADC HAS COMPLETED
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
// I set adc_conv_complete_flag variable to 1 when,
// HAL_ADC_ConvCpltCallback function is call.
adc_conv_complete_flag = 1;
}

 

 

 

GwenoleB
ST Employee

Hello @dios_kuri,

First of all, make sure your DMA is configured as circular mode and memory address is incremented.

Secondly, it seems your ADC interrupt is triggered at the end of each single conversion. That means, you raised the flag adc_conv_complete_flag after the first conversion.

Let's try by setting ADC EOC selection as follow:

 hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;

 Best Regards,

Gwénolé

Hi @GwenoleB, thanks for your reply.

Sorry i forgot to mention that i already configured circular mode and increment memory address, here i provide a screenshot in case I'm not doing it right. I used Half Word length because i use 12bit ADC mode.

dios_kuri_0-1704376777137.png

I also have my ADCs Global Interrupt set to Enabled, although it doesn't do much when I change the ADC Global Interrupt setting. 

Here are my ADC Parameter settings

dios_kuri_1-1704377062320.png

 

Anyway, i tried setting the ADC's EOC as you suggested but the result remains the same...I also tried changing the code so the array get transmitted regardless of the adc_conv_complete_flag  condition, to no success either.

 

Read out and check/post content of ADC and relevant DMA registers.

JW

TDK
Guru

Does printf even support doubles here? I would print them as integers instead.

Debug code, see content in adc buffer.

If you feel a post has answered your question, please click "Accept as Solution".

Hello, @waclawek.jan

Thank you for the suggestion. I managed to dig deeper and found this thread that suggest to put DMA init first before ADC init in the void main. The array ends up filled thoroughly, but the reading is still messed up. The read ADC values didnt change when i turned the potentiometer to each pin. It's reading around 1300 when i ground the pin (fluctuatively of course), and my max voltage (when i crank up the potentio, the voltmeter reading is 2.7V with VDD 2.8V) reading just almost 3000 fluctuatively (which should be around 3813 ish). When i pressed reset, it still show messed up values but slightly different (900ish when grounded and 2900 ish on max turn potentiometer).

 

I am not sure what might caused this

 

 

Hello, @TDK 

Do you mean the printf to convert the values to adc buffer? it works just fine. Transmitted data from USART shows several number after the decimal point (works when i transmit my volt and current calculation). 

 

For update, I managed to dig deeper and found this thread that suggest to put DMA init first before ADC init in the void main. The array ends up filled thoroughly, but the reading is still messed up. The read ADC values didnt change when i turned the potentiometer to each pin. It's reading around 1300 when i ground the pin (fluctuatively of course), and my max voltage (when i crank up the potentio, the voltmeter reading is 2.7V with VDD 2.8V) reading just almost 3000 fluctuatively (which should be around 3813 ish). When i pressed reset, it still show messed up values but slightly different (900ish when grounded and 2900 ish on max turn potentiometer).

 

tl;dr printf double works fine for me. Managed to get the array filled thoroughly but the reading is still messed up.

Okay, so it's not printf.

Perhaps your source is high impedance. What resistance is the potentiometer? See if increasing sampling time makes a difference. Try shorting it directly to GND.

If you feel a post has answered your question, please click "Accept as Solution".

Hi @TDK. Thank you for keeping up. 

Increasing the sampling time to max available (144cycles) does improve its reading on higher voltage level (near the Vdd). When i short it with gnd, however, it still yields result around 800-900ish ADC value. Not quite good.. do you think something might be wrong with my ADC bus itself? 

Cheers.