From 04d8d6639052d6640b508d68d2397f31f3b27f90 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Thu, 23 Jun 2022 12:26:52 +0100 Subject: [PATCH] UC8151: New DisplayDriver based UC8151 driver. HACK: Adds PicoGraphics_Pen1BitY to work around the UC8151 weird addressing. TODO: Replace hack with an in-place transform to UC8151 pixel order from Pen1Bit. --- drivers/CMakeLists.txt | 1 + drivers/uc8151/CMakeLists.txt | 1 + drivers/uc8151/uc8151.cmake | 10 + drivers/uc8151/uc8151.cpp | 549 ++++++++++++++++++ drivers/uc8151/uc8151.hpp | 192 ++++++ examples/CMakeLists.txt | 2 + examples/inky_pack/CMakeLists.txt | 12 + examples/inky_pack/inky_pack_demo.cpp | 57 ++ libraries/pico_graphics/pico_graphics.cmake | 1 + libraries/pico_graphics/pico_graphics.hpp | 17 + .../pico_graphics/pico_graphics_pen_1bitY.cpp | 43 ++ 11 files changed, 885 insertions(+) create mode 100644 drivers/uc8151/CMakeLists.txt create mode 100644 drivers/uc8151/uc8151.cmake create mode 100644 drivers/uc8151/uc8151.cpp create mode 100644 drivers/uc8151/uc8151.hpp create mode 100644 examples/inky_pack/CMakeLists.txt create mode 100644 examples/inky_pack/inky_pack_demo.cpp create mode 100644 libraries/pico_graphics/pico_graphics_pen_1bitY.cpp diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index 25c53994..73e38daa 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -27,6 +27,7 @@ add_subdirectory(rgbled) add_subdirectory(icp10125) add_subdirectory(scd4x) add_subdirectory(hub75) +add_subdirectory(uc8151) add_subdirectory(uc8151_legacy) add_subdirectory(pwm) add_subdirectory(servo) diff --git a/drivers/uc8151/CMakeLists.txt b/drivers/uc8151/CMakeLists.txt new file mode 100644 index 00000000..0a61e780 --- /dev/null +++ b/drivers/uc8151/CMakeLists.txt @@ -0,0 +1 @@ +include(uc8151.cmake) \ No newline at end of file diff --git a/drivers/uc8151/uc8151.cmake b/drivers/uc8151/uc8151.cmake new file mode 100644 index 00000000..80a822fa --- /dev/null +++ b/drivers/uc8151/uc8151.cmake @@ -0,0 +1,10 @@ +set(DRIVER_NAME uc8151) +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 pimoroni_bus pico_graphics) diff --git a/drivers/uc8151/uc8151.cpp b/drivers/uc8151/uc8151.cpp new file mode 100644 index 00000000..098ff11b --- /dev/null +++ b/drivers/uc8151/uc8151.cpp @@ -0,0 +1,549 @@ +#include "uc8151.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::is_busy() { + if(BUSY == PIN_UNUSED) return false; + return !gpio_get(BUSY); + } + + void UC8151::busy_wait() { + while(is_busy()) { + tight_loop_contents(); + } + } + + void UC8151::reset() { + if(RESET == PIN_UNUSED) return; + gpio_put(RESET, 0); sleep_ms(10); + gpio_put(RESET, 1); sleep_ms(10); + busy_wait(); + } + + void UC8151::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::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::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::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::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::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::power_off() { + command(POF); + } + + void UC8151::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::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::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::command(uint8_t reg, std::initializer_list values) { + 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; + } + + uint32_t UC8151::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::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 = region.h / 8; + int y1 = region.y / 8; + + int rows = region.w; + int x1 = region.x; + + uint8_t partial_window[7] = { + (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 + + 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, &fb[sy + (sx * (height / 8))]); + } + command(DSP); // data stop + + command(DRF); // start display refresh + + if(blocking) { + busy_wait(); + + command(POF); // turn off + } + } + + void UC8151::update(PicoGraphics *graphics) { + uint8_t *fb = (uint8_t *)graphics->frame_buffer; + + if(blocking) { + busy_wait(); + } + + command(PON); // turn on + + command(PTOU); // disable partial mode + + command(DTM2, (width * height) / 8, fb); // transmit framebuffer + command(DSP); // data stop + + command(DRF); // start display refresh + + if(blocking) { + busy_wait(); + + command(POF); // turn off + } + } + + void UC8151::off() { + busy_wait(); + command(POF); // turn off + } + +} diff --git a/drivers/uc8151/uc8151.hpp b/drivers/uc8151/uc8151.hpp new file mode 100644 index 00000000..6a29e76a --- /dev/null +++ b/drivers/uc8151/uc8151.hpp @@ -0,0 +1,192 @@ +#pragma once + +#include + +#include "pico/stdlib.h" +#include "hardware/spi.h" +#include "hardware/gpio.h" + +#include "common/pimoroni_common.hpp" +#include "common/pimoroni_bus.hpp" +#include "libraries/pico_graphics/pico_graphics.hpp" + +namespace pimoroni { + + class UC8151 : public DisplayDriver { + 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: + spi_inst_t *spi = PIMORONI_SPI_DEFAULT_INSTANCE; + + // interface pins with our standard defaults where appropriate + uint CS = SPI_BG_FRONT_CS; + uint DC = 20; + uint SCK = SPI_DEFAULT_SCK; + uint MOSI = SPI_DEFAULT_MOSI; + uint BUSY = PIN_UNUSED; + uint RESET = PIN_UNUSED; + + uint8_t update_speed = 0; + bool inverted = false; + bool blocking = true; + + public: + UC8151(uint16_t width, uint16_t height) : UC8151(width, height, {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, SPIPins pins, uint busy=26, uint reset=21) : + DisplayDriver(width, height, ROTATE_0), + spi(pins.spi), + CS(pins.cs), DC(pins.dc), SCK(pins.sck), MOSI(pins.mosi), BUSY(busy), RESET(reset) { + init(); + } + + //-------------------------------------------------- + // Methods + //-------------------------------------------------- + public: + void busy_wait(); + void reset(); + void power_off(); + + // 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 update(bool blocking = true); + void partial_update(int x, int y, int w, int h, bool blocking = true); + void off(); + }; + +} 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..467749d4 --- /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); +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/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