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

nrF52832 get wrong WHO_AM_I value with spi

Hey guys (and merry Christmas),

I'm working with a Laird BL652-DVK (nrF 52832 - chip), Nordic SDK v.14.2.0 and SES (Segger Embedded Studio v.4.30). I try to receive data from an accelerometer using spi. At first I configured the uart example because I want to send the data to my pc with uart and Termite v3.4. That's working.

Now I have implented the spi example into the uart example.  I have configured spi and the accelerometer. After that I read the WHO_AM_I register (uint8_t read_data_1), but I get a wrong value. I get the value "11" instead of "33".... My code is below. If I read the "uint8_t read_data[1]" (commented out), I get the value "0x0". I really don't know, what to do. I hope someone can help me.

My code:

/**
 * Copyright (c) 2014 - 2017, Nordic Semiconductor ASA
 * 
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form, except as embedded into a Nordic
 *    Semiconductor ASA integrated circuit in a product or a software update for
 *    such product, must reproduce the above copyright notice, this list of
 *    conditions and the following disclaimer in the documentation and/or other
 *    materials provided with the distribution.
 * 
 * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 * 
 * 4. This software, with or without modification, must only be used with a
 *    Nordic Semiconductor ASA integrated circuit.
 * 
 * 5. Any software provided in binary form under this license must not be reverse
 *    engineered, decompiled, modified and/or disassembled.
 * 
 * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 */
/** @file
 * @defgroup uart_example_main main.c
 * @{
 * @ingroup uart_example
 * @brief UART Example Application main file.
 *
 * This file contains the source code for a sample application using UART.
 *
 */

#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <math.h>
#include "app_uart.h"
#include "app_error.h"
#include "nrf_delay.h"
#include "nrf.h"
#include "bsp.h"
#if defined (UART_PRESENT)
#include "nrf_uart.h"
#endif
#if defined (UARTE_PRESENT)
#include "nrf_uarte.h"
#endif

//spi header
#include <nrf_drv_spi.h>
#include "app_util_platform.h"
#include "nrf_gpio.h"
#include "boards.h"
#include <string.h>
#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"


#define MAX_TEST_DATA_BYTES     (15U)                /**< max number of test bytes to be used for tx and rx. */
#define UART_TX_BUF_SIZE 256                         /**< UART TX buffer size. */
#define UART_RX_BUF_SIZE 256                         /**< UART RX buffer size. */
#define UART_HWFC APP_UART_FLOW_CONTROL_DISABLED     /* When UART is used for communication with the host do not use flow control.*/


//spi variables
#define SPI_INSTANCE  0 /**< SPI instance index. */
static const nrf_drv_spi_t spi = NRF_DRV_SPI_INSTANCE(SPI_INSTANCE);  /**< SPI instance. */
static volatile bool spi_xfer_done;  /**< Flag used to indicate that SPI instance completed the transfer. */
//uint8_t read_data[1];
uint8_t read_data[1];

//accel input register
uint8_t LIS3DH_CTRL_REG1[] = {0x20, 0x47}; //all axes, normal, 50Hz
uint8_t LIS3DH_CTRL_REG2[] = {0x21, 0x00}; //no highpass filter
uint8_t LIS3DH_CTRL_REG3[] = {0x22, 0x00}; //no interrupts
uint8_t LIS3DH_CTRL_REG4[] = {0x23, 0x88}; //all defaults
uint8_t LIS3DH_CTRL_REG5[] = {0x24, 0x00}; //all defaults
uint8_t LIS3DH_CTRL_REG6[] = {0x25, 0x00}; //all defaults


//accel output register
uint8_t LIS3DH_WHO_AM_I_REG[] = {0x0F};
uint8_t LIS3DH_OUT_X_H[] = {0x29};
uint8_t LIS3DH_OUT_X_L[] = {0x28};
uint8_t LIS3DH_OUT_Y_H[] = {0x2B};
uint8_t LIS3DH_OUT_Y_L[] = {0x2A};
uint8_t LIS3DH_OUT_Z_H[] = {0x2D};
uint8_t LIS3DH_OUT_Z_L[] = {0x2C};


//uart error handle
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);
    }
}


//init lis3dh
void init_lis() 
{
        nrf_drv_spi_transfer(&spi, LIS3DH_CTRL_REG1, sizeof(LIS3DH_CTRL_REG1), NULL, 0);
        nrf_drv_spi_transfer(&spi, LIS3DH_CTRL_REG2, sizeof(LIS3DH_CTRL_REG2), NULL, 0);
        nrf_drv_spi_transfer(&spi, LIS3DH_CTRL_REG3, sizeof(LIS3DH_CTRL_REG3), NULL, 0);
        nrf_drv_spi_transfer(&spi, LIS3DH_CTRL_REG4, sizeof(LIS3DH_CTRL_REG4), NULL, 0);
        nrf_drv_spi_transfer(&spi, LIS3DH_CTRL_REG5, sizeof(LIS3DH_CTRL_REG5), NULL, 0);
        nrf_drv_spi_transfer(&spi, LIS3DH_CTRL_REG6, sizeof(LIS3DH_CTRL_REG6), NULL, 0);
}



//spi user event handler
void spi_event_handler(nrf_drv_spi_evt_t const * p_event,
                       void *                    p_context)
{
    spi_xfer_done = true;
    printf("Transfer completed.");
}



/**
 * @brief Function for main application entry.
 */
int main(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,
          UART_HWFC,
          false,
          NRF_UART_BAUDRATE_115200
      };

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

    bsp_board_leds_init();

    printf("\r\nStart: \r\n");

    nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;  //SPI master driver instance configuration
    spi_config.ss_pin   = SPI_SS_PIN;
    spi_config.miso_pin = SPI_MISO_PIN;
    spi_config.mosi_pin = SPI_MOSI_PIN;
    spi_config.sck_pin  = SPI_SCK_PIN;
    spi_config.frequency = NRF_DRV_SPI_FREQ_2M; // data rate: 2 Mbps
    spi_config.mode     = NRF_DRV_SPI_MODE_3;	// SPI mode: SCK active low, sample on trailing edge of clock.
    nrf_drv_spi_init(&spi, &spi_config, spi_event_handler, NULL); //Function for initializing the SPI master driver instance
    
    printf("\r\nSPI config done! \r\n");

    APP_ERROR_CHECK(err_code);

    init_lis();
    
    uint8_t read_data_1 = nrf_drv_spi_transfer(&spi, LIS3DH_WHO_AM_I_REG, sizeof(LIS3DH_WHO_AM_I_REG), read_data, sizeof(read_data));
    printf("\r\nWHO_AM_I: %x\n", read_data_1);  //value: 0x11 ??
    /*
    nrf_drv_spi_transfer(&spi, LIS3DH_WHO_AM_I_REG, sizeof(LIS3DH_WHO_AM_I_REG), read_data, sizeof(read_data));
    printf("\r\nWHO_AM_I: %x\n", read_data[1]);  //value: 0x0 ?? 
    */

}


/** @} */

Here I get the value "0x11" instead of "0x33":

 uint8_t read_data_1 = nrf_drv_spi_transfer(&spi, LIS3DH_WHO_AM_I_REG, sizeof(LIS3DH_WHO_AM_I_REG), read_data, sizeof(read_data));
    printf("\r\nWHO_AM_I: %x\n", read_data_1);  //value: 0x11 ??

And here I get the value "0x0" instead of "0x33":

nrf_drv_spi_transfer(&spi, LIS3DH_WHO_AM_I_REG, sizeof(LIS3DH_WHO_AM_I_REG), read_data, sizeof(read_data));
printf("\r\nWHO_AM_I: %x\n", read_data[1]);  //value: 0x0 ??

Thanks in advance,

Christoph

  • Several issues in the code above, try the following changes:

    uint8_t read_data[1];
    change to:
    // 2-byte receive array, 2nd byte will contain Id
    uint8_t read_data[2];

    A read operation is required for the Id, so try this:

    uint8_t LIS3DH_WHO_AM_I_REG[] = {0x0F};
    Change to:
    // Set ms bit to perform a read
    uint8_t LIS3DH_WHO_AM_I_REG[] = {0x80 | 0x0F};

    The data sheet implies Mode 3, but I find Mode 0 works best:

        spi_config.mode     = NRF_DRV_SPI_MODE_0;

    This should now work, but I would suggest making this the first operation before init_lis():

        nrf_drv_spi_transfer(&spi, LIS3DH_WHO_AM_I_REG, sizeof(LIS3DH_WHO_AM_I_REG), read_data, sizeof(read_data));
        printf("\r\nWHO_AM_I: %x\n", read_data[1]);  //print 2nd returned byte, which will be Id 

    Once it works, the clock speed can be increased to 8MHz, although if connected by long traces SCK and MOSI and CS might require H0H1 drive levels at the faster clock speed. Good luck!

  • Hey hmolesworth,

    thanks for you fast reply. Because of Christmas I can't make the changes unitl December 28th (saturday). But from saturday I will make the changes und write the results here.

    Best regards and merry Christmas,

    Christoph

  • Hey hmolesworth,

    the changes doesn't work. Now I get: 0x0.

    Here is my current code:

    /**
     * Copyright (c) 2014 - 2017, Nordic Semiconductor ASA
     * 
     * All rights reserved.
     * 
     * Redistribution and use in source and binary forms, with or without modification,
     * are permitted provided that the following conditions are met:
     * 
     * 1. Redistributions of source code must retain the above copyright notice, this
     *    list of conditions and the following disclaimer.
     * 
     * 2. Redistributions in binary form, except as embedded into a Nordic
     *    Semiconductor ASA integrated circuit in a product or a software update for
     *    such product, must reproduce the above copyright notice, this list of
     *    conditions and the following disclaimer in the documentation and/or other
     *    materials provided with the distribution.
     * 
     * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
     *    contributors may be used to endorse or promote products derived from this
     *    software without specific prior written permission.
     * 
     * 4. This software, with or without modification, must only be used with a
     *    Nordic Semiconductor ASA integrated circuit.
     * 
     * 5. Any software provided in binary form under this license must not be reverse
     *    engineered, decompiled, modified and/or disassembled.
     * 
     * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
     * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
     * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
     * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
     * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     * 
     */
    /** @file
     * @defgroup uart_example_main main.c
     * @{
     * @ingroup uart_example
     * @brief UART Example Application main file.
     *
     * This file contains the source code for a sample application using UART.
     *
     */
    
    #include <stdbool.h>
    #include <stdint.h>
    #include <stdio.h>
    #include <math.h>
    #include "app_uart.h"
    #include "app_error.h"
    #include "nrf_delay.h"
    #include "nrf.h"
    #include "bsp.h"
    #if defined (UART_PRESENT)
    #include "nrf_uart.h"
    #endif
    #if defined (UARTE_PRESENT)
    #include "nrf_uarte.h"
    #endif
    
    //spi header
    #include <nrf_drv_spi.h>
    #include "app_util_platform.h"
    #include "nrf_gpio.h"
    #include "boards.h"
    #include <string.h>
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    
    
    #define MAX_TEST_DATA_BYTES     (15U)                /**< max number of test bytes to be used for tx and rx. */
    #define UART_TX_BUF_SIZE 256                         /**< UART TX buffer size. */
    #define UART_RX_BUF_SIZE 256                         /**< UART RX buffer size. */
    #define UART_HWFC APP_UART_FLOW_CONTROL_DISABLED     /* When UART is used for communication with the host do not use flow control.*/
    
    
    //spi variables
    #define SPI_INSTANCE  0 /**< SPI instance index. */
    static const nrf_drv_spi_t spi = NRF_DRV_SPI_INSTANCE(SPI_INSTANCE);  /**< SPI instance. */
    static volatile bool spi_xfer_done;  /**< Flag used to indicate that SPI instance completed the transfer. */
    uint8_t read_data[2]; //2-byte receive array, 2nd byte will contain id
    
    //accel input register
    uint8_t LIS3DH_CTRL_REG1[] = {0x20, 0x47}; //all axes, normal, 50Hz
    uint8_t LIS3DH_CTRL_REG2[] = {0x21, 0x00}; //no highpass filter
    uint8_t LIS3DH_CTRL_REG3[] = {0x22, 0x00}; //no interrupts
    uint8_t LIS3DH_CTRL_REG4[] = {0x23, 0x88}; //all defaults
    uint8_t LIS3DH_CTRL_REG5[] = {0x24, 0x00}; //all defaults
    uint8_t LIS3DH_CTRL_REG6[] = {0x25, 0x00}; //all defaults
    
    
    //accel output register
    uint8_t LIS3DH_WHO_AM_I_REG[] = {0x80 | 0x0F};  // Set ms bit to perform a read
    uint8_t LIS3DH_OUT_X_H[] = {0x29};
    uint8_t LIS3DH_OUT_X_L[] = {0x28};
    uint8_t LIS3DH_OUT_Y_H[] = {0x2B};
    uint8_t LIS3DH_OUT_Y_L[] = {0x2A};
    uint8_t LIS3DH_OUT_Z_H[] = {0x2D};
    uint8_t LIS3DH_OUT_Z_L[] = {0x2C};
    
    
    //uart error handle
    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);
        }
    }
    
    
    //init lis3dh
    void init_lis() 
    {
            nrf_drv_spi_transfer(&spi, LIS3DH_CTRL_REG1, sizeof(LIS3DH_CTRL_REG1), NULL, 0);
            nrf_drv_spi_transfer(&spi, LIS3DH_CTRL_REG2, sizeof(LIS3DH_CTRL_REG2), NULL, 0);
            nrf_drv_spi_transfer(&spi, LIS3DH_CTRL_REG3, sizeof(LIS3DH_CTRL_REG3), NULL, 0);
            nrf_drv_spi_transfer(&spi, LIS3DH_CTRL_REG4, sizeof(LIS3DH_CTRL_REG4), NULL, 0);
            nrf_drv_spi_transfer(&spi, LIS3DH_CTRL_REG5, sizeof(LIS3DH_CTRL_REG5), NULL, 0);
            nrf_drv_spi_transfer(&spi, LIS3DH_CTRL_REG6, sizeof(LIS3DH_CTRL_REG6), NULL, 0);
    }
    
    
    
    //spi user event handler
    void spi_event_handler(nrf_drv_spi_evt_t const * p_event,
                           void *                    p_context)
    {
        spi_xfer_done = true;
        printf("Transfer completed.");
    }
    
    
    
    /**
     * @brief Function for main application entry.
     */
    int main(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,
              UART_HWFC,
              false,
              NRF_UART_BAUDRATE_115200
          };
    
        APP_UART_FIFO_INIT(&comm_params,
                             UART_RX_BUF_SIZE,
                             UART_TX_BUF_SIZE,
                             uart_error_handle,
                             APP_IRQ_PRIORITY_LOWEST,
                             err_code);
    
        bsp_board_leds_init();
    
        printf("\r\nStart: \r\n");
    
        nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;  //SPI master driver instance configuration
        spi_config.ss_pin   = SPI_SS_PIN;
        spi_config.miso_pin = SPI_MISO_PIN;
        spi_config.mosi_pin = SPI_MOSI_PIN;
        spi_config.sck_pin  = SPI_SCK_PIN;
        spi_config.frequency = NRF_DRV_SPI_FREQ_2M; // data rate: 2 Mbps
        spi_config.mode     = NRF_DRV_SPI_MODE_0;	// SPI mode: SCK active low, sample on trailing edge of clock.
        nrf_drv_spi_init(&spi, &spi_config, spi_event_handler, NULL); //Function for initializing the SPI master driver instance
        
        printf("\r\nSPI config done! \r\n");
    
        APP_ERROR_CHECK(err_code);
        
        nrf_drv_spi_transfer(&spi, LIS3DH_WHO_AM_I_REG, sizeof(LIS3DH_WHO_AM_I_REG), read_data, sizeof(read_data));
        printf("\r\nWHO_AM_I: %x\n", read_data[1]);  //print 2nd returned byte, which will be Id 
    
        init_lis();
    
    }
    
    
    /** @} */
    

    And I don't know where is the issue..

  • Hope you had a good Christmas! Actually your code does work now, I just ran it (on a LIS2DH12, very similar part)! I tried both 2MHz and 8MHz and both return 0x33 as expected. I had printfs commented out and just put in a breakpoint to look at the 2-byte returned values:

    int test_main(void)
    {
        uint32_t err_code;
        //printf("\r\nStart: \r\n");
        nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;  //SPI master driver instance configuration
        spi_config.ss_pin   = SPI_SS_PIN;
        spi_config.miso_pin = SPI_MISO_PIN;
        spi_config.mosi_pin = SPI_MOSI_PIN;
        spi_config.sck_pin  = SPI_SCK_PIN;
        spi_config.frequency = NRF_DRV_SPI_FREQ_8M; // data rate: 2 Mbps
        spi_config.mode     = NRF_DRV_SPI_MODE_0;	// SPI mode: SCK active low, sample on trailing edge of clock.
        nrf_drv_spi_init(&spi, &spi_config, spi_event_handler, NULL); //Function for initializing the SPI master driver instance
        //printf("\r\nSPI config done! \r\n");
        APP_ERROR_CHECK(err_code);
        nrf_drv_spi_transfer(&spi, LIS3DH_WHO_AM_I_REG, sizeof(LIS3DH_WHO_AM_I_REG), read_data, sizeof(read_data));
        //printf("\r\nWHO_AM_I: %x\n", read_data[1]);  //print 2nd returned byte, which will be Id 
        init_lis();
    }

    Maybe share your circuit (or at least the part of the LIS3DH).in case there is some issue, perhaps i/o pin voltage. Typically (LIS2DH12) pins 5,6,7,8 are all Gnd; pins 9,10 share nRF52832 Vdd; LIS3 is similar but different pins

  • Hey hmolesworth,

    yes thanks, I hope you had a good Christmas too! Ok, I insert photos of my development kit, Uart-to-USB-Bridge and the LIS3DH-circuit. At first I wanted to say, that I changed my code a little bit and got more terminal output.

    My changes:

    //from this
    spi_config.ss_pin   = SPI_SS_PIN;
    spi_config.miso_pin = SPI_MISO_PIN;
    spi_config.mosi_pin = SPI_MOSI_PIN;
    spi_config.sck_pin  = SPI_SCK_PIN;
    spi_config.frequency = NRF_DRV_SPI_FREQ_2M;
    
    //to this
    spi_config.ss_pin   = 22;
    spi_config.miso_pin = 24;
    spi_config.mosi_pin = 23;
    spi_config.sck_pin  = 25;
    spi_config.frequency = NRF_DRV_SPI_FREQ_8M;

    And now I got this output:

    I think that the manual pin configuration made SPI transfers and the spi_event_handler (code below) was triggered by it.

    //spi user event handler
    void spi_event_handler(nrf_drv_spi_evt_t const * p_event,
                           void *                    p_context)
    {
        spi_xfer_done = true;
        printf("\r\nTransfer completed. \r\n");
    }

    From here come photos and circuits of the components:

    -BL652_SA:

    Here is the BL652-SA datasheet: https://connectivity-staging.s3.us-east-2.amazonaws.com/2019-05/CS-DS-BL652%20v2_9.pdf

    I can send BL652_SA circuit by mail, if you want. Maybe it's not allowed to public the circuit in a forum.

    -LIS3DH:

    *photo removed*  ->not public

    circuit:

    *removed*  ->not public

    ->pin6: 3.3V, pin5: GND, pin4: SCL 3.3V, pin3: MOSI, pin2:MISO, pin1:CSD/SS

    Connector:

    MOSI and MISO connectioh to board:

    SCL and SS connection to board:

    VDD and GND connection to board:

    -USB-to-UART-Bridge:

    I hope this will help. I can insert more photos if needed.

    PS: hmolesworth thank you for your dedication to help me!

Related