Timer-ISR - Impulsbreite stabil setzen

mmi

Mitglied
27. Okt. 2009
101
0
16
Northern Bavaria
Sprachen
  1. ANSI C
Hallo liebe AVR-Freunde,

eigentlich gibt es nicht wirklich ein Problem, aber als Perfektionist möchte man annähernd - insbesondere in ISR-Routinen - eine möglichst effiziente Lösung haben. ;)

Für die Steuerung eines Servos habe ich einen OVF Timer auf einem ATmega328 eingerichtet, programmiert in C.
Die Daten:

- Periode 20 msecs = 50 Hz
- Impulsbreite 1,5 msec = Mittelstellung
- Impulsbreite 0,6 msec - 2,5 msec für 180 Grad Drehung (vermutlich etwas unüblich, aber mein Billigservo will es so)

Das läuft so auch zu meiner vollsten Zufriedenheit, der Oszi zeigt diese Werte mit erstaunlicher Stabilität :girl_wacko:

Das jeweilige HIGH/LOW-Signal für die Impulsbreite liesse sich am entsprechenden Pin wahrscheinlich mit einem CompareA/B automatisch setzen, ich habe es aber aus diversen Gründen manuell gemacht und aus der Arduino-IDE vorläufig "digitalWrite" dafür verwendet.

Diesen wollte ich nun mit "PORTD |= (1<<PD5)" bzw. "PORTD &= ~(1<<PD5)" einfach und effizient ersetzen. Erstaunlicherweise wird dann der Timer langsamer. ich musste den Feinabgleich an TCNT2 um ca. 20 vermindern (prescale=1, 10usec Auflösung) und die Impulse (An-/Abstiegszeit) werden recht schlampig. Die Frequenz von 50 Hz schwankt um bis zu +-8%.

Ein Blick in den Sourcecode von Arduinos "digitalWrite" zeigt einigen Aufwand, u.a. werden auch Ints kurz abgeschaltet - damit läufts wie gesagt auch sehr sauber.

Ich habe testweise vor/nach den PORTD-Bitmanipulationen CLI und SEI probiert - vielleicht etwas stabiler, aber nicht wesentlich wahrscheinlich nur Einbildung.

Prinzipiell würde die Servosteuerung so auch noch funktionieren - aber wenn man mal die Stabilität mit "digitalWrite" gesehen hat, fragt man sich:

Warum soviel Aufwand, um lediglich ein sauberes High/Low zu setzen?

Gruß,
Manfred

P.S.: Einem falsch gesetzten fuse Bit werde ich ja hoffentlich nicht schon wieder zum Opfer gefallen sein. :(
 
Hi,

zur Arduino IDE kann ich Dir nichts sagen - mMn solltest Du aber auch da in der Lage sein, die Hardware direkt anzusprechen. Inwiefern allerdings die IDE dann zusätzlich in Deiner Hardware "rumwurschtelt", ohne daß Du es direkt bemerkst, ist 'ne andere Frage. Wenn Du den Timer festlegst (direkt), und hinterher irgendwas einbindest, was denselben Timer nutzt, ohne das Du es bemerkst...

zur Genauigkeit: die hängt halt nur vom verwendeten Taktgeber ab. Einmal initialisiert läuft der Timer halt im Hintergrund (selbst im Standby-Mode). Und somit lassen sich die entsprechenden ISR eben auch entsprechend genau anspringen.

Zum Problem: Ich gehe mal vom 88er aus, der 328er ist ja quasi nur der große Bruder...
Was willst Du eigentlich?
-Der Timer soll nach 20ms überlaufen (möglichst exakt)
-ein Pin soll beim Überlauf gesetzt werden, und bei einer einstellbaren Zeit gelöscht (oder andersrum)

Du wirst das sicher alles selbst wissen, aber vielleicht kann ja jemand anders hier noch was neues lernen - deswegen kurze Erläuterung:
Also willst Du ein PWM, in dem der Timer nur rauf zählt (single slope bzw Fast PWM Mode). Welcher Timer deine 20ms überstreichen kann, hängt vom Basistakt und dem Prescaler ab. Jetzt wirst Du den aber nicht auf genau 20ms bekommen - Du mußt ihn also vorher zurücksetzen. Das kannst Du einerseits in Software machen (bei TOV in der ISR TCNT neu setzen), Der Timer kann das aber auch selbst Durch einen Compare mit einem Register machen. Das ist dann der CTC-Mode. Und da jeder Timer 2 OCRegister anbietet, kannst Du auch bei jedem Timer eine Kombination aus beiden Modes wählen.
-PWM-Duty wird durch OCRnB festgelegt
-PWM-Frequenz wird durch OCRnA festgelegt (neben Quarz, Prescaler)
-einmal initialisiert muß zur Laufzeit dann also nur OCRnB umgeschrieben werden, wenn sich das Servo bewegen soll.

Bei den 8bitern ist das AFAIK WGM=7, beim 16er WGM=15 (ohne Gewähr - siehe eigenes DB).

So wäre das mMn in Hardware realisierbar, was sind Deine Gründe, das manuell (=in Software? oder =Hardware, aber Register manuelle gesetzt?) zu machen?

MfG
LotadaC
 
Hallo LotadaC,

danke für Deine Mühe und die Ausführungen, die für mich noch recht nützlich sein werden.

Dieser thread ist ja mein letzter Schritt, endlich ein reines AVR-System zu haben, also völlig arduinofrei.

"Problem" hat sich aufgeklärt, ich habe mich mit "digitalWrite" nur nochmal richtig verar...en lassen:
Neben dem Setzen des Portbits ist dort eine Menge Zeug codiert, u.a. werden auch Interrupts aus-/eingeschaltet.
Durch den Ersatz mit "PORTD..." ergeben sich dann andere Zeitverhältnisse, was zu dem obigen Effekt führte.

Jetzt läuft aber alles sauber und natürlich auch viel effizienter. :)

Mit den Feinheiten der Timer habe ich mich noch nicht viel beschäftigt, mein Wissen reicht gerade für einen OVF Timer mit prescaling und Feinabstimmung. Da ist Deine Beschreibung auch für mich sehr hilfreich.
Schön wäre natürlich, wenn man ein dokumentiertes C-Programmbeispiel hätte. ;)

Meine Lösung sieht bis jetzt so aus:

Code:
ISR (TIMER2_OVF_vect) {
  TCNT2 = tcnt2; // reload timer start
  
  // SERVO: Prescale 1, Period: 2000ticks=20msecs (50Hz), 1tick=10usec
  if        (ticks == 0)  { PD5HIGH; ticks++; }
  else if (ticks == PulseWidth) { PD5LOW;  ticks++; }
  else if (ticks == 2000) {
      ticks=0;
      if (ButtonPressed) {
         ...
      }
  } else ticks++;
}

Nur als Einstieg gedacht. Meine eigentliche Idee ist, ein Grundgerüst zu schaffen, welches in einem einzigen Interrupt mehrere Funktionen bedient - wie z.B. mehrere Servos, Buttons, LEDs, Shiftregister, etc. zu steuern bzw. auslesen.

Deswegen auch mein Verzicht auf eine Steuerung über CompareA/B mit direktem Setzen des Portbits. Ich glaube bis jetzt noch, daß gerade für mehrere Funktionen der Code dadurch leichter lesbar ist, obwohl es dann natürlich auch etwas Effizienz kostet.

Gruß,
Manfred.
 
Hab ich das richtig verstanden?
Du willst also sozusagen äquidistante "Zeitfenster" (=PWM-Frequenz), und dann innerhalb dieser Zeitfenster zu variablen Zeitpunkten mehrere verschiedene Ereignisse auslösen. Du hast quasi mehrere PWM gleichzeitig am laufen. Über einen Timer. Sollen die alle echt parallel laufen, oder "schachtelst" du die? (Also erstes Zeitfenster=Servo1, 2tes=Servo2, 3tes=Sclter_LED... usw - quasi wie bei den analogen Funkfernsteuerungen).
Bei letzterer Anforderung könnte man im "Fensterüberlauf" auf das nächste zu regelnde Ziel umschalten. Die erste Anforderung legt eine Lösung ähnlich dem Weckerprogramm nahe (mehrere Weckzeiten quasi - und die dort geforderte zeitliche Auflösung reicht Dir sicher nicht).

Der von mir erklärte Fast-PWM läuft halt komplett(!) im Hintergrund ab. Ohne Interrupts. Bascom zB bietet als Hochsprache Init-Routinen für die üblichen Verwendungsmöglichkeiten der Timer an. Aber eben nicht für die spezielleren Fälle (weil die ja auch bei den unterschiedlichen µC differieren). Bei C wird das ähnlich sein. ABER was macht denn so eine Init eigentlich? Sie schreibt die entsprechenden Werte (einmal) in die I/O-Register, die die Funktion der Timer festlegen. Und das kannst Du auch selbst direkt machen. Mit allen(!) Möglichkeiten, die der Controller Dir bietet. Welche Möglichkeiten das sind, und welche Werte da in Welche Register müssen, sagt Dir das Datenblatt.
(btw: Ich habe meinen Phasenanschnittsdimmer auch erst mit If-Schleifen und wait's angefangen, dann mit über den externen Interrupt gestarteten Timern, die im Überlauf den Triac zünden usw, dann fehlen mir jetzt sicher ein paar Schritte, final ist jetzt so:
-Timer läuft mit PWM durch
-beide OCR zünden die TRIACS (unterschiedliche Anschnittswinkel möglich)
-Überlauf des Timers löscht die TRIACS (bzw nimmt die Zündspannung weg) -> muß vor dem Phasendurchgang erfolgen, deswegen:
-Nulldurchgang löst INT0 aus, ISR lädt den Timer genau so, daß er sicher innerhalb der nächsten 10ms übergelaufen ist.
INT0-ISR ist der Einzige dafür benötigte Interrupt, und alles(!) andere läuft unabhängig vom Programm im Hintergrund.)

Aber zurück zu Dir:
Zu den Programmieralgorithmen brauch ich Dir sicher nichts sagen, von "Arduino-IDE"(?) hab ich gar keine Ahnung, von C vielleicht etwas, Bascom interessiert hier nicht und Assembler willst Du sicher auch nicht hören. Ich kann Dir nur was zur Hardware des Prozessors sagen.

Du hast 3 Timer
-Timer0 zählt 8bit, und bietet Dir 2 (voneinander unabhängige) PWM-Ausgänge an, die man komplett im Hintergrund arbeiten lassen kann. Die PWM-Frequenz ist in Hardware selbst lediglich über den Prescaler (und den Systemtakt) nur ziemlich grob einstellbar. Zur Feinjustierung kannst Du halt vom Zählbereich etwas abschneiden. Das geht natürlich immer zu lasten der Timerauflösung. Entweder von unten, indem Du in der TOV-ISR den Timer vorstellst (kostet Dich jedesmal Zeit im Hauptprogramm für den Interrupt), oder von oben, indem Du den Timer bei Gleichheit mit OCR0A automatisch (! - also ohne Interrupt) auf 0 vorstellen läßt (kostet Dich den Output Compare).
Eine andere Möglichkeit hast Du hier aber noch: Du kannst den Timer auch mit einer zusätzlichen externen Taktquelle laufen lassen.
-Timer2 zählt auch 8bit, mit nahezu denselben Möglichkeiten wie Timer0 (allerdings bietet er mehr Prescaler). Auch er kann mit einer anderen externen Taktquelle verwendet werden, allerdings an denselben Pins, wie der externe Systemtakt, folglich müßte man dann für die CPU und den ganzen Rest den internen Oscillator verwenden.
-Timer1 ist erstmal ein 16bit-Timer. Du kannst ihn aber auch als 8bit, 9bit oder 10bit-Timer laufen lassen (auch im Fast-PWM). Zusätzlich zu den 2 OC-Registern (->PWM) hast Du hier noch das Input Capture Register (ICR). Und mit diesem kannst Du auch (im Hintergrund) den Timer auf 0 vorsetzen lassen. Somit kannst Du die PWM-Frequenz feinjustieren, ohne hier ein OCR einzubüßen (allerdings zu lasten des Input-Capture-Units).
Auch Timer1 kann über eine externe Taktquelle getaktet werden, und bietet unterschiedliche Prescaler

Insgesamt stehen Dir direkt in Hardware 6 echte PWM-Channels zur Verfügung (große Überraschung - steht ja auch so ganz am Anfang vom Datenblatt) Die Du nutzen kannst (aber nicht mußt) - wenn Du mehr willst, mußt Du zwangsläufig mehr in Software machen
 
Ja, Du hast mich richtig verstanden:
Geschachtelte Zeitfenster innerhalb einem OVF-Timer - ich lasse meine eigenen Ticks laufen und je Interrupt (alle 100 usec) wird eine andere Funktion bedient. Mit den vielen Funktionen war nur eine erste Idee - es ist mir natürlich klar, daß die Ressourcen schnell aufgebraucht sein können. Um einen realistischeren Einblick zu bekommen, habe ich mit einem Timer rein softwaremässig 8 LED's mit exponentiell variierter PWM zeitlich völlig unabhängig voneinander auf- und abgedimmt, 3 Buttons bedient (mit Entprellen) und eine sehr exakte 50Hz ppm für den Servo erzeugt, also 9 Software-PWM-Kanäle. Wie ausgelastet der Prozessor dabei ist, muß ich erst noch genauer ermitteln, aber einen "schwitzigen" Eindruck hat der 328 dabei nicht gemacht, da scheint noch einiges an Luft da zu sein.

Wie ich ja oben schon angesprochen hatte, habe ich mich bisher nicht weiter um die Fähigkeiten der Timer gekümmert. Mit dem Arduino war es aufgrund der fertigen Funktionen bzw. Libraries auch gar nicht erforderlich, dafür sind dann die Möglichkeiten aber auch beschränkt. Das soll aber keine Kritik sein, für den an Elektronik interessierten Laien ist es ein tolles Konzept, das mittlerweile weltweit sehr erfolgreich ist.

Die 6 rein hardwareseitig steuerbaren PWM-Kanäle haben mich schon aufhorchen lassen und ich habe mich gestern mal im DB "umgesehen". Man kann zwar nicht sagen, daß das DB nicht ausführlich wäre, aber manchmal wirkt es schon etwas unübersichtlich, da wäre es z.B. angenehm, wenn für WGM11 auch gleich der Name des Registers dabei stünde und man nicht erst wieder in eine andere Tabelle schauen muß und so geht es mir zumindest an anderen Stellen immer wieder.

Was ich (noch?) nicht verstanden habe:
Da steht beispielsweise "... when the WGM13:0 bits are set ..." - warum Bits - WGM13 ist doch nur 1 Bit, und was bedeutet denn ":0" ?
Oder da steht "WGM13:0 = 9 or 11" - vielleicht stehe ich ja auf der Leitung, aber was hat 9 oder 11 mit einem Bit zu tun ?

Offenbar muß man es aber auch nicht unbedingt wissen: "learning by doing" war angesagt ;)

Ich habe an PB1 und PB2, die auch die OC1A/B Ausgänge für den 16-Bit Timer1 darstellen, zwei LEDs angeschlossen und sie testweise folgendermassen angesteuert:

Code:
  cli();

  DDRB |= (1<<PB1) | (1<<PB2); // OUTPUT

  ASSR   &= ~(1<<AS2); // clock source = internal

  TCCR1A &= ~(1<<WGM10);            // FastPWM (Mode 14)
  TCCR1A |=  (1<<WGM11);            // FastPWM (Mode 14)
  TCCR1B |=  (1<<WGM12)|(1<<WGM13); // FastPWM (Mode 14)
  TCCR1B |=  (1<<CS12);             // prescale 64 

  TCCR1A |= (1<<COM1A1); // PB1/OC1A: high at BOTTOM, low at MATCH
  TCCR1A |= (1<<COM1B1); // PB2/OC1B: high at BOTTOM, low at MATCH

  ICR1  = 0x01ff; // set TOP (Maximum Timer), seems it could be up to 65535 in this Mode?
  OCR1A = 0x00ff; // MATCH: set PB1/OC1A from high to low
  OCR1B = 0x007f; // MATCH: set PB2/OC1B from high to low
  TCNT1 = ICR1-1; // needs a BOTTOM event to set pins high at start!

  sei();

Zunächst tut sich nichts, seltsamerweise musste ich erst WGM10 auf 0 setzen. Eigentlich dachte ich, daß dieses Bit lt. DB nach einem Reset automatisch nicht gesetzt ist? Empfiehlt es sich generell, explizit solche Bits zu löschen ?

Es läuft - die beiden LEDs blinken entsprechend gemütlich im 2-4 sec. Rhythmus vor sich hin - sehr schön, so ganz ohne ISR :)

Dann fällt mir auf, daß nach Programmstart die LEDs erstmal dunkel bleiben, obwohl am Zyklusbeginn (BOTTOM) immer HIGH erwünscht ist.
Deshalb habe ich TCNT1 hochgesetzt, dann läuft es richtig, das offenbar notwendige BOTTOM Ereignis wird ausgelöst.

Ist eine bestimmte Reihenfolge beim Setzen der Register einzuhalten oder ist es egal?

Fragen über Fragen, da danke ich schon mal im Voraus für die Mühe!
 
Was ich (noch?) nicht verstanden habe:
Da steht beispielsweise "... when the WGM13:0 bits are set ..." - warum Bits - WGM13 ist doch nur 1 Bit, und was bedeutet denn ":0" ?
Oder da steht "WGM13:0 = 9 or 11" - vielleicht stehe ich ja auf der Leitung, aber was hat 9 oder 11 mit einem Bit zu tun ?
Ja, WGM13 ist ein bit. Ebenso wie WGM12, WGM11 und WGM10. Diese Bits legen den Waveform Generation Mode des Timers 1 fest. Deswegen WGM1x. Da es 16 verschiedene Modes zu verschlüsseln gilt, braucht es 4 bits dazu (halt für 0000binär bis 1111binär). Das sind Deine 4 WGM-bits. Theoretisch könntest Du also zB für Waveform 15 einfach die 15 in das WGM-Nibble schreiben. Leider liegen diese 4 Bits jedoch in unterschiedlichen I/O-Registern. Die 4 bits zusammen legen den WGM fest, jetzt klar?
ist also 1001binär=9dez, also WGM13=1, WGM12=0, WGM11=0, WGM10=1
für die 11 dann halt mit 1011binär, klar?
Zu:
"... when the WGM13:0 bits are set ..."
Das hast Du so aus dem Zusammenhang gerissen. Da gehts drum, wie sich OC1A und OC1B verhalten, wenn Du einen nicht-PWM-Mode aktivierst, indem Du die entsprechenden bits in den 4 WGM-bits (WGM13..WGM10) setzt oder löschst.
Dann fällt mir auf, daß nach Programmstart die LEDs erstmal dunkel bleiben, obwohl am Zyklusbeginn (BOTTOM) immer HIGH erwünscht ist.
Das ist logisch: im PWM-Mode beeinflussen 2 Ereignisse den OC-Pin: einerseits wenn TCNT den Wert im OC-Register erreicht (compare halt), andererseits wenn der Timer von TOP nach BOTTOM überläuft. (Dabei kann TOP unterschiedlich festgelegt werden: 0x00FF, 0x01FF, 0x03FF, 0xFFFF, den Wert von OCR1A, dito ICR1). Wenn Du also mit TCNT=0 startest, ist das kein Überlauf.
Deshalb habe ich TCNT1 hochgesetzt
alternativ könntest Du direkt vorher auch das Port-bit des entsprechenden Beinchens per Hand umsetzen.

Jetzt noch zu Deinem Code:
Wie gesagt, ist C nicht mein Ding, aber generell ist noch folgendes zu sagen:
AS2 in ASSR sollte per default bereits 0 sein, bei WGM10 allerdings dasselbe :hmmmm:
AFAIK sind Deine Zuweisungen read-modify-write-Instruktionen (notwendig, wenn man einige Bits in einem Register manipulieren will, aber nicht alle). Soweit richtig. Was Du hier aber machst ist:
-TCCR1A (in ein Rechenregister) laden
-WGM10-Bit (in der geladenen Kopie) löschen (?)
-Ergebnis nach TCCR1A schreiben (<-TCCR1A &= ~(1<<WGM10))
-TCCR1A wieder laden
-WGM11-bit setzen (in der Kopie)
-Ergebnis wieder nach TCCR1A schreiben (<-TCCR1A |= (1<<WGM11))
-TCCR1A nochmal laden
-COM1A1 setzen
-TCCR1A zurückschreiben (<-TCCR1A |= (1<<COM1A1))
-TCCR1A schon wieder laden
-COM1B1...
-und nochmal TCCR1A zurückschreiben (<-TCCR1A |= (1<<COM1B1))
klar geht das, aber es kostet unnötig Zeit und Programmspeicher. Wie Du es schon oben bei dem Datenrichtungsregister gemacht hast, kannst Du die Bits auch in einem Rutsch festlegen, und einmal ins I/O-Register (TCCR1A) schreiben. Und Weil Du ja weißt was bei den 3 Informationen, die das TCCR1A enthält (COM1A1:0, COM1B1:0 und WGM11:0) hinsoll, interessiert Dich auch nicht mehr, was da vorher stand. Somit kannst Du jetzt also direkt zuweisen, und brauchst kein r-m-f mehr. So in etwa:
-Bitmaske mit entsprechend gesetzten Bits (das macht der preprozessor aus "(1<<COM1A1)|(1<<WGM11)|(1<<blablub)") ins Rechenregister laden
-Rechenregister (Ergebnis) nach TCCR1A kopieren

in C sollte das dann (hoffentlich) so aussehen:
TCCR1A=(1<<COM1A1)|(1<<WGM11)|(1<<blablub)

Zur Reihenfolge: Du mußt die Register halt in Der Reihenfolge beschreiben, wie Du sie haben willst. Also wie Du willst, daß der entsprechende Effekt eintritt. Sinn macht es zB, einen Timer durch beschreiben des Prescalers erst dann zu starten, wenn alle anderen Register des Timers festgelegt sind. Eine weitere Besonderheit bieten die gepufferten "16bit-I/O-Register" - das sind ja in Wirklichkeit jeweils 2 Register. Um beide Register gleichzeitig zu lesen/schreiben, wird der Puffer benutzt. Dabei kommt es auf die Reihenfolge an. (siehe dazu Datenblatt S.117)
Diesen Punkt sollte Dir aber C abnehmen. Du schreibst ja bereits in das TCNT1 (=2 Register) eine Word-Variable (=2bytes).

Zu den Datenblättern: sie sind ... ähm... gewöhnungsbedürftig. Aber danach eigentlich sehr umfangreich und mMn auch gut strukturiert und übersichtlich. Zu jeder Hardware gibt es am Ende immer den Punkt Register-Description. Dort werden alle verwendeten Bits/Bitgruppen jedes einzelnen Registers erklärt. Dort tauchen auch die Tabellen auf. Schwierig wird es nur, wenn eine Bitgruppe auf mehrere Register aufgeteilt ist (WGM zB). Weil die Tabelle nämlich dann nur bei einem Register auftaucht. Aber im anderen ist dann immer ein Verweis.
 
Die 4 bits zusammen legen den WGM fest, jetzt klar?
Ist jetzt klar, dann sollte das aber in der Form "WGM10..13" oder "WGM1n" im Datenblatt stehen - bei der jetzigen Schreibweise habe ich mich vom Bezug auf ein Bit nicht so leicht abbringen lassen, steht ja schließlich genau so da. ;)

Wenn Du also mit TCNT=0 startest, ist das kein Überlauf.
alternativ könntest Du direkt vorher auch das Port-bit des entsprechenden Beinchens per Hand umsetzen.
Auf das manuelle Setzen des Portbits reagiert er bei mir nicht, Signal bleibt low.

Wie Du es schon oben bei dem Datenrichtungsregister gemacht hast, kannst Du die Bits auch in einem Rutsch festlegen, und einmal ins I/O-Register (TCCR1A) schreiben.
Das stimmt natürlich, da hatte ich viel zu sehr Angst davor, ein anderes Bit im Register kaputtzumachen. Bei diesen Registern ist das aber eher kaum der Fall.
Andererseits wollte ich für jede Funktion der guten Lesbarkeit wegen eine extra Zeile, die Effizienz kann man an der Stelle ja vernachlässigen.
Ich habe aber die überflüssigen "read-modify-write" entfernt und somit 28 Bytes und ein paar Taktzyklen gespart - es ist so auf jeden Fall auch weniger fehleranfällig, wenn man mal was ändert.

Ich habe anderweitig gelesen, daß manche Register das Ändern eines einzelnen Bits gar nicht zulassen, nach meiner Auffassung also kein "read-modify" möglich ist, sondern ausschließlich "write" ausgeführt wird. Da muss man aufpassen, da Bitmanipulationen mit AND / OR dann zu falschen Ergebnissen führen können, was der avr-gcc aber scheinbar nicht bemerkt: vorher im Register bereits gesetze Bits werden dann einfach gelöscht.

in C sollte das dann (hoffentlich) so aussehen:
TCCR1A=(1<<COM1A1)|(1<<WGM11)|(1<<blablub)
So stimmt's.
Wenn da vorher "TCCR1A |= (1<<WGM10)" stand: da darf man sich nicht verwirren lassen, es ist nur die Kurzschreibweise für "TCCR1A = TCCR1A | (1<<WGM10)". Sonst ist aber in diesen Fällen eigentlich fast alles selbsterklärend, denke ich - in ASM sieht es ja auch nicht viel anders aus.

Jedenfalls danke ich Dir für den "Schnellkurs" - wieder einiges dazugelernt. Natürlich werden meine eingangs rein softwaremässig gedachten Funktionssteuerungen jetzt je nach Anforderung anders aussehen - PWM wird sicherlich weitgehendst nicht mehr über TOIE gesteuert werden. ;)

Vielleicht interessiert es ja jemand, deshalb nochmal der verbesserte Code des obigen Beispiels:
Code:
DDRB = (1<<PB1) | (1<<PB2); // set PB1 + PB2 to output

// FastPWM (Mode 14), PB1/OC1A + PB2/OC1B: BOTTOM->high, MATCH->low
TCCR1A = (1<<WGM11) | (1<<COM1A1) | (1<<COM1B1);

// FastPWM (Mode 14), CS=prescaler
TCCR1B = (1<<WGM12) | (1<<WGM13) | (1<<CS11);

ICR1 = 0x1ff; // set TOP (Maximum Timer)
OCR1A = 0x08; // MATCH: switches PB1/OC1A from high to low
OCR1B = 0x3f;  // MATCH: switches PB2/OC1B from high to low
TCNT1 = ICR1-1;  // provocate BOTTOM event to set pins high at start

Das Ergebnis sind 2 mehr oder weniger schwach leuchtende LEDs - nicht mehr, aber auch nicht weniger - sofern man welche angeschlossen hat :)

Gruß,
Manfred
 
Achtung! Most Significant Bit immer links, Least Significant Bit immer rechts. Bei den "verorten" Zuweiseungen ist das egal, weil der Bitname (in der includierten Controller lib) definiert ist, und zwar lediglich mit seiner Bitwertigkeit (Stelle im Byte). Um so viele Stellen wird dann eine 1 nach links geschoben. Der Ausdruck in den Klammern entspricht also exakt dem einen gesetzten Bit. in welcher Reihenfolge man jetzt mehrere Bits verort, ist ja egal. Nicht vergessen darf man, daß diese Berechnungen nicht zur Laufzeit im µC erfolgen, sondern daß der Compiler/Assembler da bereits beim erstellen des Compilats... 'ne Zahl draus macht.

zu Port schalten: wenn man die Pins als Output-Compare aktiviert (COM1A1...) "overrided" der OC die "normal port funktionality" -> das output compare übernimmt also die Kontrolle.
Was passiert aber, wenn man den Pin vorher auf high legt? (kA, ausprobiere - wäre dann ein Fall für Deine Frage zur Reihenfolge)

zu RMF: kommt später...

Später: Prinzipiell ist der I/O-Space Teil des SRAM, und als solcher byteweise zu beschreiben. Natürlich gibt es hier einige Besonderheiten:
(-einige Register können trotzdem bitweise manipuliert werden (bis 0x001F), andere mit besonderen Mnemonics schneller (bis 0x003F), aber das interessiert Dich nicht)
-einige Bits, sogar ganze Register sind mit der Hardware des µC "verdrahtet" - Veränderungen haben mehr oder weniger unmittelbare Effekte.
-einige Bits, sogar ganze Register sind "unused" - Schreibinstruktionen verpuffen dort einfach, gelesen werden sie als 0. Hintergrund, warum man sie trotzdem möglichst nicht beschreiben sollte ist, daß ggf die Software auf einem neueren µC, bei dem die Register dann belegt sind, auch lauffähig sein soll. Uns als Hobbyist interessiert das i.A. nicht.
-einige Register gibt es dann physisch so gar nicht. Beim UART Data Register zB landet eine Schreibinstruktion direkt im Sendepuffer (wenn UDREn gesetzt ist, sonst verpufft sie) und wird gesendet, eine Leseinstruktion liest aber aus dem Empfangspuffer (und läßt das nächste Byte aufrücken, bzw leert ihn).
-wird eine 1 in ein Interrupt-Flag geschrieben, wird dieses Flag gelöscht. Wenn also ein unbehandeltes Interrupt-Flag in einem Register gesetzt ist, und Du da jetzt mit RMF irgendein anderes Bit manipulierst, wird das als 1 gelesene Flag auch als 1 zurückgeschrieben, was das Flag löscht.
-die bits der PIN-Register eines Ports enthalten (gelesen) die logischen ist-Pegel der entsprechenden Prozessorbeinchen. Das schreiben einer 1 in ein Bit dieser Register hat aber keinen Effekt auf das PIN-Register, sondern indirekt auf das entsprechende Port-Register Bit. Das wird dann nämlich getoggelt.
 
Danke - als Basiswissen soll es mir soweit erstmal genügen.

Als nächstes habe ich den Test von GHz-Transceivern (NRF24L01) geplant, die aufgrund des in den Chip bereits eingebauten Protokolls (handshake, etc.) nicht so schwierig zu handhaben sein sollten - mal sehen ...
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)