Estoy leyendo ADC a través de DMA en el STM32 Nucleo F334R8. Sin embargo, después de iniciar el DMA con el siguiente código, el ciclo while principal no se ejecuta
HAL_ADC_Start_DMA(&hadc1, (uint32_t *)&ADC_Raw, 4);
Entiendo que las interrupciones de DMA pueden ocurrir con tanta frecuencia que es posible que no permitan que ocurran otras interrupciones, por lo que he cambiado la prioridad de DMA para que sea lo más baja posible:
HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 15);
HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
Sé que se están manejando otras interrupciones ya que los siguientes módulos están operativos:
No entiendo por qué el ciclo while principal no se ejecutará si el ADC DMA está habilitado. Si comento el código a continuación, se ejecutará el ciclo while principal
HAL_ADC_Start_DMA(&hadc1, (uint32_t *)&ADC_Raw, 4);
Aquí está la configuración del ADC:
void MX_ADC1_Init(void)
{
ADC_MultiModeTypeDef multimode;
ADC_ChannelConfTypeDef sConfig;
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = ENABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 4;
hadc1.Init.DMAContinuousRequests = ENABLE;
hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
hadc1.Init.LowPowerAutoWait = DISABLE;
hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
if(HAL_ADC_Init(&hadc1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
if(HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure the ADC multi-mode
*/
multimode.Mode = ADC_MODE_INDEPENDENT;
if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = 1;
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.SamplingTime = ADC_SAMPLETIME_19CYCLES_5;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 0;
if(HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_2;
sConfig.Rank = 2;
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.SamplingTime = ADC_SAMPLETIME_19CYCLES_5;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 0;
if(HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_6;
sConfig.Rank = 3;
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.SamplingTime = ADC_SAMPLETIME_19CYCLES_5;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 0;
if(HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_7;
sConfig.Rank = 4;
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.SamplingTime = ADC_SAMPLETIME_19CYCLES_5;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 0;
if(HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(adcHandle->Instance == ADC1)
{
/* USER CODE BEGIN ADC1_MspInit 0 */
/* USER CODE END ADC1_MspInit 0 */
/* ADC1 clock enable */
__HAL_RCC_ADC12_CLK_ENABLE()
;
/**ADC1 GPIO Configuration
PC0 ------> ADC1_IN6
PC1 ------> ADC1_IN7
PA0 ------> ADC1_IN1
PA1 ------> ADC1_IN2
*/
GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* ADC1 DMA Init */
/* ADC1 Init */
hdma_adc1.Instance = DMA1_Channel1;
hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
hdma_adc1.Init.Mode = DMA_CIRCULAR;
hdma_adc1.Init.Priority = DMA_PRIORITY_LOW;
if(HAL_DMA_Init(&hdma_adc1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
__HAL_LINKDMA(adcHandle, DMA_Handle, hdma_adc1);
/* ADC1 interrupt Init */
HAL_NVIC_SetPriority(ADC1_2_IRQn, 0, 14);
HAL_NVIC_EnableIRQ(ADC1_2_IRQn);
/* USER CODE BEGIN ADC1_MspInit 1 */
/* USER CODE END ADC1_MspInit 1 */
/* USER CODE BEGIN ADC1_MspInit 1 */
/* USER CODE END ADC1_MspInit 1 */
}
}
Aquí está la configuración de DMA:
void MX_DMA_Init(void)
{
/* DMA controller clock enable */
__HAL_RCC_DMA1_CLK_ENABLE();
/* DMA interrupt init */
/* DMA1_Channel1_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 15);
HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
}
Aquí está lo principal:
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration----------------------------------------------------------*/
CPU_CACHE_Enable();
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_ADC1_Init();
MX_I2C1_Init();
MX_SPI1_Init();
MX_TIM1_Init();
MX_TIM3_Init();
MX_TIM6_Init();
MX_USART2_UART_Init();
HAL_ADC_Start_DMA(&hadc1, (uint32_t *)&ADC_Raw, 4);
my_printf("Version 1.0\n");
InitializeControllerCommand();
while (1)
{
if(start_pwm == true)
{
fade_LED();
}
if(readInput == 1)
{
readInputPin();
}
}
}
Entiendo que las interrupciones de DMA pueden ocurrir con tanta frecuencia que es posible que no permitan que ocurran otras interrupciones, por lo que he cambiado la prioridad de DMA para que sea lo más baja posible.
[...]
No entiendo por qué el ciclo while principal no se ejecutará si el ADC DMA está habilitado.
Si las interrupciones de DMA ocurrieran con tanta frecuencia que evitarían que se ejecutaran otros controladores de interrupciones, no permitirían que el código principal se ejecutara también.
Es posible configurar el programa principal para que se ejecute con una prioridad más alta que ciertas interrupciones, con la __set_PRIMASK()
función CMSIS, que se traduciría en una MSR PRIMASK, Rx
instrucción, pero esto deshabilitaría efectivamente todas las interrupciones de menor prioridad, porque el programa principal nunca termina (a menos que llegue una falla irrecuperable).
En mi opinión, deberías
Aumento del tiempo de muestreo
El tiempo de muestra para cada canal se puede ajustar en los registros ADC->SMPR1
y ADC->SMPR2
, o configurando el SamplingTime
campo en la estructura de inicialización del canal, de 1,5 a 601,5 ciclos de reloj ADC.
Reducir la frecuencia del reloj ADC
El reloj ADC se puede cronometrar desde el reloj AHB, opcionalmente dividido por 2 o 4. Esto está controlado por los CKMODE
campos del ADC->CCR
registro, o por el Init.ClockPrescaler
campo de la estructura de inicialización ADC.
Alternativamente, el ADC puede sincronizarse desde el PLL principal, opcionalmente dividido por 2, 4, 6, 8, 10, 12, 16, 32, 64, 128 o 256. Los CKMODE
bits o Init.ClockPrescaler
deben ser 0 ( ADC_CLOCK_ASYNC_DIV1
), y el divisor puede seleccionarse en RCC->CFGR2
, o establecido porHAL_RCCEx_PeriphCLKConfig()
Los métodos anteriores se basan en aumentar el tiempo necesario para cada conversión en la secuencia, esto podría no ser adecuado para algunas aplicaciones, donde los canales deben muestrearse en un intervalo de tiempo corto, para obtener resultados más o menos sincrónicos.
Aumentar el intervalo entre secuencias de conversión
La secuencia de conversión de ADC también puede iniciarse mediante un evento de temporizador. En este caso, se debe desactivar el modo de conversión continua y se debe seleccionar una fuente de eventos en los bits EXTEN
y EXTSEL
del ADC->CFGR
registro. Las posibles fuentes de eventos se enumeran en el Capítulo 13.3.18 Conversión en disparo externo y polaridad de disparo en el Manual de referencia . Los temporizadores ofrecen una gran flexibilidad para seleccionar una frecuencia de muestreo adecuada, pero son un recurso limitado. Si una frecuencia de muestreo de 1 kHz o inferior fuera suficiente, entonces la forma más sencilla sería iniciar la conversión cada vez desde el controlador de interrupción periódica, si tiene uno.
sin transitorios
siguió a Mónica a Codidact
sin transitorios
siguió a Mónica a Codidact
miguel marrón