Ok! 1:41am… Pinewood Derby code keeping me up!
These interrupts and timing really don’t work like they should. I had to get in there are TAKE OVER!!! When I get some sleep I’ll explain WHY I had to do what I did. EDIT: I updated the header with some info… please read. Also I’ll add that at one point I tried detaching the interrupts as soon as they fired the first time, and after re-attaching them they would randomly have their Pending Registers already set and they would fire off immediately. I still would like to scrub the spark_wiring_interrupts.cpp to see if it there’s something that can fix this behavior.
This works beautifully now. You ground D2 to start the race timer. Then you ground A6, A7, D3 and D4 to capture their times and stop the race and display times. If you short A6, A7, D3 & D4 together and ground them all at the same time, you’ll notice they fire off about ~180ns apart! Definitely not going to see that precision with polling. Also, based on the winning order, you can easily adjust the times to gain even more precision!
Sample Output (inputs grounded together):
GND D2 to Start Race, GND A6, A7, D3 & D4 to End Race
=====================================================
Waiting for A6, A7, D3 & D4 to go LOW...
======= Lane Times in seconds =======
Lane 1: 2.89765485
Lane 2: 2.89765671
Lane 3: 2.89766039
Lane 4: 2.89765853
GND D2 to Start Race, GND A6, A7, D3 & D4 to End Race
=====================================================
Waiting for A6, A7, D3 & D4 to go LOW...
======= Lane Times in seconds =======
Lane 1: 1.83023608
Lane 2: 1.83023425
Lane 3: 1.83023976
Lane 4: 1.83023790
GND D2 to Start Race, GND A6, A7, D3 & D4 to End Race
=====================================================
Waiting for A6, A7, D3 & D4 to go LOW...
======= Lane Times in seconds =======
Lane 1: 3.50006700
Lane 2: 3.50006886
Lane 3: 3.50007254
Lane 4: 3.50007068
Interrupt Driven Pinewood Derby Code:
// Interrupt Driven Pinewood Derby Timer
// BDub @ Technobly.com 3/19/2014
//
// All inputs are pulled high with internal pullups.
// GND D2 to reset and run the timer for all lanes.
// GND A6, A7, D3 and D4 to stop the timer for each
// lane and display the results.
//
// D0-D3 were initially chosen, but because
// individual control of dis/en'abling their
// interrupt handlers was not available, A6, A7
// D3 and D4 were chosen instead. D0, D1, D2
// A0, A1, A3, A4 are all tied to one interrupt
// handler, so for the one remaining input any
// of these will do. D2 was chosen to allow
// for the most flexibility of the remaining inputs.
//
// EXTI_ClearITPendingBit() is necessary to clear
// the interrupt Pending register, or interrupts
// will fire immediately after enabling the
// interrupt handlers again.
//
// DWT->CYCCNT was used for the timer instead of
// micros() which wraps this hardware counter and
// returns the number of microseconds associated
// with it; because it's not clean to handle the
// case where micros() wraps at 59.652323 seconds.
// This give us 72x more resolution on the timing
// anyway and it's super easy to deal with wrapping.
// It just works out through subtraction of unsigned
// 32-bit variables; as long as you don't time
// something longer than 59.652323 seconds.
//
//====================================================
#include "application.h"
void startRace(void);
void lane1(void);
void lane2(void);
void lane3(void);
void lane4(void);
int ledPin = D7;
bool showStartMsg = false;
bool raceEnded = true; // start off assuming the race has not started.
volatile uint32_t timeStart1;
volatile uint32_t timeStart2;
volatile uint32_t timeStart3;
volatile uint32_t timeStart4;
volatile uint32_t timeEnd1;
volatile uint32_t timeEnd2;
volatile uint32_t timeEnd3;
volatile uint32_t timeEnd4;
uint32_t startTime;
const uint32_t DISQUALIFIED_TIME = 10 * 1000; // in milliseconds (10 seconds)
void setup()
{
Serial.begin(115200);
while(!Serial.available()) SPARK_WLAN_Loop(); // Open terminal and press ENTER
Serial.println("GND D2 to Start Race, GND A6, A7, D3 & D4 to End Race");
Serial.println("=====================================================\n");
pinMode(ledPin, OUTPUT); // debug LED
pinMode(D2, INPUT_PULLUP); // startRace
pinMode(A6, INPUT_PULLUP); // lane1
pinMode(A7, INPUT_PULLUP); // lane2
pinMode(D3, INPUT_PULLUP); // lane3
pinMode(D4, INPUT_PULLUP); // lane4
attachInterrupt(D2, startRace, FALLING); // startRace
attachInterrupt(A6, lane1, FALLING); // lane1
attachInterrupt(A7, lane2, FALLING); // lane2
attachInterrupt(D3, lane3, FALLING); // lane3
attachInterrupt(D4, lane4, FALLING); // lane4
// Disable lanes by default, keep startRace enabled
NVIC_DisableIRQ(EXTI0_IRQn); // A6 "Lane 1"
NVIC_DisableIRQ(EXTI1_IRQn); // A7 "Lane 2"
NVIC_DisableIRQ(EXTI4_IRQn); // D3 "Lane 3"
NVIC_DisableIRQ(EXTI3_IRQn); // D4 "Lane 4"
}
void loop()
{
if(!raceEnded) {
if((timeEnd1 != timeStart1 &&
timeEnd2 != timeStart2 &&
timeEnd3 != timeStart3 &&
timeEnd4 != timeStart4) ||
(millis() - startTime) > DISQUALIFIED_TIME) {
double tempTime;
Serial.println("======= Lane Times in seconds =======");
Serial.print("Lane 1 (A6): ");
//Serial.print(timeEnd1); Serial.print(" ");
//Serial.print(timeStart1); Serial.print(" ");
tempTime = (double)(timeEnd1 - timeStart1)/72000000.0;
if(tempTime != 0.0) Serial.println(tempTime,8);
else Serial.println("DISQUALIFIED!");
delay(50);
Serial.print("Lane 2 (A7): ");
//Serial.print(timeEnd2); Serial.print(" ");
//Serial.print(timeStart2); Serial.print(" ");
tempTime = (double)(timeEnd2 - timeStart2)/72000000.0;
if(tempTime != 0.0) Serial.println(tempTime,8);
else Serial.println("DISQUALIFIED!");
delay(50);
Serial.print("Lane 3 (D3): ");
//Serial.print(timeEnd3); Serial.print(" ");
//Serial.print(timeStart3); Serial.print(" ");
tempTime = (double)(timeEnd3 - timeStart3)/72000000.0;
if(tempTime != 0.0) Serial.println(tempTime,8);
else Serial.println("DISQUALIFIED!");
delay(50);
Serial.print("Lane 4 (D4): ");
//Serial.print(timeEnd4); Serial.print(" ");
//Serial.print(timeStart4); Serial.print(" ");
tempTime = (double)(timeEnd4 - timeStart4)/72000000.0;
if(tempTime != 0.0) Serial.println(tempTime,8);
else Serial.println("DISQUALIFIED!");
delay(50);
raceEnded = true; // prevents results from being displayed over and over
EXTI_ClearITPendingBit(EXTI_Line5); // D2 "startRace"
NVIC_EnableIRQ(EXTI9_5_IRQn); // D2
Serial.println("\nGND D2 to Start Race, GND A6, A7, D3 & D4 to End Race");
Serial.println("=====================================================\n");
}
}
if(showStartMsg) {
Serial.println("Waiting for A6, A7, D3 & D4 to go LOW...\n");
showStartMsg = false;
raceEnded = false;
startTime = millis(); // Capture the rough start time, for disqualification timer
}
}
void lane1()
{
NVIC_DisableIRQ(EXTI0_IRQn); // A6
timeEnd1 = DWT->CYCCNT; //micros();
//digitalWrite(ledPin, HIGH);
//delayMicroseconds(200000);
//digitalWrite(ledPin, LOW);
}
void lane2()
{
NVIC_DisableIRQ(EXTI1_IRQn); // A7
timeEnd2 = DWT->CYCCNT; //micros();
//digitalWrite(ledPin, HIGH);
//delayMicroseconds(200000);
//digitalWrite(ledPin, LOW);
}
void lane3()
{
NVIC_DisableIRQ(EXTI4_IRQn); // D3
timeEnd3 = DWT->CYCCNT; //micros();
//digitalWrite(ledPin, HIGH);
//delayMicroseconds(200000);
//digitalWrite(ledPin, LOW);
}
void lane4()
{
NVIC_DisableIRQ(EXTI3_IRQn); // D4
timeEnd4 = DWT->CYCCNT; //micros();
//digitalWrite(ledPin, HIGH);
//delayMicroseconds(200000);
//digitalWrite(ledPin, LOW);
}
void startRace()
{
NVIC_DisableIRQ(EXTI9_5_IRQn); // D2
timeStart1 = DWT->CYCCNT; // reset!
timeStart2 = timeStart1;
timeStart3 = timeStart1;
timeStart4 = timeStart1;
timeEnd1 = timeStart1;
timeEnd2 = timeStart1;
timeEnd3 = timeStart1;
timeEnd4 = timeStart1;
EXTI_ClearITPendingBit(EXTI_Line0); // A6 "Lane 1"
EXTI_ClearITPendingBit(EXTI_Line1); // A7 "Lane 2"
EXTI_ClearITPendingBit(EXTI_Line4); // D3 "Lane 3"
EXTI_ClearITPendingBit(EXTI_Line3); // D4 "Lane 4"
NVIC_EnableIRQ(EXTI0_IRQn); // A6 "Lane 1"
NVIC_EnableIRQ(EXTI1_IRQn); // A7 "Lane 2"
NVIC_EnableIRQ(EXTI4_IRQn); // D3 "Lane 3"
NVIC_EnableIRQ(EXTI3_IRQn); // D4 "Lane 4"
showStartMsg = true;
//digitalWrite(ledPin, HIGH);
//delayMicroseconds(200000);
//digitalWrite(ledPin, LOW);
}