Forum: Mikrocontroller und Digitale Elektronik ATTiny85 - 25kHz PWM erzeugen


von Wolfgang V. (wolfgangvogl)


Lesenswert?

Guten Abend,

ich möchte mit einem ATTiny85 eine PWM im Bereich von 20 bis 25kHz 
erzeugen um damit einen "4-Pin" Lüfter steuern zu können.

Nach einiger Recherche und ausprobieren bin ich auf
TCCR0B = (TCCR0B & 0b11111000) | 0b001
gekommen, welches mir wohl Timer Register B auf Prescaler = 1 setzt - 
der Rest ist auf den jeweiligen Standardwerten geblieben.
Der ATTiny85 läuft auf 8MHz - somit erhalte ich Pin 5 eine PWM mit einer 
Frequenz von ~31,x kHz.
==> 8MHz/(1*2^8) = 31,25kHz ... passt soweit zu meiner Annahme.

Nun brauche ich aber irgendwas zwischen 20 und 25kHz um den PWM Lüfter 
"richtig" anzusteuern... leider ist das verringern der Frequenz für mich 
bei weitem nicht so einfach wie ich dachte.

Nach einigem Hin- und Her bin ich tatsächlich (wer hätte es gedacht) 
beim Datenblatt gelandet.
https://cdn-reichelt.de/documents/datenblatt/A300/ATTINY25-ATTINY45-ATTINY85_DATASHEET.pdf

Hier bin ich in der Beschreibung nun nicht mehr ganz sicher, ob ich's 
richtig verstehe:

1: Nach den verschiedenen PWM Modi für Timer/Counter0 komme ich mit 
diesem gar nicht auf den gewünschten Frequenzbereich?
Bei den 8MHz und einem Prescaler von 1 müsste ich ja von 0 bis 320 
zählen um auf 25kHz zu kommen - was bei einem 8bit Zähler nicht so ganz 
passt da ich ja mindestens 9bit bräuchte (Modus 2, 3 und 7... alles das 
gleiche Problem?)
==> Übersehe ich hier irgend etwas offensichtliches?

2: Die nächste Überlegung war dann natürlich den Timer/Counter1 zu 
benutzen - nur hier bin ich bisschen untergegangen... bei OCR1A hab ich 
gesehen, es ist auch ein 8-bit Register und infolgedessen habe ic hhier 
ja irgendwie das gleiche Problem wie bei Timer0 - nur scheint hier mit 
TCCR1 ja nochmal eine Stufe zwischenschaltbar zu sein...
Wenn dem so ist wäre es toll wenn mir jemand da bisschen auf die Sprünge 
helfen könnte.

3: Auf Seite 88 des Datenblatts (Table 12-3 Timer/Counter1 Clock 
Prescale Select in the Asynchronous Mode) wird die PWM Frequenz in 
Abhängigkeit von Prescale und OCR1C aufgelistet... hier wäre ja eine PWM 
Frequenz von 20kHz zu finden. Jedoch bin ich mir nicht im Klaren 
darüber, was mit "Asynchronous Mode" hierbei sagen soll und Resolution 
von 7.6(bits? Hat das Sinn, entweder 7 oder 8.. dachte ich zumindest).

Vielen Dank schon einmal!
Gruß,
Wolfgang

von Achim H. (pluto25)


Lesenswert?

Diese 20khz setzen 16Mhz Clock voraus.
Wie wärs mit 4Mhz Clock und OCR0A=200 als Top. Das ergäbe 20Khz. oder 
160 dann wärns 25KHz. Die 7,6 Bits Auflösung sind eine etwas 
unkonventionelle Darstellung. Mit 200 sinds 7,55 mit 160 ca 7,3 also 
mehr als 128 (7Bit) und weniger als 256 (8Bit)

: Bearbeitet durch User
von Thomas E. (thomase)


Lesenswert?

Wolfgang V. schrieb:
> Der ATTiny85 läuft auf 8MHz - somit erhalte ich Pin 5 eine PWM mit einer
> Frequenz von ~31,x kHz.
> ==> 8MHz/(1*2^8) = 31,25kHz ... passt soweit zu meiner Annahme.

Nimm den Phase Correct Mode(5). Dann passt das.

von Karl M. (Gast)


Lesenswert?

Hallo, noch ein Tipp,

Was die wenigstens nicht wissen ist, dass der Athlet ein E85 auch Inter 
mit 16 Megahertz laufen kann . dazu schaut man ins Datenblatt: Stichwort 
PLL.

Jetzt läuft aber leider die IO mit 64 Megahertz oder mit 32 Megahertz 
und nun muss andere Maßnahmen ergreifen, um die Timer Register 
beschreiben zu können.

von Wolfgang V. (wolfgangvogl)


Lesenswert?

Thomas E. schrieb:
> Wolfgang V. schrieb:
>> Der ATTiny85 läuft auf 8MHz - somit erhalte ich Pin 5 eine PWM mit einer
>> Frequenz von ~31,x kHz.
>> ==> 8MHz/(1*2^8) = 31,25kHz ... passt soweit zu meiner Annahme.
>
> Nimm den Phase Correct Mode(5). Dann passt das.

Ich habe mich mal am Phase Correct Mode (5) anhand des Datenblattes und 
sonstiger Suche im Internet versucht... leider bekomme ich so überhaupt 
kein PWM Signal:

Mit verschiedensten Kombinationen aus COM0x0/1 habe ich schon "gespielt" 
- aber ohne, dass es was verändert hätte. Sobald ich WGM[2:0] auf 5 
habe, tut nichts mehr.
Mit WGM[2:0] = 1 bekomme ich auf B0 und B1 jeweils ~15,7kHz als PWM 
Frequenz. (In geposteten Beispielcode habe ich hierfür einfach 1<<WGM02 
durch 0<<WGM02 ausgetauscht.)
1
 #include <Arduino.h>
2
void setup() {
3
  TCCR0A = 0b0 | ((1 << COM0A1)|(0 << COM0A0)|(1 << COM0B1)|(0 << COM0B0)| (0 << WGM01) | (1 << WGM00));
4
  TCCR0B = 0b0 | ((0 << FOC0A)|(0 << FOC0B)|(1 << WGM02)|(0 << CS02)|(0 << CS01)|(1 << CS00));
5
  OCR0A = 160;
6
  pinMode(PINB0, OUTPUT);
7
  pinMode(PINB1, OUTPUT);
8
}
9
10
void loop() {
11
  analogWrite(PINB0, 50);
12
  analogWrite(PINB1, 50);
13
  delay(1000);
14
}

Hab ich da was vergessen/falsch gemacht? (Offenbar: ja :-))

von Peter D. (peda)


Lesenswert?

Wolfgang V. schrieb:
> Hab ich da was vergessen/falsch gemacht? (Offenbar: ja :-))

Du mußt Dich schon entscheiden. Entweder Du benutzt das 
Arduino-Framework oder Du greifst direkt auf die Register zu.
Beides zusammen geht nicht.

Das analogWrite() verläßt sich darauf, daß die Register so sind, wie sie 
das Framework initialisiert hat. Wenn nicht, dann knallt es.

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Will man 2 PWMs mit einer bestimmten Frequenz, muß man T1 benutzen.
Bei 8MHz mit Prescaler 2 (CS13..CS10 = 0010) und OCR1C = 199 kommt man 
dann auf 20kHz.

von User (Gast)


Lesenswert?

Peter D. schrieb:

> Du mußt Dich schon entscheiden. Entweder Du benutzt das
> Arduino-Framework oder Du greifst direkt auf die Register zu.
> Beides zusammen geht nicht.

Moin,

das ist leider nicht richtig.
Es geht sehr wohl z.Bsp. auf meinem Arduino Mega mit
1
//************ PWM *****************
2
int myEraser = 7;
3
int myPrescaler = 1;
4
5
void setup() {
6
  //set PWM Frequency
7
  // TCCR3 -> Pin 2,3,5 
8
  // TCCR4 - > Pin 6,7,8
9
  
10
  TCCR3B &= ~myEraser;                // this operation (AND plus NOT), set the three bits in TCCR3B to 0
11
  TCCR4B &= ~myEraser;                // this operation (AND plus NOT), set the three bits in TCCR4B to 0
12
  TCCR3B |= myPrescaler;              // this operation (OR), replaces the last three bits in TCCR3B with our new value 001
13
  TCCR4B |= myPrescaler;              // this operation (OR), replaces the last three bits in TCCR4B with our new value 001
14
}
15
16
void loop() {
17
  ...
18
  analogWrite(2, 150);
19
  ...
20
}

von Wolfgang V. (wolfgangvogl)


Lesenswert?

Peter D. schrieb:
> Will man 2 PWMs mit einer bestimmten Frequenz, muß man T1 benutzen.
> Bei 8MHz mit Prescaler 2 (CS13..CS10 = 0010) und OCR1C = 199 kommt man
> dann auf 20kHz.

Ich möchte / brauche nur einen Port mit PWM - das ich beide Ports drin 
habe ist eher als Verzweiflungstat zu sehen weil ich mir nicht sicher 
war ob ich vielleicht mit den zugehörigen Ports was verhaut habe. Sorry 
dafür.

: Bearbeitet durch User
von Wolfgang V. (wolfgangvogl)


Lesenswert?

Peter D. schrieb:
> Du mußt Dich schon entscheiden. Entweder Du benutzt das
> Arduino-Framework oder Du greifst direkt auf die Register zu.
> Beides zusammen geht nicht.

Okay, das leuchtet ein...
=> bin durch die includes gegangen und hab halt recht viele ifdefs 
gesehen die das Vorhandensein einiger defines usw. geprüft haben... das 
sah schon so aus als wenn die da eine "Grundabsicherung" hätten. 
(Teilweise war Code vorhanden um bei digitalWrite PWM abzuschalten usw.) 
- habe aber nicht weiter drüber nachgedacht, ein enablePwm bzw. 
setPwmMode o.ä. habe ich nicht gefunden und es damit halt auf sich 
beruhen lassen.

==> Bin eher der Freund davon das "direkt" ohne die Arduino zu machen da 
sie ja offenbar eh nicht alles bietet, was ich gerade möchte.

Könntest du mir bitte sagen wie ich dann den Pin (B0) ohne arduino.h auf 
output schalte und den duty cicle eingebe? Da finde ich im Datenblatt 
gar nichts zu und alle Beispiele die ich online finden konnte scheinen 
auf analogWrite der arduino Bibliothek zu gehen.
Gerne auch, nach was ich im Datenblatt suchen müsste..

Wäre super, vielen Dank!

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

Wolfgang V. schrieb:
> Da finde ich im Datenblatt
> gar nichts zu

Doch, siehe Anhang.

von Peter D. (peda)


Lesenswert?

PB0 (OC0A) kann keine bestimmte Frequenz ausgeben, d.h. der TOP-Wert der 
PWM kann nicht verkürzt werden.
Das geht nur für OC0B/OC1A und OC1B.
Bzw. falls PB1 nicht benutzt wird, kann man PB0 auch als /OC1A 
konfigurieren.

von Peter D. (peda)


Lesenswert?

User schrieb:
> das ist leider nicht richtig.

Ich würds trotzdem nicht machen, auch wenn es bei Deinem ATmega2560 zu 
funktionieren scheint.
Früher oder später oder auf einem andern AVR kann es Seiteneffekte geben 
und Du legst Dir die Karten.
WIMRE benutzt das Framework T0 für Delays, d.h. das sorgt schon für 
Konflikte mit T0 als PWM.

von User (Gast)


Lesenswert?

Hallo,

dieser Lösungsansatz ist auch "offiziell" bei Arduino beschrieben..
https://www.arduino.cc/en/Tutorial/SecretsOfArduinoPWM

von Wolfgang V. (wolfgangvogl)


Lesenswert?

Peter D. schrieb:
> PB0 (OC0A) kann keine bestimmte Frequenz ausgeben, d.h. der TOP-Wert der
> PWM kann nicht verkürzt werden.
> Das geht nur für OC0B/OC1A und OC1B.
> Bzw. falls PB1 nicht benutzt wird, kann man PB0 auch als /OC1A
> konfigurieren.

Wo im Datenblatt finde ich das?
Ich hatte mich im Datenblatt auf 11.7.4 gestützt
(11.    8-bit Timer/Counter0 with PWM)
(11.7.  Modes of Operation)
(11.7.4 Phase Correct PWM)

In phase correct PWM mode, the compare unit allows generation of PWM 
waveforms on the OC0x pins. Setting
the COM0x[1:0] bits to two will produce a non-inverted PWM. An inverted 
PWM output can be generated by setting
the COM0x[1:0] to three: Setting the COM0A0 bits to one allows the OC0A 
pin to toggle on Compare Matches if
the WGM02 bit is set. This option is not available for the OC0B pin (See 
Table 11-4 on page 78).

==> Ich frage deshalb so "genau" nach, weil ich mir ja irgendwie auch 
selbst helfen können will. Thx

Und thx für den Ausschnitt aus dem Datenblatt... das war wohl 
Schlampigkeit meinerseits. :-(

von Wolfgang V. (wolfgangvogl)


Lesenswert?

Hier noch eine kurze Rückmeldung meinerseits, es scheint soweit 
funktioniert zu haben.
Habe nun 23kHz erzeugt, wie von Peter schon erwähnt nur auf PB1.

Ich hoffe noch auf den Hinweis bezüglich wo ich im Datenblatt hätte 
gucken müssen wegen PB0 und um einen sauberen Abschluss im Thread zu 
kriegen, hier mein Code (unglaublich, wieviel Zeit die paar Zeilen 
gekostet haben..):
1
#include <avr/io.h>
2
3
int main()
4
{
5
  TCCR0A = 0b0 | ((1 << COM0A1) | (0 << COM0A0) | (1 << COM0B1) | (0 << COM0B0) |                              (0 << WGM01) | (1 << WGM00));
6
  TCCR0B = 0b0 | ((0 << FOC0A)  | (0 << FOC0B)  |                                 (1 << WGM02) | (0 << CS02) | (0 << CS01)  | (1 << CS00));
7
 
8
  // freq / prescale / OCR0A / 2 = pwm freq => OCR0A = freq/(2*pwm freq)
9
  OCR0A = 174; // 160 = 25kHz, 23kHz = ~174
10
  OCR0B = 87; // duty cycle = OCR0B/OCR0A: 87/174=0.5
11
12
  // MCUCR = MCUCR | (1 << PUD); // MCU Control Register, PUD: Disable all Pull-Ups
13
  DDRB = 0b0 | (1 << PORTB1); //Data Direction Register Port B: 1 for output, 0 for input (datasheet 10.2.1, P. 55, Table 10-1)
14
  // PORTB: Port B Data Register
15
  PORTB = PORTB & 0b11111101; // Bit 1 = PORTB1 = 0
16
17
  while (true)
18
  {
19
    //analogWrite(PINB0, 50);
20
    //analogWrite(PINB1, 50);
21
  }
22
}

Vielen Dank an dieser Stelle für die wirklich kompetente Hilfe!

von Bernd (Gast)


Lesenswert?

Wolfgang V. schrieb:
> Ich hoffe noch auf den Hinweis bezüglich wo ich im Datenblatt hätte
> gucken müssen wegen PB0

In die Fußnote zu Tabelle 11-4:
1
A special case occurs when OCR0A or OCR0B equals TOP and COM0A1/COM0B1 is set. In this case, the Com-
2
pare Match is ignored, but the set or clear is done at TOP. See “Phase Correct PWM Mode” on page 74 for more
3
details.

Du verwendest den Mode 5 (WGM0[2:0]=101, Tabelle 11-5): PWM, Phase 
Correct.
Dort wird OCR0A für den Endwert des Zählers verwendet (TOP).

Wenn es auf beiden Pins (PB0 und PB1) wackeln soll, mußt Du vermutlich 
auf Mode 1 (WGM0[2:0]=001) wechseln. Dann kommt in OCR0A der 
Vergleichswert für OC0A/PB0.
Um auf 23 kHz PWM Frequenz zu kommen, müßte der Tiny dann mit 11,73 MHz 
getaktet werden (23000 * 510).

von Bernd (Gast)


Lesenswert?

Karl M. schrieb:
> Jetzt läuft aber leider die IO mit 64 Megahertz oder mit 32 Megahertz
> und nun muss andere Maßnahmen ergreifen, um die Timer Register
> beschreiben zu können.
Eigentlich nicht. Wenn man die interne PLL verwendet, werden der Timer 1 
und die dazugehörigen IO-Pins mit dem schnellen Takt versorgt.
Die Registerzugriffe sind m.E. gepuffert.
Mit der PLL sind mit Timer 1 PWM-Frequenzen zwischen 20 und 500 kHz in 
Schritten von 10 bzw. 50 kHz möglich.

von Peter D. (peda)


Lesenswert?

Die PLL (Fuse-Bits) setzt den CPU-Takt auf 16MHz.
Der asynchrone Mode (PCKE im PLLCSR-Register) setzt T1 auf 64MHz oder 
32MHz.
Beides kann unabhängig voneinander benutzt werden.

von Dani (Gast)


Lesenswert?

Für die AT Controller sind die Codeschnipsel unter

https://www.electronicsplanet.ch/mikrocontroller/source-code/ATmega8/ATmega8-Timer1-index.htm

oft auch noch recht hilfreich um funktionierenden Beispielcode zu 
erzeugen.

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.