Mouse code for the MacroRat
Diff: mbed-dev/targets/TARGET_Atmel/TARGET_SAM_CortexM4/drivers/adc/adc2.c
- Revision:
- 18:6a4db94011d3
diff -r f713758f6238 -r 6a4db94011d3 mbed-dev/targets/TARGET_Atmel/TARGET_SAM_CortexM4/drivers/adc/adc2.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed-dev/targets/TARGET_Atmel/TARGET_SAM_CortexM4/drivers/adc/adc2.c Sun May 14 23:18:57 2017 +0000 @@ -0,0 +1,518 @@ +/** + * \file + * + * \brief ADC Controller driver. + * + * Copyright (c) 2013-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#include "adc2.h" +#include "sleepmgr.h" +#include "status_codes.h" +#include "sysclk.h" +#include "pmc.h" + +/** + * \defgroup sam_drivers_adc2_group Analog-to-Digital Controller + * + * See \ref sam_adc2_quickstart. + * + * Driver for the Analog-to-Digital Controller. This driver provides access to + * the main features of the ADC controller. + * + * @{ + */ + +/* The number of channel in channel sequence1 register */ +#define ADC_SEQ1_CHANNEL_NUM (8UL) + +/* The number of ADC interrupt source */ +#define ADC_NUM_OF_INTERRUPT_SOURCE (24UL) + + +const uint32_t adc_interrupt_mask[ADC_NUM_OF_INTERRUPT_SOURCE] = { + ADC_ISR_EOC0, ADC_ISR_EOC1, ADC_ISR_EOC2, ADC_ISR_EOC3, + ADC_ISR_EOC4, ADC_ISR_EOC5, ADC_ISR_EOC6, ADC_ISR_EOC7, +#if (SAM4N) + ADC_ISR_EOC8, ADC_ISR_EOC9, ADC_ISR_EOC10, ADC_ISR_EOC11, + ADC_ISR_EOC12, ADC_ISR_EOC13, ADC_ISR_EOC14, ADC_ISR_EOC15, + ADC_ISR_EOC16, +#endif +#ifdef TEMP_SENSOR + ADC_ISR_TEMPCHG, +#endif + ADC_ISR_EOCAL, ADC_ISR_DRDY, + ADC_ISR_GOVRE, ADC_ISR_COMPE, ADC_ISR_ENDRX, ADC_ISR_RXBUFF, +}; + +adc_callback_t adc_callback_pointer[ADC_NUM_OF_INTERRUPT_SOURCE]; + +/** + * \brief Get the ADC default configurations. + * + * Use to initialize the configuration structure to known default values. This + * function should be called at the start of any ADC initiation. + * + * The default configuration is as follows: + * - 10-bit resolution + * - ADC clock frequency is 6MHz + * - Start Up Time is 64 periods ADC clock + * - Tracking Time is 3 periods of ADC clock + * - Transfer Period field shall be programmed with 2 as datasheet said + * - The controller converts channels in a simple numeric order + * - Appends the channel number to the conversion result in ADC_LCDR register + * - Only a Single Trigger is required to get an averaged value + * + * \param cfg Pointer to configuration structure to be initiated. + */ +void adc_get_config_defaults(struct adc_config *const cfg) +{ + /* Sanity check argument. */ + Assert(cfg); + +#if SAMG55 + cfg->resolution = ADC_12_BITS; +#else + cfg->resolution = ADC_10_BITS; +#endif + cfg->mck = sysclk_get_cpu_hz(); + cfg->adc_clock = 6000000UL; + cfg->startup_time = ADC_STARTUP_TIME_4; + cfg->tracktim = 2; + cfg->transfer = 2; + cfg->useq = false; + cfg->tag = false; + cfg->aste = false; +} + +/** + * \internal + * \brief Configure the ADC Module. + * + * \param adc Base address of the ADC + * \param config Configuration for the ADC + */ +static void adc_set_config(Adc *const adc, struct adc_config *config) +{ + uint32_t reg = 0; + + reg = (config->useq ? ADC_MR_USEQ_REG_ORDER : 0) | + ADC_MR_PRESCAL(config->mck / + (2 * config->adc_clock) - 1) | + ADC_MR_TRACKTIM(config->tracktim) | + ADC_MR_TRANSFER(config->transfer) | + (config->startup_time); + + adc->ADC_MR = reg; + + adc->ADC_EMR = (config->tag ? ADC_EMR_TAG : 0) | + (config->aste ? ADC_EMR_ASTE_SINGLE_TRIG_AVERAGE : 0); + + adc_set_resolution(adc, config->resolution); +} + +#ifdef TEMP_SENSOR +/** + * \brief Get the ADC Temperature Sensor default configurations. + * + * Use to initialize the configuration structure to known default values. + * + * The default configuration is as follows: + * - Generates an event when the converted data is in the comparison window + * - The window range is 0xFF ~ 0xFFF + * + * \param cfg Pointer to temperature sensor configuration structure + * to be initiated. + */ +void adc_temp_sensor_get_config_defaults( + struct adc_temp_sensor_config *const cfg) +{ + /*Sanity check argument. */ + Assert(cfg); + + cfg->tempon = true; + cfg->mode = ADC_TEMP_CMP_MODE_2; + cfg->low_threshold = 0xFF; + cfg->high_threshold = 0xFFF; +} + +/** + * \brief Configure the ADC temperature sensor. + * + * \param adc Base address of the ADC + * \param config Configuration for the ADC temperature sensor + */ +void adc_temp_sensor_set_config(Adc *const adc, + struct adc_temp_sensor_config *config) +{ + uint32_t reg = 0; + + reg = ((config->tempon) ? ADC_TEMPMR_TEMPON : 0) | (config->mode); + adc->ADC_TEMPMR = reg; + + adc->ADC_TEMPCWR = ADC_TEMPCWR_TLOWTHRES(config->low_threshold) | + ADC_TEMPCWR_THIGHTHRES(config->high_threshold); +} +#endif + +#if (SAMG) +/** + * \brief Get the Last Channel Specific Measurement default configurations. + * + * Use to initialize the configuration structure to known default values. + * + * The default configuration is as follows: + * - Generates an event when the converted data is in the comparison window + * - The window range is 0xFF ~ 0xFFF + * + * \param cfg Pointer to last channel configuration structure + * to be initiated. + */ +void adc_last_channel_get_config_defaults( + struct adc_last_channel_config *const cfg) +{ + /*Sanity check argument. */ + Assert(cfg); + + cfg->dual_trig_on = true; + cfg->mode = ADC_LAST_CHANNEL_CMP_MODE_2; + cfg->low_threshold = 0xFF; + cfg->high_threshold = 0xFFF; +} + +/** + * \brief Configure the ADC Last Channel Specific Measurement. + * + * \param adc Base address of the ADC + * \param config Configuration for the last channel + */ +void adc_last_channel_set_config(Adc *const adc, + struct adc_last_channel_config *config) +{ + uint32_t reg = 0; + + reg = ((config->dual_trig_on) ? ADC_LCTMR_DUALTRIG : 0) | (config->mode); + adc->ADC_LCTMR = reg; + + adc->ADC_LCCWR = ADC_LCCWR_LOWTHRES(config->low_threshold) | + ADC_LCCWR_HIGHTHRES(config->high_threshold); +} +#endif + +/** + * \brief Initialize the ADC Module. + * + * \param adc Base address of the ADC + * \param config Configuration for the ADC + * + * \retval STATUS_OK Initialization is finished. + * \retval STATUS_ERR_BUSY Initialization failed. + */ +enum status_code adc_init(Adc *const adc, struct adc_config *config) +{ + Assert(adc); + Assert(config); + + if ((adc_get_interrupt_status(adc) & ADC_ISR_DRDY) == ADC_ISR_DRDY) { + return STATUS_ERR_BUSY; + } + + /* Reset and configure the ADC module */ + adc->ADC_CR = ADC_CR_SWRST; + adc_set_config(adc, config); + + uint32_t i; + for (i = 0; i < ADC_NUM_OF_INTERRUPT_SOURCE; i++) { + adc_callback_pointer[i] = 0; + } + + return STATUS_OK; +} + +/** + * \brief Configure conversion resolution. + * + * \param adc Base address of the ADC. + * \param res Conversion resolution. + * + */ +void adc_set_resolution(Adc *const adc, + const enum adc_resolution res) +{ +#if SAMG55 + adc->ADC_EMR |= res; +#else + if (res == ADC_11_BITS || res == ADC_12_BITS) { + adc->ADC_MR &= ~ADC_MR_LOWRES; + adc->ADC_EMR |= res; + } else { + adc->ADC_MR |= res; + adc->ADC_EMR &= ~ADC_EMR_OSR_Msk; + } +#endif +} + +/** + * \brief Configure comparison mode. + * + * \param adc Base address of the ADC. + * \param mode Comparison mode. + * \param channel Comparison Selected Channel. + * \param cmp_filter Compare Event Filtering. + */ +void adc_set_comparison_mode(Adc *const adc, + const enum adc_cmp_mode mode, + const enum adc_channel_num channel, uint8_t cmp_filter) +{ + if (channel != ADC_CHANNEL_ALL) { + adc_ch_sanity_check(adc, channel); + } + + uint32_t reg; + + reg = adc->ADC_EMR; + + reg &= ~(ADC_EMR_CMPSEL_Msk | + ADC_EMR_CMPMODE_Msk | + ADC_EMR_CMPFILTER_Msk); + reg |= mode | + ((channel == ADC_CHANNEL_ALL) ? ADC_EMR_CMPALL + : ADC_EMR_CMPSEL(channel)) | + ADC_EMR_CMPFILTER(cmp_filter); + + adc->ADC_EMR = reg; +} + +/** + * \brief Configure ADC power mode. + * + * \param adc Base address of the ADC. + * \param mode ADC power mode value. + */ +void adc_set_power_mode(Adc *const adc, + const enum adc_power_mode mode) +{ + uint32_t reg; + + reg = adc->ADC_MR; + + switch (mode) { + case ADC_POWER_MODE_0: + reg |= ADC_MR_SLEEP_NORMAL; + break; + + case ADC_POWER_MODE_1: + reg |= ADC_MR_SLEEP_SLEEP; + break; + } + + adc->ADC_MR = reg; +} + +/** + * \brief Set callback for ADC + * + * \param adc Base address of the ADC + * \param source Interrupt source + * \param callback Callback function pointer + * \param irq_level Interrupt level + */ +void adc_set_callback(Adc *const adc, enum adc_interrupt_source source, + adc_callback_t callback, uint8_t irq_level) +{ + Assert(adc); + Assert(callback); + + adc_callback_pointer[source] = callback; + irq_register_handler(ADC_IRQn, irq_level); + + /* Enable the specified interrupt source */ + adc_enable_interrupt(adc, source); +} + +/** + * \brief Enable ADC interrupts. + * + * \param adc Base address of the ADC. + * \param interrupt_source Interrupts to be enabled. + */ +void adc_enable_interrupt(Adc *const adc, + enum adc_interrupt_source interrupt_source) +{ + if (interrupt_source == ADC_INTERRUPT_ALL) { + adc->ADC_IER = ADC_INTERRUPT_ALL; + return; + } else { + adc->ADC_IER = adc_interrupt_mask[interrupt_source]; + } +} + +/** + * \brief Disable ADC interrupts. + * + * \param adc Base address of the ADC. + * \param interrupt_source Interrupts to be disabled. + */ +void adc_disable_interrupt(Adc *const adc, + enum adc_interrupt_source interrupt_source) +{ + if (interrupt_source == ADC_INTERRUPT_ALL) { + adc->ADC_IDR = ADC_INTERRUPT_ALL; + return; + } else { + adc->ADC_IDR = adc_interrupt_mask[interrupt_source]; + } +} + +/** + * \internal + * \brief Common ADC interrupt handler + * + * The optional callback used by the interrupt handler is set by the + * adc_set_callback() function. + * + * \param inst_num ADC instance number to handle interrupt for + * \param source Interrupt source number + */ +static void adc_interrupt(enum adc_interrupt_source source) +{ + if (adc_callback_pointer[source]) { + adc_callback_pointer[source](); + } +} + +/** + * \internal + * \brief Call the callback function if the corresponding interrupt is asserted + * + * \param adc Base address of the ADC. + */ +static void adc_process_callback(Adc *const adc) +{ + volatile uint32_t status; + uint32_t cnt; + + status = adc_get_interrupt_status(adc); + + for (cnt = 0; cnt < ADC_NUM_OF_INTERRUPT_SOURCE; cnt++) { + if (status & adc_interrupt_mask[cnt]) { + adc_interrupt((enum adc_interrupt_source)cnt); + } + } +} + +/** + * \brief Interrupt handler for ADC. + */ +void ADC_Handler(void) +{ + adc_process_callback(ADC); +} + +/** + * \brief Enable ADC Module. + * + */ +void adc_enable(void) +{ + /* Enable peripheral clock. */ + pmc_enable_periph_clk(ID_ADC); + sleepmgr_lock_mode(SLEEP_MODE_ADC); +} + +/** + * \brief Disable ADC Module. + * + */ +void adc_disable(void) +{ + /* Disable peripheral clock. */ + pmc_disable_periph_clk(ID_ADC); + sleepmgr_unlock_mode(SLEEP_MODE_ADC); +} + +/** + * \brief Configure conversion sequence. + * + * \param adc Base address of the ADC. + * \param ch_list Channel sequence list. + * \param uc_num Number of channels in the list. + */ +void adc_configure_sequence(Adc *const adc, + const enum adc_channel_num ch_list[], uint8_t uc_num) +{ + uint8_t uc_counter; + + /* Set user sequence mode */ + adc->ADC_MR |= ADC_MR_USEQ_REG_ORDER; +#if (SAM4N) + adc->ADC_SEQR1 = 0; + adc->ADC_SEQR2 = 0; +#endif +#if (SAMG) + adc->ADC_SEQR1 = 0; +#endif + + if (uc_num <= ADC_SEQ1_CHANNEL_NUM) { + for (uc_counter = 0; uc_counter < uc_num; uc_counter++) { +#if (SAM4N || SAMG) + adc->ADC_SEQR1 +#endif + |= ch_list[uc_counter] << (4 * uc_counter); + } + } else { + for (uc_counter = 0; uc_counter <= ADC_SEQ1_CHANNEL_NUM; + uc_counter++) { +#if (SAM4N || SAMG) + adc->ADC_SEQR1 +#endif + |= ch_list[uc_counter] << (4 * uc_counter); + } + for (uc_counter = 0; uc_counter < uc_num - ADC_SEQ1_CHANNEL_NUM; + uc_counter++) { +#if (SAM4N) + adc->ADC_SEQR2 |= ch_list[8 + uc_counter] << (4 * uc_counter); +#endif + } + } +} + +//@}