;************************************************************ ; NJQRP MicroBeacon ; ; MC68HC705B5 Control Processor Program ; ; G.Heron, N2APB ; ; 7/5/98 Initial program: Basic serial, keypad, display ; 7/21/98 Added indexed message display ; 7/23/98 Added keyer logic ; 8/01/98 Beacon pre-programmed playback logic added (for club mtg) ; 8/02/98 Added tupple codes for recording into mbuff. ; Added a One Morse Unit wait loop around the main loop to ; be able to detect and record space ; Tried using IRQ on paddle inputs, but no advantage ; Got buffer record and playback working ; 9/07/98 Cleaned things up for Joe to demo at NorCal meeting ; ;************************************************************ portA equ $0000 ;port A data register portB equ $0001 ;port B data register portC equ $0002 ;port C data register portD equ $0003 ;port D data register ddra equ $0004 ;port A data direction register ddrb equ $0005 ;port B data direction register ddrc equ $0006 ;port C data direction register eclk equ $0007 ;b3=control for external clock on PC2 addata equ $0008 ;A/D data adstat equ $0009 ;A/D status/control plmA equ $000A ;pulse length modulation A plmB equ $000B ;pulse length modulation B misc equ $000C ;POR,INTP,INTN,INTE,SFA,SFB,SM,WDOG brate equ $000D ;SCI baud rate sccr1 equ $000E ;SCI control 1 sccr2 equ $000F ;SCI control 2 scsr equ $0010 ;SCI status scdat equ $0011 ;SCI data tcr equ $0012 ;timer control tsr equ $0013 ;timer status icr1 equ $0014 ;input capture reg 1 cr equ $0D lf equ $0A keypress equ 4 ;/keypad closure bit dit equ 1 ;paddle dit switch (active low input) dah equ 0 ;paddle dah switch (active low input) keyline equ 2 ;keyline output to rig (active low) ; start of 48 byte EPROM area ORG $0020 ; start of user 176 byte RAM area (incl stack at top) ORG $0050 temp: db 1 temp1: db 1 temp2: db 1 rx_data: db 1 pot_value: db 1 keypad: db 1 cntr: db 1 save_acc: db 1 ;generic save acc reg loc savex: db 1 ;generic saveindex reg loc blink_cntr: db 1 ;blink cntr = divide by 5 (blink_band_led) string_nr: db 1 paddlemem: db 1 ;used to catch iambic paddle hit divisor: db 1 ;cnt divisor: 1 (if dit) or 3 (if dah) divideby: db 1 ;working counter in WaitDah record_flag: db 1 ;record flag toggled in toggle_record spacetimerhi: db 1 ;space detection timing in main loop spacetimerlo: db 1 tupple_mask: db 1 ;mask for tupple under test in play_mem bitposn: db 1 ;bit position under tst in morse routines in_progress: db 1 ;flag to indicate when keying ; begins after turning on record mbytecntr: db 1 ;mbuff byte cntr ... relative posn of ; current byte mbuff: db 20 ;morse buffer ; start of 6KB user eprom area ORG $800 ; MAINLINE START: rsp ;reset stack to $00FF jsr initialize ;initialize all ports and devices jsr display_ready ;"Ready" ;-------------------------------------------------------------- ; WAIT_SCAN -- Waits here until SCAN pushbutton is actuated ; or PC command is sent ;-------------------------------------------------------------- wait_scan: jsr crlf lda #">" ;prompt jsr serial_tx ;display ">" ws1: jsr read_pot ;put pot value into pot_value ; Main Loop ... contained within 1 Morse unit ; to determine if space time has elapsed wsp1: lda pot_value ;pot reading = Morse unit length sta spacetimerhi wsp2: lda #$50 sta spacetimerlo ; Check for Host command coming over serial port ws6: lda scsr ;check sci status reg for Rx char present and #$20 ;RDRF? bne cmd_proc ;yes, go process it ; Check for Keypad command ws5: brset keypress,portb,ws1b ;nope, nothing pressed jsr wait1ms jsr read_keypad ;yes, read the keypad brclr keypress,portb,* ;wait for release cmp #$0 ;toggle record? bne ws5a ;no jsr toggle_record bra ws1b ;now go check paddles ws5a: cmp #$01 ;play mem? bne ws5b ;no, continue jsr playmem bra ws1 ;continue at top of timer loop ws5b: cmp #$03 ;play beacon? bne ws1b ;no, continue jsr beacon bra ws1 ;continue at top of timer loop ; Check for active paddle ws1b: brclr dit,portc,ws2 ;dit switch active? brclr dit,paddlemem,ws3 ;dah memory? ws2: jsr dodit ws3: brclr dah,portc,ws4 ;dah switch active? brclr dah,paddlemem,wsp4 ;dah memory? ws4: jsr dodah bra ws1 ;no need to continue space window wsp4: dec spacetimerlo bne ws6 ;space timer not timed out yet dec spacetimerhi bne wsp2 ;still not timed out tst record_flag ;record mode? beq ws1 ;no, just continue in main loop ; Add space code (11) to morse buffer ; (only if in_progress is true, meaning that keying has ; begun) tst in_progress ;have we been paddling already bne wsp5 ;yes, add an inter-character space bra ws1 ;else just continue loop wait ; Add inter-char space code to mbuff wsp5: ldx mbytecntr ;put space code (11) into morse buffer lda mbuff,x ora bitposn ;set lower bit clc rol bitposn ;move bitposn to next higher bit ora bitposn ;cset this bit too sta mbuff,x clc rol bitposn ;at byte boundary? bne ws1 ;no, so continue lda #$01 ;res, resit bitposn sta bitposn inc mbytecntr ;and increment morse buffer pointer bra ws1 ;-------------------------------------------------------- ; PC COMMAND PROCESSOR -- Get command char and process it ;-------------------------------------------------------- cmd_proc: lda scdat ; read the sci data register cmp #$31 bne cpx jmp cmd_1 ; 1 = ; Unrecognized command ... send "?" and wait again cpx: lda #"?" jsr serial_tx ;unrecognized command jmp wait_scan ;------------------------------------------------------------- ; COMMAND 1 -- cmd_1: ;*************************************************************** ;*************************************************************** ; ; S U B R O U T I N E S ; ;*************************************************************** ;*************************************************************** ;************************************************************* ; Beacon -- Send a preset message: ; "CQ CQ de WQ2RP/B W2QRP/B 3W 3W (word) (word) ; successively at 4 speed settings: 10, 20, 30 and 40wpm ; with different code word at each speed cycle beacon: jsr disp_clear lda #5 jsr disp_string ;"Ready" lda #$51 ;10wpm sta pot_value lda #$26 ;20wpm sta pot_value lda #$1b ;30wpm sta pot_value lda #$14 ;40wpm sta pot_value bra beacon ; Plays CQ CQ de WQ2RP/B W2QRP/B 3W 3W beacmain: rts ; Wait 2 Morse Units beac8: lda #2 sta divisor ;init the counter to use in 3x wait wd9: ldx pot_value ;pot reading = Morse unit length wd1: lda divisor ;get the divisor (1 or 3) sta divideby ;and put into working storage wd2: lda #$f3 wd3: brset dit,portc,wd4 nop wd4: deca bne wd3 dec divideby bne wd2 decx bne wd1 rts ;************************************************************* ; CRLF - Send CRLF to the PC crlf: lda #$0A jsr serial_tx lda #$0D jsr serial_tx rts ;************************************************************** ; Display Functions ;------------------------------------------------------------ disp_blink_off: lda #$0e jsr disp_ctl rts ;------------------------------------------------------------ disp_blink_on: lda #$0f jsr disp_ctl rts ;------------------------------------------------------------ disp_ctl: sta temp2 ;temp save data byte jsr disp_wait_ready lda temp2 rora rora rora rora ;get high nibble low and #$0f ora #$40 ;Enable and RS low sta porta bclr 6,porta ;drop Enable lda temp2 and #$0f ora #$40 ;Enable and RS low sta porta bclr 6,porta ;drop Enable rts ;------------------------------------------------------------ disp_data: sta temp2 ;temp save data byte jsr disp_wait_ready lda temp2 rora rora rora rora ;get high nibble low and #$0f ora #$50 ;Enable and RS high sta porta bclr 6,porta ;drop Enable lda temp2 and #$0f ora #$50 ;Enable and RS high sta porta bclr 6,porta ;drop Enable rts ;------------------------------------------------------------ disp_initialize: lda #3 sta cntr lda #0 sta porta jsr wait50ms ; do function set 3 times lda #$03 ;function set sta porta id1: bset 6,porta ;set Enable bclr 6,porta ;drop Enable jsr wait10ms dec cntr bne id1 lda #$42 ;function set to 4 bits (& Enable) sta porta bclr 6,porta ;drop Enable jsr wait10ms ; initialization write routine lda #$28 ;system set, 4 bit jsr disp_ctl lda #$0e ;display and cursor off jsr disp_ctl lda #$01 ;clear display & home cursor jsr disp_ctl lda #$02 jsr disp_ctl ;cursor home lda #$06 ;entry mode: no shift jsr disp_ctl rts ;------------------------------------------------------------ disp_clear: lda #$01 jsr disp_ctl jsr wait1ms rts ;------------------------------------------------------------ disp_set_posn: jsr disp_ctl rts ;------------------------------------------------------------ disp_set_to_left: lda #$80 jsr disp_ctl rts ;------------------------------------------------------------ disp_set_to_right: lda #$c0 jsr disp_ctl rts ;------------------------------------------------------------ ; Disp_String - Displays string # pointed to by Acc disp_string: clrx sta string_nr ;first find the start of the desired string ds3: dec string_nr beq ds1 ;desired string found. Go display it ds4: lda mstart,x beq ds4a ;end of string found incx bra ds4 ;keep looking for end of string ds4a: incx bra ds3 ;and now display the desired string ds1: lda mstart,x beq ds2 ;zero ends the string jsr disp_data incx bra ds1 ds2: rts ;------------------------------------------------------------ disp_wait_ready: jsr wait1ms rts ;**************************************************************** ; Display Ready -- Displays "Ready" to the LCD display_ready: jsr disp_clear lda #5 jsr disp_string rts ;**************************************************************** ; Do Dit -- Activate keyline, ; wait one dit time, ; (and set dah bit in paddlemem if dah switch closed) ; deactivate keyline, ; wait another dit time ; (and set dah bit in paddlemem if dah switch closed) dodit: stx savex inc in_progress ;set to TRUE to indicate paddles started tst record_flag ;are we recording? beq ddit2 ;no jsr add_dit ;yes, add dit to buffer ddit2: bclr dit,paddlemem bclr keyline,portc jsr wdit1 bset keyline,portc jsr wdit1 ldx savex rts wdit1: ldx pot_value ;pot reading = Morse unit length wdit2: lda #$f3 wdit3: brset dah,portc,wdit4 ;dah paddle not closed bset dah,paddlemem ;dah sw closed, set mem bit wdit4: deca bne wdit3 decx bne wdit2 rts ; Add dit to memory = Put 01 into morse buffer add_dit: ldx mbytecntr lda mbuff,x ora bitposn ;put 1 in lower bit of tupple clc rol bitposn ;move bitposn to next higher bit com bitposn and bitposn ;put 0 in upper bit of tupple sta mbuff,x com bitposn ;put bitposn back to normal clc rol bitposn bne adit3 ;all done if not 0 lda #$01 sta bitposn ;else re-init back to LSbit inc mbytecntr ;and bump morse byte cntr adit3: rts ;**************************************************************** ; Do Dah -- Activate keyline, ; wait one dah time, ; (and set dit bit in paddlemem if dit switch closed) ; deactivate keyline, ; wait another dah time. ; (and set dit bit in paddlemem if dit switch closed) dodah: stx savex inc in_progress ;set to TRUE to indicate paddles starting tst record_flag ;are we recording? beq ddah2 ;no jsr add_dah ;yes, add dah to buffer ddah2: bclr dah,paddlemem bclr keyline,portc jsr wdah8 ;wait 3 morse unit lengths bset keyline,portc jsr wdah7 ;wait 1 morse unit length ldx savex rts ; Wait either 1 or 3 morse unit lengths wdah7: lda #1 sta divisor ;init the counter to use in 1x wait bra wdah9 wdah8: lda #3 sta divisor ;init the counter to use in 3x wait wdah9: ldx pot_value ;pot reading = Morse unit length wdah1: lda divisor ;get the divisor (1 or 3) sta divideby ;and put into working storage wdah2: lda #$f3 wdah3: brset dit,portc,wdah4 bset dit,paddlemem ;set memory bit if dit switch closed wdah4: deca bne wdah3 dec divideby bne wdah2 decx bne wdah1 rts ; Add dah to memory = Put 10 into morse buffer add_dah: ldx mbytecntr lda mbuff,x com bitposn and bitposn ;put 0 in lower bit of tupple com bitposn clc rol bitposn ;move bitposn to next higher bit ora bitposn ;put 1 in upper bit of tupple sta mbuff,x clc rol bitposn bne adah3 ;all done if not 0 lda #$01 sta bitposn ;else re-init back to LSbit inc mbytecntr ;and bump morse byte cntr adah3: rts ;*********************************************************** ; Get Serial Char - waits here for a character to be entered ; from the host on the serial line. Char ; returned in reg A get_ser_char: lda scsr ;Rx char present? and #$20 ;RDRF? beq get_ser_char ;nope, keep waiting lda scdat rts ;*********************************************************** ; Initialize - Initialize all ports & devices initialize: lda #$FF ;port A as outputs sta ddra lda #0 sta ddrb ;port B as inputs lda #$04 sta ddrc ;port C as inputs (except b2) bset keyline,portc ;turn off keyer bset 5,adstat ;turn on A/D converter bclr 6,adstat ;turn off ADRC for cpu clk for A/D ; init the SCI serial port lda #$C0 sta brate ;set baud rate to 9600 @ 4MHz crystal lda #0 ;set up SCCR1 sta sccr1 lda #$2C ;set up SCCR2: enable RIE interrupt ; and Tx and Rx enables sta sccr2 jsr disp_initialize lda #0 sta paddlemem ;init paddle memory locs sta in_progress ;flag to indicate when paddles first start ldx #$0a ;clear morse buffer init3: clr mbuff,x decx bne init3 clr mbuff,x ;clear the first element too sta record_flag lda #0 sta mbytecntr ;zero the morse buff offset byte pointer lda #$01 sta bitposn ;init the bit posn (used w/ mbytecntr) lda #$03 sta tupple_mask ; Enable interrupts for negative edge (paddle actuations) ; bclr 6,misc ;clear INTP ; bset 5,misc ;set INTN ; cli ;clear interrupt mask to ENABLE IRQ rts ;************************************************************** ; IRQ Handler - IRQ interrupt vector to here upon either paddle ; being hit. IRQhandler: cli rti ;************************************************************** ; Play Mem -- Plays the contents of the Morse buffer. ; Accessed through Keypad "1" command. playmem: jsr disp_clear lda #4 jsr disp_string ;"playing" ldx #0 ;init X for pointer through mbuff pm1: lda #3 sta tupple_mask ;init tupple mask to start at LSbits pm2: lda mbuff,x ;get a byte from morse buffer and tupple_mask beq pm_end ;if tupple=00, then all done jsr play_tupple ;else play the tupple clc rol tupple_mask ;shift 2-bit mask 2 spots left clc rol tupple_mask bne pm2 ;if not out-of-byte, go do again incx ; after advancing byte pointer bra pm1 ; All done playing memory pm_end: jsr display_ready ;"ready" rts ; Play the tupple: dit(01), dah(10), or space (11) play_tupple: sta save_acc ;save the tupple lda tupple_mask cmp #$03 beq play03 cmp #$0c beq play0C cmp #$30 beq play30 ; Play Tupple position C0 playC0: lda save_acc ;get back the tupple cmp #$40 ;=01 (dit code)? beq play_dit cmp #$80 ;=10 (dah code)? beq play_dah bra play_space ;must be space code then ; Play Tupple position 30 play30: lda save_acc ;get back the tupple cmp #$10 ;=01 (dit code)? beq play_dit cmp #$20 ;=10 (dah code)? beq play_dah bra play_space ;must be space code then ; Play Tupple position 0C play0C: lda save_acc ;get back the tupple cmp #$04 ;=01 (dit code)? beq play_dit cmp #$08 ;=10 (dah code)? beq play_dah bra play_space ;must be space code then ; Play Tupple position 03 play03: lda save_acc ;get back the tupple cmp #$01 ;=01 (dit code)? beq play_dit cmp #$02 ;=10 (dah code)? beq play_dah bra play_space ;must be space code then ; Play Dit play_dit: stx savex bclr keyline,portc jsr w1mu ;wait 1 morse unit length bset keyline,portc jsr w1mu ;wait 1 morse unit length ldx savex rts ; Play Dah play_dah: stx savex bclr keyline,portc jsr w3mu ;wait 3 morse unit lengths bset keyline,portc jsr w1mu ;wait 1 morse unit length ldx savex rts ; Play Space (Inter-word space = 2 Morse units + previous char's 1) play_space: stx savex jsr w2mu ;wait 2 morse unit lengths ldx savex rts ; Wait either 1, 2 or 3 morse unit lengths w3mu: lda #3 sta divisor ;init the cntr to use in 3x wait bra waitmorseunits w2mu: lda #2 sta divisor ;init the cntr to use in 2x wait bra waitmorseunits w1mu: lda #1 sta divisor ;init the cntr to use in 1x wait waitmorseunits: ldx pot_value ;pot reading = Morse unit length wmu1: lda divisor ;get the divisor (1, 2 or 3) sta divideby ;and put into working storage wmu2: lda #$f3 wmu3: brset dit,portc,wmu4 nop wmu4: deca bne wmu3 dec divideby bne wmu2 decx bne wmu1 rts ;************************************************************** ; Read_Keypad -- Reads the keypad from portB into KEYPAD: read_keypad: lda portb coma and #$0f sta keypad rts ;************************************************************* ; Read Pot -- Get 00-FF reading from ADC #0 read_pot: lda #$20 ;select A/D chan 0 sta adstat clrx ; wait 1ms for A/D stabilize ga2: decx bne ga2 adwait: lda adstat bit #$80 beq adwait ;wait for conversion complete (COCO) lda addata ;read the A/D sta pot_value rts ;************************************************************** ; Send_Byte -- Send the hex values of the byte in Acc to serial port send_byte: sta save_acc lsra ;shift upper nibble down lsra lsra lsra jsr send_nibble lda save_acc jsr send_nibble rts send_nibble: and #$0F ;isolate lower nibble add #$30 cmp #$3A bmi sn2 add #7 sn2: jsr serial_tx ;display it rts ;************************************************************** ; Serial_Rx -- Receive character from serial port serial_rx: brclr 5,SCSR,serial_rx ;RDRF = 1 ? lda SCDAT ;OK, get the data rts ;************************************************************** ; Serial_Tx -- Send character to the serial port, but ... serial_tx: brclr 7,SCSR,* ;TDRE = 1 ? sta SCDAT ;OK, send the data rts ;************************************************** ; Space -- Send an ascii space to the PC space: lda #$20 jsr serial_tx rts ;*************************************************************** ; Toggle Record -- Starts/stops recording toggle_record: com record_flag beq tr1 ;not recording now ; Start Recording lda #0 sta in_progress ;start waiting for paddles to be active sta mbytecntr ;reset relative pointer in mbuff lda #1 sta bitposn ;and reset bit position template jsr disp_clear lda #3 jsr disp_string ;"Record" rts ; Stop Recording tr1: jsr display_ready ;"Ready" lda #0 sta in_progress ;clear paddle in progress flag ldx mbytecntr ;put EOB = 00 into morse buffer lda mbuff,x com bitposn and bitposn ;clear this bit com bitposn clc rol bitposn ;move bitposn to next higher bit com bitposn and bitposn ;clear this bit too sta mbuff,x com bitposn clc rol bitposn ;at byte boundary? bne tr2 ;no, so continue lda #$01 ;res, resit bitposn sta bitposn inc mbytecntr ;and increment morse buffer pointer tr2: rts ;*************************************************************** ; Wait500ms-- Waits here for 500ms wait500ms: lda #$0a w500a: jsr wait50ms deca bne w500a rts ;*************************************************************** ; Wait100ms-- Waits here for 100ms wait100ms: lda #$02 w100a: jsr wait50ms deca bne w100a rts ;*************************************************************** ; Wait50ms-- Waits here for 50ms wait50ms: sta temp1 ldx #$33 w50a: jsr wait1ms decx bne w50a lda temp1 rts ;*************************************************************** ; Wait10ms-- Waits here for 10ms wait10ms: sta temp1 ldx #$1e w10a: jsr wait1ms decx bne w10a lda temp1 rts ;*************************************************************** ; Wait1ms -- Waits here for 1ms wait1ms: lda #$f3 w1a: deca nop bne w1a rts ;**************************************************************** ;**************************************************************** ; T A B L E S ;**************************************************************** ;**************************************************************** ;**************************************************************** ; M E S S A G E S mstart: db 'WQ2RP/B',0 db 'N2APB',0 db 'Record',0 db 'Playing',0 db 'Ready',0 ;**************************************************************** ; Interrupt Service Routines -- (Interrupts not currently used) SCI: jmp * TOVFLW: jmp * TOCMPR: jmp * TICAPT: jmp * IRQ: jmp IRQhandler SWI: jmp * ;*********************************************************** ORG $1FF2 ; set reset vectors dw SCI ; $1FF2-3 dw TOVFLW ; $1FF4-5 dw TOCMPR ; $1FF6-7 dw TICAPT ; $1FF8-9 dw IRQ ; $1FFA-B dw SWI ; $1FFC-D dw START ; $1FFE-F