Not able to read Interrupt Status Register and reset Interrupts on MAX30102

Thread Starter

dabo

Joined Mar 23, 2023
1
I am currently trying to develop a pulse oximeter. For this I use the MAX30102 sensor and the NXP LPC1768 microcontroller. The communication is done via I2C.

I have configured the MAX30102 sensor to generate an interrupt as soon as the FIFO is full.

As soon as I start the program in debug mode, an interrupt is generated (debug jumps to the interrupt service routine and the logic analyzer shows that the interrupt line goes low).

When I try to read the MAX30102's interrupt status registers on this first interrupt, I get a value of 0x00. And no other interrupt is generated either. I suspect this is because the I2C read of the interrupt status register is not working properly, and thus the interrupt on the MAX30102 is not reset.

However, if I read the FIFO data register interrupts are continuously generated because reading this register resets the interrupts on the MAX30102.

But now to the whimsical part. If I read other registers of the MAX30102 with the same I2C read function, I get values. E.g. the FIFO_CONFIG register, in which I have written before. There I get by reading the same value that I have written into the register.

But why does the reading of the interrupt status register with the same function not work?


Following my code. I tried to cut it to the important parts, but i can not narrow it down more.
And I thought better a little too much code than too little.

A short overview:

ext_int1.h & ext_int1.c -> initialise extern Interrupt1 on LPC1768 and ISR for extern Interrupt1

MAX30102.h & MAX30102.c -> read, write and init for MAX30102

i2c.h & i2c.c -> files from NXP to handle the I2C operations

main.c
C:
#include<stdio.h>
#include<stdint.h>
#include<string.h>
#include"LPC17xx.h"
#include"i2c.h"
#include"MAX30102.h"
#include"UART.h"
#include"ext1_int.h"
extern volatile bool interrupt;
#include"LPC17xx.h"

int main(){
    uint8_tint_status1= 0;
    uint8_tint_status2= 0;
    
    I2C0Init();
    
    uart0_init();
    
    set_INT_Prio();
    
    ext1_int_init();   
    
    if(!max30102_reset())
        while(1);                       //Errorif(!max30102_init(SpO2_Mode))
        while(1);                       //Errorwhile(1)
    {       
        if(interrupt == true)
        {   
            NVIC_DisableIRQ(EINT1_IRQn);
                
            if(!max30102_read_reg(INT_STA1_REG, &int_status1))
                while(1);                   //Error
            if(!max30102_read_reg(INT_STA2_REG, &int_status2))
                while(1);                   //Error
            
            //if(int_status1 == xxx)//read FIFOinterrupt = false;
            
            NVIC_EnableIRQ(EINT1_IRQn);
        }
    }
}
ext1_int.h
C:
#ifndef__EXT1_INT_H
#define__EXT1_INT_H
#include<stdbool.h>
#include<stdio.h>
#include<stdint.h>
#include<string.h>
#include"LPC17xx.h"
void ext1_int_init(void);
void EINT1_IRQHandler(void);
void set_INT_Prio(void);
#endif//__EXT1_INT_H
ext1_int.c
C:
#include"ext1_int.h"
#include"MAX30102.h"
volatile bool interrupt;
void ext1_int_init(){
    LPC_SC->EXTMODE |= (1<<1);                              //Edge triggered EINT0LPC_SC->EXTPOLAR &= ~(1<<1);                            //Falling Edge bzw. low active LPC_PINCON->PINSEL4 &= ~(3<<22);                    //Clear bits 22&23 to be updatedLPC_PINCON->PINSEL4 |= (1<<22);                     //ENABLE EINT1 on GPIO2.11NVIC_EnableIRQ(EINT1_IRQn);
}
void EINT1_IRQHandler(){
     LPC_SC->EXTINT     |= (1<<1);                  // clear Interrupt flag interrupt = true;
}
void set_INT_Prio(void){
     NVIC_SetPriorityGrouping(0x04);
    NVIC_SetPriority(I2C0_IRQn,5);
    NVIC_SetPriority(EINT1_IRQn, 29);
}
MAX30102.h
C:
#ifndef __MAX30102_HPP
#define __MAX30102_HPP
//Status
#define INT_STA1_REG 0x00
#define INT_STA2_REG 0x01
#define INT_ENA1_REG 0x02
#define INT_ENA2_REG 0x03
#define FIFO_WR_PTR 0x04
#define FIFO_OVF_COUNT 0x05
#define FIFO_RD_PTR 0x06
#define FIFO_DATA_REG 0x07

//Configuration Registers
#define FIFO_CONFIG_REG 0x08
#define MODE_CONFIG_REG 0x09
#define SPO2_CONFIG_REG 0x0A
#define LED_PULSE_AMPL_1    0x0C
#define LED_PULSE_AMPL_2    0x0D
#define MULTI_LED_CONTR_REG_1 0x11
#define MULTI_LED_CONTR_REG_2 0x12

//MAX30102 I2C Address
#define MAX30102_WR_ADDR 0xAE
#define MAX30102_RD_ADDR 0xAF
#define PORT_USED   0               
#define HR_Mode 0x02                //Red LED only
#define SpO2_Mode 0x03          //RED & IR LED
#define MulitLED_Mode 0x07  //RED & IR LED

#include <stdbool.h>   
#include <stdint.h>
#include "i2c.h"
bool max30102_reset(void);
bool max30102_init(uint8_t Mode);
bool max30102_read_reg(uint8_t reg_addr, uint8_t *data);
bool max30102_write_reg(uint8_t reg_addr, uint8_t data);
#endif //__MAX30102_HPP
MAX30102.c
C:
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "MAX30102.h"


extern volatile uint8_t I2CMasterBuffer[I2C_PORT_NUM][BUFSIZE];
extern volatile uint8_t I2CSlaveBuffer[I2C_PORT_NUM][BUFSIZE];
extern volatile uint32_t I2CReadLength[I2C_PORT_NUM];
extern volatile uint32_t I2CWriteLength[I2C_PORT_NUM];



bool max30102_reset(void)
{
    if(!max30102_write_reg(MODE_CONFIG_REG, 0x40))
    {
        return false;
    }
    return true;
}
bool max30102_init(uint8_t Mode)
{
        if(!max30102_write_reg(INT_ENA1_REG, 0x80))     //A_FULL=1, PPG_RDY_EN=0 ALC_OVF_EN=0 -> 0x80
            while(1);
        if(!max30102_write_reg(INT_ENA2_REG, 0x00))     //DIE_TEMP_RDY_EN = 0
            while(1);
    
        if(!max30102_write_reg(FIFO_WR_PTR, 0x00))
            while(1);
        if(!max30102_write_reg(FIFO_OVF_COUNT, 0x00))
            while(1);
        if(!max30102_write_reg(FIFO_RD_PTR, 0x00))
            while(1);
        
        if(!max30102_write_reg(FIFO_CONFIG_REG, 0xE0))  //SMP_AVE=32(111), FIFO_ROLLOVER_EN=0, FIFO_A_FULL=32(0000) ->1110 0000=0xE0
            while(1);
        
        if(!max30102_write_reg(MODE_CONFIG_REG, Mode))
            while(1);
        
        if(!max30102_write_reg(SPO2_CONFIG_REG, 0x0F))          //SPO2ADC_RGE=7.81 pA LSB Size(00b), SPO2_SR=400 (011b), LED_PW=411us(18bits ADC Resolution) (11b) -> x000 1111 = 0x0F
            while(1);
        
        if(!max30102_write_reg(LED_PULSE_AMPL_1, 0x32))         //IR-LED current = 10mA
            while(1);
        
        
        if(!max30102_write_reg(LED_PULSE_AMPL_2, 0x32))         //RED-LED current = 10mA
            while(1);

    
    return true;
}

bool max30102_read_reg(uint8_t reg_addr, uint8_t *data)
{
    uint8_t i = 0;
    //clear Buffer
    for (i = 0; i < BUFSIZE; i++ )
    {
        I2CMasterBuffer[PORT_USED][i] = 0;
    }

    /* Write SLA(W), address, SLA(R), and read one byte back. */
    I2CWriteLength[PORT_USED] = 2;
    I2CReadLength[PORT_USED] = 1;                                           
    I2CMasterBuffer[PORT_USED][0] = MAX30102_WR_ADDR;
    I2CMasterBuffer[PORT_USED][1] = reg_addr;       /* address */
    I2CMasterBuffer[PORT_USED][2] = MAX30102_RD_ADDR;

    if(I2CEngine(PORT_USED) != I2C_OK)
        return false;

    *data = (uint8_t) I2CSlaveBuffer[PORT_USED][0];

    return true;
}

bool max30102_write_reg(uint8_t reg_addr, uint8_t data)
{
    uint8_t i = 0;
    //clear Buffer
    for (i = 0; i < BUFSIZE; i++ )
    {
        I2CMasterBuffer[PORT_USED][i] = 0;
    }

    I2CWriteLength[PORT_USED] = 3;
    I2CReadLength[PORT_USED] = 0;
    I2CMasterBuffer[PORT_USED][0] = MAX30102_WR_ADDR;
    I2CMasterBuffer[PORT_USED][1] = reg_addr;       /* address */
    I2CMasterBuffer[PORT_USED][2] = data;               /* Data0 */


    if(I2CEngine(PORT_USED) != I2C_OK)
        return false;

    return true;
}
i2c.h
C:
/****************************************************************************
 *   $Id:: i2c.h 5865 2010-12-08 21:42:21Z usb00423                         $
 *   Project: NXP LPC17xx I2C example
 *
 *   Description:
 *     This file contains I2C code header definition.
 *
 ****************************************************************************
 * Software that is described herein is for illustrative purposes only
 * which provides customers with programming information regarding the
 * products. This software is supplied "AS IS" without any warranties.
 * NXP Semiconductors assumes no responsibility or liability for the
 * use of the software, conveys no license or title under any patent,
 * copyright, or mask work right to the product. NXP Semiconductors
 * reserves the right to make changes in the software without
 * notification. NXP Semiconductors also make no representation or
 * warranty that such application will be suitable for the specified
 * use without further testing or modification.
****************************************************************************/

#ifndef __I2C_H
#define __I2C_H


/* If I2C SEEPROM is tested, make sure FAST_MODE_PLUS is 0.
For board to board test, this flag can be turned on. */

#include <stdint.h>
#include "LPC17xx.h"

#define FAST_MODE_PLUS      0

#define I2C_PORT_NUM        3
#define BUFSIZE             64 
#define MAX_TIMEOUT         0x00FFFFFF

static LPC_I2C_TypeDef (* const LPC_I2C[I2C_PORT_NUM]) = { LPC_I2C0, LPC_I2C1, LPC_I2C2 };

#define I2CMASTER           0x01
#define I2CSLAVE            0x02

#define PCF8594_ADDR        0xA0
#define READ_WRITE          0x01

#define MAX30102_WR_ADDR 0xAE
#define MAX30102_RD_ADDR 0xAF

#define RD_BIT              0x01

#define I2C_IDLE              0
#define I2C_STARTED           1
#define I2C_RESTARTED         2
#define I2C_REPEATED_START    3
#define DATA_ACK              4
#define DATA_NACK             5
#define I2C_BUSY              6
#define I2C_NO_DATA           7
#define I2C_NACK_ON_ADDRESS   8
#define I2C_NACK_ON_DATA      9
#define I2C_ARBITRATION_LOST  10
#define I2C_TIME_OUT          11
#define I2C_OK                12

#define I2CONSET_I2EN       (0x1<<6)  /* I2C Control Set Register */
#define I2CONSET_AA         (0x1<<2)
#define I2CONSET_SI         (0x1<<3)
#define I2CONSET_STO        (0x1<<4)
#define I2CONSET_STA        (0x1<<5)

#define I2CONCLR_AAC        (0x1<<2)  /* I2C Control clear Register */
#define I2CONCLR_SIC        (0x1<<3)
#define I2CONCLR_STAC       (0x1<<5)
#define I2CONCLR_I2ENC      (0x1<<6)

#define I2DAT_I2C           0x00000000  /* I2C Data Reg */
#define I2ADR_I2C           0x00000000  /* I2C Slave Address Reg */
#define I2SCLH_SCLH         0x00000080  /* I2C SCL Duty Cycle High Reg */
#define I2SCLL_SCLL         0x00000080  /* I2C SCL Duty Cycle Low Reg */
#define I2SCLH_HS_SCLH      0x00000008  /* Fast Plus I2C SCL Duty Cycle High Reg */
#define I2SCLL_HS_SCLL      0x00000008  /* Fast Plus I2C SCL Duty Cycle Low Reg */



extern void I2C0_IRQHandler( void );
extern void I2C0Init( void );
extern uint32_t I2CStart( uint32_t portNum );
extern uint32_t I2CStop( uint32_t portNum );
extern uint32_t I2CEngine( uint32_t portNum );


#endif /* end __I2C_H */
i2c.c
C:
/****************************************************************************
 *   $Id:: i2c.c 5865 2010-12-08 21:42:21Z usb00423                         $
 *   Project: NXP LPC17xx I2C example
 *
 *   Description:
 *     This file contains I2C code example which include I2C initialization,
 *     I2C interrupt handler, and APIs for I2C access.
 *
 ****************************************************************************
 * Software that is described herein is for illustrative purposes only
 * which provides customers with programming information regarding the
 * products. This software is supplied "AS IS" without any warranties.
 * NXP Semiconductors assumes no responsibility or liability for the
 * use of the software, conveys no license or title under any patent,
 * copyright, or mask work right to the product. NXP Semiconductors
 * reserves the right to make changes in the software without
 * notification. NXP Semiconductors also make no representation or
 * warranty that such application will be suitable for the specified
 * use without further testing or modification.
****************************************************************************/
#include "type.h"
#include "lpc17xx.h"
#include "i2c.h"

volatile uint32_t I2CMasterState[I2C_PORT_NUM] = {I2C_IDLE,I2C_IDLE,I2C_IDLE};
volatile uint32_t timeout[I2C_PORT_NUM] = {0, 0, 0};

volatile uint8_t I2CMasterBuffer[I2C_PORT_NUM][BUFSIZE];
volatile uint8_t I2CSlaveBuffer[I2C_PORT_NUM][BUFSIZE];
volatile uint32_t I2CCount[I2C_PORT_NUM] = {0, 0, 0};
volatile uint32_t I2CReadLength[I2C_PORT_NUM];
volatile uint32_t I2CWriteLength[I2C_PORT_NUM];

volatile uint32_t RdIndex0 = 0, RdIndex1 = 0, RdIndex2 = 0;
volatile uint32_t WrIndex0 = 0, WrIndex1 = 0, WrIndex2 = 0;

void I2C0_IRQHandler(void)
{   
  uint8_t StatValue;

  timeout[0] = 0;
  /* this handler deals with master read and master write only */
  StatValue = LPC_I2C0->I2STAT;
  switch ( StatValue )
  {
    case 0x08:          /* A Start condition is issued. */
    WrIndex0 = 0;
    LPC_I2C0->I2DAT = I2CMasterBuffer[0][WrIndex0++];
    LPC_I2C0->I2CONCLR = (I2CONCLR_SIC | I2CONCLR_STAC);
    break;
    
    case 0x10:          /* A repeated started is issued */
    RdIndex0 = 0;
    /* Send SLA with R bit set, */
    LPC_I2C0->I2DAT = I2CMasterBuffer[0][WrIndex0++];
    LPC_I2C0->I2CONCLR = (I2CONCLR_SIC | I2CONCLR_STAC);
    break;
    
    case 0x18:          /* Regardless, it's a ACK */
    if ( I2CWriteLength[0] == 1 )
    {
      LPC_I2C0->I2CONSET = I2CONSET_STO;      /* Set Stop flag */
      I2CMasterState[0] = I2C_NO_DATA;
    }
    else
    {
      LPC_I2C0->I2DAT = I2CMasterBuffer[0][WrIndex0++];
    }
    LPC_I2C0->I2CONCLR = I2CONCLR_SIC;
    break;

    case 0x28:  /* Data byte has been transmitted, regardless ACK or NACK */
    if ( WrIndex0 < I2CWriteLength[0] )
    {   
      LPC_I2C0->I2DAT = I2CMasterBuffer[0][WrIndex0++]; /* this should be the last one */
    }
    else
    {
      if ( I2CReadLength[0] != 0 )
      {
        LPC_I2C0->I2CONSET = I2CONSET_STA;  /* Set Repeated-start flag */
      }
      else
      {
        LPC_I2C0->I2CONSET = I2CONSET_STO;      /* Set Stop flag */
        I2CMasterState[0] = I2C_OK;
      }
    }
    LPC_I2C0->I2CONCLR = I2CONCLR_SIC;
    break;

    case 0x30:
    LPC_I2C0->I2CONSET = I2CONSET_STO;      /* Set Stop flag */
    I2CMasterState[0] = I2C_NACK_ON_DATA;
    LPC_I2C0->I2CONCLR = I2CONCLR_SIC;
    break;
    
    case 0x40:  /* Master Receive, SLA_R has been sent */
    if ( (RdIndex0 + 1) < I2CReadLength[0] )
    {
      /* Will go to State 0x50 */
      LPC_I2C0->I2CONSET = I2CONSET_AA; /* assert ACK after data is received */
    }
    else
    {
      /* Will go to State 0x58 */
      LPC_I2C0->I2CONCLR = I2CONCLR_AAC;    /* assert NACK after data is received */
    }
    LPC_I2C0->I2CONCLR = I2CONCLR_SIC;
    break;
    
    case 0x50:  /* Data byte has been received, regardless following ACK or NACK */
    I2CSlaveBuffer[0][RdIndex0++] = LPC_I2C0->I2DAT;
    if ( (RdIndex0 + 1) < I2CReadLength[0] )
    {   
      LPC_I2C0->I2CONSET = I2CONSET_AA; /* assert ACK after data is received */
    }
    else
    {
      LPC_I2C0->I2CONCLR = I2CONCLR_AAC;    /* assert NACK on last byte */
    }
    LPC_I2C0->I2CONCLR = I2CONCLR_SIC;
    break;
    
    case 0x58:
    I2CSlaveBuffer[0][RdIndex0++] = LPC_I2C0->I2DAT;
    I2CMasterState[0] = I2C_OK;
    LPC_I2C0->I2CONSET = I2CONSET_STO;  /* Set Stop flag */
    LPC_I2C0->I2CONCLR = I2CONCLR_SIC;  /* Clear SI flag */
    break;

    case 0x20:      /* regardless, it's a NACK */
    case 0x48:
    LPC_I2C0->I2CONSET = I2CONSET_STO;      /* Set Stop flag */
    I2CMasterState[0] = I2C_NACK_ON_ADDRESS;
    LPC_I2C0->I2CONCLR = I2CONCLR_SIC;
    break;
    
    case 0x38:      /* Arbitration lost, in this example, we don't
                    deal with multiple master situation */
    default:
    I2CMasterState[0] = I2C_ARBITRATION_LOST;
    LPC_I2C0->I2CONCLR = I2CONCLR_SIC; 
    break;
  }
  return;
}

uint32_t I2CStart( uint32_t portNum )
{
  uint32_t retVal = FALSE;
 
  timeout[portNum] = 0;
  /*--- Issue a start condition ---*/
  LPC_I2C[portNum]->I2CONSET = I2CONSET_STA;    /* Set Start flag */
    
  /*--- Wait until START transmitted ---*/
  while( 1 )
  {
    if ( I2CMasterState[portNum] == I2C_STARTED )
    {
      retVal = TRUE;
      break;   
    }
    if ( timeout[portNum] >= MAX_TIMEOUT )
    {
      retVal = FALSE;
      break;
    }
    timeout[portNum]++;
  }
  return( retVal );
}

uint32_t I2CStop( uint32_t portNum )
{
  LPC_I2C[portNum]->I2CONSET = I2CONSET_STO;      /* Set Stop flag */
  LPC_I2C[portNum]->I2CONCLR = I2CONCLR_SIC;  /* Clear SI flag */
            
  /*--- Wait for STOP detected ---*/
  while( LPC_I2C[portNum]->I2CONSET & I2CONSET_STO );
  return TRUE;
}

void I2C0Init( void )
{
  LPC_SC->PCONP |= (1 << 7);

  /* set PIO0.27 and PIO0.28 to I2C0 SDA and SCL */
  /* function to 01 on both SDA and SCL. */
  LPC_PINCON->PINSEL1 &= ~((0x03<<22)|(0x03<<24));
  LPC_PINCON->PINSEL1 |= ((0x01<<22)|(0x01<<24));   
 
  /*--- Clear flags ---*/
  LPC_I2C0->I2CONCLR = I2CONCLR_AAC | I2CONCLR_SIC | I2CONCLR_STAC | I2CONCLR_I2ENC;   

  /*--- Reset registers ---*/
#if FAST_MODE_PLUS
  LPC_PINCON->I2CPADCFG |= ((0x1<<0)|(0x1<<2));
  LPC_I2C0->I2SCLL   = I2SCLL_HS_SCLL;
  LPC_I2C0->I2SCLH   = I2SCLH_HS_SCLH;
#else
  LPC_PINCON->I2CPADCFG &= ~((0x1<<0)|(0x1<<2));
  /*LPC_I2C0->I2SCLL   = I2SCLL_SCLL;
  LPC_I2C0->I2SCLH   = I2SCLH_SCLH;*/
    LPC_I2C0->I2SCLL   = 0x20;      //für ~400 kHz
  LPC_I2C0->I2SCLH   = 0x1F;        //für ~400 kHz
#endif

  /* Install interrupt handler */
  NVIC_EnableIRQ(I2C0_IRQn);

  LPC_I2C0->I2CONSET = I2CONSET_I2EN;
  return;
}

uint32_t I2CEngine( uint32_t portNum )
{
  /*--- Issue a start condition ---*/
  LPC_I2C[portNum]->I2CONSET = I2CONSET_STA;    /* Set Start flag */

  I2CMasterState[portNum] = I2C_BUSY;   

  while ( I2CMasterState[portNum] == I2C_BUSY )
  {
    if ( timeout[portNum] >= MAX_TIMEOUT )
    {
      I2CMasterState[portNum] = I2C_TIME_OUT;
      break;
    }
    timeout[portNum]++;
  }
  LPC_I2C[portNum]->I2CONCLR = I2CONCLR_STAC;

  return ( I2CMasterState[portNum] );
}
 
Top