Hi, ich bastele gerade an einem Digitaluhrprojekt. Dabei wird die Uhrzeit in einem Uhrenbaustein aus dem Hause Dallas (DS1302) gespeichert. Dieser wird mit Hilfe eines GoldCap-Kondensators (1F) bei Laune gehalten, falls die Energieversorgung mal unterbrochen wird. Datum und Uhrzeit aus dem DS1302 werden auf einem grafischen LCD 128x64 Pixel dargestellt. Und das alles wird von einem Atmel 8052 mit 24kbyte Speicher kontrolliert. Soweit so gut. Nun soll das ganze um einen Drehimpulsgeber erweitert werden, damit die Uhrzeit auch ohne Hilfe eines PCs (serielle Schnittstelle) gestellt werden kann. Der Drehimpulsgeber lässt sich nach links und rechts drehen und zusätzlich drücken Ich möchte durch einen Druck auf den Drehimpulsgeber in das Uhr stellen Menü gelangen und dann die Einzelnen Positionen mit dem drehen nach links (minus) bzw. rechts (plus) einstellen. Im Moment ist der Druck auf den Drehimpulsgeber am externen Interrupt0 angeschlossen und die beiden Kontakte für links und rechts jeweils an Port0.0 bzw P0.1 Doch leider funktioniert das ganze nicht so wie geplant. Ich hatte mir überlegt bei jedem druck auf den Taster einen Interrupt auszulösen, der eine Variable hoch zählt so nach dem Motto einmal drücken Uhr stellen Modus 2. Mal drücken Tag stellen 3. Monat stellen 4. Jahr stellen und so weiter . Der Interrupt löst auch aus nur leider hat die Zählvariable dann nicht den Wert 1 oder 2 oder 3 sondern ehr in der Größenordnung 26000. Nach stöbern in diesem netten Forum habe ich herausgefunden, dass der externe Interrupt bei gedrückter Tast ein mal pro 1us auslöst . Ist es dann überhaupt sinnvoll das mit einem externen Interrupt zu lösen? Oder was würdet ihr vorschlagen? Die beiden Zustände mit denen mal herausbekommt ob der Drehimpulsgeber nach links oder nach rechts gedreht worden ist hole ich einmal pro 1ms über einen Timer0 Interrupt ab, aber das schein auch noch nicht so wirklich zu funzen Ich würde mich sehr über ein paar Anregungen von euch freuen ich bin relativ neu auf diesem Gebiet und programmiere mit C. MfG Nils etwas Quellcode zur Erklärung: #include <stdio.h> #include "mc.h" #include "Print.h" #include "DS1302.H" #include "display.h" #include "control.h" unsigned char M; int lcd_refresh, setclk, abx,set =0; long int i = 0; /** * Timer0 Interrupt-Routine. * * Regelt die Erneuerung der LCD-Anzeige ueber den * Wert von lcd_refresh. Timer0 löst alle 20ms einen * Interrupt aus. * */ void timer0() interrupt 1 { int ab,yy=0; lcd_refresh++; // regelt den LCD-Refesh //Drehimpulsgeber drehung abfragen cw bzw. ccw //--------------------------------------------------------- ab = (A_ & B_); if (ab != abx) { yy = ab ^ abx; // XOR if ((ab == 0) | (ab == 3)) // a = b { if (yy == 1);// set--; if (yy == 2) set++; } else // a != b { if (yy == 2);// set--; if (yy == 1) set++; } } abx = ab; //--------------------------------------------------------- TR0 = 0; //var Timer0 stoppen TL0 = Timer0L; // Startwerte Timer0 Low TH0 = Timer0H; // Startwerte Timer0 High TR0 = 1; // Timer0 starten } void EXT_IRS_0() interrupt 0 { IT0=0; setclk++; } /** Uhrzeit/Datum-Ausgabe auf LCD. * * Erstellt aus dem Struct Uhr die Chararrays zeit und datum * und gibt sie auf dem LCD aus. * */ void wr_clock(void) { date_s uhr; // Datumsstruckt Uhr definiert in DS1302.h unsigned char *days[] = {"Mo","Di","Mi","Do","Fr","Sa","So"}; unsigned char tag[3]; // temorärer Array zum aufnehmen des aktuellen Tages unsigned char zeit[9]; // aktuelle Zeit[std:mim:sec] unsigned char datum[9]; // aktuelles Datum [dd:mm:yy] read_ext_clk_regs(&uhr); // DS1302 Register auslesen. sprintf(zeit,"%02bx:%02bx:%02bx",uhr.hr,uhr.min,uhr.sec); // Aktuelle Uhrzeit in Zeitarry schreiben sprintf(datum,"%02bx.%02bx.%02bx", uhr.date, uhr.mn, uhr.yr); // Aktuelles Datum in Datumsarry schreiben sprintf(tag,"%s",days[uhr.dy-1]); // Aktuellen Tag ins Tagesarray schreiben if(setclk==0){ Print3(zeit,1,3,weiss); // Uhrzeit ans LCD senden Print2(tag,5,3,weiss); // Wochentag ans LCD senden Print2(datum,5,35,weiss); // Datum ans LCD senden //printf("%s\n",zeit); // Uhrzeit auf der Konsole ausgeben } } /** Uhrzeit/Datum stellen * * Liest die Angaben über Datum und Uhrzeit von der Tastatur ein * und veranlasst das schreiben in die Register des DS1302. * * */ void set_clock(){ date_s uhr; unsigned char tmp[3]; printf("\nUhr wird gestellt\n"); ClearLCD(); set=0; //zurücksetzen Print2("komplett stellen ?",0,0,weiss); Print2("Ja ->rechts",2,0,weiss); Print2("Nein ->links ",4,0,weiss); read_ext_clk_regs(&uhr);//aktuelle Uhrzeit/Datum holen while(set==0); // auf entscheidung warten ClearLCD(); printf("setclk: %d",setclk); if(set>0){ // Uhr komplett stellen while(setclk==1){// ----------------------------------Datum Print2(" ",5,41,weiss);//Jahr überschreiben while(lcd_refresh>=10); //blinken, pause 100ms if(set>=99)set=0; //Jahr zurücksetzten if(set<=-1)set=98; // uhr.yr=set; //Drehimpuls holen sprintf(tmp,"%02bx",uhr.yr); Print2(tmp,5,41,weiss); //geändertes Datum auf Display } while(setclk==2){ // ----------------------------------Datum //read_ext_clk_regs(&uhr); //aktuelle Uhrzeit/Datum holen Print2(" ",5,38,weiss); //Monat überschreiben while(lcd_refresh>=10); //blinken, pause 100ms if(set>=12)set=0; //Monat zurücksetzten if(set<=-1)set=11;// uhr.mn=set; //Drehimpuls holen sprintf(tmp,"%02bx",uhr.mn); Print2(tmp,5,38,weiss); //geändertes Datum auf Display } while(setclk==3){ // ----------------------------------Datum //read_ext_clk_regs(&uhr); //aktuelle Uhrzeit/Datum holen Print2(" ",5,35,weiss);//Tag überschreiben while(lcd_refresh>=10); //blinken, pause 100ms if(set>=31)set=0; //Tag zurücksetzten if(set<=-1)set=30; // uhr.dy=set; //Drehimpuls holen sprintf(tmp,"%02bx",uhr.dy); Print2(tmp,5,35,weiss); //geändertes Datum auf Display } while(setclk==4){ // ----------------------------------Uhrzeit //read_ext_clk_regs(&uhr); //aktuelle Uhrzeit/Datum holen Print2(" ",1,3,weiss); //Stunde überschreiben while(lcd_refresh>=10); //blinken, pause 100ms if(set>=24)set=0; //Stunde zurücksetzten if(set<=-1)set=23;// uhr.hr=set; //Drehimpuls holen sprintf(tmp,"%02bx",uhr.hr); Print2(tmp,1,3,weiss); //geändertes Datum auf Display } while(setclk==5){ // ----------------------------------Uhrzeit //read_ext_clk_regs(&uhr); //aktuelle Uhrzeit/Datum holen Print2(" ",1,6,weiss); //Minute überschreiben while(lcd_refresh>=10); //blinken, pause 100ms if(set>=60)set=0; //Minute zurücksetzten if(set<=-1)set=59;// uhr.mn=set; //Drehimpuls holen sprintf(tmp,"%02bx",uhr.mn); Print2(tmp,1,6,weiss); //geändertes Datum auf Display } while(setclk==6){ // ----------------------------------Uhrzeit //read_ext_clk_regs(&uhr); //aktuelle Uhrzeit/Datum holen Print2(" ",1,9,weiss); //Sekunden überschreiben while(lcd_refresh>=10); //blinken, pause 100ms if(set>=60)set=0; //Sekunden zurücksetzten if(set<=-1)set=59; // uhr.sec=set; //Drehimpuls holen sprintf(tmp,"%02bx",uhr.sec); Print2(tmp,5,35,weiss); //geändertes Datum auf Display } } else{ while(setclk<=1){ // ----------------------------------Uhrzeit //read_ext_clk_regs(&uhr); //aktuelle Uhrzeit/Datum holen Print2(" ",1,3,weiss); //Minute überschreiben while(lcd_refresh>=10); //blinken, pause 100ms if(set>=24)set=0; //Minute zurücksetzten if(set<=-1)set=23; // uhr.hr=set; //Drehimpuls holen sprintf(tmp,"%02bx",uhr.hr); Print2(tmp,1,3,weiss); //geändertes Datum auf Display } } setclk=0; write_ext_clk_regs(&uhr); } /** Kommunikation zwischen DS1302 und LC-Display * * Control.c initialisiert dem Atmel 8052 Kontroller und kontolliert die Kommunikation * zwischem dem Dalls DS1302 Uhrenbaustein und dem LC-Display KS1802. * @version $Revision: 1.0 $ $Name: Hoffmann / Anger $ */ void main (void) { //------------------------- Timer1 Initialisierung für Baudrate TMOD = 0x21; // Timer 1 = 8-Bit-Timer mit Auto-Reload-Modus. // Timer 0 = 16-Bit-Timer. TL1 = Timer1L; // 19200 Baud bei 22,118 MHz TH1 = Timer1H; // Nachladewert fuer TL1 TR1 = 1; // Timer 1 starten SCON = 0x52; // Freigabe von Sender und Empfaenger; Betriebsart: Modus 1 TL0 = Timer0L; // Startwert für Timer0 0x7000 TH0 = Timer0H; // dezimal 28672 => Interrupt alle 20ms ET0 = 1; // Timer0 Interrupt einschalten EA = 1; // Interrupt einschalten TR0 = 1; // Timer 0 starten EX0 = 1; // Externer Interrupt 0 einschalten IE0 = 1; // Interrupt0 reagiert auf negative Flanke ClearLCD(); // LCD löschen printf("Neustart"); Print3(" N.H&B.A",1,0,weiss); Print2(">>DigiWatch<<",6,0,weiss); while(lcd_refresh <200) ; // Warteschleife für Projekt Startseite (2 sec) ClearLCD(); while(1) { if (lcd_refresh >= 20) // Display aktuallisieren { // 10 entspricht ca. 20ms wr_clock(); lcd_refresh =0; printf("%d\n",set); } if(setclk>0) set_clock(); } }
Derartig langsame Vorgaenge wuerde ich nicht uber einen IRQ machen. Du solltest lieber einen TimerIRQ aufsetzen der so 100-200mal pro Sekunde zuschlaegt. Darin fragst du die beiden Eingaenge des Winkelencoders ab um die Drehbewegung und ihre Richtung zu erkennen. Ausserdem kannst du darin noch ein Variable hochzaehlen und dann bei ueberlauft so 10mal pro Sekunde kucken ob jemand nun eine Taste gedrueckt hat. Olaf
Schau dir mal die AVRLIB von Pascal Stang an. Da sind Routine für einen Drehimpulsgeber drin.(encoder.c) http://hubbard.engr.scu.edu/avr/avrlib/index.html Grüsse und frohe Ostern
Bezüglich Drehgeber-Entprellung schau mal hier: http://www.mikrocontroller.net/forum/read-4-37992.html#new Und Tastenentprellung hier: http://www.mikrocontroller.net/forum/read-4-310276.html#new Peter
Hi, vielen dank für die schnelle Rückmeldung. Ich hätte noch eine andere Frage.... für die Tickeinstellungen der Timerinterrupts habe ich bis jetzt ein kleines Programm benutzt (siehe Anhang). Dort gibt man den Takt seines Quarzes an und die gewünschte Frequenz für den Interrupt... ich würde aber gern verstehen was da genau passiert, denn wenn ich ein Wert von 1kHz einstelle (1ms Tick) bekomme ich hohe Rundungsfehler und das hochzählen der "lcd_refresh" Variable (oder das abfragen der selbigen) fukntionieren nicht mehr richtig ... ich vermute das der Timer schneller überläuft als die Abfrage das erkennt ..... ich hatte vor den Timer0 für die abfrage der Werte des Drehimpulsgebers (laut Datenblatt soll die Abfrage ein mal pro 1ms geschehen) und die Steuerung des LCD-refreshs (soll ca alle 100ms - 200 ms aktuallisiert werden) zu benutzen .. da ich Timer1 schon für die serielle Schnittstelle draufgeht. Ich habe diesbezüglich schon im Forum herumgesucht und einiges mit vorteilern etc. gelesen ... aber ehrlich gesagt hab ich das noch nicht ganz geblickt ... warum wird die Frequenz des Quarzes noch x mal durch irgendwelche werte gelteilt ? wenn ich den Kehrwert der Frequenz nehme hab ich doch die Zeit die ein Zyklus dauert oder nicht ... ?? Vielleicht kann mir das ja mal einer von euch an dem Beispiel meines Codes erklähren ... Ich benutze einen 22.118 MHz Quarz .... ich wünsche euch allen frohe Ostern ;) MfG Nils
"denn wenn ich ein Wert von 1kHz einstelle (1ms Tick) bekomme ich hohe Rundungsfehler und das hochzählen der "lcd_refresh" Variable (oder das abfragen der selbigen) fukntionieren nicht mehr richtig" Das ist korrekt so, denn Du machst mehrere Fehler. 1. Du verwendest int, wo unsigned char oder unsigned int ausreichen. Damit zwingst Du den 8-Bitter, alles umständlich mit mehreren Byteoperationen zu machen, bzw. noch eine zusätzliche Vorzeichenberechnung. 2. Du setzt den Timer, nachdem Du erstmal ne Menge Code ausgeführt hast, und damit verlängert sich alles um dessen Ausführungszeit und um die Interrupteintrittszeit sowieso. 3. Du verwendest int Variablen in Interrupts und im Main, was schiefgehen muß, wenn der main-Zugriff nicht unter Interruptsperre erfolgt. 16Bit-Zugriffe sind aufm 8-Bitter grundsätzlich nicht atomar ! Und generell sind C-Statements erstmal nicht atomar. Schau Dir einfach mal das Assemblerlisting an, zu welchen Umständlichkeiten Du den Complier zwingst. Und lies Dir den C51-Primer durch, wie man es besser macht. Peter
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.