• 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.

PIC18F25K42 a work in progress....

granddad

Active Member
It should be a pretty rudimentary I2C code with not much in terms of safety,
Had to smile.. I was once told by a 'pro' programmer always code for what could happen, not what should happen ,, I don't use 'any' libraries other than string , delay. I came to PIC from HEX machine code CDP1802, Z80 and the like My I2C master send , ACKSTAT fail , just put a red led on ! The MSSP has a few grey areas , I found the slave ACK after the 9th clock has timing issues , K42 eventually was a 'piece of cake' (ish)
 

Roze

New Member
I've now been sitting with this damn code for ages.... And I cant get it to work properly at all....
But the data received by the master is wrong.... it receives the first byte two times or 0 then the first byte. I have not gotten it to actually catch the first byte....
Any ideas?

C:
void I2C_SlaveInit(unsigned char addr){
    // 7bit Slave Mode (MODE = 0)
    I2C1CON0 = 0x00;
    //I2C1CON0bits.CSTR = 1;
   
    // Slave Address Match
    #ifndef I2C_Allow0Adress
    if (addr == 0x00){
        addr = I2C_DefaultAddress;
    }
    #endif
   
    #ifdef I2C_DoAddressShift
    addr = addr << 1;
    #endif

    I2C1ADR0 = addr;
    I2C1ADR1 = addr;
    I2C1ADR2 = addr;
    I2C1ADR3 = addr;

    // ACK for every valid byte (ACKDT = 0)
    // ACK at the end of a Read (ACKCNT = 0)
    // Clock stretching DISabled (CSTRDIS = 0)
    I2C1CON1 = 0b00000000;
   
    // Auto-count disabled (ACNT = 0)
    // General Call disabled (GCEN = 0)
    // Fast mode enabled (FME = 1)
    // ADB0 address buffer used (ADB = 0)
    // SDA Hold time of 30 ns (SDAHT = 2)
    // Bus free time of 8 I2C Clock pulses
    // (BFRET = 1)
    I2C1CON2 = 0x28;
   
    // Clear all I2C flags
    PIR3bits.I2C1IF = 0;
    I2C1PIR = 0x00;
   
    // Enable I2C interrupts
    PIE3bits.I2C1IE = 1;
    IPR3bits.I2C1IP = 1;
    // NOTE: Enable global and peripheral interrupts somewhere!
    //INTCON0bits.IPEN = 1;
    //INTCON0bits.GIEH = 1;
   
    // Enable local interrupt on ACK Sequence
    I2C1PIE = 0;
    I2C1PIEbits.ACKTIE = 1;
    //I2C1PIEbits.PCIE = 1;
    //I2C1PIEbits.RSCIE = 1;
    //I2C1PIEbits.ADRIE = 1;
   
    // Enable I2C module
    I2C1CON0bits.EN = 1;
   
    // Set the read and write position pointers to zero
   
}

/****************************************************************************
* Function:
*   void I2C_SlaveInterruptRoutine(void)
*
* Description:
*   This routine should be called when an interrupt was generated to
*   check if it was I2C-related
*
*
* Preconditions:
*   None
*
* Parameters:
*   None
*
* Returns:
*   None
*
* Remarks:
*   None
*
****************************************************************************/

void I2C_SlaveInterruptRoutine(void) {
    static char Widx = 0;
    static char Ridx = 0;
    char Temp = 0;
    // I2C2
    if (PIR3bits.I2C1IF) {
        // Clear the I2C interrupt flag
        PIR3bits.I2C1IF = 0;
       
        if (I2C1PIRbits.ACKTIF) { // ACK Sequence Interrupt Detected
            // Clear the interrupt flag
            I2C1PIRbits.ACKTIF = 0;

            // For Slave Read/Master Write
            if (!I2C1STAT0bits.R) {
                // Data Byte Received
                if (I2C1STAT0bits.D) {
                    // Read from RXB
                    if( Widx >= sizeof(I2C_BaseData)){   //Simply Receive bytes until we have reached at least the size of a package frame (2 bytes)
                        I2C_BaseData* i2cbase = (I2C_BaseData*)&inputBuffer;
                        if(i2cbase->size == Widx){    //Ensure we dont write out of buffer
                            Temp = I2C1RXB;                // Read out buffer to clear BF flag
                        }else{
                            inputBuffer[Widx++] = I2C1RXB;
                        }
                        if(i2cbase->size == Widx){  //Check if we have received all bytes
                            I2C_InputCallback(inputBuffer, Widx);   //Fire event for whole packet received
                        }  
                    }else{
                        inputBuffer[Widx++] = I2C1RXB;// Store data in buffer
                    }
                }
            } else { // For Slave Write/Master Read
                // Write to TXB
                if(Ridx == 0){
                    bufferInfo.size = sizeof(I2C_BaseData);
                    I2C_OutputCallback(&bufferInfo);                 // Call Method for populating output buffer
                }
                if(Ridx == bufferInfo.size){         // Prevent reading beyond buffer
                    I2C1TXB = 0;                    // Write dummy data to I2C
                }else{
                    I2C1TXB = bufferInfo.buffer[Ridx++];  // Write next data to I2C
                }
            }

        }
       
        // STOP or RESTART Interrupt Detected
        if ((I2C1PIRbits.PCIF) || (I2C1PIRbits.RSCIF)) {
            // Clear whichever interrupt flag was set
            if (I2C1PIRbits.PCIF)
                I2C1PIRbits.PCIF = 0;
            if (I2C1PIRbits.RSCIF)
                I2C1PIRbits.RSCIF = 0;

            Widx = 0;
           Ridx = 0;
        }
       
        // Reset both interrupt and error flags
        I2C1PIR = 0;
        I2C1ERR = 0;

         // Release SCL
        I2C1CON0bits.CSTR = 0;
    }
}
 
Last edited:

granddad

Active Member
No sure how i would approach this , you need to capture the bit stream when I started I2C i bought a bus pirate and learned a lot from what that showed . i don't know why but double could be clearing the flag too early ? Are you able to debug at any point ..
PIR3bits.I2C1IF = 0;
 

gophert

Well-Known Member
Most Helpful Member
For about $10 you could get a small arduino to act as an I2C device that can report, through serial connection, what it is sending or receiving in real time (or one instruction cycle delayed). You'll need the Arduino IDE and a USB cable
 

granddad

Active Member
10/10 Roze for perseverance... I2C is very much a learning curve :banghead: then :D then possibly back to :banghead: . I am a great fan of PIC ... but they do try your patience a lot..
 

granddad

Active Member
I see there have been 5000 odd views , so may be worth it ... I've copied Roze's slave code for one... :)
 

EE World Online Articles

Loading

 
Top