Zeitgleich Ausgänge setzen (AtMega8515)

Gast #2067372
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!
#2067394
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.
Gast #2067458
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?
Gast #2067511
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.
Gast #2068253
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
#2068287
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.
#2068292
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;
Gast #2068325
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?
#2068346
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.
Gast #2068493
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
Gast #2068511
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.
Gast #2068524
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
Gast #2068538
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.
Gast #2069384
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
Gast #2069488
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren