2019-10-18 15:22:51 +01:00

623 lines
22 KiB
C

/**
******************************************************************************
* @file ADC/ADC_AnalogWatchdog/Src/main.c
* @author MCD Application Team
* @brief This example provides a short description of how to use the ADC
* peripheral to perform conversions with analog watchdog and
* interruptions. Other peripherals used: DMA, TIM (ADC group regular
* conversions triggered by TIM, ADC group regular conversion data
* transfered by DMA).
******************************************************************************
* @attention
*
* <h2><center>&copy; Copyright (c) 2016 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/** @addtogroup STM32F0xx_HAL_Examples
* @{
*/
/** @addtogroup ADC_AnalogWatchdog
* @{
*/
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define RANGE_12BITS ((uint32_t) 4095) /* Max digital value with a full range of 12 bits */
/* ADC parameters */
#define ADCCONVERTEDVALUES_BUFFER_SIZE ((uint32_t) 256) /* Size of array containing ADC converted values */
#if defined(ADC_TRIGGER_FROM_TIMER)
/* Timer for ADC trigger parameters */
#define TIMER_FREQUENCY ((uint32_t) 1000) /* Timer frequency (unit: Hz). With a timer 16 bits and time base freq min 1Hz, range is min=1Hz, max=32kHz. */
#define TIMER_FREQUENCY_RANGE_MIN ((uint32_t) 1) /* Timer minimum frequency (unit: Hz), used to calculate frequency range. With a timer 16 bits, maximum frequency will be 32000 times this value. */
#define TIMER_PRESCALER_MAX_VALUE (0xFFFF-1) /* Timer prescaler maximum value (0xFFFF for a timer 16 bits) */
#endif /* ADC_TRIGGER_FROM_TIMER */
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Peripherals handlers declaration */
/* ADC handler declaration */
ADC_HandleTypeDef AdcHandle;
#if defined(ADC_TRIGGER_FROM_TIMER)
/* TIM handler declaration */
TIM_HandleTypeDef TimHandle;
#endif /* ADC_TRIGGER_FROM_TIMER */
#if defined(WAVEFORM_VOLTAGE_GENERATION_FOR_TEST)
/* DAC handler declaration */
DAC_HandleTypeDef DacHandle; /* DAC used for waveform voltage generation for test */
#endif /* WAVEFORM_VOLTAGE_GENERATION_FOR_TEST */
/* 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 */
/* Variables to manage push button on board: interface between ExtLine interruption and main program */
__IO uint8_t ubUserButtonClickEvent = RESET; /* Event detection: Set after User Button interrupt */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void Error_Handler(void);
static void ADC_Config(void);
#if defined(ADC_TRIGGER_FROM_TIMER)
static void TIM_Config(void);
#endif /* ADC_TRIGGER_FROM_TIMER */
#if defined(WAVEFORM_VOLTAGE_GENERATION_FOR_TEST)
static void WaveformVoltageGenerationForTest_Config(void);
static void WaveformVoltageGenerationForTest_Update(void);
#endif /* WAVEFORM_VOLTAGE_GENERATION_FOR_TEST */
/* Private functions ---------------------------------------------------------*/
/**
* @brief Main program.
* @param None
* @retval None
*/
int main(void)
{
/* STM32F0xx HAL library initialization:
- Configure the Flash prefetch
- Systick timer is configured by default as source of time base, but user
can eventually implement his proper time base source (a general purpose
timer for example or other time source), keeping in mind that Time base
duration should be kept 1ms since PPP_TIMEOUT_VALUEs are defined and
handled in milliseconds basis.
- Low Level Initialization
*/
HAL_Init();
/* Configure the system clock to 48 MHz */
SystemClock_Config();
/*## Configure peripherals #################################################*/
/* Initialize LED on board */
BSP_LED_Init(LED2);
/* Configure User push-button in Interrupt mode */
BSP_PB_Init(BUTTON_USER, BUTTON_MODE_EXTI);
/* Configure the ADCx peripheral */
ADC_Config();
/* Run the ADC calibration */
if (HAL_ADCEx_Calibration_Start(&AdcHandle) != HAL_OK)
{
/* Calibration Error */
Error_Handler();
}
#if defined(ADC_TRIGGER_FROM_TIMER)
/* Configure the TIM peripheral */
TIM_Config();
#endif /* ADC_TRIGGER_FROM_TIMER */
#if defined(WAVEFORM_VOLTAGE_GENERATION_FOR_TEST)
/* Configure the DAC peripheral and generate a constant voltage of Vdda/2. */
WaveformVoltageGenerationForTest_Config();
#endif /* WAVEFORM_VOLTAGE_GENERATION_FOR_TEST */
/*## Enable peripherals ####################################################*/
#if defined(ADC_TRIGGER_FROM_TIMER)
/* Timer enable */
if (HAL_TIM_Base_Start(&TimHandle) != HAL_OK)
{
/* Counter Enable Error */
Error_Handler();
}
#endif /* ADC_TRIGGER_FROM_TIMER */
/*## Start ADC conversions #################################################*/
/* 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();
}
/* Infinite loop */
while (1)
{
/* Turn-on/off LED2 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)
{
BSP_LED_Off(LED2);
}
else
{
BSP_LED_On(LED2);
}
/* For information: ADC conversion results are stored into array */
/* "aADCxConvertedValues" (for debug: check into watch window) */
/* Wait for event on push button to perform following actions */
while ((ubUserButtonClickEvent) == RESET)
{
}
/* Reset variable for next loop iteration */
ubUserButtonClickEvent = RESET;
#if defined(WAVEFORM_VOLTAGE_GENERATION_FOR_TEST)
/* Modifies the voltage level incrementally from 0V to Vdda at each call. */
/* Circular waveform of ramp: When the maximum level is reaches, */
/* restart from 0V. */
WaveformVoltageGenerationForTest_Update();
#endif /* WAVEFORM_VOLTAGE_GENERATION_FOR_TEST */
/* Reset analog watchdog status */
ubAnalogWatchdogStatus = RESET;
/* Wait for voltage settling time */
HAL_Delay(1);
}
}
/**
* @brief System Clock Configuration
* The system Clock is configured as follow :
* System Clock source = PLL (HSI48)
* SYSCLK(Hz) = 48000000
* HCLK(Hz) = 48000000
* AHB Prescaler = 1
* APB1 Prescaler = 1
* HSI Frequency(Hz) = 48000000
* PREDIV = 2
* PLLMUL = 2
* Flash Latency(WS) = 1
* @param None
* @retval None
*/
void SystemClock_Config(void)
{
RCC_ClkInitTypeDef RCC_ClkInitStruct;
RCC_OscInitTypeDef RCC_OscInitStruct;
/* Select HSI48 Oscillator as PLL source */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI48;
RCC_OscInitStruct.HSI48State = RCC_HSI48_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI48;
RCC_OscInitStruct.PLL.PREDIV = RCC_PREDIV_DIV2;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct)!= HAL_OK)
{
/* Initialization Error */
while(1);
}
/* Select PLL as system clock source and configure the HCLK and PCLK1 clocks dividers */
RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1);
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1)!= HAL_OK)
{
/* Initialization Error */
while(1);
}
}
/**
* @brief ADC configuration
* @param None
* @retval None
*/
static void ADC_Config(void)
{
ADC_ChannelConfTypeDef sConfig;
ADC_AnalogWDGConfTypeDef AnalogWDGConfig;
/* Configuration of AdcHandle init structure: ADC parameters and regular group */
AdcHandle.Instance = ADCx;
AdcHandle.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
AdcHandle.Init.Resolution = ADC_RESOLUTION_12B;
AdcHandle.Init.DataAlign = ADC_DATAALIGN_RIGHT;
AdcHandle.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD; /* Sequencer will convert the number of channels configured below, successively from the lowest to the highest channel number */
AdcHandle.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
AdcHandle.Init.LowPowerAutoWait = DISABLE;
AdcHandle.Init.LowPowerAutoPowerOff = DISABLE;
#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.DiscontinuousConvMode = DISABLE; /* 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 */
AdcHandle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
#else
AdcHandle.Init.ExternalTrigConv = ADC_SOFTWARE_START; /* Software start to trig the 1st conversion manually, without external event */
AdcHandle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; /* Parameter discarded because trig of conversion by software start (no external event) */
#endif
AdcHandle.Init.DMAContinuousRequests = ENABLE; /* ADC-DMA continuous requests to match with DMA configured in circular mode */
AdcHandle.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
AdcHandle.Init.SamplingTimeCommon = ADC_SAMPLETIME_41CYCLES_5;
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 = ADCx_CHANNELa;
sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
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 = ADCx_CHANNELa;
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();
}
}
#if defined(ADC_TRIGGER_FROM_TIMER)
/**
* @brief TIM configuration
* @param None
* @retval None
*/
static void TIM_Config(void)
{
TIM_MasterConfigTypeDef master_timer_config;
RCC_ClkInitTypeDef clk_init_struct = {0}; /* Temporary variable to retrieve RCC clock configuration */
uint32_t latency; /* Temporary variable to retrieve Flash Latency */
uint32_t timer_clock_frequency = 0; /* Timer clock frequency */
uint32_t timer_prescaler = 0; /* Time base prescaler to have timebase aligned on minimum frequency possible */
/* Configuration of timer as time base: */
/* Caution: Computation of frequency is done for a timer instance on APB1 */
/* (clocked by PCLK1) */
/* Timer frequency is configured from the following constants: */
/* - TIMER_FREQUENCY: timer frequency (unit: Hz). */
/* - TIMER_FREQUENCY_RANGE_MIN: timer minimum frequency possible */
/* (unit: Hz). */
/* Note: Refer to comments at these literals definition for more details. */
/* Retrieve timer clock source frequency */
HAL_RCC_GetClockConfig(&clk_init_struct, &latency);
/* If APB1 prescaler is different of 1, timers have a factor x2 on their */
/* clock source. */
if (clk_init_struct.APB1CLKDivider == RCC_HCLK_DIV1)
{
timer_clock_frequency = HAL_RCC_GetPCLK1Freq();
}
else
{
timer_clock_frequency = HAL_RCC_GetPCLK1Freq() *2;
}
/* Timer prescaler calculation */
/* (computation for timer 16 bits, additional + 1 to round the prescaler up) */
timer_prescaler = (timer_clock_frequency / (TIMER_PRESCALER_MAX_VALUE * TIMER_FREQUENCY_RANGE_MIN)) +1;
/* Set timer instance */
TimHandle.Instance = TIMx;
/* Configure timer parameters */
TimHandle.Init.Period = ((timer_clock_frequency / (timer_prescaler * TIMER_FREQUENCY)) - 1);
TimHandle.Init.Prescaler = (timer_prescaler - 1);
TimHandle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
TimHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
TimHandle.Init.RepetitionCounter = 0x0;
TimHandle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&TimHandle) != HAL_OK)
{
/* Timer initialization Error */
Error_Handler();
}
/* Timer TRGO selection */
master_timer_config.MasterOutputTrigger = TIM_TRGO_UPDATE;
master_timer_config.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&TimHandle, &master_timer_config) != HAL_OK)
{
/* Timer TRGO selection Error */
Error_Handler();
}
}
#endif /* ADC_TRIGGER_FROM_TIMER */
#if defined(WAVEFORM_VOLTAGE_GENERATION_FOR_TEST)
/**
* @brief For this example, generate a waveform voltage on a spare DAC
* channel, so user has just to connect a wire between DAC channel
* (pin PA.04) and ADC channel (pin PA.04) to run this example.
* (this prevents the user from resorting to an external signal generator)
* This function configures the DAC and generates a constant voltage of Vdda/2.
* To modify the voltage level, use function "WaveformVoltageGenerationForTest_Update"
* @param None
* @retval None
*/
static void WaveformVoltageGenerationForTest_Config(void)
{
static DAC_ChannelConfTypeDef sConfig;
/*## Configure peripherals #################################################*/
/* Configuration of DACx peripheral */
DacHandle.Instance = DACx;
if (HAL_DAC_Init(&DacHandle) != HAL_OK)
{
/* DAC initialization error */
Error_Handler();
}
/* Configuration of DAC channel */
sConfig.DAC_Trigger = DAC_TRIGGER_NONE;
sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;
if (HAL_DAC_ConfigChannel(&DacHandle, &sConfig, DACx_CHANNEL_TO_ADCx_CHANNELa) != HAL_OK)
{
/* Channel configuration error */
Error_Handler();
}
/*## Enable peripherals ####################################################*/
/* Set DAC Channel data register: channel corresponding to ADC channel CHANNELa */
/* Set DAC output to 1/2 of full range (4095 <=> Vdda=3.3V): 2048 <=> 1.65V */
if (HAL_DAC_SetValue(&DacHandle, DACx_CHANNEL_TO_ADCx_CHANNELa, DAC_ALIGN_12B_R, RANGE_12BITS/2) != HAL_OK)
{
/* Setting value Error */
Error_Handler();
}
/* Enable DAC Channel: channel corresponding to ADC channel CHANNELa */
if (HAL_DAC_Start(&DacHandle, DACx_CHANNEL_TO_ADCx_CHANNELa) != HAL_OK)
{
/* Start Error */
Error_Handler();
}
}
/**
* @brief For this example, generate a waveform voltage on a spare DAC
* channel, so user has just to connect a wire between DAC channel
* (pin PA.04) and ADC channel (pin PA.04) to run this example.
* (this prevents the user from resorting to an external signal generator)
* This function modifies the voltage level from 0V to Vdda in 4 steps,
* incrementally at each function call.
* Circular waveform of ramp: When the maximum level is reaches,
* restart from 0V.
* @param None
* @retval None
*/
static void WaveformVoltageGenerationForTest_Update(void)
{
static uint8_t ub_dac_steps_count = 0; /* Count number of clicks: Incremented after User Button interrupt */
/* Set DAC voltage on channel corresponding to ADCx_CHANNELa */
/* in function of user button clicks count. */
/* Set DAC output on 5 voltage levels, successively to: */
/* - minimum of full range (0 <=> ground 0V) */
/* - 1/4 of full range (4095 <=> Vdda=3.3V): 1023 <=> 0.825V */
/* - 1/2 of full range (4095 <=> Vdda=3.3V): 2048 <=> 1.65V */
/* - 3/4 of full range (4095 <=> Vdda=3.3V): 3071 <=> 2.475V */
/* - maximum of full range (4095 <=> Vdda=3.3V) */
if (HAL_DAC_SetValue(&DacHandle,
DACx_CHANNEL_TO_ADCx_CHANNELa,
DAC_ALIGN_12B_R,
((RANGE_12BITS * ub_dac_steps_count) / 4)
) != HAL_OK)
{
/* Start Error */
Error_Handler();
}
/* Wait for voltage settling time */
HAL_Delay(1);
/* Manage ub_dac_steps_count to increment it in 4 steps and circularly. */
if (ub_dac_steps_count < 4)
{
ub_dac_steps_count++;
}
else
{
ub_dac_steps_count = 0;
}
}
#endif /* WAVEFORM_VOLTAGE_GENERATION_FOR_TEST */
/**
* @brief EXTI line detection callbacks
* @param GPIO_Pin: Specifies the pins connected EXTI line
* @retval None
*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin == USER_BUTTON_PIN)
{
/* Set variable to report push button event to main program */
ubUserButtonClickEvent = SET;
}
}
/**
* @brief Conversion complete callback in non blocking mode
* @param AdcHandle : ADC 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
*/
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *AdcHandle)
{
}
/**
* @brief Conversion DMA half-transfer callback in non blocking mode
* @param hadc: ADC handle
* @retval None
*/
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc)
{
}
/**
* @brief Analog watchdog callback in non blocking mode.
* @param hadc: ADC handle
* @retval None
*/
void HAL_ADC_LevelOutOfWindowCallback(ADC_HandleTypeDef* hadc)
{
/* 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
*/
void HAL_ADC_ErrorCallback(ADC_HandleTypeDef *hadc)
{
/* In case of ADC error, call main error handler */
Error_Handler();
}
/**
* @brief This function is executed in case of error occurrence.
* @param None
* @retval None
*/
static void Error_Handler(void)
{
/* User may add here some code to deal with a potential error */
/* In case of error, LED2 is toggling at a frequency of 1Hz */
while(1)
{
/* Toggle LED2 */
BSP_LED_Toggle(LED2);
HAL_Delay(500);
}
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* Infinite loop */
while (1)
{
}
}
#endif
/**
* @}
*/
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/