1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/cpu/kinetis/periph/adc.c
2020-01-10 14:13:14 +01:00

239 lines
6.5 KiB
C

/*
* Copyright (C) 2014-2016 Freie Universität Berlin
* Copyright (C) 2014 PHYTEC Messtechnik GmbH
* Copyright (C) 2015 Eistec AB
*
* This file is subject to the terms and conditions of the GNU Lesser General
* Public License v2.1. See the file LICENSE in the top level directory for more
* details.
*/
/**
* @ingroup cpu_kinetis
* @ingroup drivers_periph_adc
* @{
*
* @file
* @brief Low-level ADC driver implementation
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
* @author Johann Fischer <j.fischer@phytec.de>
* @author Jonas Remmert <j.remmert@phytec.de>
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
*
* @}
*/
#include <stdio.h>
#include "cpu.h"
#include "bit.h"
#include "mutex.h"
#include "periph/adc.h"
/**
* @brief Maximum clock speed
*
* The ADC clock speed must be configured to be >2MHz and <12MHz in order for
* the ADC to work. For ADC calibration to work as intended, the ADC clock speed
* must further no be >4MHz, so we target a ADC clock speed > 2 and <4MHz.
*/
#define ADC_MAX_CLK (4000000U)
static mutex_t locks[] = {
MUTEX_INIT,
#ifdef ADC1
MUTEX_INIT,
#endif
};
static inline ADC_Type *dev(adc_t line)
{
return adc_config[line].dev;
}
static inline int dev_num(adc_t line)
{
if (dev(line) == ADC0) {
return 0;
}
#ifdef ADC1
else if (dev(line) == ADC1) {
return 1;
}
#endif
return -1;
}
static inline void prep(adc_t line)
{
if (dev(line) == ADC0) {
bit_set32(&SIM->SCGC6, SIM_SCGC6_ADC0_SHIFT);
}
#ifdef ADC1
else if (dev(line) == ADC1) {
#if defined(SIM_SCGC3_ADC1_SHIFT)
bit_set32(&SIM->SCGC3, SIM_SCGC3_ADC1_SHIFT);
#elif defined(SIM_SCGC6_ADC1_SHIFT)
bit_set32(&SIM->SCGC6, SIM_SCGC6_ADC1_SHIFT);
#else
#error ADC1 clock gate is not known!
#endif
}
#endif /* ADC1 */
mutex_lock(&locks[dev_num(line)]);
}
static inline void done(adc_t line)
{
if (dev(line) == ADC0) {
bit_clear32(&SIM->SCGC6, SIM_SCGC6_ADC0_SHIFT);
}
#ifdef ADC1
else if (dev(line) == ADC1) {
#if defined(SIM_SCGC3_ADC1_SHIFT)
bit_clear32(&SIM->SCGC3, SIM_SCGC3_ADC1_SHIFT);
#elif defined(SIM_SCGC6_ADC1_SHIFT)
bit_clear32(&SIM->SCGC6, SIM_SCGC6_ADC1_SHIFT);
#endif
}
#endif /* ADC1 */
mutex_unlock(&locks[dev_num(line)]);
}
/**
* @brief Perform ADC calibration routine.
*
* This is a recipe taken straight from the Kinetis K60 reference manual. It has
* been tested on MK60DN512VLL10.
*
* @param[in] dev Pointer to ADC device to operate on.
*
* @return 0 on success
* @return <0 on errors
*/
int kinetis_adc_calibrate(ADC_Type *dev)
{
uint16_t cal;
dev->SC3 |= ADC_SC3_CAL_MASK;
while (dev->SC3 & ADC_SC3_CAL_MASK) {} /* wait for calibration to finish */
while (!(dev->SC1[0] & ADC_SC1_COCO_MASK)) {}
if (dev->SC3 & ADC_SC3_CALF_MASK) {
/* calibration failed for some reason, possibly SC2[ADTRG] is 1 ? */
return -1;
}
/* From the reference manual */
/* 1. Initialize or clear a 16-bit variable in RAM. */
/* 2. Add the plus-side calibration results CLP0, CLP1, CLP2, CLP3, CLP4,
* and CLPS to the variable. */
cal = dev->CLP0 + dev->CLP1 + dev->CLP2 + dev->CLP3 + dev->CLP4 + dev->CLPS;
/* 3. Divide the variable by two. */
cal /= 2;
/* 4. Set the MSB of the variable. */
cal |= (1 << 15);
/* (5. The previous two steps can be achieved by setting the carry bit,
* rotating to the right through the carry bit on the high byte and again on
* the low byte.)
* We ignore the above optimization suggestion, we most likely only perform
* this calibration on startup and it will only save nanoseconds. */
/* 6. Store the value in the plus-side gain calibration register PG. */
dev->PG = cal;
/* 7. Repeat the procedure for the minus-side gain calibration value. */
cal = dev->CLM0 + dev->CLM1 + dev->CLM2 + dev->CLM3 + dev->CLM4 + dev->CLMS;
cal /= 2;
cal |= (1 << 15);
dev->MG = cal;
return 0;
}
int adc_init(adc_t line)
{
/* make sure the given line is valid */
if (line >= ADC_NUMOF) {
return -1;
}
/* prepare the device: lock and power on */
prep(line);
/* configure the connected pin mux */
if (adc_config[line].pin != GPIO_UNDEF) {
gpio_init_port(adc_config[line].pin, GPIO_AF_ANALOG);
}
/* The ADC requires at least 2 MHz module clock for full accuracy, and less
* than 12 MHz */
/* For the calibration it is important that the ADC clock is <= 4 MHz */
uint32_t adiv;
if (CLOCK_BUSCLOCK > (ADC_MAX_CLK << 3)) {
#if KINETIS_HAVE_ADICLK_BUS_DIV_2
/* Some CPUs, e.g. MK60D10, MKW22D5, provide an additional divide by two
* divider for the bus clock as CFG1[ADICLK] = 0b01
*/
adiv = ADC_CFG1_ADIV(3) | ADC_CFG1_ADICLK(1);
#else
/* Newer CPUs seem to have replaced this with various alternate clock
* sources instead */
adiv = ADC_CFG1_ADIV(3);
#endif
}
else {
unsigned int i = 0;
while ((i < 3) && (CLOCK_BUSCLOCK > (ADC_MAX_CLK << i))) {
++i;
}
adiv = ADC_CFG1_ADIV(i);
}
/* set configuration register 1: clocking and precision */
/* Set long sample time */
dev(line)->CFG1 = ADC_CFG1_ADLSMP_MASK | adiv;
/* select ADxxb channels, longest sample time (20 extra ADC cycles) */
dev(line)->CFG2 = ADC_CFG2_MUXSEL_MASK | ADC_CFG2_ADLSTS(0);
/* select software trigger, external ref pins */
dev(line)->SC2 = ADC_SC2_REFSEL(ADC_REF_SETTING);
/* set an arbitrary input channel configuration */
dev(line)->SC3 = 0;
dev(line)->SC1[0] = ADC_SC1_ADCH(31);
/* perform calibration routine */
int res = kinetis_adc_calibrate(dev(line));
done(line);
return res;
}
int32_t adc_sample(adc_t line, adc_res_t res)
{
int sample;
/* check if resolution is applicable */
if (res > 0xf0) {
return -1;
}
/* lock and power on the device */
prep(line);
/* set resolution */
dev(line)->CFG1 &= ~(ADC_CFG1_MODE_MASK);
dev(line)->CFG1 |= (res);
/* set the chosen hardware averaging setting for this channel */
dev(line)->SC3 = adc_config[line].avg;
/* select the channel that is sampled */
dev(line)->SC1[0] = adc_config[line].chan;
/* wait until conversion is complete */
while (!(dev(line)->SC1[0] & ADC_SC1_COCO_MASK)) {}
/* read and return result */
sample = (int)dev(line)->R[0];
/* power off and unlock the device */
done(line);
return sample;
}