Re the 373 vs 374, initially I would not have expected that to work but looking at the datasheets, I think it will. With the 373, outputs follow inputs when the control is high, and it is latched when it is low. With the 374, it latches on the low to high transition. Because we set up all the pin states first, then toggle the control low to high to low, both chips will work. Cool discovery, well done!
Re running C from the sram. Well, let's say we keep group 3 really simple. It runs the touch part of the touchscreen and the interface to the extension board, and we leave out the A to D chip for the moment. Along comes a random cache access. If the touchscreen SPI /CS line goes high at a random time, will it matter? I don't have the spec sheets on that chip but maybe it won't care. It shouldn't be in the middle of a data transfer because that code is a one line call to a cog function from the C program so that shouldn't trigger a cache change during that access. And if we offload the interface to the extension board to a cog routine, then again, calling that routine will be one line of C code so a cache change is not going to happen during a prop to prop data transfer.
So I think the key is to get all the things that talk on group 3 to be running from cogs rather than from spin. Ideally from the one cog that is running the sram access because that can have a simple lock - if that is running then the cache request has to wait. That makes sense anyway because then you can use the same driver for spin and C.
So fundamentally this is about moving things from spin to cog - the set161 code, the entire routines to read where a finger is, and a block transfer to or from the second prop.
Those are quite achievable tasks.
You know, if that works, this could well be the fastest cache solution for C that anyone has done.
Re the 373 vs 374, initially I would not have expected that to work but looking at the datasheets, I think it will. With the 373, outputs follow inputs when the control is high, and it is latched when it is low. With the 374, it latches on the low to high transition. Because we set up all the pin states first, then toggle the control low to high to low, both chips will work. Cool discovery, well done!
Re running C from the sram. Well, let's say we keep group 3 really simple. It runs the touch part of the touchscreen and the interface to the extension board, and we leave out the A to D chip for the moment. Along comes a random cache access. If the touchscreen SPI /CS line goes high at a random time, will it matter? I don't have the spec sheets on that chip but maybe it won't care. It shouldn't be in the middle of a data transfer because that code is a one line call to a cog function from the C program so that shouldn't trigger a cache change during that access. And if we offload the interface to the extension board to a cog routine, then again, calling that routine will be one line of C code so a cache change is not going to happen during a prop to prop data transfer.
So I think the key is to get all the things that talk on group 3 to be running from cogs rather than from spin. Ideally from the one cog that is running the sram access because that can have a simple lock - if that is running then the cache request has to wait. That makes sense anyway because then you can use the same driver for spin and C.
So fundamentally this is about moving things from spin to cog - the set161 code, the entire routines to read where a finger is, and a block transfer to or from the second prop.
Those are quite achievable tasks.
You know, if that works, this could well be the fastest cache solution for C that anyone has done.
re: Touch Adc. It's been a while since I looked at the datasheet, but I'm fairly sure changing the /cs at a random time won't be a problem. We may need to flush on the next access but I will investigate further.
I think the big step is getting as much as possible into PASM. I've been working on set161 and having problems *mostly because I keep getting interrupted, lol* This may take a while.
As far as catch driver speed, I think this will be the fastest cache solution yet.. The 137 driver already beat others and I can see this being slightly faster.
*edit*
I've been chugging away and I think I'm getting somewhere. The Spin portion needs some work but I think the PASM driver is coming along. Still need to check the 161 is loading proper address *which requires removing chips, I'll get to that* I THINK the problem was missing "ret" causing calls to go crazy...
There were heaps of bugs - no wonder it would not work. Anyway, attached is the latest Touch object.
Now I think you have changed some of the load routine for your displays so it does the orientation correctly. Can you pls let me know what those changes are so I can put them in this object?
HAHA, ya beat me to it! I've been workin on that puppy for a while...
The only change necessary was landscape for the SSD..
else
' for origin top right and landscape mode
Displaycmd($0001,$6B3F) ' set SS and SM bit 0001 0000 landscape
Displaycmd($0011,$6838) ' landscape $1028 = original but 1038 is correct - not mirror image
not
else
' for origin top right and landscape mode
Displaycmd($0001,$6B3F) ' set SS and SM bit 0001 0000 landscape
Displaycmd($0011,$4838) ' landscape $1028 = original but 1038 is correct - not mirror image
I've been thinking about modifying so display writes can be inverted. Gives a nice background effect IMO.
I'll crack the new touch.spin open and give it a look! Glad you got it fixed, the bugs were really gettin me!
Just check that new file then because I think I already may have made that landscape fix some time back and forgotten about it. This pasm 161 code seems faster. It does trash the latch values so if you are sending data to one or to two displays might need to reset the latch. Generally right after a 161 change you then change to group 2 and that updates the latch anyway.
Some problems with one of the displays not working. I was expecting that as the new code trashes the latch output.
Sorry, spent a fair bit of time on this one and can't find the bug. The pasm version stops one of the displays working. But they are being treated the same so this should not happen. If I set all the latch outputs high at the end of a 161 update, both displays stop working and that shouldn't happen either as the next thing in the code is to select group 2. And furthermore, with a spin version of the same thing it works fine. So I think it is an accident that one display worked. I went through and modified the code quite a bit so that there are no static variables like latchvalue that are expected to stay static. In other words, every change of the latch requires a new sending of the data.
It is all very strange especially since I have been running code that deliberately sends data to both displays at once, and so either no displays or both displays should be on. I've not got any code that selects one or the other in this debugging code.
So until it is clear what is going on, the touch object has to update the 161 chips with spin code.
Just check that new file then because I think I already may have made that landscape fix some time back and forgotten about it. This pasm 161 code seems faster. It does trash the latch values so if you are sending data to one or to two displays might need to reset the latch. Generally right after a 161 change you then change to group 2 and that updates the latch anyway.
Some problems with one of the displays not working. I was expecting that as the new code trashes the latch output.
I'm having issues with the new touch.spin. The ssd isn't working *as you mentioned* I'll keep thinking about this. About the revisions, it's getting closer. I think one important part right now is getting most bus access to the driver. Should be possible.
PRI Latch_AllHighStart ' all pins high
DIRA |= %00000000_01000000_00000000_00000000 ' pin 22 always an output
OUTA &= %11111111_10111111_11111111_11111111 ' pin 22 is low
Latchbyte := %11111111
Set373Latch
This one kinda bugs me. P22 should ONLY change from cog. Makes things WAY easier. I think a better way is to start the cog, then latch from init..
init mov outa, maskPin22
mov dira, maskPin22
call #set373
done mov err, #0 ' reset err=false=good
'mov dira,zero ' tristate the pins with the cog dira
and dira,maskP0P20low ' tristates all the common pins, leaves P22 as is though
wrlong err, errptr ' status =0=false=good, else error x
wrlong zero, comptr ' command =0 (done)
' wait for a command (pause short time to reduce power)
pause
..
..
latchvalue long %00000000_00000000_00000000_11111111 ' current 373 value
fit 496
Not tested, but I had this working. Just need to send things to "done" instead of init. Still need to think about this too.'
*edit*
Okay, I've done a fair bit of work on this. There's some bugs, I think the big one is:
or dira,maskP16P20 ' set control pins P16-P20 as outputs
or outa,maskP16P20 ' set control pins high
I think it's better to pre-set pins, then make outputs.
*editsssss*
Very nice! It seems quite a bit faster. I should be able to get this version going with the cache driver... (not today)
Addit re those lines on P22, yes I agree. They are leftover from the spin version.
Deleted these two lines so now it is
PRI Latch_AllHighStart ' all pins high
Latchbyte := %11111111
Set373Latch
Tested with several programs and works fine.
re
Okay, I've done a fair bit of work on this. There's some bugs, I think the big one is:
Good thinking. I do that lots of times - set dira then outa. You are right, better to do outa first. Ok, will need to change lots of lines of code. brb
Ok, done that. Changed dira and outa order in quite a few lines. All seems to work fine.
Staring at the icon display code at the moment. Can we make it faster? Yes, I think so, because it is only the first few lines of an icon and the last few that I think get merged with the background. Most of the ones in the middle don't get merged so no need to process those.
Addit, no that didn't work out - all my icons are actually slightly smaller than 59x60 so still need the border around the outside. So the attached touch.spin is still the latest one.
addit: ok re the big port over to C, the set161 was a large part of that. So now it is just this
PUB SelectMemGroup ' select group 1 for memory transfer
spi.stop ' and stop any other cogs here too if needed
LatchGroup2
DIRA |= %00000000_01011111_11111111_11111111 ' enable these pins for output
OUTA |= %00000000_00011111_00000000_00000000 ' set /rd /wr /disp wr and 161 clock high
PUB SelectSPIGroup
LatchGroup3 ' select group 2 for spi transfers
DIRA &= %11111111_11111111_11111111_11000000 ' so no clashes with the cog using P0-P2 set these as inputs and P3-P5
TouchStart ' start the touchscreen cog
There are three things currently connected to group3, and maybe we can rationalise that?
1) The touchscreen sensor for screen 1 (pins 0-2)
2) The touchscreen sensor for screen 2 (pins 3-5)
3) The MCP302 A to D (pins 6-8)
So - maybe we move the A to D over to the extension board. In which case it is just the touchscreen sensor. I'm really hoping it won't care if it is disabled for a bit. I doubt the user would notice if there was a tiny delay touching the screen when a cache update came through. Which actually is pretty unlikely as the program is most likely in a tight loop waiting for an input on the screen.
So - perhaps you don't need to code much at all. Just disable group3, do the memory transfer and then reenable it?
And maybe you don't even need to shutdown the touch cog and restart it?
Seems much better. The landscape bug is still there. Here's the fixed version:
PUB ChangeOrientation(n) ' pass true = portrait or false = landscape, changes global variable orientation in this object
' command $0001
'8.2.4. Driver Output Control (R01h)
'R/W RS D15 D14 D13 D12 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0
'W 1 0 0 0 0 0 SM 0 SS 0 0 0 0 0 0 0 0
'SS: Select the shift direction of outputs from the source driver.
'When SS = 0, the shift direction of outputs is from S1 to S720
'When SS = 1, the shift direction of outputs is from S720 to S1.
'In addition to the shift direction, the settings for both SS and BGR bits are required to change the
'assignment of R, G, B dots to the source driver pins.
'To assign R, G, B dots to the source driver pins from S1 to S720, set SS = 0.
'To assign R, G, B dots to the source driver pins from S720 to S1, set SS = 1.
' command $0003
'R/W RS D15 D14 D13 D12 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0
'W 1 TRI DFM 0 BGR 0 0 HWM 0 ORG 0 I/D1 I/D0 AM 0 0 0
' When AM = 0, the address is updated in horizontal writing direction.
'When AM = 1, the address is updated in vertical writing direction.
'I/D[1:0] Control the address counter (AC) to automatically increase or decrease by 1 when update one pixel
' display data.
SelectMemGroup
orientation := n
if displaymode == "I" ' ili9325
if orientation
' for origin top left and portrait mode
Displaycmd($0001,%00000001_00000000) ' $0001,$0100 set SS and SM bit 0001 0100 portrait
Displaycmd($0003,%00010000_00110000) ' $0003,$1030 set GRAM write direction and BGR=1. $0003 $1030
else
' for origin top right and landscape mode
Displaycmd($0001,%00000000_00000000) ' set SS and SM bit 0001 0000 landscape
Displaycmd($0003,%00010000_00111000) ' landscape $1028 = original but 1038 is correct - not mirror image
if displaymode == "S" ' SSD1289
if orientation
' for origin top left and portrait mode
Displaycmd($0001,$2B3F) ' Entry mode portrait
Displaycmd($0011,$6070) ' '
else
' for origin top right and landscape mode
Displaycmd($0001,$6B3F) ' landscape $2B3F = original but 6B3F is correct
Displaycmd($0011,$6838) ' landscape $1028 = original but 6838 is correct
Also, a previous version had a bug due to touchscreen. Have not checked current version but on SSD, when translating from touch percent to touch pixel, x is reversed. I think the answer is to play with the settings for ADC so it's always equivalent to ILI?
Okay, I've been working on cache driver and had no luck. Maybe another set of eyes will help. I'm trying to get the "basics" working, which is read/writes. Basically, the 2 calls are "wr_cache_line" and rd_cache_line". They translate to command S and command T. I've tried about 5 different times and feeling pretty dumb.
set161 mov count, line_size ' make a copy of line_size AND.
mov ptr, hubaddr ' hubaddr = hub page address
shr vmaddr, #1 ' schematic connects SRAM A0 to A0, not A1 - jsd
or outa,maskP16P20 ' set control pins high
or dira,maskP16P20 ' set control pins P16-P20 as outputs
mov latchvalue,#%11111110 ' group 1, displays all off
call #set373 ' send out to the latch
and outa,maskP0P20low '%11111111_11100000_00000000_00000000
or dira,maskP0P20 '%00000000_00011111_11111111_11111111
or outa,vmaddr ' send out ramaddr
or outa,maskP20 ' P20 clock high
or outa,maskP19 ' p19 load high
andn outa,maskP20 ' clock low
andn outa,maskP19 ' load low
or outa,maskP20 ' clock high
or outa,maskP19 ' load high
or outa,maskP16P20 ' set control pins high
mov latchvalue,#%11111101 ' group 1, displays all off
call #set373 ' send out to the latch
set161_ret ret
set373 or outa,maskP22 ' pin 22 high
or dira,maskP22 ' and now set as an output
or dira,#%1_11111111 ' enable pins 0-7 and 8 as outputs
andn outa,maskP8 ' P8 low
and outa,maskP0P7low ' P0-P7 low
or outa,latchvalue ' send out the data
or outa,maskP8 ' P8 high, clocks out data
andn outa,maskP8 ' P8 low
andn outa,maskP22 ' pin 22 low
set373_ret ret
' ------------------ single letter commands -------------------------------------
'----------------------------------------------------------------------------------------------------
'
' wr_cache_line - write a cache line to external memory
'
' vmaddr is the external memory address to write "??can trash??
' hubaddr is the hub memory address to read "cant trash!
' line_size is the number of bytes to write "cant trash!
'
'----------------------------------------------------------------------------------------------------
wr_cache_line
' command S
pasmhubtoram
call #set161 ' get hubaddr,ramaddr,len and set control pins
or dira,maskP0P15 ' %00000000_00000000_11111111_11111111 ' data bus outputs
hubtoram_loop and outa,maskP16P31 '%11111111_11111111_00000000_00000000 ' clear for output
rdword data_16,ptr ' get the word from hub
and data_16,maskP0P15 ' mask to a word only
or outa,data_16 ' send out the byte to P0-P15
andn outa,maskP17 ' set mem write low
add ptr,#2 ' increment by 2 bytes = 1 word. Put this here for small delay while writes
or outa,maskP17 ' mem write high
andn outa,maskP20 ' clock 161 low
or outa,maskP20 ' clock 161 high
djnz count,#hubtoram_loop ' loop this many times
and dira,maskP0P20low ' tristates all the common pins, leaves P22 as is though
wr_cache_line_ret ret
'----------------------------------------------------------------------------------------------------
'
' rd_cache_line - read a cache line from external memory
'
' vmaddr is the external memory address to read
' hubaddr is the hub memory address to write
' line_size is the number of bytes to read
'
'----------------------------------------------------------------------------------------------------
rd_cache_line
' command T
pasmramtohub
call #set161 ' get hubaddr,ramaddr,len and set control pins
and dira,maskP16P31 '%11111111_11111111_00000000_00000000 inputs
andn outa,maskP16 ' memory /rd low
ramtohub_loop mov data_16,ina ' get the data
wrword data_16,ptr ' move data to hub
andn outa,maskP20 ' clock 161 low
or outa,maskP20 ' clock 161 high
add ptr,#2 ' increment the hub address
djnz count,#ramtohub_loop
or outa,maskP16 ' memory /rd high
and dira,maskP0P20low ' tristates all the common pins, leaves P22 as is though
rd_cache_line_ret ret
' variables
pasm_n long 0 ' general purpose value
data_16 long 0 ' general purpose value
' constants
maskP0P2low long %11111111_11111111_11111111_11111000 ' P0-P2 low
maskP0P20 long %00000000_00011111_11111111_11111111 ' P0-P18 enabled for output plus P19,P20
maskP0P18low long %11111111_11111000_00000000_00000000 ' P0-P18 low
maskP16 long %00000000_00000001_00000000_00000000 ' pin 16
maskP17 long %00000000_00000010_00000000_00000000 ' pin 17
maskP18 long %00000000_00000100_00000000_00000000 ' pin 18
maskP19 long %00000000_00001000_00000000_00000000 ' pin 19
maskP20 long %00000000_00010000_00000000_00000000 ' pin 20
maskP22 long %00000000_01000000_00000000_00000000 ' pin 22
maskP16P31 long %11111111_11111111_00000000_00000000 ' pin 16 to pin 31
maskP0P15 long %00000000_00000000_11111111_11111111 ' for masking words
maskP16P20 long %00000000_00011111_00000000_00000000
maskP0P20low long %11111111_11100000_00000000_00000000 ' for returning all group pins HiZ
'maskP16P17P19 long %00000000_00001011_00000000_00000000
maskP16P17P20 long %00000000_00010011_00000000_00000000
maskP0P7low long %11111111_11111111_11111111_00000000 ' P0-P7 low
maskP8 long %00000000_00000000_00000001_00000000 ' pin 8
latchvalue long %00000000_00000000_00000000_00000000 ' current 373 value
I don't know what's wrong. Not sure how to interpret test results. I'm making things WAY to difficult.... I'll zip the project and edit post in a sec...
Okay got the archive of the latest attempt. Check txt for description.
*edit*
Had some time so I pushed more into low-level drivers. I think it's a little bit faster. Still room for improvement.
set161 mov count, line_size ' make a copy of line_size AND.
mov ptr, hubaddr ' hubaddr = hub page address
shr vmaddr, #1 ' schematic connects SRAM A0 to A0, not A1 - jsd
or outa,maskP16P20 ' set control pins high
or dira,maskP16P20 ' set control pins P16-P20 as outputs
to replace this?
set161 call #get_values ' gets ramaddr = the 161 value to set
mov latchvalue,#%11111110 ' group 1, displays all off
call #set373 ' send out to the latch
First thing there is you have to call #get_values to get the 161 latch value that has been passed from spin.
Having said that, I have a confession - I have never managed to get any pasm code to ever work without first writing the entire thing in spin. I guess one can always use debugging routines but the first pasm I ever tried modifying was the Z80 emulation and that happened to have only one or two longs free so there never was room to put in debugging code. So I got used to writing in spin, so you can put in pause routines, and put in breakpoints and send values to the screen to check they are what you think they should be.
Having said that, aren't the "S" and "T" commands the ones to use? In other words, move a block to and from hub. In which case that shouldn't need hacking the 161 driver code.
If you are trying to move this Spin code into pasm
PRI SpinHubToRam(hubaddress, ramaddress, number) | i ' P0-P15 are data lines, P16 is /RD, P17 is /WR
Load161(ramaddress) ' load the 161 counters
LatchGroup2 ' group 2 is block data transfers
CogCmd("S",hubaddress,ramaddress,number) ' pasm alternative to code below
DIRA &= %11111111_11100000_00000000_00000000 ' float all pins used
Well that might require modifying something else, because it is split into two pasm calls for a good reason and that is that the current pasm driver only lets you pass three longs. But that little PUB in spin is passing four longs because it is passing the set161 value first.
If yo want to move that entire spin PUB into pasm, well that might need changing the pasm so that you can pass four variables. Nothing wrong with that, but it will mean modifying the startup routine, the get_values routine, and every spin routine that talks to that pasm code. On the upside, you can leave out the cache driver and just see if you can get an "S" routine to work with the pasm code. Change things slowly, debug often and you will know if it is broken because the "S" routine is fundamental to getting anything on the screen.
Actually it is 5 variables. Looking at that spin code, Load161 is actually
CogCmd("Z",0,address,0)
and latch373 is a few lines to setup variables and then
CogCmd("M",Latchbyte,0,0)
and then passing 3 variables for a block move.
CogCmd("S",hubaddress,ramaddress,number)
Having said all that, is there a problem splitting up the 161set and the block move into several lines of C? Actually, I have no idea how the cache driver is written. Is it in C or is it in Spin? Is there still spin hidden behind the scenes in GCC? Or is there absolutely no spin whatsoever (like Pullmoll managed with his Z80 emulation and his friendly signon message "goodbye spin").
The cache driver is entirely in PASM. That's why the whole thing needs to be done this way. I have the driver modified a bit, working and seems a bit faster. Got rid of several instructions, current PASM looks like this
DAT
'' +-----------------------------------------------------------------------------------------------+
'' | Touchblade 161 Ram Driver (with grateful acknowlegements to Cluso and Average Joe) |
'' +-----------------------------------------------------------------------------------------------+
org 0
tbp2_start ' setup the pointers to the hub command interface (saves execution time later
' +-- These instructions are overwritten as variables after start
comptr mov comptr, par ' -| hub pointer to command
hubptr mov hubptr, par ' | hub pointer to hub address
ramptr add hubptr, #4 ' | hub pointer to ram address
lenptr mov ramptr, par ' | hub pointer to length
errptr add ramptr, #8 ' | hub pointer to error status
cmd mov lenptr, par ' | command I/R/W/G/P/Q
hubaddr add lenptr, #12 ' | hub address
ramaddr mov errptr, par ' | ram address
len add errptr, #16 ' | length
err nop ' -+ error status returned (=0=false=good)
init 'set up latches here
or outa,maskP22 ' pin 22 high
or dira,maskP22 ' and now set as an output
mov dirb, #%11111111 'set latch All hi
' Initialise hardware tristates everything and read/write set the pins
done mov err, #0 ' reset err=false=good
mov latchvalue, dirb ' restore latch value
call #set373
and dira,maskP0P20low ' tristates all the common pins, leaves P22 as is though
wrlong err, errptr ' status =0=false=good, else error x
wrlong zero, comptr ' command =0 (done)
' wait for a command (pause short time to reduce power)
pause
' mov ctr, delay wz ' if =0 no pause
' if_nz add ctr, cnt
' if_nz waitcnt ctr, #0 ' wait for a short time (reduces power)
rdlong cmd, comptr wz ' command ?
if_z jmp #pause ' not yet
' decode command
cmp cmd, #"S" wz ' hub to ram
if_z jmp #pasmhubtoram
cmp cmd, #"T" wz ' ram to hub
if_z jmp #pasmramtohub
cmp cmd, #"U" wz ' ram to display
if_z jmp #pasmramtodisplay
cmp cmd, #"V" wz ' hub to display
if_z jmp #pasmhubtodisplay
cmp cmd, #"E" wz ' convert 3 byte .raw format to 2 byte .ili format - hub to hub
if_z jmp #rawtoiliformat
cmp cmd, #"F" wz ' convert 3 byte .bmp format BGR to 2 byte ili format (same as E but order reversed)
if_z jmp #bmptoiliformat
cmp cmd, #"X" wz ' merge icon and background based on a mask
if_z jmp #mergeicons
cmp cmd, #"Z" wz ' set the 161 counters
if_z jmp #extset161
cmp cmd, #"M" wz ' pass current latch value to pasm
if_z jmp #latch373value
cmp cmd, #"I" wz ' init
if_z jmp #init
mov err, cmd ' error = cmd (unknown command)
jmp #done
' ----------------- common routines -------------------------------------
get_values rdlong hubaddr, hubptr ' get hub address
rdlong ramaddr, ramptr ' get ram address
rdlong len, lenptr ' get length
mov err, #5 ' err=5
mov dirb,latchvalue ' make copy of latchvalue here for restore
or outa,maskP16P20 ' set control pins high
or dira,maskP16P20 ' set control pins P16-P20 as outputs
get_values_ret ret
stop jmp #stop ' for debugging
'delaynop nop
' nop
' nop
' nop
'delaynop_ret ret
set373 or outa,maskP22 ' pin 22 high
or dira,#%1_11111111 ' enable pins 0-7 and 8 as outputs
and outa,maskP0P8low ' P0-P8 low
or outa,latchvalue ' send out the data
or outa,maskP8 ' P8 high, clocks out data
andn outa,maskP22 ' pin 22 low
set373_ret ret
set161 mov latchvalue,#%11111110 ' group 1, displays all off
call #set373 ' send out to the latch
and outa,maskP0P20low '%11111111_11100000_00000000_00000000
or dira,maskP0P20 '%00000000_00011111_11111111_11111111
or outa,ramaddr ' send out ramaddr
or outa,maskP20 ' clock high
or outa,maskP19 ' load high
set161_ret ret
' ------------------ single letter commands -------------------------------------
' command S
pasmhubtoram call #get_values ' get hubaddr,ramaddr,len and set control pins
call #set161
or outa,maskP16P20 ' set control pins high
mov latchvalue,#%11111101 ' group 2, displays all off
call #set373 ' send out to the latch
hubtoram_loop and outa,maskP16P31 '%11111111_11111111_00000000_00000000 ' clear for output
rdword data_16,hubaddr ' get the word from hub
and data_16,maskP0P15 ' mask to a word only
or outa,data_16 ' send out the byte to P0-P15
andn outa,maskP17 ' set mem write low
add hubaddr,#2 ' increment by 2 bytes = 1 word. Put this here for small delay while writes
or outa,maskP17 ' mem write high
andn outa,maskP20 ' clock 161 low
or outa,maskP20 ' clock 161 high
djnz len,#hubtoram_loop ' loop this many times
jmp #done ' tristate pins and listen for commands
' command T
pasmramtohub call #get_values ' get hubaddr,ramaddr,len and set control pins
call #set161
or outa,maskP16P20 ' set control pins high
mov latchvalue,#%11111101 ' group 2, displays all off
call #set373 ' send out to the latch
and dira,maskP16P31 '%11111111_11111111_00000000_00000000 inputs
andn outa,maskP16 ' memory /rd low
ramtohub_loop mov data_16,ina ' get the data
wrword data_16,hubaddr ' move data to hub
andn outa,maskP20 ' clock 161 low
or outa,maskP20 ' clock 161 high
add hubaddr,#2 ' increment the hub address
djnz len,#ramtohub_loop
or outa,maskP16 ' memory /rd high
jmp #done ' ' tristate pins and listen for commands
' command U
pasmramtodisplay call #get_values ' get hubaddr,ramaddr,len (only uses len) and set control pins
call #set161
or outa,maskP16P20 ' set control pins high
mov latchvalue, dirb
call #set373
andn outa,maskP19 ' CS low
and dira,maskP16P31 ' %11111111_11111111_00000000_00000000 so prop pins 0-15 HiZ
andn outa,maskP16 ' ram /rd low
ramtodisplay_loop andn outa,maskP18 ' ILI write low
or outa,maskP18 ' ILI write high
andn outa,maskP20 ' clock 161 low
or outa,maskP20 ' clock 161 high
djnz len,#ramtodisplay_loop
or outa,maskP16 ' mem /rd high
or outa,maskP19 ' CS high
jmp #done
' command V
pasmhubtodisplay call #get_values ' get hubaddr,ramaddr,len and set control pins high
andn outa,maskP19 ' CS low
or dira,maskP0P15 '%00000000_00000000_11111111_11111111
hubtodisplay_loop and outa,maskP16P31 '%11111111_11111111_00000000_00000000 ' clear for output
rdword data_16,hubaddr ' get the word from hub
and data_16,maskP0P15 ' mask to a word only
or outa,data_16 ' send out the byte to P0-P15
andn outa,maskP18 ' ILI write low
or outa,maskP18 ' ILI write high
add hubaddr,#2 ' one word
djnz len,#hubtodisplay_loop
or outa,maskP19 ' CS high
jmp #done
'command E
RawtoILIformat ' takes a .raw 3 byte RRRRRRRR GGGGGGGG BBBBBBBB and converts to 2 byte RRRRRGGG GGGBBBBB
' pass hubaddr, ramaddr and len
' hubaddr is source location, len is number of pixels
' ramaddr is destination in hub (messy naming) and length is 2/3 of blocklength
call #get_values ' gets hubaddress, ramaddress and len (ignores ramaddress)
rawloop
rdbyte red,hubaddr
add hubaddr,#1
rdbyte green,hubaddr
add hubaddr,#1
rdbyte blue,hubaddr
add hubaddr,#1
call #rgbtoili
wrbyte ililow,ramaddr
add ramaddr,#1
wrbyte ilihigh,ramaddr
add ramaddr,#1
djnz len,#rawloop ' loop until done
jmp #done ' set pins to tristate
RGBtoILI ' pass red,green, blue, returns ililow and ilihigh
shr red,#3 ' 000RRRRR
shl red,#3 ' RRRRR000
shr green,#2 ' 00GGGGGG
mov ilihigh,green ' ilihigh = 00GGGGGG
shr ilihigh,#3 ' ilihigh = 00000GGG
or ilihigh,red ' ilihigh = RRRRRGGG
and green,#%00000111 ' 00000GGG
shl green,#5 ' GGG00000
mov ililow,green ' ililow = GGG00000
shr blue,#3 ' blue = 000BBBBB
or ililow,blue ' ililow = GGGBBBBB
RGBtoILI_ret ret
BMPtoILIformat ' takes a .bmp 3 byte BBBBBBBB GGGGGGGG RRRRRRRR and converts to 2 byte RRRRRGGG GGGBBBBB
' same as E above but BGR instead of RGB
' pass hubaddr, ramaddr and len
' hubaddr is source location, len is number of pixels
' ramaddr is destination in hub (messy naming) and length is 2/3 of blocklength
call #get_values ' gets hubaddress, ramaddress and len (ignores ramaddress)
bmploop
rdbyte blue,hubaddr
add hubaddr,#1
rdbyte green,hubaddr
add hubaddr,#1
rdbyte red,hubaddr
add hubaddr,#1
call #rgbtoili
wrbyte ililow,ramaddr
add ramaddr,#1
wrbyte ilihigh,ramaddr
add ramaddr,#1
djnz len,#bmploop ' loop until done
jmp #done ' set pins to tristate
' **** command X *********************
MergeIcons call #get_values ' gets hubaddress, ramaddress,len which are used here as background,icon,mask
mov pasm_n,#59 ' do a single row
mergeiconsloop rdbyte ililow,len ' reuse ililow, so this is rdword mask,maskcounter
and ililow,#%11111 ' mask off low 5 bits and use just the blue as this is a grayscale bitmap
rdword red,hubaddr ' reuse red, so actually this is rdword background,backgroundcounter
cmp ililow,#%10000 wc ' compare if >128 (ie mid level gray)
if_c jmp #mergeskip
rdword green,ramaddr ' reuse green, so this is rdword iconpixel, iconpixelcounter
wrword green,hubaddr ' if replace, then move icon pixel to the background
mergeskip add hubaddr,#2
add ramaddr,#2
add len,#2
djnz pasm_n,#mergeiconsloop ' loop until done
jmp #done 'set pins to tristate
'' *** command Z **********
'changes the latch so displays won't work unless resend the spin value for the latch
extset161 call #get_values ' gets ramaddr = the 161 value to set
mov dirb, latchvalue
call #set161
jmp #done ' tristates pins etc
' *** command M - pass current latch value and send out
latch373value call #get_values
mov dirb,hubaddr
jmp #done
' variables
pasm_n long 0 ' general purpose value
data_16 long 0 ' general purpose value
ililow long 0 ' low data byte
ilihigh long 0 ' high data byte
red long 0 ' red, green blue variables
green long 0
blue long 0
' constants
Zero long %00000000_00000000_00000000_00000000 ' used in several places
'maskP0P2low long %11111111_11111111_11111111_11111000 ' P0-P2 low
maskP0P20 long %00000000_00011111_11111111_11111111 ' P0-P18 enabled for output plus P19,P20
maskP0P18low long %11111111_11111000_00000000_00000000 ' P0-P18 low
maskP16 long %00000000_00000001_00000000_00000000 ' pin 16
maskP17 long %00000000_00000010_00000000_00000000 ' pin 17
maskP18 long %00000000_00000100_00000000_00000000 ' pin 18
maskP19 long %00000000_00001000_00000000_00000000 ' pin 19
maskP20 long %00000000_00010000_00000000_00000000 ' pin 20
maskP22 long %00000000_01000000_00000000_00000000 ' pin 22
maskP16P31 long %11111111_11111111_00000000_00000000 ' pin 16 to pin 31
maskP0P15 long %00000000_00000000_11111111_11111111 ' for masking words
maskP16P20 long %00000000_00011111_00000000_00000000
maskP0P20low long %11111111_11100000_00000000_00000000 ' for returning all group pins HiZ
'maskP16P17P20 long %00000000_00010011_00000000_00000000
maskP0P8low long %11111111_11111111_11111111_00000000 ' P0-P7 low
maskP8 long %00000000_00000000_00000001_00000000 ' pin 8
latchvalue long %00000000_00000000_00000000_00000000 ' current 373 value
fit 496
Note that when the function is done, we jmp to done, not init. Init is used to latch all high, and get control of P22 *permanently*
This revised code has the 161load and set latches integrated into the command. Works flawlessly. I think there's either something wrong with the hardware, or I have a messed up test file? Something funky...
The reason I was unrolling things was trying to determine what the problem was.
set161 mov count, line_size ' make a copy of line_size AND.
mov ptr, hubaddr ' hubaddr = hub page address
shr vmaddr, #1 ' schematic connects SRAM A0 to A0, not A1 - jsd
or outa,maskP22 ' pin 22 high
or dira,maskP22 ' and now set as an output
or outa,maskP16P20 ' set control pins high
or dira,maskP16P20 ' set control pins P16-P20 as outputs
mov count, line_size ' make a copy of line_size AND.
mov ptr, hubaddr ' hubaddr = hub page address
shr vmaddr, #1 ' schematic connects SRAM A0 to A0, not A1 - jsd
mov dirb,latchvalue ' make copy of latchvalue here for restore
or outa,maskP16P20 ' set control pins high
or dira,maskP16P20 ' set control pins P16-P20 as outputs
mov latchvalue,#%11111110 ' group 1, displays all off
call #set373 ' send out to the latch
and outa,maskP0P20low '%11111111_11100000_00000000_00000000
or dira,maskP0P20 '%00000000_00011111_11111111_11111111
or outa,vmaddr ' send out ramaddr
or outa,maskP20 ' clock high
or outa,maskP19 ' load high
set161_ret ret
' ----------------- common routines -------------------------------------
release_bus mov latchvalue, #%11111111 ' clearlatch
call #set373
and dira,maskP0P20low ' tristates all the common pins, leaves P22 as is though
release_bus_ret ret
set373 or outa,maskP22 ' pin 22 high
'or dira,maskP22 ' and now set as an output
or dira,#%1_11111111 ' enable pins 0-7 and 8 as outputs
and outa,maskP0P8low ' P0-P8 low
or outa,latchvalue ' send out the data
or outa,maskP8 ' P8 high, clocks out data
andn outa,maskP22 ' pin 22 low
set373_ret ret
'----------------------------------------------------------------------------------------------------
'
' rd_cache_line - read a cache line from external memory
'
' vmaddr is the external memory address to read
' hubaddr is the hub memory address to write
' line_size is the number of bytes to read
'
'----------------------------------------------------------------------------------------------------
rd_cache_line
' command T
pasmramtohub call #set161
or outa,maskP16P20 ' set control pins high
mov latchvalue,#%11111101 ' group 2, displays all off
call #set373 ' send out to the latch
and dira,maskP16P31 '%11111111_11111111_00000000_00000000 inputs
andn outa,maskP16 ' memory /rd low
ramtohub_loop mov data_16,ina ' get the data
wrword data_16,ptr ' move data to hub
andn outa,maskP20 ' clock 161 low
or outa,maskP20 ' clock 161 high
add ptr,#2 ' increment the hub address
djnz count,#ramtohub_loop
or outa,maskP16 ' memory /rd high
call #release_bus
rd_cache_line_ret
ret
'----------------------------------------------------------------------------------------------------
'
' wr_cache_line - write a cache line to external memory
'
' vmaddr is the external memory address to write
' hubaddr is the hub memory address to read
' line_size is the number of bytes to write
'
'----------------------------------------------------------------------------------------------------
wr_cache_line
' command S
pasmhubtoram call #set161
or outa,maskP16P20 ' set control pins high
mov latchvalue,#%11111101 ' group 2, displays all off
call #set373 ' send out to the latch
hubtoram_loop and outa,maskP16P31 '%11111111_11111111_00000000_00000000 ' clear for output
rdword data_16,ptr ' get the word from hub
and data_16,maskP0P15 ' mask to a word only
or outa,data_16 ' send out the byte to P0-P15
andn outa,maskP17 ' set mem write low
add ptr,#2 ' increment by 2 bytes = 1 word. Put this here for small delay while writes
or outa,maskP17 ' mem write high
andn outa,maskP20 ' clock 161 low
or outa,maskP20 ' clock 161 high
djnz count,#hubtoram_loop ' loop this many times
call #release_bus
wr_cache_line_ret
ret
' variables
pasm_n long 0 ' general purpose value
data_16 long 0 ' general purpose value
' constants
'Zero long %00000000_00000000_00000000_00000000 ' used in several places
'maskP0P2low long %11111111_11111111_11111111_11111000 ' P0-P2 low
maskP0P20 long %00000000_00011111_11111111_11111111 ' P0-P18 enabled for output plus P19,P20
maskP0P18low long %11111111_11111000_00000000_00000000 ' P0-P18 low
maskP16 long %00000000_00000001_00000000_00000000 ' pin 16
maskP17 long %00000000_00000010_00000000_00000000 ' pin 17
'maskP18 long %00000000_00000100_00000000_00000000 ' pin 18
maskP19 long %00000000_00001000_00000000_00000000 ' pin 19
maskP20 long %00000000_00010000_00000000_00000000 ' pin 20
maskP22 long %00000000_01000000_00000000_00000000 ' pin 22
maskP16P31 long %11111111_11111111_00000000_00000000 ' pin 16 to pin 31
maskP0P15 long %00000000_00000000_11111111_11111111 ' for masking words
maskP16P20 long %00000000_00011111_00000000_00000000
maskP0P20low long %11111111_11100000_00000000_00000000 ' for returning all group pins HiZ
'maskP16P17P20 long %00000000_00010011_00000000_00000000
maskP0P8low long %11111111_11111111_11111111_00000000 ' P0-P7 low
maskP8 long %00000000_00000000_00000001_00000000 ' pin 8
latchvalue long %00000000_00000000_00000000_00000000 ' current 373 value
This is from the modified PASM, known working... I need to take a break from things. Posting updated Touch.spin, should be backward compatible and has some tuning for speed.
if it is all in pasm, how are you passing five variables? (161 value, latchvalue, source, destination, number of bytes).
It can be done but that will need different pasm driver code.
No, wait, only 4, because an "S" does not use destination, because that is the 161 value. brb
I need to think big picture here. Ok, an S command sets the 161, then sets the latch then does the block move. The latch is always latch2, but there could be an advantage in pasm knowing what the current latch value is and then setting pins. That way, if you are running the left screen or the right screen, pasm keeps it the same.
So we need one more variable passed to pasm - latchvalue
The current touch.spin saves the latchvalue in dirb, then restores after jumping to done. The 161value will always be ramaddress, so just load 161 with ramaddress from command. Passing is
cmd("S",@rambuffer,ramaddress,count)
It actually works quite well, pass the latch value in *ie display, rs, etc.* then send the data!
There's actually a bit of tuning that can be done. But, I dumped the working code into the cache driver, and poof, no work. Could be faulty hardware I guess. Not really sure..
*edit*
So I tricked you. In get_values
mov dirb,latchvalue ' make copy of latchvalue here for restore
pasmramtodisplay call #get_values ' get hubaddr,ramaddr,len (only uses len) and set control pins
call #set161
or outa,maskP16P20 ' set control pins high
mov latchvalue, dirb
call #set373
Dirb is cunning. I'd probably have just added another variable to this
tbp2_start ' setup the pointers to the hub command interface (saves execution time later
' +-- These instructions are overwritten as variables after start
comptr mov comptr, par ' -| hub pointer to command
hubptr mov hubptr, par ' | hub pointer to hub address
ramptr add hubptr, #4 ' | hub pointer to ram address
lenptr mov ramptr, par ' | hub pointer to length
errptr add ramptr, #8 ' | hub pointer to error status
cmd mov lenptr, par ' | command I/R/W/G/P/Q
hubaddr add lenptr, #12 ' | hub address
ramaddr mov errptr, par ' | ram address
len add errptr, #16 ' | length
err nop ' -+ error status returned (=0=false=good)
We need a pure pasm block move in one call. Will that work for the cache?
Actually, how is the cache going to handle the latch? That shouldn't be part of the cache driver. So we do need a separate latch pasm call. So it is back to the current 3 variables.
What we need is a "store latch" and a "fetch latch" routine in pasm. No latch value in spin. If you want to know the current latch status, do a pasm call. Then the C code can set up the latch under programmer control, but any pasm calls know what the latch is, can temporarily store it, modify the latch, then restore the value.
It's a bit complicated, but it seems to work in place. Cog always controls P22. Pass latch value, then do command. Latch value is restored when needed.
latch command becomes this
The cache driver is complicated. I want to include much of our current PASM into the driver, with extended function set. If the cache drive is also the ramdriver, it should make things less complicated since we just save the latch value before a cache hit, then restore it when done with the cache. Need to get the read/write working first.
current PASM is 215 longs, plenty small enough to fit in with the cache driver IMO.
Have added xmodem for file transfers. This adds a serial port and reuses the propeller download port for transferring data. Xmodem transfers from a terminal program (eg hyperterminal) are a little tedious opening and closing programs, but they work very well from an IDE, eg an IDE that compiles/dowloads/runs C programs.
We can do similar things for Spin as there are command line spin compilers. Design your icon in such a program, design your desktop layout, save this as an .ini file, then transfer the file with xmodem. Programs are compiled binary files sitting on an SD card. In Kyedos, such programs have an .exe extension, and to find out what programs are available you type "DIR *.EXE" and then look at the list eg MYPROG.EXE and then type "MYPROG" and it reboots the propeller chip and runs that program.
On a touchscreen it is a bit different - you would associate the file with an icon, define where the icon is on the screen, and then when you touch that part of the screen it reboots the propeller and runs that program.
xmodem transfer in action
I have a really neat set of files for KERMIT transfers using java a few years ago. Paid a bunch of money for them. If you'd like to port KERMIT to the prop I think it'd be a great thing.
I'm quite excited to be working on this. There are other implementations out there, but they have limitations. This is the FASTEST memory solution for the propeller IMO. We are close to pushing screen writes as fast as the display can handle. It's a bit hardware intensive, but having the display refresh instantly is very nice. Loading from SD to ram is the slow part, but that can be hidden at the beginning of the app. 1meg is quite a bit of memory, and the coolest thing is what can be done with the string functions in the driver.. Plus the user application look fairly simple. This draws the splash screen, background and some text. Waits for user to touch screen, then re-draws the background
DAT
sysfont byte "times20.ifn"
logo byte "logo.bmp"
SysMsg1 byte "!!!New & Improved!!!",0
SysMsg2 byte "Created by James Moxham & Joe Heinz",0
CON
_clkmode = xtal1 + pll16x ' use crystal x 16
_xinfreq = 5_000_000
OBJ
tch: "Touch" ' touchscreen driver
VAR
byte Slide1,Slide2,Slide3,Slide4,Slide5,Slide6,ButtonStates
PUB Main | wcboot,xval,yval,n,gs,gsold ' warm or cold boot '
wcboot := tch.BeginDesktop("S") ' "I" = ILI9325, "S"= SSD1289
tch.SDBMPtoRam(@logo) ' file number 1, a 240x320 file called logo.bmp
tch.SDBMPtoRam(string("mask.bmp")) ' file 2, load this here - if load after the strings get a white line on the icons
tch.LoadFont(@sysfont) 'file 3
tch.SetOrientation(false)
tch.DrawBMPRam(1,0,0) ' file number 1, location 0,0\
tch.SetCursor(0,5)
tch.TextT(3,@SysMsg1)
tch.SetCursor(0,220)
tch.TextT(3,@SysMsg2)
n:=0
tch.SelectSPIGroup ' talk to the spi touchscreen
tch.pause1ms(500)
repeat while n == 0
yval := tch.TouchYPercent ' decode yval 0-100%
xval := tch.TouchXPercent ' decode xval 0-100%
if (xval <> 255) and (yval <> 255) ' valid keypress
n := 1
tch.SelectMemGroup
tch.DrawBMPRam(1,0,0) ' file number 1, location 0,0
Takes about 3 seconds from cold boot.
*edit*
made more changes, pushed the Set161(0) from PUB Begin to init in PASM
init 'set up latches here
or outa,maskP22 ' pin 22 high
or dira,maskP22 ' and now set as an output
call #get_values
mov ramaddr, #0
call #set161
mov dirb, #%11111111 'set latch All hi
' Initialise hardware tristates everything and read/write set the pins
done mov err, #0 ' reset err=false=good
mov latchvalue, dirb ' restore latch value
call #set373
and dira,maskP0P20low ' tristates all the common pins, leaves P22 as is though
wrlong err, errptr ' status =0=false=good, else error x
wrlong zero, comptr ' command =0 (done)
' wait for a command (pause short time to reduce power)
Setting 161 to zero at the beginning isn't needed so that can be commented out (it was for debugging)
I've been thinking about the latches. Get rid of the variable in Spin and just have a variable in pasm. I've replaced the command to set the latch with two other commands - "O" to logic OR the pasm value with the value you send, and "A" to logic AND the value. So that means that pasm routines can modify the latch value if they want to and I think you need that for moving the S ant T commands into pasm.
So attached is working code. The next thing to get working is to comment out the first two lines in PRI SpinHubToRam() and replace then with the commented out lines in pasm in command S.
Only thing is... it doesn't work. I tried adding in the dira and outa commands as the program enters and exits the pasm routines. There may be one I have missed though.
I think it might be the group change rather than the 161, trying to split it into two parts for debugging.
Moving all the S and T commands into Pasm is a fundamental part of the cache driver. It should be possible to debug this code and see if the display works first before looking at the cache driver. Need to do things one at a time.
Tried adding a breakpoint with jmp #stop in pasm and pins seem to be in the correct states. Tricky.
And yes, this whole thing is a pile of fun!
[code]
CON Touch
'' ILI9325 driver using the Touchblade161 design for faster ram to display transfers
'' Average Joe and James Moxham 2012
Margin = 4 ' some characters have xoffset of -1 or -2 eg v, and won't display on the extreme left edge
_1ms = 1_000_000 / 1_000 ' Divisor for 1 ms
' touchscreen pins
Touch_Clock = 0
'Touch_ChipSelect = done with a group select
Touch_DataIn = 1
Touch_DataOut = 2' touchscreen pins
VAR
byte FileNumber ' 0 to 31
byte DisplayMode ' type of display 0 = ILI9325, 1= SSD1289, 2 etc
word ScreenWidth ' either 240 in portrait or 320 in landscape
word ScreenHeight ' 320 in portrait or 240 in landscape
long orientation ' true is portrait
long curx, cury
byte FontHeight ' size of the current loaded font (pixels = fontsize *.75)
word BackFontColor ' the background color in RRRRRGGG GGGBBBBB format
byte Latch ' current latch output
long StringAddress ' start of string address
long wcboot ' most recent boot warm or cold '
OBJ
spi: "SPI_ASM" ' thanks to Beau - spi driver for touch screen
fat: "SD-MMC_FATEngine.spin" ' Kye's latest sd driver and no RTC
DAT
RamLocation long $0[31] ' 32 file ram locatinos
RamSize long $0[31] ' File sizes in words - NOT bytes (divide byte by 2)
sdbuffer byte $0[1024] ' 1024 byte buffer for sd card interface and also for sending one row 320 x3 = 960 bytes max picture size
buffer2 byte $0[512] ' 512 general purpose hub buffer
rambuffer word $0[256] ' 512 byte (256 word) buffer easier for using with words
PUB BeginDesktop(DisplayType) ' store the type of display once by the desktop program
DisplayMode := DisplayType ' global variable I = ILI9325, S=SSD1289
Begin(true) ' start up everything running the desktop
result := wcboot
PUB BeginProgram ' used by all programs except the desktop program
Begin(false) ' any program except the desktop
result := wcboot
PRI Begin(Desktop) |n ' desktop true if run from the desktop program, false for other programs
start_ram_cog ' * start cog first *start the external ram / display driver in a cog, needed for many routines so always first
Latch_AllHighStart ' all Latch pins high and enables pin 22
'Load161(0) ' reset all the 161 counters to zero
if desktop == false
DisplayMode := GetDisplayMode ' reads "I" or "S" etc off the ram ** must have run desktop at least once first to store this ***
'DisplayMode := "I" ' temp for debugging, using the ILI9325
SelectMemGroup ' select group2
Latch_DisplayBoth ' set Latch for both hand display so intialises both (one may not be present but does not matter)
if displaymode == "I"
Start_ILI9325 ' start the display 0 = ILI9325, 1 = SSD1289
if displaymode == "S"
Start_SSD1289
wcboot := WarmColdBoot ' get the last warm or cold boot value
SetOrientation(true) ' in portrait (true) or landscape mode (false), must be before clearscreen otherwise don't know screensize
if wcboot == false and Desktop == true ' no need to redisplay every time if worked the first time
Text(string("SD")) ' string to send, uses internal prop font so can see something if the SD fails to mount
fat.fatEngineStart( _dopin, _clkpin, _dipin, _cspin, _wppin, _cdpin, _rtcres1, _rtcres2, _rtcres3)
fat.mountPartition(0) ' mount the sd card
if desktop == false ' running a program, this is called without knowing what the ramlocation and ramsize values are
ManualValues ' coming from outside so reset ramsize and ramlocation values for 0 and 1
SetCursor(0,0) ' cursor for debugging to 0,0
if desktop == false
SelectSPIGroup ' selects this group and starts the cog
'PUB BasicTesting
' text(string("Basic testing"))
' sdbuffer[0] := 6
' SpinHubToRam(@sdbuffer,5000,10) ' move data to ram
' sdbuffer[0] := 7 ' change the value
' SpinRamToHub(@sdbuffer,5000,10) ' read back the original value
' hex(sdbuffer[0],2)
' 'Drawline(100,100,110,100,$FFFF)
' Clearrectangle(120,120,129,129,$FFFF)
' Draw(100,100,109,109)
' repeat 100
' pixel(%11111000_00000000)
' ************** string routines stored in external memory so can have arrays and large text files **********************
' to make things simpler, one array StringArray. any number of entries, each up to 128 characters
' pass the source and destination numbers
' TextArray(256) = 256 entries for text files etc
' first entry is destination, second entrie(s) are the source
' Commands are
' StringDim ' reserves memory 256*128, increments filenumber, adds filesize
' StringLoadFile(Name,S) ' read a .txt file off the SD card and stores in TextArray, new string entry is crlf
' StringSaveFile(Name,S,F) ' save .txt file from TextArray to SD card adding in crlf, Start to Finish
' StringClear(Dest) ' clear a string, pass string number
' StringAddChar(Dest,N) ' adds a character to a string at end
' StringSubChar(Dest) ' remove a character from the end
' StringCopy(Dest,Src) ' copy string from one location to another
' StringStore(Dest,Src) ' convert Prop string("mystring") to string stored in external memory
' StringLeft(Dest,Src,N) ' Basic Left$
' StringMid(Dest,Src,L,N) ' Basic Mid$
' StringRight ' Basic Right$
' StringInstr ' Basic Instr
' StringUcase(Dest,Src) ' Basic Ucase$
' StringLcase ' Basic Lcase$
' StringLen ' Length
' StringStr ' string representation of a number
' StringHex(Dest,N) ' number to hex string
' StringBin ' number to binary string
' StringVal ' value (hex is 0x, binary is 0b)
' StringJoin(Dest,Src1,Src2) ' same as Basic + with strings
' StringInsert(Dest,Src1,Src2,n) ' inserts src2 into src1 starting at position n, (1 is first char) and moves to dest
' StringDebug(n) ' print using the green propeller font string number n
' StringLen() ' same as strsize but works on external memory strings
' StringRambuffer ' moves string to rambuffer array and returns result = location of this array
' StringCompare(Source1,Source2) ' returns true if strings equal in length and characters
PUB StringTest ' test all the string routines
'StringDim(10)
'StringStore(5,string("Hello World"))
'StringDebug(5)
'StringClear(5)
'StringAddChar(5,"1")
'StringAddChar(5,"2")
'StringAddChar(5,"3")
'StringAddChar(5,"4")
'StringDebug(5)
'StringSubChar(5) ' remove one character from the right, useful for backspace etc
'StringDebug(5)
'StringCopy(7,5) ' copy
'StringDebug(7)
'StringLeft(7,7,5) ' source and destination the same or different
'StringDebug(7)
'StringMid(8,5,7,3) ' dest, source, starting byte, number of bytes. first byte is 1
'StringDebug(8)
'StringRight(3,5,4) ' dest, source, number of bytes
'StringDebug(3)
'Hex(StringInstr(5,2,"o"),2) ' find this character starting at 2
'StringBin(2,255) ' convert a number to binary always 32 + 2 characters
'StringDebug(2)
'StringHex(4,100) ' always 8 + 2 characters
'StringDebug(4)
'StringDec(9,-20) ' always 11 characters
'StringDebug(9)
'StringStr(9,-7) ' Basic str$ removes leading zeroes, and first character is either - or a space
'StringDebug(9)
'StringStore(5,string("65")) ' test string number to number
'hex(StringVal(5),8)
'StringStore(5,string("0x1234")) ' test string hex to number
'hex(StringVal(5),8)
'StringStore(5,string("0b101")) ' test string binary to number
'hex(StringVal(5),8)
'StringStore(1,string("Hello "))
'StringStore(2,string("World"))
'StringJoin(1,1,2) ' string1 = string1 +string2 (shows dest and a source can be the same - or could be different)
'StringDebug(1)
'StringUcase(1,1) ' Ucase test
'StringDebug(1)
'StringLcase(1,1) ' Lcase test
'StringDebug(1)
'StringStore(1,string(" 123")) ' test the trim function
'StringTrim(1,1)
'StringDebug(1)
'StringStore(1,string("TEST6.TXT")) ' file name
'StringStore(2,string("Line a of text")) ' store some lines of text
'StringStore(3,string("Line b of text"))
'StringStore(4,string("Line c of text"))
'StringSavefile(1,2,4) ' filename is 1, store lines 2 to 4
'StringStore(1,string("TEST6.TXT")) ' file name
'StringLoadFile(1,2) ' read back the file created above.
'Stringdebug(2)
'Stringdebug(3)
'Stringdebug(4)
'repeat
PUB StringDim(n) ' reserve space for a string array, n= number of strings, 128 max bytes per string
Ramsize[Filenumber] := n << 6 ' * 64 as this is in words, not bytes
StringAddress := NextLocation ' get the location of the string array
Filenumber += 1
PUB StringStore(StringN,Source) ' store a propeller string("test") to array number Dest
bytemove(@rambuffer, Source, (strsize(Source) + 1)) ' move to rambuffer array
StringToRam(StringN)
PUB StringDebug(StringN) ' print using the green propeller font - useful if sd card not working so no fonts loaded
RamToString(StringN)
Text(@rambuffer) ' print it out
PUB StringClear(StringN)
bytefill(@rambuffer,0,128) ' only really need to do the first one but may help debugging to completely clear the string
StringToRam(StringN)
PUB StringAddChar(StringN,c) | s ' add c to the string
if c >31 and c <127 ' a character that can be displayed
RamToString(StringN) ' get the string
s := strsize(@rambuffer)
byte[@rambuffer] := c
byte[@rambuffer][s + 1] := 0 ' add in the new zero (can't assume array is cleared)
StringToRam(StringN) ' store back to ram
PUB StringSubChar(StringN) ' remove one character from the right same as strings.left(strings.len - 1)
RamToString(StringN)
if strsize(@rambuffer) <> 0 ' if at least one character
byte[@rambuffer][strsize(@rambuffer)-1] :=0
StringToRam(StringN)
PUB StringCopy(Dest,Source) ' copy string from source to dest
RamToString(Source)
StringToRam(Dest)
PUB StringLeft(Dest,Source,n) ' Basic Left$
RamToString(Source)
byte[@rambuffer][n] := 0 ' move the terminator
StringToRam(Dest) ' move back to ram
PUB StringMid(Dest,Source,start,n) | i ' Basic Mid$
RamToString(Source)
repeat i from 0 to n-1
byte[@rambuffer] := byte[@rambuffer][i+start-1] ' possibly could use bytemove here
byte[@rambuffer][n] := 0 'add in the new zero terminator
StringToRam(Dest)
PUB StringRight(Dest,Source,n) | i,m
RamToString(Source)
m := strsize(@rambuffer) - n
repeat i from 0 to n ' Basic Right$. include the terminator
byte[@rambuffer] := byte[@rambuffer][i+m]
StringToRam(Dest)
PUB StringInstr(Source,Start,c) | i ' find c in the string starting at start
RamToString(Source)
result := 0 ' return 0 if not found
repeat i from (start-1) to strsize(@rambuffer)
if byte[@rambuffer] == c
result := i+1 ' 1 is first character
quit
PUB StringBin(Dest,n) ' convert n to a binary value at Dest. Prefix 0b
repeat result from 33 to 2
byte[@rambuffer][result] := ((n & 1) + "0")
n >>= 1
byte[@rambuffer][34] := 0 ' add zero terminator
byte[@rambuffer][0] := "0" ' prefix 0b same as some versions of C
byte[@rambuffer][1] := "b"
StringToRam(Dest) ' store
PUB StringHex(Dest,n) ' number to hex, always 8 characters
repeat result from 9 to 2
byte[@rambuffer][result] := lookupz((n & $F): "0".."9", "A".."F")
n >>= 4
byte[@rambuffer][10] := 0 ' zero at end
byte[@rambuffer][0] := "0" ' prefix 0x same as C
byte[@rambuffer][1] := "x"
StringToRam(Dest)
PUB StringDec(Dest,n) | sign ' number to decimal string, always 11 characters
sign := "+"
if(n < 0)
sign := "-"
if(n == negx)
bytemove(@rambuffer, string("-2147483648"), 11) ' max negative number special case
else
repeat result from 10 to 1
byte[@rambuffer][result] := ((||(n // 10)) + "0")
n /= 10
byte[@rambuffer][0] := sign ' sign at the front
byte[@rambuffer][11] := 0 ' zero terminator
StringToRam(Dest)
PUB StringStr(Dest,n) | found ' same as Basic STR - similar to Dec above but sign is either space or negative, and leading zeros removed
StringDec(Dest,n) ' number preserved in rambuffer
found := 10 ' if answer was zero don't remove leading zero
repeat result from 1 to 10
if byte[@rambuffer][result] <> "0"
found := result
quit
repeat result from 1 to 11
byte[@rambuffer][result] := byte[@rambuffer][result +found -1] ' shuffle over if needed
if byte[@rambuffer][0] == "+"
byte[@rambuffer][0] := " " ' Basic syntax, - or blank
StringToRam(Dest) ' resend to external ram
PUB StringVal(Source) | sign,i,k ' returns value of the string, hex 0x, binary 0b, decimal + or - or space or no leading character just a number
RamToString(Source)
if byte[@rambuffer][1] == "x" ' this is a hex number
repeat k from 2 to strsize(@rambuffer) -1 ' leading two characters always 0x
result := ((result <- 4) + (byte[@rambuffer][k] & $F))
return
if byte[@rambuffer][1] == "b" ' this is a binary number
repeat k from 2 to strsize(@rambuffer) -1 ' leading two characters always 0b
result := ((result << 1) + (byte[@rambuffer][k] & 1))
return
' else if got here this must be a decimal number. It could start with a number(no leading sign), or a space, or a + or a -
sign := byte[@rambuffer][0]
i := 0 ' start location
if sign == "+" or sign == "-" or sign == " " ' this is a decimal number ** must start with a space or a + or a -**
i := 1 ' leading character so start here
repeat k from i to (strsize(@rambuffer)-1) ' if ever use right to left, don't use step -1, spin puts it in if start is less than finish and if there is a step -1 it is one less loop
result := ((result *10) + byte[@rambuffer][k] & $F) 'very clever, the & $F works because zero is hex0x30, the pre multiply shifts previous numbers to the left so can do left to right. Easier to understand with Kye's binary converter with the left shift
if sign == "-"
result *= -1 ' if negative then negate
PUB StringJoin(Dest,Source1,Source2) ' Dest$= Source1$+Source2$
RamToString(Source2)
bytemove(@buffer2,@rambuffer,128) ' store string2 to buffer2
RamToString(Source1) ' get first string
bytemove(@rambuffer+strsize(@rambuffer),@buffer2,strsize(@buffer2)+1) ' thanks to Kye for this one
StringToRam(Dest) ' store back to ram
PUB StringUcase(Dest,Source)
RamToString(Source)
repeat result from 0 to strsize(@rambuffer)
if ((byte[@rambuffer][result] => "a") and (byte[@rambuffer][result] =< "z"))
byte[@rambuffer][result] -= 32
StringToRam(Dest)
PUB StringLcase(Dest,Source)
RamToString(Source)
repeat result from 0 to strsize(@rambuffer)
if ((byte[@rambuffer][result] => "A") and (byte[@rambuffer][result] =< "Z"))
byte[@rambuffer][result] += 32
StringToRam(Dest)
PUB StringTrim(Dest,Source)
RamToString(Source)
if byte[@rambuffer][0] == "+" or byte[@rambuffer][0] == " " or byte[@rambuffer][0] == "-"
repeat result from 0 to strsize(@rambuffer)
byte[@rambuffer][result] := byte[@rambuffer][result+1] ' delete the first +,- or space
StringToRam(Dest)
PUB StringSaveFile(Filen,Start,Finish) | i,j ' filen might be string array 0, start could be 1, finish could be 4
RamToString(Filen) ' fetch the filename
fat.newfile(@rambuffer) ' create the file, if not working use \fat. for debugging, see PUB openfileread for an example
result := \fat.openfile(@rambuffer,"W") ' open for writing return error if there is one
repeat i from Start to Finish
RamToString(i)
j := strsize(@rambuffer)
byte[@rambuffer][j] := 13 ' carriage return
byte[@rambuffer][j+1] :=10 ' line feed
byte[@rambuffer][j+2] := 0 ' end of new line
fat.writedata(@rambuffer,j+2) ' write this line
FileClose ' close the file
PUB StringLoadFile(Filen,Stringnumber)| s,r,c,t,z ' read filename, new line every crlf. Must have enough space reserved in the string array
RamToString(Filen) ' copy a file from the SD card to the ram disk
OpenFileRead(@rambuffer)
s := 0 ' sd counter
r := 0 ' rambuffer counter
t := 0 ' total number of bytes
z := fat.filesize ' size of the file
repeat (z >> 9) +1 ' include any remainder as +1, do blocks of 512 bytes each as natural size of SD blocks
fat.readdata(@sdbuffer,512) ' read from SD card
repeat s from 0 to 511
c := byte[@sdbuffer] ' get the next byte
if t =< z ' might read in one more 512 byte block to ensure gets any remainder
if c == 13 or r > 126 ' end of a line so new string, or string too long
byte[@rambuffer][r] := 0 ' end of string terminator
StringToRam(Stringnumber)
stringnumber +=1 ' new string number
r := 0 ' start at beginning of rambuffer
if c >31 and c < 127
byte[@rambuffer][r] := c ' store if a valid character
r +=1
t +=1 ' number of bytes read
FileClose ' close the file
PUB StringLen(Source) ' string length
RamToString(Source)
result := strsize(@rambuffer)
PUB StringPrint(Font,Source) | i,s ' print this string using using font in memory and curx and cury
RamToString(Source)
s := strsize(@rambuffer)
repeat i from 0 to (s-1)
curx := TextChar(Font,byte[@rambuffer],curx,cury)
PUB StringRamBuffer(Source) ' string to rambuffer array
RamToString(Source)
result := @rambuffer ' return pointer to the rambuffer array
PUB StringCompare(Source1,Source2) | i
RamToString(Source2)
bytemove(@buffer2,@rambuffer,128) ' store string2 to buffer2
RamToString(Source1)
result := true
repeat i from 0 to strsize(@rambuffer)
if byte[@buffer2] <> byte[@rambuffer]
result := false
return
PRI StringToRam(StringN)
HubToRam(StringN << 6 + StringAddress,64) ' 64 words =128 bytes, so work in groups of 64 words
PRI RamToString(StringN)
RamToHub(StringN << 6 + StringAddress,64) ' moves to rambuffer array
' ***************** end string routines *************************
PUB DisplayLeft ' send all data to the left display
Latch_Display1
PUB DisplayRight
Latch_Display2
PUB RamErase(n)
Filenumber := n ' erase lookup table for the ram files, effectively erases the files
' if 0 then erase all, if 1 then leave the desktop intact (assuming this was loaded first)
PUB ClearRam
Filenumber := 2 ' clear ram but leave file 0 = warm or cold boot and 1 = background picture
PUB GetCurX
result := curx ' get the text cursor position
PUB GetCurY
result := cury
PUB SetCurx(n) ' set the text cursor position
Curx := n
PUB SetCury(n)
Cury := n
PUB GetBackFontColor
result := BackFontColor
PUB GetRamLocation(n)
result := Ramlocation[n]
PUB GetFilenumber
result := filenumber
PUB SetFilenumber(n) ' reset the file number
Filenumber := n
PUB Chain(stringptr)
result := \fat.bootpartition(stringptr) ' reboot and run this program
PUB SDtoRam(stringptr) | RamAddress,remainder,sizebytes,sizewords ' copy a file from the SD card to the ram disk
OpenFileRead(stringptr)
sizebytes := fat.filesize ' size in bytes
sizewords := sizebytes >>1 ' size in words
ramaddress := NextLocation ' next free place in ram
RamSize[FileNumber] := sizewords ' ram disk works in words, not bytes
repeat sizebytes >> 9 ' do blocks of 512 bytes each as natural size of SD blocks
fat.readdata(@sdbuffer,512) ' read from SD card
SpinHubToRam(@sdbuffer,RamAddress,512) ' move to ram
RamAddress += 256 ' increment ram address by 256 (half 512 as using words)
remainder := sizebytes & %00000000_00000000_00000001_11111111 ' remainder (if not a multiple of 512)
if remainder <> 0
fat.readdata(@sdbuffer,remainder)
SpinHubToRam(@sdbuffer,RamAddress,remainder) ' send out the remainder bytes
FileClose ' close the file
result := FileNumber ' return this file
FileNumber +=1 ' point to next location
PUB FetchRamBuffer
result := @rambuffer
PUB SDBMPtoRam(stringptr) | width,height,w,i,ramaddress ' .bmp from sd to ram and convert to 2 byte ili format and reverse order along the way
' http://en.wikipedia.org/wiki/BMP_file_format scroll down about 1/3 of the way for the header formats
' get the width, height
' store these as 2 longs at the beginning of the ram file
' the bitmap format starts at the last row and works up so need to reverse the order
ramaddress := NextLocation ' get the next ram location
OpenFileRead(stringptr)
fat.readdata(@sdbuffer,$36) ' get the header 0x36 hex bytes
width := sdbuffer[$12] + sdbuffer[$13] << 8 ' only read in two bytes as never will be >64k wide or high
height := sdbuffer[$16] + sdbuffer[$17] << 8
w := width * 3 ' this number of bytes per row, and round up to nearest 4 bytes et 327 goes to 328 The size of each row is rounded up to a multiple of 4 bytes (a 32-bit DWORD) by padding.
w +=3 ' add 3 and
w &= %11111111_11111111_11111111_11111100 ' round down
SpinHubToRam(@sdbuffer,ramaddress,$36) ' store header to ram
i := $36 + ramaddress + ((height - 1) * width) ' start + header and on the last row and work back
repeat height
fat.readdata(@sdbuffer,w) ' read in the first row
CogCmd("F",@sdbuffer,@rambuffer,width)
HubToRam(i,width) ' send to ram
i -= width ' subtract the width, move up the picture
FileClose
Ramsize[Filenumber] := $36 + (width * height) ' size of bitmap file in words
result := FileNumber ' returns the current file number
FileNumber += 1 ' add 1 to filenumber
PUB DrawBMPRam(f,x,y) | rampointer,width,height ' pass the filenumber and the x and y location, searches for this file and if found, displays it
rampointer := RamLocation[f] ' find the start of the file
SpinRamToHub(@sdbuffer,rampointer,$36) ' read in the header
width := sdbuffer[$12] + sdbuffer[$13] << 8 ' only read in two bytes as never will be >64k wide or high
height := sdbuffer[$16] + sdbuffer[$17] << 8
Draw(x,y,x+width-1,y+height-1) ' set up the area to draw
RamToDisplay(RamPointer + $36,width*height) ' skip the header and display
result := (width <<16) | (height & $0000FFFF) ' return the width and height combined into a long
' ***************** end ram disk routines
PUB MergeBackgroundIconRam(stringptr,x,y,desktop,mask,icon) | i,j,k ' merge icon and background - if mask =0 then background, else icon (not classic alpha compositing but close enough)
' i is background counter, j is icon counter, k = mask counter
' assumes desktop is in ramfile 1, mask in ramfile 2, icon in ramfile 3
FileNumber := icon
SDBMPtoRam(stringptr) ' load icon into ram
Filenumber := icon ' reset filenumber
i := RamLocation[desktop] + $36 + (y*(screenwidth+1)+x) ' location of picture pixel in ram
j := RamLocation[icon] + $36 ' location of icon in pixel in ram
k := RamLocation[mask] + $36 ' location of mask pixel in ram
repeat 60
SpinRamToHub(@sdbuffer,i,59) ' get 59 pixels from background
SpinRamToHub(@rambuffer,j,59) ' get 59 icon pixels
SpinRamToHub(@buffer2,k,59) ' get words for the mask
CogCmd("X",@sdbuffer,@rambuffer,@buffer2) ' pasm version of the above 3 lines
SpinHubToRam(@sdbuffer,i,59) ' send back modified line to the picture buffer
i += screenwidth+1 ' next line in picture
j +=59 ' icon next line
k +=59 ' next line in mask (bytes) - buffer2
' old "X" command code
'repeat n from 0 to 58
' if (word[@buffer2][n] & %00000000_00011111) > %10000 ' RGB are the same, so use BBBBB, mask 5 low bits, and if > %10000
' word[@sdbuffer][n] :=word[@rambuffer][n] ' if >128 then replace with icon pixel
PUB OpenFileRead(stringptr) ' open a file for reading and display a message if it fails
result := \fat.openfile(stringptr,"R")
if fat.partitionerror <> 0 ' failed to find the file
Text(stringptr) ' print the filename
Text(result) ' print the error message
repeat ' stop the program
PUB FileClose
fat.closefile ' close the file
PUB TouchXPercent
result := TouchValue & 255 ' decode xval 0-100%
if DisplayMode == "S"
result := 100-result ' SSD1289 x runs the other way
PUB TouchSwishX | xval,oldxval,difference,average ' returns result = +ve or -ve depending on swish direction ' did finger move left or right?
SelectSPIGroup ' turn on the spi touchscreen
oldxval := 255' for startup
average := 0
repeat until (average > 30) or (average < -30)
' possibly add a decay function here for average
pause1ms(50)
xval := TouchXPercent ' decode xval 0-100%
if xval == 255 ' not touched
oldxval := 255 ' reset oldxval as well
else
if oldxval <>255 ' discard the first reading as the screen is touched
difference := xval - oldxval
'printdecimal(xval)
'printdecimal(difference)
average := average + difference '
'printdecimal(average)
'crlf
oldxval := xval ' store for next time
result := average ' return negative or positive value
PUB TouchSwishY | yval,oldyval,difference,average ' returns result = +ve or -ve depending on swish direction ' did finger move left or right?
SelectSPIGroup ' turn on the spi touchscreen
oldyval := 255' for startup
average := 0
repeat until (average > 30) or (average < -30)
' possibly add a decay function here for average
pause1ms(50)
yval := TouchYPercent ' decode xval 0-100%
if yval == 255 ' not touched
oldyval := 255 ' reset oldyval as well
else
if oldyval <>255 ' discard the first reading as the screen is touched
difference := yval - oldyval
average := average + difference '
oldyval := yval ' store for next time
result := average ' return negative or positive value
PUB TouchStart
SPI.start(3,0) ' delay,state, start clock high
PUB TextChar(FileN,ascii,x,y) | jump,size,width,height,ramaddress,xoffset,yoffset,xadvance ' read out a character from a .ifn file stored in ram
' pass Fonttable = global
' moves to next line if off the end
ramaddress := RamLocation[FileN]
jump := ReadRamLong(ramaddress,(ascii << 2) + 256) ' jump location = ascii *4 plus fonttable + 256
SpinRamToHub(@buffer2,ramaddress+jump>>1,20) ' read in the width height as a block = quicker than readramlong
size := long[@buffer2][0] ' size in pixels of this character
xadvance := long[@buffer2][5] ' amount to move to next character
'if size > 0 and ascii <> 32 ' no need to print anything if size is zero or read more font data
width := long[@buffer2][1] ' width
height := long[@buffer2][2] ' height
xoffset := long[@buffer2][3] ' xoffset to move
yoffset := long[@buffer2][4] ' yoffset to move
if (x+width -1 + xoffset) > screenwidth
crlf ' do a new line if it won't fit
x := curx
y := cury
if ascii < 32
xadvance := 0 ' add nothing if a non displaying character
if ascii == 13 ' carriage return
cr
x := curx
if ascii ==10 ' line feed (new line)
lf
y := cury
if ascii > 32 ' space not displayed
Draw(x+xoffset,y+yoffset,x+width-1+xoffset,y+height-1+yoffset) ' draw on screen
RamToDisplay(ramaddress+((jump+32)>>1),size) ' move bytes from ram out to the display
return x + xadvance
PUB crlf
cr
lf
PUB cr ' carriage return, to left margin
curx := Margin ' some characters are -1 xoffset so won't display
PUB lf ' line feed, move up one
cury += FontHeight
if cury > screenheight
ClearRectangle(0,0,screenwidth,screenheight,BackFontColor)
cury := 0 ' if off the bottom of the screen then clear screen start at the top (VT00 does scrolling)
' don't clear background here, do in calling routine
'ClearFontBackground(0,cury,screenwidth,cury + FontHeight) ' clear background line to the background color ready to draw
PUB TextT(FileN,stringptr) 'print at curx,cury using file number in ramdisk
'' Print a zero-terminated string
repeat strsize(stringptr)
curx := TextChar(FileN,byte[stringptr++],curx,cury)
PUB SetCursor(x,y)
curx := x
cury := y
PUB ReadRamLong(WordStart,Bytevalue) ' easier calculating
SpinRamToHub(@result,Wordstart+Bytevalue>>1,2)
return result
PUB LoadFont(stringptr) | filesize,i ' read a .ifn file premade in angelcode bmfont and then the vb.net program
SDtoRam(stringptr) ' font to ram disk, adds one to filenumber
SpinRamToHub(@rambuffer,RamLocation[Filenumber-1],128) ' read first 128 words =256 bytes back
FontHeight := byte[@rambuffer][251] ' font height
repeat i from 0 to 2
sdbuffer := byte[@rambuffer][35+i]
CogCmd("E",@sdbuffer,@buffer2,1) ' convert to 2 byte .ili format
BackFontColor := ( byte[@buffer2][1] << 8) | byte[@buffer2][0] ' correct order
result := FileNumber-1 ' return pointer to the font file in ram
PUB HubToRam(RamAddress,Number) ' send pixels from rambuffer hub to ram
SpinHubToRam(@rambuffer,RamAddress,Number) ' spin version
PUB RamToHub(RamAddress,Number) ' send pixels from ram to hub at rambuffer
SpinRamToHub(@rambuffer,Ramaddress, number)
PUB RamToDisplay(RamAddress,Number) ' send pixels from ram to display
SpinRamToDisplay(Ramaddress, Number)
PUB Clearscreen(color) ' uses 2 byte ILI color (see below for rgb conversion)
wordfill(@rambuffer,color,256)
Draw(0,0,screenwidth,screenheight) ' set up the area of the screen to draw in
repeat 300
SpinHubToDisplay(@rambuffer,256)
PUB RGBtoWord(R,G,B) ' convert RGB to 2 byte ILI color
sdbuffer[0] := R
sdbuffer[1] := G
sdbuffer[2] := B
CogCmd("E",@sdbuffer,@buffer2,1)
result := ( byte[@buffer2][1] << 8) | byte[@buffer2][0] ' correct order
PUB ClearRectangle(x1,y1,x2,y2,ColorWord) | i,size,remainder ' clears an area of the screen to the current font background color
size := (x2-x1+1) * (y2 - y1 +1) ' number of pixels
remainder := size & 255 ' if not a whole number of 256 pixels
repeat i from 0 to 255 ' move the background font colour to the buffer
rambuffer := ColorWord
Draw(x1,y1,x2,y2) ' set up the area of the screen to draw in
repeat size >> 8 ' 256 pixel blocks
SpinHubToDisplay(@rambuffer,256)
if remainder <> 0
SpinHubToDisplay(@rambuffer,remainder) ' do the remainder
PUB Drawline(x1,y1,x2,y2,ColorWord) ' draws a vertical or horizontal line
ClearRectangle(x1,y1,x2,y2,ColorWord) ' same as drawing a rectangle
PUB SetOrientation(e) ' change orientation true = portrait. Changes global variable 'orientation' and also screen width and height
orientation := e
ChangeOrientation(orientation)
if orientation
screenwidth :=239
screenheight :=319
else
screenwidth :=319
screenheight :=239
PUB ChangeOrientation(n) ' pass true = portrait or false = landscape, changes global variable orientation in this object
' command $0001
'8.2.4. Driver Output Control (R01h)
'R/W RS D15 D14 D13 D12 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0
'W 1 0 0 0 0 0 SM 0 SS 0 0 0 0 0 0 0 0
'SS: Select the shift direction of outputs from the source driver.
'When SS = 0, the shift direction of outputs is from S1 to S720
'When SS = 1, the shift direction of outputs is from S720 to S1.
'In addition to the shift direction, the settings for both SS and BGR bits are required to change the
'assignment of R, G, B dots to the source driver pins.
'To assign R, G, B dots to the source driver pins from S1 to S720, set SS = 0.
'To assign R, G, B dots to the source driver pins from S720 to S1, set SS = 1.
' command $0003
'R/W RS D15 D14 D13 D12 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0
'W 1 TRI DFM 0 BGR 0 0 HWM 0 ORG 0 I/D1 I/D0 AM 0 0 0
' When AM = 0, the address is updated in horizontal writing direction.
'When AM = 1, the address is updated in vertical writing direction.
'I/D[1:0] Control the address counter (AC) to automatically increase or decrease by 1 when update one pixel
' display data.
SelectMemGroup
orientation := n
if displaymode == "I" ' ili9325
if orientation
' for origin top left and portrait mode
Displaycmd($0001,%00000001_00000000) ' $0001,$0100 set SS and SM bit 0001 0100 portrait
Displaycmd($0003,%00010000_00110000) ' $0003,$1030 set GRAM write direction and BGR=1. $0003 $1030
else
' for origin top right and landscape mode
Displaycmd($0001,%00000000_00000000) ' set SS and SM bit 0001 0000 landscape
Displaycmd($0003,%00010000_00111000) ' landscape $1028 = original but 1038 is correct - not mirror image
if displaymode == "S" ' SSD1289
if orientation
' for origin top left and portrait mode
Displaycmd($0001,$2B3F) ' Entry mode portrait
Displaycmd($0011,$6070) ' '
else
' for origin top right and landscape mode
Displaycmd($0001,$6B3F) ' landscape $2B3F = original but 6B3F is correct
Displaycmd($0011,$6838) ' landscape $1028 = original but 6838 is correct
PUB SelectMemGroup ' select group 1 for memory transfer
spi.stop ' and stop any other cogs here too if needed
LatchGroup2
DIRA |= %00000000_01011111_11111111_11111111 ' enable these pins for output
OUTA |= %00000000_00011111_00000000_00000000 ' set /rd /wr /disp wr and 161 clock high
PUB SelectSPIGroup
LatchGroup3 ' select group 2 for spi transfers
DIRA &= %11111111_11111111_11111111_11000000 ' so no clashes with the cog using P0-P2 set these as inputs and P3-P5
TouchStart ' start the touchscreen cog
' may need to set some pins here as inputs or outputs with a dira
PUB Text(stringptr) 'print at curx,cury
repeat strsize(stringptr)
Propfont_out(byte[stringptr++])
PropFontcrlf
PUB PropFontcrlf
curx := 0
cury += 32 ' new line at end of string
if cury >319 ' bottom of screen so new screen
curx:=0
cury:=0
PUB Propfont_out(ascii) | address,pixels
Draw(curx,cury,curx+15,cury+31) ' location to start drawing
address := $8000 + (ascii >> 1) << 7 ' get rom address
repeat 32 ' 32 rows per character, split in two parts
pixels := long[address] ' get rom font data
pixels := pixels >> (ascii & 1) ' shift for odd characters
repeat 16 ' 16 columns
if pixels & 1
Pixel(%00000111_11100000) ' foreground color RRRRRGGG_GGGBBBBB
else
Pixel(%00000000_00000000) ' background color
pixels := pixels >> 2 ' alternate pixels interleaved so shift 2
address += 4
curx +=16
if curx >239 ' new line
Propfontcrlf
PUB Start_ILI9325
LatchGroup2 ' talk to this display
LCD_Reset_High
pause1ms(5)
LCD_Reset_Low
pause1ms(5)
LCD_Reset_High
LCD_CS_High
LCD_RD_High
LCD_WR_High
pause1ms(20)
LCD_CS_Low
ILI9325initiate
PUB Start_SSD1289 ' based on C driver
LatchGroup2 ' talk to this display
LCD_Reset_High
pause1ms(5)
LCD_Reset_Low
pause1ms(10)
LCD_Reset_High
LCD_CS_High
LCD_RD_High
LCD_WR_High
pause1ms(20)
SSD1289initiate
PUB Draw(x1, y1, x2, y2) ' sets the pixel to x1,y1 and then fills the next (x2-x1)*(y2-y1) pixels
SelectMemGroup ' select memory group
ifnot orientation ' landscape mode so swap x and y
result :=x1 ' swap x1 and y1
x1 := y1
y1 := result
result := x2 ' swap x2 and y2
x2 :=y2
y2 := result
LCD_RS_High ' after sending out commands, set RS high via latch
PUB Pixel(pixelcolor) ' send out a pixel
Lcd_Write_Fast_Data(pixelcolor) ' need to set RS high at beginning of this group
PUB pause1ms(period) | clkcycles
'' Pause execution for period (in units of 1 ms).
clkcycles := ((clkfreq / _1ms * period) - 4296) #> 381 ' Calculate 1 ms time unit
waitcnt(clkcycles + cnt) ' Wait for designated time
PUB hex(value, digits) ' uses internal prop font and slow pixel drawing (for very basic debugging without an SD font)
SelectMemGroup ' reselect mem group for display output for debugging
'' Print a hexadecimal number
propfont_out("O")
propfont_out("x")
value <<= (8 - digits) << 2
repeat digits
propfont_out(lookupz((value <-= 4) & $F : "0".."9", "A".."F"))
PropFontcrlf
PUB SetWarmBoot
long[@rambuffer][5] := $5761726D ' spell Warm in hex, start at 5 as zero gets corrupted
byte[@rambuffer][10] := DisplayMode ' store the current display mode
HubToRam(0,20) ' send words to external ram (ramdiskstart is zero)
ClearScreen(0) ' so user sees something happen as there is a bit of a delay rebooting
reboot
PUB WarmColdBoot ' store warm or cold boot - reserve first 20 bytes in sram for interfacing with programs
' Ramdisk file 0 is the boot string Warm or Cold
' ramdisk file 1 is the background
Ramtohub(0,20) ' read words from the ram
if long[@rambuffer][5] == $5761726D
result := true
else
result := false
long[@rambuffer][5] := $FFFFFFFF ' erase previous warm/cold boot
byte[@rambuffer][10] := DisplayMode ' store display mode
HubToRam(0,20) ' send words to external ram (ramdiskstart is zero)
RamSize[0] := 20 ' reserve 20 bytes
RamLocation[0] := 0
Filenumber := 1
PRI GetDisplayMode
Ramtohub(0,20) ' read words from the ram
result := byte[@rambuffer][10] ' get the display mode
PUB ManualValues
RamLocation[0] := 0
RamSize[0] := $14 ' put in values manually - 20 bytes for warm boot
RamLocation[1] := $14
RamSize[1] := $12C36 ' 76800 for the desktop picture
FileNumber := 2 ' running a program not a desktop
PRI LatchGroup1
CogCmd("O",%00001111,0,0) ' set all group pins high
CogCmd("A",%11111110,0,0) 'set this one low
PRI LatchGroup2
CogCmd("O",%00001111,0,0) ' set all group pins high
CogCmd("A",%11111101,0,0) 'set this one low
PRI LatchGroup3
CogCmd("O",%00001111,0,0) ' set all group pins high
CogCmd("A",%11111011,0,0) 'set this one low
PRI LatchGroup4
CogCmd("O",%00001111,0,0) ' set all group pins high
CogCmd("A",%11110111,0,0) 'set this one low
PRI Latch_ResetLow
CogCmd("A",%11101111,0,0) 'set this one low
PRI Latch_ResetHigh
CogCmd("O",%00010000,0,0) ' set this pin high
PRI Latch_Display1 ' display on the left
CogCmd("O",%01100000,0,0) ' both displays high
CogCmd("A",%11011111,0,0) 'set this one low
PRI Latch_Display2 ' display on the right
CogCmd("O",%01100000,0,0) ' both displays high
CogCmd("A",%10111111,0,0) 'set this one low
PRI Latch_DisplayBoth ' both displays for debugging
CogCmd("A",%10011111,0,0) 'set both displays low
PRI Latch_RS_Low
CogCmd("A",%01111111,0,0) 'set this one low
PRI Latch_RS_High
CogCmd("O",%10000000,0,0) ' set this pin high
PRI Latch_AllHighStart ' all pins high
CogCmd("O",%11111111,0,0) ' set all pins high (disabled)
PRI Load161(address)
CogCmd("Z",0,address,0) ' call pasm version, faster, changes the latch but the next PUB always resets it anyway
PRI SpinHubToRam(hubaddress, ramaddress, number) | i ' P0-P15 are data lines, P16 is /RD, P17 is /WR
Load161(ramaddress) ' load the 161 counters
LatchGroup2 ' group 2 is block data transfers
CogCmd("S",hubaddress,ramaddress,number) ' pasm alternative to code below
DIRA &= %11111111_11100000_00000000_00000000 ' float all pins used
PRI SpinRamToHub(hubaddress,ramaddress,number) |i ' P0-P15 are data lines, P16 is /RD, P17 is /WR
Load161(ramaddress) ' load the 161 counters
LatchGroup2 ' group 2 is block data transfers
CogCmd("T",hubaddress,ramaddress,number) ' pasm alternative to code below
DIRA &= %11111111_11100000_00000000_00000000 ' float all pins used
PRI SpinRamToDisplay(ramaddress,number) | i ' ramaddress, number of words (pixels), display = 1 or 2
Load161(ramaddress) ' load the 161 counters
LatchGroup2 ' group 2 is block data transfers
CogCmd("U",0,0,number) ' pasm alternative to code below, much faster
DIRA &= %11111111_11100000_00000000_00000000 ' float all pins used
PRI SpinHubToDisplay(hubaddress,number)|i
'LatchGroup2 ' not needed as always do a Draw first
CogCmd("V",hubaddress,0,number) ' pasm version of code below
DIRA &= %11111111_11100000_00000000_00000000 ' float all pins used
PRI NextLocation ' get the next free location in ram based on last files size
if FileNumber == 0
RamLocation[0] := 0 ' work out start of this file based on last file size
else
RamLocation[FileNumber] := RamLocation[FileNumber - 1] + RamSize[FileNumber - 1] ' get location in ram
result := RamLocation[FileNumber]
PRI TouchX
SPI.SHIFTOUT(Touch_DataIn, Touch_Clock, 5, 8 , %1101_0000) ' reads x from 500 to 3700 (off < 500 )
result := SPI.SHIFTIN(Touch_DataOut,Touch_Clock,2, 12)
PRI TouchY
SPI.SHIFTOUT(Touch_DataIn, Touch_Clock, 5, 8 , %1001_0000) ' reads y from 400 to 3800 (off > 3800 )
result := SPI.SHIFTIN(Touch_DataOut,Touch_Clock,2, 12)
PRI TouchValue | xval,yval,x,y ' returns % values 0-100% (or 255 for no touch)
xval := TouchX
yval := TouchY
if (xval => 500) and (yval =< 3800) ' both have to be valid for a touch
pause1ms(2) ' tiny delay then read again as the first reads may not have the correct value
xval := TouchX
yval := TouchY
if (xval => 500) and (yval =< 3800) ' and if valid again, now more likely this value is correct
x := xval - 440 ' scale 0-3360
x /= 32 ' scale 0-100
x <#= 100 ' max 100
x #>=0 ' min 0
x := 100 - x ' so left edge on left
y := yval -420 ' scale 0-3180
y /= 32 ' scale 0-100
y <#= 100 ' max 100
y #>=0 ' min 0
y := 100-y ' so 0 is top left
result := (y <<8) | x ' return 00000000_00000000_YYYYYYYY_XXXXXXXX
if (xval < 500) or (xval >3800)
result := $0000FFFF ' invalid number(s)
PRI ILI9325initiate
' ************* Start Initial Sequence **********
Displaycmd($00E5,$78F0) ' set SRAM internal timing
Displaycmd($0001,$0100) ' set SS and SM bit 0001 0100 portrait
Displaycmd($0002,$0700) ' set 1 line inversion
Displaycmd($0003,$1030) ' set GRAM write direction and BGR=1. $0003 $1030
Displaycmd($0004,$0000) ' Resize register
Displaycmd($0008,$0207) ' set the back porch and front porch
Displaycmd($0009,$0000) ' set non-display area refresh cycle ISC[3:0]
Displaycmd($000A,$0000) ' FMARK function
Displaycmd($000C,$0000) ' RGB interface setting
Displaycmd($000D,$0000) ' Frame marker Position
Displaycmd($000F,$0000) ' RGB interface polarity
' *************Power On sequence ****************//
Displaycmd($0010,$0000) ' SAP, BT[3:0], AP, DSTB, SLP, STB
Displaycmd($0011,$0007) ' DC1[2:0], DC0[2:0], VC[2:0]
Displaycmd($0012,$0000) ' VREG1OUT voltage
Displaycmd($0013,$0000) ' VDV[4:0] for VCOM amplitude
Displaycmd($0007,$0001)
pause1ms(50) ' Dis-charge capacitor power voltage
Displaycmd($0010,$1090) ' 1490//SAP, BT[3:0], AP, DSTB, SLP, STB
Displaycmd($0011,$0227) ' DC1[2:0], DC0[2:0], VC[2:0]
pause1ms(50) ' delay
Displaycmd($0012,$001F) ' 001C// Internal reference voltage= Vci;
pause1ms(50) ' delay
Displaycmd($0013,$1500) ' $1000//1400 Set VDV[4:0] for VCOM amplitude 1A00
Displaycmd($0029,$0027) ' $0012 //001a Set VCM[5:0] for VCOMH //$0025 0034
Displaycmd($002B,$000D) ' Set Frame Rate 000C
pause1ms(50) ' delay
Displaycmd($0020,$0000) ' GRAM horizontal Address
Displaycmd($0021,$0000) ' GRAM Vertical Address
'
Adjust the Gamma Curve
//
Displaycmd($0030,$0000)
Displaycmd($0031,$0707)
Displaycmd($0032,$0307)
Displaycmd($0035,$0200)
Displaycmd($0036,$0008)
Displaycmd($0037,$0004)
Displaycmd($0038,$0000)
Displaycmd($0039,$0707)
Displaycmd($003C,$0002)
Displaycmd($003D,$1D04)
'
Set GRAM area
//
Displaycmd($0050,$0000) ' Horizontal GRAM Start Address
Displaycmd($0051,$00EF) ' Horizontal GRAM End Address
Displaycmd($0052,$0000) ' Vertical GRAM Start Address
Displaycmd($0053,$013F) ' Vertical GRAM Start Address
Displaycmd($0060,$A700) ' Gate Scan Line
Displaycmd($0061,$0001) ' NDL,VLE, REV
Displaycmd($006A,$0000) ' set scrolling line
'
Partial Display Control
/
Displaycmd($0080,$0000)
Displaycmd($0081,$0000)
Displaycmd($0082,$0000)
Displaycmd($0083,$0000)
Displaycmd($0084,$0000)
Displaycmd($0085,$0000)
' //
Panel Control
//
Displaycmd($0090,$0010)
Displaycmd($0092,$0600)
Displaycmd($0007,$0133) ' 262K color and display ON
ChangeOrientation(true) ' default to portrait
PRI SSD1289initiate
{ ILIcmddelay($0000,$0001) 'Turn Oscillator on POR-$0000
ILIcmddelay($0003,$A8A4) 'Power control (1) POR-$6664
ILIcmddelay($000C,$0000) 'Power control (2) POR- ?
ILIcmddelay($000D,$080C) 'Power control (3) POR-$0009
ILIcmddelay($000E,$2B00) 'Power control (4) POR-$3200
ILIcmddelay($001E,$00B0) 'Power control (5) POR-$0029
ILIcmddelay($0001,$4B3F) 'Driver output control, *landscape?? $6B3F* POR-$433F
ILIcmddelay($0002,$0600) 'LCD drive AC control POR-$0400
ILIcmddelay($0010,$0000) 'Sleep Mode sleep mode off POR-$0001
ILIcmddelay($0011,$6070) 'Entry Mode, *landscape? $4030* POR-$6830
ILIcmddelay($0005,$0000) 'Compare Register (1) POR-$0000
ILIcmddelay($0006,$0000) 'Compare Register (2) POR-$0000
ILIcmddelay($0016,$EF1C) 'Horizontal Porch POR-$EFC1
ILIcmddelay($0017,$0003) 'Vertical Porch POR-$0003
ILIcmddelay($0007,$0033) 'Display Control POR-$0000
ILIcmddelay($000B,$D308) 'Frame cycle Control POR-$D308
ILIcmddelay($000F,$0000) 'Gate Scan start position POR-$0000
ILIcmddelay($0041,$0000) 'Vertical Scroll Control (1) POR-$0000
ILIcmddelay($0042,$0000) 'Vertical Scroll Control (2) POR-$0000
ILIcmddelay($0048,$0000) 'First Window Start POR-$0000
ILIcmddelay($0049,$013F) 'First Window End POR-$013F
ILIcmddelay($004A,$0000) 'Second Window Start POR-$0000
ILIcmddelay($004B,$0000) 'Second Window End POR-$013F
ILIcmddelay($0044,$EF00) 'Horizontal Ram Address Postion POR-$EF00
ILIcmddelay($0045,$0000) 'Vertical Ram Address Start POR-$0000
ILIcmddelay($0046,$013F) 'Vertical Ram Address End POR-$013F
ILIcmddelay($0023,$0000) 'RAM write data mask (1) POR-$0000
ILIcmddelay($0024,$0000) 'RAM write data mask (2) POR-$0000
ILIcmddelay($0025,$8000) 'not in datasheet?
ILIcmddelay($004f,0) 'RAM Y address counter POR-$0000
ILIcmddelay($004e,0) 'RAM X address counter POR-$0000
Lcd_Write_Com($0022)
}
PRI Displaycmd(c,d) ' instruction in one method
Lcd_Write_Com(c) ' send out a word
Lcd_Write_Data(d)
PRI Lcd_Write_Com(LCDlong)
LCD_RS_low ' can do rs first then cs - better for latch board
LCD_CS_Low ' not needed for the ILI9325 but the SSD1289 needs this
LCD_Writ_Bus(LCDlong)
LCD_CS_High ' not needed for the ILI9325 but the SSD1289 needs this
PRI Lcd_Write_Data(LCDlong)
LCD_RS_High ' can do rs first then cs
LCD_CS_Low ' not needed for the ILI9325 but the SSD1289 needs this
LCD_Writ_Bus(LCDlong)
LCD_CS_High ' not needed for the ILI9325 but the SSD1289 needs this
PRI Lcd_Write_Fast_Data(LCDlong) ' write RS elsewhere then skip the RS above as this is a latch output and takes longer
LCD_CS_Low ' not needed for the ILI9325 but the SSD1289 needs this
LCD_Writ_Bus(LCDlong)
LCD_CS_High ' not needed for the ILI9325 but the S
Doc, please check Touch.spin from post 495. I THINK "S", "T" and *ram to display* are fully ported to PASM. That object works quite well for me and seems a bit faster. Let me know if you have trouble running it. As far as the latch value, I don't think we need the and or or commands. Seems better to get the latch value from PASM and pass to Spin like so
PUB GetLatch
CogCmd("Y",@rambuffer,0,0)
return := long[@rambuffer]
..
..
get_latch_val call # get_values 'to store latchvalue in dirb for return to done
mov hubaddr,latchvalue 'put latchvalue in ramaddr
jmp #done
Or something like that...
I think the driver in post #495 should be good, but it breaks when I copy and paste into the cache driver.
*edit*
Took a quick look over your code, and the one thing I'm seeing is you're not releasing P22... This is the bugger that really annoyed me while working on the driver I posted. IMO, it should NEVER be controlled in Spin. The latch command is the only time it's used and that should only be done in PASM. Makes things much easier. The other thing,
PRI CogCmd(command_, hub_address, ram_address, block_length) : err_| a,b
' Do the command: A-Z (I is reserved for Initialise)
'dira := 0 ' tristate all pins with the spin dira
dirb := dira ' store the state of these and restore at the end
outb := outa
DIRA &= %11111111_10100000_00000000_00000000 ' tristate all common pins that cog can change so no conflicts, you forgot P22!
hubaddrs := hub_address ' hub address start
ramaddrs := ram_address ' ram address start
blocklen := block_length ' block length
command := command_ ' must be last !!
' Wait for command to complete and get status
repeat while command ' driver cog sets =0 when done
err_ := errx ' driver cog sets =0 if no error, else xx = error code
outa := outa
dira := dirb ' restore dira and outa
That's probably why things are broken.
init 'set up latches here
or outa,maskP22 ' pin 22 high
or dira,maskP22 ' and now set as an output
mov dirb, #%11111111 'set latch All hi
' Initialise hardware tristates everything and read/write set the pins
done mov err, #0 ' reset err=false=good
mov latchvalue, dirb ' restore latch value
call #set373
and dira,maskP0P20low ' tristates all the common pins, leaves P22 as is though
wrlong err, errptr ' status =0=false=good, else error x
wrlong zero, comptr ' command =0 (done)
get_values rdlong hubaddr, hubptr ' get hub address
rdlong ramaddr, ramptr ' get ram address
rdlong len, lenptr ' get length
mov err, #5 ' err=5
mov dirb,latchvalue ' make copy of latchvalue here for restore
or outa,maskP16P20 ' set control pins high
or dira,maskP16P20 ' set control pins P16-P20 as outputs
get_values_ret ret
set373 or outa,maskP22 ' pin 22 high
or dira,#%1_11111111 ' enable pins 0-7 and 8 as outputs
and outa,maskP0P8low ' P0-P8 low
or outa,latchvalue ' send out the data
or outa,maskP8 ' P8 high, clocks out data
andn outa,maskP22 ' pin 22 low
set373_ret ret
set161 mov latchvalue,#%11111110 ' group 1, displays all off
call #set373 ' send out to the latch
and outa,maskP0P20low '%11111111_11100000_00000000_00000000
or dira,maskP0P20 '%00000000_00011111_11111111_11111111
or outa,ramaddr ' send out ramaddr
or outa,maskP20 ' clock high
or outa,maskP19 ' load high
set161_ret ret
' ------------------ single letter commands -------------------------------------
' command S
pasmhubtoram call #get_values ' get hubaddr,ramaddr,len and set control pins
call #set161
or outa,maskP16P20 ' set control pins high
mov latchvalue,#%11111101 ' group 2, displays all off
call #set373 ' send out to the latch
hubtoram_loop and outa,maskP16P31 '%11111111_11111111_00000000_00000000 ' clear for output
rdword data_16,hubaddr ' get the word from hub
and data_16,maskP0P15 ' mask to a word only
or outa,data_16 ' send out the byte to P0-P15
andn outa,maskP17 ' set mem write low
add hubaddr,#2 ' increment by 2 bytes = 1 word. Put this here for small delay while writes
or outa,maskP17 ' mem write high
andn outa,maskP20 ' clock 161 low
or outa,maskP20 ' clock 161 high
djnz len,#hubtoram_loop ' loop this many times
jmp #done ' tristate pins and listen for commands
' command T
pasmramtohub call #get_values ' get hubaddr,ramaddr,len and set control pins
call #set161
or outa,maskP16P20 ' set control pins high
mov latchvalue,#%11111101 ' group 2, displays all off
call #set373 ' send out to the latch
and dira,maskP16P31 '%11111111_11111111_00000000_00000000 inputs
andn outa,maskP16 ' memory /rd low
ramtohub_loop mov data_16,ina ' get the data
wrword data_16,hubaddr ' move data to hub
andn outa,maskP20 ' clock 161 low
or outa,maskP20 ' clock 161 high
add hubaddr,#2 ' increment the hub address
djnz len,#ramtohub_loop
or outa,maskP16 ' memory /rd high
jmp #done ' ' tristate pins and listen for commands
' command U
pasmramtodisplay call #get_values ' get hubaddr,ramaddr,len (only uses len) and set control pins
call #set161
or outa,maskP16P20 ' set control pins high
mov latchvalue, dirb
call #set373
andn outa,maskP19 ' CS low
and dira,maskP16P31 ' %11111111_11111111_00000000_00000000 so prop pins 0-15 HiZ
andn outa,maskP16 ' ram /rd low
ramtodisplay_loop andn outa,maskP18 ' ILI write low
or outa,maskP18 ' ILI write high
andn outa,maskP20 ' clock 161 low
or outa,maskP20 ' clock 161 high
djnz len,#ramtodisplay_loop
or outa,maskP16 ' mem /rd high
or outa,maskP19 ' CS high
jmp #done
' command V
pasmhubtodisplay call #get_values ' get hubaddr,ramaddr,len and set control pins high
andn outa,maskP19 ' CS low
or dira,maskP0P15 '%00000000_00000000_11111111_11111111
hubtodisplay_loop and outa,maskP16P31 '%11111111_11111111_00000000_00000000 ' clear for output
rdword data_16,hubaddr ' get the word from hub
and data_16,maskP0P15 ' mask to a word only
or outa,data_16 ' send out the byte to P0-P15
andn outa,maskP18 ' ILI write low
or outa,maskP18 ' ILI write high
add hubaddr,#2 ' one word
djnz len,#hubtodisplay_loop
or outa,maskP19 ' CS high
jmp #done
'' *** command Z **********
'changes the latch so displays won't work unless resend the spin value for the latch
extset161 call #get_values ' gets ramaddr = the 161 value to set
mov dirb, latchvalue
call #set161
jmp #done ' tristates pins etc
' *** command M - pass current latch value and send out
latch373value call #get_values
mov dirb,hubaddr
jmp #done
PRI Load161(address)
CogCmd("Z",0,address,0) ' call pasm version, faster, changes the latch but the next PUB always resets it anyway
PRI SpinHubToRam(hubaddress, ramaddress, number) ' P0-P15 are data lines, P16 is /RD, P17 is /WR
CogCmd("S",hubaddress,ramaddress,number) ' pasm alternative to code below
PRI SpinRamToHub(hubaddress,ramaddress,number) ' P0-P15 are data lines, P16 is /RD, P17 is /WR
CogCmd("T",hubaddress,ramaddress,number) ' pasm alternative to code below
PRI SpinRamToDisplay(ramaddress,number) ' ramaddress, number of words (pixels), display = 1 or 2
CogCmd("U",0,ramaddress,number) ' pasm alternative to code below, much faster
PRI SpinHubToDisplay(hubaddress,number)
CogCmd("V",hubaddress,0,number) ' pasm version of code below
PUB SelectMemGroup ' select group 1 for memory transfer
spi.stop ' and stop any other cogs here too if needed
LatchGroup2
DIRA |= %00000000_00011111_11111111_11111111 ' enable these pins for output 'Removed P22, now done by cog JH
OUTA |= %00000000_00011111_00000000_00000000 ' set /rd /wr /disp wr and 161 clock high
PUB SelectSPIGroup
LatchGroup3 ' select group 2 for spi transfers
DIRA &= %11111111_11111111_11111111_11000000 ' so no clashes with the cog using P0-P2 set these as inputs and P3-P5
TouchStart ' start the touchscreen cog
PUB HubToRam(RamAddress,Number) ' send pixels from rambuffer hub to ram
CogCmd("S",@rambuffer,RamAddress,Number)
PUB RamToHub(RamAddress,Number) ' send pixels from ram to hub at rambuffer
CogCmd("T",@rambuffer,Ramaddress, number)
PUB RamToDisplay(RamAddress,Number) ' send pixels from ram to display
CogCmd("U",0,Ramaddress,Number) ' pasm alternative to code below, much faster
I also "unrolled" a few calls to improve performance
PUB SDBMPtoRam(stringptr) | width,height,w,i,ramaddress ' .bmp from sd to ram and convert to 2 byte ili format and reverse order along the way
' http://en.wikipedia.org/wiki/BMP_file_format scroll down about 1/3 of the way for the header formats
' get the width, height
' store these as 2 longs at the beginning of the ram file
' the bitmap format starts at the last row and works up so need to reverse the order
ramaddress := NextLocation ' get the next ram location
OpenFileRead(stringptr)
fat.readdata(@sdbuffer,$36) ' get the header 0x36 hex bytes
width := sdbuffer[$12] + sdbuffer[$13] << 8 ' only read in two bytes as never will be >64k wide or high
height := sdbuffer[$16] + sdbuffer[$17] << 8
w := width * 3 ' this number of bytes per row, and round up to nearest 4 bytes et 327 goes to 328 The size of each row is rounded up to a multiple of 4 bytes (a 32-bit DWORD) by padding.
w +=3 ' add 3 and
w &= %11111111_11111111_11111111_11111100 ' round down
SpinHubToRam(@sdbuffer,ramaddress,$36) ' store header to ram
i := $36 + ramaddress + ((height - 1) * width) ' start + header and on the last row and work back
repeat height
fat.readdata(@sdbuffer,w) ' read in the first row
CogCmd("F",@sdbuffer,@rambuffer,width)
SpinHubToRam(@ramBuffer,i,width) ' send to ram
i -= width ' subtract the width, move up the picture
FileClose
Ramsize[Filenumber] := $36 + (width * height) ' size of bitmap file in words
result := FileNumber ' returns the current file number
FileNumber += 1 ' add 1 to filenumber
PRI StringToRam(StringN)
HubToRam(StringN << 6 + StringAddress,64) ' 64 words =128 bytes, so work in groups of 64 words
PRI RamToString(StringN)
SpinRamToHub(@rambuffer,StringN << 6 + StringAddress,64) ' moves to rambuffer array
I think that covers most changes.
Thinking about it more, there should be no need to "worry" about the latch setting in PASM. As long as it's saved before each operation, and restored after we should be able to "trash" it during the operation. When the cache receives a hit, just back up latch value then restore it.
I'm kinda scratching my head as to the problem. I'll be swapping SRAMs and 161s with my spare set *good thing I have 2 test sets AND a brand new one* I'm almost convinced it's hardware. Although it's more likely I made a mistake when moving commands into driver. I guess I'll take another go at it..
Yikes, lots of things to look at there. can we take things one at a time, when you say "Took a quick look over your code, and the one thing I'm seeing is you're not releasing P22" do you mean in spin or pasm?
It is deliberate that it is not released in pasm otherwise something else might do a dira on this pin and change it.
I was talking about the Spin code. The CogCmd PUB is most likely the one to worry about, although Spin should never make P22 an output. I'm not SURE that it is, but it's easy to forget one DIRA somewhere along the way.
I know there's a lot to look at, it took a while to get it working. The one snippet to pay close attention to is the PASM code.
*edit* looking at my code I noticed I missed a few of the DIRA commands. Although
DIRA &= %11111111_11111111_11111111_11000000
Shouldn't hurt anything, it would be better to do
DIRA &= %11111111_10111111_11111111_11000000
So if P22 is accidentally made an output somewhere along the way, it doesn't matter.
I took another attempt on the cache driver... Stumped... I need a sanity check after swapping SRAM, 161s, several attempts filling the cache driver. If you could check the archive below and verify.
1. Load "test_cache" to eeprom *saves re-loading after each test*
2. Open terminal, type "t 0" and press enter, should return
ERROR! Expected 0 @ 00007ffc after write to address 00007f7c 088b2ab9
3.reset prop, when terminal returns prompt type "t 2" press enter. Should return
ERROR at $00000000 Expected $00000001 Received $00000fc1
4. reset prop again, this time "i 2000", should return
ERROR at $00000000 Expected $00000001 Received $00000fc1
I hope you get different results *tests pass* but verifying it's driver is important.
Pin22. Now the way the 373 latch works, if its OC pin is low then the outputs are enabled, and if it is high then the outputs are HiZ. The hardware has pullup resistors on all the latch outputs and it has a pullup resistor on P22. When the propeller boots up all pins are HiZ and so the latch outputs are effectively high, which means the displays are disabled, and all groups are disabled.
The only thing that ever uses pin 22 is the latch output. The latch output code is only in pasm. So
Shouldn't hurt anything, it would be better to do
DIRA &= %11111111_10111111_11111111_11000000
would actually be a problem because this would make pin 22 HiZ. That makes all the latch outputs high, which amongst other things, disables the displays. It may or may not matter?
Next idea - how about any pasm routine is allowed to change the latch to whatever it wants. If we do that, then need to set the display /CS lines explicitly in several places - before the startup routine, before a Draw and before changing the orientation, and before a memory dump to the screen. I've rewritten the code here with those changes. So now the S and T commands can change the latch
[code]
CON Touch
'' ILI9325 driver using the Touchblade161 design for faster ram to display transfers
'' Average Joe and James Moxham 2012
Margin = 4 ' some characters have xoffset of -1 or -2 eg v, and won't display on the extreme left edge
_1ms = 1_000_000 / 1_000 ' Divisor for 1 ms
' touchscreen pins
Touch_Clock = 0
'Touch_ChipSelect = done with a group select
Touch_DataIn = 1
Touch_DataOut = 2' touchscreen pins
VAR
byte FileNumber ' 0 to 31
byte DisplayMode ' type of display 0 = ILI9325, 1= SSD1289, 2 etc
byte DisplayLeftRightBoth ' "R", "L" or "B"
word ScreenWidth ' either 240 in portrait or 320 in landscape
word ScreenHeight ' 320 in portrait or 240 in landscape
long orientation ' true is portrait
long curx, cury
byte FontHeight ' size of the current loaded font (pixels = fontsize *.75)
word BackFontColor ' the background color in RRRRRGGG GGGBBBBB format
long StringAddress ' start of string address
long wcboot ' most recent boot warm or cold
'
OBJ
spi: "SPI_ASM" ' thanks to Beau - spi driver for touch screen
fat: "SD-MMC_FATEngine.spin" ' Kye's latest sd driver and no RTC
DAT
RamLocation long $0[31] ' 32 file ram locatinos
RamSize long $0[31] ' File sizes in words - NOT bytes (divide byte by 2)
sdbuffer byte $0[1024] ' 1024 byte buffer for sd card interface and also for sending one row 320 x3 = 960 bytes max picture size
buffer2 byte $0[512] ' 512 general purpose hub buffer
rambuffer word $0[256] ' 512 byte (256 word) buffer easier for using with words
PUB BeginDesktop(DisplayType) ' store the type of display once by the desktop program
DisplayMode := DisplayType ' global variable I = ILI9325, S=SSD1289
Begin(true) ' start up everything running the desktop
result := wcboot
PUB BeginProgram ' used by all programs except the desktop program
Begin(false) ' any program except the desktop
result := wcboot
PRI Begin(Desktop) |n ' desktop true if run from the desktop program, false for other programs
start_ram_cog ' * start cog first *start the external ram / display driver in a cog, needed for many routines so always first
Latch_AllHighStart ' all Latch pins high and enables pin 22
if desktop == false
DisplayMode := GetDisplayMode ' reads "I" or "S" etc off the ram ** must have run desktop at least once first to store this ***
'DisplayMode := "I" ' temp for debugging, using the ILI9325
SelectMemGroup ' select group2
DisplayLeftRightBoth := "B" ' right left or both displays "R", "L", "B"
if displaymode == "I"
Start_ILI9325 ' start the display 0 = ILI9325, 1 = SSD1289
if displaymode == "S"
Start_SSD1289
wcboot := WarmColdBoot ' get the last warm or cold boot value
SetOrientation(true) ' in portrait (true) or landscape mode (false), must be before clearscreen otherwise don't know screensize
if wcboot == false and Desktop == true ' no need to redisplay every time if worked the first time
Text(string("SD")) ' string to send, uses internal prop font so can see something if the SD fails to mount
fat.fatEngineStart( _dopin, _clkpin, _dipin, _cspin, _wppin, _cdpin, _rtcres1, _rtcres2, _rtcres3)
fat.mountPartition(0) ' mount the sd card
if desktop == false ' running a program, this is called without knowing what the ramlocation and ramsize values are
ManualValues ' coming from outside so reset ramsize and ramlocation values for 0 and 1
SetCursor(0,0) ' cursor for debugging to 0,0
if desktop == false
SelectSPIGroup ' selects this group and starts the cog
'PUB BasicTesting
' text(string("Basic testing"))
' sdbuffer[0] := 6
' SpinHubToRam(@sdbuffer,5000,10) ' move data to ram
' sdbuffer[0] := 7 ' change the value
' SpinRamToHub(@sdbuffer,5000,10) ' read back the original value
' hex(sdbuffer[0],2)
' 'Drawline(100,100,110,100,$FFFF)
' Clearrectangle(120,120,129,129,$FFFF)
' Draw(100,100,109,109)
' repeat 100
' pixel(%11111000_00000000)
' ************** string routines stored in external memory so can have arrays and large text files **********************
' to make things simpler, one array StringArray. any number of entries, each up to 128 characters
' pass the source and destination numbers
' TextArray(256) = 256 entries for text files etc
' first entry is destination, second entrie(s) are the source
' Commands are
' StringDim ' reserves memory 256*128, increments filenumber, adds filesize
' StringLoadFile(Name,S) ' read a .txt file off the SD card and stores in TextArray, new string entry is crlf
' StringSaveFile(Name,S,F) ' save .txt file from TextArray to SD card adding in crlf, Start to Finish
' StringClear(Dest) ' clear a string, pass string number
' StringAddChar(Dest,N) ' adds a character to a string at end
' StringSubChar(Dest) ' remove a character from the end
' StringCopy(Dest,Src) ' copy string from one location to another
' StringStore(Dest,Src) ' convert Prop string("mystring") to string stored in external memory
' StringLeft(Dest,Src,N) ' Basic Left$
' StringMid(Dest,Src,L,N) ' Basic Mid$
' StringRight ' Basic Right$
' StringInstr ' Basic Instr
' StringUcase(Dest,Src) ' Basic Ucase$
' StringLcase ' Basic Lcase$
' StringLen ' Length
' StringStr ' string representation of a number
' StringHex(Dest,N) ' number to hex string
' StringBin ' number to binary string
' StringVal ' value (hex is 0x, binary is 0b)
' StringJoin(Dest,Src1,Src2) ' same as Basic + with strings
' StringInsert(Dest,Src1,Src2,n) ' inserts src2 into src1 starting at position n, (1 is first char) and moves to dest
' StringDebug(n) ' print using the green propeller font string number n
' StringLen() ' same as strsize but works on external memory strings
' StringRambuffer ' moves string to rambuffer array and returns result = location of this array
' StringCompare(Source1,Source2) ' returns true if strings equal in length and characters
PUB StringTest ' test all the string routines
'StringDim(10)
'StringStore(5,string("Hello World"))
'StringDebug(5)
'StringClear(5)
'StringAddChar(5,"1")
'StringAddChar(5,"2")
'StringAddChar(5,"3")
'StringAddChar(5,"4")
'StringDebug(5)
'StringSubChar(5) ' remove one character from the right, useful for backspace etc
'StringDebug(5)
'StringCopy(7,5) ' copy
'StringDebug(7)
'StringLeft(7,7,5) ' source and destination the same or different
'StringDebug(7)
'StringMid(8,5,7,3) ' dest, source, starting byte, number of bytes. first byte is 1
'StringDebug(8)
'StringRight(3,5,4) ' dest, source, number of bytes
'StringDebug(3)
'Hex(StringInstr(5,2,"o"),2) ' find this character starting at 2
'StringBin(2,255) ' convert a number to binary always 32 + 2 characters
'StringDebug(2)
'StringHex(4,100) ' always 8 + 2 characters
'StringDebug(4)
'StringDec(9,-20) ' always 11 characters
'StringDebug(9)
'StringStr(9,-7) ' Basic str$ removes leading zeroes, and first character is either - or a space
'StringDebug(9)
'StringStore(5,string("65")) ' test string number to number
'hex(StringVal(5),8)
'StringStore(5,string("0x1234")) ' test string hex to number
'hex(StringVal(5),8)
'StringStore(5,string("0b101")) ' test string binary to number
'hex(StringVal(5),8)
'StringStore(1,string("Hello "))
'StringStore(2,string("World"))
'StringJoin(1,1,2) ' string1 = string1 +string2 (shows dest and a source can be the same - or could be different)
'StringDebug(1)
'StringUcase(1,1) ' Ucase test
'StringDebug(1)
'StringLcase(1,1) ' Lcase test
'StringDebug(1)
'StringStore(1,string(" 123")) ' test the trim function
'StringTrim(1,1)
'StringDebug(1)
'StringStore(1,string("TEST6.TXT")) ' file name
'StringStore(2,string("Line a of text")) ' store some lines of text
'StringStore(3,string("Line b of text"))
'StringStore(4,string("Line c of text"))
'StringSavefile(1,2,4) ' filename is 1, store lines 2 to 4
'StringStore(1,string("TEST6.TXT")) ' file name
'StringLoadFile(1,2) ' read back the file created above.
'Stringdebug(2)
'Stringdebug(3)
'Stringdebug(4)
'repeat
PUB StringDim(n) ' reserve space for a string array, n= number of strings, 128 max bytes per string
Ramsize[Filenumber] := n << 6 ' * 64 as this is in words, not bytes
StringAddress := NextLocation ' get the location of the string array
Filenumber += 1
PUB StringStore(StringN,Source) ' store a propeller string("test") to array number Dest
bytemove(@rambuffer, Source, (strsize(Source) + 1)) ' move to rambuffer array
StringToRam(StringN)
PUB StringDebug(StringN) ' print using the green propeller font - useful if sd card not working so no fonts loaded
RamToString(StringN)
Text(@rambuffer) ' print it out
PUB StringClear(StringN)
bytefill(@rambuffer,0,128) ' only really need to do the first one but may help debugging to completely clear the string
StringToRam(StringN)
PUB StringAddChar(StringN,c) | s ' add c to the string
if c >31 and c <127 ' a character that can be displayed
RamToString(StringN) ' get the string
s := strsize(@rambuffer)
byte[@rambuffer] := c
byte[@rambuffer][s + 1] := 0 ' add in the new zero (can't assume array is cleared)
StringToRam(StringN) ' store back to ram
PUB StringSubChar(StringN) ' remove one character from the right same as strings.left(strings.len - 1)
RamToString(StringN)
if strsize(@rambuffer) <> 0 ' if at least one character
byte[@rambuffer][strsize(@rambuffer)-1] :=0
StringToRam(StringN)
PUB StringCopy(Dest,Source) ' copy string from source to dest
RamToString(Source)
StringToRam(Dest)
PUB StringLeft(Dest,Source,n) ' Basic Left$
RamToString(Source)
byte[@rambuffer][n] := 0 ' move the terminator
StringToRam(Dest) ' move back to ram
PUB StringMid(Dest,Source,start,n) | i ' Basic Mid$
RamToString(Source)
repeat i from 0 to n-1
byte[@rambuffer] := byte[@rambuffer][i+start-1] ' possibly could use bytemove here
byte[@rambuffer][n] := 0 'add in the new zero terminator
StringToRam(Dest)
PUB StringRight(Dest,Source,n) | i,m
RamToString(Source)
m := strsize(@rambuffer) - n
repeat i from 0 to n ' Basic Right$. include the terminator
byte[@rambuffer] := byte[@rambuffer][i+m]
StringToRam(Dest)
PUB StringInstr(Source,Start,c) | i ' find c in the string starting at start
RamToString(Source)
result := 0 ' return 0 if not found
repeat i from (start-1) to strsize(@rambuffer)
if byte[@rambuffer] == c
result := i+1 ' 1 is first character
quit
PUB StringBin(Dest,n) ' convert n to a binary value at Dest. Prefix 0b
repeat result from 33 to 2
byte[@rambuffer][result] := ((n & 1) + "0")
n >>= 1
byte[@rambuffer][34] := 0 ' add zero terminator
byte[@rambuffer][0] := "0" ' prefix 0b same as some versions of C
byte[@rambuffer][1] := "b"
StringToRam(Dest) ' store
PUB StringHex(Dest,n) ' number to hex, always 8 characters
repeat result from 9 to 2
byte[@rambuffer][result] := lookupz((n & $F): "0".."9", "A".."F")
n >>= 4
byte[@rambuffer][10] := 0 ' zero at end
byte[@rambuffer][0] := "0" ' prefix 0x same as C
byte[@rambuffer][1] := "x"
StringToRam(Dest)
PUB StringDec(Dest,n) | sign ' number to decimal string, always 11 characters
sign := "+"
if(n < 0)
sign := "-"
if(n == negx)
bytemove(@rambuffer, string("-2147483648"), 11) ' max negative number special case
else
repeat result from 10 to 1
byte[@rambuffer][result] := ((||(n // 10)) + "0")
n /= 10
byte[@rambuffer][0] := sign ' sign at the front
byte[@rambuffer][11] := 0 ' zero terminator
StringToRam(Dest)
PUB StringStr(Dest,n) | found ' same as Basic STR - similar to Dec above but sign is either space or negative, and leading zeros removed
StringDec(Dest,n) ' number preserved in rambuffer
found := 10 ' if answer was zero don't remove leading zero
repeat result from 1 to 10
if byte[@rambuffer][result] <> "0"
found := result
quit
repeat result from 1 to 11
byte[@rambuffer][result] := byte[@rambuffer][result +found -1] ' shuffle over if needed
if byte[@rambuffer][0] == "+"
byte[@rambuffer][0] := " " ' Basic syntax, - or blank
StringToRam(Dest) ' resend to external ram
PUB StringVal(Source) | sign,i,k ' returns value of the string, hex 0x, binary 0b, decimal + or - or space or no leading character just a number
RamToString(Source)
if byte[@rambuffer][1] == "x" ' this is a hex number
repeat k from 2 to strsize(@rambuffer) -1 ' leading two characters always 0x
result := ((result <- 4) + (byte[@rambuffer][k] & $F))
return
if byte[@rambuffer][1] == "b" ' this is a binary number
repeat k from 2 to strsize(@rambuffer) -1 ' leading two characters always 0b
result := ((result << 1) + (byte[@rambuffer][k] & 1))
return
' else if got here this must be a decimal number. It could start with a number(no leading sign), or a space, or a + or a -
sign := byte[@rambuffer][0]
i := 0 ' start location
if sign == "+" or sign == "-" or sign == " " ' this is a decimal number ** must start with a space or a + or a -**
i := 1 ' leading character so start here
repeat k from i to (strsize(@rambuffer)-1) ' if ever use right to left, don't use step -1, spin puts it in if start is less than finish and if there is a step -1 it is one less loop
result := ((result *10) + byte[@rambuffer][k] & $F) 'very clever, the & $F works because zero is hex0x30, the pre multiply shifts previous numbers to the left so can do left to right. Easier to understand with Kye's binary converter with the left shift
if sign == "-"
result *= -1 ' if negative then negate
PUB StringJoin(Dest,Source1,Source2) ' Dest$= Source1$+Source2$
RamToString(Source2)
bytemove(@buffer2,@rambuffer,128) ' store string2 to buffer2
RamToString(Source1) ' get first string
bytemove(@rambuffer+strsize(@rambuffer),@buffer2,strsize(@buffer2)+1) ' thanks to Kye for this one
StringToRam(Dest) ' store back to ram
PUB StringUcase(Dest,Source)
RamToString(Source)
repeat result from 0 to strsize(@rambuffer)
if ((byte[@rambuffer][result] => "a") and (byte[@rambuffer][result] =< "z"))
byte[@rambuffer][result] -= 32
StringToRam(Dest)
PUB StringLcase(Dest,Source)
RamToString(Source)
repeat result from 0 to strsize(@rambuffer)
if ((byte[@rambuffer][result] => "A") and (byte[@rambuffer][result] =< "Z"))
byte[@rambuffer][result] += 32
StringToRam(Dest)
PUB StringTrim(Dest,Source)
RamToString(Source)
if byte[@rambuffer][0] == "+" or byte[@rambuffer][0] == " " or byte[@rambuffer][0] == "-"
repeat result from 0 to strsize(@rambuffer)
byte[@rambuffer][result] := byte[@rambuffer][result+1] ' delete the first +,- or space
StringToRam(Dest)
PUB StringSaveFile(Filen,Start,Finish) | i,j ' filen might be string array 0, start could be 1, finish could be 4
RamToString(Filen) ' fetch the filename
fat.newfile(@rambuffer) ' create the file, if not working use \fat. for debugging, see PUB openfileread for an example
result := \fat.openfile(@rambuffer,"W") ' open for writing return error if there is one
repeat i from Start to Finish
RamToString(i)
j := strsize(@rambuffer)
byte[@rambuffer][j] := 13 ' carriage return
byte[@rambuffer][j+1] :=10 ' line feed
byte[@rambuffer][j+2] := 0 ' end of new line
fat.writedata(@rambuffer,j+2) ' write this line
FileClose ' close the file
PUB StringLoadFile(Filen,Stringnumber)| s,r,c,t,z ' read filename, new line every crlf. Must have enough space reserved in the string array
RamToString(Filen) ' copy a file from the SD card to the ram disk
OpenFileRead(@rambuffer)
s := 0 ' sd counter
r := 0 ' rambuffer counter
t := 0 ' total number of bytes
z := fat.filesize ' size of the file
repeat (z >> 9) +1 ' include any remainder as +1, do blocks of 512 bytes each as natural size of SD blocks
fat.readdata(@sdbuffer,512) ' read from SD card
repeat s from 0 to 511
c := byte[@sdbuffer] ' get the next byte
if t =< z ' might read in one more 512 byte block to ensure gets any remainder
if c == 13 or r > 126 ' end of a line so new string, or string too long
byte[@rambuffer][r] := 0 ' end of string terminator
StringToRam(Stringnumber)
stringnumber +=1 ' new string number
r := 0 ' start at beginning of rambuffer
if c >31 and c < 127
byte[@rambuffer][r] := c ' store if a valid character
r +=1
t +=1 ' number of bytes read
FileClose ' close the file
PUB StringLen(Source) ' string length
RamToString(Source)
result := strsize(@rambuffer)
PUB StringPrint(Font,Source) | i,s ' print this string using using font in memory and curx and cury
RamToString(Source)
s := strsize(@rambuffer)
repeat i from 0 to (s-1)
curx := TextChar(Font,byte[@rambuffer],curx,cury)
PUB StringRamBuffer(Source) ' string to rambuffer array
RamToString(Source)
result := @rambuffer ' return pointer to the rambuffer array
PUB StringCompare(Source1,Source2) | i
RamToString(Source2)
bytemove(@buffer2,@rambuffer,128) ' store string2 to buffer2
RamToString(Source1)
result := true
repeat i from 0 to strsize(@rambuffer)
if byte[@buffer2] <> byte[@rambuffer]
result := false
return
PRI StringToRam(StringN)
HubToRam(StringN << 6 + StringAddress,64) ' 64 words =128 bytes, so work in groups of 64 words
PRI RamToString(StringN)
RamToHub(StringN << 6 + StringAddress,64) ' moves to rambuffer array
' ***************** end string routines *************************
PUB DisplayLeft ' send all data to the left display
DisplayLeftRightBoth := "L" ' don't enable here, do this just before sending data
PUB DisplayRight
DisplayLeftRightBoth := "R"
PUB RamErase(n)
Filenumber := n ' erase lookup table for the ram files, effectively erases the files
' if 0 then erase all, if 1 then leave the desktop intact (assuming this was loaded first)
PUB ClearRam
Filenumber := 2 ' clear ram but leave file 0 = warm or cold boot and 1 = background picture
PUB GetCurX
result := curx ' get the text cursor position
PUB GetCurY
result := cury
PUB SetCurx(n) ' set the text cursor position
Curx := n
PUB SetCury(n)
Cury := n
PUB GetBackFontColor
result := BackFontColor
PUB GetRamLocation(n)
result := Ramlocation[n]
PUB GetFilenumber
result := filenumber
PUB SetFilenumber(n) ' reset the file number
Filenumber := n
PUB Chain(stringptr)
result := \fat.bootpartition(stringptr) ' reboot and run this program
PUB SDtoRam(stringptr) | RamAddress,remainder,sizebytes,sizewords ' copy a file from the SD card to the ram disk
OpenFileRead(stringptr)
sizebytes := fat.filesize ' size in bytes
sizewords := sizebytes >>1 ' size in words
ramaddress := NextLocation ' next free place in ram
RamSize[FileNumber] := sizewords ' ram disk works in words, not bytes
repeat sizebytes >> 9 ' do blocks of 512 bytes each as natural size of SD blocks
fat.readdata(@sdbuffer,512) ' read from SD card
SpinHubToRam(@sdbuffer,RamAddress,512) ' move to ram
RamAddress += 256 ' increment ram address by 256 (half 512 as using words)
remainder := sizebytes & %00000000_00000000_00000001_11111111 ' remainder (if not a multiple of 512)
if remainder <> 0
fat.readdata(@sdbuffer,remainder)
SpinHubToRam(@sdbuffer,RamAddress,remainder) ' send out the remainder bytes
FileClose ' close the file
result := FileNumber ' return this file
FileNumber +=1 ' point to next location
PUB FetchRamBuffer
result := @rambuffer
PUB SDBMPtoRam(stringptr) | width,height,w,i,ramaddress ' .bmp from sd to ram and convert to 2 byte ili format and reverse order along the way
' http://en.wikipedia.org/wiki/BMP_file_format scroll down about 1/3 of the way for the header formats
' get the width, height
' store these as 2 longs at the beginning of the ram file
' the bitmap format starts at the last row and works up so need to reverse the order
ramaddress := NextLocation ' get the next ram location
OpenFileRead(stringptr)
fat.readdata(@sdbuffer,$36) ' get the header 0x36 hex bytes
width := sdbuffer[$12] + sdbuffer[$13] << 8 ' only read in two bytes as never will be >64k wide or high
height := sdbuffer[$16] + sdbuffer[$17] << 8
w := width * 3 ' this number of bytes per row, and round up to nearest 4 bytes et 327 goes to 328 The size of each row is rounded up to a multiple of 4 bytes (a 32-bit DWORD) by padding.
w +=3 ' add 3 and
w &= %11111111_11111111_11111111_11111100 ' round down
SpinHubToRam(@sdbuffer,ramaddress,$36) ' store header to ram
i := $36 + ramaddress + ((height - 1) * width) ' start + header and on the last row and work back
repeat height
fat.readdata(@sdbuffer,w) ' read in the first row
CogCmd("F",@sdbuffer,@rambuffer,width)
HubToRam(i,width) ' send to ram
i -= width ' subtract the width, move up the picture
FileClose
Ramsize[Filenumber] := $36 + (width * height) ' size of bitmap file in words
result := FileNumber ' returns the current file number
FileNumber += 1 ' add 1 to filenumber
PUB DrawBMPRam(f,x,y) | rampointer,width,height ' pass the filenumber and the x and y location, searches for this file and if found, displays it
rampointer := RamLocation[f] ' find the start of the file
SpinRamToHub(@sdbuffer,rampointer,$36) ' read in the header
width := sdbuffer[$12] + sdbuffer[$13] << 8 ' only read in two bytes as never will be >64k wide or high
height := sdbuffer[$16] + sdbuffer[$17] << 8
Draw(x,y,x+width-1,y+height-1) ' set up the area to draw
RamToDisplay(RamPointer + $36,width*height) ' skip the header and display
result := (width <<16) | (height & $0000FFFF) ' return the width and height combined into a long
' ***************** end ram disk routines
PUB MergeBackgroundIconRam(stringptr,x,y,desktop,mask,icon) | i,j,k ' merge icon and background - if mask =0 then background, else icon (not classic alpha compositing but close enough)
' i is background counter, j is icon counter, k = mask counter
' assumes desktop is in ramfile 1, mask in ramfile 2, icon in ramfile 3
FileNumber := icon
SDBMPtoRam(stringptr) ' load icon into ram
Filenumber := icon ' reset filenumber
i := RamLocation[desktop] + $36 + (y*(screenwidth+1)+x) ' location of picture pixel in ram
j := RamLocation[icon] + $36 ' location of icon in pixel in ram
k := RamLocation[mask] + $36 ' location of mask pixel in ram
repeat 60
SpinRamToHub(@sdbuffer,i,59) ' get 59 pixels from background
SpinRamToHub(@rambuffer,j,59) ' get 59 icon pixels
SpinRamToHub(@buffer2,k,59) ' get words for the mask
CogCmd("X",@sdbuffer,@rambuffer,@buffer2) ' pasm version of the above 3 lines
SpinHubToRam(@sdbuffer,i,59) ' send back modified line to the picture buffer
i += screenwidth+1 ' next line in picture
j +=59 ' icon next line
k +=59 ' next line in mask (bytes) - buffer2
' old "X" command code
'repeat n from 0 to 58
' if (word[@buffer2][n] & %00000000_00011111) > %10000 ' RGB are the same, so use BBBBB, mask 5 low bits, and if > %10000
' word[@sdbuffer][n] :=word[@rambuffer][n] ' if >128 then replace with icon pixel
PUB OpenFileRead(stringptr) ' open a file for reading and display a message if it fails
result := \fat.openfile(stringptr,"R")
if fat.partitionerror <> 0 ' failed to find the file
Text(stringptr) ' print the filename
Text(result) ' print the error message
repeat ' stop the program
PUB FileClose
fat.closefile ' close the file
PUB TouchXPercent
result := TouchValue & 255 ' decode xval 0-100%
if DisplayMode == "S"
result := 100-result ' SSD1289 x runs the other way
PUB TouchSwishX | xval,oldxval,difference,average ' returns result = +ve or -ve depending on swish direction ' did finger move left or right?
SelectSPIGroup ' turn on the spi touchscreen
oldxval := 255' for startup
average := 0
repeat until (average > 30) or (average < -30)
' possibly add a decay function here for average
pause1ms(50)
xval := TouchXPercent ' decode xval 0-100%
if xval == 255 ' not touched
oldxval := 255 ' reset oldxval as well
else
if oldxval <>255 ' discard the first reading as the screen is touched
difference := xval - oldxval
'printdecimal(xval)
'printdecimal(difference)
average := average + difference '
'printdecimal(average)
'crlf
oldxval := xval ' store for next time
result := average ' return negative or positive value
PUB TouchSwishY | yval,oldyval,difference,average ' returns result = +ve or -ve depending on swish direction ' did finger move left or right?
SelectSPIGroup ' turn on the spi touchscreen
oldyval := 255' for startup
average := 0
repeat until (average > 30) or (average < -30)
' possibly add a decay function here for average
pause1ms(50)
yval := TouchYPercent ' decode xval 0-100%
if yval == 255 ' not touched
oldyval := 255 ' reset oldyval as well
else
if oldyval <>255 ' discard the first reading as the screen is touched
difference := yval - oldyval
average := average + difference '
oldyval := yval ' store for next time
result := average ' return negative or positive value
PUB TouchStart
SPI.start(3,0) ' delay,state, start clock high
PUB TextChar(FileN,ascii,x,y) | jump,size,width,height,ramaddress,xoffset,yoffset,xadvance ' read out a character from a .ifn file stored in ram
' pass Fonttable = global
' moves to next line if off the end
ramaddress := RamLocation[FileN]
jump := ReadRamLong(ramaddress,(ascii << 2) + 256) ' jump location = ascii *4 plus fonttable + 256
SpinRamToHub(@buffer2,ramaddress+jump>>1,20) ' read in the width height as a block = quicker than readramlong
size := long[@buffer2][0] ' size in pixels of this character
xadvance := long[@buffer2][5] ' amount to move to next character
'if size > 0 and ascii <> 32 ' no need to print anything if size is zero or read more font data
width := long[@buffer2][1] ' width
height := long[@buffer2][2] ' height
xoffset := long[@buffer2][3] ' xoffset to move
yoffset := long[@buffer2][4] ' yoffset to move
if (x+width -1 + xoffset) > screenwidth
crlf ' do a new line if it won't fit
x := curx
y := cury
if ascii < 32
xadvance := 0 ' add nothing if a non displaying character
if ascii == 13 ' carriage return
cr
x := curx
if ascii ==10 ' line feed (new line)
lf
y := cury
if ascii > 32 ' space not displayed
Draw(x+xoffset,y+yoffset,x+width-1+xoffset,y+height-1+yoffset) ' draw on screen
RamToDisplay(ramaddress+((jump+32)>>1),size) ' move bytes from ram out to the display
return x + xadvance
PUB crlf
cr
lf
PUB cr ' carriage return, to left margin
curx := Margin ' some characters are -1 xoffset so won't display
PUB lf ' line feed, move up one
cury += FontHeight
if cury > screenheight
ClearRectangle(0,0,screenwidth,screenheight,BackFontColor)
cury := 0 ' if off the bottom of the screen then clear screen start at the top (VT00 does scrolling)
' don't clear background here, do in calling routine
'ClearFontBackground(0,cury,screenwidth,cury + FontHeight) ' clear background line to the background color ready to draw
PUB TextT(FileN,stringptr) 'print at curx,cury using file number in ramdisk
'' Print a zero-terminated string
repeat strsize(stringptr)
curx := TextChar(FileN,byte[stringptr++],curx,cury)
PUB SetCursor(x,y)
curx := x
cury := y
PUB ReadRamLong(WordStart,Bytevalue) ' easier calculating
SpinRamToHub(@result,Wordstart+Bytevalue>>1,2)
return result
PUB LoadFont(stringptr) | filesize,i ' read a .ifn file premade in angelcode bmfont and then the vb.net program
SDtoRam(stringptr) ' font to ram disk, adds one to filenumber
SpinRamToHub(@rambuffer,RamLocation[Filenumber-1],128) ' read first 128 words =256 bytes back
FontHeight := byte[@rambuffer][251] ' font height
repeat i from 0 to 2
sdbuffer := byte[@rambuffer][35+i]
CogCmd("E",@sdbuffer,@buffer2,1) ' convert to 2 byte .ili format
BackFontColor := ( byte[@buffer2][1] << 8) | byte[@buffer2][0] ' correct order
result := FileNumber-1 ' return pointer to the font file in ram
PUB HubToRam(RamAddress,Number) ' send pixels from rambuffer hub to ram
SpinHubToRam(@rambuffer,RamAddress,Number) ' spin version
PUB RamToHub(RamAddress,Number) ' send pixels from ram to hub at rambuffer
SpinRamToHub(@rambuffer,Ramaddress, number)
PUB RamToDisplay(RamAddress,Number) ' send pixels from ram to display
SpinRamToDisplay(Ramaddress, Number)
PUB Clearscreen(color) ' uses 2 byte ILI color (see below for rgb conversion)
wordfill(@rambuffer,color,256)
Draw(0,0,screenwidth,screenheight) ' set up the area of the screen to draw in
repeat 300
SpinHubToDisplay(@rambuffer,256)
PUB RGBtoWord(R,G,B) ' convert RGB to 2 byte ILI color
sdbuffer[0] := R
sdbuffer[1] := G
sdbuffer[2] := B
CogCmd("E",@sdbuffer,@buffer2,1)
result := ( byte[@buffer2][1] << 8) | byte[@buffer2][0] ' correct order
PUB ClearRectangle(x1,y1,x2,y2,ColorWord) | i,size,remainder ' clears an area of the screen to the current font background color
size := (x2-x1+1) * (y2 - y1 +1) ' number of pixels
remainder := size & 255 ' if not a whole number of 256 pixels
repeat i from 0 to 255 ' move the background font colour to the buffer
rambuffer := ColorWord
Draw(x1,y1,x2,y2) ' set up the area of the screen to draw in
repeat size >> 8 ' 256 pixel blocks
SpinHubToDisplay(@rambuffer,256)
if remainder <> 0
SpinHubToDisplay(@rambuffer,remainder) ' do the remainder
PUB Drawline(x1,y1,x2,y2,ColorWord) ' draws a vertical or horizontal line
ClearRectangle(x1,y1,x2,y2,ColorWord) ' same as drawing a rectangle
PUB SetOrientation(e) ' change orientation true = portrait. Changes global variable 'orientation' and also screen width and height
orientation := e
ChangeOrientation(orientation)
if orientation
screenwidth :=239
screenheight :=319
else
screenwidth :=319
screenheight :=239
PUB ChangeOrientation(n) ' pass true = portrait or false = landscape, changes global variable orientation in this object
' command $0001
'8.2.4. Driver Output Control (R01h)
'R/W RS D15 D14 D13 D12 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0
'W 1 0 0 0 0 0 SM 0 SS 0 0 0 0 0 0 0 0
'SS: Select the shift direction of outputs from the source driver.
'When SS = 0, the shift direction of outputs is from S1 to S720
'When SS = 1, the shift direction of outputs is from S720 to S1.
'In addition to the shift direction, the settings for both SS and BGR bits are required to change the
'assignment of R, G, B dots to the source driver pins.
'To assign R, G, B dots to the source driver pins from S1 to S720, set SS = 0.
'To assign R, G, B dots to the source driver pins from S720 to S1, set SS = 1.
' command $0003
'R/W RS D15 D14 D13 D12 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0
'W 1 TRI DFM 0 BGR 0 0 HWM 0 ORG 0 I/D1 I/D0 AM 0 0 0
' When AM = 0, the address is updated in horizontal writing direction.
'When AM = 1, the address is updated in vertical writing direction.
'I/D[1:0] Control the address counter (AC) to automatically increase or decrease by 1 when update one pixel
' display data.
SelectMemGroup
DisplayEnable ' enable the display(s)
orientation := n
if displaymode == "I" ' ili9325
if orientation
' for origin top left and portrait mode
Displaycmd($0001,%00000001_00000000) ' $0001,$0100 set SS and SM bit 0001 0100 portrait
Displaycmd($0003,%00010000_00110000) ' $0003,$1030 set GRAM write direction and BGR=1. $0003 $1030
else
' for origin top right and landscape mode
Displaycmd($0001,%00000000_00000000) ' set SS and SM bit 0001 0000 landscape
Displaycmd($0003,%00010000_00111000) ' landscape $1028 = original but 1038 is correct - not mirror image
if displaymode == "S" ' SSD1289
if orientation
' for origin top left and portrait mode
Displaycmd($0001,$2B3F) ' Entry mode portrait
Displaycmd($0011,$6070) ' '
else
' for origin top right and landscape mode
Displaycmd($0001,$6B3F) ' landscape $2B3F = original but 6B3F is correct
Displaycmd($0011,$6838) ' landscape $1028 = original but 6838 is correct
PUB SelectMemGroup ' select group 1 for memory transfer
spi.stop ' and stop any other cogs here too if needed
LatchGroup2
DIRA |= %00000000_01011111_11111111_11111111 ' enable these pins for output
OUTA |= %00000000_00011111_00000000_00000000 ' set /rd /wr /disp wr and 161 clock high
PUB SelectSPIGroup
LatchGroup3 ' select group 2 for spi transfers
DIRA &= %11111111_11111111_11111111_11000000 ' so no clashes with the cog using P0-P2 set these as inputs and P3-P5
TouchStart ' start the touchscreen cog
' may need to set some pins here as inputs or outputs with a dira
PUB Text(stringptr) 'print at curx,cury
repeat strsize(stringptr)
Propfont_out(byte[stringptr++])
PropFontcrlf
PUB PropFontcrlf
curx := 0
cury += 32 ' new line at end of string
if cury >319 ' bottom of screen so new screen
curx:=0
cury:=0
PUB Propfont_out(ascii) | address,pixels
Draw(curx,cury,curx+15,cury+31) ' location to start drawing
address := $8000 + (ascii >> 1) << 7 ' get rom address
repeat 32 ' 32 rows per character, split in two parts
pixels := long[address] ' get rom font data
pixels := pixels >> (ascii & 1) ' shift for odd characters
repeat 16 ' 16 columns
if pixels & 1
Pixel(%00000111_11100000) ' foreground color RRRRRGGG_GGGBBBBB
else
Pixel(%00000000_00000000) ' background color
pixels := pixels >> 2 ' alternate pixels interleaved so shift 2
address += 4
curx +=16
if curx >239 ' new line
Propfontcrlf
PUB Start_ILI9325
DisplayEnable ' enable one or both displays
LatchGroup2 ' talk to this display
LCD_Reset_High
pause1ms(5)
LCD_Reset_Low
pause1ms(5)
LCD_Reset_High
LCD_CS_High
LCD_RD_High
LCD_WR_High
pause1ms(20)
LCD_CS_Low
ILI9325initiate
PUB Start_SSD1289 ' based on C driver
DisplayEnable ' enable one or both displays
LatchGroup2 ' talk to this display
LCD_Reset_High
pause1ms(5)
LCD_Reset_Low
pause1ms(10)
LCD_Reset_High
LCD_CS_High
LCD_RD_High
LCD_WR_High
pause1ms(20)
SSD1289initiate
PUB Draw(x1, y1, x2, y2) ' sets the pixel to x1,y1 and then fills the next (x2-x1)*(y2-y1) pixels
DisplayEnable ' enable one or both displays
SelectMemGroup ' select memory group
ifnot orientation ' landscape mode so swap x and y
result :=x1 ' swap x1 and y1
x1 := y1
y1 := result
result := x2 ' swap x2 and y2
x2 :=y2
y2 := result
LCD_RS_High ' after sending out commands, set RS high via latch
PUB Pixel(pixelcolor) ' send out a pixel
Lcd_Write_Fast_Data(pixelcolor) ' need to set RS high at beginning of this group (ie call Draw first)
' it is more efficent to send one Draw command then lots of pixels than sending individual pixels
' but of course, you can send draw x,y,x,y where x and y are the same and then send one pixel
PUB pause1ms(period) | clkcycles
'' Pause execution for period (in units of 1 ms).
clkcycles := ((clkfreq / _1ms * period) - 4296) #> 381 ' Calculate 1 ms time unit
waitcnt(clkcycles + cnt) ' Wait for designated time
PUB hex(value, digits) ' uses internal prop font and slow pixel drawing (for very basic debugging without an SD font)
SelectMemGroup ' reselect mem group for display output for debugging
'' Print a hexadecimal number
propfont_out("O")
propfont_out("x")
value <<= (8 - digits) << 2
repeat digits
propfont_out(lookupz((value <-= 4) & $F : "0".."9", "A".."F"))
PropFontcrlf
PUB SetWarmBoot
long[@rambuffer][5] := $5761726D ' spell Warm in hex, start at 5 as zero gets corrupted
byte[@rambuffer][10] := DisplayMode ' store the current display mode
HubToRam(0,20) ' send words to external ram (ramdiskstart is zero)
ClearScreen(0) ' so user sees something happen as there is a bit of a delay rebooting
reboot
PUB WarmColdBoot ' store warm or cold boot - reserve first 20 bytes in sram for interfacing with programs
' Ramdisk file 0 is the boot string Warm or Cold
' ramdisk file 1 is the background
Ramtohub(0,20) ' read words from the ram
if long[@rambuffer][5] == $5761726D
result := true
else
result := false
long[@rambuffer][5] := $FFFFFFFF ' erase previous warm/cold boot
byte[@rambuffer][10] := DisplayMode ' store display mode
HubToRam(0,20) ' send words to external ram (ramdiskstart is zero)
RamSize[0] := 20 ' reserve 20 bytes
RamLocation[0] := 0
Filenumber := 1
PRI GetDisplayMode
Ramtohub(0,20) ' read words from the ram
result := byte[@rambuffer][10] ' get the display mode
PUB ManualValues
RamLocation[0] := 0
RamSize[0] := $14 ' put in values manually - 20 bytes for warm boot
RamLocation[1] := $14
RamSize[1] := $12C36 ' 76800 for the desktop picture
FileNumber := 2 ' running a program not a desktop
PRI LatchGroup1
CogCmd("O",%00001111,0,0) ' set all group pins high
CogCmd("A",%11111110,0,0) 'set this one low
PRI LatchGroup2
CogCmd("O",%00001111,0,0) ' set all group pins high
CogCmd("A",%11111101,0,0) 'set this one low
PRI LatchGroup3
CogCmd("O",%00001111,0,0) ' set all group pins high
CogCmd("A",%11111011,0,0) 'set this one low
PRI LatchGroup4
CogCmd("O",%00001111,0,0) ' set all group pins high
CogCmd("A",%11110111,0,0) 'set this one low
PRI Latch_ResetLow
CogCmd("A",%11101111,0,0) 'set this one low
PRI Latch_ResetHigh
CogCmd("O",%00010000,0,0) ' set this pin high
PRI Latch_DisplayL ' display on the left
CogCmd("O",%01100000,0,0) ' both displays high
CogCmd("A",%11011111,0,0) 'set this one low
PRI Latch_DisplayR ' display on the right
CogCmd("O",%01100000,0,0) ' both displays high
CogCmd("A",%10111111,0,0) 'set this one low
PRI Latch_DisplayB ' both displays for debugging
CogCmd("A",%10011111,0,0) 'set both displays low
PRI Latch_RS_Low
CogCmd("A",%01111111,0,0) 'set this one low
PRI Latch_RS_High
CogCmd("O",%10000000,0,0) ' set this pin high
PRI Latch_AllHighStart ' all pins high
CogCmd("O",%11111111,0,0) ' set all pins high (disabled)
PRI Load161(address)
CogCmd("Z",0,address,0) ' call pasm version, faster, changes the latch but the next PUB always resets it anyway
PRI DisplayEnable ' pass DisplayRightLeftBoth
case DisplayLeftRightBoth
"R":Latch_DisplayR
"L":Latch_DisplayL
"B":Latch_DisplayB
PRI SpinHubToRam(hubaddress, ramaddress, number) | i ' P0-P15 are data lines, P16 is /RD, P17 is /WR
Load161(ramaddress) ' load the 161 counters
LatchGroup2 ' group 2 is block data transfers
CogCmd("S",hubaddress,ramaddress,number) ' pasm alternative to code below
DIRA &= %11111111_11100000_00000000_00000000 ' float all pins used
PRI SpinRamToHub(hubaddress,ramaddress,number) |i ' P0-P15 are data lines, P16 is /RD, P17 is /WR
Load161(ramaddress) ' load the 161 counters
LatchGroup2 ' group 2 is block data transfers
CogCmd("T",hubaddress,ramaddress,number) ' pasm alternative to code below
DIRA &= %11111111_11100000_00000000_00000000 ' float all pins used
PRI SpinRamToDisplay(ramaddress,number) | i ' ramaddress, number of words (pixels), display = 1 or 2
Load161(ramaddress) ' load the 161 counters
LatchGroup2 ' group 2 is block data transfers
DisplayEnable ' latch output one or both displays
CogCmd("U",0,0,number) ' pasm alternative to code below, much faster
DIRA &= %11111111_11100000_00000000_00000000 ' float all pins used
PRI SpinHubToDisplay(hubaddress,number)|i
'LatchGroup2 ' not needed as always do a Draw first, also no need to do displayenable as this is in a draw as well
CogCmd("V",hubaddress,0,number) ' pasm version of code below
DIRA &= %11111111_11100000_00000000_00000000 ' float all pins used
PRI NextLocation ' get the next free location in ram based on last files size
if FileNumber == 0
RamLocation[0] := 0 ' work out start of this file based on last file size
else
RamLocation[FileNumber] := RamLocation[FileNumber - 1] + RamSize[FileNumber - 1] ' get location in ram
result := RamLocation[FileNumber]
PRI TouchX
SPI.SHIFTOUT(Touch_DataIn, Touch_Clock, 5, 8 , %1101_0000) ' reads x from 500 to 3700 (off < 500 )
result := SPI.SHIFTIN(Touch_DataOut,Touch_Clock,2, 12)
PRI TouchY
SPI.SHIFTOUT(Touch_DataIn, Touch_Clock, 5, 8 , %1001_0000) ' reads y from 400 to 3800 (off > 3800 )
result := SPI.SHIFTIN(Touch_DataOut,Touch_Clock,2, 12)
PRI TouchValue | xval,yval,x,y ' returns % values 0-100% (or 255 for no touch)
xval := TouchX
yval := TouchY
if (xval => 500) and (yval =< 3800) ' both have to be valid for a touch
pause1ms(2) ' tiny delay then read again as the first reads may not have the correct value
xval := TouchX
yval := TouchY
if (xval => 500) and (yval =< 3800) ' and if valid again, now more likely this value is correct
x := xval - 440 ' scale 0-3360
x /= 32 ' scale 0-100
x <#= 100 ' max 100
x #>=0 ' min 0
x := 100 - x ' so left edge on left
y := yval -420 ' scale 0-3180
y /= 32 ' scale 0-100
y <#= 100 ' max 100
y #>=0 ' min 0
y := 100-y ' so 0 is top left
result := (y <<8) | x ' return 00000000_00000000_YYYYYYYY_XXXXXXXX
if (xval < 500) or (xval >3800)
result := $0000FFFF ' invalid number(s)
PRI ILI9325initiate
' ************* Start Initial Sequence **********
Displaycmd($00E5,$78F0) ' set SRAM internal timing
Displaycmd($0001,$0100) ' set SS and SM bit 0001 0100 portrait
Displaycmd($0002,$0700) ' set 1 line inversion
Displaycmd($0003,$1030) ' set GRAM write direction and BGR=1. $0003 $1030
Displaycmd($0004,$0000) ' Resize register
Displaycmd($0008,$0207) ' set the back porch and front porch
Displaycmd($0009,$0000) ' set non-display area refresh cycle ISC[3:0]
Displaycmd($000A,$0000) ' FMARK function
Displaycmd($000C,$0000) ' RGB interface setting
Displaycmd($000D,$0000) ' Frame marker Position
Displaycmd($000F,$0000) ' RGB interface polarity
' *************Power On sequence ****************//
Displaycmd($0010,$0000) ' SAP, BT[3:0], AP, DSTB, SLP, STB
Displaycmd($0011,$0007) ' DC1[2:0], DC0[2:0], VC[2:0]
Displaycmd($0012,$0000) ' VREG1OUT voltage
Displaycmd($0013,$0000) ' VDV[4:0] for VCOM amplitude
Displaycmd($0007,$0001)
pause1ms(50) ' Dis-charge capacitor power voltage
Displaycmd($0010,$1090) ' 1490//SAP, BT[3:0], AP, DSTB, SLP, STB
Displaycmd($0011,$0227) ' DC1[2:0], DC0[2:0], VC[2:0]
pause1ms(50) ' delay
Displaycmd($0012,$001F) ' 001C// Internal reference voltage= Vci;
pause1ms(50) ' delay
Displaycmd($0013,$1500) ' $1000//1400 Set VDV[4:0] for VCOM amplitude 1A00
Displaycmd($0029,$0027) ' $0012 //001a Set VCM[5:0] for VCOMH //$0025 0034
Displaycmd($002B,$000D) ' Set Frame Rate 000C
pause1ms(50) ' delay
Displaycmd($0020,$0000) ' GRAM horizontal Address
Displaycmd($0021,$0000) ' GRAM Vertical Address
'
Adjust the Gamma Curve
//
Displaycmd($0030,$0000)
Displaycmd($0031,$0707)
Displaycmd($0032,$0307)
Displaycmd($0035,$0200)
Displaycmd($0036,$0008)
Displaycmd($0037,$0004)
Displaycmd($0038,$0000)
Displaycmd($0039,$0707)
Displaycmd($003C,$0002)
Displaycmd($003D,$1D04)
'
Set GRAM area
//
Displaycmd($0050,$0000) ' Horizontal GRAM Start Address
Displaycmd($0051,$00EF) ' Horizontal GRAM End Address
Displaycmd($0052,$0000) ' Vertical GRAM Start Address
Displaycmd($0053,$013F) ' Vertical GRAM Start Address
Displaycmd($0060,$A700) ' Gate Scan Line
Displaycmd($0061,$0001) ' NDL,VLE, REV
Displaycmd($006A,$0000) ' set scrolling line
'
Partial Display Control
/
Displaycmd($0080,$0000)
Displaycmd($0081,$0000)
Displaycmd($0082,$0000)
Displaycmd($0083,$0000)
Displaycmd($0084,$0000)
Displaycmd($0085,$0000)
' //
Panel Control
//
Displaycmd($0090,$0010)
Displaycmd($0092,$0600)
Displaycmd($0007,$0133) ' 262K color and display ON
ChangeOrientation(true) ' default to portrait
PRI SSD1289initiate
{ ILIcmddelay($0000,$0001) 'Turn Oscillator on POR-$0000
ILIcmddelay($0003,$A8A4) 'Power control (1) POR-$6664
ILIcmddelay($000C,$0000) 'Power control (2) POR- ?
ILIcmddelay($000D,$080C) 'Power control (3) POR-$0009
ILIcmddelay($000E,$2B00) 'Power control (4) POR-$3200
ILIcmddelay($001E,$00B0) 'Power control (5) POR-$0029
ILIcmddelay($0001,$4B3F) 'Driver output control, *landscape?? $6B3F* POR-$433F
ILIcmddelay($0002,$0600) 'LCD drive AC control POR-$0400
ILIcmddelay($0010,$0000) 'Sleep Mode sleep mode off POR-$0001
ILIcmddelay($0011,$6070) 'Entry Mode, *landscape? $4030* POR-$6830
ILIcmddelay($0005,$0000) 'Compare Register (1) POR-$0000
ILIcmddelay($0006,$0000) 'Compare Register (2) POR-$0000
ILIcmddelay($0016,$EF1C) 'Horizontal Porch POR-$EFC1
ILIcmddelay($0017,$0003) 'Vertical Porch POR-$0003
ILIcmddelay($0007,$0033) 'Display Control POR-$0000
ILIcmddelay($000B,$D308) 'Frame cycle Control POR-$D308
ILIcmddelay($000F,$0000) 'Gate Scan start position POR-$0000
ILIcmddelay($0041,$0000) 'Vertical Scroll Control (1) POR-$0000
ILIcmddelay($0042,$0000) 'Vertical Scroll Control (2) POR-$0000
ILIcmddelay($0048,$0000) 'First Window Start POR-$0000
ILIcmddelay($0049,$013F) 'First Window End POR-$013F
ILIcmddelay($004A,$0000) 'Second Window Start POR-$0000
ILIcmddelay($004B,$0000) 'Second Window End POR-$013F
ILIcmddelay($0044,$EF00) 'Horizontal Ram Address Postion POR-$EF00
ILIcmddelay($0045,$0000) 'Vertical Ram Address Start POR-$0000
ILIcmddelay($0046,$013F) 'Vertical Ram Address End POR-$013F
ILIcmddelay($0023,$0000) 'RAM write data mask (1) POR-$0000
ILIcmddelay($0024,$0000) 'RAM write data mask (2) POR-$0000
ILIcmddelay($0025,$8000) 'not in datasheet?
ILIcmddelay($004f,0) 'RAM Y address counter POR-$0000
ILIcmddelay($004e,0) 'RAM X address counter POR-$0000
Lcd_Write_Com($0022)
}
PRI Displaycmd(c,d) ' instruction in one method
Lcd_Write_Com(c) ' send out a word
Lcd_Write_Data(d)
PRI Lcd_Write_Com(LCDlong)
LCD_RS_low ' can do rs first then cs - better for latch board
LCD_CS_Low ' not needed for the ILI9325 but the SSD1289 needs this
LCD_Writ_Bus(LCDlong)
LCD_CS_High ' not needed for the ILI9325
RE: Pin22. The ram_driver will always be loaded and controlling p22 exclusively. PASM driver is running in a separate cog from the caller, so the caller *spin code* should have p22 set as input. This way it won't interfere with PASM driver running in other cog. Sending a command to the ram_driver Hi-Z all pins in Spin Cog.
I'll look through the code in a bit. Things are a bit hectic right now.
Need to take a few days from this cache driver. It's probably something simple, but it could take quite a while to figure out. With all the string functions and chaining I'm not too concerned with getting C running. Others will find this useful but I'm not perusing C right now because it requires me learning a new language(s).
On my to-do list, getting the MCP3202 running to my liking. In my previous attempt, I was creating 2 SPI objects and using one to handle ADC... Worked great until re-draw where a few samples get lost. The key is TDM. Need to run the maths, but for decent sampling rate I'm thinking 100ksps will be sufficient, could even go to 89ksps. I'm probably over-thinking this since the last attempt was on the MCPboard. The 373 *I'm using 374s* should be sufficiently fast to keep up. Need to work on this a bit more.
*edit*
Walked away and figured out part of the issue. I have the cache partially running.. Can't be far now!
Re pin 22 I think it comes down to the way the propeller chip works. All the cogs are "wire or" with respect to dira and outa, so if a pasm routine sets the pin as an output then that overrides any other cog, including spin.
Re your bug, I am getting close. I think it is at the end of the 161 code, need to add this line.
or outa,maskP16P20 ' P16-P20 high in case the next thing is a group change and P16/P17 are low and hence upset the ram chip
After the 161 has loaded, P16-18 could be in any state. But P16 and 17 are used as the /rd and /wr lines on the ram chip. So if you then do a group change, and either of those pins is low (particularly the /wr pin) it could corrupt the ram. I think that might be why it is an intermittent bug because it depends on what was the last address you sent.
I am very close now to a single "S" and "T" command where the entire command is in pasm. Give me half an hour to test all the programs we have to be double sure it works.
VAR
byte FileNumber ' 0 to 31
byte DisplayMode ' type of display 0 = ILI9325, 1= SSD1289, 2 etc
byte DisplayLeftRightBoth ' "R", "L" or "B"
word ScreenWidth ' either 240 in portrait or 320 in landscape
word ScreenHeight ' 320 in portrait or 240 in landscape
long orientation ' true is portrait
long curx, cury
byte FontHeight ' size of the current loaded font (pixels = fontsize *.75)
word BackFontColor ' the background color in RRRRRGGG GGGBBBBB format
long StringAddress ' start of string address
long wcboot ' most recent boot warm or cold
'
OBJ
spi: "SPI_ASM" ' thanks to Beau - spi driver for touch screen
fat: "SD-MMC_FATEngine.spin" ' Kye's latest sd driver and no RTC
DAT
RamLocation long $0[31] ' 32 file ram locatinos
RamSize long $0[31] ' File sizes in words - NOT bytes (divide byte by 2)
sdbuffer byte $0[1024] ' 1024 byte buffer for sd card interface and also for sending one row 320 x3 = 960 bytes max picture size
buffer2 byte $0[512] ' 512 general purpose hub buffer
rambuffer word $0[256] ' 512 byte (256 word) buffer easier for using with words
PUB BeginDesktop(DisplayType) ' store the type of display once by the desktop program
DisplayMode := DisplayType ' global variable I = ILI9325, S=SSD1289
Begin(true) ' start up everything running the desktop
result := wcboot
PUB BeginProgram ' used by all programs except the desktop program
Begin(false) ' any program except the desktop
result := wcboot
PRI Begin(Desktop) |n ' desktop true if run from the desktop program, false for other programs
start_ram_cog ' * start cog first *start the external ram / display driver in a cog, needed for many routines so always first
Latch_AllHighStart ' all Latch pins high and enables pin 22
if desktop == false
DisplayMode := GetDisplayMode ' reads "I" or "S" etc off the ram ** must have run desktop at least once first to store this ***
'DisplayMode := "I" ' temp for debugging, using the ILI9325
SelectMemGroup ' select group2
DisplayLeftRightBoth := "B" ' "R", "L" or "B"
if displaymode == "I"
Start_ILI9325 ' start the display 0 = ILI9325, 1 = SSD1289
if displaymode == "S"
Start_SSD1289
wcboot := WarmColdBoot ' get the last warm or cold boot value
SetOrientation(true) ' in portrait (true) or landscape mode (false), must be before clearscreen otherwise don't know screensize
if wcboot == false and Desktop == true ' no need to redisplay every time if worked the first time
Text(string("SD")) ' string to send, uses internal prop font so can see something if the SD fails to mount
fat.fatEngineStart( _dopin, _clkpin, _dipin, _cspin, _wppin, _cdpin, _rtcres1, _rtcres2, _rtcres3)
fat.mountPartition(0) ' mount the sd card
if desktop == false ' running a program, this is called without knowing what the ramlocation and ramsize values are
ManualValues ' coming from outside so reset ramsize and ramlocation values for 0 and 1
SetCursor(0,0) ' cursor for debugging to 0,0
if desktop == false
SelectSPIGroup ' selects this group and starts the cog
'PUB BasicTesting
' text(string("Basic testing"))
' sdbuffer[0] := 6
' SpinHubToRam(@sdbuffer,5000,10) ' move data to ram
' sdbuffer[0] := 7 ' change the value
' SpinRamToHub(@sdbuffer,5000,10) ' read back the original value
' hex(sdbuffer[0],2)
' 'Drawline(100,100,110,100,$FFFF)
' Clearrectangle(120,120,129,129,$FFFF)
' Draw(100,100,109,109)
' repeat 100
' pixel(%11111000_00000000)
' ************** string routines stored in external memory so can have arrays and large text files **********************
' to make things simpler, one array StringArray. any number of entries, each up to 128 characters
' pass the source and destination numbers
' TextArray(256) = 256 entries for text files etc
' first entry is destination, second entrie(s) are the source
' Commands are
' StringDim ' reserves memory 256*128, increments filenumber, adds filesize
' StringLoadFile(Name,S) ' read a .txt file off the SD card and stores in TextArray, new string entry is crlf
' StringSaveFile(Name,S,F) ' save .txt file from TextArray to SD card adding in crlf, Start to Finish
' StringClear(Dest) ' clear a string, pass string number
' StringAddChar(Dest,N) ' adds a character to a string at end
' StringSubChar(Dest) ' remove a character from the end
' StringCopy(Dest,Src) ' copy string from one location to another
' StringStore(Dest,Src) ' convert Prop string("mystring") to string stored in external memory
' StringLeft(Dest,Src,N) ' Basic Left$
' StringMid(Dest,Src,L,N) ' Basic Mid$
' StringRight ' Basic Right$
' StringInstr ' Basic Instr
' StringUcase(Dest,Src) ' Basic Ucase$
' StringLcase ' Basic Lcase$
' StringLen ' Length
' StringStr ' string representation of a number
' StringHex(Dest,N) ' number to hex string
' StringBin ' number to binary string
' StringVal ' value (hex is 0x, binary is 0b)
' StringJoin(Dest,Src1,Src2) ' same as Basic + with strings
' StringInsert(Dest,Src1,Src2,n) ' inserts src2 into src1 starting at position n, (1 is first char) and moves to dest
' StringDebug(n) ' print using the green propeller font string number n
' StringLen() ' same as strsize but works on external memory strings
' StringRambuffer ' moves string to rambuffer array and returns result = location of this array
' StringCompare(Source1,Source2) ' returns true if strings equal in length and characters
PUB StringTest ' test all the string routines
'StringDim(10)
'StringStore(5,string("Hello World"))
'StringDebug(5)
'StringClear(5)
'StringAddChar(5,"1")
'StringAddChar(5,"2")
'StringAddChar(5,"3")
'StringAddChar(5,"4")
'StringDebug(5)
'StringSubChar(5) ' remove one character from the right, useful for backspace etc
'StringDebug(5)
'StringCopy(7,5) ' copy
'StringDebug(7)
'StringLeft(7,7,5) ' source and destination the same or different
'StringDebug(7)
'StringMid(8,5,7,3) ' dest, source, starting byte, number of bytes. first byte is 1
'StringDebug(8)
'StringRight(3,5,4) ' dest, source, number of bytes
'StringDebug(3)
'Hex(StringInstr(5,2,"o"),2) ' find this character starting at 2
'StringBin(2,255) ' convert a number to binary always 32 + 2 characters
'StringDebug(2)
'StringHex(4,100) ' always 8 + 2 characters
'StringDebug(4)
'StringDec(9,-20) ' always 11 characters
'StringDebug(9)
'StringStr(9,-7) ' Basic str$ removes leading zeroes, and first character is either - or a space
'StringDebug(9)
'StringStore(5,string("65")) ' test string number to number
'hex(StringVal(5),8)
'StringStore(5,string("0x1234")) ' test string hex to number
'hex(StringVal(5),8)
'StringStore(5,string("0b101")) ' test string binary to number
'hex(StringVal(5),8)
'StringStore(1,string("Hello "))
'StringStore(2,string("World"))
'StringJoin(1,1,2) ' string1 = string1 +string2 (shows dest and a source can be the same - or could be different)
'StringDebug(1)
'StringUcase(1,1) ' Ucase test
'StringDebug(1)
'StringLcase(1,1) ' Lcase test
'StringDebug(1)
'StringStore(1,string(" 123")) ' test the trim function
'StringTrim(1,1)
'StringDebug(1)
'StringStore(1,string("TEST6.TXT")) ' file name
'StringStore(2,string("Line a of text")) ' store some lines of text
'StringStore(3,string("Line b of text"))
'StringStore(4,string("Line c of text"))
'StringSavefile(1,2,4) ' filename is 1, store lines 2 to 4
'StringStore(1,string("TEST6.TXT")) ' file name
'StringLoadFile(1,2) ' read back the file created above.
'Stringdebug(2)
'Stringdebug(3)
'Stringdebug(4)
'repeat
PUB StringDim(n) ' reserve space for a string array, n= number of strings, 128 max bytes per string
Ramsize[Filenumber] := n << 6 ' * 64 as this is in words, not bytes
StringAddress := NextLocation ' get the location of the string array
Filenumber += 1
PUB StringStore(StringN,Source) ' store a propeller string("test") to array number Dest
bytemove(@rambuffer, Source, (strsize(Source) + 1)) ' move to rambuffer array
StringToRam(StringN)
PUB StringDebug(StringN) ' print using the green propeller font - useful if sd card not working so no fonts loaded
RamToString(StringN)
Text(@rambuffer) ' print it out
PUB StringClear(StringN)
bytefill(@rambuffer,0,128) ' only really need to do the first one but may help debugging to completely clear the string
StringToRam(StringN)
PUB StringAddChar(StringN,c) | s ' add c to the string
if c >31 and c <127 ' a character that can be displayed
RamToString(StringN) ' get the string
s := strsize(@rambuffer)
byte[@rambuffer] := c
byte[@rambuffer][s + 1] := 0 ' add in the new zero (can't assume array is cleared)
StringToRam(StringN) ' store back to ram
PUB StringSubChar(StringN) ' remove one character from the right same as strings.left(strings.len - 1)
RamToString(StringN)
if strsize(@rambuffer) <> 0 ' if at least one character
byte[@rambuffer][strsize(@rambuffer)-1] :=0
StringToRam(StringN)
PUB StringCopy(Dest,Source) ' copy string from source to dest
RamToString(Source)
StringToRam(Dest)
PUB StringLeft(Dest,Source,n) ' Basic Left$
RamToString(Source)
byte[@rambuffer][n] := 0 ' move the terminator
StringToRam(Dest) ' move back to ram
PUB StringMid(Dest,Source,start,n) | i ' Basic Mid$
RamToString(Source)
repeat i from 0 to n-1
byte[@rambuffer] := byte[@rambuffer][i+start-1] ' possibly could use bytemove here
byte[@rambuffer][n] := 0 'add in the new zero terminator
StringToRam(Dest)
PUB StringRight(Dest,Source,n) | i,m
RamToString(Source)
m := strsize(@rambuffer) - n
repeat i from 0 to n ' Basic Right$. include the terminator
byte[@rambuffer] := byte[@rambuffer][i+m]
StringToRam(Dest)
PUB StringInstr(Source,Start,c) | i ' find c in the string starting at start
RamToString(Source)
result := 0 ' return 0 if not found
repeat i from (start-1) to strsize(@rambuffer)
if byte[@rambuffer] == c
result := i+1 ' 1 is first character
quit
PUB StringBin(Dest,n) ' convert n to a binary value at Dest. Prefix 0b
repeat result from 33 to 2
byte[@rambuffer][result] := ((n & 1) + "0")
n >>= 1
byte[@rambuffer][34] := 0 ' add zero terminator
byte[@rambuffer][0] := "0" ' prefix 0b same as some versions of C
byte[@rambuffer][1] := "b"
StringToRam(Dest) ' store
PUB StringHex(Dest,n) ' number to hex, always 8 characters
repeat result from 9 to 2
byte[@rambuffer][result] := lookupz((n & $F): "0".."9", "A".."F")
n >>= 4
byte[@rambuffer][10] := 0 ' zero at end
byte[@rambuffer][0] := "0" ' prefix 0x same as C
byte[@rambuffer][1] := "x"
StringToRam(Dest)
PUB StringDec(Dest,n) | sign ' number to decimal string, always 11 characters
sign := "+"
if(n < 0)
sign := "-"
if(n == negx)
bytemove(@rambuffer, string("-2147483648"), 11) ' max negative number special case
else
repeat result from 10 to 1
byte[@rambuffer][result] := ((||(n // 10)) + "0")
n /= 10
byte[@rambuffer][0] := sign ' sign at the front
byte[@rambuffer][11] := 0 ' zero terminator
StringToRam(Dest)
PUB StringStr(Dest,n) | found ' same as Basic STR - similar to Dec above but sign is either space or negative, and leading zeros removed
StringDec(Dest,n) ' number preserved in rambuffer
found := 10 ' if answer was zero don't remove leading zero
repeat result from 1 to 10
if byte[@rambuffer][result] <> "0"
found := result
quit
repeat result from 1 to 11
byte[@rambuffer][result] := byte[@rambuffer][result +found -1] ' shuffle over if needed
if byte[@rambuffer][0] == "+"
byte[@rambuffer][0] := " " ' Basic syntax, - or blank
StringToRam(Dest) ' resend to external ram
PUB StringVal(Source) | sign,i,k ' returns value of the string, hex 0x, binary 0b, decimal + or - or space or no leading character just a number
RamToString(Source)
if byte[@rambuffer][1] == "x" ' this is a hex number
repeat k from 2 to strsize(@rambuffer) -1 ' leading two characters always 0x
result := ((result <- 4) + (byte[@rambuffer][k] & $F))
return
if byte[@rambuffer][1] == "b" ' this is a binary number
repeat k from 2 to strsize(@rambuffer) -1 ' leading two characters always 0b
result := ((result << 1) + (byte[@rambuffer][k] & 1))
return
' else if got here this must be a decimal number. It could start with a number(no leading sign), or a space, or a + or a -
sign := byte[@rambuffer][0]
i := 0 ' start location
if sign == "+" or sign == "-" or sign == " " ' this is a decimal number ** must start with a space or a + or a -**
i := 1 ' leading character so start here
repeat k from i to (strsize(@rambuffer)-1) ' if ever use right to left, don't use step -1, spin puts it in if start is less than finish and if there is a step -1 it is one less loop
result := ((result *10) + byte[@rambuffer][k] & $F) 'very clever, the & $F works because zero is hex0x30, the pre multiply shifts previous numbers to the left so can do left to right. Easier to understand with Kye's binary converter with the left shift
if sign == "-"
result *= -1 ' if negative then negate
PUB StringJoin(Dest,Source1,Source2) ' Dest$= Source1$+Source2$
RamToString(Source2)
bytemove(@buffer2,@rambuffer,128) ' store string2 to buffer2
RamToString(Source1) ' get first string
bytemove(@rambuffer+strsize(@rambuffer),@buffer2,strsize(@buffer2)+1) ' thanks to Kye for this one
StringToRam(Dest) ' store back to ram
PUB StringUcase(Dest,Source)
RamToString(Source)
repeat result from 0 to strsize(@rambuffer)
if ((byte[@rambuffer][result] => "a") and (byte[@rambuffer][result] =< "z"))
byte[@rambuffer][result] -= 32
StringToRam(Dest)
PUB StringLcase(Dest,Source)
RamToString(Source)
repeat result from 0 to strsize(@rambuffer)
if ((byte[@rambuffer][result] => "A") and (byte[@rambuffer][result] =< "Z"))
byte[@rambuffer][result] += 32
StringToRam(Dest)
PUB StringTrim(Dest,Source)
RamToString(Source)
if byte[@rambuffer][0] == "+" or byte[@rambuffer][0] == " " or byte[@rambuffer][0] == "-"
repeat result from 0 to strsize(@rambuffer)
byte[@rambuffer][result] := byte[@rambuffer][result+1] ' delete the first +,- or space
StringToRam(Dest)
PUB StringSaveFile(Filen,Start,Finish) | i,j ' filen might be string array 0, start could be 1, finish could be 4
RamToString(Filen) ' fetch the filename
fat.newfile(@rambuffer) ' create the file, if not working use \fat. for debugging, see PUB openfileread for an example
result := \fat.openfile(@rambuffer,"W") ' open for writing return error if there is one
repeat i from Start to Finish
RamToString(i)
j := strsize(@rambuffer)
byte[@rambuffer][j] := 13 ' carriage return
byte[@rambuffer][j+1] :=10 ' line feed
byte[@rambuffer][j+2] := 0 ' end of new line
fat.writedata(@rambuffer,j+2) ' write this line
FileClose ' close the file
PUB StringLoadFile(Filen,Stringnumber)| s,r,c,t,z ' read filename, new line every crlf. Must have enough space reserved in the string array
RamToString(Filen) ' copy a file from the SD card to the ram disk
OpenFileRead(@rambuffer)
s := 0 ' sd counter
r := 0 ' rambuffer counter
t := 0 ' total number of bytes
z := fat.filesize ' size of the file
repeat (z >> 9) +1 ' include any remainder as +1, do blocks of 512 bytes each as natural size of SD blocks
fat.readdata(@sdbuffer,512) ' read from SD card
repeat s from 0 to 511
c := byte[@sdbuffer] ' get the next byte
if t =< z ' might read in one more 512 byte block to ensure gets any remainder
if c == 13 or r > 126 ' end of a line so new string, or string too long
byte[@rambuffer][r] := 0 ' end of string terminator
StringToRam(Stringnumber)
stringnumber +=1 ' new string number
r := 0 ' start at beginning of rambuffer
if c >31 and c < 127
byte[@rambuffer][r] := c ' store if a valid character
r +=1
t +=1 ' number of bytes read
FileClose ' close the file
PUB StringLen(Source) ' string length
RamToString(Source)
result := strsize(@rambuffer)
PUB StringPrint(Font,Source) | i,s ' print this string using using font in memory and curx and cury
RamToString(Source)
s := strsize(@rambuffer)
repeat i from 0 to (s-1)
curx := TextChar(Font,byte[@rambuffer],curx,cury)
PUB StringRamBuffer(Source) ' string to rambuffer array
RamToString(Source)
result := @rambuffer ' return pointer to the rambuffer array
PUB StringCompare(Source1,Source2) | i
RamToString(Source2)
bytemove(@buffer2,@rambuffer,128) ' store string2 to buffer2
RamToString(Source1)
result := true
repeat i from 0 to strsize(@rambuffer)
if byte[@buffer2] <> byte[@rambuffer]
result := false
return
PRI StringToRam(StringN)
HubToRam(StringN << 6 + StringAddress,64) ' 64 words =128 bytes, so work in groups of 64 words
PRI RamToString(StringN)
RamToHub(StringN << 6 + StringAddress,64) ' moves to rambuffer array
' ***************** end string routines *************************
PUB DisplayLeft ' send all data to the left display
DisplayLeftRightBoth := "L" ' don't enable here, do this just before sending data
PUB DisplayRight
DisplayLeftRightBoth := "R"
PUB RamErase(n)
Filenumber := n ' erase lookup table for the ram files, effectively erases the files
' if 0 then erase all, if 1 then leave the desktop intact (assuming this was loaded first)
PUB ClearRam
Filenumber := 2 ' clear ram but leave file 0 = warm or cold boot and 1 = background picture
PUB GetCurX
result := curx ' get the text cursor position
PUB GetCurY
result := cury
PUB SetCurx(n) ' set the text cursor position
Curx := n
PUB SetCury(n)
Cury := n
PUB GetBackFontColor
result := BackFontColor
PUB GetRamLocation(n)
result := Ramlocation[n]
PUB GetFilenumber
result := filenumber
PUB SetFilenumber(n) ' reset the file number
Filenumber := n
PUB Chain(stringptr)
result := \fat.bootpartition(stringptr) ' reboot and run this program
PUB SDtoRam(stringptr) | RamAddress,remainder,sizebytes,sizewords ' copy a file from the SD card to the ram disk
OpenFileRead(stringptr)
sizebytes := fat.filesize ' size in bytes
sizewords := sizebytes >>1 ' size in words
ramaddress := NextLocation ' next free place in ram
RamSize[FileNumber] := sizewords ' ram disk works in words, not bytes
repeat sizebytes >> 9 ' do blocks of 512 bytes each as natural size of SD blocks
fat.readdata(@sdbuffer,512) ' read from SD card
SpinHubToRam(@sdbuffer,RamAddress,512) ' move to ram
RamAddress += 256 ' increment ram address by 256 (half 512 as using words)
remainder := sizebytes & %00000000_00000000_00000001_11111111 ' remainder (if not a multiple of 512)
if remainder <> 0
fat.readdata(@sdbuffer,remainder)
SpinHubToRam(@sdbuffer,RamAddress,remainder) ' send out the remainder bytes
FileClose ' close the file
result := FileNumber ' return this file
FileNumber +=1 ' point to next location
PUB FetchRamBuffer
result := @rambuffer
PUB SDBMPtoRam(stringptr) | width,height,w,i,ramaddress ' .bmp from sd to ram and convert to 2 byte ili format and reverse order along the way
' http://en.wikipedia.org/wiki/BMP_file_format scroll down about 1/3 of the way for the header formats
' get the width, height
' store these as 2 longs at the beginning of the ram file
' the bitmap format starts at the last row and works up so need to reverse the order
ramaddress := NextLocation ' get the next ram location
OpenFileRead(stringptr)
fat.readdata(@sdbuffer,$36) ' get the header 0x36 hex bytes
width := sdbuffer[$12] + sdbuffer[$13] << 8 ' only read in two bytes as never will be >64k wide or high
height := sdbuffer[$16] + sdbuffer[$17] << 8
w := width * 3 ' this number of bytes per row, and round up to nearest 4 bytes et 327 goes to 328 The size of each row is rounded up to a multiple of 4 bytes (a 32-bit DWORD) by padding.
w +=3 ' add 3 and
w &= %11111111_11111111_11111111_11111100 ' round down
SpinHubToRam(@sdbuffer,ramaddress,$36) ' store header to ram
i := $36 + ramaddress + ((height - 1) * width) ' start + header and on the last row and work back
repeat height
fat.readdata(@sdbuffer,w) ' read in the first row
CogCmd("F",@sdbuffer,@rambuffer,width)
HubToRam(i,width) ' send to ram
i -= width ' subtract the width, move up the picture
FileClose
Ramsize[Filenumber] := $36 + (width * height) ' size of bitmap file in words
result := FileNumber ' returns the current file number
FileNumber += 1 ' add 1 to filenumber
PUB DrawBMPRam(f,x,y) | rampointer,width,height ' pass the filenumber and the x and y location, searches for this file and if found, displays it
rampointer := RamLocation[f] ' find the start of the file
SpinRamToHub(@sdbuffer,rampointer,$36) ' read in the header
width := sdbuffer[$12] + sdbuffer[$13] << 8 ' only read in two bytes as never will be >64k wide or high
height := sdbuffer[$16] + sdbuffer[$17] << 8
Draw(x,y,x+width-1,y+height-1) ' set up the area to draw
RamToDisplay(RamPointer + $36,width*height) ' skip the header and display
result := (width <<16) | (height & $0000FFFF) ' return the width and height combined into a long
' ***************** end ram disk routines
PUB MergeBackgroundIconRam(stringptr,x,y,desktop,mask,icon) | i,j,k ' merge icon and background - if mask =0 then background, else icon (not classic alpha compositing but close enough)
' i is background counter, j is icon counter, k = mask counter
' assumes desktop is in ramfile 1, mask in ramfile 2, icon in ramfile 3
FileNumber := icon
SDBMPtoRam(stringptr) ' load icon into ram
Filenumber := icon ' reset filenumber
i := RamLocation[desktop] + $36 + (y*(screenwidth+1)+x) ' location of picture pixel in ram
j := RamLocation[icon] + $36 ' location of icon in pixel in ram
k := RamLocation[mask] + $36 ' location of mask pixel in ram
repeat 60
SpinRamToHub(@sdbuffer,i,59) ' get 59 pixels from background
SpinRamToHub(@rambuffer,j,59) ' get 59 icon pixels
SpinRamToHub(@buffer2,k,59) ' get words for the mask
CogCmd("X",@sdbuffer,@rambuffer,@buffer2) ' pasm version of the above 3 lines
SpinHubToRam(@sdbuffer,i,59) ' send back modified line to the picture buffer
i += screenwidth+1 ' next line in picture
j +=59 ' icon next line
k +=59 ' next line in mask (bytes) - buffer2
' old "X" command code
'repeat n from 0 to 58
' if (word[@buffer2][n] & %00000000_00011111) > %10000 ' RGB are the same, so use BBBBB, mask 5 low bits, and if > %10000
' word[@sdbuffer][n] :=word[@rambuffer][n] ' if >128 then replace with icon pixel
PUB OpenFileRead(stringptr) ' open a file for reading and display a message if it fails
result := \fat.openfile(stringptr,"R")
if fat.partitionerror <> 0 ' failed to find the file
Text(stringptr) ' print the filename
Text(result) ' print the error message
repeat ' stop the program
PUB FileClose
fat.closefile ' close the file
PUB TouchXPercent
result := TouchValue & 255 ' decode xval 0-100%
if DisplayMode == "S"
result := 100-result ' SSD1289 x runs the other way
PUB TouchSwishX | xval,oldxval,difference,average ' returns result = +ve or -ve depending on swish direction ' did finger move left or right?
SelectSPIGroup ' turn on the spi touchscreen
oldxval := 255' for startup
average := 0
repeat until (average > 30) or (average < -30)
' possibly add a decay function here for average
pause1ms(50)
xval := TouchXPercent ' decode xval 0-100%
if xval == 255 ' not touched
oldxval := 255 ' reset oldxval as well
else
if oldxval <>255 ' discard the first reading as the screen is touched
difference := xval - oldxval
'printdecimal(xval)
'printdecimal(difference)
average := average + difference '
'printdecimal(average)
'crlf
oldxval := xval ' store for next time
result := average ' return negative or positive value
PUB TouchSwishY | yval,oldyval,difference,average ' returns result = +ve or -ve depending on swish direction ' did finger move left or right?
SelectSPIGroup ' turn on the spi touchscreen
oldyval := 255' for startup
average := 0
repeat until (average > 30) or (average < -30)
' possibly add a decay function here for average
pause1ms(50)
yval := TouchYPercent ' decode xval 0-100%
if yval == 255 ' not touched
oldyval := 255 ' reset oldyval as well
else
if oldyval <>255 ' discard the first reading as the screen is touched
difference := yval - oldyval
average := average + difference '
oldyval := yval ' store for next time
result := average ' return negative or positive value
PUB TouchStart
SPI.start(3,0) ' delay,state, start clock high
PUB TextChar(FileN,ascii,x,y) | jump,size,width,height,ramaddress,xoffset,yoffset,xadvance ' read out a character from a .ifn file stored in ram
' pass Fonttable = global
' moves to next line if off the end
ramaddress := RamLocation[FileN]
jump := ReadRamLong(ramaddress,(ascii << 2) + 256) ' jump location = ascii *4 plus fonttable + 256
SpinRamToHub(@buffer2,ramaddress+jump>>1,20) ' read in the width height as a block = quicker than readramlong
size := long[@buffer2][0] ' size in pixels of this character
xadvance := long[@buffer2][5] ' amount to move to next character
'if size > 0 and ascii <> 32 ' no need to print anything if size is zero or read more font data
width := long[@buffer2][1] ' width
height := long[@buffer2][2] ' height
xoffset := long[@buffer2][3] ' xoffset to move
yoffset := long[@buffer2][4] ' yoffset to move
if (x+width -1 + xoffset) > screenwidth
crlf ' do a new line if it won't fit
x := curx
y := cury
if ascii < 32
xadvance := 0 ' add nothing if a non displaying character
if ascii == 13 ' carriage return
cr
x := curx
if ascii ==10 ' line feed (new line)
lf
y := cury
if ascii > 32 ' space not displayed
Draw(x+xoffset,y+yoffset,x+width-1+xoffset,y+height-1+yoffset) ' draw on screen
RamToDisplay(ramaddress+((jump+32)>>1),size) ' move bytes from ram out to the display
return x + xadvance
PUB crlf
cr
lf
PUB cr ' carriage return, to left margin
curx := Margin ' some characters are -1 xoffset so won't display
PUB lf ' line feed, move up one
cury += FontHeight
if cury > screenheight
ClearRectangle(0,0,screenwidth,screenheight,BackFontColor)
cury := 0 ' if off the bottom of the screen then clear screen start at the top (VT00 does scrolling)
' don't clear background here, do in calling routine
'ClearFontBackground(0,cury,screenwidth,cury + FontHeight) ' clear background line to the background color ready to draw
PUB TextT(FileN,stringptr) 'print at curx,cury using file number in ramdisk
'' Print a zero-terminated string
repeat strsize(stringptr)
curx := TextChar(FileN,byte[stringptr++],curx,cury)
PUB SetCursor(x,y)
curx := x
cury := y
PUB ReadRamLong(WordStart,Bytevalue) ' easier calculating
SpinRamToHub(@result,Wordstart+Bytevalue>>1,2)
return result
PUB LoadFont(stringptr) | filesize,i ' read a .ifn file premade in angelcode bmfont and then the vb.net program
SDtoRam(stringptr) ' font to ram disk, adds one to filenumber
SpinRamToHub(@rambuffer,RamLocation[Filenumber-1],128) ' read first 128 words =256 bytes back
FontHeight := byte[@rambuffer][251] ' font height
repeat i from 0 to 2
sdbuffer := byte[@rambuffer][35+i]
CogCmd("E",@sdbuffer,@buffer2,1) ' convert to 2 byte .ili format
BackFontColor := ( byte[@buffer2][1] << 8) | byte[@buffer2][0] ' correct order
result := FileNumber-1 ' return pointer to the font file in ram
PUB HubToRam(RamAddress,Number) ' send pixels from rambuffer hub to ram
SpinHubToRam(@rambuffer,RamAddress,Number) ' spin version
PUB RamToHub(RamAddress,Number) ' send pixels from ram to hub at rambuffer
SpinRamToHub(@rambuffer,Ramaddress, number)
PUB RamToDisplay(RamAddress,Number) ' send pixels from ram to display
SpinRamToDisplay(Ramaddress, Number)
PUB Clearscreen(color) ' uses 2 byte ILI color (see below for rgb conversion)
wordfill(@rambuffer,color,256)
Draw(0,0,screenwidth,screenheight) ' set up the area of the screen to draw in
repeat 300
SpinHubToDisplay(@rambuffer,256)
PUB RGBtoWord(R,G,B) ' convert RGB to 2 byte ILI color
sdbuffer[0] := R
sdbuffer[1] := G
sdbuffer[2] := B
CogCmd("E",@sdbuffer,@buffer2,1)
result := ( byte[@buffer2][1] << 8) | byte[@buffer2][0] ' correct order
PUB ClearRectangle(x1,y1,x2,y2,ColorWord) | i,size,remainder ' clears an area of the screen to the current font background color
size := (x2-x1+1) * (y2 - y1 +1) ' number of pixels
remainder := size & 255 ' if not a whole number of 256 pixels
repeat i from 0 to 255 ' move the background font colour to the buffer
rambuffer := ColorWord
Draw(x1,y1,x2,y2) ' set up the area of the screen to draw in
repeat size >> 8 ' 256 pixel blocks
SpinHubToDisplay(@rambuffer,256)
if remainder <> 0
SpinHubToDisplay(@rambuffer,remainder) ' do the remainder
PUB Drawline(x1,y1,x2,y2,ColorWord) ' draws a vertical or horizontal line
ClearRectangle(x1,y1,x2,y2,ColorWord) ' same as drawing a rectangle
PUB SetOrientation(e) ' change orientation true = portrait. Changes global variable 'orientation' and also screen width and height
orientation := e
ChangeOrientation(orientation)
if orientation
screenwidth :=239
screenheight :=319
else
screenwidth :=319
screenheight :=239
PUB ChangeOrientation(n) ' pass true = portrait or false = landscape, changes global variable orientation in this object
' command $0001
'8.2.4. Driver Output Control (R01h)
'R/W RS D15 D14 D13 D12 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0
'W 1 0 0 0 0 0 SM 0 SS 0 0 0 0 0 0 0 0
'SS: Select the shift direction of outputs from the source driver.
'When SS = 0, the shift direction of outputs is from S1 to S720
'When SS = 1, the shift direction of outputs is from S720 to S1.
'In addition to the shift direction, the settings for both SS and BGR bits are required to change the
'assignment of R, G, B dots to the source driver pins.
'To assign R, G, B dots to the source driver pins from S1 to S720, set SS = 0.
'To assign R, G, B dots to the source driver pins from S720 to S1, set SS = 1.
' command $0003
'R/W RS D15 D14 D13 D12 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0
'W 1 TRI DFM 0 BGR 0 0 HWM 0 ORG 0 I/D1 I/D0 AM 0 0 0
' When AM = 0, the address is updated in horizontal writing direction.
'When AM = 1, the address is updated in vertical writing direction.
'I/D[1:0] Control the address counter (AC) to automatically increase or decrease by 1 when update one pixel
' display data.
SelectMemGroup
DisplayEnable ' enable the display(s)
orientation := n
if displaymode == "I" ' ili9325
if orientation
' for origin top left and portrait mode
Displaycmd($0001,%00000001_00000000) ' $0001,$0100 set SS and SM bit 0001 0100 portrait
Displaycmd($0003,%00010000_00110000) ' $0003,$1030 set GRAM write direction and BGR=1. $0003 $1030
else
' for origin top right and landscape mode
Displaycmd($0001,%00000000_00000000) ' set SS and SM bit 0001 0000 landscape
Displaycmd($0003,%00010000_00111000) ' landscape $1028 = original but 1038 is correct - not mirror image
if displaymode == "S" ' SSD1289
if orientation
' for origin top left and portrait mode
Displaycmd($0001,$2B3F) ' Entry mode portrait
Displaycmd($0011,$6070) ' '
else
' for origin top right and landscape mode
Displaycmd($0001,$6B3F) ' landscape $2B3F = original but 6B3F is correct
Displaycmd($0011,$6838) ' landscape $1028 = original but 6838 is correct
PUB SelectMemGroup ' select group 1 for memory transfer
spi.stop ' and stop any other cogs here too if needed
LatchGroup2
DIRA |= %00000000_01011111_11111111_11111111 ' enable these pins for output
OUTA |= %00000000_00011111_00000000_00000000 ' set /rd /wr /disp wr and 161 clock high
PUB SelectSPIGroup
LatchGroup3 ' select group 2 for spi transfers
DIRA &= %11111111_11111111_11111111_11000000 ' so no clashes with the cog using P0-P2 set these as inputs and P3-P5
TouchStart ' start the touchscreen cog
' may need to set some pins here as inputs or outputs with a dira
PUB Text(stringptr) 'print at curx,cury
repeat strsize(stringptr)
Propfont_out(byte[stringptr++])
PropFontcrlf
PUB PropFontcrlf
curx := 0
cury += 32 ' new line at end of string
if cury >319 ' bottom of screen so new screen
curx:=0
cury:=0
PUB Propfont_out(ascii) | address,pixels
Draw(curx,cury,curx+15,cury+31) ' location to start drawing
address := $8000 + (ascii >> 1) << 7 ' get rom address
repeat 32 ' 32 rows per character, split in two parts
pixels := long[address] ' get rom font data
pixels := pixels >> (ascii & 1) ' shift for odd characters
repeat 16 ' 16 columns
if pixels & 1
Pixel(%00000111_11100000) ' foreground color RRRRRGGG_GGGBBBBB
else
Pixel(%00000000_00000000) ' background color
pixels := pixels >> 2 ' alternate pixels interleaved so shift 2
address += 4
curx +=16
if curx >239 ' new line
Propfontcrlf
PUB Start_ILI9325
DisplayEnable ' enable one or both displays
LatchGroup2 ' talk to this display
LCD_Reset_High
pause1ms(5)
LCD_Reset_Low
pause1ms(5)
LCD_Reset_High
LCD_CS_High
LCD_RD_High
LCD_WR_High
pause1ms(20)
LCD_CS_Low
ILI9325initiate
PUB Start_SSD1289 ' based on C driver
DisplayEnable ' enable one or both displays
LatchGroup2 ' talk to this display
LCD_Reset_High
pause1ms(5)
LCD_Reset_Low
pause1ms(10)
LCD_Reset_High
LCD_CS_High
LCD_RD_High
LCD_WR_High
pause1ms(20)
SSD1289initiate
PUB Draw(x1, y1, x2, y2) ' sets the pixel to x1,y1 and then fills the next (x2-x1)*(y2-y1) pixels
DisplayEnable ' enable one or both displays
SelectMemGroup ' select memory group
ifnot orientation ' landscape mode so swap x and y
result :=x1 ' swap x1 and y1
x1 := y1
y1 := result
result := x2 ' swap x2 and y2
x2 :=y2
y2 := result
LCD_RS_High ' after sending out commands, set RS high via latch
PUB Pixel(pixelcolor) ' send out a pixel
Lcd_Write_Fast_Data(pixelcolor) ' need to set RS high at beginning of this group (ie call Draw first)
' it is more efficent to send one Draw command then lots of pixels than sending individual pixels
' but of course, you can send draw x,y,x,y where x and y are the same and then send one pixel
PUB pause1ms(period) | clkcycles
'' Pause execution for period (in units of 1 ms).
clkcycles := ((clkfreq / _1ms * period) - 4296) #> 381 ' Calculate 1 ms time unit
waitcnt(clkcycles + cnt) ' Wait for designated time
PUB hex(value, digits) ' uses internal prop font and slow pixel drawing (for very basic debugging without an SD font)
SelectMemGroup ' reselect mem group for display output for debugging
'' Print a hexadecimal number
propfont_out("O")
propfont_out("x")
value <<= (8 - digits) << 2
repeat digits
propfont_out(lookupz((value <-= 4) & $F : "0".."9", "A".."F"))
PropFontcrlf
PUB SetWarmBoot
long[@rambuffer][5] := $5761726D ' spell Warm in hex, start at 5 as zero gets corrupted
byte[@rambuffer][10] := DisplayMode ' store the current display mode
HubToRam(0,20) ' send words to external ram (ramdiskstart is zero)
ClearScreen(0) ' so user sees something happen as there is a bit of a delay rebooting
reboot
PUB WarmColdBoot ' store warm or cold boot - reserve first 20 bytes in sram for interfacing with programs
' Ramdisk file 0 is the boot string Warm or Cold
' ramdisk file 1 is the background
Ramtohub(0,20) ' read words from the ram
if long[@rambuffer][5] == $5761726D
result := true
else
result := false
long[@rambuffer][5] := $FFFFFFFF ' erase previous warm/cold boot
byte[@rambuffer][10] := DisplayMode ' store display mode
HubToRam(0,20) ' send words to external ram (ramdiskstart is zero)
RamSize[0] := 20 ' reserve 20 bytes
RamLocation[0] := 0
Filenumber := 1
PRI GetDisplayMode
Ramtohub(0,20) ' read words from the ram
result := byte[@rambuffer][10] ' get the display mode
PUB ManualValues
RamLocation[0] := 0
RamSize[0] := $14 ' put in values manually - 20 bytes for warm boot
RamLocation[1] := $14
RamSize[1] := $12C36 ' 76800 for the desktop picture
FileNumber := 2 ' running a program not a desktop
PRI LatchGroup1
CogCmd("O",%00001111,0,0) ' set all group pins high
CogCmd("A",%11111110,0,0) 'set this one low
PRI LatchGroup2
CogCmd("O",%00001111,0,0) ' set all group pins high
CogCmd("A",%11111101,0,0) 'set this one low
PRI LatchGroup3
CogCmd("O",%00001111,0,0) ' set all group pins high
CogCmd("A",%11111011,0,0) 'set this one low
PRI LatchGroup4
CogCmd("O",%00001111,0,0) ' set all group pins high
CogCmd("A",%11110111,0,0) 'set this one low
PRI Latch_ResetLow
CogCmd("A",%11101111,0,0) 'set this one low
PRI Latch_ResetHigh
CogCmd("O",%00010000,0,0) ' set this pin high
PRI Latch_DisplayL ' display on the left
CogCmd("O",%01100000,0,0) ' both displays high
CogCmd("A",%11011111,0,0) 'set this one low
PRI Latch_DisplayR ' display on the right
CogCmd("O",%01100000,0,0) ' both displays high
CogCmd("A",%10111111,0,0) 'set this one low
PRI Latch_DisplayB ' both displays for debugging
CogCmd("A",%10011111,0,0) 'set both displays low
PRI Latch_RS_Low
CogCmd("A",%01111111,0,0) 'set this one low
PRI Latch_RS_High
CogCmd("O",%10000000,0,0) ' set this pin high
PRI Latch_AllHighStart ' all pins high
CogCmd("O",%11111111,0,0) ' set all pins high (disabled)
PRI Load161(address)
CogCmd("Z",0,address,0) ' call pasm version, faster, changes the latch but the next PUB always resets it anyway
PRI DisplayEnable ' pass DisplayRightLeftBoth
case DisplayLeftRightBoth
"R":Latch_DisplayR
"L":Latch_DisplayL
"B":Latch_DisplayB
PRI SpinHubToRam(hubaddress, ramaddress, number) ' move data from hub to ram
CogCmd("S",hubaddress,ramaddress,number) ' pasm block move
PRI SpinRamToHub(hubaddress,ramaddress,number) ' move data from ram to hub
CogCmd("T",hubaddress,ramaddress,number) ' pasm block move
PRI SpinRamToDisplay(ramaddress,number) ' ramaddress, number of words (pixels), display = 1 or 2
Load161(ramaddress) ' load the 161 counters
LatchGroup2 ' group 2 is block data transfers
DisplayEnable ' latch output one or both displays
CogCmd("U",0,0,number) ' pasm alternative to code below, much faster
PRI SpinHubToDisplay(hubaddress,number)|i
'LatchGroup2 ' not needed as always do a Draw first, also no need to do displayenable as this is in a draw as well
CogCmd("V",hubaddress,0,number) ' pasm version of code below
PRI NextLocation ' get the next free location in ram based on last files size
if FileNumber == 0
RamLocation[0] := 0 ' work out start of this file based on last file size
else
RamLocation[FileNumber] := RamLocation[FileNumber - 1] + RamSize[FileNumber - 1] ' get location in ram
result := RamLocation[FileNumber]
PRI TouchX
SPI.SHIFTOUT(Touch_DataIn, Touch_Clock, 5, 8 , %1101_0000) ' reads x from 500 to 3700 (off < 500 )
result := SPI.SHIFTIN(Touch_DataOut,Touch_Clock,2, 12)
PRI TouchY
SPI.SHIFTOUT(Touch_DataIn, Touch_Clock, 5, 8 , %1001_0000) ' reads y from 400 to 3800 (off > 3800 )
result := SPI.SHIFTIN(Touch_DataOut,Touch_Clock,2, 12)
PRI TouchValue | xval,yval,x,y ' returns % values 0-100% (or 255 for no touch)
xval := TouchX
yval := TouchY
if (xval => 500) and (yval =< 3800) ' both have to be valid for a touch
pause1ms(2) ' tiny delay then read again as the first reads may not have the correct value
xval := TouchX
yval := TouchY
if (xval => 500) and (yval =< 3800) ' and if valid again, now more likely this value is correct
x := xval - 440 ' scale 0-3360
x /= 32 ' scale 0-100
x <#= 100 ' max 100
x #>=0 ' min 0
x := 100 - x ' so left edge on left
y := yval -420 ' scale 0-3180
y /= 32 ' scale 0-100
y <#= 100 ' max 100
y #>=0 ' min 0
y := 100-y ' so 0 is top left
result := (y <<8) | x ' return 00000000_00000000_YYYYYYYY_XXXXXXXX
if (xval < 500) or (xval >3800)
result := $0000FFFF ' invalid number(s)
PRI ILI9325initiate
' ************* Start Initial Sequence **********
Displaycmd($00E5,$78F0) ' set SRAM internal timing
Displaycmd($0001,$0100) ' set SS and SM bit 0001 0100 portrait
Displaycmd($0002,$0700) ' set 1 line inversion
Displaycmd($0003,$1030) ' set GRAM write direction and BGR=1. $0003 $1030
Displaycmd($0004,$0000) ' Resize register
Displaycmd($0008,$0207) ' set the back porch and front porch
Displaycmd($0009,$0000) ' set non-display area refresh cycle ISC[3:0]
Displaycmd($000A,$0000) ' FMARK function
Displaycmd($000C,$0000) ' RGB interface setting
Displaycmd($000D,$0000) ' Frame marker Position
Displaycmd($000F,$0000) ' RGB interface polarity
' *************Power On sequence ****************//
Displaycmd($0010,$0000) ' SAP, BT[3:0], AP, DSTB, SLP, STB
Displaycmd($0011,$0007) ' DC1[2:0], DC0[2:0], VC[2:0]
Displaycmd($0012,$0000) ' VREG1OUT voltage
Displaycmd($0013,$0000) ' VDV[4:0] for VCOM amplitude
Displaycmd($0007,$0001)
pause1ms(50) ' Dis-charge capacitor power voltage
Displaycmd($0010,$1090) ' 1490//SAP, BT[3:0], AP, DSTB, SLP, STB
Displaycmd($0011,$0227) ' DC1[2:0], DC0[2:0], VC[2:0]
pause1ms(50) ' delay
Displaycmd($0012,$001F) ' 001C// Internal reference voltage= Vci;
pause1ms(50) ' delay
Displaycmd($0013,$1500) ' $1000//1400 Set VDV[4:0] for VCOM amplitude 1A00
Displaycmd($0029,$0027) ' $0012 //001a Set VCM[5:0] for VCOMH //$0025 0034
Displaycmd($002B,$000D) ' Set Frame Rate 000C
pause1ms(50) ' delay
Displaycmd($0020,$0000) ' GRAM horizontal Address
Displaycmd($0021,$0000) ' GRAM Vertical Address
'
Adjust the Gamma Curve
//
Displaycmd($0030,$0000)
Displaycmd($0031,$0707)
Displaycmd($0032,$0307)
Displaycmd($0035,$0200)
Displaycmd($0036,$0008)
Displaycmd($0037,$0004)
Displaycmd($0038,$0000)
Displaycmd($0039,$0707)
Displaycmd($003C,$0002)
Displaycmd($003D,$1D04)
'
Set GRAM area
//
Displaycmd($0050,$0000) ' Horizontal GRAM Start Address
Displaycmd($0051,$00EF) ' Horizontal GRAM End Address
Displaycmd($0052,$0000) ' Vertical GRAM Start Address
Displaycmd($0053,$013F) ' Vertical GRAM Start Address
Displaycmd($0060,$A700) ' Gate Scan Line
Displaycmd($0061,$0001) ' NDL,VLE, REV
Displaycmd($006A,$0000) ' set scrolling line
'
Partial Display Control
/
Displaycmd($0080,$0000)
Displaycmd($0081,$0000)
Displaycmd($0082,$0000)
Displaycmd($0083,$0000)
Displaycmd($0084,$0000)
Displaycmd($0085,$0000)
' //
Panel Control
//
Displaycmd($0090,$0010)
Displaycmd($0092,$0600)
Displaycmd($0007,$0133) ' 262K color and display ON
ChangeOrientation(true) ' default to portrait
PRI SSD1289initiate
{ ILIcmddelay($0000,$0001) 'Turn Oscillator on POR-$0000
ILIcmddelay($0003,$A8A4) 'Power control (1) POR-$6664
ILIcmddelay($000C,$0000) 'Power control (2) POR- ?
ILIcmddelay($000D,$080C) 'Power control (3) POR-$0009
ILIcmddelay($000E,$2B00) 'Power control (4) POR-$3200
ILIcmddelay($001E,$00B0) 'Power control (5) POR-$0029
ILIcmddelay($0001,$4B3F) 'Driver output control, *landscape?? $6B3F* POR-$433F
ILIcmddelay($0002,$0600) 'LCD drive AC control POR-$0400
ILIcmddelay($0010,$0000) 'Sleep Mode sleep mode off POR-$0001
ILIcmddelay($0011,$6070) 'Entry Mode, *landscape? $4030* POR-$6830
ILIcmddelay($0005,$0000) 'Compare Register (1) POR-$0000
ILIcmddelay($0006,$0000) 'Compare Register (2) POR-$0000
ILIcmddelay($0016,$EF1C) 'Horizontal Porch POR-$EFC1
ILIcmddelay($0017,$0003) 'Vertical Porch POR-$0003
ILIcmddelay($0007,$0033) 'Display Control POR-$0000
ILIcmddelay($000B,$D308) 'Frame cycle Control POR-$D308
ILIcmddelay($000F,$0000) 'Gate Scan start position POR-$0000
ILIcmddelay($0041,$0000) 'Vertical Scroll Control (1) POR-$0000
ILIcmddelay($0042,$0000) 'Vertical Scroll Control (2) POR-$0000
ILIcmddelay($0048,$0000) 'First Window Start POR-$0000
ILIcmddelay($0049,$013F) 'First Window End POR-$013F
ILIcmddelay($004A,$0000) 'Second Window Start POR-$0000
ILIcmddelay($004B,$0000) 'Second Window End POR-$013F
ILIcmddelay($0044,$EF00) 'Horizontal Ram Address Postion POR-$EF00
ILIcmddelay($0045,$0000) 'Vertical Ram Address Start POR-$0000
ILIcmddelay($0046,$013F) 'Vertical Ram Address End POR-$013F
ILIcmddelay($0023,$0000) 'RAM write data mask (1) POR-$0000
ILIcmddelay($0024,$0000) 'RAM write data mask (2) POR-$0000
ILIcmddelay($0025,$8000) 'not in datasheet?
ILIcmddelay($004f,0) 'RAM Y address counter POR-$0000
ILIcmddelay($004e,0) 'RAM X address counter POR-$0000
Lcd_Write_Com($0022)
}
PRI Displaycmd(c,d) ' instruction in one method
Lcd_Write_Com(c) ' send out a word
Lcd_Write_Data(d)
PRI Lcd_Write_Com(LCDlong)
LCD_RS_low ' can do rs first then cs - better for latch board
LCD_CS_Low ' not needed for the ILI9325 but the SSD1289 needs this
LCD_Writ_Bus(LCDlong)
LCD_CS_High ' not needed for the ILI9325 but the SSD1289 needs this
PRI Lcd_Write_Data(LCDlong)
LCD_RS_High ' can do rs first then cs
LCD_CS_Low ' not needed for the ILI9325 but the SSD1289 needs this
LCD_Writ_Bus(LCDlong)
LCD_CS_High ' not needed for the ILI9325 but the SSD1289 needs this
PRI Lcd_Write_Fast_Data(LCDlong) ' write RS elsewhere then skip the RS above as this is a latch output and takes longer
LCD_CS_Low ' not needed for the ILI9325 but the SSD1289 needs this
LCD_Writ_Bus(LCDlong)
LCD_CS_High ' not needed for the ILI9325 but the SSD1289 needs this
PRI LCD_Writ_Bus(LCDLong)
'LCDLong &= %00000000_00000000_11111111_11111111 ' mask to a word. Not needed if care taken always to send a word value
OUTA &= %11111111_11111111_00000000_00000000 ' set P0-P15 to zero ready to OR
OUTA |= LCDLong ' merge with the word to output
LCD_WR_Low ' send out the data
LCD_WR_High
PRI LCD_WR_Low
OUTA &= %11111111_11111011_11111111_11111111 ' p18 low
PRI LCD_WR_High
OUTA |= %00000000_00000100_00000000_00000000 ' p18 high
PRI LCD_RD_High ' not used, set high in hardware
PRI LCD_CS_Low
OUTA &= %11111111_11110111_11111111_11111111 ' p19 low
PRI LCD_CS_High
OUTA |= %00000000_00001000_00000000_00000000 ' p19 high
I have the driver working. Passes all tests. The upside is it allows sanity check once board is built. Need to bundle this as a debugging app. Cache driver still needs a ton of work. I'll work on getting Steve's burst read working. Fingers crossed. Here's the working S and T + OH for calling. Needs modding to stuff cog functions into cache extended functions. I have some idea this should work, just need to get it finished!
get_values mov ptr, hubaddr ' get hub address
mov count, line_size ' get ram address
mov dirb,latchvalue ' make copy of latchvalue here for restore
or outa,maskP16P20 ' set control pins high
or dira,maskP16P20 ' set control pins P16-P20 as outputs
get_values_ret ret
set373 or outa,maskP22 ' pin 22 high
or dira,#%1_11111111 ' enable pins 0-7 and 8 as outputs
and outa,maskP0P8low ' P0-P8 low
or outa,latchvalue ' send out the data
or outa,maskP8 ' P8 high, clocks out data
andn outa,maskP22 ' pin 22 low
set373_ret ret
set161 mov latchvalue,#%11111110 ' group 1, displays all off
call #set373 ' send out to the latch
and outa,maskP0P20low '%11111111_11100000_00000000_00000000
or dira,maskP0P20 '%00000000_00011111_11111111_11111111
or outa,vmaddr ' send out ramaddr
or outa,maskP20 ' clock high
or outa,maskP19 ' load high
or outa,maskP16P20 ' set control pins high
mov latchvalue,#%11111101 ' group 2, displays all off
call #set373 ' send out to the latch
set161_ret ret
pasmhubtoram call #get_values ' get hubaddr,ramaddr,len and set control pins
call #set161
hubtoram_loop and outa,maskP16P31 '%11111111_11111111_00000000_00000000 ' clear for output
rdword data_16,ptr ' get the word from hub
and data_16,maskP0P15 ' mask to a word only
or outa,data_16 ' send out the byte to P0-P15
andn outa,maskP17 ' set mem write low
add ptr,#2 ' increment by 2 bytes = 1 word. Put this here for small delay while writes
or outa,maskP17 ' mem write high
andn outa,maskP20 ' clock 161 low
or outa,maskP20 ' clock 161 high
djnz count,#hubtoram_loop ' loop this many times
mov latchvalue, dirb ' restore latch value
call #set373
and dira, maskP0P20low ' tristates all the common pins, leaves P22 as is though
' jmp #done ' tristate pins and listen for commands
' command T
pasmramtohub call #get_values ' get hubaddr,ramaddr,len and set control pins
call #set161
and dira,maskP16P31 '%11111111_11111111_00000000_00000000 inputs
andn outa,maskP16 ' memory /rd low
ramtohub_loop mov data_16,ina ' get the data
wrword data_16,ptr ' move data to hub
andn outa,maskP20 ' clock 161 low
or outa,maskP20 ' clock 161 high
add ptr,#2 ' increment the hub address
djnz count,#ramtohub_loop
or outa,maskP16 ' memory /rd high
mov latchvalue, dirb ' restore latch value
call #set373
and dira,maskP0P20low ' tristates all the common pins, leaves P22 as is though
'
rd_cache_line_ret ret
' variables
pasm_n long 0 ' general purpose value
data_16 long 0 ' general purpose value
latchvalue long %00000000_00000000_00000000_00000000 ' current 373 value
' constants
'Zero long %00000000_00000000_00000000_00000000 ' used in several places
maskP0P2low long %11111111_11111111_11111111_11111000 ' P0-P2 low
maskP0P20 long %00000000_00011111_11111111_11111111 ' P0-P18 enabled for output plus P19,P20
maskP0P18low long %11111111_11111000_00000000_00000000 ' P0-P18 low
maskP16 long %00000000_00000001_00000000_00000000 ' pin 16
maskP17 long %00000000_00000010_00000000_00000000 ' pin 17
maskP18 long %00000000_00000100_00000000_00000000 ' pin 18
maskP19 long %00000000_00001000_00000000_00000000 ' pin 19
maskP20 long %00000000_00010000_00000000_00000000 ' pin 20
maskP22 long %00000000_01000000_00000000_00000000 ' pin 22
maskP16P31 long %11111111_11111111_00000000_00000000 ' pin 16 to pin 31
maskP0P15 long %00000000_00000000_11111111_11111111 ' for masking words
maskP16P20 long %00000000_00011111_00000000_00000000
maskP0P20low long %11111111_11100000_00000000_00000000 ' for returning all group pins HiZ
maskP16P17P20 long %00000000_00010011_00000000_00000000
maskP0P8low long %11111111_11111111_11111110_00000000 ' P0-P7 low
maskP8 long %00000000_00000000_00000001_00000000 ' pin 8
Comments
Re running C from the sram. Well, let's say we keep group 3 really simple. It runs the touch part of the touchscreen and the interface to the extension board, and we leave out the A to D chip for the moment. Along comes a random cache access. If the touchscreen SPI /CS line goes high at a random time, will it matter? I don't have the spec sheets on that chip but maybe it won't care. It shouldn't be in the middle of a data transfer because that code is a one line call to a cog function from the C program so that shouldn't trigger a cache change during that access. And if we offload the interface to the extension board to a cog routine, then again, calling that routine will be one line of C code so a cache change is not going to happen during a prop to prop data transfer.
So I think the key is to get all the things that talk on group 3 to be running from cogs rather than from spin. Ideally from the one cog that is running the sram access because that can have a simple lock - if that is running then the cache request has to wait. That makes sense anyway because then you can use the same driver for spin and C.
So fundamentally this is about moving things from spin to cog - the set161 code, the entire routines to read where a finger is, and a block transfer to or from the second prop.
Those are quite achievable tasks.
You know, if that works, this could well be the fastest cache solution for C that anyone has done.
re: Touch Adc. It's been a while since I looked at the datasheet, but I'm fairly sure changing the /cs at a random time won't be a problem. We may need to flush on the next access but I will investigate further.
I think the big step is getting as much as possible into PASM. I've been working on set161 and having problems *mostly because I keep getting interrupted, lol* This may take a while.
As far as catch driver speed, I think this will be the fastest cache solution yet.. The 137 driver already beat others and I can see this being slightly faster.
*edit*
I've been chugging away and I think I'm getting somewhere. The Spin portion needs some work but I think the PASM driver is coming along. Still need to check the 161 is loading proper address *which requires removing chips, I'll get to that* I THINK the problem was missing "ret" causing calls to go crazy...
Not.it
There were heaps of bugs - no wonder it would not work. Anyway, attached is the latest Touch object.
Now I think you have changed some of the load routine for your displays so it does the orientation correctly. Can you pls let me know what those changes are so I can put them in this object?
The only change necessary was landscape for the SSD.. not
I've been thinking about modifying so display writes can be inverted. Gives a nice background effect IMO.
I'll crack the new touch.spin open and give it a look! Glad you got it fixed, the bugs were really gettin me!
Some problems with one of the displays not working. I was expecting that as the new code trashes the latch output.
Sorry, spent a fair bit of time on this one and can't find the bug. The pasm version stops one of the displays working. But they are being treated the same so this should not happen. If I set all the latch outputs high at the end of a 161 update, both displays stop working and that shouldn't happen either as the next thing in the code is to select group 2. And furthermore, with a spin version of the same thing it works fine. So I think it is an accident that one display worked. I went through and modified the code quite a bit so that there are no static variables like latchvalue that are expected to stay static. In other words, every change of the latch requires a new sending of the data.
It is all very strange especially since I have been running code that deliberately sends data to both displays at once, and so either no displays or both displays should be on. I've not got any code that selects one or the other in this debugging code.
So until it is clear what is going on, the touch object has to update the 161 chips with spin code.
I'm having issues with the new touch.spin. The ssd isn't working *as you mentioned* I'll keep thinking about this. About the revisions, it's getting closer. I think one important part right now is getting most bus access to the driver. Should be possible.
This one kinda bugs me. P22 should ONLY change from cog. Makes things WAY easier. I think a better way is to start the cog, then latch from init..
Not tested, but I had this working. Just need to send things to "done" instead of init. Still need to think about this too.'
*edit*
Okay, I've done a fair bit of work on this. There's some bugs, I think the big one is:
I think it's better to pre-set pins, then make outputs.
*editsssss*
Very nice! It seems quite a bit faster. I should be able to get this version going with the cache driver... (not today)
This is the line with the bug:
I'm sure all the experts here will spot it straight away. My defence is that I was flipping back and forth between pasm and spin and the spin code is
So, attached is the new touch.spin
Deleted these two lines so now it is
Tested with several programs and works fine.
re
Good thinking. I do that lots of times - set dira then outa. You are right, better to do outa first. Ok, will need to change lots of lines of code. brb
Staring at the icon display code at the moment. Can we make it faster? Yes, I think so, because it is only the first few lines of an icon and the last few that I think get merged with the background. Most of the ones in the middle don't get merged so no need to process those.
Addit, no that didn't work out - all my icons are actually slightly smaller than 59x60 so still need the border around the outside. So the attached touch.spin is still the latest one.
addit: ok re the big port over to C, the set161 was a large part of that. So now it is just this
There are three things currently connected to group3, and maybe we can rationalise that?
1) The touchscreen sensor for screen 1 (pins 0-2)
2) The touchscreen sensor for screen 2 (pins 3-5)
3) The MCP302 A to D (pins 6-8)
So - maybe we move the A to D over to the extension board. In which case it is just the touchscreen sensor. I'm really hoping it won't care if it is disabled for a bit. I doubt the user would notice if there was a tiny delay touching the screen when a cache update came through. Which actually is pretty unlikely as the program is most likely in a tight loop waiting for an input on the screen.
So - perhaps you don't need to code much at all. Just disable group3, do the memory transfer and then reenable it?
And maybe you don't even need to shutdown the touch cog and restart it?
Yes there is something different about the SSD display and the touch x and y. I think I fixed that...
Just tested it now and it seems fine with the Keyboard program.
I don't know what's wrong. Not sure how to interpret test results. I'm making things WAY to difficult.... I'll zip the project and edit post in a sec...
Okay got the archive of the latest attempt. Check txt for description.
*edit*
Had some time so I pushed more into low-level drivers. I think it's a little bit faster. Still room for improvement.
to replace this?
First thing there is you have to call #get_values to get the 161 latch value that has been passed from spin.
Having said that, I have a confession - I have never managed to get any pasm code to ever work without first writing the entire thing in spin. I guess one can always use debugging routines but the first pasm I ever tried modifying was the Z80 emulation and that happened to have only one or two longs free so there never was room to put in debugging code. So I got used to writing in spin, so you can put in pause routines, and put in breakpoints and send values to the screen to check they are what you think they should be.
Having said that, aren't the "S" and "T" commands the ones to use? In other words, move a block to and from hub. In which case that shouldn't need hacking the 161 driver code.
If you are trying to move this Spin code into pasm
Well that might require modifying something else, because it is split into two pasm calls for a good reason and that is that the current pasm driver only lets you pass three longs. But that little PUB in spin is passing four longs because it is passing the set161 value first.
If yo want to move that entire spin PUB into pasm, well that might need changing the pasm so that you can pass four variables. Nothing wrong with that, but it will mean modifying the startup routine, the get_values routine, and every spin routine that talks to that pasm code. On the upside, you can leave out the cache driver and just see if you can get an "S" routine to work with the pasm code. Change things slowly, debug often and you will know if it is broken because the "S" routine is fundamental to getting anything on the screen.
Actually it is 5 variables. Looking at that spin code, Load161 is actually and latch373 is a few lines to setup variables and then
and then passing 3 variables for a block move.
Having said all that, is there a problem splitting up the 161set and the block move into several lines of C? Actually, I have no idea how the cache driver is written. Is it in C or is it in Spin? Is there still spin hidden behind the scenes in GCC? Or is there absolutely no spin whatsoever (like Pullmoll managed with his Z80 emulation and his friendly signon message "goodbye spin").
Too many variables here
Note that when the function is done, we jmp to done, not init. Init is used to latch all high, and get control of P22 *permanently*
This revised code has the 161load and set latches integrated into the command. Works flawlessly. I think there's either something wrong with the hardware, or I have a messed up test file? Something funky...
The reason I was unrolling things was trying to determine what the problem was. This is from the modified PASM, known working... I need to take a break from things. Posting updated Touch.spin, should be backward compatible and has some tuning for speed.
It can be done but that will need different pasm driver code.
No, wait, only 4, because an "S" does not use destination, because that is the 161 value. brb
I need to think big picture here. Ok, an S command sets the 161, then sets the latch then does the block move. The latch is always latch2, but there could be an advantage in pasm knowing what the current latch value is and then setting pins. That way, if you are running the left screen or the right screen, pasm keeps it the same.
So we need one more variable passed to pasm - latchvalue
It actually works quite well, pass the latch value in *ie display, rs, etc.* then send the data!
There's actually a bit of tuning that can be done. But, I dumped the working code into the cache driver, and poof, no work. Could be faulty hardware I guess. Not really sure..
*edit*
So I tricked you. In get_values Then, restore at done or before draw
We need a pure pasm block move in one call. Will that work for the cache?
Actually, how is the cache going to handle the latch? That shouldn't be part of the cache driver. So we do need a separate latch pasm call. So it is back to the current 3 variables.
What we need is a "store latch" and a "fetch latch" routine in pasm. No latch value in spin. If you want to know the current latch status, do a pasm call. Then the C code can set up the latch under programmer control, but any pasm calls know what the latch is, can temporarily store it, modify the latch, then restore the value.
latch command becomes this
The cache driver is complicated. I want to include much of our current PASM into the driver, with extended function set. If the cache drive is also the ramdriver, it should make things less complicated since we just save the latch value before a cache hit, then restore it when done with the cache. Need to get the read/write working first.
current PASM is 215 longs, plenty small enough to fit in with the cache driver IMO.
I have a really neat set of files for KERMIT transfers using java a few years ago. Paid a bunch of money for them. If you'd like to port KERMIT to the prop I think it'd be a great thing.
Comparison of XMODEM to KERMIT
http://www.sbsw.com/Articles/kermxmod.htm
I can see pushing propeller binary image files over xbee via serial to the sd card and doing ota updates.
God, this thread is just too cool.
*edit*
made more changes, pushed the Set161(0) from PUB Begin to init in PASM
Setting 161 to zero at the beginning isn't needed so that can be commented out (it was for debugging)
I've been thinking about the latches. Get rid of the variable in Spin and just have a variable in pasm. I've replaced the command to set the latch with two other commands - "O" to logic OR the pasm value with the value you send, and "A" to logic AND the value. So that means that pasm routines can modify the latch value if they want to and I think you need that for moving the S ant T commands into pasm.
So attached is working code. The next thing to get working is to comment out the first two lines in PRI SpinHubToRam() and replace then with the commented out lines in pasm in command S.
Only thing is... it doesn't work. I tried adding in the dira and outa commands as the program enters and exits the pasm routines. There may be one I have missed though.
I think it might be the group change rather than the 161, trying to split it into two parts for debugging.
Moving all the S and T commands into Pasm is a fundamental part of the cache driver. It should be possible to debug this code and see if the display works first before looking at the cache driver. Need to do things one at a time.
Tried adding a breakpoint with jmp #stop in pasm and pins seem to be in the correct states. Tricky.
And yes, this whole thing is a pile of fun!
[code]
CON Touch
'' ILI9325 driver using the Touchblade161 design for faster ram to display transfers
'' Average Joe and James Moxham 2012
Margin = 4 ' some characters have xoffset of -1 or -2 eg v, and won't display on the extreme left edge
_1ms = 1_000_000 / 1_000 ' Divisor for 1 ms
' touchscreen pins
Touch_Clock = 0
'Touch_ChipSelect = done with a group select
Touch_DataIn = 1
Touch_DataOut = 2' touchscreen pins
' kye sd numbers
_dopin = 24
_clkpin = 25
_dipin = 26
_cspin = 27
_cdpin = -1 ' -1 if unused.
_wppin = -1 ' -1 if unused.
_rtcres1 = -1 ' -1 always.
_rtcres2 = -1 ' -1 always.
_rtcres3 = -1 ' -1 always.
_statuspin = -1 ' Status LED pin number.
VAR
byte FileNumber ' 0 to 31
byte DisplayMode ' type of display 0 = ILI9325, 1= SSD1289, 2 etc
word ScreenWidth ' either 240 in portrait or 320 in landscape
word ScreenHeight ' 320 in portrait or 240 in landscape
long orientation ' true is portrait
long curx, cury
byte FontHeight ' size of the current loaded font (pixels = fontsize *.75)
word BackFontColor ' the background color in RRRRRGGG GGGBBBBB format
byte Latch ' current latch output
long StringAddress ' start of string address
long wcboot ' most recent boot warm or cold '
OBJ
spi: "SPI_ASM" ' thanks to Beau - spi driver for touch screen
fat: "SD-MMC_FATEngine.spin" ' Kye's latest sd driver and no RTC
DAT
RamLocation long $0[31] ' 32 file ram locatinos
RamSize long $0[31] ' File sizes in words - NOT bytes (divide byte by 2)
sdbuffer byte $0[1024] ' 1024 byte buffer for sd card interface and also for sending one row 320 x3 = 960 bytes max picture size
buffer2 byte $0[512] ' 512 general purpose hub buffer
rambuffer word $0[256] ' 512 byte (256 word) buffer easier for using with words
PUB BeginDesktop(DisplayType) ' store the type of display once by the desktop program
DisplayMode := DisplayType ' global variable I = ILI9325, S=SSD1289
Begin(true) ' start up everything running the desktop
result := wcboot
PUB BeginProgram ' used by all programs except the desktop program
Begin(false) ' any program except the desktop
result := wcboot
PRI Begin(Desktop) |n ' desktop true if run from the desktop program, false for other programs
start_ram_cog ' * start cog first *start the external ram / display driver in a cog, needed for many routines so always first
Latch_AllHighStart ' all Latch pins high and enables pin 22
'Load161(0) ' reset all the 161 counters to zero
if desktop == false
DisplayMode := GetDisplayMode ' reads "I" or "S" etc off the ram ** must have run desktop at least once first to store this ***
'DisplayMode := "I" ' temp for debugging, using the ILI9325
SelectMemGroup ' select group2
Latch_DisplayBoth ' set Latch for both hand display so intialises both (one may not be present but does not matter)
if displaymode == "I"
Start_ILI9325 ' start the display 0 = ILI9325, 1 = SSD1289
if displaymode == "S"
Start_SSD1289
wcboot := WarmColdBoot ' get the last warm or cold boot value
SetOrientation(true) ' in portrait (true) or landscape mode (false), must be before clearscreen otherwise don't know screensize
if wcboot == false and Desktop == true ' no need to redisplay every time if worked the first time
Text(string("SD")) ' string to send, uses internal prop font so can see something if the SD fails to mount
fat.fatEngineStart( _dopin, _clkpin, _dipin, _cspin, _wppin, _cdpin, _rtcres1, _rtcres2, _rtcres3)
fat.mountPartition(0) ' mount the sd card
if desktop == false ' running a program, this is called without knowing what the ramlocation and ramsize values are
ManualValues ' coming from outside so reset ramsize and ramlocation values for 0 and 1
SetCursor(0,0) ' cursor for debugging to 0,0
if desktop == false
SelectSPIGroup ' selects this group and starts the cog
'PUB BasicTesting
' text(string("Basic testing"))
' sdbuffer[0] := 6
' SpinHubToRam(@sdbuffer,5000,10) ' move data to ram
' sdbuffer[0] := 7 ' change the value
' SpinRamToHub(@sdbuffer,5000,10) ' read back the original value
' hex(sdbuffer[0],2)
' 'Drawline(100,100,110,100,$FFFF)
' Clearrectangle(120,120,129,129,$FFFF)
' Draw(100,100,109,109)
' repeat 100
' pixel(%11111000_00000000)
' ************** string routines stored in external memory so can have arrays and large text files **********************
' to make things simpler, one array StringArray. any number of entries, each up to 128 characters
' pass the source and destination numbers
' TextArray(256) = 256 entries for text files etc
' first entry is destination, second entrie(s) are the source
' Commands are
' StringDim ' reserves memory 256*128, increments filenumber, adds filesize
' StringLoadFile(Name,S) ' read a .txt file off the SD card and stores in TextArray, new string entry is crlf
' StringSaveFile(Name,S,F) ' save .txt file from TextArray to SD card adding in crlf, Start to Finish
' StringClear(Dest) ' clear a string, pass string number
' StringAddChar(Dest,N) ' adds a character to a string at end
' StringSubChar(Dest) ' remove a character from the end
' StringCopy(Dest,Src) ' copy string from one location to another
' StringStore(Dest,Src) ' convert Prop string("mystring") to string stored in external memory
' StringLeft(Dest,Src,N) ' Basic Left$
' StringMid(Dest,Src,L,N) ' Basic Mid$
' StringRight ' Basic Right$
' StringInstr ' Basic Instr
' StringUcase(Dest,Src) ' Basic Ucase$
' StringLcase ' Basic Lcase$
' StringLen ' Length
' StringStr ' string representation of a number
' StringHex(Dest,N) ' number to hex string
' StringBin ' number to binary string
' StringVal ' value (hex is 0x, binary is 0b)
' StringJoin(Dest,Src1,Src2) ' same as Basic + with strings
' StringInsert(Dest,Src1,Src2,n) ' inserts src2 into src1 starting at position n, (1 is first char) and moves to dest
' StringDebug(n) ' print using the green propeller font string number n
' StringLen() ' same as strsize but works on external memory strings
' StringRambuffer ' moves string to rambuffer array and returns result = location of this array
' StringCompare(Source1,Source2) ' returns true if strings equal in length and characters
' use these routines amongst others.
'integerToDecimal(number, length) '' 5 Stack Longs
'integerToHexadecimal(number, length) '' 5 Stack Longs
'integerToBinary(number, length) '' 5 Stack Longs
'decimalToInteger(characters) | sign '' 10 Stack Longs
'hexadecimalToInteger(characters) | sign '' 10 Stack Longs
'binaryToInteger(characters) | sign '' 10 Stack Longs
PUB StringTest ' test all the string routines
'StringDim(10)
'StringStore(5,string("Hello World"))
'StringDebug(5)
'StringClear(5)
'StringAddChar(5,"1")
'StringAddChar(5,"2")
'StringAddChar(5,"3")
'StringAddChar(5,"4")
'StringDebug(5)
'StringSubChar(5) ' remove one character from the right, useful for backspace etc
'StringDebug(5)
'StringCopy(7,5) ' copy
'StringDebug(7)
'StringLeft(7,7,5) ' source and destination the same or different
'StringDebug(7)
'StringMid(8,5,7,3) ' dest, source, starting byte, number of bytes. first byte is 1
'StringDebug(8)
'StringRight(3,5,4) ' dest, source, number of bytes
'StringDebug(3)
'Hex(StringInstr(5,2,"o"),2) ' find this character starting at 2
'StringBin(2,255) ' convert a number to binary always 32 + 2 characters
'StringDebug(2)
'StringHex(4,100) ' always 8 + 2 characters
'StringDebug(4)
'StringDec(9,-20) ' always 11 characters
'StringDebug(9)
'StringStr(9,-7) ' Basic str$ removes leading zeroes, and first character is either - or a space
'StringDebug(9)
'StringStore(5,string("65")) ' test string number to number
'hex(StringVal(5),8)
'StringStore(5,string("0x1234")) ' test string hex to number
'hex(StringVal(5),8)
'StringStore(5,string("0b101")) ' test string binary to number
'hex(StringVal(5),8)
'StringStore(1,string("Hello "))
'StringStore(2,string("World"))
'StringJoin(1,1,2) ' string1 = string1 +string2 (shows dest and a source can be the same - or could be different)
'StringDebug(1)
'StringUcase(1,1) ' Ucase test
'StringDebug(1)
'StringLcase(1,1) ' Lcase test
'StringDebug(1)
'StringStore(1,string(" 123")) ' test the trim function
'StringTrim(1,1)
'StringDebug(1)
'StringStore(1,string("TEST6.TXT")) ' file name
'StringStore(2,string("Line a of text")) ' store some lines of text
'StringStore(3,string("Line b of text"))
'StringStore(4,string("Line c of text"))
'StringSavefile(1,2,4) ' filename is 1, store lines 2 to 4
'StringStore(1,string("TEST6.TXT")) ' file name
'StringLoadFile(1,2) ' read back the file created above.
'Stringdebug(2)
'Stringdebug(3)
'Stringdebug(4)
'repeat
PUB StringDim(n) ' reserve space for a string array, n= number of strings, 128 max bytes per string
Ramsize[Filenumber] := n << 6 ' * 64 as this is in words, not bytes
StringAddress := NextLocation ' get the location of the string array
Filenumber += 1
PUB StringStore(StringN,Source) ' store a propeller string("test") to array number Dest
bytemove(@rambuffer, Source, (strsize(Source) + 1)) ' move to rambuffer array
StringToRam(StringN)
PUB StringDebug(StringN) ' print using the green propeller font - useful if sd card not working so no fonts loaded
RamToString(StringN)
Text(@rambuffer) ' print it out
PUB StringClear(StringN)
bytefill(@rambuffer,0,128) ' only really need to do the first one but may help debugging to completely clear the string
StringToRam(StringN)
PUB StringAddChar(StringN,c) | s ' add c to the string
if c >31 and c <127 ' a character that can be displayed
RamToString(StringN) ' get the string
s := strsize(@rambuffer)
byte[@rambuffer] := c
byte[@rambuffer][s + 1] := 0 ' add in the new zero (can't assume array is cleared)
StringToRam(StringN) ' store back to ram
PUB StringSubChar(StringN) ' remove one character from the right same as strings.left(strings.len - 1)
RamToString(StringN)
if strsize(@rambuffer) <> 0 ' if at least one character
byte[@rambuffer][strsize(@rambuffer)-1] :=0
StringToRam(StringN)
PUB StringCopy(Dest,Source) ' copy string from source to dest
RamToString(Source)
StringToRam(Dest)
PUB StringLeft(Dest,Source,n) ' Basic Left$
RamToString(Source)
byte[@rambuffer][n] := 0 ' move the terminator
StringToRam(Dest) ' move back to ram
PUB StringMid(Dest,Source,start,n) | i ' Basic Mid$
RamToString(Source)
repeat i from 0 to n-1
byte[@rambuffer] := byte[@rambuffer][i+start-1] ' possibly could use bytemove here
byte[@rambuffer][n] := 0 'add in the new zero terminator
StringToRam(Dest)
PUB StringRight(Dest,Source,n) | i,m
RamToString(Source)
m := strsize(@rambuffer) - n
repeat i from 0 to n ' Basic Right$. include the terminator
byte[@rambuffer] := byte[@rambuffer][i+m]
StringToRam(Dest)
PUB StringInstr(Source,Start,c) | i ' find c in the string starting at start
RamToString(Source)
result := 0 ' return 0 if not found
repeat i from (start-1) to strsize(@rambuffer)
if byte[@rambuffer] == c
result := i+1 ' 1 is first character
quit
PUB StringBin(Dest,n) ' convert n to a binary value at Dest. Prefix 0b
repeat result from 33 to 2
byte[@rambuffer][result] := ((n & 1) + "0")
n >>= 1
byte[@rambuffer][34] := 0 ' add zero terminator
byte[@rambuffer][0] := "0" ' prefix 0b same as some versions of C
byte[@rambuffer][1] := "b"
StringToRam(Dest) ' store
PUB StringHex(Dest,n) ' number to hex, always 8 characters
repeat result from 9 to 2
byte[@rambuffer][result] := lookupz((n & $F): "0".."9", "A".."F")
n >>= 4
byte[@rambuffer][10] := 0 ' zero at end
byte[@rambuffer][0] := "0" ' prefix 0x same as C
byte[@rambuffer][1] := "x"
StringToRam(Dest)
PUB StringDec(Dest,n) | sign ' number to decimal string, always 11 characters
sign := "+"
if(n < 0)
sign := "-"
if(n == negx)
bytemove(@rambuffer, string("-2147483648"), 11) ' max negative number special case
else
repeat result from 10 to 1
byte[@rambuffer][result] := ((||(n // 10)) + "0")
n /= 10
byte[@rambuffer][0] := sign ' sign at the front
byte[@rambuffer][11] := 0 ' zero terminator
StringToRam(Dest)
PUB StringStr(Dest,n) | found ' same as Basic STR - similar to Dec above but sign is either space or negative, and leading zeros removed
StringDec(Dest,n) ' number preserved in rambuffer
found := 10 ' if answer was zero don't remove leading zero
repeat result from 1 to 10
if byte[@rambuffer][result] <> "0"
found := result
quit
repeat result from 1 to 11
byte[@rambuffer][result] := byte[@rambuffer][result +found -1] ' shuffle over if needed
if byte[@rambuffer][0] == "+"
byte[@rambuffer][0] := " " ' Basic syntax, - or blank
StringToRam(Dest) ' resend to external ram
PUB StringVal(Source) | sign,i,k ' returns value of the string, hex 0x, binary 0b, decimal + or - or space or no leading character just a number
RamToString(Source)
if byte[@rambuffer][1] == "x" ' this is a hex number
repeat k from 2 to strsize(@rambuffer) -1 ' leading two characters always 0x
result := ((result <- 4) + (byte[@rambuffer][k] & $F))
return
if byte[@rambuffer][1] == "b" ' this is a binary number
repeat k from 2 to strsize(@rambuffer) -1 ' leading two characters always 0b
result := ((result << 1) + (byte[@rambuffer][k] & 1))
return
' else if got here this must be a decimal number. It could start with a number(no leading sign), or a space, or a + or a -
sign := byte[@rambuffer][0]
i := 0 ' start location
if sign == "+" or sign == "-" or sign == " " ' this is a decimal number ** must start with a space or a + or a -**
i := 1 ' leading character so start here
repeat k from i to (strsize(@rambuffer)-1) ' if ever use right to left, don't use step -1, spin puts it in if start is less than finish and if there is a step -1 it is one less loop
result := ((result *10) + byte[@rambuffer][k] & $F) 'very clever, the & $F works because zero is hex0x30, the pre multiply shifts previous numbers to the left so can do left to right. Easier to understand with Kye's binary converter with the left shift
if sign == "-"
result *= -1 ' if negative then negate
PUB StringJoin(Dest,Source1,Source2) ' Dest$= Source1$+Source2$
RamToString(Source2)
bytemove(@buffer2,@rambuffer,128) ' store string2 to buffer2
RamToString(Source1) ' get first string
bytemove(@rambuffer+strsize(@rambuffer),@buffer2,strsize(@buffer2)+1) ' thanks to Kye for this one
StringToRam(Dest) ' store back to ram
PUB StringUcase(Dest,Source)
RamToString(Source)
repeat result from 0 to strsize(@rambuffer)
if ((byte[@rambuffer][result] => "a") and (byte[@rambuffer][result] =< "z"))
byte[@rambuffer][result] -= 32
StringToRam(Dest)
PUB StringLcase(Dest,Source)
RamToString(Source)
repeat result from 0 to strsize(@rambuffer)
if ((byte[@rambuffer][result] => "A") and (byte[@rambuffer][result] =< "Z"))
byte[@rambuffer][result] += 32
StringToRam(Dest)
PUB StringTrim(Dest,Source)
RamToString(Source)
if byte[@rambuffer][0] == "+" or byte[@rambuffer][0] == " " or byte[@rambuffer][0] == "-"
repeat result from 0 to strsize(@rambuffer)
byte[@rambuffer][result] := byte[@rambuffer][result+1] ' delete the first +,- or space
StringToRam(Dest)
PUB StringSaveFile(Filen,Start,Finish) | i,j ' filen might be string array 0, start could be 1, finish could be 4
RamToString(Filen) ' fetch the filename
fat.newfile(@rambuffer) ' create the file, if not working use \fat. for debugging, see PUB openfileread for an example
result := \fat.openfile(@rambuffer,"W") ' open for writing return error if there is one
repeat i from Start to Finish
RamToString(i)
j := strsize(@rambuffer)
byte[@rambuffer][j] := 13 ' carriage return
byte[@rambuffer][j+1] :=10 ' line feed
byte[@rambuffer][j+2] := 0 ' end of new line
fat.writedata(@rambuffer,j+2) ' write this line
FileClose ' close the file
PUB StringLoadFile(Filen,Stringnumber)| s,r,c,t,z ' read filename, new line every crlf. Must have enough space reserved in the string array
RamToString(Filen) ' copy a file from the SD card to the ram disk
OpenFileRead(@rambuffer)
s := 0 ' sd counter
r := 0 ' rambuffer counter
t := 0 ' total number of bytes
z := fat.filesize ' size of the file
repeat (z >> 9) +1 ' include any remainder as +1, do blocks of 512 bytes each as natural size of SD blocks
fat.readdata(@sdbuffer,512) ' read from SD card
repeat s from 0 to 511
c := byte[@sdbuffer] ' get the next byte
if t =< z ' might read in one more 512 byte block to ensure gets any remainder
if c == 13 or r > 126 ' end of a line so new string, or string too long
byte[@rambuffer][r] := 0 ' end of string terminator
StringToRam(Stringnumber)
stringnumber +=1 ' new string number
r := 0 ' start at beginning of rambuffer
if c >31 and c < 127
byte[@rambuffer][r] := c ' store if a valid character
r +=1
t +=1 ' number of bytes read
FileClose ' close the file
PUB StringLen(Source) ' string length
RamToString(Source)
result := strsize(@rambuffer)
PUB StringPrint(Font,Source) | i,s ' print this string using using font in memory and curx and cury
RamToString(Source)
s := strsize(@rambuffer)
repeat i from 0 to (s-1)
curx := TextChar(Font,byte[@rambuffer],curx,cury)
PUB StringRamBuffer(Source) ' string to rambuffer array
RamToString(Source)
result := @rambuffer ' return pointer to the rambuffer array
PUB StringCompare(Source1,Source2) | i
RamToString(Source2)
bytemove(@buffer2,@rambuffer,128) ' store string2 to buffer2
RamToString(Source1)
result := true
repeat i from 0 to strsize(@rambuffer)
if byte[@buffer2] <> byte[@rambuffer]
result := false
return
PRI StringToRam(StringN)
HubToRam(StringN << 6 + StringAddress,64) ' 64 words =128 bytes, so work in groups of 64 words
PRI RamToString(StringN)
RamToHub(StringN << 6 + StringAddress,64) ' moves to rambuffer array
' ***************** end string routines *************************
PUB DisplayLeft ' send all data to the left display
Latch_Display1
PUB DisplayRight
Latch_Display2
PUB RamErase(n)
Filenumber := n ' erase lookup table for the ram files, effectively erases the files
' if 0 then erase all, if 1 then leave the desktop intact (assuming this was loaded first)
PUB ClearRam
Filenumber := 2 ' clear ram but leave file 0 = warm or cold boot and 1 = background picture
PUB GetCurX
result := curx ' get the text cursor position
PUB GetCurY
result := cury
PUB SetCurx(n) ' set the text cursor position
Curx := n
PUB SetCury(n)
Cury := n
PUB GetBackFontColor
result := BackFontColor
PUB GetRamLocation(n)
result := Ramlocation[n]
PUB GetFilenumber
result := filenumber
PUB SetFilenumber(n) ' reset the file number
Filenumber := n
PUB Chain(stringptr)
result := \fat.bootpartition(stringptr) ' reboot and run this program
PUB SDtoRam(stringptr) | RamAddress,remainder,sizebytes,sizewords ' copy a file from the SD card to the ram disk
OpenFileRead(stringptr)
sizebytes := fat.filesize ' size in bytes
sizewords := sizebytes >>1 ' size in words
ramaddress := NextLocation ' next free place in ram
RamSize[FileNumber] := sizewords ' ram disk works in words, not bytes
repeat sizebytes >> 9 ' do blocks of 512 bytes each as natural size of SD blocks
fat.readdata(@sdbuffer,512) ' read from SD card
SpinHubToRam(@sdbuffer,RamAddress,512) ' move to ram
RamAddress += 256 ' increment ram address by 256 (half 512 as using words)
remainder := sizebytes & %00000000_00000000_00000001_11111111 ' remainder (if not a multiple of 512)
if remainder <> 0
fat.readdata(@sdbuffer,remainder)
SpinHubToRam(@sdbuffer,RamAddress,remainder) ' send out the remainder bytes
FileClose ' close the file
result := FileNumber ' return this file
FileNumber +=1 ' point to next location
PUB FetchRamBuffer
result := @rambuffer
PUB SDBMPtoRam(stringptr) | width,height,w,i,ramaddress ' .bmp from sd to ram and convert to 2 byte ili format and reverse order along the way
' http://en.wikipedia.org/wiki/BMP_file_format scroll down about 1/3 of the way for the header formats
' get the width, height
' store these as 2 longs at the beginning of the ram file
' the bitmap format starts at the last row and works up so need to reverse the order
ramaddress := NextLocation ' get the next ram location
OpenFileRead(stringptr)
fat.readdata(@sdbuffer,$36) ' get the header 0x36 hex bytes
width := sdbuffer[$12] + sdbuffer[$13] << 8 ' only read in two bytes as never will be >64k wide or high
height := sdbuffer[$16] + sdbuffer[$17] << 8
w := width * 3 ' this number of bytes per row, and round up to nearest 4 bytes et 327 goes to 328 The size of each row is rounded up to a multiple of 4 bytes (a 32-bit DWORD) by padding.
w +=3 ' add 3 and
w &= %11111111_11111111_11111111_11111100 ' round down
SpinHubToRam(@sdbuffer,ramaddress,$36) ' store header to ram
i := $36 + ramaddress + ((height - 1) * width) ' start + header and on the last row and work back
repeat height
fat.readdata(@sdbuffer,w) ' read in the first row
CogCmd("F",@sdbuffer,@rambuffer,width)
HubToRam(i,width) ' send to ram
i -= width ' subtract the width, move up the picture
FileClose
Ramsize[Filenumber] := $36 + (width * height) ' size of bitmap file in words
result := FileNumber ' returns the current file number
FileNumber += 1 ' add 1 to filenumber
PUB DrawBMPRam(f,x,y) | rampointer,width,height ' pass the filenumber and the x and y location, searches for this file and if found, displays it
rampointer := RamLocation[f] ' find the start of the file
SpinRamToHub(@sdbuffer,rampointer,$36) ' read in the header
width := sdbuffer[$12] + sdbuffer[$13] << 8 ' only read in two bytes as never will be >64k wide or high
height := sdbuffer[$16] + sdbuffer[$17] << 8
Draw(x,y,x+width-1,y+height-1) ' set up the area to draw
RamToDisplay(RamPointer + $36,width*height) ' skip the header and display
result := (width <<16) | (height & $0000FFFF) ' return the width and height combined into a long
' ***************** end ram disk routines
PUB MergeBackgroundIconRam(stringptr,x,y,desktop,mask,icon) | i,j,k ' merge icon and background - if mask =0 then background, else icon (not classic alpha compositing but close enough)
' i is background counter, j is icon counter, k = mask counter
' assumes desktop is in ramfile 1, mask in ramfile 2, icon in ramfile 3
FileNumber := icon
SDBMPtoRam(stringptr) ' load icon into ram
Filenumber := icon ' reset filenumber
i := RamLocation[desktop] + $36 + (y*(screenwidth+1)+x) ' location of picture pixel in ram
j := RamLocation[icon] + $36 ' location of icon in pixel in ram
k := RamLocation[mask] + $36 ' location of mask pixel in ram
repeat 60
SpinRamToHub(@sdbuffer,i,59) ' get 59 pixels from background
SpinRamToHub(@rambuffer,j,59) ' get 59 icon pixels
SpinRamToHub(@buffer2,k,59) ' get words for the mask
CogCmd("X",@sdbuffer,@rambuffer,@buffer2) ' pasm version of the above 3 lines
SpinHubToRam(@sdbuffer,i,59) ' send back modified line to the picture buffer
i += screenwidth+1 ' next line in picture
j +=59 ' icon next line
k +=59 ' next line in mask (bytes) - buffer2
' old "X" command code
'repeat n from 0 to 58
' if (word[@buffer2][n] & %00000000_00011111) > %10000 ' RGB are the same, so use BBBBB, mask 5 low bits, and if > %10000
' word[@sdbuffer][n] :=word[@rambuffer][n] ' if >128 then replace with icon pixel
PUB OpenFileRead(stringptr) ' open a file for reading and display a message if it fails
result := \fat.openfile(stringptr,"R")
if fat.partitionerror <> 0 ' failed to find the file
Text(stringptr) ' print the filename
Text(result) ' print the error message
repeat ' stop the program
PUB FileClose
fat.closefile ' close the file
PUB TouchXPercent
result := TouchValue & 255 ' decode xval 0-100%
if DisplayMode == "S"
result := 100-result ' SSD1289 x runs the other way
PUB TouchYPercent
result := TouchValue >> 8 ' decode yval 0-100%
PUB TouchSwishX | xval,oldxval,difference,average ' returns result = +ve or -ve depending on swish direction ' did finger move left or right?
SelectSPIGroup ' turn on the spi touchscreen
oldxval := 255' for startup
average := 0
repeat until (average > 30) or (average < -30)
' possibly add a decay function here for average
pause1ms(50)
xval := TouchXPercent ' decode xval 0-100%
if xval == 255 ' not touched
oldxval := 255 ' reset oldxval as well
else
if oldxval <>255 ' discard the first reading as the screen is touched
difference := xval - oldxval
'printdecimal(xval)
'printdecimal(difference)
average := average + difference '
'printdecimal(average)
'crlf
oldxval := xval ' store for next time
result := average ' return negative or positive value
PUB TouchSwishY | yval,oldyval,difference,average ' returns result = +ve or -ve depending on swish direction ' did finger move left or right?
SelectSPIGroup ' turn on the spi touchscreen
oldyval := 255' for startup
average := 0
repeat until (average > 30) or (average < -30)
' possibly add a decay function here for average
pause1ms(50)
yval := TouchYPercent ' decode xval 0-100%
if yval == 255 ' not touched
oldyval := 255 ' reset oldyval as well
else
if oldyval <>255 ' discard the first reading as the screen is touched
difference := yval - oldyval
average := average + difference '
oldyval := yval ' store for next time
result := average ' return negative or positive value
PUB TouchStart
SPI.start(3,0) ' delay,state, start clock high
PUB TextChar(FileN,ascii,x,y) | jump,size,width,height,ramaddress,xoffset,yoffset,xadvance ' read out a character from a .ifn file stored in ram
' pass Fonttable = global
' moves to next line if off the end
ramaddress := RamLocation[FileN]
jump := ReadRamLong(ramaddress,(ascii << 2) + 256) ' jump location = ascii *4 plus fonttable + 256
SpinRamToHub(@buffer2,ramaddress+jump>>1,20) ' read in the width height as a block = quicker than readramlong
size := long[@buffer2][0] ' size in pixels of this character
xadvance := long[@buffer2][5] ' amount to move to next character
'if size > 0 and ascii <> 32 ' no need to print anything if size is zero or read more font data
width := long[@buffer2][1] ' width
height := long[@buffer2][2] ' height
xoffset := long[@buffer2][3] ' xoffset to move
yoffset := long[@buffer2][4] ' yoffset to move
if (x+width -1 + xoffset) > screenwidth
crlf ' do a new line if it won't fit
x := curx
y := cury
if ascii < 32
xadvance := 0 ' add nothing if a non displaying character
if ascii == 13 ' carriage return
cr
x := curx
if ascii ==10 ' line feed (new line)
lf
y := cury
if ascii > 32 ' space not displayed
Draw(x+xoffset,y+yoffset,x+width-1+xoffset,y+height-1+yoffset) ' draw on screen
RamToDisplay(ramaddress+((jump+32)>>1),size) ' move bytes from ram out to the display
return x + xadvance
PUB crlf
cr
lf
PUB cr ' carriage return, to left margin
curx := Margin ' some characters are -1 xoffset so won't display
PUB lf ' line feed, move up one
cury += FontHeight
if cury > screenheight
ClearRectangle(0,0,screenwidth,screenheight,BackFontColor)
cury := 0 ' if off the bottom of the screen then clear screen start at the top (VT00 does scrolling)
' don't clear background here, do in calling routine
'ClearFontBackground(0,cury,screenwidth,cury + FontHeight) ' clear background line to the background color ready to draw
PUB TextT(FileN,stringptr) 'print at curx,cury using file number in ramdisk
'' Print a zero-terminated string
repeat strsize(stringptr)
curx := TextChar(FileN,byte[stringptr++],curx,cury)
PUB SetCursor(x,y)
curx := x
cury := y
PUB ReadRamLong(WordStart,Bytevalue) ' easier calculating
SpinRamToHub(@result,Wordstart+Bytevalue>>1,2)
return result
PUB LoadFont(stringptr) | filesize,i ' read a .ifn file premade in angelcode bmfont and then the vb.net program
SDtoRam(stringptr) ' font to ram disk, adds one to filenumber
SpinRamToHub(@rambuffer,RamLocation[Filenumber-1],128) ' read first 128 words =256 bytes back
FontHeight := byte[@rambuffer][251] ' font height
repeat i from 0 to 2
sdbuffer := byte[@rambuffer][35+i]
CogCmd("E",@sdbuffer,@buffer2,1) ' convert to 2 byte .ili format
BackFontColor := ( byte[@buffer2][1] << 8) | byte[@buffer2][0] ' correct order
result := FileNumber-1 ' return pointer to the font file in ram
PUB HubToRam(RamAddress,Number) ' send pixels from rambuffer hub to ram
SpinHubToRam(@rambuffer,RamAddress,Number) ' spin version
PUB RamToHub(RamAddress,Number) ' send pixels from ram to hub at rambuffer
SpinRamToHub(@rambuffer,Ramaddress, number)
PUB RamToDisplay(RamAddress,Number) ' send pixels from ram to display
SpinRamToDisplay(Ramaddress, Number)
PUB Clearscreen(color) ' uses 2 byte ILI color (see below for rgb conversion)
wordfill(@rambuffer,color,256)
Draw(0,0,screenwidth,screenheight) ' set up the area of the screen to draw in
repeat 300
SpinHubToDisplay(@rambuffer,256)
PUB RGBtoWord(R,G,B) ' convert RGB to 2 byte ILI color
sdbuffer[0] := R
sdbuffer[1] := G
sdbuffer[2] := B
CogCmd("E",@sdbuffer,@buffer2,1)
result := ( byte[@buffer2][1] << 8) | byte[@buffer2][0] ' correct order
PUB ClearRectangle(x1,y1,x2,y2,ColorWord) | i,size,remainder ' clears an area of the screen to the current font background color
size := (x2-x1+1) * (y2 - y1 +1) ' number of pixels
remainder := size & 255 ' if not a whole number of 256 pixels
repeat i from 0 to 255 ' move the background font colour to the buffer
rambuffer := ColorWord
Draw(x1,y1,x2,y2) ' set up the area of the screen to draw in
repeat size >> 8 ' 256 pixel blocks
SpinHubToDisplay(@rambuffer,256)
if remainder <> 0
SpinHubToDisplay(@rambuffer,remainder) ' do the remainder
PUB Drawline(x1,y1,x2,y2,ColorWord) ' draws a vertical or horizontal line
ClearRectangle(x1,y1,x2,y2,ColorWord) ' same as drawing a rectangle
PUB SetOrientation(e) ' change orientation true = portrait. Changes global variable 'orientation' and also screen width and height
orientation := e
ChangeOrientation(orientation)
if orientation
screenwidth :=239
screenheight :=319
else
screenwidth :=319
screenheight :=239
PUB ChangeOrientation(n) ' pass true = portrait or false = landscape, changes global variable orientation in this object
' command $0001
'8.2.4. Driver Output Control (R01h)
'R/W RS D15 D14 D13 D12 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0
'W 1 0 0 0 0 0 SM 0 SS 0 0 0 0 0 0 0 0
'SS: Select the shift direction of outputs from the source driver.
'When SS = 0, the shift direction of outputs is from S1 to S720
'When SS = 1, the shift direction of outputs is from S720 to S1.
'In addition to the shift direction, the settings for both SS and BGR bits are required to change the
'assignment of R, G, B dots to the source driver pins.
'To assign R, G, B dots to the source driver pins from S1 to S720, set SS = 0.
'To assign R, G, B dots to the source driver pins from S720 to S1, set SS = 1.
' command $0003
'R/W RS D15 D14 D13 D12 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0
'W 1 TRI DFM 0 BGR 0 0 HWM 0 ORG 0 I/D1 I/D0 AM 0 0 0
' When AM = 0, the address is updated in horizontal writing direction.
'When AM = 1, the address is updated in vertical writing direction.
'I/D[1:0] Control the address counter (AC) to automatically increase or decrease by 1 when update one pixel
' display data.
SelectMemGroup
orientation := n
if displaymode == "I" ' ili9325
if orientation
' for origin top left and portrait mode
Displaycmd($0001,%00000001_00000000) ' $0001,$0100 set SS and SM bit 0001 0100 portrait
Displaycmd($0003,%00010000_00110000) ' $0003,$1030 set GRAM write direction and BGR=1. $0003 $1030
else
' for origin top right and landscape mode
Displaycmd($0001,%00000000_00000000) ' set SS and SM bit 0001 0000 landscape
Displaycmd($0003,%00010000_00111000) ' landscape $1028 = original but 1038 is correct - not mirror image
if displaymode == "S" ' SSD1289
if orientation
' for origin top left and portrait mode
Displaycmd($0001,$2B3F) ' Entry mode portrait
Displaycmd($0011,$6070) ' '
else
' for origin top right and landscape mode
Displaycmd($0001,$6B3F) ' landscape $2B3F = original but 6B3F is correct
Displaycmd($0011,$6838) ' landscape $1028 = original but 6838 is correct
PUB SelectMemGroup ' select group 1 for memory transfer
spi.stop ' and stop any other cogs here too if needed
LatchGroup2
DIRA |= %00000000_01011111_11111111_11111111 ' enable these pins for output
OUTA |= %00000000_00011111_00000000_00000000 ' set /rd /wr /disp wr and 161 clock high
PUB SelectSPIGroup
LatchGroup3 ' select group 2 for spi transfers
DIRA &= %11111111_11111111_11111111_11000000 ' so no clashes with the cog using P0-P2 set these as inputs and P3-P5
TouchStart ' start the touchscreen cog
' may need to set some pins here as inputs or outputs with a dira
PUB Text(stringptr) 'print at curx,cury
repeat strsize(stringptr)
Propfont_out(byte[stringptr++])
PropFontcrlf
PUB PropFontcrlf
curx := 0
cury += 32 ' new line at end of string
if cury >319 ' bottom of screen so new screen
curx:=0
cury:=0
PUB Propfont_out(ascii) | address,pixels
Draw(curx,cury,curx+15,cury+31) ' location to start drawing
address := $8000 + (ascii >> 1) << 7 ' get rom address
repeat 32 ' 32 rows per character, split in two parts
pixels := long[address] ' get rom font data
pixels := pixels >> (ascii & 1) ' shift for odd characters
repeat 16 ' 16 columns
if pixels & 1
Pixel(%00000111_11100000) ' foreground color RRRRRGGG_GGGBBBBB
else
Pixel(%00000000_00000000) ' background color
pixels := pixels >> 2 ' alternate pixels interleaved so shift 2
address += 4
curx +=16
if curx >239 ' new line
Propfontcrlf
PUB Start_ILI9325
LatchGroup2 ' talk to this display
LCD_Reset_High
pause1ms(5)
LCD_Reset_Low
pause1ms(5)
LCD_Reset_High
LCD_CS_High
LCD_RD_High
LCD_WR_High
pause1ms(20)
LCD_CS_Low
ILI9325initiate
PUB Start_SSD1289 ' based on C driver
LatchGroup2 ' talk to this display
LCD_Reset_High
pause1ms(5)
LCD_Reset_Low
pause1ms(10)
LCD_Reset_High
LCD_CS_High
LCD_RD_High
LCD_WR_High
pause1ms(20)
SSD1289initiate
PUB Draw(x1, y1, x2, y2) ' sets the pixel to x1,y1 and then fills the next (x2-x1)*(y2-y1) pixels
SelectMemGroup ' select memory group
ifnot orientation ' landscape mode so swap x and y
result :=x1 ' swap x1 and y1
x1 := y1
y1 := result
result := x2 ' swap x2 and y2
x2 :=y2
y2 := result
if displaymode == "I" ' ILI9325 display
Displaycmd($0050,x1)
Displaycmd($0052,y1)
Displaycmd($0051,x2)
Displaycmd($0053,y2)
Displaycmd($0020,x1)
Displaycmd($0021,y1)
Lcd_Write_Com($0022)
if displaymode == "S" ' SSD1289 display
Displaycmd($0044,(x2<<8)+x1)
Displaycmd($0045,y1)
Displaycmd($0046,y2)
Displaycmd($004e,x1)
Displaycmd($004f,y1)
Lcd_Write_Com($0022)
LCD_RS_High ' after sending out commands, set RS high via latch
PUB Pixel(pixelcolor) ' send out a pixel
Lcd_Write_Fast_Data(pixelcolor) ' need to set RS high at beginning of this group
PUB pause1ms(period) | clkcycles
'' Pause execution for period (in units of 1 ms).
clkcycles := ((clkfreq / _1ms * period) - 4296) #> 381 ' Calculate 1 ms time unit
waitcnt(clkcycles + cnt) ' Wait for designated time
PUB hex(value, digits) ' uses internal prop font and slow pixel drawing (for very basic debugging without an SD font)
SelectMemGroup ' reselect mem group for display output for debugging
'' Print a hexadecimal number
propfont_out("O")
propfont_out("x")
value <<= (8 - digits) << 2
repeat digits
propfont_out(lookupz((value <-= 4) & $F : "0".."9", "A".."F"))
PropFontcrlf
PUB SetWarmBoot
long[@rambuffer][5] := $5761726D ' spell Warm in hex, start at 5 as zero gets corrupted
byte[@rambuffer][10] := DisplayMode ' store the current display mode
HubToRam(0,20) ' send words to external ram (ramdiskstart is zero)
ClearScreen(0) ' so user sees something happen as there is a bit of a delay rebooting
reboot
PUB WarmColdBoot ' store warm or cold boot - reserve first 20 bytes in sram for interfacing with programs
' Ramdisk file 0 is the boot string Warm or Cold
' ramdisk file 1 is the background
Ramtohub(0,20) ' read words from the ram
if long[@rambuffer][5] == $5761726D
result := true
else
result := false
long[@rambuffer][5] := $FFFFFFFF ' erase previous warm/cold boot
byte[@rambuffer][10] := DisplayMode ' store display mode
HubToRam(0,20) ' send words to external ram (ramdiskstart is zero)
RamSize[0] := 20 ' reserve 20 bytes
RamLocation[0] := 0
Filenumber := 1
PRI GetDisplayMode
Ramtohub(0,20) ' read words from the ram
result := byte[@rambuffer][10] ' get the display mode
PUB ManualValues
RamLocation[0] := 0
RamSize[0] := $14 ' put in values manually - 20 bytes for warm boot
RamLocation[1] := $14
RamSize[1] := $12C36 ' 76800 for the desktop picture
FileNumber := 2 ' running a program not a desktop
PRI LatchGroup1
CogCmd("O",%00001111,0,0) ' set all group pins high
CogCmd("A",%11111110,0,0) 'set this one low
PRI LatchGroup2
CogCmd("O",%00001111,0,0) ' set all group pins high
CogCmd("A",%11111101,0,0) 'set this one low
PRI LatchGroup3
CogCmd("O",%00001111,0,0) ' set all group pins high
CogCmd("A",%11111011,0,0) 'set this one low
PRI LatchGroup4
CogCmd("O",%00001111,0,0) ' set all group pins high
CogCmd("A",%11110111,0,0) 'set this one low
PRI Latch_ResetLow
CogCmd("A",%11101111,0,0) 'set this one low
PRI Latch_ResetHigh
CogCmd("O",%00010000,0,0) ' set this pin high
PRI Latch_Display1 ' display on the left
CogCmd("O",%01100000,0,0) ' both displays high
CogCmd("A",%11011111,0,0) 'set this one low
PRI Latch_Display2 ' display on the right
CogCmd("O",%01100000,0,0) ' both displays high
CogCmd("A",%10111111,0,0) 'set this one low
PRI Latch_DisplayBoth ' both displays for debugging
CogCmd("A",%10011111,0,0) 'set both displays low
PRI Latch_RS_Low
CogCmd("A",%01111111,0,0) 'set this one low
PRI Latch_RS_High
CogCmd("O",%10000000,0,0) ' set this pin high
PRI Latch_AllHighStart ' all pins high
CogCmd("O",%11111111,0,0) ' set all pins high (disabled)
PRI Load161(address)
CogCmd("Z",0,address,0) ' call pasm version, faster, changes the latch but the next PUB always resets it anyway
PRI SpinHubToRam(hubaddress, ramaddress, number) | i ' P0-P15 are data lines, P16 is /RD, P17 is /WR
Load161(ramaddress) ' load the 161 counters
LatchGroup2 ' group 2 is block data transfers
CogCmd("S",hubaddress,ramaddress,number) ' pasm alternative to code below
DIRA &= %11111111_11100000_00000000_00000000 ' float all pins used
PRI SpinRamToHub(hubaddress,ramaddress,number) |i ' P0-P15 are data lines, P16 is /RD, P17 is /WR
Load161(ramaddress) ' load the 161 counters
LatchGroup2 ' group 2 is block data transfers
CogCmd("T",hubaddress,ramaddress,number) ' pasm alternative to code below
DIRA &= %11111111_11100000_00000000_00000000 ' float all pins used
PRI SpinRamToDisplay(ramaddress,number) | i ' ramaddress, number of words (pixels), display = 1 or 2
Load161(ramaddress) ' load the 161 counters
LatchGroup2 ' group 2 is block data transfers
CogCmd("U",0,0,number) ' pasm alternative to code below, much faster
DIRA &= %11111111_11100000_00000000_00000000 ' float all pins used
PRI SpinHubToDisplay(hubaddress,number)|i
'LatchGroup2 ' not needed as always do a Draw first
CogCmd("V",hubaddress,0,number) ' pasm version of code below
DIRA &= %11111111_11100000_00000000_00000000 ' float all pins used
' ***********************************************************
PRI NextLocation ' get the next free location in ram based on last files size
if FileNumber == 0
RamLocation[0] := 0 ' work out start of this file based on last file size
else
RamLocation[FileNumber] := RamLocation[FileNumber - 1] + RamSize[FileNumber - 1] ' get location in ram
result := RamLocation[FileNumber]
PRI TouchX
SPI.SHIFTOUT(Touch_DataIn, Touch_Clock, 5, 8 , %1101_0000) ' reads x from 500 to 3700 (off < 500 )
result := SPI.SHIFTIN(Touch_DataOut,Touch_Clock,2, 12)
PRI TouchY
SPI.SHIFTOUT(Touch_DataIn, Touch_Clock, 5, 8 , %1001_0000) ' reads y from 400 to 3800 (off > 3800 )
result := SPI.SHIFTIN(Touch_DataOut,Touch_Clock,2, 12)
PRI TouchValue | xval,yval,x,y ' returns % values 0-100% (or 255 for no touch)
xval := TouchX
yval := TouchY
if (xval => 500) and (yval =< 3800) ' both have to be valid for a touch
pause1ms(2) ' tiny delay then read again as the first reads may not have the correct value
xval := TouchX
yval := TouchY
if (xval => 500) and (yval =< 3800) ' and if valid again, now more likely this value is correct
x := xval - 440 ' scale 0-3360
x /= 32 ' scale 0-100
x <#= 100 ' max 100
x #>=0 ' min 0
x := 100 - x ' so left edge on left
y := yval -420 ' scale 0-3180
y /= 32 ' scale 0-100
y <#= 100 ' max 100
y #>=0 ' min 0
y := 100-y ' so 0 is top left
result := (y <<8) | x ' return 00000000_00000000_YYYYYYYY_XXXXXXXX
if (xval < 500) or (xval >3800)
result := $0000FFFF ' invalid number(s)
PRI ILI9325initiate
' ************* Start Initial Sequence **********
Displaycmd($00E5,$78F0) ' set SRAM internal timing
Displaycmd($0001,$0100) ' set SS and SM bit 0001 0100 portrait
Displaycmd($0002,$0700) ' set 1 line inversion
Displaycmd($0003,$1030) ' set GRAM write direction and BGR=1. $0003 $1030
Displaycmd($0004,$0000) ' Resize register
Displaycmd($0008,$0207) ' set the back porch and front porch
Displaycmd($0009,$0000) ' set non-display area refresh cycle ISC[3:0]
Displaycmd($000A,$0000) ' FMARK function
Displaycmd($000C,$0000) ' RGB interface setting
Displaycmd($000D,$0000) ' Frame marker Position
Displaycmd($000F,$0000) ' RGB interface polarity
' *************Power On sequence ****************//
Displaycmd($0010,$0000) ' SAP, BT[3:0], AP, DSTB, SLP, STB
Displaycmd($0011,$0007) ' DC1[2:0], DC0[2:0], VC[2:0]
Displaycmd($0012,$0000) ' VREG1OUT voltage
Displaycmd($0013,$0000) ' VDV[4:0] for VCOM amplitude
Displaycmd($0007,$0001)
pause1ms(50) ' Dis-charge capacitor power voltage
Displaycmd($0010,$1090) ' 1490//SAP, BT[3:0], AP, DSTB, SLP, STB
Displaycmd($0011,$0227) ' DC1[2:0], DC0[2:0], VC[2:0]
pause1ms(50) ' delay
Displaycmd($0012,$001F) ' 001C// Internal reference voltage= Vci;
pause1ms(50) ' delay
Displaycmd($0013,$1500) ' $1000//1400 Set VDV[4:0] for VCOM amplitude 1A00
Displaycmd($0029,$0027) ' $0012 //001a Set VCM[5:0] for VCOMH //$0025 0034
Displaycmd($002B,$000D) ' Set Frame Rate 000C
pause1ms(50) ' delay
Displaycmd($0020,$0000) ' GRAM horizontal Address
Displaycmd($0021,$0000) ' GRAM Vertical Address
'
Adjust the Gamma Curve
//
Displaycmd($0030,$0000)
Displaycmd($0031,$0707)
Displaycmd($0032,$0307)
Displaycmd($0035,$0200)
Displaycmd($0036,$0008)
Displaycmd($0037,$0004)
Displaycmd($0038,$0000)
Displaycmd($0039,$0707)
Displaycmd($003C,$0002)
Displaycmd($003D,$1D04)
'
Set GRAM area
//
Displaycmd($0050,$0000) ' Horizontal GRAM Start Address
Displaycmd($0051,$00EF) ' Horizontal GRAM End Address
Displaycmd($0052,$0000) ' Vertical GRAM Start Address
Displaycmd($0053,$013F) ' Vertical GRAM Start Address
Displaycmd($0060,$A700) ' Gate Scan Line
Displaycmd($0061,$0001) ' NDL,VLE, REV
Displaycmd($006A,$0000) ' set scrolling line
'
Partial Display Control
/
Displaycmd($0080,$0000)
Displaycmd($0081,$0000)
Displaycmd($0082,$0000)
Displaycmd($0083,$0000)
Displaycmd($0084,$0000)
Displaycmd($0085,$0000)
' //
Panel Control
//
Displaycmd($0090,$0010)
Displaycmd($0092,$0600)
Displaycmd($0007,$0133) ' 262K color and display ON
ChangeOrientation(true) ' default to portrait
PRI SSD1289initiate
{ ILIcmddelay($0000,$0001) 'Turn Oscillator on POR-$0000
ILIcmddelay($0003,$A8A4) 'Power control (1) POR-$6664
ILIcmddelay($000C,$0000) 'Power control (2) POR- ?
ILIcmddelay($000D,$080C) 'Power control (3) POR-$0009
ILIcmddelay($000E,$2B00) 'Power control (4) POR-$3200
ILIcmddelay($001E,$00B0) 'Power control (5) POR-$0029
ILIcmddelay($0001,$4B3F) 'Driver output control, *landscape?? $6B3F* POR-$433F
ILIcmddelay($0002,$0600) 'LCD drive AC control POR-$0400
ILIcmddelay($0010,$0000) 'Sleep Mode sleep mode off POR-$0001
ILIcmddelay($0011,$6070) 'Entry Mode, *landscape? $4030* POR-$6830
ILIcmddelay($0005,$0000) 'Compare Register (1) POR-$0000
ILIcmddelay($0006,$0000) 'Compare Register (2) POR-$0000
ILIcmddelay($0016,$EF1C) 'Horizontal Porch POR-$EFC1
ILIcmddelay($0017,$0003) 'Vertical Porch POR-$0003
ILIcmddelay($0007,$0033) 'Display Control POR-$0000
ILIcmddelay($000B,$D308) 'Frame cycle Control POR-$D308
ILIcmddelay($000F,$0000) 'Gate Scan start position POR-$0000
ILIcmddelay($0041,$0000) 'Vertical Scroll Control (1) POR-$0000
ILIcmddelay($0042,$0000) 'Vertical Scroll Control (2) POR-$0000
ILIcmddelay($0048,$0000) 'First Window Start POR-$0000
ILIcmddelay($0049,$013F) 'First Window End POR-$013F
ILIcmddelay($004A,$0000) 'Second Window Start POR-$0000
ILIcmddelay($004B,$0000) 'Second Window End POR-$013F
ILIcmddelay($0044,$EF00) 'Horizontal Ram Address Postion POR-$EF00
ILIcmddelay($0045,$0000) 'Vertical Ram Address Start POR-$0000
ILIcmddelay($0046,$013F) 'Vertical Ram Address End POR-$013F
ILIcmddelay($0023,$0000) 'RAM write data mask (1) POR-$0000
ILIcmddelay($0024,$0000) 'RAM write data mask (2) POR-$0000
ILIcmddelay($0025,$8000) 'not in datasheet?
ILIcmddelay($004f,0) 'RAM Y address counter POR-$0000
ILIcmddelay($004e,0) 'RAM X address counter POR-$0000
Lcd_Write_Com($0022)
}
' based on C code
Displaycmd($0000,$0001)
Displaycmd($0003,$A8A4)
Displaycmd($000C,$0000)
Displaycmd($000D,$080C)
Displaycmd($000E,$2B00)
Displaycmd($001E,$00B0)
Displaycmd($0001,$2B3F)
Displaycmd($0002,$0600)
Displaycmd($0010,$0000)
Displaycmd($0011,$6070)
Displaycmd($0005,$0000)
Displaycmd($0006,$0000)
Displaycmd($0016,$EF1C)
Displaycmd($0017,$0003)
Displaycmd($0007,$0233)
Displaycmd($000B,$0000)
Displaycmd($000F,$0000)
Displaycmd($0041,$0000)
Displaycmd($0042,$0000)
Displaycmd($0048,$0000)
Displaycmd($0049,$013F)
Displaycmd($004A,$0000)
Displaycmd($004B,$0000)
Displaycmd($0044,$EF00)
Displaycmd($0045,$0000)
Displaycmd($0046,$013F)
Displaycmd($0030,$0707)
Displaycmd($0031,$0204)
Displaycmd($0032,$0204)
Displaycmd($0033,$0502)
Displaycmd($0034,$0507)
Displaycmd($0035,$0204)
Displaycmd($0036,$0204)
Displaycmd($0037,$0502)
Displaycmd($003A,$0302)
Displaycmd($003B,$0302)
Displaycmd($0023,$0000)
Displaycmd($0024,$0000)
Displaycmd($0025,$8000)
Displaycmd($004f,$0000)
Displaycmd($004e,$0000)
Lcd_Write_Com($0022)
PRI Displaycmd(c,d) ' instruction in one method
Lcd_Write_Com(c) ' send out a word
Lcd_Write_Data(d)
PRI Lcd_Write_Com(LCDlong)
LCD_RS_low ' can do rs first then cs - better for latch board
LCD_CS_Low ' not needed for the ILI9325 but the SSD1289 needs this
LCD_Writ_Bus(LCDlong)
LCD_CS_High ' not needed for the ILI9325 but the SSD1289 needs this
PRI Lcd_Write_Data(LCDlong)
LCD_RS_High ' can do rs first then cs
LCD_CS_Low ' not needed for the ILI9325 but the SSD1289 needs this
LCD_Writ_Bus(LCDlong)
LCD_CS_High ' not needed for the ILI9325 but the SSD1289 needs this
PRI Lcd_Write_Fast_Data(LCDlong) ' write RS elsewhere then skip the RS above as this is a latch output and takes longer
LCD_CS_Low ' not needed for the ILI9325 but the SSD1289 needs this
LCD_Writ_Bus(LCDlong)
LCD_CS_High ' not needed for the ILI9325 but the S
I think the driver in post #495 should be good, but it breaks when I copy and paste into the cache driver.
*edit*
Took a quick look over your code, and the one thing I'm seeing is you're not releasing P22... This is the bugger that really annoyed me while working on the driver I posted. IMO, it should NEVER be controlled in Spin. The latch command is the only time it's used and that should only be done in PASM. Makes things much easier. The other thing, That's probably why things are broken. I also "unrolled" a few calls to improve performance I think that covers most changes.
Thinking about it more, there should be no need to "worry" about the latch setting in PASM. As long as it's saved before each operation, and restored after we should be able to "trash" it during the operation. When the cache receives a hit, just back up latch value then restore it.
I'm kinda scratching my head as to the problem. I'll be swapping SRAMs and 161s with my spare set *good thing I have 2 test sets AND a brand new one* I'm almost convinced it's hardware. Although it's more likely I made a mistake when moving commands into driver. I guess I'll take another go at it..
It is deliberate that it is not released in pasm otherwise something else might do a dira on this pin and change it.
I know there's a lot to look at, it took a while to get it working. The one snippet to pay close attention to is the PASM code.
*edit* looking at my code I noticed I missed a few of the DIRA commands. Although Shouldn't hurt anything, it would be better to do So if P22 is accidentally made an output somewhere along the way, it doesn't matter.
1. Load "test_cache" to eeprom *saves re-loading after each test*
2. Open terminal, type "t 0" and press enter, should return 3.reset prop, when terminal returns prompt type "t 2" press enter. Should return 4. reset prop again, this time "i 2000", should return I hope you get different results *tests pass* but verifying it's driver is important.
The only thing that ever uses pin 22 is the latch output. The latch output code is only in pasm. So
would actually be a problem because this would make pin 22 HiZ. That makes all the latch outputs high, which amongst other things, disables the displays. It may or may not matter?
Next idea - how about any pasm routine is allowed to change the latch to whatever it wants. If we do that, then need to set the display /CS lines explicitly in several places - before the startup routine, before a Draw and before changing the orientation, and before a memory dump to the screen. I've rewritten the code here with those changes. So now the S and T commands can change the latch
[code]
CON Touch
'' ILI9325 driver using the Touchblade161 design for faster ram to display transfers
'' Average Joe and James Moxham 2012
Margin = 4 ' some characters have xoffset of -1 or -2 eg v, and won't display on the extreme left edge
_1ms = 1_000_000 / 1_000 ' Divisor for 1 ms
' touchscreen pins
Touch_Clock = 0
'Touch_ChipSelect = done with a group select
Touch_DataIn = 1
Touch_DataOut = 2' touchscreen pins
' kye sd numbers
_dopin = 24
_clkpin = 25
_dipin = 26
_cspin = 27
_cdpin = -1 ' -1 if unused.
_wppin = -1 ' -1 if unused.
_rtcres1 = -1 ' -1 always.
_rtcres2 = -1 ' -1 always.
_rtcres3 = -1 ' -1 always.
_statuspin = -1 ' Status LED pin number.
VAR
byte FileNumber ' 0 to 31
byte DisplayMode ' type of display 0 = ILI9325, 1= SSD1289, 2 etc
byte DisplayLeftRightBoth ' "R", "L" or "B"
word ScreenWidth ' either 240 in portrait or 320 in landscape
word ScreenHeight ' 320 in portrait or 240 in landscape
long orientation ' true is portrait
long curx, cury
byte FontHeight ' size of the current loaded font (pixels = fontsize *.75)
word BackFontColor ' the background color in RRRRRGGG GGGBBBBB format
long StringAddress ' start of string address
long wcboot ' most recent boot warm or cold
'
OBJ
spi: "SPI_ASM" ' thanks to Beau - spi driver for touch screen
fat: "SD-MMC_FATEngine.spin" ' Kye's latest sd driver and no RTC
DAT
RamLocation long $0[31] ' 32 file ram locatinos
RamSize long $0[31] ' File sizes in words - NOT bytes (divide byte by 2)
sdbuffer byte $0[1024] ' 1024 byte buffer for sd card interface and also for sending one row 320 x3 = 960 bytes max picture size
buffer2 byte $0[512] ' 512 general purpose hub buffer
rambuffer word $0[256] ' 512 byte (256 word) buffer easier for using with words
PUB BeginDesktop(DisplayType) ' store the type of display once by the desktop program
DisplayMode := DisplayType ' global variable I = ILI9325, S=SSD1289
Begin(true) ' start up everything running the desktop
result := wcboot
PUB BeginProgram ' used by all programs except the desktop program
Begin(false) ' any program except the desktop
result := wcboot
PRI Begin(Desktop) |n ' desktop true if run from the desktop program, false for other programs
start_ram_cog ' * start cog first *start the external ram / display driver in a cog, needed for many routines so always first
Latch_AllHighStart ' all Latch pins high and enables pin 22
if desktop == false
DisplayMode := GetDisplayMode ' reads "I" or "S" etc off the ram ** must have run desktop at least once first to store this ***
'DisplayMode := "I" ' temp for debugging, using the ILI9325
SelectMemGroup ' select group2
DisplayLeftRightBoth := "B" ' right left or both displays "R", "L", "B"
if displaymode == "I"
Start_ILI9325 ' start the display 0 = ILI9325, 1 = SSD1289
if displaymode == "S"
Start_SSD1289
wcboot := WarmColdBoot ' get the last warm or cold boot value
SetOrientation(true) ' in portrait (true) or landscape mode (false), must be before clearscreen otherwise don't know screensize
if wcboot == false and Desktop == true ' no need to redisplay every time if worked the first time
Text(string("SD")) ' string to send, uses internal prop font so can see something if the SD fails to mount
fat.fatEngineStart( _dopin, _clkpin, _dipin, _cspin, _wppin, _cdpin, _rtcres1, _rtcres2, _rtcres3)
fat.mountPartition(0) ' mount the sd card
if desktop == false ' running a program, this is called without knowing what the ramlocation and ramsize values are
ManualValues ' coming from outside so reset ramsize and ramlocation values for 0 and 1
SetCursor(0,0) ' cursor for debugging to 0,0
if desktop == false
SelectSPIGroup ' selects this group and starts the cog
'PUB BasicTesting
' text(string("Basic testing"))
' sdbuffer[0] := 6
' SpinHubToRam(@sdbuffer,5000,10) ' move data to ram
' sdbuffer[0] := 7 ' change the value
' SpinRamToHub(@sdbuffer,5000,10) ' read back the original value
' hex(sdbuffer[0],2)
' 'Drawline(100,100,110,100,$FFFF)
' Clearrectangle(120,120,129,129,$FFFF)
' Draw(100,100,109,109)
' repeat 100
' pixel(%11111000_00000000)
' ************** string routines stored in external memory so can have arrays and large text files **********************
' to make things simpler, one array StringArray. any number of entries, each up to 128 characters
' pass the source and destination numbers
' TextArray(256) = 256 entries for text files etc
' first entry is destination, second entrie(s) are the source
' Commands are
' StringDim ' reserves memory 256*128, increments filenumber, adds filesize
' StringLoadFile(Name,S) ' read a .txt file off the SD card and stores in TextArray, new string entry is crlf
' StringSaveFile(Name,S,F) ' save .txt file from TextArray to SD card adding in crlf, Start to Finish
' StringClear(Dest) ' clear a string, pass string number
' StringAddChar(Dest,N) ' adds a character to a string at end
' StringSubChar(Dest) ' remove a character from the end
' StringCopy(Dest,Src) ' copy string from one location to another
' StringStore(Dest,Src) ' convert Prop string("mystring") to string stored in external memory
' StringLeft(Dest,Src,N) ' Basic Left$
' StringMid(Dest,Src,L,N) ' Basic Mid$
' StringRight ' Basic Right$
' StringInstr ' Basic Instr
' StringUcase(Dest,Src) ' Basic Ucase$
' StringLcase ' Basic Lcase$
' StringLen ' Length
' StringStr ' string representation of a number
' StringHex(Dest,N) ' number to hex string
' StringBin ' number to binary string
' StringVal ' value (hex is 0x, binary is 0b)
' StringJoin(Dest,Src1,Src2) ' same as Basic + with strings
' StringInsert(Dest,Src1,Src2,n) ' inserts src2 into src1 starting at position n, (1 is first char) and moves to dest
' StringDebug(n) ' print using the green propeller font string number n
' StringLen() ' same as strsize but works on external memory strings
' StringRambuffer ' moves string to rambuffer array and returns result = location of this array
' StringCompare(Source1,Source2) ' returns true if strings equal in length and characters
' use these routines amongst others.
'integerToDecimal(number, length) '' 5 Stack Longs
'integerToHexadecimal(number, length) '' 5 Stack Longs
'integerToBinary(number, length) '' 5 Stack Longs
'decimalToInteger(characters) | sign '' 10 Stack Longs
'hexadecimalToInteger(characters) | sign '' 10 Stack Longs
'binaryToInteger(characters) | sign '' 10 Stack Longs
PUB StringTest ' test all the string routines
'StringDim(10)
'StringStore(5,string("Hello World"))
'StringDebug(5)
'StringClear(5)
'StringAddChar(5,"1")
'StringAddChar(5,"2")
'StringAddChar(5,"3")
'StringAddChar(5,"4")
'StringDebug(5)
'StringSubChar(5) ' remove one character from the right, useful for backspace etc
'StringDebug(5)
'StringCopy(7,5) ' copy
'StringDebug(7)
'StringLeft(7,7,5) ' source and destination the same or different
'StringDebug(7)
'StringMid(8,5,7,3) ' dest, source, starting byte, number of bytes. first byte is 1
'StringDebug(8)
'StringRight(3,5,4) ' dest, source, number of bytes
'StringDebug(3)
'Hex(StringInstr(5,2,"o"),2) ' find this character starting at 2
'StringBin(2,255) ' convert a number to binary always 32 + 2 characters
'StringDebug(2)
'StringHex(4,100) ' always 8 + 2 characters
'StringDebug(4)
'StringDec(9,-20) ' always 11 characters
'StringDebug(9)
'StringStr(9,-7) ' Basic str$ removes leading zeroes, and first character is either - or a space
'StringDebug(9)
'StringStore(5,string("65")) ' test string number to number
'hex(StringVal(5),8)
'StringStore(5,string("0x1234")) ' test string hex to number
'hex(StringVal(5),8)
'StringStore(5,string("0b101")) ' test string binary to number
'hex(StringVal(5),8)
'StringStore(1,string("Hello "))
'StringStore(2,string("World"))
'StringJoin(1,1,2) ' string1 = string1 +string2 (shows dest and a source can be the same - or could be different)
'StringDebug(1)
'StringUcase(1,1) ' Ucase test
'StringDebug(1)
'StringLcase(1,1) ' Lcase test
'StringDebug(1)
'StringStore(1,string(" 123")) ' test the trim function
'StringTrim(1,1)
'StringDebug(1)
'StringStore(1,string("TEST6.TXT")) ' file name
'StringStore(2,string("Line a of text")) ' store some lines of text
'StringStore(3,string("Line b of text"))
'StringStore(4,string("Line c of text"))
'StringSavefile(1,2,4) ' filename is 1, store lines 2 to 4
'StringStore(1,string("TEST6.TXT")) ' file name
'StringLoadFile(1,2) ' read back the file created above.
'Stringdebug(2)
'Stringdebug(3)
'Stringdebug(4)
'repeat
PUB StringDim(n) ' reserve space for a string array, n= number of strings, 128 max bytes per string
Ramsize[Filenumber] := n << 6 ' * 64 as this is in words, not bytes
StringAddress := NextLocation ' get the location of the string array
Filenumber += 1
PUB StringStore(StringN,Source) ' store a propeller string("test") to array number Dest
bytemove(@rambuffer, Source, (strsize(Source) + 1)) ' move to rambuffer array
StringToRam(StringN)
PUB StringDebug(StringN) ' print using the green propeller font - useful if sd card not working so no fonts loaded
RamToString(StringN)
Text(@rambuffer) ' print it out
PUB StringClear(StringN)
bytefill(@rambuffer,0,128) ' only really need to do the first one but may help debugging to completely clear the string
StringToRam(StringN)
PUB StringAddChar(StringN,c) | s ' add c to the string
if c >31 and c <127 ' a character that can be displayed
RamToString(StringN) ' get the string
s := strsize(@rambuffer)
byte[@rambuffer] := c
byte[@rambuffer][s + 1] := 0 ' add in the new zero (can't assume array is cleared)
StringToRam(StringN) ' store back to ram
PUB StringSubChar(StringN) ' remove one character from the right same as strings.left(strings.len - 1)
RamToString(StringN)
if strsize(@rambuffer) <> 0 ' if at least one character
byte[@rambuffer][strsize(@rambuffer)-1] :=0
StringToRam(StringN)
PUB StringCopy(Dest,Source) ' copy string from source to dest
RamToString(Source)
StringToRam(Dest)
PUB StringLeft(Dest,Source,n) ' Basic Left$
RamToString(Source)
byte[@rambuffer][n] := 0 ' move the terminator
StringToRam(Dest) ' move back to ram
PUB StringMid(Dest,Source,start,n) | i ' Basic Mid$
RamToString(Source)
repeat i from 0 to n-1
byte[@rambuffer] := byte[@rambuffer][i+start-1] ' possibly could use bytemove here
byte[@rambuffer][n] := 0 'add in the new zero terminator
StringToRam(Dest)
PUB StringRight(Dest,Source,n) | i,m
RamToString(Source)
m := strsize(@rambuffer) - n
repeat i from 0 to n ' Basic Right$. include the terminator
byte[@rambuffer] := byte[@rambuffer][i+m]
StringToRam(Dest)
PUB StringInstr(Source,Start,c) | i ' find c in the string starting at start
RamToString(Source)
result := 0 ' return 0 if not found
repeat i from (start-1) to strsize(@rambuffer)
if byte[@rambuffer] == c
result := i+1 ' 1 is first character
quit
PUB StringBin(Dest,n) ' convert n to a binary value at Dest. Prefix 0b
repeat result from 33 to 2
byte[@rambuffer][result] := ((n & 1) + "0")
n >>= 1
byte[@rambuffer][34] := 0 ' add zero terminator
byte[@rambuffer][0] := "0" ' prefix 0b same as some versions of C
byte[@rambuffer][1] := "b"
StringToRam(Dest) ' store
PUB StringHex(Dest,n) ' number to hex, always 8 characters
repeat result from 9 to 2
byte[@rambuffer][result] := lookupz((n & $F): "0".."9", "A".."F")
n >>= 4
byte[@rambuffer][10] := 0 ' zero at end
byte[@rambuffer][0] := "0" ' prefix 0x same as C
byte[@rambuffer][1] := "x"
StringToRam(Dest)
PUB StringDec(Dest,n) | sign ' number to decimal string, always 11 characters
sign := "+"
if(n < 0)
sign := "-"
if(n == negx)
bytemove(@rambuffer, string("-2147483648"), 11) ' max negative number special case
else
repeat result from 10 to 1
byte[@rambuffer][result] := ((||(n // 10)) + "0")
n /= 10
byte[@rambuffer][0] := sign ' sign at the front
byte[@rambuffer][11] := 0 ' zero terminator
StringToRam(Dest)
PUB StringStr(Dest,n) | found ' same as Basic STR - similar to Dec above but sign is either space or negative, and leading zeros removed
StringDec(Dest,n) ' number preserved in rambuffer
found := 10 ' if answer was zero don't remove leading zero
repeat result from 1 to 10
if byte[@rambuffer][result] <> "0"
found := result
quit
repeat result from 1 to 11
byte[@rambuffer][result] := byte[@rambuffer][result +found -1] ' shuffle over if needed
if byte[@rambuffer][0] == "+"
byte[@rambuffer][0] := " " ' Basic syntax, - or blank
StringToRam(Dest) ' resend to external ram
PUB StringVal(Source) | sign,i,k ' returns value of the string, hex 0x, binary 0b, decimal + or - or space or no leading character just a number
RamToString(Source)
if byte[@rambuffer][1] == "x" ' this is a hex number
repeat k from 2 to strsize(@rambuffer) -1 ' leading two characters always 0x
result := ((result <- 4) + (byte[@rambuffer][k] & $F))
return
if byte[@rambuffer][1] == "b" ' this is a binary number
repeat k from 2 to strsize(@rambuffer) -1 ' leading two characters always 0b
result := ((result << 1) + (byte[@rambuffer][k] & 1))
return
' else if got here this must be a decimal number. It could start with a number(no leading sign), or a space, or a + or a -
sign := byte[@rambuffer][0]
i := 0 ' start location
if sign == "+" or sign == "-" or sign == " " ' this is a decimal number ** must start with a space or a + or a -**
i := 1 ' leading character so start here
repeat k from i to (strsize(@rambuffer)-1) ' if ever use right to left, don't use step -1, spin puts it in if start is less than finish and if there is a step -1 it is one less loop
result := ((result *10) + byte[@rambuffer][k] & $F) 'very clever, the & $F works because zero is hex0x30, the pre multiply shifts previous numbers to the left so can do left to right. Easier to understand with Kye's binary converter with the left shift
if sign == "-"
result *= -1 ' if negative then negate
PUB StringJoin(Dest,Source1,Source2) ' Dest$= Source1$+Source2$
RamToString(Source2)
bytemove(@buffer2,@rambuffer,128) ' store string2 to buffer2
RamToString(Source1) ' get first string
bytemove(@rambuffer+strsize(@rambuffer),@buffer2,strsize(@buffer2)+1) ' thanks to Kye for this one
StringToRam(Dest) ' store back to ram
PUB StringUcase(Dest,Source)
RamToString(Source)
repeat result from 0 to strsize(@rambuffer)
if ((byte[@rambuffer][result] => "a") and (byte[@rambuffer][result] =< "z"))
byte[@rambuffer][result] -= 32
StringToRam(Dest)
PUB StringLcase(Dest,Source)
RamToString(Source)
repeat result from 0 to strsize(@rambuffer)
if ((byte[@rambuffer][result] => "A") and (byte[@rambuffer][result] =< "Z"))
byte[@rambuffer][result] += 32
StringToRam(Dest)
PUB StringTrim(Dest,Source)
RamToString(Source)
if byte[@rambuffer][0] == "+" or byte[@rambuffer][0] == " " or byte[@rambuffer][0] == "-"
repeat result from 0 to strsize(@rambuffer)
byte[@rambuffer][result] := byte[@rambuffer][result+1] ' delete the first +,- or space
StringToRam(Dest)
PUB StringSaveFile(Filen,Start,Finish) | i,j ' filen might be string array 0, start could be 1, finish could be 4
RamToString(Filen) ' fetch the filename
fat.newfile(@rambuffer) ' create the file, if not working use \fat. for debugging, see PUB openfileread for an example
result := \fat.openfile(@rambuffer,"W") ' open for writing return error if there is one
repeat i from Start to Finish
RamToString(i)
j := strsize(@rambuffer)
byte[@rambuffer][j] := 13 ' carriage return
byte[@rambuffer][j+1] :=10 ' line feed
byte[@rambuffer][j+2] := 0 ' end of new line
fat.writedata(@rambuffer,j+2) ' write this line
FileClose ' close the file
PUB StringLoadFile(Filen,Stringnumber)| s,r,c,t,z ' read filename, new line every crlf. Must have enough space reserved in the string array
RamToString(Filen) ' copy a file from the SD card to the ram disk
OpenFileRead(@rambuffer)
s := 0 ' sd counter
r := 0 ' rambuffer counter
t := 0 ' total number of bytes
z := fat.filesize ' size of the file
repeat (z >> 9) +1 ' include any remainder as +1, do blocks of 512 bytes each as natural size of SD blocks
fat.readdata(@sdbuffer,512) ' read from SD card
repeat s from 0 to 511
c := byte[@sdbuffer] ' get the next byte
if t =< z ' might read in one more 512 byte block to ensure gets any remainder
if c == 13 or r > 126 ' end of a line so new string, or string too long
byte[@rambuffer][r] := 0 ' end of string terminator
StringToRam(Stringnumber)
stringnumber +=1 ' new string number
r := 0 ' start at beginning of rambuffer
if c >31 and c < 127
byte[@rambuffer][r] := c ' store if a valid character
r +=1
t +=1 ' number of bytes read
FileClose ' close the file
PUB StringLen(Source) ' string length
RamToString(Source)
result := strsize(@rambuffer)
PUB StringPrint(Font,Source) | i,s ' print this string using using font in memory and curx and cury
RamToString(Source)
s := strsize(@rambuffer)
repeat i from 0 to (s-1)
curx := TextChar(Font,byte[@rambuffer],curx,cury)
PUB StringRamBuffer(Source) ' string to rambuffer array
RamToString(Source)
result := @rambuffer ' return pointer to the rambuffer array
PUB StringCompare(Source1,Source2) | i
RamToString(Source2)
bytemove(@buffer2,@rambuffer,128) ' store string2 to buffer2
RamToString(Source1)
result := true
repeat i from 0 to strsize(@rambuffer)
if byte[@buffer2] <> byte[@rambuffer]
result := false
return
PRI StringToRam(StringN)
HubToRam(StringN << 6 + StringAddress,64) ' 64 words =128 bytes, so work in groups of 64 words
PRI RamToString(StringN)
RamToHub(StringN << 6 + StringAddress,64) ' moves to rambuffer array
' ***************** end string routines *************************
PUB DisplayLeft ' send all data to the left display
DisplayLeftRightBoth := "L" ' don't enable here, do this just before sending data
PUB DisplayRight
DisplayLeftRightBoth := "R"
PUB RamErase(n)
Filenumber := n ' erase lookup table for the ram files, effectively erases the files
' if 0 then erase all, if 1 then leave the desktop intact (assuming this was loaded first)
PUB ClearRam
Filenumber := 2 ' clear ram but leave file 0 = warm or cold boot and 1 = background picture
PUB GetCurX
result := curx ' get the text cursor position
PUB GetCurY
result := cury
PUB SetCurx(n) ' set the text cursor position
Curx := n
PUB SetCury(n)
Cury := n
PUB GetBackFontColor
result := BackFontColor
PUB GetRamLocation(n)
result := Ramlocation[n]
PUB GetFilenumber
result := filenumber
PUB SetFilenumber(n) ' reset the file number
Filenumber := n
PUB Chain(stringptr)
result := \fat.bootpartition(stringptr) ' reboot and run this program
PUB SDtoRam(stringptr) | RamAddress,remainder,sizebytes,sizewords ' copy a file from the SD card to the ram disk
OpenFileRead(stringptr)
sizebytes := fat.filesize ' size in bytes
sizewords := sizebytes >>1 ' size in words
ramaddress := NextLocation ' next free place in ram
RamSize[FileNumber] := sizewords ' ram disk works in words, not bytes
repeat sizebytes >> 9 ' do blocks of 512 bytes each as natural size of SD blocks
fat.readdata(@sdbuffer,512) ' read from SD card
SpinHubToRam(@sdbuffer,RamAddress,512) ' move to ram
RamAddress += 256 ' increment ram address by 256 (half 512 as using words)
remainder := sizebytes & %00000000_00000000_00000001_11111111 ' remainder (if not a multiple of 512)
if remainder <> 0
fat.readdata(@sdbuffer,remainder)
SpinHubToRam(@sdbuffer,RamAddress,remainder) ' send out the remainder bytes
FileClose ' close the file
result := FileNumber ' return this file
FileNumber +=1 ' point to next location
PUB FetchRamBuffer
result := @rambuffer
PUB SDBMPtoRam(stringptr) | width,height,w,i,ramaddress ' .bmp from sd to ram and convert to 2 byte ili format and reverse order along the way
' http://en.wikipedia.org/wiki/BMP_file_format scroll down about 1/3 of the way for the header formats
' get the width, height
' store these as 2 longs at the beginning of the ram file
' the bitmap format starts at the last row and works up so need to reverse the order
ramaddress := NextLocation ' get the next ram location
OpenFileRead(stringptr)
fat.readdata(@sdbuffer,$36) ' get the header 0x36 hex bytes
width := sdbuffer[$12] + sdbuffer[$13] << 8 ' only read in two bytes as never will be >64k wide or high
height := sdbuffer[$16] + sdbuffer[$17] << 8
w := width * 3 ' this number of bytes per row, and round up to nearest 4 bytes et 327 goes to 328 The size of each row is rounded up to a multiple of 4 bytes (a 32-bit DWORD) by padding.
w +=3 ' add 3 and
w &= %11111111_11111111_11111111_11111100 ' round down
SpinHubToRam(@sdbuffer,ramaddress,$36) ' store header to ram
i := $36 + ramaddress + ((height - 1) * width) ' start + header and on the last row and work back
repeat height
fat.readdata(@sdbuffer,w) ' read in the first row
CogCmd("F",@sdbuffer,@rambuffer,width)
HubToRam(i,width) ' send to ram
i -= width ' subtract the width, move up the picture
FileClose
Ramsize[Filenumber] := $36 + (width * height) ' size of bitmap file in words
result := FileNumber ' returns the current file number
FileNumber += 1 ' add 1 to filenumber
PUB DrawBMPRam(f,x,y) | rampointer,width,height ' pass the filenumber and the x and y location, searches for this file and if found, displays it
rampointer := RamLocation[f] ' find the start of the file
SpinRamToHub(@sdbuffer,rampointer,$36) ' read in the header
width := sdbuffer[$12] + sdbuffer[$13] << 8 ' only read in two bytes as never will be >64k wide or high
height := sdbuffer[$16] + sdbuffer[$17] << 8
Draw(x,y,x+width-1,y+height-1) ' set up the area to draw
RamToDisplay(RamPointer + $36,width*height) ' skip the header and display
result := (width <<16) | (height & $0000FFFF) ' return the width and height combined into a long
' ***************** end ram disk routines
PUB MergeBackgroundIconRam(stringptr,x,y,desktop,mask,icon) | i,j,k ' merge icon and background - if mask =0 then background, else icon (not classic alpha compositing but close enough)
' i is background counter, j is icon counter, k = mask counter
' assumes desktop is in ramfile 1, mask in ramfile 2, icon in ramfile 3
FileNumber := icon
SDBMPtoRam(stringptr) ' load icon into ram
Filenumber := icon ' reset filenumber
i := RamLocation[desktop] + $36 + (y*(screenwidth+1)+x) ' location of picture pixel in ram
j := RamLocation[icon] + $36 ' location of icon in pixel in ram
k := RamLocation[mask] + $36 ' location of mask pixel in ram
repeat 60
SpinRamToHub(@sdbuffer,i,59) ' get 59 pixels from background
SpinRamToHub(@rambuffer,j,59) ' get 59 icon pixels
SpinRamToHub(@buffer2,k,59) ' get words for the mask
CogCmd("X",@sdbuffer,@rambuffer,@buffer2) ' pasm version of the above 3 lines
SpinHubToRam(@sdbuffer,i,59) ' send back modified line to the picture buffer
i += screenwidth+1 ' next line in picture
j +=59 ' icon next line
k +=59 ' next line in mask (bytes) - buffer2
' old "X" command code
'repeat n from 0 to 58
' if (word[@buffer2][n] & %00000000_00011111) > %10000 ' RGB are the same, so use BBBBB, mask 5 low bits, and if > %10000
' word[@sdbuffer][n] :=word[@rambuffer][n] ' if >128 then replace with icon pixel
PUB OpenFileRead(stringptr) ' open a file for reading and display a message if it fails
result := \fat.openfile(stringptr,"R")
if fat.partitionerror <> 0 ' failed to find the file
Text(stringptr) ' print the filename
Text(result) ' print the error message
repeat ' stop the program
PUB FileClose
fat.closefile ' close the file
PUB TouchXPercent
result := TouchValue & 255 ' decode xval 0-100%
if DisplayMode == "S"
result := 100-result ' SSD1289 x runs the other way
PUB TouchYPercent
result := TouchValue >> 8 ' decode yval 0-100%
PUB TouchSwishX | xval,oldxval,difference,average ' returns result = +ve or -ve depending on swish direction ' did finger move left or right?
SelectSPIGroup ' turn on the spi touchscreen
oldxval := 255' for startup
average := 0
repeat until (average > 30) or (average < -30)
' possibly add a decay function here for average
pause1ms(50)
xval := TouchXPercent ' decode xval 0-100%
if xval == 255 ' not touched
oldxval := 255 ' reset oldxval as well
else
if oldxval <>255 ' discard the first reading as the screen is touched
difference := xval - oldxval
'printdecimal(xval)
'printdecimal(difference)
average := average + difference '
'printdecimal(average)
'crlf
oldxval := xval ' store for next time
result := average ' return negative or positive value
PUB TouchSwishY | yval,oldyval,difference,average ' returns result = +ve or -ve depending on swish direction ' did finger move left or right?
SelectSPIGroup ' turn on the spi touchscreen
oldyval := 255' for startup
average := 0
repeat until (average > 30) or (average < -30)
' possibly add a decay function here for average
pause1ms(50)
yval := TouchYPercent ' decode xval 0-100%
if yval == 255 ' not touched
oldyval := 255 ' reset oldyval as well
else
if oldyval <>255 ' discard the first reading as the screen is touched
difference := yval - oldyval
average := average + difference '
oldyval := yval ' store for next time
result := average ' return negative or positive value
PUB TouchStart
SPI.start(3,0) ' delay,state, start clock high
PUB TextChar(FileN,ascii,x,y) | jump,size,width,height,ramaddress,xoffset,yoffset,xadvance ' read out a character from a .ifn file stored in ram
' pass Fonttable = global
' moves to next line if off the end
ramaddress := RamLocation[FileN]
jump := ReadRamLong(ramaddress,(ascii << 2) + 256) ' jump location = ascii *4 plus fonttable + 256
SpinRamToHub(@buffer2,ramaddress+jump>>1,20) ' read in the width height as a block = quicker than readramlong
size := long[@buffer2][0] ' size in pixels of this character
xadvance := long[@buffer2][5] ' amount to move to next character
'if size > 0 and ascii <> 32 ' no need to print anything if size is zero or read more font data
width := long[@buffer2][1] ' width
height := long[@buffer2][2] ' height
xoffset := long[@buffer2][3] ' xoffset to move
yoffset := long[@buffer2][4] ' yoffset to move
if (x+width -1 + xoffset) > screenwidth
crlf ' do a new line if it won't fit
x := curx
y := cury
if ascii < 32
xadvance := 0 ' add nothing if a non displaying character
if ascii == 13 ' carriage return
cr
x := curx
if ascii ==10 ' line feed (new line)
lf
y := cury
if ascii > 32 ' space not displayed
Draw(x+xoffset,y+yoffset,x+width-1+xoffset,y+height-1+yoffset) ' draw on screen
RamToDisplay(ramaddress+((jump+32)>>1),size) ' move bytes from ram out to the display
return x + xadvance
PUB crlf
cr
lf
PUB cr ' carriage return, to left margin
curx := Margin ' some characters are -1 xoffset so won't display
PUB lf ' line feed, move up one
cury += FontHeight
if cury > screenheight
ClearRectangle(0,0,screenwidth,screenheight,BackFontColor)
cury := 0 ' if off the bottom of the screen then clear screen start at the top (VT00 does scrolling)
' don't clear background here, do in calling routine
'ClearFontBackground(0,cury,screenwidth,cury + FontHeight) ' clear background line to the background color ready to draw
PUB TextT(FileN,stringptr) 'print at curx,cury using file number in ramdisk
'' Print a zero-terminated string
repeat strsize(stringptr)
curx := TextChar(FileN,byte[stringptr++],curx,cury)
PUB SetCursor(x,y)
curx := x
cury := y
PUB ReadRamLong(WordStart,Bytevalue) ' easier calculating
SpinRamToHub(@result,Wordstart+Bytevalue>>1,2)
return result
PUB LoadFont(stringptr) | filesize,i ' read a .ifn file premade in angelcode bmfont and then the vb.net program
SDtoRam(stringptr) ' font to ram disk, adds one to filenumber
SpinRamToHub(@rambuffer,RamLocation[Filenumber-1],128) ' read first 128 words =256 bytes back
FontHeight := byte[@rambuffer][251] ' font height
repeat i from 0 to 2
sdbuffer := byte[@rambuffer][35+i]
CogCmd("E",@sdbuffer,@buffer2,1) ' convert to 2 byte .ili format
BackFontColor := ( byte[@buffer2][1] << 8) | byte[@buffer2][0] ' correct order
result := FileNumber-1 ' return pointer to the font file in ram
PUB HubToRam(RamAddress,Number) ' send pixels from rambuffer hub to ram
SpinHubToRam(@rambuffer,RamAddress,Number) ' spin version
PUB RamToHub(RamAddress,Number) ' send pixels from ram to hub at rambuffer
SpinRamToHub(@rambuffer,Ramaddress, number)
PUB RamToDisplay(RamAddress,Number) ' send pixels from ram to display
SpinRamToDisplay(Ramaddress, Number)
PUB Clearscreen(color) ' uses 2 byte ILI color (see below for rgb conversion)
wordfill(@rambuffer,color,256)
Draw(0,0,screenwidth,screenheight) ' set up the area of the screen to draw in
repeat 300
SpinHubToDisplay(@rambuffer,256)
PUB RGBtoWord(R,G,B) ' convert RGB to 2 byte ILI color
sdbuffer[0] := R
sdbuffer[1] := G
sdbuffer[2] := B
CogCmd("E",@sdbuffer,@buffer2,1)
result := ( byte[@buffer2][1] << 8) | byte[@buffer2][0] ' correct order
PUB ClearRectangle(x1,y1,x2,y2,ColorWord) | i,size,remainder ' clears an area of the screen to the current font background color
size := (x2-x1+1) * (y2 - y1 +1) ' number of pixels
remainder := size & 255 ' if not a whole number of 256 pixels
repeat i from 0 to 255 ' move the background font colour to the buffer
rambuffer := ColorWord
Draw(x1,y1,x2,y2) ' set up the area of the screen to draw in
repeat size >> 8 ' 256 pixel blocks
SpinHubToDisplay(@rambuffer,256)
if remainder <> 0
SpinHubToDisplay(@rambuffer,remainder) ' do the remainder
PUB Drawline(x1,y1,x2,y2,ColorWord) ' draws a vertical or horizontal line
ClearRectangle(x1,y1,x2,y2,ColorWord) ' same as drawing a rectangle
PUB SetOrientation(e) ' change orientation true = portrait. Changes global variable 'orientation' and also screen width and height
orientation := e
ChangeOrientation(orientation)
if orientation
screenwidth :=239
screenheight :=319
else
screenwidth :=319
screenheight :=239
PUB ChangeOrientation(n) ' pass true = portrait or false = landscape, changes global variable orientation in this object
' command $0001
'8.2.4. Driver Output Control (R01h)
'R/W RS D15 D14 D13 D12 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0
'W 1 0 0 0 0 0 SM 0 SS 0 0 0 0 0 0 0 0
'SS: Select the shift direction of outputs from the source driver.
'When SS = 0, the shift direction of outputs is from S1 to S720
'When SS = 1, the shift direction of outputs is from S720 to S1.
'In addition to the shift direction, the settings for both SS and BGR bits are required to change the
'assignment of R, G, B dots to the source driver pins.
'To assign R, G, B dots to the source driver pins from S1 to S720, set SS = 0.
'To assign R, G, B dots to the source driver pins from S720 to S1, set SS = 1.
' command $0003
'R/W RS D15 D14 D13 D12 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0
'W 1 TRI DFM 0 BGR 0 0 HWM 0 ORG 0 I/D1 I/D0 AM 0 0 0
' When AM = 0, the address is updated in horizontal writing direction.
'When AM = 1, the address is updated in vertical writing direction.
'I/D[1:0] Control the address counter (AC) to automatically increase or decrease by 1 when update one pixel
' display data.
SelectMemGroup
DisplayEnable ' enable the display(s)
orientation := n
if displaymode == "I" ' ili9325
if orientation
' for origin top left and portrait mode
Displaycmd($0001,%00000001_00000000) ' $0001,$0100 set SS and SM bit 0001 0100 portrait
Displaycmd($0003,%00010000_00110000) ' $0003,$1030 set GRAM write direction and BGR=1. $0003 $1030
else
' for origin top right and landscape mode
Displaycmd($0001,%00000000_00000000) ' set SS and SM bit 0001 0000 landscape
Displaycmd($0003,%00010000_00111000) ' landscape $1028 = original but 1038 is correct - not mirror image
if displaymode == "S" ' SSD1289
if orientation
' for origin top left and portrait mode
Displaycmd($0001,$2B3F) ' Entry mode portrait
Displaycmd($0011,$6070) ' '
else
' for origin top right and landscape mode
Displaycmd($0001,$6B3F) ' landscape $2B3F = original but 6B3F is correct
Displaycmd($0011,$6838) ' landscape $1028 = original but 6838 is correct
PUB SelectMemGroup ' select group 1 for memory transfer
spi.stop ' and stop any other cogs here too if needed
LatchGroup2
DIRA |= %00000000_01011111_11111111_11111111 ' enable these pins for output
OUTA |= %00000000_00011111_00000000_00000000 ' set /rd /wr /disp wr and 161 clock high
PUB SelectSPIGroup
LatchGroup3 ' select group 2 for spi transfers
DIRA &= %11111111_11111111_11111111_11000000 ' so no clashes with the cog using P0-P2 set these as inputs and P3-P5
TouchStart ' start the touchscreen cog
' may need to set some pins here as inputs or outputs with a dira
PUB Text(stringptr) 'print at curx,cury
repeat strsize(stringptr)
Propfont_out(byte[stringptr++])
PropFontcrlf
PUB PropFontcrlf
curx := 0
cury += 32 ' new line at end of string
if cury >319 ' bottom of screen so new screen
curx:=0
cury:=0
PUB Propfont_out(ascii) | address,pixels
Draw(curx,cury,curx+15,cury+31) ' location to start drawing
address := $8000 + (ascii >> 1) << 7 ' get rom address
repeat 32 ' 32 rows per character, split in two parts
pixels := long[address] ' get rom font data
pixels := pixels >> (ascii & 1) ' shift for odd characters
repeat 16 ' 16 columns
if pixels & 1
Pixel(%00000111_11100000) ' foreground color RRRRRGGG_GGGBBBBB
else
Pixel(%00000000_00000000) ' background color
pixels := pixels >> 2 ' alternate pixels interleaved so shift 2
address += 4
curx +=16
if curx >239 ' new line
Propfontcrlf
PUB Start_ILI9325
DisplayEnable ' enable one or both displays
LatchGroup2 ' talk to this display
LCD_Reset_High
pause1ms(5)
LCD_Reset_Low
pause1ms(5)
LCD_Reset_High
LCD_CS_High
LCD_RD_High
LCD_WR_High
pause1ms(20)
LCD_CS_Low
ILI9325initiate
PUB Start_SSD1289 ' based on C driver
DisplayEnable ' enable one or both displays
LatchGroup2 ' talk to this display
LCD_Reset_High
pause1ms(5)
LCD_Reset_Low
pause1ms(10)
LCD_Reset_High
LCD_CS_High
LCD_RD_High
LCD_WR_High
pause1ms(20)
SSD1289initiate
PUB Draw(x1, y1, x2, y2) ' sets the pixel to x1,y1 and then fills the next (x2-x1)*(y2-y1) pixels
DisplayEnable ' enable one or both displays
SelectMemGroup ' select memory group
ifnot orientation ' landscape mode so swap x and y
result :=x1 ' swap x1 and y1
x1 := y1
y1 := result
result := x2 ' swap x2 and y2
x2 :=y2
y2 := result
if displaymode == "I" ' ILI9325 display
Displaycmd($0050,x1)
Displaycmd($0052,y1)
Displaycmd($0051,x2)
Displaycmd($0053,y2)
Displaycmd($0020,x1)
Displaycmd($0021,y1)
Lcd_Write_Com($0022)
if displaymode == "S" ' SSD1289 display
Displaycmd($0044,(x2<<8)+x1)
Displaycmd($0045,y1)
Displaycmd($0046,y2)
Displaycmd($004e,x1)
Displaycmd($004f,y1)
Lcd_Write_Com($0022)
LCD_RS_High ' after sending out commands, set RS high via latch
PUB Pixel(pixelcolor) ' send out a pixel
Lcd_Write_Fast_Data(pixelcolor) ' need to set RS high at beginning of this group (ie call Draw first)
' it is more efficent to send one Draw command then lots of pixels than sending individual pixels
' but of course, you can send draw x,y,x,y where x and y are the same and then send one pixel
PUB pause1ms(period) | clkcycles
'' Pause execution for period (in units of 1 ms).
clkcycles := ((clkfreq / _1ms * period) - 4296) #> 381 ' Calculate 1 ms time unit
waitcnt(clkcycles + cnt) ' Wait for designated time
PUB hex(value, digits) ' uses internal prop font and slow pixel drawing (for very basic debugging without an SD font)
SelectMemGroup ' reselect mem group for display output for debugging
'' Print a hexadecimal number
propfont_out("O")
propfont_out("x")
value <<= (8 - digits) << 2
repeat digits
propfont_out(lookupz((value <-= 4) & $F : "0".."9", "A".."F"))
PropFontcrlf
PUB SetWarmBoot
long[@rambuffer][5] := $5761726D ' spell Warm in hex, start at 5 as zero gets corrupted
byte[@rambuffer][10] := DisplayMode ' store the current display mode
HubToRam(0,20) ' send words to external ram (ramdiskstart is zero)
ClearScreen(0) ' so user sees something happen as there is a bit of a delay rebooting
reboot
PUB WarmColdBoot ' store warm or cold boot - reserve first 20 bytes in sram for interfacing with programs
' Ramdisk file 0 is the boot string Warm or Cold
' ramdisk file 1 is the background
Ramtohub(0,20) ' read words from the ram
if long[@rambuffer][5] == $5761726D
result := true
else
result := false
long[@rambuffer][5] := $FFFFFFFF ' erase previous warm/cold boot
byte[@rambuffer][10] := DisplayMode ' store display mode
HubToRam(0,20) ' send words to external ram (ramdiskstart is zero)
RamSize[0] := 20 ' reserve 20 bytes
RamLocation[0] := 0
Filenumber := 1
PRI GetDisplayMode
Ramtohub(0,20) ' read words from the ram
result := byte[@rambuffer][10] ' get the display mode
PUB ManualValues
RamLocation[0] := 0
RamSize[0] := $14 ' put in values manually - 20 bytes for warm boot
RamLocation[1] := $14
RamSize[1] := $12C36 ' 76800 for the desktop picture
FileNumber := 2 ' running a program not a desktop
PRI LatchGroup1
CogCmd("O",%00001111,0,0) ' set all group pins high
CogCmd("A",%11111110,0,0) 'set this one low
PRI LatchGroup2
CogCmd("O",%00001111,0,0) ' set all group pins high
CogCmd("A",%11111101,0,0) 'set this one low
PRI LatchGroup3
CogCmd("O",%00001111,0,0) ' set all group pins high
CogCmd("A",%11111011,0,0) 'set this one low
PRI LatchGroup4
CogCmd("O",%00001111,0,0) ' set all group pins high
CogCmd("A",%11110111,0,0) 'set this one low
PRI Latch_ResetLow
CogCmd("A",%11101111,0,0) 'set this one low
PRI Latch_ResetHigh
CogCmd("O",%00010000,0,0) ' set this pin high
PRI Latch_DisplayL ' display on the left
CogCmd("O",%01100000,0,0) ' both displays high
CogCmd("A",%11011111,0,0) 'set this one low
PRI Latch_DisplayR ' display on the right
CogCmd("O",%01100000,0,0) ' both displays high
CogCmd("A",%10111111,0,0) 'set this one low
PRI Latch_DisplayB ' both displays for debugging
CogCmd("A",%10011111,0,0) 'set both displays low
PRI Latch_RS_Low
CogCmd("A",%01111111,0,0) 'set this one low
PRI Latch_RS_High
CogCmd("O",%10000000,0,0) ' set this pin high
PRI Latch_AllHighStart ' all pins high
CogCmd("O",%11111111,0,0) ' set all pins high (disabled)
PRI Load161(address)
CogCmd("Z",0,address,0) ' call pasm version, faster, changes the latch but the next PUB always resets it anyway
PRI DisplayEnable ' pass DisplayRightLeftBoth
case DisplayLeftRightBoth
"R":Latch_DisplayR
"L":Latch_DisplayL
"B":Latch_DisplayB
PRI SpinHubToRam(hubaddress, ramaddress, number) | i ' P0-P15 are data lines, P16 is /RD, P17 is /WR
Load161(ramaddress) ' load the 161 counters
LatchGroup2 ' group 2 is block data transfers
CogCmd("S",hubaddress,ramaddress,number) ' pasm alternative to code below
DIRA &= %11111111_11100000_00000000_00000000 ' float all pins used
PRI SpinRamToHub(hubaddress,ramaddress,number) |i ' P0-P15 are data lines, P16 is /RD, P17 is /WR
Load161(ramaddress) ' load the 161 counters
LatchGroup2 ' group 2 is block data transfers
CogCmd("T",hubaddress,ramaddress,number) ' pasm alternative to code below
DIRA &= %11111111_11100000_00000000_00000000 ' float all pins used
PRI SpinRamToDisplay(ramaddress,number) | i ' ramaddress, number of words (pixels), display = 1 or 2
Load161(ramaddress) ' load the 161 counters
LatchGroup2 ' group 2 is block data transfers
DisplayEnable ' latch output one or both displays
CogCmd("U",0,0,number) ' pasm alternative to code below, much faster
DIRA &= %11111111_11100000_00000000_00000000 ' float all pins used
PRI SpinHubToDisplay(hubaddress,number)|i
'LatchGroup2 ' not needed as always do a Draw first, also no need to do displayenable as this is in a draw as well
CogCmd("V",hubaddress,0,number) ' pasm version of code below
DIRA &= %11111111_11100000_00000000_00000000 ' float all pins used
' ***********************************************************
PRI NextLocation ' get the next free location in ram based on last files size
if FileNumber == 0
RamLocation[0] := 0 ' work out start of this file based on last file size
else
RamLocation[FileNumber] := RamLocation[FileNumber - 1] + RamSize[FileNumber - 1] ' get location in ram
result := RamLocation[FileNumber]
PRI TouchX
SPI.SHIFTOUT(Touch_DataIn, Touch_Clock, 5, 8 , %1101_0000) ' reads x from 500 to 3700 (off < 500 )
result := SPI.SHIFTIN(Touch_DataOut,Touch_Clock,2, 12)
PRI TouchY
SPI.SHIFTOUT(Touch_DataIn, Touch_Clock, 5, 8 , %1001_0000) ' reads y from 400 to 3800 (off > 3800 )
result := SPI.SHIFTIN(Touch_DataOut,Touch_Clock,2, 12)
PRI TouchValue | xval,yval,x,y ' returns % values 0-100% (or 255 for no touch)
xval := TouchX
yval := TouchY
if (xval => 500) and (yval =< 3800) ' both have to be valid for a touch
pause1ms(2) ' tiny delay then read again as the first reads may not have the correct value
xval := TouchX
yval := TouchY
if (xval => 500) and (yval =< 3800) ' and if valid again, now more likely this value is correct
x := xval - 440 ' scale 0-3360
x /= 32 ' scale 0-100
x <#= 100 ' max 100
x #>=0 ' min 0
x := 100 - x ' so left edge on left
y := yval -420 ' scale 0-3180
y /= 32 ' scale 0-100
y <#= 100 ' max 100
y #>=0 ' min 0
y := 100-y ' so 0 is top left
result := (y <<8) | x ' return 00000000_00000000_YYYYYYYY_XXXXXXXX
if (xval < 500) or (xval >3800)
result := $0000FFFF ' invalid number(s)
PRI ILI9325initiate
' ************* Start Initial Sequence **********
Displaycmd($00E5,$78F0) ' set SRAM internal timing
Displaycmd($0001,$0100) ' set SS and SM bit 0001 0100 portrait
Displaycmd($0002,$0700) ' set 1 line inversion
Displaycmd($0003,$1030) ' set GRAM write direction and BGR=1. $0003 $1030
Displaycmd($0004,$0000) ' Resize register
Displaycmd($0008,$0207) ' set the back porch and front porch
Displaycmd($0009,$0000) ' set non-display area refresh cycle ISC[3:0]
Displaycmd($000A,$0000) ' FMARK function
Displaycmd($000C,$0000) ' RGB interface setting
Displaycmd($000D,$0000) ' Frame marker Position
Displaycmd($000F,$0000) ' RGB interface polarity
' *************Power On sequence ****************//
Displaycmd($0010,$0000) ' SAP, BT[3:0], AP, DSTB, SLP, STB
Displaycmd($0011,$0007) ' DC1[2:0], DC0[2:0], VC[2:0]
Displaycmd($0012,$0000) ' VREG1OUT voltage
Displaycmd($0013,$0000) ' VDV[4:0] for VCOM amplitude
Displaycmd($0007,$0001)
pause1ms(50) ' Dis-charge capacitor power voltage
Displaycmd($0010,$1090) ' 1490//SAP, BT[3:0], AP, DSTB, SLP, STB
Displaycmd($0011,$0227) ' DC1[2:0], DC0[2:0], VC[2:0]
pause1ms(50) ' delay
Displaycmd($0012,$001F) ' 001C// Internal reference voltage= Vci;
pause1ms(50) ' delay
Displaycmd($0013,$1500) ' $1000//1400 Set VDV[4:0] for VCOM amplitude 1A00
Displaycmd($0029,$0027) ' $0012 //001a Set VCM[5:0] for VCOMH //$0025 0034
Displaycmd($002B,$000D) ' Set Frame Rate 000C
pause1ms(50) ' delay
Displaycmd($0020,$0000) ' GRAM horizontal Address
Displaycmd($0021,$0000) ' GRAM Vertical Address
'
Adjust the Gamma Curve
//
Displaycmd($0030,$0000)
Displaycmd($0031,$0707)
Displaycmd($0032,$0307)
Displaycmd($0035,$0200)
Displaycmd($0036,$0008)
Displaycmd($0037,$0004)
Displaycmd($0038,$0000)
Displaycmd($0039,$0707)
Displaycmd($003C,$0002)
Displaycmd($003D,$1D04)
'
Set GRAM area
//
Displaycmd($0050,$0000) ' Horizontal GRAM Start Address
Displaycmd($0051,$00EF) ' Horizontal GRAM End Address
Displaycmd($0052,$0000) ' Vertical GRAM Start Address
Displaycmd($0053,$013F) ' Vertical GRAM Start Address
Displaycmd($0060,$A700) ' Gate Scan Line
Displaycmd($0061,$0001) ' NDL,VLE, REV
Displaycmd($006A,$0000) ' set scrolling line
'
Partial Display Control
/
Displaycmd($0080,$0000)
Displaycmd($0081,$0000)
Displaycmd($0082,$0000)
Displaycmd($0083,$0000)
Displaycmd($0084,$0000)
Displaycmd($0085,$0000)
' //
Panel Control
//
Displaycmd($0090,$0010)
Displaycmd($0092,$0600)
Displaycmd($0007,$0133) ' 262K color and display ON
ChangeOrientation(true) ' default to portrait
PRI SSD1289initiate
{ ILIcmddelay($0000,$0001) 'Turn Oscillator on POR-$0000
ILIcmddelay($0003,$A8A4) 'Power control (1) POR-$6664
ILIcmddelay($000C,$0000) 'Power control (2) POR- ?
ILIcmddelay($000D,$080C) 'Power control (3) POR-$0009
ILIcmddelay($000E,$2B00) 'Power control (4) POR-$3200
ILIcmddelay($001E,$00B0) 'Power control (5) POR-$0029
ILIcmddelay($0001,$4B3F) 'Driver output control, *landscape?? $6B3F* POR-$433F
ILIcmddelay($0002,$0600) 'LCD drive AC control POR-$0400
ILIcmddelay($0010,$0000) 'Sleep Mode sleep mode off POR-$0001
ILIcmddelay($0011,$6070) 'Entry Mode, *landscape? $4030* POR-$6830
ILIcmddelay($0005,$0000) 'Compare Register (1) POR-$0000
ILIcmddelay($0006,$0000) 'Compare Register (2) POR-$0000
ILIcmddelay($0016,$EF1C) 'Horizontal Porch POR-$EFC1
ILIcmddelay($0017,$0003) 'Vertical Porch POR-$0003
ILIcmddelay($0007,$0033) 'Display Control POR-$0000
ILIcmddelay($000B,$D308) 'Frame cycle Control POR-$D308
ILIcmddelay($000F,$0000) 'Gate Scan start position POR-$0000
ILIcmddelay($0041,$0000) 'Vertical Scroll Control (1) POR-$0000
ILIcmddelay($0042,$0000) 'Vertical Scroll Control (2) POR-$0000
ILIcmddelay($0048,$0000) 'First Window Start POR-$0000
ILIcmddelay($0049,$013F) 'First Window End POR-$013F
ILIcmddelay($004A,$0000) 'Second Window Start POR-$0000
ILIcmddelay($004B,$0000) 'Second Window End POR-$013F
ILIcmddelay($0044,$EF00) 'Horizontal Ram Address Postion POR-$EF00
ILIcmddelay($0045,$0000) 'Vertical Ram Address Start POR-$0000
ILIcmddelay($0046,$013F) 'Vertical Ram Address End POR-$013F
ILIcmddelay($0023,$0000) 'RAM write data mask (1) POR-$0000
ILIcmddelay($0024,$0000) 'RAM write data mask (2) POR-$0000
ILIcmddelay($0025,$8000) 'not in datasheet?
ILIcmddelay($004f,0) 'RAM Y address counter POR-$0000
ILIcmddelay($004e,0) 'RAM X address counter POR-$0000
Lcd_Write_Com($0022)
}
' based on C code
Displaycmd($0000,$0001)
Displaycmd($0003,$A8A4)
Displaycmd($000C,$0000)
Displaycmd($000D,$080C)
Displaycmd($000E,$2B00)
Displaycmd($001E,$00B0)
Displaycmd($0001,$2B3F)
Displaycmd($0002,$0600)
Displaycmd($0010,$0000)
Displaycmd($0011,$6070)
Displaycmd($0005,$0000)
Displaycmd($0006,$0000)
Displaycmd($0016,$EF1C)
Displaycmd($0017,$0003)
Displaycmd($0007,$0233)
Displaycmd($000B,$0000)
Displaycmd($000F,$0000)
Displaycmd($0041,$0000)
Displaycmd($0042,$0000)
Displaycmd($0048,$0000)
Displaycmd($0049,$013F)
Displaycmd($004A,$0000)
Displaycmd($004B,$0000)
Displaycmd($0044,$EF00)
Displaycmd($0045,$0000)
Displaycmd($0046,$013F)
Displaycmd($0030,$0707)
Displaycmd($0031,$0204)
Displaycmd($0032,$0204)
Displaycmd($0033,$0502)
Displaycmd($0034,$0507)
Displaycmd($0035,$0204)
Displaycmd($0036,$0204)
Displaycmd($0037,$0502)
Displaycmd($003A,$0302)
Displaycmd($003B,$0302)
Displaycmd($0023,$0000)
Displaycmd($0024,$0000)
Displaycmd($0025,$8000)
Displaycmd($004f,$0000)
Displaycmd($004e,$0000)
Lcd_Write_Com($0022)
PRI Displaycmd(c,d) ' instruction in one method
Lcd_Write_Com(c) ' send out a word
Lcd_Write_Data(d)
PRI Lcd_Write_Com(LCDlong)
LCD_RS_low ' can do rs first then cs - better for latch board
LCD_CS_Low ' not needed for the ILI9325 but the SSD1289 needs this
LCD_Writ_Bus(LCDlong)
LCD_CS_High ' not needed for the ILI9325
I'll look through the code in a bit. Things are a bit hectic right now.
Need to take a few days from this cache driver. It's probably something simple, but it could take quite a while to figure out. With all the string functions and chaining I'm not too concerned with getting C running. Others will find this useful but I'm not perusing C right now because it requires me learning a new language(s).
On my to-do list, getting the MCP3202 running to my liking. In my previous attempt, I was creating 2 SPI objects and using one to handle ADC... Worked great until re-draw where a few samples get lost. The key is TDM. Need to run the maths, but for decent sampling rate I'm thinking 100ksps will be sufficient, could even go to 89ksps. I'm probably over-thinking this since the last attempt was on the MCPboard. The 373 *I'm using 374s* should be sufficiently fast to keep up. Need to work on this a bit more.
*edit*
Walked away and figured out part of the issue. I have the cache partially running.. Can't be far now!
Re your bug, I am getting close. I think it is at the end of the 161 code, need to add this line.
After the 161 has loaded, P16-18 could be in any state. But P16 and 17 are used as the /rd and /wr lines on the ram chip. So if you then do a group change, and either of those pins is low (particularly the /wr pin) it could corrupt the ram. I think that might be why it is an intermittent bug because it depends on what was the last address you sent.
I am very close now to a single "S" and "T" command where the entire command is in pasm. Give me half an hour to test all the programs we have to be double sure it works.
[code]
CON Touch
'' ILI9325 driver using the Touchblade161 design for faster ram to display transfers
'' Average Joe and James Moxham 2012
Margin = 4 ' some characters have xoffset of -1 or -2 eg v, and won't display on the extreme left edge
_1ms = 1_000_000 / 1_000 ' Divisor for 1 ms
' touchscreen pins
Touch_Clock = 0
'Touch_ChipSelect = done with a group select
Touch_DataIn = 1
Touch_DataOut = 2' touchscreen pins
' kye sd numbers
_dopin = 24
_clkpin = 25
_dipin = 26
_cspin = 27
_cdpin = -1 ' -1 if unused.
_wppin = -1 ' -1 if unused.
_rtcres1 = -1 ' -1 always.
_rtcres2 = -1 ' -1 always.
_rtcres3 = -1 ' -1 always.
_statuspin = -1 ' Status LED pin number.
VAR
byte FileNumber ' 0 to 31
byte DisplayMode ' type of display 0 = ILI9325, 1= SSD1289, 2 etc
byte DisplayLeftRightBoth ' "R", "L" or "B"
word ScreenWidth ' either 240 in portrait or 320 in landscape
word ScreenHeight ' 320 in portrait or 240 in landscape
long orientation ' true is portrait
long curx, cury
byte FontHeight ' size of the current loaded font (pixels = fontsize *.75)
word BackFontColor ' the background color in RRRRRGGG GGGBBBBB format
long StringAddress ' start of string address
long wcboot ' most recent boot warm or cold
'
OBJ
spi: "SPI_ASM" ' thanks to Beau - spi driver for touch screen
fat: "SD-MMC_FATEngine.spin" ' Kye's latest sd driver and no RTC
DAT
RamLocation long $0[31] ' 32 file ram locatinos
RamSize long $0[31] ' File sizes in words - NOT bytes (divide byte by 2)
sdbuffer byte $0[1024] ' 1024 byte buffer for sd card interface and also for sending one row 320 x3 = 960 bytes max picture size
buffer2 byte $0[512] ' 512 general purpose hub buffer
rambuffer word $0[256] ' 512 byte (256 word) buffer easier for using with words
PUB BeginDesktop(DisplayType) ' store the type of display once by the desktop program
DisplayMode := DisplayType ' global variable I = ILI9325, S=SSD1289
Begin(true) ' start up everything running the desktop
result := wcboot
PUB BeginProgram ' used by all programs except the desktop program
Begin(false) ' any program except the desktop
result := wcboot
PRI Begin(Desktop) |n ' desktop true if run from the desktop program, false for other programs
start_ram_cog ' * start cog first *start the external ram / display driver in a cog, needed for many routines so always first
Latch_AllHighStart ' all Latch pins high and enables pin 22
if desktop == false
DisplayMode := GetDisplayMode ' reads "I" or "S" etc off the ram ** must have run desktop at least once first to store this ***
'DisplayMode := "I" ' temp for debugging, using the ILI9325
SelectMemGroup ' select group2
DisplayLeftRightBoth := "B" ' "R", "L" or "B"
if displaymode == "I"
Start_ILI9325 ' start the display 0 = ILI9325, 1 = SSD1289
if displaymode == "S"
Start_SSD1289
wcboot := WarmColdBoot ' get the last warm or cold boot value
SetOrientation(true) ' in portrait (true) or landscape mode (false), must be before clearscreen otherwise don't know screensize
if wcboot == false and Desktop == true ' no need to redisplay every time if worked the first time
Text(string("SD")) ' string to send, uses internal prop font so can see something if the SD fails to mount
fat.fatEngineStart( _dopin, _clkpin, _dipin, _cspin, _wppin, _cdpin, _rtcres1, _rtcres2, _rtcres3)
fat.mountPartition(0) ' mount the sd card
if desktop == false ' running a program, this is called without knowing what the ramlocation and ramsize values are
ManualValues ' coming from outside so reset ramsize and ramlocation values for 0 and 1
SetCursor(0,0) ' cursor for debugging to 0,0
if desktop == false
SelectSPIGroup ' selects this group and starts the cog
'PUB BasicTesting
' text(string("Basic testing"))
' sdbuffer[0] := 6
' SpinHubToRam(@sdbuffer,5000,10) ' move data to ram
' sdbuffer[0] := 7 ' change the value
' SpinRamToHub(@sdbuffer,5000,10) ' read back the original value
' hex(sdbuffer[0],2)
' 'Drawline(100,100,110,100,$FFFF)
' Clearrectangle(120,120,129,129,$FFFF)
' Draw(100,100,109,109)
' repeat 100
' pixel(%11111000_00000000)
' ************** string routines stored in external memory so can have arrays and large text files **********************
' to make things simpler, one array StringArray. any number of entries, each up to 128 characters
' pass the source and destination numbers
' TextArray(256) = 256 entries for text files etc
' first entry is destination, second entrie(s) are the source
' Commands are
' StringDim ' reserves memory 256*128, increments filenumber, adds filesize
' StringLoadFile(Name,S) ' read a .txt file off the SD card and stores in TextArray, new string entry is crlf
' StringSaveFile(Name,S,F) ' save .txt file from TextArray to SD card adding in crlf, Start to Finish
' StringClear(Dest) ' clear a string, pass string number
' StringAddChar(Dest,N) ' adds a character to a string at end
' StringSubChar(Dest) ' remove a character from the end
' StringCopy(Dest,Src) ' copy string from one location to another
' StringStore(Dest,Src) ' convert Prop string("mystring") to string stored in external memory
' StringLeft(Dest,Src,N) ' Basic Left$
' StringMid(Dest,Src,L,N) ' Basic Mid$
' StringRight ' Basic Right$
' StringInstr ' Basic Instr
' StringUcase(Dest,Src) ' Basic Ucase$
' StringLcase ' Basic Lcase$
' StringLen ' Length
' StringStr ' string representation of a number
' StringHex(Dest,N) ' number to hex string
' StringBin ' number to binary string
' StringVal ' value (hex is 0x, binary is 0b)
' StringJoin(Dest,Src1,Src2) ' same as Basic + with strings
' StringInsert(Dest,Src1,Src2,n) ' inserts src2 into src1 starting at position n, (1 is first char) and moves to dest
' StringDebug(n) ' print using the green propeller font string number n
' StringLen() ' same as strsize but works on external memory strings
' StringRambuffer ' moves string to rambuffer array and returns result = location of this array
' StringCompare(Source1,Source2) ' returns true if strings equal in length and characters
' use these routines amongst others.
'integerToDecimal(number, length) '' 5 Stack Longs
'integerToHexadecimal(number, length) '' 5 Stack Longs
'integerToBinary(number, length) '' 5 Stack Longs
'decimalToInteger(characters) | sign '' 10 Stack Longs
'hexadecimalToInteger(characters) | sign '' 10 Stack Longs
'binaryToInteger(characters) | sign '' 10 Stack Longs
PUB StringTest ' test all the string routines
'StringDim(10)
'StringStore(5,string("Hello World"))
'StringDebug(5)
'StringClear(5)
'StringAddChar(5,"1")
'StringAddChar(5,"2")
'StringAddChar(5,"3")
'StringAddChar(5,"4")
'StringDebug(5)
'StringSubChar(5) ' remove one character from the right, useful for backspace etc
'StringDebug(5)
'StringCopy(7,5) ' copy
'StringDebug(7)
'StringLeft(7,7,5) ' source and destination the same or different
'StringDebug(7)
'StringMid(8,5,7,3) ' dest, source, starting byte, number of bytes. first byte is 1
'StringDebug(8)
'StringRight(3,5,4) ' dest, source, number of bytes
'StringDebug(3)
'Hex(StringInstr(5,2,"o"),2) ' find this character starting at 2
'StringBin(2,255) ' convert a number to binary always 32 + 2 characters
'StringDebug(2)
'StringHex(4,100) ' always 8 + 2 characters
'StringDebug(4)
'StringDec(9,-20) ' always 11 characters
'StringDebug(9)
'StringStr(9,-7) ' Basic str$ removes leading zeroes, and first character is either - or a space
'StringDebug(9)
'StringStore(5,string("65")) ' test string number to number
'hex(StringVal(5),8)
'StringStore(5,string("0x1234")) ' test string hex to number
'hex(StringVal(5),8)
'StringStore(5,string("0b101")) ' test string binary to number
'hex(StringVal(5),8)
'StringStore(1,string("Hello "))
'StringStore(2,string("World"))
'StringJoin(1,1,2) ' string1 = string1 +string2 (shows dest and a source can be the same - or could be different)
'StringDebug(1)
'StringUcase(1,1) ' Ucase test
'StringDebug(1)
'StringLcase(1,1) ' Lcase test
'StringDebug(1)
'StringStore(1,string(" 123")) ' test the trim function
'StringTrim(1,1)
'StringDebug(1)
'StringStore(1,string("TEST6.TXT")) ' file name
'StringStore(2,string("Line a of text")) ' store some lines of text
'StringStore(3,string("Line b of text"))
'StringStore(4,string("Line c of text"))
'StringSavefile(1,2,4) ' filename is 1, store lines 2 to 4
'StringStore(1,string("TEST6.TXT")) ' file name
'StringLoadFile(1,2) ' read back the file created above.
'Stringdebug(2)
'Stringdebug(3)
'Stringdebug(4)
'repeat
PUB StringDim(n) ' reserve space for a string array, n= number of strings, 128 max bytes per string
Ramsize[Filenumber] := n << 6 ' * 64 as this is in words, not bytes
StringAddress := NextLocation ' get the location of the string array
Filenumber += 1
PUB StringStore(StringN,Source) ' store a propeller string("test") to array number Dest
bytemove(@rambuffer, Source, (strsize(Source) + 1)) ' move to rambuffer array
StringToRam(StringN)
PUB StringDebug(StringN) ' print using the green propeller font - useful if sd card not working so no fonts loaded
RamToString(StringN)
Text(@rambuffer) ' print it out
PUB StringClear(StringN)
bytefill(@rambuffer,0,128) ' only really need to do the first one but may help debugging to completely clear the string
StringToRam(StringN)
PUB StringAddChar(StringN,c) | s ' add c to the string
if c >31 and c <127 ' a character that can be displayed
RamToString(StringN) ' get the string
s := strsize(@rambuffer)
byte[@rambuffer] := c
byte[@rambuffer][s + 1] := 0 ' add in the new zero (can't assume array is cleared)
StringToRam(StringN) ' store back to ram
PUB StringSubChar(StringN) ' remove one character from the right same as strings.left(strings.len - 1)
RamToString(StringN)
if strsize(@rambuffer) <> 0 ' if at least one character
byte[@rambuffer][strsize(@rambuffer)-1] :=0
StringToRam(StringN)
PUB StringCopy(Dest,Source) ' copy string from source to dest
RamToString(Source)
StringToRam(Dest)
PUB StringLeft(Dest,Source,n) ' Basic Left$
RamToString(Source)
byte[@rambuffer][n] := 0 ' move the terminator
StringToRam(Dest) ' move back to ram
PUB StringMid(Dest,Source,start,n) | i ' Basic Mid$
RamToString(Source)
repeat i from 0 to n-1
byte[@rambuffer] := byte[@rambuffer][i+start-1] ' possibly could use bytemove here
byte[@rambuffer][n] := 0 'add in the new zero terminator
StringToRam(Dest)
PUB StringRight(Dest,Source,n) | i,m
RamToString(Source)
m := strsize(@rambuffer) - n
repeat i from 0 to n ' Basic Right$. include the terminator
byte[@rambuffer] := byte[@rambuffer][i+m]
StringToRam(Dest)
PUB StringInstr(Source,Start,c) | i ' find c in the string starting at start
RamToString(Source)
result := 0 ' return 0 if not found
repeat i from (start-1) to strsize(@rambuffer)
if byte[@rambuffer] == c
result := i+1 ' 1 is first character
quit
PUB StringBin(Dest,n) ' convert n to a binary value at Dest. Prefix 0b
repeat result from 33 to 2
byte[@rambuffer][result] := ((n & 1) + "0")
n >>= 1
byte[@rambuffer][34] := 0 ' add zero terminator
byte[@rambuffer][0] := "0" ' prefix 0b same as some versions of C
byte[@rambuffer][1] := "b"
StringToRam(Dest) ' store
PUB StringHex(Dest,n) ' number to hex, always 8 characters
repeat result from 9 to 2
byte[@rambuffer][result] := lookupz((n & $F): "0".."9", "A".."F")
n >>= 4
byte[@rambuffer][10] := 0 ' zero at end
byte[@rambuffer][0] := "0" ' prefix 0x same as C
byte[@rambuffer][1] := "x"
StringToRam(Dest)
PUB StringDec(Dest,n) | sign ' number to decimal string, always 11 characters
sign := "+"
if(n < 0)
sign := "-"
if(n == negx)
bytemove(@rambuffer, string("-2147483648"), 11) ' max negative number special case
else
repeat result from 10 to 1
byte[@rambuffer][result] := ((||(n // 10)) + "0")
n /= 10
byte[@rambuffer][0] := sign ' sign at the front
byte[@rambuffer][11] := 0 ' zero terminator
StringToRam(Dest)
PUB StringStr(Dest,n) | found ' same as Basic STR - similar to Dec above but sign is either space or negative, and leading zeros removed
StringDec(Dest,n) ' number preserved in rambuffer
found := 10 ' if answer was zero don't remove leading zero
repeat result from 1 to 10
if byte[@rambuffer][result] <> "0"
found := result
quit
repeat result from 1 to 11
byte[@rambuffer][result] := byte[@rambuffer][result +found -1] ' shuffle over if needed
if byte[@rambuffer][0] == "+"
byte[@rambuffer][0] := " " ' Basic syntax, - or blank
StringToRam(Dest) ' resend to external ram
PUB StringVal(Source) | sign,i,k ' returns value of the string, hex 0x, binary 0b, decimal + or - or space or no leading character just a number
RamToString(Source)
if byte[@rambuffer][1] == "x" ' this is a hex number
repeat k from 2 to strsize(@rambuffer) -1 ' leading two characters always 0x
result := ((result <- 4) + (byte[@rambuffer][k] & $F))
return
if byte[@rambuffer][1] == "b" ' this is a binary number
repeat k from 2 to strsize(@rambuffer) -1 ' leading two characters always 0b
result := ((result << 1) + (byte[@rambuffer][k] & 1))
return
' else if got here this must be a decimal number. It could start with a number(no leading sign), or a space, or a + or a -
sign := byte[@rambuffer][0]
i := 0 ' start location
if sign == "+" or sign == "-" or sign == " " ' this is a decimal number ** must start with a space or a + or a -**
i := 1 ' leading character so start here
repeat k from i to (strsize(@rambuffer)-1) ' if ever use right to left, don't use step -1, spin puts it in if start is less than finish and if there is a step -1 it is one less loop
result := ((result *10) + byte[@rambuffer][k] & $F) 'very clever, the & $F works because zero is hex0x30, the pre multiply shifts previous numbers to the left so can do left to right. Easier to understand with Kye's binary converter with the left shift
if sign == "-"
result *= -1 ' if negative then negate
PUB StringJoin(Dest,Source1,Source2) ' Dest$= Source1$+Source2$
RamToString(Source2)
bytemove(@buffer2,@rambuffer,128) ' store string2 to buffer2
RamToString(Source1) ' get first string
bytemove(@rambuffer+strsize(@rambuffer),@buffer2,strsize(@buffer2)+1) ' thanks to Kye for this one
StringToRam(Dest) ' store back to ram
PUB StringUcase(Dest,Source)
RamToString(Source)
repeat result from 0 to strsize(@rambuffer)
if ((byte[@rambuffer][result] => "a") and (byte[@rambuffer][result] =< "z"))
byte[@rambuffer][result] -= 32
StringToRam(Dest)
PUB StringLcase(Dest,Source)
RamToString(Source)
repeat result from 0 to strsize(@rambuffer)
if ((byte[@rambuffer][result] => "A") and (byte[@rambuffer][result] =< "Z"))
byte[@rambuffer][result] += 32
StringToRam(Dest)
PUB StringTrim(Dest,Source)
RamToString(Source)
if byte[@rambuffer][0] == "+" or byte[@rambuffer][0] == " " or byte[@rambuffer][0] == "-"
repeat result from 0 to strsize(@rambuffer)
byte[@rambuffer][result] := byte[@rambuffer][result+1] ' delete the first +,- or space
StringToRam(Dest)
PUB StringSaveFile(Filen,Start,Finish) | i,j ' filen might be string array 0, start could be 1, finish could be 4
RamToString(Filen) ' fetch the filename
fat.newfile(@rambuffer) ' create the file, if not working use \fat. for debugging, see PUB openfileread for an example
result := \fat.openfile(@rambuffer,"W") ' open for writing return error if there is one
repeat i from Start to Finish
RamToString(i)
j := strsize(@rambuffer)
byte[@rambuffer][j] := 13 ' carriage return
byte[@rambuffer][j+1] :=10 ' line feed
byte[@rambuffer][j+2] := 0 ' end of new line
fat.writedata(@rambuffer,j+2) ' write this line
FileClose ' close the file
PUB StringLoadFile(Filen,Stringnumber)| s,r,c,t,z ' read filename, new line every crlf. Must have enough space reserved in the string array
RamToString(Filen) ' copy a file from the SD card to the ram disk
OpenFileRead(@rambuffer)
s := 0 ' sd counter
r := 0 ' rambuffer counter
t := 0 ' total number of bytes
z := fat.filesize ' size of the file
repeat (z >> 9) +1 ' include any remainder as +1, do blocks of 512 bytes each as natural size of SD blocks
fat.readdata(@sdbuffer,512) ' read from SD card
repeat s from 0 to 511
c := byte[@sdbuffer] ' get the next byte
if t =< z ' might read in one more 512 byte block to ensure gets any remainder
if c == 13 or r > 126 ' end of a line so new string, or string too long
byte[@rambuffer][r] := 0 ' end of string terminator
StringToRam(Stringnumber)
stringnumber +=1 ' new string number
r := 0 ' start at beginning of rambuffer
if c >31 and c < 127
byte[@rambuffer][r] := c ' store if a valid character
r +=1
t +=1 ' number of bytes read
FileClose ' close the file
PUB StringLen(Source) ' string length
RamToString(Source)
result := strsize(@rambuffer)
PUB StringPrint(Font,Source) | i,s ' print this string using using font in memory and curx and cury
RamToString(Source)
s := strsize(@rambuffer)
repeat i from 0 to (s-1)
curx := TextChar(Font,byte[@rambuffer],curx,cury)
PUB StringRamBuffer(Source) ' string to rambuffer array
RamToString(Source)
result := @rambuffer ' return pointer to the rambuffer array
PUB StringCompare(Source1,Source2) | i
RamToString(Source2)
bytemove(@buffer2,@rambuffer,128) ' store string2 to buffer2
RamToString(Source1)
result := true
repeat i from 0 to strsize(@rambuffer)
if byte[@buffer2] <> byte[@rambuffer]
result := false
return
PRI StringToRam(StringN)
HubToRam(StringN << 6 + StringAddress,64) ' 64 words =128 bytes, so work in groups of 64 words
PRI RamToString(StringN)
RamToHub(StringN << 6 + StringAddress,64) ' moves to rambuffer array
' ***************** end string routines *************************
PUB DisplayLeft ' send all data to the left display
DisplayLeftRightBoth := "L" ' don't enable here, do this just before sending data
PUB DisplayRight
DisplayLeftRightBoth := "R"
PUB RamErase(n)
Filenumber := n ' erase lookup table for the ram files, effectively erases the files
' if 0 then erase all, if 1 then leave the desktop intact (assuming this was loaded first)
PUB ClearRam
Filenumber := 2 ' clear ram but leave file 0 = warm or cold boot and 1 = background picture
PUB GetCurX
result := curx ' get the text cursor position
PUB GetCurY
result := cury
PUB SetCurx(n) ' set the text cursor position
Curx := n
PUB SetCury(n)
Cury := n
PUB GetBackFontColor
result := BackFontColor
PUB GetRamLocation(n)
result := Ramlocation[n]
PUB GetFilenumber
result := filenumber
PUB SetFilenumber(n) ' reset the file number
Filenumber := n
PUB Chain(stringptr)
result := \fat.bootpartition(stringptr) ' reboot and run this program
PUB SDtoRam(stringptr) | RamAddress,remainder,sizebytes,sizewords ' copy a file from the SD card to the ram disk
OpenFileRead(stringptr)
sizebytes := fat.filesize ' size in bytes
sizewords := sizebytes >>1 ' size in words
ramaddress := NextLocation ' next free place in ram
RamSize[FileNumber] := sizewords ' ram disk works in words, not bytes
repeat sizebytes >> 9 ' do blocks of 512 bytes each as natural size of SD blocks
fat.readdata(@sdbuffer,512) ' read from SD card
SpinHubToRam(@sdbuffer,RamAddress,512) ' move to ram
RamAddress += 256 ' increment ram address by 256 (half 512 as using words)
remainder := sizebytes & %00000000_00000000_00000001_11111111 ' remainder (if not a multiple of 512)
if remainder <> 0
fat.readdata(@sdbuffer,remainder)
SpinHubToRam(@sdbuffer,RamAddress,remainder) ' send out the remainder bytes
FileClose ' close the file
result := FileNumber ' return this file
FileNumber +=1 ' point to next location
PUB FetchRamBuffer
result := @rambuffer
PUB SDBMPtoRam(stringptr) | width,height,w,i,ramaddress ' .bmp from sd to ram and convert to 2 byte ili format and reverse order along the way
' http://en.wikipedia.org/wiki/BMP_file_format scroll down about 1/3 of the way for the header formats
' get the width, height
' store these as 2 longs at the beginning of the ram file
' the bitmap format starts at the last row and works up so need to reverse the order
ramaddress := NextLocation ' get the next ram location
OpenFileRead(stringptr)
fat.readdata(@sdbuffer,$36) ' get the header 0x36 hex bytes
width := sdbuffer[$12] + sdbuffer[$13] << 8 ' only read in two bytes as never will be >64k wide or high
height := sdbuffer[$16] + sdbuffer[$17] << 8
w := width * 3 ' this number of bytes per row, and round up to nearest 4 bytes et 327 goes to 328 The size of each row is rounded up to a multiple of 4 bytes (a 32-bit DWORD) by padding.
w +=3 ' add 3 and
w &= %11111111_11111111_11111111_11111100 ' round down
SpinHubToRam(@sdbuffer,ramaddress,$36) ' store header to ram
i := $36 + ramaddress + ((height - 1) * width) ' start + header and on the last row and work back
repeat height
fat.readdata(@sdbuffer,w) ' read in the first row
CogCmd("F",@sdbuffer,@rambuffer,width)
HubToRam(i,width) ' send to ram
i -= width ' subtract the width, move up the picture
FileClose
Ramsize[Filenumber] := $36 + (width * height) ' size of bitmap file in words
result := FileNumber ' returns the current file number
FileNumber += 1 ' add 1 to filenumber
PUB DrawBMPRam(f,x,y) | rampointer,width,height ' pass the filenumber and the x and y location, searches for this file and if found, displays it
rampointer := RamLocation[f] ' find the start of the file
SpinRamToHub(@sdbuffer,rampointer,$36) ' read in the header
width := sdbuffer[$12] + sdbuffer[$13] << 8 ' only read in two bytes as never will be >64k wide or high
height := sdbuffer[$16] + sdbuffer[$17] << 8
Draw(x,y,x+width-1,y+height-1) ' set up the area to draw
RamToDisplay(RamPointer + $36,width*height) ' skip the header and display
result := (width <<16) | (height & $0000FFFF) ' return the width and height combined into a long
' ***************** end ram disk routines
PUB MergeBackgroundIconRam(stringptr,x,y,desktop,mask,icon) | i,j,k ' merge icon and background - if mask =0 then background, else icon (not classic alpha compositing but close enough)
' i is background counter, j is icon counter, k = mask counter
' assumes desktop is in ramfile 1, mask in ramfile 2, icon in ramfile 3
FileNumber := icon
SDBMPtoRam(stringptr) ' load icon into ram
Filenumber := icon ' reset filenumber
i := RamLocation[desktop] + $36 + (y*(screenwidth+1)+x) ' location of picture pixel in ram
j := RamLocation[icon] + $36 ' location of icon in pixel in ram
k := RamLocation[mask] + $36 ' location of mask pixel in ram
repeat 60
SpinRamToHub(@sdbuffer,i,59) ' get 59 pixels from background
SpinRamToHub(@rambuffer,j,59) ' get 59 icon pixels
SpinRamToHub(@buffer2,k,59) ' get words for the mask
CogCmd("X",@sdbuffer,@rambuffer,@buffer2) ' pasm version of the above 3 lines
SpinHubToRam(@sdbuffer,i,59) ' send back modified line to the picture buffer
i += screenwidth+1 ' next line in picture
j +=59 ' icon next line
k +=59 ' next line in mask (bytes) - buffer2
' old "X" command code
'repeat n from 0 to 58
' if (word[@buffer2][n] & %00000000_00011111) > %10000 ' RGB are the same, so use BBBBB, mask 5 low bits, and if > %10000
' word[@sdbuffer][n] :=word[@rambuffer][n] ' if >128 then replace with icon pixel
PUB OpenFileRead(stringptr) ' open a file for reading and display a message if it fails
result := \fat.openfile(stringptr,"R")
if fat.partitionerror <> 0 ' failed to find the file
Text(stringptr) ' print the filename
Text(result) ' print the error message
repeat ' stop the program
PUB FileClose
fat.closefile ' close the file
PUB TouchXPercent
result := TouchValue & 255 ' decode xval 0-100%
if DisplayMode == "S"
result := 100-result ' SSD1289 x runs the other way
PUB TouchYPercent
result := TouchValue >> 8 ' decode yval 0-100%
PUB TouchSwishX | xval,oldxval,difference,average ' returns result = +ve or -ve depending on swish direction ' did finger move left or right?
SelectSPIGroup ' turn on the spi touchscreen
oldxval := 255' for startup
average := 0
repeat until (average > 30) or (average < -30)
' possibly add a decay function here for average
pause1ms(50)
xval := TouchXPercent ' decode xval 0-100%
if xval == 255 ' not touched
oldxval := 255 ' reset oldxval as well
else
if oldxval <>255 ' discard the first reading as the screen is touched
difference := xval - oldxval
'printdecimal(xval)
'printdecimal(difference)
average := average + difference '
'printdecimal(average)
'crlf
oldxval := xval ' store for next time
result := average ' return negative or positive value
PUB TouchSwishY | yval,oldyval,difference,average ' returns result = +ve or -ve depending on swish direction ' did finger move left or right?
SelectSPIGroup ' turn on the spi touchscreen
oldyval := 255' for startup
average := 0
repeat until (average > 30) or (average < -30)
' possibly add a decay function here for average
pause1ms(50)
yval := TouchYPercent ' decode xval 0-100%
if yval == 255 ' not touched
oldyval := 255 ' reset oldyval as well
else
if oldyval <>255 ' discard the first reading as the screen is touched
difference := yval - oldyval
average := average + difference '
oldyval := yval ' store for next time
result := average ' return negative or positive value
PUB TouchStart
SPI.start(3,0) ' delay,state, start clock high
PUB TextChar(FileN,ascii,x,y) | jump,size,width,height,ramaddress,xoffset,yoffset,xadvance ' read out a character from a .ifn file stored in ram
' pass Fonttable = global
' moves to next line if off the end
ramaddress := RamLocation[FileN]
jump := ReadRamLong(ramaddress,(ascii << 2) + 256) ' jump location = ascii *4 plus fonttable + 256
SpinRamToHub(@buffer2,ramaddress+jump>>1,20) ' read in the width height as a block = quicker than readramlong
size := long[@buffer2][0] ' size in pixels of this character
xadvance := long[@buffer2][5] ' amount to move to next character
'if size > 0 and ascii <> 32 ' no need to print anything if size is zero or read more font data
width := long[@buffer2][1] ' width
height := long[@buffer2][2] ' height
xoffset := long[@buffer2][3] ' xoffset to move
yoffset := long[@buffer2][4] ' yoffset to move
if (x+width -1 + xoffset) > screenwidth
crlf ' do a new line if it won't fit
x := curx
y := cury
if ascii < 32
xadvance := 0 ' add nothing if a non displaying character
if ascii == 13 ' carriage return
cr
x := curx
if ascii ==10 ' line feed (new line)
lf
y := cury
if ascii > 32 ' space not displayed
Draw(x+xoffset,y+yoffset,x+width-1+xoffset,y+height-1+yoffset) ' draw on screen
RamToDisplay(ramaddress+((jump+32)>>1),size) ' move bytes from ram out to the display
return x + xadvance
PUB crlf
cr
lf
PUB cr ' carriage return, to left margin
curx := Margin ' some characters are -1 xoffset so won't display
PUB lf ' line feed, move up one
cury += FontHeight
if cury > screenheight
ClearRectangle(0,0,screenwidth,screenheight,BackFontColor)
cury := 0 ' if off the bottom of the screen then clear screen start at the top (VT00 does scrolling)
' don't clear background here, do in calling routine
'ClearFontBackground(0,cury,screenwidth,cury + FontHeight) ' clear background line to the background color ready to draw
PUB TextT(FileN,stringptr) 'print at curx,cury using file number in ramdisk
'' Print a zero-terminated string
repeat strsize(stringptr)
curx := TextChar(FileN,byte[stringptr++],curx,cury)
PUB SetCursor(x,y)
curx := x
cury := y
PUB ReadRamLong(WordStart,Bytevalue) ' easier calculating
SpinRamToHub(@result,Wordstart+Bytevalue>>1,2)
return result
PUB LoadFont(stringptr) | filesize,i ' read a .ifn file premade in angelcode bmfont and then the vb.net program
SDtoRam(stringptr) ' font to ram disk, adds one to filenumber
SpinRamToHub(@rambuffer,RamLocation[Filenumber-1],128) ' read first 128 words =256 bytes back
FontHeight := byte[@rambuffer][251] ' font height
repeat i from 0 to 2
sdbuffer := byte[@rambuffer][35+i]
CogCmd("E",@sdbuffer,@buffer2,1) ' convert to 2 byte .ili format
BackFontColor := ( byte[@buffer2][1] << 8) | byte[@buffer2][0] ' correct order
result := FileNumber-1 ' return pointer to the font file in ram
PUB HubToRam(RamAddress,Number) ' send pixels from rambuffer hub to ram
SpinHubToRam(@rambuffer,RamAddress,Number) ' spin version
PUB RamToHub(RamAddress,Number) ' send pixels from ram to hub at rambuffer
SpinRamToHub(@rambuffer,Ramaddress, number)
PUB RamToDisplay(RamAddress,Number) ' send pixels from ram to display
SpinRamToDisplay(Ramaddress, Number)
PUB Clearscreen(color) ' uses 2 byte ILI color (see below for rgb conversion)
wordfill(@rambuffer,color,256)
Draw(0,0,screenwidth,screenheight) ' set up the area of the screen to draw in
repeat 300
SpinHubToDisplay(@rambuffer,256)
PUB RGBtoWord(R,G,B) ' convert RGB to 2 byte ILI color
sdbuffer[0] := R
sdbuffer[1] := G
sdbuffer[2] := B
CogCmd("E",@sdbuffer,@buffer2,1)
result := ( byte[@buffer2][1] << 8) | byte[@buffer2][0] ' correct order
PUB ClearRectangle(x1,y1,x2,y2,ColorWord) | i,size,remainder ' clears an area of the screen to the current font background color
size := (x2-x1+1) * (y2 - y1 +1) ' number of pixels
remainder := size & 255 ' if not a whole number of 256 pixels
repeat i from 0 to 255 ' move the background font colour to the buffer
rambuffer := ColorWord
Draw(x1,y1,x2,y2) ' set up the area of the screen to draw in
repeat size >> 8 ' 256 pixel blocks
SpinHubToDisplay(@rambuffer,256)
if remainder <> 0
SpinHubToDisplay(@rambuffer,remainder) ' do the remainder
PUB Drawline(x1,y1,x2,y2,ColorWord) ' draws a vertical or horizontal line
ClearRectangle(x1,y1,x2,y2,ColorWord) ' same as drawing a rectangle
PUB SetOrientation(e) ' change orientation true = portrait. Changes global variable 'orientation' and also screen width and height
orientation := e
ChangeOrientation(orientation)
if orientation
screenwidth :=239
screenheight :=319
else
screenwidth :=319
screenheight :=239
PUB ChangeOrientation(n) ' pass true = portrait or false = landscape, changes global variable orientation in this object
' command $0001
'8.2.4. Driver Output Control (R01h)
'R/W RS D15 D14 D13 D12 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0
'W 1 0 0 0 0 0 SM 0 SS 0 0 0 0 0 0 0 0
'SS: Select the shift direction of outputs from the source driver.
'When SS = 0, the shift direction of outputs is from S1 to S720
'When SS = 1, the shift direction of outputs is from S720 to S1.
'In addition to the shift direction, the settings for both SS and BGR bits are required to change the
'assignment of R, G, B dots to the source driver pins.
'To assign R, G, B dots to the source driver pins from S1 to S720, set SS = 0.
'To assign R, G, B dots to the source driver pins from S720 to S1, set SS = 1.
' command $0003
'R/W RS D15 D14 D13 D12 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0
'W 1 TRI DFM 0 BGR 0 0 HWM 0 ORG 0 I/D1 I/D0 AM 0 0 0
' When AM = 0, the address is updated in horizontal writing direction.
'When AM = 1, the address is updated in vertical writing direction.
'I/D[1:0] Control the address counter (AC) to automatically increase or decrease by 1 when update one pixel
' display data.
SelectMemGroup
DisplayEnable ' enable the display(s)
orientation := n
if displaymode == "I" ' ili9325
if orientation
' for origin top left and portrait mode
Displaycmd($0001,%00000001_00000000) ' $0001,$0100 set SS and SM bit 0001 0100 portrait
Displaycmd($0003,%00010000_00110000) ' $0003,$1030 set GRAM write direction and BGR=1. $0003 $1030
else
' for origin top right and landscape mode
Displaycmd($0001,%00000000_00000000) ' set SS and SM bit 0001 0000 landscape
Displaycmd($0003,%00010000_00111000) ' landscape $1028 = original but 1038 is correct - not mirror image
if displaymode == "S" ' SSD1289
if orientation
' for origin top left and portrait mode
Displaycmd($0001,$2B3F) ' Entry mode portrait
Displaycmd($0011,$6070) ' '
else
' for origin top right and landscape mode
Displaycmd($0001,$6B3F) ' landscape $2B3F = original but 6B3F is correct
Displaycmd($0011,$6838) ' landscape $1028 = original but 6838 is correct
PUB SelectMemGroup ' select group 1 for memory transfer
spi.stop ' and stop any other cogs here too if needed
LatchGroup2
DIRA |= %00000000_01011111_11111111_11111111 ' enable these pins for output
OUTA |= %00000000_00011111_00000000_00000000 ' set /rd /wr /disp wr and 161 clock high
PUB SelectSPIGroup
LatchGroup3 ' select group 2 for spi transfers
DIRA &= %11111111_11111111_11111111_11000000 ' so no clashes with the cog using P0-P2 set these as inputs and P3-P5
TouchStart ' start the touchscreen cog
' may need to set some pins here as inputs or outputs with a dira
PUB Text(stringptr) 'print at curx,cury
repeat strsize(stringptr)
Propfont_out(byte[stringptr++])
PropFontcrlf
PUB PropFontcrlf
curx := 0
cury += 32 ' new line at end of string
if cury >319 ' bottom of screen so new screen
curx:=0
cury:=0
PUB Propfont_out(ascii) | address,pixels
Draw(curx,cury,curx+15,cury+31) ' location to start drawing
address := $8000 + (ascii >> 1) << 7 ' get rom address
repeat 32 ' 32 rows per character, split in two parts
pixels := long[address] ' get rom font data
pixels := pixels >> (ascii & 1) ' shift for odd characters
repeat 16 ' 16 columns
if pixels & 1
Pixel(%00000111_11100000) ' foreground color RRRRRGGG_GGGBBBBB
else
Pixel(%00000000_00000000) ' background color
pixels := pixels >> 2 ' alternate pixels interleaved so shift 2
address += 4
curx +=16
if curx >239 ' new line
Propfontcrlf
PUB Start_ILI9325
DisplayEnable ' enable one or both displays
LatchGroup2 ' talk to this display
LCD_Reset_High
pause1ms(5)
LCD_Reset_Low
pause1ms(5)
LCD_Reset_High
LCD_CS_High
LCD_RD_High
LCD_WR_High
pause1ms(20)
LCD_CS_Low
ILI9325initiate
PUB Start_SSD1289 ' based on C driver
DisplayEnable ' enable one or both displays
LatchGroup2 ' talk to this display
LCD_Reset_High
pause1ms(5)
LCD_Reset_Low
pause1ms(10)
LCD_Reset_High
LCD_CS_High
LCD_RD_High
LCD_WR_High
pause1ms(20)
SSD1289initiate
PUB Draw(x1, y1, x2, y2) ' sets the pixel to x1,y1 and then fills the next (x2-x1)*(y2-y1) pixels
DisplayEnable ' enable one or both displays
SelectMemGroup ' select memory group
ifnot orientation ' landscape mode so swap x and y
result :=x1 ' swap x1 and y1
x1 := y1
y1 := result
result := x2 ' swap x2 and y2
x2 :=y2
y2 := result
if displaymode == "I" ' ILI9325 display
Displaycmd($0050,x1)
Displaycmd($0052,y1)
Displaycmd($0051,x2)
Displaycmd($0053,y2)
Displaycmd($0020,x1)
Displaycmd($0021,y1)
Lcd_Write_Com($0022)
if displaymode == "S" ' SSD1289 display
Displaycmd($0044,(x2<<8)+x1)
Displaycmd($0045,y1)
Displaycmd($0046,y2)
Displaycmd($004e,x1)
Displaycmd($004f,y1)
Lcd_Write_Com($0022)
LCD_RS_High ' after sending out commands, set RS high via latch
PUB Pixel(pixelcolor) ' send out a pixel
Lcd_Write_Fast_Data(pixelcolor) ' need to set RS high at beginning of this group (ie call Draw first)
' it is more efficent to send one Draw command then lots of pixels than sending individual pixels
' but of course, you can send draw x,y,x,y where x and y are the same and then send one pixel
PUB pause1ms(period) | clkcycles
'' Pause execution for period (in units of 1 ms).
clkcycles := ((clkfreq / _1ms * period) - 4296) #> 381 ' Calculate 1 ms time unit
waitcnt(clkcycles + cnt) ' Wait for designated time
PUB hex(value, digits) ' uses internal prop font and slow pixel drawing (for very basic debugging without an SD font)
SelectMemGroup ' reselect mem group for display output for debugging
'' Print a hexadecimal number
propfont_out("O")
propfont_out("x")
value <<= (8 - digits) << 2
repeat digits
propfont_out(lookupz((value <-= 4) & $F : "0".."9", "A".."F"))
PropFontcrlf
PUB SetWarmBoot
long[@rambuffer][5] := $5761726D ' spell Warm in hex, start at 5 as zero gets corrupted
byte[@rambuffer][10] := DisplayMode ' store the current display mode
HubToRam(0,20) ' send words to external ram (ramdiskstart is zero)
ClearScreen(0) ' so user sees something happen as there is a bit of a delay rebooting
reboot
PUB WarmColdBoot ' store warm or cold boot - reserve first 20 bytes in sram for interfacing with programs
' Ramdisk file 0 is the boot string Warm or Cold
' ramdisk file 1 is the background
Ramtohub(0,20) ' read words from the ram
if long[@rambuffer][5] == $5761726D
result := true
else
result := false
long[@rambuffer][5] := $FFFFFFFF ' erase previous warm/cold boot
byte[@rambuffer][10] := DisplayMode ' store display mode
HubToRam(0,20) ' send words to external ram (ramdiskstart is zero)
RamSize[0] := 20 ' reserve 20 bytes
RamLocation[0] := 0
Filenumber := 1
PRI GetDisplayMode
Ramtohub(0,20) ' read words from the ram
result := byte[@rambuffer][10] ' get the display mode
PUB ManualValues
RamLocation[0] := 0
RamSize[0] := $14 ' put in values manually - 20 bytes for warm boot
RamLocation[1] := $14
RamSize[1] := $12C36 ' 76800 for the desktop picture
FileNumber := 2 ' running a program not a desktop
PRI LatchGroup1
CogCmd("O",%00001111,0,0) ' set all group pins high
CogCmd("A",%11111110,0,0) 'set this one low
PRI LatchGroup2
CogCmd("O",%00001111,0,0) ' set all group pins high
CogCmd("A",%11111101,0,0) 'set this one low
PRI LatchGroup3
CogCmd("O",%00001111,0,0) ' set all group pins high
CogCmd("A",%11111011,0,0) 'set this one low
PRI LatchGroup4
CogCmd("O",%00001111,0,0) ' set all group pins high
CogCmd("A",%11110111,0,0) 'set this one low
PRI Latch_ResetLow
CogCmd("A",%11101111,0,0) 'set this one low
PRI Latch_ResetHigh
CogCmd("O",%00010000,0,0) ' set this pin high
PRI Latch_DisplayL ' display on the left
CogCmd("O",%01100000,0,0) ' both displays high
CogCmd("A",%11011111,0,0) 'set this one low
PRI Latch_DisplayR ' display on the right
CogCmd("O",%01100000,0,0) ' both displays high
CogCmd("A",%10111111,0,0) 'set this one low
PRI Latch_DisplayB ' both displays for debugging
CogCmd("A",%10011111,0,0) 'set both displays low
PRI Latch_RS_Low
CogCmd("A",%01111111,0,0) 'set this one low
PRI Latch_RS_High
CogCmd("O",%10000000,0,0) ' set this pin high
PRI Latch_AllHighStart ' all pins high
CogCmd("O",%11111111,0,0) ' set all pins high (disabled)
PRI Load161(address)
CogCmd("Z",0,address,0) ' call pasm version, faster, changes the latch but the next PUB always resets it anyway
PRI DisplayEnable ' pass DisplayRightLeftBoth
case DisplayLeftRightBoth
"R":Latch_DisplayR
"L":Latch_DisplayL
"B":Latch_DisplayB
PRI SpinHubToRam(hubaddress, ramaddress, number) ' move data from hub to ram
CogCmd("S",hubaddress,ramaddress,number) ' pasm block move
PRI SpinRamToHub(hubaddress,ramaddress,number) ' move data from ram to hub
CogCmd("T",hubaddress,ramaddress,number) ' pasm block move
PRI SpinRamToDisplay(ramaddress,number) ' ramaddress, number of words (pixels), display = 1 or 2
Load161(ramaddress) ' load the 161 counters
LatchGroup2 ' group 2 is block data transfers
DisplayEnable ' latch output one or both displays
CogCmd("U",0,0,number) ' pasm alternative to code below, much faster
PRI SpinHubToDisplay(hubaddress,number)|i
'LatchGroup2 ' not needed as always do a Draw first, also no need to do displayenable as this is in a draw as well
CogCmd("V",hubaddress,0,number) ' pasm version of code below
' ***********************************************************
PRI NextLocation ' get the next free location in ram based on last files size
if FileNumber == 0
RamLocation[0] := 0 ' work out start of this file based on last file size
else
RamLocation[FileNumber] := RamLocation[FileNumber - 1] + RamSize[FileNumber - 1] ' get location in ram
result := RamLocation[FileNumber]
PRI TouchX
SPI.SHIFTOUT(Touch_DataIn, Touch_Clock, 5, 8 , %1101_0000) ' reads x from 500 to 3700 (off < 500 )
result := SPI.SHIFTIN(Touch_DataOut,Touch_Clock,2, 12)
PRI TouchY
SPI.SHIFTOUT(Touch_DataIn, Touch_Clock, 5, 8 , %1001_0000) ' reads y from 400 to 3800 (off > 3800 )
result := SPI.SHIFTIN(Touch_DataOut,Touch_Clock,2, 12)
PRI TouchValue | xval,yval,x,y ' returns % values 0-100% (or 255 for no touch)
xval := TouchX
yval := TouchY
if (xval => 500) and (yval =< 3800) ' both have to be valid for a touch
pause1ms(2) ' tiny delay then read again as the first reads may not have the correct value
xval := TouchX
yval := TouchY
if (xval => 500) and (yval =< 3800) ' and if valid again, now more likely this value is correct
x := xval - 440 ' scale 0-3360
x /= 32 ' scale 0-100
x <#= 100 ' max 100
x #>=0 ' min 0
x := 100 - x ' so left edge on left
y := yval -420 ' scale 0-3180
y /= 32 ' scale 0-100
y <#= 100 ' max 100
y #>=0 ' min 0
y := 100-y ' so 0 is top left
result := (y <<8) | x ' return 00000000_00000000_YYYYYYYY_XXXXXXXX
if (xval < 500) or (xval >3800)
result := $0000FFFF ' invalid number(s)
PRI ILI9325initiate
' ************* Start Initial Sequence **********
Displaycmd($00E5,$78F0) ' set SRAM internal timing
Displaycmd($0001,$0100) ' set SS and SM bit 0001 0100 portrait
Displaycmd($0002,$0700) ' set 1 line inversion
Displaycmd($0003,$1030) ' set GRAM write direction and BGR=1. $0003 $1030
Displaycmd($0004,$0000) ' Resize register
Displaycmd($0008,$0207) ' set the back porch and front porch
Displaycmd($0009,$0000) ' set non-display area refresh cycle ISC[3:0]
Displaycmd($000A,$0000) ' FMARK function
Displaycmd($000C,$0000) ' RGB interface setting
Displaycmd($000D,$0000) ' Frame marker Position
Displaycmd($000F,$0000) ' RGB interface polarity
' *************Power On sequence ****************//
Displaycmd($0010,$0000) ' SAP, BT[3:0], AP, DSTB, SLP, STB
Displaycmd($0011,$0007) ' DC1[2:0], DC0[2:0], VC[2:0]
Displaycmd($0012,$0000) ' VREG1OUT voltage
Displaycmd($0013,$0000) ' VDV[4:0] for VCOM amplitude
Displaycmd($0007,$0001)
pause1ms(50) ' Dis-charge capacitor power voltage
Displaycmd($0010,$1090) ' 1490//SAP, BT[3:0], AP, DSTB, SLP, STB
Displaycmd($0011,$0227) ' DC1[2:0], DC0[2:0], VC[2:0]
pause1ms(50) ' delay
Displaycmd($0012,$001F) ' 001C// Internal reference voltage= Vci;
pause1ms(50) ' delay
Displaycmd($0013,$1500) ' $1000//1400 Set VDV[4:0] for VCOM amplitude 1A00
Displaycmd($0029,$0027) ' $0012 //001a Set VCM[5:0] for VCOMH //$0025 0034
Displaycmd($002B,$000D) ' Set Frame Rate 000C
pause1ms(50) ' delay
Displaycmd($0020,$0000) ' GRAM horizontal Address
Displaycmd($0021,$0000) ' GRAM Vertical Address
'
Adjust the Gamma Curve
//
Displaycmd($0030,$0000)
Displaycmd($0031,$0707)
Displaycmd($0032,$0307)
Displaycmd($0035,$0200)
Displaycmd($0036,$0008)
Displaycmd($0037,$0004)
Displaycmd($0038,$0000)
Displaycmd($0039,$0707)
Displaycmd($003C,$0002)
Displaycmd($003D,$1D04)
'
Set GRAM area
//
Displaycmd($0050,$0000) ' Horizontal GRAM Start Address
Displaycmd($0051,$00EF) ' Horizontal GRAM End Address
Displaycmd($0052,$0000) ' Vertical GRAM Start Address
Displaycmd($0053,$013F) ' Vertical GRAM Start Address
Displaycmd($0060,$A700) ' Gate Scan Line
Displaycmd($0061,$0001) ' NDL,VLE, REV
Displaycmd($006A,$0000) ' set scrolling line
'
Partial Display Control
/
Displaycmd($0080,$0000)
Displaycmd($0081,$0000)
Displaycmd($0082,$0000)
Displaycmd($0083,$0000)
Displaycmd($0084,$0000)
Displaycmd($0085,$0000)
' //
Panel Control
//
Displaycmd($0090,$0010)
Displaycmd($0092,$0600)
Displaycmd($0007,$0133) ' 262K color and display ON
ChangeOrientation(true) ' default to portrait
PRI SSD1289initiate
{ ILIcmddelay($0000,$0001) 'Turn Oscillator on POR-$0000
ILIcmddelay($0003,$A8A4) 'Power control (1) POR-$6664
ILIcmddelay($000C,$0000) 'Power control (2) POR- ?
ILIcmddelay($000D,$080C) 'Power control (3) POR-$0009
ILIcmddelay($000E,$2B00) 'Power control (4) POR-$3200
ILIcmddelay($001E,$00B0) 'Power control (5) POR-$0029
ILIcmddelay($0001,$4B3F) 'Driver output control, *landscape?? $6B3F* POR-$433F
ILIcmddelay($0002,$0600) 'LCD drive AC control POR-$0400
ILIcmddelay($0010,$0000) 'Sleep Mode sleep mode off POR-$0001
ILIcmddelay($0011,$6070) 'Entry Mode, *landscape? $4030* POR-$6830
ILIcmddelay($0005,$0000) 'Compare Register (1) POR-$0000
ILIcmddelay($0006,$0000) 'Compare Register (2) POR-$0000
ILIcmddelay($0016,$EF1C) 'Horizontal Porch POR-$EFC1
ILIcmddelay($0017,$0003) 'Vertical Porch POR-$0003
ILIcmddelay($0007,$0033) 'Display Control POR-$0000
ILIcmddelay($000B,$D308) 'Frame cycle Control POR-$D308
ILIcmddelay($000F,$0000) 'Gate Scan start position POR-$0000
ILIcmddelay($0041,$0000) 'Vertical Scroll Control (1) POR-$0000
ILIcmddelay($0042,$0000) 'Vertical Scroll Control (2) POR-$0000
ILIcmddelay($0048,$0000) 'First Window Start POR-$0000
ILIcmddelay($0049,$013F) 'First Window End POR-$013F
ILIcmddelay($004A,$0000) 'Second Window Start POR-$0000
ILIcmddelay($004B,$0000) 'Second Window End POR-$013F
ILIcmddelay($0044,$EF00) 'Horizontal Ram Address Postion POR-$EF00
ILIcmddelay($0045,$0000) 'Vertical Ram Address Start POR-$0000
ILIcmddelay($0046,$013F) 'Vertical Ram Address End POR-$013F
ILIcmddelay($0023,$0000) 'RAM write data mask (1) POR-$0000
ILIcmddelay($0024,$0000) 'RAM write data mask (2) POR-$0000
ILIcmddelay($0025,$8000) 'not in datasheet?
ILIcmddelay($004f,0) 'RAM Y address counter POR-$0000
ILIcmddelay($004e,0) 'RAM X address counter POR-$0000
Lcd_Write_Com($0022)
}
' based on C code
Displaycmd($0000,$0001)
Displaycmd($0003,$A8A4)
Displaycmd($000C,$0000)
Displaycmd($000D,$080C)
Displaycmd($000E,$2B00)
Displaycmd($001E,$00B0)
Displaycmd($0001,$2B3F)
Displaycmd($0002,$0600)
Displaycmd($0010,$0000)
Displaycmd($0011,$6070)
Displaycmd($0005,$0000)
Displaycmd($0006,$0000)
Displaycmd($0016,$EF1C)
Displaycmd($0017,$0003)
Displaycmd($0007,$0233)
Displaycmd($000B,$0000)
Displaycmd($000F,$0000)
Displaycmd($0041,$0000)
Displaycmd($0042,$0000)
Displaycmd($0048,$0000)
Displaycmd($0049,$013F)
Displaycmd($004A,$0000)
Displaycmd($004B,$0000)
Displaycmd($0044,$EF00)
Displaycmd($0045,$0000)
Displaycmd($0046,$013F)
Displaycmd($0030,$0707)
Displaycmd($0031,$0204)
Displaycmd($0032,$0204)
Displaycmd($0033,$0502)
Displaycmd($0034,$0507)
Displaycmd($0035,$0204)
Displaycmd($0036,$0204)
Displaycmd($0037,$0502)
Displaycmd($003A,$0302)
Displaycmd($003B,$0302)
Displaycmd($0023,$0000)
Displaycmd($0024,$0000)
Displaycmd($0025,$8000)
Displaycmd($004f,$0000)
Displaycmd($004e,$0000)
Lcd_Write_Com($0022)
PRI Displaycmd(c,d) ' instruction in one method
Lcd_Write_Com(c) ' send out a word
Lcd_Write_Data(d)
PRI Lcd_Write_Com(LCDlong)
LCD_RS_low ' can do rs first then cs - better for latch board
LCD_CS_Low ' not needed for the ILI9325 but the SSD1289 needs this
LCD_Writ_Bus(LCDlong)
LCD_CS_High ' not needed for the ILI9325 but the SSD1289 needs this
PRI Lcd_Write_Data(LCDlong)
LCD_RS_High ' can do rs first then cs
LCD_CS_Low ' not needed for the ILI9325 but the SSD1289 needs this
LCD_Writ_Bus(LCDlong)
LCD_CS_High ' not needed for the ILI9325 but the SSD1289 needs this
PRI Lcd_Write_Fast_Data(LCDlong) ' write RS elsewhere then skip the RS above as this is a latch output and takes longer
LCD_CS_Low ' not needed for the ILI9325 but the SSD1289 needs this
LCD_Writ_Bus(LCDlong)
LCD_CS_High ' not needed for the ILI9325 but the SSD1289 needs this
PRI LCD_Writ_Bus(LCDLong)
'LCDLong &= %00000000_00000000_11111111_11111111 ' mask to a word. Not needed if care taken always to send a word value
OUTA &= %11111111_11111111_00000000_00000000 ' set P0-P15 to zero ready to OR
OUTA |= LCDLong ' merge with the word to output
LCD_WR_Low ' send out the data
LCD_WR_High
PRI LCD_WR_Low
OUTA &= %11111111_11111011_11111111_11111111 ' p18 low
PRI LCD_WR_High
OUTA |= %00000000_00000100_00000000_00000000 ' p18 high
PRI LCD_RD_High ' not used, set high in hardware
PRI LCD_CS_Low
OUTA &= %11111111_11110111_11111111_11111111 ' p19 low
PRI LCD_CS_High
OUTA |= %00000000_00001000_00000000_00000000 ' p19 high
PRI LCD_RESET_Low ' reset low
Latch_ResetLow
PRI LCD_RESET_High
Latch_ResetHigh
PRI LCD_RS_Low ' for historical reasons t