Last active
December 2, 2019 19:42
-
-
Save johnty/9d6e156c029e2db6c9ce1f1ee95b8b21 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| Latency measurement code -- measurement jig | |
| Simple tool to toggle a digital pin and measure the delay until | |
| the desired response happens. The function of the digital output, | |
| and the signal to be measured, are determined by whatever external | |
| hardware is attached. | |
| The resulting delay in microseconds is printed over the serial port | |
| so multiple trials can be made into a histogram. The granularity of | |
| the timer is 4us on standard 16MHz Arduino boards. | |
| (c) 2015 Andrew McPherson, C4DM, QMUL | |
| This is licensed under a Creative Commons Attribution-ShareAlike licence. | |
| updated version: 2019 Johnty Wang - IDMIL, McGill | |
| Addition of multiple trigger events: pin 3 is first input, pin 4 is second. | |
| Event T2 is now the time between T1 and when a serial byte is received | |
| To use: open serial port at 115200b | |
| T0 on UNO Pin 3: button to start measure | |
| T1 on UNO Pin 4: trigger from clapboard | |
| T2: triggered by receiving ANY serial data | |
| T2 can also be triggered on Pin 5, if you look at the gInputPin2PortReg bits | |
| and comment out the Serial waiting bits for stage 2 of the timing. | |
| the output is 3 numbers, T0, T1, and T2, in microseconds. (note first number is a useless '0')... | |
| */ | |
| const int gOutputPin = 2; // Pin sending the trigger | |
| const int gInputPin0 = 3; // Pin0 reading response t0 | |
| const int gInputPin1 = 4; // Pin1 reading response t1 | |
| const int gInputPin2 = 5; // Pin2 reading response t2 | |
| const int gLEDPin = 13; // Pin for the LED on the board, for visual feedback | |
| const int gDelayBetweenTrials = 150; // Measured in milliseconds; only approximate | |
| volatile uint8_t *gInputPin0PortReg, *gInputPin1PortReg, *gInputPin2PortReg, *gOutputPinPortReg; | |
| uint8_t gInputPin0Bit, gInputPin1Bit, gInputPin2Bit, gOutputPinBit; | |
| int gStage; //measurement stage: 0 = 1st stage, 1 = 2nd stage | |
| void setup() { | |
| pinMode(gOutputPin, OUTPUT); | |
| pinMode(gInputPin0, INPUT); | |
| pinMode(gLEDPin, OUTPUT); | |
| digitalWrite(gOutputPin, LOW); | |
| // Do some internal machinations to pull out the register for faster access | |
| // compared to digitalRead(). This is something of a violation of the abstractions | |
| // set up in the Arduino/Wiring environment, and might change on future boards. | |
| gInputPin0PortReg = portInputRegister(digitalPinToPort(gInputPin0)); | |
| gInputPin1PortReg = portInputRegister(digitalPinToPort(gInputPin1)); | |
| gInputPin2PortReg = portInputRegister(digitalPinToPort(gInputPin2)); | |
| gOutputPinPortReg = portOutputRegister(digitalPinToPort(gOutputPin)); | |
| gInputPin0Bit = digitalPinToBitMask(gInputPin0); | |
| gInputPin1Bit = digitalPinToBitMask(gInputPin1); | |
| gInputPin2Bit = digitalPinToBitMask(gInputPin2); | |
| gOutputPinBit = digitalPinToBitMask(gOutputPin); | |
| Serial.begin(115200); | |
| for (int i = 0; i < 10; i++) { | |
| digitalWrite(gLEDPin, HIGH); | |
| delay(50); | |
| digitalWrite(gLEDPin, LOW); | |
| delay(50); | |
| } | |
| delay(2000); | |
| //digitalWrite(gLEDPin, LOW); | |
| while (Serial.available()) | |
| Serial.read(); | |
| } | |
| void loop() { | |
| unsigned int overflows0 = 0; | |
| unsigned int finalCount0 = 0; | |
| unsigned int overflows1 = 0; | |
| unsigned int finalCount1 = 0; | |
| unsigned int overflows2 = 0; | |
| unsigned int finalCount2 = 0; | |
| gStage = 0; //start with stage 0 | |
| // **** This code runs with interrupts off for predictable timing **** | |
| cli(); | |
| // Prepare Timer1 initially stopped | |
| TCCR1A = 0; // normal mode | |
| TCCR1B = 0; // no clock source | |
| TCCR1C = 0; // no force compare | |
| TIMSK1 = 0; // no interrupts | |
| TIFR1 = 1; // clear overflow bit | |
| TCNT1 = 0; // count starts at 0 | |
| // Toggle pin... | |
| // Equivalent (but slower): digitalWrite(gOutputPin, HIGH); | |
| //*gOutputPinPortReg |= gOutputPinBit; | |
| // ...and wait (possibly forever) for response | |
| // Equivalent (but slower): while(digitalRead(gInputPin0) != HIGH) { /* ,,, */ } | |
| // we wait here until we get the first trigger! | |
| if (gStage == 0) { | |
| while (!(*gInputPin0PortReg & gInputPin0Bit)) { | |
| // finalCount0 = TCNT1; // Capture count | |
| // if (TIFR1 & 1) { | |
| // TIFR1 = 1; | |
| // overflows0++; | |
| // } | |
| } | |
| gStage = 1; | |
| digitalWrite(gLEDPin, HIGH); | |
| // Start timer at 16MHz (1 tick = 1/16us) | |
| TCCR1B = 1; | |
| } | |
| if (gStage == 1) { | |
| TIFR1 = 1; // clear overflow bit | |
| TCNT1 = 0; // count starts at 0 | |
| while (!(*gInputPin1PortReg & gInputPin1Bit)) { | |
| finalCount1 = TCNT1; // Capture count | |
| if (TIFR1 & 1) { | |
| TIFR1 = 1; | |
| overflows1++; | |
| } | |
| } | |
| gStage = 2; | |
| } | |
| if (gStage == 2) { //receive serial byte | |
| sei(); //unfortunately we have to do this, which will make timing less precise. | |
| // but we need it for the serial port... | |
| TIFR1 = 1; // clear overflow bit | |
| TCNT1 = 0; // count starts at 0 | |
| //while (!(*gInputPin2PortReg & gInputPin2Bit)) { //this is for the pin | |
| while (!Serial.available()) { | |
| finalCount2 = TCNT1; // Capture count | |
| if (TIFR1 & 1) { | |
| TIFR1 = 1; | |
| overflows2++; | |
| } | |
| } | |
| gStage = 0; | |
| TCCR1B = 0; // Stop timer again | |
| digitalWrite(gLEDPin, LOW); | |
| } | |
| while (Serial.available()) //clear serial buffer | |
| Serial.read(); | |
| //sei(); | |
| // **** End of interrupts-off code **** | |
| // Reset pin for next time | |
| //digitalWrite(gOutputPin, LOW); | |
| //digitalWrite(gLEDPin, LOW); | |
| // Print the result of the trial | |
| // The testing code itself results in a delay of 1us, so subtract | |
| // TODO: get updated testing code delay value | |
| // this to get the actual time delay | |
| //T0: | |
| unsigned long latencyInTimerTicks = (unsigned long)finalCount0 + ((unsigned long)overflows0 << 16); | |
| unsigned long latencyInMicros = latencyInTimerTicks / 16; | |
| Serial.print(latencyInMicros); | |
| Serial.print(" "); | |
| //T1: | |
| latencyInTimerTicks = (unsigned long)finalCount1 + ((unsigned long)overflows1 << 16); | |
| latencyInMicros = latencyInTimerTicks / 16; | |
| Serial.print(latencyInMicros - 1); | |
| Serial.print(" "); | |
| //T2: | |
| latencyInTimerTicks = (unsigned long)finalCount2 + ((unsigned long)overflows2 << 16); | |
| latencyInMicros = latencyInTimerTicks / 16; | |
| Serial.println(latencyInMicros); | |
| //need a tiny delay before we disable interrupts on next loop so serial processing can finish... | |
| delay(5); | |
| // Wait for next trial | |
| //delay(gDelayBetweenTrials); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment