diff options
author | NIIBE Yutaka <gniibe@fsij.org> | 2016-06-02 11:01:11 +0900 |
---|---|---|
committer | NIIBE Yutaka <gniibe@fsij.org> | 2016-06-02 11:01:11 +0900 |
commit | 78718e57df6f55c0670cdb6b70337204dd045dba (patch) | |
tree | 1e7a70e70931a0fdf65b04dd447e473c5e846bc7 /contrib/adc-mkl27z.c | |
parent | a756987d2ae2a029bfe5c8cf00a9853379fbcb91 (diff) |
Move files
Diffstat (limited to 'contrib/adc-mkl27z.c')
-rw-r--r-- | contrib/adc-mkl27z.c | 320 |
1 files changed, 320 insertions, 0 deletions
diff --git a/contrib/adc-mkl27z.c b/contrib/adc-mkl27z.c new file mode 100644 index 0000000..19f9f5b --- /dev/null +++ b/contrib/adc-mkl27z.c @@ -0,0 +1,320 @@ +/* + * adc-mkl27z.c - ADC driver for MKL27Z + * In this ADC driver, there are NeuG specific parts. + * It only records lower 8-bit of 16-bit data. + * You need to modify to use this as generic ADC driver. + * + * Copyright (C) 2016 Flying Stone Technology + * Author: NIIBE Yutaka <gniibe@fsij.org> + * + * This file is a part of Chopstx, a thread library for embedded. + * + * Chopstx is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Chopstx is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As additional permission under GNU GPL version 3 section 7, you may + * distribute non-source form of the Program without the copy of the + * GNU GPL normally required by section 4, provided you inform the + * receipents of GNU GPL by a written offer. + * + */ + +#include <stdint.h> +#include <stdlib.h> +#include <chopstx.h> +#include <mcu/mkl27z.h> + +struct DMAMUX { + volatile uint32_t CHCFG0; + volatile uint32_t CHCFG1; + volatile uint32_t CHCFG2; + volatile uint32_t CHCFG3; +}; +static struct DMAMUX *const DMAMUX = (struct DMAMUX *const)0x40021000; + +#define INTR_REQ_DMA0 0 + +struct DMA { + volatile uint32_t SAR; + volatile uint32_t DAR; + volatile uint32_t DSR_BCR; + volatile uint32_t DCR; +}; +static struct DMA *const DMA0 = (struct DMA *const)0x40008100; +static struct DMA *const DMA1 = (struct DMA *const)0x40008110; + + +/* We don't use ADC interrupt. Just for reference. */ +#define INTR_REQ_ADC 15 + +struct ADC { + volatile uint32_t SC1[2];/* Status and Control Registers 1 */ + volatile uint32_t CFG1; /* Configuration Register 1 */ + volatile uint32_t CFG2; /* Configuration Register 2 */ + volatile uint32_t R[2]; /* Data Result Register */ + + /* Compare Value Registers 1, 2 */ + volatile uint32_t CV1; + volatile uint32_t CV2; + + volatile uint32_t SC2; /* Status and Control Register 2 */ + volatile uint32_t SC3; /* Status and Control Register 3 */ + + volatile uint32_t OFS; /* Offset Correction Register */ + volatile uint32_t PG; /* Plus-Side Gain Register */ + volatile uint32_t MG; /* Minus-Side Gain Register */ + + /* Plus-Side General Calibration Value Registers */ + volatile uint32_t CLPD; + volatile uint32_t CLPS; + volatile uint32_t CLP4; + volatile uint32_t CLP3; + volatile uint32_t CLP2; + volatile uint32_t CLP1; + volatile uint32_t CLP0; + uint32_t rsvd0; + /* Minus-Side General Calibration Value Registers */ + volatile uint32_t CLMD; + volatile uint32_t CLMS; + volatile uint32_t CLM4; + volatile uint32_t CLM3; + volatile uint32_t CLM2; + volatile uint32_t CLM1; + volatile uint32_t CLM0; +}; +static struct ADC *const ADC0 = (struct ADC *const)0x4003B000; + +/* SC1 */ +#define ADC_SC1_DIFF (1 << 5) +#define ADC_SC1_AIEN (1 << 6) +#define ADC_SC1_COCO (1 << 7) +#define ADC_SC1_TEMPSENSOR 26 +#define ADC_SC1_BANDGAP 27 +#define ADC_SC1_ADCSTOP 31 + +/* CFG1 */ +#define ADC_CLOCK_SOURCE_ASYNCH (3 << 0) +#define ADC_MODE_16BIT (3 << 2) +#define ADC_ADLSMP_SHORT (0 << 4) +#define ADC_ADLSMP_LONG (1 << 4) +#define ADC_ADIV_1 (0 << 5) +#define ADC_ADIV_8 (3 << 5) +#define ADC_ADLPC_NORMAL (0 << 7) +#define ADC_ADLPC_LOWPOWER (1 << 7) +/**/ +#define ADC_CLOCK_SOURCE ADC_CLOCK_SOURCE_ASYNCH +#define ADC_MODE ADC_MODE_16BIT +#define ADC_ADLSMP ADC_ADLSMP_SHORT +#define ADC_ADIV ADC_ADIV_1 +#define ADC_ADLPC ADC_ADLPC_LOWPOWER + +/* CFG2 */ +#define ADC_ADLSTS_DEFAULT 0 /* 24 cycles if CFG1.ADLSMP=1, 4 if not. */ +#define ADC_ADHSC_NORMAL (0 << 2) +#define ADC_ADHSC_HIGHSPEED (1 << 2) +#define ADC_ADACK_DISABLE (0 << 3) +#define ADC_ADACK_ENABLE (1 << 3) +#define ADC_MUXSEL_A (0 << 4) +#define ADC_MUXSEL_B (1 << 4) +/**/ +#define ADC_ADLSTS ADC_ADLSTS_DEFAULT +#define ADC_ADHSC ADC_ADHSC_NORMAL +#define ADC_ADACKEN ADC_ADACK_ENABLE +#define ADC_MUXSEL ADC_MUXSEL_A + +/* SC2 */ +#define ADC_SC2_REFSEL_DEFAULT 1 /* Internal Voltage Reference??? */ +#define ADC_SC2_DMAEN (1 << 2) +#define ADC_SC2_ACREN (1 << 3) +#define ADC_SC2_ACFGT (1 << 4) +#define ADC_SC2_ACFE (1 << 5) +#define ADC_SC2_ADTRG (1 << 6) /* For hardware trigger */ + +/* SC3 */ +#define ADC_SC3_AVGS11 0x03 +#define ADC_SC3_AVGE (1 << 2) +#define ADC_SC3_ADCO (1 << 3) +#define ADC_SC3_CALF (1 << 6) +#define ADC_SC3_CAL (1 << 7) + +#define ADC_DMA_SLOT_NUM 40 + +/* + * Buffer to save ADC data. + */ +uint32_t adc_buf[64]; + +static const uint32_t adc0_sc1_setting = ADC_SC1_TEMPSENSOR; + +static chopstx_intr_t adc_intr; + +struct adc_internal { + uint32_t buf[64]; + uint8_t *p; + int phase : 8; + int count : 8; +}; +struct adc_internal adc; + +/* + * Initialize ADC module, do calibration. + * + * This is called by MAIN, only once, hopefully before creating any + * other threads (to be accurate). + * + * We configure ADC0 to kick DMA0, configure DMA0 to kick DMA1. + * DMA0 records output of ADC0 to the ADC.BUF. + * DMA1 kicks ADC0 again to get another value. + * + * ADC0 --[finish conversion]--> DMA0 --[Link channel 1]--> DMA1 + */ +int +adc_init (void) +{ + uint32_t v; + + /* Enable ADC0 and DMAMUX clock. */ + SIM->SCGC6 |= (1 << 27) | (1 << 1); + /* Enable DMA clock. */ + SIM->SCGC7 |= (1 << 8); + + /* ADC0 setting for calibration. */ + ADC0->CFG1 = ADC_CLOCK_SOURCE | ADC_MODE | ADC_ADLSMP | ADC_ADIV | ADC_ADLPC; + ADC0->CFG2 = ADC_ADLSTS | ADC_ADHSC | ADC_ADACKEN | ADC_MUXSEL; + ADC0->SC2 = ADC_SC2_REFSEL_DEFAULT; + ADC0->SC3 = ADC_SC3_CAL | ADC_SC3_CALF | ADC_SC3_AVGE | ADC_SC3_AVGS11; + + /* Wait ADC completion */ + while ((ADC0->SC1[0] & ADC_SC1_COCO) == 0) + if ((ADC0->SC3 & ADC_SC3_CALF) != 0) + /* Calibration failure */ + return -1; + + if ((ADC0->SC3 & ADC_SC3_CALF) != 0) + /* Calibration failure */ + return -1; + + /* Configure PG by the calibration values. */ + v = ADC0->CLP0 + ADC0->CLP1 + ADC0->CLP2 + ADC0->CLP3 + ADC0->CLP4 + ADC0->CLPS; + ADC0->PG = 0x8000 | (v >> 1); + + /* Configure MG by the calibration values. */ + v = ADC0->CLM0 + ADC0->CLM1 + ADC0->CLM2 + ADC0->CLM3 + ADC0->CLM4 + ADC0->CLMS; + ADC0->MG = 0x8000 | (v >> 1); + + ADC0->SC1[0] = ADC_SC1_ADCSTOP; + + /* DMAMUX setting. */ + DMAMUX->CHCFG0 = (1 << 7) | ADC_DMA_SLOT_NUM; + + /* DMA0 initial setting. */ + DMA0->SAR = (uint32_t)&ADC0->R[0]; + + /* DMA1 initial setting. */ + DMA1->SAR = (uint32_t)&adc0_sc1_setting; + DMA1->DAR = (uint32_t)&ADC0->SC1[0]; + + chopstx_claim_irq (&adc_intr, INTR_REQ_DMA0); + return 0; +} + +/* + * Start using ADC. + */ +void +adc_start (void) +{ + ADC0->CFG1 = ADC_CLOCK_SOURCE | ADC_MODE | ADC_ADLSMP | ADC_ADIV | ADC_ADLPC; + ADC0->CFG2 = ADC_ADLSTS | ADC_ADHSC | ADC_ADACKEN | ADC_MUXSEL; + ADC0->SC2 = ADC_SC2_REFSEL_DEFAULT | ADC_SC2_DMAEN; + ADC0->SC3 = 0; +} + +/* + * Kick getting data for COUNT times. + * Data will be saved in ADC_BUF starting at OFFSET. + */ +static void +adc_start_conversion_internal (int count) +{ + /* DMA0 setting. */ + DMA0->DAR = (uint32_t)&adc.buf[0]; + DMA0->DSR_BCR = 4 * count; + DMA0->DCR = (1 << 31) | (1 << 30) | (1 << 29) | (0 << 20) | (1 << 19) + | (0 << 17) | (1 << 7) | (2 << 4) | (1 << 2); + + /* Kick DMA1. */ + DMA1->DSR_BCR = 4 * count; + DMA1->DCR = (1 << 30) | (1 << 29) | (0 << 19) | (0 << 17) | (1 << 16) | (1 << 7); +} + + +/* + * Kick getting data for COUNT times. + * Data will be saved in ADC_BUF starting at OFFSET. + */ +void +adc_start_conversion (int offset, int count) +{ + adc.p = (uint8_t *)&adc_buf[offset]; + adc.phase = 0; + adc.count = count; + adc_start_conversion_internal (count); +} + + +static void +adc_stop_conversion (void) +{ + ADC0->SC1[0] = ADC_SC1_ADCSTOP; +} + +/* + * Stop using ADC. + */ +void +adc_stop (void) +{ + SIM->SCGC6 &= ~(1 << 27); +} + +/* + * Return 0 on success. + * Return 1 on error. + */ +int +adc_wait_completion (void) +{ + while (1) + { + int i; + + /* Wait DMA completion */ + chopstx_poll (NULL, 1, &adc_intr); + + DMA0->DSR_BCR = (1 << 24); + DMA1->DSR_BCR = (1 << 24); + + adc_stop_conversion (); + + for (i = 0; i < adc.count; i++) + *adc.p++ = (uint8_t)adc.buf[i]; + + if (++adc.phase >= 4) + break; + + adc_start_conversion_internal (adc.count); + } + + return 0; +} |