|
|
View previous topic :: View next topic |
Author |
Message |
L.T.
Joined: 24 Jul 2020 Posts: 62
|
PIC16F18346 to PIC16F18346 I2C Communication |
Posted: Fri Jul 02, 2021 5:39 am |
|
|
Hello,
I am working on a code. I need to communicate 2 PIC16F18346 microcontrollers with I2C. I coded the C3 pin to be SCL and the C4 pin to be SDA and set up my circuit. It is connected to a 3.3V supply voltage with both SCL and SDA pull-up resistors.
I haven't written any complicated code yet. I wanted only the master to send a command, and the slave to read the command sent and flash the led connected to the A2 pin after each value it reads.
Code: |
//Master
#include <16F18346.H>
#fuses XT,NOWDT,NOPROTECT,NOLVP,PUT
/* Delay for 4 mhz crystal */
#use delay (clock=4000000)
/* Setup I2C */
#use I2C(MASTER, sda=PIN_C4, scl=PIN_C3, SLOW)
main()
{
int8 i2c_command = 66;
while (true)
{
/* Master */
i2c_start(); // Start condition
i2c_write(0xa0); // Device address
i2c_write(i2c_command); // Write Command
i2c_stop(); // Stop condition
}
}
|
Code: |
//Slave
#include <16F18346.H>
#fuses XT,NOWDT,NOPROTECT,NOLVP,PUT
/* Delay for 4 mhz crystal */
#use delay (clock=4000000)
/* Setup I2C */
#pin_select SCL1OUT = PIN_C3
#pin_select SCL1IN = PIN_C3
#pin_select SDA1OUT = PIN_C4
#pin_select SDA1IN = PIN_C4
#use i2c(SLAVE, I2C1, address=0xa0, SLOW, FORCE_HW)
#INT_SSP
void ssp_interupt ()
{
byte incoming;
incoming = i2c_read();
output_high(pin_A2);
incoming = i2c_read();
output_low(pin_A2);
}
main()
{
delay_ms(1000);
enable_interrupts(GLOBAL);
enable_interrupts(INT_SSP);
while (true)
{
}
}
|
When I wanted to examine the signals with the oscilloscope, I could not observe any signal. Even the clock signal!
I have debugged. The master code comes to the "i2c_write(0xa0)" line after the "i2c_start()" command. However, when I say skip to the next line, it goes back to the line "#use I2C(MASTER, sda=PIN_C4, scl=PIN_C3, SLOW)" and gets stuck here.
I really need your help. Thank you. |
|
|
dyeatman
Joined: 06 Sep 2003 Posts: 1934 Location: Norman, OK
|
|
Posted: Fri Jul 02, 2021 6:47 am |
|
|
I don't see PPS lines in the master code but I do in the slave.
Since they are the same chip both need PPS lines _________________ Google and Forum Search are some of your best tools!!!! |
|
|
L.T.
Joined: 24 Jul 2020 Posts: 62
|
|
Posted: Fri Jul 02, 2021 7:06 am |
|
|
dyeatman wrote: | I don't see PPS lines in the master code but I do in the slave.
Since they are the same chip both need PPS lines |
Hello,
I updated the code as below. The problem is still the same.
Code: |
#include <16F18346.H>
#fuses XT,NOWDT,NOPROTECT,NOLVP,PUT
/* Delay for 4 mhz crystal */
#use delay (clock=4000000)
/* Setup I2C */
#pin_select SCL1OUT = PIN_C3
#pin_select SCL1IN = PIN_C3
#pin_select SDA1OUT = PIN_C4
#pin_select SDA1IN = PIN_C4
#use I2C(MASTER, I2C1, SLOW)
main()
{
int8 i2c_command = 66;
while (true)
{
/* Master */
i2c_start(); // Start condition
i2c_write(0xa0); // Device address
i2c_write(i2c_command); // Write Command
i2c_stop(); // Stop condition
}
}
|
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Fri Jul 02, 2021 7:07 am |
|
|
First thing. This is a PPS chip. Currently the compiler will be setting up
software I2C for the master. Software will work for a master, but not for
a slave. Ideally use pin_select on both chips to use the hardware port.
Look at the sticky at the top of the forum about this. So use I2C1, rather
than the pin numbers after setting up the PPS.
You are doing this on the slave. Do the same on the master,
Then a comment. What resistor values are you using?. On 3.3v,
something like 3.3K should be used. A larger value than is normally
used on 5v.
Then this chip is an ANSEL supporting chip. This means it defaults to
having all pins setup as analog. Add:
set_analog_pins();
This sets all the chip pins to digital operation.
Have your chips got 4MHz crystals between pins A4, and A5, with suitable
load capacitors?. Your oscillator setup is saying you do. Unless you have
change your clock setup to use the internal oscillator.
Get rid of 'slow' in the slave. The slave device does not have a 'speed',
only the master does. Setting this can result in incorrect setups on some
chips.
Then your approach to the interrupt is wrong. The interrupt says that
_one_ byte is available to be read. Not two. You need to use I2c_isr_state
in the interrupt to detect that the first byte is the address and then the
second reception is the data. Only when this arrives, do your flash.
Currently your flash would be too fast too see. Even at 100KHz.
You need to allow a pause in the master. Currently the stuff would send
'non stop', with no time for the slave to do anything.
First start by verifying your master and slave are both running. Simple
flash an LED on each, and check it flashes at the specified speed. Only
once this is working, move on to I2C. |
|
|
L.T.
Joined: 24 Jul 2020 Posts: 62
|
|
Posted: Fri Jul 02, 2021 7:40 am |
|
|
Hi Ttelmah,
Ttelmah wrote: |
First thing. This is a PPS chip. Currently the compiler will be setting up
software I2C for the master. Software will work for a master, but not for
a slave. Ideally use pin_select on both chips to use the hardware port.
Look at the sticky at the top of the forum about this. So use I2C1, rather
than the pin numbers after setting up the PPS.
You are doing this on the slave. Do the same on the master,
|
I updated the code like you said as I shared in the post above.
Ttelmah wrote: |
Then a comment. What resistor values are you using?. On 3.3v,
something like 3.3K should be used. A larger value than is normally
used on 5v.
|
I used 1K but I change now as 3.3K instead of 1K.
Ttelmah wrote: |
Then this chip is an ANSEL supporting chip. This means it defaults to
having all pins setup as analog. Add:
set_analog_pins();
This sets all the chip pins to digital operation.
Have your chips got 4MHz crystals between pins A4, and A5, with suitable
load capacitors?. Your oscillator setup is saying you do. Unless you have
change your clock setup to use the internal oscillator.
Get rid of 'slow' in the slave. The slave device does not have a 'speed',
only the master does. Setting this can result in incorrect setups on some
chips.
|
I updated my code as you said.
Ttelmah wrote: |
Then a comment. What resistor values are you using?. On 3.3v,
something like 3.3K should be used. A larger value than is normally
used on 5v.
|
I used 1K but I change now as 3.3K instead of 1K.
Ttelmah wrote: |
Then your approach to the interrupt is wrong. The interrupt says that
_one_ byte is available to be read. Not two. You need to use I2c_isr_state
in the interrupt to detect that the first byte is the address and then the
second reception is the data. Only when this arrives, do your flash.
Currently your flash would be too fast too see. Even at 100KHz.
You need to allow a pause in the master. Currently the stuff would send
'non stop', with no time for the slave to do anything.
First start by verifying your master and slave are both running. Simple
flash an LED on each, and check it flashes at the specified speed. Only
once this is working, move on to I2C.
|
But I didn't understand what you said here. Below is the code after making the edits you mentioned. Could you please fix what you mean on the code?
Code: |
//Slave
#include <16F18346.H>
#fuses XT,NOWDT,NOPROTECT,NOLVP,PUT
/* Setup I2C */
#pin_select SCL1OUT = PIN_C3
#pin_select SCL1IN = PIN_C3
#pin_select SDA1OUT = PIN_C4
#pin_select SDA1IN = PIN_C4
#use i2c(SLAVE, I2C1, address=0xa0, FORCE_HW)
#INT_SSP
void ssp_interupt ()
{
byte incoming;
incoming = i2c_read();
output_high(pin_A2);
incoming = i2c_read();
output_low(pin_A2);
}
main()
{
set_analog_pins(PIN_C3,PIN_C4,pin_A2);
enable_interrupts(GLOBAL);
enable_interrupts(INT_SSP);
while (true)
{
}
} |
Code: |
//Master
#include <16F18346.H>
#fuses XT,NOWDT,NOPROTECT,NOLVP,PUT
/* Delay for 24 mhz internal oscillator */
#use delay(clock = 24MHz)
/* Setup I2C */
#pin_select SCL1OUT = PIN_C3
#pin_select SCL1IN = PIN_C3
#pin_select SDA1OUT = PIN_C4
#pin_select SDA1IN = PIN_C4
#use I2C(MASTER, I2C1, SLOW)
main()
{
setup_oscillator(OSC_HFINTRC_24MHZ,OSC_CLK_DIV_BY_16);
set_analog_pins(PIN_C3,PIN_C4);
int8 i2c_command = 66;
while (true)
{
/* Master */
i2c_start(); // Start condition
i2c_write(0xa0); // Device address
i2c_write(i2c_command); // Write Command
i2c_stop(); // Stop condition
}
} |
Thank you so much |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Fri Jul 02, 2021 8:17 am |
|
|
Have you checked both chips are actually running?. Flash an LED on both.
As I said, you need to check this first.
Code: |
#INT_SSP
void ssp_interupt ()
{
byte itemp;
temp=i2c_isr_state();
if (temp==0)
{
temp=i2c(read); //read the address
return; //and exit
}
if (temp==1)
{
temp = i2c_read(); //now read the command
output_toggle(pin_A2); //signal we have seen this.
}
}
|
This will toggle A2 on each command from the master. |
|
|
L.T.
Joined: 24 Jul 2020 Posts: 62
|
|
Posted: Fri Jul 02, 2021 8:28 am |
|
|
Ttelmah wrote: | Have you checked both chips are actually running?. Flash an LED on both.
As I said, you need to check this first.
Code: |
#INT_SSP
void ssp_interupt ()
{
byte itemp;
temp=i2c_isr_state();
if (temp==0)
{
temp=i2c(read); //read the address
return; //and exit
}
if (temp==1)
{
temp = i2c_read(); //now read the command
output_toggle(pin_A2); //signal we have seen this.
}
}
|
This will toggle A2 on each command from the master. |
Hey there,
I checked, they works. I mean, I can toggle leds. But I upload code like you said, it still didn't work.
Code: | #include <16F18346.H>
#fuses XT,NOWDT,NOPROTECT,NOLVP,PUT
/* Setup I2C */
#pin_select SCL1OUT = PIN_C3
#pin_select SCL1IN = PIN_C3
#pin_select SDA1OUT = PIN_C4
#pin_select SDA1IN = PIN_C4
#use i2c(SLAVE, I2C1, address=0xa0, FORCE_HW)
#INT_SSP
void ssp_interupt ()
{
byte temp;
temp=i2c_isr_state();
if (temp==0)
{
temp=i2c_read(); //read the address
return; //and exit
}
if (temp==1)
{
temp = i2c_read(); //now read the command
output_toggle(pin_A2); //signal we have seen this.
}
}
main()
{
set_analog_pins(PIN_C3,PIN_C4,pin_A2);
enable_interrupts(GLOBAL);
enable_interrupts(INT_SSP);
while (true)
{
}
} |
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Fri Jul 02, 2021 9:41 am |
|
|
You are setting the I2C pins and the output LED as analog.
The instruction needs to be used as I posted it, setting _none_ of the
pins as analog. On both chips.
Also have you added a delay in the transmit main?. Their needs to be a
minimum of 5uSec between a stop and a start at the slow I2C rate,
you cannot just loop flat out. |
|
|
L.T.
Joined: 24 Jul 2020 Posts: 62
|
|
Posted: Sun Jul 04, 2021 11:40 pm |
|
|
Hi Ttelmah,
Ttelmah wrote: | You are setting the I2C pins and the output LED as analog.
The instruction needs to be used as I posted it, setting _none_ of the
pins as analog. On both chips. |
Ttelmah wrote: | Then this chip is an ANSEL supporting chip. This means it defaults to having all pins setup as analog. Add:
set_analog_pins();
This sets all the chip pins to digital operation. |
Did you not say to me set the communication pins as analog? I think, I didnt understand what you mean. I set the pins as analog. You can see as below.
Code: | main()
{
setup_oscillator(OSC_HFINTRC_24MHZ,OSC_CLK_DIV_BY_16);
set_analog_pins(PIN_C3,PIN_C4);
int8 i2c_command = 66; |
Where should I add the delay? After the start and stop functions? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Mon Jul 05, 2021 1:00 am |
|
|
The point is your chip has an ANSEL register. This is a register controlling
whether pins are analog or digital.
Your pins need to be set to _digital_. Now the ANSEL register on wake up
defaults to having all the pins set as analog!. So you need to set the pins
on your chip to be digital. Which means setting _none of the pins_ to analog.
So
set_analog_pins(); //sets all the pins to be digital
Which is what you need (since at the moment you are not doing anything
analog).
On the delay, you need some form of pause in the main loop after
sending the stop. Now honestly, much better for this to be quite long, like
100mSec, since then when the code works, you have a chance to actually
see the result. |
|
|
L.T.
Joined: 24 Jul 2020 Posts: 62
|
|
Posted: Mon Jul 05, 2021 4:33 am |
|
|
Ttelmah wrote: | On the delay, you need some form of pause in the main loop after
sending the stop. Now honestly, much better for this to be quite long, like
100mSec, since then when the code works, you have a chance to actually
see the result. |
Thank you so much, it works!!
Now, I need to send data from slave to master. Any tips you want to give like this? I haven't tried it yet, I can try and consult you again if I fail.
Thank you! |
|
|
L.T.
Joined: 24 Jul 2020 Posts: 62
|
|
Posted: Mon Jul 05, 2021 6:16 am |
|
|
L.T. wrote: | Now, I need to send data from slave to master. Any tips you want to give like this? I haven't tried it yet, I can try and consult you again if I fail. |
Actually, I have a question. In my code, slave address is 0xa0 (1010 0000). The first 7 bits here are the slave address (1010 000), the last bit is the R/W bit (0). But wouldn't the last bit have to be 1 for the master to write? When I write 0xa1 to the first write command on the master, the data is not transmitted.Why? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Mon Jul 05, 2021 8:42 am |
|
|
For writes, the slave code gets a lot more complex.
Are you only ever going to read/write one byte, or is more going to happen?.
You need to look at the slave I2c CCS example.
The standard sequence is:
Master sends start
Master sends write address.
Master writes the register address it wants to talk to.
Master sends a restart (start without stop)
Master sends read address.
Master now reads bytes starting from the specified register
For the last read master reads with a NACK
Master sends stop
Now the slave code has:
Slave receives an address with the write bit set. Read this.
slave stores next byte as the register address to use (static value).
slave stores any subsequent bytes into this and subsequent addresses.
Slave receives an address with the read bit set. Slave must read this
without releasing clock. Then write the byte from the register address
to the I2C bus. Release the clock. Increment the register address.
For each subsequent master read, load the next byte.
The slave code shows how to do this.
A series of 'vital' things apply.
First not releasing the clock. The slave must hold the clock until the
data reply has been loaded. It must both read the arriving address and
load the reply data,
Then the master sending the inverted ACK on the last read. This resets
the slave peripheral, and if not done can result in things getting out of
sync. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Mon Jul 05, 2021 2:03 pm |
|
|
L.T. wrote: |
When I write 0xa1 to the first write command on the master, the data is not transmitted. Why? |
Do you have pull-up resistors on SDA and SCL ? You need them.
For a 5v system, you can use 4.7K pullups. If it still doesn't work,
post your test program that fails. |
|
|
L.T.
Joined: 24 Jul 2020 Posts: 62
|
|
Posted: Tue Jul 06, 2021 4:27 am |
|
|
PCM programmer wrote: | L.T. wrote: |
When I write 0xa1 to the first write command on the master, the data is not transmitted. Why? |
Do you have pull-up resistors on SDA and SCL ? You need them.
For a 5v system, you can use 4.7K pullups. If it still doesn't work,
post your test program that fails. |
Hello there,
I have a 3.3V supply voltage and the value of the pull-up resistors is 2KOhm. When I set the address of slave microcontroller as 0x12 and send 0x12(00010010) "slave address (0001001)+read command(0)" from master the communication is ok. Master is sending data, slave is reading. But if I change it to 0x13(00010011) "slave address (0001001)+write command(1)" the communication fails. However, the opposite is true. In order to send data from the master, the master must send a write(1) command. Am I wrong?
The code is:
Code: | #include <16F18346.H>
#fuses XT,NOWDT,NOPROTECT,NOLVP,PUT
#define Slave_Read 0x12
#define Slave_Write 0x13
/* Delay for 4 mhz crystal */
#use delay(clock = 24MHz)
/* Setup I2C */
#pin_select SCL1OUT = PIN_C3
#pin_select SCL1IN = PIN_C3
#pin_select SDA1OUT = PIN_C6
#pin_select SDA1IN = PIN_C6
#use I2C(MASTER, I2C1, SLOW)
main()
{
setup_oscillator(OSC_HFINTRC_24MHZ,OSC_CLK_DIV_BY_16);
set_analog_pins();
int8 i2c_command = 66;
while (true)
{
/* Master */
i2c_start(); // Start condition
i2c_write(0x13); // Device address
i2c_write(i2c_command); // Write Command
i2c_stop(); // Stop condition
delay_ms(100);
}
} |
Code: | #include <16F18346.H>
#fuses XT,NOWDT,NOPROTECT,NOLVP,PUT
/* Setup I2C */
#pin_select SCL1OUT = PIN_C3
#pin_select SCL1IN = PIN_C3
#pin_select SDA1OUT = PIN_C6
#pin_select SDA1IN = PIN_C6
#use i2c(SLAVE, I2C1, address=0x12, FORCE_HW)
#INT_SSP
void ssp_interupt ()
{
byte temp;
temp=i2c_isr_state();
if (temp==0)
{
temp=i2c_read(); //read the address
return; //and exit
}
if (temp==1)
{
temp = i2c_read(); //now read the command
output_toggle(pin_A2); //signal we have seen this.
}
}
main()
{
set_analog_pins();
enable_interrupts(GLOBAL);
enable_interrupts(INT_SSP);
while (true)
{
}
} |
|
|
|
|
|
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
|