|
|
View previous topic :: View next topic |
Author |
Message |
TYDOM17
Joined: 24 Apr 2017 Posts: 17
|
I2C Clone |
Posted: Tue May 16, 2017 1:34 pm |
|
|
All,
I'm trying to clone the I2C slave behavior of an obsolete part.
What's working now
Today I can talk between master and slave using the example slave and 2401.c examples. I can request and send 16 bytes in succession.
Problem Statement I am getting hung up with how to respond to a request for information with a 2 byte command, where this obsolete part has up to 5 commands it can respond to. Basically I am not sure how to search through multiple commands in the ISR and respond accordingly.
Assuming the SSP interrupt finds a match for the part address, the slave needs to determine which command was sent and then respond with the appropriate payload.
I am using a PIC18F25k20 as a slave to respond to one of 5 commands from the master (FPGA).
To simplify debugging I created the same code the obolete part reponds to today (today it responds to an FPGA, I am using another PIC dev board as a fill in MASTER) – using part data sheet and have proven that the obsolete part does indeed respond appropriately to these commands.
Now I just need to make the new hardware I2C slave(PIC18F25k20) do the same.
I have shown just one of the 5 commands below.
Code: |
MASTER Function:
BYTE read_registers(int8 t) {
i2c_start();
i2c_write(0x2E); //DEVICE ADDRESS WRITE
i2c_write(0x00); //REG MSB COMMAND
i2c_write(0x40); //REG LSB COMMAND
i2c_start();
i2c_write(0x2F); //DEVICE ADDRESS READ
reg1 = i2c_read(1);//clock stretch
reg2 = i2c_read(1);//clock stretch
reg3 = i2c_read(1);//clock stretch
reg4 = i2c_read(1);//clock stretch
reg5 = i2c_read(1);//clock stretch
reg6 = i2c_read(1);//clock stretch
reg7 = i2c_read(1);//clock stretch
reg8 = i2c_read(1);//clock stretch
reg9 = i2c_read(1);//clock stretch
reg10 = i2c_read(0);//clock stretch
i2c_stop();
return(reg1,reg2, reg3,reg4, reg5,reg6, reg7,reg8, reg9,reg10);
}
|
Code: |
SLAVE ISP
#INT_SSP
void ssp_interrupt(){
unsigned int8 incoming, state, x, buffer[3],val[10];
state = i2c_isr_state();
if(state < 0x80){ //Master is sending data
incoming = i2c_read();
if(state == 0) //First received byte
buffer[0] = incoming;
if(state == 1) //Second received byte
buffer[1] = incoming;
if(state== 2) //Third received byte
buffer[2] = incoming;
}
if (state >= 0x80){
if(buffer[1] ==0x40){//command 1
for(x=0;x<10;x++){
i2c_write(val[x]);
}
}
//else if.... command 2
//else if.... command 3
//else if.... command 4
//else... command 5
}
}
|
Am I on the right track with searching and responding to bytes 2&3? |
|
|
jeremiah
Joined: 20 Jul 2010 Posts: 1349
|
|
Posted: Tue May 16, 2017 2:20 pm |
|
|
Couple of things:
1.
You are not handling the isr states completely.
1a.
When state == 0, the byte read is the I2C address. You are reading it as data...is this intentional?
1b.
You need to read the i2c buffer when state == 0x80 as this is also an address byte and must be read.
Look at the example in the compiler manual (it has typos, but the intent is there, so I modified out the typos):
Code: |
#INT_SSP
void i2c_isr() {
state = i2c_isr_state();
if(state== 0 )
i2c_read();
if(state == 0x80)
i2c_read(2);
if(state >= 0x80)
i2c_write(send_buffer[state - 0x80]);
else if(state > 0)
rcv_buffer[state - 1] = i2c_read();
}
|
Notice how it reads on all values 0 to 0x80, and tosses out the values for 0 and 80 but saves the other values inbetween.
2.
you are using a for loop in a single event ISR. The ISR is meant to handle one byte at a time. You are gonna need to get rid of that for loop and just have some logic to pick the right byte from your buffer based on the i2c_isr_state value. |
|
|
TYDOM17
Joined: 24 Apr 2017 Posts: 17
|
|
Posted: Tue May 16, 2017 4:29 pm |
|
|
Thanks for the help jeremiah,
You are right I was reading the I2C address as data, I will remove this.
I will follow the example in the book and then run a check on the address byte value outside the ISR using a flag method.
Can you tell me what the 2 does in the i2c_read(2) function call?
According to the book there are only 2 options 0 or 1 for i2c_read. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue May 16, 2017 5:43 pm |
|
|
Apparently you have some book on CCS C. Jeremiah was referring to
the CCS manual (a pdf file) here:
http://www.ccsinfo.com/downloads/ccs_c_manual.pdf
It explains all the parameters for i2c_read(), including the 2.
Also, the example that Jeremiah is talking about is in the CCS manual,
not your book. Look in the CCS manual in the section on i2c_isr_state()
(pages 231-232 in the Acrobat reader). It has the #int_ssp example
code, with typos, that he's referring to. |
|
|
TYDOM17
Joined: 24 Apr 2017 Posts: 17
|
|
Posted: Thu Jun 15, 2017 1:44 pm |
|
|
Sorry in advance for the long reply – I’ve tried to keep this short.
Prototype ISR routine works
I now have a working prototype at my desk with master and slave getting along nicely using the code below. The SSP interrupt (including flags) appear to stretch out the clock nicely allowing for the master to send up to 10 bytes in succession (ring buffer) such that the slave is able to read these bytes in, buffer them and then act on the received buffer when there is a match in the 2 byte slave register address.
As stated before this clone is trying to emulate an obsolete I2C part which was designed to model its registers after EEPROM with 2 byte implicit register addresses. For this reason, all responses to implicit register address include 10 bytes, the first 2 bytes are the address with the next 8 being data just like older eeprom. However, when I change my master (PIC18F25K20) out for the intended (not simulated) master (FPGA) the I2C bus is having some real issues.
New Issues
The 1st command from the master (FPGA running @ 50kHz) with address 0x00,0x00 comes through just fine and I respond with 10 bytes including address 0x00,0x00. Then along comes address 0x00,0x40 – no problem here. Then along comes address 0x00,0x48 and my slave can only respond with address 0x00,0x40 and associated payload for that register. All proceeding addresses including 0x00,0x50 - 0x00,0xFF behave the same way.
Debugging
When I include the printf debugging statements I am able to see the following come through the rx_buffers.
Quote: |
I2C SLAVE NOW SNIFFER FOR BYTES
00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00
00 02 00 00 00 00 00 00 00 00
00 02 54 00 00 00 00 00 00 00
00 02 54 47 00 00 00 00 00 00
00 02 54 47 14 00 00 00 00 00
00 02 54 47 14 95 00 00 00 00
00 02 54 47 14 95 75 00 00 00
00 02 54 47 14 95 75 6E 00 00
00 02 54 47 14 95 75 6E 00 00
00 02 54 47 14 95 75 6E 00 00
00 00 54 47 14 95 75 6E 00 00
00 00 54 47 14 95 75 6E 00 00
00 00 54 47 14 95 75 6E 00 00
00 40 54 47 14 95 75 6E 00 00
00 40 54 47 14 95 75 6E 00 00
00 40 54 47 14 95 75 6E 00 00
00 40 54 47 14 95 75 6E 00 00
00 48 54 47 14 95 75 6E 00 00
00 48 54 47 14 95 75 6E 00 00
00 48 54 47 14 95 75 6E 00 00
00 48 54 47 14 95 75 6E 00 00
00 50 54 47 14 95 75 6E 00 00
00 50 54 47 14 95 75 6E 00 00
00 50 54 47 14 95 75 6E 00 00
00 50 54 47 14 95 75 6E 00 00
00 58 54 47 14 95 75 6E 00 00
00 58 54 47 14 95 75 6E 00 00
00 58 54 47 14 95 75 6E 00 00
00 58 54 47 14 95 75 6E 00 00
00 58 54 47 14 95 75 6E 00 00
00 58 54 47 14 95 75 6E 00 00
00 58 54 47 14 95 75 6E 00 00
00 58 54 47 14 95 75 6E 00 00
00 58 54 47 14 95 75 6E 00 00
00 58 54 47 14 95 75 6E 00 00
00 58 54 47 14 95 75 6E 00 00
00 58 54 47 14 95 75 6E 00 00
00 58 54 47 14 95 75 6E 00 00 |
slave code
Code: | #include <18F25K20.h>
#FUSES INTRC_IO, NOMCLR
#use delay(clock=16Mhz, internal)
#use rs232(baud=38400,INVERT,parity=N,xmit=PIN_C7,rcv=PIN_C6)
#use i2c(SLAVE,FORCE_HW,I2C1,address=0x2E)
#define I2C_EN PIN_A5
#define LED PIN_B1
#define E2PWR PIN_B3
#define HUMTEMP PIN_B4
#include <DS1302_TE.C>
unsigned int8 incoming, state, x, send_buffer[10],rcv_buffer[10]={0,0,0,0,0,0,0,0,0,0};
unsigned int8 address,data[4],buffer[16],z=0,cmdlsb,cmdmsb,cnt,nshocks=1;
int8 DAY,MON,YEAR,DOW,HR,MIN,SEC,tt1,tt2,tt3,tt4,tt5,tt6,tt7,tt8,tt9;
INT16 DAY2,MON2,YEAR2,DOW2,HR2,MIN2,SEC2,tlsb,tcsb,tmsb,zeros;
int8 tlsb_low,tcsb_low,tmsb_low,tlsb_high,tcsb_high,tmsb_high;
int1 sspflag=0,hstateflag=0;
void timestamp(){
rtc_get_date(DAY,MON,YEAR,DOW);
rtc_get_time(HR,MIN,SEC);
SEC = 0b00111010;// 6 bits
MIN = 0b00100111;// 6 bits
MON = 0b00000000;// 5 bits
HR = 0b00000001;// 5 bits
DAY = 0b00000011;// 5 bits
DOW = 0b00000010;// 3 bits
YEAR = 0b01110000;// 8 bits
zeros = 0x00; // 9 bits
SEC2=SEC;
MIN2=MIN;
MON2=MON;
YEAR2=YEAR;
tlsb = ((MON2 <<12)|(MIN2 << 6)| SEC2);
//printf("tlsb = %LX ",tlsb);
tcsb = (zeros <<5 | HR);
//printf("clsb = %LX ",tcsb);
tmsb = ((YEAR2 <<8)|(DOW << 5)| DAY);
//printf("clsb = %LX ",tmsb);
tlsb_low = tlsb;
tlsb_high = tlsb>>8;
tcsb_low = tcsb;
tcsb_high = tcsb>>8;
tmsb_low = tmsb;
tmsb_high = tmsb>>8;
//i2c_write(tmsb_high);
//i2c_write(tmsb_low);
//i2c_write(tcsb_high);
//i2c_write(tcsb_low);
//i2c_write(tlsb_high);
//i2c_write(tlsb_low);
//printf("[%X][%X][%X][%X][%X][%X]\n\r",tlsb_high,tlsb_low,tcsb_high,tcsb_low,tmsb_high,tmsb_low);
}
void main(){
output_high(I2C_EN);
output_high(E2PWR);
output_high(HUMTEMP);
output_high(LED);
delay_ms(200);
printf("\n\rI2C SLAVE NOW SNIFFER FOR BYTES\n\r");
output_low(LED);
rtc_init();
/////////////////////////// DATE STAMP MANUAL ///////////////////////////////
// rtc_set_datetime(22,5,17,1,10,38); // already set, leaving alone for now
/////////////////////////// INITIALIZE INTERRUPTS ///////////////////////////
enable_interrupts(INT_SSP);
enable_interrupts(GLOBAL);
///////////////////////////////////////////////////////////////////////////////
while (TRUE){
if(hstateflag==1){
disable_interrupts(INT_SSP);
disable_interrupts(GLOBAL);
if (cmdlsb==0x00){ //GET STATUS REGISTER SUMMARY
//timestamp();
i2c_write(0x00);
i2c_write(0x00);
i2c_write(0x02);
i2c_write(0x00);
i2c_write(0x5C);
i2c_write(0x88);
i2c_write(0x00);
i2c_write(0x14);
i2c_write(0x75);
i2c_write(0xA9);
}
else if(cmdlsb==0x02 && tt3!=0){// ASSUME THE FPGA IS TRYING TO SET TIME - DON'T RESPOND
//timestamp();
i2c_write(0x00);
i2c_write(0x02);
i2c_write(tt1);
i2c_write(tt2);
i2c_write(tt3);
i2c_write(tt4);
i2c_write(tt5);
i2c_write(tt6);
}
else if(cmdlsb==0x02){
}
else if(cmdlsb==0x40){// GET 1ST PAGE SUMMARY
//timestamp();
i2c_write(0x00);
i2c_write(0x40);
i2c_write(2);//shock event cnt
i2c_write(0);//status
i2c_write(3);//status
i2c_write(3);//status
i2c_write(3);//status
i2c_write(3);//status
i2c_write(3);//status
i2c_write(3);//status
}
else if(cmdlsb==0x48){// GET 2ns PAGE SUMMARY
//timestamp();
i2c_write(0x00);
i2c_write(0x48);
i2c_write(1);//shock event cnt
i2c_write(0);//status
i2c_write(4);//status
i2c_write(4);//status
i2c_write(4);//status
i2c_write(4);//status
i2c_write(4);//status
i2c_write(4);//status
}
else if(cmdlsb==0x50){// GET 3rd PAGE SUMMARY
//timestamp();
i2c_write(0x00);
i2c_write(0x50);
i2c_write(0xFF);
i2c_write(0xFF);
i2c_write(0xFF);
i2c_write(0xFF);
i2c_write(0xFF);
i2c_write(0xFF);
i2c_write(0xFF);
i2c_write(0xFF);
}
else //DEFAULT GARBAGE DATA
i2c_write(77);
hstateflag=0;
enable_interrupts(INT_SSP);
enable_interrupts(GLOBAL);
}
if(sspflag==1){
disable_interrupts(INT_SSP);
disable_interrupts(GLOBAL);
cmdmsb=rcv_buffer[0];//MSB
cmdlsb=rcv_buffer[1];//LSB
tt1= rcv_buffer[2];
tt2= rcv_buffer[3];
tt3= rcv_buffer[4];
tt4= rcv_buffer[5];
tt5= rcv_buffer[6];
tt6= rcv_buffer[7];
tt7= rcv_buffer[8];
tt8= rcv_buffer[9];
printf("%X %X %X %X %X %X %X %X %X %X\n\r",
rcv_buffer[0],rcv_buffer[1],rcv_buffer[2],rcv_buffer[3],rcv_buffer[4],rcv_buffer[5],rcv_buffer[6],rcv_buffer[7],rcv_buffer[8],rcv_buffer[9]);
sspflag=0;
enable_interrupts(INT_SSP);
enable_interrupts(GLOBAL);
}
}
}
#INT_SSP
void ssp_interrupt(){
output_high(LED);
state = i2c_isr_state();
if(state== 0 ) //MATCH /W- read to clear int but tosses out the value
i2c_read();
if(state == 0x80) //MATCH R/- read to clear int but tosses out the value
i2c_read(2);
if(state >= 0x80){ //TRANSMIT RECIEVED, 0x81 to 0xFF RESPOND WITH I2CWRITE
hstateflag=1; //SET FLAG - service the i2c_write with if, else-if statments
}
else if(state > 0) //reads in all values 1 to 79
rcv_buffer[state - 1] = i2c_read();//fills up buffer[] 1 through 79
//this really means that it can take up to 78 i2c write commands in succession
sspflag=1;
output_low(LED);
} |
master code snippet
Code: | BYTE read_status_register() {
i2c_start();
i2c_write(0x2E); //DEVICE ADDRESS WRITE
i2c_write(0x00); //REG MSB
i2c_write(0x00); //REG LSB
i2c_stop(); //ADDED THIS TO REPLICATE THE PROBLEM
i2c_start();
i2c_write(0x2F); //DEVICE ADDRESS READ
reg1 = i2c_read(1);//clock stretch
reg2 = i2c_read(1);//clock stretch
NA = i2c_read(1);//clock stretch
NA = i2c_read(1);//clock stretch
NA = i2c_read(1);//clock stretch
NA = i2c_read(1);//clock stretch
NA = i2c_read(1);//clock stretch
NA = i2c_read(1);//clock stretch
NA = i2c_read(1);//clock stretch
NA = i2c_read(0);//clock stretch
i2c_stop();
delay_us(10);
//NA place holder for name of real data
return(reg1,reg2,shk_evnt_cnt,status_bits,ts_first_msb,ts_first_lsb,ts_middle_msb,ts_middle_lsb,ts_end_msb,ts_end_lsb);
} |
Big Picture View
All commands responses you see in the slave code work on my bench between obsolete part and my design, however only the obsolete works all the time with the FPGA.
My Questions
Is there anything I am doing in my while loop or SSP_ISR that appears wrong? Why would my code respond to address 0x00,0x48 with data from the 0x00,0x40 else if statement? Surely I must be doing something wrong and just can't see it.
Bowing down in supplication before the PICMASTERs
Thanks in advance for your time. |
|
|
TYDOM17
Joined: 24 Apr 2017 Posts: 17
|
|
Posted: Thu Jun 15, 2017 4:17 pm |
|
|
Yikes, I found my mistake.
Sometimes typing things out really forces you too look at everything.
My mistake was in responding with multiple i2c_write commands back to back in my if/else if statements rather then setting up the send_buffer[] values for one single.
Code: | i2c_write(send_buffer[state-0x80]); |
FPGA and PIC are happy now.
I'll get back on the forum when I a cleaned up generic version to share in the example code section. |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9229 Location: Greensville,Ontario
|
|
Posted: Thu Jun 15, 2017 4:22 pm |
|
|
Just had a quick look and this caught my eye...
From the master snippet...
Quote: | return(reg1,reg2,shk_evnt_cnt,status_bits,ts_first_msb,ts_first_lsb,ts_middle_msb,ts_middle_lsb,ts_end_msb,ts_end_lsb); |
I've always assumed that return() could only return one variable..
Hopefully someone can enlighten me if this is true as the CCS manual I have only shows 1 variable or 1 value being allowed... |
|
|
jeremiah
Joined: 20 Jul 2010 Posts: 1349
|
|
Posted: Fri Jun 16, 2017 6:15 am |
|
|
temtronic wrote: | just had a quick look and this caught my eye...
fromt the master snippet...
return(reg1,reg2,shk_evnt_cnt,status_bits,ts_first_msb,ts_first_lsb,ts_middle_msb,ts_middle_lsb,ts_end_msb,ts_end_lsb);
I've always assumed that return() could only return one variable..
Hopefully someone can enlighten me if this is true as the CCS manual I have only shows 1 variable or 1 value being allowed... |
It does only return 1 variable, but C allows comma separated statements and will pass the last statement in the list to the return. It's a not very often used feature of C.
In this context it is a very odd line as it really doesn't do anything in their code.
I typically use it for macros that are meant to act as functions that return a value.
EDIT: If you look at my snprintf macro here, you will see me using comma separated statements so that my macro returns a value at the end:
https://www.ccsinfo.com/forum/viewtopic.php?t=53652 |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19520
|
|
Posted: Fri Jun 16, 2017 7:21 am |
|
|
It is an error here...
Just means he only gets the last value. |
|
|
|
|
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
|