;*************************************************************************** ; ; Title :HF generator/detector ; Version :1.4 ; Author :Jean Taeymans ; Target MCU :ATmega16 ; ;*************************************************************************** ; Description ;*************************************************************************** ; ; Firmware to control a stand alone HF generator/detector consisting of a ; - DDS controlled rf generator 1kHz to 16MHz, outputs 0dBm or -30dBm ; - two logarithmic detectors +10dBm to -50dBm (DC to 100MHz) ; - one reflection bridge ; ; The instrument has two basic modes of operation: ; - transfer mode ; - reflection mode ; it can also be used as a stand alone rf generator or rf power meter ; ; Its user interface consist of a rotary encoder switch, two buttons, and a ; 4 line by 16 charcater LCD (HD44xx compatible LCD Driver) ; It as a COM port interface, to output a string of comma separated values for ; the frequency and both power estimations, folowed by a CR/LF. ; ; ; The firmware consists of four more or less independent routines: ; ; The 1st is the main routine and contains all the user interface and the ; LCD manipulation, it is the biggest part and loops after the initialisation ; ; The 2nd is the modulation routine, an interrupt is generated every 11.11ms ; and will shift one 1/2 bit from a Baudot coded message to the FSELECT port ; ; The 3rd is the COMport interface, the TDbuffer empty interrupt will trigger ; to write the next character to the UART until the transmit buffer is empty ; ; The 4th is the key detect and debounce routine, it will validate a key press ; ;*************************************************************************** ; Version history ;*************************************************************************** ; rev. date why ; ---- ---------- --------------------------------------------- ; 1.0 2007.06.20 Creation, first compiled & simulated version ; 1.1 2007.06.28 Debugging of partial hardware ; 1.2 2007.07.14 Further debugging of full hardware, functional FSK ; 1.3 2007.07.22 Adding strict sample timing and averaging of measurements ; 1.4 2007.07.30 Changing the menu structure and adding the sweep ; ;*************************************************************************** ;Hardware ;*************************************************************************** ; ; CPU clock : f=12MHz (T= 83.3 ns) ; ATmega16 port usage : ; ; - 40 porta bit 0 O LCD data 4 ; - 39 porta bit 1 O LCD data 5 ; - 38 porta bit 2 O LCD data 6 ; - 37 porta bit 3 O LCD data 7 ; - 36 porta bit 4 O AD9835 FSELECT ; - 35 porta bit 5 I not used, grounded ; - 34 porta bit 6 I not used, grounded ; - 33 porta bit 7 I not used, grounded ; ; - 1 portb bit 0 O LCD data 0 also AD9835 SDATA ; - 2 portb bit 1 O LCD data 1 also AD9835 SCLK ; - 3 portb bit 2 O LCD data 2 ; - 4 portb bit 3 O LCD data 3 ; - 5 portb bit 4 O AD9835 FSYNC ; - 6 portb bit 5 O MOSI for incircuit programming ; - 7 portb bit 6 O MISO for incircuit programming ; - 8 portb bit 7 O SCLK for incircuit programming ; ; - 22 portc bit 0 O MAX1286 CVST ; - 23 portc bit 1 I MAX1286 DOUT ; - 24 portc bit 2 O MAX1286 SCLK ; - 25 portc bit 3 O driver of Ry2/R3 of -30dB pad ; - 26 portc bit 4 O sweep sync pulse output ; - 27 portc bit 5 O driver of Ry1 of input selector ; - 28 portc bit 6 I not used, grounded ; - 29 portc bit 7 I not used, grounded ; ; - 14 portd bit 0 I serial RD from COM port ; - 15 portd bit 1 O serial TD to COM port ; - 16 portd bit 2 I button "menu" ; - 17 portd bit 3 I button "up" ; - 18 portd bit 4 I button "enter" ; - 19 portd bit 5 I button "down" ; - 20 portd bit 6 O LCD chip select ; - 21 portd bit 7 O LCD register select ; ; other pins of the ATmega16 ; - 9 reset active low ; - 10 VCC : 5V supply ; - 11 GND : ground ; - 12 XTAL : 12MHz crystal ; - 13 XTAL : 12MHz crystal ; - 30 VCC : analog 5V supply, decoupled ; - 31 GND : ground ; - 32 AREF : decoupled to ground ; ; The fuse and lock byte of the ATmega16 are programmed as follows ; (u = unprogrammed = 1; p = programmed = 0) ; - lock byte : 0hxxuuuuuu ; - fuse byte high : 0huupuuppu (no JTAG) ; - fuse byte low : 0huuupuuuu (external high frequency crystal) ; ;*************************************************************************** ; ; equates .equ debounce_3ms = 35 ; 3ms on timer 2 .equ gen_time = 30000 ; 20ms on timer 1 .equ def_bit_time = 16484 ; default amateur speed of 45.5 baud .equ Xtal_frequency = 47999911 ; calibrated master clock freqency .equ def_dev_freq = 170 ; default amateur deviation for RTTY .equ def_start_freq = 7000000 ; initial frequency .equ def_increment = 100000 .equ max_frequency = 16000000 .equ min_frequency = 1000 .equ def_start_frequency = 7000000 ; default values for the sweep function .equ def_end_frequency = 7100000 .equ def_sweep_increment = 10 .equ power_coef_1 = 131000 ; forward mode: coef, calibrated 20070811 .equ power_offset_1 = 738 ; offset, calibrated 20070811 .equ power_coef_2 = 135000 ; refection mode: coef, calibrated 20070811 .equ power_offset_2 = 721 ; offset, calibrated 20070811 .equ power_coef_3 = 131000 ; detector mode coef, calibrated 20070811 .equ power_offset_3 = 953 ; offset, calibrated 20070811 .equ baud_divider = 12 ; com port baud rate 57600 (0.2% too much) ; ; ; register variables used in the arithmetic ; .def var10 = r0 .def var11 = r1 .def var12 = r2 .def var13 = r3 .def var14 = r4 .def var15 = r5 .def var16 = r6 .def var17 = r7 .def var20 = r8 .def var21 = r9 .def var22 = r10 .def var23 = r11 .def var24 = r12 .def var25 = r13 .def var26 = r14 .def var27 = r15 .def mod0 = r16 .def mod1 = r17 .def mod2 = r18 .def mod3 = r19 .def mod4 = r20 .def mod5 = r21 .def mod6 = r22 .def mod7 = r23 .def lc = r24 .def accu = r25 ; Hardware Definition .INCLUDE "m16def.inc" ;*************************************************************************** ;**** VARIABLES .DSEG .ORG $060 LCD_line_buffer: .byte 16 ser_buffer_1: .byte 14 ser_buffer_2: .byte 14 frequency: .byte 4 increment: .byte 4 increment_pointer: .byte 1 start_frequency: .byte 4 end_frequency: .byte 4 sweep_increment: .byte 4 frequency_change_flag: .byte 1 button_pressed_flag: .byte 1 mode_sequence: .byte 1 mode_flag: .byte 1 gain_flag: .byte 1 mode_change_flag: .byte 1 sweep_flag: .byte 1 COM_port_flag: .byte 1 TX_buffer: .byte 22 TX_buffer_send: .byte 22 TX_pointer: .byte 2 TX_buffer_empty_flag: .byte 1 next_gen_time: .byte 2 FSK_flag: .byte 1 FSK_to_send: .byte 1 FSK_bit_position: .byte 1 FSK_pointer: .byte 2 FSK_bit_time: .byte 2 FSK_next_time: .byte 2 FSK_deviation: .byte 4 debounce_time: .byte 1 key_pressed_flag: .byte 1 key_value: .byte 1 old_key: .byte 1 very_old_key: .byte 1 n1_fwd_value: .byte 2 n2_fwd_value: .byte 2 n3_fwd_value: .byte 2 old_fwd_value: .byte 2 n1_rev_value: .byte 2 n2_rev_value: .byte 2 n3_rev_value: .byte 2 old_rev_value: .byte 2 n1_det_value: .byte 2 n2_det_value: .byte 2 n3_det_value: .byte 2 old_det_value: .byte 2 ;*************************************************************************** .ESEG signature: .byte 2 ; written with $55AA if RTTY message ok Baudot_message: .byte 330 ; about 30s of RTTY message Baudot_message_end: ;*************************************************************************** ;**** CODE SEG ;*************************************************************************** .CSEG .ORG $000 jmp reset ; Reset Handler jmp dummy ; IRQ0 Handler jmp dummy ; IRQ1 Handler jmp timer2_comp ; Timer2 Compare Handler jmp dummy ; Timer2 Overflow Handler jmp dummy ; Timer1 Capture Handler jmp timer1_compa ; Timer1 CompareA Handler jmp dummy ; Timer1 CompareB Handler jmp dummy ; Timer1 Overflow Handler jmp dummy ; Timer0 Overflow Handler jmp dummy ; SPI Transfer Complete Handler jmp dummy ; USART RX Complete Handler jmp uart_TD_empty ; UDR Empty Handler jmp dummy ; USART TX Complete Handler jmp dummy ; ADC Conversion Complete Handler jmp dummy ; EEPROM Ready Handler jmp dummy ; Analog Comparator Handler jmp dummy ; Two-wire Serial Interface Handler jmp dummy ; IRQ2 Handler jmp dummy ; Timer0 Compare Handler jmp dummy ; Store Program Memory Ready Handler dummy: reti ;*************************************************************************** ;**** INITIALISATION ;*************************************************************************** reset: ldi r16,low(Ramend) ;init stackpointer out SPL,r16 ldi r16,high(Ramend) out SPH,r16 ldi r16,HIGH(signature) ; check if message was already prepared out EEARH, r16 ldi r17,LOW(signature) out EEARL, r17 sbi EECR,EERE ; Start eeprom read by writing EERE in r16,EEDR ; Read data from data register cpi r16,$55 breq reset_0 jmp burn_message ; if not so, prepare the message reset_0: ldi r16,HIGH(signature+1) out EEARH, r16 ldi r17,LOW(signature+1) out EEARL, r17 sbi EECR,EERE ; Start eeprom read by writing EERE in r16,EEDR ; Read data from data register cpi r16,$AA breq reset_1 jmp burn_message ; if not so, prepare the message reset_1: ;short delay for Vdd rise, allowing the peripherals to stabilise ldi r16,10 reset_2: call wait5m dec r16 brne reset_2 ;init I/O ports ldi r16,0b00011111 out DDRA,r16 ldi r16,0b00001111 ; set nominal frequency in freg0 out PORTA,r16 ldi r16,0b11111111 out DDRB,r16 ldi r16,0b11111111 out PORTB,r16 ldi r16,0b00111101 out DDRC,r16 ldi r16,0b00000101 out PORTC,r16 ldi r16,0b11000010 out DDRD,r16 ;init USART ldi r16,0b00001000 ; Tx enable, nothing else for now out UCSRB,r16 ldi r16,0b10001110 ; 8 data bits, 2 stop bits out UCSRC,r16 ldi r16,baud_divider ; 57600 baud out UBRRL,r16 ; ldi r16,0 ; not sure we need this ; out UBRRH,r16 ;init the LDC (HD44780 controller) ;a LM041L module is used, it appears quite slow ;PB0-PB3 is LCD D0-D3 ;PA0-PA3 is LCD D4-D7 ;PD7 is LCD-RS (1=data,0=instruction) ;PD6 is LCD-CS (high to low edge is active) ;LCD R/W = write always cbi portd,6 ; set the register select low: instruction call wait5m ; initialise the LCD ldi r16,0b00111000 ; a strange sequence call lcdout call wait5m ; 2nd time call wait5m ldi r16,0b00111000 call lcdout ; 3rd time ldi r16,0b00111000 call lcdout ldi r16,0b00111000 ; function set call lcdout ; 8bit, 2lines, 5*7dots, ldi r16,0b00001110 call lcdout ; display on, cursor on ldi r16,0b00000110 call lcdout ; entry mode set ldi r16,0b00000001 call lcdout ; display clear call wait5m call wait5m sbi portd,6 ; set the register select high: data ; preset the variables clr r16 ; the power estimate variables sts n1_fwd_value,r16 sts n2_fwd_value,r16 sts n3_fwd_value,r16 sts old_fwd_value,r16 sts n1_rev_value,r16 sts n2_rev_value,r16 sts n3_rev_value,r16 sts old_rev_value,r16 sts n1_det_value,r16 sts n2_det_value,r16 sts n3_det_value,r16 sts old_det_value,r16 ldi r16,LOW(def_dev_freq) ; set to default 170Hz sts FSK_deviation,r16 ldi r16,BYTE2(def_dev_freq) sts FSK_deviation+1,r16 ldi r16,BYTE3(def_dev_freq) sts FSK_deviation+2,r16 ldi r16,BYTE4(def_dev_freq) sts FSK_deviation+3,r16 ldi r16,LOW(def_start_freq) ; set to default 7MHz sts frequency,r16 ldi r16,BYTE2(def_start_freq) sts frequency+1,r16 ldi r16,BYTE3(def_start_freq) sts frequency+2,r16 ldi r16,BYTE4(def_start_freq) sts frequency+3,r16 ldi r16,LOW(def_increment) ; set to default 1kHz sts increment,r16 ldi r16,BYTE2(def_increment) sts increment+1,r16 ldi r16,BYTE3(def_increment) sts increment+2,r16 ldi r16,BYTE4(def_increment) sts increment+3,r16 ldi r16,1 sts increment_pointer,r16 ; set the increment pointer ldi r16,LOW(def_start_frequency) ; set start sweep to default 7.0MHz sts start_frequency,r16 ldi r16,BYTE2(def_start_frequency) sts start_frequency+1,r16 ldi r16,BYTE3(def_start_frequency) sts start_frequency+2,r16 ldi r16,BYTE4(def_start_frequency) sts start_frequency+3,r16 ldi r16,LOW(def_end_frequency) ; set end sweep to default 7.1MHz sts end_frequency,r16 ldi r16,BYTE2(def_end_frequency) sts end_frequency+1,r16 ldi r16,BYTE3(def_end_frequency) sts end_frequency+2,r16 ldi r16,BYTE4(def_end_frequency) sts end_frequency+3,r16 ldi r16,LOW(def_sweep_increment) ; set sweep increment to default 10Hz sts sweep_increment,r16 ldi r16,BYTE2(def_sweep_increment) sts sweep_increment+1,r16 ldi r16,BYTE3(def_sweep_increment) sts sweep_increment+2,r16 ldi r16,BYTE4(def_sweep_increment) sts sweep_increment+3,r16 clr r16 sts sweep_flag,r16 ; set the increment pointer ser r16 ; force mode & frequency change sts mode_change_flag,r16 sts frequency_change_flag,r16 clr r16 sts FSK_flag,r16 ; force modulation flag sts mode_flag,r16 ; set mode flag clr r16 sts COM_port_flag,r16 ser r16 sts TX_buffer_empty_flag,r16 clr r16 sts mode_sequence,r16 ldi r16,LOW(def_bit_time) ; set to default 45.5 baud, = 10.98ms = 1/2 bit period sts FSK_bit_time,r16 ldi r17,HIGH(def_bit_time) sts FSK_bit_time+1,r17 ldi r16,0 ; prepare the ADC inputs ldi r17,0 sts old_fwd_value,r16 sts old_fwd_value+1,r17 sts old_rev_value,r16 sts old_rev_value+1,r17 ldi r29,high(ser_buffer_1) ; prepare the 1st serial string for the DDS chip ldi r28,low(ser_buffer_1) ldi r31,high(DDSstring_1*2) ; setup Z pointer hi ldi r30,low(DDSstring_1*2) ldi r16,14 preset_1: lpm r0,Z+ st y+,r0 dec r16 brne preset_1 ldi r29,high(ser_buffer_2) ; prepare the 2nd serial string for the DDS chip ldi r28,low(ser_buffer_2) ldi r31,high(DDSstring_2*2) ; setup Z pointer hi ldi r30,low(DDSstring_2*2) ldi r16,14 preset_2: lpm r0,Z+ st y+,r0 dec r16 brne preset_2 ldi r29,high(TX_buffer) ; prepare the text string for the COM port ldi r28,low(TX_buffer) ldi r31,high(TX_string*2) ; setup Z pointer hi ldi r30,low(TX_string*2) ldi r16,22 preset_3: lpm r0,Z+ st y+,r0 dec r16 brne preset_3 ; start the AD9835 ldi r17,$F8 ; reset all ldi r16,$00 rcall serout ldi r17,$80 ; set the freq/phase select ldi r16,$00 rcall serout ldi r17,$30 ; program 1st freq register ldi r16,LOW(def_start_freq) rcall serout ldi r17,$21 ldi r16,BYTE2(def_start_freq) rcall serout ldi r17,$32 ldi r16,BYTE3(def_start_freq) rcall serout ldi r17,$23 ldi r16,BYTE4(def_start_freq) rcall serout ldi r17,$34 ; program 2nd freq register ldi r16,LOW(def_start_freq+def_dev_freq) rcall serout ldi r17,$25 ldi r16,BYTE2(def_start_freq+def_dev_freq) rcall serout ldi r17,$36 ldi r16,BYTE3(def_start_freq+def_dev_freq) rcall serout ldi r17,$27 ldi r16,BYTE4(def_start_freq+def_dev_freq) rcall serout ldi r17,$C0 ; start everything ldi r16,$00 rcall serout ;init Timer1 for general timing ldi r16,0b00000000 out TCCR1A,r16 ; normal, no pins ldi r16,0b00000010 out TCCR1B,r16 ; clock = 12MHz/8 (0.6667µs), in r16,TCNT1L ; read TCNT1 into r17:r16 in r17,TCNT1H ldi r18,LOW(gen_time) ldi r19,HIGH(gen_time) add r16,r18 ; add general timing increment adc r17,r19 sts next_gen_time,r16 sts next_gen_time+1,r17 out OCR1BH,r17 ; preload output compare register out OCR1BL,r16 ;init Timer2 and keyboard debouncing routine ldi r16,0b00111100 ; preset variables sts very_old_key,r16 sts old_key,r16 sts key_value,r16 clr r16 sts key_pressed_flag,r16 ;init and enable timer2 interupts out TCNT2,r16 ; clear counter 2 register ldi r16,0b11111111 ; clear all timer related interupt flags out TIFR,r16 ldi r16,debounce_3ms; ; set compare to 35 * 85.33µs = 2.98ms out OCR2,r16 ; which is close enough to 3ms for persistency sts debounce_time, r16 ldi r16,0b00000111 ; then start the counter out TCCR2,r16 ; normal, no pins, clock = 12MHz/1024 (85.33µs) ldi r16,0b10000000 ; enable output compare timer 2 interrupt out TIMSK,r16 ; here we go, starting the keyboard debouncing, sei ; enable interupt system, rjmp main TX_string: .db "10000000,-" .db "10.0,-10.0" .db $20,$0D ; no line feed, a carriage returns seems to be enough DDSstring_1: ; default string, set at 10 000 000 Hz .dw $00F8, $5530, $5521, $5532, $3523, $0080, $00C0 DDSstring_2: .dw $00F8, $5534, $5525, $5536, $3527, $0080, $00C0 .dw $0000, $0000 ;*************************************************************************** ;**** MAIN RUN TIME ROUTINE ;*************************************************************************** ; this module runs continuously in normal mode, it is the heart of the firmware main: ldi r31,HIGH(first_line*2) ldi r30,LOW(first_line*2) ldi r29,HIGH(LCD_line_buffer) ldi r28,LOW(LCD_line_buffer) ldi r18,16 main_1: lpm r16,z+ st y+,r16 dec r18 brne main_1 rcall lcd_strout_1 ldi r31,HIGH(second_line*2) ldi r30,LOW(second_line*2) ldi r29,HIGH(LCD_line_buffer) ldi r28,LOW(LCD_line_buffer) ldi r18,16 main_2: lpm r16,z+ st y+,r16 dec r18 brne main_2 rcall lcd_strout_2 ldi r31,HIGH(third_line*2) ldi r30,LOW(third_line*2) ldi r29,HIGH(LCD_line_buffer) ldi r28,LOW(LCD_line_buffer) ldi r18,16 main_3: lpm r16,z+ st y+,r16 dec r18 brne main_3 rcall lcd_strout_3 ldi r31,HIGH(fourth_line*2) ldi r30,LOW(fourth_line*2) ldi r29,HIGH(LCD_line_buffer) ldi r28,LOW(LCD_line_buffer) ldi r18,16 main_4: lpm r16,z+ st y+,r16 dec r18 brne main_4 rcall lcd_strout_4 ;short delay for the publicity ldi r16,100 main_5: rcall wait5m dec r16 brne main_5 rjmp check_key first_line: .db "RF GENERATOR " second_line: .db "& DETECTOR " third_line: .db "(C°) J.Taeymans " fourth_line: .db "June 2007 " ;*************************************************************************** ;**** check any activity with the keys ;*************************************************************************** ; if "up" then freqency = frequency + increment ; don't allow frequency > 16MHz, set frequency_change_flag, clear button_pressed_flag ; if "down" then freqency = frequency - increment ; don't allow frequency < 250Hz, set frequency_change_flag, clear button_pressed_flag ; if "enter" then select next frequency increment, clear button_pressed_flag ; 10Hz, 100Hz, 1kHz, 10kHz, 100kHz and 1MHz ; if "menu" then go , clear button_pressed_flag, to the menu routines ; else go to "update mode and gain" check_key: lds r16,key_pressed_flag ; check key pressed flag cpi r16,0b00000000 brne check_key_0 jmp mode_update check_key_0: lds r16,key_value cpi r16,0b00110100 ; check for "up" brne check_key_1 lds r16,frequency ; get the current frequency lds r17,frequency+1 lds r18,frequency+2 lds r19,frequency+3 lds r20,increment ; get the current increment lds r21,increment+1 lds r22,increment+2 lds r23,increment+3 add r16,r20 ; add the increment to the frequency adc r17,r21 adc r18,r22 adc r19,r23 ldi r20,LOW(max_frequency) ; get the maximum frequency ldi r21,BYTE2(max_frequency) ldi r22,BYTE3(max_frequency) ldi r23,BYTE4(max_frequency) cp r16,r20 ; compare cpc r17,r21 cpc r18,r22 cpc r19,r23 brlo up_key_1 ldi r16,LOW(max_frequency) ; if higher, then saturate ldi r17,BYTE2(max_frequency) ldi r18,BYTE3(max_frequency) ldi r19,BYTE4(max_frequency) up_key_1: sts frequency,r16 ; restore the frequency sts frequency+1,r17 sts frequency+2,r18 sts frequency+3,r19 ser r16 sts frequency_change_flag,r16 rjmp check_key_4 ; return check_key_1: cpi r16,0b00011100 ; check for key "down" brne check_key_2 lds r16,frequency ; get the current frequency lds r17,frequency+1 lds r18,frequency+2 lds r19,frequency+3 lds r20,increment ; get the current increment lds r21,increment+1 lds r22,increment+2 lds r23,increment+3 sub r16,r20 ; subtract the increment from the frequency sbc r17,r21 sbc r18,r22 sbc r19,r23 ldi r20,LOW(min_frequency) ; get the minumum frequency ldi r21,BYTE2(min_frequency) ldi r22,BYTE3(min_frequency) ldi r23,BYTE4(min_frequency) cp r16,r20 ; compare cpc r17,r21 cpc r18,r22 cpc r19,r23 brge down_key_1 ldi r16,LOW(min_frequency) ; if lower, then saturate ldi r17,BYTE2(min_frequency) ldi r18,BYTE3(min_frequency) ldi r19,BYTE4(min_frequency) down_key_1: sts frequency,r16 ; restore the frequency sts frequency+1,r17 sts frequency+2,r18 sts frequency+3,r19 ser r16 sts frequency_change_flag,r16 rjmp check_key_4 ; return check_key_2: cpi r16,0b00101100 ; check for key "enter" brne check_key_3 lds r16,increment_pointer ; step through the increments inc r16 ; can be 0 ... 6 cpi r16,7 ; 7 is too much brne enter_key_1 clr r16 enter_key_1: sts increment_pointer,r16 clr r17 lsl r16 ; times 4 rol r17 lsl r16 rol r17 ldi r31,HIGH(increment_1*2) ; select the increment ldi r30,LOW(increment_1*2) add r30,r16 adc r31,r17 ldi r29,HIGH(increment) ldi r28,LOW(increment) ldi r18,4 enter_key_2: ; get the increment lpm r16,z+ st y+,r16 dec r18 brne enter_key_2 rcall digit_position rjmp check_key_4 ; return check_key_3: cpi r16,0b00111000 ; check for key "menu" brne check_key_4 rjmp menu_key_0 check_key_4: clr r16 ; clr key pressed flag sts key_pressed_flag,r16 rjmp mode_update increment_1: .dw LWRD(1000000) .dw HWRD(1000000) increment_2: .dw LWRD(100000) .dw HWRD(100000) increment_3: .dw LWRD(10000) .dw HWRD(10000) increment_4: .dw LWRD(1000) .dw HWRD(1000) increment_5: .dw LWRD(100) .dw HWRD(100) increment_6: .dw LWRD(10) .dw HWRD(10) increment_7: .dw LWRD(1) .dw HWRD(1) ;*************************************************************************** ;**** update mode and gain ;*************************************************************************** ; if mode_change_flag is set then ; set Ry1, Ry2 and Ry accordingly ; update line 1 of LCD, check for modulation_flag and COM_port_flag ; clear mode_change_flag ; else "check if sweeping" mode_update: lds r16,mode_change_flag ; check mode change flag cpi r16,0b00000000 brne mode_update_00 jmp power_estimate mode_update_00: lds r16,mode_flag ; check mode flag cpi r16,0b0000000 brne mode_update_0 ; if not zero, then in reflection mode sbi portc,5 ; set the input selection relay ldi r31,HIGH(first_line_trans*2) ldi r30,LOW(first_line_trans*2) ldi r29,HIGH(LCD_line_buffer) ldi r28,LOW(LCD_line_buffer) ldi r18,16 rjmp mode_update_1 mode_update_0: cbi portc,5 ; clear the input selection relay ldi r31,HIGH(first_line_refl*2) ldi r30,LOW(first_line_refl*2) ldi r29,HIGH(LCD_line_buffer) ldi r28,LOW(LCD_line_buffer) ldi r18,16 mode_update_1: lpm r16,z+ st y+,r16 dec r18 brne mode_update_1 lds r16,FSK_flag ; mark if FSK modulation is on cpi r16,0b00000000 breq mode_update_2 ldi r16,$4d ; if so, add "M" for modulation sts LCD_line_buffer+15,r16 mode_update_2: lds r16,COM_port_flag ; mark if serial transmit is on cpi r16,0b00000000 breq mode_update_3 ldi r16,$43 ; if so, add "C" for communication sts LCD_line_buffer+14,r16 mode_update_3: rcall lcd_strout_1 clr r16 ; clr mode change flag sts mode_change_flag,r16 rjmp power_estimate first_line_trans: .db "Transfer mode " first_line_refl: .db "Reflect. mode " ;*************************************************************************** ;**** power estimation ;*************************************************************************** ; set mux to fwd power and start AD conversion ; get result and scale it ; set mux to rfl power and start AD conversion ; get result and scale it ; if mode = tranfer then ; convert fwd power to ascii ; update 3 line of LCD ; clear line 4 of LCD ; go to "output through COM port" ; else then ; convert fwd power to ascii ; update 3 line of LCD ; convert rfl power to ascii ; update 4 line of LCD ; go to "output through COM port" ; third_line_refl: .db "Fwd. . dBm " fourth_line_refl: .db "Rev. . dBm " third_line_trans: .db "Det. . dBm " fourth_line_trans: .db " " power_estimate: in r16,TIFR ; check if 20ms has already passed andi r16,0b00001000 ; use timer 1 output compare B flag breq power_estimate ldi r16,0b00001000 ; clear interupt flag timer 1 OCR1B out TIFR,r16 lds r17,next_gen_time+1 lds r16,next_gen_time ldi r19,HIGH(gen_time) ldi r18,LOW(gen_time) add r16,r18 ; calculate next period adc r17,r19 sts next_gen_time+1,r17 sts next_gen_time,r16 out OCR1BH,r17 ; preload output compare register out OCR1BL,r16 ; ldi r16,10 ; wait for a long time ; debug_1: ; rcall wait5m ; dec r16 ; brne debug_1 cbi portd,6 ; we will write some to the display, hence ldi r16,0b00001100 rcall lcdout ; display on, cursor off sbi portd,6 ; here we are ready to start the power estimation lds r16,mode_flag ; check mode flag cpi r16,0b0000000 breq power_estimate_trans jmp power_estimate_rfl ; if not zero, then in reflection mode power_estimate_trans: ; strobe MAX1286 for the 1st channel ; wait for end of conversion ; get the 12bit data rcall adcin ; result in r16,17, scaled by 16 lds r18,n1_det_value ; implement a small FIR filter lds r19,n1_det_value+1 ; averaging four results lds r20,n2_det_value ; get the 3 previous lds r21,n2_det_value+1 lds r22,n3_det_value lds r23,n3_det_value+1 sts n1_det_value,r16 ; arrange the results for next time sts n1_det_value+1,r17 sts n2_det_value,r18 ; get the youngest in, discarding the oldest sts n2_det_value+1,r19 sts n3_det_value,r20 sts n3_det_value+1,r21 add r20,r22 adc r21,r23 ; the averaging by adding add r18,r20 adc r19,r21 add r16,r18 adc r17,r19 lsr r17 ; and dividing by 4 ror r16 lsr r17 ror r16 ; ready now as, as we were lds r18,old_det_value lds r19,old_det_value+1 cp r16,r18 cpc r17,r19 brne power_estimate_trans_0 rjmp power_estimate_trans_5 power_estimate_trans_0: sts old_det_value,r16 sts old_det_value+1,r17 ; convert to dBm = val*coef - coef (signed 16 bit) clr r0 clr r1 clr r2 clr r3 clr r4 clr r5 mov r6,r16 mov r7,r17 ; get result ready to divide clr r8 clr r9 clr r10 clr r11 ldi r16,LOW(power_coef_3) ; divisor is 2.4 mV per 0.1dB mov r12,r16 ldi r16,BYTE2(power_coef_3) ; value is multiplied by 65536 mov r13,r16 ldi r16,BYTE3(power_coef_3) mov r14,r16 ldi r16,BYTE4(power_coef_3) ; got rid of the scaling factor at the same time mov r15,r16 ; the 64bit divide by 64bit is a bit of an overkill rcall div64u ldi r16,LOW(power_offset_3) ldi r17,HIGH(power_offset_3) ; result of division is now in r1,r0 in 0.1dBm (signed) sub r0,r16 sbc r1,r17 ; 0V at the ADC inputs yields -95.3dBm ; display power, prepare output format ldi r31,HIGH(third_line_trans*2) ; prepare the lCD message ldi r30,LOW(third_line_trans*2) ldi r29,HIGH(LCD_line_buffer) ldi r28,LOW(LCD_line_buffer) ldi r18,16 power_estimate_trans_1: lpm r16,z+ st y+,r16 dec r18 brne power_estimate_trans_1 ldi r18,$20 ; prepare text buffer sts TX_buffer+9,r18 mov r16,r0 mov r17,r1 sbrs r17,7 ; test if negative rjmp power_estimate_trans_2 com r16 ;invert low byte com r17 ;invert high byte subi r16,$ff ;add 0x0001, low byte sbci r17,$ff ;add high byte ldi r18,$2d ; ascii "-" sts LCD_line_buffer+5,r18 sts TX_buffer+9,r18 power_estimate_trans_2: ldi r18,$30 ; ascii nul mov r19,r18 power_estimate_trans_3: inc r19 subi r16,$64 ; subtract 100 sbci r17,$00 ; a number of times brpl power_estimate_trans_3 dec r19 ; too far ? subi r16,$9c ; correct and end sbci r17,$ff sts LCD_line_buffer+6,r19 sts TX_buffer+10,r19 ; prepare text buffer mov r19,r18 power_estimate_trans_4: inc r19 subi r16,$0a ; subtract 10 sbci r17,$00 ; a number of times brpl power_estimate_trans_4 dec r19 ; too far ? subi r16,$f6 ; correct and end sbci r17,$ff sts LCD_line_buffer+7,r19 sts TX_buffer+11,r19 ; prepare text buffer subi r16,$d0 ; remaining units sts LCD_line_buffer+9,r16 sts TX_buffer+13,r19 ; prepare text buffer rcall lcd_strout_3 ; display result on line 3 power_estimate_trans_5: ldi r31,HIGH(fourth_line_trans*2) ; prepare the lCD message ldi r30,LOW(fourth_line_trans*2) ldi r29,HIGH(LCD_line_buffer) ldi r28,LOW(LCD_line_buffer) ldi r18,16 power_estimate_trans_6: lpm r16,z+ st y+,r16 dec r18 brne power_estimate_trans_6 rcall lcd_strout_4 ; clear display on line 4 ldi r16,$20 sts TX_buffer+15,r16 ; prepare text buffer sts TX_buffer+16,r16 ; prepare text buffer ldi r16,$30 sts TX_buffer+17,r16 ; prepare text buffer sts TX_buffer+19,r16 ; prepare text buffer rjmp power_estimate_9 power_estimate_rfl: ; strobe MAX1286 for the 1st channel ; wait for end of conversion ; get the 12bit data rcall adcin ; result in r16,17, scaled by 16 lds r18,n1_fwd_value ; implement a small FIR filter lds r19,n1_fwd_value+1 ; averaging four results lds r20,n2_fwd_value ; get the 3 previous lds r21,n2_fwd_value+1 lds r22,n3_fwd_value lds r23,n3_fwd_value+1 sts n1_fwd_value,r16 ; arrange the results for next time sts n1_fwd_value+1,r17 sts n2_fwd_value,r18 ; get the youngest in, discarding the oldest sts n2_fwd_value+1,r19 sts n3_fwd_value,r20 sts n3_fwd_value+1,r21 add r20,r22 adc r21,r23 ; the averaging by adding add r18,r20 adc r19,r21 add r16,r18 adc r17,r19 lsr r17 ; and dividing by 4 ror r16 lsr r17 ror r16 ; ready now as, as we were lds r18,old_fwd_value lds r19,old_fwd_value+1 cp r16,r18 cpc r17,r19 brne power_estimate_0 rjmp power_estimate_channel_2 power_estimate_0: sts old_fwd_value,r16 sts old_fwd_value+1,r17 ; convert to dBm = val*coef - coef (signed 16 bit) clr r0 clr r1 clr r2 clr r3 clr r4 clr r5 mov r6,r16 mov r7,r17 ; get result ready to divide clr r8 clr r9 clr r10 clr r11 ldi r16,LOW(power_coef_1) ; divisor is 2.4 mV per 0.1dB mov r12,r16 ldi r16,BYTE2(power_coef_1) ; value is multiplied by 65536 mov r13,r16 ldi r16,BYTE3(power_coef_1) mov r14,r16 ldi r16,BYTE4(power_coef_1) ; got rid of the scaling factor at the same time mov r15,r16 ; the 64bit divide by 64bit is a bit of an overkill rcall div64u ldi r16,LOW(power_offset_1) ldi r17,HIGH(power_offset_1) ; result of division is now in r1,r0 in 0.1dBm (signed) sub r0,r16 sbc r1,r17 ; 0V at the ADC inputs yields -95.3dBm ; display power, prepare output format ldi r31,HIGH(third_line_refl*2) ; prepare the lCD message ldi r30,LOW(third_line_refl*2) ldi r29,HIGH(LCD_line_buffer) ldi r28,LOW(LCD_line_buffer) ldi r18,16 power_estimate_1: lpm r16,z+ st y+,r16 dec r18 brne power_estimate_1 ldi r18,$20 ; prepare text buffer sts TX_buffer+9,r18 mov r16,r0 mov r17,r1 sbrs r17,7 ; test if negative rjmp power_estimate_2 com r16 ;invert low byte com r17 ;invert high byte subi r16,$ff ;add 0x0001, low byte sbci r17,$ff ;add high byte ldi r18,$2d ; ascii "-" sts LCD_line_buffer+5,r18 sts TX_buffer+9,r18 power_estimate_2: ldi r18,$30 ; ascii nul mov r19,r18 power_estimate_3: inc r19 subi r16,$64 ; subtract 100 sbci r17,$00 ; a number of times brpl power_estimate_3 dec r19 ; too far ? subi r16,$9c ; correct and end sbci r17,$ff sts LCD_line_buffer+6,r19 sts TX_buffer+10,r19 ; prepare text buffer mov r19,r18 power_estimate_4: inc r19 subi r16,$0a ; subtract 10 sbci r17,$00 ; a number of times brpl power_estimate_4 dec r19 ; too far ? subi r16,$f6 ; correct and end sbci r17,$ff sts LCD_line_buffer+7,r19 sts TX_buffer+11,r19 ; prepare text buffer subi r16,$d0 ; remaining units sts LCD_line_buffer+9,r16 sts TX_buffer+13,r19 ; prepare text buffer rcall lcd_strout_3 ; display result on line 3 power_estimate_channel_2: ; strobe MAX1286 for the 2nd channel ; wait for end of conversion ; get the 12bit data sbi portc,0 ; assert CVST (active high) for a short time cbi portc,0 ; to select the 2nd channel rcall adcin ; result in r16,17, scaled by 16 lds r18,n1_rev_value ; implement a small FIR filter lds r19,n1_rev_value+1 ; averaging four results lds r20,n2_rev_value ; get the 3 previous lds r21,n2_rev_value+1 lds r22,n3_rev_value lds r23,n3_rev_value+1 sts n1_rev_value,r16 ; arrange the results for next time sts n1_rev_value+1,r17 sts n2_rev_value,r18 ; get the youngest in, discarding the oldest sts n2_rev_value+1,r19 sts n3_rev_value,r20 sts n3_rev_value+1,r21 add r20,r22 adc r21,r23 ; the averaging by adding all add r18,r20 adc r19,r21 add r16,r18 adc r17,r19 lsr r17 ; and dividing by 4 ror r16 lsr r17 ror r16 ; ready now as, as we were lds r18,old_rev_value lds r19,old_rev_value+1 cp r16,r18 cpc r17,r19 brne power_estimate_50 rjmp power_estimate_9 power_estimate_50: sts old_rev_value,r16 sts old_rev_value+1,r17 ; convert to dBm = val*coef - coef (signed 16 bit) clr r0 clr r1 clr r2 clr r3 clr r4 clr r5 mov r6,r16 mov r7,r17 ; get result ready to divide clr r8 clr r9 clr r10 clr r11 ldi r16,LOW(power_coef_2) ; divisor is 2.4 mV per 0.1dB mov r12,r16 ldi r16,BYTE2(power_coef_2) ; value is multiplied by 65536 mov r13,r16 ldi r16,BYTE3(power_coef_2) mov r14,r16 ldi r16,BYTE4(power_coef_2) ; got rid of the scaling factor at the same time mov r15,r16 rcall div64u ldi r16,LOW(power_offset_2) ldi r17,HIGH(power_offset_2) ; result of division is now in r1,r0 in 0.1dBm (signed) sub r0,r16 sbc r1,r17 ; 0V at the ADC inputs yields -95.3dBm ; display power, prepare output format ldi r31,HIGH(fourth_line_refl*2) ; prepare the lCD message ldi r30,LOW(fourth_line_refl*2) ldi r29,HIGH(LCD_line_buffer) ldi r28,LOW(LCD_line_buffer) ldi r18,16 power_estimate_5: lpm r16,z+ st y+,r16 dec r18 brne power_estimate_5 ldi r18,$20 sts TX_buffer+15,r18 ; prepare text buffer mov r16,r0 mov r17,r1 sbrs r17,7 ; test if negative rjmp power_estimate_6 com r16 ;invert low byte com r17 ;invert high byte subi r16,$ff ;add 0x0001, low byte sbci r17,$ff ;add high byte ldi r18,$2d ; ascii "-" sts LCD_line_buffer+5,r18 sts TX_buffer+15,r18 ; prepare text buffer power_estimate_6: ldi r18,$30 ; ascii nul mov r19,r18 power_estimate_7: inc r19 subi r16,$64 ; subtract 100 sbci r17,$00 ; a number of times brpl power_estimate_7 dec r19 ; too far ? subi r16,$9c ; correct and end sbci r17,$ff sts LCD_line_buffer+6,r19 sts TX_buffer+16,r19 ; prepare text buffer mov r19,r18 power_estimate_8: inc r19 subi r16,$0a ; subtract 10 sbci r17,$00 ; a number of times brpl power_estimate_8 dec r19 ; too far ? subi r16,$f6 ; correct and end sbci r17,$ff sts LCD_line_buffer+7,r19 sts TX_buffer+17,r19 ; prepare text buffer subi r16,$d0 ; remaining units sts LCD_line_buffer+9,r16 sts TX_buffer+19,r19 ; prepare text buffer rcall lcd_strout_4 ; display result on line 3 power_estimate_9: rjmp sweep_0 ;*************************************************************************** ;**** check if sweeping ;*************************************************************************** ; if sweep_flag is set then frequency = frequency + sweep_increment ; if frequency = end_sweep then frequency = begin_sweep ; if frequency = begin sweep then set sweep_sync_out, else clear sweep_sync_out ; set frequency_change_flag ; else go to "frequency updating" sweep_0: cbi portc,4 ; clear external sweep sync bit lds r16,sweep_flag cpi r16,0b00000000 ; check if sweeping breq freq_update lds r16,frequency ; load current frequency lds r17,frequency+1 lds r18,frequency+2 lds r19,frequency+3 lds r20,sweep_increment ; load sweep increment lds r21,sweep_increment+1 lds r22,sweep_increment+2 lds r23,sweep_increment+3 add r16,r20 ; increment frequency adc r17,r21 adc r18,r22 adc r19,r23 lds r20,end_frequency ; load end frequency lds r21,end_frequency+1 lds r22,end_frequency+2 lds r23,end_frequency+3 cp r16,r20 ; check if not over end frequency cpc r17,r21 cpc r18,r22 cpc r19,r23 brlo sweep_1 lds r16,start_frequency ; if higher, then start over lds r17,start_frequency+1 lds r18,start_frequency+2 lds r19,start_frequency+3 sbi portc,4 ; set external sweep sync bit, only for 20ms sweep_1: sts frequency,r16 ; restore the frequency sts frequency+1,r17 sts frequency+2,r18 sts frequency+3,r19 ser r16 sts frequency_change_flag,r16 ; frequency has canged rjmp freq_update ;*************************************************************************** ;**** frequency updating ;*************************************************************************** ; if frequency_change_flag then ; convert frequency to ascii and update line 2 of LCD ; perform DDS calculation ; update freq0reg of AD9835 ; add FSK_deviation to frequency ; perform DDS calculation ; update freq1reg of AD9835 ; else clear frequency_change_flag ; go to "power estimation" ; ; Display in this mode: ; ****************** ;.*Refl. -30dBm OM* note the Serial & Modulation indicators ; *Fr: 15,123,450Hz* ; *Fwd: -20.5dBm * ; *Rfl: -50.5dBm * ; ****************** ; freq_update: lds r16,frequency_change_flag cpi r16,0b00000000 brne freq_update_0 jmp freq_update_5 freq_update_0: ldi r31,HIGH(second_line_run*2) ldi r30,LOW(second_line_run*2) ldi r29,HIGH(LCD_line_buffer) ldi r28,LOW(LCD_line_buffer) ldi r18,16 freq_update_1: lpm r16,z+ st y+,r16 dec r18 brne freq_update_1 rjmp bin_bcd second_line_run: .db "Fr. , , Hz" ;****************************************************************************** ; display frequency ;****************************************************************************** ; format: "..10,000,000Hz.." ; prepare output format bin_bcd: lds r20,frequency ; load current frequency lds r21,frequency+1 lds r22,frequency+2 lds r23,frequency+3 rcall disp_freq ; display the frequency on line 2 ;***************************************************************************** ; DDS_inc = (DDS_out * 2^32) / XTAL ; 32.32bit / 0.32bit => 64bit, shifted by 4 bytes ; result is 32bits ; 10000000 * 4294967296 / 48000000 = 894784853 ; var17..var10 / var23..var20 = var13..var10 ; $00989680.00000000 / $00000000.02DC6C00 = $00000000.35555555 ; dds_inc = $35555555 dds_0: clr var10 ; get the frequency clr var11 clr var12 clr var13 lds r16,frequency mov var14,r16 lds r16,frequency+1 mov var15,r16 lds r16,frequency+2 mov var16,r16 lds r16,frequency+3 mov var17,r16 ldi r16,LOW(Xtal_frequency) ; get the Xtal frequency mov var20,r16 ldi r16,BYTE2(Xtal_frequency) mov var21,r16 ldi r16,BYTE3(Xtal_frequency) mov var22,r16 ldi r16,BYTE4(Xtal_frequency) mov var23,r16 clr var24 clr var25 clr var26 clr var27 rcall div64u ldi r16,$30 sts ser_buffer_1+2,r16 sts ser_buffer_1+3,var10 ; the dds increment is known ! ldi r16,$21 sts ser_buffer_1+4,r16 sts ser_buffer_1+5,var11 ldi r16,$32 sts ser_buffer_1+6,r16 sts ser_buffer_1+7,var12 ldi r16,$23 sts ser_buffer_1+8,r16 sts ser_buffer_1+9,var13 ldi r29,high(ser_buffer_1+2) ldi r28,low(ser_buffer_1+2) ldi r19,4 ; do for four words dds_1: ld r17,y+ ld r16,y+ ; first byte $30 rolls out MSB first rcall serout dec r19 brne dds_1 ;***************************************************************************** ; do the same with the 2nd frequency for the FSK lds r16,FSK_deviation ; get the deviation frequency mov var10,r16 lds r16,FSK_deviation+1 mov var11,r16 lds r16,FSK_deviation+2 mov var12,r16 lds r16,FSK_deviation+3 mov var13,r16 lds r16,frequency ; get the frequency mov var14,r16 lds r16,frequency+1 mov var15,r16 lds r16,frequency+2 mov var16,r16 lds r16,frequency+3 mov var17,r16 sub var14,var10 ; do some substracting sbc var15,var11 ; to get the start frequency sbc var16,var12 sbc var17,var13 clr var10 ; and clean the variables again clr var11 clr var12 clr var13 ldi r16,LOW(Xtal_frequency) ; get the Xtal frequency mov var20,r16 ldi r16,BYTE2(Xtal_frequency) mov var21,r16 ldi r16,BYTE3(Xtal_frequency) mov var22,r16 ldi r16,BYTE4(Xtal_frequency) mov var23,r16 clr var24 clr var25 clr var26 clr var27 rcall div64u ldi r16,$34 sts ser_buffer_1+2,r16 sts ser_buffer_1+3,var10 ; the dds increment is known ! ldi r16,$25 sts ser_buffer_1+4,r16 sts ser_buffer_1+5,var11 ldi r16,$36 sts ser_buffer_1+6,r16 sts ser_buffer_1+7,var12 ldi r16,$27 sts ser_buffer_1+8,r16 sts ser_buffer_1+9,var13 ldi r29,high(ser_buffer_1+2) ldi r28,low(ser_buffer_1+2) ldi r19,4 ; do for four words dds_2: ld r17,y+ ld r16,y+ ; first byte: $34 rolls out MSB first rcall serout dec r19 brne dds_2 freq_update_5: clr r16 ; frequency update done sts frequency_change_flag,r16 cbi portd,6 ; no more LCD writing, hence set cursor or ldi r16,0b00001110 rcall lcdout ; display on, cursor on sbi portd,6 rcall digit_position ;*************************************************************************** ;**** output through the COM port ;*************************************************************************** ; if COM_port_flag is then ; if TX_buffer is empty then ; write ascii frequency in TX_buffer, followed by a comma ; write ascii fwd power in TX_buffer, followed by a comma ; write ascii rfl power in TX_buffer, followed by CR/LF ; enable TX_empty interrupt, ; clear TX_buffer_emty_flag ; and start serial transmit ; else wait of buffer empty ; go to "end of run time routine" com_port: lds r16,COM_port_flag ; check if com mode enabled cpi r16,0b00000000 breq com_port_2 com_port_0: lds r16,TX_buffer_empty_flag ; wait for TX buffer empty cpi r16,0b00000000 ; no wait in principle breq com_port_0 ; as the full buffer transfer takes only 12ms ldi r26,LOW(TX_buffer) ; tranfer form first TX buffer to second ldi r27,HIGH(TX_buffer) ldi r28,LOW(TX_buffer_send) ; double buffering to avoid aliasing ldi r29,HIGH(TX_buffer_send) ldi r17,22 com_port_1: ld r16,x+ st y+,r16 dec r17 brne com_port_1 clr r16 sts TX_buffer_empty_flag,r16 ; TX buffer is not empty anymore ldi r16,LOW(TX_buffer_send) ; pre set the pointer ldi r17,HIGH(TX_buffer_send) sts TX_pointer,r16 sts TX_pointer+1,r17 sbi UCSRB,5 ; enable the TX empty again com_port_2: jmp check_key ;*************************************************************************** ;**** end of run time loop ;*************************************************************************** ; go back to run time routine start ;*************************************************************************** ;**** MENU ROUTINE ;*************************************************************************** ; this module runs when activated by the user ; ;*************************************************************************** ;**** mode & gain setting ;*************************************************************************** ; update LCD accordingly ; LCD in this mode : ; ****************** ;.*mode setting : * ; *trans. / refl. * ; * 0dBm / -30dBm * ; * * ; ****************** ; update the LCD on line 2 ; if "up" then mode_flag = tranfer ; set mode_change_flag ; if "down" then mode_flag = reflect ; set mode_change_flag ; if "enter" then toggle the gain 0dBm / -30dBm ; if "menu" then "modulation setting" ; else go to "mode setting" mode_sel_line: .db $7E," mode select ",$7F mode_trans_line: .db "Transfer mode " mode_refl_line: .db "Reflection mode " mode_out_line: .db "Out level:-30dBm" mode_blank_line: .db " " menu_key_0: mode_sel_0: clr r16 ; clr key pressed flag sts key_pressed_flag,r16 mode_sel_1: ldi r31,HIGH(mode_sel_line*2) ; prepare the display ldi r30,LOW(mode_sel_line*2) ldi r29,HIGH(LCD_line_buffer) ldi r28,LOW(LCD_line_buffer) ldi r18,16 mode_sel_2: lpm r16,z+ st y+,r16 dec r18 brne mode_sel_2 rcall lcd_strout_1 ldi r31,HIGH(mode_trans_line*2) ldi r30,LOW(mode_trans_line*2) lds r16,mode_flag ; check mode flag cpi r16,0b0000000 breq mode_sel_3 ; if not zero, then in reflection mode ldi r31,HIGH(mode_refl_line*2) ldi r30,LOW(mode_refl_line*2) mode_sel_3: ldi r29,HIGH(LCD_line_buffer) ldi r28,LOW(LCD_line_buffer) ldi r18,16 mode_sel_4: lpm r16,z+ st y+,r16 dec r18 brne mode_sel_4 rcall lcd_strout_2 ldi r31,HIGH(mode_out_line*2) ; display the output level setting ldi r30,LOW(mode_out_line*2) ldi r29,HIGH(LCD_line_buffer) ldi r28,LOW(LCD_line_buffer) ldi r18,16 mode_sel_5: lpm r16,z+ st y+,r16 dec r18 brne mode_sel_5 sbic portc,3 ; check portc,3, if high then -30dBm output rjmp mode_sel_6 ldi r16,$20 sts LCD_line_buffer+10,r16 sts LCD_line_buffer+11,r16 ; blank the "-3" to make 0dBm output mode_sel_6: rcall lcd_strout_3 ldi r31,HIGH(mode_blank_line*2) ; blank the last line ldi r30,LOW(mode_blank_line*2) ldi r29,HIGH(LCD_line_buffer) ldi r28,LOW(LCD_line_buffer) ldi r18,16 mode_sel_7: lpm r16,z+ st y+,r16 dec r18 brne mode_sel_7 rcall lcd_strout_4 mode_key_0: lds r16,key_pressed_flag ; check key pressed flag cpi r16,0b00000000 breq mode_key_0 ; stay here until a key is pressed ; should add a long time out here lds r16,key_value cpi r16,0b00110100 ; check for "up" brne mode_key_1 clr r16 sts mode_flag,r16 ; clear mode flag, => transfer mode rjmp mode_sel_0 ; and return mode_key_1: cpi r16,0b00011100 ; check for key "down" brne mode_key_2 ser r16 sts mode_flag,r16 ; set mode flag, => reflection mode rjmp mode_sel_0 ; and return mode_key_2: cpi r16,0b00101100 ; check for key "enter" brne mode_key_3 sbic portc,3 ; check portc,3, if high then -30dB pad rjmp mode_key_20 sbi portc,3 ; if clear then set portc,3 rjmp mode_sel_0 ; and return mode_key_20: cbi portc,3 ; if set then clear portc,3 rjmp mode_sel_0 ; and return mode_key_3: cpi r16,0b00111000 ; check for key "menu" brne mode_key_0 ; catch the rest, an odd key was pressed rjmp modu_sel_0 ; and go the next menu item ;*************************************************************************** ;**** modulation setting ;*************************************************************************** ; update LCD accordingly ; LCD in this mode : ; ****************** ;.*modulation set. * ; *on / off * ; *fd: 170Hz * ; *baud: 45.5 * ; ****************** ; update the LCD on line 2 ; if "up" then modulation_flag = on, enable T2 timer ; set mode_change_flag ; if "down" then modulation_flag = off, disable T2 timer, clear FSELECT port ; set mode_change_flag ; if "enter" then go to "run time routine start" ; if "menu" then go to "COM port setting" ; else got to "modulation setting" modu_sel_line: .db $7E," FSK select ",$7F modu_on_line: .db "On " modu_off_line: .db "Off " modu_dev_line: .db "deviation: 170Hz" modu_baud_line: .db "baud: 45.5/s " modu_sel_0: clr r16 ; clr key pressed flag sts key_pressed_flag,r16 modu_sel_1: ldi r31,HIGH(modu_sel_line*2) ; prepare the display ldi r30,LOW(modu_sel_line*2) ldi r29,HIGH(LCD_line_buffer) ldi r28,LOW(LCD_line_buffer) ldi r18,16 modu_sel_2: lpm r16,z+ st y+,r16 dec r18 brne modu_sel_2 rcall lcd_strout_1 ldi r31,HIGH(modu_off_line*2) ldi r30,LOW(modu_off_line*2) lds r16,FSK_flag ; check modulation flag cpi r16,0b0000000 breq modu_sel_3 ; if not zero, then modulation is on ldi r31,HIGH(modu_on_line*2) ldi r30,LOW(modu_on_line*2) modu_sel_3: ldi r29,HIGH(LCD_line_buffer) ldi r28,LOW(LCD_line_buffer) ldi r18,16 modu_sel_4: lpm r16,z+ st y+,r16 dec r18 brne modu_sel_4 rcall lcd_strout_2 ldi r31,HIGH(modu_dev_line*2) ; display the deviation setting ldi r30,LOW(modu_dev_line*2) ldi r29,HIGH(LCD_line_buffer) ldi r28,LOW(LCD_line_buffer) ldi r18,16 modu_sel_5: lpm r16,z+ st y+,r16 dec r18 brne modu_sel_5 rcall lcd_strout_3 ldi r31,HIGH(modu_baud_line*2) ; display the baud rate ldi r30,LOW(modu_baud_line*2) ldi r29,HIGH(LCD_line_buffer) ldi r28,LOW(LCD_line_buffer) ldi r18,16 modu_sel_6: lpm r16,z+ st y+,r16 dec r18 brne modu_sel_6 rcall lcd_strout_4 modu_key_0: lds r16,key_pressed_flag ; check key pressed flag cpi r16,0b00000000 breq modu_key_0 ; stay here until a key is pressed ; should add a long time out lds r16,key_value cpi r16,0b00110100 ; check for "up" brne modu_key_1 ser r16 sts FSK_flag,r16 ; set FSK flag, => modulation on ; start_modulation: init Timer1 and FSK routine cli ; this must be atomic in r16,TCNT1L ; read TCNT1 into r17:r16 in r17,TCNT1H lds r19,FSK_bit_time+1 lds r18,FSK_bit_time add r16,r18 ; add with FSK timing increment adc r17,r19 sts FSK_next_time+1,r17 sts FSK_next_time,r16 out OCR1AH,r17 ; preload output compare register out OCR1AL,r16 ser r16 sts FSK_to_send,r16 ; prepare FSK patern clr r16 sts FSK_bit_position,r16 ldi r17,HIGH(Baudot_message) ldi r16,LOW(Baudot_message) sts FSK_pointer,r16 sts FSK_pointer+1,r17 ldi r16,0b00111100 ; clear all timer 1 related interrupt flag out TIFR,r16 ldi r16,0b10010000 ; enable output compare interrupt for timer 1 and timer 2 out TIMSK,r16 sei ; enable interrupts rjmp modu_sel_0 ; and return modu_key_1: cpi r16,0b00011100 ; check for key "down" brne modu_key_2 clr r16 sts FSK_flag,r16 ; clear FSK flag, => modulation off ; stop_modulation: cli ldi r16,0b10000000 ; enable output compare interrupt for timer 2 only out TIMSK,r16 sei clr r16 sts FSK_flag,r16 cbi porta,4 ; set to nominal frequency in freg0 rjmp modu_sel_0 ; and return modu_key_2: cpi r16,0b00111000 ; check for key "menu" brne modu_key_0 ; catch the rest, an odd key was pressed rjmp com_sel_0 ; and go the next menu item ; **** get deviation frequency : ; update the LCD on line 3 ; if "up" then FSK_deviation = FSK_deviation + increment ; don't allow end_frequency > 1kHz, ; if "down" then FSK_deviation = FSK_deviation - increment ; don't allow frequency < 10Hz, ; if "enter" then select next frequency increment ; 10Hz, 100Hz, 1kHz ; if "menu" then go to "get baud rate" ; else go to "get deviation frequency" ; **** get baud rate : ; update the LCD on line 4 ; if "up" then baud = 50 and FSK_bit_time = 10.00ms ; if "down" then baud = 45 and FSK_bit_time = 11.11ms ; if "enter" then go to "run time routine start" ; if "menu" then go to "COM port setting" ; else go to "get baud rate" ; ;*************************************************************************** ;**** COM port setting ;*************************************************************************** ; update LCD accordingly ; LCD in this mode : ; ****************** ;.* COM port set. * ; *on / off * ; * * ; * * ; ****************** ; update the LCD on line 2 ; if "up" then COM_port_flag = on, clear TX_buffer ; set mode_flag ; if "down" then COM_port_flag is off ; set mode_flag ; if "enter" then go to "run time routine start" ; if "menu" then ; if "off" go to "sweep setting" ; else go to ; else go to "COM port setting" ; **** get COM baud rate : ; update the LCD on line 3 ; if "up" then baud = 19200 and baud_divider = 38 ; if "down" then baud 9600 and baud_divider = 77 ; if "enter" then go to "run time routine start" ; if "menu" then go to "sweep setting" ; else go to "get COM baud rate" ; com_sel_line: .db $7E,"COMport select",$7F com_on_line: .db "On " com_off_line: .db "Off " com_blank_line: .db " " com_baud_line: .db "baud: 57600/s " com_sel_0: clr r16 ; clr key pressed flag sts key_pressed_flag,r16 com_sel_1: ldi r31,HIGH(com_sel_line*2) ; prepare the display ldi r30,LOW(com_sel_line*2) ldi r29,HIGH(LCD_line_buffer) ldi r28,LOW(LCD_line_buffer) ldi r18,16 com_sel_2: lpm r16,z+ st y+,r16 dec r18 brne com_sel_2 rcall lcd_strout_1 ldi r31,HIGH(com_off_line*2) ldi r30,LOW(com_off_line*2) lds r16,COM_port_flag ; check COM port flag cpi r16,0b0000000 breq com_sel_3 ; if not zero, then COM port is on ldi r31,HIGH(com_on_line*2) ldi r30,LOW(com_on_line*2) com_sel_3: ldi r29,HIGH(LCD_line_buffer) ldi r28,LOW(LCD_line_buffer) ldi r18,16 com_sel_4: lpm r16,z+ st y+,r16 dec r18 brne com_sel_4 rcall lcd_strout_2 ldi r31,HIGH(com_baud_line*2) ; display the baud rate ldi r30,LOW(com_baud_line*2) ldi r29,HIGH(LCD_line_buffer) ldi r28,LOW(LCD_line_buffer) ldi r18,16 com_sel_5: lpm r16,z+ st y+,r16 dec r18 brne com_sel_5 rcall lcd_strout_3 ldi r31,HIGH(com_blank_line*2) ; blank the line ldi r30,LOW(com_blank_line*2) ldi r29,HIGH(LCD_line_buffer) ldi r28,LOW(LCD_line_buffer) ldi r18,16 com_sel_6: lpm r16,z+ st y+,r16 dec r18 brne com_sel_6 rcall lcd_strout_4 com_key_0: lds r16,key_pressed_flag ; check key pressed flag cpi r16,0b00000000 breq com_key_0 ; stay here until a key is pressed ; should add a long time out lds r16,key_value cpi r16,0b00110100 ; check for "up" brne com_key_1 ser r16 sts COM_port_flag,r16 ; set COM port flag, => com port on rjmp com_sel_0 ; and return com_key_1: cpi r16,0b00011100 ; check for key "down" brne com_key_2 clr r16 sts COM_port_flag,r16 ; clear COM port flag, => com port off rjmp com_sel_0 ; and return com_key_2: cpi r16,0b00111000 ; check for key "menu" brne com_key_0 ; catch the rest, an odd key was pressed rjmp sweep_sel_0 ; and go the next menu item ;*************************************************************************** ;**** sweep setting ;*************************************************************************** ; update LCD accordingly ; LCD in this mode : ; ****************** ;.*sweep setting: * ; *single / sweep * ; * * ; * * ; ****************** ; update the LCD on line 2 ; if "up" then sweep_flag = on, ; set mode_flag ; if "down" then sweep_flag = off, ; set mode_flag ; if "enter" then go to "run time routine start" ; if "menu" then if sweep_flag = off then go to "end menu loop" ; else go to "get start frequency" ; else go to "sweep setting" sweep_sel_line: .db $7E," sweep select ",$7F sweep_on_line: .db "On " sweep_off_line: .db "Off " sweep_blank_line: .db " " sweep_sel_0: clr r16 ; clr key pressed flag sts key_pressed_flag,r16 sweep_sel_1: ldi r31,HIGH(sweep_sel_line*2) ; prepare the display ldi r30,LOW(sweep_sel_line*2) ldi r29,HIGH(LCD_line_buffer) ldi r28,LOW(LCD_line_buffer) ldi r18,16 sweep_sel_2: lpm r16,z+ st y+,r16 dec r18 brne sweep_sel_2 rcall lcd_strout_1 ldi r31,HIGH(sweep_off_line*2) ldi r30,LOW(sweep_off_line*2) lds r16,sweep_flag ; check sweep flag cpi r16,0b0000000 breq sweep_sel_3 ; if not zero, then sweep is on ldi r31,HIGH(sweep_on_line*2) ldi r30,LOW(sweep_on_line*2) sweep_sel_3: ldi r29,HIGH(LCD_line_buffer) ldi r28,LOW(LCD_line_buffer) ldi r18,16 sweep_sel_4: lpm r16,z+ st y+,r16 dec r18 brne sweep_sel_4 rcall lcd_strout_2 ldi r31,HIGH(sweep_blank_line*2) ; display a blank line ldi r30,LOW(sweep_blank_line*2) ldi r29,HIGH(LCD_line_buffer) ldi r28,LOW(LCD_line_buffer) ldi r18,16 sweep_sel_5: lpm r16,z+ st y+,r16 dec r18 brne sweep_sel_5 rcall lcd_strout_3 ldi r31,HIGH(com_blank_line*2) ; display a blank line ldi r30,LOW(com_blank_line*2) ldi r29,HIGH(LCD_line_buffer) ldi r28,LOW(LCD_line_buffer) ldi r18,16 sweep_sel_6: lpm r16,z+ st y+,r16 dec r18 brne sweep_sel_6 rcall lcd_strout_4 sweep_key_0: lds r16,key_pressed_flag ; check key pressed flag cpi r16,0b00000000 breq sweep_key_0 ; stay here until a key is pressed ; should add a long time out lds r16,key_value cpi r16,0b00110100 ; check for "up" brne sweep_key_1 ser r16 sts sweep_flag,r16 ; set sweep flag, => sweep is on lds r16,start_frequency ; set to start of sweep sts frequency,r16 ldi r16,start_frequency+1 sts frequency+1,r16 ldi r16,start_frequency+2 sts frequency+2,r16 ldi r16,start_frequency+3 sts frequency+3,r16 rjmp sweep_sel_0 ; and return sweep_key_1: cpi r16,0b00011100 ; check for key "down" brne sweep_key_2 clr r16 sts sweep_flag,r16 ; clear sweep flag, => sweep is off rjmp sweep_sel_0 ; and return sweep_key_2: cpi r16,0b00111000 ; check for key "menu" breq sweep_key_3 rjmp sweep_key_0 ; catch the rest, an odd key was pressed sweep_key_3: clr r16 ; clr key pressed flag sts key_pressed_flag,r16 ser r16 ; set mode change flag sts mode_change_flag,r16 ser r16 sts frequency_change_flag,r16 ; frequency has canged rjmp mode_update ; and go back to the main mode ; **** get start frequency: ; update LCD accordingly ; LCD in this mode : ; ****************** ;.*sweep setting: * ; *fs: 11,234,567Hz* ; *fe: 12,234,567Hz* ; *fi: 10,000hz* ; ****************** ; update the LCD on line 2 ; if "up" then start_freqency = start_frequency + increment ; don't allow start_frequency > 16MHz, ; if "down" then start_freqency = start_frequency - increment ; don't allow frequency < 250Hz, ; if "enter" then select next frequency increment ; 10Hz, 100Hz, 1kHz, 10kHz, 100kHz and 1MHz ; if "menu" then go to "get end frequency" ; else go to "get start frequency" ; **** get end frequency: ; update the LCD on line 3 ; if "up" then end_freqency = end_frequency + increment ; don't allow end_frequency > 16MHz, ; if "down" then end_freqency = end_frequency - increment ; don't allow frequency < 250Hz, ; if "enter" then select next frequency increment ; 10Hz, 100Hz, 1kHz, 10kHz, 100kHz and 1MHz ; if "menu" then go to "get sweep increment" ; else go to "get end frequency" ; **** get sweep increment ; update the LCD on line 4 ; if "up" then sweep_increment = sweep_increment + increment ; don't allow sweep_increment > 10kHz, ; if "down" then sweep_increment = sweep_increment - increment ; don't allow frequency < 10Hz, ; if "enter" then select next increment ; 10Hz, 100Hz, 1kHz, 10kHz ; if "menu" then ; set frequency change flag ; frequency = start_frequeny ; go to "end menu loop" ; else go to "get sweep increment" ;*************************************************************************** ;**** end of menu loop ;*************************************************************************** ; go back to menu routine start ;*************************************************************************** ;**** TD empty interrupt routine ;*************************************************************************** ; clear TX empty interrupt flag ; get TX_pointer ; get next character ; send character through UART ; increment TX_pointer ; if TX_pointer > TX_buffer +22 then ; disable TX_empty interrupt ; set TX_buffer_empty_flag ; else save TX-pointer ; return from interrupt uart_TD_empty: push r27 push r26 push r17 push r16 in r16,sreg push r16 lds r26,TX_pointer ; get the next byte from the lds r27,TX_pointer+1 ; 2nd TX buffer ld r16,x+ out UDR,r16 ; push it to the USART ldi r16,LOW(TX_buffer_send+22) ldi r17,HIGH(TX_buffer_send+22) cp r26,r16 ; check if bufffer empty cpc r27,r17 brne uart_TD_empty_1 ser r16 ; if so set buffer empty flag and sts TX_buffer_empty_flag,r16 cbi UCSRB,5 ; disable further TX empty interrupts uart_TD_empty_1: sts TX_pointer,r26 ; update TX buffer pointer sts TX_pointer+1,r27 pop r16 out sreg,r16 pop r16 pop r17 pop r26 pop r27 reti ;*************************************************************************** ;**** modulation timer interrupt routine ;*************************************************************************** ; clear timer2 interrupt flag ; timer2 is programmed in auto reload mode and times 1/2 a Baudot bit ; re-arm timer2 interrupt ; get FSK_bit_position ; if = 8 then ; get FSK_pointer ; if FSK_pointer = Baudot_message_end then FSK_pointer = Baudot_message ; store FSK_to_send = (FSK_pointer) ; increment FSK_pointer ; store FSK_pointer ; FSK_bit_position = 0 ; increment FSK_bit_position ; store FSK_bit_position ; get FSK_to_send ; right shift through carry ; if carry =0 then FSELECT = 0 else FSELECT = 1 ; store FSK_to_carry ; return from interrupt ; the routine takes about 6µs every 11ms, less than 1% CPU load. timer1_compa: push r16 push r17 push r18 push r19 in r16,sreg push r16 ldi r16,0b00010000 ; clear interupt flag timer 1 out TIFR,r16 lds r17,FSK_next_time+1 lds r16,FSK_next_time lds r19,FSK_bit_time+1 lds r18,FSK_bit_time add r16,r18 ; calculate next thing adc r17,r19 sts FSK_next_time+1,r17 sts FSK_next_time,r16 out OCR1AH,r17 ; preload output compare register out OCR1AL,r16 lds r16,FSK_bit_position ; check if there are still bits to shift cpi r16,0b00000000 brne timer1_compa_2 lds r18,FSK_pointer ; none, then we have to get a fresh byte lds r19,FSK_pointer+1 cpi r18,LOW(Baudot_message_end) ; check if we are running out of message brne timer1_compa_1 cpi r19,HIGH(Baudot_message_end) brne timer1_compa_1 ; if so, we will start from the top ldi r18,LOW(Baudot_message) ldi r19,HIGH(Baudot_message) timer1_compa_1: out EEARH, r19 ; load the eeprom address register out EEARL, r18 sbi EECR,EERE ; Start eeprom read by writing EERE in r17,EEDR ; Read data from data register sts FSK_to_send,r17 ; and store the fresh byte subi r18,LOW(-1) sbci r19,HIGH(-1) sts FSK_pointer,r18 ; increment the pointer for the next time sts FSK_pointer+1,r19 ldi r16,0b10000000 ; start with the msb, this was the first to be generated timer1_compa_2: lds r17,FSK_to_send and r17,r16 breq timer1_compa_3 cbi porta,4 ; idle is high (nominal) frequency in freg0 rjmp timer1_compa_4 timer1_compa_3: sbi porta,4 ; start is low frequency in freg1 timer1_compa_4: lsr r16 ; shift the mask one bit to the right sts FSK_bit_position,r16 pop r16 out sreg,r16 pop r19 ; restore and get back pop r18 pop r17 pop r16 reti ;*************************************************************************** ;**** key debounce timer interrupt ;*************************************************************************** ; produce a key_pressed_flag each time a key is pressed for longer than 3ms ; the main routine will read the key_value and clear the key_pressed_flag ; only the falling edges are acknowledged ; the routine takes about 3µs every 3ms, a bit more than 1% CPU load. ; ; clear timer1 interrupt flag ; timer1 is programmed in to reload at ca. 3ms later ; get new_key ; if key_pressed_flag =! then ; very_old_key = old_key and old_key = new_key ; return from interrupt ; else if new_key =! old_key then ; very_old_key = old_key and old_key = new_key ; return from interrupt ; else if new_key =! very_old_key then ; calculate new key value: NOT(NOT(new_key).AND.(very_old_key)) ; set key_pressed_flag ; very_old_key = old_key and old_key = new_key ; return from interrupt timer2_comp: push r16 push r17 in r16,sreg push r16 ldi r16,0b11000000 ; clear interupt flag timer 1 out TIFR,r16 lds r16,debounce_time subi r16,-debounce_3ms; out OCR2,r16 ; set compare to 10ms further sts debounce_time, r16 in r16,PIND ; get port D andi r16,0b00111100 ; get pins 2,3,4,5 lds r17,key_pressed_flag cpi r17,0b00000000 brne timer2_comp_2 ; previous key was not yet handled lds r17,old_key cp r16,r17 brne timer2_comp_2 ; inputs are not persistent push r16 com r16 lds r17,very_old_key ; little bit of logic to detect falling edges and r16,r17 com r16 andi r16,0b00111100 cpi r16,0b00111100 ; no falling edge => no new entry breq timer2_comp_1 sts key_value,r16 ; set the new key entry ser r16 sts key_pressed_flag,r16 ; set key pressed flag timer2_comp_1: pop r16 timer2_comp_2: lds r17,old_key ; shift new => old => very old sts very_old_key,r17 sts old_key,r16 pop r16 out sreg,r16 pop r17 pop r16 reti ;*************************************************************************** ;**** primitives ;*************************************************************************** disp_freq: ; now go for it, the value to be converted is in r20..r23 push r16 push r17 push r18 ldi r16,$30 ; have an ascii "0" ldi r17,$ff ; set leading zero suppersion flag ldi r18,$20 ; set a blank in temp mov r19,r16 bin2bcd_1: inc r19 subi r20,$80 ; subtract 10 000 000 sbci r21,$96 ; a number of times sbci r22,$98 sbci r23,$00 brpl bin2bcd_1 dec r19 ; too far ? subi r20,$80 ; correct and end sbci r21,$69 sbci r22,$67 sbci r23,$ff cp r19,r16 ; is it zero ? brne bin2bcd_15 mov r19,r18 ; set a blank rjmp bin2bcd_16 ; and keep the leading zero flag bin2bcd_15: clr r17 ; clear leading zero flag bin2bcd_16: sts LCD_line_buffer+4,r19 sts TX_buffer,r19 ; prepare text buffer mov r19,r16 bin2bcd_2: inc r19 subi r20,$40 ; subtract 1 000 000 sbci r21,$42 ; a number of times sbci r22,$0f sbci r23,$00 brpl bin2bcd_2 dec r19 ; too far ? subi r20,$c0 ; correct and end sbci r21,$bd sbci r22,$f0 sbci r23,$ff cp r19,r16 ; is it zero ? brne bin2bcd_25 sbrc r17,7 ; yes but leading zero flag was cleared mov r19,r18 ; set a blank rjmp bin2bcd_26 ; and keep the leading zero flag bin2bcd_25: clr r17 ; clear leading zero flag bin2bcd_26: sts LCD_line_buffer+5,r19 sts TX_buffer+1,r19 ; prepare text buffer mov r19,r16 bin2bcd_3: inc r19 subi r20,$a0 ; subtract 100 000 sbci r21,$86 ; a number of times sbci r22,$01 sbci r23,$00 brpl bin2bcd_3 dec r19 ; too far ? subi r20,$60 ; correct and end sbci r21,$79 sbci r22,$fe sbci r23,$ff cp r19,r16 ; is it zero ? brne bin2bcd_35 sbrc r17,7 ; yes but leading zero flag was cleared mov r19,r18 ; set a blank rjmp bin2bcd_36 ; and keep the leading zero flag bin2bcd_35: clr r17 ; clear leading zero flag bin2bcd_36: sts LCD_line_buffer+7,r19 sts TX_buffer+2,r19 ; prepare text buffer mov r19,r16 bin2bcd_4: inc r19 subi r20,$10 ; subtract 10 000 sbci r21,$27 ; a number of times sbci r22,$00 sbci r23,$00 brpl bin2bcd_4 dec r19 ; too far ? subi r20,$f0 ; correct and end sbci r21,$d8 sbci r22,$ff sbci r23,$ff sts LCD_line_buffer+8,r19 sts TX_buffer+3,r19 ; prepare text buffer mov r19,r16 bin2bcd_5: inc r19 subi r20,$e8 ; subtract 1 000 sbci r21,$03 ; a number of times sbci r22,$00 sbci r23,$00 brpl bin2bcd_5 dec r19 ; too far ? subi r20,$18 ; correct and end sbci r21,$fc sbci r22,$ff sbci r23,$ff sts LCD_line_buffer+9,r19 sts TX_buffer+4,r19 ; prepare text buffer mov r19,r16 bin2bcd_6: inc r19 subi r20,$64 ; subtract 100 sbci r21,$00 ; a number of times sbci r22,$00 sbci r23,$00 brpl bin2bcd_6 dec r19 ; too far ? subi r20,$9c ; correct and end sbci r21,$ff sbci r22,$ff sbci r23,$ff sts LCD_line_buffer+11,r19 sts TX_buffer+5,r19 ; prepare text buffer mov r19,r16 bin2bcd_7: inc r19 subi r20,$0a ; subtract 10 sbci r21,$00 ; a number of times sbci r22,$00 sbci r23,$00 brpl bin2bcd_7 dec r19 ; too far ? subi r20,$f6 ; correct and end sbci r21,$ff sbci r22,$ff sbci r23,$ff sts LCD_line_buffer+12,r19 sts TX_buffer+6,r19 ; prepare text buffer subi r20,$d0 ; remaining units sts LCD_line_buffer+13,r20 sts TX_buffer+7,r20 ; prepare text buffer rcall lcd_strout_2 ; display result on line 2 pop r18 pop r17 pop r16 ret ; ;**** write a string of 16 characters to line 1 lcd_strout_1: cbi portd,6 ; set the register select low: instruction ldi r16,0b10000000 ; cursor home on the 1st line lcd_strout_0: rcall lcdout sbi portd,6 ; set the register select high: data ldi r31,HIGH(LCD_line_buffer) ldi r30,LOW(LCD_line_buffer) lcd_str_1: ld r16,z+ ; send 16 characters rcall lcdout cpi r30,LOW(LCD_line_buffer)+16 brne lcd_str_1 ret ;**** write a string of 16 characters to line 2 lcd_strout_2: cbi portd,6 ; set the register select low: instruction ldi r16,0b11000000 ; cursor home on the 2nd line rjmp lcd_strout_0 ;**** write a string of 16 characters to line 3 lcd_strout_3: cbi portd,6 ; set the register select low: instruction ldi r16,0b10010000 ; cursor home on the 3rd line rjmp lcd_strout_0 ;**** write a string of 16 characters to line 4 lcd_strout_4: cbi portd,6 ; set the register select low: instruction ldi r16,0b11010000 ; cursor home on the 4th line rjmp lcd_strout_0 ;**** write to LCD lcdout: mov r17,r16 ; prepare the byte to be send swap r17 ; take most significant nible andi r17,0b00001111 in r18,porta ; get current bit settings andi r18,0b11110000 or r18,r17 ; concatenate out porta,r18 ; get most significant nible out mov r17,r16 ; prepare the byte to be send andi r17,0b00001111 ; take least significant nible in r18,portb ; get current bit settings andi r18,0b11110000 or r18,r17 ; concatenate out portb,r18 ; get least significant nible out rcall wait2u sbi portd,7 ; set the LCD-CS high rcall wait2u cbi portd,7 ; set the LCD-CS low rcall wait2u rcall wait40u ; wait a long while rcall wait40u ret ; indicate the increment by having the cursor under the digit digit_position: ldi r30,LOW(digit_position_0*2) ldi r31,HIGH(digit_position_0*2) lds r16,increment_pointer clr r17 add r30,r16 adc r31,r17 lpm r16,z andi r16,0b00001111 ori r16,0b11000000 ; cursor home on the increment digit on the 2nd line cbi portd,6 ; set the register select low: instruction rcall lcdout sbi portd,6 ; select the DDRAM rcall wait40u rcall wait40u ret digit_position_0: .db 5,7,8,9,11,12,13,0 ; digit positions ;**** wait routines wait2u: push r19 ldi r19,10 wait2u_1: ; wait for 2us, 24 cycles dec r19 brne wait2u_1 pop r19 ret wait40u: push r19 ldi r19,238 ; wait for 40us, 480 cycles rjmp wait2u_1 wait5m: push r18 ldi r18,125 ; wait fot 5ms wait5m_1: rcall wait40u dec r18 brne wait5m_1 pop r18 ret ;****************************************************************************** ; SER output for AD9835 ;****************************************************************************** ; content of r16,r17 is shifted to the DSS chip serout: ldi r18,16 ; do this for 16 bits sbi portb,1 ; set clock to high rcall wait2u cbi portb,4 ; set fsync to low rcall wait2u serout_1: rol r16 rol r17 ; word to be shifted in accb & acca brcc serout_2 ; most significant bit first sbi portb,0 ; set the serial data high rjmp serout_3 ; speed is ca. 25kbit/s serout_2: cbi portb,0 ; set the serial data low serout_3: rcall wait2u rcall wait2u cbi portb,1 ; set the serial clock low rcall wait2u sbi portb,1 ; set the serial clock high rcall wait2u dec r18 ; repeat this for all bits brne serout_1 sbi portb,4 ; set fsync to high rcall wait2u ret ;****************************************************************************** ; ADC input MAX1286 ;****************************************************************************** adcin: sbi portc,0 ; assert CVST (active high) rcall wait2u ; for 2µs which must be OK to acquire the output of the log amp cbi portc,0 ; then start the conversion rcall wait2u rcall wait2u ; wait for 4µ ldi r18,12 ; do this for each bit adcin_1: sec sbis pinc,1 ; test data bit clc ; if set, rol a "1" rol r16 ; else, rol a "0" rol r17 ; most significant bit first sbi portc,2 ; set the serial clock high nop nop nop nop nop cbi portc,2 ; set the serial clock low dec r18 ; repeat this for all bits, brne adcin_1 andi r17,0b00001111 ret ;***************************************************************************** ; 64bit/64bit Unsigned Division ;***************************************************************************** ; Register Variables ; Call: var1[7:0] = dividend ; var2[7:0] = divisor ; mod[7:0] = ; lc = (high register must be allocated) ; ; Result:var1[7:0] = var1[7:0] / var2[7:0] ; var2[7:0] = ; mod[7:0] = var1[7:0] % var2[7:0] ; lc = 0 ; ; Takes about 2000 cycles div64u: clr mod0 ;initialize variables clr mod1 ; mod = 0; clr mod2 ; lc = 32; clr mod3 clr mod4 clr mod5 clr mod6 clr mod7 ldi lc,64 ;/ div64u_1: ;---- calculating loop lsl var10 ;var1 = var1 << 1; rol var11 rol var12 rol var13 rol var14 rol var15 rol var16 rol var17 ;/ rol mod0 ;mod = mod << 1 + carry; rol mod1 rol mod2 ; rol mod3 rol mod4 rol mod5 rol mod6 rol mod7 ;/ cp mod0,var20 ;if (mod => var2) { cpc mod1,var21 ; mod -= var2; var1++; cpc mod2,var22 ; } cpc mod3,var23 cpc mod4,var24 cpc mod5,var25 cpc mod6,var26 cpc mod7,var27 brcs div64u_2 inc var10 sub mod0,var20 sbc mod1,var21 sbc mod2,var22 sbc mod3,var23 sbc mod4,var24 sbc mod5,var25 sbc mod6,var26 sbc mod7,var27 ;/ div64u_2: dec lc ;if (--lc > 0) brne div64u_1 ; continue loop; ret ;***************************************************************************** ; copy right string ;***************************************************************************** .db "all rights reserved " .db "jean.taeymans@telenet.be" .db "June 2007 " ;***************************************************************************** ;*************************************************************************** ;**** end of code ;*************************************************************************** ;*************************************************************************** ;**** routine to create a Baudot message ;*************************************************************************** ; this part is run once, to prepare the Baudot message in eeprom ; ; FSK_bit_position = 0 ; message_bit_position = 15 ; FSK_pointer = Baudot_message ; input_message_pointer = input_message ; ; encode loop ; get FSK_bit_position ; if FSK_bit_position = 8 then ; store FSK_to_send in (FSK_pointer) ; increment FSK_pointer ; FSK_bit_position = 0 ; increment FSK_bit_position ; store FSK_bit_position ; get message_bit_position ; if message_bit_position = 15 then ; get input_message_pointer ; if input_message_pointer = input_message_end then quit ; increment input_message_pointer ; get character (input_message_pointer) ; translate character: ascii to Baudot 2 bits / bit arragement ; Baudot_to_send = high byte of translated character ; Baudot_to_send+1 = low byte of translated character ; message_bit_position =0 ; increment message_bit_position ; store message_bit_position ; shift right through carry Baudot_to_send ; shift right through carry Baudot_to_send+1 ; shift right through carry FSK_to_send ; back to encode loop start burn_message: sbic EECR,EEWE ; wait for previous eeprom write to complete rjmp burn_message ; takes about 8ms/byte ldi r17,HIGH(signature) out EEARH,r17 ldi r16,LOW(signature) ; Set up address in eeprom address register out EEARL,r16 ldi r16,$55 ; write signature out EEDR,r16 ; write data to data register sbi EECR,EEMWE ; write logical one to EEMWE sbi EECR,EEWE ; start eeprom write by setting EEWE burn_message_0: sbic EECR,EEWE ; wait for previous eeprom write to complete rjmp burn_message_0 ; takes about 8ms/byte ldi r17,HIGH(signature+1) out EEARH,r17 ldi r16,LOW(signature+1) ; Set up address in eeprom address register out EEARL,r16 ldi r16,$AA ; write signature out EEDR,r16 ; write data to data register sbi EECR,EEMWE ; write logical one to EEMWE sbi EECR,EEWE ; start eeprom write by setting EEWE ldi r28,low(Baudot_message) ldi r29,high(Baudot_message) ; setup Y pointer to start of Baudot message ldi r17,120 ; a bit more than 1s of idle to start burn_message_1: clz rcall new_byte dec r17 brne burn_message_1 clr r19 ; clear output bit position clr r20 ; clear the output byte ldi r30,low(message*2) ; then a bit more than 26s of characters ldi r31,high(message*2) ; setup Z pointer to start of message in FROM ldi r22,160 ; total number of characters in message burn_message_2: lpm r16,Z+ ; get the first character mov r24,r30 mov r25,r31 ; store the message pointer for later ldi r30,low(translation_table*2) ldi r31,high(translation_table*2); setup Z pointer to start of translation table andi r16,0b00011111 ; take the 5 lower bits only clr r17 add r30,r16 adc r31,r17 lpm r16,Z ; get the translation now andi r16,0b00011111 ; take the 5 lower bits only sez ; 1/2 start bit rcall new_byte sez ; 1/2 start bit rcall new_byte mov r21,r16 andi r21,0b00000001 ; 1/2 1st bit rcall new_byte mov r21,r16 andi r21,0b00000001 ; 1/2 1st bit rcall new_byte mov r21,r16 andi r21,0b00000010 ; 1/2 2nd bit rcall new_byte mov r21,r16 andi r21,0b00000010 ; 1/2 2nd bit rcall new_byte mov r21,r16 andi r21,0b00000100 ; 1/2 3rd bit rcall new_byte mov r21,r16 andi r21,0b00000100 ; 1/2 3rd bit rcall new_byte mov r21,r16 andi r21,0b00001000 ; 1/2 4th bit rcall new_byte mov r21,r16 andi r21,0b00001000 ; 1/2 4th bit rcall new_byte mov r21,r16 andi r21,0b00010000 ; 1/2 5th bit rcall new_byte mov r21,r16 andi r21,0b00010000 ; 1/2 5tht bit rcall new_byte clz ; 1/3 stop bit rcall new_byte clz ; 1/3 stop bit rcall new_byte clz ; 1/3 stop bit rcall new_byte mov r30,r24 mov r31,r25 ; restore the message pointer dec r22 ; and loop until no more characters brne burn_message_2 ldi r17,120 ; a bit more than 1s of idle to start burn_message_3: clz rcall new_byte dec r17 brne burn_message_3 jmp reset_1 new_byte_1: clc rjmp new_byte_2 new_byte: ; small sub routine breq new_byte_1 sec ; to get the bits new_byte_2: ; and to store them away every rol r20 ; once a byte is filled inc r19 cpi r19,8 brne new_byte_4 new_byte_3: sbic EECR,EEWE ; wait for previous eeprom write to complete rjmp new_byte_3 ; takes about 8ms/byte out EEARH,r29 ; Set up address (r29:r28) in address register out EEARL,r28 out EEDR,r20 ; write data to data register sbi EECR,EEMWE ; write logical one to EEMWE sbi EECR,EEWE ; start eeprom write by setting EEWE ld r20,y+ ; dummy read to increment output pointer clr r20 clr r19 new_byte_4: ret ;***************************************************************************** ; constants etc ;***************************************************************************** translation_table: .db $04, $03 ; space, A .db $19, $0E ; B, C .db $09, $01 ; D, E .db $0D, $1A ; F, G .db $14, $06 ; H, I .db $0B, $0F ; J, K .db $12, $1C ; L, M .db $0C, $18 ; N, O .db $16, $17 ; P, Q .db $0A, $05 ; R, S .db $10, $07 ; T, U .db $1E, $13 ; V, W .db $1D, $15 ; X, Y .db $11, $08 ; Z, carriage return .db $02, $1B ; line feed, figures .db $1F, $00 ; letters, n.a. message: .db $1E, $1E, $1E, $1E, $1B, $1C .db "RYRYRYRYRYRYRYRYRYRYRYRYRYRYRYRYRYRYRYRYRYRY" .dB $1E, $1E, $1B, $1C .db "THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG " .dB $1D, $1D, $1B, $1C .db "QWERTYUIOP ABCDEFGJKLMNVXZ" .db " " .db $1E,$1E