/** * * @license MIT License * * Copyright (c) 2022 lewis he * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * @file SensorDRV2605.hpp * @author Lewis He (lewishe@outlook.com) * @date 2023-04-03 * @note Source code from https://github.com/adafruit/Adafruit_DRV2605_Library */ #pragma once #include "REG/DRV2605Constants.h" #include "SensorPlatform.hpp" class SensorDRV2605 : public DRV2605Constants { public: static constexpr uint8_t MODE_INTTRIG = 0x00; //* Internal trigger mode static constexpr uint8_t MODE_EXTTRIGEDGE = 0x01; //* External edge trigger mode static constexpr uint8_t MODE_EXTTRIGLVL = 0x02; //* External level trigger mode static constexpr uint8_t MODE_PWMANALOG = 0x03; //* PWM/Analog input mode static constexpr uint8_t MODE_AUDIOVIBE = 0x04; //* Audio-to-vibe mode static constexpr uint8_t MODE_REALTIME = 0x05; //* Real-time playback (RTP) mode static constexpr uint8_t MODE_DIAGNOS = 0x06; //* Diagnostics mode static constexpr uint8_t MODE_AUTOCAL = 0x07; //* Auto calibration mode SensorDRV2605() : comm(nullptr) {} ~SensorDRV2605() { if (comm) { comm->deinit(); } } #if defined(ARDUINO) bool begin(TwoWire &wire, int sda = -1, int scl = -1) { comm = std::make_unique(wire, DRV2605_SLAVE_ADDRESS, sda, scl); if (!comm) { return false; } comm->init(); return initImpl(); } #elif defined(ESP_PLATFORM) #if defined(USEING_I2C_LEGACY) bool begin(i2c_port_t port_num, int sda = -1, int scl = -1) { comm = std::make_unique(port_num, DRV2605_SLAVE_ADDRESS, sda, scl); if (!comm) { return false; } comm->init(); return initImpl(); } #else bool begin(i2c_master_bus_handle_t handle) { comm = std::make_unique(handle, DRV2605_SLAVE_ADDRESS); if (!comm) { return false; } comm->init(); return initImpl(); } #endif //ESP_PLATFORM #endif //ARDUINO bool begin(SensorCommCustom::CustomCallback callback) { comm = std::make_unique(callback, DRV2605_SLAVE_ADDRESS); if (!comm) { return false; } comm->init(); return initImpl(); } /**************************************************************************/ /*! @brief Select the haptic waveform to use. @param slot The waveform slot to set, from 0 to 7 @param w The waveform sequence value, refers to an index in the ROM library. Playback starts at slot 0 and continues through to slot 7, stopping if it encounters a value of 0. A list of available waveforms can be found in section 11.2 of the datasheet: http://www.adafruit.com/datasheets/DRV2605.pdf */ /**************************************************************************/ void setWaveform(uint8_t slot, uint8_t w) { comm->writeRegister(DRV2605_REG_WAVESEQ1 + slot, w); } /**************************************************************************/ /*! @brief Select the waveform library to use. @param lib Library to use, 0 = Empty, 1-5 are ERM, 6 is LRA. See section 7.6.4 in the datasheet for more details: http://www.adafruit.com/datasheets/DRV2605.pdf */ /**************************************************************************/ void selectLibrary(uint8_t lib) { comm->writeRegister(DRV2605_REG_LIBRARY, lib); } /**************************************************************************/ /*! @brief Start playback of the waveforms (start moving!). */ /**************************************************************************/ void run() { comm->writeRegister(DRV2605_REG_GO, 1); } /**************************************************************************/ /*! @brief Stop playback. */ /**************************************************************************/ void stop() { comm->writeRegister(DRV2605_REG_GO, (uint8_t)0); } /**************************************************************************/ /*! @brief Set the device mode. @param mode Mode value, see datasheet section 7.6.2: http://www.adafruit.com/datasheets/DRV2605.pdf 0: Internal trigger, call run() to start playback\n 1: External trigger, rising edge on IN pin starts playback\n 2: External trigger, playback follows the state of IN pin\n 3: PWM/analog input\n 4: Audio\n 5: Real-time playback\n 6: Diagnostics\n 7: Auto calibration */ /**************************************************************************/ void setMode(uint8_t mode) { comm->writeRegister(DRV2605_REG_MODE, mode); } /**************************************************************************/ /*! @brief Set the realtime value when in RTP mode, used to directly drive the haptic motor. @param rtp 8-bit drive value. */ /**************************************************************************/ void setRealtimeValue(uint8_t rtp) { comm->writeRegister(DRV2605_REG_RTPIN, rtp); } /**************************************************************************/ /*! @brief Use ERM (Eccentric Rotating Mass) mode. */ /**************************************************************************/ void useERM() { comm->writeRegister(DRV2605_REG_FEEDBACK, comm->readRegister(DRV2605_REG_FEEDBACK) & 0x7F); } /**************************************************************************/ /*! @brief Use LRA (Linear Resonance Actuator) mode. */ /**************************************************************************/ void useLRA() { comm->writeRegister(DRV2605_REG_FEEDBACK, comm->readRegister(DRV2605_REG_FEEDBACK) | 0x80); } private: bool initImpl() { int chipID = comm->readRegister(DRV2605_REG_STATUS); if (chipID < 0) { return false; } chipID >>= 5; if (chipID != DRV2604_CHIP_ID && chipID != DRV2605_CHIP_ID && chipID != DRV2604L_CHIP_ID && chipID != DRV2605L_CHIP_ID ) { log_e("ChipID:0x%x should be 0x03 or 0x04 or 0x06 or 0x07\n", chipID); return false; } comm->writeRegister(DRV2605_REG_MODE, (uint8_t)0x00); // out of standby comm->writeRegister(DRV2605_REG_RTPIN, (uint8_t)0x00); // no real-time-playback comm->writeRegister(DRV2605_REG_WAVESEQ1, (uint8_t)1); // strong click comm->writeRegister(DRV2605_REG_WAVESEQ2, (uint8_t)0); // end sequence comm->writeRegister(DRV2605_REG_OVERDRIVE, (uint8_t)0); // no overdrive comm->writeRegister(DRV2605_REG_SUSTAINPOS, (uint8_t)0); comm->writeRegister(DRV2605_REG_SUSTAINNEG, (uint8_t)0); comm->writeRegister(DRV2605_REG_BREAK, (uint8_t)0); comm->writeRegister(DRV2605_REG_AUDIOMAX, (uint8_t)0x64); // ERM open loop // turn off N_ERM_LRA comm->writeRegister(DRV2605_REG_FEEDBACK, comm->readRegister(DRV2605_REG_FEEDBACK) & 0x7F); // turn on ERM_OPEN_LOOP comm->writeRegister(DRV2605_REG_CONTROL3, comm->readRegister(DRV2605_REG_CONTROL3) | 0x20); return true; } protected: std::unique_ptr comm; };