@ Johannes, I just got a chance to listen to the examples and I'm very impressed! Nice work as always!
I've been stuck on the display end for so long, I forgot all the other cool things that need to be worked on. I need to test a "master-slave" config with 2 boards. Since I have 2 fully built boards and only 1 *QUESTIONABLE* display, I was going to use the second as an expansion board. I've also started to build a multi-prop audio generation circuit on the bread board. The plan is to use 4 props, 8 - R-C DAC's, and a small mixer to allow multi-voice SidCog and TINYsynth patches. More experimenting later tonight.
Johannes and Doc, keep up the great work.
*edited*
Well, I ordered 2 new screens from hong-kong @ $13.00 a piece! Too bad it will be at least a month before they get here!
C ultimately will be useful but right now it is stuck with not being able to read and write the SD card. That is my poor understanding of C probably, but even a one line fopen is not working. There is a thread about this in the C forum.
@averagejoe, I have done a massive rewrite of the code so that the sram acts as a better ramdisk. All the fonts, text files, bitmaps and music files are all treated in the same way, so there are no 'special' areas of the ramdisk. Probably the most obvious advantage is that more than one font can be stored - and I wanted that so I could put time signatures and other notation in the music font. So you can have a basic music font with notes, and then another font with fancy things like portamento.
This is also in preparation for adding .wav files for sampling.
I see Ahle2 has updated the synth - so will need to update that as well.
And glue the music font to the synth player so it can play some music. Should be a simple 256 byte lookup table and maybe some code to handle sharps and flats.
Addit: new files May21 - added in Kye's .wav player. There are a number of online .mp3 to .wav converters (plus many programs can do this) so it is very easy to have a library of music on the SD card. Need to add a selection box so can select which .wav file to play.
Very nice work Doc. I've been working on porting the "Chiptune player" over to our hardware. Porting software is always a pain. I will let you know when I finish the port. I have not had a chance to look at the new code, but I will check it out when I get a chance.
Cross post, I uploaded a new version just now which has Kye's .wav player see post #364. At the simplest level, this makes the board a music player. I put a few songs on the sd card and they sound really good. The sample is just a tiny piano.wav file as there is no need for huge demo files as a typical .wav for a song is 40Mb or so. At a more complex level I'd like to try to understand Kye's code more and think about polyphonic .wav files playing at once. Somewhere in the code it is working out the volume. The volume is a startup constant but if you could change it on the fly you can mix multiple voices from a polyphonic sample based synth.
Wonderful news! I'm stuck with the ChipTune player right now. Seems like we have quite a few files in the root directory so I tried playing with directories... The problem comes from getting the number of files. I think I have it fixed...
PRI getNrOfTunes | t,i ''Gets the number of files in "Tunes" directory
nrOfTunes := -1 'init number
fat.closefile 'prepare sd card
propfont_string(fat.changeDirectory(string("tunes"))) 'change directory and display current working directory
repeat until strcomp(@Dir2, t) 'cycle through root directory,
fat.listEntries("N") 'get next fat entry
t := fat.listname 'copy name address to "t"
propfont_String(t) 'and display that name
fat.listEntries("N")
t := fat.listname
propfont_String(t)
i := 0
repeat until byte[t][i] == 0
firstFile[i] := byte[t] [i]
propfont_out(".")
i++
crlf
propfont_string(string("FirstFile"))
propfont_string(@firstFile)
t:= -1
repeat until strcomp(@firstFile, t)
fat.listEntries("N")
t := fat.listname
propfont_String(t)
nrOfTunes++
hex(nrOfTunes,4)
DAT
Dir2
byte ".. ",0
This is what I've been able to come up with. I'd like to inline the "Dir2" but can't figure out how to get it to work. Seems working with directories is a bit confusing, but could help the "file clutter?"
*edited*
I have the chiptune player functioning. Not very pretty right now. But it opens chiptunes, and plays! I have not figured out the directory yet, so I have a copy in the root AND in "tunes" folder. I need to figure how to tack on the directory in file opens. Here's what I have working so far. Not much of a front end, but it works!
[code]
'' ILI9325 driver using the Touchblade161 design for faster ram to display transfers with a hc137 for group select
'' Average Joe and James Moxham 2012
' notes re groupings of pins
' To shut down a group, deselect it by making P22 low. This makes Y0-Y7 from the 137 all high
' To start a group, the initial state is P0-P20 all HiZ and P22 HiZ (P21 and 23 are audio and there is less audio noise if the pins are not next to each other)
' Passing to a cog, P0-P20 and P22 HiZ ie DIRA all as 0 fo these pins. This is to avoid contention between spin and the cog code
' A routine in Spin or Pasm that changes the 137 will also return P0-P20 and P22 as HiZ.
' In general terms, H is inactive and L is active. So changing a pin from HiZ to H will have no effect as pins all have pullups.
' Any handover to cog code must be done in this HiZ state and all pins in a group need to work with this
' general purpose constants
Margin = 4 ' some characters have xoffset of -1 or -2 eg v, and won't display on the extreme left edge
' external ram size of data blocks
Ram_Fontsize = 65536 ' 128k for fonts (largest is around 108k)
Ram_MaskSize = (64*64*2)/2 ' 64x64 (59x60) bytes divide by 2 for words
Ram_IconSize = (64*64*2)/2 ' 2 bytes per pixel converts from .bmp in the routine
Ram_TextSize = 32768 ' 64k for text
' external ram locations, if adding more, follow the pattern
FontTable = 0 ' address for ram chip (2 bytes per address)
Mask = FontTable + Ram_Fontsize ' address for raw mask image
Icon = Mask + Ram_MaskSize ' address for icon image
Text = Icon + Ram_IconSize ' text buffer for word processing etc
RamDiskStart = Text + Ram_TextSize ' ramdisk with searchable bitmap files
OBJ
'fat: "SD3.01_FATEngine.spin" ' thanks to Kye - sd card
fat: "SD-MMC_FATEngine.spin" ' Kye's latest sd driver and no RTC
spi: "SPI_ASM" ' thanks to Beau - spi driver for touch screen
str: "ASCII0_STREngine.spin" ' thanks to Kye - string routines
' term: "FullDuplexSerial.spin" ' Thanks to Chip - for debugging if display not working
' synth : "tinysynth" ' ' Thanks Ahle2 http://forums.parallax.com/showthread.php?139724-TinySynth-!
VAR
long orientation
long curx, cury
long clkcycles ' for the delay routine
word ScreenWidth ' either 240 in portrait or 320 in landscape
word ScreenHeight ' 320 in portrait or 240 in landscape
byte LineOfText1[20] ' general purpose string buffer
byte LineOfText2[20] ' general purpose string buffer
byte FontHeight ' size of the current loaded font (pixels = fontsize *.75)
word BackFontColor ' the background color in RRRRRGGG GGGBBBBB format
long RamDiskPointer ' pointer to current ramdisk location
long RamDiskFiles ' number of files in the ramdisk
'byte sdbuffer[512] ' can't put these here, writes to sdbuffer sometimes back overwrite variables above this eg fontheight!!
'byte buffer2[512] ' so put these in a dat section
'word rambuffer[256]
long DecRate ' synth settings
long SusLevel
long PWinit
long pwmRate
long Filter
long Portamento
''*****************Start chiptune Player CON here***********************
CON
ENABLE_PAL = false
''***************Start chiptune Player VAR here
VAR
byte buffer[25]
byte filename[18]
byte playing
byte chipTuneType
byte prevChipTuneType
byte ffw
word playRate
long wait
long fileNumber
long nrOfTunes
long scrollSpeed
long frameCounter
long scrollCounter
long textPointer
long AY_register
PUB Main | i, oldStatus, fin,yval, xval
start_ram ' start the external ram / display driver in a cog
Change161(0) ' reset all the 161 counters to zero
SelectMemGroup ' select group1
Start_ILI9325 ' start the display. Don't clear screen as this uses ram commands and these might not be working yet
SetOrientation(true) ' in portrait (true) or landscape mode (false), must be before clearscreen otherwise don't know screensize
'pause1ms(5000)
Clearscreen(RGBtoWord(0,0,0)) ' clear screen to black - uses the display driver above
propfont_string(String("Mount SD"))
fat.fatEngineStart( _dopin, _clkpin, _dipin, _cspin, _wppin, _cdpin, _rtcres1, _rtcres2, _rtcres3)
fat.mountPartition(0) ' mount the sd card
propfont_string(String("Mounted"))
ClearRamdisk ' clear ram disk for pictures not on the desktop
''*Note* Load fonts and bmp's here!
'Main loop
repeat
' Wait between register updates
if (cnt - wait) > 0
if ffw
wait := cnt + (clkfreq/(playRate<<2))
else
wait := cnt + (clkfreq/playRate)
' Update the PSG
if playing
case chipTuneType
1: updateSID
2: updateAY
' Check for a change in tv status
' if oldStatus <> tv_status
' oldStatus := tv_status
' ' Check for vbl
' if tv_status == 2
' scroll
'
' ' Handle user interactions
' if input.getLastState <> input.getCurrentState
SelectSPIGroup
yval := TouchYPercent ' decode yval 0-100%
xval := TouchXPercent ' decode xval 0-100%
if (xval <> 255) and (yval <> 255)
'SelectMemGroup
'hex(xval,2) ' if display any debug messages need to restart the SPI cog as it has been stopped by the debug display
'hex(yval,2)
if (yval > 89)
SelectMemGroup
' Handle previous tune
if (xval < 19)
handleMuting
if fileNumber == 0
fileNumber := nrOfTunes + 1
fileNumber--
openNextFile
' Handle pausing
if (xval > 37 and xval < 70)
playing := not playing
handleMuting
ifnot playing
setScrollMessage(@message2)
else
ffw := false
setScrollMessage(@songName)
' Handle next tune
if (xval > 79 and xval < 99)
handleMuting
if fileNumber == nrOfTunes
fileNumber := -1
fileNumber++
openNextFile
' ' Handle pausing
' if input.getLastState&input#START
' playing := not playing
' handleMuting
'
' ifnot playing
' setScrollMessage(@message2 - @message)
'
' else
' ffw := false
' setScrollMessage(@songName - @message)
'
' scrollSpeed := 8
'
' ' Handle fast forward
' if playing > 0 and input.getLastState&input#A
' ffw := not ffw
'
' if ffw
' setScrollMessage(@message3 - @message)
'
' else
' setScrollMessage(@songName - @message)
'
' scrollSpeed := 8
'
' ' Handle next tune
' if input.getLastState&input#RIGHT
' if fileNumber == nrOfTunes
' fileNumber := -1
'
' fileNumber++
' openNextFile
'
' ' Handle previous tune
' if input.getLastState&input#LEFT
' if fileNumber == 0
' fileNumber := nrOfTunes + 1
'
' fileNumber--
' openNextFile
'
' ' Handle next page ( += 10 )
' if input.getLastState&input#DOWN
' fileNumber += 10
'
' if fileNumber > nrOfTunes
' fileNumber := 0
'
' openNextFile
'
' ' Handle previous page ( -= 10 )
' if input.getLastState&input#UP
' fileNumber -= 10
'
' if fileNumber < 0
' fileNumber := nrOfTunes
'
' openNextFile
PRI openNextFile | i, t, nameP
ffw := false
playing := true
' Iterate through files and find the file with fileNumber == X
fat.closeFile
repeat i from 0 to fileNumber
fat.listEntries("N")
nameP := fat.listname
t:=0
repeat until byte [nameP][t] == 0
byte [@filename][t]:= byte[nameP][t]
t++
byte[@filename][t] := 0
' Find out what kind of file type we are dealing with
prevChipTuneType := chipTuneType
chipTuneType := 0
fat.openFile(@filename, "R")
if fat.readData(@buffer, 4) < 0
if byte[nameP - 3] == "D" and byte[nameP - 2] == "M" and byte[nameP - 1] == "P"
chipTuneType := 1
if buffer[0] == "S" and buffer[1] == "D" and buffer[2] == "M" and buffer[3] == "P" ' SDMP
chipTuneType := 1
elseif buffer[0] == "Y" and buffer[1] == "M" and buffer[2] == "6" and buffer[3] == "!" ' YM6!
chipTuneType := 2
' Set some standard values in case the header doesn't provide any info
playRate := 50
bytefill(@songName + 10, 32, 15)
intToString(@songName + 6, fileNumber, 5)
repeat i from 0 to 15
if filename
byte[@songName + i + 10] := filename
else
i := 16
' Init everything, read the header and start the PSG
fat.openFile(@filename, "R")
if chipTuneType == 1
readHeaderSID
initSID
elseif chipTuneType == 2
readHeaderAY
initAY
wait := cnt + 20_000_000
PRI handleMuting
case chipTuneType
1:
if playing
SID.unmute
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
propfont_string(stringptr) ' print the filename
propfont_string(result) ' print the error message
repeat ' stop the program
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)
PUB TouchSwish | 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 TouchStart
SPI.start(3,0) ' delay,state, start clock high
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)
PUB SDToDisplay(stringptr) ' bitmap from SD to display
Clearscreen(0)
ClearRamDiskExcludeDesktop
LoadBMPtoRamdisk(stringptr)
DrawBMP(stringptr,0,0)
PUB FindBMP(stringptr) | i,rampointer,ramcounter,match,searchlen,width,height ' pass the name and the x and y location, searches for this file and if found, displays it
rampointer := RamDiskStart
searchlen := str.instr(stringptr,".") - 1 ' ignore the .bmp extension
repeat ramcounter from 1 to ramdiskfiles
docmd("T",@sdbuffer,rampointer,$36) ' get hex $36 bytes from the ramdisk = filenames
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
match := true
repeat i from 0 to searchlen - 1
if sdbuffer <> byte[stringptr]
match := false ' any characters not matching, try next one. (case must match too)
if match == true
quit ' found a match so quit looking for any more files
' get the next rampointer = $36 + width * height
rampointer += $36 + (width * height)
if match == true ' if found a match return the rampointer
result := rampointer
else
result := -1 ' no match so zero
PUB ClearRamdisk
ILISetcursor(0,0) ' cursor for debugging to 0,0
RamDiskPointer := RamDiskStart ' set the global ramdisk pointer start
RamDiskFiles := 0
PUB ClearRamdiskExcludeDesktop ' exclude the desktop which is the first 76800+36 pixels and also sets ramdiskfiles to 1, not 0
ClearRamdisk
RamDiskPointer += $36 + (240 * 320) ' leave room for the desktop
RamDiskFiles +=1 ' file counter +1
PUB DrawBMP(stringptr,x,y) | rampointer,width,height ' pass the name and the x and y location, searches for this file and if found, displays it
rampointer := FindBMP(stringptr) ' find the file in the list
if rampointer <> -1 ' if a match then draw
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
PUB TextBoxClear(x1,y1,x2,y2) ' draw the outline of the box
ClearRectangle(x1,y1,x2,y2,RGBtoWord(255,255,255))
Drawline(x1-1,y1-1,x2,y1-1,RGBtoWord(64,64,64)) ' gray outline lines - extract values from paint shop pro
Drawline(x1-2,y1-2,x2+1,y1-2,RGBtoWord(128,128,128)) ' horizontal upper light gray
Drawline(x1-1,y1-1,x1-1,y2,RGBtoWord(64,64,64)) ' vertical left gray
Drawline(x1-2,y1-2,x1-2,y2+1,RGBtoWord(128,128,128)) ' vertical left light gray
Drawline(x2+2,y1-2,x2+2,y2+2,RGBtoWord(255,255,255)) ' vertical right white
Drawline(x1-2,y2+2,x2+2,y2+2,RGBtoWord(255,255,255)) ' horizontal lower white
PUB LoadBMPtoRamdisk(stringptr) | width,height,w,i ' load and display a bitmap file
' 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
' on the ram store a 0x36 byte header - the bitmap header but put in the filename at location 0
' this becomes a rudimentary file system that can be searched to find pictures
'propfont_out("1") ' for debugging display something while load in pictures
' pause1ms(Debugtimer)
fat.openfile(stringptr,"R")
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
repeat i from 0 to 11 ' add the file name at the beginning of the header
sdbuffer := byte[stringptr]
docmd("S",@sdbuffer,RamDiskPointer,$36) ' store header to ram
i := $36 + RamDiskPointer + ((height - 1) * width) ' start + header and on the last row and work back
repeat height
fat.readdata(@sdbuffer,w) ' read in the first row
docmd("F",@sdbuffer,@rambuffer,width)
HubToRam(i,width) ' send to ram
i -= width ' subtract the width, move up the picture
FixGlitch
fat.closefile
RamDiskPointer += $36 + (width * height) ' increment the ramdiskpointer for next picture
RamDiskFiles += 1 ' increment number of entries in the ram disk
FixGlitch
PUB DrawCheckbox(x,y,Checked,Stringptr) | i ' draw a checkbox
if checked
DrawBMP(string("Checked.bmp"),x,y)
else
DrawBMP(string("Uncheckd.bmp"),x,y)
curx := x+20 ' add 20 width
cury := y
ILIPrintString(stringptr)
PUB DrawRadioBox(x,y,Checked,Stringptr)| i ' draw a radio box
if checked
DrawBMP(string("RadioChk.bmp"),x,y)
else
DrawBMP(string("RadioUnc.bmp"),x,y)
curx := x+20 ' add 20 width
cury := y
ILIPrintString(stringptr)
PUB DrawButton(x,y,Stringptr) ' draw a radio box
DrawBMP(string("butn8430.bmp"),x,y)
curx := x + (84/2)-(str.len(stringptr)*4 / 2) ' centre, avg width 4 per char 84 could be a variable width passed to this routine
cury := y + 8
ILIPrintString(stringptr)
PUB LoadFile(strpointer) | filesize,i ' reads a text file into external ram
OpenFileRead(strpointer) ' open a file
filesize := fat.filesize
i := Text ' store text here
repeat (filesize >>8 ) + 1 ' so works with 256 byte blocks
fat.readdata(@sdbuffer,256) ' get 256 bytes from sd card
docmd("S",@sdbuffer,i,128) ' send 128 words to external ram
i +=128 ' increment counter
fat.closefile
return filesize
PUB NewFont(strpointer)
ILIloadfont(strpointer)
' only call this routine with the cursor curx at zero
ClearRectangle(curx,cury,screenwidth,cury+FontHeight,BackFontColor) ' clear an area the same color as the font background
PUB ILIChar(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
jump := ReadRamLong(fonttable,(ascii << 2) + 256) ' jump location = ascii *4 plus fonttable + 256
docmd("T",@buffer2,fonttable+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
ILIcrlf ' 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
ILIcr
x := curx
if ascii ==10 ' line feed (new line)
ILIlf
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(fonttable+((jump+32)>>1),size) ' move bytes from ram out to the display
return x + xadvance
PUB ILIcrlf
ILIcr
ILIlf
PUB ILIcr ' carriage return, to left margin
curx := Margin ' some characters are -1 xoffset so won't display
PUB ILIlf ' 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 ILIPrintstring(stringptr) 'print at curx,cury
'' Print a zero-terminated string
repeat strsize(stringptr)
curx := ILIChar(byte[stringptr++],curx,cury)
PUB ILISetCursor(x,y)
curx := x
cury := y
PUB ReadRamLong(WordStart,Bytevalue) ' easier calculating
docmd("T",@result,Wordstart+Bytevalue>>1,2)
return result
PUB ILILoadFont(stringptr) | filesize,i ' read a .ifn file premade in angelcode bmfont and then the vb.net program
' ' pass file name in stringptr
i := fonttable ' store at the constant 'fonttable' location, 2 bytes per address
OpenFileRead(stringptr) ' open an image. Size is in the first long and has been rounded up to the next 256 byte block
filesize := fat.filesize ' read the file size. This file has been rounded up to the next 256 byte block
repeat (filesize >> 8) ' read in this number of 256 byte blocks
fat.readdata(@rambuffer,256) ' get 256 bytes from sd card
HubToRam(i,128) ' 128 words = 256 bytes
i += 128 ' counter for ram location
fat.closefile
docmd("T",@rambuffer,FontTable,128) ' read first 128 words =256 bytes back
FontHeight := byte[@rambuffer][251] ' font height
'hex(fontheight,8)
repeat i from 0 to 2
sdbuffer := byte[@rambuffer][35+i]
docmd("E",@sdbuffer,@buffer2,1) ' convert to 2 byte .ili format
BackFontColor := ( byte[@buffer2][1] << 8) | byte[@buffer2][0] ' correct order
'hex(backfontcolor,4)
result := filesize
PUB MergeBackgroundIcon(stringptr,x,y) | i,j,k,n ' 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
LoadBMPtoRamdisk(stringptr) ' load icon into ram
i := RamDiskStart + $36 + (y*(screenwidth+1)+x) ' location of picture pixel in ram
j := FindBMP(stringptr) + $36 ' location of icon in ram
k := FindBMP(string("mask.bmp")) + $36 ' location of mask
repeat 60
docmd("T",@sdbuffer,i,59) ' get 59 pixels from background
docmd("T",@rambuffer,j,59) ' get 59 icon pixels
docmd("T",@buffer2,k,59) ' get words for the mask
'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
docmd("X",@sdbuffer,@rambuffer,@buffer2) ' pasm version of the above 3 lines
docmd("S",@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
FixGlitch
PUB HubToRam(RamAddress,Number) ' send pixels from rambuffer hub to ram
docmd("S",@rambuffer,RamAddress,Number)
PUB RamToHub(RamAddress,Number) ' send pixels from ram to hub at rambuffer
docmd("T",@rambuffer,RamAddress,Number)
PUB RamToDisplay(RamAddress,Number) ' send pixels from ram to display
docmd("U",0,RamAddress,Number)
PUB HubToDisplay(HubAddress,Number) ' send pixels from hub to display
docmd("V",HubAddress,0,Number)
PUB Change137(group)
docmd("Y",group,0,0) ' change the current group
PUB Change161(RamAddress) ' pass ramaddress. Returns in group 1
docmd("Z",0,RamAddress,0)
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
HubToDisplay(@rambuffer,256)
PUB RGBtoWord(R,G,B) ' convert RGB to 2 byte ILI color
sdbuffer[0] := R
sdbuffer[1] := G
sdbuffer[2] := B
docmd("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
docmd("V",@rambuffer,0,256)
if remainder <> 0
docmd("V",@rambuffer,0,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 SelectMemGroup ' select group 2 for memory transfer
spi.stop ' and stop any other cogs here too if needed
OUTA |= %00000000_00011111_11111111_11111111 ' set P0-P20 high
DIRA |= %00000000_00011111_11111111_11111111 ' set P0-P20 as outputs
Change137(1)
OUTA |= %00000000_00011111_00000000_00000000 ' set P16-P20 high
DIRA |= %00000000_00011111_11111111_11111111 ' set P0-P20 as outputs
PUB SelectSPIGroup ' select group 0 for the SPI
'SsdInit
OUTA |= %00000000_00000000_00000010_00000000 ' keep p9 high so doesn't reset
DIRA |= %00000000_00000000_00000010_00000000 ' P0-P2 are used to load the 137
Change137(2)
OUTA |= %00000000_00000000_00000010_00000000 ' keep p9 high so doesn't reset
DIRA |= %00000000_00000000_00000010_00000000 ' enable these pins for output
DIRA &= %11111111_11111111_11111111_11111000 ' so no clashes with the cog using P0-P2 set these as inputs
TouchStart ' start the touchscreen cog
PUB Propfont_string(stringptr) 'print at curx,cury
repeat strsize(stringptr)
Propfont_out(byte[stringptr++])
crlf
PUB crlf
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
crlf
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 ' 137 and appropriate pins
orientation := n
'_________Start ILI_______________________
' if orientation
' ' for origin top left and portrait mode
' ILIcmd($0001,%00000001_00000000) ' $0001,$0100 set SS and SM bit 0001 0100 portrait
' ILIcmd($0003,%00010000_00110000) ' $0003,$1030 set GRAM write direction and BGR=1. $0003 $1030
' else
' ' for origin top right and landscape mode
' ILIcmd($0001,%00000000_00000000) ' set SS and SM bit 0001 0000 landscape
' ILIcmd($0003,%00010000_00111000) ' landscape $1028 = original but 1038 is correct - not mirror image
'________END ILI_____________________
'________Start SSD___________________
if orientation
' for origin top left and portrait mode
ILIcmd($0001,$2B3F) ' $0001,$0100 set SS and SM bit 0001 0100 portrait
ILIcmd($0011,$6070) '6070 ' $0003,$1030 set GRAM write direction and BGR=1. $0003 $1030
else
' for origin top right and landscape mode
ILIcmd($0001,$4B3F) ' set SS and SM bit 0001 0000 landscape
ILIcmd($0011,$6838) ' landscape $1028 = original but 1038 is correct - not mirror image
'_________END SSD____________________
PUB Start_ILI9325 ' pass orientation true = portrait, false = landscape
ILI_Reset_High
pause1ms(5)
ILI_Reset_Low
pause1ms(5)
ILI_Reset_High ' returns in group 2, ie CS is high
Change137(2) ' replaces ILI_CS high - select another group
ILI_RD_High
ILI_WR_High
pause1ms(5)
'ILI_CS_Low
SelectMemGroup ' enables correct pins for output and sets CS low
'IliInit
SsdInit
PRI SsdInit
ILIcmd($0000,$0001) 'Turn Oscillator on POR-$0000
pause1ms(1)
ILIcmd($0003,$A8A4) 'Power control (1) POR-$6664
pause1ms(1)
ILIcmd($000C,$0000) 'Power control (2) POR- ?
pause1ms(1)
ILIcmd($000D,$080C) 'Power control (3) POR-$0009
pause1ms(1)
ILIcmd($000E,$2B00) 'Power control (4) POR-$3200
pause1ms(1)
ILIcmd($001E,$00B0) 'Power control (5) POR-$0029
pause1ms(1)
ILIcmd($0001,$4B3F) 'Driver output control, *landscape?? $6B3F* POR-$433F
pause1ms(1)
ILIcmd($0002,$0600) 'LCD drive AC control POR-$0400
pause1ms(1)
ILIcmd($0010,$0000) 'Sleep Mode sleep mode off POR-$0001
pause1ms(1)
ILIcmd($0011,$6070) 'Entry Mode, *landscape? $4030* POR-$6830
pause1ms(1)
ILIcmd($0005,$0000) 'Compare Register (1) POR-$0000
pause1ms(1)
ILIcmd($0006,$0000) 'Compare Register (2) POR-$0000
pause1ms(1)
ILIcmd($0016,$EF1C) 'Horizontal Porch POR-$EFC1
pause1ms(1)
ILIcmd($0017,$0003) 'Vertical Porch POR-$0003
pause1ms(1)
ILIcmd($0007,$0033) 'Display Control POR-$0000
pause1ms(1)
ILIcmd($000B,$D308) 'Frame cycle Control POR-$D308
pause1ms(1)
ILIcmd($000F,$0000) 'Gate Scan start position POR-$0000
pause1ms(1)
ILIcmd($0041,$0000) 'Vertical Scroll Control (1) POR-$0000
pause1ms(1)
ILIcmd($0042,$0000) 'Vertical Scroll Control (2) POR-$0000
pause1ms(1)
ILIcmd($0048,$0000) 'First Window Start POR-$0000
pause1ms(1)
ILIcmd($0049,$013F) 'First Window End POR-$013F
pause1ms(1)
ILIcmd($004A,$0000) 'Second Window Start POR-$0000
pause1ms(1)
ILIcmd($004B,$0000) 'Second Window End POR-$013F
pause1ms(1)
ILIcmd($0044,$EF00) 'Horizontal Ram Address Postion POR-$EF00
pause1ms(1)
ILIcmd($0045,$0000) 'Vertical Ram Address Start POR-$0000
pause1ms(1)
ILIcmd($0046,$013F) 'Vertical Ram Address End POR-$013F
pause1ms(1)
ILIcmd($0023,$0000) 'RAM write data mask (1) POR-$0000
pause1ms(1)
ILIcmd($0024,$0000) 'RAM write data mask (2) POR-$0000
pause1ms(1)
'ILIcmd($0025,$8000) 'not in datasheet?
' pause1ms(1)
ILIcmd($004f,0) 'RAM Y address counter POR-$0000
pause1ms(1)
ILIcmd($004e,0) 'RAM X address counter POR-$0000
pause1ms(1)
ChangeOrientation(false) ' default to portrait
'
SSD1289 END
PUB Draw(x1, y1, x2, y2)|HORIZONTALRAMADDRESSPOS ' sets the pixel to x1,y1 and then fills the next (x2-x1)*(y2-y1) pixels
SelectMemGroup ' set 137 to correct value
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
PUB Pixel(pixelcolor) ' send out a pixel
Lcd_Write_Data(pixelcolor)
PUB pause1ms(period)
'' 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 138 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"))
crlf
PRI ILIcmd(c,d) ' instruction in one method
Lcd_Write_Com(c) ' send out a word
Lcd_Write_Data(d)
PRI Lcd_Write_Com(ILIlong)
ILI_RS_low
LCD_Writ_Bus(ILIlong)
'docmd("W",ILIlong,0,0) ' try moving to pasm, leaves spin and cog dira as 0 so need to reset both
'SelectMemGroup ' not working though. Not sure if should be ILIlong or @ILIlong. neither work
PRI Lcd_Write_Data(ILIlong)
ILI_RS_High
LCD_Writ_Bus(ILIlong)
PRI LCD_Writ_Bus(ILILong)
'ILILong &= %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 |= ILILong ' merge with the word to output
ILI_WR_Low ' send out the data
ILI_WR_High
PRI ILI_RS_Low
OUTA &= %11111111_11111011_11111111_11111111 ' P18 low
PRI ILI_RS_High
OUTA |= %00000000_00000100_00000000_00000000 ' P18 high
PRI ILI_WR_Low
OUTA &= %11111111_11101111_11111111_11111111 ' p20 low
PRI ILI_WR_High
OUTA |= %00000000_00010000_00000000_00000000 ' p20 high
PRI ILI_RD_High ' not used, set high in hardware
'PRI ILI_CS_Low
' Change137(1) ' change to group 1
'
'PRI ILI_CS_High
' Change137(0) ' change to group 0 (any group other than 1)
PRI ILI_RESET_Low ' reset low
Change137(2) ' group 2
DIRA |= %00000000_00000000_00000010_00000000 ' set pin 9 as an output
OUTA &= %11111111_11111111_11111101_11111111 ' set pin 9 low
PRI ILI_RESET_High
Change137(2) ' group 2
DIRA |= %00000000_00000000_00000010_00000000 ' set pin 9 as an output
OUTA |= %00000000_00000000_00000010_00000000 ' set pin 9 high
DAT
sdbuffer byte $0[1024] ' 512 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 start_ram : err_
' Initialise the Drac Ram driver. No actual changes to ram as the read/write routines handle this
command := "I"
cog := 1 + cognew(@tbp2_start, @command)
if cog == 0
err_ := $FF ' error = no cog
else
repeat while command ' driver cog sets =0 when done
err_ := errx ' driver cog sets =0 if no error, else xx = error code
PUB stop_ram
if cog
cogstop(cog~ - 1)
PUB DoCmd(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
a := dira ' store the state of these and restore at the end
b := outa
DIRA &= %11111111_10100000_00000000_00000000 ' tristate all common pins that cog can change so no conflicts
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
dira := a ' restore dira and outa
outa := b
CON
'' Modified code from Cluso's triblade
'' commands to move blocks of data to the ILI9325 touchscreen display
' DoCmd(command_, hub_address, ram_address, block_length)
' I - initialise
' S - Move data from hub to ram
' T - Move data from ram to hub
' U - Move data from ram to display
' V - Hub to display
' W - not working - writecom in pasm
' E - convert from .raw RGB to two byte ILI format RRRRRGGG_GGG_BBBBB
' F - convert from .bmp BGR format to two byte ILI format
' X - merge icon and background based on a mask
' Y - Change 137 output Returns P0-P20 and P22 in HiZ. Pass hubaddrs
' Z - Set 161 pins. Returns in group 1
VAR
' communication params(5) between cog driver code - only "command" and "errx" are modified by the driver
long command, hubaddrs, ramaddrs, blocklen, errx, cog ' rendezvous between spin and assembly (can be used cog to cog)
' command = A to Z etc =0 when operation completed by cog
' hubaddrs = hub address for data buffer
' ramaddrs = ram address for data
' blocklen = ram buffer length for data transfer
' errx = returns =0 (false=good), else <>0 (true & error code)
' cog = cog no of driver (set by spin start routine)
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)
' Initialise hardware tristates everything and read/write set the pins
init mov err, #0 ' reset err=false=good
'mov dira,zero ' tristate the pins with the cog dira
and dira,maskP0P20P22 ' tristates all the common pins
done 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, #"W" wz ' lcdwritecom in pasm, not working
' if_z jmp #pasmlcdwritecom
cmp cmd, #"X" wz ' merge icon and background based on a mask
if_z jmp #mergeicons
cmp cmd, #"Y" wz ' change the 137 output
if_z jmp #changegroup
cmp cmd, #"Z" wz ' set the 161 counters
if_z jmp #set161
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
get_values_ret ret
' Pass pasm_n = 0- 7 come to this with P0-P20 and P22 tristated and returns them as this too
set137 or dira,maskP22 ' pin 22 is an output
andn outa,maskP22 ' set P22low so Y0-Y7 are all high
or dira,maskP0P20 ' pins P0-P20 are outputs
and outa,maskP0P2low ' set these 3 pins low
or outa,pasm_n ' set the 137 pins
or outa,maskP22 ' pin 22 high
set137_ret ret ' return
load161pasm ' uses ramaddr
or outa,maskP0P20 ' set P0-P20 high
or dira,maskP0P20 ' output pins 0-20
mov pasm_n,#0 ' group 0
call #set137 ' set the 137 output
and outa,maskP0P18low ' pins 0-18 set low
or outa,ramaddr ' output addres to 161 chips
or outa,maskP19 ' clock high
There's a huge step from a wav player to a polyfonic sample based synth.
It's better to do the synth from ground up than hacking Kyes wav player object; In the end almost nothing will be left from the wav player anyway.
To be able to play samples at an arbitrary frequency, a phase accumulator is the way to go.
Use X number of the MSBs of the accumulator as an index into the sample in memory.
The example above can play samples of up to 64 kB in size. You can increase sample size by shifting less than 16 bits, but the pitch accuracy will decrease.
Less than 12 bit pitch accuracy will sound out of tune. (Btw, Pokey used 8 bits... ugh..)
The rate at wich the sample is played back can be calculated by the formula below.
samplePlaybackRate = mixingRate * (32bitFreqValue / (2^(32 - nrOfMsbsUsedAsSampleIndex) )
Note that samplePlaybackRate isn't the resulting frequency of the sampled instrument itself.. it's just the sample rate. mixingRate is the sample rate out to the DAC.
A wavetable synth can be made by using just a few of the MSBs (like 8 or so) and then letting the 32 bit accumulator wrap over from 0xFFFFFFFF to 0. Each wrap is a period.
Then just apply a volume envelope and you're done.
I agree that it's a huge step from sample player to polyphonic sampling synth. You need all the things already pointed out, and I would add LPF12,LPF24,BPF,HPF filters with frequency and resonance. It would also be nice to "patch" modulation sources to the filter frequency and resonance as well as the volume envelope. There's quite a bit to consider and this would be best for C IMO.
Speaking of C, I think I have it working in XMMC AND LMM. See post here : http://forums.parallax.com/showthread.php?140010-Files&p=1099650&viewfull=1#post1099650 Looks like it's time to work on cache driver?
I have a bit of work to do before I release the chiptune player. It is functional except fast forward, rewind and GUI screening. I'm REALLY excited now! More later!
*edit*
Here's the newest transport BMP!
*more edit*
Here's an ALPHA release of chiptune player. It SHOULD be ready for the ILI but I might have missed something when porting from the SSD. These use OLD *May 13't* drivers.
Just updated to include the transport files for the SD card!
Well done re getting C working. I'll have to check that out.
There are some amazing synthesizer threads running at the moment. Lots of possibilities there. Generally, those synths are going to need knobs or sliders. I spent last night coding a multiline text box and a multi select box. Useful for things like a word processor and also for selecting which song to play. I have some rudimentary things working in gray but I am thinking windows95 gray is looking a bit boring and old-school. Maybe need some more funky sliders and buttons and checkboxes eg http://www.dreamstime.com/stock-images-stock-images-scroll-bar.-image14529454.
I wrote some of the drawing as code drawing individual lines and pixels but it may actually work out faster to use bitmaps. Also if we keep the size of things a common standard (eg a scrollbar is 15 pixels wide) then you can start to think about different 'skins'.
Oh and I just got an email saying the dual screen boards have been shipped.
Thinking more about a more modern looking GUI, I have been looking again at html. I think html could be a more universal language than vb.net etc as you can debug things in a browser. I'm back inside the w3schools tutorials looking at things like frames, buttons, checkboxes and scrollbars. Interestingly, the same html code looks slightly different visually in Firefox, Internet Explorer and Opera. I've gone for the Opera style. It is designed to go on a white background (cf a lot of older windows code which has a gray background). So to start with the html code for a frame
And then add some scrollbars. In Opera these are gray when inactive and a light purple when active. Scrollbars will have three active touchscreen areas per bar - the top and bottom arrows move up or down one line and the slider goes through an entire text file, or moves around a bigger picture/webpage than can fit on the screen.
In many ways it is easier to draw bitmaps than to draw pixels and lines, so as much as possible, draw things with bitmaps that have been loaded into the ramdisk.
Spin code
PUB HTML ' based on http://www.w3schools.com/html/html_iframe.asp and the Opera browser - text boxes, check boxes, scrollbars etc. Gray for inactive, light purple for active
Clearscreen($FFFF) ' clear the screen to white
RamErase1 ' erase all but file 0 (desktop)
SDBMPtoRam(string("arrupgry.bmp")) ' file 1 up arrow part of the slider
SDBMPtoRam(string("arrdngry.bmp")) ' file 2 down arrow part of the slider
SDBMPtoRam(string("scrolcnr.bmp")) ' file 3 corner gray part of the slider
SDBMPtoRam(string("arrlgry.bmp")) ' file 4 left gray
SDBMPtoRam(string("arrrgry.bmp")) ' file 5 right gray
SDBMPtoRam(string("slivegry.bmp")) ' file 6 slider vertical gray
SDBMPtoRam(string("slihogry.bmp")) ' file 7 slider horizontal gray
iframe(10,10,120,200,50,128) ' x1,y1,x2,y2,slider h 0-255, slider w
repeat
PRI iframe(x1,y1,x2,y2,h,w) ' see 3schools tutorial
Drawline(x1-1,y1-1,x2+1,y1-1,RGBtoWord(0,0,0)) ' black line at top
Drawline(x1-2,y1-2,x2+2,y1-2,RGBtoWord(0,0,0)) ' black line at top dual line
Drawline(x1-1,y1-1,x1-1,y2+1,RGBtoWord(0,0,0)) ' left black line
Drawline(x1-2,y1-2,x1-2,y2+2,RGBtoWord(0,0,0)) ' left double black line
Drawline(x2+1,y1,x2+1,y2+1,RGBtoWord(128,128,128)) ' right gray line
Drawline(x2+2,y1-1,x2+2,y2+2,RGBtoWord(128,128,128)) ' right gray line double
Drawline(x1,y2+1,x2+1,y2+1,RGBtoWord(128,128,128)) ' gray line at bottom
Drawline(x1-1,y2+2,x2+2,y2+2,RGBtoWord(128,128,128)) ' gray line at bottom dual line
Drawline(x2-15,y1,x2,y2,RGBtoWord(240,240,240)) ' slider background vertical
Drawline(x1,y2-15,x2,y2,RGBtoWord(240,240,240)) ' slider background horizontal
DrawBMPRam(1,x2-15,y1) ' draw file 1 up arrow
DrawBMPRam(2,x2-15,y2-31) ' draw file 2 down arrow
DrawBMPRam(3,x2-15,y2-15) ' draw file 3 corner gray
DrawBMPRam(4,x1,y2-15) ' draw file 4 left gray
DrawBMPRam(5,x2-31,y2-15) ' draw file 5 right gray
DrawBMPRam(6,x2-15,y1+16+(y2-y1-80)*h/255) ' draw file 6 vertical slider
DrawBMPRam(7,x1+16+(x2-x1-80)*w/255,y2-15) ' draw file 7 horizontal slider
Wow, you keep blowing me away with your progress. I feel like I've been stuck in reverse for a couple weeks now * and will be for at least another month*.
I was able to confirm my display is faulty. I returned to my WAY OLD hardware to run some tests and sure enough, it seems as if the /CS line is not properly gating the /WR line. Also, some bits in some registers do not work properly. My new displays are on their way from Hong Kong with a 4-6 week ship time. *My wife rolls her eyes whenever I bring up priority shipping, lol*
I really, really like the idea of skinning things. Have a text based, *.skn (or whatever you want to call it) that contains the names of the bmps attached to the elements. You could also encode the background color and text color in the file to make it super-simple to skin. Honestly the more I think about it, there's all KINDS of things we could do with skins. Which brings me to an interesting thought:
There are several different screens and controllers to look at as potential "ports" BUT... The functionality depends on the controller and some have nicer features than others. Your ILI offers resize while my SSD does not. I do like the SSD1289's writeMask 1 and 2. Once I verify operation on my new displays, this method of masking should allow us to "color-mask" in REAL-TIME! The idea is this, with a black background *all 0's* you set the write mask register to the inverse of the color you want. Then draw your Black and White image and the White will appear the color set! It works on blue *WriteMask2*, and I suspect the faulty screen is why red-green don't work properly. *writeMask1* There is also COMPARE settings that I'm not able to decipher the operation of yet and can't trust the results of testing. *My guess is you set the compare registers to a value, and then 3 other bits control the logical function of the compare? Maybe we can use this to make the black part of the image not draw to screen but increment pointer???? at least that's what I hope!
CPR [5:0], CPG [5:0] CPB [5:0] :
Set the value for the compare register, of which the data is read out from the GDDRAM or data is written to the GDDRAM by the microcomputer are compared...
...CPR [5:0] compares the pins RR[5:0], and CPG [5:0] compares the pins GG[5:0], and CPB [5:0] compares the pins BB[5:0].
Refer to section 14 interface mapping for writing methods in RGB data.
There is a mention of LG [2:0] ="000" in external mode, but no other explanation of SETTING LG?
*edit*
Just found the LG settings in Entry Mode(R$11) IB [0:2]:
"Write data to the GDDRAM after comparing the write data written to the GDDRAM by the microcomputer with the values in the compare registers (CPR [5:0], CPG [5:0] CPB [5:0]) and performing a logical and arithmetic operation on them."
Of course they are wider (D'oh) so they won't fit side by side on the dual display board.
However, I'm having problems getting the display to work. Below is some code. There is a line in the con section
ILI9325 = true
SSD1289 = false
and so set which one you are using and I think there are 3-4 places in the code that are different.
I can get it to read off the SD card and display the background correctly. However, the picture then fades away over about 4 second - various colors in the picture spread out and take over the screen until there are just a couple of large squares of color. Interestingly though, if I hit the reset button, while the prop is restarting, all the correct picture plus icons are there. So for a brief moment the display is correct and an awful lot of things would have to have worked to get that picture-plus-icons there.
In the C code, /CS is kept high once all the data has been sent out. I don't think that is the problem though because when it is waiting for touch input it is in a different group and /CS is high then.
Also - I picked up these 3.2" displays for $20 but suddenly they are now $50. Hmm.
Oh no, the problem with the display sounds like my "screen freak" event. If you search through the code and re-enable "FixGlitch" it should start working. I was hoping my display was the issue but I guess I'll have to go back to the drawing board. I noticed my supplier changed prices so I ordered from here : http://cgi.ebay.com/ws/eBayISAPI.dll?ViewItem&item=180871283713&ssPageName=ADME:X:BOCOR:US:1123
Asking price is $14 a piece, not too shabby.
I will go through and try to figure out what's going on. I doubt it will help but here's the timings I used to run:
DAT 'init Display driver
'designed for optimized hardware, pull-up resistors on RS and WR pins.
org 0
entry mov outa, PinsInit 'set pins to inital state
mov dira, DirsEnabled 'enables p0 - p15
mov bufferaddress, par 'store par in bufferaddress, par is screen buffer address at startup
'mov dira, DirsDisabled 'disables p0 - p15 if necessary
wrlong zero, bufferaddress 'set par to zero to confirm load done
'get write command from buffer, and check if it's 0
Get rdlong LCD_Data, bufferaddress 'get long from main memory and put it in ldc command, msW = LCD cmd, LSw = LCD data
cmp Lcd_Data, #0 wz 'and check if it's 0
if_z jmp #get 'if it is, try again.
'if not, prepare transfer
'mov dira, DirsEnabled 'set pins if necessary
mov LCD_cmd, LCD_Data 'copy lcd command to lcd data
shr LCD_cmd, #16 'move lcd command 16 bit to the right if not gddr write
' cmp LCD_cmd, #22 wz 'check to see if this is a gddr write
'if_nz jmp WRcmd
'optimized write to display, for gddr transfer
'enables p0 - p15
' mov pntr, #$1 'set wait period
':wait0 djnz pntr, :wait0 'and wait
WrCmd mov outa, LCD_Cmd 'place LCD_Cmd on write bus
' mov pntr, #$1 'set wait period
':wait0 djnz pntr, :wait0 'and wait
add zero, #0 wz 'prime WZ flag
muxz dira, RSpin 'make RS pin low by enabling its DIR REG
' mov pntr, #$1 'set wait period
':wait1 djnz pntr, :wait1 'and wait
muxz dira, WritePin 'make Write Pin low by enabling its DIR REG
and LCD_Data, lowWordMask 'mask off High word of Lcd_Data
' mov pntr, #1 'set wait period
':wait2 djnz pntr, :wait2 'and wait
muxnz dira, WritePin 'make Write Pin high by disabling its DIR REG
mov pntr, #$1 'set wait period
:wait3 djnz pntr, :wait3 'and wait
muxnz dira, RSpin 'make RS pin high by disabling its DIR REG
mov pntr, #$1 'set wait period
:wait4 djnz pntr, :wait4 'and wait
:WrDataPortion
mov outa, LCD_Data 'place LCD_Data on write bus
wrlong zero, bufferaddress 'wz
mov pntr, #$1 'set wait period
:wait5 djnz pntr, :wait5 'and wait
add zero, #0 wz 'prime WZ flag
muxz dira, WritePin 'make Write Pin low by enabling its DIR REG
'rdlong LCD_Data, bufferaddress 'get long from main memory and put it in ldc command, msW = LCD cmd, LSw = LCD data
mov pntr, #$2 'set wait period
:wait6 djnz pntr, :wait6 'and wait
muxnz dira, WritePin 'make Write Pin high by disabling its DIR REG
mov pntr, #$1 'set wait period
:wait7 djnz pntr, :wait7 'and wait
'mov dira, DirsDisabled 'disables p0 - p15
jmp #Get 'do it all again
I think this one might require some lateral thinking. On my hour drive to work this evening and then back again I let my mind wander a bit and try to think of things that would cause a picture to slowly fade away like a Cheshire cat. And then I did some free association trying to work out why the picture was there when you hit the propeller reset button?
I think my creative brain hemisphere came up with the answer, but it required some input from the logical hemisphere as well. What happens when you hit reset? One thing is that all the propeller pins go to HiZ.
I don't have a full answer yet, but I do have some code that works:
PUB Main | touchtest ' debug value
start_ram ' start the external ram / display driver in a cog
Change161(0) ' reset all the 161 counters to zero
SelectMemGroup ' select group1
Start_ILI9325 ' start the display. Don't clear screen as this uses ram commands and these might not be working yet
Text(string("SD")) ' string to send, uses internal prop font so can see something if the SD fails to mount
SetOrientation(true) ' in portrait (true) or landscape mode (false), must be before clearscreen otherwise don't know screensize
fat.fatEngineStart( _dopin, _clkpin, _dipin, _cspin, _wppin, _cdpin, _rtcres1, _rtcres2, _rtcres3)
fat.mountPartition(0) ' mount the sd card
Clearscreen(RGBtoWord(0,0,0)) ' clear screen to black - uses the display driver above
RamErase ' erase ram chip fat
LoadDesktop ' a image 240x320 for the desktop. Load first as there is a ramclear routine that does not delete the first entry
DrawDesktop
dira := 0 ' HiZ for debugging the SSD1289
repeat
LoadIcons ' load the mask and icons onto the screen
ILISetcursor(0,0) ' cursor for debugging to 0,0
SelectSPIGroup ' selects this group and starts the cog
Touchscreen ' wait for input
Patch that into the code I posted above.
Picture attached is the proof. Put the dira := 0 any further down and it fails.
Now my theory is that there is a short happening, and that the power supply is collapsing causing the display to fade out. Why a short would happen I do not know. It is as if "input" pins on that 40 pin header are somehow becoming "output" pins and fighting against the propeller pins.
I do not know which pins yet, but I guess one can go through them one at a time in code and make them outputs and find out.
The picture has been rock solid now for 20 minutes so that is proof that the setup and driver code is working correctly.
Can you replicate this?
Addit: $13.89 for your display? Wow, that is good value.
One thing I have noticed is the 3.2" display is not as good looking at an oblique angle. I don't think it is as true a "TFT" lcd as the smaller ones. ebay helpfully suggested some more 3.2" TFT touchscreens and some of those say that they have the "standard" 40 pin header. It is encouraging there are multiple suppliers and this 40 pin header is a standard. We could try some different displays and compare them. The code above just needs a couple of lines changed to completely change the display and that will be very handy for swapping in different sized displays. Futurlec sell the 20x2 socket which is also very useful. Once we get these SSD1289 displays working it means we are not so dependent on one supplier.
Very interesting work doc! I will need to think about it a bit more but it seems my results may have varied a bit? With my testing, I noticed the picture to still be on screen, even after the screen faded. *on reset like you say* I stepped through the code and found that the oscillator was being turned off it seems. From what I can tell, this has SOMETHING to do with HubToRam. I'm still confused as to what is going on, but I will do some more tests. I do know that this will "fix" the display after it glitches:
PUB FixGlitch
ILIcmd($0000,$0001) 'Turn Oscillator on POR-$0000
ILIcmd($0001,$6B3F) 'Driver output control, *landscape?? $6B3F* From code??2B3F? POR-$433F
ILIcmd($0002,$0600) 'LCD drive AC control POR-$0400
ILIcmd($0010,$0000) 'Sleep Mode sleep mode off POR-$0001
ILIcmd($0011,$6070) 'Entry Mode, *landscape? $4030* POR-$6830
ILIcmd($0007,$0033) 'Display Control POR-$0000 ''Used 0033
ILIcmd($0023,$0000) 'RAM write data mask (1) POR-$0000
ILIcmd($0024,$0000) 'RAM write data mask (2) POR-$0000
ChangeOrientation(orientation)
I hope we are having the same issues but on the other hand I still think the display is bad. Now that you have the SSD could you try this?
PUB TestMask(timeout)
Clearscreen(RGBtoWord(0,0,0)) ' clear screen to black - uses the display driver above
FixGlitch
' in portrait (true) or landscape mode (false), must be before clearscreen otherwise don't know screensize
Clearscreen(0) ' clear screen to black - uses the display driver above
''draw Red 'draws yellow?
propfont_string(String("RED"))
Clearscreen(0) 'Prepeare screen for mask
ILIcmd($23,$00FC) 'setMask Green0-5=1 Mask off green
ILIcmd($24,$00FC) 'setMask Blue0-5=1 Mask off blue
Clearscreen($FFFE) ' clear screen to black - use mask
FixGlitch 'fix my display
pause1ms(5_000) 'wait for a bit
propfont_string(String("green"))
Clearscreen(0) 'Prepeare screen for mask
''draw Green 'draws nothing
ILIcmd(0023,$FC00) 'setMask Blue0-5=1 Mask off blue '
ILIcmd(0024,$00fc) 'setMask RED0-5=1 Mask off RED '
Clearscreen($FFFE) ' clear screen to black - use mask
FixGlitch 'fix my display
'update orientation
pause1ms(5_000) 'wait for a bit
propfont_string(String("blue"))
Clearscreen(0) 'Prepeare screen for mask
''draw Blue -OK ' draws blue, works??
ILIcmd(0023,$fcFc) 'setMask Green0-5=1, Red0-5=1 Mask off green and red '
ILIcmd(0024,$0000) 'setMask Blue0-5=0 '
Clearscreen($FFFE) ' clear screen to white - usemask
FixGlitch 'reset display
'update orientation
pause1ms(5_000) 'wait a bit
deadend
My fingers are crossed this draws RED, GREEN, then BLUE screens.. Blue works for me. Red and Green are controlled by Red?
re displays, I do find my screen a bit dull. It would be nice to try some other displays, although I believe setting the gamma and power control properly * I can't quite figure it out* it might look better.
*edit*
Tried plugging in your code and sometimes it will load the icons, most of the time it will fail somewhere after mask. From my previous testing it seems to fail in HubToRam as I said. It seems like it only fails in LoadBMP but I'm not sure why.
I see in the C code that after many routines, /CS goes high. I don't think the ILI9325 code does that. My assumption with the ILI9325 driver is that even if /CS is low, if /RD and /WR and /RS are all high then no data will go to and from the display. But that may not be true with the SSD1289 display. Maybe /CS needs to be high to truly disable the display. It might explain why glitches happen with HubtoRam because that is going to be toggling lots of the data lines, and possibly sending data somehow to the display.
/CS at the moment is controlled by the group select but maybe it needs to be controlled by a latch?
There is a way we could test that. /CS on our board goes low when that group is selected, so if we deselect all groups between each data transfer that will put /CS high. However, on the board averagejoe and I have, we can't set /CS high when doing hub to ram transfers. We may need a revision on the board design to work around this.
I've been thinking about trying to strobe /CS after the last transfer to screen. Hopefully this will "deselect" the display? I am just postulating here but I think it's the FIRST transfer to RAM after writing to screen. I have not had a chance to check this yet. If not and /CS must be held high then a fix will be in order.. It may be time to think about a "daughter-board" to hold a latch and another `32? I'm hoping it won't come to that and it can be fixed in software.
*edit*
I'm pulling out the trusty HW_v3.4 to verify opperation of the screen. I will need to make some revisions to control /CS. Hopefully I can test a couple things out and see if it's the "last display / load RAM address" /CS
I'm going to try soldering up one of the dual display boards (they should be in the air to the USA by now). Because each display needs to be selected individually these have a latch on the /CS so this could be an unintended bonus for this board.
I am eagerly awaiting the new boards. I'm short on sockets now!
I was able to coax the XMMC - SRAM driver to life. Now I need to test the actual performance. I'm uploading files to the webpage this evening. I've been looking at your latest code and think it needs to be broken into objects. Almost 2500 lines of code! Breaking things up a bit will make it easier to port to C IMO. Still much work to be done.
Could you try the colormask when you get a chance? I really want to make sure this works before I invest more time coding for this. I am fairly sure there are a few broken registers in my display.
Also, I have been unable to replicate the glitch on HW_3.4b.
The bad news is the SSD1289. I have tried all sorts of code. I did get some text on the display at one stage, but it was split into two lines and I have seen that error before and it is due to not setting up the draw area correctly.
The problem is the /CS line. We have run out of free propeller pins and by sending this through a latch it gets complicated because the pins controlling the latch output are also pins shared by the data bus. Because of the lack of spare pins, there never is total control over all the pins in parallel. So a simple thing like replicating this
PRI LatchLow(pin)
LatchStart
Latch &= !(1 << pin) ' set this pin low
LatchSend
PRI LatchStart ' common routine to above 2 routines
Change137(3) ' group 3 is the latch group
DIRA |= %00000000_00000000_00000000_11111111 ' P0-P7 are outputs
PRI LatchSend ' common routine to both above routines
OUTA := Latch ' send out
DIRA |= %00000000_01000000_00000000_00000000 ' enable pin 22 as an output
OUTA |= %00000000_01000000_00000000_00000000 ' set P22 high - this latches the data into the latch
PUB Change137(group)
docmd("Y",group,0,0) ' change the current group
with the change137 going off in pasm and making the propeller pins HiZ (high with pullups) as the group changes. So I don't know if that upsets the display or not.
Or maybe we just have not got the startup code right.
There could be a software solution. But take a look at the photo - if you have two of the smaller displays you want them fairly close side by side. And the wider 3.2" display is too wide for the spacing on this board so you can only fit one. This means if you want a dual SSD1289 the spacing needs to be wider. And if the spacing is wider then the ILI9325 boards are further apart. Which means if we want two SSD9325's side by side we need a different board.
And if there is a different board for the dual SSD1289 displays then it might be better to think of a better design.
What I am thinking of is that if we need one more pin to do /CS then the only place to get a spare pin is P22 which is controlling the 137. One way you could do this is instead of selecting the group with the 137, use a MCP23008 on the I2C lines. Or even the 16 I/O version. That gives more I/O pins and it means the 374 latch and the 137 could be combined into one. But the main advantage would be that other propeller pins are not going up and down during latch outputs. (eg P0-P2). I think it will make the code simpler.
Speed will be an issue. The C code for the SSD1289 toggles the /CS line every time data goes out. I don't know if that is necessary and it may not be given I got a full screen picture at one stage and only toggled /CS at the beginning and the end of the entire picture, but if it is necessary, then /CS will be P22. The slower bit can be which screen to select and that can go through the MCP23008 and then some logic gates to select which screen.
This is very strange with the SSD. I have used the "workaround" and it doesn't seem to slow things down that much. It's still a pain and I can't quite understand why. I've looked for other fourms and posts relating to this controller and have not been able to find anything. I keep hoping to figure something out but I'm at a loss. I will have to order a couple ILI's in the future.
Note, the screen draws correctly with the ILI code. There's just some error, most likely in the handoff between groups *IMO* I need to get my propalyzer board finished. I'm stuck trying to figure out how to plug everything together but think I'm close to a solution. I will need to pull my rack-mount out and start strippin old hardware. Maybe Jazzed will be kind enough to test some transistions in the load-desktop. I'm mostly interested in the /CS and /WR lines, although the /RS and Data would be interesting to know. Maybe I'll finish up the rebuild next week.
I'm not too worried about displays being side by side since my dual-screen board will be going in the rackmount with displays in landscape. Ribbon cables are handy for this, but the pin-header must go on the back of the board. Oh well, I'm sure we'll figure something out.
Keep up the good work and let's not worry too much about the SSD right now until I get all the information.
I do have many things working with the SSD - I've had a picture on the screen and some text. I am very convinced now that it is just the fact you have to deselect the display /CS most of the time.
Take a look at this design. It is backwards compatible with the ILI9325. The display sockets are further apart so two SSD1289 displays fit side by side (and almost cover the whole board so doing this in surface mount is not so important if the display almost fills the board anyway). Replacing the 374 and 137 with a MCP23008 saves a chip. The design is simpler to code and simpler to understand. I also took the opportunity to move the audio closer to the propeller chip and as far from the max3232 as possible as I was worried about the stepup driver in the max3232 causing noise. I also grouped all the max3232 and reset components together because if you use the propplug you can leave that whole group out. And there are pads to bring out the group pins and P0-P15 so you can expand at least another 20 propeller pins to another board.
This is a very interesting idea, and just might solve the problem? I can see where it could simplify code and should still run pretty fast.
I REALLY like the audio layout, although I'm wondering about the "backwards compatibility" since the ILI displays will be fairly far apart?
A 28-bit logic analyzer would tell volumes about what's going on with the current display, and it's simple to build. Connecting things is a bit more difficult since the headers are on the 4 corners. I actually only need 0-22 and /CS /WR and /RS. See the theory I have is that /RS and /WR are low before /CS goes high. OR the timing of the last display write is not fully completing before the tri-state. But I'll need to burn that bridge when I get there.
The other thing I have been thinking about is this. If we stripped the touchscreen functions, how could we make the old board useful to vga, tv, keyboard and mice guys?
*edit*
Okay, I just finished looking at the board. The only suggestion I have is this. There needs to be more room around the gnd screw for the TO-220. I ran into a problem shorting this trace before. There's nothing around it, so I would leave plenty of space around this.
I put a square pad on the underside so there are no tracks near the switcher bolt hole.
The autorouter was completing very easily so I thought I might add some SPI ports. So there are now three SPI ports and one I2C port so that means you can add an external A to D converter etc.
I'm just postulating at the aesthetics. Two 2.4" displays might look okay with the additional space but we won't know till we see.
The "stripping" of functions was a reference to the presentation thread. I was thinking of integrating something like Steve's HD shield. I'm sure it would not be too difficult. Not top priority by any means, just something to ponder.
I don't have any ILI displays right now. Need to order some other parts as well. I'm stuck waiting until I move now since I've reached the cutoff for ordering more stuff.
I'm stuck waiting until I move now since I've reached the cutoff for ordering more stuff.
Ah. We can fix that. Ok, well I just put an order through for you for the parts below - if that goes off now then it will arrive about the same time as the boards and I can post both to you at the same time.
The catch is... you might have to find some obex driver code - eg for the MCP2302 A to D as I managed to squeeze that onto the board as well. (addit http://obex.parallax.com/objects/625/) So we can play around with some digital audio manipulation. I'm thinking fun things like a digital delay line. I wonder if we can do 16x16 bit multiplies fast enough?
I've sent off the boards for the MCP23008 driver. The space saved by removing one chip is the space I was able to use for the A to D converter so that makes this a better design now. Still, the one that you will get in a few days is very useful if you have a 2.4" ILI9325 display.
Re parts. Well that's good news then! I should have my new address in the next week.
The cache driver for the old board using xmm-single is working quite well. I should be able to use xmmc since Steve has it working. I'm playing the NOOB card here. Not too worried since part of my need for xmm is variable AND code space. I have also been working on the "glitch fix" since it's a viable workaround for now.. With 2 SSD's on their way, ordering 2 ILI's will have to wait till I get my deposit back from this apartment I will post example of workaround when it's fully working and done. As a note, in the XMM driver :
load161pasm ' uses vmaddr
mov count, line_size ' make a copy of line_size AND.
mov ptr, hubaddr ' hubaddr = hub page address
'or outa,maskP0P20 ' set P0-P20 high - unnecessary - jsd
*edit*
In testing of latest code, using glitchfix I have most things working
SDtoRam,
SDBMPtoRam,
MergeBackgroundIconRam,
'all get this at the end of method:
if SSD1289
fixGlitch
PUB FixGlitch
ILIcmd($0000,$0001) 'Turn Oscillator on POR-$0000
ILIcmd($0001,$6B3F) 'Driver output control, *landscape?? $6B3F* From code??2B3F? POR-$433F
ILIcmd($0002,$0600) 'LCD drive AC control POR-$0400
ILIcmd($0010,$0000) 'Sleep Mode sleep mode off POR-$0001
ILIcmd($0011,$6070) 'Entry Mode, *landscape? $4030* POR-$6830
ILIcmd($0007,$0033) 'Display Control POR-$0000 ''Used 0033
ILIcmd($0023,$0000) 'RAM write data mask (1) POR-$0000
ILIcmd($0024,$0000) 'RAM write data mask (2) POR-$0000
ChangeOrientation(orientation)
There are still things not working, since I'm missing files on the sd card. Also, buttons demo has broken radio buttons? 2 are selected, 1 is not. Just noticed this.
There are still things not working, since I'm missing files on the sd card. Also, buttons demo has broken radio buttons? 2 are selected, 1 is not. Just noticed this.
Yes I noticed that too. My dodgy code - sorry.
I'm going to recode that with a white background. Needs a frame so the code only switches radio buttons within a frame.
I see you have got some cool stuff working over on the GCC forum - well done!
Comments
I've been stuck on the display end for so long, I forgot all the other cool things that need to be worked on. I need to test a "master-slave" config with 2 boards. Since I have 2 fully built boards and only 1 *QUESTIONABLE* display, I was going to use the second as an expansion board. I've also started to build a multi-prop audio generation circuit on the bread board. The plan is to use 4 props, 8 - R-C DAC's, and a small mixer to allow multi-voice SidCog and TINYsynth patches. More experimenting later tonight.
Johannes and Doc, keep up the great work.
*edited*
Well, I ordered 2 new screens from hong-kong @ $13.00 a piece! Too bad it will be at least a month before they get here!
I thought you had something working. Follow up?
@averagejoe, I have done a massive rewrite of the code so that the sram acts as a better ramdisk. All the fonts, text files, bitmaps and music files are all treated in the same way, so there are no 'special' areas of the ramdisk. Probably the most obvious advantage is that more than one font can be stored - and I wanted that so I could put time signatures and other notation in the music font. So you can have a basic music font with notes, and then another font with fancy things like portamento.
This is also in preparation for adding .wav files for sampling.
I see Ahle2 has updated the synth - so will need to update that as well.
And glue the music font to the synth player so it can play some music. Should be a simple 256 byte lookup table and maybe some code to handle sharps and flats.
Addit: new files May21 - added in Kye's .wav player. There are a number of online .mp3 to .wav converters (plus many programs can do this) so it is very easy to have a library of music on the SD card. Need to add a selection box so can select which .wav file to play.
*edited*
I have the chiptune player functioning. Not very pretty right now. But it opens chiptunes, and plays! I have not figured out the directory yet, so I have a copy in the root AND in "tunes" folder. I need to figure how to tack on the directory in file opens. Here's what I have working so far. Not much of a front end, but it works!
[code]
'' ILI9325 driver using the Touchblade161 design for faster ram to display transfers with a hc137 for group select
'' Average Joe and James Moxham 2012
' notes re groupings of pins
' To shut down a group, deselect it by making P22 low. This makes Y0-Y7 from the 137 all high
' To start a group, the initial state is P0-P20 all HiZ and P22 HiZ (P21 and 23 are audio and there is less audio noise if the pins are not next to each other)
' Passing to a cog, P0-P20 and P22 HiZ ie DIRA all as 0 fo these pins. This is to avoid contention between spin and the cog code
' A routine in Spin or Pasm that changes the 137 will also return P0-P20 and P22 as HiZ.
' In general terms, H is inactive and L is active. So changing a pin from HiZ to H will have no effect as pins all have pullups.
' Any handover to cog code must be done in this HiZ state and all pins in a group need to work with this
CON
''LCD REGISTERS
REG_OSCILLATOR = $0000 ''Oscillator (R00h) (POR = 0000h)
REG_DRIVEROUTPUTCONTROL = $0001 ''Driver Output Control (R01h) (POR = 2B3Fh)
REG_LCDDRIVINGWAVFORM = $0002 ''LCD-Driving-Waveform Control (R02h) (POR = 0000h)
REG_POWERCONTROL1 = $0003 '' (R03H)
REG_DISPLAYCONTROL = $0007 ''Display Control (R07h) (POR = 0000h)
REG_FRAMECYCLECONTROL = $000B ''Frame Cycle Control (R0Bh) (POR = 5308h)D308 BY DATASHEET
REG_POWERCONTROL2 = $000C '' (R0Ch) (POR = 0004)
REG_POWERCONTROL3 = $000D ''
REG_POWERCONTROL4 = $000E ''
REG_GATESCANPOSITION = $000F ''Gate Scan Position (R0Fh) (POR = 0000h)
REG_SLEEPMODE = $0010 ''Sleep mode (R10h) (POR = 0001h)
REG_ENTRYMODE = $0011 ''Entry Mode (R11h) (POR = 6830h)
REG_HPORCH = $0016 '' (R16h) (POR = EF1Ch)
REG_VPORCH = $0017 '' (R17h) (POR = 0003h)
REG_POWERCONTROL5 = $001E ''
REG_RAMDATAWRITE = $0022 ''
REG_RAMWRITEDATAMASK1 = $0023 '' (R23h) (POR = 0000h)
REG_RAMWRITEDATAMASK2 = $0024 '' (R24h) (POR = 0000h)
REG_VERTICALSCROLCONTROL1 = $0041 '' (R41h) (POR = 0000h)
REG_VERTICALSCROLCONTROL2 = $0042 '' (R42h) (POR = 0000h)
REG_HORIZONTALRAMADDRESSPOS = $0044 '' (R44h) (POR = EF00h)
REG_VERTICALRAMADDRESSSTART = $0045 '' (R45h) (POR = 0000h)
REG_VERTICALRAMADDRESSEND = $0046 '' (R46h) (POR = 013Fh)
REG_FIRSTWINDOWSTART = $0048 '' (R48h) (POR = 0000h)
REG_FIRSTWINDOWEND = $0049 '' (R49h) (POR = 013Fh)
REG_SECONDWINDOWSTART = $004A '' (R4Ah) (POR = 0000h)
REG_SECONDWINDOWEND = $004B '' (R4Bh) (POR = 013Fh)
REG_SETGDDRXADDRESSCOUNTER = $004E '' (R4Eh) (POR = 0000h)
REG_SETGDDRYADDRESSCOUNTER = $004F ''
_clkmode = xtal1 + pll16x ' use crystal x 16
_xinfreq = 5_000_000
_1ms = 1_000_000 / 1_000 ' Divisor for 1 ms
' 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.
' touchscreen pins
Touch_Clock = 0
'Touch_ChipSelect = done with a 137 select
Touch_DataIn = 1
Touch_DataOut = 2
' audio pins
_leftChannelVolume = 32
_rightChannelVolume = 32
_leftChannelAudioPin = 21 ' -1 ifnot installed.
_rightChannelAudioPin = 23 ' -1 ifnot installed.
' general purpose constants
Margin = 4 ' some characters have xoffset of -1 or -2 eg v, and won't display on the extreme left edge
' external ram size of data blocks
Ram_Fontsize = 65536 ' 128k for fonts (largest is around 108k)
Ram_MaskSize = (64*64*2)/2 ' 64x64 (59x60) bytes divide by 2 for words
Ram_IconSize = (64*64*2)/2 ' 2 bytes per pixel converts from .bmp in the routine
Ram_TextSize = 32768 ' 64k for text
' external ram locations, if adding more, follow the pattern
FontTable = 0 ' address for ram chip (2 bytes per address)
Mask = FontTable + Ram_Fontsize ' address for raw mask image
Icon = Mask + Ram_MaskSize ' address for icon image
Text = Icon + Ram_IconSize ' text buffer for word processing etc
RamDiskStart = Text + Ram_TextSize ' ramdisk with searchable bitmap files
OBJ
'fat: "SD3.01_FATEngine.spin" ' thanks to Kye - sd card
fat: "SD-MMC_FATEngine.spin" ' Kye's latest sd driver and no RTC
spi: "SPI_ASM" ' thanks to Beau - spi driver for touch screen
str: "ASCII0_STREngine.spin" ' thanks to Kye - string routines
' term: "FullDuplexSerial.spin" ' Thanks to Chip - for debugging if display not working
' synth : "tinysynth" ' ' Thanks Ahle2 http://forums.parallax.com/showthread.php?139724-TinySynth-!
VAR
long orientation
long curx, cury
long clkcycles ' for the delay routine
word ScreenWidth ' either 240 in portrait or 320 in landscape
word ScreenHeight ' 320 in portrait or 240 in landscape
byte LineOfText1[20] ' general purpose string buffer
byte LineOfText2[20] ' general purpose string buffer
byte FontHeight ' size of the current loaded font (pixels = fontsize *.75)
word BackFontColor ' the background color in RRRRRGGG GGGBBBBB format
long RamDiskPointer ' pointer to current ramdisk location
long RamDiskFiles ' number of files in the ramdisk
'byte sdbuffer[512] ' can't put these here, writes to sdbuffer sometimes back overwrite variables above this eg fontheight!!
'byte buffer2[512] ' so put these in a dat section
'word rambuffer[256]
long DecRate ' synth settings
long SusLevel
long PWinit
long pwmRate
long Filter
long Portamento
''*****************Start chiptune Player CON here***********************
CON
ENABLE_PAL = false
''****************Start chiptune Player OBJ here************************
OBJ
' WaveZoom : "WaveZoomer"
' OSC : "Oscilloscope"
SID : "SIDcog"
AY : "AYcog"
' SD : "fsrw"
''***************Start chiptune Player VAR here
VAR
byte buffer[25]
byte filename[18]
byte playing
byte chipTuneType
byte prevChipTuneType
byte ffw
word playRate
long wait
long fileNumber
long nrOfTunes
long scrollSpeed
long frameCounter
long scrollCounter
long textPointer
long AY_register
PUB Main | i, oldStatus, fin,yval, xval
start_ram ' start the external ram / display driver in a cog
Change161(0) ' reset all the 161 counters to zero
SelectMemGroup ' select group1
Start_ILI9325 ' start the display. Don't clear screen as this uses ram commands and these might not be working yet
SetOrientation(true) ' in portrait (true) or landscape mode (false), must be before clearscreen otherwise don't know screensize
'pause1ms(5000)
Clearscreen(RGBtoWord(0,0,0)) ' clear screen to black - uses the display driver above
propfont_string(String("Mount SD"))
fat.fatEngineStart( _dopin, _clkpin, _dipin, _cspin, _wppin, _cdpin, _rtcres1, _rtcres2, _rtcres3)
fat.mountPartition(0) ' mount the sd card
propfont_string(String("Mounted"))
ClearRamdisk ' clear ram disk for pictures not on the desktop
''*Note* Load fonts and bmp's here!
LoadBMPtoRamdisk(string("Trans.bmp"))
sid.start(_rightChannelAudioPin, _leftChannelAudioPin)
propfont_out(".")
crlf
getNrOfTunes
'pause1ms(5000)
Clearscreen(RGBtoWord(0,0,0)) ' clear screen to black - uses the display driver above
curx := 0
cury := 0
propfont_string(@message4)
'propfont_out("!")
frameCounter := 0
fileNumber := 0
chipTuneType := 1
playRate := 50
scrollSpeed := 2
playing := false
ffw := false
wait := cnt
DrawBMP(string("Trans.bmp"),0,280)
openNextFile
'Main loop
repeat
' Wait between register updates
if (cnt - wait) > 0
if ffw
wait := cnt + (clkfreq/(playRate<<2))
else
wait := cnt + (clkfreq/playRate)
' Update the PSG
if playing
case chipTuneType
1: updateSID
2: updateAY
' Check for a change in tv status
' if oldStatus <> tv_status
' oldStatus := tv_status
' ' Check for vbl
' if tv_status == 2
' scroll
'
' ' Handle user interactions
' if input.getLastState <> input.getCurrentState
SelectSPIGroup
yval := TouchYPercent ' decode yval 0-100%
xval := TouchXPercent ' decode xval 0-100%
if (xval <> 255) and (yval <> 255)
'SelectMemGroup
'hex(xval,2) ' if display any debug messages need to restart the SPI cog as it has been stopped by the debug display
'hex(yval,2)
if (yval > 89)
SelectMemGroup
' Handle previous tune
if (xval < 19)
handleMuting
if fileNumber == 0
fileNumber := nrOfTunes + 1
fileNumber--
openNextFile
' Handle pausing
if (xval > 37 and xval < 70)
playing := not playing
handleMuting
ifnot playing
setScrollMessage(@message2)
else
ffw := false
setScrollMessage(@songName)
' Handle next tune
if (xval > 79 and xval < 99)
handleMuting
if fileNumber == nrOfTunes
fileNumber := -1
fileNumber++
openNextFile
' ' Handle pausing
' if input.getLastState&input#START
' playing := not playing
' handleMuting
'
' ifnot playing
' setScrollMessage(@message2 - @message)
'
' else
' ffw := false
' setScrollMessage(@songName - @message)
'
' scrollSpeed := 8
'
' ' Handle fast forward
' if playing > 0 and input.getLastState&input#A
' ffw := not ffw
'
' if ffw
' setScrollMessage(@message3 - @message)
'
' else
' setScrollMessage(@songName - @message)
'
' scrollSpeed := 8
'
' ' Handle next tune
' if input.getLastState&input#RIGHT
' if fileNumber == nrOfTunes
' fileNumber := -1
'
' fileNumber++
' openNextFile
'
' ' Handle previous tune
' if input.getLastState&input#LEFT
' if fileNumber == 0
' fileNumber := nrOfTunes + 1
'
' fileNumber--
' openNextFile
'
' ' Handle next page ( += 10 )
' if input.getLastState&input#DOWN
' fileNumber += 10
'
' if fileNumber > nrOfTunes
' fileNumber := 0
'
' openNextFile
'
' ' Handle previous page ( -= 10 )
' if input.getLastState&input#UP
' fileNumber -= 10
'
' if fileNumber < 0
' fileNumber := nrOfTunes
'
' openNextFile
PRI openNextFile | i, t, nameP
ffw := false
playing := true
' Iterate through files and find the file with fileNumber == X
fat.closeFile
repeat i from 0 to fileNumber
fat.listEntries("N")
nameP := fat.listname
t:=0
repeat until byte [nameP][t] == 0
byte [@filename][t]:= byte[nameP][t]
t++
byte[@filename][t] := 0
' Find out what kind of file type we are dealing with
prevChipTuneType := chipTuneType
chipTuneType := 0
fat.openFile(@filename, "R")
if fat.readData(@buffer, 4) < 0
if byte[nameP - 3] == "D" and byte[nameP - 2] == "M" and byte[nameP - 1] == "P"
chipTuneType := 1
if buffer[0] == "S" and buffer[1] == "D" and buffer[2] == "M" and buffer[3] == "P" ' SDMP
chipTuneType := 1
elseif buffer[0] == "Y" and buffer[1] == "M" and buffer[2] == "6" and buffer[3] == "!" ' YM6!
chipTuneType := 2
else
fat.closeFile
AY.stop
SID.stop
setScrollMessage(@message1)
return
' Set some standard values in case the header doesn't provide any info
playRate := 50
bytefill(@songName + 10, 32, 15)
intToString(@songName + 6, fileNumber, 5)
repeat i from 0 to 15
if filename
byte[@songName + i + 10] := filename
else
i := 16
' Init everything, read the header and start the PSG
fat.openFile(@filename, "R")
if chipTuneType == 1
readHeaderSID
initSID
elseif chipTuneType == 2
readHeaderAY
initAY
wait := cnt + 20_000_000
PRI handleMuting
case chipTuneType
1:
if playing
SID.unmute
else
SID.mute
2:
if playing
AY.unmute
else
AY.mute
PRI initSID | registers
if prevChipTuneType == 2
AY.stop
elseif prevChipTuneType == 1
SID.stop
registers := SID.start( _rightChannelAudioPin, _leftChannelAudioPin )
SID.resetRegisters
' OSC.stop
' OSC.start( display_base, registers + 28, @tv_status, @scrollSpeed )
chipTuneType := 1
' setSIDColor
PRI initAY | registers
if prevChipTuneType == 2
AY.stop
elseif prevChipTuneType == 1
SID.stop
registers := AY.start(_rightChannelAudioPin, _leftChannelAudioPin, AY_register)
AY.resetRegisters
' OSC.stop
' OSC.start( display_base, registers + 16, @tv_status, @scrollSpeed )
chipTuneType := 2
' setAYColor
PRI updateSID
if( fat.readData(@buffer, 25) ) < 0
fileNumber++
openNextFile
SID.updateRegisters(@buffer)
PRI updateAY
if( fat.readData(@buffer, 16) ) < 0
fileNumber++
openNextFile
AY.updateRegisters(@buffer)
PRI readHeaderSID | i, t
if( fat.readData(@buffer, 25) ) => 0
if buffer[0] == "S" and buffer[1] == "D" and buffer[2] == "M" and buffer[3] == "P"
playRate := buffer[4] | (buffer[5]<<8)
repeat i from 0 to 15
t := buffer[ i + 8 ]
if t
byte[ @songName + i + 10 ] := t
else
byte[ @songName + i + 10 ] := 32
PRI readHeaderAY | extra, digiDrums, i, t
if( fat.readData(@buffer, 12)) => 0 ' 00 - read "YM6!" header ' 04 - read "LeOnArD!" identifier
if( fat.readData(@buffer, 4) ) => 0 ' 0c - number of VBL frames (play rate)
if( fat.readData(@buffer, 4) ) => 0 ' 10 - attributes
if( fat.readData(@buffer, 2) ) => 0 ' 14 - number of digi drum samples
digiDrums := buffer[0] | (buffer[1]<<8)
if( fat.readData(@buffer, 4) ) => 0 ' 16 - YM2149 external frequency in Hz
AY_register := buffer[3] | (buffer[2]<<8) | (buffer[1] << 16) | (buffer[0] << 24)
if( fat.readData(@buffer, 2) ) => 0 ' 1a - player frequency in Hz
playRate := buffer[1] | (buffer[0]<<8)
if( fat.readData(@buffer, 4) ) => 0 ' 1c - frame where looping starts
if( fat.readData(@buffer, 2) ) => 0 ' 20 - extra bytes following (should be 0)
extra := buffer[0] | (buffer[1]<<8)
bytefill(@buffer, 32, 15)
if get_string ' read song name
repeat i from 0 to 14
t := buffer
if t
byte[ @songName + i + 10 ] := t
else
byte[ @songName + i + 10 ] := 32
get_string ' read author name
get_string ' read comments / software
PRI get_string | n, t
repeat n from 0 to 127
if (t := fat.readByte ) =< 0
return n
else
if n < 25
buffer[n] := t
var byte firstFile[18]
PRI getNrOfTunes | t,i
nrOfTunes := -1
fat.closefile
propfont_string(fat.changeDirectory(string("tunes")))
repeat until strcomp(@Dir2, t)
fat.listEntries("N")
t := fat.listname
'propfont_String(t)
fat.listEntries("N")
t := fat.listname
'propfont_String(t)
i := 0
repeat until byte[t] == 0
firstFile := byte[t]
' propfont_out(".")
i++
crlf
'propfont_string(string("FirstFile"))
'propfont_string(@firstFile)
t:= -1
repeat until strcomp(@firstFile, t)
fat.listEntries("N")
t := fat.listname
' propfont_String(t)
nrOfTunes++
if nrOfTunes < 1
nrOfTunes -= 2
'hex(nrOfTunes,2)
'pause1ms(5000)
PRI intToString( position, value, digits ) | i
digits += position
if value < 0
-value
byte[position++] := "-"
i := 1_000_000_000
repeat 10
if value => i
byte[position++] := value / i + "0"
value //= i
result~~
elseif result or i == 1
byte[position++] := "0"
i /= 10
repeat while position < digits
byte[position++] := " "
PUB SetScrollMessage(x)
curx :=0
cury :=127
propfont_string(x)
dat
message
byte " "
songName
byte 32, 32, 32, 32, 32, "#", 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 0
message1
byte " Invalid file ", 0
message2
byte "‣‣ Paused", 0
message3
byte " ▶▶ Fast forward ", 0
message4
byte "Propeller chip-tune player by Johannes Ahlebrand Select a tune ", 0
'zero long $80000000 T
Dir2
byte ".. ",0
PUB FixGlitch
ILIcmd($0000,$0001) 'Turn Oscillator on POR-$0000
' pause1ms(1)
' ILIcmd($0003,$A8A4) 'Power control (1) POR-$6664
' pause1ms(1)
' ILIcmd($000C,$0000) 'Power control (2) POR- ?
' pause1ms(1)
' ILIcmd($000D,$080C) 'Power control (3) POR-$0009
' pause1ms(1)
' ILIcmd($000E,$2B00) 'Power control (4) POR-$3200
' pause1ms(1)
' ILIcmd($001E,$00B0) 'Power control (5) POR-$0029
' pause1ms(1)
ILIcmd($0001,$6B3F) 'Driver output control, *landscape?? $6B3F* From code??2B3F? POR-$433F
' pause1ms(1)
ILIcmd($0002,$0600) 'LCD drive AC control POR-$0400
' pause1ms(1)
ILIcmd($0010,$0000) 'Sleep Mode sleep mode off POR-$0001
' pause1ms(1)
ILIcmd($0011,$6070) 'Entry Mode, *landscape? $4030* POR-$6830
' pause1ms(1)
ILIcmd($0007,$0033) 'Display Control POR-$0000 ''Used 0033
' pause1ms(1)
ILIcmd($0023,$0000) 'RAM write data mask (1) POR-$0000
' pause1ms(1)
ILIcmd($0024,$0000) 'RAM write data mask (2) POR-$0000
' pause1ms(1)
ChangeOrientation(orientation)
PUB Deadend
repeat
waitcnt(cnt)
' *********************************** Driver Routines *******************************
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
propfont_string(stringptr) ' print the filename
propfont_string(result) ' print the error message
repeat ' stop the program
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)
PUB TouchXPercent
result := 100 - (TouchValue & 255) ' decode xval 0-100%
PUB TouchYPercent
result := TouchValue >> 8 ' decode yval 0-100%
PUB TouchSwish | 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 TouchStart
SPI.start(3,0) ' delay,state, start clock high
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)
PUB SDToDisplay(stringptr) ' bitmap from SD to display
Clearscreen(0)
ClearRamDiskExcludeDesktop
LoadBMPtoRamdisk(stringptr)
DrawBMP(stringptr,0,0)
PUB FindBMP(stringptr) | i,rampointer,ramcounter,match,searchlen,width,height ' pass the name and the x and y location, searches for this file and if found, displays it
rampointer := RamDiskStart
searchlen := str.instr(stringptr,".") - 1 ' ignore the .bmp extension
repeat ramcounter from 1 to ramdiskfiles
docmd("T",@sdbuffer,rampointer,$36) ' get hex $36 bytes from the ramdisk = filenames
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
match := true
repeat i from 0 to searchlen - 1
if sdbuffer <> byte[stringptr]
match := false ' any characters not matching, try next one. (case must match too)
if match == true
quit ' found a match so quit looking for any more files
' get the next rampointer = $36 + width * height
rampointer += $36 + (width * height)
if match == true ' if found a match return the rampointer
result := rampointer
else
result := -1 ' no match so zero
PUB ClearRamdisk
ILISetcursor(0,0) ' cursor for debugging to 0,0
RamDiskPointer := RamDiskStart ' set the global ramdisk pointer start
RamDiskFiles := 0
PUB ClearRamdiskExcludeDesktop ' exclude the desktop which is the first 76800+36 pixels and also sets ramdiskfiles to 1, not 0
ClearRamdisk
RamDiskPointer += $36 + (240 * 320) ' leave room for the desktop
RamDiskFiles +=1 ' file counter +1
PUB DrawBMP(stringptr,x,y) | rampointer,width,height ' pass the name and the x and y location, searches for this file and if found, displays it
rampointer := FindBMP(stringptr) ' find the file in the list
if rampointer <> -1 ' if a match then draw
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
PUB TextBoxClear(x1,y1,x2,y2) ' draw the outline of the box
ClearRectangle(x1,y1,x2,y2,RGBtoWord(255,255,255))
Drawline(x1-1,y1-1,x2,y1-1,RGBtoWord(64,64,64)) ' gray outline lines - extract values from paint shop pro
Drawline(x1-2,y1-2,x2+1,y1-2,RGBtoWord(128,128,128)) ' horizontal upper light gray
Drawline(x1-1,y1-1,x1-1,y2,RGBtoWord(64,64,64)) ' vertical left gray
Drawline(x1-2,y1-2,x1-2,y2+1,RGBtoWord(128,128,128)) ' vertical left light gray
Drawline(x2+2,y1-2,x2+2,y2+2,RGBtoWord(255,255,255)) ' vertical right white
Drawline(x1-2,y2+2,x2+2,y2+2,RGBtoWord(255,255,255)) ' horizontal lower white
PUB LoadBMPtoRamdisk(stringptr) | width,height,w,i ' load and display a bitmap file
' 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
' on the ram store a 0x36 byte header - the bitmap header but put in the filename at location 0
' this becomes a rudimentary file system that can be searched to find pictures
'propfont_out("1") ' for debugging display something while load in pictures
' pause1ms(Debugtimer)
fat.openfile(stringptr,"R")
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
repeat i from 0 to 11 ' add the file name at the beginning of the header
sdbuffer := byte[stringptr]
docmd("S",@sdbuffer,RamDiskPointer,$36) ' store header to ram
i := $36 + RamDiskPointer + ((height - 1) * width) ' start + header and on the last row and work back
repeat height
fat.readdata(@sdbuffer,w) ' read in the first row
docmd("F",@sdbuffer,@rambuffer,width)
HubToRam(i,width) ' send to ram
i -= width ' subtract the width, move up the picture
FixGlitch
fat.closefile
RamDiskPointer += $36 + (width * height) ' increment the ramdiskpointer for next picture
RamDiskFiles += 1 ' increment number of entries in the ram disk
FixGlitch
PUB DrawCheckbox(x,y,Checked,Stringptr) | i ' draw a checkbox
if checked
DrawBMP(string("Checked.bmp"),x,y)
else
DrawBMP(string("Uncheckd.bmp"),x,y)
curx := x+20 ' add 20 width
cury := y
ILIPrintString(stringptr)
PUB DrawRadioBox(x,y,Checked,Stringptr)| i ' draw a radio box
if checked
DrawBMP(string("RadioChk.bmp"),x,y)
else
DrawBMP(string("RadioUnc.bmp"),x,y)
curx := x+20 ' add 20 width
cury := y
ILIPrintString(stringptr)
PUB DrawButton(x,y,Stringptr) ' draw a radio box
DrawBMP(string("butn8430.bmp"),x,y)
curx := x + (84/2)-(str.len(stringptr)*4 / 2) ' centre, avg width 4 per char 84 could be a variable width passed to this routine
cury := y + 8
ILIPrintString(stringptr)
PUB LoadFile(strpointer) | filesize,i ' reads a text file into external ram
OpenFileRead(strpointer) ' open a file
filesize := fat.filesize
i := Text ' store text here
repeat (filesize >>8 ) + 1 ' so works with 256 byte blocks
fat.readdata(@sdbuffer,256) ' get 256 bytes from sd card
docmd("S",@sdbuffer,i,128) ' send 128 words to external ram
i +=128 ' increment counter
fat.closefile
return filesize
PUB NewFont(strpointer)
ILIloadfont(strpointer)
' only call this routine with the cursor curx at zero
ClearRectangle(curx,cury,screenwidth,cury+FontHeight,BackFontColor) ' clear an area the same color as the font background
PUB ILIChar(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
jump := ReadRamLong(fonttable,(ascii << 2) + 256) ' jump location = ascii *4 plus fonttable + 256
docmd("T",@buffer2,fonttable+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
ILIcrlf ' 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
ILIcr
x := curx
if ascii ==10 ' line feed (new line)
ILIlf
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(fonttable+((jump+32)>>1),size) ' move bytes from ram out to the display
return x + xadvance
PUB ILIcrlf
ILIcr
ILIlf
PUB ILIcr ' carriage return, to left margin
curx := Margin ' some characters are -1 xoffset so won't display
PUB ILIlf ' 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 ILIPrintstring(stringptr) 'print at curx,cury
'' Print a zero-terminated string
repeat strsize(stringptr)
curx := ILIChar(byte[stringptr++],curx,cury)
PUB ILISetCursor(x,y)
curx := x
cury := y
PUB ReadRamLong(WordStart,Bytevalue) ' easier calculating
docmd("T",@result,Wordstart+Bytevalue>>1,2)
return result
PUB ILILoadFont(stringptr) | filesize,i ' read a .ifn file premade in angelcode bmfont and then the vb.net program
' ' pass file name in stringptr
i := fonttable ' store at the constant 'fonttable' location, 2 bytes per address
OpenFileRead(stringptr) ' open an image. Size is in the first long and has been rounded up to the next 256 byte block
filesize := fat.filesize ' read the file size. This file has been rounded up to the next 256 byte block
repeat (filesize >> 8) ' read in this number of 256 byte blocks
fat.readdata(@rambuffer,256) ' get 256 bytes from sd card
HubToRam(i,128) ' 128 words = 256 bytes
i += 128 ' counter for ram location
fat.closefile
docmd("T",@rambuffer,FontTable,128) ' read first 128 words =256 bytes back
FontHeight := byte[@rambuffer][251] ' font height
'hex(fontheight,8)
repeat i from 0 to 2
sdbuffer := byte[@rambuffer][35+i]
docmd("E",@sdbuffer,@buffer2,1) ' convert to 2 byte .ili format
BackFontColor := ( byte[@buffer2][1] << 8) | byte[@buffer2][0] ' correct order
'hex(backfontcolor,4)
result := filesize
PUB MergeBackgroundIcon(stringptr,x,y) | i,j,k,n ' 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
LoadBMPtoRamdisk(stringptr) ' load icon into ram
i := RamDiskStart + $36 + (y*(screenwidth+1)+x) ' location of picture pixel in ram
j := FindBMP(stringptr) + $36 ' location of icon in ram
k := FindBMP(string("mask.bmp")) + $36 ' location of mask
repeat 60
docmd("T",@sdbuffer,i,59) ' get 59 pixels from background
docmd("T",@rambuffer,j,59) ' get 59 icon pixels
docmd("T",@buffer2,k,59) ' get words for the mask
'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
docmd("X",@sdbuffer,@rambuffer,@buffer2) ' pasm version of the above 3 lines
docmd("S",@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
FixGlitch
PUB HubToRam(RamAddress,Number) ' send pixels from rambuffer hub to ram
docmd("S",@rambuffer,RamAddress,Number)
PUB RamToHub(RamAddress,Number) ' send pixels from ram to hub at rambuffer
docmd("T",@rambuffer,RamAddress,Number)
PUB RamToDisplay(RamAddress,Number) ' send pixels from ram to display
docmd("U",0,RamAddress,Number)
PUB HubToDisplay(HubAddress,Number) ' send pixels from hub to display
docmd("V",HubAddress,0,Number)
PUB Change137(group)
docmd("Y",group,0,0) ' change the current group
PUB Change161(RamAddress) ' pass ramaddress. Returns in group 1
docmd("Z",0,RamAddress,0)
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
HubToDisplay(@rambuffer,256)
PUB RGBtoWord(R,G,B) ' convert RGB to 2 byte ILI color
sdbuffer[0] := R
sdbuffer[1] := G
sdbuffer[2] := B
docmd("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
docmd("V",@rambuffer,0,256)
if remainder <> 0
docmd("V",@rambuffer,0,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 SelectMemGroup ' select group 2 for memory transfer
spi.stop ' and stop any other cogs here too if needed
OUTA |= %00000000_00011111_11111111_11111111 ' set P0-P20 high
DIRA |= %00000000_00011111_11111111_11111111 ' set P0-P20 as outputs
Change137(1)
OUTA |= %00000000_00011111_00000000_00000000 ' set P16-P20 high
DIRA |= %00000000_00011111_11111111_11111111 ' set P0-P20 as outputs
PUB SelectSPIGroup ' select group 0 for the SPI
'SsdInit
OUTA |= %00000000_00000000_00000010_00000000 ' keep p9 high so doesn't reset
DIRA |= %00000000_00000000_00000010_00000000 ' P0-P2 are used to load the 137
Change137(2)
OUTA |= %00000000_00000000_00000010_00000000 ' keep p9 high so doesn't reset
DIRA |= %00000000_00000000_00000010_00000000 ' enable these pins for output
DIRA &= %11111111_11111111_11111111_11111000 ' so no clashes with the cog using P0-P2 set these as inputs
TouchStart ' start the touchscreen cog
PUB Propfont_string(stringptr) 'print at curx,cury
repeat strsize(stringptr)
Propfont_out(byte[stringptr++])
crlf
PUB crlf
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
crlf
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 ' 137 and appropriate pins
orientation := n
'_________Start ILI_______________________
' if orientation
' ' for origin top left and portrait mode
' ILIcmd($0001,%00000001_00000000) ' $0001,$0100 set SS and SM bit 0001 0100 portrait
' ILIcmd($0003,%00010000_00110000) ' $0003,$1030 set GRAM write direction and BGR=1. $0003 $1030
' else
' ' for origin top right and landscape mode
' ILIcmd($0001,%00000000_00000000) ' set SS and SM bit 0001 0000 landscape
' ILIcmd($0003,%00010000_00111000) ' landscape $1028 = original but 1038 is correct - not mirror image
'________END ILI_____________________
'________Start SSD___________________
if orientation
' for origin top left and portrait mode
ILIcmd($0001,$2B3F) ' $0001,$0100 set SS and SM bit 0001 0100 portrait
ILIcmd($0011,$6070) '6070 ' $0003,$1030 set GRAM write direction and BGR=1. $0003 $1030
else
' for origin top right and landscape mode
ILIcmd($0001,$4B3F) ' set SS and SM bit 0001 0000 landscape
ILIcmd($0011,$6838) ' landscape $1028 = original but 1038 is correct - not mirror image
'_________END SSD____________________
PUB Start_ILI9325 ' pass orientation true = portrait, false = landscape
ILI_Reset_High
pause1ms(5)
ILI_Reset_Low
pause1ms(5)
ILI_Reset_High ' returns in group 2, ie CS is high
Change137(2) ' replaces ILI_CS high - select another group
ILI_RD_High
ILI_WR_High
pause1ms(5)
'ILI_CS_Low
SelectMemGroup ' enables correct pins for output and sets CS low
'IliInit
SsdInit
PRI SsdInit
ILIcmd($0000,$0001) 'Turn Oscillator on POR-$0000
pause1ms(1)
ILIcmd($0003,$A8A4) 'Power control (1) POR-$6664
pause1ms(1)
ILIcmd($000C,$0000) 'Power control (2) POR- ?
pause1ms(1)
ILIcmd($000D,$080C) 'Power control (3) POR-$0009
pause1ms(1)
ILIcmd($000E,$2B00) 'Power control (4) POR-$3200
pause1ms(1)
ILIcmd($001E,$00B0) 'Power control (5) POR-$0029
pause1ms(1)
ILIcmd($0001,$4B3F) 'Driver output control, *landscape?? $6B3F* POR-$433F
pause1ms(1)
ILIcmd($0002,$0600) 'LCD drive AC control POR-$0400
pause1ms(1)
ILIcmd($0010,$0000) 'Sleep Mode sleep mode off POR-$0001
pause1ms(1)
ILIcmd($0011,$6070) 'Entry Mode, *landscape? $4030* POR-$6830
pause1ms(1)
ILIcmd($0005,$0000) 'Compare Register (1) POR-$0000
pause1ms(1)
ILIcmd($0006,$0000) 'Compare Register (2) POR-$0000
pause1ms(1)
ILIcmd($0016,$EF1C) 'Horizontal Porch POR-$EFC1
pause1ms(1)
ILIcmd($0017,$0003) 'Vertical Porch POR-$0003
pause1ms(1)
ILIcmd($0007,$0033) 'Display Control POR-$0000
pause1ms(1)
ILIcmd($000B,$D308) 'Frame cycle Control POR-$D308
pause1ms(1)
ILIcmd($000F,$0000) 'Gate Scan start position POR-$0000
pause1ms(1)
ILIcmd($0041,$0000) 'Vertical Scroll Control (1) POR-$0000
pause1ms(1)
ILIcmd($0042,$0000) 'Vertical Scroll Control (2) POR-$0000
pause1ms(1)
ILIcmd($0048,$0000) 'First Window Start POR-$0000
pause1ms(1)
ILIcmd($0049,$013F) 'First Window End POR-$013F
pause1ms(1)
ILIcmd($004A,$0000) 'Second Window Start POR-$0000
pause1ms(1)
ILIcmd($004B,$0000) 'Second Window End POR-$013F
pause1ms(1)
ILIcmd($0044,$EF00) 'Horizontal Ram Address Postion POR-$EF00
pause1ms(1)
ILIcmd($0045,$0000) 'Vertical Ram Address Start POR-$0000
pause1ms(1)
ILIcmd($0046,$013F) 'Vertical Ram Address End POR-$013F
pause1ms(1)
ILIcmd($0023,$0000) 'RAM write data mask (1) POR-$0000
pause1ms(1)
ILIcmd($0024,$0000) 'RAM write data mask (2) POR-$0000
pause1ms(1)
'ILIcmd($0025,$8000) 'not in datasheet?
' pause1ms(1)
ILIcmd($004f,0) 'RAM Y address counter POR-$0000
pause1ms(1)
ILIcmd($004e,0) 'RAM X address counter POR-$0000
pause1ms(1)
ChangeOrientation(false) ' default to portrait
'
SSD1289 END
PUB Draw(x1, y1, x2, y2)|HORIZONTALRAMADDRESSPOS ' sets the pixel to x1,y1 and then fills the next (x2-x1)*(y2-y1) pixels
SelectMemGroup ' set 137 to correct value
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
HORIZONTALRAMADDRESSPOS := x1 + (x2 << 8)
ILIcmd(REG_HORIZONTALRAMADDRESSPOS,HORIZONTALRAMADDRESSPOS)
ILIcmd(REG_VERTICALRAMADDRESSSTART,y1)
ILIcmd(REG_VERTICALRAMADDRESSEND,y2)
ILIcmd(REG_SETGDDRxADDRESSCOUNTER,x1)
ILIcmd(REG_SETGDDRyADDRESSCOUNTER,y1)
Lcd_Write_Com($0022)
'ILIcmd($0050,x1)
'ILIcmd($0052,y1)
'ILIcmd($0051,x2)
'ILIcmd($0053,y2)
'ILIcmd($0020,x1)
'ILIcmd($0021,y1)
'Lcd_Write_Com($0022)
PUB Pixel(pixelcolor) ' send out a pixel
Lcd_Write_Data(pixelcolor)
PUB pause1ms(period)
'' 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 138 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"))
crlf
PRI ILIcmd(c,d) ' instruction in one method
Lcd_Write_Com(c) ' send out a word
Lcd_Write_Data(d)
PRI Lcd_Write_Com(ILIlong)
ILI_RS_low
LCD_Writ_Bus(ILIlong)
'docmd("W",ILIlong,0,0) ' try moving to pasm, leaves spin and cog dira as 0 so need to reset both
'SelectMemGroup ' not working though. Not sure if should be ILIlong or @ILIlong. neither work
PRI Lcd_Write_Data(ILIlong)
ILI_RS_High
LCD_Writ_Bus(ILIlong)
PRI LCD_Writ_Bus(ILILong)
'ILILong &= %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 |= ILILong ' merge with the word to output
ILI_WR_Low ' send out the data
ILI_WR_High
PRI ILI_RS_Low
OUTA &= %11111111_11111011_11111111_11111111 ' P18 low
PRI ILI_RS_High
OUTA |= %00000000_00000100_00000000_00000000 ' P18 high
PRI ILI_WR_Low
OUTA &= %11111111_11101111_11111111_11111111 ' p20 low
PRI ILI_WR_High
OUTA |= %00000000_00010000_00000000_00000000 ' p20 high
PRI ILI_RD_High ' not used, set high in hardware
'PRI ILI_CS_Low
' Change137(1) ' change to group 1
'
'PRI ILI_CS_High
' Change137(0) ' change to group 0 (any group other than 1)
PRI ILI_RESET_Low ' reset low
Change137(2) ' group 2
DIRA |= %00000000_00000000_00000010_00000000 ' set pin 9 as an output
OUTA &= %11111111_11111111_11111101_11111111 ' set pin 9 low
PRI ILI_RESET_High
Change137(2) ' group 2
DIRA |= %00000000_00000000_00000010_00000000 ' set pin 9 as an output
OUTA |= %00000000_00000000_00000010_00000000 ' set pin 9 high
DAT
sdbuffer byte $0[1024] ' 512 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 start_ram : err_
' Initialise the Drac Ram driver. No actual changes to ram as the read/write routines handle this
command := "I"
cog := 1 + cognew(@tbp2_start, @command)
if cog == 0
err_ := $FF ' error = no cog
else
repeat while command ' driver cog sets =0 when done
err_ := errx ' driver cog sets =0 if no error, else xx = error code
PUB stop_ram
if cog
cogstop(cog~ - 1)
PUB DoCmd(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
a := dira ' store the state of these and restore at the end
b := outa
DIRA &= %11111111_10100000_00000000_00000000 ' tristate all common pins that cog can change so no conflicts
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
dira := a ' restore dira and outa
outa := b
CON
'' Modified code from Cluso's triblade
'' commands to move blocks of data to the ILI9325 touchscreen display
' DoCmd(command_, hub_address, ram_address, block_length)
' I - initialise
' S - Move data from hub to ram
' T - Move data from ram to hub
' U - Move data from ram to display
' V - Hub to display
' W - not working - writecom in pasm
' E - convert from .raw RGB to two byte ILI format RRRRRGGG_GGG_BBBBB
' F - convert from .bmp BGR format to two byte ILI format
' X - merge icon and background based on a mask
' Y - Change 137 output Returns P0-P20 and P22 in HiZ. Pass hubaddrs
' Z - Set 161 pins. Returns in group 1
VAR
' communication params(5) between cog driver code - only "command" and "errx" are modified by the driver
long command, hubaddrs, ramaddrs, blocklen, errx, cog ' rendezvous between spin and assembly (can be used cog to cog)
' command = A to Z etc =0 when operation completed by cog
' hubaddrs = hub address for data buffer
' ramaddrs = ram address for data
' blocklen = ram buffer length for data transfer
' errx = returns =0 (false=good), else <>0 (true & error code)
' cog = cog no of driver (set by spin start routine)
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)
' Initialise hardware tristates everything and read/write set the pins
init mov err, #0 ' reset err=false=good
'mov dira,zero ' tristate the pins with the cog dira
and dira,maskP0P20P22 ' tristates all the common pins
done 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, #"W" wz ' lcdwritecom in pasm, not working
' if_z jmp #pasmlcdwritecom
cmp cmd, #"X" wz ' merge icon and background based on a mask
if_z jmp #mergeicons
cmp cmd, #"Y" wz ' change the 137 output
if_z jmp #changegroup
cmp cmd, #"Z" wz ' set the 161 counters
if_z jmp #set161
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
get_values_ret ret
' Pass pasm_n = 0- 7 come to this with P0-P20 and P22 tristated and returns them as this too
set137 or dira,maskP22 ' pin 22 is an output
andn outa,maskP22 ' set P22low so Y0-Y7 are all high
or dira,maskP0P20 ' pins P0-P20 are outputs
and outa,maskP0P2low ' set these 3 pins low
or outa,pasm_n ' set the 137 pins
or outa,maskP22 ' pin 22 high
set137_ret ret ' return
load161pasm ' uses ramaddr
or outa,maskP0P20 ' set P0-P20 high
or dira,maskP0P20 ' output pins 0-20
mov pasm_n,#0 ' group 0
call #set137 ' set the 137 output
and outa,maskP0P18low ' pins 0-18 set low
or outa,ramaddr ' output addres to 161 chips
or outa,maskP19 ' clock high
It's better to do the synth from ground up than hacking Kyes wav player object; In the end almost nothing will be left from the wav player anyway.
To be able to play samples at an arbitrary frequency, a phase accumulator is the way to go.
Use X number of the MSBs of the accumulator as an index into the sample in memory.
The example above can play samples of up to 64 kB in size. You can increase sample size by shifting less than 16 bits, but the pitch accuracy will decrease.
Less than 12 bit pitch accuracy will sound out of tune. (Btw, Pokey used 8 bits... ugh..)
The rate at wich the sample is played back can be calculated by the formula below.
samplePlaybackRate = mixingRate * (32bitFreqValue / (2^(32 - nrOfMsbsUsedAsSampleIndex) )
Note that samplePlaybackRate isn't the resulting frequency of the sampled instrument itself.. it's just the sample rate.
mixingRate is the sample rate out to the DAC.
A wavetable synth can be made by using just a few of the MSBs (like 8 or so) and then letting the 32 bit accumulator wrap over from 0xFFFFFFFF to 0. Each wrap is a period.
Then just apply a volume envelope and you're done.
Speaking of C, I think I have it working in XMMC AND LMM. See post here : http://forums.parallax.com/showthread.php?140010-Files&p=1099650&viewfull=1#post1099650 Looks like it's time to work on cache driver?
I have a bit of work to do before I release the chiptune player. It is functional except fast forward, rewind and GUI screening. I'm REALLY excited now! More later!
*edit*
Here's the newest transport BMP!
*more edit*
Here's an ALPHA release of chiptune player. It SHOULD be ready for the ILI but I might have missed something when porting from the SSD. These use OLD *May 13't* drivers.
Just updated to include the transport files for the SD card!
Found a bug, Should be:
There are some amazing synthesizer threads running at the moment. Lots of possibilities there. Generally, those synths are going to need knobs or sliders. I spent last night coding a multiline text box and a multi select box. Useful for things like a word processor and also for selecting which song to play. I have some rudimentary things working in gray but I am thinking windows95 gray is looking a bit boring and old-school. Maybe need some more funky sliders and buttons and checkboxes eg http://www.dreamstime.com/stock-images-stock-images-scroll-bar.-image14529454.
I wrote some of the drawing as code drawing individual lines and pixels but it may actually work out faster to use bitmaps. Also if we keep the size of things a common standard (eg a scrollbar is 15 pixels wide) then you can start to think about different 'skins'.
Oh and I just got an email saying the dual screen boards have been shipped.
And then add some scrollbars. In Opera these are gray when inactive and a light purple when active. Scrollbars will have three active touchscreen areas per bar - the top and bottom arrows move up or down one line and the slider goes through an entire text file, or moves around a bigger picture/webpage than can fit on the screen.
In many ways it is easier to draw bitmaps than to draw pixels and lines, so as much as possible, draw things with bitmaps that have been loaded into the ramdisk.
Spin code
I was able to confirm my display is faulty. I returned to my WAY OLD hardware to run some tests and sure enough, it seems as if the /CS line is not properly gating the /WR line. Also, some bits in some registers do not work properly. My new displays are on their way from Hong Kong with a 4-6 week ship time. *My wife rolls her eyes whenever I bring up priority shipping, lol*
I really, really like the idea of skinning things. Have a text based, *.skn (or whatever you want to call it) that contains the names of the bmps attached to the elements. You could also encode the background color and text color in the file to make it super-simple to skin. Honestly the more I think about it, there's all KINDS of things we could do with skins. Which brings me to an interesting thought:
There are several different screens and controllers to look at as potential "ports" BUT... The functionality depends on the controller and some have nicer features than others. Your ILI offers resize while my SSD does not. I do like the SSD1289's writeMask 1 and 2. Once I verify operation on my new displays, this method of masking should allow us to "color-mask" in REAL-TIME! The idea is this, with a black background *all 0's* you set the write mask register to the inverse of the color you want. Then draw your Black and White image and the White will appear the color set! It works on blue *WriteMask2*, and I suspect the faulty screen is why red-green don't work properly. *writeMask1* There is also COMPARE settings that I'm not able to decipher the operation of yet and can't trust the results of testing. *My guess is you set the compare registers to a value, and then 3 other bits control the logical function of the compare? Maybe we can use this to make the black part of the image not draw to screen but increment pointer???? at least that's what I hope! There is a mention of LG [2:0] ="000" in external mode, but no other explanation of SETTING LG?
*edit*
Just found the LG settings in Entry Mode(R$11) IB [0:2]:
"Write data to the GDDRAM after comparing the write data written to the GDDRAM by the microcomputer with the values in the compare registers (CPR [5:0], CPG [5:0] CPB [5:0]) and performing a logical and arithmetic operation on them."
That's all I can find on those... Any thoughts?
Of course they are wider (D'oh) so they won't fit side by side on the dual display board.
However, I'm having problems getting the display to work. Below is some code. There is a line in the con section
and so set which one you are using and I think there are 3-4 places in the code that are different.
I can get it to read off the SD card and display the background correctly. However, the picture then fades away over about 4 second - various colors in the picture spread out and take over the screen until there are just a couple of large squares of color. Interestingly though, if I hit the reset button, while the prop is restarting, all the correct picture plus icons are there. So for a brief moment the display is correct and an awful lot of things would have to have worked to get that picture-plus-icons there.
In the C code, /CS is kept high once all the data has been sent out. I don't think that is the problem though because when it is waiting for touch input it is in a different group and /CS is high then.
Also - I picked up these 3.2" displays for $20 but suddenly they are now $50. Hmm.
So we are close but no cigar.
So I'm back to using the 2.4" ones for the moment http://www.ebay.com.au/itm/2-4-TFT-LCD-Module-Display-Touch-Panel-PCB-adapter-/190477028273?pt=LH_DefaultDomain_0&hash=item2c5950cbb1 which are $16.87. If the 3.2" ones are going to be $50 then two 2.4" ones will be cheaper.
c code that we are copying
[code]
#include <reg51.h>
/* http://ttmcu.taobao.com
Asking price is $14 a piece, not too shabby.
I will go through and try to figure out what's going on. I doubt it will help but here's the timings I used to run: Of course this is just controlling WR, not CS.
I think my creative brain hemisphere came up with the answer, but it required some input from the logical hemisphere as well. What happens when you hit reset? One thing is that all the propeller pins go to HiZ.
I don't have a full answer yet, but I do have some code that works:
Patch that into the code I posted above.
Picture attached is the proof. Put the dira := 0 any further down and it fails.
Now my theory is that there is a short happening, and that the power supply is collapsing causing the display to fade out. Why a short would happen I do not know. It is as if "input" pins on that 40 pin header are somehow becoming "output" pins and fighting against the propeller pins.
I do not know which pins yet, but I guess one can go through them one at a time in code and make them outputs and find out.
The picture has been rock solid now for 20 minutes so that is proof that the setup and driver code is working correctly.
Can you replicate this?
Addit: $13.89 for your display? Wow, that is good value.
One thing I have noticed is the 3.2" display is not as good looking at an oblique angle. I don't think it is as true a "TFT" lcd as the smaller ones. ebay helpfully suggested some more 3.2" TFT touchscreens and some of those say that they have the "standard" 40 pin header. It is encouraging there are multiple suppliers and this 40 pin header is a standard. We could try some different displays and compare them. The code above just needs a couple of lines changed to completely change the display and that will be very handy for swapping in different sized displays. Futurlec sell the 20x2 socket which is also very useful. Once we get these SSD1289 displays working it means we are not so dependent on one supplier.
re displays, I do find my screen a bit dull. It would be nice to try some other displays, although I believe setting the gamma and power control properly * I can't quite figure it out* it might look better.
*edit*
Tried plugging in your code and sometimes it will load the icons, most of the time it will fail somewhere after mask. From my previous testing it seems to fail in HubToRam as I said. It seems like it only fails in LoadBMP but I'm not sure why.
I see in the C code that after many routines, /CS goes high. I don't think the ILI9325 code does that. My assumption with the ILI9325 driver is that even if /CS is low, if /RD and /WR and /RS are all high then no data will go to and from the display. But that may not be true with the SSD1289 display. Maybe /CS needs to be high to truly disable the display. It might explain why glitches happen with HubtoRam because that is going to be toggling lots of the data lines, and possibly sending data somehow to the display.
/CS at the moment is controlled by the group select but maybe it needs to be controlled by a latch?
You could try that ?
Yes you are right
There is a way we could test that. /CS on our board goes low when that group is selected, so if we deselect all groups between each data transfer that will put /CS high. However, on the board averagejoe and I have, we can't set /CS high when doing hub to ram transfers. We may need a revision on the board design to work around this.
*edit*
I'm pulling out the trusty HW_v3.4 to verify opperation of the screen. I will need to make some revisions to control /CS. Hopefully I can test a couple things out and see if it's the "last display / load RAM address" /CS
I'm going to try soldering up one of the dual display boards (they should be in the air to the USA by now). Because each display needs to be selected individually these have a latch on the /CS so this could be an unintended bonus for this board.
I was able to coax the XMMC - SRAM driver to life. Now I need to test the actual performance. I'm uploading files to the webpage this evening. I've been looking at your latest code and think it needs to be broken into objects. Almost 2500 lines of code! Breaking things up a bit will make it easier to port to C IMO. Still much work to be done.
Could you try the colormask when you get a chance? I really want to make sure this works before I invest more time coding for this. I am fairly sure there are a few broken registers in my display.
Also, I have been unable to replicate the glitch on HW_3.4b.
First the good news. The dual display works fine - see attached photo. So you can have a 480x320 screen if you want, or have a keyboard on one and lots of knobs on the other. That is with these displays http://www.ebay.com.au/itm/2-4-TFT-LCD-Module-Display-Touch-Panel-PCB-adapter-/190477028273?pt=LH_DefaultDomain_0&hash=item2c5950cbb1
The bad news is the SSD1289. I have tried all sorts of code. I did get some text on the display at one stage, but it was split into two lines and I have seen that error before and it is due to not setting up the draw area correctly.
The problem is the /CS line. We have run out of free propeller pins and by sending this through a latch it gets complicated because the pins controlling the latch output are also pins shared by the data bus. Because of the lack of spare pins, there never is total control over all the pins in parallel. So a simple thing like replicating this
becomes many more lines of code eg LatchLow
with the change137 going off in pasm and making the propeller pins HiZ (high with pullups) as the group changes. So I don't know if that upsets the display or not.
Or maybe we just have not got the startup code right.
There could be a software solution. But take a look at the photo - if you have two of the smaller displays you want them fairly close side by side. And the wider 3.2" display is too wide for the spacing on this board so you can only fit one. This means if you want a dual SSD1289 the spacing needs to be wider. And if the spacing is wider then the ILI9325 boards are further apart. Which means if we want two SSD9325's side by side we need a different board.
And if there is a different board for the dual SSD1289 displays then it might be better to think of a better design.
What I am thinking of is that if we need one more pin to do /CS then the only place to get a spare pin is P22 which is controlling the 137. One way you could do this is instead of selecting the group with the 137, use a MCP23008 on the I2C lines. Or even the 16 I/O version. That gives more I/O pins and it means the 374 latch and the 137 could be combined into one. But the main advantage would be that other propeller pins are not going up and down during latch outputs. (eg P0-P2). I think it will make the code simpler.
Speed will be an issue. The C code for the SSD1289 toggles the /CS line every time data goes out. I don't know if that is necessary and it may not be given I got a full screen picture at one stage and only toggled /CS at the beginning and the end of the entire picture, but if it is necessary, then /CS will be P22. The slower bit can be which screen to select and that can go through the MCP23008 and then some logic gates to select which screen.
Note, the screen draws correctly with the ILI code. There's just some error, most likely in the handoff between groups *IMO* I need to get my propalyzer board finished. I'm stuck trying to figure out how to plug everything together but think I'm close to a solution. I will need to pull my rack-mount out and start strippin old hardware. Maybe Jazzed will be kind enough to test some transistions in the load-desktop. I'm mostly interested in the /CS and /WR lines, although the /RS and Data would be interesting to know. Maybe I'll finish up the rebuild next week.
I'm not too worried about displays being side by side since my dual-screen board will be going in the rackmount with displays in landscape. Ribbon cables are handy for this, but the pin-header must go on the back of the board. Oh well, I'm sure we'll figure something out.
Keep up the good work and let's not worry too much about the SSD right now until I get all the information.
Take a look at this design. It is backwards compatible with the ILI9325. The display sockets are further apart so two SSD1289 displays fit side by side (and almost cover the whole board so doing this in surface mount is not so important if the display almost fills the board anyway). Replacing the 374 and 137 with a MCP23008 saves a chip. The design is simpler to code and simpler to understand. I also took the opportunity to move the audio closer to the propeller chip and as far from the max3232 as possible as I was worried about the stepup driver in the max3232 causing noise. I also grouped all the max3232 and reset components together because if you use the propplug you can leave that whole group out. And there are pads to bring out the group pins and P0-P15 so you can expand at least another 20 propeller pins to another board.
I REALLY like the audio layout, although I'm wondering about the "backwards compatibility" since the ILI displays will be fairly far apart?
A 28-bit logic analyzer would tell volumes about what's going on with the current display, and it's simple to build. Connecting things is a bit more difficult since the headers are on the 4 corners. I actually only need 0-22 and /CS /WR and /RS. See the theory I have is that /RS and /WR are low before /CS goes high. OR the timing of the last display write is not fully completing before the tri-state. But I'll need to burn that bridge when I get there.
The other thing I have been thinking about is this. If we stripped the touchscreen functions, how could we make the old board useful to vga, tv, keyboard and mice guys?
*edit*
Okay, I just finished looking at the board. The only suggestion I have is this. There needs to be more room around the gnd screw for the TO-220. I ran into a problem shorting this trace before. There's nothing around it, so I would leave plenty of space around this.
Yes that is a tricky one. There isn't room for more sockets. Maybe this can be a "feature"?
It would be a simple matter if the circuit all works to have two boards - one with the sockets closer.
Not quite sure what you mean but there are lots of boards out there that can do TV and VGA and audio and a keyboard eg the propeller demoboard http://elmicro.com/files/parallax/propdemod_schematic.pdf
I put a square pad on the underside so there are no tracks near the switcher bolt hole.
The autorouter was completing very easily so I thought I might add some SPI ports. So there are now three SPI ports and one I2C port so that means you can add an external A to D converter etc.
Have you got any of the 2.4" touchscreens?
The "stripping" of functions was a reference to the presentation thread. I was thinking of integrating something like Steve's HD shield. I'm sure it would not be too difficult. Not top priority by any means, just something to ponder.
I don't have any ILI displays right now. Need to order some other parts as well. I'm stuck waiting until I move now since I've reached the cutoff for ordering more stuff.
Ah. We can fix that. Ok, well I just put an order through for you for the parts below - if that goes off now then it will arrive about the same time as the boards and I can post both to you at the same time.
The catch is... you might have to find some obex driver code - eg for the MCP2302 A to D as I managed to squeeze that onto the board as well. (addit http://obex.parallax.com/objects/625/) So we can play around with some digital audio manipulation. I'm thinking fun things like a digital delay line. I wonder if we can do 16x16 bit multiplies fast enough?
I've sent off the boards for the MCP23008 driver. The space saved by removing one chip is the space I was able to use for the A to D converter so that makes this a better design now. Still, the one that you will get in a few days is very useful if you have a 2.4" ILI9325 display.
The cache driver for the old board using xmm-single is working quite well. I should be able to use xmmc since Steve has it working. I'm playing the NOOB card here. Not too worried since part of my need for xmm is variable AND code space. I have also been working on the "glitch fix" since it's a viable workaround for now.. With 2 SSD's on their way, ordering 2 ILI's will have to wait till I get my deposit back from this apartment I will post example of workaround when it's fully working and done. As a note, in the XMM driver :
*edit*
In testing of latest code, using glitchfix I have most things working There are still things not working, since I'm missing files on the sd card. Also, buttons demo has broken radio buttons? 2 are selected, 1 is not. Just noticed this.
I'm going to recode that with a white background. Needs a frame so the code only switches radio buttons within a frame.
I see you have got some cool stuff working over on the GCC forum - well done!