AnsweredAssumed Answered

Strange results, ADC by DMA (with scope shots)

Question asked by beels.marten on Nov 19, 2013
Latest reply on Nov 20, 2013 by beels.marten
In short, I am trying to continuously convert ADC values, and send them to a circular buffer by DMA.  Then perform some calculations on the values in that buffer, and output a DAC value (simple filter implementation).  I seem to have all the pieces working, but I am getting an odd result.

The ADC is configured to convert continuously with a sampling time of 239.5 cycles, and sends the results to a 16 byte (HalfWord) circular buffer ("InputData[]") by DMA channel 1. 

Every time an ADC conversion is completed, an "End of Conversion" interrupt fires, and a flag is set to update the DAC value.  It almost works correctly, except every other value in the buffer has the same wrong value which doesn't change when the input voltage is changed.

If I output DAC values from some other array (like "Filter[]") it works correctly.  I can also output the "Index" value, and it is doing what I expect, counting down from 20 to 1.

Here is a scope shot showing in the top trace, PC9 strobing high when the interrupt fires, and PA4 showing the output of the DAC.  The DAC output goes to zero when "Index=0", and then alternates between the correct voltage (~1.5 V) and an incorrect voltage (~1.2 V) for the next 20 interrupts.  The odd thing is that for the first 10 values, the even numbered outputs are correct, but then for the last 10 values, the odd numbered outputs are correct.  Then the cycle repeats.

I always get the 1.2 V, regardless of what the input is. 

Photo:
http://i.imgur.com/MTAMEFp.jpg

Code:

/* Includes ------------------------------------------------------------------*/
#include "stm32f0xx.h"
 
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define DAC_DHR12R1_ADDRESS      0x40007408
#define ADC1_DR_Address          0x40012440
 
#define NumTaps 21
 
#define LED3    0b010000
#define LED4    0b100000
#define BSRR_VAL        0x0300
 
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
 
volatile uint16_t InputData[NumTaps];
volatile uint8_t Update;
//Filter coefficients for low pass filter at 0.0288 * F_nyquist.  This is 800 Hz sampled at 55,556 Hz.
uint8_t Filter[NumTaps] = {2, 2, 4, 6, 9, 13, 16, 19, 22, 23, 24, 23, 22, 19, 16, 13, 9, 6, 4, 2, 2};
//Coefficients multiplied by 256, so a 20-bit accumulator is needed.
uint32_t Acc = 0;
uint16_t Index = 0;
uint16_t OutputValue = 0;
uint8_t i = 0;
uint16_t test = 0;
 
uint16_t Sine12bit[32] = {
                      2047, 2447, 2831, 3185, 3498, 3750, 3939, 4056, 4095, 4056,
                      3939, 3750, 3495, 3185, 2831, 2447, 2047, 1647, 1263, 909,
                      599, 344, 155, 38, 0, 38, 155, 344, 599, 909, 1263, 1647};
 
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
void ADC1_CH_DMA_Config(void);
void DAC_Config(void);
void GPIO_Config(void);
 
/**
  * @brief  Main program.
  * @param  None
  * @retval None
  */
int main(void)
{
  /*!< At this stage the microcontroller clock setting is already configured,
       this is done through SystemInit() function which is called from startup
       file (startup_stm32f0xx.s) before to branch to application main.
       To reconfigure the default setting of SystemInit() function, refer to
       system_stm32f0xx.c file
     */
 
  /* Preconfiguration before using GPIO---------------------------------------*/
  GPIO_Config();
  /* Preconfiguration before using ADC----------------------------------------*/
  /* Preconfiguration before using DMA----------------------------------------*/
  ADC1_CH_DMA_Config();
  /* Preconfiguration before using DAC----------------------------------------*/
  DAC_Config();
 
  Update = 0;
   
  /* Infinite loop */
  while (1)
  {
 
 
    if(Update == 1)
    {
        Update = 0;
        // Turn ON both LEDs
//      GPIOC->BSRR = (LED3 | LED4);
        GPIOC->BSRR = BSRR_VAL;
 
        // Get index value NDT from DMA_CNDTR1 register
        Index = DMA1_Channel1->CNDTR;
 
//      for(i = 0; i < NumTaps; i++)
//      {
//          Acc += Filter[i]*InputData[(i+Index)%NumTaps];
//      }
 
        // Turn OFF LED 3
//      GPIOC->BRR = LED3;
 
        //Divide by 256 to make gain unity in pass band
        Acc >>= 4;
 
//      DAC_SetChannel1Data(DAC_Align_12b_R, Acc );
        DAC_SetChannel1Data(DAC_Align_12b_R, (InputData[Index]) );
 
        Acc = 0;
 
 
        // Turn OFF LED 4
//      GPIOC->BRR = LED4;
        GPIOC->BRR = BSRR_VAL;
 
 
    }
 
 
 
  }
}
 
/**
  * @brief  PrecConfiguration: configure PA4 in analog,
  *                           enable DAC clock
  * @param  None
  * @retval None
  */
void DAC_Config(void)
{
 
  DAC_InitTypeDef    DAC_InitStructure;
 
  /* DAC Periph clock enable */
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);
 
  /* DAC channel1 Configuration */
  DAC_InitStructure.DAC_Trigger = DAC_Trigger_None;
  DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable;
 
  /* DAC Channel1 Init */
  DAC_Init(DAC_Channel_1, &DAC_InitStructure);
 
  /* Enable DAC Channel1: Once the DAC channel1 is enabled, PA.04 is
        automatically connected to the DAC converter. */
  DAC_Cmd(DAC_Channel_1, ENABLE);
 
}
 
 
/**
  * @brief  ADC1 channel with DMA configuration
  * @param  None
  * @retval None
  */
void ADC1_CH_DMA_Config(void)
{
  ADC_InitTypeDef     ADC_InitStructure;
  DMA_InitTypeDef     DMA_InitStructure;
  NVIC_InitTypeDef    NVIC_InitStructure;
   
  
  /* ADC1 Periph clock enable */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
   
  /* DMA1 clock enable */
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1 , ENABLE);
   
  /* DMA1 Channel1 Config */
  DMA_DeInit(DMA1_Channel1);
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC1_DR_Address;
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)InputData;
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
  DMA_InitStructure.DMA_BufferSize = NumTaps;
  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_M2M = DMA_M2M_Disable;
  DMA_Init(DMA1_Channel1, &DMA_InitStructure);
   
  /* DMA1 Channel1 enable */
  DMA_Cmd(DMA1_Channel1, ENABLE);
   
  /* ADC DMA request in circular mode */
  ADC_DMARequestModeConfig(ADC1, ADC_DMAMode_Circular);
   
  /* Enable ADC_DMA */
  ADC_DMACmd(ADC1, ENABLE); 
   
  /* Initialize ADC structure */
  ADC_StructInit(&ADC_InitStructure);
   
  /* Configure the ADC1 in continous mode withe a resolutuion equal to 12 bits  */
  ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
  ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
  ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
  ADC_InitStructure.ADC_ScanDirection = ADC_ScanDirection_Backward;
  ADC_Init(ADC1, &ADC_InitStructure);
  
  /* Convert the ADC1 Channel 11 with 239.5 Cycles as sampling time */
  ADC_ChannelConfig(ADC1, ADC_Channel_11 , ADC_SampleTime_239_5Cycles); 
  ADC_TempSensorCmd(ENABLE);
   
  /* Convert the ADC1 Vref  with 239.5 Cycles as sampling time */
  ADC_ChannelConfig(ADC1, ADC_Channel_Vrefint , ADC_SampleTime_239_5Cycles);
  ADC_VrefintCmd(ENABLE);
   
  /* ADC Calibration */
//  ADC_GetCalibrationFactor(ADC1); //This value not used? And ADC not enabled yet.
   
  /* Enable ADC1 */
  ADC_Cmd(ADC1, ENABLE);    
   
  /* Wait the ADCEN falg */
  while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_ADEN));
   
  /* ADC1 regular Software Start Conv */
  ADC_StartOfConversion(ADC1);
 
  //Setup End of Conversion Interrupt
  ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE);
 
  /* Enable and set ADC1 Interrupt */
  NVIC_InitStructure.NVIC_IRQChannel = ADC1_COMP_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
 
}
 
 
/**
  * @brief  GPIO configuration
  * @param  None
  * @retval None
  */
void GPIO_Config(void)
{
 
  GPIO_InitTypeDef GPIO_InitStructure;
 
  /* GPIOA clock enable */
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
  /* GPIOC Periph clock enable */
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE);
   
  /* Configure PA.04 (DAC_OUT1) as analog */
  GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_4;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
 
  /* Configure PC8 and PC9 in output pushpull mode */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  GPIO_Init(GPIOC, &GPIO_InitStructure);
 
  /* Configure ADC Channel11 as analog input */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 ;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
  GPIO_Init(GPIOC, &GPIO_InitStructure);
 
}
 
#ifdef  USE_FULL_ASSERT
 
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t* file, uint32_t line)
{
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
 
  /* Infinite loop */
  while (1)
  {
  }
}
#endif

and the interrupt code:
/* Includes ------------------------------------------------------------------*/
#include "stm32f0xx_it.h"
 
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define BSRR_VAL        0x0300
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
extern volatile uint8_t Update;
 
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
 
/******************************************************************************/
/*            Cortex-M0 Processor Exceptions Handlers                         */
/******************************************************************************/
 
/**
  * @brief  This function handles NMI exception.
  * @param  None
  * @retval None
  */
void NMI_Handler(void)
{
}
 
/**
  * @brief  This function handles Hard Fault exception.
  * @param  None
  * @retval None
  */
void HardFault_Handler(void)
{
  /* Go to infinite loop when Hard Fault exception occurs */
  while (1)
  {
  }
}
 
/**
  * @brief  This function handles SVCall exception.
  * @param  None
  * @retval None
  */
void SVC_Handler(void)
{
}
 
/**
  * @brief  This function handles PendSVC exception.
  * @param  None
  * @retval None
  */
void PendSV_Handler(void)
{
}
 
/**
  * @brief  This function handles SysTick Handler.
  * @param  None
  * @retval None
  */
void SysTick_Handler(void)
{
}
 
/******************************************************************************/
/*                 STM32F0xx Peripherals Interrupt Handlers                   */
/*  Add here the Interrupt Handler for the used peripheral(s) (PPP), for the  */
/*  available peripheral interrupt handler's name please refer to the startup */
/*  file (startup_stm32f0xx.s).                                               */
/******************************************************************************/
 
/**
  * @brief  This function handles External line 0 interrupt request.
  * @param  None
  * @retval None
  */
void EXTI0_1_IRQHandler(void)
{
  if(EXTI_GetITStatus(EXTI_Line0) != RESET)
  {
 
    /* Clear the EXTI line 0 pending bit */
    EXTI_ClearITPendingBit(EXTI_Line0);
  }
}
 
/**
  * @brief  This function handles ADC End of Conversion interrupt request.
  * @param  None
  * @retval None
  */
void ADC1_COMP_IRQHandler(void)
{
    //Clear ADC interrupt
    ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);
 
    //Set flag to update DAC value
    Update = 1;
 
 
}
 
/**
  * @brief  This function handles PPP interrupt request.
  * @param  None
  * @retval None
  */
/*void PPP_IRQHandler(void)
{
}*/

Does anyone have any idea where the 1.2 V output is coming from? 

Marten

Outcomes