Adds LRA haptic support (open-loop only for now).

custom
Robin Howard 1 year ago
parent e39754ba10
commit 7c075cf5b7
  1. 67
      src/drivers/haptics.cpp
  2. 24
      src/drivers/include/haptics.hpp
  3. 6
      src/drivers/include/nvs.hpp
  4. 14
      src/drivers/nvs.cpp
  5. 10
      src/system_fsm/booting.cpp

@ -28,24 +28,55 @@ namespace drivers {
static constexpr char kTag[] = "haptics";
static constexpr uint8_t kHapticsAddress = 0x5A;
Haptics::Haptics() {
Haptics::Haptics(const std::variant<ErmMotor, LraMotor>& motor) {
PowerUp();
// Put into ERM Open Loop:
// (§8.5.4.1 Programming for ERM Open-Loop Operation)
// - Turn off N_ERM_LRA first
WriteRegister(Register::kControl1,
static_cast<uint8_t>(RegisterDefaults::kControl1) &
(~ControlMask::kNErmLra));
// - Turn on ERM_OPEN_LOOP
WriteRegister(Register::kControl3,
static_cast<uint8_t>(RegisterDefaults::kControl3) |
ControlMask::kErmOpenLoop);
// Set library
// TODO(robin): try the other libraries and test response. C is marginal, D
// too much?
WriteRegister(Register::kWaveformLibrary, static_cast<uint8_t>(kDefaultLibrary));
// TODO: Break out into helper functions
// TODO: Use LRA closed loop instead, loading in calibration data from NVS, or
// running calibration + storing the result first if necessary.
if (std::holds_alternative<ErmMotor>(motor)) {
ESP_LOGI(kTag, "Setting up ERM motor...");
// Put into ERM Open Loop:
// (§8.5.4.1 Programming for ERM Open-Loop Operation)
// - Turn off N_ERM_LRA first
WriteRegister(Register::kFeedbackControl,
static_cast<uint8_t>(RegisterDefaults::kFeedbackControl) &
(~ControlMask::kNErmLra));
// - Turn on ERM_OPEN_LOOP
WriteRegister(Register::kControl3,
static_cast<uint8_t>(RegisterDefaults::kControl3) |
ControlMask::kErmOpenLoop);
// Set library
// TODO(robin): try the other libraries and test response. C is marginal, D
// too much?
WriteRegister(Register::kWaveformLibrary, static_cast<uint8_t>(kDefaultErmLibrary));
} else if (std::holds_alternative<LraMotor>(motor)) {
ESP_LOGI(kTag, "Setting up LRA motor...");
// TODO:
// auto lraInit = std::get<LraMotor>(motor);
// Put into LRA Open Loop:
// (§8.5.4.1 Programming for LRA Open-Loop Operation)
// - Turn on N_ERM_LRA first
WriteRegister(Register::kFeedbackControl,
static_cast<uint8_t>(RegisterDefaults::kFeedbackControl) &
(ControlMask::kNErmLra));
// - Turn on LRA_OPEN_LOOP
WriteRegister(Register::kControl3,
static_cast<uint8_t>(RegisterDefaults::kControl3) |
ControlMask::kLraOpenLoop);
// Set library; only option is the LRA one for, well, LRA motors.
WriteRegister(Register::kWaveformLibrary, static_cast<uint8_t>(Library::LRA));
}
// Set mode (internal trigger, on writing 1 to Go register)
WriteRegister(Register::kMode, static_cast<uint8_t>(Mode::kInternalTrigger));
@ -94,13 +125,13 @@ auto Haptics::SetWaveformEffect(Effect effect) -> void {
auto Haptics::TourEffects() -> void {
TourEffects(Effect::kFirst, Effect::kLast, kDefaultLibrary);
TourEffects(Effect::kFirst, Effect::kLast, kDefaultErmLibrary);
}
auto Haptics::TourEffects(Library lib) -> void {
TourEffects(Effect::kFirst, Effect::kLast, lib);
}
auto Haptics::TourEffects(Effect from, Effect to) -> void {
TourEffects(from, to, kDefaultLibrary);
TourEffects(from, to, kDefaultErmLibrary);
}
auto Haptics::TourEffects(Effect from, Effect to, Library lib) -> void {
ESP_LOGI(kTag, "With library #%u...", static_cast<uint8_t>(lib));

@ -11,13 +11,24 @@
#include <mutex>
#include <optional>
#include <string>
#include <variant>
namespace drivers {
typedef std::monostate ErmMotor;
struct LraMotor {
// TODO: fill out with calibration data from https://www.ti.com/lit/ds/symlink/drv2605l.pdf
bool hi;
};
class Haptics {
public:
static auto Create() -> Haptics* { return new Haptics(); }
Haptics();
static auto Create(const std::variant<ErmMotor, LraMotor>& motor)
-> Haptics* {
return new Haptics(motor);
}
Haptics(const std::variant<ErmMotor, LraMotor>& motor);
~Haptics();
// Not copyable or movable.
@ -169,11 +180,12 @@ class Haptics {
B = 2, // 3V, Rise: 40-60ms, Brake: 5-15ms
C = 3, // 3V, Rise: 60-80ms, Brake: 10-20ms
D = 4, // 3V, Rise: 100-140ms, Brake: 15-25ms
E = 5 // 3V, Rise: >140ms, Brake: >30ms
// 6 is LRA-only, 7 is 4.5V+
E = 5, // 3V, Rise: >140ms, Brake: >30ms
LRA = 6
// 7 is 4.5V+
};
static constexpr Library kDefaultLibrary = Library::C;
static constexpr Library kDefaultErmLibrary = Library::C;
auto PowerDown() -> void;
auto Reset() -> void;
@ -216,8 +228,10 @@ class Haptics {
struct ControlMask {
// FeedbackControl
static constexpr uint8_t kNErmLra = 0b10000000;
// Control3
static constexpr uint8_t kErmOpenLoop = 0b00100000;
static constexpr uint8_t kLraOpenLoop = 0b00000001;
};
// §8.6 Register Map

@ -68,6 +68,7 @@ class NvsStorage {
auto Read() -> void;
auto Write() -> bool;
// Hardware Compatibility
auto LockPolarity() -> bool;
auto LockPolarity(bool) -> void;
@ -76,6 +77,10 @@ class NvsStorage {
auto DisplaySize(std::pair<std::optional<uint16_t>, std::optional<uint16_t>>)
-> void;
auto HapticMotorIsErm() -> bool;
auto HapticMotorIsErm(bool) -> void;
// /Hardware Compatibility
auto PreferredBluetoothDevice() -> std::optional<bluetooth::MacAndName>;
auto PreferredBluetoothDevice(std::optional<bluetooth::MacAndName>) -> void;
@ -130,6 +135,7 @@ class NvsStorage {
Setting<uint8_t> lock_polarity_;
Setting<uint16_t> display_cols_;
Setting<uint16_t> display_rows_;
Setting<uint8_t> haptic_motor_type_;
Setting<uint8_t> brightness_;
Setting<uint8_t> sensitivity_;

@ -39,6 +39,7 @@ static constexpr char kKeyScrollSensitivity[] = "scroll";
static constexpr char kKeyLockPolarity[] = "lockpol";
static constexpr char kKeyDisplayCols[] = "dispcols";
static constexpr char kKeyDisplayRows[] = "disprows";
static constexpr char kKeyHapticMotorType[] = "hapticmtype";
static constexpr char kKeyDbAutoIndex[] = "dbautoindex";
static auto nvs_get_string(nvs_handle_t nvs, const char* key)
@ -166,6 +167,7 @@ NvsStorage::NvsStorage(nvs_handle_t handle)
lock_polarity_(kKeyLockPolarity),
display_cols_(kKeyDisplayCols),
display_rows_(kKeyDisplayRows),
haptic_motor_type_(kKeyHapticMotorType),
brightness_(kKeyBrightness),
sensitivity_(kKeyScrollSensitivity),
amp_max_vol_(kKeyAmpMaxVolume),
@ -188,6 +190,7 @@ auto NvsStorage::Read() -> void {
lock_polarity_.read(handle_);
display_cols_.read(handle_);
display_rows_.read(handle_);
haptic_motor_type_.read(handle_),
brightness_.read(handle_);
sensitivity_.read(handle_);
amp_max_vol_.read(handle_);
@ -205,6 +208,7 @@ auto NvsStorage::Write() -> bool {
lock_polarity_.write(handle_);
display_cols_.write(handle_);
display_rows_.write(handle_);
haptic_motor_type_.write(handle_),
brightness_.write(handle_);
sensitivity_.write(handle_);
amp_max_vol_.write(handle_);
@ -243,6 +247,16 @@ auto NvsStorage::LockPolarity(bool p) -> void {
lock_polarity_.set(p);
}
auto NvsStorage::HapticMotorIsErm() -> bool {
std::lock_guard<std::mutex> lock{mutex_};
return haptic_motor_type_.get().value_or(0) > 0;
}
auto NvsStorage::HapticMotorIsErm(bool p) -> void {
std::lock_guard<std::mutex> lock{mutex_};
haptic_motor_type_.set(p);
}
auto NvsStorage::DisplaySize()
-> std::pair<std::optional<uint16_t>, std::optional<uint16_t>> {
std::lock_guard<std::mutex> lock{mutex_};

@ -62,6 +62,10 @@ auto Booting::entry() -> void {
sServices->nvs(
std::unique_ptr<drivers::NvsStorage>(drivers::NvsStorage::OpenSync()));
// HACK: tell the unit that it has an ERM motor (we will likely default to
// LRAs in future, but all the current units in the field use ERMs.)
sServices->nvs().HapticMotorIsErm(true);
// HACK: fix up the switch polarity on newer dev units
// sServices->nvs().LockPolarity(false);
@ -85,7 +89,11 @@ auto Booting::entry() -> void {
sServices->samd(std::unique_ptr<drivers::Samd>(drivers::Samd::Create()));
sServices->touchwheel(
std::unique_ptr<drivers::TouchWheel>{drivers::TouchWheel::Create()});
sServices->haptics(std::make_unique<drivers::Haptics>());
sServices->haptics(std::make_unique<drivers::Haptics>(
sServices->nvs().HapticMotorIsErm()
? std::variant<drivers::ErmMotor, drivers::LraMotor>(
drivers::ErmMotor())
: drivers::LraMotor()));
auto adc = drivers::AdcBattery::Create();
sServices->battery(std::make_unique<battery::Battery>(

Loading…
Cancel
Save