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.
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()?
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).
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 );
}
else
{
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.
*/
break;
}
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.