I have chapter 1 on outputs pretty well in hand and I have a couple of three other chapters on things like the PAR function and reading potentiometers well along. I am on the chapter on inputs and interaction with the outputs that were learned in chapter 1. I have sketches on the drawings but there are not all in the text yet. Programs inserted but not yet polished and fully commented. I have about 120 pages done or about a third of the book. I think I'll be done by late spring.
Its moving along pretty good. Better than I thought but then again this should be the easy part!
I have not at all been impressed by the couple of books I bought as e-books. I found it extremely hard to jump back and forth for looking stuff up (which you need to do a lot in a technical beginner's book). Am thinking about providing hard copy only which could be done for about $20.00 postpaid if I don't let Amazon in on it. I'm still open on this one and I welcome comments on this so I can see what every one wants done.
I'm an E-Book fan now. Being able to just pop out the IPad and pick up reading at lunch or after dinner at a relative's house is nice and as long as the TOC is fairly well laid out I can get back and forth well enough. Not sure what overhead or fees it adds to put something in that format but I have been buying up E-Books like crazy of late and I'd love to see more techincal stuff availible out there.
This is about the MCP 3208 A to D chip reading 4 pots
I have a program in which I set the address for the 3 lines that select the channel to read directly thus
andn outa , din_Bit 'makes Din low 000 for line 0
call #Tog_Clk 'toggle clock line hi-low to read data
andn outa , din_Bit 'makes Din low 000 for line 0
call #Tog_Clk 'toggle clock line hi-low to read data
andn outa , din_Bit 'makes Din low 000 for line 0
call #Tog_Clk 'toggle clock line hi-low to read data
The above would read channel 0 on the IC
and it reads all 12 bits just fine every time
Set_next_bit0 andn outa , din_Bit 'makes Din low
call #Tog_Clk 'toggle clock line hi-low to read data
Set_next_bit0_ret ret 'return from this subroutine
Set_next_bit1 or outa , din_Bit 'makes Din hi
call #Tog_Clk 'toggle clock line hi-low to read data
Set_next_bit1_ret ret 'return from this subroutine
It seems to read all 12 bits and then if I play with the pot
it reads the two least significant bits as 0s
If I set it up to read 11 bits or less everything is fine
I went through the code and the rest is identical in both programs
I even printed both programs out and checked them a line at a time.
The reason I want to use subroutines it to allow me to read all 8 channels from one cog in PASM.
I've been at it for two days. It is very repeatable.
Any ideas, comments or solutions? Has anyone seen this.
I've attached my own high-speed MCP3208 object (which is used in commercial apps) -- it may be of use to your learning process. Note that I only use single-ended mode and the program is designed accordingly.
A Spin cog starts a second PASM cog
The PASM cog stores 4 values in hub memory with Par and offsets of 4 each etc
I can read and display the 4 values from the SPIN cog just fine.
So far so good
Then I start a third PASM cog to blink an LED
The LED blinks just fine
I can no longer read the values I had in hub memory
They are all 0s
What gives?
{{
022 PASM Read4Pots.spin
Program to read 4 pots
Repeats the program GOOD 006 PASM ReadPot 4 times
Installs in separate cog.
Reads resistances to 12 bits appears in POT_RES global variable
15 Dec 2011
By Harprit Sandhu
}}
CON
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000
bits=12
pots=4
VAR
long P_Val[8]
long pot_sub
long clear
OBJ
fds : "FullDuplexSerial"
PUB Main 'P_VAL is a local variable here
fds.start(31,30,0,115200) 'start console at 115200 for debug output
cognew(@generate, @P_Val) 'start new cog at "generate", read var to P_Val
cognew(@tone, 0) 'start new cog at "tone", read var to P_Val
waitcnt(clkfreq/4+cnt)
repeat 'endless loop
pot_sub:=0 'reset id of pot to be printed
fds.tx($1) 'home to 0,0
repeat pots 'loop to display data
fds.bin(P_val[pot_sub], bits) 'print to the PST in binary
repeat 2 'two spaces
fds.tx(" ") 'space to erase old data overflow
fds.dec(P_val[pot_sub]) 'print value as decimal value
repeat 2 'two spaces
fds.tx(" ") 'space to erase old data overflow
fds.dec(pot_sub) 'print value as decimal value
repeat 3 'three spaces
fds.tx(" ") 'space to erase old data overflow
fds.tx($d) 'new line
pot_sub:=pot_sub+1 'increment display counter
waitcnt(clkfreq/60+cnt) 'flicker free wait
clear:=clear+1 'increment counter. This routine clears up screen
if clear>10 'decision point by erasing extra lines of bot
fds.tx(16) 'clear screen of PST display every 10 loops
clear:=0 'reset counter
DAT org 0 'sets the starting point in Cog
'first set up all the counters and addresses needed
generate mov pots_read, #8 'number of pots to read [also change data]
mov bits_read, #12 'bit resolution of 3208 IC, can be less
mov mem, par 'get address of mem for PAR
mov dira, set_dira 'sets direction of the prop pins
mov pot_id, #0 'first pot read is pot 0
'inner loop for each pot
gen2 or outa , chs_Bit 'makes Chip select high
andn outa, chs_Bit 'makes chip select low
andn outa , clk_bit 'ANDN it with the Clock Bit to make low
or outa , din_Bit 'makes the Din high
call #Tog_clk 'toggle clock line hi-low to read data
or outa , din_Bit 'makes the Din high
call #Tog_Clk 'toggle clock line hi-low to read data
'set three bit address of current pot
mov temp2, pot_id 'we will read three bits from this pot
call #get_third_bit 'get bit
if_nz jmp #its_one 'jump to set it as needed
jmp #its_zero 'jump to set it as needed
its_one call #Set_next_bit1 'setting bit subroutine call
jmp #continue1 'go to next bit
its_zero call #Set_next_bit0 'setting bit subroutine call
continue1 call #get_second_bit 'get bit
if_nz jmp #its_one1 'jump to set it as needed
jmp #its_zero1 'jump to set it as needed
its_one1 call #Set_next_bit1 'setting bit subroutine call
jmp #continue2 'go to next bit
its_zero1 call #Set_next_bit0 'setting bit subroutine call
continue2 call #get_first_bit 'get bit
if_nz jmp #its_one2 'jump to set it as needed
jmp #its_zero2 'jump to set it as needed
its_one2 call #Set_next_bit1 'setting bit subroutine call
jmp #continue3 'go to next bit
its_zero2 call #Set_next_bit0 'setting bit subroutine call
'finish transfer of data to the porpeller
continue3 andn outa , din_Bit 'makes Din low
call #Tog_Clk 'toggle clock line hi-low to read data
call #Tog_Clk 'toggle clock line hi-low to read data
mov dat_red, #0 'clear register we will read data into
mov bit_count, bits_read 'counter for number of bits we will read
read_bit mov temp, ina 'read in what is in all the input lines
andn temp, mask26 wz 'mask off except Dout line. Set Z flag
shl Dat_red, #1 'shift register left 1 bit for next bit
if_nz add Dat_red, #1 'if value + add 1 to data register
call #Tog_Clk 'toggle clock get next bit ready in Dout
sub bit_count, #1 wz 'decr the "bits read" counter. Set Z flag
if_nz jmp #read_bit 'go up do it again if counter not yet 0
wrlong Dat_red, mem 'write it in PAR to share it as P.Val
add mem, #4 'add 4 to hub address for next long
add pot_id, #1 'so we can look at next pot
mov temp2, pot_id 'recall what pot we are reading
sub temp2, pots_Read wz 'check if it is how many we want to read
'jump back up to appropriate spot
if_nz jmp #gen2 'if it is not 0 go up and read next pot
jmp #generate 'go back beginning and do all pots again
tone org 0
mov dira, set_dira 'sets direction of the prop pins
mov outa, set_dira
' mov mem, par 'get address of mem for PAR
mov delay, cnt
add delay, #16
:loop or outa, test_bit
waitcnt delay, seconds
andn outa, test_bit
waitcnt delay, seconds
'rdlong seconds, mem
'shl seconds, #1
jmp #:loop
'subroutines used
Set_next_bit0 andn outa , din_Bit 'makes Din low in 000 for line
call #Tog_Clk 'toggle clock line hi-low to read data
Set_next_bit0_ret ret 'return from this subroutine
Set_next_bit1 or outa , din_Bit 'makes Din high in 000 for line
call #Tog_Clk 'toggle clock line hi-low to read data
Set_next_bit1_ret ret 'return from this subroutine
Tog_Clk nop 'nop to settle signals
or outa, clk_bit 'make clock bit high
nop 'nop to settle signals
andn outa, clk_bit 'make clock bit low
nop 'nop to settle signals
Tog_Clk_ret ret 'return from this subroutine
Get_first_bit mov temp2, pot_id 'get current pot number
andn temp2, mask0 wz 'get last bit
Get_first_bit_ret ret 'return
Get_second_bit mov temp2, pot_id 'get current pot number
shr temp2, #1 'shift right 1 bit to get second bit
andn temp2, mask0 wz 'mask 0 bit
Get_second_bit_ret ret 'return
Get_third_bit mov temp2, pot_id 'get current pot number
shr temp2, #2 'shift right 2 bits to get third bit
andn temp2, mask0 wz 'mask 0 bit
Get_third_bit_ret ret 'return
Set_dira long %00001011_00000000_00000000_00001111 'Set dira register
Chs_Bit long %00000001_00000000_00000000_00000000 'Chip select bit 24
Din_Bit long %00000010_00000000_00000000_00000000 'Data in bit 25
Dout_Bit long %00000100_00000000_00000000_00000000 'Data out bit 26
Clk_Bit long %00001000_00000000_00000000_00000000 'Clock bit 27
mask26 long %11111011_11111111_11111111_11111111 'Mask to read Dout bit
mask0 long %11111111_11111111_11111111_11111110 'Mask to read 0 bit
test_bit long %00000000_00000000_00000000_00000011 '
seconds long 10_000_000
temp res 1 'temporary storage variable, misc
temp2 res 1 'temporary storage variable, misc
bit_count res 1 'temporary storage variable, read bit counter
Dat_Red res 1 'temporary storage variable, data being read
mem res 1 'memory location for PAR variable
pot_id res 1 'current pot id number
pots_read res 1 'total number of pots to be read.
bits_Read res 1 'resolution of data read, 12 bits max
delay res 1
potval res 1
'seconds res 1
What I am really asking is
Where did I go wrong writing to hub in one PASM cog
and reading the data from a second PASM cog
or if you will
How does one do this right?
H
I haven't looked too hard at that code but I notice your PASM function generate uses subroutines that are after the "org 0" statement for your PASM tome function.
Those subroutines in turn use variables even further down the listing.
This all looks very suspicious to me.
Due to that second org the subrouines are assembled as if they are in a different COG location than they actually are when loaded into COG.
Can't see how that is going to work.
Where did I go wrong writing to hub in one PASM cog
and reading the data from a second PASM cog
I haven't looked at your code but here's what you should check: both cogs know the address to be used. When you start each cog you will want to pass an address the hub that Cog 1 writes to and Cog #2 reads from. The key is that the setup cog (Cog #0, if you will) makes that decision and passes the common address to the others.
[Edit] Okay, I just looked. You're passing 0 instead of the hub address to your "tone" cog, and within that code you've comment out the line that picks up the hub address from the par register.
I can get the blinking working with the revised code. See below
but the variables in hub go to 0 as soon as I start the 3rd cog in PASM
I moved the 3rd cog down and separated out all its variables so they are
below it. Variable for the two PASM cogs now are under the code for each of them
However, since variable names cannot be repeated each hub has to use unique
variable names. I did that. I added dira and outa descriptions for the last cog.
H
{{
022 PASM Read4Pots.spin
Program to read 4 pots
Repeats the program GOOD 006 PASM ReadPot 4 times
Installs in separate cog.
Reads resistances to 12 bits appears in POT_RES global variable
15 Dec 2011
By Harprit Sandhu
}}
CON
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000
bits=12
pots=4
VAR
long P_Val[8]
long pot_sub
long clear
OBJ
fds : "FullDuplexSerial"
PUB Main 'P_VAL is a local variable here
fds.start(31,30,0,115200) 'start console at 115200 for debug output
cognew(@generate, @P_Val) 'start new cog at "generate", read var to P_Val
cognew(@tone, @P_Val) 'start new cog at "tone", read var to P_Val
waitcnt(clkfreq/4 +cnt)
repeat 'endless loop
pot_sub:=0 'reset id of pot to be printed
fds.tx($1) 'home to 0,0
repeat pots 'loop to display data
fds.bin(P_val[pot_sub], bits) 'print to the PST in binary
repeat 2 'two spaces
fds.tx(" ") 'space to erase old data overflow
fds.dec(P_val[pot_sub]) 'print value as decimal value
repeat 2 'two spaces
fds.tx(" ") 'space to erase old data overflow
fds.dec(pot_sub) 'print value as decimal value
repeat 3 'three spaces
fds.tx(" ") 'space to erase old data overflow
fds.tx($d) 'new line
pot_sub:=pot_sub+1 'increment display counter
waitcnt(clkfreq/60+cnt) 'flicker free wait
clear:=clear+1 'increment counter. This routine clears up screen
if clear>10 'decision point by erasing extra lines of bot
fds.tx(16) 'clear screen of PST display every 10 loops
clear:=0 'reset counter
DAT
generate org 0 'sets the starting point in Cog
'first set up all the counters and addresses needed
mov pots_read, #8 'number of pots to read [also change data]
mov bits_read, #12 'bit resolution of 3208 IC, can be less
mov mem, par 'get address of mem for PAR
mov dira, set_dira 'sets direction of the prop pins
mov pot_id, #0 'first pot read is pot 0
gen2 or outa , chs_Bit 'makes Chip select high
andn outa, chs_Bit 'makes chip select low
andn outa , clk_bit 'ANDN it with the Clock Bit to make low
or outa , din_Bit 'makes the Din high
call #Tog_clk 'toggle clock line hi-low to read data
or outa , din_Bit 'makes the Din high
call #Tog_Clk 'toggle clock line hi-low to read data
mov temp2, pot_id 'we will read three bits from this pot
call #get_third_bit 'get bit
if_nz jmp #its_one 'jump to set it as needed
jmp #its_zero 'jump to set it as needed
its_one call #Set_next_bit1 'setting bit subroutine call
jmp #continue1 'go to next bit
its_zero call #Set_next_bit0 'setting bit subroutine call
continue1 call #get_second_bit 'get bit
if_nz jmp #its_one1 'jump to set it as needed
jmp #its_zero1 'jump to set it as needed
its_one1 call #Set_next_bit1 'setting bit subroutine call
jmp #continue2 'go to next bit
its_zero1 call #Set_next_bit0 'setting bit subroutine call
continue2 call #get_first_bit 'get bit
if_nz jmp #its_one2 'jump to set it as needed
jmp #its_zero2 'jump to set it as needed
its_one2 call #Set_next_bit1 'setting bit subroutine call
jmp #continue3 'go to next bit
its_zero2 call #Set_next_bit0 'setting bit subroutine call
continue3 andn outa , din_Bit 'makes Din low
call #Tog_Clk 'toggle clock line hi-low to read data
call #Tog_Clk 'toggle clock line hi-low to read data
mov dat_red, #0 'clear register we will read data into
mov bit_count, bits_read 'counter for number of bits we will read
read_bit mov temp, ina 'read in what is in all the input lines
andn temp, mask26 wz 'mask off except Dout line. Set Z flag
shl Dat_red, #1 'shift register left 1 bit for next bit
if_nz add Dat_red, #1 'if value + add 1 to data register
call #Tog_Clk 'toggle clock get next bit ready in Dout
sub bit_count, #1 wz 'decr the "bits read" counter. Set Z flag
if_nz jmp #read_bit 'go up do it again if counter not yet 0
wrlong Dat_red, mem 'write it in PAR to share it as P.Val
add mem, #4 'add 4 to hub address for next long
add pot_id, #1 'so we can look at next pot
mov temp2, pot_id 'recall what pot we are reading
sub temp2, pots_Read wz 'check if it is how many we want to read
if_nz jmp #gen2 'if it is not 0 go up and read next pot
jmp #generate 'go back beginning and do all pots again
'subroutines used
Set_next_bit0 andn outa , din_Bit 'makes Din low in 000 for line
call #Tog_Clk 'toggle clock line hi-low to read data
Set_next_bit0_ret ret 'return from this subroutine
Set_next_bit1 or outa , din_Bit 'makes Din high in 000 for line
call #Tog_Clk 'toggle clock line hi-low to read data
Set_next_bit1_ret ret 'return from this subroutine
Tog_Clk nop 'nop to settle signals
or outa, clk_bit 'make clock bit high
nop 'nop to settle signals
andn outa, clk_bit 'make clock bit low
nop 'nop to settle signals
Tog_Clk_ret ret 'return from this subroutine
Get_first_bit
mov temp2, pot_id 'get current pot number
andn temp2, mask0 wz 'get last bit
Get_first_bit_ret ret 'return
Get_second_bit
mov temp2, pot_id 'get current pot number
shr temp2, #1 'shift right 1 bit to get second bit
andn temp2, mask0 wz 'return
Get_second_bit_ret ret
Get_third_bit
mov temp2, pot_id 'get current pot number
shr temp2, #2 'shift right 2 bits to get third bit
andn temp2, mask0 wz 'return
Get_third_bit_ret ret
Set_dira long %00001011_00000000_00000000_00001111 'Set dira register
Chs_Bit long %00000001_00000000_00000000_00000000 'Chip select bit 24
Din_Bit long %00000010_00000000_00000000_00000000 'Data in bit 25
Dout_Bit long %00000100_00000000_00000000_00000000 'Data out bit 26
Clk_Bit long %00001000_00000000_00000000_00000000 'Clock bit 27
mask26 long %11111011_11111111_11111111_11111111 'Mask to read Dout bit
mask0 long %11111111_11111111_11111111_11111110 'Mask to read 0 bit
mem res 1
temp res 1 'temporary storage variable, misc
temp2 res 1 'temporary storage variable, misc
bit_count res 1 'temporary storage variable, read bit counter
Dat_Red res 1 'temporary storage variable, data being read
pot_id res 1 'current pot id number
pots_read res 1 'total number of pots to be read.
bits_Read res 1 'resolution of data read, 12 bits max
'---------------------------------------------------
tone org 0
mov dira, set_dirq 'sets direction of the prop pins
mov outa, set_dirq 'sets direction of the prop pins
mov mem9, par
mov delay, cnt
add delay, #16
loop or outa, test_bit
waitcnt delay, seconds
andn outa, test_bit
waitcnt delay, seconds
jmp #loop
Set_dirq long %00001011_00000000_00000000_00001111 'Set dira register
test_bit long %00000000_00000000_00000000_00000011
seconds long 8_000_000
mem9 res 1 'memory location for PAR variable
delay res 1
Here is the answer after about three days of banging my head against the wall
and adding and deleting code a line at a time. Very tedious but I learned a lot
Many many thanks to JonnyMac and Heater.
NOT EVERYTHING I SAID HERE MIGHT BE 100% RIGHT (REMEMBER I AM A BEGINNER!)
Need expert commentary and discussion on all this, please.
Here are my discoveries:
Each cog has to define the dira register identically
All shared variables have to be called in all cogs identically.
You cannot read more than 8 cogs from the MCP 3208 without getting in trouble.
You cannot display more pots than you actually read or there will be problems
Each cog has to be defined after everything for the previous cog has been defined.
Meaning constants and variables.
For a reason I do not understand. PASM does not isolate variables within each cog.
Each cog has to use different names for its variables. You cannot repeat variable form cog to cog
Then it works, maybe!!
Each cog has to define the dira register identically
Not so. This would be true if you want separate cogs to have output control over the same pin. There is a danger with this, however, because if one cog drives an output pin high it doesn't matter what the other cogs do, that pin will be high.
All shared variables have to be called in all cogs identically.
Shared variables indicate in the hub, hence there is only one way to call them (from PASM with rdlong, rdword, rdbyte). In order to share hub variables, each cog using a particular variable must know that variable's hub address.
You cannot read more than 8 cogs from the MCP 3208 without getting in trouble.
Unsure by what you mean with this; the MCP3208 is an eight-channel device, and there are only eight cogs in the Propeller. "More than 8" is a bit of a mystery....
You cannot display more pots than you actually read or there will be problems
And you can't wash windows you don't actually have.... You can always display the last reading of any sensor, and you don't have to read all sensors (or device channels) every time.
For a reason I do not understand. PASM does not isolate variables within each cog.
Each cog has to use different names for its variables. You cannot repeat variable form cog to cog
If you put more than one PASM section into an object file you will be limited. The key is to use a well-crafted wrapper to launch your PASM code that can be placed in its own file. That way you get easy re-use (no copy and paste) and you don't have variable name issues -- most of the time. Fair warning: within an object file you'll want to be careful not to re-use names for global variables in your PASM section.
I know I keep harping on this but your desire to do things in what you believe is an "easy" way instead of the correct way causes you all sorts of unnecessary frustration.
I think it is quite reasonable that using the same label twice in a source file is not allowed. No matter which COGs that code actually ends up running in, after all the compiler is not able to actually know which COG the code will be run in ultimately. I'm sure most assembly languages work this way. The idea of "scope" like that is mostly a high level language feature (local variables to functions for example). The PASM local vars with ":" is something I have never see elsewhere.
I agree with Jon, it's almost always better to put the PASM sections into different source files. Especially if they have unrelated functionality.
"put the PASM sections into different source files"
Please explain to me exactly what you mean by this so that I can do it right.
I thought that was what I did but apparently I am not. Confusion caused by my lack of
experience with formal assembly language jargon.
The MCP3208 object I posted above is an example of putting PASM code into its own file with Spin interface methods for that code. Typically, objects use a method called .start() to accept any parameters and launch the PASM code into its own cog. Many (if not most) objects that launch a PASM driver include a .stop() method to kill the PASM cog if that is ever required.
In the case where you want to create an object that uses shared hub memory you can pass the desired hub address as a parameter in the .start() method. Again, if you look at my MCP3208 object posted above you'll see that I'm doing that (the fourth parameter, dest, is the destination address for the ADC readings). The way that I constructed this object allows my client program to specify the address of an array of longs in the .start() method and then never have to manually call the object again. The design of the object causes the ADC to continuously read and place those readings at the target location in the hub. From the user prospective, there is a set of variables that update themselves based on the inputs to the ADC.
"source code" is the human readable text of the program as we type into the Prop Tool or whatever editor.
As opposed to the compiled "binary code" or "object code" which is what is loaded into a computer when we want to run it.
So "source file" is simply a normal file that contains source code.
A feature of Spin is that a single source file is used to define a single Spin object. You can't have two or more Spin objects in a file and you can't have a Spin object spanning over more than one file. Unlike C++, say, where many classes can be written in a single file or the methods of a class can be in different files if need be.
So in Spin each object is contained within a single source file. This includes the all the Spin methods of the object and any PASM that object may want to start in a COG.
So now: It is good programming practice to separate different functionality into different objects. So for example a UART goes into FullDuplexSerial.spin and your LED flasher code goes into LedFlasher.spin. The UART object contains nothing pertaining to LEDS and the LED object knows nothing about serial communications. This makes for easier to read code, helps make the different components reusable in other projects and has other benefits.
One of those other benefits is in code "maintainance". If you fix or change one are of functionality it is not going to break another because it is in its own module (Spin object / source file). You only have to ensure the memory interfaces used for communication between objects remains consistent. I belive this is the issue you have been fighting with here.
It makes sense therefore to put PASM code of different functionality into its own Spin object (source file) with a little Spin wrapper to start it in a a COG and perhaps stop it. Even if there is no other use Spin in that mainly PASM source file.
This way you have no worries about duplicate label names within a file. Or confusions about ORG etc etc.
As always there are times when there is a good reason to break these simple guidelines but I don't think we are there yet.
Ok guys,
Let me see if I got this right. I'm beating it down to the basics I can write up
What I did was use the SPIN cog to display to the data on the PST
and start two other cogs from the spin cog as PASM cogs 1 and 2.
Are you saying that the two PASM cogs should be set up as independent
cogs started from their own SPIN program and then put these in the files on disk as independent files
Then read them in as OBJ files used in my program.
Yes.
I see it like this:
1) One main program, however small, in Spin. In it's own file. The top level object. Generally this is the "application"
2) One or more lower level objects, in this case containing the PASM code we want to start in separate COGS. In their own files.
That top level objects uses obj statements to enable use of methods in the lower level objects.
It calls start methods in those lower level objects.
Those start methods get the PASM going in GOGs with GOGINIT.
Parameters giving the address(s) of any shared memory blocks for COG to COG communication are passed from top level to start methods to COGNEWs and into PARS as required.
Result: All your PASM blocks are neatly divided into functional areas, placed in their own objects within their own files.
No more fighting over duplicate labels or the location of ORGs etc.
First let me state that it is really nice to get two perspectives from two different experienced programmers in that it makes it easier for me to understand what they are trying to tell me. The discussion of the not so good rules that I had formulated, were specially useful in helping me further understand the structure of PASM.
Thank you JonnyMac and thank you Heater. You guys have been a godsend.
I will break the code up as suggested later today and see how it works. I need to get used to doing it this way. I will also have to go back and revise some of the things that I have written to incorporate this more sophisticated way of writing programs. Stuff like this is not really a problem, because I look forward to getting it right.
Book progress report. As you can probably tell from the level of the program that they are just discussing, I am moving along pretty good on the book. I am now doing the projects that were covered in my SPIN book in PASM. (Right now I am working on the Metronome). They won't be exactly the same, but hopefully useful comparisons will be able to be drawn from the two ways of undertaking these projects. I think, or at least I hope to be done early in spring. What with winter upon us in Illinois, there isn't a whole lot of stuff to do outside and I can concentrate on getting the project wrapped up. Now that I am finally getting a bit of the hang of writing simple stuff in PASM. It's moving a little faster.
If beginners reading your book are anything like me they will be happy to have bits of functionality split into small modules/objects/files. That way you can look at a small part and easily understand it then move on to the next module. If everything is crammed into a single file it gets overwhelming and hard to sort out what is what.
[FONT=Arial, sans-serif]Before we do anything we need to be aware of the following realities as regards the connections we make to the propeller chip.[/FONT]
[FONT=Arial, sans-serif]The propeller chip is designed to run on 3.3 Volts DC. Though it might tolerate running at 5 VDC power supply for short periods of time. This is absolutely not recommended.[/FONT]
[FONT=Arial, sans-serif]On start up and reset all input lines on the propeller are inputs. These inputs have a high impedance, and can conceivably be connected to five volts DC directly. However it is good practice to protect the chip by using a 1K to 10K ohm resistor in line with the inputs.[/FONT]
[FONT=Arial, sans-serif]If a line is programmed to be an output, and it is shorted to ground, we may have a disaster on our hands in that theoretically an infinite current could now flow to this output line, barring internal resistances. Things could get bad in a hurry! It could lead to the immediate destruction of circuitry internal to this line. Keep this in mind at all times. It is important to (almost always) have at least a 1K resistor in line with all output connections to limit the current and avoid this disaster. If the output connection will not drive the work with a 1K resistor limiting the current, you have to make sure that a shorting condition cannot every occur on the line before making the connection to your hardware.[/FONT]
You might want to be a little careful with generalities; if we follow the datasheet 1K is too low when the input signal is 5V. In Section 7.1 the max forward current through a pin with the protection diode biased is 500uA. In that same table we can see that the forward voltage of the protection diode is 0.3v. At this point, Ohm's Law is our friend:
(5 - (3.3 + 0.3)) / 0.0005 = 2800
So... for a 5V input the lowest value should be 2.8K, not 1K as you have now. I tend to use 3.3K in my own designs as that is a standard value; that said, any value higher is fine. In a project I'm doing now I have three other 10K resistors so I used 10K for the input protection to simplify the BOM.
Before we do anything we need to be aware of the following realities as regards the connections we make to the propeller chip.
The propeller chip is designed to run on 3.3 Volts DC. Though it might tolerate running at 5 VDC power supply for short periods of time. This is absolutely not recommended.
Harprit
That should read "The propeller chip is designed to run on 3.3 Volts DC, 3.6 Volts MAX. NEVER connect the Propeller to 5 Volts
Before we do anything we need to be aware of the following realities as regards the connections we make to the propeller chip.
On start up and reset all input lines on the propeller are inputs. These inputs have a high impedance, and can conceivably be connected to five volts DC directly. However it is good practice to protect the chip by using a 1K to 10K ohm resistor in line with the input
Harprit
[FONT=Arial, sans-serif]
[/FONT]On start up and reset all I/O PINS on the propeller are inputs.
Five volts should NEVER be connected to the Input without a resistor.
I need to say why we cannot connect 5V to the inputs directly.
Can you provide verbiage or guidance the I can use that would be suitable for beginners.
I need to say why we cannot connect 5V to the inputs directly.
Can you provide verbiage or guidance the I can use that would be suitable for beginners.
H
Jon's post #504 explains it. Direct from the Data Sheet.
What is the proper protocol for reading a variable created in one Cog from another Cog.
And then using it in the second Cog
I am including the first cog in the second cog as an OBJ and calling the method that created the value
I just cant seem to get it right.
This is all in PASM
HELP
Comments
I have chapter 1 on outputs pretty well in hand and I have a couple of three other chapters on things like the PAR function and reading potentiometers well along. I am on the chapter on inputs and interaction with the outputs that were learned in chapter 1. I have sketches on the drawings but there are not all in the text yet. Programs inserted but not yet polished and fully commented. I have about 120 pages done or about a third of the book. I think I'll be done by late spring.
Its moving along pretty good. Better than I thought but then again this should be the easy part!
I have not at all been impressed by the couple of books I bought as e-books. I found it extremely hard to jump back and forth for looking stuff up (which you need to do a lot in a technical beginner's book). Am thinking about providing hard copy only which could be done for about $20.00 postpaid if I don't let Amazon in on it. I'm still open on this one and I welcome comments on this so I can see what every one wants done.
H
I have a program in which I set the address for the 3 lines that select the channel to read directly thus
The above would read channel 0 on the IC
and it reads all 12 bits just fine every time
If I do this with subroutines thus with the subroutines as follows
It seems to read all 12 bits and then if I play with the pot
it reads the two least significant bits as 0s
If I set it up to read 11 bits or less everything is fine
I went through the code and the rest is identical in both programs
I even printed both programs out and checked them a line at a time.
The reason I want to use subroutines it to allow me to read all 8 channels from one cog in PASM.
I've been at it for two days. It is very repeatable.
Any ideas, comments or solutions? Has anyone seen this.
H
Will study it.
H
The PASM cog stores 4 values in hub memory with Par and offsets of 4 each etc
I can read and display the 4 values from the SPIN cog just fine.
So far so good
Then I start a third PASM cog to blink an LED
The LED blinks just fine
I can no longer read the values I had in hub memory
They are all 0s
What gives?
H
Where did I go wrong writing to hub in one PASM cog
and reading the data from a second PASM cog
or if you will
How does one do this right?
H
Those subroutines in turn use variables even further down the listing.
This all looks very suspicious to me.
Due to that second org the subrouines are assembled as if they are in a different COG location than they actually are when loaded into COG.
Can't see how that is going to work.
I haven't looked at your code but here's what you should check: both cogs know the address to be used. When you start each cog you will want to pass an address the hub that Cog 1 writes to and Cog #2 reads from. The key is that the setup cog (Cog #0, if you will) makes that decision and passes the common address to the others.
[Edit] Okay, I just looked. You're passing 0 instead of the hub address to your "tone" cog, and within that code you've comment out the line that picks up the hub address from the par register.
I can get the blinking working with the revised code. See below
but the variables in hub go to 0 as soon as I start the 3rd cog in PASM
I moved the 3rd cog down and separated out all its variables so they are
below it. Variable for the two PASM cogs now are under the code for each of them
However, since variable names cannot be repeated each hub has to use unique
variable names. I did that. I added dira and outa descriptions for the last cog.
H
and adding and deleting code a line at a time. Very tedious but I learned a lot
Many many thanks to JonnyMac and Heater.
NOT EVERYTHING I SAID HERE MIGHT BE 100% RIGHT
(REMEMBER I AM A BEGINNER!)
Need expert commentary and discussion on all this, please.
Here are my discoveries:
Each cog has to define the dira register identically
All shared variables have to be called in all cogs identically.
You cannot read more than 8 cogs from the MCP 3208 without getting in trouble.
You cannot display more pots than you actually read or there will be problems
Each cog has to be defined after everything for the previous cog has been defined.
Meaning constants and variables.
For a reason I do not understand. PASM does not isolate variables within each cog.
Each cog has to use different names for its variables. You cannot repeat variable form cog to cog
Then it works, maybe!!
On to the next chapter!!
H
Not so. This would be true if you want separate cogs to have output control over the same pin. There is a danger with this, however, because if one cog drives an output pin high it doesn't matter what the other cogs do, that pin will be high.
Shared variables indicate in the hub, hence there is only one way to call them (from PASM with rdlong, rdword, rdbyte). In order to share hub variables, each cog using a particular variable must know that variable's hub address.
Unsure by what you mean with this; the MCP3208 is an eight-channel device, and there are only eight cogs in the Propeller. "More than 8" is a bit of a mystery....
And you can't wash windows you don't actually have.... You can always display the last reading of any sensor, and you don't have to read all sensors (or device channels) every time.
If you put more than one PASM section into an object file you will be limited. The key is to use a well-crafted wrapper to launch your PASM code that can be placed in its own file. That way you get easy re-use (no copy and paste) and you don't have variable name issues -- most of the time. Fair warning: within an object file you'll want to be careful not to re-use names for global variables in your PASM section.
I know I keep harping on this but your desire to do things in what you believe is an "easy" way instead of the correct way causes you all sorts of unnecessary frustration.
I agree with Jon, it's almost always better to put the PASM sections into different source files. Especially if they have unrelated functionality.
"put the PASM sections into different source files"
Please explain to me exactly what you mean by this so that I can do it right.
I thought that was what I did but apparently I am not. Confusion caused by my lack of
experience with formal assembly language jargon.
Thanks
H
In the case where you want to create an object that uses shared hub memory you can pass the desired hub address as a parameter in the .start() method. Again, if you look at my MCP3208 object posted above you'll see that I'm doing that (the fourth parameter, dest, is the destination address for the ADC readings). The way that I constructed this object allows my client program to specify the address of an array of longs in the .start() method and then never have to manually call the object again. The design of the object causes the ADC to continuously read and place those readings at the target location in the hub. From the user prospective, there is a set of variables that update themselves based on the inputs to the ADC.
Nothing complicated.
"source code" is the human readable text of the program as we type into the Prop Tool or whatever editor.
As opposed to the compiled "binary code" or "object code" which is what is loaded into a computer when we want to run it.
So "source file" is simply a normal file that contains source code.
A feature of Spin is that a single source file is used to define a single Spin object. You can't have two or more Spin objects in a file and you can't have a Spin object spanning over more than one file. Unlike C++, say, where many classes can be written in a single file or the methods of a class can be in different files if need be.
So in Spin each object is contained within a single source file. This includes the all the Spin methods of the object and any PASM that object may want to start in a COG.
So now: It is good programming practice to separate different functionality into different objects. So for example a UART goes into FullDuplexSerial.spin and your LED flasher code goes into LedFlasher.spin. The UART object contains nothing pertaining to LEDS and the LED object knows nothing about serial communications. This makes for easier to read code, helps make the different components reusable in other projects and has other benefits.
One of those other benefits is in code "maintainance". If you fix or change one are of functionality it is not going to break another because it is in its own module (Spin object / source file). You only have to ensure the memory interfaces used for communication between objects remains consistent. I belive this is the issue you have been fighting with here.
It makes sense therefore to put PASM code of different functionality into its own Spin object (source file) with a little Spin wrapper to start it in a a COG and perhaps stop it. Even if there is no other use Spin in that mainly PASM source file.
This way you have no worries about duplicate label names within a file. Or confusions about ORG etc etc.
As always there are times when there is a good reason to break these simple guidelines but I don't think we are there yet.
Let me see if I got this right. I'm beating it down to the basics I can write up
What I did was use the SPIN cog to display to the data on the PST
and start two other cogs from the spin cog as PASM cogs 1 and 2.
Are you saying that the two PASM cogs should be set up as independent
cogs started from their own SPIN program and then put these in the files on disk as independent files
Then read them in as OBJ files used in my program.
H
I see it like this:
1) One main program, however small, in Spin. In it's own file. The top level object. Generally this is the "application"
2) One or more lower level objects, in this case containing the PASM code we want to start in separate COGS. In their own files.
That top level objects uses obj statements to enable use of methods in the lower level objects.
It calls start methods in those lower level objects.
Those start methods get the PASM going in GOGs with GOGINIT.
Parameters giving the address(s) of any shared memory blocks for COG to COG communication are passed from top level to start methods to COGNEWs and into PARS as required.
Result: All your PASM blocks are neatly divided into functional areas, placed in their own objects within their own files.
No more fighting over duplicate labels or the location of ORGs etc.
Thank you JonnyMac and thank you Heater. You guys have been a godsend.
I will break the code up as suggested later today and see how it works. I need to get used to doing it this way. I will also have to go back and revise some of the things that I have written to incorporate this more sophisticated way of writing programs. Stuff like this is not really a problem, because I look forward to getting it right.
Book progress report. As you can probably tell from the level of the program that they are just discussing, I am moving along pretty good on the book. I am now doing the projects that were covered in my SPIN book in PASM. (Right now I am working on the Metronome). They won't be exactly the same, but hopefully useful comparisons will be able to be drawn from the two ways of undertaking these projects. I think, or at least I hope to be done early in spring. What with winter upon us in Illinois, there isn't a whole lot of stuff to do outside and I can concentrate on getting the project wrapped up. Now that I am finally getting a bit of the hang of writing simple stuff in PASM. It's moving a little faster.
H.
Amen. And, frankly, it's a bad habit to teach beginners.
If not what changes are needed
[FONT=Arial, sans-serif]Before we do anything we need to be aware of the following realities as regards the connections we make to the propeller chip.[/FONT]
- [FONT=Arial, sans-serif]If a line is programmed to be an output, and it is shorted to ground, we may have a disaster on our hands in that theoretically an infinite current could now flow to this output line, barring internal resistances. Things could get bad in a hurry! It could lead to the immediate destruction of circuitry internal to this line. Keep this in mind at all times. It is important to (almost always) have at least a 1K resistor in line with all output connections to limit the current and avoid this disaster. If the output connection will not drive the work with a 1K resistor limiting the current, you have to make sure that a shorting condition cannot every occur on the line before making the connection to your hardware.[/FONT]
Harprit(5 - (3.3 + 0.3)) / 0.0005 = 2800
So... for a 5V input the lowest value should be 2.8K, not 1K as you have now. I tend to use 3.3K in my own designs as that is a standard value; that said, any value higher is fine. In a project I'm doing now I have three other 10K resistors so I used 10K for the input protection to simplify the BOM.
That should read "The propeller chip is designed to run on 3.3 Volts DC, 3.6 Volts MAX. NEVER connect the Propeller to 5 Volts
[/FONT]On start up and reset all I/O PINS on the propeller are inputs.
Five volts should NEVER be connected to the Input without a resistor.
I need to say why we cannot connect 5V to the inputs directly.
Can you provide verbiage or guidance the I can use that would be suitable for beginners.
H
Jon's post #504 explains it. Direct from the Data Sheet.
EDIT: The post may help:
http://forums.parallax.com/showthread.php?85474-How-to-safely-interface-a-5v-signal-to-the-propeller-See-Post-Reply-104&p=585920#post585920
Lots of good stuff in this Sticky:
http://forums.parallax.com/showthread.php?111166-Propeller-Resource-Index
Regards
H
And then using it in the second Cog
I am including the first cog in the second cog as an OBJ and calling the method that created the value
I just cant seem to get it right.
This is all in PASM
HELP
H