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

Parents
  • 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

Reply
  • 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

Children
  • Hello Richard,

    RichardHdrd said:
    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:

    No problem, I still see your comments here.
    I have heard other users mention this happening earlier. The issue is already reported internally, so we will examine this more closely and implement a fix, sorry for the inconvenience.
    A 'workaround' to this is to click on the timestamp of the comment you would like to reply to, this should make the buttons appear again.

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

    What is your TWI master doing at this time? Is it initiating any transfers, or trying to read out a section of the TWI slave's memory?
    The TWI slave can not initiate a transfer, this has to be done by the TWI master. The TWI slave can only do as instructed by the master. Is your TWI master attempting to read from a particular register, for example?
    If you look into the main.c of the TWI master and TWI simulated EEPROM slave example I referenced in my previous comment, you can see how TWI master communication can be initiated. 

    RichardHdrd said:
    for a first test using synchronous mode of the following given example:

    I notice in your code that you are in fact not following the instructions / example of the TWIS driver in synchronous mode, that you reference. Why is that? Please try this again, with the full example (the TWIS slave should also be prepared for RX events, and you will need to check whether the driver is already busy when using it in synchronous mode.

    Best regards,
    Karl

Related