Class similar to AnalogIn that uses burst mode to run continious background conversions so when the input is read, the last value can immediatly be returned.

Dependents:   KL25Z_FFT_Demo test_armmath KL25Z_FFT_Demo_tony KL25Z_FFT_Demo_tony ... more

Supported devices

  • LPC1768
  • LPC4088
  • LPC11u24
  • KLxx
  • K20D50M

Introduction

When you read an AnalogIn object it will enable the corresponding ADC channel, depending on the implementation do either one or multiple measurements for more accuracy, and return that value to your program. This way the ADC is only active when it is required, and it is fairly straightforward. However the downside is, is that an ADC is relatively slow. On the LPC1768 it runs at 200kHz -> in that time it could also have done 500 instructions.

FastAnalogIn

This library uses the 'burst' feature of the microcontroller. This allows the ADC on the background to perform the AD conversions without requiring intervention from the microcontroller's core. Also there are no interrupts used, so also your time-sensitive code is not affected.

What the burst feature does is check which AD-channels are enabled, and he converts the enabled AD-channels one at a time. The result he stores in a register, where each channel has its own register. So this library checks which pins are used (you may make several FastAnalogIn objects, both for different pins and for the same pin, generally not extremely useful, but it is supported), and enables the relevant channels.

Reading a pin is done exactly the same for the user as AnalogIn, the read and read_us functions both work the same, and also the float operator is supported. However now it doesn't have to start a new conversion, so minus some overhead it can almost directly return the last measured value, no need to wait on the ADC!

Enable/Disable

FastAnalogIn has a few extra options that normal AnalogIn does not have: specifically you can either choose to have a FastAnalogIn object enabled or disabled. This is done with either the enable(bool enabled) and disable() functions, where enable(false) is equal to disable(), or by adding a second true/false argument to the constructor to either have it enabled at the beginning or disabled. By default it will be enabled.

LPC1768 & LPC4088
When a FastAnalogIn object is enabled, its corresponding ADC channel is also being scanned by the ADC and so it works as described above. When it is disabled you can still use the read functions, only now it will only enable the ADC channel for one conversion (actually two since for some reason the first conversion seems a bit weird), and when that conversion is done it will disable it again.

Since the ADC has to do the conversions one channel at a time, it becomes slower per channel if you enable many channels. For example, if you want to sample a sensor at a very high rate, and you also want to monitor your battery voltage. Then there is no reason to run an AD conversion on your battery continiously, so you can disable that channel and only run it once in a while.

KLxx
Multiple Fast instances can be declared of which only ONE can be continuous (all others must be non-continuous).
Example:

FastAnalogIn   speed(PTC2);           // Fast continuous
FastAnalogIn   temp1(PTC2, 0);        // Fast non-continuous.
FastAnalogIn   temp2(PTB3, 0);        // Fast non-continuous.

Downsides

Of course there are always downsides present. The extra power consumption probably won't be relevant for most, but still it is there. Aditionally there is no median filter like the normal AnalogIn has. Finally if you use AnalogIn you know exactly when the conversion happened, with FastAnalogIn you only know it was recently done but not exactly when.

AnalogIn + FastAnalogIn

Don't run both AnalogIn and FastAnalogIn objects in parallel as the results are unpredictable.
Both objects modify microcontroller registers, and neither of them bothers to inform the other one.
That's also the reason the disable() function was added.

Committer:
humlet
Date:
Sun Apr 20 16:23:19 2014 +0000
Revision:
4:cd84739f7640
Child:
5:55274430c8df
Added support for LPC4088,; Fixed linker error (missing definition of static member)

Who changed what in which revision?

UserRevisionLine numberNew contents of line
humlet 4:cd84739f7640 1 #ifdef TARGET_LPC408X
humlet 4:cd84739f7640 2
humlet 4:cd84739f7640 3 #include "FastAnalogIn.h"
humlet 4:cd84739f7640 4
humlet 4:cd84739f7640 5 static inline int div_round_up(int x, int y)
humlet 4:cd84739f7640 6 {
humlet 4:cd84739f7640 7 return (x + (y - 1)) / y;
humlet 4:cd84739f7640 8 }
humlet 4:cd84739f7640 9
humlet 4:cd84739f7640 10 static const PinMap PinMap_ADC[] = {
humlet 4:cd84739f7640 11 {P0_23, ADC0_0, 0x01},
humlet 4:cd84739f7640 12 {P0_24, ADC0_1, 0x01},
humlet 4:cd84739f7640 13 {P0_25, ADC0_2, 0x01},
humlet 4:cd84739f7640 14 {P0_26, ADC0_3, 0x01},
humlet 4:cd84739f7640 15 {P1_30, ADC0_4, 0x03},
humlet 4:cd84739f7640 16 {P1_31, ADC0_5, 0x03},
humlet 4:cd84739f7640 17 {P0_12, ADC0_6, 0x03},
humlet 4:cd84739f7640 18 {P0_13, ADC0_7, 0x03},
humlet 4:cd84739f7640 19 {NC , NC , 0 }
humlet 4:cd84739f7640 20 };
humlet 4:cd84739f7640 21
humlet 4:cd84739f7640 22 int FastAnalogIn::channel_usage[8] = {0,0,0,0,0,0,0,0};
humlet 4:cd84739f7640 23
humlet 4:cd84739f7640 24 FastAnalogIn::FastAnalogIn(PinName pin, bool enabled)
humlet 4:cd84739f7640 25 {
humlet 4:cd84739f7640 26 ADCnumber = (ADCName)pinmap_peripheral(pin, PinMap_ADC);
humlet 4:cd84739f7640 27 if (ADCnumber == (uint32_t)NC)
humlet 4:cd84739f7640 28 error("ADC pin mapping failed");
humlet 4:cd84739f7640 29 //printf("ADC_ID=%u\n",ADCnumber);
humlet 4:cd84739f7640 30 datareg = (uint32_t*) (&LPC_ADC->DR[ADCnumber]);
humlet 4:cd84739f7640 31
humlet 4:cd84739f7640 32 wait_us(1000); // we need either the debug printf above or a wait before we try to initialize the ADC
humlet 4:cd84739f7640 33
humlet 4:cd84739f7640 34 // ensure power is turned on
humlet 4:cd84739f7640 35 LPC_SC->PCONP |= (1 << 12);
humlet 4:cd84739f7640 36
humlet 4:cd84739f7640 37 uint32_t PCLK = PeripheralClock;
humlet 4:cd84739f7640 38
humlet 4:cd84739f7640 39 // calculate minimum clock divider
humlet 4:cd84739f7640 40 // clkdiv = divider - 1
humlet 4:cd84739f7640 41 uint32_t MAX_ADC_CLK = 12400000;
humlet 4:cd84739f7640 42 uint32_t clkdiv = div_round_up(PCLK, MAX_ADC_CLK) - 1;
humlet 4:cd84739f7640 43 // Set the clkdiv
humlet 4:cd84739f7640 44 LPC_ADC->CR &= ~(255<<8);
humlet 4:cd84739f7640 45 LPC_ADC->CR |= clkdiv<<8;
humlet 4:cd84739f7640 46
humlet 4:cd84739f7640 47 //Enable ADC:
humlet 4:cd84739f7640 48 LPC_ADC->CR |= 1<<21;
humlet 4:cd84739f7640 49
humlet 4:cd84739f7640 50 //Enable burstmode, set start as zero
humlet 4:cd84739f7640 51 LPC_ADC->CR |= 1<<16;
humlet 4:cd84739f7640 52 LPC_ADC->CR &= ~(7<<24);
humlet 4:cd84739f7640 53
humlet 4:cd84739f7640 54 // must enable analog mode (ADMODE = 0) ... ??? just copied from official LPC408X analogin_api.c
humlet 4:cd84739f7640 55 __IO uint32_t *reg = (__IO uint32_t*) (LPC_IOCON_BASE + 4 * pin);
humlet 4:cd84739f7640 56 *reg &= ~(1 << 7);
humlet 4:cd84739f7640 57
humlet 4:cd84739f7640 58 //Map pins
humlet 4:cd84739f7640 59 pinmap_pinout(pin, PinMap_ADC);
humlet 4:cd84739f7640 60
humlet 4:cd84739f7640 61 //Enable channel
humlet 4:cd84739f7640 62 running = false;
humlet 4:cd84739f7640 63 enable(enabled);
humlet 4:cd84739f7640 64
humlet 4:cd84739f7640 65 }
humlet 4:cd84739f7640 66
humlet 4:cd84739f7640 67 void FastAnalogIn::enable(bool enabled)
humlet 4:cd84739f7640 68 {
humlet 4:cd84739f7640 69 //If currently not running
humlet 4:cd84739f7640 70 if (!running) {
humlet 4:cd84739f7640 71 if (enabled) {
humlet 4:cd84739f7640 72 //Enable the ADC channel
humlet 4:cd84739f7640 73 channel_usage[ADCnumber]++;
humlet 4:cd84739f7640 74 LPC_ADC->CR |= (1<<ADCnumber);
humlet 4:cd84739f7640 75 running = true;
humlet 4:cd84739f7640 76 } else
humlet 4:cd84739f7640 77 disable();
humlet 4:cd84739f7640 78 }
humlet 4:cd84739f7640 79 }
humlet 4:cd84739f7640 80
humlet 4:cd84739f7640 81 void FastAnalogIn::disable( void )
humlet 4:cd84739f7640 82 {
humlet 4:cd84739f7640 83 //If currently running
humlet 4:cd84739f7640 84 if (running) {
humlet 4:cd84739f7640 85 channel_usage[ADCnumber]--;
humlet 4:cd84739f7640 86
humlet 4:cd84739f7640 87 if (channel_usage[ADCnumber]==0)
humlet 4:cd84739f7640 88 LPC_ADC->CR &= ~(1<<ADCnumber);
humlet 4:cd84739f7640 89 }
humlet 4:cd84739f7640 90 running = false;
humlet 4:cd84739f7640 91 }
humlet 4:cd84739f7640 92
humlet 4:cd84739f7640 93 unsigned short FastAnalogIn::read_u16( void )
humlet 4:cd84739f7640 94 {
humlet 4:cd84739f7640 95 volatile unsigned int retval;
humlet 4:cd84739f7640 96 //If object is enabled return current value of datareg
humlet 4:cd84739f7640 97 if (running ){
humlet 4:cd84739f7640 98 retval = *datareg;
humlet 4:cd84739f7640 99 //If it isn't running, enable it and wait until new value is written to datareg
humlet 4:cd84739f7640 100 }else {
humlet 4:cd84739f7640 101 //Force a read to clear done bit, enable the ADC channel
humlet 4:cd84739f7640 102 retval = *datareg;
humlet 4:cd84739f7640 103 enable();
humlet 4:cd84739f7640 104 //Wait until it is converted
humlet 4:cd84739f7640 105 while(1) {
humlet 4:cd84739f7640 106 wait_us(1);
humlet 4:cd84739f7640 107 retval = *datareg;
humlet 4:cd84739f7640 108 if ((retval>>31) == 1)
humlet 4:cd84739f7640 109 break;
humlet 4:cd84739f7640 110 }
humlet 4:cd84739f7640 111 //Do a second conversion since first one always fails for some reason
humlet 4:cd84739f7640 112 while(1) {
humlet 4:cd84739f7640 113 wait_us(1);
humlet 4:cd84739f7640 114 retval = *datareg;
humlet 4:cd84739f7640 115 if ((retval>>31) == 1)
humlet 4:cd84739f7640 116 break;
humlet 4:cd84739f7640 117 }
humlet 4:cd84739f7640 118 //Disable again
humlet 4:cd84739f7640 119 disable();
humlet 4:cd84739f7640 120 }
humlet 4:cd84739f7640 121
humlet 4:cd84739f7640 122 //Do same thing as standard mbed lib, unused bit 0-3, replicate 4-7 in it
humlet 4:cd84739f7640 123 retval &= ~0xFFFF000F;
humlet 4:cd84739f7640 124 retval |= (retval >> 8) & 0x000F;
humlet 4:cd84739f7640 125 return retval;
humlet 4:cd84739f7640 126
humlet 4:cd84739f7640 127 }
humlet 4:cd84739f7640 128 #endif //defined TARGET_LPC408X