Badezimmer-Steuerung mit AVR

 

Bei der Modernisierung unseres Badezimmers im Keller galt es auch das Problem der Luftentfeuchtung nach dem Duschen zu lösen, dass bisher kein Augenmerk geschenkt wurde. Da der Raum kein Fenster besitzt sollte ein Lüfter für den notwendigen Luftaustausch sorgen. Da zusätzlich die komplette Elektrik erneuert wurde und zusätzliche Lichteffekte mit LED-Leisten an Dusche und Waschbecken die Raumatmosphäre verbessern soll, lohnt es sich für die ganze Steuerung ein Mikrocontroller  einzusetzen. Zunächst stellt sich die Frage, welche Aufgaben der Controller erfüllen soll.

Da wäre zum einem die Steuerung des Hauptlicht, welche sich als 230V Decken-Einbauspots seitlich über den Spiegel befinden und über ein Präsenzmelder ein- und ausgeschalten werden.

Die Led-Leisten in Fliesen an Dusche und Waschbecken werden jeweils über ein Flowmeter gesteuert. Zur optimalen Entfeuchtung des Bades soll der Lüfter über ein Luftfeuchtigkeits-Modul, welches die relative Feuchte der Luft ständig überwacht, gesteuert werden.

 

Als Sensoren stehen dafür ein Luftfeuchtigkeits-Modul HH10D von Pollin, ein Präsenzmelder der Firma Popp und 2 Flowmeter von GMR zur Verfügung.

Das Luftfeuchtigkeitsmodul HH10D kommuniziert über eine I²C /TWI –Verbindung mit dem Mikrocontroller gibt zusätzlich eine Frequenz aus, woraus sich die relative Feuchte bestimmen lässt. Alle anderen Sensoren geben digitale Signale aus, so dass die Anbindung an den Mikrocontroller leicht zu realisieren ist.

Optional können alle Ein- und Ausgänge über ein Terminal(R232) und einem LCD- Display ausgegeben werden.

Präsenzmelder POPP McWATCH Professional EB 58595

Abbildung 1: Präsenzmelder Popp McWatch  Professional EB 58595

 

 

Abbildung 2: Flowmeter von GMR, transparent

 


Die Hardware

 

Für die Hardware werden neben der Beschaltung des Atmega8 die Eingänge über Optokoppler und die Ausgänge über Relais mit Verstärker und Schutzdioden galvanisch getrennt. Für den Controller werden zur Programmierung eine ISP-Schnittstelle und eine RS232-Schnittstelle für die Kommunikation mit dem Terminal benötigt.

Für die Spannungsversorgung werden 3 Spannungen von 12V, 5V und 3,3V benötigt. Alle 3 Spannungen werden mit Spannungsreglern stabilisiert und zuvor gleichgerichtet.

 

Abbildung 3: Schaltplan zum Controller

 

 

Anmerkung: Im Testaufbau wurden auf die 3,3V für das Sensor-Modul bewusst verzichtet, da sowohl der verbaute Timer LM 555 und das EEPROM 24C02 in dem Modul  für Spannungen von 5V ausgelegt sind. Auch auf die Status-LED’s  für die Ausgänge (Relais) wurde verzichtet, um die Übersichtlichkeit auf der Lochraster Platine zu wahren.

 

Die Relais wurden aus Sicherheitsgründen (230V)nicht auf der Platine verbaut, sondern als Anreihklemmen mit Steckdosen auf einer Hutschiene montiert.

 

 

Abbildung 4: Experimentieraufbau

 

 


Die Software

 

Die Programmierung des Atmega8 des Controllers erfolgt in Modulen in der Programmiersprache C. Alle Programme sind mit dem Editor „Programmer’s Notpad“ geschrieben und mit dem WINAVR-20100110 und des Compilers GCC 4.3.2 übersetzt worden. Neben den Standard-Bibliotheken werden zusätzliche Programm-Module und Bibliotheken benötigt. Da der Controller nicht viele Aufgaben zu erledigen hat und damit der Programmumfang sehr klein sein wird, wird von fertigem Programmcode abgesehen und alle benötigen Module selbst programmiert.

Unter anderem wird für die Anbindung an den PC eine serielle USART-Schnittstelle benötigt. Diese soll Kontrolle und späteren Fehlerfindung dienen, als auch bei der Entwicklung der Teilmodule verwendet werden.

Die Anbindung des Luftfeuchtigkeits-Modul benötigt die I²C/TWI-Schnittstelle um die Kalibrierungswerte aus dem EEPROM auszulesen und ein Programm-Code zur Messung der Frequenz. Die Frequenz lässt sich am besten mit den vorhandenen 16 Bit Timer im CTC (Clear Timer on Compare ) - Modus messen.

Die Steuerung der Ein- und Ausgänge werden in Funktionen implementiert, um eine Multitasking-Struktur zu erhalten. Dabei wird insbesondere auch auf Warte-Schleifen verzichtet.

 

Zunächst muss erst einmal festgelegt werden welche Schnittstellen und Ports des Atmega8 nötig sind und gebraucht werden. Für die Ausgänge werden 4 digitale PORT’s als Ausgang benötigt. Damit werden die 4 Relais für die Lampen und den Lüfter geschalten.

Für den Bewegungsmelder und die beiden Flowmeter-Eingänge werden noch 3 Eingänge, also 3 digitale PORT’s in Eingangsschaltung. Dafür sind die internen PullUp-Widerstände zu aktivieren. Für die nötigen Verzögerungen und Warteroutinen ist ein Timer zu programmieren.

Der Feuchtigkeitssensor HH10D benötigt zum Auslesen der Kalibrierungswerte die I²C bzw. TWI-Schnittstelle des Atmega8. Für die Berechnung der Luftfeuchtigkeit muss die Frequenz am Modul gemessen werden. Dazu wird die Frequenz an den ICP1-PORT angelegt und mit Hilfe des Input-Capture-Mode die Zeit zwischen 2 Impulsen gemessen. Um eine hohe Genauigkeit der Messfrequenz zu bekommen, wird die Messung mehrmals wiederholt und der Durchschnitt ermittelt. Die auftretenden Ungenauigkeiten durch eine eventuelle Frequenzungenauigkeit des internen Taktes und die Flankenglättung am HH10D-Ausgang durch den integrierten Tiefpass werden dadurch ausreichend minimiert. Da mit einem Takt von 8 MHz gearbeitet wird, kann man getrost 10000 Messungen veranschlagen, ohne dass es die Geschwindigkeit des gesamten Controllers merklich beeinträchtigt. Für die Zeitmessungen zwischen 2 Perioden der Ausgangsfrequenz im Input-Capture-Mode wird der 16 Bit Timer 1 entsprechend programmiert, d.h. kein Vorteiler, steigende Flanken müssen eingestellt werden. Für die Messung muss noch geprüft werden ob der 16Bit Timer ausreicht, oder ob Überläufe entstehen. Im Datenblatt ist die Frequenz von 5  bis 10 kHz angeben. Bei einem Prozessortakt von 8 MHz ergeben sich dann:

 

8.000.000 Hz / 5.000 Hz = 1600 und

8.000.000 Hz / 10.000 Hz = 800, also

 

Zählergebnisse von 1600 bis 500. Damit reicht die Größe des Timer-Registers von 16 Bit = 65536 aus. (Messung mit mind. 2-facher Frequenz(Shannon) ebenfalls erfüllt)

 

Wenn man das Rücksetzen des Timers bei der Messung der Zeit zwischen 2 Perioden ignoriert, man mit einer Zeile Code aus. Bei jeder steigenden Flanke(Event) an ICP1 wird ein Interrupt ausgelöst und das TCNC1-Register(16Bit), welches vom Systemtakt hoch gezählt wird, in das ICR1-Register kopiert. Man speichert den Wert zwischen und misst dann nur noch die Differenz zwischen gespeicherten und den folgenden Event. Tritt ein Rücksetzen des TCNC1-Register zwischen durch auf, dann ist der Wert im ICP1-Register kleiner als der letzte gespeicherte Wert und wird ignoriert, d.h. Differenzmessung wird nicht ausgeführt und nur der Wert für die nächste Messung gespeichert.

 

Zusätzlich wird der Timer1 noch zur Zeitsteuerung für Ausschaltverzögerungen genutzt. Um in etwa einen genauen Sekunden-Takt zu bekommen, wird die Vergleichsfunktion des 16 Bit-Timer1 genutzt, speziell die CTC-Funktion. Bei einem Wert im Vergleichs-Register OCR1 von 7999 wird dann etwa jede Millisekunde ein Interrupt ausgelöst und der Timer wieder auf Null gesetzt.

 

 

Die Ausschaltverzögerung des Präsenzmelders wurde auf Minimum gestellt und eine entsprechende Verzögerung softwareseitig implementiert.

Da der Feuchtesensor sehr empfindlich ist, wurde für die Lüftersteuerung eine Hystereschleife programmiert. Diese sogenannte 2-Punktregelung soll dafür sorgen,  dass der Lüfter bei kleinen Änderungen nicht ständig an- und ausschaltet.
I²C / TWI - Grundlagen

I²C steht für Inter IC Bus und wurde von Philips Semiconducters entwickelt. Es ist synchron arbeitender, serieller Zweidraht-Bus mit einer Datenleitung (SDA) und einer Taktleitung (SCL), welcher zu Kommunikation kurzer Entfernungen zwischen Einzelmodulen gedacht war. Aus Lizenzgründen heißt der I²C-Bus bei manchen Herstellern, wie Atmel, auch TWI und steht für Two Wire Interface. Technisch sind beide Systeme identisch.

 

Der TWI besitzt einen 7 Bit breiten Adressraum und kann somit 2^7 = 128 Geräte über den Bus verbinden. Das 8. Bit dient der Einstellung der Datenrichtung. Die Kommunikation der Geräte übernimmt üblicherweise ein Mastergerät, alle anderen sind dann Slave-Geräte und gehorchen den Master. Das Mastergerät steuert die Taktleitung (SCL) und gibt damit den verbindlichen Takt (Standard = 100kHz) und die Geschwindigkeit für alle Geräte vor. Die Übertragungsrate kann vom Master beliebig verlangsamt werden. Je langsame die Datenrate, desto besser ist die Abschirmung, desto größere Distanzen können überbrückt werden.

Die Datenleitung arbeitet bidirektional und überträgt die Daten vom Master zu den Slave-Geräten und andersherum.

 

 

 

Abbildung 5: Prinzipschaltbild TWI als I²C-Bus (Quelle: Atmel Doc. AVR315 )

 

Aufgrund der Schaltungstechnik ist eine Terminierung der Signalleitungen erforderlich. Diese wird über einen Widerstand von typischerweise von 5 bis 10 kOhm zwischen Leitung und Versorgungsspannung  gewährleistet. Aufgrund des offenen Kollektors bzw. Drain der Bustreiber ist die gesamte Leitung LOW, sobald mindestens ein Gerät LOW-Pegel anlegt.

Die Datenübertragung wird durch eine Start- und Stoppsequenz eingerahmt.

 

Abbildung 6: Protokollaufbau I²C/TWI

 

Innerhalb dieser Sequenz werden die Nutzdaten byteweise übertragen. Der Master beginnt dabei stets mit der Kommunikation. Zwischen den einzelnen Sequenzen können optional Bestätigungen des Empfängers gesendet werden. Da es keine Prüfsumme gibt, müssen die Anwendungen des I²C/TWI Busses störungsfrei sein.

 


I²C mittels TWI des Atmega8

Da für die Anbindung des Luftfeuchte-Modul HH10D du die Kalibrierungswerte aus den EEPROM nur ausgelesen werden müssen, beschränkt sich die Beschreibung im Folgenden nur auf den Master Receive Mode(MRM)

 

Für die serielle Datenübertragung mittels TWI stehen dem Atmega8 folgende 5 Register zur Verfügung:  TWI-Datenregister(TWDR),

                     TWI-Adressregister (TWAR),

                     TWI-Statusregister(TWSR),

                     TWI-Bitratenregister(TWBR),

                     TWI-Controlregister(TWCR).

 

Die Hardware des Atmega8 übernimmt das Senden und Empfangen, sowie die Empfangsbestätigungen(Ack) und setzt entsprechende Statusmeldungen im TWI-Control-Register. Man muss also nur noch die richtigen Anweisungen dem Controller mitteilen und nach einer Tabelle die Statusmeldungen auswerten. Das Auslesen der Daten aus dem EEPROM lässt sich in folgendem Diagramm darstellen:

 

 

 

 

Abbildung 7: EEPROM auslesen

 

 

 

Laut Datenblatt des Luftfeuchtigkeitsmodul HH10D handelt es sich um den verbauten EEPROM um ein Standardtyp 24C02, bzw. ein kompatiblen dazu. Das EEPROM besitzt demnach 2kBit Speicher in einer 256(32 Page) x 8 Bit Aufteilung. Der Chip ist 3 Bit adressierbar, d.h. es können 8 24C02 an einem Bus verwendet werden. Die Adresse ist vom Hersteller des HH10D auf 0x01 (A0=1) festgelegt worden. Damit ergeben sich für die Schreib- und Lesezugriffe die Adressen 0xA2 und 0xA3.

 

 

Abbildung 8: Adressierung des EEPROM 24C02

 

 

Die gespeicherten Daten liegen in den Speicherzellen(Word-Adressen) 10 bis 13 auf der Seite(Page) 0, d.h. die beiden Daten sens und offset liegen als 16 Bit Zahl vor. Das Auslesen erfolgt in einem Array mit anschließender Umrechnung in den Variablen sens und offset.

Mit diesen Variablen und der gemessenen Frequenz kann nun nach der gegebenen

 

Formel:           

 

die Luftfeuchtigkeit in Rh % berechnet werden.

 

 

Flowmeter

 

Als Durchflussmesser für Steuerung der LED-Leisten für Dusche und Waschbecken wurden 2 Flowmeter von GMR eingesetzt. Der Sensor misst das Durchflussvolumen mittels eines Hallsensors in Bezug auf die Drehung eines Flügelrades. Der Hallsensor gibt entsprechende Impulse aus, womit sich das Durchflussvolumen berechnen lässt.

Da lediglich erkannt werden soll, ob Wasser läuft, also ob die Dusche oder Waschbecken benutzt wird, kann auf die Frequenzmessung verzichtet werden. Es muss lediglich erkannt werden, ob ein Impuls vorliegt oder nicht. Dieses ist mit einem Mikrokontroller mit Input Capture Eingang sehr einfach. Benutz wurde hier wieder ein Atmega8(etwas überdimensioniert,  aber genug vorhanden). Alternativ wäre ein Controller der Atiny-Serie die Wahl.

Die Steuerung erkennt, ob ein Impuls vorhanden ist und setzt einen Ausgang auf HIGH.

Somit kann man den Ausgang direkt als digitalen Eingang (high- oder low- aktiv) für den Badezimmer-Controller benutzen. Der folgende Programm-Code zeigt die entsprechende Programmierung:


// ************************************************************************

// Mirko Richter

// Durchflusserkennung für FLowmeter von GMR

// Anschluss Flowmeter: rot: +12V

// schwarz:Masse

// weiß: Impulsausgang

// Anschluss Atmega8: Eingang: PORTB0 = ICP1

// Ausgang: PORTC 1 --> high aktiv

// PORTC 2 --> low aktiv

// ************************************************************************

#include <avr/io.h>

#include <avr/interrupt.h>

#include <stdlib.h>

//#define F_CPU 8000000UL

//#define F_CPU 4000000UL

#define F_CPU 1000000UL

// ************globale Variablen ******************************************

volatile unsigned char overflow = 0;

 

// Pulseerkennung

ISR(TIMER1_CAPT_vect)

{

overflow = 0;

PORTC &= ~(1<<PC1);

PORTC|=(1<<PC2);

}

// Überlaufzählung

ISR(TIMER1_OVF_vect)

{

overflow++;

}

// *************** Hauptprogramm ******************************************

int main(void)

{

//Timer1 einstellen auf Capture Input + Overflow + Interrupt

TCCR1B = (1<<ICES1) | (1<<CS10) ; // Input Capture Edge, kein PreScale

TIMSK = (1<<TICIE1) | (1<<TOIE1); // Interrupts akivieren, Capture + Overflow

PORTB|=(1<<DDB2);//Pullup PB2

DDRC|=(1<<DDC2)|(1<<DDC1);//zum Test

sei();

while(1)

{

if(overflow>(1000*F_CPU/(65536*1000)))// ca. 1000 ms

{

PORTC &= ~(1<<PC2);

PORTC|=(1<<PC1);

overflow=0;

}

} // end while

} // end main


Ausblick

 

Zusätzlich könnte man jeden Schaltzustand und die Messung der relativen Luftfeuchte auf ein LCD sichtbar machen.

Eine Anbindung an das Ethernet für die Integration in eine Hausautomation wäre ebenfalls möglich. Dafür eignen sich am besten preiswerte IC’s, wie der ENC28J60.

 

 

Geräteliste

 

1 Luftfeuchtigkeits-Modul HH10D von Pollin auf Platine

1 Eingangs/ Ausgangs -Platine (eigen)

2 Präsenzmelder Popp

2 Flowmeter AFS1-B von GMR mit Steuerungsmodul(eigen)

1 Atmega8 von Atmel auf  myAVR Experimentierboard 2.0 USB

4 Steckdosen auf Hutschiene

4 Relais Finder 12V/230V

Spannungsversorgung +12V/ +5 V stabilisiert

 

 

Literaturverweise

 

Dokument Atmel AVR315.pdf -TWI-Master

Dokument Atmel doc0180.pdf -TWI-Serial EEPROM

Dokument Atmel doc2486.pdf –Atmega8

Benutzerhandbuch HH10D von Pollin(siehe Anhang)

 

Anhang

 

main.c

Capture.c

Capture.h

usart.c

usart.h

switch.c

config.h

i2c.c

i2c.h

timer.c

timer.h


// ************************************************************************

//

// Autor: Mirko Richter

// Projekt: Badezimmercontroller Keller

// Controller: Atmega8

// Takt: interner Oszillator 8 MHz

// Platine: myAVR Board 2.0 USB

// Thema: Hausarbeit EBS

//

// ************************************************************************

#include <avr/io.h>

#include <string.h> //Stringfunktion

#include <stdlib.h> //Systemfunktion

#include "config.h" //Werte - Einstellungen

#include "usart.h"

#include "timer.h"

#include "i2c.h"

#include "Capture.h"

#include "switch.h"

//*****************globale variablen *************************************

//Kalibrierungsdaten

double sens;

double offset;

extern unsigned int status_vanity,status_shower;

//************************ Funktionen *************************************

// Tastenabfrage

// Eingänge überwachen

//***********************************************************************************

 

//*******************************Hauptprogramm **************************************

int main(void)

{

//Konfiguration der Ausgänge bzw. Eingänge

//definition erfolgt in der config.h

DDRC = OUTC; //Ausgänge an Port C

PORTB=PullupB;// Pullups einschalten an PORT B

DDRB|=(1<<DDB5);

PORTB|=(1<<PB5);

char conv[15];//Stringlänge für Textausgabe an USART

usart_init(BAUDRATE); // UART initialisieren

// UART-Ausgaben zur Programmkontrolle

usart_write("\n\rFeuchte-Sensor\n\r");

usart_write("Compiliert am "__DATE__" um "__TIME__"\r\n");// Systen-Datum,-Zeit

usart_write("Compiliert mit GCC Version "__VERSION__"\r\n\r\n");//Compiler

usart_write("Beginne Initialisierung!\r\n");

//Timer initialisieren

timer_init();

//Daten aus dem EEPROM des Sensors lesen

//nicht in der Hauptschleife, da nur einmal je Sensor ausgelesen werden muss

usart_write("EEPROM init...\r\n");

i2c_read_sens();// Auslesen des EEPROM mit der TWI-Schnittstelle

usart_write("->offset: %s\r\n",dtostrf(offset,12,4,conv));

usart_write("->sens: %s\r\n",dtostrf(sens,12,4,conv));

usart_write("Initialisierung Abgeschlossen\r\n");

//Globale Interrupts einschalten

sei();

 


//***********************************************************************************

//******************************Hauptschleife ***************************************

// -> nur noch Funktionsaufrufe

while(1)

{

feuchte_messen();//Frequenzmessung Rh %

vent_out(64,70); //Ventilator schalten::(Ausschaltwert,Einschaltwert)

delay_out(5, PIR,spots);// Übergabe:: (delay,Eingang, Ausgang)

status_vanity=vanity_out(flow2,vanity);// Übergabe:: (Eingang, Ausgang)

status_shower=duschlicht(flow1,showerlight);// Übergabe:: (ingang, Ausgang)

statusmsg(); // Zustandsüberblick

}

//***********************************************************************************

}//main end

 

 

// Capture.h

#ifndef _CAPTURE_H_

#define _CAPTURE_H_

#include <avr/io.h>

#include <string.h>

#include <stdlib.h>

#include <inttypes.h>

#include <avr/interrupt.h>

#include "usart.h"

#include <stdint.h>

#include "switch.h"

ISR( TIMER1_CAPT_vect );

#endif //_CAPTURE_H_

 

 

 

 

// Capture.c

#include "Capture.h"

 

extern unsigned int capdiff;

unsigned int lastcap;

 

ISR( TIMER1_CAPT_vect )

{

if(ICR1 > lastcap)

capdiff = ICR1 - lastcap;

lastcap = ICR1;

}


// switch.h

#ifndef _SWITCH_H_

#define _SWITCH_H_

#include <avr/io.h>

#include <string.h>

#include <stdlib.h>

#include "usart.h"

#include <stdint.h>

#include "config.h"

unsigned int tastenabfrage(void);

void delay_out(int delay, unsigned int input, unsigned int output);

void feuchte_messen(void);

void vent_out(int bottom, int top);

int duschlicht(unsigned int input, unsigned int output);

void blinklicht(void);

int vanity_out(unsigned int input, unsigned int output);

void statusmsg(void);

#endif //_SWITCH_H_

 

 

// switch.c

// Mirko Richter

#include "switch.h"

 

//*****************globale variablen *************************************

extern volatile unsigned long time; // zähler für millisec

extern volatile unsigned int sec; //Sekunden zähler

extern volatile unsigned int status_out;//3 Sekunden zähler

//Kalibrierungsdaten

extern double sens;

extern double offset;

// Zwischenwerte für die Frequenzmessung

unsigned int capdiff=0;

double tmp, tmp1, tmp2;

unsigned int capcnt=0;

unsigned char lastvalue,oldvalue;

unsigned int status_vent,status_spots,status_vanity,status_shower;

 

//*****************Funktionen*********************************************

 


//Ausschaltverzögerung

void delay_out(int delay, unsigned int input, unsigned int output)

{

static volatile unsigned int end;

static volatile unsigned int run=0;

if(!(PINB & (1<<input))) // wenn PINB = 0

{

if((status_vanity)|(status_shower))

{          

run=1;

}

if(run==1)

{

end = sec + delay;

run = 0; // Ausschaltverzögerung läuft

if(end>59)

{

end=end-60;

}

}

if(sec == end)

{

PORTC &= ~(1<<output); //PORT C PIN 3 ausschalten

status_spots=0;

}

}

else

{

PORTC |= (1<<output);//high activ

status_spots=1;

run=1; //Ausschaltverzögerung aktivieren

}

}

 

int vanity_out(unsigned int input, unsigned int output)

{

if(!(PINB & (1<<input))) //high activ

{

PORTC &= ~(1<<output); //PORT C PIN 3 ausschalten

//status_vanity=0;

return 0;

}

else

{

PORTC |= (1<<output);

//status_vanity=1;

return 1;

}

}

 


//Duschlicht ueber Flowmeter schalten

int duschlicht(unsigned int input, unsigned int output)

{

if((PINB & (1<< input)))// Test mit Taster(high activ)

{

PORTC |= (1<<output);

return 1;//Status duschlicht

}else

{

PORTC &= ~(1<<output);

return 0;//Status duschlicht

}

}

 

//Sensor Feuchte

void feuchte_messen(void)

{

capcnt++;

tmp += capdiff;

if(capcnt == 11280) //11280 Messungen

{

tmp = tmp/11280;

tmp1 = (8000000.0/tmp);

tmp2 = (((offset-tmp1)*sens)/4096.0);

lastvalue = (unsigned char) tmp2;

//usart_write("%s, ",dtostrf(tmp,12,4,conv));

//usart_write("%s, ",dtostrf(tmp1,12,4,conv));

//usart_write("%s RH,",dtostrf(tmp2,10,4,conv));

if(lastvalue!=oldvalue)

{

//usart_write("%i %% RH \r\n",lastvalue);

}

oldvalue=lastvalue;

capcnt=0;

tmp=0;

}

}

 

//Ventilator schalten mit Hysterese

void vent_out(int bottom, int top)

{

if(lastvalue>top)

{

PORTC |= (1<<vent);//Obere Schleife erreicht

status_vent=1;

}

if(lastvalue<bottom)

{

PORTC &= ~(1<<vent);//untere Schleife erreicht

status_vent=0;

}

}

 

 


//Statusmeldungen über Uart

void statusmsg(void)

{

if (status_out)

{

usart_write("Statusmeldungen\r\n");

usart_write("**********************\r\n");

usart_write("RH --> %i %% \r\n",lastvalue);

//Ventilator

if(status_vent)

{

usart_write("Ventilator --> ein \r\n");

}else

{

usart_write("Ventilator --> aus \r\n");

}

// Spots

if(status_spots)

{

usart_write("Spots --> ein \r\n");

}else

{

usart_write("Spots --> aus \r\n");

}

//Duschlicht

if(status_shower)

{

usart_write("Duschlicht --> ein \r\n");

}else

{

usart_write("Duschlicht --> aus \r\n");

}

// vanity

if(status_vanity)

{

usart_write("Vanitylicht --> ein \r\n");

}else

{

usart_write("Vanitylicht --> aus \r\n");

}

usart_write("\r\n");

status_out=0;

}

}

}


 

// i2c.h

#ifndef _I2C_H_

#define _I2C_H_

#include <avr/io.h>

#include <string.h>

#include <stdlib.h>

#include "usart.h"

#include <stdint.h>

void init_i2c(void);

unsigned char i2c_read_sens(void);

#endif //_I2C_H_

 

 

// i2c.c

#include "i2c.h"

// ******************nur Master Reiceive Mode***************************

// erstellt nach Atmel Atmega8(L)-Dokumentation

// Statustabellen S. 173/176 Standardcode S.170

 

extern double sens; // Sensor-Kalibrierungswert

 

extern double offset; // Offset-Kalibrierungswert

// Initialisieren der Hardware-Ebene:

// Vorteiler einstellen: max. Vorteilerfaktorund max. Teiler

// da nur einmal gelesen wird , langsamste geschwindigkeit = sicherste Übertragung

 

void init_i2c(void)

{

TWBR = 0xFF; // Vorteiler 255

TWSR = 0x03; // Teiler 4^3=64

// ergibt ca. 245 HZ

//TWI aktivieren

TWCR = ((1<<TWEN)) & ~((1<<TWSTO) | (1<<TWSTA));

}

 

// Kalibrierungsadten auslesen

// Befehlesfolge:

// START->Busadresse/Write->WORD-Adresse->Start->Busadresse/Read->Datenempfang->STOP

unsigned char i2c_read_sens(void)

{

unsigned char i;

unsigned char daten[4]; // Byte Array für 4 Zeichen

usart_write("Beginne Messdaten auszulesen...\r\n"); //Statusmeldung UART

// Start-Bedingung erzeugen

usart_write("Erzeuge Start-Bedingung... "); //Statusmeldung UART

TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);

while(!(TWCR & (1<<TWINT)));

// warten bis fertig

if((TWSR & 0xF8) == 0x08)

{

usart_write("erfolgreich!\r\n"); //Statusmeldung UART

} else

{

usart_write("FEHLGESCHLAGEN! Status: 0x%x\r\n",TWSR&0xF8);//Fehlermeldung mit

Statuscode UART

return 1;

}

// Schreibzugriff mit Slaveadresse senden senden

usart_write("Sende SLA/W... "); //Statusmeldung UART

TWDR = 0xA2; // Busadresse (R/W = 0)

TWCR = (1<<TWINT) | (1<<TWEN);

// warten bis fertig

while(!(TWCR & (1<<TWINT)));

if((TWSR & 0xF8) == 0x18)

{

 

usart_write("erfolgreich!\r\n"); //Statusmeldung UART

} else

{

usart_write("FEHLGESCHLAGEN! Status: 0x%x\r\n",TWSR&0xF8);//Fehlermeldung mit

Statuscode UART

return 1;

}

// 1-Byte Word-Adresse senden zum EEPROM

usart_write("Sende Wort-Adresse... ");

TWDR = 0x0A; // 1. Speicherzelle von sens

TWCR = (1<<TWINT) | (1<<TWEN);

// warten bis fertig

while(!(TWCR & (1<<TWINT)));

if((TWSR & 0xF8) == 0x28)

{

usart_write("erfolgreich!\r\n"); //Statusmeldung UART

} else

{

usart_write("FEHLGESCHLAGEN! Status: 0x%x\r\n",TWSR&0xF8);//Fehlermeldung mit

Statuscode UART

return 1;

}

// Startwiederholung, da noch nicht fertig

usart_write("Sende Repeated-Start... "); //Statusmeldung UART

TWCR |= (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);

// warten bis fertig

while(!(TWCR & (1<<TWINT)));

if((TWSR & 0xF8) == 0x10)

{

usart_write("erfolgreich!\r\n"); //Statusmeldung UART

} else

{

usart_write("FEHLGESCHLAGEN! Status: 0x%x\r\n",TWSR&0xF8);//Fehlermeldung mit

Statuscode UART

return 1;

}

// Lesezugriff mit Slaveadresse an Bus senden, da Slave gleich sendet

usart_write("Sende SLA/R... ");

TWDR = 0xA2|0x01; // Busadresse|R/W=1

TWCR = (1<<TWINT) | (1<<TWEN);

while(!(TWCR & (1<<TWINT))); // warten bis fertig

if((TWSR & 0xF8) == 0x40)

{

usart_write("erfolgreich!\r\n"); //Statusmeldung UART

} else

{

usart_write("FEHLGESCHLAGEN! Status: 0x%x\r\n",TWSR&0xF8); //Fehlermeldung mit

Statuscode UART

return 1;

}

 

usart_write("Werte lesen:\r\n"); //Statusmeldung UART

// 3 byte auslesen mit Rückmeldung(ACK)

for(i=0; i<3; i++)

{

// 3 Byte auslesen

TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN);

while(!(TWCR & (1<<TWINT)));// warten bis fertig

if((TWSR & 0xF8) == 0x50)

{

usart_write("0x%x: 0x%x\r\n",i,TWDR); //Statusmeldung UART

daten[i]=TWDR;

} else

{

usart_write("FEHLGESCHLAGEN! Status: 0x%x\r\n",TWSR&0xF8);//Fehlermeldung

mit Statuscode UART

return 1;

}

}

// letze Byte ohne Rückmeldung(NACK) siehe Atmel doc0180.pdf, S.12

TWCR = (1<<TWINT) | (0<<TWEA)| (1<<TWEN);

while(!(TWCR & (1<<TWINT))); // warten bis fertig

if((TWSR & 0xF8) == 0x58)

{

usart_write("0x%x: 0x%x\r\nFertig!\r\n",i,TWDR); //Statusmeldung UART

daten[i] = TWDR; // 4. Byte empfangen

// Werte addieeren und in Variablen kopieren

for(i=0; i<4; i++)

{

usart_write("%i: 0x%x\r\n",i,daten[i]);

}

sens = daten[0]*256+daten[1]; // 16 Bit-Wert in Speicherzelle 10/11 = sens

offset = daten[2]*256+daten[3]; // 16 Bit-Wert in Speicherzelle 12/13 = offset

} else

{

usart_write("FEHLGESCHLAGEN! Status: 0x%x\r\n",TWSR&0xF8);//Fehlermeldung mit

Statuscode UART

return 1;

}

// Stopp-Sequenz senden

usart_write("Stopp-Sequenz generieren... "); //Statusmeldung UART

TWCR |= (1<<TWINT) | (1<<TWSTO);

return 1;

}

 


//timer.h

#ifndef _TIMER_H

#define _TIMER_H

#include <avr/io.h>

#include "usart.h"

#define WTT 1200 //Watchdog Time

volatile unsigned long time;

volatile unsigned int sec;

volatile unsigned long time_watchdog;

void timer_init (void);

#endif //_TIMER_H

 

 

 

/*----------------------------------------------------------------------------

// timer.c

------------------------------------------------------------------------------*/

#include "timer.h"

volatile unsigned long time=0,time2=0;

volatile unsigned int sec;

volatile unsigned int status_out = 0;

//----------------------------------------------------------------------------

//Diese Routine startet und inizialisiert den Timer

void timer_init (void)

{

//CNC-Modus ohne Vorteiler

TCCR1B |= (1<<WGM12) | (1<<CS10 | 0<<CS11 | 0<<CS12);

TCNT1 = 0; // bei 0 beginnen

TCCR1B |= (1<<ICES1); // Input Capture Edge positive Flanke

TIMSK |= (1<<TICIE1); // Interrupts akivieren, Capture

OCR1A = 7999;//(F_CPU / 1024) - 1;

TIMSK |= (1 << OCIE1A);//Compare-Interrupt erlauben

return;

};

//----------------------------------------------------------------------------

//Timer Interrupt

ISR (TIMER1_COMPA_vect)

{

//milliSekunde um 1 erhöhen

time++;

time2++;

if(time==1000)

{

sec++;

time=0;

}

if(sec==60)

{

sec=0;

}

if(time2==3000)

{

status_out=1;

time2=0;

}

}


// usart.h

Copyright: Radig Ulrich mailto: mail@ulrichradig.de

Author: Radig Ulrich

//***************************************************************************

#ifndef _UART_H

#define _UART_H

#include "config.h"

#define USART_ECHO 1

#define BUFFER_SIZE 50

volatile unsigned int buffercounter;

char usart_rx_buffer[BUFFER_SIZE];

char *rx_buffer_pointer_in;

char *rx_buffer_pointer_out;

struct

{

volatile unsigned char usart_ready:1;

volatile unsigned char usart_rx_ovl:1;

volatile unsigned char usart_disable:1; //benötigt für ftp2com

}usart_status ;

//----------------------------------------------------------------------------

#include <avr/interrupt.h>

#include <avr/pgmspace.h>

#include <stdlib.h>

#include <stdarg.h>

#include <ctype.h>

#include <string.h>

#include <avr/io.h>

//----------------------------------------------------------------------------

//Anpassen der seriellen Schnittstellen Register wenn ein ATMega128 benutzt wird

#if defined (__AVR_ATmega128__)

#define USR UCSR0A

#define UCR UCSR0B

#define UDR UDR0

#define UBRR UBRR0L

#define USART_RX USART0_RX_vect

#endif

#if defined (__AVR_ATmega644__) || defined (__AVR_ATmega644P__)

#define USR UCSR0A

#define UCR UCSR0B

#define UBRR UBRR0L

#define EICR EICRB

#define TXEN TXEN0

#define RXEN RXEN0

#define RXCIE RXCIE0

#define UDR UDR0

#define UDRE UDRE0

#define USART_RX USART0_RX_vect

#endif

#if defined (__AVR_ATmega32__)

#define USR UCSRA

#define UCR UCSRB

#define UBRR UBRRL

#define EICR EICRB

#define USART_RX USART_RXC_vect

#endif

#if defined (__AVR_ATmega8__)

#define USR UCSRA

#define UCR UCSRB

#define UBRR UBRRL

#endif

#if defined (__AVR_ATmega88__)

#define USR UCSR0A

#define UCR UCSR0B

#define UBRR UBRR0L

#define TXEN TXEN0

#define UDR UDR0

#define UDRE UDRE0

#endif

//----------------------------------------------------------------------------

void usart_init(unsigned long baudrate);

void usart_write_char(char c);

void usart_write_str(char *str);

void usart_write_P (const char *Buffer,...);

#define usart_write(format, args...) usart_write_P(PSTR(format) , ## args)

//----------------------------------------------------------------------------

#endif //_UART_H

 

 

// usart.c

Copyright: Radig Ulrich mailto: mail@ulrichradig.de

Author: Radig Ulrich

 

#include "usart.h"

volatile unsigned int buffercounter = 0;

char usart_rx_buffer[BUFFER_SIZE];

char *rx_buffer_pointer_in = &usart_rx_buffer[0];

char *rx_buffer_pointer_out = &usart_rx_buffer[0];

//----------------------------------------------------------------------------

//Init serielle Schnittstelle

void usart_init(unsigned long baudrate)

{

UCSRA = (1<<U2X);

UCSRB = (1<<RXCIE) | (1<<RXEN) | (1<< TXEN);

UBRRL = 103;

UBRRH = 0;

usart_status.usart_disable = 0;

}

//----------------------------------------------------------------------------

//Routine für die Serielle Ausgabe eines Zeichens (Schnittstelle0)

void usart_write_char(char c)

{

if(!usart_status.usart_disable)

{

//Warten solange bis Zeichen gesendet wurde

while(!(USR & (1<<UDRE)));

//Ausgabe des Zeichens

UDR = c;

}

return;

}

//------------------------------------------------------------------------------

void usart_write_P (const char *Buffer,...)

{

va_list ap;

va_start (ap, Buffer);

int format_flag;

char str_buffer[10];

char str_null_buffer[10];

char move = 0;

char Base = 0;

int tmp = 0;

char by;

char *ptr;

//Ausgabe der Zeichen

for(;;)

{

by = pgm_read_byte(Buffer++);

if(by==0) break; // end of format string

if (by == '%')

{

by = pgm_read_byte(Buffer++);

if (isdigit(by)>0)

{

str_null_buffer[0] = by;

str_null_buffer[1] = '\0';

move = atoi(str_null_buffer);

by = pgm_read_byte(Buffer++);

}

switch (by)

{

case 's':

ptr = va_arg(ap,char *);

while(*ptr) { usart_write_char(*ptr++); }

break;

case 'b':

Base = 2;

goto ConversionLoop;

case 'c':

//Int to char

format_flag = va_arg(ap,int);

usart_write_char (format_flag++);

break;

case 'i':

Base = 10;

goto ConversionLoop;

case 'o':

Base = 8;

goto ConversionLoop;

case 'x':

Base = 16;

//****************************

ConversionLoop:

//****************************

itoa(va_arg(ap,int),str_buffer,Base);

int b=0;

while (str_buffer[b++] != 0){};

b--;

if (b<move)

{

move -=b;

for (tmp = 0;tmp<move;tmp++)

{

str_null_buffer[tmp] = '0';

}

//tmp ++;

str_null_buffer[tmp] = '\0';

strcat(str_null_buffer,str_buffer);

strcpy(str_buffer,str_null_buffer);

}

usart_write_str (str_buffer);

move =0;

break;

}

}

else

{

usart_write_char ( by );

}

}

va_end(ap);

}

//----------------------------------------------------------------------------

//Ausgabe eines Strings

void usart_write_str(char *str)

{

while (*str)

{

usart_write_char(*str++);

}

}

//----------------------------------------------------------------------------

//Empfang eines Zeichens

ISR (USART_RXC_vect)

{

if(!usart_status.usart_disable)

{

unsigned char receive_char;

receive_char = (UDR);

#if USART_ECHO

usart_write_char(receive_char);

#endif

if (usart_status.usart_ready)

{

usart_status.usart_rx_ovl = 1;

return;

}

if (receive_char == 0x08)

{

if (buffercounter) buffercounter--;

return;

}

if (receive_char == '\r' && (!(usart_rx_buffer[buffercounter-1] == '\\')))

{

usart_rx_buffer[buffercounter] = 0;

buffercounter = 0;

usart_status.usart_ready = 1;

return;

}

if (buffercounter < BUFFER_SIZE - 1)

{

usart_rx_buffer[buffercounter++] = receive_char;

}

}

else

{

if(rx_buffer_pointer_in == (rx_buffer_pointer_out - 1))

{

//Datenverlust

return;

}

*rx_buffer_pointer_in++ = UDR;

if (rx_buffer_pointer_in == &usart_rx_buffer[BUFFER_SIZE-1])

{

rx_buffer_pointer_in = &usart_rx_buffer[0];

}

}

return;

}