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.

Dependencies:   mbed

Files at this revision

API Documentation at this revision

Comitter:
jimurai
Date:
Fri Aug 27 15:20:30 2010 +0000
Commit message:

Changed in this revision

comms.cpp Show annotated file Show diff for this revision Revisions of this file
comms.h Show annotated file Show diff for this revision Revisions of this file
daq.cpp Show annotated file Show diff for this revision Revisions of this file
daq.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
--- /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