Hallo zusammen, ich nutze einen Atmega8 zur AD-Wandlung. Dieser soll konstant alle 20ms einen digitalisierten Messwert über die USATR versenden. Wie aus dem Anhang (Code) ersichtlich: Taktrate=7372800Hz; Vorteiler 1024; Timer0= 256 Takte/Overflow; Timerwert=112 (gesetzt) => 50 Overflows/sec. Der Eingang PinB0 liegt die ganze Zeit auf low. Warum kann ich nicht alle 20ms einen Wert am UART, empfangen??? DAs Oszilloskop zeigt nicht kontinuierlich, immer mit Aussetzern, die Werte. Hilfe, bin schon den ganzen tag auf fehler suche! Danke Adrian
oh, datei vergessen
.include "m8def.inc"
.equ CLOCK = 7372800
.equ BAUD = 38400
.equ UBRRVAL = CLOCK/(BAUD*16)-1
.equ timer=112
;;;;;;;;;;;;;;;;;;;Interrupt Handler;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.org 0x000
rjmp main ; main-jump
.org OVF0addr
rjmp TIM0_OVF ; Timer 0 Overflow Handler
.org URXCaddr
rjmp USART_RXC ; USART RX Complete Handler
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
main:
;Stackpointer initialisieren
ldi r16, LOW(RAMEND)
out SPL, r16
ldi r16, HIGH(RAMEND)
out SPH, r16
;PORTB & PORTD als Ausgang (LED) konfigurieren
ldi r16,0b11111110
out DDRB, r16
ldi r16,0b11111111
out DDRD, r16
cbi PortB,1 ;Empfangsbereitschaft mikroC
;UART initialisieren
ldi r16, LOW(UBRRVAL) ;Baudrate einstellen
out UBRRL, r16
ldi r16, HIGH(UBRRVAL)
out UBRRH, r16
ldi r16, (1<<URSEL)|(3<<UCSZ0) ;Frame Format
out UCSRC, r16
sbi UCSRB, TXEN ; TX (Senden) aktiv
sbi UCSRB, RXEN ; RX (Empfang) aktiv
sbi UCSRB, RXCIE ; Interrupt bei Empfang
;ADMUX initialisieren
ldi r16,0b00100010 ; Vref extern,Kanal ADC2
out ADMUX,r16
sbi PortD, 4 ; LED Temperatur an
;TIMER0 initialisieren
ldi r16, 1<<TOIE0 ; Interrupt bei OVF Timer0
out TIMSK, r16
ldi r16, (1<<CS02)|(0<<CS01)|(1<<CS00); Vorteiler 1024
out TCCR0, r16
ldi r16, timer ; Startwert setzen
out TCNT0,r16
;Interrupt global an
sei
Hauptschleife:
rjmp Hauptschleife
;;;;;;;;;;;;;TimerInterrupt;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TIM0_OVF:
sei ; Interrupt global an
ldi r16, timer ; Timer erneut setzen
out TCNT0,r16
ldi r16,
((1<<ADEN)|(1<<ADSC)|(0<<ADFR)|(1<<ADIF)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0
)) ;ADC einschalten
out ADCSR, r16
loop:
sbis ADCSR, ADIF ; ADIF flag gelöscht = ADC abgeschlossen
rjmp loop
in r16, ADCH ; Umwandlungsergebnisregister ADCH einlesen:
rcall busy ; Empfänger bereit prüfen
rcall serout ; Subroutine serout 'Daten senden' aufrufen
reti ; Interrupt Routine verlassen
;UART bereit loop & SENDEN
serout:
sbis UCSRA,UDRE
rjmp serout
out UDR, r16
ret
;Empfänger bereit loop
busy:
sbic PinB,0 ;PortB Pin0 Busyleitung Empfänger (ist immer auf low)
rjmp busy
ret
;;;;;;;;;;;;;;;;;;USART
RX_Interrupt;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
USART_RXC:
cli
push r16 ; Arbeitsregister auf Stack sichern
in r16,udr ; Register UDR einlesen
EmpfangA:
cpi r16,'A' ; überprüft ob ein A gesendet wurde
brne EmpfangB ; wenn kein A empfangen wurde springe EmpfangenB
ldi r16, 0b00100000 ; AD Eingang ADC0 Beschleu.x-Achse
out ADMUX, r16
cbi PortD,3 ; Anzeige LED
cbi PortD,4
sbi PortD,2
rjmp weiter ; springe zu weiter
EmpfangB:
cpi r16,'B' ; überprüfen ob ein B empfangen wurde
brne EmpfangC ; wenn kein B empfangen wurde springe EmpfangenC
ldi r16, 0b00100001 ; AD Eingang ADC1 Beschleu.y-Achse
out ADMUX, r16
cbi PortD,2 ; Anzeige LED
cbi PortD,4
sbi PortD,3
rjmp weiter ; springe zu weiter
EmpfangC:
cpi r16,'C' ; überprüfen ob ein C empfangen wurde
brne weiter ; wenn kein C empfangen wurde springe weiter
ldi r16, 0b00100010 ; AD Eingang ADC2 Temperatur
out ADMUX, r16
cbi PortD,2 ; Anzeige LED
cbi PortD,3
sbi PortD,4
weiter:
pop r16 ; Arbeitsregister aus Stack laden
reti ; Interruptroutine verlassen
Hallo Adrian, hast Du die fuses richtig gesetzt ? Gruss Otto
- Sichere in den ISRs das SREG (!!!) - Lass in der Timer ISR die interrupts aus und - Benutz den ADC-Interrupt für das Senden des ADC-Werts, oder - setz in der Timer-ISR ein Flag (Register oder SRAM), dass von der Main-Loop abgefragt wird und die macht dann die Messung - du solltest wahrscheinlich nicht einfach so das ADMUX-Register ändern (hier: in der Receive-ISR) hth. Jörg
Jörg X. wrote: > - Sichere in den ISRs das SREG (!!!) > - Lass in der Timer ISR die interrupts aus und > - Benutz den ADC-Interrupt für das Senden des ADC-Werts, oder > - setz in der Timer-ISR ein Flag (Register oder SRAM), dass von der > Main-Loop abgefragt wird und die macht dann die Messung > - du solltest wahrscheinlich nicht einfach so das ADMUX-Register ändern > (hier: in der Receive-ISR) > > hth. Jörg Wenn du nur ein Bit setzt in der ISR brauchst du kein SREG zu sichern :-)) EDIT: Grrrr. Moment: Galt das nur für SBI/CBI und nicht für SBR/CBR? Ich ziehe meine Aussage erstmal zurück ;) EDIT2: Jap, SBR ist ja nur ein ORI -> SREG sichern.
hallo, die fuses-bits sind richtig gesetzt (habe ihc dem Datenblatt entnommen). Werde das Prog.umschreiben, kann es erst wieder am Mo. am System ausprobieren, aber schon mal vielen Dank!!! Wenn es nicht funk.sollte dann melde ich mich am Mo. wieder. Aber die Vorschläge klingen gut! Wenn noch jemanden was auffällt, immer raus mit der Sprache! Noch mal Danke und schönes We. Adrian
Hallo Leute, spez.Jörg X., habe grade das Prog. umgeschrieben. Mir ist aber nicht klar, was mit: > - du solltest wahrscheinlich nicht einfach so das ADMUX-Register ändern > (hier: in der Receive-ISR) gemeint ist. Wie soll ich denn die ADU Eingänge sonst umschalten??? Aktuelles Prog. im Anhang. gruß adrian
Wieder der Anhang nicht dabei, sorry!
.include "m8def.inc"
.equ CLOCK = 7372800
.equ BAUD = 38400
.equ UBRRVAL = CLOCK/(BAUD*16)-1
.equ timer=112
;;;;;;;;;;;;;;;;;;;Interrupt Handler;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.org 0x000
rjmp main ; main-jump
.org OVF0addr
rjmp TIM0_OVF ; Timer 0 Overflow Handler
.org URXCaddr
rjmp USART_RXC ; USART RX Complete Handler
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
main:
;Stackpointer initialisieren
ldi r16, LOW(RAMEND)
out SPL, r16
ldi r16, HIGH(RAMEND)
out SPH, r16
;PORTB & PORTD als Ausgang (LED) konfigurieren
ldi r16,0b11111110
out DDRB, r16
ldi r16,0b11111111
out DDRD, r16
cbi PortB,1 ;Empfangsbereitschaft mikroC
;UART initialisieren
ldi r16, LOW(UBRRVAL) ;Baudrate einstellen
out UBRRL, r16
ldi r16, HIGH(UBRRVAL)
out UBRRH, r16
ldi r16, (1<<URSEL)|(3<<UCSZ0) ;Frame Format
out UCSRC, r16
sbi UCSRB, TXEN ; TX (Senden) aktiv
sbi UCSRB, RXEN ; RX (Empfang) aktiv
sbi UCSRB, RXCIE ; Interrupt bei Empfang
;ADMUX initialisieren
ldi r16,0b00100010 ; Vref extern,Kanal ADC2
out ADMUX,r16
sbi PortD, 4 ; LED Temperatur an
;TIMER0 initialisieren
ldi r16, 1<<TOIE0 ; Interrupt bei OVF Timer0
out TIMSK, r16
ldi r16, (1<<CS02)|(0<<CS01)|(1<<CS00); Vorteiler 1024
out TCCR0, r16
ldi r16, timer ; Startwert setzen
out TCNT0,r16
;Interrupt global an
sei
Hauptschleife:
sbis TIFR,TOV0
rjmp Hauptschleife
ldi r16,
((1<<ADEN)|(1<<ADSC)|(0<<ADFR)|(1<<ADIF)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0
)) ;ADC einschalten
out ADCSR, r16
loop:
sbis ADCSR, ADIF ; ADIF flag gelöscht = ADC abgeschlossen
rjmp loop
in r16, ADCH ; Umwandlungsergebnisregister ADCH einlesen:
;Empfänger bereit loop
busy:
sbic PinB,0 ;PortB Pin0 Busyleitung Empfänger (ist immer auf low)
rjmp busy
;UART bereit loop & SENDEN
serout:
sbis UCSRA,UDRE
rjmp serout
out UDR, r16
rjmp Hauptschleife
;;;;;;;;;;;;;TimerInterrupt;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TIM0_OVF:
push r16 ; Arbeitsregister auf Stack sichern
in r16, SREG ; SREG Sichern
push r16
ldi r16, timer ; Timer erneut setzen
out TCNT0,r16
pop r16
out SREG, r16 ; SREG laden
pop r16 ; Arbeitsregister aus Stack laden
reti ; Interrupt Routine verlassen
;;;;;;;;;;;;;;;;;;USART
RX_Interrupt;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
USART_RXC:
cli
push r16 ; Arbeitsregister auf Stack sichern
in r16, SREG ; SREG Sichern
push r16
in r16,udr ; Register UDR einlesen
EmpfangA:
cpi r16,'A' ; überprüft ob ein A gesendet wurde
brne EmpfangB ; wenn kein A empfangen wurde springe EmpfangenB
ldi r16, 0b00100000 ; AD Eingang ADC0 Beschleu.x-Achse
out ADMUX, r16
cbi PortD,3 ; Anzeige LED
cbi PortD,4
sbi PortD,2
rjmp weiter ; springe zu weiter
EmpfangB:
cpi r16,'B' ; überprüfen ob ein B empfangen wurde
brne EmpfangC ; wenn kein B empfangen wurde springe EmpfangenC
ldi r16, 0b00100001 ; AD Eingang ADC1 Beschleu.y-Achse
out ADMUX, r16
cbi PortD,2 ; Anzeige LED
cbi PortD,4
sbi PortD,3
rjmp weiter ; springe zu weiter
EmpfangC:
cpi r16,'C' ; überprüfen ob ein C empfangen wurde
brne weiter ; wenn kein C empfangen wurde springe weiter
ldi r16, 0b00100010 ; AD Eingang ADC2 Temperatur
out ADMUX, r16
cbi PortD,2 ; Anzeige LED
cbi PortD,3
sbi PortD,4
weiter:
pop r16 ; Arbeitsregister aus Stack laden
out SREG, r16 ; SREG laden
pop r16
reti ; Interruptroutine verlassen
Ich habe mal versucht meine ASM-Kenntnisse auszupacken (programmiere lieber in C). Ergebnis siehe Anhang (der Anhang geht nur ohne Vorschau, btw.). Mein Programm nutzt ein Flag-Register 'flags' und ein Byte im SRAM für den nächsten ADC-Kanal. Sowohl bei der Messung, als auch beim Senden des Messwerts gibt's Busy-Wait, was sicher unpraktisch für größere/kompliziertere Anwendungen ist. hth. Jörg (der das aus Spass an der Freud' gemacht hat) ps.: die Kanalauswahl könnte man mit (UDR - 'A') stark vereinfachen ;) pps.: das Programm ist nur im Simulator getestet, scheint(!) aber zu funktionieren, was die Interrupts angeht
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.