Jump to content
curtis63

OpenCollector vs INPUT, OUTPUT, INPUT_PULLUP PinModes...

Recommended Posts

So,  I'm using Energia and getting frustrated with Wire.h.  I need to do some i2c communications and figured it would be fairly easy to write my own Bit-Bang implementation of i2c.

 

I got started and things seem to be working pretty well.   However, I discovered the concept of OpenCollector.

 

It seems that OpenCollector means that the pin is set to High by default with a Pullup Resister and you just set it to LOW and then it goes back to high when you're not setting it to LOW.  It also goes to low when a slave i2c device sets it to LOW.

 

Question is how the heck do we do this with Energia??

 

Does pinMode(INPUT) set a pin to OpenCollector mode?

Does pinMode(INPUT_PULLUP) set a pin to OpenCollector mode?

 

If either of these do, what does digitalWrite(LOW) do on that pin?  It sets it to LOW, but for how long?  Do you have to set it back to HIGH, or does it get pulled back up to HIGH, or does setting it to HIGH, just release the LOW pull-down?

 

This behavior doesn't seem to be documented anywhere that is easy to find.  Perhaps it's not intended to be used that way, but I don't know how a person would implement i2c correctly without being able to set the SCL and the SDA to OpenCollector mode and to be able to pulse the clock.

 

Speaking of pulsing the clock, how is that to be accomplished?  Seems like you should be able to pull it LOW and then let it go, but I'm not sure how to 'let it go' back to its HIGH state without just calling digitalWrite(HIGH) on that pin..  But then aren't I holding it high?

 

Anyway, if somebody has a good explanation around this, I would LOVE to hear it.  I found some hints about it at the following location

 

http://energia.nu/Constants.html

 

But it didn't use the term OpenCollector anywhere... 

Share this post


Link to post
Share on other sites

Yes, I do have external pullups on the SCL and SDA lines.  I'm using 4.7k resistors.  As to Liviu's response, I saw similar response on a different forum and here's the way I now understand it.

 

If i call:   digitalWrite(SDA, LOW); that simply sets a value somewhere that will be used on SDA when it is in OUTPUT mode.

 

When I call:  pinMode(SDA, INPUT); it sets SDA pin to OpenCollector mode which will allow other sensors to pull it LOW, or release it and allow the pullup resister to pull it up to HIGH.

 

When I call:  pinMode(SDA, OUTPUT); it sets SDA pin to whatever I set it to with my last digitalWrite statement.

 

So, to use it as an OpenCollector and be able to pull it LOW and release it to go HIGH, I just need to do the following:

Setup()
{
  pinMode(SDA, INPUT);  //use the pin as open collector
  digitalWrite(SDA, LOW); //use this as a placeholder for when I want to actually set the pin to LOW
}

Loop()
{
    ....
    ....
   pinMode(SDA, OUTPUT); //Pull the pin to LOW
    ....
    ....    ....
    ....   pinMode(SDA, INPUT); //release the pin and allow the pullup resister to pull it to HIGH if I'm writing a HIGH bit to the line, or allowing others to write to the pin
    ....    ....
    ....
    int value = digitalRead(SDA); //read whatever was written to the pin by any i2c sensor on the line
    ....    ....
    ....
   pinMode(SDA, OUTPUT); //Pull the pin to LOW if I'm writing data out to the line


}

I think this is going to work.  Thanks for all the help !!!

Share this post


Link to post
Share on other sites

Hi Curtis,

 

yes, exactly in this way I was thinking it should work. I'll would just say:

 

When I call:  pinMode(SDA, INPUT); it sets SDA pin to high impedance mode

instead of

 

When I call:  pinMode(SDA, INPUT); it sets SDA pin to OpenCollector mode

Best regards,

Liviu

Share this post


Link to post
Share on other sites

Hi,

 

Most of MSP430 cannot do open collector mode. The reason is simple : they use push-pull IN/OUT circuit, so the same signal can generate high powered output (sourcing and sinking). In open collector mode, you just need a transistor to pull down the data line, the high state is generated by a pull up resistor, but it's not possible as I explained, there are two transistor (push-pull) drived by the same signal. So the pull up resistor is completely useless as high state will be generated by the high state of the output. 

 

Open drain is very useful because every ic which is able to pull low the data line is able to speak.

 

But everything is not lost : if you play between output and input modes it's possible, and it's already used by energia (it depends on the Wire.setModule() you called), look here : 

https://github.com/energia/msp430-lg-core/blob/master/libraries/Wire/utility/twi_sw.c

Share this post


Link to post
Share on other sites

So I have frustration with Wire.h because I was having weird behavior with one of my sensors.  For about 32 seconds, I would get perfect data from it, and then after 32 seconds, my data would turn to crap.  It didn't matter if I read the data often, or at 10 second intervals, it still crapped out at 32 seconds.  I was reading 5 bytes and doing some math on them to get the actual values.  These bytes seemed to be getting 'off' by a byte or two, so the data turned to junk.  I wasn't sure why this was happening.  Then, I looked closely at the I2C on a scope and discovered that Wire.h was sending double starts...  It would send a start, start writing a couple bits, then start over.  I think that was confusing the sensor I was using.  I couldn't figure out why Wire would do that, so I figured I'd just write my own i2c library since it's really not that complicated.  Or that's what it appears to be..

 

After having attempted to implement it, I'm finding that the following isn't working as expected:

 

digitalWrite(SDA, LOW);

digitalWrite(SCL, LOW);

pinMode(SDA,INPUT);

pinMode(SCL,INPUT);

 

I would expect the above to make it so that SCL and SDA would now read as HIGH, since they're released to allow the pullup resisters to pull them up.

 

However, when I do the following afterwords:

 

Serial.println(digitalRead(SDA));

Serial.println(digitalRead(SCL));

 

I get a 0 and a 1...  I would have expected both lines to be HIGH.. Why is the SDA line still LOW ???

 

I'm using a MSP430G2553 controller and am using a 4.7k pullup resister on each the SDA and on the SCL pins (one resister per pin).

 

Any suggestions as to why Wire would be sending double starts after 32 seconds, or why the above results in SDA still being LOW would be very helpful.  My suspicion is that one of my sensors is pulling the pin low for some reason...

 

Share this post


Link to post
Share on other sites

 

 

it depends on the Wire.setModule() you called

So, I googled Wire.setModule() and got just about bupcuss..  IE:  Nada...  Can somebody explain the different modules and what each of them do?  I use setModule(0) so that I can use pins 1.6 and 1.7 for my SCL and SDA respectively.  And that's just because somebody recommended it in a posting somewhere.  I have no idea what the possible modes are or what the differences are between them...

Share this post


Link to post
Share on other sites

32 seconds ? Or exactly 32.768 s ? :) 

 

I understand you're frustration about Wire.h in Energia : I posted several topic about problems with it, and not little ones sometimes ...

 

setModule() just select the i2c port to use. The main reason is that shield pinout was modified since the launchpad creation and TI decided to standardise i2c position. But as for example on MSPG2553 there is only one i2c hardware port, they emulated one on the right pins to be able to use new launchpads shields.

 

In Energia the main problem is that pinout diagram are not clear, or updated, or false. For example, G2 launchpad are not good : i2c module #1 is simply not mentionned, but in stellaris it's ok : http://energia.nu/Guide_StellarisLaunchPad.html but there is an error on Serial4 ...

 

The best way to answer all this questions is to look at the pins_energia.h file of the board used. If I take the one of G2 I found :

static const uint8_t SS      = 8;  /* P2.0 */
static const uint8_t SCK     = 7;  /* P1.5 */
static const uint8_t MOSI    = 15; /* P1.7 */
static const uint8_t MISO    = 14; /* P1.6 */
static const uint8_t TWISCL1  = 9;   /* P2.1 SW I2C */
static const uint8_t TWISDA1  = 10;  /* P2.2 SW I2C */
static const uint8_t TWISDA0  = 15;  /* P1.7 */
static const uint8_t TWISCL0  = 14;  /* P1.6 */
static const uint8_t DEBUG_UARTRXD = 3;  /* Receive  Data (RXD) at P1.1 */
static const uint8_t DEBUG_UARTTXD = 4;  /* Transmit Data (TXD) at P1.2 */

and later :

#define DEFAULT_I2C -1 /* indicates SW I2C on pseudo module 1 */

So if you want to use HW i2c (so use pin 1.6 and 1.7), you have to use Wire.setModule(0) before calling Wire.begin(). Largelly speaking, in Energia I always use this command to be sure that i'm using the expected port. And in my library I always put this command with a preprocessor command to check if I'm working in Energia environnement and get my library compatible with Arduino ;)

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...