Erstellt am 13.05.2012
Wandeln und Lesen
Für mein aktuelles Projekt, eine Lüfter- und Lüftungssteuerung im Keller, sollte die Luftfeuchtigkeit und Temperatur gemessen werden, damit der Keller nur dann gelüftet wird, wenn er dadurch nicht noch feuchter wird. Die Temperatur auszulesen stellt ja kein Problem dar - entweder NTC oder gleich einen 1-wire-Sensor.
Für die Luftfeuchtigkeit gibt es im Internet den HH10D-Sensor zu kaufen. Dieser besteht eigentlich aus zwei Komponenten. Einmal dem eigentlichen Sensor für die Luftfeuchtigkeit und einmal einem EEPROM, in dem die Daten von der Kalibrierung im Werk gespeichert sind. Dieser EEPROM kann per i2c-Bus ausgelesen werden und enthält zwei Werte, die zusammen mit der Ausgabe des Feuchtigkeitssensors verrechnet werden. Der Feuchtigkeitssensor gibt den relative Luftfeuchtigkeit als Frequenz im Bereich von ca. 5 bis 10 kHz aus.
Interessant wird es nun deshalb, weil der Sensor und der EEPROM mit 3 V (bzw. 3,3 V sind auch möglich) betrieben werden müssen, ich aber für die übrige Mikrocontroller-Logik mit 5 V Pegeln arbeite. Da der i2c-Bus bidirektional arbeitet, gibt es recht aufwändige Pegelwandler-Chips, die aber dank einer App-Note von Jim Hagerman aus EDN durch zwei einfache NPN-Transistoren (je einer für SCL und SDA) und und ein paar Widerstände ersetzt werden können. Die Transistoren werden dabei im Sättigungsbereich betrieben. Ich habe den Schaltplan im EAGLE-Format mal hochgeladen.
Der Frequenzausgang des Luftfeuchtigkeitssensors kann direkt an den Mikrocontroller geführt werden, da dieser die 3 bzw. 3,3 V Pegel bereits als High erkennt. Für meine Schaltung verwende ich einen Atmega8 - entsprechend führe ich den Frequenzausgang an den 16-Bit-Counter T1. Die i2c-Leitungen werden ebenfalls an die vom Atmega8 vorgesehenen Pins vom TWI (Two Wire Interface) PC4/5 angeschlossen.
Umrechnung
Der nächste Schritt ist jetzt, zunächst den EEPROM auszulesen und anschließend die Frequenz. Aus dem EEPROM erhält man zwei Werte: Sensitivity und Offset. Diese sind jeweils 2 Byte lang und liegen auf den folgenden Adressen:
Koeffizient | Adresse |
Sensitivity (MSB:LSB) | 0x0A:0x0B |
Offset (MSB:LSB) | 0x0C:0x0D |
Ließt man also den Wert von 0x0A aus, muss man diesen um 8 Bit nach links schieben und den Wert von 0x0B aufaddieren. Im Code sieht das dann so aus:
float sens = 0.0;
float senslsb = 0.0;
float offset = 0.0;
float offlsb = 0.0;
i2c_init(); // I²C initialisieren
i2c_start( 0xA2 ); // I²C Übertragung starten mit 0xA2
i2c_write( 0x0A ); // Adresse die als erstes Ausgelesen werden soll
i2c_start( 0xA3 ); // Daten anfordern
sens = i2c_readAck(); // 0x0A MSB von sens
senslsb = i2c_readAck(); // 0x0B LSB von sens
offset = i2c_readAck(); // 0x0C MSB von offset
offlsb = i2c_readNak(); // 0x0D LSB von offset, es werden keine weiter Daten mehr angefordert
i2c_stop(); // I²C Übertragung beenden
offset = offset * ( 1<<8 ); // Stellenwert von offset-MSB berücksichten
offset = offset + offlsb; // MSB und LSB von Offset zusammenfügen
sens = sens * ( 1<<8 ) // Stellenwert von sens-MSB berücksichten
sens = sens + senslsb; // MSB und LSB von sens zusammenfügen
Als nächstes benötigen wir noch die Frequenz. Dazu könnte man entweder die Zeit zwischen zwei Impulsen messen, oder die Impulse innerhalb einer gewissen Zeitspanne. Ich habe mich für letzteres entschieden, da sich das mit dem Counter sehr einfach und ohne großartiges Interrupthandling bewerkstelligen lässt. Der 16-Bit Counter wird also so konfiguriert, dass er den T1-Pin als Trigger für den Counter verwendet. Anschließend wird der Counter auf 0 gesetzt, eine Pause von 100 ms eingelegt und anschließend der Counterwert ausgelesen. Nimmt man ihn mal 10, erhält man die Frequenz in Hz. Im Code sieht das dann so aus:
DDRD &= ~( 1 << DDD5 ); // PD5(T1) pin zurücksetzen
// PD5(T1) ist jetzt als input konfiguriert
// Counter einschalten, Clock bei steigender Flanke
TCCR1B |= ( 1 << CS12 ) | ( 1 << CS11 ) | ( 1 << CS10 );
float freq = 0.0;
TCNT1 = 0;
pause( 100 ); // 100 ms messen
freq = TCNT1;
freq = freq * 10; // Ergebnis * 10 nehmen
Bei einer minimalen Frequenz von 5 kHz ergibt das bei 100 ms Messzeit einen Counterstand von 500 bzw. bei 10 kHz einen Counterstand von 1000, was noch gut in den 16 Bit des Counters liegt, aber für die 8 Bit des kleineren Counters schon viel zu groß wäre.
Im Datenblatt findet sich nun die folgende Formel zur Berechnung der relativen Luftfeuchtigkeit:
RH = (offset - freq ) * sens / 2^12
Da hier ein Wert mit einer Kommastelle heraus kommt, habe ich bereits für die Initialisierung der an der Rechnung beteiligten Variablen den Typ float deklariert. Dies benötigt zwar mehr RAM im Mikrocontroller, ich habe dann aber keinen Ärger mit Typecasts. Im Code ist das dann einfach diese Zeile:
float rh = 0.0; // Relative Luftfeuchtigkeit
rh = ( offset - freq ) * sens / ( 1<<12 );
Den Code für ein fertiges Programm habe ich in einem zip zusammengestellt: hh10d_atmega8.zip
Das Programm nutzt die TWI-Implementierung i2cmaster von Peter Fleury und gibt das Ergebnis auf der seriellen Schnittstelle mit 9600-8n1 aus.
Schaut auch gern noch bei meiner Kellerlüftung vorbei - dort gehe ich ebenfalls nochmal auf den Sensor ein.