Code here is configuring DMA2 for transferring 160 samples each from two analog channels from ADC1 to memory.
The ADC,DMA,NVIC etc. seem properly configured but for some reason I am getting transfer errors exactly as the ADC is turned on.
More precisely, the TEIF0 flag for DMA2 and OVR flag for ADC1 is raised exactly after ADC_Cmd in the second snippet is called.
When looking for causes for Transfer errors in RM0090 all it states is:
Transfer error: the transfer error interrupt flag (TEIFx) is set when:
– A bus error occurs during a DMA read or a write access
– A write access is requested by software on a memory address register in Double buffer mode whereas the stream is enabled and the current target memory is the one impacted by the write into the memory address register (refer to Section 10.3.9: Double buffer mode)
The definition isn't very informative. A link to a similar issue can be found here: https://www.mikrocontroller.net/topic/389574
is it possible that the issue may be coming from the startup code? The code here was ported from a project that originally uses is compiled on Linux system with GNU makefile. But the same code was successfully compiled and run on the same chip with Keil MDK and all other functionality like LEDs, GPIO ports seems to be running well.
Debugging the code with Keil I also have access to the register values if they're needed.
Timer2 for external triggering of ADC is configured like so:
static void tim2_config(int fs_divisor)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
/* TIM2 Periph clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
/* Time base configuration */
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Period = fs_divisor - 1;
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
/* TIM2 TRGO selection */
TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);
/* TIM2 enable counter */
TIM_Cmd(TIM2, ENABLE);
}
The ADC and DMA are then configured like so:
void adc_configure(){
ADC_InitTypeDef ADC_init_structure;
GPIO_InitTypeDef GPIO_initStructre;
DMA_InitTypeDef DMA_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// Clock configuration
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1ENR_GPIOAEN,ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
// Analog pin configuration ADC1->PA1, ADC2->PA2
GPIO_initStructre.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
GPIO_initStructre.GPIO_Mode = GPIO_Mode_AN;
GPIO_initStructre.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA,&GPIO_initStructre);
// ADC structure configuration
ADC_DeInit();
ADC_init_structure.ADC_DataAlign = ADC_DataAlign_Left;
ADC_init_structure.ADC_Resolution = ADC_Resolution_12b;
ADC_init_structure.ADC_ContinuousConvMode = DISABLE;
ADC_init_structure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO;
ADC_init_structure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;
ADC_init_structure.ADC_NbrOfConversion = 2;
ADC_init_structure.ADC_ScanConvMode = ENABLE;
ADC_Init(ADCx,&ADC_init_structure);
// Select the channel to be read from
ADC_RegularChannelConfig(ADCx,ADC_Channel_1,1,ADC_SampleTime_144Cycles);
ADC_RegularChannelConfig(ADCx,ADC_Channel_2,2,ADC_SampleTime_144Cycles);
//ADC_VBATCmd(ENABLE);
DMA_DeInit(DMA_STREAMx);
DMA_InitStructure.DMA_Channel = DMA_CHANNELx;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADCx_DR_ADDRESS;
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)adc_buf;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_BufferSize = ADC_BUF_SZ;
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_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA_STREAMx, &DMA_InitStructure);
/* Enable DMA request after last transfer (Single-ADC mode) */
ADC_DMARequestAfterLastTransferCmd(ADCx, ENABLE);
/* Enable ADC1 DMA */
ADC_DMACmd(ADCx, ENABLE);
/* DMA2_Stream0 enable */
DMA_Cmd(DMA_STREAMx, ENABLE);
/* Enable DMA Half & Complete interrupts */
DMA_ITConfig(DMA2_Stream0,DMA_IT_TC | DMA_IT_HT, ENABLE);
/* Enable the DMA Stream IRQ Channel */
NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// Enable and start ADC conversion
ADC_Cmd(ADC1,ENABLE);
ADC_SoftwareStartConv(ADC1);
}