Shop OBEX P1 Docs P2 Docs Learn Events
Understanding the use of DAT data as a variable — Parallax Forums

Understanding the use of DAT data as a variable

ErlendErlend Posts: 612
edited 2016-11-07 12:28 in Propeller 1
Quite some time ago I thought I had grasped how a data element declared in DAT could be used as a variable across many instances of an Object. I am not so sure anymore.
In the I2C driver object I have
DAT                                        
          PINscl              LONG    -1   
          PINsda              LONG    -1   
                                           
                                           
PUB Init(_PINscl, _PINsda)                 
                                           
   LONG[@PINscl]:= _PINscl                 
   LONG[@PINsda]:= _PINsda                 
                                           
   DIRA[PINscl] := 0                       
   OUTA[PINscl] := 0                       
   DIRA[PINsda] := 0                       
   Reset                                   

and that allows for only the first parent caller to need to call Init, all the others can simply call the particular bus routines, being safe that this Object still remembers which two pins to use. Correct?
So, what if an other parent caller comes around and wants to initiate a different I2C bus, on an other pair of pins? The way I understand it, if this new caller calls Init with some new set of pin numbers, this will hit back on the original one, and redefine those pin numbers in the same go. Correct?
If so, do I need two different I2C drivers, one like the above, which assumes everyone want to go on the same physical bus, and an other version where the VAR is used instead of DAT?
Or is there some even more devious DAT technique where a set of pins could be defined for an object such as this??

The question is not related to I2C as such, but more in general about how this kind of 'varable sharing' can be done.

My trick for understanding this is to think of DAT as part of the code, not the variables. Code is loaded only once, variables are constructed each time an object is called (instantiated), modifying DAT is like modifying the code, and it therefore affects all the object instances. Is this far off?

Erlend

Comments

  • Tracy AllenTracy Allen Posts: 6,664
    edited 2016-11-07 20:00
    It's possible to define a pair of DAT variables for each pair of pins that you want to use, but then you have to assign each of those pairs to a "port/bus" and refer to that in each method call: i2c.xxx(port,...). It ends up with more code in the method too, in order to point to the selected pins. You are right that if a caller hits a single pair of DAT-defined pins at run time will hit on those pins for every object that uses that bus.

    A standard object like BASIC_I2C names the pin(s) to use with each and every method call, i2c.xxx(sda,...) so one i2c object can be easily used with different i2c busses.

    I like to define the pins as VAR, and then call i2c.init(sda,scl,...). Include the init with the same pins in each object that uses that bus. Each one uses a couple more bytes in VAR, but so what? Then, calls to that bus don't need to include the pin numbers. Each separate i2c bus defined needs to have its own OBJ instance and each one needs to be init'd it throughout the project.

    An i2c bus is a system resource. If it can be accessed from different cogs, one has to be sure that calls to it don't clobber one another. If that is a possibility, the parent object needs either to coordinate tightly or to check out a lock and pass it along along with the pin numbers.
  • Thanks. I see that you discuss a similar issue in the "FourSerial in Two Cogs at Once?" post too. In my case, probably when different chips want to share the same bus this should be coordinated by a common routine, and if so, that routine could make sure Init is only done once for that particular bus, so VAR will be fine, an it will allow some other bunch of chips to have an other bus initiated for themselves without upsetting this first one. So, it what cases are the DAT method benefitial?

    Erlend
  • I'd say the DAT method is beneficial in projects that have only one i2c bus; a frequent case. It would have default values of say, sda=28,scl=29 set in the DAT section. Simply declare the OBJ in every object that needs to call its i2c methods. No Init method needed. Also, method calls can be shorter if they don't have to include a pin number. Leaving the pin numbers in DAT instead of CON allows those pins to be changed at run time if need be via a changePins method for that purpose. The change, as you pointed out, would immediately and globally affect ALL subsequent calls to that i2c object. This approach might work also in a situation where first one i2c bus and then another are systematically addressed. A test jig say. No chance of confusion. But it is not so good when multiple i2c busses are needed at the same time. Doing that would require duplication of the object with two different names and a minor change in the code itself. For multiple instances that is where I prefer to use VAR for the pin numbers, even though it necessitates a call to an Init method throughout. Don't forget locks if multiple cogs might collide on the an i2c bus.

    A multiport i2c object written in PASM could (like the 4-port serial object) use a port/bus number to refer to each bus, and the pins associated with each bus would be stored in the PASM cog. If those were to be more or less fixed pins, they could be preset in the DAT section to be loaded into the cog.
  • Thanks for making me understand this clearer. I like the idea to use DAT and to assign a default set of pin numbers - that can be changed at run time if required. I now understand that there is a 'way out' in the event that more than one is needed - e.g. by calling and second, different objectobject "bus2". I think this approach should cover most eventualities for me.

    Again, thanks.

    Erlend
  • Yes, make a copy of the program with a different name and different default pin numbers.
    bus1 : "i2cDatObject1"
      bus2 : "i2cDatObject2"  ' different in BOTH name AND code from i2cDatObject1
    
    A basic i2c object doesn't need to be overweight, okay if plenty of code space is available. This approach will have about double the hub footprint of keeping the pin numbers as VARs or as parameters in method calls.

  • You could "fix" this by having the caller provide the SDA / SCL pins whenever you use the I2C object, and that object could pass the pin numbers around internally. That would allow you to decouple the pins used from the code, which would mean less overall code than duplicating it just to change the pin definitions. It would be a bit slower because it would have to pass the pins around on the stack, but it would get you the behavior you want.

    The suggestion of having multiple "ports" like Tracy's 4 port serial driver, is probably your best option with Spin. Still slower than the original, but less wasteful than having two full versions of the code just to change the pins.

    This is probably my biggest complaint with Spin - Not being able to pass objects around in the code imposes some significant limits, but it's not the kind of thing you'd normally run into until your projects get big, and it's a big part of why I went with C/C++ for the flight controller code.
  • ErlendErlend Posts: 612
    edited 2016-11-10 11:49
    I guess the best choice of approach depends on likely use. In my case I see many cases where I have several chips on the same bus - for I2C, SPI, and other busses on the way - whereas to have more than one physical bus of a particular kind is more the exception. Using DATed bus variables makes the calling code very clean - I like that.
    Thanks a bunch for all the good brain feed, right now I am implementing this scheme for the SPI bus, intended to run a number of powerStep01 chips.

    Erlend
  • The DAT method makes both the calling and the initialization easy, especially when the default pins are the only bus on the block.

    In more complex projects, I like to keep a core i2c bus that contains the eeprom and the RTC and RTC scratch ram and maybe other chips that are isolated from the outside world. Peripherals are relegated to a second bus, particularly if it goes off the board or is in any way susceptible to mess ups. Much easier to recover and continue and troubleshoot that way.

Sign In or Register to comment.