|
|
View previous topic :: View next topic |
Author |
Message |
barryg
Joined: 04 Dec 2006 Posts: 41
|
I2C interrupt problem, slave multiple reads |
Posted: Wed May 16, 2007 7:46 pm |
|
|
I have been working with i2c between PIC's for several years, and I'm now trying to develop a fancier communications link with it. So I am trying to read a string of bytes from the slave end. I've got the slave holding up the works (the clock line) just like a hundred people before me. What I think is different is the explanation.
As everyone eventually finds out, when the SSP in the slave gets a read command, it holds the clock low until a character is loaded into SSPBUF, or in CCS-speak, we do an i2c_write().
Now, I have an interrupt routine, using i2c_isr_state(), much like the one shown in the compiler manual. It will work, if it's kept simple like that. But I have a little bit of code following that and it seems that the delay causes it to miss the next interrupt. If I bypass the extra code, I can read a string of bytes.
I think what happens is that when i2c_write() is called, the character goes out almost immediately, and if I can get out of the ISR fast enough, I will catch the next interrupt. But any delay and the buffer-going-empty-again happens before I leave the interrupt routine. Then I am back in main() waiting forever.
As an added test, I wait in main for several seconds, then arbitrarily put a character with i2c_write(). That character goes out immediately, and then there is an interrupt requesting the next one! Then, leaving the ISR too late, it all sits until the next timeout. (I've got a 'scope telling me when things happen and what state they're stuck in).
I wonder if this is really what's happening, or have I overlooked something? I can think of workarounds, but I'd like to get as close to ideal as I can. Otherwise I would be dropping back to the kind of code I had before.
I can post other info if it seems relevant (other than the slave being PIC16F877A) but I thought I'd take a shot with this explanation. Don't know how many have tried this. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu May 17, 2007 12:34 am |
|
|
What is the crystal frequency used with your slave 16F877A ? |
|
|
barryg
Joined: 04 Dec 2006 Posts: 41
|
|
Posted: Thu May 17, 2007 12:30 pm |
|
|
The PIC is clocked by a 7.3728 MHz oscillator module. |
|
|
barryg
Joined: 04 Dec 2006 Posts: 41
|
|
Posted: Thu May 17, 2007 6:52 pm |
|
|
I was waiting to see what you were thinking, but I guess that ordinary clock speed stumped you
I was bouncing back and forth between thinking it was something in the compiler and something in the PIC. I worked through the details, and did some single-stepping of the machine code. Here's what I came up with.
The return-from-interrupt code that gets compiled in (presumably after the function defined with "#int_ssp" ) has as the first instruction:
This may be helpful in many circumstances (certainly if you didn't clear it yourself, you re-interrupt yourself forever) but in this case it causes the problem described in the original message. In the case here, the interrupt is cleared when the new character is loaded by i2c_write(). It sets again when the SSP gets the ACK from the master end. Then, this exit sequence clears it.
By hand patching a NOP over that instruction, the i2c frames fly at their original speed, and I can even put the artificial delays back into interrupt handler and it's immune to that.
Anyway, I'm not sure what to make of this, but I suppose a note to CCS is in order so they can at least consider the problem (and have some information the next time someone trips over it). As for me, I don't really want to be hand-patching library code after each compile, so my other choice is to get the i2c_write() as close the the end of the ISR, and pepper the comments with notes like "don't put any more code here" and "if you get hangs here, be sure you are aware that..."
I'll also do some timing tests to get an idea of how much breathing room there actually is at the place in question.
Barry |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu May 17, 2007 7:02 pm |
|
|
I was going to suggest trying a 20 MHz crystal but I didn't have any
data to prove that it would conclusively work, and by what margin.
I also didn't have the time to get that data, so I didn't post a reply.
(or at least, I postponed posting a reply -- thinking optimistically). |
|
|
Ttelmah Guest
|
|
Posted: Fri May 18, 2007 2:09 am |
|
|
You can make CCS not clear the interrupt, by defining the interrupt routine, with the keyword 'noclear'. So:
Code: |
#INT_SSP noclear
void your_int_handler(void) {
//interrupt code
}
|
This can be very useful if you are handling the clearing yourself, and may well solve your problem, without needing to 'bodge'.
Best Wishes |
|
|
barryg
Joined: 04 Dec 2006 Posts: 41
|
|
Posted: Fri May 18, 2007 3:21 pm |
|
|
Thanks for the tip! That's the sort of thing I was looking for. I know some of the compiler functions have extra options, and they're usually the result of special conditions like the one I found. Seeing it in print is a sort of confirmation of "yes, sometimes you'll need this", as opposed to "you won't find this feature because it's a mistake to try doing this". It also directly solves the problem I stated earlier, " I don't really want to be hand-patching library code after each compile".
In other news--I did do the timing tests I said I was going to try. I flipped port bits as markers on the 'scope, and here's what I found. When you call i2c_write(), even though the slave hardware can send a character without intervention, the call does not return until the character has been sent! The amount of time you then have works out to about the width of one clock. If you can be out of the ISR by the time the SSP decides it wants another character, you're OK.
Actually, the BF bit clears after the 8th bit is sent. Somewhere near the conclusion of the 9th bit (where the slave finds out if the master wants more characters) the slave SSP will lock the clock down and set the interrupt request. If your ISR clears the interrupt after this time, you will miss the event.
Maybe a picture will help.
The blue trace is the "marker". The yellow is the i2c clock. The code that produced this follows. Code: | void i2c_int_routine(void)
{
#use i2c(SLAVE, SCL=SCL_SYS, SDA=SDA_SYS, ADDRESS=0x82, SLOW, FORCE_HW)
output_low(PIN_B2); // Marker
output_float(PIN_B2);
i2c_write(0x77); // Send back the same character each time no matter what...
output_low(PIN_B2); // Marker
output_float(PIN_B2);
delay_cycles(4); // Doesn't take much to be "too late"
output_low(PIN_B2); // Marker
output_float(PIN_B2);
} |
I also tried putting multiple calls to i2c_isr_state() in my ISR. I discovered that each time you call it, it thinks another character is going out. That is, the return value keeps incrementing. That's because there isn't any real way the hardware counts characters, so it has to assume that if one is being requested, it's the next one.
Software is like fractals. No matter how close you look at something there is always some amount of complexity lurking below.
Barry |
|
|
|
|
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
|