1
1
#include " heartratetask/HeartRateTask.h"
2
2
#include < drivers/Hrs3300.h>
3
3
#include < components/heartrate/HeartRateController.h>
4
+ #include < limits>
4
5
5
6
using namespace Pinetime ::Applications;
6
7
7
8
namespace {
8
9
constexpr TickType_t backgroundMeasurementTimeLimit = 30 * configTICK_RATE_HZ;
10
+
11
+ // dividend + (divisor / 2) must be less than the max T value
12
+ template <std::unsigned_integral T>
13
+ constexpr T RoundedDiv (T dividend, T divisor) {
14
+ return (dividend + (divisor / static_cast <T>(2 ))) / divisor;
15
+ }
9
16
}
10
17
11
18
std::optional<TickType_t> HeartRateTask::BackgroundMeasurementInterval () const {
@@ -24,9 +31,40 @@ bool HeartRateTask::BackgroundMeasurementNeeded() const {
24
31
return xTaskGetTickCount () - lastMeasurementTime >= backgroundPeriod.value ();
25
32
};
26
33
27
- TickType_t HeartRateTask::CurrentTaskDelay () const {
34
+ TickType_t HeartRateTask::CurrentTaskDelay () {
28
35
auto backgroundPeriod = BackgroundMeasurementInterval ();
29
36
TickType_t currentTime = xTaskGetTickCount ();
37
+ auto CalculateSleepTicks = [&]() {
38
+ TickType_t elapsed = currentTime - measurementStartTime;
39
+
40
+ // Target system tick is the the elapsed sensor ticks multiplied by the sensor tick duration (i.e. the elapsed time)
41
+ // multiplied by the system tick rate
42
+ // Since the sensor tick duration is a whole number of milliseconds, we compute in milliseconds and then divide by 1000
43
+ // To avoid the number of milliseconds overflowing a u32, we take a factor of 2 out of the divisor and dividend
44
+ // (1024 / 2) * 65536 * 100 = 3355443200 which is less than 2^32
45
+
46
+ // Guard against future tick rate changes
47
+ static_assert ((configTICK_RATE_HZ / 2ULL ) * (std::numeric_limits<decltype (count)>::max () + 1ULL ) *
48
+ static_cast <uint64_t >((Pinetime::Controllers::Ppg::deltaTms)) <
49
+ std::numeric_limits<uint32_t >::max (),
50
+ " Overflow" );
51
+ TickType_t elapsedTarget = RoundedDiv (static_cast <uint32_t >(configTICK_RATE_HZ / 2 ) * (static_cast <uint32_t >(count) + 1U ) *
52
+ static_cast <uint32_t >((Pinetime::Controllers::Ppg::deltaTms)),
53
+ static_cast <uint32_t >(1000 / 2 ));
54
+
55
+ // On count overflow, reset both count and start time
56
+ // Count is 16bit to avoid overflow in elapsedTarget
57
+ // Count overflows every 100ms * u16 max = ~2 hours, much more often than the tick count (~48 days)
58
+ // So no need to check for tick count overflow
59
+ if (count == std::numeric_limits<decltype (count)>::max ()) {
60
+ count = 0 ;
61
+ measurementStartTime = currentTime;
62
+ }
63
+ if (elapsedTarget > elapsed) {
64
+ return elapsedTarget - elapsed;
65
+ }
66
+ return static_cast <TickType_t>(0 );
67
+ };
30
68
switch (state) {
31
69
case States::Disabled:
32
70
return portMAX_DELAY;
@@ -43,8 +81,11 @@ TickType_t HeartRateTask::CurrentTaskDelay() const {
43
81
return 0 ;
44
82
case States::BackgroundMeasuring:
45
83
case States::ForegroundMeasuring:
46
- return Pinetime::Controllers::Ppg::deltaTms ;
84
+ return CalculateSleepTicks () ;
47
85
}
86
+ // Needed to keep dumb compiler happy, this is unreachable
87
+ // Any new additions to States will cause the above switch statement not to compile, so this is safe
88
+ return portMAX_DELAY;
48
89
}
49
90
50
91
HeartRateTask::HeartRateTask (Drivers::Hrs3300& heartRateSensor,
@@ -57,7 +98,7 @@ void HeartRateTask::Start() {
57
98
messageQueue = xQueueCreate (10 , 1 );
58
99
controller.SetHeartRateTask (this );
59
100
60
- if (pdPASS != xTaskCreate (HeartRateTask::Process, " Heartrate" , 500 , this , 0 , &taskHandle)) {
101
+ if (pdPASS != xTaskCreate (HeartRateTask::Process, " Heartrate" , 500 , this , 1 , &taskHandle)) {
61
102
APP_ERROR_HANDLER (NRF_ERROR_NO_MEM);
62
103
}
63
104
}
@@ -130,6 +171,7 @@ void HeartRateTask::Work() {
130
171
131
172
if (state == States::ForegroundMeasuring || state == States::BackgroundMeasuring) {
132
173
HandleSensorData ();
174
+ count++;
133
175
}
134
176
}
135
177
}
@@ -145,6 +187,7 @@ void HeartRateTask::StartMeasurement() {
145
187
ppg.Reset (true );
146
188
vTaskDelay (100 );
147
189
measurementSucceeded = false ;
190
+ count = 0 ;
148
191
measurementStartTime = xTaskGetTickCount ();
149
192
}
150
193
0 commit comments