@@ -61,9 +61,9 @@ uint16_t hrmPollInterval = HRM_POLL_INTERVAL_DEFAULT; // in msec, so 20 = 50hz
61
61
#define VC31B_REG12 0x11 // INT?
62
62
// 0x80 appears to enable interrupts?
63
63
// 0x10 = WearStatusDetection (ENV sensor IRQ?)
64
-
64
+ #define VC31B_REG13 0x12 // ???
65
65
#define VC31B_REG14 0x13 // FIFO 0x40=some flag, + assert FIFO IRQ every X samples
66
- #define VC31B_REG15 0x14 // 16 bit time calibration (0x331 default)
66
+ #define VC31B_REG15 0x14 // 16 bit time calibration (x>>8,x) (0x31F default)
67
67
#define VC31B_REG16 0x16 // ENV samplerate. samplesPerSec-6. After how many samples is the 0x10 IRQ asserted for ENV data
68
68
#define VC31B_REG17 0x17 // SLOT0 LED Current - 0xEF = maxLedCur
69
69
#define VC31B_REG18 0x18 // SLOT1 LED Current - 0xEF = maxLedCur
@@ -73,11 +73,11 @@ uint16_t hrmPollInterval = HRM_POLL_INTERVAL_DEFAULT; // in msec, so 20 = 50hz
73
73
74
74
// Interrupts
75
75
// 0x10 = WearStatusDetection (ENV sensor IRQ?)
76
- /* Bit fields for VCREG1 */
76
+ /* Bit fields for VC31B_REG1 */
77
77
#define VC31B_STATUS_CONFLICT 0x10
78
78
#define VC31B_STATUS_INSAMPLE 0x08
79
79
#define VC31B_STATUS_OVERLOAD_MASK 0x07 // 3x bits for each of the 3 channels
80
- /* Bit fields for VCREG1 */
80
+ /* Bit fields for VC31B_REG2 */
81
81
#define VC31B_INT_PS 0x10
82
82
#define VC31B_INT_OV 0x08 // OvloadAdjust
83
83
#define VC31B_INT_FIFO 0x04
@@ -143,9 +143,6 @@ uint16_t hrmPollInterval = HRM_POLL_INTERVAL_DEFAULT; // in msec, so 20 = 50hz
143
143
#define VC31A_UNWEAR_CNT 3
144
144
#define VC31A_ISWEAR_CNT 1
145
145
146
- #define VC31A_PPG_ADJUSTED 0x1000
147
- #define VC31A_PPG_MASK 0x0FFF
148
-
149
146
typedef enum
150
147
{
151
148
AdjustDirection_Null = 0 ,
@@ -176,6 +173,9 @@ typedef struct {
176
173
bool isWearing ;
177
174
int8_t isWearCnt , unWearCnt ; // counters for switching worn/unworn state
178
175
uint16_t ppgValue ; // current PPG value
176
+ uint16_t ppgLastValue ; // last PPG value
177
+ int16_t ppgOffset ; // PPG 'offset' value. When PPG adjusts we change the offset so it matches the last value, then slowly adjust 'ppgOffset' back down to 0
178
+ uint8_t wasAdjusted ; // true if LED/etc adjusted since the last reading
179
179
// the meaning of these is device-dependent but it's nice to have them in one place
180
180
uint8_t irqStatus ;
181
181
uint8_t raw [12 ];
@@ -258,6 +258,23 @@ static void vc31_w16(uint8_t reg, uint16_t data) {
258
258
}
259
259
260
260
261
+ // we have a PPG value - save to vcInfo.ppgValue and send it to HRM monitor
262
+ void vc31_new_ppg (uint16_t value ) {
263
+ vcInfo .ppgLastValue = vcInfo .ppgValue ;
264
+ vcInfo .ppgValue = value ;
265
+
266
+ if (vcInfo .wasAdjusted ) {
267
+ // stop any sudden jerky changes to the HRM value
268
+ vcInfo .ppgOffset = vcInfo .ppgLastValue + vcInfo .ppgOffset - vcInfo .ppgValue ;
269
+ }
270
+ const int offsetAdjustment = 32 ;
271
+ if (vcInfo .ppgOffset > offsetAdjustment ) vcInfo .ppgOffset -= offsetAdjustment ;
272
+ else if (vcInfo .ppgOffset < - offsetAdjustment ) vcInfo .ppgOffset += offsetAdjustment ;
273
+ else vcInfo .ppgOffset = 0 ;
274
+
275
+ hrmCallback (vcInfo .ppgValue + vcInfo .ppgOffset );
276
+ }
277
+
261
278
262
279
static void vc31_adjust () {
263
280
uint16_t adjustParam = 0 ;
@@ -292,7 +309,7 @@ static void vc31_adjust() {
292
309
adjustParam = (uint16_t )(
293
310
adjustStep ) | VC31A_GREEN_ADJ_ENABLE | VC31A_GREEN_ADJ_UP ;
294
311
vc31_w16 (VC31A_GREEN_ADJ , adjustParam );
295
- vcInfo .ppgValue |= VC31A_PPG_ADJUSTED ;
312
+ vcInfo .wasAdjusted = 2 ; // ignore 2 samples worth
296
313
}
297
314
vcaInfo .adjustInfo .direction = vcaInfo .adjustInfo .directionLast ;
298
315
vcaInfo .adjustInfo .directionLast = AdjustDirection_Up ;
@@ -324,7 +341,7 @@ static void vc31_adjust() {
324
341
adjustParam = (uint16_t )(
325
342
adjustStep ) | VC31A_GREEN_ADJ_ENABLE | VC31A_GREEN_ADJ_DOWN ;
326
343
vc31_w16 (VC31A_GREEN_ADJ , adjustParam );
327
- vcInfo .ppgValue |= VC31A_PPG_ADJUSTED ;
344
+ vcInfo .wasAdjusted = 2 ; // ignore 2 samples worth
328
345
}
329
346
vcaInfo .adjustInfo .direction = vcaInfo .adjustInfo .directionLast ;
330
347
vcaInfo .adjustInfo .directionLast = AdjustDirection_Down ;
@@ -414,12 +431,12 @@ static void vc31b_readfifo(uint8_t startAddr, uint16_t endAddr, uint8_t IndexFla
414
431
int dataLength = endAddr - startAddr ;
415
432
vc31_rx (startAddr , sampleData , dataLength );
416
433
for (i = 0 ; i < dataLength ; i += 2 ) {
417
- vcInfo . ppgValue = ((sampleData [i ] << 8 ) | sampleData [i + 1 ]);
434
+ uint16_t ppgValue = ((sampleData [i ] << 8 ) | sampleData [i + 1 ]);
418
435
// jsiConsolePrintf("ppg %d\n",ppgValue);
419
436
// FIXME - we need a timestamp - hrmCallback creates one itself when it is called
420
437
// but if we use the FIFO properly we'll need to stamp it ourselves because
421
438
// we'll call it a bunch of times at once
422
- hrmCallback ( vcInfo . ppgValue );
439
+ vc31_new_ppg ( ppgValue ); // send PPG value
423
440
}
424
441
}
425
442
@@ -456,7 +473,7 @@ static void vc31b_adjust(uint8_t slotNum,uint8_t ledcur,uint8_t pdres) {
456
473
newPdRes = (oldPdRes < 1 ) ? 0 : (oldPdRes - 1 );
457
474
}
458
475
newLedCurrent = oldLedCurrent ;
459
- //vcHr02AddAdjustFlag(slotNum) ;
476
+ vcInfo . wasAdjusted = 2 ;
460
477
}
461
478
}
462
479
else
@@ -494,10 +511,10 @@ static void vc31b_adjust(uint8_t slotNum,uint8_t ledcur,uint8_t pdres) {
494
511
{
495
512
newLedCurrent = (oldLedCurrent < vcbInfo .adjustInfo [slotNum ].step ) ? ledcur :oldLedCurrent - vcbInfo .adjustInfo [slotNum ].step ;
496
513
}
497
- //vcHr02AddAdjustFlag(slotNum) ;
514
+ vcInfo . wasAdjusted = 2 ;
498
515
newPdRes = oldPdRes ;
499
516
vcbInfo .adjustInfo [slotNum ].directionLast = vcbInfo .adjustInfo [slotNum ].direction ;
500
- }
517
+ }
501
518
502
519
vcbInfo .ledCurrent [slotNum ] = newLedCurrent ;
503
520
vcbInfo .pdRes [slotNum ] = newPdRes ;
@@ -529,7 +546,7 @@ static void vc31b_slot_adjust(int slotNum) {
529
546
if (vcbInfo .ledCurrent [slotNum ] != 0 ) {
530
547
vcbInfo .ledCurrent [slotNum ] = vcbInfo .ledCurrent [slotNum ] - 1 ;
531
548
vcbInfo .ledMaxCurrent [slotNum ] = vcbInfo .ledCurrent [slotNum ];
532
- //FIXME call addAdjustFlag(slotNum) ;
549
+ vcInfo . wasAdjusted = 1 ;
533
550
vcbInfo .regConfig [slotNum + 7 ] = (vcbInfo .ledCurrent [slotNum ] | vcbInfo .ppgGain [slotNum ]);
534
551
vc31_w ((VC31B_REG17 + slotNum ), vcbInfo .regConfig [slotNum + 7 ]);
535
552
}
@@ -563,20 +580,22 @@ void vc31_irqhandler(bool state, IOEventFlags flags) {
563
580
uint8_t * buf = vcInfo .raw ;
564
581
vc31_rx (VC31A_STATUS , buf , 11 );
565
582
vcInfo .irqStatus = buf [0 ];
566
- vcInfo . ppgValue = (buf [2 ] << 8 ) | buf [1 ];
583
+ uint16_t ppgValue = (buf [2 ] << 8 ) | buf [1 ];
567
584
vcaInfo .currentValue = ((buf [4 ] << 8 ) | buf [3 ]) + 10 ;
568
585
vcaInfo .preValue = (buf [6 ] << 8 ) | buf [5 ];
569
586
vcaInfo .psValue = (buf [8 ] << 8 ) | buf [7 ];
570
587
vcaInfo .envValue = (buf [10 ] << 8 ) | buf [9 ];
571
588
572
- if (vcInfo .irqStatus & VC31A_STATUS_D_PPG_OK )
589
+ if (vcInfo .irqStatus & VC31A_STATUS_D_PPG_OK ) {
590
+ vc31_new_ppg (ppgValue ); // send PPG value
591
+
592
+ if (vcInfo .wasAdjusted > 0 ) vcInfo .wasAdjusted -- ;
573
593
vc31_adjust ();
594
+ }
574
595
if (vcInfo .irqStatus & VC31A_STATUS_D_PS_OK )
575
596
vc31a_wearstatus ();
576
597
577
- // sanity check ppgOffset to ensure we stay in range
578
- int ppgValue = (vcInfo .ppgValue & VC31A_PPG_MASK );
579
- hrmCallback (ppgValue );
598
+
580
599
}
581
600
if (vcType == VC31B_DEVICE ) {
582
601
uint8_t * buf = & vcInfo .raw [0 ];
@@ -598,41 +617,6 @@ void vc31_irqhandler(bool state, IOEventFlags flags) {
598
617
vcbInfo .sampleData .currentValue [1 ] = buf [1 ] & 0x7F ;
599
618
vcbInfo .sampleData .pdResValue [2 ] = (buf [5 ] >> 4 ) & 0x07 ;
600
619
vcbInfo .sampleData .currentValue [2 ] = buf [2 ] & 0x7F ;
601
- // FIXME VC31B has something about psBiasReadInPdFlag here - is it a reset for a lockup?
602
- /*
603
- if(vcbInfo.status & VC31B_CONFLICT)
604
- return VCHR02RET_ISCONFLICT;
605
- if(vcbInfo.status & VC31B_INSAMPLE)
606
- return VCHR02RET_ISINSAMPLE;
607
-
608
- for (vcHr02SlotCnt = 0; vcHr02SlotCnt < totalSlot; vcHr02SlotCnt++)
609
- {
610
- slotCount = (totalSlot == 2) ? vcHr02SlotCnt: (enSlot[0] ? 0:1);
611
- vcHr02AdjustPDResMax(pvcHr02,slotCount);
612
- ledCurrent[slotCount]= vcbInfo.regConfig[7+slotCount] & 0x7f;
613
- ppgGain[slotCount] = vcbInfo.regConfig[7+slotCount] & 0x80;
614
- pdRes[slotCount] = (vcbInfo.regConfig[10+slotCount] & 0x70) >> 4;
615
- pdResSet[slotCount] = vcbInfo.regConfig[10+slotCount] & 0x8F;
616
-
617
- // done this - wear detect
618
- //if(vcbInfo.intReason & VC31B_INT_ENV)
619
- // ret |= vcHr02EnvAdjust(pvcHr02,slotCount);
620
-
621
- if(vcbInfo.intReason & VC31B_INT_OV)
622
- {
623
- ret |= vcHr02OvloadAdjust(pvcHr02,slotCount);
624
- }
625
- if(vcbInfo.intReason & VC31B_INT_PPG)
626
- {
627
- ret |= vcHr02PpgAdjust(pvcHr02,slotCount);
628
- }
629
- else
630
- {
631
- vcbInfo.adjustInfo[slotCount].directionLast = AdjustDirection_Null;
632
- vcbInfo.adjustInfo[slotCount].direction = AdjustDirection_Null;
633
- }
634
- }*/
635
-
636
620
637
621
// if we had environment sensing, check for wear status and update LEDs accordingly
638
622
if (vcInfo .irqStatus & VC31B_INT_PS ) {
@@ -658,6 +642,7 @@ void vc31_irqhandler(bool state, IOEventFlags flags) {
658
642
vc31b_readfifo (vcbInfo .fifoReadIndex ,vcbInfo .fifoReadIndex + vcbInfo .totalSlots * 2 ,0 );
659
643
}
660
644
// now we need to adjust the PPG
645
+ if (vcInfo .wasAdjusted > 0 ) vcInfo .wasAdjusted -- ;
661
646
for (int slotNum = 0 ;slotNum < 3 ;slotNum ++ ) {
662
647
vc31b_slot_adjust (slotNum );
663
648
}
@@ -677,10 +662,10 @@ void vc31_irqhandler(bool state, IOEventFlags flags) {
677
662
fifoWriteIndex += slotNum*2;
678
663
uint8_t buf[2];
679
664
vc31_rx(fifoWriteIndex, buf, 2);
680
- vcInfo. ppgValue = (buf[0]<<8) | buf[1];
665
+ uint16_t ppgValue = (buf[0]<<8) | buf[1];
681
666
// ONLY do this here because we're not using the FIFO
682
667
jsiConsolePrintf("ppg %d\n", vcInfo.ppgValue);
683
- hrmCallback(vcInfo. ppgValue);
668
+ vc31_new_ppg( ppgValue); // send PPG value
684
669
// now we need to adjust the PPG
685
670
for (int slotNum=0;slotNum<3;slotNum++) {
686
671
vc31b_slot_adjust(slotNum);
@@ -727,13 +712,15 @@ void hrm_sensor_on(HrmCallback callback) {
727
712
728
713
memset (& vcInfo , 0 , sizeof (vcInfo ));
729
714
vcInfo .isWearing = true;
730
- vcInfo .unWearCnt = VC31A_UNWEAR_CNT ;
731
- vcInfo .isWearCnt = VC31A_ISWEAR_CNT ;
732
- vcaInfo .adjustInfo .step = 307200 ;
733
- vcaInfo .adjustInfo .direction = AdjustDirection_Null ;
734
- vcaInfo .adjustInfo .directionLast = AdjustDirection_Null ;
715
+ vcInfo .unWearCnt = VC31A_UNWEAR_CNT ;
716
+ vcInfo .isWearCnt = VC31A_ISWEAR_CNT ;
717
+ vcInfo .ppgOffset = 0 ;
735
718
736
719
if (vcType == VC31_DEVICE ) {
720
+ vcaInfo .adjustInfo .step = 307200 ;
721
+ vcaInfo .adjustInfo .direction = AdjustDirection_Null ;
722
+ vcaInfo .adjustInfo .directionLast = AdjustDirection_Null ;
723
+
737
724
vc31_w (VC31A_GREEN_WAIT , 0xb4 );
738
725
vc31_w (VC31A_TIA_WAIT , 0x54 );
739
726
vc31_w (VC31A_PS_DIV , 0x09 );
@@ -758,15 +745,17 @@ void hrm_sensor_on(HrmCallback callback) {
758
745
// vc31_w16(VC31A_GREEN_ADJ, 0xe8c3);
759
746
}
760
747
if (vcType == VC31B_DEVICE ) {
761
- vcbInfo .vcHr02SampleRate = 25 ; // Hz
748
+ vcbInfo .vcHr02SampleRate = 25 ; // 1000 / hrmPollInterval; // Hz
749
+ // FIXME SAMPLE RATE. Right now this only changes the period for ENV readings
762
750
uint8_t _regConfig [17 ] = {
763
- 0x01 , // VC31B_REG11 - just enable SLOT0
764
- 0x3F ,0x8A , // 12/13/14
765
- 0x40 , // VC31B_REG14 0x40 + FIFO Interrupt length?
766
- 0x03 ,0x1F , // VCREG15 (2 bytes) 16 bit RTC prescaler
767
- 0x00 , // VC31B_REG16 sample rate - 6
768
- 0x00 ,0x80 , // VC31B_REG17/18 solt0/1 LED current
769
- 0x00 , // VC31B_REG19 - LED current
751
+ 0x01 , // VC31B_REG11 - just enable SLOT0
752
+ VC31B_INT_OV |VC31B_INT_FIFO |VC31B_INT_ENV , // VC31B_REG12 IRQs - was 0x3F
753
+ 0x8A , // VC31B_REG13 ??
754
+ 0x40 , // VC31B_REG14 0x40 + FIFO Interrupt length in bottom 6 bits
755
+ 0x03 ,0x1F , // VC31B_REG15 (2 bytes) 16 bit counter prescaler
756
+ 0x00 , // VC31B_REG16 SLOT2 ENV sample rate - 6
757
+ 0x00 ,0x80 , // VC31B_REG17/18 SLOT0/1 LED current
758
+ 0x00 , // VC31B_REG19 - LED current
770
759
0x57 ,0x37 , // VC31B_REG20/21 ENV sensitivity?
771
760
0x07 ,0x16 ,
772
761
0x56 ,0x16 ,0x00
@@ -821,6 +810,10 @@ void hrm_sensor_off() {
821
810
JsVar * hrm_sensor_getJsVar () {
822
811
JsVar * o = jsvNewObject ();
823
812
if (o ) {
813
+ jsvObjectSetChildAndUnLock (o ,"vcPPG" ,jsvNewFromInteger (vcInfo .ppgValue ));
814
+ jsvObjectSetChildAndUnLock (o ,"vcPPGoffs" ,jsvNewFromInteger (vcInfo .ppgOffset ));
815
+ jsvObjectSetChildAndUnLock (o ,"isWearing" ,jsvNewFromBool (vcInfo .isWearing ));
816
+ jsvObjectSetChildAndUnLock (o ,"adjusted" ,jsvNewFromBool (vcInfo .wasAdjusted ));
824
817
if (vcType == VC31_DEVICE ) {
825
818
jsvObjectSetChildAndUnLock (o ,"vcCurrent" ,jsvNewFromInteger (vcaInfo .currentValue ));
826
819
jsvObjectSetChildAndUnLock (o ,"vcPre" ,jsvNewFromInteger (vcaInfo .preValue ));
@@ -833,7 +826,6 @@ JsVar *hrm_sensor_getJsVar() {
833
826
jsvObjectSetChildAndUnLock (o ,"vcEnv" ,jsvNewArrayFromBytes (vcbInfo .sampleData .envValue , 3 ));
834
827
}
835
828
jsvObjectSetChildAndUnLock (o ,"vcIRQ" ,jsvNewFromInteger (vcInfo .irqStatus ));
836
- jsvObjectSetChildAndUnLock (o ,"isWearing" ,jsvNewFromBool (vcInfo .isWearing ));
837
829
//jsvObjectSetChildAndUnLock(o,"isWearCnt",jsvNewFromInteger(vcInfo.isWearCnt));
838
830
//jsvObjectSetChildAndUnLock(o,"unWearCnt",jsvNewFromInteger(vcInfo.unWearCnt));
839
831
jsvObjectSetChildAndUnLock (o ,"vcRaw" ,jsvNewArrayBufferWithData (sizeof (vcInfo .raw ), vcInfo .raw ));
0 commit comments