|
|
View previous topic :: View next topic |
Author |
Message |
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Jun 14, 2009 3:24 pm |
|
|
Here is a simple i2c slave demo. The slave PIC reads the ADC value
every 500 ms and saves it in a global variable. This emulates a sensor.
The master PIC reads the ADC value from the slave. The master does
not send an internal address byte to the slave. It only sends the i2c
slave device address, and then it reads the data byte.
Also, notice that the master is not using FORCE_HW mode in the
#use i2c() statement. In some PICs, this can cause problems. It's best
to start by using a software i2c Master. When you get that working
reliably, then add the FORCE_HW to the master and try it.
I'm using the equivalent of 2.2K pullups on SDA and SCL. This was
tested on two PicDem2-Plus boards, with vs. 4.093 of the compiler.
Here is the output of the master PIC, as I slowly turn a trimpot that is
connected to the AN0 analog input pin. I turned the pot all the way to
the right, and then back to 0 again.
Quote: |
read 00
read 30
read 8C
read F3
read FF
read FF
read D8
read 88
read 15
read 00
|
i2c Master:
Code: | #include <16F877.H>
#fuses XT, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use delay(clock=4000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS)
#use i2c(Master, sda=PIN_C4, scl=PIN_C3)
#define SLAVE1_WRT_ADDR 0x12
#define SLAVE1_READ_ADDR 0x13
//====================================
void main()
{
int8 data;
while(1)
{
i2c_start();
i2c_write(SLAVE1_READ_ADDR);
data = i2c_read(0);
i2c_stop();
printf("read %X \n\r", data);
delay_ms(1000);
}
} |
i2c slave:
Code: | #include <16F877.h>
#device adc=8
#fuses XT,NOWDT,PUT,BROWNOUT,NOLVP
#use delay(clock=4000000)
#use i2c(SLAVE, SDA=PIN_C4, SCL=PIN_C3, address=0x12)
int8 adc_result;
#INT_SSP
void ssp_interrupt()
{
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(adc_result);
}
}
//======================================
void main ()
{
setup_adc_ports(AN0);
setup_adc(ADC_CLOCK_DIV_8);
set_adc_channel(0);
delay_us(20);
adc_result = read_adc();
enable_interrupts(INT_SSP);
enable_interrupts(GLOBAL);
while(1)
{
adc_result = read_adc();
delay_ms(500);
}
} |
---------
Edit #1: Fixed a typo
Edit #2: Changed the PROTECT fuse to NOPROTECT. There is no need
for code protection in a test program.
Last edited by PCM programmer on Sun Aug 16, 2009 12:20 pm; edited 2 times in total |
|
|
Olufola
Joined: 19 May 2009 Posts: 21
|
|
Posted: Mon Jun 15, 2009 12:27 pm |
|
|
I am very sorry for replying so late. Actually, I did not get any notification in my yahoo box. I will try your demo code straightaway. |
|
|
Olufola
Joined: 19 May 2009 Posts: 21
|
|
Posted: Mon Jun 15, 2009 12:49 pm |
|
|
Wow! The code worked perfectly for communication between 1 master and 1 Slave. I will soon post the results obtained for the single master and three slaves scenario. Thanks, I might not need to learn MPASM to implement my sensor network afterall. |
|
|
Olufola
Joined: 19 May 2009 Posts: 21
|
|
Posted: Mon Jun 15, 2009 3:30 pm |
|
|
I have modified the code for the case of three slaves. I made each of the slaves send a constant number so that I can easily track any errors in the transmission. From the codes below, the output I should be getting should be
Code: |
[34 179 80]
[34 179 80]
[34 179 80]
|
i.e. slave1 should be sending 34, slave2 should be sending 179 and slave3 should be sending 80 and this should continue indefinitely. What I am presently getting is
Code: |
[179 179 179]
[179 179 179]
[179 179 179]
|
and I noticed that whatever the value slave2 sends is what the master receives for the three slaves. It seems as if whatever slave2 sends overrides every other value sent on the bus and what the master gets for each of the three slaves is whatever slave2 sends. I mean that if slave2 sends 56, I get as my output
Code: |
[56 56 56]
[56 56 56]
[56 56 56]
|
and in general if slave2 sends dd, what I get as my output is
Code: |
[dd dd dd]
[dd dd dd]
[dd dd dd]
|
The codes from PCM Programmer that I modified follows:
Master code
Code: |
#include <18f4520.H>
#fuses XT, NOWDT, PROTECT, BROWNOUT, PUT, NOLVP
#use delay(clock=4000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS)
#use i2c(Master, sda=PIN_C4, scl=PIN_C3)
#define SLAVE1_WRT_ADDR 0x64
#define SLAVE1_READ_ADDR 0x65
#define SLAVE2_WRT_ADDR 0x14
#define SLAVE2_READ_ADDR 0x15
#define SLAVE3_WRT_ADDR 0x96
#define SLAVE3_READ_ADDR 0x97
//====================================
void main()
{
int8 data1,data2,data3;
while(1)
{
i2c_start();
i2c_write(SLAVE1_READ_ADDR);
data1 = i2c_read(0);
i2c_stop();
delay_ms(10);
i2c_start();
i2c_write(SLAVE2_READ_ADDR);
data2 = i2c_read(0);
i2c_stop();
delay_ms(10);
i2c_start();
i2c_write(SLAVE3_READ_ADDR);
data3 = i2c_read(0);
i2c_stop();
printf(" [ %3U %3U %3U ] \r\n", data1,data2,data3);
delay_ms(120);
}
} |
Slave1 Code
Code: |
#include <18F4520.h>
#device adc=8
#fuses XT,NOWDT,PUT,BROWNOUT,NOLVP
#use delay(clock=4000000)
#use i2c(SLAVE, SDA=PIN_C4, SCL=PIN_C3, address=0x64)
int8 adc_result;
#INT_SSP
void ssp_interrupt()
{
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(34);
}
}
//======================================
void main ()
{
setup_adc_ports(AN0);
setup_adc(ADC_CLOCK_DIV_8);
set_adc_channel(0);
delay_us(20);
adc_result = read_adc();
enable_interrupts(INT_SSP);
enable_interrupts(GLOBAL);
while(1)
{
adc_result = read_adc();
delay_ms(500);
}
}
|
Slave2 Code
Code: |
#include <18F4520.h>
#device adc=8
#fuses XT,NOWDT,PUT,BROWNOUT,NOLVP
#use delay(clock=4000000)
#use i2c(SLAVE, SDA=PIN_C4, SCL=PIN_C3, address=0x14)
int8 adc_result;
#INT_SSP
void ssp_interrupt()
{
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(179);
}
}
//======================================
void main ()
{
setup_adc_ports(AN0);
setup_adc(ADC_CLOCK_DIV_8);
set_adc_channel(0);
delay_us(20);
adc_result = read_adc();
enable_interrupts(INT_SSP);
enable_interrupts(GLOBAL);
while(1)
{
adc_result = read_adc();
delay_ms(500);
}
}
|
Slave3 Code
Code: |
#include <18F4520.h>
#device adc=8
#fuses XT,NOWDT,PUT,BROWNOUT,NOLVP
#use delay(clock=4000000)
#use i2c(SLAVE, SDA=PIN_C4, SCL=PIN_C3, address=0x96)
int8 adc_result;
#INT_SSP
void ssp_interrupt()
{
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(80);
}
}
//======================================
void main ()
{
setup_adc_ports(AN0);
setup_adc(ADC_CLOCK_DIV_8);
set_adc_channel(0);
delay_us(20);
adc_result = read_adc();
enable_interrupts(INT_SSP);
enable_interrupts(GLOBAL);
while(1)
{
adc_result = read_adc();
delay_ms(500);
}
} |
I don't know if anything is wrong with the modifications I have made. I replied earlier that the original code from PCM programmer worked perfectly for one master and One slave scenario but I cannot get it to work for the three slaves case. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Mon Jun 15, 2009 3:49 pm |
|
|
What happens if you disconnect two of the slaves, so you only have one
slave connected to the master at a time ? Can you make each of the
slaves work alone with the master ?
Also, post the size of the pullup resistors that you have on SDA and SCL.
Do you only have one set of pullup resistors ? That's what you need.
(Don't use 3 sets of pullups). |
|
|
Olufola
Joined: 19 May 2009 Posts: 21
|
|
Posted: Tue Jun 16, 2009 2:01 pm |
|
|
I am using just two pull up resistors (2.2k). When I disconnect slave1 and slave3, I still get [179 179 179], but should I disconnect just slave2, I start getting [255 255 255]. It is obvious that the master is using the data from slave2 as the data sent from each of the three slaves. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Jun 16, 2009 2:16 pm |
|
|
You are disconnecting the boards in various combinations.
What happens if you:
1. Test the master with only Slave1 connected ?
2. Test it with only Slave2 connected ?
3. Test it with only Slave3 connected ?
Post the results that you get in each test. |
|
|
Olufola
Joined: 19 May 2009 Posts: 21
|
|
Posted: Tue Jun 16, 2009 3:41 pm |
|
|
What happens if you:
1. Test the master with only Slave1 connected ?
With only slave1 connected, I got [255 255 255] continuously
2. Test it with only Slave2 connected ?
With only slave2 connected, I got [179 179 179] continuously
3. Test it with only Slave3 connected ?
With only slave3 connected, I got [255 255 255] continuously |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Jun 16, 2009 3:49 pm |
|
|
You need to get the Slave1 and Slave3 boards working.
Look for wiring problems on those boards.
Make sure that the correct version of software (with the correct Slave
address) was programmed into Slave1 and Slave3.
Make a simple LED blinking program for the Slave1 and Slave3 boards
(not an i2c slave program), so you can prove that the PIC is working
on each of those boards. |
|
|
Olufola
Joined: 19 May 2009 Posts: 21
|
|
Posted: Tue Jun 16, 2009 4:02 pm |
|
|
I will go ahead with your advice. I think those codes are okay. I will do an extensive hardware troubleshooting now that the likelyhood of software errors is now largely ruled out. I will keep you informed about any progresses as soon as they are made. Thanks a lot. I may one day take on your role on this forum (That was a joke since experience does not come by boasting) |
|
|
Olufola
Joined: 19 May 2009 Posts: 21
|
|
Posted: Sat Jun 20, 2009 1:56 am |
|
|
The impossible has become a possibility at last. I have a perfectly working sensor network implemented with I2C using the CCS C. All previous project managers could not do it. I will need to refer to PCM programmer when I am presenting the project but that is no name.
Thanks a thousand, a million ... infinitely to Mr PCM programmer but I cannot refer to you with that name when presenting the project.
The code that finally worked follows for any other person that might need it:
MASTER CODE
Code: |
#include <18f4520.h>
#fuses HS, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use delay(clock=20000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)
#use i2c(Master, sda=PIN_C4, scl=PIN_C3)
#define SLAVE1_WRT_ADDR 0x14
#define SLAVE1_READ_ADDR 0x15
#define SLAVE2_WRT_ADDR 0x28
#define SLAVE2_READ_ADDR 0x29
#define SLAVE3_WRT_ADDR 0x42
#define SLAVE3_READ_ADDR 0x43
//====================================
void main()
{
int8 data1,data2,data3;
while(1)
{
output_high(pin_d4);
delay_ms(60);
output_low(pin_d4);
i2c_start();
i2c_write(SLAVE1_READ_ADDR);
data1 = i2c_read(0);
i2c_stop();
delay_ms(10);
i2c_start();
i2c_write(SLAVE2_READ_ADDR);
data2 = i2c_read(0);
i2c_stop();
delay_ms(10);
i2c_start();
i2c_write(SLAVE3_READ_ADDR);
data3 = i2c_read(0);
i2c_stop();
printf(" [ %3U %3U %3U ] \r\n", data1,data2,data3);
output_high(pin_d6);
delay_ms(60);
output_low(pin_d6);
}
} |
SLAVE1 CODE
Code: |
#include <18F4520.h>
#device adc=8
#fuses HS,NOWDT,PUT,BROWNOUT,NOLVP
#use delay(clock=20000000)
#use i2c(SLAVE, SDA=PIN_C4, SCL=PIN_C3, address=0x14)
int8 adc_result;
#INT_SSP
void ssp_interrupt()
{
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(14);
}
}
//======================================
void main ()
{
setup_adc_ports(AN0);
setup_adc(ADC_CLOCK_DIV_8);
set_adc_channel(0);
delay_us(20);
adc_result = read_adc();
enable_interrupts(INT_SSP);
enable_interrupts(GLOBAL);
while(1)
{
adc_result = read_adc();
delay_ms(500);
}
} |
SLAVE2 CODE
Code: |
#include <18F4520.h>
#device adc=8
#fuses HS,NOWDT,PUT,BROWNOUT,NOLVP
#use delay(clock=20000000)
#use i2c(SLAVE, SDA=PIN_C4, SCL=PIN_C3, address=0x28)
int8 adc_result;
#INT_SSP
void ssp_interrupt()
{
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(28);
}
}
//======================================
void main ()
{
setup_adc_ports(AN0);
setup_adc(ADC_CLOCK_DIV_8);
set_adc_channel(0);
delay_us(20);
adc_result = read_adc();
enable_interrupts(INT_SSP);
enable_interrupts(GLOBAL);
while(1)
{
adc_result = read_adc();
delay_ms(500);
}
} |
SLAVE3 CODE
Code: |
#include <18F4520.h>
#device adc=8
#fuses HS,NOWDT,PUT,BROWNOUT,NOLVP
#use delay(clock=20000000)
#use i2c(SLAVE, SDA=PIN_C4, SCL=PIN_C3, address=0x42)
int8 adc_result;
#INT_SSP
void ssp_interrupt()
{
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(42);
}
}
//======================================
void main ()
{
setup_adc_ports(AN0);
setup_adc(ADC_CLOCK_DIV_8);
set_adc_channel(0);
delay_us(20);
adc_result = read_adc();
enable_interrupts(INT_SSP);
enable_interrupts(GLOBAL);
while(1)
{
adc_result = read_adc();
delay_ms(500);
}
} |
If you are using this code, then it will be okay if "PCM Programmer" which is no name but just an identifier be acknowledged. |
|
|
thedemri
Joined: 22 Sep 2007 Posts: 3
|
|
Posted: Sat Aug 21, 2010 2:55 pm |
|
|
Olufola hi
I know it was long time ago but do you remember what was the problem with the 3 slaves?
because the code with and without the problems are pretty similar.
Thanks Amnon
|
|
|
JH1987
Joined: 22 Jan 2011 Posts: 11
|
Small Change to Your Example |
Posted: Tue Jan 25, 2011 2:10 pm |
|
|
PCM programmer wrote: | Here is a simple i2c slave demo. The slave PIC reads the ADC value
every 500 ms and saves it in a global variable. This emulates a sensor.
The master PIC reads the ADC value from the slave. The master does
not send an internal address byte to the slave. It only sends the i2c
slave device address, and then it reads the data byte.
Also, notice that the master is not using FORCE_HW mode in the
#use i2c() statement. In some PICs, this can cause problems. It's best
to start by using a software i2c Master. When you get that working
reliably, then add the FORCE_HW to the master and try it.
I'm using the equivalent of 2.2K pullups on SDA and SCL. This was
tested on two PicDem2-Plus boards, with vs. 4.093 of the compiler.
Here is the output of the master PIC, as I slowly turn a trimpot that is
connected to the AN0 analog input pin. I turned the pot all the way to
the right, and then back to 0 again.
Quote: |
read 00
read 30
read 8C
read F3
read FF
read FF
read D8
read 88
read 15
read 00
|
i2c Master:
Code: | #include <16F877.H>
#fuses XT, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use delay(clock=4000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS)
#use i2c(Master, sda=PIN_C4, scl=PIN_C3)
#define SLAVE1_WRT_ADDR 0x12
#define SLAVE1_READ_ADDR 0x13
//====================================
void main()
{
int8 data;
while(1)
{
i2c_start();
i2c_write(SLAVE1_READ_ADDR);
data = i2c_read(0);
i2c_stop();
printf("read %X \n\r", data);
delay_ms(1000);
}
} |
i2c slave:
Code: | #include <16F877.h>
#device adc=8
#fuses XT,NOWDT,PUT,BROWNOUT,NOLVP
#use delay(clock=4000000)
#use i2c(SLAVE, SDA=PIN_C4, SCL=PIN_C3, address=0x12)
int8 adc_result;
#INT_SSP
void ssp_interrupt()
{
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(adc_result);
}
}
//======================================
void main ()
{
setup_adc_ports(AN0);
setup_adc(ADC_CLOCK_DIV_8);
set_adc_channel(0);
delay_us(20);
adc_result = read_adc();
enable_interrupts(INT_SSP);
enable_interrupts(GLOBAL);
while(1)
{
adc_result = read_adc();
delay_ms(500);
}
} |
---------
Edit #1: Fixed a typo
Edit #2: Changed the PROTECT fuse to NOPROTECT. There is no need
for code protection in a test program. |
Your example is good, as always, but its probably a good idea to do a read in the master that expects ACK, and to do a read in the slave isr for address 0x80, which will send the ACK. This seems more in line with actual I2C use, where its almost always true that the master expects to get an ACK after it sends the slave adr & Wr/Rd byte. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed Jan 26, 2011 4:07 pm |
|
|
Quote: | and to do a read in the slave isr for address 0x80 |
No address of 0x80 is sent. The 0x80 (and other values) comes
from the CCS function i2c_isr_state(). It is described in the CCS manual:
http://www.ccsinfo.com/downloads/ccs_c_manual.pdf |
|
|
JH1987
Joined: 22 Jan 2011 Posts: 11
|
Sorry for miscommunication |
Posted: Wed Jan 26, 2011 10:12 pm |
|
|
PCM programmer wrote: | Quote: | and to do a read in the slave isr for address 0x80 |
No address of 0x80 is sent. The 0x80 (and other values) comes
from the CCS function i2c_isr_state(). It is described in the CCS manual:
http://www.ccsinfo.com/downloads/ccs_c_manual.pdf |
I know. I apologize for mistyping. I meant state 0x80, which corresponds to the master sending the slave a byte with the correct slave address & the R/W bit set.
What I meant was that doing an I2C read() when the state is 0x80 would cause the slave to send back an ACK to the master. I didn't realize that the slave PIC did this anyway, regardless of the argument you give I2C read(). In other words it seemed like the ISR was called when the PIC received an 8-bit data byte but before it returned ACK. In light of that it seems totally pointless for the CCS manual to do a read in their example ISR when the state is 0x80, because the ISR would not have been called without the correct slave address. That's why I assumed you had to do the read to load the ACK in the shift register. But then the slave PIC would have to do clock stretching automatically to buy time to load the register. Oh well, at least from these posts the details of the PIC I2C are very clear now. The manual was vague and unhelpful. Thanks for the info.
Here's the CCS example as an FYI to anybody reading this
Code: |
#INT_SSP
void i2c_isr() {
state = i2c_isr_state();
if((state== 0 ) || (state== 0x80))
i@c_read();
if(state >= 0x80)
i2c_write(send_buffer[state - 0x80]);
else if(state > 0)
rcv_buffer[state - 1] = i2c_read();
}
|
|
|
|
|
|
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
|