Graphics
The graphics support is versatile and supports drawing on displays and LEDs of many types.
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:
Type | Description | {cfg} |
---|---|---|
0 | None (default) | |
1 | I2C Display | The I2C display's {address} . |
2 | SPI Display | The SPI display's {chipselect, control} pins. |
3 | NeoPixel 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). |
4 | LED 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. |
5 | LED Matrix List | - {cfg} contains a 2D list of LEDs pins, with width and height layout. |
Not all types support both direct and buffered modes. See individual modes for supported types.
Drawing
Function | Description |
---|---|
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. |
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.
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
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.
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.
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:
# 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:
# 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.
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.
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