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
Fixed pinmapping of LPC1114

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Sissors9:31184aa1449c 1#if defined(TARGET_KLXX) || defined(TARGET_K20D50M)
Sissors9:31184aa1449c 2
Sissors9:31184aa1449c 3#include "FastAnalogIn.h"
Sissors9:31184aa1449c 4#include "clk_freqs.h"
Sissors9:31184aa1449c 5
Sissors9:31184aa1449c 6#define MAX_FADC 6000000
Sissors9:31184aa1449c 7#define CHANNELS_A_SHIFT 5
Sissors9:31184aa1449c 8
Sissors9:31184aa1449c 9#ifdef TARGET_K20D50M
Sissors9:31184aa1449c 10static const PinMap PinMap_ADC[] = {
Sissors9:31184aa1449c 11 {PTC2, ADC0_SE4b, 0},
Sissors9:31184aa1449c 12 {PTD1, ADC0_SE5b, 0},
Sissors9:31184aa1449c 13 {PTD5, ADC0_SE6b, 0},
Sissors9:31184aa1449c 14 {PTD6, ADC0_SE7b, 0},
Sissors9:31184aa1449c 15 {PTB0, ADC0_SE8, 0},
Sissors9:31184aa1449c 16 {PTB1, ADC0_SE9, 0},
Sissors9:31184aa1449c 17 {PTB2, ADC0_SE12, 0},
Sissors9:31184aa1449c 18 {PTB3, ADC0_SE13, 0},
Sissors9:31184aa1449c 19 {PTC0, ADC0_SE14, 0},
Sissors9:31184aa1449c 20 {PTC1, ADC0_SE15, 0},
Sissors9:31184aa1449c 21 {NC, NC, 0}
Sissors9:31184aa1449c 22};
Sissors9:31184aa1449c 23#endif
Sissors9:31184aa1449c 24
Sissors9:31184aa1449c 25FastAnalogIn::FastAnalogIn(PinName pin, bool enabled)
Sissors9:31184aa1449c 26{
Sissors9:31184aa1449c 27 ADCnumber = (ADCName)pinmap_peripheral(pin, PinMap_ADC);
Sissors9:31184aa1449c 28 if (ADCnumber == (ADCName)NC) {
Sissors9:31184aa1449c 29 error("ADC pin mapping failed");
Sissors9:31184aa1449c 30 }
Sissors9:31184aa1449c 31
Sissors9:31184aa1449c 32 SIM->SCGC6 |= SIM_SCGC6_ADC0_MASK;
Sissors9:31184aa1449c 33
Sissors9:31184aa1449c 34 uint32_t port = (uint32_t)pin >> PORT_SHIFT;
Sissors9:31184aa1449c 35 SIM->SCGC5 |= 1 << (SIM_SCGC5_PORTA_SHIFT + port);
Sissors9:31184aa1449c 36
Sissors9:31184aa1449c 37 uint32_t cfg2_muxsel = ADC_CFG2_MUXSEL_MASK;
Sissors9:31184aa1449c 38 if (ADCnumber & (1 << CHANNELS_A_SHIFT)) {
Sissors9:31184aa1449c 39 cfg2_muxsel = 0;
Sissors9:31184aa1449c 40 }
Sissors9:31184aa1449c 41
Sissors9:31184aa1449c 42 // bus clk
Sissors9:31184aa1449c 43 uint32_t PCLK = bus_frequency();
Sissors9:31184aa1449c 44 uint32_t clkdiv;
Sissors9:31184aa1449c 45 for (clkdiv = 0; clkdiv < 4; clkdiv++) {
Sissors9:31184aa1449c 46 if ((PCLK >> clkdiv) <= MAX_FADC)
Sissors9:31184aa1449c 47 break;
Sissors9:31184aa1449c 48 }
Sissors9:31184aa1449c 49 if (clkdiv == 4) //Set max div
Sissors9:31184aa1449c 50 clkdiv = 0x7;
Sissors9:31184aa1449c 51
Sissors9:31184aa1449c 52 ADC0->SC1[1] = ADC_SC1_ADCH(ADCnumber & ~(1 << CHANNELS_A_SHIFT));
Sissors9:31184aa1449c 53
Sissors9:31184aa1449c 54 ADC0->CFG1 = ADC_CFG1_ADIV(clkdiv & 0x3) // Clock Divide Select: (Input Clock)/8
Sissors9:31184aa1449c 55 | ADC_CFG1_MODE(3) // (16)bits Resolution
Sissors9:31184aa1449c 56 | ADC_CFG1_ADICLK(clkdiv >> 2); // Input Clock: (Bus Clock)/2
Sissors9:31184aa1449c 57
Sissors9:31184aa1449c 58 ADC0->CFG2 = cfg2_muxsel // ADxxb or ADxxa channels
Sissors9:31184aa1449c 59 | ADC_CFG2_ADACKEN_MASK // Asynchronous Clock Output Enable
Sissors9:31184aa1449c 60 | ADC_CFG2_ADHSC_MASK; // High-Speed Configuration
Sissors9:31184aa1449c 61
Sissors9:31184aa1449c 62 ADC0->SC2 = ADC_SC2_REFSEL(0); // Default Voltage Reference
Sissors9:31184aa1449c 63
Sissors9:31184aa1449c 64 pinmap_pinout(pin, PinMap_ADC);
Sissors9:31184aa1449c 65
Sissors9:31184aa1449c 66 //Enable channel
Sissors9:31184aa1449c 67 running = false;
Sissors9:31184aa1449c 68 enable(enabled);
Sissors9:31184aa1449c 69}
Sissors9:31184aa1449c 70
Sissors9:31184aa1449c 71void FastAnalogIn::enable(bool enabled)
Sissors9:31184aa1449c 72{
Sissors9:31184aa1449c 73 //If currently not running
Sissors9:31184aa1449c 74 if (!running) {
Sissors9:31184aa1449c 75 if (enabled) {
Sissors9:31184aa1449c 76 //Enable the ADC channel
Sissors9:31184aa1449c 77 ADC0->SC3 |= ADC_SC3_ADCO_MASK; // Enable continuous conversion
Sissors9:31184aa1449c 78 ADC0->SC1[0] = ADC_SC1_ADCH(ADCnumber & ~(1 << CHANNELS_A_SHIFT)); //Start conversion
Sissors9:31184aa1449c 79 running = true;
Sissors9:31184aa1449c 80 } else
Sissors9:31184aa1449c 81 disable();
Sissors9:31184aa1449c 82 }
Sissors9:31184aa1449c 83}
Sissors9:31184aa1449c 84
Sissors9:31184aa1449c 85void FastAnalogIn::disable( void )
Sissors9:31184aa1449c 86{
Sissors9:31184aa1449c 87 //If currently running
Sissors9:31184aa1449c 88 if (running) {
Sissors9:31184aa1449c 89 ADC0->SC3 &= ~ADC_SC3_ADCO_MASK; // Disable continuous conversion
Sissors9:31184aa1449c 90 }
Sissors9:31184aa1449c 91 running = false;
Sissors9:31184aa1449c 92}
Sissors9:31184aa1449c 93
Sissors9:31184aa1449c 94uint16_t FastAnalogIn::read_u16()
Sissors9:31184aa1449c 95{
Sissors9:31184aa1449c 96 if (!running)
Sissors9:31184aa1449c 97 {
Sissors9:31184aa1449c 98 // start conversion
Sissors9:31184aa1449c 99 ADC0->SC1[0] = ADC_SC1_ADCH(ADCnumber & ~(1 << CHANNELS_A_SHIFT));
Sissors9:31184aa1449c 100 // Wait Conversion Complete
Sissors9:31184aa1449c 101 while ((ADC0->SC1[0] & ADC_SC1_COCO_MASK) != ADC_SC1_COCO_MASK);
Sissors9:31184aa1449c 102 }
Sissors9:31184aa1449c 103 if(running && ((ADC0->SC1[0]&ADC_SC1_ADCH_MASK) != (ADC_SC1_ADCH(ADCnumber & ~(1 << CHANNELS_A_SHIFT)))))
Sissors9:31184aa1449c 104 {
Sissors9:31184aa1449c 105 running = false;
Sissors9:31184aa1449c 106 enable();
Sissors9:31184aa1449c 107 while ((ADC0->SC1[0] & ADC_SC1_COCO_MASK) != ADC_SC1_COCO_MASK);
Sissors9:31184aa1449c 108 }
Sissors9:31184aa1449c 109 // Return value
Sissors9:31184aa1449c 110 return (uint16_t)ADC0->R[0];
Sissors9:31184aa1449c 111}
Sissors9:31184aa1449c 112
Sissors9:31184aa1449c 113#endif //defined TARGET_KLXX
Sissors9:31184aa1449c 114