Go Down

Topic: RTC Stopwatch (Read 4 times) previous topic - next topic

Jack Christensen


Hmm. I thought perhaps the "extra" 60 was to prevent negative values when elap_time resets and then becomes less than init_time?


Correct, leave that first 60 in there.

Not sure I'm following this, or why it's needed:

Code: [Select]

   //Use t_seconds to store each second that passes when button is pressed
   //divide seconds into itself for compound addition of one
   t_seconds+=(seconds/seconds);


Instead of:

Code: [Select]

   //Resets the seconds when t_seconds reaches 60
   if(t_seconds%60==0) {
     t_seconds=0;
     t_minutes++;
   }


Why not:

Code: [Select]

   //Resets the seconds when t_seconds reaches 60
   if(t_seconds >= 60) {
     t_seconds=0;
     t_minutes++;
   }


SImilarly, instead of:

Code: [Select]

   init_seconds = init_seconds - init_seconds; //set init_seconds to zero


I'd have written:

Code: [Select]

   init_seconds = 0;  //set init_seconds to zero



Quote

Therefore, I seem to be back at square one. Any ideas anyone?


Things are getting mighty complicated. There are four sets of variables for tracking time. The initialTime() and elapsedTime() functions are essentially duplicates. I'd have the function pass back the seconds as the return value, then only one function is needed. Lastly, some switch debouncing is needed.
MCP79411/12 RTC ... "One Million Ohms" ATtiny kit ... available at http://www.tindie.com/stores/JChristensen/

Jack Christensen

Hope you don't mind a spoiler, if so read no further ;)









Code: [Select]

//DS3231 stopwatch.
//Only utilizes seconds register, therefore cannot
//time intervals > 59 seconds.

#include <Wire.h>
#include <Button.h>                  //http://github.com/JChristensen/Button
const int START_BTN = 2;
const int STOP_BTN = 3;
const boolean PULLUP = true;
const boolean INVERT = true;
const int DEBOUNCE_MS = 25;

Button btnStart(START_BTN, PULLUP, INVERT, DEBOUNCE_MS);
Button btnStop(STOP_BTN, PULLUP, INVERT, DEBOUNCE_MS);
int startSeconds;
int stopSeconds;
int lapse;

void setup(void)
{
    Wire.begin();
    Serial.begin(9600);
    Serial.println("DS3231 Stopwatch");
}

void loop(void)
{
    btnStart.read();
    btnStop.read();
   
    if (btnStart.wasPressed()) {
        startSeconds = getRtcSeconds();
        Serial.print("Start=");
        Serial.println(startSeconds, DEC);
    }
    else if (btnStop.wasPressed()) {
        stopSeconds = getRtcSeconds();
        lapse = ((60 + stopSeconds) - startSeconds) % 60;
        Serial.print("Start=");
        Serial.print(startSeconds, DEC);
        Serial.print(" Stop=");
        Serial.print(stopSeconds, DEC);
        Serial.print(" Lapse=");
        Serial.println(lapse, DEC);
    }
}

int getRtcSeconds(void)
{
    Wire.beginTransmission(0x68);    //DS3231 device address
    Wire.write(0x00);                //seconds register
    Wire.endTransmission();
    Wire.requestFrom(0x68, 1);
    return bcd2dec(Wire.read());
}

//BCD-to-Decimal conversion
uint8_t bcd2dec(uint8_t n)
{
    return ((n / 16 * 10) + (n & 0x0F));
}
MCP79411/12 RTC ... "One Million Ohms" ATtiny kit ... available at http://www.tindie.com/stores/JChristensen/

Osiris

I did not use
Code: [Select]

if(t_seconds>=60) {
  // code
}

because originally I had t_seconds as seconds, so the value never exceeded 59 seconds. Mostly a copy/paste of the code from before.

As for subtracting init_seconds, I could have just set it to zero. However, in keeping an initialTime(); I can reset the time and the elapsedTime(); will then start at zero. Whereas if I were to subtract elap_seconds-(0) I would return a value anywhere from 0-59.

Below is the code I have right now. I've added some if statements and more conditions for increasing the minutes and while trying to avoid using a delay. I've removed some of the extra variables I wasn't using, and some of the redundant ones should only be for the current debugging purposes.
Code: [Select]

// Program Size: 6,660/32,256 bytes

#include <Wire.h>

const int button1 = 2;
const int button2 = 3;
const int LED = 13;
boolean butt1State, butt2State;
int seconds, minutes, hours,
    init_seconds, init_minutes, init_hours,
    elap_seconds, elap_minutes, elap_hours,
    t_seconds, t_minutes, t_hours,
    last_minute;
boolean passed_second=true;

void setup() {
  pinMode(button1, INPUT_PULLUP);
  pinMode(button2, INPUT_PULLUP);
  pinMode(LED, OUTPUT);
 
  Wire.begin();
  Serial.begin(9600);
 
  // clear /EOSC bit
  // Sometimes necessary to ensure that the clock keeps running on just battery power.
  // Once set, it shouldn't need to be reset but it's a good idea to make sure.
  Wire.beginTransmission(0x68); // address DS3231
  Wire.write(0x0E); // select register
  Wire.write(0b00011100); // write register bitmap, bit 7 is /EOSC
  Wire.endTransmission();
}

void loop() {
  butt1State=digitalRead(button1);
  butt2State=digitalRead(button2);
   
  if(butt1State==LOW) {
    initialTime();
    Serial.print(init_hours);Serial.print(":");Serial.print(init_minutes);Serial.print(":");Serial.println(init_seconds);
    delay(1000);
  }
   
  if(digitalRead(button2)==LOW) { // Use digitalRead(button2)==LOW for start/stop
    elapsedTime();
    // When 60 seconds has been reached and if the last minute has changed after a second has gone by
    // Increase the minute by 1
    if(seconds%60==0 && minutes==last_minute && passed_second==true) {
      minutes++; // 1 minute increase
      last_minute++; // last minute has now increased
      passed_second=false; // A second has not passed yet, wait for it to pass
    }
   
    // Wait for a full second to pass
    if((elap_seconds-init_seconds)==1) {
      passed_second=true;
    }
   
    Serial.print(init_hours);Serial.print(":");Serial.print(init_minutes);Serial.print(":");Serial.print(init_seconds);Serial.print("\t");
    Serial.print(elap_hours);Serial.print(":");Serial.print(elap_minutes);Serial.print(":");Serial.print(elap_seconds);Serial.print("\t");
    Serial.print(hours);Serial.print(":");Serial.print(minutes);Serial.print(":");Serial.print(seconds);Serial.print("\t");
    Serial.print(t_hours);Serial.print(":");Serial.print(t_minutes);Serial.print(":");Serial.println(t_seconds);
   
  }
}

int initialTime() {
  Wire.beginTransmission(0x68); // 0x68 is DS3231 device address
  Wire.write(0x00); // start at register
  Wire.endTransmission();
  Wire.requestFrom(0x68, 1);
 
  while(Wire.available()) {
    init_seconds = Wire.read();
    init_seconds = (((init_seconds & 0b11110000)>>4)*10 + (init_seconds & 0b00001111)); // convert BCD to decimal
  }
}

int elapsedTime() {
  Wire.beginTransmission(0x68); // 0x68 is DS3231 device address
  Wire.write(0x00); // start at register
  Wire.endTransmission();
  Wire.requestFrom(0x68, 1);
 
  while(Wire.available()) {
    elap_seconds = Wire.read();
    elap_seconds = (((elap_seconds & 0b11110000)>>4)*10 + (elap_seconds & 0b00001111)); // convert BCD to decimal
    seconds = (((60 + elap_seconds) - init_seconds) % 60);
  }
}


Hopefully my explanation doesn't become too confusing for what my goal is here. BTW, thanks again to all for the codes and library references and different conversion iterations, it is all appreciated. Please bear with me though, as I am the type of person that once an idea in their head, will not quit until it's accomplished, or has fully exhausted all possibilities.

The purpose of t_seconds was supposed to be a new holder for seconds. Currently, when the code is executed, seconds returns the time difference (?T in seconds) from when the first button was pushed. The ?T (from when the time was reset) is then displayed when button 2 is pushed. What I'd like to happen is this: the first button is pushed and resets the time and begins counting from zero. When the second button is pushed, the ?T is stored in t_seconds. This works well for the first time. Now after some time, a button is pressed, and t_seconds begins counting again from where it left off - despite the fact that elapsedTime(); has been continuously running.

I hope that makes a bit more sense.

Osiris

#18
May 28, 2013, 10:20 pm Last Edit: May 28, 2013, 10:42 pm by Osiris Reason: 1
Okay...

So I believe this is what I've wanted to accomplish. It may not be pretty, but it is a starting point. Basically, if you press "Button 1" the timer starts. Pressing "Button 2" will then start a secondary timer. The secondary timer will continue to increment as long as Button 2 is pressed. Releasing Button 2 will stop the secondary timer. If you press Button 2 again, the timer will continue from where it left off.

Code: [Select]

// Program Size: 6,660/32,256 bytes

#include <Wire.h>

const int button1 = 2;
const int button2 = 3;
const int LED = 13;
boolean butt1State, butt2State;
int seconds, minutes, hours,
   init_seconds, init_minutes, init_hours,
   elap_seconds, elap_minutes, elap_hours,
   t_seconds, t_minutes, t_hours,
   last_second;
boolean passed_second=true;

void setup() {
 pinMode(button1, INPUT_PULLUP);
 pinMode(button2, INPUT_PULLUP);
 pinMode(LED, OUTPUT);
 
 Wire.begin();
 Serial.begin(9600);
 
 // clear /EOSC bit
 // Sometimes necessary to ensure that the clock keeps running on just battery power.
 // Once set, it shouldn't need to be reset but it's a good idea to make sure.
 Wire.beginTransmission(0x68); // address DS3231
 Wire.write(0x0E); // select register
 Wire.write(0b00011100); // write register bitmap, bit 7 is /EOSC
 Wire.endTransmission();
}

void loop() {
 butt1State=digitalRead(button1);
 butt2State=digitalRead(button2);
 
 if(butt1State==LOW) {
   initialTime();
   Serial.print(init_hours);Serial.print(":");Serial.print(init_minutes);Serial.print(":");Serial.println(init_seconds);
   Serial.print("Ti");Serial.print("\t");Serial.print("T");Serial.print("\t");Serial.print("Te");Serial.print("\t");Serial.println("Tt");
   delay(1000);
 }
 
 if(digitalRead(button2)==LOW) { // Use digitalRead(button2)==LOW for start/stop
   elapsedTime();
   
   if(last_second != seconds && (seconds%60!=0)) {
     t_seconds+=seconds/seconds;
     last_second=seconds;
   } else if(seconds%60==0 && passed_second==true) {
     t_seconds++;
     passed_second=false;
   }
   if(t_seconds==60) {
     t_seconds=0;
     t_minutes++;
   }
   // When 60 seconds has been reached and if the last minute has changed after a second has gone by
   // Increase the minute by 1
   if(seconds%60==0 && passed_second==true) {
     minutes++; // 1 minute increase
     passed_second=false; // A second has not passed yet, wait for it to pass
   }
   
   // Wait for a full second to pass
   if((elap_seconds-init_seconds)==1) {
     passed_second=true;
   }
   
   Serial.print(init_hours);Serial.print(":");Serial.print(init_minutes);Serial.print(":");Serial.print(init_seconds);Serial.print("\t");
   Serial.print(elap_hours);Serial.print(":");Serial.print(elap_minutes);Serial.print(":");Serial.print(elap_seconds);Serial.print("\t");
   Serial.print(hours);Serial.print(":");Serial.print(minutes);Serial.print(":");Serial.print(seconds);Serial.print("\t");
   Serial.print(t_hours);Serial.print(":");Serial.print(t_minutes);Serial.print(":");Serial.println(t_seconds);
 
 }
}

int initialTime() {
 Wire.beginTransmission(0x68); // 0x68 is DS3231 device address
 Wire.write(0x00); // start at register
 Wire.endTransmission();
 Wire.requestFrom(0x68, 1);
 
 while(Wire.available()) {
   init_seconds = Wire.read();
   init_seconds = (((init_seconds & 0b11110000)>>4)*10 + (init_seconds & 0b00001111)); // convert BCD to decimal
 }
}

int elapsedTime() {
 Wire.beginTransmission(0x68); // 0x68 is DS3231 device address
 Wire.write(0x00); // start at register
 Wire.endTransmission();
 Wire.requestFrom(0x68, 1);
 
 while(Wire.available()) {
   elap_seconds = Wire.read();
   elap_seconds = (((elap_seconds & 0b11110000)>>4)*10 + (elap_seconds & 0b00001111)); // convert BCD to decimal
   seconds = (((60 + elap_seconds) - init_seconds) % 60);
   
 }
}


EDIT: Now this code will time how long each button is pressed. initialTime(); is called once in Setup and only once again in Loop.
N.B. Here are some of the things to work out: (1)This code can not count both buttons at the same time (not needed for my purposes);  (2)the code is repetitive, try and make a new function; (3)Overall Timer does not increment minutes
Code: [Select]

// Program Size: 6,660/32,256 bytes

#include <Wire.h>

const int button1 = 2;
const int button2 = 3;
const int LED = 13;
boolean butt1State, butt2State;
int seconds, minutes, hours,
    init_seconds, init_minutes, init_hours,
    elap_seconds, elap_minutes, elap_hours,
    b1_seconds, b1_minutes, b1_hours,
    b2_seconds, b2_minutes, b2_hours,
    last_second;
boolean passed_second=true;
boolean start=true;

void setup() {
  pinMode(button1, INPUT_PULLUP);
  pinMode(button2, INPUT_PULLUP);
  pinMode(LED, OUTPUT);
 
  Wire.begin();
  Serial.begin(9600);
 
  // clear /EOSC bit
  // Sometimes necessary to ensure that the clock keeps running on just battery power.
  // Once set, it shouldn't need to be reset but it's a good idea to make sure.
  Wire.beginTransmission(0x68); // address DS3231
  Wire.write(0x0E); // select register
  Wire.write(0b00011100); // write register bitmap, bit 7 is /EOSC
  Wire.endTransmission();
 
  initialTime();
  Serial.print("Start Time: ");Serial.print(init_hours);Serial.print(":");Serial.print(init_minutes);Serial.print(":");Serial.println(init_seconds);
  Serial.print("Time");Serial.print("\t");Serial.print("B1 Timer");Serial.print("\t");Serial.println("B2 Timer");
}

void loop() {
  if(start==true) {
    initialTime();
    start=false;
  }
 
  butt1State=digitalRead(button1);
  butt2State=digitalRead(button2);
   
  if(digitalRead(button1)==LOW) { // Use digitalRead(button#)==LOW for start/stop
    elapsedTime();
   
    if(last_second != seconds && (seconds%60!=0)) {
      b1_seconds+=seconds/seconds;
      last_second=seconds;
    } else if(seconds%60==0 && passed_second==true) {
      b1_seconds++;
      passed_second=false;
    }
    if(b1_seconds==60) {
      b1_seconds=0;
      b1_minutes++;
    }
    // When 60 seconds has been reached and if the last minute has changed after a second has gone by
    // Increase the minute by 1
    if(seconds%60==0 && passed_second==true) {
      //minutes++; // 1 minute increase
      passed_second=false; // A second has not passed yet, wait for it to pass
    }
   
    // Wait for a full second to pass
    if((elap_seconds-init_seconds)==1) {
      passed_second=true;
    }
   
    Serial.print(hours);Serial.print(":");Serial.print(minutes);Serial.print(":");Serial.print(seconds);Serial.print("\t");
    Serial.print(b1_hours);Serial.print(":");Serial.print(b1_minutes);Serial.print(":");Serial.print(b1_seconds);Serial.print("\t");
    Serial.print(b2_hours);Serial.print(":");Serial.print(b2_minutes);Serial.print(":");Serial.println(b2_seconds);
  }
   
  if(digitalRead(button2)==LOW) { // Use digitalRead(button#)==LOW for start/stop
    elapsedTime();
   
    if(last_second != seconds && (seconds%60!=0)) {
      b2_seconds+=seconds/seconds;
      last_second=seconds;
    } else if(seconds%60==0 && passed_second==true) {
      b2_seconds++;
      passed_second=false;
    }
    if(b2_seconds==60) {
      b2_seconds=0;
      b2_minutes++;
    }
    // When 60 seconds has been reached and if the last minute has changed after a second has gone by
    // Increase the minute by 1
    if(seconds%60==0 && passed_second==true) {
      minutes++; // 1 minute increase
      passed_second=false; // A second has not passed yet, wait for it to pass
    }
   
    // Wait for a full second to pass
    if((elap_seconds-init_seconds)==1) {
      passed_second=true;
    }
   
    Serial.print(hours);Serial.print(":");Serial.print(minutes);Serial.print(":");Serial.print(seconds);Serial.print("\t");
    Serial.print(b1_hours);Serial.print(":");Serial.print(b1_minutes);Serial.print(":");Serial.print(b1_seconds);Serial.print("\t");
    Serial.print(b2_hours);Serial.print(":");Serial.print(b2_minutes);Serial.print(":");Serial.println(b2_seconds);
   
  }
}

int initialTime() {
  Wire.beginTransmission(0x68); // 0x68 is DS3231 device address
  Wire.write(0x00); // start at register
  Wire.endTransmission();
  Wire.requestFrom(0x68, 1);
 
  while(Wire.available()) {
    init_seconds = Wire.read();
    init_seconds = (((init_seconds & 0b11110000)>>4)*10 + (init_seconds & 0b00001111)); // convert BCD to decimal
  }
}

int elapsedTime() {
  Wire.beginTransmission(0x68); // 0x68 is DS3231 device address
  Wire.write(0x00); // start at register
  Wire.endTransmission();
  Wire.requestFrom(0x68, 1);
 
  while(Wire.available()) {
    elap_seconds = Wire.read();
    elap_seconds = (((elap_seconds & 0b11110000)>>4)*10 + (elap_seconds & 0b00001111)); // convert BCD to decimal
    seconds = (((60 + elap_seconds) - init_seconds) % 60);
   
  }
}

odometer

#19
Jun 15, 2013, 03:09 pm Last Edit: Jun 15, 2013, 03:31 pm by odometer Reason: 1
You can work with BCD numbers directly, if you use these functions I wrote.
(The result will also be a BCD number)

Code: [Select]

inline byte bcdAdd (byte x, byte y) { // quick and dirty
  // "out of range" results will be e.g. A0 for 100
  byte z = (x&15)+(y&15);
  if (z<10) return (x + y);
  return (x + y + 6);
}

inline byte bcdSub (byte x, byte y) { // quick and dirty
  // "negative" results will be e.g. F9 for -1, F8 for -2...
  if ((x&15)>=(y&15)) return (x - y);
  return ((x - y) - 6);
}


And the way you exchange 60 seconds = 1 minute is quite straightforward:
Code: [Select]

if (seconds >= 0x60) {
 seconds = bcdSub(seconds, 0x60);
 minutes = bcdAdd(minutes, 0x01);
}


The way you add and subtract times (hh:mm:ss) is almost exactly like the way you learned to add and subtract in elementary school. This is true even if you do not use BCD.

Addition:

  • Add the hours, minutes, and seconds each individually.

  • If more than 60 seconds, then subtract 60 from the seconds and then add 1 to the minutes.

  • If more than 60 minutes, subtract 60 from the minutes and then add 1 to the hours.



Subtraction:

  • First, add 59 to the minutes and 60 to the seconds. This ensures that neither the minutes nor the seconds will go negative when we try to subtract.

  • Subtract the hours, minutes, and seconds, each individually.

  • Perform carries as in addition.

  • The minutes and seconds we added in the first step equal exactly 1 hour, so finish up by subtracting 1 from the hours.



Go Up