|
|
View previous topic :: View next topic |
Author |
Message |
ebenbekker
Joined: 02 Sep 2009 Posts: 4
|
Bi-directional Communication problem with i2c |
Posted: Thu Sep 17, 2009 6:56 pm |
|
|
In my project i am trying to establish I2C communication between a master 16F877A and 2 slave 16F877A's. My testing code is to communicate between the master and slave A0. The Master send a value to the slave to save in the buffer array at address location. Then the master requests that same information.
My master code based on some examples i got on this site :
Code: | #include<16F877A.h>
//#include<def_16f886.h>
#fuses HS, noprotect, NOWDT
#use delay(clock = 4000000)
#use i2c(master, sda = pin_c4, scl = pin_c3)
#use rs232(baud=9600,xmit=PIN_C6,rcv=PIN_C7)
void main(void){
// osccon = 0x41;
byte result2=0x00;
while(1){
//HERE I WANT TO SEND DATA TO SLAVE CONSISTING OF AN SLAVE_ADDRESS,
//INTERNAL ADDRESS IN BUFFER AND THE DATA THAT NEED TO BE WRITTEN THERE
i2c_start(); //begin transmission
i2c_write(0xA0); //Slave address A0
i2c_write(0x01); //Internal Adress 0x01
i2c_write(0x41); //send actual data
i2c_stop(); //terminate communication
//HERE I WANT TO READ THE DATA THAT I JUST SAVED ON THE CLIENT
output_high(PIN_D7); //Visual confirmation that the sending part is starting
delay_ms(500);
i2c_start (); //begin communication
i2c_write (0xA0); //send slave address
i2c_write (0x01); //request slave to fetch buffer array element 0x01
i2c_stop(); //stop write cycle to shift to read cycle
i2c_start (); //send repeated start command to begin read cycle
i2c_write (0xA1); //add 1 to the address to send a write bit
delay_ms(100);
result2 = i2c_read(); //read analogue information from the slave
i2c_stop (); //terminate communication
output_low(PIN_D7); //Visual confirmation that send part is done
delay_ms(150);
putc(result2); //Print result on the hyperterminal
result2=0x00;
delay_ms(1000);
}
} |
And the Slave code:
Code: | #include<16F877A.h>
#fuses HS, noprotect, NOWDT
#use delay(clock = 4000000)
#use i2c(slave, sda = pin_c4, scl = pin_c3, address = 0xa0)
int8 address = 0;
int8 buffer[5] = {0x41, 0x42, 0x43, 0x44, 0x45};
int8 incoming = 0;
int8 first_byte = 0;
int8 second_byte = 0;
int8 state = 0;
#int_ssp
void ssp_interupt ()
{
state = i2c_isr_state();
if(state < 0x80) //Master is sending data
{
incoming = i2c_read();
if(state==1)
{
address = incoming;
}
if(state==2)
{
buffer[address]=incoming;
}
}
if(state == 0x80) //Master is requesting data
{
i2c_write(buffer[address]);
}
delay_ms(100);
output_toggle(PIN_B1);
}
void main(void){
enable_interrupts(int_ssp);
enable_interrupts(global);
while(1){}
} |
The problem is that i keep getting 0xFF values. No matter what i do the communication does not work. Please give me some pointers as to what may be wrong. I know that the hardware is allright and has be checked by peers. I thank you in advance.
Eben
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Sep 17, 2009 8:17 pm |
|
|
Quote: | i2c_start (); //send repeated start command to begin read cycle
i2c_write (0xA1); //add 1 to the address to send a write bit
delay_ms(100);
result2 = i2c_read(); //read analogue information from the slave
i2c_stop (); |
I didn't look closely at your code, but I did see one thing. The last
i2c_read() operation must do a NACK. If you only do one read, then
it's the "last" one. This is in the i2c specification. You can tell it do a
NACK by using a 0 parameter, as shown below:
Code: | result2 = i2c_read(0); |
This is in the manual. |
|
|
ebenbekker
Joined: 02 Sep 2009 Posts: 4
|
|
Posted: Fri Sep 18, 2009 5:33 am |
|
|
I tried that ty but it did not resolve the latchup problem or the invalid received info. Thank you for your help! Much appreciated.
Eben |
|
|
Wayne_
Joined: 10 Oct 2007 Posts: 681
|
|
Posted: Fri Sep 18, 2009 8:12 am |
|
|
Have you given each slave a different address ? |
|
|
Ttelmah Guest
|
|
Posted: Fri Sep 18, 2009 8:20 am |
|
|
Obvious first comment. Get rid of the delay in the ISR.
Your current handler, will still be in the ISR, from receiving the slave address, when the index address byte is sent.....
Second comment. Get rid of the I2C_stop, in front of the restart. A 'restart', involves sending a new 'start', without sending a 'stop'. On the PIC, it shouldn't matter (it should still work OK with a 'stop' present), but on many I2C peripherals, if you once send the 'stop', the internal state machine resets....
Best Wishes |
|
|
ebenbekker
Joined: 02 Sep 2009 Posts: 4
|
|
Posted: Sat Sep 19, 2009 9:06 am |
|
|
I have added the NACK on the master's side, and not on the slave's side (see code below). The delays on the slave's side was not a problem, if (and this is a big if) there was sufficient delay on the master's side.
I also noticed that my programmer didn't always do what it should, meaning that at times I assumed correct code as incorrect due to a faulty programmer (this is very discouraging).
Thank you for the tips.
For those wonder what correct working code looks like for my implementation of a slave buffer reading looks like, I have attached the code (enjoy). The program merely writes 'A' to 'P' to the slave's buffer, and then reads the data from the buffer. On the CommPort is sends out "Ready123" followed by "ABCDEFGHIJKLMNOP" (if the buffer could be written and read properly).
MASTER'S CODE
Code: |
#include <16F877A.h>
#device adc=8
#FUSES NOWDT //No Watch Dog Timer
#FUSES XT //Crystal osc <= 4mhz
#FUSES PUT //Power Up Timer
#FUSES NOPROTECT //Code not protected from reading
#FUSES NODEBUG //No Debug mode for ICD
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD //No EE protection
#FUSES WRT_50% //Lower half of Program Memory is Write Protected
#use delay (clock = 4M)
#use rs232 (baud = 9600, parity = N, xmit = PIN_C6, rcv = PIN_C7, bits = 8)
#use i2c (master, fast, sda = PIN_C4, scl = PIN_C3)
byte serial_buffer_size = 30;
byte serial_buffer [30];
byte serial_input_index = 0x00;
byte serial_output_index = 0x00;
byte serial_trasmission_ended = true;
#int_rda
void rda_isr (void) {
serial_buffer [serial_input_index] = getc ();
serial_input_index = serial_input_index + 1;
}
byte serial_data_available () {
byte temp_input_index;
byte temp_output_index;
temp_input_index = serial_input_index;
temp_output_index = serial_output_index;
if (temp_input_index < temp_output_index) {
return serial_buffer_size - temp_output_index + temp_input_index;
}
else {
return temp_input_index - temp_output_index;
}
}
byte serial_read () {
byte output;
output = serial_buffer [serial_output_index];
serial_output_index = (serial_output_index + 1) % serial_buffer_size;
return output;
}
void serial_write (byte data) {
serial_trasmission_ended = false;
putc (data);
}
byte slave_read (byte slave_address, byte memory_address) {
byte output;
i2c_start ();
i2c_write (slave_address);
i2c_write (memory_address);
// i2c_stop (); // Be warned that this sends a I2C ISR reset condition.
i2c_start ();
i2c_write (slave_address + 1);
output = i2c_read (0); // Take note to add the NACK (No acknowledgement)
i2c_stop ();
return output;
}
void slave_write (byte slaveAddress, byte memoryAddress, byte value) {
i2c_start ();
i2c_write (slaveAddress);
i2c_write (memoryAddress);
i2c_write (value);
i2c_stop ();
}
void main()
{
byte buffer [0x10];
byte n = 0x00;
setup_adc_ports(NO_ANALOGS);
setup_adc(ADC_OFF);
setup_psp(PSP_DISABLED);
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
setup_timer_1(T1_DISABLED);
setup_timer_2(T2_DISABLED,0,1);
setup_comparator(NC_NC_NC_NC);
setup_vref(FALSE);
enable_interrupts(int_rda);
enable_interrupts(global);
delay_ms(1000);
printf ("ready123");
for (n = 0; n < 16; n++) {
slave_write (0xa0, n, 'A' + n);
buffer [n] = slave_read (0xa0, n);
}
for (n = 0; n < 16; n++) {
putc (buffer [n]);
}
delay_ms (1000);
while (true) {
;
}
}
|
SLAVE'S CODE
Code: |
#include <16F877A.h>
#device adc=8
#FUSES NOWDT //No Watch Dog Timer
#FUSES XT //Crystal osc <= 4mhz
#FUSES PUT //Power Up Timer
#FUSES NOPROTECT //Code not protected from reading
#FUSES NODEBUG //No Debug mode for ICD
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD //No EE protection
#FUSES WRT_50% //Lower half of Program Memory is Write Protected
#use delay(clock=4M)
#use i2c(slave, fast, sda=PIN_C4, scl=PIN_C3, address=0xA0)
#define buffer_size 0x40
byte buffer [buffer_size];
byte address = 0x00;
#int_SSP
void i2c_isr(void)
{
byte state;
state = i2c_isr_state();
// Send Buffer Data
if(state >= 0x80) {
i2c_write (buffer [address]);
}
// Reset/Data Received
else { // (state >= 0x00)
// Receive Address
if (state == 0x01) {
address = i2c_read ();
}
// Receive Buffer Data
else if (state == 0x02) {
buffer [address] = i2c_read ();
}
else {
i2c_read ();
}
}
}
void main()
{
setup_adc_ports(NO_ANALOGS);
setup_adc(ADC_OFF);
setup_psp(PSP_DISABLED);
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
setup_timer_1(T1_DISABLED);
setup_timer_2(T2_DISABLED,0,1);
setup_comparator(NC_NC_NC_NC);
setup_vref(false);
enable_interrupts (int_ssp);
enable_interrupts (global);
while (true) {
;
}
}
|
|
|
|
hmnrobots Guest
|
I2C communication |
Posted: Fri Oct 02, 2009 7:38 am |
|
|
All right with when a hardware I2C is available, but as I need to communicate with a non equipped device (12F629) I won't have the I2C interrupt and I can't see how to do it; any help ??? |
|
|
Ttelmah Guest
|
|
Posted: Fri Oct 02, 2009 8:40 am |
|
|
Seriously, you won't.
Wiithout the hardware, a _master_, is easy, but a slave, is really hard. I won't say it "can't" be done, but it requires a huge amount, of really carefully done code, and is approaching impossibility...
It is a case where if you have several weeks of time, and are intending to produce the device in many thousands off, so the saving from not using a chip with slave hardware, is possibly worthwhile, but otherwise it is going to be cheaper, and easier, to use a PIC with the required hardware.
Look at possibly the 12F1822.
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
|