Shop OBEX P1 Docs P2 Docs Learn Events
Help Please. Variables with multiple cogs and objects — Parallax Forums

Help Please. Variables with multiple cogs and objects

archerbrad700archerbrad700 Posts: 18
edited 2011-11-10 12:40 in Propeller 1
I read that a variable defined in the VAR block can be seen by all the methods of that object, but what about in other Cogs running other objects?

What I have is a method in one object that polls the user for input and saves the input to a variable (say desiredX) in another object I have a method that looks for that variable (desiredX) to change and when it does it executes a subroutine. The problem is that the subroutine never gets executed. I imagine it is because one object/cog can't see what is going on in another.

So the question is how do I get the second cog to see that the variable has changed in the first cog?

Thanks for your help,
-Brad

Comments

  • Mike GMike G Posts: 2,702
    edited 2011-11-09 13:59
    Assuming this is all SPIN, then pass the address(es) of HUB memory to each process, I'd use a DAT block not VAR. You'll also need to setup a semaphore to lock memory so one process does not step on the other. There are some great examples in the Propeller Manual under memory locking.

    If we're talking about PASM there is an extra step of getting the COG data to HUB memory.

    It helps if you post your code.
  • archerbrad700archerbrad700 Posts: 18
    edited 2011-11-09 14:21
    Thanks for your reply,

    I am coding in Spin and here is my code:
    {{__________________________________________________________
    
    
    Author: Bradley Bauer
    Date: 11-3-11
    __________________________________________________________
    
    
    
    
    }}
    
    
    CON
    
    
      _clkmode = xtal1 + pll16x     'Crystal and PLL settings.
      _xinfreq = 5_000_000          '5 MHz crystal (5 MHz x 16 = 80 MHz)
    
    
    VAR
    
    
      Word desiredX                  'Variable to store desired X position
      Word desiredY                  'Variable to store desired Y position
    
    
      Byte XResolveCog               'Stores cog ID that runs XResolve
      Byte YResolveCog               'Stores cog ID that runs YResolve
      Byte PotWatchCog               'Stores cod ID that runs PotWatch
      Byte SwitchMonitorCog          'Stores cog ID that runs SwitchMonitor
    
    
      Byte laserState                'Variable to store desired laser state (ON/OFF)
      Byte laserB                    'Variable to store desired laser brightness level (0-100)
    
    
    OBJ
    
    
      Resolve    : "ResolveXY"             'Used to resolve X-Y disparities
      SerialCom  : "Extended_FDSerial"     'Used for serial communications
      Pot        : "PotResolve"            'Used to resolve laser brightness disparities
      Switch     : "Switch Monitor"        'Watches for input from foot pedal
    
    
      'delete below after testing
      Debug   : "Parallax Serial Terminal"'Calls "Parallax Serial Terminal" object "Debug"
    
    
    Pub Main
    {Starts all the objects and methods then runs SerialMonitor on Cog 0}
    
    
      Resolve.Start                 'Starts 2 Cogs
      Pot.Start                     'Starts 1 Cog
      Switch.Start                  'Starts 1 Cog
      'SerialMonitor                 'Uses Cog 0
                                    'Total of 5 Cogs running
    
    
    'Delete remaining code in method when done testing
      Debug.Start(115_200) 
      repeat
        Debug.Str(String("Enter an X value: "))             'Prompt user to enter a number; uses immediate string.
        desiredX := Debug.DecIn
        Debug.Str(String("Enter an Y value: "))             'Prompt user to enter a number; uses immediate string.
        desiredY := Debug.DecIn  
    
    
    
    Other object:
    {{__________________________________________________________
    
    
    Author: Bradley Bauer
    __________________________________________________________
    
    
    Known bug: Does not accept negative "RX_______" values
    
    
    }}
    
    
    CON
    
    
      _clkmode = xtal1 + pll16x     'Crystal and PLL settings.
      _xinfreq = 5_000_000          '5 MHz crystal (5 MHz x 16 = 80 MHz)
    
    
      XPlus  =  13                  'Defines pin for  CW motion input on M1
      XMinus =  15                  'Defines pin for CCW motion input on M1
      YPlus  =  19                  'Defines pin for  CW motion input on M2
      YMinus =  16                  'Defines pin for CCW motion input on M2
    
    
      XDir   =   4                  'Defines pin for X direction output
      XStep  =   5                  'Defines pin for X axis step output
      YDir   =   6                  'Defines pin for Y direction output
      YStep  =   7                  'Defines pin for Y axis step output
             
      XMAX   = 200                  'Defines positive X step limit
      XMIN   =   0                  'Defines negative X step limit
      YMAX   = 200                  'Defines negative Y step limit
      YMIN   =   0                  'Defines negative Y step limit
    
    
      MS1    =  14            'Pin for Mode select 1              Microstep resolution selection
      MS2    =  13            'Pin for Mode select 2              See Allegro A4983 data sheet
      MS3    =  12            'Pin for Mode select 3              for truth table
    
    
      CW     =   1                  'Defines CW direction
      CCW    =   0                  'Defines CCW direction
    
    
      StepWait = 500                'Defines step pulse intervals (clkfreq/StepWait + cnt)
    
    
      Count  =  10                  '-----Defines a default count value
    
    
    VAR
    
    
      Word XCount                   'Variable to keep track of X steps
      Word YCount                   'Variable to keep track of Y steps
    
    
      Word desiredX                 'Variable to store desired X position
      Word desiredY                 'Variable to store desired Y position
    
    
      Byte XResolveCog              'Stores cog ID that runs XResolve
      Byte YResolveCog              'Stores cog ID that runs YResolve
    
    
      Long Stack1[100]              'Cog stack space
      Long Stack2[100]              'Cog stack space
    
    
    OBJ
    
    
      Debug   : "Parallax Serial Terminal"                  'Calls "Parallax Serial Terminal" object "Debug"
      
    PUB Main
    
    
      Debug.Start(115_200)
    
    
      waitcnt(clkfreq/100 + cnt)
    
    
      Debug.Str(String("Program is running..."))
      Debug.NewLine
    
    
      Start
    
    
    PUB Start
    
    
      XResolveCog := cognew(XResolve, @Stack1)
      YResolveCog := cognew(YResolve, @Stack2)
    
    
      dira[MS1..MS3]~~        'Sets Stepper drivers to 1/16 microstep mode
      !outa[MS1..MS3]         'by setting MS1, MS2, and MS3 pins high
    
    
    PRI XResolve
    
    
      dira[XDir..XStep]~~                                   'Set stepper control pins as outputs 
    
    
      repeat
    
    
        if (XCount < desiredX)
          repeat while (XCount < desiredX) and (XCount < XMax)
            Resolver(CW, XStep)
            XCount++
    
    
        else
          repeat while (XCount > desiredX) and (XCount > XMin)
            Resolver(CCW, XStep)
            XCount--
            
    PRI YResolve
    
    
      dira[YDir..YStep]~~           'Set stepper control pins as outputs
    
    
      repeat
      
        if (YCount < desiredY)
          repeat while (YCount < desiredY) and (YCount < YMax)
            Resolver(CW, YStep)
            YCount++
    
    
        else
          repeat while (YCount > desiredY) and (YCount > YMin)
            Resolver(CCW, YStep)
            YCount--
    
    
    PRI Resolver (direction, motorSelect) | DirPin
    {{Sets step direction and steps motor once}}
    
    
      if (motorSelect == XStep)
        DirPin := XDir
      else
        DirPin := YDir
    
    
      outa[DirPin] := direction      
      !outa[motorSelect]
      waitcnt(clkfreq/StepWait + cnt)
      !outa[motorSelect]
      waitcnt(clkfreq/StepWait + cnt)
    

    In a nutshell... Ask user for X and Y points then run the Resolve subroutine until there is no more disparity between desiredX and Xcount and desiredY and YCount

    I don't see how I can store a variable in the DAT block, maybe someone could show me?

    Thanks
  • Duane DegnDuane Degn Posts: 10,588
    edited 2011-11-09 15:24
    Brad,

    Your going to need have the parent object pass a pointer, to the variable, to the child object.

    Look for information in the manual about the "@" operator.

    The child method would then read the variable with word[addressOfWord].

    I don't see an advantage to having the variable in the DAT section unless muliple instances of an object are used.

    Locks are sometimes needed. It depends on the situation.

    I'll look around for an example of passing a pointer and post one if someone else doesn't bet me to it.

    Duane
  • Duane DegnDuane Degn Posts: 10,588
    edited 2011-11-09 15:42
    Brad,

    I haven't found a clear example yet.

    When the parent starts the child use:
      Child.Start(@desiredX)
    
    

    Modify the child object to:
    PUB Start(desiredXAddress)
    
      if (xCount < word[desiredXaddress])
    
    

    Hopefully that's enough to get you started.

    Look up "@" and word to see what the code above is doing.

    Duane
  • archerbrad700archerbrad700 Posts: 18
    edited 2011-11-09 15:44
    Duane,

    Thank you, an example would be great!
  • Mike GMike G Posts: 2,702
    edited 2011-11-09 20:14
    Simple Example, one process writes the other reads.
    CON
        _clkmode = xtal1 + pll16x
        _xinfreq = 5_000_000
    
    DAT
      command     byte    $00
      SemID       byte    $00
    
      
    VAR
      long ReadStack[20]
      long WriteStack[20] 
    
      
    OBJ
      pst           : "Parallax Serial Terminal"  
    
            
    PUB Main
    
      'Start PST
      pst.Start(115_200)
      pause(200)
    
      ' Get a lock ID
      if(semId := locknew) == -1
        pst.str(string("Error, semaphore Id available", 13))
      else
        pst.str(string("Semaphore Id    : "))
        pst.dec(SemID)
        pst.char(13)
    
        ' Start the Read and Write processes
        cognew(Write, @WriteStack)
        cognew(Read, @ReadStack) 
    
    
    PUB Read | value
    '' Write to the terminal when
    '' the command equals $10
    
      repeat 'forever
        'Wait for the lock to clear
        repeat until not lockset(SemID)
        if(command == $10)
          pst.str(string("We got a winner!: "))
          pst.hex(command, 2)
          pst.char(13)
        'Clear the lock 
        lockclr(SemID)
    
        ' Give Write some time to write 
        pause(1)
    
    
    PUB Write | i
      repeat  'forever
        'Wait for the lock to clear
        repeat until not lockset(SemID)
        command := i++
        pause(10)
        'Clear the lock
        lockclr(SemID)
    
        ' Give Read some time to read
        pause(1)
    
    
    PRI Pause(Duration)  
      waitcnt(((clkfreq / 1_000 * Duration - 3932) #> 381) + cnt)
      return   
    

    If the Read and Write processes were encapsulated in an object, then you would pass the address (pass by reference) of the command variable, @command, to the object's Start method. In this example, you would also pass the ID of the memory lock. Pass by value should be fine. Hope that helps.
  • archerbrad700archerbrad700 Posts: 18
    edited 2011-11-10 07:07
    Sweet, thank you both for your help. Progress has been made.

    I still have two questions though:

    1) I followed Duane's example and it is working except something is happening to the values. The code works perfectly in the single object, but when I use another cog to store the values are getting changed somehow.
    DAT
    
      XSpace Long $AAAAAA
      YSpace Long $BBBBBB
    
    
    OBJ
    
    
      Resolve    : "ResolveXY"             'Used to resolve X-Y disparities
      SerialCom  : "Extended_FDSerial"     'Used for serial communications
      Pot        : "PotResolve"            'Used to resolve laser brightness disparities
      Switch     : "Switch Monitor"        'Watches for input from foot pedal
    
    
      'delete below after testing
      Debug   : "Parallax Serial Terminal"'Calls "Parallax Serial Terminal" object "Debug"
    
    
    Pub Main
    {Starts all the objects and methods then runs SerialMonitor on Cog 0}
    
    
      Resolve.Start(@XSpace, @YSpace)                 'Starts 2 Cogs
      Pot.Start                     'Starts 1 Cog
      Switch.Start                  'Starts 1 Cog
      'SerialMonitor                 'Uses Cog 0
                                    'Total of 5 Cogs running
    
    
    'Delete remaining code in method when done testing
      Debug.Start(115_200)
      waitcnt(clkfreq/100 + cnt) 
      repeat
        Debug.Str(String("Enter an X value: "))             'Prompt user to enter a number; uses immediate string.
        long[@XSpace] := Debug.DecIn
        debug.dec(long[@XSpace])
        Debug.Str(String("Enter an Y value: "))             'Prompt user to enter a number; uses immediate string.
        long[@YSpace] := Debug.DecIn
        debug.dec(long[@YSpace]) 
    
    
    

    Resolve Object:
    DAT
    
      XSpace Long $AAAAAA
      YSpace long $BBBBBB
      
    OBJ
    
    
      Debug   : "Parallax Serial Terminal"                  'Calls "Parallax Serial Terminal" object "Debug"
      
    PUB Main
    
    
      Debug.Start(115_200)
    
    
      waitcnt(clkfreq/100 + cnt)
    
    
      Debug.Str(String("Program is running..."))
      Debug.NewLine
    
    
      Start(@XSpace,@YSpace)
    
    
    PUB Start(desiredXaddress, desiredYaddress)
    
    
      XResolveCog := cognew(XResolve(desiredXaddress), @Stack1)
      YResolveCog := cognew(YResolve(desiredYaddress), @Stack2)
    
    
       dira[MS1..MS3]~~       'Sets Stepper drivers to 1/16 microstep mode
      !outa[MS1..MS3]         'by setting MS1, MS2, and MS3 pins high
    
    
      'Delete remaining code in method when interfaced with LaserPOP-Parent
     { repeat
        Debug.Str(String("Enter an X value: "))             'Prompt user to enter a number; uses immediate string.
        long[@XSpace] := Debug.DecIn
        Debug.Str(String("Enter an Y value: "))             'Prompt user to enter a number; uses immediate string.
        long[@YSpace] := Debug.DecIn  
      }
    PRI XResolve(desiredXaddress)
    
    
      dira[XDir..XStep]~~           'Set stepper control pins as outputs
    
    
      repeat
    
    
        if (XCount < long[desiredXaddress])
          repeat while (XCount < long[desiredXaddress]) and (XCount < XMax)
            Resolver(CW, XStep)
            XCount++
    
    
        else
          repeat while (XCount > long[desiredXaddress]) and (XCount > XMin)
            Resolver(CCW, XStep)
            XCount--
            
    PRI YResolve(desiredYaddress)
    
    
      dira[YDir..YStep]~~           'Set stepper control pins as outputs
    
    
      repeat
      
        if (YCount < long[desiredYaddress])
          repeat while (YCount < long[desiredYaddress]) and (YCount < YMax)
            Resolver(CW, YStep)
            YCount++
    
    
        else
          repeat while (YCount > long[desiredYaddress]) and (YCount > YMin)
            Resolver(CCW, YStep)
            YCount--
    
    
    

    2) I'm still not sure how Mike's code applies to this, but I'm all for an explanation... It perhaps is what's causing my problem?

    Thanks again
  • Mike GMike G Posts: 2,702
    edited 2011-11-10 12:40
    1) I followed Duane's example and it is working except something is happening to the values. The code works perfectly in the single object, but when I use another cog to store the values are getting changed somehow.

    Sounds like you need a memory lock.
    I'm still not sure how Mike's code applies to this, but I'm all for an explanation... It perhaps is what's causing my problem?
    The code simply spins up two process in separate COGs; 1 and 2. Each process has access to the variable created in the top level object. You could, move the Read and Write process to separate files, reference the objects, and start the objects in the top level object. Pass the command variable by reference; @command, to the referenced objects (as you did) along with the semaphore. In the child objects, make sure memory access is not checked out by another process. If memory is available check out the memory lock, Read/Write, release the lock.

    The Propeller manual has an excellent explanation of memory locks.
Sign In or Register to comment.