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

Problem with SPI communication read/write for MS5540C.

Hello to all,

I am using nRF52840 DK with SDK15.0 and SES. To communicate with MS5540C I have used SPI master sample example but getting some incorrect results.

Almost 4 days I have spend to analyse this issue but still get same problem. I want to send the following commands to sensor:

Conversion start for pressure measurement (D1): 0Fh & 40h
Conversion start for temperature measurement (D2): 0Fh & 20h
Read calibration word 1 (W1): 1Dh & 50h
Read calibration word 2 (W2): 1Dh & 60h
Read calibration word 3 (W3): 1Dh & 90h
Read calibration word 4 (W4): 1Dh & A0h
Reset sequence command: 15h & 55h & 40h 

I am able to read word1-4 but not confirm is this correct result or Not and problem getting for read D1 and D2 continuously getting FF FF value. 

Will you please review my below program snippet and is it correct?

static volatile bool spi_xfer_done;  /**< Flag used to indicate that SPI instance completed the transfer. */
static volatile bool start_measurement = false;
/* Configure MS5540C Sensor Register frame & Sequence */
static uint8_t       reset_seq[] = { 0x15, 0x55, 0x40 };     /**< TX buffer. */
static uint8_t       address_word1[] = { 0x1D, 0x50, 0x00, 0x00 };       /**< TX buffer. */
static uint8_t       address_word2[] = { 0x1D, 0x60, 0x00, 0x00 };       /**< TX buffer. */
static uint8_t       address_word3[] = { 0x1D, 0x90, 0x00, 0x00 };       /**< TX buffer. */
static uint8_t       address_word4[] = { 0x1D, 0xA0, 0x00, 0x00 };       /**< TX buffer. */
static uint8_t       address_pressure[] = { 0x0F, 0x40, 0x00, 0x00 };    /**< TX buffer. */
static uint8_t       address_temp[] = { 0x0F, 0x20, 0x00, 0x00 };        /**< TX buffer. */
static uint8_t       m_rx_buf[sizeof(address_word1)];    /**< RX buffer. */
static uint8_t       p_rx_buff[sizeof(address_pressure)];    /**< RX buffer. */
//static uint8_t start = 0x1D;
//static uint8_t add_word1 = 0x50;
static const uint8_t m_length = sizeof(m_rx_buf);

/* Declair coefficients factor of MS5540C sensor */
static long c1 = 0, c2 = 0, c3 = 0, c4 = 0, c5 = 0, c6 = 0;
unsigned int sensor_val = 0;

/**
 * @brief SPI user event handler.
 * @param event
 */
void spi_event_handler(nrf_drv_spi_evt_t const *p_event, void *p_context) {
  spi_xfer_done = true;
  NRF_LOG_DEBUG("Transfer completed.");
  if (m_rx_buf[0] != 0) {
    NRF_LOG_INFO(" Received:");
    NRF_LOG_HEXDUMP_INFO(m_rx_buf, strlen((const char *)m_rx_buf));
    NRF_LOG_HEXDUMP_INFO(m_rx_buf2, strlen((const char *)m_rx_buf2));
  }
}

void wait_for_spi_event() {
  while (!spi_xfer_done) {
    idle_state_handle();
  }
}

/**
 * Initialized SPI driver and their pin configurations
 */
void spi_init() {
  nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;
//  spi_config.ss_pin = SPI_SS_PIN; /* Vibzer PCB Pin: 25*/
  spi_config.miso_pin = SPI_MISO_PIN; /* Vibzer PCB Pin: 27*/
  spi_config.mosi_pin = SPI_MOSI_PIN; /* Vibzer PCB Pin: 26*/
  spi_config.sck_pin = SPI_SCK_PIN; /* Vibzer PCB Pin: 28*/
  spi_config.mode    = NRF_DRV_SPI_MODE_0;
  spi_config.frequency    = NRF_SPI_FREQ_500K;
  spi_config.bit_order    = NRF_SPI_BIT_ORDER_MSB_FIRST;
  APP_ERROR_CHECK(nrf_drv_spi_init(&spi, &spi_config, spi_event_handler, NULL));
}

/**
 * reset sensor module and send sequence.
 */
void reset_sensor() {
  NRF_LOG_INFO("Reset Sensor");
  spi_xfer_done = false;
  APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, reset_seq, sizeof(reset_seq), NULL, 0));
  wait_for_spi_event();
  NRF_LOG_FLUSH();
}

/**
 * brief function Read calibration data (factory calibrated) from PROM of MS5540C
 * by sending specific address.
 */
unsigned int read_cal_word(const uint8_t *address) {
  spi_xfer_done = false;
  unsigned int word = 0;
  uint8_t dummy_byte = 0x00;
  memset(m_rx_buf, 0, m_length);
  memset(m_rx_buf2, 0, sizeof(m_rx_buf2));
//  NRF_LOG_INFO("m_length: %d", m_length)
//  NRF_LOG_INFO("address: %s", (uint32_t)address);
  reset_sensor(); /*Before read calibration word need to reset sensor module*/
  APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, address, sizeof(address), m_rx_buf, sizeof(m_rx_buf)));
  nrf_delay_ms(200);
  wait_for_spi_event();
  NRF_LOG_FLUSH();
  APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, &dummy_byte, sizeof(dummy_byte), m_rx_buf2, sizeof(m_rx_buf2)));
  wait_for_spi_event();
  nrf_delay_ms(200);
  NRF_LOG_INFO("RX Buff_byte1:%X byte2:%X", m_rx_buf[2], m_rx_buf[3]);
  word = (m_rx_buf[2] << 8) | m_rx_buf[3]; /*Combine received 2 bytes from sensor*/
//  NRF_LOG_DEBUG("Return Calibration word = %u", word);
  return word;
}

/**
 * brief function for sensor initialized to read factory calibrated data from PROM of MS5540C
 */
void sensor_init() {
  unsigned int word1 = 0; /*Calibration Word 1 */
  unsigned int word2 = 0; /*Calibration Word 2 */
  unsigned int word3 = 0; /*Calibration Word 3 */
  unsigned int word4 = 0; /*Calibration Word 4 */
  
  /* Read Calibration Word 1 */
  word1 = read_cal_word(address_word1);
  NRF_LOG_DEBUG("Calibration Word 1: %u", word1);
  nrf_delay_ms(200);
  /* Read Calibration Word 2 */
  word2 = read_cal_word(address_word2);
  NRF_LOG_DEBUG("Calibration Word 2: %u", word2);
  nrf_delay_ms(200);
  /* Read Calibration Word 3 */
  word3 = read_cal_word(address_word3);
  NRF_LOG_DEBUG("Calibration Word 3: %u", word3);
  nrf_delay_ms(200);
  /* Read Calibration Word 4 */
  word4 = read_cal_word(address_word4);
  NRF_LOG_DEBUG("Calibration Word 4: %u", word4);
  nrf_delay_ms(200);
  /*Do some bitshifting to extract the calibration factors*/
   /*Convert calibration data into coefficients*/
  c1 = (word1 >> 1);
  c2 = ((word3 & 0x3F) << 6) | (word4 & 0x3F);
  c3 = (word4 >> 6);
  c4 = (word3 >> 6);
  c5 = (word2 >> 6) | ((word1 & 0x1) << 10);
  c6 = word2 & 0x3F;
  NRF_LOG_INFO("coefficients- C1: %u C2:%u C3:%u C4:%u C5:%u C6:%u", c1,c2,c3,c4,c5,c6);
}

/**
 * brief function for read pressure value
 */
void read_sensor_val() {
  unsigned int dp_val = 0;
  unsigned int dt_val = 0;
  static uint32_t level = 0;
  /* Read compensated digital pressure value */
  dp_val = read_cal_word(address_pressure);
  NRF_LOG_DEBUG("compensated pressure value: %u", dp_val);
  nrf_delay_ms(200); /* wait for conversion end */
  /* Read compensated digital temperature value*/
  dt_val = read_cal_word(address_temp);
  nrf_delay_ms(200); /* wait for conversion end */
  NRF_LOG_DEBUG("compensated temperature value: %u", dt_val);
}  

The compensate pressure and temperature value getting 65535. Where the coefficients c1-c6 values getting but not sure whether is correct or not.

I am stuck here and getting difficult to understand for debugging. Because we don't have logic analyzer or CRO to check SPI signals.

Will you please provide your suggestion in program or other solution for the same. For your reference I have attached sensor datasheet ms5540c.pdf

Looking forward your response..

Thanks.....

Vishal

Parents
  • A few suggestions, if I may. First the expected command byte 0x1D is 0001'1101 in binary, which corresponds with the data sheet description of "D1" backwards. However, confusingly the actual binary code in the example for "D1" is 10-bit sequence 111'1010'000. Since you are using MSB first transfers, that requires a 10-bit sequence, say 0xF4'0, or the 16-bit sequence 000000'111'1010'000 which is 0x03, 0xD0 although the extra received data would require re-aligning. Using postfix of unwanted clocks rather than prefix would give 111'1010'000'000000 or 0xF4, 0x00. Extra 0x00 bytes would read out the data.

    D2 would then be the 16-bit sequence 000000'111'1001'000 which is 0x03, 0xC8, or postfix 111'1001'000'000000 which is 0xF2, 0x00.

    The 21-bit Reset sequence would be 101010101010101000000'000, which with MSB First is the 3-byte sequence 0xAA, 0xAA, 0x00. This could perhaps better be preceded with the 3 unwanted '0' bits, giving 000'101010101010101000000 or 0x15, 0x55, 0x40. This is what you are using I see.

    I think my starting tests would therefore use the following:

    static uint8_t reset_seq[]     = { 0x15, 0x55, 0x40 };       // 000'101010101010101000000 or 0x15, 0x55, 0x40
    static uint8_t address_word1[] = { 0xF4, 0x00, 0x00, 0x00 }; // 111'1010'000'000000 or 0xF4, 0x00.
    static uint8_t address_word2[] = { 0xF2, 0x00, 0x00, 0x00 }; // 111'1001'000'000000 or 0xF2, 0x00
    or
    static uint8_t reset_seq[]     = { 0x15, 0x55, 0x40 };       // 000'101010101010101000000 or 0x15, 0x55, 0x40
    static uint8_t address_word1[] = { 0x0F, 0x40, 0x00, 0x00 }; // 0000'111'1010'000'00
    static uint8_t address_word2[] = { 0x0F, 0x20, 0x00, 0x00 }; // 0000'111'1001'000'00
    
    So not the same as your code
    

    There is one other issue worth noting, which will break the code if anything other than 4-byte data arrays are used. Have a look at these tests, which pass:

    static uint8_t address_word1[] = { 0x1D, 0x50, 0x00, 0x00 };       /**< TX buffer. */
    const uint8_t *address_p = address_word1;
    STATIC_ASSERT(sizeof(address_p) == 4, "Misunderstanding on meaning of sizeofaddress_p)");
    STATIC_ASSERT(sizeof(address_p) == 4, "Misunderstanding on meaning of sizeof(address_p)");
    STATIC_ASSERT(sizeof(address_word1) == 4, "Error in sizeof(address_word1)");

    Now try using something other than a 4-byte command:

    static uint8_t address_word5[] = { 0x1D, 0xA0, 0x00, 0x00, 0x99 };       /**< TX buffer. */
    const uint8_t *address_p5 = address_word5;
    STATIC_ASSERT(sizeof(address_p5) == 4, "Misunderstanding on meaning of sizeofaddress_p)");
    STATIC_ASSERT(sizeof(address_p5) == 4, "Misunderstanding on meaning of sizeof(address_p)");
    STATIC_ASSERT(sizeof(address_word5) == 5, "Error in  sizeof(address_word5)");

    These tests also pass .. but in the code there is a bug:

    unsigned int read_cal_word(const uint8_t *address) {
      spi_xfer_done = false;
    //blah-blah
      APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, address, sizeof(address), m_rx_buf, sizeof(m_rx_buf)));

    sizeof(address) is always 4 even if the passed array is (say) 5.

    (edited to correct typos)

Reply
  • A few suggestions, if I may. First the expected command byte 0x1D is 0001'1101 in binary, which corresponds with the data sheet description of "D1" backwards. However, confusingly the actual binary code in the example for "D1" is 10-bit sequence 111'1010'000. Since you are using MSB first transfers, that requires a 10-bit sequence, say 0xF4'0, or the 16-bit sequence 000000'111'1010'000 which is 0x03, 0xD0 although the extra received data would require re-aligning. Using postfix of unwanted clocks rather than prefix would give 111'1010'000'000000 or 0xF4, 0x00. Extra 0x00 bytes would read out the data.

    D2 would then be the 16-bit sequence 000000'111'1001'000 which is 0x03, 0xC8, or postfix 111'1001'000'000000 which is 0xF2, 0x00.

    The 21-bit Reset sequence would be 101010101010101000000'000, which with MSB First is the 3-byte sequence 0xAA, 0xAA, 0x00. This could perhaps better be preceded with the 3 unwanted '0' bits, giving 000'101010101010101000000 or 0x15, 0x55, 0x40. This is what you are using I see.

    I think my starting tests would therefore use the following:

    static uint8_t reset_seq[]     = { 0x15, 0x55, 0x40 };       // 000'101010101010101000000 or 0x15, 0x55, 0x40
    static uint8_t address_word1[] = { 0xF4, 0x00, 0x00, 0x00 }; // 111'1010'000'000000 or 0xF4, 0x00.
    static uint8_t address_word2[] = { 0xF2, 0x00, 0x00, 0x00 }; // 111'1001'000'000000 or 0xF2, 0x00
    or
    static uint8_t reset_seq[]     = { 0x15, 0x55, 0x40 };       // 000'101010101010101000000 or 0x15, 0x55, 0x40
    static uint8_t address_word1[] = { 0x0F, 0x40, 0x00, 0x00 }; // 0000'111'1010'000'00
    static uint8_t address_word2[] = { 0x0F, 0x20, 0x00, 0x00 }; // 0000'111'1001'000'00
    
    So not the same as your code
    

    There is one other issue worth noting, which will break the code if anything other than 4-byte data arrays are used. Have a look at these tests, which pass:

    static uint8_t address_word1[] = { 0x1D, 0x50, 0x00, 0x00 };       /**< TX buffer. */
    const uint8_t *address_p = address_word1;
    STATIC_ASSERT(sizeof(address_p) == 4, "Misunderstanding on meaning of sizeofaddress_p)");
    STATIC_ASSERT(sizeof(address_p) == 4, "Misunderstanding on meaning of sizeof(address_p)");
    STATIC_ASSERT(sizeof(address_word1) == 4, "Error in sizeof(address_word1)");

    Now try using something other than a 4-byte command:

    static uint8_t address_word5[] = { 0x1D, 0xA0, 0x00, 0x00, 0x99 };       /**< TX buffer. */
    const uint8_t *address_p5 = address_word5;
    STATIC_ASSERT(sizeof(address_p5) == 4, "Misunderstanding on meaning of sizeofaddress_p)");
    STATIC_ASSERT(sizeof(address_p5) == 4, "Misunderstanding on meaning of sizeof(address_p)");
    STATIC_ASSERT(sizeof(address_word5) == 5, "Error in  sizeof(address_word5)");

    These tests also pass .. but in the code there is a bug:

    unsigned int read_cal_word(const uint8_t *address) {
      spi_xfer_done = false;
    //blah-blah
      APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, address, sizeof(address), m_rx_buf, sizeof(m_rx_buf)));

    sizeof(address) is always 4 even if the passed array is (say) 5.

    (edited to correct typos)

Children
  • Thanks for your great explanation. Now I have modified my program snippet here is below:

    unsigned int read_cal_word(const uint8_t *address, const uint8_t add_len) {
      word = 0;
      memset(m_rx_buf, 0, m_length);
      reset_sensor(); /*Before read calibration word need to reset sensor module*/
      spi_xfer_done = false;
      APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, address, add_len, m_rx_buf, sizeof(m_rx_buf)));
      wait_for_spi_event();
      NRF_LOG_FLUSH();
      NRF_LOG_INFO("RX Buff_byte1:%X byte2:%X", m_rx_buf[2], m_rx_buf[3]);
      word = (m_rx_buf[2] << 8) | m_rx_buf[3]; /*Combine received 2 bytes from sensor*/
    //  NRF_LOG_DEBUG("Return Calibration word = %u", word);
      return word;
    }
    
    \\ Below function use for read D1 and D2
    unsigned int read_sensor_value(const bool check_add) {
      sensor_val = 0;
      static uint8_t       add_pressure[] = { 0x0F, 0x40 }; // D1 command
      static uint8_t       add_temp[] = { 0x0F, 0x20 }; // D2 command       
      uint8_t dummy_byte = 0x00;
      memset(m_rx_buf, 0, m_length);
      reset_sensor(); /*Before read calibration word need to reset sensor module*/
      spi_xfer_done = false;
      if (check_add == true) { /*Check here address of read sensor value*/
      APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, add_pressure, sizeof(add_pressure), NULL, 0));
      } else {
      APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, add_temp, sizeof(add_temp), NULL, 0));
      }
      nrf_delay_ms(200);
      wait_for_spi_event();
      NRF_LOG_FLUSH();
      spi_xfer_done = false;
      APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, &dummy_byte, sizeof(dummy_byte), m_rx_buf, sizeof(m_rx_buf)));
      nrf_delay_ms(200);
      wait_for_spi_event();
      NRF_LOG_FLUSH();
      sensor_val = (m_rx_buf[1] << 8) | m_rx_buf[2];
      NRF_LOG_DEBUG("Sensor value MSB:%X LSB:%X return:[%u]", m_rx_buf[1], m_rx_buf[2], sensor_val);
      return sensor_val;
    }

    But problem is the value of D1= 58048 and D2= 63552 which is out of range of max value of sensor.

    Here is my Logs:

    <info> app: Level Detector started.
    <info> app: Reset Sensor
    <info> app:  Received:
    <info> app:  FE                     |.       
    <info> app: RX Buff_byte1:D7 byte2:9C
    <info> app: Reset Sensor
    <info> app:  Received:
    <info> app:  FE 01 9F 8C            |....    
    <info> app: RX Buff_byte1:9F byte2:8C
    <info> app: Reset Sensor
    <info> app:  Received:
    <info> app:  FE                     |.       
    <info> app: RX Buff_byte1:C6 byte2:2E
    <info> app: Reset Sensor
    <info> app:  Received:
    <info> app:  FE 01 DA 5D            |...]    
    <info> app: RX Buff_byte1:DA byte2:5D
    <info> app: coefficients- C1: 27598 C2:2973 C3:873 C4:792 C5:638 C6:12
    <info> app: Wake up sensor
    <info> app: Reset Sensor
    <info> app:  Received:
    <info> app:  21 E3 40               |!.@     
    <info> app: Sensor value MSB:E3 LSB:40 return:[58176]
    <info> app: compensated pressure value [D1]: 58176
    <info> app: Reset Sensor
    <info> app:  Received:
    <info> app:  3D F9 40               |=.@     
    <info> app: Sensor value MSB:F9 LSB:40 return:[63808]
    <info> app: compensated temperature value [D2]: 63808
    <info> app: Compensated pressure in mbar: -669
    <info> app: Water level: 0cm
    <info> app: Real Temperature in C: 208
    <info> app: Wake up sensor
    <info> app: Reset Sensor
    <info> app:  Received:
    <info> app:  21 E2 C0               |!..     
    <info> app: Sensor value MSB:E2 LSB:C0 return:[58048]
    <info> app: compensated pressure value [D1]: 58048
    <info> app: Reset Sensor
    <info> app:  Received:
    <info> app:  3D F8 40               |=.@     
    <info> app: Sensor value MSB:F8 LSB:40 return:[63552]
    <info> app: compensated temperature value [D2]: 63552
    <info> app: Compensated pressure in mbar: -1069
    <info> app: Water level: 0cm
    <info> app: Real Temperature in C: 207

    How I know the measured value is corrected, because the temperature is not getting correct.

    Thanks....

  • First I would not wait for the spi event, or use such a long delay, since the 33mSec conversion starts while the spi transfer is finishing up.

    Change:
      nrf_delay_ms(200);
      wait_for_spi_event();
    To:
      nrf_delay_ms(33 and a bit more); // 33 mSec conversion time
      wait_for_low_DOUT();             // DOUT goes low when data ready
    or just:
      wait_for_falling_edge_on_DOUT(); // DOUT goes low when data ready

    Next the SPI transfer is already in progress, so I would suggest the first 2 bytes of received data are the bytes making up the 16-bit value in D1 (say), not the 2nd and 3rd bytes. For there to be no issues with Tx presenting unwanted '1's while reading out Rx, which could cause erroneous operation, I would be inclined to make the transfer specifically send '0' on Tx while reading in the 2 Rx bytes

    // Ensure trailing Tx bits are all '0'
    spi_config.orc = 0x00;

  • Hi, 

    If I change delay 200ms to 35ms then sensor is not getting correct value. If I add 200ms delay then it should fine and working and got corrected value of D1 and D2.

    What I need to do in wait_for_low_DOUT(); and wait_for_falling_edge_on_DOUT(); functions.

    Currently my program is working with added 200ms delay but is this good way.

    Thanks....

Related