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

I2C slave implementation

Dear community,

I need to operate a BM833A as I2C slave and to do so, I performed the following steps:

I modfied my sdk_config.h and set TWIS_ENABLED and NRFX_TWIS_ENABLED identically following hint at:

devzone.nordicsemi.com/.../merge-twis-i2c-slave-example-in-ble_template-project

(Either completely remove the legacy (non NRFX_...) for TWI(S) or set them equal to the NRFX_TWIS_... definitions.)

// <e> NRFX_TWIS_ENABLED - nrfx_twis - TWIS peripheral driver
//==========================================================
#ifndef NRFX_TWIS_ENABLED
#define NRFX_TWIS_ENABLED 1
//RH: changed from 0 to 1
#endif
// <q> NRFX_TWIS0_ENABLED  - Enable TWIS0 instance
 

#ifndef NRFX_TWIS0_ENABLED
#define NRFX_TWIS0_ENABLED 1
//RH: changed from 0 to 1
#endif

// <q> NRFX_TWIS1_ENABLED  - Enable TWIS1 instance
 

#ifndef NRFX_TWIS1_ENABLED
#define NRFX_TWIS1_ENABLED 0
#endif

// <q> NRFX_TWIS_ASSUME_INIT_AFTER_RESET_ONLY  - Assume that any instance would be initialized only once
 

// <i> Optimization flag. Registers used by TWIS are shared by other peripherals. Normally, during initialization driver tries to clear all registers to known state before doing the initialization itself. This gives initialization safe procedure, no matter when it would be called. If you activate TWIS only once and do never uninitialize it - set this flag to 1 what gives more optimal code.

#ifndef NRFX_TWIS_ASSUME_INIT_AFTER_RESET_ONLY
#define NRFX_TWIS_ASSUME_INIT_AFTER_RESET_ONLY 0
#endif

// <q> NRFX_TWIS_NO_SYNC_MODE  - Remove support for synchronous mode
 

// <i> Synchronous mode would be used in specific situations. And it uses some additional code and data memory to safely process state machine by polling it in status functions. If this functionality is not required it may be disabled to free some resources.

#ifndef NRFX_TWIS_NO_SYNC_MODE
#define NRFX_TWIS_NO_SYNC_MODE 0
#endif

// <o> NRFX_TWIS_DEFAULT_CONFIG_ADDR0 - Address0
#ifndef NRFX_TWIS_DEFAULT_CONFIG_ADDR0
#define NRFX_TWIS_DEFAULT_CONFIG_ADDR0 0x01
//RH: changed from 0 to 0x01
#endif

// <o> NRFX_TWIS_DEFAULT_CONFIG_ADDR1 - Address1
#ifndef NRFX_TWIS_DEFAULT_CONFIG_ADDR1
#define NRFX_TWIS_DEFAULT_CONFIG_ADDR1 0
#endif

// <o> NRFX_TWIS_DEFAULT_CONFIG_SCL_PULL  - SCL pin pull configuration
 
// <0=> Disabled
// <1=> Pull down
// <3=> Pull up

#ifndef NRFX_TWIS_DEFAULT_CONFIG_SCL_PULL
#define NRFX_TWIS_DEFAULT_CONFIG_SCL_PULL 3
//RH: enable pullup, see i2cslave from SBRLE
#endif

// <o> NRFX_TWIS_DEFAULT_CONFIG_SDA_PULL  - SDA pin pull configuration
 
// <0=> Disabled
// <1=> Pull down
// <3=> Pull up

#ifndef NRFX_TWIS_DEFAULT_CONFIG_SDA_PULL
#define NRFX_TWIS_DEFAULT_CONFIG_SDA_PULL 3
//RH: enable pullup, see i2cslave from SBRLE
#endif

// <o> NRFX_TWIS_DEFAULT_CONFIG_IRQ_PRIORITY  - Interrupt priority
 
// <0=> 0 (highest)
// <1=> 1
// <2=> 2
// <3=> 3
// <4=> 4
// <5=> 5
// <6=> 6
// <7=> 7

#ifndef NRFX_TWIS_DEFAULT_CONFIG_IRQ_PRIORITY
#define NRFX_TWIS_DEFAULT_CONFIG_IRQ_PRIORITY 6
#endif

I added to the segger-embedded-studio (SES) project file:

<folder Name="nRF_Drivers">
...
      <file file_name="../../../../../../modules/nrfx/drivers/src/nrfx_twis.c" />

<folder>

Then I added code to main.c following:

https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.sdk5.v12.0.0%2Fhardware_driver_twis_slave.html

I created a timer that executes continuously every 0.5s (function of that could be veryfied by LED blink).

Then I added I2C call to nrf_drv_twis_tx_prepare, hoping to get a transmission.

main.c modifications:

#include "nrf_drv_twis.h"

APP_TIMER_DEF(m_bsp_tmr);

//twi
/* TWI instance ID. */
#if TWIS0_ENABLED
#define TWIS_INSTANCE_ID     0
#endif

/* TWI instance. */
static const nrf_drv_twis_t m_twi = NRF_DRV_TWIS_INSTANCE(TWIS_INSTANCE_ID);

/**
 * @brief TWI initialization.
 */
void twi_init (void)
{
    ret_code_t err_code;

    const nrf_drv_twis_config_t twi_config = {
       .scl                = 27,
       .sda                = 26,
       .interrupt_priority = APP_IRQ_PRIORITY_HIGH
    };

    //err_code = nrf_drv_twis_init(&m_twi, &twi_config, twis_event_handler);
    err_code = nrf_drv_twis_init(&m_twi, &twi_config, NULL);
    APP_ERROR_CHECK(err_code);

    nrf_drv_twis_enable(&m_twi);
}

/**@brief Handle events from button timer.
 *
 * @param[in]   p_context   parameter registered in timer start function.
 */
static void m_timer_handler(void * p_context)
{
     uint16_t count=0;
     char txbuffer[32];
     strcpy(txbuffer, "ready!\n");
     uint16_t len=strlen(txbuffer);
        
     NRF_LOG_INFO("Sending Data: %d.", len);
     ret_code_t err_code = nrf_drv_twis_tx_prepare(&m_twi, txbuffer, len);
     APP_ERROR_CHECK(err_code);
     while(nrf_drv_twis_is_pending_tx(&m_twi) && count<350)
     {
         nrf_delay_ms(1);
         count++;
     }
     NRF_LOG_INFO("Loop hops: %d.", count);
}

int main(void)
{
    // Initialize.
    log_init();
    twi_init();
  

    uint32_t timeout_ticks = APP_TIMER_TICKS(500);
    ret_code_t err_code = app_timer_create(&m_bsp_tmr, APP_TIMER_MODE_REPEATED, m_timer_handler);
    APP_ERROR_CHECK(err_code);
    app_timer_start(m_bsp_tmr, timeout_ticks, NULL);

    // Start execution.
    printf("BLE UART central example started.\r\n");
    NRF_LOG_INFO("BLE UART central example started.");

    // Enter main loop.
    for (;;)
    {
        idle_state_handle();
    }

}

In SES-debugger, I get:

<info> app_timer: RTC: initialized.
<info> app: BLE UART central example started.
<info> app: Sending Data: 7.
<info> app: Loop hops: 0.
<info> app: Sending Data: 7.
<info> app: Loop hops: 0.

(...)

But measuring shows:

1) SCL is provided with 115k clock from master

2) SDA-PIN shows no activity

Any suggestions on corrections?

Best regards,

Richard

  • Hello Richard,

    I need to operate a BM833A as I2C slave and to do so, I performed the following steps:

    I modfied my sdk_config.h and set TWIS_ENABLED and NRFX_TWIS_ENABLED identically following hint at:

    Any suggestions on corrections?

    If I have understood your intentions correctly, you intend for the nRF to be the TWI master, and the BM833A to be the TWI slave - is this correct?
    If so, you will need to setup the nRF to use the TWIM driver, for it to be in the master configuration. Currently, it seems that you have configured your nRF to be a TWI slave, which will need a TWI Master to be present on the bus, for it to work.
    If you wish to make the nRF the bus master, you could start out by taking a look at the TWI Scanner example - if the scanner example functions as expected, it will confirm that the wiring and hardware connections between the master and slave device is correct. Then, you can move over to implementing the TWIM driver's behavior.

    (Either completely remove the legacy (non NRFX_...) for TWI(S) or set them equal to the NRFX_TWIS_... definitions.)

    You should always completely remove the legacy defines from the sdk_config when you intend to use the nrfx driver, because the nrfx enable will be overwritten by the apply_old_config file if the legacy definitions are left defined (their defined value does not matter).

    1) SCL is provided with 115k clock from master

    Where is this clock being supplied from? The clock should be supplied by the bus master, but it seems to me that your bus currently does not have a master, so I am not sure what is happening here. Where else in your application is the definition of the SCL pin used?

    For future reference, please use the Insert -> Code option when sharing code here on DevZone.

    Best regards,
    Karl

  • Dear Karl,

    If I have understood your intentions correctly, you intend for the nRF to be the TWI master, and the BM833A to be the TWI slave - is this correct?

    Sorry, that is not the case.

    The BM833A contains a nordic 52811 chip. This platform shall act as I2C (TWI) slave.Programming this is the issue of my inquiry here.

    The I2C master (also supplying the clock at 115kB) is on another hardware platform and has been functionally tested and shown proof of work with other I2C slaves, independantly and before.

    What you see is the code that I used to drive the BM833A (52811 nordic) hardware to send data as I2C slave to that other hardware platform.

    Currently, there are no other slaves connected to the I2C-bus, but the plan is (once the single slave works) to connect five more BM833A slaves to the I2C-bus.

    As you suggested, I will try to remove legacy mode config and report back.

    Thank you,

    Richard

  • Dear Karl,

    I tried removing legacy defines

    removed all sdk config from:

    // <e> TWIS_ENABLED - nrf_drv_twis - TWIS peripheral driver - legacy layer

    (...until first...)

    // </e>

    This results in a broken config.

    Compiling, I get (in nrfx_twis.h):

    ‘NRF_TWISTWIS_INSTANCE_ID’ undeclared here (not in a function); did you mean ‘NRF_DRV_TWIS_INSTANCE’?

    in definition of macro ‘NRFX_CONCAT_2_’

    in expansion of macro ‘NRFX_CONCAT_2’

    in expansion of macro ‘NRFX_TWIS_INSTANCE’

    in expansion of macro ‘NRF_DRV_TWIS_INSTANCE’

    (...plus second, similar error)

    The referenced Code in nrfx_twis.h:

    /** @brief Macro for creating a TWIS driver instance. */
    #define NRFX_TWIS_INSTANCE(id)                               \
    {                                                            \
        .p_reg        = NRFX_CONCAT_2(NRF_TWIS, id),             \
        .drv_inst_idx = NRFX_CONCAT_3(NRFX_TWIS, id, _INST_IDX), \
    }

    The macro cannot be removed, as it is required in the main.c code:

    static const nrf_drv_twis_t m_twi = NRF_DRV_TWIS_INSTANCE(TWIS_INSTANCE_ID);

    So how could I succeed in removing?

    Best regards,

    Richard

  • Hello Richard,

    RichardHdrd said:

    Sorry, that is not the case.

    The BM833A contains a nordic 52811 chip. This platform shall act as I2C (TWI) slave.Programming this is the issue of my inquiry here.

    Thank you for clarifying this. 

    RichardHdrd said:
    What you see is the code that I used to drive the BM833A (52811 nordic) hardware to send data as I2C slave to that other hardware platform.

    It would also be good if you could share your entire main.c code, either through the Insert -> Code option, or by uploading the main.c file as a whole. In your current code, I see no mention of your twis_event_handler, except for in the initialization function - so I only know it exists, but not its contents.
    It might also be helpful for you to take a look at the eeprom_simulator.c file of the TWI master with TWIS slave example from the SDK. This file demonstrates how to implement a TWI slave on the nRF side.

    RichardHdrd said:
    Currently, there are no other slaves connected to the I2C-bus, but the plan is (once the single slave works) to connect five more BM833A slaves to the I2C-bus.

    This should be no problem, since the bus master will address each slave separately.

    Could you confirm for me which SDK you are using?
    I see now that one of the API reference links you provided is for SDK v12, which does not contain the nrfx drivers.

    RichardHdrd said:

    The macro cannot be removed, as it is required in the main.c code:

    static const nrf_drv_twis_t m_twi = NRF_DRV_TWIS_INSTANCE(TWIS_INSTANCE_ID);

    So how could I succeed in removing?

    Yes, you must still declare an TWIS instance, but you may do so using the NRFX version of the macro: NRFX_TWIS_INSTANCE.
    Please replace this macro call with the nrfx version, and see if it resolves the broken build issue.

    Best regards,
    Karl

  • Dear Karl,

    Either forum or my browser are not allowing me to reply on a lower thread level (no message buttons there), so I hope you still can see my post at top level here:



    For a first effort I took the following example (and also telling, I use SDK 17.0.2, Segger Embedded Studio, S140 on a BM833A (52811 nordic) hardware), for a first test using synchronous mode of the following given example:

    infocenter.nordicsemi.com/index.jsp

    Following your suggestion, I managed to remove all legacy driver stuff with help of given page:

    infocenter.nordicsemi.com/index.jsp

    This results in the following relevant code, which compiles and runs with continuous debug-output:

    (...)
    #include "nrfx_twis.h"
    
    (...)
    APP_TIMER_DEF(m_bsp_tmr);
    
    (...)
    static const nrfx_twis_t m_twi = NRFX_TWIS_INSTANCE(0);
    
    /**
     * @brief TWI initialization.
     */
    void twi_init (void)
    {
        ret_code_t err_code;
    
        const nrfx_twis_config_t twi_config = {
           .addr               = {7,0},
           .scl                = 27,
           .scl_pull           = 3, /*=> Pull up*/ 
           .sda                = 26,
           .sda_pull           = 3, /*=> Pull up*/ 
           .interrupt_priority = APP_IRQ_PRIORITY_HIGH
        };
    
        //err_code = nrf_drv_twis_init(&m_twi, &twi_config, twis_event_handler);
        err_code = nrfx_twis_init(&m_twi, &twi_config, NULL);
        APP_ERROR_CHECK(err_code);
    
        nrfx_twis_enable(&m_twi);
    }
    
    
    /**@brief Function for initializing the timer. */
    static void timer_init(void)
    {
        ret_code_t err_code = app_timer_init();
        APP_ERROR_CHECK(err_code);
    }
    
    
    /**@brief Function for initializing the nrf log module. */
    static void log_init(void)
    {
        ret_code_t err_code = NRF_LOG_INIT(NULL);
        APP_ERROR_CHECK(err_code);
    
        NRF_LOG_DEFAULT_BACKENDS_INIT();
    }
    
    /**@brief Function for initializing power management.
     */
    static void power_management_init(void)
    {
        ret_code_t err_code;
        err_code = nrf_pwr_mgmt_init();
        APP_ERROR_CHECK(err_code);
    }
    
    /**@brief Function for handling the idle state (main loop).
     *
     * @details Handles any pending log operations, then sleeps until the next event occurs.
     */
    static void idle_state_handle(void)
    {
        if (NRF_LOG_PROCESS() == false)
        {
            nrf_pwr_mgmt_run();
        }
    }
    
    /**@brief Handle events from button timer.
     *
     * @param[in]   p_context   parameter registered in timer start function.
     */
    static void m_timer_handler(void * p_context)
    {
         uint16_t count=0;
         char txbuffer[32];
         strcpy(txbuffer, "ready!\r\n");
         uint16_t len=strlen(txbuffer);
              
         if(len>0)
         {
           NRF_LOG_INFO("Sending Data: %d.", len);
           ret_code_t err_code = nrfx_twis_tx_prepare(&m_twi, txbuffer, len);   //also tried len+1
           APP_ERROR_CHECK(err_code);
    
           while (nrfx_twis_is_busy(&m_twi) && count<35000)
           {
               nrf_delay_us(10);
               count++;
           }
           NRF_LOG_INFO("Loop hops: %d.", count);                               //always reports 0
           //memcpy((void*)txbuffer, 0, 32);
         }
    }
    
    int main(void)
    {
        log_init();
        twi_init();
        //enable obtaining ms,us from CPU since firmware start
        timer_init();
        power_management_init();
        
        uint32_t timeout_ticks = APP_TIMER_TICKS(500);
        ret_code_t err_code = app_timer_create(&m_bsp_tmr, APP_TIMER_MODE_REPEATED, m_timer_handler);
        APP_ERROR_CHECK(err_code);
        app_timer_start(m_bsp_tmr, timeout_ticks, NULL);
    
        // Enter main loop.
        for (;;)
        {
            idle_state_handle();
        }
    }


    But still, I get no reaction on the I2C data line, while clock line is operating with master-bus speed of 115k.

    Best regards,

    Richard

Related