Propeller GUI touchscreen and full color display

1141517192024

Comments

  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-08-14 - 16:21:30
    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.
  • average joeaverage joe Posts: 795
    edited 2012-08-15 - 02:39:32
    Dr_Acula wrote: »
    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...

    Not.it
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-08-15 - 17:27:50
    I moved the 161 load to pasm. Woot!

    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?
  • average joeaverage joe Posts: 795
    edited 2012-08-15 - 18:09:17
    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!
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-08-15 - 18:13:38
    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.
  • average joeaverage joe Posts: 795
    edited 2012-08-15 - 19:30:39
    Dr_Acula wrote: »
    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)
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-08-15 - 19:36:28
    Hah, found it!

    This is the line with the bug:
                            mov     latchvalue,%11111110    ' group 1, displays all off     
    

    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
     latchvalue := %11111110
    

    So, attached is the new touch.spin
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-08-15 - 19:47:51
    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
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-08-15 - 20:00:55
    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?
  • average joeaverage joe Posts: 795
    edited 2012-08-15 - 20:20:42
    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?
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-08-15 - 20:36:16
    Ok added in that PUB.

    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.
  • average joeaverage joe Posts: 795
    edited 2012-08-17 - 12:22:21
    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.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-08-17 - 17:53:04
    So you are doing this
    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").

    Too many variables here :(
  • average joeaverage joe Posts: 795
    edited 2012-08-17 - 18:46:56
    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.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-08-17 - 18:51:25
    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
  • average joeaverage joe Posts: 795
    edited 2012-08-17 - 18:57:34
    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
    
    Then, restore at done
    done                    mov      err, #0                ' reset err=false=good
                            mov     latchvalue, dirb        ' restore latch value
                            call    #set373
    
    or before draw
    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
    
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-08-17 - 19:05:28
    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.
  • average joeaverage joe Posts: 795
    edited 2012-08-17 - 19:08:55
    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
    latch373value           call    #get_values
                            mov     dirb,hubaddr
                            jmp     #done
    

    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.
  • groggorygroggory Posts: 205
    edited 2012-08-17 - 19:14:05
    Dr_Acula wrote: »
    New spin code below.

    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.

    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.
  • average joeaverage joe Posts: 795
    edited 2012-08-17 - 19:22:25
    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)
    
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-08-18 - 01:12:50
    Spent some time coding

    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!

    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][s] := 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][i] := 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][i] := 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][i] == 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][s] ' 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][i],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][i] <> byte[@rambuffer][i] 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[i] := 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[i] := 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[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] := 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
  • average joeaverage joe Posts: 795
    edited 2012-08-18 - 07:08:14
    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..
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-08-18 - 07:44:24
    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.
  • average joeaverage joe Posts: 795
    edited 2012-08-18 - 07:48:29
    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.
  • average joeaverage joe Posts: 795
    edited 2012-08-18 - 18:43:15
    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.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-08-18 - 19:30:20
    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

    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][s] := 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][i] := 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][i] := 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][i] == 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][s] ' 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][i],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][i] <> byte[@rambuffer][i] 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[i] := 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[i] := 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 [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] := 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
  • average joeaverage joe Posts: 795
    edited 2012-08-18 - 19:56:18
    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!
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-08-18 - 20:13:49
    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.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-08-18 - 20:24:38
    Ok, try this. "S" and "T commands are now just one spin instruction, so should make caching much easer

    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][s] := 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][i] := 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][i] := 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][i] == 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][s] ' 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][i],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][i] <> byte[@rambuffer][i] 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[i] := 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[i] := 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[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] := 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
  • average joeaverage joe Posts: 795
    edited 2012-08-19 - 00:13:20
    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
    
Sign In or Register to comment.