Skip to content

Instantly share code, notes, and snippets.

@Electronza
Last active May 3, 2020 07:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Electronza/978d8d890e28f66293303f6e9ee6e626 to your computer and use it in GitHub Desktop.
Save Electronza/978d8d890e28f66293303f6e9ee6e626 to your computer and use it in GitHub Desktop.
Heart Rate Click: On EasyPIC v7, with MikroC code
# Project page
# https://medium.com/electronza/heart-rate-click-on-easypic-v7-ab26fd558254
// *****************************************************************************
// HEART RATE CLICK / MAX30100 demo code in MikroC Pro for PIC
//
// Place Heart rate click in socket #1 of EasyPIC v7
// Microcontroller is PIC18F45K22 @ 8MHz
// Uses the LCD onbloard EasyPIC v7
// *****************************************************************************
#include "max30100.h"
// Initialize LCD on EasyPIC v7
// Lcd pinout settings
sbit LCD_RS at RB4_bit;
sbit LCD_EN at RB5_bit;
sbit LCD_D7 at RB3_bit;
sbit LCD_D6 at RB2_bit;
sbit LCD_D5 at RB1_bit;
sbit LCD_D4 at RB0_bit;
// Pin direction
sbit LCD_RS_Direction at TRISB4_bit;
sbit LCD_EN_Direction at TRISB5_bit;
sbit LCD_D7_Direction at TRISB3_bit;
sbit LCD_D6_Direction at TRISB2_bit;
sbit LCD_D5_Direction at TRISB1_bit;
sbit LCD_D4_Direction at TRISB0_bit;
// Used to show data on LCD
char txt[8];
// Device ID
unsigned short id;
// Used to read samples
unsigned int i;
int samples;
unsigned short wrp;
// Used to store HR and SPO2 reflectance
unsigned int HR;
unsigned int SPO2;
void main() {
ANSELB = 0x00; // Configure PORTB pins as digital
ANSELC = 0x00;
ANSELE = 0x00;
TRISE = 0x00; // Configure PORTE pins as outputs
LATE = 0x00;
// Initialize LCD and print some text
Lcd_Init();
Delay_ms(100);
Lcd_Cmd(_LCD_CLEAR);
Lcd_Cmd(_LCD_CURSOR_OFF); // Cursor off
Lcd_Out(1,1,"Heart Rate Click"); // Write text in first row
Lcd_Out(2,1,"Demo code"); // Write text in second row
Delay_ms(2000);
// Initialize I2C1 (RC3 is SCL, RC4 is SDA)
I2C1_Init(400000);
Delay_ms(10);
// First we reset the MAX30100
MAX30100_reset();
Delay_ms(100); // Allow for some time to pass
// Is the heart rate click connected?
// We read from register 0xFF
// If the value is 0x11 then the sensor is connected
id = MAX30100_getPartID();
if (id = 0x11) {
Lcd_Cmd(_LCD_CLEAR);
Lcd_Out(1,1,"MAX30100 found");
id = MAX30100_getRevID();
ByteToHex(id, txt);
Lcd_Out(2,1,"Rev");
Lcd_Out(2,5,txt);
Delay_ms(2000);
}
// Set LED current
MAX30100_setLEDs(i11, i8);
// Set sample rate
MAX30100_setSR(sr50);
// Set pulse width
MAX30100_setPW(pw1600);
MAX30100_InitFIFO();
wrp = 0; // used to check FIFO overflow
// Wake up
MAX30100_wakeup();
// We set SpO2 mode and start measuring
MAX30100_SetSPO2 ();
// Or we can go in heart rate mode
//MAX30100_SetHR ();
Lcd_Cmd(_LCD_CLEAR);
while(1){
// We first check for overflows
wrp = MAX30100_read ( MAX30100_OVF_COUNTER );
if (wrp == 0x0F){ // signal overflow
LATE=0x01; // light LED on pin RE0
Lcd_Cmd(_LCD_CLEAR);
Lcd_Out(1,1,"FIFO Overflow");
Lcd_Out(2,1,"Press RESET");
while(1); // blocking
}
// Gets the number of samples in the FIFO
samples = MAX30100_getNumSamp();
// Then reads the samples
if (samples > 0 ){ // we have data in FIFO
for (i = 0; i < samples; i++){
// read one sample from the sensor
MAX30100_readFIFO(&HR, &SPO2);
// Process the samples
// One has about 14ms to do the processing
// before the next data sample is ready
// Here we show the gathered data on LCD
IntToStr(HR, txt);
Lcd_Out(1,1,txt);
IntToStr(SPO2, txt);
Lcd_Out(2,1,txt);
} // end of sample reading loop
} // end if statement that checks if we have data
else { // There's no data in FIFO
Delay_ms(20); // Wait for samples to be aquired
}
} // end of while(1)
} // end of void main()
#include "max30100.h"
// Reads from register 0xFE
// returns version ID
unsigned short MAX30100_getRevID(void){
unsigned short reg;
reg = MAX30100_read (MAX30100_REVISION_ID);
return reg;
}
// Reads from register 0xFF
// should return 0x11
unsigned short MAX30100_getPartID(void){
unsigned short reg;
reg = MAX30100_read (MAX30100_PART_ID);
return reg;
}
// Resets the MAX30100 IC
void MAX30100_reset(void){
unsigned short reg;
reg = MAX30100_read (MAX30100_MODE_CONFIG);
// RESET bit is B6
// 0x40 = 01000 0000
reg = reg | 0x40; // Set reset bit to 1
MAX30100_write (MAX30100_MODE_CONFIG, reg);
}
// Wakes up the MAX30100
// Clears bit 7 of MODE CONFIG register
void MAX30100_wakeup(void){
unsigned short reg;
reg = MAX30100_read (MAX30100_MODE_CONFIG);
// RESET bit is B7
// 0x7F = 0111 1111
reg = reg & 0x7F; // Set SHDN bit to 0
MAX30100_write (MAX30100_MODE_CONFIG, reg);
}
// Wakes up the MAX30100
// Sets bit 7 of MODE CONFIG register
void MAX30100_shutdown (void){
unsigned short reg;
reg = MAX30100_read (MAX30100_MODE_CONFIG);
// RESET bit is B7
// 0x80 = 1000 0000
reg = reg | 0x80; // Set SHDN bit to 1
MAX30100_write (MAX30100_MODE_CONFIG, reg);
}
// Sets Heart rate mode
// This means MODE{2:0} = 0b010 (or 0x02 in hexadecimal)
void MAX30100_SetHR (void){
unsigned short reg;
reg = MAX30100_read (MAX30100_MODE_CONFIG);
// RESET bit is B7
// First we clear bits 2:0
reg = reg & 0xF8;
// Then we set bits 2:0 to 0x02
reg = reg | 0x02;
MAX30100_write (MAX30100_MODE_CONFIG, reg);
}
// Sets SPO2 rate mode
// This means MODE{2:0} = 0b011 (or 0x03 in hexadecimal)
void MAX30100_SetSPO2 (void){
unsigned short reg;
reg = MAX30100_read (MAX30100_MODE_CONFIG);
// RESET bit is B7
// First we clear bits 2:0
reg = reg & 0xF8;
// Then we set bits 2:0 to 0x03
reg = reg | 0x03;
MAX30100_write (MAX30100_MODE_CONFIG, reg);
}
// Initializes FIFO
// Sets RD and WR pointers to 0
// Clears OVF
void MAX30100_InitFIFO (void){
MAX30100_write (MAX30100_FIFO_W_POINTER, 0x00);
MAX30100_write (MAX30100_FIFO_R_POINTER, 0x00);
MAX30100_write (MAX30100_OVF_COUNTER, 0x00);
}
// Sets LED currents
void MAX30100_setLEDs(ledCurrent red, ledCurrent ir){
unsigned short reg;
reg = ( red << 4 ) | ir;
MAX30100_write (MAX30100_LED_CONFIG, reg);
}
// Sets sample rate
// sample rate is bits 4:2 of register MAX30100_SPO2_CONFIG
// bitmask is 0xE3
void MAX30100_setSR (sampleRate sr){
unsigned short reg;
reg = MAX30100_read (MAX30100_SPO2_CONFIG);
reg = reg & 0xE3;
reg = reg | (sr << 2);
MAX30100_write (MAX30100_SPO2_CONFIG, reg);
}
// Sets pulse width
// sample rate is bits 1:0 of register MAX30100_SPO2_CONFIG
void MAX30100_setPW (pulseWidth pw){
unsigned short reg;
reg = MAX30100_read (MAX30100_SPO2_CONFIG);
reg = reg & 0xFC;
reg = reg | pw;
MAX30100_write (MAX30100_SPO2_CONFIG, reg);
}
// now we write functions to work with fifo
// Gets number of samples in FIFO
int MAX30100_getNumSamp(void){
unsigned short wreg;
unsigned short rreg;
wreg = MAX30100_read (MAX30100_FIFO_W_POINTER);
rreg = MAX30100_read (MAX30100_FIFO_R_POINTER);
return (abs( 16 + (int)wreg - (int)rreg ) % 16);
}
// In each sample we have to read four bytes
void MAX30100_readFIFO(unsigned int *data1, unsigned int *data2){
unsigned int reg;
unsigned int readout;
I2C1_Start();
I2C1_Wr(MAX30100_WADDRESS); // send byte via I2C (device address + W)
I2C1_Wr(MAX30100_FIFO_DATA_REG); // send byte (data address)
I2C1_Repeated_Start(); // issue I2C signal repeated start
I2C1_Wr(MAX30100_RADDRESS); // send byte (device address + R)
readout= I2C1_Rd(1u); // I2C read with ACK
reg = I2C1_Rd(1u);
*data2 = (readout << 8) | reg;
readout = I2C1_Rd(1u);
reg = I2C1_Rd(0u); // I2C read with NACK (tells slave to let go)
*data1 = (readout << 8) | reg;
I2C1_Stop();
}
// *****************************************************************************
// My I2C read and write functions
unsigned short MAX30100_read ( unsigned short device_register ){
unsigned short read_data;
I2C1_Start();
I2C1_Wr(MAX30100_WADDRESS); // send byte via I2C (device address + W)
I2C1_Wr(device_register); // send byte (data address)
I2C1_Repeated_Start(); // issue I2C signal repeated start
I2C1_Wr(MAX30100_RADDRESS); // send byte (device address + R)
read_data = I2C1_Rd(0u); // read the data (NO acknowledge)
//while (!I2C1_Is_Idle())
// asm nop; // read the data (NO acknowledge)
I2C1_Stop();
return read_data;
}
void MAX30100_write (unsigned short device_register, unsigned short reg_data){
I2C1_Start();
I2C1_Wr(MAX30100_WADDRESS); // send byte via I2C (device address + W)
I2C1_Wr(device_register); // send byte (data address)
I2C1_Wr(reg_data);
while (!I2C1_Is_Idle())
asm nop; // read the data (NO acknowledge)
I2C1_Stop();
}
/******************************************************************************/
/*********** PULSE OXIMETER AND HEART RATE REGISTER MAPPING **************/
/******************************************************************************/
// status registers
#define MAX30100_INT_STATUS 0x00
#define MAX30100_INT_ENABLE 0x01
// FIFO registers
#define MAX30100_FIFO_W_POINTER 0x02
#define MAX30100_OVF_COUNTER 0x03
#define MAX30100_FIFO_R_POINTER 0x04
#define MAX30100_FIFO_DATA_REG 0x05
// configuration registers
#define MAX30100_MODE_CONFIG 0x06
#define MAX30100_SPO2_CONFIG 0x07
#define MAX30100_LED_CONFIG 0x09
// temperature registers
#define MAX30100_TEMP_INTEGER 0x16
#define MAX30100_TEMP_FRACTION 0x17
// PART ID registers
#define MAX30100_REVISION_ID 0xFE
#define MAX30100_PART_ID 0xFF
#define I_AM_MAX30100 0x11
/************************************** REGISTERS VALUE *******************************************/
// MAX30100 I2C addresses
#define MAX30100_WADDRESS 0xAE // 8bit address converted to 7bit + W
#define MAX30100_RADDRESS 0xAF // 8bit address converted to 7bit + R
//Enable interrupts
#define MAX30100_INT_ENB_A_FULL 0x80
#define MAX30100_INT_ENB_TEMP_RDY 0x40
#define MAX30100_INT_ENB_HR_RDY 0x20
#define MAX30100_INT_ENB_SO2_RDY 0x10
#define MAX30100_MODE_HR 0x02
#define MAX30100_MODE_SPO2 0x03
//SPO2 configuration
#define MAX30100_SPO2_HI_RES_EN 0x40
typedef enum{ // This is the same for both LEDs
pw200, // 200us pulse
pw400, // 400us pulse
pw800, // 800us pulse
pw1600 // 1600us pulse
}pulseWidth;
typedef enum{
sr50, // 50 samples per second
sr100, // 100 samples per second
sr167, // 167 samples per second
sr200, // 200 samples per second
sr400, // 400 samples per second
sr600, // 600 samples per second
sr800, // 800 samples per second
sr1000 // 1000 samples per second
}sampleRate;
typedef enum{
i0, // No current
i4, // 4.4mA
i8, // 7.6mA
i11, // 11.0mA
i14, // 14.2mA
i17, // 17.4mA
i21, // 20.8mA
i24, // 24.0mA
i27, // 27.1mA
i31, // 30.6mA
i34, // 33.8mA
i37, // 37.0mA
i40, // 40.2mA
i44, // 43.6mA
i47, // 46.8mA
i50 // 50.0mA
}ledCurrent;
typedef enum{
low, // low resolution SPO2
high // high resolution SPO2 (16 bit with 1.6ms LED pulse width)
}high_resolution;
// Functions
// Reads from register 0xFE
// returns version ID
unsigned short MAX30100_getRevID(void);
// Reads from register 0xFF
// should return 0x11
unsigned short MAX30100_getPartID(void);
// Resets the MAX30100 IC
void MAX30100_reset(void);
// Wakes up the MAX30100
void MAX30100_wakeup(void);
// Shuits down the MAX30100
void MAX30100_shutdown(void);
// Sets Heart rate mode
void MAX30100_SetHR (void);
// Sets SPO2 rate mode
void MAX30100_SetSPO2 (void);
// Initializes FIFO
// Sets RD and WR pointers to 0
// Clears OVF
void MAX30100_InitFIFO (void);
// Sets LED currents
void MAX30100_setLEDs(ledCurrent red, ledCurrent ir);
// Used to set sample rate
void MAX30100_setSR(sampleRate sr);
// Sets pulse width
// sample rate is bits 1:0 of register MAX30100_SPO2_CONFIG
void MAX30100_setPW (pulseWidth pw);
// Gets number of samples in FIFO
int MAX30100_getNumSamp(void);
// Reads FIFO data
void MAX30100_readFIFO(unsigned long *data1, unsigned long *data2);
// aditional I2C functions
unsigned short MAX30100_read (unsigned short device_register);
void MAX30100_write (unsigned short device_register, unsigned short reg_data);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment