AVR ATTiny25/45/85: PWM in schnell mit Hilfe der PLL

In diesem Beitrag möchte ich ein wenig das PWM-Modul eines ATTiny25/45/85 vorstellen mit Schwerpunkt der Nutzung des PLL-Moduls als Taktquelle des PWM-Timers.

PWM steht für Pulse Width Modulation. Hierbei handelt es sich um ein Rechteck-Signal bei dem sich das Puls-Pause-Verhältnis verändern lässt. Damit lassen sich recht einfach Motoren in ihrer Geschwindigkeit beeinflussen, Servos als Stellglieder aus dem RC-Modellbausektor ansteuern oder auch, mittels geeigneter Tiefpassfilterung, Steuerspannungen generieren und noch einiges mehr.

Aufbau Beispiel

Da es hier nur um ein Beispiel geht werde ich hier demonstrieren, wie man damit eine Steuerspannung generieren kann (z.B. als Set-Wert-Vorgabe für ein Labornetzteil). Dazu wird am Pin PB2 eine Spannung von 0-5 Volt vorgegeben, an Pin PB1 wird das PWM-Signal ausgegeben und mittels eines 1kΩ Widerstandes und eines 1µF Kondensator gefiltert.

Der ATTiny wird bzgl. seiner Fuse-Bits mit den Werkseinstellungen betrieben, d.h. er läuft mit 1 MHz Taktrate. Bei maximaler PWM-Auflösung von 255 sind damit maximal PWM-Frequenzen von ~3921 Hz möglich. Genügt einem diese PWM-Frequenz nicht so lässt sich die Taktquelle für Timer 1 des ATTiny auf bis zu 64 MHz erhöhen wodurch die PWM-Frequenz auf bis zu 251 kHz steigt bei voller Auflösung. Eine weitere Steigerung der PWM-Frequenz lässt sich bewirken indem man den Wert, bis wohin der Timer zählen soll, verringert, maximal kann bis 255 gezählt werden.

Beispiel-Code

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

// Wert bis wohin der Timer zaehlen soll
static const uint8_t PWM_MAX_VALUE = 0xff;

int main(void){

  // zunaechst den ADC einstellen
  // IO-Clock = 1MHz => ADC-Prescaler auf 8 einstellen
  // ADC-Clock = 125 kHz, darf/soll zwischen 50kHz-200kHz liegen
  ADCSRA |= (1 << ADPS0)|(1 << ADPS1);
  // Referenzquelle auf AVCC einstellen und Kanal 1 auswaehlen
  ADMUX |= (1 << REFS0)|(1 << MUX0);
  // Beim Aendern der Referenzquelle wird eine Dummy-Messung empfohlen
  // ADC starten (default: free running)
  ADCSRA |= (1 << ADEN)|(1 << ADSC);
  // und auf Abschluss der Konvertierung warten, Dummy-Read
  while (ADCSRA & (1 << ADSC)) {
  }
  // Messergebnis steht in ADC, wird aber nicht ausgewertet

  // Fuer die eigentliche Anwendung wird dann der ADC im 
  // Interrupt ausgewertet
  ADCSRA |= (1 << ADIE);
  // und die naechste Messung starten
  ADCSRA |= (1 << ADSC);

  // Nun stellen wir den Timer fuer die PWM ein
  // Zunaechst wird eingestellt, bis wohin im PWM-Mode der Timer zaehlen soll
  OCR1C = PWM_MAX_VALUE;
  // PWM-Mode einschalten und als Non-Inverting-PWM konfigurieren
  TCCR1 |= (1 << PWM1A)|(1 << COM1A1);
  // Und die PLL als Taktquelle waehlen, als Standard ist IO-Clock die Taktquelle
  // Als erstes muss die Aenderung des PLL-Registers freigeschaltet werden
  // dazu das entsprechende Bit zu 1 setzen und gleichzeitig alle anderen Bits
  // im Register loeschen.
  PLLCSR = (1 << PLLE);
  // und dann hat man vier IO-Taktzyklen Zeit die PLL zuzuschalten
  PLLCSR |= (1 << PCKE);
  // nun noch den Pin fuer die PWM aus Ausgang schalten damit das PWM-Signal 
  // auch am Pin anliegt
  DDRB |= (1 << PB1);
  // und den Timer starten
  TCCR1 |= (1 << CS10);
  
  // fuer den ADC noch die Interrupts einschalten
  sei();
  for(;;){
  }
  return 0;
}
ISR(ADC_vect){
  // Der ADC soll seinen Messwert auf den maximalen PWM-Wertebereich abbilden
  // daher hier eine kleine Umrechnung dafuer
  OCR1A = PWM_MAX_VALUE*ADC/1024.0
  // ADC erneut starten
  ADCSRA |= (1 << ADSC);
}

Mit der Variablen PWM_MAX_VALUE kann man einstellen, bis wohin der Timer zählen soll und somit die Geschwindigkeit und Auflösung der PWM einstellen. Damit kann man ein wenig spielen und sich das Verhalten näher betrachten.

Ergebnisse

Gibt man an den Analogeingang einen Spannungssprung von 0V auf 500mV so stellt sich mit obigen Code der folgende Spannungsverlauf am PWM-Ausgang PB1 ein.

Ändert man obigen Code indem man die Zeilen für das PLLCSR auskommentiert/löscht so liefe der Timer für die PWM nicht mit 64 MHz sondern mit 1 MHz, gibt man hierbei dann einen Spannungssprung von 0V auf 500mV auf den Analogeingang führt dies zu folgenden Spannungsverlauf bei PB1:

In diesem Signal ist deutlich die PWM-Frequenz zu erkennen, man müsste also den Filter entsprechend anpassen was aber das Signal deutlich langsamer machen würde. Der Tiefpass mit 1kΩ und 1µF weist eine Grenzfrequenz von ca. 160Hz auf. Um die gleiche Filterwirkung bzgl. der Ripplespannung wie bei der 251 kHz Frequenz zu erhalten müsste die Grenzfrequenz um den Faktor 64 reduziert werden, als um etwa 2.5 Hz liegen (z.B. indem man den Widerstand von 1kΩ auf 64kΩ erhöht). Je nach Anwendung kann das ausreichend sein aber es kann auch viel zu langsam sein. Mit der PLL kann aber dieser Nachteil ausgeglichen werden.

Fazit

Mit der PLL kann man am ATTiny25/45/85 sehr schnelle PWMs erzeugen. Schade ist, dass dieses Modul nicht in jedem AVR integriert ist. Wirklich groß kann das Modul ja nicht sein da der ATTiny25/45/85 zu den kleinsten AVRs gehört. Für gewöhnlich nutze ich bei PWM-Anwendungen mit diesen ATTiny's immer die PLL. Es kommt natürlich immer noch auf die konkrete Anwendung an, für einen Servo aus dem Modellbausektor wäre dieser PWM-Mode denkbar ungeeignet da er viel zu schnell ist.

Dieser Beitrag wurde unter Programmierung abgelegt und mit , , , , , verschlagwortet. Setze ein Lesezeichen auf den Permalink.

2 Antworten zu AVR ATTiny25/45/85: PWM in schnell mit Hilfe der PLL

  1. Um da mal was sinnvolles daraus zu zimmern, hier ein Sinusgenerator mit der PWM:
    http://www.avr-asm-tutorial.net/avr_de/apps/sinus_tn45/sinus_tn45.html

    mfg, gsc

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Time limit is exhausted. Please reload the CAPTCHA.

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.