![]() |
Forum Index : Microcontroller and PC projects : MM-all: Display drivers in Basic
Author | Message | ||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 10000 |
The latest release of the MM2, MM+ and MMX firmware includes the ability to write drivers for all sorts of displays entirely in Basic. To write a display driver is now simple with no "C" knowledge needed. Of course this comes with a performance impact but for smaller displays and many applications this shouldn't be an issue. Once a display driver is written all of the in-built commands of the MM2, MM+ and MMX can then be used in the same way as for any in-built display. One big plus is that the same code can be used on the MM2, MM+ and MMX with no changes needed other than if different pins are used. A display driver comprises just three routines 1. initialise the display 2. output a filled rectangle to the display (a single pixel is a very small rectangle ![]() sub MM.USER_RECTANGLE x1%, y1%, x2%, y2%, c% ' code to draw a rectangle with the colour c% end sub 3. output a rectangular bitmap to the display. sub MM.USER_BITMAP x1%, y1%, width%, height%, scale%, fc%, bc%, bitmap% ' code to display a bitmap with foreground colour fc% and background bc% ' bitmap% is the address of the bitmap. Use PEEK() to get the data. ' each byte is a horizontal row of pixels starting with the most significant bit end sub The initialisation of the display can be done in the main program or in the library and the other two routines are subroutines with defined names and parameters. This is all best illustrated by looking a Basic display driver for one of the SSD1306 OLEDs, in this case driven over an I2C bus with just a two wire connection. I've tried to comment this as much as possible. The complexity is always in converting the simple drawing instruction to draw a rectangle or bitmap into the required sequence to implement this on any specific type of display if you write your own display drivers please add to this thread userdisplayinit line 0,0,mm.hres-1,mm.vres-1 line mm.hres-1,0,0,mm.vres-1 box 0,0,mm.hres,mm.vres text mm.hres/2,mm.vres/2,"Hello World",CM,8 end sub userdisplayinit ' ' The SSD1306 display is 128 x 64 monochrome pixels ' This is arranged as 128 bytes x 8 bytes so a byte will cover the pixels in 8 rows of the display ' this makes it a bit tricky to program as we have to remember what is in the other 7 rows if we want to update a pixel in any specific row ' In this example we will hold a display image in a string array with 8 elements each 128 bytes long ' option explicit option default none on error skip 'if OPTION is already set then ignore the error OPTION LCDPANEL USER, 128,64 on error skip 'if global variabels aren't yet set ignore the error erase S$(), tmaskarray(), bmaskarray() DIM S$(7) length 128 'this is the array that stores the screen image DIM integer tmaskarray(7)=(255,254,252,248,240,224,192,128) ' mask for writing pixels left to right DIM integer bmaskarray(7)=(1,3,7,15,31,63,127,255) 'mask for pixels right to left local i% on error skip ' if I2C already open ignore the error i2c open 400,1000 ' ' send the intialisation instructions to the display ' OLED.SCmd(&HAE)'DISPLAYOFF) OLED.SCmd(&HD5)'DISPLAYCLOCKDIV OLED.SCmd(&H80)'the suggested ratio &H80 OLED.SCmd(&HA8)'MULTIPLEX OLED.SCmd(&H3F)' OLED.SCmd(&HD3)'DISPLAYOFFSET OLED.SCmd(&H0)'no offset OLED.SCmd(&H40)'STARTLINE OLED.SCmd(&H8D)'CHARGEPUMP OLED.SCmd(&H14) OLED.SCmd(&H20)'MEMORYMODE OLED.SCmd(&H00)'&H0 act like ks0108 OLED.SCmd(&HA1)'SEGREMAP OR 1 OLED.SCmd(&HC8)'COMSCANDEC OLED.SCmd(&HDA)'COMPINS OLED.SCmd(&H12) OLED.SCmd(&H81)'SETCONTRAST OLED.SCmd(&HCF) OLED.SCmd(&Hd9)'SETPRECHARGE OLED.SCmd(&HF1) OLED.SCmd(&HDB)'VCOMDETECT OLED.SCmd(&H40) OLED.SCmd(&HA4)'DISPLAYALLON_RESUME OLED.SCmd(&HA6)'NORMALDISPLAY OLED.SCmd(&HAF)'DISPLAYON ' ' Set the memory image of the display to all zeroes ' for i% = 0 to 7 s$(i%)=string$(128,chr$(0)) next i% ' ' Refresh the display with the new blank image ' update 0, 0, MM.HRES-1, MM.VRES\8 -1 end sub ' ' This routine outputs a rectangle to the display. Thi limiting case is a single pixel ' The calling sequence is defined and must be adhered to ' The parameters are the coordinates of one of the extreme diagonals of the rectangle but we don't know which way diagonal. ' sub MM.USER_RECTANGLE(x1%, y1%, x2%, y2%, fcol%) local integer i, j, k, l, t, mask, top_row, bottom_row, top_start, bottom_end ' print "userrectangle ", x1%, y1%, x2%, y2%, fcol% ' ' this next section of code checks the parameters supplied and organises them ' this code will be common to any driver ' if x2% <= x1% then 'make sure x1 is less than x2 t = x1% x1% = x2% x2% = t endif if y2% <= y1% then 'make sure y1 is less than y2 t = y1% y1% = y2% y2% = t endif if x1% < 0 then x1% = 0 if x1% >= MM.HRES then x1% = MM.HRES - 1 if x2% < 0 then x2% = 0 if x2% >= MM.HRES then x2% = MM.HRES - 1 if y1% < 0 then y1% = 0 if y1% >= MM.VRES then y1% = MM.VRES - 1 if y2% < 0 then y2% = 0 if y2% >= MM.VRES then y2% = MM.VRES - 1 ' establish some useful constants for future calculation top_row=y1%\8 bottom_row=y2%\8 top_start=y1%-(top_row*8) bottom_end=y2%-(bottom_row*8) ' ' first deal with the special case where the vertical pixels in the rectangle are within a single byte ' if top_row = bottom_row then if fcol% then 'create a mask to set or clear the requisite pixels mask = (tmaskarray(top_start) AND bmaskarray(bottom_end)) else mask = notmask%(tmaskarray(top_start) AND bmaskarray(bottom_end)) endif for i=x1% to x2% 'loop through the colums and set or clear the required pixels l= peek(var S$(top_row),i+1) if (fcol%) then l=l OR mask else l=l AND mask endif poke var S$(top_row),i+1, l next i else ' otherwise deal more generally if (top_row+1) <= bottom_row then for i=x1% to x2% 'first deal with the top byte affected l = peek(var S$(top_row),i+1) if (fcol%) then l=l OR tmaskarray(top_start) else l = l AND (notmask%(tmaskarray(top_start))) endif poke var S$(top_row),i+1, l ' now deal with bottom byte affected l= peek(var S$(bottom_row),i+1) if fcol% then l=l OR bmaskarray(bottom_end) else l=l AND (notmask%(bmaskarray(bottom_end))) endif poke var S$(bottom_row),i+1, l next i endif 'now deal with bytes in the middle that will be set completely on or completely off if (top_row+1<bottom_row) then for j=top_row+1 to bottom_row-1 for i=x1% to x2% l= peek(var S$(j),i+1) if fcol% then l=&HFF else l=0 endif poke var S$(j),i+1, l next i next j endif endif update x1%,top_row, x2%, bottom_row end sub ' ' ' output a bitmap to the screen ' the bitmap is supplied as a pointer to an area of memory so we use ' peek(byte bitmap%+x) to access the x'th byte in the bitmap ' each byte is a horizontal row of pixels starting with the most significant bit ' e.g. for an 8x8 bitmap ' Byte0Bit7, Byte0Bit6, Byte0Bit5, Byte0Bit4, Byte0Bit3, Byte0Bit2, Byte0Bit1, Byte0Bit0 ' Byte1Bit7, ........., ........., ........., ........., ........., ........., ......... ' Byte2Bit7, ........., ........., ........., ........., ........., ........., ......... ' Byte3Bit7, ........., ........., ........., ........., ........., ........., ......... ' Byte4Bit7, ........., ........., ........., ........., ........., ........., ......... ' Byte5Bit7, ........., ........., ........., ........., ........., ........., ......... ' Byte6Bit7, ........., ........., ........., ........., ........., ........., ......... ' Byte7Bit7, ........., ........., ........., ........., ........., ........., Byte7bit0 ' sub MM.USER_BITMAP(x1%, y1%, width%, height%, scale%, fcol%, bcol%, bitmap%) ' print "userbitmap ", x1%, y1%, width%, height%, scale%, fcol%, bcol% local INTEGER i, j, k, mask, m, l, ll, t, tt, vCd, hCd, x, y, a%=height% * width%, ln%, c1%, c2%, c3%, c4% vCd = y1% if y1% < 0 then y1% = 0 ' the y coord is above the top of the screen for i = 0 to height%-1 ' step thru the font scan line by line ln%=i * width% for j = 0 to scale%-1 ' repeat lines to scale the font in the y axis vCd=vCd+1 if vCd >= 0 then ' we are above the top of the screen y=vCd - 1 c1%=y \ 8 mask = 1 << (y MOD 8 ) c3%=notmask%(mask) if vCd > MM.VRES then goto D_UP ' we have extended beyond the bottom of the screen so exit hCd = x1% for k = 0 to width%-1 ' step through each bit in a scan line c2%=ln% + k c4%=(a% - c2% - 1) mod 8 t=peek(BYTE bitmap% + c2%\8) tt = (t >> c4%) AND 1 for m = 0 to scale% -1 ' repeat pixels to scale in the x axis hCd = hCd +1' we have not reached the left margin if hCd >= 0 then x=hCd -1 if hCd <= MM.HRES then ' check are we beyond the right margin ll= peek(var S$(c1%),hCd) if tt then if fcol% then ll = ll OR mask else ll = ll AND c3% endif else if bcol%<>-1 then 'transparent text if bcol% then ll = ll OR mask else ll = ll AND c3% endif endif endif poke var S$(c1%), hCd, ll endif endif next m next k endif next j next i D_UP: update x1%, y1% \ 8, x, y\ 8 end sub ' ' Send a command to the display ' SUB OLED.SCmd(Comnd%) I2C WRITE &H3C,0,2,&H00,Comnd% 'address must be hardcoded to use at command line END SUB ' ' Set the cursor position on the display ' SUB OLED.SCz(x%,y%) 'set the cursor position on the display local xn%=x% OLED.SCmd(&HB0+y%) 'set page address OLED.SCmd(&H10+(xn%>>4 AND &H0F)) 'set high col address OLED.SCmd(&H00+(xn% AND &H0f)) 'set low col address END SUB ' ' Update the screen between the x coordinates specified and the 8 row block specified sub update(x1%, tr%, x2% , br%) local b$, i% if x1%< MM.HRES then for i% = tr% to br% if i%<MM.VRES\8 then OLED.SCz(x1%,i%) b$=CHR$(&H40)+mid$(s$(i%),x1%+1, x2%-x1%+1) I2C WRITE &H3C,0,len(b$),b$ endif next i% endif end sub ' ' generate the ones complement of a byte ' function notmask%(x%) notmask% = (-1 XOR x%) AND &HFF end function DefineFont #8 5C200806 00000000 82200000 00800008 00004551 4F510000 0045F994 2B1CEA21 690000C2 B04C2090 AA104A62 84608046 00000000 40108410 81400081 00841004 A89C8A00 82000080 0080203E 30000000 00000042 0000003E 01000000 21000086 00004208 CAAA2972 82210027 00872008 21842072 42F8800F 00270A04 F824C510 0FFA0041 00270A02 8A3C0831 21F80027 00044108 8A9C2872 28720027 0027089E 61008601 86010080 00846000 40208410 0F000081 0000F880 10028140 20720084 00022084 BA9A2072 28720027 8028FAA2 8ABC28F2 2872002F 00278220 8AA248E2 08FA004E 800F823C 823C08FA 28720008 80278A2E 8ABE288A 82708028 00872008 12044138 4A8A0046 8048A230 82200882 6A8B800F 80288AAA 9AAA2C8A 28728028 00278AA2 82BC28F2 28720008 8046AAA2 A2BC28F2 087A8048 002F081C 200882F8 288A0082 00278AA2 89A2288A 288A0042 00A5AAAA 5208258A 288A8028 00822094 420821F8 0471800F 00074110 A9944AA9 4170804A 00471004 00804821 00000000 800F0000 00008140 07000000 80277A02 8A320B82 0700002F 00278220 8AA62608 07008027 0007FA22 41382431 E8010004 002778A2 8A320B82 06208028 00872008 12040310 09820046 0089C228 20088260 0D000087 8028AA2A 8A320B00 07008028 00278A22 F2220F00 06000008 802078A6 82320B00 07000008 002F7020 41100E41 08000023 80668AA2 89A20800 08000042 00A5AAA2 21940800 08008048 002778A2 21840F00 0400800F 0EE24028 End DefineFont |
||||
Frank N. Furter Guru ![]() Joined: 28/05/2012 Location: GermanyPosts: 935 |
That's fantastic - but... I don't understand anything about this code... What is tmaskarray() and bmaskarray() about? Can you explain that in more detail? My goal would be to build a MMBasic wristwatch with a Sharp LS013B4DN04 display - unfortunately the display is the crux for me... Frank |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 10000 |
The pixels in this display are arranged as 128 columns of 8 bytes. So one byte covers 8 rows. Say I want to illuminate just the pixel in row 5. I need to leave the other pixels untouched and just set bit 5 If we look at the 5th element of arrays tmaskarray() and bmaskarray() and AND them together we get 224 AND 63 or in bits 11100000 AND 00111111 = 00100000. That is easy and could have been done with 1<<5. Now think about setting bits 3-5 and play with the binary..... |
||||
Kabron![]() Regular Member ![]() Joined: 30/11/2017 Location: GermanyPosts: 65 |
"Hw:"MM.DEVICE$" - firmware:"MM.VER Hw:Micromite eXtreme, Microchip ID 0x17209053 - firmware: 5.0409 On error skip 'if OPTION is already set then ignore the error Error: Syntax OPTION LCDPANEL USER, 128,64 Error: Unrecognised option On error skip 'if global variabels aren't yet set ignore the error Error: Syntax Erase S$(), tmaskarray(), bmaskarray() Error: Cannot find S etc...etc... |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 10000 |
There is a known bug in ON ERROR on all Micromites that you cannot have a comment after the command. Not sure why my listing above has them ![]() |
||||
Kabron![]() Regular Member ![]() Joined: 30/11/2017 Location: GermanyPosts: 65 |
OPTION LCDPANEL USER, 128,64 Erase S$(), tmaskarray(), bmaskarray() does not has comment, but produses errors |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 10000 |
OPTION LCDPANEL USER, 128,64 should be done outside the program then it will work |
||||
Kabron![]() Regular Member ![]() Joined: 30/11/2017 Location: GermanyPosts: 65 |
Finally, I made it works with the latest FW. Definitelly! But it is not a permanent option. It does not remain after reset. What can be done to solve it? |
||||
Pluto Guru ![]() Joined: 09/06/2017 Location: FinlandPosts: 374 |
I got by accident a SSD1306 display with I2C. (Ordered the SPI version). I noticed that matherp had published a driver also for this display! I updated a 44-pin micromite with the latest firmware (Micromite_V5.04.09.hex) and tested the driver. Initially all seemed OK, but after rewriting the display 9 times the program stops with an error. Sub MM.USER_BITMAP(x1%, y1%, width%, height%, scale%, fcol%, bcol%, bitmap%) Error: Not enough memory The test program was simply: dim as integer J userdisplayinit for J=1 to 20 cls line 0,0,mm.hres-1,mm.vres-1 line mm.hres-1,0,0,mm.vres-1 cls box 0,0,mm.hres,mm.vres text mm.hres/2,mm.vres/4,"SSD1306 0.96''",CM,8 text mm.hres/2,mm.vres/2,"OLED DISPLAY",CM,8 text mm.hres/2,mm.vres/4*3,"Pluto",CM,8 pause 1000 print J next J end sub userdisplayinit ...... continues with mtherp's driver...... Any ideas about how to get the display work for more than 9 times? Seems that the driver stores all bitmaps sent to the display without erasing old ones after the CLS (clear screen) command? |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 10000 |
Please could you try the code on version 5.04.08 and let us know the results |
||||
Pluto Guru ![]() Joined: 09/06/2017 Location: FinlandPosts: 374 |
I tried with 5.04.08 initially, but it did not work. Meanwhile I found your Ccode version of the driver. It works fine and it is OK for me. Many thanks matherp! |
||||
disco4now![]() Guru ![]() Joined: 18/12/2014 Location: AustraliaPosts: 964 |
Driver for 0.91" SSD3306 128*32 I created a driver for this display based on Peters code above. This is the LCD. SSD3306 I2C 128*32 Banggood The changes are. OPTION LCDPANEL USER,128,32 During initialization two changes: OLED.SCmd(&HA8)'MULTIPLEX 'OLED.SCmd(&H3F)' 3F required for 128x64 display OLED.SCmd(&H1F)' 1F required for 128x32 display OLED.SCmd(&HDA)'COMPINS 'OLED.SCmd(&H12)'12 for 128*64 OLED.SCmd(&H02)'02 for 128*32 The buffer required is only half the size so for i% = 0 to 3 'was 0 to 7 s$(i%)=string$(128,chr$(0)) next i% ![]() Latest F4 Latest H7 FotS |
||||
nutson Newbie ![]() Joined: 01/07/2019 Location: NetherlandsPosts: 14 |
Is this feature embedded in the Armite? I am running 5.05.08 on a H743ZI2 board, and hesitating what display to connect. I have several unused VGA displays standing around and might want to drive one with a homebrew parallel interface VGA controller, but would need to write a display driver in MMBASIC for that. Regards to all, Nico Hattink |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 10000 |
Yes. I've just run the attached simple test program on the ArmmiteF4. Should work on the H7 as well. Just fill in the two subs with the code to drive your display. Option explicit Option default integer On error skip 'if OPTION is already set then ignore the error Option LCDPANEL USER, 128,64 ' Print MM.HRes Print MM.VRes Pixel 10,10,RGB(green) Text 10,20,"H",,,,RGB(red),RGB(Blue) End ' Sub MM.USER_RECTANGLE(x1, y1, x2, y2, c) Print "Rectangle :"; Print x1,y1,x2,y2,Hex$(c) End Sub ' Sub MM.USER_BITMAP(x, y, w, h, s, fc, bc, bm) Local i,t Print "Bitmap :", Print x,y,w,h,s,Hex$(fc)+" ",Hex$(bc) For i=0 To h-1 Print Bin$(Peek(BYTE bm+i)) Next i End Sub Edited 2019-10-01 03:11 by matherp |
||||
nutson Newbie ![]() Joined: 01/07/2019 Location: NetherlandsPosts: 14 |
Thanks again for the quick reply, and the simple demonstration that this usefull feature works. Regards Nico Hattink |
||||
disco4now![]() Guru ![]() Joined: 18/12/2014 Location: AustraliaPosts: 964 |
Again to confirm that the ARMMITE F4 does support user defined LCDPanels written in MMBasic. This driver from memory has a little glitch, if you attempt to display a right justified text, that uses the scale option in the text command and it results in the text being beyond the x=0 co-ordinate it breaks.Fixing this is an exercise left for the reader. ![]() The NRF socket has 3.3v, GND and the I2C-SCL and I2C-SDA pins available and is the ideal place to connect the i2C display. If you write a USER defined driver please add it to this thread. regards Gerry Latest F4 Latest H7 FotS |
||||
lizby Guru ![]() Joined: 17/05/2016 Location: United StatesPosts: 3299 |
Interesting. Thanks for that. PicoMite, Armmite F4, SensorKits, MMBasic Hardware, Games, etc. on fruitoftheshed |
||||
ajkw Senior Member ![]() Joined: 29/06/2011 Location: AustraliaPosts: 290 |
Hi, Thanks Peter for providing the Basic and Csub drivers for these Oled screens. For sometime I have been using a particular SH1106 128x64 screen with the CSub SD1306 driver but have lived with an issue of a 2 pixel horizontal left offset in which the 2 right hand columns don't work and are filled with gibberish and columns 0 & 1 are offscreen to the left - so I have 126 usable columns and 2 columns of random gibberish. After studing the Basic driver today I have found that a simple change (see code below) allows for the full use of the screen. Great but... The 1306 Csub is bit banged for i2c and can use pins other than the i2c hardware pins 17,18 (28 Pin Pic 170). This allows me to have the display on 15,16 and an RTC on 17,18. The Basic driver uses the hardware i2c pins 17,18 and that generates a conflict in my application. As the change to the basic driver is simple enough I am hopeful it would also be simple for the Csub driver and I appreciatively ask if the SD1306 driver could be modified and perhaps renamed as a SH1106 driver. I have also found that with the Basic driver I can change the 'brightness' in a running program by writing to &H81 with a value between 00 & FF, this would be great to reduce the burn-in that occurs with these screens - eg. bright when interacting and dull during standby. Perhaps, also?, the Csub could have a little addition to set the contrast please? 'using Basic Drive OLED.SCmd(&H81)'SETCONTRAST OLED.SCmd(&Hxx Edit. It might be ok (?) to have the rtc and display on the same i2c hardware pins but I would appreciate the performance of a Csub and perhaps others would find the ability to change the contrast a benefit also. Thanks, Anthony. The fix for the Basic driver is OLED.SCz(x1%+2,i%) 'x1%+2 for SH1106, x1% for SD1306 ' Update the screen between the x coordinates specified and the 8 row block specified sub update(x1%, tr%, x2% , br%) local b$, i% if x1%< MM.HRES then for i% = tr% to br% if i%<MM.VRES\8 then OLED.SCz(x1%+2,i%) 'x1%+2 for SH1106, x1% for SD1306 b$=CHR$(&H40)+mid$(s$(i%),x1%+1, x2%-x1%+1) I2C WRITE &H3C,0,len(b$),b$ endif next i% endif end sub Edited 2021-10-30 16:03 by ajkw |
||||
jwettroth![]() Regular Member ![]() Joined: 02/08/2011 Location: United StatesPosts: 75 |
New User Basic LCD driver for SED1565 and similar mono LCD's. The code below and photo is a LCDPANE User driver for a 100 x 64 pixel mono LCD based on the SED1565 driver. This driver is similar to the UC1609 Mono LCD driver. Its a very low power SPI display driver with onboard charge pumps, for operation from 2.7 to 5.5v. Supply current is about .35 mA active. I have a bunch of these displays from an old project and thought getting them to talk to the MMITE would be a nice challenge. The code here is heavily cribbed from the SSD1306 OLED display. These displays have a similar layout and pretty minor changes were require- explained in the code. ![]() 'SED1565 LCD DRIVER TEST CODE ' THIS IS A USER LCDPANEL DRIVER FOR DISPLAYS USING THE EPSON SED1565 AND ' COMPATIBLE (UC1609). THESE ARE MONOCHROME LCD DRIVERS. THE PARTICULAR ' DISPLAY HERE IS 100 X 64. THERE IS AN 8X13 FONT EMBEDDED. ALL SEEMS TO ' WORK THOUGH IT IS QUITE SLOW. ' 'Wettroth 7/1//22 ' ' Started with a ssd1306 OLED driver that has a similar memory layout but ' different init routine, minor syntax diffs and is SPI vs I2C. The syntax ' diff is that the OLED display or'ed data with 40 hex. 'BEGIN ' 'Pins 'Connections Display on SPI- EBLCD Board 'DIN 3 DOUT <DS PIN 8> SPI OUT 'SCL 7 DOUT <DS PIN 7> SPI CLK 'CS 5 DOUT <DS PIN 4> ACT HI 'A0 2 DOUT <DS PIN 6> GPIO 'RST 4 DOUT <DS PIN 5> GPIO ' 'ARCHIVED AS REV1 ' 'REV 2- '----------------------------------------------------------------------------- 'Basic Test Code ***************** userdisplayinit 'init display 'Standard User Display Test Line 0,0,MM.HRes-1,MM.VRes-1 Line MM.HRes-1,0,0,MM.VRes-1 Box 0,0,MM.HRes-1,MM.VRes-1 Text MM.HRes/2,MM.VRes/2,"Hello World","CM",8 End 'basic end- driver code follows '------------------------------------------------------------------- 'LCD DRIVER CODE- MMite User LCDPANEL Format ' Preliminary- Global '-------------------------------------------------------------- 'init routine - main entry, setup hw and declare vars Sub userdisplayinit Option explicit Option default none On error skip Option LCDPANEL USER, 100,64 On error skip Erase S$(), tmaskarray(), bmaskarray() Dim S$(7) length 100 'this is the array that stores the screen image Dim integer tmaskarray(7)=(255,254,252,248,240,224,192,128) ' mask for writing pixels left to right Dim integer bmaskarray(7)=(1,3,7,15,31,63,127,255) 'mask for pixels right to left Local i% ' HW SETUP 'SET DEFAULT PIN STATES BEFORE DDR Pin(5)=0 'CS LOW, NOTE ACTIVE HIGH Pin(2)=0 'A0 LOW= CMD, HIGH DATA Pin(4)=0 'RESET DISPLAY Pin(24)=0 'LOGIC TRIGGER- DEBUG 'SET DDR'S SetPin 2,DOUT 'A0 COMMAND/DATA SetPin 4,DOUT 'RST SetPin 5,DOUT 'CS SetPin 24,DOUT 'AUX- DEBUG, LOGIC TRIGGER 'DEBUG Pin(24)=1 'START RISE- MAIN TRIGGER Print "MAIN TRIGGER" SPI OPEN 1000000,0,8 '1M Pause 5 'WAIT 5 MS WITH RESET HIGH Pin(4)=1 'RESET INACTIVE 'END HW SETUP- DISPLAY IS RESET, CS IS LOW- INACTIVE '---- ' send the intialisation instructions to the display ' Pin(2)=0 'A0 LOW- COMMAND Pin(5)=1 'CS ACTIVE SPI WRITE 11,&HE2,&HA3,&HA6,&HA1,&HC0,&H23,&H81,&H2B,&H2F,&HAF,&H40 Pin(5)=0 ' 'INITIALIZED- create array and clear ' Set the memory image of the display to all zeroes ' For i% = 0 To 7 'PAGES s$(i%)=String$(100,Chr$(0)) 'ZERO OUT Next i% ' ' Refresh the display with the new blank image ' update 0, 0, MM.HRes-1, MM.VRes\8 -1 End Sub '-----------------------end of userdisplayinit ------------------------------ ' standard driver requires two routines- ' 1. output a filled rectangle to display ' 2. output an image ' FILLED RECTANGLE Sub MM.USER_RECTANGLE(x1%, y1%, x2%, y2%, fcol%) Local integer i, j, k, l, t, mask, top_row, bottom_row, top_start, bottom_end 'Print X1%,X2%,Y1%,Y2%,FCOL% 'limit checks If x2% <= x1% Then t = x1% :x1% = x2% :x2% = t 'lim x2>x1 swap If y2% <= y1% Then t = y1% :y1% = y2% :y2% = t 'lim y2>y1 swap If x1% < 0 Then x1% = 0 If x1% >= MM.HRes Then x1% = MM.HRes - 1 If x2% < 0 Then x2% = 0 If x2% >= MM.HRes Then x2% = MM.HRes - 1 If y1% < 0 Then y1% = 0 If y1% >= MM.VRes Then y1% = MM.VRes - 1 If y2% < 0 Then y2% = 0 If y2% >= MM.VRes Then y2% = MM.VRes - 1 ' establish some useful constants for future calculation top_row=y1%\8 bottom_row=y2%\8 top_start=y1%-(top_row*8) bottom_end=y2%-(bottom_row*8) ' 'there are a number of cases that need to be handled 'case1- vertical pixels in the rectangle are within a single byte ' If top_row = bottom_row Then If fcol% Then 'create a mask to set or clear the requisite pixels mask = (tmaskarray(top_start) And bmaskarray(bottom_end)) Else mask = notmask%(tmaskarray(top_start) And bmaskarray(bottom_end)) EndIf For i=x1% To x2% 'loop through the colums and set or clear the required pixels l= Peek(var S$(top_row),i+1) If (fcol%) Then l=l Or mask Else l=l And mask EndIf Poke var S$(top_row),i+1, l Next i Else ' otherwise deal more generally If (top_row+1) <= bottom_row Then For i=x1% To x2% 'first deal with the top byte affected l = Peek(var S$(top_row),i+1) If (fcol%) Then l=l Or tmaskarray(top_start) Else l = l And (notmask%(tmaskarray(top_start))) EndIf Poke var S$(top_row),i+1, l ' now deal with bottom byte affected l= Peek(var S$(bottom_row),i+1) If fcol% Then l=l Or bmaskarray(bottom_end) Else l=l And (notmask%(bmaskarray(bottom_end))) EndIf Poke var S$(bottom_row),i+1, l Next i EndIf 'now deal with bytes in the middle that will be set completely on or completely off If (top_row+1<bottom_row) Then For j=top_row+1 To bottom_row-1 For i=x1% To x2% l= Peek(var S$(j),i+1) If fcol% Then l=&HFF Else l=0 EndIf Poke var S$(j),i+1, l Next i Next j EndIf EndIf update x1%,top_row, x2%, bottom_row End Sub 'end filled rectangle '----------------------------------------- 'DISPLAY A BIT MAP CODE ' ' output a bitmap to the screen ' the bitmap is supplied as a pointer to an area of memory so we use ' peek(byte bitmap%+x) to access the x'th byte in the bitmap ' each byte is a horizontal row of pixels starting with the most significant bit ' e.g. for an 8x8 bitmap ' Byte0Bit7, Byte0Bit6, Byte0Bit5, Byte0Bit4, Byte0Bit3, Byte0Bit2, Byte0Bit1, Byte0Bit0 ' Byte1Bit7, ........., ........., ........., ........., ........., ........., ......... ' Byte2Bit7, ........., ........., ........., ........., ........., ........., ......... ' Byte3Bit7, ........., ........., ........., ........., ........., ........., ......... ' Byte4Bit7, ........., ........., ........., ........., ........., ........., ......... ' Byte5Bit7, ........., ........., ........., ........., ........., ........., ......... ' Byte6Bit7, ........., ........., ........., ........., ........., ........., ......... ' Byte7Bit7, ........., ........., ........., ........., ........., ........., Byte7bit0 ' Sub MM.USER_BITMAP(x1%, y1%, width%, height%, scale%, fcol%, bcol%, bitmap%) 'Print "userbitmap ", x1%, y1%, width%, height%, scale%, fcol%, bcol% Local INTEGER i, j, k, mask, m, l, ll, t, tt, vCd, hCd, x, y, a%=height% * width%, ln%, c1%, c2%, c3%, c4% vCd = y1% If y1% < 0 Then y1% = 0 ' the y coord is above the top of the screen For i = 0 To height%-1 ' step thru the font scan line by line ln%=i * width% For j = 0 To scale%-1 ' repeat lines to scale the font in the y axis vCd=vCd+1 If vCd >= 0 Then ' we are above the top of the screen y=vCd - 1 c1%=y \ 8 mask = 1 << (y Mod 8 ) c3%=notmask%(mask) If vCd > MM.VRes Then GoTo D_UP ' we have extended beyond the bottom of the screen so exit hCd = x1% For k = 0 To width%-1 ' step through each bit in a scan line c2%=ln% + k c4%=(a% - c2% - 1) Mod 8 t=Peek(BYTE bitmap% + c2%\8) tt = (t >> c4%) And 1 For m = 0 To scale% -1 ' repeat pixels to scale in the x axis hCd = hCd +1' we have not reached the left margin If hCd >= 0 Then x=hCd -1 If hCd <= MM.HRes Then ' check are we beyond the right margin ll= Peek(var S$(c1%),hCd) If tt Then If fcol% Then ll= ll Or mask Else ll = ll And c3% Else If bcol%<>-1 Then 'transparent text If bcol% Then ll = ll Or mask Else ll= ll And C3% EndIf EndIf EndIf Poke var S$(c1%), hCd, ll EndIf EndIf Next m Next k EndIf Next j Next i D_UP: update x1%, y1% \ 8, x, y\ 8 End Sub '--------------------------------- 'local subs ' Send a command to the display ' Sub EBLC.SCmd(Comnd%) Pin(2)=0 'A0 LOW- COMMAND Pin(5)=1 'CS ACTIVE SPI WRITE 1,Comnd% Pin(5)=0 End Sub ' ' Set the cursor position on the display ' Sub EBLC.SCz(x%,y%) 'set the cursor position on the display Local xn%=x% Pin(2)=0 'CMD Pin(5)=1 'CS ACTIVE SPI WRITE 3,&HB0+y%,&H10+(XN%>>4 And &H0F),&H00+(XN% And &H0F) Pin(5)=0 'CS INACTIVE End Sub ' '------------------------------------------------------------------------------------------------ ' Update the screen between the x coordinates specified and the 8 row block specified Sub update(x1%, tr%, x2% , br%) Local b$, i% If x1%< MM.HRes Then For i% = tr% To br% If i%<MM.VRes\8 Then EBLC.SCz(x1%,i%) b$=Mid$(s$(i%),x1%+1, x2%-x1%+1) Pin(2)=1 'A0 HI- DATA Pin(5)=1 'CS ACTIVE SPI WRITE Len(B$),B$ Pin(5)=0 'CS INACTIVE EndIf Next i% EndIf End Sub '--------------------------------------------------------------------------------------------- ' generate the ones complement of a byte ' Function notmask%(x%) notmask% = (-1 Xor x%) And &HFF End Function 'BIT MAP DefineFont #8 5C200806 00000000 82200000 00800008 00004551 4F510000 0045F994 2B1CEA21 690000C2 B04C2090 AA104A62 84608046 00000000 40108410 81400081 00841004 A89C8A00 82000080 0080203E 30000000 00000042 0000003E 01000000 21000086 00004208 CAAA2972 82210027 00872008 21842072 42F8800F 00270A04 F824C510 0FFA0041 00270A02 8A3C0831 21F80027 00044108 8A9C2872 28720027 0027089E 61008601 86010080 00846000 40208410 0F000081 0000F880 10028140 20720084 00022084 BA9A2072 28720027 8028FAA2 8ABC28F2 2872002F 00278220 8AA248E2 08FA004E 800F823C 823C08FA 28720008 80278A2E 8ABE288A 82708028 00872008 12044138 4A8A0046 8048A230 82200882 6A8B800F 80288AAA 9AAA2C8A 28728028 00278AA2 82BC28F2 28720008 8046AAA2 A2BC28F2 087A8048 002F081C 200882F8 288A0082 00278AA2 89A2288A 288A0042 00A5AAAA 5208258A 288A8028 00822094 420821F8 0471800F 00074110 A9944AA9 4170804A 00471004 00804821 00000000 800F0000 00008140 07000000 80277A02 8A320B82 0700002F 00278220 8AA62608 07008027 0007FA22 41382431 E8010004 002778A2 8A320B82 06208028 00872008 12040310 09820046 0089C228 20088260 0D000087 8028AA2A 8A320B00 07008028 00278A22 F2220F00 06000008 802078A6 82320B00 07000008 002F7020 41100E41 08000023 80668AA2 89A20800 08000042 00A5AAA2 21940800 08008048 002778A2 21840F00 0400800F 0EE24028 End DefineFont John Wettroth |
||||
athlon1900 Regular Member ![]() Joined: 10/10/2019 Location: AustriaPosts: 49 |
Many thanks , good job ![]() |
||||
![]() |