Forum: Compiler & IDEs Zeitgleich Ausgänge setzen (AtMega8515)


von Finn (Gast)


Lesenswert?

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!

von Klaus F. (kfalser)


Lesenswert?

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.

von Finn (Gast)


Lesenswert?

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?

von Klaus F. (kfalser)


Lesenswert?

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.

von Finn (Gast)


Lesenswert?

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.

von Finn (Gast)


Lesenswert?

Hallo,
ich habe mal ein bisschen was ausprobiert.
Die unnötigen Zeilen sind auskommentiert.

Hier Code und Fehler:
1
/* Auto-LED-Blitzer
2
Version: 0.1
3
Datum: 027.02.2009
4
Autor: Finn Schürmann
5
Target: ATMega 8515
6
*/
7
8
9
#define F_CPU 1000000UL
10
11
#include <avr/io.h>
12
#include <util/delay.h>
13
#include <avr/eeprom.h>
14
#include <avr/interrupt.h>
15
#include <inttypes.h>
16
17
int main (void)
18
{
19
  DDRA = 0b11111111;    // Port A auf Ausgang
20
  PORTA = 0b00000000;    // Port A aus
21
  
22
  // Timer0 initialisieren
23
  TCCR0 |=  (1<<WGM01);  //  Hier wird der Timer  
24
  TCCR0 &= ~(1<<WGM00);  //  auf CTC-Modus gestellt
25
26
  TCCR0 |=  (1<<CS02);  //  Prescaler auf clock / 1024
27
  TCCR0 &= ~(1<<CS01);  //  1.000.000 / 1024 = 976,56 Hz
28
  TCCR0 |=  (1<<CS00);  //  976,56 Hz / 1000 = 0,98 ms
29
30
  OCR0 = 2;        //  Timer zählt bis 2 = 2ms
31
32
  TIMSK |=  (1<<OCIE0);  //  Interrupt für Compare Match erlauben
33
34
  
35
36
  sei();          //  Globale Interrupts aktivieren
37
38
      
39
  // Variablen definieren
40
  unsigned char programm=1;
41
//  unsigned char letzterzustand;
42
  int flash_step_counter = 0;
43
44
  // Programm Start
45
  while (1)
46
  {
47
48
49
/*    // Programm weiter
50
    if ( letzterzustand == 0 )
51
    {
52
      if ( (PINB & (1<<PB0) ))
53
      {
54
        letzterzustand = 1;
55
        programm++;
56
      }
57
    }
58
59
    if ( letzterzustand == 1 )
60
    {
61
      if ( ! (PINB & (1<<PB0) ))
62
      {
63
        letzterzustand = 0;
64
        programm++;
65
      }
66
    }
67
68
    // Programm-Begrenzung
69
    if ( programm > 4 )
70
    {
71
      programm = 1;
72
    }                
73
*/
74
75
    if( flash_step_counter == 100 )
76
    {  
77
      flash_step_counter = 0;
78
    }
79
    
80
    ISR ( TIMER0_COMP_vect )
81
    {
82
      flash_step_counter++;
83
    }
84
    
85
    
86
    
87
    // Programm 1
88
    if( programm == 1 )
89
    {
90
      if( flash_step_counter < 50 )
91
      {
92
        PORTA |=  (1<<PA0);
93
        PORTA |=  (1<<PA2);
94
      }
95
      else
96
      {
97
        PORTA &= ~(1<<PA0);
98
        PORTA &= ~(1<<PA2);
99
      }  
100
    } 
101
}
102
}
1
Build started 17.2.2011 at 21:13:05
2
avr-gcc  -mmcu=atmega128 -Wall -gdwarf-2 -Os -std=gnu99 -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD -MP -MT Auto-LED-Blitzer.o -MF dep/Auto-LED-Blitzer.o.d  -c  ../Auto-LED-Blitzer.c
3
../Auto-LED-Blitzer.c: In function 'main':
4
../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

von Karl H. (kbuchegg)


Lesenswert?

Eine ISR ist eine Funktion für sich, so wie jede andere Funktion auch.

In C kann man keine Funktionen ineinanderschachteln.

Anstelle von
1
int main()
2
{
3
  ....
4
5
  ISR( ... )
6
  {
7
    ...
8
  }
9
10
  ....
11
}

so rum
1
ISR( ... )
2
{
3
 ...
4
}
5
6
7
int main()
8
{
9
   ....
10
}

so wie mit jeder anderen Funktion auch.

von Karl H. (kbuchegg)


Lesenswert?

1
 int flash_step_counter = 0;

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
1
volatile uint8_t flash_step_counter = 0;

von Karl H. (kbuchegg)


Lesenswert?

Du das Umschalten der LED nicht viel Arbeit macht, kann man es ruhig 
auch in der ISR machen.

von Finn (Gast)


Lesenswert?

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
  volatile uint8_t flash_step_counter = 0;
10
    
11
  ISR ( TIMER0_COMP_vect )
12
  {
13
    flash_step_counter++;
14
  }
15
16
int main (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?

von Karl H. (kbuchegg)


Lesenswert?

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.

von Finn (Gast)


Lesenswert?

Hi,
ich bin es nochmal.
Heute ist nicht mein Tag.
1
/* 
2
Version: 0.1
3
Datum: 027.02.2009
4
Autor: Finn Schürmann
5
Target: ATMega 8515
6
*/
7
8
9
#define F_CPU 1000000UL
10
11
#include <avr/io.h>
12
#include <util/delay.h>
13
#include <avr/eeprom.h>
14
#include <avr/interrupt.h>
15
#include <inttypes.h>
16
17
  
18
volatile unsigned int flash_step_counter = 0;
19
20
ISR ( TIMER0_COMP_vect )
21
{
22
  flash_step_counter++;
23
}
24
25
int main (void)
26
{
27
  DDRA = 0b11111111;    // Port A auf Ausgang
28
  PORTA = 0b00000000;    // Port A aus
29
  
30
  // Timer0 initialisieren
31
  TCCR0 |=  (1<<WGM01);  //  Hier wird der Timer  
32
  TCCR0 &= ~(1<<WGM00);  //  auf CTC-Modus gestellt
33
34
  TCCR0 |=  (1<<CS02);  //  Prescaler auf clock / 1024
35
  TCCR0 &= ~(1<<CS01);  //  1.000.000 / 1024 = 976,56 Hz
36
  TCCR0 |=  (1<<CS00);  //  976,56 Hz / 1000 = 0,98 ms
37
38
  OCR0 = 2;        //  Timer zählt bis 2 = 2ms
39
40
  TIMSK |=  (1<<OCIE0);  //  Interrupt für Compare Match erlauben
41
42
            //  Globale Interrupts aktivieren
43
44
  unsigned char programm = 1;
45
  unsigned char letzterzustand;
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

von MWS (Gast)


Lesenswert?

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.

von Finn (Gast)


Lesenswert?

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

von MWS (Gast)


Lesenswert?

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.

von Finn (Gast)


Lesenswert?

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?

von Falk B. (falk)


Lesenswert?

@  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

von Finn (Gast)


Lesenswert?

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
int main (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

von Finn (Gast)


Lesenswert?

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.

von MWS (Gast)


Lesenswert?

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.

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
Noch kein Account? Hier anmelden.