Estoy usando un STM32F103C8 para leer 3 canales ADC, y he utilizado CubeMX + HAL para configurar el ADC para lanzar los valores ADC en un búfer.
He podido conseguirlo con DMA y polling: He visto ambos como formas generalmente aceptables para hacer esto en línea. Sin embargo, no he sido capaz de encontrar una configuración que me permita escanear a través de los canales iniciando manualmente las conversiones en la interrupción EOC. En todos mis intentos, o bien no avanza canales, o la interrupción no se dispara en absoluto.
Preferiría utilizar interrupciones porque mi único canal DMA en el dispositivo se utiliza para almacenar en búfer algunos datos de audio de frecuencia bastante alta, pero también estoy preocupado porque no puedo entender la forma en que las interrupciones ADC trabajan en conjunto con el modo de exploración. He utilizado los siguientes enfoques:
-
DMA : Esta parece ser la forma autorizada de escanear varios canales y almacenar sus respectivos resultados. En particular, el manual del usuario en §11.3.8 ¶3 dice:
Cuando se utiliza el modo scan, el bit DMA debe estar activado y el controlador de acceso directo a memoria se utiliza para transferir los datos convertidos de los canales de grupo regulares a la SRAM después de cada actualización del registro ADC_DR.
He conseguido que funcione con los ajustes intuitivos de CubeMX:
- ADC_Settings:
- Modo de conversión de escáner: Activado
- Modo continuo: Activado
- Modo discontinuo: Desactivado
- ADC_Regular_Conversion_Mode
- Activar conversiones regulares: Activar
- Número de conversiones: 3
- Fuente de conversión de disparo externo: Conversión regular lanzada por software
- <configuraciones de canales y rangos...>
más un DMA circular alineado por media palabra, y una llamada directa a
HAL_ADC_Start_DMA()
en la fuente. - ADC_Settings:
-
Sondeo : He intentado lo siguiente esta respuesta que desactiva los modos continuo y discontinuo, y es capaz de recorrer los canales con sucesivas llamadas a
HAL_ADC_PollForConversion
solo. Descubrí que necesitaba activar el modo discontinuo con tamaños de grupo de 1, es decir:hadc1.Init.DiscontinuousConvMode = ENABLE; hadc1.Init.NbrOfDiscConversion = 1;
A continuación, recorriendo los canales con
HAL_ADC_PollForConversion
funcionó sin problemas. -
Interrupciones: He probado todas las permutaciones de modo de exploración, modo discontinuo y número de conversiones discontinuas, y ninguna de ellas me permite recorrer los canales en el
HAL_ADC_ConvCpltCallback
rutina de interrupción. Aquí está la rutina que estoy usando:#define NUM_ADC_BUF 8 #define NUM_ADC_CH 3 volatile uint16_t adc_buf[NUM_ADC_BUF][NUM_ADC_CH]; void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) { if(hadc->Instance == ADC1) { adc_buf[adc_buf_idx & (NUM_ADC_BUF - 1)][adc_ch++] = HAL_ADC_GetValue(hadc); if(adc_ch == NUM_ADC_CH) { adc_ch = 0; adc_buf_idx++; } HAL_ADC_Start_IT(hadc); } }
Dentro del ADC_Settings en CubeMX, aquí están mis intentos y sus resultados:
+-------------------+-----------+--------------------+---------------------+------------------------------------------------------+ | (Continuous mode) | Scan mode | Discontinuous mode | Number of | Outcome | | | | | discontinuous conv. | | +-------------------+-----------+--------------------+---------------------+------------------------------------------------------+ | DISABLED | ENABLED | ENABLED | 3 | Only highest-rank-# (rank 3) channel result is given | | DISABLED | ENABLED | ENABLED | 1 | Interrupt never fires: EOC never set | | DISABLED | ENABLED | DISABLED | N/A | Only highest-rank-# (rank 3) channel result is given | | DISABLED | DISABLED | ENABLED | 3 | Only lowest-rank-# (rank 1) channel result is given | | DISABLED | DISABLED | ENABLED | 1 | Only lowest-rank-# (rank 1) channel result is given | | DISABLED | DISABLED | DISABLED | N/A | Only lowest-rank-# (rank 1) channel result is given | +-------------------+-----------+--------------------+---------------------+------------------------------------------------------+
Como puede ver, ninguna combinación funciona correctamente. ¿Es simplemente imposible? Supongo que puedo tomar el extracto que he citado del manual de usuario como que la única forma de usar el escaneo es el DMA, y que el sondeo funciona como una característica no soportada formalmente. ¿Es esto cierto?
Como referencia, aquí está mi auto-generado sin tocar
adc.c
de CubeMX:/* Includes ------------------------------------------------------------------*/ #include "adc.h" /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ ADC_HandleTypeDef hadc1; /* ADC1 init function */ void MX_ADC1_Init(void) { ADC_ChannelConfTypeDef sConfig = {0}; /** Common config */ hadc1.Instance = ADC1; hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE; hadc1.Init.ContinuousConvMode = DISABLE; hadc1.Init.DiscontinuousConvMode = ENABLE; hadc1.Init.NbrOfDiscConversion = 3; hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START; hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT; hadc1.Init.NbrOfConversion = 3; if (HAL_ADC_Init(&hadc1) != HAL_OK) { Error_Handler(); } /** Configure Regular Channel */ sConfig.Channel = ADC_CHANNEL_4; sConfig.Rank = ADC_REGULAR_RANK_1; sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5; if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) { Error_Handler(); } /** Configure Regular Channel */ sConfig.Channel = ADC_CHANNEL_8; sConfig.Rank = ADC_REGULAR_RANK_2; if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) { Error_Handler(); } /** Configure Regular Channel */ sConfig.Channel = ADC_CHANNEL_9; sConfig.Rank = ADC_REGULAR_RANK_3; if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) { Error_Handler(); } } void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle) { GPIO_InitTypeDef GPIO_InitStruct = {0}; if(adcHandle->Instance==ADC1) { /* USER CODE BEGIN ADC1_MspInit 0 */ /* USER CODE END ADC1_MspInit 0 */ /* ADC1 clock enable */ __HAL_RCC_ADC1_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); /**ADC1 GPIO Configuration PA4 ------> ADC1_IN4 PB0 ------> ADC1_IN8 PB1 ------> ADC1_IN9 */ GPIO_InitStruct.Pin = Xin_Pin; GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; HAL_GPIO_Init(Xin_GPIO_Port, &GPIO_InitStruct); GPIO_InitStruct.Pin = Zin_Pin|Yin_Pin; GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); /* ADC1 interrupt Init */ HAL_NVIC_SetPriority(ADC1_2_IRQn, 0, 0); HAL_NVIC_EnableIRQ(ADC1_2_IRQn); /* USER CODE BEGIN ADC1_MspInit 1 */ /* USER CODE END ADC1_MspInit 1 */ } } void HAL_ADC_MspDeInit(ADC_HandleTypeDef* adcHandle) { if(adcHandle->Instance==ADC1) { /* USER CODE BEGIN ADC1_MspDeInit 0 */ /* USER CODE END ADC1_MspDeInit 0 */ /* Peripheral clock disable */ __HAL_RCC_ADC1_CLK_DISABLE(); /**ADC1 GPIO Configuration PA4 ------> ADC1_IN4 PB0 ------> ADC1_IN8 PB1 ------> ADC1_IN9 */ HAL_GPIO_DeInit(Xin_GPIO_Port, Xin_Pin); HAL_GPIO_DeInit(GPIOB, Zin_Pin|Yin_Pin); /* ADC1 interrupt Deinit */ HAL_NVIC_DisableIRQ(ADC1_2_IRQn); /* USER CODE BEGIN ADC1_MspDeInit 1 */ /* USER CODE END ADC1_MspDeInit 1 */ } } /* USER CODE BEGIN 1 */ /* USER CODE END 1 */