This code is to collect data from the ADCs in burst mode, decimate the data, encapsulate it in a simple UDP-like packet and then transmit it over the serial port.
Diff: daq.cpp
- Revision:
- 0:03e8a03052c9
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/daq.cpp Fri Aug 27 15:20:30 2010 +0000 @@ -0,0 +1,144 @@ +#include "mbed.h" +#include "daq.h" + +/** + * A class to implement BURST mode sampling of multiple ADCs + */ + +ADC_BURST *ADC_BURST::instance; + +/** + * Constructor for BURST mode. + * ADCs in use are on mbed pins 15-17 and 19 + * These equate to ADCs + */ + +ADC_BURST::ADC_BURST () { + // Make this instance available for pointing to privately + instance = this; + // Power up the ADC + LPC_SC->PCONP |= (1 << 12); + // Set up the pins + // DIP15:17 = ADC0:2 + LPC_PINCON->PINSEL1 &= ~((uint32_t)0x3F << 14); + LPC_PINCON->PINSEL1 |= ((uint32_t)0x15 << 14); + // DIP19 = ADC4 + LPC_PINCON->PINSEL3 |= ((uint32_t)0x3 << 28); + // Tri-state, no-pull + LPC_PINCON->PINMODE1 &= ~((uint32_t)0x3F << 14); + LPC_PINCON->PINMODE1 |= ((uint32_t)0x2A << 14); + LPC_PINCON->PINMODE3 &= ~((uint32_t)0x3 << 28); + LPC_PINCON->PINMODE3 |= ((uint32_t)0x2 << 28); + // Set the clock divider to 256 (96e6/4/24 = 1e6) [LPC_SC->PCLKSEL0:PCLK_ADC = x20 by default] + LPC_ADC->ADCR = (255 << 8); + // Select the ADC channel + LPC_ADC->ADCR &= ~0xFF; + LPC_ADC->ADCR |= 0x17; // ADC0:2,4 - DIP15:17,19 + // Attach a function to the interrupt vector table + NVIC_SetVector(ADC_IRQn, (uint32_t)&_burst_isr); + NVIC_EnableIRQ(ADC_IRQn); + // Enable interrupts + LPC_ADC->ADINTEN = 0x17; // Interrupt on channels 0:2,4 + attached_ = false; +} + +/** + * Attach a function to the interrupt service routine + * + * @param inptr Function pointer passed from main code. + */ + +void ADC_BURST::attach (FuncPtr inptr) { + isr_pointer_ = inptr; + // Power up the ADC circuitry + LPC_ADC->ADCR |= (1 << 21); + // Enable BURST mode to start continuous conversions + LPC_ADC->ADCR |= (1 << 16); + attached_ = true; +} + +void ADC_BURST::burst_isr () { + // CanNOT read from global ADC interrupt register - otherwise interrupt is NOT CLEARED + if ((LPC_ADC->ADSTAT & 0xFF00) != 0) + // Overflow! + error("ADSTAT: %x\n", LPC_ADC->ADSTAT); + + uint32_t chan = (LPC_ADC->ADGDR >> 24) & 0xF; + switch (chan) { + case 0: + data[0] = (LPC_ADC->ADDR0 & 0x0000FFF0); + break; + case 1: + data[1] = (LPC_ADC->ADDR1 & 0x0000FFF0); + break; + case 2: + data[2] = (LPC_ADC->ADDR2 & 0x0000FFF0); + break; + case 4: + data[3] = (LPC_ADC->ADDR4 & 0x0000FFF0); + if (attached_) (*isr_pointer_)(); + break; + default: + error("Random channel interrupt: %d!\n",chan); + } +} + +void ADC_BURST::_burst_isr() { + instance->burst_isr(); +} + +ADC_BURST::~ADC_BURST () { + // Kill ADC + LPC_ADC->ADINTEN = 0; + LPC_SC->PCONP &= ~(1 << 12); +} + +/** + * Decimator class to implement simple low-pass filtering on uC + */ + +/** + * Decimator constructor. + * Constructor to zero circular decimation buffer and pointers. + * As init state is zero accumulator is safe from underflow on subtraction. + */ +Decimator::Decimator (uint8_t order) { + decimation_pointer_ = 0; // Initialise the buffer pointer + decimation_order_ = (order > 5) ? 5: order; // Limit the order to 5 + decimation_length_ = 1 << decimation_order_; + // Clear the buffer + for (uint8_t i=0; i<decimation_length_; i++) { + decimation_buffer_[i] = 0; + } + accumulator_ = 0; +} + +/** + * Data input function + * Write a new value into the decimator. + * Input is unsigned 16bits, max decimation length is 32 (5bits) + * so 32 bit accumulator is safe from overflow + */ +void Decimator::write (uint16_t in_sample) { + // Remove oldest sample from accumulator + accumulator_ -= decimation_buffer_[decimation_pointer_]; + // Write in the newest sample + decimation_buffer_[decimation_pointer_] = in_sample; + accumulator_ += in_sample; + // Increment the pointer + decimation_pointer_++; + if (decimation_pointer_ >= decimation_length_) + decimation_pointer_ = 0; +} + +/** + * Data output function + * Read the decimated signal + */ +uint16_t Decimator::read() { + // Just return the accumulator. Don't bother with any down scaling + uint32_t output = accumulator_ >> decimation_order_; + if ((accumulator_ & (decimation_length_>>1)) != 0) + output++; + return (uint16_t)output; +} \ No newline at end of file