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

Problem sending data via TWI - Fatal error

I am trying to communicate with a SH1106 OLED display using TWI and the popular u8g2 display controller library.

I've verified that the display works without problems on an Arduino and I now try to mimic the exact same behavior on the nRF52. In order to be sure the TWI is correctly configured I first search for the component based on the code of the TWI scanner. This is successful and the OLED device is found at the well-known ID 0x3c. Then I am using the u8g2 library to communicate with the display.

Basically you have to implement some cases in a user defined function and register this as handler (u8x8_HW_com_nrf52832 and u8g2_nrf_gpio_and_delay_cb).

I think the background here is not too important for the problem at hand, as the error happens from within nRF code and not in the library, so we know exactly which arguments are passed to the nRF API.

The problem is that on the first sending of data the nrf_drv_twi_tx() functions throws a Fatal Error thus crashing the application.

I've printed every buffer which is handled by the function to illustrate the problem and would like to understand what's going wrong.

I have based my experiments on the TWI scanner example and use the TWI in *blocking* mode to keep it simple at first.

The code to find the device is

#define TWI_ADDRESSES      127

#define OLED_I2C_PIN_SCL 27
#define OLED_I2C_PIN_SDA 26
#define OLED_ADDR          (0x3C)

#define TWI_INSTANCE_ID     0
static const nrf_drv_twi_t m_twi = NRF_DRV_TWI_INSTANCE(TWI_INSTANCE_ID);

void twi_init (void)
{
    ret_code_t err_code;

    const nrf_drv_twi_config_t twi_oled_config = {
       .scl                = OLED_I2C_PIN_SCL,
       .sda                = OLED_I2C_PIN_SDA,
       .frequency          = NRF_DRV_TWI_FREQ_100K,
       .interrupt_priority = APP_IRQ_PRIORITY_HIGH,
       .clear_bus_init     = false
    };

    err_code = nrf_drv_twi_init(&m_twi, &twi_oled_config, NULL, NULL);
    APP_ERROR_CHECK(err_code);

    nrf_drv_twi_enable(&m_twi);
}


uint8_t u8g2_nrf_gpio_and_delay_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)

{
    switch(msg)
    {   
        case U8X8_MSG_DELAY_MILLI:
            NRF_LOG_INFO("nrf_delay_ms(%d)", arg_int);
            nrf_delay_ms(arg_int);
            break;

        case U8X8_MSG_DELAY_10MICRO:
            NRF_LOG_INFO("nrf_delay_us(%d)", 10*arg_int);
            nrf_delay_us(10*arg_int);
            break;
        
        default:
            u8x8_SetGPIOResult(u8x8, 1); // default return value
            break;  
    }
    return 1;
}


uint8_t u8x8_HW_com_nrf52832(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
    uint8_t *data;
    bool res = false;
    ret_code_t err_code;    
    static uint8_t buffer[32];
    static uint8_t buf_idx;
    switch(msg)
    {
        case U8X8_MSG_BYTE_SEND:
            {
            data = (uint8_t *)arg_ptr;      
            while( arg_int > 0 )
              {
                buffer[buf_idx++] = *data;
                data++;
                arg_int--;
              }      
            }
            
            break;  
        case U8X8_MSG_BYTE_INIT:            
            break;
        case U8X8_MSG_BYTE_SET_DC: //Pin Data/Comand           
            break;
        case U8X8_MSG_BYTE_START_TRANSFER:                    
            buf_idx = 0;
            break;
      case U8X8_MSG_BYTE_END_TRANSFER:    
           NRF_LOG_INFO("Dumping send buffer of len %d ('buf_idx')", buf_idx);
           NRF_LOG_HEXDUMP_INFO(buffer, buf_idx);
           uint8_t addr = u8x8_GetI2CAddress(u8x8);
           NRF_LOG_INFO("Now calling 'nrf_drv_twi_tx(&m_twi, %d, buffer, buf_idx, false)'", addr);
           
           err_code = nrf_drv_twi_tx(&m_twi, addr, buffer, buf_idx, false);
           NRF_LOG_INFO("errcode = 0x%d", err_code);
           NRF_LOG_FLUSH();
           APP_ERROR_CHECK(err_code);
           break;
      default:
           return 0;
    }
    return 1;

}

int main(void)
{    
    ret_code_t err_code;
    uint8_t address;
    uint8_t sample_data;
    bool detected_device = false;

    APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
    NRF_LOG_DEFAULT_BACKENDS_INIT();

    //NRF_LOG_INFO("TWI scanner started.");
    NRF_LOG_FLUSH();
    twi_init();

    
    for (address = 1; address <= TWI_ADDRESSES; address++)
    {
        err_code = nrf_drv_twi_rx(&m_twi, address, &sample_data, sizeof(sample_data));
        if (err_code == NRF_SUCCESS)
        {
            detected_device = true;
            NRF_LOG_INFO("TWI device detected at address 0x%x.", address);
        }
        NRF_LOG_FLUSH();
    }

    if (!detected_device)
    {
        NRF_LOG_INFO("No device was found.");
        NRF_LOG_FLUSH();
        while (true)
        {
      
        }
    }
            
    u8g2_Setup_sh1106_128x64_noname_f(&u8g2, U8G2_R0, u8x8_HW_com_nrf52832, u8g2_nrf_gpio_and_delay_cb);
    u8g2.u8x8.i2c_address = OLED_ADDR;
    u8g2_InitDisplay(&u8g2); // <-- Crash happens here
    u8g2_SetPowerSave(&u8g2,0);
    while (true)
    {        
        nrf_delay_ms(1000);
        print_hello();               
    } 
    
}

The code produces the following output:

<info> app: TWI device detected at address 0x3C.
<info> app: nrf_delay_ms(100)
<info> app: nrf_delay_ms(100)
<info> app: nrf_delay_ms(100)
<info> app: Dumping send buffer of len 25 ('buf_idx')
<info> app:  AE D5 80 A8 3F D3 00 40|....?..@
<info> app:  8D 14 20 00 A1 C8 DA 12|.. .....
<info> app:  81 CF D9 F1 DB 40 2E A4|.....@..
<info> app:  A6                     |.
<info> app: Now calling 'nrf_drv_twi_tx(&m_twi, 60, buffer, buf_idx, false)'
<info> app: errcode = 0x33282
<error> app: Fatal error

What could be the root cause for the Fatal Error when trying to send those 25 bytes?

As said: The scanning for the device ID at the start of the program works! So the GPIOs, the frequency etc. should all be ok?

Do I have to call some other API before doing a nrf_drv_twi_tx()? If yes, which one? 

Parents
  • Hello,

    Glad to hear that you found the root cause of your issue. Yes. The 0x8202 means NRF_ERROR_DRV_TWI_ERR_DNACK. Why this was returned was probably a bit random, if the device was not configured for TWI. 

    Best of luck with the garbled output. Make sure that you check the datasheet of the display, so that you send the data in the way that the screen expects to receive it. 

    Best regards,

    Edvin

Reply
  • Hello,

    Glad to hear that you found the root cause of your issue. Yes. The 0x8202 means NRF_ERROR_DRV_TWI_ERR_DNACK. Why this was returned was probably a bit random, if the device was not configured for TWI. 

    Best of luck with the garbled output. Make sure that you check the datasheet of the display, so that you send the data in the way that the screen expects to receive it. 

    Best regards,

    Edvin

Children
Related