1 title "PIC16F876A Unilink Interface by Werner Johansson (c) 2004-2005"
\r
2 subtitl "Definitions"
\r
3 list c=132,P=16F876a,R=DEC,F=inhx8m
\r
4 include "p16f876a.inc" ; Standard equates & Macros
\r
5 ERRORLEVEL 1,-302 ; Get rid of those annoying 302 msgs!
\r
7 ;----------------------------------------------------------------
\r
8 ; The Configuration Word
\r
9 __CONFIG _HS_OSC&_WDT_OFF&_PWRTE_OFF&_BODEN_ON&_LVP_OFF&_CPD_OFF&_WRT_OFF&_DEBUG_OFF&_CP_OFF
\r
11 ;----------------------------------------------------------------
\r
13 ;----------------------------------------------------------------
\r
16 ; 0.0 Very first "Fucking No Work!" version
\r
17 ; 0.1 Some receiving works with the new MCU using HW SPI, yay!
\r
18 ; 0.2 Boot Agent for UART flash is back?
\r
20 ;----------------------------------------------------------------
\r
22 ; Unilink BUSON IN (blue) connected to RA4C2/T0CKI
\r
23 ; Unilink DATA (green) connected to RC4(SDI), RC5(SDO) via open collector driver (HW USART)
\r
24 ; Unilink BUSON OUT (blue) connected to RA5 via 100R resistor (this is for daisy-chaining)
\r
25 ; Unilink CLK (yellow) connected to RC3(SCK) (USART CLK)
\r
26 ; Unilink RST (lilac) connected to RB0(INT)
\r
27 ; RS-232 TX from computer connected to RC7/RX
\r
28 ; RS-232 RX to computer connected to RC6/TX
\r
29 ; RS-232 RTS from computer connected to RC2
\r
30 ; RS-232 RI and CTS to computer connected to RC1
\r
31 ; RS-232 INVALID signal from MAX connected to RC0
\r
33 ; Pin mapping as follows:
\r
35 ; RA0 Voltage sense (A/D in 0)
\r
36 ; RA1 Amp sense 1 (A/D in 1)
\r
37 ; RA2 Early-Startup sense/Blue LED drive (active low) (RA2 and RA3 have been reversed from the PCB layout!)
\r
38 ; RA3 Amp sense 2 (A/D in 3)
\r
39 ; RA4/T0CKI BUSON IN from Head Unit (can trigger interrupt on clocking?)
\r
40 ; RA5 BUSON OUT to cascade
\r
42 ; RB0 UNI RST in (interrupt triggered)
\r
43 ; RB1 Enable-PWR output to SEPIC converter
\r
44 ; RB2 Disable-AMP output
\r
45 ; RB3 Enable-AUX output to trigger analog switch relay
\r
46 ; RB4 Sign output from Amp sense 1/Red LED drive, active low
\r
47 ; RB5 Sign output from Amp sense 2/Green LED drive, active low
\r
48 ; RB6 ICSP/ICD reserved
\r
49 ; RB7 ICSP/ICD reserved
\r
51 ; RC0 INVALID input from MAX3235
\r
52 ; RC1 CTS and RI output
\r
54 ; RC3 Unilink clock input
\r
55 ; RC4 Unilink data input
\r
56 ; RC5 Unilink data output (drives inverted!)
\r
60 ;----------------------------------------------------------------
\r
61 ; File register usage
\r
63 ; Memory from 20h to 7fh (BANK 0 96 bytes)
\r
64 ; a0h to efh (BANK 1 80 bytes)
\r
65 ; 110h to 16fh (BANK 2 96 bytes)
\r
66 ; 190h to 1efh (BANK 3 96 bytes)
\r
67 ; 70-7fh, f0-ffh, 170-17fh and 1f0-1ffh share the same 16 bytes, for ISR!)
\r
71 Icount equ 2Dh ; Offset of string to print
\r
72 TxTemp equ 2Eh ; blahblah
\r
73 TxTemp2 equ 2Fh ; Blahblah2
\r
75 ; Start of code - four instructions fit before the IRQ handler
\r
77 nop ; Leave the first location as NOP, just in case (required for ICD use)
\r
78 call Bootstrap ; Call Flash Load routine at top of flash
\r
79 call IRQInit ; Set up and start the IRQ handler
\r
80 goto Main ; Run the main program loop (skip the IRQ handler)
\r
82 subtitl "IRQ Handler"
\r
83 ;----------------------------------------------------------------
\r
84 ; Interrupt handler always starts at addr 4.
\r
85 ; Let the entire handler stay here instead of branching away
\r
87 org 4 ; Must be on Address 4!
\r
88 retfie ; Interrupt return
\r
90 subtitl "IRQ Initialization code"
\r
91 ;----------------------------------------------------------------
\r
92 ; Initializes the interrupt handler and registers.
\r
105 bsf STATUS,RP0 ; Hi Bank
\r
106 movlw 04h ; AD mode 4h, 3 ad inputs AN0, 1 and 3, not 2 (!) fix pcb.. :(
\r
108 movlw 0dfh ; RA5 should be outputs...
\r
109 ; movlw 0dbh ; RA2 & RA5 should be outputs...
\r
110 ; movlw 0fbh ; RA2 should be output...
\r
112 movlw 0c1h ; RB0, 6 & 7 are inputs.
\r
114 movlw 9dh ; RC0, 2, 3, 4, 7 are inputs
\r
115 ; movlw 0bdh ; RC0, 2, 3, 4, 7 are inputs
\r
117 bcf OPTION_REG,NOT_RBPU ; Turn on port B pull-up
\r
118 bcf STATUS,RP0 ; Restore Lo Bank
\r
120 bsf PORTB,1 ; Turn on SEPIC
\r
121 ; bsf PORTA,5 ; test ss shit
\r
123 bsf STATUS,RP0 ; Access bank 1
\r
124 bsf TXSTA,TXEN ; Enable UART TX
\r
125 movlw 31 ; Divisor for 9k6 @ 20MHz Fosc
\r
126 movwf SPBRG ; Store
\r
127 bcf STATUS,RP0 ; Back to bank 0
\r
129 bsf RCSTA,SPEN ; Enable serial port
\r
130 bsf RCSTA,CREN ; Enable UART RX
\r
133 ; movwf TXREG ; Test transmission
\r
138 ; btfss PORTB,0 ; Test RST bit
\r
141 ; bsf STATUS,RP0 ; Hi Bank
\r
142 ; bsf TRISA,2 ; RA2 back as input (turn off blue led)
\r
143 ; bcf STATUS,RP0 ; Lo Bank
\r
146 bcf SSPCON,SSPEN ;disable ssp
\r
151 btfsc PORTB,0 ; Test RST bit
\r
156 btfss PORTA,4 ; Test BUSON bit
\r
157 goto WaitBUSONHigh
\r
160 ; movlw 40h ; Latch on active-to-idle edge (needs ss crap?)
\r
161 movlw 00h ; Transfer on idle-to-active edge (and latch on active-to-idle)
\r
167 btfsc PORTC,3 ; SCK input must be low before enabling SSP according to errata sheet (!)
\r
170 ; movlw 34h ; fuxx0red ss mode crap
\r
171 movwf SSPCON ; Enable SPI slave mode with SCK IDLE low, no _SS control
\r
179 btfsc PORTB,0 ; Test RST bit
\r
182 btfss PORTA,4 ; Test BUSON bit
\r
186 btfss SSPSTAT,BF ; Test for buffer complete
\r
190 xorlw 0ffh ; Invert received data
\r
191 movwf TXREG ; Send it out to Async serial
\r
200 ; NOTE!!!! This is wrong, never drive the led outputs high as there are other open-collector drivers on these pins!!!
\r
201 ; This is just for debugging, must be removed when hooking things up to the board!!!
\r
203 ; IO is inverted logic (active low)
\r
204 ; btfsc PORTA,4 ; test buson
\r
205 btfss PORTC,4 ; test IO
\r
206 bcf PORTB,5 ; turn on green led
\r
207 ; btfss PORTA,4 ; test buson clear
\r
208 btfsc PORTC,4 ; test IO
\r
209 bsf PORTB,5 ; turn off green led
\r
211 ; CLK is true logic (active high)
\r
212 ; btfsc PORTB,0 ; Test RST bit
\r
213 btfsc PORTC,3 ; test clk
\r
214 bcf PORTB,4 ; turn on red led if RST high
\r
215 ; btfss PORTB,0 ; Test RST bit
\r
216 btfss PORTC,3 ; test clk
\r
217 bsf PORTB,4 ; turn off red led if RST low
\r
221 subtitl "Bootstrap/Bootloader code"
\r
224 ;----------------------------------------------------------------------
\r
225 ; Bootstrap code - Allows PIC to flash itself with data from the async port.
\r
226 ; Accepts a standard INHX8 encoded file as input, the only caveat is that the code is slow when writing to memory
\r
227 ; (we have to wait for the flash to complete), and therefore care has to be taken not to overflow the RS232 receiver
\r
228 ; (one good way of solving that is to wait for the echo from the PIC before sending anything else)
\r
229 ; Both program memory and Data EEPROM memory can be programmed, but due to hardware contraints the configuration
\r
230 ; register can't be programmed. That means that any references to the config register in the hex file will be ignored.
\r
234 ; RAM usage for the bootstrap code
\r
236 BootBits equ 7eh ; bit0 1=write 0=read, bit1 1=PGM 0=EE, bit2 0=normal 1=no-op when prog
\r
244 BootNumBytes equ 76h
\r
247 BootHEXTemp equ 73h
\r
249 org 72eh ; Place the boot code at the top of memory page 0
\r
252 bsf STATUS,RP0 ; Access bank 1
\r
253 bsf TXSTA,TXEN ; Enable UART TX
\r
254 movlw 31 ; Divisor for 9k6 @ 20MHz Fosc
\r
255 movwf SPBRG ; Store
\r
256 bcf STATUS,RP0 ; Back to bank 0
\r
258 bsf RCSTA,SPEN ; Enable serial port
\r
259 bsf RCSTA,CREN ; Enable UART RX
\r
261 movlw low BootStartText ; Send boot banner to the serial port
\r
264 movlw 0e8h ; Initialize timeout timer (e8 is about 3 secs)
\r
265 ; movlw 0fdh ; Initialize timeout timer (fd is short enough to get the headunit to recognize us)
\r
271 incf BootTimerL,f ; A 24-bit counter
\r
276 skpnz ; When overflowing here..
\r
277 goto BootReturn ; ..Exit boot loader, no keypress within timeout period, resume program
\r
278 btfss PIR1,RCIF ; Wait for RX to complete
\r
283 goto BootTimeout ; If it wasn't ESC, wait for another key
\r
286 movlw low BootFlashText ; OK, flashing it is, send "start" text to serial port
\r
294 call BootRXB ; First find the ':'
\r
297 goto BootLoop ; Loop until we find it!
\r
299 call BootRXHEX ; Get one ASCII encoded byte (two chars)
\r
300 movwf BootNumBytes ; This is the number of bytes to be programmed on the line
\r
301 ; Maybe clear cary here?
\r
302 rrf BootNumBytes,f ; Right shift because we're double addressing this 8-bit format
\r
304 ; Note carry should be clear here as there cannot be odd number of bytes in this format
\r
306 call BootRXHEX ; Receive AddrH
\r
308 call BootRXHEX ; Receive AddrL
\r
310 rrf BootAddrH,f ; Fix the addressing again
\r
313 bcf BootBits,2 ; Assume we should program
\r
314 bsf BootBits,1 ; And assume we should program flash not ee
\r
317 xorlw 020h ; Check if it's configuration, which we can't program
\r
318 skpnz ; Skip the bit set if it was false alarm
\r
319 bsf BootBits,2 ; No programming for this line
\r
321 xorlw 001h ; Also check if it's EEPROM memory (first xor 20h then 1 =21h)
\r
322 skpnz ; Skip the bit set instr if not EE data address
\r
323 bcf BootBits,1 ; We should program EE, will ignore the AddrH
\r
325 call BootRXHEX ; Receive Record Type (must be 0 for real records)
\r
326 skpz ; Check if zero
\r
327 goto BootFlashComplete
\r
330 call BootRXHEX ; Receive low-byte of data word
\r
332 call BootRXHEX ; Receive high-byte of data word
\r
335 btfsc BootBits,2 ; Check whether this line should be programmed at all
\r
338 bcf BootBits,0 ; Read mode first, verify if we actually have to write
\r
341 xorwf BootDataL,f ; Compare and destroy DataL
\r
342 movwf BootDataL ; Write new data to DataL
\r
343 skpz ; Skip if no difference, have to check high byte as well
\r
344 goto BootWrite ; Jump directly to write
\r
347 xorwf BootDataH,f ; Compare
\r
348 skpnz ; Skip if no difference, no programming necessary
\r
353 movwf BootDataH ; Have to put the new H byte data in as well
\r
356 call BootEE ; Write directly into program mem
\r
358 ; Here a verify can take place, the read-back results are now in DataL/H
\r
362 incf BootAddrL,f ; Advance counter to next addr
\r
364 incf BootAddrH,f ; And add to high byte if needed
\r
366 decfsz BootNumBytes,f
\r
374 movlw low BootRunText
\r
377 bsf STATUS,RP0 ; Reg bank 1
\r
379 btfss TXSTA,TRMT ; Wait for last things to flush
\r
380 goto BootReturnWait
\r
381 bcf TXSTA,TXEN ; Disable UART TX
\r
382 bcf STATUS,RP0 ; Back to bank 0
\r
384 bcf RCSTA,SPEN ; Disable serial port
\r
385 bcf RCSTA,CREN ; Disable UART RX
\r
387 return ; Return to code
\r
389 ;----------------------------------------------------------------------
\r
390 ; BootTXB - Sends one byte to the UART, waits for transmitter to become
\r
391 ; free before sending
\r
395 btfss PIR1,TXIF ; Wait for TX to empty
\r
397 movwf TXREG ; Send the byte
\r
400 ;----------------------------------------------------------------------
\r
401 ; BootTXStr - Sends ASCII string pointed to by W, zero terminated
\r
404 movwf BootAddrL ; Store LSB of text pointer
\r
405 movlw 07h ; MSB of pointer to the text (0700h in this boot loader)
\r
407 movlw 02h ; Select "Read Program Memory" operation
\r
410 call BootEE ; Lookup char (actually two packed into one word)
\r
411 rlf BootDataL,w ; Shift the MSB out into carry (that's the 2nd char LSB)
\r
412 rlf BootDataH,w ; Shift it into 2nd char
\r
413 call BootTXB ; Send the high byte first
\r
414 movf BootDataL,w ; Get the low byte
\r
415 andlw 07fh ; Mask of the highest bit
\r
416 skpnz ; Stop if zero
\r
418 call BootTXB ; Send char
\r
419 incf BootAddrL,f ; Increment pointer
\r
422 ;----------------------------------------------------------------------
\r
423 ; BootRXB - Receives one byte from the UART, waits if nothing available
\r
427 btfss PIR1,RCIF ; Wait for RX to complete
\r
429 movf RCREG,w ; Get the recvd byte
\r
430 call BootTXB ; Echo to terminal
\r
433 ;----------------------------------------------------------------------
\r
434 ; BootRXHEXNibble - Receives one byte and converts it from ASCII HEX to binary
\r
437 call BootRXB ; Receive nibble
\r
439 ; This code is from piclist.com, really neat!
\r
441 addlw -'A' ; Convert from BCD to binary nibble
\r
442 skpc ; Test if if was 0-9 or A-F, skip if A-F
\r
443 addlw 'A' - 10 - '0' ; It was numeric '0'
\r
444 addlw 10 ; Add 10 (A get to be 0ah etc.)
\r
448 ;----------------------------------------------------------------------
\r
449 ; BootRXHEX - Receives two bytes from the UART, waits if nothing available
\r
450 ; Decodes the bytes as ASCII hex and returns a single byte in W
\r
453 call BootRXHEXNibble
\r
455 swapf BootHEXTemp,f ; Swap it up to the high nibble
\r
457 call BootRXHEXNibble
\r
458 addwf BootHEXTemp,w ; And add the two nibbles together
\r
461 ;----------------------------------------------------------------------
\r
462 ; BootEE - Reads or writes EE or Flash memory, BootBits specify the
\r
463 ; exact action to take. BootAddrL and BootAddrH has to be initialized
\r
464 ; to the address of choice (0000-00ffh for EE and 0000h-1fffh for flash.
\r
465 ; The data to be written has to be put in BootDataL and BootDataH, and
\r
466 ; data will be written to the same place when read back
\r
469 bsf STATUS,RP1 ; Select bank 2 (RP0 must be 0)
\r
471 movf BootAddrH,w ; Load desired address
\r
475 movf BootDataH,w ; And load the data (only used when writing)
\r
480 bsf STATUS,RP0 ; Go to bank 3
\r
482 bsf EECON1,EEPGD ; Point to Program Flash mem
\r
483 btfss BootBits,1 ; Test if that was correct or if we have to clear again
\r
484 bcf EECON1,EEPGD ; Point to EE DATA mem
\r
486 btfss BootBits,0 ; Check from read or write
\r
487 goto BootEERD ; Skip the WR if we were going for a read
\r
489 bsf EECON1,WREN ; Enable writes
\r
493 movwf EECON2 ; Unlock write operation
\r
494 bsf EECON1,WR ; And start a write cycle
\r
496 btfsc EECON1,WR ; This executes for EE only not flash, waits for WR to finish
\r
497 goto BootWRLoop ; These two instructions gets NOPed when flashing program memory
\r
499 bcf EECON1,WREN ; Finally disable writes again
\r
500 ; Here we read the data back again, can be used as verify
\r
502 bsf EECON1,RD ; Start a read cycle
\r
503 nop ; Only necessary for flash read, same thing as when writing above
\r
504 nop ; Except I could use the two words for something useful there.. :)
\r
507 bcf STATUS,RP0 ; Back to bank 2
\r
508 movf EEDATA,w ; Store our EE-data
\r
512 bcf STATUS,RP1 ; And finally back to bank 0
\r
516 ; To produce compact code the end zero byte has to be in the LSB (that means an even number of chars in every string)
\r
518 DA "WJ PIC16F876A Boot Loader - press ESC to flash...\x00"
\r
521 DA "\r\nSend INHX8 file now...\r\x00"
\r
524 DA "\r\nExiting loader\r\x00"
\r
526 ;----------------------------------------------------------------------
\r
527 ; EE Data (256 bytes), located at 2100h
\r
530 ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
\r
531 ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
\r
532 ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
\r
533 ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
\r
534 ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
\r
535 ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
\r
536 ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
\r
537 ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
\r
539 ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
\r
540 ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
\r
541 ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
\r
542 ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
\r
543 ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
\r
544 ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
\r
545 ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
\r
546 ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
\r
548 ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
\r
549 ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
\r
550 ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
\r
551 ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
\r
552 ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
\r
553 ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
\r
554 ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
\r
555 ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
\r
557 ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
\r
558 ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
\r
559 ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
\r
560 ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
\r
561 ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
\r
562 ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
\r
563 ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
\r
564 ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
\r