Sensirion / embedded-sps

Embedded i2c Driver for Sensirion Particulate Matter Sensors - Download the Zip Package from the Release Page
https://github.com/Sensirion/embedded-sps/releases
BSD 3-Clause "New" or "Revised" License
45 stars 15 forks source link

SPS30 sensor get serial fail #26

Closed FATRI closed 5 years ago

FATRI commented 5 years ago

Hi, I have some problems with your SPS30 sensor with the TI CC3220MODASF MCU. Using your SDK (sps30_get_serial), and when I read the serial number, I only get "FF" bytes and the CRC check fails. can you help me solve my problem?

Note: I am using your SCD30 sensor with the same MCU as TI CC3220MODASF and everything is working properly.

Thank yo

rnestler commented 5 years ago

Hi @FATRI

Can you show the full code you are using? Also did you make sure that the sensors starts in i2c mode and not in UART mode (pin4 pulled to ground)?

FATRI commented 5 years ago

Hi, Thank your for your reply, Pin4 is pulled to ground to start the sensor in I2C mode. First i initialize the I2C on CC3220MODASF MCU. then I use the following code to init the SPS 30 sensor :

int8_t initSPS30()
{
    int8_t l_iCounter = 0;

    /* Busy loop for initialization, Simply to check that the sensor
     * is connected to the board successfully
     */
    while (sps30_probe() != STATUS_OK)
    {

        sleep(1);
        l_iCounter++;
        if (l_iCounter == 5)
        {
            return FAILURE;
        }
    }

    return SUCCESS;
}
__s16 sps30_probe() {
    char serial[SPS_MAX_SERIAL_LEN];

    return sps30_get_serial(serial);
}

__s16 sps30_get_serial(char *serial) {
    __u16 i;
    __s16 ret;
    union {
        char serial[SPS_MAX_SERIAL_LEN];
        __u16 __enforce_alignment;
    } buffer;

    ret = sensirion_i2c_read_cmd(SPS_I2C_ADDRESS, SPS_CMD_GET_SERIAL,
                                 (__u16 *)buffer.serial,
                                 SENSIRION_NUM_WORDS(buffer.serial));
    if (ret != STATUS_OK)
        return ret;

    SENSIRION_WORDS_TO_BYTES(buffer.serial, SENSIRION_NUM_WORDS(buffer.serial));
    for (i = 0; i < SPS_MAX_SERIAL_LEN; ++i) {
        serial[i] = buffer.serial[i];
        if (serial[i] == '\0')
            return 0;
    }

    return 0;
}
__s16 sensirion_i2c_delayed_read_cmd(__u8 address, __u16 cmd, __u32 delay_us,
                                   __u16 *data_words, __u16 num_words) {
    __s16 ret;
    __u8 buf[SENSIRION_COMMAND_SIZE];

    sensirion_fill_cmd_send_buf(buf, cmd, NULL, 0);
    ret = sensirion_i2c_write(address, buf, SENSIRION_COMMAND_SIZE);
    if (ret != STATUS_OK)
        return ret;

    if (delay_us)
        sensirion_sleep_usec(delay_us);

    return sensirion_i2c_read_words(address, data_words, num_words);
}

__s16 sensirion_i2c_read_cmd(__u8 address, __u16 cmd, __u16 *data_words, __u16 num_words)
{
    return sensirion_i2c_delayed_read_cmd(address, cmd, 0, data_words,
                                          num_words);
}

__s16 sensirion_i2c_read_words(__u8 address, __u16 *data_words, __u16 num_words) {
    __s16 ret;
    __u8 i;

    ret = sensirion_i2c_read_bytes(address, (__u8 *)data_words, num_words);
    if (ret != STATUS_OK)
        return ret;

    for (i = 0; i < num_words; ++i)
        data_words[i] = be16_to_cpu(data_words[i]);

    return STATUS_OK;
}

__s16 sensirion_i2c_read_bytes(__u8 address, __u8 *data, __u16 num_words) {
    __s16 ret;
    __u16 i, j;
    __u16 size = num_words * (SENSIRION_WORD_SIZE + CRC8_LEN);
    __u16 word_buf[SENSIRION_MAX_BUFFER_WORDS];
    __u8 * const buf8 = (__u8 *)word_buf;

    ret = sensirion_i2c_read(address, buf8, size);
    if (ret != STATUS_OK)
        return ret;

    /* check the CRC for each word */
    for (i = 0, j = 0; i < size; i += SENSIRION_WORD_SIZE + CRC8_LEN) {

        ret = sensirion_common_check_crc(&buf8[i], SENSIRION_WORD_SIZE,
                                         buf8[i + SENSIRION_WORD_SIZE]);
        if (ret != STATUS_OK)
            return ret;

        data[j++] = buf8[i];
        data[j++] = buf8[i + 1];
    }

    return STATUS_OK;
}

On The last function After sensirion_i2c_read (I receive a series of FF), then the CRC check fail??

rnestler commented 5 years ago

On The last function After sensirion_i2c_read (I receive a series of FF), then the CRC check fail??

If you just receive 0xFF you should also get an error code returned. Can you also post the source of your I2C implementation? I suspect there is something wrong there.

FATRI commented 5 years ago

The Error code returned is : -1 The I2C code used is : /*

int I2C_IF_Write(unsigned char ucDevAddr, unsigned char pucData, unsigned char ucLen) { // unsigned char buf_read[2]; / Point to the concerned register and read its readLen bytes */ i2cTransaction.slaveAddress = ucDevAddr; i2cTransaction.writeBuf = pucData; i2cTransaction.writeCount = ucLen; i2cTransaction.readBuf = NULL; i2cTransaction.readCount = 0;

if (I2C_transfer(i2c, &i2cTransaction))
{

// UART_PRINT("I2C Transfer SUCCESS \n\r"); return SUCCESS; } else { UART_PRINT("I2C Bus fault\n\r"); return FAILURE; } }

//**** //!I2C_IF_Read_Reg //! \return 0: Success, < 0: Failure. //****

int I2C_IF_Read(unsigned char ucDevAddr, unsigned char pucData, unsigned char ucLen) { / Point to the concerned register and read its readLen bytes */ i2cTransaction.slaveAddress = ucDevAddr; i2cTransaction.writeBuf = NULL; i2cTransaction.writeCount = 0; i2cTransaction.readBuf = pucData; i2cTransaction.readCount = ucLen;

if (I2C_transfer(i2c, &i2cTransaction))
{

// UART_PRINT("I2C Transfer SUCCESS \n\r"); return SUCCESS; } else { UART_PRINT("I2C Bus fault\n\r"); return FAILURE; } }

Nb: This the same I2c code that i use for communicating with SCD30 sensor.

abrauchli commented 5 years ago

Hi @FATRI

Could you please clean up the code before posting? The indentation is quite bad which makes it hard to follow. Also the types do not correspond to the driver. We cannot support you if you change parts of the framework, e.g. the typedef statements used would be helpful in this case. Please also use the code markers (<> button) to make sure that the formatting is not affected by styling.

One thing I did spot while glancing at the code is the following signature which should cause a compiler warning (unless you also changed the header): __s8 sensirion_i2c_write(__u8 address, __u8 data, __u16 count)

Assuming that __u8 is mapped to unsigned char (our u8 / uint8_t), data should be a pointer __u8 *data

FATRI commented 5 years ago

Thank you for your reply. Below the cleaned code :

int8_t sensirion_i2c_read(uint8_t address, uint8_t* data, uint16_t count)
{
    int8_t l_scStatus;

    uint8_t l_ucbuf_reg[1];
    l_ucbuf_reg[0] = address;

     /*Indicate the register to be read*/
    l_scStatus = I2C_IF_Write(address, l_ucbuf_reg, 1);
     if (l_scStatus == SUCCESS)
     {
         UtilsDelay(100000);
       // Read from the indicated register 
         l_scStatus = I2C_IF_Read(address, data, count);
     }
    return l_scStatus;
}
int8_t sensirion_i2c_write(uint8_t address,  uint8_t* data, uint16_t count)
{
    int8_t l_scStatus;
    l_scStatus = I2C_IF_Write(address, data, count);
    return l_scStatus;
}

int I2C_IF_Read(unsigned char ucDevAddr, unsigned char *pucData,
                    unsigned char ucLen)
{
    i2cTransaction.slaveAddress = ucDevAddr;
    i2cTransaction.writeBuf = NULL;
    i2cTransaction.writeCount = 0;
    i2cTransaction.readBuf = pucData;
    i2cTransaction.readCount = ucLen;

    if (I2C_transfer(i2c, &i2cTransaction))
    {
        return SUCCESS;
    }
    else
    {
        UART_PRINT("I2C Bus fault\n\r");
        return FAILURE;
    }
}

int I2C_IF_Write(unsigned char ucDevAddr, unsigned char *pucData,
                unsigned char ucLen)
{
    i2cTransaction.slaveAddress = ucDevAddr;
    i2cTransaction.writeBuf = pucData;
    i2cTransaction.writeCount = ucLen;
    i2cTransaction.readBuf = NULL;
    i2cTransaction.readCount = 0;

    if (I2C_transfer(i2c, &i2cTransaction))
    {
        return SUCCESS;
    }
    else
    {
        UART_PRINT("I2C Bus fault\n\r");
        return FAILURE;
    }
}
rnestler commented 5 years ago

You can use Syntax highlighting code blocks when posting code. I updated your comment to use it.

int8_t sensirion_i2c_read(uint8_t address, uint8_t* data, uint16_t count)
{
   int8_t l_scStatus;

   uint8_t l_ucbuf_reg[1];
   l_ucbuf_reg[0] = address;

    /*Indicate the register to be read*/
   l_scStatus = I2C_IF_Write(address, l_ucbuf_reg, 1);
    if (l_scStatus == SUCCESS)
    {
        UtilsDelay(100000);
      // Read from the indicated register 
        l_scStatus = I2C_IF_Read(address, data, count);
    }
   return l_scStatus;
}

Why do you call I2C_IF_Write in this function? sensirion_i2c_read is only supposed to read from the I2C bus.

FATRI commented 5 years ago

Thank you for your help. I wanted to use I2C_IF_Write to indicate the registre that i will read from it the data, but it is this function that causes me the error. which weird, i already use the same way with SCD30 and read the data.

rnestler commented 5 years ago

Weird indeed. sensirion_i2c_read is just intended to read, any write before the read, to indicate the register to read, is done by the sps30 driver itself using sensirion_i2c_write. But glad that you could solve your issue :slightly_smiling_face:

FATRI commented 5 years ago

Thank you for your help.

you are right, i use write before read to indicate the registre to read, i forgot that SPS30 driver set a registre to read before this step. This is a mistake that i make. but, i use the same way with the SCD30 and i can read data??

Best regards.

rnestler commented 5 years ago

but, i use the same way with the SCD30 and i can read data??

I'm not sure, it probably just works by chance. Maybe the SCD30 just ignores the second write access since it is invalid or it is exactly the same register address that you send additionally. Either way, you should just remove the write call from your SCD30 driver as well.