mikrocontroller.net

Forum: Compiler & IDEs PWM mit Timer0


Autor: Ronny Schulz (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich versuche gerade in paar LEDs zu dimmen. Das geht aber nicht so
recht. Das ganze soll PWM-gesteuert sein. Die 3 LED sind aber immer
voll an. Egal welchen Wert "value" hat. Hier mal meine Code.
Vorübergehend sind mal 3 Ports geschalten. Alles über einen ULN2003AN.

void abled_init(unsigned char value)
{
  ABLED_PORT_DDR |= 1<<PD4 | 1<<PD5 | 1<<PD6;        /* Port auf Ausgang 
*/
  ABLED_PORT_OUT &= ~(1<<PD4 | 1<<PD5 | 1<<PD6);      /* Low ausgeben */
  cli();                            /* IRQs abschalten */
  TCCR0 = 1<<WGM00 | 1<<WGM01 | 1<<CS02 | 1<<CS00;      /* Timer 0: Fast
PWM, clk/1024, bei 8 MHz */
  TCNT0 = value;                        /* Startwert setzen */
  OCR0 = 0;                          /* Output compare auf 0 setzen */
  TIMSK |= 1<<OCIE0;                      /* Output compare -> IRQ */
  sei();                            /* IRQs einschalten */
  return;
}

void abled_change(unsigned char value)
{
  cli();                            /* IRQs abschalten */
  TCNT0 = value;                        /* Startwert neu setzen */
  sei();                            /* IRQs einschalten */
  return;
}

SIGNAL(SIG_OUTPUT_COMPARE0)
{
  ABLED_PORT_OUT |= 1<<PD4 | 1<<PD5 | 1<<PD6;        /* High ausgeben */
}

SIGNAL(SIG_OUTPUT_OVERFLOW0)
{
  ABLED_PORT_OUT &= ~(1<<PD4 | 1<<PD5 | 1<<PD6);      /* Low ausgeben */
}

Autor: Florian Hrubesch (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Von dem her was ich grad im datasheet gesehen hab, ist timer0 nicht für
pwm vorgesehen und es gibt gar kein compare register und auch keinen
compare interrupt.
Du müsstes das alles im overflow machen und bei jedem overflow tcnt0
neu setzen um so ne art pwm zu bekommen.
cu Flo

Autor: Ronny Schulz (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hmm .. ich hatte natürlich vergessen zu erwähnen, um welchen Controller
es sich handelt. Ich rede hier vom ATmega16. Der hat natürlich diese
Funktionalität.

Autor: Florian Hrubesch (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
OK.
Sieht so aus als hättest du dein ocr0 auf null stehen.
tcnt0 brauchst bzw darfst du auf keinen fall ändern wenn du was änderst
dan ocr0 damit definierst du dann die helligkeit.

TCNT0 steht blos der Wert drinn den der Timer grad hat, da würd ich ned
drann rumspielen.
Bei jedem inkrement von tcnt0 durch den timer wird tcnt0 mit ocr0
verglichen und der entsprechende interrupt ausgelöst.
Bei jedem overflow wird der overflow ausgelöst.
Da nun OCR0 auf null ist wird beim overflow deine leds ausgemacht und
sofort wieder ein, weil dein ocr0= 0 ist.
Deswegen leuchten die led andauernd hell.
cu Flo

Autor: Ronny Schulz (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Upsa .. einfach mal was verwechselt. :)

Der gröbste Fehler, war jedoch der IRQ-Vektor. Da habe ich den falschen
angegeben. Jetzt läuft es auch soweit. Nur das die LEDs schon bei einem
Wert von 100 mit etwa voller Helligkeit leuchten. Schöner wäre
natürlich, wenn man das prozentual von 0 - 254 aufteilen könnte. Dazu
müssten aber die Timings genau auf die LED bzw. den ULN2003A
zugeschnitten sein. Oder wie kann man das verbessern.

Eine andere Frage stellt sich mir jetzt erst. Wie kann ich da mehrere
LEDs verschieden hell leuchten lassen. Ich habe ja nicht unedlich viele
Timer zur Verfügung, wo ich das Tastverhältnis einstellen kann. Da der
Overflow ja immer zu gleichen Zeit kommt könnte ich mir vorstellen, die
Auschaltzeit im "Compare"-IRQ für jede LED zu verändern. Das würde
aber heißen, dass das Compare-Register ständig in der IRQ-Routine
angepasst werden müsste bzw. es garnicht mehr über einen PWM-Timer
gemacht wird, sondern einen der eben bei jedem Overflow aufgerufen
wird, welches ich dann zeitlich durch 255 teilen müsste. Wie würdet ihr
das machen?

Hier nochmal der fehlerbereinigte Code:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/signal.h>

#include "abled.h"


unsigned char abled_table_rgb[3][3] = {
  { 0xFF, 0xFF, 0xFF },
  { 0xFF, 0xFF, 0xFF },
  { 0xFF, 0xFF, 0xFF }
};


void abled_init(unsigned char value)
{
  if (value) {
    ABLED_PORT_DDR |= 1<<PD4 | 1<<PD5 | 1<<PD6;      /* Port auf Ausgang 
*/
    ABLED_PORT_OUT |= 1<<PD4 | 1<<PD5 | 1<<PD6;      /* High ausgeben */
    cli();                          /* IRQs abschalten */
    TCCR0 = 1<<WGM00 | 1<<WGM01 | 1<<CS02;          /* Timer 0: Fast 
PWM,
clk/256, bei 8 MHz */
    TCNT0 = 0;                        /* Zähler auf 0 setzen */
    OCR0 = value;                      /* Output compare setzen */
    TIMSK |= 1<<OCIE0 | 1<<TOIE0;              /* Overflow + Output 
compare ->
IRQ */
    sei();                          /* IRQs einschalten */
  }
  return;
}

void abled_change(unsigned char value)
{
  cli();                            /* IRQs abschalten */
  OCR0 = value;                        /* Output compare neu setzen */
  sei();                            /* IRQs einschalten */
  return;
}

SIGNAL(SIG_OUTPUT_COMPARE0)
{
  ABLED_PORT_OUT &= ~(1<<PD4 | 1<<PD5 | 1<<PD6);      /* Low ausgeben */
}

SIGNAL(SIG_OVERFLOW0)
{
  ABLED_PORT_OUT |= 1<<PD4 | 1<<PD5 | 1<<PD6;        /* High ausgeben */
}

Autor: Florian Hrubesch (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hmm der msp430f149 hat 10 ausgängen für pwm;) Der 16bit timer vom avr
hat glaub ich auch mehrer compare-register.
Du musst die leds ja auch ned mit maximal möglicher geschwindigkeit
dimmen sondern es reicht ja wenn du ne pwm von 100Hz hast(also timer
einmal pro 100tel sekunde voll durchgelaufen) das müsste sich dann in
software aufsetzend auf dem timerinterrupt auch für mehrer leds machen
lassen.
Ich glaub das Problem das deine leds ned voll regelbar sind sind die
schaltzeiten probier mal nen anderen prescaler --> langsamer
cu Flo

Autor: Ronny Schulz (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die 10 würde wahrscheinlich immernoch nicht reichen. ;)

Das mit den einzelnen LEDs klappt zumindest mit 3 Stück schon so
halbwegs. Ich setze jedes mal in der IRQ-Routine einen neuen OCR0, der
dann der nächsthöhere aus einer Tabelle mit den Helligkeitswerten ist.
Die PWM mache ich jetzt mi 8MHz/256. Wenn ich auf /1024 gehe flackert
der ganze Kram.

Dafür gibt es allerdings andere Probleme, die ich in einem neuen Thread
setze. Irgendwas haut da mit C selbst nicht hin.

Danke für deine Hilfe. Wenn die Routine mal fertig ist, werde ich die
in die Codesammlung posten. Vielleicht braucht ja jemand sowas noch.

Autor: Jörg Wunsch (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wie viele LEDs willst Du denn steuern?

Das hört sich danach an, als würde der Controller weiter nichts tun
als die LEDs anzusteuern, dann kann man sicher genauso die PWMs in
Software schreiben.  Da kannst Du dann so viele davon haben, wie Du
freie Portpins hast (zumal die Genauigkeit in dem Falle eher eine
untergeordnete Rolle spielen dürfte).

Autor: Ronny Schulz (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich dachte da so an ca. 12 LEDs ... Die Genauigkeit ist natürlich
relativ uninteressant. Viel mehr soll der Controller nacher wirklich
nicht zu tun haben. Da kommen nur noch 2 - 3 Taster ran. Die man
natürlich durch die wenigen Funktionen, die man benötigt, u.U. auch
doch IRQs abfragen könnte. Aber das ist relativ egal.

Autor: Jörg Wunsch (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich würde mir eine Zeitgeberfunktion schreiben (bzw. hab' ich schon
:-), die timer queues verwaltet.  Für jeden Kanal läßt man dann das
An- und Abschalten durch einen software timer vornehmen.  Braucht
einen einzigen hardware timer mit entsprechender Auflösung.

Wenn man die Auflösung zu hoch ansetzt, wird die Interruptlast sicher
nicht zu verachten sein, andererseits wirst Du vermutlich kaum mehr
als 50 Helligkeitsstufen unterscheiden können müssen, und 50 Hz
Flimmerfrequenz müßten auch genug sein.  Das bedeutet eine
Zeitgeberrate von 2500 Hz bzw. 400 µs.  Wenn Du nur 1 MHz Taktfrequenz
hast, kann das schon bißchen knapp werden (Du hast dann maximal 400
ausführbare Befehle zwischen den Interrupts), bei höherem Takt ist das
aber sicher kein Problem mehr.

Autor: Ronny Schulz (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Naja wenn ich meine LEDs mal alle habe, werde ich das ganze verdrahten
und dann kann ich mich ja an die wirkliche Programmierung machen. Die
Hardwareseite ist ja nicht so das Problem. Aber dauern wird das sicher
dennoch ein weilchen. Allerdings ist es erstmal schln zu wissen, wie
man das hinbekommt. Und im Prinzip funktuniert ja meine Lösung schon
mehr oder weniger.

Zeig doch mal deine Queue her. ;)

Autor: Jörg Wunsch (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Zeig doch mal deine Queue her. ;)

OK, ich habe die Möglichkeit, timertick() aus einem Interruptkontext
direkt zu rufen, noch in den Hauptzweig meiner CVS-Version
zurückgepatcht.  Wenn Du die Bibliothek mit -DTMR_INTPROTECT
compilierst, darfst Du timertick() entsprechend direkt aus der
Timer-ISR heraus rufen (dafür wird mehr Code gebraucht).

Normalerweise sollte das aber eher so sein

volatile unsigned char timer_ticked;

SIGNAL(SIG_OUTPUT_COMPARExx)
{
  /* timer running in CTC mode, just trigger timertick() */
  timer_ticked = 1;
}


...
int
main(void)
{
  ...
  for (;;) {
    if (timer_ticked) {
      timer_ticked = 0;
      timertick();
    }
    ...
  }
}

http://www.sax.de/~joerg/timer.tar.gz

Die union timeoutarg erlaubt es, 16-bit (int) Werte direkt zu
übergeben, aber auf saubere Weise stattdessen einen Zeiger auf einen
größeren Datenbereich an die nach dem Timeout gerufene Funktion zu
übergeben.

Autor: Ronny Schulz (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hmm .. letztendlich will ich ja dennoch PWM nutzen und das geht ja im
CTC-mode nicht. Ich denke aber ich werde einfach deinen Tipp mit der
Queue nutzen. Einfach eine Queue aufbauen. Alle Ports und Zeiten
reinqueuen und dann den Timer starten. Somit ist das eine sehr flexible
Bibliothelk, die halt nur auf gewünschte Ports ein geünschtes
Software-PWM zur Verfügung stellt. Die Zeiten könnte man sich dann
idealerweise in der Initialisierung ausrechnen. So das dann nachher nur
noch der Pointer zur Queue und die Zeiten übertragen werden.

Programmiertechnisch ist das allerdings ein recht hoher Aufwand. Da
muss ich wirklich überlegen, ob ich mir die Mühe machen will. :)

Autor: Jörg Wunsch (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Da mich der Spaß selbst interessiert hat, hier mal eine
Implementierung des ganzen.  8 Software-PWM-Kanäle an Port B,
die LEDs sind low-aktiv.  Getestet auf STK500/502 (also mit
ATmega128), sollte sich einfach für andere Controller ändern
lassen.

Was mir beim Spielen aufgefallen ist: die Helligkeit der LEDs
zumindest am STK500 ändert sich bei geringen Tastverhaltnissen
drastisch, bei großen Tastverhältnissen kaum noch.  Zwischen
50 und 100 % kaum noch eine optische Änderungen.  Darum bin ich
erstens von 50 auf 250 PWM-Stufen gegangen (sonst waren die
Sprünge im unteren Bereich zu groß) und habe zweitens in der
kleinen Spielerei, die die PWM-Werte verändert, bei 50 %
aufgehört und fange von vorn an.

Möglicherweise ist dieser Effekt aber bei anderen LEDs nicht so
stark ausgeprägt.

Autor: Stefan Kleinwort (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Jörg:
Habe den Effekt auch schon beobachtet. Nachdem das schon > 10 Jahre her
ist, nehme ich an, dass es auf so ziemlich alle LEDs zutrifft ;-)
Optisch passt die Ausgabe, wenn man einen quadratischen Zusammenhang
programmiert:

PWM-Einzeit(16 Bit) = Helligkeit(8 Bit) * Helligkeit(8 Bit)

Stefan

Autor: Ronny Schulz (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
War bei meinen Tests aber auch so. Daher scheint das völlig normal zu
sein. So kann man sich nacher noch überlegen, wo die 100% erreicht
sind.

Überhaupt steigt die Helligkeit nicht linear an, was man gut bei einer
LED sieht, die man langsam voll aufdreht und dann wieder langsam runter
dreht. Und das eben ständig wiederholt.

Ich denke das Grundprinzip funktioniert erstmal. Deshalb werde ich mal
sehen, dass ich meine Schaltung komplett aufbaue und dann erst richtig
anfange mit programmieren.

Autor: TobyTetzi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ein sehr alter Thread, aber trotzdem!

Hast Du mittlerweile eine laufende Versin der RGB Ansteuerung?

Würde mich auch mal intressieren, deinen Code zu sehen.
Ebenso die abled.h.

Gruß Toby

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.