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:
5:55274430c8df
Fixed pinmapping of LPC1114

Who changed what in which revision?

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