cancel
Showing results for 
Search instead for 
Did you mean: 

ADC sampling and storing on SD

dkilshtein9
Associate II
Posted on January 20, 2013 at 18:12

I need to be able to store ADC sample on a SD card. the sample rate is 200KHz.

Since the buffer size register is limited to a maximum value of 0xffff I decided to use the DMA in a dual buffer mode and ADC in Continuous Conversion Mode.

I want to supply the DMA with two addresses and this way to have a whole buffer of 100000 samples. Then I want to write this whole buffer to the SD card each 500ms.

The problem is that after a few seconds the DMA stops working and the data never changes. I also tried to use an interrupt (for debug only) but the same happened here, after a few seconds I stopped getting DMA interrupts. I'm sure I'm missing something but I just can't find the problem

BTW, this thread is a follow up to this older thread:

/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/ADC%20sampling%20rate&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B&currentviews=173

Here is the code I used:

void HwInit(void)

{

  ADC_InitTypeDef       ADC_InitStructure;

  ADC_CommonInitTypeDef ADC_CommonInitStructure;

  DMA_InitTypeDef       DMA_InitStructure;

  GPIO_InitTypeDef      GPIO_InitStructure;

  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

  //NVIC_InitTypeDef  NVIC_InitStructure;

       

  //Enable ADC3, DMA2, Timer and GPIO clocks

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 |     RCC_AHB1Periph_GPIOF , ENABLE);

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3, ENABLE);

  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

    

    Period = 25;

  TIM_TimeBaseStructure.TIM_Prescaler = (SystemCoreClock / 10000000) - 1;             // 1 MHz

  TIM_TimeBaseStructure.TIM_Period = Period - 1;  //1MHz /25 = 400KHz

  TIM_TimeBaseStructure.TIM_ClockDivision = 0;

  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

  TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

  TIM_SelectOutputTrigger (TIM3, TIM_TRGOSource_Update);

  TIM_ARRPreloadConfig(TIM3, ENABLE);

    

  //DMA2 Stream0 channel2

  DMA_InitStructure.DMA_Channel = DMA_Channel_2;  

  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC3_DR_ADDRESS;

  DMA_InitStructure.DMA_Memory0BaseAddr =  FSMC_SRAM_ADDRESS;

  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;

  DMA_InitStructure.DMA_BufferSize = 50000;

  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;

  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;

  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;

  DMA_InitStructure.DMA_Priority = DMA_Priority_High;

  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;

  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;

  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;

  DMA_Init(DMA2_Stream0, &DMA_InitStructure);

DMA_DoubleBufferModeConfig(DMA2_Stream0, FSMC_SRAM_ADDRESS + ( 50000 ), DMA_Memory_0);

DMA_DoubleBufferModeCmd(DMA2_Stream0, ENABLE);

        

  //IOs

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;

  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;

  GPIO_Init(GPIOF, &GPIO_InitStructure);

  

  //ADC 3

  ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;

  ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div8;

  ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;

  ADC_CommonInit(&ADC_CommonInitStructure);

  ADC_InitStructure.ADC_Resolution = ADC_Resolution_8b;

  ADC_InitStructure.ADC_ScanConvMode = DISABLE;

  ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;

  ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;

  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;

  ADC_InitStructure.ADC_NbrOfConversion = 1;

  

  ADC_InitStructure.ADC_ExternalTrigConv     = ADC_ExternalTrigConv_T3_TRGO;

  ADC_Init(ADC3, &ADC_InitStructure);

  /* ADC3 regular channel7 configuration *************************************/

  ADC_RegularChannelConfig(ADC3, ADC_Channel_7, 1, ADC_SampleTime_28Cycles);

  

 /* Enable DMA request after last transfer (Single-ADC mode) */

  ADC_DMARequestAfterLastTransferCmd(ADC3, ENABLE);

 

  //IRQs  -  debug only

  //NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

  //NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn ;

  //NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 13;

  //NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;

  //NVIC_Init(&NVIC_InitStructure);

  ////DMA_ITConfig(DMA2_Stream0, DMA_IT_HT | DMA_IT_TC | DMA_IT_TE, ENABLE);

  

   //Start

  DMA_Cmd(DMA2_Stream0, ENABLE);

  ADC_DMACmd(ADC3, ENABLE);

  ADC_Cmd(ADC3, ENABLE);

  TIM_Cmd(TIM3, ENABLE);

  ADC_SoftwareStartConv(ADC3);

}

//debug only

void DMA2_Stream0_IRQHandler(void)

      /* Clear the Interrupt flag */

   DMA_ClearFlag(DMA2_Stream0, DMA_IT_HT | DMA_IT_TC | DMA_IT_TE | DMA_IT_DME|DMA_IT_FE);

   

      STM_EVAL_LEDToggle(LED4);

  

}

#stm32-adc
4 REPLIES 4
Posted on January 20, 2013 at 19:06

So either the timer stops, the DMA faults, or the CPU faults.

You'll have to break out the debugger and go look at the peripheral registers, and where the processor is.

If your task is to shovel data to an SD card, and you have the intrinsic bandwidth to achieve that, using internal memory and a single 32K buffer using circular DMA, and HT and TC interrupts at the 16 /16 K interval should be quite sufficient.
Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
dkilshtein9
Associate II
Posted on January 20, 2013 at 19:22

Thanks, I think that a 32k buffer is not enough. Besides the 200khz ADC, I have two more ADC channels (sample rate of 12KHz) and a frame from a camera that I need to store each second. To achieve this I need more time between each SD access. To have more time I need a bigger buffer. This is why I wanted to use to dual buffer mode. It will allow me to accumulate much more data and provide me with the time I need to store it and handle other peripherals.

The problem is that everything worked fine (with some minor data loss) until I added the dual mode. Now, no matter what I do, the DMA (or the ADC) just stops. Other peripherals work fine. When I use the step-by-step debugging, the fault occurs when I hit fast the F5. When I hit the F5 slower (say once a second it keeps working). 

Posted on January 20, 2013 at 19:42

Really, I don't know writing 50 or 100 K isn't going to offer much benefit from the file system, it's going to be marginal at best. What will really kill you with mass-storage devices and file systems is writing blocks that violate the sector/cluster boundaries, and in flash the block/page erase boundaries. Multiple streams of data fragmenting the storage space probably won't help.

An external memory solution at saturation is going to throw away at least 66% of the bandwidth available to an internal solution. Frankly I think the number is worse than that, but it's not my battle.
Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
Posted on January 20, 2013 at 19:52

You want to inspect the cpu/peripheral state once it stops working.

You should benchmark reads and writes to external memory, vs internal memory.

You should benchmark SD/FatFs file reads/writes in bytes per second reading/writing blocks of 16,32,50,64,100,128KB, do that from internal memory and external memory for those which will fit.

You should review the device errata wrt multiple DMA channels.
Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..