Go Down

Topic: 4 Digit Seven Segment Display Countdown Timer - Minutes to (Read 17448 times) previous topic - next topic

filmie

Hey guys, I'm an indie filmmaker designing a "bomb countdown timer" to be a prop in a short film I'm producing.

I've built a timer using the wiring instructions & code here:
http://www.hobbytronics.co.uk/tutorials-code/arduino-tutorials/arduino-4digit-7segment

The timer is working as it should according to the instructions, but the only problem is it counts down in seconds, not minutes and seconds (e.g."110" instead of "1:50"), which is the norm for the bomb timers on film/tv.

I am totally new to both Arduino and electronics in general and I feel like I have jumped in at the deep end. If anyone could have a look at the code and make any suggestions as to how I might change the output on the display to minutes & seconds instead of just seconds that would be a massive help.

Thanks, (and pre-emptive apologies if I am asking for way more help than a newbie is entitled to)

Scott

Code & Wiring:
Code: [Select]
/*
6-13-2011
Spark Fun Electronics 2011
Nathan Seidle

This code is public domain but you buy me a beer if you use this and we meet
someday (Beerware license).

4 digit 7 segment display:
http://www.sparkfun.com/products/9483
Datasheet:
http://www.sparkfun.com/datasheets/Components/LED/7-Segment/YSD-439AR6B-35.pdf

This is an example of how to drive a 7 segment LED display from an ATmega
without the use of current limiting resistors. This technique is very common
but requires some knowledge of electronics - you do run the risk of dumping
too much current through the segments and burning out parts of the display.
If you use the stock code you should be ok, but be careful editing the
brightness values.

This code should work with all colors (red, blue, yellow, green) but the
brightness will vary from one color to the next because the forward voltage
drop of each color is different. This code was written and calibrated for the
red color.

This code will work with most Arduinos but you may want to re-route some of
the pins.

7 segments
4 digits
1 colon
=
12 pins required for full control

*/

int digit1 = 11; //PWM Display pin 1
int digit2 = 10; //PWM Display pin 2
int digit3 = 9; //PWM Display pin 6
int digit4 = 6; //PWM Display pin 8

//Pin mapping from Arduino to the ATmega DIP28 if you need it
//http://www.arduino.cc/en/Hacking/PinMapping
int segA = A1; //Display pin 14
int segB = 3; //Display pin 16
int segC = 4; //Display pin 13
int segD = 5; //Display pin 3
int segE = A0; //Display pin 5
int segF = 7; //Display pin 11
int segG = 8; //Display pin 15

void setup() {               
  pinMode(segA, OUTPUT);
  pinMode(segB, OUTPUT);
  pinMode(segC, OUTPUT);
  pinMode(segD, OUTPUT);
  pinMode(segE, OUTPUT);
  pinMode(segF, OUTPUT);
  pinMode(segG, OUTPUT);

  pinMode(digit1, OUTPUT);
  pinMode(digit2, OUTPUT);
  pinMode(digit3, OUTPUT);
  pinMode(digit4, OUTPUT);
 
  pinMode(13, OUTPUT);
}

void loop() {
 
  //long startTime = millis();

  displayNumber(millis()/1000);

  //while( (millis() - startTime) < 2000) {
  //displayNumber(1217);
  //}
  //delay(1000); 
}

//Given a number, we display 10:22
//After running through the 4 numbers, the display is left turned off

//Display brightness
//Each digit is on for a certain amount of microseconds
//Then it is off until we have reached a total of 20ms for the function call
//Let's assume each digit is on for 1000us
//Each digit is on for 1ms, there are 4 digits, so the display is off for 16ms.
//That's a ratio of 1ms to 16ms or 6.25% on time (PWM).
//Let's define a variable called brightness that varies from:
//5000 blindingly bright (15.7mA current draw per digit)
//2000 shockingly bright (11.4mA current draw per digit)
//1000 pretty bright (5.9mA)
//500 normal (3mA)
//200 dim but readable (1.4mA)
//50 dim but readable (0.56mA)
//5 dim but readable (0.31mA)
//1 dim but readable in dark (0.28mA)

void displayNumber(int toDisplay) {
#define DISPLAY_BRIGHTNESS  500

#define DIGIT_ON  HIGH
#define DIGIT_OFF  LOW

  long beginTime = millis();

  for(int digit = 4 ; digit > 0 ; digit--) {

    //Turn on a digit for a short amount of time
    switch(digit) {
    case 1:
      digitalWrite(digit1, DIGIT_ON);
      break;
    case 2:
      digitalWrite(digit2, DIGIT_ON);
      break;
    case 3:
      digitalWrite(digit3, DIGIT_ON);
      break;
    case 4:
      digitalWrite(digit4, DIGIT_ON);
      break;
    }

    //Turn on the right segments for this digit
    lightNumber(toDisplay % 10);
    toDisplay /= 10;

    delayMicroseconds(DISPLAY_BRIGHTNESS);
    //Display digit for fraction of a second (1us to 5000us, 500 is pretty good)

    //Turn off all segments
    lightNumber(10);

    //Turn off all digits
    digitalWrite(digit1, DIGIT_OFF);
    digitalWrite(digit2, DIGIT_OFF);
    digitalWrite(digit3, DIGIT_OFF);
    digitalWrite(digit4, DIGIT_OFF);
  }

  while( (millis() - beginTime) < 10) ;
  //Wait for 20ms to pass before we paint the display again
}

//Given a number, turns on those segments
//If number == 10, then turn off number
void lightNumber(int numberToDisplay) {

#define SEGMENT_ON  LOW
#define SEGMENT_OFF HIGH

  switch (numberToDisplay){

  case 0:
    digitalWrite(segA, SEGMENT_ON);
    digitalWrite(segB, SEGMENT_ON);
    digitalWrite(segC, SEGMENT_ON);
    digitalWrite(segD, SEGMENT_ON);
    digitalWrite(segE, SEGMENT_ON);
    digitalWrite(segF, SEGMENT_ON);
    digitalWrite(segG, SEGMENT_OFF);
    break;

  case 1:
    digitalWrite(segA, SEGMENT_OFF);
    digitalWrite(segB, SEGMENT_ON);
    digitalWrite(segC, SEGMENT_ON);
    digitalWrite(segD, SEGMENT_OFF);
    digitalWrite(segE, SEGMENT_OFF);
    digitalWrite(segF, SEGMENT_OFF);
    digitalWrite(segG, SEGMENT_OFF);
    break;

  case 2:
    digitalWrite(segA, SEGMENT_ON);
    digitalWrite(segB, SEGMENT_ON);
    digitalWrite(segC, SEGMENT_OFF);
    digitalWrite(segD, SEGMENT_ON);
    digitalWrite(segE, SEGMENT_ON);
    digitalWrite(segF, SEGMENT_OFF);
    digitalWrite(segG, SEGMENT_ON);
    break;

  case 3:
    digitalWrite(segA, SEGMENT_ON);
    digitalWrite(segB, SEGMENT_ON);
    digitalWrite(segC, SEGMENT_ON);
    digitalWrite(segD, SEGMENT_ON);
    digitalWrite(segE, SEGMENT_OFF);
    digitalWrite(segF, SEGMENT_OFF);
    digitalWrite(segG, SEGMENT_ON);
    break;

  case 4:
    digitalWrite(segA, SEGMENT_OFF);
    digitalWrite(segB, SEGMENT_ON);
    digitalWrite(segC, SEGMENT_ON);
    digitalWrite(segD, SEGMENT_OFF);
    digitalWrite(segE, SEGMENT_OFF);
    digitalWrite(segF, SEGMENT_ON);
    digitalWrite(segG, SEGMENT_ON);
    break;

  case 5:
    digitalWrite(segA, SEGMENT_ON);
    digitalWrite(segB, SEGMENT_OFF);
    digitalWrite(segC, SEGMENT_ON);
    digitalWrite(segD, SEGMENT_ON);
    digitalWrite(segE, SEGMENT_OFF);
    digitalWrite(segF, SEGMENT_ON);
    digitalWrite(segG, SEGMENT_ON);
    break;

  case 6:
    digitalWrite(segA, SEGMENT_ON);
    digitalWrite(segB, SEGMENT_OFF);
    digitalWrite(segC, SEGMENT_ON);
    digitalWrite(segD, SEGMENT_ON);
    digitalWrite(segE, SEGMENT_ON);
    digitalWrite(segF, SEGMENT_ON);
    digitalWrite(segG, SEGMENT_ON);
    break;

  case 7:
    digitalWrite(segA, SEGMENT_ON);
    digitalWrite(segB, SEGMENT_ON);
    digitalWrite(segC, SEGMENT_ON);
    digitalWrite(segD, SEGMENT_OFF);
    digitalWrite(segE, SEGMENT_OFF);
    digitalWrite(segF, SEGMENT_OFF);
    digitalWrite(segG, SEGMENT_OFF);
    break;

  case 8:
    digitalWrite(segA, SEGMENT_ON);
    digitalWrite(segB, SEGMENT_ON);
    digitalWrite(segC, SEGMENT_ON);
    digitalWrite(segD, SEGMENT_ON);
    digitalWrite(segE, SEGMENT_ON);
    digitalWrite(segF, SEGMENT_ON);
    digitalWrite(segG, SEGMENT_ON);
    break;

  case 9:
    digitalWrite(segA, SEGMENT_ON);
    digitalWrite(segB, SEGMENT_ON);
    digitalWrite(segC, SEGMENT_ON);
    digitalWrite(segD, SEGMENT_ON);
    digitalWrite(segE, SEGMENT_OFF);
    digitalWrite(segF, SEGMENT_ON);
    digitalWrite(segG, SEGMENT_ON);
    break;

  case 10:
    digitalWrite(segA, SEGMENT_OFF);
    digitalWrite(segB, SEGMENT_OFF);
    digitalWrite(segC, SEGMENT_OFF);
    digitalWrite(segD, SEGMENT_OFF);
    digitalWrite(segE, SEGMENT_OFF);
    digitalWrite(segF, SEGMENT_OFF);
    digitalWrite(segG, SEGMENT_OFF);
    break;
  }
}

CrossRoads

As a hardware, that's a poorway to run a display.

"This is an example of how to drive a 7 segment LED display from an ATmega
without the use of current limiting resistors. This technique is very common
but requires some knowledge of electronics - you do run the risk of dumping
too much current through the segments and burning out parts of the display.
If you use the stock code you should be ok, but be careful editing the
brightness values."

I would rather see you add current limiting resistors and not damage your arduino.

As for the counting down in minutes/seconds, here is the method I use to determine the digits:

Your start time is defined at the beginning, say 10:00:
byte tens_minutes = 10;
byte ones_minutes = 0;
byte tens_seconds = 0;
byte ones_seconds = 0;

I'll paste in the code I've written for counting down. Basically decrement the ones_seconds by 1, when it hits 0 decrement the tens seconds, when that hits zero decrement the one_minutes, when that hits 0 decrement the tens_minutes.
Add some if statements to rollover the digits back up to 5 or 9 as needed.

Hitting the road, will post that in an hour or so.
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.

Jack Christensen

#2
Aug 17, 2012, 11:02 pm Last Edit: Aug 17, 2012, 11:04 pm by Jack Christensen Reason: 1
I might use the Time library, has variables, functions, etc. that make handling dates and time simple, http://www.arduino.cc/playground/Code/Time

Code: [Select]
//countdown timer
#include <Time.h>         //http://www.arduino.cc/playground/Code/Time

time_t t;
tmElements_t tm;
int seconds, minutes;

void setup(void)
{
    Serial.begin(115200);

    //initialize the starting time to 3 minutes and 0 seconds.
    //the rest of the elements (hours, etc., don't matter much but should
    //at least be set to something that makes sense.
    tm.Second = 0;
    tm.Minute = 3;
    tm.Day = 1;
    tm.Month = 4;
    tm.Year = CalendarYrToTm(2012);
   
    t = makeTime(tm);
}

void loop(void)
{
    seconds = second(t);
    minutes = minute(t);
   
    if (minutes < 10) Serial.print('0');    //leading zero
    Serial.print(minutes, DEC);   
    Serial.print(':');
    if (seconds < 10) Serial.print('0');    //leading zero
    Serial.println(seconds, DEC);
    --t;                                    //subtract a second
    if (seconds + minutes == 0) {
        Serial.println("BOOM!");
        while (1);                          //infinite loop, press reset to restart
    }
    delay(100);                             //10x normal speed for demo purposes
}
MCP79411/12 RTC ... "One Million Ohms" ATtiny kit ... available at http://www.tindie.com/stores/JChristensen/

CrossRoads

Here's my time checking & digit update code.
It also flashes some Colon LEDs on/off at 1/4 second intervals so have immediate visual feedback that it's running. Watching 1 second pass with nothing happening is really boring...
Code: [Select]

void loop()
{
  // check if time needs updating
  if (time_running == 1)  // time is counting down
  {
    unsigned long currentMillis = millis();  // see how long its been

    if (currentMillis - previousMillis >= interval) // more than our quarter second interval?
    {
      // save the last time we okayed time updates
      previousMillis = currentMillis;
      quarter_interval = quarter_interval+1;
      // cycle the colon state
      if (colon == 0x80)
      {
        colon = 0x00;
      }
      else
      {
        colon = 0x80;
      }
      update_time = 1;   //  enable time display to be updated

      if (quarter_interval == 4) // we hit the one second update time
      {
        quarter_interval = 0;
        // update the time digits
        // cases:
        // 0:01, final second - stop time, disable touch lights, sound buzzer
        // Tens of seconds rollover: time = x:50, x:40, x:30, x:20, x:10: decrement tens of seconds, rollover seconds to 9
        // Minutes rollover: time = 9:00, 8:00, etc. 2:00, 1:00: decrement ones of minutes, rollover tens of
        // seconds to 5, ones of seconds to 9
          // 10:00: Roll all the digits over
        // otherwise: just roll over the seconds

        // Case: Final Second
        if ((minutes_ones == 0) && (seconds_tens == 0) && (seconds_ones == 1)) // don't need minutes_tens, can't have 10:01
        {
          time_running = 0;  // stop time running
          seconds_ones = 0;  // clear the last second
          updated = 1;  // fake a Case complete flag     

          if ((time_running == 1) && (period>0) && (period<9)) { //  update period counter if was fencing time, not 1:00 or 10:00 break
            period = period +1;
            update_period=1;  // enable period display to be updated
          }
        }  // end of  if final second

        // Case: x:50, x:40, x:30, x:20, x:10
        if ((seconds_tens >0) && (seconds_ones == 0))  // case for the last tens of seconds
        {
          seconds_tens = seconds_tens - 1;  // decrement the tens
          seconds_ones = 9;  // rollover the ones
          updated = 1;
        }  // end of if 10 of seconds rollover

        // Case: 9:00, 8:00, etc 2:00, 1:00
        if ((minutes_ones > 0) && (seconds_tens == 0) && (seconds_ones == 0)) // case for the last ones of minutes
        {
          minutes_tens = 0x00;  //
          minutes_ones = minutes_ones - 1;  // decrement the minutes
          seconds_tens = 5;  // rollover the tens of seconds;
          seconds_ones = 9;  // rollover the ones of seconds;
          updated = 1;
        } // end of if minutes rollover

        // Case: starting from 10:00
        if (minutes_tens == 0x01)  // roll over all digits
        {
          minutes_tens = 0x00;  // rollover the tens of minutes
          minutes_ones = 9;  // rollover the ones of mints;
          seconds_tens = 5;  // rollover the tens of seconds;
          seconds_ones = 9;  // rollover the ones of seconds;
          updated = 1;
        } // end of if 10:00 rollover

        // General Case: just decrement the seconds
        if (updated == 0)  // nothing else updated - but don't decrement if = 0.
        {
          seconds_ones = seconds_ones - 1;
        }
        updated = 0;  // reset for next pass thru
      } // end of if quarter_interval
    } // end of reaching our interval
  } // end of if time_running

if (updated == 1){
// update the time display
updated = 0; // reset for next pass
}

Then I have another section that checks if updated == 1 and updates the display.
I use a MAX7221 to control my digits, the sparkfun code does it segment by segment.
This board here uses 4 high current shift registers to drive the segments with no multiplexing. I can sell you a bareboard for $4.50 and you can build it up without the SD card and RS232 parts.

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.

filmie

Thanks for the help, I'm still having trouble, CrossRoads are you saying that I won't be able to achieve the minutes & seconds without the board you posted?

Again, sorry for my lack of understanding.

CrossRoads

No, you certainly can.
The design you started with I believe works by lighting up one segment of each digit one at a time, cycling thru the 4 digits.
My code gives you the means to count down the time as you requested.
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.

Hi Crossroads

Does your board take the TPIC6B595 shift registers? If so, are you willing to ship one to me? (obviously for the cost and shipping!)

The reason I ask is that I have been tasked with creating a countdown timer for my FIL's train club. They essentially want a 30 min countdown timer for some efficiency runs with a large (>2") digit display. I have been putting things together and came across this post. If it does use the TPIC6B595 it would be an ideal carrier as I will be using 4 of them to run the digits, as they need 9-12v, and it would save me a lot of time with breadboards etc.

I can also crib most of your code  :) as it seems to be mostly there.

Thanks

Dave

CrossRoads

Yes, uses TCIP6B595, nice chip for driving higher voltage digits.
I have to check to see if I have any left.
You need a bare board that you populate yourself? Or a fully populated board?
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.

BulletMagnet83

#8
Sep 06, 2012, 07:18 pm Last Edit: Sep 06, 2012, 07:21 pm by BulletMagnet83 Reason: 1
I would definitely avoid the Sparkfun module, it's a piece of filth.

Filmie, is this the sort of thing you're after? If so, you can have my code under the Beerware licence ;-) Although I think Crossroads is probably doing things in a similar way to me, regarding timekeeping variables etc.

http://www.youtube.com/watch?v=FVcnsAxUx0w


Yes, uses TCIP6B595, nice chip for driving higher voltage digits.
I have to check to see if I have any left.
You need a bare board that you populate yourself? Or a fully populated board?


I'd be happy with either - fully populated is one less soldering job but bare is fine as I have the chips already so would just need to buy the discretes.

Thanks

Dave

CrossRoads

Ok, I'll see I have any left, or if it's time to order some more.
What's your timeframe?
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.

It's not particularly urgent as they have been after one for some time. It's as much of an intellectual exercise for me as for the finished product.

Dave

CrossRoads

Ok. I don't have any left. Will order, have them in 3 weeks.
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

Oh, you may be in luck! I've ordered more boards, and at the same time tracked down some previously delivered boards that hadn't been built up yet.
Hopefully will have one mailed back to me on Saturday, can send to you Tuesday/Wednesday after it arrives.
You can get parts for it here inexpensively:
16 MHz xtal, two 22pf caps
7805
two 10uf caps for power filter
eight 0.1uF caps for decoupling
'328P if you need one
5mm screw terminals
10k reset resistor
current limit resistors for LEDs
Header pins for programming -
ICSP if needed,
FTDI if needed,
Header pins & 2 jumpers to select FTDI interface if needed
None needed if you program off board and put the 328 in a socket.
Pain in the ass for debugging - preferable to at least put a bootloaded part in a socket, then use FTDI Basic, or FTDI cable, or CP2102 module with adapter cale for downloading sketches on to the board.

Can leave off the SD socket, 3.3V regulator & 2 caps, 3.3V-5V signal buffer & decoupling cap.
Can leave off MAX232 & 4 caps (for +/-10V generation) & DB9 connector.

There are 6 pins labelled Remote - you have +5, Gnd, and 4 input pins there. Can use internal pullups, and NO switches to Gnd for buttons to start your timer, etc. Will send a schematic with the board.

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.

I'm guessing I can program the 328 on my current board (has a socket'ed 328) and just put in to your board for testing? OK, a bit of messing about but it still makes for a neat solution.

Sounds good to me.

Once you confirm I'll get a list of parts ordered ready for when it arrives (the shift registers should be here today or tomorrow). Admittedly, a populated board would be simpler but happy to have a proper board (as was going to use breadboard for final solution).

Thanks

Dave




Go Up