View previous topic :: View next topic |
Author |
Message |
rovtech
Joined: 24 Sep 2006 Posts: 262
|
I2C slave will only send one byte |
Posted: Sat Jan 21, 2017 2:39 pm |
|
|
I am using a PIC16F884 as master sending to slave PIC16F1509 with the following software stripped to relevant code.
The slave receives the instructions in base_ctrl and responds correctly however when it replies it sends the test byte 48 twice (both base_status and base_posn are 48 although they are initialized to different values). It should send 48 then 24. I have not been able to find an example that has a slave send more than one byte in reply. I tried playing with I2C_poll but the example in the CCS manual “if(i2c_poll())” gives a compile error so I don’t know how to use it.
The code for the MASTER:
Code: | #use I2C (master, SCL=PIN_C3, SDA=PIN_C4)
#define BASE_WRT_ADDR 0X1E
#define BASE_READ_ADDR 0x1F
// send commands to base section
I2C_START (); // start I2C
I2C_WRITE (BASE_WRT_ADDR); // addr of Write Base Section
I2C_WRITE (base_ctrl); // send switch state
I2C_STOP (); // stop I2C
// read base status
I2C_START (); // start I2C
I2C_WRITE (BASE_READ_ADDR); // addr of Read Base Section
base_status = I2C_READ(); // get base status
base_posn = I2C_READ(); // get base position
I2C_STOP (); // stop I2C
|
The Code for the SLAVE:
Code: | #use i2c (SLAVE, SCL=PIN_B6, SDA=PIN_B4, address=0x1E)
// Interrupt on I2C
#INT_SSP
void ssp_interrupt () // have an interrupt
{
char incoming, state; // variables
state = i2c_isr_state (); // get state
if (state < 0x80) // master is sending data
{
incoming = i2c_read (); // throw away device address if state = 0
if (state == 1) // first data received is base ctrl
base_ctrl = incoming;
// if (state == 2) // third data received is base posn
// base_posn = incoming;
}
if (state >= 0x80) // master is requesting data from slave
{
base_status=48;
base_posn=24;
i2c_write (base_status); // send base status
i2c_write (base_posn); // send base position
}
}
|
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
|
rovtech
Joined: 24 Sep 2006 Posts: 262
|
|
Posted: Sat Jan 21, 2017 7:22 pm |
|
|
Thanks PCM Programmer.
If I understand correctly the slave can only reply once per request? As you said, one reply per interrupt.
That program uses counters to make the multi requests and the slave uses the ISR state as a counter to get data from an array.
The guy's mistake was not limiting states above 7 counts (0x87), interesting but nothing to do with my problem.
What I need to do is send another request from the master and reply from the slave with the second byte? I think.
Where can I find the slave reply description? All my reference books imply only one send per master request but none of them actually say so, and don't include examples of how to send more than one reply.
Where did the second "48" come from? I assume the second read in Master just returned the unchanged buffer from the first read?
Edit:
Having just opened the slave program to edit I realize why it must be done per the example. The ISR has no way to know what the request is or what to send. Is this correct?
Second edit:
Looking at my program it is almost exactly the same as the example. All I had to do is place a (0) in the brackets after the second i2c_READ in my Master program to tell it No Acknowledge. I really don't understand it but it works. I will post both working programs later after I'm satisfied. |
|
|
rovtech
Joined: 24 Sep 2006 Posts: 262
|
|
Posted: Sat Jan 21, 2017 9:15 pm |
|
|
Actually a little more to it. The slave still has to know what to send.
Master program just has the 0 in the bracket of the second I2C_READ(0);
Code: | // send commands to base section
I2C_START (); // start I2C
I2C_WRITE (BASE_WRT_ADDR); // addr of Write Base Section
I2C_WRITE (base_ctrl); // send switch state
I2C_STOP (); // stop I2C
// read base status
I2C_START (); // start I2C
I2C_WRITE (BASE_READ_ADDR); // addr of Read Base Section
base_status = I2C_READ(); // get base status
base_posn = I2C_READ(0); // get base position
I2C_STOP (); // stop I2C
|
The slave uses 'state' to know if this is the first or second data to send:
Code: | // Interrupt on I2C
#INT_SSP
void ssp_interrupt () // have an interrupt
{
char incoming, state; // variables
base_status=33;
state = i2c_isr_state (); // get state
if (state < 0x80) // master is sending data
{
incoming = i2c_read (); // throw away device address if state = 0
if (state == 1) // first data received is base ctrl
base_ctrl = incoming;
}
if (state >= 0x80) // master is requesting data from slave
{
if (state==0x80)
i2c_write (base_status); // send base status
if (state==0x81)
i2c_write (base_posn); // send base position
}
} |
Just one slight problem; if there is a mistake the I2C can hang the PIC. I'm not sure how to get around this one. Perhaps it will never happen if the requests match the replies. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sat Jan 21, 2017 9:56 pm |
|
|
Give an example of a "mistake". |
|
|
rovtech
Joined: 24 Sep 2006 Posts: 262
|
|
Posted: Sun Jan 22, 2017 8:50 am |
|
|
Sorry, I should have explained;
If the 0 is left out of the last
in the Master program then the Master PIC hangs presumably waiting for the acknowledge.
Is there any danger of i2c communications getting confused and hanging? Is there a timeout that can be used to prevent this? Perhaps the WDT? I have never used it but think it would still be in an endless loop cutting off the rest of the program.
Perhaps you can explain it better? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19549
|
|
Posted: Sun Jan 22, 2017 9:46 am |
|
|
You can do a timeout.
However on the only chip where I have seen issues with I2C, this wouldn't solve the problem since it is a hardware fault (PIC4431)....
The slave is waiting for the end of transaction.
The lesson is get your transactions right....
You'd need to timeout at both ends and reprogram the I2C interfaces to release the line. |
|
|
|