|
|
View previous topic :: View next topic |
Author |
Message |
Mvedovetto
Joined: 17 Aug 2007 Posts: 38
|
i2c communication: one master and several slaves |
Posted: Mon Jan 16, 2023 4:45 pm |
|
|
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
|
|
Posted: Mon Jan 16, 2023 7:31 pm |
|
|
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
|
|
Posted: Tue Jan 17, 2023 6:53 am |
|
|
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: 19539
|
|
Posted: Tue Jan 17, 2023 11:13 am |
|
|
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
|
|
Posted: Tue Jan 17, 2023 11:19 am |
|
|
I did not understand in this way. I'll try now and reply.
thank you! |
|
|
Mvedovetto
Joined: 17 Aug 2007 Posts: 38
|
|
Posted: Thu Jan 19, 2023 12:52 pm |
|
|
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
|
|
Posted: Fri Jan 20, 2023 3:52 am |
|
|
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
|
|
Posted: Fri Jan 20, 2023 4:35 am |
|
|
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: 19539
|
|
Posted: Fri Jan 20, 2023 6:12 am |
|
|
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: 9243 Location: Greensville,Ontario
|
|
Posted: Fri Jan 20, 2023 7:14 am |
|
|
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
|
|
Posted: Fri Jan 20, 2023 8:22 pm |
|
|
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: 19539
|
|
Posted: Fri Jan 20, 2023 10:55 pm |
|
|
0xE is a reserved address. 7*2=0xE, still in the reserved range. |
|
|
Mvedovetto
Joined: 17 Aug 2007 Posts: 38
|
|
Posted: Sat Jan 21, 2023 4:55 am |
|
|
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
|
|
Posted: Mon Jan 23, 2023 2:54 am |
|
|
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: 19539
|
|
Posted: Mon Jan 23, 2023 3:26 am |
|
|
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. |
|
|
|
|
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
|