summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Nielsen Feltzing <linus@haxx.se>2004-09-21 08:59:36 +0000
committerLinus Nielsen Feltzing <linus@haxx.se>2004-09-21 08:59:36 +0000
commitddb6333797d845b1ec1d71983a636388f0a2bc74 (patch)
treedc1ce3840ed68158d9d37a836573735d525ec1a5
parent56cbda370d2a0af777aa55e5cc2dea018625cb3e (diff)
downloadrockbox-ddb6333797d845b1ec1d71983a636388f0a2bc74.zip
rockbox-ddb6333797d845b1ec1d71983a636388f0a2bc74.tar.gz
rockbox-ddb6333797d845b1ec1d71983a636388f0a2bc74.tar.bz2
rockbox-ddb6333797d845b1ec1d71983a636388f0a2bc74.tar.xz
New A/D conversion strategy, now updates all channels every tick
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@5099 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--firmware/drivers/adc.c98
1 files changed, 58 insertions, 40 deletions
diff --git a/firmware/drivers/adc.c b/firmware/drivers/adc.c
index c81d571..a245420 100644
--- a/firmware/drivers/adc.c
+++ b/firmware/drivers/adc.c
@@ -22,6 +22,27 @@
#include "thread.h"
#include "adc.h"
+/**************************************************************************
+ ** The A/D conversion is done every tick, in three steps:
+ **
+ ** 1) On the tick interrupt, the conversion of channels 0-3 is started, and
+ ** the A/D interrupt is enabled.
+ **
+ ** 2) After the conversion is done (approx. 256*4 cycles later), an interrupt
+ ** is generated at level 1, which is the same level as the tick interrupt
+ ** itself. This interrupt will be pending until the tick interrupt is
+ ** finished.
+ ** When the A/D interrupt is finally served, it will read the results
+ ** from the first conversion and start the conversion of channels 4-7.
+ **
+ ** 3) When the conversion of channels 4-7 is finished, the interrupt is
+ ** triggered again, and the results are read. This time, no new
+ ** conversion is started, it will be done in the next tick interrupt.
+ **
+ ** Thus, each channel will be updated HZ times per second.
+ **
+ *************************************************************************/
+
static int current_channel;
static unsigned short adcdata[NUM_ADC_CHANNELS];
static const unsigned int adcreg[NUM_ADC_CHANNELS] =
@@ -32,60 +53,57 @@ static const unsigned int adcreg[NUM_ADC_CHANNELS] =
static void adc_tick(void)
{
- /* Read the data that has bee converted since the last tick */
- adcdata[current_channel] =
- *(unsigned short *)adcreg[current_channel] >> 6;
-
- /* Start a conversion on the next channel */
- current_channel++;
- if(current_channel == NUM_ADC_CHANNELS)
- current_channel = 0;
- ADCSR = ADCSR_ADST | current_channel;
-}
+ /* Start a conversion of channel group 0. This will trigger an interrupt,
+ and the interrupt handler will take care of group 1. */
-unsigned short adc_read(int channel)
-{
- return adcdata[channel];
+ current_channel = 0;
+ ADCSR = ADCSR_ADST | ADCSR_ADIE | ADCSR_SCAN | 3;
}
-/* Batch convert 4 analog channels. If lower is true, convert AN0-AN3,
- * otherwise AN4-AN7.
- */
-static void adc_batch_convert(bool lower)
+#pragma interrupt
+void ADITI(void)
{
- int reg = lower ? 0 : 4;
- volatile unsigned short* ANx = ((unsigned short*) ADDRAH_ADDR);
- int i;
-
- ADCSR = ADCSR_ADST | ADCSR_SCAN | reg | 3;
-
- /* Busy wait until conversion is complete. A bit ugly perhaps, but
- * we should only need to wait about 4 * 14 µs */
- while(!(ADCSR & ADCSR_ADF))
+ if(ADCSR & ADCSR_ADF)
{
- }
+ ADCSR = 0;
- /* Stop scanning */
- ADCSR = 0;
-
- for (i = 0; i < 4; i++)
- {
- /* Read converted values */
- adcdata[reg++] = ANx[i] >> 6;
+ if(current_channel == 0)
+ {
+ adcdata[0] = ADDRA >> 6;
+ adcdata[1] = ADDRB >> 6;
+ adcdata[2] = ADDRC >> 6;
+ adcdata[3] = ADDRD >> 6;
+ current_channel = 4;
+
+ /* Convert the next group */
+ ADCSR = ADCSR_ADST | ADCSR_ADIE | ADCSR_SCAN | 7;
+ }
+ else
+ {
+ adcdata[4] = ADDRA >> 6;
+ adcdata[5] = ADDRB >> 6;
+ adcdata[6] = ADDRC >> 6;
+ adcdata[7] = ADDRD >> 6;
+ }
}
}
+unsigned short adc_read(int channel)
+{
+ return adcdata[channel];
+}
+
void adc_init(void)
{
- ADCR = 0x7f; /* No external trigger; other bits should be 1 according to the manual... */
+ ADCR = 0x7f; /* No external trigger; other bits should be 1 according
+ to the manual... */
+ ADCSR = 0;
+
current_channel = 0;
- /* Do a first scan to initialize all values */
- /* AN4 to AN7 */
- adc_batch_convert(false);
- /* AN0 to AN3 */
- adc_batch_convert(true);
+ /* Enable the A/D IRQ on level 1 */
+ IPRE = (IPRE & 0xf0ff) | 0x0100;
tick_add_task(adc_tick);
}