| Um die Schaltung mit Hilfe von CV zu initialissieren reicht
folgender Code aus : |
#include <90s2313.h>
// I²C Bus functions
#asm
.equ __i2c_port=0x12
.equ __sda_bit=5
.equ __scl_bit=4
#endasm
#include
#include
#include
#include
#define DISPLAY_ADRESS 0x74
#define CHIP_ADRESS 0xA0
... mehr INIT Code der später erklärt wird ...
// I²C Bus initialization
i2c_init();
|
| Nun ist der Bus initialisiert und bereit. Das ist doch klasse
oder ? Für das Ansteuern des Displays habe ich ein paar Funktionen
geschrieben, die den weiteren Programmablauf simplifizieren sollen.
|
| Die Funktion initLCD() zeigt, wie im Bild weiter
oben gesehen, den korrekten Datentransfer auf dem I2C Bus. Ein einleitendes
Startkommando gefolgt von der Adresse des I2C Slaves eröffnen das Senden.
Danach folgen die Nutzdaten, bis der Bus wieder mit einem Stopkommando freigegeben
wird. |
void initLCD() { //Init Display
i2c_start();
i2c_write(DISPLAY_ADRESS);
i2c_write(0x00);
i2c_write(0x25);
i2c_write(0x06);
i2c_write(0x24);
i2c_write(0x0f);
i2c_write(0x84);
i2c_stop();
delay_ms(PAUSE);
}
|
| Diese Funktion erlaubt es aus dem Programmablauf
Kontrollkommandos an das Display zu schicken. Diese sind notwendig für das
löschen des Display oder setzten der aktiven Zeile ... In dem Datenblatt
zum Display findet ihr all die Kommandos. |
// Erlaubt Kontrollkomandos an das LCD zu schicken
void ctrlLCD(unsigned char data) {
i2c_start();
i2c_write(DISPLAY_ADRESS);
i2c_write(0x00);
i2c_write(data);
i2c_stop();
delay_ms(PAUSE);
}
|
| Diese Funktionen ermöglichen das Ausgeben
einzelner Zeichen, oder eines ganzen Strings auf das Display. Die Funktion
writeLCD schreibt die Daten RAW, wohingegen die Funktionen writeCharLCD
und writeLineLCD die ASCII Zeichen korrekt umrechnen, um sie auf den Zeichensatz
des Displays anzupassen |
// Schreibe ein Zeichen auf das Display
// Die Position muß vorher festliegen
void writeLCD(unsigned char data) {
// Schreibe Zeichen auf LCD
i2c_start();
i2c_write(DISPLAY_ADRESS);
i2c_write(0x40);
i2c_write(data);
i2c_stop();
delay_ms(PAUSE);
}
// schreibt ein einzelnes zeichen auf das LCD Display
void writeCharLCD(unsigned char data) {
writeLCD(data+0x80);
}
// Schreibt einen String aus dem RAM auf das LCD Display
void writeLineLCD(unsigned char *data) {
int i;
for(i=0;i
|
| Um nun die Chipkarte ansprechen zu können,
habe ich mir drei kleine Funktionen geschrieben, die diese Aufgabe übernehmen..
Die Kommentare zu den Funktionen sagen eigentlich schon alles. Die Funktionen,
die keine Adresse als Parameter erwarten nutzen den Umstand aus, dass der
Chip nach jedem Zugriff ein Autoincrement durchführt. Schreiben oder
lesen wir in folge auf dem Chip, benötigen wir das Adressargument nur
einmal. |
// Schreibe ein Zeichen in Chipmemory
// Die Position wird in Adress angegeben
unsigned char writeChipA(unsigned char adr,unsigned char data) {
unsigned char ret;
// Schreibe Zeichen in Chipmemory
i2c_start();
i2c_write(CHIP_ADRESS);
i2c_write(adr);
ret=i2c_write(data);
i2c_stop();
delay_ms(PAUSE);
return ret;
}
// Ließt ein Zeichen aus dem Chip ab angegebener Adresse // Die Position wird in Adress angegeben
unsigned char readChipA(unsigned char adr) {
unsigned char data;
// Lese Zeichen aus Chip
i2c_start();
i2c_write(CHIP_ADRESS); // +1 für lesen
i2c_write(adr); // Memory Adresse
i2c_start();
i2c_write(CHIP_ADRESS+1); // +1 für lesen
data=i2c_read(0); // Nun auslesen 1=mit ACK
i2c_stop();
delay_ms(PAUSE);
return data;
}
// Ließt ein Zeichen aus dem Chip ab intern gespeicherter Adresse
// Die Position muß vorher festliegen ( durch readChipA() )
unsigned char readChip() {
unsigned char data;
// Schreibe Zeichen auf LCD
i2c_start();
i2c_write(CHIP_ADRESS+1); // +1 für lesen
data=i2c_read(0);
i2c_stop();
delay_ms(PAUSE);
return data;
}
|
| Jetz bleibt nur noch zu erwähnen, dass eine kleine Interruptroutin
eim Hintergrund die Sekunden zählt und so ein kleines Softwareuhrwerk
bildet. Folgender Code realisiert dies. |
// Timer 0 overflow interrupt service routine
interrupt [TIM0_OVF] void timer0_ovf_isr(void) {
static int count=0;
// Uhrwerk
count++;
// halbe Frequenz (15625) plus Korrektur wegen vorlaufen (2)
if(count==15627) {
sekunde++;
count=0;
}
if(sekunde==60) {
minute++;
sekunde=0;
}
if(minute==60) {
stunde++;
minute=0;
}
if(stunde==24) {
stunde=0;
}
// Uhrwerk ende
TCNT0=0xFE;
}
|
|
Das Hauptprogramm versucht nun mittels der oben beschriebenen Funktionen
Daten von der eingesteckten I2C karte zu lesen. Aber vorsicht ! Das Programm
schreibt auch Daten zurück. Also darf man nur Karten verwenden, deren
Inhalt nicht mehr benötigt wird.
Ist die Karte also eingesteckt, wenn das System bootet, versucht das
Programm Daten zu lesen. Die gelesenen Daten werden als Uhrzeit interpretiert
und angezeigt. Im weiteren Verlauf wird die Uhrzeit auf dem Chip gespeichert.
Trennt man das System von der Stromquelle und schließt es später
wieder an, wird die Uhrzeit an der letzten Uhrzeit weitergezählt.
Schlußendlich wird in einer Dauer while Schleife die Uhrzeit regelmäßig
auf das I2C Display ausgegeben und auf den Chip gespeichert. Das ist bestimmt
nicht ergonomisch, aber für Tests ganz ok.
|
// Sekundenzähler initialisieren
sekunde=0;
minute=0;
stunde=0;
altsekunde=0;
// Global enable interrupts
#asm("sei")
PORTB=0xFF;
initLCD();
ctrlLCD(CLEARLCD);
ctrlLCD(LINE1);
// Prüfe ob Chipkarte reagiert
if (writeChipA(0x05,'H')==1) {
// Chipkarte reagiert nun Daten auslesen
writeChipA(0x06,'a');
writeChipA(0x07,'l');
writeChipA(0x08,'l');
writeChipA(0x09,'o');
writeChipA(0x0a,' ');
writeChipA(0x0b,' ');
data=readChipA(0x05);
writeCharLCD(data);
for(i=0;i<6;i++) {
data=readChip();
writeCharLCD(data);
}
stunde=readChipA(0x10);
minute=readChip();
sekunde=readChip();
} else {
// Keine Chipkarte eingesteckt
writeLinefLCD("no Card");
PORTB=0xFE;
}
// Auf zweite Zeile setzen für Zeitausgabe
ctrlLCD(LINE2);
while (1) {
if(sekunde!=altsekunde) {
sprintf(zeit,"%02u:%02u:%02u",stunde,minute,sekunde);
ctrlLCD(LINE2);
writeLineLCD(zeit);
altsekunde=sekunde;
writeChipA(0x10,stunde);
writeChipA(0x11,minute);
writeChipA(0x12,sekunde);
}
};
}
|