Reading Global variables from any operating Cog
Is it possible for independant Cogs to read data in a Global variable which has been set in another Cog, without using the arg/par path?
If so, is there an example on how to do it?
An example of what I mean:
Say there is a controlling Cog (A.spin) that has a variable named NUMBER, and it is continuously varying the value of NUMBER.
The first Cog has called two other Objects(B.spin & C.spin), which in turn start their own Cog.
The purpose of B & C is to monitor the value of NUMBER and react (differently) when it reads a particular value (operates a LED).
How do B & C read the Global variable without a arg/par pass action?
Of course the A Cog could pass the value every time it is updated, but this would mean restarting the Cogs each time.
The situation required is for the B & C Cogs to monitor NUMBER continuously.
This is a simplistic example of course, just a means of discussing/understanding of the Global variables situation.
kenmac
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Perth, Western Australia
Time Zone = GMT + 8
If so, is there an example on how to do it?
An example of what I mean:
Say there is a controlling Cog (A.spin) that has a variable named NUMBER, and it is continuously varying the value of NUMBER.
The first Cog has called two other Objects(B.spin & C.spin), which in turn start their own Cog.
The purpose of B & C is to monitor the value of NUMBER and react (differently) when it reads a particular value (operates a LED).
How do B & C read the Global variable without a arg/par pass action?
Of course the A Cog could pass the value every time it is updated, but this would mean restarting the Cogs each time.
The situation required is for the B & C Cogs to monitor NUMBER continuously.
This is a simplistic example of course, just a means of discussing/understanding of the Global variables situation.
kenmac
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Perth, Western Australia
Time Zone = GMT + 8

Comments
Use arg/par to pass a pointer to the global variable between the various cogs. The various cogs can then dereference the pointer back to the global variable.
If you REALLY don't want to use arg/par, then pick a Number (any Number between 0 and 32767) and reference it like this in each of your cogs:
byte[noparse][[/noparse]Number] := NewValue 'For a byte sized global value
word[noparse][[/noparse]Number] := NewValue 'For a word sized value, but must be mod 2 (0, 2, 4 ...)
long[noparse][[/noparse]Number] := NewValue 'For a long sized value, but must be mod 4 (0, 4, 8 ...)
Just be careful with your choice of Number though, as you're accessing directly into the RAM and are very likely to corrupt your program if you're not careful. (Hence the reason for passing the pointer).
A pointer to GlobalVariable is quite easy to get: @GlobalVariable
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
(1) COGs and "objects" have nothing to do which each other in any way.
(2) The DAT area upto 512 LONGSs from the starting point of a COG is copied, thus EVERYTHING you have stored there (statiically or dynamically) will be copied into the COG
(3) Note however that
will not work, as the address of a VAR Element cannot be determined at compile time, as there can be multiple instantiations of an object. You cann of course set those values at run time
(4) But
will work fine
(5) Beware changing that area while the COG is loading (which takes around 102.4 mys)
Post Edited (deSilva) : 7/18/2007 5:23:04 PM GMT
I am trying to use the propeller to control a pololu serial motor controller. I would like to be able to have one cog(cog 0) control the motor while another cog(cog 1) waits for information(keystrokes) to come in; these keystrokes will modify the motor speed or direction in some way. I have already figured out how to control the motor by sending it four seperate bytes: start byte, motor number, direction, and speed. I would like cog 0 to run the motor while cog 1 is looking for new commands. Then, after cog 1 has proccesed the new commands I would like it to send information back to cog 0 (motor direction and speed).
If that didn't make any sense, I guess I would just like to know how to pass variables from one cog to another.
Thanks
Also, if anybody already has some code written for the pololu controller that would be very helpful.
Something that may get tricky is where you need to read several variables and be sure they are all up to date so you are not getting incomplete writes (say reading X, Y, Z - maybe X has been changed by a cog, but Y and Z are not yet written too). This is where you can start using the locks to ensure one cog doesn't use data that's not ready yet.
-Martin
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
StampPlot - GUI and Plotting, and XBee Wireless Adapters
Southern Illinois University Carbondale, Electronic Systems Technologies
But Google or Wikipedia didn't explain this word. What does it mean? Never known about it before.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Harley Shanko
h.a.s. designn
I think this is only true if you write your code as a *single* Spin file. By the way, I have considered doing this - 10000+ lines of spin and assembly (one source file) containing all the objects·just to have better/easier shareability of·variables.
I don't know what you mean by 'one spin file'.· Using objects, it's all compiled to be one file.·All variables for Spin are held in main RAM. All cogs have access. If you are saying that lower objects don't know the name of the global variables declared in the top file, this is probably true.
Even using objects that may not be part of the top file, it's not very hard to pass the address to an object.
The top spin file can be used to initialize the various cogs with thier spin code and at that time passed the variables locations to use through routines in the top file.
-Martin
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
StampPlot - GUI and Plotting, and XBee Wireless Adapters
Southern Illinois University Carbondale, Electronic Systems Technologies
You are correct about variable access. One Spin file cannot access the variables in another. It can access the named constants and any public methods.
There is a "tension" between being able to share things widely and hiding things like internal object variables to help create the abstraction. This is one of the "features" of object oriented programming. It has tremendous value in terms of helping to prevent errors and helping to simplify conceptually a program and its pieces. It's also sometimes an annoyance and sometimes a (small) source of inefficiency.
After having written many tens of thousands of lines of code in many different programming languages, I would prefer to use all the tools at hand to help with creating abstractions and well-behaved objects that force me to think about how I'm using them and what am I really trying to accomplish at any particular point. Once I have something working as expected, I'm very happy to go back and look for inefficiencies and fix them. Often, they're not what you think they might be.
I strongly suggest you don't make a 10 thousand+ lines of code program in one piece. Use objects and use them where they fit in functionally. You will find that their use will save you a lot of headaches in a program of that size.
Where I'm able to use the facilities of the language, I have. In other places I've modified existing objects (eg SerialMirror which is a variation on FullDuplexSerial) to make it work·sensibly across multiple objects. Of the 8 cogs used in my current design 2 are spin, the other 6 are highly tuned assembly language (1 of the remaining spin cogs will probably need to go to assembly in the final design, but I'll see later if that's needed). All these various parts (cogs)·build together to make a reasonably high performance data aquisition system. Tying it all together·is about 9 FIFO's (don't forget that even FullDuplexSerial has got 2 FIFO's - otherwise known as circular buffers)·which pump the data between the various·cogs/processes. The FIFO's are used to absorb system latencies - which are significant because of the occassional slow-downs that are inherent when writing to an SD card / Serial Port / Ethernet Chip etc.
Currently the project is·split into 10 source files. I'll share the 2 ethernet files (ENC28J60 based) and probably my new SD Card driver (based on stuff learned while doing the Ethernet driver) once I'm done. The Ethernet code is an illustration of how to work within the limitations of the language - but you'll just have to believe me for now.
I did say I was "considering" combining the files into a mega-file - that doesn't mean I've done it. I have found that sharing of global variables is a·place where the code is harder to write and debug. It's all working at the moment, but it's been slow going getting the cogs to talk to each other though all those FIFOs.·If I had more cogs, then I'd porbably use more SPIN cogs to tie the various parts together which would make it a lot easier, but I'm out of cogs, so I just have to make it work as it is. (BTW: no 2 cogs are performing the same function). All debugging has been done using GEAR (which is what drove me to contirbute changes to it) and a serial port.
I think·I probably have a fair appreciation of how to write a propeller based application considering I started in April.·After using the chip for the last couple of months, I still love it! Some of the code is challenging to write. I wish that SPIN was a bit faster. I wish that there was an option for·easier access to variables across objects.
I'm interested to see the new C compiler - I suspect that some of these things will be made easier, but that other stuff is going to remain just as obscured.
---
As a little thought exercise, consider FullDuplexSerial. You want a seperate assembly language cog to be able to do some processing and put the results·directly into the TxBuffer of FullDuplexSerial. Using seperate source files, please.··Your time starts now . . . (It's not too difficult, but does expose the challenges of global variables).
·
Thanks for the info.
I've read the other replies and it is somewhat confusing.
Could you please expand a bit re how the receiving Cog uses the "@GlobalVariable" number to read the contents of the variable.
I assume that the Cog that declares the variable Number does something like "x.start(@Number)...."
How do we set up the receiving Cog to interpret it to read the value in the variable Number?
It would have "pub start(xxxx)"
followed by cognew(checkno, yyyy) ....
I'm not sure what the parameters xxxx and yyyy should be.
I've tried a few things but had no success.
kenmac
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Perth, Western Australia
Time Zone = GMT + 8
You're almost halfway. The next half is what is the less obvious.
I'd normally declare the start function in the x object as something like:
PUB start(NumPtr)
I use a variable name with a Ptr suffix to let me know that it's a pointer. Now for the magic. Access to the variable is as follows:
byte[noparse][[/noparse]NumPtr] := MyFunc(byte[noparse][[/noparse]NumPtr])··· ' if Number is a byte
word[noparse][[/noparse]NumPtr] := MyFunc(word[noparse][[/noparse]NumPtr])···' if Number is a word
long[noparse][[/noparse]NumPtr] := MyFunc(long[noparse][[/noparse]NumPtr])··· ' if Number is a long
Note: it's up to you to make sure you use the right size access on the variable! In all the above instance, you'll get the value of·variable Number process it using MyFunc (which conveniently for us just happens to return the answer to life the universe and everything) and assign the result back to the·variable Number. Both the read and write of Number are done via the pointer NumPtr.
Ok, that's the lesson for accessing Number within start (of the x object).
Part 2: accessing Number from the cog
How to do it depends on wether your new cog is spin or assembly. I'm assuming from your example that its Spin. Try something like the following:
VAR word NumPtr 'pointer to Number in parent object long CogStack[noparse][[/noparse]10] PUB start(MyNumPtr) NumPtr := MyNumPtr ' keep a copy of the pointer CogNew(checkno, @CogStack) PRI checkno repeat byte[noparse][[/noparse]NumPtr] := MyFunc(byte[noparse][[/noparse]NumPtr]) ' if Number is a byte PRI MyFunc(val) return val // 42Note: In this example NumPtr may be a word, but that is just the size of the pointer, it doesn't give any clue as to what it's pointing to!
What does this cog do - once started?·Everytime you assign a·value to Number, the cog in the x object will grab the value in Number·and replace it with the modulus 42 result. It will do this continuously, but of course nothing happens for numbers from 0 to 41.
Simple!
Post Edited (mirror) : 7/19/2007 10:06:49 AM GMT
I'll have a go at it tomorrow and we'll see what happens.
kenmac
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Perth, Western Australia
Time Zone = GMT + 8
I assumed it is a dyslexic version of ms or millisecond.
Graham
p.s. Dyslexic is a cruelly hard word to spell
Mostly people in this forum are using "uS" for that while I think there is no way to type the "µ" on an english keyboard layout.
Thomas
I'd of not guessed that 'mys' was meant for 'µ/microsecond'. Unless reading some thread where various people were writing the latter also.
Kaio, on the Macintosh the 'µ' symbol is created using the Command key (the key with the Apple symbol). But on my PC laptop (for PIC and Parallax use) I just use the standard 'u' key. Don't know, but maybe there is some way to get the 'µ' from Microsoft's OS.
Thanks for the clarification. I thought I'd pulled a Rip VanWinkle and missed something in engineering in recent years!
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Harley Shanko
h.a.s. designn
My wrong, it is the Option key and 'm' key for the mu symbol on the Mac. Maybe my brain is dyslexic these days.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Harley Shanko
h.a.s. designn
mu is 230: µ
Omega is 234: Ω
pi is 227: π
There are·many more useful symbols, all outlined in the bottom table of this page: ·http://www.asciitable.com/
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Paul Baker
Propeller Applications Engineer
Parallax, Inc.
Back on to the original subject!
mirror,
I like the Galaxy touch! (To think that it took a Propeller to find the answer!)
I have tried the following:
Controlling Cog
Receiving Cog
pub start(MyNumPtr) NumPtr := MyNumPtr cognew(checkno, @Stack) pri checkno dira[noparse][[/noparse]16]~~ repeat byte[noparse][[/noparse]NumPtr] := MyFunc(byte[noparse][[/noparse]NumPtr]) pri MyFunc(val) if val == 8 !outa[noparse][[/noparse]16]The requirement was to turn on LED on Pin16 if the number = 8.
This works and the LED isn't activated if the number <>8 .
However, if I change the number to be continuously variable, it doesn't trap the required number.
Controlling Cog
pub start number := 1 repeat number++ ' increment number waitcnt(5000_000 +cnt) ' set rate of changeReceiving Cog
pri MyFunc(val) if val == 8 ' if number = 8 then !outa[noparse][[/noparse]16] ' turn on LEDShould that work?
kenmac
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Perth, Western Australia
Time Zone = GMT + 8
Further on your example:
In the code:
pri checkno repeat byte[noparse][[/noparse]NumPtr] := MyFunc(byte[noparse][[/noparse]NumPtr]) pri MyFunc(val) return val//42The actual value held in the Global Variable is = byte[noparse][[/noparse]NumPtr] - is that correct?
If so, an internal variable could be set as Num = byte[noparse][[/noparse]NumPtr], for further action?
MyFunc is just an example of a process/return action on that value?
The "return value" action isn't important at this stage, I just need to be able to monitor a changing variable data in another Cog.
Once I understand that, the use of multiple Cogs will become much easier.
Perhaps this subject should be covered in more detail in any supporting documentation - it is important.
kenmac
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Perth, Western Australia
Time Zone = GMT + 8
I think I have worked it out and answered my own questions.
The following works:
pri checkno dira[noparse][[/noparse]16]~~ repeat number := byte[noparse][[/noparse]NumPtr] if number == 8 ' trap number 8 !outa[noparse][[/noparse]16]So, having confirmed that, I can move on to other things.
Thanks for the help.
kenmac
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Perth, Western Australia
Time Zone = GMT + 8
To my impression, there has been a lot of confusion - as always - between (encapsulated) objects and COGs ("thread processors"). Because of the (maybe a little bit overdone) scope rules of SPIN, you have to use pointers to "creep in".
In contrast to assembly threads, SPIN code has no visible COG internal data, so most of the issues discussed in assembly language related postings here (RD/WR.... vs. MOV) simply do not apply for SPIN. All (user defined) SPIN variables - "local" (ever wondered where you take the "stack" from when starting a SPIn-COG?) or "global" (which is a bad term, as you definitely and always have to distinguish between VAR (static "Object" data) and DAT (static "Class" data) to avoid very tricky pitfalls) - are held in HUB memory and can be accessed in the most simple way, slightly handy-capped by the scope rules..
µS
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
E3 = Thought
http://folding.stanford.edu/·- Donating some CPU/GPU downtime just might lead to a cure for cancer! The average PC while browsing the internet typically uses less than 30% of it's potential, why not donate a portion of the rest for cancer resaerch?
What you have written is micro-siemens, which has got nothing at all to do with time.
Reference: http://en.wikipedia.org/wiki/Siemens_%28unit%29
Lowercase "s" for seconds. Uppercase "S" for siemens.
·
The original simple setup where a Controlling Cog calls an Object which starts it's own Cog and then checks a number created as a Global variable, actually worked. (See earlier in this thread)
However, when I added this function to an existing Object designed to drive an LCD, it no longer works.
The relevant code is as follows:
Controlling
Receiving
pub init 'Initialize the LCD {{ This part of the code not shown - it initialises the LCD ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: }} checkNo pub start(MyNumPtr) NumPtr := MyNumPtr cognew(init, @Stack) pri checkNo repeat num := byte[noparse][[/noparse]NumPtr] ' read number via pointer if num == 5 ' check number is 5 dira[noparse][[/noparse]16]~~ ' if so, turn on LED outa[noparse][[/noparse]16]~~ waitcnt(3000_000 + cnt)Changing the number in the Controlling has no effect whatsoever.
I inserted some code in the Receiving/Checkno to display the number on the LCD:
The character displayed is the curly bracket "{" , which is Ascii 124.
Can anyone see what is wrong here?
kenmac
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Perth, Western Australia
Time Zone = GMT + 8
1) GEAR. Step through the code. See what happens to the pointer.
2) SerialMirror. Use the serial port, and a liberal number of debug messages to track down the source of the bug. The reason for SerialMirror as opposed to FullDuplexSerial is that you can use the same serial port from multiple objects without too much hassle.
http://forums.parallax.com/showthread.php?p=649541
Actually, the LCD character read was incorrect.
The character now displayed is 0.
By altering the Ascii adjust number, the displayed number follows, e.g. 55 reads 7.
So, basically the pointer reading is 0.
Having passed the pointer to the X.start, then converting it to its own variable NumPtr, all the methods within this Object should be able to access the contents of NumPtr ? It is defined.
I'll keep playing with it, and hopefully all will be revealed!
kenmac
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Perth, Western Australia
Time Zone = GMT + 8
Was just thinking this morning that there is an alternate way to solve this problem, although it is only applicable to some applications.
Define your x object as follows:
var long stack[noparse][[/noparse]20] byte Number PUB Start cognew(checkno, @stack) PUB GetNumber return Number PUB SetNumber(val) Number := val PRI checkno repeat Number := MyFunc(Number)Then use x.GetNumber and x.SetNumber from the parent object. Number is not global, but somtimes you can do this to avoid·the pointer magic.
·
var long stack[noparse][[/noparse]20] byte Number PUB Start cognew(checkno, @stack) PUB GetNumberPtr 'return the address of Number return @Number PRI checkno repeat Number := MyFunc(Number)then easy u can access Number from the current object or the parent object using code like this. (ofc, the MyObject part is dropped when accessing GetNumberPtr from the current object)
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Lunch cures all problems! have you had lunch?
For updating circular buffers I used an even slightly more advanced technique.
In parent object:
In child object:
This way:
1) The memory definition of the FIFO buffer can be kept with the child object. This is really important to me·as I'm becoming *very* memory constrained, and this·a more accurate picture of the memory usage of the object.
2) It allows the block of pointers to be passed to some other child object. In this way the two child objects can communicate without any intervention from the parent object.
Disclaimer: The code I've shown here is in concept form only. I normally do BufPut and BufGet as pointers in the child object which allows easier buffer wrapping.
·