PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Atmel TIMER1CAPT Problem mit Registern



ähM_Key
09.11.2005, 15:40
Hi!

Ich möchte gerne die Drehzahlüberwachung über die InputCapturePin's (ICP) [Mega64L] lösen.

Programmiert wird in e-Lab's AVRco.

Zum Initialisieren hab ich folgende Register gesetzt:

INCL(TIMSK,5); //Input Capture enable PD4
INCL(TCCR1B,0); //clk
INCL(TCCR1B,6); //ICESn: Input Capture Edge Select


Und immer bei einem Interrupt wird folgende Routine ausgelöst:

interrupt TIMER1CAPT;
begin
x:=word(ICR1L)+256*word(ICR1H);
speed:=word(abs(integer(x) - integer(xold)));
xold:=x;
end;

Leider funktioniert das irgendwie noch nicht wirklich, denn ich bekomme unheimlich sprigende Werte (z.B. "55,67,116,133,52,58,151,37,139,65,25,83,93,35") und das ICR1H-Register wird auch nicht beschrieben...

Weiß jemand an was das liegt?
Muss ich ev. bei jedem Interrupt ein Register wieder leeren o.Ä.?

Gruß, ähM_Key

SprinterSB
09.11.2005, 15:57
The TCNT1, OCR1A/B, and ICR1 are 16-bit registers that can be accessed by the AVR CPU via the 8-bit data bus. The 16-bit register must be byte accessed using two read or write operations. The 16-bit timer has a single 8-bit register for temporary storing of the High byte of the 16-bit access. The same temporary register is shared between all 16-bit registers within the 16-bit timer. Accessing the Low byte triggers the 16-bit read or write operation. When the Low byte of a 16-bit register is written by the CPU, the High byte stored in the temporary register, and the Low byte written are both copied into the 16-bit register in the same clock cycle. When the Low byte of a 16-bit register is read by the CPU, the High byte of the 16-bit register is copied into the temporary register in the same clock cycle as the Low byte is read.

Not all 16-bit accesses uses the temporary register for the High byte. Reading the OCR1A/B 16-bit registers does not involve using the temporary register. To do a 16-bit write, the High byte must be written before the Low byte. For a 16-bit read, the Low byte must be read before the High byte.

The following code examples show how to access the 16-bit Timer Registers assuming that no interrupts updates the temporary register. The same principle can be used directly for accessing the OCR1A/B and ICR1 Registers. Note that when using “C”, the compiler handles the 16-bit access.

Ob die Auswertung von '+' von links nach rechts erfolgt weiß AVRco allein...

Sind andere Interrupts aktiv?

Die integer() und word() kommen mir etwas suspekt vor. Das soll wohl ein Timer-Überlauf pflastern? integer() liefert wohl nen vorzeichenbehafteten Typ (32 bit?) und word() nicht (16 bit?), zudem mit anderen Wertebereichen. Da ist möglicherweise auch ein Wurm drinne.

ähM_Key
09.11.2005, 16:10
Ob die Auswertung von '+' von links nach rechts erfolgt weiß AVRco allein...
Öhm,worauf willst du hinaus?

Soweit ich weiß, sind keine andern Interrupts aktiv. (Woher weiß man das?)

Aber die Variante, dass ich erst den ICR auslese, diesen speichere und beim nächsten Interrupt mit dem nächsten vergleiche ist doch richtig, oder?

Ja, ich weiß, dass mit dem Interger ist bissl' wild , aber auch wenn ich mit nur den aktuellen ICR1L ausgeben lasse, wird's nix.
Ich weiß nciht wirklich wie ich anders einen Überlauf verhindern soll. Ok, ich könnte alles mit Interger laufen lassen, aber speichertechnisch ist das auch ni grade das beste...

Gruß, MK

SprinterSB
09.11.2005, 16:45
Beim Lesen von 16-bit-SFRs muss man zuerst das LOW lesen und danach das HIGH, welches in TEMP gelatcht wurde.

Falls die Auswertung von Ausdrücken von rechts nach links erfolgt, wird das nicht eingehalten und du liest Schrott. In C etwa ist teilweise nicht spezifiziert, wie die Auswertung erfolgt und Ausdrücke wie foo(a++,a++) haben unterschiedliche Semantik je nach C-Implementierung.

Ob andere IRQs aktiv sind weiß man, weil man idR das Programm selber geschrieben hat ;-)
Oder man fragt Kollegen, die die anderen Module machen. Oder schaut in die Quellen. Oder schaut sich Listfiles an. Falls ein OS auf dem AVR läuft sind ziemlich sicher IRQs aktiv. In dem Falle sollte das in der Doc zu deinem OS stehen.

IRQs können deshalb Probleme machen, weil sich dadurch die Interrupt-Response-Times vergrössern. Wenn die Zeit so groß wird, daß zwei oder mehr InCapt-Ereignisse auftreten, fängst du nur den letzten Wert, dementsprechend falsch wird dein Ergebnis. Die Latenz erhöht sich auch durch einen langen ISR-Prolog.

Wenn du von einem InCapt zu einem anderen nur 35 Zyklen hast ist das *absolut* verschärft, da würd ich nicht mal mehr mit C dran sondern nur mit Assembler und ohne externe Calls wie abs() um den Overhead von Funktions-Prolog und -Epilog zu umgehen.


The main challenge when using the Input Capture unit is to assign enough processor capacity for handling the incoming events. The time between two events is critical. If the processor has not read the captured value in the ICR1 Register before the next event occurs, the ICR1 will be overwritten with a new value. In this case the result of the capture will be incorrect.

When using the Input Capture interrupt, the ICR1 Register should be read as early in the interrupt handler routine as possible. Even though the Input Capture interrupt has relatively high priority, the maximum interrupt response time is dependent on the maximum number of clock cycles it takes to handle any of the other interrupt requests.

OS = Operating System
ISR = Interrupt Service Routine
SFR = Special Function Register

ähM_Key
09.11.2005, 17:26
Also besser so:

interrupt TIMER1CAPT;
begin
x:=word(ICR1L);
x:=x+256*word(ICR1H);
[...] ?

Was ist damit gemeint, dass das High in Temp gelatcht wurde?
Habe zwar in der Prozessor-Manual gelesen, dass das irgendwie in ein Temp Register geschrieben wird, damit man's 'gleichzeitig' auslesen kann, aber nicht verstanden, wie das funktioniert bzw. wie man dann auf das Temp-Register zugreift...

Ok, dann sind keine weiteren Interrupts aktiv, da es auch ein BS (OS, wenn du willst ;) ) gibt. Oder braucht der PWM-Port sowas? Mit Systick hat's auch nix zu tun, oder?

Hmm, hab jetzt nochmal grob durchgerechnet. Also ich bekomme maximal etwa 200Hz am ICP (also nix mit 35 Zyklen, was ja ~230kHz entsprechen würde) , was bei der 8Mhz CPU den Timer bei nem Prescaler von 1 auf 40 000 Zählen müsste. Wenn ich aber den Prescaler mittels CSn2:0 (Clock Select) im TCCR1B erhöhe, dann geht mein PWM nicht mehr :(

Sorry wenn ich mich bissl's blöd ausdrücke, aber bin noch blutiger Anfänger mit so hardwarenaher Programmierung ;)

MK

SprinterSB
09.11.2005, 17:54
Read(LOW) --> du bekommst LOW und TEMP:=HIGH
Read(HIGH) --> du bekommst den Inhalt von TEMP
Ist schon richtig so wie's dasteht.

'Systick' sagt mir nix, ich kenn die Sprache nicht :-b nur die AVR-Hardware ist mir etwas vertraut. Möglicherweise unterstützt die Sprache direkt das Lesen eines 16 Bit mit foo := ICR1

InCapt zusammen mit PWM hab ich noch nicht verwendet. In einem Beitrag hiess es mal, das würde zusammen nicht funktionieren. Im Manual konnte ich aber keine Aussage dazu finden, möglicherweise war's ne Ente. Ich seh keinen Grund, warum es nicht gehen sollte -- ausser natürlich für Modi, wo ICR1 den TOP für Timer1 definiert. PWM-Modi, die ein TOP von 0xff 0der 0x1ff haben sind auch nicht so angesagt. Und je nach PWM-Mode zählt Timer1 abwechselnd hoch und runter, für InCapt ist das nicht sonderlich hilfreich.

Welchen PWM-Modus verwendest du denn?

Timer2 für die PWM ist keine Option?

Ansonsten sind zu erwartende 5ms zwischen zwei InCapt-IRQs massig Zeit. In weniger als 5ms sind schon Weltreiche erblüht und wieder versunken...

ähM_Key
09.11.2005, 19:03
Read(LOW) --> du bekommst LOW und TEMP:=HIGH
Read(HIGH) --> du bekommst den Inhalt von TEMP
Ist schon richtig so wie's dasteht.
'Systick' sagt mir nix, ich kenn die Sprache nicht nur die AVR-Hardware ist mir etwas vertraut. Möglicherweise unterstützt die Sprache direkt das Lesen eines 16 Bit mit foo := ICR1

Ich glaube das wird nicht unterstützt, aber wenn es so funktioniert (bzw. funktionieren müsste 8-[ ) muss man sich ja darüber erstmal nicht mehr den Kopf zerbrechen.

Hm, keine Ahnung was das für ein Modus ist; ist ein HardwarePWM der von Timer 1 abhängt und an OC1A, B und C ausgibt.

Ursprünglich wollte ich die im Compiler integrierte 'FreqCount'-Funktion nutzen, da ich aber 2 PWM-Frequenzen erzeugen muss (Timer1) und 2 Frequenzen einlesen möchte (Freqcount braucht für jede Frequenz nen eigenen Timer) und der Atmel nur 2 Timer (1 und 3 [desswegen Timer2 nicht möglich]) hat, geht das nicht.

Ja, in 5ms müsste sich einiges reißen lassen...
Wobei wenn ich jetzt dran denke, dass ich aufgrund der auch benötigten ADC-Wandlung 12ms 'Zwangs Pause' einlegen muss - tja, da wird mal wohl nicht um Multitasking drum rum kommen und stat nem Delay Sleep nutzen; naja bis jetzt ist das aber alles in dem Testprogramm noch nicht implementiert, kann also auch keine Fehlerquelle sein.
___

Ok, hab jetzt mal den ganzen PWM-Quatsch rausgenommen, und siehe da, es funktioniert!!!
Aber wie soll mein Roboter ohne PWM fahren können? SoftPWM?

Ich dreh hier gleich durch...

MK

EDIT: Im Anhang mal der Aufgezeichnete Verlauf mit meiner Software (hab den Motor auf 100% betrieben [da kein PWM möglich] und ihn 'per Hand' abgebremst/belastet)...da wird noch ein bisschen Filterarbeit nötig sein

SprinterSB
10.11.2005, 12:30
Hm, keine Ahnung was das für ein Modus ist; ist ein HardwarePWM der von Timer 1 abhängt und an OC1A, B und C ausgibt.

Ursprünglich wollte ich die im Compiler integrierte 'FreqCount'-Funktion nutzen, da ich aber 2 PWM-Frequenzen erzeugen muss (Timer1) und 2 Frequenzen einlesen möchte (Freqcount braucht für jede Frequenz nen eigenen Timer) und der Atmel nur 2 Timer (1 und 3 [desswegen Timer2 nicht möglich]) hat, geht das nicht.

Ja, in 5ms müsste sich einiges reißen lassen...
Wobei wenn ich jetzt dran denke, dass ich aufgrund der auch benötigten ADC-Wandlung 12ms 'Zwangs Pause' einlegen muss - tja, da wird mal wohl nicht um Multitasking drum rum kommen und stat nem Delay Sleep nutzen; naja bis jetzt ist das aber alles in dem Testprogramm noch nicht implementiert, kann also auch keine Fehlerquelle sein.


Die von deinem Compiler zur Verfügung gestellten Goodies sind bestimmt nett -- wenn man ausser denen sonst nichts braucht. Da gibt es einige Abhängigkeiten, wie du schon bemerkt hast.

Ein MUSS ist zu wissen, was in deinem Programm abgeht, also auch was der Compiler/Laufzeitumgebung dazu tut. Etwa was ein "Systick" macht. Hört sich nach Zeitbasis an. Demnach belegt Compiler/Libfunction/Laufzeitumgebung zumindest einen Timer oder implementiert sogar ne ISR für die Ticks.

Soft-PWM ist machbar, aber du hast nen Riesen-Overhead und brauchst massig Bandbreite für deine PWM-ISR. Ausserdem kannst du mit einem gründlichen Jitter auf der so generierten PWM/Frequenz rechenen.

Mega64 hat 4 Timer.
Timer0: 8 Bit,
Timer1: 16 Bit, PWM, InCapt, CTC
Timer2: 8 Bit, PWM, CTC
Timer3: wie Timer1

Du brauchst ja schon 2 Timer für die 2 PWM-Frequenzen. Oder sind's 2 PWMs bei der gleichen Frequenz?

Auch denkbar: Den InCapt via externem IRQ nachbilden. Das erhöht die Ungenauigkeit und geschachtelte IRQs müssen zugelassen werden:
Die EXT-IRQ darf alle anderen ISRs unterbrechen, ist selbst aber ununterbrachbar. Zumindest bis zu dem Zeitpunkt, wo sie TCNTx liest. IMHO ist das nicht nötig, weil genug Timer da sind.

Für den ADC gibt's auch ne IRQ, warten braucht man nicht.

ähM_Key
13.11.2005, 18:52
Hi!

Das ist fast unmöglich herauszufinden, was der Compiler da zusammenbaut...und aus dem ASM-Code zu lesen kann ich nicht.

Die beiden PWM-Kanäle haben die gleiche Frequenz.

Mir ist das trotzdem alles noch viel zu kompliziert, wesshalb jetzt ein F-U-Wandler verwendet werden soll...

Gruß, ähM_Key