AnsweredAssumed Answered

Let's Decode some ADC+DMA output

Question asked by Troyer.Tyler on Jan 8, 2016
Latest reply on Jan 12, 2016 by Troyer.Tyler
Hey all, I'm using the F446RE nucleo board on a Win7 machine using Keil 5.06. Nice software. I'm using the part @ 180 MHz on the 1.6.1 Std. Periph Lib.

I'm going off of clive's numerous postings of the basic ADC + DMA stream example. Things seems to run well enough, but the output is not what makes sense. The goal here is to sample 1 channel at 200 Hz, and do ping pong processing on 100 samples every half second. The timer triggering for this seems to work quite well.

Basically, in 12b resolution, the output found in the output buffer is always "30"+- 2. This does not change with the voltage I present on the actual input pin. Changing to another pin/channel combo presents identical results.

Changing resolution to 10 bit fills the buffer with 7+-1, and 1 for 8 bit resolution. This makes me think I'm reading a real voltage, but not where I think I'm measuring it.

As a sanity check, I ran this simple example, which work quite well!

int ConvertedValue = 0; //Converted value readed from ADC
uint32_t i;
 
 
void adc_configure(void){
 ADC_InitTypeDef ADC_init_structure; //Structure for adc confguration
 GPIO_InitTypeDef GPIO_initStructre; //Structure for analog input pin
 //Clock configuration
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);//The ADC1 is connected the APB2 peripheral bus thus we will use its clock source
 RCC_AHB1PeriphClockCmd(RCC_AHB1ENR_GPIOCEN,ENABLE);//Clock for the ADC port!! Do not forget about this one ;)
 //Analog pin configuration
 GPIO_initStructre.GPIO_Pin = GPIO_Pin_3;//The channel 10 is connected to PC0
 GPIO_initStructre.GPIO_Mode = GPIO_Mode_AN; //The PC0 pin is configured in analog mode
 GPIO_initStructre.GPIO_PuPd = GPIO_PuPd_NOPULL; //We don't need any pull up or pull down
 GPIO_Init(GPIOC,&GPIO_initStructre);//Affecting the port with the initialization structure configuration
 //ADC structure configuration
 ADC_DeInit();
 ADC_init_structure.ADC_DataAlign = ADC_DataAlign_Right;//data converted will be shifted to right
 ADC_init_structure.ADC_Resolution = ADC_Resolution_12b;//Input voltage is converted into a 12bit number giving a maximum value of 4096
 ADC_init_structure.ADC_ContinuousConvMode = ENABLE; //the conversion is continuous, the input data is converted more than once
 ADC_init_structure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;// conversion is synchronous with TIM1 and CC1 (actually I'm not sure about this one :/)
 ADC_init_structure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;//no trigger for conversion
 ADC_init_structure.ADC_NbrOfConversion = 1;//I think this one is clear :p
 ADC_init_structure.ADC_ScanConvMode = DISABLE;//The scan is configured in one channel
 ADC_Init(ADC1,&ADC_init_structure);//Initialize ADC with the previous configuration
 //Enable ADC conversion
 ADC_Cmd(ADC1,ENABLE);
 //Select the channel to be read from
 ADC_RegularChannelConfig(ADC1,ADC_Channel_13,1,ADC_SampleTime_144Cycles);
}
int adc_convert(void){
 ADC_SoftwareStartConv(ADC1);//Start the conversion
 while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));//Processing the conversion
 return ADC_GetConversionValue(ADC1); //Return the converted data
}
int main(void)
{
     
    USART3_Configuration();
    adc_configure();//Start configuration
   
  while(1){//loop while the board is working
             
            ConvertedValue = adc_convert();//Read the ADC converted value
            printf("%d\n\r", ConvertedValue);
         
            for(i = 0; i < 8000000; i++)
            {
             
            }
    }
}


Great, so then here are my init routines + main + dma interrupt handler:

#include "stdint.h"
#include "main.h"
#include "stm32f4xx.h"
#include "adc.h"
#include "utils.h"
 
 
extern volatile uint16_t ADCConvertedValues[NUM_SAMPLES];
extern volatile uint16_t sample;
 
 
void RCC_Configuration(void)
{
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1ENR_GPIOCEN,ENABLE);
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
}
 
 
void GPIO_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
     
    // Configure ADC input pin
    GPIO_InitStructure.GPIO_Pin     = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode    = GPIO_Mode_AN;
    GPIO_InitStructure.GPIO_PuPd    = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOC, &GPIO_InitStructure);
}
 
 
void ADC_Configuration(void)
{
    ADC_InitTypeDef ADC_InitStructure;
    ADC_CommonInitTypeDef ADC_CommonInitStructure;
 
 
    // Configure the ADC1 peripheral for fast single conversions to the DMA
    ADC_DeInit();
  ADC_CommonInitStructure.ADC_Mode                          = ADC_Mode_Independent;
  ADC_CommonInitStructure.ADC_Prescaler                 = ADC_Prescaler_Div2;
  ADC_CommonInitStructure.ADC_DMAAccessMode         = ADC_DMAAccessMode_Disabled;
  ADC_CommonInitStructure.ADC_TwoSamplingDelay  = ADC_TwoSamplingDelay_5Cycles;
  ADC_CommonInit(&ADC_CommonInitStructure);
     
    ADC_InitStructure.ADC_Resolution                            = ADC_Resolution_12b;
    ADC_InitStructure.ADC_ScanConvMode                      = DISABLE;                          // 1 Channel
    ADC_InitStructure.ADC_DataAlign                             = ADC_DataAlign_Right;
    ADC_InitStructure.ADC_ContinuousConvMode            = DISABLE;                          // Conversions Triggered
    ADC_InitStructure.ADC_ExternalTrigConv              = ADC_ExternalTrigConv_T2_TRGO;
    ADC_InitStructure.ADC_ExternalTrigConvEdge      = ADC_ExternalTrigConvEdge_Rising;
    ADC_InitStructure.ADC_NbrOfConversion               = 1;
    ADC_Init(ADC1,&ADC_InitStructure);
     
    // Use PC3 for the OS on CCD "ADC_Channel_13"
    ADC_RegularChannelConfig(ADC1,ADC_Channel_13,1,ADC_SampleTime_15Cycles);    // PC3
  ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);
    ADC_DMACmd(ADC1, ENABLE);
    ADC_Cmd(ADC1,ENABLE);
    ADC_RegularChannelConfig(ADC1,ADC_Channel_13,1,ADC_SampleTime_15Cycles);    // PC3
 
 
}
     
void DMA_Configuration(void)
{
    DMA_InitTypeDef DMA_InitStructure;
     
    DMA_DeInit(DMA2_Stream0);
    DMA_InitStructure.DMA_Channel                       = DMA_Channel_0; 
  DMA_InitStructure.DMA_PeripheralBaseAddr  = (uint32_t)&ADC1->DR;
  DMA_InitStructure.DMA_Memory0BaseAddr         = (uint32_t)&ADCConvertedValues[0];
  DMA_InitStructure.DMA_DIR                                 = DMA_DIR_PeripheralToMemory;
  DMA_InitStructure.DMA_BufferSize                  = NUM_SAMPLES;
  DMA_InitStructure.DMA_PeripheralInc           = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_MemoryInc                   = DMA_MemoryInc_Enable;
  DMA_InitStructure.DMA_PeripheralDataSize  = DMA_PeripheralDataSize_HalfWord;
  DMA_InitStructure.DMA_MemoryDataSize          = DMA_MemoryDataSize_HalfWord;
  DMA_InitStructure.DMA_Mode                                = DMA_Mode_Circular;
  DMA_InitStructure.DMA_Priority                        = DMA_Priority_High;
  DMA_InitStructure.DMA_FIFOMode                        = DMA_FIFOMode_Disable;     // Enable?   
  DMA_InitStructure.DMA_FIFOThreshold           = DMA_FIFOThreshold_HalfFull;
  DMA_InitStructure.DMA_MemoryBurst                 = DMA_MemoryBurst_Single;
  DMA_InitStructure.DMA_PeripheralBurst         = DMA_PeripheralBurst_Single;
  DMA_Init(DMA2_Stream0, &DMA_InitStructure);
  DMA_Cmd(DMA2_Stream0, ENABLE);
  
  /* Enable DMA Stream Transfer Complete interrupt */
  DMA_ITConfig(DMA2_Stream0, DMA_IT_TC | DMA_IT_HT, ENABLE);
  
  /* DMA2_Stream0 enable */
  DMA_Cmd(DMA2_Stream0, ENABLE);
}
 
 
/*
 
 
PF3  ADC3_IN9
PF4  ADC3_IN14
PF5  ADC3_IN15
PF6  ADC3_IN4
PF7  ADC3_IN5
PF8  ADC3_IN6
PF9  ADC3_IN7
PF10 ADC3_IN8
PC0  ADC123_IN10
PC1  ADC123_IN11
PC2  ADC123_IN12
PC3  ADC123_IN13  <---- Use this one
PA0  ADC123_IN0
PA1  ADC123_IN1
PA2  ADC123_IN2
PA3  ADC123_IN3
PA4  ADC12_IN4
PA5  ADC12_IN5
PA6  ADC12_IN6
PA7  ADC12_IN7
PB0  ADC12_IN8
PB1  ADC12_IN9
PC4  ADC12_IN14
PC5  ADC12_IN15
 
 
*/
 
 
void TIM2_Configuration(void)
{
  TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
   
  /* Time base configuration */
  TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
  TIM_TimeBaseStructure.TIM_Period              = (90000000 / 200) - 1; // 200 Hz, from 90 MHz TIM2CLK (ie APB1 = HCLK/4, TIM2CLK = HCLK/2)
  TIM_TimeBaseStructure.TIM_Prescaler       = 0;
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
  TIM_TimeBaseStructure.TIM_CounterMode     = TIM_CounterMode_Up;
  TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
   
  /* TIM2 TRGO selection */
  TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update); // ADC_ExternalTrigConv_T2_TRGO
   
  /* TIM2 enable counter */
  TIM_Cmd(TIM2, ENABLE);
}
 
 
void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
   
  /* Enable the DMA Stream IRQ Channel */
  NVIC_InitStructure.NVIC_IRQChannel                                        = DMA2_Stream0_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority  = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority                 = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd                                 = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}
 
 
void DMA2_Stream0_IRQHandler(void) // Called at 2 Hz for 200 Hz sample rate, LED Toggles at 2 Hz
{
  /* Test on DMA Stream Half Transfer interrupt */
  if(DMA_GetITStatus(DMA2_Stream0, DMA_IT_HTIF0))
  {
    /* Clear DMA Stream Half Transfer interrupt pending bit */
    DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_HTIF0);
   
    /* Turn LED3 off: Half Transfer */
    GPIO(1);
        LED(0);
    // Add code here to process first half of buffer (ping)
        // CountFallingEdges(&ADCConvertedValues[0],NUM_SAMPLES/2);
  }
   
  /* Test on DMA Stream Transfer Complete interrupt */
  if(DMA_GetITStatus(DMA2_Stream0, DMA_IT_TCIF0))
  {
    /* Clear DMA Stream Transfer Complete interrupt pending bit */
    DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0);
   
    /* Turn LED3 on: End of Transfer */
    GPIO(0);
        LED(1);
        //DMA_Cmd(DMA2_Stream0, DISABLE);
   
    // Add code here to process second half of buffer (pong)
        // CountFallingEdges(&ADCConvertedValues[99],NUM_SAMPLES/2);
  }
}
 
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stdint.h"
#include "stdlib.h"
#include "stdio.h"
#include "string.h"
#include "adc.h"
#include "usart.h"
#include "utils.h"
 
 
 
 
uint32_t i;
uint16_t j;
uint16_t sample = 0;
 
 
volatile uint16_t ADCConvertedValues[NUM_SAMPLES] = {0};
volatile uint16_t signal[NUM_SAMPLES] = {0};
 
 
int main(void)
{
    LED_Setup();
    GPIO_Setup();
    RCC_Configuration();
    USART3_Configuration();
    NVIC_Configuration();
    TIM2_Configuration();
    DMA_Configuration();
    ADC_Configuration();
     
    GPIO(0);
    LED(1);
    for(i = 0; i < 80000000; i++)
    {
        //GPIO_ToggleBits(GPIOA, GPIO_Pin_5);
    }
    GPIO(1);
    ADC_SoftwareStartConv(ADC1);
    GPIO(0);
     
  // Infinite loop
  while (1)
  {
        printf("%d\n\r", ADCConvertedValues[0]);
        j++;
        for(i = 0; i < 80000000; i++)
        {
             
        }  
  }
}


Any idea why my buffers are filled with garbage? DMA alignment seems OK. 16 bit word size etc. etc. Hmm... 

The GPIO and LED call you see are for debugging on a logic probe. The output is known to be bad by both printf statements in the console, and the debugging watch point in KEIL shows the entire buffer filled with this garbage.

What do you guys think?

Edit: Fixed code formatting

Outcomes