Skip to content

Commit a889a72

Browse files
author
Louis Pearson
committed
Implement lap tracking
1 parent 3f524e5 commit a889a72

4 files changed

Lines changed: 151 additions & 76 deletions

File tree

src/components/stopwatch/StopWatchController.cpp

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,80 @@
44

55
using namespace Pinetime::Controllers;
66

7-
// StopWatch::StopWatch() {}
7+
namespace {
8+
TickType_t calculateDelta(const TickType_t startTime, const TickType_t currentTime) {
9+
TickType_t delta = 0;
10+
// Take care of overflow
11+
if (startTime > currentTime) {
12+
delta = 0xffffffff - startTime;
13+
delta += (currentTime + 1);
14+
} else {
15+
delta = currentTime - startTime;
16+
}
17+
return delta;
18+
}
19+
}
20+
21+
StopWatch::StopWatch() {
22+
clear();
23+
}
824

9-
// StopWatch::init() {}
25+
// State Change
1026

11-
void StopWatch::start(uint32_t start) {
27+
void StopWatch::start(TickType_t start) {
1228
currentState = StopWatchStates::Running;
1329
startTime = start;
1430
}
1531

16-
// void StopWatch::lap(uint32_t lapEnd);
17-
18-
void StopWatch::pause(uint32_t end) {
32+
void StopWatch::pause(TickType_t end) {
1933
currentState = StopWatchStates::Paused;
20-
timeElapsedPreviously += end - startTime;
34+
timeElapsedPreviously += calculateDelta(startTime, end);
2135
}
2236

2337
void StopWatch::clear() {
2438
currentState = StopWatchStates::Cleared;
2539
timeElapsedPreviously = 0;
40+
41+
for (int i = 0; i < LAP_CAPACITY; i++) {
42+
laps[i].count = 0;
43+
laps[i].time = 0;
44+
}
45+
lapCount = 0;
46+
lapHead = 0;
47+
}
48+
49+
// Lap
50+
51+
void StopWatch::pushLap(TickType_t lapEnd) {
52+
laps[lapHead].time = lapEnd;
53+
laps[lapHead].count = lapCount + 1;
54+
lapCount += 1;
55+
lapHead = lapCount % LAP_CAPACITY;
2656
}
2757

58+
uint32_t StopWatch::getLapNum() {
59+
if (lapCount < LAP_CAPACITY)
60+
return lapCount;
61+
else
62+
return LAP_CAPACITY;
63+
}
64+
65+
uint32_t StopWatch::getLapCount() {
66+
return lapCount;
67+
}
68+
69+
LapInfo_t *StopWatch::lastLap(uint32_t lap) {
70+
if (lap >= LAP_CAPACITY || lap >= lapCount || lapCount == 0) {
71+
// Return "empty" LapInfo_t
72+
return &emptyLapInfo;
73+
}
74+
// Index backwards
75+
uint32_t index = ((lapHead + LAP_CAPACITY) - lap) % LAP_CAPACITY;
76+
return &laps[index];
77+
}
78+
79+
// Data acess
80+
2881
uint32_t StopWatch::getStart() {
2982
return startTime;
3083
}
Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
#pragma once
22

33
#include <cstdint>
4-
// #include "portmacro_cmsis.h"
54
#include "FreeRTOS.h"
65

6+
#define LAP_CAPACITY 2
7+
78
namespace Pinetime {
89
namespace System {
910
class SystemTask;
@@ -12,29 +13,55 @@ namespace Pinetime {
1213

1314
enum class StopWatchStates { Cleared, Running, Paused };
1415

16+
struct LapInfo_t {
17+
uint32_t count = 0; // Used to label the lap
18+
TickType_t time = 0; // delta time from beginning of stopwatch
19+
};
20+
1521
class StopWatch {
1622
public:
17-
StopWatch() = default;
23+
StopWatch();
1824

19-
// void init();
20-
void start(uint32_t start);
21-
// void lap(uint32_t lapEnd);
22-
void pause(uint32_t end);
25+
// StopWatch functionality and data
26+
void start(TickType_t start);
27+
void pause(TickType_t end);
2328
void clear();
2429

25-
uint32_t getStart();
26-
uint32_t getElapsedPreviously();
30+
TickType_t getStart();
31+
TickType_t getElapsedPreviously();
32+
33+
// Lap functionality
34+
35+
/// Only the latest laps are stored, the lap count is saved until reset
36+
void pushLap(TickType_t lapEnd);
37+
38+
/// Returns actual count of stored laps
39+
uint32_t getLapNum();
40+
41+
/// Returns lapCount
42+
uint32_t getLapCount();
43+
44+
/// Indexes into lap history, with 0 being the latest lap.
45+
/// If the lap is unavailable, count and time will be 0. If there is a
46+
/// real value, count should be above 0
47+
LapInfo_t *lastLap(uint32_t lap=0);
2748

2849
bool isRunning();
2950
bool isCleared();
3051
bool isPaused();
3152

3253
private:
33-
StopWatchStates currentState = StopWatchStates::Cleared;
54+
// Current state of stopwatch
55+
StopWatchStates currentState = StopWatchStates::Cleared;
3456
// Start time of current duration
35-
TickType_t startTime;
57+
TickType_t startTime = 0;
3658
// How much time was elapsed before current duration
37-
TickType_t timeElapsedPreviously = 0;
59+
TickType_t timeElapsedPreviously = 0;
60+
// Stores lap times
61+
LapInfo_t laps[LAP_CAPACITY];
62+
LapInfo_t emptyLapInfo = { .count = 0, .time = 0 };
63+
uint32_t lapCount = 0;
64+
uint32_t lapHead = 0;
3865
};
3966
}
4067
}

src/displayapp/screens/StopWatch.cpp

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,7 @@ StopWatch::StopWatch(DisplayApp* app, System::SystemTask& systemTask, Controller
5151
dateTimeController {dateTimeController},
5252
stopWatchController {stopWatchController},
5353
timeElapsed {},
54-
currentTimeSeparated {},
55-
lapBuffer {},
56-
lapNr {} {
54+
currentTimeSeparated {} {
5755

5856
// Running time
5957
time = lv_label_create(lv_scr_act(), nullptr);
@@ -121,6 +119,12 @@ StopWatch::StopWatch(DisplayApp* app, System::SystemTask& systemTask, Controller
121119
lv_label_set_text_fmt(msecTime, "%02d", currentTimeSeparated.hundredths);
122120
lv_obj_set_state(btnStopLap, LV_STATE_DEFAULT);
123121
lv_obj_set_state(txtStopLap, LV_STATE_DEFAULT);
122+
} else {
123+
reset();
124+
}
125+
126+
if (stopWatchController.getLapCount() > 0) {
127+
refreshLaps();
124128
}
125129
}
126130

@@ -139,8 +143,7 @@ void StopWatch::reset() {
139143

140144
lv_label_set_text(lapOneText, "");
141145
lv_label_set_text(lapTwoText, "");
142-
lapBuffer.clearBuffer();
143-
lapNr = 0;
146+
144147
lv_obj_set_state(btnStopLap, LV_STATE_DISABLED);
145148
lv_obj_set_state(txtStopLap, LV_STATE_DISABLED);
146149
}
@@ -179,6 +182,35 @@ void StopWatch::Refresh() {
179182
}
180183
}
181184

185+
void StopWatch::refreshLaps() {
186+
Pinetime::Controllers::LapInfo_t
187+
// Latest lap
188+
*lap1 = stopWatchController.lastLap(),
189+
// Second latest lap
190+
*lap2 = stopWatchController.lastLap(1);
191+
192+
if (lap1->count != 0) {
193+
TimeSeparated_t laptime = convertTicksToTimeSegments(lap1->time);
194+
lv_label_set_text_fmt(
195+
lapOneText,
196+
"#%2d %2d:%02d.%02d",
197+
lap1->count,
198+
laptime.mins,
199+
laptime.secs,
200+
laptime.hundredths);
201+
}
202+
if (lap2->count != 0) {
203+
TimeSeparated_t laptime = convertTicksToTimeSegments(lap2->time);
204+
lv_label_set_text_fmt(
205+
lapTwoText,
206+
"#%2d %2d:%02d.%02d",
207+
lap2->count,
208+
laptime.mins,
209+
laptime.secs,
210+
laptime.hundredths);
211+
}
212+
}
213+
182214
void StopWatch::playPauseBtnEventHandler(lv_event_t event) {
183215
if (event != LV_EVENT_CLICKED) {
184216
return;
@@ -198,15 +230,19 @@ void StopWatch::stopLapBtnEventHandler(lv_event_t event) {
198230
}
199231
// If running, then this button is used to save laps
200232
if (stopWatchController.isRunning()) {
201-
lapBuffer.addLaps(currentTimeSeparated);
202-
lapNr++;
203-
if (lapBuffer[1]) {
204-
lv_label_set_text_fmt(
205-
lapOneText, "#%2d %2d:%02d.%02d", (lapNr - 1), lapBuffer[1]->mins, lapBuffer[1]->secs, lapBuffer[1]->hundredths);
206-
}
207-
if (lapBuffer[0]) {
208-
lv_label_set_text_fmt(lapTwoText, "#%2d %2d:%02d.%02d", lapNr, lapBuffer[0]->mins, lapBuffer[0]->secs, lapBuffer[0]->hundredths);
209-
}
233+
// lapBuffer.addLaps(currentTimeSeparated);
234+
// lapNr++;
235+
// if (lapBuffer[1]) {
236+
// lv_label_set_text_fmt(
237+
// lapOneText, "#%2d %2d:%02d.%02d", (lapNr - 1), lapBuffer[1]->mins, lapBuffer[1]->secs, lapBuffer[1]->hundredths);
238+
// }
239+
// if (lapBuffer[0]) {
240+
// lv_label_set_text_fmt(lapTwoText, "#%2d %2d:%02d.%02d", lapNr, lapBuffer[0]->mins, lapBuffer[0]->secs, lapBuffer[0]->hundredths);
241+
// }
242+
TickType_t currentTime = stopWatchController.getElapsedPreviously() + calculateDelta(stopWatchController.getStart(), xTaskGetTickCount());
243+
stopWatchController.pushLap(currentTime);
244+
245+
refreshLaps();
210246
} else if (stopWatchController.isPaused()) {
211247
stopWatchController.clear();
212248
reset();

src/displayapp/screens/StopWatch.h

Lines changed: 3 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "Screen.h"
44
#include "components/datetime/DateTimeController.h"
5+
#include "components/stopwatch/StopWatchController.h"
56
#include "../LittleVgl.h"
67

78
#include "FreeRTOS.h"
@@ -12,54 +13,12 @@
1213

1314
namespace Pinetime::Applications::Screens {
1415

15-
enum class States { Init, Running, Halted };
16-
1716
struct TimeSeparated_t {
1817
int mins;
1918
int secs;
2019
int hundredths;
2120
};
2221

23-
// A simple buffer to hold the latest two laps
24-
template <int N> struct LapTextBuffer_t {
25-
LapTextBuffer_t() : buffer {}, currentSize {}, capacity {N}, head {-1} {
26-
}
27-
28-
void addLaps(const TimeSeparated_t& timeVal) {
29-
head++;
30-
head %= capacity;
31-
buffer[head] = timeVal;
32-
33-
if (currentSize < capacity) {
34-
currentSize++;
35-
}
36-
}
37-
38-
void clearBuffer() {
39-
buffer = {};
40-
currentSize = 0;
41-
head = -1;
42-
}
43-
44-
TimeSeparated_t* operator[](std::size_t idx) {
45-
// Sanity check for out-of-bounds
46-
if (idx >= 0 && idx < capacity) {
47-
if (idx < currentSize) {
48-
// This transformation is to ensure that head is always pointing to index 0.
49-
const auto transformed_idx = (head - idx) % capacity;
50-
return (&buffer[transformed_idx]);
51-
}
52-
}
53-
return nullptr;
54-
}
55-
56-
private:
57-
std::array<TimeSeparated_t, N> buffer;
58-
uint8_t currentSize;
59-
uint8_t capacity;
60-
int8_t head;
61-
};
62-
6322
class StopWatch : public Screen {
6423
public:
6524
StopWatch(DisplayApp* app,
@@ -76,6 +35,7 @@ namespace Pinetime::Applications::Screens {
7635
void reset();
7736
void start();
7837
void pause();
38+
void refreshLaps() ;
7939

8040
private:
8141
Pinetime::System::SystemTask& systemTask;
@@ -84,8 +44,7 @@ namespace Pinetime::Applications::Screens {
8444

8545
TickType_t timeElapsed;
8646
TimeSeparated_t currentTimeSeparated; // Holds Mins, Secs, millisecs
87-
LapTextBuffer_t<2> lapBuffer;
88-
int lapNr = 0;
47+
8948
lv_obj_t *dateTime, *time, *msecTime, *btnPlayPause, *btnStopLap, *txtPlayPause, *txtStopLap;
9049
lv_obj_t *lapOneText, *lapTwoText;
9150

0 commit comments

Comments
 (0)