|
|
View previous topic :: View next topic |
Author |
Message |
b.dokimakis
Joined: 12 Feb 2012 Posts: 3
|
PIC 16F88 SPI Interface with DS3234 RTC |
Posted: Sun Feb 12, 2012 6:06 pm |
|
|
Ι 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. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Feb 12, 2012 6:49 pm |
|
|
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.
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
|
|
Posted: Sat Feb 18, 2012 7:24 am |
|
|
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.
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! |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Sat Feb 18, 2012 7:42 am |
|
|
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
|
|
Posted: Sun Feb 19, 2012 3:50 am |
|
|
There's some usefull info. I've been looking for the meanings of the SPI modes.
Thanks! |
|
|
|
|
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
|