Archiv verlassen und diese Seite im Standarddesign anzeigen : Timer und Register
Saturas077
28.04.2014, 15:51
Hallo,
Ich hoffe Ihr seit nicht böse, dass ich schon wieder ein Problem habe.
Mein Projekt benötigt die Genauigkeit eines 8Bit Timers, ohne Prescale, über eine Dauer von mindestens 2ms, besser wären 100-200ms.
Das ist mit einem 16 Bit Register, 2 mal 8 Bit, zu lösen, allerdings weiß ich nicht wie ich ein solches benutze. Das was ich im Internet fand sieht wie folgt aus:
.def Adresse = RAMEND
LDI R16, LOW(Adresse)
LDI R17, HIGH(Adresse)
Meine Idee wäre wie bei einem 8 Bit Register, bei jedem überall des Registers ein anderes um 1 zu inkrementieren.
Das Problem ist, dass das Carry-Flag nicht von inc beeinflusst wird. Ich stehe momentan einfach mal wieder auf dem Schlauch wie ich mit solch Doppelregister Problemen am besten umgehe
.def timer0=r16
inc timer0
brbc ISR_END
inc timer0_1
Liebe Grüße
Besserwessi
28.04.2014, 17:00
Wenn man es mit dem Befehl inc machen will, kann man das Zero Flag abfragen, also statt dem brbc (was immer das sein soll) ein BRNE. Die Alternative ist es zum Hochzählen -1 abzuziehen, mit dem SUBI Reg, FF Befehl. Das geht aber nur mit den oberen 16 Registern.
Die wirkliche Schwierigkeit kommt ggf. wenn man die Zahl der Überläufe und den Timerstand zusammenfassen will. Es hängt aber davon ab, was man mit dem Timer machen will - eine externe Zeit messen, oder eine definierte Verzögerung.
Ich muss ehrlich sagen, ich verstehe nicht, wieso man von C auf Assembler wechseln will, aber es sei dir natürlich vergönnt. Ich habe es anders herum gemacht und möchte ehrlich gesagt C nicht mehr missen.
Ich denke, wenn du mit mehr als 8bit rechnen willst, wirst du um den add/adc befehl nicht herum kommen. Dieser nutzt nunmal das Carry-Flag mit.
http://www.mikrocontroller.net/articles/AVR-Tutorial:_Arithmetik8
Hier ein sehr gut geschriebenes Tut dazu, ganz am Ende des Artikels gibts auch einen Link für Arithmetik Operationen mit mehr als 8bit.
Dennis
Searcher
28.04.2014, 17:09
Ich hoffe Ihr seit nicht böse, dass ich schon wieder ein Problem habe.
Nicht, wenn wir dir helfen können;):)
Mein Projekt benötigt die Genauigkeit eines 8Bit Timers, ohne Prescale, über eine Dauer von mindestens 2ms, besser wären 100-200ms.
Das klingt rätselhaft. Eigentlich konfiguriert man den Timer, läßt ihn laufen und läßt ihn über Interrupt melden, wann seine Zeit abgelaufen ist oder so ... Erklär doch ein bißchen mehr, so daß man eine sinnvolle Konfiguration vorschlagen kann.
Auf welchem µC soll das laufen?
Das ist mit einem 16 Bit Register, 2 mal 8 Bit, zu lösen, allerdings weiß ich nicht wie ich ein solches benutze. Das was ich im Internet fand sieht wie folgt aus: .....
Ich verstehe das so, daß Du einen 16Bit Timercounter auslesen möchtest, um eins incrementieren willst und wieder zurückspeichern möchtest.
Schreiben und Lesen eines 16Bit Registers, wie das TCNT1 des Timers 1, das aus TCNT1L und TCNT1H besteht, ist im Datenblatt des ATMega8a beispielhaft so dargestellt. Man beachte die Reihenfolge der Zugriffe.
; Set TCNT1 to 0x01FF
ldi r17,0x01
ldi r16,0xFF
out TCNT1H,r17
out TCNT1L,r16
; Read TCNT1 into r17:r16
in r16,TCNT1L
in r17,TCNT1H
Auf einem ATMega8 geht das ADIW. Also zB so:
in r24,tcnt1L
in r25,tcnt1H
ADIW r24,1
out tcnt1H,r25
out tcnt1L,r24
Gruß
Searcher
avr_racer
28.04.2014, 17:23
Moin Moin aus dem sonnigen Norden,
Pass mit der RAMEND auf weil es schon vergeben ist von Haus aus für den SRAM und du damit eigentlich die letzte Ramadresse auf den Stack legst
ohne vorher irgendwas zu definieren:
ldi SPH,high(Ramend)
ldi SPL,low(Ramend)
Letzte Adresse auf Stackpointer gelelegt.
> Mein Projekt benötigt die Genauigkeit eines 8Bit Timers,
Na denn nutze doch Timer0/2 weil die meistens ein 8bitter sind.
Timer1 ist ein 16bitter und du kannst diesen Timer und dessen Register nicht zu einem 8bitter umschulen im Mode0.
Die einzigen Modes sind die PWM-Modes /CTC-Modes wo du festlegen kannst ob er in 8/9/10bit laufen soll.
> Das ist mit einem 16 Bit Register, 2 mal 8 Bit,
oder soll einfach nur eine Zeitschleife erstellt werden?
Saturas077
28.04.2014, 18:48
Danke für eure netten Antworten.
Ich möchte mit dem Timer nach zwei Gewissen Verzögerungen einen Ausgang Toggeln. Nachdem Wechsel von High auf Low muss die Laufzeit meines Signales gemessen werden. Diese liegt je nach Länge bei 0,5us-2000us. Die Verzögerung sollte sein 100us und etwa zwischen 100ms und 300ms.
Ich brauche eine Möglichst gute Auflösung, da habe ich mir selbst keine Vorgabe gesetzt, sie sollte aber als Richtung zwischen 50ns und 1us liegen. Diese Zeitspektren liegen im Bereich des Timer 0 und Timer 2. Als Test AVR dient der 328P, weil er auf den Arduino passt :P. Ist halt der, der auf Vorrat liegt.
Danke für die Beispiele mit dem RAMEND, genau euer Angesprochenes ist das Problem. Es sind fest zu geordnete Speicheradressen. Ich möchte aber gerne zwei Register von r16-r32 nutzen. Wobei die Pointer Register ja wegfallen.
Besserwessi
28.04.2014, 19:28
Für die genaue Messung einer Zeit hat Timer1 die ICP Funktion - damit sind dann auch 50 ns Auflösung drin (bei 20 MHz Takt). Eine Erklärung dazu gibt es im Wiki-Bereich unter Timer(AVR). Den Zeitkritischen Teil übernimmt dabei die Hardware, und man kommt mit C oder auch BASIC aus.
Der Mega328 hat einen 16 Bit timer - den kann man auch ruhig nutzen (es gibt kein Geld zurück dafür das man die HW nicht nutzt).
avr_racer
28.04.2014, 19:40
> Ich möchte mit dem Timer nach zwei Gewissen Verzögerungen einen Ausgang Toggeln
Den Ausgang kannst du mit Timern direkt togglen entweder im CTC-Modus der immer ein schönes Rechtecksignal _--_--_-- erzeugt oder im (Fast)PWM-Modus der dir dann gleich noch ein geändertes Tastverhältniss liefern kann _------- invers gehts auch -------_
> Nachdem Wechsel von High auf Low muss die Laufzeit meines Signales gemessen werden.
Das Signal über den INTx anlegen, INTx-ISR auslösen lassen in der dann der Timer gestartet wird und wenn man zusätzlich einen Wert in das entsprechende OCRx-Register schreibt kann man den Timer einen OCx-Int auslösen lassen weil die Zeit erreicht ist. Das ganze kann auch mit der ICP des Timers eledigt werden.
Aber was soll das werden irgendwas mit Ultraschall? Zum Verständniss um sich ein Bild des ganzen zu machen vielleicht kommt man denn auch noch auf viel simplere Ideen
Auf einem ATMega8 geht das ADIW.
Das funktioniert aber nur bei den Registern R32 bis R24, und auch jeweils nur bei den Pärchen, soweit ich mich noch erinnere, oder verwechsle ich gerade die Befehle?
Dennis
Searcher
29.04.2014, 06:33
https://www.roboternetz.de/community/images/misc/quote_icon.png Zitat von Searcher https://www.roboternetz.de/community/images/buttons/viewpost-right.png (https://www.roboternetz.de/community/showthread.php?p=598669#post598669)
Auf einem ATMega8 geht das ADIW.
Das funktioniert aber nur bei den Registern R32 bis R24, und auch jeweils nur bei den Pärchen, soweit ich mich noch erinnere, oder verwechsle ich gerade die Befehle?
Nein, stimmt schon. ADIW geht nur bei den oberen Registerpärchen. Besteht da irgendwo ein Widerspruch oder warum fragst Du nach? Ist doch eine kurze Methode um etwas zu einem 16Bit Wert zu addieren:confused:
Ich möchte aber gerne zwei Register von r16-r32 nutzen. Wobei die Pointer Register ja wegfallen.
Warum fallen die Pointerregister weg? r24/r25 sind auch keine der Pointer X, Y, Z.
Gruß
Searcher
Nein, stimmt schon. ADIW geht nur bei den oberen Registerpärchen. Besteht da irgendwo ein Widerspruch oder warum fragst Du nach? Ist doch eine kurze Methode um etwas zu einem 16Bit Wert zu addieren:confused:
Es besteht kein Widerspruch und mit deinem Post erinnerte ich mich auch wieder an diesen Befehl. Ich wollte einfach nur ergänzen, dass dieser Befehl eben nur bei bestimmten Registern funktioniert. Nicht das der TO sich dann wundert, warum es wieder nicht klappt, nur weil er die "falschen" Register nimmt.
Also ansich hattest du den besseren Vorschlag, ich wollte ihn nur ergänzen, war mir aber nicht mehr 100% sicher, ob ich da nicht etwas verwechsle, deswegen mein nachfragen
Dennis
Saturas077
29.04.2014, 16:32
Danke für all Eure Antworten.
Ich wollte vorerst mir die Begriffe im Handbuch und per Suche bei Google durchlesen bevor ich mich melde.
Die Idee mit dem ICP finde ich super, aber durch das lesen über diese Methode ist mir erstmal bewusst geworden, das der Timer 1 die gleiche Genauigkeit hat wie Timer 0.
@avr_racer deine Ausführung mit dem Pulsen per CTC finde ich klingt auch sehr Interessant, allerdings kann ich noch nicht folgen wie das Funktioniert.
Im Handbuch steht dazu folgendes:
C-2|C-1|C|C+1|C+1
Wenn ich das richtig verstanden habe Toggelt er bei jedem Übergang von C-1|C den Eingang.
Das würde aber doch bedeutet, das der Eingang 500ms an und auch aus wäre, außer ich würde C nachdem Pegel ändern. Sehe ich das falsch?
Ich laß die Frage was das für ein Projekt ist.
Das Projekt ist eine Positionsbestimmung per Töne. Es wurde vor einigen Monaten per Arduino gemacht. Funktionieren tut es, allerdings sind die Offsetzeiten zu hoch.
Eigentlich ist das Projekt abgeschlossen, ich benutze es als vorbild zum lernen von Assembler und den Umgang von AVR.
http://www.mikrocontroller.net/articles/FAQ#Timer
Sehr gut die üblichen Timer-Modi beschrieben
Dennis
avr_racer
29.04.2014, 17:37
Hallo,
> Das würde aber doch bedeutet, das der Eingang 500ms an und auch aus wäre,
Das ist korrekt. Deshalb ja die Anspielung auf den PWM-Mode da kannst du die ON/OFF-Zeit variabel gestalten soll heißen bei z.B.:10Khz = 100µs = 20/100µs Off __ und 80/100µs high
----------.
> außer ich würde C nachdem Pegel ändern.
Was ist C ? Du meinst den Timer ?
> Im Handbuch steht dazu folgendes:
Ich gehe davon aus das du Arduino nutzt, mit diesen hab ich bisher nichts zu tun gehabt. Nutze das Datenblatt des entsprechenden µC weil
> ich benutze es als vorbild zum lernen von Assembler und den Umgang von AVR.
nur das Datenblatt ist das Handbuch für den Controller dort bekommt man vielleicht die ein oder andere Info mehr.
Saturas077
29.04.2014, 18:52
Nein, das hast du falsch verstanden. Ich rede über das ATMEL_Atmega328P-PU Dokument von der Hauptseite. Angesprochene seite ist die 130. Dort wird es OCRnx statt C genannt.
avr_racer
29.04.2014, 19:49
ick find dit irgendwie net :confused: auf Seite 130 sind Diagramme und die Rede von OCR-1, OCR+1 usw
hier der Link
http://www.atmel.com/Images/Atmel-8271-8-bit-AVR-Microcontroller-ATmega48A-48PA-88A-88PA-168A-168PA-328-328P_datasheet.pdf
Achtung wenn du die Includedatei des µC öffnest dann siehst du das dort an einigen Stellen MOMERY MAPPED steht diese Adressen sind nur mit LDS/STS zu erreichen.
aber vielleicht hilft der Code ein wenig weiter
rcall mode2_T0_init
rcall prescaler_T0_on
start:
rjmp start
mode2_T0_init:;CTC_MODE OCRx update sofort oder im INT
sbi ddrd,6 ;output aktivieren wenn Toggeln am PIND6=OCR0A erwünscht
ldi temp0,$80 ;Toggeln oder Clr/Set aktiv muss OCR0A-Register beachtet werden um
out OCR0A,temp0 ;____--- oder _------ zu erreichen
;mit OCRyx wird die Frequenz festgelegt des Rechtecksignales
lds temp0,TIMSK0
ori temp0,(0<<OCIE0B||0<<OCIE0A|0<<TOIE0) ;INTs aktivieren
sts TIMSK0,temp0
ldi temp0,(0<<COM0B1|0<<COM0B0|0<<COM0A1|1<<COM0A0|1<<WGM01|0<<WGM00) ;zum Toggeln COMyx Einstellungen beachten!!!!!!!!!
out TCCR0A,temp0 ;
ldi temp0,(0<<FOC0A|0<<FOC0B|0<<WGM02)
out TCCR0B,temp0
ret
prescaler_T0_on:
in temp0,TCCR0B
ori temp0,(0<<CS02|0<<CS01|1<<CS00) ;schmeißt den counter
out TCCR0B,temp0
sei
ret
OC0A_int:
in temp0,OCR0A
out OCR0A,temp0
reti
Saturas077
02.05.2014, 11:16
Danke für dein Beispiel.
In deinem Beispiel steht, das du mit OCR0A die Frequenz einstellst. In konnte mir nicht vorstellen wie du ein _------ Signal mit einem Festen Wert erzeugen möchtest. In der Doku habe ich dazu folgenden Satz gefunden:
If the interrupt is enabled, the interrupt handler routine can be used for updating the TOP value
Zur Erklärung, der TOP Value ist der Wert an dem Der Vergleich zwischen OCR0A und TCNT statt findet und der Wert wieder auf 0 gesetzt wird. Bedeutet ich setze in der ISR einfach einen neuen Wert bei OCR0A ein?
Wieso nimmst du eigentlich den ORI befehl?
Performs the logical OR between the contents of register Rd and a constant and places the result in the destination register Rd.
Quelle: http://www.atmel.no/webdoc/avrassembler/avrassembler.wb_ORI.html
Übriegens, dein Link führt auf die Doku, die ich weiter oben meinte.
avr_racer
02.05.2014, 14:55
> Zur Erklärung, der TOP Value ist der Wert an dem Der Vergleich zwischen OCR0A und TCNT statt findet und der Wert wieder auf 0 gesetzt wird. Bedeutet ich setze in der ISR einfach einen > neuen Wert bei OCR0A ein?
jep so siehts aus. Aber solltest du den Mode ändern denn guck in die Tabelle welches Register als Vergleich herangezogen wird. Der Quellcode ist nur für den CTC-Modus (Autoreload) heißt, wenn OCR0A geändert wird, änderst du nur die Frequenz nicht aber das ON/OFF Verhältniss. Dafür müsstest du die FAST-PWM nutzen.
> Wieso nimmst du eigentlich den ORI befehl?
Der OR-Befehl bezieht sich direkt auf 2 Register die geodert werden, bei dem ORI-Befehl wird eine Konstante mit dem Register geodert. Der Klimzug mit dem ORI-Befehl habe ich nur deshalb gemacht wenn man den Timer noch andersweitig nutzt oder verändern will also kompatibel bleiben möchte.
> Übriegens, dein Link führt auf die Doku, die ich weiter oben meinte.
Check
Gruß avr_racer
Saturas077
02.05.2014, 18:23
Also erste Überlegungen:
Timer 0 kann mit einem Prescale von 1028 eine maximal Zeit von 13,15 mS erreichen bis zum Überllaufen
Timer 1 schafft eine Zeit von 3,36s
Meine Idee ist also, den Timer 0 auf einen Prescale von 8 einzustellen, dann ist die Laufzeit zum Overflow 102us. Das Vergleichs Register OCRA0 , stelle ich dann auf 0x64(100);
In der Routine des Vergleichs Vergleichs Interrupts, muss ich dann prüfen ob der Pegel High oder Low war und wie häufig es Überlaufe gab.
ISR_Compare:
inc r16 ; Zähle Überläufe
sbic PORTB, 0 ; Prüfe ob Ausgang LOW
jmp Pegel_Low
sbis PORTB, 1 ; Überprüfe ob Ausgang High
jmp Pegel_High ; Wenn nicht setze Ihn High
reti
Pegel_Low:
cpi r16, 0x05 ; Überprüfe ob Wert 5;
brne reti
ldi r16,0x00
cbi PORTB,0
reti
Pegel_High:
cpi r16, 0x01 ; Überprüfe ob Wert 1;
brne reti
ldi r16,0x00
sbi PORTB,0
reti
Nur mal als Idee.
Timer 1 würde ich als ICP fahren und damit die Messwert Laufzeit erfassen.
avr_racer
02.05.2014, 19:27
> Timer 0 kann mit einem Prescale von 1028 eine maximal Zeit von 13,15 mS erreichen bis zum Überllaufen
> Timer 1 schafft eine Zeit von 3,36s
ACHTUNG der Teiler ist max 1024 und bits max. 255bits machen 13,05...ms !!!!!! in längster Ausführung
> Meine Idee ist also, den Timer 0 auf einen Prescale von 8 einzustellen, dann ist die Laufzeit zum Overflow 102us. Das Vergleichs Register OCRA0 , stelle ich dann auf 0x64(100);
jo 8 ist gut und wenn du OCR0A mit 250 ($FA) beschreibts hast du alle 100µs eine OC0A Interrupt der ausgelöst wird und könntest diesen nutzen.
Warum auf 100? denn wird doch alle 40µs einer OVFL ausgelöst oder verstehe ich das jetzt falsch?
Saturas077
04.05.2014, 12:41
Du hast natürlich recht, ich war bei einem anderen Wert in Gedanken.
Ich habe jetzt einen Anfang gemacht und möchte das mit dem USART verbinden, allerdings klappt das nicht so wie ich das möchte.
Die Low Time ist jetzt auf 10ms eingestellt, wie könnte ich das erweitern auf 100ms mit einem 8 Bit Timer?
Es wäre lieb wenn Ihr einmal drüber schaut:
.def temp = r16
.def value = r17
.def timer0 = r20
.def timer1_low = r21
.def timer1_high = r22
/* BEGINN DES HAUPTPROGRAMMES*/
Reset:
ldi temp, HIGH(RAMEND) ; Stackpointer
out SPH, temp
ldi temp, LOW(RAMEND)
out SPL, temp
ldi temp, ( (1<<PD2) | (1<<PD3) ) ; Interrupt Pins auf Eingang, der Rest bleibt als Ausgang
out DDRD, temp
ldi temp, 0xFF ; PORTB wird als Ausgang gesetzt
out DDRB, temp
ldi temp, 0x00
out PORTD, temp ; Alle Pins auf 0 setzen (Die beiden Eingänge bekommen keinen Pull-Up)
UART_Reset:
ldi temp, HIGH(UBRR_VAL)
sts UBRR0H, temp
ldi temp, LOW(UBRR_VAL)
sts UBRR0L, temp
ldi temp, ( (1<<UMSEL00) | (1<<UCSZ01) | (1<<UCSZ00) ) ; synchron mit 8 Bit.
sts UCSR0C, temp
ldi temp, (1<<TXEN0)
sts UCSR0B, temp
Timer0_Reset:
ldi temp, (1<<CS01) ; setze Vorteiler auf 8
out TCCR0B, temp
ldi temp, (1<<COM0A1) ; der Ausgang wird immer beim Oberflow
out TCCR0A, temp ; immer auf LOW gesetzt
ldi temp, 0xFA ; 250 Schritte entsprechen genau 100us
out OCR0A, temp
ldi temp, (1<<OCIE0A) ; aktiviere Compare-Overflow Interrupt
sts TIMSK0, temp
Timer1_Reset:
ldi temp, (1<<ICES1)
sts TIMSK1, temp
Loop:
jmp Loop
isUDRclear:
ldi temp, UCSR0A
sbis temp, 5 ; Überprüfe ob das UDR Register LEER ist !!! Hier ist der Fehler :" Invalid number"
rjmp isUDRclear ; Wenn nicht bleibt in einer Schleife bis es so ist.
sts UDR0, value
ret nop
Result_Output:
mov value, timer1_low ; Timerwert auf den USART geben
rcall isUDRclear
mov value, timer1_high ; Timerwert auf den USART geben
rcall isUDRclear
rcall sync_0
jmp Loop
Sound_On:
cbi PORTD, 6
reti nop
TIM0_COM:
/*Die Pegel auf LOW setzen brauch nicht implementiert werden,
da das von der HArdware gemacht wird*/
inc timer0
cpi timer0, 0x64 ; Damit wird der High Timer auf 10ms festgelegt.
breq Sound_On
reti nop
TIM1_CAP:
/*Hardware speichert Werte im RAM*/
ldi timer1_low, TCNT1L
ldi timer1_high, TCNT1H
reti nop
In der Doku steht:
in r16, UCSRnA
sbrs r16, UDREn
Wenn das so eingetippt wird, ist das UCSR0A Register nicht in der Range.
Ich muss mir noch etwas einfallen lassen, wie ich die Ausgabe zum PC mache, ohne das interrupt zu verlängern und ohne es im Main Loop die ganze Zeit ausgeben zu lassen
Liebe Grüße
avr_racer
04.05.2014, 14:45
Ich glaub ich habe dir gestern falsche Daten gegeben in meiner Tabelle verlesen und habe die Gleichung für den FAST-PWM-Mode genutzt (Asche auf mein Haupt)
So Korrektur für den CTC-Mode bei 20Mhz für 100µs: Teiler = 8; OCR0A = $7c (124dez) = 100µs
CTC-Mode bei 20Mhz für 100µs: Teiler = 64; OCR0A = $0f (15dez) = 104µs
Tja und mit 100ms im CTC-Mode siehts schlecht aus.
jetzt zum Programm...
Versuche mal die ganze Initialisierung in kleine Unterprogramme zu packen
z.B.:
init_stack:
ldi temp, HIGH(RAMEND) ; Stackpointer
out SPH, temp
ldi temp, LOW(RAMEND)
out SPL, temp
rcall UART_Reset
loop: jmp loop
UART_Reset:
....
.....
ret
UART_Reset: ich hoffe du hast UBRR_VAL irgendwo definiert denn die Angabe fehlt in deinem Post.
Timer0_Reset: gut wäre es den Timer erst zu starten wenn du alle Timerinitalisierungen erledigt hast und du bist auch nicht m CTC-Mode weil WGM01 nicht gesetzt ist.
isUDRclear: sbis temp, 5 dieser Befehl bezieht sich auf die PORTS, du möchtest aber ein Register nutzen dann nutze SBRS ......
isUDRclear:
lds temp, UCSR0A ; Hier ist der Fehler LDS weil MOMERY MAPPED
sbrs temp, UDRE0 ; Überprüfe ob das UDR Register LEER ist !!! Hier ist der Fehler :" Invalid number"
rjmp isUDRclear ; Wenn nicht bleibt in einer Schleife bis es so ist.
sts UDR0, value
ret nop
> Wenn das so eingetippt wird, ist das UCSR0A Register nicht in der Range.
in r16, UCSRnA
sbrs r16, UDREn
In einen meiner Posts hatte ich es geschrieben, schau dir bitte mal die Inlcudedatei an, da steht bei einigen Registern MOMERY MAPPED und diese Register
werden mit LDS r16,UCSR0A geladen und mit STS gespeichert, einfach mal reinschauen
> Ich muss mir noch etwas einfallen lassen, wie ich die Ausgabe zum PC mache, ohne das interrupt zu verlängern und ohne es im Main Loop die ganze Zeit ausgeben zu
> lassen
Naja in dem du es Anforderst, du sendest über den PC ein 'A' und vergleichst es im µC wenn es stimmt machste ne Ausgabe.
hier mal überarbeitet
ldi temp, HIGH(RAMEND) ; Stackpointer
out SPH, temp
ldi temp, LOW(RAMEND)
out SPL, temp
ldi temp, ( (1<<PD2) | (1<<PD3) ) ; Interrupt Pins auf Eingang, der Rest bleibt als Ausgang
out DDRD, temp
ldi temp, 0xFF ; PORTB wird als Ausgang gesetzt
out DDRB, temp
ldi temp, 0x00
out PORTD, temp ; Alle Pins auf 0 setzen (Die beiden Eingänge bekommen keinen Pull-Up)
rcall UART_Reset
rcall Timer0_Reset
rcall Timer1_Reset
sei ;sonst wirds nix mit den INTS das ist die Globale Freigabe
Loop:
jmp Loop
Result_Output:
mov value, timer1_low ; Timerwert auf den USART geben
rcall isUDRclear
mov value, timer1_high ; Timerwert auf den USART geben
rcall isUDRclear
; rcall sync_0
jmp Loop
;***Init Uart
UART_Reset:
ldi temp, HIGH(UBRR_VAL)
sts UBRR0H, temp
ldi temp, LOW(UBRR_VAL)
sts UBRR0L, temp
ldi temp, ( (1<<UMSEL00) | (1<<UCSZ01) | (1<<UCSZ00) ) ; synchron mit 8 Bit.
sts UCSR0C, temp
ldi temp, (1<<TXEN0)
sts UCSR0B, temp
ret
isUDRclear:
lds temp, UCSR0A
sbrs temp, UDRE0 ; Überprüfe ob das UDR Register LEER ist !!! Hier ist der Fehler :" Invalid number"
rjmp isUDRclear ; Wenn nicht bleibt in einer Schleife bis es so ist.
sts UDR0, value
ret
;****Iint_T0
Timer0_Reset:
ldi temp, (1<<COM0A1|1<<WGM01) ; der Ausgang wird immer beim Oberflow
out TCCR0A, temp ; immer auf LOW gesetzt
ldi temp, $7c ; 250 Schritte entsprechen genau 100us
out OCR0A, temp
ldi temp, (1<<OCIE0A) ; aktiviere Compare-Overflow Interrupt
sts TIMSK0, temp
ldi temp, (0<<CS02|1<<CS01|0<<CS00) ; setze Vorteiler auf 8
out TCCR0B, temp
ret
;***Init_T1
Timer1_Reset:
ldi temp, (1<<ICES1)
sts TIMSK1, temp
ret
;**********UP´s INT´s*************
Sound_On:
cbi PORTD, 6
reti
TIM0_COM:
/*Die Pegel auf LOW setzen brauch nicht implementiert werden,
da das von der HArdware gemacht wird*/
inc timer0
cpi timer0, 0x64 ; Damit wird der High Timer auf 10ms festgelegt.
breq Sound_On
reti
TIM1_CAP:
/*Hardware speichert Werte im RAM------>> nein Das ist nicht der RAM ganz normaler RegisterBereich wenn mit .equ timer1_low = $0100 festlegen*/
ldi timer1_low, TCNT1L
ldi timer1_high, TCNT1H
reti
Nachtrag: Schon mal überlegt auf´s AVR-Studio 4.19 zu wechseln ? Das 6er ist so ziemlich noch nicht das gelbe vom Ei. Funktioniert auch mit MK2.
Saturas077
04.05.2014, 16:23
Danke =)
Ich habe das Programm jetzt etwas getestet. Es funktioniert nicht wie es soll. :D Wann ist das schon der Fall... Wir haben COM0A1 gesetzte, laut Tabelle sollte damit PD5 auf 0 gesetzt werden bei einem Überlauf über OCR0A. Das tut es aber nicht.
Dein Vorschlag mit dem Senden der werte erst auf Anfrage finde ich nicht schlecht, aber er sollte es möglichst selbst ständig tun. Beim Arduino hatte ich in der ISR einen Booleanischen Wert genommen der auf True und in der Ausgabe auf false gesetzt wird. Bei Assembler ein komplettes Register wegen einem Bit zu verschwendet scheint mir etwas krass.
avr_racer
04.05.2014, 17:01
> Wir haben COM0A1 gesetzte, laut Tabelle sollte damit PD5 auf 0 gesetzt werden bei einem Überlauf über OCR0A.
warum sollte PD5 gesetzt sein? Schau mal in der PinConfiguration im Datenblatt nach mit was PD5/PD6 verbunden ist!!!!!!!
> aber er sollte es möglichst selbst ständig tun
UART initalisieren und das senden wird dann selbstständig gemacht, Daten ins UDR schreiben und die HW macht den Rest
> Bei Assembler ein komplettes Register wegen einem Bit zu verschwendet scheint mir etwas krass.
Trugschluß!!!!!!!!! auch in Hochsprachen wird eine entsprechende Byteanzahl genutzt und wenn das 'A' empfangen wird ist das schon ein komplettes Byte ASCII-Tabelle anschauen.
ist jetzt ein anderes Thema aber schau mal hier da is was vorbereitet auf nem ATmega8. sonst anpassen auf dein µc
https://www.roboternetz.de/community/threads/64609-AtMega8-kleine-Bibliothek
Saturas077
04.05.2014, 20:32
Entschuldigem das war ein Tippfehler. Es sollte PD6 lautet.
Das lässt das Datenblatt verlauten:
(PCINT22/OC0A/AIN0) PD6
Daraus entnehme ich, das es der richtige Pin ist, allerdings cleared er nicht
Lässt sich der Usart eigentlich im Studio simulieren, hast du damit Erfahrung?
Wenn ja, wie bekomme ich meine Timer, die wir im letzten Code implementiert haben angezeigt. Ich kenne nur den Hauseigenen Terminal, aber dieser muss immer mit einer Com Stelle verbunden sein.
avr_racer
04.05.2014, 21:16
ok check
Das mit dem Clearen is mir in der 4erVersion auch schon aufgefallen Toggeln geht. Bei OVFL das setzen geht auch aber clearen scheint wohl ein Problem zu sein. Kam schon öfter in der SW vor das es falsch dargestellt wurde aber in der HW richtig funktioniert hat.
> Lässt sich der Usart eigentlich im Studio simulieren, hast du damit Erfahrung?
jep lässt sich simulieren nur wenn man Daten empfangen will, muss man beim pollen, dort wo die Daten ausgelesen werden sollen, erst quasi Daten reinschreiben im UDR per HAND setzen, RX Flag setzen damit Daten ausgelesen werden können.
> Wenn ja, wie bekomme ich meine Timer, die wir im letzten Code implementiert haben angezeigt
Na installieren auf ASM, Simulator wählen, µC wählen und loslegen dann siehst du das alles im I/O-View
> Ich kenne nur den Hauseigenen Terminal, aber dieser muss immer mit einer Com Stelle verbunden sein.
??? welches? wenn die UART denn genutzt wird muss Hardwareseitig ne Connection bestehen aber nicht für die Simulation. In der Simulation wie gesagt muss das UDR per Hand gesetzt werden und das RX-Flag damit beim nächsten Takt die Daten ins Register geschoben werden.
Bezug ist die 4er Version, die 6er auch mal angetestet aber da funzt das ein oder andere noch nicht so wirklich, für ASM reicht die 4er dicke aus.
Saturas077
05.05.2014, 09:17
In der HW scheint das clearen auch nicht zu funktionieren, das Messgerät zeigt nur 5 Volt an.
Searcher
05.05.2014, 10:01
Hallo Saturas077,
wie sieht denn jetzt das komplette Programm aus und mit welchem Takt wird der µC betrieben. In den letzten posts sehe ich zB. überhaupt keine Initialisierung der Interrupt Vektoren mehr.
Gruß
Searcher
Saturas077
05.05.2014, 11:39
In diesem Code setze ich die Pins Manuell, aber der Pin PD6 sollte eigentlich automatisch nach jedem Compare Interrupt auf 0 gesetzt werden.
.include "m328Pdef.inc"
/* Mikrokontroller.net; Persönliche Änderung auf 20MHz */
.equ F_CPU = 20000000 ; Frequenz
.equ BAUD = 9600 ; Baudrate
; Berechnungen
.equ UBRR_VAL = ((F_CPU+BAUD*8)/(BAUD*16)-1) ; clever runden
.equ BAUD_REAL = (F_CPU/(16*(UBRR_VAL+1))) ; Reale Baudrate
.equ BAUD_ERROR = ((BAUD_REAL*1000)/BAUD-1000) ; Fehler in Promille
.if ((BAUD_ERROR>10) || (BAUD_ERROR<-10)) ; max. +/-10 Promille Fehler
.error "Systematischer Fehler der Baudrate grösser 1 Prozent und damit zu hoch!"
.endif
.org 0x0000
jmp RESET ; Reset Handler
reti nop ; IRQ0 Handler
reti nop ; IRQ1 Handler
reti nop; PCINT0 Handler
reti nop; PCINT1 Handler
reti nop ; PCINT2 Handler
reti nop ; Watchdog Timer Handler
reti nop ; Timer2 Compare A Handler
reti nop ; Timer2 Compare B Handler
reti nop ; Timer2 Overflow Handler
jmp TIM1_CAP ; Timer1 Capture Handler
reti nop ; Timer1 Compare A Handler
reti nop ; Timer1 Compare B Handler
reti nop ; Timer1 Overflow Handler
jmp TIM0_COM ; Timer0 Compare A Handler
reti nop ; Timer0 Compare B Handler
reti nop ; Timer0 Overflow Handler
reti nop ; SPI Transfer Complete Handler
reti nop ; USART, RX Complete Handler
reti nop ; USART, UDR Empty Handler
reti nop ; USART, TX Complete Handler
.def temp = r16
.def value = r17
.def status = r18
.def timer0 = r20
.def timer1_low = r21
.def timer1_high = r22
/* BEGINN DES HAUPTPROGRAMMES*/
Reset:
ldi temp, HIGH(RAMEND) ; Stackpointer
out SPH, temp
ldi temp, LOW(RAMEND)
out SPL, temp
ldi temp, 0x11110011 ; Die Interrupt sollen als Eingang bleiben
out DDRD, temp
ldi temp, 0xFF ; PORTB wird als Ausgang gesetzt
out DDRB, temp
ldi temp, 0x00
out PORTD, temp ; Alle Pins auf 0 setzen (Die beiden Eingänge bekommen keinen Pull-Up)
rcall UART_Reset
rcall Timer0_Reset
rcall Timer1_Reset
sei
Loop:
;sbic PORTD, 6
;jmp Result_Output
jmp Loop
isUDRclear:
ldi temp, UCSR0A
sbrs temp, 6 ; Überprüfe ob das UDR Register LEER ist
rjmp isUDRclear ; Wenn nicht bleibt in einer Schleife bis es so ist.
sts UDR0, value
ret
Result_Output:
mov value, timer1_low ; Timerwert auf den USART geben
rcall isUDRclear
mov value, timer1_high ; Timerwert auf den USART geben
rcall isUDRclear
rcall sync_0
jmp Loop
/* ISR ROUTINE */
TIM0_COM:
/*Die Pegel auf LOW setzen brauch nicht implementiert werden,
da das von der HArdware gemacht wird*/
inc timer0
cpi timer0, 0x64 ; Damit wird der High Timer auf 10ms festgelegt.
breq Sound_On
cpi timer0, 0x01
breq Sound_Off
reti
TIM1_CAP:
ldi timer1_low, TCNT1L
ldi timer1_high, TCNT1H
reti
Sound_On:
ldi temp, (1<<PD6)
out PORTD, temp
ldi timer0, 0x00 ; setze Timer zurück
reti
Sound_Off:
sbis PORTD, 6
reti
ldi temp, (0<<PD6)
out PORTD, temp
ldi timer0, 0x00
reti
/* RESETS FUNCTION*/
UART_Reset:
ldi temp, HIGH(UBRR_VAL)
sts UBRR0H, temp
ldi temp, LOW(UBRR_VAL)
sts UBRR0L, temp
ldi temp, ( (1<<UMSEL00) | (1<<UCSZ01) | (1<<UCSZ00) ) ; synchron mit 8 Bit.
sts UCSR0C, temp
ldi temp, (1<<TXEN0)
sts UCSR0B, temp
ret
Timer0_Reset:
ldi temp, ( (1<<COM0A1) | (0<<COM0A0) | (0<<WGM02) | (1<<WGM01) | (0<<WGM00) ) ; der Ausgang wird immer beim Oberflow
out TCCR0A, temp ; immer auf LOW gesetzt
ldi temp, 0xFA ; 250 Schritte entsprechen genau 100us FA
out OCR0A, temp
ldi temp, (1<<OCIE0A) ; aktiviere Compare-Overflow Interrupt
sts TIMSK0, temp
ldi temp, (0<<CS02|1<<CS01|0<<CS00) ; setze Vorteiler auf 8
out TCCR0B, temp
ret
Timer1_Reset:
ldi temp, (1<<ICES1)
sts TIMSK1, temp
ret
sync_0:
ldi r25,0x03
sync_1:
ldi r26,0x03
sync_loop:
dec r26
brne sync_loop
dec r25
brne sync_1
ret
Searcher
05.05.2014, 12:49
In diesem Code setze ich die Pins Manuell, aber der Pin PD6 sollte eigentlich automatisch nach jedem Compare Interrupt auf 0 gesetzt werden.
Wenn der Code funktioniert, wie Du es möchtest, kann man den Fehler nicht suchen. PD6 sollte nach Deinen Settings und Datenblatt wirklich wieder nach jedem Compareinterrupt auf 0 gesetzt werden. Nur, wie wird er auf 1 gesetzt ohne die Sound_on Sound_off?
Sound_Off:
sbis PORTD, 6
Abfrage des Pin Zustandes geschieht normalerweise über das PIND Register außer es absichtlich so gewollt. Wenn über die COMA Bits die OC0A Funktion aktiviert ist, empfiehlt sich das besonders, da der Pin dann nicht mehr auf Zuweisungen über das PORTD Register reagiert. (Aussage etwas wackelig, da ich das jetzt nicht ausprobiert habe und meine, das noch im Gedächtnis zu haben.)
Saturas077
05.05.2014, 13:36
Es sollte nur Sound_On noch vorhanden sein. Sound_Off sollte dementsprechend von der CTC Routine abgenommen werden und zwar in jedem Compare Interrupt
Searcher
05.05.2014, 15:10
Sound_On:
ldi temp, (1<<PD6)
out PORTD, temp
ldi timer0, 0x00 ; setze Timer zurück
reti
Das "out PORTD, temp" kann keine Wirkung auf PD6 haben, da OC0A aktiviert ist (nach Datenblatt "15.6 Compare Match Output Unit"). Ich frag mich immer noch, wie PD6 auf high kommt?
Saturas077
05.05.2014, 15:41
Deine Aussage finde ich sehr Interessant, da dass das einzige war, was funktioniert hat.
Also, ohne:
Sound_Off:
sbis PORTD, 6
reti
ldi temp, (0<<PD6)
out PORTD, temp
ldi timer0, 0x00
reti
Ist der Pegel immer High auf dem Oszi. (In der Simulation in 6.2 ebenfalls)
Erst durch den Absatz wird es ein Toggeln. Es spielt keine Rolle wie ich COMxy einstelle.
Zu deiner exakten Frage wie der pegel High wird. Du hast den Übeltäter selbst gepostet:
Sound_On:
ldi temp, (1<<PD6)
out PORTD, temp
ldi timer0, 0x00 ; setze Timer zurück
reti
Ich hoffe wir finden den Fehler, ansonsten muss ich die CTC Funktion erstmal ohne das zurücksetzen akzeptieren.
Viele Grüße
Searcher
05.05.2014, 15:49
:confused: Ich blick jetzt nich mehr durch :confused: und muß erstmal in mich gehen :-)
Bin mir ziemlich sicher, daß wenn OC0A aktiviert ist, der PD6 nicht mehr auf Ausgaben ins PORTD.6 reagiert. Allerdings habe ich keinen Mega328p kann das aber später mal mit Mega88A ausprobieren. *Rätsel*
Gruß
Searcher
Searcher
05.05.2014, 20:40
Also ich habe es nicht geschafft PD6 über PORTD.6 anzusprechen, wenn OC0A über die COM0Ax Bits aktiviert war.
Überhaupt hat nur das Setzen des COM0A0 Bits zum toggeln bei Comparematch Änderungen an PD6 hervorgerufen; eben ein toggeln. Die anderen Bitkombinationen nach Datenblatt haben OC0A entweder konstant auf low oder high gelegt. Keine Ahnung welchen Sinn die beiden anderen Bitkombinationen im CTC Modus haben.
ldi temp, 0x11110011 ; Die Interrupt sollen als Eingang bleiben
out DDRD, temp
Wie wird DDRD hiermit überhaupt konfiguriert? Es müßte doch "ldi temp , 0xF3" heißen!
Was ist den die Aufgabe des Timer0? Alle 10ms einen Puls (high oder low, wie lang?) auf PD6 generieren?
Eventuell müßte eine andere Lösung als CTC finden.
Gruß
Searcher
Saturas077
06.05.2014, 18:38
Danke für eure mühen. Ich habe es jetzt erstmal manuel gelöst.
Powered by vBulletin® Version 4.2.5 Copyright ©2024 Adduco Digital e.K. und vBulletin Solutions, Inc. Alle Rechte vorbehalten.