Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs
This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

TWI I2C problem ( ssd1306 display )

Hello,

I have a problem through which unfortunately I'm not able to come across alone. I'm using nRF52832 with softdevice and UART central example from SDK14.2 (..\examples\ble_central\ble_app_uart_c\pca10040\s132\arm5_no_packs). Right now I'm trying to communicate my nordic device with LED display which is using ssd1306 libraries. Problem occures at the moment when I'm trying to use nrf_drv_twi_tx() function to transmit datas over TWI. 

Of course I've set all necessary items in sdk_config file as below:

// <e> TWI_ENABLED - nrf_drv_twi - TWI/TWIM peripheral driver
//==========================================================
#ifndef TWI_ENABLED
#define TWI_ENABLED 1
#endif
// <o> TWI_DEFAULT_CONFIG_FREQUENCY  - Frequency
 
// <26738688=> 100k 
// <67108864=> 250k 
// <104857600=> 400k 

#ifndef TWI_DEFAULT_CONFIG_FREQUENCY
#define TWI_DEFAULT_CONFIG_FREQUENCY 26738688
#endif

// <q> TWI_DEFAULT_CONFIG_CLR_BUS_INIT  - Enables bus clearing procedure during init
 

#ifndef TWI_DEFAULT_CONFIG_CLR_BUS_INIT
#define TWI_DEFAULT_CONFIG_CLR_BUS_INIT 0
#endif

// <q> TWI_DEFAULT_CONFIG_HOLD_BUS_UNINIT  - Enables bus holding after uninit
 

#ifndef TWI_DEFAULT_CONFIG_HOLD_BUS_UNINIT
#define TWI_DEFAULT_CONFIG_HOLD_BUS_UNINIT 0
#endif

// <o> TWI_DEFAULT_CONFIG_IRQ_PRIORITY  - Interrupt priority
 

// <i> Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice
// <0=> 0 (highest) 
// <1=> 1 
// <2=> 2 
// <3=> 3 
// <4=> 4 
// <5=> 5 
// <6=> 6 
// <7=> 7 

#ifndef TWI_DEFAULT_CONFIG_IRQ_PRIORITY
#define TWI_DEFAULT_CONFIG_IRQ_PRIORITY 7
#endif

// <e> TWI0_ENABLED - Enable TWI0 instance
//==========================================================
#ifndef TWI0_ENABLED
#define TWI0_ENABLED 1
#endif
// <q> TWI0_USE_EASY_DMA  - Use EasyDMA (if present)
 

#ifndef TWI0_USE_EASY_DMA
#define TWI0_USE_EASY_DMA 1
#endif

// </e>

// <e> TWI1_ENABLED - Enable TWI1 instance
//==========================================================
#ifndef TWI1_ENABLED
#define TWI1_ENABLED 1
#endif
// <q> TWI1_USE_EASY_DMA  - Use EasyDMA (if present)
 

#ifndef TWI1_USE_EASY_DMA
#define TWI1_USE_EASY_DMA 1
#endif

Below I've paste  my twi init code:

#define SSD1306_CONFIG_SCL_PIN  ARDUINO_SCL_PIN     //from pca10040.h  27 SCL signal pin
#define SSD1306_CONFIG_SDA_PIN  ARDUINO_SDA_PIN     //from pca10040.h  26 SDA signal pin

...

void ssd1306init() {
	ssd1306_init_i2c(SSD1306_CONFIG_SCL_PIN, SSD1306_CONFIG_SDA_PIN);
	ssd1306_begin(SSD1306_EXTERNALVCC, SSD1306_I2C_ADDRESS, false);
}

Than in ssd1306_init_i2c() function:

void ssd1306_init_i2c(uint32_t scl, uint32_t sda)
{
    _i2caddr = SSD1306_I2C_ADDRESS;
    use_i2c = true;
    twi_master_init(scl, sda);
}

void twi_master_init(uint32_t scl, uint32_t sda)
{
    ret_code_t ret;
    const nrf_drv_twi_config_t config = {
        .scl                = scl,
        .sda                = sda,
        .frequency          = NRF_TWI_FREQ_100K,
        .interrupt_priority = APP_IRQ_PRIORITY_HIGH,
        .clear_bus_init     = true
    };

    do {
	ret = nrf_drv_twi_init(&m_twi_master, &config, twi_handler, NULL);
        if (NRF_SUCCESS != ret) {
            break;
        }
        nrf_drv_twi_enable(&m_twi_master);
    }
    while (0);
    return;
}

And than in ssd1306_begin() function i call ssd1306_command() function  which looks like this:

void ssd1306_command(uint8_t c)
{
    if (use_i2c) {
        ret_code_t ret;
        uint8_t dta_send[] = {0x00, c};
		while(!m_xfer_done);
		m_xfer_done = false;
        ret = nrf_drv_twi_tx(&m_twi_master, _i2caddr, dta_send, 2, false);
		APP_ERROR_CHECK(ret);
        UNUSED_VARIABLE(ret);
    }
    else {
        _HI_CS();
        _LO_DC();
        _LO_CS();
        _HI_CS();
    }
}

m_xfer_done is hendled in twi_hendler which looks like this:

static volatile bool m_xfer_done = false;

void twi_evt_handler(nrf_drv_twi_evt_t const * p_event, void * p_context)
{
    if (p_event->type == NRF_DRV_TWI_EVT_DATA_NACK) {
				m_xfer_done = false;
    }

    if (p_event->type == NRF_DRV_TWI_EVT_DONE) {
				m_xfer_done = true;
    }

    if (p_event->type == NRF_DRV_TWI_EVT_ADDRESS_NACK) {
				m_xfer_done = false;
    }
}

Summarazing whole problem. All code stucks because NRF_DRV_TWI_EVT_DONE is never done actually. Something cosue that nrf_drv_twi_tx() function doesn't work properly and I can not figure out why. I'll be super grateful if someone could help me in solving this problem. So far I've been trying to use higher priority levels for example because I've read on this formu that it could cause some problems regarding to softdevice, but of course it didn't help.

Parents
  • I think that I should be more specific about ssd1306_begin() function which I'm use in my project. The function looks like this:

    void ssd1306_begin(uint8_t vccstate, uint8_t i2caddr, bool reset)
    {
        _vccstate = vccstate;
        _i2caddr = i2caddr;
        UNUSED_VARIABLE(_i2caddr);
    
        _width = WIDTH = SSD1306_LCDWIDTH;
        _height = HEIGHT = SSD1306_LCDHEIGHT;
    
        cursor_y  = cursor_x    = 0;
        textsize  = 1;
        textcolor = textbgcolor = 0xFFFF;
        wrap      = true;
        _cp437    = false;
    
        rotation  = 0;
    		
        // Init sequence for 128x32 OLED module
        ssd1306_command(SSD1306_DISPLAYOFF);                    // 0xAE
        ssd1306_command(SSD1306_SETDISPLAYCLOCKDIV);            // 0xD5
        ssd1306_command(0x80);                                  // the suggested ratio 0x80
    
        ssd1306_command(SSD1306_SETMULTIPLEX);                  // 0xA8
        ssd1306_command(0x1F);
        ssd1306_command(SSD1306_SETDISPLAYOFFSET);              // 0xD3
        ssd1306_command(0x00);                                   // no offset
        ssd1306_command(SSD1306_SETSTARTLINE | 0x0);            // line #0
        ssd1306_command(SSD1306_CHARGEPUMP);                    // 0x8D
        if (vccstate == SSD1306_EXTERNALVCC) {
            ssd1306_command(0x10);
        }
        else {
            ssd1306_command(0x14);
        }
        ssd1306_command(SSD1306_MEMORYMODE);                    // 0x20
        ssd1306_command(0x0);                                  // 0x0 act like ks0108
        ssd1306_command(SSD1306_SEGREMAP | 0x1);
        ssd1306_command(SSD1306_COMSCANDEC);
        ssd1306_command(SSD1306_SETCOMPINS);                    // 0xDA
        ssd1306_command(0x02);
        ssd1306_command(SSD1306_SETCONTRAST);                   // 0x81
        ssd1306_command(0x8F);
        ssd1306_command(SSD1306_SETPRECHARGE);                  // 0xd9
        if (vccstate == SSD1306_EXTERNALVCC) {
            ssd1306_command(0x22);
        }
        else {
            ssd1306_command(0xF1);
        }
        ssd1306_command(SSD1306_SETVCOMDETECT);                 // 0xDB
        ssd1306_command(0x40);
        ssd1306_command(SSD1306_DISPLAYALLON_RESUME);           // 0xA4
        ssd1306_command(SSD1306_NORMALDISPLAY);                 // 0xA6
        ssd1306_command(SSD1306_DISPLAYON);//--turn on oled panel
    }

    As you can see there is more then one ssd1306_command() function about which I've mentioned above.

    Right now I've changed ssd1306_command() function to be able to printf error code. I put here also nrf_delay_ms(100) because otherwise APP_ERROR_CHECK(ret) has reset my app befor error was printed:

    void ssd1306_command(uint8_t c)
    {
            ret_code_t ret;
            uint8_t dta_send[] = {0x00, c};
    				bool no_stop = false;
    				
    				m_xfer_done = false;
    				while(!m_xfer_done){
    					ret = nrf_drv_twi_tx(&m_twi_master, _i2caddr, dta_send, sizeof(dta_send), no_stop);
    					printf("error code:0x%x.\r\n", ret);
    					nrf_delay_ms(100);
    					APP_ERROR_CHECK(ret);
    				}
            UNUSED_VARIABLE(ret);
    }

    Reason why I write about this is fact that when i call ssd1306_command() only once in ssd1306_begin() function, returned error code from nrf_drv_twi_tx() is 0x00, which means NRF_SUCCESS. Unfortunately every next call of ssd1306_command() (which include nrf_drv_twi_tx() ) cause 0x11 error code - NRF_ERROR_INVALID_ADDR.
    Has anyone idea where is the problem?

Reply
  • I think that I should be more specific about ssd1306_begin() function which I'm use in my project. The function looks like this:

    void ssd1306_begin(uint8_t vccstate, uint8_t i2caddr, bool reset)
    {
        _vccstate = vccstate;
        _i2caddr = i2caddr;
        UNUSED_VARIABLE(_i2caddr);
    
        _width = WIDTH = SSD1306_LCDWIDTH;
        _height = HEIGHT = SSD1306_LCDHEIGHT;
    
        cursor_y  = cursor_x    = 0;
        textsize  = 1;
        textcolor = textbgcolor = 0xFFFF;
        wrap      = true;
        _cp437    = false;
    
        rotation  = 0;
    		
        // Init sequence for 128x32 OLED module
        ssd1306_command(SSD1306_DISPLAYOFF);                    // 0xAE
        ssd1306_command(SSD1306_SETDISPLAYCLOCKDIV);            // 0xD5
        ssd1306_command(0x80);                                  // the suggested ratio 0x80
    
        ssd1306_command(SSD1306_SETMULTIPLEX);                  // 0xA8
        ssd1306_command(0x1F);
        ssd1306_command(SSD1306_SETDISPLAYOFFSET);              // 0xD3
        ssd1306_command(0x00);                                   // no offset
        ssd1306_command(SSD1306_SETSTARTLINE | 0x0);            // line #0
        ssd1306_command(SSD1306_CHARGEPUMP);                    // 0x8D
        if (vccstate == SSD1306_EXTERNALVCC) {
            ssd1306_command(0x10);
        }
        else {
            ssd1306_command(0x14);
        }
        ssd1306_command(SSD1306_MEMORYMODE);                    // 0x20
        ssd1306_command(0x0);                                  // 0x0 act like ks0108
        ssd1306_command(SSD1306_SEGREMAP | 0x1);
        ssd1306_command(SSD1306_COMSCANDEC);
        ssd1306_command(SSD1306_SETCOMPINS);                    // 0xDA
        ssd1306_command(0x02);
        ssd1306_command(SSD1306_SETCONTRAST);                   // 0x81
        ssd1306_command(0x8F);
        ssd1306_command(SSD1306_SETPRECHARGE);                  // 0xd9
        if (vccstate == SSD1306_EXTERNALVCC) {
            ssd1306_command(0x22);
        }
        else {
            ssd1306_command(0xF1);
        }
        ssd1306_command(SSD1306_SETVCOMDETECT);                 // 0xDB
        ssd1306_command(0x40);
        ssd1306_command(SSD1306_DISPLAYALLON_RESUME);           // 0xA4
        ssd1306_command(SSD1306_NORMALDISPLAY);                 // 0xA6
        ssd1306_command(SSD1306_DISPLAYON);//--turn on oled panel
    }

    As you can see there is more then one ssd1306_command() function about which I've mentioned above.

    Right now I've changed ssd1306_command() function to be able to printf error code. I put here also nrf_delay_ms(100) because otherwise APP_ERROR_CHECK(ret) has reset my app befor error was printed:

    void ssd1306_command(uint8_t c)
    {
            ret_code_t ret;
            uint8_t dta_send[] = {0x00, c};
    				bool no_stop = false;
    				
    				m_xfer_done = false;
    				while(!m_xfer_done){
    					ret = nrf_drv_twi_tx(&m_twi_master, _i2caddr, dta_send, sizeof(dta_send), no_stop);
    					printf("error code:0x%x.\r\n", ret);
    					nrf_delay_ms(100);
    					APP_ERROR_CHECK(ret);
    				}
            UNUSED_VARIABLE(ret);
    }

    Reason why I write about this is fact that when i call ssd1306_command() only once in ssd1306_begin() function, returned error code from nrf_drv_twi_tx() is 0x00, which means NRF_SUCCESS. Unfortunately every next call of ssd1306_command() (which include nrf_drv_twi_tx() ) cause 0x11 error code - NRF_ERROR_INVALID_ADDR.
    Has anyone idea where is the problem?

Children
  • Without a 100ms delay you would like have problems with nrf_drv_twi_tx() be called multiple times in the while() loop, another transaction can be buffered while the first is executing. Also make sure that all variables are alive in the scope, so you don't exit ssd1306_command() before nrf_drv_twi_tx() is completed, which may be the case here since you are potentially calling nrf_drv_twi_tx() multiple times.

Related