11#include " heartratetask/HeartRateTask.h"
22#include < drivers/Hrs3300.h>
33#include < components/heartrate/HeartRateController.h>
4+ #include < limits>
45
56using namespace Pinetime ::Applications;
67
78namespace {
89 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+ }
916}
1017
1118std::optional<TickType_t> HeartRateTask::BackgroundMeasurementInterval () const {
@@ -24,9 +31,40 @@ bool HeartRateTask::BackgroundMeasurementNeeded() const {
2431 return xTaskGetTickCount () - lastMeasurementTime >= backgroundPeriod.value ();
2532};
2633
27- TickType_t HeartRateTask::CurrentTaskDelay () const {
34+ TickType_t HeartRateTask::CurrentTaskDelay () {
2835 auto backgroundPeriod = BackgroundMeasurementInterval ();
2936 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+ };
3068 switch (state) {
3169 case States::Disabled:
3270 return portMAX_DELAY;
@@ -43,8 +81,11 @@ TickType_t HeartRateTask::CurrentTaskDelay() const {
4381 return 0 ;
4482 case States::BackgroundMeasuring:
4583 case States::ForegroundMeasuring:
46- return Pinetime::Controllers::Ppg::deltaTms ;
84+ return CalculateSleepTicks () ;
4785 }
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;
4889}
4990
5091HeartRateTask::HeartRateTask (Drivers::Hrs3300& heartRateSensor,
@@ -57,7 +98,7 @@ void HeartRateTask::Start() {
5798 messageQueue = xQueueCreate (10 , 1 );
5899 controller.SetHeartRateTask (this );
59100
60- if (pdPASS != xTaskCreate (HeartRateTask::Process, " Heartrate" , 500 , this , 0 , &taskHandle)) {
101+ if (pdPASS != xTaskCreate (HeartRateTask::Process, " Heartrate" , 500 , this , 1 , &taskHandle)) {
61102 APP_ERROR_HANDLER (NRF_ERROR_NO_MEM);
62103 }
63104}
@@ -130,6 +171,7 @@ void HeartRateTask::Work() {
130171
131172 if (state == States::ForegroundMeasuring || state == States::BackgroundMeasuring) {
132173 HandleSensorData ();
174+ count++;
133175 }
134176 }
135177}
@@ -145,6 +187,7 @@ void HeartRateTask::StartMeasurement() {
145187 ppg.Reset (true );
146188 vTaskDelay (100 );
147189 measurementSucceeded = false ;
190+ count = 0 ;
148191 measurementStartTime = xTaskGetTickCount ();
149192}
150193
0 commit comments