Hy folks,
ich habe hier eine grundelegende Frage. Ich benutze den TIMERA im
compare mode. Es werden mir dazu ja 3 verschiedene Register zur Auswahl
gestellt. Da ich das CCR0 schon im capture mode verwende, wollte ich das
CCR1 register benutzen. Diese habe ich wie folgt initialisiert
TACTL=(mode&0xFFCF)|TACLR;// set timer register, clear TAR
8
9
CCTL1=control;// set control register
10
if(control<16384)
11
CCR1=cycles;// set compare register if compare mode selected
12
13
TACTL|=(mode&0x30);// Start/stop TIMER_A
14
}
In timer_MANCODE ist das Ergebnis aus dem Capture mode. Wenn ich das
ganze jetzt starte bekomme ich ein willkürliches Ergebnis vom Timer.
Daraufhin habe ich das Datenblatt etwas genauer studiert und gesehen das
durch die MCx bits der Upmode für das CCR0 Register gestartet wird?!
Meine Frage jetzt ist es gar nicht möglich im compare mode das Register
CCR1 oder CCR2 zu benutzen. Im Datenblatt finde ich nichts weiteres. Und
wenn ja wieso gibt es denn drei verschiedene Compare/Capture Register??
Hoffe hier kann mir jemand helfen. Dann auch schon mal vielen Dank!!
Mfg
Stephan
Aaaalso,
den TimerA mit all seinen Möglichkeiten hier zu beschreiben, ist viel zu
umfangreich. Du solltest Dir hierzu nicht das Datenblatt sondern den
User-Guide 'MSP430x1xx Family' anschauen!
Dein Code-Bsp. ist mir auch nicht ganz klar.
>bekomme ich ein willkürliches Ergebnis vom Timer
Wo liest Du denn den Timer aus?
ich benutze nicht den Timer wert den habe ich ja schon durch den capture
mode ermittelt und der ist auch OK.
Das Problem ist das ich ja einen Interrupt auslöse und da ich das mit
CCR1 Register machen wollte bekam ich einen Interrupt in einer
willkürlichen Zeit. Meines achtens liegt es daran das der Interrupt dann
durch das CCR0 Register initialisiert wurde, welches meinen capture Wert
entsprach.
Um es noch genauer zu sagen es geht mir nur darum mit dem CCR1 register
einen Interrupt, in der vorher durch den capture mode des TIMERA0 (CCR0
register) ermittelten Wert, auszulösen.
Hoffe das ist jetzt genauer. Werde mir jetzt erst mal den User Guide
anschauen, hoffentlich werde ich da fündig.
mfg
Einen CCR1-Interrupt wirst Du so nie bekommen, weil Du ihn nicht
aktiviert hast (CCTL1 = 0). Stattdessen hast Du den Timer-Interrupt
freigegeben (TACTL = (mode&0xFFCF) | TACLR; mit mode = 0x0212).
Da Du den Timer im 'Up-to-CCR0' betreibst, kommt dieser IRQ alle
CCR0-Timercounts.
Wenn Du nun einen IRQ durch CCR1 auslösen willst, musst Du CCTL1 |= CCIE
setzen. Allerdings kommt dieser IRQ dann alle CCR0/2, da Dein
Übergabeparameter timer_MANCODE/2 ist!
ok dann habe ich das falsch verstanden aus dem Datenblatt. Ich dachte da
sich der CCR1,CCR2 und der Timeroverflow-Interrupt einen Vektor teilen
muss ich diesen dann auch aktivieren, sprich TACTL = (mode&0xFFCF) |
TACLR; mit mode = 0x0212.
Werde das so mal ändern und hoffe das es klappt.
Ja und Nein :-)
TMR_A, CCR1 und CCR2 teilen sich zwar einen Vektor (Im Register TAIV
steht dann die Interruptquelle) aber die Interrupts müssen einzeln
freigegeben werden!
Ok es funzt jetzt schon mal in der Richtung in der ich will. Das ganze
dauert nur sehr lange. Wenn ich das gemessene mit dem was ich dann
ausgebe vergleiche habe ich einen Unterschied von ca.3µs bei einer
Frequenz von ca.4Mhz. Und das auch nur wenn ich den Timerinterrupt UND
den compareinterrupt anschalte. Ohne Timerinterrupt komme ich nur auf
die hälfte der Frequenz. Auch muss ich den Timer im Interrupt auf null
setzten (TAR=0) Komme leider auch mit dem Userguide nicht so recht
weiter.
hier noch mal mein aktueller code
Sorry, ich versteh' nur Bahnhof!
>habe ich einen Unterschied von ca.3µs bei einer>Frequenz von ca.4Mhz. Und das auch nur wenn ich den Timerinterrupt UND>den compareinterrupt anschalte. Ohne Timerinterrupt komme ich nur auf>die hälfte der Frequenz.
???
Hälfte der Frequenz = 2MHz oder 1/(2*3µs) ?
Wie initialisierst Du jetzt Timer_A ???
Warum übergibst Du timer_MANCODE/2, wenn Du doch timer_MANCODE haben
willst (zumindest hast Du gesagt dass CCR0 = timer_MANCODE)) ???
Beschreibe doch mal bitte eindeutig, was Du eigentlich machen willst!
Deine Erklärungen dazu sind zu ungenau!
Ok dann fange ich mal von vorne an. Ich habe ein serielles Signal. Dies
beinhaltet am Anfang ein Preamble durch welches ich mir die
Periodendauer herauslese. Das mache ich mit den capture mode von
TIMERA0. Um dann das Signal abzutasten initialisiere ich den TIMERA1 mit
der hälfte der vorher gemessenen Periodendauer ca. die Mitte des
gültigen Zustands zu erreichen. Nach erfolgreichen auslesen setze ich
den Timer auf den gemessenen Wert um dann immer wieder auf die Mitte zu
kommen. Um den Anfang der Übertragung zu erkennen wird ein langes high
Signal gesendet und mit dem TIMERB0 ausgemessen. Darauf startet die
Übertragung wie ich es vorher beschrieben habe. Das ist auf jeden Fall
meine Theorie :)
Bei Verbesserungen/Veränderungen bitte raus damit. Das ganze wird auf
einen MSP430x169 realisiert.
So um dann auf die anderen Unklarheiten zurückzukommen. Die Frequenz des
MSP habe ich auf ca.4Mhz mit einem Uhrnquarz eingestellt. Wenn ich jetzt
wie oben das Programm laufen lasse, bekomme ich immer einen Intterrupt
bei ca.53µs es sollten aber nur 50µs sein. Wenn ich das ganze mit dem
Interrup vom TIMERA0 realisiere ist das auch kein Problem. Nutze ich
allerdings den CCR1 Interrupt, verlängert sich alles um sagenhafte 3µs
was bei einer Taktfrequenz von ca.4MHz schon recht ordentlich ist.
Ich hoffe jetzt ist es etwas genauer und präziser formuliert.
mfg
Ist das serielle Protokoll vorgegeben oder kannst Du es ändern?
Ich meine, die einfachste Art wäre natürlich die vorhandene UART im MSP
zu nutzen, wenn das Protokoll das zulässt.
Ansonsten könntest Du auch weiterhin mit der Capture-Methode die
Baudrate ermitteln (Falls diese unbekannt ist) und dann die UART
entsprechend konfigurieren.
Zu der Timer-Problematik fällt mir jetzt so spontan auch nix ein.
Da muss ich noch mal in mich gehen :-)
Fragen zum Verständnis:
Preamble ist 01010101-Folge?
Capture auf beide Flanken? (ergibt Bit-Time)
Capture nur auf steigende oder fallende Flanke? (ergibt doppelte
Bit-Time)
Nach Preamble Verzögerung um 1/2Bit, um in Bitmitte abzutasten,
danach Abtastrate mit Bit-Time?
Falls ja, ist das vom Prinzip her schon richtig, würde ich sagen.
Anmerkung:
Einmaliger DCO-Abgleich auf Uhrenquarz ist 'gefährlich', da DCO bei
Temperatur- und Spannungsänderungen wegdriftet. Aber wenn Du Dich bei
jeder Übertragung erneut auf die Preable synchronisierst, sollte das
kein Problem sein!
das protokoll ist vorgegeben und die UART kann ich leider nicht nehmen.
Preamble ist 8 bit Folge, 10101010.
Capture ist auf beide Flanken und wird über jedes Bit gemittelt.
Abtastung erst mit halber Zeit und dann mit ganzer, auch richtig.
DCO Abgleich sollte wie du schon sagtest kein Prob sein da ich ja nicht
mit einer vorgegebenen Zeit arbeite sondern mich immer synchronisiere.
Bin auch erst mal weg, falls dir aber noch was einfällt bin ich Ohr.
danke erst mal!
Stephan
Zu der Timer-Geschichte:
Durch das Capture hast Du in CCR0 den Wert der Bit-Time... gut!
Diesen Wert erstmal in eine Variable sichern, da wir CCR0 ändern müssen!
Jetzt willst Du 1/2Bit-Time Verzögerung, also:
- CCR0 auf 1/2 Wert setzen
- keinen Interrupt verwenden, sondern pollen:
1
2
TACTL|=TACLR;// Timer nullen
3
TACTL&=~TAIFG;// IRQ-Flag löschen
4
TACTL|=MC0;// up-mode up to CCR0
5
while(!(TACTL&TAIFG));// warten bis Timer = CCR0,
6
// d.h. 1/2Bit-Time ist um.
- CCR0 wieder auf volle Bit-Time setzen
- Timer_A Interrupt verwenden ( nur den!)
1
TACTL|=TACLR;// Timer nullen
2
TACTL&=~TAIFG;// IRQ-Flag löschen
3
TACTL|=TAIE;// Timer_A IRQ freigeben
4
TACTL|=MC0;// up-mode up to CCR0
Der Timer_A läuft jetzt von 0 bis CCR0 und bei jedem Übergang
von CCR0 -> 0 wird der IRQ ausgelöst.
Um Dich vollends zu verwirren (:-)) kannst Du anstatt des Timer_A
IRQ's auch den CCR0-IRQ verwenden. CCR0-IRQ tritt auf, wenn Timer_A =
CCR0 ist. Während der Timer_A IRQ beim Überlauf des Timers nach 0
auftritt (also einen Takt nach CCR0-IRQ). Deshalb sollten man in dieser
Konfiguration auch nicht CCR0 und Timer_A IRQ aktivieren, da beide
fast gleichzeitig auftreten, die IRQ-Behandlung inkl. ISR aber mehr Zeit
benötigt!
B.T.W.: IRQ-Latenz + IRQ-Return = 11 Taktzyklen. Bei 4MHz macht das
2,75µs... vielleicht waren das Deine 3µs ?!
Äh.... ich korrigiere...
Du musst ja bei der ersten 1/2 Bit-Time Verzögerung auch sampeln.
Also hier auch mit Interrupt:
- CCR0 auf 1/2 Wert setzen
- Timer_A Interrupt verwenden ( nur den!)
So im großen und ganzen funktioniert es jetzt. Allerdings bin ich beim
Einlesen von den Interrupts weg. Es dauert einfach zu lange. Jetzt polle
ich auf das Overflow Flag, welches wesentlich schneller ist.
Zu meinen Problemen. Zwei sind es immer noch. Zur berechnung der Bitzeit
ermittele ich den Mittelwert von 6 Bits des Preambles. Dazu wird im
Interrupt (steigende und fallende Flanke) eine Variable aufaddiert. Will
ich jetzt den Timer das erste mal setzen dann muss ich ja den Wert durch
die Anzahl der Messungen teilen. Da dies eine 16bit Division ist dauert
es seine Zeit. Und genau das ist mein Problem. Hier einfach mal der Code
ausschnitt.
Jetzt vielleicht jemand hier der da ne bessere Idee hat. Auf Mittelwert
wollte ich eigentlich nicht verzichten.
Dann mein zweites Problem. Es ist eine einfache Codezeile, viel mehr ein
Funktionsaufruf. Und zwar dieser:
1
init_TIMERA0(0x0200,0xC110,0);
Das gehört zu meiner Init am Anfang des Codes. Wenn ich dieses jetzt
ausführe springt er schon in den Interrupt. Meiner Meinung nach ist der
Timer dann doch noch ausgeschaltet und es sollte kein Interrupt
ausgelöst werden. Hier dann noch die Funktion selber:
1
TACTL=(mode&0xFFCF)|TACLR;// set timer register, clear TAR
2
3
CCTL0=control;// set control register
4
if(control<16384)
5
CCR0=cycles;// set compare register if compare mode selected
6
7
TACTL|=(mode&0x30);// Start/stop TIMER_A
Falls jetzt jemand ne Idee hat wär ich echt dankbar!
greetz
Stephan
Weiß jetzt nicht mehr wieviele Bits Deine Preamble hat, aber Du könntest
anstatt über 6 Bits zu mittel auch 4 oder 8 Bits nehmen. Division ist
dann einfach ein Rechts-Schieben der Summe um 2 bzw. 3 Stellen.
aktivierst Du den Interrupt für CCR0.
Wahrscheinlich läufst Du in den IRQ?!
Ich vermute, Du hast die verschiedenen Timer-Interruptquellen noch nicht
so ganz verstanden. Es gibt zwei Interrupt-Vektoren, die zum Timer_A
gehören:
1
#pragma vector=TIMERA0_VECTOR
2
__interruptvoidCCR0_Interrupt(void)
3
4
#pragma vector=TIMERA1_VECTOR
5
__interruptvoidTMRA_CCR1_CCR2_Interrupt(void)
Der erste gehört ausschließlich zum CCR0.
Der zweite wird von Timer-Overflow, CCR1 und CCR2 gemeinsam benutzt.
Daher muss man hier im TAIV-Register nachschauen, welche Quelle den IRQ
ausgelöst hat!
Ja das weiss ich, oder viel mehr so habe ich das auch schon verstanden.
Nur verstehe ich nicht das ich einen IRQ bekomme wenn ich den Timer noch
gar nicht laufen lasse. Meine Frage ist explicit warum ohne laufenden
Timer der IRQ ausgelöst wird und das nicht nur einmal. Das Programm
springt dann in die CCR0 Interrupt Routine.
OK, ich glaub ich weiß wo der Hund begraben ist... Tricky ;-)
Der CCR0-IRQ wird ausgelöst, wenn der Wert im CCR-Register mit dem
Timer-Wert übereinstimmt. Du löscht den Timer, also TMR=0. Dann setzt Du
CCR0 = cycles... und als cycles hast Du auch 0 übergeben -> Bingo!
Ok das klingt logisch :) Damit wirst du auch recht haben!! Nun ein neues
Problem :) Ich kann das TACCR0 Register aus meiner Funktion heraus nicht
initialisieren. Bei TimerB geht das ohne Probleme und die Funktion ist
die gleiche. Wenn ich dann später das TACCR0 Register setze funzt es
ohne Probleme. Ich habe auch schon die Reihenfolge geändert aber beim
Debuggen stelle ich immer wieder das gleiche fest. Wenn ich den
Assembler Code anschaue sieht es auch richtig aus.
Denke langsam das irgendwie die Kombination vom MSPGCC und Eclipse die
ich benutze nicht funzt.
Aber noch mal meinen C-Code damit das auch nachvollziehbar ist
1
.......
2
init_TIMERA0(0x0200,0xC910,10);
3
// initialise TIMERB, compare mode, SMCLK/1, capture/compare int
wenn ich nun step per step durchgehe (angefangen bei temp_tb = TBCCR0;)
stelle ich fest das temp_tb auch seinen Wert von 40 hat. temp_ta dann
aber nicht 10 sondern 0 ist. Erst beim zweiten mal ist temp_ta den
gesetzten Wert von 1000. Initialisierung ist die gleiche wie oben
beschrieben.
Programmiere wirklich schon länger, allerdings das erste mal MSP, aber
so was ist mir noch nie passiert.
Mfg
Steph
das habe ich eingefügt nachdem du das mit dem comparewert gesagt hast.
Das ganze Übel liegt aber woanders. Ich habe mal angefangen alles
auszukommentieren mit dem Ergebnis das es an der Init vom LCD liegt. Das
witzige ist auch, das der Fehler wenn er einmal aufgetaucht ist, nur
durch ein abtrennen der Spannungsversorgung behoben werden kann. So war
es auch nich einfach das herauszufinden.
Ich bin jetzt mal dran mit dem Debugger eins nach dem anderen
auseinander zu nehmen. Ich meld mich wenn ich den Fehler hab.
Aber riesen dank noch mal das du dich so viel damit beschäftigst!!
mfg
So nach fast 2 Tagen debuggen habe ich es endlich gefunden. Der Fehler
trat leider ja immer nur sporadisch auf und ließ sich auch nur durch
einen Power-on-Reset beheben, wie beschrieben. Das ganze hängt an der
Init der Ports. Dieses erledige ich durch Defines aus der main.h.
Irgendwie scheint es aber Fehler bei den Defines des Port1 zu geben.
Defines wie folgt:
Ich meinte eher das Ver-Unden der Interrupt Flags. Der Befehlt hat
keinen wirklichen Sinn, denn es bleibt alles, wie es ist. Allerdings
sollte man beim manipulieren der Interrupt-Flags genau wissen, was man
macht....
Mein Problem ist immer noch das der TimerA macht was er will. Ich setze
die Register, steppe mich durch mein Programm und nach einem Durchlauf
sind die Register anders gesetzt. Dies obwohl ich nur einmal den TimerA
anfasse. Zum Beispiel wird das TACCR0 Register immer auf 0 gesetzt,
angefasst wird es aber nicht...
Stoppst du den Timer, wenn du eines der Register veränderst? Wenn nicht,
kann das zu seltsamen Verhalten führen. Außerdem muss man aufpassen,
wenn man TACLR setzt (also Timer löschen), wird der Teilerfaktor und die
Richtung des Timers ebenfalls zurückgesetzt. Sowas zu Debuggen ist
schwierig...
Verbesserung das TACCR0 wird nicht immer auf 0 sondern auf einen
beliebigen Wert gesetzt. Das liegt daran das der Int ausgelöst wird wie
ich jetzt gerade herausgefunden habe. Der Timer ist auf Capture gestellt
und während der Init läuft im Hintergrund ein kontinuirlliches Signal.
Da dann immer wieder der Capture ausgelöst wird erhalte ich einen
Capture overflow. Es lag also nicht daran das ich einen compare int
bekommen habe sondern einen capture overflow int.
Jetzt nur eine ganz allgemeine Frage:
Werden die Interruptquellen nicht mit der jeweiligen Peripherie
gestartet?? Der Timer "läuft" ja noch nicht, nur die initialisierung
wurde schon durchgeführt.
greetz
Interrupts werden immer durch das zugehörige Enable-Bit freigeschaltet.
Ob der Timer dabei läuft oder nicht ist wurscht... aber das hatten wir
doch schon ;-)