mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Überlegungen zu ATtin85 und PWM aus Timer 1


Autor: philipp (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,

ich hangel mich aktuell von Info zu Info, zu einem vermeintlich 
einfachen Vorhaben...

Ich möchte mit einem ATtin85 eine simple PWM-Steuerung für einen 4-Pin 
PC-Lüfter realisieren. Also ein 5V PWM-Signal @ 20,48KHz, welches ich 
über den Duty-Cycle modulieren kann.

Ich wüsste nun gern um eure Meinung zu meinen bisherigen Überlegungen.


Aus dem Datenblatt zum ATtin85 sowie diversen Online-Tutorials habe ich 
mir die Grundlagen angelesen und daraus für mich das Folgende 
konstruiert.

Als ISP nutze ich den Arduino mit dem ATTiny Core von SpeceKonde.

Die 20,48KHz ergeben sich aus den Specs des Lüfters (18-30KHz) und der 
Gleichung (64MHz / 16) / (199 + 1) = 20,48KHz

Ich möchte also den Timer 1 des ATtiny85 nutzen, mit 64MHz, einem 
Prescaler von 16 und einem TOP-Wert von 199.


Im PLLCSR Register muss ich keine Bits setzen, da dies bereits durch den 
ATTiny Core / Bootloader geschieht. Hier kann ich bereits einen Takt von 
64MHz für den Timer 1 vorgeben.

Anderfalls müssten die Einstellungen hier aber wie folgt aussehen:
PLLCSR = _BV(PLLE); // PLL Enable

while(!(PLLCSR & _BV(PLOCK))); // PLL Lock Detector

PLLCSR = _BV(PCKE); // PCK Enable


Nun im TCCR1 die Bits für PWM1A, COM1A1 und den Prescaler setzen:
TCCR1 = _BV(PWM1A) | _BV(COM1A1) | _BV(CS12)| _BV(CS10);


Den TOP-Wert auf 199 setzen:
OCR1C = 199;


Nach meinem Verständnis sollten ich doch nun mit OCR1A den Duty-Cycle 
zwischen 0-199 (0-100%) betimmen können, richtig?
OCR1A = 100; // 50% Duty-Cycle


Ausgabe-Pin wäre PB1 bzw. Pin6 des ATtiny85.

Habe ich in meinen Überlegungen etwas vergessen / übersehen?


Danke für eure Hilfe!


Philipp

Autor: H.Joachim S. (crazyhorse)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Erst PLL auf 64MHz und dann wieder mit Vorteiler 16? Wozu?
Nimm doch einfach den 8MHz-Takt.

Autor: spess53 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi

>Die 20,48KHz ergeben sich aus den Specs des Lüfters (18-30KHz) und der
>Gleichung (64MHz / 16) / (199 + 1) = 20,48KHz

Da kommen bei mir 20kHz raus.

MfG Spess

Autor: philipp (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
H.Joachim S. schrieb:
> Erst PLL auf 64MHz und dann wieder mit Vorteiler 16? Wozu?
> Nimm doch einfach den 8MHz-Takt.

Hatte ich aus dem Datenblatt.
Aber 8MHz mit Prescaler 2 und TOP=199 im synchronen Modus sollte ebenso 
funktionieren :)

Autor: philipp (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Kommen die übrigen Überlegungen zu den zu setzenden Bits hin?

Autor: philipp (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Kann ich statt
OCR1A = 100;
auch analogWrite() nehmen, um den entsprechenden Pin anzusprechen?
Dürfte doch theoretisch keinen Unterschied machen?!

Autor: Stefanus F. (stefanus)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
> Kommen die übrigen Überlegungen zu den zu setzenden Bits hin?

Probieren geht über studieren. Bedenke, dass dein µC mehrfach 
programmierbar ist. Wir sind nicht mehr in den 80er Jahren.

> Kann ich statt OCR1A = 100; auch analogWrite() nehmen

Das würde ich nicht machen. Entweder nimmt man die Mittel des 
Frameworks, oder programmiert es selbst. Aber dieser Mix ist sehr 
riskant. Vielleicht funktioniert das heute, aber beim nächsten Update 
nicht mehr.

Hatten wir das nicht gerade erst vor ein paar Tagen? Ich erlebe gerade 
ein Dejavu Gefühl.

> Dürfte doch theoretisch keinen Unterschied machen?!

Das hast du geraten, nicht wahr? Schau doch mal in den Quelltext der 
Funktion rein:
// Right now, PWM output only works on the pins with
// hardware support.  These are defined in the appropriate
// pins_*.c file.  For the rest of the pins, we default
// to digital output.
void analogWrite(uint8_t pin, int val)
{
  // We need to make sure the PWM output is enabled for those pins
  // that support it, as we turn it off when digitally reading or
  // writing with them.  Also, make sure the pin is in output mode
  // for consistenty with Wiring, which doesn't require a pinMode
  // call for the analog output pins.
  pinMode(pin, OUTPUT);
  if (val == 0)
  {
    digitalWrite(pin, LOW);
  }
  else if (val == 255)
  {
    digitalWrite(pin, HIGH);
  }
  else
  {
    switch(digitalPinToTimer(pin))
    {
      // XXX fix needed for atmega8
      #if defined(TCCR0) && defined(COM00) && !defined(__AVR_ATmega8__)
      case TIMER0A:
        // connect pwm to pin on timer 0
        sbi(TCCR0, COM00);
        OCR0 = val; // set pwm duty
        break;
      #endif

      #if defined(TCCR0A) && defined(COM0A1)
      case TIMER0A:
        // connect pwm to pin on timer 0, channel A
        sbi(TCCR0A, COM0A1);
        OCR0A = val; // set pwm duty
        break;
      #endif

      #if defined(TCCR0A) && defined(COM0B1)
      case TIMER0B:
        // connect pwm to pin on timer 0, channel B
        sbi(TCCR0A, COM0B1);
        OCR0B = val; // set pwm duty
        break;
      #endif

      #if defined(TCCR1A) && defined(COM1A1)
      case TIMER1A:
        // connect pwm to pin on timer 1, channel A
        sbi(TCCR1A, COM1A1);
        OCR1A = val; // set pwm duty
        break;
      #endif

      #if defined(TCCR1A) && defined(COM1B1)
      case TIMER1B:
        // connect pwm to pin on timer 1, channel B
        sbi(TCCR1A, COM1B1);
        OCR1B = val; // set pwm duty
        break;
      #endif

      #if defined(TCCR1A) && defined(COM1C1)
      case TIMER1C:
        // connect pwm to pin on timer 1, channel B
        sbi(TCCR1A, COM1C1);
        OCR1C = val; // set pwm duty
        break;
      #endif
...

      case NOT_ON_TIMER:
      default:
        if (val < 128) {
          digitalWrite(pin, LOW);
        } else {
          digitalWrite(pin, HIGH);
        }
    }
  }
}

Für die Werte 0 und 255 müsste man dann noch in den Quelltext von 
digitalWrite() schauen.

Noch Fragen?

: Bearbeitet durch User
Autor: philipp (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke für deine Ausführungen!

Denke, dann gehe ich den vermeintlich "sichereren" Weg, und programmiere 
es über die Register selbst.

Autor: philipp (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mein Code schaut nun wie folgt aus:
Werde damit morgen wohl einen Testlauf probieren...
Oder habe ich etwas übersehen?
void setup() {
  // Set 64MHz asynchronus clock mode, if bits not already set by Bootloader
  PLLCSR |= (1 << PLLE);        // PLL Enable
  while(!(PLLCSR & (1 << PLOCK))); // PLL Lock Detector
  PLLCSR |= (1 << PCKE);        // PCK Enable
  
  // Set PWM frequency to (64MHz / 16) / (199 + 1) = 20KHz
  TCCR1 =   
            (1 << PWM1A)  |   // Pulse Width Modulator A Enable
            (1 << COM1A1) |   // Comparator A Output Mode to OC1x cleared on compare match. Set when TCNT1 = $00; Inverted Output not connected
            (1 << CS12)   |   // Set Prescaler to 0101 = 16
            (1 << CS10);      // Set Prescaler to 0101 = 16
  
  OCR1C = 199;                // Set Top-Value to 199
  
  // 8-bit resolution
  // set ADLAR to 1 to enable the Left-shift result (only bits ADC9..ADC2 are available)
  // then, only reading ADCH is sufficient for 8-bit results (256 values)

  ADMUX =
            (1 << ADLAR)  |   // left shift result
            (0 << REFS1)  |   // Sets ref. voltage to VCC, bit 1
            (0 << REFS0)  |   // Sets ref. voltage to VCC, bit 0
            (0 << MUX3)   |   // use ADC2 for input (PB4), MUX bit 3
            (0 << MUX2)   |   // use ADC2 for input (PB4), MUX bit 2
            (1 << MUX1)   |   // use ADC2 for input (PB4), MUX bit 1
            (0 << MUX0);      // use ADC2 for input (PB4), MUX bit 0

  ADCSRA = 
            (1 << ADEN)   |   // Enable ADC 
            (1 << ADPS2)  |   // set prescaler to 64, bit 2 
            (1 << ADPS1)  |   // set prescaler to 64, bit 1 
            (0 << ADPS0);     // set prescaler to 64, bit 0
            
  ADCSRA |= (1 << ADSC);      // start ADC measurement
  
  pinMode(PB1, OUTPUT);
}

void loop() {
    while (ADCSRA & (1 << ADSC)); // wait till conversion complete
    OCR1A = map(ADCH, 0, 215, 0, 199) // convert analog input to pwm output
}

Autor: Äxl (geloescht) (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hast Du Angst, Dir brennt die Bude ab? Mach doch einfach drauf los...

Autor: Dieter F. (jim_quakenbush)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
philipp schrieb:
> (0 << REFS1)

Bewirkt nichts ...

Autor: Herr B. (herr_barium)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dieter F. schrieb:
> philipp schrieb:
>> (0 << REFS1)
>
> Bewirkt nichts ..

Na klar bewirkt das etwas: Vcc wird als Referenzspannung genutzt.

Du meinst: Er verdient Schelte, nur weil er die Initialisierung 
vorbildlich übersichtlich und mit allen beteiligten Bits in den 
Registern hingeschrieben hat?

Nein, dem ist nicht so! Das zahlt sich nämlich immer dann aus, wenn 
man das Programm mal wiederverwenden will. Dann ist es ein Leichtes, 
ohne erst in Unterlagen zu wühlen, eine andere Betriebsart einzustellen.

Autor: Dieter F. (jim_quakenbush)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
Herr B. schrieb:
> Na klar bewirkt das etwas:

Nö - ein ODER mit 0 (null) bewirkt rein gar nichts.

Autor: Herr B. (herr_barium)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Dieter F. schrieb:
> Nö - ein ODER mit 0 (null) bewirkt rein gar nichts.

Noch mal: Es steht nacher in dem Bit das Gleiche, wie schon vorher 
drinstand. Eine Null. Das ist der Initialisierungszustand, wenn man 
NICHTS macht. Aber: Da er das betreffende Bit schon in den Quelltext 
aufgenommen hat, kann er später für andere Zwecke da eine Eins 
reinschieben und blitzschnell die Referenzspannung umstellen.

Autor: Dieter F. (jim_quakenbush)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Herr B. schrieb:
> Noch mal: Es steht nacher in dem Bit das Gleiche, wie schon vorher
> drinstand.

Ich nehme es zurück - habe übersehen, dass

  ADMUX =

ein = ohne ODER (|) da steht.

Der Compiler nimmt die "veroderte" Zuweisung dann als Initialwert 
(analog = 0b00100010), OHNE zu "verodern".
Würde ich aber trotzdem nie so machen, weil das impliziert, dass es auch 
an anderer Stelle (mit ODER) so funktioniert. Ist aber vermutlich nur 
mein Problem.

Wenn überhaupt (und um vollständig zu sein) würde ich
ADMUX =
            (0 << REFS1)  |   // Sets ref. voltage to VCC, bit 1
            (0 << REFS0)  |   // Sets ref. voltage to VCC, bit 0
            (1 << ADLAR)  |   // left shift result                                  
            (0 << REFS2)  |   // Sets ref. voltage to VCC, bit 2
            (0 << MUX3)   |   // use ADC2 for input (PB4), MUX bit 3
            (0 << MUX2)   |   // use ADC2 for input (PB4), MUX bit 2
            (1 << MUX1)   |   // use ADC2 for input (PB4), MUX bit 1
            (0 << MUX0);      // use ADC2 for input (PB4), MUX bit 0

schreiben, damit alle Bits in der richtigen Reihenfolge (analog 
0b00100010) erkennbar sind.

: Bearbeitet durch User

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.