Offline
Newbie
Karma: 0
Posts: 8
|
 |
« on: December 18, 2012, 11:23:15 pm » |
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
Tesla Member
Karma: 71
Posts: 6624
Arduino rocks
|
 |
« Reply #1 on: December 19, 2012, 04:31:32 am » |
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
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 8
|
 |
« Reply #2 on: December 19, 2012, 10:18:55 am » |
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
|
|
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 8
|
 |
« Reply #4 on: December 19, 2012, 10:11:05 pm » |
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? // 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
Newbie
Karma: 0
Posts: 8
|
 |
« Reply #5 on: December 20, 2012, 01:02:49 am » |
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! /////////////////////////////////////////////////////////////////////////// // 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
Edison Member
Karma: 114
Posts: 2205
|
 |
« Reply #6 on: December 20, 2012, 06:57:52 am » |
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. 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
Newbie
Karma: 0
Posts: 8
|
 |
« Reply #7 on: December 20, 2012, 09:12:34 am » |
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). 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
Edison Member
Karma: 114
Posts: 2205
|
 |
« Reply #8 on: December 20, 2012, 09:35:54 am » |
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
Tesla Member
Karma: 89
Posts: 6388
-
|
 |
« Reply #9 on: December 20, 2012, 10:24:08 am » |
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: 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
|
|
|
|
|
Offline
Edison Member
Karma: 114
Posts: 2205
|
 |
« Reply #10 on: December 20, 2012, 12:29:20 pm » |
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: #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 #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
Tesla Member
Karma: 71
Posts: 6624
Arduino rocks
|
 |
« Reply #11 on: December 20, 2012, 01:38:11 pm » |
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
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 8
|
 |
« Reply #12 on: December 20, 2012, 01:44:24 pm » |
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
Melbourne, Australia
Offline
Shannon Member
Karma: 218
Posts: 13897
Lua rocks!
|
 |
« Reply #13 on: December 20, 2012, 04:19:23 pm » |
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
Melbourne, Australia
Offline
Shannon Member
Karma: 218
Posts: 13897
Lua rocks!
|
 |
« Reply #14 on: December 20, 2012, 04:23:13 pm » |
There seems to be some sort of Arduino code here: http://www.codeforge.com/read/198607/RTC_DS12887.c__htmlI Googled to find that. The comments appear to be in Chinese (Google Translate might help there) but the code may he helpful.
|
|
|
|
|
Logged
|
|
|
|
|
|