TITLE "pp88.asm: serial pic16f88 programmer, @4.0MHz" LIST p=12F675 ERRORLEVEL 0, -302 ; This is a simple and low cost solution for programming the PIC16F88. ; No host software is needed! Just copy the hex file to the serial port. ; (there are some simple rules to be followed see "Limits" below) ; ; RS323 setting is 9600 bps, 8 data bits, no parity, 1 stop bit. ; Programming is not verified; data errors are signalled by a blinking LED. ; ; When a hex file is sent to the programmer the PIC16F88 is completely ; erased and reprogrammed. When the the character 'v' is sent previously ; the PIC is only read and verified against the hexfile. ; ; Programming (linux: cat myfile.hex > /dev/ttyS0) ; ----------- ; The LED is off during programming (saves power) and on when complete. ; ; Verifying (linux: echo v > /dev/ttyS0 ; cat myfile.hex > /dev/ttyS0) ; --------- ; The LED is flickering and turned on after successful verification. ; ; Errors ; ------ ; LED blinks slowly (2 times/second): hex format or chip verification error. ; LED blinks fastly (4 times/second): receive buffer overrun. ; (the programmer is ready when the LED is blinking: no reset is needed) ; LED remains off: "end of file" missed (socket still powered). ; ; Limits ; ------ ; Hex file records must be ordered by ascending addresses with embedded ; configuration at the end. This is normally the case when gpasm is used. ; Records must start on 4 word boundaries (ORG H'..0', ..4, ..8 or ..C). ; EEPROM is erased but not programmed. Only inhx32 format is supported. ; ; Large memory gaps within program code must be avoided (trailing unused ; program memory is no problem). The receive buffer will overrun when too ; many addresses must be skipped. Large gaps are common between program ; code and lookup tables. A possibility to reduce the size of such gaps is ; to define eight words at the beginning of unused 256-word blocks e.g.: ; ; ORG H'0300' ; DT 0,0,0,0,0,0,0,0 ; ; Host software or a hexfile converter could also help here, but then ; the programmer is no longer operated without additional software. ; ; * USE AT YOUR OWN RISK * THERE IS ABSOLUTELY NO WARRANTY OF ANY KIND * ; Supply: VPP = 12V (8.5V .. 13.5V; selfpowering from RS232 may work) ; ; +12V >-----+---------------------> +12V ; | ------- ; +---| 78L05 |---+-----> +5V ; | ------- | ; 10uF | 10uF ; | | | ; GND ------+-------+-------+------ GND ; ; ; Programmer: ; -------___------- ; | | ; +5V >------------1-|VCC GND|-8------------- GND ; | | ; (PWR) <------------2-|GP5 GP0|-7-----------<> (DAT) ; | | ; GND---LED--470R----3-|GP4 GP1|-6------------> (CLK) ; | | ; RS232 +--4-|GP3 GP2|-5--+ +---< +12V ; (9pin f.) | | PIC12F675 | | | ; +--1 DCD | ----------------- | 4k7 ; | 2 TXD 22k | | | ; | 3 RXD >---\/\/\--+-----+ 4k7 +---> (VPP) ; +--4 DTR | | | ; | 5 GND -----------+ --- 4V7 Zehner: | |- ; +--6 DSR | /_\ GP3 has no upper +---| BC107 ; 7 RTS --+ | | limitting diode |-> ; 8 CTS --+ | | | ; 9 RI +-----+---------------------------+---- GND ; ; ; Target: ; -------___------- ; | | ; 1-|A2 A1|-18 ; | | ; 2-|A3 A0|-17 ; | | ; 3-|A4 A7|-16 ; | | ; (VPP)---------4-|A5 A6|-15 ; | | ; GND----------5-|VSS VDD|-14----------(PWR) ; | | ; 6-|B0 B7|-13----------(DAT) ; | | ; 7-|B1 B6|-12----------(CLK) ; | | ; 8-|B2 B5|-11 ; | | ; 9-|B3 B4|-10 ; | PIC16F88 | ; ----------------- ; __IDLOCS H'BB88' ; the baby's name __CONFIG H'01C4' ; no CP, BOD, no MCLR, no WDT, internal osc INDF EQU 0 ; indirect file access TMR0 EQU 1 ; timer 0 PCL EQU 2 ; program counter lsb STATUS EQU 3 ; program status FSR EQU 4 ; file selection register GPIO EQU 5 ; i/o port PCLATH EQU H'0A' ; program counter msb INTCON EQU H'0B' ; interrupt control PIR1 EQU H'0C' ; peripheral interrupt flags TMR1L EQU H'0E' ; timer 1 lower byte TMR1H EQU H'0F' ; timer 1 higher byte T1CON EQU H'10' ; timer 1 control CMCON EQU H'19' ; comparator control ADRESH EQU H'1E' ; most significant ADC bits ADCON EQU H'1F' ; ad control _OPTION EQU H'81' ; option register (bank 1) _TRISIO EQU H'85' ; gpio tristate register (bank 1) _PIE1 EQU H'8C' ; peripheral interrupt enable (bank 1) _OSCCAL EQU H'90' ; oscillator calibration (bank 1) _WPU EQU H'95' ; weak pullup enables (bank 1) _IOC EQU H'96' ; interrupt on change enables (bank 1) _VRCON EQU H'99' ; voltage reference control (bank 1) _EEDATA EQU H'9A' ; eeprom data (bank 1) _EEADR EQU H'9B' ; eeprom address (bank 1) _EECON1 EQU H'9C' ; eeprom control (bank 1) _EECON2 EQU H'9D' ; eeprom control (bank 1) _ADRESL EQU H'9E' ; least significant ADC bits (bank 1) _ANSEL EQU H'9F' ; analog select (bank 1) C EQU 0 ; carry DC EQU 1 ; digit carry Z EQU 2 ; zero PD EQU 3 ; power down TO EQU 4 ; watch dog time out RP0 EQU 5 ; register bank select DAT EQU 0 ; GPIO pins CLK EQU 1 VPP EQU 2 RXD EQU 3 LED EQU 4 TXD EQU 4 PWR EQU 5 ORG H'20' ; RAM (64 Bytes) flgs RES 1 ; 7=error 6=blink 3=data 0=verify scnt RES 1 ; sequence counter rxsh RES 1 ; receiver shift register tmp0 RES 1 ; temporary storage tmp1 RES 1 ; temporary storage tmp2 RES 1 ; temporary storage cod0 RES 1 ; ls 7 bits of code data cod1 RES 1 ; ms 7 bits of code data pdat RES 1 ; programming write data pver RES 1 ; programming read data cmnd RES 1 ; command character bcnt RES 1 ; byte counter rtyp RES 1 ; record type chks RES 1 ; checksum adrl RES 1 ; address low byte adrh RES 1 ; address high byte datl RES 1 ; current word low byte dath RES 1 ; current word high byte calo RES 1 ; current address low byte cahi RES 1 ; current address high byte malo RES 1 ; memory address low byte mahi RES 1 ; memory address high byte rptr RES 1 ; command buffer read pointer wptr RES 1 ; command buffer write pointer ORG H'40' ; buff RES 32 ; command buffer ORG H'000' ; reset GOTO main ; ORG H'010' ; main CLRF GPIO ; BSF GPIO,VPP ; VPP off as soon as possible BSF STATUS,RP0 ; bank 1 MOVLW B'00001000' ; rxd=input, others are outputs MOVWF _TRISIO ; CLRF _ANSEL ; all analog pins digital MOVLW B'11011111' ; no pu, risint, tmr0 iclk, fall, wdt:128 MOVWF _OPTION ; CALL H'3FF' ; oscillator calibration MOVWF _OSCCAL ; BCF STATUS,RP0 ; bank 0 MOVLW B'11111111' ; comparator off MOVWF CMCON ; CLRF INTCON ; no interrupts used CLRF flgs ; clear flags ppdone MOVLW H'21' ; clear other vars MOVWF FSR initvar CLRF 0 INCF FSR,F MOVLW H'60' XORWF FSR,W BTFSS STATUS,Z GOTO initvar ; end init vars BSF GPIO,VPP ; VPP off BCF GPIO,PWR ; PWR off BSF GPIO,LED ; LED on RRF flgs,F ; copy error to blink flag MOVLW H'40' ; and clear other flags ANDWF flgs,F loop0 CALL rxbyte ; wait for ':', check for 'v' MOVWF cmnd XORLW 'v' ; verify BTFSC STATUS,Z BSF flgs,0 MOVF cmnd,W XORLW ':' ; start BTFSS STATUS,Z GOTO loop0 ; rx next byte prog1 BCF GPIO,LED ; LED off (start programming) BSF GPIO,PWR ; PWR on BCF flgs,6 ; clear blink flag MOVLW B'00110001' ; tmr1: internal clock / 8 MOVWF T1CON ; MOVLW H'E6' ; initialisation sequence MOVWF scnt ; BCF GPIO,VPP ; VPP on (enter programming mode) BTFSC flgs,0 ; skip erase if verifying GOTO prog3 MOVLW B'00011111' ; "Chip Erase" CALL wrbuf MOVLW B'00101010' ; 10ms delay CALL wrbuf GOTO prog3 prog2 CALL rxbyte ; wait for ':' XORLW ':' BTFSS STATUS,Z GOTO prog2 prog3 CLRF chks ; clear checksum CALL rxnum MOVWF bcnt ; count BCF STATUS,C ; count / 2 RRF bcnt,F CALL rxnum MOVWF adrh ; address high byte CALL rxnum MOVWF adrl ; address low byte BCF STATUS,C ; adrh:adrl / 2 RRF adrh,F RRF adrl,F CALL rxnum MOVWF rtyp ; record type DECF rtyp,W ; type == 01 ? (eof) BTFSC STATUS,Z GOTO ppend ; ok! MOVF rtyp,W ; type != 00 ? (unknown) BTFSS STATUS,Z GOTO prog2 MOVF bcnt,W ; count == 00 ? BTFSC STATUS,Z GOTO prog2 MOVLW B'01100000' ; set address CALL wrbuf MOVF adrl,W CALL wrbuf MOVF adrh,W CALL wrbuf prog4 CALL rxnum MOVWF datl ; 8 ls bits CALL rxnum MOVWF dath ; 6 ms bits BCF STATUS,C ; shift for start/stop bit RLF datl,F RLF dath,F MOVLW B'10000010' ; "Load Data" when programming BTFSC flgs,0 MOVLW B'10000100' ; or "Read Data" when verifying CALL wrbuf MOVF datl,W ; code word to buff CALL wrbuf MOVF dath,W CALL wrbuf MOVLW B'10100000' ; increment address CALL wrbuf DECFSZ bcnt,F ; all data processed? GOTO prog4 CALL rxnum ; checksum MOVF chks,W BTFSS STATUS,Z BSF flgs,7 ; blink on checksum error GOTO prog2 ppend CALL rxbyte ; wait for buf empty MOVF rptr,W XORWF wptr,W BTFSS STATUS,Z GOTO ppend GOTO ppdone ; ok: prepare for new job wrbuf MOVWF tmp0 ; write to buffer MOVF wptr,W ADDLW buff MOVWF FSR MOVF tmp0,W MOVWF INDF INCF wptr,F BCF wptr,5 ; truncate to < 32 MOVF wptr,W ; check for buffer overrun XORWF rptr,W BTFSS STATUS,Z RETLW 0 BSF flgs,7 ; set blink flag MOVLW B'00100001' ; tmr1: internal clock / 4 MOVWF T1CON ; RETLW 0 rdbuf MOVF rptr,W ; command in buffer? XORWF wptr,W BTFSC STATUS,Z RETLW 0 ; buf is empty, Z = 1 MOVF rptr,W ADDLW buff MOVWF FSR MOVF INDF,W INCF rptr,F BCF rptr,5 ; truncate to < 32 BCF STATUS,Z RETURN ; byte available, Z = 0 ; pptask: (executed between bit receiving) ; if scnt > 0 decrement scnt and check sequence (-->ppsequ) ; else if ca != ma send increment address command and inc current address ; else if command = B'd00ccccc' command 0ccccc with two databytes if d set ; else if command = B'101xxxxx' inc ma, and "Start Programming" if required ; else if command = B'011xxxxx' set ma to next two bytes from buff ; else if command = B'001xtttt' set scnt to tttt * 8 (delay/endprog) pptask MOVF scnt,W ; check scnt BTFSC STATUS,Z GOTO ppaddr ; sequences: ; 0x7F..0x01 delay and send "End Programming" ; 0x82..0x81 send (or verify) two databytes from buffer ; 0xC5..0xC2 "Load Configuration", 0, 0, ("Begin Erase") ; 0xE6..0xE1 init: delay and send load data 0x01 ppsequ DECF scnt,F ; advance sequence BTFSS scnt,7 ; <0x7F ? : delay/endprog GOTO ppwait ppdata BTFSC scnt,6 ; 0x82 ? GOTO pplcon BTFSS scnt,0 ; first byte? CLRF scnt ; no: done BTFSC flgs,0 ; read or write? GOTO ppvery MOVF rptr,W ; inline rdbuf to save time ADDLW buff MOVWF FSR MOVF INDF,W INCF rptr,F BCF rptr,5 ; truncate to < 32 MOVWF pdat GOTO ppout8 pplcon BTFSC scnt,5 ; 0xC5 ? GOTO ppinit CLRF pdat ; "Load Configuration" or data 0 BTFSC scnt,2 GOTO ppout6 ; "Load Configuration" BTFSC scnt,1 GOTO ppout8 ; data 0 (2 steps) CLRF scnt ; this is the last step BTFSC flgs,0 ; verifying: do nothing RETLW 0 MOVLW D'20' ; delay 2.5ms MOVWF scnt MOVLW B'00001000' ; "Begin Erase" for id locations MOVWF pdat GOTO ppout6 ppinit MOVF scnt,W ADDLW H'1D' ; scnt-1 > 0xE2 BTFSC STATUS,C RETLW 0 ; yes: delay only BTFSC scnt,1 ; third last? GOTO ppinld BTFSC scnt,0 ; second last? GOTO ppinlo ppinhi CLRF scnt ; done CLRF pdat ; msb GOTO ppout8 ppinlo MOVLW H'02' ; lsb MOVWF pdat GOTO ppout8 ppinld MOVLW B'00000010' ; load data 0x01 (see programming spec) MOVWF pdat GOTO ppout6 ppwait MOVF scnt,W ; if scnt = 0 send endprog BTFSS STATUS,Z RETLW 0 MOVLW B'00010111' ; "End Programming" MOVWF pdat GOTO ppout6 ppaddr MOVF calo,W ; compare cahi:calo with mahi:malo XORWF malo,W BTFSS STATUS,Z GOTO ppckpa MOVF cahi,W XORWF mahi,W BTFSS STATUS,Z GOTO ppckpa CALL rdbuf ; check buffer BTFSC STATUS,Z RETLW 0 ; buf empty MOVWF pdat BTFSC pdat,5 ; check if command GOTO ppinad MOVLW H'82' ; two databytes if bit 7 set BTFSC pdat,7 MOVWF scnt GOTO ppout6 ; sent command ppinad BTFSS pdat,7 ; '101xxxxx' ? GOTO ppseta BSF flgs,3 ; data in this four word block INCF malo,F ; increment mahi:malo BTFSC STATUS,Z INCF mahi,F ppckpa MOVF malo,W ; check if four addresses done ANDLW H'03' BTFSC STATUS,Z GOTO ppckpr BTFSS cahi,5 ; or if 0x2008 reached GOTO ppinca BTFSS calo,3 GOTO ppinca ppckpr BTFSC flgs,3 ; program if data changed GOTO ppprog BTFSS mahi,5 ; config range requested? GOTO ppinca BTFSC cahi,5 ; not yet in config range? GOTO ppinca CLRF calo ; set cahi:calo = 0x2000 MOVLW H'20' MOVWF cahi MOVLW H'C5' ; set scnt for "Load Configuration" MOVWF scnt RETLW 0 ppprog BCF flgs,3 ; clear data change flag BTFSC flgs,0 ; no programming if verify flag set RETLW 0 MOVLW D'20' ; delay 2.5ms MOVWF scnt MOVLW B'00011000' ; "Begin Program Only" MOVWF pdat GOTO ppout6 ppinca INCF calo,F ; increment cahi:calo BTFSC STATUS,Z INCF cahi,F MOVLW B'00000110' ; "Increment Address" MOVWF pdat GOTO ppout6 ppseta BTFSS pdat,6 ; 011xxxxx ? GOTO pptime CALL rdbuf ; mahi:malo MOVWF malo CALL rdbuf MOVWF mahi RETLW 0 pptime MOVF pdat,W ; 5 lsb * 8 to scnt ANDLW H'0F' MOVWF scnt BCF STATUS,C RLF scnt,F RLF scnt,F RLF scnt,F RETLW 0 ;;; serial output to pic is linear code to save time ppout8 BTFSS pdat,0 ; check bit BCF GPIO,DAT ; DAT low BTFSC pdat,0 BSF GPIO,DAT ; DAT high BSF GPIO,CLK ; CLK high RRF pdat,F BCF GPIO,CLK ; CLK low ppout7 BTFSS pdat,0 ; check bit BCF GPIO,DAT ; DAT low BTFSC pdat,0 BSF GPIO,DAT ; DAT high BSF GPIO,CLK ; CLK high RRF pdat,F BCF GPIO,CLK ; CLK low ppout6 BTFSS pdat,0 ; check bit BCF GPIO,DAT ; DAT low BTFSC pdat,0 BSF GPIO,DAT ; DAT high BSF GPIO,CLK ; CLK high RRF pdat,F BCF GPIO,CLK ; CLK low ppout5 BTFSS pdat,0 ; check bit BCF GPIO,DAT ; DAT low BTFSC pdat,0 BSF GPIO,DAT ; DAT high BSF GPIO,CLK ; CLK high RRF pdat,F BCF GPIO,CLK ; CLK low ppout4 BTFSS pdat,0 ; check bit BCF GPIO,DAT ; DAT low BTFSC pdat,0 BSF GPIO,DAT ; DAT high BSF GPIO,CLK ; CLK high RRF pdat,F BCF GPIO,CLK ; CLK low ppout3 BTFSS pdat,0 ; check bit BCF GPIO,DAT ; DAT low BTFSC pdat,0 BSF GPIO,DAT ; DAT high BSF GPIO,CLK ; CLK high RRF pdat,F BCF GPIO,CLK ; CLK low ppout2 BTFSS pdat,0 ; check bit BCF GPIO,DAT ; DAT low BTFSC pdat,0 BSF GPIO,DAT ; DAT high BSF GPIO,CLK ; CLK high RRF pdat,F BCF GPIO,CLK ; CLK low ppout1 BTFSS pdat,0 ; check bit BCF GPIO,DAT ; DAT low BTFSC pdat,0 BSF GPIO,DAT ; DAT high BSF GPIO,CLK ; CLK high RRF pdat,F BCF GPIO,CLK ; CLK low RETLW 0 ppvery BSF GPIO,LED ; start LED flickering MOVF rptr,W ; inline rdbuf to save time ADDLW buff MOVWF FSR MOVF INDF,W INCF rptr,F BCF rptr,5 ; truncate to < 32 MOVWF pdat ppverlo BTFSS scnt,0 ; ls byte: tristate data, first bit GOTO ppver0 BSF STATUS,RP0 ; bank 1 BSF _TRISIO,DAT ; BCF STATUS,RP0 ; bank 0 BSF GPIO,CLK ; CLK high BCF STATUS,C NOP ; ignore data line BCF GPIO,CLK ; CLK low RRF pver,F ; shift in first bit ppver0 BSF GPIO,CLK ; CLK high BCF STATUS,C BTFSC GPIO,DAT ; poll data line BSF STATUS,C BCF GPIO,CLK ; CLK low RRF pver,F ; shift in data bit 1 BSF GPIO,CLK ; CLK high BCF STATUS,C BTFSC GPIO,DAT ; poll data line BSF STATUS,C BCF GPIO,CLK ; CLK low RRF pver,F ; shift in data bit 2 BSF GPIO,CLK ; CLK high BCF STATUS,C BTFSC GPIO,DAT ; poll data line BSF STATUS,C BCF GPIO,CLK ; CLK low RRF pver,F ; shift in data bit 3 BSF GPIO,CLK ; CLK high BCF STATUS,C BTFSC GPIO,DAT ; poll data line BSF STATUS,C BCF GPIO,CLK ; CLK low RRF pver,F ; shift in data bit 4 BSF GPIO,CLK ; CLK high BCF STATUS,C BTFSC GPIO,DAT ; poll data line BSF STATUS,C BCF GPIO,CLK ; CLK low RRF pver,F ; shift in data bit 5 BSF GPIO,CLK ; CLK high BCF STATUS,C BTFSC GPIO,DAT ; poll data line BSF STATUS,C BCF GPIO,CLK ; CLK low RRF pver,F ; shift in data bit 6 BSF GPIO,CLK ; CLK high BCF STATUS,C BTFSC GPIO,DAT ; poll data line BSF STATUS,C BCF GPIO,CLK ; CLK low RRF pver,F ; shift in data bit 7 ppverhi BTFSC scnt,0 ; ms byte: last bit, drive data GOTO ppver1 BSF GPIO,CLK ; CLK high BCF STATUS,C NOP ; ignore data line BCF GPIO,CLK ; CLK low RRF pver,F ; shift in last bit BSF STATUS,RP0 ; bank 1 BCF _TRISIO,DAT ; BCF STATUS,RP0 ; bank 0 ppver1 MOVF pver,W ; verify data XORWF pdat,W BTFSS STATUS,Z BSF flgs,7 ; set blink flag BCF GPIO,LED ; end LED flickering RETLW 0 blink BTFSS TMR1H,7 ; blink width timer 1 BSF GPIO,LED BTFSC TMR1H,7 BCF GPIO,LED RETLW 0 ;;; Attention! rxbyte must be called before a new startbit occurs: ;;; --> after receiving a char about 120 cycles are left for other tasks ;;; --> between bit polling pptask executes commands from buff rxbyte BTFSC GPIO,RXD ; polled receive 9600bps @4MHz GOTO rxbyte ; wait for idle RX line rxstrt BTFSC flgs,6 ; blink LED? CALL blink ; BTFSS GPIO,RXD ; wait for start bit GOTO rxstrt ; CLRF rxsh ; BSF rxsh,7 ; shift in startbit MOVLW -D'148' ; set timer to middle of first bit MOVWF TMR0 ; rxnext BCF INTCON,2 ; clear timer overflow flag CALL pptask ; max. 90 cycles ! rxbit BTFSS INTCON,2 ; wait for bit timer GOTO rxbit ; MOVLW -D'102' ; set bit timer ADDWF TMR0,F ; BCF STATUS,C ; poll rx-line BTFSS GPIO,RXD ; BSF STATUS,C ; RRF rxsh,F ; shift in new bit BTFSS STATUS,C ; startbit rotated to carry? GOTO rxnext ; MOVF rxsh,W ; RETURN ; rxhex CALL rxbyte ; receive hex char BTFSC rxsh,6 ADDLW H'09' ANDLW H'0F' RETURN rxnum CALL rxhex ; receive two digit hex byte MOVWF tmp1 SWAPF tmp1,F CALL rxhex IORWF tmp1,W ADDWF chks,F ; update checksum RETURN END;