Pages: [1] 2   Go Down
Author Topic: Dallas Realtime Clock DS12887  (Read 2693 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 8
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi, I have one of these RTCs (DS12887) and I was wondering if someone could explain the workflow for reading the data/date-time from the chip. I read the datasheet, but I am still not clear. I am not looking for a schematic, just a simple processing workflow.

example:

set CS to HIGH
READ the first 8 bits (LSB or MSB?)
set AS low
set AS high
READ next 8 bits
etc.

Thanks!

P.S. I know there are other easier I2C based RTCs but I had a few of these hanging around and I really want to use this as a learning opportunity.
Logged

0
Offline Offline
Shannon Member
****
Karma: 206
Posts: 12177
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

This chip has a microprocessor bus, you have a choice of motorola or intel style signalling set by the MOT pin, then access as per timing diagrams - first send an address byte, then read or write the data byte.

For intel mode read it'll be something like:

ensure CS, DS, AS, R/W HIGH (idle state).
make bus pins OUTPUTs
CS goes LOW
put address byte on bus
AS goes LOW (strobes address)
make bus pins INPUTs
DS goes LOW
read data from bus
DS goes HIGH
AS and CS go HIGH.
Logged

[ I won't respond to messages, use the forum please ]

Offline Offline
Newbie
*
Karma: 0
Posts: 8
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thank you very much!

I have been looking everywhere for this.

So to recap,

1 ) Set IDLE state (CS, DS, AS, R/W to HIGH)
2 ) Switch BUS pins MODE to OUTPUT
3 ) Set CS LOW
4 ) Put Address on BUS pins
    0x00 Seconds,
    0x01 Seconds alarm,
    0x02 Minutes,
    0x03 Minutes alarm,
    0x04 Hours,
    0x05 Hours alarm,
    0x06 Day of week,
    0x07 Day of month,
    0x08 Month,
    0x09 Year,
    0x0A REGISTER A,
    0x0B REGISTER B,
    0x0C REGISTER C,
    0x0D REGISTER D
5 ) Set AS LOW to Commit/Strobe Address
6 ) Switch BUS pins MODE to INPUT
7 ) Set DS LOW
8 ) Read data from bus
9 ) Set DS HIGH
10 ) Return to IDLE state (AS and CS HIGH)

I assume that writing to the bus is as simple as skipping step 6 and swapping AS with R/W?

Thanks again
Logged

Cumming, GA
Offline Offline
Edison Member
*
Karma: 20
Posts: 1655
Ultimate DIY: Arduino
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

An example of how to implement a microprocessor compatible  bus with arduino can be found here:  http://softsolder.com/2009/07/18/arduino-hardware-assisted-spi-synchronous-serial-data-io/
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 8
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

So I have it wired up and signalling seems to be good... however, the data is not as expected.

The returned value for seconds is always 0b00000000... but there is data from other registers... which leave me wondering if I am sending the right address on the bus.

How are AD0 - 7 mapped to the byte value? Which is LSB and MSB?

Here is my code... anyone see what could be going wrong?

Code:
// DS12887 ADDRESS REGISTERS
const uint8_t REGISTER_SECONDS       = B00000000;   // Seconds
const uint8_t REGISTER_SECONDS_ALARM = B00000001;   // Seconds alarm
const uint8_t REGISTER_MINUTES       = B00000010;   // Minutes
const uint8_t REGISTER_MINUTES_ALARM = B00000011;   // Minutes alarm
const uint8_t REGISTER_HOURS         = B00000100;   // Hours
const uint8_t REGISTER_HOURS_ALARM   = B00000101;   // Hours alarm
const uint8_t REGISTER_DOW           = B00000110;   // Day of week
const uint8_t REGISTER_DOM           = B00000111;   // Day of month
const uint8_t REGISTER_MONTH         = B00001000;   // Month
const uint8_t REGISTER_YEAR          = B00001001;   // Year
const uint8_t REGISTER_A             = B00001010;   // REGISTER A
const uint8_t REGISTER_B             = B00001011;   // REGISTER B
const uint8_t REGISTER_C             = B00001100;   // REGISTER C
const uint8_t REGISTER_D             = B00001101;   // REGISTER D

// dataPins = AD0 - AD7
int dataPins[8] = {2, 3, 4, 5, 6, 7, 8, 9};
// int dataPins[8] = {9, 8, 7, 6, 5, 4, 3, 2}; INVERTED
int CS = 10; // SELECT CHIP
int AS = 11; // STROBE ADDRESS
int WR = 12; // HIGH READ LOW WRITE
int DS = 13; // READ

uint8_t seconds;
uint8_t asecs;
uint8_t minutes;
uint8_t amins;
uint8_t hours;
uint8_t ahour;
uint8_t dow;
uint8_t dom;
uint8_t month;
uint8_t year;
uint8_t regA;
uint8_t rebB;
uint8_t rebC;
uint8_t rebD;
uint8_t lastsecs = 0b11111111;

boolean getBit(uint8_t myVarIn, uint8_t whatBit) {
  boolean bitState;
  bitState = myVarIn & (1 << whatBit);
  return bitState;
}

uint8_t setBit(uint8_t myVarIn, uint8_t whatBit, boolean bitState) {
  if (bitState) {
    myVarIn |= (1 << whatBit);
  }
  else {
    myVarIn &= ~(1 << whatBit);
  }
  return myVarIn;
}

void setIdle() {
  digitalWrite(CS, HIGH);
  digitalWrite(AS, HIGH);
  digitalWrite(WR, HIGH);
  digitalWrite(DS, HIGH);
}

void switchRead() {
  for (int i=0; i<7; i++) {
    pinMode(dataPins[i], INPUT);
  }
}

void switchWrite() {
  for (int i=0; i<7; i++) {
    pinMode(dataPins[i], OUTPUT);
  }
}

uint8_t readBusData() {
  uint8_t dataBuffer = 0b00000000;
  for (int i=0; i<8; i++) {
    boolean state = digitalRead(dataPins[i]);
    dataBuffer = setBit(dataBuffer, i, state);
  }
  return dataBuffer;
}

void writeBusData(uint8_t data) {
  for (int i=0; i<8; i++) {
    digitalWrite(dataPins[i], getBit(data, i));
  }
}

void readSeconds() {
  seconds = getRegisterData(REGISTER_SECONDS);
}

uint8_t getRegisterData(uint8_t registerAddr) {
  uint8_t dataBuffer = 0b00000000;
  switchWrite();
  digitalWrite(CS, LOW);
  delayMicroseconds(20);
  writeBusData(registerAddr);
  digitalWrite(AS, LOW);
  delayMicroseconds(20);
  switchRead();
  delayMicroseconds(20);
  digitalWrite(DS, LOW);
  delayMicroseconds(20);
  dataBuffer = readBusData();
  digitalWrite(DS, HIGH);
  delayMicroseconds(20);
  digitalWrite(AS, HIGH);
  delayMicroseconds(20);
  digitalWrite(CS, HIGH);
  return dataBuffer;
}

void getRTCDate() {
  seconds = getRegisterData(REGISTER_SECONDS);
  minutes = getRegisterData(REGISTER_MINUTES);
  hours   = getRegisterData(REGISTER_HOURS);
  dow     = getRegisterData(REGISTER_DOW);
  dom     = getRegisterData(REGISTER_DOM);
  month   = getRegisterData(REGISTER_MONTH);
  year    = getRegisterData(REGISTER_YEAR);
}

void setup() {
  pinMode(CS, OUTPUT);
  pinMode(AS, OUTPUT);
  pinMode(WR, OUTPUT);
  pinMode(DS, OUTPUT);
  setIdle();
  switchRead();
  Serial.begin(115200);
}

void loop() {
  getRTCDate();
  Serial.print("(");
  Serial.print(dow);
  Serial.print(") ");
  Serial.print(month);
  Serial.print("/");
  Serial.print(dom);
  Serial.print("/");
  Serial.print(year);
  Serial.print("  ");
  Serial.print(hours);
  Serial.print(":");
  Serial.print(minutes);
  Serial.print(".");
  Serial.println(seconds);
  delay(1000);

  /*
  if (lastsecs != seconds) {
    lastsecs = seconds;
    for (int i=0; i<8; i++) {
      //Serial.print(getBit(seconds, i));
    }
  }
  */

}
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 8
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I here some code I found for controlling a DS12887 Microprocessor Bus based RTC... I can't seem to grasp exactly what's going on here... can someone tear it apart and provide a working example for the Arduino?

Thanks!

Code:
///////////////////////////////////////////////////////////////////////////
//  Name   : ds12887.c                                                     //
//  Author : Timothy Reitmeyer                                           //
//  Notice:
//         :                                       //
//  Date   : 05-05-2005                                                  //
//  Version: 1.00                       //
/////////////////////////////////////////////////////////////////////////////
#define CLK_SECS     0
#define CLK_SECS_ALM 1
#define CLK_MINS     2
#define CLK_MINS_ALM 3
#define CLK_HRS      4
#define CLK_HRS_ALM  5
#define CLK_DOW      6
#define CLK_DOM      7
#define CLK_MON      8
#define CLK_YR       9
#define REGA         10
#define REGB         11
#define REGC         12
#define REGD         13
#define nvram_min    14
#define nvram_max    127

const char dow_text[8][4] =
{
  "---",
  "Mon",
  "Tue",
  "Wed",
  "Thr",
  "Fri",
  "Sat",
  "Sun"
};

const char month_text[13][4] =
{
  "---",
  "Jan",
  "Feb",
  "Mar",
  "Apr",
  "May",
  "Jun",
  "Jul",
  "Aug",
  "Sep",
  "Oct",
  "Nov",
  "Dec"
};

struct rtc_pin_def
{
  BOOLEAN cs_bar;
  BOOLEAN as;
  BOOLEAN rw_bar;
  BOOLEAN ds;
  BOOLEAN reset_bar;
  BOOLEAN irq_bar;
  BOOLEAN rclr_bar;
  BOOLEAN swq;
  int8 ad;
};

struct rtc_rega_struc
{
  BOOLEAN   RS0;
  BOOLEAN   RS1;
  BOOLEAN   RS2;
  BOOLEAN   RS3;
  BOOLEAN   DV0;
  BOOLEAN   DV1;
  BOOLEAN   DV2;
  BOOLEAN   UIP;
} rega_var;
struct rtc_regb_struc
{
  BOOLEAN   DSE;
  BOOLEAN   MIL;
  BOOLEAN   DM;
  BOOLEAN   SQWE;
  BOOLEAN   UIE;
  BOOLEAN   AIE;
  BOOLEAN   PIE;
  BOOLEAN   SET;
} regb_var;//0b00001100
struct rtc_regc_struc
{
  BOOLEAN   bit0;
  BOOLEAN   bit1;
  BOOLEAN   bit2;
  BOOLEAN   bit3;
  BOOLEAN   UF;
  BOOLEAN   AF;//1=current time has matched the alarm time.
  BOOLEAN   PF;
  BOOLEAN   IRQF;
} regc_var;
struct rtc_regd_struc
{
  BOOLEAN   bit0;
  BOOLEAN   bit1;
  BOOLEAN   bit2;
  BOOLEAN   bit3;
  BOOLEAN   bit4;
  BOOLEAN   bit5;
  BOOLEAN   bit6;
  BOOLEAN   VRT;
} regd_var;

struct rtc_pin_def  rtc; //pins for the rtc
struct rtc_pin_def  rtc_tris;//tris for the rtc
#byte rtc  = 0xF82      //note: no semicolin 0x82=C0 on a 18F452
#byte rtc_tris = 0xF94  //tris location for port C pin 0
#define rtc_tris_r() rtc_tris=0;rtc_tris.ad=0xFF//read data is input
#define rtc_tris_w() rtc_tris=0;rtc_tris.ad=0//write data is output

void init_rtc(void);
char read_rtc(char addr);
void write_rtc(char addr,char data);
void set_time(void);

void write_rtc(char addr,char data)
{
  //C7=swq,6=rclr_bar,5=irq_bar,4=reset_bar,3=ds,2=rw_bar,1=as,0=cs_bar
  rtc_tris_w();
  rtc.cs_bar=0;//chip active
  rtc.ad=addr; //addr is on bus
  rtc.rw_bar=0;//write mode
  rtc.ds=0;    //data strob idle
  rtc.as=1;    //addr strob
  delay_cycles(1); // pause
  rtc.as=0;    //latch address
  rtc.ds=1;    //data strob idle
  rtc.ad=data; //data is on bus
  delay_cycles(1); // pause
  rtc.ds=0;    //latch data
  rtc_tris_r(); //set the tris of C  and D to ALL INPUTS
}

char read_rtc(char addr)
{
  //C7=swq,6=rclr_bar,5=irq_bar,4=reset_bar,3=ds,2=rw_bar,1=as,0=cs_bar
  char data;
  rtc_tris=0b11100000;//set the tris of C for setting address
  rtc_tris.ad=0x00; //set the tris of D for setting address
  rtc     =0b00011110;//set C for for setting address
  rtc.ad=addr;      //put address on bus
  #asm nop #endasm    //pause
  rtc.as=0;           //latch
  delay_cycles(1); // pause
  rtc_tris.ad=0xFF; //set the tris of D for reading data
  rtc.ds=0;           //release
  #asm nop #endasm    //pause
  data=rtc.ad;      //read the data from the bus
  rtc_tris_r(); //set the tris of B  and D to ALL INPUTS
  return(data);
}

void init_rtc()
{
  rtc_tris.reset_bar=0;//set the tris of C
  rtc.reset_bar=0;     //reset
  delay_ms(200);// delay the required time for reset
  rtc.reset_bar=1;  //release
  rtc_tris_r(); //set the tris of C & D to ALL INPUTS
  while(rega_var.UIP);//wait for update to finish
  regd_var=read_rtc(REGD);
  if(regd_var.VRT) fprintf(DEBUG,"Lithium battery OK\n\r");
  else fprintf(DEBUG,"Lithium battery dead\n\r");

  rega_var=0;
  write_rtc(REGA,0b11110100); //set reg A
  write_rtc(REGB,0b11111100); //(msb) SET,PIE,AIE,UIE,SQWE,DM,MIL,DSE (lsb)
}

void set_time()
{
  char yr, mn, dt, dy, hr, min, sec;
  char input[10];
  regb_var=read_rtc(REGB);
  if(regb_var.DM=1) /* Binary data */
  {

    fprintf(DEBUG,"\nEnter the year (0-99): ");
    fgets(input,DEBUG);//scanf("%bd", &yr);
    yr=atoi(input);
    fprintf(DEBUG,"\n\rread %u\n\r",yr);

    fprintf(DEBUG,"Enter the month (1-12): ");
    fgets(input,DEBUG);// scanf("%bd", &mn);
    mn=atoi(input);
    fprintf(DEBUG,"\n\rread %u\n\r",mn);


    fprintf(DEBUG,"Enter the date (1-31): ");
    fgets(input,DEBUG);// scanf("%bd", &dt);
    dt=atoi(input);
    fprintf(DEBUG,"\n\rread %u\n\r",dt);

    fprintf(DEBUG,"Enter the day (1-7): ");
    fgets(input,DEBUG);// scanf("%bd", &dy);
    dy=atoi(input);
    fprintf(DEBUG,"\n\rread %u\n\r",dy);

    if(regb_var.MIL=1) /* if 24 hour mode */
    {
      fprintf(DEBUG,"Enter the hour (1-23): ");
      fgets(input,DEBUG);//scanf("%bd", &hr);
      hr=atoi(input);
      fprintf(DEBUG,"\n\rread %u\n\r",hr);
    }
    else
    {
      fprintf(DEBUG,"Enter the hour (1-11): ");
      fgets(input,DEBUG);//scanf("%bd", &hr);
      hr=atoi(input);
      fprintf(DEBUG,"\n\rread %u\n\r",hr);
      fprintf(DEBUG,"A)M or P)M (A/P) " );
      fgets(input,DEBUG);//scanf("%1bs", &min);
      if(input == 'P' || input == 'p')
      hr |= 0x80; /* add PM indicator */
    }
    fprintf(DEBUG,"Enter the minute (0-59): ");
    fgets(input,DEBUG);//scanf("%bd", &min);
    min=atoi(input);
    fprintf(DEBUG,"\n\rread %u\n\r",min);
    fprintf(DEBUG,"Enter the second (0-59): ");
    fgets(input,DEBUG);//scanf("%bd", &sec);
    sec=atoi(input);
    fprintf(DEBUG,"\n\rread %u\n\r",sec);
  }

  regb_var.SET=1;//  REGB |= 0x80; /* inhibit update while writing to clock */
  write_rtc(REGB,(int8)regb_var); //set reg B
  write_rtc(CLK_SECS,sec);
  write_rtc(CLK_MINS,min);
  write_rtc(CLK_HRS,hr);
  write_rtc(CLK_DOW,dy);
  write_rtc(CLK_DOM,dt);
  write_rtc(CLK_MON,mn);
  write_rtc(CLK_YR,yr);
  write_rtc(CLK_HRS_ALM,0xFF);
  write_rtc(CLK_MINS_ALM,0xFF);
  write_rtc(CLK_SECS_ALM,0xFF);

  regb_var.SET=0;
  write_rtc(REGB,(int8)regb_var); //set reg B
}
 
Logged

Offline Offline
Edison Member
*
Karma: 116
Posts: 2205
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
I here some code I found for controlling a DS12887 Microprocessor Bus based RTC...

The code is actually very well written, mostly in C, making it quite portable.

Quote
I can't seem to grasp exactly what's going on here.

In that event, it is a lot easier to just write your own from the datasheet.
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 8
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
The code is actually very well written, mostly in C, making it quite portable.

Maybe I wasn't clear. I am not looking for a language translation, I was looking for a Arduino port. The real issue is that I do not understand the underlying differences between PIC and Arduino programming (in regards to code methods).

Quote
In that event, it is a lot easier to just write your own from the datasheet.

I was fully intending to do so, the datasheet (like many) is TOO complete and technical. It requires a degree in Electronics to understand. I tried to decipher the signalling on my own but I haven't found any good references to give me clues how to read the signal diagrams, so it's a little like shooting in the dark.
Logged

Offline Offline
Edison Member
*
Karma: 116
Posts: 2205
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I thought my point then was that the code is mostly C. So it requires very little changes to be ported to Arduino.

As to datasheet, that's pretty much a pre-requisite for embedded programmers.

Or at least the good ones.
Logged

UK
Offline Offline
Shannon Member
****
Karma: 223
Posts: 12630
-
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I'm not sure, because this is not standard 'C'/C++, but I suspect this part of the code is defining some memory mapped I/O ports:

Code:
struct rtc_pin_def  rtc; //pins for the rtc
struct rtc_pin_def  rtc_tris;//tris for the rtc
#byte rtc  = 0xF82      //note: no semicolin 0x82=C0 on a 18F452
#byte rtc_tris = 0xF94  //tris location for port C pin 0
#define rtc_tris_r() rtc_tris=0;rtc_tris.ad=0xFF//read data is input
#define rtc_tris_w() rtc_tris=0;rtc_tris.ad=0//write data is output

I don't imagine you will be using memory mapped access on the Arduino, so you need to understand what mechanism you're going to use to access the device's control registers.

The code also uses various I/O functions (fprintf, fgets, that sort of thing) which you would need to either get working by setting up the 'C' stdio environment in your sketch, or rework this code to send and receive this data via your sketch using whatever communication mechanism you want (serial port, push buttons + LCD, etc).

Also the code uses various primitive types such as int8 which are not defined as standard in the Arduino environment. You would need to either edit the code to use the equivalent Arduino/AVR Libc types, or create some typedefs or #defines for these types to associate them with the existing type definitions.
Logged

I only provide help via the forum - please do not contact me for private consultancy.

Offline Offline
Edison Member
*
Karma: 116
Posts: 2205
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I will give you two approaches (they are equally simple) that hopefully will get you going.

1) Take the existing code: try to make your code as compatible with the existing code.

Two issues that may trip you, depending on your familiarity with C.

The code uses bit fields, and hard-coded address for IO registers (port register and direction register).

Both are fairly easy to deal with:

Code:
#define BOOLEAN unsigned char //remap boolean to bit fields

...

struct rtc_pin_def
{
  BOOLEAN cs_bar:1; //map'd to bits
  BOOLEAN as:1; //map'd to bits
... //need to check for compiler endianness here

#define RTC_PORT PORTB //rtc port on pb
#define RTC_TRIS  DDRB //direction register
struct rtc_pin_def *rtc = (struct rtc_pin_def *) &RTC_PORT; //point rtc to rtc_port
struct rtc_pin_def *rtc_tris = (struct rtc_pin_def *) &RTC_TRIS; //point rtc_tris to the port direction register

...
    rtc->cs_bar=0;//chip active

With these changes, your code will look 90% like the PIC code.

The downside here is that the code isn't very Arduino - it is fundamentally a C-piece that is ported to the Arduino. It is fast but not quite as portable.

2. Rewrite the whole port operation related pieces. You let the function calls / interfaces remain the same but rewrite the port operations in Aruidno. For example

Code:
#define rtc_cs_bar  1 //_cs on pin 1
#define rtc_rw_bar 2 //_rw pin on pin 2
...

After that, you can operate pins accordingly.

This approach preserves the function interface but performs each function with Arduino calls.

It is slower but more portable.
Logged

0
Offline Offline
Shannon Member
****
Karma: 206
Posts: 12177
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

You've sort of cross-posted this thread - keep all the conversation in one thread so we don't end up answering the same
questions twice please, or at least provide a pointer to the other thread: http://arduino.cc/forum/index.php/topic,138324.0.html
Logged

[ I won't respond to messages, use the forum please ]

Offline Offline
Newbie
*
Karma: 0
Posts: 8
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

You've sort of cross-posted this thread - keep all the conversation in one thread so we don't end up answering the same
questions twice please, or at least provide a pointer to the other thread: http://arduino.cc/forum/index.php/topic,138324.0.html

Thanks, I was intending to keep the topics separate. While they are related, they cover two very distinct components. One is explicitly code and the other is about the electronic signalling.

I did post code in this thread but it was in an attempt to convey a working concept of my understanding of the process.

In any event, I agree that there should be a relevant cross-link to each post so that there is a clear relation between threads.
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 485
Posts: 18771
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

In my opinion these threads are closely related, so I've merged them. One asks for an explanation of existing code, the other discusses how the chip works. Basically the same thing.
Logged


Global Moderator
Offline Offline
Brattain Member
*****
Karma: 485
Posts: 18771
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

There seems to be some sort of Arduino code here:

http://www.codeforge.com/read/198607/RTC_DS12887.c__html

I Googled to find that. The comments appear to be in Chinese (Google Translate might help there) but the code may he helpful.
Logged


Pages: [1] 2   Go Up
Jump to: