Mbed ADC
Contents
- 1 Getting the best ADC performance from mbed
- 2 topic 1514 Mbed design of analog part
- 3 topic 1866 AnalogIn 'glitches' --- a 'cure' and a tool
- 4 topic 131 AnalogIn problem
- 5 topic 2849 Analog In
- 6 topic 2919 spikes again
- 7 topic 2871 Increasing Sampling Frequency
- 8 Hiroshi Yamaguchi, AnalogIn2 Library and Program
- 9 Simon Blandford ADC Library
- 10 AD7190 with mbed
Getting the best ADC performance from mbed
topic 1514 Mbed design of analog part
- http://mbed.org/forum/bugs-suggestions/topic/1514/
- http://mbed.org/forum/bugs-suggestions/topic/1514/?page=2#comment-7783
- Check word:
bead
- user Thomas Olson # 13 Dec 2010 . Edited: 13 Dec 2010:
topic 1866 AnalogIn 'glitches' --- a 'cure' and a tool
- user Armin Strobel 09 Aug 2011:
- I read 3 times directly and chose than the averages of the two values with the least difference. Its a dirty hack but it works very well.
<cpp> raw[0] = ax.read_u16(); raw[1] = ax.read_u16(); raw[2] = ax.read_u16(); raw01 = abs(raw[0]-raw[1]); raw12 = abs(raw[1]-raw[2]); raw20 = abs(raw[2]-raw[0]); if((raw01<=raw12)&&(raw01<=raw20))
rawM = (raw[0]+raw[1])/2;
if((raw12<=raw01)&&(raw12<=raw20))
rawM = (raw[1]+raw[2])/2;
if((raw20<=raw12)&&(raw20<=raw01))
rawM = (raw[2]+raw[0])/2;
</cpp>
- user David Peters # 08 Aug 2011:
- Its a very simple way to filter, but it works good for me, see the result here:
<cpp> for(int i=0; i<1024; i++) {
if (abs((samples[i]-samples[i-1]))>3500 && i!=0){samples[i]=((samples[i-1]+samples[i+1])/2);} sampleresult= (float(samples[i])*3.3)/(65536); printf("%.2f,\r", sampleresult); }
</cpp>
topic 131 AnalogIn problem
- http://mbed.org/forum/mbed/topic/131/?page=1#comment-1835
- user Simon Ford # 04 Jan 2010 . Edited: 04 Jan 2010:
- Hi Rob,
I too would like to get to the bottom of this; sorry to disappoint you about the time it has taken for us to officially address it.
I'd definitely encourage people to have a play directly with the analog hardware (or any functions of the chip for that matter). The high-level libraries are certainly meant to help get going, but are not meant to stand in the way of you writing directly to the hardware as you would normally.
Here is some basic code I just put together for you to control the ADC directly - please feel free to have a play around:
<cpp>
// analog input p20 (ADC0[5], pin 3) test
- include "mbed.h"
int main() {
// power on, clk divider /4 LPC_SC->PCONP |= (1 << 12); LPC_SC->PCLKSEL0 &= ~(0x3 << 24); // software-controlled ADC settings LPC_ADC->ADCR = (0 << 0) // SEL: 0 = no channels selected | (25 << 8) // CLKDIV: PCLK max ~= 25MHz, /25 to give safe 1MHz | (0 << 16) // BURST: 0 = software control | (0 << 17) // CLKS: not applicable | (1 << 21) // PDN: 1 = operational | (0 << 24) // START: 0 = no start | (0 << 27); // EDGE: not applicable
// setup P1_31 as sel 3 (ADC), mode 2 (no pull) 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;
while(1) { // Select channel and start conversion LPC_ADC->ADCR &= ~0xFF; LPC_ADC->ADCR |= 1 << 5; // ADC0[5] LPC_ADC->ADCR |= 1 << 24; // Repeatedly get the sample data until DONE bit unsigned int data; do { data = LPC_ADC->ADGDR; } while ((data & ((unsigned int)1 << 31)) == 0);
// Stop conversion LPC_ADC->ADCR &= ~(1 << 24); printf("0x%3X\n", data); }
} </cpp>
topic 131 page2
- http://mbed.org/forum/mbed/topic/131/?page=2
- Check word "V29"
- AnalogIn is speed-improved and a media-filter is added
- mbed Library V29
- http://mbed.org/projects/libraries-testing/svn/beta
- If you want to test the library, just delete the "mbed" library amd choose "Import Library..." with the URL as http://mbed.org/projects/libraries-testing/svn/beta
- http://mbed.org/projects/libraries-testing/svn/beta/AnalogIn.h
- http://mbed.org/projects/libraries-testing/svn/beta
- Ferrite beads
- wikipedia Median filter
- Check word "V29"
topic 2849 Analog In
- http://mbed.org/forum/mbed/topic/2849/?page=1#comment-14726
- user Igor Martinovski # 23 Nov 2011
- The beta has done two things for AnalogIn:
- 1) Sampling rate has been increased (i.e. reads are up to 3 times faster now)
- 2) Each read() takes 3 consecutive samples and outputs the middle one. It does all this in a signle read read().
- I don't know if this will be 'fast' enough for you. It depends on the project requirements. But you should see a big improvement on both fronts.
- Let us know how it goes :)
- The beta has done two things for AnalogIn:
topic 2919 spikes again
- ...ends with...I see a significant improvement in reading stability with the V29 Library. Thanks to everyone who worked on this...
topic 2871 Increasing Sampling Frequency
- ...V29 AnalogIn...the final sampling rate is 49.8khz without any changes! :)
Hiroshi Yamaguchi, AnalogIn2 Library and Program
- AnalogIn2 library
- HIGHLIGHTS:
- Uses Median Filter
AnalogIn2(median-window-size)
- Based on Simon Ford code from topic 131
- Sort algorithmn from "Numerical recipes in C"
- Page 455 of edition III of book "Numerical recipes in C", section 8.5 Selecting the Mth Largest, note by NX
- Page 365 of edition 2 of book "Numerical recipes in C", section 8.5 Selecting the Mth Largest, note by NX
- Good implementation
- Easy to understand code
- Control with
START
andDONE
bits, no Burst mode
- Uses Median Filter
- program Imported to compiler
<cpp> /* AnalogIn2.cpp */
short int quickSelect(unsigned short arr[], int n); AnalogIn2::AnalogIn2(PinName pinName) : pinName(pinName) { switch (pinName) { case p15: channel = 0; break; case p16: channel = 1; break; case p17: channel = 2; break; case p18: channel = 3; break; case p19: channel = 4; break; case p20: channel = 5; break; } read_u16(1); } unsigned short AnalogIn2::read_u16(int nSamples) { // power on, clk divider /4 LPC_SC->PCONP |= (1 << 12); LPC_SC->PCLKSEL0 &= ~(0x3 << 24); // software-controlled ADC settings LPC_ADC->ADCR = (0 << 0) // SEL: 0 = no channels selected |
(25 << 8) // CLKDIV: PCLK max ~= 25MHz, /25 to give safe 1MHz | (0 << 16) // BURST: 0 = software control | (0 << 17) // CLKS: not applicable | (1 << 21) // PDN: 1 = operational | (0 << 24) // START: 0 = no start | (0 << 27); // EDGE: not applicable
switch (pinName) { 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; } // Repeatedly get the sample data until DONE bit unsigned short a[nSamples]; for (int i = 0; i < nSamples; i++) { unsigned int data; // Select channel and start conversion LPC_ADC->ADCR &= ~0xFF; LPC_ADC->ADCR |= 1 << channel; LPC_ADC->ADCR |= 1 << 24; do { data = LPC_ADC->ADGDR; } while ((data & ((unsigned int)1 << 31)) == 0); // Stop conversion LPC_ADC->ADCR &= ~(1 << 24); a[i] = data & 65535; } return quickSelect(a, nSamples); } float AnalogIn2::read(int nSamples) { return (read_u16(nSamples) >> 4) / 4096.0; } AnalogIn2::operator float() { return read(); } /* * This Quickselect routine is based on the algorithm described in * "Numerical recipes in C", Second Edition, * Cambridge University Press, 1992, Section 8.5, ISBN 0-521-43108-5 * This code by Nicolas Devillard - 1998. Public domain. */ /* Page 455 of edition III of the above book, section 8.5 Selecting the Mth Largest, note by NX Page 365 of edition 2 of the book, section 8.5 Selecting the Mth Largest, note by NX */
short int quickSelect(unsigned short arr[], int n) { int low = 0, high = n - 1; unsigned int median = (low + high) / 2; for (;;) { if (high <= low) /* One element only */ return arr[median]; if (high == low + 1) { /* Two elements only */ if (arr[low] > arr[high]) SWAP(arr[low], arr[high]); return arr[median]; } /* Find median of low, middle and high items; swap into position low */ int middle = (low + high) / 2; if (arr[middle] > arr[high]) SWAP(arr[middle], arr[high]); if (arr[low] > arr[high]) SWAP(arr[low], arr[high]); if (arr[middle] > arr[low]) SWAP(arr[middle], arr[low]); /* Swap low item (now in position middle) into position (low + 1) */ SWAP(arr[middle], arr[low + 1]); /* Nibble from each end towards middle, swapping items when stuck */ int ll = low + 1; int hh = high; for (;;) { do { ll++; } while (arr[low] > arr[ll]); do { hh--; } while (arr[hh] > arr[low]); if (hh < ll) break; SWAP(arr[ll], arr[hh]); } /* Swap middle item (in position low) back into correct position */ SWAP(arr[low], arr[hh]); /* Re-set active partition */ if (hh <= median) low = ll; if (hh >= median) high = hh - 1; } } </cpp> |
<cpp> /* AnalogIn2.h */
class AnalogIn2 { public: AnalogIn2(PinName pinName); float read(int nSamples = 10); unsigned short read_u16(int nSamples = 10); operator float(); private: PinName pinName; int channel; };
</cpp> |
<cpp> /* main.cpp */
AnalogIn2 a20(p20); AnalogIn2 a15(p15); int main() { printf("Temperature AnalogIn2 test\n"); printf("a15 %d, %f\n", a15.read_u16(), (3.3 * a15.read() - 0.424) / 0.00625); printf("a20 %d, %f\n", a20.read_u16(100), (3.3 * a20.read(100) - 0.424) / 0.00625); wait(1); } </cpp> |
Simon Blandford ADC Library
- http://mbed.org/users/simonb/programs/ADC_test/5zlnn
- Used in h6 project
- In START-DONE mode
- Response is 9μs for every ADC-channel
- 9μs --> effective sampling rate 111 KHz with below settings:
- Response is 9μs for every ADC-channel
- In START-DONE mode
<cpp>
ADC adc(SAMPLE_RATE, 1); //Initialise ADC to maximum SAMPLE_RATE and cclk divide set to 1 </cpp> |
ADC_test program, tested in Compiler
<cpp> /*adc.cpp*/ /* mbed Library - ADC * Copyright (c) 2010, sblandford * released under MIT license http://mbed.org/licence/mit */
ADC::ADC(int sample_rate, int cclk_div) { int i, adc_clk_freq, pclk, clock_div, max_div=1; //Work out CCLK 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; //Power up the ADC LPC_SC->PCONP |= (1 << 12); //Set clock at cclk / 1. 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; } pclk = cclk / cclk_div; 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; } _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); 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 ) | //Clkdiv ( 1 << 21 ); //A/D operational //Default no channels enabled LPC_ADC->ADCR &= ~0xFF; //Default NULL global custom isr _adc_g_isr = NULL; //Initialize arrays for (i=7; i>=0; i--) { _adc_data[i] = 0; _adc_isr[i] = NULL; }
//* Attach IRQ instance = this; NVIC_SetVector(ADC_IRQn, (uint32_t)&_adcisr); //Disable global interrupt LPC_ADC->ADINTEN &= ~0x100; }; void ADC::_adcisr(void) { instance->adcisr(); }
uint32_t stat; int chan; // Read status 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; // Channel that triggered interrupt chan = (LPC_ADC->ADGDR >> 24) & 0x07; //User defined interrupt handlers if (_adc_isr[chan] != NULL) _adc_isr[chan](_adc_data[chan]); if (_adc_g_isr != NULL) _adc_g_isr(chan, _adc_data[chan]); return; } int ADC::_pin_to_channel(PinName pin) { int chan; switch (pin) { case p15://=p0.23 of LPC1768 default: chan=0; break; case p16://=p0.24 of LPC1768 chan=1; break; case p17://=p0.25 of LPC1768 chan=2; break; case p18://=p0.26 of LPC1768 chan=3; break; case p19://=p1.30 of LPC1768 chan=4; break; case p20://=p1.31 of LPC1768 chan=5; break; } return(chan); } PinName ADC::channel_to_pin(int chan) { const PinName pin[8]={p15, p16, p17, p18, p19, p20, p15, p15}; if ((chan < 0) || (chan > 5)) fprintf(stderr, "ADC channel %u is outside range available to MBED pins.\n", chan); return(pin[chan & 0x07]); }
const int pin[8]={15, 16, 17, 18, 19, 20, 0, 0}; if ((chan < 0) || (chan > 5)) fprintf(stderr, "ADC channel %u is outside range available to MBED pins.\n", chan); return(pin[chan & 0x07]); }
//If in burst mode and at least one interrupt enabled then //take all values from _adc_data if (burst() && (LPC_ADC->ADINTEN & 0x3F)) { return(_adc_data[_pin_to_channel(pin)]); } else { //Return current register value or last value from interrupt switch (pin) { case p15://=p0.23 of LPC1768 default: 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); } } } //Enable or disable an ADC pin void ADC::setup(PinName pin, int state) { int chan; chan=_pin_to_channel(pin); if ((state & 1) == 1) { switch(pin) { case p15://=p0.23 of LPC1768 default: 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; } //Only one channel can be selected at a time if not in burst mode if (!burst()) LPC_ADC->ADCR &= ~0xFF; //Select channel LPC_ADC->ADCR |= (1 << chan); } else { switch(pin) { case p15://=p0.23 of LPC1768 default: 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; } LPC_ADC->ADCR &= ~(1 << chan); } } //Return channel enabled/disabled state int ADC::setup(PinName pin) { int chan; chan = _pin_to_channel(pin); return((LPC_ADC->ADCR & (1 << chan)) >> chan); } //Select channel already setup void ADC::select(PinName pin) { int chan; //Only one channel can be selected at a time if not in burst mode if (!burst()) LPC_ADC->ADCR &= ~0xFF; //Select channel chan = _pin_to_channel(pin); LPC_ADC->ADCR |= (1 << chan); } //Enable or disable burst mode void ADC::burst(int state) { if ((state & 1) == 1) { if (startmode(0) != 0) fprintf(stderr, "Warning. startmode is %u. Must be 0 for burst mode.\n", startmode(0)); LPC_ADC->ADCR |= (1 << 16); } else LPC_ADC->ADCR &= ~(1 << 16); } //Return burst mode state int ADC::burst(void) { return((LPC_ADC->ADCR & (1 << 16)) >> 16); } //Set startmode and edge void ADC::startmode(int mode, int edge) { int lpc_adc_temp; //Reset start mode and edge bit, lpc_adc_temp = LPC_ADC->ADCR & ~(0x0F << 24); //Write with new values lpc_adc_temp |= ((mode & 7) << 24) | ((edge & 1) << 27); LPC_ADC->ADCR = lpc_adc_temp; } //Return startmode state according to mode_edge=0: mode and mode_edge=1: edge int ADC::startmode(int mode_edge){ switch (mode_edge) { case 0: default: return((LPC_ADC->ADCR >> 24) & 0x07); case 1: return((LPC_ADC->ADCR >> 27) & 0x01); } } //Start ADC conversion void ADC::start(void) { startmode(1,0); }
int chan; chan = _pin_to_channel(pin); if (state == 1) { LPC_ADC->ADINTEN &= ~0x100; LPC_ADC->ADINTEN |= 1 << chan; /* Enable the ADC Interrupt */ NVIC_EnableIRQ(ADC_IRQn); } else { LPC_ADC->ADINTEN &= ~( 1 << chan ); //Disable interrrupt if no active pins left if ((LPC_ADC->ADINTEN & 0xFF) == 0) NVIC_DisableIRQ(ADC_IRQn); } } //Return enable/disable state of interrupt for pin int ADC::interrupt_state(PinName pin) { int chan; chan = _pin_to_channel(pin); return((LPC_ADC->ADINTEN >> chan) & 0x01); }
//* Attach IRQ NVIC_SetVector(ADC_IRQn, (uint32_t)fptr); } //Restore default interrupt handler void ADC::detach(void) { //* Attach IRQ instance = this; NVIC_SetVector(ADC_IRQn, (uint32_t)&_adcisr); }
int chan; chan = _pin_to_channel(pin); _adc_isr[chan] = fptr; } //Append interrupt handler for pin to function isr void ADC::unappend(PinName pin) { int chan; chan = _pin_to_channel(pin); _adc_isr[chan] = NULL; } //Unappend global interrupt handler to function isr void ADC::append(void(*fptr)(int chan, uint32_t value)) { _adc_g_isr = fptr; } //Detach global interrupt handler to function isr void ADC::unappend() { _adc_g_isr = NULL; } //Set ADC offset void offset(int offset) { LPC_ADC->ADTRM &= ~(0x07 << 4); LPC_ADC->ADTRM |= (offset & 0x07) << 4; } //Return current ADC offset int offset(void) { return((LPC_ADC->ADTRM >> 4) & 0x07); } //Return value of ADC on pin int ADC::read(PinName pin) { //Reset DONE and OVERRUN flags of interrupt handled ADC data _adc_data[_pin_to_channel(pin)] &= ~(((uint32_t)0x01 << 31) | ((uint32_t)0x01 << 30)); //Return value return((_data_of_pin(pin) >> 4) & 0xFFF); } //Return DONE flag of ADC on pin int ADC::done(PinName pin) { return((_data_of_pin(pin) >> 31) & 0x01); } //Return OVERRUN flag of ADC on pin int ADC::overrun(PinName pin) { return((_data_of_pin(pin) >> 30) & 0x01); } int ADC::actual_adc_clock(void) { return(_adc_clk_freq); } int ADC::actual_sample_rate(void) { return(_adc_clk_freq / CLKS_PER_SAMPLE); } </cpp>
|
<cpp> /*adc.h*/ /* mbed Library - ADC * Copyright (c) 2010, sblandford * released under MIT license http://mbed.org/licence/mit */
class ADC { public: //Initialize ADC with ADC maximum sample rate of //sample_rate and system clock divider of cclk_div //Maximum recommened sample rate is 184000 ADC(int sample_rate, int cclk_div); //Enable/disable ADC on pin according to state //and also select/de-select for next conversion void setup(PinName pin, int state); //Return enabled/disabled state of ADC on pin int setup(PinName pin); //Enable/disable burst mode according to state void burst(int state); //Select channel already setup void select(PinName pin); //Return burst mode enabled/disabled int burst(void); /*Set start condition and edge according to mode: 0 - No start (this value should be used when clearing PDN to 0). 1 - Start conversion now. 2 - Start conversion when the edge selected by bit 27 occurs on the P2.10 / EINT0 / NMI pin. 3 - Start conversion when the edge selected by bit 27 occurs on the P1.27 / CLKOUT / USB_OVRCRn / CAP0.1 pin. 4 - Start conversion when the edge selected by bit 27 occurs on MAT0.1. Note that this does not require that the MAT0.1 function appear on a device pin. 5 - Start conversion when the edge selected by bit 27 occurs on MAT0.3. Note that it is not possible to cause the MAT0.3 function to appear on a device pin. 6 - Start conversion when the edge selected by bit 27 occurs on MAT1.0. Note that this does not require that the MAT1.0 function appear on a device pin. 7 - Start conversion when the edge selected by bit 27 occurs on MAT1.1. Note that this does not require that the MAT1.1 function appear on a device pin. When mode >= 2, conversion is triggered by edge: 0 - Rising edge 1 - Falling edge */ void startmode(int mode, int edge); //Return startmode state according to mode_edge=0: mode and mode_edge=1: edge int startmode(int mode_edge); //Start ADC conversion void start(void); //Set interrupt enable/disable for pin to state void interrupt_state(PinName pin, int state); //Return enable/disable state of interrupt for pin int interrupt_state(PinName pin); //Attach custom interrupt handler replacing default void attach(void(*fptr)(void)); //Restore default interrupt handler void detach(void); //Append custom interrupt handler for pin void append(PinName pin, void(*fptr)(uint32_t value)); //Unappend custom interrupt handler for pin void unappend(PinName pin); //Append custom global interrupt handler void append(void(*fptr)(int chan, uint32_t value)); //Unappend custom global interrupt handler void unappend(void); //Set ADC offset to a value 0-7 void offset(int offset); //Return current ADC offset int offset(void); //Return value of ADC on pin int read(PinName pin); //Return DONE flag of ADC on pin int done(PinName pin); //Return OVERRUN flag of ADC on pin int overrun(PinName pin); //Return actual ADC clock int actual_adc_clock(void); //Return actual maximum sample rate int actual_sample_rate(void); //Return pin ID of ADC channel PinName channel_to_pin(int chan); //Return pin number of ADC channel int channel_to_pin_number(int chan);
int _pin_to_channel(PinName pin); uint32_t _data_of_pin(PinName pin); int _adc_clk_freq; void adcisr(void); static void _adcisr(void); static ADC *instance; uint32_t _adc_data[8]; void(*_adc_isr[8])(uint32_t value); void(*_adc_g_isr)(int chan, uint32_t value); void(*_adc_m_isr)(void); };
</cpp> |
<cpp> /*main.cpp*/
DigitalOut int_led(p19); //Initialise ADC to maximum SAMPLE_RATE and cclk divide set to 1 ADC adc(SAMPLE_RATE, 1); //Toggle LED on interrupt void led_toggle(int chan, uint32_t value) { int_led = !int_led; } //Report ADC value on interrupt void print_value(int chan, uint32_t value) { printf("ADC interrupt on pin %u, value=%04u.\n", adc.channel_to_pin_number(chan), (value >> 4) & 0xFFF); }
int i; printf("Requested max sample rate is %u, actual max sample rate is %u.\n", SAMPLE_RATE, adc.actual_sample_rate()); while (1) { adc.setup(p17,1); //Set up ADC on pin 17 adc.setup(p15,1); //Set up ADC on pin 15 wait(1); adc.select(p17); //Measure pin 17 adc.start(); //Start ADC conversion while(!adc.done(p17)); //Wait for it to complete printf("Measured value on pin 17 is %04u.\n", adc.read(p17)); wait(1); adc.select(p15); //Measure pin 15 adc.start(); //Start ADC conversion while(!adc.done(p15)); //Wait for it to complete printf("Measured value on pin 15 is %04u.\n", adc.read(p15)); wait(1); adc.append(print_value); //Append an interrupt handler that prints the channel and value adc.select(p17); //Measure pin 17 adc.interrupt_state(p20,1); //Enable the interrupt adc.start(); //Start ADC conversion while(!adc.done(p17)); //Wait for it to complete wait(1); adc.setup(p17,0); //Unset pin 17 //Togle LED on each converstion. //Should be 12.5KHz on LED for all 6 pins. //Sample rate=150KHz / 6 channels / 2 adc.append(led_toggle); //Prepare for burst mode on all ADC pins adc.startmode(0,0); adc.burst(1); adc.setup(p20,1); adc.setup(p19,1); adc.setup(p18,1); adc.setup(p17,1); adc.setup(p16,1); adc.setup(p15,1); //For burst mode, only one interrupt is required //which can be on any enabled pin. We have enabled all //of them here. adc.interrupt_state(p15,1); printf("Burst mode, printing once per second...\n"); for (i=0; i<5; i++) { printf("%04u %04u %04u %04u %04u %04u\n", adc.read(p20), adc.read(p19), adc.read(p18), adc.read(p17), adc.read(p16), adc.read(p15)); wait(1); } adc.burst(0); adc.setup(p20,0); adc.setup(p19,0); adc.setup(p18,0); adc.setup(p17,0); adc.setup(p16,0); adc.setup(p15,0); adc.interrupt_state(p20,0); adc.interrupt_state(p19,0); adc.interrupt_state(p18,0); adc.interrupt_state(p17,0); adc.interrupt_state(p16,0); adc.interrupt_state(p15,0); printf("\n"); } } </cpp> |
AD7190 with mbed