Go Down

Topic: Stopwatch with Pause/Lap  6 digit 7 seg LED. HELP! (Read 127728 times) previous topic - next topic

CrossRoads

I tweaked it a little, try this.
You had a couple of } in the wrong place, think it was messing the logic up a little, thats why I think you had 4 at the end.
Next edit, use the Tools:Autoformat key, it can help you spot things like that, while the compiler may not necessarily complain.
You are shifting out 3 times, that may be slowing things down a little as well.  Try taking the first 2 sets out.

Code: [Select]

unsigned long currentmillis = 0;
unsigned long previousmillis = 0;
unsigned long interval = 10;

int latchpin = 8; // connect to pin 12 on the 74HC595
int clockpin = 12; // connect to pin 11 on the 74HC595
int datapin = 11; // connect to pin 14 on the 74HC595
float b = 0;
int c = 0;
float d = 0;
int e = 0;
float f = 0;
int g = 0;
float h = 0;
int i = 0;
float j = 0;
int k = 0;
float l = 0;
int m = 0;
int hundredths1;
int seconds;
int minutes;
int hundredths;

[glow]int segdisp[10] = {
 63,6,91,79,102,109,125,7,127,111 }; //segment references using 74HC595 Shift Registers
// not really sure what this is, I did not mess with it[/glow]

[glow]int time_update = 0;// added new flag[/glow]
void setup()
{
 pinMode(latchpin, OUTPUT);
 pinMode(clockpin, OUTPUT);
 pinMode(datapin, OUTPUT);
}

void loop()
{
 currentmillis = millis();  // read the time.
 if (currentmillis - previousmillis >= interval) // 10 milliseconds have gone by
 {
   previousmillis  = currentmillis;  // save the time for the next comparison
   hundredths = hundredths +1;
   [glow][glow]time_update = 1;[/glow]  }  // set flag to upate & shift out[/glow]

[glow]  if (time_update == 1){  // no updating if not at 10ms interval, skip this whole section
   // increment the counters, roll as needed, shift the digits out
   time_update = 0; // reset for next pass thru[/glow]
     digitalWrite(latchpin, LOW);

[glow]// why do this? I would think it would just make the display look flickery,
// going between 0 & a real number each update. Maybe put a copy of this in void setup() to clear the displays to start the program[/glow]
     shiftOut(datapin, clockpin, MSBFIRST, 0); // clears the left display
     shiftOut(datapin, clockpin, MSBFIRST, 0); // clears the left display
     shiftOut(datapin, clockpin, MSBFIRST, 0); // clears the left display
     shiftOut(datapin, clockpin, MSBFIRST, 0); // clears the left display
     shiftOut(datapin, clockpin, MSBFIRST, 0); // clears the left display
     shiftOut(datapin, clockpin, MSBFIRST, 0); // clears the left display
     digitalWrite(latchpin, HIGH);
     if (hundredths<1)
     {
       digitalWrite(latchpin, LOW);

[glow]// why do this again? I would think it would just make the display look flickery,
// going between 0 & a real number each update[/glow]

       shiftOut(datapin, clockpin, MSBFIRST, segdisp[hundredths]); // sends the digit down the serial path
       shiftOut(datapin, clockpin, MSBFIRST, 0); // sends a blank down the serial path to push the digit to the right
       shiftOut(datapin, clockpin, MSBFIRST, 0); // sends a blank down the serial path to push the digit to the right
       shiftOut(datapin, clockpin, MSBFIRST, 0); // sends a blank down the serial path to push the digit to the right
       digitalWrite(latchpin, HIGH);
     }
     else if (hundredths>=1)
       //------------------------------ Hundredths below
     {
       d=hundredths%10; // find the remainder of dividing hundredths by 10, this will be the right-hand digit
       c=int(d); // make it an integer, c is the right hand digit

       b=hundredths/10; // divide hundredths by 10 - the whole number value will be the left-hand digit
       e = int(b); // e is the left hand digit
     }
       //------------------------------ Ten of Seconds (60) below

       if( hundredths == 100) {
         c=0; // clear the hundredths digit
         e=0; // clear the hundredths digit
         hundredths=0; // reset the timer
         seconds++; // add 1 second to "seconds"
         f=seconds%10; // float the %
         g=int (f); // print the % first "seconds" digit

         h=seconds/10; // divide the seconds by 10 to get the tens of seconds
         i = int (h); // print the tens of seconds digit
       }

       //------------------------------ Minutes (60) below

       if( seconds == 60) {
         g=0; // clear the "seconds" digit
         i=0; // clear the "tens" of seconds digit

         seconds=0; // reset the seconds
         minutes++; // add 1 minute to "minutes"
         j=minutes%10; // float the %
         k=int (j); // print the % first "minute" digit

         l=minutes/10; // divid the minutes by 10 to get the tens of minutes
         m = int (l); // print the tens of minutes digit

       }
       //------------------------------

       if( minutes == 60) {
         k=0; // clear the "minutes" digit
         m=0; // clear the "tens" of minutes digit
         minutes=0; // reset the minutes
       }
[glow]// counters are all updated now, just do the shiftout one time here:[/glow]
       digitalWrite(latchpin, LOW); // send the digits down to the shift registers!
       shiftOut(datapin, clockpin, MSBFIRST, segdisp[c]); // print the % first "hundredths" digit
       shiftOut(datapin, clockpin, MSBFIRST, segdisp[e]); // print the tens of hundredths digit
       shiftOut(datapin, clockpin, MSBFIRST, segdisp[g]); // print the % first "seconds" digit
       shiftOut(datapin, clockpin, MSBFIRST, segdisp[i]); // print the tens of seconds digit
       shiftOut(datapin, clockpin, MSBFIRST, segdisp[k]); // print the % first "minute" digit
       shiftOut(datapin, clockpin, MSBFIRST, segdisp[m]); // print the tens of minutes digit
       digitalWrite(latchpin, HIGH);

 } // end if time to be updated

} // end void loop

Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

Warren Reeve

Hi Robert.. your a star!! I have it all working and took out alot of that code that wasn't needed. It is now running almost accurately.. I have highlighted in the code [glow]if( hundredths == 100)[/glow] because I can make the time go faster or slower by entering 99 or 100. 99 is too fast and 100 is too slow?? How can this be edited?
After 11mins 30secs it's almost 5 seconds slow.

Code: [Select]
unsigned long currentmillis = 0;
unsigned long previousmillis = 0;
unsigned long interval = 10;

int latchpin = 8; // connect to pin 12 on the 74HC595
int clockpin = 12; // connect to pin 11 on the 74HC595
int datapin = 11; // connect to pin 14 on the 74HC595

int c = 0;
int e = 0;
int g = 0;
int i = 0;
int k = 0;
int m = 0;

int seconds;
int minutes;
int hundredths;

int segdisp[10] = {
 63,6,91,79,102,109,125,7,127,111 }; //segment references using 74HC595 Shift Registers
//The above numbers light up different segments of a digit

int time_update = 0;// added new flag
void setup()
{
 pinMode(latchpin, OUTPUT);
 pinMode(clockpin, OUTPUT);
 pinMode(datapin, OUTPUT);
}

void loop()
{
 currentmillis = millis();  // read the time.
 if (currentmillis - previousmillis >= interval) // 10 milliseconds have gone by
 {
   previousmillis  = currentmillis;  // save the time for the next comparison
   hundredths = hundredths +1;
 
time_update = 1;  }  // set flag to upate & shift out

 if (time_update == 1){  // no updating if not at 10ms interval, skip this whole section
   // increment the counters, roll as needed, shift the digits out
   time_update = 0; // reset for next pass thru
     


       //------------------------------ Hundredths below
     {
       c=hundredths%10; // find the remainder of dividing hundredths by 10, this will be the right-hand digit
       e=hundredths/10; // divide hundredths by 10 - the whole number value will be the left-hand digit
     
     }
       //------------------------------ Ten of Seconds (60) below

       [glow]if( hundredths == 100) [/glow]{
       
         hundredths=0; // reset the timer
         seconds++; // add 1 second to "seconds"
       
           g=seconds%10; //
         i=seconds/10; // divide the seconds by 10 to get the tens of seconds
       
       }

       //------------------------------ Minutes (60) below

       if( seconds == 60) {
         g=0; // clear the "seconds" digit
         i=0; // clear the "tens" of seconds digit

         seconds=0; // reset the seconds
         minutes++; // add 1 minute to "minutes"
         k=minutes%10; //
         m=minutes/10; // divid the minutes by 10 to get the tens of minutes
       
       }
       //------------------------------

       if( minutes == 60) {
         k=0; // clear the "minutes" digit
         m=0; // clear the "tens" of minutes digit
         minutes=0; // reset the minutes
       }
// counters are all updated now, just do the shiftout one time here:
       digitalWrite(latchpin, LOW); // send the digits down to the shift registers!
       shiftOut(datapin, clockpin, MSBFIRST, segdisp[c]); // print the % first "hundredths" digit
       shiftOut(datapin, clockpin, MSBFIRST, segdisp[e]); // print the tens of hundredths digit
       shiftOut(datapin, clockpin, MSBFIRST, segdisp[g]); // print the % first "seconds" digit
       shiftOut(datapin, clockpin, MSBFIRST, segdisp[i]); // print the tens of seconds digit
       shiftOut(datapin, clockpin, MSBFIRST, segdisp[k]); // print the % first "minute" digit
       shiftOut(datapin, clockpin, MSBFIRST, segdisp[m]); // print the tens of minutes digit
       digitalWrite(latchpin, HIGH);

 } // end if time to be updated

} // end void loop



CrossRoads

#17
Dec 26, 2010, 07:44 pm Last Edit: Dec 26, 2010, 07:45 pm by CrossRoads Reason: 1
Try simplifying it - maybe the divisions & ints & floats is introducing some roundover type errors?

Code: [Select]

unsigned long currentmillis = 0;
unsigned long previousmillis = 0;
unsigned long interval = 10;

int latchpin = 8; // connect to pin 12 on the 74HC595
int clockpin = 12; // connect to pin 11 on the 74HC595
int datapin = 11; // connect to pin 14 on the 74HC595

int ones_seconds = 0;
int tens_seconds = 0;
int ones_minutes = 0;
int tens_minutes = 0;
int tenths = 0;
int hundredths= 0;

int segdisp[10] = {
 63,6,91,79,102,109,125,7,127,111 }; //segment references using 74HC595 Shift Registers
//The above numbers light up different segments of a digit

int time_update = 0;// added new flag
void setup()
{
 pinMode(latchpin, OUTPUT);
 pinMode(clockpin, OUTPUT);
 pinMode(datapin, OUTPUT);
}

void loop()
{
 currentmillis = millis();  // read the time.
 if (currentmillis - previousmillis >= interval) // 10 milliseconds have gone by
 {
   previousmillis  = currentmillis;  // save the time for the next comparison

   time_update = 1;  
 }  // set flag to upate & shift out

 if (time_update == 1){  // no updating if not at 10ms interval, skip this whole section
   // increment the counters, roll as needed, shift the digits out
   time_update = 0; // reset for next pass thru

   hundredths = hundredths +1;
   if (hundredths == 10){
     hundredths = 0;
     tenths = tenths +1;
   }

   if (tenths == 10){
     tenths = 0;
     ones_seconds = ones_seconds +1;
   }

   if (ones_seconds == 10){
     ones_seconds = 0;
     tens_seconds = tens_seconds +1;
   }

   if (tens_seconds == 6){
     tens_seconds = 0;
     ones_minutes = ones_minutes +1;
   }

   if (ones_minutes == 10){
     ones_minutes = 0;
     tens_minutes = tens_minutes +1;
   }
   if (tens_minutes == 6){
     tens_minutes = 0;
   }

   // counters are all updated now, just do the shiftout one time here:
   digitalWrite(latchpin, LOW); // send the digits down to the shift registers!
   shiftOut(datapin, clockpin, MSBFIRST, segdisp[hundredths]); // print the % first "hundredths" digit
   shiftOut(datapin, clockpin, MSBFIRST, segdisp[tenths]); // print the tens of hundredths digit
   shiftOut(datapin, clockpin, MSBFIRST, segdisp[ones_seconds]); // print the % first "seconds" digit
   shiftOut(datapin, clockpin, MSBFIRST, segdisp[tens_seconds]); // print the tens of seconds digit
   shiftOut(datapin, clockpin, MSBFIRST, segdisp[ones_minutes]); // print the % first "minute" digit
   shiftOut(datapin, clockpin, MSBFIRST, segdisp[tens_minutes]); // print the tens of minutes digit
   digitalWrite(latchpin, HIGH);

 } // end if time to be updated

} // end void loop


Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

Warren Reeve

#18
Dec 26, 2010, 07:57 pm Last Edit: Dec 26, 2010, 08:16 pm by warrenreeve Reason: 1
Cheers Robert.. I will try this now.  :)

---

That code works fine.. although after 5 minutes it still looses around 2 seconds??

This is what I have now;
Code: [Select]


unsigned long currentmillis = 0;
unsigned long previousmillis = 0;
unsigned long interval = 10;

int latchpin = 8; // connect to pin 12 on the 74HC595
int clockpin = 12; // connect to pin 11 on the 74HC595
int datapin = 11; // connect to pin 14 on the 74HC595

int ones_seconds = 0;
int tens_seconds = 0;
int ones_minutes = 0;
int tens_minutes = 0;
int tenths = 0;
int hundredths= 0;


int segdisp[10] = {
 63,6,91,79,102,109,125,7,127,111 }; //segment references using 74HC595 Shift Registers
//The above numbers light up different segments of a digit

int time_update = 0;// added new flag
void setup()
{
 pinMode(latchpin, OUTPUT);
 pinMode(clockpin, OUTPUT);
 pinMode(datapin, OUTPUT);
}

void loop()
{
 currentmillis = millis();  // read the time.
 if (currentmillis - previousmillis >= interval) // 10 milliseconds have gone by
 {
   previousmillis  = currentmillis;  // save the time for the next comparison
 
 
time_update = 1;  }  // set flag to upate & shift out

 if (time_update == 1){  // no updating if not at 10ms interval, skip this whole section
   // increment the counters, roll as needed, shift the digits out
   time_update = 0; // reset for next pass thru
     

 hundredths = hundredths +1;
     
   if (hundredths == 10){
     hundredths = 0;
     tenths = tenths +1;
   }

   if (tenths == 10){
     tenths = 0;
     ones_seconds = ones_seconds +1;
   }

   if (ones_seconds == 10){
     ones_seconds = 0;
     tens_seconds = tens_seconds +1;
   }

   if (tens_seconds == 6){
     tens_seconds = 0;
     ones_minutes = ones_minutes +1;
   }

   if (ones_minutes == 10){
     ones_minutes = 0;
     tens_minutes = tens_minutes +1;
   }
   if (tens_minutes == 6){
     tens_minutes = 0;
   }

   // counters are all updated now, just do the shiftout one time here:
   digitalWrite(latchpin, LOW); // send the digits down to the shift registers!
   shiftOut(datapin, clockpin, MSBFIRST, segdisp[hundredths]); // print the % first "hundredths" digit
   shiftOut(datapin, clockpin, MSBFIRST, segdisp[tenths]); // print the tens of hundredths digit
   shiftOut(datapin, clockpin, MSBFIRST, segdisp[ones_seconds]); // print the % first "seconds" digit
   shiftOut(datapin, clockpin, MSBFIRST, segdisp[tens_seconds]); // print the tens of seconds digit
   shiftOut(datapin, clockpin, MSBFIRST, segdisp[ones_minutes]); // print the % first "minute" digit
   shiftOut(datapin, clockpin, MSBFIRST, segdisp[tens_minutes]); // print the tens of minutes digit
   digitalWrite(latchpin, HIGH);



 } // end if time to be updated

} // end void loop




Is this way supposed to be as in-accurate as this or would I have to fit a RTC clock or something?? As a stopwatch/lap time it is going to need to be accurate for at least 5 minutes for short circuit and road race events.

Warren Reeve

Hi Robert,
Just a quick note to let you know I have applied a fix to the timer. (see code below)
I added 7 x 100ths to the 'hundredths' each loop. Now it's almost accurate to an hour.. just got to tweak it a little more.  :)
Really pleased with the whole thing and can't thank you enough for your help with the code.
I am now trying to work with the buttons.. start, stop, lap/pause with background restart.. then resume.
Using de-bounce I should be able to work something out?? I will of course keep you updated.

Code: [Select]
if (ones_seconds == 10){
     ones_seconds = 0;
     [glow]hundredths = hundredths +7;[/glow]
tens_seconds = tens_seconds +1;
   }


CrossRoads

Wow, I didn't think it would lose that much time.  I wonder if you will see that change with temperature.

As for debouncing, just add reading of switches in your loop as well.
If the state of the button is normally high (say a switch on a pin with internal pullup that gets connected to ground when pressed), then take action on the first read of 0, set a flag, and wait for 5 hundreths or a tenth to elapse before you allow it to be read again.
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

CrossRoads

The other thing to try is replace millis() with micros()

http://arduino.cc/en/Reference/Micros

Change interval to 10000,
10,000uS = 10mS
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

Coding Badly

Quote
I added 7 x 100ths to the 'hundredths' each loop. Now it's almost accurate to an hour.. just got to tweak it a little more.  

Don't do that!  There's a flaw in your Sketch that needs to be fixed.  Fixing the flaw will very likely correct the timing error.

Do you want hints so you can try to figure out the problem on your own or do you want a straight-up full description?

CrossRoads

#23
Dec 29, 2010, 07:46 am Last Edit: Dec 29, 2010, 07:55 am by CrossRoads Reason: 1
I'd like the full-up please, as I have been helping him & I am stumped as to why it would be so inaccurate. I have a 10 minute countdown timer, which I have not timed for accuracy, but works on similar principal (only rolling backwards required a lot more current state checking to decide if the digits should roll or not).
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

Coding Badly

#24
Dec 29, 2010, 08:22 am Last Edit: Dec 29, 2010, 08:29 am by bcook Reason: 1

No sweat!  Well, I say "no sweat" assuming that the gunk that I try to pass as English prose can be deciphered.  :D

Start at the top of loop.  The current time is recorded and compared to the previous recorded time checking for a difference of 10 ms...

 currentmillis = millis();  // read the time.
 if (currentmillis - previousmillis >= interval) // 10 milliseconds have gone by
 {


Slowly scroll through loop.  You'll notice that there is quite a lot of code.  Lots of integer math (which is the correct choice).  Lots of if's.  Then there are some calls to digitalWrite and shiftOut.  

How long do you think all that code takes to run?  Let's say it takes 10.001 ms.

We go back around to the top of loop.  The current time is recorded and compared to the previous recorded time checking for a difference of 10ms.  Because the code in loop took 10.001 ms, the condition is already true and hundredths is incremented.  But what happens to the 0.001 ms?  Because the "line in the sand" is moved up to the current time...

   previousmillis  = currentmillis;  // save the time for the next comparison

...the 0.001 ms is lost.  On a single pass through loop, the 0.001 ms is irrelevant.  But, after 1000 passes through loop, the error adds up to a full millisecond.  Add enough of those tiny errors and eventually we get to 2 seconds lost out of 5 minutes.

The problem is much easier to visualize if you assume that loop takes 11 milliseconds (or more; like 2 full seconds) to run.  In that case, we add one to hundredths but we should have added 1.1.

Does that make sense?




There are at least three solutions to the problem...

The first is to reduce the amount of code in loop.  Changing the appropriate variables (like hundredths) to byte will certainly help.  But, without very careful analysis and testing we cannot prove that we've solved the problem.

The second solution is to loop while "currentmillis - previousmillis >= interval" is true AND change "previousmillis  = currentmillis;" to "previousmillis += interval;".  This solution is very likely to fully succeed.  If there is still too much work to do, in the worst case, the display will "jump ahead" by one one-hundredth when the error accumulates to a full 10 ms.

The final solution is remove the incrementing code and calculate the various parts each pass through loop.  This solution is nice because the display is guarenteed to always be as accurate as possible at the bottom of loop.  But, it requires a bunch of fiddly math.

Coding Badly


These may help...
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1265097594
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1255643562

CrossRoads

Thanks Paul.
I didn't think it was possible for that little bit of code take more than 10mS - I mean that's
.01/(1/16,000,000) which is 160,000 clock cycles - altho I couldn't estiate pi recently :-) . I'll run the code with an oscilloscope tomorrow and see how long it really is.
(I check every 250ms in my 10:00, 3:00, 1:00 count down timer).

I see your point about only incrementing by 1 hundredth  when the elapsed interval may have been 10.001 mS, and then losing that .001.
It seems that perhaps previousMillis should be increased by the amount of (currentMillis-previousMillis) to catch the full amount if time elapsed, vs just interval, which is fixed at 10mS. Unless that is what you are suggesting and I just didn't read it that way. I suppose we could check to see if the difference is more than 2x interval and then throw in the occasional increment by 2 to catch up also.
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

CrossRoads

Thanks also Coding, I think you snuck that in while I was typing my other response and looking up what += did.
Got some testing to try tomorrow ...
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

Coding Badly

Quote
Thanks Paul.

Paul?  Who's Paul?

Quote
I didn't think it was possible for that little bit of code take more than 10mS - I mean that's
.01/(1/16,000,000) which is 160,000 clock cycles

Well, I could easily be wrong.  For example: I don't know what shiftOut does or how it's coded.  I assumed that it may wait for an acknowledgement.

Quote
altho I couldn't estiate pi recently

Ouch!

Quote
I'll run the code with an oscilloscope tomorrow and see how long it really is.

Definately a good plan before changing anything.

Quote
(I check every 250ms in my 10:00, 3:00, 1:00 count down timer).

Do you have a timer that's showing an error?

Quote
I see your point about only incrementing by 1 hundredth  when the elapsed interval may have been 10.001 mS, and then losing that .001.
It seems that perhaps previousMillis should be increased by the amount of (currentMillis-previousMillis) to catch the full amount if time elapsed, vs just interval, which is fixed at 10mS.

Work through by hand using a large loop time (like 20 ms).  I believe you'll find the error is still there.

Quote
I suppose we could check to see if the difference is more than 2x interval and then throw in the occasional increment by 2 to catch up also.

That's essentially how the second suggestion works...

Code: [Select]
 currentmillis = millis();  // read the time.

 while (currentmillis - previousmillis >= interval)
 {
   // 10 milliseconds (or more) have gone by.

   // Your code to update the counters goes here.

   previousmillis += interval;
 }

CrossRoads

"Paul?  Who's Paul?"

Sorry about that coding,  I had PaulS on my mind, it was 2:00AM, had been reading stuff both of you had posted. Mostly just tired I think.
My pi estimate - I was thinking 22/7, but had 3, 22, 7 going around in my head, somehow got 7/3 = 2.x so it seemed to make sense in distracted way :-)

I didn't check my timer against anything - it only does minutes, seconds, and I fixed things like when time started it didn't immediately jump down 1 second, but took a second to get there. Or if you stopped time as soon as it changed a second, it didn't immediately drop to the next second when restarted. Those were obvious. I didn't get down into the 10s of mS like this code is doing.

Now, where'd my USB scope end up ...
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

Go Up