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.
Revision 0:03e8a03052c9, committed 2010-08-27
- Comitter:
- jimurai
- Date:
- Fri Aug 27 15:20:30 2010 +0000
- Commit message:
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/comms.cpp Fri Aug 27 15:20:30 2010 +0000 @@ -0,0 +1,86 @@ +#include "mbed.h" +#include "comms.h" + +void le2be_u16(uint8_t* buffer, uint16_t in_word) { + *buffer++ = (uint8_t)((in_word & 0xFF00) >> 8); + *buffer = (uint8_t)(in_word & 0x00FF); +} + +/** + * This function is used to calculate a one's complement based checksum + * as is commonly used in IP based protocols + */ +uint16_t chksum_u16(uint32_t in_word) { + uint32_t temp = in_word & 0x0000FFFF; + temp += (in_word & 0xFFFF0000)>>16; + return (uint16_t)( (temp & 0x0000FFFF) + ((temp & 0xFFFF0000)>>16) ); +} + +/** + * Transport constructor. + * Set up the pseudo header and packet to be used in communications + */ +Transport::Transport (uint32_t src_addr, uint32_t dst_addr, uint32_t ID) { + // Set up IPv4 pseudo header for checksum calculation + pseudo_header_.src_addr = src_addr; + pseudo_header_.dst_addr = dst_addr; + pseudo_header_.protocol = PROTOCOL; + pseudo_header_.length = sizeof(header_); + // Set up default ports + header_.src_port = PORT_BASE + ID; + header_.dst_port = PORT_BASE; + header_.length = sizeof(header_); + // Calculate checksum of the pseudo-header: this should save some future recalculation + uint32_t w_checksum = chksum_u16(src_addr); + w_checksum = chksum_u16(dst_addr+w_checksum); + w_checksum = chksum_u16(pseudo_header_.protocol+w_checksum); + pseudo_header_.checksum = chksum_u16(pseudo_header_.length+w_checksum); + // Now calculate checksum of the UDP packet header + pseudo-header + w_checksum = chksum_u16(header_.src_port+pseudo_header_.checksum); + w_checksum = chksum_u16(header_.dst_port+w_checksum); + header_.checksum = chksum_u16(header_.length+w_checksum); + // Load up the packet + le2be_u16((packet_+0),header_.src_port); + le2be_u16((packet_+2),header_.dst_port); + le2be_u16((packet_+4),header_.length); + le2be_u16((packet_+6),header_.checksum); +} + +/** + * Provides a pointer to a buffer containing a formatted packet and + * the total length of said packet. + */ +void Transport::get_packet (uint8_t** buffer, uint16_t* length) { + *buffer = packet_; + *length = header_.length; +} + +/** + * Loads the transport packet with an array of 16bit samples. + * This will require translating the words into big-endian. + */ +void Transport::load_data (uint16_t* buffer, uint16_t length) { + uint8_t* pkt_ptr = packet_ + sizeof(header_); + for (uint16_t i=0; i<length; i++) { + le2be_u16(pkt_ptr,*buffer++); + pkt_ptr += 2; + } + // Set the header length (in octets/bytes) + header_.length = sizeof(header_) + (length<<1); + le2be_u16((packet_+4),header_.length); + // For now set the checksum to IGNORE + header_.checksum = 0x0000; + le2be_u16((packet_+6),header_.checksum); +} + +/** + * Set the destination port. + */ +void Transport::set_dst_port (uint16_t port) { + // Store the port both in the local struct... + header_.dst_port = port; + // ...and in the transmission packet + le2be_u16((packet_+2),port); + //packet_[2] = (uint8_t)((port & 0xFF00) >> 8); + //packet_[3] = (uint8_t)(port & 0x00FF); +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/comms.h Fri Aug 27 15:20:30 2010 +0000 @@ -0,0 +1,53 @@ +/** + * @file comms.h + * Header file for comms.cpp + * A simplistic communication profile for streaming data sampled + * from sensors over UART->radio->UART->dislpay + * + * @brief Header file for comms.cpp + * + * @author James A C Patterson + */ +#ifndef COMMS_H +#define COMMS_H + +#define MAX_PACKET_SIZE 64 + +#define PORT_BASE (1<<13) +#define PROTOCOL 17 + +typedef struct header { + uint16_t src_port; + uint16_t dst_port; + uint16_t length; + uint16_t checksum; +} header_t; + + +typedef uint8_t packet_t[MAX_PACKET_SIZE]; + +typedef struct pseudo_header { + uint32_t src_addr; + uint32_t dst_addr; + uint16_t protocol; + uint16_t length; + uint16_t checksum; +} pseudo_header_t; + +/** + * A class to implement a basic transport layer that is based very + * loosely on UDP/UDP-lite + */ +class Transport { +public: + Transport (uint32_t, uint32_t, uint32_t); + void get_packet (uint8_t**,uint16_t*); + void load_data (uint16_t*,uint16_t); + void set_dst_port (uint16_t); +private: + pseudo_header_t pseudo_header_; + header_t header_; + packet_t packet_; +}; + +#endif \ No newline at end of file
--- /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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/daq.h Fri Aug 27 15:20:30 2010 +0000 @@ -0,0 +1,49 @@ +/** + * @file daq.h + * Header file for daq.cpp - a data acquisition system capable of + * sampling from multiple ADCs in BURST mode to allow consistent, + * high-rate sampling. The samples are passed to a bank of decimators + * to allow the samples to be synchronised with another, slower + * sampling clock or other sampling sources/ + * + * @brief Header file for daq.cpp + * + * @author James A C Patterson + */ +#ifndef DAQ_H +#define DAQ_H + +typedef void (*FuncPtr)(void); + +class ADC_BURST { +public: + ADC_BURST (); + ~ADC_BURST (); + void attach(FuncPtr); + void burst_isr(void); + uint32_t data[4]; +private: + FuncPtr isr_pointer_; + bool attached_; + static ADC_BURST *instance; + static void _burst_isr(void); +}; + +/** + * Decimator class to implement simple low-pass filtering on uC + * Unsigned integer decimator. Depth up to 32 samples. + * Currently just behaves as a uniform weighted FIR + */ +class Decimator { +public: + Decimator (uint8_t); + void write (uint16_t); + uint16_t read (); +private: + uint8_t decimation_order_; + uint8_t decimation_length_; + uint8_t decimation_pointer_; + uint16_t decimation_buffer_[32]; + uint32_t accumulator_; +}; +#endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Fri Aug 27 15:20:30 2010 +0000 @@ -0,0 +1,128 @@ +#include "mbed.h" +#include "daq.h" +#include "comms.h" + +#define NODE_ID 1 +#define SAMPLE_PERIOD_US 20000 +#define EXT_BIAS_VOLTAGE 0.1 + +DigitalOut myled(LED1); +Serial pc(USBTX, USBRX); +Serial radioUART(p13,p14); +DigitalOut rst(p29); +DigitalOut en_mux_EXT(p29); +BusOut mux_EXT(p23,p24); +AnalogOut bias_EXT(p18); + +ADC_BURST adc; +Decimator dec_0(5); +Decimator dec_1(5); +Decimator dec_2(5); +Decimator dec_3_0(5); +Decimator dec_3_1(5); +Decimator dec_3_2(5); +Decimator dec_3_3(5); +Ticker sampler; + +Transport transmission(0xC0A80001, 0xC0A80000, NODE_ID); + +uint8_t* radio_ptr = NULL; +uint16_t radio_len = 0; + +uint8_t ext_mux_channel = 0; +bool waiting = false; + +/** + * Collects sample from multiple sensor sources. + * Create a packets of samples from multiple sensor and pack + * them into a single, synchronised (via decimators) packet. + */ +void pack_samples_ISR() { + uint16_t syncd_samples[7]; + syncd_samples[0] = dec_0.read(); + syncd_samples[1] = dec_1.read(); + syncd_samples[2] = dec_2.read(); + syncd_samples[3] = dec_3_0.read(); + syncd_samples[4] = dec_3_1.read(); + syncd_samples[5] = dec_3_2.read(); + syncd_samples[6] = dec_3_3.read(); + transmission.load_data(syncd_samples, 7); + waiting = true; +} + +void adc_ISR() { + // Toggle a LED for visual feedback of activity + myled = !myled; + // Send the latest set of samples to the decimators + dec_0.write((uint16_t)(adc.data[0] >> 4)); + dec_1.write((uint16_t)(adc.data[1] >> 4)); + dec_2.write((uint16_t)(adc.data[2] >> 4)); + switch (ext_mux_channel) { + case 0: + dec_3_0.write((uint16_t)(adc.data[3] >> 4)); + break; + case 1: + dec_3_1.write((uint16_t)(adc.data[3] >> 4)); + break; + case 2: + dec_3_2.write((uint16_t)(adc.data[3] >> 4)); + break; + case 3: + dec_3_3.write((uint16_t)(adc.data[3] >> 4)); + break; + default: + error("Random multiplexer channel: %d!\n",ext_mux_channel); + } + // Select the next channel on the external multiplexer + if (++ext_mux_channel == 4) ext_mux_channel = 0; + mux_EXT = ext_mux_channel; +} + +int main() { + // Set up UART interface over USB + pc.baud(115200); + + // Set up UART interface to radio module node + // - CTS1 is set to DIP12 = port0.17 + LPC_PINCON->PINSEL1 &= ~((uint32_t)0x3 << 2); + LPC_PINCON->PINSEL1 |= ((uint32_t)0x1 << 2); + // - CTS1 is given a weak pull-up + LPC_PINCON->PINMODE1 &= ~((uint32_t)0x3 << 2); + // - Enable autocts mode on UART1 + LPC_UART1->MCR |= (1 <<7 ); + // - Baud rate set fairly high + radioUART.baud(115200); + + // Reset the radio board and wait a while for it to boot + rst = 0; + rst = 1; + wait(0.25); + + // Attach a function to the sample period ticker + // - this will enable continuous sampling of the ADC + sampler.attach_us(&pack_samples_ISR, SAMPLE_PERIOD_US); + + // Set up the external analogue circuitry + bias_EXT = EXT_BIAS_VOLTAGE; + en_mux_EXT = 1; + mux_EXT = ext_mux_channel; + + // Set up any remaining comm's + + // Choose which function to process the latest ADC values + adc.attach(&adc_ISR); + + + while (1) { + if (waiting) { + + transmission.get_packet(&radio_ptr, &radio_len); + //radioUART.attach(&uartISR,Serial::TxIrq); + while (radio_len-- != 0) { + radioUART.putc(*radio_ptr++); + } + pc.printf("C = %d\n",radio_len); + waiting = false; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Fri Aug 27 15:20:30 2010 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/mbed/builds/9114680c05da