Merge pull request #322 from pimoroni/driver/uc8159

PicoGraphics support for UC8159-based displays
patch-ff-use-strfunc
Philip Howard 2022-06-24 11:20:06 +01:00 zatwierdzone przez GitHub
commit fde585867e
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
15 zmienionych plików z 434 dodań i 50 usunięć

Wyświetl plik

@ -28,6 +28,7 @@ add_subdirectory(icp10125)
add_subdirectory(scd4x)
add_subdirectory(hub75)
add_subdirectory(uc8151)
add_subdirectory(uc8159)
add_subdirectory(uc8151_legacy)
add_subdirectory(pwm)
add_subdirectory(servo)
@ -37,4 +38,3 @@ add_subdirectory(vl53l5cx)
add_subdirectory(pcf85063a)
add_subdirectory(pms5003)
add_subdirectory(sh1107)

Wyświetl plik

@ -0,0 +1 @@
include(uc8159.cmake)

Wyświetl plik

@ -0,0 +1,10 @@
set(DRIVER_NAME uc8159)
add_library(${DRIVER_NAME} INTERFACE)
target_sources(${DRIVER_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}/${DRIVER_NAME}.cpp)
target_include_directories(${DRIVER_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR})
# Pull in pico libraries that we need
target_link_libraries(${DRIVER_NAME} INTERFACE pico_stdlib hardware_spi)

Wyświetl plik

@ -0,0 +1,157 @@
#include "uc8159.hpp"
#include <cstdlib>
#include <math.h>
#include <string.h>
namespace pimoroni {
enum reg {
PSR = 0x00,
PWR = 0x01,
POF = 0x02,
PFS = 0x03,
PON = 0x04,
BTST = 0x06,
DSLP = 0x07,
DTM1 = 0x10,
DSP = 0x11,
DRF = 0x12,
IPC = 0x13,
PLL = 0x30,
TSC = 0x40,
TSE = 0x41,
TSW = 0x42,
TSR = 0x43,
CDI = 0x50,
LPD = 0x51,
TCON = 0x60,
TRES = 0x61,
DAM = 0x65,
REV = 0x70,
FLG = 0x71,
AMV = 0x80,
VV = 0x81,
VDCS = 0x82,
PWS = 0xE3,
TSSET = 0xE5
};
bool UC8159::is_busy() {
return !gpio_get(BUSY);
}
void UC8159::busy_wait() {
while(is_busy()) {
tight_loop_contents();
}
}
void UC8159::reset() {
gpio_put(RESET, 0); sleep_ms(10);
gpio_put(RESET, 1); sleep_ms(10);
busy_wait();
}
void UC8159::init() {
// configure spi interface and pins
spi_init(spi, 3'000'000);
gpio_set_function(DC, GPIO_FUNC_SIO);
gpio_set_dir(DC, GPIO_OUT);
gpio_set_function(CS, GPIO_FUNC_SIO);
gpio_set_dir(CS, GPIO_OUT);
gpio_put(CS, 1);
gpio_set_function(RESET, GPIO_FUNC_SIO);
gpio_set_dir(RESET, GPIO_OUT);
gpio_put(RESET, 1);
gpio_set_function(BUSY, GPIO_FUNC_SIO);
gpio_set_dir(BUSY, GPIO_IN);
gpio_set_pulls(BUSY, true, false);
gpio_set_function(SCK, GPIO_FUNC_SPI);
gpio_set_function(MOSI, GPIO_FUNC_SPI);
};
void UC8159::setup() {
reset();
busy_wait();
command(0x00, {0xE3, 0x08});
command(0x01, {0x37, 0x00, 0x23, 0x23});
command(0x03, {0x00});
command(0x06, {0xC7, 0xC7, 0x1D});
command(0x30, {0x3C});
command(0x40, {0x00});
command(0x50, {0x37});
command(0x60, {0x22});
command(0x61, {0x02, 0x58, 0x01, 0xC0});
command(0xE3, {0xAA});
sleep_ms(100);
command(0x50, {0x37});
}
void UC8159::power_off() {
busy_wait();
command(POF); // turn off
}
void UC8159::command(uint8_t reg, size_t len, const uint8_t *data) {
gpio_put(CS, 0);
gpio_put(DC, 0); // command mode
spi_write_blocking(spi, &reg, 1);
if(len > 0) {
gpio_put(DC, 1); // data mode
spi_write_blocking(spi, (const uint8_t*)data, len);
}
gpio_put(CS, 1);
}
void UC8159::data(size_t len, const uint8_t *data) {
gpio_put(CS, 0);
gpio_put(DC, 1); // data mode
spi_write_blocking(spi, (const uint8_t*)data, len);
gpio_put(CS, 1);
}
void UC8159::command(uint8_t reg, std::initializer_list<uint8_t> values) {
command(reg, values.size(), (uint8_t *)values.begin());
}
void UC8159::update(const void *data, bool blocking) {
if(blocking) {
busy_wait();
}
setup();
command(DTM1, (width * height) / 2, (uint8_t *)data); // transmit framebuffer
busy_wait();
command(PON); // turn on
busy_wait();
command(DRF); // start display refresh
busy_wait();
if(blocking) {
busy_wait();
command(POF); // turn off
}
}
void UC8159::update(PicoGraphics *graphics) {
if(graphics->pen_type != PicoGraphics::PEN_P4) return; // Incompatible buffer
update(graphics->frame_buffer, false);
}
}

Wyświetl plik

@ -0,0 +1,79 @@
#pragma once
#include <initializer_list>
#include "pico/stdlib.h"
#include "hardware/spi.h"
#include "hardware/gpio.h"
#include "common/pimoroni_common.hpp"
#include "common/pimoroni_bus.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
namespace pimoroni {
class UC8159 : public DisplayDriver {
//--------------------------------------------------
// Variables
//--------------------------------------------------
private:
// highest possible resolution is 160x296 which at 1 bit per pixel
// requires 5920 bytes of frame buffer
//uint8_t frame_buffer[5920] = {0};
uint8_t *frame_buffer;
spi_inst_t *spi = PIMORONI_SPI_DEFAULT_INSTANCE;
// interface pins with our standard defaults where appropriate
uint CS = SPI_BG_FRONT_CS;
uint DC = 27;
uint SCK = SPI_DEFAULT_SCK;
uint MOSI = SPI_DEFAULT_MOSI;
uint BUSY = 26;
uint RESET = 25;
public:
enum colour : uint8_t {
BLACK = 0,
WHITE = 1,
GREEN = 2,
BLUE = 3,
RED = 4,
YELLOW = 5,
ORANGE = 6,
CLEAN = 7
};
UC8159(uint16_t width, uint16_t height) : UC8159(width, height, {PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_FRONT_CS, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, PIN_UNUSED, 27, PIN_UNUSED}) {};
UC8159(uint16_t width, uint16_t height, SPIPins pins, uint busy=26, uint reset=25) :
DisplayDriver(width, height, ROTATE_0),
spi(pins.spi),
CS(pins.cs), DC(pins.dc), SCK(pins.sck), MOSI(pins.mosi), BUSY(busy), RESET(reset) {
init();
}
//--------------------------------------------------
// Methods
//--------------------------------------------------
public:
void busy_wait();
void reset();
void power_off();
bool is_busy() override;
void update(PicoGraphics *graphics) override;
private:
void init();
void setup();
void update(const void *data, bool blocking = true);
void command(uint8_t reg, size_t len, const uint8_t *data);
void command(uint8_t reg, std::initializer_list<uint8_t> values);
void command(uint8_t reg, const uint8_t data) {command(reg, 0, &data);};
void command(uint8_t reg) {command(reg, 0, nullptr);};
void data(size_t len, const uint8_t *data);
};
}

Wyświetl plik

@ -31,3 +31,4 @@ add_subdirectory(tufty2040)
add_subdirectory(servo2040)
add_subdirectory(motor2040)
add_subdirectory(adcfft)
add_subdirectory(jpegdec)

Wyświetl plik

@ -0,0 +1 @@
include(jpegdec.cmake)

Wyświetl plik

@ -0,0 +1,16 @@
#include "JPEGDEC.h"
/*
int32_t readRAM(JPEGFILE *pFile, uint8_t *pBuf, int32_t iLen) {
return 0;
}
int32_t seekMem(JPEGFILE *pFile, int32_t iPosition) {
return 0;
}
int32_t readFile(JPEGFILE *pFile, uint8_t *pBuf, int32_t iLen) {
return 0;
}
int32_t seekFile(JPEGFILE *pFile, int32_t iPosition) {
return 0;
}
void closeFile(void *handle) {
}*/

Wyświetl plik

@ -0,0 +1,10 @@
add_library(jpegdec
${CMAKE_CURRENT_LIST_DIR}/jpeg.cpp
${CMAKE_CURRENT_LIST_DIR}/JPEGDEC.cpp
)
set_source_files_properties(${CMAKE_CURRENT_LIST_DIR}/JPEGDEC.cpp PROPERTIES COMPILE_FLAGS "-Wno-error=unused-function")
target_include_directories(jpegdec INTERFACE ${CMAKE_CURRENT_LIST_DIR})
target_link_libraries(jpegdec pico_stdlib)

Wyświetl plik

@ -99,10 +99,16 @@ namespace pimoroni {
void PicoGraphics_PenP4::set_pixel_dither(const Point &p, const RGB &c) {
if(!bounds.contains(p)) return;
uint used_palette_entries = 0;
for(auto i = 0u; i < palette_size; i++) {
if(!used[i]) break;
used_palette_entries++;
}
if(!cache_built) {
for(uint i = 0; i < 512; i++) {
RGB cache_col((i & 0x1C0) >> 1, (i & 0x38) << 2, (i & 0x7) << 5);
get_dither_candidates(cache_col, palette, palette_size, candidate_cache[i]);
get_dither_candidates(cache_col, palette, used_palette_entries, candidate_cache[i]);
}
cache_built = true;
}

Wyświetl plik

@ -22,12 +22,62 @@ typedef struct _ModPicoGraphics_obj_t {
typedef struct _JPEG_obj_t {
mp_obj_base_t base;
JPEGDEC *jpeg;
mp_obj_t file;
mp_buffer_info_t buf;
ModPicoGraphics_obj_t *graphics;
} _JPEG_obj_t;
PicoGraphics *current_graphics = nullptr;
void *jpegdec_open_callback(const char *filename, int32_t *size) {
mp_obj_t fn = mp_obj_new_str(filename, (mp_uint_t)strlen(filename));
mp_obj_t args[2] = {
fn,
MP_OBJ_NEW_QSTR(MP_QSTR_r),
};
// Stat the file to get its size
// example tuple response: (32768, 0, 0, 0, 0, 0, 5153, 1654709815, 1654709815, 1654709815)
mp_obj_t stat = mp_vfs_stat(fn);
mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR2(stat, mp_obj_tuple_t);
*size = mp_obj_get_int(tuple->items[6]);
mp_obj_t fhandle = mp_vfs_open(MP_ARRAY_SIZE(args), &args[0], (mp_map_t *)&mp_const_empty_map);
return (void *)fhandle;
}
void jpegdec_close_callback(void *handle) {
mp_stream_close((mp_obj_t)handle);
}
int32_t jpegdec_read_callback(JPEGFILE *jpeg, uint8_t *p, int32_t c) {
mp_obj_t fhandle = jpeg->fHandle;
int error;
return mp_stream_read_exactly(fhandle, p, c, &error);
}
// Re-implementation of stream.c/STATIC mp_obj_t stream_seek(size_t n_args, const mp_obj_t *args)
int32_t jpegdec_seek_callback(JPEGFILE *jpeg, int32_t p) {
mp_obj_t fhandle = jpeg->fHandle;
struct mp_stream_seek_t seek_s;
seek_s.offset = p;
seek_s.whence = SEEK_SET;
const mp_stream_p_t *stream_p = mp_get_stream(fhandle);
int error;
mp_uint_t res = stream_p->ioctl(fhandle, MP_STREAM_SEEK, (mp_uint_t)(uintptr_t)&seek_s, &error);
if (res == MP_STREAM_ERROR) {
mp_raise_OSError(error);
}
return seek_s.offset;
}
int JPEGDraw(JPEGDRAW *pDraw) {
#ifdef MICROPY_EVENT_POLL_HOOK
MICROPY_EVENT_POLL_HOOK
@ -105,56 +155,25 @@ mp_obj_t _JPEG_del(mp_obj_t self_in) {
return mp_const_none;
}
static int _open(_JPEG_obj_t *self) {
int result = self->jpeg->openRAM((uint8_t *)self->buf.buf, self->buf.len, JPEGDraw);
if (result == 1) {
switch(self->graphics->graphics->pen_type) {
case PicoGraphics::PEN_RGB332:
case PicoGraphics::PEN_RGB565:
case PicoGraphics::PEN_P8:
case PicoGraphics::PEN_P4:
self->jpeg->setPixelType(RGB565_BIG_ENDIAN);
break;
// TODO 2-bit is currently unsupported
case PicoGraphics::PEN_P2:
self->jpeg->setPixelType(TWO_BIT_DITHERED);
break;
case PicoGraphics::PEN_1BIT:
self->jpeg->setPixelType(ONE_BIT_DITHERED);
break;
}
}
return result;
}
// open_FILE
mp_obj_t _JPEG_openFILE(mp_obj_t self_in, mp_obj_t filename) {
_JPEG_obj_t *self = MP_OBJ_TO_PTR2(self_in, _JPEG_obj_t);
mp_obj_t args[2] = {
filename,
MP_OBJ_NEW_QSTR(MP_QSTR_r),
};
// Stat the file to get its size
// example tuple response: (32768, 0, 0, 0, 0, 0, 5153, 1654709815, 1654709815, 1654709815)
mp_obj_t stat = mp_vfs_stat(filename);
mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR2(stat, mp_obj_tuple_t);
size_t filesize = mp_obj_get_int(tuple->items[6]);
// TODO Check for valid filename, and maybe that file exists?
self->file = filename;
self->buf.buf = (void *)m_new(uint8_t, filesize);
mp_obj_t file = mp_vfs_open(MP_ARRAY_SIZE(args), &args[0], (mp_map_t *)&mp_const_empty_map);
int errcode;
self->buf.len = mp_stream_rw(file, self->buf.buf, filesize, &errcode, MP_STREAM_RW_READ | MP_STREAM_RW_ONCE);
if (errcode != 0) {
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Failed to open file!"));
}
return mp_const_true;
}
// open_RAM
mp_obj_t _JPEG_openRAM(mp_obj_t self_in, mp_obj_t buffer) {
_JPEG_obj_t *self = MP_OBJ_TO_PTR2(self_in, _JPEG_obj_t);
mp_get_buffer_raise(buffer, &self->buf, MP_BUFFER_READ);
// TODO Check for valid buffer
self->file = buffer;
return mp_const_true;
}
@ -177,12 +196,53 @@ mp_obj_t _JPEG_decode(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args
int y = args[ARG_y].u_int;
int f = args[ARG_scale].u_int;
if(_open(self) != 1) return mp_const_false;
// Just-in-time open of the filename/buffer we stored in self->file via open_RAM or open_file
// Source is a filename
int result = -1;
if(mp_obj_is_str_or_bytes(self->file)){
GET_STR_DATA_LEN(self->file, str, str_len);
std::string t((const char*)str);
result = self->jpeg->open(
t.c_str(),
jpegdec_open_callback,
jpegdec_close_callback,
jpegdec_read_callback,
jpegdec_seek_callback,
JPEGDraw);
// Source is a buffer
} else {
mp_get_buffer_raise(self->file, &self->buf, MP_BUFFER_READ);
result = self->jpeg->openRAM((uint8_t *)self->buf.buf, self->buf.len, JPEGDraw);
}
if(result != 1) mp_raise_msg(&mp_type_RuntimeError, "JPEG: could not read file/buffer.");
// Force a specific data output type to best match our PicoGraphics buffer
switch(self->graphics->graphics->pen_type) {
case PicoGraphics::PEN_RGB332:
case PicoGraphics::PEN_RGB565:
case PicoGraphics::PEN_P8:
case PicoGraphics::PEN_P4:
self->jpeg->setPixelType(RGB565_BIG_ENDIAN);
break;
// TODO 2-bit is currently unsupported
case PicoGraphics::PEN_P2:
self->jpeg->setPixelType(TWO_BIT_DITHERED);
break;
case PicoGraphics::PEN_1BIT:
self->jpeg->setPixelType(ONE_BIT_DITHERED);
break;
}
// We need to store a pointer to the PicoGraphics surface
// since the JPEGDRAW struct has no userdata
current_graphics = self->graphics->graphics;
int result = -1;
if(self->graphics->graphics->pen_type == PicoGraphics::PEN_P4 || self->graphics->graphics->pen_type == PicoGraphics::PEN_1BIT) {
uint8_t *buf = new uint8_t[2048];
@ -191,7 +251,12 @@ mp_obj_t _JPEG_decode(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args
} else {
result = self->jpeg->decode(x, y, f);
}
current_graphics = nullptr;
// Close the file since we've opened it on-demand
self->jpeg->close();
return result == 1 ? mp_const_true : mp_const_false;
}

Wyświetl plik

@ -9,6 +9,7 @@ target_sources(usermod_${MOD_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}/../../../drivers/st7735/st7735.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../drivers/sh1107/sh1107.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../drivers/uc8151/uc8151.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../drivers/uc8159/uc8159.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/pico_graphics.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/pico_graphics_pen_1bit.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/pico_graphics_pen_1bitY.cpp
@ -35,4 +36,4 @@ set_source_files_properties(
${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.c
PROPERTIES COMPILE_FLAGS
"-Wno-discarded-qualifiers"
)
)

Wyświetl plik

@ -119,6 +119,7 @@ STATIC const mp_map_elem_t picographics_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_LCD_160X80), MP_ROM_INT(DISPLAY_LCD_160X80) },
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_I2C_OLED_128X128), MP_ROM_INT(DISPLAY_I2C_OLED_128X128) },
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_INKY_PACK), MP_ROM_INT(DISPLAY_INKY_PACK) },
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_INKY_FRAME), MP_ROM_INT(DISPLAY_INKY_FRAME) },
{ MP_ROM_QSTR(MP_QSTR_PEN_1BIT), MP_ROM_INT(PEN_1BIT) },
{ MP_ROM_QSTR(MP_QSTR_PEN_P4), MP_ROM_INT(PEN_P4) },
@ -138,4 +139,4 @@ const mp_obj_module_t picographics_user_cmodule = {
MP_REGISTER_MODULE(MP_QSTR_picographics, picographics_user_cmodule, MODULE_PICOGRAPHICS_ENABLED);
#else
MP_REGISTER_MODULE(MP_QSTR_picographics, picographics_user_cmodule);
#endif
#endif

Wyświetl plik

@ -2,6 +2,7 @@
#include "drivers/st7735/st7735.hpp"
#include "drivers/sh1107/sh1107.hpp"
#include "drivers/uc8151/uc8151.hpp"
#include "drivers/uc8159/uc8159.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
#include "common/pimoroni_common.hpp"
#include "common/pimoroni_bus.hpp"
@ -82,6 +83,12 @@ bool get_display_settings(PicoGraphicsDisplay display, int &width, int &height,
if(rotate == -1) rotate = (int)Rotation::ROTATE_0;
if(pen_type == -1) pen_type = PEN_1BIT;
break;
case DISPLAY_INKY_FRAME:
width = 600;
height = 448;
if(rotate == -1) rotate = (int)Rotation::ROTATE_0;
if(pen_type == -1) pen_type = PEN_P4;
break;
default:
return false;
}
@ -108,13 +115,14 @@ size_t get_required_buffer_size(PicoGraphicsPenType pen_type, uint width, uint h
mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
ModPicoGraphics_obj_t *self = nullptr;
enum { ARG_display, ARG_rotate, ARG_bus, ARG_buffer, ARG_pen_type };
enum { ARG_display, ARG_rotate, ARG_bus, ARG_buffer, ARG_pen_type, ARG_extra_pins };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_display, MP_ARG_INT | MP_ARG_REQUIRED },
{ MP_QSTR_rotate, MP_ARG_INT, { .u_int = -1 } },
{ MP_QSTR_bus, MP_ARG_OBJ, { .u_obj = mp_const_none } },
{ MP_QSTR_buffer, MP_ARG_OBJ, { .u_obj = mp_const_none } },
{ MP_QSTR_pen_type, MP_ARG_INT, { .u_int = -1 } },
{ MP_QSTR_extra_pins, MP_ARG_OBJ, { .u_obj = mp_const_none } },
};
// Parse args.
@ -135,7 +143,19 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size
if(rotate == -1) rotate = (int)Rotation::ROTATE_0;
// Try to create an appropriate display driver
if (display == DISPLAY_TUFTY_2040) {
if (display == DISPLAY_INKY_FRAME) {
pen_type = PEN_P4; // FORCE to P4 since it's the only supported mode
// TODO grab BUSY and RESET from ARG_extra_pins
if (args[ARG_bus].u_obj == mp_const_none) {
self->display = m_new_class(UC8159, width, height);
} else if (mp_obj_is_type(args[ARG_bus].u_obj, &SPIPins_type)) {
_PimoroniBus_obj_t *bus = (_PimoroniBus_obj_t *)MP_OBJ_TO_PTR(args[ARG_bus].u_obj);
self->display = m_new_class(UC8159, width, height, *(SPIPins *)(bus->pins));
} else {
mp_raise_ValueError("SPIBus expected!");
}
}
else if (display == DISPLAY_TUFTY_2040) {
if (args[ARG_bus].u_obj == mp_const_none) {
self->display = m_new_class(ST7789, width, height, (Rotation)rotate, {10, 11, 12, 13, 14, 2});
} else if (mp_obj_is_type(args[ARG_bus].u_obj, &ParallelPins_type)) {
@ -230,7 +250,9 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size
self->graphics->clear();
// Update the LCD from the graphics library
self->display->update(self->graphics);
if (display != DISPLAY_INKY_FRAME) {
self->display->update(self->graphics);
}
return MP_OBJ_FROM_PTR(self);
}
@ -393,8 +415,21 @@ mp_obj_t ModPicoGraphics_update(mp_obj_t self_in) {
self->graphics->scanline_interrupt = nullptr;
}
*/
while(self->display->is_busy()) {
#ifdef MICROPY_EVENT_POLL_HOOK
MICROPY_EVENT_POLL_HOOK
#endif
}
self->display->update(self->graphics);
while(self->display->is_busy()) {
#ifdef MICROPY_EVENT_POLL_HOOK
MICROPY_EVENT_POLL_HOOK
#endif
}
return mp_const_none;
}

Wyświetl plik

@ -11,7 +11,8 @@ enum PicoGraphicsDisplay {
DISPLAY_ENVIRO_PLUS,
DISPLAY_LCD_160X80,
DISPLAY_I2C_OLED_128X128,
DISPLAY_INKY_PACK
DISPLAY_INKY_PACK,
DISPLAY_INKY_FRAME
};
enum PicoGraphicsPenType {
@ -76,4 +77,4 @@ extern mp_obj_t ModPicoGraphics_set_framebuffer(mp_obj_t self_in, mp_obj_t frame
extern mp_int_t ModPicoGraphics_get_framebuffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags);
extern mp_obj_t ModPicoGraphics__del__(mp_obj_t self_in);
extern mp_obj_t ModPicoGraphics__del__(mp_obj_t self_in);