DSP program for the Surfboard hardware (PCB to be open sourced) http://www.avbotz.com/ourauv/electrical/signal-processing/

Dependencies:   MODDMA SimpleIOMacros mbed-dsp mbed

surfboard.cpp

Committer:
avbotz
Date:
2013-08-02
Revision:
0:2381a319fc35

File content as of revision 0:2381a319fc35:

// I think we should move this to the offline compiler so we can get loop unrolling. -- Kevin
// TODO: code size is small (4% of available space) so we should totally do loop unrolling

#include "surfboard.h"

// Might have to use IO Macros library for fast digital I/O http://mbed.org/users/AjK/code/SimpleIOMacros/
// less abstracted InterruptIn http://mbed.org/forum/mbed/topic/2326/
//InterruptIn drdy(p8); // active low
// interruptin too slow http://mbed.org/forum/mbed/topic/2326/
DigitalOut sync(p14); // active low

//Serial submarine(/*tx*/ p28, /*rx*/ p27);
Serial submarine(USBTX, USBRX);
// From sub to hydrophone board:
// r = start running
// s = stop running
// From hydrophone board to sub:
// h = hello world
// b = turned off because battery low

DigitalOut avdd_ctl(p15);

AnalogIn abat_sense(p16);
// We don't want the battery to go below 6 V. 
// TODO: read the battery after each IIR batch?
// Calculate what the minimum battery voltage should be after it goes through
// the voltage divider and mbed AnalogIn ADC.
#define ABAT_RATIO ((2.21f / 6.95f) / 3.3f)
#define ABAT_THRESH_LO (ABAT_RATIO * 6.0f)
#define ABAT_THRESH_HI (ABAT_RATIO * 6.5f)
Ticker abat_tick;

// On Surfboard v1.0, setting these does not actually control the power because
// of the modifications on the board.
DigitalOut clkvdd(p9), iovdd(p10);

volatile bool fresh_data = false;

// lights for debugging
// Replacing with SimpleIOMacros.h because each toggle takes ~300 ns (!!)
DigitalOut led1(LED1), // blinking: wait for run command. solid: ready
           led2(LED2), // in function get_data()
           led3(LED3), // toggles when IIR filter is run
           led4(LED4); // toggles on fresh data
// Counts how many times we toggled the LED
uint32_t led1num, led2num, led3num, led4num;
Ticker* led1ticks[2];

int i_timestamps[NUM_CHANNELS];

// For saving received data to disk
LocalFileSystem local("local");
bool log_data_enabled = true;

// Call this function to reset the mbed
extern "C" void mbed_reset();

int main()
{
    pre_setup(); // For safety, this must be the first thing that runs
    
    /*for (int i = 0; i < sizeof(parsed)/sizeof(uint16_t); i++)
    {
        *(((int16_t*)parsed) + i) = 12345;
    }*/
    
    wait(5);
    
    led1ticks[0] = new Ticker();
    led1ticks[1] = new Ticker();
    
    led1 = 1;
    led1ticks[0]->attach(&led1_cb, 1.0);
    wait_ms(60);
    led1ticks[1]->attach(&led1_cb, 1.0);
    led1 = 0;
    
    submarine.baud(115200);
    submarine.putc('h');
    submarine.printf("%d ", sizeof(q15_t));
    
    // Wait until we are told to turn on
    //while (submarine.getc() != 'r');
    
    submarine.attach(&submarine_cb);
    led1ticks[0]->detach();
    led1ticks[1]->detach();
    
    setup();
    
    while (true)
    {
        if (fresh_data)
        {
            fresh_data = false;
            
            LED4_TOGGLE;
            
            if (log_data_enabled /*&& read_blocks > 3*/)
            {
                // Disable DRDY interrupt to prevent the buffer from changing
                // while we save it. Writing to disk takes a long time.
//                drdy.rise(NULL);
                // Only log data once
                log_data_enabled = false;
                
                FILE *fp = fopen("/local/out.csv", "w");
                
                fprintf(fp, "Index,Ch1,Ch2,Ch3,Ch4\n");
                for (int i = 0; i < BLOCK_SIZE; i++)
                {
                    fprintf(fp, "%d", i);
                    for (int j = 0; j < NUM_CHANNELS; j++)
                    {
                        fprintf(fp, ",%02X%02X%02X", parsed[write_block^1][j][i][0], parsed[write_block^1][j][i][1], parsed[write_block^1][j][i][2]);
                        //int16_t n = parsed[write_block^1][j][i];
                        //fprintf(fp, ",%X", n);
                    }
                    fprintf(fp, "\n");
                }
                
                fclose(fp);
                
              //  drdy.rise(&get_data);
            }
            
            /*for (char i = 0; i < NUM_CHANNELS; i++)
            {
                arm_iir_lattice_q15(iir_filter+i, parsed[write_block^1][i], filtered, BLOCK_SIZE);
                i_timestamps[i] = find_timestamp_tdoa(filtered);
            }*/
            // hyperbolic positioning
            // send it to the sub
        }
    }
}

void pre_setup()
{
    
}

int setup()
{
    int retValue = 0;
    retValue += setup_ads1274();
    retValue += setup_dma();
    
    led1 = 1;   // Debug
    
    // Should be at the end of setup function, in case ADS1274 tries to send data
    //drdy.rise(&get_data);
    event_irq_init();
    
    return retValue;
}

// Return values
// 0: setup finished normally
// 1: setup aborted because battery voltage was too low
int setup_ads1274()
{
    // Don't start up if the battery is too low
    abat_cb();
    abat_tick.attach(&abat_cb, 1);
    
    // Sequence the power-on of the ADC
    iovdd = 1;
    __nop();
    avdd_ctl = 1;
    // Give it some time, just in case.
    wait_ms(5);
    // Now we can start driving pins without destroying the ADS1274
    // Turn on sampling clock (CLK)
    clkvdd = 1;
    
    // ADS1274 needs 2^18 clk cycles + 129 conversions before data are valid
    // minimum wait time = 2^18 / f_clk + 129 / f_data = 2^18/27MHz + 129/0.1MHz = 11 ms
    wait_ms(11 + 1);
    
    // Datasheet suggested resetting the ads1274 after start up to synchronize outputs
    // Need to put it low for at least 1 CLK period
    // On mbed, this is 96 MHz/27MHz = 3.556 instruction cycles
    sync = 1;
    // Drive *sync to logic low for about 4 instruction cycles
    sync = 0;
    __nop(); __nop(); __nop(); __nop(); // wait 4 cycles
    sync = 1;
    // Wait 1/f_data = 1/0.1MHz = 10 us for new data to be valid
    wait_us(10 + 1);
    
    return 0; // success
}

void teardown()
{
    // Disable interrupts. Not sure if this is necessary, but I don't want samples to be read while we shut down
    __disable_irq();
    
    teardown_ads1274();
    teardown_dma();
}

void teardown_ads1274()
{
    // Turn off peripherals, then turn off ADS1274
    // Turn off CLK
    clkvdd = 0;
    __nop(); __nop();
    // Turn off op amps and analog power to ADS1274
    avdd_ctl = 0;
    // Turn off IO power
    iovdd = 0;
}

// Called when the submarine sends a message to us
void submarine_cb()
{
    // Loop as long as there are more characters to be read
    while (submarine.readable())
    {
        // If we are told to stop
        if (submarine.getc() == 's')
        {
            // Shut everything down
            teardown();
            // Reset the mbed
            mbed_reset();
        }
    }
}

// Called every few seconds to check the battery status
void abat_cb()
{
    if (abat_sense < ABAT_THRESH_LO)
    {
        // Uh oh, stop the hydrophone board
        teardown();
        
        submarine.putc('b');
        led1 = led2 = led3 = led4 = 0;
        
        // Wait until someone changes the battery
        int num_hi = 0, loop_count = 0;;
        while (num_hi < 100)
        {
            if (abat_sense > ABAT_THRESH_HI)
            {
                num_hi++;
            }
            else if (num_hi > 0)
            {
                num_hi--;
            }
            
            if (loop_count == 32000)
            {
                led1 = led2 = led3 = led4 = !led1;
                loop_count = 0;
            }
            else
            {
                loop_count++;
            }
        }
        
        // On battery replacement, wait for the user to plug the battery in all the way
        wait_ms(3000);
        
        mbed_reset();
    }
}

void led1_cb()
{
    led1 = !led1;
}