High speed analog input class.

Dependents:   HighSpeedAnalogIn_TestProgram WallbotR4_BTLE brunoDrome

Files at this revision

API Documentation at this revision

Comitter:
shintamainjp
Date:
Fri Feb 04 22:17:00 2011 +0000
Commit message:
Initial version.

Changed in this revision

HighSpeedAnalogIn.cpp Show annotated file Show diff for this revision Revisions of this file
HighSpeedAnalogIn.h Show annotated file Show diff for this revision Revisions of this file
diff -r 000000000000 -r db55359719e7 HighSpeedAnalogIn.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HighSpeedAnalogIn.cpp	Fri Feb 04 22:17:00 2011 +0000
@@ -0,0 +1,292 @@
+
+#include "HighSpeedAnalogIn.h"
+
+HighSpeedAnalogIn *HighSpeedAnalogIn::instance;
+int HighSpeedAnalogIn::refcnt = 0;
+
+HighSpeedAnalogIn::HighSpeedAnalogIn(PinName pin0, PinName pin1, PinName pin2, PinName pin3, PinName pin4, PinName pin5) {
+
+    refcnt++;
+    if (refcnt > 1) {
+        error("Please do not use over an object.");
+    }
+
+    static const int sample_rate = 200000;
+    static const int cclk_div = 1;
+
+    int adc_clk_freq = CLKS_PER_SAMPLE * sample_rate;
+    int m = (LPC_SC->PLL0CFG & 0xFFFF) + 1;
+    int n = (LPC_SC->PLL0CFG >> 16) + 1;
+    int cclkdiv = LPC_SC->CCLKCFG + 1;
+    int Fcco = (2 * m * XTAL_FREQ) / n;
+    int cclk = Fcco / cclkdiv;
+
+    LPC_SC->PCONP |= (1 << 12);
+    LPC_SC->PCLKSEL0 &= ~(0x3 << 24);
+    switch (cclk_div) {
+        case 1:
+            LPC_SC->PCLKSEL0 |= 0x1 << 24;
+            break;
+        case 2:
+            LPC_SC->PCLKSEL0 |= 0x2 << 24;
+            break;
+        case 4:
+            LPC_SC->PCLKSEL0 |= 0x0 << 24;
+            break;
+        case 8:
+            LPC_SC->PCLKSEL0 |= 0x3 << 24;
+            break;
+        default:
+            fprintf(stderr, "Warning: ADC CCLK clock divider must be 1, 2, 4 or 8. %u supplied.\n", cclk_div);
+            fprintf(stderr, "Defaulting to 1.\n");
+            LPC_SC->PCLKSEL0 |= 0x1 << 24;
+            break;
+    }
+    int pclk = cclk / cclk_div;
+    int clock_div = pclk / adc_clk_freq;
+
+    if (clock_div > 0xFF) {
+        fprintf(stderr, "Warning: Clock division is %u which is above 255 limit. Re-Setting at limit.\n", clock_div);
+        clock_div = 0xFF;
+    }
+    if (clock_div == 0) {
+        fprintf(stderr, "Warning: Clock division is 0. Re-Setting to 1.\n");
+        clock_div = 1;
+    }
+
+    int _adc_clk_freq = pclk / clock_div;
+    if (_adc_clk_freq > MAX_ADC_CLOCK) {
+        fprintf(stderr, "Warning: Actual ADC sample rate of %u which is above %u limit\n", _adc_clk_freq / CLKS_PER_SAMPLE, MAX_ADC_CLOCK / CLKS_PER_SAMPLE);
+        int max_div = 1;
+        while ((pclk / max_div) > MAX_ADC_CLOCK) {
+            max_div++;
+        }
+        fprintf(stderr, "Maximum recommended sample rate is %u\n", (pclk / max_div) / CLKS_PER_SAMPLE);
+    }
+
+    LPC_ADC->ADCR = ((clock_div - 1) << 8) | (1 << 21);
+    LPC_ADC->ADCR &= ~0xFF;
+
+    for (int i = 0; i < 8; i++) {
+        _adc_data[i] = 0;
+    }
+
+    // Attach IRQ
+    instance = this;
+    NVIC_SetVector(ADC_IRQn, (uint32_t)&static_adcisr);
+
+    // Disable global interrupt
+    LPC_ADC->ADINTEN &= ~0x100;
+
+    // Clock frequency.
+    printf("Clock frequency:%d\n", _adc_clk_freq);
+
+    // Actual sampling rate.
+    printf("Actual sampling rate:%d\n", _adc_clk_freq / CLKS_PER_SAMPLE);
+    
+    int tmp = LPC_ADC->ADCR & ~(0x0F << 24);
+    tmp |= ((0x0 & 7) << 24) | ((0x0 & 1) << 27);
+    LPC_ADC->ADCR = tmp;
+    LPC_ADC->ADCR |= (1 << 16);
+
+    if (pin0 != NC) setup(pin0, 1);
+    if (pin1 != NC) setup(pin1, 1);
+    if (pin2 != NC) setup(pin2, 1);
+    if (pin3 != NC) setup(pin3, 1);
+    if (pin4 != NC) setup(pin4, 1);
+    if (pin5 != NC) setup(pin5, 1);
+
+    interrupt_state(pin0, 1);
+}
+
+HighSpeedAnalogIn::~HighSpeedAnalogIn() {
+}
+
+void HighSpeedAnalogIn::static_adcisr(void) {
+    instance->adcisr();
+}
+
+void HighSpeedAnalogIn::adcisr(void) {
+    uint32_t stat = LPC_ADC->ADSTAT;
+    // Scan channels for over-run or done and update array
+    if (stat & 0x0101) _adc_data[0] = LPC_ADC->ADDR0;
+    if (stat & 0x0202) _adc_data[1] = LPC_ADC->ADDR1;
+    if (stat & 0x0404) _adc_data[2] = LPC_ADC->ADDR2;
+    if (stat & 0x0808) _adc_data[3] = LPC_ADC->ADDR3;
+    if (stat & 0x1010) _adc_data[4] = LPC_ADC->ADDR4;
+    if (stat & 0x2020) _adc_data[5] = LPC_ADC->ADDR5;
+    if (stat & 0x4040) _adc_data[6] = LPC_ADC->ADDR6;
+    if (stat & 0x8080) _adc_data[7] = LPC_ADC->ADDR7;
+}
+
+int HighSpeedAnalogIn::get_channel(PinName pin) {
+    int ch;
+    switch (pin) {
+        case p15:// =p0.23 of LPC1768
+            ch = 0;
+            break;
+        case p16:// =p0.24 of LPC1768
+            ch = 1;
+            break;
+        case p17:// =p0.25 of LPC1768
+            ch = 2;
+            break;
+        case p18:// =p0.26 of LPC1768
+            ch = 3;
+            break;
+        case p19:// =p1.30 of LPC1768
+            ch = 4;
+            break;
+        case p20:// =p1.31 of LPC1768
+            ch = 5;
+            break;
+        default:
+            ch = 0;
+            break;
+    }
+    return ch;
+}
+
+uint32_t HighSpeedAnalogIn::get_data(PinName pin) {
+    // If in burst mode and at least one interrupt enabled then
+    // take all values from _adc_data
+    if (LPC_ADC->ADINTEN & 0x3F) {
+        return (_adc_data[get_channel(pin)]);
+    } else {
+        // Return current register value or last value from interrupt
+        switch (pin) {
+            case p15:// =p0.23 of LPC1768
+                return ((LPC_ADC->ADINTEN & 0x01) ? _adc_data[0] : LPC_ADC->ADDR0);
+            case p16:// =p0.24 of LPC1768
+                return ((LPC_ADC->ADINTEN & 0x02) ? _adc_data[1] : LPC_ADC->ADDR1);
+            case p17:// =p0.25 of LPC1768
+                return ((LPC_ADC->ADINTEN & 0x04) ? _adc_data[2] : LPC_ADC->ADDR2);
+            case p18:// =p0.26 of LPC1768:
+                return ((LPC_ADC->ADINTEN & 0x08) ? _adc_data[3] : LPC_ADC->ADDR3);
+            case p19:// =p1.30 of LPC1768
+                return ((LPC_ADC->ADINTEN & 0x10) ? _adc_data[4] : LPC_ADC->ADDR4);
+            case p20:// =p1.31 of LPC1768
+                return ((LPC_ADC->ADINTEN & 0x20) ? _adc_data[5] : LPC_ADC->ADDR5);
+            default:
+                return 0;
+        }
+    }
+}
+
+// Enable or disable an HighSpeedAnalogIn pin
+void HighSpeedAnalogIn::setup(PinName pin, int state) {
+    int ch = get_channel(pin);
+    if ((state & 1) == 1) {
+        switch (pin) {
+            case p15:// =p0.23 of LPC1768
+                LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 14);
+                LPC_PINCON->PINSEL1 |= (unsigned int)0x1 << 14;
+                LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 14);
+                LPC_PINCON->PINMODE1 |= (unsigned int)0x2 << 14;
+                break;
+            case p16:// =p0.24 of LPC1768
+                LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 16);
+                LPC_PINCON->PINSEL1 |= (unsigned int)0x1 << 16;
+                LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 16);
+                LPC_PINCON->PINMODE1 |= (unsigned int)0x2 << 16;
+                break;
+            case p17:// =p0.25 of LPC1768
+                LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 18);
+                LPC_PINCON->PINSEL1 |= (unsigned int)0x1 << 18;
+                LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 18);
+                LPC_PINCON->PINMODE1 |= (unsigned int)0x2 << 18;
+                break;
+            case p18:// =p0.26 of LPC1768:
+                LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 20);
+                LPC_PINCON->PINSEL1 |= (unsigned int)0x1 << 20;
+                LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 20);
+                LPC_PINCON->PINMODE1 |= (unsigned int)0x2 << 20;
+                break;
+            case p19:// =p1.30 of LPC1768
+                LPC_PINCON->PINSEL3 &= ~((unsigned int)0x3 << 28);
+                LPC_PINCON->PINSEL3 |= (unsigned int)0x3 << 28;
+                LPC_PINCON->PINMODE3 &= ~((unsigned int)0x3 << 28);
+                LPC_PINCON->PINMODE3 |= (unsigned int)0x2 << 28;
+                break;
+            case p20:// =p1.31 of LPC1768
+                LPC_PINCON->PINSEL3 &= ~((unsigned int)0x3 << 30);
+                LPC_PINCON->PINSEL3 |= (unsigned int)0x3 << 30;
+                LPC_PINCON->PINMODE3 &= ~((unsigned int)0x3 << 30);
+                LPC_PINCON->PINMODE3 |= (unsigned int)0x2 << 30;
+                break;
+            default:
+                error("Invalid pin.");
+                break;
+        }
+        // Select channel
+        LPC_ADC->ADCR |= (1 << ch);
+    } else {
+        switch (pin) {
+            case p15://=p0.23 of LPC1768
+                LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 14);
+                LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 14);
+                break;
+            case p16://=p0.24 of LPC1768
+                LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 16);
+                LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 16);
+                break;
+            case p17://=p0.25 of LPC1768
+                LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 18);
+                LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 18);
+                break;
+            case p18://=p0.26 of LPC1768:
+                LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 20);
+                LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 20);
+                break;
+            case p19://=p1.30 of LPC1768
+                LPC_PINCON->PINSEL3 &= ~((unsigned int)0x3 << 28);
+                LPC_PINCON->PINMODE3 &= ~((unsigned int)0x3 << 28);
+                break;
+            case p20://=p1.31 of LPC1768
+                LPC_PINCON->PINSEL3 &= ~((unsigned int)0x3 << 30);
+                LPC_PINCON->PINMODE3 &= ~((unsigned int)0x3 << 30);
+                break;
+            default:
+                error("Invalid pin.");
+                break;
+        }
+        LPC_ADC->ADCR &= ~(1 << ch);
+    }
+}
+
+void HighSpeedAnalogIn::interrupt_state(PinName pin, int state) {
+    int ch = get_channel(pin);
+    if (state == 1) {
+        LPC_ADC->ADINTEN &= ~0x100;
+        LPC_ADC->ADINTEN |= 1 << ch;
+        /* Enable the HighSpeedAnalogIn Interrupt */
+        NVIC_EnableIRQ(ADC_IRQn);
+    } else {
+        LPC_ADC->ADINTEN &= ~(1 << ch);
+        //Disable interrrupt if no active pins left
+        if ((LPC_ADC->ADINTEN & 0xFF) == 0)
+            NVIC_DisableIRQ(ADC_IRQn);
+    }
+}
+
+float HighSpeedAnalogIn::read(PinName pin) {
+    /*
+     * Reset DONE and OVERRUN.
+     *
+     * bit 31 : DONE
+     * bit 30 : OVERRUN
+     */
+    _adc_data[get_channel(pin)] &= ~(((uint32_t)0x01 << 31) | ((uint32_t)0x01 << 30));
+    return (float)((get_data(pin) >> 4) & 0xFFF) / (float)0xFFF;
+}
+
+unsigned short HighSpeedAnalogIn::read_u16(PinName pin) {
+    /*
+     * Reset DONE and OVERRUN.
+     *
+     * bit 31 : DONE
+     * bit 30 : OVERRUN
+     */
+    _adc_data[get_channel(pin)] &= ~(((uint32_t)0x01 << 31) | ((uint32_t)0x01 << 30));
+    return ((get_data(pin) >> 4) & 0xFFF);
+}
diff -r 000000000000 -r db55359719e7 HighSpeedAnalogIn.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HighSpeedAnalogIn.h	Fri Feb 04 22:17:00 2011 +0000
@@ -0,0 +1,35 @@
+#ifndef HIGH_SPEED_ANALOG_IN_H
+#define HIGH_SPEED_ANALOG_IN_H
+
+#include "mbed.h"
+
+class HighSpeedAnalogIn {
+public:
+
+    HighSpeedAnalogIn(PinName pin0, PinName pin1 = NC, PinName pin2 = NC, PinName pin3 = NC, PinName pin4 = NC, PinName pin5 = NC);
+    ~HighSpeedAnalogIn();
+    float read(PinName pin);
+    unsigned short read_u16(PinName pin);
+
+private:
+
+    HighSpeedAnalogIn();
+    uint32_t _adc_data[8];
+
+    static const int XTAL_FREQ = 12000000;
+    static const int MAX_ADC_CLOCK = 13000000;
+    static const int CLKS_PER_SAMPLE = 64;
+    
+    static HighSpeedAnalogIn *instance;
+    static int refcnt;
+
+    static void static_adcisr(void);
+
+    int get_channel(PinName pin);
+    uint32_t get_data(PinName pin);
+    void adcisr(void);
+    void setup(PinName pin, int state);
+    void interrupt_state(PinName pin, int state);
+};
+
+#endif