$mod51 $debug mcon equ 0c6h ta equ 0c7h org 0 ; reset interrupt vector sjmp start org 0bh ; timer 0 overflow interrupt vector ajmp timerint org 2bh ; power fail interrupt vector ajmp powerfail org 30h ; message strings crlf: db 0dh,0ah,'$' timer: db 17, 50, 08, 15 ; January db 18, 35, 07, 40 ; February db 19, 15, 06, 50 ; March db 19, 55, 05, 50 ; April db 20, 35, 05, 05 ; May db 21, 00, 04, 45 ; June db 20, 55, 05, 00 ; July db 20, 15, 05, 30 ; August db 19, 15, 06, 15 ; September db 18, 20, 06, 55 ; October db 17, 35, 07, 40 ; November db 17, 25, 08, 10 ; December org 78h ; start of main program code start: mov sp,#17h ; allow use of Rn banks 00, 01, and 10 acall inithw ; initialize hardware setb rs1 ; switch to controller register space mov r5,#3 ; R5:R4 = controller set point mov r4,#232 mov r3,#0fh ; R3:R2 = current HV control value mov r2,#0ffh setb psw.5 ; start with control/transmit enabled clr rs1 ; return to default register space mov r7,#6 ; initialize counter variables used in mov r6,#25 ; the timer interrupt handler main: sjmp main ; continue main loop ; ReadADC. Get 12 bit value from the MAX186 on port 1. R1 selects the ; channel to read (0 to 8), or more specifically, SEL2-SEL0. The most ; significant nybble of conversion is returned in R1 bits 3-0, and the ; least significant byte is returned in R0. Pin assignments: ; P1.0 <-- Dout, P1.1 --> Din, P1.2 --> SCLK, P1.3 --> CS. ; (Operates in currently selected register space). readadc: clr p1.3 ; take CS low to wake up the MAX186 mov a,r1 ; prepare a control byte rl a ; channel select bits need to end up rl a ; in a.6 through a.4 rl a rl a orl a,#10001110b ; complete the control byte mov r0,#8 ; initialize bit counter nbit1: rlc a ; send the control byte, MSB first mov p1.1,c setb p1.2 ; pulse SCLK between bits clr p1.2 djnz r0,nbit1 ; repeat for all eight control bits nop ; wait 10us to insure complete conversion nop nop nop nop nop nop nop nop nop nop nop clr a mov r0,#4 ; initialize bit counter nbit2: setb p1.2 ; shift high nybble of result into acc, clr p1.2 ; pulsing SCLK between bits mov c,p1.0 rlc a djnz r0,nbit2 ; repeat to get four bits mov r1,a ; store the high nybble in R1 mov r0,#8 ; initialize bit counter nbit3: setb p1.2 ; shift lower two nybbles of result into clr p1.2 ; accumulator, pulsing SCLK between bits mov c,p1.0 rlc a djnz r0,nbit3 ; repeat to get eight bits mov r0,a ; store the low byte in R0 setb p1.2 ; shift out the remaining four bits clr p1.2 ; (zeros) required by the MAX186 setb p1.2 clr p1.2 setb p1.2 clr p1.2 setb p1.2 clr p1.2 setb p1.3 ; take CS high ret ; WriteDAC. Send 12 bit value (most significant nybble in R3 bits 3-0, ; least significant byte in R2) to the DAC-8043 on port 1. Pin ; assignments: P1.4 --> SRI, P1.5 --> CLK, P1.6 --> LD. ; (Operates in currently selected register space). writedac: mov b,r3 ; save R3 register (caller needs it) mov a,r3 ; put upper nybble in accumulator rlc a ; prepare to shift out the MSB rlc a rlc a rlc a mov r3,#4 ; initialize bit counter nbit4: rlc a ; shift four bits out to SRI, mov p1.4,c ; providing a clock pulse on CLK setb p1.5 ; between each one clr p1.5 djnz r3,nbit4 ; repeat four times mov a,r2 ; put lower two nybbles in accumulator mov r3,#8 ; initialize bit counter nbit5: rlc a ; shift eight bits out to SRI, mov p1.4,c ; providing a clock pulse on CLK setb p1.5 ; between each one clr p1.5 djnz r3,nbit5 ; repeat eight times clr p1.6 ; pulse LD low to load the DAC register setb p1.6 mov r3,b ; restore R3 register ret ; BinToFloat. Converts a 12 bit value (most significant nybble in R1 bits ; 3-0, least significant byte in R0) into an ASCII floating point string ; of the form wxyz. The ASCII bytes for each digit are returned in R3, ; R2, R1, and R0 (for digits w, x, y, and z, respectively) of the RS=01 ; register space. May only be called from the RS=10 register space. bintofloat: mov a,r0 ; transfer parameters to new Rn space clr rs1 ; and remain there for this subroutine setb rs0 mov r0,a setb rs1 clr rs0 mov a,r1 clr rs1 setb rs0 mov r1,a mov r3,#0ffh mov a,r0 clr c subtra: inc r3 mov b,a subb a,#100 jnc subtra clr c cjne r1,#0,next sjmp skip next: djnz r1,subtra subtra2:mov b,a subb a,#100 inc r3 jnc subtra2 skip: mov a,b mov b,#10 div ab add a,#48 ; convert to ASCII mov r1,a ; R1 now has the 10's place mov a,b add a,#48 ; convert to ASCII mov r0,a ; R0 now has the 1's place mov a,r3 mov b,#10 div ab add a,#48 ; convert to ASCII mov r3,a ; R3 now has the 1000's place mov a,b add a,#48 ; convert to ASCII mov r2,a ; R2 now has the 100's place setb rs1 ; restore controller Rn space clr rs0 ret ; SendFloat. Takes the four ASCII numeric characters returned by BinToFloat ; (R3 . R2 R1 R0 in the RS=01 register space) and sends them to the serial ; port after a leading space character. May only be called from the RS=10 ; register space. sendfloat: clr rs1 ; values are in separate Rn space setb rs0 jnb ti,$ ; send a space character clr ti mov sbuf,#' ' jnb ti,$ ; send ones place clr ti mov sbuf,r3 jnb ti,$ ; send decimal point clr ti mov sbuf,#'.' jnb ti,$ ; send tenths place clr ti mov sbuf,r2 jnb ti,$ ; send hundredths place clr ti mov sbuf,r1 jnb ti,$ ; send thousandths place clr ti mov sbuf,r0 setb rs1 ; restore controller Rn space clr rs0 ret ; SendStr. Sends characters pointed to by DPTR out the serial port until ; a '$' character is encountered. Uses currently selected register space. sendstr: mov a,#0 movc a,@a+dptr cjne a,#'$',sendnext ret sendnext: jnb ti,$ clr ti mov sbuf,a inc dptr sjmp sendstr ; Add16. Adds the 16-bit number in R1:R0 to the 16-bit number in R3:R2, and ; stores the result in R3:R2. Uses the currently selected register space. add16: mov a,r2 add a,r0 mov r2,a mov a,r3 addc a,r1 mov r3,a ret ; Sub16. Subtracts the 16-bit number in R5:R4 from the 16-bit number in ; R1:R0, and stores the result in R1:R0. Uses the currently selected ; register space. sub16: clr c mov a,r0 subb a,r4 mov r0,a mov a,r1 subb a,r5 mov r1,a ret ; DivBy2. Divides the 16-bit number in R1:R0 by two, preserving the sign. ; Uses the currently selected register space. divby2: clr c mov a,r1 anl a,#80h jz positive setb c positive: mov a,r1 rrc a mov r1,a mov a,r0 rrc a mov r0,a ret ; Trunc12. Examines the 16-bit signed integer in R3:R2 and truncates it ; to a 12-bit unsigned integer. Values < 0 are truncated to 0 and ; values > 0xfff are truncated to 0xfff. Uses the currently selected ; register space. trunc12: mov a,r3 rlc a jc isneg ; test sign bit anl a,#0e0h ; check other three bits past 12-bit limit jz isok mov r3,#0fh ; R3:R2 > 0xfff, so truncate to 0xfff mov r2,#0ffh ret isneg: mov r3,#0 ; R3:R2 < 0, so truncate to 0 mov r2,#0 isok: ret ; 0 < R3:R2 < 0xfff, so leave as-is ; InitHW. Initializes the serial port, ADC, DAC, and processor safety ; features at beginning of program execution. inithw: mov r3,#0fh ; send 0xfff to the DAC so we will mov r2,#0ffh ; start with the PMT high voltage acall writedac ; turned off mov ta,#0aah ; timed access mov ta,#55h anl pcon,#0fbh ; disable watchdog timer temporarily mov ie,#82h ; enable only the timer 0 interrupt mov tmod,#21h ; select timer 1 mode 2 (auto reload) ; and timer 0 mode 1 (16 bit) mov th1,#0f0h ; this is correct for 1200 bps with a mov tl1,#0f0h ; 7.3728 MHz crystal mov th0,#10h ; this will generate overflow interrupts mov tl0,#08h ; every 0.1s (10 Hz) mov scon,#52h ; select serial mode 1 mov tcon,#40h ; start up timer 1 only (for serial port) mov r3,#0eh ; allow roughly 3 seconds for the TNC loop2: mov r2,#0ffh ; to power up and arrive at the command loop1: mov r1,#0ffh ; prompt djnz r1,$ djnz r2,loop1 djnz r3,loop2 jnb ti,$ ; put the TNC into transparent mode by clr ti ; sending "tr" mov sbuf,#'t' jnb ti,$ clr ti mov sbuf,#'r' jnb ti,$ clr ti mov sbuf,#0dh mov ta,#0aah ; timed access mov ta,#55h setb ip.7 ; set RWT (reset watchdog timer) mov ta,#0aah ; timed access mov ta,#55h orl pcon,#4ch ; set POR, EWT, and EPFW setb tr0 ; start timer 0 for 10 Hz interrupts acall readadc ; "prime" the analog input channels acall readadc ret ; PowerFail. ISR for the power fail interrupt; sets high voltage to zero ; and takes any other action necessary to prepare for program termination, ; then waits in a loop until Vcc again reaches a safe level or program ; execution is terminated. powerfail: push acc mov r3,#0fh ; turn off high voltage to the PMT mov r2,#0ffh acall writedac danger: acall resetwt ; wait until the danger condition passes mov a,pcon ; or execution is terminated mov a,pcon anl a,#20h jnz danger pop acc reti ; TimerInt. ISR for a periodic 10 Hz interrupt generated by timer 0. ; This code utilizes the RS=10 register space (the "controller space") ; as well as the standard RS=00 space. timerint: clr tr0 ; halt timer mov th0,#10h ; reload for another 0.1s run mov tl0,#08h setb tr0 ; restart timer acall gettime ; fetch date/time information clr rs1 ; go to the date/time register space setb rs0 mov a,r4 ; convert BCD month into decimal month-1 jnb acc.4,notens anl a,#11101111b add a,#9 sjmp testtime notens: clr c subb a,#1 testtime: mov b,#4 ; multiply value by 4 to index into the mul ab ; times array jnb psw.5,testtim2 ; add two if we want to look at a potential add a,#2 ; turnoff (to fetch sunrise times) testtim2: mov r7,a ; save this index value for later uses mov a,r2 ; convert BCD hours into decimal hours anl a,#0f0h rr a rr a rr a rr a mov b,#10 mul ab mov b,r2 anl b,#0fh add a,b mov b,a mov a,r7 mov dptr,#timer ; fetch the hours value from the array, movc a,@a+dptr ; appropriate for the current month cjne a,b,nomatch ; test hour mov a,r1 ; convert BCD minutes into decimal minutes anl a,#0f0h rr a rr a rr a rr a mov b,#10 mul ab mov b,r1 anl b,#0fh add a,b mov b,a mov a,r7 inc a ; fetch the minutes value from the array, movc a,@a+dptr ; appropriate for the current month cjne a,b,nomatch ; test minute cpl psw.5 ; change modes from nighttime (xmit) to ; daytime (standby), or vice-versa jb psw.5,nomatch setb rs1 clr rs0 mov r3,#0fh ; turn off high voltage to the PMT mov r2,#0ffh acall writedac acall resetwt clr rs1 clr rs0 reti nomatch: clr rs1 ; insure that we start out in default clr rs0 ; register space jnb psw.5,short ; do nothing if the "transmit bit" is zero djnz r7,short1 ; return early if not yet time for a control ; action mov r7,#6 ; schedule a control cycle every ~0.6s sjmp ctrl short1: ajmp short ctrl: setb rs1 ; switch to controller register space clr rs0 mov r1,#0 ; read PMT signal acall readadc mov a,r1 ; save PMT value for possible later display mov r7,a mov a,r0 mov r6,a acall sub16 ; subtract set point (R5:R4) from PMT acall divby2 ; level (R1:R0), and divide resultant acall divby2 ; (R1:R0) by eight acall divby2 acall add16 ; add result to current HV control value acall trunc12 ; (R3:R2) and truncate to positive 12-bit ; range limits acall writedac ; take control action acall resetwt ; reset watchdog timer clr rs1 ; switch to default register space clr rs0 djnz r6,short ; return without transmitting a status line ; if it is not yet time mov r6,#25 ; schedule a status run every ~15s mov r7,#3 ; schedule a control cycle sooner than ; usual, since the status transmission ; takes ~0.34s setb rs1 ; switch to controller register space clr rs0 clr tr0 ; this is gonna take >> 0.1 seconds... acall sendtime ; send current time/date to serial port acall resetwt mov a,r3 ; display HV value mov r1,a mov a,r2 mov r0,a acall bintofloat acall sendfloat acall resetwt mov a,r7 ; retrieve and display last PMT value mov r1,a mov a,r6 mov r0,a acall bintofloat acall sendfloat acall resetwt mov r1,#4 ; display PD value acall readadc acall bintofloat acall sendfloat acall resetwt mov r1,#1 ; display temperature value acall readadc acall bintofloat acall sendfloat acall resetwt mov dptr,#crlf ; add CR/LF at end of line acall sendstr acall resetwt clr rs1 ; restore default register space clr rs0 mov th0,#7ch ; these should be tweaked to equalize delays mov tl1,#00h ; depending on the time required for setb tr0 ; transmitting the status line short: acall resetwt ; reset watchdog timer reti ; ResetWT. Resets the watchdog timer on-board the DS5000T processor module. resetwt: mov ta,#0aah ; timed access mov ta,#55h setb ip.7 ; set RWT ret ; GetTime. Reads the DS1215 Real-Time Clock onboard the DS5000T processor ; module. GetTime may only be called from the RS=00 (default) ; register space. gettime: clr rs1 ; switch to a spare set of Rn registers setb rs0 mov mcon,#0f8h ; turn off CE2 for memory access acall open ; set up to read date/time acall rbyte ; read hundredths/tenths of a second mov r6,a acall rbyte ; read ones/tens of seconds mov r0,a acall rbyte ; read minutes mov r1,a acall rbyte ; read hours mov r2,a acall rbyte ; discard day of week acall rbyte ; read date mov r3,a acall rbyte ; read month mov r4,a acall rbyte ; read year mov r5,a acall close ; close clock register clr rs1 ; go back to default register space clr rs0 ret ; SendTime. Sends a concise time/date ASCII string out the serial port, ; given the data in RS=01 from the GetTime routine. SendTime may only ; be called from the RS=10 (controller) register space. sendtime: clr rs1 ; switch to a spare set of Rn registers setb rs0 mov a,r2 ; send first hour digit anl a,#0f0h rr a rr a rr a rr a add a,#30h jnb ti,$ clr ti mov sbuf,a mov a,r2 ; send second hour digit anl a,#0fh add a,#30h jnb ti,$ clr ti mov sbuf,a mov a,r1 ; send first minute digit anl a,#0f0h rr a rr a rr a rr a add a,#30h jnb ti,$ clr ti mov sbuf,a mov a,r1 ; send second minute digit anl a,#0fh add a,#30h jnb ti,$ clr ti mov sbuf,a mov a,r0 ; send first second digit anl a,#0f0h rr a rr a rr a rr a add a,#30h jnb ti,$ clr ti mov sbuf,a mov a,r0 ; send second second digit anl a,#0fh add a,#30h jnb ti,$ clr ti mov sbuf,a jnb ti,$ ; send "." clr ti mov sbuf,#'.' mov a,r6 ; send tenths of a second digit anl a,#0f0h rr a rr a rr a rr a add a,#30h jnb ti,$ clr ti mov sbuf,a jnb ti,$ ; send " " clr ti mov sbuf,#' ' mov a,r4 ; send first month digit anl a,#0f0h rr a rr a rr a rr a add a,#30h jnb ti,$ clr ti mov sbuf,a mov a,r4 ; send second month digit anl a,#0fh add a,#30h jnb ti,$ clr ti mov sbuf,a mov a,r3 ; send first date digit anl a,#0f0h rr a rr a rr a rr a add a,#30h jnb ti,$ clr ti mov sbuf,a mov a,r3 ; send second date digit anl a,#0fh add a,#30h jnb ti,$ clr ti mov sbuf,a mov a,r5 ; send first year digit anl a,#0f0h rr a rr a rr a rr a add a,#30h jnb ti,$ clr ti mov sbuf,a mov a,r5 ; send second year digit anl a,#0fh add a,#30h jnb ti,$ clr ti mov sbuf,a setb rs1 ; go back to controller register space clr rs0 ret ;****************************************** ;**** SUBROUTINE TO OPEN TIMEKEEPER ******* ;****************************************** ; ; This subroutine executes the sequence of reads and writes which ; is required in order to open communication with the timekeeper. ; The subroutine returns with the timekeeper opened for data ; access and with both the accumulator and B register modified. ; OPEN: ACALL CLOSE ; Make sure it is closed. MOV B,#4 ; Set pattern period count. MOV A,#0C5H ; Load first byte of pattern. OPENA: ACALL WBYTE ; Send out the byte. XRL A,#0FFH ; Generate next pattern byte. ACALL WBYTE ; Send out the byte. SWAP A ; Generate next pattern byte. DJNZ B,OPENA ; Repeat until 8 bytes sent. RET ; Return. ;****************************************** ;**** SUBROUTINE TO CLOSE TIMEKEEPER ****** ;****************************************** ; ; This subroutine insures that the registers of the timekeeper ; are closed by executing 9 successive reads of the date and time ; registers. The subroutine returns with both the accumulator ; and the B register modified. ; CLOSE: MOV B,#9 ; Set up to read 9 bytes. CLOSEA: ACALL RBYTE ; Read a byte; DJNZ B,CLOSEA ; Loop for 9 byte reads. RET ; Return. ;****************************************** ;**** SUBROUTINE TO READ A DATA BYTE ****** ;****************************************** ; ; This subroutine performs a "context switch" to the CE2 data ; space and then reads one byte from the timekeeping device. ; Then it switches back to the CE1 data space and returns ; the byte read in the accumulator, with all other registers ; unchanged. ; RBYTE: PUSH DPL ; Save the data PUSH DPH ; pointer on stack. PUSH MCON ; Save MCON register. ORL MCON,#4 ; Switch to CE2. PUSH B ; Save the B register. MOV DPL ,#4 ; Set up for data input. MOV DPH, #0 ; Set high address byte. MOV B, #8 ; Set the bit count. LI: PUSH ACC ; Save the accumulator. MOVX A, @DPTR ; Input the data bit. RLC A ; Move it to carry. POP ACC ; Get the accumulator. RRC A ; Save the data bit. DJNZ B, LI ; Loop for a whole byte. POP B ; Restore the B register. POP MCON ; Restore MCON register. POP DPH ; Restore the data POP DPL ; pointer from stack. RET ; Return. ;****************************************** ;**** SUBROUTINE TO WRITE A DATA BYTE ***** ;****************************************** ; ; This subroutine performs a "context switch" to the CE2 data ; space and then writes one byte from the accumulator to the ; timekeeping device. Then it switches back to the CE1 data ; space and returns with all registers unchanged. ; WBYTE: PUSH DPL ; Save the data PUSH DPH ; pointer on stack. PUSH MCON ; Save MCON register. ORL MCON,#4 ; Switch to CE2. PUSH B ; Save the B register. MOV DPH, #0 ; Set high address byte. MOV B, #8 ; Set the bit count. LO: PUSH ACC ; Save the accumulator. ANL A, #1 ; Set up bit for output. MOV DPL, A ; Set address to write bit. MOVX A, @DPTR ; Output the data bit. POP ACC ; Restore the accumulator. RR A ; Position next bit. DJNZ B, LO ; Loop for a whole byte. POP B ; Restore the B register. POP MCON ; Restore MCON register. POP DPH ; Restore the data POP DPL ; pointer from stack. RET ; Return. end