Forum: Mikrocontroller und Digitale Elektronik Wahrscheinlich einfache Timerfrage


von zaphod_beebelbrox (Gast)


Lesenswert?

Hallo,
ich habe vor, ein Positionslicht für ein Modell zu bauen.
Dieses soll drei unterschiedliche Modi bisitzen:
Mode 0: 50ms an, 1450ms aus;
Mode 1: 50ms an, 50ms aus, 50ms an, 1350ms aus;
Mode 2: 50ms an, 50ms aus.

Sinn dahinter:
0 steht für "Modell im Standby, soweit alles roger";
1 ist normaler Betrieb (Doppelblitz);
2 ist das Signal für einen Fehler.

Das ganze hab ich mir mit Delayschleifen schnell hinprogrammiert. Aber 
das ist ja nicht so das Gelbe...
Ich würde das gerne mit einem Timer im Hintergrund machen. Da ich aber 
doch noch sehr neu in der ganzen Sach bin, würde ich mich über einen 
Denkanstoß freuen... Keine komplette Lsg.

Ich hab jetzt schon rausgefunden, dass es Timervectoren gibt, in denen 
ich Code ausführen lassen kann, wenn ein Timer überläuft. Aber so recht 
bringt mich das noch nicht weiter...

Hier ist erstmal der Code mit Trödelschleifen:
1
uint8_t status = 0;
2
uint8_t lde = 0;
3
4
if (status == 0) {
5
  if (led == 0) {
6
    PORTD ^= (1 << PD5);
7
    _delay_ms(50);
8
    led = 1;
9
  }
10
  if (led == 1) {
11
    PORTD ^= (1 << PD5);
12
    _delay_ms(1450);
13
    led = 0;
14
  }
15
} else if (status == 1) {
16
  if (led == 0) {
17
    PORTD ^= (1 << PD5);
18
    _delay_ms(50);
19
    led = 1;
20
  }
21
  if (led == 1) {
22
    PORTD ^= (1 << PD5);
23
    _delay_ms(50);
24
    led = 2;
25
  }
26
  if (led == 2) {
27
    PORTD ^= (1 << PD5);
28
    _delay_ms(50);
29
    led = 3;
30
  }
31
  if (led == 3) {
32
    PORTD ^= (1 << PD5);
33
    _delay_ms(1350);
34
    led = 0;
35
  }
36
37
} else if (status == 2) {
38
  if (led == 0) {
39
    PORTD ^= (1 << PD5);
40
    _delay_ms(50);
41
    led = 1;
42
  }
43
  if (led == 1) {
44
    PORTD ^= (1 << PD5);
45
    _delay_ms(50);
46
    led = 0;
47
  }
48
} else {
49
  PORTD &= ~(1 << PD5);
50
}

Viele Grüße,
Matthias

von STK500-Besitzer (Gast)


Lesenswert?

Stell dir einen Timer auf 50ms Periodendauer (CTC-Mode) ein.
In der ISR zählst du dann eine Variable herunter, die dir den 
Umschaltzeitpunkt signalisiert.
Wurde umgeschaltet, lädt man die Variable mit dem nächsten 
Countdownwert...
Die Werte schreibt man vorher in ein Array und muss dann nur noch den 
Wert, der duch den Index beschrieben wird in die Zähövariable laden.

von zaphod_beebelbrox (Gast)


Lesenswert?

Super, danke!

Bin jetzt schon mal ein ganzes Stück weiter und freunde mich mit
1
ISR(TIMER2_COMP_vect){
2
TCCR2 |= (1<<WGM21) | (1<<COM20) | (1<<CS20) | (1<<CS22);
3
TIMSK |= (1<< OCIE2);
und Co. an.

Wird heut noch was :-)

VG,
Matthias

von zaphod_beebelbrox (Gast)


Lesenswert?

So, ich habe fertig :-)

Das ist bei rausgekommen:
1
  uint16_t leuchtzeiten[3][4] = {
2
      { 30, 860, 0, 0 },
3
      { 30, 30, 30, 800 },
4
      { 30, 30, 0, 0 } };
5
  TCCR2 |= (1<<WGM21) | (1<<COM20) | (1<<CS20) | (1<<CS22);
6
  TIMSK |= (1<< OCIE2);
7
  OCR2=255;
8
  sei();
9
  PORTD |= (1<<PD5);
10
  while (1) {
11
    if (Taster.t1)
12
      status = 1;
13
    else
14
      status = 0;
15
16
    if (counter >= leuchtzeiten[status][phase]) {
17
      counter = 0;
18
      if (phase < 3) {
19
        phase++;
20
      } else
21
        phase = 0;
22
      PORTD ^= (1<<PD5); // LED toggeln
23
    }
24
  }

Ist sicherlich noch nicht so 100% aber es funktioniert!

Viele Grüße & danke nochmal für den Tipp,
Matthias

von zaphod_beebelbrox (Gast)


Lesenswert?

Natürlich noch das dazu:
1
ISR(TIMER2_COMP_vect) {
2
  counter++;
3
}

von Ulf R. (roolf)


Lesenswert?

Gut, dass schonmal etwas läuft.

* Du hast immer noch im Hauptprogramm eine Schleife, die sich um alles 
kümmern muss.

Ausgehend von obiger Lösung verlegst Du jetzt noch einen Teil in die 
Interrupt-Routine. Vorschlag: Die Kommunikation erfolgt über Variablen: 
status wird von main geschrieben und vom Interrupt gelesen, phase 
wird alleine vom Interrupt benutzt, ebenso counter.

Damit ist main völlig unabhängig und braucht außer dem Schreiben von 
status bei einem Statuswechsel sich überhaupt nicht mehr mit dem Blinken 
zu befassen.

* Du setzt COM20 und das sorgt dafür, dass am IO-Port "OC2" ein Signal 
ausgegeben wird (falls dieser Pin auch als Ausgang geschaltet ist). Das 
ist für Debugzwecke oder in anderen Zusammenhängen nützlich, hat aber 
mit Deinem Blinken nichts zu tun und ist daher überflüssig.

von zaphod_beebelbrox (Gast)


Lesenswert?

Hi, danke für den Tipp, hört sich sehr sinnig an!

Aber da komm ich an meine C-Grenze...
Ich habe das jetzt so gemacht:

blinker.c:
1
volatile uint8_t status;
2
3
int main(void) {
4
  DDRD = 0b11100011;
5
  TCCR2 |= (1<<WGM21) | (1<<CS20) | (1<<CS22);
6
  TIMSK |= (1<< OCIE2);
7
  OCR2=255;
8
9
  sei();
10
  PORTD |= (1<<PD5);
11
  while (1) {
12
    if (Taster.t1)
13
      status = 1;
14
    else
15
      status = 0;
16
17
}
interruptVectors.c:
1
uint8_t counter = 0;
2
uint8_t phase = 0;
3
volatile uint8_t status;
4
const uint16_t leuchtzeiten[3][4] = { 
5
    { 30, 860, 0, 0 }, 
6
    { 30, 30, 30, 800 }, 
7
    { 30, 30, 0, 0 } };
8
9
10
ISR(TIMER2_COMP_vect)
11
{  counter++;
12
13
  if (counter >= leuchtzeiten[status][phase]) {
14
    counter = 0;
15
    if (phase < 3)
16
      phase++;
17
    else
18
      phase = 0;
19
    PORTD ^= (1<<PD5);
20
  }
21
}

Aber es tut sich nichts... Bei einem TAstendruck leuchtet die LED 
einmal, beim Loslassen ein weiteres mal...

Ist der Fehler für jemanden von euch ersichtlich?

Viele Grüße,
Matthias

von zaphod_beebelbrox (Gast)


Lesenswert?

So, Problem gelößt. Falscher Datentyp. uint8 kann schwer die gewünschte 
Pausenzeit von 860 erreichen O_o

Jetzt aber mal ne andere Frage:

Warum funktioniert das Programm auch, wenn ich vor keine der Variabeln 
extern, volatile oder ähnliches schreibe. Es compiliert und läuft in 
dieser Form:

intteruptVectors.c:
1
uint8_t status;
2
const uint16_t leuchtzeiten[3][4] = { { 30, 860, 0, 0 }, { 30, 30, 30, 800 }, {
3
    30, 30, 0, 0 } };
4
uint8_t phase = 0;
5
uint16_t timer = 0;
6
7
ISR(TIMER2_COMP_vect)
8
{
9
  timer++;
10
  if (timer >= leuchtzeiten[status][phase]) {
11
    timer = 0;
12
    if (phase < 3)
13
      phase++;
14
    else
15
      phase = 0;
16
    PORTD ^= (1<<PD5);
17
  }
18
}

blinker.c
1
uint8_t status;
2
int main(void) {
3
  DDRD = 0b11100011;
4
  TCCR2 |= (1<<WGM21) | (1<<CS20) | (1<<CS22);
5
  TIMSK |= (1<< OCIE2);
6
  OCR2=255;
7
8
  sei();
9
  PORTD |= (1<<PD5);
10
  while (1) {
11
    if (Taster.t1)
12
      status = 1;
13
    else
14
      status = 0;
15
  }
16
}

Viele Grüße & schönen Nikolaus!

von gast (Gast)


Lesenswert?

hast du die Codeoptimierung an??

Wenn nicht, dann liegt es daran. volatile saget dem Compiler nur, dass 
er diese Variable nicht irgendwie optimieren darf, da sich diese während 
der Programmabarbeitung ändern kann (z. B. durch eine ISR)

von zaphod_beebelbrox (Gast)


Lesenswert?

Hallo,
ich habe mit OPT={s|0|3} probiert. Der Code ist immer gleich groß und 
verhält sich immer gleich.

Naja, ich mach erstmal an anderen Baustellen weiter.

Danke jedenfalls allen Helfern :-)

Viele Grüße,
Matthias

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.