348 lines
9.9 KiB
C
348 lines
9.9 KiB
C
/*
|
|
Functions for integrating U8glib into the nodemcu platform.
|
|
*/
|
|
|
|
#include "lauxlib.h"
|
|
#include "platform.h"
|
|
|
|
#include "c_stdlib.h"
|
|
|
|
#include "u8g.h"
|
|
#include "u8g_glue.h"
|
|
|
|
|
|
// ------------------------------------------------------------
|
|
// comm functions
|
|
//
|
|
#define I2C_CMD_MODE 0x000
|
|
#define I2C_DATA_MODE 0x040
|
|
|
|
#define ESP_I2C_ID 0
|
|
|
|
|
|
static uint8_t do_i2c_start(uint8_t id, uint8_t sla)
|
|
{
|
|
platform_i2c_send_start( id );
|
|
|
|
// ignore return value -> tolerate missing ACK
|
|
platform_i2c_send_address( id, sla, PLATFORM_I2C_DIRECTION_TRANSMITTER );
|
|
|
|
return 1;
|
|
}
|
|
|
|
static uint8_t u8g_com_esp8266_ssd_start_sequence(struct _lu8g_userdata_t *lu8g)
|
|
{
|
|
/* are we requested to set the a0 state? */
|
|
if ( lu8g->u8g.pin_list[U8G_PI_SET_A0] == 0 )
|
|
return 1;
|
|
|
|
/* setup bus, might be a repeated start */
|
|
if ( do_i2c_start( ESP_I2C_ID, lu8g->i2c_addr ) == 0 )
|
|
return 0;
|
|
if ( lu8g->u8g.pin_list[U8G_PI_A0_STATE] == 0 )
|
|
{
|
|
// ignore return value -> tolerate missing ACK
|
|
if ( platform_i2c_send_byte( ESP_I2C_ID, I2C_CMD_MODE ) == 0 )
|
|
; //return 0;
|
|
}
|
|
else
|
|
{
|
|
platform_i2c_send_byte( ESP_I2C_ID, I2C_DATA_MODE );
|
|
}
|
|
|
|
lu8g->u8g.pin_list[U8G_PI_SET_A0] = 0;
|
|
return 1;
|
|
}
|
|
|
|
|
|
static void lu8g_digital_write( struct _lu8g_userdata_t *lu8g, uint8_t pin_index, uint8_t value )
|
|
{
|
|
uint8_t pin;
|
|
|
|
pin = lu8g->u8g.pin_list[pin_index];
|
|
if ( pin != U8G_PIN_NONE )
|
|
platform_gpio_write( pin, value );
|
|
}
|
|
|
|
void u8g_Delay(u8g_t *u8g, uint16_t msec)
|
|
{
|
|
struct _lu8g_userdata_t *lu8g = (struct _lu8g_userdata_t *)u8g;
|
|
const uint16_t chunk = 50;
|
|
|
|
if (lu8g->use_delay == 0)
|
|
return;
|
|
|
|
while (msec > chunk)
|
|
{
|
|
os_delay_us( chunk*1000 );
|
|
msec -= chunk;
|
|
}
|
|
if (msec > 0)
|
|
os_delay_us( msec*1000 );
|
|
}
|
|
void u8g_MicroDelay(void)
|
|
{
|
|
os_delay_us( 1 );
|
|
}
|
|
void u8g_10MicroDelay(void)
|
|
{
|
|
os_delay_us( 10 );
|
|
}
|
|
|
|
|
|
uint8_t u8g_com_esp8266_hw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr)
|
|
{
|
|
struct _lu8g_userdata_t *lu8g = (struct _lu8g_userdata_t *)u8g;
|
|
|
|
switch(msg)
|
|
{
|
|
case U8G_COM_MSG_STOP:
|
|
break;
|
|
|
|
case U8G_COM_MSG_INIT:
|
|
// we assume that the SPI interface was already initialized
|
|
// just care for the /CS and D/C pins
|
|
lu8g_digital_write( lu8g, U8G_PI_CS, PLATFORM_GPIO_HIGH );
|
|
platform_gpio_mode( lu8g->u8g.pin_list[U8G_PI_CS], PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT );
|
|
platform_gpio_mode( lu8g->u8g.pin_list[U8G_PI_A0], PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT );
|
|
break;
|
|
|
|
case U8G_COM_MSG_ADDRESS: /* define cmd (arg_val = 0) or data mode (arg_val = 1) */
|
|
lu8g_digital_write( lu8g, U8G_PI_A0, arg_val == 0 ? PLATFORM_GPIO_LOW : PLATFORM_GPIO_HIGH );
|
|
break;
|
|
|
|
case U8G_COM_MSG_CHIP_SELECT:
|
|
if (arg_val == 0)
|
|
{
|
|
/* disable */
|
|
lu8g_digital_write( lu8g, U8G_PI_CS, PLATFORM_GPIO_HIGH );
|
|
}
|
|
else
|
|
{
|
|
/* enable */
|
|
lu8g_digital_write( lu8g, U8G_PI_CS, PLATFORM_GPIO_LOW );
|
|
}
|
|
break;
|
|
|
|
case U8G_COM_MSG_RESET:
|
|
if ( lu8g->u8g.pin_list[U8G_PI_RESET] != U8G_PIN_NONE )
|
|
lu8g_digital_write( lu8g, U8G_PI_RESET, arg_val == 0 ? PLATFORM_GPIO_LOW : PLATFORM_GPIO_HIGH );
|
|
break;
|
|
|
|
case U8G_COM_MSG_WRITE_BYTE:
|
|
platform_spi_send( 1, 8, arg_val );
|
|
break;
|
|
|
|
case U8G_COM_MSG_WRITE_SEQ:
|
|
case U8G_COM_MSG_WRITE_SEQ_P:
|
|
{
|
|
register uint8_t *ptr = arg_ptr;
|
|
while( arg_val > 0 )
|
|
{
|
|
platform_spi_send( 1, 8, *ptr++ );
|
|
arg_val--;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
uint8_t u8g_com_esp8266_ssd_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr)
|
|
{
|
|
struct _lu8g_userdata_t *lu8g = (struct _lu8g_userdata_t *)u8g;
|
|
|
|
switch(msg)
|
|
{
|
|
case U8G_COM_MSG_INIT:
|
|
// we assume that the i2c bus was already initialized
|
|
//u8g_i2c_init(u8g->pin_list[U8G_PI_I2C_OPTION]);
|
|
|
|
break;
|
|
|
|
case U8G_COM_MSG_STOP:
|
|
break;
|
|
|
|
case U8G_COM_MSG_RESET:
|
|
/* Currently disabled, but it could be enable. Previous restrictions have been removed */
|
|
/* u8g_com_arduino_digital_write(u8g, U8G_PI_RESET, arg_val); */
|
|
break;
|
|
|
|
case U8G_COM_MSG_CHIP_SELECT:
|
|
lu8g->u8g.pin_list[U8G_PI_A0_STATE] = 0;
|
|
lu8g->u8g.pin_list[U8G_PI_SET_A0] = 1; /* force a0 to set again, also forces start condition */
|
|
if ( arg_val == 0 )
|
|
{
|
|
/* disable chip, send stop condition */
|
|
platform_i2c_send_stop( ESP_I2C_ID );
|
|
}
|
|
else
|
|
{
|
|
/* enable, do nothing: any byte writing will trigger the i2c start */
|
|
}
|
|
break;
|
|
|
|
case U8G_COM_MSG_WRITE_BYTE:
|
|
//u8g->pin_list[U8G_PI_SET_A0] = 1;
|
|
if ( u8g_com_esp8266_ssd_start_sequence(lu8g) == 0 )
|
|
return platform_i2c_send_stop( ESP_I2C_ID ), 0;
|
|
// ignore return value -> tolerate missing ACK
|
|
if ( platform_i2c_send_byte( ESP_I2C_ID, arg_val) == 0 )
|
|
; //return platform_i2c_send_stop( ESP_I2C_ID ), 0;
|
|
// platform_i2c_send_stop( ESP_I2C_ID );
|
|
break;
|
|
|
|
case U8G_COM_MSG_WRITE_SEQ:
|
|
case U8G_COM_MSG_WRITE_SEQ_P:
|
|
//u8g->pin_list[U8G_PI_SET_A0] = 1;
|
|
if ( u8g_com_esp8266_ssd_start_sequence(lu8g) == 0 )
|
|
return platform_i2c_send_stop( ESP_I2C_ID ), 0;
|
|
{
|
|
register uint8_t *ptr = arg_ptr;
|
|
while( arg_val > 0 )
|
|
{
|
|
// ignore return value -> tolerate missing ACK
|
|
if ( platform_i2c_send_byte( ESP_I2C_ID, *ptr++) == 0 )
|
|
; //return platform_i2c_send_stop( ESP_I2C_ID ), 0;
|
|
arg_val--;
|
|
}
|
|
}
|
|
// platform_i2c_send_stop( ESP_I2C_ID );
|
|
break;
|
|
|
|
case U8G_COM_MSG_ADDRESS: /* define cmd (arg_val = 0) or data mode (arg_val = 1) */
|
|
lu8g->u8g.pin_list[U8G_PI_A0_STATE] = arg_val;
|
|
lu8g->u8g.pin_list[U8G_PI_SET_A0] = 1; /* force a0 to set again */
|
|
|
|
break;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
// 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;
|
|
}
|