Attempting low voltage programming (MCLR) on PIC18F24K22 and failing...

Thread Starter

geekoftheweek

Joined Oct 6, 2013
1,219
A little background...

Due to my PICKit2 going up in smoke I thought I would give a try at programming a programmer. I have a PIC18F2550 set up to reprogram itself on the fly along with a few other things besides the latest task of being a programmer.

The 18F2550 is connected up to a PIC18F24K22 and with the help of a home brewed logic analyzer I can see that so far it seems things are working somewhat the way they should. The analyzer is an extremely basic design and only 177 kHz so it may be missing important details, but I'm hoping that is not the issue at the moment...

Following the programming specification (programming spec here) I'm trying to read the device id from the PIC as a way to decide if it is in program mode or not.

The 18F24K22 is fresh out of the box so the low voltage mode is still enabled from the factory. I have 1K resistors between the 18F2550 and the PGD and PGC pins on the 18F24K22 to prevent any short circuits so I know I haven't destroyed anything yet. The MCLR is has a 10K pull up resistor to 5V and it then is pulled low by a NFET controlled by the 18F2550 when needed.

What seems to be happening is the 18F2550 is doing exactly what it is programmed to do so far. It sends the enter low voltage program mode sequence, toggles the MCLR pin when it needs to, and sends commands like it should. The 18F24K22 on the other hand is either is not entering low voltage program mode or not getting what it expects as input and is not responding like it should.

Maybe I just need to stare at the program longer, but thought maybe a fresh set of eyes could spot any problems.

Timer 3 is used to make the clock cycles as even as possible (it probably doesn't matter as far as I can tell, but it helps make it show up on the analyzer), and add delays when needed.

Relevant code:

Code:
movlfa macro literal, reg
	movlw literal
	movwf reg, a
	endm

caseGotoA macro literal, reg, new_pc
    movlw literal
    xorwf reg, w
    btfsc STATUS, Z
    goto new_pc
    endm

setAndWaitForTimer3 macro literalh, literall    
    bsf T3CON, RD16
    bsf T3CON, TMR3ON
    movlfa literalh, TMR3H
    movlfa literall, TMR3L
    bcf PIR2, TMR3IF
    btfss PIR2, TMR3IF
    bra $-2
    clrf T3CON
    endm
The first step in the process is __programmer_get_devid gets called and from there it goes where it needs to. It does some back and forth between the code blocks which I apologize in advance for making you scroll back and forth.

Code:
; gets called first to start process of reading the PIC device id / revision bytes -- in next code block
    
__programmer_get_devid
    caseGotoA PROGRAMMER_18F24K22, programmer_pic_part, __programmer_get_devid_18f2xk22
    
    
; don't return... pic wasn't found

; ----------------------------------------    
    
__programmer_invalid_pic
    clrf programmer_pic_part
    movlfa SL_INVALID_PIC, status_led_code 
    return

; ---------------------------------------------------------------------------------------------------------------------------------------

__programmer_write_bytes_lsb_first
    bsf programmer_flags, PF_LSB_FIRST
    bra .programmer_write_bytes_main_loop
    
; ---------------------------------------------------------------------------------------------------------------------------------------

__programmer_write_bytes_msb_first
    bcf programmer_flags, PF_LSB_FIRST
        
.programmer_write_bytes_main_loop
    lfsr 0, programmer_buffer_out  ; set buffer 
    
; byte loop
.programmer_write_bytes_main_loop_loop
    movlfa 8, programmer_bits_left ; set number of bits
    
    movff POSTINC0, programmer_temp_byte ; set up bit buffer
    call __programmer_write_bits    ; write the bits
    decfsz programmer_buffer_len, f ; see if more bytes to write
    bra .programmer_write_bytes_main_loop_loop
    
    bcf programmer_flags, PF_LSB_FIRST
    return    
    
; ---------------------------------------------------------------------------------------------------------------------------------------

; writes a 4 bit command and 2 byte payload lsb first

__programmer_write_4_bit_command_16_bit_data_lsb_first  ; buffer needs set
    lfsr 0, programmer_buffer_out
    
__programmer_write_4_bit_command_16_bit_data_lsb_first_fsr_set  ; buffer already set
    bsf programmer_flags, PF_LSB_FIRST
    clrf programmer_bytes_out   ; used to keep track of if 4 or 8 bits to send
    bra .programmer_write_4_bit_command_16_bit_data  ; branch to start writing
    
; ---------------------------------------------------------------------------------------------------------------------------------------

; writes a 4 bit command and 2 byte payload msb first

__programmer_write_4_bit_command_16_bit_data_msb_first ; buffer needs set 
    lfsr 0, programmer_buffer_out
    
__programmer_write_4_bit_command_16_bit_data_msb_first_fsr_set  ; buffer already set
    bcf programmer_flags, PF_LSB_FIRST        
    clrf programmer_bytes_out ; used to keep track of if 4 or 8 bits to send

; ----------------------------------------    

; start writing...
; find out if 4 or 8 bits to send
.programmer_write_4_bit_command_16_bit_data
    ; if programmer_bytes_out == 0 then 4 bit otherwise 8 bit
    tstfsz programmer_bytes_out
    bra .programmer_write_4_bit_command_16_bit_data_8_bits
    
    movlfa 4, programmer_bits_left ; set to 4 bits
    bra .programmer_write_4_bit_command_16_bit_data_bit_count_set ; branch to start writing 

.programmer_write_4_bit_command_16_bit_data_8_bits    
    movlfa 8, programmer_bits_left ; set to 8 bits
    
; ----------------------------------------    

.programmer_write_4_bit_command_16_bit_data_bit_count_set
    movff POSTINC0, programmer_temp_byte ; move from buffer to temp byte
    
    ; write bits from programmer_temp_byte    
    ; bit count in programmer_bits_left
    call __programmer_write_bits 
    
    incf programmer_bytes_out, f ; increment programmer_bytes_out
    
    ; if programmer_bytes_out == 3 reset to 0 to send 4 bits next time around
    movlw 3
    xorwf programmer_bytes_out, w
    btfsc STATUS, Z
    clrf programmer_bytes_out
    
    ; see if any more bytes to write
    decfsz programmer_buffer_len, f
    bra .programmer_write_4_bit_command_16_bit_data
    return
    
; ---------------------------------------------------------------------------------------------------------------------------------------

; writes a 4 bit command and 1 byte payload lsb first

__programmer_write_4_bit_command_8_bit_data_lsb_first  ; buffer needs set
    lfsr 0, programmer_buffer_out
    
__programmer_write_4_bit_command_8_bit_data_lsb_first_fsr_set  ; buffer already set    
    bsf programmer_flags, PF_LSB_FIRST
    clrf programmer_bytes_out ; used to keep track of if 4 or 8 bits to send
    bra .programmer_write_4_bit_command_8_bit_data   ; branch to start writing
    
; ---------------------------------------------------------------------------------------------------------------------------------------

__programmer_write_4_bit_command_8_bit_data_msb_first  ; buffer needs set
    lfsr 0, programmer_buffer_out
    
__programmer_write_4_bit_command_8_bit_data_msb_first_fsr_set ; buffer already set    
    bcf programmer_flags, PF_LSB_FIRST        
    clrf programmer_bytes_out ; used to keep track of if 4 or 8 bits to send

; ----------------------------------------    

; start writing...    
; find out if 4 or 8 bits to send
.programmer_write_4_bit_command_8_bit_data
    ; if programmer_bytes_out == 0 then 4 bit otherwise 8 bit
    tstfsz programmer_bytes_out
    bra .programmer_write_4_bit_command_8_bit_data_8_bits
    
    movlfa 4, programmer_bits_left ; set to 4 bits
    bra .programmer_write_4_bit_command_8_bit_data_bit_count_set
    
.programmer_write_4_bit_command_8_bit_data_8_bits    
    movlfa 8, programmer_bits_left ; set to 8 bits
  
; ----------------------------------------  

.programmer_write_4_bit_command_8_bit_data_bit_count_set
    movff POSTINC0, programmer_temp_byte ; move from buffer to temp byte
    call __programmer_write_bits  ; write the bits
    incf programmer_bytes_out, f
    
    ; if programmer_bytes_out == 2 reset to 0 to send 4 bits next time around
    btfsc programmer_bytes_out, 1
    clrf programmer_bytes_out
    
    ; see if more bytes left
    decfsz programmer_buffer_len, f
    bra .programmer_write_4_bit_command_8_bit_data
    return
    
; ---------------------------------------------------------------------------------------------------------------------------------------

__programmer_write_bits
; find out if lsb or msb first
    btfsc programmer_flags, PF_LSB_FIRST
    bra .programmer_write_bits_lsb_first_loop
    
; ----------------------------------------
; msb first
.programmer_write_bits_msb_first_loop
    ; wait half of a low cycle and set clock high
    setAndWaitForTimer3 CLOCK_HALF_CYCLE_H,CLOCK_HALF_CYCLE_L    
    bsf PROGRAMMER_CLOCK_LATCH

    ; wait half of a high cycle and set data bit
    setAndWaitForTimer3 CLOCK_HALF_CYCLE_H,CLOCK_HALF_CYCLE_L
    btfss programmer_temp_byte, 7
    bcf PROGRAMMER_DATA_LATCH
    btfsc programmer_temp_byte, 7
    bsf PROGRAMMER_DATA_LATCH

    ; wait half of a high cycle then set clock low
    setAndWaitForTimer3 CLOCK_HALF_CYCLE_H,CLOCK_HALF_CYCLE_L
    bcf PROGRAMMER_CLOCK_LATCH
    
    ; wait half of a low cycle then loop 
    setAndWaitForTimer3 CLOCK_HALF_CYCLE_H,CLOCK_HALF_CYCLE_L
    
    ; shift output byte left for next bit
    rlncf programmer_temp_byte, f
    
    ; see if more bits to write
    decfsz programmer_bits_left, f
    bra .programmer_write_bits_msb_first_loop
    
    ; wait half of a total clock cycle (to help with seeing the end in analyzer)
    setAndWaitForTimer3 CLOCK_HALF_CYCLE_H,CLOCK_HALF_CYCLE_L
    setAndWaitForTimer3 CLOCK_HALF_CYCLE_H,CLOCK_HALF_CYCLE_L
    return

; ----------------------------------------
; lsb first 
.programmer_write_bits_lsb_first_loop
    ; wait half of a low cycle and set clock high
    setAndWaitForTimer3 CLOCK_HALF_CYCLE_H,CLOCK_HALF_CYCLE_L
    bsf PROGRAMMER_CLOCK_LATCH

    ; wait half of a high cycle and read data bit    
    setAndWaitForTimer3 CLOCK_HALF_CYCLE_H,CLOCK_HALF_CYCLE_L    
    btfss programmer_temp_byte, 0
    bcf PROGRAMMER_DATA_LATCH
    btfsc programmer_temp_byte, 0
    bsf PROGRAMMER_DATA_LATCH
    
    ; wait half of a high cycle then set clock low    
    setAndWaitForTimer3 CLOCK_HALF_CYCLE_H,CLOCK_HALF_CYCLE_L
    bcf PROGRAMMER_CLOCK_LATCH
    setAndWaitForTimer3 CLOCK_HALF_CYCLE_H,CLOCK_HALF_CYCLE_L

    ; shift output byte right for next bit
    rrncf programmer_temp_byte, f    
    
    ; see if more bits to write
    decfsz programmer_bits_left, f
    bra .programmer_write_bits_lsb_first_loop

    ; wait half of a total clock cycle (to help with seeing the end in analyzer)    
    setAndWaitForTimer3 CLOCK_HALF_CYCLE_H,CLOCK_HALF_CYCLE_L
    setAndWaitForTimer3 CLOCK_HALF_CYCLE_H,CLOCK_HALF_CYCLE_L
    return
    
; ---------------------------------------------------------------------------------------------------------------------------------------

__programmer_read_bytes_lsb_first 
    lfsr 1, programmer_buffer_in
    bsf PROGRAMMER_DATA_TRIS    ; set data direction to in
    

__programmer_read_bytes_lsb_first_fsr_set
    bsf programmer_flags, PF_LSB_FIRST
    bra .programmer_write_bytes_main_loop
    
; ---------------------------------------------------------------------------------------------------------------------------------------

__programmer_read_bytes_msb_first
    lfsr 1, programmer_buffer_in
    bsf PROGRAMMER_DATA_TRIS

__programmer_read_bytes_msb_first_fsr_set
    bcf programmer_flags, PF_LSB_FIRST

; ----------------------------------------
; start reading

.programmer_read_bytes_main_loop
    movlfa 8, programmer_bits_left  ; set bit count
    call __programmer_read_bits ; read bits
    movff programmer_temp_byte, POSTINC1 ; store returned bits
    decfsz programmer_buffer_len, f ; see if more bytes to read
    bra .programmer_read_bytes_main_loop
    
    bcf programmer_flags, PF_LSB_FIRST
    bcf PROGRAMMER_DATA_TRIS  ; set data direction to out
    return    

; ---------------------------------------------------------------------------------------------------------------------------------------

__programmer_read_bits
    clrf programmer_temp_byte
    btfsc programmer_flags, PF_LSB_FIRST
    bra .programmer_read_bits_lsb_first_loop
    

.programmer_read_bits_msb_first_loop
    ; wait half of a low cycle and set clock high
    setAndWaitForTimer3 CLOCK_HALF_CYCLE_H,CLOCK_HALF_CYCLE_L
    bsf PROGRAMMER_CLOCK_LATCH
    
    ; wait half of a high cycle and read bit
    setAndWaitForTimer3 CLOCK_HALF_CYCLE_H,CLOCK_HALF_CYCLE_L
    btfss PROGRAMMER_DATA_PORT
    bcf programmer_temp_byte, 7
    btfsc PROGRAMMER_DATA_PORT
    bsf programmer_temp_byte, 7
    
    ; wait half of a high cycle and set clock low
    setAndWaitForTimer3 CLOCK_HALF_CYCLE_H,CLOCK_HALF_CYCLE_L
    bcf PROGRAMMER_CLOCK_LATCH
    
    rrncf programmer_temp_byte, f ; rotate bits right
    
    ; wait half of a low cycle
    setAndWaitForTimer3 CLOCK_HALF_CYCLE_H,CLOCK_HALF_CYCLE_L
    
    ;see if more bits to read
    decfsz programmer_bits_left, f
    bra .programmer_read_bits_msb_first_loop
    
    ; shift input back left one bit (shifts too many times in loop)
    rlncf programmer_temp_byte, f    
    
    ; wait half of a total clock cycle (to help with seeing the end in analyzer)    
    setAndWaitForTimer3 CLOCK_HALF_CYCLE_H,CLOCK_HALF_CYCLE_L
    setAndWaitForTimer3 CLOCK_HALF_CYCLE_H,CLOCK_HALF_CYCLE_L
    return
   
.programmer_read_bits_lsb_first_loop
    ; wait half of a low cycle and set clock high
    setAndWaitForTimer3 CLOCK_HALF_CYCLE_H,CLOCK_HALF_CYCLE_L
    bsf PROGRAMMER_CLOCK_LATCH
    
    ; wait half of a high cycle and read bit
    setAndWaitForTimer3 CLOCK_HALF_CYCLE_H,CLOCK_HALF_CYCLE_L
    btfss PROGRAMMER_DATA_PORT
    bcf programmer_temp_byte, 0
    btfsc PROGRAMMER_DATA_PORT
    bsf programmer_temp_byte, 0
    
    ; wait half of a high cycle and set clock low
    setAndWaitForTimer3 CLOCK_HALF_CYCLE_H,CLOCK_HALF_CYCLE_L
    bcf PROGRAMMER_CLOCK_LATCH
    
    rlncf programmer_temp_byte, f ; rotate bits left
    
    ; wait half of a low cycle
    setAndWaitForTimer3 CLOCK_HALF_CYCLE_H,CLOCK_HALF_CYCLE_L
    
    ;see if more bits to read
    decfsz programmer_bits_left, f
    bra .programmer_read_bits_lsb_first_loop
    
    ; shift input back right one bit (shifts too many times in loop)
    rrncf programmer_temp_byte, f    
    
    ; wait half of a total clock cycle (to help with seeing the end in analyzer)    
    setAndWaitForTimer3 CLOCK_HALF_CYCLE_H,CLOCK_HALF_CYCLE_L
    setAndWaitForTimer3 CLOCK_HALF_CYCLE_H,CLOCK_HALF_CYCLE_L
    return
18F24K22 specific sequences

Code:
; __programmer_get_devid branches to here...
; attempt to enter low voltage program mode and read device id and revision bytes
; section 2.6 of programming specification (pg 11) -- enter low volt program mode

__programmer_get_devid_18f2xk22
    ; set up initial state of TRIS and LAT for programming pins
    bcf PROGRAMMER_CLOCK_LATCH
    bcf PROGRAMMER_CLOCK_TRIS
    bcf PROGRAMMER_DATA_LATCH
    bcf PROGRAMMER_DATA_TRIS
    bcf PROGRAMMER_MCLR_LATCH
    
    ; wait for half a low clock cycle (makes it visible in analyzer)
    setAndWaitForTimer3 CLOCK_HALF_CYCLE_H,CLOCK_HALF_CYCLE_L

    ; toggle MCLR  -- set up as open drain on nfet gate -- bcf releases MCLR
    
    bcf PROGRAMMER_MCLR_TRIS ; bcf releases MCLR

    setAndWaitForTimer3 CLOCK_HALF_CYCLE_H,CLOCK_HALF_CYCLE_L
    
    bsf PROGRAMMER_MCLR_TRIS ; bsf pulls MCLR low
    
    ; wait 1 ms
    setAndWaitForTimer3 0xd1, 0x18

    ; send enter low volt program mode sequence
    lfsr 0, programmer_buffer_out
    movlfa 0x4d, POSTINC0
    movlfa 0x43, POSTINC0
    movlfa 0x48, POSTINC0
    movlfa 0x50, POSTINC0
    movlfa 4, programmer_buffer_len
    call __programmer_write_bytes_msb_first
    
    ; release MCLR
    bcf PROGRAMMER_MCLR_TRIS
    
    ; wait 400 usec
    setAndWaitForTimer3 0xed, 0x38

    ; set up read code memory sequence 
    ; section 4 of programming specification (pg 24)
    lfsr 0, programmer_buffer_out
    movlfa 0x00, POSTINC0   ; 4 bit command
    movlfa 0x0e, POSTINC0   ; 1 st payload byte
    movlfa 0x3f, POSTINC0   ; 2 nd payload byte
        
    movlfa 0x00, POSTINC0   ; 4 bit command
    movlfa 0x6e, POSTINC0   ; 1 st payload byte
    movlfa 0xf8, POSTINC0   ; 2 nd payload byte

    movlfa 0x00, POSTINC0
    movlfa 0x0e, POSTINC0
    movlfa 0xff, POSTINC0
    
    movlfa 0x00, POSTINC0
    movlfa 0x6e, POSTINC0
    movlfa 0xf7, POSTINC0

    movlfa 0x00, POSTINC0
    movlfa 0x0e, POSTINC0
    movlfa 0xfe, POSTINC0
    
    movlfa 0x00, POSTINC0
    movlfa 0x6e, POSTINC0
    movlfa 0xf6, POSTINC0
    
    ; send read code memory sequence to PIC
    movlfa 18, programmer_buffer_len
    call __programmer_write_4_bit_command_16_bit_data_lsb_first
    
    ; set up buffer for incoming from PIC    
    lfsr 1, programmer_buffer_in 
    
    ; set up buffer for outgoing to PIC    
    lfsr 0, programmer_buffer_out 
    movlfa 0x09, POSTINC0   ; tblrd *+ command
    clrf POSTINC0           ; payload byte

    movlfa 2, temp_count ; 2 bytes to read for full device id / revision
    
; command / read loop
.programmer_get_devid_18f2xk22_devid_loop
    movlfa 2, programmer_buffer_len ; write 2 bytes to PIC

    ; send tblrd *+ to pic    
    call __programmer_write_4_bit_command_8_bit_data_lsb_first

    movlfa 1, programmer_buffer_len ; read 1 byte from PIC
    ; read byte from PIC
    call __programmer_read_bytes_lsb_first_fsr_set ; skip setup of FSR (keep adding to earlier)
    
    decfsz temp_count, f ; loop if bytes left to read
    bra .programmer_get_devid_18f2xk22_devid_loop
    
    
    
; toggle MCLR to exit low voltage programming mode
    bsf PROGRAMMER_MCLR_TRIS ; bsf pulls MCLR low
    setAndWaitForTimer3 CLOCK_HALF_CYCLE_H, CLOCK_HALF_CYCLE_L
    bsf PROGRAMMER_CLOCK_TRIS
    bsf PROGRAMMER_DATA_TRIS
    setAndWaitForTimer3 CLOCK_HALF_CYCLE_H, CLOCK_HALF_CYCLE_L
    bcf PROGRAMMER_MCLR_TRIS ; bcf releases MCLR
    
    return
 

Thread Starter

geekoftheweek

Joined Oct 6, 2013
1,219
I found a goto in the read part of the programming pins that made the program go back to a write section. I also found out I have to reverse the order of the command payload bytes.

I added a high voltage MCLR function and after some testing found things still aren't working out that way either. Swapped out the PIC for another new one just in case I somehow did something that trashed it and found no change. I ran across the usbpicprog project and think I'll look into that for ideas.

Thanks all for taking a look. I'm going to get this figured out, but in the meantime maybe a new PICKit would be quicker...
 
Top