Hallo, ich bin neu auf dem Gebiet und habe mir daher die Aufgabe gestellt eine Uhr zu bauen (mittel 7-Segmentanzeigen). Das ganze funktioniert als Brettaufbau an sich auch gut, nur dass die Uhr zu langsam läuft. Daten: - 8Mhz interner Osc. (laut Datenblatt und AVR Studio) - Prescaler auf 1024 - 8Bit-Counter Das macht also rund 30 Überläufe je Sekunde (256). Eine entsprechende Zählvariable sorgt bei erreichten 30 für die Inkrementierung der Zeitvariable (sek). Nun ändert sich die Sekundenanzeige aber nur etwa alle 3-4 sek. Hat jemand einen Vorschlag? Das ganze riecht nach geringerem Takt als 8Mhz, ich habe aber nie etwas am Takt geändert. Anbei noch eine Frage: Zählt der Timer auch dann hoch, wenn gerade eine ISR (Timer Overflow) abläuft, oder bleibt er währenddessen konst.? Vielen Dank schonmal...
Henry schrieb:
> Hat jemand einen Vorschlag?
Ja. Schaltplan und Code posten.
Glaskugel ist leider gerade in der Abkühlphase.
Henry schrieb: > Zählt der Timer auch dann hoch, wenn gerade eine > ISR (Timer Overflow) abläuft, oder bleibt er währenddessen konst.? Ja.
Wenn du deinen Code zeigst bekommst du mehr Hilfe. Interner Takt ist eh zu Ungenau.
Der interne RC-Oszillator ist eigentlich nicht genau genug für eine Uhr. Nimm lieber einen Quartz.
Henry schrieb: > Brettaufbau an sich auch gut, nur dass die Uhr zu langsam läuft. > > Daten: > - 8Mhz interner Osc. (laut Datenblatt und AVR Studio) Aus diesem Grund wird deine Uhr auch nie einigermassen genau gehen > Das macht also rund 30 Überläufe je Sekunde (256). Eine entsprechende Da ist die nächste Fehlerquelle > Nun ändert sich die Sekundenanzeige aber nur etwa alle 3-4 sek. Das ist allerdings zu heftig um es mit den beiden Fehlerquellen zu erklären. > Das ganze riecht nach geringerem Takt als 8Mhz, allerdings > ich habe aber nie etwas am Takt geändert. macht nichts. Trotzdem kontrollieren. (Endlosschleife und mit einem _delay_ms dazwischen eine LED blinken lassen. Optisch kontrollieren ob die delay Zeiten stimmen können. 500ms kann man auch optisch kontrollieren. Ob eine Led pro Sekunde 1 mal oder 3 blinkt, sieht auch ein Blinder)
Erstmal herzlichen Dank für die Antworten. Das die Uhr nicht genau geht ist bei den Voraussetzungen ja klar, aber wie schon erwidert können alle Abweichungen nicht ein derartigen Fehler erzeugen. Den Schaltplan müsste ich erst noch "zeichnen". Daher poste ich zunächst nur den Code: Ich gebe zu bedenken, dass ich harter Anfänger bin - der Programmierstil ist mit Sicherheit verbesserungswürdig: #include <avr/io.h> #include <stdint.h> #include <avr/interrupt.h> #include <util/delay.h> #ifndef F_CPU #define F_CPU 8000000UL #endif volatile uint8_t min=0,hrs=0, i; uint16_t counter=0; uint8_t segment_array[4] ={0,0,0,0}; uint8_t debounce_h (volatile uint8_t *reg, uint8_t pin); //PB6 uint8_t debounce_m (volatile uint8_t *reg, uint8_t pin); //PB7 void integer_split (volatile uint8_t stunden, uint8_t minuten); int main (void) { DDRB= 0xFF; DDRD= 0xFF; //Transistoren DDRC &= ~(1<<PC5) | (1<<PC4); // Taster-Eingänge TCCR0B |= ((1<<CS02)|(1<<CS00)); TIMSK0 |= (1<<TOIE0); PCICR |= (1<<PCIE1); PCMSK1 |= (1<<PCINT13) | (1<<PCINT12); // PCINT 5 und 4 aktiviert sei(); while (1) { for (i=0;i<=3; i++) { PORTD = 0x00; PORTB = 0x00; switch (i) { case 0: PORTD |= (1<<PD0); break; case 1: PORTD |= (1<<PD1); break; case 2: PORTD |= (1<<PD2); break; case 3: PORTD |= (1<<PD3); break; } switch (segment_array[i]) { case 0: PORTB = 0b11111100; break; case 1: PORTB = 0b01100000; break; case 2: PORTB = 0b11011010; break; case 3: PORTB = 0b11110010; break; case 4: PORTB = 0b01100110; break; case 5: PORTB = 0b10110110; break; case 6: PORTB = 0b10111110; break; case 7: PORTB = 0b11100000; break; case 8: PORTB = 0b11111110; break; case 9: PORTB = 0b11110110; break; } } } return 0; } uint8_t debounce_h (volatile uint8_t *reg, uint8_t pin) {cli(); if ((*reg & (1<<pin))!=0) { _delay_ms(30); _delay_ms(30); if ((*reg & (1<<pin))!=0) { _delay_ms(30); _delay_ms(30); return 1; } } return 0; sei(); } uint8_t debounce_m (volatile uint8_t *reg, uint8_t pin) {cli(); if ((*reg & (1<<pin))!=0) { _delay_ms(30); _delay_ms(30); if ((*reg & (1<<pin))!=0) { _delay_ms(30); _delay_ms(30); return 1; } } return 0; sei(); } void integer_split (volatile uint8_t stunden, uint8_t minuten) { uint8_t k; k = stunden - (stunden%10); segment_array[0]= k/10; segment_array[1]= stunden%10; k = minuten - (minuten%10); segment_array[2]= k/10; segment_array[3]=minuten%10; } ISR(TIMER0_OVF_vect) {counter++; if (counter > 30) { if (min<59) { min++; } else { min = 0; hrs++; } if (hrs>23) {hrs=0; } counter=0; integer_split(hrs, min); } } ISR (PCINT1_vect) { if (debounce_h(&PINC, PC5)==1) { hrs++;} if (debounce_m(&PINC, PC4)==1) { min++;} integer_split(hrs, min); } Im Code ist auch noch ein Fehler, die Variable "min" soll später mal die Minuten darstellen (habe nur vier Anzeigen HH:MM) Den Vorschlag mit dem Delay werde ich schnellstmöglich mal testen.
Du darfst natürlich nur alle 60s die Minute inkrementieren. Du machst es aber alle 30s. Nimmt man an, das Du die CKDIV8 Fuse nicht zurückgesetzt hat, läuft der uC mit 1MHz. Das würde etwa den Faktor 3-4 (im Zusammenhang mit dem nicht korrekten 30s wg. 8MHz / 1024) erklären.
Oops. Da habe ich mich verguckt. Du inkrementierst die Minute jedenfalls denke ich alle Sekunde da Du 30 Überläufe pro Sekunde hast. CKDIV8 würde ich auf jeden Fall angucken.
Die Funktionalität in integer_split ist schon reichlich viel Tobak für eine ISR. Divisionen sind so ziemlich das schlimmste, was du einem AVR bei den Grundrechnungsarten antun kannst. Da muss er richtig arbeiten. Die würde ich in die main Schleife rausziehen. Die ISR setzt nur ein Flag, dass die min (welche eigentlich Sekunden sind) nicht mehr stimmen und die Hauptschleife zerlegt sich dann die Zahlen so wie sie das braucht.
Hi! Danke zunächst für die Antworten. Werde das Bit und den Takt nochmal überprüfen (mittels LED etc.). Bezüglich der ISR habe ich noch eine Frage: Ursprünglich wurde die Funktion "integer_split" in der main-schleife ausgeführt. Da hatte ich allerdings das Problem, dass die Segmentanzeigen nicht richtig funktionierten (Multiplex). Heißt, während der Ausführung der integer_split blieb die letzte Anzeige an. Die letzte Anzeige ist länger an als die anderen, so dass es fürs Auge aussieht als ginge nur die letzte. Vielleicht sollte ich während der Abarbeitung des integer_split kein Signal auf die Anzeigen schicken.... Die überfüllte ISR braucht also u.U. derart lange, dass ich damit den eigentlich folgenden Overflow Interrupt verpasse?
Henry schrieb: > ausgeführt. Da hatte ich allerdings das Problem, dass die > Segmentanzeigen nicht richtig funktionierten (Multiplex). Na ja. Der ganze Multiplex ist .... unkonventionell aufgebaut :-) Normalerweise verlegt man diesen Multiplex in eine Timer-ISR Allerdings ist die eine, die du hast, mit 30 Aufrufen in der Sekunde ein wenig zu schmalbrüstig für einen 7-Segment Multiplex. Du hast 2 Möglichkeiten * für das Multiplexing einen eigenen Timer samt ISR abstellen. * Die Aufrufrate deiner jetzigen ISR erhöhen und dort den 7-Seg Multiplex mitmachen lassen Welche Variante du auch immer wählst: Bei einem Aufruf wird nur auf die jeweils nächste Stelle der Anzeige weitergeschaltet und NICHT alle 4 Anzeigen in einem Rutsch durchgeschaltet. Dei jeweils nächste Anzeige kommt erst beim nächsten ISR Aufruf drann. Auf diese Art leuchten alle Anzeigen immer gleich lang (minus dem bischen Zeitversatz, der in der ISR für das Zeit hochzählen vergeht. Das ist aber zu kurz, als dass du es sehen wirst) > Vielleicht sollte ich während der Abarbeitung des integer_split kein > Signal auf die Anzeigen schicken.... Du solltest den Update der Anzeige von allen anderen Dingen entkoppeln, indem du ihn in eine ISR verlagerst. Dann brauchst du dir um die Anzeige keine Sorgen mehr machen. Du beschreibst einfach nur deine Variablen und die ISR sorgt dafür, dass dieser Inhalt auch zur Anzeige kommt. > Die überfüllte ISR braucht also u.U. derart lange, dass ich damit den > eigentlich folgenden Overflow Interrupt verpasse? Ich habs nicht nachgerechnet. Aber mein Bauch sagt: könnte sein. Du hast ziemlich viele Divisionen in dieser split Funktion. Edit: Jetzt seh ichs erst. Ich wusste doch, dass mich da etwas stört. Du zählst nicht 30 ISR Aufrufe ab, sondern 31 ISR(TIMER0_OVF_vect) {counter++; if (counter > 30) Probiers mit einer kleineren Zahl und deinen Fingern aus. ISR(TIMER0_OVF_vect) {counter++; if (counter > 4) Der Counter hat beim Betreten der ISR die Werte: 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4 Von einer 0 zur nächsten 0 sind das aber 5 Zählvorgänge und nicht 4 Schreib deine ISR nicht so kryptisch
1 | ISR(TIMER0_OVF_vect) |
2 | {
|
3 | counter++; |
4 | |
5 | if (counter == 30) |
6 | {
|
7 | counter = 0; |
8 | sec++; |
9 | if( sec == 60 ) |
10 | {
|
11 | sec = 0; |
12 | min++; |
13 | if( min == 60 ) |
14 | {
|
15 | min = 0; |
16 | hrs++; |
17 | if( hrs == 24 ) |
18 | hrs = 0; |
19 | }
|
20 | }
|
21 | }
|
22 | }
|
So funktionieren alle Zählstufen gleich und es ist leichter sich von der korekten Funktion zu überzeugen.
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.