1 title "PIC16F870 Unilink Interface by Werner Johansson (c) 2003"
\r
2 subtitl "Definitions"
\r
3 list c=132,P=16F870,R=DEC,F=inhx8m
\r
4 include "p16f870.inc" ; Standard equates & Macros
\r
5 ERRORLEVEL 1,-302 ; Get rid of those annoying 302 msgs!
\r
8 ;----------------------------------------------------------------
\r
9 ; The Configuration Word
\r
10 __CONFIG _HS_OSC&_WDT_OFF&_PWRTE_ON&_BODEN_ON&_LVP_OFF&_CPD_OFF&_WRT_ENABLE_ON&_DEBUG_OFF&_CP_OFF
\r
12 ;----------------------------------------------------------------
\r
14 ;----------------------------------------------------------------
\r
17 ; 0.0 Very first "Fucking No Work!" version
\r
19 ;----------------------------------------------------------------
\r
21 ; Unilink BUSON IN (blue) connected to RC2/CCP1
\r
22 ; Unilink DATA (green) connected to RC3
\r
23 ; Unilink BUSON OUT (blue) connected to RC4 (this is for daisy-chaining)
\r
24 ; Unilink CLK (yellow) connected to RB0/INT (Interrupt pin)
\r
25 ; Unilink RST (lilac) connected to RA4
\r
26 ; LCD RS connected to pin RB1
\r
27 ; LCD RW connected to pin RB2
\r
28 ; LCD E connected to pin RB3
\r
29 ; LCD DB4-DB7 connected to RB4-RB7
\r
30 ; RS-232 TX from computer connected to RC7/RX
\r
31 ; RS-232 RX to computer connected to RC6/TX
\r
32 ; RS-232 RI to computer connected to RC5
\r
34 ; This leaves RC0, RC1 and the analog inputs (AN0-AN4) free for now...
\r
36 #define LCD_RS_BIT PORTB,1
\r
37 #define LCD_RW_BIT PORTB,2
\r
38 #define LCD_E_BIT PORTB,3
\r
39 #define LCD_DB4_BIT PORTB,4
\r
40 #define LCD_DB5_BIT PORTB,5
\r
41 #define LCD_DB6_BIT PORTB,6
\r
42 #define LCD_DB7_BIT PORTB,7
\r
44 ;----------------------------------------------------------------
\r
45 ; File register usage
\r
49 Icount equ 2Dh ; Offset of string to print
\r
50 TxTemp equ 2Eh ; blahblah
\r
51 TxTemp2 equ 2Fh ; Blahblah2
\r
66 ;----------------------------------------------------------------
\r
67 ; Power up/Reset starting point [den rulerar]
\r
70 call Bootstrap ; Call Flash Load routine
\r
71 call LCDInit ; Initialize LCD I/F
\r
72 call IRQInit ; Set up and start the IRQ handler
\r
73 goto Main ; Run the main program loop (skip the IRQ handler)
\r
75 subtitl "IRQ Handler"
\r
76 ;----------------------------------------------------------------
\r
77 ; Interrupt handler always starts at addr 4
\r
79 org 4 ; Must be on Address 4!
\r
81 swapf STATUS,w ; Get the status register into w
\r
82 clrf STATUS ; Zero out the status reg, gives us Bank0 all the time
\r
88 btfss INTCON,INTF ; Check if it's INT (CLK)
\r
89 goto IRQNotINT ; Nope
\r
91 movlw 8 ; Loop this many times
\r
95 btfsc PORTA,4 ; Check for RST
\r
97 btfss PORTC,2 ; Check for BUSON
\r
99 btfss PORTB,0 ; Wait for clock to go high
\r
102 btfsc PORTA,4 ; Check for RST
\r
104 btfss PORTC,2 ; Check for BUSON
\r
106 btfsc PORTB,0 ; Wait for clock to go low
\r
110 btfss PORTC,3 ; Test DATA
\r
111 setc ; Set carry if data is LOW (data is inverted!)
\r
112 rlf DataStore,f ; Shift it into our accumulator
\r
114 decfsz DataCount,f ; Loop once more perhaps?
\r
119 bcf INTCON,INTF ; Clear our IRQ
\r
124 movwf PCLATH ; Restore PCLATH
\r
126 movwf STATUS ; Restore STATUS
\r
128 swapf IRQW,w ; Restore W
\r
129 retfie ; Interrupt return
\r
133 subtitl "Main loop"
\r
136 ;----------------------------------------------------------------
\r
137 ; Data can be stored between here and 100h...
\r
140 DT "-WJ UniLink I/F-"
\r
142 DT "Code and design:"
\r
144 DT "**TCC of Yodel**"
\r
147 LookUp movwf PCL ; Go to it
\r
149 ;----------------------------------------------------------------
\r
150 ; Main program begins here. [Called after bootloader, lcdinit and irqinit...]
\r
156 bsf TXSTA,TXEN ; Enable UART TX
\r
157 bcf STATUS,RP0 ; Back to bank 0
\r
159 bsf RCSTA,SPEN ; Enable serial port
\r
160 bsf RCSTA,CREN ; Enable UART RX
\r
164 ; movlw 8 ; Loop this many times
\r
168 ; btfsc PORTA,4 ; Check for RST
\r
170 ; btfss PORTC,2 ; Check for BUSON
\r
172 ; btfss PORTB,0 ; Wait for clock to go high
\r
175 ; btfsc PORTA,4 ; Check for RST
\r
177 ; btfss PORTC,2 ; Check for BUSON
\r
179 ; btfsc PORTB,0 ; Wait for clock to go low
\r
182 ; clrc ; Clear carry
\r
183 ; btfss PORTC,3 ; Test DATA
\r
184 ; setc ; Set carry if data is LOW (data is inverted!)
\r
185 ; rlf DataStore,f ; Shift it into our accumulator
\r
187 ; decfsz DataCount,f ; Loop once more perhaps?
\r
190 bcf LCD_RS_BIT ;Command mode
\r
191 movlw 80h ;DisplayRam 0
\r
196 btfsc PORTA,4 ; Test RST
\r
201 btfsc PORTB,0 ; Test CLK
\r
206 btfsc PORTC,2 ; Test BUSON-IN
\r
211 btfsc PORTC,3 ; Test DATA
\r
215 movf DataCount,w ; Load bit counter (if 0 then byte is available)
\r
219 movf DataStore,w ; Get the result
\r
220 decf DataCount,f ; Set it non-zero
\r
222 call BootTXB ; Send to terminal
\r
232 bsf PORTA,4 ; turn off LED
\r
238 bcf PORTA,4 ; turn on LED
\r
247 ;----------------------------------------------------------------
\r
248 ; IRQInit - Sets up the IRQ Handler
\r
251 bsf STATUS,RP0 ; Reg bank 1
\r
252 ; bcf OPTION_REG,INTEDG ; We want RB0 to give us an IRQ on the falling edge
\r
253 bsf INTCON,INTE ; Enable the RB0/INT
\r
254 bsf INTCON,GIE ; Enable global interrupts
\r
255 bcf STATUS,RP0 ; Back to bank 0
\r
258 ;----------------------------------------------------------------
\r
259 ; Initialize LCD Controller...
\r
263 bsf STATUS,RP0 ; Hi Bank
\r
264 movlw 0cfh ; RC4 & RC5 should be outputs...
\r
266 movlw 001h ; All but RB0 are outputs.
\r
268 bcf OPTION_REG,NOT_RBPU ; Turn on port B pull-up
\r
269 bcf STATUS,RP0 ; Restore Lo Bank
\r
271 ; bcf PORTA,4 ; turn on LED
\r
273 ; movlw 44 ; Should be 16ms delay
\r
274 movlw 255 ; Should be 16ms delay
\r
277 movlw 3 ; Write 3 to the LCD
\r
278 call TxLCD ; Send to LCD
\r
279 ; movlw 12 ; Should be 5ms delay
\r
280 movlw 255 ; Should be 16ms delay
\r
283 movlw 3 ; Write 3 to the LCD
\r
285 ; movlw 12 ; Should be 16ms delay
\r
286 movlw 255 ; Should be 16ms delay
\r
289 movlw 3 ; Write 3 to the LCD
\r
292 movlw 255 ; Should be 16ms delay
\r
296 call TxLCD ; | 4-bit interface
\r
297 ; movlw 55 ; | After this we are ready to ROCK!
\r
298 movlw 255 ; Should be 16ms delay
\r
301 ; bsf PORTA,4 ; turn off LED
\r
303 movlw 28h ; Some random commands :)))
\r
317 ;----------------------------------------------------------------
\r
318 ; LongDelay - Well, guess that for yourself...
\r
321 ; btfss PORTB,6 ; Talk to da PC?
\r
322 ; goto PCTalk ; Oh yeah...
\r
358 ;----------------------------------------------------------------
\r
360 ; Send a string to the LCD.
\r
365 movlw 80h ;DisplayRam 0
\r
370 movlw 80h+40 ;DisplayRam 40 (row 2)
\r
376 ;----------------------------------------------------------------
\r
378 ; Send a string to the LCD.
\r
381 ; movwf Icount ; Icount = W
\r
383 movwf e_LEN ; Move to e_LEN
\r
385 Txm_lp movf Icount,w ; get the byte
\r
387 incf Icount,f ; ...else ++Icount (table index)
\r
388 call TxLCDB ; Send out the byte
\r
393 ;----------------------------------------------------------------
\r
394 ; TxLCDB - send a byte to the LCD
\r
397 movwf TxTemp ; Store byte to send for a while...
\r
399 bcf temp,0 ; Clear my temp bit
\r
400 btfss LCD_RS_BIT ; Check if we try the correct reg
\r
403 bsf temp,0 ; Indicate RS change
\r
407 call RxLCDB ; Receive byte from LCD, status reg
\r
409 btfss STATUS,Z ; If the bit was set, the zero flag is not
\r
412 btfsc temp,0 ; If we had to clear RS reset it now
\r
415 swapf TxTemp,w ; Hi nibble of data to send in lo w bits
\r
416 call TxLCD ; Send them first...
\r
417 movf TxTemp,w ; Then we have the low nibble in low w bits
\r
418 call TxLCD ; And send that one as well
\r
421 ;----------------------------------------------------------------
\r
422 ; RxLCDB - recv a byte from the LCD
\r
425 call RxLCD ; Receive the high nibble
\r
427 swapf LCDWTmp,f ; Swap it back to file
\r
428 call RxLCD ; Receive the low nibble
\r
429 addwf LCDWTmp,w ; Put the nibbles together and return in W
\r
433 ;----------------------------------------------------------------
\r
434 ; TxLCD - send a nibble to the LCD
\r
437 movwf LCDWTmp ; Write nibble to tmp
\r
438 bcf LCD_DB4_BIT ; Clear previous data
\r
443 btfsc LCDWTmp,0 ; Test bit 0, transfer a set bit to LCD PORT
\r
445 btfsc LCDWTmp,1 ; Test bit 1, transfer a set bit to LCD PORT
\r
447 btfsc LCDWTmp,2 ; Test bit 2, transfer a set bit to LCD PORT
\r
449 btfsc LCDWTmp,3 ; Test bit 3, transfer a set bit to LCD PORT
\r
452 bsf LCD_E_BIT ; And set E to clock the data into the LCD module
\r
453 nop ; Let it settle
\r
454 bcf LCD_E_BIT ; And clear the Enable again.
\r
455 return ; Returns without modifying W
\r
457 ;----------------------------------------------------------------
\r
458 ; RxLCD - recv a nibble from the LCD
\r
461 clrw ; Clear W register, return data in lower 4 bits
\r
463 bsf STATUS,RP0 ; Select 2nd reg bank, now TRIS regs can be accessed
\r
465 bsf LCD_DB4_BIT ; This sets the port bit as an input
\r
469 bcf STATUS,RP0 ; Back at reg bank 0
\r
471 bsf LCD_RW_BIT ; Set Read mode for the LCD
\r
472 bsf LCD_E_BIT ; And set E to clock the data out of the LCD module
\r
473 nop ; Let the bus settle
\r
474 btfsc LCD_DB4_BIT ; Transfer a set port bit into W
\r
476 btfsc LCD_DB5_BIT ; Transfer a set port bit into W
\r
478 btfsc LCD_DB6_BIT ; Transfer a set port bit into W
\r
480 btfsc LCD_DB7_BIT ; Transfer a set port bit into W
\r
482 bcf LCD_E_BIT ; And clear the Enable again.
\r
483 bcf LCD_RW_BIT ; Set Write mode for the LCD
\r
485 bsf STATUS,RP0 ; Select 2nd reg bank, now TRIS regs can be accessed
\r
486 bcf LCD_DB4_BIT ; Set the port as an output again
\r
490 bcf STATUS,RP0 ; Back at reg bank 0
\r
492 return ; Returns with data in W
\r
494 ;----------------------------------------------------------------------
\r
495 ; Delay routines (one iteration=3 cycles. That is 0.366211ms @32kHz)
\r
496 ; 2.73* # of ms is good...
\r
498 DelayW movwf Dcount ; Set delay counter
\r
501 DelayLp decfsz Dcount,f
\r
504 DelayIn decfsz Dcount2,f
\r
513 subtitl "Bootstrap/Bootloader code"
\r
516 ;----------------------------------------------------------------------
\r
517 ; Bootstrap code - Allows PIC to flash itself with data from async port
\r
520 org 700h ; Place the boot code at the top of memory
\r
522 BootRXState equ 7fh ; What are we waiting for @RX
\r
523 BootBits equ 7eh ; bit0 1=write 0=read, bit1 1=PGM 0=EE, bit2 0=normal 1=no-op when prog
\r
531 BootNumBytes equ 76h
\r
534 BootHEXTemp equ 73h
\r
535 BootStrTemp equ 72h
\r
541 bsf STATUS,RP0 ; Access bank 1
\r
542 bsf TXSTA,TXEN ; Enable UART TX
\r
543 movlw 31 ; Divisor for 9k6 @ 20MHz Fosc
\r
544 movwf SPBRG ; Store
\r
545 bcf STATUS,RP0 ; Back to bank 0
\r
547 bsf RCSTA,SPEN ; Enable serial port
\r
548 bsf RCSTA,CREN ; Enable UART RX
\r
550 ; clrf BootRXState ; Waiting for command
\r
552 movlw BootStartText
\r
561 incf BootTimerL,f ; A 24-bit counter
\r
566 skpnz ; When overflowing here..
\r
567 goto BootReturn ; ..Exit boot loader, no keypress within timeout period, resume program
\r
568 btfss PIR1,RCIF ; Wait for RX to complete
\r
573 goto BootTimeout ; If it wasn't space, wait for another key
\r
576 movlw BootFlashText
\r
584 call BootRXB ; First find the ':'
\r
587 goto BootLoop ; Loop until we find it!
\r
589 call BootRXHEX ; Get one ASCII encoded byte (two chars)
\r
590 movwf BootNumBytes ; This is the number of bytes to be programmed on the line
\r
591 ; Maybe clear cary here?
\r
592 rrf BootNumBytes,f ; Right shift because we're double addressing this 8-bit format
\r
594 ; Note carry should be clear here as there cannot be odd number of bytes in this format
\r
596 call BootRXHEX ; Receive AddrH
\r
598 call BootRXHEX ; Receive AddrL
\r
600 rrf BootAddrH,f ; Fix the addressing again
\r
603 bcf BootBits,2 ; Assume we should program
\r
604 bsf BootBits,1 ; And assume we should program flash not ee
\r
607 xorlw 020h ; Check if it's configuration, which we can't program
\r
608 skpnz ; Skip the bit set if it was false alarm
\r
609 bsf BootBits,2 ; No programming for this line
\r
611 xorlw 001h ; Also check if it's EEPROM memory (first xor 20h then 1 =21h)
\r
612 skpnz ; Skip the bit set instr if not EE data address
\r
613 bcf BootBits,1 ; We should program EE, will ignore the AddrH
\r
615 call BootRXHEX ; Receive Record Type (must be 0 for real records)
\r
616 skpz ; Check if zero
\r
617 goto BootFlashComplete
\r
620 call BootRXHEX ; Receive low-byte of data word
\r
622 call BootRXHEX ; Receive high-byte of data word
\r
625 btfsc BootBits,2 ; Check whether this line should be programmed at all
\r
628 bcf BootBits,0 ; Read mode first, verify if we actually have to write
\r
631 xorwf BootDataL,f ; Compare and destroy DataL
\r
632 movwf BootDataL ; Write new data to DataL
\r
633 skpz ; Skip if no difference, have to check high byte as well
\r
634 goto BootWrite ; Jump directly to write
\r
637 xorwf BootDataH,f ; Compare
\r
638 skpnz ; Skip if no difference, no programming necessary
\r
643 movwf BootDataH ; Have to put the new H byte data in as well
\r
646 ; call BootTXB ; Send progword indicator
\r
649 call BootEE ; Write directly into program mem
\r
651 ; Here a verify can take place, the read-back results are now in DataL/H
\r
661 incf BootAddrL,f ; Advance counter to next addr
\r
663 incf BootAddrH,f ; And add to high byte if needed
\r
665 decfsz BootNumBytes,f
\r
668 movlw 13 ; Progress
\r
670 movlw 10 ; Progress
\r
683 btfss TXSTA,TRMT ; Wait for last things to flush
\r
684 goto BootReturnWait
\r
685 bcf TXSTA,TXEN ; Disable UART TX
\r
686 bcf STATUS,RP0 ; Back to bank 0
\r
688 bcf RCSTA,SPEN ; Enable serial port
\r
689 bcf RCSTA,CREN ; Enable UART RX
\r
692 return ; Return to code
\r
694 ;----------------------------------------------------------------------
\r
695 ; BootTXB - Sends one byte to the UART, waits for transmitter to become
\r
696 ; free before sending
\r
700 btfss PIR1,TXIF ; Wait for TX to empty
\r
702 movwf TXREG ; Send the byte
\r
705 ;----------------------------------------------------------------------
\r
706 ; BootTXStr - Sends ASCII string pointed to by W, zero terminated
\r
709 movwf BootStrTemp ; Store offset
\r
710 call BootLookup ; Lookup char
\r
714 call BootTXB ; Send char
\r
715 incf BootStrTemp,w ; Retrieve
\r
718 ;----------------------------------------------------------------------
\r
719 ; BootRXB - Receives one byte from the UART, waits if nothing available
\r
723 btfss PIR1,RCIF ; Wait for RX to complete
\r
725 movf RCREG,w ; Get the recvd byte
\r
728 ;----------------------------------------------------------------------
\r
729 ; BootRXHEXNibble - Receives one byte and converts it from ASCII HEX to binary
\r
732 call BootRXB ; Receive nibble
\r
734 addlw -'A' ; Convert from BCD to binary nibble
\r
735 skpc ; Test if if was 0-9 or A-F, skip if A-F
\r
736 addlw 'A' - 10 - '0' ; It was numeric '0'
\r
737 addlw 10 ; Add 10 (A get to be 0ah etc.)
\r
740 ;----------------------------------------------------------------------
\r
741 ; BootRXHEX - Receives two bytes from the UART, waits if nothing available
\r
742 ; Decodes the bytes as ASCII hex and returns a single byte in W
\r
745 call BootRXHEXNibble
\r
747 swapf BootHEXTemp,f ; Swap it up to the high nibble
\r
749 call BootRXHEXNibble
\r
750 addwf BootHEXTemp,w ; And add the two nibbles together
\r
753 ;----------------------------------------------------------------------
\r
754 ; BootEE - Reads or writes EE or Flash memory, BootBits specify the
\r
755 ; exact action to take. BootAddrL and BootAddrH has to be initialized
\r
756 ; to the address of choice (0000-003fh for EE and 0000h-07ffh for flash
\r
757 ; The data to be written has to be put in BootDataL and BootDataH, and
\r
758 ; data will be to the same place when read back
\r
761 bsf STATUS,RP1 ; Select bank 2 (RP0 must be 0)
\r
763 movf BootAddrH,w ; Load desired address
\r
767 movf BootDataH,w ; And load the data (only used when writing)
\r
772 bsf STATUS,RP0 ; Go to bank 3
\r
774 bsf EECON1,EEPGD ; Point to Program Flash mem
\r
775 btfss BootBits,1 ; Test if that was correct or if we have to clear again
\r
776 bcf EECON1,EEPGD ; Point to EE DATA mem
\r
778 btfss BootBits,0 ; Check from read or write
\r
779 goto BootEERD ; Skip the WR if we were going for a read
\r
781 bsf EECON1,WREN ; Enable writes
\r
785 movwf EECON2 ; Unlock write operation
\r
786 bsf EECON1,WR ; And start a write cycle
\r
788 btfsc EECON1,WR ; This executes for EE only not flash, waits for WR to finish
\r
789 goto BootWRLoop ; These two instructions gets NOPed when flashing
\r
791 bcf EECON1,WREN ; Finally disable writes again
\r
792 ; Here we read the data back again, can be used as verify
\r
794 bsf EECON1,RD ; Start a read cycle
\r
795 nop ; Only necessary for flash read, same thing as when writing above
\r
796 nop ; Except I could use the two words for something useful there.. :)
\r
799 bcf STATUS,RP0 ; Back to bank 2
\r
800 movf EEDATA,w ; Store our EE-data
\r
804 bcf STATUS,RP1 ; And finally back to bank 0
\r
809 DT "WJBoot - press ESC to flash",0
\r
811 DT 13,10,"Send INHX8 file now...",13,10,0
\r
813 DT 13,10,"Exiting loader",13,10,0
\r
816 movwf PCL ; Go fetch the char data
\r
818 ;----------------------------------------------------------------------
\r
819 ; EE Data (64 bytes), located at 2100h
\r
822 ; data 0f2h, 099h, 000h, 000h, 018h, 0a5h, 090h, 084h
\r