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 support@ccsinfo.com

I2C Master & Slave with 2 PIC's SOLVED
Goto page Previous  1, 2, 3, 4, 5, 6  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
gjs_rsdi



Joined: 06 Feb 2006
Posts: 468
Location: Bali

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

PostPosted: Wed Jul 17, 2019 10:57 pm     Reply with quote

Me again, with questions

I find a working code of PCM Programmer when a master reads data from 3 slaves.
I made some small change to 1 master and 1 slave and it works.
Master code:
Code:
      i2c_start();
      i2c_write(0xC1);//slave address with R/W set
      data = i2c_read(0);//reads data from slave with NAK
      i2c_stop();

Slave code:
Code:
#INT_SSP
void ssp_interrupt ()
{
   int8 incoming, state;
   state = i2c_isr_state();
   if(state < 0x80)     // Master is sending data
     {
         incoming = i2c_read(); 
     }
   if(state >= 0x80)   // Master is requesting data from slave
     {
      data=data+1; //to see if the slave works
         i2c_write(data);
     }    
}

The program works Smile I tested with master Slow and Fast.
One thing that I don't understand:
I am expecting to get on the PC terminal the data, each 4 seconds:
0x01, 0x02, 0x03.....
But I am getting:
0x01, 0x03, 0x05.....
If I am out-commenting the line:
Code:
//data=data+1; //to see if the slave works

I am getting 0x00, 0x00, 0x00...
In the PCM Programmer slave the return is constant for each slave.
I think that the program go twice to the place of the increment during the I2C communication and I don't understand why Sad

CCS PCM C Compiler, Version 5.062
Best wishes
Joe
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Wed Jul 17, 2019 11:37 pm     Reply with quote

Two separate issues:

First, the code is not right.

On I2C states, the following has to happen:

State
0 to 0x80 must read
0x80 to 0xFF must write

Now the _key_ is that on state 0x80, the code must _read then write_.

Your code is not doing the read on state 0x80.
This is why you have the missed number.

Then the code as written, sends back 'data', which has to be changed.
Removing the data=data+1, this is not being incremented, so the
return stays as 0....
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Thu Jul 18, 2019 3:23 am     Reply with quote

A demo set of code:
Code:

//mainmasterseq.c - master I2C sequential read/writes only
//**********************************************************
//
#include <18F4520.h>
#device ADC=10

#FUSES NOWDT                    //No Watch Dog Timer

#use delay(crystal=20000000)
//Very basic chip setup. Change to suit your PIC
//Simple code to demonstrate a sequential read of 16bytes from a I2C slave
//Without supporting register addressing, and a similar write to the slave
//use with mainslaveseq.c for the slave device.
#USE RS232(UART1, BAUD=9600, ERRORS, STREAM=SERIAL)
//Basic serial port setup to display the results. Using UART1

#USE I2C(MASTER, BAUD=400000, I2C1, STREAM=I2CPORT)
//SETUP for an I2C master on the hardware I2C at 400KHz

#define SLAVE_ADDR 0x40  //Select some suitable address. Must not be <16, even,
//and must match slave
#define I2C_WRT 1 //flag for write

void main()
{
   byte data[16]; //array to hold read data from slave
   byte test[16] = {'1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
   //Test transmission data to send to slave.
   unsigned int8 ctr;
   while(TRUE)
   {
      delay_ms(1000); //Slow the loop
      i2c_start(I2CPORT); //open the connection
      i2c_write(I2CPORT, SLAVE_ADDR); //select the slave device to read
      for (ctr=0;ctr<16;ctr++)
      {
         //Now read 16bytes from the slave. Must NACK the last read
         if (ctr==15)
            data[ctr]=i2c_read(I2CPORT,0); //This generates NACK
         else
            data[ctr]=i2c_read(I2CPORT);           
      }
      i2c_stop(I2CPORT); //and close the connection
     
      //Display the values
      for (ctr=0;ctr<16;ctr++)
      {
         fprintf(SERIAL,"%02x ", data[ctr]);
      }
      fprintf(SERIAL,"\n\r"); //LF/CR to show end of data     
     
      //Now write new values to the slave
      i2c_start(I2CPORT); //open the connection
      i2c_write(I2CPORT, SLAVE_ADDR | I2C_WRT); //select the slave device to write
      for (ctr=0;ctr<16;ctr++)
      {
         //Now send 16bytes to the slave.
         i2c_write(I2CPORT, test[ctr]);           
      }
      i2c_stop(I2CPORT); //and close the connection     
   }
}

//Now the slave mainslaveseq.c
//***********************************************************
//
#include <18F4520.h>
#device ADC=10

#FUSES NOWDT                    //No Watch Dog Timer

#use delay(crystal=20000000)
//Very basic chip setup. Change to suit your PIC
//Simple code to demonstrate an I2C slave supporting sequential read/write
//up to 16 bytes
//use with mainmasterseq.c for the master device.

#define SLAVE_ADDR 0x40  //Select some suitable address. Must not be <16, even,
//and must match master
#USE I2C(SLAVE, I2C1, ADDRESS=SLAVE_ADDR, STREAM=I2CPORT)
//SETUP for an I2C SLAVE. This must always be on the hardware.

byte data[16] = {0xF0,0xF1,0xF2,0xF3,0xF4, 0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC, 0xFD,0xFE,0xFF};
//array to hold data from master
//Initialise with values so these can be read before the master writes, and be seen
//to change after the write.


#bit CKP=getenv("BIT:CKP")

#INT_SSP
void i2c_int(void)
{
   //The core I2C slave code.
   unsigned int8 state;
   byte value;
   state=i2c_isr_state(I2CPORT);
   //Now states < 0x80, are read states. The master is sending us data
   //However state 0x0, is where the master sends up the address itself
   //so we need to use state-1 as the array index to write data
   //State 0x80 is where the master sends the address for the write. On this
   //we must read, and then write the byte for the reply.
   if (state<=0x80)
   {
      if (state==0x80)
      {
         //here we need to read, and not release the clock
         value=i2c_read(I2CPORT,2);
      }
      else
      {
         value=i2c_read(I2CPORT); //normal read releasing the clock
         if (state>0)
            data[(state-1) & 0xF] = value; //limit to just 16 addresses allowed
      }
   }
   if (state>=0x80)
   {
      //We have a read request from the master so we need to write
      i2c_write(I2CPORT, data[state & 0xF]); //again limit to 16 addresses
      //Now the slave _should_ release CKP here but some chips have an issue
      //where this does not happen. The following code is a 'bodge' to ensure
      //it does release.
      CKP=TRUE; //Add this for chips that don't release the clock....     
   }
}

void main()
{
   enable_interrupts(INT_SSP);
   enable_interrupts(GLOBAL);
   //All slave communication is being handled by the interrupt.
   
   while(TRUE)
   {
      delay_cycles(1); //simply sit here doing nothing....   
   }
}


As written, on the first read it should get F0 F1 F2 ....FE
Then the master writes to the slave changing this to
31 32... 41 42 43 44 45 46
It'll then just keep looping displaying the same values
gjs_rsdi



Joined: 06 Feb 2006
Posts: 468
Location: Bali

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

PostPosted: Thu Jul 18, 2019 5:27 am     Reply with quote

Thanks Ttelmah

Will try to adapt your master and slave to one data read for PIC16F1847
Will be back after testing it

Best wishes
Joe
gjs_rsdi



Joined: 06 Feb 2006
Posts: 468
Location: Bali

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

PostPosted: Fri Sep 27, 2019 9:07 pm     Reply with quote

Hi

I didn't post for a very long time, my computer crashed totally and take me long time to recover things.

* First, I adapted Ttelmah program for one bit send/receive and it works Smile
The master:
Code:

#include <16F1847.h>

#FUSES INTRC_IO,NOIESO,PLL,WDT,MCLR,PUT
#FUSES NOPROTECT,NOCPD,WRT,STVREN
#FUSES BROWNOUT,BORV25,NOLVP,NODEBUG
#use delay(clock=32MHz,INTERNAL=8MHz)
#use rs232(baud=9600,parity=N,xmit=PIN_B5,rcv=PIN_B2,bits=8,restart_wdt,errors)
#use i2c(Master,Fast=1000000,I2C1)//I2C1 module, automatically hardware

void I2C (void)
{
    if(I2CstartF==1)
    {
//write RX data to the Slave PIC
         I2CstartF=0;
//write data to Slave PIC
        i2c_start();
        i2c_write(0xB0);//Slave address with write
        i2c_write(data);//data byte from RX
        i2c_stop();
//read back data from the Slave PIC
        delay_us(100); //ensure the Slave has had time to complete the write
        i2c_start();
        i2c_write(0xB1);//Slave address with read~
        Slaverpt = i2c_read(0);//Slave rpt to master
        i2c_stop();
        scomtxw2 = Slaverpt;
        enable_interrupts(INT_TBE);//rpt to PC data from the Slave       
      }
}

The slave:
Code:

#include <16F1847.h>

#FUSES INTRC_IO,NOIESO,PLL,WDT,MCLR,PUT
#FUSES NOPROTECT,NOCPD,WRT,STVREN
#FUSES BROWNOUT,BORV25,NOLVP,NODEBUG
#use delay(clock=32MHz,INTERNAL=8MHz)
#use rs232(baud=9600,parity=N,xmit=PIN_B5,rcv=PIN_B2,bits=8,restart_wdt,errors)
#use i2c(Slave,Address=0xB0,I2C1)//I2C1 module, automatically hardware

#INT_SSP
void I2C_isr (void)
{
      state=i2c_isr_state();
      if (state<=0x80)
      {
         if (state==0x80)//here we need to read, and not release the clock
         {       
            value=i2c_read(0x02);
         }
         else
         {
            value=i2c_read(); //normal read releasing the clock
            data = value+1;//data=value+1 for testing
         }
      }
      if (state>=0x80)//We have a read request from the master so we need to write
      {         
         i2c_write(data);//writes data to master     
      }
}


On the PC Terminal I am getting what I am sending +1.

Tried to send 2 bytes for I2C.
Main
Code:
void I2C (void)
{
    if(I2CstartF==1)
    {
//write RX data to the Slave PIC
         I2CstartF=0;
//write data to Slave PIC
        i2c_start();
        i2c_write(0xB0);//Slave address with write
        i2c_write(data0);//data byte from RX
        i2c_write(data1);//data byte from RX
        i2c_stop();
//read back data from the Slave PIC
        delay_us(100); //ensure the Slave has had time to complete the write
        i2c_start();
        i2c_write(0xB1);//Slave address with read~
//        slaverpt0 = i2c_read();//Slave rpt to master !!!no TX to PC!!!
        slaverpt0 = i2c_read(0);//Slave rpt to master !!!Sending 55,AA,00,10,0F; Expecting 55,AA,01,11,11; Get 55,AA,11,FF,0F;!!! 
        slaverpt1 = i2c_read(0);//Slave rpt to master
        i2c_stop();
        scomtxw2 = slaverpt0;
        scomtxw3 = slaverpt1;
        enable_interrupts(INT_TBE);//rpt to PC data from the Slave       
      }
}

I wrote in the code above what I am getting //

The slave:
Code:
#INT_SSP
void I2C_isr (void)
{
      state=i2c_isr_state();
      if (state<=0x80)
      {
         if (state==0x80)
         {       
            value=i2c_read(0x02);//here we need to read, and not release the clock
         }
         else
         {
            value0=i2c_read(); //normal read releasing the clock
             value1=i2c_read(); //normal read releasing the clock
            data0 = value0+1;//data0=value0+1 for testing
            data1 = value1+1;//data0=value0+1 for testing
         }
      }
      if (state>=0x80)//We have a read request from the master so we need to write
      {         
         i2c_write(data0);//writes data to master
         i2c_write(data1);//writes data to master     
      }
}


Made additional master and slave as close as possible to Ttelmah example but I am not getting nothing on the terminal also with delay 1000 ms up to 5000 ms.
I suppose I am doing something wrong in the settings Sad
Master:
Code:
#include <16F1847.h>

#FUSES INTRC_IO,NOIESO,PLL,NOWDT,MCLR,PUT
#FUSES NOPROTECT,NOCPD,WRT,STVREN
#FUSES BROWNOUT,BORV25,NOLVP,NODEBUG
#use delay(clock=32MHz,INTERNAL=8MHz)
//#use rs232(baud=9600,parity=N,xmit=PIN_B5,rcv=PIN_B2,bits=8,errors)
//#use rs232(baud=9600,parity=N,xmit=PIN_B5,rcv=PIN_B2,errors,STREAM=SERIAL)
#USE RS232(UART1, BAUD=9600, ERRORS, STREAM=SERIAL)
//#use i2c(Master,Fast=400000,I2C1)//I2C1 module, automatically hardware
//#USE I2C(MASTER, BAUD=400000, I2C1, STREAM=I2CPORT)//!!! BOUD=400000 gives: error Option invalid Bad Option: BAUD
#USE I2C(MASTER, Fast=400000, I2C1, STREAM=I2CPORT)
#define SLAVE_ADDR 0x40  //Select some suitable address. Must not be <16, even,
//and must match slave
#define I2C_WRT 1 //flag for write

#define flashled PIN_A2
int flashcnt=0;

#INT_TIMER1
void  TIMER1_isr(void)
{
      flashcnt++;
    if(flashcnt>=25)
    {
        flashcnt=0; 
        output_toggle(flashled);//LED flash every sec
    }
      set_timer1(25536);//timer1 overflow 40ms
}
 
#ZERO_RAM

void main()
{
    setup_adc_ports(NO_ANALOGS);   
    setup_timer_1(T1_INTERNAL|T1_DIV_BY_8);//65 ms overflow
    enable_interrupts(INT_TIMER1);
    enable_interrupts(GLOBAL);
      byte data[16]; //array to hold read data from slave
      byte test[16] = {'1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
      //Test transmission data to send to slave.
      unsigned int8 ctr;

    while(TRUE)
    {   
      delay_ms(5000); //Slow the loop
      i2c_start(I2CPORT); //open the connection
      i2c_write(I2CPORT, SLAVE_ADDR); //select the slave device to read
      for (ctr=0;ctr<16;ctr++)
      {
         //Now read 16bytes from the slave. Must NACK the last read
         if (ctr==15)
            data[ctr]=i2c_read(I2CPORT,0); //This generates NACK
         else
            data[ctr]=i2c_read(I2CPORT);           
      }
      i2c_stop(I2CPORT); //and close the connection
     
      //Display the values
      for (ctr=0;ctr<16;ctr++)
      {
         fprintf(SERIAL,"%02x ", data[ctr]);
      }
      fprintf(SERIAL,"\n\r"); //LF/CR to show end of data     
     
      //Now write new values to the slave
      i2c_start(I2CPORT); //open the connection
      i2c_write(I2CPORT, SLAVE_ADDR | I2C_WRT); //select the slave device to write
      for (ctr=0;ctr<16;ctr++)
      {
         //Now send 16bytes to the slave.
         i2c_write(I2CPORT, test[ctr]);           
      }
      i2c_stop(I2CPORT); //and close the connection       
    }
}

Tried also with the out-commented settings.

Slave:
Code:
#include <16F1847.h>

#FUSES INTRC_IO,NOIESO,PLL,NOWDT,MCLR,PUT
#FUSES NOPROTECT,NOCPD,WRT,STVREN
#FUSES BROWNOUT,BORV25,NOLVP,NODEBUG
#use delay(clock=32MHz,INTERNAL=8MHz)
#define SLAVE_ADDR 0x40
#USE I2C(SLAVE, I2C1, ADDRESS=SLAVE_ADDR, STREAM=I2CPORT)
//#use i2c(Slave,Address=0xB0,I2C1)//I2C1 module, automatically hardware

byte data[16] = {0xF0,0xF1,0xF2,0xF3,0xF4, 0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC, 0xFD,0xFE,0xFF};
#bit CKP=getenv("BIT:CKP")
#define flashled PIN_A2
int flashcnt=0;

#INT_TIMER1
void  TIMER1_isr(void)
{
   flashcnt++;
      if(flashcnt>=25)
      {
         flashcnt=0;
        output_toggle(flashled);//LED flash every sec
   }
   set_timer1(25536);//timer1 overflow 40ms
}

///////////////////////////////////////////////////////////////////
//The core I2C slave code.   
//Now states < 0x80, are read states. The master is sending us data
//However state 0x0, is where the master sends up the address itself
//so we need to use state-1 as the array index to write data
//State 0x80 is where the master sends the address for the write. On this
//we must read, and then write the byte for the reply.
/////////////////////////////////////////////////////////////////// 
#INT_SSP
void i2c_int(void)
{
   //The core I2C slave code.
   unsigned int8 state;
   byte value;
   state=i2c_isr_state(I2CPORT);
   //Now states < 0x80, are read states. The master is sending us data
   //However state 0x0, is where the master sends up the address itself
   //so we need to use state-1 as the array index to write data
   //State 0x80 is where the master sends the address for the write. On this
   //we must read, and then write the byte for the reply.
   if (state<=0x80)
   {
      if (state==0x80)
      {
         //here we need to read, and not release the clock
         value=i2c_read(I2CPORT,2);
      }
      else
      {
         value=i2c_read(I2CPORT); //normal read releasing the clock
         if (state>0)
            data[(state-1) & 0xF] = value; //limit to just 16 addresses allowed
      }
   }
   if (state>=0x80)
   {
      //We have a read request from the master so we need to write
      i2c_write(I2CPORT, data[state & 0xF]); //again limit to 16 addresses
      //Now the slave _should_ release CKP here but some chips have an issue
      //where this does not happen. The following code is a 'bodge' to ensure
      //it does release.
      CKP=TRUE; //Add this for chips that don't release the clock....     
   }
}


#ZERO_RAM

void main()
{
      setup_adc_ports(NO_ANALOGS);   
    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)
    {
      delay_cycles(1);   
    }
}


The hardware as below:
Quote:
//Hardware connections, pin numbers.
//PIC16F1847
//PIN1 Flash LED
//PIN4 MCLR~ Orange
//PIN5 VSS/GND
//PIN7 SDA1 Grey
//PIN8 RX/PCTX Green
//PIN10 SCL1 White
//PIN11 TX/PCRX White
//PIN12 ICSPCLK Yellow
//PIN13 ICSPDAT Blue
//PIN14 VDD/VCC


I tested the adapted program from Ttelmah with 24C512 and up to 8 bytes and it works Smile

Please help Crying or Very sad
Best wishes
Joe
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Fri Sep 27, 2019 10:37 pm     Reply with quote

The slave ISR is called for every byte.
It needs to only ever send one byte each time it is called.
It gets called again for the nect byte.
Having twin writes in the ISR won't work (as you have found).
Same applies to the twin reads.

I2C_ISR_STATE gives the 'index' to what must happen:

0 = read adress
1 = read first byte
2 = read second byte
3,4... read nth byte

80 = read address then write first byte
81,82... write nth byte....

Except for state 0x80, each call to the ISR must only read or write
one byte only.
gjs_rsdi



Joined: 06 Feb 2006
Posts: 468
Location: Bali

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

PostPosted: Fri Sep 27, 2019 11:11 pm     Reply with quote

Hi Ttelmah

Thank you for the prompt answer.
Now is clear to me how the ISR works Smile
I will try to make a "switch" or a "for" inside the ISR

Can you check also why my adaptation to PIC16F1847 don't work?
Where I made mistakes?

Best wishes
Joe
gjs_rsdi



Joined: 06 Feb 2006
Posts: 468
Location: Bali

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

PostPosted: Sat Sep 28, 2019 3:18 am     Reply with quote

Hi

I changed the send/receive 1 byte to 2 bytes via the I2C with switches in the slave IRS and it works with one problem:
The data I am sending is swapped in the report.
Sending: h\55,AA,00,10,0F. Data bytes sent are h\00,10, received h\01,11
Expecting: h\55,01,11,11
Getting: h\55,11,01,11
If I swap the data anywhere in the Master or Slave, getting them correct.
Somewhere is swapping I suppose, can't find where.
Apologizing for the clumsy rs232

Master
Code:
///////////////////////////////////////////////////////////////////
//PIC16F1847 Master I2C with PIC16F1847 Slave test program
//I2C1847MV2.c
//PROGRAM adapted from Ttelmah program to send/receive 2 bytes
//The data is from RX sent by PC
//Sending data from PC to Master via rs232
//Master send I2C to Slave; Slave back to master;
//Master to PC
///////////////////////////////////////////////////////////////////
//Hardware connections, pin mumbers.
//PIC16F1847           
//PIN1 Flash LED
//PIN4 MCLR~ Orange         
//PIN5 VSS/GND           
//PIN7 SDA1 Grey         
//PIN8 RX/PCTX Green     
//PIN10 SCL1 White         
//PIN11 TX/PCRX White     
//PIN12 ICSPCLK Yellow     
//PIN13 ICSPDAT Blue
//PIN14 VDD/VCC
///////////////////////////////////////////////////////////////////
#include <16F1847.h>

#FUSES INTRC_IO,NOIESO,PLL,WDT,MCLR,PUT
#FUSES NOPROTECT,NOCPD,WRT,STVREN
#FUSES BROWNOUT,BORV25,NOLVP,NODEBUG
#use delay(clock=32MHz,INTERNAL=8MHz)
#use rs232(baud=9600,parity=N,xmit=PIN_B5,rcv=PIN_B2,bits=8,restart_wdt,errors)
#use i2c(Master,Fast=1000000,I2C1)//I2C1 module, automatically hardware

#define flashled PIN_A2
int flashcnt=0;
short I2CstartF=0;
int txwords,txw2,txw3,txwchs;
int rxwords,rxw0,rxw1,rxw2,rxw3,rxw4,rxwchs;
int data0,data1;
int slaverpt0,slaverpt1;

#INT_TIMER1
void  TIMER1_isr(void)
{
      flashcnt++;
    if(flashcnt>=25)
    {
        flashcnt=0; 
        output_toggle(flashled);//LED flash every sec
    }
      set_timer1(25536);//timer1 overflow 40ms
}
//START of serial communication////////////////////////////////////
#int_TBE
void TBE_isr(void)
{
   switch(txwords)
   {
         case 0:
         {
            putc(85);
            txwchs=85;   
            txwords++;     
         }
         break;
        case 1:
        {
            putc(170);
            txwchs=255;
            txwords++;         
        }
          break;
          case 2:
          {
            putc(txw2);
            txwchs=txwchs+txw2;
            txwords++;         
         }
        break;
          case 3:
          {
            putc(txw3);
            txwchs=txwchs+txw3;
            txwords++;         
         }
        break;
        case 4:
        {
            putc(txwchs);
            txwords=0;   
            disable_interrupts(INT_TBE);           
        }
        break;
    }   
}

#INT_RDA
void  RDA_isr(void)
{
    switch(rxwords)
    {
        case 0:
        {
            rxw0=getc();
            if (rxw0==85)
            {
               rxwchs=85;
               rxwords=1;
            }
            else
            {
               rxwords=0;
            }   
        }
        break;
        case 1:
        {
            rxw1=getc();
            if (rxw1==170)
            {
               rxwchs=255;
               rxwords++;
            }
            else
            {
               rxwords=0;
            }         
        }
        break;
        case 2:
        {
            rxw2=getc();//data0
            rxwchs=rxwchs+rxw2;
            rxwords++;         
        }
        break;
        case 3:
        {
            rxw3=getc();//data1
            rxwchs=rxwchs+rxw3;
            rxwords++;         
        }
        break;
        case 4:
        {
            rxw4=getc();
            if (rxwchs==rxw4)
            {
               data0=rxw2;
               data1=rxw3;
               I2CstartF=1;//start i2c   
            }
            rxwords=0;   
        }
        break;
    }
}
//END of serial communication//////////////////////////////////////
void I2C (void)
{
    if(I2CstartF==1)
    {
         I2CstartF=0;
//write data to Slave PIC
        i2c_start();
        i2c_write(0xB0);//Slave address with write
        i2c_write(data0);
        i2c_write(data1);
        i2c_stop();
//read back data from the Slave PIC
        delay_us(1000); //ensure the Slave has had time to complete the write
        i2c_start();
        i2c_write(0xB1);//Slave address with read
        slaverpt0 = i2c_read();
        slaverpt1 = i2c_read(0);
        i2c_stop();
        txw2 = slaverpt0;
        txw3 = slaverpt1;
        enable_interrupts(INT_TBE);//rpt to PC data from the Slave       
      }
}

#ZERO_RAM

void main()
{
    setup_adc_ports(NO_ANALOGS);   
    setup_timer_1(T1_INTERNAL|T1_DIV_BY_8);//65 ms overflow
    enable_interrupts(INT_TIMER1);
    disable_interrupts(INT_TBE);
    enable_interrupts(INT_RDA);
    enable_interrupts(GLOBAL);

    while(TRUE)
    {
        I2C();
        delay_cycles(1);
    }
}
/////////////////////////////////////////////


Slave
Code:
///////////////////////////////////////////////////////////////////
//PIC16F1847 Slave I2C test program
//I2C1847SV2.c Slave works with I2C1847MV2.c Master
//PROGRAM adapted from Ttelmah, writes/reads two bytes from/to master.
///////////////////////////////////////////////////////////////////
//Hardware connections, pin mumbers.
//PIC16F1847         
//PIN1 Flash LED
//PIN4 MCLR~ Orange         
//PIN5 VSS/GND            
//PIN7 SDA1 Grey         
//PIN8 RX/PCTX Green      
//PIN10 SCL1 White         
//PIN11 TX/PCRX White      
//PIN12 ICSPCLK Yellow   
//PIN13 ICSPDAT Blue
//PIN14 VDD/VCC
///////////////////////////////////////////////////////////////////
#include <16F1847.h>

#FUSES INTRC_IO,NOIESO,PLL,NOWDT,MCLR,PUT
#FUSES NOPROTECT,NOCPD,WRT,STVREN
#FUSES BROWNOUT,BORV25,NOLVP,NODEBUG
#use delay(clock=32MHz,INTERNAL=8MHz)
#use rs232(baud=9600,parity=N,xmit=PIN_B5,rcv=PIN_B2,bits=8,restart_wdt,errors)
#use i2c(Slave,Address=0xB0,I2C1)//I2C1 module, automatically hardware

#define flashled PIN_A2
short powerupF=1;
int flashcnt=0;
int data0,data1;
int state=0;
int value=0;
int I2Creadsw=0;
int I2Cwritesw=0;

#INT_TIMER1
void  TIMER1_isr(void)
{
   flashcnt++;
   set_timer1(25536);//timer1 overflow 40ms
      if(flashcnt>=25)
      {
         flashcnt=0;
         if(powerupF==1)
        output_toggle(flashled);//LED flash every sec
   }
}

///////////////////////////////////////////////////////////////////
//The core I2C slave code.   
//Now states < 0x80, are read states. The master is sending us data
//However state 0x0, is where the master sends up the address itself
//so we need to use state-1 as the array index to write data
//State 0x80 is where the master sends the address for the write. On this
//we must read, and then write the byte for the reply.
/////////////////////////////////////////////////////////////////// 
#INT_SSP
void I2C_isr (void)
{
      state=i2c_isr_state();
      if (state<=0x80)
      {
         if (state==0x80)//here we need to read, and not release the clock
         {       
            value=i2c_read(0x02);//here we need to read, and not release the clock 
         }
         else
         {
         switch(I2Creadsw)
         {
            case 0:
            {
                  value=i2c_read();
                  data0 = value+1;
               I2Creadsw++;
            }
            break;
            case 1:
            {
                  value=i2c_read();
                  data1 = value+1;
               I2Creadsw=0;
            }
            break;
         }
         }
      }
      if (state>=0x80)//We have a read request from the master so we need to write
      {
      switch(I2Cwritesw)
      {
         case 0:
         {
            i2c_write(data0);
            I2Cwritesw++;
         }
         break;
         case 1:
         {
            i2c_write(data1);
            I2Cwritesw=0;
         }
         break;
      }       
      }
}

#ZERO_RAM

void main()
{
      setup_adc_ports(NO_ANALOGS);     
    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)
    {
      delay_cycles(1);
    }
}
/////////////////////////////////////////////


Please help
Best wishes
Joe
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Sat Sep 28, 2019 3:27 am     Reply with quote

Quote:
#use i2c(Master,Fast=1000000,I2C1)

You have your i2c speed set to 1 MHz. How about backing it off to 100 KHz ?

If that doesn't work, try running the slave PIC fast, but the master slow.
Remove the section in bold for the Master:
Quote:
#use delay(clock=32MHz,INTERNAL=8MHz)
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Sat Sep 28, 2019 9:39 am     Reply with quote

Big issue is state 0.

On I2c, the very first transfer is the device address.
When the routine arrives and I2C_ISR_STATE==0, the byte available
is the device address, not a byte of data. So, sending two bytes
actually involves _three_ reads.
Address 0
Byte 0 1
Byte 1 2

The number to the right, is the I2C_ISR_STATE value.

Because your routine 'toggles' it's count between 0 & 1, it gets set to
1 for the first actual byte, then goes back to 0, and replaces the
address already read with the second byte. Result the values are
reversed.
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Sat Sep 28, 2019 9:59 am     Reply with quote

You also don't need the extra count variables. Just use the state:
Code:

#INT_SSP
void I2C_isr (void)
{
      state=i2c_isr_state();
      if (state<=0x80)
      {
         switch(state) {

         case 0x80:     
              value=i2c_read(0x02);//here we need to read, and not release the clock
              break;
       
         case 0:
              value=i2c_read();
              break; //throw away address read
         case 1:
              date0=i2c_read()+1;
              break;
         case 2:
              date1=i2c_read()+1;
              break;
         }
      }
      switch(state)  {
      case 0x80:
           i2c_write(data0);
           break;
      case 0x81:
           i2c_write(data1);
           break;       
      }
}


The state tells you where you are in the transfer. Use it.
gjs_rsdi



Joined: 06 Feb 2006
Posts: 468
Location: Bali

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

PostPosted: Sat Sep 28, 2019 10:12 pm     Reply with quote

Thank you for the answer Ttelmah.
The program you posted works correct.
I am getting all correct.

Thank you for the answer PCM Programmer.
I tried Slow=100000; Fast=400000; Fast=1000000, all work with the Slave ISR posted by Ttelmah.
I don't know why clock=32MHz in bold, in the MPLAB IDE v8.92 is not bold.

***
I have another problem that sometimes the Master Flash LED have just short ON Flash. The period of 2 seconds is OK more or less, I am working with the internal RC clock.
Sometimes Reset helps.
Sometimes new message from PC (and I2C I suppose) helps.
All the system on prototyping board with jumpers so maybe this is the problem.
I will come back after I solve this problem.

Thank you again
Best wishes
Joe
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Sun Sep 29, 2019 4:21 am     Reply with quote

Two comments:

Change the clock setting to just

#use delay(INTERNAL=32MHz)

This is the safest and easiest way to enable the 32MHz oscillator with
the PLL and the clock set correctly.

It was bold in PCM_Programmers post, since he was telling you what to
remove. He was suggesting testing with the master chip set to run at
8MHz, since this then ensures the slave has enough time to do everything.

The code has a danger. You have the WDT enabled in the master.
On this chip the default WDT timeout is _nominally_ 2 seconds.
Now the 'nominally' is important. The actual timeout defends on the chip,
temperature, and supply voltage, and can be as low as 1.5 seconds and
as high as 2.56 seconds.
At no point in the code do you reset the WDT, instead relying on the
'internal' reset generated by the serial code. This _only_ resets the
watchdog when data is _received_. So unless you are sending characters
to the chip every 1.5 seconds or faster, it will be restarting.
Also though the entire I2C could be hung, and it would still reset the
watchdog.... Sad
This is very much not how to use a watchdog. Do some research on this
and start with the watchdog disabled, then design a proper way of
resetting this.
gjs_rsdi



Joined: 06 Feb 2006
Posts: 468
Location: Bali

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

PostPosted: Sun Sep 29, 2019 5:55 pm     Reply with quote

Thank you Ttelmah

Shame on me Embarassed

I deleted the restart_wdt(); from the "While" but didn't change the WDT to NOWDT in the fuses Sad

Now everything works 100% Smile

I still have two question regarding the Main I2C:
* What happening if an interrupt occurs when the program is inside an I2C statement, like i2c_start(); i2c_write(data0); etc...
* How many program cycles each takes?

Thank you again for all the help. Finally I am starting to understand how I2C works in CCS.
Best wishes
Joe
gjs_rsdi



Joined: 06 Feb 2006
Posts: 468
Location: Bali

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

PostPosted: Sun Sep 29, 2019 6:18 pm     Reply with quote

Sorry, forget to ask also:
If I am changing from:
Code:
#use delay(clock=32MHz,INTERNAL=8MHz)

To:
Code:
#use delay(INTERNAL=32MHz)

The program works the same and now the LED's blink together.

How I should set for 8MHz crystal or external oscillator if I want to work on 32MHz?

I used in the past for the PIC18F26K22 the below:
Code:
#FUSES ECM_IO
#FUSES PLLEN
#use delay(clock=64MHz)

How the compiler knows that the crystal is 16MHz in this case?

Best wishes
Joe
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 Previous  1, 2, 3, 4, 5, 6  Next
Page 2 of 6

 
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