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

nRF52840 - ADS1292

Hi,

I want to read register 0x00 (chip ID) of ads1292. i do following...

=============================================================================================================================

///// init spi /////

spi_config.ss_pin = ARDUINO_7_PIN;
spi_config.miso_pin = ARDUINO_12_PIN;
spi_config.mosi_pin = ARDUINO_11_PIN;
spi_config.sck_pin = ARDUINO_13_PIN;
spi_config.bit_order = NRF_DRV_SPI_BIT_ORDER_MSB_FIRST;
spi_config.mode = NRF_DRV_SPI_MODE_0;
spi_config.frequency = NRF_DRV_SPI_FREQ_4M;
spi_config.orc = 0x00;
APP_ERROR_CHECK(nrf_drv_spi_init(&spi, &spi_config, spi_event_handler, NULL));
NRF_LOG_INFO("SPI example started.");NRF_LOG_FLUSH();

///// reset the board by pulling low hardware pin /////

nrf_gpio_pin_write(ADS1292_PWDN_PIN, action);

delay(100);

nrf_gpio_pin_write(ADS1292_PWDN_PIN, action);

delay(100);

nrf_gpio_pin_write(ADS1292_PWDN_PIN, action);

delay(100);

/////// stop continues data receive mode ///////

spi_xfer_done=false;memset(m_rx_buf,0,sizeof(m_rx_buf));
spiDataTx[0]=0x11;
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, spiDataTx, 1, m_rx_buf, sizeof(m_rx_buf)));
while (!spi_xfer_done){__WFE();}
NRF_LOG_FLUSH();

//////// read chip id register (0x00) ///////////

char tempData[3];
tempData[0]=0x20;
tempData[1]=0x00;
tempData[2]=0x00;
spi_xfer_done=false;memset(m_rx_buf,0,sizeof(m_rx_buf));
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, tempData, 3, m_rx_buf, sizeof(m_rx_buf)));
while (!spi_xfer_done){__WFE();}
NRF_LOG_FLUSH();

=============================================================================================================================

but i always get garbage value. It should come as 0x73 but every time receiving garbage values.

i did exactly same as described in datasheet of ads1292.

Did i anything wrong as far as nRF52 board concern ? Waiting for better solution.

Thanks in advance.

  • I suggest you get a scope of the SPI communication and ask TI if whether it's correct or not. As long as the SPI peripheral itself is behaving as it should then you need to go to TI for ads1292 support. Either way you need a scope of the SPI communication. 

  • 4MHz and Mode 0 will create difficulties for the ADS1292. Try the following:

    spi_config.frequency = NRF_DRV_SPI_FREQ_500K;  // Ensure less than 4 clk cycles for ADS1292
    spi_config.bit_order = NRF_DRV_SPI_BIT_ORDER_MSB_FIRST;
    spi_config.mode      = NRF_DRV_SPI_MODE_1;
    APP_ERROR_CHECK(nrf_drv_spi_init(&mAfeSpiInstance, &spi_config, afe_spi_event_handler, NULL));
    

    Reading the version code is easiest using a single transfer:

    // Scratch pad comms buffers, used for reading all AFE registers - want to preserve these for debugging
    #define AFE_HARDWARE_VERSION 0x53
    
    static uint8_t mTxBuf[] = {ADS_CMND_RREG, NUMBER_OF_ADS_REGISTERS-1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
    static uint8_t mRxBuf[sizeof(mTxBuf)];
    static const uint8_t mRxTxBufLength = sizeof(mTxBuf);
    
    /**
     * @brief SPI command to read AFE registers
     * @param event
     */
    static void ReadAfeRegisters(void)
    {
        uint32_t spiAttempCount, mAfeSpiErrorCount = 0;
    
        for (spiAttempCount=0; spiAttempCount < AFE_MAX_ATTEMPTS; spiAttempCount++)
        {
            memset(mRxBuf, '?', mRxTxBufLength);
            mAfePacketTransferComplete = false;
            APP_ERROR_CHECK(nrf_drv_spi_transfer(&mAfeSpiInstance, mTxBuf, mRxTxBufLength, mRxBuf, mRxTxBufLength));
            // Hard delay per byte to transmit
            nrf_delay_us(mRxTxBufLength*AFE_SPI_PER_BYTE_uSECS);
            // Check for ADS1292 Id
            if (mAfePacketTransferComplete && (mRxBuf[2] == AFE_HARDWARE_VERSION))
            {
                // Correct Id for this AFE
                break;
            }
            else
            {
                mAfeSpiErrorCount++;
                // Extra hard delay per byte to transmit
                nrf_delay_us(mRxTxBufLength*AFE_SPI_PER_BYTE_uSECS);
            }
        }
        // Post the results (may be bad)
        ...
    }
    
    /**
     * @brief SPI event handler indicating SPI transfer has completed
     * @param event
     */
    static void afe_spi_event_handler(nrf_drv_spi_evt_t const * p_event, void *p_context)
    {
        mAfePacketTransferComplete = true;
    }
    
    

    Read the data sheet to see why the slow transfer speed is being used in my example; it is related to the number of clock cycles required by the part to handle SPI commands before the next SPI info; faster speeds are ok provided the code manages that restriction - I choose to take the simplest solution :-)

  • Read the data sheet

    Always good advice!

    -  How to properly post source code:

  • #include "nrf_drv_spi.h"
    #include "app_util_platform.h"
    #include "nrf_gpio.h"
    #include "nrf_delay.h"
    #include "boards.h"
    #include "app_error.h"
    #include <string.h>
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    
    #include "ads1292.h"
    
    #define DEBUG 1
    
    static const nrf_drv_spi_t spi = NRF_DRV_SPI_INSTANCE(SPI_INSTANCE);  /**< SPI instance. */
    char spiDataTx[1];
    static uint8_t       m_rx_buf[50];    /**< RX buffer. */
    static volatile bool spi_xfer_done;  /**< Flag used to indicate that SPI instance completed the transfer. */
    
    void pinMode(long pinNum, long mode)
    {
      if(mode == OUTPUT)
        nrf_gpio_cfg_output(pinNum);
      else if(mode == INPUT)
        nrf_gpio_cfg_input(pinNum, BUTTON_PULL);
    }
    
    void digitalWrite(long pinNum, long action)
    {
      nrf_gpio_pin_write(pinNum, action);
    }
    
    long digitalRead(long pinNum)
    {
      return nrf_gpio_pin_read(pinNum);
    }
    
    void delay(int amount)
    {
      nrf_delay_ms(amount);
    }
    
    void spi_event_handler(nrf_drv_spi_evt_t const * p_event,
                           void *                    p_context)
    {
        spi_xfer_done = true;
        NRF_LOG_INFO("Transfer completed.");//NRF_LOG_FLUSH();
        if (m_rx_buf[0] != 0)
        {
            NRF_LOG_INFO(" Received:");
            NRF_LOG_HEXDUMP_INFO(m_rx_buf, strlen((const char *)m_rx_buf));
        }
    }
    
    char* ads1292_Read_Data()
    {
        static char SPI_Dummy_Buff[10];
        digitalWrite(ADS1292_CS_PIN, LOW);
        for (int i = 0; i < 9; ++i)
        {
                spiDataTx[0]=CONFIG_SPI_MASTER_DUMMY;
                spi_xfer_done=false;memset(m_rx_buf,0,sizeof(m_rx_buf));
                APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, spiDataTx, 1, m_rx_buf, sizeof(m_rx_buf)));
                while (!spi_xfer_done){__WFE();}
                NRF_LOG_FLUSH();
        }
        digitalWrite(ADS1292_CS_PIN, HIGH);
        return SPI_Dummy_Buff;
    }
    
    void ads1292_Init()
    {
      // initalize the  data ready and chip select pins:
      pinMode(ADS1292_DRDY_PIN, INPUT);  //6
      pinMode(ADS1292_CS_PIN, OUTPUT);    //7
      pinMode(ADS1292_START_PIN, OUTPUT);  //5
      pinMode(ADS1292_PWDN_PIN, OUTPUT);  //4
    
      nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;
      //spi_config.ss_pin   = ADS1292_CS_PIN;
      spi_config.miso_pin = ARDUINO_12_PIN;
      spi_config.mosi_pin = ARDUINO_11_PIN;
      spi_config.sck_pin  = ARDUINO_13_PIN;
      spi_config.bit_order = NRF_DRV_SPI_BIT_ORDER_MSB_FIRST;
      spi_config.mode = NRF_DRV_SPI_MODE_0;
      spi_config.frequency = NRF_DRV_SPI_FREQ_2M;
      spi_config.orc = 0x00;
      APP_ERROR_CHECK(nrf_drv_spi_init(&spi, &spi_config, spi_event_handler, NULL));
      NRF_LOG_INFO("SPI example started.");NRF_LOG_FLUSH();
    
      if(DEBUG){NRF_LOG_INFO("0.0");NRF_LOG_FLUSH();}
      ads1292_Reset();
      delay(100);
      if(DEBUG){NRF_LOG_INFO("0.1");NRF_LOG_FLUSH();}
      ads1292_Disable_Start();
      if(DEBUG){NRF_LOG_INFO("0.2");NRF_LOG_FLUSH();}
      ads1292_Enable_Start();
      
      if(DEBUG){NRF_LOG_INFO("0.3");NRF_LOG_FLUSH();}
      ads1292_Hard_Stop();
      if(DEBUG){NRF_LOG_INFO("0.4");NRF_LOG_FLUSH();}
      ads1292_Start_Data_Conv_Command();
      if(DEBUG){NRF_LOG_INFO("0.5");NRF_LOG_FLUSH();}
      ads1292_Soft_Stop();
      if(DEBUG){NRF_LOG_INFO("0.6");NRF_LOG_FLUSH();}
      delay(50);
      if(DEBUG){NRF_LOG_INFO("0.7");NRF_LOG_FLUSH();}
      ads1292_Stop_Read_Data_Continuous();					// SDATAC command
      delay(300);
      if(DEBUG){NRF_LOG_INFO("0.8");NRF_LOG_FLUSH();}
    
      /// to read chip id register 0x00 ///
      ChipID();
      
    
      /*if(DEBUG){NRF_LOG_INFO("0.9");NRF_LOG_FLUSH();}
      ads1292_Reg_Write(ADS1292_REG_CONFIG1, 0x00); 		//Set sampling rate to 125 SPS
      delay(10);
      if(DEBUG){NRF_LOG_INFO("0.10");NRF_LOG_FLUSH();}
      ads1292_Reg_Write(ADS1292_REG_CONFIG2, 0b10100000);	//Lead-off comp off, test signal disabled
      delay(10);
      if(DEBUG){NRF_LOG_INFO("0.11");NRF_LOG_FLUSH();}
      ads1292_Reg_Write(ADS1292_REG_LOFF, 0b00010000);		//Lead-off defaults
      delay(10);
      if(DEBUG){NRF_LOG_INFO("0.12");NRF_LOG_FLUSH();}
      ads1292_Reg_Write(ADS1292_REG_CH1SET, 0b01000000);	//Ch 1 enabled, gain 6, connected to electrode in
      delay(10);
      if(DEBUG){NRF_LOG_INFO("0.13");NRF_LOG_FLUSH();}
      ads1292_Reg_Write(ADS1292_REG_CH2SET, 0b01100000);	//Ch 2 enabled, gain 6, connected to electrode in
      delay(10);
      if(DEBUG){NRF_LOG_INFO("0.14");NRF_LOG_FLUSH();}
      ads1292_Reg_Write(ADS1292_REG_RLDSENS, 0b00101100);	//RLD settings: fmod/16, RLD enabled, RLD inputs from Ch2 only
      delay(10);
      if(DEBUG){NRF_LOG_INFO("0.15");NRF_LOG_FLUSH();}
      ads1292_Reg_Write(ADS1292_REG_LOFFSENS, 0x00);		//LOFF settings: all disabled
      delay(10);
    														//Skip register 8, LOFF Settings default
      if(DEBUG){NRF_LOG_INFO("0.16");NRF_LOG_FLUSH();}
      ads1292_Reg_Write(ADS1292_REG_RESP1, 0b11110010);		//Respiration: MOD/DEMOD turned only, phase 0
      delay(10);
      if(DEBUG){NRF_LOG_INFO("0.17");NRF_LOG_FLUSH();}
      ads1292_Reg_Write(ADS1292_REG_RESP2, 0b00000011);		//Respiration: Calib OFF, respiration freq defaults
      delay(10);
      if(DEBUG){NRF_LOG_INFO("0.18");NRF_LOG_FLUSH();}
      ads1292_Start_Read_Data_Continuous();
      delay(10);
      if(DEBUG){NRF_LOG_INFO("0.19");NRF_LOG_FLUSH();}
      ads1292_Enable_Start();*/
    }
    
    void ads1292_Reset()
    {
      digitalWrite(ADS1292_PWDN_PIN, HIGH);
      delay(100);					// Wait 100 mSec
      digitalWrite(ADS1292_PWDN_PIN, LOW);
      delay(100);
      digitalWrite(ADS1292_PWDN_PIN, HIGH);
      delay(100);
    }
    
    void ads1292_Disable_Start()
    {
      digitalWrite(ADS1292_START_PIN, LOW);
      delay(20);
    }
    
    void ads1292_Enable_Start()
    {
      digitalWrite(ADS1292_START_PIN, HIGH);
      delay(20);
    }
    
    void ads1292_Hard_Stop (void)
    {
      digitalWrite(ADS1292_START_PIN, LOW);
      delay(100);
    }
    
    void ads1292_Start_Data_Conv_Command (void)
    {
      ads1292_SPI_Command_Data(START_CONV);					// Send 0x08 to the ADS1x9x
    }
    
    void ads1292_Soft_Stop (void)
    {
      ads1292_SPI_Command_Data(STOP_CONV);                   // Send 0x0A to the ADS1x9x
    }
    
    void ads1292_Start_Read_Data_Continuous (void)
    {
      ads1292_SPI_Command_Data(RDATAC);					// Send 0x10 to the ADS1x9x
    }
    
    void ads1292_Stop_Read_Data_Continuous (void)
    {
      ads1292_SPI_Command_Data(SDATAC);					// Send 0x11 to the ADS1x9x
    }
    
    void ads1292_SPI_Command_Data(unsigned char data_in)
    {
      //byte data[1];
      //data[0] = data_in;
      digitalWrite(ADS1292_CS_PIN, LOW);
      delay(2);
      digitalWrite(ADS1292_CS_PIN, HIGH);
      delay(2);
      digitalWrite(ADS1292_CS_PIN, LOW);
      delay(2);
    
      if(DEBUG){NRF_LOG_INFO("1.0");NRF_LOG_FLUSH();}
      spiDataTx[0]=data_in;
      spi_xfer_done=false;memset(m_rx_buf,0,sizeof(m_rx_buf));
      APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, spiDataTx, 1, m_rx_buf, sizeof(m_rx_buf)));
      while (!spi_xfer_done){__WFE();}
      NRF_LOG_FLUSH();
      
      delay(2);
      digitalWrite(ADS1292_CS_PIN, HIGH);
    }
    
    //Sends a write command to SCP1000
    void ads1292_Reg_Write (unsigned char READ_WRITE_ADDRESS, unsigned char DATA)
    {
      switch (READ_WRITE_ADDRESS)
      {
        case 1:
                DATA = DATA & 0x87;
    	    break;
        case 2:
                DATA = DATA & 0xFB;
    	    DATA |= 0x80;		
    	    break;
        case 3:
    	    DATA = DATA & 0xFD;
    	    DATA |= 0x10;
    	    break;
        case 7:
    	    DATA = DATA & 0x3F;
    	    break;
        case 8:
        	    DATA = DATA & 0x5F;
    	    break;
        case 9:
    	    DATA |= 0x02;
    	    break;
        case 10:
    	    DATA = DATA & 0x87;
    	    DATA |= 0x01;
    	    break;
        case 11:
    	    DATA = DATA & 0x0F;
    	    break;
        default:
    	    break;		
      }
      
      // now combine the register address and the command into one byte:
      char dataToSend[1];
      dataToSend[0] = READ_WRITE_ADDRESS | WREG;
      
      digitalWrite(ADS1292_CS_PIN, LOW);
      delay(2);
      digitalWrite(ADS1292_CS_PIN, HIGH);
      delay(2);
      // take the chip select low to select the device:
      digitalWrite(ADS1292_CS_PIN, LOW);
      delay(2);
    
      if(DEBUG){NRF_LOG_INFO("2.0");NRF_LOG_FLUSH();}
      spi_xfer_done=false;memset(m_rx_buf,0,sizeof(m_rx_buf));
      APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, dataToSend, 1, m_rx_buf, sizeof(m_rx_buf)));
      while (!spi_xfer_done){__WFE();}
      NRF_LOG_FLUSH();
      
      if(DEBUG){NRF_LOG_INFO("2.1");NRF_LOG_FLUSH();}
      spi_xfer_done=false;memset(m_rx_buf,0,sizeof(m_rx_buf));
      APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, 0x00, 1, m_rx_buf, sizeof(m_rx_buf)));
      while (!spi_xfer_done){__WFE();}
      NRF_LOG_FLUSH();
      
      if(DEBUG){NRF_LOG_INFO("2.2");NRF_LOG_FLUSH();}
      spiDataTx[0]=DATA;;
      spi_xfer_done=false;memset(m_rx_buf,0,sizeof(m_rx_buf));
      APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, spiDataTx, 1, m_rx_buf, sizeof(m_rx_buf)));
      while (!spi_xfer_done){__WFE();}
      NRF_LOG_FLUSH();
      
      delay(2);
      // take the chip select high to de-select:
      digitalWrite(ADS1292_CS_PIN, HIGH);
    }
    
    void ChipID(void)
    {
      //byte data[1];
      //data[0] = data_in;
      digitalWrite(ADS1292_CS_PIN, LOW);
      delay(2);
      digitalWrite(ADS1292_CS_PIN, HIGH);
      delay(2);
      digitalWrite(ADS1292_CS_PIN, LOW);
      delay(2);
    
      char tempData[3]={0x20,0x00,0x00};
      if(DEBUG){NRF_LOG_INFO("3.0");NRF_LOG_FLUSH();}
      spi_xfer_done=false;memset(m_rx_buf,0,sizeof(m_rx_buf));
      APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, tempData, sizeof(tempData), m_rx_buf, sizeof(m_rx_buf)));
      while (!spi_xfer_done){__WFE();}
      NRF_LOG_FLUSH();
      
      delay(2);
      digitalWrite(ADS1292_CS_PIN, HIGH);
    }
    #include "nrf_drv_spi.h"
    #include "app_util_platform.h"
    #include "nrf_gpio.h"
    #include "nrf_delay.h"
    #include "boards.h"
    #include "app_error.h"
    #include <string.h>
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    
    #include "ecg.h"
    #include "ads1292.h"
    
    volatile uint8_t  SPI_Dummy_Buff[30];
    uint8_t DataPacketHeader[16];
    volatile signed long s32DaqVals[8];
    uint8_t data_len = 7;
    volatile char SPI_RX_Buff[15] ;
    volatile static int SPI_RX_Buff_Count = 0;
    volatile char *SPI_RX_Buff_Ptr;
    volatile bool ads1292dataReceived = false;
    unsigned long uecgtemp = 0;
    signed long secgtemp = 0;
    int i, j;
    
    //************** ecg *******************
    int16_t CoeffBuf_40Hz_LowPass[FILTERORDER] =
    {
      -72,    122,    -31,    -99,    117,      0,   -121,    105,     34,
      -137,     84,     70,   -146,     55,    104,   -147,     20,    135,
      -137,    -21,    160,   -117,    -64,    177,    -87,   -108,    185,
      -48,   -151,    181,      0,   -188,    164,     54,   -218,    134,
      112,   -238,     90,    171,   -244,     33,    229,   -235,    -36,
      280,   -208,   -115,    322,   -161,   -203,    350,    -92,   -296,
      361,      0,   -391,    348,    117,   -486,    305,    264,   -577,
      225,    445,   -660,     93,    676,   -733,   -119,    991,   -793,
      -480,   1486,   -837,  -1226,   2561,   -865,  -4018,   9438,  20972,
      9438,  -4018,   -865,   2561,  -1226,   -837,   1486,   -480,   -793,
      991,   -119,   -733,    676,     93,   -660,    445,    225,   -577,
      264,    305,   -486,    117,    348,   -391,      0,    361,   -296,
      -92,    350,   -203,   -161,    322,   -115,   -208,    280,    -36,
      -235,    229,     33,   -244,    171,     90,   -238,    112,    134,
      -218,     54,    164,   -188,      0,    181,   -151,    -48,    185,
      -108,    -87,    177,    -64,   -117,    160,    -21,   -137,    135,
      20,   -147,    104,     55,   -146,     70,     84,   -137,     34,
      105,   -121,      0,    117,    -99,    -31,    122,    -72
    };
    int16_t ECG_WorkingBuff[2 * FILTERORDER];
    unsigned char Start_Sample_Count_Flag = 0;
    unsigned char first_peak_detect = FALSE ;
    unsigned int sample_index[MAX_PEAK_TO_SEARCH + 2] ;
    uint16_t scaled_result_display[150];
    uint8_t indx = 0;
    
    int cnt = 0;
    volatile uint8_t flag = 0;
    
    int QRS_Second_Prev_Sample = 0 ;
    int QRS_Prev_Sample = 0 ;
    int QRS_Current_Sample = 0 ;
    int QRS_Next_Sample = 0 ;
    int QRS_Second_Next_Sample = 0 ;
    
    static uint16_t QRS_B4_Buffer_ptr = 0 ;
    /*   Variable which holds the threshold value to calculate the maxima */
    int16_t QRS_Threshold_Old = 0;
    int16_t QRS_Threshold_New = 0;
    unsigned int sample_count = 0 ;
    
    /* Variable which will hold the calculated heart rate */
    volatile uint16_t QRS_Heart_Rate = 0 ;
    int16_t ecg_wave_buff[1], ecg_filterout[1];
    
    volatile uint8_t global_HeartRate = 0;
    volatile uint8_t global_RespirationRate = 0;
    long status_byte=0;
    uint8_t LeadStatus=0;
    bool leadoff_deteted = true;
    long time_elapsed=0;
    
    void ECG_Init()
    {
      // initalize the  data ready and chip select pins:
      /*pinMode(ADS1292_DRDY_PIN, INPUT);  //6
      pinMode(ADS1292_CS_PIN, OUTPUT);    //7
      pinMode(ADS1292_START_PIN, OUTPUT);  //5
      pinMode(ADS1292_PWDN_PIN, OUTPUT);  //4*/
    
      ads1292_Init();
    
      NRF_LOG_INFO("Initiliziation is done");NRF_LOG_FLUSH();
    }
    
    
    void MeasureHeartRate()
    {
      if ((digitalRead(ADS1292_DRDY_PIN)) == LOW)      // Sampling rate is set to 125SPS ,DRDY ticks for every 8ms
      {
        SPI_RX_Buff_Ptr = ads1292_Read_Data(); // Read the data,point the data to a pointer
    
        for (i = 0; i < 9; i++)
        {
          SPI_RX_Buff[SPI_RX_Buff_Count++] = *(SPI_RX_Buff_Ptr + i);  // store the result data in array
        }
        ads1292dataReceived = true;
      }
    
    
      if (ads1292dataReceived == true)      // process the data
      {
        j = 0;
        for (i = 3; i < 9; i += 3)         // data outputs is (24 status bits + 24 bits Respiration data +  24 bits ECG data)
        {
    
          uecgtemp = (unsigned long) (  ((unsigned long)SPI_RX_Buff[i + 0] << 16) | ( (unsigned long) SPI_RX_Buff[i + 1] << 8) |  (unsigned long) SPI_RX_Buff[i + 2]);
          uecgtemp = (unsigned long) (uecgtemp << 8);
          secgtemp = (signed long) (uecgtemp);
          secgtemp = (signed long) (secgtemp >> 8);
    
          s32DaqVals[j++] = secgtemp;  //s32DaqVals[0] is Resp data and s32DaqVals[1] is ECG data
        }
    
        status_byte = (long)((long)SPI_RX_Buff[2] | ((long) SPI_RX_Buff[1]) <<8 | ((long) SPI_RX_Buff[0])<<16); // First 3 bytes represents the status
        status_byte  = (status_byte & 0x0f8000) >> 15;  // bit15 gives the lead status
        LeadStatus = (unsigned char ) status_byte ;  
    
        if(!((LeadStatus & 0x1f) == 0 ))
          leadoff_deteted  = true; 
        else
          leadoff_deteted  = false;
        
        ecg_wave_buff[0] = (int16_t)(s32DaqVals[1] >> 8) ;  // ignore the lower 8 bits out of 24bits
    
        if(leadoff_deteted == false) 
           {
              ECG_ProcessCurrSample(&ecg_wave_buff[0], &ecg_filterout[0]);   // filter out the line noise @40Hz cutoff 161 order
              QRS_Algorithm_Interface(ecg_filterout[0]);             // calculate 
           }
        else
           ecg_filterout[0] = 0;
    
    
        //if(millis() > time_elapsed)  // update every one second
        {
          if(leadoff_deteted == true) // lead in not connected
          {
            NRF_LOG_INFO("ECG lead error!!! ensure the leads are properly connected");NRF_LOG_FLUSH();
          }
          else
          {
            NRF_LOG_INFO("Heart rate: ");//NRF_LOG_FLUSH();
            NRF_LOG_INFO(global_HeartRate);//NRF_LOG_FLUSH();
            NRF_LOG_INFO("BPM");NRF_LOG_FLUSH();
          }
          //time_elapsed += 1000;
        }
        
      }
    
      ads1292dataReceived = false;
      SPI_RX_Buff_Count = 0;
    }
    
    
    void ECG_FilterProcess(int16_t * WorkingBuff, int16_t * CoeffBuf, int16_t* FilterOut)
    {
    
      int32_t acc = 0;   // accumulator for MACs
      int  k;
    
      // perform the multiply-accumulate
      for ( k = 0; k < 161; k++ )
      {
        acc += (int32_t)(*CoeffBuf++) * (int32_t)(*WorkingBuff--);
      }
      // saturate the result
      if ( acc > 0x3fffffff )
      {
        acc = 0x3fffffff;
      } else if ( acc < -0x40000000 )
      {
        acc = -0x40000000;
      }
      // convert from Q30 to Q15
      *FilterOut = (int16_t)(acc >> 15);
      //*FilterOut = *WorkingBuff;
    }
    
    
    
    
    void ECG_ProcessCurrSample(int16_t *CurrAqsSample, int16_t *FilteredOut)
    {
      static uint16_t ECG_bufStart = 0, ECG_bufCur = FILTERORDER - 1, ECGFirstFlag = 1;
      static int16_t ECG_Pvev_DC_Sample, ECG_Pvev_Sample;/* Working Buffer Used for Filtering*/
      //  static short ECG_WorkingBuff[2 * FILTERORDER];
      int16_t *CoeffBuf;
      int16_t temp1, temp2, ECGData;
    
      /* Count variable*/
      uint16_t Cur_Chan;
      int16_t FiltOut = 0;
      //  short FilterOut[2];
      CoeffBuf = CoeffBuf_40Hz_LowPass;         // Default filter option is 40Hz LowPass
    
      if  ( ECGFirstFlag )                // First Time initialize static variables.
      {
        for ( Cur_Chan = 0 ; Cur_Chan < FILTERORDER; Cur_Chan++)
        {
          ECG_WorkingBuff[Cur_Chan] = 0;
        }
        ECG_Pvev_DC_Sample = 0;
        ECG_Pvev_Sample = 0;
        ECGFirstFlag = 0;
      }
    
      temp1 = NRCOEFF * ECG_Pvev_DC_Sample;       //First order IIR
      ECG_Pvev_DC_Sample = (CurrAqsSample[0]  - ECG_Pvev_Sample) + temp1;
      ECG_Pvev_Sample = CurrAqsSample[0];
      temp2 = ECG_Pvev_DC_Sample >> 2;
      ECGData = (int16_t) temp2;
    
      /* Store the DC removed value in Working buffer in millivolts range*/
      ECG_WorkingBuff[ECG_bufCur] = ECGData;
      ECG_FilterProcess(&ECG_WorkingBuff[ECG_bufCur], CoeffBuf, (int16_t*)&FiltOut);
      /* Store the DC removed value in ECG_WorkingBuff buffer in millivolts range*/
      ECG_WorkingBuff[ECG_bufStart] = ECGData;
    
      /* Store the filtered out sample to the LeadInfo buffer*/
      FilteredOut[0] = FiltOut ;//(CurrOut);
    
      ECG_bufCur++;
      ECG_bufStart++;
    
      if ( ECG_bufStart  == (FILTERORDER - 1))
      {
        ECG_bufStart = 0;
        ECG_bufCur = FILTERORDER - 1;
      }
      return ;
    }
    
    
    void QRS_Algorithm_Interface(int16_t CurrSample)
    {
      //  static FILE *fp = fopen("ecgData.txt", "w");
      static int16_t prev_data[32] = {0};
      int16_t i;
      long Mac = 0;
      prev_data[0] = CurrSample;
    
      for ( i = 31; i > 0; i--)
      {
        Mac += prev_data[i];
        prev_data[i] = prev_data[i - 1];
    
      }
      Mac += CurrSample;
      Mac = Mac >> 2;
      CurrSample = (int16_t) Mac;
      QRS_Second_Prev_Sample = QRS_Prev_Sample ;
      QRS_Prev_Sample = QRS_Current_Sample ;
      QRS_Current_Sample = QRS_Next_Sample ;
      QRS_Next_Sample = QRS_Second_Next_Sample ;
      QRS_Second_Next_Sample = CurrSample ;
    
    
    
      QRS_process_buffer();
    }
    
    static void QRS_process_buffer( void )
    {
    
      int16_t first_derivative = 0 ;
      int16_t scaled_result = 0 ;
    
      static int16_t Max = 0 ;
    
      /* calculating first derivative*/
      first_derivative = QRS_Next_Sample - QRS_Prev_Sample  ;
    
    
    
      /*taking the absolute value*/
    
      if (first_derivative < 0)
      {
        first_derivative = -(first_derivative);
      }
    
    
      scaled_result = first_derivative;
    
      if ( scaled_result > Max )
      {
        Max = scaled_result ;
      }
    
    
      QRS_B4_Buffer_ptr++;
      if (QRS_B4_Buffer_ptr ==  TWO_SEC_SAMPLES)
      {
        QRS_Threshold_Old = ((Max * 7) / 10 ) ;
        QRS_Threshold_New = QRS_Threshold_Old ;
       // if (Max > 70)
          first_peak_detect = TRUE ;
        Max = 0;
     
        //  ecg_wave_buff[0] = first_derivative;
        QRS_B4_Buffer_ptr = 0;
    
    
      }
    
    
      if ( TRUE == first_peak_detect )
      {
        QRS_check_sample_crossing_threshold( scaled_result ) ;
      }
    
    
    }
    
    
    static void QRS_check_sample_crossing_threshold( uint16_t scaled_result )
    {
      /* array to hold the sample indexes S1,S2,S3 etc */
    
      static uint16_t s_array_index = 0 ;
      static uint16_t m_array_index = 0 ;
    
      static unsigned char threshold_crossed = FALSE ;
      static uint16_t maxima_search = 0 ;
      static unsigned char peak_detected = FALSE ;
      static uint16_t skip_window = 0 ;
      static long maxima_sum = 0 ;
      static unsigned int peak = 0;
      static unsigned int sample_sum = 0;
      static unsigned int nopeak = 0;
      uint16_t Max = 0 ;
      uint16_t HRAvg;
      uint16_t  RRinterval = 0;
    
      if ( TRUE == threshold_crossed  )
      {
        /*
        Once the sample value crosses the threshold check for the
        maxima value till MAXIMA_SEARCH_WINDOW samples are received
        */
        sample_count ++ ;
        maxima_search ++ ;
    
        if ( scaled_result > peak )
        {
          peak = scaled_result ;
        }
    
        if ( maxima_search >= MAXIMA_SEARCH_WINDOW )
        {
          // Store the maxima values for each peak
          maxima_sum += peak ;
          maxima_search = 0 ;
    
    
          threshold_crossed = FALSE ;
          peak_detected = TRUE ;
        }
    
      }
      else if ( TRUE == peak_detected )
      {
        /*
        Once the sample value goes below the threshold
        skip the samples untill the SKIP WINDOW criteria is meet
        */
        sample_count ++ ;
        skip_window ++ ;
    
        if ( skip_window >= MINIMUM_SKIP_WINDOW )
        {
          skip_window = 0 ;
          peak_detected = FALSE ;
        }
    
        if ( m_array_index == MAX_PEAK_TO_SEARCH )
        {
          sample_sum = sample_sum / (MAX_PEAK_TO_SEARCH - 1);
          HRAvg =  (uint16_t) sample_sum  ;
    
          // Compute HR without checking LeadOffStatus
          QRS_Heart_Rate = (uint16_t) 60 *  SAMPLING_RATE;
          QRS_Heart_Rate =  QRS_Heart_Rate / HRAvg ;
          if (QRS_Heart_Rate > 250)
            QRS_Heart_Rate = 250 ;
     //     Serial.println(QRS_Heart_Rate);
    
          /* Setting the Current HR value in the ECG_Info structure*/
          maxima_sum =  maxima_sum / MAX_PEAK_TO_SEARCH;
          Max = (int16_t) maxima_sum ;
          /*  calculating the new QRS_Threshold based on the maxima obtained in 4 peaks */
          maxima_sum = Max * 7;
          maxima_sum = maxima_sum / 10;
          QRS_Threshold_New = (int16_t)maxima_sum;
    
          /* Limiting the QRS Threshold to be in the permissible range*/
          if (QRS_Threshold_New > (4 * QRS_Threshold_Old))
          {
            QRS_Threshold_New = QRS_Threshold_Old;
          }
    
          sample_count = 0 ;
          s_array_index = 0 ;
          m_array_index = 0 ;
          maxima_sum = 0 ;
          sample_index[0] = 0 ;
          sample_index[1] = 0 ;
          sample_index[2] = 0 ;
          sample_index[3] = 0 ;
          Start_Sample_Count_Flag = 0;
    
          sample_sum = 0;
        }
      }
      else if ( scaled_result > QRS_Threshold_New )
      {
        /*
          If the sample value crosses the threshold then store the sample index
        */
        Start_Sample_Count_Flag = 1;
        sample_count ++ ;
        m_array_index++;
        threshold_crossed = TRUE ;
        peak = scaled_result ;
        nopeak = 0;
    
        /*  storing sample index*/
        sample_index[ s_array_index ] = sample_count ;
        if ( s_array_index >= 1 )
        {
          sample_sum += sample_index[ s_array_index ] - sample_index[ s_array_index - 1 ] ;
        }
        s_array_index ++ ;
      }
    
      else if (( scaled_result < QRS_Threshold_New ) && (Start_Sample_Count_Flag == 1))
      {
        sample_count ++ ;
        nopeak++;
        if (nopeak > (3 * SAMPLING_RATE))
        {
          sample_count = 0 ;
          s_array_index = 0 ;
          m_array_index = 0 ;
          maxima_sum = 0 ;
          sample_index[0] = 0 ;
          sample_index[1] = 0 ;
          sample_index[2] = 0 ;
          sample_index[3] = 0 ;
          Start_Sample_Count_Flag = 0;
          peak_detected = FALSE ;
          sample_sum = 0;
    
          first_peak_detect = FALSE;
          nopeak = 0;
    
          QRS_Heart_Rate = 0;
    
        }
      }
      else
      {
        nopeak++;
        if (nopeak > (3 * SAMPLING_RATE))
        {
          /* Reset heart rate computation sate variable in case of no peak found in 3 seconds */
          sample_count = 0 ;
          s_array_index = 0 ;
          m_array_index = 0 ;
          maxima_sum = 0 ;
          sample_index[0] = 0 ;
          sample_index[1] = 0 ;
          sample_index[2] = 0 ;
          sample_index[3] = 0 ;
          Start_Sample_Count_Flag = 0;
          peak_detected = FALSE ;
          sample_sum = 0;
          first_peak_detect = FALSE;
          nopeak = 0;
          QRS_Heart_Rate = 0;
        }
      }
    
      global_HeartRate = (uint8_t)QRS_Heart_Rate;
      //Serial.println(global_HeartRate);
    
    }

    i used above two files.

    i used ECG_Init() function. Correct me if i am doing wrong.

    Please also correct me if i am performing wrong sequence.

    I am getting below output.

    <info> app: SPI example started.
    <info> app: 0.0
    <info> app: 0.1
    <info> app: 0.2
    <info> app: 0.3
    <info> app: 0.4
    <info> app: 1.0
    <info> app: Transfer completed.
    <info> app:  Received:
    <info> app:  60                     |`       
    <info> app: 0.5
    <info> app: 1.0
    <info> app: Transfer completed.
    <info> app:  Received:
    <info> app:  60                     |`       
    <info> app: 0.6
    <info> app: 0.7
    <info> app: 1.0
    <info> app: Transfer completed.
    <info> app:  Received:
    <info> app:  60                     |`       
    <info> app: 0.8
    <info> app: 3.0
    <info> app: Transfer completed.
    <info> app: 0.9
    <info> app: 2.0
    <info> app: Transfer completed.
    <info> app: 2.1
    <info> app: Transfer completed.
    <info> app:  Received:
    <info> app:  3F FF FF E0            |?...    
    <info> app: 2.2
    <info> app: Transfer completed.
    <info> app: 0.10
    <info> app: 2.0
    <info> app: Transfer completed.
    <info> app:  Received:
    <info> app:  FF FF E0               |...     
    <info> app: 2.1
    <info> app: Transfer completed.
    <info> app:  Received:
    <info> app:  40                     |@       
    <info> app: 2.2
    <info> app: Transfer completed.
    <info> app:  Received:
    <info> app:  FF E0                  |..      
    <info> app: 0.11
    <info> app: 2.0
    <info> app: Transfer completed.
    <info> app: 2.1
    <info> app: Transfer completed.
    <info> app:  Received:
    <info> app:  E0                     |.       
    <info> app: 2.2
    <info> app: Transfer completed.
    <info> app: 0.12
    <info> app: 2.0
    <info> app: Transfer completed.
    <info> app: 2.1
    <info> app: Transfer completed.
    <info> app:  Received:
    <info> app:  3F FF FF E0            |?...    
    <info> app: 2.2
    <info> app: Transfer completed.
    <info> app: 0.13
    <info> app: 2.0
    <info> app: Transfer completed.
    <info> app:  Received:
    <info> app:  FF FF E0               |...     
    <info> app: 2.1
    <info> app: Transfer completed.
    <info> app:  Received:
    <info> app:  40                     |@       
    <info> app: 2.2
    <info> app: Transfer completed.
    <info> app:  Received:
    <info> app:  FF E0                  |..      
    <info> app: 0.14
    <info> app: 2.0
    <info> app: Transfer completed.
    <info> app: 2.1
    <info> app: Transfer completed.
    <info> app:  Received:
    <info> app:  E0                     |.       
    <info> app: 2.2
    <info> app: Transfer completed.
    <info> app: 0.15
    <info> app: 2.0
    <info> app: Transfer completed.
    <info> app: 2.1
    <info> app: Transfer completed.
    <info> app:  Received:
    <info> app:  3F FF FF E0            |?...    
    <info> app: 2.2
    <info> app: Transfer completed.
    <info> app: 0.16
    <info> app: 2.0
    <info> app: Transfer completed.
    <info> app:  Received:
    <info> app:  FF FF E0               |...     
    <info> app: 2.1
    <info> app: Transfer completed.
    <info> app:  Received:
    <info> app:  40                     |@       
    <info> app: 2.2
    <info> app: Transfer completed.
    <info> app:  Received:
    <info> app:  FF E0                  |..      
    <info> app: 0.17
    <info> app: 2.0
    <info> app: Transfer completed.
    <info> app: 2.1
    <info> app: Transfer completed.
    <info> app:  Received:
    <info> app:  E0                     |.       
    <info> app: 2.2
    <info> app: Transfer completed.
    <info> app: 0.18
    <info> app: 1.0
    <info> app: Transfer completed.
    <info> app: 0.19
    <info> app: Initiliziation is done
    

    Thanks

  • I will look later today at the sequence, but for now note the following:

    APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, spiDataTx, 1, m_rx_buf, sizeof(m_rx_buf)));
    
    

    SPI transfers as noted elsewhere on this forum clock out as many bits as they clock in, it is a synchronous protocol. The size of the Tx transfer in the code above is only 1 byte but the rx is 50 bytes so you should verify the number of bytes actually being transmitted so that all receive bytes arrive.

    Have you tried Mode 1 and 500kHz clock as I suggested? The code above still shows Mode 0 and 2MHz.

    spi_config.mode = NRF_DRV_SPI_MODE_0;
    spi_config.frequency = NRF_DRV_SPI_FREQ_2M;
    
    
    // Chip Select (CS)
    // CS selects the ADS1291, ADS1292, and ADS1292R for SPI communication. CS must remain low for the entire
    // duration of the serial communication. After the serial communication is finished, always wait four or more tCLK
    // cycles before taking CS high. When CS is taken high, the serial interface is reset, SCLK and DIN are ignored,
    // and DOUT enters a high-impedance state. DRDY asserts when data conversion is complete, regardless of
    // whether CS is high or low
    
    // Sending Multi-Byte Commands
    // The ADS1291, ADS1292, and ADS1292R serial interface decodes commands in bytes and requires 4 tCLK cycles
    // to decode and execute. Therefore, when sending multi-byte commands, a 4 tCLK period must separate the end of
    // one byte (or opcode) and the next.
    // Assume CLK is 512 kHz, then tSDECODE (4 tCLK) is 7.8125 us. When SCLK is 16 MHz, one byte can be
    // transferred in 500 ns. This byte-transfer time does not meet the tSDECODE specification; therefore, a delay must be
    // inserted so the end of the second byte arrives 7.3125 us later. If SCLK is 1 MHz, one byte is transferred in 8 �s.
    // Because this transfer time exceeds the tSDECODE specification, the processor can send subsequent bytes without
    // delay. In this later scenario, the serial port can be programmed to move from single-byte transfer per cycle to
    // multiple bytes.
    

    CS low-High-low prefix is not required.

Related