PWM per Software mit Timer

Hi,

ich habe im Internet gesehen das man einen beliebigen Pin mit PWM per Software schalten kann. Dafür hab ich mir die entsprechende Libary runtergeladen und ausprobiert. Nun habe ich mich gefragt, da ich mich seit ein paar tagen mit den Timern beschäftige, ob man das auch mit Registern bzw. Timer erzeugen kann. Wenn ja wie?
Mfg

Hi

Die Pins, Die Das in Hardware können, haben auf der Arduino-Platine ein ~ Zeichen.
Genau Das ist das Zeichen dafür, daß hier ein Timer mitmischt.

Oder möchtest Du einen Timer-Interrupt dafür benutzen, beliebige Pins an/abzuschalten?
Das geht natürlich auch.
Die MoBa-Tools funktionieren in diese Richtung - vll. einen Blick wert.
Der kreative Kopf dahinter ist hier im Forum unterwegs - MicroBahner, hat die MoBa-Tools sowohl bereits in der Arduino-IDE drin, wie eine neuere Version auf GitHub.

MfG

postmaster-ino:
Oder möchtest Du einen Timer-Interrupt dafür benutzen, beliebige Pins an/abzuschalten?

Hi

ja genau. Also zur Erklärung ich habe ein Projekt mit einem nano auf einer geätzten Platine. Durch einen Fehler meinerseits ist ein Transistor an einem nicht PWM Pin der eigentlich mittels PWM gesteuert werden soll. Hab mich schlau gemacht und gesehen man kann mit der SoftPWM lib einen Beliebigen Pin mit PWM steuern. Da ich mich zur Zeit aber mit den Timern vom Atmega beschäftige wollte ich statt einen Befehl einzugeben das ganze mit den Timern und Registern versuchen. Meine Google suche hat nichts ergeben. Deshalb habe ich mich gefragt ob mir jemand sagen kann ob und wie das geht.
Mfg

Du möchtest einen der vielen Timer Interrupts nutzen.

Dazu solltest du untersuchen, wie die Timer funktionieren.
Und das findest du im Datenblatt

Hi

Dieser Pin wird zumindest nicht direkt vom Timer in Hardware angesprochen - dann hätte Er diese ~ Linie.
Somit müsstest Du einem der Timer beibringen, auf einen von Dir vorgegebenen Wert zu Prüfen und anhand der Prüfung den Ausgangspin HIGH/LOW setzen.
Je nachdem, was für Lib’s Du noch benutzt, sind die Timer bereits gut beschäftigt.
Du könntest aber auch einfach nur in loop() bei jedem Durchgang prüfen, ob der Pin AN oder AUS gehört und dahin schalten.
digitalWrite(Pin,millis()%100<30?1:0);
Wenn der Rest (millis():100) kleiner 30 ist, HIGH, sonst LOW.
So hättest Du eine Periodenlänge von 100ms und einen HIGH-Anteil von 30ms.
Wenn Du hier micros() nimmst, wird’s vll. etwas feinfühliger - 100ms ist für PWM vll. etwas lahm - eine LED ist halt keine Heizung (hier würden Sekunden reichen).

MfG

Edit
nach #14 ein ‘Hoppala’ ausgebessert - beim Fragezeichen muß ein : für das ELSE hin, nicht, wie zuvor von mir, ein Komma - Danke combie.

analogWrite() verwendet auch Timer um das zu machen. Da kann man ich Dinge per Hand etwas besser machen. z.B. höhere Frequenzen oder eine höhere Auflösung.
Das schalten der Pins geschieht aber durch den Timer selbst ohne dass irgendein Code ausgeführt wird oder ein Interrupt ausgelöst wird. Dadurch ist man auf bestimmte Pins festgelegt

Was Software PWM Bibliotheken machen ist eine Interrupt auslösen um das Zeitverhalten zu haben und im Interrupt schaltet man dann Pins per Hand. Dadurch ist man flexibler, aber es kostet halt Rechenzeit

Im Prinzip ist es eigentlich einfach. Wie serenifly schreibt, musst Du das HW Verhalten an den PWM Pins per SW nachbilden. Den Timer fast genau wie bei HW PWM konfigurieren. Nur statt der Verbindung zu den HW Pin konfigurierst Du bei Overflow und bei Comparematch einen IRQ In der Ovl ISR den Pin einschalten und in Compare ISR wieder ausschalten. Aber wie combie schrieb, ohne Datenblattstudium wird's nicht gehen.

Welche Frequenz brauchst Du denn?

Hier mal eben schnell mit der heißen Nadel gestrickt:

SoftPWM für einen beliebigen Pin.

Futter für die Wühlkiste:

// Soft PWM für den UNO/Nano/ProMini
// Nutzt Timer0, welcher auch für Millis()usw verwendet wird


const byte softpwmpin = 13;

ISR(TIMER0_COMPA_vect)
{
  digitalWrite(softpwmpin,HIGH);
}

ISR(TIMER0_COMPB_vect)
{
  digitalWrite(softpwmpin,LOW);
}

void softpwm(byte pwm)
{
  pinMode(softpwmpin,OUTPUT);
  switch(pwm)
  {
    case   0 : TIMSK0 &= ~(_BV(OCIE0B)|_BV(OCIE0A));
               digitalWrite(softpwmpin,LOW);
               break;
               
    case 255 : TIMSK0 &= ~(_BV(OCIE0B)|_BV(OCIE0A));
               digitalWrite(softpwmpin,HIGH);
               break;
               
    default  : OCR0A = 1;
               OCR0B = pwm;
               TIMSK0 |= _BV(OCIE0B)|_BV(OCIE0A);
                  
  }
  
}

void setup() 
{

}



void loop() 
{
  static byte dim = 0;  
  softpwm(dim++);
  delay(20);
}

Ohne den Sketch von combie zu verstehen, wäre es nicht wesentlich schneller, den Pin per direkter Portmanipulation zu setzen als per digitalWrite ?

Weil dem TE ging es ja um einen ganz bestimmten Pin auf einem Nano.

Klar!

Dieses ist die schnellste Version.
Auch der göttlichste ASM Programmierer bekommt die ISR nicht noch schneller hin.

// Soft PWM für den UNO/Nano/ProMini
// Nutzt Timer0, welcher auch für Millis()usw verwendet wird

#include <CombiePin.h>

Combie::Pin::OutputPin<13> led;

ISR(TIMER0_COMPA_vect)
{
  led.setHigh();
}

ISR(TIMER0_COMPB_vect)
{
  led.setLow();
}

void softpwm(byte pwm)
{
  led.init();
  switch(pwm)
  {
    case   0 : TIMSK0 &= ~(_BV(OCIE0B)|_BV(OCIE0A));
               led.setHigh();
               break;
               
    case 255 : TIMSK0 &= ~(_BV(OCIE0B)|_BV(OCIE0A));
               led.setLow();
               break;
               
    default  : OCR0A = 1;
               OCR0B = pwm;
               TIMSK0 |= _BV(OCIE0B)|_BV(OCIE0A);
  }
}

void setup(){}

void loop() 
{
  static byte dim = 0;  
  softpwm(dim++);
  delay(20);
}

CombieLib.zip (56.1 KB)

Schön. War das nun so schwer :smiley: :grin:

ElEspanol:
Schön. War das nun so schwer :smiley: :grin:

Nee...

Aber eigentlich etwas muss ja was zum "selber denken" für dich und die anderen übrig bleiben.
Oder?
:o :grin:

postmaster-ino:
Dieser Pin wird zumindest nicht direkt vom Timer in Hardware angesprochen - dann hätte Er diese ~ Linie.

Je nachdem, was für Lib’s Du noch benutzt, sind die Timer bereits gut beschäftigt.
Du könntest aber auch einfach nur in loop() bei jedem Durchgang prüfen, ob der Pin AN oder AUS gehört und dahin schalten.

MfG

Frage 1. Was steckt denn für Hardware in einem PWM Pin?
Frage 2. Kann man denn sehen wie “beschäftigt” die Timer sind. Ich denke mal bei sowas kommt es sehr auf Geschwindigkeit an oder ?

digitalWrite(Pin,millis()%100<30?1,0);

Das habe ich ja noch nie gesehen sieht aber cool aus.

Serenifly:
analogWrite() verwendet auch Timer um das zu machen. Da kann man ich Dinge per Hand etwas besser machen. z.B. höhere Frequenzen oder eine höhere Auflösung.

Wie denn ?

combie:
Auch der göttlichste ASM Programmierer bekommt die ISR nicht noch schneller hin.

werd ich mal ausprobieren Danke. Ist das Zufall das die Lib so heißt wie du oder ist das deine?

Eurovision:
digitalWrite(Pin,millis()%100<30?1,0);

Das habe ich ja noch nie gesehen sieht aber cool aus.

Ist das Zufall das die Lib so heißt wie du oder ist das deine?

ja, das erste ist combie, und die Lib ist auch von ihm

digitalWrite(Pin,millis()%100<30?1,0);

Das habe ich ja noch nie gesehen sieht aber cool aus.

Es ist bestimmt dieses gemeint:

digitalWrite(Pin,millis()%100<30?1:0);

Dieses reicht:

digitalWrite(Pin,millis()%100<30);

Ist das Zufall das die Lib so heißt wie du oder ist das deine?

Meine, jau.
In meinen Libs ist viel aus diesem Forum eingeflossen.
Meinen Dank, für die vielen Anregungen!

Dieses reicht:

digitalWrite(Pin,millis()%100<30);

Schöner ist aber, auf eine Division durch so krumme Zahlen wie 100 zu verzichten und lieber mit 256, 128 oder 64 zu rechnen.
10 Hz PWM ist auch nicht sehr augenfreundlich, und wenns da nicht drauf ankommt, geht sogar

byte val = 77; //  PWM-Wert 30 % 
digitalWrite(Pin,(byte)millis()<val);  // 4 Hz SoftPWM

michael_x:
10 Hz PWM ist auch nicht sehr augenfreundlich

Eurovision:
Durch einen Fehler meinerseits ist ein Transistor an einem nicht PWM Pin der eigentlich mittels PWM gesteuert werden soll.

Leider hat der TO immer noch nicht gesagt, was für eine PWM Frequenz er denn eigentlich braucht. Davon hängt ja auch ab, welche Lösung zielführend ist.

Eurovision:
Da ich mich zur Zeit aber mit den Timern vom Atmega beschäftige wollte ich statt einen Befehl einzugeben das ganze mit den Timern und Registern versuchen.

Dann wäre combies Ansatz aus #7 ein erster Einstieg. Ansonsten Datenblatt studieren, und einen Timer mal von Grund auf einrichten. Da kann man dann eine Menge lernen.

MicroBahner:
Leider hat der TO immer noch nicht gesagt, was für eine PWM Frequenz er denn eigentlich braucht.

Hi

Ich möchte einen Transistor ansteuern den ich über zwei Tastern von 0-100% in 10ner schritten durchsteure. Also 0%, 10%, 20%, 30%....100%. In 0,5V Schritten dann.

Hi

Das sagt noch rein gar Nichts über die angedachte Frequenz aus.
Wenn Dir 100ms reichen - die millis()-Modulo-Zeile
Wenn's doch 'etwas' schneller sein darf: wie schnell?
Oder Anders: Was hast Du vor?
Daraus können wir eventuell entscheiden, wie schnell Du wirklich willst (brauchst).

MfG

Ach komm…
Ob das jetzt 4Hz, 10Hz oder fast 1000Hz wie bei meiner softPWM() sind…
Darauf kommts doch vielleicht gar nicht an.

Ich habe PWMsen mir deutlich unter 0,1Hz in den Einsatz gebracht.
Hat auch gut geklappt.