Skip to main content

Graphics


The graphics support is versatile and supports drawing on displays and LEDs of many types.

tip

The graphics libraries do not include any device initialization. An application must initialize the device as necessary with GfxCfg() before using any drawing functions, like in the examples below.

Configuration

Before any drawing operations, the system needs to know what device it will access and what pixel mapping it needs to process internally.

GfxCfg (type, {cfg}, width, height, mode) Is needed to set the device type with width and height. The {cfg} depends on the device type.

When mode is 0 then graphics is sent directly to display. No need for internal buffering and no need for Show(). This uses a lot less memory.

When mode is more than 0 then the engine uses buffering for graphics, where the value is the pixel multiplier. For example, setting mode to 3 will cause the graphics engine to multiply each pixel 3 times horizontally and vertically, 9 pixels in total. A 320x240 display runs at 106x80 pixels.

When selecting to use graphics with mode set to more than 0 (use graphics buffering), make sure the overall memory need is under 10KBytes. 320x240 with 3x multiplier is 106x80, needing 8,480 Bytes.

Supported device types:

TypeDescription{cfg}
0None (default)
1I2C DisplayThe I2C display's {address}.
2SPI DisplayThe SPI display's {chipselect, control} pins.
3NeoPixel WS2812{pin, pwidth, pheight, scan}. Configure for LED matrix connected to pin, with individual panel width pwidth and height pheight, with scan horizontal (0) or vertical (1).
4LED Matrix Scanner{cfg} the first element sets for common-cathode (1) or common-anode (0). Then followed by data pin then scan pins. The width and height are used to determine how many pins are used for data and scan, respectively.
5LED Matrix List- {cfg} contains a 2D list of LEDs pins, with width and height layout.
tip

Not all types support both direct and buffered modes. See individual modes for supported types.


Drawing

FunctionDescription
Clear(color)Clear the buffer to color.
Pixel(color, x, y)Set a color pixel at x,y.
Circle(color, x, y, radius)Draw a color circle at x,y with radius.
Line(color, x1, y1, x2, y2)Draw a color line starting at x1,y1 and ending at x2,y2.
Rect(color, x, y, width, height)Draw a color rectangle at x,y with width and height.
Fill(color, x, y, width, height)Fill an area with color, starting at x,y with width and height.
Text("text", color, x, y)Draw 7x5px text with color at x,y.
TextS("text", color, x, y, scaleWidth, scaleHeight)Same as Text() but adds scaleWidth and scaleHeight
TextT("text", color, x, y)Draw a tine 5x5px text with color at x,y.
Img({image}, x, y, w, h, transform)Draw an image at x,y with transform: 0 = none, 1 = 90 deg, 2 = 180 deg, 3 = 270 deg, 4 = Flip horz, 5 = Flip vert
ImgS({image}, x, y, w, h, transform, scaleWidth, scaleHeight)Same as Img() but adds scaleWidth and scaleHeight
Show()Show graphics buffer on the configured device.
tip

When using buffered mode, the graphics buffer is not cleared automatically. Also, the display is not cleared automatically in direct mode.

Always start with Clear().


Color

All color arguments take 24BPP format RRGGBB, similar to what is used on the web. When using direct mode, the full color is used. In buffered graphics mode, the system store pixels as 8BPP 3R3G2B (256 colors). The 8BPP is scaled from 24BPP RRGGBB. Color pallette is not supported.

In direct mode, drawing goes directly on the display, where Show() doesn't do anything.

tip

Color 1 is a special color that results in white 0xFFFFFF, useful for code samples that work on both color and B&W displays.


Image

Image Array

The Img() function uses a color array bitmap to create an image. These arrays can sometimes be visualized when looking at the array.

Dim a1[8*8] = {
0, 0, 0, 1, 1, 0, 0, 0,
0, 0, 1, 1, 1, 1, 0, 0,
0, 1, 1, 1, 1, 1, 1, 0,
1, 1, 0, 1, 1, 0, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
0, 0, 1, 0, 0, 1, 0, 0,
0, 1, 0, 1, 1, 0, 1, 0,
1, 0, 1, 0, 0, 1, 0, 1}
Clear(0)
Img(a1,50,35,8,8,0)
Show()

This example below creates the same sprite using color, note the use of Alias() to make the code more readable, and change the colors of sections easier.

Color Image Array

Alias(body=0x00ff00,hat=0xff00ff,legs=0x0000FF,back=0xFFFFFF)

Dim a1[8*8] = {
back,back,back, hat, hat,back,back,back,
back,back, hat, hat, hat, hat,back,back,
back,body,body,body,body,body,body,back,
body,body,back,body,body,back,body,body,
body,body,body,body,body,body,body,body,
back,back,body,back,back,body,back,back,
back,legs,back,legs,legs,back,legs,back,
legs,back,legs,back,back,legs,back,legs}
Clear(1)
Img(a1,50,35,8,8,0)
Show()

SPI Display

SPI supports both, direct and buffered modes.

This example uses a 320x240 display, like TFT CP23 in buffered mode with x3 multiplier.

_s = 5
_r = 6
_x = 50
_d = -9
Init()

while 1
clear(0)
text(Str(_x), 0xffffff, 30, 5)
Circle(0xff,_x,50,5)
_x = _x + _d
if(_x < 0 || _x > 106)
_d = _d * -1
end
show()
wend


fn Init()
dwrite(4,1)
dwrite(7,1)
spicfg(0, 24000)
gfxcfg(2, {_s,_r},106,80, 3)# type 2, 106x80 pixels, buffered x3
Cmd(0xc8, [0xFF])
Cmd(0x93, [0xFF])
Cmd(0x36, [0xc8])
Cmd(0x3a, [0x55])
Cmd(0xc0, [0x10,0x10])
Cmd(0xc1, [0x36])
Cmd(0xc5, [0xc3])
Cmd(0xE0, [0x00,0x05,0x08,0x02,0x1a,0x0c,0x42,0x7a,0x54,0x08,0x0d,0x0c,0x23,0x25,0x0f])
Cmd(0xE1, [0x00,0x29,0x2f,0x03,0x0f,0x05,0x42,0x55,0x53,0x06,0x0f,0x0c,0x38,0x3a,0x0f])
Cmd(0x11,[])
Wait(120)
Cmd(0x36, [0xc8])
Cmd(0x2a, [0x00,0x00,0x01,0x3d])
Cmd(0xE1, [0x00,0x00,0x00,0xef])
Cmd(0x29,[])
fend

fn Cmd(c, b1)
##SendCmd(c)
dwrite(_s, 0)#select
dwrite(_r, 0)#cmd
SpiWr(c)
dwrite(_r, 1)#data
for i in range(0,Len(b1))
SpiWr(b1[i])
next
dwrite(_s, 1)#deselect
fend

In direct mode, you will likely not clear the entire screen is this will cause flicker. Only erase specific regions. Enjoy the full resolution and full color from any Supported System!

_s = 5
_r = 6
_x = 320/2
_d = -9
Init()

clear(0)
TextS("Amazing DUELink!", 0xFFA500, 20, 5,3,5)
for _i in range(0, 255, 10) #5bit color in 5:6:5 format
_c = _i #5 bit to 8 bit
_q = (0xFF -_i) # invert count on color
Line(_q<<8 | _c, 30, 80, _i+30, 240)
Line(_q<<16 | _c, 280, 80, 280-_i, 240)
next


while 1
Circle(0xFF00ff, _x, 100, 8)
wait(10)
Circle(0, _x, 100, 8)# clear the circle area, not the whole screen
_x=_x+_d
if(_x < 120 || _x > 320-120)
_d = _d * -1
end
wend


fn Init()
dwrite(4,1)
dwrite(7,1)
spicfg(0, 24000)
gfxcfg(2, {_s,_r},320,240, 0)# type 2, 320x240 pixels, direct
Cmd(0xc8, [0xFF])
Cmd(0x93, [0xFF])
Cmd(0x36, [0xc8])
Cmd(0x3a, [0x55])
Cmd(0xc0, [0x10,0x10])
Cmd(0xc1, [0x36])
Cmd(0xc5, [0xc3])
Cmd(0xE0, [0x00,0x05,0x08,0x02,0x1a,0x0c,0x42,0x7a,0x54,0x08,0x0d,0x0c,0x23,0x25,0x0f])
Cmd(0xE1, [0x00,0x29,0x2f,0x03,0x0f,0x05,0x42,0x55,0x53,0x06,0x0f,0x0c,0x38,0x3a,0x0f])
Cmd(0x11,[])
Wait(120)
Cmd(0x36, [0xc8])
Cmd(0x2a, [0x00,0x00,0x01,0x3d])
Cmd(0xE1, [0x00,0x00,0x00,0xef])
Cmd(0x29,[])
fend

fn Cmd(c, b1)
##SendCmd(c)
dwrite(_s, 0)#select
dwrite(_r, 0)#cmd
SpiWr(c)
dwrite(_r, 1)#data
for i in range(0,Len(b1))
SpiWr(b1[i])
next
dwrite(_s, 1)#deselect
fend

I2C Display

I2C supports buffered mode only, with x1 multiplier.

In this case, we are using SSD1306. This display is found on PixoBit microcomputer and OLED 096 display.

dim b1[2]
_x = 50
_d = -9
Init()


while 1
clear(1)
texts(Str(_x), 0, 50, 5, 2, 2)
Circle(0, _x, 50, 5)
_x=_x+_d
if(_x < 0 || _x > 106)
_d = _d * -1
end
show()
wend

fn Init()
dwrite(11, 1) # reset pin
# config I2C bus with 400Kz
i2ccfg(400)
Wait(20)
gfxcfg(1,{0x3C}, 128, 64, 1)# type 1, 128x64 pixels, buffered x1


SendCmd(0xAE):SendCmd(0x00):SendCmd(0x10)
SendCmd(0x40):SendCmd(0x81):SendCmd(0xCF)
SendCmd(0xA1):SendCmd(0xA6):SendCmd(0xA8)
SendCmd(0x3F):SendCmd(0xD3):SendCmd(0x00)
SendCmd(0xD5):SendCmd(0x80):SendCmd(0xD9)
SendCmd(0xF1):SendCmd(0xDA):SendCmd(0x12)
SendCmd(0xDB):SendCmd(0x40):SendCmd(0x8D)
SendCmd(0x14):SendCmd(0xAF):SendCmd(0xC8)
SendCmd(0x20):SendCmd(0x00):SendCmd(0x21)
SendCmd(0):SendCmd(128-1)
SendCmd(0x22):SendCmd(0):SendCmd(7)
fend

fn SendCmd(c)
b1[0] = 0
b1[1] = c
i2cwr(0x3c, b1, 0)
fend

NeoPixel Smart LEDs

NeoPixel supports buffered graphics only, with x1 multiplier.

Typical NeoPixel displays consists of one or more panels. Each panel has LEDs connected in a zigzag pattern.

NeoPixel Zigzag

Calculating the absolute pixel position can be challenging, but not to worry as the graphics engine handles all that automatically.

When connecting panels horizontally the panels need to be ordered follows:

Horizontal signal path

# 3 horizontal 16x16 panelsm creating 48x16 display
dim a1[]={1,# Pin used
16, # Individual panel width
16, # Individual panel height
0} # Horizontal scanning
GfxCfg(3, a1, 48, 16, 1)# type 3, 48x16 leds, buffered x1

_x = 6

while 1
TextS("DUELink", 5, _x, 0, 1, 2)
Show()
_x = _x + 1
if _x < -20
_x = 6
end
wait(100)
wend

When connecting panels in a matrix, to build up larger "panels", they need to be connected as follows:

Horizontal signal path

# 4 panels placed as 2x2, where each one is 16x16 pixels, creating 48x48 pixel displays
dim a1[]= {1,# Pin used
16, # Individual panel width
16, # Individual panel height
0} # Horizontal scanning
GfxCfg(3, a1, 48, 48, 1)# type 3, 48x48 leds, buffered x1

_x = 6

while 1
TextS("DUELink", 5, _x, 0, 1, 2)
Show()
_x = _x + 1
if _x < -20
_x = 6
end
wait(100)
wend

It is also possible to connect panels vertically by simply ordering them exactly same as the matrix above, but only use the first left column.

## 3 vertical panels of 16x16 each creating 16x48 display
dim a1[]={1,# Pin used
16, # Individual panel width
16, # Individual panel height
0} # Horizontal scanning
GfxCfg(3, a1, 16, 48, 1)# type 3, 16x48 leds, buffered x1

_x = 6

while 1
TextS("DUELink", 5, _x, 0, 1, 2)
Show()
_x = _x + 1
if _x < -20
_x = 6
end
wait(100)
wend

It is possible to make your own panels using LED strips. For example, 5 strips of 50 LED each can be used to make a 50x5 LED panel. The strips needs to be connected in a zigzag pattern. This makes it easier to run the connections anyway.

This is a partial image to demonstrate the connections.

LED strip signal path

dim a1[]={1, # Pin used
50, # Individual panel width
5, # Individual panel height
0} # Horizontal scanning
GfxCfg(3,a1,50,5, 1)# type 3, 50x5 leds, buffered x1

_x = 50

while 1
TextT("DUELink", 5, _x, 0)
Show()
_x =_x + 1
if _x < -20
_x = 50
end
wait(100)
wend

A single strip of NeoPixel LEDs is nothing but a panel with a single row. This example assumes a strip of 50 pixels.

dim a1[]={1, #pin used
50, #individual panel width
1} #individual panel height
GfxCfg(3, a1, 50, 1, 1)

_x = 0

while 1
Pixel(0x110011, _x, 0)
Show()
_x =_x + 1
if _x > 50
_x = 0
Clear(0)
end
wend

LED Matrix Scanner

This is a perfect match for CincoBit with its 5x5 LED matrix. This type supports buffered mode, with 1x multiplier only.

CincoBit

dim a1[10] = {0,11,19,18,21,22,7,5,0,8,6} # first 5 for data, second 5 for scan

gfxcfg(4, a1, 5, 5, 1)# type 4: 5x5 type, 1: buffered x1

while 1
TextT("DUELink", 1, _x, 0)
Show()
_x = _x - 1
if _x < -20
_x = 6
end
wait(100)
end

wait(250)
clear(0)

wend

The scanner also works great for other LED modules, such as LED S404 and others. The data are used to set the individual segments and the scanner pins sweep through the individual digits.

The LED driver helps in setting the right pixels to show the wanted digits.

dim a1[13] = {1,1,2,3,4,5,6,7,8,9,10,11,12} # common pin, first 8 for data, second 4 for scan

gfxcfg(4, a1, 8, 4, 1)# type 4: 7seg (8 leds) x 4 digits, 1: buffered x1

# Show number 1 by turning on the right 2 LEDs on the first segment
Clear(0)
Pixel(1,2,0)
Pixel(1,1,0)
Show()

LED Matrix List

In this mode, the graphics engine scans and updates the LEDs one at a time. This mode works great with LED MT1208. In this mode, an array containing a list of each LED pins. This is only supported in buffered mode, with x1 multiplier.

dim a1[]={1,12,
12,1,
1,3,
3,1,
12,3,#5
3,12,
1,4,
4,1,
12,4,
4,12,#10
3,4,
4,3,
1,5,
5,1,
12,5,#15
5,12,
3,5,
5,3,
4,5,
5,4,#20
1,13,
13,1,
12,13,
13,12, #24
## add the rest, to 96!




}
GfxCfg(5, a1, 12, 8, 1)# type 5, 12x8 leds, buffered x1

_x = 6
while 1
Text("DUELink", 0xFFFFFF, _x, 0)
Show()
_x = _x + 1
if _x < -20
_x = 6
end
wait(100)
wend