My motors and other parts aren't here yet, so let's talk infrared a little more. The IR LED/phototransistor pair units I have are TCRT5000 from Vishay. I went over the datasheet and attached the most relevant bits here. First one is the absolute maximum specs for the IR LED portion. Take note of the maximum current allowed indicated by "forward surge current": 3 whole Amperes. Awesome. Caveat: for 10 microseconds max. Just like with a camera's flash, if I want to maximize range, I need to push as much power through the LED as I can get away with, for just as long as I need to.
How long do I need to have it on though? I need the IR LED on for as long as it takes to read the result from the I2C ADC. Using the 16-bit timer Timer 1 with no prescaler (simple time measuring for intervals under 8.2ms with an 8Mhz AVR, 4.1ms with a 16Mhz), I got the following results:
single ADC read: ~515 microseconds
2-read average: ~606 microseconds
4-read average: ~794 microseconds
That's actually pretty long, damn. So I can have the LED on with 3 Amps flowing through it for 10 microseconds. Assuming a linear relationship between current and time, having the LED on 51x longer should mean that if I reduce the current to 1/51th of 3A, I should be good. In other words, ~59 milliamperes. But wait, the datasheet says the max continuous current is 60mA. Pulsing for 515us with a low duty cycle, say, once every 20ms, should let us go higher than 60mA, somewhere between 60mA and 3A. I guess the relationship isn't actually linear. I would guess I could go as high as 150~200mA for 500us. By the way, the earlier ranging tests were done at ~64mA pulsed. The code for this time test is as follows:
// Timer 1 setup
TCCR1B = (1 << CS10); // no prescaler
TCNT1 = 0x00; // set timer1 counter initial value to 0
while (1)
{
_delay_ms(100);
TCNT1 = 0x00; // set timer1 counter initial value to 0
// read backgroud noise
adc_read_noise = PCF8591_read(PCF8591_ADDR, 0x01);
elapsed = TCNT1; // number of counts
// 1 / F_CPU * count = elapsed time in seconds
PORTB |= (1 << PB0); // turn on IR LED
TCNT1 = 0x00; // reset timer1 counter
// get distance reading
adc_read_real = PCF8591_read(PCF8591_ADDR, 0x01);
elapsed += TCNT1;
PORTB &= ~(1 << PB0); // turn off IR LED
// subtract noise
if (adc_read_noise > adc_read_real) // subtraction would cause an underflow
{
adc_read_real = 0;
}
else
{
adc_read_real -= adc_read_noise;
}
send_string("elapsed count: ");
sprintf(word_buffer, "%d", (elapsed >> 1)); // divide by two
send_string(word_buffer);
send_string("\n\r");
send_string("ADC value: ");
sprintf(byte_buffer, "%d", adc_read_real);
send_string(byte_buffer);
send_string("\n\r");
}
A more sophisticated approach would start a timer, start the I2C transaction, have an interrupt trigger after 300us, turn on the LED in the interrupt handler and disable the timer, then get the reading from the ADC, potentially reducing your on-time to ~200us. But I digress. So how to switch 100-200mA per LED anyway? AVR GPIOs aren't going to want to do that. The PCF8574 won't sink more than 50mA per output, and 100mA at a time total. Enter the P-channel MOSFET. Available in tiny SOT and dual-channel SOP-8 packages, and with near infinite gate resistance, the PCF8574 can drive them to saturation at 3.3V with ease. Schematic related is what the circuit for 2 IR LED/phototransistor pairs would look like, and what I'll likely end up doing.