The following code alternates the LEDs on lines 0 and 1
It works (with your input)
What I want to know is, Is this the right way to do it for beginners
Also tell me what you mean by code tags.
con
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000
pub main
cognew(@toggle, 0)
dat
org 0
toggle mov dira, pin
mov time, cnt
add time, #9
:loop waitcnt time, delay
mov outa, pin2
waitcnt time, delay
mov outa, pin3
jmp #:loop
pin long %00000000_00000000_00000011
pin2 long %00000000_00000000_00000010
pin3 long %00000000_00000000_00000001
delay long 10_000_000
time res 1
A general rule is not to use mov on outa as it will also turn off all other pins that this cog have as outputs in dira.
or ' turn pin high
1100 'current state for example
0010 ' ora this value
1110 ' result
andn 'turn pin low
1110 'current state for example
1101 ' and this value, this would be the bit negative value of pin so that's the reason to use andN to save you from declearing a not version
1100 ' result
xor 'toggle pin.
use [ c o d e ] and [ / c o d e] without space in your postings.
You only show 24 bits in pin, to be correct you should have 4 * 8bits
or use: 1<<pin# (or also: |<pin#)
could also use (to get rid of the pin _00000011)
mov dira,pin2
or dira,pin3
or at least call it 'myoutputs"
and the other pin1 and pin 2
:loop waitcnt time, delay
mov outa, %01
waitcnt time, delay
xor outa, %11
jmp #:loop
This works but I really don't know why because I have tried a bunch of other
ways of doing it and they work too. I don't understand them either. Could you
write this loop for me exactly the way is should be with comments.
Using "MOV OUTA, value" is fine as long as the COG only drives a known set of outputs.
There are advantages in some cases and hazards in others. One of the more interesting instructions is "RDBYTE OUT, ptr". Often if a signal such as write enable WE* needs to be driven in addition to P0..P7, DIRA can be set to drive WE* at the same time. A pull up can be used for making WE* high with DIRA setting it to an input which allows other operations such as read.
Using "OR OUTA, value" and "ANDN OUTA, value" certainly have their place when complicated COG IO is being used. Very often however the instructions mentioned along with other operations on the bus can slow things down.
That really captured the essence of it and made me see it in a flash
And yes of course I knew that, but I could not see it for the life of me and
you made it so clear. I hope I can transfer such clarity to the readers in
the text.
Here is the next batch, there is still a lot of editing to do and the figures are not in place
The two LEDs to to lines 0 and 1
Change the program so that it looks like the following:
con
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000
pub main
cognew(@toggle, 0)
dat
org 0 'start of the program storage locations
toggle mov dira, pin 'pin now sets lines 0 and 1 as outputs
mov time, cnt 'sets the delay time to 10m cycles
add time, #9 'adds 9 to the time count
:loop waitcnt time, delay 'the wait instruction
mov outa, pin0on 'sets output 0 on and 1 off
waitcnt time, delay 'the wait instruction
mov outa, pin1on 'sets output 1 on and 0 off
jmp #:loop 'go back and loop.
pin long %00000000_00000000_00000000_00000011 'used to set 0 and 1 as outputs
kine0on long %00000000_00000000_00000000_00000001 'used to turn on line 0
line1on long %00000000_00000000_00000000_00000010 'used to turn on line 1
delay long 10_000_000 'delay cycles defined
time res 1 'storage location for time
Program XXX
Blinking lines alternately.
The above program XXX will blink the LEDs on lines 0 and 1 on and off alternately. This code is easy to read but it is archaic and it is not the best way to do it. An only slightly better way to write this program is shown next in Program XXX. Again the changes are to the looped part of the program
con
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000
pub main
cognew(@toggle, 0)
dat
org 0 'start of the program storage locations
toggle mov dira, pin 'pin now sets lines 0 and 1 as outputs
mov time, cnt 'sets the delay time to 10m cycles
add time, #9 'adds 9 to the time count
:loop waitcnt time, delay 'the wait instruction
or outa, %01 'sets output 0 on and 1 off
waitcnt time, delay 'the wait instruction
or outa, %10 'sets output 1 on and 0 off
jmp #:loop 'go back and loop.
delay long 10_000_000 'delay cycles defined
time res 1 'storage location for time
Program XXX a slightly better way to write program XXX
Blinking lines 0 and 1 alternately.
When you run the above program you will see that two LEDS can be turned on and off alternately with the above register assignments. The program shows you how to set the lines as Inputs and Outputs with simple statements and how to affect the I/O operation of the pins. However this is both the tedious and archaic way of doing it. A number of better ways to do it follow. In these examples we are using shorthand notation and binary math techniques to manipulate the registers. Handling registers in this way is important in almost all the things that we will be doing. The techniques we use next use the following principles:
There are three basic things we can do to one bit in one operation.
Change it from a low to high-level (0-->1)
Change it from a high to low-level (1-->0)
Invert its state, in other words toggle the bit. Make it high if low and low if high.
Assume that the current 4 bits under consideration are %1010
We can change any bit in this group to high with the ora operation on that bit.
1010 current content
ora 0001 addresses the last bit
1011 result: the last bit is turned on (changed from 0 to 1)
If we want to turn a bit off we can do it with the andn operation
1010 current content
andn 0010 addresses the third bit
1000 result: the third bit is turned off (changed from 1 to 0)
If we want to change (or toggle) the state of a bit we use the xor command
1010 current content
xor 0011 addresses the third and fourth bits.
1001 result: the third and fourth bit are toggled (changed from 1 to 0 and 0 to 1)
Now let us look at the looping part of the code in our program and see how we can use the above commands to make the LEDS flash alternatively.
The simplest way is to first set the two bits that control the LEDs to 01 or 10 with the outa command and then do a toggling procedure with the xor command.
dat
org 0 'start of the program storage locations
toggle mov dira, #%11 'pin now sets lines 0 and 1 as outputs
mov time, cnt 'sets the delay time to 10m cycles
add time, #20 'adds 12 to the time count
mov outa, #%01 'sets the two bits
:loop waitcnt time, delay
xor outa, #%11 'toggles bits 0 and 1
jmp #:loop
Now that we understand the simplest of bit manipulations and the creation of the simplest of loops. Let us expand on these idea to learn how to undertake some other often used techniques.
Shifting the bits in a register left and right.
The two instructions used to shift bits left and right are SHL and SHR. Bits can be shifted from 1 to 32 places within the 32 bit registers.
If we use a bit shifting technique to move the ON bit back and forth to alternate the coming on of the two bits we have been considering above, the data part of program would look like the listing shown in Program XXX. Here we have to wait after each shift to duplicate the effect in program XXX above.
dat
org 0 'start of the program storage locations
toggle mov dira, #%11 'pin now sets lines 0 and 1 as outputs
mov time, cnt 'sets the delay time to 10m cycles
add time, #20 'adds 12 to the time count
mov outa, #%01 'sets the two bits
:loop waitcnt time, delay 'delay
shl outa, #%1 'shift left one bit
waitcnt time, delay 'delay
shr outa, #%1 'shift back right one bit
jmp #:loop
Program XXX
Controlling off the LEDs by shifting the active bit left and right.
Creating Subroutines/Methods
PASM has the waitcnt command for creating pauses but we are going to ignore that in the immediate discussion.
First let us see how we call a subroutine in PASM. We will make the wait a part of a subroutine and then call the subroutine whenever we need to wait. This needs to be done after each shift command. The complete code for this is
con
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000
pub main
cognew(@toggle, 0)
dat
org 0 'start of the program storage locations
toggle mov dira, #%11 'pin now sets lines 0 and 1 as outputs
mov time, cnt 'sets the delay time to 10m cycles
add time, #200 'adds 12 to the time count
mov outa, #%01 'sets the two bits
:loop
call #clkdelay 'call the delay subroutine
shl outa, #%1 'shift left one bit
call #clkdelay 'call the delay subroutine
shr outa, #%1 'shift back right one bit
jmp #:loop
clkdelay waitcnt time, delay 'the delay subroutine
clkdelay_ret ret 'return for delay subroutine
delay long 10_000_000 'delay cycles defined
time res 1 'location for time
Program XXX
Places the waitcnt command in a subroutine.
Often we need to be able to do something a fixed number of times and then do something else. In order to do this we need to learn how to set up counters. Let us use a counter that uses waits of one quarter (0.25 seconds) repeatedly to make up the delays we need in our programs from time to time. The wait period will need to run through 80_000_000_/4 cycles of an empty loop before exiting. We will then place this method in our blink routine to make sure it works.
Again: our clock is running at 80 MHz, so one second takes 80_000_000 cycles and 0.25 seconds take 20_000_000 cycles. We also know that the average instruction takes 4 clock cycles. So our 0.25 second subroutine has to have 5_000_000 iterations through its loop.(Our counts are not exact because we are not counting every instruction but it will be close enough for what we are trying to learn at this time. We will learn to count exact cycles later on in the book)
con
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000
pub main
cognew(@toggle3, 0)
dat
org 0 'start of the program storage locations
toggle3 mov dira, #%11 'pin now sets lines 0 and 1 as outputs
mov outa, #%01 'sets the two bits
:loop
call #clkdelay 'call the delay subroutine
shl outa, #%1 'shift left one bit
call #clkdelay 'call the delay subroutine
shr outa, #%1 'shift back right one bit
jmp #:loop
clkdelay mov time, deltime 'the delay subroutine, load deltime into toime
take4 'imternal to subroutine flag
sub time, #1 wz 'sub 1 from time and set flag if 0
if_nz jmp #take4 'if flag not 0 go back to take4
clkdelay_ret ret 'return for delay subroutine
deltime long 5_000_000 'time of delay
time res 1 'location for time
Program XXX
Subroutine creation and use for 0.25 second delay.
I'm not sure archaic is a good word in the contexts you use. Simple seems a more fitting term.
The first 2 examples wont compile of course, so I suppose in that way they are "old and no longer in use."
The new code in your next example is bad:
:loop waitcnt time, delay 'the wait instruction
or outa, %01 'sets output 0 on and 1 off
...
What's wrong with this? %01 refers to the content of COG address 1 which in the given case will be $a0bfcc?? .... Can't tell extactly what value it will be because pin is not in the second listing and the program will not compile. The program might actually half work if pin was on register address 10.
Your last example gives a 0.5 second delay. If could be about 0.25s delay if you used "djnz time,#$".
Other than those problems, I like your progressive examples.
Also, you don't get anywhere with or to switch pins on and off. On yes, off never. I think what you meant to use was mov which would at least match your comments.
Here is my version, using only mov as first stepping stone with mostly indirect numbers.
CON
_clkmode = xtal1 + pll16x 'Standard clock mode * crystal frequency = 80 MHz
_xinfreq = 5_000_000
PUB main
cognew(@entry,0)
DAT
entry mov dira,#%11 'pin p0 and p1 as output
mov time,#5 'first time we only want to wait 0 clock(+ 5 for the overhead)
add time,cnt 'add current counter value to time
loop waitcnt time, delay 'wait until counter matches time, when done add delay to time
mov outa,#%01 'set pin p0 high, p1 low
waitcnt time, delay 'wait, time had 15million added to it from above waitcnt opcode
mov outa,#%10 'set pin p0 low, p1 high
jmp #loop 'jmp to loop, don't forget the # sign
delay long 15_000_000 '15 million clock cycles, underscores is just for easy reading by human eyes.
time res 1
2nd updated version, that shows better programming style.
CON
_clkmode = xtal1 + pll16x 'Standard clock mode * crystal frequency = 80 MHz
_xinfreq = 5_000_000
PUB main
cognew(@entry,0)
DAT
entry mov dira,output 'set outputs
mov time,#5 'first time we only want to wait 0 clock(+ 5 for the overhead)
add time,cnt 'add current counter value to time
loop waitcnt time, delay 'wait until counter matches time, when done add delay to time.
or outa, pin1 'set pin1 high
andn outa,pin2 'set pin2 low
waitcnt time, delay 'wait, time had 15million added to it from above waitcnt opcode.
andn outa,pin1 'set pin1 low
or outa, pin2 'set pin2 high
jmp #loop 'jmp to loop, don't forget the # sign.
delay long 15_000_000 '15 million clock cycles
pin1 long 1<<0 '1 shifted zero(p0) steps up = 1 (= %00000001)
pin2 long 1<<1 '1 shifted one(p1) step up = 2 (= %00000010)
output long 1<<0 | 1<<1 '| =or, so ora the pins we want as output
time res 1
3rd way with xor, and declaring values in CON sections.
CON
_clkmode = xtal1 + pll16x 'Standard clock mode * crystal frequency = 80 MHz
_xinfreq = 5_000_000
pin1= 0 'p0 (use a value between 0 and 31)
pin2= 1 'p1 (though 28-31 is sometimes reserved)
PUB main
cognew(@entry,0)
DAT
entry mov dira,output 'set outputs
or outa,led1 'set pin1 high
mov time,#5 'first time we only want to wait 0 clock(+ 5 for the overhead)
add time,cnt 'add current counter, don't add code between this and next line
' without adding at least 4 to the #5 above for each line added.
:loop waitcnt time, delay 'wait until counter matches time, when done add delay to time.
xor outa, output 'bit invert all output pins.
jmp #:loop 'jmp to :loop, don't forget the # sign.
delay long 15_000_000 '15 million clock cycles
led1 long 1<<pin1 '1 shifted steps up
led2 long 1<<pin2 '1 shifted steps up
output long 1<<pin1 | 1<<pin2 '| =or, so ora the pins we want as output
time res 1 'hint: time can be replaced with cnt, yes cnt the shadow register.
@Jazzed
I compiled and ran all the programs. They all work, though you may well be right about using
better technique, (which I have not yet developed and welcome help with).
@Kuroneko
Good point. I understand what you are saying.
@ Tony, I'll add your code as another way of doing it. Later during the final edits. Thanks.
When do I use
:loop and jmp #l:loop
and when do I use l
loop and jmp #loop
and is there a difference?
:loop, with colon you are allowed to reuse that name so you can call them all by the name :loop (with another label name in between)
instead of having to use loop1, loop2 etc.
good if you cut and paste code in as they all probably use the name loop somewhere.
@Jazzed
I compiled and ran all the programs. They all work, though you may well be right about using
better technique, (which I have not yet developed and welcome help with).
Technique is subjective - compiling is not.
It's very likely that you had copy/paste errors. The second program as originally posted would never even successfully compile without the pin variable defined. I suggest you be more careful with what you post.
I am writing the code (done) to read two potentiometers from an MCP 3202 chip (all of it in PASM)
When I get done, the data is in two variables in the Cog that read them (in PASM.)
How do I relate these two registers to two named values in SPIN so that I can send them
to the Parallax Serial Terminal to see if what I did was right.
I am writing the code (done) to read two potentiometers from an MCP 3202 chip (all of it in PASM)
When I get done, the data is in two variables in the Cog that read them (in PASM.)
How do I relate these two registers to two named values in SPIN so that I can send them
to the Parallax Serial Terminal to see if what I did was right.
Harprit
Add a mailbox with the start address passed to PAR via cognew. Example:
{{
===========================================================
sample pasm mailbox interface
===========================================================
}}
con
_clkmode = xtal1 + pll16x
_clkfreq = 80_000_000
var
long mailbox, data1, data2
long cog
obj ser: "FullDuplexSerial"
{{
-----------------------------------------------------------
main demo loop
-----------------------------------------------------------
}}
pub demo
' start up the serial driver
'
ser.start(31,30,0,115200)
' wait for serial driver to start up ... very imprecise
'
waitcnt(clkfreq+cnt)
' start up adc reader pasm
'
start
' read and display values once per second
'
repeat
readAdc ' assuming a command is used for the read
ser.hex(data1, 8) ' show data
ser.tx(" ")
ser.hex(data2, 8)
ser.str(string($d,$a)) ' newline for everyone
waitcnt(clkfreq+cnt) ' wait a second
{{
-----------------------------------------------------------
start the pasm code with mailbox control
-----------------------------------------------------------
}}
pub start
' start with non-zero in mailbox so we know when cog has started
mailbox := -1
cog := cognew(@pasm, @mailbox)+1
' wait for cog to start
repeat while mailbox
{{
-----------------------------------------------------------
cogstop method
-----------------------------------------------------------
}}
pub stop
if cog > 0
cogstop(cog~ - 1)
{{
-----------------------------------------------------------
mailbox interface code for reading ADC on demand
-----------------------------------------------------------
}}
pub readAdc
'' optional read command
'' if not used, remove cmdWait code in the pasm
mailbox := -1
repeat while mailbox
{{
===========================================================
pasm for using mailbox interface
===========================================================
}}
dat org
pasm
cmd mov $, par
cmdp mov $, cmd
d1p add cmd, #4
d2p mov d1p, cmd
d1 add cmd, #4
d2 mov d2p, cmd
zero long 0 ' 0 is nop and is used for command done
cmdDone ' tell start code the COG is running
wrlong zero, cmdp
cmdWait ' optionallly wait for read command
rdlong cmd, cmdp
' TODO add waitcnt here to lower COG power consumption
tjz cmd, #cmdWait
' read adc data to d1 and d2
' TODO call your read subroutine(s) here
wrlong d1, d1p
wrlong d2, d2p
'
' end of main loop
'
jmp #cmdDone
{{
===========================================================
end of file
===========================================================
}}
I use short variable names since I find such code easier to read.
I find comments above statements easier to read, but I also put them in line often.
Notice that cmdWait is a loop that does nothing until a valid command comes.
This loop is optional, but with a waitcnt can save power.
Result below prints the instructions at address d1 and d2.
Your PASM ADC reader code would save ADC values there.
c:\Propeller\harprit>bstc -d com6 -p0 -f -Ograux -L c:\bstc\spin mailbox_demo.spin
Brads Spin Tool Compiler v0.15.4-pre3 - Copyright 2008,2009 All rights reserved
Compiled for i386 Win32 at 19:57:58 on 2010/01/15
Loading Object mailbox_demo
Loading Object FullDuplexSerial
Program size is 740 longs
Compiled 230 Lines of Code in 0.01 Seconds
We found a Propeller Version 1
Propeller Load took 0.609 Seconds
c:\Propeller\harprit>uterm com6 115200
80FC0004 A0BC0600
80FC0004 A0BC0600
80FC0004 A0BC0600
80FC0004 A0BC0600
80FC0004 A0BC0600
←
All done.
Thanks for taking a such large chunk of your precious time to write the code you posted above
I will have questions as I try to understand it all but right now I have work to do just understanding it.
Thanks again
Thanks for taking a such large chunk of your precious time to write the code you posted
No big deal. I do it all the time, so it's relatively quick and easy.
The hard part is making sure it compiles.
The harder part is making sure it works.
The hardest part is communicating what it's all supposed to do
On retrospect, I realized the first lines of pasm were not obvious. Here are some comments.
dat org
pasm
cmd mov $, par ' read par value to cmd with "$" ($ means the address of this line).
cmdp mov $, cmd ' cmd is used as a temp variable, set cmdp to par address via cmd
d1p add cmd, #4 ' add 4 to address so that we can find data 1's pointer "d1p"
d2p mov d1p, cmd ' assign address to data 1 pointer
d1 add cmd, #4 ' add 4 to address so that we can find data 2's pointer "d2p"
d2 mov d2p, cmd ' assign address to data 2 pointer - d1 and d2 get overwritten
I've been banging my head against the wall all afternoon with the above code and now I have a question
I have not tried this yet but...
Why is it not possible for me to write a 32 bit value to the main memory with WRLONG in the cog written in PASM and then
read that same memory location with WORD in another cog written in SPIN
now to find out this I would store two different values that are easy to discover as hexnumbers and then using PASD to find the locations in the HUB-RAM
maybe it has something to do with that SPIN-variables become reordered in HUB-RAM longs words bytes
Please add your code to a posting. With your code it will be much easier to explain what happens
I've been banging my head against the wall all afternoon with the above code and now I have a question
Maybe you should limit your wall banger time to 15 minutes or less before asking for help.
I suspect that you need to do lots more reading.
All you have to do is use the same address for your PASM write as for your SPIN read. it doesn't matter how you get your address - you may end up corrupting something though if it's not allocated as a variable.
Yes, by all means paste your code here, and someone can tell you what the problem is.
I've seen using a debugger mentioned twice now. Maybe you really should follow that advice.
... it might be as simple as a long not fitting into a word ...
Following up on [post=1020338]post #40[/post] you really should get into the habit of matching your comments with the code. Finding blindingly obvious contradictions isn't going to help the beginner and they get distracted from the actual lesson or - worse - give up.
@jazzed:
tried again your debugger. You wrote PASD is "limited". Could you please explain what are the advantages of BMA-Debugger over PASD?
looking up the memory through dumping and then have the code out of sight looks uncomfortable to me. So from testing it I did not see what the advantage is
I had hoped that some beginner would participate but so far no one has.
Beginners would lend a different perspective.
My postings reflect what I am up to at my computer.
This is at least 50 times more difficult than writing in SPIN. I have to feel my way through the haze.
Comments received have been very helpful special the sample programs because I need to see real code that I can understand.
Later today I will post my efforts to write a word in PASM and read it in SPIN. So far I am having difficulty addressing the same memory location in hub from both cogs.
See following (kuroneko's) post, so no need to me to post by me.
You wrote PASD is "limited". Could you please explain what are the advantages of BMA-Debugger over PASD?
Everyone has their own tastes.
What is simple to some may be overwhelming for others.
What is limiting to some may be overwhelming for others.
What is less limiting to some may be overwhelming for others.
Things with less limits tend to be more complicated in some ways:
Think MAC -vs- UNIX ... up until OS-X that is
BMA/PASD Comparisons
BMA PASD
Step animation record No step animation record
Multi-COG debugger Single COG debugger only
Can use any complier Must use Propeller Tool
Command line tool + terminal program Windows program
Needs long 0 [8] at addr 0 for stub User adds 12 instructions at addr 0
Requires using listings for symbols Has symbol address/data in windows
BMA is best used in conjunction with BSTC / Homespun listings.
Listings show the best picture of what is in the chip at startup.
Listings show all object DAT addresses and data.
Listings show all spin code.
PASD scans the Propeller Tool and can cause lock up if compile fails.
Both provide COG + HUB memory view/edit
BMA also has print special register command.
BMA is an on chip debugger and can be used with SpinSim.
PASD hub footprint is smaller than BMA.
The step animation record below is of great value.
It steps through kuroneko's example showing all transactions.
Displayed are the COG address, any special effects (none),
instruction name/code, destination and source information.
Thanks.
I got out my demo board and ran your program.
Then..
I eliminated the last two lines of your program as I could not understand what they did.
I still works but I wondered of I had missed something. Did I do it right?
Can one say that the RES area has to list all variables that will be used in the program and
that all constants have to be listed as " value long 2000" which means that these are
the equivalent of the VAR and CON areas of a SPIN program?
I really appreciate your crisp input.
@stephan
Though I intend to learn to use a debugger later on, I am not sure that they are useful beginner's tools.
Too long a learning curve to allow them to use them effectively in the beginning stages.
Besides they want to learn PASM not debuggers. I think that comes later.
I may change my mind and (at this stage) I may well be wrong.
I say this because with all its mistakes in it, beginners still like my very simple SPIN book very much.
The criticisms are from people who already know SPIN rather well, but then the book is not for them!
I would like the PASM book to be similarly received.
Anyone with enough desire will learn anything by any means including buying a book written by someone who knows little about the subject. Published books "project" a certain amount of credibility. People who know better and have several worthless beginners books will always be more selective. Fortunately for publishers, there always will be a near infinite flow of naive beginners.
I hate saying this.
I don't think that was helpful, polite or necessary.
I know that you are very knowledgeable about the subject and respect you for that,
however such comments are not furthering the discussion.
I sincerely need and ask for help. Above comments are not helping or encouraging.
Would you please, if I may, seriously consider opting out of this discussion.
If not, I will ignore and not respond to anything nonconstructive that you have to say.
Comments
The following code alternates the LEDs on lines 0 and 1
It works (with your input)
What I want to know is, Is this the right way to do it for beginners
Also tell me what you mean by code tags.
con
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000
pub main
cognew(@toggle, 0)
dat
org 0
toggle mov dira, pin
mov time, cnt
add time, #9
:loop waitcnt time, delay
mov outa, pin2
waitcnt time, delay
mov outa, pin3
jmp #:loop
pin long %00000000_00000000_00000011
pin2 long %00000000_00000000_00000010
pin3 long %00000000_00000000_00000001
delay long 10_000_000
time res 1
or ' turn pin high
1100 'current state for example
0010 ' ora this value
1110 ' result
andn 'turn pin low
1110 'current state for example
1101 ' and this value, this would be the bit negative value of pin so that's the reason to use andN to save you from declearing a not version
1100 ' result
xor 'toggle pin.
use [ c o d e ] and [ / c o d e] without space in your postings.
You only show 24 bits in pin, to be correct you should have 4 * 8bits
or use: 1<<pin# (or also: |<pin#)
could also use (to get rid of the pin _00000011)
mov dira,pin2
or dira,pin3
or at least call it 'myoutputs"
and the other pin1 and pin 2
Looking at just the loop
:loop waitcnt time, delay
mov outa, %01
waitcnt time, delay
xor outa, %11
jmp #:loop
This works but I really don't know why because I have tried a bunch of other
ways of doing it and they work too. I don't understand them either. Could you
write this loop for me exactly the way is should be with comments.
Harprit.
But it's always good to learn coding in the 'correct way' so the code can be easily expanded.
I toggle two leds here, I would not say the code is in a correct way. (click see more to see the source code)
http://www.youtube.com/watch?v=_5zRxYyfDN0
There are advantages in some cases and hazards in others. One of the more interesting instructions is "RDBYTE OUT, ptr". Often if a signal such as write enable WE* needs to be driven in addition to P0..P7, DIRA can be set to drive WE* at the same time. A pull up can be used for making WE* high with DIRA setting it to an input which allows other operations such as read.
Using "OR OUTA, value" and "ANDN OUTA, value" certainly have their place when complicated COG IO is being used. Very often however the instructions mentioned along with other operations on the bus can slow things down.
Jazzed. Your note was over my head.
Harprit
That really captured the essence of it and made me see it in a flash
And yes of course I knew that, but I could not see it for the life of me and
you made it so clear. I hope I can transfer such clarity to the readers in
the text.
Harprit.
The two LEDs to to lines 0 and 1
Change the program so that it looks like the following: Program XXX
Blinking lines alternately.
The above program XXX will blink the LEDs on lines 0 and 1 on and off alternately. This code is easy to read but it is archaic and it is not the best way to do it. An only slightly better way to write this program is shown next in Program XXX. Again the changes are to the looped part of the program Program XXX a slightly better way to write program XXX
Blinking lines 0 and 1 alternately.
When you run the above program you will see that two LEDS can be turned on and off alternately with the above register assignments. The program shows you how to set the lines as Inputs and Outputs with simple statements and how to affect the I/O operation of the pins. However this is both the tedious and archaic way of doing it. A number of better ways to do it follow. In these examples we are using shorthand notation and binary math techniques to manipulate the registers. Handling registers in this way is important in almost all the things that we will be doing. The techniques we use next use the following principles:
There are three basic things we can do to one bit in one operation.
Change it from a low to high-level (0-->1)
Change it from a high to low-level (1-->0)
Invert its state, in other words toggle the bit. Make it high if low and low if high.
Assume that the current 4 bits under consideration are %1010
We can change any bit in this group to high with the ora operation on that bit.
1010 current content
ora 0001 addresses the last bit
1011 result: the last bit is turned on (changed from 0 to 1)
If we want to turn a bit off we can do it with the andn operation
1010 current content
andn 0010 addresses the third bit
1000 result: the third bit is turned off (changed from 1 to 0)
If we want to change (or toggle) the state of a bit we use the xor command
1010 current content
xor 0011 addresses the third and fourth bits.
1001 result: the third and fourth bit are toggled (changed from 1 to 0 and 0 to 1)
Now let us look at the looping part of the code in our program and see how we can use the above commands to make the LEDS flash alternatively.
The simplest way is to first set the two bits that control the LEDs to 01 or 10 with the outa command and then do a toggling procedure with the xor command. Now that we understand the simplest of bit manipulations and the creation of the simplest of loops. Let us expand on these idea to learn how to undertake some other often used techniques.
Shifting the bits in a register left and right.
The two instructions used to shift bits left and right are SHL and SHR. Bits can be shifted from 1 to 32 places within the 32 bit registers.
If we use a bit shifting technique to move the ON bit back and forth to alternate the coming on of the two bits we have been considering above, the data part of program would look like the listing shown in Program XXX. Here we have to wait after each shift to duplicate the effect in program XXX above. Program XXX
Controlling off the LEDs by shifting the active bit left and right.
Creating Subroutines/Methods
PASM has the waitcnt command for creating pauses but we are going to ignore that in the immediate discussion.
First let us see how we call a subroutine in PASM. We will make the wait a part of a subroutine and then call the subroutine whenever we need to wait. This needs to be done after each shift command. The complete code for this is Program XXX
Places the waitcnt command in a subroutine.
Often we need to be able to do something a fixed number of times and then do something else. In order to do this we need to learn how to set up counters. Let us use a counter that uses waits of one quarter (0.25 seconds) repeatedly to make up the delays we need in our programs from time to time. The wait period will need to run through 80_000_000_/4 cycles of an empty loop before exiting. We will then place this method in our blink routine to make sure it works.
Again: our clock is running at 80 MHz, so one second takes 80_000_000 cycles and 0.25 seconds take 20_000_000 cycles. We also know that the average instruction takes 4 clock cycles. So our 0.25 second subroutine has to have 5_000_000 iterations through its loop.(Our counts are not exact because we are not counting every instruction but it will be close enough for what we are trying to learn at this time. We will learn to count exact cycles later on in the book) Program XXX
Subroutine creation and use for 0.25 second delay.
Harprit
The first 2 examples wont compile of course, so I suppose in that way they are "old and no longer in use."
The new code in your next example is bad:
What's wrong with this? %01 refers to the content of COG address 1 which in the given case will be $a0bfcc?? .... Can't tell extactly what value it will be because pin is not in the second listing and the program will not compile. The program might actually half work if pin was on register address 10.
Your last example gives a 0.5 second delay. If could be about 0.25s delay if you used "djnz time,#$".
Other than those problems, I like your progressive examples.
2nd updated version, that shows better programming style.
3rd way with xor, and declaring values in CON sections.
I compiled and ran all the programs. They all work, though you may well be right about using
better technique, (which I have not yet developed and welcome help with).
@Kuroneko
Good point. I understand what you are saying.
@ Tony, I'll add your code as another way of doing it. Later during the final edits. Thanks.
When do I use
:loop and jmp #l:loop
and when do I use l
loop and jmp #loop
and is there a difference?
Harprit.
instead of having to use loop1, loop2 etc.
good if you cut and paste code in as they all probably use the name loop somewhere.
It's very likely that you had copy/paste errors. The second program as originally posted would never even successfully compile without the pin variable defined. I suggest you be more careful with what you post.
--Steve
I am writing the code (done) to read two potentiometers from an MCP 3202 chip (all of it in PASM)
When I get done, the data is in two variables in the Cog that read them (in PASM.)
How do I relate these two registers to two named values in SPIN so that I can send them
to the Parallax Serial Terminal to see if what I did was right.
Harprit
Add a mailbox with the start address passed to PAR via cognew. Example:
I use short variable names since I find such code easier to read.
I find comments above statements easier to read, but I also put them in line often.
Notice that cmdWait is a loop that does nothing until a valid command comes.
This loop is optional, but with a waitcnt can save power.
Result below prints the instructions at address d1 and d2.
Your PASM ADC reader code would save ADC values there.
Thanks for taking a such large chunk of your precious time to write the code you posted above
I will have questions as I try to understand it all but right now I have work to do just understanding it.
Thanks again
Harprit.
No big deal. I do it all the time, so it's relatively quick and easy.
The hard part is making sure it compiles.
The harder part is making sure it works.
The hardest part is communicating what it's all supposed to do
On retrospect, I realized the first lines of pasm were not obvious. Here are some comments.
Cheers.
I've been banging my head against the wall all afternoon with the above code and now I have a question
I have not tried this yet but...
Why is it not possible for me to write a 32 bit value to the main memory with WRLONG in the cog written in PASM and then
read that same memory location with WORD in another cog written in SPIN
Harprit
maybe it has something to do with that SPIN-variables become reordered in HUB-RAM longs words bytes
Please add your code to a posting. With your code it will be much easier to explain what happens
best regards
Stefan
I suspect that you need to do lots more reading.
All you have to do is use the same address for your PASM write as for your SPIN read. it doesn't matter how you get your address - you may end up corrupting something though if it's not allocated as a variable.
Yes, by all means paste your code here, and someone can tell you what the problem is.
I've seen using a debugger mentioned twice now. Maybe you really should follow that advice.
I use this http://forums.parallax.com/showthread.php?115068-BMA-Multi-COG-PASM-Debugger-V1.9-Now-Available&p=829927&viewfull=1#post829927. I started using PASD long ago, but quickly found it "limiting."
I use my debugger any time i don't understand why something is not working, and it's usually something really stupid that's causing the problem.
A debugger can present several new challenges though. Sometimes more real-time tools are necessary.
Following up on [post=1020338]post #40[/post] you really should get into the habit of matching your comments with the code. Finding blindingly obvious contradictions isn't going to help the beginner and they get distracted from the actual lesson or - worse - give up.
tried again your debugger. You wrote PASD is "limited". Could you please explain what are the advantages of BMA-Debugger over PASD?
looking up the memory through dumping and then have the code out of sight looks uncomfortable to me. So from testing it I did not see what the advantage is
best regards
Stefan
Beginners would lend a different perspective.
My postings reflect what I am up to at my computer.
This is at least 50 times more difficult than writing in SPIN. I have to feel my way through the haze.
Comments received have been very helpful special the sample programs because I need to see real code that I can understand.
Later today I will post my efforts to write a word in PASM and read it in SPIN. So far I am having difficulty addressing the same memory location in hub from both cogs.
See following (kuroneko's) post, so no need to me to post by me.
Harprit
What is simple to some may be overwhelming for others.
What is limiting to some may be overwhelming for others.
What is less limiting to some may be overwhelming for others.
Things with less limits tend to be more complicated in some ways:
Think MAC -vs- UNIX ... up until OS-X that is
The step animation record below is of great value.
It steps through kuroneko's example showing all transactions.
Displayed are the COG address, any special effects (none),
instruction name/code, destination and source information.
Here is kuroneko's LED blinker with changes to support the BMA debugger.
Thanks.
I got out my demo board and ran your program.
Then..
I eliminated the last two lines of your program as I could not understand what they did.
I still works but I wondered of I had missed something. Did I do it right?
Can one say that the RES area has to list all variables that will be used in the program and
that all constants have to be listed as " value long 2000" which means that these are
the equivalent of the VAR and CON areas of a SPIN program?
I really appreciate your crisp input.
@stephan
Though I intend to learn to use a debugger later on, I am not sure that they are useful beginner's tools.
Too long a learning curve to allow them to use them effectively in the beginning stages.
Besides they want to learn PASM not debuggers. I think that comes later.
I may change my mind and (at this stage) I may well be wrong.
I say this because with all its mistakes in it, beginners still like my very simple SPIN book very much.
The criticisms are from people who already know SPIN rather well, but then the book is not for them!
I would like the PASM book to be similarly received.
Harprit
I hate saying this.
I don't think that was helpful, polite or necessary.
I know that you are very knowledgeable about the subject and respect you for that,
however such comments are not furthering the discussion.
I sincerely need and ask for help. Above comments are not helping or encouraging.
Would you please, if I may, seriously consider opting out of this discussion.
If not, I will ignore and not respond to anything nonconstructive that you have to say.
Harprit