CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

i2c communication: one master and several slaves
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
Mvedovetto



Joined: 17 Aug 2007
Posts: 38

View user's profile Send private message Send e-mail

i2c communication: one master and several slaves
PostPosted: Mon Jan 16, 2023 4:45 pm     Reply with quote

Hi all,
one master (16F18323) has to transfer to several (normally 3 to 7) slaves (16F18424) one byte as soon as boards are powered up (all boards have the same supply source), then slave has to to drive high or low outputs within 10ms from power on.
The master can also update the bytes to the slave after the first send.
Very simple! But the problem is that the master (or the whole system) does not always transfer bytes correctly: sometimes (not regularly) some slaves do not receive the updated byte in subsequent sends.

The pull-up resistors on SDA and SCK are 3k3 Ohm @5V. The overall length does not exceed 15 cm.

Address is 7bit length

Compiler PCM version 5.114. MPLABX 5.40. ICD4

master code

Code:

#include <16F18323.h>
#fuses NOWDT,NOPUT,BROWNOUT,NOLVP,MCLR
#use delay(internal=16000000)
#pin_select SDA1OUT=PIN_C1 //PIN 9
#pin_select SDA1IN=PIN_C1 //PIN 9
#pin_select SCL1OUT=PIN_C0 //PIN 10
#pin_select SCL1IN=PIN_C0 //PIN 10
#use i2c(master,I2C1,FORCE_HW,fast)

//.....

int8 MODULI_INIT[32];
int8 module_id; // number of slaves

//........

void main() {

  delay_ms(4); //wait slaves i2c setup complete

   while (TRUE) {

   // **** set module_id, bytes to send MODULI_INIT[]

      for (int zz=0;zz<module_id;zz++) {
            M_ADDRESS=(zz*0x02)+0x02; // slave address is 0x02, 0x04, 0x06...
            i2c_start();
            i2c_write(M_ADDRESS);
            i2c_write(MODULI_INIT[zz]);
            i2c_stop();
            delay_us(500);                        // I added this delay to try slow down and solve... obviously no success
      }
    }
}



slave code

Code:

#include <16f18424.h>
#fuses NOWDT,PUT_1MS,BROWNOUT,NOLVP,NOMCLR
#use delay(internal=16000000) // 14/1/23 prima era 12000000 4/11/2021 prima era 8000000

#pin_select SCL1=PIN_C0 //PIN 10
#pin_select SDA1=PIN_C1 //PIN 9   

#use i2c(slave, sda=PIN_C1,scl=PIN_C0, address=0xAA)

unsigned int8 dato=0;
unsigned int8 da_master=0;
unsigned int8 state;
int1 Arrivato=FALSE;        //flag arrivo dati da Master
int1 RICONOSCIUTO=FALSE;    //flag riconoscimento del modulo da parte del Master
unsigned int8 STATO=0;      //Stato del modulo


#INT_SSP
void ssp_interrupt(){
      state = i2c_isr_state();
   
     if(state == 0 ) {
        i2c_read(1);             
      }
   
     if(state == 0x80)  i2c_read(2); // The master requires the data
     
     if(state >= 0x80) {                 
        i2c_write(STATO);
     }
    else {   
        da_master = i2c_read(1);
        Arrivato=TRUE;
    }
   
}




void main(){

   dato=read_eeprom(0);                             //reads i2c Address from EEPROM
   if (dato != 0xFF) i2c_slaveaddr(dato);        //if exists, update i2c slave address
                                                                 //eeprom is set with correct address by another software /verified)
 //************

 while(TRUE){
           if (Arrivato) {
                Arrivato=FALSE;
                STATO=da_master;           // update byte
                comanda_bobine();           // use updated byte to drive outputs
                           
            }
 }
}



Do you find any trouble in code?

Thank you for any help

Marco





[/code]
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Mon Jan 16, 2023 7:31 pm     Reply with quote

Quote:

// Master code
for (int zz=0;zz<module_id;zz++) {
M_ADDRESS=(zz*0x02)+0x02; // slave address is 0x02, 0x04,

You're using reserved addresses. Don't do that. Change them.
See page 16 of the i2c Spec for the table of reserved addreses:
https://www.pololu.com/file/0J435/UM10204.pdf

Quote:

// Slave code
#use i2c(slave, sda=PIN_C1,scl=PIN_C0, address=0xAA)

Your master code says that the slaves are at 0x02,0x04,0x06,0x08 etc.
Then you set the slave address of one slave as 0xAA. What ?
You can't use 0x02, etc because these are reserved i2c addresses.

Your master code should use the same address as in each slave.
Each slave should have a different address. The master should
talk to each slave address separately.
Mvedovetto



Joined: 17 Aug 2007
Posts: 38

View user's profile Send private message Send e-mail

PostPosted: Tue Jan 17, 2023 6:53 am     Reply with quote

Thank you for your quick reply.

Sorry, I did not explain the full context and further details.

Here it is!

The first part of the software (not shown because it works perfectly) recognises how many slaves are connected on the bus. The master assigns to each slave a different address (0x02, 0x04... ) and stores the address and configuration of each slave in EEPROM, which configuration is retrieved in the MODULI_INIT array at the start of the software.

Slave stores the address, given by master, in its EEPROM.

Code:
int8 MODULI_INIT[32];


Each slave has the same i2c early address: 0xAA. The very first lines of slave software update address upon value stored in EEPROM.

Code:
if (dato != 0xFF) i2c_slaveaddr(dato);


this is the address that master will use for talk to each slave.


A delay of 4 ms in the master's software should be sufficient to ensure that i2c communication starts after the slave address has been updated.

Code:
for (int i= 0;i<module_id;i++) MODULI_INIT[i]=read_eeprom(i+1);// retrieve in MODULI_INIT from EEPROM
delay_ms(4);                            //waiting for slaves updating address completion
while (TRUE){
//**** cycle through update values to slaves
//...



Moreover, since the system is closed, a slave can never be used in different applications, so as the i2c specification states, I think those values for the address are not forbidden and should not be changed
Quote:
The assignment of addresses within a local system is up to the system architect, who must take into account the devices used on the bus and any future interaction with other conventional I2C buses. For example, a device with seven user-assignable address pins allows all 128 addresses to be assigned. If you know that the reserved address will never be used for its intended purpose, you can use a reserved address for a destination address.
Ttelmah



Joined: 11 Mar 2010
Posts: 19495

View user's profile Send private message

PostPosted: Tue Jan 17, 2023 11:13 am     Reply with quote

You are still missing the key point about using reserved addresses. You must
not use these for your devices.
Adresses 0 to 7 are reserved, and 120 to 127. Double these numbers when
dealing with a PIC (7 bit shifted left once).
Your addresses are bang in the reserved range. The PIC hardware 'knows'
about reserved addresses.
Mvedovetto



Joined: 17 Aug 2007
Posts: 38

View user's profile Send private message Send e-mail

PostPosted: Tue Jan 17, 2023 11:19 am     Reply with quote

I did not understand in this way. I'll try now and reply.
thank you!
Mvedovetto



Joined: 17 Aug 2007
Posts: 38

View user's profile Send private message Send e-mail

PostPosted: Thu Jan 19, 2023 12:52 pm     Reply with quote

I modified addresses in this way


Code:
const int8 M_ADDRESS=0X10;            //first module address (>0x07)
const int8 D_ADDRESS=0X02;                     //step address
unsigned int8 NEXTADDRESS;

//....

for (int zz=0;zz<module_id;zz++) {
            NEXTADDRESS=(zz*D_ADDRESS)+M_ADDRESS;   
            i2c_start();
            delay_us(60);
            data33=i2c_write(NEXTADDRESS);
            delay_us(60);
            data34=i2c_write(MODULI_INIT[zz]);
            delay_us(60);
            i2c_stop();
            delay_us(60);
}



But this did not solve problem.


Master deliveries inputs to active outputs of slaves via i2c. Input signals (24V) power up all electronics by DC DC converter @5V output. Frequency of input signals can reach up to 3Hz, 50% duty cycle.

Starting from frequency over 0,1Hz, driving outputs is wrong in a random way.

Rise up time of DC/DC converter is below 4ms so I set fuses that should ensure to power up meeting all requirements (DS00000607C)


Code:
#include <16F18323.h>

#fuses NOWDT
#fuses PUT //64ms after POR
#fuses BROWNOUT //BOR Reset enabled
#fuses BORV24 // reset if under 2.4V
#fuses NOLVP//
#fuses NOMCLR//MCLR not enabled: MCLR weak pull-up

#use delay(internal=16000000)
#use i2c(master,sda=PIN_C1, scl=PIN_C0,fast=50000)




with 7 slaves it could take less than 5ms: S, write (address), write (data), stop.

So what's wrong?

I appreciate your valuable time.
Mvedovetto



Joined: 17 Aug 2007
Posts: 38

View user's profile Send private message Send e-mail

PostPosted: Fri Jan 20, 2023 3:52 am     Reply with quote

Ok, I see that address does not comply to requirements.

Modified start address: now slave address starts from 0x0F.

Anyway no success.

Some ideas?
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Fri Jan 20, 2023 4:35 am     Reply with quote

A slave cannot have an odd address. It must be an even address.
Change the slave address back to 0x10.
Ttelmah



Joined: 11 Mar 2010
Posts: 19495

View user's profile Send private message

PostPosted: Fri Jan 20, 2023 6:12 am     Reply with quote

Also, there are issues with how the slave code is laid out:
Code:

#INT_SSP
void ssp_interrupt(){
      state = i2c_isr_state();
   
     if(state == 0 ) {
        i2c_read(1);    //At this point a read has been done of the address         
      }
   
     if(state == 0x80)  i2c_read(2); // The master requires the data
     
     if(state >= 0x80) {                 
        i2c_write(STATO);
     }
    else {   
        da_master = i2c_read(1);  //It'll get here if address==0
        Arrivato=TRUE;
    }   
}


In the 'state==0' condition, it'll set the Arrivato byte and read _twice_.
He has missed the 'if (state>0)', out when copying this from the manual.
temtronic



Joined: 01 Jul 2010
Posts: 9221
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Fri Jan 20, 2023 7:14 am     Reply with quote

I'm curious about how the OP actually programs the 7 slaves with different addresses. Initially all 7 are the same '0xAA' address and next to each other. What 'method' is used to decide this slave is address x, that one is x+2, etc.

My remote energy system com modules addresses were programmable,but only ONE unit at a time..address stored in RAM(security requirement). version 2.0 of the units included DIP switches for hard address 'storage'.
Mvedovetto



Joined: 17 Aug 2007
Posts: 38

View user's profile Send private message Send e-mail

PostPosted: Fri Jan 20, 2023 8:22 pm     Reply with quote

Thank you all for replies! I appreciate your time!


Quote:
A slave cannot have an odd address. It must be an even address.
Change the slave address back to 0x10.


I've got it! In fact, address is 0x1E. 7bit value (=i2c slave ADDRESS) is 0x0F. the "address" to write slave will be 0x1E and the "address" to read from slave will be 0x1F. I also used an external i2c scanner to ensure correct address.

Quote:
In the 'state==0' condition, it'll set the Arrivato byte and read _twice_.
He has missed the 'if (state>0)', out when copying this from the manual.


If I add (state>0) the first part of software does not recognize slaves. But you gave me an idea: I can provide two sets of instructions: one to to recognize slaves, and another to update output in slaves. In fact for an unexplainable reason the SSP interrupt routine does not work for both modes. In the first, the master asks the slave for the configuration and then tells it the new address; in the latter, the master tells the slave only one byte that the slave must use to update the outputs. This is why I asked the forum for help! I tried also using i2c_transfer function but no success. I'll try two sets of instructions! It sounds good also if it is not so elegant. But how to say: it doesn't matter if the cat is white or black, it's important it catches the mouse

Quote:
I'm curious about how the OP actually programs the 7 slaves with different addresses. Initially all 7 are the same '0xAA' address and next to each other. What 'method' is used to decide this slave is address x, that one is x+2, etc.


Master, once finds a slave in the bus that did not recognize yet (because it's the only one that ACKs master request), updates address (x=x+2) and teaches the slave that must update its address with x. I used an external pin to manage each updating the slaves' address.
Ttelmah



Joined: 11 Mar 2010
Posts: 19495

View user's profile Send private message

PostPosted: Fri Jan 20, 2023 10:55 pm     Reply with quote

0xE is a reserved address. 7*2=0xE, still in the reserved range.
Mvedovetto



Joined: 17 Aug 2007
Posts: 38

View user's profile Send private message Send e-mail

PostPosted: Sat Jan 21, 2023 4:55 am     Reply with quote

Ttelmah wrote:
0xE is a reserved address. 7*2=0xE, still in the reserved range.


Surely my explanation was not clear. I said that the address is 0x1E 0001 1110. The destination address is 0001 111. This value is 0x0F. Unproperly I said that this (0x0F) is address.
When I need to send a write command to the slave with address 0001 111, I will use 0001 1110 (=0x1E).
When I need to send a write command to the slave with address 0001 111, I will use 0001 1111 (=0x1F).

Target address 0001 111 is not a reserved addresses.

Thank you for reply.
Mvedovetto



Joined: 17 Aug 2007
Posts: 38

View user's profile Send private message Send e-mail

PostPosted: Mon Jan 23, 2023 2:54 am     Reply with quote

OK guys, this is the interrupt routine of slave
Quote:
#INT_SSP

void ssp_interrupt(){

state = i2c_isr_state();
if(state== 0 ) i2c_read(); //read address and ACK
if(state == 0x80)
i2c_read(2); //read address and allow stretch
if(state >= 0x80)
i2c_write(STATO); //write data requested in buffer
else if(state > 0)
{
da_master = i2c_read(); //read data from master and ACK
Arrivato=TRUE; //set the flag
}


}


This routine runs well in first part, without fail to master. Really a very lot of trials.

This is the code that master uses

Quote:


#pin_select SDA1=PIN_C1 //PIN 10
#pin_select SCL1=PIN_C0 //PIN 9

#use i2c(master, I2C1,FORCE_HW,fast=10000)

const int8 M_ADDRESS=0x1E;
const int8 D_ADDRESS=0x02;
unsigned int8 SLAVE_TO_WRITE;
......

for (int zz=0;zz<module_id;zz++) {
SLAVE_TO_WRITE=(zz*D_ADDRESS)+M_ADDRESS;

i2c_start();
//delay_us(60);
data33=i2c_write(SLAVE_TO_WRITE);
// delay_us(60);
data34=i2c_write(MODULI_INIT[zz]);
//delay_us(60);
i2c_stop();


if (data34!=0) {
output_high(LED);
delay_ms(30);
output_low(LED);
delay_ms(100);
}

}//for zz


I wonder why behaviour is the same nevertheless in ISP was performed i2c_read twice ore one. Anyway random slave (expecuially the 2nd) doesn't ack data sent (I made a led blinkingand to warn) than doesn't update its outputs. I also tried to look for first ack, but doen't seen any such event. Why this beaviour??? I tried a lot of bus speed, but in this application there is something wrong... I can't find out the reason. I'm thinking to use a earlier compiler or an earlier MPLABX... I developed similar designs and never occurs this kind of trouble...
Hope some ideas
Ttelmah



Joined: 11 Mar 2010
Posts: 19495

View user's profile Send private message

PostPosted: Mon Jan 23, 2023 3:26 am     Reply with quote

The reason it works, is that the second read, waits for the next byte.
Means the code actually sits and stays inside the interrupt, but doesn't
stop things working.

Are you sure you are not suffering from electrical interference?.
Your comments suggest you are using some quite high power stuff
being controlled by the PIC's. Classic would be that some switching event
is introducing a spike on the bus.

The way to handle a hung I2C bus, is for the master if it does not see
an ACK, to turn off the I2C peripheral, and then send a sequence of clocks
only. Then turn the peripheral back on. 8 clocks should un-hang the bus.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
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