AnsweredAssumed Answered

STM32L1xx clear DMA TC / ADC_EOC flags?

Question asked by mihaylov.kaloyan on Mar 9, 2017
Latest reply on Mar 13, 2017 by mihaylov.kaloyan

Hello!

 

I would like to use the ADC with DMA in order to do the following:

- manually start the ADC in (discontinuous) scan mode

- sample 3 channels

- put the adc result with DMA to some array.

- no further (automatic/continuous sampling) till the next manual ADC start.

 

So the ADC should start, scan the 3 regular channels. The DMA should fill the array and afterwards I expect that the ADC_FLAG_EOC should be set?

Since I never saw it set I read the DMA1_FLAG_TC1 instead.

 

The problems are:

1. The DMA1_FLAG_TC1 is always set. (because ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE), but otherwise I should re-initialize the DMA?)

2. I cannot clear the ADC_FLAG_EOC or DMA1_FLAG_TC1.

3. There are always some values changing in the DMA_CNDTR1 register and I'm not sure why since everything should have been done quickly after the ADC_SoftwareStartConv(ADC).

 

The ADC uses the HSI (no prescaler) and the CPU is clocked @ 2MHz

I guess I misunderstand how DMA works in this case. Please review my code and give me directions.

 

Thanks!

ADC_InitTypeDef ADC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
DMA_InitTypeDef DMA_InitStructure;

uint8_t i = 0;
uint32_t volatile adcResult[3]= { 0, 0, 0 };
uint32_t volatile voltage[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
uint32_t volatile current[10];// = 0;

#define ADC1_DR_ADDRESS    ((uint32_t)0x40012458)

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //DMA clock enable
DMA_DeInit(DMA1_Channel1); //DMA1 channel1 configuration
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_ADDRESS;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&adcResult;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = 3;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //peripheral address register...??
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //increment pointer
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //Byte, HalfWord or Word
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; //Byte, HalfWord or Word
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//DMA_Mode_Circular; //????
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //???
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
DMA_Cmd(DMA1_Channel1, ENABLE); //Enable DMA1 channel1

ADC_TempSensorVrefintCmd(ENABLE);
delay_ms(5); //Internal reference needs 3ms startup time! ; there is a flag like vrefrdy - check the datasheet

RCC_HSICmd(ENABLE); //16MHz, ADC requirement

/* Enable the GPIOF or GPIOA Clock */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE); //IDD_MEASUREMENT_GPIO_CLK = RCC_AHBPeriph_GPIOA

/* GPIOA0 as ADC_Channel_0 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_3; //PA0,3
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; //analog
GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init( GPIOA, &GPIO_InitStructure );

/* Check that HSI oscillator is ready */
while(RCC_GetFlagStatus(RCC_FLAG_HSIRDY) == RESET);

RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

ADC_Cmd(ADC1, DISABLE);
/* For medium+ and high density devs -> two banks (A,B) 11.3.3 from RefManual
ADC_BankSelection(ADC1, ADC_Bank_B);
*/

ADC_DeInit(ADC1);

ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
//ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfConversion = 3;//1;
ADC_Init(ADC1, &ADC_InitStructure);

/* Define delay between ADC1 conversions */
// Delay is introduced when the ADC clock is higher than the CPU clock...
//ADC_DelayLength_Freeze: Delay until the converted data has been read.
ADC_DelaySelectionConfig(ADC1, ADC_DelayLength_Freeze);

/* Enable ADC1 Power Down during Delay */
ADC_PowerDownCmd(ADC1, ADC_PowerDown_Idle_Delay, ENABLE);
/*
If the channel is configured in noncircular mode, no DMA request is served after the last
transfer (that is once the number of data items to be transferred has reached zero). In order
to reload a new number of data items to be transferred into the DMA_CNDTRx register, the
DMA channel must be disabled.
*/

ADC_RegularChannelConfig(ADC1, 0x11, 1, ADC_SampleTime_16Cycles);
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 2, ADC_SampleTime_16Cycles); //ref.manual-> 12.8, for 12 bit-> 12+4 cycles (16 cycles) min. -> 384+12?
ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 3, ADC_SampleTime_16Cycles);

/* Enable the request after last transfer for DMA Circular mode */
ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);
ADC_DMACmd(ADC1, ENABLE);

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

while ( ADC_GetFlagStatus(ADC1, ADC_FLAG_ADONS) == RESET ) {} /* Wait until ADC1 ON status */

uint16_t vref;
for( i=0; i<10; i++ ) {
     ADC_SoftwareStartConv(ADC1);
     while( ! DMA_GetFlagStatus( DMA1_FLAG_TC1 ) );
     //while( ADC_GetFlagStatus( ADC1, ADC_FLAG_EOC ) == RESET );// DO NOT USE WITH DMA, CHECK DMA TRANSFER COMPLETE THOUGH!
     vref = adcResult[0];
     current[i] = adcResult[1];
     voltage[i] = adcResult[2];
     ADC_GetConversionValue(ADC1);
     DMA_ClearFlag( DMA1_FLAG_GL1 );
     DMA_ClearITPendingBit( DMA1_IT_GL1 );
     delay_ms(1);
}

ADC_Cmd(ADC1, DISABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, DISABLE);
ADC_TempSensorVrefintCmd(DISABLE);
RCC_HSICmd(DISABLE);

Outcomes