PIC32 + RTC via SPI doesn’t work

picrtcspi

I'm trying to read the time from an RTC with my PIC32 Microcontroller via SPI.
I've already succeeded in reading the time with an Arduino from the RTC.

PIC32: http://www.sparkfun.com/products/9713 (Documentation, Datasheet)
RTC: http://www.sparkfun.com/products/10160 (Documentation, Datasheet)
Arduino: Arduino Pro Mini

Problem: Somehow the values which the PIC32 reads from the RTC aren't correct. Maybe the values aren't read or stored correctly with the PIC32. For example the value of the seconds read from the RTC is only below 30 when the RTC actually is between the seconds 50 to 59. (The second value % 2 is still = 0 every two seconds.) I don't know why I get those weird values from the RTC.

Codes: I've included both code examples: the working Arduino version, and the not working PIC32 version.

Working Arduino Version:

#include <SPI.h>
const int  cs=10; //chip select 

int TimeDate[7]; //second,minute,hour,null,day,month,year

void setup() {
  Serial.begin(9600);
  pinMode(cs,OUTPUT); // chip select
  pinMode(8,OUTPUT); // LED light
  // start the SPI library:
  SPI.begin();
  SPI.setBitOrder(MSBFIRST); 
  SPI.setDataMode(SPI_MODE1); // both mode 1 & 3 should work 
  //set control register 
  digitalWrite(cs, LOW);  
  SPI.transfer(0x8E);
  SPI.transfer(0x60); //60= disable Osciallator and Battery SQ wave @1hz, temp compensation, Alarms disabled
  digitalWrite(cs, HIGH);
  delay(10); 
}

void loop() {
  ReadTimeDate();
  // (TimeDate[0] contains the seconds read from the rtc)
  if(TimeDate[0]<30) digitalWrite(8, HIGH); // works perfectly, is on from 0 t0 30
  else digitalWrite(8, LOW);
  delay(50);
}

void ReadTimeDate(){
  for(int i=0; i<=6;i++){
    if(i==3) i++;
    digitalWrite(cs, LOW);
    SPI.transfer(i+0x00); 
    unsigned int n = SPI.transfer(0x00);        
    digitalWrite(cs, HIGH);
    int a=n & B00001111;    
    if(i==2){   
      int b=(n & B00110000)>>4; //24 hour mode
      if(b==B00000010) b=20;        
      else if(b==B00000001) b=10;
      TimeDate[i]=a+b;
    }else if(i==4){
      int b=(n & B00110000)>>4;
      TimeDate[i]=a+b*10;
    }else if(i==5){
      int b=(n & B00010000)>>4;
      TimeDate[i]=a+b*10;
    }else if(i==6){
      int b=(n & B11110000)>>4;
      TimeDate[i]=a+b*10;
    }else{  
      int b=(n & B01110000)>>4;
      TimeDate[i]=a+b*10;   
    }
  }
}

Not working PIC32 Version:

#include <p32xxxx.h>
#include <plib.h>

// configure bit settings
#pragma config FPLLMUL = MUL_20, FPLLIDIV = DIV_2, FPLLODIV = DIV_1, FWDTEN = OFF
#pragma config POSCMOD = XT, FNOSC = PRIPLL, FPBDIV = DIV_1, CP = OFF, BWP = OFF

// I/O Definitions
#define CS  _RG9        // chip select
#define TCS _TRISG9     // tris control for CS pin

#define SYS_FREQ        (80000000L)

int TimeDate[7]; //second,minute,hour,null,day,month,year

// send one byte of data and receive one back at the same time
char writeSPI2( char i ) {
    SPI2BUF = i;                    // write to buffer for TX
    while( !SPI2STATbits.SPIRBF );  // wait for TX complete
    return SPI2BUF;                 // read the received values
}

// delay in microseconds function
void delay_us( delay ) {
    // note that 1 core tick = 2 SYS cycles (this is fixed)
    int us_ticks=( SYS_FREQ / 1000000 ) / 2;
    WriteCoreTimer( 0 );
    while( ReadCoreTimer() < delay*us_ticks );
}

main() {
    SYSTEMConfig( SYS_FREQ, SYS_CFG_WAIT_STATES | SYS_CFG_PCACHE );
        mOSCSetPBDIV( OSC_PB_DIV_2 ); // added

    // Open core timer for delay function
    OpenCoreTimer( 0xFFFFFFFF );

    // SPI config
    // CKP (clock polarity control) = 0
    // CKE (clock edge control) = 1
    // 8-bit, Master Mode
    // Baud = 4MHz (Fpb/20 = 80/20 MHz)
    SpiChnOpen( 2, SPICON_MSTEN | SPICON_CKE | SPICON_ON, 20 );

    // initialize the SPI peripheral
    TCS = 0;                // make CS pin output
    CS = 1;                 // release chip

    // 2. RTC settings
    delay_us( 1000000 );    // wait 1s
    CS = 0;
    delay_us( 40000 );      // delay 40ms
    writeSPI2(0x8E);
    writeSPI2(0x60);        // 60 = disable Osciallator and Battery SQ wave @1hz,
                            //  temp compensation, Alarms disabled
    CS = 1;
    delay_us( 10000 );

    // 3. Pin settings (to visualize feedback)
    DDPCONbits.JTAGEN = 0; // disable JTAGport, free up PORTA
    TRISA = 0b0000000000000000; // all PORTA as output (0 = 0 utput , 1 = 1 nput)

    // main loop
    while(1) {
        ReadTimeDate();
        if( TimeDate[0] < 30 ) PORTA = 0b1111111111111111; // does not work at all, the LED is on from 50 to 59 (why??????)
        else PORTA = 0b0000000000000000;
        delay_us( 5000 );
    }
}

ReadTimeDate() {
    int i;  // change TODO: changed to char
    for (i = 0; i <= 6; i++) {
        if (i == 3)
            i++;
        CS = 0;
        writeSPI2(i + 0x00);
        unsigned int n = writeSPI2(0x00);
        CS = 1;
        int a = n & 0b00001111;
        if (i == 2) {
            int b = (n & 0b00110000) >> 4; //24 hour mode
            if (b == 0b00000010) b = 20;
            else if (b == 0b00000001) b = 10;
            TimeDate[i] = a + b;
        } else if (i == 4) {
            int b = (n & 0b00110000) >> 4;
            TimeDate[i] = a + b * 10;
        } else if (i == 5) {
            int b = (n & 0b00010000) >> 4;
            TimeDate[i] = a + b * 10;
        } else if (i == 6) {
            int b = (n & 0b11110000) >> 4;
            TimeDate[i] = a + b * 10;
        } else {
            int b = (n & 0b01110000) >> 4;
            TimeDate[i] = a + b * 10;
        }
    }
}

Possible errors:

  • I don't know if it's necessary to set the setting of the BITOrder to MSBFirst on the PIC32. (like in the Arduino version) (and I don't know how)
  • The BaudRate might not be set correct. (altough I got it from a great tutorial)
  • The type and value conversions and the bit operations in the ReadTime() function can't be handled in the same way as on the Arduino, which might cause the strange values read from the RTC

Not Working PIC32 RTCC Version:

Here's a code snippet i've done to use for the RTCC module on the UBW32.

It is not working, as the minutes show up to be odd all the time in the test.

I don't know if I need further pin definitions, or if I should attach a crystal.

Here's the schematic. It looks to me like a crystal is included:
http://schmalzhaus.com/UBW32/EE/v26/UBW32_MX795_v26_sch.pdf

// Master header file for all peripheral library includes
#include <plib.h>

// configuration settings
#pragma config FNOSC = PRIPLL, POSCMOD = HS, FPLLMUL = MUL_18, FPLLIDIV = DIV_2, FPBDIV = DIV_2, FPLLODIV = DIV_1
#pragma config FWDTEN = OFF

int main(void) {

    rtccTime    tm;          // time structure
    rtccDate    dt;          // date structure

    // Configure the device for maximum performance.
    // This macro sets flash wait states, PBCLK divider and DRM wait states based on the specified
    // clock frequency. It also turns on the cache mode if avaialble.
    // Based on the current frequency, the PBCLK divider will be set at 1:2. This knoweldge
    // is required to correctly set UART baud rate, timer reload value and other time sensitive
    // setting.
    SYSTEMConfigPerformance(72000000L);

    RtccInit();   // init the RTCC

    while(RtccGetClkStat()!=RTCC_CLK_ON);   // wait for the SOSC to be actually running and RTCC to have its clock source
                                            // could wait here at most 32ms

    RtccOpen(0x10073000, 0x07011602, 0);    // set time and date (- actually i don't know what time is set, but for the moment it doesn't matter.)
                                            // time is MSb: hour, min, sec, rsvd. date is MSb: year, mon, mday, wday.
                                            // please note that the rsvd field has to be 0 in the time field!

    RtccGetTimeDate(&tm, &dt);              // get current time

    DDPCONbits.JTAGEN = 0; // disable JTAGport, free up PORTA
    TRISA = 0b0000000000000000;
    TRISB = 0b0000000000000000;

    while(1){

        RtccGetTimeDate(&tm, &dt);

        // test: minutes odd or even
        int minutes = tm.min%2;
        switch(minutes){
            case 0:
                PORTA = 0b0000000000000000;
                PORTB = 0b1111111111111111;
                break;
            case 1:
                PORTA = 0b1111111111111111;
                PORTB = 0b0000000000000000;
                break;
        }
    }
}

Does someone have an idea how i could get the correct values with the PIC32?

Best Answer

So thanks to the help of all here here's an alternative solution using the RTCC of the pic.

This solution makes much more sense then reading the time from an external SPI module.

you can buy a 32.768 crystal like this one: http://www.sparkfun.com/products/540

and two 11pf or 12 pf crystals and align them as shown on this schematic (top right): http://www.sparkfun.com/datasheets/DevTools/PIC/UBW32_MX795_v262.pdf

What I've still not figured out is how to power the RTC with an additional battery. If someone knows how I'd enjoy a post!

Here's a working example code:

// Master header file for all peripheral library includes
#include <plib.h>

// configuration settings
#pragma config FNOSC = PRIPLL, POSCMOD = HS, FPLLMUL = MUL_18, FPLLIDIV = DIV_2, FPBDIV = DIV_2, FPLLODIV = DIV_1
#pragma config FWDTEN = OFF

int main(void) {

    rtccTime    tm;          // time structure
    rtccDate    dt;          // date structure

    // Configure the device for maximum performance.
    // This macro sets flash wait states, PBCLK divider and DRM wait states based on the specified
    // clock frequency. It also turns on the cache mode if avaialble.
    // Based on the current frequency, the PBCLK divider will be set at 1:2. This knoweldge
    // is required to correctly set UART baud rate, timer reload value and other time sensitive
    // setting.
    SYSTEMConfigPerformance(72000000L);

    RtccInit();   // init the RTCC

    while(RtccGetClkStat()!=RTCC_CLK_ON);   // wait for the SOSC to be actually running and RTCC to have its clock source
                                            // could wait here at most 32ms

    RtccOpen(0x10073000, 0x07011602, 0);    // set time and date (- actually i don't know what time is set, but for the moment it doesn't matter.)
                                            // time is MSb: hour, min, sec, rsvd. date is MSb: year, mon, mday, wday.
                                            // please note that the rsvd field has to be 0 in the time field!

    RtccGetTimeDate(&tm, &dt);              // get current time

    DDPCONbits.JTAGEN = 0; // disable JTAGport, free up PORTA
    TRISA = 0b0000000000000000;
    TRISB = 0b0000000000000000;

    while(1){

        RtccGetTimeDate(&tm, &dt);

        // test: minutes odd or even
        int minutes = tm.sec%2;
        switch(minutes){
            case 0:
                PORTA = 0b0000000000000000;
                PORTB = 0b1111111111111111;
                break;
            case 1:
                PORTA = 0b1111111111111111;
                PORTB = 0b0000000000000000;
                PORTA._RA0 = 0;

                break;
        }
    }
}