| CODE |
;*************************************************************************** ;* Title: AVR_UI_TNC PRELIMINARY CODE 1.0 ;* Made available for educational, and non-commercial use. ;* Commercial use forbidden without license from author. ;* Code may be modified as long as it is released publicly and credit ;* is given to the original author(s). ;* (c) copyright 2000 Henry Carl Ott N2RVQ, all Rights reserved. ;* Questions, comments or offers of cash? carlott@interport.net www.interport.net/~carlott/ ;*************************************************************************** ;* Description: Example code to test basic UI packet functions on an AT90s2313 / MXCOMM MX-614 combo. ;* Please reference separate documents for schematics / hardware description. ;* Provides minimal tnc functionality for ui-frames. ;* Outputs received ax25 packets to console (total packet length 224 chars including path) ;* Transmits strings from console, max length, 96 chars not including path ;* Beacons at user determined intervals, beacon text up to 64 chars of eeprom stored message ;* Console configuration of mycall, ui destination and digi path, ;* beacon text (max 64 chars),beacon time, and console echo ;************************************************************************** ;* Credits: The main inspiration for the design was the TAPR PIC-E project. ;* I had used Microchip MCUs in a number of other projects, but I was currently ;* experimenting with Atmel AVR MCUs and found that the additional RAM/EPROM and ;* additional indexing modes made code development easier (at least for me). Besides I ;* was getting tired of all the bank switching on the PICs :). ;* For the software I looked very closely at the PIC-E code posted by ;* Byon Garrabrant, John Hansen, Mike Berg and others (sorry if I missed anyone). ;* The jump table decoding was inspired by Byons code, and the CRC calculation ;* is an almost direct port of his crc routine. ;*************************************************************************** ;* Disclaimer: I'm a lousy programmer. Please don't use this code/design for any ;* application where human life is at risk or property damage may occur. ;*************************************************************************** ; Console command descriptions: ; Commands are case insensitive. ; In most cases only the first letter of command is significant ; ; Mycall examples 'Mycall NOCALL-1' or 'M NOCALL' ; Unproto examples 'UNPROTO APRS VIA RELAY,WIDE5-5' or 'U APRS V WIDE' or 'u apz001' ; Echo examples 'ECHO OFF' or 'ECHO ON' or 'e off' ; Beacon in minutes, examples 'BEACON 10' or 'B 10' or 'B E 10' or 'B 00' ('B 00' turns off beaconing) ; BText examples 'BTEXT Test Message' or 'BT THIS IS A TEST,' ; Converse examples 'C' or 'converse' or 'conv' Enters converse mode ; Id examples 'ID' or 'I' Sends a sample beacon (will key radio!) ;--- start of the actual code --- .nolist .include "2313def.inc" .list .listmac ;*************************************************************************** ;* ;* MACROS ;* ;*************************************************************************** ;--- simple led macros .macro led_1_on sbi PORTD,led_1 .endm .macro led_1_off cbi PORTD,led_1 .endm .macro led_2_on sbi PORTD,led_2 .endm .macro led_2_off cbi PORTD,led_2 .endm ;--- basic i2c macros .macro _scl_low sbi DDRB,SCL .endm .macro _scl_hi cbi DDRB,SCL .endm .macro _sda_low sbi DDRD,SDA .endm .macro _sda_hi cbi DDRD,SDA .endm ;--- mx-614 specific macros .macro tx_mx614 sbi PORTB,mode0 cbi PORTB,mode1 .endm .macro rx_mx614 cbi PORTB,mode0 sbi PORTB,mode1 .endm .macro off_mx614 sbi PORTB,mode0; power down mode sbi PORTB,mode1 .endm ;--- radio control .macro ptt_down sbi PORTB,rad_ptt .endm .macro ptt_up cbi PORTB,rad_ptt .endm ;--- send single char to console .macro _send ldi temp,@0 rcall putc .endm ;--- send string to console .macro _send_232_str ldi zh,high(@0 * 2) ldi zl,low(@0 * 2) ;Init Z-pointer rcall ser_ram_str .endm ;--- skip next instruction .macro skip rjmp (pc + 2); skip, next instruction .endm ;--- copy zero terminated eeprom string into i2c ram (left shift data first) .macro _copy_ax_string_shift ldi temp,@0 rcall ax25_s_string .endm ;--- copy zero terminated eeprom string into i2c ram .macro _copy_ax_string ldi temp,@0 rcall ax25_string .endm ;--- send zero terminated eeprom string to console .macro _232_str ldi temp,@0 rcall serial_string .endm ;------delay two cycles in one instruction .macro _nop_2 rjmp (pc + 1) .endm ;----------------------------------------------------------------- ;--- port and pin deinations ;--- portb .equ scl = 0; 0 i2c clock .equ sw_inp = 1; I sw_inp .equ txd_614 = 2; 614_txd .equ det_614 = 3; O I 614_det .equ mic_ptt = 4; I mic ptt input .equ rad_ptt = 5; 0 radio ptt output .equ mode1 = 6; 0 mx614 mode 1 select .equ mode0 = 7; 0 mx614 mode 0 select ;--- portd .equ sda = 6; I/0 i2c data .equ led_1 = 5; O led_1 .equ led_2 = 4; 0 led_2 .equ rxd_614 = 3; I 614_rxd /int0 .equ spare_232 = 2; I external rs232 input .equ txd_232 = 1; O 232_txd .equ rxd_232 = 0; I 232_rxd ;---- some program equates and constants .equ ram_begin = 0x60; start of ram on 2313 .equ i2c_ram_begin = 0x10; beginning of free ram on i2c rtc .equ ctrl_c = 0x03 .equ bs = 0x08 ; back space .equ bel = 0x07 ; bell .equ cr = 0x0d ; carriage return .equ lf = 0x0a ; line feed .equ clock = 8000000 ; cpu clock .equ console_baud = 9600 .equ isr_reload_val= (0xffff - (clock/9600)) + 19 .equ tx_delay = 40; number of flag bytes before data .equ tail_flags = 2; number of flags to send after data .equ tx_hang = 1; delay (.001) after last flag before un-keying .equ min_packet_len = 18; minimum length, + con+pid and crc of rx packet .equ max_packet_len = 224; maximim length, determined by available ram, isr discards extra chars .equ max_str_len = 96; how much of avr ram to use for string storage .equ reload_count = 59; ax25 rxd transition time interval counter reload value, used by rx isr .equ control_byte = 0x03; control and pid for ax25 ui packets .equ pid_byte = 0xf0; ;***************************** ;* Global Register Variables * ;***************************** .def r0 = r00; used with lpm instruction .def tic = r01; incs @ 9600 hz resets at 96 0-95 .def sec_001 = r02; incs @ 100 hz .def random = r03; used for pseudorandom number generation .def u_data = r04; rs-232 data (rx) .def ax25_txbuff = r05; ax25 data to send .def ax25_ones = r06; no of continous ones sent, used for bit stuffing .def ax25_crc_lo = r07; crc calculation low order byte rx/tx .def ax25_crc_hi = r08 ; crc calculation hi order byte rx/tx .def packet_len = r09; length of received packet not including crc .def ax25_rxbuff = r10; used to assemble rx bytes .def ax25_rx_count = r11; time interval between transitions .def ax25_tx_bits = r12; number of bits to send .def isr_save = r15; saves mcu status reg during isr .def temp = r16; gp working reg .def temp2 = r17 ; yet another working register .def ii = r18; gp loop counter .def flags = r19; various bit flags and ax25_tx flags .def rx_flags = r20; bit flags used to communicate with isr .def ax25_rx_chars = r21; received chars ptr/counter (used only within isr) .def ax25_rx_bitcnt = r22; used to assemble individual bits into bytes .def EEaddr = r23; eeprom address .def EEdata = r24; eeprom data for rd/wr .def byte_cnt = r25; general purpose byte counter .def xl = r26 .def xh = r27 .def yl = r28 .def yh = r29 .def zl = r30; used for indirect indexing and a gp register otherwise .def zh = r31; used for indirect indexing and a gp register otherwise .def bitcnt = r31; gp bit counter ;************************** ;- bit flags defined ;************************** .equ uart_rx = 0x00 ; has a char been received via rs232? .equ ax25_flag = 0x01 ; sending flags? (no bit stuffing or crc) .equ ax25_shift = 0x02 ; do we shift the data before sending? (address) .equ echo = 0x03 ; if set echo console chars .equ i2c_busy = 0x06 ; i2c is currently trying to rd/wr ;------------------------- ;************************** ;- ax25 recieve bits defined ;************************** .equ old_bit = 0x00 ; previous bit sample .equ rx_enable = 0x01 ; enable reception of packet data .equ in_packet = 0x02 ; currently receiving packet data .equ good_packet = 0x03 ; a valid packet was received .equ new_bit = 0x04 ; new bit sample, new and old bit must be in these positions .equ flag_det = 0x05 ; last byte was a valid flag .equ half_flag = 0x06 ; set if last bit received was a zero (more of an 1/8 flag) ;------------------------- ;********************* ;* Interrupt Vectors * ;********************* .CSEG .org 0x00 rjmp reset ; Reset Handle .org INT0addr ; external int 0; bit bang secondary serial port reti .org INT1addr ; external int 1 reti .org OVF1addr ; overflow of 16 bit timer 1 basic 9600hz in rjmp t1int .org OVF0addr ; overflow of 8 bit timer 0 usedx for secondary 4800 baud serial port reti .org URXCaddr ; reception of data by uart rjmp uart_rx_isr ;--- timer 1 overflow interupt ;--- we should hit the isr @ 9600 hz ;--- the isr handles the low level transmission of ax25 byte and assembles received packets t1int: in isr_save,SREG ; save SREG push temp push zl ; we have to use zl and zh for thejump table anyway push zh ; so we might as well use the for gp ldi zh,high(isr_reload_val); reload timer1 ldi zl,low(isr_reload_val) out tcnt1h,zh out tcnt1l,zl inc tic ; bump counter everytime through ;--- ax25 transmit portion of isr ax25_tx: tst ax25_tx_bits ; any bits to send? breq ax25_tx_end ; no, branch to end mov temp,tic ; 9600 divided by 8, see if we are in a tx bit period (1200 baud) andi temp,0x07 ; mask off hi bits brne ax25_tx_end ; nope, fall through sbrc flags,ax25_flag ; clr ax25_ones ; if a flag byte, no bit stuffing mov temp,ax25_ones cpi temp,5 ; check for bit stuffing brne send_bit ; no match keep going rcall ax25_tx_0 ; stuff a zero and exit rjmp ax25_tx_end ; send_bit: sbrc ax25_txbuff,0 ; test lsb inc ax25_ones ; if a one, don't change output, just bump the ones counter sbrs ax25_txbuff,0 ; test lsb, data is sent lsb first rcall ax25_tx_0 dec ax25_tx_bits ; number of bits left to send lsr ax25_txbuff ; shift next bit into tx position, bit just sent goes into carry sbrs flags,ax25_flag ; if a flag byte, don't calculate crc rcall ax25_calc_crc ; generate crc on carry bit ax25_tx_end: ;--- ax25 rx portion of isr ax25_rx: sbrs rx_flags,rx_enable; is reception enabled? rjmp rx_reset_cnt ; no, jump to end sbis PINB,det_614 ; any dcd? rjmp rx_flush ; nope, reset registers sbrc rx_flags,good_packet; if last packet has not been processed rjmp rx_flush ; tst ax25_rx_count ; test transition counter breq pc + 2 ; if @ zero, stay at zero dec ax25_rx_count cbr rx_flags,(1 << new_bit) sbic PIND,rxd_614 ; copy pin status sbr rx_flags,(1 << new_bit) mov temp,rx_flags swap temp ; moves old_bit and new_bit into same bit position eor temp,rx_flags sbrs temp,0 ; check for change rjmp ax25_rx_end ; nope rx_change: cbr rx_flags,(1 << old_bit); copy new_bit to old sbrc rx_flags,new_bit sbr rx_flags,(1 << old_bit) tst ax25_rx_count ; test for timout brne pc + 2 ; > 0 ? rjmp rx_flush ; timeout lsr ax25_rx_count lsr ax25_rx_count lsr ax25_rx_count ; divide count by 8 ldi zh,high(rx_jmp) ; ldi zl,low(rx_jmp) ; Init Z-pointer add zl,ax25_rx_count brcc pc +2 ; check for carry, if we are not near a page boundary, we could probaly ignore inc zh ; if a carry from low order add it in ijmp ;--- jump table rx_jmp: rjmp rx_flag ;0 flag rjmp rx_add_5 ;1 zero stuff, add five ones toss zero rcall rx_add_1 ;2 11110 rcall rx_add_1 ;3 1110 rcall rx_add_1 ;4 110 rcall rx_add_1 ;5 10 rjmp rx_add_0 ;6 0 rjmp rx_flush ;7 error, too soon ;--- check that at least one bit has been received and that it was a zero ;--- if so a flag has been received, otherwise error rx_flag: sbrs rx_flags,half_flag; check for first zero of flag rjmp rx_flush sbr rx_flags,(1 << flag_det); set flag detected flag ldi ax25_rx_bitcnt,8; reload bit counter for next byte sbrc rx_flags,in_packet; are we currently within a packet? rjmp ax25_rx_close ; yes, try to close rjmp rx_reset_cnt ; just keep going ;--- add 5 ones to buffer, and discard extra stuffed zero rx_add_5: cbr rx_flags,(1<<half_flag); clear half flag bit because last bit written is a one rcall rx_add_1 ; only one more instruction then a loop rcall rx_add_1 ; is faster, and we needed the register elsewhere rcall rx_add_1 rcall rx_add_1 rcall rx_add_1 rjmp rx_reset_cnt ; done ;--- add a single one to buffer, carry contains bit rx_add_1: sec ; set carry rcall rx_in_bit ret ;--- add a zero to received byte rx_add_0: sbr rx_flags,(1<<half_flag) ; set half flag bit because we are writing a zero clc ; clear carry rcall rx_in_bit rjmp rx_reset_cnt ; done ax25_rx_close: cpi ax25_rx_chars,min_packet_len; minimum length packet brlo rx_flush ldi temp,0xF0 ; compare with calculated crc eor temp,ax25_crc_hi brne rx_flush ; bad crc ldi temp,0xB8 eor temp,ax25_crc_lo brne rx_flush ; bad crc subi ax25_rx_chars,2 ; reduce to discard crc mov packet_len,ax25_rx_chars; and save length led_1_on ; indicator sbr rx_flags,(1 << good_packet); set flag to indicate all done rx_flush: rcall ax25_rx_init rx_reset_cnt: ldi temp,reload_count ; get ready for next bit mov ax25_rx_count,temp ; reload counter ax25_rx_end: ldi temp,96 ; divide by 96 to get our 10ms tic eor temp,tic ; brne no_rollover ; no rollover clr tic inc sec_001 ; bump hunds of seconds (10ms) no_rollover: pop zh ; restore pop zl ; pop temp ; out SREG,isr_save ; restore reti ;--- flip mx614 txd line line, zero the ones counter ;--- trashes temp and zl ax25_tx_0: in temp,PORTB ; get current pin status ldi zl,(1 << txd_614); can't xor directly to io port eor temp,zl ; invert bit out PORTB,temp ; and back out to pin clr ax25_ones ; clear ones counter ret ;--- rotates carry into rx buffer, determines status, calcs crc on full bytes only ;--- store the byte if in packet rx_in_bit: ror ax25_rxbuff ; save bit rotate carry into position (lsb first) dec ax25_rx_bitcnt brne store_ax_end ; not zero, still within byte, just exit ldi ax25_rx_bitcnt,8 ; reset bit counter counter sbrc rx_flags,in_packet rjmp store_ax_byte ; within a packet, handle byte sbrs rx_flags,flag_det ; not in packet, was the last byte a flag? ret ; nope, just discard store_ax_byte: cbr rx_flags,(1<<flag_det)|(1<<half_flag); out of flags and into data sbr rx_flags,(1<<in_packet) ; within packet data (in theory) rx_crc_byte: mov zl,ax25_rxbuff ; grab a copy of the data rx_crc_loop: lsr zl ; rotate lsb into carry rcall ax25_calc_crc dec ax25_rx_bitcnt ; bitcnt was set to 8 above brne rx_crc_loop ldi ax25_rx_bitcnt,8 ; reset counter for next byte inc ax25_rx_chars ; cpi ax25_rx_chars,max_packet_len; brsh store_ax_end ; too many chars, discard the byte sbrs ax25_rxbuff,0 ; check for end of path rjmp rx_save_it cpi ax25_rx_chars,(min_packet_len - 4); length - control,pid, and crc brsh rx_save_it ; long enough rjmp ax25_rx_init ; too short, note jmp rx_save_it: ldi zl,(i2c_ram_begin - 1) add zl,ax25_rx_chars mov temp,ax25_rxbuff rcall i2c_wr store_ax_end: ret ;--- init registers and flags for next bit ax25_rx_init: cbr rx_flags,(1 << half_flag)|(1 << flag_det)|(1 << in_packet) ldi ax25_rx_bitcnt,8; reset bit counter clr ax25_rxbuff clr ax25_rx_chars ; start over ldi temp,0xff ; reset crc mov ax25_crc_lo,temp mov ax25_crc_hi,temp ret ;------------------------------------------------------------ ; ported from Byon Garrabrant's pic based code ; assume carry contains data bit, init crc to 0xffff before starting ax25_calc_crc: clr temp ; rotate carry into lsb rol temp eor ax25_crc_lo,temp ; xor lsb lsr ax25_crc_hi ; zero hi bit, rotate ror ax25_crc_lo ; rotate all, lsb goes into carry brcs pc+2 ; skip on carry set ret ldi temp,0x08 ; if carry is one, xor in poly eor ax25_crc_lo,temp ldi temp,0x84 eor ax25_crc_hi,temp ret ;--- rs-232 char reception isr, checks for errors and discards bad chars uart_rx_isr: in isr_save,SREG ; store SREG push temp sbic USR, FE ; skip if framing error rjmp uart_err ; in temp, UDR ; get rx byte sbic USR, OR ; skip if overrun error rjmp uart_err ; mov u_data,temp ; save actual data sbr flags,(1<<uart_rx); set received data flag rjmp uart_isr_end uart_err: in temp, UDR ; read and discard byte to clear UDR cbr flags,(1<<uart_rx); clear flag return uart_isr_end: pop temp ; restore out SREG,isr_save ; restore reti ;------------------------------------------------------------------------ ;--- start here on power up or external reset ;--- init ports, ints, and zero all registers reset: ldi r16,0 ldi zl,0 ; beginning of registers clr_reg: st z+,r16 cpi zl,0x1e ; don't zero z reg brne clr_reg ;--- init stack pointer (somewhat important) ldi temp,low(RAMEND) ; init stack out spl,temp ;--- portb setup ldi temp,(1<<rad_ptt)|(1<<mode1)|(1<<mode0)|(1<<txd_614)|(1 << sw_inp) out DDRB,temp sbi PORTB,sw_inp ; charge cap on switch input pin ;--- portd setup ldi temp,(1<<led_1)|(1<<led_2)|(1<<txd_232) out DDRD,temp ;--- setup uart, 9600 baud, int on rx, no tx ints ldi temp,((clock / console_baud) / 16) -1 out UBRR,temp ; Set baud rate generator ldi temp, (1<<RXCIE)|(1<<RXEN)|(1<<TXEN); enable rxc interrupts out UCR,temp ; enable UART tx & rx w/o interrupts ;--- set up timer 1 ldi temp,(1 << CS10) ; timer1 no prescale out TCCR1B,temp ldi temp,(1<<TOIE1) ; enable timer 1 overflow int out TIMSK,temp ;--- global enable ints sei ; enable global ints ldi EEaddr,_EE_flags rcall EEread mov flags,EEdata rcall rtc_init ; init the real time clock off_mx614 ; init mx614 _send_232_str _hello ; display sign on message rcall led_flash ; blink rx_mx614 ; put into rx mode to start rcall get_switch ; get switch status tst temp breq converse_mode ; if switch is up go directly to conv mode ;---------------------------------------------- cmd_top: cbr rx_flags,(1 << rx_enable)|(1 << good_packet); disable recieve rcall crlf ; prompt _send '.' rcall get_string ldi zl,ram_begin ; convert first char to upper case ld temp,z rcall to_upper do_cmd: cpi temp,'B' brne pc+2 rjmp b_commands cpi temp,'C' ; enter converse mode brne pc+2 rjmp converse_mode cpi temp,'M' ; get my callsign brne pc+2 rjmp save_mycall cpi temp,'U' ; ui station and path brne pc+2 rjmp save_ui cpi temp,'E' ; get echo mode brne pc+2 rjmp echo_mode cpi temp,'I' ; send beacon (id) brne cmd_top rjmp test_beacon ; enter converse mode ; display all received packets ; send all strings ; send beacon text every beacon time out ; ctrl-c gets back to command mode converse_mode: rcall crlf converse_loop: sbr rx_flags,(1 << rx_enable); enable/re-enable reception sbrs flags,uart_rx ; check for console data rjmp converse_1 rcall get_string brcc pc + 2 ; check for ctrl-c rjmp cmd_top rcall send_ram_string ; send the string rcall crlf ; make pretty rcall display_packet ; local echo of transmitted string converse_1: sbrc rx_flags,good_packet ; check for received packet rcall display_packet rcall check_beacon ; back to top rjmp converse_loop ; keep looping ;--- send beacon text, then back to top test_beacon: rcall send_beacon rjmp cmd_top ;-- check to see if this is a BText or BEacon command b_commands: lds temp,(ram_begin + 1) rcall to_upper cpi temp,'T' breq get_b_text cpi temp,' ' breq get_beacon_time cpi temp,'E' breq get_beacon_time rjmp cmd_top ;--- enable or disable local char echo echo_mode: rcall count_param cpi byte_cnt,0x02 brne echo_mode_end ldi ii,1 ; point to first char of arg rcall pos_zl ld temp,z+ rcall to_upper cpi temp,'O' brne echo_mode_end ; err ld temp,z rcall to_upper cpi temp,'F' breq echo_off cpi temp,'N' brne echo_mode_end echo_on: sbr flags,(1<<echo) rjmp pc + 2 echo_off: cbr flags,(1<<echo) mov EEdata,flags andi EEdata,(1 << echo) ldi EEaddr,_EE_flags rcall EEwrite echo_mode_end: rjmp cmd_top ;--- copy beacon text to eeprom ----------------- get_b_text: rcall count_param cpi byte_cnt,0x02 brne get_btxt_end ; check for correct number of arguments ldi EEaddr,_b_text ; point to b_text storage ldi ii,1 ; point to beginning of string rcall pos_zl get_b_loop: ldi EEdata,0 cpi EEaddr,0x7f ; test for end of storage breq pc + 2 ld EEdata,z+ ; get char rcall EEwrite tst EEdata ; tst for zero termination brne get_b_loop ; get next char get_btxt_end: rjmp cmd_top ;--- parse beacon timeout rate from console string get_beacon_time: rcall count_param ldi ii,01 ; point to second arg cpi byte_cnt,0x02 breq get_btime_b ; second arg is time out val cpi byte_cnt,0x03 brne get_btime_end ; error rcall pos_zl ; point to second arg ldi ii,2 ; point to third arg ld temp,z rcall to_upper cpi temp,'E' breq get_btime_b cpi temp,'A' brne get_btime_end get_btime_b: rcall pos_zl ; position zl at beginning of second arg ld temp,z+ rcall asc_hex brcc pc + 2 ; test for bad char rjmp get_btime_end push temp ld temp,z rcall asc_hex brcs btime_0_9 rcall asc_hex pop temp2 swap temp2 or temp,temp2 push temp btime_0_9: pop EEdata ldi EEaddr,_b_time rcall EEwrite get_btime_end: rcall crlf _send_232_str _b_msg ldi EEaddr,_b_time rcall EEread mov temp,EEdata rcall send_byte rjmp cmd_top ;--------------------------------------- ;--- save my call sign save_mycall: rcall count_param cpi byte_cnt,0x02 ; check for correct num of args breq pc + 2 rjmp cmd_top ldi ii,1 ; point to second arg rcall pos_zl ldi EEaddr,_mycall rcall save_call rjmp save_ui_end ; write a trailing zero to eeprom, and exit ;------------------------------------------------------------------------------ ;--- save ui destination and optional digi path save_ui: ldi ii,1 ; point to begining of call in ram rcall pos_zl ldi EEaddr,_dest rcall save_call ldi EEdata,0 rcall EEwrite ; terminate station with a zero rcall count_param cpi byte_cnt,4 brlo save_ui_end ; no digis cpi byte_cnt,10 brsh save_ui_end ; too many digis ldi ii,2 ; point to first char of third arg (shoud be v) rcall pos_zl ld temp,z rcall to_upper cpi temp,'V' brne save_ui_end ldi ii,3 ; point to first digi in string ldi EEaddr,_digi ; point to eeprom digi storage save_ui_loop: push ii ; ii gets trashed in save_call rcall pos_zl ; position zl at digi rcall save_call ; parse to eeprom pop ii inc ii cp ii,byte_cnt ; compare current arg with total brne save_ui_loop ; not done save_ui_end: ldi EEdata,0 rcall EEwrite rjmp cmd_top ; write a trailing zero to eeprom, and exit ;--- parse out call sign and ssid, save in eeprom ; EEaddr points to storage, zl points to first char of station ;--- save_call: push EEaddr ; save pointer to beginning of call storage ldi ii,6 ldi EEdata,' ' fill_sp_lp: rcall EEwrite ; fill destination with six space chars dec ii brne fill_sp_lp subi EEaddr,6 ; reset ee pointer to start of storage ldi ii,6 ; max 6 char get_call_lp: ld temp,z+ tst temp ; check for end of string breq ssid_0 cpi temp,',' breq ssid_0 cpi temp,'-' ; test for ssid delim char breq get_ssid cpi temp,' ' breq ssid_0 save_call_char: rcall to_upper mov EEdata,temp rcall EEwrite ; store it dec ii brne get_call_lp ld temp,z+ cpi temp,'-' ; if the char following the sixth char is a - get ssid brne ssid_0 ; otherwise ssid = 0 get_ssid: ld temp,z+ ; get first char after '-' rcall asc_hex ; convert brcc pc + 2 rjmp ssid_0 ; was garbage char mov temp2,temp ; save it ld temp,z ; get second char after '-' rcall asc_hex brcc pc + 2 rjmp ssid_0_9 tst temp2 breq pc + 2 subi temp,-10 rjmp save_ssid ssid_0_9: mov temp,temp2 ; get saved first digit rjmp save_ssid ssid_0: ldi temp,0 save_ssid: ori temp,0x30 ; call was less then 6 chars and no delim mov EEdata,temp pop EEaddr ; get beginning of call storage subi EEaddr,-6 rcall EEwrite ; and save save_call_end: ret ;--- blink led 1 led_flash: led_1_on ; blink led ldi temp,100 rcall del_hunds led_1_off ret ;--- call before building any messages in 12c ram for sending ;--- if any packets waiting to display,display them, check carrier, disable ax25 reception wait_display: sbrc rx_flags,good_packet ; check for received packet rcall display_packet sbic PINB,det_614 ; check for carrier rjmp wait_display cbr rx_flags,(1 << rx_enable)|(1 << good_packet); disable receive ret ;--- ship formatted packet data to consoole ;--- data must reside at i2c_ram_begin, packet_len is msg length (no crc) display_packet: ;--- un-comment to only display packets with a pid of f0 (pure ax25) ; ldi zl,(i2c_ram_begin); point to beginning of data ;pid_loop: rcall i2c_rd ; look for end of path and start of data ; sbrs temp,0 ; rjmp pid_loop ; mov temp2,zl ; save pointer to data ; ; inc zl ; rcall i2c_rd ; cpi temp,0xf0 ; compare pid ; brne disp_pd_end ; no match abort ldi zl,(i2c_ram_begin + 7); pointer to txmit station rcall disp_call _send '>' ; make pretty ldi zl,(i2c_ram_begin); load pointer of destination station rcall disp_call ldi zl,(i2c_ram_begin + 13) rcall i2c_rd sbrc temp,0 ; any digis? rjmp disp_pack_data ; no, just display data digi_loop: ldi temp,',' rcall putc rcall disp_call ; display formatted call, ssid, will return with lsb of ssid location in carry brcc digi_loop ; keep looping disp_pack_data: _send ':' ; delim for data subi zl,-2 ; point past control and pid ldi ii,i2c_ram_begin add ii,packet_len sub ii,zl ; how many chars left breq disp_pd_end ; if no data just exit disp_pd_loop: rcall i2c_rd rcall putc dec ii brne disp_pd_loop disp_pd_end: rcall crlf ; new line cbr rx_flags,(1 << good_packet) ; clear flag to allow reception of next packet led_1_off ret ;--- display call and ssid on console ;--- z points to data, display 6 shifted chars, then ssid, ;--- returns with carry indicating lsb of ssid location disp_call: ldi ii,6 ; num of chars dp_loop: rcall i2c_rd ; get char lsr temp ; cpi temp,' ' ; is space? breq pc + 2 ; don't ship rcall putc dec ii brne dp_loop rcall i2c_rd mov ii,temp ; get ssid lsr ii ; shift off lsb andi ii,0x0f ; mask off top nibble breq ssid_end ; if ssid = zero display nothing _send '-' cpi ii,10 brlo ssid_1_9 _send '1' subi ii,10 ssid_1_9: mov temp,ii rcall send_nibble ssid_end: dec zl rcall i2c_rd ; get ssid again lsr temp ; shift lsb into carry ret ;-------------------------------------------------------------------------------------- ;--- send zero terminated string from ram, string must start at ram_begin ;--- trashes xl,zl,packet_len,temp ;--- sends eepom stored ui path before string send_ram_string:rcall wait_display ; make sure there are no packets waiting to display rcall copy_path_i2c ; start buildeing packet in i2c_ram ldi xl,ram_begin ; point to beginning of string send_str_loop: ld temp,x+ ; start copying ram msg to 12c_ram tst temp breq send_str_doit rcall i2c_wr ; save it rjmp send_str_loop send_str_doit: subi zl,(i2c_ram_begin); total msg length mov packet_len,zl rcall send_packet send_str_end: ret check_beacon: ldi EEaddr,_b_time rcall EEread ; get b_time tst EEdata breq check_b_end ; if b time is zero never beacon ldi zl,7 ; location in rtc for minutes counter rcall i2c_rd eor temp,EEdata brne check_b_end rcall send_beacon check_b_end: ret ;--------------------------------------------- ;--- send beacon text, reset beacon timer send_beacon: rcall wait_display rcall copy_path_i2c ; build msg in i2c ram _copy_ax_string _b_text ; copy the message text beaconl_1: subi zl,(i2c_ram_begin); total msg length mov packet_len,zl rcall send_packet rcall display_packet ; display on local console ldi zl,7 ; reset minutes timer in rtc ldi temp,0 rcall i2c_wr ret ;--- copy path from eeprom to i2c_ram ;--- add control and pid bytes copy_path_i2c: ldi zl,i2c_ram_begin; point to beggining of i2c ram _copy_ax_string_shift _dest; copy destination shifted _copy_ax_string_shift _mycall; copy mycall from eeprom _copy_ax_string_shift _digi; copy digi path (if any) dec zl ; point last char of path rcall i2c_rd ; get last char dec zl ; undo the inc zl at the end of the last read ori temp,0x01 ; set lsb to indicate end of path rcall i2c_wr ldi temp,control_byte; write control rcall i2c_wr ldi temp,pid_byte ; write pid rcall i2c_wr ret ;--- send packet from i2c_ram, packet must start at i2c_ram_begin, ;--- packet_len contains length total length send_packet: rcall wait_dcd ; wait for clear channel tx_mx614 ; change mode ptt_down ; key the radio led_2_on ; indicator ldi ii,tx_delay rcall send_ax_flags ; initial delay ldi temp,0xff mov ax25_crc_lo,temp; init crc mov ax25_crc_hi,temp mov ii,packet_len ; get message length ldi zl,i2c_ram_begin; pointer to beggining of message send_p_loop: rcall i2c_rd ; get char rcall put_ax ; send char dec ii brne send_p_loop rcall send_ax_crc ldi ii,tail_flags ; send final flag bytes rcall send_ax_flags ldi temp,tx_hang rcall del_hunds ptt_up ; release ptt led_2_off rx_mx614 ; back to rx mode cbi PORTB,txd_614 ; clear data out line, if high mx614 sends backchannel tone ret ;--- send calculated crc, lo byte first send_ax_crc: rcall ax25_tx_wait ; wait for last char to be sent, otherwise crc is in-correct com ax25_crc_lo ; invert calculated crc com ax25_crc_hi push ax25_crc_hi ; save hi because it will get trashed when we send lo mov temp,ax25_crc_lo rcall put_ax ; ship lo pop temp ; get saved hi rcall put_ax ret ;--- send ii number of flag bytes send_ax_flags: rcall ax25_tx_wait ; wait till all all data is sent sbr flags,(1 << ax25_flag); set flag bit to disble bit stuffing and crc ldi temp,0x7e rcall put_ax dec ii brne send_ax_flags rcall ax25_tx_wait cbr flags,(1 << ax25_flag); wait until last bit ships to clear flag bit ret ;--- original crc calculation routine ; now the isr does it on each bit sent ;calc_tx_crc: ldi temp,0xff ; mov ax25_crc_lo,temp; init crc ; mov ax25_crc_hi,temp ; ; ldi zl,i2c_ram_begin ; mov ii,packet_len ; ;tx_crc_l1: rcall i2c_rd ; ldi bitcnt,8 ; mov temp2,temp ;tx_crc_l2: lsr temp2 ; rotate lsb into carry ; rcall ax25_calc_crc ; dec bitcnt ; bitcnt was set to 8 above ; brne tx_crc_l2 ; dec ii ; brne tx_crc_l1 ; get next byte ; ret ;--- wait for carrier det to drop, then wait a random period, recheck carrier det pin. wait_dcd: sbic PINB,det_614 ; check pin rjmp wait_dcd rcall get_random andi temp,0b00111000 ; just some random delay rcall del_hunds sbic PINB,det_614 ; check pin again rjmp wait_dcd ret ;--- init rtc chip to enable minutes timer rtc_init: cli ; ldi zl,0 ; control status ldi temp,0x04 ; enable alarm register rcall i2c_wr ldi zl,0x07 ; ldi temp,00 rcall i2c_wr ; zero minutes counter ;ldi zl,0x08 ; last wr should post inc zl ldi temp,0b00000011 ; enable minutes counter rcall i2c_wr sei ret ;--- get toggle switch position ;--- charge cap on input pin, switch pin to input, ;--- see how long cap takes to discharge get_switch: cbi DDRB,sw_inp ; make pin an input cbi PORTB,sw_inp ; turn off pull up current sbic PINB,sw_inp ; wait for cap to discharge rjmp pc -1 ldi temp,2 ; switch down sbrc tic,3 ; < 8? ldi temp,0 ; switch up sbrc tic,4 ; < 0x10? ldi temp,1 ; switch middle sbi PORTB,sw_inp sbi DDRB,sw_inp ; charge cap for next time ret ; get a pseudo random 8 bit value via linear congruential algorithm ; ported from pic code posted by Nikolai Golovchenko get_random: mov temp,random lsl temp swap temp andi temp,0xE0 add random,temp add random,temp add random,temp ldi temp,0x35 sub random,temp mov temp,random ret ;--- get cr terminated string from console, store as zero terminated string in ram ;--- max length 96 chars, if terminated by ctrl-c, zero length, set carry ;--- try to support back space, may not work on all terminals get_string: ldi zl,ram_begin get_str_loop: rcall getc cpi temp,cr breq get_str_done cpi temp,ctrl_c breq get_str_abort cpi temp,bs brne save_str_char _send ' ' _send bs dec zl cpi zl,ram_begin ; we don't want to be able to dec past beginning of ram brsh pc + 2 ldi zl,ram_begin rjmp get_str_loop save_str_char: cpi zl,ram_begin + max_str_len breq get_str_loop st z+,temp rjmp get_str_loop get_str_abort: ldi zl,0 sec get_str_done: ldi temp,0 st z,temp ret ;--- set zl to first char of arg, ii pos_zl: ldi zl,ram_begin ; point to beginning pos_zl_loop: rcall next_arg dec ii brne pos_zl_loop ret ;--- count total number of parameters in string, parameters are seperated by ' ' or ',' count_param: clr byte_cnt ldi zl,ram_begin ; point to beginning of count_p_loop: rcall next_arg ld temp,z brne pc + 2 ret inc byte_cnt rjmp count_p_loop ; inc zl till a non-white char or ',' is encountered discard_white: ld temp,z cpi temp,' ' breq discard_loop cpi temp,',' breq discard_loop ret discard_loop: inc zl ; point to to next location rjmp discard_white ; inc zl till a ' ',',', or end of string discard_nw: ld temp,z tst temp ; test for zero termination breq discard_end cpi temp,',' breq discard_end cpi temp,' ' breq discard_end inc zl rjmp discard_nw discard_end: ret ; position zl at first char of next argument next_arg: ld temp,z tst temp brne pc + 2 ; test for end of string ret cpi temp,' ' breq pc + 2 rcall discard_nw rcall discard_white next_arg_end: ret ;---------------------------------------------------- ;---- delay temp * 10 ms (approx) del_hunds: add temp,sec_001 del_h_loop: cpse temp,sec_001 rjmp del_h_loop ret ;---------------------------------------------------- ;--- send a cr and lf to console crlf: ldi temp,cr rcall putc ldi temp,lf rcall putc ret ;--- wait for char from console, returns in u_data getc: sbrs flags,uart_rx ; check for data rjmp getc cbr flags,(1<<uart_rx); clear bit flag mov temp,u_data ; move data to temp sbrc flags,echo rcall putc ret ;---- put char to console putc: sbis USR,UDRE ;Is UART transmitter ready? rjmp putc ;If not, wait out UDR,temp ;Put character to UART ret ;--- wait for ax25_txbits = 0 then load buffer and set bit put_ax: rcall ax25_tx_wait mov ax25_txbuff,temp ldi temp,0x08 mov ax25_tx_bits,temp; 8 bits to send, start ret ;--- wait for buffer to empty/last bit was sent ax25_tx_wait: tst ax25_tx_bits brne ax25_tx_wait ret ;--- convert temp to upper case to_upper: cpi temp,'a' brlo toupper_end cpi temp,'z'+1 brsh toupper_end subi temp,' ' toupper_end: ret ;-------------------------------------- ;--- send byte to console as two asc chars send_byte: push temp ; save temp rcall send_hi_nibble; send hi nibble pop temp ; reget data push temp ; and save it again rcall send_nibble pop temp ; just restores temp ret ; fall into send nibble ;---------------------------------------- ;--- send low nibble as ascii send_hi_nibble: swap temp send_nibble: rcall hex_asc rcall putc ret ;-------------------------- ;--- routine converts 0-f hex in temp to 0 to F ascii hex_asc: andi temp,0b00001111 ;lower nybble only cpi temp,10 ;0-9 or A-F? brlo hex_asc_1 ; subi temp,-7 ;adjust A-F hex_asc_1: subi temp,-48 ;add offset to convert to ascii ret ;------------------------------------------------ ;--- convert asci char in temp to 0-15 nibble ;--- set carry if char is invalid (not 0-9 or a-f) asc_hex: rcall to_upper ; convert to upper case cpi temp,'F'+1 ; test for 'E' and above brsh asc_error cpi temp,'A' brsh asc_letters cpi temp,'9'+1 brsh asc_error cpi temp,'0' brsh asc_numbers asc_error: sec ; set carry to indicate error ret ; and out asc_letters: subi temp,0x07 ;A-F ('0'+7) is the same as ('A' - 10) asc_numbers: subi temp,'0' ;0-9 ret ; and out ;--- minimal 12c routines for accessing the pcf8583 ;--- NOTE: we are running the i2c bus faster then spec, ;--- it works fine with the pcf8583, but we may want to slow things down later ;----------------------------------------------------------------------------- ;--- random write, temp to address in zl on the external i2c_bus post incs zl ;--- trashes bit_cnt i2c_wr: push temp ; save the data rcall i2c_get_ack pop temp rcall i2c_tx rjmp i2_rw_done ;----------------------------------------------- ;--- random read, data pointed to by zl into temp post inc zl ;--- trashes bit_cnt i2c_rd: rcall i2c_get_ack rcall i2c_start ; generate another start, for random read ldi temp,0b10100001 ; device address with rd/wr bit set rcall i2c_tx rcall i2c_rx i2_rw_done: rcall i2c_stop inc zl cbr flags,(1 << i2c_busy); let isr know i2c is free ret ;------------------------------------------------- ;--- see if device is present, and is not currently in write phase i2c_get_ack: sbr flags,(1 << i2c_busy); let isr know that i2c is locked rcall i2c_start ; generate start condition ldi temp,0b10100000 ; device address with rd/wr bit cleared rcall i2c_tx brcs i2c_get_ack ; no ack try again mov temp,zl rcall i2c_tx ; send address ret ;------------------------------------------------- ;--- create start condition, sda going low while scl is high i2c_start: _sda_hi _scl_hi _sda_low ; sda goin low while scl is high _nop_2 _scl_low ; start clock chain ret ;-------------------------------------------------- ;--- stop condition, data goes high while clock high for stop bit i2c_stop: _sda_low i2c_st_1: _scl_hi ; set clock high _sda_hi ; data goes high while clock high for stop bit ret ;----------------------------------------------- ;--- clock out 8 bits on i2c bus i2c_tx: ldi bitcnt,8; total number of bits to send i2c_tx_1: rol temp ; shift msb to carry brcc i2c_tx_2 _sda_hi i2c_tx_2: brcs pc + 2 ; skpcs _sda_low rcall i2c_clock dec bitcnt ; any more bits? brne i2c_tx_1 _sda_hi ; bring data line hi for ack phase rol temp ; one more rotate to restores temp i2c_clock: _scl_hi ; bring clock hi _nop_2 ; a little delay clc ; copy status on data pin to carry bit sbic PIND,SDA sec _scl_low ; drop clock ret ; and done ;-------------------------------------------------- ;--- read 8 bits from 8 bit bus to i2c_data ;--- no support for ack phase! i2c_rx: ldi bitcnt,8; eight bits total to get from 12c bus _sda_hi ; just make sure data line is configured as an input i2c_rx_1: _scl_hi ; bring clock hi clc sbic PIND,SDA; copy pin status to carry bit sec rol temp ; rotate carry bit into lsb _scl_low ; drop clock dec bitcnt ; done? brne i2c_rx_1 ret ;---------------------------------------------------- ;--- internal eeprom routines ;--- read eeprom (post incs EEAR!) EERead: cli sbic EECR,EEWE; if EEWE not clear rjmp EERead ; keep waiting out EEAR,EEaddr sbi EECR,EERE; set EEPROM Read strobe in EEdata,EEDR; get data rjmp inc_eear; bump address and exit ;--- write EEPROM (post incs EEAR!) EEWrite: cli sbic EECR,EEWE; if EEWE not clear rjmp EEWrite ; keep waiting out EEAR,EEaddr out EEDR,EEdata; output the data sbi EECR,EEMWE; set master write enable sbi EECR,EEWE; set EEPROM Write strobe inc_eear: inc EEaddr sei ret ;-------------------------------------------------------------- ;--- copy eeprom strings to i2c_ram ;--- if shifting is enabled, left shift before copying ax25_s_string: sbr flags,(1 << ax25_shift); enable data shift before sending ax25 address data ax25_string: mov EEaddr,temp ; move temp to addr next_ax25_char: rcall EERead ; get char from eeprom tst EEdata ; test for eom brne next_ax ; cbr flags,(1 << ax25_shift); clear shift enable flag ret next_ax: mov temp,EEdata ; sbrc flags,ax25_shift; lsl temp ; rcall i2c_wr rjmp next_ax25_char ; loop it ;--- send zero terminated strings from eeprom to console ;serial_string: out EEAR,temp ; move temp to addr ;next_ser_char: rcall EERead ; get char from eeprom ; tst EEdata ; test for eom ; brne pc + 2 ; skip nz ; ret ; mov temp,EEdata ; ; rcall putc ; ship it ; rjmp next_ser_char ; loop it ;--- send zero terminated strings from flash to console ; use z for pointer ser_ram_str: lpm ; tst r0 ; brne pc + 2 ; ret mov temp,R0 ; rcall putc adiw zl,1 rjmp ser_ram_str ;********************************************************************* ; codespace stored string storage ;********************************************************************* ;--- message strings---------------- _hello: .db cr,lf,"AVR_UI_TNC 1.0 Copyright Henry Carl Ott N2RVQ",cr,lf,0 _b_msg: .db "B_TIME=",0 ;********************************************************************* ; EEPROM ;********************************************************************* .eseg .org 0x00 empty: .db 0x00 ; leave empty, may get corrupted do to possible bug ;--- mycall and dest should be 6 chars followed by ssid (add 30 hex) and then zero terminated ;--- total digi path should be 0 terminated ;--- the program will set the low bit of the final digi .org 0x01 _mycall: .db "NOCALL",0x30,0 ; call .ORG 0X09 _dest: .db "APZAVR",0x30,0 ; dest _digi: .db "RELAY ",0x30 ; digi path .db "WIDE ",0x30,0 ; final digi, zero terminate .org 0x3e _b_time: .db 0x00 ; beacon time in minutes _ee_flags: .db (1 << echo) ; just various status bits to remember during power down .org 0x40 _b_text: .db "AVR_UI_TNC TEST, Hello from an AVR",0 |
| CODE |
;*************************************************************************** ;* Title: AVR_UI_TNC EXAMPLE CODE Version 1.7 ;* Made available for educational, and non-commercial use. ;* Commercial use forbidden without license from author. ;* Code may be modified as long as it is released publicly and credit ;* is given to the original author(s). ;* (c) copyright 2000 Henry Carl Ott N2RVQ, all Rights reserved. ;* Questions, comments or offers of cash? carlott@si.rr.com http://users.rcn.com/carlott/ ;*************************************************************************** ;* Description: Example code to test basic UI packet functions on an AT90s2313 / MXCOMM MX-614 combo. ;* Please reference separate documents for schematics / hardware description. ;* Provides minimal tnc functionality for ui-frames. ;* Outputs received ax25 packets to console (total packet length 224 chars including path) ;* Transmits strings from console, max length, 96 chars not including path ;* Beacons at user determined intervals, beacon text up to 64 chars of eeprom stored message ;* Console configuration of mycall, ui destination and digi path, ;* beacon text (max 64 chars),beacon time, and console echo ;************************************************************************** ;* Credits: The main inspiration for the design was the TAPR PIC-E project. ;* I had used Microchip MCUs in a number of other projects, but I was currently ;* experimenting with Atmel AVR MCUs and found that the additional RAM/EPROM and ;* additional indexing modes made code development easier (at least for me). Besides I ;* was getting tired of all the bank switching on the PICs :). ;* For the software I looked very closely at the PIC-E code posted by ;* Byon Garrabrant, John Hansen, Mike Berg and others (sorry if I missed anyone). ;* The jump table decoding was inspired by Byons code, and the CRC calculation ;* is an almost direct port of his crc routine. ;*************************************************************************** ;* REVISIONS: ;*1.0 first release ;*1.1 cut a few cycles in rx isr to try to try to avoid overloading ;*1.2 put rx crc routine inline within rx-isr to speed up isr ;*1.5 changed code to support SPI FM25160 FRAM part instead of the I2C PCF8583 ;* with the lack of the rtc, we simplified the beacon timer (just on or off) ;* NOTE: Be sure to reference the rev 1.5 schematic ;*1.6 bug fixes, changed string reception routine to be int driven. misc tweaks ;*1.7 fixed beacon text bug, added non volitile converse mode flag bit ;*************************************************************************** ;* Disclaimer: I'm a lousy programmer. Please don't use this code/design for any ;* application where human life is at risk or property damage may occur. ;*************************************************************************** ; Console command descriptions: ; Commands are case insensitive. ; In most cases only the first letter of command is significant ; ; Mycall examples 'Mycall NOCALL-1' or 'M NOCALL' ; Unproto examples 'UNPROTO APRS VIA RELAY,WIDE5-5' or 'U APRS V WIDE' or 'u apz001' ; Echo examples 'ECHO OFF' or 'ECHO ON' or 'e off' ; Beacon examples 'BEACON ON' 'BEACON OFF' 'B OFF' 'B ON' ; BText examples 'BTEXT Test Message' or 'BT THIS IS A TEST,' ; Converse examples 'C' or 'converse' or 'conv' Enters converse mode ; Id examples 'ID' or 'I' Sends a sample beacon (will key radio!) ;--- start of the actual code --- .nolist .include "2313def.inc" .list .listmac ;*************************************************************************** ;* ;* MACROS ;* ;*************************************************************************** ;--- simple led macros .macro led_grn sbi DDRD,led sbi PORTD,led .endm .macro led_red sbi DDRD,led cbi PORTD,led .endm .macro led_off cbi DDRD,led cbi PORTD,led .endm ;---- SPI macros .macro spi_data_inp cbi DDRD,SPI_data .endm .macro spi_data_out sbi DDRD,spi_data .endm ;--- mx-614 specific macros .macro tx_mx614 sbi PORTB,mode0 cbi PORTB,mode1 .endm .macro rx_mx614 cbi PORTB,mode0 sbi PORTB,mode1 .endm .macro off_mx614 sbi PORTB,mode0; power down mode sbi PORTB,mode1 .endm ;--- radio control .macro ptt_down sbi PORTB,rad_ptt .endm .macro ptt_up cbi PORTB,rad_ptt .endm ;--- send single char to console .macro _send ldi temp,@0 rcall putc .endm ;--- send string to console .macro _send_232_str ldi zh,high(@0 * 2) ldi zl,low(@0 * 2) ;Init Z-pointer rcall ser_ram_str .endm ;--- skip next instruction .macro skip rjmp (pc + 2); skip, next instruction .endm ;--- copy zero terminated eeprom string into fram (left shift data first) .macro _copy_ax_string_shift ldi temp,@0 rcall ax25_s_string .endm ;--- copy zero terminated eeprom string into fram .macro _copy_ax_string ldi temp,@0 rcall ax25_string .endm ;--- send zero terminated eeprom string to console .macro _232_str ldi temp,@0 rcall serial_string .endm ;------delay two cycles in one instruction .macro _nop_2 rjmp (pc + 1) .endm ;----------------------------------------------------------------- ;--- port and pin defines ;--- portb .equ rxd_614 = 0; I 614_rxd .equ sw_inp = 1; I sw_inp .equ txd_614 = 2; O 614_txd .equ det_614 = 3; O I 614_det .equ mic_ptt = 4; I mic ptt input .equ rad_ptt = 5; O radio ptt output .equ mode1 = 6; O mx614 mode 1 select .equ mode0 = 7; O mx614 mode 0 select ;--- portd .equ spi_port = PORTD .equ spi_sck = 6; O SPI clock .equ led = 5; O led_1 .equ spi_cs = 4; O SPI chip select .equ spi_data = 3; I/O SPI data .equ spare_232 = 2; I external rs232 input .equ txd_232 = 1; O 232_txd .equ rxd_232 = 0; I 232_rxd ;---- some program equates and constants .equ ram_begin = 0x60; start of ram on 2313 .equ random = 0xc1 ; storage for random number generator .equ pack_buff_0 = 0xc2; .equ pack_buff_1 = 0xc3 .equ pack_buff_2 = 0xc4 .equ pack_buff_3 = 0xc5 .equ ctrl_c = 0x03 .equ bs = 0x08 ; back space .equ bel = 0x07 ; bell .equ cr = 0x0d ; carriage return .equ lf = 0x0a ; line feed .equ clock = 8000000 ; cpu clock .equ console_baud = 9600 .equ isr_reload_val= (0xffff - (clock/9600)) + 19 .equ beacon_time = 235 ; fixed beacon reload value in 2.56 second multiples .equ fram_op_wren = 0b00000110 .equ fram_op_rd = 0b00000011 .equ fram_op_wr = 0b00000010 .equ tx_bank = 7; what fram bank to send packets from .equ tx_delay = 40; number of flag bytes before data .equ tail_flags = 2; number of flags to send after data .equ tx_hang = 1; delay (.001) after last flag before un-keying .equ min_packet_len = 18; minimum length, + con+pid and crc of rx packet .equ max_packet_len = 255; maximim length, determined by available ram, isr discards extra chars .equ max_str_len = 96; how much of avr ram to use for string storage .equ reload_count = 59; ax25 rxd transition time interval counter reload value, used by rx isr .equ control_byte = 0x03; control and pid for ax25 ui packets .equ pid_byte = 0xf0; ;***************************** ;* Global Register Variables * ;***************************** .def r0 = r00; used with lpm instruction .def tic = r01; incs @ 9600 hz resets at 96 0-95 .def sec_01 = r02; incs @ 100 hz .def b_timer = r03; decs at .256 hz, will not go past zero, used for beacon timer .def ax25_txbuff = r05; ax25 data to send .def ax25_ones = r06; no of continous ones sent, used for bit stuffing .def ax25_crc_lo = r07; crc calculation low order byte rx/tx .def ax25_crc_hi = r08 ; crc calculation hi order byte rx/tx .def packet_len = r09; length of received packet not including crc .def ax25_rxbuff = r10; used to assemble rx bytes .def ax25_rx_count = r11; time interval between transitions .def ax25_tx_bits = r12; number of bits to send .def isr_save = r15; saves mcu status reg during isr .def temp = r16; gp working reg .def temp2 = r17 ; yet another working register .def ii = r18; gp loop counter .def flags = r19; various bit flags and ax25_tx flags .def rx_flags = r20; bit flags used to communicate with isr .def ax25_rx_chars = r21; received chars ptr/counter (used only within isr) .def ax25_rx_bitcnt = r22; used to assemble individual bits into bytes .def EEaddr = r23; eeprom address .def EEdata = r24; eeprom data for rd/wr .def byte_cnt = r25; general purpose byte counter .def xl = r26 .def xh = r27 .def yl = r28; string pointer, used to assemble rs-232 string in uart isr .def fram_bank = r29; bank select on FM25160 (low three bits), hi four bits are used by isr! .def yh = r29 .def zl = r30; used for indirect indexing and a gp register otherwise .def zh = r31; used for indirect indexing and a gp register otherwise .def bitcnt = r31; gp bit counter, ;************************** ;- bit flags defined ;************************** .equ string_rx = 0x00 ; cr terminated string was received .equ ax25_flag = 0x01 ; sending flags? (no bit stuffing or crc) .equ ax25_shift = 0x02 ; do we shift the data before sending? (address) .equ fram_no_sei = 0x03 ; used to disable sei on fram return routines when called from an isr .equ conv_mode = 0x05 ; non volitile flag bit, if we were in converse mode on power down ; re-enter converse mode on power up .equ echo = 0x06 ; if set echo console chars .equ beacon_enable = 0x07 ; enable beacon ;------------------------- ;************************** ;- ax25 recieve bits defined ;************************** .equ old_bit = 0x00 ; previous bit sample .equ rx_enable = 0x01 ; enable reception of packet data .equ in_packet = 0x02 ; currently receiving packet data .equ half_flag = 0x03 ; set if last bit received was a zero (more of an 1/8 flag) .equ new_bit = 0x04 ; new bit sample, new and old bit must be in these positions .equ flag_det = 0x05 ; last byte was a valid flag ;------------------------- ;********************* ;* Interrupt Vectors * ;********************* .CSEG .org 0x00 rjmp reset ; Reset Handle .org INT0addr ; external int 0; bit bang secondary serial port reti .org INT1addr ; external int 1 reti .org OVF1addr ; overflow of 16 bit timer 1 basic 9600hz tic rjmp t1int .org OVF0addr ; overflow of 8 bit timer 0 reti .org URXCaddr ; reception of data by uart rjmp uart_rx_isr ;--- timer 1 overflow interupt ;--- we should hit the isr @ 9600 hz ;--- the isr handles the low level transmission of ax25 byte and assembles received packets t1int: in isr_save,SREG ; save SREG push temp push zl ; we have to use zl and zh for thejump table anyway push zh ; so we might as well use the for gp ldi zh,high(isr_reload_val); reload timer1 ldi zl,low(isr_reload_val) out tcnt1h,zh out tcnt1l,zl inc tic ; bump counter everytime through ;--- ax25 transmit portion of isr ax25_tx: tst ax25_tx_bits ; any bits to send? breq ax25_tx_end ; no, branch to end mov temp,tic ; 9600 divided by 8, see if we are in a tx bit period (1200 baud) andi temp,0x07 ; mask off hi bits brne ax25_tx_end ; nope, fall through sbrc flags,ax25_flag ; clr ax25_ones ; if a flag byte, no bit stuffing mov temp,ax25_ones cpi temp,5 ; check for bit stuffing brne send_bit ; no match keep going rcall ax25_tx_0 ; stuff a zero and exit rjmp ax25_tx_end ; send_bit: sbrc ax25_txbuff,0 ; test lsb inc ax25_ones ; if a one, don't change output, just bump the ones counter sbrs ax25_txbuff,0 ; test lsb, data is sent lsb first rcall ax25_tx_0 dec ax25_tx_bits ; number of bits left to send lsr ax25_txbuff ; shift next bit into tx position, bit just sent goes into carry sbrs flags,ax25_flag ; if a flag byte, don't calculate crc rcall ax25_calc_crc ; generate crc on carry bit ax25_tx_end: ;--- ax25 rx portion of isr ax25_rx: sbrs rx_flags,rx_enable; is reception enabled? rjmp rx_reset_cnt ; no, jump to end sbis PINB,det_614 ; any dcd? rjmp rx_flush ; nope, reset registers tst ax25_rx_count ; test transition counter breq pc + 2 ; if @ zero, stay at zero dec ax25_rx_count cbr rx_flags,(1 << new_bit) sbic PINB,rxd_614 ; copy pin status sbr rx_flags,(1 << new_bit) mov temp,rx_flags swap temp ; moves old_bit and new_bit into same bit position eor temp,rx_flags sbrs temp,0 ; check for change rjmp ax25_rx_end ; nope rx_change: bst rx_flags,new_bit; copy new_bit to old bld rx_flags,old_bit tst ax25_rx_count ; test for timout brne pc + 2 ; > 0 ? rjmp rx_flush ; timeout lsr ax25_rx_count lsr ax25_rx_count lsr ax25_rx_count ; divide count by 8 ldi zh,high(rx_jmp) ; ldi zl,low(rx_jmp) ; Init Z-pointer add zl,ax25_rx_count clr temp ; adc zh,temp ; if a carry from low order add it in ijmp ;--- jump table rx_jmp: rjmp rx_flag ;0 flag rjmp rx_add_5 ;1 zero stuff, add five ones toss zero rcall rx_add_1 ;2 11110 rcall rx_add_1 ;3 1110 rcall rx_add_1 ;4 110 rcall rx_add_1 ;5 10 rjmp rx_add_0 ;6 0 rjmp rx_flush ;7 error, too soon ;--- check that at least one bit has been received and that it was a zero ;--- if so a flag has been received, otherwise error rx_flag: sbrs rx_flags,half_flag; check for first zero of flag rjmp rx_flush sbr rx_flags,(1 << flag_det); set flag detected flag cbr rx_flags,(1 << half_flag) ldi ax25_rx_bitcnt,8; reload bit counter for next byte sbrc rx_flags,in_packet; are we currently within a packet? rjmp ax25_rx_close ; yes, try to close rjmp rx_reset_cnt ; just keep going ;--- add 5 ones to buffer, and discard extra stuffed zero rx_add_5: cbr rx_flags,(1<<half_flag); clear half flag bit because last bit written is a one rcall rx_add_1 ; only one more instruction then a loop rcall rx_add_1 ; is faster, and we needed the register elsewhere rcall rx_add_1 rcall rx_add_1 rcall rx_add_1 rjmp rx_reset_cnt ; done ;--- add a single one to buffer, carry contains bit rx_add_1: sec ; set carry rcall rx_in_bit ret ;--- add a zero to received byte rx_add_0: sbr rx_flags,(1<<half_flag) ; set half flag bit because we are writing a zero clc ; clear carry rcall rx_in_bit rjmp rx_reset_cnt ; done ax25_rx_close: cpi ax25_rx_chars,min_packet_len; minimum length packet brlo rx_flush ldi temp,0xF0 ; compare with calculated crc eor temp,ax25_crc_hi brne rx_flush ; bad crc ldi temp,0xB8 eor temp,ax25_crc_lo brne rx_flush ; bad crc subi ax25_rx_chars,2 ; reduce to discard crc mov zl,fram_bank swap zl andi zl,0b00000011 ; mask off all but low two bits subi zl,(- pack_buff_0) ; add in address st z,ax25_rx_chars ; store number of chars received led_red ; indicator rx_flush: rcall ax25_rx_init rx_reset_cnt: ldi temp,reload_count ; get ready for next bit mov ax25_rx_count,temp ; reload counter ax25_rx_end: ldi temp,96 ; divide by 96 to get our 10ms tic eor temp,tic ; brne no_rollover ; no rollover clr tic inc sec_01 ; bump hunds of seconds (10ms) brne no_rollover tst b_timer ; see if b_timer is at zero breq no_rollover ; leave at zero dec b_timer ; b_timer decs at .256 hz, but won't dec past zero no_rollover: pop zh ; restore pop zl ; pop temp ; out SREG,isr_save ; restore reti ;--- flip mx614 txd line line, zero the ones counter ;--- trashes temp and zl ax25_tx_0: in temp,PORTB ; get current pin status ldi zl,(1 << txd_614) ; can't xor directly to io port eor temp,zl ; invert bit out PORTB,temp ; and back out to pin clr ax25_ones ; clear ones counter ret ;--- rotates carry into rx buffer, determines packet status ;--- store the byte if in packet, calcs crc on full bytes only rx_in_bit: ror ax25_rxbuff ; save bit rotate carry into position (lsb first) dec ax25_rx_bitcnt brne store_ax_end ; not zero, still within byte, just exit ldi ax25_rx_bitcnt,8 ; reset bit counter counter sbrc rx_flags,in_packet rjmp store_ax_byte ; within a packet, handle byte sbrs rx_flags,flag_det ; not in packet, was the last byte a flag? ret ; nope, just discard start_packet: sbr rx_flags,(1<<in_packet) ; start of packet data (that's the theory) ldi zl,pack_buff_0 ; find an empty bank to store new packet andi fram_bank,0x0f ; start at bank zero find_bank_lp: ld temp,z+ tst temp breq store_ax_byte ; got an empty buffer subi fram_bank,(- 0x10) ; add 0x10 cpi zl,pack_buff_0 + 4 ; done? brne find_bank_lp rjmp ax25_rx_init ; no place to store data, dump packet store_ax_byte: cbr rx_flags,(1<<flag_det)|(1<<half_flag); clear flags mov zl,ax25_rxbuff ; grab a copy of the data rx_crc_loop: mov temp,zl ; get data andi temp,0x01 ; mask off all but lsb eor ax25_crc_lo,temp ; xor lsb lsr ax25_crc_hi ; zero hi bit, rotate ror ax25_crc_lo ; rotate all, lsb goes into carry brcc rx_crc_tst ; if lsb was zero, get out ldi temp,0x08 ; if lsb is one, xor in poly eor ax25_crc_lo,temp ldi temp,0x84 eor ax25_crc_hi,temp rx_crc_tst: lsr zl ; move next bit into position dec ax25_rx_bitcnt ; bitcnt was set to 8 above brne rx_crc_loop ldi ax25_rx_bitcnt,8 ; reset counter for next byte inc ax25_rx_chars ; cpi ax25_rx_chars,max_packet_len; brsh store_ax_end ; too many chars, discard the byte sbrs ax25_rxbuff,0 ; check for end of path rjmp rx_save_it cpi ax25_rx_chars,(min_packet_len - 4); length - control,pid, and crc brsh rx_save_it ; long enough rjmp ax25_rx_init ; too short, note jmp rx_save_it: mov zl,ax25_rx_chars ; pointer to storage dec zl ; adjust mov temp,ax25_rxbuff swap fram_bank ; get correct bank to write to rcall fram_wr_no_sei ; write data swap fram_bank ; restore fram_bank store_ax_end: ret ;--- init registers and flags for next packet ax25_rx_init: cbr rx_flags,(1 << half_flag)|(1 << flag_det)|(1 << in_packet) ldi ax25_rx_bitcnt,8; reset bit counter clr ax25_rxbuff clr ax25_rx_chars ; start over ldi temp,0xff ; reset crc mov ax25_crc_lo,temp mov ax25_crc_hi,temp ret ;------------------------------------------------------------ ; ported from Byon Garrabrant's pic based code ; assume carry contains data bit, init crc to 0xffff before starting ax25_calc_crc: clr temp ; rotate carry into lsb rol temp eor ax25_crc_lo,temp ; xor lsb lsr ax25_crc_hi ; zero hi bit, rotate ror ax25_crc_lo ; rotate all, lsb goes into carry brcs pc+2 ; skip on carry set ret ldi temp,0x08 ; if carry is one, xor in poly eor ax25_crc_lo,temp ldi temp,0x84 eor ax25_crc_hi,temp ret ;--- rs-232 char reception isr, checks for errors and discards bad chars uart_rx_isr: in isr_save,SREG ; store SREG push temp sbic USR, FE ; skip if framing error rjmp uart_err ; in temp, UDR ; get rx byte sbic USR, OR ; skip if overrun error rjmp uart_err ; sbrc flags,echo ; if echo is enabled rcall putc ; echo char sbrc flags,string_rx ; has last string been processed rjmp uart_isr_end ; no, don't string process ;--------------------------------------------------------------------- cpi temp,cr breq uart_str_done ; cpi temp,ctrl_c ; check for any char = ctrl c ; breq uart_rx_abort ; write ctrl_c char and terminat sbrs flags,echo ; if echo is enabled support bs char rjmp uart_rx_store ; nope cpi temp,bs brne uart_rx_store cpi yl,ram_begin ; we don't want to dec past breq pc + 2 dec yl sbrs flags,echo rjmp uart_isr_end ; and exit without saving char _send ' ' _send bs rjmp uart_isr_end uart_rx_store: cpi yl,ram_begin + max_str_len breq uart_isr_end ; no room, discard st y+,temp ; store the char in ram rjmp uart_isr_end ; all done ;uart_rx_abort: ldi yl,ram_begin ; reset to beginning of ram storage ; ldi temp,ctrl_c ; save the char ; st y+,temp uart_str_done: ldi temp,0 st y,temp ; store zero terminate sbr flags,(1 << string_rx); set flag to indicate complete ldi yl,ram_begin ; reset pointer to beginning of ram rjmp uart_isr_end uart_err: in temp, UDR ; read and discard byte to clear UDR uart_isr_end: pop temp ; restore out SREG,isr_save ; restore reti ;------------------------------------------------------------------------ ;--- start here on power up or external reset ;--- init ports, ints, and zero all registers reset: ldi r16,0 ldi zl,0 ; beginning of registers clr_reg: st z+,r16 cpi zl,0x1e ; don't zero z reg brne clr_reg ldi zl,ram_begin clr_ram: st z+,r16 cpi zl,0xe0 brne clr_ram ;--- init stack pointer (somewhat important) ldi temp,low(RAMEND) ; init stack out spl,temp ;--- portb setup ldi temp,(1<<rad_ptt)|(1<<mode1)|(1<<mode0)|(1<<txd_614)|(1 << sw_inp) out DDRB,temp sbi PORTB,sw_inp ; charge cap on switch input pin ;--- portd setup ldi temp,(1<<led)|(1<<txd_232)|(1<<spi_sck)|(1<<spi_cs)|(1<<spi_data) out DDRD,temp ;--- setup uart, 9600 baud, int on rx, no tx ints ldi temp,((clock / console_baud) / 16) -1 out UBRR,temp ; Set baud rate generator ldi temp, (1<<RXCIE)|(1<<RXEN)|(1<<TXEN); enable rxc interrupts out UCR,temp ; enable UART tx & rx w/o interrupts ;--- set up timer 1 ldi temp,(1 << CS10) ; timer1 no prescale out TCCR1B,temp ldi temp,(1<<TOIE1) ; enable timer 1 overflow int out TIMSK,temp ;--- set up tmr0 ; ldi temp,2 ; timer 0 prescale/8 ; out TCCR0,temp ; ; ldi temp,0b00000010 ; enable Timer 0 interrupt ; out TIMSK,temp ;--- global enable ints sei ; global enable ints ldi EEaddr,_EE_flags ; copy eeprom stored flags to flag reg rcall EEread mov flags,EEdata ldi yl,ram_begin ; location of string storage for isr rcall fram_init clr fram_bank off_mx614 ; init mx614 _send_232_str _hello ; display sign on message rcall led_flash ; blink rx_mx614 ; put into rx mode to start sbr rx_flags,(1 << rx_enable); enable/re-enable reception sbrc flags,conv_mode rjmp converse_mode rcall get_switch ; get switch status tst temp breq converse_mode ; if switch is up goto converse mode ;---------------------------------------------- cmd_top: cbr flags,(1 << string_rx)|(1<<conv_mode) ; allow for new string rcall save_ee_flags sbrs flags,echo rjmp cmd_no_echo rcall crlf ; prompt _send '.' cmd_no_echo: sbrs flags,string_rx rjmp cmd_no_echo ldi zl,ram_begin ; convert first char to upper case ld temp,z rcall to_upper do_cmd: cpi temp,'B' brne pc+2 rjmp b_commands ; check for btest or beacon command cpi temp,'C' ; enter converse mode brne pc+2 rjmp converse_mode cpi temp,'M' ; get my callsign brne pc+2 rjmp save_mycall cpi temp,'U' ; ui station and path brne pc+2 rjmp save_ui cpi temp,'E' ; get echo mode brne pc+2 rjmp echo_mode cpi temp,'I' ; send a beacon (id) (will key radio) brne cmd_top rjmp test_beacon ; enter converse mode ; display all received packets ; send all cr terminated rs-232 strings ; send beacon text every beacon time out (if beacon enabled) ; ctrl-c as first char followed by cr will return to command mode converse_mode: cbr flags,(1<<string_rx) sbr flags,(1<<conv_mode) rcall save_ee_flags ; save coverse mode flag bit rcall crlf sbr rx_flags,(1 << rx_enable); enable/re-enable reception converse_loop: sbrs flags,string_rx ; check for console data rjmp converse_1 lds temp,ram_begin ; get first char of string cpi temp,ctrl_c breq cmd_top rcall send_ram_string ; send the string cbr flags,(1 << string_rx) rcall crlf ; make pretty rcall display_packet ; local echo of transmitted string converse_1: sbrs flags,echo rjmp converse_2 cpi yl,ram_begin ; are we currently receiving a string? brne converse_loop ; yes back to top converse_2: rcall disp_any_packets ; see if any packets have been received rcall check_beacon ; check for beacon rjmp converse_loop ; keep looping ;--- send beacon text, then back to top test_beacon: rcall crlf rcall send_beacon rjmp cmd_top ;-- check to see if this is a BText or BEacon command b_commands: lds temp,(ram_begin + 1) rcall to_upper cpi temp,'T' breq get_b_text cpi temp,' ' breq beacon_mode cpi temp,'E' breq beacon_mode rjmp cmd_top ;--- copy beacon text to eeprom ----------------- get_b_text: ldi EEaddr,_b_text ; point to b_text storage ldi ii,1 ; point to beginning of string rcall pos_zl get_b_loop: ldi EEdata,0 cpi EEaddr,0x7f ; test for end of storage breq pc + 2 ld EEdata,z+ ; get char rcall EEwrite tst EEdata ; tst for zero termination brne get_b_loop ; get next char get_btxt_end: rjmp cmd_top ;--- enable or disable local char echo echo_mode: rcall count_param cpi byte_cnt,0x02 brne echo_mode_end rcall get_on_off brcs echo_mode_end cbr flags,1<<echo ; assume off sbrc ii,0 sbr flags,1<<echo rcall save_ee_flags ; save flags echo_mode_end: rjmp cmd_top ;---- enable or disable beacon at fixed interval beacon_mode: rcall count_param cpi byte_cnt,0x02 brne beacon_m_end rcall get_on_off brcs beacon_m_end cbr flags,1<<beacon_enable ; assume off sbrc ii,0 sbr flags,1<<beacon_enable rcall save_ee_flags ; save flags sbrs flags,echo ; if echo is enabled echo beacon status rjmp cmd_top _send_232_str _beacon sbrs flags,beacon_enable rjmp beacon_off _send 'N' rjmp cmd_top beacon_off: _send 'F' _send 'F' beacon_m_end: rjmp cmd_top ;--- get second command argument, test for on or off (actually on or of) ;--- return value in ii, 0 = off. 1 = on, carry set if no match get_on_off: ldi ii,1 ; point to first char of 2nd arg rcall pos_zl ld temp,z+ rcall to_upper cpi temp,'O' brne get_of_end ; error ld temp,z rcall to_upper clr ii clc cpi temp,'F' ; is second char of string an 'f'? breq get_of_end inc ii cpi temp,'N' ; is second char of string an 'n'? breq get_of_end get_of_err: sec ; set carry to indicate no match get_of_end: ret ;--- write non volitile flag bits to eeprom save_ee_flags: mov EEdata,flags andi EEdata,0b11100000; we only save the top 2 non-volatile flag bits ldi EEaddr,_EE_flags rcall EEwrite ret ;--------------------------------------- ;--- save my call sign save_mycall: rcall count_param cpi byte_cnt,0x02 ; check for correct num of args breq pc + 2 rjmp cmd_top ldi ii,1 ; point to second arg rcall pos_zl ldi EEaddr,_mycall rcall save_call rjmp save_ui_end ; write a trailing zero to eeprom, and exit ;------------------------------------------------------------------------------ ;--- save ui destination and optional digi path save_ui: ldi ii,1 ; point to begining of call in ram rcall pos_zl ldi EEaddr,_dest rcall save_call ldi EEdata,0 rcall EEwrite ; terminate station with a zero rcall count_param cpi byte_cnt,4 brlo save_ui_end ; no digis cpi byte_cnt,10 brsh save_ui_end ; too many digis ldi ii,2 ; point to first char of third arg (shoud be v) rcall pos_zl ld temp,z rcall to_upper cpi temp,'V' brne save_ui_end ldi ii,3 ; point to first digi in string ldi EEaddr,_digi ; point to eeprom digi storage save_ui_loop: push ii ; ii gets trashed in save_call rcall pos_zl ; position zl at digi rcall save_call ; parse to eeprom pop ii inc ii cp ii,byte_cnt ; compare current arg with total brne save_ui_loop ; not done save_ui_end: ldi EEdata,0 rcall EEwrite rjmp cmd_top ; write a trailing zero to eeprom, and exit ;--- parse out call sign and ssid, save in eeprom ; EEaddr points to storage, zl points to first char of station ;--- save_call: push EEaddr ; save pointer to beginning of call storage ldi ii,6 ldi EEdata,' ' fill_sp_lp: rcall EEwrite ; fill destination with six space chars dec ii brne fill_sp_lp subi EEaddr,6 ; reset ee pointer to start of storage ldi ii,6 ; max 6 char get_call_lp: ld temp,z+ tst temp ; check for end of string breq ssid_0 cpi temp,',' breq ssid_0 cpi temp,'-' ; test for ssid delim char breq get_ssid cpi temp,' ' breq ssid_0 save_call_char: rcall to_upper mov EEdata,temp rcall EEwrite ; store it dec ii brne get_call_lp ld temp,z+ cpi temp,'-' ; if the char following the sixth char is a - get ssid brne ssid_0 ; otherwise ssid = 0 get_ssid: ld temp,z+ ; get first char after '-' rcall asc_hex ; convert brcc pc + 2 rjmp ssid_0 ; was garbage char mov temp2,temp ; save it ld temp,z ; get second char after '-' rcall asc_hex brcc pc + 2 rjmp ssid_0_9 tst temp2 breq pc + 2 subi temp,-10 rjmp save_ssid ssid_0_9: mov temp,temp2 ; get saved first digit rjmp save_ssid ssid_0: ldi temp,0 save_ssid: ori temp,0x30 ; call was less then 6 chars and no delim mov EEdata,temp pop EEaddr ; get beginning of call storage subi EEaddr,-6 rcall EEwrite ; and save save_call_end: ret ;--- blink led 1 led_flash: led_red ; blink led ldi temp,100 rcall del_hunds led_off ret ;---- check buffers and send any queued packets to console disp_any_packets: ldi zl,pack_buff_0 ; pointer to first buffer andi fram_bank,0xf0 ; mask off low bits, start at bank zero disp_buff_lp: ld temp,z+ tst temp ; test for packet length in buffer brne disp_doit inc fram_bank cpi zl,pack_buff_0 + 4 brne disp_buff_lp disp_buff_done: led_off ret disp_doit: mov packet_len,temp rcall display_packet clr temp mov zl,fram_bank andi zl,0x03 ; mask off hi bits subi zl,(- pack_buff_0) st z,temp ; mark buffer as empty rjmp disp_any_packets; start over ;--- ship formatted packet data to console ;--- data must reside in fram starting at zero, packet_len is msg length without crc ;--- fram_bank must already be set display_packet: ;--- un-comment to only display packets with a pid of f0 (pure ax25) ; clr zl ; point to beginning of data ;pid_loop: rcall fram_rd ; look for end of path and start of data ; sbrs temp,0 ; rjmp pid_loop ; inc zl ; rcall fram_rd ; cpi temp,0xf0 ; compare pid ; brne disp_pd_end ; no match abort ldi zl,7 ; pointer to txmit station rcall disp_call _send '>' ; make pretty clr zl ; load pointer of destination station rcall disp_call ldi zl,13 rcall fram_rd sbrc temp,0 ; any digis? rjmp disp_pack_data ; no, just display data digi_loop: ldi temp,',' rcall putc rcall disp_call ; display formatted call, ssid, will return with lsb of ssid location in carry brcc digi_loop ; keep looping disp_pack_data: _send ':' ; delim for data subi zl,-2 ; point past control and pid disp_pd_loop: cp packet_len,zl ; display the msg data breq disp_pd_end rcall fram_rd rcall putc rjmp disp_pd_loop disp_pd_end: rcall crlf ; new line ret ;--- display call and ssid on console ;--- z points to data, display 6 shifted chars, then ssid, ;--- returns with carry indicating lsb of ssid location disp_call: ldi ii,6 ; num of chars dp_loop: rcall fram_rd ; get char lsr temp ; cpi temp,' ' ; is space? breq pc + 2 ; don't ship rcall putc dec ii brne dp_loop rcall fram_rd mov ii,temp ; get ssid lsr ii ; shift off lsb andi ii,0x0f ; mask off top nibble breq ssid_end ; if ssid = zero display nothing _send '-' cpi ii,10 brlo ssid_1_9 _send '1' subi ii,10 ssid_1_9: mov temp,ii rcall send_nibble ssid_end: dec zl rcall fram_rd ; get ssid again lsr temp ; shift lsb into carry ret ;-------------------------------------------------------------------------------------- ;--- send zero terminated string from ram, string must start at fram_begin ;--- trashes xl,zl,packet_len,temp ;--- sends eepom stored ui path before string send_ram_string: andi fram_bank,0xf0 ; mask off bottonm bits ori fram_bank,tx_bank; set to tx bank in fram rcall copy_path_fram ; start buildeing packet in fram ldi xl,ram_begin ; point to beginning of string send_str_loop: ld temp,x+ ; start copying ram msg to fram tst temp breq send_str_doit rcall fram_wr ; save it rjmp send_str_loop send_str_doit: mov packet_len,zl rcall send_packet send_str_end: ret ;--- check beacon for time out(if enabled) , check_beacon: sbrs flags,beacon_enable rjmp check_b_end ; if beacon is not enabled tst b_timer ; is beacon timer at zero? breq send_beacon check_b_end: ret ;--------------------------------------------- ;--- send beacon text, reset beacon timer send_beacon: andi fram_bank,0xf0 ; mask off bottonm bits ori fram_bank,tx_bank; set to high bank in fram rcall copy_path_fram ; start building build msg in fram _copy_ax_string _b_text ; copy the message text beaconl_1: mov packet_len,zl rcall send_packet rcall display_packet ; display on local console ldi temp,beacon_time mov b_timer,temp ; ret ;--- copy path from eeprom to already selected fram_bank ;--- add control and pid bytes copy_path_fram: clr zl ; point to beggining of fram _copy_ax_string_shift _dest; copy destination shifted _copy_ax_string_shift _mycall; copy mycall from eeprom _copy_ax_string_shift _digi; copy digi path (if any) dec zl ; point last char of path rcall fram_rd ; get last char dec zl ; undo the inc zl at the end of the last read ori temp,0x01 ; set lsb to indicate end of path rcall fram_wr ldi temp,control_byte; write control rcall fram_wr ldi temp,pid_byte ; write pid rcall fram_wr ret ;--- send packet from fram, packet must start fram_begin, fram_bank must be set properly ;--- packet_len contains length total length send_packet: rcall wait_dcd ; wait for clear channel cbr rx_flags,(1 << rx_enable); disable receive (rx routine will change crc registers) tx_mx614 ; change mode on modem chip ptt_down ; key the radio ldi ii,tx_delay rcall send_ax_flags ; initial delay ldi temp,0xff mov ax25_crc_lo,temp; init crc mov ax25_crc_hi,temp mov ii,packet_len ; get message length clr zl ; pointer to start of message send_p_loop: rcall fram_rd ; get char rcall put_ax ; send char dec ii brne send_p_loop rcall send_ax_crc ldi ii,tail_flags ; send final flag bytes rcall send_ax_flags ldi temp,tx_hang rcall del_hunds ptt_up ; release ptt rx_mx614 ; back to rx mode cbi PORTB,txd_614 ; clear data out line, if high mx614 sends backchannel tone sbr rx_flags,(1 << rx_enable) ret ;--- send calculated crc, lo byte first send_ax_crc: rcall ax25_tx_wait ; wait for last char to be sent, otherwise crc is in-correct com ax25_crc_lo ; invert calculated crc com ax25_crc_hi push ax25_crc_hi ; save hi because it will get trashed when we send lo mov temp,ax25_crc_lo rcall put_ax ; ship lo pop temp ; get saved hi rcall put_ax ; and send hi ret ;--- send ii number of flag bytes send_ax_flags: rcall ax25_tx_wait ; wait till all all data is sent sbr flags,(1 << ax25_flag); set flag bit to disble bit stuffing and crc ldi temp,0x7e rcall put_ax dec ii brne send_ax_flags rcall ax25_tx_wait cbr flags,(1 << ax25_flag); wait until last bit ships to clear flag bit ret ;--- wait for carrier det to drop, then wait a random period, re-check carrier det pin. wait_dcd:;rcall disp_any_packets sbic PINB,det_614 ; check pin rjmp wait_dcd rcall get_random andi temp,0b00111000 ; just some random delay rcall del_hunds sbic PINB,det_614 ; check pin again rjmp wait_dcd ret ;--- get toggle switch position ;--- charge cap on input pin, switch pin to input, ;--- see how long cap takes to discharge get_switch: tst tic ; wait for tic = 0 brne pc - 1 cbi DDRB,sw_inp ; make pin an input cbi PORTB,sw_inp ; turn off pull up current sbic PINB,sw_inp ; wait for cap to discharge rjmp pc -1 ldi temp,2 ; switch down sbrc tic,3 ; < 8? ldi temp,0 ; switch up sbrc tic,4 ; < 0x10? ldi temp,1 ; switch middle sbi PORTB,sw_inp sbi DDRB,sw_inp ; charge cap for next time sei ret ; get a pseudo random 8 bit value via linear congruential algorithm ; ported from pic code posted by Nikolai Golovchenko ; trashes ii, returns random value in temp get_random: lds temp,random ; get last value from ram mov ii,temp ; save in both ii and temp lsl ii swap ii andi ii,0xE0 add temp,ii add temp,ii add temp,ii ldi ii,0x35 sub temp,ii sts random,temp ret ;--- set zl to first char of arg, ii pos_zl: ldi zl,ram_begin ; point to beginning pos_zl_loop: rcall next_arg dec ii brne pos_zl_loop ret ;--- count total number of parameters in string, parameters are seperated by ' ' or ',' count_param: clr byte_cnt ldi zl,ram_begin ; point to beginning count_p_loop: rcall next_arg ld temp,z brne pc + 2 ret inc byte_cnt rjmp count_p_loop ;--- inc zl till a non-white char or ',' is encountered discard_white: ld temp,z cpi temp,' ' breq discard_loop cpi temp,',' breq discard_loop ret discard_loop: inc zl ; point to to next location rjmp discard_white ; inc zl till a ' ',',', or end of string discard_nw: ld temp,z tst temp ; test for zero termination breq discard_end cpi temp,',' breq discard_end cpi temp,' ' breq discard_end inc zl rjmp discard_nw discard_end: ret ; position zl at first char of next argument next_arg: ld temp,z tst temp brne pc + 2 ; test for end of string ret cpi temp,' ' breq pc + 2 rcall discard_nw rcall discard_white next_arg_end: ret ;---------------------------------------------------- ;---- delay temp * 10 ms (approx) del_hunds: add temp,sec_01 del_h_loop: cpse temp,sec_01 rjmp del_h_loop ret ;---------------------------------------------------- ;--- send a cr and lf to console crlf: ldi temp,cr rcall putc ldi temp,lf rcall putc ret ;---- put char to console putc: sbis USR,UDRE ;Is UART transmitter ready? rjmp putc ;If not, wait out UDR,temp ;Put character to UART ret ;--- wait for ax25_txbits = 0 then load buffer and set bit put_ax: rcall ax25_tx_wait mov ax25_txbuff,temp ldi temp,0x08 mov ax25_tx_bits,temp; 8 bits to send, start ret ;--- wait for buffer to empty/last bit was sent ax25_tx_wait: tst ax25_tx_bits brne ax25_tx_wait ret ;--- convert temp to upper case to_upper: cpi temp,'a' brlo toupper_end cpi temp,'z'+1 brsh toupper_end subi temp,' ' toupper_end: ret ;-------------------------------------- ;--- send byte to console as two asc chars send_byte: push temp ; save temp rcall send_hi_nibble; send hi nibble pop temp ; reget data push temp ; and save it again rcall send_nibble pop temp ; just restores temp ret ; fall into send nibble ;---------------------------------------- ;--- send low nibble as ascii send_hi_nibble: swap temp send_nibble: rcall hex_asc rcall putc ret ;-------------------------- ;--- routine converts 0-f hex in temp to 0 to F ascii hex_asc: andi temp,0b00001111 ;lower nybble only cpi temp,10 ;0-9 or A-F? brlo hex_asc_1 ; subi temp,-7 ;adjust A-F hex_asc_1: subi temp,-48 ;add offset to convert to ascii ret ;------------------------------------------------ ;--- convert asci char in temp to 0-15 nibble ;--- set carry if char is invalid (not 0-9 or a-f) asc_hex: rcall to_upper ; convert to upper case cpi temp,'F'+1 ; test for 'E' and above brsh asc_error cpi temp,'A' brsh asc_letters cpi temp,'9'+1 brsh asc_error cpi temp,'0' brsh asc_numbers asc_error: sec ; set carry to indicate error ret ; and out asc_letters: subi temp,0x07 ;A-F ('0'+7) is the same as ('A' - 10) asc_numbers: subi temp,'0' ;0-9 ret ; and out ;----- random write temp to address pointed to by zl ;----- post inc zl fram_wr_no_sei: sbr flags,(1<<fram_no_sei) fram_wr: cli push temp ; save data cbi spi_port,spi_cs ; select chip ldi temp,fram_op_wren; get op code rcall fram_tx ; send it sbi spi_port,spi_cs ; de-select chip cbi spi_port,spi_cs ; re-enable chip mov temp,fram_bank ; get hi order bits swap temp lsr temp ; move into position andi temp,0b00111000 ; mask off other bits ori temp,fram_op_wr ; add in write op code rcall fram_tx ; send hi bits and wr op code mov temp,zl rcall fram_tx ; send low address pop temp ; get data rcall fram_tx ; and send data inc zl ; bump low address rjmp fram_stop ;--- clock out 8 bits from temp to spi bus fram_tx: spi_data_out ; set spi data line as output ldi bitcnt,0x08 ; 8 bits to send fram_tx_loop: rol temp ; rotate msb to carry ; brcs pc + 2 cbi spi_port,spi_data; assume a zero brcc pc + 2 sbi spi_port,spi_data sbi spi_port,spi_sck; blip clock nop ; a little delay for fram timing spec cbi spi_port,spi_sck dec bitcnt brne fram_tx_loop ret ;----- read address pointed to by zl, returns in temp ;----- post inc zl fram_rd: cli cbi spi_port,spi_cs ; enable chip mov temp,fram_bank ; get hi order bits swap temp lsr temp ; move into position andi temp,0b00111000 ; mask off other bits ori temp,fram_op_rd ; add in write op code rcall fram_tx mov temp,zl rcall fram_tx spi_data_inp ; set spi data line as an input ldi bitcnt,0x08 ; 8 bits to send fram_rx_loop: sbi spi_port,spi_sck; set clock hi clc ; clear carry sbic PIND,spi_data ; test input pin sec rol temp cbi spi_port,spi_sck; clear clock dec bitcnt brne fram_rx_loop inc zl fram_init: cbi spi_port,spi_sck; put spi bus into known state fram_stop: sbi spi_port,spi_cs spi_data_out cbi spi_port,spi_data sbrs flags,fram_no_sei; if we were called by isr don't re-enable ints sei cbr flags,(1<<fram_no_sei) ret ;---------------------------------------------------- ;--- internal eeprom routines ;--- read eeprom (post incs EEAR!) EERead: cli sbic EECR,EEWE; if EEWE not clear rjmp EERead ; keep waiting out EEAR,EEaddr sbi EECR,EERE; set EEPROM Read strobe in EEdata,EEDR; get data rjmp inc_eear; bump address and exit ;--- write EEPROM (post incs EEAR!) EEWrite: cli sbic EECR,EEWE; if EEWE not clear rjmp EEWrite ; keep waiting out EEAR,EEaddr out EEDR,EEdata; output the data sbi EECR,EEMWE; set master write enable sbi EECR,EEWE; set EEPROM Write strobe inc_eear: inc EEaddr sei ret ;-------------------------------------------------------------- ;--- copy eeprom strings to fram ;--- if shifting is enabled, left shift before copying ax25_s_string: sbr flags,(1 << ax25_shift); enable data shift before sending ax25 address data ax25_string: mov EEaddr,temp ; move temp to addr next_ax25_char: rcall EERead ; get char from eeprom tst EEdata ; test for eom brne next_ax ; cbr flags,(1 << ax25_shift); clear shift enable flag ret next_ax: mov temp,EEdata ; sbrc flags,ax25_shift; lsl temp ; rcall fram_wr rjmp next_ax25_char ; loop it ;--- send zero terminated strings from eeprom to console ;serial_string: out EEAR,temp ; move temp to addr ;next_ser_char: rcall EERead ; get char from eeprom ; tst EEdata ; test for eom ; brne pc + 2 ; skip nz ; ret ; mov temp,EEdata ; ; rcall putc ; ship it ; rjmp next_ser_char ; loop it ;--- send zero terminated strings from flash to console ; use z for pointer ser_ram_str: lpm ; tst r0 ; brne pc + 2 ; ret mov temp,R0 ; rcall putc adiw zl,1 rjmp ser_ram_str ;********************************************************************* ; codespace stored string storage ;********************************************************************* ;--- message strings---------------- _hello: .db cr,lf,"AVR_UI_TNC 1.7 Copyright Henry Carl Ott N2RVQ",cr,lf,0 _beacon: .db "BEACON=O",0 ;********************************************************************* ; EEPROM ;********************************************************************* .eseg .org 0x00 empty: .db 0x00 ; leave empty, may get corrupted do to possible bug ;--- mycall and dest should be 6 chars followed by ssid (add 30 hex) and then zero terminated ;--- total digi path should be 0 terminated ;--- the program will set the low bit of the final digi .org 0x01 _mycall: .db "NOCALL",0x30,0 ; call .ORG 0X09 _dest: .db "APZAVR",0x30,0 ; dest _digi: .db "RELAY ",0x30 ; digi path .db "WIDE ",0x30,0 ; final digi, zero terminate ;.org 0x3e ;_b_time: .db 0x00 ; beacon time in minutes _ee_flags: .db (1 << echo) ; just various status bits to remember during power down .org 0x40 _b_text: .db "AVR_UI_TNC TEST, Hello from an AVR",0 |