[STM32F1] ADC_AnalogWatchdog example
Posted: Thu Jan 16, 2020 5:30 pm
Hi all,
I've converted an example from STM32F1Cube to Arduino sketch using the STM32 Core.
The Cube example is the ADC_AnalogWatchdog which use several feature ADC, DMA, watchdog for the Nucleo F103RB.
It is available here:
https://github.com/STMicroelectronics/S ... ogWatchdog
To achieve this I simply disable the HAL ADC usage by the arduino API by defining -DHAL_ADC_MODULE_ONLY in build_opt.h file.
Then I mainly copy all required code and prefixed functions by extern "C" .
For some GPIO config and write, I've used the standard API.
Main has been splitted in the setup() and loop() of the sketch.
I've converted an example from STM32F1Cube to Arduino sketch using the STM32 Core.
The Cube example is the ADC_AnalogWatchdog which use several feature ADC, DMA, watchdog for the Nucleo F103RB.
It is available here:
https://github.com/STMicroelectronics/S ... ogWatchdog
To achieve this I simply disable the HAL ADC usage by the arduino API by defining -DHAL_ADC_MODULE_ONLY in build_opt.h file.
Then I mainly copy all required code and prefixed functions by extern "C" .
For some GPIO config and write, I've used the standard API.
Main has been splitted in the setup() and loop() of the sketch.
Code: Select all
/* Private define ------------------------------------------------------------*/
#define ADCCONVERTEDVALUES_BUFFER_SIZE ((uint32_t) 256) /* Size of array containing ADC converted values */
#define RANGE_12BITS ((uint32_t) 4095) /* Max value with a full range of 12 bits */
/* Private variables ---------------------------------------------------------*/
/* ADC handler declaration */
ADC_HandleTypeDef AdcHandle;
/* Variable containing ADC conversions results */
__IO uint16_t aADCxConvertedValues[ADCCONVERTEDVALUES_BUFFER_SIZE];
/* Variable to report ADC analog watchdog status: */
/* RESET <=> voltage into AWD window */
/* SET <=> voltage out of AWD window */
uint8_t ubAnalogWatchdogStatus = RESET; /* Set into analog watchdog interrupt callback */
/**
@brief This function handles ADC interrupt request.
@param None
@retval None
*/
extern "C" void ADC1_2_IRQHandler(void)
{
HAL_ADC_IRQHandler(&AdcHandle);
}
/**
@brief This function handles DMA interrupt request.
@param None
@retval None
*/
extern "C" void DMA1_Channel1_IRQHandler(void)
{
HAL_DMA_IRQHandler(AdcHandle.DMA_Handle);
}
/**
@brief ADC MSP initialization
This function configures the hardware resources used in this example:
- Enable clock of ADC peripheral
- Configure the GPIO associated to the peripheral channels
- Configure the DMA associated to the peripheral
- Configure the NVIC associated to the peripheral interruptions
@param hadc: ADC handle pointer
@retval None
*/
extern "C" void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
{
GPIO_InitTypeDef GPIO_InitStruct;
static DMA_HandleTypeDef DmaHandle;
RCC_PeriphCLKInitTypeDef PeriphClkInit;
/*##-1- Enable peripherals and GPIO Clocks #################################*/
/* Enable clock of GPIO associated to the peripheral channels */
// __HAL_RCC_GPIOA_CLK_ENABLE();
/* Enable clock of ADCx peripheral */
__HAL_RCC_ADC1_CLK_ENABLE();
/* Configure ADCx clock prescaler */
/* Caution: On STM32F1, ADC clock frequency max is 14MHz (refer to device */
/* datasheet). */
/* Therefore, ADC clock prescaler must be configured in function */
/* of ADC clock source frequency to remain below this maximum */
/* frequency. */
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;
HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);
/* Enable clock of DMA associated to the peripheral */
__HAL_RCC_DMA1_CLK_ENABLE();
/*##-2- Configure peripheral GPIO ##########################################*/
/* Configure GPIO pin of the selected ADC channel */
GPIO_InitStruct.Pin = ADC_CHANNEL_4;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_PIN_4;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*##-3- Configure the DMA ##################################################*/
/* Configure DMA parameters */
DmaHandle.Instance = DMA1_Channel1;
DmaHandle.Init.Direction = DMA_PERIPH_TO_MEMORY;
DmaHandle.Init.PeriphInc = DMA_PINC_DISABLE;
DmaHandle.Init.MemInc = DMA_MINC_ENABLE;
DmaHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; /* Transfer from ADC by half-word to match with ADC configuration: ADC resolution 10 or 12 bits */
DmaHandle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; /* Transfer to memory by half-word to match with buffer variable type: half-word */
DmaHandle.Init.Mode = DMA_CIRCULAR; /* DMA in circular mode to match with ADC configuration: DMA continuous requests */
DmaHandle.Init.Priority = DMA_PRIORITY_HIGH;
/* Deinitialize & Initialize the DMA for new transfer */
HAL_DMA_DeInit(&DmaHandle);
HAL_DMA_Init(&DmaHandle);
/* Associate the initialized DMA handle to the ADC handle */
__HAL_LINKDMA(hadc, DMA_Handle, DmaHandle);
/*##-4- Configure the NVIC #################################################*/
/* NVIC configuration for DMA interrupt (transfer completion or error) */
/* Priority: high-priority */
HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
/* NVIC configuration for ADC interrupt */
/* Priority: high-priority */
HAL_NVIC_SetPriority(ADC1_2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(ADC1_2_IRQn);
}
/**
@brief ADC MSP de-initialization
This function frees the hardware resources used in this example:
- Disable clock of ADC peripheral
- Revert GPIO associated to the peripheral channels to their default state
- Revert DMA associated to the peripheral to its default state
- Revert NVIC associated to the peripheral interruptions to its default state
@param hadc: ADC handle pointer
@retval None
*/
extern "C" void HAL_ADC_MspDeInit(ADC_HandleTypeDef *hadc)
{
/*##-1- Reset peripherals ##################################################*/
__HAL_RCC_ADC1_FORCE_RESET();
__HAL_RCC_ADC1_RELEASE_RESET();
/*##-2- Disable peripherals and GPIO Clocks ################################*/
/* De-initialize GPIO pin of the selected ADC channel */
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_4);
/*##-3- Disable the DMA ####################################################*/
/* De-Initialize the DMA associated to the peripheral */
if (hadc->DMA_Handle != NULL)
{
HAL_DMA_DeInit(hadc->DMA_Handle);
}
/*##-4- Disable the NVIC ###################################################*/
/* Disable the NVIC configuration for DMA interrupt */
HAL_NVIC_DisableIRQ(DMA1_Channel1_IRQn);
/* Disable the NVIC configuration for ADC interrupt */
HAL_NVIC_DisableIRQ(ADC1_2_IRQn);
}
/**
@brief ADC configuration
@param None
@retval None
*/
static void ADC_Config(void)
{
ADC_ChannelConfTypeDef sConfig;
ADC_AnalogWDGConfTypeDef AnalogWDGConfig;
/* Configuration of ADCx init structure: ADC parameters and regular group */
AdcHandle.Instance = ADC1;
AdcHandle.Init.DataAlign = ADC_DATAALIGN_RIGHT;
AdcHandle.Init.ScanConvMode = ADC_SCAN_DISABLE; /* Sequencer disabled (ADC conversion on only 1 channel: channel set on rank 1) */
#if defined ADC_TRIGGER_FROM_TIMER
AdcHandle.Init.ContinuousConvMode = DISABLE; /* Continuous mode disabled to have only 1 conversion at each conversion trig */
#else
AdcHandle.Init.ContinuousConvMode = ENABLE; /* Continuous mode to have maximum conversion speed (no delay between conversions) */
#endif
AdcHandle.Init.NbrOfConversion = 1; /* Parameter discarded because sequencer is disabled */
AdcHandle.Init.DiscontinuousConvMode = DISABLE; /* Parameter discarded because sequencer is disabled */
AdcHandle.Init.NbrOfDiscConversion = 1; /* Parameter discarded because sequencer is disabled */
#if defined ADC_TRIGGER_FROM_TIMER
AdcHandle.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_Tx_TRGO; /* Trig of conversion start done by external event */
#else
AdcHandle.Init.ExternalTrigConv = ADC_SOFTWARE_START; /* Software start to trig the 1st conversion manually, without external event */
#endif
if (HAL_ADC_Init(&AdcHandle) != HAL_OK)
{
/* ADC initialization error */
Error_Handler();
}
/* Configuration of channel on ADCx regular group on sequencer rank 1 */
/* Note: Considering IT occurring after each ADC conversion if ADC */
/* conversion is out of the analog watchdog window selected (ADC IT */
/* enabled), select sampling time and ADC clock with sufficient */
/* duration to not create an overhead situation in IRQHandler. */
sConfig.Channel = ADC_CHANNEL_4;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_41CYCLES_5;
if (HAL_ADC_ConfigChannel(&AdcHandle, &sConfig) != HAL_OK)
{
/* Channel Configuration Error */
Error_Handler();
}
/* Set analog watchdog thresholds in order to be between steps of DAC */
/* voltage. */
/* - High threshold: between DAC steps 1/2 and 3/4 of full range: */
/* 5/8 of full range (4095 <=> Vdda=3.3V): 2559<=> 2.06V */
/* - Low threshold: between DAC steps 0 and 1/4 of full range: */
/* 1/8 of full range (4095 <=> Vdda=3.3V): 512 <=> 0.41V */
/* Analog watchdog 1 configuration */
AnalogWDGConfig.WatchdogMode = ADC_ANALOGWATCHDOG_ALL_REG;
AnalogWDGConfig.Channel = ADC_CHANNEL_4;
AnalogWDGConfig.ITMode = ENABLE;
AnalogWDGConfig.HighThreshold = (RANGE_12BITS * 5 / 8);
AnalogWDGConfig.LowThreshold = (RANGE_12BITS * 1 / 8);
if (HAL_ADC_AnalogWDGConfig(&AdcHandle, &AnalogWDGConfig) != HAL_OK)
{
/* Channel Configuration Error */
Error_Handler();
}
}
/**
@brief Conversion complete callback in non blocking mode
@param AdcHandle : AdcHandle handle
@note This example shows a simple way to report end of conversion
and get conversion result. You can add your own implementation.
@retval None
*/
extern "C" void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef * ) {
}
/**
@brief Conversion DMA half-transfer callback in non blocking mode
@param hadc: ADC handle
@retval None
*/
extern "C" void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef * ) {
}
/**
@brief Analog watchdog callback in non blocking mode.
@param hadc: ADC handle
@retval None
*/
extern "C" void HAL_ADC_LevelOutOfWindowCallback(ADC_HandleTypeDef * ) {
/* Set variable to report analog watchdog out of window status to main */
/* program. */
ubAnalogWatchdogStatus = SET;
}
/**
@brief ADC error callback in non blocking mode
(ADC conversion with interruption or transfer by DMA)
@param hadc: ADC handle
@retval None
*/
extern "C" void HAL_ADC_ErrorCallback(ADC_HandleTypeDef * )
{
/* In case of ADC error, call main error handler */
Error_Handler();
}
// the setup routine runs once when you press reset:
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
pinMode(PA4, INPUT_ANALOG);
/* Configure the ADC peripheral */
ADC_Config();
/* Run the ADC calibration */
if (HAL_ADCEx_Calibration_Start(&AdcHandle) != HAL_OK) {
/* Calibration Error */
Error_Handler();
}
/* Start ADC conversion on regular group with transfer by DMA */
if (HAL_ADC_Start_DMA(&AdcHandle,
(uint32_t *)aADCxConvertedValues,
ADCCONVERTEDVALUES_BUFFER_SIZE
) != HAL_OK) {
/* Start Error */
Error_Handler();
}
}
// the loop routine runs over and over again forever:
void loop() {
/* Turn-on/off LED_BUILTIN in function of ADC conversion result */
/* - Turn-off if voltage is into AWD window */
/* - Turn-on if voltage is out of AWD window */
/* Variable of analog watchdog status is set into analog watchdog */
/* interrupt callback */
if (ubAnalogWatchdogStatus == RESET) {
digitalWrite(LED_BUILTIN, LOW); // turn the LED on (HIGH is the voltage level)
}
else {
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
/* Reset analog watchdog status for next loop iteration */
ubAnalogWatchdogStatus = RESET;
}
}