fast-feedback virtual target task code on STM Nucleo

Dependencies:   mbed

states.cpp

Committer:
gwappa
Date:
2018-06-19
Revision:
10:7c216d528c35
Parent:
9:e136394bdb39
Child:
11:897ecd5413e0

File content as of revision 10:7c216d528c35:

#include "states.h"
#include "rig.h"
#include "automaton.h"
#include "IO.h"

#define STATE_IS_LOGGED

#ifdef STATE_IS_LOGGED
#define LOGSTATE(S) IO::info(#S);
#else
#define LOGSTATE(S)
#endif

void (*whiskhandler)() = 0;
void (*lickhandler)() = 0;
bool logged = false;

void whiskcallback() {
    if (logged) {
        trial.whisking_events.add(timer.read_ms());
    }
    if (whiskhandler != 0) {
        whiskhandler();
    }
}

void lickcallback() {
    if (logged) {
        trial.licking_events.add(timer.read_ms());
    }
    if (lickhandler != 0) {
        lickhandler();
    }
}

void turnOff_trialStart() {
    trialStart.write(0);
}

void stopReward() {
    rewardOut.write(0);
}

void finalize() {
    cueOut.write(0); // if any
    trialEnd.write(1);
    wait_us(ms_to_us(TRIGGER_DUR_MS));
    
    trial.writeToSerial();
    timer.stop();
    timer.reset();
}

void Delay::setup() {
    LOGSTATE(Delay)
    
    // initialize the trial-related params
    trial.reset(task);
    
    // configure the flags
    cueOut.write(0);
    if ( (task.mode.value == WithCue) || (task.mode.value == Report) ) {
        enableOut.write(1);
    } else {
        enableOut.write(0);
    }
    rewardOut.write(0);
    
    // configure the interrupts
    whiskIn.rise(&whiskcallback);
    lickIn.rise(&lickcallback);
    // no whisk handler, no lick handler
    // only to be logged
    logged = true;
    
    // sets the timeout for the next state
    switch (task.mode.value) {
    case Pair:
        stateTimeout.attach_us(&automaton::jump<Delay,Paired>, ms_to_us(trial.delay_dur_ms + task.prep_dur_ms.value));
        break;
    case WithCue:
        stateTimeout.attach_us(&automaton::jump<Delay,Cued>, ms_to_us(trial.delay_dur_ms + task.prep_dur_ms.value));
        break;
    default:
        stateTimeout.attach_us(&automaton::jump<Delay,Prepare>, ms_to_us(trial.delay_dur_ms));
    }
    
    // trigger trialStart
    trialStart.write(1);
    triggerTimeout.attach_us(&turnOff_trialStart, ms_to_us(TRIGGER_DUR_MS));
    
    // update the timestamp
    trial.starting = timer.read_ms();
    timer.start();
}

void Delay::teardown() {
    // do nothing
}

void Prepare::setup() {
    // mostly the same with for Delay
    // except that the animal cannot lick freely
    LOGSTATE(Prepare)
    
    // configure the interrupts
    // no whisk handler (i.e. only to be logged)
    lickhandler = &automaton::jump<Prepare,Abort>;
    
    // set timeout for the next state
    stateTimeout.attach_us(&automaton::jump<Prepare,Cued>, ms_to_us(task.prep_dur_ms.value));
}

void Prepare::teardown() {
    // de-register lick inhibition
    lickhandler = 0;
}

void Paired::setup() {
    LOGSTATE(Paired)
    trial.response |= TrialFlags::Cues;
    lickhandler     = &automaton::jump<Paired,WithResp>;
    
    // reward & visual feedback
    enableOut.write(1);
    trial.cuestarting = timer.read_ms();
    trial.waiting     = trial.cuestarting - trial.starting;
    
    rewardOut.write(1);
    rewardTimeout.attach_us(&stopReward, ms_to_us(task.reward_ms.value));
    
    stateTimeout.attach_us(&automaton::jump<Paired,NoResp>, ms_to_us(task.cue_dur_ms.value));
}

void Paired::teardown() {
    enableOut.write(0);
}

void Cued::setup() {
    LOGSTATE(Cued)
    
    if (task.mode.value == Report) {
        enableOut.write(1); // enable the feedback only during Cued
    }
    
    // configure the interrupts
    if (task.mode.value == WithCue) {
        whiskhandler    = &automaton::jump<Cued,WithResp>;
        trial.response |= TrialFlags::Cues;
        lickhandler     = 0;
        
    } else {
        whiskhandler = &Cued::gate;
        lickhandler  = &automaton::jump<Cued,Abort>;
    }
    
    // start cue output
    cueOut.write(1);
    
    // sets the timeout for the next state
    stateTimeout.attach_us(&automaton::jump<Cued,NoResp>, ms_to_us(task.cue_dur_ms.value));
    
    // update the timestamp
    trial.cuestarting = timer.read_ms();
    trial.waiting     = trial.cuestarting - trial.starting;
}

void Cued::gate() {
    whiskhandler = 0; // logging only
    trial.response |= TrialFlags::Cues;
    lickhandler  = &automaton::jump<Cued,WithResp>;
}

void Cued::teardown() {
    whiskhandler = 0;
    lickhandler  = 0;
    // end cue output
    cueOut.write(0);
    
    stateTimeout.detach();
    
    if (task.mode.value == Report) {
        enableOut.write(0);
    }
}

void Abort::setup() {
    LOGSTATE(Abort)
    trial.waiting  = timer.read_ms() - trial.starting;
    trial.response = TrialFlags::Licked;
    stateTimeout.attach_us(&automaton::done<Abort>, ms_to_us(task.post_dur_ms.value));
}

void Abort::teardown() {
    finalize();
}

void WithResp::setup() {
    LOGSTATE(WithResp)
    
    trial.response |= TrialFlags::Responded;
    
    whiskhandler = 0;
    lickhandler  = 0;
    
    if (task.mode.value != Pair) {
        // open/close the valve
        rewardOut.write(1);
        rewardTimeout.attach_us(&stopReward, ms_to_us(task.reward_ms.value));
    }
    
    stateTimeout.attach_us(&automaton::done<WithResp>, ms_to_us(task.post_dur_ms.value));
}

void WithResp::teardown() {
    finalize();
}

void NoResp::setup() {
    LOGSTATE(NoResp)
    
    // no reward here
    
    stateTimeout.attach_us(&automaton::done<NoResp>, ms_to_us(task.post_dur_ms.value));
}
void NoResp::teardown() {
    finalize();
}

void TestReward::setup() {
    LOGSTATE(TestReward)
    
    // open/close the valve
    rewardOut.write(1);
    rewardTimeout.attach_us(&stopReward, ms_to_us(task.reward_ms.value));
    stateTimeout.attach_us(&automaton::done<TestReward>, ms_to_us(task.reward_ms.value+20));
}

void TestReward::teardown() {
    // do nothing
}