SparkFun Large RGB 8x8 LED Matrix & Controller - scrolling issue
I have a couple of these SparkFun RGB LED Matrix & Controllers (actually I got my second one in today):
http://www.sparkfun.com/commerce/product_info.php?products_id=760
I have some code working that can display characters on them and finally got the routine to scroll them right to left across an 8x8 LED module (haven't tried 2 of them yet).
My code works for scrolling but it requires that the DATA statements·for each character·be back to back. However, without going into the advanced (yet) routine as used in JonnyMac's Badge.sxb and the RoboGames badge...I am looking for another coding technique perhaps as a·LOOKUP table for easier setup. I have seen this done for the BS2 but can't get it right for SX/B.
Attached is my current working code.
Post Edited (T&E Engineer) : 10/23/2007 11:53:25 AM GMT
http://www.sparkfun.com/commerce/product_info.php?products_id=760
I have some code working that can display characters on them and finally got the routine to scroll them right to left across an 8x8 LED module (haven't tried 2 of them yet).
My code works for scrolling but it requires that the DATA statements·for each character·be back to back. However, without going into the advanced (yet) routine as used in JonnyMac's Badge.sxb and the RoboGames badge...I am looking for another coding technique perhaps as a·LOOKUP table for easier setup. I have seen this done for the BS2 but can't get it right for SX/B.
Attached is my current working code.
Post Edited (T&E Engineer) : 10/23/2007 11:53:25 AM GMT

Comments
pattern VAR Byte(65)
Keep in mind that I am using SX Beta software because the SX-28 can only have arrays up to 16 bytes. However, when I make the array larger to accommodate both displays (requireing 64 bytes each) then I quickly run out of RAM space.
pattern VAR Byte(129)
Any ideas on another way to connect (2) 64 byte SPI LED display devices?
Here is what works for 1 SPI LED matrix:
DISPLAY_RGB1: FOR tmpB1 = 0 TO 196 step 8 tmpB2 = tmpB1 tmpB3 = 0 DO WHILE tmpB3 < 65 READINC Ltr_Space + tmpB2, tmpB4 tmpB4 = tmpB4 * color INC tmpB3 LOW ChipSel PAUSE 1 SHIFTOUT DataIn, Clock, MSBFIRST, tmpB4 LOOP PAUSE 1 HIGH ChipSel DELAY_MS 100 NEXT RETURN Ltr_Space: DATA 0,0,0,0,0,0,0,0 DATA 0,0,0,0,0,0,0,0 DATA 0,0,0,0,0,0,0,0 DATA 0,0,0,0,0,0,0,0 DATA 0,0,0,0,0,0,0,0 DATA 0,0,0,0,0,0,0,0 DATA 0,0,0,0,0,0,0,0 DATA 0,0,0,0,0,0,0,0Post Edited (T&E Engineer) : 10/24/2007 12:46:24 PM GMT
DISPLAY_RGB1: FOR tmpB1 = 0 TO 196 step 8 tmpB2 = tmpB1 tmpB3 = 0 ' note that CS is held low, then ALL the bits are shifted out, then it's brought high again LOW ChipSel PAUSE 1 ' for example, sending bits 0-127 to two registers DO WHILE tmpB3 < 128 READINC Ltr_Space + tmpB2, tmpB4 tmpB4 = tmpB4 * color INC tmpB3 SHIFTOUT DataIn, Clock, MSBFIRST, tmpB4 ' remember that bits 0-64 will be in the SECOND register, and bits 64-127 will be in the FIRST register LOOP ' latch it all in PAUSE 1 HIGH ChipSel DELAY_MS 100 NEXT RETURNThat said, I would really urge you to set up an ASCII mapped character set, then feed strings to the character mapper. It will greatly simplify your code for messages. I think we've discussed this? So instead of sending hard-coded bitmaps of the characters in the order of the message, you can send character strings to a function that looks up the correct map for the character and sends it.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
When the going gets weird, the weird turn pro. -- HST
Post Edited (Zoot) : 10/24/2007 5:49:51 PM GMT
On another note, to get the ascii characters to be read in from a data statement, JonnyMac has·written a routine for·the RoboGames badge for·scrolling 5x7 characters (not in my case 8x8). But I would still be happy to use 5x7 characters but the display needs to see 64 bytes·shifted in not 35 bytes. So I modified it a little and will also try this out tonight:
' Use: SCROLL_LF [noparse][[/noparse] String | Label ] ' -- scrolls ASCII message; embedded string or z-string at "Label" ' -- strings in DATA statements may contain non-printing chars (bell, cls) SUB SCROLL_LF tmpW2 = __WPARAM12 ' start of string Get_Next_Char: READINC tmpW2, tmpB1 ' get character IF tmpB1 = 0 THEN Scroll_Lf_Exit ' exit if at end of string ' handle non-printable ' IF tmpB1 < $20 THEN ' non-printable? IF tmpB1 = Bell THEN 'beepTmr = BellTm ' beeper on GOTO Get_Next_Char ' get next char ELSEIF tmpB1 = FormFeed THEN CLS GOTO Get_Next_Char ELSE tmpB1 = " " ENDIF ENDIF ' convert non-ASCII chars to space ' IF tmpB1 > 127 THEN tmpB1 = " " ENDIF ' read character map into char buffer ' tmpB1 = tmpB1 - $20 ' zero adjust tmpW3 = tmpB1 * 5 ' calc character offset FOR tmpB2 = 0 TO 4 READINC ASCII_Set + tmpW3, charBuf(tmpB2) ' read character map NEXT charBuf(5) = %00000000 ' space pad ' push character into display from right ' tmpB5 = 0 LOW ChipSel PAUSE 1 DO WHILE tmpB5 < 65 FOR tmpB2 = 0 TO 5 ' char columns + space dispBuf(8) = charBuf(tmpB2) ' get one colum from char FOR tmpB3 = 0 TO 7 ' loop through display tmpB4 = tmpB3 + 1 dispBuf(tmpB3) = dispBuf(tmpB4) ' shift display left tmpB5 = tmpB5 * color INC tmpB5 SHIFTOUT DataIn, Clock, MSBFIRST, dispBuf(tmpB3) NEXT DELAY_MS scrollSpd NEXT LOOP PAUSE 1 HIGH ChipSel GOTO Get_Next_Char ' get another character Scroll_Lf_Exit: ENDSUB' ------------------------------------------------------------------------- ' Letters, Numbers, and Symbols ' ------------------------------------------------------------------------- ASCII_Set: DATA %00000000 ' space (ASCII 32) DATA %00000000 DATA %00000000 DATA %00000000 DATA %00000000 DATA %00000000 ' ! DATA %00000000 DATA %01001111 DATA %00000000 DATA %00000000 DATA %00000000 ' " DATA %00000111 DATA %00000000 DATA %00000111 DATA %00000000 DATA %00010100 ' # DATA %01111111 DATA %00010100 DATA %01111111 DATA %00010100 DATA %00100100 ' $ DATA %00101010 DATA %01111111 DATA %00101010 DATA %00010010 DATA %00100011 ' % DATA %00010011 DATA %00001000 DATA %01100100 DATA %01100010 DATA %00110110 ' & DATA %01001001 DATA %01010101 DATA %00100010 DATA %01010000 DATA %00000000 ' ' DATA %00000101 DATA %00000011 DATA %00000000 DATA %00000000 DATA %00000000 ' ( DATA %00011100 DATA %00100010 DATA %01000001 DATA %00000000 DATA %00000000 ' ) DATA %01000001 DATA %00100010 DATA %00011100 DATA %00000000 DATA %00010100 ' * DATA %00001000 DATA %00111110 DATA %00001000 DATA %00010100 DATA %00001000 ' + DATA %00001000 DATA %00111110 DATA %00001000 DATA %00001000 DATA %00000000 ' , DATA %01010000 DATA %00110000 DATA %00000000 DATA %00000000 DATA %00001000 ' - DATA %00001000 DATA %00001000 DATA %00001000 DATA %00001000 DATA %00000000 ' . DATA %01100000 DATA %01100000 DATA %00000000 DATA %00000000 DATA %00100000 ' / DATA %00010000 DATA %00001000 DATA %00000100 DATA %00000010 DATA %00111110 ' 0 DATA %01010001 DATA %01001001 DATA %01000101 DATA %00111110 DATA %00000000 ' 1 DATA %01000010 DATA %01111111 DATA %01000000 DATA %00000000 DATA %01000010 ' 2 DATA %01100001 DATA %01010001 DATA %01001001 DATA %01000110 DATA %00100001 ' 3 DATA %01000001 DATA %01000101 DATA %01001011 DATA %00110001 DATA %00011000 ' 4 DATA %00010100 DATA %00010010 DATA %01111111 DATA %00010000 DATA %00100111 ' 5 DATA %01000101 DATA %01000101 DATA %01000101 DATA %00111001 DATA %00111100 ' 6 DATA %01001010 DATA %01001001 DATA %01001001 DATA %00110000 DATA %00000001 ' 7 DATA %01110001 DATA %00001001 DATA %00000101 DATA %00000011 DATA %00110110 ' 8 DATA %01001001 DATA %01001001 DATA %01001001 DATA %00110110 DATA %00000110 ' 9 DATA %01001001 DATA %01001001 DATA %00101001 DATA %00011110 DATA %00000000 ' : DATA %00110110 DATA %00110110 DATA %00000000 DATA %00000000 DATA %00000000 ' ; DATA %01010110 DATA %00110110 DATA %00000000 DATA %00000000 DATA %00000000 ' < DATA %00001000 DATA %00010100 DATA %00100010 DATA %01000001 DATA %00010100 ' = DATA %00010100 DATA %00010100 DATA %00010100 DATA %00010100 DATA %00000000 ' > DATA %01000001 DATA %00100010 DATA %00010100 DATA %00001000 DATA %00000010 ' ? DATA %00000001 DATA %01010001 DATA %00001001 DATA %00000110 DATA %00110010 ' @ DATA %01001001 DATA %01111001 DATA %01000001 DATA %00111110 DATA %01111110 ' A DATA %00010001 DATA %00010001 DATA %00010001 DATA %01111110 DATA %01111111 ' B DATA %01001001 DATA %01001001 DATA %01001001 DATA %00110110 DATA %00111110 ' C DATA %01000001 DATA %01000001 DATA %01000001 DATA %00100010 DATA %01111111 ' D DATA %01000001 DATA %01000001 DATA %01000001 DATA %00111110 DATA %01111111 ' E DATA %01001001 DATA %01001001 DATA %01001001 DATA %01000001 DATA %01111111 ' F DATA %00001001 DATA %00001001 DATA %00001001 DATA %00000001 DATA %00111110 ' G DATA %01000001 DATA %01001001 DATA %01001001 DATA %01111010 DATA %01111111 ' H DATA %00001000 DATA %00001000 DATA %00001000 DATA %01111111 DATA %00000000 ' I DATA %01000001 DATA %01111111 DATA %01000001 DATA %00000000 DATA %00100000 ' J DATA %01000000 DATA %01000001 DATA %00111111 DATA %00000001 DATA %01111111 ' K DATA %00001000 DATA %00010100 DATA %00100010 DATA %01000001 DATA %01111111 ' L DATA %01000000 DATA %01000000 DATA %01000000 DATA %01000000 DATA %01111111 ' M DATA %00000010 DATA %00001100 DATA %00000010 DATA %01111111 DATA %01111111 ' N DATA %00000100 DATA %00001000 DATA %00010000 DATA %01111111 DATA %00111110 ' O DATA %01000001 DATA %01000001 DATA %01000001 DATA %00111110 DATA %01111111 ' P DATA %00001001 DATA %00001001 DATA %00001001 DATA %00000110 DATA %00111110 ' Q DATA %01000001 DATA %01010001 DATA %00100001 DATA %01011110 DATA %01111111 ' R DATA %00001001 DATA %00011001 DATA %00101001 DATA %01000110 DATA %00100110 ' S DATA %01001001 DATA %01001001 DATA %01001001 DATA %00110010 DATA %00000001 ' T DATA %00000001 DATA %01111111 DATA %00000001 DATA %00000001 DATA %00111111 ' U DATA %01000000 DATA %01000000 DATA %01000000 DATA %00111111 DATA %00011111 ' V DATA %00100000 DATA %01000000 DATA %00100000 DATA %00011111 DATA %00111111 ' W DATA %01000000 DATA %00111000 DATA %01000000 DATA %00111111 DATA %01100011 ' X DATA %00010100 DATA %00001000 DATA %00010100 DATA %01100011 DATA %00000111 ' Y DATA %00001000 DATA %01110000 DATA %00001000 DATA %00000111 DATA %01100001 ' Z DATA %01010001 DATA %01001001 DATA %01000101 DATA %01000011 DATA %00000000 ' [noparse][[/noparse] DATA %01111111 DATA %01000001 DATA %01000001 DATA %00000000 DATA %00000010 ' \ DATA %00000100 DATA %00001000 DATA %00010000 DATA %00100000 DATA %00000000 ' ] DATA %01000001 DATA %01000001 DATA %01111111 DATA %00000000 DATA %00000100 ' ^ DATA %00000010 DATA %00000001 DATA %00000010 DATA %00000100 DATA %01000000 ' _ (underscore) DATA %01000000 DATA %01000000 DATA %01000000 DATA %01000000 DATA %00000000 ' ` DATA %00000001 DATA %00000010 DATA %00000100 DATA %00000000 DATA %00100000 ' a DATA %01010100 DATA %01010100 DATA %01010100 DATA %01111000 DATA %01111111 ' b DATA %01001000 DATA %01000100 DATA %01000100 DATA %00111000 DATA %00111000 ' c DATA %01000100 DATA %01000100 DATA %01000100 DATA %00100000 DATA %00111000 ' d DATA %01000100 DATA %01000100 DATA %01001000 DATA %01111111 DATA %00111000 ' e DATA %01010100 DATA %01010100 DATA %01010100 DATA %00011000 DATA %00001000 ' f DATA %01111110 DATA %00001001 DATA %00000001 DATA %00000010 DATA %00001100 ' g DATA %01010010 DATA %01010010 DATA %01010010 DATA %00111110 DATA %01111111 ' h DATA %00001000 DATA %00000100 DATA %00000100 DATA %01111000 DATA %00000000 ' i DATA %01000100 DATA %01111101 DATA %01000000 DATA %00000000 DATA %00100000 ' j DATA %01000000 DATA %01000100 DATA %00111101 DATA %00000000 DATA %01111111 ' k DATA %00010000 DATA %00101000 DATA %01000100 DATA %00000000 DATA %00000000 ' l DATA %01000001 DATA %01111111 DATA %01000000 DATA %00000000 DATA %01111100 ' m DATA %00000100 DATA %00011000 DATA %00000100 DATA %01111000 DATA %01111100 ' n DATA %00001000 DATA %00000100 DATA %00000100 DATA %01111000 DATA %00111000 ' o DATA %01000100 DATA %01000100 DATA %01000100 DATA %00111000 DATA %01111100 ' p DATA %00010100 DATA %00010100 DATA %00010100 DATA %00001000 DATA %00001000 ' q DATA %00010100 DATA %00010100 DATA %00011000 DATA %01111100 DATA %01111100 ' r DATA %00001000 DATA %00000100 DATA %00000100 DATA %00001000 DATA %01001000 ' s DATA %01010100 DATA %01010100 DATA %01010100 DATA %00100000 DATA %00000100 ' t DATA %00111111 DATA %01000100 DATA %01000000 DATA %00100000 DATA %00111100 ' u DATA %01000000 DATA %01000000 DATA %00100000 DATA %01111100 DATA %00011100 ' v DATA %00100000 DATA %01000000 DATA %00100000 DATA %00011100 DATA %00111100 ' w DATA %01000000 DATA %00110000 DATA %01000000 DATA %00111100 DATA %01000100 ' x DATA %00101000 DATA %00010000 DATA %00101000 DATA %01000100 DATA %00001100 ' y DATA %01010000 DATA %01010000 DATA %01010000 DATA %00111100 DATA %01000100 ' z DATA %01100100 DATA %01010100 DATA %01001100 DATA %01000100 DATA %00000000 ' { DATA %00001000 DATA %00110110 DATA %01000001 DATA %00000000 DATA %00000000 ' | DATA %00000000 DATA %01111111 DATA %00000000 DATA %00000000 DATA %00000000 ' } DATA %01000001 DATA %00110110 DATA %00001000 DATA %00000000 DATA %00000100 ' ~ DATA %00000010 DATA %00000100 DATA %00000010 DATA %00000000 DATA %00001000 ' <- (non ASCII) DATA %00011100 DATA %00101010 DATA %00001000 DATA %00001000This way it continues loading in 5x7 ascii characters until it reaches 64 bytes and then it dumps it to the display. I don't know if this will work or not but it makes sense at the moment. I will let you all know. If it does work then I should be able to simply change the 65 to a 128 (if your original method for 2 displays works for me as well). Thanks!
·
' tmpW3 = tmpB1 * 5 ' calc character offset
would be
tmpW3 = tmpB1 * 8 ' calc character offset
because you want 8 bytes per character. Then instead of reading and counting BITS like in your original program, just grab the bytes and shift each byte one a time... remember your main dumping "loop" is counting CHARACTERS... this is pseudo code just to illustrate the flow:
messagePntr = address 'point to data address of first character of text string ' now dump 8 CHARACTERS LOW CS ' get shift registers ready FOR char = 7 TO 0 READ --> from messagePntr+char into tmpByte1 'get the char FOR outBytes = 7 TO 0 'now, you need to shift out 8 bytes of pixel map for this character --- 'each character map has 8 bytes, organized following ascii chart --- does not include offset for 0 or other error checking READ --> from (charMaps+char)*8 into tmpByte2 'great, now you've got the first row (or column as you prefer) of map data in tmpByte2 'process tmpByte2 for each pixel if you need RGB info, etc. SHIFTOUT tmpByte2 NEXT 'next row/col for this character NEXT 'next char in string HIGH CS 'latch the data!!!!Now, keep in mind the above dumps the entire 8 chars to display WITHOUT holding them in memory (which I don't think you'll be able to do). The SXB RoboBadge code DOES hold the display in a buffer, but it's much smaller.
The above could be set to "scroll" by changing the start and end chars of the main loop and one more outside loop:
FOR start = 0 TO 7 end = start+7 'set "tail" of this sliding window messagePntr = address 'point to data address of first character of text string ' now dump 8 CHARACTERS LOW CS ' get shift registers ready FOR char = end TO start 'on second loop, start will be 1, end will be 8 READ --> from messagePntr+char into tmpByte1 'get the char FOR outBytes = 7 TO 0 'now, you need to shift out 8 bytes of pixel map for this character --- 'each character map has 8 bytes, organized following ascii chart --- does not include offset for 0 or other error checking READ --> from (charMaps+char)*8 into tmpByte2 'great, now you've got the first row (or column as you prefer) of map data in tmpByte2 'process tmpByte2 for each pixel if you need RGB info, etc. SHIFTOUT tmpByte2 NEXT 'next row/col for this character NEXT 'next char in string HIGH CS 'latch the data!!!! MS_DELAY 100 'delay between scroll "frames" NEXTEDIT -- p.s., I forgot about firstin/firstout -- pseudo code above changed to loop through chars and bytes backwards. Also the above does not include checking for "end of string" and such, or error checking for non-ascii characters, but hopefully it helps in breaking down the flow of things?
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
When the going gets weird, the weird turn pro. -- HST
Post Edited (Zoot) : 10/24/2007 7:56:26 PM GMT
It appears that this display does scroll across 2 SPI LED·displays BUT acts strangely when sending more than 64 bytes to the 2 SPI LED displays. I have attached the documentation but it looks straight forward to what I have done and what you stated below seems ok but it is not. If I connect the 2 LED SPI displays with DataOut of the first going to DataIn of the second (and paralleled Clock and ChipSel signals), I get a "ghosted" image of the character breifly on the other 1st display when the data is being shifted onto the 2nd display. I haven't been able to find away to clear this. Ideas?
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
When the going gets weird, the weird turn pro. -- HST
This is what is running:
DISPLAY_RGB1: FOR tmpB1 = 0 TO 255 step 8 tmpB2 = tmpB1 tmpB3 = 0 LOW ChipSel DELAY_MS 1 DO WHILE tmpB3 < 64 READINC Ltr_Space + tmpB2, tmpB4 tmpB4 = tmpB4 * color SHIFTOUT DataIn, Clock, MSBFIRST, tmpB4 INC tmpB3 LOOP DELAY_MS 1 HIGH ChipSel DELAY_MS 100 NEXT RETURNThe ghosting problem happens whether I have 1·OR 2 SPI LED displays when you change the DO WHILE tmpB3 < 64 to anything higher (in this case 127 or 128, etc.)
DO WHILE tmpB3 < 127
I do see strange results for anything over 65 or so. I think this is because of the 64 byte buffer in the controller that the documentation (attachment) indicates. If this is the case, why have DataOut?
I also had some problems following your previous posting. In particular:
·How do I find out the data address? Isn't this CharMaps: ? or Ascii_Set:·?
If so then why do you reference CharMaps later?
You can't have a complex line in SX/B like this.
Please explain a little bit more.
Also was I supposed to use this within the SCROLL_LF routine as I previously posted OR is this it's own routine in place of it?
Is there anyway to do 5x7 characters instead of 8x8 (as I would rather use a dataset already made). If not ok, but just asking.
Sorry for all the questions. I just want to understand this clearly.
Thanks so much!
Post Edited (T&E Engineer) : 10/25/2007 12:27:39 AM GMT
How do I find out the data address? Isn't this CharMaps: ? or Ascii_Set: ?
-- it would be something like
Msg1:
DATA "T and E is so great!"
If so then why do you reference CharMaps later?
READ --> from (charMaps+char)*8 into tmpByte2
You can't have a complex line in SX/B like this.
-- that's why I called it pseudo code. Actually, there's an error there -- should be charMaps+(char*8) -- but regardless... what you are doing is reading the first byte from Msg1 -- which is "T" -- that's really a number -- the ASCII number. Presuming your charMaps are ordered like the ASCII chart, you can use the number that *is* "T" to get a data address (offsetting for non-alphanumeric chars if you like, and multiplying by data block size).
In SXB (though I would do this part in ASM -- way way faster to access "arrays" in ASM then SXB) would be something like:
pntr = char * 8 'set pointer to ASCII # of "char" * 8 (for the byte size of each map)
pntr = pntr - "0" 'offset to start of alphanum chars -- note quotes -- this make it the "number" that represents the char "0"
pntr = pntr+charMaps 'add to address of start of charMaps
'--> start reading 8 bytes from pointer
I'll have a chance to look at your program a little later tonight in detail.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
When the going gets weird, the weird turn pro. -- HST
That said, this does NOT do scrolling per se, it just prints characters to the display from text string message. See comments in code regarding why only last two chars of any message will "print". I'll leave it for you to do "sliding window" work -- basically you would wrap my DISPLAY_MSG function in new code that chooses the start and end characters of the string, so instead of dumping characters from the string till "0" is reached, you would, say, start with character 0 and print character 0 and character 1 (two character display), pause, then print characters 1 and 2, etc. till "0" is reached.
The biggest difference in my little piece of code is that nothing is held in buffers -- the characters are dumped straight through. If you do sliding window, this will save you a lot of work.
That said, for cool scrolling (one column at time, for example) you would either need to redo the code so that columns are dumped (I think this will be very tricky since your drivers want pixels L to R, T to
Last but not least, I wasn't thinking straight earlier -- to avoid the whole FIRST IN, FIRST OUT conundrum with characters, if you are using my code, put the LAST display (right most char) on the D line from the Stamp, and put the DOUT from that display to the DIN of the LEFT char. In other words, cascade them in reverse -- if the "first" 8x8 backpack on your display is actually wired "last" in the cascade, then when you dump the FIRST character out, it will end up at that driver.
I sure hope this is helpful. I also can't encourage you enough to buckle down and create your bitmap ASCII art -- I did some characters (see my notes in the code) but remember you need 8 bytes of binary info for each char (not 64 bytes as in your char maps).
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
When the going gets weird, the weird turn pro. -- HST
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
When the going gets weird, the weird turn pro. -- HST
I will look at it tonight to try and understand what is being done.
Thanks again for your efforts and time!
I'll probably have some questions later on your code.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
When the going gets weird, the weird turn pro. -- HST
So the first Msg1 has red for a color and I see some lines being drawn but just wrong display for what should be A B C.
'Use: SEND_MSG MsgAddr, color (0-6) DISPLAY_MSG: tmpW2 = __WPARAM12 ' pass address of message to print; tmpW1 is used in other subs color = __PARAM3 ' entire message will be one color, but you can play with this I'm sure Setup_Led_Driver: LOW ChipSel DELAY_MS 1 ' delay following spec sheet for driver Read_Char: READINC tmpW2, char ' read char from string... IF char = 0 THEN Chars_Done ' end of string? done... 'now, you've got a char, you need to point READ to 'to that 8 byte character map and read in each row of bits, 'convert each bit to byte with color, send it 'but it's FIRST IN, FIRST OUT to the shift register Map_Char: tmpW3 = char - 32 'subtract 32 so 0 = space 'you could do other trickery here to get form feed chars, case sensitivity, etc. tmpW3 = tmpW3 * 8 'it's the charactuer "number" * 8 bytes 'now some trickery is needed to "mask" the bit of the pixel, so you can add color and create a byte... Parse_Char: FOR tmpB5 = 0 TO 7 ' loop through rows, top to bottom READINC ASCII_Set + tmpW3, tmpB2 'read first row of pixels bits ("bitmap") into tmpB2, tmpW3 will increment on each pass FOR tmpB3 = 0 TO 7 ' unless you mirror image your charMaps, you need to send pixels HSB to MSB, hence mask rotates right tmpB4 = %1000_0000 >> tmpB5 'bit mask for this pixel remember it's just a BIT tmpB4 = tmpB2 & tmpB4 'mask with row and save to mask variable space IF tmpB4 <> 0 THEN 'pixel on? -- set to 1 and add color tmpB4 = 1 + color ENDIF 'whew, now you've got a byte with color/on/off ready to go to top left shift register 'so send it SHIFTOUT DataIn, Clock, MSBFIRST, tmpB4 NEXT ' next pixel this row, from LEFT TO RIGHT, not right to left [b]DELAY_MS 100[/b] NEXT 'next row this char Next_Char: GOTO Read_Char Chars_Done: ' latch it and pause!!! [b]DELAY_MS 1 [/b] HIGH ChipSel Msg_Done: RETURNHow about using JonnyMac's BIT routine too (if needed):
·This helped when converting your BS2 DoChar routine for use on the SX/B.
' Use: value = BITVAL someVal, position ' -- "someVal" can be a byte or word FUNC BITVAL IF __PARAMCNT = 2 THEN ' byte passed? tmpW1 = __PARAM1 ' get byte value temp1 = __PARAM2 ' get bit position ELSE ' word passed tmpW1 = __WPARAM12 ' word was passed temp1 = __PARAM3 ' get bit position ENDIF temp2 = 0 ' assume cleared IF temp1 >= 0 THEN ' position value legal? IF temp1 <= 15 THEN tmpW2 = 1 << temp1 ' create bit mask tmpW2 = tmpW2 & tmpW1 ' clear other bits IF tmpW2 > 0 THEN ' if not zero temp2 = 1 ' bit was 1 ENDIF ENDIF ENDIF RETURN temp2 ENDFUNC doChar: LOW ChipSel 'get ready for SPI PAUSE 1 FOR rows = 0 TO 7 'loop through rows pixels = buf(rows) FOR cols = 0 TO 7 'loop cols and make a pixel byte value = BITVAL pixels, cols value = value * Color SHIFTOUT DataIn, Clock, MSBFIRST, value 'if bit for this col/row is 1, then color gets you 1-7 if "on" 'Un-comment to have every LED pixel random 'RANDOM seed 'Rand_no = seed // 7 + 1 'color = Rand_no NEXT NEXT 'Un-comment to have every letter random 'RANDOM temp1 ' 'temp2 = temp1 // 7 'temp2 = temp2 + 1 'color = temp2 RANDOM seed color = seed // 7 color = color + 1 'Hold down on keypad for random color cycling PAUSE 1 HIGH ChipSel 'latch it RETURNSee attached program using these. It uses a 4x4 keypad and lets the user hit the button to display 1-9,0,A-F on the display and it works well.
Remember on the ABC message, you should just see " C" because my routine dumps chars till 0 is reached -- with two chars in the display you would see the RIGHT two most characters. Maybe just try a message with "AB".
The bit buffer routines in the RoboBadge are good, but make sure you are getting good dumps of bitmaps first -- then you can take things up a level by dumping the char maps into the bit buffer rather than straight to display. Work in pieces.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
When the going gets weird, the weird turn pro. -- HST
Thanks again.· However seeing those lines are not a good indication that the letter's are coming out correct. I will post my findings tonight after work.
DISPLAY_RGB1: FOR tmpB1 = 0 TO 255 step 8 tmpB2 = tmpB1 tmpB3 = 0 LOW ChipSel DELAY_MS 1 [b]DO WHILE tmpB3 < 64 'or use DO WHILE tmpB3 < 128 for 2 displays...[/b] READINC Ltr_Space + tmpB2, tmpB4 tmpB4 = tmpB4 * color SHIFTOUT DataIn, Clock, MSBFIRST, tmpB4 INC tmpB3 LOOP DELAY_MS 1 HIGH ChipSel DELAY_MS 100 NEXT RETURNI can still marquee scroll across from right to left on 1 SPI LED display. The problem is when I connect 2 displays which requires the "DO WHILE tmpB3 < 64" to get set to a higher value like 128. This is when the "ghost" imaging appears offset from the correct scrolling. So I am saying that I can scroll correctly across 2 displays with "DO WHILE tmpB3 < 128" - BUT I also get the "ghost" imaging of the same scrolling faintly offset in the background making the regular scrolling unusable across 2 displays.
I spent many hours on this and other programs in modification and got nowheres. Where can I step back to begin again?
You last program shows just some red lines. I did modify the inner section for JonnyMac's BITVAL routine which then shows alot of fast scrambling pixel data on the display when it finally lands on what looks like "(" (or the 10th character down).· I was thinking there was something not right with the mulitplying times 8 of tmpW3. I'm really lost with all this and previous coding to go through.
- you've got to be able to read text strings with one routine (e.g. DATA "I am cool.", 0) one byte at a time
- once you've got the char byte, read an ascii map with each char as 8 bytes, not 64 bytes as you have in your hand-pixellated messages. Using a byte for each pixel is very inefficient and will chew up all the space is in your SX.
- now that you've got the char, apply it to your buffer (128bits or 16 bytes) one column at a time (this is how the RoboBadge SXB code work -- the buffer is shifted and the next column added). I think you will have some work to do here because you have a larger bitmap -- when one bit is shifted off the left of one byte you need to carry that bit over to the next byte. If I had to guess, this is where the ghosting is coming from -- the column is not being carried over properly
So, following the RoboBadge example, I would create all your ASCII characters (starting with #32) as 8 bytes per character, in columns (i.e., turned sideways). You can even leave most of them blank and just do your first messages with the characters' you've drawn.
Then make sure you can add a single char to the buffer, dump the buffer, see the character. THEN start manipulating the buffer for scrolling.
I think what may be throwing is you the bitmap -- leaving color aside for now -- remember that in a bitmap a 1 means "turn pixel on" and 0 means "off". Your goal here is to NOT turn that single bit (which takes up little space) into a BYTE (with color added) until the moment you shiftout. You want to keep as little as possible in memory. E.g.
[noparse][[/noparse]code]
dispBuffer VAR Byte(16)· '128 bits display buffer
FOR row = 0 TO·15 'loop through BYTES of buffer -- you can't access bits as array
·· pixelMask = %1000_0000 'initialize mask for this row
·· FOR pixel = 0 TO 7 'loop through each byte in this row
····· tmp = dispBuffer(row)
····· tmp = tmp &·pixelMask ·'mask off each bit in turn for this row
····· IF tmp > 0 THEN· 'if the bit in this position is 1, then it's a pixel
············ tmp = 1 + color 'NOW you make it·a byte with the color
······ENDIF ' else it's off and is already 0····
····· SHIFTOUT tmp 'etc.... send to display
·· NEXT
NEXT
[noparse][[/noparse]/code]
Here's the thing you need to think about -- in the above the first pixel to be sent from the buffer would be at the TOP RIGHT of the buffer if you were reading it -- bit0 in row0, and what you really want to send first is the TOP LEFT pixel, which would be BIT7 of ROW8 (the second column, left most bit). So the rub is deciding how to orient your bitmap buffer and the bitmaps in data to begin with. You could consider the display buffer columns, and scan the bits from 7 TO 0 which might work better for you. Going through the buffer as columns to your displays will be tricky though.
Remember the above ONLY takes an existing "image" of the display in BITS (pixels on/off) and dumps it. The second part of all this is adding/changing/manipulating that buffer image so that you get the effects you want (new characters, scrolling). The code from the robo badge· does this here, but you would not OUTPUT it here, you would do that with a routine similar to above.
This code is meant to grab just ONE COLUMN from a "turned sideways" ascii map, shift all the pixels of the display buffer over 1, and add the column to the side. A few things about robobage -- there was the room for an "off screen" column, so the new pixels are put there, then everything is shifted. You don't have the room nor the need for that. I would shift then add the column for scrolling.
[noparse][[/noparse]code]
' you will need to SAVE the last column from this char that you added so that the next time through this routine
'·you can get the next column. If column >= 8 you're done, because there are only 8 cols in a char
······ INC lastCol
······ IF lastCol < 8 THEN 'yer good
DO WHILE cols <·14·· 'let's pretend display buffer is set up as columns, not rows, so you've got 16 columns as bytes
····························· 'only loop through 15 because the last column will be col 14 when you are done
····· tmp1 = cols·+ 1
······dispBuf(tmpB1) = dispBuf(cols)··' shift display columns to the left unless it's last column
LOOP 'now you've got 15 columns at left of display with right column blank
······
······ dispBuf(15) = charBuf(lastCol)··' get the one colum from char --·charBuf as an 8 byte array of the map was set by ascii string function
······ ENDIF
[noparse][[/noparse]/code]
·Not sure if any of this is helpful? I would start backwards and write up routines in small steps -- get your character maps built. Then code the function that dumps a BIT map display buffer (16 bytes) to the display. Don't worry about characters, just that your bitmap is in columns and displays properly.
Then write a function that takes a single byte-stored character and adds it to the display buffer. Then write the function that reads a text string into chars. THEN you know you've got a solid flow:
- read char from string
- add char to display buffer
- print display buffer
If you've got that, then a) it is not hard to write relatively simple code that moves around the display buffer bits before "printing" it, and b) you will know your own buffer and routine organization well enough that manipulating the buffer will not be difficult:
- read char from string
- (manipulate buffer)
- add char (or part of char)·to display buffer
- (manipulate buffer some more)
- print display buffer
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
When the going gets weird, the weird turn pro. -- HST
As far as the ghosting...There is no ghosting until you go past the 64 byte internal buffer of the display device I believe. I think this is what the problem is. I can always get scrolling for either 64 bytes or 128 bytes (e.g. DO WHILE tmpB3 < 65·· or·· DO WHILE tmpB3· <· 129 ). I know you want me to start using 8 bytes vs 64 bytes but I am explaining this from a view point of a past working program for information purposes about the problem.
The problem is that the ghosting ALSO shows with the regular scrolling for anything larger than the 65 bytes. My guess is that from reading the documentation of the display (previously attached), there is an internal buffer of 64 bytes so perhaps this is affecting it. I don't know. Scrolling is fine for either 1 or 2 displays (except for the annoying faint offset ghost scrolling) with the regular normal scrolling.
Thanks for hanging in there with me on this.
AFter your next round of playing, post your running sx/b program and I will try to take a look this weekend.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
When the going gets weird, the weird turn pro. -- HST
DO WHILE tmpB3 < 64
then the top right·LED of the display is always blank (or off) during scrolling. If I change to:
DO WHILE tmpB3 < 65
the top right LED goes off and on correctly while scrolling. If this is the case then this is why I choose 129 which is 1 more than 128 (or logically thinking for a single display 65 is 1 more than 64).
Since I am loading in a bunch of 0's (Ltr_Space) as the first character, I can't tell exactly at what number past 65 the ghosting /flickering occurs at. I have also posted this problem to the SparkFun forum but haven't heard any replies yet. I really don't think I am doing anything wrong in my programming to cause this ghosting / flickering for any number > 65 (or in my case for 2 displays ~129).
As taken from the matrix_backpack.pdf (previously posted):
What about this too?
Is the SPI clock exceeding 125 KHz?
I know the SX-28 is running at 4MHz....but don't know if this is related.
As stated before it scrolls fine on 1 display using my original code routine:
DISPLAY_RGB1: FOR tmpB1 = 0 TO 255 step 8 tmpB2 = tmpB1 tmpB3 = 0 LOW ChipSel DELAY_MS 1 [b]DO WHILE tmpB3 < 64 'or use DO WHILE tmpB3 < 128 for 2 displays...[/b] READINC Ltr_Space + tmpB2, tmpB4 tmpB4 = tmpB4 * color SHIFTOUT DataIn, Clock, MSBFIRST, tmpB4 INC tmpB3 LOOP DELAY_MS 1 HIGH ChipSel DELAY_MS 100 NEXT RETURN·
I set the DEVICE and FREQ as below:
DEVICE········· SX28, OSC128KHZ, TURBO, STACKX, OPTIONX
FREQ··········· 128_000
It certainly got rid of the ghosting but also brought about another problem. Now the scanning rate is so slow that it redraws too slow so that it is certainly not "smooth" at all - at least with my current program using 64 Bytes not 8 Bytes like Zoot said. Whats weird is that it is ok at higher speeds like 4 MHz with 64 bytes but anything more forget it.
What now?
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
When the going gets weird, the weird turn pro. -- HST
Certainly at 128 KHZ it is too slow to do any kind of scrolling (at least using my current program loading in 64 bytes (or more)).
Can anyone else comment on this oddity?
Take a look at this link to the SparkFun forum in which a user wrote a program for the RGB backpack using an aduino USB board. The program states that you have to use 125 KHz which makes sense to what the backpack pdf guide states too:
http://forum.sparkfun.com/viewtopic.php?p=37163#37163
Thanks.
Post Edited (T&E Engineer) : 10/29/2007 2:14:06 AM GMT
125khz SPI -- that's not the frequency of the uController clock, that's the bit rate on clocked data transfers, e.g. SHIFTIN/SHIFTOUT (for SPI) and I2C. The SX/B documentation says that SHIFTIN/SHIFTOUT use a rate of 83khz, independent of uC clock setting (I believe). Far below the max data transfer rate of the backpacks. The forum post you pointed me to refers to a controller where the slowest rate available for SPI 125khz, which means that controller is pushing the envelope on what the backpack can handle. Remember too, that with any clocked data transfer, slower will generally be fine, while faster will not. In this case, unless you are using the speedmult parameter on your SHIFTOUT to jack up the transfer rate, I don't think that's it.
Also be careful not to confuse bit rate on dumping your data with the rate at which things are actually happening the program itself. For example,
1. dump 128 bytes (1024 bits) of data to backpacks at 83khz (default for SHIFTOUT). This is good -- gets your data out there in 1/83 of second, which is ACTUALLY NOT ALL THAT FAST. Think about it -- your MAX refresh rate would be about 83hz (dumping frame after frame of pixels as fast as you can, not counting code overhead). A computer monitor at 83hz would "flicker". But still, it's pretty fast and most of the time you are not dumping frame after frame as fast as you can.
2. now pause 25 ms (or not!) while your eyeball rests. Pausing 25 ms may take 100,000 ticks at 4_000_000 device speed, or 200,000 ticks at 8_000_000 device speed, etc, but it's still 25 ms.
3. go to step 1.
Step 2 is what matters for the pace of your program, scroll speed, etc. Step 1 should always be as quick as possible.
I would look to two places in your current code -- when/where you latch the cascaded shift registers on the backpack, and which rows/columns of your data and/or buffer are being dumped.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
When the going gets weird, the weird turn pro. -- HST
Post Edited (Zoot) : 10/29/2007 3:00:14 AM GMT
It doesn't have to *be* 125khz (the whole point of clocked data transfer is that it is baud and strict time independent -- it doesn't matter how slow the bits come, as long as the clock line pulses when each bit is "ready"). It just has to be less than 125khz.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
When the going gets weird, the weird turn pro. -- HST
OK. now that it has been identified, can we work on your last program which doesn't work at the moment, or take another look at this for scrolling?
Something else I was thinking about -- you don't really need to do 8x8 chars -- you would do 5x8 chars (5 wide, 8 high) which would let you fit a bit more on the display at one time (2.5 characters at a time). Up to you, natch.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
When the going gets weird, the weird turn pro. -- HST