.include "c:\avrtools\appnotes\2333def.inc" ;Auf 4+4 Kanäle reduzierte Version ;4 analoge Kanäle und 4 digitale Kanäle (8 digitale Eingänge) ;Gesendete Bits: ;SSSSAAAAHHHHHHHHCHANNEL1CHANNEL2CHANNEL3CHANNEL4DDDDDDDD ;= 7 Bytes, umgewandelt in Manchester 14 Bytes (=140 Bits) .cseg .org 0 rjmp reset ;Reset handler reti ;External interrupt 0 - Not used reti ;External interrupt 1 reti ;Counter1 capture event reti ;Counter1 compare match reti ;Counter1 overflow reti ;Counter0 overflow reti ;Serial Transfer complete reti ;UART RX complete reti ;UART Data register empty rjmp serial ;UART TX complete rjmp adc_int ;ADC Conversion Complete reti ;EEPROM Ready reti ;Analog comparator .def digit_0 = r3 .def digit_1 = r4 .def temp = r16 .def temp2 = r17 .def temp3 = r18 .def ad_kanal = r20 .def seriell_cnt = r21 .equ buffer = $60 ;Im Buffer werden die Rohdaten gespeichert (mit Startbyte) .equ sendbuffer= $68 ;Bereits in Manchester-Code sendefertige Daten (ohne Startbyte) .equ quarz = 3686400 .equ baud = 2400 .equ baud_wert = (quarz/(16*baud))-1 reset: ldi temp,$c0 ;Stack-pointer auf Adresse $C0 out SPL,temp ldi temp,$00 ;A/D-Kanal auf 0 setzen out ADMUX,temp ldi temp,$8E ;A/D Wandler initialisieren out ADCSR,temp sbi ADCSR,ADSC ;1. Wandlung starten ldi temp,$48 ; out UCSRB,temp ;UART: Int TX complete + TXD Enable ldi temp,baud_wert ;Baudrate setzen out UBRR,temp clr temp out DDRD,temp out DDRB,temp ;alle Ports auf Eingang (Reset-Zustand) ldi temp,$FC out PORTD,temp ;Pull-Ups PD 2,3,4,5,6,7 ldi temp,$3F out PORTB,temp ;Pull-Ups PB 0,1,2,3,4,5 ldi ad_kanal,$03 ;Kanal-Zähler für A/D-Wandler initialisieren ldi seriell_cnt,$01 ;nur 1 Byte übertragen clr xh ;Die high-Bytes von X/Y auf null setzen clr yh ;(für Controller mit mehr als 256 Bytes RAM) clr temp out UDR,temp ;die serielle Übertragung auslösen sei loop: ;HAUPTPROGRAMM ;garnix machen rjmp loop ;adc_int wird jedesmal aufgerufen, wenn ein A/D-Kanal fertig ist. Gesteuert über ;die Adresse wird der aktuelle Wert im RAM gespeichert. adc_int: push temp ;benutzte Register und Statusregister auf dem Stack sichern in temp,SREG push temp push temp2 push temp3 ldi xl,buffer+2 ;Basisadresse für analoge Werte (Low-Bytes) add xl,ad_kanal ;die Kanal-Nummer hinzuaddieren in temp,ADCL ;das Low-Byte des A/D-Werts lesen st x,temp ;in den Speicher schreiben in temp,ADCH ;das High-Byte des A/D-Werts lesen mov temp2,ad_kanal ;die Kanal-Nummer in Temp2 kopieren ldi temp3,$03 ;Maske definieren adc_int_loop: tst temp2 ;ist die Kanal-Nummer = 0? breq adc_int_1 ;falls ja, dann Sprung lsl temp ;Temp um 2 Bits nach links schieben lsl temp lsl temp3 ;Die Maske auch verschieben lsl temp3 dec temp2 ;Zähler aktualisieren rjmp adc_int_loop ;Schleife wiederholen adc_int_1: com temp3 ;die Maske negieren lds temp2,buffer+1 ;den aktuellen Wert aus dem Speicher lesen and temp2,temp3 ;die aktuellen Bits löschen or temp2,temp ;und mit dem neuen Wert überschreiben sts buffer+1,temp2 ;das Ergebnis in den Speicher zurückschreiben dec ad_kanal ;nächster Kanal out ADMUX,ad_kanal ;Kanal selektieren brpl adc_end ;kein Unterlauf, dann OK ldi ad_kanal,$03 ;Unterlauf: Register neu initialisieren (Anzahl Analog-Kanäle) out ADMUX,ad_kanal adc_end: sbi ADCSR,ADSC ;nächste Wandlung starten pop temp3 pop temp2 ;Die gesicherten Register + Status pop temp ;wiederherstellen out SREG,temp pop temp reti ;Konvertiert das obere Nibble von (temp) in den Manchester-Code, ;Ergebnis ist in temp3. Durch 2-maligen Aufruf wird das komplette ;Byte gewandelt. konv_nibble: ldi temp2,$04 ;4 Bit wandeln konv_nibble_loop: rol temp ;MSB ins Carry schieben brcs konv_nibble_1 ;wenn Bit gesetzt, dann Sprung rol temp3 ;Bit war 0, also eine 0 in temp3 schieben sec ;C=1 rol temp3 ;eine 1 in temp3 schieben ( "01" ) rjmp konv_nibble_2 ;weiter konv_nibble_1: rol temp3 ;Bit war 1, also eine 1 in temp3 schieben clc ;C=0 rol temp3 ;eine 0 in temp3 schieben ( "10" ) konv_nibble_2: dec temp2 ;Schleifenzähler brne konv_nibble_loop ;Schleife wiederholen ret ;Ergebnis ist in temp3 ;***************************************************************************************** ;* Diese Routine ruft sich immer wieder selbst auf, wenn das nächste Zeichen über * ;* die serielle Schnittstelle ausgegeben werden soll. Dieser Aufruf geschieht durch * ;* beschreiben des UDR. Dadurch wird eine serielle Ausgabe initiiert, und der Interrupt * ;* wird erneut aufgerufen, wenn das UDR-Register leer ist. * ;***************************************************************************************** serial: ;Serielles senden push temp ;Register und Status sichern in temp,SREG push temp push temp2 push temp3 dec seriell_cnt ;Anzahl der zu sendenden Bytes breq serial_new ;=0? Falls ja, dann die Daten aktualisieren serial1: ld temp,y+ ;sonst Daten aus dem Puffer lesen out UDR,temp ;aktuelles Byte senden serial_end: pop temp3 ;Die Register + Status wiederherstellen pop temp2 pop temp out SREG,temp pop temp reti ;Ende der seriellen Sende-Routine serial_new: in temp,PIND ;PortD lesen (Adresse) andi temp,$3C ;Bits 2,3,4,5 isolieren lsr temp ;rechtsbündig auf Bit 0,1,2,3 verschieben lsr temp sts buffer,temp ;in den Puffer schreiben ;die digitalen Eingänge sind wegen eines einfacheren ;Platinenlayouts verdreht. Dies wird von der Software ;korrigiert. in temp,PINB ;den ersten Teil der digitalen Eingänge lesen ldi temp2,$06 ;6 Bits clr temp3 serial_new1: ror temp ;ein Bit nach rechts ins Carry schieben rol temp3 ;das Carry nach links in temp3 schieben - dadurch werden ;die Bits verdreht dec temp2 ;den Zähler aktualisieren brne serial_new1 ;wiederholen, bis 6 Bits gedreht sind in temp,PIND ;die noch fehlenden digital-Bits lesen andi temp,$C0 ;Bit 6 und 7 isolieren or temp,temp3 ;mit dem vorhergehenden Ergebnis kombinieren sts buffer+6,temp ;in den Speicher schreiben ldi yl,sendbuffer ;Das Y-Register mit der Startadresse des Sendepuffers laden ldi xl,buffer ;Das X-Register mit der Startadresse des Puffers laden ldi seriell_cnt,$07 ;7 Bytes wandeln serial_new2: ld temp,x+ ;Ein Byte aus dem Puffer lesen rcall konv_nibble ;das obere Nibble (Bit 4..7) in Manchester kodieren st y+,temp3 ;den Manchester-Code in temp3 in den Sendepuffer schreiben rcall konv_nibble ;das untere Nibble wurde ins obere geschoben - nochmals konvertieren st y+,temp3 ;den 2. Manchester-Code in den Sendepuffer schreiben dec seriell_cnt brne serial_new2 ;die Schleife wiederholen, bis alle Daten gewandelt wurden ldi temp,$F0 sts sendbuffer,temp ;das Startbit korrigieren ldi seriell_cnt,$0E ;14 Bytes senden ldi yl,sendbuffer ;den Pointer initialisieren rjmp serial1 ;Die Übertragung der neuen Werte beginnen