El código aquí es la configuración de DMA2 para transferir 160 muestras cada una desde dos canales analógicos desde ADC1 a la memoria.
El ADC, DMA, NVIC, etc. parecen configurados correctamente, pero por alguna razón, estoy obteniendo errores de transferencia exactamente cuando se enciende el ADC.
Más precisamente, el indicador TEIF0 para DMA2 y el indicador OVR para ADC1 se activan exactamente después de llamar a ADC_Cmd en el segundo fragmento de código.
Al buscar las causas de los errores de Transferencia en RM0090, todo lo que dice es:
Error de transferencia: el indicador de interrupción de error de transferencia (TEIFx) se establece cuando:
: se produce un error de bus durante una lectura DMA o un acceso de escritura
- El software solicita un acceso de escritura en un registro de dirección de memoria en Doble modo de búfer mientras que el flujo está habilitado y la memoria de destino actual es la uno impactado por la escritura en el registro de direcciones de memoria (consulte Sección 10.3.9: Modo de doble búfer)
La definición no es muy informativa. Puede encontrar un enlace a un problema similar aquí: enlace
¿es posible que el problema provenga del código de inicio? El código aquí fue portado desde un proyecto que originalmente se compila en un sistema Linux con GNU makefile. Pero el mismo código se compiló con éxito y se ejecutó en el mismo chip con Keil MDK y todas las demás funciones como LED, los puertos GPIO parecen funcionar bien.
Depurando el código con Keil, también tengo acceso a los valores de registro si son necesarios.
Timer2 para la activación externa de ADC se configura así:
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);
}
El ADC y el DMA se configuran así:
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);
}