844 lines
26 KiB
Plaintext
844 lines
26 KiB
Plaintext
module 'Spark' 'cat;Input'
|
|
author 'FABLAB BH credits Turgut for the OLED Graphics'
|
|
version 1 1
|
|
depends /Spark_Digital /Spark_Analog /Spark_MP /Spark_PWM /Spark_I2C
|
|
choices ModeMenu Horizontal Vertical
|
|
choices OnOffMenu On Off
|
|
choices VideoMenu Inverse Normal
|
|
choices DispTypeMenu 'OLED_0.96in' 'OLED_2.42in'
|
|
description 'Supports 0.96in and 2.42in OLED Displays with SD1306 and SD1309 chipsets. Comm mode is I2C or SPI.
|
|
Changes:
|
|
- always display buffer
|
|
- defer display updates block
|
|
- remove reveal
|
|
- switch to x (0-127), y (0-63)
|
|
- Pruned commands and vars
|
|
- textX and textY
|
|
- Text at any x and y
|
|
- handles newLines
|
|
- removed dependencies
|
|
- unified data format for chars, sprites, and images
|
|
- image draw at any x,y
|
|
- filled rectangle
|
|
- image draw bug fix
|
|
- stack overflow fix
|
|
- improve conversion ops, i2c io, rect fill, img processing, and buffer displays
|
|
- reduce i2c buffer to 128 bytes
|
|
- reduce i2c buffer to 64 bytes
|
|
- correct internal draws, clean-up vars, restore _clearDisplay
|
|
'
|
|
variables OLEDReady OLEDi2cAddr _GDBuffer _cDecTBL _comma _cTABLE _comMode _dcPin _delayGDUpd _displayType _eol _resetPin _textX _textY _dataPrefix _byteCount _imgData _imgWidth _imgHeight
|
|
|
|
spec ' ' 'OLEDInit_I2C' 'initialize i2c _ address(hex) _ reset pin# _ flip _' 'str.DispTypeMenu auto str bool' 'OLED_0.96in' '3C' '-' false
|
|
spec ' ' 'OLEDInit_SPI' 'initialize spi _ d/c pin# _ reset pin# _ flip _' 'str.DispTypeMenu auto auto bool' 'OLED_2.42in' 16 8 false
|
|
spec ' ' 'OLEDwrite' 'write _ at x _ y _ inverse _' 'auto auto auto bool' 'Hello!' 0 0 false
|
|
spec ' ' 'OLEDshowGDBuffer' 'show display buffer'
|
|
spec ' ' 'OLEDclear' 'clear'
|
|
spec ' ' 'OLEDcontrast' 'set contrast (1-4) _' 'auto' 2
|
|
spec ' ' 'OLEDdrawCircle' 'draw circle at x _ y _ radius _ erase _' 'auto auto auto bool' 64 32 '10' false
|
|
spec ' ' 'OLEDdrawImage' 'draw image _ at x _ y _' 'auto auto auto' 0 0 0
|
|
spec ' ' 'OLEDdrawLine' 'draw line from x _ y _ to x _ y _ erase _' 'auto auto auto auto bool' 0 0 127 63 false
|
|
spec ' ' 'OLEDdrawRect' 'draw rectangle x _ y _ w _ h _ erase _ rounding(3-15) _' 'auto auto auto auto bool auto' 0 0 127 63 false 0
|
|
spec ' ' 'OLEDfillRect' 'fill rectangle x _ y _ w _ h _ erase _' 'auto auto auto auto bool' 20 20 30 20 false
|
|
spec ' ' '_OLEDflip' '_flip display top _' 'bool' false
|
|
spec 'r' 'OLEDmakeImage' 'make image _' 'microbitDisplay' 33084991
|
|
spec ' ' 'OLEDpixel' 'set pixel x _ y _ erase _' 'auto auto bool' 0 0 false
|
|
spec ' ' 'OLEDsetVideo' 'set video _' 'str.VideoMenu' 'Inverse'
|
|
spec 'r' 'OLEDwru' 'cursor location'
|
|
spec ' ' 'defer display updates' 'defer display updates'
|
|
spec ' ' '_GDDRAMoff' '_GDDRAMoff'
|
|
spec ' ' '_GDDRAMon' '_GDDRAMon'
|
|
spec ' ' '_OLEDcursorReset' '_cursor reset'
|
|
spec ' ' '_OLEDsetDisplay' '_set display _' 'str.OnOffMenu' 'On'
|
|
spec ' ' '_OLEDreset' '_set reset Pin# _' 'auto' '0'
|
|
spec ' ' '_SPIWriteCmd' '_SPIWriteCmd'
|
|
spec ' ' '_SPIWriteData' '_SPIWriteData'
|
|
spec ' ' '_T1' '_T1'
|
|
spec ' ' '_T2' '_T2'
|
|
spec ' ' '_T3' '_T3'
|
|
spec 'r' '_cBMP' '_cBMP _ InvYN _' 'auto bool' '' false
|
|
spec ' ' '_clearDisplay' '_clearDisplay'
|
|
spec ' ' '_corner' '_corner _ _ _ _ _' 'auto auto auto auto bool' 'TL' 32 1 0 true
|
|
spec 'r' '_dec2hex' '_dec2hex _' 'auto' 0
|
|
spec ' ' '_initChars' '_initChars'
|
|
spec ' ' '_initCheck' '_initCheck'
|
|
spec ' ' '_initDisplayHW' '_initDisplayHW'
|
|
spec ' ' '_initLibrary' '_initLibrary'
|
|
spec ' ' '_process image data' '_process image data _' 'auto' ' '
|
|
spec ' ' '_sendCmd' '_sendCmd _' 'auto' ''
|
|
spec ' ' '_verifyXY' '_verifyXY _ _' 'auto auto' 0 0
|
|
|
|
to OLEDInit_I2C dispType i2cAddr resetPin flip {
|
|
comment 'Sets display type and interface and initializes HW settings.
|
|
Do NOT set a ResetPin# unless one exists on the display hardware.
|
|
NOTE:
|
|
Displays supported:
|
|
- OLED 0.96in (SD1306 chip) and
|
|
- OLED 2.42" (SD1309 chip)
|
|
Both displays are 128x64 pixels or 16x8 characters in size.
|
|
Displays come in dual mode version (i2c and spi) and pure i2c.
|
|
Pure i2c models do not have a Reset pin.
|
|
|
|
flip setting controls display hardware write direction:
|
|
- false: top to bottom
|
|
- true: bottom to top'
|
|
_comma = ('[data:unicodeString]' 44)
|
|
_comMode = 'i2c'
|
|
OLEDi2cAddr = (hexToInt ('[data:join]' ('[data:unicodeString]' 32) i2cAddr))
|
|
_resetPin = resetPin
|
|
_displayType = (ifExpression (dispType == 'OLED_0.96in') 9 6)
|
|
'_initDisplayHW'
|
|
if flip {
|
|
'_OLEDflip' true
|
|
}
|
|
'_initLibrary'
|
|
OLEDclear
|
|
}
|
|
|
|
to OLEDInit_SPI dispType dcPin resetPin flip {
|
|
comment 'Sets display type and interface and initializes HW settings.
|
|
SPI four wire in Mode-0 is supported. Max speed=10000000.
|
|
|
|
Do NOT set a ResetPin# unless one exists on the display hardware.
|
|
NOTE:
|
|
Displays supported:
|
|
- OLED 0.96in (SD1306 chip) and
|
|
- OLED 2.42" (SD1309 chip)
|
|
Both displays are 128x64 pixels or 16x8 characters in size.
|
|
Displays come in dual mode version (i2c and spi) and pure i2c.
|
|
Pure i2c models do not have a Reset pin.
|
|
flip setting controls display hardware write direction:
|
|
- false: top to bottom
|
|
- true: bottom to top'
|
|
_comma = ('[data:unicodeString]' 44)
|
|
_comMode = 'spi'
|
|
_dcPin = dcPin
|
|
if (_displayType != 0) {
|
|
return
|
|
}
|
|
if (dispType == 'OLED_0.96in') {
|
|
_displayType = 6
|
|
_resetPin = resetPin
|
|
} else {
|
|
_displayType = 9
|
|
_resetPin = resetPin
|
|
}
|
|
'[sensors:spiSetup]' 10000000
|
|
'_initDisplayHW'
|
|
if flip {
|
|
'_OLEDflip' true
|
|
}
|
|
'_initLibrary'
|
|
OLEDclear
|
|
}
|
|
|
|
to OLEDclear {
|
|
comment 'Set bounds to FullScreen and CLEARs display
|
|
and GDBuffer.'
|
|
_GDBuffer = ('[data:newByteArray]' 1024)
|
|
'_initCheck'
|
|
'_OLEDcursorReset'
|
|
OLEDshowGDBuffer
|
|
}
|
|
|
|
to OLEDcontrast contrast {
|
|
comment 'Sets the brightness control of the display to one of four values.
|
|
1 is the least bright, 4 is the brightest setting.'
|
|
'_initCheck'
|
|
local 'cLevels' ('[data:makeList]' 0 '1F' '2F' 'F0')
|
|
if (and (contrast >= 1) (contrast <= 4)) {
|
|
local 'i2cCmd' ('[data:join]' '81' _comma (at contrast cLevels))
|
|
'_sendCmd' i2cCmd
|
|
} else {
|
|
sayIt 'Error in CONTRAST Level'
|
|
}
|
|
}
|
|
|
|
to OLEDdrawCircle cx cy r erase {
|
|
comment 'Bresenham Circle:
|
|
Draws circles, even partially bigger than the display.
|
|
x: 0-127
|
|
y: 0-63
|
|
void plotCircle(int xm, int ym, int r)
|
|
{
|
|
int x = -r, y = 0, err = 2-2*r; /* II. Quadrant */
|
|
do {
|
|
setPixel(xm-x, ym+y); /* I. Quadrant */
|
|
setPixel(xm-y, ym-x); /* II. Quadrant */
|
|
setPixel(xm+x, ym-y); /* III. Quadrant */
|
|
setPixel(xm+y, ym+x); /* IV. Quadrant */
|
|
r = err;
|
|
if (r <= y) err += ++y*2+1; /* e_xy+e_y < 0 */
|
|
if (r > x || err > y) err += ++x*2+1; /* e_xy+e_x > 0 or no 2nd y-step */
|
|
} while (x < 0);
|
|
}'
|
|
'_initCheck'
|
|
local 'oldDelayGDUpd' _delayGDUpd
|
|
_delayGDUpd = (booleanConstant true)
|
|
local 'x' (-1 * r)
|
|
local 'y' 0
|
|
local 'err' (2 - (2 * r))
|
|
repeatUntil (x >= 0) {
|
|
OLEDpixel (cx - x) (cy + y) erase
|
|
OLEDpixel (cx - y) (cy - x) erase
|
|
OLEDpixel (cx + x) (cy - y) erase
|
|
OLEDpixel (cx + y) (cy + x) erase
|
|
r = err
|
|
if (r <= y) {
|
|
y += 1
|
|
err = (err + ((y * 2) + 1))
|
|
}
|
|
if (or (r > x) (err > y)) {
|
|
x += 1
|
|
err = (err + ((x * 2) + 1))
|
|
}
|
|
}
|
|
_delayGDUpd = oldDelayGDUpd
|
|
if (not _delayGDUpd) {
|
|
OLEDshowGDBuffer
|
|
}
|
|
}
|
|
|
|
to OLEDdrawImage image x y {
|
|
comment '_imgHeight has to be mod8 or 1-8'
|
|
if (0 == image) {
|
|
return 0
|
|
}
|
|
local 'oldDelayGDUpd' _delayGDUpd
|
|
_delayGDUpd = (booleanConstant true)
|
|
for row# (maximum 1 ((minimum _imgHeight (64 - y)) / 8)) {
|
|
local 'GDidx' ((((y / 8) * 128) + x) + 1)
|
|
local 'imgDispl' ((row# - 1) * _imgWidth)
|
|
for byte# (minimum _imgWidth (128 - x)) {
|
|
comment 'If y is on page boundary, just copy'
|
|
if (0 == (y % 8)) {
|
|
atPut GDidx _GDBuffer (at (imgDispl + byte#) image)
|
|
GDidx += 1
|
|
if (GDidx > 1024) {
|
|
_delayGDUpd = oldDelayGDUpd
|
|
if (not _delayGDUpd) {
|
|
OLEDshowGDBuffer
|
|
}
|
|
return 0
|
|
}
|
|
} else {
|
|
comment 'Else, copy GDLowBits + shiftedByte + GDHiBits to GDBuffer and GDBuffer +128'
|
|
local 'shiftedByte' ('_dec2hex' ((at (imgDispl + byte#) image) << (y % 8)))
|
|
local 'temp' 0
|
|
local 'tempHI' ('[data:copyFromTo]' shiftedByte 1 2)
|
|
local 'tempLO' ('[data:copyFromTo]' shiftedByte 3 4)
|
|
local 'GDLowBits' ((1 << (y % 8)) - 1)
|
|
local 'GDHiBits' (255 - GDLowBits)
|
|
tempLO = ((hexToInt tempLO) | ((at GDidx _GDBuffer) & GDLowBits))
|
|
atPut GDidx _GDBuffer tempLO
|
|
if ((GDidx + 128) <= 1024) {
|
|
tempHI = ((hexToInt tempHI) | ((at (GDidx + 128) _GDBuffer) & GDHiBits))
|
|
atPut (GDidx + 128) _GDBuffer tempHI
|
|
}
|
|
GDidx += 1
|
|
if (GDidx > 1024) {
|
|
_delayGDUpd = oldDelayGDUpd
|
|
if (not _delayGDUpd) {
|
|
OLEDshowGDBuffer
|
|
}
|
|
return 0
|
|
}
|
|
}
|
|
waitMillis 0
|
|
}
|
|
y += 8
|
|
}
|
|
_delayGDUpd = oldDelayGDUpd
|
|
if (not _delayGDUpd) {
|
|
OLEDshowGDBuffer
|
|
}
|
|
}
|
|
|
|
to OLEDdrawLine x0 y0 x1 y1 erase {
|
|
comment 'Draws a line from x0,y0 to x1,y1 using the Bresenham Algorithm
|
|
x: 0-127
|
|
y: 0-63
|
|
plotLine(int x0, int y0, int x1, int y1)
|
|
dx = abs(x1-x0);
|
|
sx = x0<x1 ? 1 : -1;
|
|
dy = -abs(y1-y0);
|
|
sy = y0<y1 ? 1 : -1;
|
|
err = dx+dy; /* error value e_xy */
|
|
while (true) /* loop */
|
|
plot(x0, y0);
|
|
if (x0 == x1 && y0 == y1) break;
|
|
e2 = 2*err;
|
|
if (e2 >= dy) /* e_xy+e_x > 0 */
|
|
err += dy;
|
|
x0 += sx;
|
|
end if
|
|
if (e2 <= dx) /* e_xy+e_y < 0 */
|
|
err += dx;
|
|
y0 += sy;
|
|
end if
|
|
end while'
|
|
'_initCheck'
|
|
local 'oldDelayGDUpd' _delayGDUpd
|
|
_delayGDUpd = (booleanConstant true)
|
|
results = ('[data:makeList]')
|
|
local 'dx' (absoluteValue (x1 - x0))
|
|
local 'dy' (-1 * (absoluteValue (y1 - y0)))
|
|
local 'err' (dx + dy)
|
|
local 'e2' 0
|
|
local 'done' (booleanConstant false)
|
|
if (x0 < x1) {
|
|
local 'sx' 1
|
|
} else {
|
|
local 'sx' -1
|
|
}
|
|
if (y0 < y1) {
|
|
local 'sy' 1
|
|
} else {
|
|
local 'sy' -1
|
|
}
|
|
repeatUntil done {
|
|
OLEDpixel x0 y0 erase
|
|
if (and (x0 == x1) (y0 == y1)) {
|
|
done = (booleanConstant true)
|
|
}
|
|
e2 = (2 * err)
|
|
if (e2 >= dy) {
|
|
err += dy
|
|
x0 += sx
|
|
}
|
|
if (e2 <= dx) {
|
|
err += dx
|
|
y0 += sy
|
|
}
|
|
}
|
|
_delayGDUpd = oldDelayGDUpd
|
|
if (not _delayGDUpd) {
|
|
OLEDshowGDBuffer
|
|
}
|
|
}
|
|
|
|
to OLEDdrawRect TLx TLy width height erase cornerRad {
|
|
comment 'Draw Rectangle with optional rounded corners with radius R.
|
|
Does not check for reversed rectangle coordinates for round corners.'
|
|
'_initCheck'
|
|
local 'oldDelayGDUpd' _delayGDUpd
|
|
_delayGDUpd = (booleanConstant true)
|
|
local 'BRx' (TLx + width)
|
|
local 'BRy' (TLy + height)
|
|
if (cornerRad >= 3) {
|
|
'_corner' 'TL' TLx TLy cornerRad erase
|
|
'_corner' 'TR' BRx TLy cornerRad erase
|
|
'_corner' 'BR' BRx BRy cornerRad erase
|
|
'_corner' 'BL' TLx BRy cornerRad erase
|
|
comment 'TOP - Adjust x'
|
|
OLEDdrawLine (TLx + cornerRad) TLy ((BRx - 1) - cornerRad) TLy erase
|
|
comment 'RIGHT- Adjust y'
|
|
OLEDdrawLine BRx ((TLy + 1) + cornerRad) BRx ((BRy - 1) - cornerRad) erase
|
|
comment 'BOTTOM - Adjust x'
|
|
OLEDdrawLine ((BRx - 1) - cornerRad) BRy ((TLx + 1) + cornerRad) BRy erase
|
|
comment 'LEFT - Adjust y'
|
|
OLEDdrawLine TLx ((BRy - 1) - cornerRad) TLx ((TLy + 1) + cornerRad) erase
|
|
} else {
|
|
OLEDdrawLine TLx TLy BRx TLy erase
|
|
OLEDdrawLine BRx (TLy + 1) BRx BRy erase
|
|
OLEDdrawLine (BRx - 1) BRy TLx BRy erase
|
|
OLEDdrawLine TLx (BRy - 1) TLx (TLy + 1) erase
|
|
}
|
|
_delayGDUpd = oldDelayGDUpd
|
|
if (not _delayGDUpd) {
|
|
OLEDshowGDBuffer
|
|
}
|
|
}
|
|
|
|
to OLEDfillRect x y w h erase {
|
|
local 'oldDelayGDUpd' _delayGDUpd
|
|
_delayGDUpd = (booleanConstant true)
|
|
for row ('[data:range]' 0 (h - 1)) {
|
|
for col ('[data:range]' 0 (w - 1)) {
|
|
OLEDpixel (x + col) (y + row) erase
|
|
}
|
|
}
|
|
_delayGDUpd = oldDelayGDUpd
|
|
if (not _delayGDUpd) {
|
|
OLEDshowGDBuffer
|
|
}
|
|
}
|
|
|
|
to OLEDmakeImage spr# {
|
|
comment 'Receives a spr# representing 5x5 image matrix.
|
|
Converts it to a HOR array of 5 VERT bitmap numbers.
|
|
This can be displayed with the draw image block.
|
|
sprNum max is 33554431'
|
|
'_initCheck'
|
|
local 'spriteList' ('[data:newByteArray]' 5)
|
|
local 'val' 0
|
|
for col 5 {
|
|
for row ('[data:asByteArray]' ('[data:makeList]' 0 5 10 15 20)) {
|
|
local 'bit' (col + row)
|
|
local 'vertBit#' (bit / 5)
|
|
if ((spr# & (1 << (bit - 1))) != 0) {
|
|
if (col != 5) {
|
|
val += (1 << vertBit#)
|
|
} else {
|
|
val += (1 << (vertBit# - 1))
|
|
}
|
|
}
|
|
}
|
|
atPut col spriteList val
|
|
val = 0
|
|
}
|
|
_imgWidth = 5
|
|
_imgHeight = 5
|
|
return spriteList
|
|
}
|
|
|
|
to OLEDpixel x y erase {
|
|
comment 'Places a pixel at x,y in the virtual GDBuffer
|
|
Use OLEDshowGDBuffer to display it
|
|
8192 Pixels over 1024 Bytes
|
|
x: 0-127
|
|
y: 0-63
|
|
page#: 0-7
|
|
pagePixel#: 0-7
|
|
GDIndex: 1-1024
|
|
|
|
RangeCheck:
|
|
verify x and y are in range of display limits
|
|
if X <= num <= Y'
|
|
'_initCheck'
|
|
local 'pagePixel#' (y % 8)
|
|
local 'GDIndex' ((x + ((y / 8) * 128)) + 1)
|
|
local 'byteBMP' (at GDIndex _GDBuffer)
|
|
byteBMP = (ifExpression (not erase) (byteBMP | (1 << pagePixel#)) (byteBMP & ('~' (1 << pagePixel#))))
|
|
atPut GDIndex _GDBuffer byteBMP
|
|
}
|
|
|
|
to OLEDsetVideo videoMode {
|
|
comment 'Switches the entire display:
|
|
Inverse: inverse video mode (bit 0 = on)
|
|
Normal: normal video mode (bit 1 = on).
|
|
Any image on the display will be preserved when mode changes.'
|
|
'_initCheck'
|
|
if (videoMode == 'Inverse') {
|
|
local 'i2cCmd' 'A7'
|
|
} else {
|
|
local 'i2cCmd' 'A6'
|
|
}
|
|
'_sendCmd' i2cCmd
|
|
}
|
|
|
|
to OLEDshowGDBuffer {
|
|
comment 'Copies contents of virtual _GDBuffer to display
|
|
i2c:
|
|
in 60 byte chunks for speed.
|
|
1 byte is used for the command 40
|
|
spi:
|
|
fastest is dump buffer
|
|
_GDBuffer is in decimal'
|
|
'_initCheck'
|
|
if (_comMode == 'i2c') {
|
|
local 'idx' 0
|
|
repeat 17 {
|
|
'[sensors:i2cWrite]' OLEDi2cAddr ('[data:join]' _dataPrefix ('[data:copyFromTo]' _GDBuffer idx (idx + 60)))
|
|
idx += 61
|
|
}
|
|
} else {
|
|
'_SPIWriteData'
|
|
'[sensors:spiExchange]' ('[data:copyFromTo]' _GDBuffer 1)
|
|
}
|
|
_delayGDUpd = (booleanConstant false)
|
|
}
|
|
|
|
to OLEDwrite string x y invFlag {
|
|
comment 'Writes strings to display at any x,y; processing CR LF and wrapping at col x.
|
|
_textX and _textY are next write locations.'
|
|
'_initCheck'
|
|
local 'oldDelayGDUpd' _delayGDUpd
|
|
_delayGDUpd = (booleanConstant true)
|
|
'_verifyXY' x y
|
|
local 'origX' x
|
|
if (not (isType string 'string')) {
|
|
string = ('[data:join]' '' string)
|
|
}
|
|
for char string {
|
|
comment 'If in table process it - LINE SET is not supported.'
|
|
if (('[data:find]' char _cTABLE) != -1) {
|
|
OLEDdrawImage ('_cBMP' char invFlag) x y
|
|
x += 8
|
|
if (x > 127) {
|
|
x = origX
|
|
y += 8
|
|
if (y > 63) {
|
|
y = 0
|
|
}
|
|
}
|
|
} (13 == ('[data:unicodeAt]' 1 char)) {
|
|
noop
|
|
} (10 == ('[data:unicodeAt]' 1 char)) {
|
|
x = origX
|
|
y += 8
|
|
if (y > 63) {
|
|
y = 0
|
|
}
|
|
} else {
|
|
comment 'bad char - STOP'
|
|
sayIt 'Invalid CHAR value:' char 'uniCode:' ('[data:unicodeAt]' 1 char)
|
|
stopTask
|
|
}
|
|
}
|
|
_textX = x
|
|
_textY = y
|
|
_delayGDUpd = oldDelayGDUpd
|
|
if (not _delayGDUpd) {
|
|
OLEDshowGDBuffer
|
|
}
|
|
}
|
|
|
|
to OLEDwru {
|
|
comment 'Next Row and Column to print
|
|
_textY, _textX'
|
|
return ('[data:asByteArray]' ('[data:makeList]' _textY _textX))
|
|
}
|
|
|
|
to '_GDDRAMoff' {
|
|
comment 'Displays a full empty screen of reverse video.
|
|
It disengages the hardware GDBuffer. '
|
|
local 'i2cCmd' 'A5'
|
|
'_sendCmd' i2cCmd
|
|
}
|
|
|
|
to '_GDDRAMon' {
|
|
comment 'It disables the GDDRAMoff mode and engages the hardware GDBuffer for display content.'
|
|
local 'i2cCmd' 'A4'
|
|
'_sendCmd' i2cCmd
|
|
}
|
|
|
|
to '_OLEDcursorReset' {
|
|
comment 'Sets display bounds to full range and resets cursor
|
|
to the origin top-left (0,0)
|
|
Rows: 0-7
|
|
Cols: 0-127
|
|
Cursor position is affected by any display operation
|
|
and also by OLEDColMode block.'
|
|
'_initCheck'
|
|
local 'cmdString' '22,00,07,21,00,7F'
|
|
'_sendCmd' cmdString
|
|
_textX = 0
|
|
_textY = 0
|
|
}
|
|
|
|
to '_OLEDflip' flip {
|
|
comment 'Flips the display initialization horizontally or vertically.
|
|
Horizontal or vertical is based on the pin connector location.'
|
|
if flip {
|
|
'_sendCmd' 'A0,C0'
|
|
} else {
|
|
'_sendCmd' 'A1,C8'
|
|
}
|
|
}
|
|
|
|
to '_OLEDreset' pin {
|
|
comment 'Does a power off and on on the display,
|
|
thus forcing a hardware initialization.'
|
|
digitalWriteOp pin false
|
|
waitMillis 1
|
|
digitalWriteOp pin true
|
|
waitMillis 1
|
|
}
|
|
|
|
to '_OLEDsetDisplay' onoff {
|
|
comment 'Puts the display into
|
|
Off:SLEEP On:WAKE mode.
|
|
Images on display are preserved.'
|
|
'_initCheck'
|
|
if (onoff == 'On') {
|
|
local 'i2cCmd' 'AF'
|
|
} else {
|
|
local 'i2cCmd' 'AE'
|
|
}
|
|
'_sendCmd' i2cCmd
|
|
}
|
|
|
|
to '_SPIWriteCmd' {
|
|
comment 'In SPI mode, we send either a write command
|
|
or write data control code. Then follow it with
|
|
the appropriate command/data bundle.'
|
|
digitalWriteOp _dcPin false
|
|
}
|
|
|
|
to '_SPIWriteData' {
|
|
comment 'In SPI mode, we send either a write command
|
|
or write data control code. Then follow it with
|
|
the appropriate command/data bundle.'
|
|
digitalWriteOp _dcPin true
|
|
}
|
|
|
|
to '_T1' {
|
|
comment 'THIN-SS Character Set
|
|
Range: space - ?'
|
|
local '_cHEX1' '00000000000000000000005F00000000000007000007000000147F14147F140000242A6B6B2A12000046261008646200304A454D324848000000040300000000001C224100000000000041221C000000082A1C1C1C2A08000008083E080800000000806000000000000808080808080000000060000000000040201008040200003E615149453E000044427F4040000000625151494966000022414949493600101814527F5010000027454545453900003C4A4949493000000301710905030000364949494936000006494949291E00000000660000000000008066000000000008142241000000002424242424240000000041221408000002010151090600'
|
|
local 'idx' 1
|
|
for i 256 {
|
|
atPut i _cDecTBL (hexToInt ('[data:copyFromTo]' _cHEX1 idx (idx + 1)))
|
|
idx += 2
|
|
}
|
|
_cHEX1 = ''
|
|
}
|
|
|
|
to '_T2' {
|
|
comment 'THIN-SS Character Set
|
|
Range: @ - _ (underscore)'
|
|
local '_cHEX2' '003E415D55551E00007C121111127C0000417F4949493600001C22414141220000417F4141221C0000417F495D41630000417F491D010300001C224151517200007F080808087F000000417F4100000000304040413F010000417F081422414000417F4140406000007F01020402017F007F010204087F00003E414141413E0000417F4909090600001E212131215E4000417F49192946000026494949493200000301417F410103003F404040403F00000F10204020100F003F40403840403F004122140814224100010244784402010043615149454361007F4141410000000102040810204000004141417F00000008040201020408008080808080808080'
|
|
local 'idx' 1
|
|
for i 256 {
|
|
atPut (256 + i) _cDecTBL (hexToInt ('[data:copyFromTo]' _cHEX2 idx (idx + 1)))
|
|
idx += 2
|
|
}
|
|
_cHEX2 = ''
|
|
}
|
|
|
|
to '_T3' {
|
|
comment 'THIN-SS Character Set
|
|
Range: '' - . (last char)'
|
|
local '_cHEX3' '0000000304000000002054545454784000017F304848483000384444444428000030484848317F4000385454545418000000487E490102000098A4A4A4A4780400417F08040478000000447D400000000060808080847D0000017F10284440000000417F40000000007C040478040478007C08040404780000384444444438000084FC98242418000018242498FC840000447C480404180000485454545424000004043F44442000003C404040207C00000C10204020100C003C40403840403C0044281028440000009CA0A0A0A07C00004464544C44000000080836414100000000007700000000000041413608080000020101020201000000000000000000'
|
|
local 'idx' 1
|
|
for i 256 {
|
|
atPut (512 + i) _cDecTBL (hexToInt ('[data:copyFromTo]' _cHEX3 idx (idx + 1)))
|
|
idx += 2
|
|
}
|
|
_cHEX3 = ''
|
|
}
|
|
|
|
to '_cBMP' char invFlag {
|
|
comment 'Returns DEC char bitmap from _cHexTbl and
|
|
optionally converts it to inverse (XOR).
|
|
Line segments are NOT supported.
|
|
A: 00,7c,12,11,12,7c,00
|
|
dec: 0,124,18,17,17,124,0
|
|
inv: 255,131,237,238,238,131,255'
|
|
local 'key' (((('[data:unicodeAt]' 1 char) - 32) * 8) + 1)
|
|
local 'charList' ('[data:copyFromTo]' _cDecTBL key (key + 7))
|
|
comment 'inverse'
|
|
if invFlag {
|
|
for item# (size charList) {
|
|
atPut item# charList ((at item# charList) ^ 255)
|
|
}
|
|
}
|
|
_imgWidth = 8
|
|
_imgHeight = 8
|
|
return charList
|
|
}
|
|
|
|
to '_clearDisplay' {
|
|
comment 'Clear Screen without initializing _GDBuffer'
|
|
'_initCheck'
|
|
'_OLEDcursorReset'
|
|
if ('i2c' == _comMode) {
|
|
repeat 17 {
|
|
'[sensors:i2cWrite]' OLEDi2cAddr ('[data:join]' _dataPrefix ('[data:newByteArray]' 60))
|
|
}
|
|
'[sensors:i2cWrite]' OLEDi2cAddr ('[data:join]' _dataPrefix ('[data:newByteArray]' 4))
|
|
} else {
|
|
'_SPIWriteData'
|
|
'[sensors:spiExchange]' ('[data:newByteArray]' 1024)
|
|
}
|
|
}
|
|
|
|
to '_corner' loc cx cy r erase {
|
|
comment 'Calculates and displays the rounded corners for the rectangles.
|
|
loc is one of TL, TR, BL, BR ... topLeft, topright, bottomleft, bottomright
|
|
cx,cy are the corner coordinates for the rounded corner.
|
|
r is the radius in pixels of the arc to be calculated.'
|
|
local 'x' (-1 * r)
|
|
local 'y' 0
|
|
local 'err' (2 - (2 * r))
|
|
if (loc == 'TL') {
|
|
cx += r
|
|
cy += r
|
|
} (loc == 'TR') {
|
|
cx += (-1 * r)
|
|
cy += r
|
|
} (loc == 'BL') {
|
|
cx += r
|
|
cy += (-1 * r)
|
|
} (loc == 'BR') {
|
|
cx += (-1 * r)
|
|
cy += (-1 * r)
|
|
}
|
|
repeatUntil (x >= 0) {
|
|
if (loc == 'TL') {
|
|
OLEDpixel (cx + x) (cy - y) erase
|
|
} (loc == 'TR') {
|
|
OLEDpixel (cx + y) (cy + x) erase
|
|
} (loc == 'BL') {
|
|
OLEDpixel (cx - y) (cy - x) erase
|
|
} (loc == 'BR') {
|
|
OLEDpixel (cx - x) (cy + y) erase
|
|
}
|
|
r = err
|
|
if (r <= y) {
|
|
y += 1
|
|
err = (err + ((y * 2) + 1))
|
|
}
|
|
if (or (r > x) (err > y)) {
|
|
x += 1
|
|
err = (err + ((x * 2) + 1))
|
|
}
|
|
}
|
|
}
|
|
|
|
to '_dec2hex' num {
|
|
comment 'Fast version w/o inversion. (~115uSecs)'
|
|
local '_hexTbl' '0123456789ABCDEF'
|
|
local 'hexNum' ''
|
|
repeatUntil (num < 0) {
|
|
local 'temp' ('[data:join]' (at ((num % 16) + 1) _hexTbl) hexNum)
|
|
hexNum = temp
|
|
num = (ifExpression ((num / 16) != 0) (num / 16) -1)
|
|
}
|
|
comment 'If not half-byte length, pad it.'
|
|
hexNum = ('[data:join]' ('[data:copyFromTo]' '000' 1 (4 - (size hexNum))) hexNum)
|
|
return hexNum
|
|
}
|
|
|
|
to '_initChars' {
|
|
comment 'Creates the character set used in the Library (96 characters)
|
|
used JOIN for SPACE (uni-32) character so it won''t be deleted by mistake.
|
|
For each character, _cDecTbl is updated with 8 byte array values.'
|
|
_cTABLE = ('[data:join]' ('[data:unicodeString]' 32) '!"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~.')
|
|
'_T1'
|
|
'_T2'
|
|
'_T3'
|
|
}
|
|
|
|
to '_initCheck' {
|
|
comment 'Verifies Library initialization status.'
|
|
if (not OLEDReady) {
|
|
sayIt 'Display needs to be initialized before using the library blocks.'
|
|
stopTask
|
|
}
|
|
}
|
|
|
|
to '_initDisplayHW' {
|
|
comment '*** DO NOT CHANGE THESE WITHOUT UNDERSTANDING THEIR MEANINGS ***
|
|
*** IT CAN MAKE THE DISPLAY NOT OPERATE CORRECTLY ***
|
|
|
|
Initializes display HW timings
|
|
OLED INITIALIZATION STRINGS:
|
|
1306: pins are on top
|
|
1309: pins are on the right
|
|
|
|
A8 3F - Multiplex Ratio 0F-3F (16mux to 64mux)
|
|
D3 00 - Display Offset (vertical) 00-63
|
|
40 - Display Start Line 40-7F (40:0)
|
|
A0 - Set Segment Remap A0: Remap 0:0, A1: 127:0 [A1]
|
|
C0 - Scan Direction C0:0-127, C8:127-0 [C8]
|
|
DA 12 - COM pin hw config 02:double-high 12:normal bits
|
|
81 F0 - Set contrast 00-FF (00:dimmest, FF:brightest) [F0]
|
|
A4 - A4:display shows GDRAM, A5:display all ON pixels
|
|
A6 - A6:Normal video, A7:Inverse video
|
|
D5 F0 - Set display clock HB:OSC freq / LB:divide ratio (00-FF)
|
|
D9 22 - Set pre-charge period HB:Phase2 / LB:Phase1 (1-15 DCLK)
|
|
DB 20 - Set Vcomh deselect level 20:1306(~0.77xVcc), 34:1309(~0.78xVcc)
|
|
8D 14 - Charge Pump Setting 10:disable CP, 14:enable CP
|
|
20 00 - Set HORIZONTAL mode
|
|
AF - Display ON/OFF AE:off (sleep mode), AF:on'
|
|
if (_resetPin != '-') {
|
|
'_OLEDreset' _resetPin
|
|
}
|
|
if (9 == _displayType) {
|
|
local 'initCmd' 'A8,3F,D3,00,40,A1,C8,DA,12,81,F0,A4,A6,D5,70,D9,22,DB,34,8D,14,20,00,AF'
|
|
} else {
|
|
local 'initCmd' 'A8,3F,D3,00,40,A1,C8,DA,12,81,F0,A4,A6,D5,70,D9,22,DB,20,8D,14,20,00,AF'
|
|
}
|
|
'_sendCmd' initCmd
|
|
}
|
|
|
|
to '_initLibrary' {
|
|
comment 'Sets all Library variables and initializes the display hardware.
|
|
It also initializes the virtual GDBuffer, and clears the display.
|
|
NOTE:
|
|
Displays supported: OLED1306 (0.96in) and OLED1309 (2.42"), selected via boolean position.
|
|
OLED1309 requires the connection of RESET pin to a digital pin.
|
|
Both displays are 128x64 pixels or 16x8 characters in size.
|
|
Make sure the character hex tables are \n (LF) terminated.
|
|
Otherwise, extra lines are added in between and it gets messed up.
|
|
eg: A: 00,7C,12,11,11,12,7C,00\n
|
|
Max i2c IO is 64 bytes: buffered writes need to be max that size - 16 x 64'
|
|
_comma = ('[data:unicodeString]' 44)
|
|
_eol = ('[data:unicodeString]' 10)
|
|
comment 'Col starts are in HEX'
|
|
_dataPrefix = ('[data:newByteArray]' 1 (hexToInt '40'))
|
|
comment 'There is no FF/255 in th cHexTBL.
|
|
this value is used to build the byteArray.'
|
|
_cDecTBL = ('[data:newByteArray]' 768 255)
|
|
_GDBuffer = ('[data:newByteArray]' 1024)
|
|
_textX = 0
|
|
_textY = 0
|
|
_delayGDUpd = (booleanConstant false)
|
|
'_initChars'
|
|
OLEDReady = (booleanConstant true)
|
|
'_OLEDsetDisplay' 'On'
|
|
repeat 2 {
|
|
setUserLED true
|
|
waitMillis 50
|
|
setUserLED false
|
|
waitMillis 50
|
|
}
|
|
}
|
|
|
|
to '_process image data' image {
|
|
comment 'Processes the new hex image data format.
|
|
Each _imgHex gets added to _imgData in DEC byteArray format.
|
|
_byteCount keeps track of total bytes processed.
|
|
_imgWidth and _imgHeight (mod8) are dimensions.'
|
|
local '_ptr' 1
|
|
comment '_imgData is not initialized yet.'
|
|
if (or (0 == _imgData) (not (isType _imgData 'byte array'))) {
|
|
_imgWidth = (hexToInt ('[data:copyFromTo]' image 1 2))
|
|
_imgHeight = (hexToInt ('[data:copyFromTo]' image 3 4))
|
|
image = ('[data:copyFromTo]' image 5)
|
|
comment 'If _imgHeight is partial byte, adjust to full byte'
|
|
if (not (0 == (_imgHeight % 8))) {
|
|
_imgHeight += (8 - (_imgHeight % 8))
|
|
}
|
|
_imgData = ('[data:newByteArray]' ((_imgWidth * _imgHeight) / 8))
|
|
}
|
|
for byte ('[data:range]' 1 (size image) 2) {
|
|
atPut (_byteCount + _ptr) _imgData (hexToInt ('[data:copyFromTo]' image byte (byte + 1)))
|
|
_ptr += 1
|
|
}
|
|
_byteCount += (_ptr - 1)
|
|
}
|
|
|
|
to '_sendCmd' cmdString {
|
|
comment 'Input is a comma sep. STRING.
|
|
MAKE SURE ALL INPUT PARAMETERS ARE HEX'
|
|
local '_cList' ('[data:split]' cmdString _comma)
|
|
local 'cmdPrefix' (hexToInt '80')
|
|
if ('i2c' == _comMode) {
|
|
for cmd _cList {
|
|
'[sensors:i2cWrite]' OLEDi2cAddr ('[data:asByteArray]' ('[data:makeList]' cmdPrefix (hexToInt cmd)))
|
|
}
|
|
} else {
|
|
'_SPIWriteCmd'
|
|
for cmd _cList {
|
|
spiSend (hexToInt cmd)
|
|
}
|
|
}
|
|
}
|
|
|
|
to '_verifyXY' x y {
|
|
if (and (and (x >= 0) (x <= 127)) (and (y >= 0) (y <= 63))) {
|
|
return 0
|
|
} else {
|
|
sayIt 'x or y value error:' ('[data:unicodeString]' 10) 'x:' x ' y:' y
|
|
stopTask
|
|
}
|
|
}
|
|
|
|
to 'defer display updates' {
|
|
_delayGDUpd = (booleanConstant true)
|
|
}
|
|
|