Hallo,
ich habe folgendes Priblem:
Ich habe zwei Programme in jeweils einer Schleife. In diesen Programm
wird immer eine Variable hochgezählt. Und vergleicht. Z.B.:
Wenn Wert zwischen 100 und 200 -> LED an.
Da die Programme unterschiedlich lang sind, müsste ich für jedes
Programm mit unterschiedlichen An-Aus-Logarythmen die Zeiten anpassen.
Deshalb wäre das mit einem Timer doch einfacher.
Die Takte für die Einzeit liegen bei ca. 2ms. Aus bei ca. 10ms.
Wenn ich jetzt den 8 Bit Timer nehmen. Bei 1Mhz / 1024 = ca. 2ms
Ich könnte doch dann den Wert des Timers auslesen, indem ich das
Register Abfrage:
Wenn "Timer-Wert-Speicher" = 1 und 2 also 2ms und 4 ms = LED an.
Wenn dann alle Schritte durchgelaufen sind, setze ich den Timer zurück.
Oder gibt es noch eine andere Variante?
Vielen dank für eure Hilfe!
z.B. Der Timer ruft alle 1 ms eine Interruptroutine auf und zählt einen
Zähler bis z.B. 10.
In 2 globalen Variablen stehen die Werte für ein- und aus.
Die ISR vergleicht diese Werte mit dem Zählerstand und schaltet die LEDs
ein und aus.
Das Hauptprogramm braucht dann nur noch die globalen Variablen über die
Zeit anpassen.
Das könnte ich doch über: CTC Clear Timer on Compare Match (Auto Reload)
machen oder?
Ich lasse den Timer einfach jede ms überlaufen. Dadurch wird dann der
Interrupt ausgelöst.
Richtig?
Nur das mit den 2 Variablen und alles darunter habe ich nicht
verstanden. Welche Werte meinst du genau?
Finn schrieb:> Nur das mit den 2 Variablen und alles darunter habe ich nicht> verstanden. Welche Werte meinst du genau?
Habe ich wahrscheinlich falsch verstanden. Ich dachte, Deine 2
Programmteile schalten 2 unterschiedliche Leds.
Ne, ich hab es zu kurz erklärt.
Ich habe zwei Leds. Mehr nicht.
Mit Programm meine ich eine if-Schleife.
Programm 1 soll die Leds in einer anderen Reihenfolge schalten lassen.
Dabei habe ich drei Zeiten:
t ein
t aus-kurz
t aus- lang
Diesen festen Zeiten müssen nur global in jedem Programm verfügbar sein.
../Auto-LED-Blitzer.c:80: error: static declaration of '__vector_15' follows non-static declaration
5
../Auto-LED-Blitzer.c:80: error: previous declaration of '__vector_15' was here
6
make: *** [Auto-LED-Blitzer.o] Error 1
7
Build failed with 2 errors and 0 warnings...
Wenn ich im Datenblatt nachsehe, ist aber der Vector 15 Timer/Counter0
Compare Match. Also eigentlich richtig.
Ist der sonstige Aufbau denn richtig?
Liebe Grüße und vielen Dank,
Finn
das muss eine volatile Variable sein.
Und int ist hier nicht so günstig. Denn dann musst du selber die
Zugriffe atomar machen. Wenn eine Variable sowieso nie den Bereich 0 bis
255 verlassen wird, dann nimm einen uint8_t dafür. Dann brauchst du auf
atomare Zugriffe nicht achten
Hi, danke für die Hilfe.
So sieht jetzt mein Kopf aus.
1
#define F_CPU 1000000UL
2
3
#include<avr/io.h>
4
#include<util/delay.h>
5
#include<avr/eeprom.h>
6
#include<avr/interrupt.h>
7
#include<inttypes.h>
8
9
volatileuint8_tflash_step_counter=0;
10
11
ISR(TIMER0_COMP_vect)
12
{
13
flash_step_counter++;
14
}
15
16
intmain(void)
17
{
18
.....
Habe beim komiplieren dann auch keine Fehler.
Die flash_step_counter ist jetzt im ganzen Programm erreichbar, weil sie
volatile und nicht nur in einer Funktion deklariert wird. (Genau wie bei
z.B. if-Abfragen) richtig?
Finn schrieb:> Die flash_step_counter ist jetzt im ganzen Programm erreichbar, weil sie> volatile und nicht nur in einer Funktion deklariert wird. (Genau wie bei> z.B. if-Abfragen) richtig?
Falsch.
Die Variable ist deswegen im ganzen Programm erreichbar, weil sie global
(ausserhalb einer Funktion) definiert wurde.
Das volatile sagt dem Compiler nur, dass er sich mit Optimierungen auf
dieser Variablen zurückhalten soll und keinen Zugriff wegoptimieren
darf.
TIMSK|=(1<<OCIE0);// Interrupt für Compare Match erlauben
41
42
// Globale Interrupts aktivieren
43
44
unsignedcharprogramm=1;
45
unsignedcharletzterzustand;
46
47
// Programm Start
48
while(1)
49
{
50
51
52
/* // Programm weiter
53
if ( letzterzustand == 0 )
54
{
55
if ( (PINB & (1<<PB0) ))
56
{
57
letzterzustand = 1;
58
programm++;
59
}
60
}
61
62
if ( letzterzustand == 1 )
63
{
64
if ( ! (PINB & (1<<PB0) ))
65
{
66
letzterzustand = 0;
67
programm++;
68
}
69
}
70
71
// Programm-Begrenzung
72
if ( programm > 4 )
73
{
74
programm = 1;
75
}
76
*/
77
78
if(flash_step_counter>500)
79
{
80
flash_step_counter=0;
81
}
82
83
84
// Programm 1
85
if(programm==1)
86
{
87
88
if(flash_step_counter<250)
89
{
90
PORTA|=(1<<PA0);
91
PORTA|=(1<<PA2);
92
}
93
if(flash_step_counter>250)
94
{
95
PORTA&=~(1<<PA0);
96
PORTA&=~(1<<PA2);
97
}
98
99
}
100
101
}
102
}
- Alle zwei Millisekunden läuft der Timer über.
1.000.000 / 1024 = 976,6 Hz / 1000 = 0,98ms -> Timer nach ca. 2ms voll
-> ISR wird aufgerufen => 2ms = flash_step_counter +1
Das bedeutet dass nach 1 s die Periode von vorne anfängt. 500 Steps *
2ms = 1000ms -> 1s.
Die hälfte dieser Zeit (Step 250 *2ms = 500ms) soll PA0 und PA2 an sein.
Die andere halbe s aus.
Die LEDs müssten also im 1Hz Takt blinken, richtig?
Leider leuchten die LEDs nur in voller Helligkeit vor sich hin.
Schonmal 1000 dank!!
Liebe Grüße,
Finn
SEI() fehlt.
Außerdem könnt' es zu lästigen Nebeneffekten kommen, da Tests eines
Integers bei 8bittern nicht atomar sind und von der ISR jederzeit
unterbrochen werden können. Evtl. an den Leds nicht zu sehen, weil zu
schnell für's Auge, jedoch vorhanden.
Hi, danke für die schnelle Antwort.
Sei(); kann hinter der Initialisierung des Timers richtig?
Ich habe int genommen, da ich sonst ja nur 255 * 2ms = 510ms Dauer in
der Variable erfassen kann.
Sind denn meine Rechnungen richtig?
Das Programm sollte also theoretisch funktionieren, wenn sei();
eingefügt und die Variable angepasst wurde.
Vielen dank und gute Nacht !
Finn
Die Zeit wird nicht stimmen, es gibt eine Formel im Datenblatt für CTC
die auch hier zutrifft, der Teiler ist dabei OCRx + 1.
Es würde sich anbieten den Prescaler kleiner zu machen, dafür OCR0
größer.
SEI() muss irgendwann nach der Init kommen und vor while. Bereits nach
Hinzufügen von SEI() wird das Programm funktionieren, außer ich überseh'
etwas. Aufgrund der Kürze des Codes würde man den Led-Teil komplett in
die ISR setzen, dann gibt's auch keine Probleme wegen des atomaren
Zugriffs. Ansonsten kann man atomaren Zugriff durch Kapselung mit
CLI()/SEI() erzwingen, oder durch Sperren/Freigabe des spezifischen
Interrupts.
So langsam bin ich am verzweifeln. Die LEDs sind nur am leuchten.
Das kann doch nicht so schwer sein. Ist es möglich, dass der Timer im
PIC defekt ist?
@ Finn (Gast)
>So langsam bin ich am verzweifeln. Die LEDs sind nur am leuchten.
Ruhig Blut.
>Das kann doch nicht so schwer sein.
Ist es auch nicht.
> Ist es möglich, dass der Timer im PIC defekt ist?
Äußerst unwahrscheinlich. Praktisch unmöglich.
MfG
Falk
So,
ich habs alles hingeschmissen und nochmal von vorne angefangen.
1
#define F_CPU 1000000UL
2
3
4
#include<avr/io.h>
5
#include<avr/interrupt.h>
6
#include<util/delay.h>
7
8
9
10
intmain(void)
11
{
12
13
DDRA=0b11111111;
14
PORTA=0b00000000;
15
16
// Timer 0 initialisieren
17
18
sei();
19
20
TIMSK|=(1<<OCIE0);
21
22
OCR0=128+1;
23
24
TCCR0|=(1<<WGM01);
25
TCCR0&=~(1<<WGM00);
26
27
TCCR0&=~(1<<CS02);
28
TCCR0|=(1<<CS01);
29
TCCR0|=(1<<CS00);
30
31
32
33
34
35
36
}
37
38
39
ISR(TIMER0_COMP_vect)
40
{
41
42
PORTA=0b11111111;
43
44
_delay_ms(500);
45
46
PORTA=0b00000000;
47
}
Berechnung:
1000000 / 64 = 15625 / 1000 = 15,625ms
=> Alle 15,625ms zählt TCNT0 + 1.
2000ms / 15,625ms = 128 => Wenn der Zähler auf TCNT0 = 128 steht, sind 2
Sekunden vergangen.
Der Interrupt kommt also alle 2ms. Die LEDs an PORTA schalten an und
alles wird für 0,5s lahm gelegt => bleiben also leuchten. (Ich weiß,
delay() ist nicht schön -> aber schnell ;-) )
Danach wieder aus und das gleiche nach den nächsten Zwei sekunden.
So die Theorie!
Praxis:
Alle Register werden passend gesetzt.
Liebe Grüße,
Finn
Mir ist gerade etwas aufgefallen.
Wenn ich die Stromversorgung entferne und wieder reinstecke, leuchten
die LEDs ca. 1 Sekunde und gehen dann einmal für ca. 1/4 Sekunde aus.
Jedenfalls sehr kurz.
Wo ist den while (1){} abgeblieben ;-)
Außerdem ist ein Delay in der ISR ein No-Go, denn es wird immer ein
Comp-Interrupt anstehen, da diese schneller ausgelöst werden, als die
ISR fertig ist.
D.h. nach Reset des PortA wird die ISR SOFORT wieder ausgeführt und
PortA wieder gesetzt, leuchtet also immer.
Nimm das Delay raus und toggle den Port.