CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

I2C slave not detecting ACK/NACK

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
dluu13



Joined: 28 Sep 2018
Posts: 395
Location: Toronto, ON

View user's profile Send private message Visit poster's website

I2C slave not detecting ACK/NACK
PostPosted: Fri Jun 12, 2020 7:55 am     Reply with quote

EDIT: ugh... just saw in the manual that in slave mode, i2c_write does not return ACK. How do I detect ACK status when using i2c slave?

EDIT2: I have added this and now it is only detecting ACK. At this point I am not sure what to do next...
Code:

struct
{
    unsigned GCEN : 1;
    unsigned ACKSTAT : 1;
    unsigned ACKDT : 1;
    unsigned ACKEN : 1;
    unsigned RCEN : 1;
    unsigned PEN : 1;
    unsigned RSEN : 1;
    unsigned SEN : 1;
} SSP1CON2bits;
#word SSP1CON2=getenv("SFR:SSP1CON2")
#word SSP1CON2bits=SSP1CON2

// and

i2c_write(temperature.i8[state & 7]);
ack_status = SSP1CON2bits.ACKSTAT;
if (ack_status == 0) output_toggle(LED2); // ack
else if (ack_status == 1) output_toggle(LED3);  // nack


Hi,

I am using a PIC16LF19156 as a I2C slave (CCS PCM v5.078) and I am not able to detect ACK/NACK properly. I have some code in the ISR that sends the upper and lower byte of a 16-bit int, and the master is meant to send an ACK after reading the first byte, and a NACK after reading the second byte. In my setup, I have an LED that is meant to toggle after getting an ACK, and another LED that is to toggle after receiving a NACK. I notice that all of my toggling only happens on the NACK LED. Therefore, I believe that I am either not sending the ACK, or

I am otherwise reading from my slave just fine. However, as you can see, in my slave ISR, I have to add the "state < 0x82" otherwise, it will try and write a third byte. I hope to try and detect a NACK in order for me to have more flexibility with data sizes later on.

I think this has to do with the slave code because according to my logic analyzer, I am ACKing and NACKing as I coded in the master during reading.

Here is my slave code:
Code:
#case
#include <16LF19156.h>

#DEVICE ADC=12

#fuses BROWNOUT
#fuses BORV27
#fuses LVP
#fuses RSTOSC_HFINTRC_32MHZ // needed to enable PIN_A7 as I/O
#fuses NOVBATEN
#fuses NOEXTOSC // needed to enable PIN_A7 as I/O

#use delay(clock=32000000)

#include "const.h"

#PIN_SELECT U1RX=UART1_RX
#PIN_SELECT U1TX=UART1_TX
#define PC U1STREAM
#USE RS232(BAUD=115200, UART1, BITS=8, PARITY=N, STOP=1, STREAM=PC, ERRORS, RECEIVE_BUFFER=128)

#pin_select SCL1IN=SLAVE_SCL//C3
#pin_select SCL1OUT=SLAVE_SCL
#pin_select SDA1IN=SLAVE_SDA//C4
#pin_select SDA1OUT=SLAVE_SDA
#use I2C(SLAVE, I2C1, address=0x90)

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <crc.c>

#define TEMPERATURE 0x22
#define IMPEDANCE 0x23
#define CONC 0x24

uint8_t I2C_register = TEMPERATURE;

typedef union
{
    uint8_t i8[2];
    uint16_t i16;
} data_t;

data_t temperature;
data_t impedance;
data_t conc;

int main(int argc, char** argv)
{

    enable_interrupts(GLOBAL);
    enable_interrupts(INT_RDA);
    enable_interrupts(INT_SSP);

    delay_ms(1000);

   
    temperature.i16 = 0x1223;
    impedance.i16 = 0x3445;
    conc.i16 = 0x5667;

    while (1)
    {
    }
    return 0;
}

#INT_SSP

void ssp_isr()
{
    unsigned int8 state, incoming;
    uint8_t ack_status = 0;
    state = i2c_isr_state();

    if (state < 0x80) //Master is sending data
    {

        if (state == 0x80)
        {
            incoming = i2c_read(2); //Passing 2 as parameter, causes the function to read the SSPBUF without releasing the clock
        }
        else
        {
            incoming = i2c_read(1);
            if (incoming == TEMPERATURE) I2C_register = TEMPERATURE;
            else if (incoming == IMPEDANCE) I2C_register = IMPEDANCE;
            else I2C_register = CONC;
        }
    }

    //////////////// HERE IS THE CONFUSION //////////////////////
    if (state >= 0x80 && state < 0x82) //Master is requesting data
    {
        if (I2C_register == TEMPERATURE)
        {
            ack_status = i2c_write(temperature.i8[state & 7]);
            if (ack_status == 0) output_toggle(LED2); // ack
            else if (ack_status == 1) output_toggle(LED3);  // nack
        }
        else if (I2C_register == IMPEDANCE)
        {
            ack_status = i2c_write(impedance.i8[state & 7]);     
            if (ack_status == 0) output_toggle(LED2); // ack
            else if (ack_status == 1) output_toggle(LED3);  // nack
        }
        else if (I2C_register == CONC)
        {
            ack_status = i2c_write(conc.i8[state & 7]);
            if (ack_status == 0) output_toggle(LED2); // ack
            else if (ack_status == 1) output_toggle(LED3);  // nack
        }
    }
}


Here is my master code (PIC24FJ128GA204 CCS PCD 5.085):
Code:
#include<24FJ128GA204.h>

#FUSES NOWDT, NODEBUG, NOWRT, NOPROTECT, NOJTAG, ICSP1
#FUSES NOLVR, NOBROWNOUT, NOIOL1WAY, NODSBOR, NODSWDT
#FUSES NOALTCMPI
#FUSES FRC_PLL, PLL_FROM_FRC, PLL8X//FRC_PLL = use internal oscillator, PLL8X, see datasheet ch9.7
// setup the oscillator here, and don't use setup_oscillator()

#PIN_SELECT U3RX=PIN_B5
#PIN_SELECT U3TX=PIN_B6

#USE DELAY(clock=32MHZ)
#USE RS232(BAUD=115200, UART3, BITS=8, PARITY=N, STOP=1, STREAM=PC, ERRORS, RECEIVE_BUFFER=128)

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

// uses the second i2c module. Not remappable on the pic24f128ga204
// PIN_B3 = SCL2
// PIN_B2 = SDA2
#USE I2C(MASTER, I2C2, STREAM=I2CSTREAM, FORCE_HW)

uint8_t i2caddr = 0x90;
uint8_t reshi = 0;
uint8_t reslo = 0;

int main(int argc, char** argv)
{
    delay_ms(1000);
    fprintf(PC, "initialize master\r\n");

    while (1)
    {
        delay_ms(1000);
        i2c_start(I2CSTREAM);
        i2c_write(I2CSTREAM, i2caddr);
        i2c_write(I2CSTREAM, 0x22);
        i2c_start(I2CSTREAM, 1);
        i2c_write(I2CSTREAM, i2caddr + 1);

        reslo = i2c_read(1);
        reshi = i2c_read(0);
        i2c_stop(I2CSTREAM);

        fprintf(PC, "temperature 0x%4X\r\n", make16(reshi, reslo));
       
        delay_ms(1000);
        i2c_start(I2CSTREAM);
        i2c_write(I2CSTREAM, i2caddr);
        i2c_write(I2CSTREAM, 0x23);
        i2c_start(I2CSTREAM, 1);
        i2c_write(I2CSTREAM, i2caddr + 1);

        reslo = i2c_read(1);
        reshi = i2c_read(0);
        i2c_stop(I2CSTREAM);

        fprintf(PC, "impedance 0x%4X\r\n", make16(reshi, reslo));
       
        delay_ms(1000);
        i2c_start(I2CSTREAM);
        i2c_write(I2CSTREAM, i2caddr);
        i2c_write(I2CSTREAM, 0x24);
        i2c_start(I2CSTREAM, 1);
        i2c_write(I2CSTREAM, i2caddr + 1);

        reslo = i2c_read(1);
        reshi = i2c_read(0);
        i2c_stop(I2CSTREAM);

        fprintf(PC, "conc 0x%4X\r\n", make16(reshi, reslo));
    }

    return 0;
}


Here is an image of one transaction:
https://sta.sh/01yswy20lj2d[/code]
Ttelmah



Joined: 11 Mar 2010
Posts: 19516

View user's profile Send private message

PostPosted: Fri Jun 12, 2020 10:12 am     Reply with quote

I2C_WRITE in a slave, can't detect ACK/NACK. This only loads the
byte into the output buffer, it is the master that then has to clock the
byte out. You will (should be) exiting the interrupt, long before the
byte has actually been sent. So the ACK/NACK, doesn't actually exist
at this point....
dluu13



Joined: 28 Sep 2018
Posts: 395
Location: Toronto, ON

View user's profile Send private message Visit poster's website

PostPosted: Fri Jun 12, 2020 11:11 am     Reply with quote

Ok, that makes sense.

As far as I can tell from the logic analyzer trace, it is behaving as I intend. Just not sure if there is anything I am doing incorrectly.

I am wondering now though, if I cannot read the ACK or NACK status, does it matter what the master sends when it calls i2c_read()?
Ttelmah



Joined: 11 Mar 2010
Posts: 19516

View user's profile Send private message

PostPosted: Fri Jun 12, 2020 11:17 am     Reply with quote

Yes.
It depends on the implementation in the particular PIC. They do differ.
On some it doesn't matter and everything resets OK on the STOP. On
others the NACK is needed.
dluu13



Joined: 28 Sep 2018
Posts: 395
Location: Toronto, ON

View user's profile Send private message Visit poster's website

PostPosted: Fri Jun 12, 2020 1:24 pm     Reply with quote

Thanks for the info. I just tested it, and I need to NACK before stopping or else my slave will hang up.
Ttelmah



Joined: 11 Mar 2010
Posts: 19516

View user's profile Send private message

PostPosted: Sat Jun 13, 2020 1:12 am     Reply with quote

That is why it is safer to always 'assume' it will be needed. Smile
dluu13



Joined: 28 Sep 2018
Posts: 395
Location: Toronto, ON

View user's profile Send private message Visit poster's website

PostPosted: Tue Jul 21, 2020 7:28 am     Reply with quote

Reviving this old thread:

I'm having some problem again with this I2C slave. Before, I had it on a Microchip Curiosity board, but now I have it on a PCB.

The master is the same as before, running a PIC24FJ128GA204 on the PIC24 Curiosity board from Microchip.

It is curious that I am now not able to read more than 2 bytes from the slave. It locks up after I send the first ACK (i.e. start, send addr, send data register, repeat start, read with ack, read with nack, stop). It is fine when I am requesting single bytes (i.e. start, send addr, send data register, repeat start, read with nack, stop).

This appears to happen because the clock speed was decreased from 32 MHz to 4 MHz.

32 MHz -> works
16 MHz -> works
8 MHz -> works
4 MHz -> does not work

On the master side, my #use I2C is set as follows:
Code:
#USE I2C(MASTER, I2C2, STREAM=I2CSTREAM, FORCE_HW)

I have tried to set:
FAST=100000
SLOW

But when using 4 MHz there is no difference and the slave still locks up after the first ACK. I would like to use the lower speed because it will then use less power. I am now temporarily just running at 8 MHz.
Ttelmah



Joined: 11 Mar 2010
Posts: 19516

View user's profile Send private message

PostPosted: Tue Jul 21, 2020 7:40 am     Reply with quote

A slave I2C PIC, always needs to be running fast enough to actually
load the reply byte before the master starts to clock it out.
If you search here, you will find lots of threads where people are told to
make sure the slave is running at least as fast as the master.
This is down to the total time between the first I2C address write, and
the I2C read on the master. So not changed by the actual I2C speed.

Honestly clock the slave as fast as possible. Why not put it to sleep till
an I2C transaction starts?. Will give much lower power consumption than
reducing the clock.
dluu13



Joined: 28 Sep 2018
Posts: 395
Location: Toronto, ON

View user's profile Send private message Visit poster's website

PostPosted: Tue Jul 21, 2020 7:45 am     Reply with quote

Ttelmah wrote:
A slave I2C PIC, always needs to be running fast enough to actually
load the reply byte before the master starts to clock it out.
If you search here, you will find lots of threads where people are told to
make sure the slave is running at least as fast as the master.
This is down to the total time between the first I2C address write, and
the I2C read on the master. So not changed by the actual I2C speed.

Honestly clock the slave as fast as possible. Why not put it to sleep till
an I2C transaction starts?. Will give much lower power consumption than
reducing the clock.


I thought the slave would hold the clock line until it was ready for the Master to start clocking. But I will take your advice with high clock speed and sleep... It's about time I learned how the sleep modes work anyway...
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group