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.