I must be getting confused here. I have thought that the cog architecture was what we called in the bad old core days, memory to memory. daSilva calls them cells, special ones are referred to as registers, but still in all I understand them ALL to be memory.
Does it really matter what you call them? Use whatever you're comfortable with. Besides, even calling them memory doesn't justify finding or going through their location
Anyway, check the data sheet (rev 1.4) chapter 5.2 Cog RAM, specifically table 15. Everything inside the cog is either a General or Special Purpose Register (GPR/SPR). Also, all instructions (see manual) refer to them as registers, e.g. mov: Instruction: Set a register to a value.
I will pick your comments up in the final writing, as I will all your other comments. I have a question that I need an answer for in detailed, plain English.
The manual says categorically that PAR is a read only register yet you can write a long to it with
wrlong value, PAR
Why is this so and how can one tell that a read only register can be written to. Also if this register can be written to when does one use
mov mem, PAR
It retrieves a shared memory address. Where is this address? It seems to me that this is useful only for multiple variable transfer. So if we can address mem+4 and so on it has to be at the end of the program space. Is this so and what are the implications for other variables and constants. Does it have to be declared last
PAR is in the highest 16 highest memory registers in each cog so its address never changes ($1F0). Does reading this register always give us mem (as used above). Which means to me that writing to PAR is the same as writing to mem but writing to mem+4 is not the same as writing to PAR+4. I kinda have some confused thoughts but I and the beginners need a really clear exposition from you. I think this is at the heart of the shyness to use this construct.
Please consult the manual. re: Source and destination address bit fields. At no time is the value of PAR written to in the examples above. Also see hub operations, and how they differ from cog operations, in the use of those fields.
means write value to PAR. This has to be explained to beginners so they (and I) have a better understanding.
Why is this true for all other registers and different for PAR. Maybe I will need a whole chapter on this kind of stuff.
That might be what it will take to get the beginners programming in PASM.
No, this means to write the value to the hub address in PAR. We usually pass the first address of a block of variables to the PASM code via PAR. Knowing the structure of that block we can access more than that pointed to by the address in PAR. The PAR register is read-only, you cannot write to it.
For example, let's say you have three long values in the hub: the first two are parameters you want to pass to the cog and the third will hold some value created in the cog -- your code might be structured like this:
dat
org 0
entry mov tmp1, par ' get start of structure
rdlong param1, tmp1 ' get first (long) parameter
add tmp1, #4 ' point to next
rdlong param2, tmp1 ' get second (long) parameter
add tmp1, #4 ' point to next
mov hubpntr, tmp1 ' save hub address
...
wrlong somevalue, hubpntr ' write to hub
This is not right. The WRLONG and RDLONG commands are the only two commands to modify HUB-RAM. All other commands modify COG-RAM.
What they all have in common is that the names represent adresses. Not variables. For COG-RAM this almost means the same. But only almost.
mov MyVar1, MyVar2 means take value that is stored in COG-RAM-register with adress that is labeled "MyVar2"
and
store it into COG-RAM-register with adress that is labeled "MyVar1".
For all COG-RAM-commands the first parameter is the destination and the second is the source
For WRLONG and RDLONG it is different. The first parameter is the value and the second is the HUB-RAM-destination
So a command WRLONG MyVar3, PAR means
take the value wich is stored in COG-RAM-register with adresslabeled "MyVar3" and store this value into a HUB-RAM-register. The COG-RAM-register labeled "PAR" contains the HUB-RAM-register-adress where to store it.
In the meantime I have thought several times over this adress-value-source-destination thing. Right now I'm sure to make it easy to understand for beginners we need a graphic that shows the COG-RAM and HUB-RAM with its real adresses and the values that are stored into the registers. And then there have to be a minimum six varied examples to show what stays constant and which modification of what (value, COG-RAM-register-adress, HUB-RAM-register-adress) changes what in the code and in the results.
It also will help to explain what the compiler does when the compiler evaluates label-names to adresses.
It all depends on the instruction. RDxxxx and WRxxxx are special cases because they read or write to the shared hub memory. They're like I/O instructions if you think about the hub memory as an external device.
RDxxxx behaves the most like other assembly instructions in that the destination field has the destination address (which is a cog register) and the source field's value is the hub address.
WRxxxx is a bit backwards in that the destination field actually has the source address (which is a cog register) and the source field's value is still the hub address, but, in this case, it's the destination hub address.
Just treat RDxxxx and WRxxxx as special cases. Their destination field is always a cog register and their source field always produces a value that's used as the hub address to be used, regardless of the direction of the data transfer.
PAR is just a assembler name for a specific cog register address.
This register is to be used the same way as any other cog register memory.
Except that it's read (source side) register only
the write (destination side) will not have the the value that was set by cognew.
Yes you can write to this shadow register, example you could copy Par to shadow Par.
Wrlong using par, is the same as using any other cog register.
The source side, points to a cog register that holds the value of the hub address you want to write to.
Just like any other time you don't use intermediate values (#0-511)
So how do we tell when we write to an address in a register and when we write to a register
What is the rule for beginners
H
Code, like wet clay, is very malleable -- we have the responsibility to shape it as required. PAR is usually used to pass an address (as I demoed above), but not always; it could be used to pass a command. In the attached program I am a passing the address of a command for the processes known to the cog. Have a look; I think it will help (I wrote a lot of programs like this for myself trying to learn [what I know] about PASM).
[Edit] I've also attached a simple blinker demo where PAR is used to pass the pin to blink. Shows that the programmer controls the meaning of the contents of the PAR register.
Marko pointed out that I got very lucky with my blinker demo; I had two fundamental programming errors. I have corrected those errors and spiced up the demo -- it is posted below.
Does this come close to it. It will be refined later for book
[FONT=Arial, sans-serif]When we are working in the PASM environment, we have access to two types of memory. We have access to the hub memory which is the main memory and is shared by all the clogs, and we have access to the cog memory which is the memory in each of the cogs and is isolated within each cog. It is not shared between the cogs. We cannot access the memory in a cog that we are not in. If we want to share information from cog to cog we have to write it to the hub memory in one cog and then read it from there with the second cog.[/FONT]
[FONT=Arial, sans-serif]In order to differentiate the two operations writing to and reading from the two memories uses two different addressing schemes as well as commands in PASM.[/FONT]
[FONT=Arial, sans-serif]wrlong variable, [to destination stored in register addressed][/FONT]
[FONT=Arial, sans-serif]is used for addressing memory in the hub. Note that we are writing to the address stored in the register that we identify in this instruction. So when we write a long to PAR, we are not writing to the PAR register, we are writing to an address which is stored in the PAR register. (When we write to cog memory, we write to the register itself). When we want to write to the address stored in PAR we first move that address into a memory register and then write to that register thus[/FONT]
Knowing mem allow us to add offsets of 4 to mem to store more than one piece of information. Each time we need a new address to store a variable, we add 4 to mem and write the long to it.
[FONT=Arial, sans-serif]When we move data in cog memory we move information directly between registers. We actually write to the register we are addressing. So when we say[/FONT]
[FONT=Arial, sans-serif]we are asking PASM to copy, what was in variable2 to variable in the cog memory. [/FONT]
[FONT=Arial, sans-serif]MOV moves data between cog memory locations[/FONT]
[FONT=Arial, sans-serif]WRLONG writes to addresses stored in HUB memory locations.[/FONT]
What does it do? Assuming data and pointer are valid registers in the COG, it reads a long from the hub address in pointer to COG register data. Without the number sign #, the instruction refers to the contents of pointer which is a number representing a hub address.
wrlong data, pointer
What does that do? It does almost the same operation with the same registers, except in this case it writes the long in COG data to the HUB address in the pointer register. As a matter of fact, if you change just one bit in the actual instruction, a write becomes a read (vis-a-vis) ! (wrlong data, pointer wr).
Now a little quiz.
What does this do?
dat org 0
rdlong data, #0
:end jmp #:end
data res 1
Well, it breaks a rule about using hard coded addresses, but in this case there is one and only one outcome assuming there has been no write to hub address 0. Hint press F8 and look at the dump.
What does this do?
dat org 0
pasm long 0
rdlong data, 0
:end jmp #:end
data res 1
[Edit] I've also attached a simple blinker demo where PAR is used to pass the pin to blink. Shows that the programmer controls the meaning of the contents of the PAR register.
That only works by accident (hub address #16 contains %1011_0000). What you should be doing is calling cognew with pin << 2 to sidestep par's 4n limitation. And pin extraction could then look like this:
entry mov tmp1, par ' get LED pin *4
shr tmp1, #2 ' extract pin number
mov ledmask, #1 ' |<
shl ledmask, tmp1 ' pin number -> pin mask
That only works by accident (hub address #16 contains %1011_0000).
Darn those lucky coincidences (the program gave a false positive with the pin I chose) -- I should have tested with other values. Thanks for the correction/reminder; I'll fix the posted code. I don't tend to pass values in PAR, hence my foul-up; I did want to point out -- with minor restrictions -- the programmer has control of what the contents of PAR signify.
Corrected version attached loops through eight LEDs on Demo or QuickStart board
Here is an initial (graphical) attempt re: how mov, mov[dis] and wrlong work. Note that the emphasis is on where the address comes from, i.e. the destination bit locations for mov[dis] are not specifically mentioned.
[FONT=Arial, sans-serif]Chapter xxx[/FONT]
[FONT=Arial, sans-serif]Interfacing to an external device. Based on a[/FONT]
[FONT=Arial, sans-serif]2 Line by 16 character Liquid Crystal Display (LCD)[/FONT]
[FONT=Arial, sans-serif]One of the tasks that we will find ourselves using the assembly language for is to interface to devices that would be tedious and slow to respond if we were using spin. A commonly used and inexpensive 2X16 display is often desirable in our projects to display pertinent information from time to time. Accordingly, we will investigate what it takes to interface the propeller to one of these LCDs using assembly language programming.[/FONT]
[FONT=Arial, sans-serif]This interfacing was covered in detail on my book on the spin language and if you want to refer to that as we proceed with interfacing in assembly language, it may be helpful to you. [/FONT]
[FONT=Arial, sans-serif]Interfacing to a device like the 16 character 2 line display that we have in mind is a matter of first sending the device a bit pattern that will initialize it and get it ready for displaying the information that we will send, and then sending the information. How the device is to be started up will have been described in detail in the data sheet for the display controller. The controller that we will be addressing is the Hitachi 44780. It is by far the most popular controller for using small LCDs. Its data sheet can be downloaded from.[/FONT]
[FONT=Arial, sans-serif]The data sheet tells us that we can control this LCD, either in 4-bit mode or in 8-bit mode. We will use 4 bit mode to save on the number of lines needed for the interface. This means that a group of 4 contiguous lines on the propeller have to be connected to the 4 data lines on the display. We will also have to connect the control lines, the power, and the ground and any other lines that may be necessary to make the device fully functional. After its all said and done, we need a minimum of seven lines to interface to the display. In addition to that we need another two lines (5 volts and power) if we want to activate the back light panel for the display. For now we will ignore the back light in that it is not a part of the programming challenge.[/FONT]
[FONT=Arial, sans-serif]A number of objects (software programs) that will control these displays are available on the Propeller Object Exchange (OBE) but we will ignore them for now. There is not much to be learned by using pre-written software though of course we should develop the skill to use these objects. Since our goal is to learn how to write a fully functional interface, we will start from scratch and build a functional interface.[/FONT]
[FONT=Arial, sans-serif]Figure XXX is a wiring diagram that illustrates how we need to connect the LC display to the propeller. The lines we are shown connected are as follows[/FONT]
[FONT=Arial, sans-serif] 8 Device select[/FONT]
[FONT=Arial, sans-serif] 9 Read/Write select[/FONT]
[FONT=Arial, sans-serif] 10 Data bit 0[/FONT]
[FONT=Arial, sans-serif] 11 Data bit 1[/FONT]
[FONT=Arial, sans-serif] 12 Data bit 2[/FONT]
[FONT=Arial, sans-serif] 13 Data bit 3[/FONT]
[FONT=Arial, sans-serif] 14 Data bit 4[/FONT]
[FONT=Arial, sans-serif] 15 Data bit 7[/FONT]
[FONT=Arial, sans-serif] 16 Enable [/FONT]
[FONT=Arial, sans-serif] 17 Read/Write select[/FONT]
[FONT=Arial, sans-serif] 18 RS Register select[/FONT]
[FONT=Arial, sans-serif]Table xxx[/FONT]
[FONT=Arial, sans-serif]Chapter xxx. Figure xxx. Connecting the LCD to the Propeller chip. [/FONT]
[FONT=Arial, sans-serif]Eight bit mode. [/FONT]
[FONT=Arial, sans-serif]We always refer to the internal pin (P) numbers in our discussions. The external numbers are ignored. The diagrams do not show the power, ground, reset lines etc.[/FONT]
[FONT=Arial, sans-serif]Pins 8 to 15 form the full 8 bit data path. To conserves lines we will be will be using a 4 bit path but I have shown all 8 bits connected to that you can experiment with both modes if you like. In this, pin 15 is the MSB. In the 4 bit mode, the 4 MSB are used. Unused lines can be left unconnected.[/FONT]
[FONT=Arial, sans-serif]The entire 14 pin connection to most of the LC displays on the market is as follows[/FONT]
[FONT=Arial, sans-serif]Pin No. Symbol [/FONT]
[FONT=Arial, sans-serif] 1 VSS Logic Ground[/FONT]
[FONT=Arial, sans-serif] 2 VDD Logic power 5 volts[/FONT]
[FONT=Arial, sans-serif] 3 VO Contrast of the display, can usually be grounded.[/FONT]
[FONT=Arial, sans-serif] 4 RS Register select ) These are [/FONT]
[FONT=Arial, sans-serif] 5 R/W Read/Write ) the 3 control [/FONT]
[FONT=Arial, sans-serif] 6 E Enable ) lines[/FONT]
[FONT=Arial, sans-serif] 7 DB0 [/FONT]
[FONT=Arial, sans-serif] 8 DB1 [/FONT]
[FONT=Arial, sans-serif] 9 DB2 [/FONT]
[FONT=Arial, sans-serif] 10 DB3 [/FONT]
[FONT=Arial, sans-serif] 11 DB4 [/FONT]
[FONT=Arial, sans-serif] 12 DB5 [/FONT]
[FONT=Arial, sans-serif] 13 DB6 [/FONT]
[FONT=Arial, sans-serif] 14 DB7 [/FONT]
[FONT=Arial, sans-serif] 15 BL Backlight power )These two lines[/FONT]
[FONT=Arial, sans-serif] 16 BL Backlight ground )can be ignored[/FONT]
[FONT=Arial, sans-serif]Many of the units do[FONT=Arial, sans-serif] not support lines 15 and 16 and we will not be using them in any of our experiments either.[/FONT][/FONT]
[FONT=Arial, sans-serif]The Hitachi data sheet for the 44780 controller used by the LCD specifies the start up sequence in detail. Basically the initial communication is with 4 lines and then when we specify that we want to continue with using 4 lines, the LCD becomes a 4 line device. Three more lines are needed to control the data into the LCD as is shown in Figure XXX. That makes a total of 7 lines.[/FONT]
[FONT=Arial, sans-serif]The other 3 lines we need have to do with power ground and the contrast for the LCD. These three lines are hardware connections that do not need to go through the MCU.[/FONT]
[FONT=Arial, sans-serif]Here is the SPIN code that was used to initialize the LCD in the SPIN book. This is what has to be converted to assembly language,[/FONT]
[FONT=Arial, sans-serif][SIZE=3]PRI INITIALIZE_LCD [/SIZE] 'The addresses and data used here are as[/FONT]
[FONT=Arial, sans-serif][SIZE=3]waitcnt(150_000+cnt) 'specified in the Hitachi data sheet for the[/SIZE][/FONT]
[FONT=Arial, sans-serif][SIZE=3]'display. YOU MUST CHECK THE DATA SHEET.[/SIZE][/FONT]
[FONT=Arial, sans-serif][SIZE=3]OUTA[RegSelect] := 0 'three lines are specified so we can write[/SIZE][/FONT]
[FONT=Arial, sans-serif][SIZE=3]OUTA[ReadWrite] := 0 'the initial set up bits for the LCD[/SIZE][/FONT]
[FONT=Arial, sans-serif][SIZE=3]OUTA[Enable ] := 0 'See Hitachi HD44780 data sheet[/SIZE][/FONT]
[FONT=Arial, sans-serif][SIZE=3]SEND_INSTRUCTION (%0011_1000) 'Sets DL=8 bits, N=2 lines, F=5x7 font[/SIZE][/FONT]
[FONT=Arial, sans-serif][SIZE=3]waitcnt(50_000+cnt)’ 'wait for instruction to execute[/SIZE][/FONT]
[FONT=Arial, sans-serif][SIZE=3]SEND_INSTRUCTION (%0000_1111) 'Display on, Cursor on, Blink on [/SIZE][/FONT]
[FONT=Arial, sans-serif][SIZE=3]waitcnt(12_000+cnt) 'wait for instruction to execute [/SIZE][/FONT]
[FONT=Arial, sans-serif][SIZE=3]SEND_INSTRUCTION (%0000_0110) 'Move Cursor, Do not shift display[/SIZE][/FONT]
[FONT=Arial, sans-serif][SIZE=3]waitcnt(12_000+cnt) 'wait for instruction to execute[/SIZE][/FONT]
[FONT=Arial, sans-serif]Program xxx for initializing LCD, in SPIN[/FONT]
[FONT=Arial, sans-serif]Program segment.[/FONT]
[FONT=Arial, sans-serif]Storing the LCD's display data[/FONT]
[FONT=Arial, sans-serif]We can display 32 characters on our LCD display. A 4 byte long can hold 4 characters. So me need 8 longs with the 32 characters packed into them for the data we will be moving back and forth between the cogs. These 8 longs are read in a loop by the PASM cog, unpacked and placed in the display memory of the LCD. [/FONT]
[FONT=Arial, sans-serif]Our task breaks down to creating a write routine in Spin and a read routine in PASM. There are other ancillary tasks that have to be undertaken as a part of all this but we know already know how to do most of that so that will be the easy part.[/FONT]
[FONT=Arial, sans-serif]Note: There are additional undisplayed memory locations in most LCDs but we will ignore that feature for now. (The manufacturer of the device we select specifies how much more there is). You can take these locations into consideration in the software once you get comfortable with the talking to the LCD and moving the data around.[/FONT]
[FONT=Arial, sans-serif]The difficult part is following the timing constraints specified in the Hitachi manual as closely as possible. We have to create very accurate delays to make sure the device will actually start up because if it does not, there is not much else for us to do! So the first task is to write a routine that will give us the delays specified in the data sheet. Since we have a wait instruction that specifies waits in machine cycles this is fairly straightforward.[/FONT]
[FONT=Arial, sans-serif]We will need shell to place the routines we develop in to lets create the shell first. Here is what we need in the SPIN Cog so we can look at what is going on in the first long. This long can hold 4 characters and we will set to an arbitrary binary number as a place holder for now.[/FONT]
[FONT=Arial, sans-serif]The routine breaks up the long and displays it as 4 bytes to make it easier for us to read. The display matches the bits that the FirstLong is set to.[/FONT]
[FONT=Lucida Console, monospace][SIZE=2]VAR[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]long FirstLong, shared[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]OBJ[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]fds: "FullDuplexSerial"[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]PUB FirstCog 'displays values on console[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]fds.start(31,30,0,115200) 'start console at 115200 for debug output[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]' cognew(@SecondCog, @FirstLong) 'start the second Cog in PASM [/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]waitcnt(clkfreq/4+cnt) 'wait 1/4 for everything to stabilize.[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]repeat 'loop [/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]FirstLong:=%00100000_00001000_11111111_10000000 [/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]fds.tx($1) 'home to 0,0[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]FirstLong->=24 'rotate 24 bit to read first byte[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]fds.bin(FirstLong,8) 'display byte[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]FirstLong->=24 'repeat for second byte[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]fds.tx("_") '[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]fds.bin(FirstLong,8) '[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]FirstLong->=24 'repeat for third byte[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]fds.tx("_") '[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]fds.bin(FirstLong,8) '[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]FirstLong->=24 'repeat for fourth byte[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]fds.tx("_") '[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]fds.bin(FirstLong,8) '[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]fds.tx(" ") '[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]fds.tx($d) 'new line[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]waitcnt(clkfreq/60+cnt) 'flicker free wait[/SIZE][/FONT]
[FONT=Arial, sans-serif]Next we need to start work on the PASM routine. All we are doing here is writing the long to the HUB memory from where it can be retrieved by the SPIN routine.[/FONT]
[FONT=Lucida Console, monospace][SIZE=2]DAT[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]org 0 'start at 0 location in the cog[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]SecondCog 'start point identification[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]:loop wrlong shared, PAR 'write variable to location in PAR[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]jmp #:loop 'jump back do it again[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]shared long %00100000_00001000_10101010_10000000 'variable declaration[/SIZE][/FONT]
[FONT=Arial, sans-serif]Putting it all together so we have a program you can run, we get.[/FONT]
[FONT=Lucida Console, monospace][SIZE=2]VAR[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]long FirstLong[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]OBJ[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]fds: "FullDuplexSerial"[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]PUB FirstCog 'displays values on console[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]fds.start(31,30,0,115200) 'start console at 115200 for debug output[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]cognew(@SecondCog, @shared) 'start the second Cog in PASM [/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]waitcnt(clkfreq/4+cnt) 'wait 1/4 for everything to stabilize.[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]repeat 'loop [/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]FirstLong:=shared [/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]fds.tx($1) 'home to 0,0[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]FirstLong->=24 [/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]fds.bin(FirstLong,8) 'display first value[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]FirstLong->=24[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]fds.tx("_") [/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]fds.bin(FirstLong,8) 'display first value [/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]FirstLong->=24 [/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]fds.tx("_") [/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]fds.bin(FirstLong,8)[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]FirstLong->=24[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]fds.tx("_") [/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]fds.bin(FirstLong,8)[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]fds.tx(" ") 'display first value[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]fds.tx($d) 'new line[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]waitcnt(clkfreq/60+cnt) 'flicker free wait[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]DAT[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]org 0 'start at 0 location in the cog[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]SecondCog 'start point identification[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]:loop wrlong shared, PAR 'write variable to location in PAR[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]jmp #:loop 'jump back do it again[/SIZE][/FONT]
[FONT=Lucida Console, monospace][SIZE=2]shared long %00100000_00001000_10101010_10000000 'variable declaration[/SIZE][/FONT]
[FONT=Arial, sans-serif]The communication is complete but the PASM routine is doing the writing and the SPIN code is doing the reading. It is easier to do it this way for now (so we can see what we are doing) but we will need to reverse things one we get the LCD working.. [/FONT]
[FONT=Arial, sans-serif]Lets write to code to get the LCD up and running. NEXT POST!
Please explain why this affects the communications. What is happening because it was not done?
If nothing is specified then RCFAST is used (check F8 display). This can be anything between 8..20MHz. clkfreq is - however - set to 12MHz. FullDuplexSerial sets its timing based on clkfreq which doesn't work for me in RCFAST mode (and I guess for other people as well). Manually setting long[0] to 13M sorts the problem for me but in general people will have a crystal attached so you might as well get them into the habit of using it.
PAR contains the address of the first free register in HUB memory after all the cogs are up and running if all the cogs share the passed variable.
Not sure what you're getting at here. You start cogs with cognew/coginit. This function/insn takes a parameter regardless of whether it's used from SPIN or PASM. The user defines said parameter. Period. If I - for example - pass 9 to all cogs I will get 8 (lower 2 bits cleared, %1001 -> %1000) in par. That's unlikely to be the first free register in HUB memory (whatever that means).
What made you ask this question?
Update: In general, if you pass parameter N to e.g. cognew you will see 14bit of it in PASM context (N[15..2], available from par).
I got to thinking that we have access to the registers above PAR. Therefore they must be free. Or important data would be compromised. But it was obviouss that it must be more complicared than that
I got to thinking that we have access to the registers above PAR. Therefore they must be free. Or important data would be compromised. But it was obviouss that it must be more complicared than that.
Is this what you mean?
VAR
long storage[8]
PUB null
cognew(@entry, @storage{0})
Now par points to storage{0} and you can access storage[1]..storage[7] by adding the relevant offset. Note that I might as well use
VAR
long storage[8]
PUB null
cognew(@entry, @storage[7])
and subtract the offset in order to reach storage[0]..storage[6]. Difference here being that the free/usable space is belowpar. So it's all up to you
Questionable in its usefulness but still feasible is to e.g. pass @storage[-42] which doesn't point to free/usable memory until you apply the correct offset. It's just a value which your PASM code has to interpret (correctly).
I got to thinking that we have access to the registers above PAR. Therefore they must be free. Or important data would be compromised. But it was obviouss that it must be more complicared than that
So the question. But its too much for beginners.
H
No! Not to much for beginners unless you want to lock them into permanent hello world level of code. The use of PAR for inter-process communication is critical to any system that must have speed. You can use an MC320X and spin to capture an analog state and that is fine if you want to restrict yourself to the 500hz range and below. But you need to have speed to do the really fun stuff. A while back someone here posed the question of why PASM. I can not do the curve tracer I am working on in spin. I need to capture 256 points in 7.5ms, for each of three channels simultaneously. Spin will be fine for the readout from main ram and transferring for permanent capture to a PC, but no way on the actual data acquisition. 256 points will easily fit into cog ram 512 points if I want to get creative and pack 2*12 bit into one word. But then I need to get it into the main ram. MUST UNDERSTAND PAR and w/rlong. You can and do write to registers above PAR, outa and dira come to mind. Perhaps rather than a routine to clock the data out, (not tried yet but this weekend) I plan to see if I can master the counter control to have the counter set and controlled on the fly to do the same thing.
Frank
Jazzed, great demo of IPC in post 347/8 I am already using parts of this to instrument my ADC capture as well as kickstart the data transfer routine from cog array to main ram array. Best way to do it, read it, play with it, dissect it, b@stardize it until it fits and I have the method cold. B.Z. man!!
No! Not to much for beginners unless you want to lock them into permanent hello world level of code.
The part that is too much for beginners is this higher level discussion. The don't to know this. Of course PAR will be covered in some detail as you might have noticed if you have been following this thread faithfully. And of course any serious user of the propeller needs to be conversant with PASM or we would be having this thread would we?
Besides why do you feel so strongly that a good primary education is a prescription for NOT being able ever to discuss nuclear physics. I personally am amazed by that conclusion but there may be aspects to learning a language that are beyond me.
I find the constant discussion of subject matter that has nothing to do with what beginners need somewhat distracting and wish the discussion focused more narrowly on the subject at hand. Specially so since so few beginner have stated what they would like the book to cover.
I am finding that interfacing to a 2x16 in PASM is a bit more difficult than I had imagined! A good lesson for a beginner. I had little difficulty in SPIN. Its coming along though and I will post some of the subroutines soon.
"so few beginner have stated what they would like the book to cover"
How would they know?
People may well get the idea that they want to learn PASM to get the speed they need for their projects, or just because it grabs their interest somehow, but as they know nothing much about it how could they know what to ask for?
What you are fishing for is advise from the "not so beginner anymore" who can say, "I wish someone had explained, X,Y,Z more clearly when I was starting out."
IMHO, on a project level, beginners asking for coverage of specific things isn't a bad thing. Some code examples are more appealing than others are.
Frankly, I think some of the best are tools, where the beginner can complete the project and get the understanding from it, and be empowered to do more by the product of it. A logic analyzer, or debugger, signal generator, etc... are great in this way, because it's a two-fer! Learning, and building tools is always productive. At the very least, doing things that way gets people eating their own dog food early on. Never a bad thing. This is why the "must have scope" discussion happened, for example.
The most challenging thing the beginner has in front of them is learning how to learn in a given discipline. There are generalities that are always good, but there are also specifics that can very significantly improve the process too. That's what a lot of the meta-discussion here has been about.
My own personal experience has been building tools useful to the discipline at hand is one of the better ways to reach some level of mastery.
in the following code note the two lines marked with ********************************
DAT
org 0 'start at 0 location in the cog
SecondCog 'start point identification
:test
mov dira, setdira
mov temp, :value
wrlong temp, par '****************works here*********************************
call #sendInstr
'**************fails if moved here*******************************
wrlong temp, par
jmp #:test
:value long %00001111_00001111_00001111_00001111
CheckBusyBit
mov :timer, cnt
add :timer, wait500
waitcnt :timer, #0
:timer res 1
CheckBusyBit_ret ret
SendInstr
call #CheckBusyBit
andn outa, readbit
andn outa, selectbit
or outa, enablebit
mov outa, temp
andn dira, enablebit
SendInstr_ret ret
wait500 long 500_000
wait49 long 49_200
wait1 long 1_200
wait12 long 12_000
SetDira long %00000000_00000111_11111111_00000000
dataByte long %00000000_00000000_11111111_00000000
Send1 long %00000000_00000000_00110000_00000000
Send2 long %00000000_00000000_00110000_00000000
Send3 long %00000000_00000000_00110000_00000000
Send4 long %00000000_00000000_00111000_00000000
Send5 long %00000000_00000000_00001111_00000000
clear long %00000000_00000000_00000001_00000000
SelectBit long %00000000_00000100_00000000_00000000
ReadBit long %00000000_00000010_00000000_00000000
EnableBit long %00000000_00000001_00000000_00000000
mem1 res 1 'first address storage space
wait res 1 'second address storage space
temp res 1
timer res 1
I am using par to look at the value of temp at two locations marked in the code.
If I move the par command to the first ******* line I can read the value of temp OK
If I move the par command to the second ******* line, the value of temp goes to 0
I did not modify temp in any of the following called routines.
WHY? does temp go to 0
Am I correct or not that in this case temp is global to this cog?
I started learning PASM exactly 6 weeks ago to I am still very much a beginner.
I have a lot of things that I have figured out but there is much much more to go.
And I do tend to get stuck and then things get slow for me.
Before I ask a question I have to investigate the phenomenon in some detail.
Still I find your observations very interesting.
So maybe the experience programmers should provide more beginners guidance ideas
Comments
Anyway, check the data sheet (rev 1.4) chapter 5.2 Cog RAM, specifically table 15. Everything inside the cog is either a General or Special Purpose Register (GPR/SPR). Also, all instructions (see manual) refer to them as registers, e.g. mov: Instruction: Set a register to a value.
I will pick your comments up in the final writing, as I will all your other comments. I have a question that I need an answer for in detailed, plain English.
The manual says categorically that PAR is a read only register yet you can write a long to it with
wrlong value, PAR
Why is this so and how can one tell that a read only register can be written to. Also if this register can be written to when does one use
mov mem, PAR
It retrieves a shared memory address. Where is this address? It seems to me that this is useful only for multiple variable transfer. So if we can address mem+4 and so on it has to be at the end of the program space. Is this so and what are the implications for other variables and constants. Does it have to be declared last
PAR is in the highest 16 highest memory registers in each cog so its address never changes ($1F0). Does reading this register always give us mem (as used above). Which means to me that writing to PAR is the same as writing to mem but writing to mem+4 is not the same as writing to PAR+4. I kinda have some confused thoughts but I and the beginners need a really clear exposition from you. I think this is at the heart of the shyness to use this construct.
Regards
H
To a beginner
wrlong value, PAR
means write value to PAR. This has to be explained to beginners so they (and I) have a better understanding.
Why is this true for all other registers and different for PAR. Maybe I will need a whole chapter on this kind of stuff.
That might be what it will take to get the beginners programming in PASM.
Please expound while I do the reading.
H
No, this means to write the value to the hub address in PAR. We usually pass the first address of a block of variables to the PASM code via PAR. Knowing the structure of that block we can access more than that pointed to by the address in PAR. The PAR register is read-only, you cannot write to it.
For example, let's say you have three long values in the hub: the first two are parameters you want to pass to the cog and the third will hold some value created in the cog -- your code might be structured like this:
This is not right. The WRLONG and RDLONG commands are the only two commands to modify HUB-RAM. All other commands modify COG-RAM.
What they all have in common is that the names represent adresses. Not variables. For COG-RAM this almost means the same. But only almost.
mov MyVar1, MyVar2 means take value that is stored in COG-RAM-register with adress that is labeled "MyVar2"
and
store it into COG-RAM-register with adress that is labeled "MyVar1".
For all COG-RAM-commands the first parameter is the destination and the second is the source
For WRLONG and RDLONG it is different. The first parameter is the value and the second is the HUB-RAM-destination
So a command WRLONG MyVar3, PAR means
take the value wich is stored in COG-RAM-register with adress labeled "MyVar3" and store this value into a HUB-RAM-register. The COG-RAM-register labeled "PAR" contains the HUB-RAM-register-adress where to store it.
In the meantime I have thought several times over this adress-value-source-destination thing. Right now I'm sure to make it easy to understand for beginners we need a graphic that shows the COG-RAM and HUB-RAM with its real adresses and the values that are stored into the registers. And then there have to be a minimum six varied examples to show what stays constant and which modification of what (value, COG-RAM-register-adress, HUB-RAM-register-adress) changes what in the code and in the results.
It also will help to explain what the compiler does when the compiler evaluates label-names to adresses.
keep the questions coming
best regards
Stefan
So how do we tell when we write to an address in a register and when we write to a register
What is the rule for beginners
I guess Stefan answered this in the above post.
Thanks Stefan, I will add many graphics in the book.
H
RDxxxx behaves the most like other assembly instructions in that the destination field has the destination address (which is a cog register) and the source field's value is the hub address.
WRxxxx is a bit backwards in that the destination field actually has the source address (which is a cog register) and the source field's value is still the hub address, but, in this case, it's the destination hub address.
Just treat RDxxxx and WRxxxx as special cases. Their destination field is always a cog register and their source field always produces a value that's used as the hub address to be used, regardless of the direction of the data transfer.
PAR is just a assembler name for a specific cog register address.
This register is to be used the same way as any other cog register memory.
Except that it's read (source side) register only
the write (destination side) will not have the the value that was set by cognew.
Yes you can write to this shadow register, example you could copy Par to shadow Par.
Wrlong using par, is the same as using any other cog register.
The source side, points to a cog register that holds the value of the hub address you want to write to.
Just like any other time you don't use intermediate values (#0-511)
Code, like wet clay, is very malleable -- we have the responsibility to shape it as required. PAR is usually used to pass an address (as I demoed above), but not always; it could be used to pass a command. In the attached program I am a passing the address of a command for the processes known to the cog. Have a look; I think it will help (I wrote a lot of programs like this for myself trying to learn [what I know] about PASM).
[Edit] I've also attached a simple blinker demo where PAR is used to pass the pin to blink. Shows that the programmer controls the meaning of the contents of the PAR register.
Marko pointed out that I got very lucky with my blinker demo; I had two fundamental programming errors. I have corrected those errors and spiced up the demo -- it is posted below.
[FONT=Arial, sans-serif]When we are working in the PASM environment, we have access to two types of memory. We have access to the hub memory which is the main memory and is shared by all the clogs, and we have access to the cog memory which is the memory in each of the cogs and is isolated within each cog. It is not shared between the cogs. We cannot access the memory in a cog that we are not in. If we want to share information from cog to cog we have to write it to the hub memory in one cog and then read it from there with the second cog.[/FONT]
[FONT=Arial, sans-serif]In order to differentiate the two operations writing to and reading from the two memories uses two different addressing schemes as well as commands in PASM.[/FONT]
[FONT=Arial, sans-serif]wrlong variable, [to destination stored in register addressed][/FONT]
[FONT=Arial, sans-serif]is used for addressing memory in the hub. Note that we are writing to the address stored in the register that we identify in this instruction. So when we write a long to PAR, we are not writing to the PAR register, we are writing to an address which is stored in the PAR register. (When we write to cog memory, we write to the register itself). When we want to write to the address stored in PAR we first move that address into a memory register and then write to that register thus[/FONT]
[FONT=Arial, sans-serif]mov mem, PAR[/FONT]
[FONT=Arial, sans-serif]wrlong variable, mem[/FONT]
Knowing mem allow us to add offsets of 4 to mem to store more than one piece of information. Each time we need a new address to store a variable, we add 4 to mem and write the long to it.
[FONT=Arial, sans-serif]When we move data in cog memory we move information directly between registers. We actually write to the register we are addressing. So when we say[/FONT]
[FONT=Arial, sans-serif]mov variable, variable2[/FONT]
[FONT=Arial, sans-serif]we are asking PASM to copy, what was in variable2 to variable in the cog memory. [/FONT]
[FONT=Arial, sans-serif]MOV moves data between cog memory locations[/FONT]
[FONT=Arial, sans-serif]WRLONG writes to addresses stored in HUB memory locations.[/FONT]
"rdlong destination, source" is clearer
The thing is, wrlong has to follow the same basic rules as all the other PASM instructions:
[label] [conditionals] instruction destination, [#]source [modifiers]
So consider this:
rdlong data, pointer
What does it do? Assuming data and pointer are valid registers in the COG, it reads a long from the hub address in pointer to COG register data. Without the number sign #, the instruction refers to the contents of pointer which is a number representing a hub address.
wrlong data, pointer
What does that do? It does almost the same operation with the same registers, except in this case it writes the long in COG data to the HUB address in the pointer register. As a matter of fact, if you change just one bit in the actual instruction, a write becomes a read (vis-a-vis) ! (wrlong data, pointer wr).
Now a little quiz.
What does this do?
dat org 0
rdlong data, #0
:end jmp #:end
data res 1
Well, it breaks a rule about using hard coded addresses, but in this case there is one and only one outcome assuming there has been no write to hub address 0. Hint press F8 and look at the dump.
What does this do?
dat org 0
pasm long 0
rdlong data, 0
:end jmp #:end
data res 1
Darn those lucky coincidences (the program gave a false positive with the pin I chose) -- I should have tested with other values. Thanks for the correction/reminder; I'll fix the posted code. I don't tend to pass values in PAR, hence my foul-up; I did want to point out -- with minor restrictions -- the programmer has control of what the contents of PAR signify.
Corrected version attached loops through eight LEDs on Demo or QuickStart board
[FONT=Arial, sans-serif]Interfacing to an external device. Based on a[/FONT]
[FONT=Arial, sans-serif]2 Line by 16 character Liquid Crystal Display (LCD)[/FONT]
[FONT=Arial, sans-serif]One of the tasks that we will find ourselves using the assembly language for is to interface to devices that would be tedious and slow to respond if we were using spin. A commonly used and inexpensive 2X16 display is often desirable in our projects to display pertinent information from time to time. Accordingly, we will investigate what it takes to interface the propeller to one of these LCDs using assembly language programming.[/FONT]
[FONT=Arial, sans-serif]This interfacing was covered in detail on my book on the spin language and if you want to refer to that as we proceed with interfacing in assembly language, it may be helpful to you. [/FONT]
[FONT=Arial, sans-serif]Interfacing to a device like the 16 character 2 line display that we have in mind is a matter of first sending the device a bit pattern that will initialize it and get it ready for displaying the information that we will send, and then sending the information. How the device is to be started up will have been described in detail in the data sheet for the display controller. The controller that we will be addressing is the Hitachi 44780. It is by far the most popular controller for using small LCDs. Its data sheet can be downloaded from.[/FONT]
[FONT=Arial, sans-serif] http://semiconductor.hitachi.com/products/pdf/99rtd006d2.pdf[/FONT]
[FONT=Arial, sans-serif] http://pic.rocklizard.org/LCDDriver/HD44780U.pdf[/FONT]
[FONT=Arial, sans-serif]The data sheet tells us that we can control this LCD, either in 4-bit mode or in 8-bit mode. We will use 4 bit mode to save on the number of lines needed for the interface. This means that a group of 4 contiguous lines on the propeller have to be connected to the 4 data lines on the display. We will also have to connect the control lines, the power, and the ground and any other lines that may be necessary to make the device fully functional. After its all said and done, we need a minimum of seven lines to interface to the display. In addition to that we need another two lines (5 volts and power) if we want to activate the back light panel for the display. For now we will ignore the back light in that it is not a part of the programming challenge.[/FONT]
[FONT=Arial, sans-serif]A number of objects (software programs) that will control these displays are available on the Propeller Object Exchange (OBE) but we will ignore them for now. There is not much to be learned by using pre-written software though of course we should develop the skill to use these objects. Since our goal is to learn how to write a fully functional interface, we will start from scratch and build a functional interface.[/FONT]
[FONT=Arial, sans-serif]Figure XXX is a wiring diagram that illustrates how we need to connect the LC display to the propeller. The lines we are shown connected are as follows[/FONT]
[FONT=Arial, sans-serif] 8 Device select[/FONT]
[FONT=Arial, sans-serif] 9 Read/Write select[/FONT]
[FONT=Arial, sans-serif] 10 Data bit 0[/FONT]
[FONT=Arial, sans-serif] 11 Data bit 1[/FONT]
[FONT=Arial, sans-serif] 12 Data bit 2[/FONT]
[FONT=Arial, sans-serif] 13 Data bit 3[/FONT]
[FONT=Arial, sans-serif] 14 Data bit 4[/FONT]
[FONT=Arial, sans-serif] 15 Data bit 7[/FONT]
[FONT=Arial, sans-serif] 16 Enable [/FONT]
[FONT=Arial, sans-serif] 17 Read/Write select[/FONT]
[FONT=Arial, sans-serif] 18 RS Register select[/FONT]
[FONT=Arial, sans-serif]Table xxx[/FONT]
[FONT=Arial, sans-serif]Chapter xxx. Figure xxx. Connecting the LCD to the Propeller chip. [/FONT]
[FONT=Arial, sans-serif]Eight bit mode. [/FONT]
[FONT=Arial, sans-serif]We always refer to the internal pin (P) numbers in our discussions. The external numbers are ignored. The diagrams do not show the power, ground, reset lines etc.[/FONT]
[FONT=Arial, sans-serif]Pins 8 to 15 form the full 8 bit data path. To conserves lines we will be will be using a 4 bit path but I have shown all 8 bits connected to that you can experiment with both modes if you like. In this, pin 15 is the MSB. In the 4 bit mode, the 4 MSB are used. Unused lines can be left unconnected.[/FONT]
[FONT=Arial, sans-serif]The entire 14 pin connection to most of the LC displays on the market is as follows[/FONT]
[FONT=Arial, sans-serif]Pin No. Symbol [/FONT]
[FONT=Arial, sans-serif] 1 VSS Logic Ground[/FONT]
[FONT=Arial, sans-serif] 2 VDD Logic power 5 volts[/FONT]
[FONT=Arial, sans-serif] 3 VO Contrast of the display, can usually be grounded.[/FONT]
[FONT=Arial, sans-serif] 4 RS Register select ) These are [/FONT]
[FONT=Arial, sans-serif] 5 R/W Read/Write ) the 3 control [/FONT]
[FONT=Arial, sans-serif] 6 E Enable ) lines[/FONT]
[FONT=Arial, sans-serif] 7 DB0 [/FONT]
[FONT=Arial, sans-serif] 8 DB1 [/FONT]
[FONT=Arial, sans-serif] 9 DB2 [/FONT]
[FONT=Arial, sans-serif] 10 DB3 [/FONT]
[FONT=Arial, sans-serif] 11 DB4 [/FONT]
[FONT=Arial, sans-serif] 12 DB5 [/FONT]
[FONT=Arial, sans-serif] 13 DB6 [/FONT]
[FONT=Arial, sans-serif] 14 DB7 [/FONT]
[FONT=Arial, sans-serif] 15 BL Backlight power )These two lines[/FONT]
[FONT=Arial, sans-serif] 16 BL Backlight ground )can be ignored[/FONT]
[FONT=Arial, sans-serif]Many of the units do[FONT=Arial, sans-serif] not support lines 15 and 16 and we will not be using them in any of our experiments either.[/FONT][/FONT]
[FONT=Arial, sans-serif]The Hitachi data sheet for the 44780 controller used by the LCD specifies the start up sequence in detail. Basically the initial communication is with 4 lines and then when we specify that we want to continue with using 4 lines, the LCD becomes a 4 line device. Three more lines are needed to control the data into the LCD as is shown in Figure XXX. That makes a total of 7 lines.[/FONT]
[FONT=Arial, sans-serif]The other 3 lines we need have to do with power ground and the contrast for the LCD. These three lines are hardware connections that do not need to go through the MCU.[/FONT]
[FONT=Arial, sans-serif]Here is the SPIN code that was used to initialize the LCD in the SPIN book. This is what has to be converted to assembly language,[/FONT]
[FONT=Arial, sans-serif]Program xxx for initializing LCD, in SPIN[/FONT]
[FONT=Arial, sans-serif]Program segment.[/FONT]
[FONT=Arial, sans-serif]Storing the LCD's display data[/FONT]
[FONT=Arial, sans-serif]We can display 32 characters on our LCD display. A 4 byte long can hold 4 characters. So me need 8 longs with the 32 characters packed into them for the data we will be moving back and forth between the cogs. These 8 longs are read in a loop by the PASM cog, unpacked and placed in the display memory of the LCD. [/FONT]
[FONT=Arial, sans-serif]Our task breaks down to creating a write routine in Spin and a read routine in PASM. There are other ancillary tasks that have to be undertaken as a part of all this but we know already know how to do most of that so that will be the easy part.[/FONT]
[FONT=Arial, sans-serif]Note: There are additional undisplayed memory locations in most LCDs but we will ignore that feature for now. (The manufacturer of the device we select specifies how much more there is). You can take these locations into consideration in the software once you get comfortable with the talking to the LCD and moving the data around.[/FONT]
[FONT=Arial, sans-serif]The difficult part is following the timing constraints specified in the Hitachi manual as closely as possible. We have to create very accurate delays to make sure the device will actually start up because if it does not, there is not much else for us to do! So the first task is to write a routine that will give us the delays specified in the data sheet. Since we have a wait instruction that specifies waits in machine cycles this is fairly straightforward.[/FONT]
[FONT=Arial, sans-serif]We will need shell to place the routines we develop in to lets create the shell first. Here is what we need in the SPIN Cog so we can look at what is going on in the first long. This long can hold 4 characters and we will set to an arbitrary binary number as a place holder for now.[/FONT]
[FONT=Arial, sans-serif]The routine breaks up the long and displays it as 4 bytes to make it easier for us to read. The display matches the bits that the FirstLong is set to.[/FONT]
[FONT=Arial, sans-serif]Next we need to start work on the PASM routine. All we are doing here is writing the long to the HUB memory from where it can be retrieved by the SPIN routine.[/FONT]
[FONT=Arial, sans-serif]Putting it all together so we have a program you can run, we get.[/FONT]
[FONT=Arial, sans-serif]The communication is complete but the PASM routine is doing the writing and the SPIN code is doing the reading. It is easier to do it this way for now (so we can see what we are doing) but we will need to reverse things one we get the LCD working.. [/FONT]
[FONT=Arial, sans-serif]Lets write to code to get the LCD up and running. NEXT POST!
H
[/FONT]
Please explain why this affects the communications
What is happening because it was not done
Harprit
Can it ever be true?
Is it always true?
PAR contains the address of the first free register in HUB memory after all the cogs are up and running if all the cogs share the passed variable.
This confused me enough that I thought I had better ask the experts.
Harprit.
What made you ask this question?
Update: In general, if you pass parameter N to e.g. cognew you will see 14bit of it in PASM context (N[15..2], available from par). This means that if you pass e.g. an unaligned address (e.g. 4n+2) only 4n will arrive in PASM.
I got to thinking that we have access to the registers above PAR. Therefore they must be free. Or important data would be compromised. But it was obviouss that it must be more complicared than that
So the question. But its too much for beginners.
H
Questionable in its usefulness but still feasible is to e.g. pass @storage[-42] which doesn't point to free/usable memory until you apply the correct offset. It's just a value which your PASM code has to interpret (correctly).
No! Not to much for beginners unless you want to lock them into permanent hello world level of code. The use of PAR for inter-process communication is critical to any system that must have speed. You can use an MC320X and spin to capture an analog state and that is fine if you want to restrict yourself to the 500hz range and below. But you need to have speed to do the really fun stuff. A while back someone here posed the question of why PASM. I can not do the curve tracer I am working on in spin. I need to capture 256 points in 7.5ms, for each of three channels simultaneously. Spin will be fine for the readout from main ram and transferring for permanent capture to a PC, but no way on the actual data acquisition. 256 points will easily fit into cog ram 512 points if I want to get creative and pack 2*12 bit into one word. But then I need to get it into the main ram. MUST UNDERSTAND PAR and w/rlong. You can and do write to registers above PAR, outa and dira come to mind. Perhaps rather than a routine to clock the data out, (not tried yet but this weekend) I plan to see if I can master the counter control to have the counter set and controlled on the fly to do the same thing.
Frank
Jazzed, great demo of IPC in post 347/8 I am already using parts of this to instrument my ADC capture as well as kickstart the data transfer routine from cog array to main ram array. Best way to do it, read it, play with it, dissect it, b@stardize it until it fits and I have the method cold. B.Z. man!!
The part that is too much for beginners is this higher level discussion. The don't to know this. Of course PAR will be covered in some detail as you might have noticed if you have been following this thread faithfully. And of course any serious user of the propeller needs to be conversant with PASM or we would be having this thread would we?
Besides why do you feel so strongly that a good primary education is a prescription for NOT being able ever to discuss nuclear physics. I personally am amazed by that conclusion but there may be aspects to learning a language that are beyond me.
I find the constant discussion of subject matter that has nothing to do with what beginners need somewhat distracting and wish the discussion focused more narrowly on the subject at hand. Specially so since so few beginner have stated what they would like the book to cover.
I am finding that interfacing to a 2x16 in PASM is a bit more difficult than I had imagined! A good lesson for a beginner. I had little difficulty in SPIN. Its coming along though and I will post some of the subroutines soon.
Harprit
"so few beginner have stated what they would like the book to cover"
How would they know?
People may well get the idea that they want to learn PASM to get the speed they need for their projects, or just because it grabs their interest somehow, but as they know nothing much about it how could they know what to ask for?
What you are fishing for is advise from the "not so beginner anymore" who can say, "I wish someone had explained, X,Y,Z more clearly when I was starting out."
IMHO, on a project level, beginners asking for coverage of specific things isn't a bad thing. Some code examples are more appealing than others are.
Frankly, I think some of the best are tools, where the beginner can complete the project and get the understanding from it, and be empowered to do more by the product of it. A logic analyzer, or debugger, signal generator, etc... are great in this way, because it's a two-fer! Learning, and building tools is always productive. At the very least, doing things that way gets people eating their own dog food early on. Never a bad thing. This is why the "must have scope" discussion happened, for example.
The most challenging thing the beginner has in front of them is learning how to learn in a given discipline. There are generalities that are always good, but there are also specifics that can very significantly improve the process too. That's what a lot of the meta-discussion here has been about.
My own personal experience has been building tools useful to the discipline at hand is one of the better ways to reach some level of mastery.
in the following code note the two lines marked with ******************************** I am using par to look at the value of temp at two locations marked in the code.
If I move the par command to the first ******* line I can read the value of temp OK
If I move the par command to the second ******* line, the value of temp goes to 0
I did not modify temp in any of the following called routines.
WHY? does temp go to 0
Am I correct or not that in this case temp is global to this cog?
Harprit
I started learning PASM exactly 6 weeks ago to I am still very much a beginner.
I have a lot of things that I have figured out but there is much much more to go.
And I do tend to get stuck and then things get slow for me.
Before I ask a question I have to investigate the phenomenon in some detail.
Still I find your observations very interesting.
So maybe the experience programmers should provide more beginners guidance ideas
Harprit.