summaryrefslogtreecommitdiff
path: root/src/drivers/include
diff options
context:
space:
mode:
Diffstat (limited to 'src/drivers/include')
-rw-r--r--src/drivers/include/haptics.hpp316
1 files changed, 316 insertions, 0 deletions
diff --git a/src/drivers/include/haptics.hpp b/src/drivers/include/haptics.hpp
new file mode 100644
index 00000000..dfafa2eb
--- /dev/null
+++ b/src/drivers/include/haptics.hpp
@@ -0,0 +1,316 @@
+/*
+ * Copyright 2023 jacqueline <me@jacqueline.id.au>, robin <robin@rhoward.id.au>
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <initializer_list>
+#include <mutex>
+#include <optional>
+
+namespace drivers {
+
+class Haptics {
+ public:
+ static auto Create() -> Haptics* { return new Haptics(); }
+ Haptics();
+ ~Haptics();
+
+ // Not copyable or movable.
+ Haptics(const Haptics&) = delete;
+ Haptics& operator=(const Haptics&) = delete;
+
+ // See the datasheet for section references in the below comments:
+ // https://www.ti.com/lit/ds/symlink/drv2605l.pdf
+
+ // §12.1.2 Waveform Library Effects List
+ enum class Effect {
+ kStop = 0, // Sentinel/terminator Effect for the Waveform Sequence Slots
+ kStrongClick_100Pct = 1,
+ kStrongClick_60Pct = 2,
+ kStrongClick_30Pct = 3,
+ kSharpClick_100Pct = 4,
+ kSharpClick_60Pct = 5,
+ kSharpClick_30Pct = 6,
+ kSoftBump_100Pct = 7,
+ kSoftBump_60Pct = 8,
+ kSoftBump_30Pct = 9,
+ kDoubleClick_100Pct = 10,
+ kDoubleClick_60Pct = 11,
+ kTripleClick_100Pct = 12,
+ kSoftFuzz_60Pct = 13,
+ kStrongBuzz_100Pct = 14,
+ k750msAlert_100Pct = 15,
+ k1000msAlert_100Pct = 16,
+ kStrongClick1_100Pct = 17,
+ kStrongClick2_80Pct = 18,
+ kStrongClick3_60Pct = 19,
+ kStrongClick4_30Pct = 20,
+ kMediumClick1_100Pct = 21,
+ kMediumClick2_80Pct = 22,
+ kMediumClick3_60Pct = 23,
+ kSharpTick1_100Pct = 24,
+ kSharpTick2_80Pct = 25,
+ kSharpTick3_60Pct = 26,
+ kShortDoubleClickStrong1_100Pct = 27,
+ kShortDoubleClickStrong2_80Pct = 28,
+ kShortDoubleClickStrong3_60Pct = 29,
+ kShortDoubleClickStrong4_30Pct = 30,
+ kShortDoubleClickMedium1_100Pct = 31,
+ kShortDoubleClickMedium2_80Pct = 32,
+ kShortDoubleClickMedium3_60Pct = 33,
+ kShortDoubleSharpTick1_100Pct = 34,
+ kShortDoubleSharpTick2_80Pct = 35,
+ kShortDoubleSharpTick3_60Pct = 36,
+ kLongDoubleSharpClickStrong1_100Pct = 37,
+ kLongDoubleSharpClickStrong2_80Pct = 38,
+ kLongDoubleSharpClickStrong3_60Pct = 39,
+ kLongDoubleSharpClickStrong4_30Pct = 40,
+ kLongDoubleSharpClickMedium1_100Pct = 41,
+ kLongDoubleSharpClickMedium2_80Pct = 42,
+ kLongDoubleSharpClickMedium3_60Pct = 43,
+ kLongDoubleSharpTick1_100Pct = 44,
+ kLongDoubleSharpTick2_80Pct = 45,
+ kLongDoubleSharpTick3_60Pct = 46,
+ kBuzz1_100Pct = 47,
+ kBuzz2_80Pct = 48,
+ kBuzz3_60Pct = 49,
+ kBuzz4_40Pct = 50,
+ kBuzz5_20Pct = 51,
+ kPulsingStrong1_100Pct = 52,
+ kPulsingStrong2_60Pct = 53,
+ kPulsingMedium1_100Pct = 54,
+ kPulsingMedium2_60Pct = 55,
+ kPulsingSharp1_100Pct = 56,
+ kPulsingSharp2_60Pct = 57,
+ kTransitionClick1_100Pct = 58,
+ kTransitionClick2_80Pct = 59,
+ kTransitionClick3_60Pct = 60,
+ kTransitionClick4_40Pct = 61,
+ kTransitionClick5_20Pct = 62,
+ kTransitionClick6_10Pct = 63,
+ kTransitionHum1_100Pct = 64,
+ kTransitionHum2_80Pct = 65,
+ kTransitionHum3_60Pct = 66,
+ kTransitionHum4_40Pct = 67,
+ kTransitionHum5_20Pct = 68,
+ kTransitionHum6_10Pct = 69,
+ kTransitionRampDownLongSmooth1_100to0Pct = 70,
+ kTransitionRampDownLongSmooth2_100to0Pct = 71,
+ kTransitionRampDownMediumSmooth1_100to0Pct = 72,
+ kTransitionRampDownMediumSmooth2_100to0Pct = 73,
+ kTransitionRampDownShortSmooth1_100to0Pct = 74,
+ kTransitionRampDownShortSmooth2_100to0Pct = 75,
+ kTransitionRampDownLongSharp1_100to0Pct = 76,
+ kTransitionRampDownLongSharp2_100to0Pct = 77,
+ kTransitionRampDownMediumSharp1_100to0Pct = 78,
+ kTransitionRampDownMediumSharp2_100to0Pct = 79,
+ kTransitionRampDownShortSharp1_100to0Pct = 80,
+ kTransitionRampDownShortSharp2_100to0Pct = 81,
+ kTransitionRampUpLongSmooth1_0to100Pct = 82,
+ kTransitionRampUpLongSmooth2_0to100Pct = 83,
+ kTransitionRampUpMediumSmooth1_0to100Pct = 84,
+ kTransitionRampUpMediumSmooth2_0to100Pct = 85,
+ kTransitionRampUpShortSmooth1_0to100Pct = 86,
+ kTransitionRampUpShortSmooth2_0to100Pct = 87,
+ kTransitionRampUpLongSharp1_0to100Pct = 88,
+ kTransitionRampUpLongSharp2_0to100Pct = 89,
+ kTransitionRampUpMediumSharp1_0to100Pct = 90,
+ kTransitionRampUpMediumSharp2_0to100Pct = 91,
+ kTransitionRampUpShortSharp1_0to100Pct = 92,
+ kTransitionRampUpShortSharp2_0to100Pct = 93,
+ kTransitionRampDownLongSmooth1_50to0Pct = 94,
+ kTransitionRampDownLongSmooth2_50to0Pct = 95,
+ kTransitionRampDownMediumSmooth1_50to0Pct = 96,
+ kTransitionRampDownMediumSmooth2_50to0Pct = 97,
+ kTransitionRampDownShortSmooth1_50to0Pct = 98,
+ kTransitionRampDownShortSmooth2_50to0Pct = 99,
+ kTransitionRampDownLongSharp1_50to0Pct = 100,
+ kTransitionRampDownLongSharp2_50to0Pct = 101,
+ kTransitionRampDownMediumSharp1_50to0Pct = 102,
+ kTransitionRampDownMediumSharp2_50to0Pct = 103,
+ kTransitionRampDownShortSharp1_50to0Pct = 104,
+ kTransitionRampDownShortSharp2_50to0Pct = 105,
+ kTransitionRampUpLongSmooth_10to50Pct = 106,
+ kTransitionRampUpLongSmooth_20to50Pct = 107,
+ kTransitionRampUpMediumSmooth_10to50Pct = 108,
+ kTransitionRampUpMediumSmooth_20to50Pct = 109,
+ kTransitionRampUpShortSmooth_10to50Pct = 110,
+ kTransitionRampUpShortSmooth_20to50Pct = 111,
+ kTransitionRampUpLongSharp_10to50Pct = 112,
+ kTransitionRampUpLongSharp_20to50Pct = 113,
+ kTransitionRampUpMediumSharp_10to50Pct = 114,
+ kTransitionRampUpMediumSharp_20to50Pct = 115,
+ kTransitionRampUpShortSharp_10to50Pct = 116,
+ kTransitionRampUpShortSharp_20to50Pct = 117,
+ kSmoothHum1NoKickOrBrakePulse_50Pct = 119,
+ kSmoothHum2NoKickOrBrakePulse_40Pct = 120,
+ kSmoothHum3NoKickOrBrakePulse_30Pct = 121,
+ kSmoothHum4NoKickOrBrakePulse_20Pct = 122,
+ kSmoothHum5NoKickOrBrakePulse_10Pct = 123,
+
+ // We can't use this one; need to have the EN pin hooked up.
+ kDontUseThis_Longbuzzforprogrammaticstopping_100Pct = 118,
+
+ kFirst = kStrongClick_100Pct,
+ kLast = kSmoothHum5NoKickOrBrakePulse_10Pct,
+ };
+
+ static constexpr Effect kStartupEffect = Effect::kLongDoubleSharpTick1_100Pct;
+
+ // §8.3.5.2 Internal Memory Interface
+ // Pick the ERM Library matching the motor.
+ enum class Library : uint8_t {
+ A = 1, // 1.3V-3V, Rise: 40-60ms, Brake: 20-40ms
+ 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+
+ };
+
+ static constexpr Library kDefaultLibrary = Library::C;
+
+ auto PowerDown() -> void;
+ auto Reset() -> void;
+
+ auto PlayWaveformEffect(Effect effect) -> void;
+
+ // Play a range of Effects
+ auto TourEffects() -> void;
+ auto TourEffects(Effect from, Effect to) -> void;
+ auto TourEffects(Library lib) -> void;
+ auto TourEffects(Effect from, Effect to, Library lib) -> void;
+
+ // Play a range of Effects to all the Libraries we support.
+ // TODO(robin): remove; I'm leaving this around for temporary testing
+ auto TourLibraries(Effect from, Effect to) -> void;
+
+ private:
+ std::optional<Effect> current_effect_;
+ std::mutex playing_effect_;
+
+ // §8.4.2 Changing Modes of Operation
+ enum class Mode : uint8_t {
+ kInternalTrigger = 0,
+ kExternalTriggerEdge = 1,
+ kExternalTriggerLevel = 2,
+ kPwmAnalog = 3,
+ kAudioToVibe = 4,
+ kRealtimePlayback = 5,
+ kDiagnostics = 6,
+ kAutoCalibrate = 7,
+ };
+
+ struct ModeMask {
+ // §8.4.1.4 Operation With STANDBY Control
+ static constexpr uint8_t kStandby = 0b01000000;
+ // §8.4.1.5 Operation With DEV_RESET Control
+ static constexpr uint8_t kDevReset = 0b10000000;
+ };
+
+ struct ControlMask {
+ // Control1
+ static constexpr uint8_t kNErmLra = 0b10000000;
+ // Control3
+ static constexpr uint8_t kErmOpenLoop = 0b00100000;
+ };
+
+ // §8.6 Register Map
+ enum class Register {
+ kStatus = 0x00,
+ kMode = 0x01,
+
+ kRealtimePlaybackInput = 0x02,
+ kWaveformLibrary = 0x03, // see Library enum
+
+ kWaveformSequenceSlot1 = 0x04,
+ kWaveformSequenceSlot2 = 0x05,
+ kWaveformSequenceSlot3 = 0x06,
+ kWaveformSequenceSlot4 = 0x07,
+ kWaveformSequenceSlot5 = 0x08,
+ kWaveformSequenceSlot6 = 0x09,
+ kWaveformSequenceSlot7 = 0x0a,
+ kWaveformSequenceSlot8 = 0x0b,
+
+ kGo = 0x0C,
+
+ // §8.3.5.2.2 Library Parameterization
+ kOverdriveTimeOffset = 0x0D,
+ kSustainTimeOffsetPositive = 0x0E,
+ kSustainTimeOffsetNegative = 0x0F,
+ kBrakeTimeOffset = 0x10,
+ kAudioToVibeControl = 0x11,
+
+ kAudioToVibeInputLevelMin = 0x12,
+ kAudioToVibeInputLevelMax = 0x13,
+ kAudioToVibeOutputLevelMin = 0x14,
+ kAudioToVibeOutputLevelMax = 0x15,
+ kRatedVoltage = 0x16,
+ kOverdriveClampVoltage = 0x17,
+ kAutoCalibrationCompensationResult = 0x18,
+ kAutoCalibrationBackEmfResult = 0x19,
+
+ // A bunch of different options, not grouped
+ // in any particular sensible way
+ kControl1 = 0x1A,
+ kControl2 = 0x1B,
+ kControl3 = 0x1C,
+ kControl4 = 0x1D,
+ kControl5 = 0x1E,
+ kControl6 = 0x1F,
+
+ kSupplyVoltageMonitor = 0x21, // "VBAT"
+ kLraResonancePeriod = 0x22,
+ };
+
+ enum class RegisterDefaults : uint8_t {
+ kStatus = 0xE0,
+ kMode = 0x40,
+ kRealtimePlaybackInput = 0,
+ kWaveformLibrary = 0x01,
+ kWaveformSequenceSlot1 = 0x01,
+ kWaveformSequenceSlot2 = 0,
+ kWaveformSequenceSlot3 = 0,
+ kWaveformSequenceSlot4 = 0,
+ kWaveformSequenceSlot5 = 0,
+ kWaveformSequenceSlot6 = 0,
+ kWaveformSequenceSlot7 = 0,
+ kWaveformSequenceSlot8 = 0,
+ kGo = 0,
+ kOverdriveTimeOffset = 0,
+ kSustainTimeOffsetPositive = 0,
+ kSustainTimeOffsetNegative = 0,
+ kBrakeTimeOffset = 0,
+ kAudioToVibeControl = 0x05,
+ kAudioToVibeInputLevelMin = 0x19,
+ kAudioToVibeInputLevelMax = 0xFF,
+ kAudioToVibeOutputLevelMin = 0x19,
+ kAudioToVibeOutputLevelMax = 0xFF,
+ kRatedVoltage = 0x3E,
+ kOverdriveClampVoltage = 0x8C,
+ kAutoCalibrationCompensationResult = 0x0C,
+ kAutoCalibrationBackEmfResult = 0x6C,
+ kControl1 = 0x36,
+ kControl2 = 0x93,
+ kControl3 = 0xF5,
+ kControl4 = 0xA0,
+ kControl5 = 0x20,
+ kControl6 = 0x80,
+ kSupplyVoltageMonitor = 0,
+ kLraResonancePeriod = 0,
+ };
+
+ auto PowerUp() -> void;
+ auto WriteRegister(Register reg, uint8_t val) -> void;
+
+ auto SetWaveformEffect(Effect effect) -> void;
+ auto Go() -> void;
+
+ auto EffectToLabel(Effect effect) -> std::string;
+};
+
+} // namespace drivers