From 166977c65bddb50d600a3c1e1f278c425b673697 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 18 May 2013 03:18:19 -0700 Subject: altos/lpc: Add ADC driver Uses burst mode to get the whole set of values in one interrupt Signed-off-by: Keith Packard --- src/lpc/ao_adc_lpc.c | 229 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+) create mode 100644 src/lpc/ao_adc_lpc.c (limited to 'src/lpc/ao_adc_lpc.c') diff --git a/src/lpc/ao_adc_lpc.c b/src/lpc/ao_adc_lpc.c new file mode 100644 index 00000000..70e8b2d6 --- /dev/null +++ b/src/lpc/ao_adc_lpc.c @@ -0,0 +1,229 @@ +/* + * Copyright © 2012 Keith Packard + * + * 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; version 2 of the License. + * + * 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, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include +#include + +#ifndef AO_ADC_0 +#define AO_ADC_0 0 +#endif + +#ifndef AO_ADC_1 +#define AO_ADC_1 0 +#endif + +#ifndef AO_ADC_2 +#define AO_ADC_2 0 +#endif + +#ifndef AO_ADC_3 +#define AO_ADC_3 0 +#endif + +#ifndef AO_ADC_4 +#define AO_ADC_4 0 +#endif + +#ifndef AO_ADC_5 +#define AO_ADC_5 0 +#endif + +#ifndef AO_ADC_6 +#define AO_ADC_6 0 +#endif + +#ifndef AO_ADC_7 +#define AO_ADC_7 0 +#endif + +#if AO_ADC_7 +# define AO_ADC_LAST 7 +#else +# if AO_ADC_6 +# define AO_ADC_LAST 6 +# else +# if AO_ADC_5 +# define AO_ADC_LAST 5 +# else +# if AO_ADC_4 +# define AO_ADC_LAST 4 +# else +# if AO_ADC_3 +# define AO_ADC_LAST 3 +# else +# if AO_ADC_2 +# define AO_ADC_LAST 2 +# else +# if AO_ADC_1 +# define AO_ADC_LAST 1 +# else +# if AO_ADC_0 +# define AO_ADC_LAST 0 +# endif +# endif +# endif +# endif +# endif +# endif +# endif +#endif + +static uint8_t ao_adc_ready; + +void lpc_adc_isr(void) +{ + uint16_t *out = (uint16_t *) &ao_data_ring[ao_data_head].adc; + vuint32_t *in = &lpc_adc.dr[0]; + + /* Store converted values in packet */ + +#if AO_ADC_0 + *out++ = ((uint16_t) *in++) >> 1; +#endif +#if AO_ADC_1 + *out++ = ((uint16_t) *in++) >> 1; +#endif +#if AO_ADC_2 + *out++ = ((uint16_t) *in++) >> 1; +#endif +#if AO_ADC_3 + *out++ = ((uint16_t) *in++) >> 1; +#endif +#if AO_ADC_4 + *out++ = ((uint16_t) *in++) >> 1; +#endif +#if AO_ADC_5 + *out++ = ((uint16_t) *in++) >> 1; +#endif +#if AO_ADC_6 + *out++ = ((uint16_t) *in++) >> 1; +#endif +#if AO_ADC_7 + *out++ = ((uint16_t) *in++) >> 1; +#endif + + AO_DATA_PRESENT(AO_DATA_ADC); + if (ao_data_present == AO_DATA_ALL) { +#if HAS_MS5607 + ao_data_ring[ao_data_head].ms5607_raw = ao_ms5607_current; +#endif +#if HAS_MMA655X + ao_data_ring[ao_data_head].mma655x = ao_mma655x_current; +#endif +#if HAS_HMC5883 + ao_data_ring[ao_data_head].hmc5883 = ao_hmc5883_current; +#endif +#if HAS_MPU6000 + ao_data_ring[ao_data_head].mpu6000 = ao_mpu6000_current; +#endif + ao_data_ring[ao_data_head].tick = ao_tick_count; + ao_data_head = ao_data_ring_next(ao_data_head); + ao_wakeup((void *) &ao_data_head); + } + ao_adc_ready = 1; +} + +#define AO_ADC_MASK ((AO_ADC_0 << 0) | \ + (AO_ADC_1 << 1) | \ + (AO_ADC_2 << 2) | \ + (AO_ADC_3 << 3) | \ + (AO_ADC_4 << 4) | \ + (AO_ADC_5 << 5) | \ + (AO_ADC_6 << 6) | \ + (AO_ADC_7 << 7)) + +#define AO_ADC_CLKDIV (AO_LPC_CLKOUT / 4500000) + +/* + * Start the ADC sequence using the DMA engine + */ +void +ao_adc_poll(void) +{ + if (!ao_adc_ready) + return; + ao_adc_ready = 0; + + lpc_adc.cr = ((AO_ADC_MASK << LPC_ADC_CR_SEL) | + (AO_ADC_CLKDIV << LPC_ADC_CR_CLKDIV) | + (1 << LPC_ADC_CR_BURST) | + (LPC_ADC_CR_CLKS_11 << LPC_ADC_CR_CLKS)); +} + +static void +ao_adc_dump(void) __reentrant +{ + struct ao_data packet; + int16_t *d; + uint8_t i; + + ao_data_get(&packet); +#ifdef AO_ADC_DUMP + AO_ADC_DUMP(&packet); +#else + printf("tick: %5u", packet.tick); + d = (int16_t *) (&packet.adc); + for (i = 0; i < AO_NUM_ADC; i++) + printf (" %2d: %5d", i, d[i]); + printf("\n"); +#endif +} + +__code struct ao_cmds ao_adc_cmds[] = { + { ao_adc_dump, "a\0Display current ADC values" }, + { 0, NULL }, +}; + +void +ao_adc_init(void) +{ + lpc_scb.sysahbclkctrl |= (1 << LPC_SCB_SYSAHBCLKCTRL_ADC); + lpc_scb.pdruncfg &= ~(1 << LPC_SCB_PDRUNCFG_ADC_PD); + + /* Enable interrupt when last channel is complete */ + lpc_adc.inten = (1 << AO_ADC_LAST); + + lpc_nvic_set_enable(LPC_ISR_ADC_POS); + lpc_nvic_set_priority(LPC_ISR_ADC_POS, AO_LPC_NVIC_CLOCK_PRIORITY); +#if AO_ADC_0 + ao_enable_analog(0, 11); +#endif +#if AO_ADC_1 + ao_enable_analog(0, 12); +#endif +#if AO_ADC_2 + ao_enable_analog(0, 13); +#endif +#if AO_ADC_3 + ao_enable_analog(0, 14); +#endif +#if AO_ADC_4 + ao_enable_analog(0, 14); +#endif +#if AO_ADC_5 + ao_enable_analog(0, 14); +#endif +#if AO_ADC_6 + ao_enable_analog(0, 14); +#endif +#if AO_ADC_7 + ao_enable_analog(0, 14); +#endif + ao_cmd_register(&ao_adc_cmds[0]); + + ao_adc_ready = 1; +} -- cgit v1.2.3 From d51c9fda3478f205e4bcdf1b7bf21eb1e0a516bc Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 19 May 2013 20:07:52 -0700 Subject: altos/lpc: Pull ADC data from the correct registers Was just stepping through register space arbitrarily, which would have worked for EasyMini, but might have failed later if the ADC pin usage wasn't consecutive. Signed-off-by: Keith Packard --- src/lpc/ao_adc_lpc.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'src/lpc/ao_adc_lpc.c') diff --git a/src/lpc/ao_adc_lpc.c b/src/lpc/ao_adc_lpc.c index 70e8b2d6..9ecc7c13 100644 --- a/src/lpc/ao_adc_lpc.c +++ b/src/lpc/ao_adc_lpc.c @@ -86,34 +86,38 @@ static uint8_t ao_adc_ready; void lpc_adc_isr(void) { + uint32_t stat = lpc_adc.stat; uint16_t *out = (uint16_t *) &ao_data_ring[ao_data_head].adc; vuint32_t *in = &lpc_adc.dr[0]; + lpc_adc.cr = 0; + lpc_adc.stat = 0; + /* Store converted values in packet */ #if AO_ADC_0 - *out++ = ((uint16_t) *in++) >> 1; + *out++ = lpc_adc.dr[0] >> 1; #endif #if AO_ADC_1 - *out++ = ((uint16_t) *in++) >> 1; + *out++ = lpc_adc.dr[1] >> 1; #endif #if AO_ADC_2 - *out++ = ((uint16_t) *in++) >> 1; + *out++ = lpc_adc.dr[2] >> 1; #endif #if AO_ADC_3 - *out++ = ((uint16_t) *in++) >> 1; + *out++ = lpc_adc.dr[3] >> 1; #endif #if AO_ADC_4 - *out++ = ((uint16_t) *in++) >> 1; + *out++ = lpc_adc.dr[4] >> 1; #endif #if AO_ADC_5 - *out++ = ((uint16_t) *in++) >> 1; + *out++ = lpc_adc.dr[5] >> 1; #endif #if AO_ADC_6 - *out++ = ((uint16_t) *in++) >> 1; + *out++ = lpc_adc.dr[6] >> 1; #endif #if AO_ADC_7 - *out++ = ((uint16_t) *in++) >> 1; + *out++ = lpc_adc.dr[7] >> 1; #endif AO_DATA_PRESENT(AO_DATA_ADC); -- cgit v1.2.3 From 7361371190bf3805b6d0414e61f697aca7c7cff1 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 14 Jun 2013 04:38:11 -0700 Subject: altos/lpc: Make ADC inputs work They're still very unstable (bouncing around a lot), but at least they seem to report useful stuff now. Signed-off-by: Keith Packard --- src/lpc/ao_adc_lpc.c | 52 +++++++++++++++++++++++++++++-------------------- src/lpc/ao_arch_funcs.h | 18 +++++++++++++---- 2 files changed, 45 insertions(+), 25 deletions(-) (limited to 'src/lpc/ao_adc_lpc.c') diff --git a/src/lpc/ao_adc_lpc.c b/src/lpc/ao_adc_lpc.c index 9ecc7c13..25a121b5 100644 --- a/src/lpc/ao_adc_lpc.c +++ b/src/lpc/ao_adc_lpc.c @@ -82,19 +82,33 @@ # endif #endif +#define AO_ADC_MASK ((AO_ADC_0 << 0) | \ + (AO_ADC_1 << 1) | \ + (AO_ADC_2 << 2) | \ + (AO_ADC_3 << 3) | \ + (AO_ADC_4 << 4) | \ + (AO_ADC_5 << 5) | \ + (AO_ADC_6 << 6) | \ + (AO_ADC_7 << 7)) + +#define AO_ADC_CLKDIV (AO_LPC_SYSCLK / 4500000 - 1) + static uint8_t ao_adc_ready; void lpc_adc_isr(void) { uint32_t stat = lpc_adc.stat; - uint16_t *out = (uint16_t *) &ao_data_ring[ao_data_head].adc; - vuint32_t *in = &lpc_adc.dr[0]; + uint16_t *out; - lpc_adc.cr = 0; + lpc_adc.cr = ((AO_ADC_MASK << LPC_ADC_CR_SEL) | + (AO_ADC_CLKDIV << LPC_ADC_CR_CLKDIV) | + (0 << LPC_ADC_CR_BURST) | + (LPC_ADC_CR_CLKS_11 << LPC_ADC_CR_CLKS)); lpc_adc.stat = 0; /* Store converted values in packet */ + out = (uint16_t *) &ao_data_ring[ao_data_head].adc; #if AO_ADC_0 *out++ = lpc_adc.dr[0] >> 1; #endif @@ -141,16 +155,6 @@ void lpc_adc_isr(void) ao_adc_ready = 1; } -#define AO_ADC_MASK ((AO_ADC_0 << 0) | \ - (AO_ADC_1 << 1) | \ - (AO_ADC_2 << 2) | \ - (AO_ADC_3 << 3) | \ - (AO_ADC_4 << 4) | \ - (AO_ADC_5 << 5) | \ - (AO_ADC_6 << 6) | \ - (AO_ADC_7 << 7)) - -#define AO_ADC_CLKDIV (AO_LPC_CLKOUT / 4500000) /* * Start the ADC sequence using the DMA engine @@ -204,29 +208,35 @@ ao_adc_init(void) lpc_nvic_set_enable(LPC_ISR_ADC_POS); lpc_nvic_set_priority(LPC_ISR_ADC_POS, AO_LPC_NVIC_CLOCK_PRIORITY); #if AO_ADC_0 - ao_enable_analog(0, 11); + ao_enable_analog(0, 11, 0); #endif #if AO_ADC_1 - ao_enable_analog(0, 12); + ao_enable_analog(0, 12, 1); #endif #if AO_ADC_2 - ao_enable_analog(0, 13); + ao_enable_analog(0, 13, 2); #endif #if AO_ADC_3 - ao_enable_analog(0, 14); + ao_enable_analog(0, 14, 3); #endif #if AO_ADC_4 - ao_enable_analog(0, 14); + ao_enable_analog(0, 15, 4); #endif #if AO_ADC_5 - ao_enable_analog(0, 14); + ao_enable_analog(0, 16, 5); #endif #if AO_ADC_6 - ao_enable_analog(0, 14); + ao_enable_analog(0, 22, 6); #endif #if AO_ADC_7 - ao_enable_analog(0, 14); + ao_enable_analog(0, 23, 7); #endif + + lpc_adc.cr = ((AO_ADC_MASK << LPC_ADC_CR_SEL) | + (AO_ADC_CLKDIV << LPC_ADC_CR_CLKDIV) | + (0 << LPC_ADC_CR_BURST) | + (LPC_ADC_CR_CLKS_11 << LPC_ADC_CR_CLKS)); + ao_cmd_register(&ao_adc_cmds[0]); ao_adc_ready = 1; diff --git a/src/lpc/ao_arch_funcs.h b/src/lpc/ao_arch_funcs.h index 204d1227..179b2f4d 100644 --- a/src/lpc/ao_arch_funcs.h +++ b/src/lpc/ao_arch_funcs.h @@ -51,12 +51,22 @@ (1 << LPC_IOCONF_ADMODE)); \ } while (0) -#define ao_enable_analog(port,bit) do { \ - vuint32_t *_ioconf = &lpc_ioconf.pio0_0 + ((port)*24+(bit)); \ +#define lpc_token_paster_2(x,y) x ## y +#define lpc_token_evaluator_2(x,y) lpc_token_paster_2(x,y) +#define lpc_token_paster_3(x,y,z) x ## y ## z +#define lpc_token_evaluator_3(x,y,z) lpc_token_paster_3(x,y,z) +#define lpc_token_paster_4(w,x,y,z) w ## x ## y ## z +#define lpc_token_evaluator_4(w,x,y,z) lpc_token_paster_4(w,x,y,z) +#define analog_reg(port,bit) lpc_token_evaluator_4(pio,port,_,bit) +#define analog_func(id) lpc_token_evaluator_2(LPC_IOCONF_FUNC_AD,id) + +#define ao_enable_analog(port,bit,id) do { \ + uint32_t _mode; \ ao_enable_port(port); \ lpc_gpio.dir[port] &= ~(1 << bit); \ - *_ioconf = *_ioconf & ~((1 << LPC_IOCONF_ADMODE) | \ - (LPC_IOCONF_MODE_MASK << LPC_IOCONF_MODE)); \ + _mode = ((analog_func(id) << LPC_IOCONF_FUNC) | \ + (0 << LPC_IOCONF_ADMODE)); \ + lpc_ioconf.analog_reg(port,bit) = _mode; \ } while (0) #define ARM_PUSH32(stack, val) (*(--(stack)) = (val)) -- cgit v1.2.3 From be9ee9ed2d041c4ab4e77ee2010fe3c7a1ca6597 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 15 Jun 2013 01:20:49 -0700 Subject: altos/lpc: Filter ADC inputs They're amazingly noisy on EasyMini, so just filter them as the only thing we use them for is battery and pyro numbers. Signed-off-by: Keith Packard --- src/lpc/ao_adc_lpc.c | 39 +++++++++++++++++++++++++-------------- src/lpc/ao_arch_funcs.h | 6 ++---- src/lpc/lpc.h | 7 +++++++ 3 files changed, 34 insertions(+), 18 deletions(-) (limited to 'src/lpc/ao_adc_lpc.c') diff --git a/src/lpc/ao_adc_lpc.c b/src/lpc/ao_adc_lpc.c index 25a121b5..40173aa4 100644 --- a/src/lpc/ao_adc_lpc.c +++ b/src/lpc/ao_adc_lpc.c @@ -91,47 +91,58 @@ (AO_ADC_6 << 6) | \ (AO_ADC_7 << 7)) -#define AO_ADC_CLKDIV (AO_LPC_SYSCLK / 4500000 - 1) +#define AO_ADC_CLKDIV (AO_LPC_SYSCLK / 4500000) -static uint8_t ao_adc_ready; +static uint8_t ao_adc_ready; + +static uint16_t ao_adc_prev[AO_NUM_ADC]; + +#define sample(id) do { \ + uint16_t p = *prev; \ + uint16_t v = lpc_adc.dr[id]; \ + p -= p >> 4; \ + p += v >> 4; \ + *prev++ = p; \ + *out++ = p >> 1; \ + } while (0) void lpc_adc_isr(void) { uint32_t stat = lpc_adc.stat; uint16_t *out; + uint16_t *prev; - lpc_adc.cr = ((AO_ADC_MASK << LPC_ADC_CR_SEL) | - (AO_ADC_CLKDIV << LPC_ADC_CR_CLKDIV) | - (0 << LPC_ADC_CR_BURST) | - (LPC_ADC_CR_CLKS_11 << LPC_ADC_CR_CLKS)); + /* Turn off burst mode */ + lpc_adc.cr = 0; lpc_adc.stat = 0; /* Store converted values in packet */ out = (uint16_t *) &ao_data_ring[ao_data_head].adc; + prev = ao_adc_prev; #if AO_ADC_0 - *out++ = lpc_adc.dr[0] >> 1; + sample(0); #endif #if AO_ADC_1 - *out++ = lpc_adc.dr[1] >> 1; + sample(1); #endif #if AO_ADC_2 - *out++ = lpc_adc.dr[2] >> 1; + sample(2); #endif #if AO_ADC_3 - *out++ = lpc_adc.dr[3] >> 1; + sample(3); #endif #if AO_ADC_4 - *out++ = lpc_adc.dr[4] >> 1; + sample(4); #endif #if AO_ADC_5 - *out++ = lpc_adc.dr[5] >> 1; + sample(5); #endif #if AO_ADC_6 - *out++ = lpc_adc.dr[6] >> 1; + sample(6); #endif #if AO_ADC_7 - *out++ = lpc_adc.dr[7] >> 1; + sample(7); #endif AO_DATA_PRESENT(AO_DATA_ADC); diff --git a/src/lpc/ao_arch_funcs.h b/src/lpc/ao_arch_funcs.h index 179b2f4d..1bbb14f5 100644 --- a/src/lpc/ao_arch_funcs.h +++ b/src/lpc/ao_arch_funcs.h @@ -61,12 +61,10 @@ #define analog_func(id) lpc_token_evaluator_2(LPC_IOCONF_FUNC_AD,id) #define ao_enable_analog(port,bit,id) do { \ - uint32_t _mode; \ ao_enable_port(port); \ lpc_gpio.dir[port] &= ~(1 << bit); \ - _mode = ((analog_func(id) << LPC_IOCONF_FUNC) | \ - (0 << LPC_IOCONF_ADMODE)); \ - lpc_ioconf.analog_reg(port,bit) = _mode; \ + lpc_ioconf.analog_reg(port,bit) = ((analog_func(id) << LPC_IOCONF_FUNC) | \ + (0 << LPC_IOCONF_ADMODE)); \ } while (0) #define ARM_PUSH32(stack, val) (*(--(stack)) = (val)) diff --git a/src/lpc/lpc.h b/src/lpc/lpc.h index 49034c1c..d66f0dd0 100644 --- a/src/lpc/lpc.h +++ b/src/lpc/lpc.h @@ -1169,6 +1169,13 @@ extern struct lpc_adc lpc_adc; #define LPC_ADC_CR_CLKS_6 5 #define LPC_ADC_CR_CLKS_5 6 #define LPC_ADC_CR_CLKS_4 7 +#define LPC_ADC_CR_START 24 +#define LPC_ADC_CR_START_NONE 0 +#define LPC_ADC_CR_START_NOW 1 + +#define LPC_ADC_GDR_CHN 24 +#define LPC_ADC_GDR_OVERRUN 30 +#define LPC_ADC_GDR_DONE 31 #define LPC_ADC_INTEN_ADINTEN 0 #define LPC_ADC_INTEN_ADGINTEN 8 -- cgit v1.2.3 From 025beb0fea011d0e3dab59b5d16e7ffae97c613c Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 17 Jun 2013 14:52:32 -0700 Subject: altos/lpc: Get rid of ADC filter Now that the source of the Vcc noise has been identified, remove the unnecessary ADC filtering. Signed-off-by: Keith Packard --- src/lpc/ao_adc_lpc.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'src/lpc/ao_adc_lpc.c') diff --git a/src/lpc/ao_adc_lpc.c b/src/lpc/ao_adc_lpc.c index 40173aa4..874d2d29 100644 --- a/src/lpc/ao_adc_lpc.c +++ b/src/lpc/ao_adc_lpc.c @@ -95,22 +95,12 @@ static uint8_t ao_adc_ready; -static uint16_t ao_adc_prev[AO_NUM_ADC]; - -#define sample(id) do { \ - uint16_t p = *prev; \ - uint16_t v = lpc_adc.dr[id]; \ - p -= p >> 4; \ - p += v >> 4; \ - *prev++ = p; \ - *out++ = p >> 1; \ - } while (0) +#define sample(id) (*out++ = lpc_adc.dr[id] >> 1) void lpc_adc_isr(void) { uint32_t stat = lpc_adc.stat; uint16_t *out; - uint16_t *prev; /* Turn off burst mode */ lpc_adc.cr = 0; @@ -119,7 +109,6 @@ void lpc_adc_isr(void) /* Store converted values in packet */ out = (uint16_t *) &ao_data_ring[ao_data_head].adc; - prev = ao_adc_prev; #if AO_ADC_0 sample(0); #endif -- cgit v1.2.3 From 1aed2eb5c7d477a2f3d4fada22980041aba97cb8 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 23 Aug 2013 11:22:10 -0700 Subject: altos/lpc: Stop using burst mode for LPC ADC Burst mode doesn't stop after one round of conversions, so we end up getting incorrect values in whatever the last conversion register is. Just use single conversions and take an interrupt per channel. Also, slow down the ADC so that our values are more stable -- just need to make sure we get the whole conversion sequence done 100 times a second. Signed-off-by: Keith Packard --- src/lpc/ao_adc_lpc.c | 119 ++++++++++++++++++++------------------------------- 1 file changed, 46 insertions(+), 73 deletions(-) (limited to 'src/lpc/ao_adc_lpc.c') diff --git a/src/lpc/ao_adc_lpc.c b/src/lpc/ao_adc_lpc.c index 874d2d29..7005f86e 100644 --- a/src/lpc/ao_adc_lpc.c +++ b/src/lpc/ao_adc_lpc.c @@ -50,89 +50,65 @@ #define AO_ADC_7 0 #endif -#if AO_ADC_7 -# define AO_ADC_LAST 7 -#else -# if AO_ADC_6 -# define AO_ADC_LAST 6 -# else -# if AO_ADC_5 -# define AO_ADC_LAST 5 -# else -# if AO_ADC_4 -# define AO_ADC_LAST 4 -# else -# if AO_ADC_3 -# define AO_ADC_LAST 3 -# else -# if AO_ADC_2 -# define AO_ADC_LAST 2 -# else -# if AO_ADC_1 -# define AO_ADC_LAST 1 -# else -# if AO_ADC_0 -# define AO_ADC_LAST 0 -# endif -# endif -# endif -# endif -# endif -# endif -# endif -#endif - -#define AO_ADC_MASK ((AO_ADC_0 << 0) | \ - (AO_ADC_1 << 1) | \ - (AO_ADC_2 << 2) | \ - (AO_ADC_3 << 3) | \ - (AO_ADC_4 << 4) | \ - (AO_ADC_5 << 5) | \ - (AO_ADC_6 << 6) | \ - (AO_ADC_7 << 7)) - -#define AO_ADC_CLKDIV (AO_LPC_SYSCLK / 4500000) - -static uint8_t ao_adc_ready; +#define AO_ADC_NUM (AO_ADC_0 + AO_ADC_1 + AO_ADC_2 + AO_ADC_3 + \ + AO_ADC_4 + AO_ADC_5 + AO_ADC_6 + AO_ADC_7) -#define sample(id) (*out++ = lpc_adc.dr[id] >> 1) - -void lpc_adc_isr(void) -{ - uint32_t stat = lpc_adc.stat; - uint16_t *out; - - /* Turn off burst mode */ - lpc_adc.cr = 0; - lpc_adc.stat = 0; +/* ADC clock is divided by this value + 1, which ensures that + * the ADC clock will be strictly less than 4.5MHz as required + */ +#define AO_ADC_CLKDIV (AO_LPC_SYSCLK / 450000) - /* Store converted values in packet */ +static uint8_t ao_adc_ready; +static uint8_t ao_adc_sequence; - out = (uint16_t *) &ao_data_ring[ao_data_head].adc; +static const uint8_t ao_adc_mask_seq[AO_ADC_NUM] = { #if AO_ADC_0 - sample(0); + 1 << 0, #endif #if AO_ADC_1 - sample(1); + 1 << 1, #endif #if AO_ADC_2 - sample(2); + 1 << 2, #endif #if AO_ADC_3 - sample(3); + 1 << 3, #endif #if AO_ADC_4 - sample(4); + 1 << 4, #endif #if AO_ADC_5 - sample(5); + 1 << 6, #endif #if AO_ADC_6 - sample(6); + 1 << 6, #endif #if AO_ADC_7 - sample(7); + 1 << 7, #endif +}; + +#define sample(id) (*out++ = (uint16_t) lpc_adc.dr[id] >> 1) + +static inline void lpc_adc_start(void) { + lpc_adc.cr = ((ao_adc_mask_seq[ao_adc_sequence] << LPC_ADC_CR_SEL) | + (AO_ADC_CLKDIV << LPC_ADC_CR_CLKDIV) | + (0 << LPC_ADC_CR_BURST) | + (LPC_ADC_CR_CLKS_11 << LPC_ADC_CR_CLKS) | + (LPC_ADC_CR_START_NOW << LPC_ADC_CR_START)); +} + +void lpc_adc_isr(void) +{ + uint16_t *out; + + /* Store converted value in packet */ + out = (uint16_t *) &ao_data_ring[ao_data_head].adc; + out[ao_adc_sequence] = (uint16_t) lpc_adc.gdr >> 1; + if (++ao_adc_sequence < AO_ADC_NUM) { + lpc_adc_start(); + return; + } AO_DATA_PRESENT(AO_DATA_ADC); if (ao_data_present == AO_DATA_ALL) { @@ -157,7 +133,7 @@ void lpc_adc_isr(void) /* - * Start the ADC sequence using the DMA engine + * Start the ADC sequence using burst mode */ void ao_adc_poll(void) @@ -165,11 +141,8 @@ ao_adc_poll(void) if (!ao_adc_ready) return; ao_adc_ready = 0; - - lpc_adc.cr = ((AO_ADC_MASK << LPC_ADC_CR_SEL) | - (AO_ADC_CLKDIV << LPC_ADC_CR_CLKDIV) | - (1 << LPC_ADC_CR_BURST) | - (LPC_ADC_CR_CLKS_11 << LPC_ADC_CR_CLKS)); + ao_adc_sequence = 0; + lpc_adc_start(); } static void @@ -202,8 +175,8 @@ ao_adc_init(void) lpc_scb.sysahbclkctrl |= (1 << LPC_SCB_SYSAHBCLKCTRL_ADC); lpc_scb.pdruncfg &= ~(1 << LPC_SCB_PDRUNCFG_ADC_PD); - /* Enable interrupt when last channel is complete */ - lpc_adc.inten = (1 << AO_ADC_LAST); + /* Enable interrupt when channel is complete */ + lpc_adc.inten = (1 << LPC_ADC_INTEN_ADGINTEN); lpc_nvic_set_enable(LPC_ISR_ADC_POS); lpc_nvic_set_priority(LPC_ISR_ADC_POS, AO_LPC_NVIC_CLOCK_PRIORITY); @@ -232,7 +205,7 @@ ao_adc_init(void) ao_enable_analog(0, 23, 7); #endif - lpc_adc.cr = ((AO_ADC_MASK << LPC_ADC_CR_SEL) | + lpc_adc.cr = ((0 << LPC_ADC_CR_SEL) | (AO_ADC_CLKDIV << LPC_ADC_CR_CLKDIV) | (0 << LPC_ADC_CR_BURST) | (LPC_ADC_CR_CLKS_11 << LPC_ADC_CR_CLKS)); -- cgit v1.2.3