|
|
View previous topic :: View next topic |
Author |
Message |
dluu13
Joined: 28 Sep 2018 Posts: 395 Location: Toronto, ON
|
I2C slave not detecting ACK/NACK |
Posted: Fri Jun 12, 2020 7:55 am |
|
|
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: 19563
|
|
Posted: Fri Jun 12, 2020 10:12 am |
|
|
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
|
|
Posted: Fri Jun 12, 2020 11:11 am |
|
|
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: 19563
|
|
Posted: Fri Jun 12, 2020 11:17 am |
|
|
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
|
|
Posted: Fri Jun 12, 2020 1:24 pm |
|
|
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: 19563
|
|
Posted: Sat Jun 13, 2020 1:12 am |
|
|
That is why it is safer to always 'assume' it will be needed. |
|
|
dluu13
Joined: 28 Sep 2018 Posts: 395 Location: Toronto, ON
|
|
Posted: Tue Jul 21, 2020 7:28 am |
|
|
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: 19563
|
|
Posted: Tue Jul 21, 2020 7:40 am |
|
|
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
|
|
Posted: Tue Jul 21, 2020 7:45 am |
|
|
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... |
|
|
|
|
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
|