SensorLib 0.5.0
Multi-platform sensor driver library for Arduino, PlatformIO, and ESP-IDF
Loading...
Searching...
No Matches
HapticDriver_AW86224.cpp
Go to the documentation of this file.
1
31#include "../SensorBuildOpt.h"
32#if !SENSORLIB_EXCLUDE_HAPTIC_AW86224
33
36#include "AW86224_RamData.hpp"
37
38using namespace AW86224Regs;
39
41 HapticBase("AW86224"),
42 _rstPin(-1),
43 _chipID(0),
44 _playMode(PlayMode::STANDBY),
45 _gain(0x80),
46 _f0(0), _vbat(0), _lra(0),
47 _ramInit(false),
48 _cont_drv2_lvl(0),
49 _f0_cali_data(0),
50 _f0_pre(1700),
51 _ramBaseAddr(0), _ramNum(0),
52 _lra_vrms(1000),
53 _cont_brk_time(0x06), _cont_brk_gain(0x08),
54 _cont_drv1_lvl(0x7F), _cont_drv1_time(0x04), _cont_drv2_time(0x14),
55 _cont_track_margin(0x0F),
56 _d2s_gain(0x05),
57 _pwmcfg4_prtime(0x32), _pwmcfg3_prlvl(0x3F),
58 _auto_brk_enabled(false),
59 _f0_cali_percent(7),
60 _duration(0), _vibrationStartTime(0),
61 _isCalibrated(false)
62{
63}
64
65void HapticDriver_AW86224::setPins(uint8_t resetPin, uint8_t intPin)
66{
67 _rstPin = resetPin;
68 _intPin = intPin;
69}
70
72{
73 stop();
74 setReady(false);
75}
76
78{
79 return _isReady && _ramInit;
80}
81
83{
84 switch (_chipID) {
85 case static_cast<uint8_t>(ChipID::AW86214):
86 return "AW86214";
87 case static_cast<uint8_t>(ChipID::AW86223):
88 return "AW86223";
89 case static_cast<uint8_t>(ChipID::AW86224):
90 return "AW86224";
91 default:
92 break;
93 }
94 return "UNKNOWN";
95}
96
97// ==================== HapticBase Interface ====================
98
100{
102 cap.hasF0Calibration = 1;
103 cap.hasVbatCompensation = 1;
104 cap.hasRTP = 1;
105 cap.hasSequence = 1;
106 cap.hasAutoCalibration = 0;
107 cap.hasBreakEffect = 1;
108 cap.hasOverdrive = 0;
109 cap.hasContinuousMode = 1;
110 cap.maxEffectCount = _ramNum > 0 ? _ramNum : 4;
111 cap.maxSequenceLength = 8;
112 cap.maxDurationMs = 0; // Unlimited
113 return cap;
114}
115
123
125{
126 if (_playMode == PlayMode::STANDBY) return false;
127 uint8_t state = getGlobalState() & 0x0F;
128 return state != 0x00 && state != 0x07;
129}
130
132{
133 return _gain;
134}
135
137{
138 if (!_ramInit) return false;
139 shortVibration(static_cast<uint8_t>(effect), _gain, 1);
140 return true;
141}
142
144{
145 if (!_ramInit) return false;
146 if (isPlaying()) {
147 stop();
148 hal->delay(20);
149 }
150 cancelVibration();
151 softReset();
152 hal->delay(10);
153
154 setTrimLRA(static_cast<uint8_t>(_f0_cali_data));
155 setWaveSeq(0, static_cast<uint8_t>(effect));
156 setWaveSeq(1, 0);
157 setWaveLoop(0, 0);
158 setGain(_gain);
159 playMode(PlayMode::RAM);
160 run();
161 return true;
162}
163
164void HapticDriver_AW86224::continuousVibration(uint32_t duration_ms, bool blocking)
165{
166 if (!_ramInit) return;
167 longVibration(2, _gain, duration_ms, blocking);
168}
169
171{
172 if (!isPlaying()) return;
173
174 if (_duration > 0 && hal->millis() - _vibrationStartTime >= _duration) {
175 stop();
176 return;
177 }
178
179 if ((getGlobalState() & 0x0F) != 0x07) run();
180}
181
183{
184 if (slot >= 8) return false;
185 setWaveSeq(slot, static_cast<uint8_t>(effect));
186 return true;
187}
188
190{
191 for (uint8_t i = 0; i < 8; i++) {
192 setWaveSeq(i, 0);
193 }
194 return true;
195}
196
198{
199 if (!_ramInit) return false;
200 playMode(PlayMode::RAM);
201 return run();
202}
203
205{
206 if (!_isReady) return false;
207 f0Calibration();
208 _isCalibrated = true;
209 return _f0 > 0;
210}
211
213{
214 return !_isCalibrated || _f0 == 0;
215}
216
217// ==================== Internal Helper Functions ====================
218
219void HapticDriver_AW86224::cancelVibration()
220{
221 playStop();
222}
223
224void HapticDriver_AW86224::ram_vbat_comp(bool flag)
225{
226 if (!flag) return;
227
228 uint32_t vbat = _vbat < 3000 ? 3000 : _vbat;
229 int temp_gain = static_cast<int>(_gain) * AW_VBAT_REFER / vbat;
230 if (temp_gain > 255) temp_gain = 255;
231
232#ifdef HAPTIC_DEBUG
233 Serial.printf("[VBAT_COMP] vbat=%d, orig=0x%02X, comp=0x%02X\n", vbat, _gain, temp_gain);
234#endif
235
236 setGain(static_cast<uint8_t>(temp_gain));
237}
238
239void HapticDriver_AW86224::ramInit(bool enable)
240{
241 if (enable) {
242 updateBits(REG_SYSCTRL1, MASK_EN_RAMINIT, MASK_EN_RAMINIT);
243 hal->delayMicroseconds(500);
244 } else {
245 updateBits(REG_SYSCTRL1, MASK_EN_RAMINIT, 0);
246 }
247}
248
249void HapticDriver_AW86224::playStop()
250{
251 _playMode = PlayMode::STANDBY;
252 ramInit(true);
253 updateBits(REG_PLAYCFG3, MASK_PLAY_MODE, MASK_PLAY_MODE);
254 writeReg(REG_PLAYCFG4, PLAYCFG4_GO_ON);
255 ramInit(false);
256
257 uint8_t val = 0;
258 uint8_t cnt = 40;
259 while (cnt--) {
260 val = readReg(REG_GLBRD5);
261 if ((val & MASK_GLB_STATE) == STATE_STANDBY) break;
262 hal->delay(5);
263 }
264
265 if ((val & MASK_GLB_STATE) != STATE_STANDBY) {
266 updateBits(REG_SYSCTRL2, MASK_STANDBY, MASK_STANDBY);
267 hal->delay(2);
268 updateBits(REG_SYSCTRL2, MASK_STANDBY, 0);
269 }
270}
271
272bool HapticDriver_AW86224::checkQualify()
273{
274 return (readReg(REG_CHIPID) & 0x80) == 0x80;
275}
276
277bool HapticDriver_AW86224::offsetCalibration()
278{
279 if (_d2s_gain == 0) return false;
280
281 updateBits(REG_SYSCTRL7, MASK_D2S_GAIN, _d2s_gain & MASK_D2S_GAIN);
282 int d2s_gain = selectD2sGain(_d2s_gain & MASK_D2S_GAIN);
283 if (d2s_gain < 0) return false;
284
285 ramInit(true);
286 updateBits(REG_DETCFG1, MASK_RL_OS, 0);
287 updateBits(REG_DETCFG2, MASK_DIAG_GO, MASK_DIAG_GO);
288 hal->delay(3);
289
290 uint8_t reg_val[2];
291 reg_val[0] = readReg(REG_DET_OS);
292 reg_val[1] = readReg(REG_DET_LO);
293 ramInit(false);
294
295 int os_code = (reg_val[1] & 0xFC) >> 2;
296 os_code = (reg_val[0] << 2) | os_code;
297 os_code = os_formula(os_code, d2s_gain);
298
299 return (os_code <= 15 && os_code >= -15);
300}
301
302int HapticDriver_AW86224::selectD2sGain(uint8_t reg)
303{
304 switch (reg) {
305 case 0: return 1;
306 case 1: return 2;
307 case 2: return 4;
308 case 3: return 5;
309 case 4: return 8;
310 case 5: return 10;
311 case 6: return 20;
312 case 7: return 40;
313 default: return -1;
314 }
315}
316
317void HapticDriver_AW86224::setSramSize(SramSize size)
318{
319 switch (size) {
321 updateBits(REG_RTPCFG1, sensorlib::_bv(5), 0);
322 updateBits(REG_RTPCFG1, sensorlib::_bv(4), sensorlib::_bv(4));
323 break;
325 updateBits(REG_RTPCFG1, sensorlib::_bv(5), sensorlib::_bv(5));
326 updateBits(REG_RTPCFG1, sensorlib::_bv(4), 0);
327 break;
330 break;
331 }
332}
333
334void HapticDriver_AW86224::autoBrkConfig(bool enable)
335{
336 updateBits(REG_PLAYCFG3, MASK_BRK_EN, enable ? MASK_BRK_EN : 0);
337}
338
339void HapticDriver_AW86224::setPwm(PwmMode mode)
340{
341 switch (mode) {
342 case PwmMode::PWM_48K: updateBits(REG_SYSCTRL2, MASK_WAVDAT_MODE, 1); break;
343 case PwmMode::PWM_24K: updateBits(REG_SYSCTRL2, MASK_WAVDAT_MODE, 0); break;
344 case PwmMode::PWM_12K: updateBits(REG_SYSCTRL2, MASK_WAVDAT_MODE, 2); break;
345 }
346}
347
348void HapticDriver_AW86224::playMode(PlayMode mode)
349{
350 _playMode = mode;
351 switch (mode) {
353 playStop();
354 break;
355 case PlayMode::RAM:
356 setPwm(PwmMode::PWM_12K);
357 autoBrkConfig(false);
358 updateBits(REG_PLAYCFG3, MASK_PLAY_MODE, 0);
359 break;
361 setPwm(PwmMode::PWM_12K);
362 autoBrkConfig(true);
363 updateBits(REG_PLAYCFG3, MASK_PLAY_MODE, 0);
364 break;
365 case PlayMode::CONT:
366 autoBrkConfig(true);
367 updateBits(REG_PLAYCFG3, MASK_PLAY_MODE, 2);
368 break;
369 case PlayMode::RTP:
370 setPwm(PwmMode::PWM_24K);
371 autoBrkConfig(true);
372 updateBits(REG_PLAYCFG3, MASK_PLAY_MODE, 1);
373 break;
374 }
375}
376
377void HapticDriver_AW86224::config()
378{
379 setSramSize(SramSize::SRAM_3K);
380 updateBits(REG_TRGCFG8, MASK_TRG1_MODE, 0x10);
381 updateBits(REG_ANACFG8, MASK_TRTF_CTRL_HDRV, MASK_TRTF_CTRL_HDRV);
382
383 if (_cont_brk_time) writeReg(REG_CONTCFG10, _cont_brk_time);
384 else _cont_brk_time = 1;
385
386 if (_cont_brk_gain) updateBits(REG_CONTCFG5, MASK_BRK_GAIN, _cont_brk_gain);
387 else _cont_brk_gain = 1;
388}
389
390void HapticDriver_AW86224::protectConfig(uint8_t prtime, uint8_t prlvl)
391{
392 updateBits(REG_PWMCFG1, MASK_PRC_EN, 0);
393 if (prlvl != 0) {
394 writeReg(REG_PWMCFG3, MASK_PR_EN | (prlvl & ~MASK_PRLVL));
395 writeReg(REG_PWMCFG4, prtime);
396 } else {
397 updateBits(REG_PWMCFG3, MASK_PR_EN, 0);
398 }
399}
400
401void HapticDriver_AW86224::miscParaInit()
402{
403 updateBits(REG_SYSCTRL7, MASK_GAIN_BYPASS, GAIN_BYPASS_CHANGEABLE);
404 _cont_drv2_lvl = drv2_lvl_formula(_f0_pre, _lra_vrms);
405 if (_cont_drv2_lvl > 127) _cont_drv2_lvl = 127;
406 config();
407 setPwm(PwmMode::PWM_12K);
408 protectConfig(_pwmcfg4_prtime, _pwmcfg3_prlvl);
409}
410
411void HapticDriver_AW86224::trigInit()
412{
413 updateBits(REG_TRGCFG8, MASK_TRG1_MODE, 0x10);
414 updateBits(REG_SYSCTRL2, MASK_INTN_PIN, 0);
415}
416
418{
419 updateBits(REG_TRIMCFG3, MASK_TRIM_LRA, val);
420}
421
422bool HapticDriver_AW86224::measureF0()
423{
424 uint8_t val[2] = {0};
425 uint8_t brk_en_temp = 0;
426 uint32_t f0_reg = 0;
427 int cnt = 200;
428 bool success = false;
429
430 playStop();
431 updateBits(REG_SYSCTRL7, MASK_D2S_GAIN, _d2s_gain & MASK_D2S_GAIN);
432
433 _playMode = PlayMode::CONT;
434 autoBrkConfig(true);
435 updateBits(REG_PLAYCFG3, MASK_PLAY_MODE, 2);
436 updateBits(REG_CONTCFG1, MASK_EN_F0_DET, MASK_EN_F0_DET);
437 updateBits(REG_CONTCFG6, MASK_TRACK_EN, MASK_TRACK_EN);
438
439 brk_en_temp = readReg(REG_PLAYCFG3) & MASK_BRK_EN;
440 updateBits(REG_CONTCFG6, MASK_DRV1_LVL, _cont_drv1_lvl);
441 writeReg(REG_CONTCFG7, _cont_drv2_lvl);
442 writeReg(REG_CONTCFG8, _cont_drv1_time);
443 writeReg(REG_CONTCFG9, _cont_drv2_time);
444 writeReg(REG_CONTCFG11, _cont_track_margin);
445
446 if (_f0_pre == 0) return false;
447 int drv_width = (((_f0_pre / 10) * 15) / (_cont_track_margin + 3)) / 100;
448 if (drv_width < 8) drv_width = 8;
449 if (drv_width > 255) drv_width = 255;
450 writeReg(REG_CONTCFG3, static_cast<uint8_t>(drv_width));
451
452 run();
453 hal->delay(20);
454
455 while (cnt--) {
456 if ((getGlobalState() & MASK_GLB_STATE) == STATE_STANDBY) {
457 success = true;
458 break;
459 }
460 hal->delay(10);
461 }
462
463 if (success) {
464 readRegBuff(REG_CONTRD14, val, 2);
465 f0_reg = (val[0] << 8) | val[1];
466 if (f0_reg) _f0 = f0_formula(f0_reg);
467 }
468
469 updateBits(REG_CONTCFG1, MASK_EN_F0_DET, 0);
470 updateBits(REG_PLAYCFG3, MASK_BRK_EN, brk_en_temp);
471 playStop();
472 return success;
473}
474
475bool HapticDriver_AW86224::measureVbat()
476{
477 playStop();
478 ramInit(true);
479 updateBits(REG_DETCFG2, MASK_VBAT_GO, MASK_VBAT_GO);
480 hal->delay(2);
481
482 uint32_t vbat_code = readReg(REG_DET_VBAT) << 2;
483 vbat_code |= (readReg(REG_DET_LO) & MASK_VBAT_LO) >> SHIFT_VBAT_LO;
484
485 _vbat = vbat_formula(vbat_code);
486 if (_vbat > 4500) _vbat = 4500;
487 if (_vbat < 3000) _vbat = 3000;
488
489 ramInit(false);
490 return true;
491}
492
493void HapticDriver_AW86224::getLraResistance()
494{
495 uint8_t d2s_gain_temp = readReg(REG_SYSCTRL7) & MASK_D2S_GAIN;
496 int d2s_gain = selectD2sGain(5);
497 if (d2s_gain < 0) return;
498
499 playStop();
500 ramInit(true);
501 hal->delay(2);
502 updateBits(REG_SYSCTRL2, MASK_STANDBY, 0);
503 updateBits(REG_DETCFG1, MASK_RL_OS, MASK_RL_OS);
504 updateBits(REG_DETCFG2, MASK_DIAG_GO, MASK_DIAG_GO);
505 hal->delay(3);
506
507 uint32_t lra_code = readReg(REG_DET_RL) << 2;
508 lra_code |= readReg(REG_DET_LO) & MASK_RL_LO;
509 _lra = rl_formula(lra_code, d2s_gain) * 10;
510
511 ramInit(false);
512 updateBits(REG_SYSCTRL7, MASK_D2S_GAIN, d2s_gain_temp);
513}
514
516{
517 playMode(mode);
518}
519
521{
522 _gain = gain;
523 writeReg(REG_PLAYCFG2, gain);
524 return true;
525}
526
528{
529 ramInit(true);
530 hal->delayMicroseconds(50);
531 int ret = writeReg(REG_PLAYCFG4, PLAYCFG4_GO_ON);
532 hal->delayMicroseconds(50);
533 ramInit(false);
534 return ret == 0;
535}
536
538{
539 playStop();
540 _duration = 0;
541 return true;
542}
543
544void HapticDriver_AW86224::shortVibration(uint8_t index, uint8_t gain, uint8_t loop)
545{
546 if (!_ramInit || index == 0) return;
547
548 if (isPlaying()) {
549 stop();
550 hal->delay(20);
551 }
552
553 cancelVibration();
554 softReset();
555 hal->delay(10);
556
557 setTrimLRA(static_cast<uint8_t>(_f0_cali_data));
558 setWaveSeq(0, index);
559 setWaveSeq(1, 0);
560 setWaveLoop(0, loop > 0 ? loop - 1 : 0);
561 setGain(gain);
562 playMode(PlayMode::RAM);
563 run();
564
565 uint32_t playTime = loop * 50;
566 if (playTime < 100) playTime = 100;
567 hal->delay(playTime);
568}
569
570void HapticDriver_AW86224::longVibration(uint8_t index, uint8_t gain, uint32_t duration_ms, bool blocking)
571{
572 if (!_ramInit || index == 0 || duration_ms == 0) return;
573
574 if (isPlaying()) {
575 stop();
576 hal->delay(20);
577 }
578
579 cancelVibration();
580 softReset();
581 hal->delay(10);
582
583 _gain = gain;
584 _duration = duration_ms;
585 _vibrationStartTime = hal->millis();
586 setTrimLRA(static_cast<uint8_t>(_f0_cali_data));
587 setGain(gain);
588 ram_vbat_comp(true);
589
590 setRepeatSeq(index);
591 playMode(PlayMode::RAM_LOOP);
592 run();
593
594 if (blocking) {
595 uint32_t start = hal->millis();
596 while (hal->millis() - start < duration_ms) {
597 if ((getGlobalState() & 0x0F) != 0x07) run();
598 hal->delay(10);
599 }
600 stop();
601 }
602}
603
608
609void HapticDriver_AW86224::setWaveSeq(uint8_t wav, uint8_t seq)
610{
611 writeReg(REG_WAVCFG1 + wav, seq);
612}
613
614void HapticDriver_AW86224::setWaveLoop(uint8_t wav, uint8_t loop)
615{
616 uint8_t reg = REG_WAVCFG9 + (wav / 2);
617 if (wav % 2) {
618 updateBits(reg, MASK_SEQ2LOOP, loop << SHIFT_SEQ2LOOP);
619 } else {
620 updateBits(reg, MASK_SEQ1LOOP, loop << SHIFT_SEQ1LOOP);
621 }
622}
623
624void HapticDriver_AW86224::setFifoAddr()
625{
626 uint8_t ae_addr_h = (_ramBaseAddr >> 1) >> 4 & 0xF0;
627 uint8_t af_addr_h = ((_ramBaseAddr - (_ramBaseAddr >> 2)) >> 8) & 0x0F;
628 uint8_t val[3] = {
629 static_cast<uint8_t>(ae_addr_h | af_addr_h),
630 static_cast<uint8_t>((_ramBaseAddr >> 1) & 0xFF),
631 static_cast<uint8_t>((_ramBaseAddr - (_ramBaseAddr >> 2)) & 0xFF)
632 };
633 writeRegBuff(REG_RTPCFG3, val, 3);
634}
635
636void HapticDriver_AW86224::setBaseAddr()
637{
638 updateBits(REG_RTPCFG1, MASK_BASE_ADDR_H, (_ramBaseAddr >> 8) & MASK_BASE_ADDR_H);
639 writeReg(REG_RTPCFG2, _ramBaseAddr & 0xFF);
640}
641
642void HapticDriver_AW86224::setRamAddr()
643{
644 writeReg(REG_RAMADDRH, (_ramBaseAddr >> 8) & 0x3F);
645 writeReg(REG_RAMADDRL, _ramBaseAddr & 0xFF);
646}
647
648void HapticDriver_AW86224::setRtpData(uint8_t *data, uint32_t len)
649{
650 writeRegBuff(REG_RTPDATA, data, len);
651}
652
653bool HapticDriver_AW86224::playRtp(const uint8_t *data, uint32_t len, uint8_t gain)
654{
655 if (!data || len == 0) return false;
656
657 playStop();
658 irqClear();
659 setRtpAei(false);
660 setGain(gain);
661 setTrimLRA(0x00);
662 setPwm(PwmMode::PWM_24K);
663 autoBrkConfig(true);
664 playMode(PlayMode::RTP);
665
666 ramInit(true);
667 updateBits(REG_RTPCFG1, MASK_BASE_ADDR_H, (_ramBaseAddr >> 8) & MASK_BASE_ADDR_H);
668 writeReg(REG_RTPCFG2, _ramBaseAddr & 0xFF);
669 ramInit(false);
670
671 if (!run()) return false;
672 hal->delay(1);
673
674 if (!waitRtpGo(200)) {
675 playStop();
676 return false;
677 }
678
679 writeRegBuff(REG_RTPDATA, const_cast<uint8_t*>(data), len);
680
681 if (len > 256) {
682 uint32_t cnt = 256;
683 while (cnt < len && _playMode == PlayMode::RTP) {
684 uint32_t buf_len = (len - cnt) > 64 ? 64 : (len - cnt);
685 writeRegBuff(REG_RTPDATA, const_cast<uint8_t*>(data + cnt), buf_len);
686 cnt += buf_len;
687 }
688 }
689 return true;
690}
691
692bool HapticDriver_AW86224::waitRtpGo(uint32_t timeout_ms)
693{
694 uint32_t start = hal->millis();
695 while (hal->millis() - start < timeout_ms) {
696 if ((readReg(REG_GLBRD5) & 0x0F) == STATE_RTP_GO) return true;
697 hal->delayMicroseconds(1000);
698 }
699 return false;
700}
701
702void HapticDriver_AW86224::setRepeatSeq(uint8_t seq)
703{
704 setWaveSeq(0, seq);
705 setWaveSeq(1, 0);
706 setWaveLoop(0, WAVLOOP_INIFINITELY);
707}
708
709void HapticDriver_AW86224::contConfig()
710{
711 _playMode = PlayMode::CONT;
712 updateBits(REG_CONTCFG6, MASK_TRACK_EN | MASK_DRV1_LVL, MASK_TRACK_EN | (_cont_drv1_lvl & MASK_DRV1_LVL));
713 writeReg(REG_CONTCFG7, _cont_drv2_lvl);
714 writeReg(REG_CONTCFG9, 0xFF);
715 run();
716}
717
718void HapticDriver_AW86224::interruptSetup()
719{
720 updateBits(REG_SYSCTRL7, INT_MODE_EDGE, INT_MODE_EDGE);
721 updateBits(REG_SYSCTRL7, sensorlib::_bv(5), INT_EDGE_MODE_POS);
722 updateBits(REG_SYSINTM, MASK_UVLM | MASK_FF_AEM | MASK_FF_AFM | MASK_OCDM | MASK_OTM | MASK_DONEM, MASK_UVLM);
723}
724
725uint8_t HapticDriver_AW86224::getGlobalState() const
726{
727 return readReg(REG_GLBRD5);
728}
729
730bool HapticDriver_AW86224::waitStandby(uint32_t timeout_ms)
731{
732 uint32_t start = hal->millis();
733 while (hal->millis() - start < timeout_ms) {
734 if ((getGlobalState() & MASK_GLB_STATE) == STATE_STANDBY) return true;
735 hal->delay(2);
736 }
737 return false;
738}
739
740int HapticDriver_AW86224::getIrqState()
741{
742 uint8_t reg_val = readReg(REG_SYSINT);
743 if (reg_val & MASK_UVLI) return IRQ_UVL;
744 if (reg_val & MASK_OCDI) return IRQ_OCD;
745 if (reg_val & MASK_OTI) return IRQ_OT;
746 if (reg_val & MASK_DONEI) return IRQ_DONE;
747 if (reg_val & MASK_FF_AFI) return IRQ_ALMOST_FULL;
748 if (reg_val & MASK_FF_AEI) return IRQ_ALMOST_EMPTY;
749 return IRQ_NULL;
750}
751
752void HapticDriver_AW86224::irqClear()
753{
754 readReg(REG_SYSINT);
755}
756
757uint8_t HapticDriver_AW86224::rtpGetFifoAfs()
758{
759 return (readReg(REG_SYSST) & MASK_FF_AFS) >> 3;
760}
761
762void HapticDriver_AW86224::setRtpAei(bool enable)
763{
764 updateBits(REG_SYSINTM, MASK_FF_AEM, enable ? 0 : MASK_FF_AEM);
765}
766
767bool HapticDriver_AW86224::loadRamData(const uint8_t *data, uint32_t len)
768{
769 if (!data || len == 0) return false;
770 if (!getBaseAddr(data, len)) return false;
771
772 playStop();
773 ramInit(true);
774 setBaseAddr();
775 setFifoAddr();
776 setRamAddr();
777
778 uint32_t i = 0;
779 while (i < len) {
780 uint32_t chunk = (len - i) > 32 ? 32 : (len - i);
781 writeRegBuff(REG_RAMDATA, const_cast<uint8_t*>(data + i), chunk);
782 i += chunk;
783 hal->delayMicroseconds(100);
784 }
785
786 ramInit(false);
787 trigInit();
788 _ramInit = true;
789 return true;
790}
791
792bool HapticDriver_AW86224::getBaseAddr(const uint8_t *data, uint32_t len)
793{
794 uint16_t last_end = 0, next_start = 0;
795 int ram_num = 1;
796
797 for (int i = 3; i < static_cast<int>(len); i += 4) {
798 last_end = (data[i] << 8) | data[i + 1];
799 next_start = (data[i + 2] << 8) | data[i + 3];
800 if ((next_start - last_end) == 1) ram_num++;
801 else break;
802 }
803
804 for (int i = ram_num * 4; i >= 4; i -= 4) {
805 last_end = (data[i - 1] << 8) | data[i];
806 _ramBaseAddr = static_cast<int>((data[1] << 8) | data[2]) - ram_num * 4 - 1;
807 if ((last_end - _ramBaseAddr + 1) == static_cast<int>(len)) {
808 _ramNum = ram_num;
809 return true;
810 }
811 ram_num--;
812 }
813
814 _ramBaseAddr = (data[1] << 8) | data[2];
815 _ramNum = ((_ramBaseAddr - _ramBaseAddr) - 1) / 4;
816 return true;
817}
818
819void HapticDriver_AW86224::vbatModeConfig(uint8_t flag)
820{
821 if (flag == CONT_VBAT_HW_COMP_MODE) {
822 writeReg(REG_GLBCFG2, 0x0C);
823 updateBits(REG_SYSCTRL1, MASK_VBAT_MODE, MASK_VBAT_MODE);
824 } else {
825 updateBits(REG_SYSCTRL1, MASK_VBAT_MODE, 0);
826 }
827}
828
829void HapticDriver_AW86224::calculateCaliData()
830{
831 int f0_cali_step = 100000 * (static_cast<int>(_f0) - static_cast<int>(_f0_pre)) / (static_cast<int>(_f0_pre) * F0_CALI_ACCURACY);
832
833 if (f0_cali_step >= 0) {
834 f0_cali_step = (f0_cali_step % 10 >= 5) ? (32 + f0_cali_step / 10 + 1) : (32 + f0_cali_step / 10);
835 } else {
836 f0_cali_step = (f0_cali_step % 10 <= -5) ? (32 + f0_cali_step / 10 - 1) : (32 + f0_cali_step / 10);
837 }
838
839 int8_t f0_cali_lra = (f0_cali_step > 31) ? static_cast<int8_t>(f0_cali_step - 32) : static_cast<int8_t>(f0_cali_step + 32);
840 _f0_cali_data = f0_cali_lra;
841}
842
843void HapticDriver_AW86224::reset()
844{
845 if (_rstPin != -1) {
846 hal->pinMode(_rstPin, OUTPUT);
847 hal->digitalWrite(_rstPin, LOW);
848 hal->delay(2);
849 hal->digitalWrite(_rstPin, HIGH);
850 hal->delay(8);
851 }
852}
853
855{
856 (void)type;
857 return false;
858}
859
864
866{
867 switch (mode) {
869 playMode(PlayMode::RAM);
870 break;
872 playMode(PlayMode::RTP);
873 break;
875 playStop();
876 break;
877 default:
878 return false;
879 }
880 return true;
881}
882
884{
885 switch (_playMode) {
886 case PlayMode::RTP:
888 case PlayMode::CONT:
891 return HapticMode::STANDBY;
892 break;
893 default:
895 }
896}
897
899{
900 if (_playMode != PlayMode::RTP) {
901 playMode(PlayMode::RTP);
902 }
903 setGain(value);
904 return true;
905}
906
908{
909 measureVbat();
910 return _vbat;
911}
912
914{
915 return _f0;
916}
917
919{
920 writeReg(REG_SRST, SOFTRESET_VAL);
921 hal->delay(2);
922}
923
925{
926 return _chipID;
927}
928
929bool HapticDriver_AW86224::initImpl(uint8_t param)
930{
931 (void)param;
932
933 if (_rstPin != -1) {
934 hal->pinMode(_rstPin, OUTPUT);
935 hal->digitalWrite(_rstPin, LOW);
936 hal->delay(2);
937 hal->digitalWrite(_rstPin, HIGH);
938 hal->delay(8);
939 }
940
941 if (!parseChipID()) return false;
942
943 softReset();
944
945 if (!offsetCalibration()) {
946 softReset();
947 offsetCalibration();
948 }
949
950 interruptSetup();
951 hapticInit();
952 trigInit();
953
954 if (!loadRamData(aw862xx_ram_data, aw862xx_ram_len)) return false;
955 playMode(PlayMode::STANDBY);
956 return true;
957}
958
959bool HapticDriver_AW86224::parseChipID()
960{
961 uint8_t reg = 0;
962 for (int i = 0; i < 5; i++) {
963 reg = readReg(0x00);
964 if (reg != 0) break;
965 hal->delay(2);
966 }
967
968 if (reg == 0x00) {
969 uint8_t ef_id = readReg(REG_CHIPID);
970 if ((ef_id & 0x41) == 0x01) {
971 _chipID = static_cast<uint8_t>(ChipID::AW86223);
972 } else if ((ef_id & 0x41) == 0x00) {
973 _chipID = static_cast<uint8_t>(ChipID::AW86224);
974 } else {
975 return false;
976 }
977 } else if (reg == 0x01) {
978 uint8_t ef_id = readReg(REG_CHIPID);
979 if ((ef_id & 0x41) == 0x41) {
980 _chipID = static_cast<uint8_t>(ChipID::AW86214);
981 } else {
982 return false;
983 }
984 } else {
985 return false;
986 }
987 return true;
988}
989
990void HapticDriver_AW86224::hapticInit()
991{
992 _f0_pre = F0_PRE_VAL;
993 playMode(PlayMode::STANDBY);
994 miscParaInit();
995 measureVbat();
996 vbatModeConfig(CONT_VBAT_HW_COMP_MODE);
997 f0Calibration();
998 playMode(PlayMode::STANDBY);
999}
1000
1001void HapticDriver_AW86224::f0Calibration()
1002{
1003 setTrimLRA(0x00);
1004
1005 if (!measureF0()) {
1006 _f0 = _f0_pre;
1007 }
1008
1009 if (!judgeF0WithinRange()) return;
1010
1011 calculateCaliData();
1012 setTrimLRA(static_cast<uint8_t>(_f0_cali_data));
1013 _isCalibrated = true;
1014}
1015
1016bool HapticDriver_AW86224::judgeF0WithinRange()
1017{
1018 uint32_t f0_cali_min = _f0_pre * (100 - _f0_cali_percent) / 100;
1019 uint32_t f0_cali_max = _f0_pre * (100 + _f0_cali_percent) / 100;
1020 return (_f0 >= f0_cali_min && _f0 <= f0_cali_max);
1021}
1022
1023void HapticDriver_AW86224::printRegs()
1024{
1025#ifdef HAPTIC_DEBUG
1026 Serial.println("\n=== Register Dump ===");
1027 Serial.printf("PLAYCFG2: 0x%02X\n", readReg(0x07));
1028 Serial.printf("PLAYCFG3: 0x%02X\n", readReg(0x08));
1029 Serial.printf("PLAYCFG4: 0x%02X\n", readReg(0x09));
1030 Serial.printf("WAVCFG1: 0x%02X\n", readReg(0x0A));
1031 Serial.printf("WAVCFG2: 0x%02X\n", readReg(0x0B));
1032 Serial.printf("WAVCFG9: 0x%02X\n", readReg(0x12));
1033 Serial.printf("GLBRD5: 0x%02X\n", readReg(0x3F));
1034 Serial.printf("SYSCTRL7: 0x%02X\n", readReg(0x49));
1035 Serial.printf("TRIMCFG3: 0x%02X\n", readReg(0x5A));
1036 Serial.println("======================");
1037#endif
1038}
1039
1040#endif
@license MIT License
HapticStatus
Vibration playback status.
@ PLAYING
Currently playing.
@ IDLE
Not playing.
@ ERROR
Error state.
@ STANDBY
Standby mode.
HapticActuatorType
Actuator type.
@ LRA
Linear Resonant Actuator.
HapticMode
Generic haptic operating mode.
@ INTERNAL_TRIGGER
Play by selecting waveforms/effects and calling run()
@ AUTO_CALIBRATE
Auto-calibration mode.
@ STANDBY
Standby mode.
@ REAL_TIME_PLAYBACK
RTP mode (direct drive value)
uint16_t HapticEffectId
Haptic effect ID type.
@license MIT License
@license MIT License
void loop()
std::unique_ptr< SensorHal > hal
Abstract base class for haptic drivers.
void setReady(bool ready)
Set ready state.
void end() override
Deinitialize the haptic driver and release resources.
void setPlayMode(PlayMode mode)
Set playback mode.
bool setSequence(uint8_t slot, HapticEffectId effect) override
Set a waveform in the sequence.
bool playEffectAsync(HapticEffectId effect) override
Play a haptic effect (non-blocking).
void setWaveLoop(uint8_t wav, uint8_t loop)
Set waveform loop count.
@ PWM_12K
12kHz PWM rate (default)
void stopVibration()
Stop any ongoing vibration.
void setWaveSeq(uint8_t wav, uint8_t seq)
Update vibration state (for non-blocking mode)
bool calibrate() override
Perform auto-calibration.
bool isPlaying() const override
Check if haptic is currently playing.
void setTrimLRA(uint8_t val)
Set LRA frequency trim value.
bool setGain(uint8_t gain) override
Set the gain/amplitude (0x00 to 0xFF).
uint32_t getVbat() override
Get the battery voltage.
bool loadRamData(const uint8_t *data, uint32_t len)
Load RAM waveform data.
void setPins(uint8_t resetPin, uint8_t intPin)
setPins
bool stop() override
Stop haptic playback immediately.
bool setMode(HapticMode mode) override
Set the operating mode.
uint32_t getF0() const override
Get the measured resonant frequency (F0).
HapticMode getMode() const override
Get the current operating mode.
void setRtpData(uint8_t *data, uint32_t len)
Set RTP waveform data.
bool playRtp(const uint8_t *data, uint32_t len, uint8_t gain=0x80)
Play RTP (Real-Time Playback) waveform.
bool setActuatorType(HapticActuatorType type) override
Set the actuator type.
uint8_t getGain() const override
Get the current gain value.
bool setRealtimeValue(uint8_t value) override
Set RTP (real-time playback) value.
bool isReady() const override
Check if the driver is initialized and ready.
void softReset()
Perform soft reset.
void vibrationUpdate() override
Update vibration state (for non-blocking mode).
void continuousVibration(uint32_t duration_ms, bool blocking=true) override
Play a continuous vibration for specified duration.
HapticActuatorType getActuatorType() const override
Get the actuator type.
bool playEffect(HapticEffectId effect) override
Play a haptic effect.
HapticStatus getStatus() const override
Get current playback status.
bool run() override
Start haptic playback.
bool clearSequence() override
Clear the waveform sequence.
HapticDriver_AW86224()
Default constructor.
void shortVibration(uint8_t index, uint8_t gain=0x80, uint8_t loop=1)
Play a short vibration effect.
HapticCapabilities getCapabilities() const override
Get device capabilities.
const char * getChipName() const override
Get the chip name.
void longVibration(uint8_t index, uint8_t gain=0x80, uint32_t duration_ms=1000, bool blocking=true)
Play a long continuous vibration effect.
PlayMode
Playback mode enumeration.
@ CONT
Continuous mode with F0 tracking.
@ RAM_LOOP
RAM loop mode, continuous playback.
@ RAM
RAM playback mode, single play.
@ STANDBY
Standby mode, minimal power consumption.
@ RTP
Real-time playback mode.
bool needsCalibration() const override
Check if calibration is needed.
uint8_t getChipID() const override
Get the chip ID.
bool playSequence() override
Play the waveform sequence.
int readReg(uint8_t reg) const
int writeRegBuff(uint8_t reg, uint8_t *buf, size_t len)
int writeReg(uint8_t reg, uint8_t val)
int readRegBuff(uint8_t reg, uint8_t *buf, size_t len) const
int updateBits(uint8_t reg, uint8_t mask, uint8_t value_shifted)
constexpr uint32_t _bv(uint8_t b)
Definition SensorLib.h:62
uint8_t i
Device capability flags.
uint8_t maxSequenceLength
Maximum waveform sequence length.
uint8_t hasF0Calibration
Supports F0 (resonant frequency) calibration.
uint16_t maxDurationMs
Maximum vibration duration in ms (0 = unlimited)
uint8_t hasAutoCalibration
Supports auto-calibration.
uint8_t hasVbatCompensation
Supports automatic voltage compensation.
uint8_t hasRTP
Supports Real-Time Playback mode.
uint8_t hasBreakEffect
Supports brake/break effects.
uint8_t hasOverdrive
Supports overdrive effects.
uint8_t maxEffectCount
Maximum number of effect IDs supported.
uint8_t hasContinuousMode
Supports continuous vibration mode.
uint8_t hasSequence
Supports waveform sequencing.