aboutsummaryrefslogtreecommitdiffstats
path: root/src/drivers/Hrs3300.cpp
blob: 9c77975e83298e64cfc1e7dc1e54cf5b5652bee0 (plain)
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
/*
  SPDX-License-Identifier: LGPL-3.0-or-later
  Original work Copyright (C) 2020 Daniel Thompson
  C++ port Copyright (C) 2021 Jean-François Milants
*/

#include "drivers/Hrs3300.h"
#include <algorithm>
#include <nrf_gpio.h>

#include <FreeRTOS.h>
#include <task.h>
#include <nrf_log.h>

using namespace Pinetime::Drivers;

namespace {
  static constexpr uint8_t ledDriveCurrentValue = 0x2f;
}

/** Driver for the HRS3300 heart rate sensor.
 * Original implementation from wasp-os : https://github.com/wasp-os/wasp-os/blob/master/wasp/drivers/hrs3300.py
 *
 * Experimentaly derived changes to improve signal/noise (see comments below) - Ceimour
 */
Hrs3300::Hrs3300(TwiMaster& twiMaster, uint8_t twiAddress) : twiMaster {twiMaster}, twiAddress {twiAddress} {
}

void Hrs3300::Init() {
  nrf_gpio_cfg_input(30, NRF_GPIO_PIN_NOPULL);

  Disable();
  vTaskDelay(100);

  // HRS disabled, 50ms wait time between ADC conversion period, current 12.5mA
  WriteRegister(static_cast<uint8_t>(Registers::Enable), 0x50);

  // Current 12.5mA and low nibble 0xF.
  // Note: Setting low nibble to 0x8 per the datasheet results in
  // modulated LED driver output. Setting to 0xF results in clean,
  // steady output during the ADC conversion period.
  WriteRegister(static_cast<uint8_t>(Registers::PDriver), ledDriveCurrentValue);

  // HRS and ALS both in 15-bit mode results in ~50ms LED drive period
  // and presumably ~50ms ADC conversion period.
  WriteRegister(static_cast<uint8_t>(Registers::Res), 0x77);

  // Gain set to 1x
  WriteRegister(static_cast<uint8_t>(Registers::Hgain), 0x00);
}

void Hrs3300::Enable() {
  NRF_LOG_INFO("ENABLE");
  auto value = ReadRegister(static_cast<uint8_t>(Registers::Enable));
  value |= 0x80;
  WriteRegister(static_cast<uint8_t>(Registers::Enable), value);

  WriteRegister(static_cast<uint8_t>(Registers::PDriver), ledDriveCurrentValue);
}

void Hrs3300::Disable() {
  NRF_LOG_INFO("DISABLE");
  auto value = ReadRegister(static_cast<uint8_t>(Registers::Enable));
  value &= ~0x80;
  WriteRegister(static_cast<uint8_t>(Registers::Enable), value);

  WriteRegister(static_cast<uint8_t>(Registers::PDriver), 0);
}

Hrs3300::PackedHrsAls Hrs3300::ReadHrsAls() {
  constexpr Registers dataRegisters[] =
    {Registers::C1dataM, Registers::C0DataM, Registers::C0DataH, Registers::C1dataH, Registers::C1dataL, Registers::C0dataL};
  // Calculate smallest register address
  constexpr uint8_t baseOffset = static_cast<uint8_t>(*std::min_element(std::begin(dataRegisters), std::end(dataRegisters)));
  // Calculate largest address to determine length of read needed
  // Add one to largest relative index to find the length
  constexpr uint8_t length = static_cast<uint8_t>(*std::max_element(std::begin(dataRegisters), std::end(dataRegisters))) - baseOffset + 1;

  Hrs3300::PackedHrsAls res;
  uint8_t buf[length];
  auto ret = twiMaster.Read(twiAddress, baseOffset, buf, length);
  if (ret != TwiMaster::ErrorCodes::NoError) {
    NRF_LOG_INFO("READ ERROR");
  }
  // hrs
  uint8_t m = static_cast<uint8_t>(Registers::C0DataM) - baseOffset;
  uint8_t h = static_cast<uint8_t>(Registers::C0DataH) - baseOffset;
  uint8_t l = static_cast<uint8_t>(Registers::C0dataL) - baseOffset;
  // There are two extra bits (17 and 18) but they are not read here
  // as resolutions >16bit aren't practically useful (too slow) and
  // all hrs values throughout InfiniTime are 16bit
  res.hrs = (buf[m] << 8) | ((buf[h] & 0x0f) << 4) | (buf[l] & 0x0f);

  // als
  m = static_cast<uint8_t>(Registers::C1dataM) - baseOffset;
  h = static_cast<uint8_t>(Registers::C1dataH) - baseOffset;
  l = static_cast<uint8_t>(Registers::C1dataL) - baseOffset;
  res.als = ((buf[h] & 0x3f) << 11) | (buf[m] << 3) | (buf[l] & 0x07);

  return res;
}

void Hrs3300::WriteRegister(uint8_t reg, uint8_t data) {
  auto ret = twiMaster.Write(twiAddress, reg, &data, 1);
  if (ret != TwiMaster::ErrorCodes::NoError)
    NRF_LOG_INFO("WRITE ERROR");
}

uint8_t Hrs3300::ReadRegister(uint8_t reg) {
  uint8_t value;
  auto ret = twiMaster.Read(twiAddress, reg, &value, 1);
  if (ret != TwiMaster::ErrorCodes::NoError)
    NRF_LOG_INFO("READ ERROR");
  return value;
}