AnsweredAssumed Answered

STM32F1xx - How to implement moving average - SOLVED

Question asked by germanovix.gabriel on Feb 23, 2014
Latest reply on Feb 28, 2014 by fm
Hello everyone,

I'm try to implement moving average on my micro...but as yet works partially. I think is because of processing time.. or because i don't know the correct way to implement..hehe

Here is some stretch of my code:

#include "stm32f10x.h"

//MACROS
#define TAM_BUFF     64          //Colocar multiplo de 2
#define NUM_SH          6          //Shift-register


//prototipos
void GPIO_inic(void);
void TIM3_inic(void);
void NVIC_inic(void);
void DMA_inic(void);
void ADC_DMA_inic(void);
void Delay(vu32);

//variavel global

//Vetor de amostras
__IO int16_t AMOSTRAS_SENSOR[TAM_BUFF];
__IO int32_t SOMA = 0;
__IO uint32_t MEDIA = 0;
__IO uint8_t FIRSTTRANSF = 0;
ErrorStatus HSEStartUpStatus;


int main(void)
{
     int i=0;

     GPIO_inic();


     TIM3_inic();


     NVIC_inic();


     ADC_DMA_inic();

     /* Start ADC1 Software Conversion */
     ADC_SoftwareStartConvCmd(ADC1, ENABLE);

     while(FIRSTTRANSF == 0);


     for(i=TAM_BUFF/2;i<TAM_BUFF;i++)
     {
          SOMA = SOMA + AMOSTRAS_SENSOR[i];
     }
     MEDIA = SOMA >> (NUM_SH - 1); // 2^NUM_SH = TAM_BUFF


     ADC_Cmd(ADC1, ENABLE);


     //precisa de um novo SoftwareStartConvCmd??
     //Usar LEDs pra debugar o codigo!!!!!!


    while(1)
    {


    }
}



void GPIO_inic(void)
{
//***************************************************************************
//
//Configures the different GPIO ports.
//
//***************************************************************************


     GPIO_InitTypeDef GPIO_InitStructure;


     RCC_APB2PeriphClockCmd(SENSOR_IN_APB2 | RCC_APB2Periph_AFIO, DISABLE);
     RCC_APB2PeriphClockCmd(SENSOR_IN_APB2 | RCC_APB2Periph_AFIO, ENABLE);


     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;


     //Comando de inicializacao da GPIO
     GPIO_Init(LED_GPIO, &GPIO_InitStructure);


     //Config ADC PORTS====================================================================
     // input of ADC (it doesn't seem to be needed, as default GPIO state is floating input)
     GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AIN;
     GPIO_InitStructure.GPIO_Pin   = SENSOR_IN_Pin;               // that's ADC1 (PC0 on STM32f107)
     GPIO_Init(SENSOR_IN_GPIO, &GPIO_InitStructure);


}

void TIM3_inic(void)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;


    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, DISABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);


    //setting timer 3 interrupt to 100Hz
    TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
     TIM_TimeBaseStructure.TIM_Prescaler = 100;      //max 16bits
     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
     TIM_TimeBaseStructure.TIM_Period = 7200-1;           //max 16bits
     TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
     TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);


     /* TIM3 TRGO selection */
     TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Update); // ADC_ExternalTrigConv_T3_TRGO


     /* TIM3 enable counter */
     TIM_Cmd(TIM3, ENABLE);


}


void NVIC_inic(void)
{
//***************************************************************************
//
//Configure the nested vectored interrupt controller.
//
//***************************************************************************


    NVIC_InitTypeDef NVIC_InitStructure;


    //
    // Set the Vector Table base location at 0x08000000
    //
    NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);


    //
    // Configure one bit for preemption priority
    //
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);




     //Enable DMA1 channel IRQ Channel */
     NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
     NVIC_Init(&NVIC_InitStructure);


}




//----------------------------------------------------------------------------------------------------//
//------------------------------ Function DMA Configuration ------------------------------------------//
//----------------------------------------------------------------------------------------------------//


void DMA_inic(void)
{
     DMA_InitTypeDef DMA_InitStructure;


     /* Enable DMA1 clock */
     RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);


     /* DMA1 channel1 configuration ----------------------------------------------*/
     DMA_DeInit(DMA1_Channel1);
     //Endereco do local onde o ADC1 coloca o resultado da conversao
     DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
     //Endereco do vetor de amostras
     DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&AMOSTRAS_SENSOR;
     DMA_InitStructure.DMA_BufferSize = TAM_BUFF;
     DMA_InitStructure.DMA_Priority = DMA_Priority_High;
     DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
     DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
     DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
     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_Init(DMA1_Channel1, &DMA_InitStructure);


     // Enable DMA1 Channel Half / Transfer Complete interrupt
     DMA_ITConfig(DMA1_Channel1, DMA_IT_TC | DMA_IT_HT, ENABLE);


     /* Enable DMA1 channel1 */
     DMA_Cmd(DMA1_Channel1, ENABLE);
}
//----------------------------------------------------------------------------------------------------//
//------------------------------ Function ADC Configuration ------------------------------------------//
//----------------------------------------------------------------------------------------------------//


void ADC_DMA_inic(void)
{
     ADC_InitTypeDef ADC_InitStructure;


     /* ADCCLK = PCLK2/8 = 72MHz/6 = 12MHz. O maximo permitido eh 14MHz*/
     RCC_ADCCLKConfig(RCC_PCLK2_Div6);//12MHz


     /* Enable ADC1 clock */
     RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);


     /* ADC1 configuration ------------------------------------------------------*/
     ADC_DeInit(ADC1);
     ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
     ADC_InitStructure.ADC_ScanConvMode = DISABLE;
     ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
     ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_TRGO;     //Timer 3 - Trigger Output
     ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
     ADC_InitStructure.ADC_NbrOfChannel = 1;
     ADC_Init(ADC1, &ADC_InitStructure);


     /* ADC1 regular channel10 configuration */
     ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_1Cycles5);


     DMA_inic();


     //Enable End of Conversion ADC1 interrupt
     //ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE);


     /* Enable ADC1 DMA */
     ADC_DMACmd(ADC1, ENABLE);


     ADC_ExternalTrigConvCmd(ADC1, ENABLE);


     /* Enable ADC1 */
     ADC_Cmd(ADC1, ENABLE);


     /* Enable ADC1 reset calibration register */
     ADC_ResetCalibration(ADC1);
     /* Check the end of ADC1 reset calibration register */
     while(ADC_GetResetCalibrationStatus(ADC1));
     /* Start ADC1 calibration */
     ADC_StartCalibration(ADC1);
     /* Check the end of ADC1 calibration */
     while(ADC_GetCalibrationStatus(ADC1));


     /* Start ADC1 Software Conversion */
     //ADC_SoftwareStartConvCmd(ADC1, ENABLE);


}

and interrupts....
void DMA1_Channel1_IRQHandler(void)
{
     int i=0;


     /* Test on DMA Stream Half Transfer interrupt */
     if(DMA_GetITStatus(DMA1_IT_HT1))
     {
          /* Clear DMA Stream Half Transfer interrupt pending bit */
          DMA_ClearITPendingBit(DMA1_IT_HT1);


          if(FIRSTTRANSF)
          {
               // Add code here to process first half of buffer (ping)


               for(i=0;i<TAM_BUFF/2;i++)
               {
                    SOMA = SOMA + AMOSTRAS_SENSOR[i] - AMOSTRAS_SENSOR[i+TAM_BUFF/2];
               }
          }


     }


     /* Test on DMA Stream Transfer Complete interrupt */
     if(DMA_GetITStatus(DMA1_IT_TC1))
     {
          /* Clear DMA Stream Transfer Complete interrupt pending bit */
          DMA_ClearITPendingBit(DMA1_IT_TC1);


          if(FIRSTTRANSF)
          {
               // Add code here to process second half of buffer (pong)
               for(i=TAM_BUFF/2;i<TAM_BUFF;i++)
               {
                    SOMA = SOMA + AMOSTRAS_SENSOR[i] - AMOSTRAS_SENSOR[i-TAM_BUFF/2];
               }
          }
          else
          {
               FIRSTTRANSF = 1;
               ADC_Cmd(ADC1, DISABLE);
          }
     }
}

The execution of my code starts, first average is done..OK!!
The moving average starts through DMA interrupt... the initials values is done correct...
but after some seconds or minutes the average value is no longer correct..
If i force some fast changes in my input voltage.. the problem occurs faster..


I think because of processing time the operation to do average is unable to do average of TAM_BUFF/2 before a new ADC value.


I didn't want to depend of processing time.
I would like something that is protected from data loss (because of the circular buffer) 


If anyone knows how to fix my problem I would be grateful. 
Or any example in STM32 moving average that works.


Help me.. Thanks..


My problem was solved:

It was a problem of interruptions.
Besides the codes that I made available above there is also a set timer and its interrupt. 


In this case the priorities was wrong which caused the loss of data and consequent failure to calculate the moving average.

Wrong way:


//Enable DMA1 channel IRQ Channel */ 
    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn; 
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; 
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; 
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 
    NVIC_Init(&NVIC_InitStructure); 
  
    //enable TIM2 interrupt 
    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; 
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; 
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; 
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 
    NVIC_Init(&NVIC_InitStructure);


Correct way:


//Enable DMA1 channel IRQ Channel */ 
    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn; 
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; 
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; 
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 
    NVIC_Init(&NVIC_InitStructure); 
  
    //enable TIM2 interrupt 
    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; 
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; 
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; 
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 
    NVIC_Init(&NVIC_InitStructure);

Thanks for all discussions!

Outcomes