;*************************************************************************
;
; Project : RDD (REMOTE DATA DISPLAY)
;
; Title : RDD2.SRC
;
; Registry No. :
;
; Version Edit author Date Change description
; ------- ----------- ----- ---------------------
; A G.HOPKINS 07/06/99 Initial development
; B G.HOPKINS 28/09/99 Reply anomaly correction
;***********************************************************************
;
; SOFTWARE FOR REMOTE DATA DISPLAY USING AT89C2051
;
;***********************************************************************
;
;
;-----------------------------------------------------------------------
;
; CONSTANTS
;
$mod51
stack EQU 0000FH ; start of stack 
mess_store_origin EQU 00030H ; start of o/p message store
data_store EQU 00038H ; start of data
fl_rate EQU 0004CH ; flash rate register
flc EQU 0004BH ; flash counter
del_count1 EQU 0004AH ; delay counter msb
del_count0 EQU 00049H ; delay counter lsb
TOH EQU 00048H ; timeout counter hi
TOL EQU 00047H ; timeout counter lo
mess_length EQU 00046H ; current message length
rx_byte_store EQU 00045H ; rx byte store
rx_chk_acc EQU 00044H ; rx checksum accumulator
rx_byte_count EQU 00043H ; rx byte counter
mess_type EQU 00041H ; message type store
tx_chk_acc EQU 00041H ; tx checksum accumulator
addr_store EQU 00040H ; address store
disp_done EQU 036H ; indicates display has been updated
dp EQU 037H ; decimal point
new_data EQU 038H ; new data is being displayed
flash EQU 039H ; flash flag
reply_ack EQU 03AH ; acknowledge flag (1=acknowledge)
char_received EQU 03BH ; indicates a character has arrived
message_incoming EQU 03CH ; indicates a message is arriving 
length_byte_flag EQU 03DH ; indicates byte is length
new_rx_mess EQU 03EH ; indicates new message for processing
delay_over_flag EQU 03FH ; indicates delay has finished
baud_rate EQU 0FDH ; reload value to give
; baud rate for timer 1 in mode 3
; =(2^PCON.7)*Osc/(384*(256-baud_rate))
; Osc=11.0592E6, baud=9600, PCON.7=0.
;
;-----------------------------------------------------------------------------
;
; I/O port usage
;
; P1.0 lsb |
; P1.1 |
; P1.2 | slave 
; P1.3 | address 
; P1.4 | select 
; P1.5 | lines 
; P1.6 | 
; P1.7 msb | 
;
; P3.0 RXD
; P3.1 TXD
; P3.2 Tx/Rx Control
; P3.3 display /data enable 
; P3.4 display data
; P3.5 display clock
; P3.7 acknowledge input 
;
;-----------------------------------------------------------------------------
;
ORG 00000H ;reset vector
JMP init ;goto start of program
ORG 0000BH ;T0 vector
CALL t0_isr
RETI
ORG 00023H ;serial interrupts
CALL serial_isr ;RI and TI interrupts
RETI ; 

;------------------------------------------------------------------------------
;
init:
MOV P1,#0FFH ;initialise port 1, all input
MOV P3,#0CBH ;initialise port 3, clk, data, tx/rx low 
ANL PSW,#0E7H ;select reg bank 0
MOV SP,#stack ;stack origin
CLR EA ;disable interrupts
CLR TR0 ;Timer 0 off
CLR TR1 ;Timer 1 off
CLR delay_over_flag ;reset delay flag
MOV fl_rate,#008H ;initial flash rate ~1Hz
MOV TMOD,#021H ;Timer 0 set to 16 bit for delay
;Timer 1 set to 8 bit auto for baud
start_serial_link:
MOV SCON,#11110000B ;Run serial port in mode 3 (7,6)
;Turn on multiproc. Feature (5)
;Enable data reception (4)
;Set 9th bit to space parity (3)
;(2,1,0) are RB8,TI,RI and are
;controlled by hardware
ANL PCON,#000H ;NOT Double baud rate for UART (7)
MOV TH0,#000H ;set up timer 0 to give delay of
MOV TL0,#000H ;~100ms
MOV TH1,#baud_rate ;set up timer 1 to give baud rate of
MOV TL1,#baud_rate ;9600
SETB TR1 ;start baud rate generator timer 1.
MOV IP,#00000010B ;Not used (7,6,5)
;Give serial port normal priority (4)
;Give T0 high priority (1)
;Give low priority to other
;potential sources (3,2,0)
SETB EA ;enable interrupts
SETB ET0 ;allow T0 interrupts first
CALL clear_disp ;ensure correct start-up
CALL self_test ;self test routine
SETB ES ;now allow serial interrupts
SETB SM2 ;ready for address
main:
JNB new_data,det_mess ;if no new data displayed, detect message
JB P3.7,det_mess ;carry on if button not pressed
SETB reply_ack ;reply flag
CLR flash ;inhibit flashing
CLR new_data ;until next new data arrives
det_mess:
JB message_incoming,timeout_check ;timeout if message duff
JBC new_rx_mess,interpret ;interpret completed message
JMP main ;loop if there is no command to do 

timeout_check:
MOV TOH,#03CH ;approx 100ms timeout
MOV TOL,#022H ;for each character
next_to: 
JB char_received,no_problem ;if char arrives in time
DJNZ TOL,next_to ;small wait
DJNZ TOH,next_to ;next large wait
timeout:
CLR message_incoming ;forget it and start again
SETB SM2 ;ready for address 
no_problem:
CLR char_received ;clear flag
JMP main

interpret:
MOV A,mess_type ;display uses type 6, readback uses type 5
intp_5:
CJNE A,#005H,intp_6 ;jump if not type 5
CALL type_5
JMP main ;just in case
intp_6:
CJNE A,#006H,end_intp ;jump if not type 6
CALL type_6
end_intp:
JMP main

type_5: ;type 5 messages read back switch data
CALL tx_over_rs485 ;return acknowledge data
CLR A ;reset A after message interpretation
RET

type_6: ;type 6 messages contain display data
CALL led_display ;update display
SETB new_data ;expect acknowledge after display
CLR C ;clear carry ready
MOV A,fl_rate ;get current flash rate
RRC A ;halve flash period
JNZ not_flash_lim ;flash limit reached if zero
RLC A ;minimum flash period of 1
not_flash_lim:
MOV fl_rate,A ;put rate back
JB flash, st_fl ;faster rate if already flashing
MOV fl_rate,#008H ;eight lots of 100ms normally
st_fl:
MOV flc,fl_rate ;load flash counter
SETB flash ;start flashing dp's
SETB TR0 ;start flashing timer
CLR A ;reset A after message interpretation
RET

clear_disp:
CLR P3.5
CLR P3.4
CLR P3.3
MOV R2,#002H ;do it twice
next_clr_disp:
MOV R1,#028H ;segs to do
SETB P3.4 ;start bit
NOP
SETB P3.5 ;clock
NOP
CLR P3.5
NOP
CLR P3.4 ;all data zero
NOP
next_clr_seg:
SETB P3.5 ;clock
NOP
CLR P3.5
NOP
DJNZ R1,next_clr_seg ;do all segs
DJNZ R2,next_clr_disp ;clear twice
SETB P1.0 ;buzzer off
RET 

led_display:
CLR EA ;disable interrupts during display
CLR P3.3 ;enable data
MOV DPTR,#char_segs ;point to look-up table
MOV R0,#data_store ;point to segmented stores
MOV R2,#004H ;4 chars to do
SETB P3.4 ;start bit
NOP
SETB P3.5 ;sr clock high
NOP
CLR P3.5 ;sr clock low
NOP
next_disp_char:
MOV A,@R0 ;get ascii char
CLR C
SUBB A,#020H ;<space> is lowest valid char
MOVC A,@A+DPTR ;get segmented version
MOV C,dp ;get dp setting
MOV ACC.0,C ;dp into A
MOV R1,#008H ;8 segs to do
next_seg:
RLC A ;segment into C
MOV P3.4,C ;C to display
NOP
SETB P3.5 ;sr clock high
NOP
CLR P3.5 ;sr clock low
NOP
DJNZ R1,next_seg ;do all segs
INC R0 ;point to next segmented char
DJNZ R2,next_disp_char ;do next display character
MOV A,@R0 ;get external LEDs
ANL A,#0C0H ;ensure rest is zero
MOV R1,#006H ;6 more to do including 3 spare
next_seg2:
RLC A ;LED into C
MOV P3.4,C ;C to display
NOP
SETB P3.5 ;sr clock high
NOP
CLR P3.5 ;sr clock low
NOP
DJNZ R1,next_seg2 ;do all segs
SETB EA ;re-enable interrupts
RET

tx_over_rs485: ;having entered ip data, transmit over rs485
CLR ET0 ;disable timer interrupts
SETB P3.2 ;enable tx mode & 'TX' LED on
CALL delay_500us ;500us delay for buffer to settle
CLR A ;zero
MOV C,P3.7 ;get switch reply - 0 = ack, 1 = no ack yet 
RLC A ;switch into A.0
MOV SBUF,A ;put data into output buffer
JNB TI,$ ;wait here until byte has been sent
CLR TI
MOV SBUF,A ;put checksum data into output buffer, same as byte 1
JNB TI,$ ;wait here until byte has been sent
CLR TI
CALL delay_50us
CLR P3.2 ;revert to rx mode
CLR reply_ack ;when we have replied
SETB ET0 ;re-enable timer interrupts
RET

delay_50us:
MOV del_count1,#001H ;all incremented by one
MOV del_count0,#010H ;for djnz
CALL delay
RET

delay_500us:
MOV del_count1,#001H ;all incremented by one 
MOV del_count0,#09AH ;for djnz 
CALL delay
RET

delay: ;553/256 us per count 
DJNZ del_count0,delay ;small delay
DJNZ del_count1,delay ;medium delay
RET

get_addr:
MOV A,P1 ;read board address set
SWAP A ;on P1.4 to P1.7
ANL A,#00FH ;put into lsn and mask
MOV addr_store,A ;store address read from switches
RET

t0_isr:
JNB flash,stop_flash ;stop flashing if acknowledge detected
DJNZ flc,next_t0
CPL dp
CPL P1.0 ;toggle buzzer
CALL led_display ;toggle decimal points
MOV flc,fl_rate ;reload from flash rate register
SETB disp_done ;indicate display has been updated
next_t0:
RET ;carry on
stop_flash:
CLR TR0 ;stop timer
CLR dp
SETB P1.0 ;buzzer off
CALL led_display ;decimal points off
RET

self_test:
MOV flc,fl_rate ;ready for flashing
CLR disp_done ;not done yet
SETB flash ;allow flashing
SETB TR0 ;set flash timer running
SETB RS0 ;select RB1
MOV R2,#000H ;start point in sequence
MOV R4,#01AH ;26 chars to scroll
st_loop2: ;get next 4
MOV DPTR,#st_data ;point to self test character store
MOV R3,#004H ;4 chars to load each time
MOV R0,#data_store ;place to put characters
MOV R1,00AH ;MOV R1,R2 equivalent
st_loop1: ;get next char
MOV A,R1 ;load offset
MOVC A,@A+DPTR
MOV @R0,A ;put char in store
INC R0
INC R1
DJNZ R3,st_loop1 ;do all 4 chars
CLR RS0 ;revert to RB0
wait_st: ;expect flash timer when disp done
JBC disp_done,cont_st ;if disp is done, continue self test sequence
JMP wait_st ;otherwise wait here
cont_st:
SETB RS0 ;back to RB1 for next load
INC R2 ;point to next start char
DJNZ R4,st_loop2 ;scroll 26 times
CALL get_addr ;get board address into A
ADD A,#030H ;convert to ascii 0 to 9
CJNE A,#039H,not_9 ;compare addr with 9, C set if A<9
JMP not_hex 
not_9:
JC not_hex
ADD A,#007H ;extra offset for A to F
not_hex:
DEC R0 ;back one after inc
MOV @R0,A ;store board address in last digit
CLR RS0 ;back to RB1 after address put on display
; JB P3.7,$ ;wait here for button push
MOV TOH,#0FFH ;viewing delay 
MOV TOL,#0FFH 
next_vd: 
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
DJNZ TOL,next_vd ;small wait
DJNZ TOH,next_vd ;next large wait
CALL clear_disp ;clear after test sequence
CLR TR0 ;stop flash timer
CLR flash ;disable flashing
SETB P1.0 ;buzzer off
RET

st_data:
DB ' '
DB ' '
DB ' '
DB ' '
DB 'S'
DB 'E'
DB 'L'
DB 'F'
DB ' '
DB 't'
DB 'E'
DB 'S'
DB 't'
DB ' '
DB '0'
DB '1'
DB '2'
DB '3'
DB '4'
DB '5'
DB '6'
DB '7'
DB '8'
DB '9'
DB ' '
DB 'A'
DB 'd'
DB 'd'
DB ' '

;---------------------------------------------------------------------------
; SERIAL PORT INTERRUPT HANDLER

serial_isr:
JB RI,test_for_addr ;jump if an RI interrupt (byte recd)
RET ;return if a TI interrupt

test_for_addr:
PUSH ACC ;store acc
PUSH PSW ;store psw
SETB RS0 ;select RB1
SETB char_received ;set flag
MOV rx_byte_store,SBUF ;store rx data
JNB RB8,test_for_length ;receive data if not an address

test_addr:
CALL get_addr ;read address
MOV A,rx_byte_store ;get rx byte
MOV B,#010H ;divide A by 16,
DIV AB ;A=integer (addr), B=remainder (m type)
JZ no_quit ;accept anything sent to address zero
CJNE A,addr_store,quit_rx_int;return if wrong address
no_quit:
MOV mess_type,B ;store message type no. 
CLR SM2 ;prepare to receive data, not addr
SETB length_byte_flag ;indicate next byte is mess length
SETB message_incoming ;indicate message being received
CLR A
MOV rx_chk_acc,A ;clear rx checksum accumulator
MOV rx_byte_count,A ;clear rx byte counter
JMP quit_rx_int ;return

test_for_length:
MOV A,rx_byte_store ;get received byte
JNB length_byte_flag,rec_data;jump if actual data
MOV mess_length,A ;store length of this message
CLR length_byte_flag ;indicate actual data
check_mess_type: ;determine where message is to be stored
MOV A,#006H ;if message is direct echo, store in
CJNE A,mess_type,not_6 ;same place as input store
MOV R1,#data_store ;load R1 with display data origin
JMP quit_rx_int ;return
not_6:
MOV R1,#mess_store_origin ;load R1 with tx mess origin
JMP quit_rx_int ;return
rec_data:
INC rx_byte_count ;byte counter
MOV A,rx_byte_count ;get byte count
CJNE A,mess_length,store_rx_data ;test for last byte
last_rx_byte:
MOV A,rx_chk_acc ;get calculated checksum
CJNE A,rx_byte_store,rx_re_init ;jump if invalid checksum
DEC mess_length ;leave off checksum for processing
SETB new_rx_mess ;indicate valid message if checksum ok
rx_re_init:
SETB SM2 ;ready for next address byte
CLR message_incoming ;ready for next message
JMP quit_rx_int ;exit
store_rx_data: 
MOV A,rx_byte_store ;get received data
MOV @R1,A ;store data in buffer
XRL A,rx_chk_acc ;calculate checksum
MOV rx_chk_acc,A ;store new checksum
INC R1 ;next store position
quit_rx_int:
POP PSW
POP ACC
CLR RI ;Acknowledge interrupt handled.
RET ;Return to interrupt vector.

char_segs:
DB 00000000B ;<space> 32
DB 0
DB 01000100B ;" 34
DB 0
DB 0
DB 0
DB 0
DB 01000000B ;' 39
DB 10011100B ;( 40
DB 11110000B ;) 41
DB 0
DB 0
DB 00000100B ;` 44
DB 00000010B ;- 45
DB 0
DB 0
DB 11111100B ;0 48
DB 01100000B ;1 49
DB 11011010B ;2 50
DB 11110010B ;3 51
DB 01100110B ;4 52
DB 10110110B ;5 53
DB 10111110B ;6 54
DB 11100000B ;7 55
DB 11111110B ;8 56
DB 11110110B ;9 57
DB 0
DB 0
DB 0
DB 0
DB 0
DB 11001010B ;? 63
DB 0
DB 11101110B ;A 65
DB 11111110B ;B 66
DB 10011100B ;C 67
DB 11111100B ;D 68
DB 10011110B ;E 69
DB 10001110B ;F 70
DB 10111100B ;G 71
DB 01101110B ;H 72
DB 01100000B ;I 73
DB 11111000B ;J 74
DB 0
DB 00011100B ;L 76
DB 0
DB 0
DB 11111100B ;O 79
DB 11001110B ;P 80
DB 0
DB 0
DB 10110110B ;S 83
DB 0
DB 01111100B ;U 85
DB 0
DB 0
DB 0
DB 0
DB 0
DB 10011101B ;[ 91
DB 0
DB 11110000B ;] 93
DB 0
DB 00010000B ;_ 95
DB 00000100B ;` 96
DB 11111010B ;a 97
DB 00111110B ;b 98
DB 00011010B ;c 99
DB 01111010B ;d 100
DB 11011110B ;e 101
DB 10001110B ;f 102
DB 11110110B ;g 103
DB 00101110B ;h 104
DB 01000000B ;i 105
DB 01111000B ;j 106
DB 0
DB 01100000B ;l 108
DB 0
DB 00101010B ;n 110
DB 00111010B ;o 111
DB 11001110B ;p 112
DB 0
DB 00001010B ;r 114
DB 10110110B ;s 115
DB 00011110B ;t 116
DB 00111000B ;u 117
DB 0
DB 0
DB 0
DB 01110110B ;y 121
DB 0


END

