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
ext1_int.h
ext1_int.c
MAX30102.h
MAX30102.c
i2c.h
i2c.c
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);
}
}
}
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
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);
}
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
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;
}
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 */
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] );
}