add u8g.fb_rle display
This commit is contained in:
parent
19f80abb32
commit
492ee578ae
|
@ -75,6 +75,10 @@
|
||||||
U8G_DISPLAY_TABLE_ENTRY(ssd1306_128x64_hw_spi) \
|
U8G_DISPLAY_TABLE_ENTRY(ssd1306_128x64_hw_spi) \
|
||||||
|
|
||||||
#undef U8G_DISPLAY_TABLE_ENTRY
|
#undef U8G_DISPLAY_TABLE_ENTRY
|
||||||
|
|
||||||
|
// Special display device to provide run-length encoded framebuffer contents
|
||||||
|
// to a Lua callback:
|
||||||
|
#define U8G_DISPLAY_FB_RLE
|
||||||
//
|
//
|
||||||
// ***************************************************************************
|
// ***************************************************************************
|
||||||
|
|
||||||
|
|
|
@ -2,24 +2,14 @@
|
||||||
|
|
||||||
#include "module.h"
|
#include "module.h"
|
||||||
#include "lauxlib.h"
|
#include "lauxlib.h"
|
||||||
|
#include "lmem.h"
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
|
|
||||||
#include "c_stdlib.h"
|
|
||||||
|
|
||||||
#include "u8g.h"
|
#include "u8g.h"
|
||||||
|
#include "u8g_glue.h"
|
||||||
|
|
||||||
#include "u8g_config.h"
|
#include "u8g_config.h"
|
||||||
|
|
||||||
struct _lu8g_userdata_t
|
|
||||||
{
|
|
||||||
u8g_t u8g;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct _lu8g_userdata_t lu8g_userdata_t;
|
|
||||||
|
|
||||||
// shorthand macro for the u8g structure inside the userdata
|
|
||||||
#define LU8G (&(lud->u8g))
|
|
||||||
|
|
||||||
|
|
||||||
// helper function: retrieve and check userdata argument
|
// helper function: retrieve and check userdata argument
|
||||||
static lu8g_userdata_t *get_lud( lua_State *L )
|
static lu8g_userdata_t *get_lud( lua_State *L )
|
||||||
|
@ -986,8 +976,6 @@ uint8_t u8g_com_esp8266_ssd_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, voi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// device destructor
|
// device destructor
|
||||||
static int lu8g_close_display( lua_State *L )
|
static int lu8g_close_display( lua_State *L )
|
||||||
{
|
{
|
||||||
|
@ -996,6 +984,19 @@ static int lu8g_close_display( lua_State *L )
|
||||||
if ((lud = get_lud( L )) == NULL)
|
if ((lud = get_lud( L )) == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
if (lud->cb_ref != LUA_NOREF) {
|
||||||
|
// this is the fb_rle device
|
||||||
|
u8g_dev_t *fb_dev = LU8G->dev;
|
||||||
|
u8g_pb_t *fb_dev_pb = (u8g_pb_t *)(fb_dev->dev_mem);
|
||||||
|
uint8_t *fb_dev_buf = fb_dev_pb->buf;
|
||||||
|
|
||||||
|
luaM_free( L, fb_dev_buf );
|
||||||
|
luaM_free( L, fb_dev_pb );
|
||||||
|
luaM_free( L, fb_dev );
|
||||||
|
|
||||||
|
luaL_unref( L, lud->cb_ref, LUA_REGISTRYINDEX );
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1016,6 +1017,7 @@ static int lu8g_close_display( lua_State *L )
|
||||||
return luaL_error( L, "i2c address required" ); \
|
return luaL_error( L, "i2c address required" ); \
|
||||||
\
|
\
|
||||||
lu8g_userdata_t *lud = (lu8g_userdata_t *) lua_newuserdata( L, sizeof( lu8g_userdata_t ) ); \
|
lu8g_userdata_t *lud = (lu8g_userdata_t *) lua_newuserdata( L, sizeof( lu8g_userdata_t ) ); \
|
||||||
|
lud->cb_ref = LUA_NOREF; \
|
||||||
\
|
\
|
||||||
lud->u8g.i2c_addr = (uint8_t)addr; \
|
lud->u8g.i2c_addr = (uint8_t)addr; \
|
||||||
lud->u8g.use_delay = del > 0 ? 1 : 0; \
|
lud->u8g.use_delay = del > 0 ? 1 : 0; \
|
||||||
|
@ -1049,6 +1051,7 @@ U8G_DISPLAY_TABLE_I2C
|
||||||
unsigned del = luaL_optinteger( L, 4, 0 ); \
|
unsigned del = luaL_optinteger( L, 4, 0 ); \
|
||||||
\
|
\
|
||||||
lu8g_userdata_t *lud = (lu8g_userdata_t *) lua_newuserdata( L, sizeof( lu8g_userdata_t ) ); \
|
lu8g_userdata_t *lud = (lu8g_userdata_t *) lua_newuserdata( L, sizeof( lu8g_userdata_t ) ); \
|
||||||
|
lud->cb_ref = LUA_NOREF; \
|
||||||
\
|
\
|
||||||
lud->u8g.use_delay = del > 0 ? 1 : 0; \
|
lud->u8g.use_delay = del > 0 ? 1 : 0; \
|
||||||
\
|
\
|
||||||
|
@ -1064,6 +1067,53 @@ U8G_DISPLAY_TABLE_I2C
|
||||||
// Unroll the display table and insert binding functions for SPI based displays.
|
// Unroll the display table and insert binding functions for SPI based displays.
|
||||||
U8G_DISPLAY_TABLE_SPI
|
U8G_DISPLAY_TABLE_SPI
|
||||||
//
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// This display forwards the framebuffer contents as run-length encoded chunks to a Lua callback
|
||||||
|
static int lu8g_fb_rle( lua_State *L ) {
|
||||||
|
lu8g_userdata_t *lud;
|
||||||
|
|
||||||
|
if ((lua_type( L, 1 ) != LUA_TFUNCTION) &&
|
||||||
|
(lua_type( L, 1 ) != LUA_TLIGHTFUNCTION)) {
|
||||||
|
luaL_typerror( L, 1, "function" );
|
||||||
|
}
|
||||||
|
|
||||||
|
int width = luaL_checkint( L, 2 );
|
||||||
|
int height = luaL_checkint( L, 3 );
|
||||||
|
|
||||||
|
luaL_argcheck( L, (width > 0) && (width < 256) && ((width % 8) == 0), 2, "invalid width" );
|
||||||
|
luaL_argcheck( L, (height > 0) && (height < 256) && ((height % 8) == 0), 3, "invalid height" );
|
||||||
|
|
||||||
|
// construct display device structures manually because width and height are configurable
|
||||||
|
uint8_t *fb_dev_buf = (uint8_t *)luaM_malloc( L, width );
|
||||||
|
|
||||||
|
u8g_pb_t *fb_dev_pb = (u8g_pb_t *)luaM_malloc( L, sizeof( u8g_pb_t ) );
|
||||||
|
fb_dev_pb->p.page_height = 8;
|
||||||
|
fb_dev_pb->p.total_height = height;
|
||||||
|
fb_dev_pb->p.page_y0 = 0;
|
||||||
|
fb_dev_pb->p.page_y1 = 0;
|
||||||
|
fb_dev_pb->p.page = 0;
|
||||||
|
fb_dev_pb->width = width;
|
||||||
|
fb_dev_pb->buf = fb_dev_buf;
|
||||||
|
|
||||||
|
u8g_dev_t *fb_dev = (u8g_dev_t *)luaM_malloc( L, sizeof( u8g_dev_t ) );
|
||||||
|
fb_dev->dev_fn = u8g_dev_gen_fb_fn;
|
||||||
|
fb_dev->dev_mem = fb_dev_pb;
|
||||||
|
fb_dev->com_fn = u8g_com_esp8266_fbrle_fn;
|
||||||
|
|
||||||
|
lud = (lu8g_userdata_t *) lua_newuserdata( L, sizeof( lu8g_userdata_t ) );
|
||||||
|
lua_pushvalue( L, 1 ); // copy argument (func) to the top of stack
|
||||||
|
lud->cb_ref = luaL_ref( L, LUA_REGISTRYINDEX );
|
||||||
|
|
||||||
|
/* set metatable for userdata */
|
||||||
|
luaL_getmetatable(L, "u8g.display");
|
||||||
|
lua_setmetatable(L, -2);
|
||||||
|
|
||||||
|
u8g_Init8BitFixedPort( LU8G, fb_dev, width, height, 0, 0, 0);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
//
|
||||||
// ***************************************************************************
|
// ***************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
@ -1137,6 +1187,8 @@ static const LUA_REG_TYPE lu8g_map[] = {
|
||||||
#define U8G_FONT_TABLE_ENTRY(font) \
|
#define U8G_FONT_TABLE_ENTRY(font) \
|
||||||
{ LSTRKEY( #font ), LUDATA( (void *)(u8g_ ## font) ) },
|
{ LSTRKEY( #font ), LUDATA( (void *)(u8g_ ## font) ) },
|
||||||
U8G_FONT_TABLE
|
U8G_FONT_TABLE
|
||||||
|
//
|
||||||
|
{ LSTRKEY( "fb_rle" ), LFUNCVAL( lu8g_fb_rle ) },
|
||||||
// Options for circle/ ellipse drawing
|
// Options for circle/ ellipse drawing
|
||||||
{ LSTRKEY( "DRAW_UPPER_RIGHT" ), LNUMVAL( U8G_DRAW_UPPER_RIGHT ) },
|
{ LSTRKEY( "DRAW_UPPER_RIGHT" ), LNUMVAL( U8G_DRAW_UPPER_RIGHT ) },
|
||||||
{ LSTRKEY( "DRAW_UPPER_LEFT" ), LNUMVAL( U8G_DRAW_UPPER_LEFT ) },
|
{ LSTRKEY( "DRAW_UPPER_LEFT" ), LNUMVAL( U8G_DRAW_UPPER_LEFT ) },
|
||||||
|
|
|
@ -0,0 +1,136 @@
|
||||||
|
/*
|
||||||
|
Functions for integrating U8glib into the nodemcu platform.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "lauxlib.h"
|
||||||
|
|
||||||
|
#include "c_stdlib.h"
|
||||||
|
|
||||||
|
#include "u8g.h"
|
||||||
|
#include "u8g_glue.h"
|
||||||
|
|
||||||
|
|
||||||
|
// ***************************************************************************
|
||||||
|
// Generic framebuffer device and RLE comm driver
|
||||||
|
//
|
||||||
|
uint8_t u8g_dev_gen_fb_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t msg, void *arg)
|
||||||
|
{
|
||||||
|
switch(msg)
|
||||||
|
{
|
||||||
|
case U8G_DEV_MSG_PAGE_FIRST:
|
||||||
|
// tell comm driver to start new framebuffer
|
||||||
|
u8g_SetChipSelect(u8g, dev, 1);
|
||||||
|
break;
|
||||||
|
case U8G_DEV_MSG_PAGE_NEXT:
|
||||||
|
{
|
||||||
|
u8g_pb_t *pb = (u8g_pb_t *)(dev->dev_mem);
|
||||||
|
if ( u8g_pb_WriteBuffer(pb, u8g, dev) == 0 )
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return u8g_dev_pb8v1_base_fn(u8g, dev, msg, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bit_at( uint8_t *buf, int line, int x )
|
||||||
|
{
|
||||||
|
uint8_t byte = buf[x];
|
||||||
|
|
||||||
|
return buf[x] & (1 << line) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct _lu8g_fbrle_item
|
||||||
|
{
|
||||||
|
uint8_t start_x;
|
||||||
|
uint8_t len;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _lu8g_fbrle_line
|
||||||
|
{
|
||||||
|
uint8_t num_valid;
|
||||||
|
struct _lu8g_fbrle_item items[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
uint8_t u8g_com_esp8266_fbrle_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr)
|
||||||
|
{
|
||||||
|
struct _lu8g_userdata_t *lud = (struct _lu8g_userdata_t *)u8g;
|
||||||
|
|
||||||
|
switch(msg)
|
||||||
|
{
|
||||||
|
case U8G_COM_MSG_INIT:
|
||||||
|
// allocate memory -> done
|
||||||
|
// init buffer
|
||||||
|
break;
|
||||||
|
|
||||||
|
case U8G_COM_MSG_CHIP_SELECT:
|
||||||
|
if (arg_val == 1) {
|
||||||
|
// new frame starts
|
||||||
|
if (lud->cb_ref != LUA_NOREF) {
|
||||||
|
// fire callback with nil argument
|
||||||
|
lua_State *L = lua_getstate();
|
||||||
|
lua_rawgeti( L, LUA_REGISTRYINDEX, lud->cb_ref );
|
||||||
|
lua_pushnil( L );
|
||||||
|
lua_call( L, 1, 0 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case U8G_COM_MSG_WRITE_SEQ:
|
||||||
|
case U8G_COM_MSG_WRITE_SEQ_P:
|
||||||
|
{
|
||||||
|
uint8_t xwidth = u8g->pin_list[U8G_PI_EN];
|
||||||
|
size_t fbrle_line_size = sizeof( struct _lu8g_fbrle_line ) + sizeof( struct _lu8g_fbrle_item ) * (xwidth/2);
|
||||||
|
int num_lines = arg_val / (xwidth/8);
|
||||||
|
uint8_t *buf = (uint8_t *)arg_ptr;
|
||||||
|
|
||||||
|
struct _lu8g_fbrle_line *fbrle_line;
|
||||||
|
if (!(fbrle_line = (struct _lu8g_fbrle_line *)c_malloc( fbrle_line_size ))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int line = 0; line < num_lines; line++) {
|
||||||
|
int start_run = -1;
|
||||||
|
fbrle_line->num_valid = 0;
|
||||||
|
|
||||||
|
for (int x = 0; x < xwidth; x++) {
|
||||||
|
if (bit_at( buf, line, x ) == 0) {
|
||||||
|
if (start_run >= 0) {
|
||||||
|
// inside run, end it and enter result
|
||||||
|
fbrle_line->items[fbrle_line->num_valid].start_x = start_run;
|
||||||
|
fbrle_line->items[fbrle_line->num_valid++].len = x - start_run;
|
||||||
|
//NODE_ERR( " line: %d x: %d len: %d\n", line, start_run, x - start_run );
|
||||||
|
start_run = -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (start_run < 0) {
|
||||||
|
// outside run, start it
|
||||||
|
start_run = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fbrle_line->num_valid >= xwidth/2) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// active run?
|
||||||
|
if (start_run >= 0 && fbrle_line->num_valid < xwidth/2) {
|
||||||
|
fbrle_line->items[fbrle_line->num_valid].start_x = start_run;
|
||||||
|
fbrle_line->items[fbrle_line->num_valid++].len = xwidth - start_run;
|
||||||
|
}
|
||||||
|
|
||||||
|
// line done, trigger callback
|
||||||
|
if (lud->cb_ref != LUA_NOREF) {
|
||||||
|
lua_State *L = lua_getstate();
|
||||||
|
|
||||||
|
lua_rawgeti( L, LUA_REGISTRYINDEX, lud->cb_ref );
|
||||||
|
lua_pushlstring( L, (const char *)fbrle_line, fbrle_line_size );
|
||||||
|
lua_call( L, 1, 0 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c_free( fbrle_line );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
|
||||||
|
#ifndef _U8G_GLUE_H_
|
||||||
|
#define _U8G_GLUE_H_
|
||||||
|
|
||||||
|
#include "u8g.h"
|
||||||
|
|
||||||
|
struct _lu8g_userdata_t
|
||||||
|
{
|
||||||
|
u8g_t u8g;
|
||||||
|
int cb_ref;
|
||||||
|
};
|
||||||
|
typedef struct _lu8g_userdata_t lu8g_userdata_t;
|
||||||
|
|
||||||
|
// shorthand macro for the u8g structure inside the userdata
|
||||||
|
#define LU8G (&(lud->u8g))
|
||||||
|
|
||||||
|
uint8_t u8g_com_esp8266_fbrle_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr);
|
||||||
|
uint8_t u8g_dev_gen_fb_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t msg, void *arg);
|
||||||
|
|
||||||
|
#endif
|
|
@ -202,6 +202,30 @@ disp = u8g.ssd1306_128x64_hw_spi(cs, dc, res)
|
||||||
#### See also
|
#### See also
|
||||||
[I²C Display Drivers](#i2c-display-drivers)
|
[I²C Display Drivers](#i2c-display-drivers)
|
||||||
|
|
||||||
|
## u8g.fb_rle
|
||||||
|
Initialize a virtual display that provides run-length encoded framebuffer contents to a Lua callback.
|
||||||
|
|
||||||
|
The callback function can be used to process the framebuffer line by line. It's called with either `nil` as parameter to indicate the start of a new frame or with a string containing a line of the framebuffer with run-length encoding. First byte in the string specifies how many pairs of (x, len) follow, while each pair defines the start (leftmost x-coordinate) and length of a sequence of lit pixels. All other pixels in the line are dark.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
n = struct.unpack("B", rle_line)
|
||||||
|
print(n.." pairs")
|
||||||
|
for i = 0,n-1 do
|
||||||
|
print(string.format(" x: %d len: %d", struct.unpack("BB", rle_line, 1+1 + i*2)))
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Syntax
|
||||||
|
`u8g.fb_rle(cb_fn, width, height)`
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
- `cb_fn([rle_line])` callback function. `rle_line` is a string containing a run-length encoded framebuffer line, or `nil` to indicate start of frame.
|
||||||
|
- `width` of display. Must be a multiple of 8, less than or equal to 248.
|
||||||
|
- `height` of display. Must be a multiple of 8, less than or equal to 248.
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
u8g display object
|
||||||
|
|
||||||
___
|
___
|
||||||
|
|
||||||
## Constants
|
## Constants
|
||||||
|
|
Loading…
Reference in New Issue