
There are a few spare I/O lines on the STC89C52 board and looking at my small collection of sensors, I formed the idea to interface a DHT22 sensor, also known as an AM2302, for temperature and humidity and display them on request.
Electrically this sensor is easy to wire up. It only requires 3.3 V to 5 V power and one data line. A one-wire protocol is used to read it. However this uses a custom one-wire protocol and not any of the ones supported by MCU hardware. Probably because the maker wanted to avoid paying licensing fees. The datasheet has all the needed details. 40 bits of data (16 each for humidity and temperature, plus 8 bits of checksum) are sent by the sensor serially. 0 is represented by high for 26-28 µs while 1 is represented by high for 70 µs. The relative humidity is multiplied by 10 and sent as an unsigned 16-bit integer, while the temperature is multiplied by 10 and sent as a sign bit followed by an unsigned 15-bit integer. For example, 765 represents a RH of 76.5%, while 234 represents a temperature of 23.4 C. For my use case I ignore negative temperatures. The checksum is the sum of the previous 4 unsigned bytes truncated to 8 bits.
The tricky operation is how to measure the high times of the 40 bits. This is a time-critical part of the code as the sensor will not wait. The decoding can be done afterwards. For this I thought I could enlist Timer 2 in the 8052 architecture. This can be configured as a counter incrementing at the rate of crystal frequency ÷ 12. With a 12 MHz crystal, this means one count per µs. We also need to detect a non-responsive sensor so that the clock doesn't freeze waiting for a data line that won't change. We can use the overflow flag of the timer for this; 16 bits gives up to 65 ms to complete the read. So my first attempt was something like this:
start timer at 0
for i from 0 to 39
wait for high or overflow
start_time = timer
wait for low or overflow
end_time = timer
duration[i] = end_time - start_time
stop timer
On overflow, the loop terminates prematurely and the function signals a failed read.
First I coded synthetic test data to test the decoding routine that could be enabled by setting a #define. This showed that my decoding was correct.
Next I wired up the sensor to my development board, described in #Adventures with a STC89C52 development board , as depicted in the photo.
Unfortunately in testing I often got overflow timeouts and even when I didn't the data didn't pass the checksum validation.
Eventually I realised that this MCU wasn't fast enough. Most instructions take 1 µs and some take 2 µs. This means precious few instructions can be completed in the core of the loop. This included reading the 16-bit counter with two 8-bit reads and also taking care of the carry race condition.
I could configure this MCU to 6T mode instead of normal 12T mode, meaning that instructions run twice as fast. But there is no scaler for the clock so it would also overflow twice as fast.
I decided to ditch the timer for duration measurement, but retain it for timeout detection. Instead in the heart of the loop I did the simplest thing possible: increment a counter. So the value of the counter would not represent µs but some fraction of it. Hopefully there would be enough difference in count between a 0 and a 1 bit. They turned out to be around 2-3 and 8 respectively. So in the decoding I used a threshold of 5. Here's the loop in question.
for (uchar i = 0; i < 40; i++) {
while (!TF2 && !DHT22D) // wait for 1
;
uchar count = 0;
while (!TF2 && DHT22D) // wait for 0
count++;
hightime[i] = count;
if (TF2) { // timed out, didn't collect bits
TR2 = 0;// turn off T2
TF2 = 0;// clear overflow
DHT22H; // pull up
return i + 1;
}
}
With this change I could reliably read the sensor. But the code is working at the limit of this MCU's power. A more modern and faster MCU would break no sweat. For sure the Atmega 328P in the Arduino can cope, as test programs for this sensor attest. The problem is the time-critical 1-wire protocol. But a slower protocol would have increased the acquisition time. A smarter sensor with say I2C interface would allow a more leisurely read, but cost more. Maybe this is a case for using a 10¢ MCU to interface the sensor to the main MCU.
It also occurred to me that I might be able to use the timer in external enable mode where the data line from the sensor gates the timer and when this is stopped, the count represents the number of µs the data has been high. But I prefer to leave this old-fangled MCU behind and move on to better architectures. (Though there are 8051 descendants that can run at higher clock rates.)
The UI and display code to handle humidity and temperature is straightforward, just adding more modes to the selection. As reading the sensor cannot be interrupted, the display is blanked for the duration, and as the MCU also drives a multiplexed display, this causes a visible flicker at the top of every minute. I shall call this a feature, indicating a sensor read, instead of a bug. An independent display controller like the TM1637 would avoid this.
The DHT22 handling modifications will be uploaded to the clock code repository in due course.
Ken Yap
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.