title "PIC16F876A Unilink Interface by Werner Johansson (c) 2004-2005" subtitl "Definitions" list c=132,P=16F876a,R=DEC,F=inhx8m include "p16f876a.inc" ; Standard equates & Macros ERRORLEVEL 1,-302 ; Get rid of those annoying 302 msgs! ;---------------------------------------------------------------- ; The Configuration Word __CONFIG _HS_OSC&_WDT_OFF&_PWRTE_OFF&_BODEN_ON&_LVP_OFF&_CPD_OFF&_WRT_OFF&_DEBUG_OFF&_CP_OFF ;---------------------------------------------------------------- ; HISTORY ;---------------------------------------------------------------- ; Version ; ; 0.0 Very first "Fucking No Work!" version ; 0.1 Some receiving works with the new MCU using HW SPI, yay! ; 0.2 Boot Agent for UART flash is back? ; ;---------------------------------------------------------------- ; Unilink BUSON IN (blue) connected to RA4C2/T0CKI ; Unilink DATA (green) connected to RC4(SDI), RC5(SDO) via open collector driver (HW USART) ; Unilink BUSON OUT (blue) connected to RA5 via 100R resistor (this is for daisy-chaining) ; Unilink CLK (yellow) connected to RC3(SCK) (USART CLK) ; Unilink RST (lilac) connected to RB0(INT) ; RS-232 TX from computer connected to RC7/RX ; RS-232 RX to computer connected to RC6/TX ; RS-232 RTS from computer connected to RC2 ; RS-232 RI and CTS to computer connected to RC1 ; RS-232 INVALID signal from MAX connected to RC0 ; Pin mapping as follows: ; RA0 Voltage sense (A/D in 0) ; RA1 Amp sense 1 (A/D in 1) ; RA2 Early-Startup sense/Blue LED drive (active low) (RA2 and RA3 have been reversed from the PCB layout!) ; RA3 Amp sense 2 (A/D in 3) ; RA4/T0CKI BUSON IN from Head Unit (can trigger interrupt on clocking?) ; RA5 BUSON OUT to cascade ; RB0 UNI RST in (interrupt triggered) ; RB1 Enable-PWR output to SEPIC converter ; RB2 Disable-AMP output ; RB3 Enable-AUX output to trigger analog switch relay ; RB4 Sign output from Amp sense 1/Red LED drive, active low ; RB5 Sign output from Amp sense 2/Green LED drive, active low ; RB6 ICSP/ICD reserved ; RB7 ICSP/ICD reserved ; RC0 INVALID input from MAX3235 ; RC1 CTS and RI output ; RC2 RTS input ; RC3 Unilink clock input ; RC4 Unilink data input ; RC5 Unilink data output (drives inverted!) ; RC6 TX output ; RC7 RX input ;---------------------------------------------------------------- ; File register usage ; Memory from 20h to 7fh (BANK 0 96 bytes) ; a0h to efh (BANK 1 80 bytes) ; 110h to 16fh (BANK 2 96 bytes) ; 190h to 1efh (BANK 3 96 bytes) ; 70-7fh, f0-ffh, 170-17fh and 1f0-1ffh share the same 16 bytes, for ISR!) Dcount equ 20h e_LEN equ 21h Icount equ 2Dh ; Offset of string to print TxTemp equ 2Eh ; blahblah TxTemp2 equ 2Fh ; Blahblah2 ; Start of code - four instructions fit before the IRQ handler org 0 nop ; Leave the first location as NOP, just in case (required for ICD use) call Bootstrap ; Call Flash Load routine at top of flash call IRQInit ; Set up and start the IRQ handler goto Main ; Run the main program loop (skip the IRQ handler) subtitl "IRQ Handler" ;---------------------------------------------------------------- ; Interrupt handler always starts at addr 4. ; Let the entire handler stay here instead of branching away org 4 ; Must be on Address 4! retfie ; Interrupt return subtitl "IRQ Initialization code" ;---------------------------------------------------------------- ; Initializes the interrupt handler and registers. IRQInit return subtitl "Main loop" page org 100h Main clrf PORTA clrf PORTB clrf PORTC bsf STATUS,RP0 ; Hi Bank movlw 04h ; AD mode 4h, 3 ad inputs AN0, 1 and 3, not 2 (!) fix pcb.. :( movwf ADCON1 movlw 0dfh ; RA5 should be outputs... ; movlw 0dbh ; RA2 & RA5 should be outputs... ; movlw 0fbh ; RA2 should be output... movwf TRISA ; Yep. movlw 0c1h ; RB0, 6 & 7 are inputs. movwf TRISB ; Yep movlw 9dh ; RC0, 2, 3, 4, 7 are inputs ; movlw 0bdh ; RC0, 2, 3, 4, 7 are inputs movwf TRISC bcf OPTION_REG,NOT_RBPU ; Turn on port B pull-up bcf STATUS,RP0 ; Restore Lo Bank bsf PORTB,1 ; Turn on SEPIC ; bsf PORTA,5 ; test ss shit bsf STATUS,RP0 ; Access bank 1 bsf TXSTA,TXEN ; Enable UART TX movlw 31 ; Divisor for 9k6 @ 20MHz Fosc movwf SPBRG ; Store bcf STATUS,RP0 ; Back to bank 0 bsf RCSTA,SPEN ; Enable serial port bsf RCSTA,CREN ; Enable UART RX ; movlw 'W' ; movwf TXREG ; Test transmission ;WaitRSTHigh ; call UpdateLEDS ; btfss PORTB,0 ; Test RST bit ; goto WaitRSTHigh ; bsf STATUS,RP0 ; Hi Bank ; bsf TRISA,2 ; RA2 back as input (turn off blue led) ; bcf STATUS,RP0 ; Lo Bank RestartCrap bcf SSPCON,SSPEN ;disable ssp WaitRSTLow call UpdateLEDS ;b WaitRSTLow btfsc PORTB,0 ; Test RST bit goto WaitRSTLow WaitBUSONHigh call UpdateLEDS btfss PORTA,4 ; Test BUSON bit goto WaitBUSONHigh bsf STATUS,RP0 ; movlw 40h ; Latch on active-to-idle edge (needs ss crap?) movlw 00h ; Transfer on idle-to-active edge (and latch on active-to-idle) movwf SSPSTAT bcf STATUS,RP0 WaitCLKLow call UpdateLEDS btfsc PORTC,3 ; SCK input must be low before enabling SSP according to errata sheet (!) goto WaitCLKLow movlw 25h ; movlw 34h ; fuxx0red ss mode crap movwf SSPCON ; Enable SPI slave mode with SCK IDLE low, no _SS control clrw movwf SSPBUF OnceMore WaitForByte call UpdateLEDS btfsc PORTB,0 ; Test RST bit goto RestartCrap btfss PORTA,4 ; Test BUSON bit goto RestartCrap bsf STATUS,RP0 btfss SSPSTAT,BF ; Test for buffer complete goto WaitForByte bcf STATUS,RP0 movf SSPBUF,w xorlw 0ffh ; Invert received data movwf TXREG ; Send it out to Async serial clrw movwf SSPBUF goto OnceMore meck b meck UpdateLEDS ; NOTE!!!! This is wrong, never drive the led outputs high as there are other open-collector drivers on these pins!!! ; This is just for debugging, must be removed when hooking things up to the board!!! bcf STATUS,RP0 ; IO is inverted logic (active low) ; btfsc PORTA,4 ; test buson btfss PORTC,4 ; test IO bcf PORTB,5 ; turn on green led ; btfss PORTA,4 ; test buson clear btfsc PORTC,4 ; test IO bsf PORTB,5 ; turn off green led ; CLK is true logic (active high) ; btfsc PORTB,0 ; Test RST bit btfsc PORTC,3 ; test clk bcf PORTB,4 ; turn on red led if RST high ; btfss PORTB,0 ; Test RST bit btfss PORTC,3 ; test clk bsf PORTB,4 ; turn off red led if RST low return subtitl "Bootstrap/Bootloader code" page ;---------------------------------------------------------------------- ; Bootstrap code - Allows PIC to flash itself with data from the async port. ; Accepts a standard INHX8 encoded file as input, the only caveat is that the code is slow when writing to memory ; (we have to wait for the flash to complete), and therefore care has to be taken not to overflow the RS232 receiver ; (one good way of solving that is to wait for the echo from the PIC before sending anything else) ; Both program memory and Data EEPROM memory can be programmed, but due to hardware contraints the configuration ; register can't be programmed. That means that any references to the config register in the hex file will be ignored. ; ; Startup @9600bps ; RAM usage for the bootstrap code BootBits equ 7eh ; bit0 1=write 0=read, bit1 1=PGM 0=EE, bit2 0=normal 1=no-op when prog BootAddrL equ 7dh BootAddrH equ 7ch BootDataL equ 7bh BootDataH equ 7ah BootTimerL equ 79h BootTimerM equ 78h BootTimerH equ 77h BootNumBytes equ 76h BootDataVL equ 75h BootDataVH equ 74h BootHEXTemp equ 73h org 72eh ; Place the boot code at the top of memory page 0 Bootstrap bsf STATUS,RP0 ; Access bank 1 bsf TXSTA,TXEN ; Enable UART TX movlw 31 ; Divisor for 9k6 @ 20MHz Fosc movwf SPBRG ; Store bcf STATUS,RP0 ; Back to bank 0 bsf RCSTA,SPEN ; Enable serial port bsf RCSTA,CREN ; Enable UART RX movlw low BootStartText ; Send boot banner to the serial port call BootTXStr movlw 0e8h ; Initialize timeout timer (e8 is about 3 secs) ; movlw 0fdh ; Initialize timeout timer (fd is short enough to get the headunit to recognize us) movwf BootTimerL movwf BootTimerM movwf BootTimerH BootTimeout incf BootTimerL,f ; A 24-bit counter skpnz incf BootTimerM,f skpnz incf BootTimerH,f skpnz ; When overflowing here.. goto BootReturn ; ..Exit boot loader, no keypress within timeout period, resume program btfss PIR1,RCIF ; Wait for RX to complete goto BootTimeout call BootRXB xorlw 27 ; ESC skpz goto BootTimeout ; If it wasn't ESC, wait for another key BootFlash movlw low BootFlashText ; OK, flashing it is, send "start" text to serial port call BootTXStr bsf BootBits,1 clrf BootAddrL clrf BootAddrH BootLoop call BootRXB ; First find the ':' xorlw ':' skpz goto BootLoop ; Loop until we find it! call BootRXHEX ; Get one ASCII encoded byte (two chars) movwf BootNumBytes ; This is the number of bytes to be programmed on the line ; Maybe clear cary here? rrf BootNumBytes,f ; Right shift because we're double addressing this 8-bit format ; Note carry should be clear here as there cannot be odd number of bytes in this format call BootRXHEX ; Receive AddrH movwf BootAddrH call BootRXHEX ; Receive AddrL movwf BootAddrL rrf BootAddrH,f ; Fix the addressing again rrf BootAddrL,f bcf BootBits,2 ; Assume we should program bsf BootBits,1 ; And assume we should program flash not ee movf BootAddrH,w xorlw 020h ; Check if it's configuration, which we can't program skpnz ; Skip the bit set if it was false alarm bsf BootBits,2 ; No programming for this line xorlw 001h ; Also check if it's EEPROM memory (first xor 20h then 1 =21h) skpnz ; Skip the bit set instr if not EE data address bcf BootBits,1 ; We should program EE, will ignore the AddrH call BootRXHEX ; Receive Record Type (must be 0 for real records) skpz ; Check if zero goto BootFlashComplete BootLineLoop call BootRXHEX ; Receive low-byte of data word movwf BootDataVL call BootRXHEX ; Receive high-byte of data word movwf BootDataVH btfsc BootBits,2 ; Check whether this line should be programmed at all goto BootWriteSkip bcf BootBits,0 ; Read mode first, verify if we actually have to write call BootEE movf BootDataVL,w xorwf BootDataL,f ; Compare and destroy DataL movwf BootDataL ; Write new data to DataL skpz ; Skip if no difference, have to check high byte as well goto BootWrite ; Jump directly to write movf BootDataVH,w xorwf BootDataH,f ; Compare skpnz ; Skip if no difference, no programming necessary goto BootWriteSkip BootWrite movf BootDataVH,w movwf BootDataH ; Have to put the new H byte data in as well bsf BootBits,0 call BootEE ; Write directly into program mem ; Here a verify can take place, the read-back results are now in DataL/H BootWriteSkip incf BootAddrL,f ; Advance counter to next addr skpnz incf BootAddrH,f ; And add to high byte if needed decfsz BootNumBytes,f goto BootLineLoop goto BootLoop BootFlashComplete BootReturn movlw low BootRunText call BootTXStr bsf STATUS,RP0 ; Reg bank 1 BootReturnWait btfss TXSTA,TRMT ; Wait for last things to flush goto BootReturnWait bcf TXSTA,TXEN ; Disable UART TX bcf STATUS,RP0 ; Back to bank 0 bcf RCSTA,SPEN ; Disable serial port bcf RCSTA,CREN ; Disable UART RX return ; Return to code ;---------------------------------------------------------------------- ; BootTXB - Sends one byte to the UART, waits for transmitter to become ; free before sending BootTXB BootTXW1 btfss PIR1,TXIF ; Wait for TX to empty goto BootTXW1 movwf TXREG ; Send the byte return ;---------------------------------------------------------------------- ; BootTXStr - Sends ASCII string pointed to by W, zero terminated BootTXStr movwf BootAddrL ; Store LSB of text pointer movlw 07h ; MSB of pointer to the text (0700h in this boot loader) movwf BootAddrH movlw 02h ; Select "Read Program Memory" operation movwf BootBits BootTXStrLoop call BootEE ; Lookup char (actually two packed into one word) rlf BootDataL,w ; Shift the MSB out into carry (that's the 2nd char LSB) rlf BootDataH,w ; Shift it into 2nd char call BootTXB ; Send the high byte first movf BootDataL,w ; Get the low byte andlw 07fh ; Mask of the highest bit skpnz ; Stop if zero return call BootTXB ; Send char incf BootAddrL,f ; Increment pointer goto BootTXStrLoop ;---------------------------------------------------------------------- ; BootRXB - Receives one byte from the UART, waits if nothing available BootRXB BootRXW1 btfss PIR1,RCIF ; Wait for RX to complete goto BootRXW1 movf RCREG,w ; Get the recvd byte call BootTXB ; Echo to terminal return ;---------------------------------------------------------------------- ; BootRXHEXNibble - Receives one byte and converts it from ASCII HEX to binary BootRXHEXNibble call BootRXB ; Receive nibble ; This code is from piclist.com, really neat! addlw -'A' ; Convert from BCD to binary nibble skpc ; Test if if was 0-9 or A-F, skip if A-F addlw 'A' - 10 - '0' ; It was numeric '0' addlw 10 ; Add 10 (A get to be 0ah etc.) return ;---------------------------------------------------------------------- ; BootRXHEX - Receives two bytes from the UART, waits if nothing available ; Decodes the bytes as ASCII hex and returns a single byte in W BootRXHEX call BootRXHEXNibble movwf BootHEXTemp swapf BootHEXTemp,f ; Swap it up to the high nibble call BootRXHEXNibble addwf BootHEXTemp,w ; And add the two nibbles together return ;---------------------------------------------------------------------- ; BootEE - Reads or writes EE or Flash memory, BootBits specify the ; exact action to take. BootAddrL and BootAddrH has to be initialized ; to the address of choice (0000-00ffh for EE and 0000h-1fffh for flash. ; The data to be written has to be put in BootDataL and BootDataH, and ; data will be written to the same place when read back BootEE bsf STATUS,RP1 ; Select bank 2 (RP0 must be 0) movf BootAddrH,w ; Load desired address movwf EEADRH movf BootAddrL,w movwf EEADR movf BootDataH,w ; And load the data (only used when writing) movwf EEDATH movf BootDataL,w movwf EEDATA bsf STATUS,RP0 ; Go to bank 3 bsf EECON1,EEPGD ; Point to Program Flash mem btfss BootBits,1 ; Test if that was correct or if we have to clear again bcf EECON1,EEPGD ; Point to EE DATA mem btfss BootBits,0 ; Check from read or write goto BootEERD ; Skip the WR if we were going for a read bsf EECON1,WREN ; Enable writes movlw 55h movwf EECON2 movlw 0AAh movwf EECON2 ; Unlock write operation bsf EECON1,WR ; And start a write cycle BootWRLoop btfsc EECON1,WR ; This executes for EE only not flash, waits for WR to finish goto BootWRLoop ; These two instructions gets NOPed when flashing program memory bcf EECON1,WREN ; Finally disable writes again ; Here we read the data back again, can be used as verify BootEERD bsf EECON1,RD ; Start a read cycle nop ; Only necessary for flash read, same thing as when writing above nop ; Except I could use the two words for something useful there.. :) BootEEX bcf STATUS,RP0 ; Back to bank 2 movf EEDATA,w ; Store our EE-data movwf BootDataL movf EEDATH,w movwf BootDataH bcf STATUS,RP1 ; And finally back to bank 0 return ; To produce compact code the end zero byte has to be in the LSB (that means an even number of chars in every string) BootStartText DA "WJ PIC16F876A Boot Loader - press ESC to flash...\x00" BootFlashText DA "\r\nSend INHX8 file now...\r\x00" BootRunText DA "\r\nExiting loader\r\x00" ;---------------------------------------------------------------------- ; EE Data (256 bytes), located at 2100h org 2100h ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh END