|
|
View previous topic :: View next topic |
Author |
Message |
stefsun
Joined: 23 May 2007 Posts: 22
|
i2c master&slave problem |
Posted: Mon Apr 28, 2008 1:49 am |
|
|
master PIC4523 is unable to receive the value which slave PIC16F877 transmits via the I2C, the PC terminator can't display any letter
the code is following
master
Code: |
#include <18F4523.h> // PICF4523 header file
#device ADC=12 // 12 bits ADC
#use delay(clock=4000000) // for 4Mhz crystal
#fuses XT, NOWDT, NOPROTECT, NOLVP // for debug mode
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, Parity=N, Bits=8, ERRORS)
#use i2c(Master, sda=PIN_C4, scl=PIN_C3)
void main()
{
int8 data[8] = {0x01,0x02,0x03,0x04,0x05,0x06};
i2c_start();
i2c_write(0xA1);
data[0] = i2c_read();
data[1] = i2c_read();
data[2] = i2c_read();
data[3] = i2c_read();
data[4] = i2c_read();
data[5] = i2c_read(0);
i2c_stop();
delay_ms(100);
while(1)
{
putc(data[0]);
putc(data[1]);
putc(data[2]);
putc(data[3]);
putc(data[4]);
putc(data[5]);
delay_ms(100);
}
} |
slave
Code: |
#include <16F877.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP
#use delay(clock=4000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)
#use i2c(SLAVE, SDA=PIN_C4, SCL=PIN_C3, address=0xa0)
BYTE buffer[0x10] = {0x50,0x51,0x52,0x53,0x54,0x55} ;
#INT_SSP
void ssp_interupt ()
{
BYTE state;
state = i2c_isr_state();
if(state >= 0x80) //Master is requesting data
{
i2c_write(buffer[0]);
i2c_write(buffer[1]);
i2c_write(buffer[2]);
i2c_write(buffer[3]);
i2c_write(buffer[4]);
i2c_write(buffer[5]);
}
}
void main ()
{
enable_interrupts(GLOBAL);
enable_interrupts(INT_SSP);
while (TRUE) {}
} |
|
|
|
Matro Guest
|
|
Posted: Mon Apr 28, 2008 4:17 am |
|
|
You can try to add "FORCE_HW" to both of your #use i2c(..) instructions.
Be also aware that your slave shall absolutely be running before you start the master.
Matro. |
|
|
Ttelmah Guest
|
|
Posted: Mon Apr 28, 2008 7:08 am |
|
|
I'd also say you ought to read the address on the first I2C call, to empty the buffer, and really only send one byte at a time. So:
Code: |
#INT_SSP
void ssp_interupt ()
{
BYTE state;
static int8 ctr=0;
int8 dummy;
state = i2c_isr_state();
if(state >= 0x80) //Master is requesting data
{
i2c_write(buffer[ctr++]);
}
else {
dummy=i2c_read();
ctr=0;
}
}
|
As another 'comment', why 'HS', in the slave. This engages the high gain oscillator (designed fo higher frequency crystals), and increases the risk of oscillator problems, if used at lower frequencies, unless you have a series resistor to limit the gain of the whole circuit.
Obvious final comment - you have got thepull-up resistors on the I2C lines?.
Best Wishes |
|
|
Guest
|
|
Posted: Mon Apr 28, 2008 7:16 am |
|
|
Ttelmah wrote: | I'd also say you ought to read the address on the first I2C call, to empty the buffer, and really only send one byte at a time. So:
Code: |
#INT_SSP
void ssp_interupt ()
{
BYTE state;
static int8 ctr=0;
int8 dummy;
state = i2c_isr_state();
if(state >= 0x80) //Master is requesting data
{
i2c_write(buffer[ctr++]);
}
else {
dummy=i2c_read();
ctr=0;
}
}
|
|
Even if it is better that is not needed here. The address will give an i2c_isr_state of 0x80 (that's a read).
But once again, it can only be better. ;-)
Matro |
|
|
LEVENT
Joined: 04 May 2006 Posts: 16
|
|
Posted: Mon Apr 28, 2008 7:59 am |
|
|
Hello,
I agree with Guest..Try to send one data by every one interrupt. |
|
|
stefsun
Joined: 23 May 2007 Posts: 22
|
|
Posted: Mon Apr 28, 2008 8:24 am |
|
|
I read all of the replies,I know a lot.thank you Matro,Ttelmah and LEVENT
I understand the #INT_SSP and i2c_isr_state(),send one byte at a time.
master call i2c_read() function,then slave will call the interrupt #INT_SSP,every interrupt judge the state of i2c_isr_state(),then respond with i2c_write() or i2c_read().
'HS' I copy others program foget to modify it.thanks again.
I modify my program, then try it. |
|
|
Ttelmah Guest
|
|
Posted: Mon Apr 28, 2008 8:56 am |
|
|
Yes, I'm not used to using the internal I2C state function (tend to use my own). In which case, he should read in the '80', and output the first byte in this. So:
Code: |
#INT_SSP
void ssp_interupt ()
{
BYTE state;
static int8 ctr=0;
int8 dummy;
state = i2c_isr_state();
if(state >0x80) //Master is requesting data
{
i2c_write(buffer[ctr++]);
}
else {
if (state==0x80) {
dummy=i2c_read();
i2c_write(buffer[0]);
ctr =1;
}
else dummy=i2c_read();
}
}
|
Best Wishes |
|
|
Matro Guest
|
|
Posted: Mon Apr 28, 2008 9:04 am |
|
|
The above code is enough for the posted example :
Code: |
#INT_SSP
void ssp_interupt ()
{
BYTE state,dummy;
state = i2c_isr_state();
if(state >= 0x80) //Master is requesting data
{
i2c_write(buffer[state - 0x80]);
}
else
{
dummy=i2c_read(); //this line is to avoid locking the bus in case of a write
}
}
|
A good improvement is to add in the main() a line to clear SSPOV bit.
Another one is to use the NOCLEAR directive with the ISR and to clear interrupt flag at the beginning of the ISR.
Matro |
|
|
rnielsen
Joined: 23 Sep 2003 Posts: 852 Location: Utah
|
|
Posted: Mon Apr 28, 2008 12:25 pm |
|
|
Some people have the initial problem with timing between the master & slave units. Both of your PICs are running at the same speed. The master might end up sending commands faster than the slave can process them. Try placing delays between each of the master's commands and see if that helps any. You can experiment with the delays to get the optimum timing. An o-scope is very useful to work on the delay times.
Doing only one I2C action in the slave interrupt is the best way to do it.
Ronald |
|
|
Guest
|
|
Posted: Mon Apr 28, 2008 1:06 pm |
|
|
Matro wrote: | The above code is enough for the posted example :
Code: |
#INT_SSP
void ssp_interupt ()
{
BYTE state,dummy;
state = i2c_isr_state();
if(state >= 0x80) //Master is requesting data
{
i2c_write(buffer[state - 0x80]);
}
else
{
dummy=i2c_read(); //this line is to avoid locking the bus in case of a write
}
}
|
A good improvement is to add in the main() a line to clear SSPOV bit.
Another one is to use the NOCLEAR directive with the ISR and to clear interrupt flag at the beginning of the ISR.
Matro |
Hi Matro,
I tried your code above with a master request below
Code: |
i2c_start();
i2c_write(0xa1);
data=i2c_read(0);
i2c_stop();
lcd_putc(data);
|
After the master received the 1st data, the slave will be hang.
I found that the problem is cause by the state.
when the 1st request the state is 0x80, the slave sent buffer[0], after that the state will change to 0x81...and the slave will try to sent buffer[1]. However the slave will hang while master din request for second data.
I have tried to solve this by
Code: |
#INT_SSP
void ssp_interupt ()
{
BYTE state,dummy;
state = i2c_isr_state();
if(state == 0x80) //Master is requesting data
{
i2c_write(buffer[state - 0x80]);
}
if(state > 0x80)
{
//do nothing after sent the 1st data
}
else
{
dummy=i2c_read();
}
}
|
however, is there any other ways to solve it if the slave dunno how many data the master requested?
Thank you. |
|
|
Matro Guest
|
|
Posted: Tue Apr 29, 2008 1:31 am |
|
|
Anonymous wrote: |
Hi Matro,
I tried your code above with a master request below
Code: |
i2c_start();
i2c_write(0xa1);
data=i2c_read(0);
i2c_stop();
lcd_putc(data);
|
After the master received the 1st data, the slave will be hang.
I found that the problem is cause by the state.
when the 1st request the state is 0x80, the slave sent buffer[0], after that the state will change to 0x81...and the slave will try to sent buffer[1]. However the slave will hang while master din request for second data.
|
Normally, you shouldn't have this behavior because the read is NACKed.
What should happen is :
The slave receives the address+read and trigger the SSP interrupt with state = 0x80.
So you place the buffer[0] in the I²C buffer and release the clock (that's the purpose of i2c_write). The master send a clock and NACK the 1st byte (that's what i2c_read(0) does).
Since the data is NACKed, the slave shouldn't trigger an interrupt again and should stop the transfer.
Normally, the code I gave should work as it is, and the problem is otherwhere I guess. Could you post a complete "compilable" code that demonstrates the problem (with fuses and all preprocessor directives)?
Matro. |
|
|
stefsun
Joined: 23 May 2007 Posts: 22
|
|
Posted: Tue Apr 29, 2008 1:53 am |
|
|
thanks every one ,everything is ok |
|
|
steven Guest
|
|
Posted: Tue Apr 29, 2008 11:07 am |
|
|
Matro wrote: | Anonymous wrote: |
Hi Matro,
I tried your code above with a master request below
Code: |
i2c_start();
i2c_write(0xa1);
data=i2c_read(0);
i2c_stop();
lcd_putc(data);
|
After the master received the 1st data, the slave will be hang.
I found that the problem is cause by the state.
when the 1st request the state is 0x80, the slave sent buffer[0], after that the state will change to 0x81...and the slave will try to sent buffer[1]. However the slave will hang while master din request for second data.
|
Normally, you shouldn't have this behavior because the read is NACKed.
What should happen is :
The slave receives the address+read and trigger the SSP interrupt with state = 0x80.
So you place the buffer[0] in the I²C buffer and release the clock (that's the purpose of i2c_write). The master send a clock and NACK the 1st byte (that's what i2c_read(0) does).
Since the data is NACKed, the slave shouldn't trigger an interrupt again and should stop the transfer.
Normally, the code I gave should work as it is, and the problem is otherwhere I guess. Could you post a complete "compilable" code that demonstrates the problem (with fuses and all preprocessor directives)?
Matro. |
master.c
Code: |
#include <16F877a.h>
#use delay (clock = 20000000)
#fuses HS, NOWDT,NOPROTECT,NOLVP
#include <lcd.c>
#use i2c(Master, sda=PIN_C4, scl=PIN_C3)
void main()
{
unsigned char data;
lcd_init();
while (1)
{
i2c_start();
i2c_write(0xa0);
i2c_write(0x41);
i2c_write(0x42);
i2c_write(0x43);
i2c_start();
i2c_write(0xa1);
data=i2c_read();
lcd_putc(data);
data=i2c_read();
lcd_putc(data);
data=i2c_read(0);
i2c_stop();
lcd_putc(data);
output_high(PIN_A0);
delay_ms(500);
output_low(PIN_A0);
delay_ms(500);
}
}
|
slave.c
Code: |
#include <16F877a.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP
#use delay(clock=20000000)
#include <lcd.c>
#use i2c(slave, address=0xa0, SDA=PIN_C4, SCL=PIN_C3 )
BYTE buffer[0x10] = {0x41,0x42,0x43,0x44,0x45,0x46} ;
#INT_SSP
void ssp_interupt ()
{
BYTE state,dummy;
clear_interrupt(int_SSP);
state = i2c_isr_state();
if(state >= 0x80)
{
i2c_write(buffer[state - 0x80]);
}
else
{
dummy=i2c_read();
lcd_putc(dummy);
}
}
void main()
{
lcd_init();
enable_interrupts(INT_SSP);
enable_interrupts(global);
while (1)
{
//i2c_write(0x50);
}
}
|
thanks for helping.
steven |
|
|
stefsun
Joined: 23 May 2007 Posts: 22
|
|
Posted: Tue Apr 29, 2008 7:09 pm |
|
|
master.c
Code: |
#include <16F877a.h>
#use delay (clock = 20000000)
#fuses HS, NOWDT,NOPROTECT,NOLVP
#include <lcd.c>
#use i2c(Master, sda=PIN_C4, scl=PIN_C3)
void main()
{
unsigned char data;
lcd_init();
while (1)
{
i2c_start();
i2c_write(0xa0);
i2c_write(0x41);
i2c_write(0x42);
i2c_write(0x43);
i2c_start();
i2c_write(0xa1);
data=i2c_read();
lcd_putc(data);
data=i2c_read();
lcd_putc(data);
data=i2c_read(0);
i2c_stop();
lcd_putc(data);
output_high(PIN_A0);
delay_ms(500);
output_low(PIN_A0);
delay_ms(500);
}
}
|
thanks for helping.
steven[/quote]
i2c_start();
i2c_write(0xa0);
i2c_write(0x41);
i2c_write(0x42);
i2c_write(0x43);
these statements are not needed |
|
|
|
|
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
|