PWM - zwischen zwei Pins schalten

ich möchte ein umgebautes fergesteurtes Auto vor-, bzw. rückwärts fahren lassen, und benötige hierfür ein pwm-Signal auf zwei Pins.

ich verwende D9 und D10. wenn ich nun an D10 das pwm-Signal habe, möchte ich zu Testzwecken mit einem Button mit Interupt zwischen den beiden hin und herschalten.
Bei D10 bekomme ich das pwm-signal. auf D9 bekomme ich nur ein HIGH wenn ich den Button drücke.
kann mir da bitte jemand weiterhelfen?

Code:


int interruptNumber = 0;
int tasterPin = 2;
bool on = 1;

void setup() {
Serial.begin(9600);
DDRB |= (1 << PB2); //Port D10
DDRB |= (1 << PB1); //Port D9
TCCR1A = 0b10100011; //Clear OC1A/OC1B on compare match, set OC1A/OC1B at BOTTOM, WGM10, WGM11(Fast-PWM)
TCCR1B = 0b00011100; //CS12 - Prescaler 256; WMG12 & WGM13 für Fast-PWM
OCR1A = 455; //für benötigte Frequenz von 130Hz
OCR1B = 20;
pinMode(tasterPin, INPUT_PULLUP);
attachInterrupt(interruptNumber, interruptfunction, FALLING); //sollte PWM-Signal zwischen D9 und D10 hin und herschalten
}

void loop() {
OCR1B = map(analogRead(A0), 0, 1023, 0, 455); //Steuerung von duty-cycle
}

void interruptfunction() {
if (on == 1) {
DDRB = 0b00000100;
on = 0;
}
else {
DDRB = 0b00000010;
on = 1;
}
Serial.println(on);
}``

Setze Deinen Code bitte in Codetags (</>-Button oben links im Forumseditor oder [code] davor und [/code] dahinter ohne *).
Dann ist er auch auf mobilen Geräten besser lesbar.
Das kannst Du auch noch nachträglich ändern.

Gruß Tommy

Das ist reichlich seltsam. Du machst was am Timer und fragst dann aber den Taster per Interrupt ab. Taster sind so langsam dass man da keine Interrupts braucht. Den solltest du einfach regulär abfragen

Es stimmt zwar dass das entsprechende Bit im DDR Register gesetzt werden muss, aber mit scheint es insgesamt sauberer zu sein dass über den Timer zu erledigen. Du kannst doch im Timer sagen was dessen Pins tun sollen. Über die COM1A1/COM1A0, COM1B1/COM1B0 Bits. Also zwei Bits pro Compare Match. Wenn beide Bits 0 sind ist der Ausgang vom Timer getrennt. Wenn sie 01 sind wird getoggelt. Das kannst du über den Taster ändern.

Man kann das übrigens auch so schreiben dass man selbsterklärend sieht welche Bits gesetzt sind z.B.:

TCCR1A = _BV(WGM10) | _BV(WGM11);

BV steht für Bit-Value und liefert die Maske des entsprechenden Bits. Die verodert man um sie zu setzten.
Löschen von Bits geht mit einem Und der negierten Maske:

TCCR1A &= ~_BV(COM1A1);

So kann man einzelne Bits ändern ohne den Rest anzufassen
Und um es wieder zu setzen:

TCCR1A |= _BV(COM1A1);

Achtung: beim ersten mal in setup() darfst du nicht |= machen da sonst die Arduino PWM Konfiguration nicht überschrieben wird

Serial hat in Interrupts übrigens nichts verloren

(deleted)

danke für die Tipps und Verbesserungsvorschläge zur Leserlichkeit des Codes!

ich schaffe es immer noch nicht das PWM-Signal auf D9 & D10 zu bekommen.

auf D10 bekomme ich die ~130Hz; auf D9 nichts, bzw nur ein HIGH. was fehlt im Code noch?

hier der optimierte Code:

int interruptNumber = 0;
int tasterPin = 2;
bool on = 1;

void setup() {
  Serial.begin(9600);
  DDRB = 0b00000110;
  TCCR1A = 0;
  TCCR1A = _BV(WGM10) | _BV(WGM11) | _BV(COM1A1) | _BV(COM1B1);
  TCCR1B = 0;
  TCCR1B = _BV(CS12) | _BV(WGM12) | _BV(WGM13);
  OCR1A = 490;    //für benötigte Frequenz von 130Hz
  OCR1B = 100;   //duty-cycle
  pinMode(tasterPin, INPUT_PULLUP);
  attachInterrupt(interruptNumber, interruptfunction, FALLING);   //sollte PWM-Signal zwischen D9 und D10 hin und herschalten
}

void loop() {
 OCR1A = map(analogRead(A0), 0, 1023, 0, 490);   //Steuerung von duty-cycle
}

void interruptfunction() {
  if (on == 1) {
    TCCR1A = _BV(WGM10) | _BV(WGM11) | _BV(COM1A1) | _BV(COM1B1);
    on = 0;
  }
  else {
    TCCR1A = _BV(~WGM10) | _BV(~WGM11) | _BV(~COM1A1) | _BV(~COM1B1);
    on = 1;
  }
}

Generell scheinst du den Timer nicht ganz verstanden zu haben. Die kennst das Prinzip, aber bringst die Details durcheinander.

  OCR1A = 490;    //für benötigte Frequenz von 130Hz
  OCR1B = 100;   //duty-cycle

Du willst zwei verschiedene Pins, aber opferst dann einen um die Frequenz per Hand zu bestimmen. Braucht du wirklich genau diese Frequenz?

Normal hat man zwei Kanäle A und B. Jeder ist für einen Pin zuständig. Jetzt benutzt du OCR1A für den TOP Wert. Dann kannst du auch keinen Compare Match auf dem Kanal haben. Genau was du beobachtest

Du hast zwei Optionen:
1.) Benutzte Modus 5, 6 oder 7 mit festem TOP Wert. Das ist normal ok, da die genau Frequenz nicht so wichtig ist, und man über den Prescaler in einen bestimmten Bereich kommt
2.) Benutze Modus 14 mit ICR1 als TOP. Das ist das Input Capture Register was normal nie verwendet wird. Dann hat man OCR1A und OCR1B für Compare Matches

Dann ist noch mehr falsch:

_BV(~WGM10)

Du musst du Maske invertieren! Nicht die Bit-Nummer. Schau dir noch mal genau an was ich oben geschrieben habe.
Außerdem hast du den Unterschied zwischen | und & nicht berücksichtigt. Ein | mit der invertierten Maske ist genauso falsch. Du löscht ein Bit indem du ein UND mit 0 machst. Die 0 bekommt man durch das invertieren der Bit-Maske (~0001 -> 1110). Die anderen Bits sind dann 1 und bleiben wie sie waren

Alternativ, wenn du dir das nicht merken willst gibt es auch Makros dafür:
Set Bit:

sbi(TCCR1A, COM1A1);

Clear bit:

cbi(TCCR1A, COM1A1);

Und wieso änderst du überhaupt die WGM Bits? Der Timer kann intern ruhig weiterlaufen! Du musst nur ändern was die Pins machen.

Ansonsten scheint COM1A/B1 für Fast PWM korrekt zu sein. Das hatte ich erst anders gesehen :confused: Aber so macht es auch die Arduino Software in wiring_analog.c :

// connect pwm to pin on timer 1, channel A
sbi(TCCR1A, COM1A1);

Und wenn man digitalWrite() macht wird auf PWM Pins immer erst sicherheitshalber mal PWM abgeschaltet:

case TIMER1A:   cbi(TCCR1A, COM1A1);    break;
case TIMER1B:   cbi(TCCR1A, COM1B1);    break;

Ist übrigens ein Grund weshalb das recht lange dauert. Da gibt es sogar einen Kommentar dass das in pinMode() sinnvoller wäre :smiley:
Aber du siehst dass der Timer weitermacht. Alle anderen Konfigurationen bleiben und werden nur am Anfang gesetzt. Es werden nur die externen Pins mit dem Timer verbunden oder getrennt

Hallo,

ich würde vorschlagen erstmal den Taster zu entprellen. Ohne Interrupt - bringt eh keine Punkte wie schon geschrieben wurde. Wenn entprellt dann nur auf Änderung reagieren. Damit befasste dich solange bis du eine LED nur auf Tastendruck hin ein- und wieder ausschalten kannst. Das wird später deine bool Umschaltvariable. Erst dann würde ich sagen ist der Timer dran. :wink:

Serenifly:
Generell scheinst du den Timer nicht ganz verstanden zu haben. Die kennst das Prinzip, aber bringst die Details durcheinander.

  OCR1A = 490;    //für benötigte Frequenz von 130Hz

OCR1B = 100;  //duty-cycle



Du willst zwei verschiedene...

danke Dir Serenifly, jetzt läuft es wie gewollt! und die Funktion ist für mich jetzt auch völlig logisch so... danke für die ausführliche Erklärung! :slight_smile:

:slight_smile:

Dass du in diesem Modus bist war mir erst gar nicht klar. Das hätte ich gleich an Hand der Kommentare sehen können. Auch ohne die schönere Schreibweise. Aber hatte nicht ins Datenblatt geschaut was da eigentlich gemacht wird. Ich habe nur "Fast PWM? Ok" gedacht.

Es ist bei PWM auch nicht üblich dass man exakt eine bestimmte Frequenz braucht. Man kann es machen und wenn du das willst ist es ok. Aber i.d.R. kann man von einem festen TOP ausgehen und passt nur den Prescaler an um die Frequenz ungefähr zu bestimmen.
Deshalb habe ich erst angenommen dass es an der Sache mit den Pins liegt. Wobei das so auch besser ist.