Home  |  Contents 

Microcontroller and PC projects
  Forum Index : Microcontroller and PC projects         Section
Subject Topic: MM-all: Display drivers in Basic Post ReplyPost New Topic
Author
Message << Prev Topic | Next Topic >>
matherp
Guru
Guru


Joined: 11 December 2012
Location: United Kingdom
Online Status: Online
Posts: 2338
Posted: 12 January 2018 at 2:07am | IP Logged Quote matherp

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





Back to Top View matherp's Profile Search for other posts by matherp
 
Frank N. Furter
Guru
Guru


Joined: 28 May 2012
Location: Germany
Online Status: Offline
Posts: 366
Posted: 14 January 2018 at 5:54am | IP Logged Quote Frank N. Furter

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
Back to Top View Frank N. Furter's Profile Search for other posts by Frank N. Furter
 
matherp
Guru
Guru


Joined: 11 December 2012
Location: United Kingdom
Online Status: Online
Posts: 2338
Posted: 14 January 2018 at 6:06am | IP Logged Quote matherp

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.....
Back to Top View matherp's Profile Search for other posts by matherp
 
Kabron
Newbie
Newbie


Joined: 30 November 2017
Location: Russian Federation
Online Status: Offline
Posts: 13
Posted: 11 February 2018 at 12:26am | IP Logged Quote Kabron

"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 on 11 February 2018 at 12:27am
Back to Top View Kabron's Profile Search for other posts by Kabron
 
matherp
Guru
Guru


Joined: 11 December 2012
Location: United Kingdom
Online Status: Online
Posts: 2338
Posted: 11 February 2018 at 1:54am | IP Logged Quote matherp

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
Back to Top View matherp's Profile Search for other posts by matherp
 
Kabron
Newbie
Newbie


Joined: 30 November 2017
Location: Russian Federation
Online Status: Offline
Posts: 13
Posted: 11 February 2018 at 3:41am | IP Logged Quote Kabron

OPTION LCDPANEL USER, 128,64
Erase S$(), tmaskarray(), bmaskarray()
does not has comment, but produses errors

Edited by Kabron on 11 February 2018 at 3:42am
Back to Top View Kabron's Profile Search for other posts by Kabron
 
matherp
Guru
Guru


Joined: 11 December 2012
Location: United Kingdom
Online Status: Online
Posts: 2338
Posted: 11 February 2018 at 3:43am | IP Logged Quote matherp

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 work

Edited by matherp on 11 February 2018 at 3:58am
Back to Top View matherp's Profile Search for other posts by matherp
 
Kabron
Newbie
Newbie


Joined: 30 November 2017
Location: Russian Federation
Online Status: Offline
Posts: 13
Posted: 11 February 2018 at 11:08pm | IP Logged Quote Kabron

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


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 on 11 February 2018 at 11:08pm
Back to Top View Kabron's Profile Search for other posts by Kabron
 
Pluto
Newbie
Newbie


Joined: 09 June 2017
Location: Finland
Online Status: Offline
Posts: 4
Posted: 11 October 2018 at 9:11pm | IP Logged Quote Pluto

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:
[CODE][/CODE] 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......[CODE][/CODE]

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?


Back to Top View Pluto's Profile Search for other posts by Pluto
 
matherp
Guru
Guru


Joined: 11 December 2012
Location: United Kingdom
Online Status: Online
Posts: 2338
Posted: 11 October 2018 at 10:07pm | IP Logged Quote matherp

Please could you try the code on version 5.04.08 and let us know the results
Back to Top View matherp's Profile Search for other posts by matherp
 
Pluto
Newbie
Newbie


Joined: 09 June 2017
Location: Finland
Online Status: Offline
Posts: 4
Posted: 11 October 2018 at 11:32pm | IP Logged Quote Pluto

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!
Back to Top View Pluto's Profile Search for other posts by Pluto
 


If you wish to post a reply to this topic you must first login
If you are not already registered you must first register

  Search the forums using Google.
In the news...
 
Post ReplyPost New Topic
Printable version Printable version
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot delete your posts in this forum
You cannot edit your posts in this forum
You cannot create polls in this forum
You cannot vote in polls in this forum

Powered by Web Wiz Forums version 7.8
Copyright ©2001-2004 Web Wiz Guide

This page was generated in 0.1250 seconds.
Privacy Policy     Process times : 0, 0, 0, 0.12