diff --git a/drivers/uc8151/uc8151.cpp b/drivers/uc8151/uc8151.cpp index 6292d03f..76477141 100644 --- a/drivers/uc8151/uc8151.cpp +++ b/drivers/uc8151/uc8151.cpp @@ -322,20 +322,18 @@ namespace pimoroni { setup(); }; - void UC8151::setup(uint8_t speed) { + void UC8151::setup() { reset(); - update_speed = speed; - uint8_t psr_setting = RES_128x296 | FORMAT_BW | BOOSTER_ON | RESET_NONE; - psr_setting |= speed == 0 ? LUT_OTP : LUT_REG; + psr_setting |= update_speed == 0 ? LUT_OTP : LUT_REG; psr_setting |= rotation == ROTATE_180 ? SHIFT_LEFT | SCAN_UP : SHIFT_RIGHT | SCAN_DOWN; command(PSR, 1, &psr_setting); - switch(speed) { + switch(update_speed) { case 0: // Note: the defult luts are built in so we don't really need to flash them here // they are preserved above for posterity and reference mostly. @@ -447,12 +445,10 @@ namespace pimoroni { command(reg, values.size(), (uint8_t *)values.begin()); } - void UC8151::set_update_speed(uint8_t speed) { - setup(speed); - } - - uint8_t UC8151::get_update_speed() { - return update_speed; + bool UC8151::set_update_speed(int update_speed) { + this->update_speed = (uint8_t)update_speed; + setup(); + return true; } uint32_t UC8151::update_time() { @@ -511,9 +507,7 @@ namespace pimoroni { command(DRF); // start display refresh if(blocking) { - busy_wait(); - - command(POF); // turn off + off(); } } @@ -534,9 +528,7 @@ namespace pimoroni { command(DRF); // start display refresh if(blocking) { - busy_wait(); - - command(POF); // turn off + off(); } } diff --git a/drivers/uc8151/uc8151.hpp b/drivers/uc8151/uc8151.hpp index 0208214f..4f1fd7b2 100644 --- a/drivers/uc8151/uc8151.hpp +++ b/drivers/uc8151/uc8151.hpp @@ -163,6 +163,7 @@ namespace pimoroni { bool is_busy() override; void update(PicoGraphics *graphics) override; void partial_update(PicoGraphics *graphics, Rect region) override; + bool set_update_speed(int update_speed) override; // UC8151 Specific void default_luts(); @@ -170,13 +171,12 @@ namespace pimoroni { void fast_luts(); void turbo_luts(); - void set_update_speed(uint8_t speed); uint8_t get_update_speed(); uint32_t update_time(); private: void init(); - void setup(uint8_t speed=0); + void setup(); void read(uint8_t reg, size_t len, uint8_t *data); void command(uint8_t reg, size_t len, const uint8_t *data); diff --git a/libraries/pico_graphics/pico_graphics.hpp b/libraries/pico_graphics/pico_graphics.hpp index f709ed90..7b7b5907 100644 --- a/libraries/pico_graphics/pico_graphics.hpp +++ b/libraries/pico_graphics/pico_graphics.hpp @@ -384,6 +384,7 @@ namespace pimoroni { virtual void update(PicoGraphics *display) {}; virtual void partial_update(PicoGraphics *display, Rect region) {}; + virtual bool set_update_speed(int update_speed) {return false;}; virtual void set_backlight(uint8_t brightness) {}; virtual bool is_busy() {return false;}; virtual void cleanup() {}; diff --git a/micropython/modules/picographics/picographics.c b/micropython/modules/picographics/picographics.c index af1e0df3..590432e1 100644 --- a/micropython/modules/picographics/picographics.c +++ b/micropython/modules/picographics/picographics.c @@ -9,7 +9,9 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(ModPicoGraphics_get_required_buffer_size_obj, M // Class Methods MP_DEFINE_CONST_FUN_OBJ_1(ModPicoGraphics_update_obj, ModPicoGraphics_update); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ModPicoGraphics_partial_update_obj, 5, 5, ModPicoGraphics_partial_update); MP_DEFINE_CONST_FUN_OBJ_2(ModPicoGraphics_set_backlight_obj, ModPicoGraphics_set_backlight); +MP_DEFINE_CONST_FUN_OBJ_2(ModPicoGraphics_set_update_speed_obj, ModPicoGraphics_set_update_speed); // Palette management MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ModPicoGraphics_update_pen_obj, 5, 5, ModPicoGraphics_update_pen); @@ -56,6 +58,8 @@ STATIC const mp_rom_map_elem_t ModPicoGraphics_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&ModPicoGraphics_clear_obj) }, { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&ModPicoGraphics_update_obj) }, + { MP_ROM_QSTR(MP_QSTR_partial_update), MP_ROM_PTR(&ModPicoGraphics_partial_update_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_update_speed), MP_ROM_PTR(&ModPicoGraphics_set_update_speed_obj) }, { MP_ROM_QSTR(MP_QSTR_set_clip), MP_ROM_PTR(&ModPicoGraphics_set_clip_obj) }, { MP_ROM_QSTR(MP_QSTR_remove_clip), MP_ROM_PTR(&ModPicoGraphics_remove_clip_obj) }, { MP_ROM_QSTR(MP_QSTR_pixel_span), MP_ROM_PTR(&ModPicoGraphics_pixel_span_obj) }, diff --git a/micropython/modules/picographics/picographics.cpp b/micropython/modules/picographics/picographics.cpp index cc201ee7..78c73c85 100644 --- a/micropython/modules/picographics/picographics.cpp +++ b/micropython/modules/picographics/picographics.cpp @@ -34,60 +34,76 @@ typedef struct _ModPicoGraphics_obj_t { DisplayDriver *display; void *spritedata; void *buffer; + _PimoroniI2C_obj_t *i2c; //mp_obj_t scanline_callback; // Not really feasible in MicroPython } ModPicoGraphics_obj_t; -bool get_display_settings(PicoGraphicsDisplay display, int &width, int &height, int &rotate, int &pen_type) { +bool get_display_settings(PicoGraphicsDisplay display, int &width, int &height, int &rotate, int &pen_type, PicoGraphicsBusType &bus_type) { switch(display) { case DISPLAY_PICO_DISPLAY: width = 240; height = 135; + bus_type = BUS_SPI; // Portrait to match labelling if(rotate == -1) rotate = (int)Rotation::ROTATE_270; if(pen_type == -1) pen_type = PEN_RGB332; break; - case DISPLAY_PICO_DISPLAY_2: case DISPLAY_TUFTY_2040: width = 320; height = 240; + bus_type = BUS_PARALLEL; // Tufty display is upside-down if(rotate == -1) rotate = (int)Rotation::ROTATE_180; if(pen_type == -1) pen_type = PEN_RGB332; break; + case DISPLAY_PICO_DISPLAY_2: + width = 320; + height = 240; + bus_type = BUS_SPI; + // Tufty display is upside-down + if(rotate == -1) rotate = (int)Rotation::ROTATE_0; + if(pen_type == -1) pen_type = PEN_RGB332; + break; case DISPLAY_PICO_EXPLORER: case DISPLAY_LCD_240X240: case DISPLAY_ENVIRO_PLUS: width = 240; height = 240; + bus_type = BUS_SPI; if(pen_type == -1) pen_type = PEN_RGB332; break; case DISPLAY_ROUND_LCD_240X240: width = 240; height = 240; + bus_type = BUS_SPI; if(pen_type == -1) pen_type = PEN_RGB332; break; case DISPLAY_LCD_160X80: width = 160; height = 80; + bus_type = BUS_SPI; if(pen_type == -1) pen_type = PEN_RGB332; break; case DISPLAY_I2C_OLED_128X128: width = 128; height = 128; + bus_type = BUS_I2C; if(rotate == -1) rotate = (int)Rotation::ROTATE_0; if(pen_type == -1) pen_type = PEN_1BIT; break; case DISPLAY_INKY_PACK: width = 296; height = 128; + bus_type = BUS_SPI; 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; + bus_type = BUS_SPI; + if(rotate == -1) rotate = (int)Rotation::ROTATE_0; + if(pen_type == -1) pen_type = PEN_P4; break; default: return false; @@ -140,68 +156,67 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size int height = 0; int pen_type = args[ARG_pen_type].u_int; int rotate = args[ARG_rotate].u_int; - if(!get_display_settings(display, width, height, rotate, pen_type)) mp_raise_ValueError("Unsupported display!"); + PicoGraphicsBusType bus_type = BUS_SPI; + if(!get_display_settings(display, width, height, rotate, pen_type, bus_type)) mp_raise_ValueError("Unsupported display!"); if(rotate == -1) rotate = (int)Rotation::ROTATE_0; + pimoroni::SPIPins spi_bus = get_spi_pins(BG_SPI_FRONT); + pimoroni::ParallelPins parallel_bus = {10, 11, 12, 13, 14, 2}; // Default for Tufty 2040 parallel + pimoroni::I2C *i2c_bus = nullptr; + + if (mp_obj_is_type(args[ARG_bus].u_obj, &SPIPins_type)) { + if(bus_type != BUS_SPI) mp_raise_ValueError("unexpected SPI bus!"); + _PimoroniBus_obj_t *bus = (_PimoroniBus_obj_t *)MP_OBJ_TO_PTR(args[ARG_bus].u_obj); + spi_bus = *(SPIPins *)(bus->pins); + + } else if (mp_obj_is_type(args[ARG_bus].u_obj, &ParallelPins_type)) { + if(bus_type != BUS_PARALLEL) mp_raise_ValueError("unexpected Parallel bus!"); + _PimoroniBus_obj_t *bus = (_PimoroniBus_obj_t *)MP_OBJ_TO_PTR(args[ARG_bus].u_obj); + parallel_bus = *(ParallelPins *)(bus->pins); + + } else if (mp_obj_is_type(args[ARG_bus].u_obj, &PimoroniI2C_type) || MP_OBJ_IS_TYPE(args[ARG_bus].u_obj, &machine_hw_i2c_type)) { + if(bus_type != BUS_I2C) mp_raise_ValueError("unexpected I2C bus!"); + self->i2c = PimoroniI2C_from_machine_i2c_or_native(args[ARG_bus].u_obj); + i2c_bus = (pimoroni::I2C *)(self->i2c->i2c); + + } else { + // No bus given, fall back to defaults + if(bus_type == BUS_I2C) { + self->i2c = (_PimoroniI2C_obj_t *)MP_OBJ_TO_PTR(PimoroniI2C_make_new(&PimoroniI2C_type, 0, 0, nullptr)); + i2c_bus = (pimoroni::I2C *)(self->i2c->i2c); + } else if (bus_type == BUS_SPI) { + if(display == DISPLAY_INKY_FRAME) { + spi_bus = {PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_FRONT_CS, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, PIN_UNUSED, 27, PIN_UNUSED}; + } else if (display == DISPLAY_INKY_PACK) { + spi_bus = {PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_FRONT_CS, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, PIN_UNUSED, 20, PIN_UNUSED}; + } + } + } + // Try to create an appropriate display driver 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!"); - } + self->display = m_new_class(UC8159, width, height, spi_bus); + } 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)) { - _PimoroniBus_obj_t *bus = (_PimoroniBus_obj_t *)MP_OBJ_TO_PTR(args[ARG_bus].u_obj); - self->display = m_new_class(ST7789, width, height, (Rotation)rotate, *(ParallelPins *)(bus->pins)); - } else { - mp_raise_ValueError("ParallelBus expected!"); - } - } else if (display == DISPLAY_LCD_160X80) { - if (args[ARG_bus].u_obj == mp_const_none) { - self->display = m_new_class(ST7735, width, height, get_spi_pins(BG_SPI_FRONT)); - } 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(ST7735, width, height, *(SPIPins *)(bus->pins)); - } else { - mp_raise_ValueError("SPIBus expected!"); - } - } else if (display == DISPLAY_I2C_OLED_128X128) { - if (mp_obj_is_type(args[ARG_bus].u_obj, &PimoroniI2C_type)) { - _PimoroniI2C_obj_t *i2c = PimoroniI2C_from_machine_i2c_or_native(args[ARG_bus].u_obj); + self->display = m_new_class(ST7789, width, height, (Rotation)rotate, parallel_bus); + + } else if (display == DISPLAY_LCD_160X80) { + self->display = m_new_class(ST7735, width, height, spi_bus); + + } else if (display == DISPLAY_I2C_OLED_128X128) { + int i2c_address = args[ARG_i2c_address].u_int; + if(i2c_address == -1) i2c_address = SH1107::DEFAULT_I2C_ADDRESS; + + self->display = m_new_class(SH1107, width, height, *i2c_bus, (uint8_t)i2c_address); - int i2c_address = args[ARG_i2c_address].u_int; - if(i2c_address == -1) i2c_address = SH1107::DEFAULT_I2C_ADDRESS; - self->display = m_new_class(SH1107, width, height, *(pimoroni::I2C *)(i2c->i2c), (uint8_t)i2c_address); - } else { - mp_raise_ValueError("I2C bus required!"); - } } else if (display == DISPLAY_INKY_PACK) { - if (args[ARG_bus].u_obj == mp_const_none) { - self->display = m_new_class(UC8151, width, height, (Rotation)rotate); - } 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(UC8151, width, height, (Rotation)rotate, *(SPIPins *)(bus->pins)); - } else { - mp_raise_ValueError("SPIBus expected!"); - } + self->display = m_new_class(UC8151, width, height, (Rotation)rotate, spi_bus); + } else { - if (args[ARG_bus].u_obj == mp_const_none) { - self->display = m_new_class(ST7789, width, height, (Rotation)rotate, round, get_spi_pins(BG_SPI_FRONT)); - } 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(ST7789, width, height, (Rotation)rotate, round, *(SPIPins *)(bus->pins)); - } else { - mp_raise_ValueError("SPIBus expected!"); - } + self->display = m_new_class(ST7789, width, height, (Rotation)rotate, round, spi_bus); } // Create or fetch buffer @@ -254,7 +269,7 @@ 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 - if (display != DISPLAY_INKY_FRAME) { + if (display != DISPLAY_INKY_FRAME && display != DISPLAY_INKY_PACK) { self->display->update(self->graphics); } @@ -381,7 +396,8 @@ mp_obj_t ModPicoGraphics_get_required_buffer_size(mp_obj_t display_in, mp_obj_t int height = 0; int rotation = 0; int pen_type = mp_obj_get_int(pen_type_in); - if(!get_display_settings(display, width, height, rotation, pen_type)) mp_raise_ValueError("Unsupported display!"); + PicoGraphicsBusType bus_type = BUS_SPI; + if(!get_display_settings(display, width, height, rotation, pen_type, bus_type)) mp_raise_ValueError("Unsupported display!"); size_t required_size = get_required_buffer_size((PicoGraphicsPenType)pen_type, width, height); if(required_size == 0) mp_raise_ValueError("Unsupported pen type!"); @@ -437,6 +453,45 @@ mp_obj_t ModPicoGraphics_update(mp_obj_t self_in) { return mp_const_none; } +mp_obj_t ModPicoGraphics_partial_update(size_t n_args, const mp_obj_t *args) { + enum { ARG_self, ARG_x, ARG_y, ARG_w, ARG_h }; + + ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self], ModPicoGraphics_obj_t); + + while(self->display->is_busy()) { + #ifdef MICROPY_EVENT_POLL_HOOK + MICROPY_EVENT_POLL_HOOK + #endif + } + + self->display->partial_update(self->graphics, { + mp_obj_get_int(args[ARG_x]), + mp_obj_get_int(args[ARG_y]), + mp_obj_get_int(args[ARG_w]), + mp_obj_get_int(args[ARG_h]) + }); + + while(self->display->is_busy()) { + #ifdef MICROPY_EVENT_POLL_HOOK + MICROPY_EVENT_POLL_HOOK + #endif + } + + return mp_const_none; +} + +mp_obj_t ModPicoGraphics_set_update_speed(mp_obj_t self_in, mp_obj_t update_speed) { + ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t); + + int speed = mp_obj_get_int(update_speed); + + if(!self->display->set_update_speed(speed)) { + mp_raise_ValueError("update speed not supported"); + } + + return mp_const_none; +} + mp_obj_t ModPicoGraphics_set_backlight(mp_obj_t self_in, mp_obj_t brightness) { ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t); diff --git a/micropython/modules/picographics/picographics.h b/micropython/modules/picographics/picographics.h index 33bde1e4..05f9481e 100644 --- a/micropython/modules/picographics/picographics.h +++ b/micropython/modules/picographics/picographics.h @@ -24,6 +24,12 @@ enum PicoGraphicsPenType { PEN_RGB565 }; +enum PicoGraphicsBusType { + BUS_I2C, + BUS_SPI, + BUS_PARALLEL +}; + // Type extern const mp_obj_type_t ModPicoGraphics_type; @@ -38,7 +44,9 @@ extern mp_obj_t ModPicoGraphics_get_required_buffer_size(mp_obj_t display_in, mp extern 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); extern mp_obj_t ModPicoGraphics_update(mp_obj_t self_in); +extern mp_obj_t ModPicoGraphics_partial_update(size_t n_args, const mp_obj_t *args); extern mp_obj_t ModPicoGraphics_set_backlight(mp_obj_t self_in, mp_obj_t brightness); +extern mp_obj_t ModPicoGraphics_set_update_speed(mp_obj_t self_in, mp_obj_t update_speed); // Palette management extern mp_obj_t ModPicoGraphics_update_pen(size_t n_args, const mp_obj_t *args); diff --git a/micropython/modules/pimoroni_bus/pimoroni_bus.cpp b/micropython/modules/pimoroni_bus/pimoroni_bus.cpp index 095de541..12ac7fc8 100644 --- a/micropython/modules/pimoroni_bus/pimoroni_bus.cpp +++ b/micropython/modules/pimoroni_bus/pimoroni_bus.cpp @@ -8,6 +8,7 @@ extern "C" { #include "pimoroni_bus.h" #include "py/mperrno.h" + void PimoroniBus_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { _PimoroniBus_obj_t *self = MP_OBJ_TO_PTR2(self_in, _PimoroniBus_obj_t); if(self->base.type == &SPIPins_type) {