Shop OBEX P1 Docs P2 Docs Learn Events
Scope of variables for shared (or duplicate?) objects — Parallax Forums

Scope of variables for shared (or duplicate?) objects

ErlendErlend Posts: 612
edited 2015-06-20 20:25 in Propeller 1
My Main calls an object which manages a set of devices on a bus, which again calls a number of objects for each type & instance of devices (so far MCP2301 and VL6180), each of which calls an I2C object to do the work reading and writing to the individual devices (two MCP's and three VL's) connected to the bus.
In my mind the elegant way would be for the bus manager object to assign scl and sda pins and reset the bus, so that the device object happily ever after did not need to concern about the slc and sda pin definition. But that won't work (?) they need to call their I2C objects at least once with slc and sda to set that definition - or provide those parameters each call.
As far as I can see there will be many instances of the I2C object in this scheme, each caring for its own variables (even though the compiler probably makes sure there is only one set of object code).
Am I right?

I would like to keep the structure where there is one object (=non standard) managing the specific set of bus'ed devices, one object (=half standard) for each specific type of device, and the the I2C object (full standard) at the lowest level.
What would be the elegant way of managing slc and sda pin variable initiation?

Erlend

(code is not ready)

Comments

  • kwinnkwinn Posts: 8,697
    edited 2015-06-19 10:12
    Not sure I understand exactly what you are doing but if all these devices are on a single I2C bus you need to make sure the various objects don't step on each others data. Wouldn't it make sense to have the bus manager object start the I2C object and assign the pins, start the device type objects, and then manage access to the I2C bus for the various device type objects?
  • Heater.Heater. Posts: 21,230
    edited 2015-06-19 14:01
    Erlend,

    I know nothing of I2C but I think you need a different architecture.

    1) For any give device or bus (I2C) connected to a particular bunch of pins there should only be one COG accessing those pins. Otherwise you are going to get into a mess. That COG will probably be running code from a single object. That is your "bus manager" or low level driver.

    2) Any application level code that needs to access that device/bus should do so via a "mailbox" in memory through which messages are passed issuing commands and collecting results.

    3) The address of that mailbox can be passed around to any other code that needs to talk to the bus.

    4) Probably want to use locks around access to that mailbox to avoid race conditions.


    That is to say, instead of thinking about calling some methods on some object and having multiple instances of that object, send messages to a single instance instead.
  • ErlendErlend Posts: 612
    edited 2015-06-19 20:46
    Not so easy to explain my scheme in text. Main runs in Cog0. In this context it assigns the pins and the global variables for the remote io, and it issues an Initiate and a Start to the remote io DeviceManager to run in a new cog. The purpose of the DeviceManager is to initiate then start continuously reading/writing the devices. To do that it calls objects that provides the interfacing for each type of device. Since there are several devices of the same type, there are several calls to the same type of object, but no need for a new cog, because the devices are all polled one after another. Then, in the objects written to handle a specific type of device there will be need for I2C operation, so calls are made to a generic I2C object.
    The information about the pins and global variables have to make its way all the way down from the Main and to the I2C for this to work. The DeviceManager could pass that as part of the call each time it orchestrates a poll of devices on the bus, or maybe it can be done once and for all as part of the Initiate routine?
    OBJ
               dio      : "MCP23017"
               lrf      : "VL6180X"
               bus      : "BusI2C"
              
    
    PUB Init(slcPin, sdaPin, pwrPin)                                                 'Initialize the specific devices on the bus                                                                                              
         bus.InitBus(sclpin, sdapin)                                                    'Initialize the bus pins and reset bus  
         lrf.init(sclpin, sdapin, cNode1Addr)                                                          'Initialize this LRF
         lrf.init(sclpin, sdapin, cNode2Addr)                                                          'Initialize this LRF      
         lrf.init(sclpin, sdapin, cNode3Addr)                                                          'Initialize this LRF        
         dio.init(sclpin, sdapin, cNode4Addr, cIOdirIn, cIOdirOut, cPUresOff, cPUresOff)               'Initialize and configure this MCP
         dio.init(sclpin, sdapin, cNode5Addr, cIOdirIn, cIOdirOut, cPUresOff, cPUresOff)               'Initialize this MCP         
    
    PUB Start(slcPin, sdaPin, ptrOutA, ptrOutB, ptrInA, ptrInB, ptrRange, ptrAmbient): Success
    
       Stop
       
       Success:= (cog:= COGNEW(ContinousRWio(slcPin, sdaPin, ptrOutA, ptrOutB, ptrInA, ptrInB, ptrRange, ptrAmbient), @RWstack) + 1)    'Start continous read&write method
    

    This is an outline of the DeviceManager.

    Erlend
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2015-06-19 21:18
    Perhaps this is not relevant to the specific question, but I do see an error in your code. You appear to be trying to start multiple instances of dio and lrf, but in fact you're restarting the same one over and over in each case. For multiple instances, you need something like this:
    OBJ
               dio[B][2][/B]      : "MCP23017"
               lrf[B][3][/B]      : "VL6180X"
               bus      : "BusI2C"
              
    
    PUB Init(slcPin, sdaPin, pwrPin)                                                 'Initialize the specific devices on the bus                                                                                              
         bus.InitBus(sclpin, sdapin)                                                    'Initialize the bus pins and reset bus  
         lrf[B][0][/B].init(sclpin, sdapin, cNode1Addr)                                                          'Initialize this LRF
         lrf[B][1][/B].init(sclpin, sdapin, cNode2Addr)                                                          'Initialize this LRF      
         lrf[B][2][/B].init(sclpin, sdapin, cNode3Addr)                                                          'Initialize this LRF        
         dio[B][0][/B].init(sclpin, sdapin, cNode4Addr, cIOdirIn, cIOdirOut, cPUresOff, cPUresOff)               'Initialize and configure this MCP
         dio[B][1][/B].init(sclpin, sdapin, cNode5Addr, cIOdirIn, cIOdirOut, cPUresOff, cPUresOff)               'Initialize this MCP         
    
    PUB Start(slcPin, sdaPin, ptrOutA, ptrOutB, ptrInA, ptrInB, ptrRange, ptrAmbient): Success
    
       Stop
       
       Success:= (cog:= COGNEW(ContinousRWio(slcPin, sdaPin, ptrOutA, ptrOutB, ptrInA, ptrInB, ptrRange, ptrAmbient), @RWstack) + 1)    'Start continous read&write method
    

    But more to the subject at hand, I think you need to refactor your code. There is no reason for more than one object to know which pins are scl and sda, and that should be the only object that controls the I2C bus.

    -Phil
  • ErlendErlend Posts: 612
    edited 2015-06-19 23:32
    I am not good at explaining this. When to use the term 'object' and when to use 'method' to explain stuff?
    The lrf.init() and dio.init() are not intended to create new instances, they are just calls to library functions. I could as well have put them in this code as PRIV methods, but want to avoid that because of overall software management considerations (they should be in the Library). Therefore lrf[n] is not applicable, right?

    Erlend
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2015-06-20 00:07
    I guess it depends on what happens inside your init methods. If, for example, cNode1Addr gets assigned to a variable via lrf.init for later reference by the lrf object, cNode2Addr will get assigned to the same variable, overwriting the first value. But if you create separate instances of the lrf object and if the variable assigned to is a VAR variable, each instance will contain its own copy of that variable.

    -Phil
  • Heater.Heater. Posts: 21,230
    edited 2015-06-20 03:56
    Erlend,
    When to use the term 'object' and when to use 'method' to explain stuff?
    In Spin an "object" is all that code that is in a single Spin file. The object name comes from the files name.

    A "method" is one of the functions within a object (Spin file). Those PUBs and PRIs

    An instance is a particular incarnation of an object create by an entry in a OBJ section of some parent onject instance.

    Instances of a particular object all have their own separate memory spaces for all the variables in the objects VAR.

    Instances of a particular object all share the same DAT section data of the object.
  • ErlendErlend Posts: 612
    edited 2015-06-20 07:09
    Heater. wrote: »
    ,,,
    Instances of a particular object all share the same DAT section data of the object.

    Ah- so if I put the pin numbers for slc and sda into DAT variables of (e.g. the i2C) object then it sticks? Means I could let the DeviceManager do it by calling a method in busI2C which sets up the DATs
    bus.InitBus(sclpin, sdapin)
    
    and then thereafter any calls to bus r/w methods in BusI2C would not need to include the pin numbers as parameters? That would be magic.

    Erlend

    EDIT:

    Even when DeviceManager has
    OBJ
               dio      : "MCP23017"
               lrf      : "VL6180X"
               bus      : "BusI2C"
    
    -and then both the MCP and the VL also include
    OBJ
               bus      : "BusI2C"
    
    -still the magic applies?
  • ErlendErlend Posts: 612
    edited 2015-06-20 07:14
    I guess it depends on what happens inside your init methods. If, for example, cNode1Addr gets assigned to a variable via lrf.init for later reference by the lrf object, cNode2Addr will get assigned to the same variable, overwriting the first value. But if you create separate instances of the lrf object and if the variable assigned to is a VAR variable, each instance will contain its own copy of that variable.

    -Phil
    The lrf.init and others are one time exercises to load each device with parameters, basically. Then I am curious to wether they could also serve to set slc and sda pin numbers once and for all.

    Erlend
  • Heater.Heater. Posts: 21,230
    edited 2015-06-20 07:52
    Erlend,
    ...still the magic applies?
    Yes. There is only one DAT section for all the instances of the same object at run time.

    So if yo do:

    someObjectInstance1.init(someParameter, otherParameter)

    from one place at start up and those parameters are saved in a DAT section. Then you can do:

    someOjectInstance1.doSomething()

    or:

    someObjectInstance2.doSometing()

    from anywhere in the application and those same saved DAT values will apply everywhere for use by "doSomething"

    Now, one issue is that if you have those different instances running in different COGs and those parameters select pins you need to ensure that DIRA is set correctly in all of them. Every COG has it's own DIRA register. You also need to insure that those instances don't try to use the pins at the same time during read/write calls.

    All in all I would only have one object instance running on one COG handling the pins for one bus or device. Then arrange for any other part of the application to use that device by sending messages to that device handler.

    Of course those messages can be sent by having an interface object to the driver object that can be used from anywhere and uses it's common DAT areas as a message bus in memory.
  • ErlendErlend Posts: 612
    edited 2015-06-20 20:25
    Amazing. I did not realize that DAT variables were that different from VAR,
    Once the Init procedures are done, all the bus-polling takes place in one cog, the one created by DeviceManager. There is no need for more.
    Using DAT as a message bus in memory I have to think a bit more about, I do not fully grasp the possibilities of that.

    Erlend
Sign In or Register to comment.