From 360bf4310cf24b150bff46f868c0d22509335e44 Mon Sep 17 00:00:00 2001 From: jon Date: Wed, 15 Jun 2022 16:37:27 +0100 Subject: [PATCH] SH1107: Add driver and example. Add 1bit pen mode to PicoGraphics. TODO: * Rotation support * Sizes other than 128x128 support --- drivers/CMakeLists.txt | 76 ++++++------- drivers/sh1107/CMakeLists.txt | 1 + drivers/sh1107/README.md | 58 ++++++++++ drivers/sh1107/sh1107.cmake | 12 ++ drivers/sh1107/sh1107.cpp | 49 +++++++++ drivers/sh1107/sh1107.hpp | 85 +++++++++++++++ examples/CMakeLists.txt | 103 +++++++++--------- examples/breakout_oled_128x128/CMakeLists.txt | 12 ++ .../oled_128x128_demo.cpp | 52 +++++++++ libraries/pico_graphics/pico_graphics.cmake | 1 + libraries/pico_graphics/pico_graphics.hpp | 18 ++- .../pico_graphics/pico_graphics_pen_1bit.cpp | 35 ++++++ micropython/modules/jpegdec/jpegdec.cpp | 5 +- 13 files changed, 417 insertions(+), 90 deletions(-) create mode 100644 drivers/sh1107/CMakeLists.txt create mode 100644 drivers/sh1107/README.md create mode 100644 drivers/sh1107/sh1107.cmake create mode 100644 drivers/sh1107/sh1107.cpp create mode 100644 drivers/sh1107/sh1107.hpp create mode 100644 examples/breakout_oled_128x128/CMakeLists.txt create mode 100644 examples/breakout_oled_128x128/oled_128x128_demo.cpp create mode 100644 libraries/pico_graphics/pico_graphics_pen_1bit.cpp diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index b5478d6d..8e4989d3 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -1,37 +1,39 @@ -add_subdirectory(analog) -add_subdirectory(analogmux) -add_subdirectory(esp32spi) -add_subdirectory(ioexpander) -add_subdirectory(ltp305) -add_subdirectory(ltr559) -add_subdirectory(pmw3901) -add_subdirectory(sgp30) -add_subdirectory(st7735) -add_subdirectory(st7789) -add_subdirectory(msa301) -add_subdirectory(rv3028) -add_subdirectory(trackball) -add_subdirectory(vl53l1x) -add_subdirectory(is31fl3731) -add_subdirectory(fatfs) -add_subdirectory(sdcard) -add_subdirectory(as7262) -add_subdirectory(bh1745) -add_subdirectory(bme68x) -add_subdirectory(bmp280) -add_subdirectory(bme280) -add_subdirectory(button) -add_subdirectory(pid) -add_subdirectory(plasma) -add_subdirectory(rgbled) -add_subdirectory(icp10125) -add_subdirectory(scd4x) -add_subdirectory(hub75) -add_subdirectory(uc8151) -add_subdirectory(pwm) -add_subdirectory(servo) -add_subdirectory(encoder) -add_subdirectory(motor) -add_subdirectory(vl53l5cx) -add_subdirectory(pcf85063a) -add_subdirectory(pms5003) +add_subdirectory(analog) +add_subdirectory(analogmux) +add_subdirectory(esp32spi) +add_subdirectory(ioexpander) +add_subdirectory(ltp305) +add_subdirectory(ltr559) +add_subdirectory(pmw3901) +add_subdirectory(sgp30) +add_subdirectory(st7735) +add_subdirectory(st7789) +add_subdirectory(msa301) +add_subdirectory(rv3028) +add_subdirectory(trackball) +add_subdirectory(vl53l1x) +add_subdirectory(is31fl3731) +add_subdirectory(fatfs) +add_subdirectory(sdcard) +add_subdirectory(as7262) +add_subdirectory(bh1745) +add_subdirectory(bme68x) +add_subdirectory(bmp280) +add_subdirectory(bme280) +add_subdirectory(button) +add_subdirectory(pid) +add_subdirectory(plasma) +add_subdirectory(rgbled) +add_subdirectory(icp10125) +add_subdirectory(scd4x) +add_subdirectory(hub75) +add_subdirectory(uc8151) +add_subdirectory(pwm) +add_subdirectory(servo) +add_subdirectory(encoder) +add_subdirectory(motor) +add_subdirectory(vl53l5cx) +add_subdirectory(pcf85063a) +add_subdirectory(pms5003) +add_subdirectory(sh1107) + diff --git a/drivers/sh1107/CMakeLists.txt b/drivers/sh1107/CMakeLists.txt new file mode 100644 index 00000000..3d2eb2bd --- /dev/null +++ b/drivers/sh1107/CMakeLists.txt @@ -0,0 +1 @@ +include(${CMAKE_CURRENT_LIST_DIR}/sh1107.cmake) \ No newline at end of file diff --git a/drivers/sh1107/README.md b/drivers/sh1107/README.md new file mode 100644 index 00000000..b9e92b4b --- /dev/null +++ b/drivers/sh1107/README.md @@ -0,0 +1,58 @@ +# ST7789 Display Driver for Pimoroni LCDs + +The ST7789 driver supports both Parallel and Serial (SPI) ST7789 displays and is intended for use with: + +* Pico Display +* Pico Display 2.0 +* Tufty 2040 +* Pico Explorer +* 240x240 Round & Square SPI LCD Breakouts + +## Setup + +Construct an instance of the ST7789 driver with either Parallel or SPI pins. + +Parallel: + +```c++ +ST7789 st7789(WIDTH, HEIGHT, ROTATE_0, { + Tufty2040::LCD_CS, // Chip-Select + Tufty2040::LCD_DC, // Data-Command + Tufty2040::LCD_WR, // Write + Tufty2040::LCD_RD, // Read + Tufty2040::LCD_D0, // Data 0 (start of a bank of 8 pins) + Tufty2040::BACKLIGHT // Backlight +}); +``` + +SPI: + +```c++ +ST7789 st7789(WIDTH, HEIGHT, ROTATE_0, false, { + PIMORONI_SPI_DEFAULT_INSTANCE, // SPI instance + SPI_BG_FRONT_CS, // Chip-select + SPI_DEFAULT_SCK, // SPI Clock + SPI_DEFAULT_MOSI, // SPI Out + PIN_UNUSED, // SPI In + SPI_DEFAULT_DC, // SPI Data/Command + PIN_UNUSED // Backlight +}); +``` + +## Reference + +### Update + +ST7789's `update` accepts an instance of `PicoGraphics` in any colour mode: + +```c++ +st7789.update(&graphics); +``` + +### Set Backlight + +If a backlight pin has been configured, you can set the backlight from 0 to 255: + +```c++ +st7789.set_backlight(128) +``` \ No newline at end of file diff --git a/drivers/sh1107/sh1107.cmake b/drivers/sh1107/sh1107.cmake new file mode 100644 index 00000000..f152c0e4 --- /dev/null +++ b/drivers/sh1107/sh1107.cmake @@ -0,0 +1,12 @@ +set(DRIVER_NAME sh1107) +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}) + +target_include_directories(sh1107 INTERFACE ${CMAKE_CURRENT_LIST_DIR}) + +# Pull in pico libraries that we need +target_link_libraries(${DRIVER_NAME} INTERFACE pico_stdlib pimoroni_i2c pico_graphics) diff --git a/drivers/sh1107/sh1107.cpp b/drivers/sh1107/sh1107.cpp new file mode 100644 index 00000000..9a72b514 --- /dev/null +++ b/drivers/sh1107/sh1107.cpp @@ -0,0 +1,49 @@ +#include "sh1107.hpp" + +#include +#include +#include + +namespace pimoroni { + + void SH1107::update(PicoGraphics *graphics) { + if(graphics->pen_type == PicoGraphics::PEN_1BIT) { // Display buffer is screen native + + uint8_t *p = (uint8_t *)graphics->frame_buffer; + uint framebuffer_size = PicoGraphics_Pen1Bit::buffer_size(width, height); + uint page_size = framebuffer_size / 16; + + uint8_t temp[framebuffer_size] = {0}; + uint8_t *ptemp = temp; + + for(int y = 0; y < height; y++) { + for(int x = 0; x < width; x++) { + uint bo = 7 - (x & 0b111); + uint8_t color = p[(x / 8) + (y * width / 8)] & (1U << bo); + if(color) { + temp[x + (y / 8 ) * width] |= 1 << (y % 8); + }else{ + temp[x + (y / 8 ) * width] &= ~(1 << (y % 8)); + } + } + } + + uint8_t buf[page_size + 1]; + + for(int i = 0; i < 16; i++) { + i2c.reg_write_uint8(0x3c, 0, 0xb0 + i); + i2c.reg_write_uint8(0x3c, 0, 0x00); + i2c.reg_write_uint8(0x3c, 0, 0x10); + + memcpy(buf + 1, ptemp, page_size); + buf[0] = 0x40; + + i2c.write_blocking(0x3c, buf, page_size + 1, false); + + ptemp += page_size; + } + + } + } + +} diff --git a/drivers/sh1107/sh1107.hpp b/drivers/sh1107/sh1107.hpp new file mode 100644 index 00000000..864a4631 --- /dev/null +++ b/drivers/sh1107/sh1107.hpp @@ -0,0 +1,85 @@ +#pragma once + +#include "hardware/i2c.h" +#include "hardware/gpio.h" +#include "common/pimoroni_common.hpp" + +#include "common/pimoroni_i2c.hpp" + +#include "libraries/pico_graphics/pico_graphics.hpp" + +#include + + +namespace pimoroni { + + class SH1107 : public DisplayDriver { + I2C &i2c; + + public: + bool round; + + //-------------------------------------------------- + // Variables + //-------------------------------------------------- + private: + + public: + // Parallel init + SH1107(uint16_t width, uint16_t height, I2C &i2c) : + DisplayDriver(width, height, ROTATE_0), + i2c(i2c) { + + + i2c.reg_write_uint8(0x3c, 0, 0xae); + + i2c.reg_write_uint8(0x3c, 0, 0x20); // set memory addressing mode + i2c.reg_write_uint8(0x3c, 0, 0x00); + + i2c.reg_write_uint8(0x3c, 0, 0xb0); // set page start address + + i2c.reg_write_uint8(0x3c, 0, 0xc0); // mirror vertically (for australian market) + + i2c.reg_write_uint8(0x3c, 0, 0x00); + i2c.reg_write_uint8(0x3c, 0, 0x10); + + i2c.reg_write_uint8(0x3c, 0, 0x40); + + i2c.reg_write_uint8(0x3c, 0, 0xa0); // mirror horizontally + + i2c.reg_write_uint8(0x3c, 0, 0xa6); // no inversion + + i2c.reg_write_uint8(0x3c, 0, 0xff); // ??????! + i2c.reg_write_uint8(0x3c, 0, 0x3f); // confusion intensifies.. + + i2c.reg_write_uint8(0x3c, 0, 0xa4); + + i2c.reg_write_uint8(0x3c, 0, 0xd3); // set display offset + i2c.reg_write_uint8(0x3c, 0, 0x00); + + i2c.reg_write_uint8(0x3c, 0, 0xd5); // set display clock divide + i2c.reg_write_uint8(0x3c, 0, 0xf0); + + i2c.reg_write_uint8(0x3c, 0, 0xd9); // set precharge period + i2c.reg_write_uint8(0x3c, 0, 0x22); + + i2c.reg_write_uint8(0x3c, 0, 0xda); // set com pins hardware configuration + i2c.reg_write_uint8(0x3c, 0, 0x12); + + i2c.reg_write_uint8(0x3c, 0, 0xdb); // set vcomh + i2c.reg_write_uint8(0x3c, 0, 0x20); + + i2c.reg_write_uint8(0x3c, 0, 0x8d); // set dc-dc enable + i2c.reg_write_uint8(0x3c, 0, 0x14); + + i2c.reg_write_uint8(0x3c, 0, 0xaf); // turn display on + } + + void update(PicoGraphics *graphics) override; + + private: + void common_init(); + void command(uint8_t command, size_t len = 0, const char *data = NULL); + }; + +} diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index f9675d27..e741ba8a 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,51 +1,52 @@ -add_subdirectory(breakout_dotmatrix) -add_subdirectory(breakout_encoder) -add_subdirectory(breakout_ioexpander) -add_subdirectory(breakout_ltr559) -add_subdirectory(breakout_colourlcd160x80) -add_subdirectory(breakout_roundlcd) -add_subdirectory(breakout_rgbmatrix5x5) -add_subdirectory(breakout_matrix11x7) -add_subdirectory(breakout_mics6814) -add_subdirectory(breakout_pmw3901) -add_subdirectory(breakout_potentiometer) -add_subdirectory(breakout_rtc) -add_subdirectory(breakout_trackball) -add_subdirectory(breakout_sgp30) -add_subdirectory(breakout_colourlcd240x240) -add_subdirectory(breakout_msa301) -add_subdirectory(breakout_bme688) -add_subdirectory(breakout_bmp280) -add_subdirectory(breakout_bme280) -add_subdirectory(breakout_as7262) -add_subdirectory(breakout_bh1745) -add_subdirectory(breakout_icp10125) -add_subdirectory(breakout_scd41) -add_subdirectory(breakout_vl53l5cx) -add_subdirectory(breakout_pms5003) - -add_subdirectory(pico_display) -add_subdirectory(pico_display_2) -add_subdirectory(pico_unicorn) -add_subdirectory(pico_unicorn_plasma) -add_subdirectory(pico_scroll) -add_subdirectory(pico_enc_explorer) -add_subdirectory(pico_explorer) -add_subdirectory(pico_pot_explorer) -add_subdirectory(pico_explorer_encoder) -add_subdirectory(pico_motor_shim) -add_subdirectory(pico_rgb_keypad) -add_subdirectory(pico_rtc_display) -add_subdirectory(pico_rtc) -add_subdirectory(pico_tof_display) -add_subdirectory(pico_trackball_display) -add_subdirectory(pico_audio) -add_subdirectory(pico_wireless) - -add_subdirectory(plasma2040) -add_subdirectory(badger2040) -add_subdirectory(tufty2040) -add_subdirectory(interstate75) -add_subdirectory(servo2040) -add_subdirectory(motor2040) -add_subdirectory(encoder) +add_subdirectory(breakout_dotmatrix) +add_subdirectory(breakout_encoder) +add_subdirectory(breakout_ioexpander) +add_subdirectory(breakout_ltr559) +add_subdirectory(breakout_colourlcd160x80) +add_subdirectory(breakout_roundlcd) +add_subdirectory(breakout_rgbmatrix5x5) +add_subdirectory(breakout_matrix11x7) +add_subdirectory(breakout_mics6814) +add_subdirectory(breakout_pmw3901) +add_subdirectory(breakout_potentiometer) +add_subdirectory(breakout_rtc) +add_subdirectory(breakout_trackball) +add_subdirectory(breakout_sgp30) +add_subdirectory(breakout_colourlcd240x240) +add_subdirectory(breakout_msa301) +add_subdirectory(breakout_bme688) +add_subdirectory(breakout_bmp280) +add_subdirectory(breakout_bme280) +add_subdirectory(breakout_as7262) +add_subdirectory(breakout_bh1745) +add_subdirectory(breakout_icp10125) +add_subdirectory(breakout_scd41) +add_subdirectory(breakout_vl53l5cx) +add_subdirectory(breakout_pms5003) +add_subdirectory(breakout_oled_128x128) + +add_subdirectory(pico_display) +add_subdirectory(pico_display_2) +add_subdirectory(pico_unicorn) +add_subdirectory(pico_unicorn_plasma) +add_subdirectory(pico_scroll) +add_subdirectory(pico_enc_explorer) +add_subdirectory(pico_explorer) +add_subdirectory(pico_pot_explorer) +add_subdirectory(pico_explorer_encoder) +add_subdirectory(pico_motor_shim) +add_subdirectory(pico_rgb_keypad) +add_subdirectory(pico_rtc_display) +add_subdirectory(pico_rtc) +add_subdirectory(pico_tof_display) +add_subdirectory(pico_trackball_display) +add_subdirectory(pico_audio) +add_subdirectory(pico_wireless) + +add_subdirectory(plasma2040) +add_subdirectory(badger2040) +add_subdirectory(tufty2040) +add_subdirectory(interstate75) +add_subdirectory(servo2040) +add_subdirectory(motor2040) +add_subdirectory(encoder) diff --git a/examples/breakout_oled_128x128/CMakeLists.txt b/examples/breakout_oled_128x128/CMakeLists.txt new file mode 100644 index 00000000..05d4564b --- /dev/null +++ b/examples/breakout_oled_128x128/CMakeLists.txt @@ -0,0 +1,12 @@ +set(OUTPUT_NAME oled_128x128_demo) + +add_executable( + ${OUTPUT_NAME} + oled_128x128_demo.cpp +) + +# Pull in pico libraries that we need +target_link_libraries(${OUTPUT_NAME} pico_stdlib pico_graphics sh1107) + +# create map/bin/hex file etc. +pico_add_extra_outputs(${OUTPUT_NAME}) \ No newline at end of file diff --git a/examples/breakout_oled_128x128/oled_128x128_demo.cpp b/examples/breakout_oled_128x128/oled_128x128_demo.cpp new file mode 100644 index 00000000..c9d84e9c --- /dev/null +++ b/examples/breakout_oled_128x128/oled_128x128_demo.cpp @@ -0,0 +1,52 @@ +#include +#include +#include +#include + +#include "drivers/sh1107/sh1107.hpp" +#include "libraries/pico_graphics/pico_graphics.hpp" +#include "time.h" + +using namespace pimoroni; + + +const int WIDTH = 128; +const int HEIGHT = 128; + +I2C i2c(4, 5); + +SH1107 sh1107(WIDTH, HEIGHT, i2c); +PicoGraphics_Pen1Bit graphics(sh1107.width, sh1107.height, nullptr); + +int main() { + + + uint i = 0; + while(true) { + // for(int y = 0; y < 128; y++) { + // for(int x = 0; x < 128; x++) { + // graphics.set_pen(((x + y + i) & 0b1111) > 8 ? 1 : 0); + // graphics.set_pixel({x, y}); + // } + // } + + graphics.set_pen(1); + graphics.clear(); + + float s = (sin(i / 10.0f) * 1.0f) + 1.5f; + float a = (cos(i / 10.0f) * 10.0f); + + graphics.set_font("gothic"); + + graphics.set_pen(0); + int w = graphics.measure_text("PIRATES!", s); + graphics.text("PIRATES!", {64 - w / 2, 60}, 0, s, a); + + sh1107.update(&graphics); + + //sleep_ms(10); + + i++; + } + +} \ No newline at end of file diff --git a/libraries/pico_graphics/pico_graphics.cmake b/libraries/pico_graphics/pico_graphics.cmake index e06ea5d7..71d0cf9a 100644 --- a/libraries/pico_graphics/pico_graphics.cmake +++ b/libraries/pico_graphics/pico_graphics.cmake @@ -1,6 +1,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_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 765573e3..0fddba41 100644 --- a/libraries/pico_graphics/pico_graphics.hpp +++ b/libraries/pico_graphics/pico_graphics.hpp @@ -128,7 +128,8 @@ namespace pimoroni { class PicoGraphics { public: enum PenType { - PEN_P2 = 0, + PEN_1BIT, + PEN_P2, PEN_P4, PEN_P8, PEN_RGB332, @@ -221,6 +222,21 @@ namespace pimoroni { void line(Point p1, Point p2); }; + class PicoGraphics_Pen1Bit : public PicoGraphics { + public: + uint8_t color; + + PicoGraphics_Pen1Bit(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; + + static size_t buffer_size(uint w, uint h) { + return w * h / 8; + } + }; + class PicoGraphics_PenP4 : public PicoGraphics { public: diff --git a/libraries/pico_graphics/pico_graphics_pen_1bit.cpp b/libraries/pico_graphics/pico_graphics_pen_1bit.cpp new file mode 100644 index 00000000..af052d4b --- /dev/null +++ b/libraries/pico_graphics/pico_graphics_pen_1bit.cpp @@ -0,0 +1,35 @@ +#include "pico_graphics.hpp" + +namespace pimoroni { + + PicoGraphics_Pen1Bit::PicoGraphics_Pen1Bit(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_Pen1Bit::set_pen(uint c) { + color = c != 0 ? 1 : 0; + } + + void PicoGraphics_Pen1Bit::set_pen(uint8_t r, uint8_t g, uint8_t b) { + color = r != 0 || g != 0 || b != 0 ? 1 : 0; + } + + void PicoGraphics_Pen1Bit::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.x / 8) + (p.y * bounds.w / 8)]; + + uint bo = 7 - (p.x & 0b111); + + // forceably clear the bit + *f &= ~(1U << bo); + + // set pixel + *f |= (color << bo); + } + +} \ No newline at end of file diff --git a/micropython/modules/jpegdec/jpegdec.cpp b/micropython/modules/jpegdec/jpegdec.cpp index ee853ae9..9791aa56 100644 --- a/micropython/modules/jpegdec/jpegdec.cpp +++ b/micropython/modules/jpegdec/jpegdec.cpp @@ -107,6 +107,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; } } return result; @@ -192,4 +195,4 @@ mp_obj_t _JPEG_getHeight(mp_obj_t self_in) { return mp_obj_new_int(self->jpeg->getHeight()); } -} \ No newline at end of file +}