|
|
View previous topic :: View next topic |
Author |
Message |
jallum
Joined: 05 Mar 2008 Posts: 9
|
i2c_isr_state() return value not incrementing? |
Posted: Wed Mar 05, 2008 5:49 pm |
|
|
Hello, I'm new to CCS C and I2C but so far I have been able to get several things working. I am having problems implementing the SLAVE hardware on my 16f877A. The chip is also being used as the master on a different bus, and this is working properly.
I have been able to get the following things to work in SLAVE mode:
*address match and ACK
*receive data command and ACK
However, to get the second byte (the "command"), I have had to cheat. here is the SSP interrupt routine:
Code: |
#int_SSP // interrupt on i2c activity
void i2c_isr(void)
{
int state, incoming;
state = i2c_isr_state();
incoming = i2c_read(I2CS,1);
cmd = incoming;
if (state == 0)
{
}
else //if (state == 1)
{
printf("state=%x\n\r",state);
//cmd = incoming;
}
}
|
I know that I don't really want to have a printf in the interrupt. That is only there for debugging, and it never executes, which means i2c_isr_state() always returns 0.
I have a scope here and see that my master sends the address and then one byte. The address matches and the PIC ACKs. The next byte is transmitted ~50usec later by the master and it is also ACKed. Future transmissions of two bytes from the master have the same effect, with the PIC ACKing both bytes appropriately.
I've found that I must do the i2c_read() unconditionally upon entering the interrupt routine. If i2c_read() is not performed after the address match, I find that the pic does not ACK the data byte. This is contrary to the examples of i2c_isr_state() in the CCS documentation and examples.
Can anyone explain why i2c_isr_state() is not incrementing? |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed Mar 05, 2008 6:45 pm |
|
|
For a question like this, you've got to post the slave program, the master
program, and the compiler version. The programs must be complete, and
compilable with no errors. |
|
|
jallum
Joined: 05 Mar 2008 Posts: 9
|
|
Posted: Wed Mar 05, 2008 7:27 pm |
|
|
PCM programmer wrote: | For a question like this, you've got to post the slave program, the master
program, and the compiler version. The programs must be complete, and
compilable with no errors. |
I was afraid you'd ask. Here is a small complete program for the slave:
Code: |
#include <16F877A.h>
#device adc=10
#FUSES NOWDT //No Watch Dog Timer
#FUSES HS //High speed Osc (> 4mhz)
#FUSES PUT //Power Up Timer
#FUSES PROTECT //Code protected from reads
#FUSES NODEBUG //No Debug mode for ICD
#FUSES BROWNOUT //Brownout reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD //No EE protection
#FUSES NOWRT //Program memory not write protected
#use delay(clock=20M)
#use i2c(SLAVE, sda=PIN_C4, scl=PIN_C3, address=0x60, SLOW, force_hw, stream=I2CS)
#use i2c(MASTER, sda=PIN_C1, scl=PIN_C0, stream=I2CM)
#use rs232(baud=57600, parity=N, xmit=PIN_C6, rcv=PIN_C7, bits=8, stream=COM1)
#use fast_io(D)
int rcv_buf[0x10]; //status bytes for communication with TR-10
int wrt_buf[0x10]; //status bytes for communication with TR-10
int cmd=0xFF; //i2c command read from TR-10
#int_SSP // interrupt on i2c activity
void i2c_isr(void)
{
int state, incoming;
state = i2c_isr_state();
if(state < 0x80)
{
incoming = i2c_read(I2CS,1);
if (state == 1)
{
cmd = incoming;
printf("CMD received");
}
else if (state > 1)
{
rcv_buf[state-2]=incoming;
}
}
else
{
i2c_write(I2CS,wrt_buf[state-0x80]);
}
}
#int_COMP //interrupt on comparator
void COMP_isr(void)
{
}
void main()
{// Enter Bootloader if Pin B5 is low after a RESET
int i;
enable_interrupts(INT_SSP);
enable_interrupts(GLOBAL);
while (TRUE)
{
for (i=0;i<10;i++)
{
if(i==0)
{
delay_ms(500);
printf("\n\rcmd: %x", cmd);
}
printf(".");
delay_ms(100);
}
}
}
|
This is slightly different than I posted above. I just compiled this and tested it, same problem. The printf in the SSP interrupt never executes. I am sending two bytes at a time to the PIC, the first is the address and the second is the intended command. The PIC ACKs both bytes.
The rest of my program uses the PIC as a master also, on different pins, with the SW implementation. I've removed it from the above for simplicity.
The master of this bus, on which the PIC is a slave, is non-PIC hardware developed by another engineer. I can request changes to it but I don't think there is any need to. I've already checked the high and low times of SCL on the scope and they are ok according to the specs in the 16F877A data sheet. So, the source code for the master is probably useless in this case.
Thanks in advance for your help. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed Mar 05, 2008 7:34 pm |
|
|
Quote: |
The master of this bus, on which the PIC is a slave, is non-PIC hardware
developed by another engineer. |
If you know what bytes the master is sending, then write a PIC program
to emulate it. Put the emulation program into a PIC demo board.
Connect this board to your slave board, instead of the normal master.
Now you have a test setup that you can control. See if it works with
the slave. If it does, then it means there's something different about
the baud rate, or the bit timing, or the spacing of the bytes, or the i2c
drivers, etc., between your emulation board and the real master. You
can now investigate the problem. |
|
|
jallum
Joined: 05 Mar 2008 Posts: 9
|
|
Posted: Wed Mar 05, 2008 7:56 pm |
|
|
There's an example of what I'm seeing on the scope. Note the address shown on the scope, "30" is the 7 bit address display. If I change the scope to display 8 bit address, it would say "60". So I am sure there is an address match here.
I have very good control over the master, with software written for a PC and our custom hardware. I can change the bytes that the master sends in PC software on the fly without writing another C source, compiling it, loading a PIC.... etc. I don't see any value in doing that based on my setup, only possible complications.
The slave should not care what the baud rate is, because it is edge triggered and provided by the master. You will notice that SDA goes high just after the ACK pulse, which means the PIC sees the 9th pulse falling edge and releases SDA, just like it should according to the datasheet. The clock frequency is about 78KHz FYI. I think if there were some problem with syncing/clock rate, we would not get the ACK.
Previously I had problems with the second ACK because there were debugging printf commands in the interrupt, and the second byte was missed. I corrected that.
If you are asking me to make a master PIC because you see something wrong what the master is sending, then really we need to fix the master. I could start with a PIC in this case, but I do not think there is a problem with the master.
My compiler version is PCW v4.066
I know this is long and perhaps I seem stubborn... but I would very much appreciate any help regarding why i2c_isr_state always seems to return 0.
PS.
In some previous versions of my code, with 2 i2c_read() commands in the interrupt, I was able to get i2c_isr_state to increment, but this caused other problems, and it would increment beyond 0x80.
A big question for me right now is: what causes i2c_isr_state() to ever reset to zero? If it simply increments with every read, wouldn't it increment beyond 0x80 after many reads? This would be a bad thing. |
|
|
Ttelmah Guest
|
|
Posted: Thu Mar 06, 2008 4:22 am |
|
|
First, simplify a bit further. Get rid of the streams designation in the I2C (another thing to go wrong), and the printf in the ISR (not because of the potential delay in the ISR, but because having this here, implies that interrupts _will_ be disabled in the prints in the external code, which could be another problem).
Get rid of the 'slow' designator in the I2C definition. _Timing_ definitions, are down to the master. While the compiler normally is smart enough to ignore this, it is quite possible that when using streamed I2C, it is not so smart...
If you just have the interrupt routine, set 'cmd' to 0xFF, and only set it to another value in state==1, then your main can simply test for cmd having a value other than 0xFF, to say you have reached state 1.
Best Wishes |
|
|
jallum
Joined: 05 Mar 2008 Posts: 9
|
|
Posted: Thu Mar 06, 2008 11:10 am |
|
|
Thanks PCM Programmer and Ttelmah for the ideas. It appears that whether I have "slow" or "stream=I2CS" in the #use i2c for the slave, the compiler generates the same hex file (I'm viewing the diff in a text editor).
Here is the latest code:
Code: |
#include <16F877A.h>
#device adc=10
#FUSES NOWDT //No Watch Dog Timer
#FUSES HS //High speed Osc (> 4mhz)
#FUSES PUT //Power Up Timer
#FUSES PROTECT //Code protected from reads
#FUSES NODEBUG //No Debug mode for ICD
#FUSES BROWNOUT //Brownout reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD //No EE protection
#FUSES NOWRT //Program memory not write protected
#use delay(clock=20M)
#use i2c(SLAVE, sda=PIN_C4, scl=PIN_C3, address=0x60, force_hw, stream=I2CS)
//#use i2c(MASTER, sda=PIN_C1, scl=PIN_C0, stream=I2CM)
#use rs232(baud=57600, parity=N, xmit=PIN_C6, rcv=PIN_C7, bits=8, stream=COM1)
#use fast_io(D)
int rcv_buf[0x10]; //status bytes for communication with TR-10
int wrt_buf[0x10]; //status bytes for communication with TR-10
int cmd=0xFF; //i2c command read from TR-10
#int_SSP // interrupt on i2c activity
void i2c_isr(void)
{
int state, incoming;
state = i2c_isr_state();
if(state < 0x80)
{
incoming = i2c_read(I2CS);
if (state == 1)
{
cmd = incoming;
}
else if (state > 1)
{
rcv_buf[state-2]=incoming;
}
}
else
{
i2c_write(wrt_buf[state-0x80]);
}
}
void main()
{
enable_interrupts(INT_SSP);
enable_interrupts(GLOBAL);
printf("\n\rbegin");
while (TRUE)
{
if (cmd<0xFF)
printf("\n\rcmd: %x", cmd);
}
}
|
When compiled and loaded into the PIC, the printf prints the correct cmd byte. Great! I am now sure that state=1 when expected for each transmission, then i2c_isr_state() resets to 0 at some time before the next transmission of two bytes (does this happen at the stop bit, or the next start bit, or...?).
When I uncomment the second #use i2c for the master, a different hex file is created, and then the PIC no longer receives the commands. Despite the fact that I never use the master bus! I need to use 2 i2c buses for different purposes, and it would be convenient to use streams to distinguish between the two.
If using the streams creates non-working code, I have to assume it's a bug in the compiler that I will report. But I expect I can find a workaround:
Can I put a duplicate #use i2c(slave...) in the ISR?
Should I duplicate the #use i2c(master...) just above wherever I want to do a read or write from that bus?
Swap the order of the two #use i2c() statements?
Other ideas?
I'm going to play with these ideas, any other suggestions will also be appreciated. |
|
|
jallum
Joined: 05 Mar 2008 Posts: 9
|
|
Posted: Thu Mar 06, 2008 11:21 am |
|
|
First thing I tried may shed some light:
Uncommenting the #use i2c(master) and compiling creates a very different hex file from the above source.
But uncommenting the #use i2c(master) and placing it before the #use i2c(slave) generates the same hex file as when the #use i2c(master) is commented out. And this works.
It seems like this should not make a difference, especially considering I specify which stream I want to use in the i2c_read(). I would also put the I2CS stream argument in i2c_isr_state(), but it takes no arguments. If I had to guess, I would say that i2c_isr_state() was returning the state of the master i2c bus, which doesn't make any sense.
I think I'll report this and post working code when I'm finished. |
|
|
jallum
Joined: 05 Mar 2008 Posts: 9
|
|
Posted: Thu Mar 06, 2008 12:58 pm |
|
|
swapping the order of the two #use i2c statements seems to do the trick and solve all my problems.
I believe i2c_isr_state() was somehow getting the state from the master i2c which was always 0, because it was the most recently encountered #use i2c statement. By definition, the function should only care about the i2c slave HW, so I'm going to report this as a bug.
Thanks for the suggestions, hope this saves someone else the trouble when in my situation |
|
|
Ttelmah Guest
|
|
Posted: Thu Mar 06, 2008 3:41 pm |
|
|
The behaviour, is then the same as for RS232 streams. Any 'non stream' function, talks to the last defined stream. So the I2C_ISR_STATE function, will take it's value from the software I2C stream (presumably they have some attempt at emulation of the hardware for this), if this is the second stream defined.
Best Wishes |
|
|
|
|
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
|