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

Nordic Thingy 52 - external I2C sensor device driver implementation

Hello everyone,

i'm trying to hook up an external i2C sensor to P5 connector (TWI_SCL_EXT, TWI_SDA_EXT) and created a device driver template ( from drv_gas_sensor.c example) .

to start with  i have created this function in m_environment.c:

/**@brief Function for initializing the MY sensor
 */
static uint32_t my_sensor_init(const nrf_drv_twi_t * p_twi_instance)
{
    uint32_t       err_code;
    drv_my_init_t init_params;

    static const nrf_drv_twi_config_t twi_config =
    {
        .scl                = TWI_SCL_EXT,
        .sda                = TWI_SDA_EXT,
        .frequency          = NRF_TWI_FREQ_400K,
        .interrupt_priority = APP_IRQ_PRIORITY_LOW
    };

    init_params.p_twi_instance = p_twi_instance;
    init_params.p_twi_cfg      = &twi_config;
    init_params.twi_addr       = MY_ADDR;
    init_params.data_handler   = drv_my_data_handler;

    err_code = drv_my_init(&init_params);
    RETURN_IF_ERROR(err_code);

    return NRF_SUCCESS;

}

//and calling this function in  m_environment_init() function as below

     err_code = my_sensor_init(p_params->p_twi_instance);
    APP_ERROR_CHECK(err_code);

let me know if this is right.

Parents
  • I don't see anything wrong initially, but it is diffiucult to say without seeing your custom made sensor driver. Remember to not use the same pins for the SDA and SCL as any of the already connected sensor. An option is to connect the external sensor using the pins available due I/O expander, as Martin suggested in this post.

    Best regards,

    Simon

  • Hello Simon, thanks for your response and suggestion.
    to clarify i just assume that i2c bus can have multiple i2c sensors on
    same bus provided they have unique slave address. with that in mind i
    connected my sensor to TWI_SCL_EXT and TWI_SDA_EXT pin which is also
    share with LIS2DH12. let me know if this is OK.

    if i have to connect an exclusive i2c pin using IO expanded, please do
    let me know  which are the pins can be used from P4 connector.

    also  i do not have any interrupt pin on my sensor, hence i can not
    use io interrupt driven - event handler. let me know best possible
    example.

    By the way connecting my sensor to P5 ( TWI_SCL_EXT, TWI_SDA_EXT )
    connector and then running my i2c sensor code as a standalone
    application ( using ble peripheral example from Sdk 15.0)  i'm able to
    read sensor data and pass this data to a custom BLE characteristics.
    however i do not have any event handling in that standalone
    application.

    trouble mounts only when i combine this code with Thingy FW which is
    basically non blocking implementation.

    thanks for your time.

  • BlueTech said:
    if i have to connect an exclusive i2c pin using IO expanded, please do
    let me know  which are the pins can be used from P4 connector.

     No, you probably don't need to use the IO expander. I was too quick to answer and forgot that several TWI sensors can be connected to the SDA and SCL lines simultaneously.

    However I managed to integrate the ADXL375 I2C sensor to the thingy project (pca20020_s132), and have attached the main code (and the drivers for the ADXL375 sensor if that is of interest). Be aware that I only printed the sensor data once, but you should probably implement a timer, and print the twi sensor data in the timer event handler.

    Best regards,

    Simon

    /*
      Copyright (c) 2010 - 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
     *
     * @brief    Thingy application main file.
     *
     * This file contains the source code for the Thingy application that uses the Weather Station service.
     */
    
    #include <stdint.h>
    #include <float.h>
    #include <string.h>
    #include "nordic_common.h"
    #include "nrf.h"
    #include "ble_hci.h"
    #include "ble_advdata.h"
    #include "ble_advertising.h"
    #include "ble_conn_params.h"
    #include "softdevice_handler.h"
    #include "app_scheduler.h"
    #include "app_button.h"
    #include "app_util_platform.h"
    #include "m_ble.h"
    #include "m_environment.h"
    #include "m_sound.h"
    #include "m_motion.h"
    #include "m_ui.h"
    #include "m_batt_meas.h"
    #include "drv_ext_light.h"
    #include "drv_ext_gpio.h"
    #include "nrf_delay.h"
    #include "twi_manager.h"
    #include "support_func.h"
    #include "pca20020.h"
    #include "app_error.h"
    
    /////Added for TWI/////
    #include "nrf_drv_twi.h"
    #include "ADXL375_I2C.h"
    /////Added for TWI/////
    
    #define  NRF_LOG_MODULE_NAME "main          "
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    
    #define DEAD_BEEF   0xDEADBEEF          /**< Value used as error code on stack dump, can be used to identify stack location on stack unwind. */
    #define SCHED_MAX_EVENT_DATA_SIZE   MAX(APP_TIMER_SCHED_EVENT_DATA_SIZE, BLE_STACK_HANDLER_SCHED_EVT_SIZE) /**< Maximum size of scheduler events. */
    #define SCHED_QUEUE_SIZE            60  /**< Maximum number of events in the scheduler queue. */
    
    /////Added for TWI/////
    /* TWI instance ID. */
    #define TWI_INSTANCE_ID     0
    /* TWI instance. */
    static const nrf_drv_twi_t m_twi = NRF_DRV_TWI_INSTANCE(TWI_INSTANCE_ID);
    /////Added for TWI/////
    
    static const nrf_drv_twi_t     m_twi_sensors = NRF_DRV_TWI_INSTANCE(TWI_SENSOR_INSTANCE);
    static m_ble_service_handle_t  m_ble_service_handles[THINGY_SERVICES_MAX];
    
    void app_error_fault_handler(uint32_t id, uint32_t pc, uint32_t info)
    {
        #if NRF_LOG_ENABLED
            error_info_t * err_info = (error_info_t*)info;
            NRF_LOG_ERROR(" id = %d, pc = %d, file = %s, line number: %d, error code = %d = %s \r\n", \
            id, pc, nrf_log_push((char*)err_info->p_file_name), err_info->line_num, err_info->err_code, nrf_log_push((char*)nrf_strerror_find(err_info->err_code)));
        #endif
        
        (void)m_ui_led_set_event(M_UI_ERROR);
        NRF_LOG_FINAL_FLUSH();
        nrf_delay_ms(5);
        
        // On assert, the system can only recover with a reset.
        #ifndef DEBUG
            NVIC_SystemReset();
        #endif
    
        app_error_save_and_stop(id, pc, info);
    }
    
    
    /**@brief Function for assert macro callback.
     *
     * @details This function will be called in case of an assert in the SoftDevice.
     *
     * @warning On assert from the SoftDevice, the system can only recover on reset.
     *
     * @param[in] line_num    Line number of the failing ASSERT call.
     * @param[in] p_file_name File name of the failing ASSERT call.
     */
    void assert_nrf_callback(uint16_t line_num, const uint8_t * p_file_name)
    {
        app_error_handler(DEAD_BEEF, line_num, p_file_name);
    }
    
    /**@brief Function for putting Thingy into sleep mode.
     *
     * @note This function will not return.
     */
    static void sleep_mode_enter(void)
    {
        uint32_t err_code;
    
        NRF_LOG_INFO("Entering sleep mode \r\n");
        err_code = m_motion_sleep_prepare(true);
        APP_ERROR_CHECK(err_code);
    
        err_code = support_func_configure_io_shutdown();
        APP_ERROR_CHECK(err_code);
        
        // Enable wake on button press.
        nrf_gpio_cfg_sense_input(BUTTON, NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_SENSE_LOW);
        // Enable wake on low power accelerometer.
        nrf_gpio_cfg_sense_input(LIS_INT1, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_SENSE_HIGH);
       
        NRF_LOG_FLUSH();
        nrf_delay_ms(5);
        // Go to system-off (sd_power_system_off() will not return; wakeup will cause a reset). When debugging, this function may return and code execution will continue.
        err_code = sd_power_system_off();
        NRF_LOG_WARNING("sd_power_system_off() returned. -Probably due to debugger being used. Instructions will still run. \r\n");
        NRF_LOG_FLUSH();
        
        #ifdef DEBUG
            if(!support_func_sys_halt_debug_enabled())
            {
                APP_ERROR_CHECK(err_code); // If not in debug mode, return the error and the system will reboot.
            }
            else
            {
                NRF_LOG_WARNING("Exec stopped, busy wait \r\n");
                NRF_LOG_FLUSH();
                
                while(true) // Only reachable when entering emulated system off.
                {
                    // Infinte loop to ensure that code stops in debug mode.
                }
            }
        #else
            APP_ERROR_CHECK(err_code);
        #endif
    }
    
    
    /**@brief Function for placing the application in low power state while waiting for events.
     */
    #define FPU_EXCEPTION_MASK 0x0000009F
    static void power_manage(void)
    {
        __set_FPSCR(__get_FPSCR()  & ~(FPU_EXCEPTION_MASK));
        (void) __get_FPSCR();
        NVIC_ClearPendingIRQ(FPU_IRQn);
    
        uint32_t err_code = sd_app_evt_wait();
        APP_ERROR_CHECK(err_code);
    }
    
    
    /**@brief Battery module data handler.
     */
    static void m_batt_meas_handler(m_batt_meas_event_t const * p_batt_meas_event)
    {
        NRF_LOG_INFO("Voltage: %d V, Charge: %d %%, Event type: %d \r\n",
                    p_batt_meas_event->voltage_mv, p_batt_meas_event->level_percent, p_batt_meas_event->type);
       
        if (p_batt_meas_event != NULL)
        {
            if( p_batt_meas_event->type == M_BATT_MEAS_EVENT_LOW)
            {
                uint32_t err_code;
    
                err_code = support_func_configure_io_shutdown();
                APP_ERROR_CHECK(err_code);
                
                // Enable wake on USB detect only.
                nrf_gpio_cfg_sense_input(USB_DETECT, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_SENSE_HIGH);
    
                NRF_LOG_WARNING("Battery voltage low, shutting down Thingy. Connect USB to charge \r\n");
                NRF_LOG_FINAL_FLUSH();
                // Go to system-off mode (This function will not return; wakeup will cause a reset).
                err_code = sd_power_system_off();
    
                #ifdef DEBUG
                    if(!support_func_sys_halt_debug_enabled())
                    {
                        APP_ERROR_CHECK(err_code); // If not in debug mode, return the error and the system will reboot.
                    }
                    else
                    {
                        NRF_LOG_WARNING("Exec stopped, busy wait \r\n");
                        NRF_LOG_FLUSH();
                        while(true) // Only reachable when entering emulated system off.
                        {
                            // Infinte loop to ensure that code stops in debug mode.
                        }
                    }
                #else
                    APP_ERROR_CHECK(err_code);
                #endif
            }
        }
    }
    
    
    /**@brief Function for handling BLE events.
     */
    static void thingy_ble_evt_handler(m_ble_evt_t * p_evt)
    {
        switch (p_evt->evt_type)
        {
            case thingy_ble_evt_connected:
                NRF_LOG_INFO(NRF_LOG_COLOR_CODE_GREEN "Thingy_ble_evt_connected \r\n");
                break;
    
            case thingy_ble_evt_disconnected:
                NRF_LOG_INFO(NRF_LOG_COLOR_CODE_YELLOW "Thingy_ble_evt_disconnected \r\n");
                NRF_LOG_FINAL_FLUSH();
                nrf_delay_ms(5);
                NVIC_SystemReset();
                break;
    
            case thingy_ble_evt_timeout:
                NRF_LOG_INFO(NRF_LOG_COLOR_CODE_YELLOW "Thingy_ble_evt_timeout \r\n");
                sleep_mode_enter();
                NVIC_SystemReset();
                break;
        }
    }
    
    
    /**@brief Function for initializing the Thingy.
     */
    static void thingy_init(void)
    {
        uint32_t                 err_code;
        m_ui_init_t              ui_params;
        m_environment_init_t     env_params;
        m_motion_init_t          motion_params;
        m_ble_init_t             ble_params;
        batt_meas_init_t         batt_meas_init = BATT_MEAS_PARAM_CFG;
    
        /**@brief Initialize the TWI manager. */
        err_code = twi_manager_init(APP_IRQ_PRIORITY_THREAD);
        APP_ERROR_CHECK(err_code);
    
        /**@brief Initialize LED and button UI module. */
        ui_params.p_twi_instance = &m_twi_sensors;
        err_code = m_ui_init(&m_ble_service_handles[THINGY_SERVICE_UI],
                             &ui_params);
        APP_ERROR_CHECK(err_code);
    
        /**@brief Initialize environment module. */
        env_params.p_twi_instance = &m_twi_sensors;
        err_code = m_environment_init(&m_ble_service_handles[THINGY_SERVICE_ENVIRONMENT],
                                      &env_params);
        APP_ERROR_CHECK(err_code);
    
        /**@brief Initialize motion module. */
        motion_params.p_twi_instance = &m_twi_sensors;
    
        err_code = m_motion_init(&m_ble_service_handles[THINGY_SERVICE_MOTION],
                                 &motion_params);
        APP_ERROR_CHECK(err_code);
    
        err_code = m_sound_init(&m_ble_service_handles[THINGY_SERVICE_SOUND]);
        APP_ERROR_CHECK(err_code);
    
        /**@brief Initialize the battery measurement. */
        batt_meas_init.evt_handler = m_batt_meas_handler;
        err_code = m_batt_meas_init(&m_ble_service_handles[THINGY_SERVICE_BATTERY], &batt_meas_init);
        APP_ERROR_CHECK(err_code);
    
        err_code = m_batt_meas_enable(BATT_MEAS_INTERVAL_MS);
        APP_ERROR_CHECK(err_code);
    
        /**@brief Initialize BLE handling module. */
        ble_params.evt_handler       = thingy_ble_evt_handler;
        ble_params.p_service_handles = m_ble_service_handles;
        ble_params.service_num       = THINGY_SERVICES_MAX;
    
        err_code = m_ble_init(&ble_params);
        APP_ERROR_CHECK(err_code);
    
        err_code = m_ui_led_set_event(M_UI_BLE_DISCONNECTED);
        APP_ERROR_CHECK(err_code);
    }
    
    
    static void board_init(void)
    {
        uint32_t            err_code;
        drv_ext_gpio_init_t ext_gpio_init;
    
        #if defined(THINGY_HW_v0_7_0)
            #error   "HW version v0.7.0 not supported."
        #elif defined(THINGY_HW_v0_8_0)
            NRF_LOG_WARNING("FW compiled for depricated Thingy HW v0.8.0 \r\n");
        #elif defined(THINGY_HW_v0_9_0)
            NRF_LOG_WARNING("FW compiled for depricated Thingy HW v0.9.0 \r\n");
        #endif
    
        static const nrf_drv_twi_config_t twi_config =
        {
            .scl                = TWI_SCL,
            .sda                = TWI_SDA,
            .frequency          = NRF_TWI_FREQ_400K,
            .interrupt_priority = APP_IRQ_PRIORITY_LOW
        };
    
        static const drv_sx1509_cfg_t sx1509_cfg =
        {
            .twi_addr       = SX1509_ADDR,
            .p_twi_instance = &m_twi_sensors,
            .p_twi_cfg      = &twi_config
        };
    
        ext_gpio_init.p_cfg = &sx1509_cfg;
        
        err_code = support_func_configure_io_startup(&ext_gpio_init);
        APP_ERROR_CHECK(err_code);
    
        nrf_delay_ms(100);
    }
    
    /////Added for TWI/////
    /**
     * @brief TWI initialization.
     */
    void twi_init (void)
    {
        ret_code_t err_code;
    
        const nrf_drv_twi_config_t twi_config = {
           .scl                = 15,//ARDUINO_SCL_PIN, //27
           .sda                = 14,//ARDUINO_SDA_PIN, //26
           .frequency          = 0x06680000UL,//NRF_DRV_TWI_FREQ_400K,//<-- same as Bjorn, old -->NRF_DRV_TWI_FREQ_100K,
           .interrupt_priority = 2,//APP_IRQ_PRIORITY_HIGH,
           .clear_bus_init     = false
        };
    
        err_code = nrf_drv_twi_init(&m_twi, &twi_config, NULL, NULL);
        APP_ERROR_CHECK(err_code);
    
        nrf_drv_twi_enable(&m_twi);
    }
    /////Added for TWI/////
    
    /**@brief Application main function.
     */
    int main(void)
    {
        uint32_t err_code;
        err_code = NRF_LOG_INIT(NULL);
        APP_ERROR_CHECK(err_code);
    
        NRF_LOG_INFO(NRF_LOG_COLOR_CODE_GREEN"===== Thingy started! =====\r\n");
        
        // Initialize.
        APP_SCHED_INIT(SCHED_MAX_EVENT_DATA_SIZE, SCHED_QUEUE_SIZE);
        err_code = app_timer_init();
        APP_ERROR_CHECK(err_code);
    
        board_init();
        thingy_init();
    	
    		/////Added for TWI/////
    		twi_init();
    		err_code = ADXL375_I2C_init(&m_twi);
        APP_ERROR_CHECK(err_code);
    		// Data rate set to 1010, which is 100 Hz
        ADXL375_I2C_data_rate_set(0x0A);
        ADXL375_I2C_offsets_set(0,0,0);
    
        int16_t x_val;
        int16_t y_val;
        int16_t z_val;
    		ADXL375_I2C_acc_data_read(&x_val,&y_val,&z_val);
        NRF_LOG_INFO("ACC DATA:  X: %d  Y: %d  Z: %d  \n", x_val ,y_val, z_val);
    		/////Added for TWI/////
    
        for (;;)
        {
            app_sched_execute();
            
            if (!NRF_LOG_PROCESS()) // Process logs
            { 
                power_manage();
            }
        }
    }
    

    ADXL375_I2C.h

    #include <stdbool.h>
    #include "SEGGER_RTT.h"
    #include "nrf_drv_twi.h"
    #include "nrf_delay.h"
    #include "app_util_platform.h"
    #include "app_error.h"
    //#include "boards.h"
    #include <stdint.h>
    #include "ADXL375_I2C.h" 
    
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    //#include "nrf_log_default_backends.h"
    
    static const nrf_drv_twi_t * p_twi;
    
     ret_code_t ADXL375_I2C_register_write(uint8_t slave_addr,uint8_t * pdata, uint32_t bytes)
    {
        /*The way to write to a register is in the following manner (I have skipped the ACKNOWLEDMENT, which is done for every byte transmitted):
           --> ((START condition: High-to-low transition while SCL is high (otherwise the bits are read while clock is low) ))
           --> Write address of sensor/slave on the SDA bus
           --> Set the SDA bus low for writing --> (0) (single bit/one clock cycle)
           --> Write the register address
           --> Write the data
           --> (STOP condition)
        */
        ret_code_t ret_code;
        ret_code = nrf_drv_twi_tx(p_twi, slave_addr, pdata, bytes , false);
        
        return ret_code;
    }
    
     ret_code_t ADXL375_I2C_register_read(uint8_t slave_addr, uint8_t reg_addr,  uint8_t * pdata, uint32_t bytes)
    {   
        /*The way to read a register is in the following manner (I have skipped the ACKNOWLEDMENT, which is done for every byte transmitted):
           --> Write address of sensor/slave on the SDA bus
           --> Set the SDA bus low for writing (0) (single bit/one clock cycle)
           --> Write the register address
           --> (STOP condition)
           --> (START condition)
           --> Write slave address
           --> Set the SDA bus high for reading --> (1) (single bit/one clock cycle)
           --> Now the slave will send the data from the register and master will listen and ACKNOWLEDGE
           -->
        */
        ret_code_t ret_code;
        
        ret_code = nrf_drv_twi_tx(p_twi,slave_addr, &reg_addr,1,false);
        
        if(ret_code != NRF_SUCCESS)
        {
            return ret_code;
        }
        
        ret_code = nrf_drv_twi_rx(p_twi,slave_addr, pdata, bytes);
        
        return ret_code;
    }
    
     ret_code_t ADXL375_I2C_init(const nrf_drv_twi_t * p_twi_instance)
    {
        ret_code_t ret_code;
        
        p_twi = p_twi_instance;
    
        uint8_t tx_data[2] = {POWER_CTL, 0x08}; 
    
        //The way i2c works is by sending one byte at the time, and the sensor will acknowlegdge for each byte
        //Here we are sending an array of 16 bits (2 bytes), the way to write to a register is to write the register address first
        //then write the data. Since the size is also sent, it knows how many bytes from that address it should write
    
        // Put the ADXL375 in Measurement Mode by writing 0x08 to the POWER_CTL Register
        ret_code = ADXL375_I2C_register_write(ADXL375_ADDR, tx_data, sizeof(tx_data) );
        
        return ret_code;
    }
    
     void ADXL375_I2C_offsets_set(uint8_t x_offset, uint8_t y_offset,uint8_t z_offset)
    {
    
        //  The value stored in the offset registers is automat-ically added to the acceleration data
        // However, there is actually three offset registers: OFSX, OFSY and OFSZ, I guess if you write exceed the first register
        // OFSX, it will automatically write to the next registers
        ret_code_t ret_code;
        
        uint8_t tx_data[4] = {OFSX, x_offset,y_offset,z_offset};
        
        ret_code = ADXL375_I2C_register_write(ADXL375_ADDR, tx_data, sizeof(tx_data));
        
        APP_ERROR_CHECK(ret_code);
        
        uint8_t rx_data[3] = {x_offset, y_offset, z_offset}; // {0, 0, 0};
        
        ret_code = ADXL375_I2C_register_read(ADXL375_ADDR, OFSX, rx_data, sizeof(rx_data)); 
        
        APP_ERROR_CHECK(ret_code);
        
        
        if( rx_data[0] == x_offset &&
            rx_data[1] == y_offset &&
            rx_data[2] == z_offset  )
        {
            NRF_LOG_INFO("Setting Offsets Sucessful! \n");
        }
        else
        {
            NRF_LOG_INFO("Setting Offsets Failed! \n");
        }
    }
    
     void ADXL375_I2C_data_rate_set(uint8_t rate)
    {
        ret_code_t ret_code;
        // The BW_RATE register (4 bits) controls the data rate
        // More info can be found here: https://www.analog.com/media/en/technical-documentation/data-sheets/ADXL375.PDF
        uint8_t tx_data[2] = {BW_RATE,rate};
        
        ret_code = ADXL375_I2C_register_write(ADXL375_ADDR,tx_data, sizeof(tx_data));
        
        APP_ERROR_CHECK(ret_code);
        
        uint8_t rx_data = 0x00;
        //This read is just for control (checking if write was successful)
        ret_code = ADXL375_I2C_register_read(ADXL375_ADDR, BW_RATE, &rx_data,1);
         
        APP_ERROR_CHECK(ret_code);
        
        if(rx_data == rate)
        {
            NRF_LOG_INFO("Setting Data rate Sucessful! \n");
        }
        else
        {
            NRF_LOG_INFO("Setting Data rate Failed! \n");
        }
    }
    
    void ADXL375_I2C_acc_data_read(int16_t * x_value, int16_t * y_value, int16_t * z_value)
    {
        ret_code_t ret_code;
        
        uint8_t rx_buffer[6];
        
        // The DATAX0 register contains the acceleration data
        ret_code = ADXL375_I2C_register_read(ADXL375_ADDR, DATAX0, rx_buffer, sizeof(rx_buffer)); 
        
        APP_ERROR_CHECK(ret_code);
        
        // The reason it is done in this manner is because the buffer field 0 and 1, represents the x value
        // and you must left shift the second buffer, so it occupies the 8 MSB bits (same logic with y and z value)
        *x_value = (rx_buffer[1]<<8)|rx_buffer[0];
        *y_value = (rx_buffer[3]<<8)|rx_buffer[2];
        *z_value = (rx_buffer[5]<<8)|rx_buffer[4];
    }
    
    
    

  • Hello Simon,

    Thank you very much for sharing example code.

    however i'm able to communicate with my sensor now by changing my mysensor_i2c_write() function.

    int8_t mysensor_i2c_write(uint8_t address, const uint8_t* data, uint16_t count)
    {
    m_drv_mysensor.p_drv_mysensor_cfg->twi_addr to match Thingy52 structure
      // int8_t err = nrf_drv_twi_tx(&i2c_instance, address, data, (uint8_t)count, false);
       int8_t err = nrf_drv_twi_tx(m_drv_mysensor.p_drv_mysensor_cfg->p_twi_instance, m_drv_mysensor.p_drv_mysensor_cfg->twi_addr, data, (uint8_t)count, false);
       return err;
    }

    with this modification now i'm able to read write sensor registers.

    Can you please explain how timer timeout and Bluetooth configuration service is handled in Thingy FW architecture. for example how static void temperature_timeout_handler(void * p_context) function in m_environment.c will pulls data from       err_code = drv_humidity_sample(); and how it is linked to  err_code = drv_hts221_temperature_get(&t_out);.

    Thanks for your time.

  • In m_environment.c an app timer is created and started, it is a repeated timer that will invoke the function temperature_timeout_handler(..)-->drv_humidity_sample(..) every 2000 ms. The function drv_humidity_sample(..) will set the ONE_SHOT bit of the CTRL_REG2 register of the HTS221 sensor, which will start single acquisition of the temperature and store the values in the output registers (Read more in the HTS221 datasheet). So in short, the temperature output registers will be updated every 2 seconds.

    In order to read the temperature output registers (TEMP_OUT_L_REG and TEMP_OUT_H_REG), the function drv_hts221_temperature_get(..) needs to be called, which is done in the following manner: drv_humidity_evt_handler(..)-->drv_humidity_temp_get(..)..drv_hts221_temperature_get(..). 

    The function drv_humidity_evt_handler(..) is set through drv_humidity_init(..) inside m_environment.c, and in drv_humidity.c you can see that it is set equal  to m_drv_humidity.evt_handler.

    That function (m_drv_humidity.evt_handler) is called from gpiote_evt_handler(..)-->gpiote_evt_sceduled(..) in drv_humidity.c, and through nrf_drv_gpiote_in_init(..) it is configured to be called very time pin 24 toggles. So in short, the output registers will be read every time pin 24 toggles.

    Hopefully I understood it correctly and explained it in a somehow understandable manner.

    Best regards,

    Simon.

Reply
  • In m_environment.c an app timer is created and started, it is a repeated timer that will invoke the function temperature_timeout_handler(..)-->drv_humidity_sample(..) every 2000 ms. The function drv_humidity_sample(..) will set the ONE_SHOT bit of the CTRL_REG2 register of the HTS221 sensor, which will start single acquisition of the temperature and store the values in the output registers (Read more in the HTS221 datasheet). So in short, the temperature output registers will be updated every 2 seconds.

    In order to read the temperature output registers (TEMP_OUT_L_REG and TEMP_OUT_H_REG), the function drv_hts221_temperature_get(..) needs to be called, which is done in the following manner: drv_humidity_evt_handler(..)-->drv_humidity_temp_get(..)..drv_hts221_temperature_get(..). 

    The function drv_humidity_evt_handler(..) is set through drv_humidity_init(..) inside m_environment.c, and in drv_humidity.c you can see that it is set equal  to m_drv_humidity.evt_handler.

    That function (m_drv_humidity.evt_handler) is called from gpiote_evt_handler(..)-->gpiote_evt_sceduled(..) in drv_humidity.c, and through nrf_drv_gpiote_in_init(..) it is configured to be called very time pin 24 toggles. So in short, the output registers will be read every time pin 24 toggles.

    Hopefully I understood it correctly and explained it in a somehow understandable manner.

    Best regards,

    Simon.

Children
  • Hello Simon,

    Thank you very much for your detailed explanation.

    Do you have any sensor on Thingy that does not have an INTERRUPT pin , curious to know how do you handle such situation?. Just use another timer based interrupt ? Basically my sensor does not have an interrupt pin and  i just want to send a start measurement command(similar to drv_humidity_sample(..) function ) and then wait for 1 second or so before reading the sensor data(similar to drv_humidity_temp_get(..)). and then i have created a custom characteristics on m_environment.c  which will take this value and pass it to ble_tes_mysensor_set(..) in ble_tes.c file.

    Thanks

  • I am not too familiar with the hts221 sensor, but I think you can read the temperature output registers without using the interrupt. If you look at the image below, you can see that HTS_INT (P0.24) is connected to DRDY, and according to the hts221 datasheet the DRDY signal will go high after new data is available, and goes low after the registers are read. 

    However, I don't think there is anything stopping you from reading the output register without checking the DRDY line, you just risk reading the same data over again. If there is a small delay between the call to drv_humidity_sample(..) and  drv_humidity_temp_get(..) I think this should be fine.

    Be aware that I might be wrong. Test it out, and give me an update of the result.

    Best regards,

    Simon

  • Hello Simon,

    sorry for the confusion, i'm not using hts221 sensor.  i have a sensor which does not have DRDY/INT pin. it has only i2c interface and i'm able to read/write registers  through i2c.  i was thinking some kind of event scheduler will mimic DRDY/INT functionality and hence i can use rest of the API's similar to hts221 sensor.

    Thanks

  • Hello Simon,

    here is what i'm attempting to do.

    //i have written a drv_mysensor.c and created following event scheduler.

    APP_TIMER_DEF(m_idle_timer_id);

    void reset_idle_timer(void)
    {
        uint32_t err_code;

        // Stop timer
        err_code = app_timer_stop(m_idle_timer_id);
        APP_ERROR_CHECK(err_code);

        // Start timer
           err_code = app_timer_start(m_idle_timer_id,APP_TIMER_TICKS(1000),NULL);
        APP_ERROR_CHECK(err_code);
    }

    /**@brief event handler
     */
    void reset_idle_timer_event(void * p_event_data, uint16_t event_size)
    {
        reset_idle_timer();
        // Data ready
        m_drv_pm.evt_handler(DRV_MYSENSOR_EVT_DATA);
    }


    /**@brief event scheduler
     */
    static void idle_timer_evt_scheduler()
    {
        uint32_t err_code;

        // Reset idle timer
            err_code = app_sched_event_put(NULL, NULL, reset_idle_timer_event);
            APP_ERROR_CHECK(err_code);

    }

    // and  i'm expecting following function to be called from m_environment.c file every 1 second

    /**@brief My sensor event handler.
     */
    static void drv_mysensor_evt_handler(drv_mysensor_evt_t event)
    {
        uint32_t err_code;



        if (event == DRV_MYSENSOR_EVT_DATA)
        {
            ble_tes_mysensor_t dat;
     
            float value = drv_mysensor_get();
        
            if (m_get_mysensor== true)
            {
                err_code = ble_tes_mysensor_set(&m_tes, &value);
                APP_ERROR_CHECK(err_code);
                m_get_mysensor = false;
            }
        }
        else
        {
            APP_ERROR_CHECK_BOOL(false);
        }
    }

    // but my problem is i'm not hitting following case statement which i have added in  m_environment.c file, because of which idle_timer_evt_scheduler() function is not started.

            case BLE_TES_EVT_NOTIF_MYSENSOR:
                NRF_LOG_DEBUG("tes_evt_handler: BLE_TES_EVT_NOTIF_MYSENSOR: %d\r\n", p_tes->is_mysensor_notif_enabled);
                if (p_tes->is_mysensor_notif_enabled)
                {
                    err_code = mysensor_start();
                    APP_ERROR_CHECK(err_code);
                }
                else
                {
                    err_code = mysensor_stop(p_tes->is_mysensor_notif_enabled == false);
                    APP_ERROR_CHECK(err_code);
                }
                break;

  • Why are you starting and stopping the timer? Isn't it possible to create a repeated timer that fires every 1 second?

    I have not tested this myself, and I am not sure if this is the best way to go about it, but here I will present an approach that might work

    In drv_mysensor.c

    • Create an enable function ( similar to drv_humidity_enable(..) where you create and start a repeated timer that triggers idle_timer_evt_scheduler()
    • Inside idle_timer_evt_scheduler() run app_sched_event_put(NULL, NULL, reset_idle_timer_event)
    • In the function reset_idle_timer_event() only run m_drv_pm.evt_handler(DRV_MYSENSOR_EVT_DATA);
    • Create in init function (similar to drv_humidity_init(..) )
      • Here you can create the repeated timer (not start it)
      • In addition you set your event handler m_drv_pm.evt_handler here

    In m_environment.c

    • Create the event handler (similar to humidity_timeout_handler() )
    • Run the init and enable function that you created in drv_mysensor.c

    Best regards,

    Simon

Related