diff options
author | Kevin O'Connor <kevin@koconnor.net> | 2018-12-26 16:50:44 -0500 |
---|---|---|
committer | Kevin O'Connor <kevin@koconnor.net> | 2019-01-07 19:33:26 -0500 |
commit | 94c86d6c6ce85143d47b79f7cc2680c6b0ee6889 (patch) | |
tree | a9b8e293c4ba986e306366f039d9b979086bb12b /src/sam3/sam4e_afec.c | |
parent | e278552d44e2d795e335ec33f898c9d6e1413688 (diff) | |
download | kutter-94c86d6c6ce85143d47b79f7cc2680c6b0ee6889.tar.gz kutter-94c86d6c6ce85143d47b79f7cc2680c6b0ee6889.tar.xz kutter-94c86d6c6ce85143d47b79f7cc2680c6b0ee6889.zip |
sam3: Merge sam4e8e support into sam3 code
Most of the peripherals on the sam4e8e are similar to the ones on the
sam3x8e mcu. Merge the code together and use just one code directory.
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
Diffstat (limited to 'src/sam3/sam4e_afec.c')
-rw-r--r-- | src/sam3/sam4e_afec.c | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/src/sam3/sam4e_afec.c b/src/sam3/sam4e_afec.c new file mode 100644 index 00000000..69205c50 --- /dev/null +++ b/src/sam3/sam4e_afec.c @@ -0,0 +1,206 @@ +// SAM4e8e Analog Front-End Converter (AFEC) support +// +// Copyright (C) 2018 Florian Heilmann <Florian.Heilmann@gmx.net> +// +// This file may be distributed under the terms of the GNU GPLv3 license. + +#include "autoconf.h" // CONFIG_CLOCK_FREQ +#include "command.h" // shutdown +#include "gpio.h" // gpio_adc_setup +#include "internal.h" // GPIO +#include "sam4e.h" // AFEC0 +#include "sched.h" // sched_shutdown + +static const uint8_t afec_pins[] = { + //remove first channel, since it offsets the channel number: GPIO('A', 8), + GPIO('A', 17), GPIO('A', 18), GPIO('A', 19), + GPIO('A', 20), GPIO('B', 0), GPIO('B', 1), GPIO('C', 13), + GPIO('C', 15), GPIO('C', 12), GPIO('C', 29), GPIO('C', 30), + GPIO('C', 31), GPIO('C', 26), GPIO('C', 27), GPIO('C',0), + // AFEC1 + GPIO('B', 2), GPIO('B', 3), GPIO('A', 21), GPIO('A', 22), + GPIO('C', 1), GPIO('C', 2), GPIO('C', 3), GPIO('C', 4), +}; + +#define AFEC1_START 15 // The first 15 pins are on afec0 + +static inline struct gpio_adc +pin_to_gpio_adc(uint8_t pin) +{ + int chan; + for (chan=0; ; chan++) { + if (chan >= ARRAY_SIZE(afec_pins)) + shutdown("Not a valid ADC pin"); + if (afec_pins[chan] == pin) { + break; + } + } + return (struct gpio_adc){ .chan=chan }; +} + +static inline Afec * +gpio_adc_to_afec(struct gpio_adc g) +{ + return (g.chan >= AFEC1_START ? AFEC1 : AFEC0); +} + +static inline uint32_t +gpio_adc_to_afec_chan(struct gpio_adc g) +{ + return (g.chan >= AFEC1_START ? g.chan - AFEC1_START : g.chan); +} + +#define ADC_FREQ_MAX 6000000UL +DECL_CONSTANT(ADC_MAX, 4095); + +static int +init_afec(Afec* afec) { + + // Enable PMC + if (afec == AFEC0) + PMC->PMC_PCER0 = 1 << ID_AFEC0; + else + PMC->PMC_PCER0 = 1 << ID_AFEC1; + + // If busy, return busy + if ((afec->AFE_ISR & AFE_ISR_DRDY) == AFE_ISR_DRDY) { + return -1; + } + + // Reset + afec->AFE_CR = AFE_CR_SWRST; + + // Configure afec + afec->AFE_MR = AFE_MR_ANACH_ALLOWED | \ + AFE_MR_PRESCAL (SystemCoreClock / (2 * ADC_FREQ_MAX) -1) | \ + AFE_MR_SETTLING_AST3 | \ + AFE_MR_TRACKTIM(2) | \ + AFE_MR_TRANSFER(1) | \ + AFE_MR_STARTUP_SUT64; + afec->AFE_EMR = AFE_EMR_TAG | \ + AFE_EMR_RES_NO_AVERAGE | \ + AFE_EMR_STM; + afec->AFE_ACR = AFE_ACR_IBCTL(1); + + // Disable interrupts + afec->AFE_IDR = 0xDF00803F; + + // Disable SW triggering + uint32_t mr = afec->AFE_MR; + + mr &= ~(AFE_MR_TRGSEL_Msk | AFE_MR_TRGEN | AFE_MR_FREERUN_ON); + mr |= AFE_MR_TRGEN_DIS; + afec->AFE_MR = mr; + + return 0; +} + +void +gpio_afec_init(void) { + + while(init_afec(AFEC0) != 0) { + (void)(AFEC0->AFE_LCDR & AFE_LCDR_LDATA_Msk); + } + while(init_afec(AFEC1) != 0) { + (void)(AFEC1->AFE_LCDR & AFE_LCDR_LDATA_Msk); + } + +} +DECL_INIT(gpio_afec_init); + +struct gpio_adc +gpio_adc_setup(uint8_t pin) +{ + struct gpio_adc adc_pin = pin_to_gpio_adc(pin); + Afec *afec = gpio_adc_to_afec(adc_pin); + uint32_t afec_chan = gpio_adc_to_afec_chan(adc_pin); + + //config channel + uint32_t reg = afec->AFE_DIFFR; + reg &= ~(1u << afec_chan); + afec->AFE_DIFFR = reg; + reg = afec->AFE_CGR; + reg &= ~(0x03u << (2 * afec_chan)); + reg |= 1 << (2 * afec_chan); + afec->AFE_CGR = reg; + + // Configure channel + // afec_ch_get_config_defaults(&ch_cfg); + // afec_ch_set_config(afec, afec_chan, &ch_cfg); + // Remove default internal offset from channel + // See Atmel Appnote AT03078 Section 1.5 + afec->AFE_CSELR = afec_chan; + afec->AFE_COCR = (0x800 & AFE_COCR_AOFF_Msk); + + // Enable and calibrate Channel + afec->AFE_CHER = 1 << afec_chan; + + reg = afec->AFE_CHSR; + afec->AFE_CDOR = reg; + afec->AFE_CR = AFE_CR_AUTOCAL; + + return adc_pin; +} + +enum { AFE_DUMMY=0xff }; +uint8_t active_channel = AFE_DUMMY; + +// Try to sample a value. Returns zero if sample ready, otherwise +// returns the number of clock ticks the caller should wait before +// retrying this function. +uint32_t +gpio_adc_sample(struct gpio_adc g) +{ + Afec *afec = gpio_adc_to_afec(g); + uint32_t afec_chan = gpio_adc_to_afec_chan(g); + if (active_channel == g.chan) { + if ((afec->AFE_ISR & AFE_ISR_DRDY) + && (afec->AFE_ISR & (1 << afec_chan))) { + // Sample now ready + return 0; + } else { + // Busy + goto need_delay; + } + } else if (active_channel != AFE_DUMMY) { + goto need_delay; + } + + afec->AFE_CHDR = 0x803F; // Disable all channels + afec->AFE_CHER = 1 << afec_chan; + + active_channel = g.chan; + + for (uint32_t chan = 0; chan < 16; ++chan) + { + if ((afec->AFE_ISR & (1 << chan)) != 0) + { + afec->AFE_CSELR = chan; + (void)(afec->AFE_CDR); + } + } + afec->AFE_CR = AFE_CR_START; + +need_delay: + return ADC_FREQ_MAX * 10000ULL / CONFIG_CLOCK_FREQ; // about 400 mcu clock cycles or 40 afec cycles +} + +// Read a value; use only after gpio_adc_sample() returns zero +uint16_t +gpio_adc_read(struct gpio_adc g) +{ + Afec *afec = gpio_adc_to_afec(g); + uint32_t afec_chan = gpio_adc_to_afec_chan(g); + active_channel = AFE_DUMMY; + afec->AFE_CSELR = afec_chan; + return afec->AFE_CDR; +} + +// Cancel a sample that may have been started with gpio_adc_sample() +void +gpio_adc_cancel_sample(struct gpio_adc g) +{ + if (active_channel == g.chan) { + active_channel = AFE_DUMMY; + } +} |