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.

FastAnalogIn_LPC11UXX.cpp

Committer:
Sissors
Date:
16 months ago
Revision:
12:46fbc645de4d
Parent:
11:14744c4ac884

File content as of revision 12:46fbc645de4d:

#if defined(TARGET_LPC11UXX) || defined (TARGET_LPC11XX)

#include "FastAnalogIn.h"
static inline int div_round_up(int x, int y)
{
    return (x + (y - 1)) / y;
}

#define LPC_IOCON0_BASE (LPC_IOCON_BASE)
#define LPC_IOCON1_BASE (LPC_IOCON_BASE + 0x60)
#define MAX_ADC_CLK     4500000

#ifdef TARGET_LPC11UXX
static const PinMap PinMap_ADC[] = {
    {P0_11, ADC0_0, 0x02},
    {P0_12, ADC0_1, 0x02},
    {P0_13, ADC0_2, 0x02},
    {P0_14, ADC0_3, 0x02},
    {P0_15, ADC0_4, 0x02},
    {P0_16, ADC0_5, 0x01},
    {P0_22, ADC0_6, 0x01},
    {P0_23, ADC0_7, 0x01},
    {NC   , NC    , 0   }
};
#else
static const PinMap PinMap_ADC[] = {
    {P0_11, ADC0_0, 2},
    {P1_0 , ADC0_1, 2},
    {P1_1 , ADC0_2, 2},
    {P1_2 , ADC0_3, 2},
    // {P1_3 , ADC0_4, 2}, -- should be mapped to SWDIO only
    {P1_4 , ADC0_5, 1},
    {P1_10, ADC0_6, 1},
    {P1_11, ADC0_7, 1},
    {NC   , NC    , 0}
};
#endif

static int channel_usage[8] = {0,0,0,0,0,0,0,0};



FastAnalogIn::FastAnalogIn(PinName pin, bool enabled)
{
    ADCnumber = (ADCName)pinmap_peripheral(pin, PinMap_ADC);
    if (ADCnumber == (uint32_t)NC)
        error("ADC pin mapping failed");
    
    //Seriously software people, can't you guys never keep the namings the same?
    #ifdef TARGET_LPC11UXX
    datareg = (uint32_t*) (&LPC_ADC->DR0 + ADCnumber);
    #else
    datareg = (uint32_t*) (&LPC_ADC->DR[ADCnumber]);
    #endif
    
    // Power up ADC
    LPC_SYSCON->PDRUNCFG &= ~ (1 << 4);
    LPC_SYSCON->SYSAHBCLKCTRL |= ((uint32_t)1 << 13);
 
    #ifdef TARGET_LPC11UXX
    uint32_t pin_number = (uint32_t)pin;
    __IO uint32_t *reg = (pin_number < 32) ? (__IO uint32_t*)(LPC_IOCON0_BASE + 4 * pin_number) : (__IO uint32_t*)(LPC_IOCON1_BASE + 4 * (pin_number - 32));
    #else
    uint32_t offset = (uint32_t)pin & 0xff; 
    __IO uint32_t *reg = (__IO uint32_t*)(LPC_IOCON_BASE + offset); 
    #endif

 
    // set pin to ADC mode
    *reg &= ~(1 << 7); // set ADMODE = 0 (analog mode)
 
    uint32_t clkdiv = div_round_up(SystemCoreClock, MAX_ADC_CLK) - 1;
 
    LPC_ADC->CR = (LPC_ADC->CR & 0xFF)      // keep current channels
                | (clkdiv << 8) // max of 4.5MHz
                | (1 << 16)     // BURST = 1, hardware controlled
                | ( 0 << 17 );  // CLKS = 0, we stick to 10 bit mode
    
    pinmap_pinout(pin, PinMap_ADC);

    //Enable channel
    running = false;
    enable(enabled);

}

void FastAnalogIn::enable(bool enabled)
{
    //If currently not running
    if (!running) {
        if (enabled) {
            //Enable the ADC channel
            channel_usage[ADCnumber]++;
            LPC_ADC->CR |= (1<<ADCnumber);
            running = true;
        } else
            disable();
    }
}

void FastAnalogIn::disable( void )
{
    //If currently running
    if (running) {
        channel_usage[ADCnumber]--;
        
        if (channel_usage[ADCnumber]==0)
            LPC_ADC->CR &= ~(1<<ADCnumber);
    }
    running = false;
}

unsigned short FastAnalogIn::read_u16( void )
{
    unsigned int retval;
    //If object is enabled return current value of datareg
    if (running)
        retval = *datareg;
 
    //If it isn't running, enable it and wait until new value is written to datareg
    else {
        //Force a read to clear done bit, enable the ADC channel
        retval = *datareg;
        enable();
        //Wait until it is converted
        while(1) {
            retval = *datareg;
            if ((retval>>31) == 1)
                break;
        }
        //Disable again
        disable();
    }
    
    //Do same thing as standard mbed lib, unused bit 0-3, replicate 4-7 in it
    retval &= ~0xFFFF003F;
    retval |= (retval >> 6) & 0x003F;
    return retval;
}
#endif //defined TARGET_LPC11UXX