• Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

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:D)

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.
 

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.
 

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_device_address	;7 bit-address of the slave plus 0 for writing, 1 for reading
		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
	bsf		I2C_failure,0	;add an error
	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
	movfw	I2C_device_address
	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
	movwf	I2C_device_address
	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:

Pommie

Well-Known Member
Most Helpful 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.
 

Pommie

Well-Known Member
Most Helpful 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
Most Helpful 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.

Latest threads

EE World Online Articles

Loading
Top