diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index 8e4989d3..73e38daa 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -28,6 +28,7 @@ add_subdirectory(icp10125) add_subdirectory(scd4x) add_subdirectory(hub75) add_subdirectory(uc8151) +add_subdirectory(uc8151_legacy) add_subdirectory(pwm) add_subdirectory(servo) add_subdirectory(encoder) diff --git a/drivers/uc8151/uc8151.cmake b/drivers/uc8151/uc8151.cmake index 60627e32..80a822fa 100644 --- a/drivers/uc8151/uc8151.cmake +++ b/drivers/uc8151/uc8151.cmake @@ -7,4 +7,4 @@ target_sources(${DRIVER_NAME} INTERFACE 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) +target_link_libraries(${DRIVER_NAME} INTERFACE pico_stdlib hardware_spi pimoroni_bus pico_graphics) diff --git a/drivers/uc8151/uc8151.cpp b/drivers/uc8151/uc8151.cpp index b61fedca..6292d03f 100644 --- a/drivers/uc8151/uc8151.cpp +++ b/drivers/uc8151/uc8151.cpp @@ -49,6 +49,7 @@ namespace pimoroni { }; bool UC8151::is_busy() { + if(BUSY == PIN_UNUSED) return false; return !gpio_get(BUSY); } @@ -59,6 +60,7 @@ namespace pimoroni { } void UC8151::reset() { + if(RESET == PIN_UNUSED) return; gpio_put(RESET, 0); sleep_ms(10); gpio_put(RESET, 1); sleep_ms(10); busy_wait(); @@ -323,17 +325,16 @@ namespace pimoroni { void UC8151::setup(uint8_t speed) { reset(); - _update_speed = speed; + update_speed = speed; - if(speed == 0) { - command(PSR, { - RES_128x296 | LUT_OTP | FORMAT_BW | SHIFT_RIGHT | BOOSTER_ON | RESET_NONE - }); - } else { - command(PSR, { - RES_128x296 | LUT_REG | FORMAT_BW | SHIFT_RIGHT | BOOSTER_ON | RESET_NONE - }); - } + uint8_t psr_setting = RES_128x296 | FORMAT_BW | BOOSTER_ON | RESET_NONE; + + psr_setting |= 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) { case 0: // Note: the defult luts are built in so we don't really need to flash them here @@ -379,7 +380,7 @@ namespace pimoroni { }); command(TCON, {0x22}); // tcon setting - command(CDI, {(uint8_t)(inverted ? 0b01'01'1100 : 0b01'00'1100)}); // vcom and data interval + command(CDI, {(uint8_t)(inverted ? 0b10'01'1100 : 0b01'00'1100)}); // vcom and data interval command(PLL, { HZ_100 @@ -446,40 +447,16 @@ namespace pimoroni { command(reg, values.size(), (uint8_t *)values.begin()); } - void UC8151::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[(y / 8) + (x * (height / 8))]; - - uint8_t o = 7 - (y & 0b111); // bit offset within byte - uint8_t m = ~(1 << o); // bit mask for byte - uint8_t b = (v == 0 ? 0 : 1) << o; // bit value shifted to position - - *p &= m; // clear bit - *p |= b; // set bit value - } - - uint8_t* UC8151::get_frame_buffer() { - return frame_buffer; - } - - void UC8151::invert(bool inv) { - inverted = inv; - command(CDI, {(uint8_t)(inverted ? 0b01'01'1100 : 0b01'00'1100)}); // vcom and data interval - } - - void UC8151::update_speed(uint8_t speed) { + void UC8151::set_update_speed(uint8_t speed) { setup(speed); } - uint8_t UC8151::update_speed() { - return _update_speed; + uint8_t UC8151::get_update_speed() { + return update_speed; } uint32_t UC8151::update_time() { - switch(_update_speed) { + switch(update_speed) { case 0: return 4500; case 1: @@ -493,28 +470,29 @@ namespace pimoroni { } } - void UC8151::partial_update(int x, int y, int w, int h, bool blocking) { - // y is given in columns ("banks"), which are groups of 8 horiontal pixels - // x is given in pixels + void UC8151::partial_update(PicoGraphics *graphics, Rect region) { + // region.y is given in columns ("banks"), which are groups of 8 horiontal pixels + // region.x is given in pixels + + uint8_t *fb = (uint8_t *)graphics->frame_buffer; + if(blocking) { busy_wait(); } - int cols = h / 8; - int y1 = y / 8; - //int y2 = y1 + cols; + int cols = region.h / 8; + int y1 = region.y / 8; - int rows = w; - int x1 = x; - //int x2 = x + rows; + int rows = region.w; + int x1 = region.x; uint8_t partial_window[7] = { - (uint8_t)(y), - (uint8_t)(y + h - 1), - (uint8_t)(x >> 8), - (uint8_t)(x & 0xff), - (uint8_t)((x + w - 1) >> 8), - (uint8_t)((x + w - 1) & 0xff), + (uint8_t)(region.y), + (uint8_t)(region.y + region.h - 1), + (uint8_t)(region.x >> 8), + (uint8_t)(region.x & 0xff), + (uint8_t)((region.x + region.w - 1) >> 8), + (uint8_t)((region.x + region.w - 1) & 0xff), 0b00000001 // PT_SCAN }; command(PON); // turn on @@ -526,7 +504,7 @@ namespace pimoroni { for (auto dx = 0; dx < rows; dx++) { int sx = dx + x1; int sy = y1; - data(cols, &frame_buffer[sy + (sx * (height / 8))]); + data(cols, &fb[sy + (sx * (height / 8))]); } command(DSP); // data stop @@ -539,7 +517,9 @@ namespace pimoroni { } } - void UC8151::update(bool blocking) { + void UC8151::update(PicoGraphics *graphics) { + uint8_t *fb = (uint8_t *)graphics->frame_buffer; + if(blocking) { busy_wait(); } @@ -548,7 +528,7 @@ namespace pimoroni { command(PTOU); // disable partial mode - command(DTM2, (width * height) / 8, frame_buffer); // transmit framebuffer + command(DTM2, (width * height) / 8, fb); // transmit framebuffer command(DSP); // data stop command(DRF); // start display refresh diff --git a/drivers/uc8151/uc8151.hpp b/drivers/uc8151/uc8151.hpp index 07007d8d..0208214f 100644 --- a/drivers/uc8151/uc8151.hpp +++ b/drivers/uc8151/uc8151.hpp @@ -6,11 +6,13 @@ #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 UC8151 { + class UC8151 : public DisplayDriver { enum PSR_FLAGS { RES_96x230 = 0b00000000, RES_96x252 = 0b01000000, @@ -125,90 +127,66 @@ namespace pimoroni { // 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 - //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 = SPI_DEFAULT_MISO; + uint DC = 20; uint SCK = SPI_DEFAULT_SCK; uint MOSI = SPI_DEFAULT_MOSI; uint BUSY = PIN_UNUSED; uint RESET = PIN_UNUSED; - bool inverted = false; - - uint8_t _update_speed = 0; + uint8_t update_speed = 0; + bool inverted = true; // Makes 0 black and 1 white, as is foretold. + bool blocking = true; public: - UC8151(uint16_t width, uint16_t height) : - width(width), height(height), frame_buffer(new uint8_t[width * height / 8]) { - } - - UC8151(uint16_t width, uint16_t height, uint8_t *frame_buffer) : - width(width), height(height), frame_buffer(frame_buffer) { - } - - UC8151(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 / 8]), - spi(spi), - CS(CS), DC(DC), SCK(SCK), MOSI(MOSI), BUSY(BUSY), RESET(RESET) {} - - UC8151(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) {} + UC8151(uint16_t width, uint16_t height, Rotation rotate) : UC8151(width, height, rotate, {PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_FRONT_CS, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, PIN_UNUSED, 20, PIN_UNUSED}) {}; + UC8151(uint16_t width, uint16_t height, Rotation rotate, SPIPins pins, uint busy=26, uint reset=21) : + DisplayDriver(width, height, rotate), + 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(uint8_t speed=0); void power_off(); - + + // DisplayDriver API + bool is_busy() override; + void update(PicoGraphics *graphics) override; + void partial_update(PicoGraphics *graphics, Rect region) override; + + // UC8151 Specific void default_luts(); void medium_luts(); 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 read(uint8_t reg, size_t len, uint8_t *data); 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) {command(reg, 0, nullptr);}; void data(size_t len, const uint8_t *data); - void invert(bool invert); - void update_speed(uint8_t speed); - uint8_t update_speed(); - uint32_t update_time(); void update(bool blocking = true); void partial_update(int x, int y, int w, int h, bool blocking = true); void off(); - - void pixel(int x, int y, int v); - uint8_t* get_frame_buffer(); }; } diff --git a/drivers/uc8151_legacy/CMakeLists.txt b/drivers/uc8151_legacy/CMakeLists.txt new file mode 100644 index 00000000..6ee28263 --- /dev/null +++ b/drivers/uc8151_legacy/CMakeLists.txt @@ -0,0 +1 @@ +include(uc8151_legacy.cmake) \ No newline at end of file diff --git a/drivers/uc8151_legacy/uc8151_legacy.cmake b/drivers/uc8151_legacy/uc8151_legacy.cmake new file mode 100644 index 00000000..11f5e096 --- /dev/null +++ b/drivers/uc8151_legacy/uc8151_legacy.cmake @@ -0,0 +1,10 @@ +set(DRIVER_NAME uc8151_legacy) +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) diff --git a/drivers/uc8151_legacy/uc8151_legacy.cpp b/drivers/uc8151_legacy/uc8151_legacy.cpp new file mode 100644 index 00000000..10e9431c --- /dev/null +++ b/drivers/uc8151_legacy/uc8151_legacy.cpp @@ -0,0 +1,568 @@ +#include "uc8151_legacy.hpp" + +#include +#include + +namespace pimoroni { + + enum reg { + PSR = 0x00, + PWR = 0x01, + POF = 0x02, + PFS = 0x03, + PON = 0x04, + PMES = 0x05, + BTST = 0x06, + DSLP = 0x07, + DTM1 = 0x10, + DSP = 0x11, + DRF = 0x12, + DTM2 = 0x13, + LUT_VCOM = 0x20, + LUT_WW = 0x21, + LUT_BW = 0x22, + LUT_WB = 0x23, + LUT_BB = 0x24, + PLL = 0x30, + TSC = 0x40, + TSE = 0x41, + TSR = 0x43, + TSW = 0x42, + CDI = 0x50, + LPD = 0x51, + TCON = 0x60, + TRES = 0x61, + REV = 0x70, + FLG = 0x71, + AMV = 0x80, + VV = 0x81, + VDCS = 0x82, + PTL = 0x90, + PTIN = 0x91, + PTOU = 0x92, + PGM = 0xa0, + APG = 0xa1, + ROTP = 0xa2, + CCSET = 0xe0, + PWS = 0xe3, + TSSET = 0xe5 + }; + + bool UC8151_Legacy::is_busy() { + return !gpio_get(BUSY); + } + + void UC8151_Legacy::busy_wait() { + while(is_busy()) { + tight_loop_contents(); + } + } + + void UC8151_Legacy::reset() { + gpio_put(RESET, 0); sleep_ms(10); + gpio_put(RESET, 1); sleep_ms(10); + busy_wait(); + } + + void UC8151_Legacy::default_luts() { + command(LUT_VCOM, { + 0x00, 0x64, 0x64, 0x37, 0x00, 0x01, + 0x00, 0x8c, 0x8c, 0x00, 0x00, 0x04, + 0x00, 0x64, 0x64, 0x37, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 + }); + + command(LUT_WW, { + 0x54, 0x64, 0x64, 0x37, 0x00, 0x01, + 0x60, 0x8c, 0x8c, 0x00, 0x00, 0x04, + 0xa8, 0x64, 0x64, 0x37, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }); + + command(LUT_BW, { + 0x54, 0x64, 0x64, 0x37, 0x00, 0x01, + 0x60, 0x8c, 0x8c, 0x00, 0x00, 0x04, + 0xa8, 0x64, 0x64, 0x37, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }); + + command(LUT_WB, { + 0xa8, 0x64, 0x64, 0x37, 0x00, 0x01, + 0x60, 0x8c, 0x8c, 0x00, 0x00, 0x04, + 0x54, 0x64, 0x64, 0x37, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }); + + command(LUT_BB, { + 0xa8, 0x64, 0x64, 0x37, 0x00, 0x01, + 0x60, 0x8c, 0x8c, 0x00, 0x00, 0x04, + 0x54, 0x64, 0x64, 0x37, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }); + + busy_wait(); + } + + void UC8151_Legacy::medium_luts() { + + command(LUT_VCOM, { + 0x00, 0x16, 0x16, 0x0d, 0x00, 0x01, + 0x00, 0x23, 0x23, 0x00, 0x00, 0x02, + 0x00, 0x16, 0x16, 0x0d, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 + }); + + command(LUT_WW, { + 0x54, 0x16, 0x16, 0x0d, 0x00, 0x01, + 0x60, 0x23, 0x23, 0x00, 0x00, 0x02, + 0xa8, 0x16, 0x16, 0x0d, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }); + + command(LUT_BW, { + 0x54, 0x16, 0x16, 0x0d, 0x00, 0x01, + 0x60, 0x23, 0x23, 0x00, 0x00, 0x02, + 0xa8, 0x16, 0x16, 0x0d, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }); + + command(LUT_WB, { + 0xa8, 0x16, 0x16, 0x0d, 0x00, 0x01, + 0x60, 0x23, 0x23, 0x00, 0x00, 0x02, + 0x54, 0x16, 0x16, 0x0d, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }); + + command(LUT_BB, { + 0xa8, 0x16, 0x16, 0x0d, 0x00, 0x01, + 0x60, 0x23, 0x23, 0x00, 0x00, 0x02, + 0x54, 0x16, 0x16, 0x0d, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }); + + busy_wait(); + } + + void UC8151_Legacy::fast_luts() { + // 0x3c, 0x00, 0x2b, 0x2b, 0x24, 0x1a, ???? + command(LUT_VCOM, { + 0x00, 0x04, 0x04, 0x07, 0x00, 0x01, + 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x02, + 0x00, 0x04, 0x04, 0x07, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 + }); + + command(LUT_WW, { + 0x54, 0x04, 0x04, 0x07, 0x00, 0x01, + 0x60, 0x0c, 0x0c, 0x00, 0x00, 0x02, + 0xa8, 0x04, 0x04, 0x07, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }); + + command(LUT_BW, { + 0x54, 0x04, 0x04, 0x07, 0x00, 0x01, + 0x60, 0x0c, 0x0c, 0x00, 0x00, 0x02, + 0xa8, 0x04, 0x04, 0x07, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }); + + command(LUT_WB, { + 0xa8, 0x04, 0x04, 0x07, 0x00, 0x01, + 0x60, 0x0c, 0x0c, 0x00, 0x00, 0x02, + 0x54, 0x04, 0x04, 0x07, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }); + + command(LUT_BB, { + 0xa8, 0x04, 0x04, 0x07, 0x00, 0x01, + 0x60, 0x0c, 0x0c, 0x00, 0x00, 0x02, + 0x54, 0x04, 0x04, 0x07, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }); + + command(PLL, { + HZ_200 + }); + + busy_wait(); + } + + void UC8151_Legacy::turbo_luts() { + // 0x3c, 0x00, 0x2b, 0x2b, 0x24, 0x1a, ???? + command(LUT_VCOM, { + 0x00, 0x01, 0x01, 0x02, 0x00, 0x01, + 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, + 0x00, 0x02, 0x02, 0x03, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 + }); + + command(LUT_WW, { + 0x54, 0x01, 0x01, 0x02, 0x00, 0x01, + 0x60, 0x02, 0x02, 0x00, 0x00, 0x02, + 0xa8, 0x02, 0x02, 0x03, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }); + + command(LUT_BW, { + 0x54, 0x01, 0x01, 0x02, 0x00, 0x01, + 0x60, 0x02, 0x02, 0x00, 0x00, 0x02, + 0xa8, 0x02, 0x02, 0x03, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }); + + command(LUT_WB, { + 0xa8, 0x01, 0x01, 0x02, 0x00, 0x01, + 0x60, 0x02, 0x02, 0x00, 0x00, 0x02, + 0x54, 0x02, 0x02, 0x03, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }); + + command(LUT_BB, { + 0xa8, 0x01, 0x01, 0x02, 0x00, 0x01, + 0x60, 0x02, 0x02, 0x00, 0x00, 0x02, + 0x54, 0x02, 0x02, 0x03, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }); + + command(PLL, { + HZ_200 + }); + + busy_wait(); + } + + void UC8151_Legacy::init() { + // configure spi interface and pins + spi_init(spi, 12'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); + + setup(); + }; + + void UC8151_Legacy::setup(uint8_t speed) { + reset(); + + _update_speed = speed; + + if(speed == 0) { + command(PSR, { + RES_128x296 | LUT_OTP | FORMAT_BW | SHIFT_RIGHT | BOOSTER_ON | RESET_NONE + }); + } else { + command(PSR, { + RES_128x296 | LUT_REG | FORMAT_BW | SHIFT_RIGHT | BOOSTER_ON | RESET_NONE + }); + } + switch(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. + break; + case 1: + medium_luts(); + break; + case 2: + fast_luts(); + break; + case 3: + turbo_luts(); + break; + default: + break; + } + + command(PWR, { + VDS_INTERNAL | VDG_INTERNAL, + VCOM_VD | VGHL_16V, + 0b101011, + 0b101011, + 0b101011 + }); + + command(PON); // power on + busy_wait(); + + // booster soft start configuration + command(BTST, { + START_10MS | STRENGTH_3 | OFF_6_58US, + START_10MS | STRENGTH_3 | OFF_6_58US, + START_10MS | STRENGTH_3 | OFF_6_58US + }); + + command(PFS, { + FRAMES_1 + }); + + command(TSE, { + TEMP_INTERNAL | OFFSET_0 + }); + + command(TCON, {0x22}); // tcon setting + command(CDI, {(uint8_t)(inverted ? 0b01'01'1100 : 0b01'00'1100)}); // vcom and data interval + + command(PLL, { + HZ_100 + }); + + command(POF); + busy_wait(); + } + + void UC8151_Legacy::power_off() { + command(POF); + } + + void UC8151_Legacy::read(uint8_t reg, size_t len, uint8_t *data) { + gpio_put(CS, 0); + + gpio_put(DC, 0); // command mode + spi_write_blocking(spi, ®, 1); + + if(len > 0) { + gpio_put(DC, 1); // data mode + gpio_set_function(SCK, GPIO_FUNC_SIO); + gpio_set_dir(SCK, GPIO_OUT); + gpio_set_function(MOSI, GPIO_FUNC_SIO); + gpio_set_dir(MOSI, GPIO_IN); + for(auto i = 0u; i < len; i++) { + int byte = i / 8; + int bit = i % 8; + gpio_put(SCK, true); + bool value = gpio_get(MOSI); + data[byte] |= value << (7-bit); + gpio_put(SCK, false); + } + + gpio_set_function(SCK, GPIO_FUNC_SPI); + gpio_set_function(MOSI, GPIO_FUNC_SPI); + } + + gpio_put(CS, 1); + } + + void UC8151_Legacy::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, ®, 1); + + if(len > 0) { + gpio_put(DC, 1); // data mode + spi_write_blocking(spi, (const uint8_t*)data, len); + } + + gpio_put(CS, 1); + } + + void UC8151_Legacy::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 UC8151_Legacy::command(uint8_t reg, std::initializer_list values) { + command(reg, values.size(), (uint8_t *)values.begin()); + } + + void UC8151_Legacy::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[(y / 8) + (x * (height / 8))]; + + uint8_t o = 7 - (y & 0b111); // bit offset within byte + uint8_t m = ~(1 << o); // bit mask for byte + uint8_t b = (v == 0 ? 0 : 1) << o; // bit value shifted to position + + *p &= m; // clear bit + *p |= b; // set bit value + } + + uint8_t* UC8151_Legacy::get_frame_buffer() { + return frame_buffer; + } + + void UC8151_Legacy::invert(bool inv) { + inverted = inv; + command(CDI, {(uint8_t)(inverted ? 0b01'01'1100 : 0b01'00'1100)}); // vcom and data interval + } + + void UC8151_Legacy::update_speed(uint8_t speed) { + setup(speed); + } + + uint8_t UC8151_Legacy::update_speed() { + return _update_speed; + } + + uint32_t UC8151_Legacy::update_time() { + switch(_update_speed) { + case 0: + return 4500; + case 1: + return 2000; + case 2: + return 800; + case 3: + return 250; + default: + return 4500; + } + } + + void UC8151_Legacy::partial_update(int x, int y, int w, int h, bool blocking) { + // y is given in columns ("banks"), which are groups of 8 horiontal pixels + // x is given in pixels + if(blocking) { + busy_wait(); + } + + int cols = h / 8; + int y1 = y / 8; + //int y2 = y1 + cols; + + int rows = w; + int x1 = x; + //int x2 = x + rows; + + uint8_t partial_window[7] = { + (uint8_t)(y), + (uint8_t)(y + h - 1), + (uint8_t)(x >> 8), + (uint8_t)(x & 0xff), + (uint8_t)((x + w - 1) >> 8), + (uint8_t)((x + w - 1) & 0xff), + 0b00000001 // PT_SCAN + }; + command(PON); // turn on + + command(PTIN); // enable partial mode + command(PTL, sizeof(partial_window), partial_window); + + command(DTM2); + for (auto dx = 0; dx < rows; dx++) { + int sx = dx + x1; + int sy = y1; + data(cols, &frame_buffer[sy + (sx * (height / 8))]); + } + command(DSP); // data stop + + command(DRF); // start display refresh + + if(blocking) { + busy_wait(); + + command(POF); // turn off + } + } + + void UC8151_Legacy::update(bool blocking) { + if(blocking) { + busy_wait(); + } + + command(PON); // turn on + + command(PTOU); // disable partial mode + + command(DTM2, (width * height) / 8, frame_buffer); // transmit framebuffer + command(DSP); // data stop + + command(DRF); // start display refresh + + if(blocking) { + busy_wait(); + + command(POF); // turn off + } + } + + void UC8151_Legacy::off() { + busy_wait(); + command(POF); // turn off + } + +} diff --git a/drivers/uc8151_legacy/uc8151_legacy.hpp b/drivers/uc8151_legacy/uc8151_legacy.hpp new file mode 100644 index 00000000..dbb50fcd --- /dev/null +++ b/drivers/uc8151_legacy/uc8151_legacy.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include + +#include "pico/stdlib.h" +#include "hardware/spi.h" +#include "hardware/gpio.h" + +#include "../../common/pimoroni_common.hpp" + +namespace pimoroni { + + class UC8151_Legacy { + enum PSR_FLAGS { + RES_96x230 = 0b00000000, + RES_96x252 = 0b01000000, + RES_128x296 = 0b10000000, + RES_160x296 = 0b11000000, + + LUT_OTP = 0b00000000, + LUT_REG = 0b00100000, + + FORMAT_BWR = 0b00000000, + FORMAT_BW = 0b00010000, + + SCAN_DOWN = 0b00000000, + SCAN_UP = 0b00001000, + + SHIFT_LEFT = 0b00000000, + SHIFT_RIGHT = 0b00000100, + + BOOSTER_OFF = 0b00000000, + BOOSTER_ON = 0b00000010, + + RESET_SOFT = 0b00000000, + RESET_NONE = 0b00000001 + }; + + enum PWR_FLAGS_1 { + VDS_EXTERNAL = 0b00000000, + VDS_INTERNAL = 0b00000010, + + VDG_EXTERNAL = 0b00000000, + VDG_INTERNAL = 0b00000001 + }; + + enum PWR_FLAGS_2 { + VCOM_VD = 0b00000000, + VCOM_VG = 0b00000100, + + VGHL_16V = 0b00000000, + VGHL_15V = 0b00000001, + VGHL_14V = 0b00000010, + VGHL_13V = 0b00000011 + }; + + enum BOOSTER_FLAGS { + START_10MS = 0b00000000, + START_20MS = 0b01000000, + START_30MS = 0b10000000, + START_40MS = 0b11000000, + + STRENGTH_1 = 0b00000000, + STRENGTH_2 = 0b00001000, + STRENGTH_3 = 0b00010000, + STRENGTH_4 = 0b00011000, + STRENGTH_5 = 0b00100000, + STRENGTH_6 = 0b00101000, + STRENGTH_7 = 0b00110000, + STRENGTH_8 = 0b00111000, + + OFF_0_27US = 0b00000000, + OFF_0_34US = 0b00000001, + OFF_0_40US = 0b00000010, + OFF_0_54US = 0b00000011, + OFF_0_80US = 0b00000100, + OFF_1_54US = 0b00000101, + OFF_3_34US = 0b00000110, + OFF_6_58US = 0b00000111 + }; + + enum PFS_FLAGS { + FRAMES_1 = 0b00000000, + FRAMES_2 = 0b00010000, + FRAMES_3 = 0b00100000, + FRAMES_4 = 0b00110000 + }; + + enum TSE_FLAGS { + TEMP_INTERNAL = 0b00000000, + TEMP_EXTERNAL = 0b10000000, + + OFFSET_0 = 0b00000000, + OFFSET_1 = 0b00000001, + OFFSET_2 = 0b00000010, + OFFSET_3 = 0b00000011, + OFFSET_4 = 0b00000100, + OFFSET_5 = 0b00000101, + OFFSET_6 = 0b00000110, + OFFSET_7 = 0b00000111, + + OFFSET_MIN_8 = 0b00001000, + OFFSET_MIN_7 = 0b00001001, + OFFSET_MIN_6 = 0b00001010, + OFFSET_MIN_5 = 0b00001011, + OFFSET_MIN_4 = 0b00001100, + OFFSET_MIN_3 = 0b00001101, + OFFSET_MIN_2 = 0b00001110, + OFFSET_MIN_1 = 0b00001111 + }; + + enum PLL_FLAGS { + // other frequency options exist but there doesn't seem to be much + // point in including them - this is a fair range of options... + HZ_29 = 0b00111111, + HZ_33 = 0b00111110, + HZ_40 = 0b00111101, + HZ_50 = 0b00111100, + HZ_67 = 0b00111011, + HZ_100 = 0b00111010, + HZ_200 = 0b00111001 + }; + + //-------------------------------------------------- + // 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 + //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 = SPI_DEFAULT_MISO; + uint SCK = SPI_DEFAULT_SCK; + uint MOSI = SPI_DEFAULT_MOSI; + uint BUSY = PIN_UNUSED; + uint RESET = PIN_UNUSED; + + bool inverted = false; + + uint8_t _update_speed = 0; + + public: + UC8151_Legacy(uint16_t width, uint16_t height) : + width(width), height(height), frame_buffer(new uint8_t[width * height / 8]) { + } + + UC8151_Legacy(uint16_t width, uint16_t height, uint8_t *frame_buffer) : + width(width), height(height), frame_buffer(frame_buffer) { + } + + UC8151_Legacy(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 / 8]), + spi(spi), + CS(CS), DC(DC), SCK(SCK), MOSI(MOSI), BUSY(BUSY), RESET(RESET) {} + + UC8151_Legacy(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) {} + + + //-------------------------------------------------- + // Methods + //-------------------------------------------------- + public: + void init(); + void busy_wait(); + bool is_busy(); + void reset(); + void setup(uint8_t speed=0); + void power_off(); + + void default_luts(); + void medium_luts(); + void fast_luts(); + void turbo_luts(); + + void read(uint8_t reg, size_t len, uint8_t *data); + 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) {command(reg, 0, nullptr);}; + void data(size_t len, const uint8_t *data); + + void invert(bool invert); + void update_speed(uint8_t speed); + uint8_t update_speed(); + uint32_t update_time(); + void update(bool blocking = true); + void partial_update(int x, int y, int w, int h, bool blocking = true); + void off(); + + void pixel(int x, int y, int v); + uint8_t* get_frame_buffer(); + }; + +} diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index e741ba8a..499b3c52 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -43,6 +43,8 @@ add_subdirectory(pico_trackball_display) add_subdirectory(pico_audio) add_subdirectory(pico_wireless) +add_subdirectory(inky_pack) + add_subdirectory(plasma2040) add_subdirectory(badger2040) add_subdirectory(tufty2040) diff --git a/examples/inky_pack/CMakeLists.txt b/examples/inky_pack/CMakeLists.txt new file mode 100644 index 00000000..76a8401c --- /dev/null +++ b/examples/inky_pack/CMakeLists.txt @@ -0,0 +1,12 @@ +set(OUTPUT_NAME inky_pack_demo) + +add_executable( + ${OUTPUT_NAME} + inky_pack_demo.cpp +) + +# Pull in pico libraries that we need +target_link_libraries(${OUTPUT_NAME} pico_stdlib hardware_spi hardware_pwm hardware_dma rgbled button uc8151 pico_graphics) + +# create map/bin/hex file etc. +pico_add_extra_outputs(${OUTPUT_NAME}) \ No newline at end of file diff --git a/examples/inky_pack/inky_pack_demo.cpp b/examples/inky_pack/inky_pack_demo.cpp new file mode 100644 index 00000000..4fcab7e2 --- /dev/null +++ b/examples/inky_pack/inky_pack_demo.cpp @@ -0,0 +1,57 @@ +#include +#include +#include +#include + +#include "drivers/uc8151/uc8151.hpp" +#include "libraries/pico_graphics/pico_graphics.hpp" +#include "button.hpp" + + +using namespace pimoroni; + +enum Pin { + A = 12, + B = 13, + C = 14, + D = 15, + E = 11, + UP = 15, // alias for D + DOWN = 11, // alias for E + USER = 23, + CS = 17, + CLK = 18, + MOSI = 19, + DC = 20, + RESET = 21, + BUSY = 26, + VBUS_DETECT = 24, + LED = 25, + BATTERY = 29, + ENABLE_3V3 = 10 +}; + + +UC8151 uc8151(296, 128, ROTATE_0); +PicoGraphics_Pen1BitY graphics(uc8151.width, uc8151.height, nullptr); + +Button button_a(Pin::A); +Button button_b(Pin::B); +Button button_c(Pin::C); +Button button_d(Pin::D); +Button button_e(Pin::E); + + +int main() { + graphics.set_pen(0); + graphics.clear(); + + graphics.set_pen(1); + graphics.set_font("bitmap8"); + graphics.text("Hello World", {0, 0}, 296); + graphics.text("Has this worked?!", {0, 16}, 296); + + uc8151.update(&graphics); + + return 0; +} diff --git a/libraries/badger2040/badger2040.cmake b/libraries/badger2040/badger2040.cmake index fe789b94..6d8df34b 100644 --- a/libraries/badger2040/badger2040.cmake +++ b/libraries/badger2040/badger2040.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 bitmap_fonts hershey_fonts pico_stdlib hardware_pwm uc8151) +target_link_libraries(${LIB_NAME} INTERFACE bitmap_fonts hershey_fonts pico_stdlib hardware_pwm uc8151_legacy) diff --git a/libraries/badger2040/badger2040.cpp b/libraries/badger2040/badger2040.cpp index 78dc200d..6be32b91 100644 --- a/libraries/badger2040/badger2040.cpp +++ b/libraries/badger2040/badger2040.cpp @@ -64,7 +64,7 @@ namespace pimoroni { gpio_set_function(LED, GPIO_FUNC_PWM); led(0); - uc8151.init(); + uc8151_legacy.init(); // TODO: set default image? } @@ -126,7 +126,7 @@ namespace pimoroni { void Badger2040::clear() { const uint32_t column_len = 128 / 8; const uint32_t buf_len = column_len * 296; - uint8_t* buf = uc8151.get_frame_buffer(); + uint8_t* buf = uc8151_legacy.get_frame_buffer(); if (_pen == 0) { memset(buf, 0xff, buf_len); @@ -145,12 +145,12 @@ namespace pimoroni { void Badger2040::pixel(int32_t x, int32_t y) { if(_thickness == 1) { - uc8151.pixel(x, y, _dither_value(x, y, _pen)); + uc8151_legacy.pixel(x, y, _dither_value(x, y, _pen)); }else{ uint8_t ht = _thickness / 2; for(int sy = 0; sy < _thickness; sy++) { for(int sx = 0; sx < _thickness; sx++) { - uc8151.pixel(x + sx - ht, y + sy - ht, _dither_value(x + sx - ht, y + sy - ht, _pen)); + uc8151_legacy.pixel(x + sx - ht, y + sy - ht, _dither_value(x + sx - ht, y + sy - ht, _pen)); } } } @@ -163,7 +163,7 @@ namespace pimoroni { // Display an image that fills the screen (296*128) void Badger2040::image(const uint8_t* data) { - uint8_t* ptr = uc8151.get_frame_buffer(); + uint8_t* ptr = uc8151_legacy.get_frame_buffer(); for (uint32_t x = 0; x < 296; ++x) { // extract bitmask for this pixel @@ -205,7 +205,7 @@ namespace pimoroni { uint32_t bm = 0b10000000 >> ((x + sx) & 0b111); // draw the pixel - uc8151.pixel(dx + x, dy + y, data[o] & bm); + uc8151_legacy.pixel(dx + x, dy + y, data[o] & bm); } } } @@ -234,7 +234,7 @@ namespace pimoroni { if (h >= 8) { // Directly write to the frame buffer when clearing a large area - uint8_t* buf = uc8151.get_frame_buffer(); + uint8_t* buf = uc8151_legacy.get_frame_buffer(); for(int cx = x; cx < x + w; cx++) { uint8_t* buf_ptr = &buf[cx * 16 + y / 8]; @@ -253,7 +253,7 @@ namespace pimoroni { else { for(int cx = x; cx < x + w; cx++) { for(int cy = y; cy < y + h; cy++) { - uc8151.pixel(cx, cy, _dither_value(cx, cy, _pen)); + uc8151_legacy.pixel(cx, cy, _dither_value(cx, cy, _pen)); } } } @@ -283,11 +283,11 @@ namespace pimoroni { } void Badger2040::debug_command(uint8_t reg, size_t len, const uint8_t *data) { - uc8151.command(reg, len, data); + uc8151_legacy.command(reg, len, data); } void Badger2040::dump_otp(uint8_t *data) { - uc8151.read(0xa2, 0xFFF, data); + uc8151_legacy.read(0xa2, 0xFFF, data); } void Badger2040::update_button_states() { @@ -301,31 +301,31 @@ namespace pimoroni { } bool Badger2040::is_busy() { - return uc8151.is_busy(); + return uc8151_legacy.is_busy(); } void Badger2040::power_off() { - uc8151.power_off(); + uc8151_legacy.power_off(); } void Badger2040::invert(bool invert) { - uc8151.invert(invert); + uc8151_legacy.invert(invert); } void Badger2040::update_speed(uint8_t speed) { - uc8151.update_speed(speed); + uc8151_legacy.update_speed(speed); } uint32_t Badger2040::update_time() { - return uc8151.update_time(); + return uc8151_legacy.update_time(); } void Badger2040::partial_update(int x, int y, int w, int h, bool blocking) { - uc8151.partial_update(x, y, w, h, blocking); + uc8151_legacy.partial_update(x, y, w, h, blocking); } void Badger2040::update(bool blocking) { - uc8151.update(blocking); + uc8151_legacy.update(blocking); } const hershey::font_glyph_t* Badger2040::glyph_data(unsigned char c) { diff --git a/libraries/badger2040/badger2040.hpp b/libraries/badger2040/badger2040.hpp index 774a9e67..0d5b1731 100644 --- a/libraries/badger2040/badger2040.hpp +++ b/libraries/badger2040/badger2040.hpp @@ -2,7 +2,7 @@ #include -#include "drivers/uc8151/uc8151.hpp" +#include "drivers/uc8151_legacy/uc8151_legacy.hpp" #include "libraries/hershey_fonts/hershey_fonts.hpp" #include "libraries/bitmap_fonts/bitmap_fonts.hpp" @@ -14,7 +14,7 @@ namespace pimoroni { class Badger2040 { protected: - UC8151 uc8151; + UC8151_Legacy uc8151_legacy; const hershey::font_t *_font = &hershey::futural; const bitmap::font_t *_bitmap_font = nullptr; uint8_t _pen = 0; @@ -25,11 +25,11 @@ namespace pimoroni { public: Badger2040() - : uc8151(296, 128, spi0, CS, DC, CLK, MOSI, BUSY, RESET) { + : uc8151_legacy(296, 128, spi0, CS, DC, CLK, MOSI, BUSY, RESET) { }; // Constructor for Python-managed buffer Badger2040(uint8_t *framebuffer) - : uc8151(296, 128, framebuffer, spi0, CS, DC, CLK, MOSI, BUSY, RESET) { + : uc8151_legacy(296, 128, framebuffer, spi0, CS, DC, CLK, MOSI, BUSY, RESET) { }; void init(); void update(bool blocking=false); diff --git a/libraries/pico_graphics/pico_graphics.cmake b/libraries/pico_graphics/pico_graphics.cmake index 71d0cf9a..a60d6b33 100644 --- a/libraries/pico_graphics/pico_graphics.cmake +++ b/libraries/pico_graphics/pico_graphics.cmake @@ -2,6 +2,7 @@ add_library(pico_graphics ${CMAKE_CURRENT_LIST_DIR}/types.cpp ${CMAKE_CURRENT_LIST_DIR}/pico_graphics.cpp ${CMAKE_CURRENT_LIST_DIR}/pico_graphics_pen_1bit.cpp + ${CMAKE_CURRENT_LIST_DIR}/pico_graphics_pen_1bitY.cpp ${CMAKE_CURRENT_LIST_DIR}/pico_graphics_pen_p4.cpp ${CMAKE_CURRENT_LIST_DIR}/pico_graphics_pen_p8.cpp ${CMAKE_CURRENT_LIST_DIR}/pico_graphics_pen_rgb332.cpp diff --git a/libraries/pico_graphics/pico_graphics.hpp b/libraries/pico_graphics/pico_graphics.hpp index 9493acb0..f709ed90 100644 --- a/libraries/pico_graphics/pico_graphics.hpp +++ b/libraries/pico_graphics/pico_graphics.hpp @@ -259,6 +259,21 @@ namespace pimoroni { } }; + class PicoGraphics_Pen1BitY : public PicoGraphics { + public: + uint8_t color; + + PicoGraphics_Pen1BitY(uint16_t width, uint16_t height, void *frame_buffer); + void set_pen(uint c) override; + void set_pen(uint8_t r, uint8_t g, uint8_t b) override; + + void set_pixel(const Point &p) override; + void set_pixel_span(const Point &p, uint l) override; + + static size_t buffer_size(uint w, uint h) { + return w * h / 8; + } + }; class PicoGraphics_PenP4 : public PicoGraphics { public: @@ -368,7 +383,9 @@ namespace pimoroni { : width(width), height(height), rotation(rotation) {}; virtual void update(PicoGraphics *display) {}; + virtual void partial_update(PicoGraphics *display, Rect region) {}; virtual void set_backlight(uint8_t brightness) {}; + virtual bool is_busy() {return false;}; virtual void cleanup() {}; }; diff --git a/libraries/pico_graphics/pico_graphics_pen_1bitY.cpp b/libraries/pico_graphics/pico_graphics_pen_1bitY.cpp new file mode 100644 index 00000000..0fa14d02 --- /dev/null +++ b/libraries/pico_graphics/pico_graphics_pen_1bitY.cpp @@ -0,0 +1,43 @@ +#include "pico_graphics.hpp" + +namespace pimoroni { + + PicoGraphics_Pen1BitY::PicoGraphics_Pen1BitY(uint16_t width, uint16_t height, void *frame_buffer) + : PicoGraphics(width, height, frame_buffer) { + this->pen_type = PEN_1BIT; + if(this->frame_buffer == nullptr) { + this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]); + } + } + + void PicoGraphics_Pen1BitY::set_pen(uint c) { + color = c != 0 ? 1 : 0; + } + + void PicoGraphics_Pen1BitY::set_pen(uint8_t r, uint8_t g, uint8_t b) { + color = r != 0 || g != 0 || b != 0 ? 1 : 0; + } + + void PicoGraphics_Pen1BitY::set_pixel(const Point &p) { + // pointer to byte in framebuffer that contains this pixel + uint8_t *buf = (uint8_t *)frame_buffer; + uint8_t *f = &buf[(p.y / 8) + (p.x * bounds.h / 8)]; + + uint bo = 7 - (p.y & 0b111); + + // forceably clear the bit + *f &= ~(1U << bo); + + // set pixel + *f |= (color << bo); + } + + void PicoGraphics_Pen1BitY::set_pixel_span(const Point &p, uint l) { + Point po(p); + while(l--) { + set_pixel(po); + po.x++; + } + } + +} \ No newline at end of file diff --git a/micropython/modules/badger2040/micropython.cmake b/micropython/modules/badger2040/micropython.cmake index 770e4660..2bbe2bf4 100644 --- a/micropython/modules/badger2040/micropython.cmake +++ b/micropython/modules/badger2040/micropython.cmake @@ -7,13 +7,13 @@ target_sources(usermod_${MOD_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.cpp ${CMAKE_CURRENT_LIST_DIR}/badgerinit.S ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/badger2040/badger2040.cpp - ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/uc8151/uc8151.cpp + ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/uc8151_legacy/uc8151_legacy.cpp ) target_include_directories(usermod_${MOD_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/badger2040/ - ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/uc8151/ + ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/uc8151_legacy/ ) target_compile_definitions(usermod_${MOD_NAME} INTERFACE diff --git a/micropython/modules/jpegdec/jpegdec.cpp b/micropython/modules/jpegdec/jpegdec.cpp index 9791aa56..821b0b59 100644 --- a/micropython/modules/jpegdec/jpegdec.cpp +++ b/micropython/modules/jpegdec/jpegdec.cpp @@ -47,6 +47,18 @@ MICROPY_EVENT_POLL_HOOK current_graphics->pixel({pDraw->x + x, pDraw->y + y}); } } + } else if(pDraw->iBpp == 1) { + uint8_t *pixels = (uint8_t *)pDraw->pPixels; + for(int y = 0; y < pDraw->iHeight; y++) { + for(int x = 0; x < pDraw->iWidth; x++) { + int i = y * pDraw->iWidth + x; + uint8_t p = pixels[i / 8]; + p >>= 7 - (i & 0b111); + p &= 0x1; + current_graphics->set_pen(p); + current_graphics->pixel({pDraw->x + x, pDraw->y + y}); + } + } } else { for(int y = 0; y < pDraw->iHeight; y++) { for(int x = 0; x < pDraw->iWidth; x++) { @@ -107,9 +119,9 @@ static int _open(_JPEG_obj_t *self) { case PicoGraphics::PEN_P2: self->jpeg->setPixelType(TWO_BIT_DITHERED); break; - case PicoGraphics::PEN_1BIT: - self->jpeg->setPixelType(ONE_BIT_DITHERED); - break; + case PicoGraphics::PEN_1BIT: + self->jpeg->setPixelType(ONE_BIT_DITHERED); + break; } } return result; @@ -172,7 +184,7 @@ mp_obj_t _JPEG_decode(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args current_graphics = self->graphics->graphics; int result = -1; - if(self->graphics->graphics->pen_type == PicoGraphics::PEN_P4) { + if(self->graphics->graphics->pen_type == PicoGraphics::PEN_P4 || self->graphics->graphics->pen_type == PicoGraphics::PEN_1BIT) { uint8_t *buf = new uint8_t[2048]; result = self->jpeg->decodeDither(x, y, buf, f); delete[] buf; diff --git a/micropython/modules/picographics/micropython.cmake b/micropython/modules/picographics/micropython.cmake index 9a1675af..26813f56 100644 --- a/micropython/modules/picographics/micropython.cmake +++ b/micropython/modules/picographics/micropython.cmake @@ -8,8 +8,10 @@ target_sources(usermod_${MOD_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/st7789/st7789.cpp ${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}/../../../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 ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/pico_graphics_pen_p4.cpp ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/pico_graphics_pen_p8.cpp ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/pico_graphics_pen_rgb332.cpp diff --git a/micropython/modules/picographics/picographics.c b/micropython/modules/picographics/picographics.c index 63bafca8..24d8d0ac 100644 --- a/micropython/modules/picographics/picographics.c +++ b/micropython/modules/picographics/picographics.c @@ -118,6 +118,7 @@ STATIC const mp_map_elem_t picographics_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_DISPLAY_ENVIRO_PLUS), MP_ROM_INT(DISPLAY_ENVIRO_PLUS) }, { 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_PEN_1BIT), MP_ROM_INT(PEN_1BIT) }, { MP_ROM_QSTR(MP_QSTR_PEN_P4), MP_ROM_INT(PEN_P4) }, diff --git a/micropython/modules/picographics/picographics.cpp b/micropython/modules/picographics/picographics.cpp index abffeb59..3ba6a839 100644 --- a/micropython/modules/picographics/picographics.cpp +++ b/micropython/modules/picographics/picographics.cpp @@ -1,6 +1,7 @@ #include "drivers/st7789/st7789.hpp" #include "drivers/st7735/st7735.hpp" #include "drivers/sh1107/sh1107.hpp" +#include "drivers/uc8151/uc8151.hpp" #include "libraries/pico_graphics/pico_graphics.hpp" #include "common/pimoroni_common.hpp" #include "common/pimoroni_bus.hpp" @@ -35,13 +36,14 @@ typedef struct _ModPicoGraphics_obj_t { //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) { +bool get_display_settings(PicoGraphicsDisplay display, int &width, int &height, int &rotate, int &pen_type) { switch(display) { case DISPLAY_PICO_DISPLAY: width = 240; height = 135; // 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: @@ -49,25 +51,36 @@ bool get_display_settings(PicoGraphicsDisplay display, int &width, int &height, height = 240; // 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_EXPLORER: case DISPLAY_LCD_240X240: case DISPLAY_ENVIRO_PLUS: width = 240; height = 240; + if(pen_type == -1) pen_type = PEN_RGB332; break; case DISPLAY_ROUND_LCD_240X240: width = 240; height = 240; + if(pen_type == -1) pen_type = PEN_RGB332; break; case DISPLAY_LCD_160X80: width = 160; height = 80; + if(pen_type == -1) pen_type = PEN_RGB332; break; case DISPLAY_I2C_OLED_128X128: width = 128; height = 128; 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; + if(rotate == -1) rotate = (int)Rotation::ROTATE_0; + if(pen_type == -1) pen_type = PEN_1BIT; break; default: return false; @@ -101,7 +114,7 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size { 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 = PEN_RGB332 } }, + { MP_QSTR_pen_type, MP_ARG_INT, { .u_int = -1 } }, }; // Parse args. @@ -111,15 +124,14 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size self = m_new_obj_with_finaliser(ModPicoGraphics_obj_t); self->base.type = &ModPicoGraphics_type; - - PicoGraphicsPenType pen_type = (PicoGraphicsPenType)args[ARG_pen_type].u_int; PicoGraphicsDisplay display = (PicoGraphicsDisplay)args[ARG_display].u_int; bool round = display == DISPLAY_ROUND_LCD_240X240; int width = 0; 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)) mp_raise_ValueError("Unsupported display!"); + if(!get_display_settings(display, width, height, rotate, pen_type)) mp_raise_ValueError("Unsupported display!"); if(rotate == -1) rotate = (int)Rotation::ROTATE_0; // Try to create an appropriate display driver @@ -148,6 +160,15 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size } else { mp_raise_ValueError("I2C bus expected!"); } + } 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!"); + } } 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)); @@ -176,9 +197,13 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size // Create an instance of the graphics library // use the *driver* width/height because they may have been swapped due to rotation - switch(pen_type) { + switch((PicoGraphicsPenType)pen_type) { case PEN_1BIT: - self->graphics = m_new_class(PicoGraphics_Pen1Bit, self->display->width, self->display->height, self->buffer); + if (display == DISPLAY_INKY_PACK) { + self->graphics = m_new_class(PicoGraphics_Pen1BitY, self->display->width, self->display->height, self->buffer); + } else { + self->graphics = m_new_class(PicoGraphics_Pen1Bit, self->display->width, self->display->height, self->buffer); + } break; case PEN_P4: self->graphics = m_new_class(PicoGraphics_PenP4, self->display->width, self->display->height, self->buffer); @@ -325,13 +350,13 @@ mp_obj_t ModPicoGraphics_set_framebuffer(mp_obj_t self_in, mp_obj_t framebuffer) } mp_obj_t ModPicoGraphics_get_required_buffer_size(mp_obj_t display_in, mp_obj_t pen_type_in) { - PicoGraphicsPenType pen_type = (PicoGraphicsPenType)mp_obj_get_int(pen_type_in); PicoGraphicsDisplay display = (PicoGraphicsDisplay)mp_obj_get_int(display_in); int width = 0; int height = 0; int rotation = 0; - if(!get_display_settings(display, width, height, rotation)) mp_raise_ValueError("Unsupported display!"); - size_t required_size = get_required_buffer_size(pen_type, width, height); + 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!"); + size_t required_size = get_required_buffer_size((PicoGraphicsPenType)pen_type, width, height); if(required_size == 0) mp_raise_ValueError("Unsupported pen type!"); return mp_obj_new_int(required_size); diff --git a/micropython/modules/picographics/picographics.h b/micropython/modules/picographics/picographics.h index e081ed83..3a33eed5 100644 --- a/micropython/modules/picographics/picographics.h +++ b/micropython/modules/picographics/picographics.h @@ -10,7 +10,8 @@ enum PicoGraphicsDisplay { DISPLAY_TUFTY_2040, DISPLAY_ENVIRO_PLUS, DISPLAY_LCD_160X80, - DISPLAY_I2C_OLED_128X128 + DISPLAY_I2C_OLED_128X128, + DISPLAY_INKY_PACK }; enum PicoGraphicsPenType {