www.mikrocontroller.net

Forum: Compiler & IDEs PWM: ASM-Code in C-Code umwandeln


Autor: Tobias Paul (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
es geht um folgenden ASM Code:
http://www.mikrocontroller.net/articles/AVR-Tutorial:_PWM
Abschnitt: PWM in Software

Leider funktioniert mein Programm nicht so ganz wie ich es mir 
vorstelle.
Um den Fehler einzugrenzen, wollte ich mal fragen ob mal jemand kurz 
einen Blick auf meinen C-Code werfen kann und mir sagen kann, ob ich die 
ASM Befehle richtig nach C portiert hab.

Interrupt-Routine:
timer0_overflow:                      ; Timer 0 Overflow Handler
        inc     PWMCount              ; den PWM Zähler von 0 bis
        cpi     PWMCount, 128         ; 127 zählen lassen
        brne    WorkPWM
        clr     PWMCount
SIGNAL(SIG_OVERFLOW0) // Timer0 Overflow Interrupt
{
  PWMCount++;
  if (PWMcounter <= 128) 
   { PWMwork(); }
  else
    { PWMcounter = 0; }
  
}


PWM ausgeben:
ASM-Code steht im Artikel, den ich verlinkt hab.
void PWMwork()
{
 LEDstat = 0;
 
 if (PWMcounter <= 20)
 { LEDstat = LEDstat + 0x01; }
 
 if (PWMcounter <= 60)
 { LEDstat = LEDstat + 0x02; }
 
 if (PWMcounter <= 80)
 { LEDstat = LEDstat + 0x04; }
 
 PORTB = LEDstat;
}

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Auf die Schnelle würde ich das hier als C-Code draus machen:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>

register uint8_t PWMCount asm("r2");
static uint8_t ocrs[6] =
{
    0, 1, 10, 20, 80, 127
};

static void
ioinit(void)
{
    DDRB = 0xff;
    TCCR0 = _BV(CS00);
    TIMSK = _BV(TOIE0);
    sei();
}


ISR(TIMER1_OVF_vect)
{
    if (++PWMCount == 128)
    {
        PWMCount = 0;
    }
    uint8_t temp = 0b11000000;
    uint8_t i, j;
    for (i = 0, j = 1; i < 6; i++, j <<= 1)
    {
        if (PWMCount > ocrs[i])
        {
            temp |= j;
        }
    }
    PORTB = temp;
}

int
main(void)
{
    ioinit();
    for (;;)
        sleep_mode();
}

Ist natürlich ungetestet, die Ergänzung, die CPU schlafen zu legen,
darfst du auch weglassen. ;-)

Wenn du das mit -O3 compilierst, hast du das loop enrolling in der ISR
ebenfalls mit drin, d. h. das Zusammenbasteln der Bits ist genauso
effektiv wie in der Assemblerversion.  Wenn du das sleep_mode()
weglässt, darfst du statt ISR() auch ISR_NAKED() schreiben, dann
werden Prolog und Epilog der ISR noch weggelassen.  Theoretisch leben
meine ocr[]-Daten zwar im RAM, aber der Optimizer erkennt den
`degraded case', dass sie konstant sind :) und ersetzt die Zahlen
gleich komplett...  Das wird in der Realität sicher anders werden.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die direkte Umsetzung ist eher sowas:
SIGNAL(SIG_OVERFLOW0) // Timer0 Overflow Interrupt
{
  uint8_t LEDstat;

  PWMcounter++;
  if (PWMcounter == 128) 
    PWMcounter = 0;

  LEDstat = 0;

  if (PWMcounter > 20)
    LEDstat |= 0x01;

  if (PWMcounter > 60)
    LEDstat |= 0x02;
 
  if (PWMcounter > 80)
    LEDstat |= 0x04;
 
  PORTB = LEDstat;
}

Autor: Tobias Paul (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hab meinen Fehler gefunden. Hatte ein paar ASM Befehle falsch 
interpretiert.

Ich hätte da noch ein paar Fragen zu PWM. Wie PWM vom Prinzip her 
funktioniert ist mir klar, nur bei der Umsetzung in der Software sind 
mir einige Dinge noch nicht so ganz klar.

Ich zähle mit dem Timer0 bei jedem Overflow den PWMCounter eins hoch. 
Bei 128 setze ich den Zähler wieder auf Null.
Warum eigendlich genau bei 128? Hat das einen bestimmten Grund, ich 
könnte ja genau so gut bei 100 oder 200.

if (PWMcounter > 0)
 LEDstat |= 0x01;

Da der Zähler immer größer als 0 ist, wäre die LED in dem Fall immer 
100% eingeschaltet. Richtig?


if (PWMcounter > 64)
 LEDstat |= 0x01;

In dem Fall hätte ich dann eine PWM von 50:50. Auch Richtig?

Bei (PWMcounter > 32) dann 72:25 (ein:aus) usw.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Tobias Paul wrote:
>
> Ich zähle mit dem Timer0 bei jedem Overflow den PWMCounter eins hoch.
> Bei 128 setze ich den Zähler wieder auf Null.
> Warum eigendlich genau bei 128? Hat das einen bestimmten Grund, ich
> könnte ja genau so gut bei 100 oder 200.

Könntest du.
Du musst nur einen Kompromiss finden, bei dem
* du noch genügend viele Updates in der Sekunde zustande
  bringst
* du noch genügend hohe PWM-Auflösung bekommst.
  Eine PWM mit 3 Bit (also 8 Stufen) wird dir beim Dimmen
  einer LED nicht viel bringen, weil nach der Stufe 0
  (LED aus), die nächste Stufe schon so hell ist, dass da ein
  enormer Helligkeitssprung stattfindet, während beim Übergang
  von Suufe 1 auf 2, 2 auf 3 usw. die Helligkeit nur mehr sehr
  wenig zunimmt.


>
> if (PWMcounter > 0)
>  LEDstat |= 0x01;
>
> Da der Zähler immer größer als 0 ist,

Sagt wer?
Ganz im Gegenteil: Hier
  if (PWMcounter == 128) 
    PWMcounter = 0;
wird der Counter explizit auf 0 gesetzt.

> wäre die LED in dem Fall immer
> 100% eingeschaltet.

Oder 100% ausgeschaltet. Kommt drauf an, wie deine
LED an den Port angeschlossen ist.

> if (PWMcounter > 64)
>  LEDstat |= 0x01;
>
> In dem Fall hätte ich dann eine PWM von 50:50. Auch Richtig?

Ja.

>
> Bei (PWMcounter > 32) dann 72:25 (ein:aus) usw.

Siehe den 100% Fall: kann auch anders rum sein.
Aber vom Prinzip her: Stimmt schon.

Autor: Tobias Paul (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke für eure Hilfe. Habs jetzt verstanden und auch hinbekommen.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.