The traditional Harris 1-Wire® bus has a protocol that works well for interfacing using timer interrupts. Every bit transferred is in a single time slot that is always initiated by the microcontroller “master”. And the spacing between bits can be as long as desired, in this case the time between timer interrupts, about 1ms. As an example, here is the read time slot for reading a bit from the sensor:
The microcontroller pulls the data line down for 1µs then releases the line. It then accurately delays 14µs and reads the line. At that point it is done until the next interrupt. So the microcontroller is “busy” about 16µs every millisecond.
However the one wire interface of the DHT11 or DHT22 poses a problem. The microcontroller issues a command signal for 18ms, which it can do over multiple interrupt periods. But then the sensor drives the data line continuously to return 40 data bits, each 78µs to 120µs, plus an additional 210µs of signaling.
The response can take almost 5ms. The Arduino libraries I looked at either disable interrupts during that entire 5ms or never disable interrupts at all. If interrupts are disabled, then because of the long response time interrupts and data can be lost. Consider that a serial port at 9600bps needs to interrupt once a millisecond to avoid data loss. If interrupts are not disabled and an interrupt occurs during a critical time, when the high pulse time is being measured to differentiate between 0/1, then the data is corrupted, and the read will need to be reattempted one (DHT11) or two (DHT22) seconds later.
My solution was to enable interrupts except when doing the critical measurement of pulse width. Interrupts are disabled while the 40 bits are being received, except for a small window at the start of each bit period when the signal is low. If an interrupt occurs and completes within 50µs, a reasonable period of time if interrupt routines are kept short, then the measurement of the high pulse width can be accurately made.
Full code will be in the new book I’m working on. Meanwhile look at this oscilloscope capture:
The yellow, top, trace shows the signal line for a complete reading. It just fits on the display. The 18ms pulse from the microcontroller can be easily seen since it is so long! This is followed by the data being returned from the sensor.A continuously running UART signal is the middle purple trace. It is running at 19200bps, requiring an interrupt roughly every 0.5ms. The bottom trace is a square wave generated in the same interrupt routine that runs the DHT sensor reading code. It can be seen that everything continues to run during the sensor reading.
The square wave can be generated while the sensor is being read in the same interrupt routine by making the interrupt routine reentrant. However the code for the sensor reading is not reentrant. This is handled by using a check for an attempt to reenter:
ISR (TIMER0_COMPA_vect)
{
static volatile bool lock = false;
// We can have other timer tasks here.
// They get a chance to run even while the DHT is being read
pin.TOGGLE_PIN = 1; // Toggle this pin
// The state machine is not reentrant
if (!lock) {
lock = true;
sei(); // reenable interrupts
dht_state_machine();
cli(); // disable interrupts
lock = false;
}
}