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

TWIM easydma write write fails

Hi,

I' m trying to create a TMIM easydma sequence to same an ADC. The sequence involves a write, and timer delay and a read.

Given what I'm trying to achieve, I think I need to perform a write, without stopping the transaction.

I'm having problems getting the i2c write without stopping the transaction to work. My code is shown below:

void i2c_write(uint8_t addr, uint8_t data, bool noStop)
{
uint8_t tx_buf[2];

if (noStop == false)
NRF_TWIM0->SHORTS = TWIM_SHORTS_LASTTX_STOP_Msk;

tx_buf[0] = data;
NRF_TWIM0->TXD.MAXCNT = 1;
NRF_TWIM0->TXD.PTR = (uint32_t)&tx_buf[0];

NRF_TWIM0->EVENTS_STOPPED = 0;
NRF_TWIM0->TASKS_STARTTX = 1;
while (NRF_TWIM0->EVENTS_STOPPED == 0);
}

uint8_t i2c_read(uint8_t addr, uint8_t *data)
{
volatile uint8_t rx_buf[4];
NRF_TWIM0->SHORTS = TWIM_SHORTS_LASTTX_STARTRX_Msk | TWIM_SHORTS_LASTRX_STOP_Msk;

NRF_TWIM0->RXD.MAXCNT = 4;
NRF_TWIM0->RXD.PTR = (uint32_t)rx_buf;

NRF_TWIM0->EVENTS_STOPPED = 0;
NRF_TWIM0->TASKS_STARTRX = 1;
while (NRF_TWIM0->EVENTS_STOPPED == 0);

NRF_LOG_INFO("val: %X:%X:%X:%X\r\n", rx_buf[0], rx_buf[1], rx_buf[2], rx_buf[3]);

data[0] = rx_buf[0];
data[1] = rx_buf[1];
data[2] = rx_buf[2];
data[3] = rx_buf[3];

return 0;
}

void simple_test(void) {
volatile uint8_t data[4];

NRF_LOG_INFO("in simple_test\r\n");
NRF_LOG_FLUSH();

i2c_write(0x68, 0x88, true); //convert
nrf_delay_us(66667);
NRF_LOG_INFO("in simple_test: conv\r\n");
NRF_LOG_FLUSH();

i2c_read(0x68, data);
NRF_LOG_INFO("val: %X:%X:%X:%X\r\n", data[0], data[1], data[2], data[3]);
}

The code shown above hangs.

If I rmake the i2c_write stop, then the code no longer hangs, but does not perform the

write successfully, and the read just reads whatever is in the adc register.

If I change the highlighted write to use:

nrf_drv_twi_tx in the form of:

ret_code_t i2c_write_block(uint8_t i2cInstIdx, uint8_t address,uint8_t const *cmd, uint8_t cmd_length)
{
ret_code_t err_code;
switch (i2cInstIdx)
{
case I2C_INST_00:
err_code = nrf_drv_twi_tx(&twi_instance_00, address, cmd, cmd_length, false);
APP_ERROR_CHECK(err_code);
break;
case I2C_INST_01:
err_code = nrf_drv_twi_tx(&twi_instance_01, address, cmd, cmd_length, false);
APP_ERROR_CHECK(err_code);
break;
default:
NRF_LOG_WARNING("DRV:i2C: Bad inst idx : %d \r\n", i2cInstIdx);
break;
}
return err_code;
}

void simple_mixed_test(void) {
volatile uint8_t data[4];

ret_code_t err_code;
uint8_t instIdx = I2C_INST_00;
uint8_t init_cmd[2] = {0x0, 0x0};
uint8_t address = 0x68;

NRF_LOG_INFO("in simple_mixed_test\r\n");
NRF_LOG_FLUSH();
i2c_init_block(I2C_INST_00);

init_cmd[0] = 0x88;
err_code = i2c_write_block(instIdx, address, init_cmd, 1);
//i2c_write(0x68, 0x88, true); //convert
nrf_delay_us(66667);

i2c_read(0x68, data);
NRF_LOG_INFO("val: %X:%X:%X:%X\r\n", data[0], data[1], data[2], data[3]);
}

Then it all works fine. 

Please can you advise!

Parents
  • Hi Kenneth,

    Thanks for your reply!

    When you say the twi driver, do you refer to the current or legacy?

    The code above which works uses the legacy form of the twi driver, however its either

    blocking or interrupt based. I've looked at the source and it all seems to map down to

    the same NRFX easydma based driver.

    If I make the calls in form:

    uint8_t tx_buf[1] = {0x88};
    nrfx_twim_xfer_desc_t xfer = NRFX_TWIM_XFER_DESC_TX(addr, (uint8_t*)tx_buf, sizeof(tx_buf));

    uint32_t err_code = nrfx_twim_xfer(&(twi_instance_00.u.twim), &xfer, 0);

    APP_ERROR_CHECK(err_code);

    Which replicates whats in nrf_drv_twi_tx  , its work, and successfully writes.

    However my understanding is that this code is blocking the same as nrf_drv_twi_tx .

    If however I try to make defer execution and suitable for initiation via PPI:

    uint32_t flags = NRF_DRV_TWI_FLAG_NO_XFER_EVT_HANDLER | 
                              NRFX_TWIM_FLAG_TX_NO_STOP |
                              NRF_DRV_TWI_FLAG_HOLD_XFER;

    uint32_t err_code = nrfx_twim_xfer(&(twi_instance_00.u.twim), &xfer, flags);

    then the nrfx_twim_xfer call never completes?!?

    Any ideas?

    BR, Paul

Reply
  • Hi Kenneth,

    Thanks for your reply!

    When you say the twi driver, do you refer to the current or legacy?

    The code above which works uses the legacy form of the twi driver, however its either

    blocking or interrupt based. I've looked at the source and it all seems to map down to

    the same NRFX easydma based driver.

    If I make the calls in form:

    uint8_t tx_buf[1] = {0x88};
    nrfx_twim_xfer_desc_t xfer = NRFX_TWIM_XFER_DESC_TX(addr, (uint8_t*)tx_buf, sizeof(tx_buf));

    uint32_t err_code = nrfx_twim_xfer(&(twi_instance_00.u.twim), &xfer, 0);

    APP_ERROR_CHECK(err_code);

    Which replicates whats in nrf_drv_twi_tx  , its work, and successfully writes.

    However my understanding is that this code is blocking the same as nrf_drv_twi_tx .

    If however I try to make defer execution and suitable for initiation via PPI:

    uint32_t flags = NRF_DRV_TWI_FLAG_NO_XFER_EVT_HANDLER | 
                              NRFX_TWIM_FLAG_TX_NO_STOP |
                              NRF_DRV_TWI_FLAG_HOLD_XFER;

    uint32_t err_code = nrfx_twim_xfer(&(twi_instance_00.u.twim), &xfer, flags);

    then the nrfx_twim_xfer call never completes?!?

    Any ideas?

    BR, Paul

Children
  • A long time ago I made an example which I think can be used as a reference.

    /* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved.
     *
     * The information contained herein is property of Nordic Semiconductor ASA.
     * Terms and conditions of usage are described in detail in NORDIC
     * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
     *
     * Licensees are granted free, non-transferable use of the information. NO
     * WARRANTY of ANY KIND is provided. This heading must NOT be removed from
     * the file.
     *
     */
    
    /**
     * @file
     * @brief File with example code presenting usage of drivers for TWI in master mode with EasyDMA
     *
     * @sa twi_master_with_mma7660_slave_example
     */
    
    #include <stdbool.h>
    #include <stdint.h>
    #include <stdio.h>
    #include <ctype.h>
    #include "config.h"
    #include "app_uart.h"
    #include "nrf_drv_twi_mod.h"
    #include "nrf_gpio.h"
    #include "app_error.h"
    #include "nrf.h"
    #include "bsp.h"
    #include "app_util_platform.h"
    #include "mma7660.h"
    #include "nrf_delay.h"
    #include "nrf_drv_twi_mod.h"
    #include "nrf_drv_ppi.h"
    
    #define SENSOR_POLL_RATE SAMPLES_PER_SEC_4
    
    static const nrf_drv_twi_t m_twi_master = NRF_DRV_TWI_INSTANCE(0);
    static uint8_t m_rxbuf[3] = {0, 0, 0};
    static uint8_t m_txbuf[1] = {0};
    
    // How frequently the RTC should read the accel.
    uint32_t rtc_init(mma7660_mode_t sensor_poll_mode)
    {
        NRF_CLOCK->TASKS_LFCLKSTART = 1;
        while (NRF_CLOCK->EVENTS_LFCLKSTARTED == 0);
        
        NRF_RTC0->PRESCALER = 0;
        NRF_RTC0->EVTENSET = RTC_EVTENSET_COMPARE0_Msk;    
        uint32_t cc0_value;
        switch (sensor_poll_mode)
        {
            case SAMPLES_PER_SEC_120:
                cc0_value = 273 - 1;
                break;
            case SAMPLES_PER_SEC_64:
                cc0_value = 512 - 1;
                break;
            case SAMPLES_PER_SEC_32:
                cc0_value = 1024 - 1;
                break;
            case SAMPLES_PER_SEC_16:
                cc0_value = 2048 - 1;
                break;
            case SAMPLES_PER_SEC_8:
                cc0_value = 4096 - 1;
                break;
            case SAMPLES_PER_SEC_4:
                cc0_value = 8192 - 1;
                break;
            case SAMPLES_PER_SEC_2:
                cc0_value = 16384 - 1;
                break;
            case SAMPLES_PER_SEC_1:
                cc0_value = 32768 - 1;
                break;
            default:
                return NRF_ERROR_INVALID_PARAM;            
        }
        NRF_RTC0->CC[0] = cc0_value;
        NRF_RTC0->TASKS_START =1;
        return NRF_SUCCESS;
    }
    
    // Enable PPI channels to connect the RTC Compare0 event to trigger TWI transfer without CPU interaction.
    uint32_t ppi_init(uint32_t * p_twim_master_start_task)
    {
        uint32_t err_code;
        nrf_ppi_channel_t ppi_channel, ppi_channel2;
    
        err_code = nrf_drv_ppi_channel_alloc(&ppi_channel);
        if (err_code != NRF_SUCCESS)
            return err_code;
        // Create a PPI channel from RTC Compare0 event to start TWI transfer task
        err_code = nrf_drv_ppi_channel_assign(ppi_channel, (uint32_t)&NRF_RTC0->EVENTS_COMPARE[0], (uint32_t)*p_twim_master_start_task);
        if (err_code != NRF_SUCCESS)
            return err_code;    
    
        err_code = nrf_drv_ppi_channel_enable(ppi_channel);
        if (err_code != NRF_SUCCESS)
            return err_code;    
    
        err_code = nrf_drv_ppi_channel_alloc(&ppi_channel2);
        if (err_code != NRF_SUCCESS)
            return err_code;
        // Create a PPI channel from Compare0 event to clear RTC timer (repeated until stopped)
        err_code = nrf_drv_ppi_channel_assign(ppi_channel2, (uint32_t)&NRF_RTC0->EVENTS_COMPARE[0], (uint32_t)&NRF_RTC0->TASKS_CLEAR);
        if (err_code != NRF_SUCCESS)
            return err_code;    
    
        err_code = nrf_drv_ppi_channel_enable(ppi_channel2);
        
        return err_code;
    }
    
    // Callback  executed after every twi transfer (in this case just outputs the data over the UART)
    void twi_cb_handler(nrf_drv_twi_evt_t const * p_event, void * p_context)
    {
        // Pack the raw data into a more representable format and cast to int8_t (or with 0xE0, see MMA7660 data output values)
        if(m_rxbuf[0] > 31) m_rxbuf[0] = m_rxbuf[0] | 0xE0;        
        if(m_rxbuf[1] > 31) m_rxbuf[1] = m_rxbuf[1] | 0xE0;        
        if(m_rxbuf[2] > 31) m_rxbuf[2] = m_rxbuf[2] | 0xE0;        
        
        printf("%4i %4i %4i \n\r", (int8_t)m_rxbuf[0], (int8_t)m_rxbuf[1], (int8_t)m_rxbuf[2]);
    }
    
    // Init the TWI module; speed, pins, and callback 'twi_cb_handler()'
    static ret_code_t twi_master_init(void)
    {
        ret_code_t ret;
        const nrf_drv_twi_config_t config =
        {
           .scl                = 27,
           .sda                = 26,
           .frequency          = NRF_TWI_FREQ_400K,
           .interrupt_priority = APP_IRQ_PRIORITY_HIGH
        };
    
        do
        {
            ret = nrf_drv_twi_init(&m_twi_master, &config, twi_cb_handler, NULL);
            if(NRF_SUCCESS != ret)
            {
                break;
            }
            nrf_drv_twi_enable(&m_twi_master);
        }while(0);
        return ret;
    }
    
    // UART error handler.
    static void uart_error_handle(app_uart_evt_t * p_event)
    {
        if (p_event->evt_type == APP_UART_COMMUNICATION_ERROR)
        {
            APP_ERROR_HANDLER(p_event->data.error_communication);
        }
        else if (p_event->evt_type == APP_UART_FIFO_ERROR)
        {
            APP_ERROR_HANDLER(p_event->data.error_code);
        }
    }
    
    // UART used to output debug and accel. data in 'twi_cb_handler()'
    uint32_t uart_init(void)
    {
        uint32_t err_code;
        const app_uart_comm_params_t comm_params =
        {
            RX_PIN_NUMBER,
            TX_PIN_NUMBER,
            RTS_PIN_NUMBER,
            CTS_PIN_NUMBER,
            APP_UART_FLOW_CONTROL_ENABLED,
            false,
            UART_BAUDRATE_BAUDRATE_Baud460800
        };
    
        APP_UART_FIFO_INIT(&comm_params,
                           4,
                           32,
                           uart_error_handle,
                           APP_IRQ_PRIORITY_LOW,
                           err_code);
        return err_code;
    }
    
    // Setup parameters (buffers, type of transfer, and flags) for the next twi transfer by calling 'nrf_drv_twi_xfer()'
    // Useful information: 'nrf_drv_twi_rx()' and 'nrf_drv_twi_tx()' wraps around this function (refer to those also for example on how to setup 'nrf_drv_twi_xfer()')
    uint32_t twim_sync_xfer_setup(uint32_t * p_twim_master_start_task)
    {
        nrf_drv_twi_xfer_desc_t p_xfer_desc;
        uint32_t err_code;    
        
        p_xfer_desc.address = MMA7660_DEFAULT_ADDRESS;
        p_xfer_desc.type = NRF_DRV_TWI_XFER_TXRX;
        p_xfer_desc.primary_length = 1;
        p_xfer_desc.secondary_length = 3;
        p_xfer_desc.p_primary_buf = m_txbuf;
        p_xfer_desc.p_secondary_buf = m_rxbuf;
        
        uint32_t flags = NRF_DRV_TWI_FLAG_HOLD_XFER | NRF_DRV_TWI_FLAG_REPEATED_XFER;
    
        do 
        {
            err_code = nrf_drv_twi_xfer(&m_twi_master, &p_xfer_desc, flags);
            
            // If success, read out task required to start the twi transfer, will be used to setup the correct PPI channel to trigger the twi transfer.
            if(err_code == NRF_SUCCESS)
            {
                * p_twim_master_start_task = nrf_drv_twi_start_task_get(&m_twi_master, NRF_DRV_TWI_XFER_TXRX); 
            }
        } while (err_code == NRF_ERROR_BUSY);   
        
        return err_code;
    }
    
    /**
     *  The begin of the journey
     */
    int main(void)
    {    
    	uint32_t twim_master_start_task;
    	
        // For debugging purposes only, writes accel. data to UART in twi callback 'twi_cb_handler()'
        APP_ERROR_CHECK(uart_init());  
    
        // Init. twi module, need to provide a callback 'twi_cb_handler()', else each call to twi driver will be blocking
        APP_ERROR_CHECK(twi_master_init());        
        
        // Write some init. twi data to the accel. to enable it, create new data every 'SENSOR_POLL_RATE'
        APP_ERROR_CHECK(mma7660_init(&m_twi_master, SENSOR_POLL_RATE));    
    
        // Setup repeated twi transfer (e.g. provide tx and rx buffers and type of twi transfer), each twi transfer can now be triggered by 'twim_master_start_task'
        APP_ERROR_CHECK(twim_sync_xfer_setup(&twim_master_start_task));    
        
        // Enable ppi channel from RTC compare0 event to start twi transfer 'twim_master_start_task'
        // Useful information: Could have used a pin interrupt from the accel. to trigger twi transfer, but the mbed module doesn't expose the interrupt data ready pin, so use RTC instead
        APP_ERROR_CHECK(ppi_init(&twim_master_start_task));
        
        // Start RTC, RTC ompare0 event will execute twi transfer on every 'SENSOR_POLL_RATE'
        APP_ERROR_CHECK(rtc_init(SENSOR_POLL_RATE));
        
        // No more CPU activity needed, go to sleep, CPU only interrupted when data is ready in buffer in twi callback 'twi_cb_handler()'.
        // RTC Compare0 event wil ensure data is read at specific intervals by triggering twi transfer through PPI, while CPU can process the data at it's own pace.
        SCB->SCR |= SCB_SCR_SEVONPEND_Msk;    
        while(1)
        {
            __sev();
            __wfe();
            __wfe();
        }        
    }
    
    /** @} */ /* End of group twi_master_with_mma7660_slave_example */
    
    

  • Hi,

    I was not able to fined where you set the TX buffer before the transfer.

    I need to trigger by PPI a TWI transfer only .

    Best Regards

    Jawad

  • The TX and RX buffer is setup on nrf_drv_twi_xfer().

    Best regards,
    Kenneth

Related