i2cmplabpic32real-time-clock

Can't get I2C to work with PIC32MX270F256B


I'm sorry to bother you guys but I've been spending hours messing with that PIC and I can't get anything from the I2C1 port ! I guess I must be doing something terribly wrong, or I forgot to switch something ON on the configuration bits but I just can't figure out what...

The goal of the code is to read a real time clock (PCF8583) that's connected in I2C. I wrote the library for an 8 bits PIC18F45K50 and it works fine, and now I want to move it to a PIC32MX. That's my very first project on a PIC32.

Here's my configuration (MCC generated) :

#pragma config PMDL1WAY = ON    // Peripheral Module Disable Configuration->Allow only one reconfiguration
#pragma config IOL1WAY = ON    // Peripheral Pin Select Configuration->Allow only one reconfiguration
#pragma config FUSBIDIO = ON    // USB USID Selection->Controlled by the USB Module
#pragma config FVBUSONIO = ON    // USB VBUS ON Selection->Controlled by USB Module

// DEVCFG2
#pragma config FPLLIDIV = DIV_2    // PLL Input Divider->2x Divider
#pragma config FPLLMUL = MUL_20    // PLL Multiplier->20x Multiplier
#pragma config UPLLIDIV = DIV_12    // USB PLL Input Divider->12x Divider
#pragma config UPLLEN = OFF    // USB PLL Enable->Disabled and Bypassed
#pragma config FPLLODIV = DIV_2    // System PLL Output Clock Divider->PLL Divide by 2

// DEVCFG1
#pragma config FNOSC = FRCPLL    // Oscillator Selection Bits->Fast RC Osc with PLL
#pragma config FSOSCEN = ON    // Secondary Oscillator Enable->Enabled
#pragma config IESO = ON    // Internal/External Switch Over->Enabled
#pragma config POSCMOD = OFF    // Primary Oscillator Configuration->Primary osc disabled
#pragma config OSCIOFNC = OFF    // CLKO Output Signal Active on the OSCO Pin->Disabled
#pragma config FPBDIV = DIV_4    // Peripheral Clock Divisor->Pb_Clk is Sys_Clk/4
#pragma config FCKSM = CSDCMD    // Clock Switching and Monitor Selection->Clock Switch Disable, FSCM Disabled
#pragma config WDTPS = PS1048576    // Watchdog Timer Postscaler->1:1048576
#pragma config WINDIS = OFF    // Watchdog Timer Window Enable->Watchdog Timer is in Non-Window Mode
#pragma config FWDTEN = OFF    // Watchdog Timer Enable->WDT Disabled (SWDTEN Bit Controls)
#pragma config FWDTWINSZ = WINSZ_25    // Watchdog Timer Window Size->Window Size is 25%

// DEVCFG0
#pragma config DEBUG = OFF    // Background Debugger Enable->Debugger is Disabled
#pragma config JTAGEN = ON    // JTAG Enable->JTAG Port Enabled
#pragma config ICESEL = ICS_PGx1    // ICE/ICD Comm Channel Select->Communicate on PGEC1/PGED1
#pragma config PWP = OFF    // Program Flash Write Protect->Disable
#pragma config BWP = OFF    // Boot Flash Write Protect bit->Protection Disabled
#pragma config CP = OFF    // Code Protect->Protection Disabled

#include "mcc.h"

/**
  @Summary
    Indicates the exception cause. 

  @Description
    This array identifies the cause for exception.
 */

static char *cause[] = 
{
    "Interrupt", "Undefined", "Undefined", "Undefined",
    "Load/fetch address error", "Store address error",
    "Instruction bus error", "Data bus error", "Syscall", 
    "Breakpoint", "Reserved instruction", "Coprocessor unusable", 
    "Arithmetic overflow", "Trap", "Reserved", "Reserved", 
    "Reserved", "Reserved", "Reserved"
};

void SYSTEM_Initialize(void)
{
    PIN_MANAGER_Initialize();
    OSCILLATOR_Initialize();
    SPI1_Initialize();
    I2C1_Initialize();
    INTERRUPT_Initialize();

}

void SYSTEM_RegUnlock(void)
{
    SYSKEY = 0x12345678;
    SYSKEY = 0xAA996655;
    SYSKEY = 0x556699AA;
}

void SYSTEM_RegLock(void)
{
    SYSKEY = 0x00000000; 
}

void OSCILLATOR_Initialize(void)
{
    SYSTEM_RegUnlock(); 
    // CF no clock failure; COSC FRCPLL; PLLODIV DIV_2; PBDIVRDY disabled; SLOCK out of lock; FRCDIV FRC/256; SLPEN Idle on WAIT instruction; NOSC FRCPLL; PLLMULT MUL_20; SOSCEN disabled; PBDIV DIV_4; CLKLOCK unlocked; OSWEN Switch is Complete; SOSCRDY disabled; 
    OSCCON = 0xF151100;
    SYSTEM_RegLock();
    // TUN Center Frequency; 
    OSCTUN = 0x0;
    // DIVSWEN disabled; RSLP disabled; ACTIVE Active; ROSEL SYSCLK; OE Not Driven out on REFCLKO pin; SIDL disabled; RODIV 0; ON disabled; 
    REFOCON = 0x100;
    // ROTRIM 0; 
    REFOTRIM = 0x0;
}

void _general_exception_handler ()
{
    /* Mask off the ExcCode Field from the Cause Register
    Refer to the MIPs Software User's manual */
    uint8_t _excep_code;
    uint8_t _excep_addr;
    uint8_t *_cause_str;
    _excep_code = (_CP0_GET_CAUSE() & 0x0000007C) >> 2;
    _excep_addr = _CP0_GET_EPC();
    _cause_str  = cause[_excep_code];

    while(1)
    {
      ;
    }
}

Main.c is here (simplified) :

#include "mcc_generated_files/mcc.h"
#include <stdio.h>
#include "Nokia5510.h"
#include "PCF8583.h"

int main(void)
{
    // initialize the device
    SYSTEM_Initialize();

    while (1)
    {
        // Read values from PCF8583
        unsigned char buf;
        MasterStart();
        MasterAddress(PCF8583_ADDRESS, 0);  // WRITE
        // Start writing at the PCF8583_100S_REG address
        buf = PCF8583_100S_REG;
        MasterWriteData(1, &buf);
        MasterStart();
        MasterAddress(PCF8583_ADDRESS, 1);  // READ
        MasterReadData(size, data);
        MasterStop();  
    }
}

Functions for the PCF8583 are defined here in PCF8583.c:

#include "PCF8583.h"

void myDelay()
{
    int i;
    for (i=0; i<1000; i++)
    {
        Nop();
    }
}

void extractWeekday(char _data, char* str)
{
    // Extract the 3 MSBs
    _data = _data>>5;
    if(_data == SUNDAY)
        sprintf(str, "Sunday");
    else if(_data == MONDAY)
            sprintf(str, "Monday");
    else if(_data == TUESDAY)
            sprintf(str, "Tuesday");
    else if(_data == WEDNESDAY)
            sprintf(str, "Wednesday");
    else if(_data == THURSDAY)
            sprintf(str, "Thursday");
    else if(_data == FRIDAY)
            sprintf(str, "Friday");
    else if(_data == SATURDAY)
            sprintf(str, "Saturday");
}

void MasterStart()
{
    I2C1CONbits.ON = 1;
    // Generate a START condition by setting Start Enable bit
    I2C1CONbits.SEN = 1;
    // Wait for START to be completed
    while(IFS1bits.I2C1MIF);
    // Clear flag
    IFS1bits.I2C1MIF = 0;
}

void MasterAddress(uint8_t _address, bool _RnW)
{
    // Load the address + RW byte in SSP1BUF
    // READ -> LSB=1 ; WRITE -> LSB=0
    if (_RnW)
        I2C1TRN = (_address<<1)+1;
    else
        I2C1TRN = _address<<1;
    // Wait for ack
    while (I2C1STATbits.ACKSTAT);
    // Wait for MSSP interrupt
    while (!IFS1bits.I2C1MIF);
    // Clear flag
    IFS1bits.I2C1MIF = 0;
}

void MasterWriteData(char _size, char* _data)
{
    int i;
    for (i=0; i<_size; i++)
    {
        // Load data in SSP1BUF
        I2C1TRN = *(_data+i);
        // Wait for ack
        while (I2C1STATbits.ACKSTAT);
        // Wait for MSSP interrupt
        while (!IFS1bits.I2C1MIF);
        // Clear flag
        IFS1bits.I2C1MIF = 0;
    }
}

void MasterReadData(char _size, char* _data)
{
    int i;
    for (i=0; i<size; i++)
    {
        // Enable reception
        I2C1CONbits.RCEN = 1;
        // Wait for MSSP interrupt flag and for the rx buffer to be full
        while(!IFS1bits.I2C1MIF && !I2C1STATbits.RBF);
        // Clear flag
        IFS1bits.I2C1MIF = 0;
        // Read the received byte
        *(_data+i) = I2C1RCV;
        // Clear BF to let MSSP know that we've read the buffer
        I2C1STATbits.RBF = 0;
        // Set ACK/NACK value to be sent slave
        if (i != (size-1))
            I2C1CONbits.ACKDT = 0; // ACK
        else
            I2C1CONbits.ACKDT = 1; // NACK for the last byte
        // Initiate the ACK/NACK
        I2C1CONbits.ACKEN = 1;
        // Wait for ACK/NACK to be completed -> INT
        while(!IFS1bits.I2C1MIF);    
        // Clear flag
        IFS1bits.I2C1MIF = 0;    
    }
}

void MasterStop()
{
    // Generate a STOP condition
    I2C1CONbits.PEN = 1;
    // Wait for STOP to be completed
    while(!IFS1bits.I2C1MIF);
    // Clear flag
    IFS1bits.I2C1MIF = 0;
}

void PCF8583_init(char* _data)
{
    *(_data) = CURRENT_100s;
    *(_data+1) = CURRENT_SEC;
    *(_data+2) = CURRENT_MIN;
    *(_data+3) = CURRENT_HOURS;
    *(_data+4) = CURRENT_DATE | (((CURRENT_YEAR-2016)%4)<<6);
    *(_data+5) = CURRENT_MONTH | (CURRENT_WEEKDAY<<5);
    *(_data+6) = CURRENT_TIMER;

    // Start the counter
    unsigned char buf;
    MasterStart();
    MasterAddress(PCF8583_ADDRESS, 0);  // WRITE
    // Register address in PCF8583
    buf = PCF8583_CTRL_STATUS_REG;
    MasterWriteData(1, &buf);
    // Value PCF8583_START_COUNTING to make it start
    buf = PCF8583_START_COUNTING;
    MasterWriteData(1, &buf);
    MasterStop();
    myDelay();

    // Set the date/month/time/...
    MasterStart();
    MasterAddress(PCF8583_ADDRESS, 0);  // WRITE
    // Start writing at the PCF8583_100S_REG address
    buf = PCF8583_100S_REG;
    MasterWriteData(1, &buf);
    // Shove the datas in it
    MasterWriteData(size, _data);
    MasterStop();
    myDelay();
}

And the corresponding header PCF8583.h :

#include "PCF8583.h"

void myDelay()
{
    int i;
    for (i=0; i<1000; i++)
    {
        Nop();
    }
}

void extractWeekday(char _data, char* str)
{
    // Extract the 3 MSBs
    _data = _data>>5;
    if(_data == SUNDAY)
        sprintf(str, "Sunday");
    else if(_data == MONDAY)
            sprintf(str, "Monday");
    else if(_data == TUESDAY)
            sprintf(str, "Tuesday");
    else if(_data == WEDNESDAY)
            sprintf(str, "Wednesday");
    else if(_data == THURSDAY)
            sprintf(str, "Thursday");
    else if(_data == FRIDAY)
            sprintf(str, "Friday");
    else if(_data == SATURDAY)
            sprintf(str, "Saturday");
}

void MasterStart()
{
    I2C1CONbits.ON = 1;
    // Generate a START condition by setting Start Enable bit
    I2C1CONbits.SEN = 1;
    // Wait for START to be completed
    while(IFS1bits.I2C1MIF);
    // Clear flag
    IFS1bits.I2C1MIF = 0;
}

void MasterAddress(uint8_t _address, bool _RnW)
{
    // Load the address + RW byte in SSP1BUF
    // READ -> LSB=1 ; WRITE -> LSB=0
    if (_RnW)
        I2C1TRN = (_address<<1)+1;
    else
        I2C1TRN = _address<<1;
    // Wait for ack
    while (I2C1STATbits.ACKSTAT);
    // Wait for MSSP interrupt
    while (!IFS1bits.I2C1MIF);
    // Clear flag
    IFS1bits.I2C1MIF = 0;
}

void MasterWriteData(char _size, char* _data)
{
    int i;
    for (i=0; i<_size; i++)
    {
        // Load data in SSP1BUF
        I2C1TRN = *(_data+i);
        // Wait for ack
        while (I2C1STATbits.ACKSTAT);
        // Wait for MSSP interrupt
        while (!IFS1bits.I2C1MIF);
        // Clear flag
        IFS1bits.I2C1MIF = 0;
    }
}

void MasterReadData(char _size, char* _data)
{
    int i;
    for (i=0; i<size; i++)
    {
        // Enable reception
        I2C1CONbits.RCEN = 1;
        // Wait for MSSP interrupt flag and for the rx buffer to be full
        while(!IFS1bits.I2C1MIF && !I2C1STATbits.RBF);
        // Clear flag
        IFS1bits.I2C1MIF = 0;
        // Read the received byte
        *(_data+i) = I2C1RCV;
        // Clear BF to let MSSP know that we've read the buffer
        I2C1STATbits.RBF = 0;
        // Set ACK/NACK value to be sent slave
        if (i != (size-1))
            I2C1CONbits.ACKDT = 0; // ACK
        else
            I2C1CONbits.ACKDT = 1; // NACK for the last byte
        // Initiate the ACK/NACK
        I2C1CONbits.ACKEN = 1;
        // Wait for ACK/NACK to be completed -> INT
        while(!IFS1bits.I2C1MIF);    
        // Clear flag
        IFS1bits.I2C1MIF = 0;    
    }
}

void MasterStop()
{
    // Generate a STOP condition
    I2C1CONbits.PEN = 1;
    // Wait for STOP to be completed
    while(!IFS1bits.I2C1MIF);
    // Clear flag
    IFS1bits.I2C1MIF = 0;
}

void PCF8583_init(char* _data)
{
    *(_data) = CURRENT_100s;
    *(_data+1) = CURRENT_SEC;
    *(_data+2) = CURRENT_MIN;
    *(_data+3) = CURRENT_HOURS;
    *(_data+4) = CURRENT_DATE | (((CURRENT_YEAR-2016)%4)<<6);
    *(_data+5) = CURRENT_MONTH | (CURRENT_WEEKDAY<<5);
    *(_data+6) = CURRENT_TIMER;

    // Start the counter
    unsigned char buf;
    MasterStart();
    MasterAddress(PCF8583_ADDRESS, 0);  // WRITE
    // Register address in PCF8583
    buf = PCF8583_CTRL_STATUS_REG;
    MasterWriteData(1, &buf);
    // Value PCF8583_START_COUNTING to make it start
    buf = PCF8583_START_COUNTING;
    MasterWriteData(1, &buf);
    MasterStop();
    myDelay();

    // Set the date/month/time/...
    MasterStart();
    MasterAddress(PCF8583_ADDRESS, 0);  // WRITE
    // Start writing at the PCF8583_100S_REG address
    buf = PCF8583_100S_REG;
    MasterWriteData(1, &buf);
    // Shove the datas in it
    MasterWriteData(size, _data);
    MasterStop();
    myDelay();
}

The result is : nothing ever happens on SCL1/SDA1 ! I've added some 2k pullups and monitor it with a logic analyser. I start the logic analyser and then start the circuit, to be able to monitor what the PIC is doing at startup.

Any help or clue would be reeeally appreciated !

Thanx in advance !

Best regards.

Eric


Solution

  • Is there a reason you have JTAG on in the config bits? If you're using a regular PICKIT3 etc. you probably don't need then JTAG on. Also, I didn't look real hard, but did you turn off the ANALOG pin functions? Both of these are things that will make Digital IO just not work from the get go.

    AD1PCFGbits.PCFG = 0xFFFF;