mirror of
https://github.com/markqvist/RNode_Firmware.git
synced 2026-04-27 14:30:33 +00:00
Add DRV2605 haptic driver with boot and sleep feedback
Minimal I2C driver for the DRV2605 ERM vibration motor. 117 built-in effects via the ERM effect library. Named constants for watch use cases (click, bump, alert, buzz, tick, etc.). Boot: sharp click on startup. Sleep: soft bump before entering deep sleep. XL9555 DRV_EN and DISP_EN now explicitly enabled at boot. Updated Makefile upload-twatch_ultra to set firmware hash after JTAG flash.
This commit is contained in:
parent
b9319fa761
commit
9f034e8d0d
3 changed files with 151 additions and 1 deletions
136
DRV2605.h
Normal file
136
DRV2605.h
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
// DRV2605 Haptic Driver — Minimal I2C driver for T-Watch Ultra
|
||||
// ERM vibration motor with 117 built-in effects
|
||||
// EN pin controlled by XL9555 EXPANDS_DRV_EN (port 0, pin 6)
|
||||
|
||||
#ifndef DRV2605_H
|
||||
#define DRV2605_H
|
||||
|
||||
#if BOARD_MODEL == BOARD_TWATCH_ULT
|
||||
|
||||
#include <Wire.h>
|
||||
|
||||
#define DRV2605_ADDR 0x5A
|
||||
|
||||
// Registers
|
||||
#define DRV2605_STATUS 0x00
|
||||
#define DRV2605_MODE 0x01
|
||||
#define DRV2605_RTPIN 0x02
|
||||
#define DRV2605_LIBRARY 0x03
|
||||
#define DRV2605_WAVESEQ1 0x04
|
||||
#define DRV2605_GO 0x0C
|
||||
#define DRV2605_OVERDRIVE 0x0D
|
||||
#define DRV2605_SUSTAINPOS 0x0E
|
||||
#define DRV2605_SUSTAINNEG 0x0F
|
||||
#define DRV2605_BRAKE 0x10
|
||||
#define DRV2605_FEEDBACK 0x1A
|
||||
#define DRV2605_CONTROL3 0x1D
|
||||
|
||||
// Named effects for watch use cases (ERM Library 1, effects 1-117)
|
||||
#define HAPTIC_STRONG_CLICK 1 // Strong Click - 100%
|
||||
#define HAPTIC_MEDIUM_CLICK 2 // Strong Click - 60%
|
||||
#define HAPTIC_LIGHT_CLICK 3 // Strong Click - 30%
|
||||
#define HAPTIC_SHARP_CLICK 4 // Sharp Click - 100%
|
||||
#define HAPTIC_SOFT_BUMP 7 // Soft Bump - 100%
|
||||
#define HAPTIC_DOUBLE_CLICK 10 // Double Click - 100%
|
||||
#define HAPTIC_TRIPLE_CLICK 12 // Triple Click - 100%
|
||||
#define HAPTIC_BUZZ 14 // Strong Buzz - 100%
|
||||
#define HAPTIC_ALERT 15 // 750ms Alert - 100%
|
||||
#define HAPTIC_LONG_ALERT 16 // 1000ms Alert - 100%
|
||||
#define HAPTIC_TICK 4 // Sharp Click (subtle tick)
|
||||
#define HAPTIC_TRANSITION 47 // Transition Click - 100%
|
||||
|
||||
static bool drv2605_ready = false;
|
||||
|
||||
static void drv2605_write(uint8_t reg, uint8_t val) {
|
||||
Wire.beginTransmission(DRV2605_ADDR);
|
||||
Wire.write(reg);
|
||||
Wire.write(val);
|
||||
Wire.endTransmission();
|
||||
}
|
||||
|
||||
static uint8_t drv2605_read(uint8_t reg) {
|
||||
Wire.beginTransmission(DRV2605_ADDR);
|
||||
Wire.write(reg);
|
||||
Wire.endTransmission(false);
|
||||
Wire.requestFrom((uint8_t)DRV2605_ADDR, (uint8_t)1);
|
||||
return Wire.available() ? Wire.read() : 0;
|
||||
}
|
||||
|
||||
bool drv2605_init() {
|
||||
// Probe device
|
||||
Wire.beginTransmission(DRV2605_ADDR);
|
||||
if (Wire.endTransmission() != 0) return false;
|
||||
|
||||
// Verify chip ID (bits 7:5 of STATUS should be 3 or 7)
|
||||
uint8_t id = drv2605_read(DRV2605_STATUS) >> 5;
|
||||
if (id != 3 && id != 7) return false;
|
||||
|
||||
// Exit standby
|
||||
drv2605_write(DRV2605_MODE, 0x00);
|
||||
|
||||
// Disable real-time playback input
|
||||
drv2605_write(DRV2605_RTPIN, 0x00);
|
||||
|
||||
// Select ERM mode: clear bit 7 of FEEDBACK register
|
||||
uint8_t fb = drv2605_read(DRV2605_FEEDBACK);
|
||||
drv2605_write(DRV2605_FEEDBACK, fb & 0x7F);
|
||||
|
||||
// Enable open-loop drive: set bit 5 of CONTROL3
|
||||
uint8_t ctrl3 = drv2605_read(DRV2605_CONTROL3);
|
||||
drv2605_write(DRV2605_CONTROL3, ctrl3 | 0x20);
|
||||
|
||||
// Select ERM effect library 1
|
||||
drv2605_write(DRV2605_LIBRARY, 1);
|
||||
|
||||
// Clear timing offsets
|
||||
drv2605_write(DRV2605_OVERDRIVE, 0);
|
||||
drv2605_write(DRV2605_SUSTAINPOS, 0);
|
||||
drv2605_write(DRV2605_SUSTAINNEG, 0);
|
||||
drv2605_write(DRV2605_BRAKE, 0);
|
||||
|
||||
// Clear all waveform slots
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
drv2605_write(DRV2605_WAVESEQ1 + i, 0);
|
||||
}
|
||||
|
||||
drv2605_ready = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Play a single effect (1-117 from ERM library)
|
||||
void drv2605_play(uint8_t effect) {
|
||||
if (!drv2605_ready) return;
|
||||
drv2605_write(DRV2605_MODE, 0x00); // Internal trigger mode
|
||||
drv2605_write(DRV2605_WAVESEQ1, effect); // Effect in slot 1
|
||||
drv2605_write(DRV2605_WAVESEQ1 + 1, 0); // End sequence
|
||||
drv2605_write(DRV2605_GO, 1); // Start playback
|
||||
}
|
||||
|
||||
// Play a sequence of up to 8 effects
|
||||
void drv2605_sequence(const uint8_t *effects, uint8_t count) {
|
||||
if (!drv2605_ready || count == 0) return;
|
||||
if (count > 8) count = 8;
|
||||
drv2605_write(DRV2605_MODE, 0x00);
|
||||
for (uint8_t i = 0; i < count; i++) {
|
||||
drv2605_write(DRV2605_WAVESEQ1 + i, effects[i]);
|
||||
}
|
||||
if (count < 8) {
|
||||
drv2605_write(DRV2605_WAVESEQ1 + count, 0); // Terminate
|
||||
}
|
||||
drv2605_write(DRV2605_GO, 1);
|
||||
}
|
||||
|
||||
// Stop any playing effect
|
||||
void drv2605_stop() {
|
||||
if (!drv2605_ready) return;
|
||||
drv2605_write(DRV2605_GO, 0);
|
||||
}
|
||||
|
||||
// Check if an effect is still playing
|
||||
bool drv2605_busy() {
|
||||
if (!drv2605_ready) return false;
|
||||
return drv2605_read(DRV2605_GO) & 1;
|
||||
}
|
||||
|
||||
#endif // BOARD_MODEL == BOARD_TWATCH_ULT
|
||||
#endif // DRV2605_H
|
||||
4
Makefile
4
Makefile
|
|
@ -121,11 +121,13 @@ firmware-twatch_ultra:
|
|||
arduino-cli compile --log --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=-DBOARD_MODEL=0x45"
|
||||
|
||||
upload-twatch_ultra: firmware-twatch_ultra
|
||||
@echo "Flashing T-Watch Ultra via OpenOCD JTAG..."
|
||||
@echo "Flashing T-Watch Ultra app via JTAG (no BOOT+RST needed)..."
|
||||
$(HOME)/.arduino15/packages/esp32/tools/openocd-esp32/v0.12.0-esp32-20230921/bin/openocd \
|
||||
-s $(HOME)/.arduino15/packages/esp32/tools/openocd-esp32/v0.12.0-esp32-20230921/share/openocd/scripts \
|
||||
-f board/esp32s3-builtin.cfg \
|
||||
-c "program_esp build/esp32.esp32.esp32s3/RNode_Firmware.ino.bin 0x10000 verify reset exit"
|
||||
@sleep 5
|
||||
rnodeconf /dev/ttyACM4 --firmware-hash $$(./partition_hashes ./build/esp32.esp32.esp32s3/RNode_Firmware.ino.bin)
|
||||
|
||||
firmware-lora32_v10: check_bt_buffers
|
||||
arduino-cli compile --log --fqbn esp32:esp32:ttgo-lora32 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x39\""
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#if BOARD_MODEL == BOARD_TWATCH_ULT
|
||||
#include "XL9555.h"
|
||||
#include "CO5300.h"
|
||||
#include "DRV2605.h"
|
||||
#endif
|
||||
|
||||
#define CHANNEL_FIFO_SIZE (CONFIG_UART_BUFFER_SIZE / NUM_CHANNELS)
|
||||
|
|
@ -274,6 +275,11 @@ void setup() {
|
|||
#if BOARD_MODEL == BOARD_TWATCH_ULT
|
||||
xl9555_init();
|
||||
xl9555_enable_lora_antenna();
|
||||
xl9555_set(EXPANDS_DRV_EN, true); // Enable haptic motor driver
|
||||
xl9555_set(EXPANDS_DISP_EN, true); // Enable display power gate
|
||||
delay(10);
|
||||
drv2605_init();
|
||||
if (drv2605_ready) drv2605_play(HAPTIC_SHARP_CLICK); // Boot feedback
|
||||
|
||||
// Beacon timer wakeup: if we woke from deep sleep via timer,
|
||||
// take the fast path — init GPS/LoRa only, transmit, sleep again.
|
||||
|
|
@ -1999,6 +2005,12 @@ void loop() {
|
|||
// Safely shuts down peripherals and enters ESP32 deep sleep.
|
||||
// Does not return — device reboots on wake.
|
||||
void twatch_enter_deep_sleep(bool beacon_timer) {
|
||||
// 0. Haptic feedback before sleep
|
||||
if (drv2605_ready) {
|
||||
drv2605_play(HAPTIC_SOFT_BUMP);
|
||||
delay(150); // Let the motor spin briefly before powering down
|
||||
}
|
||||
|
||||
// 1. Put display controller into sleep mode (must happen before SPI.end)
|
||||
#if HAS_DISPLAY
|
||||
co5300_sleep();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue