|
|
View previous topic :: View next topic |
Author |
Message |
Maze
Joined: 12 Sep 2012 Posts: 4
|
I2C issue with PIC18F2550 |
Posted: Wed Sep 12, 2012 5:08 am |
|
|
Hi everyone.
I don't know if there is any other topic about my problem, anyway I'm going to explain.
I'm using 18F2550 trying to program a Master and a Slave that must communicate with I2C module. The function of the master is to read the adc value from slave but it gets stuck and doesn't read anything, giving as result just "128". I'm sending 49 with V, using a voltage source of 5V and pull-up resistors of 2.2k for the SDA and SCL.
I just can't find the errors so I'm going to post here the codes, praying for someone to help me
Master: Code: |
#include <prova master.h>
#device adc=10
#FUSES NOWDT
#FUSES WDT128
#FUSES CPUDIV4
#FUSES INTRC_IO
#FUSES NOBROWNOUT
#FUSES NOMCLR
#FUSES NOLVP
#FUSES NOXINST
#use delay(int=8000000)
#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8,stream=PORT1)
#use i2c(Master,Fast,sda=PIN_B0,scl=PIN_B1)
#define SLAVE1_WRT_ADDR 0x14
#define SLAVE1_READ_ADDR 0x15
int data;
void main()
{
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1); //32.7 ms overflow
setup_timer_3(T3_DISABLED | T3_DIV_BY_1);
enable_interrupts(INT_RTCC);
enable_interrupts(INT_EXT);
enable_interrupts(INT_SSP);
enable_interrupts(GLOBAL);
setup_oscillator(OSC_8MHZ|OSC_INTRC|OSC_31250|OSC_PLL_OFF);
while(TRUE)
{
i2c_start();
i2c_write(SLAVE1_READ_ADDR);
data = i2c_read(0);
i2c_stop();
delay_ms(500);
printf("%u \n\r", data);
}
} |
Slave: Code: |
#include <prova slave.h>
#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8)
#include <18F2550.h>
#device adc=10
#FUSES NOWDT //No Watch Dog Timer
#FUSES WDT128 //Watch Dog Timer uses 1:128 Postscale
#FUSES CPUDIV4 //System Clock by 4
#FUSES INTRC_IO //Internal RC Osc, no CLKOUT
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOMCLR //Master Clear pin used for I/O
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOXINST //Extended set extension and Indexed Addressing mode disabled (Legacy mode)
#use delay(int=8000000)
#use i2c(Slave,sda=PIN_B0,scl=PIN_B1,address=0x14)
int V;
float temp;
#define ris 20.48f
#int_SSP
void SSP_isr(void)
{
int8 incoming, state;
state = i2c_isr_state();
if(state <= 0x80) // Master is sending data
{
incoming = i2c_read();
}
if(state == 0x80) // Master is requesting data from slave
{
i2c_write(V);
}
}
void main()
{
setup_adc(ADC_CLOCK_INTERNAL);
setup_adc_ports(AN0);
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1); //32.7 ms overflow
setup_timer_3(T3_DISABLED | T3_DIV_BY_1);
enable_interrupts(INT_SSP);
enable_interrupts(GLOBAL);
setup_oscillator(OSC_8MHZ|OSC_INTRC|OSC_31250|OSC_PLL_OFF);
set_adc_channel(0);
while(TRUE)
{
temp=read_adc();
V=(int8)(temp/ris);
printf("%u \n\r", V);
}
} |
|
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9249 Location: Greensville,Ontario
|
|
Posted: Wed Sep 12, 2012 5:25 am |
|
|
hmm...
without trying it and quickly scanning your code..
1) you need ( must ) add 'errors' to the use rs232(...) options. This will keep the hardware UART from 'locking' up, 'stalling', 'freezing'...
2) never enable an interrupt unless you have an ISR for it! Sooner or later, it'll get triggered and the PIC will 'freeze'.
3) it's good programming to specify the I2C speed in both programs, to tell the compiler (and YOU) what speed is desired. Might not matter here but it's another 'going to get you ' item.
4) #define ris 20.48f ...hmm, don't really need the 'f' on the end, might cause a problem?
hth
jay |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19556
|
|
Posted: Wed Sep 12, 2012 8:19 am |
|
|
I'd say temtronic's #2, is what is killing the code. You never want INT_SSP on a master device (unless you are implementing a circular transmit buffer), and this interrupt will trigger after every SSP transaction, resetting, or hanging the chip....
The same applies to the other interrupts.
I disagree with him about putting a speed in the slave. This can actually result in invalid numbers being put into the registers on some chips. Only the master want/needs a speed value.
I suspect if you disable the interrupts in the master things will suddenly improve a lot (and ERRORS should _always_ be used with the hardware UART).
Best Wishes |
|
|
Maze
Joined: 12 Sep 2012 Posts: 4
|
|
Posted: Thu Sep 13, 2012 1:56 am |
|
|
Thanks for your answers
I think I found the errors, or so it seems. I added all the features you said in your posts but the things that made this work was changing the conditions in the slave with the address 0x80, instead of:
Code: | #int_SSP
void SSP_isr(void)
{
int8 incoming, state;
state = i2c_isr_state();
if(state <= 0x80) // Master is sending data
{
incoming = i2c_read();
}
if(state == 0x80) // Master is requesting data from slave
{
i2c_write(V);
}
} |
I used:
Code: | #int_SSP
void SSP_isr(void)
{
int8 incoming, state;
state = i2c_isr_state();
if(state < 0x80) // Master is sending data
{
incoming = i2c_read();
}
if(state == 0x80) // Master is requesting data from slave
{
i2c_write(V);
}
}
|
without the = in the first "if" it works how it should, even if the "built in function" of CCS said that conditions |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19556
|
|
Posted: Thu Sep 13, 2012 3:07 am |
|
|
On a lot of chips, the I2C fails, _unless_ you perform the read as well as the write on state 80. Some others allow this to be skipped. Yours is the first one I've heard of, that is requiring it to be skipped!....
Your code is 'unusual', in going straight to the read routine. In general the standard I2C transaction is:
Start
send device write address
send slave register number
Then either:
send bytes to write to registers
Or:
send restart
send device read address
read bytes from slave
Then
stop
So there is normally at least one write before the read, to send the register number required. Suggests that failing to do this, may be changing the behaviour of the slave!.
Best Wishes |
|
|
Maze
Joined: 12 Sep 2012 Posts: 4
|
|
Posted: Thu Sep 13, 2012 4:22 am |
|
|
There's another problem, I have modified the code adding timer0 because I had to put in output a waveform and when the master decides the waveform to send in output after some seconds the I2C stop working (even the SDA and SCL pins stay always low). Here is the code:
master:
Code: | #include <i2c_master.h>
#include <18F2550.h>
#device adc=10
#FUSES NOWDT //Watch Dog Timer
#FUSES WDT64 //Watch Dog Timer uses 1:64 Postscale
#FUSES CPUDIV4 //System Clock by 4
#FUSES INTRC_IO //Internal RC Osc, no CLKOUT
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOMCLR //Master Clear pin used for I/O
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOXINST //Extended set extension and Indexed Addressing mode disabled (Legacy mode)
#use delay(int=8000000)
#use i2c(Master,Fast,sda=PIN_B0,scl=PIN_B1)
#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8,stream=PORT1,errors)
#define SLAVE1_WRT_ADDR 0x14
#define SLAVE1_READ_ADDR 0x15
int i, y, var, data, a, b, c;
#int_RTCC
void RTCC_isr(void)
{
output_toggle(PIN_B7);
set_timer0(45609);
}
void main()
{
setup_adc_ports(AN0);
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1); //32.7 ms overflow
setup_timer_3(T3_DISABLED | T3_DIV_BY_1);
enable_interrupts(INT_RTCC);
enable_interrupts(GLOBAL);
setup_oscillator(OSC_8MHZ|OSC_INTRC|OSC_31250|OSC_PLL_OFF);
set_timer0(45536);
delay_ms(10);
while(TRUE)
{
i2c_start();
i2c_write(SLAVE1_WRT_ADDR);
i2c_write(1);
i2c_start();
i2c_write(SLAVE1_READ_ADDR);
data = i2c_read(0);
i2c_stop(); //prova:
printf("%u \n\r", data);
i2c_start();
i2c_write(SLAVE1_WRT_ADDR);
i2c_write(2);
i2c_write(2);
i2c_stop();
delay_ms(100);
i2c_start();
i2c_write(SLAVE1_WRT_ADDR);
i2c_write(2);
i2c_write(3);
i2c_stop();
delay_ms(100);
i2c_start();
i2c_write(SLAVE1_WRT_ADDR);
i2c_write(2);
i2c_write(1);
i2c_stop();
delay_ms(100);
}
} |
Slave:
Code: | #include <i2c.h>
#define ris 204
#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8,stream=PORT1,errors)
#include <18F2550.h>
#device adc=10
#FUSES NOWDT //No Watch Dog Timer
#FUSES WDT128 //Watch Dog Timer uses 1:128 Postscale
#FUSES CPUDIV4 //System Clock by 4
#FUSES INTRC_IO //Internal RC Osc, no CLKOUT
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOMCLR //Master Clear pin used for I/O
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOXINST //Extended set extension and Indexed Addressing mode disabled (Legacy mode)
#use delay(int=8000000)
#use i2c(SLAVE, SDA=PIN_B0, SCL=PIN_B1, address=0x14, FORCE_HW)
int16 x, y, x1=64420, y1=48198, x2=62661, y2=51596, x3=59494, y3=58054; //+62
int number, V, function, ok=0;
float temp;
#int_RTCC
void RTCC_isr(void)
{
if(ok==0){
output_low(PIN_A1);
set_timer0(x);
ok=1;
}
else if(ok==1){
output_high(PIN_A1);
set_timer0(y);
ok=2;
}
else{
output_low(PIN_A1);
set_timer0(x);
ok=0;
}
}
#int_EXT2
void EXT2_isr(void)
{
disable_interrupts(INT_RTCC);
if(function==2){
if(number==2){
x=x2;
y=y2;
}
else if(number==3){
x=x3;
y=y3;
}
else{
x=x1;
y=y1;
}
}
output_low(PIN_A1);
set_timer0(x);
ok=1;
enable_interrupts(INT_RTCC);
}
#int_SSP
void SSP_isr(void)
{
BYTE state, incoming;
state=i2c_isr_state();
if(state < 0x80) //Master is sending data
{
incoming = i2c_read();
if(state == 1) //First received byte is address
function = incoming;
if(state == 2) //Second received byte is data
number = incoming;
}
if(state >= 0x80) //Master is requesting data
{
if(function==1)
i2c_write(V); //V=tensione batteria
}
}
void main()
{
setup_adc(ADC_CLOCK_INTERNAL);
setup_adc_ports(AN0);
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1); //32.7 ms overflow
setup_timer_3(T3_DISABLED | T3_DIV_BY_1);
disable_interrupts(INT_RTCC);
enable_interrupts(INT_EXT2);
enable_interrupts(INT_SSP);
enable_interrupts(GLOBAL);
setup_oscillator(OSC_8MHZ|OSC_INTRC|OSC_31250|OSC_PLL_OFF);
x=x1;
y=y1;
set_adc_channel(0);
output_low(PIN_A1);
set_timer0(x-62);
while(TRUE)
{
temp=read_adc();
V=(int8)(temp/ris);
delay_ms(100);
printf("%u \n\r", V);
}
} |
If I comment the code inside INT_RTCC the i2c keep working. Is it possible that the interrupt of the timer force the i2c to stop? |
|
|
jeremiah
Joined: 20 Jul 2010 Posts: 1358
|
|
Posted: Thu Sep 13, 2012 6:52 am |
|
|
2 comments:
1. You are not handling i2c isr state 0x80 fully, which you should be. Read the manual description for i2c_isr_state() for requirements.
2. You are not always writing when state is >= 0x80, which you should be. Even if you write another value instead of the one for the if, you need to write something.
The hardware expects certain things to happen. Not doing them can cause it to lock up. |
|
|
Maze
Joined: 12 Sep 2012 Posts: 4
|
|
Posted: Thu Sep 13, 2012 8:36 am |
|
|
I modified the code with this things but keep not working
Code: | #int_SSP
void SSP_isr(void)
{
BYTE state, incoming;
state=i2c_isr_state();
if(state <= 0x80) //Master is sending data
{
incoming = i2c_read();
if(state == 1) //First received byte is address
function = incoming;
if(state == 2) //Second received byte is data
number = incoming;
}
if(state >= 0x80) //Master is requesting data
{
if(function==1)
i2c_write(V); //V=tensione batteria
else i2c_write(1);
}
} |
|
|
|
|
|
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
|