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:
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.
18F24K22 specific sequences
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
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
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