Shop OBEX P1 Docs P2 Docs Learn Events
Passing Values between objects with Cogs. — Parallax Forums

Passing Values between objects with Cogs.

The fact that all serial objects pass data to AND from the top objects that called them, tells me this is possible.
But even in my brief P1 Days I wasn't able to fully understand it. Passing data to the child objects was easy enough, but navigating the address space to get data back to the top was frustrating.

The Top object here calls three instances of the child object. Sending each a time value to stay lit.

What is the process that would enable the child to tell the top how long it took to complete is program?

top Object:
{Cog usage with child control via shared register"sync".}

{pin 0----------(gLed)------\
 pin 1----------(yLed)------|---(330 ohm res)--gnd
 pin 2----------(rLed)------/
}
CON
  CLK_FREQ = 5_000_000
  _clkfreq = CLK_FREQ
  sec      = 1000
VAR
  long  sync
  long  cognum
  long  StkAddr[100]
  long  StkAddr1[100]
  long  StkAddr2[100]
OBJ
  rLed      : "ContolledChild"
  gLed      : "ContolledChild"
  yLed      : "ContolledChild"
pub main()| okay,c
 cognum:=COGSPIN(newcog, gLed.On(0,sec,@sync) , @StkAddr)
 cognum:=COGSPIN(newcog, yLed.On(1,sec,@sync) , @StkAddr1)
 cognum:=COGSPIN(newcog, rLed.On(2,sec,@sync) , @StkAddr2)
 repeat
  repeat c from 0 to 2
    sync:=c
    waitms(sec)


Child Object:
{Basic child with parent variable used for pin synchronization}
VAR
  long  pin
  long  duration
  long  sync
  long  syncAddr
PUB On(num,d,s)
  pin:=num       'Save passed parems
  duration:=d
  syncAddr:=s     'Save passed parent control variable address to local var
repeat
  LONGMOVE(@sync, syncAddr, 1) ' Move value at parent address to local var
  toggle()
pri Light()
  pinhigh(pin)
PRI delay()
  waitms(duration)
  pinclear(pin)
PRI toggle()| t
 t:=getct()  
  if sync==pin
   Light()
   delay()
 t:=getct()-t-48
   return

Comments

  • JonnyMacJonnyMac Posts: 9,104
    edited 2020-12-19 03:16
    To start timing an event -- assuming it's less than about 10 seconds -- you can grab the cnt register
      elapsed := getct()
    
    Run through your code and when you want to end timing, using this line:
      elapsed := getct() - elapsed - 40
    
    The value in elapsed is the the code execution in system ticks.
    If the expected run time is more than 10 seconds, you can use the latest version of PNut and the getms() function.
      elapsed := getms()
    
      ' run your code
    
      elapsed := getms() - elapsed
    
    This is not as fine as using the cnt register, but for durations that long, 1ms resolution is probably okay.

    A few minutes later... I see you have something like my suggestion in your child object. I guess I don't understand the question.

    Suggestion: Use one resistor per LED. Differences in the LED forward voltage can create problems with Parallel LEDs (the one with the lowest Vf will light, the others will not).
  • Thanks Jon, And I did have some minor questions along those lines. Maybe My magic number 48 has you scratching your head.
    But I digress!
    For this lesson the value is irrelevant. Any none 0 value could let top know when to change the red light!

    How to get it back to the top object. I've tried get statements(The cog throws that off)
    I've tried longmoves(but where from and where to)
    Not even considering wasting a couple pins for cog cog comms.
  • bambino69bambino69 Posts: 126
    edited 2020-12-19 03:41
    The setup works. the red light flashes from green to yellow to red endlessly.
    But the timing of each cog was done trial and error.
    If the child object cog could say to the top object "Hey I'm red and I just turned off" then the top would know he could safely turn on the green light.


    ps. By trial and error I mean at the speed where running the light looks like a functioning red light, But technically the top object is assuming each child finishes in the allotted second
  • I may not understand you, but now I think you want to pass control between processes running in separate cogs. Consider this:
    pri flasher(p_baton, myID, nextID, mypin) | baton
    
      repeat
        repeat
          baton := byte[p_baton]                                    ' wait for my turn
          if (baton == myID)
            quit
    
        term.fstr1(string("Process %d has the baton.\r"), myID)     ' announce
        pinh(mypin)                                                 ' show off
        waitms(prng.xrandomize(100, 500))
        pinl(mypin)
        
        byte[p_baton] := nextID                                     ' pass baton to next
    
    This will wait for its ID to be placed into an shared hub location. When it's done, it passes the baton to the next process by putting that process ID into the shared location.
  • Ok, in "jm_fullduplexserial" . It starts its on cog, and data produced by the receive buffers can be leaved in the top object thru the use of rx(0).

    Top object........child object.......cog in child object........data
    data<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<data

    The process of getting data from a cog in a child up to the top object.

    In my code above.
    repeat
      LONGMOVE(@sync, syncAddr, 1) ' Move value at parent address to local var
      time := toggle()
    

    I would wont to send time back to the top object. I'm using time here but a boolean would work too.
  • Granted, this code starts the cogs in the top object, but still cant see how to do it
  • bambino69bambino69 Posts: 126
    edited 2020-12-19 05:08
    Here is the original example. This one failed miserably.
    {Child object for driving pins,with timeing controlled by parent.
     Self cog usage}
    VAR
      long  pin
      long  duration
      long  sync
      long  syncAddr
      long  StkAddr[100]
    pub init(num,d,s) : mySync
      pin:=num
      duration:=d
      syncAddr:=s
      COGSPIN(newcog, On() , @StkAddr)
      mySync:= @syncAddr
    pri On()
    repeat
      LONGMOVE(@sync, syncAddr, 1)
      if sync==pin
       Light()
       delay()
    pri Light()
      pinhigh(pin)
    PRI delay()
      waitms(duration)
      pinclear(pin)
    

    Would like to write it this way though, with the cog started in the child object, But the sync value wouldn't make it to the cog.
  • JonnyMacJonnyMac Posts: 9,104
    edited 2020-12-19 05:28
    Jumpin' Jiminy on a cracker...
    This one failed miserably.
    Which is why I ignored it and rolled my own.

    You can always have the slave process write 0 back to the shared location so that the foreground process can see that it's done and send control to another process.
    pri flasher(p_baton, myID, mypin) | baton
    
      repeat
        repeat
          baton := byte[p_baton]                                    ' wait for my turn
          if (baton == myID)
            quit
    
        term.fstr1(string("Process %d has the baton.\r"), myID)     ' announce
        pinh(mypin)                                                 ' show off
        waitms(prng.xrandomize(100, 500))
        pinl(mypin)
    
        byte[p_baton] := 0                                          ' tell supervisor we're done
    
  • Just to know it's working I set the top duration to 5 sec, but in the child I hard coded it to 1 sec
    {Cog usage with child control via shared register"sync".}
    
    {pin 0----------(gLed)------\
     pin 1----------(yLed)------|---(330 ohm res)--gnd
     pin 2----------(rLed)------/
    }
    CON
      CLK_FREQ = 5_000_000
      _clkfreq = CLK_FREQ
      sec      = 5000
    VAR
      long  sync
      long  cognum
      long  StkAddr[100]
      long  StkAddr1[100]
      long  StkAddr2[100]
    OBJ
      rLed      : "ContolledChild"
      gLed      : "ContolledChild"
      yLed      : "ContolledChild"
    pub main()| okay,c
     cognum:=COGSPIN(newcog, gLed.On(0,sec,@sync) , @StkAddr)
     cognum:=COGSPIN(newcog, yLed.On(1,sec,@sync) , @StkAddr1)
     cognum:=COGSPIN(newcog, rLed.On(2,sec,@sync) , @StkAddr2)
     repeat
      repeat c from 0 to 2
        sync:=c
        repeat until c<>sync
    

    Child object
    {Basic child with parent variable used for pin synchronization}
    VAR
      long  pin
      long  duration
      long  sync
      long  syncAddr
    PUB On(num,d,s) 
      pin:=num       'Save passed parems
      duration:=1000
      syncAddr:=s     'Save passed parent control variable address to local var
    repeat
      LONGMOVE(@sync, syncAddr, 1) ' Move value at parent address to local var
      sync:=toggle()
    pri Light()
      pinhigh(pin)
    PRI delay()
      waitms(duration)
      pinclear(pin)
    PRI toggle(): t
     t:=getct()  
      if sync==pin
       Light()
       delay()
     t:=getct()-t-48
       return  t
    

    Green light fires and stays lit.
    code is stuck here.
    repeat until c<>sync
    
    [/code]
  • Tried writting back to s as well:
    PUB On(num,d,s)
    
  • bambino69bambino69 Posts: 126
    edited 2020-12-19 07:19
    To make it simpler I changed it to this.
    {Cog usage with child control via shared register"sync".}
    
    {pin 0----------(gLed)------\
     pin 1----------(yLed)------|---(330 ohm res)--gnd
     pin 2----------(rLed)------/
    }
    CON
      CLK_FREQ = 5_000_000
      _clkfreq = CLK_FREQ
      sec      = 1000
    VAR
      long  sync
      long  cognum
      long  StkAddr[100]
      long  StkAddr1[100]
      long  StkAddr2[100]
    OBJ
      gLed      : "CC"
      rLed      : "CC"
    pub main()| okay,c
     cognum:=COGSPIN(newcog, rLed.On(0,sec,@sync) , @StkAddr)
     sync:=5
     repeat until sync==10
     cognum:=COGSPIN(newcog, gLed.On(0,sec,@sync) , @StkAddr1)
    
    and
    {Basic child with parent variable used for pin synchronization}
    VAR
      long  pin
      long  duration
      long  sync
      long  syncAddr
    PUB On(num,d,s)
      pin:=num       'Save passed parems
      duration:=d
      syncAddr:=s     'Save passed parent control variable address to local var
    
      LONGMOVE(@sync, syncAddr, 1) ' Move value at parent address to local var
      Light()
      delay()
      sync:=10        'Tried sync, s , even with the syncAddr thru longmoves
      repeat
    pri Light()
      pinhigh(pin)
    PRI delay()
      waitms(duration)
      pinclear(pin)
    

    I would expect to see the red light come on after the green goes out.
    So how would I make the top object see sync as 10
  • bambino69bambino69 Posts: 126
    edited 2020-12-19 06:03
    I tried
    byte[sync]:=10 and long[sync]:=10.
    
    I tried
    byte[s]:=10 and long[s]:=10.
    
  • I would expect to see the red light come on after the green goes out.
    Funny... both the programs I posted (that you seem to have ignored) did that.
    So how would I make the top object see sync as 10
    I showed you. Pass the address of your sync variable to the background cog. Have the background cog wait for a trigger value in the sync location to run its process. When that process is done it writes a "finished" value back to the sync address.

    Easy-peasy. JonnyMac out!!!
  • Ignore no, Just dense I guess!
    How is this:
    byte[p_baton] := 0
    
    Different from this:
    byte[s]:=10
    
    Or this:
    pri flasher(p_baton, myID, mypin) | baton
    
    From this:
    PUB On(num,d,s)
    

    I looked at your code and saw where you declared p_baton and in my code its "s" in the "On" routine.
    Something more in your code I not implementing.


  • Anyway Thanks. I'll try again tomorrow.
  • bambino69bambino69 Posts: 126
    edited 2020-12-19 16:08
    @JonnyMac
    In going back thru the discussion I noticed your zip file. I did not know you had posted that.
    Thanks, That code works as good as mine.

    Both led routines are in their own cogs.
    However, yours are in the same file with the top object, Yes, that would be as you say...
    "Jumpin' Jiminy on a cracker..."
    But I did not ask how to do that.

    Imagine your flasher routine in its own object, and its cog being started in said object. How would it pipe data back to the Top Object?
    My code passes the baton, but cant get it back.

    Hopefully I can get back on it tonight!
  • JonnyMacJonnyMac Posts: 9,104
    edited 2020-12-19 17:39
    This code is self-documenting:
    pri flasher(p_baton, myID, mypin) | baton
    
    This code is not.
    PUB On(num,d,s)
    

    Thanks, That code works as good as mine.
    No, according to your complaints, my code works better because it can tell when the external process is done.

    Yeah, yeah, I know you want an external file -- but that's a Smile argument here because the mechanism is the same: the cogs simply need to what location in memory they're using as a mailbox. Want proof? Run the attached demo. I took my internal object code and moved it to an external object -- that is, a proper object with start() and stop() methods. I also added the ability to pass the timing to the external object from the master -- again, this has to do with an agreement. In this case, the mailbox is a long defined in the top object, and the timing is the next long in memory. The external process knows this so it can pick up the timing. This can be extended to as many values as you like -- there just has to be agreement on both sides.

    You probably missed my presentation on Propeller programming. One of the most important things I said is that you should build to things incrementally. I've been programming the Propeller for 15 years (since before the P1 release) and I've had a lot of time to develop working strategies.
    But I did not ask how to do that.
    Did you ever see the movie "The Karate Kid"? In that movie, Daniel didn't ask to be taught how to wax cars, but Mr. Miyagi made him do it as it would provide a foundation for what was to come. If I or one of the other forum old-timers give you information, you can -- and should -- assume it's for good reason.
  • AribaAriba Posts: 2,690
    This works fine for me:
    Parent object:
    {Cog usage with child control via shared register"sync".}
    
    {pin 0----------(gLed)------\
     pin 1----------(yLed)------|---(330 ohm res)--gnd
     pin 2----------(rLed)------/
    }
    CON
      _clkfreq = 100_000_000
      sec = 1000
    
    VAR
      long  sync1,sync2,sync3
      long  cognum
      long  StkAddr[100]
      long  StkAddr1[100]
      long  StkAddr2[100]
    
    OBJ
      rLed      : "BambinoChild"
      gLed      : "BambinoChild"
      yLed      : "BambinoChild"
    
    pub main()| okay,c
     cognum:=COGSPIN(newcog, gLed.On(0, 1200, @sync1) , @StkAddr)
     cognum:=COGSPIN(newcog, yLed.On(1, 1500, @sync2) , @StkAddr1)
     cognum:=COGSPIN(newcog, rLed.On(2, 1900, @sync3) , @StkAddr2)
     repeat
      if sync1 > 0          'duration done?
        debug(udec(sync1))  'show time
        sync1 := 0          'trigger next cycle
      if sync2 > 0
        debug(udec(sync2))
        sync2 := 0
      if sync3 > 0
        debug(udec(sync3))
        sync3 := 0
    

    Child Object "bambinoChild":
    {Basic child with parent variable used for pin synchronization}
    
    VAR
      long  pin
      long  duration
      long syncptr
    
    PUB On(num,d,s)
      pin:=num       'Save passed parems
      duration:=d
      syncptr := s
      repeat
        long[syncptr] := toggle()
    
    PRI toggle() : t
      t:=getct()
      repeat until long[syncptr]==0   'trig from parent
      pintoggle(pin)
      waitms(duration)
      t:=getct()-t-48
      return
    
    It's remarkable that you can start methodes in objects from the parent object in Spin2, that was not possible with Spin1, you needed a start routine that started the cog in the child object.

    Andy
  • Thanks again Johnny Mac, Looking forward to going over the new zip File. I does look to to be exactly what I was asking about.
    It's important to me to be able to compartmentalize my learning in child objects, for a library, if you will, of coding strategies.

    My personal strategy is to write spin, then try to do it in asm.

    Thanks to you Andy.
    Indeed that was one of my reasons for it. Don't remember much of my code from 2008-2009, but I do remember not being able to do this in P1. That memory is what caused me to wonder if it had changed in P2. I can remember blowing up my file size by putting serial objects every where to get my debug statements out of the chip.
Sign In or Register to comment.