Here is a Filter algorithm that has served me well with several projects. For the moment I'm going to assume your code serves 2 main functions: read the heart rate MAX30102 and report the results. This means the processor execution time can be maximized for these 2 functions.
Reporting process is what it is. That's whatever protocol (UART, SPI, I2C, or LCDOUT etc) you have chosen to make use of the processed data.
As for reading and filtering the MAX30102 data, the first stage is to take multiple ADC reads (or appropriately retrieve the latest data report) each Subroutine CALL and average them. The result gets fed into a circular buffer, where the average is the last X readings, and what you shoot out in your report. It looks something like this:
Somewhere a CALL (GOSUB) is made to check the input from the MAX30102 sensor. Within the Subroutine, you do this 4X and average the results. The average result is fed into a [4] BYTE/WORD circular buffer. An Accumulator adds all buffer entries and divides the result by the number of entries (calculates an average). The value reported is the average of the circular buffer -- buffered value. With me so far? Let's take it for a test drive, shall we?
Code:
b0 VAR BYTE ;Used for FOR/LOOP
HrVal VAR BYTE ;if using 8-bit ADC, ...VAR WORD for 10-bit, or whatever protocol demands
HrValBuf VAR BYTE[4] ;again, WORD for 10-bit, this is your 4 immediate ADC (MAX30102 input) reads Buffer
HrValCir VAR BYTE[4] ;again, WORD for 10-bit, this is your 4 entry Circular Buffer
HrBufAcc VAR WORD ;Total of all 4 HrValBuf readings
HrCirAcc Var WORD ;Total of all 4 HrCirBuf readings
HeartRate VAR BYTE ;again, WORD for 10-bit, End Filtered Result to be Reported or Processed
... ;from Somewhere in Code:
GOSUB Get_HeartRate
GOSUB Send_HeartRate ;Do something with the HeartRate Value Acquired & Filtered
... ;then Continue Code...
Get_Heartrate:
HrValCir[3] = HrValCir[2] ;Rotate the Circular Buffer
HrValCir[2] = HrValCir[1]
HrValCir[1] = HrValCir[0]
FOR b0 = 0 TO 3
ADCIN MAX30102,HrVal ;or I2CREAD or SPIREAD -- Retrieve a Current Value
HrValBuf[b0] = HrVal
NEXT b0
HrBufAcc = HrValBuf[3] + HrValBuf[2] + HrValBuf[1] + HrValBuf[0] ;Add All 4 Readings...
HrValCir[0] = HrBufAcc >> 2 ;Same as HrValCir[0] = HrBufAcc / 4 -- Places the Latest Averaged Value into the Circular Buffer
HrCirAcc = HrValCir[3] + HrValCir[2] + HrValCir[1] + HrValCir[0] ;Totals the Circular Buffer
HeartRate = HrCirAcc >> 2 ;Same as HeartRate = HrCirAcc / 4, Averages the Circular Buffer
RETURN
To change the impact of the filter, you can increase or decrease the size of the HrValBuf[4] buffer and/or the HrValCir[4] buffer. If too jumpy, add more entries for either/both buffer(s). Conversely, to economize, try reducing buffer sizes and see if you still like the results. Changes would have to be reflected in Get_Heartrate:. No, of course this isn't the absolute most efficient code to accomplish the goal, and it is too abbreviated to actually work, but I hope it illustrates an effective filter that has served me well over many projects.
Bookmarks