Erik Olieman / FastAnalogIn

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:
Sissors
Date:
Mon Mar 21 07:41:52 2016 +0000
Revision:
12:46fbc645de4d
Parent:
4:cd84739f7640
Fixed pinmapping of LPC1114

Who changed what in which revision?

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