Timer1 LED Uhr test mit modulo

(Firma: RWTH Aachen) #3858812
Lesenswert?

Habe den Timer1 mit 64 als Vorteiler aktiv und verwende 7.3728MHz.
Interrupts werden ca alle 0,569 Sekunden ausgelöst.

Nun möchte ich aber einen separaten Counter die Interrupts hochzählen 
und später z.B. nach dem 10 Interrupt was anderes ausführen.

Daher dachte ich an dem Modulo Operator.
Ganz banal sollen die LEDs nach und nach aufblinken und wieder ausgehen.
1
#ifndef F_CPU
2
#   define  F_CPU   7372800    /* 7.3728 MHz */
3
#endif
4
#include <avr/io.h> // Registernamen
5
#include <stdlib.h>
6
#include <util/delay.h>
7
#include <inttypes.h>
8
#include <avr/wdt.h>
9
#include <avr/interrupt.h>
10

11
#include <string.h>
12

13
volatile uint16_t counter = 0;
14

15
void timer1 ()
16
{
17
  TCCR1B = (1<<CS11)|(1<<CS10);  // Vorteiler: 64
18
  TIMSK = (1<<TOIE1); // Overflow Interrupt aktiviert  
19
} 
20

21
ISR (TIMER1_OVF_vect)
22
{
23
  cli();
24
  counter++;
25
  PORTD ^= (1<< PD0);  
26
  sei();
27
}
28

29
void long_delay(uint16_t ms)
30
{
31
    for(; ms>0; ms--) _delay_ms(1);
32
}
33

34
int main(void)
35
{
36
  DDRC = 0x00; // Eingänge
37
  DDRC = ((1 << DDC0) | (1 << DDC1) | (1 << DDC2)) ; // PC0-3 Ausgang
38
  PORTC = ((1 << PC0) | (1 << PC1) | (1 << PC2)) ;
39
  
40
  DDRD = 0xFF;  // Alle Pins von Port D als Ausgang
41
  DDRD &= ~ (1<<DDD7); // Pin 7 als Eingang
42
  PORTD = (1<<PD0)| (1<<PD1)|(1<<PD2)|(1<<PD3)|(1<<PD4)|(1<<PD5)|(1<<PD6); 
43
  
44
  timer1();
45
  
46
  while(1)
47
  {
48
    if(counter%7==1) PORTD ^= (1<< PD1);  
49
    if(counter%7==2) PORTD ^= (1<< PD2);  
50
    if(counter%7==3) PORTD ^= (1<< PD3);  
51
    if(counter%7==4) PORTD ^= (1<< PD4);  
52
    if(counter%7==5) PORTD ^= (1<< PD5);  
53
    if(counter%7==6) PORTD ^= (1<< PD6);
54
        }
55
}

Aber es leuchtet nur die LED in 0,5 Sekunden Takt an PD1 und PD2, welche 
nicht konsequent im Richtigen Moment leuchtet.

Woran könnte es liegen?
Gast #3858815
Lesenswert?

Leider keine Lösung für dein Problem, dafür ein paar Anmerkungen:

 cli();
 sei();

habe nichts in der ISR zu suchen, du schaffst damit nur neue Probleme.


#ifndef F_CPU
#   define  F_CPU   7372800    /* 7.3728 MHz */
#endif

ist auch fehleranfällig, du bekommst nicht mit wenn F_CPU auf einen 
andere Wert gesetzt ist. Lass es komplett weg und setze es im Projekt.
(Firma: RWTH Aachen) #3858824
Lesenswert?

Peter II schrieb:
> Leider keine Lösung für dein Problem, dafür ein paar Anmerkungen:
>
>  cli();
>  sei();
>
> habe nichts in der ISR zu suchen, du schaffst damit nur neue Probleme.

Da ich parallel noch die ADC Eingänge nutze und den Analog Comperator, 
muss ich doch bei einem Interrupt andere zunächst ausblenden?
Oder geraten die anderen Interrupts dann in Warteschleife?
Gast #3858828
Lesenswert?

Rene Müller schrieb:
> Da ich parallel noch die ADC Eingänge nutze und den Analog Comperator,
> muss ich doch bei einem Interrupt andere zunächst ausblenden?
> Oder geraten die anderen Interrupts dann in Warteschleife?

die Atmels können nur eine ISR gleichzeitig bearbeiten. Diese Sperre 
hebelst du mit deinem Code aus. Damit wird im schlimmsten Fall die 2.ISR 
schon ausgeführt obwohl die 1.ISR nicht zu ende ist.
(Firma: RWTH Aachen) #3858837
Lesenswert?

Peter II schrieb:
> die Atmels können nur eine ISR gleichzeitig bearbeiten. Diese Sperre
> hebelst du mit deinem Code aus. Damit wird im schlimmsten Fall die 2.ISR
> schon ausgeführt obwohl die 1.ISR nicht zu ende ist.

Genau deswegen cli() um keine Interrupts zuerlauben während ein 
Interrupt läuft oder nicht?
Ging davon aus,dass man genau aus diesem Grund in einem Interrupt andere 
Interrupts nicht erlaubt.
Gast #3858841
Lesenswert?

Rene Müller schrieb:
> Genau deswegen cli() um keine Interrupts zuerlauben während ein
> Interrupt läuft oder nicht?
> Ging davon aus,dass man genau aus diesem Grund in einem Interrupt andere
> Interrupts nicht erlaubt.

cli() am Anfang ist unnötig, weil das schon die Hardware selber macht -> 
siehe Datenblatt

sei() ist das Problem, denn damit schaltest du die Interrupts ein, 
obwohl du noch in der ISR bist. Es müssen ja noch Daten von Stack 
gelöscht werden und das Prozessorregister widerherstellt werden.
(Firma: RWTH Aachen) #3858857
Lesenswert?

Danke!
Wenn ich in der While Schleife nach jedem Durchlauf ein Port auslese um 
dementsprechend das Programmlaufen lassen möchte: Hier Analog Comperator 
oder ADC Einlesung.
Ist es für diese Abfrage sinnvoll Interrupts nicht zu zulassen?
1
     while(1)
2
  {
3
    cli();
4
    menu = (PINC & 0b00100000); // PIN C5 auslesen
5
    // PIN C5 = 1: ADC, C5 = 0 Analog Comperator
6
    if(menu)ADC_Init();
7
    else AC_Init();
8
    sei();
9
    
10
    if(menu)
11
    // ADC Aktiv
12
    {....
Gast #3858936
Lesenswert?

>Meine LED PD1 müsste prinipiell dann leuchten wenn Interrupt das zweite
>mal ausgeführt wird. Also quasi wenn erste LED an PD0 aus ist.
>Aber es ist nicht ganz rund. Mal leuchten 5 Sekunden lang beide im Takt
>an und aus und mal eben richtig versetzt.

Du hast ein Problem mit atomaren Zugriffen.
counter kann sich ändern wenn du in der main()
darauf zugreifst. Genauso ist ^= beim Port nicht atomar.
Der ganze Block muss unter Interruptsperre:
1
    cli();
2
    if(counter%7==1) PORTD ^= (1<< PD1);  
3
    if(counter%7==2) PORTD ^= (1<< PD2);  
4
    if(counter%7==3) PORTD ^= (1<< PD3);  
5
    if(counter%7==4) PORTD ^= (1<< PD4);  
6
    if(counter%7==5) PORTD ^= (1<< PD5);  
7
    if(counter%7==6) PORTD ^= (1<< PD6);
8
sei();
(Firma: RWTH Aachen) #3858966
Lesenswert?

holger schrieb:
> Du hast ein Problem mit atomaren Zugriffen.
> counter kann sich ändern wenn du in der main()
> darauf zugreifst. Genauso ist ^= beim Port nicht atomar.
> Der ganze Block muss unter Interruptsperre:

In der Tat. Danke, habe dennoch gelegentlich aussetzer. Liegt das daran, 
dass ein Interrupt ausgelöst werden möchte gerade wenn es gesperrt wird? 
Müsste das nicht ziemlich unwahrscheinlich sein bei über 7MHz.
Vorallem wenn nun wirklich nur noch dieser Quellcode genutzt wird:
1
int main(void)
2
{
3
  DDRB = 0xFF; // Blaue LED: Ausgang
4
  DDRB &= ~( (1 << DDB2) | (1 << DDB3) ); // PB2 und PB3 Eingänge
5
  DDRC = 0x00; // Eingänge
6
  DDRC = ((1 << DDC0) | (1 << DDC1) | (1 << DDC2)) ; // PC0-3 Ausgang
7
  PORTC = ((1 << PC0) | (1 << PC1) | (1 << PC2)) ;
8
  
9
  DDRD = 0xFF;  // Alle Pins von Port D als Ausgang
10
  DDRD &= ~ (1<<DDD7); // Pin 7 als Eingang
11
  PORTD = (1<<PD0)| (1<<PD1)|(1<<PD2)|(1<<PD3)|(1<<PD4)|(1<<PD5)|(1<<PD6); 
12
  
13
  timer1();
14
  sei();
15
  
16
  while(1)
17
  {
18
                cli();
19
    if(counter%7==1) PORTD ^= (1<< PD1);  
20
    if(counter%7==2) PORTD ^= (1<< PD2);  
21
    if(counter%7==3) PORTD ^= (1<< PD3);  
22
    if(counter%7==4) PORTD ^= (1<< PD4);  
23
    if(counter%7==5) PORTD ^= (1<< PD5);  
24
    if(counter%7==6) PORTD ^= (1<< PD6);    
25
    sei();  
26
  }  
27
}
Gast #3859001
Lesenswert?

>Nach 2 Wellen ist alles Chaos. Wird doch irgendwie machbar sein eine
>Lichterwelle konstant und sicher laufen zu bekommen?

Natürlich geht das. Frag dich mal ob du wirklich jeden
main() Zyklus deine Ports xoren willst? Oder vieleicht
nur wenn counter sich ändert?
1
  uint16_t last_counter = 0;
2
  while(1)
3
  {
4

5
    cli();
6
    if(last_counter != counter)
7
    {
8
      last_counter = counter;
9
      if(last_counter%7==1) PORTD ^= (1<< PD1);  
10
      if(last_counter%7==2) PORTD ^= (1<< PD2);  
11
      if(last_counter%7==3) PORTD ^= (1<< PD3);  
12
      if(last_counter%7==4) PORTD ^= (1<< PD4);  
13
      if(last_counter%7==5) PORTD ^= (1<< PD5);  
14
      if(last_counter%7==6) PORTD ^= (1<< PD6);
15
    }    
16
    sei();  
17
  }
(Firma: RWTH Aachen) #3859005
Lesenswert?

Omg schrieb:
> ;)

Für den Zweck an sich sehr nice.
Nur möchte ich in Zukunft kontinuierlich 8 ADC Eingänge permanent 
einlesen und bei einem Tastendruck einen Timer aktivieren, während 
dessen soll bei einem gewissen Zeitintervall wo der Knopf erneut 
gedrückt wird das Eingangssignal switchen, welches gerade am Display 
ausgegeben wird. Und zuletzt nach einer gewissen Zeit ohne jeglichem 
Tastendruck in den Ursprungsmodus/Anzeige zurückswitcht.

Kurz: Wenn möglich sicher 2 Prozesse parllel laufen.
(Firma: RWTH Aachen) #3859033
Lesenswert?

holger schrieb:
> Natürlich geht das. Frag dich mal ob du wirklich jeden
> main() Zyklus deine Ports xoren willst? Oder vieleicht
> nur wenn counter sich ändert?

So funktioniert aktuell dann gar nichts. Ich denke mal, dass sich Deine 
Idee für ein Programm eignet, wo auch deutlich mehr Inhalt ist als der 
Timer an sich. So sperrt er fast durchgehend die Interrupts :(.
Gast #3859091
Lesenswert?

>So funktioniert aktuell dann gar nichts.

Was funktioniert nicht?

> Ich denke mal, dass sich Deine
>Idee für ein Programm eignet, wo auch deutlich mehr Inhalt ist als der
>Timer an sich. So sperrt er fast durchgehend die Interrupts :(.

Dann mach halt ein _delay_ms(10); in die Mainloop mit rein;)
Die Interruptsperre ist dann nur zu 0,00002% aktiv und den
Rest der Zeit wird heisse Luft erzeugt.
Gast #3859103
Lesenswert?

>>So funktioniert aktuell dann gar nichts.
>
>Was funktioniert nicht?

Bei mir funktioniert es nach meinem Vorschlag. Allerdings an PORTB.
PB0 blinkt perfekt. PB1-6 gehen nacheinander an und
dann nacheinander aus. Also was erzählst du für einen Blödsinn?
Gast #3859123
Lesenswert?

Und hier eine Version mit minimaler Interruptsperre
zum nachdenken:
1
#include <stdint.h>
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4

5
volatile uint8_t counter = 1;
6

7
void timer1 (void)
8
{
9
  TCCR1B = (1<<CS11)|(1<<CS10);  // Vorteiler: 64
10
  TIMSK = (1<<TOIE1); // Overflow Interrupt aktiviert  
11
} 
12

13
ISR (TIMER1_OVF_vect)
14
{
15
  counter++;
16
  if(counter == 7) counter = 1;
17
  PORTB ^= (1<< PB0);  
18
}
19

20
int main(void)
21
{
22
  DDRB = 0xFF;  // Alle Pins von Port D als Ausgang
23
  PORTB = (1<<PB0)| (1<<PB1)|(1<<PB2)|(1<<PB3)|(1<<PB4)|(1<<PB5)|(1<<PB6); 
24
  
25
  timer1();
26
  sei();
27
  
28
  uint8_t last_counter = 0, tmp_counter;
29
  while(1)
30
  {
31
    tmp_counter = counter;
32
    
33
    if(last_counter != tmp_counter)
34
    {
35
      last_counter = tmp_counter;
36
      tmp_counter %= 7;
37
      cli();
38
      if(tmp_counter==1) PORTB ^= (1<< PB1);  
39
      if(tmp_counter==2) PORTB ^= (1<< PB2);  
40
      if(tmp_counter==3) PORTB ^= (1<< PB3);  
41
      if(tmp_counter==4) PORTB ^= (1<< PB4);  
42
      if(tmp_counter==5) PORTB ^= (1<< PB5);  
43
      if(tmp_counter==6) PORTB ^= (1<< PB6);
44
      sei();  
45
    }    
46
  }  
47
}
Gast #3859142
Lesenswert?

>die neuere Atmels könnne auch einen Pin Togglen und das Atomar, dann
>braucht man die sperre auch nicht.

Das geht dann aber auch nur für neuere Atmels.

Schade das da ein Post gelöscht wurde.
Den ganzen Kram im Interrupt zu machen entspricht
auch einer Interruptsperre weil in Interrupts
Interrupts disabled sind;)
Gast #3859150
Lesenswert?

Noch mehr zum Nachdenken: Es ist ganz normal, dass es für einen µC 
(kurzzeitig) nichts zu tun gibt. Damit er solche Zeitspannen nicht 
pseudo-beschäftigt verbringen muss, sondern wirklich ruhen kann, hat man 
den Sleep-Modus erfunden. Mach Dich darüber mal schlau.
#3859153
Lesenswert?

Rene Müller schrieb:
> cli();
>     if(counter%7==1) PORTD ^= (1<< PD1);
>     if(counter%7==2) PORTD ^= (1<< PD2);
>     if(counter%7==3) PORTD ^= (1<< PD3);
>     if(counter%7==4) PORTD ^= (1<< PD4);
>     if(counter%7==5) PORTD ^= (1<< PD5);
>     if(counter%7==6) PORTD ^= (1<< PD6);
>     sei();

Hör mit dieser blödsinnigen Interrupt-Sperrerei auf und setz diesen 
Piffelkram in die ISR. Es gibt zwar die allgemeine 
So-Kurz-Wie-Möglich-Empfehlung, das heisst aber nicht, dass man in der 
ISR gar nichts machen darf. Und das bisschen ist so gut wie gar nichts.

Dein ganzes Timing ist völliger Mist.
1
TCCR1B = (1<<CS10);  // Vorteiler: 1
2

3
ISR(TIMER1_OVF_vect)
4
{
5
  static unsigned char PreCounter = 0;
6

7
  Precounter++;
8
  if(Precounter == 64) 
9
  {
10
    PreCounter = 0;
11
    Counter++;
12
  }
13
  switch(Counter % 7)
14
  {
15
    case 1: PORTD ^= (1<< PD1); break;
16
    case 2: PORTD ^= (1<< PD2); break;
17
    case 3: PORTD ^= (1<< PD3); break;
18
    case 4: PORTD ^= (1<< PD4); break;
19
    case 5: PORTD ^= (1<< PD5); break;
20
    case 6: PORTD ^= (1<< PD6); break;
21
  }
22
}

mfg.
Gast #3859155
Lesenswert?

LostInMusic schrieb:
> Noch mehr zum Nachdenken: Es ist ganz normal, dass es für einen µC
> (kurzzeitig) nichts zu tun gibt. Damit er solche Zeitspannen nicht
> pseudo-beschäftigt verbringen muss, sondern wirklich ruhen kann, hat man
> den Sleep-Modus erfunden. Mach Dich darüber mal schlau.

und wozu? Ihm wird schon nicht langweile wenn er in einer schleife NOP 
ausführt.

Bei einen Gerät was nicht grade an einer Batterie hängt, macht der Sleep 
wenig sinn. Denn Sleep wurde zum Energiesparen eingebaut.

Die ersten PCs haben auch nicht geschlafen, weil es einfach keinen Sinn 
gemacht hat.
#3859173
Lesenswert?

holger schrieb:
>>  switch(Counter % 7)
>
> Beim Überlauf von counter geht wieder alles durcheinander;)
> Schon mal drüber nachgedacht?

Nö. Aber dann lässt man ihn einfach nicht überlaufen.
1
  ...
2
  Counter++;
3
  if (Counter == 253) Counter = 0;

16 Bit für den Counter ist sowieso Quatsch. Genauso wie Modulo. von 0 - 
6 durchzählen reicht für das Programm oben auch. Genauso wie es Perlen 
vor die Säue werfen ist, den 16-Bit Timer zu benutzen. Dafür reicht ein 
8-Bit Timer dicke.

mfg.
#3859204
Lesenswert?

Rene Müller schrieb:
> Ganz banal sollen die LEDs nach und nach aufblinken und wieder ausgehen.
1
//Timer0 Prescaler = 64
2
ISR(TIMER0_OVF_vect)
3
{
4
  static unsigned char PreCounter = 0;
5
  static unsigned char Counter = 0;
6
  
7
  switch(Counter)
8
  {
9
    case 0: TOGGLE_D1; break;
10
    case 1: TOGGLE_D2; break;
11
    case 2: TOGGLE_D3; break;
12
    case 3: TOGGLE_D4; break;
13
    case 4: TOGGLE_D5; break;
14
    case 5: TOGGLE_D6; break;
15
  }
16

17
  PreCounter++;
18
  if(PreCounter == 128) 
19
  {
20
    PreCounter = 0;
21
    Counter++;
22
    if(Counter == 6) Counter = 0;
23
  }
24
}

mfg.
Gast #3859234
Lesenswert?

>macht der Sleep wenig sinn.

Für mich macht das, was der µC ohne Sleep tun muss, noch weniger Sinn. 
Schaden nimmt der µC in beiden Fällen keinen.

>Denn Sleep wurde zum Energiesparen eingebaut.

Ja. Deshalb sind anderweitige Verwendungen aber nicht verboten.

>Die ersten PCs haben auch nicht geschlafen, weil es einfach keinen Sinn
>gemacht hat.

Oder weil es mangels Sleepmodus keine Alternative gab? ;-)
(Firma: RWTH Aachen) #3864072
Lesenswert?

Wow, so viel Antworten, Danke. Werde dies versuchen umzusetzen.

Prinzipiell spricht doch nicht gegen einen 16Bit Timer1 anstelle 8Bit 
Timer0?

Der Mikrocontroller wird später keinen Leerlauf mehr haben. Er soll 
permanent von PA0 bis PA7 die ADC Werte der Signale einlesen und 
vergleichen. Je nach Schwellwert einen ASCI Code entsprechend am 
Bildschirm ausgeben.
Daher sollte der Timer und die Interrupts so unabhängig wie möglich sein 
und keinen Einfluss auf Interpretation der Signale haben.
#3864191
Lesenswert?

Rene Müller schrieb:
> Thomas Eckmann schrieb:
>> ISR(TIMER0_OVF_vect)
>> {
>>   static unsigned char PreCounter = 0;
>>   static unsigned char Counter = 0;
>
> Besser die Variablen nicht im Interrupt immer neu deklarieren sondern
> einmal außerhalb der ISR.

Besser erst mal ergründen, was 'static' eigentlich macht und dann 
darüber nachdenken, warum wohl Thomas den Scope der Variablen auf die 
ISR beschränken möchte.
Das hat nämlich dann auch was mit sog. 'defensivem Programmieren' zu 
tun.

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