/** @addtogroup adc_file ADC peripheral API * @ingroup peripheral_apis * * based on F3 file * * @date 14 July 2013 * * LGPL License Terms @ref lgpl_license */ /* * This file is part of the libopencm3 project. * * Copyright (C) 2012 Ken Sarkies * * This library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library. If not, see . */ #include #include /**@{*/ /*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /** * @defgroup adc_api_opmode ADC Operation mode API * @ingroup adc_file * * @brief ADC Result API * *@{*/ /*---------------------------------------------------------------------------*/ /** @brief ADC Enable Discontinuous Mode for Regular Conversions * * @param[in] adc Unsigned int32. ADC base address (@ref adc_reg_base) */ void adc_enable_discontinuous_mode(uint32_t adc) { ADC_CFGR1(adc) |= ADC_CFGR1_DISCEN; } /*---------------------------------------------------------------------------*/ /** @brief ADC Disable Discontinuous Mode for Regular Conversions * * @param[in] adc Unsigned int32. ADC base address (@ref adc_reg_base) */ void adc_disable_discontinuous_mode(uint32_t adc) { ADC_CFGR1(adc) &= ~ADC_CFGR1_DISCEN; } /*---------------------------------------------------------------------------*/ /** ADC Set operation mode * * There are some operation modes, common for entire stm32 branch. In the text * the braces are describing result to single trigger event. The trigger event * is described by character T in the description. The ADC is configured to * convert list of inputs [0, 1, 2, 3]. In Grouped modes, there is used group * size of 2 conversions in the examples * * @li @c ADC_MODE_SEQUENTIAL: T(0) T(1) T(2) T(3)[EOSEQ] T(0) T(1) T(2) ... * * In this mode, after the trigger event a single channel is converted and the * next channel in the list is prepared to convert on next trigger edge. * * @note This mode can be emulated by ADC_MODE_GROUPED with group size * of 1. * * @li @c ADC_MODE_SCAN: T(0123)[EOSEQ] T(0123)[EOSEQ] T(0123)[EOSEQ] * * In this mode, after the trigger event, all channels will be converted once, * storing results sequentially. * * @note The DMA must be configured properly for more than single channel to * convert. * * @li @c ADC_MODE_SCAN_INFINITE: T(0123[EOSEQ]0123[EOSEQ]0123[EOSEQ]...) * * In this mode, after the trigger event, all channels from the list are * converted. At the end of list, the conversion continues from the beginning. * * @note The DMA must be configured properly to operate in this mode.@par * * @li @c ADC_MODE_GROUPED: T(12) T(34)[EOSEQ] T(12) T(34)[EOSEQ] T(12) * * In this mode, after the trigger event, a specified group size of channels * are converted. If the end of channel list occurs, the EOSEQ is generated * and on the next trigger it wraps to the beginning. * * @note The DMA must be configured properly to operate on more than single * channel conversion groups. * * @warning not all families supports all modes of operation of ADC. * */ /*---------------------------------------------------------------------------*/ /** @brief ADC Set conversion operation mode * * @note on SEQUENTIAL mode, the trigger event is necessary to start conversion. * * @param[in] adc Unsigned int32. ADC base address (@ref adc_reg_base) * @param[in] opmode ADC operation mode */ void adc_set_operation_mode(uint32_t adc, enum adc_opmode opmode) { switch (opmode) { case ADC_MODE_SEQUENTIAL: ADC_CFGR1(adc) &= ~ADC_CFGR1_CONT; ADC_CFGR1(adc) |= ADC_CFGR1_DISCEN; break; case ADC_MODE_SCAN: ADC_CFGR1(adc) &= ~(ADC_CFGR1_CONT | ADC_CFGR1_DISCEN); break; case ADC_MODE_SCAN_INFINITE: ADC_CFGR1(adc) &= ~ADC_CFGR1_DISCEN; ADC_CFGR1(adc) |= ADC_CFGR1_CONT; break; } } /**@}*/ /*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /** * @defgroup adc_api_trigger ADC Trigger API * @ingroup adc_file * * @brief ADC Trigger API * *@{*/ /*---------------------------------------------------------------------------*/ /** @brief ADC Enable an External Trigger for Regular Channels * * This enables an external trigger for set of defined regular channels, and * sets the polarity of the trigger event: rising or falling edge or both. Note * that if the trigger polarity is zero, triggering is disabled. * * @param[in] adc peripheral of choice (@ref adc_reg_base) * @param[in] trigger external trigger @ref adc_cfgr1_extsel * @param[in] polarity Trigger polarity @ref adc_cfgr1_exten */ void adc_enable_external_trigger_regular(uint32_t adc, uint32_t trigger, uint32_t polarity) { uint32_t reg = ADC_CFGR1(adc); reg &= ~(ADC_CFGR1_EXTSEL_MASK << ADC_CFGR1_EXTSEL_SHIFT); reg &= ~(ADC_CFGR1_EXTEN_MASK); reg |= polarity | (trigger << ADC_CFGR1_EXTSEL_SHIFT); ADC_CFGR1(adc) = reg; } /*---------------------------------------------------------------------------*/ /** @brief ADC Disable an External Trigger for Regular Channels * * @param[in] adc Unsigned int32. ADC base address (@ref adc_reg_base) */ void adc_disable_external_trigger_regular(uint32_t adc) { ADC_CFGR1(adc) &= ~ADC_CFGR1_EXTEN_MASK; } /**@}*/ /*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /** * @defgroup adc_api_interrupts ADC Interrupt configuration API * @ingroup adc_file * * @brief ADC Interrupt configuration API * *@{*/ /*---------------------------------------------------------------------------*/ /** @brief ADC Enable Analog Watchdog Interrupt * * @param[in] adc Unsigned int32. ADC base address (@ref adc_reg_base) */ void adc_enable_watchdog_interrupt(uint32_t adc) { ADC_IER(adc) |= ADC_IER_AWD1IE; } /*---------------------------------------------------------------------------*/ /** @brief ADC Disable Regular End-Of-Conversion Interrupt * * @param[in] adc Unsigned int32. ADC base address (@ref adc_reg_base) */ void adc_disable_watchdog_interrupt(uint32_t adc) { ADC_IER(adc) &= ~ADC_IER_AWD1IE; } /*---------------------------------------------------------------------------*/ /** @brief ADC Read the Analog Watchdog Flag * * This flag is set when the converted voltage crosses the high or low * thresholds. * * @param[in] adc Unsigned int32. ADC base address (@ref adc_reg_base) * @returns bool true, if the signal is out of defined analog range. */ bool adc_get_watchdog_flag(uint32_t adc) { return ADC_ISR(adc) & ADC_ISR_AWD1; } /*---------------------------------------------------------------------------*/ /** @brief ADC Clear Analog Watchdog Flag * * @param[in] adc Unsigned int32. ADC base address (@ref adc_reg_base) */ void adc_clear_watchdog_flag(uint32_t adc) { ADC_ISR(adc) = ADC_ISR_AWD1; } /*---------------------------------------------------------------------------*/ /** @brief ADC Enable Regular End-Of-Conversion Sequence Interrupt * * @param[in] adc Unsigned int32. ADC base address (@ref adc_reg_base) */ void adc_enable_eoc_sequence_interrupt(uint32_t adc) { ADC_IER(adc) |= ADC_IER_EOSEQIE; } /*---------------------------------------------------------------------------*/ /** @brief ADC Disable Regular End-Of-Conversion Sequence Interrupt * * @param[in] adc Unsigned int32. ADC base address (@ref adc_reg_base) */ void adc_disable_eoc_sequence_interrupt(uint32_t adc) { ADC_IER(adc) &= ~ADC_IER_EOSEQIE; } /*---------------------------------------------------------------------------*/ /** @brief ADC Read the Regular End-Of-Conversion Sequence Flag * * @param[in] adc Unsigned int32. ADC base address (@ref adc_reg_base) */ bool adc_get_eoc_sequence_flag(uint32_t adc) { return ADC_ISR(adc) & ADC_ISR_EOSEQ; } /*---------------------------------------------------------------------------*/ /** @brief ADC Clear Regular End-Of-Conversion Sequence Flag * * @param[in] adc Unsigned int32. ADC base address (@ref adc_reg_base) */ void adc_clear_eoc_sequence_flag(uint32_t adc) { ADC_ISR(adc) = ADC_ISR_EOSEQ; } /**@}*/ /*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /** * @defgroup adc_api_config ADC Basic configuration API * @ingroup adc_file * * @brief ADC Basic configuration API * *@{*/ /*---------------------------------------------------------------------------*/ /** @brief ADC Set Clock Source * * The ADC clock taken from the many sources. * * @param[in] adc Unsigned int32. ADC base address (@ref adc_reg_base) * @param[in] source Unsigned int32. Source (@ref adc_api_clksource) */ void adc_set_clk_source(uint32_t adc, uint32_t source) { ADC_CFGR2(adc) = ((ADC_CFGR2(adc) & ~ADC_CFGR2_CKMODE) | source); } /*---------------------------------------------------------------------------*/ /** @brief ADC Set a Regular Channel Conversion Sequence * * Define a sequence of channels to be converted as a regular group with a * length from 1 to 18 channels. If this is called during conversion, the * current conversion is reset and conversion begins again with the newly * defined group. * * @warning This core doesn't support the random order of ADC conversions. * The channel list must be ordered by channel number. * * @param[in] adc Unsigned int32. ADC base address (@ref adc_reg_base) * @param[in] length Unsigned int8. Number of channels in the group. * @param[in] channel Unsigned int8[]. Set of channels to convert, integers * 0..18. */ void adc_set_regular_sequence(uint32_t adc, uint8_t length, uint8_t channel[]) { uint32_t reg32 = 0; uint8_t i = 0; bool stepup = false, stepdn = false; if (length == 0) { ADC_CHSELR(adc) = 0; return; } reg32 |= (1 << channel[0]); for (i = 1; i < length; i++) { reg32 |= (1 << channel[i]); stepup |= channel[i-1] < channel[i]; stepdn |= channel[i-1] > channel[i]; } /* Check, if the channel list is in order */ if (stepup && stepdn) { cm3_assert_not_reached(); } /* Update the scan direction flag */ if (stepdn) { ADC_CFGR1(adc) |= ADC_CFGR1_SCANDIR; } else { ADC_CFGR1(adc) &= ~ADC_CFGR1_SCANDIR; } ADC_CHSELR(adc) = reg32; } /*---------------------------------------------------------------------------*/ /** @brief ADC Set the Sample Time for All Channels * * The sampling time can be selected in ADC clock cycles from 1.5 to 239.5, * same for all channels. * * @param[in] adc Unsigned int32. ADC base address (@ref adc_reg_base) * @param[in] time Unsigned int8. Sampling time selection (@ref adc_api_smptime) */ void adc_set_sample_time_on_all_channels(uint32_t adc, uint8_t time) { ADC_SMPR(adc) = time & ADC_SMPR_SMP; } /*---------------------------------------------------------------------------*/ /** @brief ADC Enable The VBat Sensor * * This enables the battery voltage measurements on channel 17. */ void adc_enable_vbat_sensor(void) { ADC_CCR(ADC1) |= ADC_CCR_VBATEN; } /*---------------------------------------------------------------------------*/ /** @brief ADC Disable The VBat Sensor * * Disabling this will reduce power consumption from the battery voltage * measurement. */ void adc_disable_vbat_sensor(void) { ADC_CCR(ADC1) &= ~ADC_CCR_VBATEN; } /*---------------------------------------------------------------------------*/ /** @brief ADC Start the calibration procedure * @deprecated Replaced by adc_calibrate/_async/is_calibrating * * @param[in] adc Unsigned int32. ADC base address (@ref adc_reg_base) */ void adc_calibrate_start(uint32_t adc) { ADC_CR(adc) = ADC_CR_ADCAL; } /*---------------------------------------------------------------------------*/ /** @brief ADC Wait to finish the ADC calibration procedure * @deprecated Replaced by adc_calibrate/_async/is_calibrating * * @param[in] adc Unsigned int32. ADC base address (@ref adc_reg_base) */ void adc_calibrate_wait_finish(uint32_t adc) { while (ADC_CR(adc) & ADC_CR_ADCAL); } /**@}*/ /*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /** * @defgroup adc_api_wdg ADC Analog watchdog API * @ingroup adc_file * * @brief ADC analog watchdog API definitions. * * The analog watchdog allows the monitoring of an analog signal between two * threshold levels. The thresholds must be preset. Analog watchdog is disabled * by default. * * @warning Comparison is done before data alignment takes place, so the * thresholds are left-aligned. * * Example 1: Enable watchdog checking on all channels * * @code * // in configuration * adc_enable_analog_watchdog_on_all_channels(ADC1); * adc_set_watchdog_high_threshold(ADC1, 0xE00); * adc_set_watchdog_low_threshold(ADC1, 0x200); * * // in the main application thread * if (adc_get_watchdog_flag(ADC1)) { * // the converted signal is out of AWD ranges * adc_clear_watchdog_flag(ADC1); * } * @endcode * * Example 2: Enable watchdog checking on channel 5 * * @code * // in configuration * adc_enable_analog_watchdog_on_selected_channel(ADC1,5); * adc_set_watchdog_high_threshold(ADC1, 0xE00); * adc_set_watchdog_low_threshold(ADC1, 0x200); * * // in the main application thread * if (adc_get_watchdog_flag(ADC1)) { * // the converted signal is out of AWD ranges * adc_clear_watchdog_flag(ADC1); * } * @endcode *@{*/ /*---------------------------------------------------------------------------*/ /** @brief ADC Enable Analog Watchdog for All Channels * * @param[in] adc Unsigned int32. ADC base address (@ref adc_reg_base) */ void adc_enable_analog_watchdog_on_all_channels(uint32_t adc) { ADC_CFGR1(adc) |= ADC_CFGR1_AWD1EN; ADC_CFGR1(adc) &= ~ADC_CFGR1_AWD1SGL; } /*---------------------------------------------------------------------------*/ /** @brief ADC Enable Analog Watchdog for a Selected Channel * * @param[in] adc Unsigned int32. ADC base address (@ref adc_reg_base) * @param[in] chan Unsigned int8. ADC channel number @ref adc_api_channel */ void adc_enable_analog_watchdog_on_selected_channel(uint32_t adc, uint8_t chan) { ADC_CFGR1(adc) = (ADC_CFGR1(adc) & ~ADC_CFGR1_AWD1CH) | ADC_CFGR1_AWD1CH_VAL(chan); ADC_CFGR1(adc) |= ADC_CFGR1_AWD1EN | ADC_CFGR1_AWD1SGL; } /*---------------------------------------------------------------------------*/ /** @brief ADC Disable Analog Watchdog * * @param[in] adc Unsigned int32. ADC base address (@ref adc_reg_base) */ void adc_disable_analog_watchdog(uint32_t adc) { ADC_CFGR1(adc) &= ~ADC_CFGR1_AWD1EN; } /*---------------------------------------------------------------------------*/ /** @brief ADC Set Analog Watchdog Upper Threshold * * @param[in] adc Unsigned int32. ADC base address (@ref adc_reg_base) * @param[in] threshold Upper threshold value */ void adc_set_watchdog_high_threshold(uint32_t adc, uint16_t threshold) { ADC_TR1(adc) = (ADC_TR1(adc) & ~ADC_TR1_HT) | ADC_TR1_HT_VAL(threshold); } /*---------------------------------------------------------------------------*/ /** @brief ADC Set Analog Watchdog Lower Threshold * * @param[in] adc Unsigned int32. ADC base address (@ref adc_reg_base) * @param[in] threshold Lower threshold value */ void adc_set_watchdog_low_threshold(uint32_t adc, uint16_t threshold) { ADC_TR1(adc) = (ADC_TR1(adc) & ~ADC_TR1_LT) | ADC_TR1_LT_VAL(threshold); } /**@}*/ /*---------------------------------------------------------------------------*/ /**@}*/