added prescaler for 16 bit pwm in LPC1347 target
Fork of mbed-dev by
targets/hal/TARGET_Atmel/TARGET_SAM_CortexM4/drivers/adc/adc2.c
- Committer:
- JojoS
- Date:
- 2016-09-10
- Revision:
- 147:ba84b7dc41a7
- Parent:
- 107:414e9c822e99
File content as of revision 147:ba84b7dc41a7:
/** * \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 } } } //@}