Forum: Mikrocontroller und Digitale Elektronik Integertyp verlangsamt die Programmausführung (ISR)


von C. H. (hedie)


Lesenswert?

Hallo zusammen


Ich habe folgendes Codefragment:
1
volatile unsigned char   uiPWMCounter;
2
unsigned char  uiPWMRed;
3
unsigned char  uiPWMGreen;
4
unsigned char  uiPWMBlue;
5
6
ISR(TIMER0_COMPA_vect)
7
{
8
  uiPWMCounter++;
9
  if(uiPWMCounter == 250)
10
  {
11
    uiPWMCounter = 0;
12
    RGB_R_0; //LED's ausschalten
13
    RGB_G_0;
14
    RGB_B_0;
15
  }
16
17
  if(uiPWMCounter == uiPWMRed)   RGB_R_1; //LED einschalten
18
  if(uiPWMCounter == uiPWMGreen)   RGB_G_1;
19
  if(uiPWMCounter == uiPWMBlue)   RGB_B_1;
20
}

Damit erzeuge ich einen 3Phasen RGB PWM.

Den Timer habe ich wie folgt konfiguriert:
1
void timer_init(void)
2
{
3
4
  TCCR0A |= (1<<WGM01);
5
  TCCR0B |= (1<<CS01); 
6
  OCR0A = 10;
7
  TIMSK0 |= (1<<OCIE0A); //iNTERRUPT BEIM erreichen des TOP wertes
8
}

Controller: Atmega324PV mit 10MHz

Wenn ich nun als Counter ein Byte also ein Char verwende,
dann läuft mir der PWM mommentan mit einer Frequenz von knapp 500Hz

wenn ich jedoch den typ auf int ändere also:
1
volatile unsigned int uiPWMCounter;

Dann erhalte ich zwar immer noch knapp 500Hz jedoch wird die Ausführung 
des Restlichen Programmes massiv verzögert.

Etwa um den Faktor 50 mal langsamer.

Nun meine Vermutung:

Dauert das vergleichen eines Integers so viel länger als das eines 
chars?

Hat jemand Empfehlungen, wie ich meinen Code auf speed optimieren 
könnte?

Ich würde gerne mehr als nur 8bit PWM haben.
10bit und mehr wäre mein Ziel.

Danke schon mal.

von Falk B. (falk)


Lesenswert?

@  Claudio Hediger (hedie)

>volatile unsigned char   uiPWMCounter;

Warum volatile hier? Die Variable interessiert nur die ISR. Also sollte 
sie auch dort lokal sein.

static char   uiPWMCounter;

>unsigned char  uiPWMRed;
>unsigned char  uiPWMGreen;
>unsigned char  uiPWMBlue;

Und hier fehlt das volatile ;-)

Siehe Interrupt.

>void timer_init(void)
>{

>  TCCR0A |= (1<<WGM01);
>  TCCR0B |= (1<<CS01);
>  OCR0A = 10;
>  TIMSK0 |= (1<<OCIE0A); //iNTERRUPT BEIM erreichen des TOP wertes
>}

Das ist Prescaler 1/8, sprich 1,25 MHz, macht bei einem 8-Bit Zähler 
4,88 kHz PWM-Frequenz. Unsinnig hoch.

>Wenn ich nun als Counter ein Byte also ein Char verwende,
>dann läuft mir der PWM mommentan mit einer Frequenz von knapp 500Hz

Wirklich? Da fehlt eher Faktor 10. ODer dein Controller läuft mit dem 
internen RC-Oszillator von 1 MHz. AVR Fuses geprüft?

>Dann erhalte ich zwar immer noch knapp 500Hz jedoch wird die Ausführung
>des Restlichen Programmes massiv verzögert.

>Etwa um den Faktor 50 mal langsamer.

Ist ein Nebeneffekt. Wenn gleich die 16 Bit Rechung etwas länger dauert, 
sooo viel ist das nicht.

>Dauert das vergleichen eines Integers so viel länger als das eines
>chars?

Nicht soooo viel.

>Hat jemand Empfehlungen, wie ich meinen Code auf speed optimieren
>könnte?

Indem du VOLLSTÄNDIGEN Code als Anhang postest.

>Ich würde gerne mehr als nur 8bit PWM haben.
>10bit und mehr wäre mein Ziel.

Siehe Soft-PWM

von avr (Gast)


Lesenswert?

* volatile wegmachen (da du den Counter nur in der ISR verwendest)
* counter runterzählen und mit 0 vergleichen
* die drei Leds in einem Portzugriff ausschalten

Aber eigentlich sollte der Code, wenn er auch nicht ganz effektiv ist, 
reichen. Bei noch mehr Leds würde ich zu BAM greifen.

von C. H. (hedie)


Lesenswert?

Falk Brunner schrieb:
> Das ist Prescaler 1/8, sprich 1,25 MHz, macht bei einem 8-Bit Zähler
> 4,88 kHz PWM-Frequenz. Unsinnig hoch.

Das ist nicht korrekt.

Du vergisst meinen TOP Value von 10.
Damit liege ich bei 125Khz und dann zählt er auf 250 was dann
einer PWM Frequenz von 500Hz entspricht!

Falk Brunner schrieb:
>>Wenn ich nun als Counter ein Byte also ein Char verwende,
>>dann läuft mir der PWM mommentan mit einer Frequenz von knapp 500Hz
>
> Wirklich? Da fehlt eher Faktor 10. ODer dein Controller läuft mit dem
> internen RC-Oszillator von 1 MHz. AVR Fuses geprüft?

Ich spreche hier nur vom Datentyp. Der Topwert welcher verglichen wird 
(250) belibt der selbe.

Falk Brunner schrieb:
>>Hat jemand Empfehlungen, wie ich meinen Code auf speed optimieren
>>könnte?
>
> Indem du VOLLSTÄNDIGEN Code als Anhang postest.

Ich bezog mich nur auf meinen PWM Code in sich.

Falk Brunner schrieb:
>>Ich würde gerne mehr als nur 8bit PWM haben.
>>10bit und mehr wäre mein Ziel.
>
> Siehe Soft-PWM

Das was ich hier tue ist bereits Soft-PWM.

Aber trozdem Danke für deine Antwort :)

avr schrieb:
> * volatile wegmachen (da du den Counter nur in der ISR verwendest)
> * counter runterzählen und mit 0 vergleichen
> * die drei Leds in einem Portzugriff ausschalten

Das war die Rettung! :)

avr schrieb:
> Bei noch mehr Leds würde ich zu BAM greifen.

BAM? Bill at Monday? :)

von kaplic (Gast)


Lesenswert?

BAM = Bit Angle Modulation

von Falk B. (falk)


Lesenswert?

@Claudio Hediger (hedie)

>> Das ist Prescaler 1/8, sprich 1,25 MHz, macht bei einem 8-Bit Zähler
>> 4,88 kHz PWM-Frequenz. Unsinnig hoch.

>Das ist nicht korrekt.

>Du vergisst meinen TOP Value von 10.
>Damit liege ich bei 125Khz und dann zählt er auf 250 was dann
>einer PWM Frequenz von 500Hz entspricht!

Hmm, dann hst du aber ebenfalls unsinnige 125 kHz Interruptfrequenz. 
Macht bei 10 MHz CPU-Takt gerade mal noch 80 Takte pro ISR. Das ist SEHR 
knapp. Siehe Artikel Soft-PWM.

>Ich spreche hier nur vom Datentyp. Der Topwert welcher verglichen wird
>(250) belibt der selbe.

Jaja, hab den Waveform Mode übersehen.

>> Indem du VOLLSTÄNDIGEN Code als Anhang postest.

>Ich bezog mich nur auf meinen PWM Code in sich.

Ich ebenfalls ;-)

>Das was ich hier tue ist bereits Soft-PWM.

Nur schlecht implementiert.

>> * volatile wegmachen (da du den Counter nur in der ISR verwendest)
>> * counter runterzählen und mit 0 vergleichen
>> * die drei Leds in einem Portzugriff ausschalten

>Das war die Rettung! :)

Nur minimal. Deine CPU dampft immern noch kurz vor der 
Überlastungsgrenze.

von Thomas E. (thomase)


Lesenswert?

Claudio Hediger schrieb:
> Das war die Rettung! :)
Du bist aber immer ganz hart an der Grenze.

Claudio Hediger schrieb:
> TCCR0B |= (1<<CS01);
>   OCR0A = 10;
Dein Timer zählt bis 80 und löst einen Interrupt aus.
Das bedeutet, du hast 80 Takte, bis der nächste Interrupt kommt.
Dafür ist deine ISR aber viel zu lang. Wenn die fertig ist, steht sofort 
wieder das nächste Flag an. Das Hauptprogramm wird dadurch nach nach 
jedem
(Assembler-)Befehl unterbrochen. Sozusagen in Einzelschritten 
abgearbeitet.
Mit dieser Maßnahme:

Claudio Hediger schrieb:
> avr schrieb:
>> * volatile wegmachen (da du den Counter nur in der ISR verwendest)
>> * counter runterzählen und mit 0 vergleichen
>> * die drei Leds in einem Portzugriff ausschalten

hast du dir für den Moment ein paar Takte Luft verschafft. Aber mit 10 
Bit wird das nichts.

Wozu 500Hz? 100Hz reichen auch. Da hast du 5x soviel Zeit.

Claudio Hediger schrieb:
> Controller: Atmega324PV mit 10MHz
Und ein 324P mit 20MHz verdoppelt die Zeit nochmal.

mfg.

von xfr (Gast)


Lesenswert?

Schau mal in den anderen Thread:
Beitrag "Poti Wert zu PWM Signal verarbeiten"

Da macht jemand das gleiche und ich und andere haben ihm was dazu 
geschrieben.

Was Du eigentlich willst, ist, den Timer so zu konfigurieren, dass Du in 
OCR0A die Helligkeit der LED schreibst. Der Counter zählt immer von 0 
bis 255 durch. Bei 0 wird die LED eingeschaltet (Counter Overflow) und 
bei Erreichen von Compare (OCR0A) wieder aus. Das Ein- und Ausschalten 
kannst Du entweder in der Overflow- und Compare-Match-ISR machen oder Du 
schließt die LED an dem Pin an, der zum Timer gehört. Dann geht das 
völlig automatisch ohne Interrupts und ohne Flackern und der 
Mikrocontroller kann sinnvollere Dinge tun.

von xfr (Gast)


Lesenswert?

Sehe gerade erst, Du willst eigentlich mehr als 8 Bit PWM. Dann solltest 
Du trotzdem einen Hardware-Timer benutzen und Dir mehrere 16-Bit-Timer 
in Software erzeugen. Allerdings nicht, indem Du in der Overflow-ISR 
immer nur um eins hochzählst. Sonder Du rechnest Dir aus, wie viele 
Zyklen es bis zur nächsten Aktion sind und schreibst den Wert ins 
Compare-Register. Bei einem 8-Bit-Timer sind dazu möglicherweise erstmal 
mehrere Overflows nötig. Aber die erhöhen den Zähler dann gleich um 256. 
Die Feinarbeit machst Du mit dem Compare-Register.

von avr (Gast)


Lesenswert?

xfr schrieb:
> Sonder Du rechnest Dir aus, wie viele
> Zyklen es bis zur nächsten Aktion sind und schreibst den Wert ins
> Compare-Register.

Da ist BAM nun wirklich bei Weitem einfacher.

von Falk B. (falk)


Lesenswert?

@  avr (Gast)

>Da ist BAM nun wirklich bei Weitem einfacher.

Beispiel?

von avr (Gast)


Lesenswert?

Bei der intelligenten Softpwm Methode muss man die PWM-Werte sortieren. 
Ok bei 3 Werten fällt das noch nicht ins Gewicht. Je mehr Kanäle man 
braucht desto eher eignet sich BAM.

Bei 10bit BAM braucht man ein Array mit 10 Werten. Diese werden so 
gefüllt, dass man sie in der ISR nur nacheinander ausgeben muss. Bei 
einer guten Implementierung ist das auch relativ schnell.
1
#define BAM_BIT(x) if(val & (1<<x)) buf[x] |= bit
2
3
volatile uint16_t buffer[3];
4
uint8_t bam[10];
5
void SetValues(void)
6
{
7
  uint8_t buf[10];
8
  uint8_t bit = 1;
9
  for(int i = 0; i < 3; i++)
10
  {
11
    uint8_t val = buffer[i];
12
    BAM_BIT(0);
13
    BAM_BIT(1);
14
    BAM_BIT(2);
15
    BAM_BIT(3);
16
    BAM_BIT(4);
17
    BAM_BIT(5);
18
    BAM_BIT(6);
19
    BAM_BIT(7);
20
    BAM_BIT(8);
21
    BAM_BIT(9);
22
    bit <<= 1;
23
  }
24
  memcpy(bam, buf, 10);
25
}

ist aber ungetestet.

von schnupp (Gast)


Lesenswert?

Auf was ist denn die code optimierung eingestellt?

von C. H. (hedie)


Lesenswert?

Thomas Eckmann schrieb:
> Wozu 500Hz? 100Hz reichen auch. Da hast du 5x soviel Zeit.

Diese 500Hz hatte ich mit einem TopWert von 250
Ich wollte jedoch 1000 folglich faktor 4 mehr und damit sinkt die 
Frequenz auf 125Hz.

Thomas Eckmann schrieb:
>> Controller: Atmega324PV mit 10MHz
> Und ein 324P mit 20MHz verdoppelt die Zeit nochmal.

Habe eben 160 PV's hier. deshalb mache ich das mit denen.

xfr schrieb:
> ...Sonder Du rechnest Dir aus, wie viele
> Zyklen es bis zur nächsten Aktion sind und schreibst den Wert ins
> Compare-Register...

Ich verstehe deine Idee leider nicht ganz.

schnupp schrieb:
> Auf was ist denn die code optimierung eingestellt?

Auf Size Optimaizations (-Os)


------------------------------------

Habe nun folgende Anpassungen vorgenommen:
1
unsigned int       uiPWMCounter;
2
volatile unsigned int  uiPWMRed;
3
volatile unsigned int  uiPWMGreen;
4
volatile unsigned int  uiPWMBlue;
5
6
ISR(TIMER0_COMPA_vect)
7
{
8
  if(uiPWMCounter == 0)
9
  {
10
    uiPWMCounter = PWM_MAX_VALUE; //ist 1000
11
    RGB_R_0;
12
    RGB_G_0;
13
    RGB_B_0;
14
  }
15
16
  if(uiPWMCounter == uiPWMRed)     RGB_R_1;
17
  if(uiPWMCounter == uiPWMGreen)     RGB_G_1;
18
  if(uiPWMCounter == uiPWMBlue)     RGB_B_1;
19
20
  uiPWMCounter--;
21
}

...
1
void timer_init(void)
2
{
3
4
  TCCR0A |= (1<<WGM01);  //CTC Modus für Timer0
5
  TCCR0B |= (1<<CS01);    //CLK / 8 = 1.25MHz
6
  OCR0A = 20-1;      //nochmals durch 20 teilen = 62.5KHz (durch ISR divided / 1000) = 62.5Hz PWM Frequenz!
7
  TIMSK0 |= (1<<OCIE0A);   //Interrupt beim erreichen des TOP wertes
8
}

Damit erreiche ich eine PWM-Frequenz von 62.5Hz.
Und habe beim Timer einen Topwert von 20 was also 160 "übrigen" 
Taktzyklen entsprechen würde zwischen jedem Interrupt.

Das restliche Programm wird nun nicht merklich beeinträchtigt und der 
PWM sauber erzeugt.

So wie ich gelesen habe, gibt es jetzt nicht die ultimative Lösung für 
mich, wie ich meinen PWM eleganter erzeugen könnte.
Wenn ich da etwas falsch verstanden habe, bitte korrigiert mich.

von xfr (Gast)


Lesenswert?

Claudio Hediger schrieb:
> So wie ich gelesen habe, gibt es jetzt nicht die ultimative Lösung für
> mich, wie ich meinen PWM eleganter erzeugen könnte.
> Wenn ich da etwas falsch verstanden habe, bitte korrigiert mich.

Doch, ist hier im Artikel beschrieben: 
http://www.mikrocontroller.net/articles/Soft-PWM (unter intelligenter 
Lösungsansatz). Ist zugegebenermaßen nicht ganz trivial, aber der Code 
steht da ja schon fertig.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Claudio Hediger schrieb:

> volatile unsigned char   uiPWMCounter;
>
> ISR(TIMER0_COMPA_vect)
> {
>   uiPWMCounter++;
>   if(uiPWMCounter == 250)
>   {
>     uiPWMCounter = 0;
>     RGB_R_0; //LED's ausschalten
>     RGB_G_0;
>     RGB_B_0;
>   }
>
>   if(uiPWMCounter == uiPWMRed)   RGB_R_1; //LED einschalten
>   if(uiPWMCounter == uiPWMGreen)   RGB_G_1;
>   if(uiPWMCounter == uiPWMBlue)   RGB_B_1;
> }

uiPWMCounter ist volatile, d.h. in obigem Code wird auf diesen Wert 
(statisch) 7 mal zugegriffen. Wozu?

Alternative:
1
ISR (TIMER0_COMPA_vect)
2
{
3
  unsigned char counter = uiPWMCounter + 1;
4
  if (counter == 250)
5
  {
6
    counter = 0;
7
    RGB_R_0; //LED's ausschalten
8
    RGB_G_0;
9
    RGB_B_0;
10
  }
11
12
  uiPWMCounter = counter;
13
14
  if (counter == uiPWMRed)     RGB_R_1;
15
  if (counter == uiPWMGreen)   RGB_G_1;
16
  if (counter == uiPWMBlue)    RGB_B_1;
17
}

Das fasst die Variable nur 2x an.

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.