; *************************************************************** ; * * ; * Arbeitsversion * ; * Kapazitätsmesser Messzeit jeweils: 0,4 sec * ; * Maximale Frequenz des Schwingkreises 750 kHz * ; * sonst gibt es einen überlauf in den Matheroutinen * ; * Bei f>1,31 MHz falsche Anzeige wg. internem Überlauf * ; * in der Zählroutine * ; * Timer-Schleife im Polling-Modus ohne Interrupt * ; * * ; * (C)06. Ausgust 2003 by Joern Bartels * ; * klettermaxi@web.de Letzte Änderung: * ; * 29.06.2004 Beginn der Codeüberarbeitung hin zum Mega 128 ; *************************************************************** ; ; Hardware Anforderungen: AVI Mega_8 mit 4 MHz ; Port PD5 (T1): Eingangssignal kommend von der Schaltung ; PD6 : Ausgang zum Relai, dass den Messkondensator zuschaltet ; PD7 : Taste Null/Eichen ; PD0 : Taste Cx_messen ; PD1 : Taste Lx_messen ; Software Funktionen: ; .NOLIST .INCLUDE "m8def.inc" .LIST ; Variablen und Konstanten werden im RAM-Speicher zwischengespeichert ; um immer wieder einen Zugriff auf sie zu haben. ; Die Kapazitaetsangaben sind alle in 1/100 piko Farrad um Ungenauigkeiten ; durch Rundungen etc. zu vermeiden. ; Gemessen werden können Kapazitäten im Bereich 1pF bis 10mF ; (ungepolte Kondensatoren - also keine Elkos!!!) .EQU C1_Schwingkreis0Bit= 0x0060 ;Grund-Kondensator im Schwingkreis .EQU C1_Schwingkreis1Bit= 0x0061 ; ca. 680 00 (1/100 pF) .EQU C1_Schwingkreis2Bit= 0x0062 ; wird geeicht und verbessert .EQU C2_Messergebnis0Bit= 0x0063 ;zugeschalteter Kondensator .EQU C2_Messergebnis1Bit= 0x0064 ;kann auch der Eichkondensator sei .EQU C2_Messergebnis2Bit= 0x0065 ;40 Bit Darstellung, da die Werte .EQU C2_Messergebnis3Bit= 0x0066 ;immer in 1/100 pF dargestellt werden .EQU C2_Messergebnis4Bit= 0x0067 ;max. Wert geschätzt 10mF = 10^12 (1/100 pF) .EQU C2_Messergebnis5Bit= 0x0068 .EQU C2_Messergebnis6Bit= 0x0069 ;64-Bit um bei der Fehlersuche auch .EQU C2_Messergebnis7Bit= 0x006A ;Zwischenergebnisse darstellen zu können ;.EQU L1_Vorgabe0Bit= 0x0069 ; noch nicht verwendet ;.EQU L1_Vorgabe1Bit= 0x006A ; noch nicht verwendet ;.EQU L1_Vorgabe2Bit= 0x006B ; noch nicht verwendet .EQU L2_Messergebnis0Bit= 0x006F ; noch nicht verwendet .EQU L2_Messergebnis1Bit= 0x0070 ; noch nicht verwendet .EQU L2_Messergebnis2Bit= 0x0071 ; noch nicht verwendet .EQU f1_Messung_eins0Bit= 0x0072 .EQU f1_Messung_eins1Bit= 0x0073 .EQU f1_Messung_eins2Bit= 0x0074 .EQU f1_Messung_zwei0Bit= 0x0075 .EQU f1_Messung_zwei1Bit= 0x0076 .EQU f1_Messung_zwei2Bit= 0x0077 .EQU f1_Messung_drei0Bit= 0x0078 .EQU f1_Messung_drei1Bit= 0x0079 .EQU f1_Messung_drei2Bit= 0x007A .EQU f1_Mittelwert0Bit= 0x007B .EQU f1_Mittelwert1Bit= 0x007C .EQU f1_Mittelwert2Bit= 0x007D .EQU f2_Messung_eins0Bit= 0x007E .EQU f2_Messung_eins1Bit= 0x007F .EQU f2_Messung_eins2Bit= 0x0080 .EQU f2_Messung_zwei0Bit= 0x0081 .EQU f2_Messung_zwei1Bit= 0x0082 .EQU f2_Messung_zwei2Bit= 0x0083 .EQU f2_Messung_drei0Bit= 0x0084 .EQU f2_Messung_drei1Bit= 0x0085 .EQU f2_Messung_drei2Bit= 0x0086 .EQU f2_Mittelwert0Bit= 0x0087 .EQU f2_Mittelwert1Bit= 0x0088 .EQU f2_Mittelwert2Bit= 0x0089 .EQU f1quadrat0Bit= 0x008A .EQU f1quadrat1Bit= 0x008B .EQU f1quadrat2Bit= 0x008C .EQU f1quadrat3Bit= 0x008D .EQU f1quadrat4Bit= 0x008E .EQU f2quadrat0Bit= 0x008F .EQU f2quadrat1Bit= 0x0090 .EQU f2quadrat2Bit= 0x0091 .EQU f2quadrat3Bit= 0x0092 .EQU f2quadrat4Bit= 0x0093 .EQU Cs0Bit= 0x0094 .EQU Cs1Bit= 0x0095 .EQU Cs2Bit= 0x0096 .EQU Eichkondensator0Bit= 0x0097 .EQU Eichkondensator1Bit= 0x0098 .EQU Eichkondensator2Bit= 0x0099 .EQU Eichkondensator3Bit= 0x009A ; Benutzte Register ; weitere Definitionen weiter hinten .def temp = r16 ; allgemeines Register Nr 0 z.B. für LCD .DEF temp1 = R17 ; allgemeines Register Nr 1 z.B. für LCD .DEF temp2 = R18 ; allgemeines Register Nr 2 z.B. für LCD .DEF temp3 = R19 ; allgemeines Register Nr 3 ; ;.CSEG; Code beginnt hier ;##################################################################### ; Reset- und Interrupt-Vektoren .ORG $0000 rjmp main ;RESET ; Reset Handler Reset-vector reti ; rjmp EXT_INT0 ; IRQ0 Handler External Interrupt Request 0 reti ; rjmp EXT_INT1 ; IRQ1 Handler External Interrupt Request 1 reti ; rjmp TIM2_COMP ; Timer2 Compare Handler reti ; rjmp TIM2_OVF ; Timer2 Overflow Handler reti ; rjmp TIM1_CAPT ; Timer1 Capture Handler Timer/Counter1 Capture event reti ; rjmp TIM1_COMPA ; Timer1 CompareA Handler Timer/Counter1 Compare match A reti ; rjmp TIM1_COMPB ; Timer1 CompareB Handler Timer/Counter1 Compare Match B reti ; rjmp TIM1_OVF ; Timer1 Overflow Handler Timer/Counter1 Overflow reti ; rjmp counter0 ; Timer0 Overflow Handler Timer/Counter0 Overflow reti ; rjmp SPI_STC ; SPI Transfer Complete Handler reti ; rjmp USART_RXC ; USART RX Complete Handler reti ; rjmp USART_UDRE ; UDR Tx data register empty Empty Handler reti ; rjmp USART_TXC ; USART TX Complete Handler reti ; rjmp ADC ; ADC Conversion Complete Handler reti ; rjmp EE_RDY ; EEPROM Ready Handler reti ; rjmp ANA_COMP ; Analog Comparator Handler reti ; rjmp TWSI ; Two-wire Serial Interface Handler reti ; rjmp SPM_RDY ; Store Program Memory Ready Handler ;###################################################################### ; Verschiedene Unterprogramme ;###################################################################### ;###################################################################### ; Unterprogramme Bildschirm ;###################################################################### ; MAKROS .MACRO locate ;(Zeile,Spalte) Aufrufen des Makros z.B.: locate 1,5 ;verwendet die Register r17r18 push temp1 ldi temp1, 0b10000000|(((@0)-1)<<6)|((@1)-1) rcall lcd_command pop temp1 .ENDMACRO ; Unterprogramm: Warteschleife Dauer 1 Sekunde delay_1sek: ; wait ca. 1 seconds ldi r17, 0 ldi r18, 0 ldi r19, 35 delay1: dec r17 brne delay1 dec r18 brne delay1 dec r19 brne delay1 ret ;###################################################################### ; Unterprogramm für Kapazitätsmesser und Frequenzzähler ; Register: R16r17r18r19 r22r23r24 ;###################################################################### Zaehlschleife: ;Gezaehlt werden fuer genau 0,4s die fallenden Flanken an PortD5 (T1) ;Mit dem Counter1 wird gezaehlt ;ES wird eine Zaehlschleife ca. 0,4s durchlaufen. ;In dieser Zeit wird der Zaehler gestartet und durchlaeft mehr als 16-Bit ;Daher wird das Überlauf-Flag im PollinModus abgefragt und so der Zaehler ;auf 19-Bit (programmiert sind 24-Bit) erweitert. ;Beim Kapazitaetsmesser werden nur 19-Bit benoetigt (max. Frequenz 750 kHz) ;Das Ergebnis wird gespeichert in: ;bit0 TCNT1L ;bit1 TCNT1H ;bit2 r22 ;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ;MEGA128 %%%%%%%%%%%%%%% Ports anpassen : Andere Belegung ;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Zaehlschleife_starten: ;Die Zaehlschleife ist genau 0,4s lang!!!! nop Zaehler_starten: ldi temp, 0b00000000 ;Zähler anhalten out TCCR1B, temp ; ldi temp, 0x00 ;Zähler auf Null stellen (Startwert) out TCNT1H, temp ; out TCNT1L, temp ; ldi temp, 0x00 out TIFR, temp ldi temp, 0b00000111 ;Zählweise einstellen und Zähler starten out TCCR1B, temp ; Counter inkrementiert bei fallender ; Flanke an Pin T0 (Pin 4 von Port D) ;die Zähl-Ergebnisse sollen in r20 bis r23 ;gespeicher werden (r23 bleibt 0x00) ;Software-Timer starten ldi r16, 0xFF ldi r17, 0xC0 ; 0xC0 ldi r18, 0x08 ;0x08 ldi r19, 0x00 ldi r22, 0x00 ; weiteres Zählregister für delay__ vorbereiten delay__: dec r16 brne delay__ dec r17 brne delay__ ;dec r18 ;brne delay__ ldi r24, 0xFF ;Ausnahme: 0b11111111 schreiben um flag-bits ;zu löschen IN r23, TIFR out TIFR, r24 cbr r23, 0b11111011 ;;IF r23=0 test auf null vorbereiten TST r23 ;; auf Null testen-wenn null dann springen BREQ kein_Ueberlauf ;;Then 'Sprung nach kein_Ueberlauf' out TIFR, r24 ;;Else Ausnahme: 0b11111111 schreibe um flag-bits ;; zu löschen inc r22 ;; kein_Ueberlauf: nop nop dec r18 brne delay__ ldi r16, 0xD0 ldi r17, 0x5E ; 0x5E delay__2: dec r16 brne delay__2 dec r17 brne delay__2 Zaehler_stoppen: ;Zaehler stoppen ldi r16, 0x00 out TCCR1B, r16 ;Zähler erneut und abschliessend auf Überlauf ;prüfen und im Falle des Überlaufs r22 erhöhen ldi r24, 0xFF ;0b11111111 schreiben um flag-bits ;zu löschen IN r23, TIFR out TIFR, r24 cbr r23, 0b11111011 ;;IF r23=0 test auf null vorbereiten TST r23 ;; auf Null testen wenn null dann springen BREQ Return1 ;;Then GOTO Return1 ;Anzeige_Frequenzzaehler ; inc r22 ;;Else r22=r22+1 Return1: ret ;Ende der Zaehlschleife Rücksrung ;Das Ergebnis ist in ;bit0 TCNT1L ;bit1 TCNT1H ;bit2 r22 ;###################################################################### ; Startkonfiguration ;###################################################################### Startkonfiguration: Ports_initialisieren: ;Ports initialisieren ;Port D Pin PD0 Eingang für Taster ;Port D Pin PD1 Eingang für Taster ;Port D Pin PD2 Eingang für Taster ;Port D Pin PD5 auf Eingang für Zähler stellen ;Port D Pin PD6 auf Ausgang für Relais später ;Port D Pin PD7 auf Ausgang für Relais:Eichen/Zero C-eichen zuschalten ldi temp, 0xb11000000 out DDRD, temp ;Ausgang für Kontroll-LED konfigurieren Wird zZ für die ldi temp, 0xFF ;Relaissteuerung out DDRC, temp ; PORTC configured for output verwendet ; Ausgang für LCD konfigurieren ldi temp, 0xFF out DDRB, temp ; PORTB configured for output (fuer LCD) rcall lcd_init ;Display initialisieren rcall lcd_clear ;Display löschen ;sRam-Speicher mit Daten versehen (Startkonfiguration) ;Schwingkreiskondensator ldi temp, 0xD0 ;680 00 (1/100 pF) hex 01 09 A0 sts C1_Schwingkreis0Bit, temp ;Kapazität wird durch Eichvorgang ldi temp, 0x01 ;genauer vermessen und der Wert sts C1_Schwingkreis1Bit, temp ;wird leicht angepasst ldi temp, 0x01 sts C1_Schwingkreis2Bit, temp ;verändert Wert gleich 652,02pF= hexFEB2 ;Eichkondensator 3,3 nF ldi temp, 0x10 ;3300nF=3300 00 (1/100 pF) = hex 50910 sts C2_Messergebnis0Bit, temp ;Eichkondensator der zugeschaltet wird ldi temp, 0x09 sts C2_Messergebnis1Bit, temp ldi temp, 0x05 sts C2_Messergebnis2Bit, temp ret ;###################################################################### ;Die Startfreqenz f1 (gemeint sind die Zählimpulse während 1/4 Sekunde) ;wird dreimal ermittelt, der Mittelwert gebildet gespeichert ;###################################################################### Messung_f1: ;Hier beginnt der Messvorgang f1 (1.Runde) rcall Zaehlschleife ;Übergabe der Messwerte und speichern im sram in r20, TCNT1L in r21, TCNT1H sts f1_Messung_eins0Bit, r20 sts f1_Messung_eins1Bit, r21 sts f1_Messung_eins2Bit, r22 ;Messvorgang f1(2.Runde) rcall Zaehlschleife ;Übergabe der Messwerte und speichern im sram in r20, TCNT1L in r21, TCNT1H sts f1_Messung_zwei0Bit, r20 sts f1_Messung_zwei1Bit, r21 sts f1_Messung_zwei2Bit, r22 ;Messvorgang f1(3.Runde) rcall Zaehlschleife ;Übergabe der Messwerte und speichern im sram in r20, TCNT1L in r21, TCNT1H sts f1_Messung_drei0Bit, r20 sts f1_Messung_drei1Bit, r21 sts f1_Messung_drei2Bit, r22 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;Mittelwert f2 bilden ; lds r16, f1_Messung_eins0Bit lds r17, f1_Messung_eins1Bit lds r18, f1_Messung_eins2Bit lds r19, f1_Messung_zwei0Bit lds r20, f1_Messung_zwei1Bit lds r21, f1_Messung_zwei2Bit lds r22, f1_Messung_drei0Bit lds r23, f1_Messung_drei1Bit lds r24, f1_Messung_drei2Bit ldi r25, 0x00 ;alle drei Messwerte addieren clc ;lösche carry-flag add r16, r19 ;Add low bytes adc r17, r20 ;Add higher bytes with carry adc r18, r21 ; clc ;lösche carry-flag add r22, r16 ;Add low bytes adc r23, r17 ;Add higher bytes with carry adc r24, r18 ; ;Zischenergebnis ist in r24|r23|r22 ; push r24 ;Verschieben der Register nach r22|r21|r20 push r23 ;und vorbereitend auf r23=0 setzen push r22 ; pop r20 ; pop r21 ; pop r22 ;Es soll der Mittelwert berechnet werden ldi r23, 0x00 ; ldi r16, 0x03 ;Divisor: geteilt durch drei ldi r17, 0x00 ldi r18, 0x00 ldi r19, 0x00 ;gerechnet wird mit Div32 Div32 == 32/32 Bit ;* dividend divisor result remainder ;* r20r21r22r23 / r16r17r18r19 = r20r21r22r23 r24r25r26r27 ; rcall Div32 ;Divisionroutine wird aufgerufen sts f1_Mittelwert0Bit, r20 ;Mittelwert f1 abspeichern sts f1_Mittelwert1Bit, r21 sts f1_Mittelwert2Bit, r22 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;f1quadrat berechnen und abspeichern mit Mul32 == 32x32 ; multiplier multiplicand result ; r20r21r22r23 x r16r17r18r19 = r20r21r22r23r24r25r26r27 ; ;Multiplikator (r20 bis r22) hat schon die richtigen Daten lds r20, f1_Mittelwert0Bit lds r21, f1_Mittelwert1Bit lds r22, f1_Mittelwert2Bit ldi r23, 0x00 ;r23 mit 0x00 belegen, da es nicht benötigt wird lds r16, f1_Mittelwert0Bit lds r17, f1_Mittelwert1Bit lds r18, f1_Mittelwert2Bit ldi r19, 0x00 rcall mul32 ; Multiplikationsroutine aufrufen sts f1quadrat0Bit, r20 ;Ergebnisse sichern sts f1quadrat1Bit, r21 sts f1quadrat2Bit, r22 sts f1quadrat3Bit, r23 sts f1quadrat4Bit, r24 ret ;Ende von Messung_f1 ;###################################################################### ;Hier beginnt der Messvorgang f2 ;Die Messfrequenz (gemeint sind die Zählimpulse während 1/4 Sekunde) ;bei zugeschaltetem unbekannten Kondensator ;wird dreimal ermittelt, der Mittelwert gebildet und in f2-Variablen ;gespeichert ;###################################################################### Messung_f2: rcall Zaehlschleife ;Übergabe der Messwerte und speichern im sram in r20, TCNT1L in r21, TCNT1H sts f2_Messung_eins0Bit, r20 sts f2_Messung_eins1Bit, r21 sts f2_Messung_eins2Bit, r22 ;Messvorgang f2(2.Runde) rcall Zaehlschleife ;Übergabe der Messwerte und speichern im sram in r20, TCNT1L in r21, TCNT1H sts f2_Messung_zwei0Bit, r20 sts f2_Messung_zwei1Bit, r21 sts f2_Messung_zwei2Bit, r22 ;Messvorgang f2(3.Runde) rcall Zaehlschleife ;Übergabe der Messwerte und speichern im sram in r20, TCNT1L in r21, TCNT1H sts f2_Messung_drei0Bit, r20 sts f2_Messung_drei1Bit, r21 sts f2_Messung_drei2Bit, r22 ;Mittelwert f2 bilden lds r16, f2_Messung_eins0Bit lds r17, f2_Messung_eins1Bit lds r18, f2_Messung_eins2Bit lds r19, f2_Messung_zwei0Bit lds r20, f2_Messung_zwei1Bit lds r21, f2_Messung_zwei2Bit lds r22, f2_Messung_drei0Bit lds r23, f2_Messung_drei1Bit lds r24, f2_Messung_drei2Bit ldi r25, 0x00 ;alle drei Messwerte addieren clc ;lösche carry-flag add r16, r19 ;Add low bytes adc r17, r20 ;Add higher bytes with carry adc r18, r21 ; clc ;lösche carry-flag add r22, r16 ;Add low bytes adc r23, r17 ;Add higher bytes with carry adc r24, r18 ; ; Zischenergebnis ist in r24|r23|r22 ;Mittelwert berechnen (durch drei teilen) ;Parameterübergabe r24|r23|r22 geteilt durch drei push r24 push r23 push r22 pop r20 pop r21 pop r22 ldi r23, 0x00 ;Divisor: geteilt durch drei ldi r16, 0x03 ldi r17, 0x00 ldi r18, 0x00 ldi r19, 0x00 ;gerechnet wird mit Div32 Div32 == 32/32 Bit ;*dd32uL:dd32uH / dv32uL:dv32uH = dres32uL:dres32uH (drem32uL:drem32uH) ;* dividend divisor result remainder ;* r20r21r22r23 / r16r17r18r19 = r20r21r22r23 r24r25r26r27 ;********************************************************************** rcall Div32 ;Divisionroutine wird aufgerufen ;Mittelwert f2 abspeichern sts f2_Mittelwert0Bit, r20 sts f2_Mittelwert1Bit, r21 sts f2_Mittelwert2Bit, r22 ;f1quadrat berechnen und abspeichern mit Mul32 == 32x32 ; mp32uL::mp32uH x mc32uL::mc32uH = m32uL::m32uH ; multiplier multiplicand result ; r20r21r22r23 x r16r17r18r19 = r20r21r22r23r24r25r26r27 ; ;Multiplikator ;r20 bis r22 haben schon die richtigen Daten ldi r23, 0x00 ;r23 mit 0x00 belegen, da es nicht benötigt wird ;Multiplikant ;mov r16, r20 ;mov r17, r21 ;mov r18, r22 ;mov r19, r23 lds r20, f2_Mittelwert0Bit lds r21, f2_Mittelwert1Bit lds r22, f2_Mittelwert2Bit ldi r23, 0x00 lds r16, f2_Mittelwert0Bit lds r17, f2_Mittelwert1Bit lds r18, f2_Mittelwert2Bit ldi r19, 0x00 rcall mul32 ; Multiplikationsroutine aufrufen sts f2quadrat0Bit, r20 ;Später ist nur die Speicherung von sts f2quadrat1Bit, r21 ;von f1Quadrat und f2Quadrat wichtig sts f2quadrat2Bit, r22 sts f2quadrat3Bit, r23 sts f2quadrat4Bit, r24 ret ;Ende von Messung_f2 ;##################################################################### ;Berechnung der unbekannten Kapazität ; ( (f1)^2 ) (f1)^2 * C1 ; Cx = ( ------ - 1 ) * C1 oder Cx = ----------- - C1 ; ( (f2)^2 ) (f2)^2 ;gewählt wird die zweite Variante, da sie leichter und mit weniger Fehlern ;berechnen ist (durch Rundungsfehler) ;Mit L1 ist der Kondensator um nunmehr geeichten Schwingkreis gemeint ;Er hat ca. 680 pF siehe: C1_Schwingkreis0Bit bis C1_Schwingkreis2Bit ;##################################################################### Berechnung_Cx: ; Berechnung von (f1)^2 * C1 mit dem Unterprogramm: mul40x24_64 ; mul40x24_64 Unsigned multiply 40bits with 24 bits and 64bits result. ;r19:r18:r17:r16:r15 * r14:r13:r12 = r27:r26:r25:r24:r23:r22:r21:r20 lds r15, f1quadrat0Bit lds r16, f1quadrat1Bit lds r17, f1quadrat2Bit lds r18, f1quadrat3Bit lds r19, f1quadrat4Bit lds r12, C1_Schwingkreis0Bit lds r13, C1_Schwingkreis1Bit lds r14, C1_Schwingkreis2Bit ;Aufruf der Multiplikationsroutine rcall mul40x24_64 ;Das Ergebnis ist in r27:r26:r25:r24:r23:r22:r21:r20 ;Berechnung von (f1)^2 * L1 mit div64_64/32 ; ----------- ; (f2)^2 ;mit Div64 ==>> 64/40=64 Bit register: r13 bis r31 ==>> Ergebnis r20 bis r27 ; dividend divisor result remainder ; r20r21r22r23 r24r25r26r27 / r16r17r18r19r13 = r20r21_bis_r27 r14r28r29r30r31 ; Divident ist noch richtig in den Registern ; Divisor lds r16, f2quadrat0Bit lds r17, f2quadrat1Bit lds r18, f2quadrat2Bit lds r19, f2quadrat3Bit lds r13, f2quadrat4Bit rcall Div64 ;Aufruf der Divisionsroutine ; Berechnung von (f1)^2 * L1 Sub64 == 64Bit-64Bit ; ----------- - C1 mit alle Zahlen müssen positiv sein ; (f2)^2 ; minuend subtrahend difference ; r20r21r22r23r24r25r26r27 - r12r13r14r15r16r17r18r19 = r20r21r22r23r24r25r26r27 lds r12, C1_Schwingkreis0Bit lds r13, C1_Schwingkreis1Bit lds r14, C1_Schwingkreis2Bit ldi r16, 0x00 ldi r17, 0x00 ldi r18, 0x00 ldi r19, 0x00 mov r15, r19 ; weil ldi r15, 0x00 nicht erlaubt rcall Sub64 ; Aufruf der Subtraktionsroutine sts C2_Messergebnis0Bit, r20 ;Ergebniss sichern sts C2_Messergebnis1Bit, r21 sts C2_Messergebnis2Bit, r22 sts C2_Messergebnis3Bit, r23 sts C2_Messergebnis4Bit, r24 sts C2_Messergebnis5Bit, r25 sts C2_Messergebnis6Bit, r26 sts C2_Messergebnis7Bit, r27 ret ;##################################################################### ;Hier entsteht die Eichroutine ;PC5:Cx PC4:Null ;##################################################################### Eichen: ldi temp1, 0x10 sts Eichkondensator0Bit, temp1 ;3300 00 pF= hex 50910 ldi temp1, 0x09 sts Eichkondensator1Bit, temp1 ldi temp1, 0x05 sts Eichkondensator2Bit, temp1 ldi temp1, 0x00 sts Eichkondensator3Bit, temp1 ;f1 messen ldi temp1, 0b00000000 ;Relais Cx und Null sind in Grundstellung out PORTC, temp1 rcall Messung_f1 ;2*als Warteschleife zum Entprellen der Relais rcall Messung_f1 ;f2 (Eichfrequenz) messen ldi temp1, 0b00010000 out PORTC, temp1 rcall Messung_f2 ;2*als Warteschleife zum Entprellen der Relais rcall Messung_f2 ;ldi temp1, 0b00000000 ;Vorsichtshalber wieder Grundstellung ;out PORTC, temp1 ;Berechnung von C_eich, d.h. C1 ca 780 pF wird genau ermittelt ; (f2)^2 * C2 C2=3300 pF +/- 1% (Eichkondessator ) ;C1 = ------------- in hex 50910 ; (f1)^2-(f2)^2 lds r16, Eichkondensator0Bit ;Berechnung vom [(f2)^2 * C2] lds r17, Eichkondensator1Bit ; lds r18, Eichkondensator2Bit ; clr r19 ; clr r15 ; ; lds r20, f2quadrat0Bit ; lds r21, f2quadrat1Bit ; lds r22, f2quadrat2Bit ; lds r23, f2quadrat3Bit ; lds r24, f2quadrat4Bit ; ; rcall Mul40 ;Mul40 == 40x24 Bit Unsigned Multiplication ; multiplier multiplicand result r28 Loop counter ;r20r21r22r23r24 x r16r17r18r19r15 = r20r21r22r23r24r25r26r27r28r29 push r20 ; push r21 ;Ergebnis auf dem Stack sichern push r22 ; push r23 ; push r24 ; push r25 ; push r26 ; push r27 ; lds r12, f2quadrat0Bit ;Berechnung von [(f1)^2-(f2)^2] lds r13, f2quadrat1Bit ; lds r14, f2quadrat2Bit ; lds r15, f2quadrat3Bit ; lds r16, f2quadrat4Bit ; ldi r17, 0x00 ; ldi r18, 0x00 ; ldi r19, 0x00 ; ; lds r20, f1quadrat0Bit ; lds r21, f1quadrat1Bit ; lds r22, f1quadrat2Bit ; lds r23, f1quadrat3Bit ; lds r24, f1quadrat4Bit : ldi r25, 0x00 ; ldi r26, 0x00 ; ldi r27, 0x00 ; ; rcall Sub64 ; Aufruf der Subtraktionsroutine ;minuend subtrahend difference ;(r20 bis r27) - (r12 bis 19) = (r20 bis r27) ldi r16, 0x00 ;r16 bis r 19 und r13 gleich Null setzen ldi r17, 0x00 ; ldi r18, 0x00 ; ldi r19, 0x00 ; mov r13, r19 ; mov r16, r20 ;Divisor für Division vorbereiten mov r17, r21 ; mov r18, r22 ; mov r19, r23 ; mov r13, r24 ; ;Berechnung von (f2)^2 * C2 C2=3300 pF (Eichkondensator) ; C1 = ------------- in hex 50910 ; (f1)^2-(f2)^2 pop r27 ;Divident für Division vorbereiten pop r26 pop r25 pop r24 pop r23 pop r22 pop r21 pop r20 rcall Div64 ; Division ; Div64: dividend divisor result Rest ;(r20 bis 27)/(r16 bis r19 r13)=(r20 bis r27) (r14 r28 bis r31) sts C1_Schwingkreis0Bit, r20 ;sichern der Ergebnisse sts C1_Schwingkreis1Bit, r21 ; sts C1_Schwingkreis2Bit, r22 ; ret ;##################################################################### ; ;Hier entsteht die Routine,die einen Nullabgleich vornimmt(ca.3-100pF) ; ;##################################################################### ;fa und f2 messen abspeichernin Cs0Bit bis Cs2Bit ;kleinere nullpunktverschiebungen werden korriegiert. Diese entstehen durch ;Anschlussdräte, lange Kabel , etc. Ohne externem Kondensator wird gemessen ;und die 'Restkapazität' abgezogen ;################################################################################ ;hier entsteht die Messwertanzeige ; wenn C2_Messergebnis4Bit größer null ist soll durch 1000 (dezimal) ; geteilt werden (1000dez = 03E8) Messwertanzeige_Cx: ; CLZ ;Clear Zero Flag ; tst r24 ;Prüfe auf null (wenn register gleich null wird Z-Flag gesetzt ;wenn Register gleich null springe nach anzeigen, sonst C2 durch 1000 teilen ; brne anzeigen ; clr r25 ; clr r26 ; clr r27 ;Divisor ; ldi r16, 0xE8 ; ldi r17, 0x03 ; clr r18 ; clr r19 ; clr r13 ;div64_64/40 Div64 ==>> 64/40=64 Bit Unsigned Division ; dividend divisor result remainder ; r20r21r22r23 r24r25r26r27 / r16r17r18r19r13 = r20r21_bis_r27 r14r28r29r30r31 ; rcall Div64 ;anzeigen: ;r20 bis r23 in BCD umwandeln und anzeigen ; in dem folgenden Unterprogramm wird der Befehl Bin4BCD verwendet ; Bin4BCD == 32-bit Binary to BCD conversion ; fbin0:fbin1:fbin2:fbin3 >>> tBCD0:tBCD1:tBCD2:tBCD3:tBCD4 ; hex dec ; r18r19r20r21 >>> r20r21r22r23r24 ; push r20 ; push r21 ; push r22 ; push r23 ;locate 1,1 ;LCD 1.Zeile Anfang ;(steht hier, weil so keine Register zerstört werden) ; pop r21 ; pop r20 ; pop r19 ; pop r18 ; rcall bin32Bit_in_BCD_an_LCD ;32Bit-Zahl umwandeln und auf dem LCD ausgeben ; ret ;##################################################################### ; Testanzeige ;##################################################################### anzeigen_test: locate 1,1 ;LCD 1.Zeile Anfang lds r18, f1_Messung_eins0Bit lds r19, f1_Messung_eins1Bit lds r20, f1_Messung_eins2Bit ldi r21, 0x00 rcall bin32Bit_in_BCD_an_LCD ;32Bit-Zahl umwandeln und auf dem LCD ausgeben locate 2,1 ;LCD 1.Zeile Anfang lds r18, f1_Messung_zwei0Bit lds r19, f1_Messung_zwei1Bit lds r20, f1_Messung_zwei2Bit ldi r21, 0x00 rcall bin32Bit_in_BCD_an_LCD ;32Bit-Zahl umwandeln und auf dem LCD ausgeben ret ;##################################################################### ; Unterprogramm Frequenzzähler ; gemessen werden die Impulse pro 1/4 Sekunde ;##################################################################### Anzeige_Frequenzzaehler: ;zaehlergebnis einlesen in r20, TCNT1L ; Zählegister auslesen für BCD Umwandlung in r21, TCNT1H ; ldi r23, 0x00 ; 0x00 für die beiden hohen ungenutzten Stellen rjmp test01 ; zaehlergebnis in frequenz umrechnen ; mal 2,5 d.h. mal dezimal 25 und durch 10 ; in hex mal 0x19 und durch 0x0A ;mal 0x19 d.h. mal dezimal 25 ldi r16, 0x19 ldi r17, 0x00 ldi r18, 0x00 ldi r19, 0x00 rcall mul32 ldi r16, 0x0A ;durch dez 10 durch 0x0A ldi r17, 0x00 ldi r18, 0x00 ldi r19, 0x00 rcall div32 ;Umrechnung in BCD und LCD-Anzeige vorbereiten test01: push r20 push r21 push r22 push r23 locate 2,1 ;Makro 1.Zeile 1. Spalte (verwendet r17r18) pop r21 pop r20 pop r19 pop r18 bin32Bit_in_BCD_an_LCD: ; 32-Bit-Zahl umwandeln und auf dem LCD ausgeben rcall Bin4BCD ;Bin4BCD == 32-bit Binary to BCD conversion ;fbin0:fbin1:fbin2:fbin3 > tBCD0:tBCD1:tBCD2:tBCD3:tBCD4 ; hex dec ; r18r19r20r21 >>> r20r21r22r23r24 tst tBCD4 ;Die hohen Register werden auf Null getestet und BRNE Stelle_vier ;nicht auf dem LCD angezeigt (wegen der Lesbarkeit) tst tBCD3 BRNE Stelle_drei tst tBCD2 BRNE Stelle_zwei tst tBCD1 BRNE Stelle_eins rjmp Stelle_null ; 32-Bit-Zahl auf dem LCD ausgeben Stelle_vier: mov temp1, tBCD4 rcall lcd_data_aus_speicher_in_hex Stelle_drei: mov temp1, tBCD3 rcall lcd_data_aus_speicher_in_hex Stelle_zwei: mov temp1, tBCD2 rcall lcd_data_aus_speicher_in_hex Stelle_eins: mov temp1, tBCD1 rcall lcd_data_aus_speicher_in_hex Stelle_null: mov temp1, tBCD0 rcall lcd_data_aus_speicher_in_hex ret Anzeige_start: ;kann später gelöscht werden locate 1,1 ldi temp1, 'F' rcall lcd_data ldi temp1, 'r' rcall lcd_data ldi temp1, 'e' rcall lcd_data ldi temp1, 'q' rcall lcd_data ldi temp1, 'u' rcall lcd_data ldi temp1, 'e' rcall lcd_data ldi temp1, 'n' rcall lcd_data ldi temp1, 'z' rcall lcd_data ldi temp1, 'm' rcall lcd_data ldi temp1, 'e' rcall lcd_data ldi temp1, 's' rcall lcd_data ldi temp1, 's' rcall lcd_data ldi temp1, 'e' rcall lcd_data ldi temp1, 'r' rcall lcd_data locate 2,12 ldi temp1, 'H' rcall lcd_data ldi temp1, 'z' rcall lcd_data ret ;Es fehlt noch: Einheit Hz angeben - Fehler berechnen d.h. ;Ungenauigkeit des Quarzes (100ppm): ;Zählungenauigkeit (wg. 1/4 Sek wird gemessen): 2,5 Digit ; Flanke des Signals und Zähleranfang und Ende stimmen natürlich ;nicht exakt überein, da sie nicht synchronisiert werden. ;############################################################## ;############################################################## ; **************** Hauptprogramm ******************** ;PD0 Schalter Null ;PD1 Schalter Cx ;PD2 Schalter Lx ;PD5 Signaleingang ;PD6 Relais Null Eichen zZ noch Relais an PortC ;PD7 Relais Cx messen PC5:Cx PC4:Null/Eichen ;############################################################## ;############################################################## .CSEG main: ; Stack initialisieren ldi temp1, LOW(RAMEND) ; LOW-Byte der obersten RAM-Adresse out SPL, temp1 ldi temp1, HIGH(RAMEND) ; HIGH-Byte der obersten RAM-Adresse out SPH, temp1 rcall Startkonfiguration rcall Eichen ;Schwingkreis eichen ;später sollen hier auch Leitungskapazitäten ;eliminiert werden - Nullpunktverschiebungen ldi temp1, 0b00000000 ;Grundschwingung wird gemessen out PORTC, temp1 ; rcall Messung_f1 ; rcall Messung_f1 ; Schalterabfrage: ;Was soll gemacht werden?????? ;SBIC PIND,0 ;Überspringe wenn Bit0 Null ist:PD0 ist Schalter-Null/Eichen ;RJMP Eichen ;d.h. Springe zum Label Eichen, wenn Taste gedrückt ;SBIC PIND,1 ;Überspringe wenn Bit 1 Null ist ::PD1 ist Schalter Cx-messen ;RJMP Cx ;Springe zum Label Cx ;SBIC PIND,2 ;Überspringe wenn Bit 2 Null ist ::PD2 ist Schalter Lx-messen ;RJMP Lx ;Springe zum Label Lx ;rjmp Schalterabfrage Cx: ldi temp1, 0b00100000 out PORTC, temp1 rcall Messung_f2 ;2* als Warteschleife zum Entprellen der Relais rcall Messung_f2 ;und Einschwingen des Schwingkreises rcall Berechnung_Cx Schalterabfrage2: ;SBIC PIND,0 ;Überspringe wenn Bit0 Null ist:PD0 ist Schalter-Null/Eichen ;RJMP Eichen ;d.h. Springe zum Label Eichen, wenn Taste gedrückt ;SBIC PIND,1 ;Überspringe wenn Bit1 Null ist:PD1 ist Schalter Cx-messen ;RJMP Cx ;Springe zum Label Cx ;SBIC PIND,2 ;Überspringe wenn Bit2 Null ist:PD2 ist Schalter Lx-messen ;RJMP Lx ;Springe zum Label Lx locate 1,1 ;LCD 2.Zeile Anfang Ausgabe an LCD (in hex) lds temp1, C2_Messergebnis7Bit rcall lcd_data_aus_speicher_in_hex lds temp1, C2_Messergebnis6Bit rcall lcd_data_aus_speicher_in_hex lds temp1, C2_Messergebnis5Bit rcall lcd_data_aus_speicher_in_hex lds temp1, C2_Messergebnis4Bit rcall lcd_data_aus_speicher_in_hex lds temp1, C2_Messergebnis3Bit rcall lcd_data_aus_speicher_in_hex lds temp1, C2_Messergebnis2Bit rcall lcd_data_aus_speicher_in_hex lds temp1, C2_Messergebnis1Bit rcall lcd_data_aus_speicher_in_hex lds temp1, C2_Messergebnis0Bit rcall lcd_data_aus_speicher_in_hex locate 2,1 ;LCD 2.Zeile Anfang Ausgabe an LCD (in hex) lds r18, C2_Messergebnis0Bit lds r19, C2_Messergebnis1Bit lds r20, C2_Messergebnis2Bit lds r21, C2_Messergebnis3Bit rcall Bin4BCD ;Bin4BCD == 32-bit Binary to BCD conversion push r20 ; hex dec push r21 ;r18r19r20r21 >>> r20r21r22r23r24 push r22 push r23 push r24 pop temp1 ;Ausgabe an den Bildschirm rcall lcd_data_aus_speicher_in_hex ldi temp1, 0x2E rcall lcd_data pop temp1 rcall lcd_data_aus_speicher_in_hex pop temp1 rcall lcd_data_Punkt_zwischen_Nibbles pop temp1 rcall lcd_data_aus_speicher_in_hex ldi temp1, 0x2C rcall lcd_data pop temp1 rcall lcd_data_aus_speicher_in_hex ldi temp1, 'p' rcall lcd_data ldi temp1, 'F' rcall lcd_data rjmp Cx ;§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§ ;§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§ loop5: nop rjmp loop5 rjmp Cx rcall lcd_clear ;Display löschen locate 1,1 ldi temp1, 'C' rcall lcd_data ldi temp1, 'x' rcall lcd_data ldi temp1, ' ' rcall lcd_data ldi temp1, 'e' rcall lcd_data ldi temp1, 'n' rcall lcd_data ldi temp1, 't' rcall lcd_data ldi temp1, 'f' rcall lcd_data ldi temp1, 'e' rcall lcd_data ldi temp1, 'r' rcall lcd_data ldi temp1, 'n' rcall lcd_data ldi temp1, 'e' rcall lcd_data ldi temp1, 'n' rcall lcd_data locate 2,1 locate 1,1 lds temp1, f1_Mittelwert2Bit rcall lcd_data_aus_speicher_in_hex lds temp1, f1_Mittelwert1Bit rcall lcd_data_aus_speicher_in_hex lds temp1, f1_Mittelwert0Bit rcall lcd_data_aus_speicher_in_hex ldi temp1, 0x3A rcall lcd_data lds temp1, f2_Mittelwert2Bit rcall lcd_data_aus_speicher_in_hex lds temp1, f2_Mittelwert1Bit rcall lcd_data_aus_speicher_in_hex lds temp1, f2_Mittelwert0Bit rcall lcd_data_aus_speicher_in_hex locate 2,1 rcall Eichen ; lds temp1, f1quadrat4Bit rcall lcd_data_aus_speicher_in_hex lds temp1, f1quadrat3Bit rcall lcd_data_aus_speicher_in_hex lds temp1, f1quadrat2Bit rcall lcd_data_aus_speicher_in_hex lds temp1, f1quadrat1Bit rcall lcd_data_aus_speicher_in_hex lds temp1, f1quadrat0Bit rcall lcd_data_aus_speicher_in_hex loop6: rjmp loop6 rjmp Eichen1 rcall Eichen locate 1,1 lds temp1, f2_Mittelwert2Bit rcall lcd_data_aus_speicher_in_hex lds temp1, f2_Mittelwert1Bit rcall lcd_data_aus_speicher_in_hex lds temp1, f2_Mittelwert0Bit rcall lcd_data_aus_speicher_in_hex ldi temp1, 0x3A rcall lcd_data lds temp1, C1_Schwingkreis2Bit rcall lcd_data_aus_speicher_in_hex lds temp1, C1_Schwingkreis1Bit rcall lcd_data_aus_speicher_in_hex lds temp1, C1_Schwingkreis0Bit rcall lcd_data_aus_speicher_in_hex locate 2,1 lds temp1, f1_Mittelwert2Bit rcall lcd_data_aus_speicher_in_hex lds temp1, f1_Mittelwert1Bit rcall lcd_data_aus_speicher_in_hex lds temp1, f1_Mittelwert0Bit rcall lcd_data_aus_speicher_in_hex ;f2quadrat4Bit f2_Mittelwert2Bit geprüft und OK ;f1quadrat4Bit f1_Mittelwert2Bit geprüft und OK ;Einschub Eichen Eichen1: rcall Eichen1 mov temp1, r27 rcall lcd_data_aus_speicher_in_hex mov temp1, r26 rcall lcd_data_aus_speicher_in_hex mov temp1, r25 rcall lcd_data_aus_speicher_in_hex mov temp1, r24 rcall lcd_data_aus_speicher_in_hex mov temp1, r23 rcall lcd_data_aus_speicher_in_hex mov temp1, r22 rcall lcd_data_aus_speicher_in_hex mov temp1, r21 rcall lcd_data_aus_speicher_in_hex mov temp1, r20 rcall lcd_data_aus_speicher_in_hex lds temp1, C1_Schwingkreis2Bit rcall lcd_data_aus_speicher_in_hex lds temp1, C1_Schwingkreis1Bit rcall lcd_data_aus_speicher_in_hex lds temp1, C1_Schwingkreis0Bit rcall lcd_data_aus_speicher_in_hex lds temp1, f1quadrat4Bit rcall lcd_data_aus_speicher_in_hex lds temp1, f1quadrat3Bit rcall lcd_data_aus_speicher_in_hex lds temp1, f1quadrat2Bit rcall lcd_data_aus_speicher_in_hex lds temp1, f1quadrat1Bit rcall lcd_data_aus_speicher_in_hex lds temp1, f1quadrat0Bit rcall lcd_data_aus_speicher_in_hex ;Ende einschub Eichen lds temp1, f2quadrat4Bit rcall lcd_data_aus_speicher_in_hex lds temp1, f2quadrat3Bit rcall lcd_data_aus_speicher_in_hex lds temp1, f2quadrat2Bit rcall lcd_data_aus_speicher_in_hex lds temp1, f2quadrat1Bit rcall lcd_data_aus_speicher_in_hex Lx: ldi temp1, 0x39 rcall lcd_data ;###################################################################### ;###################################################################### ;###################################################################### ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; LCD-Routinen ;; ;; ============ ;; temp ;; (c)andreas-s@web.de veraendert durch Jörn; temp1 ;; eingabezeichen: temp1 ;; temp2 ;; 4bit-Interface (muss ueber jeweils 1kOhm ;; ;; DB4-DB7: PB0-PD3 abgesichert werden) ; ;; RS: PB4 ;; ;; E: PB5 ;; ;; RW: an low !!!!!!! ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;sendet ein Datenbyte an das LCD lcd_data: mov temp2, temp1 ;"Sicherungskopie" für ;die Übertragung des 2.Nibbles swap temp1 ;Vertauschen andi temp1, 0b00001111 ;oberes Nibble auf Null setzen sbr temp1, 1<<4 ;entspricht 0b00010000 out PORTB, temp1 ;ausgeben rcall lcd_enable ;Enable-Routine aufrufen ;2. Nibble, kein swap da es schon ;an der richtigen stelle ist andi temp2, 0b00001111 ;obere Hälfte auf Null setzen sbr temp2, 1<<4 ;entspricht 0b00010000 out PORTB, temp2 ;ausgeben rcall lcd_enable ;Enable-Routine aufrufen rcall delay50us ;Delay-Routine aufrufen ret ;zurück zum Hauptprogramm ;sendet ein Datenbyte an das LCD ; temp1 wird ausgelesen und beide Nibbles in Ascii Zeichen umbewandelt ; und auf dem LCD ausgegeben ; von Joern lcd_data_aus_speicher_in_hex: ;verwendet r16r17 push temp1 ; Sichern von temp1 im Stack, unveraendert swap temp1 ;Vertauschen der Nibbles, damit die Zeichen in ;der richtigen Reihenfolge ausgegeben werden push temp1 ;Sichern von temp1 im Stack, veraendert andi temp1, 0b00001111 ;oberes Nibble auf Null setzen test: CPI temp1,0x0A BRGE Buchstaben ori temp1, 0x30 Ausgabe1: rcall lcd_data pop temp1 ; Zurückholen von temp1 vom Stack swap temp1 ;Vertauschen von oberem und unterem Nibbles andi temp1, 0b00001111 ;oberes Nibble auf Null setzen test2: CPI temp1,0x0A BRGE Buchstaben2 ori temp1, 0x30 Ausgabe2: rcall lcd_data pop temp1 ; temp1 wird wieder hergestellt ret ;zurück zum Hauptprogramm Buchstaben: clh cls cln clc ldi temp, 0x37 ; add temp1, temp ; rjmp Ausgabe1 ; Buchstaben2: clh cls cln clc ldi temp, 0x37 ; add temp1, temp ; rjmp Ausgabe2 ; ret lcd_data_Punkt_zwischen_Nibbles: push temp1 ; Sichern von temp1 im Stack, unveraendert swap temp1 ;Vertauschen der Nibbles, damit die Zeichen in ;der richtigen Reihenfolge ausgegeben werden push temp1 ;Sichern von temp1 im Stack, veraendert andi temp1, 0b00001111 ;oberes Nibble auf Null setzen ori temp1, 0x30 rcall lcd_data ldi temp1, 0x2E rcall lcd_data pop temp1 ; Zurückholen von temp1 vom Stack swap temp1 ;Vertauschen von oberem und unterem Nibbles andi temp1, 0b00001111 ;oberes Nibble auf Null setzen ori temp1, 0x30 rcall lcd_data pop temp1 ; temp1 wird wieder hergestellt ret ;zurück zum Hauptprogramm ;sendet einen Befehl an das LCD lcd_command: ;wie lcd_data, nur ohne RS zu setzen mov temp2, temp1 swap temp1 andi temp1, 0b00001111 out PORTB, temp1 rcall lcd_enable andi temp2, 0b00001111 out PORTB, temp2 rcall lcd_enable rcall delay50us ret ;erzeugt den Enable-Puls lcd_enable: sbi PORTB, 5 ;Enable high nop ;3 Taktzyklen warten nop nop cbi PORTB, 5 ;Enable wieder low ret ;Und wieder zurück ;Pause nach jeder Übertragung delay50us: ;50us Pause ldi temp1, $42 delay50us_:dec temp1 brne delay50us_ ret ;wieder zurück ;Längere Pause für manche Befehle delay5ms: ;5ms Pause ldi temp1, $21 WGLOOP0: ldi temp2, $C9 WGLOOP1: dec temp2 brne WGLOOP1 dec temp1 brne WGLOOP0 ret ;wieder zurück ;Initialisierung: muss ganz am Anfang des Programms aufgerufen werden lcd_init: ldi temp3,50 powerupwait: rcall delay5ms dec temp3 brne powerupwait ldi temp1, 0b00000011 ;muss 3mal hintereinander gesendet out PORTB, temp1 ;werden zur Initialisierung rcall lcd_enable ;1 rcall delay5ms rcall lcd_enable ;2 rcall delay5ms rcall lcd_enable ;und 3! rcall delay5ms ldi temp1, 0b00000010 ;4bit-Modus einstellen out PORTB, temp1 rcall lcd_enable rcall delay5ms ldi temp1, 0b00101000 ;noch was einstellen... rcall lcd_command ldi temp1, 0b00001100 ;...nochwas... rcall lcd_command ldi temp1, 0b00000100 ;endlich fertig rcall lcd_command ret ;Sendet den Befehl zur Löschung des Displays lcd_clear: ldi temp1, 0b00000001 ;Display löschen rcall lcd_command rcall delay5ms ret ;###################################################################### ;###################################################################### ;###################################################################### ;* ;* M A T H E R O U T I N E S ;* ;* von joern Bartels (klettermaxi@web.de) August 2003 ;* leicht verändert und gekürzt ;* Folgende Quellen wurden verändert und erweitert: ;* (C) ATMEL Corporation (mailto:avr@atmel.com) ;* (C) 1999-2001 Andre Birua (mailto:birua@hotmail.com) ;* Target MCU: AVR Mega 8 oder andere Mega (Mega wegen mul-Befehl) ;*Alle Routinen sind für nur für ganze positive Zahlen ; ;* Number of words & cycles (Min/Max) c o m m e n t s ;* "ADD32" 6 4/5 Size of Add32sign ;* "SUB32" 16 6/15 Size of Sub32sign ;* "MUL32" 24 460/556 Size of Mul32b, based on AVR200 16x16 unsigned ;* "DIV32" 28 528/688 Size of Div32b, based on AVR200 16/16 unsigned ;* "COM32" 5 4/4 Part of Div32 ;* "Bin2BCD" 22 19/177 Equivalent of AVR204, but smaller & much faster ;* "Bin4BCD" 37 515/671 Based on AVR204 16-bit Bin to BCD conversion ;*************************************************************************** ;*************************************************************************** ;* Add32 == 32+32 Bit Unsigned Addition ;* add1L::add1H + add2L::add2H = add1L::add1H ;* item item sum ;* r20r21r22r23 + r16r17r18r19 = r20r21r22r23 ;*************************************************************************** .def add20 = r16 ; item 2 byte 0 (LSB) .def add21 = r17 ; item 2 byte 1 .def add22 = r18 ; item 2 byte 2 .def add23 = r19 ; item 2 byte 3 (MSB) .def add10 = r20 ; item 1 byte 0 (LSB) .def add11 = r21 ; item 1 byte 1 .def add12 = r22 ; item 1 byte 2 .def add13 = r23 ; item 1 byte 3 (MSB) Add32sign: brts Sub32sign ; Add32: add add10,add20 ;Add low bytes adc add11,add21 ;Add higher bytes with carry adc add12,add22 ; adc add13,add23 ; ret ; ;*************************************************************************** ;* Sub32 == 32-32 Bit Unsigned Subtraction ;* sub1L::sub1H - sub2L::sub2H = sub1L::sub1H ;* minuend subtrahend difference ;* r20r21r22r23 - r16r17r18r19 = r20r21r22r23 ;*************************************************************************** .def sub20 = r16 ; subtrahend byte 0 (LSB) .def sub21 = r17 ; subtrahend byte 1 .def sub22 = r18 ; subtrahend byte 2 .def sub23 = r19 ; subtrahend byte 3 (MSB) .def sub10 = r20 ; minuend byte 0 (LSB) .def sub11 = r21 ; minuend byte 1 .def sub12 = r22 ; minuend byte 2 .def sub13 = r23 ; minuend byte 3 (MSB) Sub32sign: clt ;sign + Sub32: sub sub10,sub20 ;Subtract low bytes sbc sub11,sub21 ;Subtract higher bytes with carry sbc sub12,sub22 ; sbc sub13,sub23 ; ret ;return ;*************************************************************************** ;* Sub64 == 64-64 Bit alle Zahlen und Ergebnisse müssen positiv sein ;* sub1L::sub1H - sub2L::sub2H = sub1L::sub1H ;* minuend subtrahend difference ;* r20r21r22r23r24r25r26r27 - r12r13r14r15r16r17r18r19 = r20r21r22r23r24r25r26r27 ;*************************************************************************** .def sub20 = r12 ; subtrahend byte 0 (LSB) .def sub21 = r13 ; subtrahend byte 1 .def sub22 = r14 ; subtrahend byte 2 .def sub23 = r15 ; subtrahend byte 3 (MSB) .def sub24 = r16 ; subtrahend byte 4 (MSB) .def sub25 = r17 ; subtrahend byte 5 (MSB) .def sub26 = r18 ; subtrahend byte 6 (MSB) .def sub27 = r19 ; subtrahend byte 7 (MSB) .def sub10 = r20 ; minuend byte 0 (LSB) .def sub11 = r21 ; minuend byte 1 .def sub12 = r22 ; minuend byte 2 .def sub13 = r23 ; minuend byte 3 (MSB) .def sub14 = r24 ; minuend byte 4 (MSB) .def sub15 = r25 ; minuend byte 5 (MSB) .def sub16 = r26 ; minuend byte 6 (MSB) .def sub17 = r27 ; minuend byte 7 (MSB) Sub64sign:clt ;sign + Sub64: sub sub10,sub20 ;Subtract low bytes sbc sub11,sub21 ;Subtract higher bytes with carry sbc sub12,sub22 ; sbc sub13,sub23 ; sbc sub14,sub24 ; sbc sub15,sub25 ; sbc sub16,sub26 ; sbc sub17,sub27 ; ret ;*************************************************************************** ;* Mul32 == 32x32 Bit Unsigned Multiplication ;* mp32uL::mp32uH x mc32uL::mc32uH = m32uL::m32uH ;* multiplier multiplicand result r28 Loop counter ;* r20r21r22r23 x r16r17r18r19 = r20r21r22r23r24r25r26r27 ;*************************************************************************** .def mc32u0 =r16 ; multiplicand byte 0 (LSB) .def mc32u1 =r17 ; multiplicand byte 1 .def mc32u2 =r18 ; multiplicand byte 2 .def mc32u3 =r19 ; multiplicand byte 3 (MSB) .def mp32u0 =r20 ; multiplier byte 0 (LSB) .def mp32u1 =r21 ; multiplier byte 1 .def mp32u2 =r22 ; multiplier byte 2 .def mp32u3 =r23 ; multiplier byte 3 (MSB) .def m32u0 =r20 ; result byte 0 (LSB) .def m32u1 =r21 ; result byte 1 .def m32u2 =r22 ; result byte 2 .def m32u3 =r23 ; result byte 3 .def m32u4 =r24 ; result byte 4 .def m32u5 =r25 ; result byte 5 .def m32u6 =r26 ; result byte 6 .def m32u7 =r27 ; result byte 7 (MSB) .def mcnt32u =r28 ; loop counter Mul32b: clr mc32u1 ;multiplicand is one byte Mul32w: clr mc32u2 ; two bytes Mul32t: clr mc32u3 ; three bytes Mul32: clr m32u7 ;clear 4 highest bytes of result clr m32u6 ; clr m32u5 ; sub m32u4,m32u4 ;and carry ldi mcnt32u,33 ;init loop counter m32u_loop:ror m32u3 ;rotate result and multiplier ror m32u2 ; ror m32u1 ; ror m32u0 ; dec mcnt32u ;decrement loop counter breq Return ;if counter zero return brcc m32u_skip ;if bit 0 of multiplier set add m32u4,mc32u0 ; add multiplicand to result adc m32u5,mc32u1 ; adc m32u6,mc32u2 ; adc m32u7,mc32u3 ; m32u_skip:ror m32u7 ;shift right result byte 7 ror m32u6 ;rotate right result ror m32u5 ; ror m32u4 ; rjmp m32u_loop ; Return: ret ;****************************************************************************** ;* mul16x16_32 Unsigned multiply of two 16bits numbers with 32bits result. ;* r19:r18:r17:r16 = r23:r22 * r21:r20 ;* STATISTICS: Cycles : 17 + ret Words : 13 + ret ;* Register usage: r0 to r2 and r16 to r23 (11 registers) ;****************************************************************************** mul16x16_32: clr r2 mul r23, r21 ; ah * bh movw r19:r18, r1:r0 mul r22, r20 ; al * bl movw r17:r16, r1:r0 mul r23, r20 ; ah * bl add r17, r0 adc r18, r1 adc r19, r2 mul r21, r22 ; bh * al add r17, r0 adc r18, r1 adc r19, r2 ret ;************************************************************************ ;eventuell fehlerhaft ;* FUNCTION von Joern Bartels (c) 08.08.2003 klettermaxi@web.de ;* mul40x24_64 Unsigned multiply 40bits with 24 bits and 64bits result. ;* r27:r26:r25:r24:r23:r22:r21:r20 = r19:r18:r17:r16:r15 * r14:r13:r12 ;* Cycles : dezimal 95 + ret Words : dezimal 80 + ret ;* Register usage: r0 to r2 and r12 to r27 (16 registers) ;* r0 low bit von mul ;* r1 hight bit von mul ;* r2 Zwischenregister zum Rechnen ;************************************************************************ ; r19:r18:r17:r16:r15 * r14:r13:r12 ; 15 12 >>21|20 ; 16 12 ; 17 12 >>23|22 ; 18 12 ; 19 12 >>25|24 ; 15 13 ; 16 13 ; 17 13 ; 18 13 ; 19 13 ; 15 14 X X ; 16 14 ; 17 14 ; 18 14 ;19 14 >>27|26 mul40x24_64: clr r2 clr r3 clr r4 clr r5 clr r6 clr r7 mul r15, r12 movw r21:r20, r1:r0 mul r17, r12 movw r23:r22, r1:r0 mul r19, r12 movw r25:r24, r1:r0 mul r19, r14 movw r27:r26, r1:r0 mul r16, r12 add r21, r0 adc r22, r1 adc r23, r2 adc r24, r2 adc r25, r2 adc r26, r2 adc r27, r2 mul r15, r13 add r21, r0 adc r22, r1 adc r23, r2 adc r24, r2 adc r25, r2 adc r26, r2 adc r27, r2 mul r16, r13 add r22, r0 adc r23, r1 adc r24, r2 adc r25, r2 adc r26, r2 adc r27, r2 mul r15, r14 add r22, r0 adc r23, r1 adc r24, r2 adc r25, r2 adc r26, r2 adc r27, r2 mul r18, r12 add r23, r0 adc r24, r1 adc r25, r2 adc r26, r2 adc r27, r2 mul r17, r13 add r23, r0 adc r24, r1 adc r25, r2 adc r26, r2 adc r27, r2 mul r16, r14 add r23, r0 adc r24, r1 adc r25, r2 adc r26, r2 adc r27, r2 mul r18, r13 add r24, r0 adc r25, r1 adc r26, r2 adc r27, r2 mul r17, r14 add r24, r0 adc r25, r1 adc r26, r2 adc r27, r2 mul r19, r13 add r25, r0 adc r26, r1 adc r27, r2 mul r18, r14 add r25, r0 adc r26, r1 adc r27, r2 ret ;*************************************************************************** ;* Div32 == 32Bit/32Bit Unsigned Division ;* dd32uL::dd32uH / dv32uL::dv32uH = dres32uL::dres32uH (drem32uL::drem32uH) ;* dividend divisor result remainder ;* r20r21r22r23 / r16r17r18r19 = r20r21r22r23 r24r25r26r27 ;*************************************************************************** .def dv32u0 =r16 ; divisor byte 0 (LSB) .def dv32u1 =r17 ; divisor byte 1 .def dv32u2 =r18 ; divisor byte 2 .def dv32u3 =r19 ; divisor byte 3 (MSB) .def dres32u0 =r20 ; result byte 0 (LSB) .def dres32u1 =r21 ; result byte 1 .def dres32u2 =r22 ; result byte 2 .def dres32u3 =r23 ; result byte 3 (MSB) .def dd32u0 =r20 ; dividend byte 0 (LSB) .def dd32u1 =r21 ; dividend byte 1 .def dd32u2 =r22 ; dividend byte 2 .def dd32u3 =r23 ; dividend byte 3 (MSB) .def drem32u0 =r24 ; remainder byte 0 (LSB) .def drem32u1 =r25 ; remainder byte 1 .def drem32u2 =r26 ; remainder byte 2 .def drem32u3 =r27 ; remainder byte 3 (MSB) .def dcnt32u =r28 ; loop counter Div32b: clr dv32u1 ;divisor is one byte Div32w: clr dv32u2 ; two bytes Div32t: clr dv32u3 ; three bytes Div32: clr drem32u0 ;clear 4 lower remainde byte clr drem32u1 ; clr drem32u2 ; sub drem32u3,drem32u3;and carry ldi dcnt32u,33 ;init loop counter d32u_loop: rol dd32u0 ;shift left dividend rol dd32u1 ; rol dd32u2 ; rol dd32u3 ; dec dcnt32u ;decrement loop counter breq Com32 ;if counter zero invert result rol drem32u0 ;shift dividend into remainder rol drem32u1 ; rol drem32u2 ; rol drem32u3 ; sub drem32u0,dv32u0 ;remainder = remainder - divisor sbc drem32u1,dv32u1 ; sbc drem32u2,dv32u2 ; sbc drem32u3,dv32u3 ; brcc d32u_loop ;clear carry to be shifted into res add drem32u0,dv32u0 ;if result negative adc drem32u1,dv32u1 ; restore remainder adc drem32u2,dv32u2 ; adc drem32u3,dv32u3 ; rjmp d32u_loop ; set carry to be shifted into res Com32: com sub10 ; & com sub11 ; (com result) com sub12 ; com sub13 ; return set carry after com ret ;*************************************************************************** ;* Div64 ==>> 64/32=32 Bit Unsigned Division ca. 1800 cycles ;* register: r13 bis r31 ==>> Ergebnis r20 bis r27 ;* dd32uL::dd32uH: / dv32uL::dv32uH=dres32uL::dres32uH (drem32uL::drem32uH) ;* dividend divisor result remainder ;*r20r21r22r23 r24r25r26r27/r16r17r18r19r13=r20r21_bis_r27 r14r28r29r30r31 ;*************************************************************************** .def dv64u0 =r16 ; divisor byte 0 (LSB) .def dv64u1 =r17 ; divisor byte 1 .def dv64u2 =r18 ; divisor byte 2 .def dv64u3 =r19 ; divisor byte 3 (MSB) .def dv64u4 =r13 ; divisor byte 4 (MSB) .def dres64u0 =r20 ; result byte 0 (LSB) .def dres64u1 =r21 ; result byte 1 .def dres64u2 =r22 ; result byte 2 .def dres64u3 =r23 ; result byte 3 (MSB) .def dres64u4 =r24 ; result byte 3 (MSB) .def dres64u5 =r25 ; result byte 3 (MSB) .def dres64u6 =r26 ; result byte 3 (MSB) .def dres64u7 =r27 ; result byte 3 (MSB) .def dd64u0 =r20 ; dividend byte 0 (LSB) .def dd64u1 =r21 ; dividend byte 1 .def dd64u2 =r22 ; dividend byte 2 .def dd64u3 =r23 ; dividend byte 3 (MSB) .def dd64u4 =r24 ; dividend byte 4 (MSB) .def dd64u5 =r25 ; dividend byte 5 (MSB) .def dd64u6 =r26 ; dividend byte 6 (MSB) .def dd64u7 =r27 ; dividend byte 7 (MSB) .def drem64u0 =r14 ; remainder byte 0 (LSB) .def drem64u1 =r28 ; remainder byte 1 .def drem64u2 =r29 ; remainder byte 2 .def drem64u3 =r30 ; remainder byte 3 (MSB) .def drem64u4 =r31 ; remainder byte 4 (MSB) .def dcnt64u =r15 ; loop counter Div64b: clr dv64u1 ;divisor is one byte Div64w: clr dv64u2 ; two bytes Div64t: clr dv64u3 ; three bytes Div64f: clr dv64u4 ; three bytes Div64: ldi r28, 0x41 ;41 init loop counter (bei mul32 waren es 33 oder 0x21) mov dcnt64u, r28 ;wegen r15 (ldi nicht erlaubt) clr drem64u0 ;clear 4 lower remainde byte clr drem64u1 ; clr drem64u2 ; clr drem64u3 ; sub drem64u4,drem64u4;and carry d64u_loop: rol dd64u0 ;shift left dividend rol dd64u1 ; rol dd64u2 ; rol dd64u3 rol dd64u4 ; rol dd64u5 ; rol dd64u6 rol dd64u7 ; ; dec dcnt64u ;decrement loop counter breq Com64 ;if counter zero invert result rol drem64u0 ;shift dividend into remainder rol drem64u1 ; rol drem64u2 ; rol drem64u3 ; rol drem64u4 ; sub drem64u0,dv64u0 ;remainder = remainder - divisor sbc drem64u1,dv64u1 ; sbc drem64u2,dv64u2 ; sbc drem64u3,dv64u3 ; sbc drem64u4,dv64u4 ; brcc d64u_loop ;clear carry to be shifted into res add drem64u0,dv64u0 ;if result negative adc drem64u1,dv64u1 ; restore remainder adc drem64u2,dv64u2 ; adc drem64u3,dv64u3 ; adc drem64u4,dv64u4 ; rjmp d64u_loop ; set carry to be shifted into res Com64: com r20 ;com divident (Joern) & com r21 ; (com result) com r22 ; com r23 ; com r24 ; com r25 ; com r26 ; com r27 ; return set carry after com ret ; ;*************************************************************************** ;* Bin2BCD == 16-bit Binary to BCD conversion ;* fbinL:fbinH >>> tBCD0:tBCD1:tBCD2 ;* hex dec ;* r16r17 >>> r20r21r22 ;*************************************************************************** .def fbinL =r16 ; binary value Low byte .def fbinH =r17 ; binary value High byte .def tBCD0 =r20 ; BCD value digits 1 and 0 .def tBCD1 =r21 ; BCD value digits 3 and 2 .def tBCD2 =r22 ; BCD value digit 4 (MSD is lowermost nibble) Bin2BCD20: mov r16,r20 ;for compatibility with Math32 mov r17,r21 ; Bin2BCD16: ldi tBCD2,0xff ;initialize digit 4 binBCD_4: inc tBCD2 ; subi fbinL,low(10000);subiw fbin,10000 sbci fbinH,high(10000) brcc binBCD_4 ; ldi tBCD1,0x9f ;initialize digits 3 and 2 binBCD_3: subi tBCD1,0x10 ; subi fbinL,low(-1000);subiw fbin,1000 sbci fbinH,high(-1000) brcs binBCD_3 ; binBCD_2: inc tBCD1 ; subi fbinL,low(100) ;subiw fbin,100 sbci fbinH,high(100) ; brcc binBCD_2 ; ldi tBCD0,0xa0 ;initialize digits 1 and 0 binBCD_1: subi tBCD0,0x10 ; subi fbinL,-10 ;subiw fbin,10 brcs binBCD_1 ; add tBCD0,fbinL ; binBCD_ret: ret ; .equ Bin2BCD=Bin2BCD20 ;default registers BIN to BCD call ;*************************************************************************** ;* Bin4BCD == 32-bit Binary to BCD conversion ;* fbin0:fbin1:fbin2:fbin3 >>> tBCD0:tBCD1:tBCD2:tBCD3:tBCD4 ;* hex dec ;* r18r19r20r21 >>> r20r21r22r23r24 ;*************************************************************************** .def fbin0 =r18 ; binary value byte 0 (LSB) .def fbin1 =r19 ; binary value byte 1 .def fbin2 =r20 ; binary value byte 2 .def fbin3 =r21 ; binary value byte 3 (MSB) .def tBCD0 =r20 ; BCD value digits 1 and 0 (same as fbin2) .def tBCD1 =r21 ; BCD value digits 3 and 2 (same as fbin3) .def tBCD2 =r22 ; BCD value digits 4 and 5 .def tBCD3 =r23 ; BCD value digits 6 and 7 .def tBCD4 =r24 ; BCD value digits 8 and 9 Bin4BCD: rcall Bin2BCD20 ; clr tBCD3 ;initial highest bytes of result ldi tBCD4,0xfe ; binBCD_loop: subi tBCD0,-0x33 ;add 0x33 to digit 1 and 0 sbrs tBCD0,3 ;if bit 3 clear subi tBCD0,0x03 ; sub 3 sbrs tBCD0,7 ;if bit 7 clear subi tBCD0,0x30 ; sub $30 subi tBCD1,-0x33 ;add 0x33 to digit 3 and 2 sbrs tBCD1,3 ;if bit 3 clear subi tBCD1,0x03 ; sub 3 sbrs tBCD1,7 ;if bit 7 clear subi tBCD1,0x30 ; sub $30 subi tBCD2,-0x33 ;add 0x33 to digit 5 and 4 sbrs tBCD2,3 ;if bit 3 clear subi tBCD2,0x03 ; sub 3 sbrs tBCD2,7 ;if bit 7 clear subi tBCD2,0x30 ; sub $30 lsl fbin0 ; rol fbin1 ;shift lower word rol tBCD0 ;through all bytes rol tBCD1 ; rol tBCD2 ; rol tBCD3 ; rol tBCD4 ; brmi binBCD_loop ;7 shiftes w/o correction of MSD rol fbinH ;since Bin2BCD fbinH = 0xff brcc binBCD_ret ; so as to do 16 shiftes in total subi tBCD3,-0x33 ;add 0x33 to digit 7 and 6 sbrs tBCD3,3 ;if bit 3 clear subi tBCD3,0x03 ; sub 3 sbrs tBCD3,7 ;if bit 7 clear subi tBCD3,0x30 ; sub $30 subi tBCD4,-0x03 ;add 0x03 to digit 8 only sbrs tBCD4,3 ;if bit 3 clear subi tBCD4,0x03 ; sub 3 rjmp binBCD_loop ; ;*************************************************************************** ;* Mul40 == 40x24 Bit Unsigned Multiplication ;* mp40uL::mp40uH x mc40uL::mc40uH = m40uL::m40uH ;* multiplier multiplicand result r28 Loop counter ;* r20r21r22r23r24 x r16r17r18r19r15 = r20r21r22r23r24r25r26r27r28r29 ;*************************************************************************** .def mc40u0 =r16 ; multiplicand byte 0 (LSB) .def mc40u1 =r17 ; multiplicand byte 1 .def mc40u2 =r18 ; multiplicand byte 2 .def mc40u3 =r19 ; multiplicand byte 3 (MSB) .def mc40u4 =r15 ; multiplicand byte 4 (MSB) .def mp40u0 =r20 ; multiplier byte 0 (LSB) .def mp40u1 =r21 ; multiplier byte 1 .def mp40u2 =r22 ; multiplier byte 2 .def mp40u3 =r23 ; multiplier byte 3 (MSB) .def mp40u4 =r24 ; multiplier byte 4 (MSB) .def m40u0 =r20 ; result byte 0 (LSB) .def m40u1 =r21 ; result byte 1 .def m40u2 =r22 ; result byte 2 .def m40u3 =r23 ; result byte 3 .def m40u4 =r24 ; result byte 4 .def m40u5 =r25 ; result byte 5 .def m40u6 =r26 ; result byte 6 .def m40u7 =r27 ; result byte 7 (MSB) .def m40u8 =r28 ; result byte 8 (MSB) .def m40u9 =r29 ; result byte 9 (MSB) .def mcnt40u =r30 ; loop counter Mul40: clr m40u7 ;clear 4 highest bytes of result clr m40u8 ; clr m40u9 ; clr m40u6 ; clr m40u5 sub m40u5,m40u5 ;and carry ldi mcnt40u,0x29 ;init loop counter früher 33 m40u_loop:ror m40u4 ;rotate result and multiplier ror m40u3 ror m40u2 ; ror m40u1 ; ror m40u0 ; dec mcnt40u ;decrement loop counter breq mu40Return ;if counter zero return brcc m40u_skip ;if bit 0 of multiplier set add m40u5,mc40u0 ; add multiplicand to result adc m40u6,mc40u1 ; adc m40u7,mc40u2 ; adc m40u8,mc40u3 ; adc m40u9,mc40u4 ; m40u_skip:ror m40u9 ror m40u8 ror m40u7 ;shift right result byte 7 ror m40u6 ;rotate right result ror m40u5 ; rjmp m40u_loop ; mu40Return: ret ;###################################################################### ;###################################################################### ; ; E N D E D E R ; ; M A T H E R O U T I N E S ; ;###################################################################### ;######################################################################