ATMega328p analogWrite PWM Frequenz

Ich würde gern an einem Uno/Nano (ATMega328p), die Frequenz des analogWrite erzeugten PWM Signal, ändern. Es geht darum einen Motor zu treiben, der an Pin 9 hängt. Dieser Motor hätte gern ein Signal in der Nähe von 100Hz.

Also mal erkundigt, welcher Timer welche Pins steuert. Wenn ich es richtig verstanden habe, ist

  • Pins 5 und 6: gesteuert durch Timer 0
  • Pins 9 und 10: gesteuert durch Timer 1
  • Pins 11 und 3: gesteuert durch Timer 2
    zuständig.

Die Standard Frequenz an den Pins 3, 9, 10, 11 ist 490Hz
Die Standard Frequenz an den Pins 5, 6 ist 980Hz

Ich hatte die Idee einfach den Vorteiler zu ändern. Aber jetzt kommt das Fragezeichen. Welcher Vorteiler ist den standardmäßig hinterlegt?

Wenn ich den timer1 einfach ohne Vorteiler durchlaufen lasse, habe ich nach 65535 Takten ein Überlauf. Bei 16MHz dann also 244,14… Überläufe pro Sekunde. Da die Standard Frequenz aber höher ist, muss da ja vorher ein Match auslösen. Oder denke ich falsch? Denn jeder Vorteiler würde ja die Frequenz senken.

Wie sind denn die Standardeinstellungen des Timer1, das ein analogWrite auf Pin9 490Hz erzeugt?

Vielleicht hilft Dir das ja weiter:

https://playground.arduino.cc/Code/PwmFrequency/

1 Like

Danke. Demnach ist standardmäßig der Vorteiler 64 eingestellt.

31250 / 64 = 488,28125

Wenn ich den Vorteiler auf 256 stelle sollte ich also

31250 / 256 = 122,0703125

kommen. Was ziemlich nah an 100 ist.

TCCR1B = TCCR1B & 0b11111000 | 0b00000100

Zu dem Schluss bin ich nach kurzer Ansicht auch gekommen. Selber ausprobiert habe ich das jedoch noch nicht.

Wäre noch interessant, wie die anderen Register des Timer1 eingestellt sind, das die 32500Hz Basis Frequenz erreicht wird.

Gerade kam mir die Idee, ich kann sie ja auslesen, und ausgeben. Vielleicht heut Abend, wenn ich Zugriff auf Uno/Nano habe.

Oder es weis schon jemand.

Du kannst sogar in den Quellcode schauen, denn der liegt ja auf deinem Rechner.

Da hast du Recht. Natürlich müsste man noch wissen wo. Also wieder suchen und lesen (und verstehen).

Also heute Abend ransetzen.

Prescaler 64 bei allen Timern, mit 8 Bit „phase correct pwm mode“

Ich nehme an, das ist es.

1 Like

Ahh, danke. Und bei PWM, phase correct, 8-bit ist TOP 0x00FF. Also aller 256 Zählungen.

Daher 16.000.000 / 256 / 2 = 31.250

Nicht alle. Pin 5 und 6 (Timer0) laufen mit 960Hz. Und denke gelesen zu haben mit FastPWM.

#define potiPin 2 //Eingang Poti ist A2
int potiWert;

void setup() {
  Serial.begin(115200);
  //Löschen der Timer/Counter Control Register A und B
  TCCR1A = 0;
  TCCR1B = 0;
  //Modus Fast PWM-Mode 9 Bit einstellen
  TCCR1A |= (0 << WGM10) | (1 << WGM11);
  TCCR1B |= (1 << WGM12);
  //Vorteiler auf 256 setzen
  TCCR1B |= (1 << CS12);
  //Nichtinvertiertes PWM-Signal setzen
  TCCR1A |= (1 << COM1A1);
  //PWM-Pin 9 als Ausgang definieren
  DDRB |= (1 << DDB1);
}


void loop() {
  potiWert = analogRead(potiPin); //Potiwert einlesen (Auflösung Analogeingang = 10 Bit)
  //Ist die Auflösung des PWM-Signals z.B 9 Bit, muss der potiWert angepasst werden:
  potiWert = map(potiWert, 0, 1023, 0, 511);
  OCR1A = potiWert; // Setzen des Impuls-Pausenverhältnis
  Serial.println(potiWert);
  delay(200);
}

Quelle

EDIT: Kommentar auf "9 Bit" geändert

2 Likes

Danke. Den Link kannte ich noch nicht.

Ziel ist es wenn möglich analogWrite zu verwenden, um nicht das ganze alte bestehende Programm zu überarbeiten. Was nicht heißt das es nicht möglich ist.

Bin dennoch gespannt was das auslesen der Register bringt.

Wenn Du die 100 Hz genauer erreichen möchtest, kannst Du Dir auch ein PWM-Signal selbst zusammenstellen:

#define PWM_FrLen 10000  // Framelänge in µs
#define onState 1
#define sigPin 9

int pwm = 0;  // 0 bis 10000

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(sigPin, OUTPUT);
  digitalWrite(sigPin, !onState);

  cli();
  TCCR1A = 0; // set entire TCCR1 register to 0
  TCCR1B = 0;

  OCR1A = 100;  // compare match register, change this
  TCCR1B |= (1 << WGM12);  // turn on CTC mode
  TCCR1B |= (1 << CS11);  // 8 prescaler: 0,5 microseconds at 16mhz
  TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt
  sei();
}

void loop() {
  static uint32_t vorhin = 0;
  static uint32_t intervall = 1000;
  static byte schritt = 0;

  if (millis() - vorhin >= intervall) {
    vorhin = millis();
    switch (schritt) {
      case 0:
        analogSchreiben(100);
        schritt++;
        break;
      case 1:
        analogSchreiben(255);
        digitalWrite(LED_BUILTIN, HIGH);
        schritt++;
        break;
      default:
        analogSchreiben(200);
        digitalWrite(LED_BUILTIN, LOW);
        schritt = 0;
    }
  }
}

void analogSchreiben(int pwm_wert)
{
  if (pwm_wert < 0) pwm_wert = 0;
  if (pwm_wert > 255) pwm_wert = 255;
  cli();
  pwm = map(pwm_wert, 0, 256, 0, PWM_FrLen);
  sei();
}

ISR(TIMER1_COMPA_vect) { //leave this alone
  static boolean state = true;

  TCNT1 = 0;

  if (state) { //start pulse
    digitalWrite(sigPin, onState);
    OCR1A = pwm * 2;
    state = false;
  } else {
    digitalWrite(sigPin, !onState);
    state = true;
    OCR1A = (PWM_FrLen - pwm) * 2;
  }
}

analogWrite ändert OCR1A, aber mit einer Sonderbehandlung vom Wert 255. Außerdem brauchst Du bei neun Bit Auflösung 511 als maximalen Wert. Das haut also nicht hin.

Wenn Du analogWrite durch analogSchreiben ersetzt, geht es einfach.

Pin 5 und 6 laufen über Timer0. Aber Du hast recht, dafür wird zwar auch ein Prescaler von 64 verwendet, aber kein phase correct pwm.

Schon an Überladen gedacht?

struct Pin{byte pin;};

const byte apin {5};
const Pin  bpin {9};

void analogWrite(const Pin &pin, byte value)
{
  if(pin.pin == 9) OCR1A = value;
}

void setup() 
{
  // timer initialisieren
 // pinMode

  analogWrite(apin,128);
  analogWrite(bpin,128);
}

void loop() 
{

}
2 Likes

Nein, noch nicht daran gedacht.

Zuhause, und die Register mal ausgelesen.

TCCR0A: 0b00000011
TCCR0B: 0b00000011
TCCR1A: 0b00000001
TCCR1B: 0b00000011
TCCR2A: 0b00000001
TCCR2B: 0b00000100

Timer0 also im Mode 3, FastPWM, Überlauf bei 0xFF (255), Vorteiler 64
Timer1 also im Mode 1, PWM, phase correct, 8-bit, Überlauf bei 0x00FF (255), Vorteiler 64
Timer2 also im Mode 1, PWM, phase correct, Überlauf bei 0xFF (255), Vorteiler 64

Moin. Bei Phase Correct PWM ist mir noch etwas unklar. Und ich finde leider nicht die Antwort.

z.B. beim Timer1 kann man einstellen, das TOP feste Werte wie 0x00FF oder 0x01FF oder 0x03FF hat (8Bit, 9Bit, 10Bit Auflösung) Beim Erreichen des dazugehörigen Wert im Compare Registers, wird der dazugehörige Pin beim hochzählen HIGH/LOW (kann man vorgeben) gesetzt, und entgegengesetzt, wenn der Wert des Compare Registers beim runterzählen erreicht wird.

Nun kann man TOP auch den Wert von dem Compare Register übergeben (Mode 11). Aber wenn TOP gleich den Compare Register ist, dann wird doch der Pin nicht mehr angesprochen. Also kann man darüber nicht einen Pin direkt HIGH/LOW setzen lassen, sondern man kann nur ein Flag beim Erreichen von TOP und BOTTOM setzen lassen. Zur direkten PWM Erzeugung auf dem PIN also nicht nutzbar? Bzw. was ist dann der Unterschied zu Fast-PWM mit einstellbaren TOP gleich Compare Register. Es wird beim Erreichen von TOP auch ein Flag gesetzt. In der gleichen Zeit wo in FastPWM wieder TOP erreicht wird, ist in PhaseCorectPWM wieder BOTTOM erreicht. Es wird also Zeitgleich ein Flag gesetzt. Wo ist also der Unterschied? Und wozu braucht man diesen Modus?


Edit:
Ich denke ich habe die Antwort auf die Frage gefunden. Man kann OCR1A als TOP nutzen und OCR1B als Trigger zum steuern von Arduino Pin 10. Ein steuern von Arduino Pin 9 ist dann aber nicht möglich.
Man kann aber auch ICR1 als TOP verwenden (Mode 10, bei Phase Correct), wenn man diesen nicht anderweitig verwendet, und somit OCR1A als Trigger für Arduino Pin 9 und OCR1B als Trigger für Arduino Pin 10.
Programmierung von 16 Bit Timer auf Atmega328 - Embedds

Hallo,

bevor ich wieder Aufsätze tippe stelle ich diesmal Gegenfragen die du dir sicherlich beantworten kannst. Wenn es keine Unterschiede geben würde, würden die Modi nicht unterschiedliche Bezeichnungen haben. Damit sind wir beim Punkt. Der Bezeichnung der Modi sagt die Kernaufgabe der Modi aus. Stichtwort syncrones Schaltverhalten. Zusätzlich sind die Compare Register dieser Modi gepuffert. Damit werden Änderungen nie zum falschen Zeitpunkt übernommen. Deswegen bevorzuge ich diese "correct" Modi solange die Wunschfrequenz damit erreichbar ist. Bei FastPWM hat man zudem mit Compare 0 Nadelimpulse, sauberes 0,000% Duty ist so nicht möglich.

2 Likes

Es sei denn bei Duty "Null" schaltet man das PWM ab :slightly_smiling_face:.

Frage hierzu. Warum muss auf == 9 abgefragt werden? Die "Variante" der Funktion wird doch nur aufgerufen, wenn sie mit bpin aufgerufen wird. Und bpin stellt doch schon sicher, dass es sich um Pin 9 handelt. Oder habe ich da ein Gedankenfehler?

Gibt es mehrere AnalogOutPins?

Wie man die Pin zu Compare Register Zuordnung macht, ist egal, solange sie funktioniert.