This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

MAX30003 unable to read right datas from ECG_FIFO registers

I tried to drive MAX30003 to get ECG signals through nrf52832.

I use an analog signal instrument to give the standard ECG signals. Now,the nrf's SPI timing sequence is right, and the data I got from the MAX30003 RTOR registers(0X25) is right (HR values is similiar to the signal instrument ).But the data I read from ECG_FIFO is not true. Can I get some help? 

#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "app_uart.h"
#include "app_error.h"
#include "nrf_delay.h"
#include "nrf_gpio.h"
#include "boards.h"
#include "max30003.h"
#include "nrf_drv_spi.h"
#include "SEGGER_RTT.h"
#include "nrf_drv_pwm.h"



#define UART_TX_BUF_SIZE 256                         /**< UART TX buffer size. */
#define UART_RX_BUF_SIZE 1                           /**< UART RX buffer size. */

static nrf_drv_pwm_t m_pwm0 = NRF_DRV_PWM_INSTANCE(0);
#define APP_PWM_DEFAULT_CONFIG_1CH(period_in_us, pin)
#define USED_PWM(idx) (1UL << idx)
static uint8_t m_used = 0;

static void pwm(void)
{
    nrf_drv_pwm_config_t const config0 =
    {
        .output_pins =
        {
            MAX30003_PIN_FCLK | NRF_DRV_PWM_PIN_INVERTED,  // channel 0  Êä³öÒý½Å
            NRF_DRV_PWM_PIN_NOT_USED,             // channel 1
            NRF_DRV_PWM_PIN_NOT_USED,             // channel 2
            NRF_DRV_PWM_PIN_NOT_USED,             // channel 3
        },
        .irq_priority = APP_IRQ_PRIORITY_LOWEST,
        .base_clock   = NRF_PWM_CLK_16MHz,
        .count_mode   = NRF_PWM_MODE_UP,
        .top_value    = 256,
        .load_mode    = NRF_PWM_LOAD_COMMON,
        .step_mode    = NRF_PWM_STEP_AUTO
    };
    APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm0, &config0, NULL));
    m_used |= USED_PWM(0);

    
			static uint16_t /*const*/ seq_values[] =
    {
         0x8000,
             0,
    };
    nrf_pwm_sequence_t const seq =
    {
        .values.p_common = seq_values,
        .length          = NRF_PWM_VALUES_LENGTH(seq_values),
        .repeats         = 0,
        .end_delay       = 0
    };

    (void)nrf_drv_pwm_simple_playback(&m_pwm0, &seq, 1, NRF_DRV_PWM_FLAG_LOOP);
}


void uart_error_handle(app_uart_evt_t * p_event)
{
    if (p_event->evt_type == APP_UART_COMMUNICATION_ERROR)
    {
        APP_ERROR_HANDLER(p_event->data.error_communication);
    }
    else if (p_event->evt_type == APP_UART_FIFO_ERROR)
    {
        APP_ERROR_HANDLER(p_event->data.error_code);
    }
}
/**********************************************************************************************
 * Ãè  Êö : ´®¿Ú³õʼ»¯¡£²¨ÌØÂÊ=115200bps
 * Èë  ²Î : ÎÞ
 * ·µ»ØÖµ : ÎÞ
 *********************************************************************************************/ 
void uart_init(void)
{
	  uint32_t err_code;
    const app_uart_comm_params_t comm_params =
    {
          RX_PIN_NUMBER,
          TX_PIN_NUMBER,	
          RTS_PIN_NUMBER,
          CTS_PIN_NUMBER,
          APP_UART_FLOW_CONTROL_DISABLED,
          false, 
          UART_BAUDRATE_BAUDRATE_Baud115200
    };

    APP_UART_FIFO_INIT(&comm_params,
                         UART_RX_BUF_SIZE,
                         UART_TX_BUF_SIZE,
                         uart_error_handle,
                         APP_IRQ_PRIORITY_LOW,
                         err_code);

    APP_ERROR_CHECK(err_code);
}




/**********************************************************************************************
 * Ãè  Êö : mainº¯Êý
 * Èë  ²Î : ÎÞ
 * ·µ»ØÖµ : ÎÞ
 *********************************************************************************************/ 
int main(void)
{
	  uint8_t SPI_temp_32b[4],SPI_temp_32b1[4],SPI_temp_32b2[4];
		uint8_t DataPacketHeader[20];
		signed long ecgdata;
		unsigned long data;

    uart_init();
		SEGGER_RTT_ConfigUpBuffer(0, NULL, NULL, 0, SEGGER_RTT_MODE_NO_BLOCK_SKIP);
	  nrf_delay_ms(100);
		
		nrf_gpio_cfg_output(MAX30003_PIN_FCLK);
		pwm();
		max30003_spi_init();
		max30003_init();
	
    while (true)
    {

			max30003_read_register(MAX30003_REGADDR_ECG_FIFO,SPI_temp_32b2);

			data = (SPI_temp_32b2[0] <<16)|(SPI_temp_32b2[1] <<8)|((SPI_temp_32b2[2] >>6)&0x03);
			ecgdata = (signed long) (data);

			printf("%d\r\n",ecgdata);			

			max30003_read_register(MAX30003_REGADDR_RTOR,SPI_temp_32b);
			unsigned long RTOR_msb = (unsigned long) (SPI_temp_32b[0]);
			unsigned char RTOR_lsb = (unsigned char) (SPI_temp_32b[1]);
	 
			unsigned long rtor = (RTOR_msb<<8 | RTOR_lsb);
			rtor = ((rtor >>2) & 0x3fff) ;		
			float hr =  60 /((float)rtor*0.008); 
			unsigned int HR = (unsigned int)hr;  // type cast to int
			unsigned int RR = (unsigned int)rtor*8 ;  //8ms
			
//			printf("%d\r\n",HR);
	
			DataPacketHeader[0] = 0x0A;
      DataPacketHeader[1] = 0xFA;
      DataPacketHeader[2] = 0x0C;
      DataPacketHeader[3] = 0;
      DataPacketHeader[4] = 0x02;
   
      DataPacketHeader[5] = ecgdata;
      DataPacketHeader[6] = ecgdata>>8;
      DataPacketHeader[7] = ecgdata>>16;
      DataPacketHeader[8] = ecgdata>>24; 
   
      DataPacketHeader[9] =  RR ;
      DataPacketHeader[10] = RR >>8;
      DataPacketHeader[11] = 0x00;
      DataPacketHeader[12] = 0x00; 

      DataPacketHeader[13] = HR ;
      DataPacketHeader[14] = HR >>8;
      DataPacketHeader[15] = 0x00;
      DataPacketHeader[16] = 0x00; 
        
      DataPacketHeader[17] = 0x00;
      DataPacketHeader[18] = 0x0b;
			
//			for(int i=0;i<19;i++)
//			{app_uart_put(DataPacketHeader[i]);}
			
    }	

}

/********************************************END FILE*******************************************/

Parents
  • Hi

    How do you know the data is incorrect?

    What do you receive from the sensor, and what do you expect?

    Have you tried to log the status of the SPI_temp_32b2[] array so you can see if data conversion on line 132 of main.c is correct?

    Best regards
    Torbjørn

  • Hi~ 

    I calculate ECG from the values of the SPI_temp_32b2[] array and compared them with the analog instrument.

    I received values like...figure 1. But when it's drived by arduino, it can received right datas like figure 2 and 3.

     Fig 1    Fig 2       Fig 3  

    I have tried log the  SPI_temp_32b2[] array,the data is incorrect...

    Maybe I read the ECG_FIFO in a wrong way?..

  • Hi

    Are you sure that the nRF52 implementation sends the same setup/configuration commands to the ECG sensor?

    I assume you might have to send some commands to put it in the right state?

    Have you tried to probe the SPI lines when using the Arduino so that you can compare it to the nRF52 implementation, and see if there is a significant difference on the data sent back and forth?

    Best regards
    Torbjørn

  • The register configuration of MAX30003 was same,here is the Arduino codes.

    #include<SPI.h>
    #include <TimerOne.h>
    #include "MAX30003.h"
    
    #define MAX30003_CS_PIN   7
    #define CLK_PIN          6
    
    volatile char SPI_RX_Buff[5] ;
    volatile char *SPI_RX_Buff_Ptr;
    int i=0;
    unsigned long uintECGraw = 0;
    signed long intECGraw=0;
    uint8_t DataPacketHeader[20];
    uint8_t data_len = 8;
    signed long ecgdata;
    unsigned long data;
    
    char SPI_temp_32b[4];
    char SPI_temp_Burst[100];
    
    // 32KHz clock using timer1
    void timerIsr()
    {
        digitalWrite( CLK_PIN, digitalRead(CLK_PIN ) ^ 1 ); // toggle Digital6 attached to FCLK  of MAX30003
    }
    
    void setup() 
    {
        Serial.begin(115200); //Serial begin
        
        pinMode(MAX30003_CS_PIN,OUTPUT);
        digitalWrite(MAX30003_CS_PIN,HIGH); //disable device
    
        SPI.begin();
        SPI.setBitOrder(MSBFIRST); 
        SPI.setDataMode(SPI_MODE0);
      
      //please enable the below lines if you are using older version of Protocentral_max30003 board without 32k f-clock generator
        SPI.setClockDivider(SPI_CLOCK_DIV4);
        pinMode(CLK_PIN,OUTPUT);
         
         //Start CLK timer
        Timer1.initialize(16);              // set a timer of length 100000 microseconds (or 0.1 sec - or 10Hz => the led will blink 5 times, 5 cycles of on-and-off, per second)
        Timer1.attachInterrupt( timerIsr ); // attach the service routine here
    
        MAX30003_begin();   // initialize MAX30003 
    }
    
    void loop() 
    {
        MAX30003_Reg_Read(ECG_FIFO);
    
    //    unsigned long data0 = (unsigned long) (SPI_temp_32b[0]);
    //    data0 = data0 <<24;
    //    unsigned long data1 = (unsigned long) (SPI_temp_32b[1]);
    //    data1 = data1 <<16;
    //    unsigned long data2 = (unsigned long) (SPI_temp_32b[2]);
    //    data2 = data2 >>6;
    //    data2 = data2 & 0x03;
    //    
    //    data = (unsigned long) (data0 | data1 | data2);
    //    ecgdata = (signed long) (data);
    
        unsigned long data0 = (unsigned long) (SPI_temp_32b[0]);
        data0 = data0 <<16;
        unsigned long data1 = (unsigned long) (SPI_temp_32b[1]);
        data1 = data1 <<8;
        unsigned long data2 = (unsigned long) (SPI_temp_32b[2]);
        data2 = data2 >>6;
        data2 = data2 & 0x03;
        
        data = (unsigned long) (data0 | data1 | data2);
        ecgdata = (signed long) (data);
    
        MAX30003_Reg_Read(RTOR);
        unsigned long RTOR_msb = (unsigned long) (SPI_temp_32b[0]);
       // RTOR_msb = RTOR_msb <<8;
        unsigned char RTOR_lsb = (unsigned char) (SPI_temp_32b[1]);
    
        unsigned long rtor = (RTOR_msb<<8 | RTOR_lsb);
        rtor = ((rtor >>2) & 0x3fff) ;
      
        float hr =  60 /((float)rtor*0.008); 
        unsigned int HR = (unsigned int)hr;  // type cast to int
    
        unsigned int RR = (unsigned int)rtor*8 ;  //8ms
    
         Serial.print(ecgdata);
         Serial.print("\n");
    //     Serial.print(RTOR_msb);
    //     Serial.print(",");
    //     Serial.print(RTOR_lsb);
    //     Serial.print(",");
    //     Serial.print(rtor); 
    //     Serial.print(",");
    //     Serial.print(RR);
    //     Serial.print(",");
    //     Serial.println(HR);      
    
          DataPacketHeader[0] = 0x0A;
          DataPacketHeader[1] = 0xFA;
          DataPacketHeader[2] = 0x0C;
          DataPacketHeader[3] = 0;
          DataPacketHeader[4] = 0x02;
       
    //      DataPacketHeader[5] = ecgdata;
    //      DataPacketHeader[6] = ecgdata>>8;
    //      DataPacketHeader[7] = ecgdata>>16;
    //      DataPacketHeader[8] = ecgdata>>24; 
          DataPacketHeader[5] = data0;
          DataPacketHeader[6] = data1;
          DataPacketHeader[7] = data2;
          DataPacketHeader[8] = data;  
       
          DataPacketHeader[9] =  RR ;
          DataPacketHeader[10] = RR >>8;
          DataPacketHeader[11] = 0x00;
          DataPacketHeader[12] = 0x00; 
    
          DataPacketHeader[13] = HR ;
          DataPacketHeader[14] = HR >>8;
          DataPacketHeader[15] = 0x00;
          DataPacketHeader[16] = 0x00; 
            
          DataPacketHeader[17] = 0x00;
          DataPacketHeader[18] = 0x0b;
    //  
    //      for(i=0; i<19; i++) // transmit the data
    //      {
    //        Serial.write(DataPacketHeader[i]);  
    //       }    
    
        delay(8);      
    }
    
    void MAX30003_Reg_Write (unsigned char WRITE_ADDRESS, unsigned long data)
    {
     
        // now combine the register address and the command into one byte:
         byte dataToSend = (WRITE_ADDRESS<<1) | WREG;
      
         // take the chip select low to select the device:
         digitalWrite(MAX30003_CS_PIN, LOW);
         
         delay(2);
         SPI.transfer(dataToSend);   //Send register location
         SPI.transfer(data>>16);     //number of register to wr
         SPI.transfer(data>>8);      //number of register to wr
         SPI.transfer(data);      //Send value to record into register
         delay(2);
         
         // take the chip select high to de-select:
         digitalWrite(MAX30003_CS_PIN, HIGH);
    }
    
    void max30003_sw_reset(void)
    {
        MAX30003_Reg_Write(SW_RST,0x000000);     
        delay(100);
    }
    
    void max30003_synch(void)
    {
        MAX30003_Reg_Write(SYNCH,0x000000);
    }
    
    void MAX30003_Reg_Read(uint8_t Reg_address)
    {
        uint8_t SPI_TX_Buff;
       
        digitalWrite(MAX30003_CS_PIN, LOW);
        
        SPI_TX_Buff = (Reg_address<<1 ) | RREG;
        SPI.transfer(SPI_TX_Buff); //Send register location
         
        for ( i = 0; i < 3; i++)
        {
           SPI_temp_32b[i] = SPI.transfer(0xff);
        }
      
        digitalWrite(MAX30003_CS_PIN, HIGH);
    }
    
    void MAX30003_Read_Data(int num_samples)
    {
        uint8_t SPI_TX_Buff;
      
        digitalWrite(MAX30003_CS_PIN, LOW);   
      
        SPI_TX_Buff = (ECG_FIFO_BURST<<1 ) | RREG;
        SPI.transfer(SPI_TX_Buff); //Send register location
      
        for ( i = 0; i < num_samples*3; ++i)
        {
          SPI_temp_Burst[i] = SPI.transfer(0x00);
        }
        
        digitalWrite(MAX30003_CS_PIN, HIGH);  
    }
    
    void MAX30003_begin()
    {    
        max30003_sw_reset();
        delay(100);
        MAX30003_Reg_Write(CNFG_GEN, 0x081007);
        delay(100);
        MAX30003_Reg_Write(CNFG_CAL, 0x720000);  // 0x700000  
        delay(100);
        MAX30003_Reg_Write(CNFG_EMUX,0x0B0000);
        delay(100);
        MAX30003_Reg_Write(CNFG_ECG, 0x805000);  // d23 - d22 : 10 for 250sps , 00:500 sps
        delay(100);
        MAX30003_Reg_Write(CNFG_RTOR1,0x3fc600);   
        delay(100);  
        max30003_synch();
        delay(100);
        
    }

    I tried to probe the SPI lines through logic analyzer when using the Arduino, but the timing sequence was strange.But when I used NRF,the timing sequence was corresponding to the datasheet. So..I can't obtain the difference.

Reply
  • The register configuration of MAX30003 was same,here is the Arduino codes.

    #include<SPI.h>
    #include <TimerOne.h>
    #include "MAX30003.h"
    
    #define MAX30003_CS_PIN   7
    #define CLK_PIN          6
    
    volatile char SPI_RX_Buff[5] ;
    volatile char *SPI_RX_Buff_Ptr;
    int i=0;
    unsigned long uintECGraw = 0;
    signed long intECGraw=0;
    uint8_t DataPacketHeader[20];
    uint8_t data_len = 8;
    signed long ecgdata;
    unsigned long data;
    
    char SPI_temp_32b[4];
    char SPI_temp_Burst[100];
    
    // 32KHz clock using timer1
    void timerIsr()
    {
        digitalWrite( CLK_PIN, digitalRead(CLK_PIN ) ^ 1 ); // toggle Digital6 attached to FCLK  of MAX30003
    }
    
    void setup() 
    {
        Serial.begin(115200); //Serial begin
        
        pinMode(MAX30003_CS_PIN,OUTPUT);
        digitalWrite(MAX30003_CS_PIN,HIGH); //disable device
    
        SPI.begin();
        SPI.setBitOrder(MSBFIRST); 
        SPI.setDataMode(SPI_MODE0);
      
      //please enable the below lines if you are using older version of Protocentral_max30003 board without 32k f-clock generator
        SPI.setClockDivider(SPI_CLOCK_DIV4);
        pinMode(CLK_PIN,OUTPUT);
         
         //Start CLK timer
        Timer1.initialize(16);              // set a timer of length 100000 microseconds (or 0.1 sec - or 10Hz => the led will blink 5 times, 5 cycles of on-and-off, per second)
        Timer1.attachInterrupt( timerIsr ); // attach the service routine here
    
        MAX30003_begin();   // initialize MAX30003 
    }
    
    void loop() 
    {
        MAX30003_Reg_Read(ECG_FIFO);
    
    //    unsigned long data0 = (unsigned long) (SPI_temp_32b[0]);
    //    data0 = data0 <<24;
    //    unsigned long data1 = (unsigned long) (SPI_temp_32b[1]);
    //    data1 = data1 <<16;
    //    unsigned long data2 = (unsigned long) (SPI_temp_32b[2]);
    //    data2 = data2 >>6;
    //    data2 = data2 & 0x03;
    //    
    //    data = (unsigned long) (data0 | data1 | data2);
    //    ecgdata = (signed long) (data);
    
        unsigned long data0 = (unsigned long) (SPI_temp_32b[0]);
        data0 = data0 <<16;
        unsigned long data1 = (unsigned long) (SPI_temp_32b[1]);
        data1 = data1 <<8;
        unsigned long data2 = (unsigned long) (SPI_temp_32b[2]);
        data2 = data2 >>6;
        data2 = data2 & 0x03;
        
        data = (unsigned long) (data0 | data1 | data2);
        ecgdata = (signed long) (data);
    
        MAX30003_Reg_Read(RTOR);
        unsigned long RTOR_msb = (unsigned long) (SPI_temp_32b[0]);
       // RTOR_msb = RTOR_msb <<8;
        unsigned char RTOR_lsb = (unsigned char) (SPI_temp_32b[1]);
    
        unsigned long rtor = (RTOR_msb<<8 | RTOR_lsb);
        rtor = ((rtor >>2) & 0x3fff) ;
      
        float hr =  60 /((float)rtor*0.008); 
        unsigned int HR = (unsigned int)hr;  // type cast to int
    
        unsigned int RR = (unsigned int)rtor*8 ;  //8ms
    
         Serial.print(ecgdata);
         Serial.print("\n");
    //     Serial.print(RTOR_msb);
    //     Serial.print(",");
    //     Serial.print(RTOR_lsb);
    //     Serial.print(",");
    //     Serial.print(rtor); 
    //     Serial.print(",");
    //     Serial.print(RR);
    //     Serial.print(",");
    //     Serial.println(HR);      
    
          DataPacketHeader[0] = 0x0A;
          DataPacketHeader[1] = 0xFA;
          DataPacketHeader[2] = 0x0C;
          DataPacketHeader[3] = 0;
          DataPacketHeader[4] = 0x02;
       
    //      DataPacketHeader[5] = ecgdata;
    //      DataPacketHeader[6] = ecgdata>>8;
    //      DataPacketHeader[7] = ecgdata>>16;
    //      DataPacketHeader[8] = ecgdata>>24; 
          DataPacketHeader[5] = data0;
          DataPacketHeader[6] = data1;
          DataPacketHeader[7] = data2;
          DataPacketHeader[8] = data;  
       
          DataPacketHeader[9] =  RR ;
          DataPacketHeader[10] = RR >>8;
          DataPacketHeader[11] = 0x00;
          DataPacketHeader[12] = 0x00; 
    
          DataPacketHeader[13] = HR ;
          DataPacketHeader[14] = HR >>8;
          DataPacketHeader[15] = 0x00;
          DataPacketHeader[16] = 0x00; 
            
          DataPacketHeader[17] = 0x00;
          DataPacketHeader[18] = 0x0b;
    //  
    //      for(i=0; i<19; i++) // transmit the data
    //      {
    //        Serial.write(DataPacketHeader[i]);  
    //       }    
    
        delay(8);      
    }
    
    void MAX30003_Reg_Write (unsigned char WRITE_ADDRESS, unsigned long data)
    {
     
        // now combine the register address and the command into one byte:
         byte dataToSend = (WRITE_ADDRESS<<1) | WREG;
      
         // take the chip select low to select the device:
         digitalWrite(MAX30003_CS_PIN, LOW);
         
         delay(2);
         SPI.transfer(dataToSend);   //Send register location
         SPI.transfer(data>>16);     //number of register to wr
         SPI.transfer(data>>8);      //number of register to wr
         SPI.transfer(data);      //Send value to record into register
         delay(2);
         
         // take the chip select high to de-select:
         digitalWrite(MAX30003_CS_PIN, HIGH);
    }
    
    void max30003_sw_reset(void)
    {
        MAX30003_Reg_Write(SW_RST,0x000000);     
        delay(100);
    }
    
    void max30003_synch(void)
    {
        MAX30003_Reg_Write(SYNCH,0x000000);
    }
    
    void MAX30003_Reg_Read(uint8_t Reg_address)
    {
        uint8_t SPI_TX_Buff;
       
        digitalWrite(MAX30003_CS_PIN, LOW);
        
        SPI_TX_Buff = (Reg_address<<1 ) | RREG;
        SPI.transfer(SPI_TX_Buff); //Send register location
         
        for ( i = 0; i < 3; i++)
        {
           SPI_temp_32b[i] = SPI.transfer(0xff);
        }
      
        digitalWrite(MAX30003_CS_PIN, HIGH);
    }
    
    void MAX30003_Read_Data(int num_samples)
    {
        uint8_t SPI_TX_Buff;
      
        digitalWrite(MAX30003_CS_PIN, LOW);   
      
        SPI_TX_Buff = (ECG_FIFO_BURST<<1 ) | RREG;
        SPI.transfer(SPI_TX_Buff); //Send register location
      
        for ( i = 0; i < num_samples*3; ++i)
        {
          SPI_temp_Burst[i] = SPI.transfer(0x00);
        }
        
        digitalWrite(MAX30003_CS_PIN, HIGH);  
    }
    
    void MAX30003_begin()
    {    
        max30003_sw_reset();
        delay(100);
        MAX30003_Reg_Write(CNFG_GEN, 0x081007);
        delay(100);
        MAX30003_Reg_Write(CNFG_CAL, 0x720000);  // 0x700000  
        delay(100);
        MAX30003_Reg_Write(CNFG_EMUX,0x0B0000);
        delay(100);
        MAX30003_Reg_Write(CNFG_ECG, 0x805000);  // d23 - d22 : 10 for 250sps , 00:500 sps
        delay(100);
        MAX30003_Reg_Write(CNFG_RTOR1,0x3fc600);   
        delay(100);  
        max30003_synch();
        delay(100);
        
    }

    I tried to probe the SPI lines through logic analyzer when using the Arduino, but the timing sequence was strange.But when I used NRF,the timing sequence was corresponding to the datasheet. So..I can't obtain the difference.

Children
No Data
Related