News:

Let's find out together what makes a PIC Tick!

Main Menu

Debugging i2c NACK issues

Started by TimB, Feb 26, 2023, 06:43 PM

Previous topic - Next topic

TimB


Hi All looking for and pointers to why I'm having issues with the i2c code

For starters I'm doing my work in Labcenter VSM and that means I could have issues caused by that

I started with code that works on a pic18f13k22 talking to another pic18f13k22 running at 64mhz. Again in the VSM I can talk to and read from the pic acting as the slave. Works great in the individual commands and i2cin / i2cout

So I used the slave code to convert it to the Pic16f1823 running at 32mhz.

What I get now as soon as I read 1 byte from the slave I get a NACK (debugging in the VSM) so slave basically says ok no more data needed and I think shuts down until it gets a new start. I can see though that the master is caring on requesting more data.

Now looking up what causes a NACK it seems to be the master in this case saying I did not like that data byte. But I have seen that it can rx that data fine coming from the Pic18f13k22

I have run out of places to look. At this time I do not have the kit to test the master side using a real device. I do have the hardware that is the slave though. Until I get the master sorted I'm stuck using the VSM

Any pointers appreciated

Tim

This is the slave code

    Symbol ci2cSLAVE_ADDR = $60                                                     ' Even number, must match address sent by Master PIC
                                                                                    ' To Read from Slave, make address from Master = (address + 1)
    Symbol SSPIF = PIR1.3                                                           ' SSP Interrupt flag Bit
    Symbol PEIE = INTCON.6                                                          ' Interrupt Peripheral enable

    Symbol ci2cS_TO_M_BUF_LEN = 7                                                   ' Buffer size for the MASTER Read data Buffer   Status and 3 x words of the ADC
    Dim ai2cS_TO_M_REGS[ci2cS_TO_M_BUF_LEN] As Byte                                 ' MASTER Read  Buffer array declare (Slave to Master)
    Dim bi2cS_TO_M_REG_INDEX As Byte                                                ' MASTER Read  Buffer Index


    Dim bi2cCASE_SWITCH As Byte      ' States situation to be used by Select function

    ' There can be 5 possible i2c states, SSPSTAT register relevant bits
    Symbol ci2cSTATE1 = $09          ' D_A=0,S=1,R/W=0,BF=1. Master Tx, slave addr
    Symbol ci2cSTATE2 = $29          ' D_A=1,S=1,R/W=0,BF=1. Master Tx, Data
    Symbol ci2cSTATE3 = $0C          ' D_A=0,S=1,R/W=1,BF=0. Master Rx, slave addr+1
    Symbol ci2cSTATE4 = $2C          ' D_A=1,S=1,R/W=1,BF=0. Master Rx, Data with ACK
    Symbol ci2cSTATE5 = $28          ' D_A=1,S=1,R/W=0,BF=0. Master Rx, Data with NACK

    Dim bi2cIntTemp as Byte                                                         ' Temp variable

    Dim pi2cClk as portc.0
    Dim pi2cDat as portc.1




     Goto CodeStart

    ; HIGH PRIORITY INTERRUPT HANDLER
I2C_INTERRUPT:

    Context Save
    ; CLear the interrupt flag
        If PIR1bits_SSP1IF = 1 Then           ' Check if I2C interrupt flag is set
            PIR1bits_SSP1IF = 0              ' Clear I2C interrupt flag

        ; Mask the SSPSTAT reg to get the status so it can be used in a select case system
        bi2cCASE_SWITCH = SSPSTAT & %00101101

        Select bi2cCASE_SWITCH

        Case ci2cSTATE1                                                                 ' Master write operation, (address Byte goes in SSPBUF. (ignored)
            SSPOV = 0                                                                   ' Clear the receive overflow flag
            bi2cIntTemp = SSPBUF                                                        '
            CKP = 1                                                                     ' Allow master to continue by re-enabling SCL

        Case ci2cSTATE2                                                                 ' Master write operation, Data Byte (Master-->Slave) in SSPBUF
            ; We take no action as we do not accept incoming data
            SSPOV = 0                                                                   ' Clear receive overflow flag
            bi2cIntTemp = SSPBUF
            CKP = 1                                                                     ' Tell Master to continue by re-enabling SCL


        Case ci2cSTATE3                                                                 ' Master is beggining a new Read operation by initiating a START Or RESTART
            bi2cS_TO_M_REG_INDEX = 0                                                    ' So we reset the buffer index

            SSPBUF = ai2cS_TO_M_REGS[bi2cS_TO_M_REG_INDEX]                              ' Loads the first byte (from USB_BUF Slave-->Master) to be sent
            Inc bi2cS_TO_M_REG_INDEX                                                    ' Increment buffer Index
            CKP = 1                                                                     ' High SCL so Master can get the Byte by shifting it out

        Case ci2cSTATE4                                                                 ' Master Read operation, last Byte was Read Data (ie STATE3), SSPBUF empty.

            SSPBUF = ai2cS_TO_M_REGS[bi2cS_TO_M_REG_INDEX]                              ' Get next byte (from USB_BUF Slave-->Master) To be sent

            If bi2cS_TO_M_REG_INDEX < ci2cS_TO_M_BUF_LEN then
                Inc bi2cS_TO_M_REG_INDEX                                                ' Increment buffer Index
            Endif

            CKP = 1                                                                     ' High SCL so Master can get the Byte by shifting it out

        Case ci2cSTATE5

            Nop
            CKP = 1

        EndSelect

Endif

    Context Restore




    Proc INIT_I2C()

    ' Configure I2C registers

        SSP1CON1 = $36                                                       ' SSP settings: i2c slave mode, 7-bit address, enable SSP
        SSP1ADD = ci2cSLAVE_ADDR                                             ' Initial slave address setting
        SSP1CON2bits_SEN = 1                                                       ' Enable clock streaching
        SSP1STAT = 0                                                         ' Clear SSP Status reg
        PIE1bits_SSP1IE = 1                                                 ' Enable SSP interrupts (SSPIE)
        PIR1bits_SSP1IF = 0                                                 ' Clear SSP Interrupt flag
        input  pi2cClk
        input pi2cDat

    EndProc


tumbleweed

QuoteWorks great in the individual commands and i2cin / i2cout
Are you using the software i2c functions on the master side?
If so, are you sure they support clock-stretching? Many software implementations don't.

The slave requires the master to support it, and since the 16F slave would be slower than the 18F version that might explain what you're seeing.

TimB

Quote from: tumbleweed on Feb 27, 2023, 03:24 PM
QuoteWorks great in the individual commands and i2cin / i2cout
Are you using the software i2c functions on the master side?
If so, are you sure they support clock-stretching? Many software implementations don't.

The slave requires the master to support it, and since the 16F slave would be slower than the 18F version that might explain what you're seeing.

Thanks tumbleweed

You were spot on I reduced the master to 16mhz and it is working again

Hmm though it now puts me in a bind I designed all my PCB's as if I was using software i2c. I will have to see if I can work out what Les's code is doing and see if I can modify it to implement clock stretching or a fudge that will give the slave time as if it had clock stretching.

Only other option is for the one board running at 32mhz to on the fly turn the main 64mhz board OSC down using OSCCON or something

Again many thanks

Tim

TimB


Well the fix was actually pretty easy

I just used the individual i2c commands and added some delays

I'm sure I do not need them all and they could be shorter but when I need to talk to this board I can use this code


      ; RECEIVE BYTES EXAMPLE
      ;; Now Read some numbers without releasing i2c bus,
      ;; could do a Stop here And a new START.
      BStart                     ;; A restart
      BusOut SLAVE_ADDR_READ     ;; Slave read address
      DelayUS 40
      For i = 0 To 6
         RXBUF[i] = BusIn        ;; Note that Slave may be delaying response
                                 ;; by holding SCL Low. i2c_read() monitors
                                 ;; SCL And takes care of this.
         DelayUS 40
         BusAck                  ; acknowlegde all but last buffer element
         DelayUS 40
      Next

      DelayUS 40

      BusIn RXBUF#3             ;; Last one nack (high), resets slave i2c logic. Very important, i2c bus will hang otherwise.
      BStop                     ;; Release the i2c bus