Forum: Compiler & IDEs Verständnisfrage für Anfänger


von Heiko L. (Gast)


Lesenswert?

Guten Tag,

als Neuling in diesem Gebiet möchte ich ein Software PWM programmieren.
Dazu habe ich neben anderen auch folgenden Code hier im Tutorial 
gefunden (siehe unten).
Bei uint8_t könnte man doch auch den "Rückfallwert" von 100 auf 255 
stellen, um das Tastverhältnis noch genauer zu stellen, oder?
Ist der Code gut zu gebrauchen oder, so wie er ist, nur als 
Anschauungsbeispiel gedacht, der aus Performancegründen verändert werden 
muss? (andere Beispiele waren viel größer)
Wieviel Leistung erfordert solch ein Code, wenn etwa 15 LEDs einzeln 
gedimmt werden sollen?
(Mikrocontroller: Atmega 644 PU 20)


Code in C:

int main( void )
{
  uint8_t pwm_soll = 30; // gewünschter Dimmerwert 0..100
  uint8_t pwm_phase = 0; // Laufwert der Schleife 0..100

  DDRB |= (1<<PB0);

  while( 1 )
  {
    if( pwm_soll == pwm_phase )
    {
      PORTB |= (1<<PB0); // active low LED aus
    }
    pwm_phase++;
    if( pwm_phase == 100 )
    {
      pwm_phase = 0;
      PORTB &= ~(1<<PB0); // active low LED an
    }
  }
  return 0;
}

von Karl H. (kbuchegg)


Lesenswert?

Heiko L. schrieb:

> Bei uint8_t könnte man doch auch den "Rückfallwert" von 100 auf 255
> stellen, um das Tastverhältnis noch genauer zu stellen, oder?

Hat aber den Nachteil, dass dadurch die PWM Frequenz in Summe kleiner 
wird.

> Ist der Code gut zu gebrauchen oder, so wie er ist, nur als
> Anschauungsbeispiel gedacht, der aus Performancegründen verändert werden
> muss? (andere Beispiele waren viel größer)

Der Code hat den Nachteil, dass der µC ausser der PWM nichts anderes 
mehr machen kann.
PWM ohne Timer ist in der Praxis sinnlos.
Sieh diesen speziellen Code eher als Anschauung an, wie eine PWM im 
Prinzip funktioniert.

> Wieviel Leistung erfordert solch ein Code, wenn etwa 15 LEDs einzeln
> gedimmt werden sollen?

Leistung hast du genug, dass ist nicht das Problem.

von bobtheworker (Gast)


Lesenswert?

Sowohl deine Frequenz als auch deine Tastverhältnisse sind hier Abhängig 
von der Frequenz mit der dein Kontroller läuft, den Taktzyklen die ein 
Befehl braucht, und der Umsetzung des Complilers (also wie viele 
Assembler-Befehle er daraus macht)

Denke nicht das jemals das raus kommt was du erwartest.

von Heiko L. (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Hat aber den Nachteil, dass dadurch die PWM Frequenz in Summe kleiner
> wird.

Stimmt, daran hab ich ja gar nicht gedacht.
Sind Gleitkommazahlen als Dimmwert in diesem Bereich eine Alternative?

> Der Code hat den Nachteil, dass der µC ausser der PWM nichts anderes
> mehr machen kann.
> PWM ohne Timer ist in der Praxis sinnlos.
> Sieh diesen speziellen Code eher als Anschauung an, wie eine PWM im
> Prinzip funktioniert.

Kann ich mir das so vorstellen, dass im Hauptprogramm ein Timer 
aktiviert  und Interrupt eingeschaltet wird, dann regelmäßig in ein 
Unterprogramm gesprungen wird, welches einen Zähler erhöht und alle 
Pins, denen ein Wert unter dem Zähler zugewiesen bekommen haben, gehen 
an, bis sich der Zähler ab einem gewissen Wert wieder zurück setzt?

Weitere Unterprogramme ließen sich ja dann einfach verwirklichen, oder?

Ach ja, der Atmega644 hat auf jedem I/O Pin auch die Pin-Change 
Interrupt Funktion. Also ließe sich doch jeder Pin als externer 
Interrupt benutzen, oder? (nicht für die LED Steuerung, sondern für eine 
Erweiterung)

von Karl H. (kbuchegg)


Lesenswert?

Heiko L. schrieb:
> Karl Heinz Buchegger schrieb:
>> Hat aber den Nachteil, dass dadurch die PWM Frequenz in Summe kleiner
>> wird.
>
> Stimmt, daran hab ich ja gar nicht gedacht.
> Sind Gleitkommazahlen als Dimmwert in diesem Bereich eine Alternative?

Gleitkommazahlen sind (auf einem µC in der Leitungsklasse um die es hier 
geht) fast nie eine Alternative.

Abgesheen davon: Das würde ja auch nichts ändern.

Das Grundprinzip ist ja immer noch:

Du hast n logische Zustände und entscheidest abhängig von einem Paramter 
m, ob ein Ausgang auf 0 oder auf 1 sein muss.
Das Verhältnis n/m entscheidet darüber, wie lange im Mittel der Ausgang 
auf 1 ist und n gibt vor, in wievielen Stufen dieses Verhältnis variiert 
werden kann.

Ob man das jetzt mit ganzen Zahlen, Fixkomma oder Gleitkomma realisiert, 
ändert ja am Prinzip nichts.

> Kann ich mir das so vorstellen, dass im Hauptprogramm ein Timer
> aktiviert  und Interrupt eingeschaltet wird, dann regelmäßig in ein
> Unterprogramm gesprungen wird, welches einen Zähler erhöht und alle
> Pins, denen ein Wert unter dem Zähler zugewiesen bekommen haben, gehen
> an, bis sich der Zähler ab einem gewissen Wert wieder zurück setzt?

Das ist eine Möglichkeit, ja.

> Ach ja, der Atmega644 hat auf jedem I/O Pin auch die Pin-Change
> Interrupt Funktion. Also ließe sich doch jeder Pin als externer
> Interrupt benutzen, oder? (nicht für die LED Steuerung, sondern für eine
> Erweiterung)

Das 'Problem' besteht darin, dass du den PWM-Interrupt möglichst 
gleichmässig ausgelöst haben willst. PWM ist ja etwas, das über einen 
zeitlichen Mittelwert funktioniert. D.h. aber auch, dass jede 
Verzögerung kurzzeitig diesen zeitlichen Mittelwert durcheinander 
bringt.

von Rolf Magnus (Gast)


Lesenswert?

Heiko L. schrieb:
> Karl Heinz Buchegger schrieb:
>> Hat aber den Nachteil, dass dadurch die PWM Frequenz in Summe kleiner
>> wird.
>
> Stimmt, daran hab ich ja gar nicht gedacht.
> Sind Gleitkommazahlen als Dimmwert in diesem Bereich eine Alternative?

Wenn du nichts dagegen hast, daß deine PWM-Frequenz extrem kleiner wird 
und gleichzeitig der Speicherverbrauch explodiert...
(Mit anderen Worten: Nein)

>> Der Code hat den Nachteil, dass der µC ausser der PWM nichts anderes
>> mehr machen kann.
>> PWM ohne Timer ist in der Praxis sinnlos.
>> Sieh diesen speziellen Code eher als Anschauung an, wie eine PWM im
>> Prinzip funktioniert.
>
> Kann ich mir das so vorstellen, dass im Hauptprogramm ein Timer
> aktiviert  und Interrupt eingeschaltet wird, dann regelmäßig in ein
> Unterprogramm gesprungen wird, welches einen Zähler erhöht und alle
> Pins, denen ein Wert unter dem Zähler zugewiesen bekommen haben, gehen
> an, bis sich der Zähler ab einem gewissen Wert wieder zurück setzt?

Kann man z.B. so machen. Teilweise können die µCs eine begrenzte Zahl an 
PWM-Kanälen auch komplett in Hardware machen, indem der Timer ein 
Register für den PWM-Wert und einen Vergleicher hat, der dann direkt den 
Pin ansteuert.

> Weitere Unterprogramme ließen sich ja dann einfach verwirklichen, oder?

Ja.

> Ach ja, der Atmega644 hat auf jedem I/O Pin auch die Pin-Change
> Interrupt Funktion. Also ließe sich doch jeder Pin als externer
> Interrupt benutzen, oder? (nicht für die LED Steuerung, sondern für eine
> Erweiterung)

Ja.

von Heiko L. (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Gleitkommazahlen sind (auf einem µC in der Leitungsklasse um die es hier
> geht) fast nie eine Alternative.
>
> Abgesheen davon: Das würde ja auch nichts ändern.
>
> Das Grundprinzip ist ja immer noch:
>
> Du hast n logische Zustände und entscheidest abhängig von einem Paramter
> m, ob ein Ausgang auf 0 oder auf 1 sein muss.
> Das Verhältnis n/m entscheidet darüber, wie lange im Mittel der Ausgang
> auf 1 ist und n gibt vor, in wievielen Stufen dieses Verhältnis variiert
> werden kann.
>
> Ob man das jetzt mit ganzen Zahlen, Fixkomma oder Gleitkomma realisiert,
> ändert ja am Prinzip nichts.

Ja ok, da muss ich dir recht geben. Da hatte ich wohl einen Denkfehler 
drinne.

> Das ist eine Möglichkeit, ja.

Sehr gut. :)
Ich werde dann mal einen Code zusammen schreiben und euch mal drüber 
schauen lassen, was ihr davon so haltet.

> Das 'Problem' besteht darin, dass du den PWM-Interrupt möglichst
> gleichmässig ausgelöst haben willst. PWM ist ja etwas, das über einen
> zeitlichen Mittelwert funktioniert. D.h. aber auch, dass jede
> Verzögerung kurzzeitig diesen zeitlichen Mittelwert durcheinander
> bringt.

Es soll parallel ein IR-Empfänger Singale auswerten und damit die 
Dimmung der LEDs beeinträchtigen und ein kleines Display (zweizeilig) 
angesprochen werden.
Ich denke, das sollte noch möglich sein, ohne den Rest allzu sehr zu 
beeinträchtigen, oder?

von Heiko L. (Gast)


Lesenswert?

Hier mal ein Entwurf des eines Codes.
Ich hab bisher noch keine große Erfahrung mit Microcontrollern, also 
kann noch einiges falsch sein.

Er soll ein SoftPWM erzeugen und bei einem Signal auf Pin 6 im Register 
C soll die Led dunkler werden (nur als Beispiel).

Was haltet ihr davon?

#include <avr/io.h>
#include <avr/interrupt.h>

volatile uint_t pwm_soll = 30;

ISR(TIMER0_COMPA_vect)
{
  zähler++;
  if( zähler >= pwm_soll)
    {
    PORTA = 0x01; // LED ein
    }

  if( zähler == 100)
  {
    zähler = 0;
    PORTA = 0x00; // LED aus
  }

}

ISR(PCINT2_vect)
{
  pwm_soll = 60;
}

int main( void )
{
  DDRA = 0xFF;
  DDRC = 0xBF; // Alle auf Ausgang, außer PIN6

  uint8_t zähler=0;

  PCICR |= (1 << PCIE2);
  PCMSK2 |= (1<<PCINT22);

  TCCR0A = 1;
  TIMSK0 |= (1<<OCIE0A);
  ;
  sei();

  while(1){};
  return 0;

 }

von Karl H. (kbuchegg)


Lesenswert?

Schick es durch den Compiler und korriger erst mal die C-Fehler :-)


(Aber vom Prinzip her ist das für eine PWM ok. Ich werde zwar meiner 
Lebtag nicht verstehen, warum alle Welt immer als erstes einen Taster an 
einen Interrupt hängen muss, aber ok, das ist eine andere Baustelle)

von Heiko L. (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Schick es durch den Compiler und korriger erst mal die C-Fehler :-)
>
> (Aber vom Prinzip her ist das für eine PWM ok. Ich werde zwar meiner
> Lebtag nicht verstehen, warum alle Welt immer als erstes einen Taster an
> einen Interrupt hängen muss, aber ok, das ist eine andere Baustelle)

Auch die Ansteuerung der Register ist so korrekt?

Der Taster soll hier nur als Platzhalter für ein beliebiges Input-Signal 
gelten.
Nachher soll das ganze über IR gesteuert werden (wenn ich es ohne 
Probleme hinbekomme).

Heute Abend wir das ganze auch erstmal mit der Hardware getestet.

Ich danke dir schon mal für deine Hilfe! :)

von Rolf Magnus (Gast)


Lesenswert?

Heiko L. schrieb:
> Auch die Ansteuerung der Register ist so korrekt?

Nein. Die Timer-Einstellung stimmt nicht. Ich vermute, du wolltest 
eigentlich TCCR0B statt TCCR0A beschreiben.
Und warum benutzt du den Compare-Match-Interrupt? Du hast das 
Compare-Register nirgends beschrieben, außerdem wäre eh der 
Overflow-Interrupt passender.

von Levi (Gast)


Lesenswert?

Ich hab mir die Teile aus dem Code, die ich noch nicht kannte, aus 
anderen Programmschnipseln abgeschaut.
Daher hätte es mich auch gewundert, wenn so alles korrekt wäre. ;)

Was ist denn der Unterschied zwischen TCCR0A und TCCR0B?
Ich dachte mir einfach, sie wären beide gleich zu benutzen und ich fange 
bei A an, was wohl falsch war.

Wie müsste es ingesamt denn richtig heißen?

von Karl H. (kbuchegg)


Lesenswert?

Levi schrieb:
> Ich hab mir die Teile aus dem Code, die ich noch nicht kannte, aus
> anderen Programmschnipseln abgeschaut.
> Daher hätte es mich auch gewundert, wenn so alles korrekt wäre. ;)
>
> Was ist denn der Unterschied zwischen TCCR0A und TCCR0B?

Mach dir selbst einen Gefallen und hol dir von Atmel das Datenblatt zu 
deinem Chip. Da ist das alles genauestens beschrieben.

Ohne das Datenblatt kannst du nicht programmieren.

von Heiko L. (Gast)


Lesenswert?

Ich hab den Code mal ein wenig angepasst und Fehler in der Syntax 
bereinigt.
Er funktioniert prinzipiell auch, nur ist das Zeitfenster viel zu groß.
Wenn ich das richtig verstanden habe, geht die Led ja nach 100 
Timerüberläufen an, oder? Das ist im Moment eine Zeitspanne von etwas 
über 10s, also von PWM noch weit entfernt.
Habe ich noch einen Fehler bei den Bezeichnungen, einen grundsätzlichen 
Gedankenfehler oder lässt sich das ganze leicht an kürzere Taktzeiten 
anpassen?

Code in C:

#include <avr/io.h>
#include <avr/interrupt.h>

volatile uint8_t pwm_soll = 50, i=0;

ISR(TIMER0_OVF_vect)
{
  i++;
  if( i >= pwm_soll)
  {
    PORTA = 0x01; // LED ein
  }

  if( i == 100)
  {
    i = 0;
    PORTA = 0x00; // LED aus
  }

}

ISR(PCINT2_vect)
{
  pwm_soll = 60;
}

int main( void )
{
  DDRA = 0xFF;
  DDRC = 0xBF; // Alle auf Ausgang, außer PIN6

  PCICR |= (1 << PCIE2);
  PCMSK2 |= (1<<PCINT22);

  TCCR0B |= (1<<CS00)|(1<<CS02);
  TIMSK0 |= (1<<TOIE0);

  sei();

  while(1){};
  return 0;

 }

von Heiko L. (Gast)


Lesenswert?

Ah, ich sehe gerade, wenn ich den Vorteiler entferne, scheint es ohne 
Probleme zu funktionieren.

Wer mal das gleiche Problem hat:
Ich habe also: TCCR0B |= (1<<CS00)|(1<<CS02);
durch:         TCCR0B |= (1<<CS00);
ersetzt.

Nun scheint alles ok zu sein, oder?

von Karl H. (kbuchegg)


Lesenswert?

Heiko L. schrieb:

> Nun scheint alles ok zu sein, oder?

Was nicht in Ordnung ist ist, dass du diese Dinge durch Versuch und 
Irrtum klärst, wenn du sie eigentlich durchrechnen solltest.

Ja, man kann berechnen, wie lange die Dinge dauern. Und nein, das ist 
gar nicht mal so schwer, sondern absolut logisch nachvollziehbar.

http://www.mikrocontroller.net/articles/FAQ#Timer

von Heiko L. (Gast)


Lesenswert?

Mir ist klar, dass man das auch ausrechnen kann, doch bringt das nichts, 
wenn man versehentlich einen Faktor von 1000 im Programm hat, den man 
erst beim zweiten Durchschauen sieht.

Ach ja, es gibt Menschen, die eher durch ausprobieren und und 
berichtigen eigener Fehler lernen, als sich den Stoff NUR anzulesen.

Gruß

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.