AnsweredAssumed Answered

13 channels of ADC + DMA = data in the wrong order

Question asked by jackson.dennis on Jun 4, 2014
Latest reply on Jul 24, 2014 by jackson.dennis
I'm trying to sample 13 channels on ADC1 on an STM32F407.  I have DMA set up and am getting data; however, the data does not appear in the order that I expect it.  When I output the data over the UART in the enumeration order, I see CH_A in element 4 instead of element 0.  

Here's the weird thing: if I remove one channel (any channel) and sample just 12 channels, all of the data is exactly where I expect it.   What's happening when I add the 13th channel?

I've confirmed that ADC1->SQR[1-3] contain the order that I specify below and that it will convert 13 channels.  I've tried switching the DMA from words to half-words and get the same behavior.  

This is on an STM32F407 F405RGT6, 64-pin.

typedef enum {                   /* Must set first item in enum to zero; this is an index into the DMA array */
    IVSENSE_CH_A = 0,    /* Main battery current (not backup battery)            */
    IVSENSE_CH_B,
    IVSENSE_CH_C,
    IVSENSE_CH_D,
    IVSENSE_CH_E,
    IVSENSE_CH_F,
    IVSENSE_CH_G,
    IVSENSE_CH_H,
    IVSENSE_CH_I,
    IVSENSE_CH_J,
    IVSENSE_CORETEMP,        // STM internal
    IVSENSE_VREFINT,         // STM internal
    IVSENSE_VBAT,            // STM internal             /* Backup battery voltage (not main battery)            */
} ivsense_channel_t;
  
  
#define   IVSENSE_ADC                 (ADC1)
#define   IVSENSE_DMA_STREAM          (DMA2_Stream0)            /* IMPORTANT: Be sure nothing else uses this stream     */
  
typedef struct {
    ivsense_channel_t  rank;                                    /* Number in conversion sequence                        */
    uint8_t            chan;                                    /* ADC channel number (see schematic)                   */
} rank_channel_tuple_t;
  
static const rank_channel_tuple_t rank_channel_table[] = {      /* Channel assignment                                   */
    { IVSENSE_CH_A,       0  },
    { IVSENSE_CH_B,       4  },
    { IVSENSE_CH_C,       14 },
    { IVSENSE_CH_D,       15 },
    { IVSENSE_CH_E,       8  },
    { IVSENSE_CH_F,       9  },
    { IVSENSE_CH_G,       10 },
    { IVSENSE_CH_H,       11 },
    { IVSENSE_CH_I,       12 },
    { IVSENSE_CH_J,       13 },
    { IVSENSE_CORETEMP,   16 },
    { IVSENSE_VREFINT,    17 },
    { IVSENSE_VBAT,       18 }
};
  
#define   IVSENSE_NUM_CHANNELS        (sizeof(rank_channel_table) / sizeof(rank_channel_table[0]))
  
static CPU_INT32U adc_buffer[IVSENSE_NUM_CHANNELS];             /* Destination of DMA transfers                         */
  
static void  IVSense_BSP_Actuator_CfgDMA (void)
{
    DMA_InitTypeDef  DMA_InitStructure;
    CPU_SR_ALLOC();                                             /* Must be declared after all other local variables     */
  
    /* Initialize structure with default values. Override below. */
    DMA_StructInit(&DMA_InitStructure);
  
    /* Ensure stream is actually disabled */
    CPU_CRITICAL_ENTER();
    DMA_Cmd(IVSENSE_DMA_STREAM, DISABLE);
    CPU_CRITICAL_EXIT();
  
    /* As recommended by manufacturer, verify stream is disabled */
    /*LDRA_INSPECTED 28 D Potentially infinite loop found. */
    while (ENABLE == DMA_GetCmdStatus(IVSENSE_DMA_STREAM)) {
        /* No operation */
    }
  
    /* Use channel hard-wired to appropriate ADC peripheral */
    DMA_InitStructure.DMA_Channel            = DMA_Channel_0;
  
    /* Set source base address (analog-to-digital converter's data register) */
    /*LDRA_INSPECTED 94 S Casting operation on a pointer.*/
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&(IVSENSE_ADC->DR));
  
    /* Set destination base address */
    /*LDRA_INSPECTED 439 S Cast from pointer to integral type.*/
    DMA_InitStructure.DMA_Memory0BaseAddr    = (uint32_t)(&adc_buffer);
  
    /* Transfer direction is from analog-to-digital converter peripheral to memory */
    DMA_InitStructure.DMA_DIR                = DMA_DIR_PeripheralToMemory;
  
    /* Buffer size (in data items) is number of channels to transfer */
    DMA_InitStructure.DMA_BufferSize         = IVSENSE_NUM_CHANNELS;
  
    /* Do not increment peripheral (i.e. source) address */
    DMA_InitStructure.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;
  
    /* Increment memory (i.e. destination) address */
    DMA_InitStructure.DMA_MemoryInc          = DMA_MemoryInc_Enable;
  
    /* Source and destination sizes are words (32-bits) */
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
    DMA_InitStructure.DMA_MemoryDataSize     = DMA_MemoryDataSize_Word;
  
    /* Circular mode automatically reloads the number of data items to be transferred */
    DMA_InitStructure.DMA_Mode               = DMA_Mode_Circular;
  
    /* Write configuration to hardware registers */
    CPU_CRITICAL_ENTER();
    DMA_Init(IVSENSE_DMA_STREAM, &DMA_InitStructure);
    CPU_CRITICAL_EXIT();
  
    /* Enable stream */
    CPU_CRITICAL_ENTER();
    DMA_Cmd(IVSENSE_DMA_STREAM, ENABLE);
    CPU_CRITICAL_EXIT();
  
    /* As recommended by manufacturer, verify stream is enabled */
    /*LDRA_INSPECTED 28 D Potentially infinite loop found. */
    while (DISABLE == DMA_GetCmdStatus(IVSENSE_DMA_STREAM)) {
        /* No operation */
    }
}
  
/**********************************************************************************************************/
void  IVSense_BSP_Actuator_ClkEn (void)
{
    BSP_PeriphEn(BSP_PERIPH_ID_ADC1);                           /* Enable clock to ADC1                                 */
    BSP_PeriphEn(BSP_PERIPH_ID_DMA2);                           /* Enable clock to DMA2                                 */
    BSP_PeriphEn(BSP_PERIPH_ID_GPIOA);                          /* Enable clock to GPIO Port A                          */
    BSP_PeriphEn(BSP_PERIPH_ID_GPIOB);                          /* Enable clock to GPIO Port B                          */
    BSP_PeriphEn(BSP_PERIPH_ID_GPIOC);                          /* Enable clock to GPIO Port C                          */
}
  
/**********************************************************************************************************/
void  IVSense_BSP_Actuator_ClkDis (void)
{
    BSP_PeriphDis(BSP_PERIPH_ID_ADC1);                          /* Disable clock to ADC1                                */
    BSP_PeriphDis(BSP_PERIPH_ID_DMA2);                          /* Disable clock to DMA2                                */
}
  
/**********************************************************************************************************/
void  IVSense_BSP_Actuator_CfgGPIO (void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    CPU_SR_ALLOC();                                             /* Must be declared after all other local variables     */
  
    GPIO_StructInit(&GPIO_InitStructure);                       /* Common configuration                                 */
    GPIO_InitStructure.GPIO_Mode     = GPIO_Mode_AN;
    GPIO_InitStructure.GPIO_PuPd     = GPIO_PuPd_NOPULL;
  
    CPU_CRITICAL_ENTER();                                    /* Start of critical code                               */
  
    GPIO_InitStructure.GPIO_Pin      = GPIO_Pin_0;           /* Configure GPIOA.0 as analog input (CH A)    */
    GPIO_Init(GPIOA, &GPIO_InitStructure);
  
    GPIO_InitStructure.GPIO_Pin      = GPIO_Pin_4;           /* Configure GPIOA.4 as analog input (CH B)      */
    GPIO_Init(GPIOA, &GPIO_InitStructure);
  
    GPIO_InitStructure.GPIO_Pin      = GPIO_Pin_0;           /* Configure GPIOB.0 as analog input (CH E)     */
    GPIO_Init(GPIOB, &GPIO_InitStructure);
  
    GPIO_InitStructure.GPIO_Pin      = GPIO_Pin_1;           /* Configure GPIOB.1 as analog input (CH F)     */
    GPIO_Init(GPIOB, &GPIO_InitStructure);
  
    GPIO_InitStructure.GPIO_Pin      = GPIO_Pin_0;           /* Configure GPIOC.0 as analog input (CH G)       */
    GPIO_Init(GPIOC, &GPIO_InitStructure);
  
    GPIO_InitStructure.GPIO_Pin      = GPIO_Pin_1;           /* Configure GPIOC.1 as analog input (CH H)       */
    GPIO_Init(GPIOC, &GPIO_InitStructure);
  
    GPIO_InitStructure.GPIO_Pin      = GPIO_Pin_2;           /* Configure GPIOC.2 as analog input (CH I)       */
    GPIO_Init(GPIOC, &GPIO_InitStructure);
  
    GPIO_InitStructure.GPIO_Pin      = GPIO_Pin_3;           /* Configure GPIOC.3 as analog input (CH J)       */
    GPIO_Init(GPIOC, &GPIO_InitStructure);
  
    GPIO_InitStructure.GPIO_Pin      = GPIO_Pin_4;           /* Configure GPIOC.4 as analog input (CH C)     */
    GPIO_Init(GPIOC, &GPIO_InitStructure);
  
    GPIO_InitStructure.GPIO_Pin      = GPIO_Pin_5;           /* Configure GPIOC.5 as analog input (CH D)     */
    GPIO_Init(GPIOC, &GPIO_InitStructure);
  
    CPU_CRITICAL_EXIT();                                     /* End of critical code                                 */
}
  
/**********************************************************************************************************/
void  IVSense_BSP_Actuator_CfgADC (void)
{
    uint8_t          i;
    ADC_InitTypeDef  ADC_InitStructure;
    CPU_SR_ALLOC();                                             /* Must be declared after all other local variables     */
  
    /* Initialize structure with default values. Override below. */
    ADC_StructInit(&ADC_InitStructure);
  
    /* 12-bit resolution. More resolution allows for more accurate conversion. */
    ADC_InitStructure.ADC_Resolution           = ADC_Resolution_12b;
  
    /* Right alignment fixes the sample range independent of resolution */
    ADC_InitStructure.ADC_DataAlign            = ADC_DataAlign_Right;
  
    /* Scan mode performs multi-channel conversion, as opposed to single channel conversion */
    ADC_InitStructure.ADC_ScanConvMode         = ENABLE;
  
    /* Continuous mode automatically recycles conversion to first channel after last channel */
    ADC_InitStructure.ADC_ContinuousConvMode   = ENABLE;
  
    /* Not externally triggered */
    ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
  
    /* Number of channels in sequence group */
    ADC_InitStructure.ADC_NbrOfConversion      = IVSENSE_NUM_CHANNELS;
  
    /* Write configuration to hardware registers */
    CPU_CRITICAL_ENTER();
    ADC_Init(IVSENSE_ADC, &ADC_InitStructure);
    CPU_CRITICAL_EXIT();
  
    /* Enable the channels for reading internal sensors */
    ADC_TempSensorVrefintCmd( ENABLE );
    ADC_VBATCmd( ENABLE );
  
    /* Define regular channels group */
    for( i = 0; i < IVSENSE_NUM_CHANNELS; i++ ) {
        ADC_RegularChannelConfig( IVSENSE_ADC,
                                  rank_channel_table[i].chan,
                                  rank_channel_table[i].rank + 1, // ST rank is 1-based
                                  ADC_SampleTime_480Cycles );
    }
  
    /* Activate continuous mode */
    ADC_ContinuousModeCmd( IVSENSE_ADC, ENABLE );
  
    /* Enable ADC DMA request after last transfer */
    CPU_CRITICAL_ENTER();
    ADC_DMARequestAfterLastTransferCmd( IVSENSE_ADC, ENABLE );
    CPU_CRITICAL_EXIT();
  
    /* Configure the DMA controller for ADC transfers */
    IVSense_BSP_Actuator_CfgDMA();
}
  
/**********************************************************************************************************/
void  IVSense_BSP_Actuator_Start (void)
{
    ADC_DMACmd(IVSENSE_ADC, ENABLE);                            /* Enable ADC DMA requests                              */
    ADC_Cmd(IVSENSE_ADC, ENABLE);                               /* Enable ADC                                           */
    ADC_SoftwareStartConv(IVSENSE_ADC);                         /* Start conversion of regular channels                 */
}
  
/**********************************************************************************************************/
void  IVSense_BSP_Actuator_Stop (void)
{
    ADC_Cmd(IVSENSE_ADC, DISABLE);                              /* Disable ADC                                          */
    ADC_DMACmd(IVSENSE_ADC, DISABLE);                           /* Disable ADC DMA requests                             */
}
  
/**********************************************************************************************************/
CPU_INT32S  IVSense_BSP_Actuator_Read (ivsense_channel_t channel, CPU_INT16U *value)
{
    CPU_INT32U  retval = IVSENSE_ERROR_NONE;
  
    /* Verify that destination pointer not NULL */
    if (NULL != value) {
        /* Verify that channel specification valid */
        if ((0 <= channel) && (channel < IVSENSE_NUM_CHANNELS)) {
            /* Atomically read channel value from DMA buffer and write to destination */
            *value = adc_buffer[channel];
        } else {
            retval = IVSENSE_ERROR_RANGE;
        }
    } else {
        retval = IVSENSE_ERROR_NULLPTR;
    }
  
    return retval;
}

Outcomes