Shop OBEX P1 Docs P2 Docs Learn Events
How to test the return value of a function launched in to a new cog? — Parallax Forums

How to test the return value of a function launched in to a new cog?

rwgast_logicdesignrwgast_logicdesign Posts: 1,464
edited 2013-02-07 23:47 in Propeller 1
Ok im still really struggling to wrap my mind around how deal with the parallel processing paradigm, im not sure if I just havent read far enough through the spin books yet, or if I just cant get my thinking on track!! Anyways im writing a driver for the polar Heart Rate Reciver, basically the hardware just flips on hi every time time it detects a beat, very simple hardware interface.

So the first function im writing is to just wait till the sensor returns a HI, and set a variable to one. Im trying to launch part of this in a second cog which runs waitpne, then shuts down the cog when the pin is pulled hi. Basically I just want the user to be able to call a function which will return 1 if there is a heart beat detected... here is what ive written
{{   FILE: pHeartRateReceiver.spin


    This is a simple Driver interface for the Wireless Polar Heart Rate Receiver, that is supplied
    with the Parallax MicroMedic Kit. The Heat Rate Sensor comes with the Polar T34 Heart Rate
    Trasnsmitter which straps around your chest, and a small 3 pin Reciever module which is compatible
    with any basic servo header found on Parallax Dev Boards, a servo header is not required though.


   SCHEMATIC:
                                        
                            Wireless    ┌----------┐       
                             Signal     | Receiver |─────── To Ground/Vss
     ┌------------------┐           |  Module  |─────── To + 3.3v/5.0v
     | Transmitter Band |           |          |────┐              
     └------------------┘           └----------┘                      
                                             Propeller I/O Pin of Choice


    Please change receiverPIN, in CON section, to match the Propeller I/O pin wich you have connected
    Polar Heart Rate Reciver's signal pin too. 
                                             
}}
CON
   receiverPIN = 19  'Change to Prop PIN the Polar Receivers signal line is connected too
VAR


   LONG isBeatingSTACK[50]
   BYTE polarHeart_COG


PUB isSensorUp | newCOG
   newCOG := cognew(waitForBeat, @isBeatingSTACK)


PRI waitForBeat : isBeating
   waitpne( |< receiverPIN, |< receiverPIN, 0)
   isBeating := 1

Once I started writing the public method isSensorUp I realized I cant get the return value of waitForBeat, or at least I don't know how because it is in a new cog. My thinking would usually be to do something like,
if waitForBeat == 1
   do something or another

but wont that then launch waitforbeat, in the same cog that isSensorUp is running in along with the cog that, cognew launched it in to? Maybe im just over thinking all this or something, but im getting really confused and frustrated.

Comments

  • Mike GreenMike Green Posts: 23,101
    edited 2013-02-03 21:44
    When you do the COGNEW, the new cog will call waitForBeat and that will return once the waitpne is satisfied, but it will return to a COGSTOP instruction internal to the Spin interpreter and the return value isBeating will disappear and you will never know what happened or when.

    Do you actually need to do something in a separate cog? Couldn't your main program wait for the heartbeat? Do you want to just see if a heartbeat has occurred while you're doing something else? How about if you use one of the cog counters instead? You initialize the counter's PHSx register to zero and FRQx register to 1, then set the counter into POSEDGE mode (%01010). Whenever the signal pin goes from 0 to 1, the PHSx counter will increment and your program can just check for non-zero. Look at AN001 for details. You could even see how many pulses occurred in a period of time (using CNT to tell you the elapsed time ... up to about 50 seconds).
  • rwgast_logicdesignrwgast_logicdesign Posts: 1,464
    edited 2013-02-04 13:04
    Well the reason I was doing this in a separate cog is so so waitpne wouldnt lock up the main program, I also planned on using waitcnt to average out beats per minute. I really would just rather start the whole object in a separate cog. I will look in to maybe using the counters instead/too. Im definitely not set on the way im trying to write this code, I just figured it was a pretty simple piece of hardware to write a driver for and learn more about multi processing.
  • Beau SchwabeBeau Schwabe Posts: 6,566
    edited 2013-02-04 14:03
    Since you were so close. I went ahead and edited your code to do what I think it is that you are wanting to do....
      _clkmode = xtal1 + pll16x                                                     'Must Set crystal and clock for serial terminal communication
      _xinfreq = 5_000_000
    
    
    {{   FILE: pHeartRateReceiver.spin
    
    
        This is a simple Driver interface for the Wireless Polar Heart Rate Receiver, that is supplied
        with the Parallax MicroMedic Kit. The Heat Rate Sensor comes with the Polar T34 Heart Rate
        Trasnsmitter which straps around your chest, and a small 3 pin Reciever module which is compatible
        with any basic servo header found on Parallax Dev Boards, a servo header is not required though.
    
    
       SCHEMATIC:
                                            
                                Wireless    &#9484;----------&#9488;       
                                 Signal     | Receiver |&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#61627; To Ground/Vss
         &#9484;------------------&#9488;  &#61629;&#61627;       &#61629;&#61627;  |  Module  |&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#61627; To + 3.3v/5.0v
         | Transmitter Band |  &#61629;&#61627;       &#61629;&#61627;  |          |&#9472;&#9472;&#9472;&#9472;&#9488;              
         &#9492;------------------&#9496;  &#61629;&#61627;       &#61629;&#61627;  &#9492;----------&#9496;    &#61602;                  
                                                 Propeller I/O Pin of Choice
    
    
        Please change receiverPIN, in CON section, to match the Propeller I/O pin wich you have connected
        Polar Heart Rate Reciver's signal pin too. 
                                                 
    }}
    CON
        receiverPIN = 19  'Change to Prop PIN the Polar Receivers signal line is connected too
    
    OBJ
    
    PST     : "Parallax Serial Terminal"                                            'Setup Serial Terminal                          DEBUG
        
    VAR
    
        LONG isBeatingSTACK[50]
        LONG isBeating
        BYTE polarHeart_COG
    
    
    PUB isSensorUp | newCOG
    
        PST.Start(19200)                                                            'Start Serial Terminal                          DEBUG
    
        newCOG := cognew(waitForBeat(@isBeating), @isBeatingSTACK)                  'Start new cog passing the adress of isBeating as arg0
    
    
        repeat
          PST.DEC(isBeating)                                                        'Display isBeating value                        DEBUG
          PST.Char(13)                                                              'New Line                                       DEBUG
    
        
    
    
    PRI waitForBeat(arg0)
        waitpne( |< receiverPIN, |< receiverPIN, 0)
        long[arg0] := 1                                                             'Update isBeating via the passed address
    
  • rwgast_logicdesignrwgast_logicdesign Posts: 1,464
    edited 2013-02-04 18:12
    Thank you Beau, I will try this out when I get a few spare minutes and see how it works out. In my head im thinking about the driver being a nice interface for the coder like any API would be, But understanding exactly how to do this using multiple processors so you dont tie up your main loop, and not going overboard with the cognew function is where Im really getting hung up.

    If your code doesnt end up helping to enlighten me a little more, im just going to write everything in to a single cog and then come back to it after reading a bit more, and browsing some examples, to implement the multi tasking.
  • MagIO2MagIO2 Posts: 2,243
    edited 2013-02-04 23:48
    I'm just thinking a bit about what happens if a COG runs into the outermost return statement. Shouldn't it be executed as any return-statement? Isn't the return variable created on the stack? Does a return work like that:
    1. write the return value to the return variable
    2. get the return-address
    3. jump to the return-address
    4. decrement stack-pointer
    5. if there is an assignment of the return-value, the value of the return-variable is copied
    ...

    The return in a function which is called via cognew would not do anything different. The only exception is, that the return-address it finds on the stack points into some ROM-code which is doing a COGSTOP.
    The difference for the caller is, that there is definitely no assignment, as a cognew does not wait. But shouldn't the return-variable still be available on the stack used in cognew?

    Nice investigation task ;o) ... unless someone else already knows.
  • MagIO2MagIO2 Posts: 2,243
    edited 2013-02-05 04:08
    So .. nice lunch-break today ;o)

    Here is my test-code:
    con
      _CLKMODE      = XTAL1 + PLL16X                        
      _XINFREQ      = 5_000_000
    
    obj
      term: "CogOS_IO(Term)"
    
    VAR
      long myStack[10]
      long sync
         
    pub testit | ext,idx,tmp
      ' Initialize terminal
      term.start( 0,0 )
    
      ' wait until any key has been pushed
      tmp:=-1
      repeat while tmp==-1
        term.str( string( "Test CognewReturn",$0d ) )
        term.str( string( "== please press key to start ==",$0d ) )
        tmp:=term.rxtime( 1000 )
      term.tx( 0 )
    
      ' print the stack before starting the COG
      printStack
      sync:=0
      cognew( doSomething, @myStack )
    
      ' wait until the COG is up and running
      repeat until sync==1
      printStack
      sync := 2
    
      ' wait til the result variable has been set
      repeat until sync==3
      printStack
      sync := 4
    
      ' print the final stack (hopefully return is faster than printStack, but at least from 2nd value on it should show the right content ;o)
      printStack
    
    pub printStack | i
      repeat i from 0 to 9
        term.hex( myStack[ i ], 8 )
        term.tx( " " )
      term.tx( 13 )
    
    pub doSomething
      sync := 1
      repeat until sync==2
      
      result:=$a5a5a5a5
      sync := 3
      
      repeat until sync==4
      
      return $5a5a5a5a
    
    Well CogOS_IO(Term) is a simple wrapper and it should be possible to simply replace it with FullDuplexSerial when adapting the start-function according to your settings.

    And here is my result:
    00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    FFFFFFFF FFF9FFFF 00000000 00000001 00000002 043C00CE 00000000 00000000 00000000 00000000
    FFFFFFFF FFF9FFFF A5A5A5A5 00000000 00000004 043C00CE 00000000 00000000 00000000 00000000
    FFFFFFFF FFF9FFFF A5A5A5A5 5A5A5A5A 00000004 043C00CE 00000000 00000000 00000000 00000000
    

    And as we can see both, the return variable named "result" AND a value set by return instruction are available after returning from the function. So, I'd consider it to being safe for the caller to simply check the stack with myStack[2] or myStack[3] after termination of the SPIN-COG, as the caller should be the real master of the stack. These longs could safely be used as a message-box as well.
  • rwgast_logicdesignrwgast_logicdesign Posts: 1,464
    edited 2013-02-07 15:30
    @Beau, I just realized you added xtal settings to the file. I was just doing this in my TOP demo file, what is the standard way to do this for an object? I would think leaving it out so the user can set the specific boards frequency is the best idea?
  • msrobotsmsrobots Posts: 3,709
    edited 2013-02-07 22:30
    @MagIO2,

    hm. Interesting. But why is there result AND return? My understanding from spin was that setting result to a value equals returning that value. But it ends up in two locations on the stack! very interesting.

    Enjoy!

    Mike
  • Beau SchwabeBeau Schwabe Posts: 6,566
    edited 2013-02-07 23:20
    rwgast_logicdesign,

    "@Beau, I just realized you added xtal settings to the file" - I was doing this mainly for the serial DEBUG aspect of the program. It's certainly valid to set the xtal settings at a higher hierarchical level in your code.
  • MagIO2MagIO2 Posts: 2,243
    edited 2013-02-07 23:47
    @msrobots:
    I did not know this before my testing and actually I can't say why it is like that! Maybe it is somehow related to the abort and catch. Would require some more testing.
Sign In or Register to comment.