DS3234 Alarms

SPI is driving me nuts (okay is out of the way now)

I got the DS3234 from sparkfun and got the time set and able to read it with the code examples. The problem is no where are there good examples for using the alarms.

I have been trying all night and haven’t gotten it to work.

I found this

http://arduino.cc/forum/index.php/topic,54482.0.html

But the library link the bottom doens’t have much for alarms.

Here is the datasheet http://datasheets.maxim-ic.com/en/ds/DS3234.pdf

What value to I write to register 0x8E to enable alarms?

I would like to set an alarm to trigger every minute.

Then how to turn off the alarm, set the alarm flag to 0?

I guess my problem is how to do you write to a register that has multiple values/setting for different stuff?

Register 0x8E sets a few settings and I need to set the last 2.

Thanks for any help. I really want to understand the ins and outs of this. I2C seemed a bit easier than this.

(don't have such a chip) maybe you should check for code for similar chips like the alarm for the DS1307 ? There exists libraries with alarm code. The ones I've seen were polling based.

I think the problem is less about I2C vs. SPI, and just really about getting everything set up right. I see the alarm mask bits are spread over three or four registers! Inconvenient, but given your requirements I think shortcuts can be taken. The general answer to you question, and this is true regardless of the interface type, is in order to modify say just one bit in a register, the register would have to first be read, then the value modified, then written back. But if you just want a once-per-minute alarm, I think a lot of the values in the various registers are inconsequential.

You don't say whether you want an interrupt once a minute, or if you just want to test the alarm flag. Let me know, and while I haven't used this chip, I can take a whack at specifying the register settings.

And do you care which alarm is used? There might be one less register to set if Alarm 2 is used.

Rob, Unlike the DS1307, the DS3234 (and DS3231) have hardware alarm functionality.

To set up a once-per-minute alarm with Alarm 2, write 0x80 to all three Alarm 2 control registers: 0x8B, 0x8C, and 0x8D. This sets the three mask bits for a once-per-minute alarm. Since only the seconds are being compared to generate the alarm, I believe all the other values in the alarm registers (minutes, hours, day/date, etc.) are unused and can be left at zero.

If you just want to test the alarm flag, this is all that is needed to set up the alarm. To test the flag, read register 0x0F and mask A2F, which is bit 1, so AND the register value with 0x02. To reset the flag, take the original value read from register 0x0F, AND it with 0xFD to turn A2F off, then write that value back to 0x8F.

If you want to enable interrupts, in addition to setting the three alarm control registers, set the 0x8E control register to 0x1E. This is just the default value, plus turns on the alarm 2 interrupt flag, A2IE. Reset the alarm flag as in the previous paragraph. To disable interrupts, write 0x1C to the 0x8E control register.

I do have a Chronodot which uses the DS3231 which has the same alarms, but uses I2C so it doesn't use different addresses to read and write a given register. Anyway I haven't actually tried using the hardware alarms myself, so let me know if this works!

Rob, Unlike the DS1307, the DS3234 (and DS3231) have hardware alarm functionality.

Good to know! thanx

[quote author=Jack Christensen link=topic=64297.msg468552#msg468552 date=1308411170] To set up a once-per-minute alarm with Alarm 2, write 0x80 to all three Alarm 2 control registers: 0x8B, 0x8C, and 0x8D. This sets the three mask bits for a once-per-minute alarm. Since only the seconds are being compared to generate the alarm, I believe all the other values in the alarm registers (minutes, hours, day/date, etc.) are unused and can be left at zero.

If you just want to test the alarm flag, this is all that is needed to set up the alarm. To test the flag, read register 0x0F and mask A2F, which is bit 1, so AND the register value with 0x02. To reset the flag, take the original value read from register 0x0F, AND it with 0xFD to turn A2F off, then write that value back to 0x8F.

If you want to enable interrupts, in addition to setting the three alarm control registers, set the 0x8E control register to 0x1E. This is just the default value, plus turns on the alarm 2 interrupt flag, A2IE. Reset the alarm flag as in the previous paragraph. To disable interrupts, write 0x1C to the 0x8E control register.

I do have a Chronodot which uses the DS3231 which has the same alarms, but uses I2C so it doesn't use different addresses to read and write a given register. Anyway I haven't actually tried using the hardware alarms myself, so let me know if this works! [/quote]

Thanks that helps. I didn't realize you had to write to all the masks.

How do you get 0x80 to write it to 1? Is that hex for binary 1?

If I want to print the value to the serial port for testing would i use Serial.println(value, BIN)? or hex?

So clear the flag I read 0x0F then add it to 0xFD to turn if off and write it back to 0x8F. So how do you come up with the hex values?

so can i do this

this is called when the interrupt is triggered change

void rtcAlarm(){
  byte a;
  
  if (digitalRead(alarmPin)) {
    
    Serial.println("Alarm Interrupt High");
  }
  
  else {
    Serial.println("Alarm Interrupt Low");
    
  }
  
  a = read_rtc_register(0x0F);
  
  Serial.println(a, BIN);
  
  digitalWrite(RTC_CS, LOW);

  SPI.transfer(0x8F);

  SPI.transfer(a + 0xFD);
  
  digitalWrite(RTC_CS, HIGH);
  
}

I am using interrupts or at least trying to.

I have SQW output connected to pin 2 (interrupt 0)

For testing I have interrupt 0 set to trigger on change. Right now though I think I have the alarm set but its not triggering.

Here is what I have to setup alarm 2 and enable the interrupt

void SetAlarm(){

  digitalWrite(RTC_CS, LOW);

  //Set Alarm 2 to once per minute
  
  SPI.transfer(0x8B);
  SPI.transfer(0x80);
  
  SPI.transfer(0x8C);
  SPI.transfer(0x80);
  
  SPI.transfer(0x8D);
  SPI.transfer(0x80);
  
//Enable Alarm Interrupt 2
  SPI.transfer(0x8E);

  SPI.transfer(0x1E); //how did you come up with the hex value?

  
  digitalWrite(RTC_CS, HIGH);

}

Here is my RTC init function that is from sparkfun's example. I don't understand how they got 0x60

int RTC_init(){ 
          //Turn on internal pullup for SQW pin 
          pinMode(alarmPin, INPUT);
          digitalWrite(alarmPin, HIGH);
          attachInterrupt(0, rtcAlarm, CHANGE);  
  
          
      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(0x60); //60= disable Osciallator and Battery SQ wave @1hz, temp compensation, Alarms disabled
      digitalWrite(RTC_CS, HIGH);
      delay(10);

         SetAlarm();
}

I just did a bunch of reading and figured out the bin to hex stuff. Time to try again.

Is using the internal pull up okay for sqw?

Google around for a tutorial on numbering systems (binary, octal, decimal, hexadecimal) and on logical operations (AND, OR, XOR, NOT). Also find a C tutorial and learn how the logical operations are implemented.

MobileWill: How do you get 0x80 to write it to 1? Is that hex for binary 1?

The flag is the most significant bit in the 8-bit register. 0x80 is the same as B10000000, or decimal 128.

If I want to print the value to the serial port for testing would i use Serial.println(value, BIN)? or hex?

Either works, it just depends how you want to see it. Print it twice, once each way! Print it a third time in decimal! But like I like to say, it's all just Ones and Zeroes :D

So clear the flag I read 0x0F then add it to 0xFD to turn if off and write it back to 0x8F.

Not ADD ... AND. Assuming the value read from the register is in a variable called rtcReg,

byte rtcReg;
rtcReg = read_rtc_register(0x0F);
//AND the register value with 0x02, which leaves only bit 1, the flag bit. "&" is the bitwise AND operator
if (rtcReg & 0x02 != 0) then {
  //the flag is set
}
else {
  //the flag is not set
}
...
//Turn the flag bit off and rewrite the register value to the RTC
//0xFD is the complement of 0x02, so the AND preserves all the bits in the register as 
//read earlier, but zeros bit 1 (the 2nd bit from the right in the register, bit 0 is the rightmost or least significant bit)
SPI.transfer(0x8F);    //send the address
SPI.transfer(rtcReg & 0xFD);  //send the data

SPI.transfer(0x1E); //how did you come up with the hex value?

Top of page 14 in the datasheet, note the power-on value of the control register is B00011100, or 0x1C. The Alarm 2 interrupt enable bit is bit 1, so writing B00011110, or 0x1E to the register turns the A2IE bit on and leaves the other bits in their power-on default state.

HTH

Great! Going to work on it now.

So you could technically do spi.transfer(B11000000); in stead of the hex value?

MobileWill: So you could technically do spi.transfer(B11000000); in stead of the hex value?

Abolutely! Or plain old decimal. B11000000, 0xC0, and 192 are just different representations of the same number.

I cleaned things up a bit and i think I have writing the correct values working, but when I read back the registers I don’t get back what I expect.

Am I reading to fast, do i need delays?

For example I am writing

B00000110 to 0x8E on startup

but when I read back i get

B11111111

For 0x0F i write 0x00 and I read back 1111 on startup and 10001101 on each interval.

Doesn’t make sense!!!

I am printing to serial every minute the data from those control registers and it doesn’t get what I wrote. Sometimes on startup when I read back it looks good and then at the interval reading it doesn’t. But on startup it isn’t consistent.

I checked the (using internal pull up) pull up voltage on the INT/SQW pin and it is 3.3v is that enough? I am using 3.3v to power the RTC.

Edit:

Also do i need to clear the interrupt flag?

EIFR=(1<<INTF0);?

You shouldn't need delays. Looking at the datasheet, the DS3234 will run SPI at 4MHz, and this is the default setting for the SPI library. You could slow things down a bit just for fun, e.g. SPI.setClockDivider(SPI_CLOCK_DIV16) which will give 1MHz (assuming you have a 16MHz clock) which should still be plenty fast enough.

Now, were you not originally able to set the RTC time and read it back? Can you still? If so, then setting the alarm or control registers is no different at all, just different register addresses.

Wait on the interrupts for now. I wouldn't even enable interrupts until reading and writing the RTC registers is working perfectly. One thing at a time...

Using the code from sparkfun I am able to set and read the time. I can still read the time fine. But for some reason the time is off. I think with all the register testing the clock might have been off for awhile.

I am thinking of connecting it to another arduino with no other code and see what happens. I will try setting the time again first.

So here is the results after lots of hours and using another Arduino with only running RTC code

  1. The RTC was acting up and becoming more and more unresponsive at times. Ended up pulling the battery until I figured out so that it would reset to defaults.

  2. Reading alarm and control/status values doesn't work right. Period. If I read 2 values they come out of the serial port in reverse order and some values are zero that shouldn't be. If you read one value its 0 regardless. So I gave up on reading the settings to verify.

  3. Despite the data sheet you can not have two transactions in 1 chip select session. You need to end it delay 10 or something and start another session.

  4. The Arduino clears the interrupt flag for you

  5. The internal pull up works great.

  6. I learned a lot!!

  7. I learned a lot!!

  8. It works better at 5v instead of 3.3 since Vcc needs to higher than Vbat and its too close.

I was able to use this as a reference and saw that each session only had 1 transaction. With a delay between each. I opted for a slightly longer delay of 10.

https://github.com/maniacbug/RTClib

Its a nice library but no alarm support. I might improve it later and use that library but for now I will use my own code.

Thank you for everyone's help, I couldn't of done it without all the input.

Okay I think I am convinced this RTC is bad.

It works after I configure it and set the time and all. But once I take power away and back again it starts not responding till I remove the battery to reset it. I will have to see if Sparkfun will send me another one.

Glad to (try to) help, too bad I don’t have one of these critters to fool with, some things do seem fishy. I suppose it is possible that the chip is bad, although that’s usually my conclusion of last resort.

MobileWill:

  1. The RTC was acting up and becoming more and more unresponsive at times. Ended up pulling the battery until I figured out so that it would reset to defaults.

What do you mean by unresponsive, exactly?

  1. Reading alarm and control/status values doesn’t work right. Period. If I read 2 values they come out of the serial port in reverse order and some values are zero that shouldn’t be. If you read one value its 0 regardless. So I gave up on reading the settings to verify.

Reverse order bit-wise, or byte-wise?

  1. Despite the data sheet you can not have two transactions in 1 chip select session. You need to end it delay 10 or something and start another session.

The way I read it, CS has to be cycled for each read or write operation. That is, (1) Drive CS low, (2) Read or write, (3) Drive CH high. Each operation consists of one address byte, followed by one or more data bytes. If more than one data byte, they are written to/read from the next sequential address(es).

  1. It works better at 5v instead of 3.3 since Vcc needs to higher than Vbat and its too close.

“Better”? The max spec on VPF is 2.70V, should be plenty of headroom there. How is this powered? Have you checked voltages with a meter? If you’ve tried both 5V and 3.3V, is that just for the RTC, or for the MCU as well?
[/quote]

I was able to use this as a reference and saw that each session only had 1 transaction. With a delay between each. I opted for a slightly longer delay of 10.

Datasheet says CS Inactive Time, tCWH has to be at least 400ns. If the MCU clock is 16MHz, that’s only seven instructions. I’d think that if there is any processing at all between one read/write and the next, it’d be way more than that. Heck, it probably takes more instructions than that to set up the next IO. Bottom line, if you mean 10ms, that is orders of magnitude more than needed. One microsecond is more than twice as much as needed.

MobileWill:

  1. The RTC was acting up and becoming more and more unresponsive at times. Ended up pulling the battery until I figured out so that it would reset to defaults.

What do you mean by unresponsive, exactly?

I mean that if I request data from it, it doesn’t respond and causes the Arduino to stop waiting for a response. At least that is what it looks like and now it is consistant on startup. Until I pull the battery and reset it.

  1. Reading alarm and control/status values doesn’t work right. Period. If I read 2 values they come out of the serial port in reverse order and some values are zero that shouldn’t be. If you read one value its 0 regardless. So I gave up on reading the settings to verify.

Reverse order bit-wise, or byte-wise?

I mean that if I do 2 serial.print(spi.transfer(0x0e)); and then do another one with a different address it displays in the serial port in reverse order like i had reversed the print statements. But if i do reverse i will get 0 for both. Idk its really weird. I tired with multiple cs sessions. I guess I can try it again and try to narrow it down.

  1. Despite the data sheet you can not have two transactions in 1 chip select session. You need to end it delay 10 or something and start another session.

The way I read it, CS has to be cycled for each read or write operation. That is, (1) Drive CS low, (2) Read or write, (3) Drive CH high. Each operation consists of one address byte, followed by one or more data bytes. If more than one data byte, they are written to/read from the next sequential address(es).

Well I saw something in there that you can have multiple byte transfers per session.

  1. It works better at 5v instead of 3.3 since Vcc needs to higher than Vbat and its too close.

“Better”? The max spec on VPF is 2.70V, should be plenty of headroom there. How is this powered? Have you checked voltages with a meter? If you’ve tried both 5V and 3.3V, is that just for the RTC, or for the MCU as well?

[/quote]

Well I had thought the not responding was because the voltage was close to Vbat and causing it to shutdown SPI. But I guess not. I thought I saw in there Vcc had to be higher then Vbat and Vpf?

I was able to use this as a reference and saw that each session only had 1 transaction. With a delay between each. I opted for a slightly longer delay of 10.

Datasheet says CS Inactive Time, tCWH has to be at least 400ns. If the MCU clock is 16MHz, that’s only seven instructions. I’d think that if there is any processing at all between one read/write and the next, it’d be way more than that. Heck, it probably takes more instructions than that to set up the next IO. Bottom line, if you mean 10ms, that is orders of magnitude more than needed. One microsecond is more than twice as much as needed.
{/quote}

Now that you mention it I remember seeing something like that the first time i read it, but I guess I didn’t go back to the beginning of it. I was doing a delay(10); The library uses delay(1); I can try it with that. But I really need to fix this hanging not responding problem. Something is wrong though because it does it with the code example from sparkfun.

Looking at the code you posted earlier, this is what you cannot do. The code is copied from the earlier post, I've just added comments.

void SetAlarm(){

  digitalWrite(RTC_CS, LOW);

  //Set Alarm 2 to once per minute
  
  SPI.transfer(0x8B);  //first byte is always the address byte
  SPI.transfer(0x80);  //bytes 2-n are always data bytes.  So this writes 0x80 to register 0x8B.
  
  SPI.transfer(0x8C);  //and since CS is still low, this is interpreted as a data byte and written to 0x8C.
  SPI.transfer(0x80);  //ditto, this gets written to 0x8D.
  
  SPI.transfer(0x8D);  //and this to 0x8E,
  SPI.transfer(0x80);  //and this to 0x8F,
  
//Enable Alarm Interrupt 2
  SPI.transfer(0x8E);  //and since CS has never gone high, this writes 0x8E to 0x90, which is the Crystal Aging Offset

  SPI.transfer(0x1E); //and this attempts to write 0x1E to the Temperature MSB at 0x91, but that's a read-only register, so probably nothing happens.

  
  digitalWrite(RTC_CS, HIGH);

}

Now, if you could try this way:

void SetAlarm(){

  digitalWrite(RTC_CS, LOW);  //start a write operation

  //Set Alarm 2 to once per minute
  
  SPI.transfer(0x8B);  //send the address byte
  SPI.transfer(0x80);  //write 0x80 to register 0x8B
  SPI.transfer(0x80);  //and to 0x8C
  SPI.transfer(0x80);  //and to 0x8D
  
//Enable Alarm Interrupt 2
  SPI.transfer(0x1E);  //write 0x1E to the control register (0x8E) to enable Alarm 2 interrupts
  
  digitalWrite(RTC_CS, HIGH); //end of the data write

}

We could do all that in one operation because we were writing to four contiguous registers. If they weren't contiguous, something like this would work (and this will still work even if they are contiguous):

void SetAlarm(){

  digitalWrite(RTC_CS, LOW);  //start the first write operation

  //Set Alarm 2 to once per minute
  
  SPI.transfer(0x8B);  //send the address byte
  SPI.transfer(0x80);  //write 0x80 to register 0x8B
  SPI.transfer(0x80);  //and to 0x8C
  SPI.transfer(0x80);  //and to 0x8D
  digitalWrite(RTC_CS, HIGH); //end the first write operation

//need to wait at least 400ns in between read/write operations, so we'll wait 1 microsecond, which is 1000ns.
  delayMicroseconds(1);
  
//Enable Alarm Interrupt 2
  digitalWrite(RTC_CS, LOW); //start the second write operation
  SPI.transfer(0x8E);  //this is the register address
  SPI.transfer(0x1E);  //write 0x1E to the control register (0x8E) to enable Alarm 2 interrupts
  digitalWrite(RTC_CS, HIGH); //end the second write operation

}

Hope that helps. Again, I'd forget the interrupts for now, and just try to get it to work playing with the time registers. Once you can read and write them, the others are just the same except for different addresses.

Meant to add, once I had this figured out, I'd check the Crystal Aging Offset register, and if it didn't contain a zero, I'd write a zero to it one time. It's possible that it could have had something unintended written to it.