Skip to content

Commit d4f5737

Browse files
committed
HRM: tweaks to use ppgOffset again, but to gradually reduce it to 0 over time
1 parent cdd79e3 commit d4f5737

File tree

1 file changed

+63
-71
lines changed

1 file changed

+63
-71
lines changed

libs/misc/hrm_vc31.c

+63-71
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,9 @@ uint16_t hrmPollInterval = HRM_POLL_INTERVAL_DEFAULT; // in msec, so 20 = 50hz
6161
#define VC31B_REG12 0x11 // INT?
6262
// 0x80 appears to enable interrupts?
6363
// 0x10 = WearStatusDetection (ENV sensor IRQ?)
64-
64+
#define VC31B_REG13 0x12 // ???
6565
#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)
6767
#define VC31B_REG16 0x16 // ENV samplerate. samplesPerSec-6. After how many samples is the 0x10 IRQ asserted for ENV data
6868
#define VC31B_REG17 0x17 // SLOT0 LED Current - 0xEF = maxLedCur
6969
#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
7373

7474
// Interrupts
7575
// 0x10 = WearStatusDetection (ENV sensor IRQ?)
76-
/* Bit fields for VCREG1 */
76+
/* Bit fields for VC31B_REG1 */
7777
#define VC31B_STATUS_CONFLICT 0x10
7878
#define VC31B_STATUS_INSAMPLE 0x08
7979
#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 */
8181
#define VC31B_INT_PS 0x10
8282
#define VC31B_INT_OV 0x08 // OvloadAdjust
8383
#define VC31B_INT_FIFO 0x04
@@ -143,9 +143,6 @@ uint16_t hrmPollInterval = HRM_POLL_INTERVAL_DEFAULT; // in msec, so 20 = 50hz
143143
#define VC31A_UNWEAR_CNT 3
144144
#define VC31A_ISWEAR_CNT 1
145145

146-
#define VC31A_PPG_ADJUSTED 0x1000
147-
#define VC31A_PPG_MASK 0x0FFF
148-
149146
typedef enum
150147
{
151148
AdjustDirection_Null = 0,
@@ -176,6 +173,9 @@ typedef struct {
176173
bool isWearing;
177174
int8_t isWearCnt, unWearCnt; // counters for switching worn/unworn state
178175
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
179179
// the meaning of these is device-dependent but it's nice to have them in one place
180180
uint8_t irqStatus;
181181
uint8_t raw[12];
@@ -258,6 +258,23 @@ static void vc31_w16(uint8_t reg, uint16_t data) {
258258
}
259259

260260

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+
261278

262279
static void vc31_adjust() {
263280
uint16_t adjustParam = 0;
@@ -292,7 +309,7 @@ static void vc31_adjust() {
292309
adjustParam = (uint16_t)(
293310
adjustStep) | VC31A_GREEN_ADJ_ENABLE | VC31A_GREEN_ADJ_UP;
294311
vc31_w16(VC31A_GREEN_ADJ, adjustParam);
295-
vcInfo.ppgValue |= VC31A_PPG_ADJUSTED;
312+
vcInfo.wasAdjusted = 2; // ignore 2 samples worth
296313
}
297314
vcaInfo.adjustInfo.direction = vcaInfo.adjustInfo.directionLast;
298315
vcaInfo.adjustInfo.directionLast = AdjustDirection_Up;
@@ -324,7 +341,7 @@ static void vc31_adjust() {
324341
adjustParam = (uint16_t)(
325342
adjustStep) | VC31A_GREEN_ADJ_ENABLE | VC31A_GREEN_ADJ_DOWN;
326343
vc31_w16(VC31A_GREEN_ADJ, adjustParam);
327-
vcInfo.ppgValue |= VC31A_PPG_ADJUSTED;
344+
vcInfo.wasAdjusted = 2; // ignore 2 samples worth
328345
}
329346
vcaInfo.adjustInfo.direction = vcaInfo.adjustInfo.directionLast;
330347
vcaInfo.adjustInfo.directionLast = AdjustDirection_Down;
@@ -414,12 +431,12 @@ static void vc31b_readfifo(uint8_t startAddr, uint16_t endAddr, uint8_t IndexFla
414431
int dataLength = endAddr - startAddr;
415432
vc31_rx(startAddr, sampleData, dataLength);
416433
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]);
418435
// jsiConsolePrintf("ppg %d\n",ppgValue);
419436
// FIXME - we need a timestamp - hrmCallback creates one itself when it is called
420437
// but if we use the FIFO properly we'll need to stamp it ourselves because
421438
// we'll call it a bunch of times at once
422-
hrmCallback(vcInfo.ppgValue);
439+
vc31_new_ppg(ppgValue); // send PPG value
423440
}
424441
}
425442

@@ -456,7 +473,7 @@ static void vc31b_adjust(uint8_t slotNum,uint8_t ledcur,uint8_t pdres) {
456473
newPdRes = (oldPdRes < 1) ? 0 : (oldPdRes - 1);
457474
}
458475
newLedCurrent = oldLedCurrent;
459-
//vcHr02AddAdjustFlag(slotNum);
476+
vcInfo.wasAdjusted = 2;
460477
}
461478
}
462479
else
@@ -494,10 +511,10 @@ static void vc31b_adjust(uint8_t slotNum,uint8_t ledcur,uint8_t pdres) {
494511
{
495512
newLedCurrent = (oldLedCurrent < vcbInfo.adjustInfo[slotNum].step) ? ledcur:oldLedCurrent - vcbInfo.adjustInfo[slotNum].step;
496513
}
497-
//vcHr02AddAdjustFlag(slotNum);
514+
vcInfo.wasAdjusted = 2;
498515
newPdRes = oldPdRes;
499516
vcbInfo.adjustInfo[slotNum].directionLast = vcbInfo.adjustInfo[slotNum].direction;
500-
}
517+
}
501518

502519
vcbInfo.ledCurrent[slotNum] = newLedCurrent;
503520
vcbInfo.pdRes[slotNum] = newPdRes;
@@ -529,7 +546,7 @@ static void vc31b_slot_adjust(int slotNum) {
529546
if(vcbInfo.ledCurrent[slotNum] != 0) {
530547
vcbInfo.ledCurrent[slotNum] = vcbInfo.ledCurrent[slotNum] - 1;
531548
vcbInfo.ledMaxCurrent[slotNum] = vcbInfo.ledCurrent[slotNum];
532-
//FIXME call addAdjustFlag(slotNum);
549+
vcInfo.wasAdjusted = 1;
533550
vcbInfo.regConfig[slotNum+7] = (vcbInfo.ledCurrent[slotNum] | vcbInfo.ppgGain[slotNum]);
534551
vc31_w((VC31B_REG17 + slotNum), vcbInfo.regConfig[slotNum+7]);
535552
}
@@ -563,20 +580,22 @@ void vc31_irqhandler(bool state, IOEventFlags flags) {
563580
uint8_t *buf = vcInfo.raw;
564581
vc31_rx(VC31A_STATUS, buf, 11);
565582
vcInfo.irqStatus = buf[0];
566-
vcInfo.ppgValue = (buf[2] << 8) | buf[1];
583+
uint16_t ppgValue = (buf[2] << 8) | buf[1];
567584
vcaInfo.currentValue = ((buf[4] << 8) | buf[3]) + 10;
568585
vcaInfo.preValue = (buf[6] << 8) | buf[5];
569586
vcaInfo.psValue = (buf[8] << 8) | buf[7];
570587
vcaInfo.envValue = (buf[10] << 8) | buf[9];
571588

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--;
573593
vc31_adjust();
594+
}
574595
if (vcInfo.irqStatus & VC31A_STATUS_D_PS_OK)
575596
vc31a_wearstatus();
576597

577-
// sanity check ppgOffset to ensure we stay in range
578-
int ppgValue = (vcInfo.ppgValue & VC31A_PPG_MASK);
579-
hrmCallback(ppgValue);
598+
580599
}
581600
if (vcType == VC31B_DEVICE) {
582601
uint8_t *buf = &vcInfo.raw[0];
@@ -598,41 +617,6 @@ void vc31_irqhandler(bool state, IOEventFlags flags) {
598617
vcbInfo.sampleData.currentValue[1] = buf[1] & 0x7F;
599618
vcbInfo.sampleData.pdResValue[2] = (buf[5] >> 4) & 0x07;
600619
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-
636620

637621
// if we had environment sensing, check for wear status and update LEDs accordingly
638622
if (vcInfo.irqStatus & VC31B_INT_PS) {
@@ -658,6 +642,7 @@ void vc31_irqhandler(bool state, IOEventFlags flags) {
658642
vc31b_readfifo(vcbInfo.fifoReadIndex,vcbInfo.fifoReadIndex+vcbInfo.totalSlots*2,0);
659643
}
660644
// now we need to adjust the PPG
645+
if (vcInfo.wasAdjusted>0) vcInfo.wasAdjusted--;
661646
for (int slotNum=0;slotNum<3;slotNum++) {
662647
vc31b_slot_adjust(slotNum);
663648
}
@@ -677,10 +662,10 @@ void vc31_irqhandler(bool state, IOEventFlags flags) {
677662
fifoWriteIndex += slotNum*2;
678663
uint8_t buf[2];
679664
vc31_rx(fifoWriteIndex, buf, 2);
680-
vcInfo.ppgValue = (buf[0]<<8) | buf[1];
665+
uint16_t ppgValue = (buf[0]<<8) | buf[1];
681666
// ONLY do this here because we're not using the FIFO
682667
jsiConsolePrintf("ppg %d\n", vcInfo.ppgValue);
683-
hrmCallback(vcInfo.ppgValue);
668+
vc31_new_ppg(ppgValue); // send PPG value
684669
// now we need to adjust the PPG
685670
for (int slotNum=0;slotNum<3;slotNum++) {
686671
vc31b_slot_adjust(slotNum);
@@ -727,13 +712,15 @@ void hrm_sensor_on(HrmCallback callback) {
727712

728713
memset(&vcInfo, 0, sizeof(vcInfo));
729714
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;
735718

736719
if (vcType == VC31_DEVICE) {
720+
vcaInfo.adjustInfo.step = 307200;
721+
vcaInfo.adjustInfo.direction = AdjustDirection_Null;
722+
vcaInfo.adjustInfo.directionLast = AdjustDirection_Null;
723+
737724
vc31_w(VC31A_GREEN_WAIT, 0xb4);
738725
vc31_w(VC31A_TIA_WAIT, 0x54);
739726
vc31_w(VC31A_PS_DIV, 0x09);
@@ -758,15 +745,17 @@ void hrm_sensor_on(HrmCallback callback) {
758745
// vc31_w16(VC31A_GREEN_ADJ, 0xe8c3);
759746
}
760747
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
762750
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
770759
0x57,0x37, // VC31B_REG20/21 ENV sensitivity?
771760
0x07,0x16,
772761
0x56,0x16,0x00
@@ -821,6 +810,10 @@ void hrm_sensor_off() {
821810
JsVar *hrm_sensor_getJsVar() {
822811
JsVar *o = jsvNewObject();
823812
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));
824817
if (vcType == VC31_DEVICE) {
825818
jsvObjectSetChildAndUnLock(o,"vcCurrent",jsvNewFromInteger(vcaInfo.currentValue));
826819
jsvObjectSetChildAndUnLock(o,"vcPre",jsvNewFromInteger(vcaInfo.preValue));
@@ -833,7 +826,6 @@ JsVar *hrm_sensor_getJsVar() {
833826
jsvObjectSetChildAndUnLock(o,"vcEnv",jsvNewArrayFromBytes(vcbInfo.sampleData.envValue, 3));
834827
}
835828
jsvObjectSetChildAndUnLock(o,"vcIRQ",jsvNewFromInteger(vcInfo.irqStatus));
836-
jsvObjectSetChildAndUnLock(o,"isWearing",jsvNewFromBool(vcInfo.isWearing));
837829
//jsvObjectSetChildAndUnLock(o,"isWearCnt",jsvNewFromInteger(vcInfo.isWearCnt));
838830
//jsvObjectSetChildAndUnLock(o,"unWearCnt",jsvNewFromInteger(vcInfo.unWearCnt));
839831
jsvObjectSetChildAndUnLock(o,"vcRaw",jsvNewArrayBufferWithData(sizeof(vcInfo.raw), vcInfo.raw));

0 commit comments

Comments
 (0)