;****************************************************************************** ; ; Frequenzzähler für den DL5UP Low Band Receiver ; ;Der Empfänger überstreicht die Amateurbänder 160m (über Konverter auf 40m), ;80m, 40m und 30m (über Konverter auf 40m). Der VFO ist im Bereich von ;ca 5,2 bis 5,8 MHz abstimmbar. Die ZF des Empfängers beträgt 1,7MHz. Die BFO ;Frequenz ist um etwa +/-10kHz verstimmbar (1.690...1,710 MHz). ;Der Zähler erfasst im Gegentakt die VFO und BFO Frequenz. Das eingehende ;Signal wird vor dem Prozessor nochmals um 10 geteilt. Das Signal wird dann ;über den 16-Bit Counter des ATMEGA8 gezählt mit einer Torzeit von 100ms. ;Zählerstand bei VFO Signal 52000...58000 ;Zählerstand bei BFO Signal 16900...17100 ;Für eine korrekte RX Frequenzanzeige müssen die folgenden Werte zum/vom Zähl- ;ergebnis addiert/subtrahiert werden. ; ;160m (Konverter VXO=5.2MHz) 1.7....2.3Mhz VFO Offset -35000 ; 80m 3.5....4.1Mhz VFO Offset -17000 ; 40m 6.9....7.5Mhz VFO Offset +17000 ; 30m (Konverter VXO=3.0MHz) 9.9...10.5Mhz VFO Offset +47000 ;opt.(30m (Konverter VXO=3.072MHz) 9.9...10.5Mhz VFO Offset +47720) ; ; BFO -10.0..+10.0khz BFO Offset -17000 ;****************************************************************************** .INCLUDE "m8def.inc" .DEF UNI_REG16 = r16 .DEF UNI_REG17 = r17 .DEF UNI_REG18 = r18 .DEF RMP = r19 .DEF BAND_SEL = r20 .DEF BIN1_L = r22 .DEF BIN1_H = r23 .DEF BIN2_L = r24 .DEF BIN2_H = r25 .EQU ASCII_DATA_VFO = 0x0060 .EQU ASCII_DATA_BFO_OFFSET = 0x0066 .EQU ASCII_DATA_BFO = 0x006C ;-------------------------------------------------------------------------- ; Der Zähler hat 2 Eingänge (VFO und BFO). Über ein Register wird das ; Eingangsgate so gesteuert, dass der eine oder andere Eingang auf den ; Prozessorzähler geschaltet wird. ; Das Öffnen des GATES muss über einen BIT SET Befehl erfolgen innerhalb ; der GATE Routine ;-------------------------------------------------------------------------- .equ COUNT_A = 0b00011111;Zählereingang A aktiv .equ COUNT_B = 0b00101111;Zählereingang B aktiv ;-------------------------------------------------------------------------- ;Datenmaske zur Anzeige auf dem LCDisplay ;-------------------------------------------------------------------------- INTRO_Z1: .DB " DL5UP's " INTRO_Z2: .DB " LOW BAND RX " MASKE_Z1: .DB "RX 00.0000 MHz" MASKE_Z2: .DB "BFO 00.0 kHz" OFFSET: .DW 35000,17000,17000,47720,17000 LCD_DATA_POINTER: .DB $05,$06,$08,$09,$0A,$0B,$47,$48,$49,$4B ; Stackpointer initialisieren ;------------------------------- ldi UNI_REG16, LOW(RAMEND) out SPL, UNI_REG16 ldi UNI_REG16, HIGH(RAMEND) out SPH, UNI_REG16 ;------------------------------- ldi UNI_REG16, 0b11111111 out DDRD, UNI_REG16 ;Port D als Ausgang (LCD Ansteuerung) ldi UNI_REG16, 0b00110000 ;Bit 0..3 dienen zur Bandselektion (INPUTS) ;Bit 4..5 dienen zum Schalten zwischen VFO (Bit4) und BFO out DDRC, UNI_REG16 ;Port C zur Steuerung der Eingänge rcall LCD_INIT ;Display initialisieren rcall LCD_CLEAR ;Display löschen rcall LCD_1ST_ROW rcall LCD_HOME ldi ZL, LOW(INTRO_Z1*2) ; Low-Byte der Adresse in Z-Pointer ldi ZH, HIGH(INTRO_Z1*2) ; High-Byte der Adresse in Z-Pointer rcall LCD_TEXT rcall LCD_2ND_ROW ldi ZL, LOW(INTRO_Z2*2) ; Low-Byte der Adresse in Z-Pointer ldi ZH, HIGH(INTRO_Z2*2) ; High-Byte der Adresse in Z-Pointer rcall LCD_TEXT ldi UNI_REG16,10 ldi UNI_REG17,10 rcall PAUSE rcall LCD_1ST_ROW ldi ZL, LOW(MASKE_Z1*2) ; Low-Byte der Adresse in Z-Pointer ldi ZH, HIGH(MASKE_Z1*2) ; High-Byte der Adresse in Z-Pointer rcall LCD_TEXT rcall LCD_2ND_ROW ldi ZL, LOW(MASKE_Z2*2) ; Low-Byte der Adresse in Z-Pointer ldi ZH, HIGH(MASKE_Z2*2) ; High-Byte der Adresse in Z-Pointer rcall LCD_TEXT ldi UNI_REG16,10 ldi UNI_REG17,1 MAIN_REPEAT: ;--------------'Zuerst die VFO Frequenz zählen und speichern ldi UNI_REG18,COUNT_A out PORTC, UNI_REG18 rcall GATE ;Den Zähler für 100ms aktivieren ldi ZH, HIGH(ASCII_DATA_VFO) ;SRAM Adresse für ASCII Ergebnis setzen ldi ZL, LOW(ASCII_DATA_VFO)+1 rcall BIN16_TO_ASCII ;Binärdaten in ASCII umwandeln und in SRAM schreiben ;---------------'...jetzt noch die BFO Frequenz messen und speichern ldi UNI_REG18,COUNT_B out PORTC, UNI_REG18 rcall GATE ;Den Zähler für 100ms aktivieren ldi ZH, HIGH(ASCII_DATA_BFO);SRAM Adresse für ASCII Ergebnis setzen ldi ZL, LOW(ASCII_DATA_BFO) rcall BIN16_TO_ASCII ;Binärdaten in ASCII umwandeln und in SRAM schreiben ;---------------'Schalterstellung (Frequenzband) prüfen und die entsprechende Anzeigefrequenzen berechnen rcall SELECT_OFFSET ;Je nach Band den entsprechenden OFFSET beaufschlagen rcall CALCULATE_RX_FREQ rcall CALCULATE_BFO rcall MOVE_ASCII_DATA rjmp MAIN_REPEAT ;ENDE des Hauptprogramms ;***************************************************************************** ;* Die Routine prüft welches Band selektiert ist und legt den entsprechenden * ;* OFFSET als ASCII in den Resgister r0 bis r4 ab. * ;* Das selektierte Band ist abhängig davon an welchem PORTC Pin (0...3) * ;* ein Signal anliegt: * ;* PORTC Pin 0 = GND 160m Band (PORTC = 7) * ;* PORTC Pin 1 = GND 80m Band (PORTC = 11) * ;* PORTC Pin 2 = GND 40m Band (PORTC = 13) * ;* PORTC Pin 3 = GND 30m Band (PORTC >= 14) * ;***************************************************************************** SELECT_OFFSET: in BAND_SEL, PINC ldi ZL, LOW(OFFSET*2) ;Z-Register als Pointer auf die OFFSET Daten ldi ZH, HIGH(OFFSET*2) ;Zuerst auf 160m Band OFFSET andi BAND_SEL,0b00001111 ;Nur die BAND SELECT Bits extrahieren cpi BAND_SEL, 7 breq SELECT_OFFSET_A ;Ist das 160m Band eingestellt ? adiw ZL,2 ;nein, Pointer auf 80m Band OFFSET cpi BAND_SEL, 11 breq SELECT_OFFSET_A ;Ist das 80m Band eingestellt ? adiw ZL,2 ;nein, Pointer auf 40m Band OFFSET cpi BAND_SEL, 13 breq SELECT_OFFSET_A ;Ist das 40m Band eingestellt ? adiw ZL, 2 ;nein, Pointer auf 30m Band OFFSET SELECT_OFFSET_A: lpm mov BIN1_L,r0 ;Den OFFSET als 16-Bit Wert einlesen... adiw ZL,1 lpm mov BIN1_H,r0 clr ZL clr ZH rcall BIN16_TO_ASCII ;...und in 5 ASCII's umwandeln (r0...r4) ;jetzt wird noch der BFO OFFSET eingelesen und mit 5 ASCII's ldi ZL, LOW(OFFSET*2) ;Z-Register als Pointer auf die OFFSET Daten ldi ZH, HIGH(OFFSET*2) adiw ZL,8 ;Pointer auf BFO OFFSET lpm r5, Z mov BIN1_L,r5 ;Den OFFSET als 16-Bit Wert einlesen... adiw ZL,1 lpm r5,Z mov BIN1_H,r5 ldi ZL,5 clr ZH rcall BIN16_TO_ASCII ;...und in 5 ASCII's umwandeln (r5...r9) ret ;***************************************************************************** ;* Die Routine erzeugt die ASCII's für die richtige Anzeige der RX Frequenz * ;***************************************************************************** CALCULATE_RX_FREQ: ldi UNI_REG16, SREG push UNI_REG16 ldi XH, HIGH(ASCII_DATA_VFO) ;SRAM Adresse für ASCII Ergebnis setzen ldi XL, LOW(ASCII_DATA_VFO) clr ZH ldi ZL,5 adiw XH:XL,6 ;Pointer auf die 100Hz Stelle +1 ldi UNI_REG18,6 ;r18 als Durchlaufzähler verwenden cpi BAND_SEL, 12 ;7, 11 < 12 > 13, 14 brlo LOWER_FREQ CALC_NEXT_A: dec UNI_REG18 breq CALC_READY ld UNI_REG16, -X ;VFO Werte lesen ld UNI_REG17, -Z ;OFFSET Werte lesen add UNI_REG16, UNI_REG17;VFO mit OFFSET addieren cpi UNI_REG16,10 ;Ist die Summe < 10 brlo CALC_NEXT_B ;Nein, dann zur nächsten Stelle sbci UNI_REG16,10 ;Ja.... ld UNI_REG17, -Z inc ZL inc UNI_REG17 st -Z, UNI_REG17 ;...nächst höhere Stelle +1 inc ZL ;Pointer aber wieder eins zurück sonst CALC_NEXT_B: st X,UNI_REG16 rjmp CALC_NEXT_A ;stimmt der Zugriff auf die nächste Stelle nicht mehr LOWER_FREQ: dec UNI_REG18 breq CALC_READY ld UNI_REG16, -X ;VFO Werte lesen ld UNI_REG17, -Z ;OFFSET Werte lesen clc sbc UNI_REG16, UNI_REG17;OFFSET von OFFSET subtrahieren cpi UNI_REG16,10 ;Ist die Differenz > 10 (entspricht einem neg. Wert) brlo CALC_NEXT_C ;Nein, dann zur nächsten Stelle ldi UNI_REG17,10 add UNI_REG16,UNI_REG17 ;Ja,.... ld UNI_REG17, -Z inc ZL inc UNI_REG17 st -Z, UNI_REG17 ;...nächst höhere Stelle +1 inc ZL ;Pointer aber wieder eins zurück sonst CALC_NEXT_C: st X,UNI_REG16 rjmp LOWER_FREQ ;stimmt der Zugriff auf die nächste Stelle nicht mehr CALC_READY: ld UNI_REG16, X dec XL cpi UNI_REG16, 0 brne GANZ_READY_1 ldi UNI_REG16,1 st X, UNI_REG16 rjmp GANZ_READY_2 GANZ_READY_1: ldi UNI_REG16, 240 st X, UNI_REG16 GANZ_READY_2: pop UNI_REG16 out SREG, UNI_REG16 ret ;***************************************************************************** ;* Die Routine erzeugt die ASCII's für die richtige Anzeige des BFO Offsets * ;***************************************************************************** CALCULATE_BFO: ldi UNI_REG16, SREG push UNI_REG16 ldi XH, HIGH(ASCII_DATA_BFO) ;SRAM Adresse für ASCII Ergebnis setzen ldi XL, LOW(ASCII_DATA_BFO) ldi YH, HIGH(ASCII_DATA_BFO_OFFSET) ldi YL, LOW(ASCII_DATA_BFO_OFFSET) clr ZH ldi ZL,10 adiw XH:XL,1 ;Püfen ob die BFO Frequenz grösser oder keiner ld UNI_REG16, X ;als der OFFSET ist cpi UNI_REG16,7 brne CALC_BFO_CC adiw XH:XL,4 ;Pointer auf die 100Hz Stelle +1 ldi UNI_REG18,4 ;r18 als Durchlaufzähler verwenden ;BFO Frequenz ist grösser als OFFSET ldi UNI_REG16, 251 st Y, UNI_REG16 inc YL inc YL inc YL inc YL CALC_BFO_A: dec UNI_REG18 breq CALC_BFO_READY ld UNI_REG16, -X ;BFO Werte lesen ld UNI_REG17, -Z ;OFFSET Werte lesen clc sbc UNI_REG16, UNI_REG17;OFFSET von BFO subtrahieren cpi UNI_REG16,10 ;Ist die Differenz > 10 (..bedeutet neg. Wert) brlo CALC_BFO_B ;Nein, dann zur nächsten Stelle ldi UNI_REG17,255 sbc UNI_REG17,UNI_REG16 ;Ja,dann denn Wert von 255 abziehen. Das ergibt den mov UNI_REG16, UNI_REG17;eigentlichen neg. Zahlenwert. Ergebnis in r16 damit die ld UNI_REG17, -Z ;Routine wieder gleich ist zu VFO OFFSET inc ZL inc UNI_REG17 st -Z, UNI_REG17 ;...nächst höhere Stelle +1 inc ZL ;Pointer aber wieder eins zurück sonst gibt es Wirrwarr CALC_BFO_B: ;BFO Frequenz ist kleiner als OFFSET st -Y,UNI_REG16 rjmp CALC_BFO_A ;stimmt der Zugriff auf die nächste Stelle nicht mehr CALC_BFO_CC: ldi UNI_REG16, 253 st Y, UNI_REG16 inc YL inc YL inc YL inc YL CALC_BFO_C: adiw XH:XL,4 ;Pointer auf die 100Hz Stelle +1 ldi UNI_REG18,4 ;r18 als Durchlaufzähler verwenden CALC_BFO_D: dec UNI_REG18 breq CALC_BFO_READY ld UNI_REG16, -X ;BFO Werte lesen ld UNI_REG17, -Z ;OFFSET Werte lesen clc sbc UNI_REG17, UNI_REG16;OFFSET von BFO subtrahieren cpi UNI_REG17,10 ;Ist die Differenz > 10 (..bedeutet neg. Wert) brlo CALC_BFO_E ;Nein, dann zur nächsten Stelle ldi UNI_REG16,10 add UNI_REG16,UNI_REG17 ;Ja,dann denn Wert von 255 abziehen. Das ergibt den ld UNI_REG17, -X ;Routine wieder gleich ist zu VFO OFFSET inc XL inc UNI_REG17 st -X, UNI_REG17 ;...nächst höhere Stelle +1 inc XL ;Pointer aber wieder eins zurück sonst gibt es Wirrwarr CALC_BFO_E: st -Y,UNI_REG16 rjmp CALC_BFO_D ;stimmt der Zugriff auf die nächste Stelle nicht mehr CALC_BFO_READY: pop UNI_REG16 out SREG, UNI_REG16 ret ;***************************************************************************** ;* Diese Routine bringt die ASCII Daten für die RX Frequenz und den BFO * ;* OFFSET in den Datenbereich für die LCD Zeilen * ;***************************************************************************** MOVE_ASCII_DATA: ldi XH, HIGH(ASCII_DATA_VFO) ;SRAM Adresse für ASCII Ergebnis setzen ldi XL, LOW(ASCII_DATA_VFO) ldi ZL, LOW(LCD_DATA_POINTER*2) ldi ZH, HIGH(LCD_DATA_POINTER*2) ldi UNI_REG17,10 NEXT_CHAR: lpm mov UNI_REG18, r0 adiw ZL,1 ldi RMP, 0b10000000 add RMP, UNI_REG18 mov UNI_REG16, RMP rcall LCD_COMMAND rcall DELAY_5ms ld UNI_REG16, X+ ldi RMP,48 add UNI_REG16, RMP rcall LCD_DISPLAY_DATA rcall DELAY_5ms dec UNI_REG17 tst UNI_REG17 brne NEXT_CHAR ret ;***************************************************************************** ;* Einfache Pausenroutine * ;* Werte von 1 bis 255 müssen in r16 und r17 stehen (ACHTUNG nicht 0) * ;***************************************************************************** PAUSE: ldi UNI_REG18, $FF PAUSE_B: dec UNI_REG18 brne PAUSE_B dec UNI_REG17 brne PAUSE dec UNI_REG16 brne PAUSE ret ;wieder zurück ;*************************************************************** ;* Die GATE Routine lässt den 16Bit Counter 100ms lange zählen * ;*************************************************************** GATE: in UNI_REG16, SREG ;Inhalt des Status Registers.... push UNI_REG16 ;auf dem Stack speichern ldi UNI_REG17,20 ;20x5ms Torzeit (bei 4MHz Takt) cli ldi UNI_REG16,0 out TCNT1L, UNI_REG16 ;Zähler auf 0 setzen out TCNT1H, UNI_REG16 ldi UNI_REG16,7 out TCCR1B,UNI_REG16 ;Zähler starten WAIT_1: ldi ZH, HIGH(4999) ;16 Bit HB in Z Register schreiben ldi ZL, LOW(4999) ;16 Bit LB in Z-Register schreiben WAIT_2: sbiw ZH:ZL,1 ;16 Bit Wert im Z-Register um 1 vermindern brne WAIT_2 ;Solange nicht 0 erreicht ist zurück zu WAIT_2 dec UNI_REG17 ;Den Millisekundenzähler um eins vermindern brne WAIT_1 ;Solange nicht 0 erreicht ist zurück zu WAIT_1 ldi UNI_REG16,0 out TCCR1B,UNI_REG16 ;Zähler stoppen in BIN1_H, TCNT1H ;Ergebnis des 16-Bit Zählers laden in BIN1_L, TCNT1L sei pop UNI_REG16 ;den alten Wert des Statusregisters (wie vor der Routine)... out SREG, UNI_REG16 ;...wieder in das Statusregister schreiben ret ;Zurück zur Hauptroutine ;************************************************************ ;* Routinen zur Umwandlung eines 16-Bit Wertes in 5 ASCII's * ;************************************************************ BIN16_TO_ASCII: push BIN1_H push BIN1_L ldi RMP, HIGH(10000) mov BIN2_H,RMP ldi RMP, LOW(10000) mov BIN2_L,RMP rcall BIN_TO_DIGIT ldi RMP, HIGH(1000) mov BIN2_H,RMP ldi RMP, LOW(1000) mov BIN2_L,RMP rcall BIN_TO_DIGIT ldi RMP, HIGH(100) mov BIN2_H,RMP ldi RMP, LOW(100) mov BIN2_L,RMP rcall BIN_TO_DIGIT ldi RMP, HIGH(10) mov BIN2_H,RMP ldi RMP, LOW(10) mov BIN2_L,RMP rcall BIN_TO_DIGIT st z, BIN1_L sbiw ZL,4 pop BIN1_L pop BIN1_H ret BIN_TO_DIGIT: clr RMP BIN_TO_DIGIT_A: cp BIN1_H,BIN2_H brcs BIN_TO_DIGIT_C brne BIN_TO_DIGIT_B cp BIN1_L,BIN2_L brcs BIN_TO_DIGIT_C BIN_TO_DIGIT_B: sub BIN1_L,BIN2_L sbc BIN1_H,BIN2_H inc RMP rjmp BIN_TO_DIGIT_A BIN_TO_DIGIT_C: st z+,RMP ret ;******************************************** ;* Routinen zur Ansteuerung des LCDisplays * ;******************************************** ;sendet ein Datenbyte an das LCD LCD_TEXT: ldi UNI_REG18,16 LCD_TEXT_START: tst UNI_REG18 breq LCD_TEXT_END lpm mov UNI_REG16,r0 rcall LCD_DISPLAY_DATA adiw ZL,1 dec UNI_REG18 rjmp LCD_TEXT_START LCD_TEXT_END: ret LCD_DISPLAY_DATA: push UNI_REG17 mov UNI_REG17, UNI_REG16 ;"Sicherungskopie" für ;die Übertragung des 2.Nibbles andi UNI_REG16, 0b11110000 ;oberes Nibble auf Null setzen sbr UNI_REG16, 0b00000100 ;entspricht 0b00010000 out PORTD, UNI_REG16 ;ausgeben rcall LCD_ENABLE ;Enable-Routine aufrufen ;2. Nibble, kein swap da es schon ;an der richtigen stelle ist swap UNI_REG17 ;Vertauschen andi UNI_REG17, 0b11110000 ;obere Hälfte auf Null setzen sbr UNI_REG17, 0b00000100 ;entspricht 0b00010000 out PORTD, UNI_REG17 ;ausgeben rcall LCD_ENABLE ;Enable-Routine aufrufen rcall DELAY_50us ;Delay-Routine aufrufen pop UNI_REG17 ret ;zurück zum Hauptprogramm ;sendet einen Befehl an das LCD LCD_COMMAND: ;wie LCD_DISPLAY_DATA, nur ohne RS zu setzen push UNI_REG17 mov UNI_REG17, UNI_REG16 andi UNI_REG16, 0b11110000 out PORTD, UNI_REG16 rcall LCD_ENABLE swap UNI_REG17 andi UNI_REG17, 0b11110000 out PORTD, UNI_REG17 rcall LCD_ENABLE rcall DELAY_50us pop UNI_REG17 ret ;erzeugt den Enable-Puls LCD_ENABLE: sbi PORTD, 3 ;Enable high nop ;3 Taktzyklen warten nop nop cbi PORTD, 3 ;Enable wieder low ret ;Und wieder zurück ;Pause nach jeder Übertragung DELAY_50us: ;50us Pause ldi UNI_REG16, $42 DELAY_50us_A: dec UNI_REG16 brne DELAY_50us_A ret ;wieder zurück ;Längere Pause für manche Befehle DELAY_5ms: ;5ms Pause push UNI_REG16 push UNI_REG17 ldi UNI_REG16, $21 DELAY_5ms_A: ldi UNI_REG17, $C9 DELAY_5ms_B: dec UNI_REG17 brne DELAY_5ms_B dec UNI_REG16 brne DELAY_5ms_A pop UNI_REG17 pop UNI_REG16 ret ;wieder zurück ;Initialisierung: muss ganz am Anfang des Programms aufgerufen werden LCD_INIT: ldi UNI_REG18,50 POWERUP_WAIT: rcall DELAY_5ms dec UNI_REG18 brne POWERUP_WAIT ldi UNI_REG16, 0b00110000 ;muss 3mal hintereinander gesendet out PORTD, UNI_REG16 ;werden zur Initialisierung rcall LCD_ENABLE ;1.mal rcall DELAY_5ms rcall LCD_ENABLE ;2.mal rcall DELAY_5ms rcall LCD_ENABLE ;3.mal rcall DELAY_5ms ldi UNI_REG16, 0b00100000 ;4-Bit Mode out PORTD, UNI_REG16 rcall LCD_ENABLE rcall DELAY_5ms ldi UNI_REG16, 0b00101000 ;2.Zeile, Aufruf muss explizit durchgeführt werden rcall LCD_COMMAND ldi UNI_REG16, 0b00001100 ;Display ON rcall LCD_COMMAND ldi UNI_REG16, 0b00000100 ;Entry mode Set rcall LCD_COMMAND ret LCD_CLEAR: ldi UNI_REG16, 0b00000001 ;Display löschen rcall LCD_COMMAND rcall DELAY_5ms ret LCD_ON: ldi UNI_REG16, 0b00011000 rcall LCD_COMMAND rcall DELAY_5ms ret LCD_HOME: ldi UNI_REG16, 0b00000010 rcall LCD_COMMAND rcall DELAY_5ms ret LCD_2ND_ROW: ldi UNI_REG16, 0b11000000 rcall LCD_COMMAND rcall DELAY_5ms ret LCD_1ST_ROW: ldi UNI_REG16, 0b10000000 rcall LCD_COMMAND rcall DELAY_5ms ret