Hardware components | ||||||
![]() |
| × | 4 | |||
![]() |
| × | 1 | |||
![]() |
| × | 8 | |||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
| × | 1 | ||||
![]() |
| × | 3 |
Just bought my Arduino. Trying out stuff, up to and including publishing here.
Learned to use different parts, real time programming, create a library, use Fritzing to create the drawings, add a new part, publish a project. A clock just seemed a nice project to do this.
The clock using a single 4 digit-7 segment display. It uses three buttons set time, alarm and a stopwatch with lap times.
The three buttons are:
Mode: switches between clock, time set, alarm set and stopwatch
Set: shows minutes/seconds in clock mode, snooze when alarm sounds, increments hours and minutes when setting the time or the alarm, start/stop the stopwatch
Function: toggles alarm on/off in clock mode, accepts the entered time in time and alarm set mode, shows lap time and reset in stopwatch mode
/*
Digital alarm clock with stopwatch function
*/
#include <Display4x7.h> // Display library is required
/*
Define ports to control the 4x7 segment display
*/
const int digit[] = {13, 12, 11, 10};
const int segment[] = {2, 3, 4, 5, 6, 7, 8, 9};
const int BuzzerPin = A4;
const int ButtonPin = A5;
/*
Other definitions
*/
const unsigned int OnTime = 250; // When blinking, this is the time the display is on
const unsigned int OffTime = 250; // When blinking, this is the time the display is off
const int Step = 10; // Milliseconds resolution of the clock (1/100 sec)
const unsigned int Refresh = 6500; // Display refresh rate in microseconds
unsigned long MaxSec = 86400U; // Seconds in a day
const unsigned int am_pattern = 0x02; // AM indicated by top left segment
const unsigned int pm_pattern = 0x04; // PM indicated by lower left segment
const unsigned int h_pattern = 0x2EU; // Smaal 'h' in clock mode setting
const unsigned int r_pattern = 0x0AU; // Small 'r' in clock mode setting
/*
Button settings
Using a 4 resistor ladder, 1 pin can read 3 buttons
Nominal values:
- button 1: 0V (read value: 0)
- button 2: 2.5V (read value: 512)
- button 3: 3.33V (read value: 682)
- No button: 3.75V (read value: 768)
*/
unsigned long debounceTime = 500; // millisec before new button input is scanned
const int Button1 = 256; // Threshold for button 1
const int Button2 = 597; // Threshold for button 2
const int Button3 = 725; // Threshold for button 3
/*
The clock has different modes, switched by the "Mode" button.
Button actions depend on the clock's current mode
*/
enum Mode { Clock,
ClockSet, ClockModeSet,
AlarmSet,
Stopwatch
};
/*
Action resulting from button presses during clock/alarm time set
*/
enum SetAction { None, NextMode, Increment, Shift, Accept };
/*
The clock's operating modes
*/
enum Mode mode = Clock; // Current clock mode
bool Hr24 = true; // 24-hour mode, false for 12-hour
bool Seconds = false; // Showing hours.minutes, true if showing minutes.seconds instead
bool AlarmOn = false; // Alarm on
unsigned AlarmFreq = 400; // Alarm sound frequency
unsigned long SnoozeTime = 420UL; // Snooze time (7 minutes = 420 seconds)
unsigned long MaxAlarmMs = 600000UL; // Max alarm time (10 minutes = 600000 millisecs)
/*
Variables that hold the clock's actual state
*/
Display4x7 d(digit, segment, Refresh);// The display
unsigned long sc = 0; // The clock's value
unsigned long alarm = 0; // The alarm time in seconds
bool alarmSounding = false; // True if the buzzer is on
unsigned long alarmAutoOff = 0; // Indicates when the alarm automatically switches off
unsigned long snooze = 0; // Time to end snooze period
unsigned long stopwatch = 0; // Stopwatch time
bool stopwatchRunning = false; // Indicates that the stopwatch is running
unsigned long laptime; // Lap time when using stopwatch
bool showLapTime = false; // Show lap time
unsigned long clockset = 0; // Used for setting the clock
unsigned long alarmset = 0; // Used for setting the alarm
bool setMin = false; // During time set: changing hours, true if changing minutes
int hs = 0; // 1/100 seconds counter
unsigned long count = 0; // Next 1/100 slice counter
unsigned long debounce = 0; // Debounce button counter
void setup()
{
pinMode(ButtonPin, INPUT);
pinMode(BuzzerPin, OUTPUT);
d.setup(); // Set-up display stuff
d.setDot(0, AlarmOn);
}
/*
Show the time in hours and minutes on the display, taking 12/24
hour mode into account
*/
void showTime(unsigned int hours, unsigned int minutes, bool isAm)
{
int h = hours % (Hr24 ? 24 : 12); // Convert to 12 or 24 hours
h = (!Hr24 && h == 0 ? 12 : h); // 0 becomes 12 in 12-hr mode
d.setPair(h, minutes); // Display values
if (h < 10 || !Hr24) // Blank leading zero and set AM/PM
{
d.setSegments(3, // This concerns digit 3 (leftmost)
(h > 9 ? Display4x7::Digit[1] // 1
: 0x00) // else blank
| // combine with
(Hr24 ? 0x00 : (isAm ? am_pattern // AM
: 0x04))); // else PM
}
}
/*
Set time using buttons:
- Mode button (1): change mode, setting cancelled
- Set button (2): increment time by 1 minute or 1 hour
- Function button (3): accept input, set next
Blinking digits show hours/minutes change
*/
enum SetAction setTime(bool& min, int button, unsigned long& set)
{
enum SetAction action = None; // Action taken based on button press
switch (button)
{
case 1: // Mode button
action = NextMode;
break;
case 2: // Set button
set += min ? 60UL : 3600UL; // Increment by 1 hour or 1 minute
if (set > MaxSec)
{
set -= MaxSec; // Roll-over to zero
}
action = Increment;
break;
case 3: // Function button
action = min ? Accept : Shift; // If changing hours, shift to minutes
min = !min; // Shift to minutes and adjust blinking digits
d.setBlink(0, min ? OnTime : 0, min ? OffTime : 0);
d.setBlink(1, min ? OnTime : 0, min ? OffTime : 0);
d.setBlink(2, !min ? OnTime : 0, min ? OffTime : 0);
d.setBlink(3, !min ? OnTime : 0, min ? OffTime : 0);
break;
}
showTime(set / 3600UL, (set / 60) % 60, set < MaxSec / 2);
return action;
}
/*
* Switch the alarm sound on and off
*/
void switchAlarm(bool on, unsigned long currentMs)
{
alarmSounding = on;
if (on)
{
alarmAutoOff = currentMs + MaxAlarmMs; // Alarm stops automatically after predefined time
}
else
{
noTone(BuzzerPin);
}
}
void loop()
{
unsigned long tm = millis(); // Current time in milliseconds
int button = 0; // Button pressed
d.update(); // Display update in every loop
if (tm > debounce) // Prevent button read twice very fast
{
int buttonVal = analogRead(ButtonPin);
debounce = tm + 2UL; // Give the ADC time to settle
if (buttonVal < Button1) // Convert button voltage to button number
{
button = 1;
}
else if (buttonVal < Button2)
{
button = 2;
}
else if (buttonVal < Button3)
{
button = 3;
}
if (button > 0)
{
debounce = tm + debounceTime; // Stop reading buttons for some time
}
}
unsigned int mn = (sc / 60) % 60; // Calculate hours and minutes from time in seconds
unsigned int hr = (sc / 3600U) % (Hr24 ? 24 : 12);
bool am = sc < MaxSec / 2; // AM is first half of 24 hours
int hrsmode; // Used in setting 12/24 hours mode
unsigned int p[] = { 0, 0, 0, 0 }; // Used to display 12/24 hr
int dot; // Dot to show in stopwatch
bool dotOn; // Dot state (true is on, false is off)
unsigned long st; // Stopwatch time to display, adjusted to display size
hr = (!Hr24 && hr == 0 ? 12 : hr); // In 12 hour clock mode 0 is set to 12
if (tm > count) // The next 1/100 time slice is reached
{
hs++; // Next 1/100 second
if (stopwatchRunning) // Stopwatch is also increased by 1/100 second when running
{
stopwatch++;
}
count += Step; // Set the value for the next 1/100 second
if (hs == 100) // Next second reached
{
sc++; // Increment time with 1 second
if (sc >= MaxSec)
{
sc = 0; // Roll-over to zero at midnight
}
hs = 0; // Reset 1/100 second counter
if (AlarmOn && sc == snooze) // Alarm should sound now
{
switchAlarm(true, tm); // ... so switch it on
}
}
}
if (alarmSounding) // The buzzer is on, make it beep
{
if (hs == 0)
{
tone(BuzzerPin, AlarmFreq, 500);
}
if (tm > alarmAutoOff) // Auto off time reached?
{
switchAlarm(false, tm); // If yes, switch off the alarm
AlarmOn = false;
}
}
switch (mode) // Determine display based on clock's current mode
{
case Clock: // Regular clock mode
if (Seconds)
{
d.setPair(mn, sc % 60); // Show minutes/seconds
}
else
{
showTime(hr, mn, am); // Show hours/minutes
}
d.setDot(2, hs < 50); // Blink dot between hours and minutes, 1 time/second
switch (button) // Handle buttons in clock mode
{
case 1:
clockset = sc; // Clock set starts at current time
setMin = false; // Start setting the hours and make hours blink
d.setBlink(2, OnTime, OffTime);
d.setBlink(3, OnTime, OffTime);
d.setDot(0, false); // Alarm indicator is off when setting clock time
d.setDot(2, true); // Hour/minute separator is on
mode = ClockSet; // Next mode is clock set
break;
case 2: // Set button
if (alarmSounding) // If the alarm is on,
{
switchAlarm(false, tm);
snooze = sc + SnoozeTime; // New alarm time is SnoozeTime later
if (snooze > MaxSec)
{
snooze -= MaxSec; // Roll-over
}
}
else
{
Seconds = !Seconds; // Toggle between hours/minutes and minutes/seconds display
}
break;
case 3: // Function button
if (alarmSounding) // If the alarm is on,
{
switchAlarm(false, tm); // ... switch it off now
AlarmOn = false;
}
else
{
AlarmOn = !AlarmOn; // ... otherwise toggle alarm on/off
}
d.setDot(0, AlarmOn); // Update alarm indicator
break;
}
break;
case ClockSet: // Clock time set mode
switch (setTime(setMin, button, clockset)) // Handle time set
{
case NextMode: // Mode button pressed, ignore new time
setMin = false; // Adjust digit blinking to hours (for alarm set)
d.setBlink(2, OnTime, OffTime);
d.setBlink(3, OnTime, OffTime);
d.setDot(0, true); // Alarm indicator is on when setting alarm time
d.setDot(2, true); // Hour/minute separator is on
mode = AlarmSet; // Next mode is alarm set
break;
case Accept: // New time set
d.setBlinkAll(OnTime, OffTime); // Switch on blinking for clock mode set
sc = clockset; // Current time becomes the time set
mode = ClockModeSet; // Now move to 12/24-hour mode selction
break;
}
break;
case ClockModeSet: // Clock mode (12/24-hour) setting
hrsmode = (Hr24 ? 2 : 1); // Set-up pattern to display "12hr" or "24hr"
p[3] = (Display4x7::Digit[hrsmode] << 1);
p[2] = (Display4x7::Digit[2 * hrsmode] << 1);
p[1] = h_pattern;
p[0] = r_pattern;
d.setPatterns(p); // Display the current clock mode
switch (button) // Handle buttons for clock mode set
{
case 1: // Mode or function button do not change current setting
case 3:
setMin = false; // Reset time set to hours
d.setBlinkAll(); // Switch off all blinking
d.setDot(0, AlarmOn); // Update alarm indicator
mode = Clock; // Back to clock mode
break;
case 2: // The set button toggles the clock mode between 12 and 24 hours
Hr24 = !Hr24;
break;
}
break;
case AlarmSet: // Alarm time set mode
switch (setTime(setMin, button, alarmset)) // Handle time set
{
case NextMode: // Mode button pressed, ignore new alarm time
d.setBlinkAll(); // Blinking off
setMin = false; // Reset time set to hours
mode = Stopwatch; // Next mode is stopwatch
break;
case Accept: // New alarm time set
d.setBlinkAll(); // Blinking off
snooze = alarm = alarmset; // Set alarm and snooze times to new time
AlarmOn = true; // Alarm is switched on
d.setDot(0, AlarmOn); // Update alarm indicator
mode = Clock; // Back to clock mode
break;
}
break;
case Stopwatch: // Stopwatch mode
dot = 2; // Determine where to place decimal point, start with 1/100 seconds
st = (showLapTime ? laptime : stopwatch);
dotOn = stopwatchRunning ? (hs < 50) : true; // DP is only on in the first half second
if (st > 9999) // If stopwatch time goes beyond 9999/100 seconds...
{
dot = 1; // ... show only 1/10 of seconds
st /= 10UL;
}
if (st > 9999) // If stopwatch time goes beyond 9999/10 seconds...
{
dot = 0; // ... show seconds
st /= 10UL;
}
d.setNumber(st, false); // Show the stopwatch time
if (st < 10) // If still under 0.1 seconds...
{
d.setDigit(1, 0); // ... set digit after DP to 0
}
d.setDot(2, dotOn && dot == 2); // Show the decimal point at the right place
d.setDot(1, dotOn && dot == 1);
d.setDot(0, dotOn && dot == 0);
switch (button)
{
case 1: // Mode button pressed
d.setDot(1, false); // Update DP and alarm indicator for clock mode
d.setDot(0, AlarmOn);
mode = Clock; // Back to clock mode (stopwatch keeps running if on)
break;
case 2: // Set button pressed
stopwatchRunning = !stopwatchRunning; // Start/stop the stopwatch
break;
case 3: // Function button pressed
if (stopwatchRunning || showLapTime)
{
laptime = stopwatch; // When running or showing lap time,
showLapTime = !showLapTime; // ... toggle lap time/stopwatch time
}
else
{
stopwatch = laptime = 0; // When showing stopped time, reset stopwatch
}
break;
}
break;
}
}
Display library
ArduinoNo preview (download only).
Display4x7.h
C Header File#ifndef _display4x7_h_
#define _display4x7_h_
#include <Arduino.h>
#include <ShiftReg.h>
#define DfltOnTime 5500
#define MinusPattern 0x02
class Display4x7
{
public:
static const unsigned int Digit[];
Display4x7(const int digitPorts[], const int segmentPorts[], int onTime = DfltOnTime, bool commonCathode = true);
Display4x7(const int digitPorts[], const ShiftReg& shift, int onTime = DfltOnTime, bool commonCathode = true);
void setDigit(int digit, unsigned int number);
void setNumber(long number, bool leadingZeroes);
void setNumber(unsigned int number, unsigned int base = 10, bool leadingZeroes = false);
void setPair(unsigned int x, unsigned int y, unsigned int base = 10);
void setDigits(const int digits[]);
void setSegments(int digit, unsigned int segments);
void setPattern(int digit, unsigned int pattern);
void setPatterns(unsigned int pattern[]);
void setPattern(int digit, const bool pattern[]);
void setDot(int digit, bool on);
void setBlink(int digit, unsigned int onTime = 0, unsigned int offTime = 0);
void setBlinkAll(unsigned int onTime = 0, unsigned int offTime = 0);
void setup();
void update();
protected:
void init(const int digitPorts[], int onTime, bool commonCathode);
void doNum(bool sign, unsigned int num, unsigned int base, bool leading);
int _currentDigit = 0;
unsigned long _refresh = 0UL;
int _onTime;
int _segOn;
int _segOff;
int _dgtOn;
int _dgtOff;
int _seg[8];
int _dgt[4];
static bool _buf[][8];
unsigned long _blinkOn[4];
unsigned long _blinkOff[4];
bool _blinkState[4];
unsigned long _blinkTimer[4];
const ShiftReg* _shift = NULL;
};
#endif
Display4x7.cpp
C/C++#include <Display4x7.h>
const unsigned int Display4x7::Digit[] =
{
0x7EU, // 0
0x30U, // 1
0x6DU, // 2
0x79U, // 3
0x33U, // 4
0x5BU, // 5
0x5FU, // 6
0x70U, // 7
0x7FU, // 8
0x7BU, // 9
0x77U, // A
0x1FU, // b
0x4EU, // C
0x3DU, // d
0x4FU, // E
0x47U // F
};
bool Display4x7::_buf[][8] =
{
{ false, false, false, false, false, false, false, false},
{ false, false, false, false, false, false, false, false},
{ false, false, false, false, false, false, false, false},
{ false, false, false, false, false, false, false, false}
};
// Display4x7::Display4x7(const int digitPorts[], const int segmentPorts[], int onTime, bool commonCathode)
// {
// int d;
// int s;
// for (d = 0; d < 4; d++)
// {
// _dgt[d] = digitPorts[d];
// }
// for (s = 0; s < 8; s++)
// {
// _seg[s] = segmentPorts[s];
// }
// for (d = 0; d < 4; d++)
// {
// for (s = 0; s < 8; s++)
// {
// _buf[d][s] = false;
// }
// }
// _onTime = onTime;
// _segOn = _dgtOff = commonCathode ? HIGH : LOW;
// _segOff = _dgtOn = commonCathode ? LOW : HIGH;
// }
Display4x7::Display4x7(const int digitPorts[], const int segmentPorts[], int onTime, bool commonCathode)
{
int d;
int s;
// for (d = 0; d < 4; d++)
// {
// _dgt[d] = digitPorts[d];
// }
for (s = 0; s < 8; s++)
{
_seg[s] = segmentPorts[s];
}
init(digitPorts, onTime, commonCathode);
// for (d = 0; d < 4; d++)
// {
// for (s = 0; s < 8; s++)
// {
// _buf[d][s] = false;
// }
// }
// _onTime = onTime;
// _segOn = _dgtOff = commonCathode ? HIGH : LOW;
// _segOff = _dgtOn = commonCathode ? LOW : HIGH;
}
Display4x7::Display4x7(const int digitPorts[], const ShiftReg& shift, int onTime, bool commonCathode)
{
_shift = &shift;
init(digitPorts, onTime, commonCathode);
}
void Display4x7::init(const int digitPorts[], int onTime, bool commonCathode)
{
int d;
int s;
for (d = 0; d < 4; d++)
{
_dgt[d] = digitPorts[d];
}
// for (s = 0; s < 8; s++)
// {
// _seg[s] = segmentPorts[s];
// }
for (d = 0; d < 4; d++)
{
for (s = 0; s < 8; s++)
{
_buf[d][s] = false;
}
}
_onTime = onTime;
_segOn = _dgtOff = commonCathode ? HIGH : LOW;
_segOff = _dgtOn = commonCathode ? LOW : HIGH;
}
void Display4x7::setDigit(int digit, unsigned int number)
{
setSegments(digit, Digit[number]);
}
void Display4x7::doNum(bool sign, unsigned int num, unsigned int base, bool leading)
{
unsigned int n = num;
int i;
for (i = 0; i < 4 && (i == 0 || n > 0); i++)
{
int d = n % base;
n /= base;
setSegments(i, Digit[d]);
}
for (; leading && i < (sign ? 3 : 4); i++)
{
setDigit(i, 0);
}
if (sign)
{
setPattern(i++, MinusPattern);
}
for (; i < 4; i++)
{
setSegments(i, 0x00);
}
}
void Display4x7::setNumber(long number, bool leadingZeroes)
{
int n = number < 0 ? -number : number;
int i;
doNum(number < 0, n, 10, leadingZeroes);
}
void Display4x7::setNumber(unsigned int number, unsigned int base, bool leadingZeroes)
{
doNum(false, number, base, leadingZeroes);
}
void Display4x7::setPair(unsigned int x, unsigned int y, unsigned int base)
{
int xy[] = { 0,0,0,0 };
xy[0] = y % base;
xy[1] = y / base;
xy[2] = x % base;
xy[3] = x / base;
setDigits(xy);
}
void Display4x7::setDigits(const int digits[])
{
for (int d = 0; d < 4; d++)
{
setSegments(d, Digit[digits[d]]);
}
}
void Display4x7::setSegments(int digit, unsigned int segments)
{
unsigned int p = segments;
for (int s = 0; s < 7; s++)
{
_buf[digit][6 - s] = ((p & 0x0001) > 0);
p >>= 1;
}
}
void Display4x7::setPattern(int digit, const bool pattern[])
{
for (int s = 0; s < 8; s++)
{
_buf[digit][s] = pattern[s];
}
}
void Display4x7::setPattern(int digit, unsigned int pattern)
{
unsigned int p = pattern;
for (int s = 0; s < 8; s++)
{
_buf[digit][7 - s] = ((p & 0x0001) > 0);
p >>= 1;
}
}
void Display4x7::setPatterns(unsigned int pattern[])
{
for (int d = 0; d < 4; d++)
{
setPattern(d, pattern[d]);
}
}
void Display4x7::setDot(int digit, bool on)
{
_buf[digit][7] = on;
}
void Display4x7::setBlink(int digit, unsigned int onTime, unsigned int offTime)
{
_blinkOn[digit] = onTime * 1000UL; // usec
_blinkOff[digit] = offTime * 1000UL;
if (onTime == 0UL || offTime == 0UL)
{
_blinkOn[digit] = 0UL; // Blinking off
}
_blinkTimer[digit] = 0;
_blinkState[digit] = true;
}
void Display4x7::setBlinkAll(unsigned int onTime, unsigned int offTime)
{
for (int digit = 0; digit < 4; digit++)
{
setBlink(digit, onTime, offTime);
}
}
void Display4x7::setup()
{
int i;
for (i = 0; i < 4; i++)
{
pinMode(_dgt[i], OUTPUT);
}
if (_shift == NULL)
{
for (i = 0; i < 8; i++)
{
pinMode(_seg[i], OUTPUT);
}
}
else
{
_shift->setup();
_shift->reset();
_shift->enableOutput();
}
setBlinkAll();
_currentDigit = 0;
_refresh = 0;
}
void Display4x7::update()
{
unsigned long time = micros();
for (int d = 0; d < 4; d++)
{
if (_blinkOn[d] > 0)
{
if (time >= _blinkTimer[d])
{
_blinkState[d] = !_blinkState[d];
_blinkTimer[d] = time + (_blinkState[d] ? _blinkOn[d] : _blinkOff[d]);
}
}
}
if (time >= _refresh)
{
digitalWrite(_dgt[_currentDigit], _dgtOff);
_currentDigit++;
if (_currentDigit >= 4)
{
_currentDigit = 0;
}
for (int s = 0; s < 8; s++)
{
if (_shift == NULL)
{
digitalWrite(_seg[s], Display4x7::_buf[_currentDigit][s] ? _segOn : _segOff);
}
else
{
_shift->loadBit(Display4x7::_buf[_currentDigit][7 - s]);
}
}
if (_shift != NULL)
{
_shift->latch();
}
digitalWrite(_dgt[_currentDigit], _blinkState[_currentDigit] ? _dgtOn : _dgtOff);
_refresh += _onTime;
}
}
keywords.txt
Plain text#######################################
# Syntax Coloring Map For Display4x7
#######################################
#######################################
# Class (KEYWORD1)
#######################################
Display4x7 KEYWORD1 Display4x7Library
#######################################
# Methods and Functions (KEYWORD2)
#######################################
# method names in Display4x7
setDigit KEYWORD2
setNumber KEYWORD2
setPair KEYWORD2
setDigits KEYWORD2
setSegments KEYWORD2
setPattern KEYWORD2
setPatterns KEYWORD2
setDot KEYWORD2
setBlink KEYWORD2
setBlinkAll KEYWORD2
setup KEYWORD2
update KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################
DfltOnTime LITERAL1
MinusPattern LITERAL1
library.properties
Propertiesname=Display4x7
version=1.0.0
author=Qixaz
maintainer=Qixaz (www.qixaz.com)
sentence=4x7 segment display control
paragraph=Library to send numbers and other patterns to a 4x7 segment display
category=Communication
url=http://www.qixaz.com
architectures=*
dot_a_linkage=true
Comments
Please log in or sign up to comment.