# Pic16F628 + portexpander I2C

Status
Not open for further replies.

#### kowey

##### New Member
A humble request from a new visitor!
For a couple of days now, I have been trying to connect a PIC16F628 with a port expander (Philips PCF8574) via I2C. Alas, without succes. Hardware looks OK, the port expander works well (with arduino), the pic is OK: mplab, pickit2, win2K. Compiles and burns without problems. Pullups on both lines (15K). Everything mounted on a breadboard, connections checked umpteen times with a digital multimeter. No, SCL and SDA are not reversed!
Here is my code:
http://pebblescorner.be/robotics/test.asm
Alas, I do not have an oscilloscope, so I cannot see what is going on. I do see that SDA and SCl are high when not busy.
I thought that I really understood the whole I2C thing, but now I am beginning to doubt even that. So, in order to check that out too, I tried one of the excellent tutorials of
Nigel's PIC Tutorial Page (tutorial 6.1: eeprom and LCD).
I constructed the whole thing on a breadboard, (an 24C2 instead of a pcf8574), compiled Mr. Goodwin's code, burned & turned on the power, and read: "writing ...efg" on the LCD. An error! With I2C again! Ohoh!!
I have spend so much time at it lately that my wife has started giving me "the look". Any hint or advice is very much appreciated! It could even save my marriage! (Just joking)

Thanks, and keep up the good works.

#### jimlovell777

##### New Member
I was having a similar issue not to long ago and in my case a noisy power supply was to blame so better filtering on your supply might be worth trying. Also of course keep the data and clock lines short and as noise free as possible on a breadboard.

#### blueroomelectronics

##### Well-Known Member
Why not use a PIC with I2C hardware like the 16F88?

#### be80be

##### Well-Known Member
I'd tell what it is but I don't think you'll believe me. Your using the pickit2 for power. Can't do that have to power the chip with 5 volts on it's own and not have the pickit2 hooked up. I did the same thing for a week couldn't get Nigel Tutorial to work thought
I had bad chips Lol unhook the pickit2 and power it up with my supply and away she went. See your using RB6 and 7 same pins the pickit2 use to program with it kills the signal.
Both b6 and b7 are tied to gnd with 4.7K resistors. One more variable in the works.

Having a pullup and pulldown resistor creates a voltage divider. You need to move the ICSP SDA line to another pin
That's what happens the pickit2 pulls RB6 and RB7 to gnd with 4.7 ohm resistor

Last edited:

#### kowey

##### New Member
Dear Be80be:

quote:"...I'd tell what it is but I don't think you'll believe me. Your using the pickit2 for power. ...". Indeed I do not: after burning, I always disconnect the pickit2 and use a battery for power. But your hint is very wellcome indeed and it will be most helpfull for future projects.
Mr. Jimlovell777, I see that my datalines are not only ridiculously long, they are nicely coiled so that the workbench is not too cluttered. Dimwit has perhaps created an induction-coil. Even a 16F88 would probably object to that! I 'll sort that out and see if that helps....

Thank all of you for considering my problem.

#### blueroomelectronics

##### Well-Known Member
Since you have a PK2 you could rig it up to capture the SCK, SDA using the logic tool mode.

#### be80be

##### Well-Known Member
Well that's what made mine not work and there 1 more are you building in debug in mplab or release use release
I constructed the whole thing on a breadboard, (an 24C2 instead of a pcf8574), compiled Mr. Goodwin's code, burned & turned on the power, and read: "writing ...efg" on the LCD. An error! With I2C again! Ohoh!!
You sure you have a 10k pulling them up
the SCL and SDA because that's the error you get when floating.

Last edited:

#### AtomSoft

##### Well-Known Member
not sure if its just me not knowing but i thought when you set the TRIS it just sets the direction and you still have to set the PORT value itself.

Code:
send_start_bit
;initialize: SDA and SCL must be high
bsf	STATUS,RP0
bsf	I2C_TRIS,SDA
bsf	I2C_TRIS,SCL
nop
;send start bit by bringing SCL low
bcf	I2C_TRIS,SCL
bcf	STATUS,RP0
return
should be more like:
Code:
send_start_bit
;initialize: SDA and SCL must be high
bsf	STATUS,RP0
bcf	I2C_TRIS,SDA
bcf	I2C_TRIS,SCL
bsf	I2C_PORT,SDA
bsf	I2C_PORT,SCL
nop
;send start bit by bringing SCL low
bcf	I2C_PORT,SCL
bcf	STATUS,RP0
return
When setting the TRIS a 1 = input and a 0 = output. so you have to set both to output and then set low/high.

If im right and i think i am lol you have to repair alot of that code

Last edited:

#### AtomSoft

##### Well-Known Member
Try this:
Code:
	LIST	p=16F628		;tell assembler what chip we are using
include "P16F628.inc"		;include the defaults for the chip
__config 0x3D18			;sets the configuration settings (oscillator type etc.)

cblock 	0x20 			;start of general purpose registers
counta 			;used in delay routine
countb 			;used in delay routine
countc 			;used in delay routine
countx			;used in delay routine
flags
w_temp			;temp storage used by isr
status_temp
;general registers used by I2C
I2C_data		;here comes the data that will be send
I2C_buffer		;buffer used during the writing of a byte
I2C_index		;7>6>5>4>3>2>1
I2C_failure		;I2C_failure > 0: true (1) or false(0)
I2C_countdown_tries	;if this is zero = total failure!!
I2C_counter	;counter used by waiting for ACK
endc

I2C_PORT	equ	PORTB
I2C_TRIS	equ	TRISB

#define		SDA	7
#define		SCL	6

org	0x0000			;org sets the origin, 0x0000 for the 16F628,
;this is where the program starts running
goto	main

;**************************************************************************
;I2C  LOW LEVEL STUFF BEGINS HERE******************************************
;**************************************************************************
low_SDA
bsf	STATUS,RP0
bcf	I2C_TRIS,SDA
bcf	STATUS,RP0
bcf	I2C_PORT,SDA
return

low_SCL
bsf	STATUS,RP0
bcf	I2C_TRIS,SCL
bcf	STATUS,RP0
bcf	I2C_PORT,SCL
return

high_SDA
bsf	STATUS,RP0
bcf	I2C_TRIS,SDA
bcf	STATUS,RP0
bsf	I2C_PORT,SDA
return

high_SCL
bsf	STATUS,RP0
bcf	I2C_TRIS,SCL
bcf	STATUS,RP0
bsf	I2C_PORT,SCL
return

SCL_pulse
call	high_SCL
call	low_SCL
return

reset_I2C_registers
clrf	I2C_buffer		;buffer used during the writing of a byte
clrf	I2C_index		;7>6>5>4>3>2>1
clrf	I2C_failure		;I2C_failure > 0: true (1) or false(0)
movlw	d'100'
movwf	I2C_countdown_tries	;if this is zero = total failure!!
movwf	I2C_counter	;counter used by waiting for ACK
return
;*************************************************************************
send_start_bit
;initialize: SDA and SCL must be high
bsf	STATUS,RP0
bcf	I2C_TRIS,SDA
bcf	I2C_TRIS,SCL
bcf	STATUS,RP0
bsf	I2C_PORT,SDA
bsf	I2C_PORT,SCL
nop
;send start bit by bringing SCL low
bcf	I2C_PORT,SCL
return
;**************************************************************************
send_stop_bit
;initialize: SDA and SCL must be high
bsf	STATUS,RP0
bcf	I2C_TRIS,SDA
bcf	I2C_TRIS,SCL
bcf	STATUS,RP0
bsf	I2C_PORT,SCL
nop
;send stop bit by bringing SDA high
bsf	I2C_PORT,SDA
return
;**************************************************************************
send_bit_high			;beginnig SDA and SCL low
call	high_SDA
call	SCL_pulse
return
;**************************************************************************
send_bit_low			;beginnig SDA and SCL low
call	low_SDA
call	SCL_pulse
return
;**************************************************************************
wait_for_ack
;SDA should be high! The slave is expected to bring it low
call	high_SDA
call	high_SCL

;prepare counter
movlw	d'100'
movwf	I2C_counter

wait_loop
btfsc	I2C_PORT,SDA
goto	SDA_is_still_high
goto	ack_SDA_is_cleared

SDA_is_still_high
decfsz	I2C_counter,f
goto	wait_loop
call	send_stop_bit
return

ack_SDA_is_cleared
;give a pulse
bsf	STATUS,RP0
bsf	I2C_TRIS,SCL
nop
bcf	I2C_TRIS,SCL
bcf	STATUS,RP0
;finished pulsing; clear failure (succes) and return
bcf	I2C_failure,0	;succes
return
;**************************************************************************
send_byte			;the byte should be in the working_register
;bits in the buffer are send MSB first
;prepare
movlw	d'8'
movwf	I2C_index
movwf	I2C_buffer
;send the bits
send_the_bit
btfsc	I2C_buffer,7
goto	the_bit_is_high
goto	the_bit_is_low

the_bit_is_high
call	send_bit_high
goto	check_if_sending_is_finished

the_bit_is_low
call	send_bit_low
call	high_SDA
goto	check_if_sending_is_finished

check_if_sending_is_finished
decfsz	I2C_index,f
goto	get_next_bit
return

get_next_bit
rlf		I2C_buffer,f
goto	send_the_bit

;********************************************************************************
do_write_sequence
;prepare the 7bit-addres of the device, put in the buffer and roll left
;LSB should be 0 when writing
movwf	I2C_buffer
rlf		I2C_buffer,f
bcf		I2C_buffer,0

;start the writing, (startbit + address), wait for ACK, if errors: restart
call	send_start_bit
call	send_byte
call	wait_for_ack
btfsc	I2C_failure,0	;succes?
goto	another_try

;now write the data, if errors: restart
movfw	I2C_data
call	send_byte
call	wait_for_ack
btfsc	I2C_failure,0	;succes?
goto	another_try
call	send_stop_bit
return	;succes!!!!!

another_try
decfsz	I2C_countdown_tries,f
goto	do_write_sequence		;restart
call	send_stop_bit
return	;give up

;**************************************************************************
;I2C  LOW LEVEL STUFF ENDS HERE******************************************
;**************************************************************************

setup
;turn comparators off (make it like a 16F84)
movlw	0x07
movwf	CMCON			;turn comparators off (make it like a 16F84)

;setup ports
bsf 	STATUS,	RP0	;select bank 1
;in TRISA and TRISB 0= output, 1 = INPUT
;RA4 and RA5 only POSSIBLE AS OUTPUT
movlw 	b'11111111'
movwf	TRISA			;set PortA all inputs
movlw 	b'11111111'		;set PortB all inputs
movwf 	TRISB
clrf	OPTION_REG		;disable internal pullups on port-b
bsf		OPTION_REG,7
;back to bank0:
bcf	STATUS,	RP0	;select bank 0
movlw	b'00000000'
movwf	PORTA
movwf	PORTB			;set all bits off

;NO interrupts:
clrf	INTCON		;clear flags and switches

retlw	0

;**************************************************************************
;**************************************************************************
;**************************************************************************

;to wait x msec (max 255!) put x in the working register
;and call this routine (wait_x_msec)
wait_x_msec

Loop_c	movwf	countc

loop_b	movlw	d'10'
movwf	countb

loop_a	movlw	d'100'
movwf	counta

decfsz	counta, f
goto	\$-1

decfsz	countb, f
goto	loop_a

decfsz	countc	,f
goto	loop_b
return
;**************************************************************************
;**************************************************************************
;**************************************************************************
main
call setup
call reset_I2C_registers
movlw	b'100000'	;address of Philips port expander
movlw	b'11111111'
movwf	I2C_data
loop

movlw	d'250'
call	wait_x_msec
movlw	d'250'
call	wait_x_msec
movlw	d'250'
call	wait_x_msec
movlw	d'250'
call	wait_x_msec

movlw	b'00000000'
movwf	I2C_data
call do_write_sequence

movlw	d'250'
call	wait_x_msec
movlw	d'250'
call	wait_x_msec
movlw	d'250'
call	wait_x_msec
movlw	d'250'
call	wait_x_msec

movlw	b'11111111'
movwf	I2C_data
call do_write_sequence

goto loop
end

#### jimlovell777

##### New Member
It's supposed to be like that. When using I2c you use resisters to pull the clk and data lines high so anything can talk on the bus by pulling the lines low. If you left the uC pin as an output you'd essentially be shorting the pin to ground, so instead of doing that you switch the pin to input mode (TRIS=1) which makes it high impedance and leaves the bus free to operate. I've really simplified the process and I'm leaving a lot out but if you want to know more about I2c and uC control techniques google it.

Edit: Oh also changing the TRIS value for a pin from Input to Output typically doesn't affect the pins output value, at least with the PIC micros I'm used to dealing with. If you want to know why again try google or look at the block diagram for a pin in a uC datasheet. Microchip PIC datasheets normally include such diagrams.

Last edited:

#### AtomSoft

##### Well-Known Member
heh i learned to do it one way which never gives me a issue so i stick to it. I know enough about i2c to get it working 100% and about pics as well. Im not the one with the non working I2C so heh maybe you should give advice to the other guy.

#### be80be

##### Well-Known Member
I told you the only time my I2C not working it was the pickit2 pulling RB6 & RB7 low can't power with it. But what Atom is telling you to send data your pins have to be output to receive data your pins need to be input. can't work just setting the tris. Say you set the
Tris to output and thats all you do then it will read as low if you don't set it high It will never go high it will only send out a 0 you what some 01011100 going out that pin.

Last edited:

Thanks Burt

#### Pommie

##### Well-Known Member
You never set either pin to output and drive it high. The way I²C works is by having pullups so you either have the pin as input and the line gets pulled high by the pullups or you set it to output and low. If you drive the bus high then other devices can't pull it low.

Mike.

#### AtomSoft

##### Well-Known Member
ok but i assumed only 1 device can be used at a time anyway which means it doesnt matter if the others cant use the lines.

#### Pommie

##### Well-Known Member
If you drive the line high then the other device can't send any data back and so a read of any device will not work. If you have somehow got this to work then you have bus contention.

Mike.

#### be80be

##### Well-Known Member
Mike that's not what I'm trying to say what I'm saying is how do you sent data out a input?
You send it out a output right.
So to send data out you set your line to output send the data out 100110 like that when done you change back to input to wait for data to be received
Which is polling the line to see if it gos low.
Now this is what i'm thinking I set to send data out I make my pin high so as no
errors happen clock out my data return to input. Seeing the line is high to start why would you want to come on line low. I no you can't hold the line high or low You have to return to input.

#### Pommie

##### Well-Known Member
Burt,

With I²C you either,
Do nothing (leave as input) to the line and the pullups make it a high.
Pull the line low. Set pin to output and low.

There is never any need to drive it high. And, if you do it will stop working.

Mike.

#### 3v0

##### Coop Build Coordinator
Forum Supporter
Since you have a PK2 you could rig it up to capture the SCK, SDA using the logic tool mode.
This is good advice. It is easier to look at the output then check the code to see why it is wrong.

EDIT: In the PICkit2 programmer software (not MPLAB) go to TOOLS>LOGIC_TOOL to see how to connect the PICkit2 as a logic analyzer.

3v0

#### AtomSoft

##### Well-Known Member
Pommie you are right too but. You can do the same with a pullup and using a normal high/low scheme as output. It wouldnt short because of the resistor.

Im sure he has only 1 device on the bus. I understand its use for multiple I2C devices but this is not the case.

Status
Not open for further replies.