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 i2c communication, recieving an ACK pulse from the slave

Status
Not open for further replies.

Steve311

Member
Hi All, I am working with a PIC16F684 which does not have i2c hardware, so I am doing it all in the software. I am stuck with receiving an ACK pulse from the slave (Kionix KXTJ9). Its slave address is 0x1C. My code below shows a start command (S), and slave address plus write (SAD + W). I then set RC3 high if the PIC receives an ACK from the KXTJ9 (just as a test, and in infinite loop to terminate the program for this troubleshoot.

I should be able to see the data line get pulled low by the slave. However I do not. Here are my i2c lines (as in code):

SCL = RC4
SDA = RA2

Thanks all!

;.......................................................................................................................
;.......................................................................................................................
;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 _INTRC_OSC_CLKOUT & _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
wait_startup_counter
endc
;.......................................................................................................................
;.......................................................................................................................
;Define Global Deifnitions:
;#define _p825Volt 0x4e
;.......................................................................................................................
;.......................................................................................................................
START_PROGRAM
;Initialize Inputs/Outputs, and Registers on the PIC16F684.
;Initialize TRISA and TRISB registers.
bsf STATUS, RP0 ;Sets bit RP0 in address STATUS to 1 (switches from bank 0 to 1).
movlw b'00000000'
movwf TRISC ;Assigns all TRISC as outputs.
movlw b'00000000'
movwf TRISA ;Assigns all TRISA as outputs. Note - RA2 must be bi-directional. Default is output.
movlw 0x03
movwf OSCCON ;Loads internal 31.25kHz clock
clrf ANSEL ;Clears default analog inputs and allows for digital I/O on analog pins
bcf STATUS, RP0 ;Moves back to bank 0 for execution

movlw b'00000111'
movwf CMCON0 ;Turns analog comparators off and allows for digital I/O on all pins

;......................................................................................................................
;.......................................................................................................................


;READ Register from accelerometer -- WHO_AM_I

bsf PORTA, 2 ;Set DATA high
bsf PORTC, 4 ;Set CLK high


call Wait_Startup ;Startup delay

call Start_Command
call SAD_W
call ACK



bsf PORTC, 3 ;TEST -- If program recieves ACK, set RC3 and loop forever
done
goto done




;---------------------------------------------------------------------------------------
;---------------------------------------------------------------------------------------
;Sub Routines for PIC

Send_i2c_Byte
movwf i2c_transmit_byte
call Set_RA2_Output
movlw 0x08
movwf i2c_bit_counter
Send_i2c_Bit
bcf PORTC, 4 ;Sets Clock high to say a data bit is not present yet.
rrf i2c_transmit_byte, f
btfsc STATUS, C
bsf PORTA, 2
btfss STATUS, C
bcf PORTA, 2
call Clock_Pulse_Delay
bcf PORTC, 4
call Clock_Pulse_Delay
bsf PORTC, 4 ;Sets CLock High to allow for another low transition to say a new data bit is present.
call Clock_Pulse_Delay
decfsz i2c_bit_counter, f
goto Send_i2c_Bit
return


Set_RA2_Input
bsf STATUS, RP0
movlw b'00000100'
movwf TRISA
bcf STATUS, RP0 ;Sets RA2 as input to check for the line being low.
return

Set_RA2_Output
bsf STATUS, RP0
movlw b'00000000'
movwf TRISA
bcf STATUS, RP0 ;Sets RA2 as output for sending data.
return

;---------------------------------------------------------------------------------------
;---------------------------------------------------------------------------------------
;Delays
Clock_Pulse_Delay
movlw 0x0F
movwf pw_counter
Clock_Pulse_Delay_Wait
nop
decfsz pw_counter, f
goto Clock_Pulse_Delay_Wait
return

Wait_Startup
movlw 0xFF
movwf wait_startup_counter
Wait_Startup_Count
nop
decfsz wait_startup_counter, f
goto Wait_Startup_Count
return

;---------------------------------------------------------------------------------------
;---------------------------------------------------------------------------------------
;i2c routines

Start_Command
call Clock_Pulse_Delay
call Set_RA2_Output
bsf PORTA, 2 ;Sets DATA high.
bsf PORTC, 4 ;Turns on CLK
call Clock_Pulse_Delay
bcf PORTA, 2 ;Toggles DATA from high to low indicating a start command (S).
call Clock_Pulse_Delay
return
;----------------------------------
SAD_W
call Set_RA2_Output
; movlw 0x1C
movlw 0x38
call Send_i2c_Byte ; Slave Address plus write.
call Clock_Pulse_Delay
return
;----------------------------------
ACK
bcf PORTC, 4 ;Set CLK low
call Clock_Pulse_Delay
bsf PORTA, 2 ;Sets DATA high
call Clock_Pulse_Delay ;Delay
bsf PORTC, 4 ;Release Clock
call Set_RA2_Input ;Sets DATA as input
Get_ACK
btfsc PORTA, 2
goto Get_ACK
bsf PORTC, 4 ;Set CLK high
call Set_RA2_Output
return
;-----------------------------------


end
 
You can't set the lines high by setting the port pin. You have to set the port to input and let the pullup resistor make the line high. The reason for this is two fold. The slave device will hold the clock line low if it needs time to fetch the data (Called clock stretching). The slave device will pull the line low to generate an acknowledge. Note also that any RMW instruction (bsf etc) while the pin is an input will write a 1 to the clk/data pins. So, you should never write to the port(s) during the I²C operations, only the TRIS register(s).

Mike.
 
Thanks, so I cannot set the SDA or SCL line high at all? I need to just release them (Set to inputs) and let the them pull up to vdd? What about setting them low, I must have to drive them low right? And When I release them to the pull up resistors, I must wait a delay for them to actually settle, correct?

This is true for both the SDA and SCL lines? I can only drive them low?
 
Correct. However, you have to make sure that the output latches are set to zero. This is normally done once by clearing the port(s). Note, you cannot normally use bcf for this as the two lines are normally on the same port and the second bsf will write a 1 back to the other pin. The safest way to do this is read the port, clear the bit(s) in W with an ANDLW instruction and then write it back. This only needs to be done once at the start of each I²C sequence.

Edit, Also, after you release the clock line, you must wait for it to become high before proceeding. This allows slower devices to make the master pause long enough for them to do their thing.

Edit2, Another common problem with I²C is the two ends can get out of sequence and you might need to power down between attempts to get it working. I have a software work around that I can post if you encounter this problem.

Mike.
 
Last edited:
Thanks Again Mike for the help.

So I understand correctly,

1)
I would need to set the SCL line to an input to test BEFORE checking the SDA line during an ACK pulse for line stretching? And if the slave does NOT recieve the 8 data bits, will is hold the SCL low? Or just release it and not give an ACK on the data line?

2)In my code above, when I set SDA (RA2) to an input go look for an ACK, I do not use bcf or bsf, I simply use "btfsc"to see if the line it pulled low. You are saying this is inproper and should be looking at the TRIS register instead to ensure the latches are set to 0?

3) When sending data bits on the SDA line, I can only drive it low for 0's, and must release the line (set to input) and wait for it to be pulled high to send a 1?, then set back to an output to send another 0?

Thanks in Advance, Much of help.
Steve
 
Status
Not open for further replies.

Latest threads

Back
Top