As the say here in Hollywood... for your consideration.
This program does exactly what yours does, but uses named constants to make the program easier to modify for different hardware. I'm a firm believer of not using pin #s in listings; this practice can lead to confusion and to bugs. Using a number in a waitms() instruction is not so problematic, but defining a constant for that value means only one edit to change the behavior.
I just made it simple, so a newbie can see that just a few simple lines of code can get them up and running. Anyway your basically the only one writing spin2 examples, and they all work. Thanks for your input. I wonder who Bob Sweaney is.
Sorry, Bob, did I confuse you with someone else? I will fix those listings with your forum handle. My apologies.
On a serious note, I think it's very dangerous to show newcomers code with "magic numbers" (e.g. embedded pin #s) -- habits die hard, so it's better to teach newcomers good habits from the start.
Writing to a contiguous pin group is different in the P2 -- we use the LSB with addpins and the number of pins past the LSB..
pinwrite(0 addpins 7, getrnd())
Again, though, I would caution against the use of "magic numbers" in the code (even test code -- been bitten by this or than once). You could create this constant
BUS4X = 0 addpins 3
...and then update the code like this:
pinwrite(BUS4X, getrnd())
...if you wanted to write to a 4-bit bus. I attached a working demo that uses the LEDs on the Eval board.
No problem, call me Bob, DigitalBob, just don't call me late for dinner. I understand your templates, with assigned pins in the Con for I2C, serial, etc., but might be overwhelming for the newcomer. Anyway keep up the hardwork, My library is full with JonnyMac spin objects in P1 and P2.
I have 6 other beginner programs that I tried to use "Magic Numbers"
to initiate and control 8 pins. I attached all 5 because each program
tried something different.
I tried to use the P2 GitHub OBEX as a guide line for initializing pins,
but there were almost no demo objects?
Do we need to initialize the pins, I heard someone say that with the
new Pin(functions) the pins are automatically set to outputs?
All the different problems are in the comments. 4 out of 5 programs
compiled but did not run?
Are the old pin registers (dira, outa, ina) necessary?
Is everything I did wrong, or are there compiler issues? Mainly I am
curious why many pin initialization worked (or at least compiled)
like outa[7] and outa.[7], and others did not.
Also the difference on using ' or ' ' for comments. It worked on the
P1, but I am not sure it works on the P2. The new ide displays those
comments in 2 different shades?
Thanks
Bill M.
One other question. In many other P1 programs the pin#'s were passed
from the demo object. Is there any reason why I should not do this (pass
the pins) with a P2 program? TY
Do we need to initialize the pins, I heard someone say that with the new Pin(functions) the pins are automatically set to outputs?
It may have been me during one of my prosthelyzing posts. The pinX() functions in Spin2 actually do three three things:
1) Select the correct IO registers ( A or B )
2) Make pin(s) inputs or outputs as required
3) Write to or read from the desired pin(s).
To make a pin high in the P1, we would do this:
dira[pin] := 1
outa[pin] := 1
In the P2, this handles it:
pinhigh(pin)
We only have 32 pins on the P1, but 64 on the P2. While we *can* directly access the IO registers, I don't recommend it because programs change. When I used to design products for EFX-TEK, I would never connect external circuitry to the Propeller in my schematic. When the PCB layout guy was finished, he had decided the best pins to use for the layout -- all I had to do was update my pin constants in the listing to move test code to the final product. This happens all the time (pins moving), which is one (of many) reasons why embedding pin #s into our code is a bad idea.
In the P1, we could do this:
outa[MSB..LSB] := value
dira[MSB..LSB] := %1111
In the P2, we do it like this:
pinwrite(LSB addpins (MSB-LSB), value)
There is a caveat, however. In the P1, we can also do this:
outa[LSB..MSB] := value
...which will reverse the output order of the lower four bits in value. How can we do this in the P2?
pinwrite(LSB addpins (MSB-LSB), value rev (MSB-LSB))
Okay, this is not quite as friendly as the P1 version, but -- thankfully -- is an infrequent requirement.
It might be tempting to do this:
outa.[LSB..MSB] := value
...but this doesn't work. The rightmost value in the .[ ] syntax is used as the LSB.
Are the old pin registers (dira, outa, ina) necessary?
In Spin2, I don't see any need for direct IO register access.
Is everything I did wrong, or are there compiler issues? Mainly I am curious why many pin initialization worked (or at least compiled) like outa[7] and outa.[7], and others did not.
Things have changed from the P1 to the P2. This syntax:
outa[7]
...is actually referring to register $003 in the cog. Why? In P2 syntax, outa is just another register and anything in [ ] is an offset. The address of outa is $1FC. Add 7 and you get $203 which is an illegal address because the max is $1FF, hence it wraps around to $003.
I think you see now why I have become a bit adamant about NOT using IO registers in Spin2 code -- let the Spin2 instructions do the work for you.
Also the difference on using ' or ' ' for comments. It worked on the P1, but I am not sure it works on the P2. The new ide displays those comments in 2 different shades?
Single and double apostrophes are both legal line-comment starters. The difference is in the view when using Propeller tool. There is a radio-button a the top marked Documentation. In this mode, only the comments with double apostrophes or double block comments {{ }} will be displayed.
One other question. In many other P1 programs the pin#'s were passed from the demo object. Is there any reason why I should not do this (pass the pins) with a P2 program?
It is my (very strong) opinion that one should always craft objects that accept pins from the calling applications. Again, pins move from application to application, and hard-wiring pins is a bad idea. The exceptions, of course, are the fixed pins for programming and application memory (EE or flash). This is why my code calls out fixed IO pins versus application IO pins.
Code Reviews:
-- Blink_v01 : The syntax you're using causes the IO registers to be treated as long arrays, not a single array of 32 bits. Again, use the pinX() functions.
-- Blink_v02 : Mixed syntax with IO registers that Spin2 doesn't support.
-- Blink_v03 : Same issue with registers in setup(). The main() method never runs because it is not called. Have a look at my template; setup() is called from main().
-- Blink_v04 : Again, main() is never called. Note: Spin is not like C that looks for main(). In Spin, the first method runs (this is why I place main() at the top).
-- Blink_v05 : Again, main() is never called.
Important notes:
-- It is best NOT to use IO registers directly in Spin2; the underlying code for the high-level instructions is very efficient and fast.
-- In Spin, the first method in the listing runs and controls the program. The code does not care about its name, nor go looking for anything with a specific name.
Side note. This blinker code works, but is tedious to write versus using the new Spin2 functions for IO.
To access bits with in a register we have to use the .[] notation. Registers are only 32 bits, so we have to use & $1F with the B IO registers. Again tedious. Don't do it.
Final comment about pin groups. They only work within one register. What happens if you do this?
BUS = 30 addpins 7
The Propeller will create a group that will impact pins 30, 31, 0, 1, 2, 3, 4, 5 -- which is probably not what we want. I was just writing multi-channel LED driver for the P2 that looks for these boundary issues. It's something to be careful with.
I've attached my P2 template for your examination and use if you choose.
Fantastic article, JonnyMac! One complaint one hears, whether it's fair or not, is that Propeller information is spread hither and yon, and takes too much effort and time to piece together from a vast body of shaggy dog posts. Therefore any attempt to gather together relevant information for would-be newcomers would be a boon! But what you've written for N&V is far far better than just 'any' attempt. A place of prominence ought to be found for it!
I don't code spin, but on the P1 if I have a data line that is bi-directional with a pullup, I will write a zero to that pin and then change the direction of the pin as required. To send a one, I change the direction to input, to send a zero I change the direction to output, but only for as long as that data bit is required. This helps avoid unintentional data collisions or bus conflicts. I just wondered how P2 spin does that?
I don't code spin, but on the P1 if I have a data line that is bi-directional with a pullup, I will write a zero to that pin and then change the direction of the pin as required. To send a one, I change the direction to input, to send a zero I change the direction to output, but only for as long as that data bit is required. This helps avoid unintentional data collisions or bus conflicts. I just wondered how P2 spin does that?
Dave
With the P2 you don't need such tricks, you can configure the pin for open-drain with an internal pullup (or no pullup if you want an external).
Then you use the same commands for output as you would use with a normal push-pull output.
Here's an example of what Andy was talking about. In my P2 I2C driver, the application can choose to have pull-ups on the bus or not. If not, the P_HIGH_FLOAT configuration is used which disconnects the output when the pin is written high -- this allows the external pull-up to have control. I'm doing it in PASM2 here, but it also works in Spin.
pub setup(scl, sda, khz, pullup) | tix
'' Define I2C SCL (clock) and SDA (data) pins
'' -- khz is bus frequency: 100 (standard), 400 (full), 1000 (fast)
'' * circuit/connections will affect maximum bus speed
'' -- pullup controls high level drive configuration of SCL and SDA
longmove(@sclpin, @scl, 2) ' copy pins
clktix := tix := (clkfreq / (khz * 1_000)) >> 2 ' calculate ticks in 1/4 period
case pullup
PU_NONE : pullup := P_HIGH_FLOAT ' use external pull-up
PU_1K5 : pullup := P_HIGH_1K5 ' 1.5k
PU_3K3 : pullup := P_HIGH_1MA ' acts like ~3.3k
other : pullup := P_HIGH_15K ' 15K
org
fltl scl ' clear old smart pin setup
fltl sda
wrpin pullup, scl ' configure high drive
wrpin pullup, sda
drvh scl ' both high
drvh sda
waitx tix
waitx tix
rep #8, #9 ' bus clear (if SDA stuck low)
testp sda wc ' sample sda
if_c jmp #.done ' abort loop if sda == 1
drvl scl ' scl low
waitx tix
waitx tix
drvh scl ' scl high
waitx tix
waitx tix
.done
end
I did a quick check of the interpreter source; the pinread() function does not affect the dir register for the pin(s) read.
Oh I do like it.
You do seem to be picking this P2 stuff up quickly.
Ok, wrpin is an interesting command, it's available in flexbasic, so now I have to find what values
P_HIGH_FLOAT
P_HIGH_1K5
P_HIGH_1MA
P_HIGH_15K
are.
In Blink_v08.spin2 I am working with returning binary numbers
from random() : Rnd function, and using it to drive an 8 bit led bus
and then print the binary data to Parallax Serial Monitor.
My problem is that I am using too many "new line" commands and
I thought there must be a better way to format my serial output.
Possibly using a binary formatted string?
fstr2(p_str, arg1, arg2)
%[w[.p]]b print argument as binary
-- w is field width
* positive w causes right alignment in field
* negative w causes left alignment in field
-- %ws aligns s in field (may truncate)
-- %wc prints w copies of c
-- p is precision characters
* number of characters to use, aligned in field
-- prepends 0 if needed to match p
-- for %w.pf, p is number of digits after decimal point
Oh I do like it.
You do seem to be picking this P2 stuff up quickly.
Ok, wrpin is an interesting command, it's available in flexbasic, so now I have to find what values
P_HIGH_FLOAT
P_HIGH_1K5
P_HIGH_1MA
P_HIGH_15K
are.
Thanks
Dave
Those constants are built in to the compiler, and should work in flexbasic as well as Spin
I am in the process of building a list of P1 v P2 differences, or has
someone already made one??
I have questions in the code comments about (%) (.) and why the endless
loop trap?
TY
Bill M.
pub main() | rn ' term.fstr1(string("LEDs = %.8b\r"), rn)
setup()
wait_for_terminal(true)
'' Neat Trick
'' I thought it was broken
repeat
rn := prng.xrandom() ' get random number
pinwrite(LEDS, rn) ' write to LEDs
term.fstr1(string("Leds = %.8b\r"), rn) ' write to terminal
'' I figured out the obvious stuff in the formatted strings
'' ("Statement (%) (.) Bit field-width Base-Binary Ascii commands")
'' What does (%) and (.) represent?
'' When % or . is removed, the formatting falls apart.
'' Or are % or . just mandatory symbols to format a string?
waitms(1000)
repeat
'' I assume this is an error trap, what can happen if
'' there is no trap?
waitct(0)
con { formatted strings }
'' Is this part of the BS2 Debug?
{{
Escaped characters
\\ backslash char
\% percent char '' thought it meant binary
\q double quote
\b backspace
\t tab (horizontal)
\n new line (vertical tab)
\r carriage return
\nnn arbitrary ASCII value (nnn is decimal)
Formatted arguments %.8b\r
%w.pf print argument as decimal width decimal point
%[w[.p]]d print argument as decimal
%[w[.p]]x print argument as hex
%[w[.p]]o print argument as octal
%[w[.p]]q print argument as quarternary
%[w[.p]]b print argument as binary
%[w]s print argument as string
%[w]c print argument as character (
-- w is field width
* positive w causes right alignment in field
* negative w causes left alignment in field
-- %ws aligns s in field (may truncate)
-- %wc prints w copies of c
-- p is precision characters
* number of characters to use, aligned in field
-- prepends 0 if needed to match p
-- for %w.pf, p is number of digits after decimal point
}}
...at the end of main() to prevent the Propeller from rebooting and running what's in EE or flash.
Is this part of the BS2 Debug?
No. I did my best to match formatted string control as used in other languages -- am open to changes. See attached from demo of formatted strings. I was recently re-reading the K&R and may need to review and update that formatting code. I haven't had any problems with it, and a couple of users have found it very handy.
What does (%) and (.) represent?
That forces the number to be zero-padded. Again, see the attached demo. Note that the demo uses the format %0.width, but you can leave out the leading 0 due to the way get_nargs() (get numeric arguments) method works.
Is this part of the BS2 Debug?
No. I did my best to match formatted string control as used in other languages -- am open to changes. See attached from demo of formatted strings. I was recently re-reading the K&R and may need to review and update that formatting code. I haven't had any problems with it, and a couple of users have found it very handy.
I have found it very handy indeed so many thanks Jon.
Comments
This program does exactly what yours does, but uses named constants to make the program easier to modify for different hardware. I'm a firm believer of not using pin #s in listings; this practice can lead to confusion and to bugs. Using a number in a waitms() instruction is not so problematic, but defining a constant for that value means only one edit to change the behavior.
On a serious note, I think it's very dangerous to show newcomers code with "magic numbers" (e.g. embedded pin #s) -- habits die hard, so it's better to teach newcomers good habits from the start.
Parallax minimal program to blink an 8 bit buss, and transmit serial
data.
Unfortunately my next 5 programs that I tried to control an 8 bit buss
were not successful.
Bill M.
I have 6 other beginner programs that I tried to use "Magic Numbers"
to initiate and control 8 pins. I attached all 5 because each program
tried something different.
I tried to use the P2 GitHub OBEX as a guide line for initializing pins,
but there were almost no demo objects?
Do we need to initialize the pins, I heard someone say that with the
new Pin(functions) the pins are automatically set to outputs?
All the different problems are in the comments. 4 out of 5 programs
compiled but did not run?
Are the old pin registers (dira, outa, ina) necessary?
Is everything I did wrong, or are there compiler issues? Mainly I am
curious why many pin initialization worked (or at least compiled)
like outa[7] and outa.[7], and others did not.
Also the difference on using ' or ' ' for comments. It worked on the
P1, but I am not sure it works on the P2. The new ide displays those
comments in 2 different shades?
Thanks
Bill M.
One other question. In many other P1 programs the pin#'s were passed
from the demo object. Is there any reason why I should not do this (pass
the pins) with a P2 program? TY
1) Select the correct IO registers ( A or B )
2) Make pin(s) inputs or outputs as required
3) Write to or read from the desired pin(s).
To make a pin high in the P1, we would do this: In the P2, this handles it: We only have 32 pins on the P1, but 64 on the P2. While we *can* directly access the IO registers, I don't recommend it because programs change. When I used to design products for EFX-TEK, I would never connect external circuitry to the Propeller in my schematic. When the PCB layout guy was finished, he had decided the best pins to use for the layout -- all I had to do was update my pin constants in the listing to move test code to the final product. This happens all the time (pins moving), which is one (of many) reasons why embedding pin #s into our code is a bad idea.
In the P1, we could do this: In the P2, we do it like this: There is a caveat, however. In the P1, we can also do this: ...which will reverse the output order of the lower four bits in value. How can we do this in the P2? Okay, this is not quite as friendly as the P1 version, but -- thankfully -- is an infrequent requirement.
It might be tempting to do this: ...but this doesn't work. The rightmost value in the .[ ] syntax is used as the LSB.
In Spin2, I don't see any need for direct IO register access.
Things have changed from the P1 to the P2. This syntax: ...is actually referring to register $003 in the cog. Why? In P2 syntax, outa is just another register and anything in [ ] is an offset. The address of outa is $1FC. Add 7 and you get $203 which is an illegal address because the max is $1FF, hence it wraps around to $003.
I think you see now why I have become a bit adamant about NOT using IO registers in Spin2 code -- let the Spin2 instructions do the work for you.
Single and double apostrophes are both legal line-comment starters. The difference is in the view when using Propeller tool. There is a radio-button a the top marked Documentation. In this mode, only the comments with double apostrophes or double block comments {{ }} will be displayed.
It is my (very strong) opinion that one should always craft objects that accept pins from the calling applications. Again, pins move from application to application, and hard-wiring pins is a bad idea. The exceptions, of course, are the fixed pins for programming and application memory (EE or flash). This is why my code calls out fixed IO pins versus application IO pins.
Code Reviews:
-- Blink_v01 : The syntax you're using causes the IO registers to be treated as long arrays, not a single array of 32 bits. Again, use the pinX() functions.
-- Blink_v02 : Mixed syntax with IO registers that Spin2 doesn't support.
-- Blink_v03 : Same issue with registers in setup(). The main() method never runs because it is not called. Have a look at my template; setup() is called from main().
-- Blink_v04 : Again, main() is never called. Note: Spin is not like C that looks for main(). In Spin, the first method runs (this is why I place main() at the top).
-- Blink_v05 : Again, main() is never called.
Important notes:
-- It is best NOT to use IO registers directly in Spin2; the underlying code for the high-level instructions is very efficient and fast.
-- In Spin, the first method in the listing runs and controls the program. The code does not care about its name, nor go looking for anything with a specific name.
Side note. This blinker code works, but is tedious to write versus using the new Spin2 functions for IO. It's worse if you want to do multiple pins manually. Note the use of addbits versus addpins due to the nature of the syntax. To access bits with in a register we have to use the .[] notation. Registers are only 32 bits, so we have to use & $1F with the B IO registers. Again tedious. Don't do it.
Final comment about pin groups. They only work within one register. What happens if you do this? The Propeller will create a group that will impact pins 30, 31, 0, 1, 2, 3, 4, 5 -- which is probably not what we want. I was just writing multi-channel LED driver for the P2 that looks for these boundary issues. It's something to be careful with.
I've attached my P2 template for your examination and use if you choose.
-- https://www.nutsvolts.com/magazine/article/an-introduction-to-the-parallax-propeller-2
I don't code spin, but on the P1 if I have a data line that is bi-directional with a pullup, I will write a zero to that pin and then change the direction of the pin as required. To send a one, I change the direction to input, to send a zero I change the direction to output, but only for as long as that data bit is required. This helps avoid unintentional data collisions or bus conflicts. I just wondered how P2 spin does that?
Dave
With the P2 you don't need such tricks, you can configure the pin for open-drain with an internal pullup (or no pullup if you want an external).
Then you use the same commands for output as you would use with a normal push-pull output.
Andy
Thanks for that- makes sense.
Dave
Here's an example of what Andy was talking about. In my P2 I2C driver, the application can choose to have pull-ups on the bus or not. If not, the P_HIGH_FLOAT configuration is used which disconnects the output when the pin is written high -- this allows the external pull-up to have control. I'm doing it in PASM2 here, but it also works in Spin.
I did a quick check of the interpreter source; the pinread() function does not affect the dir register for the pin(s) read.
Blink_v06.spin2 works as expected. Now I am going to apply that
to one of my favorite P1 programs.
The other P2 file is the comments using 4 different notations. There
are 2 colors or shades of the comments depending on the notation.
I did have an issue once with a ' ' but only once.
Bill M.
Oh I do like it.
You do seem to be picking this P2 stuff up quickly.
Ok, wrpin is an interesting command, it's available in flexbasic, so now I have to find what values
P_HIGH_FLOAT
P_HIGH_1K5
P_HIGH_1MA
P_HIGH_15K
are.
Thanks
Dave
from random() : Rnd function, and using it to drive an 8 bit led bus
and then print the binary data to Parallax Serial Monitor.
My problem is that I am using too many "new line" commands and
I thought there must be a better way to format my serial output.
Possibly using a binary formatted string?
fstr2(p_str, arg1, arg2)
%[w[.p]]b print argument as binary
-- w is field width
* positive w causes right alignment in field
* negative w causes left alignment in field
-- %ws aligns s in field (may truncate)
-- %wc prints w copies of c
-- p is precision characters
* number of characters to use, aligned in field
-- prepends 0 if needed to match p
-- for %w.pf, p is number of digits after decimal point
? -32 = 11111111_11111111_11111111_11100000
Thanks
Bill M.
Those constants are built in to the compiler, and should work in flexbasic as well as Spin
I am in the process of building a list of P1 v P2 differences, or has
someone already made one??
I have questions in the code comments about (%) (.) and why the endless
loop trap?
TY
Bill M.
No. I did my best to match formatted string control as used in other languages -- am open to changes. See attached from demo of formatted strings. I was recently re-reading the K&R and may need to review and update that formatting code. I haven't had any problems with it, and a couple of users have found it very handy.
That forces the number to be zero-padded. Again, see the attached demo. Note that the demo uses the format %0.width, but you can leave out the leading 0 due to the way get_nargs() (get numeric arguments) method works.