|
|
View previous topic :: View next topic |
Author |
Message |
gjs_rsdi
Joined: 06 Feb 2006 Posts: 468 Location: Bali
|
|
Posted: Wed Jul 17, 2019 10:57 pm |
|
|
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 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
CCS PCM C Compiler, Version 5.062
Best wishes
Joe |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19430
|
|
Posted: Wed Jul 17, 2019 11:37 pm |
|
|
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: 19430
|
|
Posted: Thu Jul 18, 2019 3:23 am |
|
|
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
|
|
Posted: Thu Jul 18, 2019 5:27 am |
|
|
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
|
|
Posted: Fri Sep 27, 2019 9:07 pm |
|
|
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
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
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
Please help
Best wishes
Joe |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19430
|
|
Posted: Fri Sep 27, 2019 10:37 pm |
|
|
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
|
|
Posted: Fri Sep 27, 2019 11:11 pm |
|
|
Hi Ttelmah
Thank you for the prompt answer.
Now is clear to me how the ISR works
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
|
|
Posted: Sat Sep 28, 2019 3:18 am |
|
|
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
|
|
Posted: Sat Sep 28, 2019 3:27 am |
|
|
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: 19430
|
|
Posted: Sat Sep 28, 2019 9:39 am |
|
|
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: 19430
|
|
Posted: Sat Sep 28, 2019 9:59 am |
|
|
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
|
|
Posted: Sat Sep 28, 2019 10:12 pm |
|
|
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: 19430
|
|
Posted: Sun Sep 29, 2019 4:21 am |
|
|
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....
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
|
|
Posted: Sun Sep 29, 2019 5:55 pm |
|
|
Thank you Ttelmah
Shame on me
I deleted the restart_wdt(); from the "While" but didn't change the WDT to NOWDT in the fuses
Now everything works 100%
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
|
|
Posted: Sun Sep 29, 2019 6:18 pm |
|
|
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 |
|
|
|
|
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
|