Forum: Mikrocontroller und Digitale Elektronik C-Code Problem (LED-Faden mit Timer)


von Felix (Gast)


Lesenswert?

Hallo MK-Gemeinde,

Wenn ich den Code unten auf einen mega8515 lade funktioniert das Faden 
einer LED (langsam heller, dann wieder langsam dunkler) ganz gut bis auf 
dass die LED, wenn sie ganz dunkel sein sollte ein mal kurz aufblitzt. 
Ich habe schon ziemlich lange rumprobiert aber sehe keinen Fehler oder 
Grund für dieses eine kurze Aufblitzen.
Kennt jemand einen Lösungsvorschlag?
1
#include <avr/io.h>
2
#include <stdint.h>
3
#include <avr/interrupt.h>
4
#include <avr/pgmspace.h>
5
6
//DEFINITION
7
#define PLED  PORTB
8
#define DLED  DDRB
9
10
11
//Enthaltene Methoden
12
ISR(TIMER1_COMPA_vect);
13
ISR(TIMER1_OVF_vect);
14
int main(void);
15
16
17
//IMPLEMENTATION
18
static uint16_t pwmtable[] PROGMEM = {1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2,
19
                                     2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3,
20
                                     4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6,
21
                                     6, 7, 7, 7, 8, 8, 8, 9, 9, 10, 10, 10, 11,
22
                                     11, 12, 12, 13, 13, 14, 15, 15, 16, 17, 17,
23
                                     18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
24
                                     29, 31, 32, 33, 35, 36, 38, 40, 41, 43, 45,
25
                                     47, 49, 52, 54, 56, 59, 61, 64, 67, 70, 73,
26
                                     76, 79, 83, 87, 91, 95, 99, 103, 108, 112,
27
                                     117, 123, 128, 134, 140, 146, 152, 159, 166,
28
                                     173, 181, 189, 197, 206, 215, 225, 235, 245,
29
                                     256, 267, 279, 292, 304, 318, 332, 347, 362,
30
                                     378, 395, 412, 431, 450, 470, 490, 512, 535,
31
                                     558, 583, 609, 636, 664, 693, 724, 756, 790,
32
                                     825, 861, 899, 939, 981, 1024, 1069, 1117,
33
                                     1166, 1218, 1272, 1328, 1387, 1448, 1512,
34
                                     1579, 1649, 1722, 1798, 1878, 1961, 2048,
35
                                     2139, 2233, 2332, 2435, 2543, 2656, 2773,
36
                                     2896, 3025, 3158, 3298, 3444, 3597, 3756,
37
                                     3922, 4096, 4277, 4467, 4664, 4871, 5087,
38
                                     5312, 5547, 5793, 6049, 6317, 6596, 6889,
39
                                     7194, 7512, 7845, 8192, 8555, 8933, 9329,
40
                                     9742, 10173, 10624, 11094, 11585, 12098,
41
                                     12634, 13193, 13777, 14387, 15024, 15689,
42
                                     16384, 17109, 17867, 18658, 19484, 20346,
43
                                     21247, 22188, 23170, 24196, 25267, 26386,
44
                                     27554, 28774, 30048, 31378, 32768, 34218,
45
                                     35733, 37315, 38967, 40693, 42494, 44376,
46
                                     46340, 48392, 50534, 52772, 55108, 57548,
47
                                     60096, 62757, 65535};
48
49
50
ISR(TIMER1_COMPA_vect) {    //Timer Interrupt Compare Routine
51
  PLED |= (1 << 0);      //AN
52
};
53
54
ISR(TIMER1_OVF_vect) {
55
  static uint8_t zyklcntr = 0;          //Zum einstellen der Geschwindigkeit
56
  static uint8_t tblpos = 0;            //Pos. im PWM Table
57
  static uint8_t richtung = 0;          // 0 = heller werden, 1 = dunkler werden
58
59
  PLED &= ~(1 << 0);                //AUS
60
  if(zyklcntr == 1) {
61
    if(richtung == 0) {
62
      OCR1A = pgm_read_word(pwmtable+tblpos);  //Compare Wert lesen und setzen
63
      if(tblpos < 255)
64
        tblpos++;
65
      else richtung = 1;
66
    };
67
    if(richtung == 1) {
68
      OCR1A = pgm_read_word(pwmtable+tblpos);  //Compare Wert lesen und setzen
69
      if(tblpos > 0)
70
        tblpos--;
71
      else richtung = 0;
72
    };
73
    zyklcntr = 0;
74
  }
75
  else {
76
    zyklcntr++;
77
  };
78
};
79
80
81
int main(void) {
82
  //Initialisation
83
  DLED |= (1 << 0)|(1 << 1)|(1 << 2);  //Pin1,2 Ausgang
84
  PLED |= (1 << 1)|(1 << 2);      //LED1 AUS
85
  
86
  //Timer
87
  TCCR1B = 1;          //Timer l‰uft mit CPU Takt
88
  TIMSK |= (1 << OCIE1A);    //Output Compare Match Interrupt aktivieren
89
  TIMSK |= (1 << TOIE1);    //Overflow Interrupt aktivieren
90
  OCR1A = 0;
91
92
  sei();            //Interrupts AN
93
94
  //Hauptschleife
95
  while(1) {
96
  
97
  }
98
  return 0;
99
}

von Felix (Gast)


Lesenswert?

Schade, dass so wenig Resonanz kam aber ich bin den Code noch einmal mit 
einem Kolegen durchgegangen und wir haben wahrscheinlich die Lösung, die 
ich euch nicht vorenthalten möchte.

Wir haben hier wohl einen klassischen Fall von zu langer Interrupt 
Routine.
Seztz man den unteren Wert
1
      if(tblpos > 0)
2
        tblpos--;
3
      else richtung = 0;
auf mindestens ca 95 klappt alles super.
1
      if(tblpos > 95)
Wahrscheinlich kommen sich die IR-Routinen da in die Queere...

Grüße,
Felix

von Entwickler (Gast)


Lesenswert?

Seh ich das richtig? Die Tabelle hat 256 Einträge, jedenfalls hat mein 
Editor 255 Kommas gezählt. Die Stelle

if (tblpos < 255)
  tblpos++;

Erhöht den Index aber auf 256, der nunmal nicht existiert. Das kann ich 
nicht gut heissen :-)

von Entwickler (Gast)


Lesenswert?

Hm. Eigentlich doch nicht. Schade.

von Felix (Gast)


Lesenswert?

;)

von Falk B. (falk)


Lesenswert?

@ Felix (Gast)

>Kennt jemand einen Lösungsvorschlag?

>//Enthaltene Methoden
>ISR(TIMER1_COMPA_vect);
>ISR(TIMER1_OVF_vect);
>int main(void);

Das ist schon mal Unsinn. Die ISRs werden NICHT deklariert. Main 
ebensowenig. Und es sind auch keine Methoden!

>ISR(TIMER1_COMPA_vect) {    //Timer Interrupt Compare Routine
>  PLED |= (1 << 0);      //AN
>};

???
Warum nutzt du ich direkt die Output COmpare Funktion? ist das Pin schon 
aberweitig belegt?

>  if(zyklcntr == 1) {

Warum 1? Sollte das Ding nicht von 0..255 zählen?

>    zyklcntr = 0;

Denn hier wird ja wieder auf Null gesetzt.

Warum so kompliziert? Das Beispiel im Artikel LED-Fading ist 
einfacher.

MFG
Falk

von Oliver (Gast)


Lesenswert?

Die ISR's sind so kurz, da überlappt nichts.

Ich habe jetzt kein Datenblatt vom 8515 , aber in dem vom Mega32 steht 
der nette Satz:

>If the interrupt is enabled, the interrupt handler routine can be used for 
>updating the TOP value. However, changing the TOP to a value close to BOTTOM 
>when the counter is running with none or a low prescaler value must be done
>with care since the CTC mode does not have the double buffering feature. If
>the new value written to OCR1A or ICR1 is lower than the current value of
>TCNT1, the counter will miss the compare match. The counter will then have to 
>count to its maximum value (0xFFFF) and wrap around starting at 0x0000 before 
>the compare match can occur.

Das könnte dein Problem sein.

Ansonsten hat Falk schon alles gesagt - umständlicher geht es nicht.

Oliver

von Felix (Gast)


Lesenswert?

Herzlichen Dank für euer Feedback.

Den Artikel LED-Fading habe ich auch schon gelesen aber zum Einen wollte 
ich nicht nur Copy-Paste coden sondern den ganzen Code selber schreiben, 
man möchte ja was lernen ;). Zum Anderen kann man den Ausgangspin ja bei 
dem Tutorial nicht frei wählen, man muss den PWM-Ausgangs-Pin verwenden. 
Stimmt das?

Gibt es einen bestimten Grund, warum man die enthaltenen Methoden (oder 
Prozeduren? - wie heißt es richtig @Falk) nicht deklarieren soll? Bzw. 
gehört sowas eher in das Header-File? Es ging mir damit nur um die 
Übersichtlichkeit.

>>ISR(TIMER1_COMPA_vect) {    //Timer Interrupt Compare Routine
>>  PLED |= (1 << 0);      //AN
>>};
>
>???
>Warum nutzt du ich direkt die Output COmpare Funktion? ist das Pin schon
>aberweitig belegt?

Warum ein Pin belegen? Dafür ist doch der Compare Match ISR da...

@Oliver
Danke für das Raussuchen. Ich denke genau das wird es sein.

Grüße,
Felix

von Felix (Gast)


Lesenswert?

Vergessen:
Das
>  if(zyklcntr == 1) {
ist beabsichtigt. Es dient nur dazu die Länge des Fade-Intervalls zu 
verlängern. Bei 255 dauert es allerdings sehr lang, bis die LED ein mal 
an und wieder aus war ;)
Man könnte den zykcntr auch weglassen...

von Falk B. (falk)


Lesenswert?

@ Felix (Gast)

>man möchte ja was lernen ;). Zum Anderen kann man den Ausgangspin ja bei
>dem Tutorial nicht frei wählen, man muss den PWM-Ausgangs-Pin verwenden.
>Stimmt das?

Ja. Das hat auch seinen Grund. Eine 16 Bit PWM macht man nicht mal fix 
in Software. Praktisch kann man keine 16 Bit PWM in Software machen. 10 
Bit ist so ziemlich die Obergrenze, wenn man noch wenigsten 100 HZ PWM 
Frequenz haben will. Und dann ist die CPU schon SEHR gut beschäftigt.

>Gibt es einen bestimten Grund, warum man die enthaltenen Methoden (oder
>Prozeduren? - wie heißt es richtig @Falk) nicht deklarieren soll?

Es sind Interrupt-Service Routinen, ISRs. Das sind Sonderfälle im C.

> Bzw. gehört sowas eher in das Header-File?

ISRs werden nur einmal direkt definiert, OHNE Deklaration irgendwo. 
Fertig.

>Warum ein Pin belegen? Dafür ist doch der Compare Match ISR da...

NEIN! Denn deine Routine kann keine 1 Takt breite Ansteuerung der LED 
erreichen. Dazu ist der Interrupt prinzipiell zu langsam. Damit ist 
deine "scheinbar 16 Bit PWM" effektiv bestenfalls 10 Bit. Von dreckigen 
Nebeneffekten ganz zu schweigen.

>verlängern. Bei 255 dauert es allerdings sehr lang, bis die LED ein mal
>an und wieder aus war ;)
>Man könnte den zykcntr auch weglassen...

Man könnte es auch gleich richtig machen. Wie im Artikel. Der Autor hat 
sich was dabei gedacht. Ich kenn den . . . ;-)

MfG
Falk

von Felix N. (time2ride)


Lesenswert?

> Ja. Das hat auch seinen Grund. Eine 16 Bit PWM macht man nicht mal fix
> in Software. Praktisch kann man keine 16 Bit PWM in Software machen. 10
> Bit ist so ziemlich die Obergrenze, wenn man noch wenigsten 100 HZ PWM
> Frequenz haben will. Und dann ist die CPU schon SEHR gut beschäftigt.

Ok macht Sinn. Kann man irgenwo in AVR-Studio sehen, in wie viele 
Assembler Befehler bestimmte Teile des Codes compilliert werden oder 
muss man sich dazu die Hex-Datei anschauen?

> Man könnte es auch gleich richtig machen. Wie im Artikel. Der Autor hat
> sich was dabei gedacht. Ich kenn den . . . ;-)

Ja mag sein aber das ist ja nur ein Lösungsansatz. Ich muss aber den 
Ausgangspin frei belegen können. Deshalb habe ich ja auch die Software 
PWM geschrieben... (Der Autor hat sich da auch was bei gedacht ;) )
Und es funktioniert ja auch so. Die LED fadet schön wie sie soll. 
Ressourcenspaaren ist sicherlich anders aber das ist auch mein erstes 
C-Projekt bzw. überhaupt uC.

P.S. ISR Deklaration schon wieder entfernt :)

von Falk B. (falk)


Lesenswert?

@ Felix C. (time2ride)

>Ok macht Sinn. Kann man irgenwo in AVR-Studio sehen, in wie viele
>Assembler Befehler bestimmte Teile des Codes compilliert werden oder
>muss man sich dazu die Hex-Datei anschauen?

In den Optionen kann man einstellen, dass ein List-file erzeugt wird, 
endung .lss . Dort stehen zu jeder Zeie C die ASM Befehle drin.

>Ja mag sein aber das ist ja nur ein Lösungsansatz. Ich muss aber den
>Ausgangspin frei belegen können. Deshalb habe ich ja auch die Software
>PWM geschrieben...

Womit du 16 Bit direkt vergessen kannst. Ich wiederhole mich.

>Und es funktioniert ja auch so. Die LED fadet schön wie sie soll.

Das glaubst du. Die Dreckeffekte hast du ja schon gesehen.

MfG
Falk

von Felix N. (time2ride)


Lesenswert?

> In den Optionen kann man einstellen, dass ein List-file erzeugt wird,
> endung .lss . Dort stehen zu jeder Zeie C die ASM Befehle drin.
Klasse hat geklappt. Danke! Die 1. ISR hat 11 ASM Befehle, die 2. hat 
66.

> Womit du 16 Bit direkt vergessen kannst. Ich wiederhole mich.
>
>>Und es funktioniert ja auch so. Die LED fadet schön wie sie soll.
>
> Das glaubst du. Die Dreckeffekte hast du ja schon gesehen.

Ich hab das mal durchgerechnet. 8000000 Hz -> Befehle in der Sekunde.
Der Timer läuft bis 65536, das ist (8000000/56536) 122 mal in der 
Sekunde. Während einem Timer durchlauf müssen insgesamt (11+66) 77 
Befehle in den ISRs abgearbeitet werden. Das sind (77*122) 9394 Befehle 
in der Sekunde. Da ist die CPU doch nicht so krass ausgelastet oder hab 
ich mich da verrechnet?

von Falk B. (falk)


Lesenswert?

@ Felix C. (time2ride)

>Ich hab das mal durchgerechnet. 8000000 Hz -> Befehle in der Sekunde.

Nein, Takte. Es gibt einige Befehle die 2, 3 oder mehr Takte brauchen.

>Der Timer läuft bis 65536, das ist (8000000/56536) 122 mal in der
>Sekunde.

Genau.

> Während einem Timer durchlauf müssen insgesamt (11+66) 77
>Befehle in den ISRs abgearbeitet werden.

Stimmt.

> Das sind (77*122) 9394 Befehle in der Sekunde.

Stimmt auch.

> Da ist die CPU doch nicht so krass ausgelastet oder hab
>ich mich da verrechnet?

Ist sie auch nicht, im MITTEL! Doch was passiert, wenn du die 
PWM-Einstellung 1 erzeugen willst?

MFG
Falk

von Felix N. (time2ride)


Lesenswert?

> Ist sie auch nicht, im MITTEL! Doch was passiert, wenn du die
> PWM-Einstellung 1 erzeugen willst?

Ja stimmt und deshalb funktionieren die einstellungen für den 
Comparewert, die unter 80 oder 90 (was ungefähr der abarbeitung der 
einen ISR entspricht) oder so (ich habs vorher in dem pwmtbl 
nachgeschaut) nicht mehr richtig und da kommt er dann ins straucheln 
(siehe Beitrag von Oliver). Aber für Werte größer 90 klappt das. Aber 
ich denke so kann ich den Code verwenden, wenn ich die Werte aus dem 
pwmtbl kleiner als 90 rausnehme (siehe 2. Beitrag, da werden aus dem 
pwmtbl die ersten 95 Einträge, die alle kleiner als 90 garnicht 
gelesen). Dann ist zwar kurzzeitig die CPU Belastung hoch aber das geht 
ja in Ordnung.

Vielen Dank nochmal Falk für deine Bemühung!!

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.