Impulsgenerator?

Hallo Forum,

nach zwei Jahren Pause möchte ich mein altes Tachoprojekt nochmal neu aufsetzen. Dazu hab ich für Testzwecke eine Art Impulsgenerator gebaut. Zuerst hatte ich tone() verwndet, bis ich gelesen hatte das ich damit nur Frequenzen >30Hz erzeugen kann. Für mein Tachoprojekt reicht das aber nicht, also dachte ich, ich könnte mittels Timer einfach einen Pin zwischen HIGH und LOW hin- und herschalten lassen. Zusätzlich noch ein Poti um die Frequenz zu verändern…
Leider scheint das Ganz nicht zu funktionieren, das Programm läuft zwar, nach einfügen der Serial.print-Funktionen sehe ich auch das das Poti funktioniert, jedoch scheint an PIN2 kein Signal anzukommen. Habe für Testzwecke mal PIN13 verwendet um anhand der verbauten LED etwas zu erkennen, aber auch hier scheinbar keine Funktion. Ich vermute die Interruptroutine wird nicht aufgerufen, warum?

int impuls=0;
volatile byte b_mode=0;
volatile int dauer_neu=0;


ISR(TIMER1_COMPA_vect) { // will be called when an interrupt occurs at timer 1
    if (b_mode==1) {
    digitalWrite(2, HIGH);
    b_mode=1;
  } else {
    digitalWrite(2, LOW);
    b_mode=0;
  }
  OCR1A=dauer_neu;
 
}

void setup() {
  // put your setup code here, to run once:
  pinMode(2,OUTPUT);
  Serial.begin(19200);
  digitalWrite(2, LOW);

  cli(); //Lösche globales Interrupt-Enable-Bit
  //CTC-Mode aktivieren
  TCCR1A = 0; //Löschen des TCCR1A-Registers
  TCCR1B = 0; //Löschen des TCCR1B-Registers
  TCNT1  = 0; // reset counter value
  OCR1A=32500;  //countervergleichsregister 
  TCCR1B |= (1 << CS11) | (1 << CS10);  //Setze CS11 und CS12 (Clock Select) Takt/64
  TCCR1B |= (1 << WGM12);  //Setze CTC-Mode 
  TIMSK1 |= (1 << OCIE1A); //Bit Output Compare A Match Interrupt Enable setzen 
 
  
}

void loop() {
  // put your main code here, to run repeatedly:
  impuls = analogRead(A2);
  if (impuls>0){
      sei();
      dauer_neu=impuls <<5; //mit 32 multiplizieren
      
  } else {
    cli();
    dauer_neu=0;
    digitalWrite(2,LOW);
  }
  Serial.print("dauer_neu: ");Serial.println(dauer_neu);



}

Viele Grüße

if (impuls>0){
      sei();
     
     
  } else {
    cli();

  }

gekürzt Was soll denn das unsymetrische Zeug?

ich bin unsicher ob ich die Frage richtig verstanden habe, der If-Teil sorgt dafür das keine 0 ins OCR1A geschrieben wird.

Es geht um deine etwas willkürlich verstreuten sei() und cli(). Du kannst nicht einfach beliebig die Interrupts generell ausschalten. Das darf immer nur kurzfristig gemacht werden, d.h. cli() und sei() müssen immer paarweise kurz hintereinander auftauchen. Es gibt schließlich auch noch andere Funktionen im Arduino, die auf Interrupts angewiesen sind. Edit: Wenn die ISR nicht mehr aufgerufen werden soll, musst Du explizit diesen Interrupt im TIMSK1 wieder abschalten.

Zu deinem Problem: Das toggeln in der ISR funktioniert nicht, da Du dein b_mode nicht umschaltest. ( wird auf 1 gesetzt, wenn es schon 1 ist, ebenso wenn es 0 ist - es bleibt einfach wie es ist ). Den aus Ausgang toggeln kannst Du aber auch einfach mit

  digitalWrite(2, !digitalRead(2) );

Hi

Und? Das entspräche 256.

Auch wird Dir das cli 'auf die Füße fallen' - lasse Dir Mal die millis() ausgeben - Die dürften von dem Dauer-cli durchaus beeindruckt sein.

MfG

Kay19: ich bin unsicher ob ich die Frage richtig verstanden habe,

Verwende besser ATOMIC_BLOCK Konstrukte.

der If-Teil sorgt dafür das keine 0 ins OCR1A geschrieben wird.

Den Teil habe ich extra raus geschnitten, da sich meine Aussage eben genau darauf NICHT bezieht. Meinte schon die die unsymetrische Interrupt ein/aus schalterei.

Hallo,

du solltest dich vorher dringend mit dem Timer allgemein befassen. Du möchtest dir im Grunde einen Frequenzgenerator bauen mit jederzeit änderbarer Frequenz. Dabei gibts paar Dinge zu beachten hinsichtlich des aktualisierens der Compare-Match Register. Lies den Abschnitt im Manual zum Timer. Sonst wird das nichts, glaube mir.

Das macht er schon richtig, und das funktioniert auch.

Sein Problem ist der Fehler beim toggeln des Ausgangs ( wobei er das auch der HW überlassen könnte ), die falschen cli/sei und den nicht ATOMIC Zugriff beim Ändern des dauer_neu. Auch wäre es sinnvoll, die Variable dauer_neu als 'unsigned int' zu deklarieren. Schließlich hat auch OCR1A kein Vorzeichen. Dass er 'volatile' noch nicht richtig verstanden hat, ist dabei erstmal Nebensache.

Mit dem richtigen Timer mode spielt die Hardware den Ton doch ganz von alleine ab, ohne ISR.

Hallo,

ja okay, wenn er OCR1A in dessen ISR aktualisiert passt das, Entschuldigung. Beim Rest muss der TO einfach nochmal seinen Code lesen. Ich würde ja der Einfachheit vorschlagen immer eine bool Variable negieren und diese am Pin ausgeben.

DrDiettrich: Mit dem richtigen Timer mode spielt die Hardware den Ton doch ganz von alleine ab, ohne ISR.

Würde ich pauschal immer dafür plädieren. Wenn keine anderen Gründe vorliegen irgendeinen Mode mit OCRxn Buffer.

DrDiettrich: Mit dem richtigen Timer mode spielt die Hardware den Ton doch ganz von alleine ab, ohne ISR.

Die ISR braucht er immer, um die Frequenz verändern zu können ( setzen von OCR1A ). Wie ich auch schon schrieb, kann er das Toggeln des Ausgangs der HW überlassen. Der Timer-Mode bleibt dabei aber derselbe (er will ja eine variable Frequenz, und kein PWM ), er muss nur den Ausgang entsprechend aktivieren ( in TCCR1A ). Allerdings ist er dann auf einen festen Pin ( hier 9 ) festgelegt. Macht er es per SW kann er den Pin selbst frei bestimmen.

Aber jetzt schau'n wer mal, was der TO zu dem Ganzen sagt ;) .

Mehr als einen Pin braucht man ja selten mit der gleichen Frequenz. Zum Ändern der Frequenz ist IMO kein ISR notwendig, die Zugriffe erfolgen bei 16 Bit Zählern synchronisiert.

Hallo,

da muss ich nochmal einhaken. Man kann einen Frequenzgenerator ohne ISR inkl. Pulsweitenänderung bauen. Hier in dem Fall müßte man nur die Pulsweite konstant auf 50% halten. Das funktioniert zum Bsp. alles im Phase Correct Mode einwandfrei, weil die Compare Register gepuffert sind. Dadurch kann man gefahrlos jederzeit die Einstellung ändern. Ob das sich hier im Falle lohnt ist eine andere Frage. Möchte nur sagen es ginge auch ohne ISR. ;) Nach nochmaliger aktueller Überlegung würde ich an Stelle vom TO jedoch bei CTC bleiben. Dann muss man sich nicht um die Pulsweitenanpassung kümmern. :)

DrDiettrich: Mehr als einen Pin braucht man ja selten mit der gleichen Frequenz. Zum Ändern der Frequenz ist IMO kein ISR notwendig, die Zugriffe erfolgen bei 16 Bit Zählern synchronisiert.

:o :o :o In Modi ohne gepufferte Compare Register macht der Timer Mist wenn du zufällig zur falschen Zeit den Comparewert änderst. Der Wert wird sofort übernommen. Wenn der TCNT zu dem Zeitpunkt schon drüber ist läuft der Timer kurzzeitig falsch. Deswegen ändert man hier im Fall CTC den Compare in der eigenen Compare ISR, da kann nichts schief gehen.

Oder man löscht TCNT nach der Änderung des OCRx.

Hallo,

DrDiettrich: Oder man löscht TCNT nach der Änderung des OCRx.

ich habe lange überlegt wie du das meinen könntest, komme jedoch immer zu der Überzeugung das die Aussage keinen Sinn macht. Egal welcher Timermode.

a) ich habe eine Konstellation nicht auf dem Schirm (kann was übersehen haben) b) ich denke du hast dich gedanklich verrannt (passiert mir auch manchmal)

Du müßtest jetzt dazu sagen in welchen Mode du wann den OCR ändern und TCNT nullen möchtest. ? Dazu vielleicht noch ohne ISR? Ich sehe wie gesagt keine Möglichkeiten.

Dann schauen wir uns doch mal die allgemeine Timer Funktion an. Dazu schlagen jetzt alle das 16-bit Timer/Counter Block Diagram im 328er Datenblatt auf, bei mir Bild 16-1. Haben das alle gefunden?

Dort finden wir das Timer-Counter-Register TCNT, 2-3 Output Compare Register OCR und ein Input Compare Register ICR. Im Betrieb läuft TCNT von BOTTOM (0) bis TOP und wird dann zurückgesetzt oder wieder heruntergezählt. TOP gibt also die Frequenz vor und kann eine Konstante sein oder in ICR oder OCRA (CTC mode) vorgegeben werden. Wer Angst hat, daß eine zu lange Pause vorkommt weil TCNT höher ist als das neue TOP, kann nach dem Setzen von TOP den TCNT auf 0 löschen.

Wenn man das einmal begriffen hat, ist der Rest (PWM...) auch nicht mehr schwer zu verstehen.

Noch Fragen?

DrDiettrich: Wer Angst hat, daß eine zu lange Pause vorkommt weil TCNT höher ist als das neue TOP, kann nach dem Setzen von TOP den TCNT auf 0 löschen.

Es kommt immer darauf an, wie exakt ich es beim Übergang der Frequenzen haben will. Auch das Rücksetzen führt dazu, dass ich eine Periode bekomme, die weder der alten, noch der neuen Frequenz entspricht. Sauber geht es nur über die ISR, oder - wie schon angesprochen - über einen PWM mode, bei dem TOP durch OCR1A mit double buffering definiert wird. Um bei einem 50% PWM-Verhältnis zu bleiben, müssen dann aber immer beide OCR-Register parallel verändert werden. Da das aber nicht wirklich gleichzeitig möglich ist, können auch da race-Bedingungen auftreten.

Ob das alles im konkreten Anwendungsfall stört, ist eine andere Frage. Warten wir doch einfach mal was Kay dazu sagt.

Um bei einem 50% PWM-Verhältnis zu bleiben,

Wie kommst du auf die Idee, dass ein Tacho ein 50% Verhältnis sehen möchte

Zumindest der Threadtitel spricht eindeutig von Pulsen. Und das ist auch das, wie ich Tachos kenne

Ich, für meinen Teil, habe mich entschieden nicht an der Lösungsfindung teilzunehmen, bis das Thema geklärt ist.

Da ist was dran. Ich hatte mich an dem Sketch im Ausgangspost orientiert - da wird ein Signal mit 50% Tastverhältnis erzeugt. Bei Impulsen gleicher Länge, aber unterschiedlicher Wiederholfrequenz sieht das alles wieder anders aus. Ist wohl wirklich am besten wir warten mal ab was Kay sagt.

DrDiettrich: Noch Fragen?

Hallo, du hast meine Frage noch nicht beantwortet. Zu welchen Zeitpunkt möchtest du TCNT nullen? Woher weißt du (ohne ISR ?) das du es 'jetzt' tun mußt? Von welchen Mode redest du in dem du das machen möchtest? Der Anspruch ist saubere Frequenzen zu erzeugen und nicht irgendwas was danach aussieht.

Wegen den 50% Tastverhältnis. Ich glaube in seinem letzten Thread dazu wollte er nur verschiedene Frequenzen erzeugen.