ATMega und Timer (statt RTC und SQW bzw 32K)

@ doc.

meinst du mich ?

weil in dem sketch, den ich gepostet hab, wird der led pin doch über eine funktion über interrup aufgerufen ?

wenn eine kleine verzögerung im loop dafür sorgt, dass das an und aus verhältnis erst leicht verzögert geändert wird, ist das nicht so tragisch, solange an und aus möglichst genau laufen......

aber wenns noch genauer geht, gerne ! (:

ein kleines problem hab ich aber noch, ich würd das gerne ohne die lib umsetzen, da es später auf ein atmega1284 soll und ich vieles aus der lib nicht brauche.....

@ agmue: hab leider kein oszilloskop oder ähnliches...... hab nur eien led angeschlossen, bei der ich mal 5s nachgessen/gestoppt hab und die scheinen sehr genau zu sein.......

hätte da nochmal eine offtropic-frage......

warum listet man die funktionen (void xxx()) im setup auf ? (habe das letztens in einem sketch gesehen) ? und warum schreibt man zB hier: void initTimer(void) nochmal void in die klammer ?

Was man an einer Library nicht braucht wird auch nicht übersetzt.

Die TimerOne Version von Paul Stoffregen läuft auch auf einem Atmega1284 mit dem Core von Maniacbug (ganz unten):

warum listet man die funktionen (void xxx()) im setup auf ? (habe das letztens in einem sketch gesehen) ?

Meinst du die Angabe der Funktion die aufgerufen werden soll?:

Wenn du nur das void als Parameter meinst, das ist rein optional. Kann man auch weglassen, aber bei Funktionszeigern ist es manchmal etwas übersichtlicher was genau gemeint ist.

Oder bist du auch jemand der meinst “void” würde eine Funktion einleiten? Das ist eine Art Datentyp. Und zwar “nichts”. Hier heißt dass das nichts übergeben wird. Bei Rückgabewerten heißt es dass nichts zurückbegeben wird. Es gibt auch void Zeiger die auf alle möglichen anderen Typen zeigen können.

Hallo,

@ agmue:
meine Antwort war an agmue gerichtet. Er schreibt vom Sekundentakt, zeigt aber einen Diagramm in kHz.

@ magic
Der Timer Aufruf mit seinen Einstellungen muß man nur einmal aufrufen. Dann sind die Bits für den Timer gesetzt und das Ding taktet. Daran ändert sich nichts mehr. Außer man ändert im laufenden Betrieb ein Register für PWM.

Ohne Verzögerung ist möglich
a) direkt Timer eigenen Pin verwenden, schaltet syncron mit µC Takt
b) direkt im ISR irgendeinen einen Pin schalten, schaltet minimal verzögert zum µC Takt.

wenn es wirklich hochpräzise sein soll nimmt man den Pin der zum Timer gehört. Im Pinpout mit OCnX bezeichet.

Wenn in der ISR nur eine Statusvariable geändert wird, dann wird diese irgendwann einmal abgearbeitet, sobald der Ablauf in der loop an der Auswertestelle vorbeikommt. Das muß nicht konstant sein. Je nachdem was die loop alles machen muß.

Du schreibst von 0 … 1000ms. Aber welche Auflösung brauchst du denn? Kleinster Schritt 1ms, 0,1ms oder 10ms … ???

@serenifly danke ! dann hat sich das ja erledigt......... ich nutze die timerone aber in der, die ich hab, ist nur ein bruchteil des codes enthalten ????????? oben im kommentar steht das gleiche.....

ich versuchs mal zu beschreiben: im sketch waren einige funktionen (void).

die wurden aber alle oben VOR dem setup aufgelistet.....(sorry nicht im setup !) hab ich noch nie so gesehen oder gemacht und habs mal gelöscht und der sketch läuft trotzdem......

ich dachte ein void ist eine funktion ?

also macht es keinen untershcied, ob ich danahc in die klammer nochmal void schreibe ?

@doc "Der Timer Aufruf mit seinen Einstellungen muß man nur einmal aufrufen. Dann sind die Bits für den Timer gesetzt und das Ding taktet. Daran ändert sich nichts mehr. Außer man ändert im laufenden Betrieb ein Register für PWM."

mache ich das denn nicht so ???? wenn nicht, was mache ich denn da falsch ?

"b) direkt im ISR irgendeinen einen Pin schalten, schaltet minimal verzögert zum µC Takt."

mach ich das nicht auch ???? sorry, bin da grade etwas verwirrt.......

zum hochpräzise...... sehe ich das richtig, wenn ich jetzt nicht den timer eigenen pin nehme, dann bekomme ich durch jeden interrupt aufruf eine kleine verzögerung ? also nicht nur beim ändern der intervalzeiten ?

auflösung sollte 1ms sein (;

vielen dank an euch ! ihr seid echt eine sehr große hilfe ! auch wenn ich manchmal schwierigkeiten hab, euch zu folgen, da ich das meisten autodidaktisch, unter etwas zeitdruck erlerne .........

Wahrscheinlich Funktionsprototypen: https://de.wikipedia.org/wiki/Funktionsprototyp

Die sind Standard in C/C++, aber werden von der Arduino IDE in den meisten Fällen automatisch erzeugt. Es gibt aber Konstrukte bei denen das fehlschlägt und man sie selbst schreiben muss. void func(void) zählt zwar nichts dazu, aber es schadet auch nichts die selbst zu machen.

ich dachte ein void ist eine funktion ?

NEIN! Sowas wie "eine void" gibt es nicht! Void ist der Rückgabewert: https://de.wikipedia.org/wiki/Void_%28Schl%C3%BCsselwort%29

Hast du noch nie eine Funktion geschriebenen die eine Wert zurück gibt? Sowas in der Art:

int getSensor()
{
   return mySensor.read();
}

Nennst du das dann "eine int"?

Als Argument um einen Wert an eine Funktion zu übergeben war void in C nötig. In C++ nicht mehr. Aber man kann es trotzdem schreiben. Siehe der Wiki-Artikel.

vielen dank ! das wars !

zu den Funktionsprototypen.
also wäre es besser die zu verwenden ?

ok !
aber wenn ich int getsensor () verwende, wird ein eingang/sensor ausgelesen und dein ein wert zurückgegeben…
zB X = getsensor ();

und wenn ich void () verwende, schreib ich da mehr verschiedene sachen rein und auch zb sowas:
digitalWrite(ledpin, LOW);
interval_an = 5000;

magictrips: zu den Funktionsprototypen. also wäre es besser die zu verwenden ?

Nein. Die schreibt man nur explizit hin wenn man sie braucht. Das wäre z.B. hier der Fall: http://forum.arduino.cc/index.php?topic=357005.msg2481575#msg2481575

Da wird ein struct als Parameter verwendet. Wenn man die Prototypen nicht explizit deklariert, dann schreibst sie die IDE ganz oben in die Datei. Bevor das struct deklariert wurde. Was dann dazuführt dass das struct nicht bekannt ist und einen Fehler produziert.

Das die Arduino IDE die automatisch erzeugt ist schon schön. Aber bei manchen Sachen nervt es gewaltig. Mit Default Parametern kann der Parser auch nicht umgehen. Wobei ich da auf github gelesen habe, dass man ein paar dieser Sachen in zukünftigen Versionen endlich mal ausgebessert hat.

aber wenn ich int getsensor () verwende, wird ein eingang/sensor ausgelesen und dein ein wert zurückgegeben..... zB X = getsensor ();

Das war nur ein Beispiel. Du kannst alle erdenklichen Sachen in einer Funktion machen

Man kann z.B. bool(ean) als Rückgabe-Wert vewenden um mitzuteilen, ob etwas erledigt wurde. Entweder weil es auch schiefgehen kann (z.B. etwas auf eine SD Karte schreiben) oder weil es noch nicht fertig ist (z.B. Einlesen einer Zeichenkette über die serielle Schnittstelle). In der Arduino IDE wird da auch oft int verwendet. -1 wenn von irgendeiner Quelle nichts eingelesen wurde, weil keine Daten da sind (z.B. keine Daten auf Serial, oder eine Datei enthält nichts oder ist zu Ende).

ok, vielen dank ! wieder was dazugelernt (;

also wenn ichs mit reinschreiben muss, sagt mir die ide das, indem sie ein fehler erzeugt bzw das programm nicht läuft ? ? ? ?

zum timer:

was mache ich denn da jetzt "falsch" ? ausser das ich nicht den timer-pin verwende ?

Pin 13 sollte doch ein Timer-Pin sein oder nicht ? https://www.pjrc.com/teensy/td_libs_TimerOne.html

Auf dem Atmega1284 und Atmega2560 (Mega) ja. Auf dem Atmega328 (UNO) nein. Das ist sehr Prozessor-abhängig.

Falsch ist nicht unbedingt, wenn es bei dir geht. Aber auch nicht elegant. Bei deinen Kenntnissen sollte man es vielleicht dabei belassen.

also hab ich für meine zwecke den richtigen pin !?

ja, würde ich auch (;
kommt es denn irgendwie zu verzögerungen, so wie ich das gemacht hab ? bzw ist das “genau” ?

ich möchte hierrauf hinaus :

"Der Timer Aufruf mit seinen Einstellungen muß man nur einmal aufrufen. Dann sind die Bits für den Timer gesetzt und das Ding taktet. Daran ändert sich nichts mehr. Außer man ändert im laufenden Betrieb ein Register für PWM.

Ohne Verzögerung ist möglich
a) direkt Timer eigenen Pin verwenden, schaltet syncron mit µC Takt
b) direkt im ISR irgendeinen einen Pin schalten, schaltet minimal verzögert zum µC Takt.

wenn es wirklich hochpräzise sein soll nimmt man den Pin der zum Timer gehört. Im Pinpout mit OCnX bezeichet."

Genau ist relativ. Du bist im ms Bereich. Es geht um das sichtbare blinken einer LED. Da kümmert es niemanden ob das ein paar µs daneben liegt.

Hallo,

Entschuldigung das ich dich magic mit dem Timer durcheinander gebracht habe. Für Deine Zwecke reicht deine Methode wirklich völlig. Ich stecke nur gerade in der Thematik Timer voll drin. Gerade mit Timer 1 habe ich mich jetzt sehr beschäftigt. Deshalb habe ich das erwähnt. Macht nur keinen Sinn wenn man die Genaugkeit nicht benötigt.

dein delay sollte noch raus. Das delay führt sogar das Interval ins Absurdum. Das delay muß auf jeden Fall raus. Und nur die Intervallvorgaben werden genutzt. Laut meiner Logik taktet das beides nicht mit 5000 sondern beides mit 10000. Du hebst die 5000 mit den 10000 auf.

void loop()  {
  delay(10000);
  interval_an = 5000; 
  interval_aus = 5000;
}

ok, danke !

@serenifly

naja, um eine led gehts ja nicht !!!!!!!!!! war nur ein beispiel um es für mich zu lernen! es geht eigentlich, wie gesagt, um ein flüssigkeitsteiler für destillationsanlagen ! also ein elektromagneten...... den wollte ich über ein fet ansteuern und das verhältnis zwischen AN und AUS sollte relativ genau und gleichmäßig sein......

es ist halt einstellbar von 0-1000ms in 1ms schritten.....jeweils für interval_aus und für interval_an.......

wenns dann 500µs daneben liegt, is das mMn schon viel....... mit ein paar µs kann man denke ich mal leben, da der magnet ja auch nicht SOFORT anzieht usw......

diese geschichte läuft jetzt aber unabhängig vom restlichen sketch, richtig ?

also selbst wenns im loop eien verzögerung gibt, bleibt der interval_an und interval_aus davon unberührt ?

@ doc

ja, kla das delay war ja auch nur um ein taster/schalter oder so zu simulieren....... ich wollte gucken ob der nach einer gewissen zeit mit einem interval von 1000ms, wirklich auf 5000ms wechselt (;

wie gesagt, das is jetzt nur ein lern-und-versteh-beispiel-sketch..... im richtigen sketch stehen interval_aus und interval_an auf einem display und werden je nach bedarf eingestellt bzw im falle eines alarmes usw wird der flussigkeitsteiler auf kompletten rückfluss gestellt usw

Hallo,

gut, dann sollte das schon genau werden denke ich. Dann nimmste einen 8Bit Timer 2 oder Timer 1 im 8 Bit Modus. Den nutzte im CTC Modus und Compare Match Interrupt Handler. Wenn ich mich nicht verrechnet habe, nimmste Prescaler 256 und einen Compare Match Wert von 124. Dann wird der ISR genau aller 1ms aufgerufen. Damit bauste dir quasi ein eigenes genaues millis nach. Und jetzt mußte mittels dem ISR einen eigenen ms Zähler bauen, denn du möglichst ohne weitere Verzögerung nutzt. Du kannst sicherlich auch Deine Schaltvorgänge im ISR direkt einbauen. 2 if Abfragen zum toggeln je nach Zählerstand oder so. Aber so schlank wie möglichst im ISR hantieren.

Wobei die Abweichung auch nicht ein paar 100µs betragen sollte.

Wenn du du die An-und-Aus-Zeit vollkommen getrennt einstellen willst, dann ist Doc_Arduinos Vorschlag mit dem CTC Modus und einem manuellem Interrupt korrekt. Das sollte man aber eher so machen dass man beim Compare Match einfach den Vergleich für die nächste An- bzw. Aus-Zeit einstellt. Also z.B. erst ein Vergleich bei 800ms, dann 400ms, dann wieder 800ms, etc.

Ich habe es mal mit PWM vollkommen in Hardware implementiert. Da ist dann die Frequenz von 1Hz fest vorgegeben und was man einstellt ist das Verhältnis der zwei Zeiten. Also wenn 20% bedeutet 200ms An und 800ms Aus. 90% wäre 900ms An und 100ms Aus.

Das ist vielleicht doch nicht was du willst...

const int timerPin = 9;   //OC1A = UNO Pin 9

const unsigned long TOP_VALUE = 15625;    //64µs * 15625 = 1s

void setup()
{
  pinMode(timerPin, OUTPUT);

  TCCR1A = _BV(COM1A1) | _BV(WGM11);                          //Clear OC1A on compare match, Mode 14 (Fast PWM mit TOP = ICR1)
  TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS12) | _BV(CS10);   //Mode 14, Prescaler = 1024 (= 64µs pro Tick)
  ICR1 = TOP_VALUE;       //TOP Register
}

void loop()
{
  int percentage = map(analogRead(A0), 0, 1023, 0, 100);

  if (percentage == 0)
  {
    digitalWrite(timerPin, LOW);
  }
  else if (percentage == 100)
  {
    digitalWrite(timerPin, HIGH);
  }
  else
  {
    TCCR1A |= _BV(COM1A1);      //Pin wieder verbinden. Das wird von digitalRead/Write() abgeschaltet!
    OCR1A = TOP_VALUE * percentage / 100;
  }
}

Ich habe auch nicht nachgemessen ob die Zeit wirklich genau stimmt! Vom Gefühl passt es aber.

Hier die andere Variante im CTC Modus. Die Aus-Zeit ist der Einfachheit halber fest auf 1000ms. Die An-Zeit wird mit dem Poti eingestellt:

const int timerPin = 9;   //OC1A = UNO Pin 9

const int offTime = 1000;       //Aus-Zeit in ms
volatile int onTime;

void setup()
{
  pinMode(timerPin, OUTPUT);

  TCCR1A = _BV(COM1A0);                         //Toggle OC1A on compare match
  TCCR1B = _BV(WGM12) | _BV(CS12) | _BV(CS10);  //Mode 4 (CTC), Prescaler = 1024 (= 64µs pro Tick)
  TIMSK1 = _BV(OCIE1A);             //Enable Output Compare Match A Interrupt

  OCR1A = offTime * 1000UL / 64;      //Zeit in ms * 1000ms / 64µs (Zeit pro Timer-Tick)
}

void loop()
{
  onTime = analogRead(A0);
}

ISR(TIMER1_COMPA_vect)
{
  static bool onPhase;

  if (onPhase)
    OCR1A = offTime * 1000UL / 64;
  else
    OCR1A = onTime * 1000UL / 64;

  onPhase = !onPhase;
}

Ich hier gilt, dass ich die Zeit nicht gemessen habe!

Bei beiden Varianten zählt der Timer hoch und vergleicht den aktuellen Wert mit dem OCR1A Register. Bei einer Übereinstimmung wird was gemacht. Bei der PWM Variante geht das alles automatisch. Bei der CTC Variante wird ein Interrupt ausgelöst. In beiden Fällen wird der Pin aber in Hardware geschaltet. Bei CTC könnte man es aber auch per Hand machen. Dann ist man mit dem Pin nicht festgelegt.

ok, vielen dank !

und was ist jetzt davon für mich geeignet ?

es müssen halt an und aus-zeit seperat von 0-1000ms (in 1ms schritten) einstellbar sein...... wenns geht sehr genau..... unabhängig vom loop....

im loop wird dann halt der wert für die an bzw aus-zeit mittgeteilt..... durch je eine variable, die mit zwei tastern (- und +) eingestellt wird......

Ich habe doch gesagt was der Unterschied ist!

Das erste ist PWM. Feste Frequenz von 1Hz und du kannst das An/Aus-Verhältnis einstellen

Beim zweiten kannst du beiden Zeiten getrennt einstellen. Also was du willst. Außerdem könnte man die Pins auch per Hand schalten wenn man will (dazu einfach TCCR1A auf 0 setzen und die Pins in der ISR schalten)

Die Auflösung beträgt 64µs. 1 / 16MHz * 1024

Beim Atmega1284 mit dem Mighty1284 Core ist es statt Pin 9 glaube ich Pin 13

Hallo,

für Dich kommt demzufolge nur der CTC Modus in Frage. PWM ist zu starr für dich. Du müßtest zu viel umrechnen und ändern.

So. Vielleicht denke ich als Hobbybastler noch zu kompliziert, ich hätte weiter gemacht und einen Blinksketch in die ISR verpflanzt. Kannste irgendeinen Pin verwenden.

In der ISR einen Zähler rein, der mit jeden Aufruf inkrementiert. Das was in loop unten steht ist ein Blinker. Das würde ich in die ISR verfrachten. Und statt dem Arduino millis() den eigenen ISR Zähler als Zeitvergleich nehmen. Und das digitalWrite würde ich noch durch direktes Port.Bit ändern ersetzen. Weil digitalWrite(x,LOW) und digitalWrite(x,HIGH) dauern minimal unterschiedlich lang in der Abarbeitung. Könnte bei dir stören und hält die ISR weniger auf.

// Blinker ohne delay()

#define LED_PIN    13
unsigned long lastTime;
unsigned long on_time  = 330;  // ms
unsigned long off_time = 550;  // ms
bool state_LED;

void setup() {
  pinMode(LED_PIN,OUTPUT);
}

void loop() {
  
 if (state_LED == LOW && millis() - lastTime > off_time ) {
   digitalWrite(LED_PIN, HIGH);       // LED einschalten für x ms
   lastTime = millis();             
   state_LED = HIGH;
 }

 if (state_LED == HIGH && millis() - lastTime > on_time ) {
   digitalWrite(LED_PIN, LOW);         // LED ausschalten für x ms
   lastTime = millis();  
   state_LED = LOW;
 }

}  // Ende loop

Vorschlag, Timer programmieren im CTC Modus, 8 Bit, Prescaler 256 und Compare Match 124. Kannste ruhig mal Datenblätter lesen und im Netz stöbern. Dann den ISR Handler programmieren

Wobei der ISR Handler (CTC Modus) von Serenifly schön kompakt aussieht. Wie macht der das immer nur? :) Direkten Timer Pin verwenden spart Code. :)

... kann erst morgen Abend wieder ... Tschau