0

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

ricothebrol
  • 81
  • 12

1 Answers1

1

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;

blsmit5728
  • 434
  • 3
  • 11
  • Hello, thank you very much for your answer. JTAG was the issue indeed ! Now I can get it to start. The result leads me to another question : I had to add some delays between each step (START, STOP or byte transfer) of the I2C. If I don't only the START and STOP are sent, SCL doesn't even start. It's acting as if the interrupt flag polling isn't working. Could the flag itself not be working as expected ? Best regards. Eric – ricothebrol Oct 24 '17 at 19:27
  • I’ll take a look at the datasheet for the Part and see but you’re not using interrupts here. I think your Read/Write methods aren’t in the right order. Again I’ll look at the datasheet and confirm my suspicions. – blsmit5728 Oct 26 '17 at 00:25
  • Thanx ! What do you mean by right order ? A WRITE seems pretty straightforward to me (but again I might be wrong) : WRITE BUF - WAIT ACK - WAIT IF - CLEAR IF... I don't see what can go wrong here excepted if the flag just doesn't set... – ricothebrol Oct 26 '17 at 16:33
  • IFS1bits.I2C1MIF won't ever trigger becuase you did not setup interrupts for the I2C bus... Take a look at: http://ww1.microchip.com/downloads/en/DeviceDoc/31008a.pdf and: http://ww1.microchip.com/downloads/en/DeviceDoc/31008a.pdf You won't get an interrupt without actually enabling it. – blsmit5728 Oct 27 '17 at 19:16
  • Actually I wasn't looking to get an interrupt but to simply poll the flag with while(!IFS1bits.I2C1MIF). In the pdf you linked the state on p8 : "Interrupt flag bits get set when an interrupt condition occurs regardless of the state of its corresponding enable bit or the global enable bit, GIE (INTCON<7>)." This document is for mid range pics, maybe that's not valid for PIC32MX series (I'll try to find the corresponding document for that series). Polling actually works fine with mid range pics. – ricothebrol Oct 28 '17 at 09:15
  • In the end, polling isn't a good technique anyway as it blocks the pic, so I guess I'd better go for interrupts... – ricothebrol Oct 28 '17 at 09:19
  • OK I finally got it ! I forgot the NOT when polling the interrupt flag in MasterStart() function : while(IFS1bits.I2C1MIF); instead of while(!IFS1bits.I2C1MIF); Now it's working just fine, no interrupts activated. Thanx blsmit for your help anyway ! – ricothebrol Oct 29 '17 at 11:57
  • Glad you got it. I actually was unaware those ISR bits fire without the IE bits set.. I may have to go add some logic to somethings I've written. Could you please mark the original as the answer. gotta get that sweet sweet karma.. – blsmit5728 Oct 30 '17 at 12:12
  • Did it ! Sorry I'm not that familiar with the forum, didn't even know that existed ;) – ricothebrol Oct 31 '17 at 13:42