• AVR Freaks

PIC 18F I2C / IIC Example

Author
joclegg1
New Member
  • Total Posts : 1
  • Reward points : 0
  • Joined: 2013/08/26 13:21:39
  • Location: 0
  • Status: offline
2013/08/26 13:36:44 (permalink)
5 (2)

PIC 18F I2C / IIC Example

I don't know why, but I spent ages looking for simple working I2C/IIC code something like this and couldn't find anything.
This code works and I'm posting it here so the next person to go looking finds a better starting point.


/* ***********************************************************
*
*  PIC C18 Example I2C SLAVE for PIC18F ()PIC18F45K20)
*  Author:  John Clegg
*  Date:    26 August 2013
*
*  Read/write to a bank of 8 bit register values.
*  I2C read returns the contents of the current register, multi-byte
*  reads return subsequent register bytes.
*  I2C write, the first data byte is the register address, subsequent bytes
*  are written, initially to the register address supplied, then to
*  successive registers addresses.
************************************************************* */

/** C O N F I G U R A T I O N   B I T S ******************************/

#pragma config FOSC = INTIO67, FCMEN = OFF, IESO = OFF                       // CONFIG1H
#pragma config PWRT = OFF, BOREN = SBORDIS, BORV = 30                        // CONFIG2L
#pragma config WDTEN = OFF, WDTPS = 32768                                    // CONFIG2H
#pragma config MCLRE = OFF, LPT1OSC = OFF, PBADEN = ON, CCP2MX = PORTC       // CONFIG3H
#pragma config STVREN = ON, LVP = OFF, XINST = OFF                             // CONFIG4L
#pragma config CP0 = OFF, CP1 = OFF, CP2 = OFF, CP3 = OFF                    // CONFIG5L
#pragma config CPB = OFF, CPD = OFF                                          // CONFIG5H
#pragma config WRT0 = OFF, WRT1 = OFF, WRT2 = OFF, WRT3 = OFF                // CONFIG6L
#pragma config WRTB = OFF, WRTC = OFF, WRTD = OFF                            // CONFIG6H
#pragma config EBTR0 = OFF, EBTR1 = OFF, EBTR2 = OFF, EBTR3 = OFF            // CONFIG7L
#pragma config EBTRB = OFF                                                   // CONFIG7H


/** I N C L U D E S **************************************************/
#include "p18f45k20.h"
#include "delays.h"

/** D E C L A R A T I O N S *******************************************/

#define I2C_ADDR    0xaa    // 8 bit address

typedef unsigned char   byte;
void low_isr(void);
void high_isr(void);

volatile byte           i2c_reg_addr     = 0;
volatile byte           i2c_reg_map[16 ] = {0,};
volatile byte           i2c_byte_count   = 0;

/*
* For PIC18 devices the high interrupt vector is found at
* 00000008h. The following code will branch to the
* high_interrupt_service_routine function to handle
* interrupts that occur at the high vector.
*/
#pragma code high_vector=0x08
void interrupt_at_high_vector(void)
{
_asm GOTO high_isr _endasm
}

#pragma code /* return to the default code section */
/*
* For PIC18 devices the low interrupt vector is found at
* 00000018h. The following code will branch to the
* low_interrupt_service_routine function to handle
* interrupts that occur at the low vector.
*/#pragma code low_vector=0x18
void interrupt_at_low_vector(void)
{
_asm GOTO low_isr _endasm
}

#pragma code /* return to the default code section */

void main (void)
{
    OSCCON            = 0x60;       // IRCFx = 110
    OSCTUNEbits.PLLEN = 0;          // x4 PLL disabled

    // Port D used for diagnostic LEDs
    TRISD      = 0b00111111;     // PORTD bit 7 to output (0) ; bits 6:0 are inputs (1)
    LATDbits.LATD7 = 0;             // RED LED
    LATDbits.LATD6 = 0;             // YLW LED
       
    // Setup MSSP in 7 bit I2C Slave mode
    TRISC          = 0b00011000;    // TRISC 3&4 (SCL & SDA) inputs
    LATC           = 0b00011000;
    SSPADD         = I2C_ADDR;      // Set I2C address
    SSPCON1        = 0x36;          // SSPEN: Synchronous Serial Port Enable bit - Enables the serial port and configures the SDA and SCL pins as the serial port pins
                                    // CKP: SCK Release Control bit              - Release clock
                                    // SSPM3:SSPM0: SSP Mode Select bits         - 0110 = I2C Slave mode, 7-bit address   
    SSPSTAT        = 0x00;
    SSPCON2        = 0x01;          // GCEN: General Call address (00h) (Slave mode only) 0 = General call address disabled
                                    // SEN: Start Condition Enable/Stretch Enable bit(1) ()Slave mode) 1 = Clock stretching is enabled
    PIR1bits.SSPIF = 0;             // Clear MSSP interrupt request flag
    PIE1bits.SSPIE = 1;             // Enable MSSP interrupt enable bit
    INTCONbits.GIE_GIEH  = 1;       // GIE/GIEH: Global Interrupt Enable bit
    INTCONbits.PEIE_GIEL = 1;       // PEIE/GIEL: Peripheral Interrupt Enable bit

    while (1)
    {
        Delay1KTCYx(50);    // Delay 50 x 1000 = 50,000 cycles; 200ms @ 1MHz
    }   
}

#pragma interruptlow low_isr
void low_isr (void)
{
}

#pragma interruptlow high_isr
void high_isr (void)
{
    byte    sspBuf;
   
    if (PIR1bits.SSPIF) {
       
        if (!SSPSTATbits.D_NOT_A) {
            //
            // Slave Address
            //
            i2c_byte_count = 0;

            if (SSPSTATbits.BF) {
                // Discard slave address
                sspBuf = SSPBUF;    // Clear BF
            }
           
            if (SSPSTATbits.R_NOT_W) {               
                // Reading - read from register map
                SSPCON1bits.WCOL = 0;
                SSPBUF           = i2c_reg_map[i2c_reg_addr++];
            }
           
        } else {
            //
            // Data bytes
            //
            i2c_byte_count++;

            if (SSPSTATbits.BF) {
                sspBuf = SSPBUF;    // Clear BF
            }

            if (SSPSTATbits.R_NOT_W) {

                // Multi-byte read - advance to next address
                SSPCON1bits.WCOL = 0;
                SSPBUF           = i2c_reg_map[i2c_reg_addr++];
                LATDbits.LATD6 = 1;
               
            } else {               

                if (i2c_byte_count == 1) {
                    // First write byte is register address
                    i2c_reg_addr = sspBuf;

                } else {
                    // Write to register address - auto advance
                    //   to allow multiple bytes to be written
                    i2c_reg_map[i2c_reg_addr++] = sspBuf;
                }
            }
        }
        // Limit address to size of register map
        i2c_reg_addr %= sizeof(i2c_reg_map);
       
        // Finally
        PIR1bits.SSPIF  = 0;            // Clear MSSP interrupt flag
        SSPCON1bits.CKP = 1;            // Release clock       
    }   
}

#1

15 Replies Related Threads

    dowonderatwill
    Starting Member
    • Total Posts : 40
    • Reward points : 0
    • Joined: 2013/07/22 20:12:51
    • Location: 0
    • Status: offline
    Re:PIC 18F I2C / IIC Example 2013/09/08 19:11:53 (permalink)
    -2 (1)
    Hi
    I am trying to communicate with AQM0802A LCD slave device with PIC16F877A.
    I followed the code given in the pdf of microchip tutorial for I2C.
    The very initial stage of the code:
    --
    I2CWrite
     BANKSEL SSPCON2
     BSF  SSPCON2, SEN
     CALL  WaitMSSP
    --
    hangs! The clock I am using is 10MHz. I hoped that Call WaitMSSP should come out after some time (secs) but it remains hanging there. The code of WaitMSSP is:
    ---
    WaitMSSP
     BANKSEL PIR1
     BTFSS PIR1, SSPIF
     GOTO $-1
     BCF  PIR1,SSPIF
     RETLW 0
    ---
    Does anyone knows why it happens and how can I solve it?
    I have observed BSF SSPCON2, SEN (In I2CWrite above) does not set it 1. But I understand that it is reset by the hardware so, I guess that is not the problem. Possibility of the problem is in WaitMSSP !!
    ---- The initial piece of code----
    LIST  P=PIC16F877A
    INCLUDE P16F877A.INC
    __CONFIG _CP_OFF & _WDT_OFF & _HS_OSC & _PWRTE_ON & _LVP_OFF & _CPD_OFF & _BODEN_OFF
    ORG 0
    ; *** Setup I/O ***
    CLRF PORTB
    BANKSEL TRISC
    MOVLW B'00011000' ;RC3, RC4 are inputs and others are outputs.
    MOVWF TRISC
    CLRF TRISB  ; all PORTB are output mode.
    ; *** Setup Registers for I2C ***
    ; Configure MSSP module for Master Mode
    BANKSEL SSPCON
    MOVLW B'00101000' ;Enables MSSP and set PORTC pins for I2C.
    MOVWF SSPCON 
    ; Input Levels and slew rate as I2C Standard Levels
    BANKSEL SSPSTAT
    MOVLW B'10000000'
    MOVWF SSPSTAT
    ; Configure Baud Rate
    BANKSEL SSPADD
    MOVLW (FOSC / (4*BAUD)) -1
    MOVWF SSPADD
    ; *** Begin I2C Data Transfer Sequences ***
    ; Set StartCondtion ( *Start* -> Address -> ControlByte -> Command  )
    I2CWrite
     BANKSEL SSPCON2
     BSF  SSPCON2, SEN
     CALL  WaitMSSP
    ------------------------------------
     
    Thank you.
     
     
     
     
     
    #2
    WaltR
    Super Member
    • Total Posts : 3759
    • Reward points : 0
    • Joined: 2003/11/07 12:38:21
    • Status: offline
    Re:PIC 18F I2C / IIC Example 2013/09/09 05:55:02 (permalink)
    +4 (2)
    CALL  WaitMSSP
    What does this function do????
    Code????????
     
    Also, you hijacked a thread that deals with I2C Slave code which your problem has nothing to do with. Did you search the forums here? There has been many, many threads on using I2C on 16F PICs.
    #3
    katela
    Super Member
    • Total Posts : 1385
    • Reward points : 0
    • Joined: 2013/06/11 05:25:18
    • Location: South Africa
    • Status: offline
    Re:PIC 18F I2C / IIC Example 2013/09/09 06:42:20 (permalink)
    0
    This article as well will help somebody for the PIC 18F I2C / IIC Example:
     PIC Microcontroller Communication with I²C Bus.
    post edited by katela - 2018/08/22 16:27:52

    Free online Microcontroller Tutorials and Projects for Hobbyists and students. From beginners to advanced. Website: www.studentcompanion.co.za
    YouTube Tutorials: https://www.youtube.com/StudentCompanionSA
    #4
    dowonderatwill
    Starting Member
    • Total Posts : 40
    • Reward points : 0
    • Joined: 2013/07/22 20:12:51
    • Location: 0
    • Status: offline
    Re:PIC 18F I2C / IIC Example 2013/09/09 18:30:10 (permalink)
    0
    Actually I had searched but could not find info. Will search again and will post over there.
    Btw the code of WaitMSSP is:
    -----
    WaitMSSP
     BANKSEL PIR1
     BTFSS PIR1, SSPIF
     GOTO $-1
     BCF  PIR1,SSPIF
     RETLW 0
    -------
    #5
    dougsisco
    New Member
    • Total Posts : 1
    • Reward points : 0
    • Joined: 2013/12/23 11:20:59
    • Location: 0
    • Status: offline
    Re:PIC 18F I2C / IIC Example 2013/12/23 11:38:10 (permalink)
    +2 (1)
    John, just want to give you a big THANKS for posting your PIC18 I2C slave code.
    I've been struggling with the MicoTech library routines using http://electronics.stacke...ing-with-i2c-on-pic18s and they just don't work.  I'm interfacing a PIC182455 with the Beaglebone and your code works perfectly!
    #6
    ACCORDEON
    New Member
    • Total Posts : 9
    • Reward points : 0
    • Joined: 2012/07/30 05:48:35
    • Location: Olen-BELGIUM
    • Status: offline
    Re:PIC 18F I2C / IIC Example 2013/12/26 02:34:04 (permalink)
    0
    hi john
    i think you are missing the i2c header file;when using this file it is not necessary to reinvent the 'warm water'
    i have also a problem with an i2c i/o expander; but the code is working easyer:it is in the mplabx forum called i2c i/o expander with pic18f45k20 ; maybe it can help you using the already written code in the incude library files.
    #7
    Givi
    New Member
    • Total Posts : 15
    • Reward points : 0
    • Joined: 2014/02/18 15:26:47
    • Location: 0
    • Status: offline
    Re:PIC 18F I2C / IIC Example 2016/06/26 16:41:50 (permalink)
    0
    Hi,
    I am late to this, but this is a great example.  I been trying to get PIC 18F2525 to Communicate with RPi as slave on I2C .  However, I am not clear on below calls "high_isr _endasm" and what codes the assembly is referring to.
    I am not sure  if it is needed for the simple code I listed below. 
     
    I am only trying to count (0 to 15) from data sent from RPi to blink PortB on the PIC and send (0 to 15) back to RPi to display on the Screen.  I am using "wiringPi" compiler and code on RPi and Mikroelektronika compiler for the PIC . 
     
    If you would please expand on that Function call and assembly code.  It would help me tremendously on how does that work with the code I listed below.
     
     
    #pragma code high_vector=0x08
    void interrupt_at_high_vector(void)
    {
    _asm GOTO high_isr _endasm
    }
     
    ==============
     

    //------------------------------------------------------------------------------
    const Address = 0x14;                    // set I2C device address
    const Delay_Time = 500;               // port check delay
     
    //------------------------------------------------------------------------------
    //                      Global Processing Variables
    //------------------------------------------------------------------------------
    unsigned short junk;                      // just dummy for buffer read
    unsigned short rxbuffer;               //
    unsigned short tx_data;                //
    //------------------------------------------------------------------------------
    void init()
    {
      OSCCON =  0xFE;              //bit 6-4 IRCF2:IRCF0: Internal Oscillator Frequency Select bits   11111110
                                   //111 = 0xFE 8 MHz, EE=4 MHZ(INTOSC drives clock directly)       11011111
      OSCTUNE = 0XDF;              //OSCILATION AT 8MHZ     11000000
      ADCON1 = 0XF;                //DIGITAL I/O
      //====================
      LATA = 0X00;                  // Clear PORTA
      LATB = 0X00;                  // Clear PORTB
      LATC = 0X00;                  // Clear PORTC
      TRISA = 0xFF;                 // Set PORTA as input
      TRISB = 0x0;                  // Set PORTB as output
      TRISC = 0xFF;                 // Set PORTC as input
      //====================
      SSPADD =  Address;             // Get address (7bit). Lsb is read/write flag
      SSPCON1 = 0X36 ;               // 110110  Set to I2C slave with 7-bit Address bit <3-0> 0110 = I2C Slave mode, 7-bit address
      //bit 5 is 1 = Enables the serial port and configures the SDA and SCL pins as the serial port pins
      //bit 4 is 1 = Releases clock
      SSPCON2.GCEN = 0x80;         //10000000  bit 7 is 1 = Enables interrupt when a general call address (0000h) is received in the SSPSR.  GCEN: General Call Enable bit (Slave mode only). bit 0 is 0 = Clock stretching is disabled
      SSPSTAT.SMP = 0x01;          //bit 7 is 1 SMP: Slew Rate Control bit. Slew rate control disabled for Standard Speed mode (100 kHz)
      SSPSTAT.CKE = 0x01;          //bit 6 is 1 = Enable SMBus specific inputsCKE: SMBus Select bit
      //SSPSR)
      PIE1.SSPIE = 0x01;           // bit 3 is 1 = Enables the MSSP interrupt. SSPIE: Master Synchronous Serial Port Interrupt Enable bit
      //INTCON = 0xC0;             // enable INTCON.GIE
      I2C1_Init(100000);
      I2C1_Start();                // issue I2C start signal
     #if (Mode==7)
      SSPCON1=6|(1<<SSPEN);
     #else
      SSPCON1=7|(1<<SSPEN);
     #endif
     PIE1|=(1<<SSPIE);
     INTCON|=(1<<PEIE);
    }
    //------------------------------------------------------------------------------
    void  interrupt(){                      // I2C slave interrupt handler
      if (PIR1.SSPIE == 1){                // I2C Interrupt
        PIR1.SSPIF = 0;                    // reset SSP interrupt flag
        //transmit data to master
        if (SSPSTAT.R_W == 1){             // Read request from master
          SSPBUF = tx_data;                // Get data to send
          SSPCON1.CKP = 1;                 // Release SCL line
          junk = SSPBUF;                   // That's it
          return;
        }
        if (SSPSTAT.BF == 0){              // all done,
          junk = SSPBUF;                   // Nothing in buffer so exit
          return;
        }
        //recieve data from master
        if (SSPSTAT.D_A == 1){             // Data [not address]
          rxbuffer = SSPBUF;               // get data
          junk = SSPBUF;                   // read buffer to clear flag [address]
          SSPCON1.SSPOV = 0 ;              //MY ADDITION: bit 6 SSPOV: Receive Overflow Indicator bit
          return;
        }
      }
      junk = SSPBUF;                        // read buffer to clear flag [address]
    }
    //------------------------------------------------------------------------------
    void main()
    {//MAIN START
         unsigned int i;
         unsigned short take;
      init();
      //Delay_ms(Delay_Time);
       TRISC.b0 = 0;
       TRISC.b1 = 0;
       TRISC.b2 = 0;
     
     
      while(1)
      {//WHILE START
      //I2C1_Start();              // issue I2C start signal
    //-----    recieve data from master---------
               LATB = 0x0 ;
              for(i=0; i<=0xF; i++)
              {//FOR-1
                LATC.b0 = 1;   //To indicate what segment of the code turn this LED on
            //Delay_ms(Delay_Time);
                LATB = rxbuffer;
            Delay_ms(Delay_Time);
            Delay_ms(Delay_Time);
            I2C1_Stop();               // issue I2C stop signal
                LATC.b1 = 1;          //To indicate what segment of the code turn this LED on
                LATB = 0;
                I2C1_Repeated_Start();     // issue I2C signal repeated start Mikroeektronika library call
            take = I2C1_Rd(0u);        //receive data from master Mikroeektronika library call
            LATB = take;
            Delay_ms(Delay_Time);
            Delay_ms(Delay_Time);
                    LATC.b0 = 0;
                    LATC.b1 = 0;
                   // LATB = 0x0;
                    }//FOR-1
     //-----    Send data to master---------
               for(i=0; i<=15; i++)
                {//FOR-2
                    LATC.b2 = 1;    //To indicate what segment of the code turn this LED on
            tx_data = 0xAA;
            I2C1_Stop();               // issue I2C stop signal Mikroeektronika library call
            Delay_ms(Delay_Time);
            Delay_ms(Delay_Time);
            LATC.b0 = 1;               //To indicate what segment of the code turn this LED on
            I2C1_Repeated_Start();     // issue I2C signal repeated start Mikroeektronika library call
            I2C1_Wr(0x14 + 0xA);
            Delay_ms(Delay_Time);
            I2C1_Wr(0x15 + R);         // Mikroeektronika library call
            LATC.b2 = 0;
            LATC.b0 = 0;
                       }//FOR-2
    }
    }
    #8
    oliverb
    Super Member
    • Total Posts : 206
    • Reward points : 0
    • Joined: 2009/02/16 13:12:38
    • Location: 0
    • Status: offline
    Re:PIC 18F I2C / IIC Example 2016/09/15 07:39:46 (permalink)
    0
    OK I'm interested too, not certain which PIC I'll need to use though but I'll probably want to try a 18F4520 as I have an old dev board that only supports older 5V parts?
     
    I see the header mentions C18, has anyone compiled it with XC8 free?
     
    Has anyone profiled how long the ISR takes to respond.
     
    Also is there a migration guide for I2C as there seem to be some odd subtle differences between PIC series, for example in how ACK is handled during a slave read. That's not counting the extensions such as address masking but I think I can ignore those if I am not using them?
    post edited by oliverb - 2016/09/15 10:11:41
    #9
    oliverb
    Super Member
    • Total Posts : 206
    • Reward points : 0
    • Joined: 2009/02/16 13:12:38
    • Location: 0
    • Status: offline
    Re:PIC 18F I2C / IIC Example 2016/09/16 04:41:24 (permalink)
    0
    I changed the declarations a bit and now it compiles in XC8
    Sorry about the commenting but I haven't got round to removing the previous configuration.
    Target is a PIC18F4520 with 8MHz external crystal. SCL and SDA have 10K pull up resistors and also monitoring LEDs which MUST be disconnected or communication fails
    /*
     * File: main.c
     * Author: OliverB
     *
     * Created on 15 September 2016, 16:05
     */


    #include <xc.h>
     /* ***********************************************************
    *
    * PIC C18 Example I2C SLAVE for PIC18F ()PIC18F45K20)
    * Author: John Clegg
    * Date: 26 August 2013
    *
    * Read/write to a bank of 8 bit register values.
    * I2C read returns the contents of the current register, multi-byte
    * reads return subsequent register bytes.
    * I2C write, the first data byte is the register address, subsequent bytes
    * are written, initially to the register address supplied, then to
    * successive registers addresses.
    ************************************************************* */

    // #pragma config statements should precede project file includes.
    // Use project enums instead of #define for ON and OFF.

    // CONFIG1H
    #pragma config OSC = HS // Oscillator Selection bits (HS oscillator)
    #pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor disabled)
    #pragma config IESO = OFF // Internal/External Oscillator Switchover bit (Oscillator Switchover mode disabled)

    // CONFIG2L
    #pragma config PWRT = ON // Power-up Timer Enable bit (PWRT enabled)
    #pragma config BOREN = SBORDIS // Brown-out Reset Enable bits (Brown-out Reset enabled in hardware only (SBOREN is disabled))
    #pragma config BORV = 2 // Brown Out Reset Voltage bits ()

    // CONFIG2H
    #pragma config WDT = OFF // Watchdog Timer Enable bit (WDT disabled (control is placed on the SWDTEN bit))
    #pragma config WDTPS = 32768 // Watchdog Timer Postscale Select bits (1:32768)

    // CONFIG3H
    #pragma config CCP2MX = PORTC // CCP2 MUX bit (CCP2 input/output is multiplexed with RC1)
    #pragma config PBADEN = ON // PORTB A/D Enable bit (PORTB<4:0> pins are configured as analog input channels on Reset)
    #pragma config LPT1OSC = OFF // Low-Power Timer1 Oscillator Enable bit (Timer1 configured for higher power operation)
    #pragma config MCLRE = OFF // MCLR Pin Enable bit (RE3 input pin enabled; MCLR disabled)

    // CONFIG4L
    #pragma config STVREN = ON // Stack Full/Underflow Reset Enable bit (Stack full/underflow will cause Reset)
    #pragma config LVP = OFF // Single-Supply ICSP Enable bit (Single-Supply ICSP disabled)
    #pragma config XINST = OFF // Extended Instruction Set Enable bit (Instruction set extension and Indexed Addressing mode disabled (Legacy mode))

    // CONFIG5L
    #pragma config CP0 = OFF // Code Protection bit (Block 0 (000800-001FFFh) not code-protected)
    #pragma config CP1 = OFF // Code Protection bit (Block 1 (002000-003FFFh) not code-protected)
    #pragma config CP2 = OFF // Code Protection bit (Block 2 (004000-005FFFh) not code-protected)
    #pragma config CP3 = OFF // Code Protection bit (Block 3 (006000-007FFFh) not code-protected)

    // CONFIG5H
    #pragma config CPB = OFF // Boot Block Code Protection bit (Boot block (000000-0007FFh) not code-protected)
    #pragma config CPD = OFF // Data EEPROM Code Protection bit (Data EEPROM not code-protected)

    // CONFIG6L
    #pragma config WRT0 = OFF // Write Protection bit (Block 0 (000800-001FFFh) not write-protected)
    #pragma config WRT1 = OFF // Write Protection bit (Block 1 (002000-003FFFh) not write-protected)
    #pragma config WRT2 = OFF // Write Protection bit (Block 2 (004000-005FFFh) not write-protected)
    #pragma config WRT3 = OFF // Write Protection bit (Block 3 (006000-007FFFh) not write-protected)

    // CONFIG6H
    #pragma config WRTC = OFF // Configuration Register Write Protection bit (Configuration registers (300000-3000FFh) not write-protected)
    #pragma config WRTB = OFF // Boot Block Write Protection bit (Boot block (000000-0007FFh) not write-protected)
    #pragma config WRTD = OFF // Data EEPROM Write Protection bit (Data EEPROM not write-protected)

    // CONFIG7L
    #pragma config EBTR0 = OFF // Table Read Protection bit (Block 0 (000800-001FFFh) not protected from table reads executed in other blocks)
    #pragma config EBTR1 = OFF // Table Read Protection bit (Block 1 (002000-003FFFh) not protected from table reads executed in other blocks)
    #pragma config EBTR2 = OFF // Table Read Protection bit (Block 2 (004000-005FFFh) not protected from table reads executed in other blocks)
    #pragma config EBTR3 = OFF // Table Read Protection bit (Block 3 (006000-007FFFh) not protected from table reads executed in other blocks)

    // CONFIG7H
    #pragma config EBTRB = OFF // Boot Block Table Read Protection bit (Boot block (000000-0007FFh) not protected from table reads executed in other blocks)

    /** C O N F I G U R A T I O N B I T S ******************************/
    /*
    #pragma config FOSC = INTIO67, FCMEN = OFF, IESO = OFF // CONFIG1H
    #pragma config PWRT = OFF, BOREN = SBORDIS, BORV = 30 // CONFIG2L
    #pragma config WDTEN = OFF, WDTPS = 32768 // CONFIG2H
    #pragma config MCLRE = OFF, LPT1OSC = OFF, PBADEN = ON, CCP2MX = PORTC // CONFIG3H
    #pragma config STVREN = ON, LVP = OFF, XINST = OFF // CONFIG4L
    #pragma config CP0 = OFF, CP1 = OFF, CP2 = OFF, CP3 = OFF // CONFIG5L
    #pragma config CPB = OFF, CPD = OFF // CONFIG5H
    #pragma config WRT0 = OFF, WRT1 = OFF, WRT2 = OFF, WRT3 = OFF // CONFIG6L
    #pragma config WRTB = OFF, WRTC = OFF, WRTD = OFF // CONFIG6H
    #pragma config EBTR0 = OFF, EBTR1 = OFF, EBTR2 = OFF, EBTR3 = OFF // CONFIG7L
    #pragma config EBTRB = OFF // CONFIG7H
    */

    /** I N C L U D E S **************************************************/
    //#include "p18f45k20.h"
    //#include "delays.h"

    /** D E C L A R A T I O N S *******************************************/

    #define I2C_ADDR 0xaa // 8 bit address

    typedef unsigned char byte;
    // void low_isr(void);
    //void high_isr(void);

    volatile byte i2c_reg_addr = 0;
    volatile byte i2c_reg_map[16 ] = {0,};
    volatile byte i2c_byte_count = 0;

    /*
    * For PIC18 devices the high interrupt vector is found at
    * 00000008h. The following code will branch to the
    * high_interrupt_service_routine function to handle
    * interrupts that occur at the high vector.
    */
    /*#pragma code high_vector=0x08
    void interrupt_at_high_vector(void)
    {
    _asm GOTO high_isr _endasm
    }

    #pragma code */ /* return to the default code section */
    /*
    * For PIC18 devices the low interrupt vector is found at
    * 00000018h. The following code will branch to the
    * low_interrupt_service_routine function to handle
    * interrupts that occur at the low vector.
    *//*#pragma code low_vector=0x18
    void interrupt_at_low_vector(void)
    {
    _asm GOTO low_isr _endasm
    }

    #pragma code */ /* return to the default code section */

    void main (void)
    {
        //OSCCON = 0x60; // IRCFx = 110
        //OSCTUNEbits.PLLEN = 0; // x4 PLL disabled

        // Port D used for diagnostic LEDs
        TRISD = 0b00111111; // PORTD bit 7 to output (0) ; bits 6:0 are inputs (1)
        LATDbits.LATD7 = 0; // RED LED
        LATDbits.LATD6 = 0; // YLW LED
            
        // Setup MSSP in 7 bit I2C Slave mode
        TRISC = 0b00011000; // TRISC 3&4 (SCL & SDA) inputs
        LATC = 0b00011000;
        SSPADD = I2C_ADDR; // Set I2C address
        SSPCON1 = 0x36; // SSPEN: Synchronous Serial Port Enable bit - Enables the serial port and configures the SDA and SCL pins as the serial port pins
                                        // CKP: SCK Release Control bit - Release clock
                                        // SSPM3:SSPM0: SSP Mode Select bits - 0110 = I2C Slave mode, 7-bit address
        SSPSTAT = 0x00;
        SSPCON2 = 0x01; // GCEN: General Call address (00h) (Slave mode only) 0 = General call address disabled
                                        // SEN: Start Condition Enable/Stretch Enable bit(1) ()Slave mode) 1 = Clock stretching is enabled
        PIR1bits.SSPIF = 0; // Clear MSSP interrupt request flag
        PIE1bits.SSPIE = 1; // Enable MSSP interrupt enable bit
        INTCONbits.GIE_GIEH = 1; // GIE/GIEH: Global Interrupt Enable bit
        INTCONbits.PEIE_GIEL = 1; // PEIE/GIEL: Peripheral Interrupt Enable bit

        while (1)
        {
          __delay_ms(1);
          // Delay1KTCYx(50); // Delay 50 x 1000 = 50,000 cycles; 200ms @ 1MHz
        }
    }

    //#pragma interruptlow low_isr
    void low_priority interrupt low_isr (void)
    {
    }

    //#pragma interruptlow high_isr
    void high_priority interrupt high_isr (void)
    {
        byte ssp_Buf;
        
        if (PIR1bits.SSPIF) {
            
            if (!SSPSTATbits.D_NOT_A) {
                //
                // Slave Address
                //
                i2c_byte_count = 0;

                if (SSPSTATbits.BF) {
                    // Discard slave address
                    ssp_Buf = SSPBUF; // Clear BF
                }
                
                if (SSPSTATbits.R_NOT_W) {
                    // Reading - read from register map
                    SSPCON1bits.WCOL = 0;
                    SSPBUF = i2c_reg_map[i2c_reg_addr++];
                }
                
            } else {
                //
                // Data bytes
                //
                i2c_byte_count++;

                if (SSPSTATbits.BF) {
                    ssp_Buf = SSPBUF; // Clear BF
                }

                if (SSPSTATbits.R_NOT_W) {

                    // Multi-byte read - advance to next address
                    SSPCON1bits.WCOL = 0;
                    SSPBUF = i2c_reg_map[i2c_reg_addr++];
                    LATDbits.LATD6 = 1;
                    
                } else {

                    if (i2c_byte_count == 1) {
                        // First write byte is register address
                        i2c_reg_addr = ssp_Buf;

                    } else {
                        // Write to register address - auto advance
                        // to allow multiple bytes to be written
                        i2c_reg_map[i2c_reg_addr++] = ssp_Buf;
                    }
                }
            }
            // Limit address to size of register map
            i2c_reg_addr %= sizeof(i2c_reg_map);
            
            // Finally
            PIR1bits.SSPIF = 0; // Clear MSSP interrupt flag
            SSPCON1bits.CKP = 1; // Release clock
        }
    }



    Master is a MBED board transmitting test values and reading them back.
     
    NOTE: I've renamed the sspBuf variable to ssp_Buf to distinguish it from the SSPBUF register. I wanted to compile it in MikroC and in that compiler something odd seems to happen when a name differs only by case, it seems to treat them as equivalent and optimise out the read operation.
    post edited by oliverb - 2016/09/22 23:25:24
    #10
    oliverb
    Super Member
    • Total Posts : 206
    • Reward points : 0
    • Joined: 2009/02/16 13:12:38
    • Location: 0
    • Status: offline
    Re:PIC 18F I2C / IIC Example 2017/02/27 07:03:18 (permalink)
    0
    Follow up to the above: I'm wondering what I could do to read or write 16 bit values and avoid the problem of an I2C and main program memory access colliding.
     
    I'm thinking of just having the main program loop wait for I2C stop before accessing the buffer, crude but it should fix it. Otherwise there's the possibility of checking the index in i2c_reg_addr for collision?
    I'm liking the collision method better but it does raise the possibility of race conditions.
     
    #11
    NKurzman
    A Guy on the Net
    • Total Posts : 17720
    • Reward points : 0
    • Joined: 2008/01/16 19:33:48
    • Location: 0
    • Status: online
    Re:PIC 18F I2C / IIC Example 2017/02/27 07:33:19 (permalink)
    0
    You can real it in main.
    Or if in a interupt use a flag to indicate that the data is ready.
    #12
    MOZI
    New Member
    • Total Posts : 1
    • Reward points : 0
    • Joined: 2011/07/18 12:39:59
    • Location: 0
    • Status: offline
    Re:PIC 18F I2C / IIC Example 2017/07/13 06:14:08 (permalink)
    0
    Thanks John.  No changes required for use on 18F14K50 as a slave to Raspberry Pi 2.
    #13
    Givi
    New Member
    • Total Posts : 15
    • Reward points : 0
    • Joined: 2014/02/18 15:26:47
    • Location: 0
    • Status: offline
    Re:PIC 18F I2C / IIC Example 2018/01/18 18:19:09 (permalink)
    +1 (1)
    Guys, Thank you for your help.  Its a year later, but better late than never.  Code worked and I got my answer.Smile: Smile  
    #14
    ckelley
    New Member
    • Total Posts : 2
    • Reward points : 0
    • Joined: 2017/08/17 10:13:05
    • Location: 0
    • Status: offline
    Re:PIC 18F I2C / IIC Example 2018/04/19 09:57:46 (permalink)
    +1 (1)
    Be careful, however, when using functions (this includes float operators) inside the ISR if you use them outside. This results in possible reentrant conditions that may corrupt the interrupted function execution. I haven't inspected C18 assembly code, but XC8 is non-reentrant, non recursive. There is no stack frame. Local variable signatures are created as "function@var", so only one variable is created. If "function" is called in the ISR while it is being executed outside, the values in the variables being used will be corrupted for the interrupted call.
     
    Always declare variables used in the ISR as volatile if they are used outside the IS
     
    post edited by ckelley - 2018/04/19 10:00:55
    #15
    qɥb
    Monolothic Member
    • Total Posts : 3332
    • Reward points : 0
    • Joined: 2017/09/09 05:07:30
    • Location: Jupiter
    • Status: offline
    Re:PIC 18F I2C / IIC Example 2018/04/25 05:26:45 (permalink)
    0
    ckelley
    Be careful, however, when using functions (this includes float operators) inside the ISR if you use them outside. This results in possible reentrant conditions that may corrupt the interrupted function execution.

    Not strictly correct, see below.

    I haven't inspected C18 assembly code, but XC8 is non-reentrant, non recursive. There is no stack frame. Local variable signatures are created as "function@var", so only one variable is created.

    Agree with everything you have said here
    If "function" is called in the ISR while it is being executed outside, the values in the variables being used will be corrupted for the interrupted call.

    This is incorrect.
    XC8 detects the dual usage, and creates a second copy of every function used by ISR and non ISR code.
    This is explicitly documented in the XC8 User Guide.
     
     

    Always declare variables used in the ISR as volatile if they are used outside the ISR

    Agree

    This forum is mis-configured so it only works correctly if you access it via https protocol.
    The Microchip website links to it using http protocol. Will they ever catch on?
    PicForum "it just works"
    #16
    Jump to:
    © 2019 APG vNext Commercial Version 4.5