Where did the Nibs and Bts go?
T Chap
Posts: 4,223
I read through all the available manual sections and have a few remaining issues before I can translate a Pbasic program to Spin:
1. To simulate a bit sized variable, as in: SomeBit VAR BIT what would be the Spin equilavent for BIT and NIB
2. Is "For Next" in Spineeze "Repeat"?
3. If a variable is in a Public method, can the value of that variable be accesed by another cog?
Thanks to all the contributers of examples, they are very helpful!
1. To simulate a bit sized variable, as in: SomeBit VAR BIT what would be the Spin equilavent for BIT and NIB
2. Is "For Next" in Spineeze "Repeat"?
3. If a variable is in a Public method, can the value of that variable be accesed by another cog?
Thanks to all the contributers of examples, they are very helpful!
Comments
3) Variables declared in a VAR or DAT section can be accessed by any method (PUB or PRI) in the same object no matter which COG is executing the method. In other words, the variables do not belong to a COG. You can also pass the address of a variable to another object and that object can access the variable regardless of the COG executing the object (Be careful when you do this! You can get really bizarre bugs when two COGs change the same variable in an unplanned way, but it's how COGs communicate).
If you're writing assembly language routines, the variables in the DAT section are strictly local to the COG that's running the routines and are not accessible from any other COG. Assembly language routines have to communicate through a shared area in the HUB memory.
Is that correct?
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
"If you want more fiber, eat the package.· Not enough?· Eat the manual."········
I know there is an object that shows some great examples for BS2> Spin, but, I really wish this forum had a sticky called PBasic>SPIN Translation Tips that could help people translate their projects over more easily. Even with the excellent reply of yours, I am still clueless what is the method to write a variable for a bit and nib to replace code from the Stamp. Granted that with enough experimenting and reading it will come to light. I'll have the Pro stick next week and then will start section by section converting code over, and I am pretty excited about the move and subsequent future use for all new projects on the Propeller.
If anyone agreed that a post with an accumulation of tips for this purpose would be welcome, here are a few things I'd like to grasp:
GOSUB
Return from GOSUB
Labels compared to Pub Pri
Variables for Nib/Bit
Do while Loop
Even after looking at a lot of examples and the manual segments last night(much too late at night), I don't have a grasp of how you have one program that has a section that can run on one cog only for it's own dedeicated role, whilst another code is running for another cog.
In a practical sense, I am looking for an example that may already exist of how to have one cog managing all button inputs, LCD and LED feedback, with that cog telling another cog to move a stepper to the desired position reltive to a COUNT that is taking place on another cog thats full time job is to count up and down pulses from an encoder. In othe words, 3 cogs working together sharing variables.
Thanks again for the great replys, this is going to be fun.
2) See #1. Look at the RETURN statement in the Propellor manual chapter 4. Also look at PRI and PUB in the same chapter.
3) There are no labels in Spin for points in your program code. Similarly, there is no GOTO. If you're writing assembly language, there are conventional labels, but that's not Spin.
4) Use BYTEs for variables. If you start getting tight on space, show us what you're doing and I'm sure we'll have suggestions. There's a huge lot of space in the Propellor for variables compared to the Stamps.
5) Use REPEAT. It does the same things and more. Look in the Propellor manual chapter 4.
There are no examples yet for what you want to do. The best thing would be to look at one of the text terminal demos and start playing with it. The keyboard driver uses an assembly cog for the actual keyboard I/O and Spin code for talking to the cog. The tv text driver similarly uses a cog for the actual video generation, but uses Spin code to set up the text buffer.
As I mentioned in another thread, think of the active cogs as either stopped or executing the actual source code you've written. One cog can start another to execute the same source code starting at a specified subroutine. You can have several cogs executing the same subroutine or executing different ones. When one cog executes a function call, it simply continues at the new function (subroutine). The return addresses (from the function calls) and any local variables declared in the function declarations are allocated in the stack that must be unique to that cog. If you have two cogs executing the same code, there must be two different stack variables in HUB memory for them and these are supplied when you start the new cog (using cognew or coginit). Assembly language is a bit different and another discussion.
If you wanted to handle each button with a different cog, you could do that by writing a universal button routine (method) that is given the pin number to use and the address of a variable in HUB memory to use for the state of the button. Your would do a cognew for each button and provide an array of longs for a stack, the pin number to use, and the address of a variable to hold the button state. You might run out of cogs quickly and it's actually better to devote one cog for all buttons and have that cog do all of them, but you could have one for each.
Isn't this not the same thing as LABELS for purposes of navigation to certain code to run in the order that you want?
PUB Start
BlinkingLED_A16 'This call is executed first.
BlinkingLED_A17 'This call is executed next
PRI BlinkingLED_A16 | Pin
<do something here>
PRI BlinkingLED_A17 | Pin
<do something here>
In the example above the PUB calls the first PRI, executes it, then RETURNS to execute the second PRI.
In the follwoing example, although the notation says it runs in sync, does it not in fact run 1 clock pulse out of phase due to calling one PRI, then calling the second which can only happen at a subsequent clock right? Unless there is something unseen that would get both PRI's ready, and laung them simultaneously:
PUB Start
cognew(BlinkingLED_A16, @stack0) 'Starts a Cog, calls BlinkingLED_A16, the Cog uses @stack0
cognew(BlinkingLED_A17, @stack1) 'Starts a Cog, calls BlinkingLED_A17, the Cog uses @stack1
Although Mike said there are no LABELS as such, but for understand how to navigate, is not PUB SomePlace the same thing, and if so, are there methods to go to that PUB even though there are no GOTO?
Thanks for any clarification. This is a lot harder than I anticipated!
PUB and PRI define functions / procedures / methods. There's a single entry point and there are no GOTOs. You can do the same thing with LABELs and GOSUBs in Stamp Basic, but there are some program structures that you can't readily translate to the GOTOless form (without rewriting them). Keep in mind that a call in Spin causes a return address to be put on the stack. It's not a GOTO.
Still, you can think of PUB and PRI somewhat like labels and you can think of the use of the method name from the PRI/PUB declaration as a kind of GOSUB and the return statement is still a RETURN. You can pass parameters to a Spin function/method and pass a return value back and you can even pass parameters to a Spin function being launched with cognew or coginit ... Nice for specifying an I/O pin number or some kind of mode information.
Here is an example of what I need to translate:
Main:
If But1 = 1 Then MTRStart
If But2 = 1 Then SomethingElse
If But3 = 1 Then SomethingElse2
GOTO main
MTRStart:
<make the motor do something based on other info, position etc>
goto main
SomethingElse:
<whatever needs to happen>
goto main
SomethingElse2:
<whatever needs to happen>
goto main
In these cases, MTRStart and SomethingElse(2) never get ran unless the conditions are met. My understanding is that you can't just have these labels converted to PUB ot PRI, as they will run in the order they are seen, whereas in PBasic, they will only be seen when something sends the "tape head" there to play them.
Is there a good example somewhere of navigating to code that is only used as needed?
- PUB Main
- repeat
- if ina[noparse][[/noparse]But1] == 1
- ' make the motor do something
- if ina[noparse][[/noparse]But2] == 1
- ' something else
- if ina[noparse][[/noparse]But3] == 1
- ' something else 2
See ... Thing are rearranged. You don't need so many explicit labels, but everything is still there without the GOTOs.
CON
Byte Pin
Out = %1
· In = %0
Hi := 11111111
Lo := 00000000
High := 1
Low := 0
VAR
Byte TrackSomething
PUB main
TrackSomething := 00000000
PIN := 0
DirA[noparse][[/noparse]PIN] := In
Repeat
If PIN := High
!TrackSomething 'toggle this var
<Display Tracksomething new state on LCD>
Elseif PIN:= Low
Next
<some other If statement>
Maybe therre is a better way but write this since only one bit needs changing in the TrackSomething, but if it works who cares, I just need to toggle some states that can be used elsewherein the program.
I guess from your last reply that there is no way to jump to any place, although I thought I understoodfrom somewhere that you could run any PRI or PUB by calling the correct name of the PRi or PUB block.
CON
_pin = 5
VAR
_byte TrackSomething
PUB Main
_TrackSomething := Low
_dira[noparse][[/noparse]pin] := In
_repeat
__TrackSomething := ina[noparse][[/noparse]pin] 'you can save the state of the pin
__if TrackSomething == High ' or you can use the pin to control a decision
___' Now do something
Even with Quick Reply, you can use the [code] and [/code] tags to enclose program lines and preserve your leading spaces intact.
-Phil
I could only get this to upload using a BS2 format, I dont have a pc with me to save it as .spin
I am looking for clarification on something, I'll quote from chapter 4-150 regarding Return:
"Assume that DisplayDivByZeroError is a method defined elsewhere"
PUB Divide(Dividend, Divisor)
If Divisor == 0
DisplayDivByZeroError
Return 0
Retrun Dividend / Divisor
This indicates to me that although we don't have GOTO, we can essentially "run" the "PUB or PRI DisplayDivByZeroError" method remotely just by putting its name under the IF statement, assuming the IF were true as in the example.
I have some code that needs to run under some circumstances, and would like to run it ONLY under those conditions, as the example shows. However, what makes " Pub or Pri DisplayDivByZeroError" NOT RUN? What I mean is, the "error" in the example DOESN'T need to run unless called by PUB Divide IF test, and since the example claims that DisplayDivByZeroError IS a PUB or PRI that exists somewhere else, what stops DisplayDivByZeroError from running where is is located? Certainly in a real case, the error would not be in a timeline that displays the error every cycle throughout the repeating of the code(object?).
Again the navigation is very confusing in the example. What I think the manual lacks is a clear and consice explanation of how to structure a timeline, with departures to run code(other PUB or PRI) " as needed".
I am stuck on the Pbasic structure of timelines:
start here at main:
do this if x
do that if y
go back to main and start over
If the analogy is correct:
PUB init
repeat
PUB main
PUB x
PUB y
PUB DisplayDivByZeroError 'as in example
What am I missing to get the timeline straight?
Thanks
PUB Start··
· Repeat
···· MeasureFrequency
···· DisplayFrequency
PUB MeasureFrequency | Pin
PUB DisplayFrequency
If I am correct, the way to NOT see a PUB is to have it not "played" because the REPEAT above it stopped the playhead from ever getting there. PUB DisplayFrequency only plays when called from the infinite repeat loop in back up in PUB Start.
IF the repeat were not there, the timeline of code execution would have been as follows:
PUB Start
PUB MeasureFrequency | Pin
PUB DisplayFrequency
END here as there is no way to return to the top. Right?
The "Do_Something_x" can be complicated, perhaps an if statement or a "repeat i from 1 to 10" or whatever. You usually want any given "method" to not be longer than a page of text so you can "grasp" it more easily. As things make sense as logical units, you package those up in their own "method"s ... That's a subroutine. Unlike PBasic, execution doesn't "fall through" from one section of a program to another. The structure is strictly hierarchical. If you draw out the structure of a Spin program, you can draw it as a tree without any crossing of branches. That's not true in PBasic where you often have to draw arrows every which way.
Here is the updated partial code. Notice I am attempting to call other methods from within a method. My understandng is that there is always an implied return address to get back from where you left from, and it resumes from there. In this case:
PUB Check_A_Pos 'if OM or OS = 1, check A pos, toggle it, then start motor
Repeat
If (Lock_Latch_M = 1) AND (OS = 1)
Exit
If (LOCK_Latch_S = 1) AND( OM = 1)
Exit
If (OM = 0) AND (OS = 0)
Exit
If (OM = 1) AND (Lock_Latch_S = 0)
Mtrstart
If (OS = 1) AND (Lock_Latch_M = 0)
Mtrstart ' goes and runs Mtrstart, then returns back to HERE. Maybe the RETURN below isn't req'd
Return 'exit Check_A_Pos if no conditions met
The above code is the only way I can think of to check for all the conditions, and then Mtrstart if the right conditions are met.
You've used some IF, ELSEIF. That's often used when there are a series of exclusive choices.
When these choices are in numeric order mostly (like 0,1,2 or 0,2,3), a CASE statement is clearer.
I like your use of RETURN to handle lockout conditions for the motor operation.
Check my translation of the logic. The logic operators (AND, OR, NOT) treat all non-zero values as
TRUE and zero as FALSE. Although it's sometimes clearer to write something like "if ina[noparse][[/noparse]pin] == 1",
it's also valid to just write "if ina[noparse][[/noparse]pin]" for the same thing. The first format is necessary if you're
looking at more than one bit as in "if ina[noparse][[/noparse]15..14] == %10"
This version of the program is simply to replicte the existing Pbasic-BS2P40 version so the Propeller can be a drop in with a few board changes. The next version will incorporate a cognew for controlling the motor driver directly, including accleration values, deceleration, step outputs, direction outs, speed etc. Then, another cog is to read the encoder(translated up/down pulses) full time and store the counts in a global variable for use by the main program. The next version will also feature an LCD with additional buttons for programming setup, since the servo I am currently using has its own controller and PC based software setup for all positions, speeds, etc.
This was a great exercise, there may be a few things that will surface when I get the chip plugged in, but I predict it is very close to functional. I may build an adapter board to drop in the propstick on the existing board for testing.
I included the new program with all edits, it is likely hard to follow not knowing how the thing works. It is quite a bit more elegant than the Pbasic version though, which was all over the place. At least it worked.
Thanks again, it sure is nice to have a fully coded project within 3 days on a brand new language.