This thread is becoming an interesting study in so many things that may or may not be helpful to Harprit and his efforts. This posting certainly follows those lines!
...
This is one of the most wonderful posts I've read in a while. Thank you for taking the time to write it.
Back to basic guys!
I know this is not quite right but...
Why does this not open up a third cog and toggle line 12
It compiles but I get a dead line 12
In other word, how do you start two PASM cogs
PUBnull | P_Val, Delay_Valcognew(@generate, @P_Val) ' start second cog at "generate" cognew(@Toggle, @Delay_Val) 'start third cog at togglerepeat'do something unrelated to other cogsDATOrg0
generate movouta, set_dira
jmp #generate
org30
toggle movdira, set_dira
movouta, pin_hi
movouta, pin_lo
jmp #toggle
'Constants. This section is similar to the CON block in SPIN
Set_dira long%00000001_00000000_00010000_00000000'Set dira register
Pin_Hi long%00000000_00000000_00010000_00000000
Pin_Lo long%00000000_00000000_00010000_00000000
My goal is to provide enough information to allow someone who knows nothing about
binary math
PASM
assembly language
jargon infested writings
to want to mess around with PASM and his propeller chip
Maybe inspire him or her to learn more from you guys.
I will keep in very simple and disappoint some of you who think there is no way I can get this done
Here is letter from some one who know very little but is very excited. I got it this morning.
All he is really turned on about is controlling a few LEDs.
Hopefully some day he will make a great Tech or even an Engineer. Made my day.
Dear Sir:
I am enjoying your book immensely! I especially enjoyed seeing your section on page 23/24, A Fundamental Reality We Have to Consider, as I need to have LEDs on all my I/O pins (not multiplexed, and more than 10 ma. per pin -- very bright). I plan on using a LYP47F reverse mount LED from OSRAM (sold by Mouser). (The reverse mount and small size are why I chose that style.)
I was a bit surprised to see the 7404 driving LEDs, so I set up six on a chip with 470 ohms as you suggested. Ran all afternoon until I had to shut down and go home! I measured the total chip current (ICCL = 49.2 ma). This exceeded the data sheet spec (33 ma max) a bit but did not blow any outputs. As a personal hobby application or learning situation I can see the 7404 working fine.
However, I have been looking for a long time for a chip for product development where reliability and safety margins can be considered in the design. I am wondering if you might know of anything more robust (and as simple as the 7404) that you could suggest where I would have more driving current per LED. You are the only author that has addressed the situation I face. (Another issue was how to interface a 5 volt family to the Propeller. So I thank you for moving me forward on my project!)
I am looking forward to the remainder of your book. Very well done.
(I like that fact that you do not repeat all the material in other reference sources, but direct the reader to the sources. That makes your book a faster/easier read and more pages for what it is you want to guide us through.)
Lots of things going on here - not sure most of them are what you intend.
First, the startup state on a reset or freshly started COG for the direction register (DIRA) is 0, that is, all pins set as INPUT
Your first COGNEW starts a COG at address 0 (generate) and begins program execution there
1) move the contents of Set_dira to the outa register (this sets bits 12 and 24 to 1, BUT the DIRA register is still INPUT
2) jump back to 1
You've never set DIRA to output, you'll never see bits 12 and 24 anywhere.
Your second COGNEW starts execution in a new cog at address toggle
1) you set DIRA bits 12 and 24 to output
2) you set OUTA bit 12 HIGH
3) you set OUTA bit 12 HIGH (even though the label says Pin_Lo, you still have a 1 bit at bit 12)
4) you jump back to #1 and do the same things
Even if you did set bit 12 LO in #3, it woudl be toggling so fast, you would need an oscilscope or logic probe to see it.
Here's code from Potatohead's tutorial that does what you want - two cogs blinking two leds. I was running this on a Quickstart, so it blinks pin 16 and 17
CON_clkmode = xtal1 + pll16x_xinfreq = 5_000_000PUBMain' a freshly started COG has OUTA = 0 and DIRA = 0 - this is where understanding the HW is importantcognew(@Toggle, 0) ' start a COG at address Togglecognew(@Toggle1, 0) ' start a COG at address Toggle1DAT{Toggle Pin 16}ORG0' Begin at COG RAM address 0
Toggle
movdira, Pin16 ' move CONTENTS of COG Memory Pin16 to DIRA register' This sets PIN 16 to OUTPUTmov Time, cnt' move CONTENTS of CNT register to COG Memory address Timeadd Time, #$f' bump the counter a bit (trust me on this one for now)
:loop
waitcnt Time, Delay ' Tell this COG to wait until the CNT register equals the contents of Time' when CNT = Time, start the COG and add contents of Delay to Time for the next loop xorouta, Pin16 ' toggle bit 16 of OUTA (this is where understanding BINARY operations ' comes into play)jmp #:loop ' jump to :loop - need the # in this case since you want the IMMEDIATE address'
Pin16 long |< 16' %00000000_00000001_0000000_00000000 if you prefer
Delay long5_000_000' delay for 5_000_000 clock ticks
Time res1' a place to save the WAITCNT value{Toggle Pin 17}' similar code for the second PIN (PIN#17)ORG0
Toggle1 movdira, Pin17
mov Time1, cntadd Time1, #$F
:loop waitcnt Time1, Delay1
xorouta, Pin17
jmp #:loop
Pin17 long |< 17
Delay1 long80_000_000
Time1 res1
When I was learning assembler way back when, I was always told to "play computer" it might sound dumb but it really helps to go through each instruction and do everything the computer has to do. If you understand how the computer works for each instruction, you can "play computer" through ANY program.
P.S. This is not how I usually comment code but for instructional purposes, step by step comments are often helpful. I normally despise code such as:
For now, just FYI
If I eliminate the initial "jmp generate" instruction,
the I/O works fine and I can see it on the scope just fine so that part seems OK
Pin 24 should not make any difference to the cog running.
It was a mistake but its not critical.
I removed it, nothing changed
That's a tough call
I will add LEDs to this code for now but eventually we will need a scope.
Its really a very basic piece of equipment every serious beginner need to learn how to use.
Just too much stuff that goes too fast to not own one. Its our eyes in electronics.
Note to self: After building things since 1975, I need to buy a scope to be a serious beginner.
@Harpirt: I removed the jmp #generate and I got no output on either pin when I watched it through the logic probe on Gear. If you watch COG1 and COG2 run, you see them looping through all of COG memory and they just happen to execute your instructions as they loop.
If I remove the jmp #generate AND the 'ORG 30' I get a steady HIGH on PIN 12 and a pulsed pin 24. COG1 and COG2 are at least inthe tight loop inside your code.
Both COGs are running through the same code path pulsing the same pin at the same rate. I can't imagine this is what you intended by your example. I'm not sure what concept this shows your reader.
The example I attached from Potatohead's tutorial shows two COGs pulsing two LEDs at two different rates - this at least shows independent COG funtions.
I'm not trying to be difficult, just providing feedback from 'a PASM beginner'.
Back to basic guys!
I know this is not quite right but...
Why does this not open up a third cog and toggle line 12
It compiles but I get a dead line 12
In other word, how do you start two PASM cogs
PUBnull | P_Val, Delay_Valcognew(@generate, @P_Val) ' start second cog at "generate" cognew(@Toggle, @Delay_Val) 'start third cog at togglerepeat'do something unrelated to other cogsDATOrg0
generate movouta, set_dira
jmp #generate
org30
toggle movdira, set_dira
movouta, pin_hi
movouta, pin_lo
jmp #toggle
'Constants. This section is similar to the CON block in SPIN
Set_dira long%00000001_00000000_00010000_00000000'Set dira register
Pin_Hi long%00000000_00000000_00010000_00000000
Pin_Lo long%00000000_00000000_00010000_00000000
Some observations:
As mentioned in #127: Pin_Hi and Pin_Lo both have bit 12 set and thus no toggling.
Referring to post #128: removing "jmp #generate" just makes both cogs run the same loop.
"toggle" is at "generate" cog's address 2 (or address 1 if you get rid of "jmp #generate").
Remember that saying org 30 does not change the cog start address.
What was strange (and unexpected to PASM newbie, me) The code generated sequentially, 0 thru 4 or 5 but the ORG 30 caused the label 'toggle' to be at $1E. This caused the jmp to go to $1E and then execute code from there to the end of COG memory. This is a least how GEAR simulated it.
I was expecting the ORG 30 to cause a hole in the COG memory.....I expected incorrectly. I have much to learn!
@Steve, it's an honor to be quoted!
I guess MOV OUTA, PEACE only shows if you're sure of your direction!
2) I don't know Kuroneko but respect his knowledge and talent and genius. I assume he didn't just walk in one day from being a frustrated musician and decide to download the Propeller datasheet and start writing complex PASM code.
Apologies when this came accross as you only need the datasheet. At the time I already had lots of experience with different chips so the datasheet was sufficient. Also, I started simple, working my way up ... and I can't even play an instrument ...
Well, I finally got two Cogs working at the same time in PASM. Took all day.
My problem was that I did not appreciate that each cog has to be defined completely with
all its subroutines, variables and constants before you enter ORG 0 and start
defining the second Cog and all its goodies.
And you cannot duplicate variable names even across cogs (Which I thought was rather strange)
And you cannot duplicate variable names even across cogs (Which I thought was rather strange).
Global (variable) labels that is. After all, it's still the same DAT block. You could work around that issue by using local labels (which are sufficently separated by the main label), e.g.
I will post complete listing after I clean it up tomorrow. But essentially......
The problem I am having is that I read a potentiometer into a global variable in one Cog
I add the global variable to another variable in a second Cog and it acts like it is 0 all the time. (I know that for sure)
(It actually varies 12 bits from 0 to 4095. And I know that for sure)
What up?
I though it might be something really silly on my part that you have seen 1000 times.
No, not using HUB instruction
Lets wait till I can post the code.
Using PAR for transfer to the SPIN cog but both my other cogs are using PASM
Par to SPIN works fine, thats how I know variable is being read right.
One PASM cog determines variable (and xfers to SPIN via PAR) but the other one will not read it as same global name.
There is the shared HUB-RAM and to each cog exclusive COG-RAM
If you want to transfer a value from one cog to another the only way is
writing the value from COG1-RAM WRLONG ==> HUB-RAM
then reading the HUB-RAM with COG2 RDLONG COG2-RAM <=== HUB_RAM
I think it is the same with the HUB-RAM-adress itself too.This means declare a SPIN-variable and start the two cogs
from SPIN. If you want to start the second cog from PASM do I remember right that DIRB and OUTB are existing as registers that are just not connected to any hardware?
But can be used as some kind of global scratchpad-RAM that is easy adressable?
If you want to transfer a value from one cog to another the only way is writing the value from COG1-RAM WRLONG ==> HUB-RAM then reading the HUB-RAM with COG2 RDLONG COG2-RAM <=== HUB_RAM
Have a look at [thread=129585]this thread[/thread] when you have a minute
I agree there is more than one way to communicate between cogs.
Would you mind answering the question about DIRB/OUTB as scratchpad-RAM
or do you insist on test it yourself :-)
Would you mind answering the question about DIRB/OUTB as scratchpad-RAM or do you insist on test it yourself :-)
I wasn't quite sure about your use of global (unless you meant that in the context of a single cog). Both registers are just that, registers. No side effects, no outside connection(s). So feel free to use them. If you don't actively use counters then those registers are free as well. Which is true for nearly every SPR. Personally I stay away from dira for storage/code and vscl for code.
I think Cluso's debugger uses the first four SPRs and somewhere there is a cog storage object which uses the whole SPR area.
I am ready to post my program but the read/write routine is not working to my satisfaction (or at all)
So the program is not runnable.
The problem is that I can not read and write across two PASM cogs accurately.
I am using WRLONG and RDLONG to main memory per the PASM manual (and Stephan in #143)
I have a question (here is what I am doing)
If I have a variable "data_read"
in a PASM cog and then write
WRLONG data_read, temp_var
data_read res 1 'later on in the program to set up the variable
and then read Temp_var into a second PASM cog with
RDLONG time, temp_var
should it work?..It should the way I read it.
It does not for some reason, in my program. I get 0.
It would be so much easier to help if you just post your code. No guessing.
Is temp_var an address pointing to a hub memory location?
RDLONG D, S ' D is the variable you want to read to and S is where you want to read from.
WRLONG D, S ' D is the variable you want to write and S is where you want to write to.
I am ready to post my program but the read/write routine is not working to my satisfaction (or at all)
So the program is not runnable.
The problem is that I can not read and write across two PASM cogs accurately.
I am using WRLONG and RDLONG to main memory per the PASM manual (and Stephan in #143)
I have a question (here is what I am doing)
If I have a variable "data_read"
in a PASM cog and then write
WRLONG data_read, temp_var
data_read res 1 'later on in the program to set up the variable
and then read Temp_var into a second PASM cog with
RDLONG time, temp_var
should it work?..It should the way I read it.
It does not for some reason, in my program. I get 0.
I am hesitant to do this because it is not cleaned up
I will clean it up later. This is per JAZZED request
CON_clkmode = xtal1 + pll16x_xinfreq = 5_000_000PUBnull | P_Valcognew(@generate, @P_Val) ' start new cog at "generate" and read variable at P_Valcognew(@Toggle, 0) ' start new cog at Toggle '==========================================================================================================================='=======================================COG 1 works fine===================================================================='===========================================================================================================================dira[0 ..11]~~ ' all lines are outputs except 26repeat' endless loop to display dataouta[0..11] := P_Val ' displays 1.5 bytes of data DAT'==========================================================================================================================='=======================================COG 2 works fine===================================================================='===========================================================================================================================org0'sets the starting point in Cog
generate movdira, set_dira 'sets direction of the prop pinscall #chip_sel_lo 'selects chip by pulling line lowcall #Clk_lo 'START. Clock needs to be low to load datacall #Din_hi 'must start with Din high to set up 3208call #Tog_clk 'clk hi to read datacall #Din_Hi 'SINGLE DIFF Low to loadcall #Tog_Clk 'toggle clock line hi then low to read in the datacall #Din_Lo 'D2 Low to load input line selection sequence 000 for line 0call #Tog_Clk 'toggle clock line hi then low to read in the datacall #Din_Lo 'D1 Low to load input line selection sequence 000 for line 0call #Tog_Clk 'toggle clock line hi then low to read in the datacall #Din_Lo 'D0 Low to load input line selection sequence 000 for line 0call #Tog_Clk 'toggle clock line hi then low to read in the datacall #Din_Lo 'blank bit needs a clock cycle, nextcall #Tog_Clk 'toggle clock line hi then low to read in the data'next toggle is for the null bit, nothing readcall #Tog_Clk 'toggle clock line hi then low to read in the datamov dat_red, #0'Clear register we will read data into mov count, #12'Counter for number of bits we will read
read_bit mov temp, ina'read in what is in all the input linesandn temp, inputmask wz'mask off everything except Dout line. Set Z flagif_nzadd Dat_red, #1'if value is still positive add 1 to data register ror Dat_red, #1'roll register right 1 bit to get ready for next bitcall #Tog_Clk 'toggle clock to get next bit ready in Doutsub count, #1wz'decrement the "bits read" counter. Set Z flagif_nzjmp #read_bit 'go up and do it again if counter not 0rol dat_red, #12'roll back 12 bits to get data into 12 LSBits of register wrlong dat_red, par'put it in PAR to share it as P.Valcall #Chip_Sel_Hi 'Put chip to sleep by delselecting it, for low power usagerdlong dat_red, temp2 'get data that as read **********************************************************jmp #generate 'go back to do it all again 'Subroutines
Clk_Hi mov temp, outa'Get the OUTA registeror temp, clk_bit 'OR it with the Clock Bit to male highmovouta, temp 'put it back in OUTA register
Clk_Hi_ret ret
Clk_Lo mov temp, outa'Get the OUTA register andn temp, clk_bit 'ANDN it with the Clock Bi to make lowtmovouta, temp 'put it back in OUTA register
Clk_Lo_ret ret'return from this subroutine
Tog_Clk call #Clk_hi 'make clock bit highcall #clk_lo 'make clock bit low
Tog_Clk_ret ret'return from this subroutine
Din_Hi mov temp, outa'Get the OUTA registeror temp, din_Bit 'Makes the Din highmovouta, temp 'put it back in OUTA register
Din_Hi_ret ret'return from this subroutine
Din_Lo mov temp, outa'Get the OUTA registerandn temp, din_Bit 'makes Din lowmovouta, temp 'put it back in OUTA register
Din_Lo_ret ret'return from this subroutine
Chip_Sel_Hi mov temp, outa'Get the OUTA registeror temp, chs_Bit 'Makes Chip select highmovouta, temp 'put it back in OUTA register
Chip_Sel_Hi_ret ret'return from this subroutine
Chip_Sel_Lo mov temp, outa'Get the OUTA registerandn temp, chs_Bit 'makes chip select lowmovouta, temp 'put it back in OUTA register
Chip_Sel_Lo_ret ret'return from this subroutine
Read_Dout mov temp, ina'Get the INA register
Read_Dout_ret ret'return from this subroutine
Read_Next_Bit mov temp, ina'Get the INA registeror temp, inputmask 'mask all but Din bit
Read_Next_Bit_ret ret'return from this subroutine'Constants. This section is similar to the CON block in SPIN
Set_dira long%00001011_00000000_00000000_00000000'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
inputmask long%11111011_11111111_11111111_11111111'Mask for reading the Dout bit only'Variables. This section is similar to the VAR block in SPIN
temp res1'temporary storage variable
temp2 res1'temporary storage variable
count res1'temporary storage variable
Dat_Red res1'temporary storage variable'==========================================================================================================================='=======================================COG 3 can't move temp2 into variable effectively===================================='===========================================================================================================================org0'begin a 0
toggle movdira, pin_12 'set pin 12 as output
toggle1 mov temp1, outa'read in outa or temp1, pin_12 'make pin 12 lomovouta, temp1 'put it back in outa call #clkdelay 'call the delay subroutinemov temp1, outa'read in outaandn temp1, pin_12 'make pin 12 highmovouta, temp1 'put it back in outacall #clkdelay 'call the delay subroutinejmp #toggle1 'repeat
clkdelay rdlong time, temp2 'the delay subroutine,load TEMP2 into time ********ACTS LINE TEMP2=0************************add time, #$1f'delay added to skip past underflow of CNT
dloop sub time, #1wz'sub 1 from time and set flag if 0if_nzjmp #dloop 'if flag not 0 go back to take4
clkdelay_ret ret'return for delay subroutine'Constants. This section is similar to the CON block in SPIN
Pin_12 long%00000000_00000000_00010000_00000000
del_time long400'Variables. This section is similar to the VAR block in SPIN
deltime2 res1'temporary storage variable
temp1 res1'temporary storage variable
time res1'temporary storage variable
You have to create variables for each cog that contains the hub address for rdlong/wrlong.
Using temp2 will not work because it contains some other data (it can be forced to work with advanced understanding of SPIN/PASM).
Here's a simple sharedmem.spin example (as simple as i know how to make it).
'''' SIMPLE share data demo''con' clock setup_clkmode = xtal1 + pll16x_clkfreq = 80_000_000var' hub shared memory section. variables must be kept in this orderlong share ' share is set to parlong data ' data is at share address + 4'===============================' test harnessobj
sx : "FullDuplexSerial"pubmain'' program main entry point
sx.start(31,30,0,115200) ' start console at 115200 for debug output
start ' start the producer and consumer cogsrepeat' print shared data every second
sx.hex(share, 8)
sx.tx(" ")
sx.hex(data, 8)
sx.tx($d)
waitcnt(_clkfreq+cnt)
'===============================pubstart'' start producer and consumer cogscognew(@producer, @share)
cognew(@consumer, @share)
waitcnt(_clkfreq+cnt)
datorg'---------------------------------' producer is used to write incrementing data to two shared hub variables'
producer
' par will have the hub address of share' padr will have the hub address of data' let's put the address of data in padrmov padr, par' save par to padradd padr, #4' reference the second long in the shared memory' will increment pshare and pdata in the loop for writing' set pshare and pdata to known valuesmov pshare, #0' could have used "pshare long 0" instead below mov pdata, #0' could have used "pdata long 0" instead below'---------------------------------' main program loop'
:loop
add pshare, #1' increment variablewrlong pshare, par' write pshare to hub share variableadd pdata, #1' increment variablewrlong pdata, padr ' write pdata to hub data variablejmp #:loop
'---------------------------------' variables'
pshare res1
pdata res1
padr res1datorg'---------------------------------' consumer is used to read producer data from two shared hub variables'
consumer
' par will have the hub address of share' cadr will have the hub address of data' let's put the address of data in cadrmov cadr, par' save par to padradd cadr, #4' reference the second long in the shared memory'---------------------------------' main program loop simply reads from shared memory'
:loop
rdlong cshare, par' read pshare from hub share variablerdlong cdata, cadr ' read pdata from hub data variablejmp #:loop
'---------------------------------' variables'
cshare res1
cdata res1
cadr res1
if I remember right all predefined longs (constants) like
'Constants. This section is similar to the CON block in SPIN Set_dira long %00001011_00000000_00000000_00000000'Set dira register Chs_Bit long %00000001_00000000_00000000_00000000'Chip select bit 24 Din_Bit long %00000010_00000000_00000000_00000000'Datain bit 25 Dout_Bit long %00000100_00000000_00000000_00000000'Data out bit 26 Clk_Bit long %00001000_00000000_00000000_00000000'Clock bit 27 inputmask long %11111011_11111111_11111111_11111111'Maskfor reading the Dout bit only
and all reserved longs (not predefined) acting as variables
'Variables. This section is similar to the VAR block in SPIN temp res 1'temporary storage variable temp2 res 1'temporary storage variable count res 1'temporary storage variable Dat_Red res 1'temporary storage variable
should be placed behind the last PASM-command.
temp2 that should contain the HUB-RAM-adress is never initialised with a valid HUB-RAM-adress.
You start a second cog for toggling the IO-pin 12
The code uses temp2 as the HUB-RAM-adress. But here it is the same temp2 is not initialised with a valid HUB-RAM-adress
As you start the second cog from SPIN too you simply use the second parameter there too.
instead of
cognew(@Toggle, 0) ' start new cog at Toggle
use
cognew(@Toggle, @P_Val) ' start new cog at Toggle
With this cognew the second cog has the HUB-RAM-adress of variable P_Val in its PAR-register
and can be accessed with
clkdelay rdlong time, par 'the delay subroutine,load TEMP2 into time ********ACTS LINE TEMP2=0************************
another comment on your variable names
using temp1, temp2 is bad programming style. The names temp1, temp2 don't say anything about their purpose.
If you think about the code your brain has to do some extra work translating temp1 contains bitpattern of outa
temp2 contains HUB-RAM-adress of P_Val. This extra brain-work makes it harder to understand for beginners.
Variable-names should be selfexplaining.
P_Val says almost nothing. "MCP3802_ADC_Val1" is self-explaining
As you write a book for PASM-beginners I even would define a variable for par.
So the codeline looks like this
'the delay subroutine'the value of the SPIN-variable P_Val accessed through its HUB-RAM-adress "HUB_RAM_Adr_of_ADC_Val1"'is used as start-value for the variable named "time" which is counted down to zero'in a loop to generate a delay
clkdelay rdlong time, HUB_RAM_Adr_of_ADC_Val1
I also added a comment of 4 lines to explain its purpose in detail.
That's what I would call easy to understand
"HUB_RAM_Adr_of_ADC_Val1" is a quite long name I guess for 50-90% of all programmers too long but it is self-explaining
maybe something like "HR_Ptr_ADC1" is still saying enough if you use the leading characters "HR_Ptr" as a short name for H)ub-R)am-Pointer all the the time
whenever you use RDLONG or WRLONG.
If somebody says: "too many characters to type". I'm using Ctrl-Cursor Ctrl-Shift-curcor Ctrl-C, Ctrl-v all the time and this even avoids typos.
When writing the variable names with ctrl-c/v it doesn't matter how long they are. always the same amount of keypresses.
I guess if you use self-explaining variable-names it will be easier for yourself to find bugs.
And you are giving a good model for beginners how to write code in a good style.
keep the questions coming and continue to post your code
if I remember right all predefined longs (constants) like
'Constants. This section is similar to the CON block in SPIN
Set_dira long %00001011_00000000_00000000_00000000'Set dira register ...
and all reserved longs (not predefined) acting as variables
'Variables. This section is similar to the VAR block in SPIN temp res 1'temporary storage variable temp2 res 1'temporary storage variable count res 1'temporary storage variable Dat_Red res 1'temporary storage variable
should be placed behind the last PASM-command.
Longs can be put anywhere. Any long that does not have the upper 16 bits (more or less) set will be treated as a NOP instruction. Often when pressed for space, variables are put in an initialization section where the init code can be over-written. The "RES" variables absolutely must be put at the end of a DAT section for a COG because RES does not allocate space for the variables.
Just my personal preference and you can ignore this if you like, but I like shorter variable names and comment decorations. Variable names should communicate their purpose without expanding into sentences like VariableNamesShouldCommunicateTheirPurpose. Huge comment decorations are wasteful and generally tell the reader things about the programmer that may not be interpreted as intended.
Please consider trimming any code or comment line which has more than 70 characters.
Comments
--Steve
I know this is not quite right but...
Why does this not open up a third cog and toggle line 12
It compiles but I get a dead line 12
In other word, how do you start two PASM cogs
PUB null | P_Val, Delay_Val cognew(@generate, @P_Val) ' start second cog at "generate" cognew(@Toggle, @Delay_Val) 'start third cog at toggle repeat 'do something unrelated to other cogs DAT Org 0 generate mov outa, set_dira jmp #generate org 30 toggle mov dira, set_dira mov outa, pin_hi mov outa, pin_lo jmp #toggle 'Constants. This section is similar to the CON block in SPIN Set_dira long %00000001_00000000_00010000_00000000 'Set dira register Pin_Hi long %00000000_00000000_00010000_00000000 Pin_Lo long %00000000_00000000_00010000_00000000
binary math
PASM
assembly language
jargon infested writings
to want to mess around with PASM and his propeller chip
Maybe inspire him or her to learn more from you guys.
I will keep in very simple and disappoint some of you who think there is no way I can get this done
Here is letter from some one who know very little but is very excited. I got it this morning.
All he is really turned on about is controlling a few LEDs.
Hopefully some day he will make a great Tech or even an Engineer. Made my day.
Dear Sir:
I am enjoying your book immensely! I especially enjoyed seeing your section on page 23/24, A Fundamental Reality We Have to Consider, as I need to have LEDs on all my I/O pins (not multiplexed, and more than 10 ma. per pin -- very bright). I plan on using a LYP47F reverse mount LED from OSRAM (sold by Mouser). (The reverse mount and small size are why I chose that style.)
I was a bit surprised to see the 7404 driving LEDs, so I set up six on a chip with 470 ohms as you suggested. Ran all afternoon until I had to shut down and go home! I measured the total chip current (ICCL = 49.2 ma). This exceeded the data sheet spec (33 ma max) a bit but did not blow any outputs. As a personal hobby application or learning situation I can see the 7404 working fine.
However, I have been looking for a long time for a chip for product development where reliability and safety margins can be considered in the design. I am wondering if you might know of anything more robust (and as simple as the 7404) that you could suggest where I would have more driving current per LED. You are the only author that has addressed the situation I face. (Another issue was how to interface a 5 volt family to the Propeller. So I thank you for moving me forward on my project!)
I am looking forward to the remainder of your book. Very well done.
(I like that fact that you do not repeat all the material in other reference sources, but direct the reader to the sources. That makes your book a faster/easier read and more pages for what it is you want to guide us through.)
Thanking you in advance,
First, the startup state on a reset or freshly started COG for the direction register (DIRA) is 0, that is, all pins set as INPUT
Your first COGNEW starts a COG at address 0 (generate) and begins program execution there
1) move the contents of Set_dira to the outa register (this sets bits 12 and 24 to 1, BUT the DIRA register is still INPUT
2) jump back to 1
You've never set DIRA to output, you'll never see bits 12 and 24 anywhere.
Your second COGNEW starts execution in a new cog at address toggle
1) you set DIRA bits 12 and 24 to output
2) you set OUTA bit 12 HIGH
3) you set OUTA bit 12 HIGH (even though the label says Pin_Lo, you still have a 1 bit at bit 12)
4) you jump back to #1 and do the same things
Even if you did set bit 12 LO in #3, it woudl be toggling so fast, you would need an oscilscope or logic probe to see it.
Here's code from Potatohead's tutorial that does what you want - two cogs blinking two leds. I was running this on a Quickstart, so it blinks pin 16 and 17
CON _clkmode = xtal1 + pll16x _xinfreq = 5_000_000 PUB Main ' a freshly started COG has OUTA = 0 and DIRA = 0 - this is where understanding the HW is important cognew(@Toggle, 0) ' start a COG at address Toggle cognew(@Toggle1, 0) ' start a COG at address Toggle1 DAT {Toggle Pin 16} ORG 0 ' Begin at COG RAM address 0 Toggle mov dira, Pin16 ' move CONTENTS of COG Memory Pin16 to DIRA register ' This sets PIN 16 to OUTPUT mov Time, cnt ' move CONTENTS of CNT register to COG Memory address Time add Time, #$f ' bump the counter a bit (trust me on this one for now) :loop waitcnt Time, Delay ' Tell this COG to wait until the CNT register equals the contents of Time ' when CNT = Time, start the COG and add contents of Delay to Time for the next loop xor outa, Pin16 ' toggle bit 16 of OUTA (this is where understanding BINARY operations ' comes into play) jmp #:loop ' jump to :loop - need the # in this case since you want the IMMEDIATE address ' Pin16 long |< 16 ' %00000000_00000001_0000000_00000000 if you prefer Delay long 5_000_000 ' delay for 5_000_000 clock ticks Time res 1 ' a place to save the WAITCNT value {Toggle Pin 17} ' similar code for the second PIN (PIN#17) ORG 0 Toggle1 mov dira, Pin17 mov Time1, cnt add Time1, #$F :loop waitcnt Time1, Delay1 xor outa, Pin17 jmp #:loop Pin17 long |< 17 Delay1 long 80_000_000 Time1 res 1
When I was learning assembler way back when, I was always told to "play computer" it might sound dumb but it really helps to go through each instruction and do everything the computer has to do. If you understand how the computer works for each instruction, you can "play computer" through ANY program.
P.S. This is not how I usually comment code but for instructional purposes, step by step comments are often helpful. I normally despise code such as:
$daysInWeek = 7; # set daysInWeek to 7
oh, wait, I don't normally comment code.........
I'll take a closer look at the code you sent
For now, just FYI
If I eliminate the initial "jmp generate" instruction,
the I/O works fine and I can see it on the scope just fine so that part seems OK
Pin 24 should not make any difference to the cog running.
It was a mistake but its not critical.
I removed it, nothing changed
Finally, Thanks I think I can make it work now.
Regards
HSS
That's a tough call
I will add LEDs to this code for now but eventually we will need a scope.
Its really a very basic piece of equipment every serious beginner need to learn how to use.
Just too much stuff that goes too fast to not own one. Its our eyes in electronics.
HSS
@Harpirt: I removed the jmp #generate and I got no output on either pin when I watched it through the logic probe on Gear. If you watch COG1 and COG2 run, you see them looping through all of COG memory and they just happen to execute your instructions as they loop.
If I remove the jmp #generate AND the 'ORG 30' I get a steady HIGH on PIN 12 and a pulsed pin 24. COG1 and COG2 are at least inthe tight loop inside your code.
Both COGs are running through the same code path pulsing the same pin at the same rate. I can't imagine this is what you intended by your example. I'm not sure what concept this shows your reader.
The example I attached from Potatohead's tutorial shows two COGs pulsing two LEDs at two different rates - this at least shows independent COG funtions.
I'm not trying to be difficult, just providing feedback from 'a PASM beginner'.
Some observations:
As mentioned in #127: Pin_Hi and Pin_Lo both have bit 12 set and thus no toggling.
Referring to post #128: removing "jmp #generate" just makes both cogs run the same loop.
"toggle" is at "generate" cog's address 2 (or address 1 if you get rid of "jmp #generate").
Remember that saying org 30 does not change the cog start address.
"MOV OUTA, PEACE" - @mindrobots
--Steve
I was expecting the ORG 30 to cause a hole in the COG memory.....I expected incorrectly. I have much to learn!
@Steve, it's an honor to be quoted!
I guess MOV OUTA, PEACE only shows if you're sure of your direction!
Indeed. I quoted you precisely since PEACE is variable but normally understood.
#PEACE would mean "immediately" within a smaller range
Yes, "jmp #toggle" is adjusted by org 30. The rules of org have been mentioned in this thread.
My problem was that I did not appreciate that each cog has to be defined completely with
all its subroutines, variables and constants before you enter ORG 0 and start
defining the second Cog and all its goodies.
And you cannot duplicate variable names even across cogs (Which I thought was rather strange)
And you still have to watch for I/O conflicts..
I will post my program soon.
Thanks for all the help guys.
Harprit
DAT org 0 one mov :b, :a ' ... :a long 0 :b res 1 DAT org 0 [COLOR="orange"]two[/COLOR] neg :b, :a ' ... :a long 0 :b res 1
And be careful with multi cog images in the same file. You can easily end up with one image referencing a symbol in another.The problem I am having is that I read a potentiometer into a global variable in one Cog
I add the global variable to another variable in a second Cog and it acts like it is 0 all the time. (I know that for sure)
(It actually varies 12 bits from 0 to 4095. And I know that for sure)
What up?
Harprit
(This is why PAR is a great idea, BTW)
No, not using HUB instruction
Lets wait till I can post the code.
Using PAR for transfer to the SPIN cog but both my other cogs are using PASM
Par to SPIN works fine, thats how I know variable is being read right.
One PASM cog determines variable (and xfers to SPIN via PAR) but the other one will not read it as same global name.
Thanks
HSS
If you want to transfer a value from one cog to another the only way is
writing the value from COG1-RAM WRLONG ==> HUB-RAM
then reading the HUB-RAM with COG2 RDLONG COG2-RAM <=== HUB_RAM
I think it is the same with the HUB-RAM-adress itself too.This means declare a SPIN-variable and start the two cogs
from SPIN. If you want to start the second cog from PASM do I remember right that DIRB and OUTB are existing as registers that are just not connected to any hardware?
But can be used as some kind of global scratchpad-RAM that is easy adressable?
keep the questions coming
best regards
Stefan
I agree there is more than one way to communicate between cogs.
Would you mind answering the question about DIRB/OUTB as scratchpad-RAM
or do you insist on test it yourself :-)
keep the improving comments coming
best regards
Stefan
I think Cluso's debugger uses the first four SPRs and somewhere there is a cog storage object which uses the whole SPR area.
So the program is not runnable.
The problem is that I can not read and write across two PASM cogs accurately.
I am using WRLONG and RDLONG to main memory per the PASM manual (and Stephan in #143)
I have a question (here is what I am doing)
If I have a variable "data_read"
in a PASM cog and then write
WRLONG data_read, temp_var
data_read res 1 'later on in the program to set up the variable
and then read Temp_var into a second PASM cog with
RDLONG time, temp_var
should it work?..It should the way I read it.
It does not for some reason, in my program. I get 0.
Harprit
Is temp_var an address pointing to a hub memory location?
RDLONG D, S ' D is the variable you want to read to and S is where you want to read from.
WRLONG D, S ' D is the variable you want to write and S is where you want to write to.
I will clean it up later. This is per JAZZED request
CON _clkmode = xtal1 + pll16x _xinfreq = 5_000_000 PUB null | P_Val cognew(@generate, @P_Val) ' start new cog at "generate" and read variable at P_Val cognew(@Toggle, 0) ' start new cog at Toggle '=========================================================================================================================== '=======================================COG 1 works fine==================================================================== '=========================================================================================================================== dira[0 ..11]~~ ' all lines are outputs except 26 repeat ' endless loop to display data outa[0..11] := P_Val ' displays 1.5 bytes of data DAT '=========================================================================================================================== '=======================================COG 2 works fine==================================================================== '=========================================================================================================================== org 0 'sets the starting point in Cog generate mov dira, set_dira 'sets direction of the prop pins call #chip_sel_lo 'selects chip by pulling line low call #Clk_lo 'START. Clock needs to be low to load data call #Din_hi 'must start with Din high to set up 3208 call #Tog_clk 'clk hi to read data call #Din_Hi 'SINGLE DIFF Low to load call #Tog_Clk 'toggle clock line hi then low to read in the data call #Din_Lo 'D2 Low to load input line selection sequence 000 for line 0 call #Tog_Clk 'toggle clock line hi then low to read in the data call #Din_Lo 'D1 Low to load input line selection sequence 000 for line 0 call #Tog_Clk 'toggle clock line hi then low to read in the data call #Din_Lo 'D0 Low to load input line selection sequence 000 for line 0 call #Tog_Clk 'toggle clock line hi then low to read in the data call #Din_Lo 'blank bit needs a clock cycle, next call #Tog_Clk 'toggle clock line hi then low to read in the data 'next toggle is for the null bit, nothing read call #Tog_Clk 'toggle clock line hi then low to read in the data mov dat_red, #0 'Clear register we will read data into mov count, #12 'Counter for number of bits we will read read_bit mov temp, ina 'read in what is in all the input lines andn temp, inputmask wz 'mask off everything except Dout line. Set Z flag if_nz add Dat_red, #1 'if value is still positive add 1 to data register ror Dat_red, #1 'roll register right 1 bit to get ready for next bit call #Tog_Clk 'toggle clock to get next bit ready in Dout sub count, #1 wz 'decrement the "bits read" counter. Set Z flag if_nz jmp #read_bit 'go up and do it again if counter not 0 rol dat_red, #12 'roll back 12 bits to get data into 12 LSBits of register wrlong dat_red, par 'put it in PAR to share it as P.Val call #Chip_Sel_Hi 'Put chip to sleep by delselecting it, for low power usage rdlong dat_red, temp2 'get data that as read ********************************************************** jmp #generate 'go back to do it all again 'Subroutines Clk_Hi mov temp, outa 'Get the OUTA register or temp, clk_bit 'OR it with the Clock Bit to male high mov outa, temp 'put it back in OUTA register Clk_Hi_ret ret Clk_Lo mov temp, outa 'Get the OUTA register andn temp, clk_bit 'ANDN it with the Clock Bi to make lowt mov outa, temp 'put it back in OUTA register Clk_Lo_ret ret 'return from this subroutine Tog_Clk call #Clk_hi 'make clock bit high call #clk_lo 'make clock bit low Tog_Clk_ret ret 'return from this subroutine Din_Hi mov temp, outa 'Get the OUTA register or temp, din_Bit 'Makes the Din high mov outa, temp 'put it back in OUTA register Din_Hi_ret ret 'return from this subroutine Din_Lo mov temp, outa 'Get the OUTA register andn temp, din_Bit 'makes Din low mov outa, temp 'put it back in OUTA register Din_Lo_ret ret 'return from this subroutine Chip_Sel_Hi mov temp, outa 'Get the OUTA register or temp, chs_Bit 'Makes Chip select high mov outa, temp 'put it back in OUTA register Chip_Sel_Hi_ret ret 'return from this subroutine Chip_Sel_Lo mov temp, outa 'Get the OUTA register andn temp, chs_Bit 'makes chip select low mov outa, temp 'put it back in OUTA register Chip_Sel_Lo_ret ret 'return from this subroutine Read_Dout mov temp, ina 'Get the INA register Read_Dout_ret ret 'return from this subroutine Read_Next_Bit mov temp, ina 'Get the INA register or temp, inputmask 'mask all but Din bit Read_Next_Bit_ret ret 'return from this subroutine 'Constants. This section is similar to the CON block in SPIN Set_dira long %00001011_00000000_00000000_00000000 '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 inputmask long %11111011_11111111_11111111_11111111 'Mask for reading the Dout bit only 'Variables. This section is similar to the VAR block in SPIN temp res 1 'temporary storage variable temp2 res 1 'temporary storage variable count res 1 'temporary storage variable Dat_Red res 1 'temporary storage variable '=========================================================================================================================== '=======================================COG 3 can't move temp2 into variable effectively==================================== '=========================================================================================================================== org 0 'begin a 0 toggle mov dira, pin_12 'set pin 12 as output toggle1 mov temp1, outa 'read in outa or temp1, pin_12 'make pin 12 lo mov outa, temp1 'put it back in outa call #clkdelay 'call the delay subroutine mov temp1, outa 'read in outa andn temp1, pin_12 'make pin 12 high mov outa, temp1 'put it back in outa call #clkdelay 'call the delay subroutine jmp #toggle1 'repeat clkdelay rdlong time, temp2 'the delay subroutine,load TEMP2 into time ********ACTS LINE TEMP2=0************************ add time, #$1f 'delay added to skip past underflow of CNT dloop sub time, #1 wz 'sub 1 from time and set flag if 0 if_nz jmp #dloop 'if flag not 0 go back to take4 clkdelay_ret ret 'return for delay subroutine 'Constants. This section is similar to the CON block in SPIN Pin_12 long %00000000_00000000_00010000_00000000 del_time long 400 'Variables. This section is similar to the VAR block in SPIN deltime2 res 1 'temporary storage variable temp1 res 1 'temporary storage variable time res 1 'temporary storage variable
Harprit
Using temp2 will not work because it contains some other data (it can be forced to work with advanced understanding of SPIN/PASM).
Here's a simple sharedmem.spin example (as simple as i know how to make it).
'' '' SIMPLE share data demo '' con ' clock setup _clkmode = xtal1 + pll16x _clkfreq = 80_000_000 var ' hub shared memory section. variables must be kept in this order long share ' share is set to par long data ' data is at share address + 4 '=============================== ' test harness obj sx : "FullDuplexSerial" pub main '' program main entry point sx.start(31,30,0,115200) ' start console at 115200 for debug output start ' start the producer and consumer cogs repeat ' print shared data every second sx.hex(share, 8) sx.tx(" ") sx.hex(data, 8) sx.tx($d) waitcnt(_clkfreq+cnt) '=============================== pub start '' start producer and consumer cogs cognew(@producer, @share) cognew(@consumer, @share) waitcnt(_clkfreq+cnt) dat org '--------------------------------- ' producer is used to write incrementing data to two shared hub variables ' producer ' par will have the hub address of share ' padr will have the hub address of data ' let's put the address of data in padr mov padr, par ' save par to padr add padr, #4 ' reference the second long in the shared memory ' will increment pshare and pdata in the loop for writing ' set pshare and pdata to known values mov pshare, #0 ' could have used "pshare long 0" instead below mov pdata, #0 ' could have used "pdata long 0" instead below '--------------------------------- ' main program loop ' :loop add pshare, #1 ' increment variable wrlong pshare, par ' write pshare to hub share variable add pdata, #1 ' increment variable wrlong pdata, padr ' write pdata to hub data variable jmp #:loop '--------------------------------- ' variables ' pshare res 1 pdata res 1 padr res 1 dat org '--------------------------------- ' consumer is used to read producer data from two shared hub variables ' consumer ' par will have the hub address of share ' cadr will have the hub address of data ' let's put the address of data in cadr mov cadr, par ' save par to padr add cadr, #4 ' reference the second long in the shared memory '--------------------------------- ' main program loop simply reads from shared memory ' :loop rdlong cshare, par ' read pshare from hub share variable rdlong cdata, cadr ' read pdata from hub data variable jmp #:loop '--------------------------------- ' variables ' cshare res 1 cdata res 1 cadr res 1
Let me get your ideas implemented and I will get back with the forum when I get it working
Thanks
Harprit
if I remember right all predefined longs (constants) like
'Constants. This section is similar to the CON block in SPIN Set_dira long %00001011_00000000_00000000_00000000 '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 inputmask long %11111011_11111111_11111111_11111111 'Mask for reading the Dout bit only
and all reserved longs (not predefined) acting as variables
'Variables. This section is similar to the VAR block in SPIN temp res 1 'temporary storage variable temp2 res 1 'temporary storage variable count res 1 'temporary storage variable Dat_Red res 1 'temporary storage variable
should be placed behind the last PASM-command.
temp2 that should contain the HUB-RAM-adress is never initialised with a valid HUB-RAM-adress.
You start a second cog for toggling the IO-pin 12
The code uses temp2 as the HUB-RAM-adress. But here it is the same temp2 is not initialised with a valid HUB-RAM-adress
As you start the second cog from SPIN too you simply use the second parameter there too.
instead of
cognew(@Toggle, 0) ' start new cog at Toggle
use
cognew(@Toggle, @P_Val) ' start new cog at Toggle
With this cognew the second cog has the HUB-RAM-adress of variable P_Val in its PAR-register
and can be accessed with
clkdelay rdlong time, par 'the delay subroutine,load TEMP2 into time ********ACTS LINE TEMP2=0************************
another comment on your variable names
using temp1, temp2 is bad programming style. The names temp1, temp2 don't say anything about their purpose.
If you think about the code your brain has to do some extra work translating temp1 contains bitpattern of outa
temp2 contains HUB-RAM-adress of P_Val. This extra brain-work makes it harder to understand for beginners.
Variable-names should be selfexplaining.
P_Val says almost nothing. "MCP3802_ADC_Val1" is self-explaining
As you write a book for PASM-beginners I even would define a variable for par.
So the codeline looks like this
'the delay subroutine 'the value of the SPIN-variable P_Val accessed through its HUB-RAM-adress "HUB_RAM_Adr_of_ADC_Val1" 'is used as start-value for the variable named "time" which is counted down to zero 'in a loop to generate a delay clkdelay rdlong time, HUB_RAM_Adr_of_ADC_Val1
I also added a comment of 4 lines to explain its purpose in detail.
That's what I would call easy to understand
"HUB_RAM_Adr_of_ADC_Val1" is a quite long name I guess for 50-90% of all programmers too long but it is self-explaining
maybe something like "HR_Ptr_ADC1" is still saying enough if you use the leading characters "HR_Ptr" as a short name for H)ub-R)am-Pointer all the the time
whenever you use RDLONG or WRLONG.
If somebody says: "too many characters to type". I'm using Ctrl-Cursor Ctrl-Shift-curcor Ctrl-C, Ctrl-v all the time and this even avoids typos.
When writing the variable names with ctrl-c/v it doesn't matter how long they are. always the same amount of keypresses.
I guess if you use self-explaining variable-names it will be easier for yourself to find bugs.
And you are giving a good model for beginners how to write code in a good style.
keep the questions coming and continue to post your code
best regards
Stefan
Just my personal preference and you can ignore this if you like, but I like shorter variable names and comment decorations. Variable names should communicate their purpose without expanding into sentences like VariableNamesShouldCommunicateTheirPurpose. Huge comment decorations are wasteful and generally tell the reader things about the programmer that may not be interpreted as intended.
Please consider trimming any code or comment line which has more than 70 characters.
"MOV OUTA, PEACE"