Home
JAQForum Ver 20.06
Log In or Join  
Active Topics
Local Time 20:36 28 Apr 2024 Privacy Policy
Jump to

Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.

Forum Index : Microcontroller and PC projects : MM-all: Display drivers in Basic

Author Message
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 8583
Posted: 04:07pm 11 Jan 2018
Copy link to clipboard 
Print this post

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: Germany
Posts: 813
Posted: 07:54pm 13 Jan 2018
Copy link to clipboard 
Print this post

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 Kingdom
Posts: 8583
Posted: 08:06pm 13 Jan 2018
Copy link to clipboard 
Print this post

  Quote  What is tmaskarray() and bmaskarray() about? Can you explain that in more detail?


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: Germany
Posts: 65
Posted: 02:26pm 10 Feb 2018
Copy link to clipboard 
Print this post

"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...Edited by Kabron 2018-02-12
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 8583
Posted: 03:54pm 10 Feb 2018
Copy link to clipboard 
Print this post

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 sorry
 
Kabron

Regular Member

Joined: 30/11/2017
Location: Germany
Posts: 65
Posted: 05:41pm 10 Feb 2018
Copy link to clipboard 
Print this post

OPTION LCDPANEL USER, 128,64
Erase S$(), tmaskarray(), bmaskarray()
does not has comment, but produses errors Edited by Kabron 2018-02-12
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 8583
Posted: 05:43pm 10 Feb 2018
Copy link to clipboard 
Print this post

  Quote  OPTION LCDPANEL USER, 128,64
does not has comment, but produses error


OPTION LCDPANEL USER, 128,64 should be done outside the program then it will workEdited by matherp 2018-02-12
 
Kabron

Regular Member

Joined: 30/11/2017
Location: Germany
Posts: 65
Posted: 01:08pm 11 Feb 2018
Copy link to clipboard 
Print this post

Finally, I made it works with the latest FW.
  matherp said  

OPTION LCDPANEL USER, 128,64 should be done outside the program then it will work

Definitelly!
But it is not a permanent option. It does not remain after reset.
What can be done to solve it?Edited by Kabron 2018-02-12
 
Pluto
Guru

Joined: 09/06/2017
Location: Finland
Posts: 329
Posted: 11:11am 11 Oct 2018
Copy link to clipboard 
Print this post

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 Kingdom
Posts: 8583
Posted: 12:07pm 11 Oct 2018
Copy link to clipboard 
Print this post

Please could you try the code on version 5.04.08 and let us know the results
 
Pluto
Guru

Joined: 09/06/2017
Location: Finland
Posts: 329
Posted: 01:32pm 11 Oct 2018
Copy link to clipboard 
Print this post

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: Australia
Posts: 844
Posted: 05:46am 16 Nov 2018
Copy link to clipboard 
Print this post

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%



  Quote  

'https://www.banggood.com/0_91-Inch-128x32-IIC-I2C-Blue-OLED-LCD-Display-DIY-Oled-Module-SSD1306-Driver-IC-D C-3_3V-5V-p-1140506.html?cur_warehouse=CN

on error skip 'if OPTION is already set then ignore the error
OPTION LCDPANEL USER, 128,32


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 0.91" SSD1306 display is 128 x 32 monochrome pixels
' This is arranged as 128 bytes x 4 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 global variabels aren't yet set ignore the error
erase S$(), tmaskarray(), bmaskarray()
DIM S$(3) 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)' 3F required for 128x64 display
OLED.SCmd(&H1F)' 1F required for 128x32 display
'*********************************************************************************************************************** ***********

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 Horizontal Address Mode

OLED.SCmd(
&HA1)'SEGREMAP OR 1 A0 for Reverse direction

OLED.SCmd(
&HC8)'COMSCANDEC C0 for Reverse Landscape

OLED.SCmd(
&HDA)'COMPINS
'*********************************************************************************************************************** ***********
'OLED.SCmd(&H12)'12 for 128*64
OLED.SCmd(&H02)'02 for 128*32
'*********************************************************************************************************************** ***********

OLED.SCmd(
&H81)'SETCONTRAST
OLED.SCmd(&HCF) 'CF

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 3
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. The 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, ........., ........., ........., ........., ........., ........., Byte3bit0

'
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





Edited by disco4now 2018-11-17
Latest F4 Latest H7
 
nutson
Newbie

Joined: 01/07/2019
Location: Netherlands
Posts: 14
Posted: 08:44am 30 Sep 2019
Copy link to clipboard 
Print this post

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 Kingdom
Posts: 8583
Posted: 05:10pm 30 Sep 2019
Copy link to clipboard 
Print this post

  Quote  Is this feature embedded in the Armite?


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: Netherlands
Posts: 14
Posted: 06:52pm 01 Oct 2019
Copy link to clipboard 
Print this post

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: Australia
Posts: 844
Posted: 10:04am 20 Feb 2021
Copy link to clipboard 
Print this post

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.

  Quote  'Geekcreit 0.91 Inch 128x32 IIC I2C Blue OLED LCD Display DIY Module SSD1306 Driver IC DC 3.3V 5V
'https://www.banggood.com/0_91-Inch-128x32-IIC-I2C-Blue-OLED-LCD-Display-DIY-Oled-Module-SSD1306-Driver-IC-DC-3_3V-5V-p-1140506.html?cur_warehouse=CN
' I2C is pins I2C-SCL PB6 (NRF_CE)
'             I2C-SDA PB7 (NRF_CS)
'  NRF socket viewed with USB cpnnector up ie
'
'                 USB HERE
'                ---------------------------------
'                |3.3V |I2C-SDA |SPI-OUT | PWM2A |
'                ---------------------------------
'                |GND  |I2C-SCL |SPI-CLK |SPI-IN |
'                ---------------------------------
'

on error skip 'if OPTION is already set then ignore the error
OPTION LCDPANEL USER, 128,32

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,7

end
sub UserDisplayInit
'
' The 0.91" SSD1306 display is 128 x 32 monochrome pixels
' This is arranged as 128 bytes x 4 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 global variabels aren't yet set ignore the error
 erase S$(), tmaskarray(), bmaskarray()
 
DIM S$(3) 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)' 3F required for 128x64 display
  OLED.SCmd(&H1F)' 1F required for 128x32 display
 '**********************************************************************************************************************************
 
 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 Horizontal Address Mode
 
 OLED.SCmd(
&HA1)'SEGREMAP OR 1 A0 for Reverse direction
 
 OLED.SCmd(
&HC8)'COMSCANDEC C0 for Reverse Landscape
 
 OLED.SCmd(
&HDA)'COMPINS
 '**********************************************************************************************************************************
 'OLED.SCmd(&H12)'12 for 128*64
 OLED.SCmd(&H02)'02 for 128*32  
 '**********************************************************************************************************************************
 
 OLED.SCmd(
&H81)'SETCONTRAST
 OLED.SCmd(&HCF) 'CF
 
 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 3
   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. The 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, ........., ........., ........., ........., ........., ........., Byte3bit0

'
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


If you write a USER defined driver please add it to this thread.

regards
Gerry
Latest F4 Latest H7
 
lizby
Guru

Joined: 17/05/2016
Location: United States
Posts: 3015
Posted: 12:45pm 20 Feb 2021
Copy link to clipboard 
Print this post

Interesting. Thanks for that.
PicoMite, Armmite F4, SensorKits, MMBasic Hardware, Games, etc. on fruitoftheshed
 
ajkw
Senior Member

Joined: 29/06/2011
Location: Australia
Posts: 290
Posted: 06:00am 30 Oct 2021
Copy link to clipboard 
Print this post

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 States
Posts: 70
Posted: 12:49am 27 Jul 2022
Copy link to clipboard 
Print this post

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: Austria
Posts: 48
Posted: 02:26am 27 Jul 2022
Copy link to clipboard 
Print this post

  jwettroth said  New User Basic LCD driver for SED1565 and similar mono LCD's.


Many thanks , good job  
 
Print this page


To reply to this topic, you need to log in.

© JAQ Software 2024