CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

PIC 16F88 SPI Interface with DS3234 RTC

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
b.dokimakis



Joined: 12 Feb 2012
Posts: 3

View user's profile Send private message

PIC 16F88 SPI Interface with DS3234 RTC
PostPosted: Sun Feb 12, 2012 6:06 pm     Reply with quote

Ι have a ds3234 RTC with which i successfully interacted via SPI using an Arduino Duemilanove.


I did this using the code below:

Code:
#include <SPI.h>
#include <avr/sleep.h>
const int alarmPin = 2;
const int RTC_CS=10;
int count =0;

void setup()
{
    RTC_init();
}

void loop(){
   if (count >= 10) {
      delay(100);     
      count = 0;
 
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);   // sleep mode is set here

    sleep_enable();          // enables the sleep bit in the mcucr register
                             // so sleep is possible. just a safety pin

    attachInterrupt(0,rtcAlarm, LOW); // use interrupt 0 (pin 2) and run function
                                       // rtcAlarm when pin 2 gets LOW

    sleep_mode();            // here the device is actually put to sleep!!
                             // THE PROGRAM CONTINUES FROM HERE AFTER WAKING UP

    sleep_disable();         // first thing after waking from sleep:
                             // disable sleep...
    detachInterrupt(0);      // disables interrupt 0 on pin 2 so the
                             // wakeUpNow code will not be executed
                             // during normal running time.                       
  }                           
}


void RTC_init(){
  pinMode(RTC_CS,OUTPUT); // chip select
    // start the SPI library:
    SPI.begin();
    SPI.setBitOrder(MSBFIRST);
    SPI.setDataMode(SPI_MODE1); // both mode 1 & 3 should work
    //set control register
    digitalWrite(RTC_CS, LOW); 
    SPI.transfer(0x8E);
         SPI.transfer(0x05); //00000110 EOSC/BBSQW/TEMPCONV/RS1/RS2/INTCN/A2IE/A1IE
         digitalWrite(RTC_CS, HIGH);
         delay(10);
         digitalWrite(RTC_CS, LOW);
         SPI.transfer(0x8F);
         SPI.transfer(0x0);
    digitalWrite(RTC_CS, HIGH);
    delay(10);
    SetAlarm();
 //Turn on internal pullup for INT/SQW pin
         pinMode(alarmPin, INPUT);
         digitalWrite(alarmPin, HIGH); 
         attachInterrupt(0, rtcAlarm, LOW);     
}

void rtcAlarm(){
  digitalWrite(RTC_CS, LOW);
  SPI.transfer(0x8F);
  SPI.transfer(0x0);
  digitalWrite(RTC_CS, HIGH);
}

void SetAlarm(){
 delay(10);
 digitalWrite(RTC_CS, LOW);

  //Set Alarm 1 to once per sec
       
  SPI.transfer(0x87);
  SPI.transfer(0x80);
 
  digitalWrite(RTC_CS, HIGH);
  delay(10);
  digitalWrite(RTC_CS, LOW);

  SPI.transfer(0x88);
  SPI.transfer(0x80);
   
  digitalWrite(RTC_CS, HIGH);
  delay(10);
  digitalWrite(RTC_CS, LOW);

  SPI.transfer(0x89);
  SPI.transfer(0x80);
  delay(10);
 
  digitalWrite(RTC_CS, HIGH);
  delay(10);
  digitalWrite(RTC_CS, LOW);
 
  SPI.transfer(0x8A);
  SPI.transfer(0x80);
  delay(10);

  digitalWrite(RTC_CS, HIGH);
}






I then tried to program a PIC16F88 to take the place of Arduino.

I tried to replicate the commands used in the Arduino program, as it had proven to be working all right. After several hours, i managed to blink a led using an external interrupt with my PIC, but no luck in using the SPI.

The code i have created so far is this:
Code:
#include <16F88.h>
#device adc=8

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES HS                       //High speed Osc (> 4mhz for PCM/PCH) (>10mhz for PCD)
#FUSES NOPUT                    //No Power Up Timer
#FUSES NOMCLR                   //Master Clear pin used for I/O
#FUSES BROWNOUT                 //Reset when brownout detected
#FUSES LVP                      //Low Voltage Programming on B3(PIC16) or B5(PIC18)
#FUSES NOCPD                    //No EE protection
#FUSES NOWRT                    //Program memory not write protected
#FUSES NODEBUG                  //No Debug mode for ICD
#FUSES NOPROTECT                //Code not protected from reading
#FUSES FCMEN                    //Fail-safe clock monitor enabled

#use delay(clock=16000000)
#INT_EXT


void blink(){
   output_high(PIN_A2);
   delay_ms(1000);
   output_low(PIN_A2);
   delay_ms(1000);
   clear_interrupt(INT_EXT);
   output_low(PIN_B5);
   spi_write(0x8F);
   spi_write(0x0);
   output_high(PIN_B5);
}

void setalarm()
{
   output_low(PIN_B5);
   spi_write(0x87);
   spi_write(0x80);
   output_high(PIN_B5);
   delay_ms(10);
   output_low(PIN_B5); 
   spi_write(0x88);
   spi_write(0x80);
   output_high(PIN_B5);
   delay_ms(10);
   output_low(PIN_B5); 
   spi_write(0x89);
   spi_write(0x80);
   output_high(PIN_B5);
   delay_ms(10);
   output_low(PIN_B5); 
   spi_write(0x8A);
   spi_write(0x80);
   output_high(PIN_B5);
}

void rtcinit()
{
   output_high(PIN_B5);
   spi_write(0x8E);
   spi_write(0x05);
   output_low(PIN_B5);
   delay_ms(10);
   output_high(PIN_B5); 
   spi_write(0x8F);
   spi_write(0x00);
   output_low(PIN_B5);
   delay_ms(10);
   setalarm();
}

void main()
{
   output_low(PIN_B5);
   setup_spi(SPI_MASTER|SPI_L_TO_H|SPI_CLK_DIV_4);
   delay_ms(3000);
   set_tris_a(0x00);
   enable_interrupts(INT_EXT);
   enable_interrupts(GLOBAL);
   rtcinit();

   while(1){

   }
}



The connections on the breadboard are:

ds3234 PIC16F88

SQW -> RB0
MISO -> RB1
MOSI -> RB2
CLK -> RB4
SS -> RB5

and i have a led connected in pin RA2.

The led blinks if i manually cause an external interrupt on RB0, so i now that my basic connections (oscillator, ground, vcc and led) and programming are correct.

However, an interrupt from the ds3234 never occurs, so there's something i'm not doing right with the SPI.

Any help will be appreciated. Smile
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Sun Feb 12, 2012 6:49 pm     Reply with quote

Quote:

void rtcinit()
{
output_high(PIN_B5);
spi_write(0x8E);
spi_write(0x05);
output_low(PIN_B5);
delay_ms(10);

output_high(PIN_B5);
spi_write(0x8F);
spi_write(0x00);
output_low(PIN_B5);
delay_ms(10);

setalarm();
}

This routine has your Slave Select pulse inverted from what it should be.


Quote:

#FUSES LVP

Are you really using an LVP programmer ? Most people don't.
If you're not using LVP, change the fuse to NOLVP.



Quote:

#INT_EXT


void blink(){
output_high(PIN_A2);
delay_ms(1000);
output_low(PIN_A2);
.
.
.
}

Is the blink() routine associated with #int_ext ? If so, the #int_ext line
should be placed directly above the routine, for safety. If you
accidentally place any other code (such as variable declarations) in
between the two lines, you will stop the compiler from interpreting blink()
as an interrupt routine. Also, I would rename blink() to ext_isr() or
blink_isr() or something that definitely reminds me that it's an isr. Again,
for safety.


Quote:
void main()
{
output_low(PIN_B5);
setup_spi(SPI_MASTER|SPI_L_TO_H|SPI_CLK_DIV_4);
delay_ms(3000);
set_tris_a(0x00);
enable_interrupts(INT_EXT);
enable_interrupts(GLOBAL);
rtcinit();

while(1){

Since the Slave Select line is active low, you should properly initialize it
to the inactive state in your SPI init code. The inactive state is high.


Also, PIN_B5 should have been a #define statement. It should be:
Code:

#define DS3234_CS  PIN_B5

and then everywhere:
Code:
output_high(DS3234_CS);

Etc. This allows you to easily change the pin later, without having to
edit or do a Search & Replace on your code.

Also, ideally, you should have routines for ds3234_write() and
ds3234_read() with passed parameters. Currently you have inline code.
It's OK for initial testing but a driver should use routines.
b.dokimakis



Joined: 12 Feb 2012
Posts: 3

View user's profile Send private message

PostPosted: Sat Feb 18, 2012 7:24 am     Reply with quote

PCM programmer wrote:

This routine has your Slave Select pulse inverted from what it should be.

You most definitely have a point here :P
I inverted the pulse values during the time i was trying to make it work, and forgot to restore them.

Quote:

Are you really using an LVP programmer ? Most people don't.
If you're not using LVP, change the fuse to NOLVP.

Done.

Quote:

Is the blink() routine associated with #int_ext ? If so, the #int_ext line
should be placed directly above the routine, for safety. If you
accidentally place any other code (such as variable declarations) in
between the two lines, you will stop the compiler from interpreting blink()
as an interrupt routine. Also, I would rename blink() to ext_isr() or
blink_isr() or something that definitely reminds me that it's an isr. Again,
for safety.

I believe i hadn't made a mistake in that point, but i thank you for the valuable advice.

Quote:

Since the Slave Select line is active low, you should properly initialize it
to the inactive state in your SPI init code. The inactive state is high.

Didn't matter after all. The SPI setup propably doesn't have anything to do with the RTC itself, but only with the PIC.

Quote:
Also, PIN_B5 should have been a #define statement. It should be:
Code:

#define DS3234_CS  PIN_B5

and then everywhere:
Code:
output_high(DS3234_CS);

Etc. This allows you to easily change the pin later, without having to
edit or do a Search & Replace on your code.

Done. It sure is easier to read and modify this way. Smile

Quote:

Also, ideally, you should have routines for ds3234_write() and
ds3234_read() with passed parameters. Currently you have inline code.
It's OK for initial testing but a driver should use routines.

Will do, as soon as the exams period is over and i get the chance to work on the project full time.

So, here is the code that works:

Code:
#include <16F88.h>

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES INTRC_IO
#FUSES NOPUT                    //No Power Up Timer
#FUSES NOMCLR                   //Master Clear pin used for I/O
#FUSES BROWNOUT                 //Reset when brownout detected
#FUSES NOLVP                     
#FUSES NOCPD                    //No EE protection
#FUSES NOWRT                    //Program memory not write protected
#FUSES NODEBUG                  //No Debug mode for ICD
#FUSES NOPROTECT                //Code not protected from reading
#FUSES FCMEN                    //Fail-safe clock monitor enabled

#define DS3234_CS  PIN_B5

#use delay(clock=8000000)
//#use rs232(baud=9600, xmit=PIN_A0, rcv=PIN_A1)

#INT_EXT
void ext_isr()
{
   output_high(PIN_A2);
   delay_ms(150);
   output_low(PIN_A2);
   
   output_low(DS3234_CS);
   spi_write(0x8F);
   spi_write(0x00);
   output_high(DS3234_CS);
   
   ext_int_edge(H_TO_L); 
}

void setalarm()
{
   output_low(DS3234_CS);
   spi_write(0x87);
   spi_write(0x80);
   output_high(DS3234_CS);
   
   output_low(DS3234_CS); 
   spi_write(0x88);
   spi_write(0x80);
   output_high(DS3234_CS);

   output_low(DS3234_CS); 
   spi_write(0x89);
   spi_write(0x80);
   output_high(DS3234_CS);

   output_low(DS3234_CS); 
   spi_write(0x8A);
   spi_write(0x80);
   output_high(DS3234_CS);
   
   output_low(DS3234_CS); 
   spi_write(0x8F);
   spi_write(0x00);
   output_high(DS3234_CS);
}

void rtcinit()
{
   output_low(DS3234_CS);
   spi_write(0x8E);
   spi_write(0x05);
   output_high(DS3234_CS);
   
   output_low(DS3234_CS); 
   spi_write(0x8F);
   spi_write(0x00);
   output_high(DS3234_CS);
   
   setalarm();
}

void main()
{
   setup_spi(SPI_MASTER|SPI_L_TO_H|SPI_CLK_DIV_4|SPI_XMIT_L_TO_H);
   
   rtcinit();
   
   enable_interrupts(INT_EXT);
   enable_interrupts(GLOBAL);
   
   while(TRUE)
   {
   sleep();
   }
}


Thanks for your help PCM programmer! Smile Smile
ckielstra



Joined: 18 Mar 2004
Posts: 3680
Location: The Netherlands

View user's profile Send private message

PostPosted: Sat Feb 18, 2012 7:42 am     Reply with quote

I'm surprised your code works. From the first page of the DS3234 datasheet:
Quote:
4MHz SPI Bus Supports Modes 1 and 3

You have configured mode 0.

Please, next time use the following defines in your code to make it easier to read and minimize configuration errors like this:
Code:
// SPI Mode | MOTOROLA | MICROCHIP | CCS                          | Clock line idle | Data clocked in at
//----------------------------------------------------------------|-----------------|-------------------
//          | CPOL CPHA|  CKP CKE  |                              |                 |
//    0     |  0    0  |   0   1   | SPI_L_TO_H | SPI_XMIT_L_TO_H | low             | low to high edge
//    1     |  0    1  |   0   0   | SPI_L_TO_H                   | low             | high to low edge
//    2     |  1    0  |   1   1   | SPI_H_TO_L                   | high            | high to low edge
//    3     |  1    1  |   1   0   | SPI_H_TO_L | SPI_XMIT_L_TO_H | high            | low to high edge
//
// Example: setup_spi(SPI_MASTER | SPI_MODE_3 | SPI_CLK_DIV_4 );
//
#define SPI_MODE_0  (SPI_L_TO_H | SPI_XMIT_L_TO_H)
#define SPI_MODE_1  (SPI_L_TO_H)
#define SPI_MODE_2  (SPI_H_TO_L)
#define SPI_MODE_3  (SPI_H_TO_L | SPI_XMIT_L_TO_H)
b.dokimakis



Joined: 12 Feb 2012
Posts: 3

View user's profile Send private message

PostPosted: Sun Feb 19, 2012 3:50 am     Reply with quote

There's some usefull info. I've been looking for the meanings of the SPI modes.
Thanks!
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group