fast-feedback virtual target task code on STM Nucleo

Dependencies:   mbed

Revision:
11:897ecd5413e0
Parent:
10:7c216d528c35
Child:
12:06ea96546af1
--- a/states.cpp	Tue Jun 19 10:11:23 2018 +0000
+++ b/states.cpp	Thu Jun 21 17:57:22 2018 +0000
@@ -13,6 +13,7 @@
 
 void (*whiskhandler)() = 0;
 void (*lickhandler)() = 0;
+void (*gatehandler)() = 0;
 bool logged = false;
 
 void whiskcallback() {
@@ -33,22 +34,33 @@
     }
 }
 
-void turnOff_trialStart() {
-    trialStart.write(0);
+void gateReward() {
+    if (gatehandler != 0) {
+        gatehandler();
+    }
+}
+
+void tickBuzzer() {
+    audioOut = !audioOut;
 }
 
-void stopReward() {
-    rewardOut.write(0);
+void setupInterrupts() {
+    // configure the interrupts
+    whiskIn.rise(&whiskcallback);
+    lickIn.rise(&lickcallback);
+    
+    whiskhandler = 0;
+    lickhandler  = 0;
+    gatehandler  = 0;
+    
+    // no whisk handler, no lick handler
+    // only to be logged
+    logged = true;
 }
 
 void finalize() {
-    cueOut.write(0); // if any
-    trialEnd.write(1);
-    wait_us(ms_to_us(TRIGGER_DUR_MS));
-    
-    trial.writeToSerial();
-    timer.stop();
-    timer.reset();
+    audioOut.write(0); // if any
+    trial.markTrialEnd();
 }
 
 void Delay::setup() {
@@ -56,48 +68,48 @@
     
     // initialize the trial-related params
     trial.reset(task);
+    visualOut.reset(task, (task.mode.value==Associate)||(task.mode.value==Motion));
+    audioOut.write(0);
+    rewardOut.setDuration(ms_to_us(task.reward_ms.value));
+    setupInterrupts();
     
-    // 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
+    // set up 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));
+    // TODO: start visual stim sequence for Associate mode
     
-    // update the timestamp
-    trial.starting = timer.read_ms();
-    timer.start();
+    trial.markTrialStart();
 }
 
 void Delay::teardown() {
     // do nothing
 }
 
+void Paired::setup() {
+    LOGSTATE(Paired)
+    trial.markEndOfWait();
+    
+    trial.response |= TrialFlags::Cues;
+    lickhandler     = &automaton::jump<Paired,WithResp>;
+    
+    // reward & visual feedback
+    visualOut.direct(true);
+    rewardOut.start();
+    
+    stateTimeout.attach_us(&automaton::jump<Paired,NoResp>, ms_to_us(task.aud_dur_ms.value));
+}
+
+void Paired::teardown() {
+    visualOut.direct(false);
+    lickhandler = 0;
+}
+
 void Prepare::setup() {
     // mostly the same with for Delay
     // except that the animal cannot lick freely
@@ -109,64 +121,55 @@
     
     // set timeout for the next state
     stateTimeout.attach_us(&automaton::jump<Prepare,Cued>, ms_to_us(task.prep_dur_ms.value));
+    if (task.mode.value != Motion) {
+        visualOut.startPrepare();
+    }
 }
 
 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);
+    if (task.mode.value != Motion) {
+        visualOut.endState();
+    }
 }
 
 void Cued::setup() {
     LOGSTATE(Cued)
     
-    if (task.mode.value == Report) {
-        enableOut.write(1); // enable the feedback only during Cued
-    }
+    trial.markEndOfWait();
     
     // configure the interrupts
-    if (task.mode.value == WithCue) {
-        whiskhandler    = &automaton::jump<Cued,WithResp>;
-        trial.response |= TrialFlags::Cues;
-        lickhandler     = 0;
-        
+    lickhandler     = &automaton::jump<Cued,Abort>;
+    
+    // "cue" comes from either internal visual stimulus generation (Report, Associate)
+    // or the animal's whisker motion (Motion)
+    if (task.mode.value == Motion) {
+        whiskhandler = &Cued::gate;
     } else {
-        whiskhandler = &Cued::gate;
-        lickhandler  = &automaton::jump<Cued,Abort>;
+        gatehandler  = &Cued::gate;
     }
     
     // start cue output
-    cueOut.write(1);
+    switch (task.mode.value) {
+    case Report:
+        visualOut.startCue(&gateReward);
+        break;
+    case Associate:
+        visualOut.startCue(&gateReward);
+        // fallthrough
+    case Motion:
+        audioOut.write(1);
+        buzzerTicker.attach_us(&tickBuzzer, trial.aud_ticker_cycle);
+    }
     
     // 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;
+    stateTimeout.attach_us(&automaton::jump<Cued,NoResp>, ms_to_us(task.aud_dur_ms.value));
 }
 
 void Cued::gate() {
-    whiskhandler = 0; // logging only
+    gatehandler  = 0;
+    whiskhandler = 0;
     trial.response |= TrialFlags::Cues;
     lickhandler  = &automaton::jump<Cued,WithResp>;
 }
@@ -174,19 +177,18 @@
 void Cued::teardown() {
     whiskhandler = 0;
     lickhandler  = 0;
+    gatehandler  = 0;
     // end cue output
-    cueOut.write(0);
-    
-    stateTimeout.detach();
-    
-    if (task.mode.value == Report) {
-        enableOut.write(0);
+    buzzerTicker.detach();
+    audioOut.write(0);
+    if (task.mode.value != Motion) {
+        visualOut.endState();
     }
 }
 
 void Abort::setup() {
     LOGSTATE(Abort)
-    trial.waiting  = timer.read_ms() - trial.starting;
+    trial.markEndOfWait();
     trial.response = TrialFlags::Licked;
     stateTimeout.attach_us(&automaton::done<Abort>, ms_to_us(task.post_dur_ms.value));
 }
@@ -200,30 +202,38 @@
     
     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));
+        rewardOut.start();
     }
     
     stateTimeout.attach_us(&automaton::done<WithResp>, ms_to_us(task.post_dur_ms.value));
+    if (task.mode.value != Motion) {
+        visualOut.startPrepare();
+    }
 }
 
 void WithResp::teardown() {
+    if (task.mode.value != Motion) {
+        visualOut.endState();
+    }
     finalize();
 }
 
 void NoResp::setup() {
     LOGSTATE(NoResp)
     
-    // no reward here
+    // no reward here no matter the mode
     
     stateTimeout.attach_us(&automaton::done<NoResp>, ms_to_us(task.post_dur_ms.value));
+    if (task.mode.value != Motion) {
+        visualOut.startPrepare();
+    }
 }
 void NoResp::teardown() {
+    if (task.mode.value != Motion) {
+        visualOut.endState();
+    }
     finalize();
 }
 
@@ -231,8 +241,8 @@
     LOGSTATE(TestReward)
     
     // open/close the valve
-    rewardOut.write(1);
-    rewardTimeout.attach_us(&stopReward, ms_to_us(task.reward_ms.value));
+    rewardOut.setDuration(ms_to_us(task.reward_ms.value));
+    rewardOut.start();
     stateTimeout.attach_us(&automaton::done<TestReward>, ms_to_us(task.reward_ms.value+20));
 }