-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path03-tm1637-counter.ino
More file actions
283 lines (246 loc) · 8.23 KB
/
03-tm1637-counter.ino
File metadata and controls
283 lines (246 loc) · 8.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
// 10,000 hour desktop counter using arduino mega and 2x tm1637 modules
// Usage: plug in power to start the timer automatically,
// press push button to pause and save data into EEPROM
// Loads stored time from EEPROM after reboot
// **user must save time manually by pausing the timer with the pushbutton before shutting down**
// TODO - brownout detection and automatically save to EEPROM
// currently takes 3.3ms to perform EEPROM write, not sure if that's enough time to perform an analog read
// and write before the MCU goes brown,
// probably look into a coin cell backup with timer for this
#include <Arduino.h>
#include <TM1637Display.h>
#include <EEPROM.h>
#define DEBUG_ENABLED 0
#if DEBUG_ENABLED
#define debug_println(str) Serial.println(str)
#define debug_print(str) Serial.print(str)
#else
#define debug_println(str)
#define debug_print(str)
#endif
#define TEST_PIN 62
// Module connection pins (Digital Pins)
#define A_CLK 61 // A -Minutes & Seconds
#define A_DIO 60
#define B_CLK 55 // B - Hours
#define B_DIO 54
#define BUTTON_PIN 2
#define DEBOUNCE_DELAY 50
#define BUTTON_PRESSED 1
#define DEBOUNCE_FALSE 0
#define TEST_DELAY 200// tm1637 - The amount of time (in milliseconds) between blinking tests
// Global variables
uint8_t stopwatch_on = 1;
unsigned long SW_PREV_ms = 0; // stopwatch store current millis() time
const long SW_INTERVAL_SECOND = 1000; // interval in ms
// TODO load these from EEPROM
#define SIGNATURE_EEADDR 0
#define EXPECTED_SIGNATURE 0x2A
#define SW_EEADDR_BASE 4
#define MINUTES_EEADDR 8
#define HOURS_EEADDR 12
typedef struct { // initialized to reset values, read from EEPROM at runtime
uint32_t seconds;
uint32_t minutes;
uint32_t hours;
} SW_COUNTER_t;
SW_COUNTER_t stopwatch = { 0, 0, 0}; // Initial values if EEPROM is not set
uint8_t fill[] = { 0xff, 0xff, 0xff, 0xff };
uint8_t blank[] = { 0x00, 0x00, 0x00, 0x00 };
TM1637Display tm1637_minsec(A_CLK, A_DIO);
TM1637Display tm1637_hour(B_CLK, B_DIO);
// Function Decl
int update_button();
int second_has_passed();
int count_dig_return_pos(int x, int y);
void clear_eeprom();
void init_eeprom_struct();
void save_to_eeprom_struct();
void read_eeprom_struct();
void toggle_pin(int x);
void display_blink2();
void display_init_test2();
//////////////////////////////////////////////////////////////
void setup() {
#if DEBUG_ENABLED
Serial.begin(9600); Serial.println("Started Serial");
while (!Serial); // wait until Serial is ready
clear_eeprom();
#endif
// Check and load from EEPROM
int signature = EEPROM.read(SIGNATURE_EEADDR);
if (signature != EXPECTED_SIGNATURE) {
init_eeprom_struct(); // will also write values to eeprom
debug_println("EEPROM uninitialized, initialized");
} else {
read_eeprom_struct();
debug_print("Loaded HH:MM:SS from EEPROM: "); debug_print(stopwatch.hours); debug_print(":"); debug_print(stopwatch.minutes); debug_print(":"); debug_println(stopwatch.seconds);
}
// Setup button as input pullup
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(TEST_PIN, OUTPUT);
debug_println("Starting timer...");
// Initialize Brightness and Test Displays
display_init_test2(&tm1637_minsec, &tm1637_hour);
}
//////////////////////////////////////////////////////////////
void loop() {
// Check for button press
int buttonPress = update_button();
if (buttonPress) { // Start/stop timer
debug_println("Button Press");
if (stopwatch_on) {
save_to_eeprom_struct();
} else {
second_has_passed(); // set timer currentMillis to zero to start again
}
stopwatch_on = !stopwatch_on;
display_blink2(&tm1637_minsec, &tm1637_hour, 1); update_stopwatch(); // hack to re-display digits
}
if (stopwatch_on && second_has_passed()) {
stopwatch.seconds += 1;
if (stopwatch.seconds % 60 == 0) {
stopwatch.minutes += 1;
if (stopwatch.minutes % 60 == 0) {
stopwatch.hours += 1;
if (stopwatch.hours % 10000 == 0) {
tm1637_minsec.clear();
while(1) {
tm1637_minsec.setSegments(blank);
tm1637_hour.setSegments(blank);
delay(TEST_DELAY);
tm1637_minsec.setSegments(fill);
tm1637_hour.setSegments(fill);
delay(TEST_DELAY);
}
}
}
}
debug_println(stopwatch.seconds);
update_stopwatch();
}
}
// Calculate if second has passed
int second_has_passed() {
unsigned long now = millis();
if ( now - SW_PREV_ms > SW_INTERVAL_SECOND) {
// update
SW_PREV_ms = now;
return true;
}
return false;
}
// Non blocking debounce function that returns 1 on a Unpressed to Pressed transition
// Requires: Button pin as input pullup
int update_button() {
static int lastButtonState = 1; // unpressed = HIGH
static unsigned long lastDebounceTime = 0; // overflow is defined for unsigned
const int debounceDelay = DEBOUNCE_DELAY;
int buttonState = digitalRead(BUTTON_PIN);
static int flagDebouncing;
// Reset/Start debounce timer if state change
if (!flagDebouncing && buttonState != lastButtonState) {
// start debounce timer
lastDebounceTime = millis();
flagDebouncing = 1;
}
// Debounce timer finished, accept current button state
if ( flagDebouncing && (millis() - lastDebounceTime > debounceDelay)) {
// Remove thiese
// lastDebounceTime += millis();
flagDebouncing = 0;
if (buttonState != lastButtonState) {
// Only return button pressed for Unpressed -> Pressed
if (buttonState == 0) {// low is pressed
lastButtonState = buttonState; // update for next iteration
return BUTTON_PRESSED;
}
} else {
lastButtonState = buttonState; // update for next iteration
return DEBOUNCE_FALSE;
}
}
// Fall through to here if timer is not finished or we are not debouncing
if (!flagDebouncing) {
// Update last button state outside of the debounce check when not debouncing
lastButtonState = buttonState;
} else {
// do nothing (wait), since we are debouncing
}
return DEBOUNCE_FALSE;
}
/*
**************** EEPROM Functions *****************************
*/
void clear_eeprom() {
SW_COUNTER_t emptyStruct = { 0, 0, 0};
EEPROM.put(SIGNATURE_EEADDR, 0); // to use custom initial values
EEPROM.put(SW_EEADDR_BASE, emptyStruct);
}
void init_eeprom_struct() {
save_to_eeprom_struct();
EEPROM.put(SIGNATURE_EEADDR, EXPECTED_SIGNATURE);
}
void read_eeprom_struct() {
EEPROM.get(SW_EEADDR_BASE, stopwatch); // read struct from start address
}
void save_to_eeprom_struct() {
// benchmarking: 3.49 ms for (3) x .put commands
EEPROM.put(SW_EEADDR_BASE, stopwatch);
}
/*
**************** TM1637 and Clock Functions *****************************
*/
// Uses global variables to update clock display
void update_stopwatch() {
// Update Minutes and Seconds Display
tm1637_minsec.showNumberDecEx(stopwatch.seconds % 60, 0, true, 2, 2);
tm1637_minsec.showNumberDecEx(stopwatch.minutes % 60, 1 << 6, true, 2, 0);
// Update Hours Display
// leave leading blanks, "1"hr starts at pos 3, "10"hr at pos 2, etc.
if (stopwatch.hours) {
int pos = count_dig_return_pos(stopwatch.hours, 3);
int len = 3 - pos + 1;
tm1637_hour.showNumberDec(stopwatch.hours, false, len, pos);
}
}
// Returns starting position for right aligned integer (max position = 3)
int count_dig_return_pos(int num, int startPos)
{
num /= 10;
int i = startPos;
while (num != 0) {
num /= 10;
i--;
}
return i;
}
/*
**************** TM1637 Display Functions *****************************
*/
void display_init_test2(TM1637Display* pDisplay1, TM1637Display* pDisplay2) {
pDisplay1->setBrightness(0x0f);
pDisplay2->setBrightness(0x0f);
display_blink2(pDisplay1,pDisplay2, 4);
// On/Off test
}
void display_blink2(TM1637Display* pDisplay1, TM1637Display* pDisplay2, int blinks){
for (int k = 0; k < blinks; k++) {
pDisplay1->setSegments(blank);
pDisplay2->setSegments(blank);
delay(TEST_DELAY);
pDisplay2->setSegments(fill);
pDisplay1->setSegments(fill);
delay(TEST_DELAY);
}
// Leave blank for next write
pDisplay1->setSegments(blank);
pDisplay2->setSegments(blank);
}
/*
**************** MISC / DEBUG *****************************
*/
// For scope use
void toggle_pin(int PIN_NO){
digitalWrite(PIN_NO, !digitalRead(PIN_NO));
}