From b30d0617aa9770eaa946c46f87bb973dbf168f15 Mon Sep 17 00:00:00 2001 From: smlng <s@mlng.net> Date: Thu, 6 Jul 2017 13:38:54 +0200 Subject: [PATCH] cpu, cc2538: add low-lever adc driver --- cpu/cc2538/include/cpu_conf.h | 4 +- cpu/cc2538/include/periph_cpu.h | 75 +++++++++++++++++++-- cpu/cc2538/periph/adc.c | 114 ++++++++++++++++++++++++++++++++ 3 files changed, 186 insertions(+), 7 deletions(-) create mode 100644 cpu/cc2538/periph/adc.c diff --git a/cpu/cc2538/include/cpu_conf.h b/cpu/cc2538/include/cpu_conf.h index 7376a90241..b2c1e0f2e7 100644 --- a/cpu/cc2538/include/cpu_conf.h +++ b/cpu/cc2538/include/cpu_conf.h @@ -7,7 +7,7 @@ */ /** - * @addtogroup cpu_cc2538 + * @ingroup cpu_cc2538 * @{ * * @file @@ -35,7 +35,7 @@ extern "C" { #endif /** - * @brief ARM Cortex-M specific CPU configuration + * @name ARM Cortex-M specific CPU configuration * @{ */ #define CPU_DEFAULT_IRQ_PRIO (1U) diff --git a/cpu/cc2538/include/periph_cpu.h b/cpu/cc2538/include/periph_cpu.h index 04c5ad8bec..83d7877c17 100644 --- a/cpu/cc2538/include/periph_cpu.h +++ b/cpu/cc2538/include/periph_cpu.h @@ -160,7 +160,7 @@ typedef struct { } i2c_conf_t; /** - * @brief declare needed generic SPI functions + * @name declare needed generic SPI functions * @{ */ #define PERIPH_SPI_NEEDS_INIT_CS @@ -170,7 +170,7 @@ typedef struct { /** @} */ /** - * @brief Override the default GPIO mode settings + * @name Override the default GPIO mode settings * @{ */ #define HAVE_GPIO_MODE_T @@ -185,7 +185,7 @@ typedef enum { /** @} */ /** - * @brief Override SPI mode settings + * @name Override SPI mode settings * @{ */ #define HAVE_SPI_MODE_T @@ -198,7 +198,7 @@ typedef enum { /** @ */ /** - * @brief Override SPI clock settings + * @name Override SPI clock settings * @{ */ #define HAVE_SPI_CLK_T @@ -220,7 +220,7 @@ typedef struct { } spi_clk_conf_t; /** - * @brief SPI configuration data structure + * @name SPI configuration data structure * @{ */ typedef struct { @@ -241,6 +241,71 @@ typedef struct { uint_fast8_t cfg; /**< timer config word */ } timer_conf_t; +/** + * @name Override resolution options + * @{ + */ +#define HAVE_ADC_RES_T +typedef enum { + ADC_RES_6BIT = (0xa00), /**< not supported by hardware */ + ADC_RES_7BIT = (0 << 4), /**< ADC resolution: 7 bit */ + ADC_RES_8BIT = (0xb00), /**< not supported by hardware */ + ADC_RES_9BIT = (1 << 4), /**< ADC resolution: 9 bit */ + ADC_RES_10BIT = (2 << 4), /**< ADC resolution: 10 bit */ + ADC_RES_12BIT = (3 << 4), /**< ADC resolution: 12 bit */ + ADC_RES_14BIT = (0xc00), /**< not supported by hardware */ + ADC_RES_16BIT = (0xd00), /**< not supported by hardware */ +} adc_res_t; +/** @}Â */ + +/** + * @brief ADC configuration wrapper + */ +typedef gpio_t adc_conf_t; + +/** + * @name SOC_ADC_ADCCON3 register bit masks + * @{ + */ +#define SOC_ADC_ADCCON3_EREF (0x000000C0) /**< Reference voltage for extra */ +#define SOC_ADC_ADCCON3_EDIV (0x00000030) /**< Decimation rate for extra */ +#define SOC_ADC_ADCCON3_ECH (0x0000000F) /**< Single channel select */ +/** @} */ + +/** + * @name SOC_ADC_ADCCONx registers field values + * @{ + */ +#define SOC_ADC_ADCCON_REF_INT (0 << 6) /**< Internal reference */ +#define SOC_ADC_ADCCON_REF_EXT (1 << 6) /**< External reference on AIN7 pin */ +#define SOC_ADC_ADCCON_REF_AVDD5 (2 << 6) /**< AVDD5 pin */ +#define SOC_ADC_ADCCON_REF_DIFF (3 << 6) /**< External reference on AIN6-AIN7 differential input */ +#define SOC_ADC_ADCCON_CH_GND (0xC) /**< GND */ +/** @} */ + +/** + * @brief Mask to check end-of-conversion (EOC) bit + */ +#define SOC_ADC_ADCCON1_EOC_MASK (0x80) + +/** + * @name Masks for ADC raw data + * @{ + */ +#define SOC_ADC_ADCL_MASK (0x000000FC) +#define SOC_ADC_ADCH_MASK (0x000000FF) +/** @} */ + +/** + * @name Bit shift for data per ADC resolution + * @{ + */ +#define SOCADC_7_BIT_RSHIFT (9U) /**< Mask for getting data( 7 bits ENOB) */ +#define SOCADC_9_BIT_RSHIFT (7U) /**< Mask for getting data( 9 bits ENOB) */ +#define SOCADC_10_BIT_RSHIFT (6U) /**< Mask for getting data(10 bits ENOB) */ +#define SOCADC_12_BIT_RSHIFT (4U) /**< Mask for getting data(12 bits ENOB) */ +/** @} */ + #ifdef __cplusplus } #endif diff --git a/cpu/cc2538/periph/adc.c b/cpu/cc2538/periph/adc.c new file mode 100644 index 0000000000..62e5909333 --- /dev/null +++ b/cpu/cc2538/periph/adc.c @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2017 HAW Hamburg + * + * 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_cc2538 + * @ingroup drivers_periph_adc + * @{ + * + * @file + * @brief Low-level ADC driver implementation + * + * @notice based on TI peripheral drivers library + * + * @author Sebastian Meiling <s@mlng.net> + * @} + */ + +#include "board.h" +#include "cpu.h" +#include "periph_conf.h" +#include "periph_cpu.h" +#include "periph/adc.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +int adc_init(adc_t line) +{ + if (line >= ADC_NUMOF) { + DEBUG("adc_init: invalid ADC line (%d)!\n", line); + return -1; + } + + cc2538_soc_adc_t *adca = SOC_ADC; + /* stop random number generator, and set STSEL = 1 */ + adca->cc2538_adc_adccon1.ADCCON1 = 0x3c; + /* disable any DMA, continous ADC settings */ + adca->ADCCON2 = 0x0; + /* configure ADC GPIO as analog input */ + gpio_init(adc_config[line], IOC_OVERRIDE_ANA); + + return 0; +} + +int adc_sample(adc_t line, adc_res_t res) +{ + /* check if adc line valid */ + if (line >= ADC_NUMOF) { + DEBUG("adc_sample: invalid ADC line!\n"); + return -1; + } + + uint8_t rshift; + /* check if given resolution valid, and set right shift */ + switch(res) { + case ADC_RES_7BIT: + rshift = SOCADC_7_BIT_RSHIFT; + break; + case ADC_RES_9BIT: + rshift = SOCADC_9_BIT_RSHIFT; + break; + case ADC_RES_10BIT: + rshift = SOCADC_10_BIT_RSHIFT; + break; + case ADC_RES_12BIT: + rshift = SOCADC_12_BIT_RSHIFT; + break; + default: + DEBUG("adc_sample: invalid resultion!\n"); + return -1; + } + /** + * @attention CC2538 ADC supports differential comparision of two analog + * GPIO inputs, hence negative values are possible. RIOT currently allows + * positive ADC output only. Thus, reduce shift by one to compensate and + * get full value range according to ADC resolution. E.g. 10 Bit resultion + * with diff ADC would have [-512,511] range but RIOT expects [0,1023]. + */ + rshift--; + + cc2538_soc_adc_t *adca = SOC_ADC; + /* configure adc line with parameters and trigger a single conversion*/ + uint32_t reg = (adca->ADCCON3) & ~(SOC_ADC_ADCCON3_EREF | + SOC_ADC_ADCCON3_EDIV | + SOC_ADC_ADCCON3_ECH); + adca->ADCCON3 = reg | res | SOC_ADC_ADCCON_REF | + gpio_pp_num(adc_config[line]); + + DEBUG("ADCCON1: %"PRIu32" ADCCON2: %"PRIu32" ADCCON3: %"PRIu32"\n", + adca->cc2538_adc_adccon1.ADCCON1, adca->ADCCON2, adca->ADCCON3); + + /* Poll/wait until end of conversion */ + while ((adca->cc2538_adc_adccon1.ADCCON1 & + SOC_ADC_ADCCON1_EOC_MASK) == 0) {} + + /* Read result after conversion completed, + * reading SOC_ADC_ADCH last will clear SOC_ADC_ADCCON1.EOC */ + int16_t sample = adca->ADCL & SOC_ADC_ADCL_MASK; + sample |= (adca->ADCH & SOC_ADC_ADCH_MASK) << 8; + /* sample right shifted depending on resolution */ + sample = sample >> rshift; + DEBUG("adc_sample: raw value %"PRIi16"\n", sample); + /* FIXME: currently RIOT ADC allows values >0 only */ + if (sample < 0) { + sample = 0; + } + + return (int)sample; +} -- GitLab