Keisuke Sehara
/
STM32_Whisking
fast-feedback virtual target task code on STM Nucleo
states.cpp
- Committer:
- gwappa
- Date:
- 2018-06-21
- Revision:
- 11:897ecd5413e0
- Parent:
- 10:7c216d528c35
- Child:
- 12:06ea96546af1
File content as of revision 11:897ecd5413e0:
#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; void (*gatehandler)() = 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 gateReward() { if (gatehandler != 0) { gatehandler(); } } void tickBuzzer() { audioOut = !audioOut; } 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() { audioOut.write(0); // if any trial.markTrialEnd(); } void Delay::setup() { LOGSTATE(Delay) // 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(); // 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; default: stateTimeout.attach_us(&automaton::jump<Delay,Prepare>, ms_to_us(trial.delay_dur_ms)); } // TODO: start visual stim sequence for Associate mode 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 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)); if (task.mode.value != Motion) { visualOut.startPrepare(); } } void Prepare::teardown() { // de-register lick inhibition lickhandler = 0; if (task.mode.value != Motion) { visualOut.endState(); } } void Cued::setup() { LOGSTATE(Cued) trial.markEndOfWait(); // configure the interrupts 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 { gatehandler = &Cued::gate; } // start cue output 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.aud_dur_ms.value)); } void Cued::gate() { gatehandler = 0; whiskhandler = 0; trial.response |= TrialFlags::Cues; lickhandler = &automaton::jump<Cued,WithResp>; } void Cued::teardown() { whiskhandler = 0; lickhandler = 0; gatehandler = 0; // end cue output buzzerTicker.detach(); audioOut.write(0); if (task.mode.value != Motion) { visualOut.endState(); } } void Abort::setup() { LOGSTATE(Abort) trial.markEndOfWait(); 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; if (task.mode.value != Pair) { // open/close the valve 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 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(); } void TestReward::setup() { LOGSTATE(TestReward) // open/close the valve 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)); } void TestReward::teardown() { // do nothing }