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*******************************************/

  • Hi

    Can you send me the max30003 library that you use on the nRF52? 

    Then I should be able to compare it to the Arduino code. 

    It would also be interesting to see the plots from the logic analyzer, to verify that the data is sent in the same way (same polarity, bit order, frequency etc). 

    Best regards
    Torbjørn

  • #include "max30003.h"
    #include "nrf_drv_spi.h"
    #include "nrf_delay.h"
    #include "app_error.h"
    #include "app_util_platform.h"
    
    #define SPI_BUFSIZE  10
    
    uint8_t   SPI_Tx_Buf[SPI_BUFSIZE];
    uint8_t   SPI_Rx_Buf[SPI_BUFSIZE];
    volatile  uint8_t   SPIReadLength, SPIWriteLength;
    
    
    static const nrf_drv_spi_t spi = NRF_DRV_SPI_INSTANCE(0);
    static volatile bool spi_xfer_done;
    
    void spi_event_handler(nrf_drv_spi_evt_t const * p_event)
    {
        spi_xfer_done = true;
    }
    
    void max30003_spi_init(void) {
    	nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG(0);
      spi_config.bit_order = NRF_DRV_SPI_BIT_ORDER_MSB_FIRST;
      spi_config.frequency = NRF_DRV_SPI_FREQ_125K;
      spi_config.irq_priority = APP_IRQ_PRIORITY_HIGHEST;
      spi_config.mode = NRF_DRV_SPI_MODE_0;
      spi_config.miso_pin = MAX30003_PIN_SDO;
      spi_config.sck_pin = MAX30003_PIN_SCLK;
      spi_config.mosi_pin = MAX30003_PIN_SDI;
      spi_config.ss_pin = MAX30003_PIN_CSB;
    //  spi_config.orc = 0x55;
      APP_ERROR_CHECK(nrf_drv_spi_init(&spi, &spi_config, spi_event_handler));
    	nrf_delay_ms(200);
    }
    
    void max30003_spi_uninit(void) {
      nrf_drv_spi_uninit(&spi);
    }
    
    void max30003_read_register(uint8_t reg_addr, uint8_t *reg_array) {
      uint8_t tx_data[1] = {MAX30003_READ_ADDR(reg_addr)};
      uint8_t rx_data[4];
      spi_xfer_done = false;
      APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, tx_data, sizeof(tx_data), rx_data, sizeof(rx_data)));
      while (!spi_xfer_done) {
        __WFE();
      }
      // Copy results to array: TEMP
    	for(uint8_t i=0;i<3;i++)
    	{ 
    		reg_array[i]=rx_data[i+1];
    	}
    	
    }
    
    void max30003_write_register(uint8_t reg_addr, uint8_t b0, uint8_t b1, uint8_t b2) {
      uint8_t tx_data[4];
      tx_data[0] = MAX30003_WRITE_ADDR(reg_addr);
      tx_data[1] = b0;
      tx_data[2] = b1;
      tx_data[3] = b2;
    
      // SPI Command
      spi_xfer_done = false;
      APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, tx_data, sizeof(tx_data), NULL, NULL));
      while (!spi_xfer_done) {
        __WFE();
      }
      nrf_delay_ms(5);
    }
    
    void max30003_read_device_info(void) {
      uint8_t dummy_array[3];
      max30003_read_register(MAX30003_REGADDR_INFO, dummy_array);
    }
    
    void max30003_read_device_status(void) {
      uint8_t dummy_array[3];
      max30003_read_register(MAX30003_REGADDR_STAT, dummy_array);
    }
    
    // Control functions>
    void max30003_init(void) {
    	max30003_soft_reset();
    	
    //  // Interrupts and Dynamic Modes
    //  max30003_write_register(MAX30003_REGADDR_EN_INT, 0x00, 0x00, 0x03);   //0x02
    //  max30003_write_register(MAX30003_REGADDR_EN_INT2, 0x00, 0x00, 0x03);  //0x03
    //  max30003_write_register(MAX30003_REGADDR_MNGR_INT, 0x78, 0x00, 0x04); //0x04
    //  max30003_write_register(MAX30003_REGADDR_MNGR_DYN, 0x3f, 0x00, 0x00); //0x05
    
      // Set up ECG Configurations:
      max30003_write_register(MAX30003_REGADDR_CNFG_GEN, 0x08, 0x10, 0x07);   //0x10
      max30003_write_register(MAX30003_REGADDR_CNFG_CAL, 0x70, 0x00, 0x00);   //0x12
      max30003_write_register(MAX30003_REGADDR_CNFG_EMUX, 0x0B, 0x00, 0x00);  //0x14
      max30003_write_register(MAX30003_REGADDR_CNFG_ECG, 0x80, 0x50, 0x00);   //0x15
      max30003_write_register(MAX30003_REGADDR_CNFG_RTOR1, 0x3f, 0xC6, 0x00); //0x1D
    	
    	max30003_sync();
    }
    
    void max30003_readback_registers(void) {
      uint8_t dummy_array[3];
      max30003_read_register(MAX30003_REGADDR_EN_INT, dummy_array);
      max30003_read_register(MAX30003_REGADDR_EN_INT2, dummy_array);
      max30003_read_register(MAX30003_REGADDR_MNGR_INT, dummy_array);
    
      max30003_read_register(MAX30003_REGADDR_CNFG_GEN, dummy_array);
      max30003_read_register(MAX30003_REGADDR_CNFG_CAL, dummy_array);
      max30003_read_register(MAX30003_REGADDR_CNFG_EMUX, dummy_array);
      max30003_read_register(MAX30003_REGADDR_CNFG_ECG, dummy_array);
      max30003_read_register(MAX30003_REGADDR_CNFG_RTOR1, dummy_array);
    }
    
    void max30003_soft_reset(void) {
      max30003_write_register(MAX30003_REGADDR_SW_RST, 0x00, 0x00, 0x00);
      nrf_delay_ms(5); // Give time to start up.
    }
    
    void max30003_sync(void) {
      max30003_write_register(MAX30003_REGADDR_SYNCH, 0x00, 0x00, 0x00);
    }
    
    void max30003_read_fifo_data(uint8_t *reg_array) {
      uint8_t tx_data[1] = {MAX30003_READ_ADDR(MAX30003_REGADDR_ECG_FIFO_BURST)};
      uint8_t rx_data[51+1];
      spi_xfer_done = false;
      APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, tx_data, sizeof(tx_data), rx_data, sizeof(rx_data)));
      while (!spi_xfer_done) {
        __WFE();
      }
    		for(uint8_t i=0;i<51;i++)
    	{ 
    		reg_array[i]=rx_data[i+1];
    	}
    
    }
    

    Hi,thanks for your reply.

    Here is the max30003 code. And the main code I have given in the question above.

    I can't see right plots from the logic analyzer when I use Arduino,but it outputs the right data.So I can't compare their timing sequence.

    Best regards.

  • Hi 

    Thanks for sharing your code. 

    Currently I am on travel, so I haven't been able to look into it unfortunately. I will be back in the office tomorrow and do my best to get you a reply by the end of the day. 

    Best regards
    Torbjørn

  • Hi Victoriaz,

    I see that you have added MAX30003_READ_ADDR() function in the above codes. Where is it defined? Can you post that too?

  • The interpretation of the 24-bit signed numbers here is incorrect:

      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);

    You might find this a better approach

    // Number of bits in ECG 30003 defines the signed ADC output number format
    #define ADC_NUMBER_OF_BITS   (24u)
    #define ADC_SIGN_MASK        (1u << (ADC_NUMBER_OF_BITS-1))
    #define ADC_NUMBER_MASK      (ADC_SIGN_MASK-1u)
    #define ADC_FIELD_MASK       (ADC_SIGN_MASK | ADC_NUMBER_MASK)
    
    // Sanity check on these 24-bit masks
    STATIC_ASSERT (ADC_FIELD_MASK  == 0xFFFFFF, "ADC_FIELD_MASK  == 0xFFFFFF fails check");
    STATIC_ASSERT (ADC_SIGN_MASK   == 0x800000, "ADC_SIGN_MASK   == 0x800000 fails check");
    STATIC_ASSERT (ADC_NUMBER_MASK == 0x7FFFFF, "ADC_NUMBER_MASK == 0x7FFFFF fails check");
    // Sanity check on this CPU implementation for 4-byte 32-bit signed number
    STATIC_ASSERT (sizeof (int32_t) == 4, "(sizeof (int32_t) == 4) failed");
    
    // Define position in response packet, note high byte is first!
    #define MSB_INDEX  3  // <== choose these in ascending order to match your registers
    #define MID_INDEX  4
    #define LSB_INDEX  5
    
    // Signed 32-bit ECG result
    int32_t value_s32;
    
      max30003_read_register(MAX30003_REGADDR_ECG_FIFO,SPI_buffer);
    
      // Convert to signed 32-bit, sign-extension is not automatic
      value_s32 = (SPI_buffer[MSB_INDEX] << 16) | (SPI_buffer[MID_INDEX] << 8) | (SpiBuffer[LSB_INDEX]);
      // Handle a negative (2's complement) 24-bit value
      if ( value_s32 & ADC_SIGN_MASK )
      {
         // Sign extend the -ve 24-bit value to a -ve 32-bit value
         value_s32 |= ~(int32_t)ADC_NUMBER_MASK;
      }
    
      ecgdata = (signed long) value_s32;

    Note the byte index values are just examples here; SPI_buffer[40] is used for multiple bytes read from 30003

Related