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 data on i2c bus using PIC16F684

Status
Not open for further replies.

Steve311

Member
Hi all; I am stuck receiving data on an i2c bus using a pic16f684 and kionix kxtj2 accelerometer. I have two issues:

1) I can receive an ACK from the slave only if I don't check the CLK line for stretching. I see the DAT line being pulled low signaling an ACK when I address the slave correctly, and don't see it if I dont. I have a PC Digital O-Scope for a visual reference on the CLK and DAT lines. So I do know the accelerometer is pulling the DAT line low. I should be able to test the CLK line here for stretching but I'm not sure why I can't?

2) When receiving a byte from the accelerometer, I see the clock pulses that the pic generates and I see the data line toggling high and low which is the expceted value I'm looking to read (0x55). However, when I test the "Received_I2C_Byte", it is always = 0x00. I have an output routine to send the received byte to my PC via hyperterminal in ASCII characters. I know this section of the code works as I have used it plenty of times and have verified its functionality.

Below is my code, any help is much 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	 	0
#define		SCL 	1	
;.......................................................................................................................
;.......................................................................................................................
START_PROGRAM
;Initialize Inputs/Outputs, and Registers on the PIC16F684.
;Initialize TRISA and TRISB registers. 
 	bsf 	STATUS, RP0			;Bank 1
 	movlw 	b'00000011'		 
 	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
;......................................................................................................................
;.......................................................................................................................
;READ Register from Kionix KXTJ9 accelerometer -- DCST_RESP Register located at 0x0C
;BusFixed
;	call	Set_SCL_High
;	call	Set_SDA_High
	call	Clock_Pulse_Delay
	call	Clock_Pulse_Delay
	call	Clock_Pulse_Delay	;Startup Delay

	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
	call	Receive_i2c_Byte
	call	NACK
	call	Stop_Command

;Test to check the value of the received byte
;	movlw	0x00
;	subwf	Received_I2C_Byte, 0
;	btfsc	STATUS, Z
;	bsf		PORTC, 4

	call	Transmit_RS232

;TEST
	bsf		PORTC, 3
done
	goto	done




	goto	START_PROGRAM


;---------------------------------------------------------------------------------------
;Sub Routines for PIC
;---------------------------------------------------------------------------------------
;i2c Routines
;----------------------------------
Start_Command
	call	Set_SCL_High
	call	Set_SDA_High
	call	Clock_Pulse_Delay
	call	Set_SDA_Low
	call	Clock_Pulse_Delay			;(S) - Sets SDA low while SCL is high
	return
;----------------------------------
SAD_W
	movlw	0x1E
	call	Send_i2c_Byte				;(SAD+W) Slave Address plus write.
	return
;----------------------------------
SAD_R
	movlw	0x1F
	call	Send_i2c_Byte				; Slave Address plus write.
;	call	Clock_Pulse_Delay
	return
;----------------------------------
ACK
	call	Set_SCL_Low					;Ensure SCL is low
	call	Set_SDA_High				;Release SDA
	call	Clock_Pulse_Delay			;Wait (Slave will normally pull down the SDA line by this time)
	call	Set_SCL_High				;Set SCL High for ACK pulse
Clock_Stretch
;	call	Clock_Pulse_Delay
;	btfss	I2C_PORT, SCL
;	goto	Clock_Stretch				;Wait for slave to release clock line (if "stretching")

	call	Clock_Pulse_Delay
	bcf		STATUS, C
	btfsc	I2C_PORT, SDA
	goto	_Error							;Clear carry for NACK
	bsf		STATUS, C					;Set carry for ACK

	btfss	STATUS, C
	goto	START_PROGRAM
	call	Set_SCL_Low
	return
;----------------------------------

NACK
	call	Set_SCL_High
	call	Clock_Pulse_Delay
	call	Set_SCL_Low
	call	Set_SDA_High
	call	Clock_Pulse_Delay
	return

Stop_Command
	call	Set_SCL_High
	call	Clock_Pulse_Delay
	call	Set_SDA_High
	call	Clock_Pulse_Delay
	call	Clock_Pulse_Delay
	return

;Send i2c Byte:
Send_i2c_Byte
	movwf	i2c_transmit_byte
	movlw	0x08	
	movwf	i2c_bit_counter
Send_i2c_Bit
	call	Set_SCL_Low
	call	Clock_Pulse_Delay
	rlf		i2c_transmit_byte, f		;MSB is now in carry
	btfsc	STATUS, C
	call	Set_SDA_High
	btfss	STATUS, C
	call	Set_SDA_Low
	call	Clock_Pulse_Delay
	call	Set_SCL_High
 	call	Clock_Pulse_Delay
	decfsz	i2c_bit_counter, f
	goto	Send_i2c_Bit
	call	Set_SCL_Low
	call	Clock_Pulse_Delay
	return
;-------------------------------------	

Receive_i2c_Byte
	clrf	Received_I2C_Byte
	movlw	0x08
	movwf	i2c_bit_counter
Get_i2c_Bit
	call	Set_SCL_High				;Release SCL
	call	Set_SDA_High 				;Release SDA

	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, 1		;move carry into data (MSB first)
;	call	Clock_Pulse_Delay
	call	Set_SCL_Low	
	call	Clock_Pulse_Delay

	decfsz	i2c_bit_counter, f			;Have 8 bits been received?
	goto	Get_i2c_Bit					;No

	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	
	call	Clock_Pulse_Delay
	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
	call	Clock_Pulse_Delay
	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
	call	Clock_Pulse_Delay
	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
	call	Clock_Pulse_Delay
	return	

;---------------------------------------------------------------------------------------
;---------------------------------------------------------------------------------------
;Delays
Clock_Pulse_Delay
	movlw	0x09
	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
	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	

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

	call	Osc_Stablize


	bcf		PORTC, 2
;	movlw	0x55
;	movwf	Received_I2C_Byte
	movf	Received_I2C_Byte, w
	movwf	transmit_byte_rs232		;moves W to transmit_byte
	movlw	0x08		
	movwf	bit_counter_rs232		;sets 8 bits to send out
	bsf		PORTC, 2
	call	bit_delay
;TEst
;	movlw	0x55
;	movwf	transmit_byte_rs232

Send_Serial_Loop_RS232
	rrf		transmit_byte_rs232, f		;Send one bit
	btfss 	STATUS,	 C
	bsf		PORTC, 2
	btfsc	STATUS,  C
	bcf		PORTC, 2
	call	bit_delay
	decfsz	bit_counter_rs232, f		;Tests if all done
	goto	Send_Serial_Loop_RS232
	bcf		PORTC, 2 ;Is this needed to keep the line high??
	call	bit_delay
	call	bit_delay
;	clrf	transmit_byte_rs232
	return

_Error
	movlw	'E'
	call	Transmit_RS232
	movlw	'R'
	call	Transmit_RS232
	movlw	'R'
	call	Transmit_RS232
	movlw	'O'
	call	Transmit_RS232
	movlw	'R'
	call	Transmit_RS232
	goto	START_PROGRAM

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



	end
 
Why have you removed the delay from Receive_i2c_Byte? The original code that I posted had a delay between the clock pulse and reading the data. This is needed to give the slave time to put the data on the line.

Original,
Code:
I2cReceiveByte	clrf	I2cData
		bsf	I2cData,0	;will cause C when 8 bits received
ReceiveLoop	call	Set_SCL_High	;release clock
		call	Set_SDA_High	;release the data line
		call	Clock_Pulse_Delay
		bcf	STATUS,C
		call	Set_SCL_Low
		btfsc	I2C_PORT,SDA	;read port 
		bsf	STATUS,C	;move bit into carry
		rlf	I2cData,F	;move C into data
		call	Clock_Pulse_Delay
		btfss	STATUS,C	;Have we finished
		goto	ReceiveLoop	;no
		movfw	I2cData		;Return Received byte in W
		return

Mike.
 
Hi Mike; I had removed that in midst of troubleshooting, When I put it back in the code it still performs exactly the same. I assume its an impedance issue but I believe I set the inputs/outputs properly.


I did buy some 24LC16 EEPROMs which finally came in to also start working with trying to communicate with which you had recommended. Do you know of a quick bus verifcation with this chip to verify the i2c communication?
 
Simply putting the delay back won't help. You have also moved where the delay is and it needs moving to before the port read.

I don't have time at the moment but should be able to hoop up a 24LC16 in the next couple of days. Probably Monday.

Mike.
 
Great, thank you Mike.

I have a feeling the issue is coming when I check the PORT bit (either CLK or DAT) to read its level. I still have the issue with checking the CLK line for stretching which is a btfsc function as well as reading the DAT line which uses the same command. These two issues I feel are related.

Also, as a note, The only way I can address the slave correctly and see the DAT line getting pulled low is when I connect the addr bit (pin 8) on the accelerometer to Vcc. When I first started this project I had the addr bit tied to ground (which the datasheet says is perfectly fine, user selectable), but for some reason it did not work.

Anyway, thanks for the help again and please let me know if you have time for the eeprom as a comparison.

Steve
 
Hi Again Mike;

I am also starting to question my evaluation board, There is a Quad Shottk Bus Terminator IC on the evaluation board I'm using (I previously had attached a the schematic) which are in-line with the 2.1k pull-ups. Could these diodes be causing an issue or are they truly needed on a CLK and DAT line?

Steve
 
I have just realised that the 16F684 has analogue inputs and comparators and you are not turning them off. Any pin that is set as analogue always reads back as a 0. You need to clear ANSEL and set CMCON0=7 and your problem should be fixed.

Mike.
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top