Teensy 3.2 in I2C slave mode - Some times SCL stay low

Status
Not open for further replies.
Dear all

Since few days (and weeks) I try to managed by myself with an I2C issue, but I now freezed.
I made an 5V I2C network with one master and many slaves : some PCF8574 devices and one Teensy 3.2.
I used for Teensy:
- i2c_t3 - I2C library for Teensy 3.x & LC - (v11.0) Modified 01Dec18 by Brian (nox771 at gmail.com)
- a I2C level translator, a PCA9517 with 2.2k pull-up resistors on 3.3V side

All works fine, except that at least once by day, the SCL line stay low. After investigation, I realised that the SCL low is due to the Teensy.
Jumpers on my Teensy's "mother board" allow me to disconnect SDA et SCL of the Teensy to the I2C network.
So, when I disconnect Teensy's SCL of the rest of the network, other I2C re-works, but freeze when I reconnect it.
The only solution was to manualy restart the Teensy (power off and after on).

I have two questions :
1- In a slave mode, what are the reasons for the Teensy act on SCL line?
2- The more importante, because it could happen : How, can I automatically maneged the "I2C module" restart?

Many thanks in advance for your ideas
 
This note is in the i2c_t3.h file:
// ------------------------------------------------------------------------------------------------------
// Auto retry - uncomment to make the library automatically call resetBus() if it has a timeout while
// trying to send a START (occurs at the beginning of any endTransmission() or requestFrom()
// call). This will toggle SCL to try and get a hung Slave device to release the SDA line.
// If successful then it will try again to send a START, if not then it will return a timeout
// error (same as if auto retry was not defined).
//
// Note: this is incompatible with multi-master buses, only use in single-master configurations
//
//#define I2C_AUTO_RETRY

This seems like it is worth trying as the code triggers for SLAVE usage for a single Master bus:
#if defined(I2C_AUTO_RETRY)
// if not master and auto-retry set, then reset bus and try one last time

Not sure if it will help. what bus speed is in use? Are those level translators up to that speed?
 
Also, after the finding of this issue, I begining to investigate in detail and made many test.
First of all I read the K20 microcontroller's datasheet.
And especially chapter 46 - Inter-Integrated Circuit (I2C) page 1169

  • SBRC bit : I realised that in slave mode the i2c_t3 library don't setting the SBRC bit (Slave Baut Rate Control) of Control Register 2 (I2Cx_C2) that can allow the slave to strech the clock and in this fact to put SCL low (page 1178)
    -->so, I made modifications
    - in "kinetis.h" :
    Code:
    #define I2C_C2_SBRC			((uint8_t)0x10)			// Slave Baud Rate Control
    - in ""i2c_t3.cpp" function i2c_t3::begin_ :
    Code:
            *(i2c->C2) = (address2) ? (I2C_C2_HDRS|I2C_C2_RMEN|I2C_C2_SBRC) // Set high drive select and range-match enable			
                                    : (I2C_C2_HDRS|I2C_C2_SBRC);              // Set high drive select
    --> but no effect on the issue
  • SCL Low Timeout Flag : I tried a way to catch the SCL low event. I'm lucky there a Timeout interrupt for that : bit SLTF of I2Cx_SMB register (page 1181). Behavior descibed on pages 1191 and 1194
    --> I made
    - setting on I2Cx_SLTH and I2Cx_SLTL on my setup() function
    Code:
    	l_Temp =  F_BUS / 100;
    	l_Temp = l_Temp  * TIMEOUT_I2C_SCL_LOW;
    	l_Temp = l_Temp / (64 * 10000);							//divider of /64 cause of TCKSEL = 0 of register I2C0_SMB
    	byte_Temp = (l_Temp >> 16) & 0xFF;
    	I2C0_SLTH = byte_Temp;
    	byte_Temp = l_Temp & 0xFF;
    	I2C0_SLTL = byte_Temp;
    - modifications in ""i2c_t3.cpp" function i2c_isr_handler :
    Code:
            //
            // Slave Mode
            //
    
        	if ((*(i2c->SMB) & I2C_SMB_SLTF) == I2C_SMB_SLTF)				
        	{	*(i2c->SMB) = I2C_SMB_SLTF;									// clearle flag
        		i2c->ui_NbInterruptTimeoutSCL = i2c->ui_NbInterruptTimeoutSCL + 1;
        		if (i2c->b_FlagTimeoutSCL == false)							        // to take account the first timeout of a serie of timeout
        		{	i2c->ui_NbFlagInterruptTimeoutSCL = i2c->ui_NbFlagInterruptTimeoutSCL + 1;
        			i2c->b_FlagTimeoutSCL = true; }
        	}
    b_FlagTimeoutSCL is clear in my program each time I received an I2C message
    --> so, this "trap" works fine but I have to add a reset I2C or something that release the SCL line.

By the way, there is something I don't understand well. When you read the spec :

page 1191 - When the I2C module is a slave, if it detects the TTIMEOUT,MIN condition, it resets its communication and is then able to receive a new START condition.
page 1194 - The SLTF bit must be cleared by software by writing 1 to it in the interrupt routine. You can determine the interrupt type by reading the Status Register.
page 1199 - Figure 46-43. Typical I2C SMBus interrupt routine

Nothing said that the user have to reset by software the I2C module...
I probably miss something

Any way, thanks having take time to read me, and of course to nox771 of the library
 
Code noted above when enabled does resetBus_(i2c,bus); in only this case.
Code:
        #if defined(I2C_AUTO_RETRY)
            // if not master and auto-retry set, then reset bus and try one last time
            if(!(*(i2c->C1) & I2C_C1_MST))
            {
                resetBus_(i2c,bus);
 
@defragster : thanks for your time, and ideas

My I2C network runs at 100kHz
Code:
	Wire.begin(I2C_SLAVE, adresse_I2C_TEENSY, I2C_PINS_18_19, I2C_PULLUP_EXT, 100000);				// Setup for Slave mode, address 0x66, pins 18/19, external pullups, 100kHz
	Wire.setDefaultTimeout (3000);

Concerning the #define I2C_AUTO_RETRY, I'm a little bit confused of its usage.
For me, that mades a reset of the bus, when the device is a master, to force a "wrong' slave device to release SDA line.
But this reset function is call only when your device is a slave (!I2C_C1_MST)...
 
... Just browsing the code … as I understood there to be a resetBus() in that library.

The original post says : Teensy 3.2 in I2C slave mode

That code is only active - when the #define is enabled and then when the i2c object is Not { ! } in Master mode as I read it: if(!(*(i2c->C1) & I2C_C1_MST))

That agreed with the comments in the code as well.

If the problem is reproducible it will be easy to tell if it helps by uncommenting this in the .h file :: //#define I2C_AUTO_RETRY

And perhaps adding a digitalWriteFast( LED_BUILTIN, !(digitalReadFast( LED_BUILTIN )) ); in that code to a pin you can monitor for the execution of that code when it would do the reset and hopefully see the bus stay active..
 
I will uncomment this line and made test, and also use the Build-in Led to check.

But for me there is something strange with that way of reset.
Because, for me, in a slave mode an I2C device don't have to sent START, or toggles SCL line to force other I2C devices to relaese bus.
And also the function i2c_t3::acquireBus_ in which the resetBus_(i2c,bus) function is, is only call in "master" functions.

Any way, I will made test and put GND on SCL to by sure.
 
Interested to hear the results - it is there for a reason. And it reads like it may involve the issue as reported - AFAIK. I did look at some part of this code some time ago - but only to replicate other parts - never actual details.

It seems the master cycle would only trigger in a specific case, so that may not happen if anything.

Might just try taking the LED always LOW at setup() and just setting it HIGH at first - to see it ON - of course as provided above it will toggle the LED on each instance.

100 KHz isn't the fastest for sure - not sure what the level shift parts used are reliably capable of.
 
@defragster : thank again for our discussion, even if my issue isn't solve, it make me feel not alone :)

Well, I did test as described below :
  • uncomment ligne #define I2C_AUTO_RETRY
  • put ligne digitalWriteFast( LED_BUILTIN, true); after resetBus_(i2c,bus); in i2c_t3.cpp
  • put ligne digitalWriteFast( LED_BUILTIN, false); in the message received fonction
  • configure the teensy as an slave I2C
  • make a short circuit between GND and SCL

An unfortnately, as I think, nothing happens...

But if I put the code

Code:
    	if ((*(i2c->SMB) & I2C_SMB_SLTF) == I2C_SMB_SLTF)				
    	{	*(i2c->SMB) = I2C_SMB_SLTF;									// earse  flag
              digitalWriteFast( LED_BUILTIN, true);
    	}
juste after the code
Code:
    else
    {
        //
        // Slave Mode
        //
inside function
Code:
void i2c_isr_handler(struct i2cStruct* i2c, uint8_t bus)
the led switch on when I made the short circuit

My question is, that I probably not be alone to encounter the issue. How can I properly reset the I2C module, and is it normal do want do that?
I know that for MEGA 2560 there is a simple way to achieved that
Code:
	TWCR = 0; //releases SDA and SCL lines to high impedance
	TWCR = _BV(TWEN) | _BV(TWEA); //reinitialize TWI
 
A really simple solution is to put the teensy on a serial port and communicate that way, the teensy has many more ways of communicating other than i2c
 
Shame on me... :eek:
Even if it didn't solve my problem, here is the correct setup of registers I2C0_SLTH and I2C0_SLTL
Code:
	l_TempTimeout =  F_BUS / 64;
	l_TempTimeout = 1000000000  / l_TempTimeout; 													
	l_TempTimeout = (TIMEOUT_I2C_SCL_LOW * 1000) / l_TempTimeout;									// /64 cause of TCKSEL = 0 of register I2C0_SMB
	byte_Temp = (l_TempTimeout >> 8) & 0xFF;
	I2C0_SLTH = byte_Temp;
	byte_Temp = l_TempTimeout & 0xFF;
	I2C0_SLTL = byte_Temp;
 
Well, now I can detected and managed the SCL staying low with the registers I2C0_SLTH and I2C0_SLTL registers it seems that the I2C reset freeze the Teensy or have no effets.
In this fact, I decied to rewrite all my programm and to check step by step when the disturbs happens.

And then, I realize that the IntervalTimer is the key. I use IntervalTimer.h to time my main loop and this is the cause of my issue.
I will investigate a little bit to find which Timer is use with IntervalTimer and why is incompatible with FTM2 of OCTOWS2811.

I will open an other thread, to know how to use a timer to sequence the main loop with the OctoWS2811 library.
 
Status
Not open for further replies.
Back
Top