;**************************************************************************************** ;* * ;* Digitales Messgerät für Drehzal und Schnittgeschwindigkeit * ;* ---------------------------------------------------------- * ;* * ;* Eine Taktscheibe die an der Motorachse befestigt ist, wird mit einer * ;* Gabellichtschranke abgetastet. Der Kollektor des Fototransistor ist mit dem * ;* INT1 Pin des Tiny2313 verbunden. Die Taktscheibe hat 15 Bohrungen. Da sowohl * ;* positive als auch negative Flanken am INT0 ausgewertet werden, erhält man 30 * ;* Impulse pro Umdrehung. Diese werden für 2 Sekunden gezählt, und man erhält die * ;* Umdrehungen pro Minute (UPM). Aus UPM und dem Werkzeugdurchmesser, können dann die * ;* andere Einheiten wie z.B.Schnittgeschwindigkeit m/min berechnet werden. * ;* Formeln: * ;* Umdrehungen/Sek = UPM / 60 * ;* m/min = d * pi * UPM / 1000 * ;* m/sek = m/min / 60 * ;* * ;* Messmethode * ;* ----------- * ;* Da eine Messung 2 Sekunden dauert, hätte das aktualisieren des Displays alle 2 * ;* Sekunden große Sprünge zur Folge und ließe sich nur schwer ablesen. Deshalb * ;* speichere ich im SRam alle 1/4 Sekunde den neusten Messwert ab und addiere die * ;* letzten 8 Messwerte. Eine Messung dauert so zwar immer noch 2 Sekunden allerdings * ;* ist eine Tendez nach oben oder unten sofort zu erkennen, wenn man die Drehzahl * ;* ändert. * ;* * ;* Autor: Jürgen Woetzel www.avr-projekte.de * ;* Version vom 29.09.2013 * ;**************************************************************************************** #include "tn2313def.inc" ;************************************************************************************************ ;Config für bedingte Assemblierung ;************************************************************************************************ ;------------------------------------------------------------------------------------------------ ;Auf Unterschiedliche Art, den Gesamtmesswert bilden ;------------------------------------------------------------------------------------------------ ;.set interpol = 0 ;Addieren. (Messung nicht interpolieren). ;.set interpol = 1 ;Stärkeres gewichten der neusten Messungen. ;Die Maximale Drehzahl darf bei interpol=1 nur noch 32.767 betragen. .set interpol = 2 ;1 Sekunde messen, Zwischenwerte interpolieren ;.set interpol = 3 ;Hochrechnen der letzten Sekunde. 1 Sekunde messen, dann *2 ;.set interpol = 4 ;Hochrechnen der letzten 500mS. 1/2 Sekunde messen, dann *4, runden ! ;------------------------------------------------------------------------------------------------ ;Die letzte Stelle runden. Beruhigt die Anzeige auf Kosten der Auflösung ;------------------------------------------------------------------------------------------------ .set genau = 0 ;Wenn 1, nicht runden. Wenn 0, die Letzte Ziffer = 0 oder 5 .set strong = 0 ;Starkes runden. Wenn strong=1 UND genau=0 zeigt die letzte Stelle ständig Null ;------------------------------------------------------------------------------------------------ ;Startwerte ;------------------------------------------------------------------------------------------------ .set startdm = 30 ;Werkzeugdurchmesser(*10) beim einschalten 30=3,0 120=12 .set everon = 0 ;Werkzeugdurchmesser in allen Modi anzeigen (0=aus) .set everchange = 0 ;Werkzeugdurchmesser in allen Modi einstellbar (0=aus) ;************************************************************************************************* ;--------------------------------------------------------- ;Register ;--------------------------------------------------------- .def null = r0 ;Konstante 0 .def stat = r1 ;Statusregister .def cnt_low = r2 ;Zähler int1 (Flanken der Lichtschranke) .def cnt_high= r3 .def Pointer = r4 ;Zeiger auf Messwert .def ms = r5 ;Zähler Timerinterrupt .def taste1 = r6 ;Merkt sich den Tastenzustand im Timerinterrupt (entprellen) .def taste2 = r7 .def taste3 = r8 .def pi_mal_d_1 =r9 ;Zwischenergebniss (3,14*d) 24Bit .def pi_mal_d_2 =r10 .def pi_mal_d_3 =r11 .def tick =r12 ;Zähler Timerinterrupt .def repeat =r13 ;Repeatzähler Up -Downtaste .def sechzig =r14 ;Konstante .def temp1 = r16 ;Arbeitsregister .def temp2 = r17 .def temp3 = r18 .def temp4 = r19 .def temp5 = r20 .def itemp = r21 ;Arbeitsregister Timerinterrupt .def durchmesser = r22 ;Werkzeugdurchmesser *10 (Festkomma, eine Stelle) .def flags = r23 ;Programmflags .equ error = 7 ;Überlauf .equ enter = 6 ;Entprellte Tasten .equ down = 5 .equ up = 4 .equ runden = 3 ;Wird gesetzt wenn bei Division /10 der Rest >=5 ist .equ mode1 = 1 ;Entscheiden den Anzeigemodus (UPM, m/min usw.) .equ mode0 = 0 ;-------------------------------------------------------------------------- ;SRamadressen ;-------------------------------------------------------------------------- .dseg .org $60 Segbuffer: .byte 8 ;Inhalt der 7 Anzeigen und der 4 LEDs. CntBuffer: .byte 16 ;8 Words (low/high). Jedes Wort enthält 1/8 (250mS) der Gesamtmessung (2Sek.) ;-------------------------------------------------------------------------- .cseg .org 0 rjmp init .org INT1addr rjmp Ext_int .org OC1Aaddr ;Timer0 Overflow Interrupt rjmp Timer_int ;************************************************************************** ; Externer Interrupt (int1) ;************************************************************************** Ext_int: ;------------------------------------------------------------------------------- ;Eine Codierscheibe mit 15 Bohrungen wird von einer Gabellichschranke abgetastet. ;Da der INT1 Interrupt bei steigender und fallender Flanke aufgerufen wird, erhält ;man 30 Aufrufe pro Umdrehung. Zählt man für genau 2 Sekunden die Aufrufe, erhält ;man die Umdrehungen pro Minute (UPM) ;------------------------------------------------------------------------------- in stat,SREG ;Status sichern inc cnt_low ;Flanken der Lichtschranke zählen brne ext1 inc cnt_high ; brne ext1 ; sbr flags,1< mit Repeat ;---------------------- deb1: lsl itemp ;Up-Taste ins Carry rol taste2 ;und in Taste2 schieben brne deb2 ;Taste ist 0 wenn die letzten 8 Durchläufe gedrückt war dec repeat ;Repeatzähler-1 brne rp0 ;Wenn Repeatzeit noch nicht abgelaufen, auf neuen Tastendruck testen mov repeat,sechzig ;Repeatwert (kurz) neu laden sec ;Tastenflag durch Repeat setzen rp0: brcc deb2 ;Das Carry setzt das Tastenflag durch Repeat oder ersten Tastendruck sbr flags,1< mit Repeat ;----------------------- deb2: lsl itemp ;Down-Taste ins Carry rol taste3 ;und in Taste schieben brne deb3 ;Taste ist 0 wenn die letzten 8 Durchläufe gedrückt war dec repeat ;Repeatzähler-1 brne rp1 ;Wenn Repeatzeit noch nicht abgelaufen, auf neuen Tastendruck testen mov repeat,sechzig ;Repeatwert (kurz) neu laden sec ;Tastenflag durch Repeat setzen rp1: brcc deb3 ;Das Carry setzt das Tastenflag durch Repeat oder ersten Tastendruck sbr flags,1<10 mit Dezimalpunkt (1/10 mm anzeigen) brcc ab10 ;Durchmesser + 1mm inc durchmesser ;Durchmesser + 0,1mm rcall print_dm ret ab10: cpi durchmesser,250 ;Maximum (25,0mm) erreicht ? brcc maxdm ;Wenn ja, Sprung subi durchmesser,-10 ;ansonsten Durchmesser + 1,0 mm rcall print_dm ;Aufs Display damit maxdm: ret TasteDown: sbrs flags, down ;Wenn Tastenflag gesetzt, überspringen ret ;Ansonsten rücksprung cbr flags,1<=5 ldi temp1,_5 .endif .endif sts segbuffer+4,temp1 ;In die letzte Stelle des Displays schreiben rcall test0 ;Divident = 0 -> Leerzeichen. Ansonsten zu div10 springen sts segbuffer+3,temp1 ;In der Vorletzten Stelle eintragen rcall test0 ;Weiter wie gehabt sts segbuffer+2,temp1 rcall test0 sts segbuffer+1,temp1 rcall test0 sts segbuffer,temp1 rjmp main ;-------------------------------------------------------- ;U/sekunde ;UPM/6, + Dezimalpunkt eine Stelle nach links (enspricht /60) ;-------------------------------------------------------- printups:rcall upm ;Die letzten 8 Messwerte (Impulse pro 250mS) addieren rcall div6 ;Da 10tel angezeigt werden, nur durch 6 anstatt 60 teilen rcall div10 ;1/10tel berechnen sts segbuffer+4,temp1 ;1/10tel in die letzte Stelle rcall div10 ;Einer sbr temp1,1<<7 ;Dezimalpunkt der vorletzten Anzeige einschalten sts segbuffer+3,temp1 rcall test0 sts segbuffer+2,temp1 rcall test0 sts segbuffer+1,temp1 rcall test0 sts segbuffer,temp1 rjmp main ;------------------------------------------------------------------------------------------- ;Meter pro Minute ;------------------------------------------------------------------------------------------- ;Die Formel n*d*pi/1000 wurde erweitert, um in ASM mit Festkomma rechnen zu können (2 Nachkommastellen) ;Die neue Formel lautet n*(d*10)*(pi*655,36)/$10000 ;n=Umdrehungen/Minute (UPM) ;d=Werkzeugdurchmesser .Dieser liegt als Festkommazahl mit einer Stelle nach dem Komma (*10) ;in r22 (durchmesser) vor. ;pi = die Keiszahl 3,14.....Diese wird mit 655,36 ($1000/100dez) multipliziert. Dadurch kann die ;anschliesende Division /1000 entfallen, es werden einfach die beiden untersten Bytes verworfen. ; ;Die endgültige Formel mit Programmvariablen: ;temp1..5 =durchmesser*2059*upm mit anschliesendem verwerfen der Bytes temp1 und temp2. ;------------------------------------------------------------------------------------------- printmm:rcall mul2059 ;durchmesser(*10) mal 2059 (pi*655,36) rcall mul_upm ;Ergebniss mal upm rcall div010 ;Ergebniss in einzelne Ziffern (7-Seg) wandeln und ausgeben .if genau ==0 ;Wenn gewünscht, letzte Stelle runden ldi temp1,_0 .if strong ==0 ;Wenn gewünscht, eine Null in die letzte Stelle sbrc flags,runden ldi temp1,_5 ;Wenn gewünscht 0 oder 5 in die letzte Stelle .endif .endif sts segbuffer+4,temp1 rcall div010 sts segbuffer+3,temp1 rcall div010 sbr temp1,1<<7 ;2 Stellen nach dem Komma den DP einschalten sts segbuffer+2,temp1 rcall test0 sts segbuffer+1,temp1 rcall test0 sts segbuffer,temp1 rjmp main ;------------------------------------------------------------------------------------------- ;Meter pro Sekunde ;------------------------------------------------------------------------------------------- ;Formel wie oben, nur mit anschliesender Division durch 6. Durch die Division durch 6 anstatt ;durch 60, erhält man eine Stelle hinter dem Komma mehr als bei m/min. Hier also 1000stel ;------------------------------------------------------------------------------------------- printms:rcall mul2059 ;durchmesser(*10) mal 2059 (pi*655,36) rcall mul_upm ;Ergebniss * upm rcall div6 ;Ergebniss / 6 rcall div10 ;In einzelne Ziffern (7-Seg) wandeln und ausgeben sts segbuffer+4,temp1 rcall div10 sts segbuffer+3,temp1 rcall div10 sts segbuffer+2,temp1 rcall div10 sbr temp1,1<<7 ;3 Stellen nach dem Komma den DP dazuodern sts segbuffer+1,temp1 rcall test0 sts segbuffer,temp1 rjmp main ;************************************************************************** ;Aus den 8 Messwerten auf Unterschiedliche Art, das Gesamtergebniss bilden ;************************************************************************** ;------------------------------------------------------------------------------------------ ;Wenn man am Fräsmotor die Drehzahl ändert, reagiert bei normaler Messung die Anzeige etwas ;träge, da eine genaue Messung 2 volle Sekunden dauert. Um dem entgegezuwirken kann man die ;neueren Messungen stärker gewichten als ältere Messungen. Das Extrem währe, die letzte Messung ;mit 8 zu Multiplizieren. Das hätte allerdings zur Folge, das auch die Auflösung auf 8 ;steigen würde (man hätte also nur ganzzahlige Werte der 8er Reihe auf dem Display). ;Die Lösung unten ist also ein Kompromiss zwischen Auflösung und der Reaktion auf ;Drehzahländerungen der Anzeige. ;------------------------------------------------------------------------------------------ ;Die 8 Messungen werden wie folgt gewichtet: ;------------------------------------------------------------------------------------------ ;Neueste.....................................................................älteste Messung ; 1 2 3 4 5 6 7 8 ;------------------------------------------------------------------------------------------ ;31,25% 18,75% 12,5% 12,5% 6,25% 6,25% 6,25% 6,25% ; 5 3 2 2 1 1 1 1 ;------------------------------------------------------------------------------------------ ;In der unteren Zeile der Tab ist die Anzahl der Messungen eingetragen die zum Gesamtwert ;addiert werden. ;Zuerst werden alle 8 Messungen zum Gesamtwert addiert. Dann wird der neuste Wert 4mal ;dazuaddiert, der nächste 2 mal, der 3. und 4. Wert 1mal. ;Zum Schluss wird der Gesamtwert durch 2 geteilt. ;------------------------------------------------------------------------------------------ upm: .if interpol ==1 rcall _upm ;Alle Messwerte der letzten 2 Sekunden addieren mov temp5,pointer ;Zeigt auf den letzten Messwert subi temp5,6 ;Gehe 3 Words zurück andi temp5,$0f ;Unterlauf interresiert nicht (Ringbuffer) ldi xl,low(CntBuffer) ;Zeiger auf Messwert vor 1 Sekunde setzen ldi xh,high(CntBuffer) add xl,temp5 ;Index dazu adc xh,null ;Carry dazu rcall adm ;Messwert (vor 1 Sekunde) addieren rcall adm ;Messwert (vor 0,75 Sekunde) addieren rcall adm ;Messwert (vor 0,5 Sekunden) 2* addieren rcall am5 rcall adm ;Messwert (vor 0,25 Sekunden) 4* addieren rcall am5 rcall am5 rcall am5 brcc ok1 sbr flags,1< temp1..2 ;------------------------------------------------ addm: subi temp5,-2 andi temp5,$0f ldi xl,low(CntBuffer) ldi xh,high(CntBuffer) add xl,temp5 ;Index dazu adc xh,null ;Carry dazu ld temp1,x+ ;Messwert nach temp1..2 ld temp2,x ;------------------------------------------------ ;Mittelwert und ganzer Messwert zum Gesamtergebniss addieren ;------------------------------------------------ add zl,temp1 ;Mittelwert aus Z und Temp1..2 bilden (z= alter Wert) adc zh,temp2 ;interpolierter Wert = alter Wert + neuer Wert / 2 lsr zh ror zl add temp3,zl ;Gesamtwert + interpolierter Wert adc temp4,zh mov zl,temp1 ;neuer alter Wert ;-) mov zh,temp2 add temp3,zl ;Gesamtwert + neuer Wert adc temp4,zh ret .endif ;------------------------------------------------ ;Nur die letzten 1000 mS Auswerten und Hochrechnen ;------------------------------------------------ .if interpol ==3 mov temp5,pointer ;Zeigt auf den letzten Messwert subi temp5,6 ;3 Words zurück andi temp5,$0f ;Unterlauf interresiert nicht (Ringbuffer) ldi xl,low(CntBuffer) ;Zeiger auf Messwert vor 1 Sekunde setzen ldi xh,high(CntBuffer) add xl,temp5 ;Index dazu adc xh,null ;Carry dazu clr temp3 clr temp4 rcall adm rcall adm rcall adm rcall adm lsl temp3 rol temp4 ret .endif ;------------------------------------------------ ;Nur die letzten 500 mS Auswerten und Hochrechnen ;------------------------------------------------ .if interpol ==4 mov temp5,pointer ;Zeigt auf den letzten Messwert subi temp5,2 ;1 Word zurück andi temp5,$0f ;Unterlauf interresiert nicht (Ringbuffer) ldi xl,low(CntBuffer) ;Zeiger auf Messwert vor 1/2 Sekunde setzen ldi xh,high(CntBuffer) add xl,temp5 ;Index dazu adc xh,null ;Carry dazu clr temp3 clr temp4 rcall adm rcall adm lsl temp3 rol temp4 lsl temp3 rol temp4 ret .endif ;---------------------------------------------- ;Messwert addieren und Zeiger im CntBuffer erhöhen ;---------------------------------------------- .if interpol >0 adm: cli ;Bitte nicht stören ld temp1,x+ ;Messwert nach temp1 und 2 ld temp2,x+ sei add temp3,temp1 ;Zum gesamtmesswert addieren adc temp4,temp2 subi temp5,-2 ;Index +2 andi temp5,$0f ;Überlauf weg unden ldi xl,low(CntBuffer) ;Zeiger auf Bufferanfang ldi xh,high(CntBuffer) add xl,temp5 ;Index und Carry dazu adc xh,null ret ;---------------------------------------------- ;Messwert nochmal addieren ;---------------------------------------------- am5: add temp3,temp1 adc temp4,temp2 ret .endif ;------------------------------------------------------------------------------------------ ;Die letzten 8 Messwerte addieren ;------------------------------------------------------------------------------------------ ;Der CntBuffer (8 Words) wird im Timerinterrupt alle 250 mS mit den gezählten ;Impulsen der Taktscheibe beschieben. Es wird immer der älteste Wert mit dem ;neusten überschrieben (Ringbuffer). ;------------------------------------------------------------------------------------------ .if interpol <=1 _upm: ldi xl,low(CntBuffer) ;X zeigt auf den CntBuffer ldi xh,high(CntBuffer) clr temp3 ;Ergebniss =0 clr temp4 ldi temp5,8 ;8 Additionen addupm: cli ld temp1,x+ ;1. Word nach temp1..2 ld temp2,x+ sei add temp3,temp1 ;addieren adc temp4,temp2 dec temp5 ;8 Additionen durch ? brne addupm ;Wenn nein, Sprung brcc ok sbr flags,1<= 6 ... rol temp4 rol temp1 ;Rest cpi temp1,6 brcs div62 ;Wenn der Rest <6 ist, 0-Bit (LSL oben) im Ergebniss stehen lassen inc temp3 ;Ansonsten im Ergebniss Bit0=1 ... subi temp1,6 ;.. und 6 vom Rest abziehen div62: dec temp2 ;das ganze 16 mal brne div61 ret ;---------------------------------------------- ;24Bit Division durch 10 ;Dividend und Ergebniss in temp3..5 ;Rest in temp1 ;----------------------------------------- div010: clr temp1 ;Rest ldi temp2,24 ;24* schieben div0101:lsl temp3 ;Solange Bits vom Divident in Rest schieben bis >= 10 ... rol temp4 rol temp5 rol temp1 ;Rest cpi temp1,10 brcs div0102 ;Wenn der Rest <10 ist, 0-Bit (LSL oben) im Ergebniss stehen lassen inc temp3 ;Ansonsten im Ergebniss Bit0=1 ... subi temp1,10 ;.. und 10 vom Rest abziehen div0102:dec temp2 ;das ganze 24 mal brne div0101 rjmp seg7 ;Rest nach 7Seg wandeln ;---------------------------------------------- ;16Bit Division durch 10 ;Dividend und Ergebniss in temp3 und temp4 ;Rest in temp1 ;----------------------------------------- div10: clr temp1 ;Rest ldi temp2,16 ;16* schieben div101: lsl temp3 ;Solange Bits vom Divident in Rest schieben bis >= 10 ... rol temp4 rol temp1 ;Rest cpi temp1,10 brcs div102 ;Wenn der Rest <10 ist, 0-Bit (LSL oben) im Ergebniss stehen lassen inc temp3 ;Ansonsten im Ergebniss Bit0=1 ... subi temp1,10 ;.. und 10 vom Rest abziehen div102: dec temp2 ;das ganze 16 mal brne div101 ; ret ;----------------------------------------- ;Ende Division ;Aus dem Rest, den 7-Segmentcode ermitteln ;----------------------------------------- seg7: cbr flags,1<