I've been trying to learn through the STM32F3Discovery board how to use the ADC with interrupt-driven callback to move ADC data into a user-defined variable.
I have followed two sources to build my code. The Visual-GDB tutorial located here.
As well as the generic CubeMX setup for my device with the HAL library.
Here are relevant functions to my program:
System Clock Initialization
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;
RCC_PeriphCLKInitTypeDef PeriphClkInit;
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = 16;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL16;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC12|RCC_PERIPHCLK_ADC34;
PeriphClkInit.Adc12ClockSelection = RCC_ADC12PLLCLK_DIV1;
PeriphClkInit.Adc34ClockSelection = RCC_ADC34PLLCLK_DIV1;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}
ADC Initialization
static void MX_ADC1_Init(void)
{
ADC_MultiModeTypeDef multimode;
ADC_ChannelConfTypeDef sConfig;
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
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 = 1;
hadc1.Init.DMAContinuousRequests = DISABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc1.Init.LowPowerAutoWait = DISABLE;
hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
multimode.Mode = ADC_MODE_INDEPENDENT;
if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.SamplingTime = ADC_SAMPLETIME_601CYCLES_5;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 0;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
IRQ handling, callback & main program loop
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* AdcHandle)
{
adcVal1 = HAL_ADC_GetValue(AdcHandle);
}
void ADC_IRQHandler()
{
HAL_ADC_IRQHandler(&hadc1);
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_ADC1_Init();
HAL_ADC_Start_IT(&hadc1); // REF1
HAL_NVIC_EnableIRQ(ADC1_2_IRQn); // REF2
HAL_NVIC_SetPriority(ADC1_2_IRQn, 0, 0);
while (1)
{
}
}
My expectation is that my ADC should be set up in regular, constant conversion mode. Lines marked with REF1 and REF2 should enable end of conversion interrupts, which my ADC_IRQHandler
and HAL_ADC_ConvCpltCallback
should subsequently be invoked, storing my hadc1->Instance->DR
register value in variable adcVal1
.
When debugging, the hadc1->Instance->DR
register will update as expected, reading the correct voltage values that I set on the ADC channel. So I know that the ADC read is functioning correctly. When setting a breakpoint in my callback function, the function is never called, and so I can conclude there is an error in my logic in initiating or handling the ADC IRQ, so adcVal1
is always 0.
Can anyone provide correct setup of IRQ handling for my device, or point out my error?
The IRQ handler name ADC_IRQHandler
is correct for the STM32F407 used in the tutorial, which has a single ADC, but you are using a different part - the name differs, so your handler is not really a handler - just an unused function. If you do not override the appropriate handler with a function of the same name, the default "do nothing" handler will be invoked.
The symbol naming convention for IRQ handlers is straightforward, IRQ XXXXX_IRQn
, has a handler called XXXXX_IRQHandler
, so for ADC1_2_IRQn
, the handler is ADC1_2_IRQHandler
To be sure for your specific target, check the the interrupt handler weak symbols defined in the file normally named startup_stm32f3xxx.s (where the xxx part defines your specific target - startup_stm32f303x8.s for example).
Note that the header stm32sxxx.h (stm32f303x8.h for example):
/* Aliases for __IRQn */
#define ADC1_IRQn ADC1_2_IRQn
...
/* Aliases for __IRQHandler */
#define ADC1_IRQHandler ADC1_2_IRQHandler
...
Using these aliases makes sense only if you are not using the other peripheral with interrupts, since you can only have one handler for both peripherals.