Wiring it up

As explained above, we’ll be detecting the flash by measuring the voltage from the photodiode. Arduino programmers are probably thinking that I’m about to break out analogRead. Unfortunately that’s far, far too slow. We not only need fast readings but we also need accurate timers and the standard Arduino ones aren’t good enough either. We’ll need to drop down to native AVR code. I’m developing this on a Leonardo, which uses an ATMega32u4 micro. I’m going to be working directly with the micro’s registers, so the code is specific to the Leonardo. With a bit of fiddling it should work on other Arduinos too, but it won’t out of the box.

ATMega chips have a built-in analog comparator which is great for this. This measures whether the voltage on one pin is higher or lower than the reference voltage and generates an interrupt or sets a register accordingly. The reference voltage can either be the micro’s internal reference voltage, or a voltage on an external pin. We’ll be using the latter so that we can set our threshold. Once the voltage from the photodiode goes over that threshold it will trigger an interrupt. A little trial and error with the oscilloscope showed that 100-200mV was a good threshold, so I used a voltage divider to generate this. The Arduino site has a useful page showing pin mappings for the Leonardo. From this I can see AIN0, the analog comparator input pin, is mapped to Arduino digital pin 7. The 32u4 uses the ADC multiplexer for the reference voltage, so we’ll use ADC0, which is Analog Input 5 in Arduino world. We’ll put the push button on an interrupt pin, in this case INT0 which is Digital 3. We’ll trigger the flash via a MOSFET on digital pin 4.

If you just want to download and run the code you can get it on Github. However if you’re interested in high-speed timers and interrupts on Arduino read on, as there are a few interesting problems that I’ve had to solve.

Overview

The sequence is as follows: the user presses the button, triggering the external interrupt. In the interrupt handler we turn on the flash trigger pin and record the current time from timer1, then start timer4. Timer4 overflows after 40µs, at which point we turn off the flash trigger pin. Separately, the analog comparator is constantly watching for a pulse from the photodiode. This will only be triggered by a flash, as anything else will not be bright enough. When it detects a pulse, it stores the current time in the input capture register and triggers the input capture interrupt. Inside the input capture handler we store the pulse start time, reset the timer and flip the comparator to trigger on fall rather than rise. Once the flash ends, the falling edge of the pulse triggers the interrupt again and stores the time in the input capture register. In that handler we set a flag saying a flash has just finished and flip the interrupt again to watch for new flashes. Inside the main loop we see that a flash flag has been set and grab the value from the input capture register. That value is the number of clock cycles between the rising edge and the falling edge: i.e. the pulse width. If the trigger time has been set then it means the flash was triggered by us, and we can calculate the lag too. We convert these values to microseconds and print them over serial.

The code

As I said before, I’m having to use native AVR code as we need access to the faster timers and interrupts, but this can be mixed in with regular Arduino code. If you’re not familiar with AVR registers this can look a little strange but I’ve tried to comment it thoroughly.

We’re using two timers. One is for the timing of the pulse width and the lag: i.e. the important stuff. We’re using another timer for things like sending the trigger signal to the flash, debouncing, reset delays and various other housekeeping. First we’ll set up the analog comparator. We’ll use input capture, as that gets us the value of the timer at the exact point that the voltage crossed the threshold. We’ll set these all up in the Arduino setup function.