Continue to Site

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.

  • 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.

Help with receiving a byte on an i2c bus..

Status
Not open for further replies.

Steve311

Member
Hi All; I am trying to receive a byte on an i2c bus using a pic16f684. I am receiving the 3 required ACK's from the slave, then when I try to read the data bits (and I can see on an oscope), that the DATA line stays high during all of the clock pulses that the pic provides. I am trying to read a register that should return 0x55 so I know this is definately wrong. The output of the project is to a hyperterminal. This part of the code I know works I have used it man times before and have tested it in this application.

Any help is greatly appreciated,
Steve


Code:
;.......................................................................................................................
;.......................................................................................................................
;Call proper header, include and source files to initialize PIC
 	LIST  		p=16F684    ; Tells Assembler what PIC is used
 	#INCLUDE	<p16f684.inc>    ; Includes factory defaults for the PIC
;.......................................................................................................................
;Configure PIC optional properties
 __CONFIG     _WDT_OFF & _MCLRE_OFF ; Enables CLKOUT, WDT, and Digital I/O on MCLR/RA3 
;.......................................................................................................................
 ;errorlevel -302, -207, -305	
;.......................................................................................................................
;.......................................................................................................................
;Define Constant Working Register Variables 
	cblock 0x20
	i2c_transmit_byte
	i2c_bit_counter
	pw_counter
	pw_counter1
	wait_startup_counter
	Received_I2C_Byte
	transmit_byte_rs232
	bit_counter_rs232
	delay_counter
	osc_counter
	endc
;.......................................................................................................................
;.......................................................................................................................
;Define Global Deifnitions:	
I2C_PORT	equ		PORTC
I2C_TRIS	equ		TRISC

#define		SDA	 	2
#define		SCL 	3	
;.......................................................................................................................
;.......................................................................................................................
START_PROGRAM
;Initialize Inputs/Outputs, and Registers on the PIC16F684.
;Initialize TRISA and TRISB registers. 

 	bsf 	STATUS, RP0			;Bank 1
 	movlw 	b'00001100'		 
 	movwf 	TRISC				;Assigns SDA and SCL Inputs.
 	movlw	b'00000000'		
	movwf	TRISA				;Assigns all TRISA as outputs. 
	movlw	0x03
	movwf	OSCCON				;Loads internal 31.25kHz clock
	bcf		STATUS, RP0			;Bank 0

	clrf	CCP1CON

	call	Sleep_Routine

	
;......................................................................................................................
;.......................................................................................................................
;READ Register from Kionix KXTJ9 accelerometer -- DCST_RESP Register located at 0x0C
;BusFixed
	call	Clock_Pulse_Delay
	call	Clock_Pulse_Delay
	call	Clock_Pulse_Delay	;Startup Delay

;	call	Transmit_RS232

	bcf		PORTC, 0

	call	Start_Command		;Start Command	
	call	SAD_W				;Address Slave plus write
	call	ACK					;Get Acknowledge from slave
	call	Who_Am_I_Reg
	call	ACK
	call	Start_Command
	call	SAD_R
	call	ACK					;Successful ACK received up to this point


	call	Receive_i2c_Byte

	call	Transmit_RS232


	call	NACK
	call	Stop_Command


done
	bsf		PORTC, 0
	goto	done





	goto	START_PROGRAM


;---------------------------------------------------------------------------------------
;Sub Routines for PIC
;---------------------------------------------------------------------------------------
;i2c Routines
;----------------------------------
Start_Command	
	call	Set_SCL_High				;Release SCL
	call	Set_SDA_High				;Release SDA
	call	Clock_Pulse_Delay			;Delay
	call	Set_SDA_Low					;Set SDA Low
	call	Clock_Pulse_Delay			;Delay
	return
;----------------------------------
SAD_W
	movlw	0x1E
	call	Send_i2c_Byte				;(SAD+W)Slave Address plus write.
	return
;----------------------------------
SAD_R
	movlw	0x1F
	call	Send_i2c_Byte				;(SAD+R)Slave Address plus read.
	return
;----------------------------------
ACK
	call	Set_SCL_High				;Release SCL
	call	Set_SDA_High				;Release SDA
	call	Clock_Pulse_Delay			;Delay
Clock_Stretch
	btfss	I2C_PORT, SCL				
	goto	Clock_Stretch				;Wait for slave to release clock line (if "stretching")
	call	Clock_Pulse_Delay			;Delay
	bcf		STATUS, C					;Initally clears the carry bit
	btfsc	I2C_PORT, SDA				;Checks if slave is pulling SDA low (giving its ACK)
	goto	_Error						;Clear carry for NACK
	bsf		STATUS, C					;Set carry for ACK
	btfss	STATUS, C					;Check carry bit to ensure ACK
	goto	START_PROGRAM				;Start over if NACK
	return								;If ACK received, continue 
;----------------------------------
NACK
	call	Set_SCL_High				;Release SCL
	call	Clock_Pulse_Delay			;Delay
	call	Set_SCL_Low					;Set SCL Low
	call	Set_SDA_High				;Release SDA
	call	Clock_Pulse_Delay			;Delay
	return
;----------------------------------
Stop_Command
	call	Set_SCL_High				;Release SCL
	call	Clock_Pulse_Delay			;Delay
	call	Set_SDA_High				;Release SDA
	call	Clock_Pulse_Delay			;Delay
	call	Clock_Pulse_Delay			;Delay
	return
;----------------------------------
Send_i2c_Byte
	movwf	i2c_transmit_byte			;Moves whatever value is in the working register into i2c_transmit_byte
	movlw	0x08	
	movwf	i2c_bit_counter				;Sets 8 bits to be sent out
Send_i2c_Bit
	call	Set_SCL_Low					;Ensure SCL is low initially
	call	Clock_Pulse_Delay			;Delay
	rlf		i2c_transmit_byte, f		;MSB is now in carry (MSB is sent first)
	btfsc	STATUS, C					
	call	Set_SDA_High
	btfss	STATUS, C
	call	Set_SDA_Low					;Sets either 0 or 1 to be sent out. (Checks carry bit)
	call	Clock_Pulse_Delay			;Delay
	call	Set_SCL_High				;Trigger Clock; set SCL high
 	call	Clock_Pulse_Delay			;Delay
	decfsz	i2c_bit_counter, f			;Checks if 8 bits have been sent out
	goto	Send_i2c_Bit				;No
	call	Set_SCL_Low					;Yes; Set SCL Low
	call	Clock_Pulse_Delay			;Delay
	return
;-------------------------------------	
Receive_i2c_Byte
	clrf	Received_I2C_Byte			;Clears previous received byte
	bsf		Received_I2C_Byte, 0
Get_i2c_Bit
	call	Set_SCL_Low
	call	Set_SDA_High				;Set SCL Low
	call	Clock_Pulse_Delay			;Delay
	bcf		STATUS, C					;Defaults to incoming bit = 0 (sets carry = 0)
	call	Set_SCL_High				;Release SCL
	call	Clock_Pulse_Delay
	btfsc	I2C_PORT, SDA				;Read port, skip if clear
	bsf		STATUS, C					;If incoming bit = 1, set carry flag to 1
	rlf		Received_I2C_Byte, f		;move carry into data (MSB first)
	call	Clock_Pulse_Delay
	btfss	STATUS, C
	goto	Get_i2c_Bit					;No

;Test to ensure hypertermianl works correctly. 
;	movlw	0x55
;	movwf	Received_I2C_Byte

	call	Set_SCL_Low

	movf	Received_I2C_Byte, w		;Moves result to the working register

	return								;Yes, return
	

;Routines to Set SDA and SCL High/Low

Set_SCL_High
	bsf		STATUS, RP0				;Bank 1
	bsf		I2C_TRIS, SCL			;Sets SCL as an input
	clrf	ANSEL
	bcf		STATUS, RP0				;Bank 0	
	movlw	0x07
	movwf	CMCON0
	return

Set_SCL_Low
	bcf		I2C_PORT, SCL			;Sets SCL port pin low
	bsf		STATUS, RP0				;Bank 1
	bcf		I2C_TRIS, SCL			;Sets SCL as an output
	clrf	ANSEL
	bcf		STATUS, RP0				;Bank 0
	movlw	0x07
	movwf	CMCON0
	return	

Set_SDA_High
	bsf		STATUS, RP0				;Bank 1
	bsf		I2C_TRIS, SDA			;Sets SDA as an input (high impedance)
	clrf	ANSEL
	bcf		STATUS, RP0				;Bank 0
	movlw	0x07
	movwf	CMCON0
	return

Set_SDA_Low
	bcf		I2C_PORT, SDA			;Sets SDA port pin low
	bsf		STATUS, RP0				;Bank 1
	bcf		I2C_TRIS, SDA			;Sets SDA as an output
	clrf	ANSEL
	bcf		STATUS, RP0				;Bank 0
	movlw	0x07
	movwf	CMCON0
	return	

;---------------------------------------------------------------------------------------
;---------------------------------------------------------------------------------------
;Delays
Clock_Pulse_Delay
	movlw	0xFF
	movwf	pw_counter
Clock_Pulse_Delay_Wait
	nop
	movlw	0x01
	movwf	pw_counter1
Clock_Pulse_Delay_Wait1
	decfsz	pw_counter1, f
	goto	Clock_Pulse_Delay_Wait1
	decfsz	pw_counter, f
	goto	Clock_Pulse_Delay_Wait
	return

bit_delay
	movlw	0x16
	movwf	delay_counter
bit_wait
	nop
	decfsz  delay_counter, f
	goto	bit_wait
	return

Osc_Stablize
	movlw	0xFF
	movwf	osc_counter
Osc_Wait
	nop
	nop
	nop
	decfsz	osc_counter, f
	goto	Osc_Wait
	return

I2cInit	
	call	Set_SCL_Low
	call	Set_SDA_Low
	call	Set_SCL_High
	call	Set_SDA_High
ClockLoop
	btfsc	PORTC,SDA
;	goto	BusFixed
	bcf		PORTC,SCL
	bsf		STATUS,RP0
	bcf		TRISC,SCL
	goto	$+1
	bsf		TRISC,SCL
	bcf		STATUS,RP0
	goto	ClockLoop

Who_Am_I_Reg
	movlw	0x0C  ; - Substitute for DSCT_RESP
	call	Send_i2c_Byte
	return


;---------------------------------------------------------------------------------------
;---------------------------------------------------------------------------------------
;Sub routines for PIC to Hyperterminal	
;****SENDS INVERSE LOGIC TO PROLIFIC RS-232 TO USE CONVERTER
Transmit_RS232	
	movwf	transmit_byte_rs232		;moves W to transmit_byte

	bsf		STATUS, RP0
	movlw	0x65
	movwf	OSCCON
	movlw	0x00
	movwf	TRISC
	movlw	0x00
	movwf	TRISA
	bcf		STATUS, RP0

;	movwf	transmit_byte_rs232		;moves W to transmit_byte

	call	Osc_Stablize

;	movlw	0x55	
;	movwf	transmit_byte_rs232



	bcf		PORTC, 5
;	movlw	0x55
;	movwf	Received_I2C_Byte
;	movf	Received_I2C_Byte, w
	movlw	0x08		
	movwf	bit_counter_rs232		;sets 8 bits to send out
	bsf		PORTC, 5
	call	bit_delay

Send_Serial_Loop_RS232
	rrf		transmit_byte_rs232, f		;Send one bit
	btfss 	STATUS,	 C
	bsf		PORTC, 5
	btfsc	STATUS,  C
	bcf		PORTC, 5
	call	bit_delay
	decfsz	bit_counter_rs232, f		;Tests if all done
	goto	Send_Serial_Loop_RS232
	bcf		PORTC, 5
	call	bit_delay
	call	bit_delay

	bsf		STATUS, RP0
	movlw	0x03
	movwf	OSCCON
;	movlw	0x00
;	movwf	TRISC
;	movlw	0x00
;	movwf	TRISA
	bcf		STATUS, RP0
	return

_Error
	movlw	0x45
	call	Transmit_RS232
	movlw	0x52
	call	Transmit_RS232
	movlw	0x52
	call	Transmit_RS232
	movlw	0x4F
	call	Transmit_RS232
	movlw	0x52
	call	Transmit_RS232
	goto	START_PROGRAM

Sleep_Routine
	movlw	0x07	
	movwf	WDTCON
	sleep
	movlw	0x00	
	movwf	WDTCON
	return



	end
 
I just checked your receive against mine and I see you are pulling the clock line low after receiving the byte. This will cause spurious clock pulses and prevent you from receiving an ACK. I see you're also doing the same in the send routine. I suspect these spurious clock pulses are getting the master/slave out of sync and causing your problem.

BTW, you only need to set ANSEL and CMCON0 once at the beginning of your program.

Mike.
 
When I don't set the clock line low at that point in the send routine, the slave does not ACK. When I set the clock low there it does ACK... I'm a little confused.

What other than spurs can get the communication out of sync?
 
I hooked up a 24LC16 to a 16F886 and got it half working with your code. I'll have another go at weekend and hopefully get it working.

Mike.
 
  • Like
Reactions: 3v0
Oh Great! I have some of those too...

Where did you get it to half way working? Or Rather, where does it stop working?
 
I got it writing 0x45 to address 0 and noted the NACK whilst the write took place but it failed to read back properly. I get acknowledges on all writes and a delay before more acknowledges on the read back but the value is 0xff. I'll have another try over weekend, probably not until Sunday.

Mike.
 
Very interesting Mike, That is EXACTLY what I am experiencing too. All data that I'm receiving is 0xff which is wrong. I will have time for it this weekend myself.

Steve
 
I managed to finally get it working.

These are the I2C routines,
Code:
;---------------------------------------------------------------------------------------  
;i2c Routines
;----------------------------------  
Start_Command
		call	Set_SCL_High	;Release SCL
		call	Set_SDA_High	;Release SDA
		call	Clock_Pulse_Delay    ;Delay
		call	Set_SDA_Low	;Set SDA Low
		call	Clock_Pulse_Delay    ;Delay
		return
;----------------------------------  
Stop_Command	
		call	Set_SCL_Low
		call	Set_SDA_Low
		call	Clock_Pulse_Delay
		call	Set_SCL_High
		call	Clock_Pulse_Delay
		call	Set_SDA_High
		return
;----------------------------------
GetACK
		call	Set_SCL_Low	
		call	Set_SDA_High	
		call	Clock_Pulse_Delay    
		call	Set_SCL_High
Clock_Stretch
		btfss	I2C_PORT, SCL
		goto	Clock_Stretch	;Wait for slave to release clock line (if "stretching")
		bcf	STATUS,C	;Initally clears the carry bit
		btfss	I2C_PORT,SDA	;Checks if slave is pulling SDA low (giving its ACK)
		bsf	STATUS,C	;Set carry for ACK
		return			;If ACK received, continue 

;----------------------------------  
SendNACK
		call	Set_SCL_Low
		call	Clock_Pulse_Delay    ;Delay
		call	Set_SDA_High
		call	Set_SCL_High
		call	Clock_Pulse_Delay    ;Delay
		return
;----------------------------------  
Send_i2c_Byte
		movwf	i2c_transmit_byte    ;Moves whatever value is in the working register into i2c_transmit_byte
		movlw	0x08
		movwf	i2c_bit_counter     ;Sets 8 bits to be sent out
Send_i2c_Bit	call	Set_SCL_Low	;Ensure SCL is low initially
		rlf	i2c_transmit_byte, f   ;MSB is now in carry (MSB is sent first)
		btfsc	STATUS, C
		call	Set_SDA_High
		btfss	STATUS, C
		call	Set_SDA_Low	;Sets either 0 or 1 to be sent out. (Checks carry bit)
		call	Clock_Pulse_Delay    ;Delay
		call	Set_SCL_High	;Trigger Clock; set SCL high
		call	Clock_Pulse_Delay    ;Delay
		decfsz	i2c_bit_counter, f    ;Checks if 8 bits have been sent out
		goto	Send_i2c_Bit	;No
		return
;-------------------------------------	  
Receive_i2c_Byte 
		clrf	Received_I2C_Byte    ;Clears previous received byte
		bsf	Received_I2C_Byte, 0
Get_i2c_Bit	call	Set_SCL_Low
		call	Set_SDA_High
		call	Clock_Pulse_Delay    ;Delay
		call	Set_SCL_High	;Release SCL
		call	Clock_Pulse_Delay
		bcf	STATUS,C	;Defaults to incoming bit = 0 (sets carry = 0)
		btfsc	I2C_PORT,SDA	;Read port, skip if clear
		bsf	STATUS,C	;If incoming bit = 1, set carry flag to 1
		rlf	Received_I2C_Byte, f   ;move carry into data (MSB first)
		call	Clock_Pulse_Delay
		btfss	STATUS, C
		goto	Get_i2c_Bit	;No
		movf	Received_I2C_Byte, w   ;Moves result to the working register
		return			;Yes, return
			 
			 
;Routines to Set SDA and SCL High/Low  
			 
Set_SCL_High
		bsf	STATUS,RP0	;Bank 1
		bsf	I2C_TRIS,SCL	;Sets SCL as an input
		bcf	STATUS,RP0	;Bank 0
		return
			 
Set_SCL_Low
		bcf	I2C_PORT,SCL	;Sets SCL port pin low
		bsf	STATUS,RP0	;Bank 1
		bcf	I2C_TRIS,SCL	;Sets SCL as an output
		bcf	STATUS,RP0	;Bank 0
		return
			 
Set_SDA_High
		bsf	STATUS,RP0	;Bank 1
		bsf	I2C_TRIS,SDA	;Sets SDA as an input (high impedance)
		bcf	STATUS,RP0	;Bank 0
		return
			 
Set_SDA_Low
		bcf	I2C_PORT,SDA	;Sets SDA port pin low
		bsf	STATUS,RP0	;Bank 1
		bcf	I2C_TRIS,SDA	;Sets SDA as an output
		bcf	STATUS,RP0	;Bank 0
		return
			 
;---------------------------------------------------------------------------------------  
I2cInit
		call	Set_SCL_High
		call	Set_SDA_High
ClockLoop
		btfsc	PORTC,SDA
		return
		call	Set_SCL_Low	;Set SCL Low
		call	Clock_Pulse_Delay    ;Delay
		call	Set_SCL_High	;Set SCL Low
		call	Clock_Pulse_Delay    ;Delay		
		goto	ClockLoop			 
;---------------------------------------------------------------------------------------  
;Delays
Clock_Pulse_Delay
		goto	$+1 
		goto	$+1 
		goto	$+1 
		goto	$+1 
		goto	$+1 
		goto	$+1 
		goto	$+1 
		goto	$+1 
		goto	$+1 
		goto	$+1 
		goto	$+1 
		goto	$+1 
		return

And this is what I had to do to write and then read back a byte.
Code:
		call	Start_Command	;Start Command
		movlw	0xa0
		call	Send_i2c_Byte	;send ID and Write
		call	GetACK
		btfss	STATUS,C
		goto	$-1
		movlw	0x00		;address 0
		call	Send_i2c_Byte
		call	GetACK
		btfss	STATUS,C
		goto	$-1
		movlw	0x56		;data to write
		call	Send_i2c_Byte
		call	GetACK
		btfss	STATUS,C
		goto	$-1
		call	Stop_Command

again		call	Start_Command	;Start Command
		movlw	0xa0
		call	Send_i2c_Byte
		call	GetACK
		btfss	STATUS,C
		goto	again
		movlw	0x00		;address 0
		call	Send_i2c_Byte
		call	GetACK
		btfss	STATUS,C
		goto	$-1
		call	Stop_Command   ;doesn't work without this command
		call	Start_Command
		movlw	0xa1
		call	Send_i2c_Byte
		call	GetACK
		btfss	STATUS,C
		goto	$-1
		call	Receive_i2c_Byte
		call	SendNACK
		call	Stop_Command
		movfw	Received_I2C_Byte

hang	goto	hang

The thing that was stopping it working was the lack of the stop command in the read back part.

Mike.
 
Status
Not open for further replies.

Latest threads

Back
Top