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