Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

I2C Master write problem using SSP (here's code)

Status
Not open for further replies.

Oznog

Active Member
I have a system with a PIC18F452 I2C 7-bit addr, clk stretching enabled master and a PIC18F252 I2C slave. Both use the SSP module entirely on an interrupt basis. It has been working well with the master doing reads.

I needed to add master-to-slave writes. Now the reads work ok, the master is able to complete the writes, but the slave isn't hitting the ISR for first or repeated write bytes coming in (it changes data that will show up on the next read).

I still have not been able to find a clear state machine for an interrupt based one, while AN734 looks detailed, after consulting it I really didn't find where its state descriptions and actions were comprehensive. I reverse engineered from some assembly examples, but as I said the writes are screwed, and after 2 days of trying to find out why, I'm basically sunk. Can somebody please help??

This is the master:
Code:
#include <pic18.h>
#include <stdio.h>
#include "common.h"
#include "crc.h"

#define I2C_WDT_TIMEOUT 126

#define I2CM_ReadStart     0
#define I2CM_SendReadAddr  1
#define I2CM_ReadAckTest   2
#define I2CM_ReadData      3
#define I2CM_ReadStop      4
#define I2CM_WriteStart    5
#define I2CM_SendWriteAddr 6
#define I2CM_WriteAckTest  7
#define I2CM_WriteStop     8

extern struct I2CData fieldData[FIELD_END_ENUM];
extern unsigned int  sleepTimer;
extern unsigned char changeSleepMode;

unsigned int i2cBuf[FIELD_END_ENUM];
unsigned char i2cAddr=SLAVE_I2C_ADDR;
unsigned char i2cState;
unsigned char i2cWDT=0;
unsigned char i2cProb=0;
unsigned int  i2cErrCount=0;
unsigned char i2cCRCErrCount=0;
unsigned char i2cPktCount=0;
unsigned int  crcExp;
unsigned char writePacket[5];
unsigned char writePacketSize=0;

enum Field i2cFieldIter=FIELD_START_ENUM;

void decWritePktSize(){
    writePacketSize=0;	
}

unsigned char getWritePktSize(){
    return writePacketSize;
}

unsigned char getWritePktByte(){
    return 0x33;
}

void initI2C(){
    TRISC |= 0b00011000;                 // ensure SDI and SD0 are inputs
    SSPIF = 0;                           // reset I2C based interrupt flag
    SSPCON2 = 0b00000000;                // ensure all state bits are reset
    SSPSTAT = 0b00000000;                // 
    SSPADD=24;  //400KHz
    SSPCON1= 0b00111000; //Master mode, 7 bit no S/P interrupts
	
    i2cState=I2CM_ReadStart; 
    SSPIF=0;
}

void i2cIncrWDT(){
    ++i2cWDT;
}

void i2cMasterISR(){
    static unsigned char byteCounter;
    static unsigned int buf16;
	
    SSPIF=0;
    i2cWDT=0;

    if(i2cState==I2CM_ReadStart){
        if(changeSleepMode==2){
	    changeSleepMode=1;
        } else {
	    SEN=1;
            i2cState=I2CM_SendReadAddr;
        }
    } else if(i2cState==I2CM_SendReadAddr){
	//SEN done, send addr
	//BF=0 SSPOV=0 ACKEN=0
        SSPBUF=i2cAddr | 0b00000001; //Read Req to other device
	i2cFieldIter=FIELD_START_ENUM;
	byteCounter=0;
	initCRC16();
        i2cState=I2CM_ReadAckTest;
    } else if(i2cState==I2CM_ReadAckTest && !ACKEN){
	    //Addr acknowledged, ask for Read
	    //BF=0 SSPOV=0 ACKEN=0
	RCEN=1;
        i2cState=I2CM_ReadData;
    } else if(i2cState==I2CM_ReadData){
	    //Data received
	    //BF=1 SSPOV=1 ACKEN=
        if(byteCounter==0){
	    buf16=SSPBUF;
	    ++byteCounter;
        } else {
	    buf16=(buf16<<8)|SSPBUF;

	    i2cBuf[i2cFieldIter]=buf16;

	    if(i2cFieldIter==FIELD_CRC16){
                ++i2cPktCount;
	        if(buf16!=getCRC16()){
                    //Bad CRC
	   	    i2cProb=1;
		    ++i2cCRCErrCount;
                    crcExp=getCRC16(); //crc16;
                } else {
                    //Good CRC                                   
                    enum Field i2cBufIter;
		    for(i2cBufIter=FIELD_START_ENUM;i2cBufIter<FIELD_CRC16;++i2cBufIter){	
	                fieldData[i2cBufIter].data=i2cBuf[i2cBufIter]; 
		        fieldData[i2cBufIter].new=1;
		    }
                }
            } else {
                pushCRC16(buf16);
	    }
		  
            if(i2cFieldIter==FIELD_RPM && buf16>0 && buf16!=0xffff){
	        sleepTimer=0;
	    }

	    ++i2cFieldIter;
	    byteCounter=0;
        }
        //Decide if we want to send ACK or NACK
        if(i2cFieldIter==FIELD_LAST){
	    ACKDT=1;
            i2cState=I2CM_ReadStop;
        } else {		    
	    ACKDT=0;
	    i2cState=I2CM_ReadAckTest;
        }
        ACKEN=1;
    } else if(i2cState==I2CM_ReadStop){
static char ctr=0;
        i2cProb=0;
        PEN=1;
++ctr;
if(ctr==255){
writePacketSize=1;
ctr=0;
} else {
writePacketSize=0;
}
	if( getWritePktSize()>0 ){
	    i2cState=I2CM_WriteStart;
	} else {
            i2cState=I2CM_ReadStart;
	}
    }

    else if(i2cState==I2CM_WriteStart) {
	SEN=1;
//        i2cState=I2CM_SendWriteAddr;
//    }
//    else if(i2cState==I2CM_SendWriteAddr){
	SSPBUF=i2cAddr | 0b00001;
        i2cState=I2CM_WriteAckTest;
    }

    else if(i2cState==I2CM_WriteAckTest){
//        if(!ACKSTAT){
            //WrtData
//	    if( getWritePktSize()>0 ){
//		//send_byte
		SSPBUF=getWritePktByte();
		decWritePktSize();
//	    } 
	    if(getWritePktSize()==0){
	        i2cState=I2CM_WriteStop;
	    }
//	} else {
            //ack error
//	    i2cState=I2CM_WriteStart;
//	    PEN=1;
//	}
    }
    

    else if(i2cState==I2CM_WriteStop){
//        if(!ACKSTAT){
	    //no_error
	    PEN=1;
	    i2cState=I2CM_ReadStart;
//	} else {
	    //error
	    //ack_error=1
//	    i2cState=I2CM_WriteStart;
	    //stop
//	    PEN=1;
//	}
    }
}

This is the slave:
Code:
#include <pic18.h>
#include "common.h"
#include "crc.h"

#define PIN_I2C_SCK     RC3
#define PIN_I2C_SDA     RC4

#define I2C_WDT_TIMEOUT 6

#define ALT_HISTORY_SIZE 16

//Defined in main
extern signed int altHistory[ALT_HISTORY_SIZE];
extern unsigned char altHistoryPtr;
extern struct Struct16 field16[FIELD_END_ENUM];

static unsigned char resetI2C=0;
static unsigned char i2cWDT=0;
static unsigned char i2cState=0;
static unsigned char i2cIdle=1;
static unsigned char i2cExpectStart=1;
static unsigned char i2cProb=0;
static unsigned char BF_fix=0;
static enum Field i2cFieldIter=FIELD_START_ENUM;
static unsigned char byteCounter=0;

unsigned char checkI2CSlaveIdle(){
    return i2cExpectStart;
}

void initI2C(){	
    SSPIF = 0;             // reset I2C based interrupt flag
    SSPADD=   0b10101010;
    SSPCON1=  0b00110110;  // Slave mode, 7 bit no interrupts
    SSPADD=   SLAVE_I2C_ADDR; 
    SSPCON2 = 0b00000001;  // Enable clock stretching
    SSPSTAT = 0b00000000;
    SSPIP = 0;
    SSPIE = 1;
    i2cWDT=0;
    resetI2C=0;
    i2cExpectStart=1;
    i2cProb=0;
}

void resetI2CSlave(){
    //i2cWDT=0
    if(!PIN_I2C_SCK || !PIN_I2C_SDA){
        SSPCON1=0b0;           //Disable i2c
        SSPCON1=  0b00110110;  // Slave mode, 7 bit no interrupts
        i2cExpectStart=1;
        i2cIdle=1;
        i2cProb=1;
	i2cWDT=0;
    }
}

void i2cSlaveIncrWDT(){
    if(!i2cIdle && !i2cExpectStart && i2cWDT<255){
        ++i2cWDT;
    } else {
        CLRWDT();
    }
	  
    if(i2cWDT>I2C_WDT_TIMEOUT){
        resetI2CSlave();
    }
}

void putNextByteInSSPBUF(){	
    static unsigned char nextByte;
	
    if(i2cFieldIter>=FIELD_END_ENUM){
        SSPBUF=0xfe;
        i2cFieldIter==FIELD_END_ENUM;
    } else {		    
        if(byteCounter==0){
	    if(i2cFieldIter==FIELD_CRC16){
	        field16[i2cFieldIter].val=getCRC16();
            } else {
		pushCRC16(field16[i2cFieldIter].val);
            }
	     
	    SSPBUF=field16[i2cFieldIter].val>>8;
	    nextByte=field16[i2cFieldIter].val;
            TMR1IE=1;
            CKP=1;
	    ++byteCounter;
	} else {
	    SSPBUF=nextByte;
	    CKP=1;
            ++i2cFieldIter;
	    byteCounter=0;
        }
    }
}

void initPktOut(){
    i2cWDT=0;
    i2cExpectStart=0;
    i2cFieldIter=FIELD_START_ENUM;
    byteCounter=0;
    initCRC16();
    S=0;	
}

void i2cSlaveISR(){
    static int i2cCounter=0;
    SSPIF=0;
    WCOL=0;
    SSPOV=0;

    if(S && RW && !BF){
        unsigned char thisSSPBUF;
	BF_fix=0;
	i2cIdle=0;
	    	    
	//First byte
	if(!DA){   
            initPktOut();
	} 
	     
	if(i2cExpectStart){
	    //Got an unexpected interrupt when a START is needed
            SSPBUF=0xfc;
	    CKP=1;
	    i2cProb=1;
	} else {
	    i2cWDT=0;		
            putNextByteInSSPBUF();
	}    
    }

    //Start of write, just clear addr
    else if(S && !RW && !DA && BF){
	unsigned char readBuf=SSPBUF;
        field16[FIELD_OIL_TEMP].val=0x4444;	
	SSPIF=0;
	
    //Write
    } else if(S && DA && !RW && BF){   //S && !RW && DA && BF){
	unsigned char readBuf=SSPBUF;
        field16[FIELD_OIL_TEMP].val=0x5555;  //readBuf;
        SSPIF=0;
    }

    //NACK received	    
    else if(S && DA && !RW && !(BF||BF_fix) ) { // && !i2cExpectStart){
        BF_fix=0;
	if(i2cFieldIter==FIELD_END_ENUM){
	    i2cWDT=0;
            i2cExpectStart=1;
            i2cProb=0;
	    CLRWDT();
	} else {
	    SSPEN=0;
//	    SSPIF=0;
	    SSPEN=1;
        }
	i2cFieldIter=FIELD_START_ENUM;
	byteCounter=0;
	i2cIdle=1;
        initCRC16();
	SSPIF=0;
    }      
}
 
Status
Not open for further replies.

Latest threads

Back
Top