Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

I^2C Question - Confusion

Status
Not open for further replies.

3BABY

Member level 5
Joined
Jan 14, 2011
Messages
91
Helped
0
Reputation
0
Reaction score
0
Trophy points
1,286
Location
New Zealand
Activity points
2,252
Hi Guys,

im just starting to work with some I2C EPROMs but im getting confused about the Read/Write sequence.. im confused between these two websites as they seem to have completely opposite ways of Reading and Writing

I2C Tutorial

I2C PIC Interfacing Tutorial - Theory | PyroElectro - News, Projects & Tutorials

and then this website doesnt mention that you need to send a 2nd start Bit..

An I2C Tutorial.

Could someone please confirm that to Write you:

1. Send a start Bit
2. Send the I2C address of the slave with the R/W bit low
3. wait for ACK
4. Send the internal register address you want the data to be written to
5. wait for ACK
6. Send the data byte
7. wait for ACK
8. Send the stop Bit



and to Read you:

1. Send a start Bit
2. Send address of slave with the R/W bit low
3. send ACK
4. Send Internal register address of the byte you want to read
5. send ACK
6. Send a start Bit again
7. Send address of slave with the R/W bit high this time
8. send ACK
9. Read data byte from slave
10. send ACK
11. Send the stop Bit


any help would be much appreciated!
 

Your writing sequence is correct but I think the read sequence should be:


1. Send a start Bit
2. Send address of slave with the R/W bit low
3. send ACK
4. Send Internal register address of the byte you want to read
5. send ACK
6. Send a start Bit again
7. Send address of slave with the R/W bit high this time
8. send ACK
9. Read data byte from slave
10. send NACK
11. Send the stop Bit

The NACK from the host to the slave in step 10 signals to the slave not to ready any more data for transmitting as the host has enough.

Hope this helps.

Also, both websites above ( Top 2 ) are correct. The difference in the reads is that one sends a stop bit after writing the address to read from but the other just sends a start bit again. This is a repeated start condition and is used so that the host does not need to give up control of the bus ( which sending a stop would have done ) but can retain control and continue with it addressing sequence.

If there were many devices on the same bus then another device may be polling the slave waiting for it's opportunity to talk and as soon as the stop bit is sent from you host the bus is open and free for this other device to take control. By issuing a repeated start ( which nearly all I2C systems support ) you keep control.

Sorry is this is a long winded explanation.
 
Last edited:

Steps 3/5/8 are still incorrect. After each send address action, perform wait for ACK rather than send ACK. Step 10 is right, send NACK after last data read.
 

you seem to ..


Steps 3/5/8 a...

Thanks for the replys Guys, ive found some code on PICLIST but i relly cant get it to work, i have modified it abit but still having some major issues.. if anyone could assist? at this point im just trying to get and ACK back from the Slave (24C08) to test that the "Byte_out" function is working.. ive just included below the function.. the byte to be send is in WERG.. and is 10100000 ... please note i am just trying to write the slave address.. the problem is im not getting a ACK and the code just falls through to the "No_ACK_Rec" routine

i have at ORG 0x00

#define SDA TRISA, 1
#define SCL TRISA, 0



here is a link to the code on PICList: PIC Microcontoller Input / Output Methods for I2C


Wish i had a Logic Analyzer!!



Firstly

Code:
						; START CONDITION		
		bsf	 SDA					; ensure SDA line is high
		bsf	 SCL					; pull clock high
		bcf	 SDA					; START - data line low

		movlw	b'10100000'			; send write (10100000) to set address					
									; bit 0 should be LOW for a Write operation
		call	Byte_Out

then goes to Byte_Out
Code:
;------ This routine sends out the byte in the W register and then waits for ACK from EPROM (256us timeout period)
Byte_Out
		movwf	OutputByte
		movlw	0x08			; 8 bits to send
		movwf	GenCount
		rrncf	OutputByte, 1		; shift right in preparation for next loop
ControlOut
		rlncf	OutputByte, 1		; shift bits out of buffer
		bcf	SCL			; pull clock line low

		CALL Delay1MS

		btfsc	OutputByte, 7		; send current "bit 7"
		goto	BitHigh
		bcf	SDA
		goto	ClockOut
BitHigh		
		bsf	SDA
ClockOut	
		bsf	SCL			; pull clock high after sending bit
	
		CALL Delay1MS	
	
		decfsz	GenCount, 1		;skips next instruction if Bit count is Zero
		goto	ControlOut

		bcf	SCL			; pull clock low for ACK change
		bsf	SDA			; free up SDA line for slave to generate ACK


		CALL Delay1MS

						; wait for slave to pull down ACK
		bsf	SCL			; pull clock high for ACK read
		clrf	GenCount		; reuse this register as a timeout counter (to 256us) to test for ACK

		CALL Delay1MS

WaitForACK
		
		incf	GenCount, 1		; increase timeout counter each time ACK is not received
		btfsc	STATUS, Z
		goto	No_ACK_Rec
	
		btfsc	SDA				; test pin. If clear, EEPROM is pulling SDA low for ACK
		goto	WaitForACK		; ...otherwise, continue to wait
		
		bcf	flags, 0		; clear flag bit (ACK received)
		return
 
Last edited:

To make sure you are on the right track just read the datasheet of I2C EEPROM(partnumber: The one you are using). It will tell you the exact way the protocol should follow.
 

To make sure you are on the right track just read the datasheet of I2C EEPROM(partnumber: The one you are using). It will tell you the exact way the protocol should follow.

Hi there,

after reading the datasheet a little more, 10bit addressing with the 24C08.. i have changed the code to represent this:

Code:
						; START CONDITION		
		bsf	 SDA					; ensure SDA line is high
		bsf	 SCL					; pull clock high
		bcf	 SDA					; START - data line low

		CALL Delay1MS

		movlw	b'11110100'			; send write (11110100 to set address - HIGH					
									; bit 0 should be LOW for a Write operation
		call	Byte_Out

		btfsc	flags, 0
		goto	Error_Routine		; NOTE: MUST USE "RETURN" FROM THERE


		movlw	b'10000000'			; send write (10000000) (10100000 for device) to set address - LOW					
									; bit 0 should be LOW for a Write operation
		call	Byte_Out

		btfsc	flags, 0
		goto	Error_Routine		; NOTE: MUST USE "RETURN" FROM THERE


also figured out why it just kept falling through to the "No_ACK_Rec" on the OutputByte function.. was because i was bit testing TRISA.. instead of PORTA.. this seemed to solve that issue.. but.. even though the routine completes, i cannot be sure (with the whole Write process) if the data is actually been written or not.. ???
 

GUYS im really starting to get confused here, i have a 24C08.. 1Kbytes right, for this 10bit addressing im sending 11110100 (LSB at 0 for write) first the HIGH 2 bits of the 10 bit address in XX (1111 0XX0) and 10000000 second for the LOW (XXXX XXXX) ..if im sending 10100000 for a device address.. what are the last two bits in the second byte for? where do i send the memory address i want to write to? if im supposed to send the memory address in the 10bit address.. how do i address a specific device?

from what i understand i need to address a device, and then give it a 10bit address to write the data to? if i have 4 pages of 8 bytes doesnt that mean in the 10 bit address i need to specify the page in the upper 2 bits and the memory location on that page in the lower 8bits?

when am i sending the device address of 10100000 for the 24C08.. is that even the device address??? i have A0-A2 connected to Ground.


REALLY CONFUSED!!!!
 

24C08 has two upper memory address bits in the device address byte and the 8 lower in a second byte. Just read the data sheet. Select pins A0 and A1 are ignored for 2408.

The write address of 2408 is 10100XY0, with XY being the highest memory address bits.
 

24C08 has two upper memory address.....

Thanks for the reply, i have set the two bytes up to address page 0 location decimal 55..


Could someone please have a look at the below code for me? im pulling my hair out here.. the code completes but.. if i try to Read .. nothing ends up in Data_Buf.. is it even writing 'A' to location d'55'?

Code:
;---------------------------------------------------------------------------
I2C_WRITE


		CLRF flags
		CLRF PORTA
		CLRF PORTD

		CALL GET_OUT_BYTE

		CALL WriteEPROM


RETURN


I2C_READTT


		CLRF flags
		CLRF PORTA
		CLRF PORTD


		CALL ReadEPROM


RETURN


;--------------------------------------------------------------------------------------------------------------------
GET_OUT_BYTE

		CLRF Data_Buf

		MOVLW 'A'
		MOVWF Data_Buf

	RETURN
;--------------------------------------------------------------------------------------------------------------------
;--------------------------------------------------------------------------------------------------------------------

;
; Call with: EEPROM address in Mem_Loc - HAVE STATICLY SET THIS TO d'55'
; Returns with: byte in Data_Buf



ReadEPROM

		CLRF Data_Buf
		
		movf	PORTA, 0		; for EEPROM operation,
		andlw	b'11111100'			; load zero into RB1 and RB2
		movwf	PORTA			; for passive control of bus
		
		bsf	SDA			; let SDA line get pulled high
		bsf	SCL			; let SCL line get pulled high
		bcf	SDA			; START - data line low

		CALL Delay1MS


;REGISTER ADDRESS - as Write
		movlw	b'11110000'			; send write (10100000) to set address					
									; bit 0 should be LOW for a Write operation
		call	Byte_Out

		btfsc	flags, 0
		goto	Error_Routine		; NOTE: MUST USE "RETURN" FROM THERE



		movlw	d'55'				; send write (10100000) to set address					
									; bit 0 should be LOW for a Write operation
		call	Byte_Out

		btfsc	flags, 0
		goto	Error_Routine		; NOTE: MUST USE "RETURN" FROM THERE






		bcf	SCL			; pull clock line low in preparation for 2nd START bit
		bsf	SDA			; pull data line high - data transition during clock low
		CALL Delay1MS
		bsf	SCL			; pull clock line high to begin generating START
		CALL Delay1MS
		bcf	SDA			; 2nd START - data line low



;REGISTER ADDRESS - as Read
		movlw	b'11110001'			; send write (10100000) to set address					
									; bit 0 should be LOW for a Write operation
		call	Byte_Out

		btfsc	flags, 0
		goto	Error_Routine		; NOTE: MUST USE "RETURN" FROM THERE



		movlw	d'55'			; send write (10100000) to set address					
									; bit 0 should be LOW for a Write operation
		call	Byte_Out

		btfsc	flags, 0
		goto	Error_Routine		; NOTE: MUST USE "RETURN" FROM THERE




;------ Note that Byte_Out leaves with SDA line freed to allow slave to send data in to master.
		call	Byte_In
	;	movf	Data_Buf, W		; put result into W register for returning to CALL

		bcf	SCL			; extra cycle for SDA line to be freed from EPROM
		
		CALL Delay1MS

		bcf	SDA			; ensure SDA line low before generating STOP
		bsf	SCL			; pull clock high for STOP
		CALL Delay1MS
		bsf	SDA			; STOP - data line high
		
		return





; Call with:  EEPROM address in Mem_Loc - HAVE STATICLY SET THIS TO d'55', byte to be sent in Data_Buf
; Returns with:  nothing returned
WriteEPROM
	
		movf	PORTA, 0		; for EEPROM operation,
		andlw	b'11111100'			; load zero into RB1 and RB2
		movwf	PORTA			; for passive control of bus
				
						; START CONDITION		
		bsf	 SDA					; ensure SDA line is high
		bsf	 SCL					; pull clock high
		bcf	 SDA					; START - data line low

		CALL Delay1MS

;REGISTER ADDRESS - as Write
		movlw	b'11110000'			; HIGH	(1111 0XX0)				
									; bit 0 should be LOW for a Write operation
		call	Byte_Out

		btfsc	flags, 0
		goto	Error_Routine		; NOTE: MUST USE "RETURN" FROM THERE



		movlw	d'55'				; LOW (XXXX XXXX)				
									; bit 0 should be LOW for a Write operation
		call	Byte_Out

		btfsc	flags, 0
		goto	Error_Routine		; NOTE: MUST USE "RETURN" FROM THERE



		movf	Data_Buf, W		; move data to be sent to W
		call	Byte_Out
		btfsc	flags, 0
		goto	Error_Routine



		bcf	SCL			; extra cycle for SDA line to be freed from EPROM

		CALL Delay1MS

		bcf	SDA			; ensure SDA line low before generating STOP
		bsf	SCL			; pull clock high for STOP
		bsf	SDA			; STOP - data line high

		CALL Delay1MS		; 10ms delay max. required for EPROM write cycle
		

			




		return




;------ This routine reads one byte of data from the EPROM into Data_Buf
Byte_In

		bsf	SDA			; free up SDA line for slave to control

		clrf	Data_Buf
		movlw	0x08			; 8 bits to receive
		movwf	GenCount
ControlIn
		rlncf	Data_Buf, 1		; shift bits into buffer
		bcf	SCL			; pull clock line low

		CALL Delay1MS

		bsf	SCL			; pull clock high to read bit

		CALL Delay1MS

	;	btfss	SDA			; test bit from EPROM (if bit=clear, skip because Data_Buf is clear)
		btfss	PORTA, 1	;	Bit Tests SDA
		goto	here1
		
		bsf	Data_Buf, 0		; read bit into 0 first, then eventually shift to 7
	
here1		decfsz	GenCount, 1
		goto	ControlIn
		return




;------ This routine sends out the byte in the W register and then waits for ACK from EPROM (256us timeout period)
Byte_Out
		movwf	OutputByte
		movlw	0x08			; 8 bits to send
		movwf	GenCount
		rrncf	OutputByte, 1		; shift right in preparation for next loop
ControlOut
		rlncf	OutputByte, 1		; shift bits out of buffer
		bcf	SCL			; pull clock line low

		CALL Delay1MS

		btfsc	OutputByte, 7		; send current "bit 7"
		goto	BitHigh

		bcf	SDA			; pull Data line LOW
		goto	ClockOut
BitHigh		
		bsf	SDA			; pull Data line HIGH
ClockOut	
		bsf	SCL			; pull clock high after sending bit
	
		CALL Delay1MS	
	
		decfsz	GenCount, 1		;skips next instruction if Bit count is Zero
		goto	ControlOut

		bcf	SCL			; pull clock low for ACK change
		bsf	SDA			; free up SDA line for slave to generate ACK


		CALL Delay1MS

						; wait for slave to pull down ACK
		bsf	SCL			; pull clock high for ACK read
		clrf	GenCount		; reuse this register as a timeout counter (to 256us) to test for ACK

		MOVLW d'250'
		MOVWF TO_CNTR
WaitForACK
		
		incf	GenCount, 1		; increase timeout counter each time ACK is not received
		btfsc	STATUS, Z
		DECF	TO_CNTR, 1

		btfsc	STATUS, Z
		goto	No_ACK_Rec
	
	;	btfsc	SDA				; test pin. If clear, EEPROM is pulling SDA low for ACK
		btfsc	PORTA, 1
		goto	WaitForACK		; ...otherwise, continue to wait
		
		bcf	flags, 0		; clear flag bit (ACK received)
		return






;------ No ACK received from slave (must use "return" from here)
;; Typically, set a flag bit to indicate failed write and check for it upon return.
No_ACK_Rec
		bsf	flags, 0		; set flag bit
		return				; returns to Byte_Out routine (Bank 1 selected)


;------ No ACK received from slave.  This is the error handler.
Error_Routine
; Output error message, etc. here
				BSF LATD, RD2



		return				; returns to INITIAL calling routine
 

Hey Guys,

an anyone help me with this problem? i have the code working in ISIS but for some rason im getting NACK's when i should be getting ACKs.. below is the section of code that waits for a ACK.. but for some reason it just moves onto the next Data Byte Output..

Code:
		decfsz	GenCount, 1		;skips next instruction if Bit count is Zero
		goto	ControlOut

		bcf	SCL			; pull clock low for ACK change
		bsf	SDA			; free up SDA line for slave to generate ACK


		CALL Delay1MS

						; wait for slave to pull down ACK
		bsf	SCL			; pull clock high for ACK read
		clrf	GenCount		; reuse this register as a timeout counter (to 256us) to test for ACK

BSF LATD, RD0

		MOVLW d'250'
		MOVWF TO_CNTR
WaitForACK
		
		incf	GenCount, 1		; increase timeout counter each time ACK is not received
		btfsc	STATUS, Z
		DECF	TO_CNTR, 1

		btfsc	STATUS, Z
		goto	No_ACK_Rec
	
	;	btfsc	SDA				; test pin. If clear, EEPROM is pulling SDA low for ACK
		btfsc	PORTA, 1
		goto	WaitForACK		; ...otherwise, continue to wait
		

BSF LATD, RD1

		bcf	flags, 0		; clear flag bit (ACK received)
		return


**broken link removed**


**broken link removed**



Any help would me much appreciated!!!
 

Ok Guys, so i have it working in Proteus.. (with Device address/write as A0) but on my hardware if i send a write (as seen in pic, value 0x41 to address d'55') .. and then send a receive (from the same memory location).. i get nothing back..

Big thanks to Easyrider83 for his help so far!

**broken link removed**

1.5 weeks on this same problem!!! :-(
 

OK..

after even trying to work with the Hardware I2C module on the 18F4550 i cannot get a I2C write or read subroutine to work.. this probelm is holding up my entire project now.. so heres my proposition:

I am looking for someone that can help me with this problem, I WILL PAY YOU.. to help me, or produce for me a simple READ and WRITE program to interface a ATMEL 24C08.. i have this board connected to a 18F8722 (on a BIGPIC6 board from MikroElektronika) .. if you would like to produce for me a simple read/write routine, i would prefer it to be in PIC18 Asembily language... using the hardware I2C or a software I2C module i dont mind too much please PM me with how much you would be willing to charge for this.. if you are interested... i can pay via Paypal.

Also.. i can provide you all the code i have been working with thus far.. as it might make your job easier.. and/or my job to work out what was done to make it work (and how i can use it, as im familiar with it) a little less of a headache..

Many thnaks,

Tony
 
Last edited:

Sorry to hear your still having problems. The best I can do at the moment is to point you to a good C bitbang I2C routine here:

http://www.atmel.com/dyn/resources/prod_documents/QTAN0040.pdf

Look under Source Code.

I'm not very familiar with the PIC or Assembly so this code might get you on the right track somehow. It is for AVR but with a few changes could be made to work with PIC.

Good Luck.
 

Sorry to hear your still having problems. The best I can do at the moment is to point you to a good C bitbang I2C routine here:

http://www.atmel.com/dyn/resources/prod_documents/QTAN0040.pdf

thanks for the link .. have had a look through it..

Attach your project, please.

please find the whole project .rar here or separate files (found in the .rar) here .

the project is trying to us the hardware I2C MSSP unit... on the 18F4550.. i got most of the code from this website and had to slightly modify it for use with the 4550.. and getting it to work with Proteus.. it works in Proteus.. but not on my hardware..



alternatively you can find the Software Read/Write functions here the the project name is 55_I2C and on the website has the same option to download separate files or the whole project as .rar

and the Header file 18F4550.INC file can be found here

thanks for the help :)
 
Last edited:

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top