mbed library sources. Supersedes mbed-src.

Fork of mbed-dev by mbed official

Revision:
18:da299f395b9e
diff -r 0b20abc991ca -r da299f395b9e targets/hal/TARGET_Atmel/TARGET_SAM_CortexM0P/drivers/adc/TARGET_SAML21/adc.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/targets/hal/TARGET_Atmel/TARGET_SAM_CortexM0P/drivers/adc/TARGET_SAML21/adc.c	Mon Nov 09 13:30:11 2015 +0000
@@ -0,0 +1,769 @@
+/**
+ * \file
+ *
+ * \brief SAM Peripheral Analog-to-Digital Converter Driver
+ *
+ * Copyright (C) 2014-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 "adc.h"
+#if (ADC_INST_NUM > 1) || (SAMC20)
+
+#  define _ADC_GCLK_ID(n,unused)           TPASTE3(ADC,n,_GCLK_ID),
+#  define _ADC_APBCMASK(n,unused)          TPASTE2(MCLK_APBCMASK_ADC,n),
+
+#  define _ADC_FUSES_BIASCOMP_ADDR(n,unused)        TPASTE3(ADC,n,_FUSES_BIASCOMP_ADDR),
+#  define _ADC_FUSES_BIASCOMP_Pos(n,unused)         TPASTE3(ADC,n,_FUSES_BIASCOMP_Pos),
+#  define _ADC_FUSES_BIASREFBUF_ADDR(n,unused)      TPASTE3(ADC,n,_FUSES_BIASREFBUF_ADDR),
+#  define _ADC_FUSES_BIASREFBUF_Pos(n,unused)       TPASTE3(ADC,n,_FUSES_BIASREFBUF_Pos),
+#  define _ADC_EXTCHANNEL_MSB(n,unused)             TPASTE3(ADC,n,_EXTCHANNEL_MSB),
+
+#  define ADC_GCLK_ID       MREPEAT(ADC_INST_NUM, _ADC_GCLK_ID, 0)
+#  define ADC_APBCMASKS     MREPEAT(ADC_INST_NUM, _ADC_APBCMASK, 0)
+
+#  define ADC_FUSES_BIASCOMP_ADDR     MREPEAT(ADC_INST_NUM, _ADC_FUSES_BIASCOMP_ADDR, 0)
+#  define ADC_FUSES_BIASCOMP_Pos      MREPEAT(ADC_INST_NUM, _ADC_FUSES_BIASCOMP_Pos, 0)
+#  define ADC_FUSES_BIASREFBUF_ADDR   MREPEAT(ADC_INST_NUM, _ADC_FUSES_BIASREFBUF_ADDR, 0)
+#  define ADC_FUSES_BIASREFBUF_Pos    MREPEAT(ADC_INST_NUM, _ADC_FUSES_BIASREFBUF_Pos, 0)
+#  define ADC_EXTCHANNEL_MSB          MREPEAT(ADC_INST_NUM, _ADC_EXTCHANNEL_MSB, 0)
+
+#endif
+
+/* List of ADC GCLK IDs */
+const uint8_t _adc_gclk_ids[ADC_INST_NUM] = { ADC_GCLK_ID };
+
+/* List of ADC APB Masks */
+#if (SAML21)
+const uint32_t _adc_apbcmasks[ADC_INST_NUM] = { MCLK_APBDMASK_ADC };
+#else
+const uint32_t _adc_apbcmasks[ADC_INST_NUM] = { ADC_APBCMASKS };
+#endif
+
+/* List of Number of external channels of ADC modules. */
+const uint32_t _adc_extchannel_msb[ADC_INST_NUM] = { ADC_EXTCHANNEL_MSB };
+
+/* List of address of comparator scaling of ADC modules. */
+const uint32_t _adc_biascomp_addr[ADC_INST_NUM] = { ADC_FUSES_BIASCOMP_ADDR };
+
+/* List of address of bias reference buffer scaling of ADC modules. */
+const uint32_t _adc_biasrefbuf_addr[ADC_INST_NUM] = { ADC_FUSES_BIASREFBUF_ADDR };
+
+/* List of offset of comparator scaling of ADC modules. */
+const uint8_t _adc_biascomp_pos[ADC_INST_NUM] = { ADC_FUSES_BIASCOMP_Pos };
+
+/* List of offset of bias reference buffer scaling of ADC modules. */
+const uint8_t _adc_biasrefbuf_pos[ADC_INST_NUM] = { ADC_FUSES_BIASREFBUF_Pos };
+
+
+/**
+ * \internal Find the index of given ADC module instance.
+ *
+ * \param[in] ADC module instance pointer.
+ *
+ * \return Index of the given ADC module instance.
+ */
+uint8_t _adc_get_inst_index(
+    Adc *const hw)
+{
+    /* List of available ADC modules. */
+    Adc *const adc_modules[ADC_INST_NUM] = ADC_INSTS;
+
+    /* Find index for ADC instance. */
+    for (uint32_t i = 0; i < ADC_INST_NUM; i++) {
+        if (hw == adc_modules[i]) {
+            return i;
+        }
+    }
+
+    /* Invalid data given. */
+    Assert(false);
+    return 0;
+}
+
+/**
+ * \brief Initializes an ADC configuration structure to defaults.
+ *
+ * Initializes a given ADC configuration struct to a set of known default
+ * values. This function should be called on any new instance of the
+ * configuration struct before being modified by the user application.
+ *
+ * The default configuration is as follows:
+ *  \li GCLK generator 0 (GCLK main) clock source
+ *  \li Internal bandgap reference
+ *  \li Div 2 clock prescaler
+ *  \li 12-bit resolution
+ *  \li Window monitor disabled
+ *  \li Positive input on ADC PIN 1
+ *  \li Negative input on Internal ground
+ *  \li Averaging disabled
+ *  \li Oversampling disabled
+ *  \li Right adjust data
+ *  \li Single-ended mode
+ *  \li Free running disabled
+ *  \li All events (input and generation) disabled
+ *  \li ADC run in standby disabled
+ *  \li ADC On demand disabled
+ *  \li No sampling time compensation
+ *  \li Disable the positive input sequense
+ *  \li No reference compensation
+ *  \li No gain/offset correction
+ *  \li No added sampling time
+ *
+ * \param[out] config  Pointer to configuration struct to initialize to
+ *                     default values
+ */
+void adc_get_config_defaults(struct adc_config *const config)
+{
+    Assert(config);
+    config->clock_source                  = GCLK_GENERATOR_0;
+    config->reference                     = ADC_REFERENCE_INTREF;
+    config->clock_prescaler               = ADC_CLOCK_PRESCALER_DIV2;
+    config->resolution                    = ADC_RESOLUTION_12BIT;
+    config->window.window_mode            = ADC_WINDOW_MODE_DISABLE;
+    config->window.window_upper_value     = 0;
+    config->window.window_lower_value     = 0;
+    config->positive_input                = ADC_POSITIVE_INPUT_PIN1;
+    config->negative_input                = ADC_NEGATIVE_INPUT_GND;
+    config->accumulate_samples            = ADC_ACCUMULATE_DISABLE;
+    config->divide_result                 = ADC_DIVIDE_RESULT_DISABLE;
+    config->left_adjust                   = false;
+    config->differential_mode             = false;
+    config->freerunning                   = false;
+    config->event_action                  = ADC_EVENT_ACTION_DISABLED;
+    config->run_in_standby                = false;
+    config->on_demand                     = false;
+    config->sampling_time_compensation_enable  = false;
+    config->positive_input_sequence_mask_enable = 0;
+    config->reference_compensation_enable = false;
+    config->correction.correction_enable  = false;
+    config->correction.gain_correction    = ADC_GAINCORR_RESETVALUE;
+    config->correction.offset_correction  = ADC_OFFSETCORR_RESETVALUE;
+    config->sample_length                 = 0;
+}
+
+/**
+ * \brief Sets the ADC window mode.
+ *
+ * Sets the ADC window mode to a given mode and value range.
+ *
+ * \param[in] module_inst         Pointer to the ADC software instance struct
+ * \param[in] window_mode         Window monitor mode to set
+ * \param[in] window_lower_value  Lower window monitor threshold value
+ * \param[in] window_upper_value  Upper window monitor threshold value
+  */
+void adc_set_window_mode(
+    struct adc_module *const module_inst,
+    const enum adc_window_mode window_mode,
+    const int16_t window_lower_value,
+    const int16_t window_upper_value)
+{
+    /* Sanity check arguments */
+    Assert(module_inst);
+    Assert(module_inst->hw);
+
+    Adc *const adc_module = module_inst->hw;
+
+    while (adc_is_syncing(module_inst)) {
+        /* Wait for synchronization */
+    }
+
+    /* Set window mode */
+    adc_module->CTRLC.reg = window_mode;
+
+    while (adc_is_syncing(module_inst)) {
+        /* Wait for synchronization */
+    }
+
+    /* Set lower window monitor threshold value */
+    adc_module->WINLT.reg = window_lower_value;
+
+    while (adc_is_syncing(module_inst)) {
+        /* Wait for synchronization */
+    }
+
+    /* Set upper window monitor threshold value */
+    adc_module->WINUT.reg = window_upper_value;
+
+    while (adc_is_syncing(module_inst)) {
+        /* Wait for synchronization */
+    }
+}
+
+/**
+* \internal Configure MUX settings for the analog pins.
+*
+* This function will set the given ADC input pins
+* to the analog function in the pin mux, giving
+* the ADC access to the analog signal.
+*
+* \param [in] index   Index of the ADC module instance.
+* \param [in] pin     AINxx pin to configure
+*/
+static inline void _adc_configure_ain_pin(uint8_t index, uint32_t pin)
+{
+#define PIN_INVALID_ADC_AIN    0xFFFFUL
+
+    /* Pinmapping table for AINxx -> GPIO pin number */
+#if (SAML21)
+    const uint32_t pinmapping[] = {
+#   if (SAML21E)
+        PIN_PA02B_ADC_AIN0,  PIN_PA03B_ADC_AIN1,
+        PIN_INVALID_ADC_AIN, PIN_INVALID_ADC_AIN,
+        PIN_PA04B_ADC_AIN4,  PIN_PA05B_ADC_AIN5,
+        PIN_PA06B_ADC_AIN6,  PIN_PA07B_ADC_AIN7,
+        PIN_INVALID_ADC_AIN, PIN_INVALID_ADC_AIN,
+        PIN_INVALID_ADC_AIN, PIN_INVALID_ADC_AIN,
+        PIN_INVALID_ADC_AIN, PIN_INVALID_ADC_AIN,
+        PIN_INVALID_ADC_AIN, PIN_INVALID_ADC_AIN,
+        PIN_PA08B_ADC_AIN16, PIN_PA09B_ADC_AIN17,
+        PIN_PA10B_ADC_AIN18, PIN_PA11B_ADC_AIN19,
+        PIN_INVALID_ADC_AIN, PIN_INVALID_ADC_AIN,
+        PIN_INVALID_ADC_AIN, PIN_INVALID_ADC_AIN,
+#   elif (SAML21G)
+        PIN_PA02B_ADC_AIN0,  PIN_PA03B_ADC_AIN1,
+        PIN_PB08B_ADC_AIN2,  PIN_PB09B_ADC_AIN3,
+        PIN_PA04B_ADC_AIN4,  PIN_PA05B_ADC_AIN5,
+        PIN_PA06B_ADC_AIN6,  PIN_PA07B_ADC_AIN7,
+        PIN_INVALID_ADC_AIN, PIN_INVALID_ADC_AIN,
+        PIN_PB02B_ADC_AIN10, PIN_PB03B_ADC_AIN11,
+        PIN_INVALID_ADC_AIN, PIN_INVALID_ADC_AIN,
+        PIN_INVALID_ADC_AIN, PIN_INVALID_ADC_AIN,
+        PIN_PA08B_ADC_AIN16, PIN_PA09B_ADC_AIN17,
+        PIN_PA10B_ADC_AIN18, PIN_PA11B_ADC_AIN19,
+        PIN_INVALID_ADC_AIN, PIN_INVALID_ADC_AIN,
+        PIN_INVALID_ADC_AIN, PIN_INVALID_ADC_AIN,
+#   elif (SAML21J)
+        PIN_PA02B_ADC_AIN0,  PIN_PA03B_ADC_AIN1,
+        PIN_PB08B_ADC_AIN2,  PIN_PB09B_ADC_AIN3,
+        PIN_PA04B_ADC_AIN4,  PIN_PA05B_ADC_AIN5,
+        PIN_PA06B_ADC_AIN6,  PIN_PA07B_ADC_AIN7,
+        PIN_PB00B_ADC_AIN8,  PIN_PB01B_ADC_AIN9,
+        PIN_PB02B_ADC_AIN10, PIN_PB03B_ADC_AIN11,
+        PIN_PB04B_ADC_AIN12, PIN_PB05B_ADC_AIN13,
+        PIN_PB06B_ADC_AIN14, PIN_PB07B_ADC_AIN15,
+        PIN_PA08B_ADC_AIN16, PIN_PA09B_ADC_AIN17,
+        PIN_PA10B_ADC_AIN18, PIN_PA11B_ADC_AIN19,
+        PIN_INVALID_ADC_AIN, PIN_INVALID_ADC_AIN,
+        PIN_INVALID_ADC_AIN, PIN_INVALID_ADC_AIN,
+#   else
+#       error ADC pin mappings are not defined for this device.
+#   endif
+    };
+#elif (SAMC20)
+    const uint32_t pinmapping[] = {
+#   if (SAMC20E)
+        PIN_PA02B_ADC0_AIN0,  PIN_PA03B_ADC0_AIN1,
+        PIN_INVALID_ADC_AIN,  PIN_INVALID_ADC_AIN,
+        PIN_PA04B_ADC0_AIN4,  PIN_PA05B_ADC0_AIN5,
+        PIN_PA06B_ADC0_AIN6,  PIN_PA07B_ADC0_AIN7,
+        PIN_PA08B_ADC0_AIN8,  PIN_PA09B_ADC0_AIN9,
+        PIN_PA10B_ADC0_AIN10, PIN_PA11B_ADC0_AIN11,
+#   elif (SAMC20G)
+        PIN_PA02B_ADC0_AIN0,  PIN_PA03B_ADC0_AIN1,
+        PIN_PB08B_ADC0_AIN2,  PIN_PB09B_ADC0_AIN3,
+        PIN_PA04B_ADC0_AIN4,  PIN_PA05B_ADC0_AIN5,
+        PIN_PA06B_ADC0_AIN6,  PIN_PA07B_ADC0_AIN7,
+        PIN_PA08B_ADC0_AIN8,  PIN_PA09B_ADC0_AIN9,
+        PIN_PA10B_ADC0_AIN10, PIN_PA11B_ADC0_AIN11,
+#   elif (SAMC20J)
+        PIN_PA02B_ADC0_AIN0,  PIN_PA03B_ADC0_AIN1,
+        PIN_PB08B_ADC0_AIN2,  PIN_PB09B_ADC0_AIN3,
+        PIN_PA04B_ADC0_AIN4,  PIN_PA05B_ADC0_AIN5,
+        PIN_PA06B_ADC0_AIN6,  PIN_PA07B_ADC0_AIN7,
+        PIN_PA08B_ADC0_AIN8,  PIN_PA09B_ADC0_AIN9,
+        PIN_PA10B_ADC0_AIN10, PIN_PA11B_ADC0_AIN11,
+#   else
+#       error ADC pin mappings are not defined for this device.
+#   endif
+    };
+#elif (SAMC21)
+    const uint32_t *pinmapping = NULL;;
+    const uint32_t pinmapping0[] = {
+#   if (SAMC21E)
+        PIN_PA02B_ADC0_AIN0,  PIN_PA03B_ADC0_AIN1,
+        PIN_INVALID_ADC_AIN,  PIN_INVALID_ADC_AIN,
+        PIN_PA04B_ADC0_AIN4,  PIN_PA05B_ADC0_AIN5,
+        PIN_PA06B_ADC0_AIN6,  PIN_PA07B_ADC0_AIN7,
+        PIN_PA08B_ADC0_AIN8,  PIN_PA09B_ADC0_AIN9,
+        PIN_PA10B_ADC0_AIN10, PIN_PA11B_ADC0_AIN11,
+#   elif (SAMC21G)
+        PIN_PA02B_ADC0_AIN0,  PIN_PA03B_ADC0_AIN1,
+        PIN_PB08B_ADC0_AIN2,  PIN_PB09B_ADC0_AIN3,
+        PIN_PA04B_ADC0_AIN4,  PIN_PA05B_ADC0_AIN5,
+        PIN_PA06B_ADC0_AIN6,  PIN_PA07B_ADC0_AIN7,
+        PIN_PA08B_ADC0_AIN8,  PIN_PA09B_ADC0_AIN9,
+        PIN_PA10B_ADC0_AIN10, PIN_PA11B_ADC0_AIN11,
+#   elif (SAMC21J)
+        PIN_PA02B_ADC0_AIN0,  PIN_PA03B_ADC0_AIN1,
+        PIN_PB08B_ADC0_AIN2,  PIN_PB09B_ADC0_AIN3,
+        PIN_PA04B_ADC0_AIN4,  PIN_PA05B_ADC0_AIN5,
+        PIN_PA06B_ADC0_AIN6,  PIN_PA07B_ADC0_AIN7,
+        PIN_PA08B_ADC0_AIN8,  PIN_PA09B_ADC0_AIN9,
+        PIN_PA10B_ADC0_AIN10, PIN_PA11B_ADC0_AIN11,
+#   else
+#       error ADC pin mappings are not defined for this device.
+#   endif
+    };
+    const uint32_t pinmapping1[] = {
+#   if (SAMC21E)
+        PIN_INVALID_ADC_AIN,  PIN_INVALID_ADC_AIN,
+        PIN_INVALID_ADC_AIN,  PIN_INVALID_ADC_AIN,
+        PIN_INVALID_ADC_AIN,  PIN_INVALID_ADC_AIN,
+        PIN_INVALID_ADC_AIN,  PIN_INVALID_ADC_AIN,
+        PIN_INVALID_ADC_AIN,  PIN_INVALID_ADC_AIN,
+        PIN_PA08B_ADC1_AIN10, PIN_PA09B_ADC1_AIN11,
+#   elif (SAMC21G)
+        PIN_INVALID_ADC_AIN,  PIN_INVALID_ADC_AIN,
+        PIN_PB02B_ADC1_AIN2,  PIN_PB03B_ADC1_AIN3,
+        PIN_PB08B_ADC1_AIN4,  PIN_PB09B_ADC1_AIN5,
+        PIN_INVALID_ADC_AIN,  PIN_INVALID_ADC_AIN,
+        PIN_INVALID_ADC_AIN,  PIN_INVALID_ADC_AIN,
+        PIN_PA08B_ADC1_AIN10, PIN_PA09B_ADC1_AIN11,
+#   elif (SAMC21J)
+        PIN_PB00B_ADC1_AIN0,  PIN_PB01B_ADC1_AIN1,
+        PIN_PB02B_ADC1_AIN2,  PIN_PB03B_ADC1_AIN3,
+        PIN_PB08B_ADC1_AIN4,  PIN_PB09B_ADC1_AIN5,
+        PIN_PB04B_ADC1_AIN6,  PIN_PB05B_ADC1_AIN7,
+        PIN_PB06B_ADC1_AIN8,  PIN_PB07B_ADC1_AIN9,
+        PIN_PA08B_ADC1_AIN10, PIN_PA09B_ADC1_AIN11,
+#   else
+#       error ADC pin mappings are not defined for this device.
+#   endif
+    };
+
+    switch(index) {
+        case 0:
+            pinmapping = pinmapping0;
+            break;
+        case 1:
+            pinmapping = pinmapping1;
+            break;
+        default:
+            break;
+    }
+    Assert(pinmapping);
+#endif
+
+    uint32_t pin_map_result = PIN_INVALID_ADC_AIN;
+
+    if (pin <= _adc_extchannel_msb[index]) {
+        pin_map_result = pinmapping[pin >> ADC_INPUTCTRL_MUXPOS_Pos];
+
+        Assert(pin_map_result != PIN_INVALID_ADC_AIN);
+
+        struct system_pinmux_config config;
+        system_pinmux_get_config_defaults(&config);
+
+        /* Analog functions are all on MUX setting B */
+        config.input_pull   = SYSTEM_PINMUX_PIN_PULL_NONE;
+        config.mux_position = 1;
+
+        system_pinmux_pin_set_config(pin_map_result, &config);
+    }
+}
+
+/**
+ * \internal Writes an ADC configuration to the hardware module.
+ *
+ * Writes out a given ADC module configuration to the hardware module.
+ *
+ * \param[in]  index        Index of the ADC module instance
+ * \param[out] module_inst  Pointer to the ADC software instance struct
+ * \param[in]  config       Pointer to configuration struct
+ *
+ * \return Status of the configuration procedure.
+ * \retval STATUS_OK               The configuration was successful
+ * \retval STATUS_ERR_INVALID_ARG  Invalid argument(s) were provided
+ */
+static enum status_code _adc_set_config(
+    uint8_t index,
+    struct adc_module *const module_inst,
+    struct adc_config *const config)
+{
+    uint8_t adjres = 0;
+    uint32_t resolution = ADC_RESOLUTION_16BIT;
+    enum adc_accumulate_samples accumulate = ADC_ACCUMULATE_DISABLE;
+
+    /* Get the hardware module pointer */
+    Adc *const adc_module = module_inst->hw;
+
+    /* Configure GCLK channel and enable clock */
+    struct system_gclk_chan_config gclk_chan_conf;
+    system_gclk_chan_get_config_defaults(&gclk_chan_conf);
+    gclk_chan_conf.source_generator = config->clock_source;
+    system_gclk_chan_set_config(_adc_gclk_ids[index], &gclk_chan_conf);
+    system_gclk_chan_enable(_adc_gclk_ids[index]);
+
+    /* Setup pinmuxing for analog inputs */
+    _adc_configure_ain_pin(index, config->positive_input);
+    _adc_configure_ain_pin(index, config->negative_input);
+
+    /* Set pinmux for positive input sequence*/
+    for(uint8_t i=0; i <= _adc_extchannel_msb[index]; i++) {
+        if(config->positive_input_sequence_mask_enable & (1 << i)) {
+            _adc_configure_ain_pin(index, i);
+        }
+    }
+
+    /* Configure run in standby and on demand */
+    adc_module->CTRLA.reg = ((config->run_in_standby << ADC_CTRLA_RUNSTDBY_Pos)
+                             | (config->on_demand << ADC_CTRLA_ONDEMAND_Pos)) ;
+
+    /* Configure reference */
+    adc_module->REFCTRL.reg =
+        (config->reference_compensation_enable << ADC_REFCTRL_REFCOMP_Pos)
+        | (config->reference);
+
+    /* Set adjusting result and number of samples */
+    switch (config->resolution) {
+
+        case ADC_RESOLUTION_CUSTOM:
+            adjres = config->divide_result;
+            accumulate = config->accumulate_samples;
+            /* 16-bit result register */
+            resolution = ADC_RESOLUTION_16BIT;
+            break;
+
+        case ADC_RESOLUTION_13BIT:
+            /* Increase resolution by 1 bit */
+            adjres = ADC_DIVIDE_RESULT_2;
+            accumulate = ADC_ACCUMULATE_SAMPLES_4;
+            /* 16-bit result register */
+            resolution = ADC_RESOLUTION_16BIT;
+            break;
+
+        case ADC_RESOLUTION_14BIT:
+            /* Increase resolution by 2 bit */
+            adjres = ADC_DIVIDE_RESULT_4;
+            accumulate = ADC_ACCUMULATE_SAMPLES_16;
+            /* 16-bit result register */
+            resolution = ADC_RESOLUTION_16BIT;
+            break;
+        case ADC_RESOLUTION_15BIT:
+            /* Increase resolution by 3 bit */
+            adjres = ADC_DIVIDE_RESULT_2;
+            accumulate = ADC_ACCUMULATE_SAMPLES_64;
+            /* 16-bit result register */
+            resolution = ADC_RESOLUTION_16BIT;
+            break;
+
+        case ADC_RESOLUTION_16BIT:
+            /* Increase resolution by 4 bit */
+            adjres = ADC_DIVIDE_RESULT_DISABLE;
+            accumulate = ADC_ACCUMULATE_SAMPLES_256;
+            /* 16-bit result register */
+            resolution = ADC_RESOLUTION_16BIT;
+            break;
+        case ADC_RESOLUTION_8BIT:
+            /* 8-bit result register */
+            resolution = ADC_RESOLUTION_8BIT;
+            break;
+        case ADC_RESOLUTION_10BIT:
+            /* 10-bit result register */
+            resolution = ADC_RESOLUTION_10BIT;
+            break;
+        case ADC_RESOLUTION_12BIT:
+            /* 12-bit result register */
+            resolution = ADC_RESOLUTION_12BIT;
+            break;
+
+        default:
+            /* Unknown. Abort. */
+            return STATUS_ERR_INVALID_ARG;
+    }
+
+    adc_module->AVGCTRL.reg = ADC_AVGCTRL_ADJRES(adjres) | accumulate;
+
+    while (adc_is_syncing(module_inst)) {
+        /* Wait for synchronization */
+    }
+
+    /* Check validity of sample length value */
+    if (config->sample_length > 63) {
+        return STATUS_ERR_INVALID_ARG;
+    } else {
+        /* Configure sample length */
+        adc_module->SAMPCTRL.reg =
+            (config->sample_length << ADC_SAMPCTRL_SAMPLEN_Pos)
+            | (config->sampling_time_compensation_enable << ADC_SAMPCTRL_OFFCOMP_Pos);
+    }
+
+    while (adc_is_syncing(module_inst)) {
+        /* Wait for synchronization */
+    }
+
+    /* Configure CTRLB */
+    adc_module->CTRLB.reg =
+        config->clock_prescaler;
+    adc_module->CTRLC.reg =
+        resolution |
+        (config->correction.correction_enable << ADC_CTRLC_CORREN_Pos) |
+        (config->freerunning << ADC_CTRLC_FREERUN_Pos) |
+        (config->left_adjust << ADC_CTRLC_LEFTADJ_Pos) |
+        (config->differential_mode << ADC_CTRLC_DIFFMODE_Pos);
+
+    while (adc_is_syncing(module_inst)) {
+        /* Wait for synchronization */
+    }
+
+    /* Check validity of window thresholds */
+    if (config->window.window_mode != ADC_WINDOW_MODE_DISABLE) {
+        switch (resolution) {
+            case ADC_RESOLUTION_8BIT:
+                if (config->differential_mode &&
+                        (config->window.window_lower_value > 127 ||
+                         config->window.window_lower_value < -128 ||
+                         config->window.window_upper_value > 127 ||
+                         config->window.window_upper_value < -128)) {
+                    /* Invalid value */
+                    return STATUS_ERR_INVALID_ARG;
+                } else if (config->window.window_lower_value > 255 ||
+                           config->window.window_upper_value > 255) {
+                    /* Invalid value */
+                    return STATUS_ERR_INVALID_ARG;
+                }
+                break;
+            case ADC_RESOLUTION_10BIT:
+                if (config->differential_mode &&
+                        (config->window.window_lower_value > 511 ||
+                         config->window.window_lower_value < -512 ||
+                         config->window.window_upper_value > 511 ||
+                         config->window.window_upper_value < -512)) {
+                    /* Invalid value */
+                    return STATUS_ERR_INVALID_ARG;
+                } else if (config->window.window_lower_value > 1023 ||
+                           config->window.window_upper_value > 1023) {
+                    /* Invalid value */
+                    return STATUS_ERR_INVALID_ARG;
+                }
+                break;
+            case ADC_RESOLUTION_12BIT:
+                if (config->differential_mode &&
+                        (config->window.window_lower_value > 2047 ||
+                         config->window.window_lower_value < -2048 ||
+                         config->window.window_upper_value > 2047 ||
+                         config->window.window_upper_value < -2048)) {
+                    /* Invalid value */
+                    return STATUS_ERR_INVALID_ARG;
+                } else if (config->window.window_lower_value > 4095 ||
+                           config->window.window_upper_value > 4095) {
+                    /* Invalid value */
+                    return STATUS_ERR_INVALID_ARG;
+                }
+                break;
+            case ADC_RESOLUTION_16BIT:
+                if (config->differential_mode &&
+                        (config->window.window_lower_value > 32767 ||
+                         config->window.window_lower_value < -32768 ||
+                         config->window.window_upper_value > 32767 ||
+                         config->window.window_upper_value < -32768)) {
+                    /* Invalid value */
+                    return STATUS_ERR_INVALID_ARG;
+                } else if (config->window.window_lower_value > 65535 ||
+                           config->window.window_upper_value > 65535) {
+                    /* Invalid value */
+                    return STATUS_ERR_INVALID_ARG;
+                }
+                break;
+        }
+    }
+
+    /* Configure window mode */
+    adc_module->CTRLC.reg |= config->window.window_mode;
+
+    while (adc_is_syncing(module_inst)) {
+        /* Wait for synchronization */
+    }
+
+    /* Configure lower threshold */
+    adc_module->WINLT.reg =
+        config->window.window_lower_value << ADC_WINLT_WINLT_Pos;
+
+    while (adc_is_syncing(module_inst)) {
+        /* Wait for synchronization */
+    }
+
+    /* Configure lower threshold */
+    adc_module->WINUT.reg = config->window.window_upper_value <<
+                            ADC_WINUT_WINUT_Pos;
+
+    while (adc_is_syncing(module_inst)) {
+        /* Wait for synchronization */
+    }
+
+    /* Configure pin scan mode and positive and negative input pins */
+    adc_module->INPUTCTRL.reg =
+        config->negative_input |
+        config->positive_input;
+
+    while (adc_is_syncing(module_inst)) {
+        /* Wait for synchronization */
+    }
+
+    /* Configure events */
+    adc_module->EVCTRL.reg = config->event_action;
+
+    /* Disable all interrupts */
+    adc_module->INTENCLR.reg =
+        (1 << ADC_INTENCLR_WINMON_Pos) |(1 << ADC_INTENCLR_OVERRUN_Pos)
+        | (1 << ADC_INTENCLR_RESRDY_Pos);
+
+    if (config->correction.correction_enable) {
+        /* Make sure gain_correction value is valid */
+        if (config->correction.gain_correction > ADC_GAINCORR_GAINCORR_Msk) {
+            return STATUS_ERR_INVALID_ARG;
+        } else {
+            /* Set gain correction value */
+            adc_module->GAINCORR.reg = config->correction.gain_correction <<
+                                       ADC_GAINCORR_GAINCORR_Pos;
+        }
+
+        while (adc_is_syncing(module_inst)) {
+            /* Wait for synchronization */
+        }
+
+        /* Make sure offset correction value is valid */
+        if (config->correction.offset_correction > 2047 ||
+                config->correction.offset_correction < -2048) {
+            return STATUS_ERR_INVALID_ARG;
+        } else {
+            /* Set offset correction value */
+            adc_module->OFFSETCORR.reg = config->correction.offset_correction <<
+                                         ADC_OFFSETCORR_OFFSETCORR_Pos;
+        }
+
+        while (adc_is_syncing(module_inst)) {
+            /* Wait for synchronization */
+        }
+    }
+
+    /* Load in the fixed device ADC calibration constants */
+    adc_module->CALIB.reg =
+        ADC_CALIB_BIASREFBUF(
+            (*(uint32_t *)_adc_biasrefbuf_addr[index] >> _adc_biasrefbuf_pos[index])
+        ) |
+        ADC_CALIB_BIASCOMP(
+            (*(uint32_t *)_adc_biascomp_addr[index] >> _adc_biascomp_pos[index])
+        );
+
+    return STATUS_OK;
+}
+
+/**
+ * \brief Initializes the ADC.
+ *
+ * Initializes the ADC device struct and the hardware module based on the
+ * given configuration struct values.
+ *
+ * \param[out] module_inst Pointer to the ADC software instance struct
+ * \param[in]  hw          Pointer to the ADC module instance
+ * \param[in]  config      Pointer to the configuration struct
+ *
+ * \return Status of the initialization procedure.
+ * \retval STATUS_OK                The initialization was successful
+ * \retval STATUS_ERR_INVALID_ARG   Invalid argument(s) were provided
+ * \retval STATUS_BUSY          The module is busy with a reset operation
+ * \retval STATUS_ERR_DENIED        The module is enabled
+ */
+enum status_code adc_init(
+    struct adc_module *const module_inst,
+    Adc *hw,
+    struct adc_config *config)
+{
+    /* Sanity check arguments */
+    Assert(module_inst);
+    Assert(hw);
+    Assert(config);
+
+    /* Temporary variable to hold ADC instance number */
+    uint8_t instance = _adc_get_inst_index(hw);
+
+    /* Associate the software module instance with the hardware module */
+    module_inst->hw = hw;
+
+    /* Turn on the digital interface clock */
+    system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, _adc_apbcmasks[instance]);
+
+    if (hw->CTRLA.reg & ADC_CTRLA_SWRST) {
+        /* We are in the middle of a reset. Abort. */
+        return STATUS_BUSY;
+    }
+
+    while (adc_is_syncing(module_inst)) {
+        /* Wait for synchronization */
+    }
+
+    if (hw->CTRLA.reg & ADC_CTRLA_ENABLE) {
+        /* Module must be disabled before initialization. Abort. */
+        return STATUS_ERR_DENIED;
+    }
+
+    /* Store the selected reference for later use */
+    module_inst->reference = config->reference;
+
+    /* Make sure the voltage reference is enabled if requested by the config */
+    if (module_inst->reference == ADC_REFERENCE_INTREF) {
+        system_voltage_reference_enable(SYSTEM_VOLTAGE_REFERENCE_OUTPUT);
+    }
+
+#if ADC_CALLBACK_MODE == true
+    for (uint8_t i = 0; i < ADC_CALLBACK_N; i++) {
+        module_inst->callback[i] = NULL;
+    };
+
+    module_inst->registered_callback_mask = 0;
+    module_inst->enabled_callback_mask = 0;
+    module_inst->remaining_conversions = 0;
+    module_inst->job_status = STATUS_OK;
+
+    _adc_instances[instance] = module_inst;
+
+    if (config->event_action == ADC_EVENT_ACTION_DISABLED &&
+            !config->freerunning) {
+        module_inst->software_trigger = true;
+    } else {
+        module_inst->software_trigger = false;
+    }
+#endif
+
+    /* Write configuration to module */
+    return _adc_set_config(instance, module_inst, config);
+}