Last active
May 3, 2020 07:50
-
-
Save Electronza/978d8d890e28f66293303f6e9ee6e626 to your computer and use it in GitHub Desktop.
Heart Rate Click: On EasyPIC v7, with MikroC code
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Project page | |
# https://medium.com/electronza/heart-rate-click-on-easypic-v7-ab26fd558254 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ***************************************************************************** | |
// 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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/******************************************************************************/ | |
/*********** 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