How to test the return value of a function launched in to a new cog?
rwgast_logicdesign
Posts: 1,464
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
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,
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.
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
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).
_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 ┌----------┐ 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 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 addressIf 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.
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.
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 $5a5a5a5aWell 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:
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.
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, 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.
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.