Arianna autonomous DAQ firmware

Dependencies:   mbed SDFileSystemFilinfo AriSnProtocol NetServicesMin AriSnComm MODSERIAL PowerControlClkPatch DS1820OW

main.cpp

Committer:
uci1
Date:
2012-08-02
Revision:
4:a91682e19d6b
Parent:
3:24c5f0f50bf1
Child:
5:9cea89700c66

File content as of revision 4:a91682e19d6b:

#include "mbed.h"

#include <stdint.h>
#include "SDFileSystem.h"
#include "MODSERIAL.h"
#include "Watchdog.h"
#include "SnConstants.h"
#include "SnBitUtils.h"
#include "SnSDUtils.h"
#include "SnConfigFrame.h"
#include "SnEventFrame.h"
#include "SnStatusFrame.h"
#include "SnHeaderFrame.h"
#include "SnCommWin.h"
#include "SnCommAfar.h"
#include "SnCommUsb.h"
#include "SnBase64.h"

//
// MBED PINS (ordered by number)
//
// leds (for debugging)
DigitalOut led1(LED1);
DigitalOut led2(LED2);
DigitalOut led3(LED3);
DigitalOut led4(LED4);
// Set up power pins - Note that it's Zero for "on"
DigitalOut PIN_turn_on_system(p17); // this turns on system
DigitalOut PIN_turn_on_amps(p25);
// Activate/select chip by falling edge
DigitalOut PIN_ADC_CS( p9 );
// clock signal to activate PLA setting
DigitalOut PIN_PLA_cs(p10);
// To force a trigger
DigitalOut PIN_forceTrigger(p11);  //modification
// To suppress thermal triggers
DigitalOut PIN_enableThermTrig(p12);
// Restart clock on all FPGAs.
DigitalOut PIN_DoNotRestartAllClocks( p13 );
// This tells the DFPGAs to store the data on motherboard FPGA and
// read it out.
DigitalIn PIN_a_sf_clk( p14 );
DigitalIn PIN_rst_a_sf(p15);
// afar power
DigitalOut PIN_afar_power(p16);
// batter voltage/current measurement
AnalogIn PIN_vADC1(p19); 
AnalogIn PIN_vADC2(p18); 
// Lock daughter card registeres (during data readout).
DigitalOut PIN_lockRegisters( p20 );
// iridium (SBD) power
DigitalOut PIN_iridSbd_power(p21);
// Majority logic pins
DigitalOut PIN_MajLogHiBit(p22);
DigitalOut PIN_MajLogLoBit(p23);
// Tell FPGA to be ready to accept DAC values
DigitalOut PIN_start_fpga(p26); 
// Two bits to the select the daughter card for readout
DigitalOut PIN_selCardHiBit( p29 );
DigitalOut PIN_selCardLoBit( p30 );
// To launch a heartbeat pulse
DigitalOut PIN_heartbeat(p24);
// Setup SPI pins
SPI PIN_spi( p5, p6, p7 );
// The SD card

// this needs to be first in case some other global uses a print statement
static MODSERIAL      gCpu( USBTX, USBRX ); // defined here so it might be used for debugging output

SDFileSystem sd(p5, p6, p7, p8, SnSDUtils::kSDsubDir+1);
LocalFileSystem local("local");

//
// fwd declare fcns
//
void                      ReadAllRegisters();
void                      ReadRegister(const uint8_t chan, int16_t* dev);
void                      SaveEvent(const int32_t etms);
void                      WaitTrigAndSendClock();
void                      SetConfigAndMakeOutputFile();
SnCommWin::ECommWinResult OpenCommWin();
void                      MakeOutputFile(const bool stopRunning=false);
void                      SetPower(const bool isCommWin);

//
// globals
//
// readout objs
static Ticker         gForceTicker;
static Ticker         gHeartbeatTicker;
static Ticker         gCommWinTicker;
static Timer          gEvtTimer;
static SnConfigFrame  gConf;
static SnEventFrame   gEvent;
// parameters
static bool           gFirstEvt         = true;
static bool           gReadingOut       = false;
static bool           gCommWinOpen      = false; // if it's open
static volatile bool  gOpenCommWin      = false; // if it should be opened
static int32_t        gEvtNum           = 0;   // num of evt written
static int32_t        gTrgNum[kNumTrgs] = {0}; // num of this type of trg received
// i/o
static time_t         gLastCommWin      = 0;
static const uint32_t gBufSize=SnStatusFrame::kMaxSizeOf + SnHeaderFrame::kMaxSizeOf;
static const uint32_t gB64Bsize=BASE64ENC_LEN(gBufSize)+1;
static char           gB64Buf[gB64Bsize];
static char           gGenBuf[gBufSize]; // must be big enough for event or status or config!
//static SnCommWin*     gComms[kNcomms]   = { new SnCommAfar, new SnCommUsb(&gCpu) }; // order => priority
//static SnCommWin*     gComms[kNcomms]   = { new SnCommUsb(&gCpu) }; // order => priority
static SnCommWin*     gComms[kNcomms]   = { new SnCommAfar(gB64Buf, gB64Bsize) }; // order => priority

void procForceTrigger() {
    led3=!led3;
    if (gReadingOut==false && gCommWinOpen==false) {
        gEvent.SetTrgBit(kFrcTrg);
        gEvent.SetTrgNum((gTrgNum[kFrcTrg])++);
        PIN_forceTrigger = 1;     // force a trigger
    }
}

void procHeartbeat() {
    if (gReadingOut==false && gCommWinOpen==false) {
        PIN_heartbeat = 1; // heartbeat pulse
        PIN_heartbeat = 0;
    }
}

void procCommWin() {
    if (gReadingOut==false && gCommWinOpen==false) {
        if ( (time(0) - gLastCommWin) > gConf.GetCommWinPeriod() ) {
            led3=!led3;
            gOpenCommWin = true;
        }
    }
}

uint32_t GetTimeoutTime(const uint32_t startTime,
                        const uint32_t delta) {
    const uint32_t lst = time(0)-startTime;
    const uint32_t lio = 
        ((lst+delta) < gConf.GetCommWinDuration()) ?
          lst+delta  : gConf.GetCommWinDuration();
    return lio+startTime;
}


// TODO: add block-id's to output file? (config block, event block, file header block, etc.)

// TODO: HEARTBEAT!

// TODO: make websocket URL settable in the config (i.e. via SBD?)

int main() {
    {
        led1=1; wait(0.2);
        led1=0; led2=1; wait(0.2);
        led2=0; led3=1; wait(0.2);
        led3=0; led4=1; wait(0.2);
        led4=0;
    }
    
    led2=1;
    //wait_ms(100);
    
    printf("\n\n\n\n\n\nstarting\r\n");
        
    // a failsafe
    Watchdog::kick(kWDFailsafe);
    
    // set the clock to the BS time, if it's not set
    if ( (static_cast<int32_t>(time(0)))<0 ) {
        set_time(kBStime);
    }
    printf("time = %d\r\n",(int32_t)time(0));
    gLastCommWin = time(0); // prevent comm win proc
    
    gForceTicker.detach();
    gFirstEvt = true;
    
    // (probably) power down comms and power up cards,amps
    SetPower(false);

    printf("Using config %s\r\n",gConf.GetLabel());
    SetConfigAndMakeOutputFile(); // setup defaults in case no communication

    //
    // get config
    //
    //printf("open window\r\n");
    OpenCommWin();
    
    // get ready to trigger
    PIN_spi.format( 16, 1 ); // change to data readout format

    led2=0;
    
    // the main event loop. wait for triggers in SendClock
    gEvtTimer.start();
    while( true )
    {
        // in here, we wait for triggers from the MB-FPGA
        Watchdog::kick(); // don't reset!
        
        led1 = !led1;
        
        printf("calling wait trig\r\n");
        printf("gFirstEvt=%s\r\n",gFirstEvt?"true":"false");

        PIN_lockRegisters = 0; // allow data to come from DFPGA
        WaitTrigAndSendClock();
        PIN_lockRegisters = 1; // block registers during readout
        
        const int32_t etms = gEvtTimer.read_ms(); // time since last trigger
        gEvtTimer.reset(); gEvtTimer.start();     // start counter from this trigger
        
        printf("wait trig send clock exited\r\n");
                
        Watchdog::kick(); // don't reset!
        
        if (gReadingOut) {
            //
            // got trigger. read registers to mbed and build the event
            //
            
            led4=1;
            printf("readout\r\n");
            
            // read data & calc CRC
            gEvent.ReadWaveforms(PIN_spi, PIN_selCardHiBit, PIN_selCardLoBit);
            gEvent.SetCurMbedTime();
            // TODO: no way to check for external trigger?
            if (gEvent.IsForcedTrg()==false) {
                gEvent.SetTrgBit(kThmTrg);
                gEvent.SetTrgNum((gTrgNum[kThmTrg])++);
            } // else already set by procForceTrigger
            // (no need to calc if we throw this event away)
            
            Watchdog::kick(); // don't reset!
    
            printf("gFirstEvt=%s\r\n",gFirstEvt?"true":"false");

            if ( gEvent.IsForcedTrg() || gFirstEvt ||
                (etms>gConf.GetEvtThrtlPeriodMs()) ) {
                
                led2=1;
                
                PIN_lockRegisters = 0; // done reading, unlock so we can talk to SD card.
                
                SaveEvent(etms);

                if (gEvtNum>=(gConf.GetFirstEvt()+gConf.GetEvtsPerFile())) {
                    MakeOutputFile(gConf.IsSingleSeqRunMode());
                }
            /*
            } else {
                printf("forced=%s, gFirstEvt=%s, e>t %d>%hu %s\r\n",
                    gEvent.IsForcedTrg()?"true":"false", gFirstEvt?"true":"false",
                    etms, gConf.GetEvtThrtlPeriodMs(),
                    etms>gConf.GetEvtThrtlPeriodMs() ? "true":"false");
            */
            }
        }
        printf("past reading out\r\n");
        
        led4=0; led2=0;
        
        if (gOpenCommWin) {
            printf("gOpenComWin=%s, opening\r\n",gOpenCommWin?"true":"false");
            OpenCommWin();
            gOpenCommWin=false;
        } else {
            printf("gOpenCommWin=false\r\n");
        }
    }

}

//
// save the event
//
void SaveEvent(const int32_t etms) {
    // write the event
        
    printf("save event\r\n");

    // set the event number & dt
    gEvent.SetEvtNum(gEvtNum);
    gEvent.SetDTms(etms);
    
    // save to SD
    SnSDUtils::WriteEventTo(SnSDUtils::GetCurFile(), gGenBuf, gEvent, gConf);
    
    // reset
    gEvent.ClearEvent();
    
    // increment event number
    ++gEvtNum;
    
    printf("gEvtNum=%d\r\n",gEvtNum);
}

void MakeOutputFile(const bool stopRunning) {
    SnSDUtils::CloseOutputFile(SnSDUtils::GetCurFile());
    if (stopRunning) {
        while (true) {
            led3 = 1; led4=1;
            wait(0.5);
            led3 = 0; led4=0;
            wait(0.5);
            Watchdog::kick();
        }
    }
    SnSDUtils::OpenNewOutputFile(gConf.GetMacAddress(),
                                 gConf.GetRun(),
                                 PIN_vADC1.read_u16(),
                                 PIN_vADC2.read_u16());
    printf("made output file with run %u\r\n",gConf.GetRun());
    printf("filename=%s\r\n",SnSDUtils::GetCurFileName());
    SnSDUtils::WriteConfig(SnSDUtils::GetCurFile(), gConf);
}

//
// power stuff
//
void SetPower(const bool isCommWin) {
    if (isCommWin) {
        PIN_turn_on_system = gConf.IsPoweredFor(SnConfigFrame::kCardComWin);
        wait_ms(10);
        PIN_turn_on_amps = gConf.IsPoweredFor(SnConfigFrame::kAmpsComWin);
        wait_ms(10);
        PIN_iridSbd_power = gConf.IsPoweredFor(SnConfigFrame::kIridComWin);
        wait_ms(10);
        PIN_afar_power = gConf.IsPoweredFor(SnConfigFrame::kAfarComWin);
        wait_ms(10);
    } else {
        PIN_turn_on_system = gConf.IsPoweredFor(SnConfigFrame::kCardDatTak);
        wait_ms(10);
        PIN_turn_on_amps = gConf.IsPoweredFor(SnConfigFrame::kAmpsDatTak);
        wait_ms(10);
        PIN_iridSbd_power = gConf.IsPoweredFor(SnConfigFrame::kIridDatTak);
        wait_ms(10);
        PIN_afar_power = gConf.IsPoweredFor(SnConfigFrame::kAfarDatTak);
        wait_ms(10);
    }
}

//
// set configuration
//
void SetConfigAndMakeOutputFile() {
    printf("SetConfigAndMakeOutputFile\r\n");
    
    // restart watchdog
    Watchdog::kick(gConf.GetWatchdogPeriod());
    
    // block (thermal) triggers during configuration
    PIN_enableThermTrig       = 0;
    PIN_ADC_CS                = 1;
    PIN_DoNotRestartAllClocks = 1;
    PIN_forceTrigger          = 0;
    PIN_heartbeat             = 0;
    wait_ms(20);
    
    // reset event, timers, trigger counters
    gEvent.ClearEvent();
    gEvtNum = gConf.GetFirstEvt();
    memset(gTrgNum, 0, sizeof(int32_t)*kNumTrgs);
    
    // make new output file
    MakeOutputFile();
    
    // TODO: turn on amps individually, when that's possible
    PIN_turn_on_amps = gConf.IsEachAmpOn() ? 0 : 1;
    
    // Set PLA value(s)
    PIN_spi.format( 16, 0 ); // change mode for DAC & PLA value setting
    PIN_spi.frequency(1000000);
    PIN_MajLogHiBit=1;
    PIN_MajLogLoBit=1;
    PIN_enableThermTrig=0;

    uint16_t hi, lo;
    PIN_PLA_cs=1;
    wait(4);
    for (uint8_t pi=0; pi<kNplas; pi++) {
        if (pi < gConf.GetNumPlas()) {
            SnConfigFrame::GetHiLoPlas(gConf.GetPla(pi), hi, lo);
            PIN_spi.write(hi);
            PIN_spi.write(lo);
            printf("pla hi %hu, lo %hu\r\n",hi,lo);
        } else {
            PIN_spi.write(kNoTrigPla); // hi
            PIN_spi.write(kNoTrigPla); // lo
            printf("pla hi %hu, lo %hu\r\n",kNoTrigPla,kNoTrigPla);
        }
        Watchdog::kick();
    }
    wait(3);
    PIN_PLA_cs=0;
    wait(3);
    
    // DAC values
    //
    // first 12 bits = DAC value
    // next 2 bits = DAC ID
    // last 2 bits = dFPGA ID
    //
    // But FPGA uses "gray encoding" which means only 1 bit
    // can change at a time (of the last 4 bits). So even tho
    // the card/dac# is encoded, the order is also important
    // 0000 (dac0,card0), 0001 (dac0,card1), 0011 (dac0,card3), 0010 (dac0,card2),
    // 0110 (dac1,card2), 0111 (dac1,card3), 0101 (dac1,card1), etc.
    printf("setting dacs\r\n");
    uint16_t dv=0;
    for (uint8_t i=0, gri=0; i<kTotDacs; i++) {
        // get the gray-codes for this iteration
        gri = SnBitUtils::binToGray(i);
        
        // build bit word
        dv   = static_cast<int>(gConf.GetDac(gri & 0x0003u, gri >> 2u));
        dv <<= 4u;
        dv  |= gri;
        
        printf("dac %04x\r\n",dv);
        
        // send to FPGA
        PIN_start_fpga=1;
        PIN_spi.write(dv);
        PIN_start_fpga=0;

        Watchdog::kick();
        
    }
    printf("dacs set\r\n");
    wait_ms(20);
    
    // Majority Logic Trigger selection (# of cards)
    SnBitUtils::SetChanNumBits(gConf.GetNumCardsMajLog() - 1u,
                   PIN_MajLogHiBit, PIN_MajLogLoBit);
    
    // Enable thermal trigger?
    PIN_enableThermTrig = gConf.IsThermTrigEnabled();

    PIN_spi.format( 16, 1 ); // back to trigger mode
    PIN_spi.frequency( 10000000 );  // Max is 12.5 MHz

    // force a trigger every...
    gForceTicker.detach();
    if (gConf.GetForceTrigPeriod()>0) {
        gForceTicker.attach(&procForceTrigger, 
            gConf.GetForceTrigPeriod() > kAbsMaxTimer ?
            kAbsMaxTimer : gConf.GetForceTrigPeriod()); // force period has a maximum
    }
    // heartbeat every ...
    gHeartbeatTicker.detach();
    if (gConf.GetHeartbeatPeriod()>0) {
        gHeartbeatTicker.attach(&procHeartbeat,
            gConf.GetHeartbeatPeriod() > kAbsMaxTimer ?
            kAbsMaxTimer : gConf.GetHeartbeatPeriod());
    }
    // proc a comm win every...
    gCommWinTicker.detach();
    gCommWinTicker.attach(&procCommWin,
        gConf.GetCommWinPeriod() > kAbsMaxTimer ?
        kCommWinLongPrdTk : gConf.GetCommWinPeriod()); // periodic check if above max
    printf("attach comm win ticker %u\r\n",
        gConf.GetCommWinPeriod() > kAbsMaxTimer ?
        kCommWinLongPrdTk : gConf.GetCommWinPeriod());
    
    Watchdog::kick(); // don't reset!

}

//
// readout functions
//
void WaitTrigAndSendClock() {
    
    printf("WaitTrigAndSendClock\r\n");
        
    if (gFirstEvt==false) {
        PIN_DoNotRestartAllClocks    = 0;
        wait_us(1);
        PIN_DoNotRestartAllClocks    = 1;
        //led3 = !led3; // toggle send clock led
    } else {
        gFirstEvt = false;
    }
    
    //
    // wait for a trigger here.
    //
    gReadingOut = false;  // this will allow forced triggers (see procForceTrigger())
    while ( PIN_a_sf_clk == 1 ) {
        if (gOpenCommWin) {
            return; // break out to open comms
        }
    }
    PIN_forceTrigger=0;   // necessary for forced triggers, harmless for other triggers
    gReadingOut = true;   // disallow new forced triggers
    //
    // collect data from daughter cards
    //
    // TODO: what if some card (set of channels) doesn't respond?
    // currently, will wait forever?
    // also, if ch1 is dead, will wait forever (due to FPGA code)
    
    for( uint8_t i = 0; i < kNsamps; i++ ) {
        if( PIN_a_sf_clk == 1 ) {
            if( i == 0 )
                wait_us( 1 );

            PIN_ADC_CS = 0;
            PIN_spi.write( 0x00 );
            PIN_ADC_CS = 1;
        } else {
            i--;
        }
    }
    
}

SnCommWin::ECommWinResult OpenCommWin() {
    // loop through each comm mode:
    //  a) try to connect
    //  b) if connected, listen for config
    //  c) if config requests data, send it

    gLastCommWin = time(0);
    if (gConf.GetCommWinDuration()==0) {
        // TODO: set min so this is not possible
        return SnCommWin::kOkNoMsg;
    }
    
    gCommWinOpen = true;
    Watchdog::kick(); // don't reset!
    
    printf("opening comm window at %d\r\n", (int32_t)gLastCommWin);
    
    // close the file so that the data is all written out.
    // and open it back up at the beginning (for reading)
    SnSDUtils::CloseOutputFile(SnSDUtils::GetCurFile());
    SnSDUtils::OpenExistingFile(SnSDUtils::GetCurFileName(), true);
    
    // (probably) power down cards,amps and power up comms
    SetPower(true);
    
    const uint32_t conto = (gConf.GetCommWinDuration() < kConnectTimeout) ?
        gConf.GetCommWinDuration() : kConnectTimeout;
    const uint32_t listo = (gConf.GetCommWinDuration() < kListenTimeout) ?
        gConf.GetCommWinDuration() : kListenTimeout;
    
    SnCommWin::ECommWinResult res = SnCommWin::kUndefFail;
    
    bool gotNewConfig=false;
    bool sendStat[kNcomms];
    for (uint8_t i=0; i<kNcomms; i++) {
        sendStat[i]=true;
    }
    bool* ss = sendStat;
    SnCommWin** cw = gComms;
    for (uint8_t i=0; ((time(0)-gLastCommWin)<gConf.GetCommWinDuration()); i++, cw++, ss++) {
        Watchdog::kick(); // don't reset!
        if (i==kNcomms) {
            i=0;
            cw = gComms;
            ss = sendStat;
        }
        // open window and (mabye) send status update
        printf("calling OpenWindow. ss=%d\r\n",(int)(*ss));
        printf("gtt=%u, ct=%d, lcw=%d, dur=%u\r\n",GetTimeoutTime(gLastCommWin,conto),
            time(0), gLastCommWin, gConf.GetCommWinDuration());
        const SnCommWin::ECommWinResult conres = (*cw)->OpenWindow(
            GetTimeoutTime(gLastCommWin, conto), *ss, gConf, gEvent, gGenBuf);
        if (conres>=SnCommWin::kConnected) {
            Watchdog::kick(); // don't reset!
            // connected. listen for config
            *ss = false; // don't send status next time
            const SnCommWin::ECommWinResult cfgres = (*cw)->GetConfig(
                gConf, GetTimeoutTime(gLastCommWin, listo), gGenBuf, gBufSize);
            if (cfgres>=SnCommWin::kOkWithMsg) {
                Watchdog::kick(); // don't reset!
                printf("received config (%u)!\r\n",gConf.SizeOf());
                char* b = gGenBuf;
                gConf.WriteTo(b);
                const uint32_t csz = gConf.SizeOf();
                for (uint32_t i=0; i<csz; i++) {
                    printf("%02x ",gGenBuf[i]);
                }
                printf("\r\n");
                // send data if need be (files, some events, etc)
                printf("send data = %d\r\n", gConf.GetCommSendData());
                if (gConf.GetCommSendData()!=0) {
                    printf("sending data\r\n");
                    res = (*cw)->SendData(gConf, gEvent, gGenBuf, gBufSize,
                        GetTimeoutTime(gLastCommWin, gConf.GetCommWinDuration()));
                } else {
                    // don't send anything
                    res = cfgres;
                }
                printf("Got config!\r\n");
                gotNewConfig=true;
                Watchdog::kick(); // don't reset!
                break;
            }
        }
        
        Watchdog::kick(); // don't reset!

    }
    
    // (probably) power down comms and power up cards,amps
    SetPower(false);

    gFirstEvt = true;

    // reset config with system powered (for DAC/PLA setting)
    if (gotNewConfig) {
        printf("calling SetConfigAndMakeOutputFile\r\n");
        SetConfigAndMakeOutputFile();
        // TODO: remove
    }
    printf("closing comm win at %d\r\n",(int32_t)time(0));
    
    gCommWinOpen = false;
    return res;
}