Hallo Leute,
ich bin noch relativ neu in Sachen uC Programmieren und habe ein kleines
Problemchen mit dem ich nicht klar komme. Etwas unten seht ihr den Code
den ich geschrieben habe, allerdings funct er nicht so wie er funcen
sollte. Der uC dreht nämlich durch und spring an Stellen wo er nichts zu
suchen hat. Seit zwei Tagen versuche ich das auf die Reihe zu kriegen
aber ohne Erfolg. Ich habe zwei verschiedene uC(ATmega16) ausprobiert
und beide machen dasselbe. Also ich denke, irgendwas habe ich nicht
richtig programmiert oder vergessen, habe aber keine Ahnung was.
Der Code sollte folgendes machen: ich schicke im über UART zeichen und
er gibt es seriel weiter und erzeung einen clock(soll einen fpga
proggen).
Was macht also der uC? Diese Version des Codes macht z.B das, dass es
in die Methode do_done springt aus der Main Methode(dort wo so viele
rjmp write sind), wobei PIND7 ist direkt mit Masse verbunden. Wenn ich
dort nicht nur einmal rjmp write hatte, ging er zur end Marke wo er
nirgendswie dürfte kommen können. Ich habe auch andere Versionen
ausprobiert und hatte immer irgendwelche ähnliche Ergebnisse. Daher
denke ich mir, dass es Fehler in meinem Code sind.
Wenn sich also jeman den Code anschauen würde und mir ein Paar
Vorschläge geben würde, wie ich meinen ProgStyle verbessern kann, wäre
ich ihm dankbar.
danke
patrik
.include "m16def.inc"
;.dseg Sequenz
;-----------------------------------------------------------------------
----------------------------------------
.dseg
; end .dseg Sequenz
;-----------------------------------------------------------------------
----------------------------------------
;.cseg Sequenz
;-----------------------------------------------------------------------
----------------------------------------
.cseg
; end .cseg Sequenz
;-----------------------------------------------------------------------
----------------------------------------
;.equ Sequenz
;-----------------------------------------------------------------------
----------------------------------------
.equ CLOCK = 16000000 ; eigene Frequenz
.equ BAUD = 9600 ; die Baudrate mit der man kommunizieren
will
.equ UBRRVAL = CLOCK/(BAUD*16)-1
.equ cCTS = 4 ; CTS Pin
;.equ cRTS = 5 ; RTS Pin
.equ cINIT_B = 0
.equ STATUS_INIT_B = (1 << cINIT_B)
.equ cPROG_B = 1
.equ STATUS_PROG_B = (1 << cPROG_B)
.equ cDONE = 2
.equ STATUS_DONE = (1 << cDONE)
.equ cSTARTED = 3
.equ STATUS_STARTED = (1 << cSTARTED)
.equ cCRC_ERROR = 4
.equ STATUS_CRC_ERROR = (1 << cCRC_ERROR)
.equ cTX = 5
.equ TX = (1 << cTX)
.equ cNEW_CHAR = 6 ; signalisiert, dass ein Zeichen im
eingelesen wurde
.equ NEW_CHAR = (1 << cNEW_CHAR)
; A0 -> CCLK nur output
; A1 -> DATA nur output
; A2 -> PROG_B nur output
.equ CCLK = 0
.equ DATA = 1
.equ PROG_B = 2
; B2 -> INIT_B nur input/output
; B3 -> DONE nur input
.equ INIT_B = 2
.equ DONE = 3
.def temp = r16
.def DATA_REG = r17
.def STATUS_REGISTER = r18
; end .equ Sequenz
;-----------------------------------------------------------------------
----------------------------------------
.org 0x00
rjmp init
.org INT0addr ; Zuweisung der grossen/kleinen Ziffer an rpm bzw.
speed
rjmp int_init_b
.org INT1addr
rjmp int_done
.org OC2addr
reti
.org OVF2addr ;Overflow2 Interrupt Vector Address
reti
.org ICP1addr ;Input Capture1 Interrupt Vector Address
reti
.org OC1Aaddr ;Output Compare1A Interrupt Vector Address
reti
.org OC1Baddr ;Output Compare1B Interrupt Vector Address
reti
.org OVF1addr ;Interruptvektor für TimerOverFlow1
reti
.org OVF0addr ;Interruptvektor für TimerOverFlow0
reti
.org SPIaddr ;SPI Interrupt Vector Address
reti
.org URXCaddr ;Interruptvektor für UART-Empfang
rjmp int_rxc
.org UDREaddr ;UART Data Register Empty Interrupt Vector Address
reti
.org UTXCaddr ;UART Transmit Complete Interrupt Vector Address
rjmp utx_complete
.org ADCCaddr ;Interruptvektor für AD-Wandler
reti
.org ERDYaddr ;EEPROM Interrupt Vector Address
reti
.org ACIaddr ;Analog Comparator Interrupt Vector Address
reti
.org TWIaddr ;Irq. vector address for Two-Wire Interface
reti
.org INT2addr ;External Interrupt2 Vector Address
reti
.org OC0addr ;Interruptvektor für TimerOverFlow
reti
.org SPMRaddr ;Store Program Memory Ready Interrupt Vector Address
reti
init:
;-----------------------------------------------------------------------
-----------
; Sicherheitshalber löschen wir alle Register, weil sie irgendeine Werde
haben können,
; die in diese Register geschrieben wurden, bevor der uC resetet wurde
; hier wird nur clr r0-31 ausgeführt
.include "clear_regs.def"
;-----------------------------------------------------------------------
-----------
; Stack initialisieren
ldi temp, LOW(RAMEND)
out SPL, temp
ldi temp, HIGH(RAMEND)
out SPH, temp
; Baudrate einstellen
ldi temp, LOW(UBRRVAL)
out UBRRL, temp
ldi temp, HIGH(UBRRVAL)
out UBRRH, temp
;dies Einstellung entspricht 1 Start Bit, 8 Data Bit, 1 Stop Bit, kein
Parity Bit
ldi temp, (1<<URSEL)|(3<<UCSZ0)
out UCSRC, temp
ldi temp, (1<<RXEN)|(1<<TXEN)|(1<<TXCIE)|(1<<RXCIE)
out UCSRB, temp
; A0 -> CLK nur output
; A1 -> DATA nur output
; A2 -> PROG_B nur output ohne Transistor da nur auf LOW ziehen und
dann das Pin als input konfigurieren
; B2 -> INIT_B nur input/output
; B3 -> DONE nur input
; nur die Pins 1 und 2 des PORTD sind Ausgänge
; Pin 0 ist RXD, Pin 1 ist TXD, Pin 5 ist RTS, Pin 4 ist CTS(Schaltung
nach SerialPA.sch CTS und RTS eigentlich umgekehrt aber es funct!)
ldi temp, 0b00010010
out DDRD, temp
ldi temp, 0b00000000
out DDRA, temp
ser temp
out DDRB, temp
sei
;-----------------------------------------------------------------------
-----------
; start main
start:
clr STATUS_REGISTER
clr temp
out DDRA, temp
cbi PORTD, cCTS
rcall waitForChar ; warte auf das erste Zeichen vom PC
ldi temp, 0b00000111 ; wenn das erste Zeichen kommt, werden PIN A0-3
als Ausgang konfiguriert
out DDRA, temp
sbi PORTA, CCLK ; CCLK muss auf HIGH gesetzt werden und PROG_B
auf mindestens 300ns auf 0!!!
cbi PORTA, PROG_B
sbr STATUS_REGISTER, STATUS_PROG_B ; hier wird vermerkt, dass PROG_B
und INIT_B auf LOW gezogen wurde
wait_for_init_b_low:
sbic PIND, INIT_B ; wenn INIT_B LOW ist, gehe zur LowBearbeitung
rjmp wait_for_init_b_low
ldi temp, 50 ; für 300ns brauchen wir cca 50 Taktzycklen bei
16MHz
wait_prog_b:
dec temp
breq end_prog_b
rjmp wait_prog_b
end_prog_b:
ldi temp, 0b00000011 ; nach 300ns muss PROG_B released werden, d.h.
PIN A2 als Input konfigurieren
out DDRA, temp
sbi PORTA, CCLK ; CCLK muss auf HIGH gesetzt werden und PROG_B
auf mindestens 300ns auf 0!!!
wait_for_init_b_high: ; hier wird gewartet, bis INIT_B wieder auf
HIGH geht
sbis PIND, INIT_B ; wenn INIT_B LOW ist, gehe zur LowBearbeitung
rjmp wait_for_init_b_high
cbr STATUS_REGISTER, STATUS_INIT_B ; Nachdem INIT_B auf HIGH ist, kann
die Konfiguration anfangen
cbr STATUS_REGISTER, STATUS_PROG_B
sbr STATUS_REGISTER, STATUS_STARTED ; Proggen kann starten
ser DATA_REG
rcall sendData
ldi temp, (1<<ISC01)
out MCUCR, temp
in temp, GIFR
ori temp, (1<<INTF0)
out GIFR, temp
; Interrupt0 aktivieren
ldi temp, (1<<INT0)
out GICR, temp
write:
sbic PIND, 7
rcall do_done
; HIER DREHT DER uC DURCH D.H. ER SPRING IN METHODEN WO ER NICHTS ZU
SUCHEN HAT
sbis PIND, INIT_B
sbi PORTB, 1 ; crc error
nop
nop
nop
rjmp write
rjmp write
rjmp write
rjmp write
rjmp write
rjmp write
rjmp write
end:
rjmp write
error:
rjmp write
; end main
;-----------------------------------------------------------------------
-----------
int_init_b:
reti
waitForChar:
sbrs STATUS_REGISTER, cNEW_CHAR
rjmp waitForChar
ret
; Interruptroutine: wird ausgeführt sobald ein Byte über das UART
empfangen wurde
int_rxc:
in DATA_REG, UDR
sbrs STATUS_REGISTER, cNEW_CHAR
rjmp end_uart
rcall write_char
end_uart:
sbr STATUS_REGISTER, NEW_CHAR
reti ; Interrupt beenden
wait:
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
ret
write_char:
sbrs DATA_REG, 7 ; setze das Bit 7
cbi PORTA, DATA
sbrc DATA_REG, 7
sbi PORTA, DATA
cbi PORTA, CCLK ; das Bit 7 übernehmen
rcall wait
sbi PORTA, CCLK
rcall wait
sbrs DATA_REG, 6 ; setze das Bit 6
cbi PORTA, DATA
sbrc DATA_REG, 6
sbi PORTA, DATA
cbi PORTA, CCLK ; das Bit 6 übernehmen
rcall wait
sbi PORTA, CCLK
rcall wait
sbrs DATA_REG, 5 ; setze das Bit 5
cbi PORTA, DATA
sbrc DATA_REG, 5
sbi PORTA, DATA
cbi PORTA, CCLK ; das Bit 5 übernehmen
rcall wait
sbi PORTA, CCLK
rcall wait
sbrs DATA_REG, 4 ; setze das Bit 4
cbi PORTA, DATA
sbrc DATA_REG, 4
sbi PORTA, DATA
cbi PORTA, CCLK ; das Bit 4 übernehmen
rcall wait
sbi PORTA, CCLK
rcall wait
sbrs DATA_REG, 3 ; setze das Bit 3
cbi PORTA, DATA
sbrc DATA_REG, 3
sbi PORTA, DATA
cbi PORTA, CCLK ; das Bit 3 übernehmen
rcall wait
sbi PORTA, CCLK
rcall wait
sbrs DATA_REG, 2 ; setze das Bit 2
cbi PORTA, DATA
sbrc DATA_REG, 2
sbi PORTA, DATA
cbi PORTA, CCLK ; das Bit 2 übernehmen
rcall wait
sbi PORTA, CCLK
rcall wait
sbrs DATA_REG, 1 ; setze das Bit 1
cbi PORTA, DATA
sbrc DATA_REG, 1
sbi PORTA, DATA
cbi PORTA, CCLK ; das Bit 1 übernehmen
rcall wait
sbi PORTA, CCLK
rcall wait
sbrs DATA_REG, 0 ; setze das Bit 0
cbi PORTA, DATA
sbrc DATA_REG, 0
sbi PORTA, DATA
cbi PORTA, CCLK ; das Bit 0 übernehmen
rcall wait
sbi PORTA, CCLK
rcall wait
ret
do_done:
ldi temp, 0b00000001 ; wenn das erste Zeichen kommt, werden PIN A0-3
als Ausgang konfiguriert
out DDRA, temp
; Interrupt0 bei sinkender Flanke, Interrupt1 nur bei steigender
Flanke
ldi temp, (1<<ISC01) | (1<<ISC10) | (1<<ISC11)
out MCUCR, temp
; Interrupt0 und Interrupt1 aktivieren
ldi temp, (1<<INT1)
out GICR, temp
sbi PORTB, 7
do_done1:
sbis PINB, INIT_B
sbi PORTB, 1 ; crc error
cbi PORTA, CCLK ; ein DONE Zycklus
rcall wait
sbi PORTA, CCLK
rcall wait
rjmp do_done1
ret
int_done:
sbi PORTB, 0 ; done
reti
sendData:
; Wait for empty transmit buffer
sbis UCSRA, UDRE
rjmp sendData
sbr STATUS_REGISTER, TX
out UDR, DATA_REG
ret
utx_complete:
cbr STATUS_REGISTER, TX
reti
Zusatz: ich dachte an sowas wie 'wenn du in eine Methode/Interrupt kommst musst du solche und solche Register so und so sichern' oder ähnliches
Warum ASM ??????? Verwende C und schon kann jemand der deinen Code nicht kennt, das ganze in kurzer Zeit verstehen. Dann hast du auch größere Chancen, vernünftige Hilfen zu bekommen. Was aber auf jedenfall fehlt, sind die Instructions PUSH und POP bei Funktionsbeginn und Funkionsende. ASM zu benutzen gehört in dem Bereich SadoMaso ... Gruß Ralph
Kein push ? Kein pop ? Aber viel schlimmer ist dass das Statusregister nicht gesavt wurde. Generell wird zuviel rumgesprungen in der interruptroutine. Und ein wait gibt es eh nicht im interrupt. Als Anregung, ein paar Seiten, allerdings ohne interrupt http://www.ibrtses.com/embedded/avr.html P.
Hallo, ich habe da noch nicht so richtig reingeschaut, aber 2 Anmerkungen: Du hast schnelle 16MHZ, bei mir dauert ein Takt bei 16MHz 62,5ns, für 300 brauche also rund 4 und nicht 50. Außerdem wartet Deine Schleife pro Durchlauf 4 Takte, macht * 50 *62,5 bei mir 12500 ns oder 12,5µs. Außerdem: warum sollte man nach Reset alle Register auf 0 setzen? Der Inhalt ist ohnehin nach Reset zufällig, wenn ein Register im Programmlauf benutzt wird, mu0 sowiesod er passende Wert geladen werden, der wird kaum bei allen Registern 0 sein. Für mich also nutzlos, auch wenn es nicht stört. Zur Interruptroutine: Du sichert das Statusregister nicht, das bringt in jedem Fall das Hauptprogramm aus dem Konzept, wenn ein IRQ z.B. genau zwischen einem CPI und dem davon abhängigem BRNE erfolgt und in der ISR das Z-Flag geändert wird... Kann nicht lange gut gehen, SREG sichern! Außerdem rufst Du eine ziemlich lange write_char innerhalb der ISR auf. Was passiert, wenn die länger braucht und das nächste Zeichen ist schon da? Gruß aus Berlin Michael
Hallo Patrik, 1. zerlege Dein Programm zunächst in übersichtliche kleine Schritte, die jeweils eine Funktion erfüllen und prüfe diese Funktion. 2. Achte dabei darauf, nach Möglichkeit "temporäre" Register zu verwenden. 3. Rufe diese Funktionen dann so nacheinander auf, wie es für den Funktionsablauf erforderlich ist. 4. Prüfe, ob Du mit Schleifen Vereinfachung erreichen kannst (z. B. Deine "wait - Funktion"). Gruss Otto
Hallo, @Ralph: sorry, aber Du schreibst schlicht Blödsinn! Durcheinander und ungünstige Strukturen und zu sparsame Kommentare kann man auch in C basteln. Mich stört eher, daß in seinen "Methoden" so wenig Methode ist. ;) Dazu kommt, daß hier offenbat was mit Hadrware gemacht wird, zu dem (wie meist) zumindest ein Schaltplanauszug oder eine verständlichere Beschreibung von Port- und Pinbenutzung fehlt, damit es Außenstehende überhaupt nachvollziehen können, was passieren soll. Gruß aus Berlin Michael
Hallo Jungs, erstmals vielen Dank für euere Ratschläge, ich versuche das Ding bissl umzubauen. Ich hatte viel sauberere Versionen davon, aber ohne Sichern des SREG's denke ich komme wirklich nicht weit.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.