2013-01-20 09:12 AM
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 problemBTW, 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¤tviews=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 onlyvoid 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-adc2013-01-20 10:06 AM
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.2013-01-20 10:22 AM
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).
2013-01-20 10:42 AM
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.2013-01-20 10:52 AM
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.