View previous topic :: View next topic |
Author |
Message |
gurhalil
Joined: 16 Jul 2009 Posts: 4
|
whats so wrong with my i2c communication code |
Posted: Thu Jul 23, 2009 4:18 am |
|
|
Code: | #include <16F876.h>
#device ADC=8
#fuses XT,NOWDT,NOPROTECT,NOLVP, PUT , BROWNOUT
#use delay(clock=4000000) //4 Mhz osilatör hızı
#use i2c(Master, sda=PIN_C4, scl=PIN_C3, FORCE_HW,RESTART_WDT)
#byte portb = TRUE //portb tanımlaması
#byte portc = TRUE
#byte porta = TRUE
#define SLAVE1_WRT_ADDR 0x1A
#define SLAVE1_READ_ADDR 0x1B
void main()
{
int key;
set_tris_b (0xF8);// B portunun giris cıkıs pinlerinin ayarlanması
set_tris_c(0x3D);// C portunun giris cıkıs pinlerinin ayarlanması
set_tris_a(0x17);
delay_ms(10);
WHILE (true)
{
key = 0x16
i2c_start();
i2c_write(SLAVE1_WRT_ADDR);
i2c_write(key);
i2c_stop();
delay_ms(5);
delay_ms(2);
} |
and for slave
Code: |
#include <18F452.h>
#device ADC=8
#fuses XT,NOWDT,PUT,BROWNOUT,NOLVP, NOPROTECT
#use delay(clock=4000000)
#use i2c(SLAVE, SDA=PIN_C4, SCL=PIN_C3, address=0x1A, FORCE_HW,RESTART_WDT)
#INT_SSP
void ssp_interrupt()
{
int8 state, adc_result, ad;
disable_interrupts(INT_SSP);
state = i2c_isr_state();
if(state < 0x80) // Master is sending data
{
while(!i2c_poll());
key = i2c_read();
}
if(state == 0x80) // Master is requesting data from slave
{
i2c_write(adc_result);
}
clear_interrupt(INT_SSP);
delay_ms(100);
enable_interrupts(INT_SSP);
} |
it works fine with proteus but when with real hardware it send 0x01 - 0x0A but after then it stucks |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Thu Jul 23, 2009 4:40 am |
|
|
NEVER enable the global interrupt flag inside an interrupt handler!!!
If you do this and another interrupt is waiting to be handled the hardware will immediately execute the interrupt dispatcher again. You will get nested interrupts, a feature not supported by the hardware and software.
When an interrupt becomes active the hardware will disable the global interrupt and on exiting the interrupt handler the hardware will enable the flag again. There is no need for you to do this in the interrupt handler. Disabling the interrupt (again) is only extra overhead, and enabling it will create havoc as explained.
Code: | clear_interrupt(INT_SSP); | On exiting the interrupt handler the CCS compiler adds code for clearing the interrupt. This line can be left out.
Code: | #byte portb = TRUE //portb tanımlaması
#byte portc = TRUE
#byte porta = TRUE | The #byte command expects a memory address, not TRUE or FALSE. Use of this command is only recommended if you want to directly manipulate port registers. Much easier is use the CCS commands for input/output output_low(), output_high(), etc.
Code: | set_tris_b (0xF8);// B portunun giris cıkıs pinlerinin ayarlanması
set_tris_c(0x3D);// C portunun giris cıkıs pinlerinin ayarlanması
set_tris_a(0x17); | The CCS compiler will automatically set the TRIS registers for you when you use the inbuilt I/O functions. If you want to manipulate the TRIS registers yourself you have to specify '#use fast_io'.
adc_result is not initialised, so random data may be expected.
Other errors might be present. |
|
|
gurhalil
Joined: 16 Jul 2009 Posts: 4
|
|
Posted: Thu Jul 23, 2009 5:12 am |
|
|
Quote: | NEVER enable the global interrupt flag inside an interrupt handler!!!
If you do this and another interrupt is waiting to be handled the hardware will immediately execute the interrupt dispatcher again. You will get nested interrupts, a feature not supported by the hardware and software.
When an interrupt becomes active the hardware will disable the global interrupt and on exiting the interrupt handler the hardware will enable the flag again. There is no need for you to do this in the interrupt handler. Disabling the interrupt (again) is only extra overhead, and enabling it will create havoc as explained.
|
Thanks for that I have tried without disabling the interrupt too.
The real problem is it transmits some values. The master will send the key values ranging 0x01 to 0x19, but it transmits 0x01 to 0x0A but does not transmit other values. I thinks it sticks at one of the i2c_read() functions. The master is wating to send data and freezes there. |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Thu Jul 23, 2009 5:36 am |
|
|
In the code as posted the master will only transmit 0x16 and the slave will transmit random values. This tells me the code as posted is not the same code you are testing with. I'm not going to waste time looking into this as the problem might be in some part you didn't post. |
|
|
gurhalil
Joined: 16 Jul 2009 Posts: 4
|
|
Posted: Thu Jul 23, 2009 5:48 am |
|
|
Well, the rest of the code is irrelevant. Just the idea is that master must transmit the key value that ranges between specific values. It works fine with the Proteus simulator and again works fine with values up to 0x0A. But when the key value is greater than that, the master is freezing and I think it freezes at the i2c_write() waiting for a respond. Is it because of the ack? |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Thu Jul 23, 2009 3:19 pm |
|
|
You think the rest of the code is irrelevant. I don't. Even in this short program I discovered one major error.
Another problem in your code is the delay_ms(100) inside the slave interrupt routine. _Always_ keep interrupt routines as quick as possible or you will miss incoming data.
The code working in Proteus proves little. See the above mentioned problems not detected in the simulator.
If you want my help you have to play by my rules and post a short but complete program demonstrating your problem. |
|
|
bkamen
Joined: 07 Jan 2004 Posts: 1615 Location: Central Illinois, USA
|
|
Posted: Thu Jul 23, 2009 4:05 pm |
|
|
Also,
Inside an interrupt, you don't need to disable, clear and enable the interrupt. CCS's compiler takes care of all this stuff for you.
Also, keep in mind you don't NEED to poll the I2C register. This isn't a software I2C... As I understand it, an interrupt is generated when the byte is received. You just need to go fetch it. (someone else can correct me if I'm wrong)
Only enable/disable/clear OUTSIDE your ISR routine.
Don't use delay_ms(! -- as mentioned)
-Ben
p.s. don't forget the ; after key=0x16 in your code.
p.p.s. Read the datasheet for the part carefully. This will help guide your coding. _________________ Dazed and confused? I don't think so. Just "plain lost" will do. :D |
|
|
|