Hallo,
hoffe ihr könnt mir weiterhelfen.
Ich hab für mein EVG nen dimmer gebastelt der ab einer bestimmten
Uhrzeit das EVG auf bzw Runter dimmen soll.
Den Code hab ich zusammenkopiert von einer Seite...
1
#include<avr/io.h>
2
#include<stdio.h>
3
#include<inttypes.h>
4
#define F_CPU 1000000 // CPU-Frequenz in Hz
5
#include<util/delay.h>
6
#include<avr/interrupt.h>
7
#include"lcd-routines.h"
8
9
volatileinttimer;
10
11
ISR(TIMER1_OVF_vect){
12
TCNT1=49911;
13
timer++;
14
}
15
16
17
intmain(void){
18
19
intstunden=21,minuten=59,sekunden=00;//hier bitte die Uhrzeit einstellen
sprintf(ausgabestring,"%i",a);// Wandelt x nach String - das LCD kann nur Strings!
107
set_cursor(10,2);lcd_string(" ");
108
set_cursor(10,2);lcd_string(ausgabestring);
109
_delay_ms(2000);
110
a--;
111
}
112
113
}
114
115
116
117
}
118
119
return(0);
120
}
aber jetzt das Problem.
Sobald die Zeit zum Handeln erreicht ist bleibt die Uhr auf dem LCD
stehen weil die PWM in einer while() stattfindet
erst wenns auf 0 Runter gedimmt ist läuft alles weiter.
Wie muss die Anweisung lauten das die Uhr während dem Handeln auch
weiterlauft?
1
if(stunden==21&&minuten==59&&sekunden==10){
2
3
while(a>0){
4
//OCR1A = a;
5
OCR2=a;
6
set_cursor(10,1);lcd_string("runter");
7
sprintf(ausgabestring,"%i",a);// Wandelt x nach String - das LCD kann nur Strings!
Lagere den Teil, der die Uhrzeit ausgibt in eine Funktion aus, und rufe
diese auch aus der Herunterdimm-Schreife aus, die Du noch so
umfrunzelst, dass während des _delay_ms(2000) nicht nichts passiert:
> Wie muss die Anweisung lauten das die Uhr während dem Handeln> auch weiterlauft?
Falsche Frage.
Die ganze Vorgehensweise mit der while Schleife und dem _delay_ms ist
nicht dafür geeignet, mehrere Dinge (Uhrzeit aktualisieren + LED dimmen)
gleichzeitig zu machen. Du musst das Programm komplett umkrempeln und
dazu musst du erst mal deine Denkweise umstellen.
Im ganzen Programm gibt es nur eine einzige Schleife und das ist die
zentrale while(1) Endlos-Hauptschleife. Alles andere muss eventgesteuert
funktionieren.
* anstatt des Timer vorladens nimmst du den CTC Modus.
Damit kannst du dann später mal die genau Taktrate auf 1 Quarz-
schwingung genau einstellen. Mit dem interen RC-Generator wirst du
auf Dauer sowieso nicht glücklich. Für eine länger laufende Uhr
ist der ungeeignet.
* In der ISR zählst du die komplette Uhrzeit hoch, nicht nur die
Sekunden. Du willst verhindern, dass dir eine Sekundenerhöhung
durch die Lappen geht weil der µC gerade an einer etwas komplexeren
GLCD Ausgabe rummacht. 'Die Zeit' ist eine integrale Einheit,
bestehend aus Stunden#Minuten#Sekunden. Wenn du 'die Zeit' erhöhst,
dann mach das gleich vollständig.
Wichtig: Du erhöhst nur die Zeit! Die Anzeige der Zeit findet nach
wie vor in der Hauptschleife statt. Das erhöhen von Sekunden, Minuten
und Stunden ist schnell gemacht und kann als solches ruhig in der
ISR bleiben.
Kurze ISR bedeutet nicht, dass man gar nichts in einer ISR machen
darf!
* Wenn du schon sprintf benutzt (wogegen erst mal nichts zu sagen ist),
dann lass sprintf für dich arbeiten. sprintf kann dir perfekte
führende 0-en in die Ausgabe reinmachen. Du musst sie nur anfordern.
* Die Dimmung kannst du am einfachsten mit einer Hilfsvariablen machen.
Die Hilfsvariable ist der Sollwert der Helligkeit. In der ISR prüfst
du ganz einfach ob die momentane Ist-Einstellung kleiner oder größer
des Sollwerts ist und wenn ja, stellst du den Ist-Wert um 1 nach.
Dadurch wird automatisch die PWM jede Sekunde um 1 Wert in Richtung
des Sollwertes geführt. In Summe ergibt sich daraus die gewünschte
Dimmung. Du brauchst nur den Sollwert auf den Wert stellen, den du
am Ende haben willst und die ISR dimmt die Lampe langsam auf diesen
Wert.
* In der Hauptschleife machst du nur noch die Anzeige (die ISR sagt
dir, wann das notwendig ist) und machst dann auch noch den Test
ob deine Lampe aufzudimmen oder abzuschalten ist. Das Aufdimmen
bzw. Abschalten geschieht ganz einfach, in dem man einen neuen
Helligkeitswert vorgibt. Die ISR sorgt dann dafür, dass die PWM
im Laufe der Zeit jede Sekunde entsprechend nachgestellt wird.
Ich hab hier mal eine Steuerung gebaut, die die PWM zu jeder vollen
Minute entweder aufdimmt oder abdimmt (bei den geraden Minuten wird
aufgedimmt).
Das Programm ist für einen Mega16 geschrieben. Ich denke die Timer
Einstellungen sind beim Mega8 identisch. Ich hab eine LED am Ausgabepin
(OCR2 ist bei mir PortD/PD7), die anders rum angeschlossen ist, daher
ist der Wert für Dunkel bei mir größer als der Wert für Hell. Musst du
bei dir anpassen. Bedenke: Wenn du die Helligkeit von 0 bis 255
variieren lässt, dann dauert das vollständige Auf/Ab-dimmen 255
Sekunden, oder knapp 4 Minuten. Drum hab ich mich auf eine kleinere
Zahlendifferenz geeinigt. Ich will ja um die Uhrezeit nicht noch ewig
für der Schaltung sitzen und zusehen wie die LED in 4 Minuten einmal von
'aus' bis 'volle Helligkeit' durchdimmt :-).
1
#define F_CPU 1000000UL
2
#include<avr/io.h>
3
#include<avr/interrupt.h>
4
#include<stdio.h>
5
#include"../Lcd/lcd.h"
6
7
#ifndef TRUE
8
#define TRUE 1
9
#define FALSE 0
10
#endif
11
12
13
#define PWM_PIN PB7 // PB3
14
#define PWM_PORT PORTD // PORTB
15
#define PWM_DDR DDRD // DDRB
16
17
#define HELL 230
18
#define DUNKEL 255
19
20
volatileuint8_tTimeTick;
21
volatileuint8_tSekunden;
22
volatileuint8_tMinuten;
23
volatileuint8_tStunden;
24
25
volatileuint8_tHelligkeit;
26
27
ISR(TIMER1_COMPA_vect)
28
{
29
//
30
// Uhr erhöhen
31
//
32
Sekunden++;
33
if(Sekunden==60){
34
Sekunden=0;
35
36
Minuten++;
37
if(Minuten==60){
38
Minuten=0;
39
40
Stunden++;
41
if(Stunden==24){
42
Stunden=0;
43
}
44
}
45
}
46
47
//
48
// PWM eventuell in Richtung Sollwert nachstellen
49
//
50
if(OCR2>Helligkeit)
51
OCR2--;
52
if(OCR2<Helligkeit)
53
OCR2++;
54
55
//
56
// Für alle die's interessiert: 1 Sekunde ist rum
57
//
58
TimeTick=TRUE;
59
}
60
61
intmain()
62
{
63
charBuffer[10];
64
65
TimeTick=TRUE;
66
Helligkeit=DUNKEL;
67
68
//
69
// PWM Ausgang
70
//
71
PWM_DDR=(1<<PWM_PIN);
72
TCCR2|=(1<<WGM21)|(1<<WGM20);// Fast Pwm
73
TCCR2|=(1<<COM21);// non inverting
74
OCR2=Helligkeit;
75
TCCR2|=(1<<CS21);// Prescaler 1
76
77
//
78
// Uhr
79
//
80
TCCR1B|=(1<<WGM12);// CTC
81
OCR1A=(F_CPU/64)-1;// Für 1 Sekunde
82
TIMSK=(1<<OCIE1A);
83
TCCR1B|=(1<<CS11)|(1<<CS10);// Prescaler: 64
84
85
lcd_init(LCD_DISP_ON);
86
87
sei();
88
89
while(1){
90
if(TimeTick){// schon wieder 1 Sekunde um? Kinder, wie die Zeit vergeht!
Michael P. schrieb:> Ohje ohje... bin anfänger komm noch net mal mit der Syntax klar von C :D
Dann wirds aber Zeit. So schwer hab ich dir die Syntax auch wieder nicht
gemacht. Wenn du dein erstes Beispiel durch zusammenkopieren erstellen
konntest, wirst du auch dieses Beispiel verstehen.
Da musst du durch. Wer nach Australien auswandert, wird Englisch lernen
müssen. Die Ausrede "Bin neu, kann noch kein Englisch" zieht nicht.
PS: meine LCD Funktionen heißen ein wenig anders. Aus dem Zusammenhang
sollte sich aber ergeben, welche Funktion was macht.
Hallo Karl Heinz,
Karl Heinz Buchegger schrieb:> * In der ISR zählst du die komplette Uhrzeit hoch, nicht nur die> Sekunden.
Äh... wäre es da nicht bedeutend einfacher, einfach nur die Sekunden
seit 00:00:00 Uhr zu zählen? Wenn man daraus dann die Uhrzeit braucht
oder umgekehrt eine Uhrzeit in die Sekundenzahl ausrechnen will, geht
das ja flott:
1
/** convert seconds to timestring */
2
void s2t(unsigned int time, char* res) {
3
unsigned int hr = (time - (time % 3600)) / 3600;
4
unsigned int sc = time - (hr * 3600);
5
unsigned int mn = (sc - (sc % 60)) / 60;
6
sc = sc - (mn * 60);
7
sprintf(res, "%02d:%02d:%02d", hr, mn, sc);
8
}
9
10
/** convert timestring to seconds */
11
int s2s(char* str) {
12
unsigned int hr = 0;
13
unsigned int mn = 0;
14
unsignet int sc = 0;
15
sscanf(str, "%i:%i:%i", &hr, &mn, &sc);
16
return hr * 3600 + mn * 60 + sc;
17
}
Dann bleibt der betreffende Teil in der ISR schön klein:
1
volatile uint64 daytime;
2
ISR(TIMER1_COMPA_vect) {
3
daytime++;
4
if(daytime == 86400) {
5
daytime = 0;
6
}
7
}
Bei dem konkreten Beispiel hier wäre auch die LCD-Ausgabe einfacher,
weil der ganze Timestring in einem Rutsch ausgegeben werden kann.
Was spricht dagegen?
Beste Grüße,
Klaus
KlausK schrieb:> Was spricht dagegen?
Alleine schon die Divisionen. Das dauert auf einem ATmega
Größenordnungen länger als die Vergleiche mit bedarfsweiser
Inkrementierung. Solange die Dauer der Hauptschleife sicher unter 1
Sekunde bleibt, gibt es sowieso keinen Grund, die Sek-Min-Hour Überträge
in die ISR zu packen.
Karl Heinz Buchegger schrieb:> Damit kannst du dann später mal die genau Taktrate auf 1 Quarz-> schwingung genau einstellen. Mit dem interen RC-Generator wirst du> auf Dauer sowieso nicht glücklich. Für eine länger laufende Uhr> ist der ungeeignet.
Selbst mit einem Quartz wird die Uhr ohne einen Abgleich nicht genau
genug laufen. Ein Beipiel für die genaue Erzeugung eines Sekundentaktes
hat Peter hier gezeigt:
Beitrag "Die genaue Sekunde / RTC"
KlausK schrieb:> Dann bleibt der betreffende Teil in der ISR schön klein:>>
1
> volatile uint64 daytime;
2
> ISR(TIMER1_COMPA_vect) {
3
> daytime++;
4
> if(daytime == 86400) {
5
> daytime = 0;
6
> }
7
> }
8
>
Hast du das mal verglichen?
Ich denke ich trau mich sogar wetten, dass das Laden, Inkrementieren und
Vergleichen eines uint64_t länger dauert, als 3 Bytes bei Bedarf
hochzuzählen.
Lass dich nicht vom C-Code blenden. Der sieht realtiv umfangreich aus.
Wenn man sich aber mal überlegt, wie der zugehörige Assemblercode
aussieht, dann kommt man drauf, dass das weniger als ca. 20 Takte sind.
Ist ja praktisch nichts zu tun. Byte holen inkrementieren, vergleichen,
zurückspeichern. Und das (möglicherweise) 3 mal. Da das restliche
Programm in keiner Weise zeitkritisch ist, sind diese Takte völlig
problemlos zu verschmerzen.
In einem Punkt hast du allerdings recht: Wenn man mit der Zeit
Vergleiche machen muss, ist es einfacher, wenn man eine laufende Zahl
seit Mitternacht hat. Wobei du den Datentyp aber auch nicht so hoch
drehen willst, dass du mit der Rechnerei den µC lahm legst. Höher als
einen uint16_t würde ich nicht gehen, zumal bei solchen Schaltuhr Sachen
nur ganz selten Sekunden gebraucht werden. Normalerweise reichen Minuten
locker aus.