mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
c0628a3058
add peripheral drivers for Freescale Kinetis MCUs: adc driver cpuid driver gpio driver hwtimer_arch driver (hwtimer used Low Power Timer) i2c driver (master mode only) mcg driver pwm driver random_rnga driver random_rngb driver rtc driver spi driver timer driver (timer used Periodic Interrupt Timer) uart driver add doc.txt (configuration examples) random_rnga: Update RNGA driver in preparation for RNGB driver. random_rngb: Add RNGB driver. spi: refactor SPI to work for multiple CTARS, add spi_acquire, spi_release gpio: Add gpio_irq_enable, gpio_irq_disable. Refactor GPIO. gpio: Add gpio_irq_enable, gpio_irq_disable. gpio: Refactor ISR functions to work with all GPIOs (0-31) and all ports (PORTA-PORTH) adc: Refactor ADC, add calibration and scaling. Added integer scaling of results in adc_map. Handle precision setting in adc_init. Set ADC clock divider depending on module clock. Add ADC_1 as a possible device. Add ADC calibration procedure according to K60 ref manual. Handle ADC pins which are not part of the pin function mux. Signed-off-by: Joakim Gebart <joakim.gebart@eistec.se>
427 lines
11 KiB
C
427 lines
11 KiB
C
/*
|
|
* Copyright (C) 2014 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_common_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 Gebart <joakim.gebart@eistec.se>
|
|
*
|
|
* @}
|
|
*/
|
|
#include <stdio.h>
|
|
|
|
#include "cpu.h"
|
|
#include "periph/adc.h"
|
|
#include "periph_conf.h"
|
|
|
|
/* guard in case that no ADC device is defined */
|
|
#if ADC_NUMOF
|
|
|
|
typedef struct {
|
|
int max_value;
|
|
int bits;
|
|
} adc_config_t;
|
|
|
|
adc_config_t adc_config[ADC_NUMOF];
|
|
|
|
/**
|
|
* @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] ADC_ptr Pointer to ADC device to operate on.
|
|
*
|
|
* @return 0 on success
|
|
* @return <0 on errors
|
|
*/
|
|
int kinetis_adc_calibrate(ADC_Type *ADC_ptr)
|
|
{
|
|
uint16_t cal;
|
|
|
|
ADC_ptr->SC3 |= ADC_SC3_CAL_MASK;
|
|
|
|
while (ADC_ptr->SC3 & ADC_SC3_CAL_MASK); /* wait for calibration to finish */
|
|
|
|
while (!(ADC_ptr->SC1[0] & ADC_SC1_COCO_MASK));
|
|
|
|
if (ADC_ptr->SC3 & ADC_SC3_CALF_MASK) {
|
|
/* calibration failed for some reason, possibly SC2[ADTRG] is 1 ? */
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Following the steps in 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 = ADC_ptr->CLP0 + ADC_ptr->CLP1 + ADC_ptr->CLP2 + ADC_ptr->CLP3 +
|
|
ADC_ptr->CLP4 + ADC_ptr->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. */
|
|
ADC_ptr->PG = cal;
|
|
|
|
/* 7. Repeat the procedure for the minus-side gain calibration value. */
|
|
cal = ADC_ptr->CLM0 + ADC_ptr->CLM1 + ADC_ptr->CLM2 + ADC_ptr->CLM3 +
|
|
ADC_ptr->CLM4 + ADC_ptr->CLMS;
|
|
cal /= 2;
|
|
cal |= (1 << 15);
|
|
ADC_ptr->MG = cal;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int adc_init(adc_t dev, adc_precision_t precision)
|
|
{
|
|
ADC_Type *adc = 0;
|
|
PORT_Type *port[ADC_MAX_CHANNELS];
|
|
uint8_t pins[ADC_MAX_CHANNELS];
|
|
uint8_t af[ADC_MAX_CHANNELS];
|
|
int channels = 0;
|
|
uint32_t mode = 0;
|
|
uint32_t div = 0;
|
|
int i = 0;
|
|
uint32_t clock = 0;
|
|
|
|
adc_poweron(dev);
|
|
|
|
switch (dev) {
|
|
#if ADC_0_EN
|
|
|
|
case ADC_0:
|
|
adc = ADC_0_DEV;
|
|
port[0] = ADC_0_CH0_PORT;
|
|
port[1] = ADC_0_CH1_PORT;
|
|
port[2] = ADC_0_CH2_PORT;
|
|
port[3] = ADC_0_CH3_PORT;
|
|
port[4] = ADC_0_CH4_PORT;
|
|
port[5] = ADC_0_CH5_PORT;
|
|
pins[0] = ADC_0_CH0_PIN;
|
|
pins[1] = ADC_0_CH1_PIN;
|
|
pins[2] = ADC_0_CH2_PIN;
|
|
pins[3] = ADC_0_CH3_PIN;
|
|
pins[4] = ADC_0_CH4_PIN;
|
|
pins[5] = ADC_0_CH5_PIN;
|
|
af[0] = ADC_0_CH0_PIN_AF;
|
|
af[1] = ADC_0_CH1_PIN_AF;
|
|
af[2] = ADC_0_CH2_PIN_AF;
|
|
af[3] = ADC_0_CH3_PIN_AF;
|
|
af[4] = ADC_0_CH4_PIN_AF;
|
|
af[5] = ADC_0_CH5_PIN_AF;
|
|
channels = ADC_0_CHANNELS;
|
|
clock = ADC_0_MODULE_CLOCK;
|
|
ADC_0_PORT_CLKEN();
|
|
break;
|
|
#endif
|
|
#if ADC_1_EN
|
|
|
|
case ADC_1:
|
|
adc = ADC_1_DEV;
|
|
port[0] = ADC_1_CH0_PORT;
|
|
port[1] = ADC_1_CH1_PORT;
|
|
port[2] = ADC_1_CH2_PORT;
|
|
port[3] = ADC_1_CH3_PORT;
|
|
port[4] = ADC_1_CH4_PORT;
|
|
port[5] = ADC_1_CH5_PORT;
|
|
pins[0] = ADC_1_CH0_PIN;
|
|
pins[1] = ADC_1_CH1_PIN;
|
|
pins[2] = ADC_1_CH2_PIN;
|
|
pins[3] = ADC_1_CH3_PIN;
|
|
pins[4] = ADC_1_CH4_PIN;
|
|
pins[5] = ADC_1_CH5_PIN;
|
|
af[0] = ADC_1_CH0_PIN_AF;
|
|
af[1] = ADC_1_CH1_PIN_AF;
|
|
af[2] = ADC_1_CH2_PIN_AF;
|
|
af[3] = ADC_1_CH3_PIN_AF;
|
|
af[4] = ADC_1_CH4_PIN_AF;
|
|
af[5] = ADC_1_CH5_PIN_AF;
|
|
channels = ADC_1_CHANNELS;
|
|
clock = ADC_1_MODULE_CLOCK;
|
|
ADC_1_PORT_CLKEN();
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
if (channels > ADC_MAX_CHANNELS) {
|
|
return -1;
|
|
}
|
|
|
|
/* set precision, these numbers are valid for the K60 */
|
|
switch (precision) {
|
|
case ADC_RES_6BIT:
|
|
/* Not supported by hardware, use 8 bit mode. */
|
|
mode = ADC_CFG1_MODE(0);
|
|
adc_config[dev].bits = 8;
|
|
break;
|
|
|
|
case ADC_RES_8BIT:
|
|
mode = ADC_CFG1_MODE(0);
|
|
adc_config[dev].bits = 8;
|
|
break;
|
|
|
|
case ADC_RES_10BIT:
|
|
mode = ADC_CFG1_MODE(2);
|
|
adc_config[dev].bits = 10;
|
|
break;
|
|
|
|
case ADC_RES_12BIT:
|
|
mode = ADC_CFG1_MODE(1);
|
|
adc_config[dev].bits = 12;
|
|
break;
|
|
|
|
case ADC_RES_14BIT:
|
|
/* Not supported by hardware, use 16 bit mode. */
|
|
mode = ADC_CFG1_MODE(3);
|
|
adc_config[dev].bits = 16;
|
|
break;
|
|
|
|
case ADC_RES_16BIT:
|
|
mode = ADC_CFG1_MODE(3);
|
|
adc_config[dev].bits = 16;
|
|
break;
|
|
}
|
|
|
|
adc_config[dev].max_value = (1 << adc_config[dev].bits) - 1;
|
|
|
|
for (i = 0; i < channels; i++) {
|
|
/* Check whether we need to set the pin mux for this channel. */
|
|
if (port[i] != NULL) {
|
|
port[i]->PCR[pins[i]] = PORT_PCR_MUX(af[i]);
|
|
}
|
|
}
|
|
|
|
/* 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 */
|
|
if (clock > 4000000 * 8) {
|
|
/* Need to divide by 16, we set the clock input to BusClock/2 and
|
|
* divide clock by 8 (the maximum divider) */
|
|
div = ADC_CFG1_ADIV(3) | ADC_CFG1_ADICLK(1);
|
|
}
|
|
else if (clock > 4000000 * 4) {
|
|
/* divide clock by 8 */
|
|
div = ADC_CFG1_ADIV(3);
|
|
}
|
|
else if (clock > 4000000 * 2) {
|
|
/* divide clock by 4 */
|
|
div = ADC_CFG1_ADIV(2);
|
|
}
|
|
else if (clock > 4000000 * 1) {
|
|
/* divide clock by 2 */
|
|
div = ADC_CFG1_ADIV(1);
|
|
}
|
|
else {
|
|
/* no clock divider */
|
|
div = ADC_CFG1_ADIV(0);
|
|
}
|
|
|
|
/* set configuration register 1: clocking and precision */
|
|
/* Set long sample time */
|
|
adc->CFG1 = ADC_CFG1_ADLSMP_MASK | mode | div;
|
|
|
|
/* select ADxxb channels, longest sample time (20 extra ADC cycles) */
|
|
adc->CFG2 = ADC_CFG2_MUXSEL_MASK | ADC_CFG2_ADLSTS(0);
|
|
|
|
/* select software trigger, external ref pins */
|
|
adc->SC2 = ADC_SC2_REFSEL(0);
|
|
|
|
/* select hardware average over 32 samples */
|
|
adc->SC3 = ADC_SC3_AVGE_MASK | ADC_SC3_AVGS(3);
|
|
|
|
/* set an (arbitrary) input channel, single-ended mode */
|
|
adc->SC1[0] = ADC_SC1_ADCH(ADC_0_CH0);
|
|
adc->SC1[1] = ADC_SC1_ADCH(ADC_0_CH0);
|
|
|
|
/* perform calibration routine */
|
|
if (kinetis_adc_calibrate(adc) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int adc_sample(adc_t dev, int channel)
|
|
{
|
|
ADC_Type *adc = 0;
|
|
|
|
switch (dev) {
|
|
#if ADC_0_EN
|
|
|
|
case ADC_0:
|
|
adc = ADC_0_DEV;
|
|
|
|
/* start single conversion on corresponding channel */
|
|
switch (channel) {
|
|
case 0:
|
|
adc->SC1[0] = ADC_SC1_ADCH(ADC_0_CH0);
|
|
break;
|
|
|
|
case 1:
|
|
adc->SC1[0] = ADC_SC1_ADCH(ADC_0_CH1);
|
|
break;
|
|
|
|
case 2:
|
|
adc->SC1[0] = ADC_SC1_ADCH(ADC_0_CH2);
|
|
break;
|
|
|
|
case 3:
|
|
adc->SC1[0] = ADC_SC1_ADCH(ADC_0_CH3);
|
|
break;
|
|
|
|
case 4:
|
|
adc->SC1[0] = ADC_SC1_ADCH(ADC_0_CH4);
|
|
break;
|
|
|
|
case 5:
|
|
adc->SC1[0] = ADC_SC1_ADCH(ADC_0_CH5);
|
|
break;
|
|
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
break;
|
|
#endif
|
|
#if ADC_1_EN
|
|
|
|
case ADC_1:
|
|
adc = ADC_1_DEV;
|
|
|
|
/* start single conversion on corresponding channel */
|
|
switch (channel) {
|
|
case 0:
|
|
adc->SC1[0] = ADC_SC1_ADCH(ADC_1_CH0);
|
|
break;
|
|
|
|
case 1:
|
|
adc->SC1[0] = ADC_SC1_ADCH(ADC_1_CH1);
|
|
break;
|
|
|
|
case 2:
|
|
adc->SC1[0] = ADC_SC1_ADCH(ADC_1_CH2);
|
|
break;
|
|
|
|
case 3:
|
|
adc->SC1[0] = ADC_SC1_ADCH(ADC_1_CH3);
|
|
break;
|
|
|
|
case 4:
|
|
adc->SC1[0] = ADC_SC1_ADCH(ADC_1_CH4);
|
|
break;
|
|
|
|
case 5:
|
|
adc->SC1[0] = ADC_SC1_ADCH(ADC_1_CH5);
|
|
break;
|
|
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
/* wait until conversion is complete */
|
|
/* TODO: Use interrupts and yield the thread */
|
|
while (!(adc->SC1[0] & ADC_SC1_COCO_MASK));
|
|
|
|
/* read and return result */
|
|
return (int)adc->R[0];
|
|
}
|
|
|
|
void adc_poweron(adc_t dev)
|
|
{
|
|
switch (dev) {
|
|
#if ADC_0_EN
|
|
|
|
case ADC_0:
|
|
ADC_0_CLKEN();
|
|
break;
|
|
#endif
|
|
#if ADC_1_EN
|
|
|
|
case ADC_1:
|
|
ADC_1_CLKEN();
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void adc_poweroff(adc_t dev)
|
|
{
|
|
switch (dev) {
|
|
#if ADC_0_EN
|
|
|
|
case ADC_0:
|
|
ADC_0_CLKDIS();
|
|
break;
|
|
#endif
|
|
#if ADC_1_EN
|
|
|
|
case ADC_1:
|
|
ADC_1_CLKDIS();
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
int adc_map(adc_t dev, int value, int min, int max)
|
|
{
|
|
int scale = max - min;
|
|
|
|
/* NB: There is additional room for the multiplication result if using the
|
|
* actual precision setting of the ADC as the limit in this if statement: */
|
|
if (scale < (1 << 16)) {
|
|
/* The ADCs on these CPUs are limited to 16 bits, the result of
|
|
* value x scale will be 31 bits long or less. We use the upper part
|
|
* of a 32 bit word when scaling */
|
|
int32_t tmp = value * scale;
|
|
/* Divide by ADC range to get the scaled result */
|
|
return (tmp / (1 << adc_config[dev].bits));
|
|
}
|
|
else {
|
|
/* Target scale is too large to use int32_t as an in between result */
|
|
int64_t tmp = scale;
|
|
/* Make sure the compiler does not generate code which will truncate the
|
|
* result. */
|
|
tmp *= value;
|
|
/* Divide by ADC range to get the scaled result */
|
|
tmp /= (1 << adc_config[dev].bits);
|
|
return tmp;
|
|
}
|
|
}
|
|
|
|
float adc_mapf(adc_t dev, int value, float min, float max)
|
|
{
|
|
return ((max - min) / ((float)adc_config[dev].max_value)) * value;
|
|
}
|
|
|
|
#endif /* ADC_NUMOF */
|