diff --git a/drivers/uc8159/uc8159.cpp b/drivers/uc8159/uc8159.cpp index b00fdc2f..e07ef5c2 100644 --- a/drivers/uc8159/uc8159.cpp +++ b/drivers/uc8159/uc8159.cpp @@ -74,93 +74,26 @@ namespace pimoroni { gpio_set_function(SCK, GPIO_FUNC_SPI); gpio_set_function(MOSI, GPIO_FUNC_SPI); - - memset(frame_buffer, WHITE << 4 | WHITE, width * height / 2); }; void UC8159::setup() { reset(); busy_wait(); - /* - Resolution Setting - 10bit horizontal followed by a 10bit vertical resolution - we'll let struct.pack do the work here and send 16bit values - life is too short for manual bit wrangling - */ - uint16_t resolution[2] = { - __builtin_bswap16(width), - __builtin_bswap16(height) - }; - - command(TRES, 4, { - (uint8_t *)resolution - }); + 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}); - /* - Panel Setting - 0b11000000 = Resolution select, 0b00 = 640x480, our panel is 0b11 = 600x448 - 0b00100000 = LUT selection, 0 = ext flash, 1 = registers, we use ext flash - 0b00010000 = Ignore - 0b00001000 = Gate scan direction, 0 = down, 1 = up (default) - 0b00000100 = Source shift direction, 0 = left, 1 = right (default) - 0b00000010 = DC-DC converter, 0 = off, 1 = on - 0b00000001 = Soft reset, 0 = Reset, 1 = Normal (Default) - 0b11 = 600x448 - 0b10 = 640x400 - */ + sleep_ms(100); - command(PSR, { - (uint8_t)((width == 640) ? 0b10101111 : 0b11101111), - 0x08 // UC8159 7-colour - }); - - command(PWR, { - (0x06 << 3) | // ??? - not documented in UC8159 datasheet - (0x01 << 2) | // SOURCE_INTERNAL_DC_DC - (0x01 << 1) | // GATE_INTERNAL_DC_DC - 0x01, // LV_SOURCE_INTERNAL_DC_DC - 0x00, // VGx_20V - 0x23, // UC8159_7C - 0x23 // UC8159_7C - }); - - /* - Set the PLL clock frequency to 50Hz - 0b11000000 = Ignore - 0b00111000 = M - 0b00000111 = N - PLL = 2MHz * (M / N) - PLL = 2MHz * (7 / 4) - PLL = 2,800,000 ??? - */ - command(PLL, 0x3C); - - command(TSE, 0x00); - - /* - VCOM and Data Interval setting - 0b11100000 = Vborder control (0b001 = LUTB voltage) - 0b00010000 = Data polarity - 0b00001111 = Vcom and data interval (0b0111 = 10, default) - */ - command(CDI, (1 << 5) | 0x17); - - /* - Gate/Source non-overlap period - 0b11110000 = Source to Gate (0b0010 = 12nS, default) - 0b00001111 = Gate to Source - */ - command(TCON, 0x22); - - // Disable externalflash - command(DAM, 0x00); - - command(PWS, 0xAA); - - command(PFS, 0x00); - - //power_off(); + command(0x50, {0x37}); } void UC8159::power_off() { @@ -193,29 +126,14 @@ namespace pimoroni { command(reg, values.size(), (uint8_t *)values.begin()); } - void UC8159::pixel(int x, int y, int v) { - // bounds check - if(x < 0 || y < 0 || x >= width || y >= height) return; - - // pointer to byte in framebuffer that contains this pixel - uint8_t *p = &frame_buffer[(x / 2) + (y * width / 2)]; - - uint8_t o = (~x & 0b1) * 4; // bit offset within byte - uint8_t m = ~(0b1111 << o); // bit mask for byte - uint8_t b = v << o; // bit value shifted to position - - *p &= m; // clear bits - *p |= b; // set value - } - - void UC8159::update(bool blocking) { + void UC8159::update(const void *data, bool blocking) { if(blocking) { busy_wait(); } setup(); - command(DTM1, (width * height) / 2, frame_buffer); // transmit framebuffer + command(DTM1, (width * height) / 2, (uint8_t *)data); // transmit framebuffer busy_wait(); command(PON); // turn on @@ -231,4 +149,9 @@ namespace pimoroni { } } + void UC8159::update(PicoGraphics *graphics) { + if(graphics->pen_type != PicoGraphics::PEN_P4) return; // Incompatible buffer + update(graphics->frame_buffer, false); + } + } diff --git a/drivers/uc8159/uc8159.hpp b/drivers/uc8159/uc8159.hpp index e19378f4..01846e27 100644 --- a/drivers/uc8159/uc8159.hpp +++ b/drivers/uc8159/uc8159.hpp @@ -5,19 +5,17 @@ #include "pico/stdlib.h" #include "hardware/spi.h" #include "hardware/gpio.h" - -#include "../../common/pimoroni_common.hpp" +#include "common/pimoroni_common.hpp" +#include "common/pimoroni_bus.hpp" +#include "libraries/pico_graphics/pico_graphics.hpp" namespace pimoroni { - class UC8159 { + class UC8159 : public DisplayDriver { //-------------------------------------------------- // Variables //-------------------------------------------------- private: - // screen properties - uint16_t width; - uint16_t height; // highest possible resolution is 160x296 which at 1 bit per pixel // requires 5920 bytes of frame buffer @@ -34,8 +32,6 @@ namespace pimoroni { uint BUSY = 26; uint RESET = 25; - bool inverted = false; - public: enum colour : uint8_t { BLACK = 0, @@ -48,53 +44,36 @@ namespace pimoroni { CLEAN = 7 }; - UC8159(uint16_t width, uint16_t height) : - width(width), height(height), frame_buffer(new uint8_t[width * height / 2]) { - } + 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, uint8_t *frame_buffer) : - width(width), height(height), frame_buffer(frame_buffer) { - } - - UC8159(uint16_t width, uint16_t height, - spi_inst_t *spi, - uint CS, uint DC, uint SCK, uint MOSI, - uint BUSY = PIN_UNUSED, uint RESET = PIN_UNUSED) : - width(width), height(height), - frame_buffer(new uint8_t[width * height / 2]), - spi(spi), - CS(CS), DC(DC), SCK(SCK), MOSI(MOSI), BUSY(BUSY), RESET(RESET) {} - - UC8159(uint16_t width, uint16_t height, - uint8_t *frame_buffer, - spi_inst_t *spi, - uint CS, uint DC, uint SCK, uint MOSI, - uint BUSY = PIN_UNUSED, uint RESET = PIN_UNUSED) : - width(width), height(height), - frame_buffer(frame_buffer), - spi(spi), - CS(CS), DC(DC), SCK(SCK), MOSI(MOSI), BUSY(BUSY), RESET(RESET) {} + 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 init(); void busy_wait(); - bool is_busy(); void reset(); - void setup(); 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 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); - - void update(bool blocking = true); - void pixel(int x, int y, int v); }; } diff --git a/micropython/modules/picographics/micropython.cmake b/micropython/modules/picographics/micropython.cmake index 26813f56..84bba6ec 100644 --- a/micropython/modules/picographics/micropython.cmake +++ b/micropython/modules/picographics/micropython.cmake @@ -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" -) \ No newline at end of file +) diff --git a/micropython/modules/picographics/picographics.c b/micropython/modules/picographics/picographics.c index 24d8d0ac..af1e0df3 100644 --- a/micropython/modules/picographics/picographics.c +++ b/micropython/modules/picographics/picographics.c @@ -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 \ No newline at end of file +#endif diff --git a/micropython/modules/picographics/picographics.cpp b/micropython/modules/picographics/picographics.cpp index 3ba6a839..6dca17c8 100644 --- a/micropython/modules/picographics/picographics.cpp +++ b/micropython/modules/picographics/picographics.cpp @@ -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; } diff --git a/micropython/modules/picographics/picographics.h b/micropython/modules/picographics/picographics.h index 3a33eed5..33bde1e4 100644 --- a/micropython/modules/picographics/picographics.h +++ b/micropython/modules/picographics/picographics.h @@ -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); \ No newline at end of file +extern mp_obj_t ModPicoGraphics__del__(mp_obj_t self_in);