Set delay time between two functions

yeerang wrote on Thursday, December 11, 2014:


I want to call the function i2c_master_transaction() in the task, and there are i2c_send() and i2c_receive() in the i2c_master_transaction() function. Those i2c_send() and i2c_receive() functions operates with Queue.

I would like to set some delay time between i2c_send() and i2c_receive(), but it seems that delay_us() and vTaskDelay were not effective.

Could you advise how to set the delay time in the task?
Thank you in advance.

heinbali01 wrote on Thursday, December 11, 2014:

Hi Yeerang,

Functions like delay_us() are implemented as counted loops, using the CPU intensively.

vTaskDelay() has a lower resolution (of 1 clock tick) but it does make the task sleep.

Your i2C_receive() already blocks while waiting for data in xQueueReceive(), why would you want to have an extra delay?


yeerang wrote on Thursday, December 11, 2014:

Because if i2c_receive() request the response too fast, there will be the value which means ‘the response is not ready’.
Could I set the delay time using the vTaskDelay()?

heinbali01 wrote on Thursday, December 11, 2014:

You can always use:

    void vTaskDelay( const TickType_t xTicksToDelay )

to introduce some delay.

Your i2c_receive() calls xQueueReceive() and if it is called too quickly, you say there is a value which means ‘the response is not ready’.

Shouldn’t it just wait once more until the real response comes in?

yeerang wrote on Friday, December 12, 2014:

I don’t get what you mentioned in the last line.

I alreaday tried to use vTaskDelay as below:


And it was not working as what I wanted.

rtel wrote on Friday, December 12, 2014:


It is a bit of a crude device in this case. Ideally you would want to
be using DMA or at least interrupt and let the hardware do it for you
(assuming you are using an I2C peripheral, and not just bit-banging the
protocol through an IO port. If not then the next best is to make no
assumptions about timing, but instead query the hardware to see the
system state (i.e. read the registers).


heinbali01 wrote on Friday, December 12, 2014:

In my experience, writing a perfect interrupt-driven TWI (I2C) device driver is very complex. Using DMA will make it more efficient but also more complex.
Within each interrupt you’ll have to ask your self: where am I in the process? what has been done? What must be done now?

Also, very important is error handling. It is possible that some TWI device gets into a state where the bus becomes totally unusable. In that case there are two ways to solve it: either a total power reset, or a bus-reset.

In case you’ll encounter a blocked bus in which SDA is constantly low, here’s a way to do a bus-reset:

void set_data_dir( int direction );
void set_data_val( int value );

int get_data( void ); /* read the SDA pin */

void set_clock_dir( int direction );
void set_clock_val( int value );

void us_delay( int useconds );

void twi_gpio_reset (int aDoLock)
    int bitNr;
     * In case a part is keeping SDA constantly low by
     * pulling it down, a bus reset is needed:
     * 1. Clock up to 9 cycles while SDA in high-impedance
     * 2. Look for SDA high in each cycle while SCL is high
     * 3. Create a start condition as SDA is high.

    /* First define SDA/SCL temporarily as normal GPIO's */

    set_clock( 1 );
    set_data( 1 );
    set_clock_dir( _OUTPUT );
    set_data_dir( _INPUT );
        // CLOCK  11110011 11001010 10101010 10101010
        unsigned clock_pattern = 0xF3CAAAAA;
        // DATA   11100001 10011111 11111111 11111111
        unsigned data_pattern  = 0xE19FFFFF;
        // READ   00000000 00001111 11111111 11111111
        unsigned read_pattern  = 0x000FFFFF;

        for (bitNr = 0; bitNr < 32; bitNr++) {
            /* Clock out any data a slave might have */
            set_clock_val( clock_pattern & 1 );

            if (read_pattern & 1)
                set_data_dir( _INPUT );
                set_data_dir( _OUTPUT );
                set_data_val( data_pattern & 1 );
            us_delay( 20 ); /* Minimal 20 uS delay */

            if( ( read_pattern & 1 ) &&
                ( clock_pattern & 1 ) &&
                ( get_data( ) != 0 ) )
                /* A device has released the SDA.
                 * This should happen between 1 and 9 bits.
            clock_pattern >>= 1;
            data_pattern  >>= 1;
            read_pattern  >>= 1;
    /* Give SDA/SCL pins back to the TWI peripheral */

How much data do you exchange with your I2C devices and how often?

If you’re polling some chip occasionally, like once a second, you could also consider leaving-out the interrupts and DMA and just send, poll, receive.