diff --git a/common/pimoroni_bus.cmake b/common/pimoroni_bus.cmake new file mode 100644 index 00000000..fb24b56e --- /dev/null +++ b/common/pimoroni_bus.cmake @@ -0,0 +1,11 @@ +set(LIB_NAME pimoroni_bus) +add_library(${LIB_NAME} INTERFACE) + +target_sources(${LIB_NAME} INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/${LIB_NAME}.cpp +) + +target_include_directories(${LIB_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR}) + +# Pull in pico libraries that we need +target_link_libraries(${LIB_NAME} INTERFACE pico_stdlib hardware_spi) diff --git a/common/pimoroni_bus.cpp b/common/pimoroni_bus.cpp new file mode 100644 index 00000000..794ba973 --- /dev/null +++ b/common/pimoroni_bus.cpp @@ -0,0 +1,15 @@ +#include "pimoroni_bus.hpp" + +namespace pimoroni { + SPIPins get_spi_pins(BG_SPI_SLOT slot) { + switch(slot) { + case PICO_EXPLORER_ONBOARD: + return {PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_FRONT_CS, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, SPI_DEFAULT_MISO, PIN_UNUSED}; + case BG_SPI_FRONT: + return {PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_FRONT_CS, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, SPI_DEFAULT_MISO, SPI_BG_FRONT_PWM}; + case BG_SPI_BACK: + return {PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_BACK_CS, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, SPI_DEFAULT_MISO, SPI_BG_BACK_PWM}; + } + return {PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_FRONT_CS, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, SPI_DEFAULT_MISO, SPI_BG_FRONT_PWM}; + }; +} \ No newline at end of file diff --git a/common/pimoroni_bus.hpp b/common/pimoroni_bus.hpp new file mode 100644 index 00000000..eafecc19 --- /dev/null +++ b/common/pimoroni_bus.hpp @@ -0,0 +1,26 @@ +#pragma once +#include "pimoroni_common.hpp" +#include "hardware/gpio.h" +#include "hardware/spi.h" + +namespace pimoroni { + struct SPIPins { + spi_inst_t *spi; + uint cs; + uint sck; + uint mosi; + uint miso; + uint bl; + }; + + struct ParallelPins { + uint cs; + uint dc; + uint wr_sck; + uint rd_sck; + uint d0; + uint bl; + }; + + SPIPins get_spi_pins(BG_SPI_SLOT slot); +} \ No newline at end of file diff --git a/common/pimoroni_common.hpp b/common/pimoroni_common.hpp index 03b64f1f..90c949b2 100644 --- a/common/pimoroni_common.hpp +++ b/common/pimoroni_common.hpp @@ -53,6 +53,13 @@ namespace pimoroni { INTERSTATE_75, SERVO_2040 }; + + enum Rotation { + ROTATE_0 = 0, + ROTATE_90 = 90, + ROTATE_180 = 180, + ROTATE_270 = 270 + }; enum Polarity { ACTIVE_LOW = 0, diff --git a/drivers/st7789/st7789.cmake b/drivers/st7789/st7789.cmake index 5dbf4c8b..80420fef 100644 --- a/drivers/st7789/st7789.cmake +++ b/drivers/st7789/st7789.cmake @@ -9,4 +9,4 @@ target_include_directories(${DRIVER_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR}) target_include_directories(st7789 INTERFACE ${CMAKE_CURRENT_LIST_DIR}) # Pull in pico libraries that we need -target_link_libraries(${DRIVER_NAME} INTERFACE pico_stdlib hardware_spi hardware_pwm hardware_dma) +target_link_libraries(${DRIVER_NAME} INTERFACE pico_stdlib pimoroni_bus hardware_spi hardware_pwm hardware_dma) diff --git a/drivers/st7789/st7789.cpp b/drivers/st7789/st7789.cpp index 089f1cd4..32068544 100644 --- a/drivers/st7789/st7789.cpp +++ b/drivers/st7789/st7789.cpp @@ -70,8 +70,7 @@ namespace pimoroni { command(reg::GMCTRN1, 14, "\xD0\x04\x0C\x11\x13\x2C\x3F\x44\x51\x2F\x1F\x1F\x20\x23"); } - if((width == 320 && height == 240) - || (width == 240 && height == 320)) { + if(width == 320 && height == 240) { command(reg::GCTRL, 1, "\x35"); command(reg::VCOMS, 1, "\x1f"); command(0xd6, 1, "\xa1"); // ??? @@ -85,7 +84,7 @@ namespace pimoroni { sleep_ms(100); - configure_display(false); + configure_display(rotation); if(bl != PIN_UNUSED) { //update(); // Send the new buffer to the display to clear any previous content @@ -94,9 +93,15 @@ namespace pimoroni { } } - void ST7789::configure_display(bool rotate180) { + void ST7789::configure_display(Rotation rotate) { + + bool rotate180 = rotate == ROTATE_180 || rotate == ROTATE_90; + + if(rotate == ROTATE_90 || rotate == ROTATE_270) { + std::swap(width, height); + } + // 240x240 Square and Round LCD Breakouts - // TODO: How can we support 90 degree rotations here? if(width == 240 && height == 240) { caset[0] = 0; caset[1] = 239; @@ -108,6 +113,7 @@ namespace pimoroni { raset[1] = rotate180 ? 329 : 239; } madctl = rotate180 ? (MADCTL::COL_ORDER | MADCTL::ROW_ORDER) : 0; + if (rotate == ROTATE_90) madctl |= MADCTL::SWAP_XY; madctl |= MADCTL::HORIZ_ORDER; } @@ -160,30 +166,6 @@ namespace pimoroni { command(reg::MADCTL, 1, (char *)&madctl); } - spi_inst_t* ST7789::get_spi() const { - return spi; - } - - uint ST7789::get_cs() const { - return cs; - } - - uint ST7789::get_dc() const { - return dc; - } - - uint ST7789::get_sck() const { - return wr_sck; - } - - uint ST7789::get_mosi() const { - return d0; - } - - uint ST7789::get_bl() const { - return bl; - } - void ST7789::write_blocking_parallel(const uint8_t *src, size_t len) { uint32_t mask = 0xff << d0; while(len--) { @@ -226,10 +208,21 @@ namespace pimoroni { // 8-bit framebuffer with palette conversion update void ST7789::update(uint16_t *palette) { - command(reg::RAMWR); + uint8_t command = reg::RAMWR; uint16_t row[width]; - gpio_put(dc, 1); // data mode + + gpio_put(dc, 0); // command mode + gpio_put(cs, 0); + + if(spi) { + spi_write_blocking(spi, &command, 1); + } else { + write_blocking_parallel(&command, 1); + } + + gpio_put(dc, 1); // data mode + for(auto y = 0u; y < height; y++) { for(auto x = 0u; x < width; x++) { auto i = y * width + x; @@ -242,6 +235,7 @@ namespace pimoroni { write_blocking_parallel((const uint8_t*)row, width * sizeof(uint16_t)); } } + gpio_put(cs, 1); } @@ -252,8 +246,4 @@ namespace pimoroni { uint16_t value = (uint16_t)(pow((float)(brightness) / 255.0f, gamma) * 65535.0f + 0.5f); pwm_set_gpio_level(bl, value); } - - void ST7789::flip(){ - configure_display(true); - } } diff --git a/drivers/st7789/st7789.hpp b/drivers/st7789/st7789.hpp index df15d747..1effcb41 100644 --- a/drivers/st7789/st7789.hpp +++ b/drivers/st7789/st7789.hpp @@ -3,22 +3,28 @@ #include "hardware/spi.h" #include "hardware/gpio.h" #include "hardware/pwm.h" -#include "../../common/pimoroni_common.hpp" +#include "common/pimoroni_common.hpp" +#include "common/pimoroni_bus.hpp" + +#include namespace pimoroni { class ST7789 { spi_inst_t *spi = PIMORONI_SPI_DEFAULT_INSTANCE; + + public: + // screen properties + uint16_t width; + uint16_t height; + Rotation rotation; + bool round; //-------------------------------------------------- // Variables //-------------------------------------------------- private: - // screen properties - uint16_t width; - uint16_t height; - bool round; // interface pins with our standard defaults where appropriate uint cs; @@ -35,22 +41,16 @@ namespace pimoroni { public: + // frame buffer where pixel data is stored void *frame_buffer; // Parallel init - ST7789(uint16_t width, uint16_t height, void *frame_buffer, - uint cs, uint dc, uint wr_sck, uint rd_sck, uint d0, uint bl = PIN_UNUSED) : + ST7789(uint16_t width, uint16_t height, Rotation rotation, void *frame_buffer, ParallelPins pins) : spi(nullptr), - width(width), height(height), round(false), - cs(cs), dc(dc), wr_sck(wr_sck), rd_sck(rd_sck), d0(d0), bl(bl), frame_buffer(frame_buffer) { + width(width), height(height), rotation(rotation), round(false), + cs(pins.cs), dc(pins.dc), wr_sck(pins.wr_sck), rd_sck(pins.rd_sck), d0(pins.d0), bl(pins.bl), frame_buffer(frame_buffer) { - gpio_set_function(cs, GPIO_FUNC_SIO); - gpio_set_dir(cs, GPIO_OUT); - - gpio_set_function(dc, GPIO_FUNC_SIO); - gpio_set_dir(dc, GPIO_OUT); - gpio_set_function(wr_sck, GPIO_FUNC_SIO); gpio_set_dir(wr_sck, GPIO_OUT); @@ -68,22 +68,14 @@ namespace pimoroni { } // Serial init - ST7789(uint16_t width, uint16_t height, bool round, void *frame_buffer, - spi_inst_t *spi, - uint cs, uint dc, uint sck, uint mosi, uint bl = PIN_UNUSED) : - spi(spi), - width(width), height(height), round(round), - cs(cs), dc(dc), wr_sck(sck), d0(mosi), bl(bl), frame_buffer(frame_buffer) { + ST7789(uint16_t width, uint16_t height, Rotation rotation, bool round, void *frame_buffer, SPIPins pins) : + spi(pins.spi), + width(width), height(height), rotation(rotation), round(round), + cs(pins.cs), dc(pins.miso), wr_sck(pins.sck), d0(pins.mosi), bl(pins.bl), frame_buffer(frame_buffer) { // configure spi interface and pins spi_init(spi, SPI_BAUD); - 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_set_function(wr_sck, GPIO_FUNC_SPI); gpio_set_function(d0, GPIO_FUNC_SPI); @@ -91,52 +83,26 @@ namespace pimoroni { } void init(); - void configure_display(bool rotate180); - - spi_inst_t* get_spi() const; - uint get_cs() const; - uint get_dc() const; - uint get_sck() const; - uint get_mosi() const; - uint get_bl() const; - void command(uint8_t command, size_t len = 0, const char *data = NULL); + void set_backlight(uint8_t brightness); + void update(); void update(uint16_t *palette); - void set_backlight(uint8_t brightness); - void flip(); - - static uint get_slot_cs(BG_SPI_SLOT slot) { - switch(slot) { - case PICO_EXPLORER_ONBOARD: - return SPI_BG_FRONT_CS; - case BG_SPI_FRONT: - return SPI_BG_FRONT_CS; - case BG_SPI_BACK: - return SPI_BG_BACK_CS; - } - return PIN_UNUSED; - }; - - static uint get_slot_bl(BG_SPI_SLOT slot) { - switch(slot) { - case PICO_EXPLORER_ONBOARD: - return PIN_UNUSED; - case BG_SPI_FRONT: - return SPI_BG_FRONT_PWM; - case BG_SPI_BACK: - return SPI_BG_BACK_PWM; - } - return PIN_UNUSED; - }; private: + void configure_display(Rotation rotate); void write_blocking_parallel(const uint8_t *src, size_t len); void common_init() { if(!this->frame_buffer) { this->frame_buffer = new uint8_t[width * height]; } + 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); + // if a backlight pin is provided then set it up for // pwm control if(bl != PIN_UNUSED) { diff --git a/libraries/generic_st7789/generic_st7789.cmake b/libraries/generic_st7789/generic_st7789.cmake index f25f5797..25a98272 100644 --- a/libraries/generic_st7789/generic_st7789.cmake +++ b/libraries/generic_st7789/generic_st7789.cmake @@ -8,4 +8,4 @@ target_sources(${LIB_NAME} INTERFACE target_include_directories(${LIB_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR}) # Pull in pico libraries that we need -target_link_libraries(${LIB_NAME} INTERFACE pico_stdlib hardware_spi hardware_pwm hardware_dma st7789 pico_graphics) \ No newline at end of file +target_link_libraries(${LIB_NAME} INTERFACE pico_stdlib hardware_spi hardware_pwm hardware_dma pimoroni_bus st7789 pico_graphics) \ No newline at end of file diff --git a/libraries/generic_st7789/generic_st7789.cpp b/libraries/generic_st7789/generic_st7789.cpp index d0dc2d98..7bb09a4b 100644 --- a/libraries/generic_st7789/generic_st7789.cpp +++ b/libraries/generic_st7789/generic_st7789.cpp @@ -5,43 +5,11 @@ namespace pimoroni { - spi_inst_t* ST7789Generic::get_spi() const { - return st7789.get_spi(); - } - - int ST7789Generic::get_cs() const { - return st7789.get_cs(); - } - - int ST7789Generic::get_dc() const { - return st7789.get_dc(); - } - - int ST7789Generic::get_sck() const { - return st7789.get_sck(); - } - - int ST7789Generic::get_mosi() const { - return st7789.get_mosi(); - } - - int ST7789Generic::get_bl() const { - return st7789.get_bl(); - } - void ST7789Generic::update() { st7789.update(palette); } - void ST7789Generic::flip() { - st7789.configure_display(true); - } - void ST7789Generic::set_backlight(uint8_t brightness) { st7789.set_backlight(brightness); } - - void ST7789Generic::configure_display(bool rotate180) { - st7789.configure_display(rotate180); - } } diff --git a/libraries/generic_st7789/generic_st7789.hpp b/libraries/generic_st7789/generic_st7789.hpp index 3de609dd..857027bb 100644 --- a/libraries/generic_st7789/generic_st7789.hpp +++ b/libraries/generic_st7789/generic_st7789.hpp @@ -2,6 +2,7 @@ #include "drivers/st7789/st7789.hpp" #include "libraries/pico_graphics/pico_graphics.hpp" +#include "common/pimoroni_bus.hpp" namespace pimoroni { @@ -10,50 +11,32 @@ namespace pimoroni { ST7789 st7789; public: - ST7789Generic(uint16_t width, uint16_t height, bool round=false, void *frame_buffer=nullptr) : + ST7789Generic(uint16_t width, uint16_t height, Rotation rotation, bool round=false, void *frame_buffer=nullptr) : PicoGraphics(width, height, frame_buffer), - st7789(width, height, round, frame_buffer, PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_FRONT_CS, SPI_DEFAULT_MISO, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, SPI_BG_FRONT_PWM) { - this->frame_buffer = (Pen *)st7789.frame_buffer; - this->st7789.init(); - this->st7789.update(palette); + st7789(width, height, rotation, round, frame_buffer, get_spi_pins(BG_SPI_FRONT)) { + common_init(); }; - ST7789Generic(uint16_t width, uint16_t height, bool round, void *frame_buffer, BG_SPI_SLOT slot) : + ST7789Generic(uint16_t width, uint16_t height, Rotation rotation, bool round, void *frame_buffer, SPIPins bus_pins) : PicoGraphics(width, height, frame_buffer), - st7789(width, height, round, frame_buffer, PIMORONI_SPI_DEFAULT_INSTANCE, st7789.get_slot_cs(slot), SPI_DEFAULT_MISO, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, st7789.get_slot_bl(slot)) { - this->frame_buffer = (Pen *)st7789.frame_buffer; - this->st7789.init(); - this->st7789.update(palette); + st7789(width, height, rotation, round, frame_buffer, bus_pins) { + common_init(); }; - ST7789Generic(uint16_t width, uint16_t height, bool round, void *frame_buffer, - spi_inst_t *spi, - uint cs, uint dc, uint sck, uint mosi, uint bl = PIN_UNUSED) : + ST7789Generic(uint16_t width, uint16_t height, Rotation rotation, void *frame_buffer, ParallelPins bus_pins) : PicoGraphics(width, height, frame_buffer), - st7789(width, height, round, frame_buffer, spi, cs, dc, sck, mosi, bl) { - this->frame_buffer = (Pen *)st7789.frame_buffer; - this->st7789.init(); - this->st7789.update(palette); + st7789(width, height, rotation, frame_buffer, bus_pins) { + common_init(); }; - ST7789Generic(uint16_t width, uint16_t height, void *frame_buffer, - uint cs, uint dc, uint wr_sck, uint rd_sck, uint d0, uint bl = PIN_UNUSED) : - PicoGraphics(width, height, frame_buffer), - st7789(width, height, frame_buffer, cs, dc, wr_sck, rd_sck, d0, bl) { - this->frame_buffer = (Pen *)st7789.frame_buffer; - this->st7789.init(); - this->st7789.update(palette); - }; - - spi_inst_t* get_spi() const; - int get_cs() const; - int get_dc() const; - int get_sck() const; - int get_mosi() const; - int get_bl() const; + void common_init() { + this->frame_buffer = (Pen *)st7789.frame_buffer; + this->st7789.init(); + this->set_dimensions(this->st7789.width, this->st7789.height); + this->st7789.update(palette); + } void update(); - [[deprecated("Use configure_display(true) instead.")]] void flip(); void set_backlight(uint8_t brightness); void configure_display(bool rotate180); void set_framebuffer(void* frame_buffer) { diff --git a/libraries/pico_graphics/pico_graphics.hpp b/libraries/pico_graphics/pico_graphics.hpp index dbda0324..f9fba09e 100644 --- a/libraries/pico_graphics/pico_graphics.hpp +++ b/libraries/pico_graphics/pico_graphics.hpp @@ -87,6 +87,13 @@ namespace pimoroni { return __builtin_bswap16(p); } + void set_dimensions(int width, int height) { + bounds.w = width; + bounds.h = height; + clip.w = width; + clip.h = height; + } + int create_pen(uint8_t r, uint8_t g, uint8_t b); void empty_palette(); diff --git a/micropython/examples/breakout_colourlcd240x240/demo.py b/micropython/examples/breakout_colourlcd240x240/colourlcd240x240_demo.py similarity index 80% rename from micropython/examples/breakout_colourlcd240x240/demo.py rename to micropython/examples/breakout_colourlcd240x240/colourlcd240x240_demo.py index 90ae788c..fcf64c4b 100644 --- a/micropython/examples/breakout_colourlcd240x240/demo.py +++ b/micropython/examples/breakout_colourlcd240x240/colourlcd240x240_demo.py @@ -1,11 +1,15 @@ import time import random -from st7789 import ST7789 +from st7789 import ST7789, PALETTE_USER WIDTH, HEIGHT = 240, 240 display = ST7789(WIDTH, HEIGHT, round=False) +# We're creating 100 balls with their own individual colour and 1 BG colour +# for a total of 101 colours, which will all fit in the 256 entry palette! +display.set_palette_mode(PALETTE_USER) + display.set_backlight(1.0) @@ -33,9 +37,11 @@ for i in range(0, 100): display.create_pen(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)), ) ) + +BG = display.create_pen(40, 40, 40) while True: - display.set_pen(40, 40, 40) + display.set_pen(BG) display.clear() for ball in balls: @@ -58,3 +64,4 @@ while True: display.update() time.sleep(0.01) + diff --git a/micropython/modules/pimoroni_bus/micropython.cmake b/micropython/modules/pimoroni_bus/micropython.cmake new file mode 100644 index 00000000..c9eb1aba --- /dev/null +++ b/micropython/modules/pimoroni_bus/micropython.cmake @@ -0,0 +1,19 @@ +set(MOD_NAME pimoroni_bus) +string(TOUPPER ${MOD_NAME} MOD_NAME_UPPER) +add_library(usermod_${MOD_NAME} INTERFACE) + +target_sources(usermod_${MOD_NAME} INTERFACE + #${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.c + #${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.cpp + ${CMAKE_CURRENT_LIST_DIR}/../../../common/pimoroni_bus.cpp +) + +target_include_directories(usermod_${MOD_NAME} INTERFACE + ${CMAKE_CURRENT_LIST_DIR} +) + +target_compile_definitions(usermod_${MOD_NAME} INTERFACE + MODULE_${MOD_NAME_UPPER}_ENABLED=1 +) + +target_link_libraries(usermod INTERFACE usermod_${MOD_NAME}) \ No newline at end of file diff --git a/micropython/modules/st7789/st7789.c b/micropython/modules/st7789/st7789.c index 7881aad1..86092b31 100644 --- a/micropython/modules/st7789/st7789.c +++ b/micropython/modules/st7789/st7789.c @@ -32,6 +32,9 @@ MP_DEFINE_CONST_FUN_OBJ_KW(GenericST7789_polygon_obj, 2, GenericST7789_polygon); MP_DEFINE_CONST_FUN_OBJ_KW(GenericST7789_triangle_obj, 1, GenericST7789_triangle); MP_DEFINE_CONST_FUN_OBJ_KW(GenericST7789_line_obj, 1, GenericST7789_line); +// Utility +MP_DEFINE_CONST_FUN_OBJ_1(GenericST7789_get_bounds_obj, GenericST7789_get_bounds); + STATIC const mp_rom_map_elem_t GenericST7789_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_pixel), MP_ROM_PTR(&GenericST7789_pixel_obj) }, { MP_ROM_QSTR(MP_QSTR_set_pen), MP_ROM_PTR(&GenericST7789_set_pen_obj) }, @@ -55,6 +58,7 @@ STATIC const mp_rom_map_elem_t GenericST7789_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_triangle), MP_ROM_PTR(&GenericST7789_triangle_obj) }, { MP_ROM_QSTR(MP_QSTR_line), MP_ROM_PTR(&GenericST7789_line_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_bounds), MP_ROM_PTR(&GenericST7789_get_bounds_obj) }, { MP_ROM_QSTR(MP_QSTR_set_framebuffer), MP_ROM_PTR(&GenericST7789_set_framebuffer_obj) }, }; STATIC MP_DEFINE_CONST_DICT(GenericST7789_locals_dict, GenericST7789_locals_dict_table); @@ -63,7 +67,6 @@ STATIC MP_DEFINE_CONST_DICT(GenericST7789_locals_dict, GenericST7789_locals_dict const mp_obj_type_t GenericST7789_type = { { &mp_type_type }, .name = MP_QSTR_st7789, - .print = GenericST7789_print, .make_new = GenericST7789_make_new, .locals_dict = (mp_obj_dict_t*)&GenericST7789_locals_dict, }; @@ -71,22 +74,37 @@ const mp_obj_type_t GenericST7789_type = { const mp_obj_type_t GenericST7789Parallel_type = { { &mp_type_type }, .name = MP_QSTR_st7789, - .print = GenericST7789_print, .make_new = GenericST7789Parallel_make_new, .locals_dict = (mp_obj_dict_t*)&GenericST7789_locals_dict, }; +const mp_obj_type_t GenericST7789SPI_type = { + { &mp_type_type }, + .name = MP_QSTR_st7789, + .make_new = GenericST7789SPI_make_new, + .locals_dict = (mp_obj_dict_t*)&GenericST7789_locals_dict, +}; + /***** Module Globals *****/ STATIC const mp_map_elem_t st7789_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_st7789) }, { MP_ROM_QSTR(MP_QSTR_ST7789), (mp_obj_t)&GenericST7789_type }, { MP_ROM_QSTR(MP_QSTR_ST7789Parallel), (mp_obj_t)&GenericST7789Parallel_type }, + { MP_ROM_QSTR(MP_QSTR_ST7789SPI), (mp_obj_t)&GenericST7789Parallel_type }, { MP_ROM_QSTR(MP_QSTR_RGB332), MP_ROM_PTR(&GenericST7789_module_RGB332_obj) }, { MP_ROM_QSTR(MP_QSTR_RGB565), MP_ROM_PTR(&GenericST7789_module_RGB565_obj) }, { MP_ROM_QSTR(MP_QSTR_PALETTE_RGB332), MP_ROM_INT(0) }, { MP_ROM_QSTR(MP_QSTR_PALETTE_USER), MP_ROM_INT(1) }, + + { MP_ROM_QSTR(MP_QSTR_DISPLAY_LCD_240X240), MP_ROM_INT(0) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_ROUND_LCD_240X240), MP_ROM_INT(1) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_PICO_DISPLAY), MP_ROM_INT(2) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_PICO_DISPLAY_2), MP_ROM_INT(3) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_PICO_EXPLORER), MP_ROM_INT(4) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_TUFTY_2040), MP_ROM_INT(5) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_ENVIRO_PLUS), MP_ROM_INT(6) }, }; STATIC MP_DEFINE_CONST_DICT(mp_module_st7789_globals, st7789_globals_table); diff --git a/micropython/modules/st7789/st7789.cpp b/micropython/modules/st7789/st7789.cpp index 43430009..9042de7e 100644 --- a/micropython/modules/st7789/st7789.cpp +++ b/micropython/modules/st7789/st7789.cpp @@ -1,4 +1,6 @@ #include "libraries/generic_st7789/generic_st7789.hpp" +#include "common/pimoroni_common.hpp" +#include "common/pimoroni_bus.hpp" #include "micropython/modules/util.hpp" @@ -9,57 +11,101 @@ using namespace pimoroni; extern "C" { #include "st7789.h" -/***** Variables Struct *****/ +enum ST7789Display { + DISPLAY_LCD_240X240=0, + DISPLAY_ROUND_LCD_240X240, + DISPLAY_PICO_DISPLAY, + DISPLAY_PICO_DISPLAY_2, + DISPLAY_PICO_EXPLORER, + DISPLAY_TUFTY_2040, + DISPLAY_ENVIRO_PLUS +}; + typedef struct _GenericST7789_obj_t { mp_obj_base_t base; ST7789Generic *st7789; - bool parallel; void *buffer; } GenericST7789_obj_t; -/***** Print *****/ -void GenericST7789_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { - (void)kind; //Unused input parameter - GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(self_in, GenericST7789_obj_t); - - if(self->parallel) { - mp_print_str(print, "ST7789Parallel()"); - } else { - mp_print_str(print, "ST7789("); - - mp_print_str(print, "spi = "); - mp_obj_print_helper(print, mp_obj_new_int((self->st7789->get_spi() == spi0) ? 0 : 1), PRINT_REPR); - - mp_print_str(print, ", cs = "); - mp_obj_print_helper(print, mp_obj_new_int(self->st7789->get_cs()), PRINT_REPR); - - mp_print_str(print, ", dc = "); - mp_obj_print_helper(print, mp_obj_new_int(self->st7789->get_dc()), PRINT_REPR); - - mp_print_str(print, ", sck = "); - mp_obj_print_helper(print, mp_obj_new_int(self->st7789->get_sck()), PRINT_REPR); - - mp_print_str(print, ", mosi = "); - mp_obj_print_helper(print, mp_obj_new_int(self->st7789->get_mosi()), PRINT_REPR); - - mp_print_str(print, ", bl = "); - mp_obj_print_helper(print, mp_obj_new_int(self->st7789->get_bl()), PRINT_REPR); - - mp_print_str(print, ")"); - } -} - -/***** Constructor *****/ mp_obj_t GenericST7789_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { GenericST7789_obj_t *self = nullptr; - enum { ARG_width, ARG_height, ARG_round, ARG_rotate180, ARG_slot, ARG_buffer, ARG_spi, ARG_cs, ARG_dc, ARG_sck, ARG_mosi, ARG_bl }; + enum { ARG_display, ARG_rotate, ARG_slot, ARG_buffer }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_display, MP_ARG_INT | MP_ARG_REQUIRED }, + { MP_QSTR_rotate, MP_ARG_INT, { .u_int = Rotation::ROTATE_0 } }, + { MP_QSTR_slot, MP_ARG_INT, { .u_int = 0 } }, + { MP_QSTR_buffer, MP_ARG_OBJ, { .u_obj = mp_const_none } }, + }; + + // Parse args. + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + self = m_new_obj(GenericST7789_obj_t); + self->base.type = &GenericST7789_type; + + Rotation rotate = (Rotation)args[ARG_rotate].u_int; + bool round = false; + int width = 0; + int height = 0; + + ST7789Display display = (ST7789Display)args[ARG_display].u_int; + + switch(display) { + case DISPLAY_PICO_DISPLAY: + width = 240; + height = 135; + break; + case DISPLAY_PICO_DISPLAY_2: + case DISPLAY_TUFTY_2040: + width = 320; + height = 240; + break; + case DISPLAY_PICO_EXPLORER: + case DISPLAY_LCD_240X240: + case DISPLAY_ENVIRO_PLUS: + width = 240; + height = 240; + break; + case DISPLAY_ROUND_LCD_240X240: + width = 240; + height = 240; + round = true; + break; + } + + if (args[ARG_buffer].u_obj != mp_const_none) { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_RW); + self->buffer = bufinfo.buf; + if(bufinfo.len < (size_t)(width * height * sizeof(Pen))) { + mp_raise_ValueError("Supplied buffer is too small!"); + } + } else { + self->buffer = m_new(uint8_t, width * height * sizeof(Pen)); + } + + + if (display == DISPLAY_TUFTY_2040) { + self->st7789 = m_new_class(ST7789Generic, width, height, rotate, self->buffer, {10, 11, 12, 13, 14, 2}); + } else { + BG_SPI_SLOT slot = (BG_SPI_SLOT)args[ARG_slot].u_int; + self->st7789 = m_new_class(ST7789Generic, width, height, rotate, round, self->buffer, get_spi_pins(slot)); + } + + return MP_OBJ_FROM_PTR(self); +} + +mp_obj_t GenericST7789SPI_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + GenericST7789_obj_t *self = nullptr; + + enum { ARG_width, ARG_height, ARG_rotate, ARG_round, ARG_buffer, ARG_spi, ARG_cs, ARG_dc, ARG_sck, ARG_mosi, ARG_bl }; static const mp_arg_t allowed_args[] = { { MP_QSTR_width, MP_ARG_REQUIRED | MP_ARG_INT }, { MP_QSTR_height, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_rotate, MP_ARG_INT, {.u_int = Rotation::ROTATE_0} }, { MP_QSTR_round, MP_ARG_OBJ, {.u_obj = mp_const_false} }, - { MP_QSTR_rotate180, MP_ARG_OBJ, {.u_obj = mp_const_false} }, - { MP_QSTR_slot, MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_buffer, MP_ARG_OBJ, {.u_obj = mp_const_none} }, { MP_QSTR_spi, MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_cs, MP_ARG_INT, {.u_int = pimoroni::SPI_BG_FRONT_CS} }, @@ -75,10 +121,9 @@ mp_obj_t GenericST7789_make_new(const mp_obj_type_t *type, size_t n_args, size_t self = m_new_obj(GenericST7789_obj_t); self->base.type = &GenericST7789_type; - self->parallel = false; - bool rotate180 = args[ARG_rotate180].u_obj == mp_const_true; bool round = args[ARG_round].u_obj == mp_const_true; + Rotation rotate = (Rotation)args[ARG_rotate].u_int; int width = args[ARG_width].u_int; int height = args[ARG_height].u_int; @@ -93,50 +138,42 @@ mp_obj_t GenericST7789_make_new(const mp_obj_type_t *type, size_t n_args, size_t self->buffer = m_new(uint8_t, width * height * sizeof(Pen)); } - if(args[ARG_slot].u_int != -1) { - BG_SPI_SLOT slot = (BG_SPI_SLOT)args[ARG_slot].u_int; - self->st7789 = m_new_class(ST7789Generic, width, height, round, self->buffer, slot); - if (rotate180) { - self->st7789->configure_display(true); - } - } else { - // Get SPI bus. - int spi_id = args[ARG_spi].u_int; - int sck = args[ARG_sck].u_int; - int mosi = args[ARG_mosi].u_int; - int dc = args[ARG_dc].u_int; - int cs = args[ARG_cs].u_int; - int bl = args[ARG_bl].u_int; + int spi_id = args[ARG_spi].u_int; - if(spi_id == -1) { - spi_id = (sck >> 3) & 0b1; // If no spi specified, choose the one for the given SCK pin - } - if(spi_id < 0 || spi_id > 1) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("SPI(%d) doesn't exist"), spi_id); - } + // Get SPI bus. + SPIPins bus_pins = { + nullptr, + (uint)args[ARG_cs].u_int, + (uint)args[ARG_sck].u_int, + (uint)args[ARG_mosi].u_int, + (uint)args[ARG_dc].u_int, + args[ARG_bl].u_int == -1 ? PIN_UNUSED : (uint)args[ARG_bl].u_int + }; - if(!IS_VALID_SCK(spi_id, sck)) { - mp_raise_ValueError(MP_ERROR_TEXT("bad SCK pin")); - } - - if(!IS_VALID_MOSI(spi_id, mosi)) { - mp_raise_ValueError(MP_ERROR_TEXT("bad MOSI pin")); - } - spi_inst_t *spi = (spi_id == 0) ? spi0 : spi1; - self->st7789 = m_new_class(ST7789Generic, width, height, round, self->buffer, - spi, cs, dc, sck, mosi, bl); - if (rotate180) { - self->st7789->configure_display(true); - } + if(spi_id == -1) { + spi_id = (bus_pins.sck >> 3) & 0b1; // If no spi specified, choose the one for the given SCK pin } + if(spi_id < 0 || spi_id > 1) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("SPI(%d) doesn't exist"), spi_id); + } + + if(!IS_VALID_SCK(spi_id, (int)bus_pins.sck)) { + mp_raise_ValueError(MP_ERROR_TEXT("bad SCK pin")); + } + + if(!IS_VALID_MOSI(spi_id, (int)bus_pins.mosi)) { + mp_raise_ValueError(MP_ERROR_TEXT("bad MOSI pin")); + } + + bus_pins.spi = (spi_id == 0) ? spi0 : spi1; + + self->st7789 = m_new_class(ST7789Generic, width, height, rotate, round, self->buffer, bus_pins); return MP_OBJ_FROM_PTR(self); } mp_obj_t GenericST7789Parallel_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - GenericST7789_obj_t *self = nullptr; - - enum { ARG_width, ARG_height, ARG_cs, ARG_dc, ARG_wr_sck, ARG_rd_sck, ARG_d0, ARG_bl, ARG_rotate180, ARG_buffer }; + enum { ARG_width, ARG_height, ARG_cs, ARG_dc, ARG_wr_sck, ARG_rd_sck, ARG_d0, ARG_bl, ARG_rotate, ARG_buffer }; static const mp_arg_t allowed_args[] = { { MP_QSTR_width, MP_ARG_REQUIRED | MP_ARG_INT }, { MP_QSTR_height, MP_ARG_REQUIRED | MP_ARG_INT }, @@ -147,7 +184,7 @@ mp_obj_t GenericST7789Parallel_make_new(const mp_obj_type_t *type, size_t n_args { MP_QSTR_d0, MP_ARG_INT, {.u_int = 14} }, { MP_QSTR_bl, MP_ARG_INT, {.u_int = 2} }, - { MP_QSTR_rotate180, MP_ARG_OBJ, {.u_obj = mp_const_false} }, + { MP_QSTR_rotate, MP_ARG_INT, {.u_int = Rotation::ROTATE_0} }, { MP_QSTR_buffer, MP_ARG_OBJ, {.u_obj = mp_const_none} }, }; @@ -155,11 +192,10 @@ mp_obj_t GenericST7789Parallel_make_new(const mp_obj_type_t *type, size_t n_args mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - self = m_new_obj(GenericST7789_obj_t); + GenericST7789_obj_t *self = m_new_obj(GenericST7789_obj_t); self->base.type = &GenericST7789_type; - self->parallel = true; - bool rotate180 = args[ARG_rotate180].u_obj == mp_const_true; + Rotation rotate = (Rotation)args[ARG_rotate].u_int; int width = args[ARG_width].u_int; int height = args[ARG_height].u_int; @@ -174,18 +210,16 @@ mp_obj_t GenericST7789Parallel_make_new(const mp_obj_type_t *type, size_t n_args self->buffer = m_new(uint8_t, width * height); } - int cs = args[ARG_cs].u_int; - int dc = args[ARG_dc].u_int; - int wr_sck = args[ARG_wr_sck].u_int; - int rd_sck = args[ARG_rd_sck].u_int; - int d0 = args[ARG_d0].u_int; - int bl = args[ARG_bl].u_int; + ParallelPins bus_pins = { + (uint)args[ARG_cs].u_int, + (uint)args[ARG_dc].u_int, + (uint)args[ARG_wr_sck].u_int, + (uint)args[ARG_rd_sck].u_int, + (uint)args[ARG_d0].u_int, + args[ARG_bl].u_int == -1 ? PIN_UNUSED : (uint)args[ARG_bl].u_int + }; - self->st7789 = m_new_class(ST7789Generic, width, height, self->buffer, - cs, dc, wr_sck, rd_sck, d0, bl); - if (rotate180) { - self->st7789->configure_display(true); - } + self->st7789 = m_new_class(ST7789Generic, width, height, rotate, self->buffer, bus_pins); return MP_OBJ_FROM_PTR(self); } @@ -208,6 +242,14 @@ mp_obj_t GenericST7789_set_framebuffer(mp_obj_t self_in, mp_obj_t framebuffer) { return mp_const_none; } +mp_obj_t GenericST7789_get_bounds(mp_obj_t self_in) { + GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(self_in, GenericST7789_obj_t); + mp_obj_t tuple[2]; + tuple[0] = mp_obj_new_int(self->st7789->bounds.w); + tuple[1] = mp_obj_new_int(self->st7789->bounds.h); + return mp_obj_new_tuple(2, tuple); +} + mp_obj_t GenericST7789_update(mp_obj_t self_in) { GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(self_in, GenericST7789_obj_t); self->st7789->update(); diff --git a/micropython/modules/st7789/st7789.h b/micropython/modules/st7789/st7789.h index 97ea6e9a..ab1fc771 100644 --- a/micropython/modules/st7789/st7789.h +++ b/micropython/modules/st7789/st7789.h @@ -9,10 +9,9 @@ extern mp_obj_t GenericST7789_module_RGB332(mp_obj_t r, mp_obj_t g, mp_obj_t b); extern mp_obj_t GenericST7789_module_RGB565(mp_obj_t r, mp_obj_t g, mp_obj_t b); // Class methods -extern void GenericST7789_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind); extern mp_obj_t GenericST7789_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 GenericST7789SPI_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 GenericST7789Parallel_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 GenericST7789_set_framebuffer(mp_obj_t self_in, mp_obj_t framebuffer); extern mp_obj_t GenericST7789_update(mp_obj_t self_in); extern mp_obj_t GenericST7789_set_backlight(mp_obj_t self_in, mp_obj_t brightness); @@ -38,4 +37,8 @@ extern mp_obj_t GenericST7789_text(size_t n_args, const mp_obj_t *pos_args, mp_m extern mp_obj_t GenericST7789_measure_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); extern mp_obj_t GenericST7789_polygon(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); extern mp_obj_t GenericST7789_triangle(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t GenericST7789_line(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); \ No newline at end of file +extern mp_obj_t GenericST7789_line(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); + +// Utility +extern mp_obj_t GenericST7789_get_bounds(mp_obj_t self_in); +extern mp_obj_t GenericST7789_set_framebuffer(mp_obj_t self_in, mp_obj_t framebuffer); \ No newline at end of file