|
|
View previous topic :: View next topic |
Author |
Message |
gjs_rsdi
Joined: 06 Feb 2006 Posts: 468 Location: Bali
|
I2C Master & Slave with 2 PIC's SOLVED |
Posted: Sun Jun 23, 2019 7:26 pm |
|
|
Hi
I finally have connected two boards to make a master and slave communication with PIC16F1847
1. The master connected to PC terminal
2. Master & Slave boards connected with 6" cables for I2C
3. 2K2 resistors on SDA and SCL
4. Both boards powered from same 5VDC source
I am sending from the PC to the master:
Quote: | 55 AA CC CB or
55 AA DD DC or
55 AA EE ED |
and suppose to get the same back twice, ones report from the master and ones report from the slave I2C to master and to PC.
The communication of master with PC is OK but the data supposed to be from the slave is the default value in the main, for example:
Quote: | 55 AA CC CB
55 AA 00 FF |
My conclusion is the I2C don't work.
Code: | /////////////////////////////////////////////////////////////////
//Master I2C test program
//TM1847.c
/////////////////////////////////////////////////////////////////
#include <16F1847.h>
#FUSES HS,NOIESO,PLL,WDT,MCLR,PUT
#FUSES NOPROTECT,NOCPD,WRT,STVREN
#FUSES BROWNOUT,BORV25,NODEBUG,NOLVP
#use delay(clock=32MHz,crystal=8MHz,restart_wdt)
#use rs232(baud=9600,parity=N,xmit=PIN_B5,rcv=PIN_B2,bits=8,restart_wdt,errors)//alternative
#use i2c(Master,Slow,I2C1,restart_wdt)//I2C1 module, automatically hardware
//////////////////////////////////////////////////////////////
#define flashled PIN_A3
int flashcnt=0;
int scomtxwords=0;
int scomtxw2=0;
int scomtxwchs=0;
short rxnewmsgF=0;
int rxwords=0;
int rxdata0=0;
int rxdata1=0;
int rxdata2=0;
int rxdata3=0;
int rxwchs=0;
short I2CstartF=0;
int slave01addr=0x80;
int slave01dataout=0x00;
int slave01datain=0x00;
/////////////////////////////////////////////////////////////
#INT_TIMER1
void TIMER1_isr(void)
{
flashcnt++;
set_timer1(25536);//timer1 overflow 40ms
}
//FUNCTIONS////////////////////////////////////////////////////////
void FUNCTIONS(void)
{
//LED FLASH////////////////////////////////////////////////////////
if(flashcnt>=25)
{
flashcnt=0;
output_toggle(flashled);
}
//RX NEW MESSAGE///////////////////////////////////////////////////
if(rxnewmsgF==1)
{
scomtxw2=rxdata2;
slave01dataout=rxdata2;//updates data received to send to slave
enable_interrupts(INT_TBE);//sends back to PC the rx data received
delay_ms(2000);//delay before sending data to slave
I2CstartF=1;
}
//I2C & PC communication///////////////////////////////////////////
if(I2CstartF==1)
{
i2c_start();//Start condition
i2c_write(slave01addr);//R/W bit low for a write
i2c_write(slave01dataout);//Data to the slave
i2c_start();//Restart the bus
i2c_write((slave01addr)+1);//R/W bit high for a read
slave01datain = i2c_read();//Read the data from the slave
i2c_stop();
scomtxw2=slave01datain;//sends slave data to PC
enable_interrupts(INT_TBE);
}
}
//rs232 TX/////////////////////////////////////////////////////////
#int_TBE
void TBE_isr(void)
{
switch(scomtxwords)
{
case 0:
{
putc(85);
scomtxwchs=85;
scomtxwords++;
}
break;
case 1:
{
putc(170);
scomtxwchs=255;
scomtxwords++;
}
break;
case 2:
{
putc(scomtxw2);
scomtxwchs=scomtxwchs+scomtxw2;
scomtxwords++;
}
break;
case 3:
{
putc(scomtxwchs);
scomtxwords=0;
disable_interrupts(INT_TBE);
}
break;
}
}
//rs232 RX/////////////////////////////////////////////////////////
#INT_RDA
void RDA_isr(void)
{
switch(rxwords)
{
case 0:
{
rxdata0=getc();
if (rxdata0==85)
{
rxwchs=85;
rxwords=1;
}
else
{
rxwords=0;
}
}
break;
case 1:
{
rxdata1=getc();
if (rxdata1==170)
{
rxwchs=255;
rxwords++;
}
else
{
rxwords=0;
}
}
break;
case 2:
{
rxdata2=getc();
rxwchs=rxwchs+rxdata2;
rxwords++;
}
break;
case 3:
{
rxdata3=getc();
if (rxwchs==rxdata3)
{
rxnewmsgF=1;
}
rxwords=0;
}
break;
}
}
#ZERO_RAM
void main()
{
setup_adc_ports(NO_ANALOGS);
setup_wdt(WDT_256MS);//~256 ms reset
setup_timer_1(T1_INTERNAL|T1_DIV_BY_8);//65 ms overflow
enable_interrupts(INT_TIMER1);
enable_interrupts(INT_TBE);
enable_interrupts(INT_RDA);
enable_interrupts(GLOBAL);
while(TRUE)
{
FUNCTIONS();
restart_wdt();
}
}
|
Code: | ////////////////////////////////////////////////////////////////
//Slave I2C test program
//TS1847.c
/////////////////////////////////////////////////////////////////
#include <16F1847.h>
#FUSES HS,NOIESO,PLL,WDT,MCLR,PUT
#FUSES NOPROTECT,NOCPD,WRT,STVREN
#FUSES BROWNOUT,BORV25,NODEBUG,NOLVP
#use delay(clock=32MHz,crystal=8MHz,restart_wdt)
#use i2c(Slave,I2C1,restart_wdt)//I2C1 module, automatically hardware
//////////////////////////////////////////////////////////////
#define flashled PIN_A3
int flashcnt=0;
int slave01addr=0x80;
int slave01dataout=0xAA;
int slave01datain=0x00;
/////////////////////////////////////////////////////////////
#INT_TIMER1
void TIMER1_isr(void)
{
flashcnt++;
set_timer1(25536);//timer1 overflow 40ms
}
//////////////////////////////////////////////////////////////////
#INT_SSP
void SSP_isr(void)
{
slave01addr = i2c_isr_state();
if(slave01addr==0x80)//Master is sending data
{
slave01datain = i2c_read();
slave01dataout=slave01datain;
}
if(slave01addr==0x81)//Master is requesting data from slave
{
i2c_write(slave01dataout);
}
}
///////////////////////////////////////////////////////////////////
void FUNCTIONS(void)
{
if(flashcnt>=25)
{
flashcnt=0;
output_toggle(flashled);
}
}
///////////////////////////////////////////////////////////////////
#ZERO_RAM
void main()
{
setup_adc(NO_ANALOGS);
setup_wdt(WDT_256MS);
setup_timer_1(T1_INTERNAL|T1_DIV_BY_8);//65 ms overflow
enable_interrupts(INT_TIMER1);
enable_interrupts(INT_SSP);
enable_interrupts(GLOBAL);
while(TRUE)
{
FUNCTIONS();
restart_wdt();
}
} |
First time I am trying to make an I2C using CCS, what I am doing wrong?
(made in the far past I2C in assembler and worked)
Best wishes
Joe
Last edited by gjs_rsdi on Mon Feb 03, 2020 5:56 pm; edited 3 times in total |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Jun 23, 2019 7:54 pm |
|
|
Quote: | if(I2CstartF==1)
{
i2c_start();//Start condition
i2c_write(slave01addr);//R/W bit low for a write
i2c_write(slave01dataout);//Data to the slave
i2c_start();//Restart the bus
i2c_write((slave01addr)+1);//R/W bit high for a read
slave01datain = i2c_read();//Read the data from the slave
i2c_stop();
scomtxw2=slave01datain;//sends slave data to PC
enable_interrupts(INT_TBE);
} |
You are missing the NACK on the last i2c_read().
It should be:
Code: | slave01datain = i2c_read(0); |
|
|
|
gjs_rsdi
Joined: 06 Feb 2006 Posts: 468 Location: Bali
|
|
Posted: Sun Jun 23, 2019 11:37 pm |
|
|
Thank you PCM Programmer
I change:
Code: | slave01datain = i2c_read(0); |
Sill the same.
I checked again the boards and connections to be sure that they are OK, no problems there.
I must have some additional mistake
Best wishes
Joe |
|
|
gjs_rsdi
Joined: 06 Feb 2006 Posts: 468 Location: Bali
|
|
Posted: Sun Jun 23, 2019 11:44 pm |
|
|
Sorry, forget to mention:
CCS PCM C Compiler, Version 5.062, 31220
Best wishes
Joe |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Mon Jun 24, 2019 12:29 am |
|
|
Your slave handling is wrong.
You are misunderstanding what has to happen, and when.
State = 0x00 - this is the address byte being received for a _write_
0x01 - this is the data byte then written (state increments for each
following byte.
State = 0x80 - this is the address byte being received for a _read_.
In this state the address byte must be read, and then immediately the
reply written.
On many chips the 'standard' is to follow the address byte, with a second
'register address' byte. This is what is done in the CCS examples, so
on these the first actual data byte is state==2. However for what you
show state==1 can be used.
Now you are not handling the write state numbers. State 0x0, and 0x1
will not result in your slave doing an I2C read. Result there will be a
peripheral overflow....
Now also understand that the first byte that arrives is always the address.
Not the data.
For your system, you need:
Code: |
//////////////////////////////////////////////////////////////////
#INT_SSP
void SSP_isr(void)
{
int8 incoming;
slave01addr = i2c_isr_state();
if (slave01address==0x0)
incoming=i2c_read(); //dummy read of the device address
if (slave01address==0x1) {
//This is where the data _arrives_
slave01datain = i2c_read();
slave01dataout=slave01datain;
}
if(slave01addr==0x80)//Master is requesting data from slave
{
dummy=i2c_read(2); //here we need to read the address byte
//without releasing the clock line, ready to load the data
i2c_write(slave01dataout);
//Now load the data
//Now on some chips, the clock does not correctly release.
#BIT CKP=getenv("BIT:CKP")
CKP=TRUE; //This ensures it does release.
}
}
|
I've also added the patch for CKP. This is not needed on most chips
but it's safer to include it. |
|
|
gjs_rsdi
Joined: 06 Feb 2006 Posts: 468 Location: Bali
|
|
Posted: Mon Jun 24, 2019 3:19 am |
|
|
Thank you for the answers
I read the link PCM Programmer wrote and the post of Ttelmah.
I will learn the subject, implement accordingly and test.
Will be back with the results.
Best wishes
Joe |
|
|
gjs_rsdi
Joined: 06 Feb 2006 Posts: 468 Location: Bali
|
|
Posted: Wed Jun 26, 2019 3:39 am |
|
|
Hi
I am back after tried a few things but with no success to make a communication between I2C master and slave
Whatever I am doing I am getting in the terminal:
Quote: | 55,AA,CC,CB
55,AA,CC,CB
55,AA,00,FF |
The 55,AA,00,FF supposed to be the slave answer.
My last slave is the ex_slave.c
Code: | #INT_SSP
void SSP_isr(void)
{
unsigned int8 incoming, state;
state = i2c_isr_state();
if(state <= 0x80) //Master is sending data
{
if(state == 0x80)
incoming = i2c_read(2); //Passing 2 as parameter, causes the function to read the SSPBUF without releasing the clock
else
incoming = i2c_read();
if(state == 1) //First received byte is address
address = incoming;
else if(state >= 2 && state != 0x80) //Received byte is data
buffer[address++] = incoming;
}
if(state >= 0x80) //Master is requesting data
{
i2c_write(buffer[address++]);
}
} |
My last main:
Code: | if(I2CstartF==1)
{
//Master write to Slave////////////////////////////////////////////
i2c_start();
i2c_write(0xA0);//slave address
i2c_write(i2cWaddr);//slave location address
i2c_write(i2cWdata);//CC; DD; FF
i2c_stop();
delay_cycles(60);
//Master read from Slave///////////////////////////////////////////
i2c_start();
i2c_write(0xA0);//device address
i2c_write(i2cWaddr);//write location address
i2c_start();
i2c_write(0xA1);//write command to slave
i2cRdata=i2c_read(0);
i2c_stop();
scomtxw2=i2cRdata;
I2CstartF=0;
enable_interrupts(INT_TBE);//rpt to PC data from slave
} |
I made int i2cRdata=0xFF; in the master.
If the slave not sending data, the i2cRdata should be 0xFF, but I have 0x00 so I think the slave sends 0x00
I made the slave to send 0xAA instead of buffer[address++] but still have 0x00.
For sure I am making mistakes.
Please help
Best wishes
Joe |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Jun 27, 2019 1:36 am |
|
|
Here is a bug. You don't specify the slave address in your #use i2c()
statement:
Quote: | //Slave I2C test program
//TS1847.c
/////////////////////////////////////////////////////////////////
#include <16F1847.h>
#FUSES HS,NOIESO,PLL,WDT,MCLR,PUT
#FUSES NOPROTECT,NOCPD,WRT,STVREN
#FUSES BROWNOUT,BORV25,NODEBUG,NOLVP
#use delay(clock=32MHz,crystal=8MHz,restart_wdt)
#use i2c(Slave,I2C1,restart_wdt) // I2C1 module, automatically hardware
////////////////////////////////////////////////////////////// |
Example of how to specify the slave address:
Code: | #use i2c(Slave, I2C1, address=0x80, restart_wdt) |
|
|
|
gjs_rsdi
Joined: 06 Feb 2006 Posts: 468 Location: Bali
|
|
Posted: Mon Jul 01, 2019 7:55 pm |
|
|
Thank you PCM Programmer
I corrected the slave bug (added slave address) still not working.
I have already many versions tested without success.
I found a program I wrote in 2006 for PIC18F252 to read & write for EEPROM 24C512.
I will buy a 24C512 and will start from there, I don't remember if the program was working.
Best wishes
Joe |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Mon Jul 01, 2019 9:19 pm |
|
|
Another useful test is to run the slave at 32 MHz, and
run the master at 4 MHz. This gives the slave plenty
of time to respond. If there is a problem with the slave
responding in time, and this test makes it work, then
you know what to do. ie., insert larger delay(s) in the
master code, if both are running at 32 MHz. |
|
|
gjs_rsdi
Joined: 06 Feb 2006 Posts: 468 Location: Bali
|
|
Posted: Tue Jul 02, 2019 2:06 am |
|
|
Thank you again PCM Programmer.
I will do as you advised, just right now my brain stuck
So I will start first to talk with the 24C512.
Best wishes
Joe |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9226 Location: Greensville,Ontario
|
|
Posted: Tue Jul 02, 2019 4:59 am |
|
|
I suggest you stop using the WDT until you get your program running 100%.
All too often I see code here with WDT enabled and wonder why. Unless properly coded a WDT can easily lead you 'down the wrong path'... thinking 'this' is wrong when in reality it's 'that'.
WDT should only be installed AFTER 'main()' is truly working properly.
Jay |
|
|
gjs_rsdi
Joined: 06 Feb 2006 Posts: 468 Location: Bali
|
|
Posted: Tue Jul 02, 2019 4:22 pm |
|
|
Thanks Jay, will disable it.
Best wishes
Joe |
|
|
gjs_rsdi
Joined: 06 Feb 2006 Posts: 468 Location: Bali
|
|
Posted: Wed Jul 10, 2019 12:52 am |
|
|
Finally I have a working master with 24C512
Thanks everyone with the help
Understanding how I2C works (or any other subject) is the key.
I added back the wdt (eliminated it per Jay advice) because in some stage the PIC stuck so I wanted to learn why.
The working program:
Code: | /////////////////////////////////////////////////////////////////
//PIC16F1847 Master I2C with 24C512 eeprom test program
//TM1847.c
/////////////////////////////////////////////////////////////////
#include <16F1847.h>
#FUSES INTRC_IO,NOIESO,PLL,WDT,MCLR,PUT
#FUSES NOPROTECT,NOCPD,WRT,STVREN
#FUSES BROWNOUT,BORV25,NODEBUG,NOLVP
#use delay(clock=32MHz,INTERNAL=8MHz)
#use rs232(baud=9600,parity=N,xmit=PIN_B5,rcv=PIN_B2,bits=8,restart_wdt,errors)//V1
#use i2c(Master,Slow,I2C1)//I2C1 module, automatically hardware
#define flashled PIN_A2
short powerupF=1;
int flashcnt=0;
short I2CstartF=0;
int C512rpt=0;
int i2cStartTimer=0;
int scomtxwords=0;//V1
int scomtxw2=0;//V1
int scomtxwchs=0;//V1
#INT_TIMER1
void TIMER1_isr(void)
{
flashcnt++;
set_timer1(25536);//timer1 overflow 40ms
}
#int_TBE//V1
void TBE_isr(void)
{
switch(scomtxwords)
{
case 0:
{
putc(85);
scomtxwchs=85;
scomtxwords++;
}
break;
case 1:
{
putc(170);
scomtxwchs=255;
scomtxwords++;
}
break;
case 2:
{
putc(scomtxw2);
scomtxwchs=scomtxwchs+scomtxw2;
scomtxwords++;
}
break;
case 3:
{
putc(scomtxwchs);
scomtxwords=0;
disable_interrupts(INT_TBE);
}
break;
}
}
void COUNTERS(void)
{
if(flashcnt>=25)
{
flashcnt=0;
if(powerupF==1)
{
powerupF=0;
enable_interrupts(INT_TBE);//should send 0x00
}
output_toggle(flashled);//LED flash every sec
i2cStartTimer++;
if(i2cStartTimer==4)
{
I2CstartF=1;//start i2c every 4 sec
i2cStartTimer=0;
}
}
}
void I2C (void)
{
if(I2CstartF==1)
{
//write 0xAA to the 24C512
I2CstartF=0;
i2c_start();
i2c_write(0xA0);//24C512 address with write
i2c_write(0x00);//24C512 write address high
i2c_write(0x00);//24C512 write address low
i2c_write(0xAA);//data byte
i2c_stop();
//read back 0xAA from the 24C512
delay_ms(60); //ensure the 24C512 has had time to complete the write
i2c_start();
i2c_write(0xA0);//24C512 address with write
i2c_write(0x00);//24C512 read address high
i2c_write(0x00);//24C512 read address low
i2c_start();
i2c_write(0xA1);//24C512 address with read~
C512rpt = i2c_read(0);//24C512 rpt to master
i2c_stop();
scomtxw2 = C512rpt;
enable_interrupts(INT_TBE);//rpt to PC data from 24C512
}
}
#ZERO_RAM
void main()
{
setup_adc_ports(NO_ANALOGS);
setup_wdt(WDT_128MS);
setup_timer_1(T1_INTERNAL|T1_DIV_BY_8);//65 ms overflow
enable_interrupts(INT_TIMER1);
disable_interrupts(INT_TBE);//V1
enable_interrupts(GLOBAL);
while(TRUE)
{
COUNTERS();
I2C();
restart_wdt();
}
}
|
The program works also without sending address, then you get the data from location 0x01 that is 0xFF for empty chip
Code: | i2c_start();
i2c_write(0xA1);//24C512 address with read~
C512rpt = i2c_read(0);//24C512 rpt to master
i2c_stop(); |
Thank you again PCM Programmer, Ttelmah and Jay
Will start now with the slave PIC
Best wishes
Joe |
|
|
|
|
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
|