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 Apr 21 10:26:49 2014 +0000
Revision:
7:965a2b0e477f
Parent:
5:55274430c8df
moved channel_usage to .cpp

Who changed what in which revision?

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