Add ADC support
authorNathael Pajani <nathael.pajani@ed3l.fr>
Wed, 6 Nov 2013 01:05:55 +0000 (02:05 +0100)
committerNathael Pajani <nathael.pajani@ed3l.fr>
Tue, 8 Nov 2022 16:03:04 +0000 (17:03 +0100)
README
core/system.c
drivers/adc.c [new file with mode: 0644]
drivers/gpio.c
include/core/lpc_regs_12xx.h
include/drivers/adc.h [new file with mode: 0644]

diff --git a/README b/README
index c0000cc..bbfed75 100644 (file)
--- a/README
+++ b/README
@@ -75,6 +75,7 @@ SUPPORTED FEATURES and INTERFACES
    - I²C
    - I²C EEPROM
    - TMP101 temperature sensor
+   - ADC
    - GPIO
 
 
@@ -82,7 +83,6 @@ SUPPORTED FEATURES and INTERFACES
 TODO :
 
 - Deep sleep mode support.
-- ADC support
 - Test all the GPIO in different modes
 - Add support for SPI
 - Add support for SDCard over SPI
index 5d0db4f..4f2a056 100644 (file)
@@ -243,12 +243,14 @@ void Dummy_Clk_Updater(void) {
 void uart_clk_update(void) __attribute__ ((weak, alias ("Dummy_Clk_Updater")));
 void i2c_clock_update(void) __attribute__ ((weak, alias ("Dummy_Clk_Updater")));
 void spi_clk_update(void) __attribute__ ((weak, alias ("Dummy_Clk_Updater")));
+void adc_clk_update(void) __attribute__ ((weak, alias ("Dummy_Clk_Updater")));
 
 static void propagate_main_clock(void)
 {
        uart_clk_update();
        i2c_clock_update();
        spi_clk_update();
+       adc_clk_update();
 }
 
 /* IO config clock */
@@ -286,6 +288,7 @@ void set_i2c_pins(void) __attribute__ ((weak, alias ("Dummy_Pin_Config")));
 void set_spi_pins(void) __attribute__ ((weak, alias ("Dummy_Pin_Config")));
 void set_uarts_pins(void) __attribute__ ((weak, alias ("Dummy_Pin_Config")));
 void set_gpio_pins(void) __attribute__ ((weak, alias ("Dummy_Pin_Config")));
+void set_adc_pins(void) __attribute__ ((weak, alias ("Dummy_Pin_Config")));
 
 void system_set_default_pins(void)
 {
@@ -293,4 +296,5 @@ void system_set_default_pins(void)
        set_i2c_pins();
        set_spi_pins();
        set_gpio_pins();
+       set_adc_pins();
 }
diff --git a/drivers/adc.c b/drivers/adc.c
new file mode 100644 (file)
index 0000000..de786ce
--- /dev/null
@@ -0,0 +1,186 @@
+/****************************************************************************
+ *  drivers/adc.c
+ *
+ * Copyright 2012 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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/>.
+ *
+ *************************************************************************** */
+
+
+
+/***************************************************************************** */
+/*                Analog to Digital Converter (ADC)                            */
+/***************************************************************************** */
+
+#include <stdint.h>
+#include "core/lpc_regs_12xx.h"
+#include "core/lpc_core_cm0.h"
+#include "core/system.h"
+
+/* Should be as near to 9MHz as possible */
+#define adc_clk_Val  9000000
+
+
+
+/***************************************************************************** */
+/* Generic ADC handler */
+void ADC_Handler(void)
+{
+/*
+       volatile struct lpc_adc* adc = LPC_ADC;
+       uint32_t status = adc->status;
+*/
+       /* .... What to do ... is specific to your application */
+}
+
+/* Start a conversion on the given channel (0 to 7) */
+void adc_start_convertion_once(unsigned int channel, int use_int)
+{
+       struct lpc_adc* adc = LPC_ADC;
+
+       if (channel > 7)
+               return;
+
+       /* Set conversion channel bit */
+       adc->ctrl = ((adc->ctrl & ~LPC_ADC_CHANNEL_MASK) | (0x01 << channel));
+       if (use_int) {
+               /* Set interrupt Bit */
+               adc->int_en = (0x01 << channel);
+       } else {
+               adc->int_en = 0;
+       }
+       /* Clear burst conversion if running ? */
+       adc->ctrl &= ~LPC_ADC_BURST;
+       /* Start conversion */
+       adc->ctrl = ((adc->ctrl & ~LPC_ADC_START_CONV_MASK) | LPC_ADC_START_CONV_NOW);
+}
+
+
+/* Read the conversion from the given channel (0 to 7) 
+ * This function reads the conversion value directly in the data register and
+ * always returns a value.
+ * Return 1 if the value is a new one, else return 0.
+ * Return -1 if channel does not exist
+ */
+int adc_get_value(uint16_t * val, int channel)
+{
+       struct lpc_adc* adc = LPC_ADC;
+       uint32_t save_reg = 0;
+
+       if (channel > 7)
+               return -1;
+
+       /* Save the whole register as some bits are cleared when register is read */
+       save_reg = adc->data[channel];
+       *val = ((save_reg >> LPC_ADC_RESULT_SHIFT) & LPC_ADC_RESULT_MASK);
+       /* Has this conversion value been already read ? */
+       if (! (save_reg & LPC_ADC_CONV_DONE)) {
+               return 0;
+       }
+       return 1;
+}
+
+/* Start burst conversions.
+ * channels is a bit mask of requested channels.
+ * Use LPC_ADC_CHANNEL_0 .. 7
+ */
+void adc_start_burst_conversion(uint8_t channels)
+{
+       struct lpc_adc* adc = LPC_ADC;
+
+       /* Set conversion channel bits */
+       adc->ctrl = ((adc->ctrl & ~LPC_ADC_CHANNEL_MASK) | channels);
+       adc->ctrl |= LPC_ADC_BURST;
+}
+
+/* Unsupported Yet */
+/* This should be used to configure conversion start on falling or rising edges of
+ * some signals, or on timer for burst conversions.
+ */
+void adc_prepare_conversion_on_event(void)
+{
+       /* Unsupported Yet */
+}
+
+/* On LPC1224 there is no possibility to change the ADC resolution */
+void adc_set_resolution(int bits)
+{
+}
+
+
+
+/***************************************************************************** */
+/*   ADC Setup : private part : Clocks, Pins, Power and Mode   */
+void set_adc_pins(void)
+{
+       struct lpc_io_control* ioctrl = LPC_IO_CONTROL;
+
+       /* Make sure IO_Config is clocked */
+       io_config_clk_on();
+       /* Configure ADC pins */
+       ioctrl->pio0_30 = LPC_IO_FUNC_ALT(3) | LPC_IO_ANALOG;
+       ioctrl->pio0_31 = LPC_IO_FUNC_ALT(3) | LPC_IO_ANALOG;
+       ioctrl->pio1_0 = LPC_IO_FUNC_ALT(2) | LPC_IO_ANALOG;
+       ioctrl->pio1_1 = LPC_IO_FUNC_ALT(2) | LPC_IO_ANALOG;
+       ioctrl->pio1_2 = LPC_IO_FUNC_ALT(2) | LPC_IO_ANALOG;
+       ioctrl->pio1_3 = LPC_IO_FUNC_ALT(1) | LPC_IO_ANALOG;
+       /* Config done, power off IO_CONFIG block */
+       io_config_clk_off();
+}
+
+void adc_clk_update(void)
+{
+       struct lpc_adc* adc = LPC_ADC;
+       uint32_t main_clock = get_main_clock();
+       uint32_t clkdiv = 0;
+
+       /* Configure ADC */
+       clkdiv = (main_clock/adc_clk_Val);
+       adc->ctrl |= ((clkdiv & 0x0F) << 8);
+}
+
+
+void adc_on(void)
+{
+       struct lpc_sys_control* sys_ctrl = LPC_SYS_CONTROL;
+       struct lpc_adc* adc = LPC_ADC;
+
+       /* Disable ADC Interrupt */
+       NVIC_DisableIRQ(ADC_IRQ);
+       /* Power-up ADC */
+       sys_ctrl->powerdown_run_cfg &= ~LPC_POWER_DOWN_ADC;
+       /* Provide clock to ADC */
+       subsystem_power(LPC_SYS_ABH_CLK_CTRL_ADC, 1);
+       adc_clk_update();
+
+       /* Prevent unconfigured conversion start */
+       adc->ctrl &= ~LPC_ADC_START_CONV_MASK;
+
+       /* Enable ADC Interrupt */
+       NVIC_EnableIRQ(ADC_IRQ);
+}
+
+void adc_off(void)
+{
+       struct lpc_sys_control* sys_ctrl = LPC_SYS_CONTROL;
+
+       /* Disable ADC Interrupt */
+       NVIC_DisableIRQ(ADC_IRQ);
+       /* Power Down ADC */
+       sys_ctrl->powerdown_run_cfg |= LPC_POWER_DOWN_ADC;
+       /* Remove clock from ADC block */
+       subsystem_power(LPC_SYS_ABH_CLK_CTRL_ADC, 0);
+}
+
index aff5e3c..6b589ac 100644 (file)
@@ -83,14 +83,14 @@ void set_gpio_pins(void)
        ioctrl->pio0_27 = LPC_IO_FUNC_ALT(0) | LPC_IO_MODE_PULL_UP;
        ioctrl->pio0_28 = LPC_IO_FUNC_ALT(0) | LPC_IO_MODE_PULL_UP;
        ioctrl->pio0_29 = LPC_IO_FUNC_ALT(0) | LPC_IO_MODE_PULL_UP;
-       /* Configure ADC pins */
-       ioctrl->pio0_30 = LPC_IO_FUNC_ALT(1) | LPC_IO_ANALOG;
-       ioctrl->pio0_31 = LPC_IO_FUNC_ALT(1) | LPC_IO_ANALOG;
-       ioctrl->pio1_0 = LPC_IO_FUNC_ALT(1) | LPC_IO_ANALOG;
-       ioctrl->pio1_1 = LPC_IO_FUNC_ALT(1) | LPC_IO_ANALOG;
-       ioctrl->pio1_2 = LPC_IO_FUNC_ALT(1) | LPC_IO_ANALOG;
-       ioctrl->pio1_3 = LPC_IO_FUNC_ALT(1) | LPC_IO_ANALOG;
-
+#if 0 /* ADC pins configured in ADC driver. Kept for info, for those not using them as ADC */
+-   ioctrl->pio0_30 = LPC_IO_FUNC_ALT(0) | LPC_IO_MODE_PULL_UP;
+-   ioctrl->pio0_31 = LPC_IO_FUNC_ALT(0) | LPC_IO_MODE_PULL_UP;
+-   ioctrl->pio1_0 = LPC_IO_FUNC_ALT(0) | LPC_IO_MODE_PULL_UP;
+-   ioctrl->pio1_1 = LPC_IO_FUNC_ALT(0) | LPC_IO_MODE_PULL_UP;
+-   ioctrl->pio1_2 = LPC_IO_FUNC_ALT(0) | LPC_IO_MODE_PULL_UP;
+-   ioctrl->pio1_3 = LPC_IO_FUNC_ALT(0) | LPC_IO_MODE_PULL_UP;
+#endif
        /* Config done, power off IO_CONFIG block */
        io_config_clk_off();
 }
index 4629a45..d9dce50 100644 (file)
@@ -640,18 +640,32 @@ struct lpc_adc
 };
 #define LPC_ADC         ((struct lpc_adc *) LPC_ADC_BASE)
 
+/* ADC Control register bits */
+/* LPC_ADC_CHANNEL_* are also used for interrupt register */
+#define LPC_ADC_CHANNEL_MASK (0xFF << 0)
+#define LPC_ADC_CHANNEL_0 (0x01 << 0)
+#define LPC_ADC_CHANNEL_1 (0x01 << 1)
+#define LPC_ADC_CHANNEL_2 (0x01 << 2)
+#define LPC_ADC_CHANNEL_3 (0x01 << 3)
+#define LPC_ADC_CHANNEL_4 (0x01 << 4)
+#define LPC_ADC_CHANNEL_5 (0x01 << 5)
+#define LPC_ADC_CHANNEL_6 (0x01 << 6)
+#define LPC_ADC_CHANNEL_7 (0x01 << 7)
+#define LPC_ADC_BURST     (0x01 << 16)
+/* These are unused for LPC1224 */
 #define LPC_ADC_10BITS  (0x00 << 17)
-#define LPC_ADC_9BITS  (0x01 << 17)
-#define LPC_ADC_8BITS  (0x02 << 17)
-#define LPC_ADC_7BITS  (0x03 << 17)
-#define LPC_ADC_6BITS  (0x04 << 17)
+#define LPC_ADC_9BITS   (0x01 << 17)
+#define LPC_ADC_8BITS   (0x02 << 17)
+#define LPC_ADC_7BITS   (0x03 << 17)
+#define LPC_ADC_6BITS   (0x04 << 17)
 #define LPC_ADC_START_CONV_NOW  (0x01 << 24)
 #define LPC_ADC_START_CONV_MASK (0x07 << 24)
-#define LPC_ADC_CONV_AD0   (0x01 << 0)
-
-#define LPC_ADC_INT_EN_AD0 (0x01 << 0)
-
 
+/* ADC Data register bits */
+#define LPC_ADC_RESULT_SHIFT  6
+#define LPC_ADC_RESULT_MASK   0x3FF
+#define LPC_ADC_OVERRUN    (0x01 << 30)
+#define LPC_ADC_CONV_DONE  (0x01 << 31)
 
 
 #endif  /* LPC_REGS_H */
diff --git a/include/drivers/adc.h b/include/drivers/adc.h
new file mode 100644 (file)
index 0000000..b5e29f9
--- /dev/null
@@ -0,0 +1,65 @@
+/****************************************************************************
+ *  drivers/adc.h
+ *
+ * Copyright 2012 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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/>.
+ *
+ *************************************************************************** */
+
+
+
+/***************************************************************************** */
+/*                Analog to Digital Converter (ADC)                            */
+/***************************************************************************** */
+
+#include <stdint.h>
+#include "core/lpc_regs_12xx.h"
+#include "core/lpc_core_cm0.h"
+#include "core/system.h"
+
+/* Start a conversion on the given channel (0 to 7) */
+void adc_start_convertion_once(unsigned int channel, int use_int);
+
+/* Read the conversion from the given channel (0 to 7)
+ * This function reads the conversion value directly in the data register and
+ * always returns a value.
+ * Return 1 if the value is a new one, else return 0.
+ * Return -1 if channel does not exist
+ */
+int adc_get_value(uint16_t * val, int channel);
+
+/* Start burst conversions.
+ * channels is a bit mask of requested channels.
+ * Use LPC_ADC_CHANNEL_0 .. 7
+ */
+void adc_start_burst_conversion(uint8_t channels);
+
+/* Unsupported Yet */
+/* This should be used to configure conversion start on falling or rising edges of
+ * some signals, or on timer for burst conversions.
+ */
+void adc_prepare_conversion_on_event(void);
+
+/* On LPC1224 there is no possibility to change the ADC resolution */
+void adc_set_resolution(int bits);
+
+
+/***************************************************************************** */
+/*   ADC Setup : private part : Clocks, Pins, Power and Mode   */
+void set_adc_pins(void);
+void adc_clk_update(void);
+void adc_on(void);
+void adc_off(void);
+