diff --git a/.github/workflows/micropython-badger2040.yml b/.github/workflows/micropython-badger2040.yml index ff469c40..da266933 100644 --- a/.github/workflows/micropython-badger2040.yml +++ b/.github/workflows/micropython-badger2040.yml @@ -7,7 +7,7 @@ on: types: [created] env: - MICROPYTHON_VERSION: v1.18 + MICROPYTHON_VERSION: v1.19 BOARD_TYPE: PICO # MicroPython version will be contained in github.event.release.tag_name for releases RELEASE_FILE: pimoroni-badger2040-${{github.event.release.tag_name || github.sha}}-micropython.uf2 diff --git a/.github/workflows/micropython.yml b/.github/workflows/micropython.yml index 23fb076a..8a2c8523 100644 --- a/.github/workflows/micropython.yml +++ b/.github/workflows/micropython.yml @@ -7,7 +7,7 @@ on: types: [created] env: - MICROPYTHON_VERSION: v1.18 + MICROPYTHON_VERSION: v1.19 jobs: deps: @@ -66,6 +66,8 @@ jobs: board: PIMORONI_PICOLIPO_4MB - name: picolipo_16mb board: PIMORONI_PICOLIPO_16MB + - name: tufty2040 + board: PIMORONI_TUFTY2040 env: # MicroPython version will be contained in github.event.release.tag_name for releases @@ -107,6 +109,12 @@ jobs: with: submodules: true path: pimoroni-pico-${{ github.sha }} + + - name: "HACK: MicroPython Board Fixups" + shell: bash + working-directory: micropython/ports/rp2 + run: | + ../../../pimoroni-pico-${GITHUB_SHA}/micropython/_board/board-fixup.sh ${{matrix.name}} ${{matrix.board}} ../../../pimoroni-pico-${GITHUB_SHA}/micropython/_board - name: Configure MicroPython shell: bash diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index fc199f40..3c870348 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -1 +1,2 @@ include(pimoroni_i2c.cmake) +include(pimoroni_bus.cmake) \ No newline at end of file diff --git a/libraries/breakout_roundlcd/CMakeLists.txt b/common/pimoroni_bus.cmake similarity index 55% rename from libraries/breakout_roundlcd/CMakeLists.txt rename to common/pimoroni_bus.cmake index 46e19eb2..fb24b56e 100644 --- a/libraries/breakout_roundlcd/CMakeLists.txt +++ b/common/pimoroni_bus.cmake @@ -1,11 +1,11 @@ -set(LIB_NAME breakout_roundlcd) +set(LIB_NAME pimoroni_bus) add_library(${LIB_NAME} INTERFACE) target_sources(${LIB_NAME} INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/${LIB_NAME}.cpp + ${CMAKE_CURRENT_LIST_DIR}/${LIB_NAME}.cpp ) target_include_directories(${LIB_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR}) # Pull in pico libraries that we need -target_link_libraries(${LIB_NAME} INTERFACE pico_stdlib st7789 pico_graphics) \ No newline at end of file +target_link_libraries(${LIB_NAME} INTERFACE pico_stdlib hardware_spi) diff --git a/common/pimoroni_bus.cpp b/common/pimoroni_bus.cpp new file mode 100644 index 00000000..d6c7eac1 --- /dev/null +++ b/common/pimoroni_bus.cpp @@ -0,0 +1,15 @@ +#include "pimoroni_bus.hpp" + +namespace pimoroni { + SPIPins get_spi_pins(BG_SPI_SLOT slot) { + switch(slot) { + case PICO_EXPLORER_ONBOARD: + return {PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_FRONT_CS, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, PIN_UNUSED, SPI_DEFAULT_DC, PIN_UNUSED}; + case BG_SPI_FRONT: + return {PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_FRONT_CS, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, PIN_UNUSED, SPI_DEFAULT_DC, SPI_BG_FRONT_PWM}; + case BG_SPI_BACK: + return {PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_BACK_CS, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, PIN_UNUSED, SPI_DEFAULT_DC, SPI_BG_BACK_PWM}; + } + return {PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_FRONT_CS, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, PIN_UNUSED, SPI_DEFAULT_DC, SPI_BG_FRONT_PWM}; + }; +} \ No newline at end of file diff --git a/common/pimoroni_bus.hpp b/common/pimoroni_bus.hpp new file mode 100644 index 00000000..8a9d400b --- /dev/null +++ b/common/pimoroni_bus.hpp @@ -0,0 +1,27 @@ +#pragma once +#include "pimoroni_common.hpp" +#include "hardware/gpio.h" +#include "hardware/spi.h" + +namespace pimoroni { + struct SPIPins { + spi_inst_t *spi; + uint cs; + uint sck; + uint mosi; + uint miso; + uint dc; + uint bl; + }; + + struct ParallelPins { + uint cs; + uint dc; + uint wr_sck; + uint rd_sck; + uint d0; + uint bl; + }; + + SPIPins get_spi_pins(BG_SPI_SLOT slot); +} \ No newline at end of file diff --git a/common/pimoroni_common.hpp b/common/pimoroni_common.hpp index 03b64f1f..78481588 100644 --- a/common/pimoroni_common.hpp +++ b/common/pimoroni_common.hpp @@ -32,6 +32,7 @@ namespace pimoroni { // SPI static const unsigned int SPI_DEFAULT_MOSI = 19; static const unsigned int SPI_DEFAULT_MISO = 16; + static const unsigned int SPI_DEFAULT_DC = 16; static const unsigned int SPI_DEFAULT_SCK = 18; static const unsigned int SPI_BG_FRONT_PWM = 20; @@ -53,6 +54,13 @@ namespace pimoroni { INTERSTATE_75, SERVO_2040 }; + + enum Rotation { + ROTATE_0 = 0, + ROTATE_90 = 90, + ROTATE_180 = 180, + ROTATE_270 = 270 + }; enum Polarity { ACTIVE_LOW = 0, @@ -100,8 +108,8 @@ namespace pimoroni { uint8_t enable; }; - pin_pair() : first(0), second(0) {} - pin_pair(uint8_t first, uint8_t second) : first(first), second(second) {} + constexpr pin_pair() : first(0), second(0) {} + constexpr pin_pair(uint8_t first, uint8_t second) : first(first), second(second) {} }; struct bool_pair { 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/drivers/st7735/st7735.cmake b/drivers/st7735/st7735.cmake index 3a44ee58..85c8ae60 100644 --- a/drivers/st7735/st7735.cmake +++ b/drivers/st7735/st7735.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 hardware_pwm hardware_dma) +target_link_libraries(${DRIVER_NAME} INTERFACE pico_stdlib pimoroni_bus hardware_spi hardware_pwm hardware_dma) diff --git a/drivers/st7735/st7735.cpp b/drivers/st7735/st7735.cpp index 52cd714d..aac94901 100644 --- a/drivers/st7735/st7735.cpp +++ b/drivers/st7735/st7735.cpp @@ -67,7 +67,6 @@ namespace pimoroni { }; void ST7735::init(bool auto_init_sequence) { - // configure spi interface and pins spi_init(spi, spi_baud); gpio_set_function(dc, GPIO_FUNC_SIO); @@ -76,21 +75,9 @@ namespace pimoroni { gpio_set_function(cs, GPIO_FUNC_SIO); gpio_set_dir(cs, GPIO_OUT); - gpio_set_function(sck, GPIO_FUNC_SPI); + gpio_set_function(sck, GPIO_FUNC_SPI); gpio_set_function(mosi, GPIO_FUNC_SPI); - if(miso != PIN_UNUSED) { - gpio_set_function(miso, GPIO_FUNC_SPI); - } - - // if supported by the display then the vsync pin is - // toggled high during vertical blanking period - if(vsync != PIN_UNUSED) { - gpio_set_function(vsync, GPIO_FUNC_SIO); - gpio_set_dir(vsync, GPIO_IN); - gpio_set_pulls(vsync, false, true); - } - // if a backlight pin is provided then set it up for // pwm control if(bl != PIN_UNUSED) { @@ -98,6 +85,7 @@ namespace pimoroni { pwm_set_wrap(pwm_gpio_to_slice_num(bl), 65535); pwm_init(pwm_gpio_to_slice_num(bl), &cfg, true); gpio_set_function(bl, GPIO_FUNC_PWM); + set_backlight(0); // Turn backlight off initially to avoid nasty surprises } // if auto_init_sequence then send initialisation sequence @@ -155,30 +143,10 @@ namespace pimoroni { command(reg::DISPON); sleep_ms(100); } - } - spi_inst_t* ST7735::get_spi() const { - return spi; - } - - int ST7735::get_cs() const { - return cs; - } - - int ST7735::get_dc() const { - return dc; - } - - int ST7735::get_sck() const { - return sck; - } - - int ST7735::get_mosi() const { - return mosi; - } - - int ST7735::get_bl() const { - return bl; + if(bl != PIN_UNUSED) { + set_backlight(255); // Turn backlight on now surprises have passed + } } void ST7735::command(uint8_t command, size_t len, const char *data) { @@ -195,8 +163,21 @@ namespace pimoroni { gpio_put(cs, 1); } - void ST7735::update(bool dont_block) { - ST7735::command(reg::RAMWR, width * height * sizeof(uint16_t), (const char*)frame_buffer); + // Native 16-bit framebuffer update + void ST7735::update(PicoGraphics *graphics) { + if(graphics->pen_type == PicoGraphics::PEN_RGB565) { + command(reg::RAMWR, width * height * sizeof(uint16_t), (const char*)graphics->frame_buffer); + } else { + command(reg::RAMWR); + gpio_put(dc, 1); // data mode + gpio_put(cs, 0); + + graphics->scanline_convert(PicoGraphics::PEN_RGB565, [this](void *data, size_t length) { + spi_write_blocking(spi, (const uint8_t*)data, length); + }); + + gpio_put(cs, 1); + } } void ST7735::set_backlight(uint8_t brightness) { diff --git a/drivers/st7735/st7735.hpp b/drivers/st7735/st7735.hpp index e4f8c327..66edf56e 100644 --- a/drivers/st7735/st7735.hpp +++ b/drivers/st7735/st7735.hpp @@ -2,21 +2,16 @@ #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 ST7735 { + class ST7735 : public DisplayDriver { //-------------------------------------------------- // Constants //-------------------------------------------------- - public: - static const uint8_t DEFAULT_CS_PIN = 17; - static const uint8_t DEFAULT_DC_PIN = 16; - static const uint8_t DEFAULT_SCK_PIN = 18; - static const uint8_t DEFAULT_MOSI_PIN = 19; - static const uint8_t DEFAULT_BL_PIN = 20; - private: static const uint8_t ROWS = 162; static const uint8_t COLS = 132; @@ -25,83 +20,44 @@ namespace pimoroni { // Variables //-------------------------------------------------- private: - // screen properties - uint16_t width; - uint16_t height; - uint16_t row_stride; - public: - // frame buffer where pixel data is stored - uint16_t *frame_buffer; - - private: spi_inst_t *spi = spi0; uint32_t dma_channel; // interface pins with our standard defaults where appropriate - uint cs = DEFAULT_CS_PIN; - uint dc = DEFAULT_DC_PIN; - uint sck = DEFAULT_SCK_PIN; - uint mosi = DEFAULT_MOSI_PIN; - uint miso = PIN_UNUSED; // we generally don't use this pin - uint bl = DEFAULT_BL_PIN; - uint vsync = PIN_UNUSED; // only available on some products + uint cs; + uint dc; + uint sck; + uint mosi; + uint bl; uint32_t spi_baud = 30 * 1024 * 1024; uint8_t offset_cols = 0; uint8_t offset_rows = 0; - //-------------------------------------------------- // Constructors/Destructor //-------------------------------------------------- public: - ST7735(uint16_t width, uint16_t height, uint16_t *frame_buffer, BG_SPI_SLOT slot) : - width(width), height(height), frame_buffer(frame_buffer) { - switch(slot) { - case PICO_EXPLORER_ONBOARD: // Don't read too much into this, the case is just here to avoid a compile error - cs = SPI_BG_FRONT_CS; - bl = PIN_UNUSED; - break; - case BG_SPI_FRONT: - cs = SPI_BG_FRONT_CS; - bl = SPI_BG_FRONT_PWM; - break; - case BG_SPI_BACK: - cs = SPI_BG_BACK_CS; - bl = SPI_BG_BACK_PWM; - break; + ST7735(uint16_t width, uint16_t height, SPIPins pins) : + DisplayDriver(width, height, ROTATE_0), + spi(pins.spi), cs(pins.cs), dc(pins.dc), sck(pins.sck), mosi(pins.mosi), bl(pins.bl) { + init(); } - } - - ST7735(uint16_t width, uint16_t height, uint16_t *frame_buffer) : - width(width), height(height), frame_buffer(frame_buffer) {} - - ST7735(uint16_t width, uint16_t height, uint16_t *frame_buffer, - spi_inst_t *spi, - uint8_t cs, uint8_t dc, uint8_t sck, uint8_t mosi, uint8_t miso = -1, uint8_t bl = -1) : - width(width), height(height), frame_buffer(frame_buffer), - spi(spi), cs(cs), dc(dc), sck(sck), mosi(mosi), miso(miso), bl(bl) {} //-------------------------------------------------- // Methods //-------------------------------------------------- public: - void init(bool auto_init_sequence = true); + void update(PicoGraphics *graphics) override; + void set_backlight(uint8_t brightness) override; - spi_inst_t* get_spi() const; - int get_cs() const; - int get_dc() const; - int get_sck() const; - int get_mosi() const; - int get_bl() const; - + private: + void init(bool auto_init_sequence = true); void command(uint8_t command, size_t len = 0, const char *data = NULL); - void update(bool dont_block = false); - void set_backlight(uint8_t brightness); }; } diff --git a/drivers/st7789/README.md b/drivers/st7789/README.md new file mode 100644 index 00000000..b9e92b4b --- /dev/null +++ b/drivers/st7789/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/st7789/st7789.cmake b/drivers/st7789/st7789.cmake index 5dbf4c8b..45d5247c 100644 --- a/drivers/st7789/st7789.cmake +++ b/drivers/st7789/st7789.cmake @@ -4,9 +4,11 @@ add_library(${DRIVER_NAME} INTERFACE) target_sources(${DRIVER_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR}/${DRIVER_NAME}.cpp) +pico_generate_pio_header(${DRIVER_NAME} ${CMAKE_CURRENT_LIST_DIR}/st7789_parallel.pio) + target_include_directories(${DRIVER_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR}) target_include_directories(st7789 INTERFACE ${CMAKE_CURRENT_LIST_DIR}) # Pull in pico libraries that we need -target_link_libraries(${DRIVER_NAME} INTERFACE pico_stdlib hardware_spi hardware_pwm hardware_dma) +target_link_libraries(${DRIVER_NAME} INTERFACE pico_stdlib pimoroni_bus hardware_spi hardware_pwm hardware_pio hardware_dma pico_graphics) diff --git a/drivers/st7789/st7789.cpp b/drivers/st7789/st7789.cpp index 0c0e2969..e3243122 100644 --- a/drivers/st7789/st7789.cpp +++ b/drivers/st7789/st7789.cpp @@ -13,7 +13,7 @@ namespace pimoroni { COL_ORDER = 0b01000000, SWAP_XY = 0b00100000, // AKA "MV" SCAN_ORDER = 0b00010000, - RGB = 0b00001000, + RGB_BGR = 0b00001000, HORIZ_ORDER = 0b00000100 }; @@ -46,7 +46,23 @@ namespace pimoroni { PWMFRSEL = 0xCC }; - void ST7789::init() { + void ST7789::common_init() { + gpio_set_function(dc, GPIO_FUNC_SIO); + gpio_set_dir(dc, GPIO_OUT); + + gpio_set_function(cs, GPIO_FUNC_SIO); + gpio_set_dir(cs, GPIO_OUT); + + // if a backlight pin is provided then set it up for + // pwm control + if(bl != PIN_UNUSED) { + pwm_config cfg = pwm_get_default_config(); + pwm_set_wrap(pwm_gpio_to_slice_num(bl), 65535); + pwm_init(pwm_gpio_to_slice_num(bl), &cfg, true); + gpio_set_function(bl, GPIO_FUNC_PWM); + set_backlight(0); // Turn backlight off initially to avoid nasty surprises + } + command(reg::SWRESET); sleep_ms(150); @@ -70,8 +86,7 @@ namespace pimoroni { command(reg::GMCTRN1, 14, "\xD0\x04\x0C\x11\x13\x2C\x3F\x44\x51\x2F\x1F\x1F\x20\x23"); } - if((width == 320 && height == 240) - || (width == 240 && height == 320)) { + if(width == 320 && height == 240) { command(reg::GCTRL, 1, "\x35"); command(reg::VCOMS, 1, "\x1f"); command(0xd6, 1, "\xa1"); // ??? @@ -85,30 +100,78 @@ namespace pimoroni { sleep_ms(100); - configure_display(false); + configure_display(rotation); if(bl != PIN_UNUSED) { - update(); // Send the new buffer to the display to clear any previous content + //update(); // Send the new buffer to the display to clear any previous content sleep_ms(50); // Wait for the update to apply set_backlight(255); // Turn backlight on now surprises have passed } } - void ST7789::configure_display(bool rotate180) { + void ST7789::cleanup() { + if(spi) return; // SPI mode needs no tear down + if(dma_channel_is_claimed(parallel_dma)) { + dma_channel_abort(parallel_dma); + dma_channel_unclaim(parallel_dma); + } + + if(pio_sm_is_claimed(parallel_pio, parallel_sm)) { + pio_sm_set_enabled(parallel_pio, parallel_sm, false); + pio_sm_drain_tx_fifo(parallel_pio, parallel_sm); + pio_sm_unclaim(parallel_pio, parallel_sm); + } + } + + void ST7789::configure_display(Rotation rotate) { + + bool rotate180 = rotate == ROTATE_180 || rotate == ROTATE_90; + + if(rotate == ROTATE_90 || rotate == ROTATE_270) { + std::swap(width, height); + } + // 240x240 Square and Round LCD Breakouts - // TODO: How can we support 90 degree rotations here? if(width == 240 && height == 240) { - caset[0] = 0; - caset[1] = 239; - if(round) { - raset[0] = 40; - raset[1] = 279; - } else { - raset[0] = rotate180 ? 80 : 0; - raset[1] = rotate180 ? 329 : 239; + int row_offset = round ? 40 : 80; + int col_offset = 0; + + switch(rotate) { + case ROTATE_90: + if (!round) row_offset = 0; + caset[0] = row_offset; + caset[1] = width + row_offset - 1; + raset[0] = col_offset; + raset[1] = width + col_offset - 1; + + madctl = MADCTL::HORIZ_ORDER | MADCTL::COL_ORDER | MADCTL::SWAP_XY; + break; + case ROTATE_180: + caset[0] = col_offset; + caset[1] = width + col_offset - 1; + raset[0] = row_offset; + raset[1] = width + row_offset - 1; + + madctl = MADCTL::HORIZ_ORDER | MADCTL::COL_ORDER | MADCTL::ROW_ORDER; + break; + case ROTATE_270: + caset[0] = row_offset; + caset[1] = width + row_offset - 1; + raset[0] = col_offset; + raset[1] = width + col_offset - 1; + + madctl = MADCTL::ROW_ORDER | MADCTL::SWAP_XY; + break; + default: // ROTATE_0 (and for any smart-alec who tries to rotate 45 degrees or something...) + if (!round) row_offset = 0; + caset[0] = col_offset; + caset[1] = width + col_offset - 1; + raset[0] = row_offset; + raset[1] = width + row_offset - 1; + + madctl = MADCTL::HORIZ_ORDER; + break; } - madctl = rotate180 ? (MADCTL::COL_ORDER | MADCTL::ROW_ORDER) : 0; - madctl |= MADCTL::HORIZ_ORDER; } // Pico Display @@ -160,46 +223,99 @@ namespace pimoroni { command(reg::MADCTL, 1, (char *)&madctl); } - spi_inst_t* ST7789::get_spi() const { - return spi; + void ST7789::write_blocking_parallel_dma(const uint8_t *src, size_t len) { + while (dma_channel_is_busy(parallel_dma)) + ; + dma_channel_set_trans_count(parallel_dma, len, false); + dma_channel_set_read_addr(parallel_dma, src, true); } - uint ST7789::get_cs() const { - return cs; - } + void ST7789::write_blocking_parallel(const uint8_t *src, size_t len) { + const uint8_t *p = src; + while(len--) { + // Does not byte align correctly + //pio_sm_put_blocking(parallel_pio, parallel_sm, *p); + while (pio_sm_is_tx_fifo_full(parallel_pio, parallel_sm)) + ; + *(volatile uint8_t*)¶llel_pio->txf[parallel_sm] = *p; + p++; + } - uint ST7789::get_dc() const { - return dc; - } - - uint ST7789::get_sck() const { - return sck; - } - - uint ST7789::get_mosi() const { - return mosi; - } - - uint ST7789::get_bl() const { - return bl; + uint32_t sm_stall_mask = 1u << (parallel_sm + PIO_FDEBUG_TXSTALL_LSB); + parallel_pio->fdebug = sm_stall_mask; + while (!(parallel_pio->fdebug & sm_stall_mask)) + ; + /*uint32_t mask = 0xff << d0; + while(len--) { + gpio_put(wr_sck, false); + uint8_t v = *src++; + gpio_put_masked(mask, v << d0); + //asm("nop;"); + gpio_put(wr_sck, true); + asm("nop;"); + }*/ } void ST7789::command(uint8_t command, size_t len, const char *data) { - gpio_put(cs, 0); - gpio_put(dc, 0); // command mode - spi_write_blocking(spi, &command, 1); + + gpio_put(cs, 0); + + if(spi) { + spi_write_blocking(spi, &command, 1); + } else { + write_blocking_parallel(&command, 1); + } if(data) { gpio_put(dc, 1); // data mode - spi_write_blocking(spi, (const uint8_t*)data, len); + if(spi) { + spi_write_blocking(spi, (const uint8_t*)data, len); + } else { + write_blocking_parallel((const uint8_t*)data, len); + } } gpio_put(cs, 1); } + + void ST7789::update(PicoGraphics *graphics) { + uint8_t cmd = reg::RAMWR; - void ST7789::update() { - command(reg::RAMWR, width * height * sizeof(uint16_t), (const char*)frame_buffer); + if(graphics->pen_type == PicoGraphics::PEN_RGB565) { // Display buffer is screen native + command(cmd, width * height * sizeof(uint16_t), (const char*)graphics->frame_buffer); + } else if(spi) { // SPI Bus + gpio_put(dc, 0); // command mode + gpio_put(cs, 0); + spi_write_blocking(spi, &cmd, 1); + gpio_put(dc, 1); // data mode + + graphics->scanline_convert(PicoGraphics::PEN_RGB565, [this](void *data, size_t length) { + spi_write_blocking(spi, (const uint8_t*)data, length); + }); + + gpio_put(cs, 1); + } else { // Parallel Bus + gpio_put(dc, 0); // command mode + gpio_put(cs, 0); + write_blocking_parallel(&cmd, 1); + gpio_put(dc, 1); // data mode + + int scanline = 0; + + graphics->scanline_convert(PicoGraphics::PEN_RGB565, [this, scanline](void *data, size_t length) mutable { + write_blocking_parallel_dma((const uint8_t*)data, length); + + // Stall on the last scanline since "data" goes out of scope and is lost + scanline++; + if(scanline == height) { + while (dma_channel_is_busy(parallel_dma)) + ; + } + }); + + gpio_put(cs, 1); + } } void ST7789::set_backlight(uint8_t brightness) { @@ -209,8 +325,4 @@ namespace pimoroni { uint16_t value = (uint16_t)(pow((float)(brightness) / 255.0f, gamma) * 65535.0f + 0.5f); pwm_set_gpio_level(bl, value); } - - void ST7789::flip(){ - configure_display(true); - } } diff --git a/drivers/st7789/st7789.hpp b/drivers/st7789/st7789.hpp index e2bc7cc7..2a7fa36f 100644 --- a/drivers/st7789/st7789.hpp +++ b/drivers/st7789/st7789.hpp @@ -1,32 +1,46 @@ #pragma once #include "hardware/spi.h" +#include "hardware/dma.h" #include "hardware/gpio.h" +#include "hardware/pio.h" #include "hardware/pwm.h" -#include "../../common/pimoroni_common.hpp" +#include "common/pimoroni_common.hpp" +#include "common/pimoroni_bus.hpp" +#include "libraries/pico_graphics/pico_graphics.hpp" + + +#include "st7789_parallel.pio.h" + +#include namespace pimoroni { - class ST7789 { + class ST7789 : public DisplayDriver { spi_inst_t *spi = PIMORONI_SPI_DEFAULT_INSTANCE; + + public: + bool round; //-------------------------------------------------- // Variables //-------------------------------------------------- private: - // screen properties - uint16_t width; - uint16_t height; - bool round; // interface pins with our standard defaults where appropriate uint cs; uint dc; - uint sck; - uint mosi; + uint wr_sck; + uint rd_sck = PIN_UNUSED; + uint d0; uint bl; uint vsync = PIN_UNUSED; // only available on some products + uint parallel_sm; + PIO parallel_pio; + uint parallel_offset; + uint parallel_dma; + // The ST7789 requires 16 ns between SPI rising edges. // 16 ns = 62,500,000 Hz @@ -34,87 +48,83 @@ namespace pimoroni { public: - // frame buffer where pixel data is stored - uint16_t *frame_buffer; + // Parallel init + ST7789(uint16_t width, uint16_t height, Rotation rotation, ParallelPins pins) : + DisplayDriver(width, height, rotation), + spi(nullptr), round(false), + cs(pins.cs), dc(pins.dc), wr_sck(pins.wr_sck), rd_sck(pins.rd_sck), d0(pins.d0), bl(pins.bl) { - ST7789(uint16_t width, uint16_t height, bool round, uint16_t *frame_buffer, - spi_inst_t *spi, - uint cs, uint dc, uint sck, uint mosi, uint bl = PIN_UNUSED) : - spi(spi), - width(width), height(height), round(round), - cs(cs), dc(dc), sck(sck), mosi(mosi), bl(bl), frame_buffer(frame_buffer) { + parallel_pio = pio1; + parallel_sm = pio_claim_unused_sm(parallel_pio, true); + parallel_offset = pio_add_program(parallel_pio, &st7789_parallel_program); + + //gpio_init(wr_sck); + //gpio_set_dir(wr_sck, GPIO_OUT); + //gpio_set_function(wr_sck, GPIO_FUNC_SIO); + pio_gpio_init(parallel_pio, wr_sck); - if(!this->frame_buffer) { - this->frame_buffer = new uint16_t[width * height]; - } - - // configure spi interface and pins - spi_init(spi, SPI_BAUD); - - gpio_set_function(dc, GPIO_FUNC_SIO); - gpio_set_dir(dc, GPIO_OUT); - - gpio_set_function(cs, GPIO_FUNC_SIO); - gpio_set_dir(cs, GPIO_OUT); - - gpio_set_function(sck, GPIO_FUNC_SPI); - gpio_set_function(mosi, GPIO_FUNC_SPI); - - // if a backlight pin is provided then set it up for - // pwm control - if(bl != PIN_UNUSED) { - pwm_config cfg = pwm_get_default_config(); - pwm_set_wrap(pwm_gpio_to_slice_num(bl), 65535); - pwm_init(pwm_gpio_to_slice_num(bl), &cfg, true); - gpio_set_function(bl, GPIO_FUNC_PWM); - set_backlight(0); // Turn backlight off initially to avoid nasty surprises - } + gpio_set_function(rd_sck, GPIO_FUNC_SIO); + gpio_set_dir(rd_sck, GPIO_OUT); + for(auto i = 0u; i < 8; i++) { + //gpio_set_function(d0 + i, GPIO_FUNC_SIO); + //gpio_set_dir(d0 + i, GPIO_OUT); + //gpio_init(d0 + 0); gpio_set_dir(d0 + i, GPIO_OUT); + pio_gpio_init(parallel_pio, d0 + i); } + pio_sm_set_consecutive_pindirs(parallel_pio, parallel_sm, d0, 8, true); + pio_sm_set_consecutive_pindirs(parallel_pio, parallel_sm, wr_sck, 1, true); - //-------------------------------------------------- - // Methods - //-------------------------------------------------- - public: - void init(); - void configure_display(bool rotate180); + pio_sm_config c = st7789_parallel_program_get_default_config(parallel_offset); - spi_inst_t* get_spi() const; - uint get_cs() const; - uint get_dc() const; - uint get_sck() const; - uint get_mosi() const; - uint get_bl() const; + sm_config_set_out_pins(&c, d0, 8); + sm_config_set_sideset_pins(&c, wr_sck); + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); + sm_config_set_out_shift(&c, false, true, 8); + sm_config_set_clkdiv(&c, 4); + + pio_sm_init(parallel_pio, parallel_sm, parallel_offset, &c); + pio_sm_set_enabled(parallel_pio, parallel_sm, true); + + parallel_dma = dma_claim_unused_channel(true); + dma_channel_config config = dma_channel_get_default_config(parallel_dma); + channel_config_set_transfer_data_size(&config, DMA_SIZE_8); + channel_config_set_bswap(&config, false); + channel_config_set_dreq(&config, pio_get_dreq(parallel_pio, parallel_sm, true)); + dma_channel_configure(parallel_dma, &config, ¶llel_pio->txf[parallel_sm], NULL, 0, false); + + gpio_put(rd_sck, 1); + + common_init(); + } + + // Serial init + ST7789(uint16_t width, uint16_t height, Rotation rotation, bool round, SPIPins pins) : + DisplayDriver(width, height, rotation), + spi(pins.spi), round(round), + cs(pins.cs), dc(pins.dc), wr_sck(pins.sck), d0(pins.mosi), bl(pins.bl) { + + // configure spi interface and pins + spi_init(spi, SPI_BAUD); + + gpio_set_function(wr_sck, GPIO_FUNC_SPI); + gpio_set_function(d0, GPIO_FUNC_SPI); + + common_init(); + } + + void cleanup() override; + void update(PicoGraphics *graphics) override; + void set_backlight(uint8_t brightness) override; + + private: + void common_init(); + void configure_display(Rotation rotate); + void write_blocking_parallel_dma(const uint8_t *src, size_t len); + void write_blocking_parallel(const uint8_t *src, size_t len); void command(uint8_t command, size_t len = 0, const char *data = NULL); - void update(); - void set_backlight(uint8_t brightness); - void flip(); - - static uint get_slot_cs(BG_SPI_SLOT slot) { - switch(slot) { - case PICO_EXPLORER_ONBOARD: - return SPI_BG_FRONT_CS; - case BG_SPI_FRONT: - return SPI_BG_FRONT_CS; - case BG_SPI_BACK: - return SPI_BG_BACK_CS; - } - return PIN_UNUSED; - }; - - static uint get_slot_bl(BG_SPI_SLOT slot) { - switch(slot) { - case PICO_EXPLORER_ONBOARD: - return PIN_UNUSED; - case BG_SPI_FRONT: - return SPI_BG_FRONT_PWM; - case BG_SPI_BACK: - return SPI_BG_BACK_PWM; - } - return PIN_UNUSED; - }; }; } diff --git a/drivers/st7789/st7789_parallel.pio b/drivers/st7789/st7789_parallel.pio new file mode 100644 index 00000000..6438c028 --- /dev/null +++ b/drivers/st7789/st7789_parallel.pio @@ -0,0 +1,7 @@ +.program st7789_parallel +.side_set 1 + +.wrap_target + out pins, 8 side 0 + nop side 1 +.wrap \ No newline at end of file diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 8e45b4d8..e741ba8a 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,50 +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(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_as7262/explorer_bargraph.cmake b/examples/breakout_as7262/explorer_bargraph.cmake index 2baa50d4..2972e1a0 100644 --- a/examples/breakout_as7262/explorer_bargraph.cmake +++ b/examples/breakout_as7262/explorer_bargraph.cmake @@ -10,7 +10,7 @@ pico_enable_stdio_usb(${OUTPUT_NAME} 1) pico_enable_stdio_uart(${OUTPUT_NAME} 1) # Pull in pico libraries that we need -target_link_libraries(${OUTPUT_NAME} pico_stdlib breakout_as7262 pico_explorer) +target_link_libraries(${OUTPUT_NAME} pico_stdlib breakout_as7262 pico_explorer pico_graphics st7789) # create map/bin/hex file etc. pico_add_extra_outputs(${OUTPUT_NAME}) diff --git a/examples/breakout_as7262/explorer_bargraph.cpp b/examples/breakout_as7262/explorer_bargraph.cpp index 189f624f..35ba9c6f 100644 --- a/examples/breakout_as7262/explorer_bargraph.cpp +++ b/examples/breakout_as7262/explorer_bargraph.cpp @@ -4,29 +4,33 @@ #include "breakout_as7262.hpp" #include "pico_explorer.hpp" +#include "drivers/st7789/st7789.hpp" +#include "libraries/pico_graphics/pico_graphics.hpp" + using namespace pimoroni; constexpr float INTEGRATION_TIME = 10.0f; I2C i2c(BOARD::PICO_EXPLORER); BreakoutAS7262 as7262(&i2c); -uint16_t buffer[PicoExplorer::WIDTH * PicoExplorer::HEIGHT]; -PicoExplorer pico_explorer(buffer); -uint8_t bar_width = PicoExplorer::WIDTH / 6; -uint8_t bar_height = PicoExplorer::HEIGHT; +ST7789 st7789(PicoExplorer::WIDTH, PicoExplorer::HEIGHT, ROTATE_0, false, get_spi_pins(BG_SPI_FRONT)); +PicoGraphics_PenRGB332 graphics(st7789.width, st7789.height, nullptr); + void draw_bar(float scale, uint16_t channel) { + static uint8_t bar_width = st7789.width / 6; + static uint8_t bar_height = st7789.height; + int16_t bar_top = bar_height - (bar_height * scale); bar_top = std::max((int16_t)0, bar_top); int16_t current_bar_height = bar_height - bar_top; - pico_explorer.rectangle(Rect(channel * bar_width, bar_top, bar_width, current_bar_height - 1)); + graphics.rectangle(Rect(channel * bar_width, bar_top, bar_width, current_bar_height - 1)); } int main() { stdio_init_all(); - pico_explorer.init(); as7262.init(); uint8_t dev_type = as7262.device_type(); @@ -43,9 +47,17 @@ int main() { as7262.set_indicator_current(AS7262::indicator_current::ma4); as7262.set_leds(true, true); + Pen BLACK = graphics.create_pen(0, 0, 0); + Pen RED = graphics.create_pen(255, 0, 0); + Pen ORANGE = graphics.create_pen(255, 128, 0); + Pen YELLOW = graphics.create_pen(255, 255, 0); + Pen GREEN = graphics.create_pen(0, 255, 0); + Pen BLUE = graphics.create_pen(0, 0, 255); + Pen VIOLET = graphics.create_pen(255, 0, 255); + while(true) { - pico_explorer.set_pen(0, 0, 0); - pico_explorer.clear(); + graphics.set_pen(BLACK); + graphics.clear(); AS7262::reading reading = as7262.read(); printf("R: %f O: %f Y: %f G: %f B: %f V: %f \n", @@ -64,34 +76,34 @@ int main() { if(reading.blue > m) m = reading.blue; if(reading.violet > m) m = reading.violet; - pico_explorer.set_pen(0, 0, 0); - pico_explorer.clear(); + graphics.set_pen(BLACK); + graphics.clear(); // Red - pico_explorer.set_pen(255, 0, 0); + graphics.set_pen(RED); draw_bar(reading.red / m, 0); // Orange - pico_explorer.set_pen(255, 128, 0); + graphics.set_pen(ORANGE); draw_bar(reading.orange / m, 1); // Yellow - pico_explorer.set_pen(255, 255, 0); + graphics.set_pen(YELLOW); draw_bar(reading.yellow / m, 2); // Green - pico_explorer.set_pen(0, 255, 0); + graphics.set_pen(GREEN); draw_bar(reading.green / m, 3); // Blue - pico_explorer.set_pen(0, 0, 255); + graphics.set_pen(BLUE); draw_bar(reading.blue / m, 4); // Violet - pico_explorer.set_pen(255, 0, 255); + graphics.set_pen(VIOLET); draw_bar(reading.violet / m, 5); - pico_explorer.update(); + st7789.update(&graphics); sleep_ms(INTEGRATION_TIME); } diff --git a/examples/breakout_colourlcd160x80/CMakeLists.txt b/examples/breakout_colourlcd160x80/CMakeLists.txt index c54ee549..93f98791 100644 --- a/examples/breakout_colourlcd160x80/CMakeLists.txt +++ b/examples/breakout_colourlcd160x80/CMakeLists.txt @@ -2,11 +2,11 @@ set(OUTPUT_NAME colourlcd160x80_demo) add_executable( ${OUTPUT_NAME} - demo.cpp + colorlcd_160x80_demo.cpp ) # Pull in pico libraries that we need -target_link_libraries(${OUTPUT_NAME} pico_stdlib breakout_colourlcd160x80) +target_link_libraries(${OUTPUT_NAME} pico_stdlib pico_graphics st7735 hardware_spi) # create map/bin/hex file etc. pico_add_extra_outputs(${OUTPUT_NAME}) diff --git a/examples/breakout_colourlcd160x80/colorlcd_160x80_demo.cpp b/examples/breakout_colourlcd160x80/colorlcd_160x80_demo.cpp new file mode 100644 index 00000000..e94c985c --- /dev/null +++ b/examples/breakout_colourlcd160x80/colorlcd_160x80_demo.cpp @@ -0,0 +1,59 @@ +#include +#include + +#include "drivers/st7735/st7735.hpp" +#include "libraries/pico_graphics/pico_graphics.hpp" + +using namespace pimoroni; + +ST7735 st7735(160, 80, get_spi_pins(BG_SPI_FRONT)); +PicoGraphics_PenP8 graphics(st7735.width, st7735.height, nullptr); + +int main() { + st7735.set_backlight(255); + + struct pt { + float x; + float y; + uint8_t r; + float dx; + float dy; + Pen pen; + }; + + std::vector shapes; + for(int i = 0; i < 1000; i++) { + pt shape; + shape.x = rand() % graphics.bounds.w; + shape.y = rand() % graphics.bounds.h; + shape.r = (rand() % 10) + 3; + shape.dx = float(rand() % 255) / 128.0f; + shape.dy = float(rand() % 255) / 128.0f; + shape.pen = graphics.create_pen(rand() % 255, rand() % 255, rand() % 255); + shapes.push_back(shape); + } + + uint8_t bg = graphics.create_pen(120, 40, 60); + + while(true) { + graphics.set_pen(bg); + graphics.clear(); + + for(auto &shape : shapes) { + shape.x += shape.dx; + shape.y += shape.dy; + if(shape.x < 0) shape.dx *= -1; + if(shape.x >= graphics.bounds.w) shape.dx *= -1; + if(shape.y < 0) shape.dy *= -1; + if(shape.y >= graphics.bounds.h) shape.dy *= -1; + + graphics.set_pen(shape.pen); + graphics.circle(Point(shape.x, shape.y), shape.r); + } + + // update screen + st7735.update(&graphics); + } + + return 0; +} diff --git a/examples/breakout_colourlcd160x80/demo.cpp b/examples/breakout_colourlcd160x80/demo.cpp deleted file mode 100644 index cb4a06ca..00000000 --- a/examples/breakout_colourlcd160x80/demo.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include -#include - -#include "breakout_colourlcd160x80.hpp" - -using namespace pimoroni; - -uint16_t buffer[BreakoutColourLCD160x80::WIDTH * BreakoutColourLCD160x80::HEIGHT]; -BreakoutColourLCD160x80 lcd(buffer); - -int main() { - lcd.init(); - lcd.set_backlight(255); - - struct pt { - float x; - float y; - uint8_t r; - float dx; - float dy; - uint16_t pen; - }; - - std::vector shapes; - for(int i = 0; i < 1000; i++) { - pt shape; - shape.x = rand() % lcd.bounds.w; - shape.y = rand() % lcd.bounds.h; - shape.r = (rand() % 10) + 3; - shape.dx = float(rand() % 255) / 128.0f; - shape.dy = float(rand() % 255) / 128.0f; - shape.pen = lcd.create_pen(rand() % 255, rand() % 255, rand() % 255); - shapes.push_back(shape); - } - - while(true) { - lcd.set_pen(120, 40, 60); - lcd.clear(); - - for(auto &shape : shapes) { - shape.x += shape.dx; - shape.y += shape.dy; - if(shape.x < 0) shape.dx *= -1; - if(shape.x >= lcd.bounds.w) shape.dx *= -1; - if(shape.y < 0) shape.dy *= -1; - if(shape.y >= lcd.bounds.h) shape.dy *= -1; - - lcd.set_pen(shape.pen); - lcd.circle(Point(shape.x, shape.y), shape.r); - } - - // update screen - lcd.update(); - } - - return 0; -} diff --git a/examples/breakout_colourlcd240x240/CMakeLists.txt b/examples/breakout_colourlcd240x240/CMakeLists.txt index cc7825d2..bcb4b533 100644 --- a/examples/breakout_colourlcd240x240/CMakeLists.txt +++ b/examples/breakout_colourlcd240x240/CMakeLists.txt @@ -2,11 +2,11 @@ set(OUTPUT_NAME colourlcd240x240_demo) add_executable( ${OUTPUT_NAME} - demo.cpp + colorlcd_240x240_demo.cpp ) # Pull in pico libraries that we need -target_link_libraries(${OUTPUT_NAME} pico_stdlib generic_st7789) +target_link_libraries(${OUTPUT_NAME} pico_stdlib pico_graphics st7789) # create map/bin/hex file etc. pico_add_extra_outputs(${OUTPUT_NAME}) diff --git a/examples/breakout_colourlcd240x240/colorlcd_240x240_demo.cpp b/examples/breakout_colourlcd240x240/colorlcd_240x240_demo.cpp new file mode 100644 index 00000000..e4fb9f4b --- /dev/null +++ b/examples/breakout_colourlcd240x240/colorlcd_240x240_demo.cpp @@ -0,0 +1,68 @@ +#include +#include + +#include "drivers/st7789/st7789.hpp" +#include "libraries/pico_graphics/pico_graphics.hpp" + +using namespace pimoroni; + +const int WIDTH = 240; +const int HEIGHT = 240; + +ST7789 st7789(WIDTH, HEIGHT, ROTATE_0, false, get_spi_pins(BG_SPI_FRONT)); +PicoGraphics_PenRGB332 graphics(st7789.width, st7789.height, nullptr); + +int main() { + st7789.set_backlight(255); + + struct pt { + float x; + float y; + uint8_t r; + float dx; + float dy; + Pen pen; + }; + + std::vector shapes; + for(int i = 0; i < 100; i++) { + pt shape; + shape.r = (rand() % 10) + 3; + shape.x = rand() % (graphics.bounds.w - (shape.r * 2)); + shape.y = rand() % (graphics.bounds.h - (shape.r * 2)); + shape.x += shape.r; + shape.y += shape.r; + shape.dx = float(rand() % 255) / 64.0f; + shape.dy = float(rand() % 255) / 64.0f; + shape.pen = graphics.create_pen(rand() % 255, rand() % 255, rand() % 255); + shapes.push_back(shape); + } + + Pen BG = graphics.create_pen(120, 40, 60); + Pen WHITE = graphics.create_pen(255, 255, 255); + + while(true) { + graphics.set_pen(BG); + graphics.clear(); + + for(auto &shape : shapes) { + shape.x += shape.dx; + shape.y += shape.dy; + if(shape.x < shape.r) shape.dx *= -1; + if(shape.x >= graphics.bounds.w - shape.r) shape.dx *= -1; + if(shape.y < shape.r) shape.dy *= -1; + if(shape.y >= graphics.bounds.h - shape.r) shape.dy *= -1; + + graphics.set_pen(shape.pen); + graphics.circle(Point(shape.x, shape.y), shape.r); + } + + graphics.set_pen(WHITE); + graphics.text("Hello World", Point(0, 0), 240); + + // update screen + st7789.update(&graphics); + } + + return 0; +} diff --git a/examples/breakout_colourlcd240x240/demo.cpp b/examples/breakout_colourlcd240x240/demo.cpp deleted file mode 100644 index 3ab5f8c7..00000000 --- a/examples/breakout_colourlcd240x240/demo.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include -#include - -#include "generic_st7789.hpp" - -using namespace pimoroni; - -const int WIDTH = 240; -const int HEIGHT = 240; - -ST7789Generic lcd(WIDTH, HEIGHT, false, nullptr, BG_SPI_FRONT); - -int main() { - //lcd.configure_display(false); - lcd.set_backlight(255); - - struct pt { - float x; - float y; - uint8_t r; - float dx; - float dy; - uint16_t pen; - }; - - std::vector shapes; - for(int i = 0; i < 100; i++) { - pt shape; - shape.r = (rand() % 10) + 3; - shape.x = rand() % (lcd.bounds.w - (shape.r * 2)); - shape.y = rand() % (lcd.bounds.h - (shape.r * 2)); - shape.x += shape.r; - shape.y += shape.r; - shape.dx = float(rand() % 255) / 64.0f; - shape.dy = float(rand() % 255) / 64.0f; - shape.pen = lcd.create_pen(rand() % 255, rand() % 255, rand() % 255); - shapes.push_back(shape); - } - - while(true) { - lcd.set_pen(120, 40, 60); - lcd.clear(); - - for(auto &shape : shapes) { - shape.x += shape.dx; - shape.y += shape.dy; - if(shape.x < shape.r) shape.dx *= -1; - if(shape.x >= lcd.bounds.w - shape.r) shape.dx *= -1; - if(shape.y < shape.r) shape.dy *= -1; - if(shape.y >= lcd.bounds.h - shape.r) shape.dy *= -1; - - lcd.set_pen(shape.pen); - lcd.circle(Point(shape.x, shape.y), shape.r); - } - - lcd.set_pen(255, 255, 255); - lcd.text("Hello World", Point(0, 0), 240); - - // update screen - lcd.update(); - } - - return 0; -} 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/examples/breakout_roundlcd/CMakeLists.txt b/examples/breakout_roundlcd/CMakeLists.txt index a671c7bd..abf05124 100644 --- a/examples/breakout_roundlcd/CMakeLists.txt +++ b/examples/breakout_roundlcd/CMakeLists.txt @@ -2,11 +2,11 @@ set(OUTPUT_NAME roundlcd_demo) add_executable( ${OUTPUT_NAME} - demo.cpp + roundlcd_demo.cpp ) # Pull in pico libraries that we need -target_link_libraries(${OUTPUT_NAME} pico_stdlib generic_st7789) +target_link_libraries(${OUTPUT_NAME} pico_stdlib pico_graphics st7789) # create map/bin/hex file etc. pico_add_extra_outputs(${OUTPUT_NAME}) \ No newline at end of file diff --git a/examples/breakout_roundlcd/demo.cpp b/examples/breakout_roundlcd/roundlcd_demo.cpp similarity index 71% rename from examples/breakout_roundlcd/demo.cpp rename to examples/breakout_roundlcd/roundlcd_demo.cpp index 9c8880eb..4761c240 100644 --- a/examples/breakout_roundlcd/demo.cpp +++ b/examples/breakout_roundlcd/roundlcd_demo.cpp @@ -3,7 +3,8 @@ #include #include -#include "generic_st7789.hpp" +#include "drivers/st7789/st7789.hpp" +#include "libraries/pico_graphics/pico_graphics.hpp" #include "time.h" // Place a 1.3 Round SPI LCD in the *front* slot of breakout garden. @@ -14,7 +15,8 @@ using namespace pimoroni; const int WIDTH = 240; const int HEIGHT = 240; -ST7789Generic display(WIDTH, HEIGHT, true, nullptr, BG_SPI_FRONT); +ST7789 st7789(WIDTH, HEIGHT, ROTATE_0, true, get_spi_pins(BG_SPI_FRONT)); +PicoGraphics_PenRGB332 graphics(st7789.width, st7789.height, nullptr); constexpr float RADIUS = WIDTH / 2; @@ -37,23 +39,26 @@ Pen from_hsv(float h, float s, float v) { case 5: r = v; g = p; b = q; break; } - return display.create_pen(r, g, b); + return graphics.create_pen(r, g, b); } int main() { - display.set_backlight(255); + st7789.set_backlight(255); uint32_t steps = 70; float angle_step = 0.5f; + Pen BLACK = graphics.create_pen(0, 0, 0); + Pen WHITE = graphics.create_pen(255, 255, 255); + while(1) { absolute_time_t at = get_absolute_time(); uint64_t t = to_us_since_boot(at) / 100000; float angle = (t % 360) * M_PI / 180.0f; - display.set_pen(0, 0, 0); - display.clear(); - display.set_pen(255, 255, 255); + graphics.set_pen(BLACK); + graphics.clear(); + graphics.set_pen(WHITE); for(auto step = 0u; step < steps; step++) { auto distance = RADIUS / steps * step; @@ -68,11 +73,11 @@ int main() { auto p = from_hsv((t / 10.0f) + distance / 120.0f, 1.0, 1.0); - display.set_pen(p); - display.circle(Point(x, y), l); + graphics.set_pen(p); + graphics.circle(Point(x, y), l); } - display.update(); + st7789.update(&graphics); sleep_ms(10); } } \ No newline at end of file diff --git a/examples/pico_display/CMakeLists.txt b/examples/pico_display/CMakeLists.txt index 1e97ebd3..d6f1847a 100644 --- a/examples/pico_display/CMakeLists.txt +++ b/examples/pico_display/CMakeLists.txt @@ -1,11 +1,11 @@ add_executable( pico_display_demo - demo.cpp + pico_display_demo.cpp image_data.cpp ) # Pull in pico libraries that we need -target_link_libraries(pico_display_demo pico_stdlib hardware_spi hardware_pwm hardware_dma rgbled pico_display generic_st7789) +target_link_libraries(pico_display_demo pico_stdlib hardware_spi hardware_pwm hardware_dma rgbled pico_display pico_graphics st7789) # create map/bin/hex file etc. pico_add_extra_outputs(pico_display_demo) \ No newline at end of file diff --git a/examples/pico_display/demo.cpp b/examples/pico_display/pico_display_demo.cpp similarity index 56% rename from examples/pico_display/demo.cpp rename to examples/pico_display/pico_display_demo.cpp index 85c31082..6cc55122 100644 --- a/examples/pico_display/demo.cpp +++ b/examples/pico_display/pico_display_demo.cpp @@ -4,22 +4,20 @@ #include #include "pico_display.hpp" -#include "generic_st7789.hpp" +#include "drivers/st7789/st7789.hpp" +#include "libraries/pico_graphics/pico_graphics.hpp" #include "rgbled.hpp" using namespace pimoroni; -const bool ROTATE_180 = false; - -// Swap WIDTH and HEIGHT to rotate 90 degrees -ST7789Generic pico_display(PicoDisplay::WIDTH, PicoDisplay::HEIGHT); +ST7789 st7789(PicoDisplay::WIDTH, PicoDisplay::HEIGHT, ROTATE_0, false, get_spi_pins(BG_SPI_FRONT)); +PicoGraphics_PenRGB332 graphics(st7789.width, st7789.height, nullptr); RGBLED led(PicoDisplay::LED_R, PicoDisplay::LED_G, PicoDisplay::LED_B); int main() { - pico_display.configure_display(ROTATE_180); - pico_display.set_backlight(100); + st7789.set_backlight(100); struct pt { float x; @@ -38,25 +36,30 @@ int main() { shape.r = (rand() % 10) + 3; shape.dx = float(rand() % 255) / 128.0f; shape.dy = float(rand() % 255) / 128.0f; - shape.pen = pico_display.create_pen(rand() % 255, rand() % 255, rand() % 255); + shape.pen = graphics.create_pen(rand() % 255, rand() % 255, rand() % 255); shapes.push_back(shape); } uint32_t i = 0; + Pen BG = graphics.create_pen(120, 40, 60); + Pen YELLOW = graphics.create_pen(255, 255, 0); + Pen TEAL = graphics.create_pen(0, 255, 255); + Pen WHITE = graphics.create_pen(255, 255, 255); + while(true) { - pico_display.set_pen(120, 40, 60); - pico_display.clear(); + graphics.set_pen(BG); + graphics.clear(); for(auto &shape : shapes) { shape.x += shape.dx; shape.y += shape.dy; if(shape.x < 0) shape.dx *= -1; - if(shape.x >= pico_display.bounds.w) shape.dx *= -1; + if(shape.x >= graphics.bounds.w) shape.dx *= -1; if(shape.y < 0) shape.dy *= -1; - if(shape.y >= pico_display.bounds.h) shape.dy *= -1; + if(shape.y >= graphics.bounds.h) shape.dy *= -1; - pico_display.set_pen(shape.pen); - pico_display.circle(Point(shape.x, shape.y), shape.r); + graphics.set_pen(shape.pen); + graphics.circle(Point(shape.x, shape.y), shape.r); } float led_step = fmod(i / 20.0f, M_PI * 2.0f); @@ -72,17 +75,17 @@ int main() { poly.push_back(Point(50, 85)); poly.push_back(Point(30, 45)); - pico_display.set_pen(255, 255, 0); + graphics.set_pen(YELLOW); //pico_display.pixel(Point(0, 0)); - pico_display.polygon(poly); + graphics.polygon(poly); - pico_display.set_pen(0, 255, 255); - pico_display.triangle(Point(50, 50), Point(130, 80), Point(80, 110)); + graphics.set_pen(TEAL); + graphics.triangle(Point(50, 50), Point(130, 80), Point(80, 110)); - pico_display.set_pen(255, 255, 255); - pico_display.line(Point(50, 50), Point(120, 80)); - pico_display.line(Point(20, 20), Point(120, 20)); - pico_display.line(Point(20, 20), Point(20, 120)); + graphics.set_pen(WHITE); + graphics.line(Point(50, 50), Point(120, 80)); + graphics.line(Point(20, 20), Point(120, 20)); + graphics.line(Point(20, 20), Point(20, 120)); for(int r = 0; r < 30; r++) { for(int j = 0; j < 10; j++) { @@ -91,12 +94,12 @@ int main() { rads += (float(j) / 100.0f); float cx = sin(rads) * 300.0f; float cy = cos(rads) * 300.0f; - pico_display.line(Point(120, 67), Point(cx + 120, cy + 67)); + graphics.line(Point(120, 67), Point(cx + 120, cy + 67)); } } // update screen - pico_display.update(); + st7789.update(&graphics); sleep_ms(1000 / 60); i++; } diff --git a/examples/pico_display_2/CMakeLists.txt b/examples/pico_display_2/CMakeLists.txt index 0ceb13d7..0dfd3438 100644 --- a/examples/pico_display_2/CMakeLists.txt +++ b/examples/pico_display_2/CMakeLists.txt @@ -2,11 +2,11 @@ set(OUTPUT_NAME pico_display2_demo) add_executable( ${OUTPUT_NAME} - demo.cpp + pico_display_2_demo.cpp ) # Pull in pico libraries that we need -target_link_libraries(${OUTPUT_NAME} pico_stdlib hardware_spi hardware_pwm hardware_dma rgbled button pico_display_2 generic_st7789) +target_link_libraries(${OUTPUT_NAME} pico_stdlib hardware_spi hardware_pwm hardware_dma rgbled button pico_display_2 st7789 pico_graphics) # create map/bin/hex file etc. pico_add_extra_outputs(${OUTPUT_NAME}) \ No newline at end of file diff --git a/examples/pico_display_2/demo.cpp b/examples/pico_display_2/pico_display_2_demo.cpp similarity index 69% rename from examples/pico_display_2/demo.cpp rename to examples/pico_display_2/pico_display_2_demo.cpp index f1a06a38..ad9d9bdf 100644 --- a/examples/pico_display_2/demo.cpp +++ b/examples/pico_display_2/pico_display_2_demo.cpp @@ -3,17 +3,16 @@ #include #include -#include "pico_display_2.hpp" -#include "generic_st7789.hpp" +#include "libraries/pico_display_2/pico_display_2.hpp" +#include "drivers/st7789/st7789.hpp" +#include "libraries/pico_graphics/pico_graphics.hpp" #include "rgbled.hpp" #include "button.hpp" using namespace pimoroni; -const bool ROTATE_180 = false; - -// Swap WIDTH and HEIGHT to rotate 90 degrees -ST7789Generic pico_display(240, 240); +ST7789 st7789(320, 240, ROTATE_0, false, get_spi_pins(BG_SPI_FRONT)); +PicoGraphics_PenRGB332 graphics(st7789.width, st7789.height, nullptr); RGBLED led(PicoDisplay2::LED_R, PicoDisplay2::LED_G, PicoDisplay2::LED_B); @@ -43,8 +42,7 @@ void from_hsv(float h, float s, float v, uint8_t &r, uint8_t &g, uint8_t &b) { } int main() { - pico_display.configure_display(ROTATE_180); - pico_display.set_backlight(255); + st7789.set_backlight(255); struct pt { float x; @@ -58,17 +56,20 @@ int main() { std::vector shapes; for(int i = 0; i < 100; i++) { pt shape; - shape.x = rand() % pico_display.bounds.w; - shape.y = rand() % pico_display.bounds.h; + shape.x = rand() % graphics.bounds.w; + shape.y = rand() % graphics.bounds.h; shape.r = (rand() % 10) + 3; shape.dx = float(rand() % 255) / 64.0f; shape.dy = float(rand() % 255) / 64.0f; - shape.pen = pico_display.create_pen(rand() % 255, rand() % 255, rand() % 255); + shape.pen = graphics.create_pen(rand() % 255, rand() % 255, rand() % 255); shapes.push_back(shape); } Point text_location(0, 0); + Pen BG = graphics.create_pen(120, 40, 60); + Pen WHITE = graphics.create_pen(255, 255, 255); + while(true) { if(button_a.raw()) text_location.x -= 1; if(button_b.raw()) text_location.x += 1; @@ -76,8 +77,8 @@ int main() { if(button_x.raw()) text_location.y -= 1; if(button_y.raw()) text_location.y += 1; - pico_display.set_pen(120, 40, 60); - pico_display.clear(); + graphics.set_pen(BG); + graphics.clear(); for(auto &shape : shapes) { shape.x += shape.dx; @@ -86,21 +87,21 @@ int main() { shape.dx *= -1; shape.x = shape.r; } - if((shape.x + shape.r) >= pico_display.bounds.w) { + if((shape.x + shape.r) >= graphics.bounds.w) { shape.dx *= -1; - shape.x = pico_display.bounds.w - shape.r; + shape.x = graphics.bounds.w - shape.r; } if((shape.y - shape.r) < 0) { shape.dy *= -1; shape.y = shape.r; } - if((shape.y + shape.r) >= pico_display.bounds.h) { + if((shape.y + shape.r) >= graphics.bounds.h) { shape.dy *= -1; - shape.y = pico_display.bounds.h - shape.r; + shape.y = graphics.bounds.h - shape.r; } - pico_display.set_pen(shape.pen); - pico_display.circle(Point(shape.x, shape.y), shape.r); + graphics.set_pen(shape.pen); + graphics.circle(Point(shape.x, shape.y), shape.r); } @@ -112,11 +113,11 @@ int main() { led.set_rgb(r, g, b); - pico_display.set_pen(255, 255, 255); - pico_display.text("Hello World", text_location, 320); + graphics.set_pen(WHITE); + graphics.text("Hello World", text_location, 320); // update screen - pico_display.update(); + st7789.update(&graphics); } return 0; diff --git a/examples/pico_enc_explorer/CMakeLists.txt b/examples/pico_enc_explorer/CMakeLists.txt index a082bef9..0a6b4195 100644 --- a/examples/pico_enc_explorer/CMakeLists.txt +++ b/examples/pico_enc_explorer/CMakeLists.txt @@ -2,11 +2,11 @@ set(OUTPUT_NAME encoder_explorer) add_executable( ${OUTPUT_NAME} - demo.cpp + pico_enc_explorer.cpp ) # Pull in pico libraries that we need -target_link_libraries(${OUTPUT_NAME} pico_stdlib breakout_encoder pico_explorer) +target_link_libraries(${OUTPUT_NAME} pico_stdlib breakout_encoder pico_explorer pico_graphics st7789) # create map/bin/hex file etc. pico_add_extra_outputs(${OUTPUT_NAME}) diff --git a/examples/pico_enc_explorer/demo.cpp b/examples/pico_enc_explorer/pico_enc_explorer.cpp similarity index 71% rename from examples/pico_enc_explorer/demo.cpp rename to examples/pico_enc_explorer/pico_enc_explorer.cpp index 75250a30..d2e79507 100644 --- a/examples/pico_enc_explorer/demo.cpp +++ b/examples/pico_enc_explorer/pico_enc_explorer.cpp @@ -6,11 +6,18 @@ #include "pico_explorer.hpp" #include "breakout_encoder.hpp" +#include "drivers/st7789/st7789.hpp" +#include "libraries/pico_graphics/pico_graphics.hpp" using namespace pimoroni; -uint16_t buffer[PicoExplorer::WIDTH * PicoExplorer::HEIGHT]; -PicoExplorer pico_explorer(buffer); +ST7789 st7789(PicoExplorer::WIDTH, PicoExplorer::HEIGHT, ROTATE_0, false, get_spi_pins(BG_SPI_FRONT)); +PicoGraphics_PenRGB332 graphics(st7789.width, st7789.height, nullptr); + +Pen BLACK = graphics.create_pen(0, 0, 0); +Pen RED = graphics.create_pen(255, 0, 0); +Pen GREEN = graphics.create_pen(0, 255, 0); +Pen BLUE = graphics.create_pen(0, 0, 255); static const uint8_t STEPS_PER_REV = 24; @@ -44,47 +51,49 @@ void count_changed(int16_t count) { from_hsv(h, 1.0f, 1.0f, r, g, b); enc.set_led(r, g, b); - pico_explorer.set_pen(0, 0, 0); - pico_explorer.clear(); + graphics.set_pen(BLACK); + graphics.clear(); { - pico_explorer.set_pen(255, 0, 0); + graphics.set_pen(RED); std::ostringstream ss; ss << "R = "; ss << (int)r; std::string s(ss.str()); - pico_explorer.text(s, Point(10, 10), 220, 6); + graphics.text(s, Point(10, 10), 220, 6); } { - pico_explorer.set_pen(0, 255, 0); + graphics.set_pen(GREEN); std::ostringstream ss; ss << "G = "; ss << (int)g; std::string s(ss.str()); - pico_explorer.text(s, Point(10, 70), 220, 6); + graphics.text(s, Point(10, 70), 220, 6); } { - pico_explorer.set_pen(0, 0, 255); + graphics.set_pen(BLUE); std::ostringstream ss; ss << "B = "; ss << (int)b; std::string s(ss.str()); - pico_explorer.text(s, Point(10, 130), 220, 6); + graphics.text(s, Point(10, 130), 220, 6); } { - pico_explorer.set_pen(r, g, b); + // Shouldn't really use create_pen in-line. + // In default (RGB332) palette mode this will lookup the nearest 8-bit colour + graphics.set_pen(graphics.create_pen(r, g, b)); std::ostringstream ss; ss << "#"; ss << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << (int)r; ss << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << (int)g; ss << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << (int)b; std::string s(ss.str()); - pico_explorer.text(s, Point(10, 190), 220, 5); + graphics.text(s, Point(10, 190), 220, 5); } - pico_explorer.update(); + st7789.update(&graphics); } int main() { @@ -93,9 +102,6 @@ int main() { stdio_init_all(); - pico_explorer.init(); - pico_explorer.update(); - int16_t count = 0; if(enc.init()) { printf("Encoder found...\n"); diff --git a/examples/pico_explorer/CMakeLists.txt b/examples/pico_explorer/CMakeLists.txt index 02238a31..0f44144a 100644 --- a/examples/pico_explorer/CMakeLists.txt +++ b/examples/pico_explorer/CMakeLists.txt @@ -1,21 +1,21 @@ add_executable( - explorer - demo.cpp + pico_explorer_demo + pico_explorer_demo.cpp ) -add_resource(explorer fox.tga) +add_resource(pico_explorer_demo fox.tga) # Pull in pico libraries that we need -target_link_libraries(explorer pico_stdlib pico_explorer msa301) +target_link_libraries(pico_explorer_demo pico_stdlib pico_explorer msa301 pico_graphics st7789 button motor analog) # create map/bin/hex file etc. -pico_add_extra_outputs(explorer) +pico_add_extra_outputs(pico_explorer_demo) add_executable( text_demo text_demo.cpp ) -target_link_libraries(text_demo pico_stdlib pico_explorer msa301) +target_link_libraries(text_demo pico_stdlib pico_explorer msa301 pico_graphics st7789) pico_add_extra_outputs(text_demo) \ No newline at end of file diff --git a/examples/pico_explorer/demo.cpp b/examples/pico_explorer/demo.cpp deleted file mode 100644 index 6e7cbbf7..00000000 --- a/examples/pico_explorer/demo.cpp +++ /dev/null @@ -1,222 +0,0 @@ -#include -#include -#include -#include - -#include "pico_explorer.hpp" -#include "msa301.hpp" - -using namespace pimoroni; - -extern unsigned char _binary_fox_tga_start[]; - -uint16_t buffer[PicoExplorer::WIDTH * PicoExplorer::HEIGHT]; -PicoExplorer pico_explorer(buffer); -MSA301 msa301; - -uint8_t arrow[] = { - 0b00010000, - 0b00110000, - 0b01110000, - 0b11111111, - 0b11111111, - 0b01110000, - 0b00110000, - 0b00010000 - }; - -uint8_t tick[] = { - 0b00000000, - 0b00000010, - 0b00000111, - 0b01001110, - 0b11111100, - 0b00111000, - 0b00010000, - 0b00000000, - }; - -/* -void sprite(uint8_t *p, int x, int y, bool flip, uint16_t c) { - for(int ay = 0; ay < 8; ay++) { - uint8_t sl = p[ay]; - for(int ax = 0; ax < 8; ax++) { - if(flip) { - if((0b10000000 >> ax) & sl) { - pixel(ax + x, ay + y, c); - } - }else{ - if((0b1 << ax) & sl) { - pixel(ax + x, ay + y, c); - } - } - } - } -}*/ - -int main() { - pico_explorer.init(); - msa301.init(); - - struct pt { - float x; - float y; - uint8_t r; - float dx; - float dy; - uint16_t pen; - }; - - std::vector shapes; - for(int i = 0; i < 100; i++) { - pt shape; - shape.x = rand() % 240; - shape.y = rand() % 135; - shape.r = (rand() % 10) + 3; - shape.dx = float(rand() % 255) / 128.0f; - shape.dy = float(rand() % 255) / 128.0f; - shape.pen = pico_explorer.create_pen(rand() % 255, rand() % 255, rand() % 255); - shapes.push_back(shape); - } - - pico_explorer.set_audio_pin(pico_explorer.GP0); - - uint32_t i = 0; - while(true) { - pico_explorer.set_pen(120, 40, 60); - pico_explorer.clear(); - - for(auto &shape : shapes) { - shape.x += shape.dx; - shape.y += shape.dy; - if(shape.x < 0) shape.dx *= -1; - if(shape.x >= pico_explorer.bounds.w) shape.dx *= -1; - if(shape.y < 0) shape.dy *= -1; - if(shape.y >= pico_explorer.bounds.h) shape.dy *= -1; - - pico_explorer.set_pen(shape.pen); - pico_explorer.circle(Point(shape.x, shape.y), shape.r); - } - - float rv = pico_explorer.get_adc(pico_explorer.ADC0); - pico_explorer.set_pen(255, 255, 255); - pico_explorer.circle(Point(rv * 140 + 50, 110), 20); - pico_explorer.set_pen(rv * 255, 0, 0); - pico_explorer.circle(Point(rv * 140 + 50, 110), 15); - - float gv = pico_explorer.get_adc(pico_explorer.ADC1); - pico_explorer.set_pen(255, 255, 255); - pico_explorer.circle(Point(gv * 140 + 50, 160), 20); - pico_explorer.set_pen(0, gv * 255, 0); - pico_explorer.circle(Point(gv * 140 + 50, 160), 15); - - float bv = pico_explorer.get_adc(pico_explorer.ADC2); - pico_explorer.set_pen(255, 255, 255); - pico_explorer.circle(Point(bv * 140 + 50, 210), 20); - pico_explorer.set_pen(0, 0, bv * 255); - pico_explorer.circle(Point(bv * 140 + 50, 210), 15); - - pico_explorer.set_motor(pico_explorer.MOTOR1, pico_explorer.FORWARD, bv); - pico_explorer.set_motor(pico_explorer.MOTOR2, pico_explorer.FORWARD, rv); - - pico_explorer.set_tone(440, 0.5); - - if(pico_explorer.is_pressed(pico_explorer.A)) { - pico_explorer.set_pen(255, 255, 255); - pico_explorer.character('A', Point(120, 180), 5); - } - - if(pico_explorer.is_pressed(pico_explorer.B)) { - pico_explorer.set_pen(255, 255, 255); - pico_explorer.character('B', Point(120, 180), 5); - } - - if(pico_explorer.is_pressed(pico_explorer.X)) { - pico_explorer.set_pen(255, 255, 255); - pico_explorer.character('X', Point(120, 180), 5); - } - - if(pico_explorer.is_pressed(pico_explorer.Y)) { - pico_explorer.set_pen(255, 255, 255); - pico_explorer.character('Y', Point(120, 180), 5); - } - - float tyoff = cos(i / 20.0f) * 50.0f - 50.0f; - Rect text_box(10, 10, 150, 150); - pico_explorer.set_pen(55, 65, 75); - pico_explorer.rectangle(text_box); - text_box.deflate(10); - pico_explorer.set_clip(text_box); - pico_explorer.set_pen(255, 255, 255); - pico_explorer.text("This is a test of some text data that should wrap nicely onto multiple lines which is dead useful like.", Point(text_box.x, text_box.y + tyoff), 100); - - float xoff = sin(i / 20.0f) * 50.0f; - xoff += 120 - (81 / 2); - float yoff = cos(i / 20.0f) * 50.0f; - yoff += 120 - (68 / 2); - for(int y = 0; y < 68; y++) { - // uint16_t *dest = pico_explorer.frame_buffer + (y * 240); - uint8_t *src = _binary_fox_tga_start + 18 + (y * 81 * 3); - for(int x = 0; x < 81; x++) { - uint8_t b = *src++; - uint8_t g = *src++; - uint8_t r = *src++; - - pico_explorer.set_pen(r, g, b); - pico_explorer.pixel(Point(x + xoff, 68 - y + yoff)); - } - } - - pico_explorer.remove_clip(); - - pico_explorer.set_pen(255, 255, 255); - pico_explorer.text("x: " + std::to_string(int(msa301.get_axis(msa301.X, 16) * 100)), Point(10, 190), 100); - pico_explorer.text("y: " + std::to_string(int(msa301.get_axis(msa301.Y, 16) * 100)), Point(10, 205), 100); - pico_explorer.text("z: " + std::to_string(int(msa301.get_axis(msa301.Z, 16) * 100)), Point(10, 220), 100); - - uint16_t xpos = (msa301.get_axis(msa301.X, 16) * 120) + 120; - uint16_t ypos = (msa301.get_axis(msa301.Z, 16) * 120) + 120; - pico_explorer.set_pen(255, 255, 255); - pico_explorer.circle(Point(xpos, ypos), 20); - pico_explorer.set_pen(255, 0, 255); - pico_explorer.circle(Point(xpos, ypos), 15); - - -/* - if(pico_display.is_pressed(pico_display.A)) { - pico_display.rectangle(0, 0, 18, 18); - //sprite(tick, 5, 5, true, green); - }else{ - //sprite(arrow, 10 + bounce, 10, true, white); - } - - if(pico_display.is_pressed(pico_display.B)) { - pico_display.rectangle(0, 49, 18, 18); - //sprite(tick, 5, 54, true, green); - }else{ - //sprite(arrow, 10 - bounce, 50, true, white); - } - - - if(pico_display.is_pressed(pico_display.X)) { - pico_display.rectangle(102, 0, 18, 18); - //sprite(tick, 107, 5, true, green); - }else{ - //sprite(arrow, 102 - bounce, 10, false, white); - } - - if(pico_display.is_pressed(pico_display.Y)) { - pico_display.rectangle(102, 49, 18, 18); - //sprite(tick, 107, 54, true, green); - }else{ - //sprite(arrow, 102 + bounce, 50, false, white); - } -*/ - // update screen - pico_explorer.update(); - - i++; - } - - return 0; -} diff --git a/examples/pico_explorer/pico_explorer_demo.cpp b/examples/pico_explorer/pico_explorer_demo.cpp new file mode 100644 index 00000000..963d021f --- /dev/null +++ b/examples/pico_explorer/pico_explorer_demo.cpp @@ -0,0 +1,215 @@ +#include +#include +#include +#include + +#include "pico_explorer.hpp" +#include "drivers/st7789/st7789.hpp" +#include "libraries/pico_graphics/pico_graphics.hpp" +#include "button.hpp" +#include "motor.hpp" +#include "msa301.hpp" +#include "analog.hpp" + +using namespace pimoroni; +using namespace motor; + +extern unsigned char _binary_fox_tga_start[]; + +ST7789 st7789(PicoExplorer::WIDTH, PicoExplorer::HEIGHT, ROTATE_0, false, get_spi_pins(BG_SPI_FRONT)); +PicoGraphics_PenRGB332 graphics(st7789.width, st7789.height, nullptr); + +// Buttons +Button button_a(PicoExplorer::A); +Button button_b(PicoExplorer::B); +Button button_x(PicoExplorer::X); +Button button_y(PicoExplorer::Y); + +Motor motor1(PicoExplorer::MOTOR1_PINS); +Motor motor2(PicoExplorer::MOTOR2_PINS); + +Analog adc0(PicoExplorer::ADC0_PIN); +Analog adc1(PicoExplorer::ADC1_PIN); +Analog adc2(PicoExplorer::ADC2_PIN); + +MSA301 msa301; + +uint8_t arrow[] = { + 0b00010000, + 0b00110000, + 0b01110000, + 0b11111111, + 0b11111111, + 0b01110000, + 0b00110000, + 0b00010000 +}; + +uint8_t tick[] = { + 0b00000000, + 0b00000010, + 0b00000111, + 0b01001110, + 0b11111100, + 0b00111000, + 0b00010000, + 0b00000000, +}; + +/* +void sprite(uint8_t *p, int x, int y, bool flip, uint16_t c) { + for(int ay = 0; ay < 8; ay++) { + uint8_t sl = p[ay]; + for(int ax = 0; ax < 8; ax++) { + if(flip) { + if((0b10000000 >> ax) & sl) { + pixel(ax + x, ay + y, c); + } + }else{ + if((0b1 << ax) & sl) { + pixel(ax + x, ay + y, c); + } + } + } + } +}*/ + +int main() { + msa301.init(); + motor1.init(); + motor2.init(); + + struct pt { + float x; + float y; + uint8_t r; + float dx; + float dy; + uint16_t pen; + }; + + std::vector shapes; + for(int i = 0; i < 100; i++) { + pt shape; + shape.x = rand() % 240; + shape.y = rand() % 135; + shape.r = (rand() % 10) + 3; + shape.dx = float(rand() % 255) / 128.0f; + shape.dy = float(rand() % 255) / 128.0f; + shape.pen = graphics.create_pen(rand() % 255, rand() % 255, rand() % 255); + shapes.push_back(shape); + } + + Pen BG = graphics.create_pen(120, 40, 60); + Pen WHITE = graphics.create_pen(255, 255, 255); + Pen BOX = graphics.create_pen(55, 65, 75); + Pen PURPLE = graphics.create_pen(255, 0, 255); + + uint32_t i = 0; + while(true) { + graphics.set_pen(BG); + graphics.clear(); + + for(auto &shape : shapes) { + shape.x += shape.dx; + shape.y += shape.dy; + if(shape.x < 0) shape.dx *= -1; + if(shape.x >= graphics.bounds.w) shape.dx *= -1; + if(shape.y < 0) shape.dy *= -1; + if(shape.y >= graphics.bounds.h) shape.dy *= -1; + + graphics.set_pen(shape.pen); + graphics.circle(Point(shape.x, shape.y), shape.r); + } + + float rv = adc0.read_voltage() / 3.3f; + graphics.set_pen(WHITE); + graphics.circle(Point(rv * 140 + 50, 110), 20); + graphics.set_pen(graphics.create_pen(rv * 255, 0, 0)); + graphics.circle(Point(rv * 140 + 50, 110), 15); + + float gv = adc1.read_voltage() / 3.3f; + graphics.set_pen(WHITE); + graphics.circle(Point(gv * 140 + 50, 160), 20); + graphics.set_pen(graphics.create_pen(0, gv * 255, 0)); + graphics.circle(Point(gv * 140 + 50, 160), 15); + + float bv = adc2.read_voltage() / 3.3f; + graphics.set_pen(WHITE); + graphics.circle(Point(bv * 140 + 50, 210), 20); + graphics.set_pen(graphics.create_pen(0, 0, bv * 255)); + graphics.circle(Point(bv * 140 + 50, 210), 15); + + motor1.speed(bv); + motor2.speed(rv); + + // TODO make this work + // display.set_tone(440, 0.5); + + if(button_a.read()) { + graphics.set_pen(WHITE); + graphics.character('A', Point(120, 180), 5); + } + + if(button_b.read()) { + graphics.set_pen(WHITE); + graphics.character('B', Point(120, 180), 5); + } + + if(button_x.read()) { + graphics.set_pen(WHITE); + graphics.character('X', Point(120, 180), 5); + } + + if(button_y.read()) { + graphics.set_pen(WHITE); + graphics.character('Y', Point(120, 180), 5); + } + + float tyoff = cos(i / 20.0f) * 50.0f - 50.0f; + Rect text_box(10, 10, 150, 150); + graphics.set_pen(BOX); + graphics.rectangle(text_box); + text_box.deflate(10); + graphics.set_clip(text_box); + graphics.set_pen(WHITE); + graphics.text("This is a test of some text data that should wrap nicely onto multiple lines which is dead useful like.", Point(text_box.x, text_box.y + tyoff), 100); + + float xoff = sin(i / 20.0f) * 50.0f; + xoff += 120 - (81 / 2); + float yoff = cos(i / 20.0f) * 50.0f; + yoff += 120 - (68 / 2); + for(int y = 0; y < 68; y++) { + uint8_t *src = _binary_fox_tga_start + 18 + (y * 81 * 3); + for(int x = 0; x < 81; x++) { + uint8_t b = *src++; + uint8_t g = *src++; + uint8_t r = *src++; + + graphics.set_pen(graphics.create_pen(r, g, b)); + graphics.pixel(Point(x + xoff, 68 - y + yoff)); + } + } + + graphics.remove_clip(); + + graphics.set_pen(WHITE); + graphics.text("x: " + std::to_string(int(msa301.get_axis(msa301.X, 16) * 100)), Point(10, 190), 100); + graphics.text("y: " + std::to_string(int(msa301.get_axis(msa301.Y, 16) * 100)), Point(10, 205), 100); + graphics.text("z: " + std::to_string(int(msa301.get_axis(msa301.Z, 16) * 100)), Point(10, 220), 100); + + uint16_t xpos = (msa301.get_axis(msa301.X, 16) * 120) + 120; + uint16_t ypos = (msa301.get_axis(msa301.Z, 16) * 120) + 120; + graphics.set_pen(WHITE); + graphics.circle(Point(xpos, ypos), 20); + graphics.set_pen(PURPLE); + graphics.circle(Point(xpos, ypos), 15); + + // update screen + st7789.update(&graphics); + + i++; + } + + return 0; +} diff --git a/examples/pico_explorer/text_demo.cpp b/examples/pico_explorer/text_demo.cpp index 971b8883..e5f63da7 100644 --- a/examples/pico_explorer/text_demo.cpp +++ b/examples/pico_explorer/text_demo.cpp @@ -4,6 +4,9 @@ #include #include "pico_explorer.hpp" +#include "drivers/st7789/st7789.hpp" +#include "libraries/pico_graphics/pico_graphics.hpp" + #include "font6_data.hpp" #include "font8_data.hpp" #include "msa301.hpp" @@ -12,29 +15,32 @@ using namespace pimoroni; extern unsigned char _binary_fox_tga_start[]; -uint16_t buffer[PicoExplorer::WIDTH * PicoExplorer::HEIGHT]; -PicoExplorer pico_explorer(buffer); +ST7789 st7789(PicoExplorer::WIDTH, PicoExplorer::HEIGHT, ROTATE_0, false, get_spi_pins(BG_SPI_FRONT)); +PicoGraphics_PenRGB332 graphics(st7789.width, st7789.height, nullptr); + MSA301 msa301; int main() { - pico_explorer.init(); - pico_explorer.set_font(&font8); + graphics.set_font(&font8); msa301.init(); + Pen BG = graphics.create_pen(120, 40, 60); + Pen WHITE = graphics.create_pen(255, 255, 255); + uint32_t i = 0; while(true) { - pico_explorer.set_pen(120, 40, 60); - pico_explorer.clear(); + graphics.set_pen(BG); + graphics.clear(); - pico_explorer.set_pen(255, 255, 255); - pico_explorer.set_font(&font6); - pico_explorer.text("6x6: The quick, brown fox jumps over the lazy dog! UPPER. lower.", Point(10, 10), 220); - pico_explorer.text("0123456789 !$%^&*()", Point(10, 70), 220); - pico_explorer.set_font(&font8); - pico_explorer.text("6x8: The quick, brown fox jumps over the lazy dog! UPPER. lower.", Point(10, 120), 220); - pico_explorer.text("0123456789 !$%^&*()", Point(10, 180), 220); + graphics.set_pen(WHITE); + graphics.set_font(&font6); + graphics.text("6x6: The quick, brown fox jumps over the lazy dog! UPPER. lower.", Point(10, 10), 220); + graphics.text("0123456789 !$%^&*()", Point(10, 70), 220); + graphics.set_font(&font8); + graphics.text("6x8: The quick, brown fox jumps over the lazy dog! UPPER. lower.", Point(10, 120), 220); + graphics.text("0123456789 !$%^&*()", Point(10, 180), 220); - pico_explorer.update(); + st7789.update(&graphics); i++; } diff --git a/examples/pico_explorer_encoder/CMakeLists.txt b/examples/pico_explorer_encoder/CMakeLists.txt index cab9a39f..dba74a85 100644 --- a/examples/pico_explorer_encoder/CMakeLists.txt +++ b/examples/pico_explorer_encoder/CMakeLists.txt @@ -1,12 +1,12 @@ add_executable( - explorerencoder - demo.cpp + pico_explorer_encoder + pico_explorer_encoder.cpp ) -pico_generate_pio_header(explorerencoder ${CMAKE_CURRENT_LIST_DIR}/quadrature_out.pio) +pico_generate_pio_header(pico_explorer_encoder ${CMAKE_CURRENT_LIST_DIR}/quadrature_out.pio) # Pull in pico libraries that we need -target_link_libraries(explorerencoder pico_stdlib pico_explorer encoder) +target_link_libraries(pico_explorer_encoder pico_stdlib pico_explorer encoder button motor pico_graphics st7789) # create map/bin/hex file etc. -pico_add_extra_outputs(explorerencoder) \ No newline at end of file +pico_add_extra_outputs(pico_explorer_encoder) \ No newline at end of file diff --git a/examples/pico_explorer_encoder/demo.cpp b/examples/pico_explorer_encoder/pico_explorer_encoder.cpp similarity index 81% rename from examples/pico_explorer_encoder/demo.cpp rename to examples/pico_explorer_encoder/pico_explorer_encoder.cpp index 98d4a614..61cafc02 100644 --- a/examples/pico_explorer_encoder/demo.cpp +++ b/examples/pico_explorer_encoder/pico_explorer_encoder.cpp @@ -4,6 +4,9 @@ #include "pico/stdlib.h" #include "encoder.hpp" #include "quadrature_out.pio.h" +#include "drivers/st7789/st7789.hpp" +#include "libraries/pico_graphics/pico_graphics.hpp" +#include "button.hpp" /* An interactive demo of how rotary encoders work. @@ -31,6 +34,7 @@ be used by jumping GP0 to GP6 and GP1 to GP7. using namespace pimoroni; using namespace encoder; +using namespace motor; //-------------------------------------------------- // Constants @@ -90,8 +94,15 @@ enum DrawState { //-------------------------------------------------- // Variables //-------------------------------------------------- -uint16_t buffer[PicoExplorer::WIDTH * PicoExplorer::HEIGHT]; -PicoExplorer pico_explorer(buffer); +ST7789 st7789(PicoExplorer::WIDTH, PicoExplorer::HEIGHT, ROTATE_0, false, get_spi_pins(BG_SPI_FRONT)); +PicoGraphics_PenRGB332 graphics(st7789.width, st7789.height, nullptr); + +Button button_a(PicoExplorer::A); +Button button_b(PicoExplorer::B); +Button button_x(PicoExplorer::X); +Button button_y(PicoExplorer::Y); + +Motor motor1(PicoExplorer::MOTOR1_PINS); Encoder enc(pio0, 0, ENCODER_PINS, ENCODER_COMMON_PIN, NORMAL_DIR, COUNTS_PER_REV, COUNT_MICROSTEPS, FREQ_DIVIDER); @@ -166,13 +177,13 @@ uint32_t draw_plot(Point p1, Point p2, volatile bool (&readings)[READINGS_SIZE], switch(draw_state) { case DRAW_TRANSITION: for(uint8_t y = p1.y; y < p2.y; y++) - pico_explorer.pixel(Point(x + p1.x, y)); + graphics.pixel(Point(x + p1.x, y)); break; case DRAW_HIGH: - pico_explorer.pixel(Point(x + p1.x, p1.y)); + graphics.pixel(Point(x + p1.x, p1.y)); break; case DRAW_LOW: - pico_explorer.pixel(Point(x + p1.x, p2.y - 1)); + graphics.pixel(Point(x + p1.x, p2.y - 1)); break; } } @@ -215,11 +226,7 @@ void setup() { gpio_pull_down(ENCODER_SWITCH_PIN); } - pico_explorer.init(); - pico_explorer.set_pen(0); - pico_explorer.clear(); - pico_explorer.update(); - + motor1.init(); enc.init(); bool_pair state = enc.state(); @@ -243,6 +250,7 @@ void setup() { // MAIN //////////////////////////////////////////////////////////////////////////////////////////////////// int main() { + Pen WHITE = graphics.create_pen(255, 255, 255); // Perform the main setup for the demo setup(); @@ -273,18 +281,18 @@ int main() { Encoder::Capture capture = enc.capture(); // Spin Motor 1 either clockwise or counterclockwise depending on if B or Y are pressed - if(pico_explorer.is_pressed(PicoExplorer::B) && !pico_explorer.is_pressed(PicoExplorer::Y)) { - pico_explorer.set_motor(PicoExplorer::MOTOR1, PicoExplorer::FORWARD, 1.0f); + if(button_b.read() && !button_y.read()) { + motor1.speed(1.0f); // Full forward } - else if(pico_explorer.is_pressed(PicoExplorer::Y) && !pico_explorer.is_pressed(PicoExplorer::B)) { - pico_explorer.set_motor(PicoExplorer::MOTOR1, PicoExplorer::REVERSE, 0.2f); + else if(button_y.read() && !button_b.read()) { + motor1.speed(-0.2f); // Reverse } else { - pico_explorer.set_motor(PicoExplorer::MOTOR1, PicoExplorer::STOP); + motor1.speed(0.0f); // Stop } // If A has been pressed, zoom the view out to a min of x1 - if(pico_explorer.is_pressed(PicoExplorer::A)) { + if(button_a.read()) { if(!button_latch_a) { button_latch_a = true; current_zoom_level = std::max(current_zoom_level / 2, 1); @@ -295,7 +303,7 @@ int main() { } // If X has been pressed, zoom the view in to the max of x512 - if(pico_explorer.is_pressed(PicoExplorer::X)) { + if(button_x.read()) { if(!button_latch_x) { button_latch_x = true; current_zoom_level = std::min(current_zoom_level * 2, 512); @@ -308,16 +316,16 @@ int main() { //-------------------------------------------------- // Draw the encoder readings to the screen as a signal plot - pico_explorer.set_pen(0, 0, 0); - pico_explorer.clear(); + graphics.set_pen(graphics.create_pen(0, 0, 0)); + graphics.clear(); drawing_to_screen = true; - pico_explorer.set_pen(255, 255, 0); + graphics.set_pen(graphics.create_pen(255, 255, 0)); uint32_t local_pos = next_reading_index; uint32_t alignment_offset = draw_plot(Point(0, 10), Point(PicoExplorer::WIDTH, 10 + 50), enc_a_readings, local_pos, current_zoom_level > EDGE_ALIGN_ABOVE_ZOOM); - pico_explorer.set_pen(0, 255, 255); + graphics.set_pen(graphics.create_pen(0, 255, 255)); draw_plot(Point(0, 80), Point(PicoExplorer::WIDTH, 80 + 50), enc_b_readings, (local_pos + (READINGS_SIZE - alignment_offset)) % READINGS_SIZE, false); // Copy values that may have been stored in the scratch buffers, back into the main buffers @@ -333,49 +341,49 @@ int main() { drawing_to_screen = false; next_scratch_index = 0; - pico_explorer.set_pen(255, 255, 255); - pico_explorer.character('A', Point(5, 10 + 15), 3); - pico_explorer.character('B', Point(5, 80 + 15), 3); + graphics.set_pen(WHITE); + graphics.character('A', Point(5, 10 + 15), 3); + graphics.character('B', Point(5, 80 + 15), 3); if(current_zoom_level < 10) - pico_explorer.text("x" + std::to_string(current_zoom_level), Point(220, 62), 200, 2); + graphics.text("x" + std::to_string(current_zoom_level), Point(220, 62), 200, 2); else if(current_zoom_level < 100) - pico_explorer.text("x" + std::to_string(current_zoom_level), Point(210, 62), 200, 2); + graphics.text("x" + std::to_string(current_zoom_level), Point(210, 62), 200, 2); else - pico_explorer.text("x" + std::to_string(current_zoom_level), Point(200, 62), 200, 2); + graphics.text("x" + std::to_string(current_zoom_level), Point(200, 62), 200, 2); //-------------------------------------------------- // Write out the count, frequency and rpm of the encoder - pico_explorer.set_pen(8, 8, 8); - pico_explorer.rectangle(Rect(0, 140, PicoExplorer::WIDTH, PicoExplorer::HEIGHT - 140)); + graphics.set_pen(graphics.create_pen(8, 8, 8)); + graphics.rectangle(Rect(0, 140, PicoExplorer::WIDTH, PicoExplorer::HEIGHT - 140)); - pico_explorer.set_pen(64, 64, 64); - pico_explorer.rectangle(Rect(0, 140, PicoExplorer::WIDTH, 2)); + graphics.set_pen(graphics.create_pen(64, 64, 64)); + graphics.rectangle(Rect(0, 140, PicoExplorer::WIDTH, 2)); { std::stringstream sstream; sstream << capture.count(); - pico_explorer.set_pen(255, 255, 255); pico_explorer.text("Count:", Point(10, 150), 200, 3); - pico_explorer.set_pen(255, 128, 255); pico_explorer.text(sstream.str(), Point(110, 150), 200, 3); + graphics.set_pen(WHITE); graphics.text("Count:", Point(10, 150), 200, 3); + graphics.set_pen(graphics.create_pen(255, 128, 255)); graphics.text(sstream.str(), Point(110, 150), 200, 3); } { std::stringstream sstream; sstream << std::fixed << std::setprecision(1) << capture.frequency() << "hz"; - pico_explorer.set_pen(255, 255, 255); pico_explorer.text("Freq: ", Point(10, 180), 220, 3); - pico_explorer.set_pen(128, 255, 255); pico_explorer.text(sstream.str(), Point(90, 180), 220, 3); + graphics.set_pen(WHITE); graphics.text("Freq: ", Point(10, 180), 220, 3); + graphics.set_pen(graphics.create_pen(128, 255, 255)); graphics.text(sstream.str(), Point(90, 180), 220, 3); } { std::stringstream sstream; sstream << std::fixed << std::setprecision(1) << capture.revolutions_per_minute(); - pico_explorer.set_pen(255, 255, 255); pico_explorer.text("RPM: ", Point(10, 210), 220, 3); - pico_explorer.set_pen(255, 255, 128); pico_explorer.text(sstream.str(), Point(80, 210), 220, 3); + graphics.set_pen(WHITE); graphics.text("RPM: ", Point(10, 210), 220, 3); + graphics.set_pen(graphics.create_pen(255, 255, 128)); graphics.text(sstream.str(), Point(80, 210), 220, 3); } - pico_explorer.update(); // Refresh the screen + st7789.update(&graphics); // Refresh the screen gpio_put(PICO_DEFAULT_LED_PIN, false); // Show the screen refresh has ended } } diff --git a/examples/pico_pot_explorer/CMakeLists.txt b/examples/pico_pot_explorer/CMakeLists.txt index dc5649d4..433e6096 100644 --- a/examples/pico_pot_explorer/CMakeLists.txt +++ b/examples/pico_pot_explorer/CMakeLists.txt @@ -2,11 +2,11 @@ set(OUTPUT_NAME potentiometer_explorer) add_executable( ${OUTPUT_NAME} - demo.cpp + pico_pot_explorer.cpp ) # Pull in pico libraries that we need -target_link_libraries(${OUTPUT_NAME} pico_stdlib breakout_potentiometer pico_explorer) +target_link_libraries(${OUTPUT_NAME} pico_stdlib breakout_potentiometer pico_explorer pico_graphics st7789) # create map/bin/hex file etc. pico_add_extra_outputs(${OUTPUT_NAME}) diff --git a/examples/pico_pot_explorer/demo.cpp b/examples/pico_pot_explorer/pico_pot_explorer.cpp similarity index 69% rename from examples/pico_pot_explorer/demo.cpp rename to examples/pico_pot_explorer/pico_pot_explorer.cpp index 4455c18c..a375b8f0 100644 --- a/examples/pico_pot_explorer/demo.cpp +++ b/examples/pico_pot_explorer/pico_pot_explorer.cpp @@ -7,11 +7,18 @@ #include "common/pimoroni_i2c.hpp" #include "pico_explorer.hpp" #include "breakout_potentiometer.hpp" +#include "drivers/st7789/st7789.hpp" +#include "libraries/pico_graphics/pico_graphics.hpp" using namespace pimoroni; -uint16_t buffer[PicoExplorer::WIDTH * PicoExplorer::HEIGHT]; -PicoExplorer pico_explorer(buffer); +ST7789 st7789(PicoExplorer::WIDTH, PicoExplorer::HEIGHT, ROTATE_0, false, get_spi_pins(BG_SPI_FRONT)); +PicoGraphics_PenRGB332 graphics(st7789.width, st7789.height, nullptr); + +Pen BLACK = graphics.create_pen(0, 0, 0); +Pen RED = graphics.create_pen(255, 0, 0); +Pen GREEN = graphics.create_pen(0, 255, 0); +Pen BLUE = graphics.create_pen(0, 0, 255); I2C i2c(PICO_EXPLORER); BreakoutPotentiometer pot(&i2c); @@ -43,9 +50,6 @@ int main() { stdio_init_all(); - pico_explorer.init(); - pico_explorer.update(); - printf("Starting...\n"); if(pot.init()) { @@ -62,47 +66,49 @@ int main() { from_hsv(percent, 1.0f, 1.0f, r, g, b); pot.set_led(r, g, b); - pico_explorer.set_pen(0, 0, 0); - pico_explorer.clear(); + graphics.set_pen(BLACK); + graphics.clear(); { - pico_explorer.set_pen(255, 0, 0); + graphics.set_pen(RED); std::ostringstream ss; ss << "R = "; ss << (int)r; std::string s(ss.str()); - pico_explorer.text(s, Point(10, 10), 220, 6); + graphics.text(s, Point(10, 10), 220, 6); } { - pico_explorer.set_pen(0, 255, 0); + graphics.set_pen(GREEN); std::ostringstream ss; ss << "G = "; ss << (int)g; std::string s(ss.str()); - pico_explorer.text(s, Point(10, 70), 220, 6); + graphics.text(s, Point(10, 70), 220, 6); } { - pico_explorer.set_pen(0, 0, 255); + graphics.set_pen(BLUE); std::ostringstream ss; ss << "B = "; ss << (int)b; std::string s(ss.str()); - pico_explorer.text(s, Point(10, 130), 220, 6); + graphics.text(s, Point(10, 130), 220, 6); } { - pico_explorer.set_pen(r, g, b); + // Shouldn't really use create_pen in-line. + // In default (RGB332) palette mode this will lookup the nearest 8-bit colour + graphics.set_pen(graphics.create_pen(r, g, b)); std::ostringstream ss; ss << "#"; ss << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << (int)r; ss << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << (int)g; ss << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << (int)b; std::string s(ss.str()); - pico_explorer.text(s, Point(10, 190), 220, 5); + graphics.text(s, Point(10, 190), 220, 5); } - pico_explorer.update(); + st7789.update(&graphics); } } else { diff --git a/examples/pico_rtc_display/CMakeLists.txt b/examples/pico_rtc_display/CMakeLists.txt index 8f03ce48..df7cba8c 100644 --- a/examples/pico_rtc_display/CMakeLists.txt +++ b/examples/pico_rtc_display/CMakeLists.txt @@ -2,11 +2,11 @@ set(OUTPUT_NAME rtc_display) add_executable( ${OUTPUT_NAME} - demo.cpp + pico_rtc_display.cpp ) # Pull in pico libraries that we need -target_link_libraries(${OUTPUT_NAME} pico_stdlib pico_explorer pico_display breakout_rtc) +target_link_libraries(${OUTPUT_NAME} pico_stdlib pico_explorer pico_display breakout_rtc pico_graphics st7789 button rgbled) # create map/bin/hex file etc. pico_add_extra_outputs(${OUTPUT_NAME}) diff --git a/examples/pico_rtc_display/demo.cpp b/examples/pico_rtc_display/pico_rtc_display.cpp similarity index 74% rename from examples/pico_rtc_display/demo.cpp rename to examples/pico_rtc_display/pico_rtc_display.cpp index 266d70b8..2a3db860 100644 --- a/examples/pico_rtc_display/demo.cpp +++ b/examples/pico_rtc_display/pico_rtc_display.cpp @@ -17,7 +17,7 @@ // To use PicoExplorer rather than PicoDisplay, uncomment the following line // #define USE_PICO_EXPLORER 1 // This: -// - Includes pico_explorer.hpp rather than pico_display.hpp +// - Includes pico_explorer.hpp rather than display.hpp // - Replaces all PicoDisplay references with PicoExplorer // - Leaves out the .set_led() calls in flash_led() #ifdef USE_PICO_EXPLORER @@ -27,6 +27,11 @@ #endif #include "breakout_rtc.hpp" +#include "drivers/st7789/st7789.hpp" +#include "libraries/pico_graphics/pico_graphics.hpp" +#include "drivers/button/button.hpp" +#include "drivers/rgbled/rgbled.hpp" + #define MODE_DISP_CLOCK 0 #define MODE_DISP_TIMER 1 #define MODE_SET_TIMER 2 @@ -36,17 +41,29 @@ using namespace pimoroni; #ifdef USE_PICO_EXPLORER -uint16_t buffer[PicoExplorer::WIDTH * PicoExplorer::HEIGHT]; -PicoExplorer pico_display(buffer); uint16_t screen_width = PicoExplorer::WIDTH; uint16_t screen_height = PicoExplorer::HEIGHT; + +Button button_a(PicoExplorer::A); +Button button_b(PicoExplorer::B); +Button button_x(PicoExplorer::X); +Button button_y(PicoExplorer::Y); + #else -uint16_t buffer[PicoDisplay::WIDTH * PicoDisplay::HEIGHT]; -PicoDisplay pico_display(buffer); uint16_t screen_width = PicoDisplay::WIDTH; uint16_t screen_height = PicoDisplay::HEIGHT; + +Button button_a(PicoDisplay::A); +Button button_b(PicoDisplay::B); +Button button_x(PicoDisplay::X); +Button button_y(PicoDisplay::Y); + +RGBLED led(PicoDisplay::LED_R, PicoDisplay::LED_G, PicoDisplay::LED_B); #endif +ST7789 st7789(screen_width, screen_height, ROTATE_0, false, get_spi_pins(BG_SPI_FRONT)); +PicoGraphics_PenRGB332 graphics(st7789.width, st7789.height, nullptr); + BreakoutRTC rtc; #define LOW_COUNT_MOD 40 @@ -69,17 +86,20 @@ void flash_led(uint32_t curr_count) { #ifndef USE_PICO_EXPLORER if((curr_count % FLASH_MOD) < (FLASH_MOD / 2)) { // value less than half modded number - LED off - pico_display.set_led(0, 0, 0); + led.set_rgb(0, 0, 0); } else { // value more than half modded number - LED on - pico_display.set_led(128, 128, 128); + led.set_rgb(128, 128, 128); } #endif } int main() { - pico_display.init(); + Pen WHITE = graphics.create_pen(255, 255, 255); + Pen BG = graphics.create_pen(55, 65, 75); + Pen RED = graphics.create_pen(255, 0, 0); + Pen GREEN = graphics.create_pen(0, 255, 0); rtc.init(); // rtc.setup(false); @@ -112,7 +132,7 @@ int main() { while(true) { - if(a_pressed == 0 && pico_display.is_pressed(pico_display.A)) { + if(a_pressed == 0 && button_a.read()) { a_pressed = 1; if(display_mode == MODE_DISP_CLOCK) { // We were displaying clock = set up timer @@ -134,11 +154,11 @@ int main() { display_mode = MODE_SET_TIMER; } } - else if(a_pressed >= 1 && !pico_display.is_pressed(pico_display.A)) { + else if(a_pressed >= 1 && !button_a.read()) { a_pressed = 0; } - if(b_pressed == 0 && pico_display.is_pressed(pico_display.B)) { + if(b_pressed == 0 && button_b.read()) { b_pressed = 1; if((display_mode == MODE_DISP_TIMER) || (display_mode == MODE_SET_TIMER)) { @@ -150,117 +170,117 @@ int main() { timer_count = DEFAULT_TIMER_COUNT; } } - else if(b_pressed >= 1 && !pico_display.is_pressed(pico_display.B)) { + else if(b_pressed >= 1 && !button_b.read()) { b_pressed = 0; } - if(x_pressed == 0 && pico_display.is_pressed(pico_display.X)) { + if(x_pressed == 0 && button_x.read()) { x_pressed = 1; if(display_mode == MODE_SET_TIMER) { // Setting timer - Increment count timer_count++; } } - else if(x_pressed >= 1 && pico_display.is_pressed(pico_display.X)) { + else if(x_pressed >= 1 && button_x.read()) { // Button still pressed - check if has reached repeat count if(repeat_count_reached(x_pressed++)) { timer_count++; } } - else if(x_pressed >= 1 && !pico_display.is_pressed(pico_display.X)) { + else if(x_pressed >= 1 && !button_x.read()) { x_pressed = 0; } - if(y_pressed == 0 && pico_display.is_pressed(pico_display.Y)) { + if(y_pressed == 0 && button_y.read()) { y_pressed = 1; if(display_mode == MODE_SET_TIMER) { // Setting timer - Decrement count if (timer_count >= 1) timer_count--; } } - else if(y_pressed >= 1 && pico_display.is_pressed(pico_display.Y)) { + else if(y_pressed >= 1 && button_y.read()) { // Button still pressed - check if has reached repeat count if(repeat_count_reached(y_pressed++)) { if(timer_count >= 1) timer_count--; } } - else if(y_pressed >= 1 && !pico_display.is_pressed(pico_display.Y)) { + else if(y_pressed >= 1 && !button_y.read()) { y_pressed = 0; } Rect text_box(5, 5, screen_width-10, screen_height-10); - pico_display.set_pen(55, 65, 75); - pico_display.rectangle(text_box); + graphics.set_pen(BG); + graphics.rectangle(text_box); // text_box.deflate(10); - pico_display.set_clip(text_box); - pico_display.set_pen(255, 255, 255); + graphics.set_clip(text_box); + graphics.set_pen(WHITE); switch(display_mode) { case MODE_DISP_CLOCK: // Show the clock face flash_led(0); if(rtc.update_time()) { - pico_display.text("Set Timer", + graphics.text("Set Timer", Point(text_box.x, text_box.y+2), 230, 1); - pico_display.set_pen(0, 255, 0); - pico_display.text(rtc.string_date(), + graphics.set_pen(GREEN); + graphics.text(rtc.string_date(), Point(text_box.x, text_box.y+20), 230, 4); - pico_display.set_pen(255, 0, 0); - pico_display.text(rtc.string_time(), + graphics.set_pen(RED); + graphics.text(rtc.string_time(), Point(text_box.x, text_box.y+60), 230, 6); - pico_display.set_pen(255, 255, 255); - pico_display.text("Clock", + graphics.set_pen(WHITE); + graphics.text("Clock", Point(text_box.x, text_box.y+screen_height-20), 230, 1); } else { sprintf(buf, "Time: rtc.updateTime() ret err"); - pico_display.text(buf, + graphics.text(buf, Point(text_box.x, text_box.y), 30, 2); } break; case MODE_DISP_TIMER: - pico_display.text("Set Timer", + graphics.text("Set Timer", Point(text_box.x, text_box.y+2), 230, 1); if(rtc.read_timer_interrupt_flag()) { // Go periodic time interupt - say loop ended - pico_display.set_pen(255, 0, 0); + graphics.set_pen(RED); sprintf(buf, "%s", "Timer complete"); - pico_display.text(buf, + graphics.text(buf, Point(text_box.x, text_box.y+30), 230, 4); - pico_display.set_pen(255, 255, 255); + graphics.set_pen(WHITE); flash_led(i); } else { sprintf(buf, "%s %d", "Timer running", rtc.get_timer_count()); - pico_display.text(buf, + graphics.text(buf, Point(text_box.x, text_box.y+30), 230, 3); } - pico_display.text("Clock", + graphics.text("Clock", Point(text_box.x, text_box.y+screen_height-20), 230, 1); break; case MODE_SET_TIMER: flash_led(0); - pico_display.text("Run Timer", + graphics.text("Run Timer", Point(text_box.x, text_box.y+2), 230, 1); - pico_display.text("+ Time", + graphics.text("+ Time", Point(text_box.x+screen_width-42, text_box.y+2), 230, 1); sprintf(buf, "Time %d secs", timer_count); - pico_display.text(buf, + graphics.text(buf, Point(text_box.x, text_box.y+30), 230, 3); - pico_display.text("Clock", + graphics.text("Clock", Point(text_box.x, text_box.y+screen_height-20), 230, 1); - pico_display.text("- Time", + graphics.text("- Time", Point(text_box.x+screen_width-42, text_box.y+screen_height-20), 230, 1); break; } - pico_display.remove_clip(); + graphics.remove_clip(); // update screen - pico_display.update(); + st7789.update(&graphics); i++; } diff --git a/examples/pico_tof_display/CMakeLists.txt b/examples/pico_tof_display/CMakeLists.txt index 2b372d56..90c9d20a 100644 --- a/examples/pico_tof_display/CMakeLists.txt +++ b/examples/pico_tof_display/CMakeLists.txt @@ -1,10 +1,10 @@ add_executable( tof_display - demo.cpp + pico_tof_display.cpp ) # Pull in pico libraries that we need -target_link_libraries(tof_display pico_stdlib pico_explorer pico_display vl53l1x) +target_link_libraries(tof_display pico_stdlib pico_explorer pico_display vl53l1x pico_graphics st7789 rgbled button) pico_enable_stdio_uart(tof_display 1) diff --git a/examples/pico_tof_display/demo.cpp b/examples/pico_tof_display/pico_tof_display.cpp similarity index 65% rename from examples/pico_tof_display/demo.cpp rename to examples/pico_tof_display/pico_tof_display.cpp index cd750d81..f70219b2 100644 --- a/examples/pico_tof_display/demo.cpp +++ b/examples/pico_tof_display/pico_tof_display.cpp @@ -24,62 +24,22 @@ #include "pico_display.hpp" #endif #include "vl53l1x.hpp" +#include "drivers/button/button.hpp" +#include "drivers/rgbled/rgbled.hpp" + +#include "drivers/st7789/st7789.hpp" +#include "libraries/pico_graphics/pico_graphics.hpp" using namespace pimoroni; -class AutoRepeat { - public: - AutoRepeat(uint32_t repeat_time=200, uint32_t hold_time=1000) { - this->repeat_time = repeat_time; - this->hold_time = hold_time; - } - bool next(uint32_t time, bool state) { - bool changed = state != last_state; - last_state = state; - - if(changed) { - if(state) { - pressed_time = time; - pressed = true; - last_time = time; - return true; - } - else { - pressed_time = 0; - pressed = false; - last_time = 0; - } - } - // Shortcut for no auto-repeat - if(repeat_time == 0) return false; - - if(pressed) { - uint32_t repeat_rate = repeat_time; - if(hold_time > 0 && time - pressed_time > hold_time) { - repeat_rate /= 3; - } - if(time - last_time > repeat_rate) { - last_time = time; - return true; - } - } - - return false; - } - private: - uint32_t repeat_time; - uint32_t hold_time; - bool pressed = false; - bool last_state = false; - uint32_t pressed_time = 0; - uint32_t last_time = 0; -}; - #ifdef USE_PICO_EXPLORER -uint16_t buffer[PicoExplorer::WIDTH * PicoExplorer::HEIGHT]; -PicoExplorer pico_display(buffer); -uint16_t screen_width = PicoExplorer::WIDTH; -uint16_t screen_height = PicoExplorer::HEIGHT; +ST7789 st7789(PicoExplorer::WIDTH, PicoExplorer::HEIGHT, ROTATE_0, false, get_spi_pins(BG_SPI_FRONT)); + +Button button_a(PicoExplorer::A); +Button button_b(PicoExplorer::B); +Button button_x(PicoExplorer::X); +Button button_y(PicoExplorer::Y); + uint16_t disptext_reminder_size = 2; uint16_t disptext_b_reminder_xoff = 5; uint16_t disptext_b_reminder_yoff = 210; @@ -94,10 +54,15 @@ uint16_t disptext_dist_xoff = 10; uint16_t disptext_dist_yoff = 90; uint16_t disptext_dist_size = 6; #else -uint16_t buffer[PicoDisplay::WIDTH * PicoDisplay::HEIGHT]; -PicoDisplay pico_display(buffer); -uint16_t screen_width = PicoDisplay::WIDTH; -uint16_t screen_height = PicoDisplay::HEIGHT; +ST7789 st7789(PicoDisplay::WIDTH, PicoDisplay::HEIGHT, ROTATE_0, false, get_spi_pins(BG_SPI_FRONT)); + +RGBLED rgbled(PicoDisplay::LED_R, PicoDisplay::LED_G, PicoDisplay::LED_B); + +Button button_a(PicoDisplay::A); +Button button_b(PicoDisplay::B); +Button button_x(PicoDisplay::X); +Button button_y(PicoDisplay::Y); + uint16_t disptext_reminder_size = 2; uint16_t disptext_b_reminder_xoff = 2; uint16_t disptext_b_reminder_yoff = 110; @@ -113,6 +78,11 @@ uint16_t disptext_dist_yoff = 45; uint16_t disptext_dist_size = 4; #endif +PicoGraphics_PenRGB332 graphics(st7789.width, st7789.height, nullptr); + +uint16_t screen_width = graphics.bounds.w; +uint16_t screen_height = graphics.bounds.h; + #define MM_TO_INCH 25.4 VL53L1X vl53l1x; @@ -124,11 +94,6 @@ const char mode_to_text[4][7] = { "Long" }; -AutoRepeat ar_button_a; -AutoRepeat ar_button_b; -AutoRepeat ar_button_x; -AutoRepeat ar_button_y; - #define FLASH_MOD 20 void flash_led(uint32_t curr_count) { // Flash the LED based on the current loop counter @@ -136,10 +101,10 @@ void flash_led(uint32_t curr_count) { #ifndef USE_PICO_EXPLORER if ((curr_count % FLASH_MOD) < (FLASH_MOD / 2)) { // value less than half modded number - LED off - pico_display.set_led(0, 0, 0); + rgbled.set_rgb(0, 0, 0); } else { // value more than half modded number - LED on - pico_display.set_led(128, 128, 128); + rgbled.set_rgb(128, 128, 128); } #endif } @@ -148,8 +113,6 @@ int main() { bool vl53_present = false; uint16_t vl53_mode = 1; - pico_display.init(); - vl53_present = vl53l1x.init(); uint32_t i = 0; @@ -169,11 +132,15 @@ int main() { // Whether the display is being held bool dist_held = false; + Pen WHITE = graphics.create_pen(255, 255, 255); + Pen REDDISH = graphics.create_pen(255, 64, 64); + Pen BG = graphics.create_pen(55, 65, 75); + while(true) { - // bool a_pressed = ar_button_a.next(i, pico_display.is_pressed(pico_display.A)); - bool b_pressed = ar_button_b.next(i, pico_display.is_pressed(pico_display.B)); - bool x_pressed = ar_button_x.next(i, pico_display.is_pressed(pico_display.X)); - bool y_pressed = ar_button_y.next(i, pico_display.is_pressed(pico_display.Y)); + // bool a_pressed = button_a.read(); + bool b_pressed = button_b.read(); + bool x_pressed = button_x.read(); + bool y_pressed = button_y.read(); if (b_pressed) { dist_held = !dist_held; @@ -192,30 +159,30 @@ int main() { } Rect text_box(5, 5, screen_width-10, screen_height-10); - pico_display.set_pen(55, 65, 75); - pico_display.rectangle(text_box); + graphics.set_pen(BG); + graphics.rectangle(text_box); // text_box.deflate(10); - pico_display.set_clip(text_box); - pico_display.set_pen(255, 255, 255); + graphics.set_clip(text_box); + graphics.set_pen(WHITE); // Show the current distance flash_led(0); if (vl53_present) { - pico_display.text("Units", + graphics.text("Units", Point(text_box.x+disptext_x_reminder_xoff, text_box.y+disptext_x_reminder_yoff), 230, disptext_reminder_size); - pico_display.text("+Mode", + graphics.text("+Mode", Point(text_box.x+disptext_y_reminder_xoff, text_box.y+disptext_y_reminder_yoff), 230, disptext_reminder_size); if(dist_held) { - pico_display.set_pen(255, 64, 64); + graphics.set_pen(REDDISH); } - pico_display.text("Hold", + graphics.text("Hold", Point(text_box.x+disptext_b_reminder_xoff, text_box.y+disptext_b_reminder_yoff), 230, disptext_reminder_size); - pico_display.set_pen(255, 255, 255); + graphics.set_pen(WHITE); sprintf(buf, "Mode: %s", mode_to_text[vl53_mode]); - pico_display.text(buf, + graphics.text(buf, Point(text_box.x+disptext_mode_xoff, text_box.y+disptext_mode_yoff), 230, disptext_mode_size); @@ -228,19 +195,19 @@ int main() { sprintf(buf, "%dft %.1fin", ft, ((float)dist/MM_TO_INCH)-ft*12.0); } - pico_display.text(buf, + graphics.text(buf, Point(text_box.x+disptext_dist_xoff, text_box.y+disptext_dist_yoff), 120, disptext_dist_size); } else { - pico_display.text("VL53L1X Missing", + graphics.text("VL53L1X Missing", Point(text_box.x+disptext_dist_xoff, text_box.y+disptext_dist_yoff), 230, disptext_dist_size); } - pico_display.remove_clip(); + graphics.remove_clip(); // update screen - pico_display.update(); + st7789.update(&graphics); i++; } diff --git a/examples/pico_trackball_display/CMakeLists.txt b/examples/pico_trackball_display/CMakeLists.txt index e860fce5..ac18ef67 100644 --- a/examples/pico_trackball_display/CMakeLists.txt +++ b/examples/pico_trackball_display/CMakeLists.txt @@ -2,11 +2,11 @@ set(OUTPUT_NAME trackball_display) add_executable( ${OUTPUT_NAME} - demo.cpp + pico_trackball_display.cpp ) # Pull in pico libraries that we need -target_link_libraries(${OUTPUT_NAME} pico_stdlib pico_explorer pico_display breakout_trackball) +target_link_libraries(${OUTPUT_NAME} pico_stdlib pico_explorer pico_display breakout_trackball pico_graphics st7789) pico_enable_stdio_uart(${OUTPUT_NAME} 1) diff --git a/examples/pico_trackball_display/demo.cpp b/examples/pico_trackball_display/pico_trackball_display.cpp similarity index 70% rename from examples/pico_trackball_display/demo.cpp rename to examples/pico_trackball_display/pico_trackball_display.cpp index 0250bad5..bdb5e27d 100644 --- a/examples/pico_trackball_display/demo.cpp +++ b/examples/pico_trackball_display/pico_trackball_display.cpp @@ -16,7 +16,7 @@ // To use PicoExplorer rather than PicoDisplay, uncomment the following line #define USE_PICO_EXPLORER 1 // This: -// - Includes pico_explorer.hpp rather than pico_display.hpp +// - Includes pico_explorer.hpp rather than display.hpp // - Replaces all PicoDisplay references with PicoExplorer #ifdef USE_PICO_EXPLORER #include "pico_explorer.hpp" @@ -25,6 +25,9 @@ #endif #include "breakout_trackball.hpp" +#include "drivers/st7789/st7789.hpp" +#include "libraries/pico_graphics/pico_graphics.hpp" + using namespace pimoroni; struct TrackballColour { @@ -35,16 +38,16 @@ struct TrackballColour { }; #ifdef USE_PICO_EXPLORER -uint16_t buffer[PicoExplorer::WIDTH * PicoExplorer::HEIGHT]; -PicoExplorer pico_display(buffer); const uint16_t screen_width = PicoExplorer::WIDTH; const uint16_t screen_height = PicoExplorer::HEIGHT; #else -uint16_t buffer[PicoDisplay::WIDTH * PicoDisplay::HEIGHT]; -PicoDisplay pico_display(buffer); const uint16_t screen_width = PicoDisplay::WIDTH; const uint16_t screen_height = PicoDisplay::HEIGHT; #endif + +ST7789 st7789(screen_width, screen_height, ROTATE_0, false, get_spi_pins(BG_SPI_FRONT)); +PicoGraphics_PenRGB332 graphics(st7789.width, st7789.height, nullptr); + const Point screen_centre(screen_width / 2, screen_height / 2); const uint16_t circle_radius = std::min(screen_centre.x, screen_centre.y) / 4; const float ring_radius_mult = 0.7f; @@ -71,7 +74,6 @@ bool centre_circle_state = false; int main() { int16_t x = screen_centre.x; int16_t y = screen_centre.y; - pico_display.init(); trackball.init(); @@ -84,52 +86,57 @@ int main() { positions[i] = pos; } + Pen WHITE = graphics.create_pen(255, 255, 255); + Pen BLACK = graphics.create_pen(0, 0, 0); + Pen LIGHT_GREY = graphics.create_pen(212, 212, 212); + Pen MID_GREY = graphics.create_pen(128, 128, 128); + while(true) { Trackball::State state = trackball.read(); x = std::min(std::max(x - state.left + state.right, 0), (int)screen_width); y = std::min(std::max(y - state.up + state.down, 0), (int)screen_height); Point cursor_pos(x, y); - pico_display.set_pen(0, 0, 0); - pico_display.clear(); + graphics.set_pen(BLACK); + graphics.clear(); //Draw a set of circles in a ring around the screen centre for(uint8_t i = 0; i < NUM_CIRCLES; i++) { TrackballColour col = colour_circles[i]; if(circle_states[i]) { - pico_display.set_pen(col.r, col.g, col.b); - pico_display.circle(positions[i], circle_radius + circle_border); - pico_display.set_pen(col.r >> 1, col.g >> 1, col.b >> 1); - pico_display.circle(positions[i], circle_radius); + graphics.set_pen(graphics.create_pen(col.r, col.g, col.b)); + graphics.circle(positions[i], circle_radius + circle_border); + graphics.set_pen(graphics.create_pen(col.r >> 1, col.g >> 1, col.b >> 1)); + graphics.circle(positions[i], circle_radius); } else { - pico_display.set_pen(col.r >> 1, col.g >> 1, col.b >> 1); - pico_display.circle(positions[i], circle_radius + circle_border); - pico_display.set_pen(col.r, col.g, col.b); - pico_display.circle(positions[i], circle_radius); + graphics.set_pen(graphics.create_pen(col.r >> 1, col.g >> 1, col.b >> 1)); + graphics.circle(positions[i], circle_radius + circle_border); + graphics.set_pen(graphics.create_pen(col.r, col.g, col.b)); + graphics.circle(positions[i], circle_radius); } } //Draw a centre circle if(centre_circle_state) { - pico_display.set_pen(255, 255, 255); - pico_display.circle(screen_centre, circle_radius + circle_border); - pico_display.set_pen(128, 128, 128); - pico_display.circle(screen_centre, circle_radius); + graphics.set_pen(WHITE); + graphics.circle(screen_centre, circle_radius + circle_border); + graphics.set_pen(MID_GREY); + graphics.circle(screen_centre, circle_radius); } else { - pico_display.set_pen(128, 128, 128); - pico_display.circle(screen_centre, circle_radius + circle_border); - pico_display.set_pen(255, 255, 255); - pico_display.circle(screen_centre, circle_radius); + graphics.set_pen(MID_GREY); + graphics.circle(screen_centre, circle_radius + circle_border); + graphics.set_pen(WHITE); + graphics.circle(screen_centre, circle_radius); } //Draw the cursor - pico_display.set_pen(0, 0, 0); - pico_display.circle(cursor_pos, cursor_radius + cursor_border); - pico_display.set_pen(212, 212, 212); - pico_display.circle(cursor_pos, cursor_radius); + graphics.set_pen(BLACK); + graphics.circle(cursor_pos, cursor_radius + cursor_border); + graphics.set_pen(LIGHT_GREY); + graphics.circle(cursor_pos, cursor_radius); int16_t x_diff = cursor_pos.x - screen_centre.x; int16_t y_diff = cursor_pos.y - screen_centre.y; @@ -161,7 +168,7 @@ int main() { } // update screen - pico_display.update(); + st7789.update(&graphics); } return 0; diff --git a/examples/tufty2040/CMakeLists.txt b/examples/tufty2040/CMakeLists.txt new file mode 100644 index 00000000..282ad558 --- /dev/null +++ b/examples/tufty2040/CMakeLists.txt @@ -0,0 +1 @@ +include(tufty2040_drawing.cmake) diff --git a/examples/tufty2040/tufty2040_drawing.cmake b/examples/tufty2040/tufty2040_drawing.cmake new file mode 100644 index 00000000..17dee086 --- /dev/null +++ b/examples/tufty2040/tufty2040_drawing.cmake @@ -0,0 +1,15 @@ +set(OUTPUT_NAME tufty2040_drawing) +add_executable(${OUTPUT_NAME} tufty2040_drawing.cpp) + +target_link_libraries(${OUTPUT_NAME} + tufty2040 + hardware_spi + pico_graphics + st7789 + button +) + +# enable usb output +pico_enable_stdio_usb(${OUTPUT_NAME} 1) + +pico_add_extra_outputs(${OUTPUT_NAME}) diff --git a/examples/tufty2040/tufty2040_drawing.cpp b/examples/tufty2040/tufty2040_drawing.cpp new file mode 100644 index 00000000..48f68e2b --- /dev/null +++ b/examples/tufty2040/tufty2040_drawing.cpp @@ -0,0 +1,138 @@ +#include "pico/stdlib.h" +#include +#include +#include +#include +#include "pico/time.h" +#include "pico/platform.h" + +#include "common/pimoroni_common.hpp" +#include "drivers/st7789/st7789.hpp" +#include "libraries/pico_graphics/pico_graphics.hpp" +#include "tufty2040.hpp" +#include "button.hpp" + +using namespace pimoroni; + +Tufty2040 tufty; + +ST7789 st7789( + Tufty2040::WIDTH, + Tufty2040::HEIGHT, + ROTATE_0, + ParallelPins{ + Tufty2040::LCD_CS, + Tufty2040::LCD_DC, + Tufty2040::LCD_WR, + Tufty2040::LCD_RD, + Tufty2040::LCD_D0, + Tufty2040::BACKLIGHT + } +); + +PicoGraphics_PenRGB332 graphics(st7789.width, st7789.height, nullptr); + +Button button_a(Tufty2040::A); +Button button_b(Tufty2040::B); +Button button_c(Tufty2040::C); +Button button_up(Tufty2040::UP); +Button button_down(Tufty2040::DOWN); + +uint32_t time() { + absolute_time_t t = get_absolute_time(); + return to_ms_since_boot(t); +} + +// HSV Conversion expects float inputs in the range of 0.00-1.00 for each channel +// Outputs are rgb in the range 0-255 for each channel +void from_hsv(float h, float s, float v, uint8_t &r, uint8_t &g, uint8_t &b) { + float i = floor(h * 6.0f); + float f = h * 6.0f - i; + v *= 255.0f; + uint8_t p = v * (1.0f - s); + uint8_t q = v * (1.0f - f * s); + uint8_t t = v * (1.0f - (1.0f - f) * s); + + switch (int(i) % 6) { + case 0: r = v; g = t; b = p; break; + case 1: r = q; g = v; b = p; break; + case 2: r = p; g = v; b = t; break; + case 3: r = p; g = q; b = v; break; + case 4: r = t; g = p; b = v; break; + case 5: r = v; g = p; b = q; break; + } +} + +int main() { + st7789.set_backlight(255); + + Pen WHITE = graphics.create_pen(255, 255, 255); + Pen BG = graphics.create_pen(120, 40, 60); + + struct pt { + float x; + float y; + uint8_t r; + float dx; + float dy; + uint16_t pen; + }; + + std::vector shapes; + for(int i = 0; i < 100; i++) { + pt shape; + shape.x = rand() % graphics.bounds.w; + shape.y = rand() % graphics.bounds.h; + shape.r = (rand() % 10) + 3; + shape.dx = float(rand() % 255) / 64.0f; + shape.dy = float(rand() % 255) / 64.0f; + shape.pen = graphics.create_pen(rand() % 255, rand() % 255, rand() % 255); + shapes.push_back(shape); + } + + Point text_location(0, 0); + uint8_t i = 0; + + while(true) { + + graphics.set_pen(BG); + graphics.clear(); + + for(auto &shape : shapes) { + shape.x += shape.dx; + shape.y += shape.dy; + if((shape.x - shape.r) < 0) { + shape.dx *= -1; + shape.x = shape.r; + } + if((shape.x + shape.r) >= graphics.bounds.w) { + shape.dx *= -1; + shape.x = graphics.bounds.w - shape.r; + } + if((shape.y - shape.r) < 0) { + shape.dy *= -1; + shape.y = shape.r; + } + if((shape.y + shape.r) >= graphics.bounds.h) { + shape.dy *= -1; + shape.y = graphics.bounds.h - shape.r; + } + + graphics.set_pen(shape.pen); + graphics.circle(Point(shape.x, shape.y), shape.r); + + } + + + graphics.set_pen(WHITE); + graphics.text("Hello World", text_location, 320); + + // update screen + st7789.update(&graphics); + + i+=10; + tufty.led(i); + } + + return 0; +} diff --git a/libraries/CMakeLists.txt b/libraries/CMakeLists.txt index 0304d153..a7498a1a 100644 --- a/libraries/CMakeLists.txt +++ b/libraries/CMakeLists.txt @@ -4,7 +4,6 @@ add_subdirectory(breakout_dotmatrix) add_subdirectory(breakout_encoder) add_subdirectory(breakout_ioexpander) add_subdirectory(breakout_ltr559) -add_subdirectory(breakout_colourlcd160x80) add_subdirectory(breakout_rgbmatrix5x5) add_subdirectory(breakout_matrix11x7) add_subdirectory(breakout_mics6814) @@ -17,7 +16,6 @@ add_subdirectory(breakout_sgp30) add_subdirectory(breakout_as7262) add_subdirectory(breakout_msa301) add_subdirectory(breakout_bh1745) -add_subdirectory(generic_st7789) add_subdirectory(pico_graphics) add_subdirectory(pico_display) add_subdirectory(pico_display_2) @@ -29,6 +27,7 @@ add_subdirectory(pico_rgb_keypad) add_subdirectory(pico_wireless) add_subdirectory(plasma2040) add_subdirectory(badger2040) +add_subdirectory(tufty2040) add_subdirectory(servo2040) add_subdirectory(motor2040) add_subdirectory(adcfft) diff --git a/libraries/breakout_colourlcd160x80/CMakeLists.txt b/libraries/breakout_colourlcd160x80/CMakeLists.txt deleted file mode 100644 index d855c97b..00000000 --- a/libraries/breakout_colourlcd160x80/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -include(breakout_colourlcd160x80.cmake) diff --git a/libraries/breakout_colourlcd160x80/breakout_colourlcd160x80.cmake b/libraries/breakout_colourlcd160x80/breakout_colourlcd160x80.cmake deleted file mode 100644 index ece9be90..00000000 --- a/libraries/breakout_colourlcd160x80/breakout_colourlcd160x80.cmake +++ /dev/null @@ -1,11 +0,0 @@ -set(LIB_NAME breakout_colourlcd160x80) -add_library(${LIB_NAME} INTERFACE) - -target_sources(${LIB_NAME} INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/${LIB_NAME}.cpp -) - -target_include_directories(${LIB_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR}) - -# Pull in pico libraries that we need -target_link_libraries(${LIB_NAME} INTERFACE pico_stdlib st7735 pico_graphics hardware_spi) diff --git a/libraries/breakout_colourlcd160x80/breakout_colourlcd160x80.cpp b/libraries/breakout_colourlcd160x80/breakout_colourlcd160x80.cpp deleted file mode 100644 index bfa71ff4..00000000 --- a/libraries/breakout_colourlcd160x80/breakout_colourlcd160x80.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "breakout_colourlcd160x80.hpp" - -namespace pimoroni { - - BreakoutColourLCD160x80::BreakoutColourLCD160x80(uint16_t *buf) - : PicoGraphics(WIDTH, HEIGHT, buf), screen(WIDTH, HEIGHT, buf) { - __fb = buf; - } - - BreakoutColourLCD160x80::BreakoutColourLCD160x80(uint16_t *buf, spi_inst_t *spi, - uint cs, uint dc, uint sck, uint mosi, uint miso, uint bl) - : PicoGraphics(WIDTH, HEIGHT, buf), screen(WIDTH, HEIGHT, buf, spi, cs, dc, sck, mosi, miso, bl) { - __fb = buf; - } - - BreakoutColourLCD160x80::BreakoutColourLCD160x80(uint16_t *buf, BG_SPI_SLOT slot) - : PicoGraphics(WIDTH, HEIGHT, buf), screen(WIDTH, HEIGHT, buf, slot) { - __fb = buf; - } - - void BreakoutColourLCD160x80::init() { - // initialise the screen - screen.init(); - } - - spi_inst_t* BreakoutColourLCD160x80::get_spi() const { - return screen.get_spi(); - } - - int BreakoutColourLCD160x80::get_cs() const { - return screen.get_cs(); - } - - int BreakoutColourLCD160x80::get_dc() const { - return screen.get_dc(); - } - - int BreakoutColourLCD160x80::get_sck() const { - return screen.get_sck(); - } - - int BreakoutColourLCD160x80::get_mosi() const { - return screen.get_mosi(); - } - - int BreakoutColourLCD160x80::get_bl() const { - return screen.get_bl(); - } - - void BreakoutColourLCD160x80::update() { - screen.update(); - } - - void BreakoutColourLCD160x80::set_backlight(uint8_t brightness) { - screen.set_backlight(brightness); - } - -} \ No newline at end of file diff --git a/libraries/breakout_colourlcd160x80/breakout_colourlcd160x80.hpp b/libraries/breakout_colourlcd160x80/breakout_colourlcd160x80.hpp deleted file mode 100644 index 8db78425..00000000 --- a/libraries/breakout_colourlcd160x80/breakout_colourlcd160x80.hpp +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include "drivers/st7735/st7735.hpp" -#include "libraries/pico_graphics/pico_graphics.hpp" -#include "common/pimoroni_common.hpp" - -namespace pimoroni { - - class BreakoutColourLCD160x80 : public PicoGraphics { - //-------------------------------------------------- - // Constants - //-------------------------------------------------- - public: - static const int WIDTH = 160; - static const int HEIGHT = 80; - - //-------------------------------------------------- - // Variables - //-------------------------------------------------- - public: - uint16_t *__fb; - private: - ST7735 screen; - - - //-------------------------------------------------- - // Constructors/Destructor - //-------------------------------------------------- - public: - BreakoutColourLCD160x80(uint16_t *buf); - BreakoutColourLCD160x80(uint16_t *buf, spi_inst_t *spi, - uint cs, uint dc, uint sck, uint mosi, uint miso = PIN_UNUSED, uint bl = PIN_UNUSED); - BreakoutColourLCD160x80(uint16_t *buf, BG_SPI_SLOT slot); - - - //-------------------------------------------------- - // Methods - //-------------------------------------------------- - public: - void init(); - - spi_inst_t* get_spi() const; - int get_cs() const; - int get_dc() const; - int get_sck() const; - int get_mosi() const; - int get_bl() const; - - void update(); - void set_backlight(uint8_t brightness); - }; - -} diff --git a/libraries/breakout_colourlcd240x240/CMakeLists.txt b/libraries/breakout_colourlcd240x240/CMakeLists.txt deleted file mode 100644 index 2c234a7d..00000000 --- a/libraries/breakout_colourlcd240x240/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -set(LIB_NAME breakout_colourlcd240x240) -add_library(${LIB_NAME} INTERFACE) - -target_sources(${LIB_NAME} INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/${LIB_NAME}.cpp -) - -target_include_directories(${LIB_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR}) - -# Pull in pico libraries that we need -target_link_libraries(${LIB_NAME} INTERFACE pico_stdlib st7789 pico_graphics) \ No newline at end of file diff --git a/libraries/breakout_colourlcd240x240/breakout_colourlcd240x240.cmake b/libraries/breakout_colourlcd240x240/breakout_colourlcd240x240.cmake deleted file mode 100644 index cf2f3148..00000000 --- a/libraries/breakout_colourlcd240x240/breakout_colourlcd240x240.cmake +++ /dev/null @@ -1,14 +0,0 @@ -include(${CMAKE_CURRENT_LIST_DIR}/../../drivers/st7789/st7789.cmake) -include(${CMAKE_CURRENT_LIST_DIR}/../pico_graphics/pico_graphics.cmake) - -set(LIB_NAME breakout_colourlcd240x240) -add_library(${LIB_NAME} INTERFACE) - -target_sources(${LIB_NAME} INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/${LIB_NAME}.cpp -) - -target_include_directories(${LIB_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR}) - -# Pull in pico libraries that we need -target_link_libraries(${LIB_NAME} INTERFACE pico_stdlib st7789 pico_graphics) diff --git a/libraries/breakout_colourlcd240x240/breakout_colourlcd240x240.cpp b/libraries/breakout_colourlcd240x240/breakout_colourlcd240x240.cpp deleted file mode 100644 index f24f0e70..00000000 --- a/libraries/breakout_colourlcd240x240/breakout_colourlcd240x240.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "breakout_colourlcd240x240.hpp" - -namespace pimoroni { - - BreakoutColourLCD240x240::BreakoutColourLCD240x240(uint16_t *buf) - : PicoGraphics(WIDTH, HEIGHT, buf), screen(WIDTH, HEIGHT, false, buf, - PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_FRONT_CS, SPI_DEFAULT_MISO, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, SPI_BG_FRONT_PWM) { - __fb = buf; - } - - BreakoutColourLCD240x240::BreakoutColourLCD240x240(uint16_t *buf, spi_inst_t *spi, - uint cs, uint dc, uint sck, uint mosi, uint bl) - : PicoGraphics(WIDTH, HEIGHT, buf), screen(WIDTH, HEIGHT, false, buf, spi, cs, dc, sck, mosi, bl) { - __fb = buf; - } - - BreakoutColourLCD240x240::BreakoutColourLCD240x240(uint16_t *buf, BG_SPI_SLOT slot) - : PicoGraphics(WIDTH, HEIGHT, buf), screen(WIDTH, HEIGHT, false, buf, - PIMORONI_SPI_DEFAULT_INSTANCE, screen.get_slot_cs(slot), SPI_DEFAULT_MISO, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, screen.get_slot_bl(slot)) { - __fb = buf; - } - - void BreakoutColourLCD240x240::init() { - } - - spi_inst_t* BreakoutColourLCD240x240::get_spi() const { - return screen.get_spi(); - } - - int BreakoutColourLCD240x240::get_cs() const { - return screen.get_cs(); - } - - int BreakoutColourLCD240x240::get_dc() const { - return screen.get_dc(); - } - - int BreakoutColourLCD240x240::get_sck() const { - return screen.get_sck(); - } - - int BreakoutColourLCD240x240::get_mosi() const { - return screen.get_mosi(); - } - - int BreakoutColourLCD240x240::get_bl() const { - return screen.get_bl(); - } - - void BreakoutColourLCD240x240::update() { - screen.update(); - } - - void BreakoutColourLCD240x240::set_backlight(uint8_t brightness) { - screen.set_backlight(brightness); - } - -} \ No newline at end of file diff --git a/libraries/breakout_colourlcd240x240/breakout_colourlcd240x240.hpp b/libraries/breakout_colourlcd240x240/breakout_colourlcd240x240.hpp deleted file mode 100644 index 67588696..00000000 --- a/libraries/breakout_colourlcd240x240/breakout_colourlcd240x240.hpp +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include "drivers/st7789/st7789.hpp" -#include "libraries/pico_graphics/pico_graphics.hpp" -#include "common/pimoroni_common.hpp" - -namespace pimoroni { - - class BreakoutColourLCD240x240 : public PicoGraphics { - //-------------------------------------------------- - // Constants - //-------------------------------------------------- - public: - static const int WIDTH = 240; - static const int HEIGHT = 240; - - //-------------------------------------------------- - // Variables - //-------------------------------------------------- - public: - uint16_t *__fb; - private: - ST7789 screen; - - - //-------------------------------------------------- - // Constructors/Destructor - //-------------------------------------------------- - public: - BreakoutColourLCD240x240(uint16_t *buf); - BreakoutColourLCD240x240(uint16_t *buf, spi_inst_t *spi, - uint cs, uint dc, uint sck, uint mosi, uint bl = PIN_UNUSED); - BreakoutColourLCD240x240(uint16_t *buf, BG_SPI_SLOT slot); - - - //-------------------------------------------------- - // Methods - //-------------------------------------------------- - public: - void init(); - - spi_inst_t* get_spi() const; - int get_cs() const; - int get_dc() const; - int get_sck() const; - int get_mosi() const; - int get_bl() const; - - void update(); - void set_backlight(uint8_t brightness); - }; - -} diff --git a/libraries/breakout_roundlcd/breakout_roundlcd.cmake b/libraries/breakout_roundlcd/breakout_roundlcd.cmake deleted file mode 100644 index aa0d5718..00000000 --- a/libraries/breakout_roundlcd/breakout_roundlcd.cmake +++ /dev/null @@ -1,14 +0,0 @@ -include(${CMAKE_CURRENT_LIST_DIR}/../../drivers/st7789/st7789.cmake) -include(${CMAKE_CURRENT_LIST_DIR}/../pico_graphics/pico_graphics.cmake) - -set(LIB_NAME breakout_roundlcd) -add_library(${LIB_NAME} INTERFACE) - -target_sources(${LIB_NAME} INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/${LIB_NAME}.cpp -) - -target_include_directories(${LIB_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR}) - -# Pull in pico libraries that we need -target_link_libraries(${LIB_NAME} INTERFACE pico_stdlib st7789 pico_graphics) \ No newline at end of file diff --git a/libraries/breakout_roundlcd/breakout_roundlcd.cpp b/libraries/breakout_roundlcd/breakout_roundlcd.cpp deleted file mode 100644 index d398f2cc..00000000 --- a/libraries/breakout_roundlcd/breakout_roundlcd.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "breakout_roundlcd.hpp" - -namespace pimoroni { - - BreakoutRoundLCD::BreakoutRoundLCD(uint16_t *buf) - : PicoGraphics(WIDTH, HEIGHT, buf), screen(WIDTH, HEIGHT, true, buf, - PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_FRONT_CS, SPI_DEFAULT_MISO, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, SPI_BG_FRONT_PWM) { - __fb = buf; - } - - BreakoutRoundLCD::BreakoutRoundLCD(uint16_t *buf, spi_inst_t *spi, - uint cs, uint dc, uint sck, uint mosi, uint bl) - : PicoGraphics(WIDTH, HEIGHT, buf), screen(WIDTH, HEIGHT, true, buf, spi, cs, dc, sck, mosi, bl) { - __fb = buf; - } - - BreakoutRoundLCD::BreakoutRoundLCD(uint16_t *buf, BG_SPI_SLOT slot) - : PicoGraphics(WIDTH, HEIGHT, buf), screen(WIDTH, HEIGHT, true, buf, - PIMORONI_SPI_DEFAULT_INSTANCE, screen.get_slot_cs(slot), SPI_DEFAULT_MISO, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, screen.get_slot_bl(slot)) { - __fb = buf; - } - - void BreakoutRoundLCD::init() { - } - - spi_inst_t* BreakoutRoundLCD::get_spi() const { - return screen.get_spi(); - } - - int BreakoutRoundLCD::get_cs() const { - return screen.get_cs(); - } - - int BreakoutRoundLCD::get_dc() const { - return screen.get_dc(); - } - - int BreakoutRoundLCD::get_sck() const { - return screen.get_sck(); - } - - int BreakoutRoundLCD::get_mosi() const { - return screen.get_mosi(); - } - - int BreakoutRoundLCD::get_bl() const { - return screen.get_bl(); - } - - void BreakoutRoundLCD::update() { - screen.update(); - } - - void BreakoutRoundLCD::set_backlight(uint8_t brightness) { - screen.set_backlight(brightness); - } - -} \ No newline at end of file diff --git a/libraries/breakout_roundlcd/breakout_roundlcd.hpp b/libraries/breakout_roundlcd/breakout_roundlcd.hpp deleted file mode 100644 index 8c990b05..00000000 --- a/libraries/breakout_roundlcd/breakout_roundlcd.hpp +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once - -#include "drivers/st7789/st7789.hpp" -#include "libraries/pico_graphics/pico_graphics.hpp" - -namespace pimoroni { - - class BreakoutRoundLCD : public PicoGraphics { - //-------------------------------------------------- - // Constants - //-------------------------------------------------- - public: - static const int WIDTH = 240; - static const int HEIGHT = 240; - - static const uint8_t PIN_UNUSED = UINT8_MAX; - - - //-------------------------------------------------- - // Variables - //-------------------------------------------------- - public: - uint16_t *__fb; - private: - ST7789 screen; - - - //-------------------------------------------------- - // Constructors/Destructor - //-------------------------------------------------- - public: - BreakoutRoundLCD(uint16_t *buf); - BreakoutRoundLCD(uint16_t *buf, spi_inst_t *spi, - uint cs, uint dc, uint sck, uint mosi, uint bl = PIN_UNUSED); - BreakoutRoundLCD(uint16_t *buf, BG_SPI_SLOT slot); - - - //-------------------------------------------------- - // Methods - //-------------------------------------------------- - public: - void init(); - - spi_inst_t* get_spi() const; - int get_cs() const; - int get_dc() const; - int get_sck() const; - int get_mosi() const; - int get_bl() const; - - void update(); - void set_backlight(uint8_t brightness); - }; - -} diff --git a/libraries/generic_st7789/CMakeLists.txt b/libraries/generic_st7789/CMakeLists.txt deleted file mode 100644 index 6e96f70b..00000000 --- a/libraries/generic_st7789/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -include(generic_st7789.cmake) \ No newline at end of file diff --git a/libraries/generic_st7789/README.md b/libraries/generic_st7789/README.md deleted file mode 100644 index 48268841..00000000 --- a/libraries/generic_st7789/README.md +++ /dev/null @@ -1,106 +0,0 @@ -# Genereic ST7789 - Pico Display Pack & Pico Display Pack 2.0" and 240x240 Square & Round LCD Breakouts - -Our Pico Display Packs offers a vibrant 1.14" (240x135) or 2.0" (320x240) IPS LCD screen for your Raspberry Pi Pico it also includes four switches and and an RGB LED! - -We've included helper functions to handle every aspect of drawing to the screen and interfacing with the buttons and LED. See the [function reference](#function-reference) for details. - -- [Example Program](#example-program) -- [Function Reference](#function-reference) - - [PicoGraphics](#picographics) - - [set_backlight](#set_backlight) - - [update](#update) - -## Example Program - -The following example sets up Pico Display, displays some basic demo text and graphics and will illuminate the RGB LED green if the A button is pressed. - -```c++ -#include "pico_display.hpp" -#include "generic_st7789.hpp" -#include "rgbled.hpp" -#include "button.hpp" - -using namespace pimoroni; - -const bool ROTATE_180 = false; - -uint16_t buffer[PicoDisplay::WIDTH * PicoDisplay::HEIGHT]; - -// Swap WIDTH and HEIGHT to rotate 90 degrees -ST7789Generic pico_display(PicoDisplay::WIDTH, PicoDisplay::HEIGHT, buffer); - -// RGB LED controller -RGBLED led(PicoDisplay::LED_R, PicoDisplay::LED_G, PicoDisplay::LED_B); - -// Buttons -Button button_a(PicoDisplay::A); -Button button_b(PicoDisplay::B); -Button button_x(PicoDisplay::X); -Button button_y(PicoDisplay::Y); - -int main() { - pico_display.configure_display(ROTATE_180); - - // set the backlight to a value between 0 and 255 - // the backlight is driven via PWM and is gamma corrected by our - // library to give a gorgeous linear brightness range. - pico_display.set_backlight(100); - - while(true) { - // detect if the A button is pressed (could be A, B, X, or Y) - if(button_a.raw()) { - // make the led glow green - // parameters are red, green, blue all between 0 and 255 - // these are also gamma corrected - led.set_rgb(0, 255, 0); - } - - // set the colour of the pen - // parameters are red, green, blue all between 0 and 255 - pico_display.set_pen(30, 40, 50); - - // fill the screen with the current pen colour - pico_display.clear(); - - // draw a box to put some text in - pico_display.set_pen(10, 20, 30); - Rect text_rect(10, 10, 150, 150); - pico_display.rectangle(text_rect); - - // write some text inside the box with 10 pixels of margin - // automatically word wrapping - text_rect.deflate(10); - pico_display.set_pen(110, 120, 130); - pico_display.text("This is a message", Point(text_rect.x, text_rect.y), text_rect.w); - - // now we've done our drawing let's update the screen - pico_display.update(); - } -} -``` - -## Function Reference - -### PicoGraphics - -The generic ST7789 driver uses our Pico Graphics library to draw graphics and text. For more information [read the Pico Graphics function reference.](../pico_graphics/README.md#function-reference). - -You will also need to use the RGBLED library to drive the RGB LED, and the Button library for the four buttons. - -### set_backlight - -Set the display backlight from 0-255. - -```c++ -pico_display.set_backlight(brightness); -``` - -Uses hardware PWM to dim the display backlight, dimming values are gamma-corrected to provide smooth brightness transitions across the full range of intensity. This may result in some low values mapping as "off." - -### update - -To display your changes on Pico Display's screen you need to call `update`: - -```c++ -pico_display.update(); -``` diff --git a/libraries/generic_st7789/generic_st7789.cpp b/libraries/generic_st7789/generic_st7789.cpp deleted file mode 100644 index 44bd6b97..00000000 --- a/libraries/generic_st7789/generic_st7789.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include -#include - -#include "generic_st7789.hpp" - - -namespace pimoroni { - spi_inst_t* ST7789Generic::get_spi() const { - return st7789.get_spi(); - } - - int ST7789Generic::get_cs() const { - return st7789.get_cs(); - } - - int ST7789Generic::get_dc() const { - return st7789.get_dc(); - } - - int ST7789Generic::get_sck() const { - return st7789.get_sck(); - } - - int ST7789Generic::get_mosi() const { - return st7789.get_mosi(); - } - - int ST7789Generic::get_bl() const { - return st7789.get_bl(); - } - - void ST7789Generic::update() { - st7789.update(); - } - - void ST7789Generic::flip() { - st7789.configure_display(true); - } - - void ST7789Generic::set_backlight(uint8_t brightness) { - st7789.set_backlight(brightness); - } - - void ST7789Generic::configure_display(bool rotate180) { - st7789.configure_display(rotate180); - } -} diff --git a/libraries/generic_st7789/generic_st7789.hpp b/libraries/generic_st7789/generic_st7789.hpp deleted file mode 100644 index fd7e61d0..00000000 --- a/libraries/generic_st7789/generic_st7789.hpp +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include "drivers/st7789/st7789.hpp" -#include "libraries/pico_graphics/pico_graphics.hpp" - -namespace pimoroni { - - class ST7789Generic : public PicoGraphics { - private: - ST7789 st7789; - - public: - ST7789Generic(uint16_t width, uint16_t height, bool round=false, uint16_t *frame_buffer=nullptr) : - PicoGraphics(width, height, frame_buffer), - st7789(width, height, round, frame_buffer, PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_FRONT_CS, SPI_DEFAULT_MISO, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, SPI_BG_FRONT_PWM) { - this->frame_buffer = st7789.frame_buffer; - this->st7789.init(); - }; - - ST7789Generic(uint16_t width, uint16_t height, bool round, uint16_t *frame_buffer, BG_SPI_SLOT slot) : - PicoGraphics(width, height, frame_buffer), - st7789(width, height, round, frame_buffer, PIMORONI_SPI_DEFAULT_INSTANCE, st7789.get_slot_cs(slot), SPI_DEFAULT_MISO, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, st7789.get_slot_bl(slot)) { - this->frame_buffer = st7789.frame_buffer; - this->st7789.init(); - }; - - ST7789Generic(uint16_t width, uint16_t height, bool round, uint16_t *frame_buffer, - spi_inst_t *spi, - uint cs, uint dc, uint sck, uint mosi, uint bl = PIN_UNUSED) : - PicoGraphics(width, height, frame_buffer), - st7789(width, height, round, frame_buffer, spi, cs, dc, sck, mosi, bl) { - this->frame_buffer = st7789.frame_buffer; - this->st7789.init(); - }; - - spi_inst_t* get_spi() const; - int get_cs() const; - int get_dc() const; - int get_sck() const; - int get_mosi() const; - int get_bl() const; - - void update(); - [[deprecated("Use configure_display(true) instead.")]] void flip(); - void set_backlight(uint8_t brightness); - void configure_display(bool rotate180); - }; - -} diff --git a/libraries/jpegdec/JPEGDEC.cpp b/libraries/jpegdec/JPEGDEC.cpp new file mode 100644 index 00000000..fd3c82e0 --- /dev/null +++ b/libraries/jpegdec/JPEGDEC.cpp @@ -0,0 +1,203 @@ +// +// JPEG Decoder +// +// written by Larry Bank +// bitbank@pobox.com +// Arduino port started 8/2/2020 +// Original JPEG code written 26+ years ago :) +// The goal of this code is to decode baseline JPEG images +// using no more than 18K of RAM (if sent directly to an LCD display) +// +// Copyright 2020 BitBank Software, Inc. All Rights Reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//=========================================================================== +// +#include "JPEGDEC.h" + +// forward references +JPEG_STATIC int JPEGInit(JPEGIMAGE *pJPEG); +JPEG_STATIC int JPEGParseInfo(JPEGIMAGE *pPage, int bExtractThumb); +JPEG_STATIC void JPEGGetMoreData(JPEGIMAGE *pPage); +JPEG_STATIC int DecodeJPEG(JPEGIMAGE *pImage); + +// Include the C code which does the actual work +#include "jpeg.inl" + +void JPEGDEC::setPixelType(int iType) +{ + if (iType >= 0 && iType < INVALID_PIXEL_TYPE) + _jpeg.ucPixelType = (uint8_t)iType; + else + _jpeg.iError = JPEG_INVALID_PARAMETER; +} /* setPixelType() */ + +void JPEGDEC::setMaxOutputSize(int iMaxMCUs) +{ + if (iMaxMCUs < 1) + iMaxMCUs = 1; // don't allow invalid value + _jpeg.iMaxMCUs = iMaxMCUs; +} /* setMaxOutputSize() */ +// +// Memory initialization +// +int JPEGDEC::openRAM(uint8_t *pData, int iDataSize, JPEG_DRAW_CALLBACK *pfnDraw) +{ + memset(&_jpeg, 0, sizeof(JPEGIMAGE)); + _jpeg.ucMemType = JPEG_MEM_RAM; + _jpeg.pfnRead = readRAM; + _jpeg.pfnSeek = seekMem; + _jpeg.pfnDraw = pfnDraw; + _jpeg.pfnOpen = NULL; + _jpeg.pfnClose = NULL; + _jpeg.JPEGFile.iSize = iDataSize; + _jpeg.JPEGFile.pData = pData; + _jpeg.iMaxMCUs = 1000; // set to an unnaturally high value to start + return JPEGInit(&_jpeg); +} /* openRAM() */ + +int JPEGDEC::openFLASH(uint8_t *pData, int iDataSize, JPEG_DRAW_CALLBACK *pfnDraw) +{ + memset(&_jpeg, 0, sizeof(JPEGIMAGE)); + _jpeg.ucMemType = JPEG_MEM_FLASH; + _jpeg.pfnRead = readFLASH; + _jpeg.pfnSeek = seekMem; + _jpeg.pfnDraw = pfnDraw; + _jpeg.pfnOpen = NULL; + _jpeg.pfnClose = NULL; + _jpeg.JPEGFile.iSize = iDataSize; + _jpeg.JPEGFile.pData = pData; + _jpeg.iMaxMCUs = 1000; // set to an unnaturally high value to start + return JPEGInit(&_jpeg); +} /* openRAM() */ + +int JPEGDEC::getOrientation() +{ + return (int)_jpeg.ucOrientation; +} /* getOrientation() */ + +int JPEGDEC::getLastError() +{ + return _jpeg.iError; +} /* getLastError() */ + +int JPEGDEC::getWidth() +{ + return _jpeg.iWidth; +} /* getWidth() */ + +int JPEGDEC::getHeight() +{ + return _jpeg.iHeight; +} /* getHeight() */ + +int JPEGDEC::hasThumb() +{ + return (int)_jpeg.ucHasThumb; +} /* hasThumb() */ + +int JPEGDEC::getThumbWidth() +{ + return _jpeg.iThumbWidth; +} /* getThumbWidth() */ + +int JPEGDEC::getThumbHeight() +{ + return _jpeg.iThumbHeight; +} /* getThumbHeight() */ + +int JPEGDEC::getBpp() +{ + return (int)_jpeg.ucBpp; +} /* getBpp() */ + +int JPEGDEC::getSubSample() +{ + return (int)_jpeg.ucSubSample; +} /* getSubSample() */ + +// +// File (SD/MMC) based initialization +// +int JPEGDEC::open(const char *szFilename, JPEG_OPEN_CALLBACK *pfnOpen, JPEG_CLOSE_CALLBACK *pfnClose, JPEG_READ_CALLBACK *pfnRead, JPEG_SEEK_CALLBACK *pfnSeek, JPEG_DRAW_CALLBACK *pfnDraw) +{ + memset(&_jpeg, 0, sizeof(JPEGIMAGE)); + _jpeg.pfnRead = pfnRead; + _jpeg.pfnSeek = pfnSeek; + _jpeg.pfnDraw = pfnDraw; + _jpeg.pfnOpen = pfnOpen; + _jpeg.pfnClose = pfnClose; + _jpeg.iMaxMCUs = 1000; // set to an unnaturally high value to start + _jpeg.JPEGFile.fHandle = (*pfnOpen)(szFilename, &_jpeg.JPEGFile.iSize); + if (_jpeg.JPEGFile.fHandle == NULL) + return 0; + return JPEGInit(&_jpeg); + +} /* open() */ + +#ifdef FS_H +static int32_t FileRead(JPEGFILE *handle, uint8_t *buffer, int32_t length) +{ + return ((File *)(handle->fHandle))->read(buffer, length); +} +static int32_t FileSeek(JPEGFILE *handle, int32_t position) +{ + return ((File *)(handle->fHandle))->seek(position); +} +static void FileClose(void *handle) +{ + ((File *)handle)->close(); +} + +int JPEGDEC::open(File &file, JPEG_DRAW_CALLBACK *pfnDraw) +{ + if (!file) return 0; + memset(&_jpeg, 0, sizeof(JPEGIMAGE)); + _jpeg.pfnRead = FileRead; + _jpeg.pfnSeek = FileSeek; + _jpeg.pfnClose = FileClose; + _jpeg.pfnDraw = pfnDraw; + _jpeg.iMaxMCUs = 1000; + _jpeg.JPEGFile.fHandle = &file; + _jpeg.JPEGFile.iSize = file.size(); + return JPEGInit(&_jpeg); +} +#endif // FS_H + +void JPEGDEC::close() +{ + if (_jpeg.pfnClose) + (*_jpeg.pfnClose)(_jpeg.JPEGFile.fHandle); +} /* close() */ + +// +// Decode the image +// returns: +// 1 = good result +// 0 = error +// +int JPEGDEC::decode(int x, int y, int iOptions) +{ + _jpeg.iXOffset = x; + _jpeg.iYOffset = y; + _jpeg.iOptions = iOptions; + _jpeg.pDitherBuffer = nullptr; + return DecodeJPEG(&_jpeg); +} /* decode() */ + +// TODO PR these tweaks to https://github.com/bitbank2/JPEGDEC +int JPEGDEC::decodeDither(int x, int y, uint8_t *pDither, int iOptions) +{ + _jpeg.iXOffset = x; + _jpeg.iYOffset = y; + _jpeg.iOptions = iOptions; + _jpeg.pDitherBuffer = pDither; + return DecodeJPEG(&_jpeg); +} diff --git a/libraries/jpegdec/JPEGDEC.h b/libraries/jpegdec/JPEGDEC.h new file mode 100644 index 00000000..8f88ff33 --- /dev/null +++ b/libraries/jpegdec/JPEGDEC.h @@ -0,0 +1,262 @@ +// +// Copyright 2020 BitBank Software, Inc. All Rights Reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//=========================================================================== +// +#ifndef __JPEGDEC__ +#define __JPEGDEC__ +#if defined( __MACH__ ) || defined( __LINUX__ ) || defined( __MCUXPRESSO ) || defined( PICO_BUILD ) +#include +#include +#include +#include +#else +#include +#if !defined(HAL_ESP32_HAL_H_) && defined(__has_include) && __has_include() +#include +#endif +#endif +#ifndef PROGMEM +#define memcpy_P memcpy +#define PROGMEM +#endif +// +// JPEG Decoder +// Written by Larry Bank +// Copyright (c) 2020 BitBank Software, Inc. +// +// Designed to decode baseline JPEG images (8 or 24-bpp) +// using less than 22K of RAM +// + +/* Defines and variables */ +#define FILE_HIGHWATER 1536 +#define JPEG_FILE_BUF_SIZE 2048 +#define HUFF_TABLEN 273 +#define HUFF11SIZE (1<<11) +#define DC_TABLE_SIZE 1024 +#define DCTSIZE 64 +#define MAX_MCU_COUNT 6 +#define MAX_COMPS_IN_SCAN 4 +#define MAX_BUFFERED_PIXELS 2048 + +// Decoder options +#define JPEG_AUTO_ROTATE 1 +#define JPEG_SCALE_HALF 2 +#define JPEG_SCALE_QUARTER 4 +#define JPEG_SCALE_EIGHTH 8 +#define JPEG_LE_PIXELS 16 +#define JPEG_EXIF_THUMBNAIL 32 +#define JPEG_LUMA_ONLY 64 + +#define MCU0 (DCTSIZE * 0) +#define MCU1 (DCTSIZE * 1) +#define MCU2 (DCTSIZE * 2) +#define MCU3 (DCTSIZE * 3) +#define MCU4 (DCTSIZE * 4) +#define MCU5 (DCTSIZE * 5) + +// Pixel types (defaults to little endian RGB565) +enum { + RGB565_LITTLE_ENDIAN = 0, + RGB565_BIG_ENDIAN, + EIGHT_BIT_GRAYSCALE, + FOUR_BIT_DITHERED, + TWO_BIT_DITHERED, + ONE_BIT_DITHERED, + INVALID_PIXEL_TYPE +}; + +enum { + JPEG_MEM_RAM=0, + JPEG_MEM_FLASH +}; + +// Error codes returned by getLastError() +enum { + JPEG_SUCCESS = 0, + JPEG_INVALID_PARAMETER, + JPEG_DECODE_ERROR, + JPEG_UNSUPPORTED_FEATURE, + JPEG_INVALID_FILE +}; + +typedef struct buffered_bits +{ +unsigned char *pBuf; // buffer pointer +uint32_t ulBits; // buffered bits +uint32_t ulBitOff; // current bit offset +} BUFFERED_BITS; + +typedef struct jpeg_file_tag +{ + int32_t iPos; // current file position + int32_t iSize; // file size + uint8_t *pData; // memory file pointer + void * fHandle; // class pointer to File/SdFat or whatever you want +} JPEGFILE; + +typedef struct jpeg_draw_tag +{ + int x, y; // upper left corner of current MCU + int iWidth, iHeight; // size of this MCU + int iBpp; // bit depth of the pixels (8 or 16) + uint16_t *pPixels; // 16-bit pixels +} JPEGDRAW; + +// Callback function prototypes +typedef int32_t (JPEG_READ_CALLBACK)(JPEGFILE *pFile, uint8_t *pBuf, int32_t iLen); +typedef int32_t (JPEG_SEEK_CALLBACK)(JPEGFILE *pFile, int32_t iPosition); +typedef int (JPEG_DRAW_CALLBACK)(JPEGDRAW *pDraw); +typedef void * (JPEG_OPEN_CALLBACK)(const char *szFilename, int32_t *pFileSize); +typedef void (JPEG_CLOSE_CALLBACK)(void *pHandle); + +/* JPEG color component info */ +typedef struct _jpegcompinfo +{ +// These values are fixed over the whole image +// For compression, they must be supplied by the user interface +// for decompression, they are read from the SOF marker. +unsigned char component_needed; /* do we need the value of this component? */ +unsigned char component_id; /* identifier for this component (0..255) */ +unsigned char component_index; /* its index in SOF or cinfo->comp_info[] */ +//unsigned char h_samp_factor; /* horizontal sampling factor (1..4) */ +//unsigned char v_samp_factor; /* vertical sampling factor (1..4) */ +unsigned char quant_tbl_no; /* quantization table selector (0..3) */ +// These values may vary between scans +// For compression, they must be supplied by the user interface +// for decompression, they are read from the SOS marker. +unsigned char dc_tbl_no; /* DC entropy table selector (0..3) */ +unsigned char ac_tbl_no; /* AC entropy table selector (0..3) */ +// These values are computed during compression or decompression startup +//int true_comp_width; /* component's image width in samples */ +//int true_comp_height; /* component's image height in samples */ +// the above are the logical dimensions of the downsampled image +// These values are computed before starting a scan of the component +//int MCU_width; /* number of blocks per MCU, horizontally */ +//int MCU_height; /* number of blocks per MCU, vertically */ +//int MCU_blocks; /* MCU_width * MCU_height */ +//int downsampled_width; /* image width in samples, after expansion */ +//int downsampled_height; /* image height in samples, after expansion */ +// the above are the true_comp_xxx values rounded up to multiples of +// the MCU dimensions; these are the working dimensions of the array +// as it is passed through the DCT or IDCT step. NOTE: these values +// differ depending on whether the component is interleaved or not!! +// This flag is used only for decompression. In cases where some of the +// components will be ignored (eg grayscale output from YCbCr image), +// we can skip IDCT etc. computations for the unused components. +} JPEGCOMPINFO; + +// +// our private structure to hold a JPEG image decode state +// +typedef struct jpeg_image_tag +{ + int iWidth, iHeight; // image size + int iThumbWidth, iThumbHeight; // thumbnail size (if present) + int iThumbData; // offset to image data + int iXOffset, iYOffset; // placement on the display + uint8_t ucBpp, ucSubSample, ucHuffTableUsed; + uint8_t ucMode, ucOrientation, ucHasThumb, b11Bit; + uint8_t ucComponentsInScan, cApproxBitsLow, cApproxBitsHigh; + uint8_t iScanStart, iScanEnd, ucFF, ucNumComponents; + uint8_t ucACTable, ucDCTable, ucMaxACCol, ucMaxACRow; + uint8_t ucMemType, ucPixelType; + int iEXIF; // Offset to EXIF 'TIFF' file + int iError; + int iOptions; + int iVLCOff; // current VLC data offset + int iVLCSize; // current quantity of data in the VLC buffer + int iResInterval, iResCount; // restart interval + int iMaxMCUs; // max MCUs of pixels per JPEGDraw call + JPEG_READ_CALLBACK *pfnRead; + JPEG_SEEK_CALLBACK *pfnSeek; + JPEG_DRAW_CALLBACK *pfnDraw; + JPEG_OPEN_CALLBACK *pfnOpen; + JPEG_CLOSE_CALLBACK *pfnClose; + JPEGCOMPINFO JPCI[MAX_COMPS_IN_SCAN]; /* Max color components */ + JPEGFILE JPEGFile; + BUFFERED_BITS bb; + uint8_t *pDitherBuffer; // provided externally to do Floyd-Steinberg dithering + uint16_t usPixels[MAX_BUFFERED_PIXELS]; + int16_t sMCUs[DCTSIZE * MAX_MCU_COUNT]; // 4:2:0 needs 6 DCT blocks per MCU + int16_t sQuantTable[DCTSIZE*4]; // quantization tables + uint8_t ucFileBuf[JPEG_FILE_BUF_SIZE]; // holds temp data and pixel stack + uint8_t ucHuffDC[DC_TABLE_SIZE * 2]; // up to 2 'short' tables + uint16_t usHuffAC[HUFF11SIZE * 2]; +} JPEGIMAGE; + +#ifdef __cplusplus +#if defined(__has_include) && __has_include() +#include "FS.h" +#endif +#define JPEG_STATIC static +// +// The JPEGDEC class wraps portable C code which does the actual work +// +class JPEGDEC +{ + public: + int openRAM(uint8_t *pData, int iDataSize, JPEG_DRAW_CALLBACK *pfnDraw); + int openFLASH(uint8_t *pData, int iDataSize, JPEG_DRAW_CALLBACK *pfnDraw); + int open(const char *szFilename, JPEG_OPEN_CALLBACK *pfnOpen, JPEG_CLOSE_CALLBACK *pfnClose, JPEG_READ_CALLBACK *pfnRead, JPEG_SEEK_CALLBACK *pfnSeek, JPEG_DRAW_CALLBACK *pfnDraw); +#ifdef FS_H + int open(File &file, JPEG_DRAW_CALLBACK *pfnDraw); +#endif + void close(); + int decode(int x, int y, int iOptions); + int decodeDither(int x, int y, uint8_t *pDither, int iOptions); + int getOrientation(); + int getWidth(); + int getHeight(); + int getBpp(); + int getSubSample(); + int hasThumb(); + int getThumbWidth(); + int getThumbHeight(); + int getLastError(); + void setPixelType(int iType); // defaults to little endian + void setMaxOutputSize(int iMaxMCUs); + + private: + JPEGIMAGE _jpeg; +}; +#else +#define JPEG_STATIC +int JPEG_openRAM(JPEGIMAGE *pJPEG, uint8_t *pData, int iDataSize, JPEG_DRAW_CALLBACK *pfnDraw); +int JPEG_openFile(JPEGIMAGE *pJPEG, const char *szFilename, JPEG_DRAW_CALLBACK *pfnDraw); +int JPEG_getWidth(JPEGIMAGE *pJPEG); +int JPEG_getHeight(JPEGIMAGE *pJPEG); +int JPEG_decode(JPEGIMAGE *pJPEG, int x, int y, int iOptions); +int JPEG_decodeDither(JPEGIMAGE *pJPEG, uint8_t *pDither, int iOptions); +void JPEG_close(JPEGIMAGE *pJPEG); +int JPEG_getLastError(JPEGIMAGE *pJPEG); +int JPEG_getOrientation(JPEGIMAGE *pJPEG); +int JPEG_getBpp(JPEGIMAGE *pJPEG); +int JPEG_getSubSample(JPEGIMAGE *pJPEG); +int JPEG_hasThumb(JPEGIMAGE *pJPEG); +int JPEG_getThumbWidth(JPEGIMAGE *pJPEG); +int JPEG_getThumbHeight(JPEGIMAGE *pJPEG); +int JPEG_getLastError(JPEGIMAGE *pJPEG); +void JPEG_setPixelType(JPEGIMAGE *pJPEG, int iType); // defaults to little endian +void JPEG_setMaxOutputSize(JPEGIMAGE *pJPEG, int iMaxMCUs); +#endif // __cplusplus + +// Due to unaligned memory causing an exception, we have to do these macros the slow way +#define INTELSHORT(p) ((*p) + (*(p+1)<<8)) +#define INTELLONG(p) ((*p) + (*(p+1)<<8) + (*(p+2)<<16) + (*(p+3)<<24)) +#define MOTOSHORT(p) (((*(p))<<8) + (*(p+1))) +#define MOTOLONG(p) (((*p)<<24) + ((*(p+1))<<16) + ((*(p+2))<<8) + (*(p+3))) + +// Must be a 32-bit target processor +#define REGISTER_WIDTH 32 + +#endif // __JPEGDEC__ diff --git a/libraries/jpegdec/jpeg.inl b/libraries/jpegdec/jpeg.inl new file mode 100644 index 00000000..6618a35c --- /dev/null +++ b/libraries/jpegdec/jpeg.inl @@ -0,0 +1,3487 @@ +// +// JPEG Decoder +// +// written by Larry Bank +// bitbank@pobox.com +// Arduino port started 8/2/2020 +// Original JPEG code written 26+ years ago :) +// The goal of this code is to decode baseline JPEG images +// using no more than 18K of RAM (if sent directly to an LCD display) +// +// Copyright 2020 BitBank Software, Inc. All Rights Reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//=========================================================================== +// +#include "JPEGDEC.h" + +#if defined(ARM_MATH_CM4) || defined(ARM_MATH_CM7) +#define HAS_SIMD +#endif + +// forward references +static int JPEGInit(JPEGIMAGE *pJPEG); +static int JPEGParseInfo(JPEGIMAGE *pPage, int bExtractThumb); +static void JPEGGetMoreData(JPEGIMAGE *pPage); +static int DecodeJPEG(JPEGIMAGE *pImage); +static int32_t readRAM(JPEGFILE *pFile, uint8_t *pBuf, int32_t iLen); +static int32_t seekMem(JPEGFILE *pFile, int32_t iPosition); +static int32_t readFile(JPEGFILE *pFile, uint8_t *pBuf, int32_t iLen); +static int32_t seekFile(JPEGFILE *pFile, int32_t iPosition); +static void closeFile(void *handle); +static void JPEGDither(JPEGIMAGE *pJPEG, int iWidth, int iHeight); +/* JPEG tables */ +// zigzag ordering of DCT coefficients +static const unsigned char cZigZag[64] = {0,1,5,6,14,15,27,28, + 2,4,7,13,16,26,29,42, + 3,8,12,17,25,30,41,43, + 9,11,18,24,31,40,44,53, + 10,19,23,32,39,45,52,54, + 20,22,33,38,46,51,55,60, + 21,34,37,47,50,56,59,61, + 35,36,48,49,57,58,62,63}; + +// un-zigzag ordering +static const unsigned char cZigZag2[64] = {0,1,8,16,9,2,3,10, + 17,24,32,25,18,11,4,5, + 12,19,26,33,40,48,41,34, + 27,20,13,6,7,14,21,28, + 35,42,49,56,57,50,43,36, + 29,22,15,23,30,37,44,51, + 58,59,52,45,38,31,39,46, + 53,60,61,54,47,55,62,63}; + +// For AA&N IDCT method, multipliers are equal to quantization +// coefficients scaled by scalefactor[row]*scalefactor[col], where +// scalefactor[0] = 1 +// scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 +// For integer operation, the multiplier table is to be scaled by +// IFAST_SCALE_BITS. +static const int iScaleBits[64] = {16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, + 22725, 31521, 29692, 26722, 22725, 17855, 12299, 6270, + 21407, 29692, 27969, 25172, 21407, 16819, 11585, 5906, + 19266, 26722, 25172, 22654, 19266, 15137, 10426, 5315, + 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, + 12873, 17855, 16819, 15137, 12873, 10114, 6967, 3552, + 8867, 12299, 11585, 10426, 8867, 6967, 4799, 2446, + 4520, 6270, 5906, 5315, 4520, 3552, 2446, 1247}; +// +// Range clip and shift for RGB565 output +// input value is 0 to 255, then another 256 for overflow to FF, then 512 more for negative values wrapping around +// Trims a few instructions off the final output stage +// +static const uint8_t ucRangeTable[] = {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f, + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f, + 0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf, + 0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf, + 0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf, + 0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf, + 0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef, + 0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 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,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,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,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,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,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,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,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, + 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,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,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,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,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,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,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, + 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f, + 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f, + 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f, + 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f}; + +// +// Convert 8-bit grayscale into RGB565 +// +static const uint16_t usGrayTo565[] = {0x0000,0x0000,0x0000,0x0000,0x0020,0x0020,0x0020,0x0020, // 0 + 0x0841,0x0841,0x0841,0x0841,0x0861,0x0861,0x0861,0x0861, + 0x1082,0x1082,0x1082,0x1082,0x10a2,0x10a2,0x10a2,0x10a2, + 0x18c3,0x18c3,0x18c3,0x18c3,0x18e3,0x18e3,0x18e3,0x18e3, + 0x2104,0x2104,0x2104,0x2104,0x2124,0x2124,0x2124,0x2124, + 0x2945,0x2945,0x2945,0x2945,0x2965,0x2965,0x2965,0x2965, + 0x3186,0x3186,0x3186,0x3186,0x31a6,0x31a6,0x31a6,0x31a6, + 0x39c7,0x39c7,0x39c7,0x39c7,0x39e7,0x39e7,0x39e7,0x39e7, + 0x4208,0x4208,0x4208,0x4208,0x4228,0x4228,0x4228,0x4228, + 0x4a49,0x4a49,0x4a49,0x4a49,0x4a69,0x4a69,0x4a69,0x4a69, + 0x528a,0x528a,0x528a,0x528a,0x52aa,0x52aa,0x52aa,0x52aa, + 0x5acb,0x5acb,0x5acb,0x5acb,0x5aeb,0x5aeb,0x5aeb,0x5aeb, + 0x630c,0x630c,0x630c,0x630c,0x632c,0x632c,0x632c,0x632c, + 0x6b4d,0x6b4d,0x6b4d,0x6b4d,0x6b6d,0x6b6d,0x6b6d,0x6b6d, + 0x738e,0x738e,0x738e,0x738e,0x73ae,0x73ae,0x73ae,0x73ae, + 0x7bcf,0x7bcf,0x7bcf,0x7bcf,0x7bef,0x7bef,0x7bef,0x7bef, + 0x8410,0x8410,0x8410,0x8410,0x8430,0x8430,0x8430,0x8430, + 0x8c51,0x8c51,0x8c51,0x8c51,0x8c71,0x8c71,0x8c71,0x8c71, + 0x9492,0x9492,0x9492,0x9492,0x94b2,0x94b2,0x94b2,0x94b2, + 0x9cd3,0x9cd3,0x9cd3,0x9cd3,0x9cf3,0x9cf3,0x9cf3,0x9cf3, + 0xa514,0xa514,0xa514,0xa514,0xa534,0xa534,0xa534,0xa534, + 0xad55,0xad55,0xad55,0xad55,0xad75,0xad75,0xad75,0xad75, + 0xb596,0xb596,0xb596,0xb596,0xb5b6,0xb5b6,0xb5b6,0xb5b6, + 0xbdd7,0xbdd7,0xbdd7,0xbdd7,0xbdf7,0xbdf7,0xbdf7,0xbdf7, + 0xc618,0xc618,0xc618,0xc618,0xc638,0xc638,0xc638,0xc638, + 0xce59,0xce59,0xce59,0xce59,0xce79,0xce79,0xce79,0xce79, + 0xd69a,0xd69a,0xd69a,0xd69a,0xd6ba,0xd6ba,0xd6ba,0xd6ba, + 0xdedb,0xdedb,0xdedb,0xdedb,0xdefb,0xdefb,0xdefb,0xdefb, + 0xe71c,0xe71c,0xe71c,0xe71c,0xe73c,0xe73c,0xe73c,0xe73c, + 0xef5d,0xef5d,0xef5d,0xef5d,0xef7d,0xef7d,0xef7d,0xef7d, + 0xf79e,0xf79e,0xf79e,0xf79e,0xf7be,0xf7be,0xf7be,0xf7be, + 0xffdf,0xffdf,0xffdf,0xffdf,0xffff,0xffff,0xffff,0xffff}; +// +// Clip and convert red value into 5-bits for RGB565 +// +static const uint16_t usRangeTableR[] = {0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // 0 + 0x0800,0x0800,0x0800,0x0800,0x0800,0x0800,0x0800,0x0800, + 0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000, + 0x1800,0x1800,0x1800,0x1800,0x1800,0x1800,0x1800,0x1800, + 0x2000,0x2000,0x2000,0x2000,0x2000,0x2000,0x2000,0x2000, + 0x2800,0x2800,0x2800,0x2800,0x2800,0x2800,0x2800,0x2800, + 0x3000,0x3000,0x3000,0x3000,0x3000,0x3000,0x3000,0x3000, + 0x3800,0x3800,0x3800,0x3800,0x3800,0x3800,0x3800,0x3800, + 0x4000,0x4000,0x4000,0x4000,0x4000,0x4000,0x4000,0x4000, + 0x4800,0x4800,0x4800,0x4800,0x4800,0x4800,0x4800,0x4800, + 0x5000,0x5000,0x5000,0x5000,0x5000,0x5000,0x5000,0x5000, + 0x5800,0x5800,0x5800,0x5800,0x5800,0x5800,0x5800,0x5800, + 0x6000,0x6000,0x6000,0x6000,0x6000,0x6000,0x6000,0x6000, + 0x6800,0x6800,0x6800,0x6800,0x6800,0x6800,0x6800,0x6800, + 0x7000,0x7000,0x7000,0x7000,0x7000,0x7000,0x7000,0x7000, + 0x7800,0x7800,0x7800,0x7800,0x7800,0x7800,0x7800,0x7800, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000, + 0x8800,0x8800,0x8800,0x8800,0x8800,0x8800,0x8800,0x8800, + 0x9000,0x9000,0x9000,0x9000,0x9000,0x9000,0x9000,0x9000, + 0x9800,0x9800,0x9800,0x9800,0x9800,0x9800,0x9800,0x9800, + 0xa000,0xa000,0xa000,0xa000,0xa000,0xa000,0xa000,0xa000, + 0xa800,0xa800,0xa800,0xa800,0xa800,0xa800,0xa800,0xa800, + 0xb000,0xb000,0xb000,0xb000,0xb000,0xb000,0xb000,0xb000, + 0xb800,0xb800,0xb800,0xb800,0xb800,0xb800,0xb800,0xb800, + 0xc000,0xc000,0xc000,0xc000,0xc000,0xc000,0xc000,0xc000, + 0xc800,0xc800,0xc800,0xc800,0xc800,0xc800,0xc800,0xc800, + 0xd000,0xd000,0xd000,0xd000,0xd000,0xd000,0xd000,0xd000, + 0xd800,0xd800,0xd800,0xd800,0xd800,0xd800,0xd800,0xd800, + 0xe000,0xe000,0xe000,0xe000,0xe000,0xe000,0xe000,0xe000, + 0xe800,0xe800,0xe800,0xe800,0xe800,0xe800,0xe800,0xe800, + 0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000, + 0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800, + 0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800, // 256 + 0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800, + 0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800, + 0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800, + 0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800, + 0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800, + 0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800, + 0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800, + 0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800, + 0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800, + 0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800, + 0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800, + 0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800, + 0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800, + 0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800, + 0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800, + 0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800, + 0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800, + 0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800, + 0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800, + 0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800, + 0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800, + 0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800, + 0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800, + 0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800, + 0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800, + 0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800, + 0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800, + 0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800, + 0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800, + 0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800, + 0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 512 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 768 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; +// +// Clip and convert green value into 5-bits for RGB565 +// +static const uint16_t usRangeTableG[] = {0x0000,0x0000,0x0000,0x0000,0x0020,0x0020,0x0020,0x0020, // 0 + 0x0040,0x0040,0x0040,0x0040,0x0060,0x0060,0x0060,0x0060, + 0x0080,0x0080,0x0080,0x0080,0x00a0,0x00a0,0x00a0,0x00a0, + 0x00c0,0x00c0,0x00c0,0x00c0,0x00e0,0x00e0,0x00e0,0x00e0, + 0x0100,0x0100,0x0100,0x0100,0x0120,0x0120,0x0120,0x0120, + 0x0140,0x0140,0x0140,0x0140,0x0160,0x0160,0x0160,0x0160, + 0x0180,0x0180,0x0180,0x0180,0x01a0,0x01a0,0x01a0,0x01a0, + 0x01c0,0x01c0,0x01c0,0x01c0,0x01e0,0x01e0,0x01e0,0x01e0, + 0x0200,0x0200,0x0200,0x0200,0x0220,0x0220,0x0220,0x0220, + 0x0240,0x0240,0x0240,0x0240,0x0260,0x0260,0x0260,0x0260, + 0x0280,0x0280,0x0280,0x0280,0x02a0,0x02a0,0x02a0,0x02a0, + 0x02c0,0x02c0,0x02c0,0x02c0,0x02e0,0x02e0,0x02e0,0x02e0, + 0x0300,0x0300,0x0300,0x0300,0x0320,0x0320,0x0320,0x0320, + 0x0340,0x0340,0x0340,0x0340,0x0360,0x0360,0x0360,0x0360, + 0x0380,0x0380,0x0380,0x0380,0x03a0,0x03a0,0x03a0,0x03a0, + 0x03c0,0x03c0,0x03c0,0x03c0,0x03e0,0x03e0,0x03e0,0x03e0, + 0x0400,0x0400,0x0400,0x0400,0x0420,0x0420,0x0420,0x0420, + 0x0440,0x0440,0x0440,0x0440,0x0460,0x0460,0x0460,0x0460, + 0x0480,0x0480,0x0480,0x0480,0x04a0,0x04a0,0x04a0,0x04a0, + 0x04c0,0x04c0,0x04c0,0x04c0,0x04e0,0x04e0,0x04e0,0x04e0, + 0x0500,0x0500,0x0500,0x0500,0x0520,0x0520,0x0520,0x0520, + 0x0540,0x0540,0x0540,0x0540,0x0560,0x0560,0x0560,0x0560, + 0x0580,0x0580,0x0580,0x0580,0x05a0,0x05a0,0x05a0,0x05a0, + 0x05c0,0x05c0,0x05c0,0x05c0,0x05e0,0x05e0,0x05e0,0x05e0, + 0x0600,0x0600,0x0600,0x0600,0x0620,0x0620,0x0620,0x0620, + 0x0640,0x0640,0x0640,0x0640,0x0660,0x0660,0x0660,0x0660, + 0x0680,0x0680,0x0680,0x0680,0x06a0,0x06a0,0x06a0,0x06a0, + 0x06c0,0x06c0,0x06c0,0x06c0,0x06e0,0x06e0,0x06e0,0x06e0, + 0x0700,0x0700,0x0700,0x0700,0x0720,0x0720,0x0720,0x0720, + 0x0740,0x0740,0x0740,0x0740,0x0760,0x0760,0x0760,0x0760, + 0x0780,0x0780,0x0780,0x0780,0x07a0,0x07a0,0x07a0,0x07a0, + 0x07c0,0x07c0,0x07c0,0x07c0,0x07e0,0x07e0,0x07e0,0x07e0, + 0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0, // 256 + 0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0, + 0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0, + 0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0, + 0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0, + 0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0, + 0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0, + 0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0, + 0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0, + 0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0, + 0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0, + 0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0, + 0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0, + 0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0, + 0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0, + 0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0, + 0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0, + 0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0, + 0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0, + 0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0, + 0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0, + 0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0, + 0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0, + 0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0, + 0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0, + 0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0, + 0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0, + 0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0, + 0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0, + 0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0, + 0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0, + 0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0,0x07e0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 512 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 768 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; +// +// Clip and convert blue value into 5-bits for RGB565 +// +static const uint16_t usRangeTableB[] = {0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // 0 + 0x0001,0x0001,0x0001,0x0001,0x0001,0x0001,0x0001,0x0001, + 0x0002,0x0002,0x0002,0x0002,0x0002,0x0002,0x0002,0x0002, + 0x0003,0x0003,0x0003,0x0003,0x0003,0x0003,0x0003,0x0003, + 0x0004,0x0004,0x0004,0x0004,0x0004,0x0004,0x0004,0x0004, + 0x0005,0x0005,0x0005,0x0005,0x0005,0x0005,0x0005,0x0005, + 0x0006,0x0006,0x0006,0x0006,0x0006,0x0006,0x0006,0x0006, + 0x0007,0x0007,0x0007,0x0007,0x0007,0x0007,0x0007,0x0007, + 0x0008,0x0008,0x0008,0x0008,0x0008,0x0008,0x0008,0x0008, + 0x0009,0x0009,0x0009,0x0009,0x0009,0x0009,0x0009,0x0009, + 0x000a,0x000a,0x000a,0x000a,0x000a,0x000a,0x000a,0x000a, + 0x000b,0x000b,0x000b,0x000b,0x000b,0x000b,0x000b,0x000b, + 0x000c,0x000c,0x000c,0x000c,0x000c,0x000c,0x000c,0x000c, + 0x000d,0x000d,0x000d,0x000d,0x000d,0x000d,0x000d,0x000d, + 0x000e,0x000e,0x000e,0x000e,0x000e,0x000e,0x000e,0x000e, + 0x000f,0x000f,0x000f,0x000f,0x000f,0x000f,0x000f,0x000f, + 0x0010,0x0010,0x0010,0x0010,0x0010,0x0010,0x0010,0x0010, + 0x0011,0x0011,0x0011,0x0011,0x0011,0x0011,0x0011,0x0011, + 0x0012,0x0012,0x0012,0x0012,0x0012,0x0012,0x0012,0x0012, + 0x0013,0x0013,0x0013,0x0013,0x0013,0x0013,0x0013,0x0013, + 0x0014,0x0014,0x0014,0x0014,0x0014,0x0014,0x0014,0x0014, + 0x0015,0x0015,0x0015,0x0015,0x0015,0x0015,0x0015,0x0015, + 0x0016,0x0016,0x0016,0x0016,0x0016,0x0016,0x0016,0x0016, + 0x0017,0x0017,0x0017,0x0017,0x0017,0x0017,0x0017,0x0017, + 0x0018,0x0018,0x0018,0x0018,0x0018,0x0018,0x0018,0x0018, + 0x0019,0x0019,0x0019,0x0019,0x0019,0x0019,0x0019,0x0019, + 0x001a,0x001a,0x001a,0x001a,0x001a,0x001a,0x001a,0x001a, + 0x001b,0x001b,0x001b,0x001b,0x001b,0x001b,0x001b,0x001b, + 0x001c,0x001c,0x001c,0x001c,0x001c,0x001c,0x001c,0x001c, + 0x001d,0x001d,0x001d,0x001d,0x001d,0x001d,0x001d,0x001d, + 0x001e,0x001e,0x001e,0x001e,0x001e,0x001e,0x001e,0x001e, + 0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f, + 0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f, // 256 + 0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f, + 0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f, + 0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f, + 0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f, + 0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f, + 0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f, + 0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f, + 0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f, + 0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f, + 0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f, + 0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f, + 0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f, + 0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f, + 0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f, + 0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f, + 0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f, + 0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f, + 0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f, + 0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f, + 0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f, + 0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f, + 0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f, + 0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f, + 0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f, + 0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f, + 0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f, + 0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f, + 0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f, + 0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f, + 0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f, + 0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f,0x001f, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 512 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 768 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; +#if defined (__MACH__) || defined( __LINUX__ ) || defined( __MCUXPRESSO ) || defined( PICO_BUILD ) +// +// API for C +// + +// +// Memory initialization +// +int JPEG_openRAM(JPEGIMAGE *pJPEG, uint8_t *pData, int iDataSize, JPEG_DRAW_CALLBACK *pfnDraw) +{ + memset(pJPEG, 0, sizeof(JPEGIMAGE)); + pJPEG->ucMemType = JPEG_MEM_RAM; + pJPEG->pfnRead = readRAM; + pJPEG->pfnSeek = seekMem; + pJPEG->pfnDraw = pfnDraw; + pJPEG->pfnOpen = NULL; + pJPEG->pfnClose = NULL; + pJPEG->JPEGFile.iSize = iDataSize; + pJPEG->JPEGFile.pData = pData; + pJPEG->iMaxMCUs = 1000; // set to an unnaturally high value to start + return JPEGInit(pJPEG); +} /* JPEG_openRAM() */ +// +// File initialization +// +/* +int JPEG_openFile(JPEGIMAGE *pJPEG, const char *szFilename, JPEG_DRAW_CALLBACK *pfnDraw) +{ + memset(pJPEG, 0, sizeof(JPEGIMAGE)); + pJPEG->ucMemType = JPEG_MEM_RAM; + pJPEG->pfnRead = readFile; + pJPEG->pfnSeek = seekFile; + pJPEG->pfnDraw = pfnDraw; + pJPEG->pfnOpen = NULL; + pJPEG->pfnClose = closeFile; + pJPEG->iMaxMCUs = 1000; // set to an unnaturally high value to start + pJPEG->JPEGFile.fHandle = fopen(szFilename, "r+b"); + if (pJPEG->JPEGFile.fHandle == NULL) + return 0; + fseek((FILE *)pJPEG->JPEGFile.fHandle, 0, SEEK_END); + pJPEG->JPEGFile.iSize = (int)ftell((FILE *)pJPEG->JPEGFile.fHandle); + fseek((FILE *)pJPEG->JPEGFile.fHandle, 0, SEEK_SET); + return JPEGInit(pJPEG); +} *//* JPEG_openFile() */ + +int JPEG_getLastError(JPEGIMAGE *pJPEG) +{ + return pJPEG->iError; +} /* JPEG_getLastError() */ + +int JPEG_getWidth(JPEGIMAGE *pJPEG) +{ + return pJPEG->iWidth; +} /* JPEG_getWidth() */ + +int JPEG_getHeight(JPEGIMAGE *pJPEG) +{ + return pJPEG->iHeight; +} /* JPEG_getHeight() */ + +int JPEG_getOrientation(JPEGIMAGE *pJPEG) +{ + return (int)pJPEG->ucOrientation; +} /* JPEG_getOrientation() */ + +int JPEG_getBpp(JPEGIMAGE *pJPEG) +{ + return (int)pJPEG->ucBpp; +} /* JPEG_getBpp() */ +int JPEG_getSubSample(JPEGIMAGE *pJPEG) +{ + return (int)pJPEG->ucSubSample; +} /* JPEG_getSubSample() */ +int JPEG_hasThumb(JPEGIMAGE *pJPEG) +{ + return (int)pJPEG->ucHasThumb; +} /* JPEG_hasThumb() */ +int JPEG_getThumbWidth(JPEGIMAGE *pJPEG) +{ + return pJPEG->iThumbWidth; +} /* JPEG_getThumbWidth() */ +int JPEG_getThumbHeight(JPEGIMAGE *pJPEG) +{ + return pJPEG->iThumbHeight; +} /* JPEG_getThumbHeight() */ +void JPEG_setPixelType(JPEGIMAGE *pJPEG, int iType) +{ + pJPEG->ucPixelType = (uint8_t)iType; +} /* JPEG_setPixelType() */ +void JPEG_setMaxOutputSize(JPEGIMAGE *pJPEG, int iMaxMCUs) +{ + if (iMaxMCUs < 1) + iMaxMCUs = 1; // don't allow invalid value + pJPEG->iMaxMCUs = iMaxMCUs; +} /* JPEG_setMaxOutputSize() */ + +int JPEG_decode(JPEGIMAGE *pJPEG, int x, int y, int iOptions) +{ + pJPEG->iXOffset = x; + pJPEG->iYOffset = y; + pJPEG->iOptions = iOptions; + return DecodeJPEG(pJPEG); +} /* JPEG_decode() */ + +int JPEG_decodeDither(JPEGIMAGE *pJPEG, uint8_t *pDither, int iOptions) +{ + pJPEG->iOptions = iOptions; + pJPEG->pDitherBuffer = pDither; + return DecodeJPEG(pJPEG); +} /* JPEG_decodeDither() */ + +void JPEG_close(JPEGIMAGE *pJPEG) +{ + if (pJPEG->pfnClose) + (*pJPEG->pfnClose)(pJPEG->JPEGFile.fHandle); +} /* JPEG_close() */ + +#endif // !__cplusplus +// +// Helper functions for memory based images +// +static int32_t readRAM(JPEGFILE *pFile, uint8_t *pBuf, int32_t iLen) +{ + int32_t iBytesRead; + + iBytesRead = iLen; + if ((pFile->iSize - pFile->iPos) < iLen) + iBytesRead = pFile->iSize - pFile->iPos; + if (iBytesRead <= 0) + return 0; + memcpy(pBuf, &pFile->pData[pFile->iPos], iBytesRead); + pFile->iPos += iBytesRead; + return iBytesRead; +} /* readRAM() */ + +static int32_t readFLASH(JPEGFILE *pFile, uint8_t *pBuf, int32_t iLen) +{ + int32_t iBytesRead; + + iBytesRead = iLen; + if ((pFile->iSize - pFile->iPos) < iLen) + iBytesRead = pFile->iSize - pFile->iPos; + if (iBytesRead <= 0) + return 0; + memcpy_P(pBuf, &pFile->pData[pFile->iPos], iBytesRead); + pFile->iPos += iBytesRead; + return iBytesRead; +} /* readFLASH() */ + +static int32_t seekMem(JPEGFILE *pFile, int32_t iPosition) +{ + if (iPosition < 0) iPosition = 0; + else if (iPosition >= pFile->iSize) iPosition = pFile->iSize-1; + pFile->iPos = iPosition; + return iPosition; +} /* seekMem() */ + +#if defined (__MACH__) || defined( __LINUX__ ) || defined( __MCUXPRESSO ) || defined( PICO_BUILD ) + +static void closeFile(void *handle) +{ + fclose((FILE *)handle); +} /* closeFile() */ + +static int32_t seekFile(JPEGFILE *pFile, int32_t iPosition) +{ + if (iPosition < 0) iPosition = 0; + else if (iPosition >= pFile->iSize) iPosition = pFile->iSize-1; + pFile->iPos = iPosition; + fseek((FILE *)pFile->fHandle, iPosition, SEEK_SET); + return iPosition; +} /* seekFile() */ + +static int32_t readFile(JPEGFILE *pFile, uint8_t *pBuf, int32_t iLen) +{ + int32_t iBytesRead; + + iBytesRead = iLen; + if ((pFile->iSize - pFile->iPos) < iLen) + iBytesRead = pFile->iSize - pFile->iPos; + if (iBytesRead <= 0) + return 0; + iBytesRead = (int)fread(pBuf, 1, iBytesRead, (FILE *)pFile->fHandle); + pFile->iPos += iBytesRead; + return iBytesRead; +} /* readFile() */ + +#endif // __LINUX__ +// +// The following functions are written in plain C and have no +// 3rd party dependencies, not even the C runtime library +// +// +// Initialize a JPEG file and callback access from a file on SD or memory +// returns 1 for success, 0 for failure +// Fills in the basic image info fields of the JPEGIMAGE structure +// +static int JPEGInit(JPEGIMAGE *pJPEG) +{ + return JPEGParseInfo(pJPEG, 0); // gather info for image +} /* JPEGInit() */ +// +// Unpack the Huffman tables +// +static int JPEGGetHuffTables(uint8_t *pBuf, int iLen, JPEGIMAGE *pJPEG) +{ + int i, j, iOffset, iTableOffset; + uint8_t ucTable, *pHuffVals; + + iOffset = 0; + pHuffVals = (uint8_t *)pJPEG->usPixels; // temp holding area to save RAM + while (iLen > 17) // while there are tables to copy (we may have combined more than 1 table together) + { + ucTable = pBuf[iOffset++]; // get table index + if (ucTable & 0x10) // convert AC offset of 0x10 into offset of 4 + ucTable ^= 0x14; + pJPEG->ucHuffTableUsed |= (1 << ucTable); // mark this table as being defined + if (ucTable <= 7) // tables are 0-3, AC+DC + { + iTableOffset = ucTable * HUFF_TABLEN; + j = 0; // total bits + for (i=0; i<16; i++) + { + j += pBuf[iOffset]; + pHuffVals[iTableOffset+i] = pBuf[iOffset++]; + } + iLen -= 17; // subtract length of bit lengths + if (j == 0 || j > 256 || j > iLen) // bogus bit lengths + { + return -1; + } + iTableOffset += 16; + for (i=0; ib11Bit = 1; // indicate we're using the bigger A/C decode tables + // first do DC components (up to 4 tables of 12-bit codes) + // we can save time and memory for the DC codes by knowing that there exist short codes (<= 6 bits) + // and long codes (>6 bits, but the first 5 bits are 1's). This allows us to create 2 tables: a 6-bit and 7 or 8-bit + // to handle any DC codes + iMaxLength = 12; // assume DC codes can be 12-bits + iMaxMask = 0x7f; // lower 7 bits after truncate 5 leading 1's + if (pJPEG->ucMode == 0xc3) // create 13-bit tables for lossless mode + { + iMaxLength = 13; + iMaxMask = 0xff; + } + for (iTable = 0; iTable < 2; iTable++) + { + if (pJPEG->ucHuffTableUsed & (1<huffdcFast[iTable] = (int *)PILIOAlloc(0x180); // short table = 128 bytes, long table = 256 bytes + pucShort = (unsigned char *)&pJPEG->ucHuffDC[iTable*DC_TABLE_SIZE]; + // pJPEG->huffdc[iTable] = pJPEG->huffdcFast[iTable] + 0x20; // 0x20 longs = 128 bytes + pucLong = (unsigned char *)&pJPEG->ucHuffDC[iTable*DC_TABLE_SIZE + 128]; + pBits = &pJPEG->ucHuffVals[iTable * HUFF_TABLEN]; + p = pBits; + p += 16; // point to bit data + cc = 0; // start with a code of 0 + for (iBitNum = 1; iBitNum <= 16; iBitNum++) + { + iLen = *pBits++; // get number of codes for this bit length + if (iBitNum > iMaxLength && iLen > 0) // we can't handle codes longer a certain length + { + return -1; + } + while (iLen) + { + // if (iBitNum > 6) // do long table + if ((cc >> (iBitNum-5)) == 0x1f) // first 5 bits are 1 - use long table + { + count = iMaxLength - iBitNum; + codestart = cc << count; + pucTable = &pucLong[codestart & iMaxMask]; // use lower 7/8 bits of code + } + else // do short table + { + count = 6 - iBitNum; + if (count < 0) + return -1; // DEBUG - something went wrong + codestart = cc << count; + pucTable = &pucShort[codestart]; + } + ucCode = *p++; // get actual huffman code + if (ucCode == 16 && pJPEG->ucMode == 0xc3) // lossless mode + { + // in lossless mode, this code won't fit in 4 bits, so save it's length in the next slot + ucCode = 255; + pucLong[256] = (unsigned char)iBitNum; + } + // does precalculating the DC value save time on ARM? +#ifndef USE_ARM_ASM + if (ucCode != 0 && (ucCode + iBitNum) <= 6 && pJPEG->ucMode != 0xc2) // we can fit the magnitude value in the code lookup (not for progressive) + { + int k, iLoop; + unsigned char ucCoeff; + unsigned char *d = &pucTable[512]; + unsigned char ucMag = ucCode; + ucCode |= ((iBitNum+ucCode) << 4); // add magnitude bits to length + repeat = 1<ucHuffTableUsed & (1<<(iTable+4))) // if this table is defined + { + pBits = &pJPEG->ucHuffVals[(iTable+4) * HUFF_TABLEN]; + p = pBits; + p += 16; // point to bit data + pShort = &pJPEG->usHuffAC[iTable*HUFF11SIZE]; + pLong = &pJPEG->usHuffAC[iTable*HUFF11SIZE + 1024]; // long codes start here + cc = 0; // start with a code of 0 + // construct the decode table + for (iBitNum = 1; iBitNum <= 16; iBitNum++) + { + iLen = *pBits++; // get number of codes for this bit length + while (iLen) + { + if ((cc >> (iBitNum-4)) == 0xf) // first 4 bits are 1 - use long table + { + count = 16 - iBitNum; + codestart = cc << count; + pTable = &pLong[codestart & 0xfff]; // use lower 12 bits of code + } + else + { + count = 12 - iBitNum; + if (count < 0) // a 13-bit? code - that doesn't fit our optimized scheme, see if we can do a bigger table version + { + return -1; // DEBUG - fatal error, we currently don't support it + } + codestart = cc << count; + pTable = &pShort[codestart]; // 11 bits or shorter + } + code = *p++; // get actual huffman code + if (bThumbnail && code != 0) // add "extra" bits to code length since we skip these codes + { + // get rid of extra bits in code and add increment (1) for AC index + code = ((iBitNum+(code & 0xf)) << 8) | ((code >> 4)+1); + } + else + { + code |= (iBitNum << 8); + } + if (count) // do it as dwords to save time + { + repeat = 1 << (count-1); // store as dwords (/2) + ul = code | (code << 16); + pLongTable = (uint32_t *)pTable; + for (j=0; jusPixels; + for (j=0; j<4; j++) + { + if (pJPEG->ucHuffTableUsed & (1 << j)) + iTablesUsed++; + } + // first do DC components (up to 4 tables of 12-bit codes) + // we can save time and memory for the DC codes by knowing that there exist short codes (<= 6 bits) + // and long codes (>6 bits, but the first 5 bits are 1's). This allows us to create 2 tables: a 6-bit and 7 or 8-bit + // to handle any DC codes + iMaxLength = 12; // assume DC codes can be 12-bits + iMaxMask = 0x7f; // lower 7 bits after truncate 5 leading 1's + for (iTable = 0; iTable < 4; iTable++) + { + if (pJPEG->ucHuffTableUsed & (1 << iTable)) + { + // pJPEG->huffdcFast[iTable] = (int *)PILIOAlloc(0x180); // short table = 128 bytes, long table = 256 bytes + pucShort = &pJPEG->ucHuffDC[iTable*DC_TABLE_SIZE]; + // pJPEG->huffdc[iTable] = pJPEG->huffdcFast[iTable] + 0x20; // 0x20 longs = 128 bytes + pucLong = &pJPEG->ucHuffDC[iTable*DC_TABLE_SIZE + 128]; + pBits = &pHuffVals[iTable * HUFF_TABLEN]; + p = pBits; + p += 16; // point to bit data + cc = 0; // start with a code of 0 + for (iBitNum = 1; iBitNum <= 16; iBitNum++) + { + iLen = *pBits++; // get number of codes for this bit length + if (iBitNum > iMaxLength && iLen > 0) // we can't handle codes longer a certain length + { + return 0; + } + while (iLen) + { + // if (iBitNum > 6) // do long table + if ((cc >> (iBitNum-5)) == 0x1f) // first 5 bits are 1 - use long table + { + count = iMaxLength - iBitNum; + codestart = cc << count; + pucTable = &pucLong[codestart & iMaxMask]; // use lower 7/8 bits of code + } + else // do short table + { + count = 6 - iBitNum; + if (count < 0) + return 0; // DEBUG - something went wrong + codestart = cc << count; + pucTable = &pucShort[codestart]; + } + ucCode = *p++; // get actual huffman code + // does precalculating the DC value save time on ARM? +#ifndef USE_ARM_ASM + if (ucCode != 0 && (ucCode + iBitNum) <= 6 && pJPEG->ucMode != 0xc2) // we can fit the magnitude value in the code lookup (not for progressive) + { + int k, iLoop; + unsigned char ucCoeff; + unsigned char *d = &pucTable[512]; + unsigned char ucMag = ucCode; + ucCode |= ((iBitNum+ucCode) << 4); // add magnitude bits to length + repeat = 1<ucHuffTableUsed & (1 << (iTable+4))) // if this table is defined + { + pBits = &pHuffVals[(iTable+4) * HUFF_TABLEN]; + p = pBits; + p += 16; // point to bit data + pShort = &pJPEG->usHuffAC[iTable*HUFF11SIZE]; + pLong = &pJPEG->usHuffAC[iTable*HUFF11SIZE + 1024]; + cc = 0; // start with a code of 0 + // construct the decode table + for (iBitNum = 1; iBitNum <= 16; iBitNum++) + { + iLen = *pBits++; // get number of codes for this bit length + while (iLen) + { + if ((cc >> (iBitNum-6)) == 0x3f) // first 6 bits are 1 - use long table + { + count = 16 - iBitNum; + codestart = cc << count; + pTable = &pLong[codestart & 0x3ff]; // use lower 10 bits of code + } + else + { + count = 10 - iBitNum; + if (count < 0) // an 11/12-bit? code - that doesn't fit our optimized scheme, see if we can do a bigger table version + { + if (count == -1 && iTablesUsed <= 4) // we need to create "slow" tables + { // DEBUG +// j = JPEGMakeHuffTables_Slow(pJPEG, bThumbnail); + return 0; + } + else + return 0; // DEBUG - fatal error, more than 2 big tables we currently don't support + } + codestart = cc << count; + pTable = &pShort[codestart]; // 10 bits or shorter + } + code = *p++; // get actual huffman code + if (bThumbnail && code != 0) // add "extra" bits to code length since we skip these codes + { + // get rid of extra bits in code and add increment (1) for AC index + code = ((iBitNum+(code & 0xf)) << 8) | ((code >> 4)+1); + } +#ifdef BOGUS // precalculating the AC coeff makes it run slightly slower + else if ((code & 0xf) != 0 && (code + iBitNum) <= 10) // we can fit the magnitude value + huffman code in a single read + { + int k, iLoop; + unsigned short usCoeff; + unsigned short *d = &pTable[4096]; // use unused table slots 2+3 for extra coeff data + unsigned char ucMag = (unsigned char)(code & 0xf); + code |= ((iBitNum + (code & 0xf)) << 8); // add magnitude bits to length + repeat = 1< 1) + { + iType = 4; + } + switch (iType) + { + case 3: /* Short */ + i = TIFFSHORT(p+8, bMotorola); + break; + case 4: /* Long */ + case 7: // undefined (treat it as a long since it's usually a multibyte buffer) + i = TIFFLONG(p+8, bMotorola); + break; + case 6: // signed byte + i = (signed char)p[8]; + break; + case 2: /* ASCII */ + case 5: /* Unsigned Rational */ + case 10: /* Signed Rational */ + i = TIFFLONG(p+8, bMotorola); + break; + default: /* to suppress compiler warning */ + i = 0; + break; + } + return i; + +} /* TIFFVALUE() */ +static void GetTIFFInfo(JPEGIMAGE *pPage, int bMotorola, int iOffset) +{ + int iTag, iTagCount, i; + uint8_t *cBuf = pPage->ucFileBuf; + + iTagCount = TIFFSHORT(&cBuf[iOffset], bMotorola); /* Number of tags in this dir */ + if (iTagCount < 1 || iTagCount > 256) // invalid tag count + return; /* Bad header info */ + /*--- Search the TIFF tags ---*/ + for (i=0; iucOrientation = TIFFVALUE(p, bMotorola); + } + else if (iTag == 256) // width of thumbnail + { + pPage->iThumbWidth = TIFFVALUE(p, bMotorola); + } + else if (iTag == 257) // height of thumbnail + { + pPage->iThumbHeight = TIFFVALUE(p, bMotorola); + } + else if (iTag == 513) // offset to JPEG data + { + pPage->iThumbData = TIFFVALUE(p, bMotorola); + } + } +} /* GetTIFFInfo() */ + +static int JPEGGetSOS(JPEGIMAGE *pJPEG, int *iOff) +{ + int16_t sLen; + int iOffset = *iOff; + int i, j; + uint8_t uc,c,cc; + uint8_t *buf = pJPEG->ucFileBuf; + + sLen = MOTOSHORT(&buf[iOffset]); + iOffset += 2; + + // Assume no components in this scan + for (i=0; i<4; i++) + pJPEG->JPCI[i].component_needed = 0; + + uc = buf[iOffset++]; // get number of components + pJPEG->ucComponentsInScan = uc; + sLen -= 3; + if (uc < 1 || uc > MAX_COMPS_IN_SCAN || sLen != (uc*2+3)) // check length of data packet + return 1; // error + for (i=0; iJPCI[j].component_id == cc) + break; + } + if (j == 4) // error, not found + return 1; + if ((c & 0xf) > 3 || (c & 0xf0) > 0x30) + return 1; // bogus table numbers + pJPEG->JPCI[j].dc_tbl_no = c >> 4; + pJPEG->JPCI[j].ac_tbl_no = c & 0xf; + pJPEG->JPCI[j].component_needed = 1; // mark this component as being included in the scan + } + pJPEG->iScanStart = buf[iOffset++]; // Get the scan start (or lossless predictor) for this scan + pJPEG->iScanEnd = buf[iOffset++]; // Get the scan end for this scan + c = buf[iOffset++]; // successive approximation bits + pJPEG->cApproxBitsLow = c & 0xf; // also point transform in lossless mode + pJPEG->cApproxBitsHigh = c >> 4; + + *iOff = iOffset; + return 0; + +} /* JPEGGetSOS() */ +// +// Remove markers from the data stream to allow faster decode +// Stuffed zeros and restart interval markers aren't needed to properly decode +// the data, but they make reading VLC data slower, so I pull them out first +// +static int JPEGFilter(uint8_t *pBuf, uint8_t *d, int iLen, uint8_t *bFF) +{ + // since we have the entire jpeg buffer in memory already, we can just change it in place + unsigned char c, *s, *pEnd, *pStart; + + pStart = d; + s = pBuf; + pEnd = &s[iLen-1]; // stop just shy of the end to not miss a final marker/stuffed 0 + if (*bFF) // last byte was a FF, check the next one + { + if (s[0] == 0) // stuffed 0, keep the FF + *d++ = 0xff; + s++; + *bFF = 0; + } + while (s < pEnd) + { + c = *d++ = *s++; + if (c == 0xff) // marker or stuffed zeros? + { + if (s[0] != 0) // it's a marker, skip both + { + d--; + } + s++; // for stuffed 0's, store the FF, skip the 00 + } + } + if (s == pEnd) // need to test the last byte + { + c = s[0]; + if (c == 0xff) // last byte is FF, take care of it next time through + *bFF = 1; // take care of it next time through + else + *d++ = c; // nope, just store it + } + return (int)(d-pStart); // filtered output length +} /* JPEGFilter() */ +// +// Read and filter more VLC data for decoding +// +static void JPEGGetMoreData(JPEGIMAGE *pPage) +{ + int iDelta = pPage->iVLCSize - pPage->iVLCOff; +// printf("Getting more data...size=%d, off=%d\n", pPage->iVLCSize, pPage->iVLCOff); + // move any existing data down + if (iDelta >= (JPEG_FILE_BUF_SIZE-64) || iDelta < 0) + return; // buffer is already full; no need to read more data + if (pPage->iVLCOff != 0) + { + memcpy(pPage->ucFileBuf, &pPage->ucFileBuf[pPage->iVLCOff], pPage->iVLCSize - pPage->iVLCOff); + pPage->iVLCSize -= pPage->iVLCOff; + pPage->iVLCOff = 0; + pPage->bb.pBuf = pPage->ucFileBuf; // reset VLC source pointer too + } + if (pPage->JPEGFile.iPos < pPage->JPEGFile.iSize && pPage->iVLCSize < JPEG_FILE_BUF_SIZE-64) + { + int i; + // Try to read enough to fill the buffer + i = (*pPage->pfnRead)(&pPage->JPEGFile, &pPage->ucFileBuf[pPage->iVLCSize], JPEG_FILE_BUF_SIZE - pPage->iVLCSize); // max length we can read + // Filter out the markers + pPage->iVLCSize += JPEGFilter(&pPage->ucFileBuf[pPage->iVLCSize], &pPage->ucFileBuf[pPage->iVLCSize], i, &pPage->ucFF); + } +} /* JPEGGetMoreData() */ + +// +// Parse the JPEG header, gather necessary info to decode the image +// Returns 1 for success, 0 for failure +// +static int JPEGParseInfo(JPEGIMAGE *pPage, int bExtractThumb) +{ + int iBytesRead; + int i, iOffset, iTableOffset; + uint8_t ucTable, *s = pPage->ucFileBuf; + uint16_t usMarker, usLen = 0; + int iFilePos = 0; + + if (bExtractThumb) // seek to the start of the thumbnail image + { + iFilePos = pPage->iThumbData; + (*pPage->pfnSeek)(&pPage->JPEGFile, iFilePos); + } + iBytesRead = (*pPage->pfnRead)(&pPage->JPEGFile, s, JPEG_FILE_BUF_SIZE); + if (iBytesRead < 256) // a JPEG file this tiny? probably bad + { + pPage->iError = JPEG_INVALID_FILE; + return 0; + } + iFilePos += iBytesRead; + if (MOTOSHORT(pPage->ucFileBuf) != 0xffd8) + { + pPage->iError = JPEG_INVALID_FILE; + return 0; // not a JPEG file + } + iOffset = 2; /* Start at offset of first marker */ + usMarker = 0; /* Search for SOFx (start of frame) marker */ + while (usMarker != 0xffda && iOffset < pPage->JPEGFile.iSize) + { + if (iOffset >= JPEG_FILE_BUF_SIZE/2) // too close to the end, read more data + { + // Do we need to seek first? + if (iOffset >= JPEG_FILE_BUF_SIZE) + { + iFilePos += (iOffset - iBytesRead); + iOffset = 0; + (*pPage->pfnSeek)(&pPage->JPEGFile, iFilePos); + iBytesRead = 0; // throw away any old data + } + // move existing bytes down + if (iOffset) + { + memcpy(pPage->ucFileBuf, &pPage->ucFileBuf[iOffset], iBytesRead - iOffset); + iBytesRead -= iOffset; + iOffset = 0; + } + i = (*pPage->pfnRead)(&pPage->JPEGFile, &pPage->ucFileBuf[iBytesRead], JPEG_FILE_BUF_SIZE-iBytesRead); + iFilePos += i; + iBytesRead += i; + } + usMarker = MOTOSHORT(&s[iOffset]); + iOffset += 2; + usLen = MOTOSHORT(&s[iOffset]); // marker length + + if (usMarker < 0xffc0 || usMarker == 0xffff) // invalid marker, could be generated by "Arles Image Web Page Creator" or Accusoft + { + iOffset++; + continue; // skip 1 byte and try to resync + } + switch (usMarker) + { + case 0xffc1: + case 0xffc2: + case 0xffc3: + pPage->iError = JPEG_UNSUPPORTED_FEATURE; + return 0; // currently unsupported modes + + case 0xffe1: // App1 (EXIF?) + if (s[iOffset+2] == 'E' && s[iOffset+3] == 'x' && (s[iOffset+8] == 'M' || s[iOffset+8] == 'I')) // the EXIF data we want + { + int bMotorola, IFD, iTagCount; + pPage->iEXIF = iFilePos - iBytesRead + iOffset + 8; // start of TIFF file + // Get the orientation value (if present) + bMotorola = (s[iOffset+8] == 'M'); + IFD = TIFFLONG(&s[iOffset+12], bMotorola); + iTagCount = TIFFSHORT(&s[iOffset+16], bMotorola); + GetTIFFInfo(pPage, bMotorola, IFD+iOffset+8); + // The second IFD defines the thumbnail (if present) + if (iTagCount >= 1 && iTagCount < 32) // valid number of tags for EXIF data 'page' + { + // point to next IFD + IFD += (12 * iTagCount) + 2; + IFD = TIFFLONG(&s[IFD + iOffset + 8], bMotorola); + if (IFD != 0) // Thumbnail present? + { + pPage->ucHasThumb = 1; + GetTIFFInfo(pPage, bMotorola, IFD+iOffset+8); // info for second 'page' of TIFF + pPage->iThumbData += iOffset + 8; // absolute offset in the file + } + } + } + break; + case 0xffc0: // SOFx - start of frame + pPage->ucMode = (uint8_t)usMarker; + pPage->ucBpp = s[iOffset+2]; // bits per sample + pPage->iHeight = MOTOSHORT(&s[iOffset+3]); + pPage->iWidth = MOTOSHORT(&s[iOffset+5]); + pPage->ucNumComponents = s[iOffset+7]; + pPage->ucBpp = pPage->ucBpp * pPage->ucNumComponents; /* Bpp = number of components * bits per sample */ + if (pPage->ucNumComponents == 1) + pPage->ucSubSample = 0; // use this to differentiate from color 1:1 + else + { + usLen -= 8; + iOffset += 8; +// pPage->ucSubSample = s[iOffset+9]; // subsampling option for the second color component + for (i=0; iucNumComponents; i++) + { + uint8_t ucSamp; + pPage->JPCI[i].component_id = s[iOffset++]; + pPage->JPCI[i].component_index = (unsigned char)i; + ucSamp = s[iOffset++]; // get the h+v sampling factor + if (i == 0) // Y component? + pPage->ucSubSample = ucSamp; +// pPage->JPCI[i].h_samp_factor = ucSamp >> 4; +// pPage->JPCI[i].v_samp_factor = ucSamp & 0xf; + pPage->JPCI[i].quant_tbl_no = s[iOffset++]; // quantization table number + usLen -= 3; + } + } + break; + case 0xffdd: // Restart Interval + if (usLen == 4) + pPage->iResInterval = MOTOSHORT(&s[iOffset+2]); + break; + case 0xffc4: /* M_DHT */ // get Huffman tables + iOffset += 2; // skip length + usLen -= 2; // subtract length length + if (JPEGGetHuffTables(&s[iOffset], usLen, pPage) != 0) // bad tables? + { + pPage->iError = JPEG_DECODE_ERROR; + return 0; // error + } + break; + case 0xffdb: /* M_DQT */ + /* Get the quantization tables */ + /* first byte has PPPPNNNN where P = precision and N = table number 0-3 */ + iOffset += 2; // skip length + usLen -= 2; // subtract length length + while (usLen > 0) + { + ucTable = s[iOffset++]; // table number + if ((ucTable & 0xf) > 3) // invalid table number + { + pPage->iError = JPEG_DECODE_ERROR; + return 0; + } + iTableOffset = (ucTable & 0xf) * DCTSIZE; + if (ucTable & 0xf0) // if word precision + { + for (i=0; isQuantTable[i+iTableOffset] = MOTOSHORT(&s[iOffset]); + iOffset += 2; + } + usLen -= (DCTSIZE*2 + 1); + } + else // byte precision + { + for (i=0; isQuantTable[i+iTableOffset] = (unsigned short)s[iOffset++]; + } + usLen -= (DCTSIZE + 1); + } + } + break; + } // switch on JPEG marker + iOffset += usLen; + } // while + if (usMarker == 0xffda) // start of image + { + if (pPage->ucBpp != 8) // need to match up table IDs + { + iOffset -= usLen; + JPEGGetSOS(pPage, &iOffset); // get Start-Of-Scan info for decoding + } + if (!JPEGMakeHuffTables(pPage, 0)) //int bThumbnail) DEBUG + { + pPage->iError = JPEG_UNSUPPORTED_FEATURE; + return 0; + } + // Now the offset points to the start of compressed data + i = JPEGFilter(&pPage->ucFileBuf[iOffset], pPage->ucFileBuf, iBytesRead-iOffset, &pPage->ucFF); + pPage->iVLCOff = 0; + pPage->iVLCSize = i; + JPEGGetMoreData(pPage); // read more VLC data + return 1; + } + pPage->iError = JPEG_DECODE_ERROR; + return 0; +} /* JPEGParseInfo() */ +// +// Fix and reorder the quantization table for faster decoding.* +// +static void JPEGFixQuantD(JPEGIMAGE *pJPEG) +{ + int iTable, iTableOffset; + signed short sTemp[DCTSIZE]; + int i; + uint16_t *p; + + for (iTable=0; iTableucNumComponents; iTable++) + { + iTableOffset = iTable * DCTSIZE; + p = (uint16_t *)&pJPEG->sQuantTable[iTableOffset]; + for (i=0; isQuantTable[iTableOffset], sTemp, DCTSIZE*sizeof(short)); // copy back to original spot + + // Prescale for DCT multiplication + p = (uint16_t *)&pJPEG->sQuantTable[iTableOffset]; + for (i=0; i> 12); + } + } +} /* JPEGFixQuantD() */ +// +// Decode the 64 coefficients of the current DCT block +// +static int JPEGDecodeMCU(JPEGIMAGE *pJPEG, int iMCU, int *iDCPredictor) +{ + uint32_t ulCode, ulTemp; + uint8_t *pZig; + signed char cCoeff; + unsigned short *pFast; + unsigned char ucHuff, *pucFast; + uint32_t usHuff; // this prevents an unnecessary & 65535 for shorts + uint32_t ulBitOff, ulBits; // local copies to allow compiler to use register vars + uint8_t *pBuf, *pEnd, *pEnd2; + signed short *pMCU = &pJPEG->sMCUs[iMCU]; + uint8_t ucMaxACCol, ucMaxACRow; + + #define MIN_DCT_THRESHOLD 8 + + ulBitOff = pJPEG->bb.ulBitOff; + ulBits = pJPEG->bb.ulBits; + pBuf = pJPEG->bb.pBuf; + + pZig = (unsigned char *)&cZigZag2[1]; + pEnd = (unsigned char *)&cZigZag2[64]; + + if (ulBitOff > (REGISTER_WIDTH-17)) // need to get more data + { + pBuf += (ulBitOff >> 3); + ulBitOff &= 7; + ulBits = MOTOLONG(pBuf); + } + if (pJPEG->iOptions & (JPEG_SCALE_QUARTER | JPEG_SCALE_EIGHTH)) // reduced size DCT + { + pMCU[1] = pMCU[8] = pMCU[9] = 0; + pEnd2 = (uint8_t *)&cZigZag2[5]; // we only need to store the 4 elements we care about + } + else + { + memset(pMCU, 0, 64*sizeof(short)); // pre-fill with zero since we may skip coefficients + pEnd2 = (uint8_t *)&cZigZag2[64]; + } + ucMaxACCol = ucMaxACRow = 0; + pZig = (unsigned char *)&cZigZag2[1]; + pEnd = (unsigned char *)&cZigZag2[64]; + + // get the DC component + pucFast = &pJPEG->ucHuffDC[pJPEG->ucDCTable * DC_TABLE_SIZE]; + ulCode = (ulBits >> (REGISTER_WIDTH - 12 - ulBitOff)) & 0xfff; // get as lower 12 bits + if (ulCode >= 0xf80) // it's a long code + ulCode = (ulCode & 0xff); // point to long table and trim to 7-bits + 0x80 offset into long table + else + ulCode >>= 6; // it's a short code, use first 6 bits only + ucHuff = pucFast[ulCode]; + cCoeff = (signed char)pucFast[ulCode+512]; // get pre-calculated extra bits for "small" values + if (ucHuff == 0) // invalid code + return -1; + ulBitOff += (ucHuff >> 4); // add the Huffman length + ucHuff &= 0xf; // get the actual code (SSSS) + if (ucHuff) // if there is a change to the DC value + { // get the 'extra' bits + if (cCoeff) + { + (*iDCPredictor) += cCoeff; + } + else + { + if (ulBitOff > (REGISTER_WIDTH - 17)) // need to get more data + { + pBuf += (ulBitOff >> 3); + ulBitOff &= 7; + ulBits = MOTOLONG(pBuf); + } + ulCode = ulBits << ulBitOff; + ulTemp = ~(uint32_t)(((int32_t)ulCode)>>31); // slide sign bit across other 31 bits + ulCode >>= (REGISTER_WIDTH - ucHuff); + ulCode -= ulTemp>>(REGISTER_WIDTH-ucHuff); + ulBitOff += ucHuff; // add bit length + (*iDCPredictor) += (int)ulCode; + } + } + pMCU[0] = (short)*iDCPredictor; // store in MCU[0] + // Now get the other 63 AC coefficients + pFast = &pJPEG->usHuffAC[pJPEG->ucACTable * HUFF11SIZE]; + if (pJPEG->b11Bit) // 11-bit "slow" tables used + { +// if (pJPEG->pHuffACFast == pJPEG->huffacFast[1]) // second table +// pFast = &pJPEG->ucAltHuff[0]; + while (pZig < pEnd) + { + if (ulBitOff >(REGISTER_WIDTH - 17)) // need to get more data + { + pBuf += (ulBitOff >> 3); + ulBitOff &= 7; + ulBits = MOTOLONG(pBuf); + } + ulCode = (ulBits >> (REGISTER_WIDTH - 16 - ulBitOff)) & 0xffff; // get as lower 16 bits + if (ulCode >= 0xf000) // first 4 bits = 1, use long table + ulCode = (ulCode & 0x1fff); + else + ulCode >>= 4; // use lower 12 bits (short table) + usHuff = pFast[ulCode]; + if (usHuff == 0) // invalid code + return -1; + ulBitOff += (usHuff >> 8); // add length + usHuff &= 0xff; // get code (RRRR/SSSS) + if (usHuff == 0) // no more AC components + { + goto mcu_done; + } + if (ulBitOff > (REGISTER_WIDTH - 17)) // need to get more data + { + pBuf += (ulBitOff >> 3); + ulBitOff &= 7; + ulBits = MOTOLONG(pBuf); + } + pZig += (usHuff >> 4); // get the skip amount (RRRR) + usHuff &= 0xf; // get (SSSS) - extra length + if (pZig < pEnd && usHuff) // && piHisto) + { + ulCode = ulBits << ulBitOff; + ulTemp = ~(uint32_t) (((int32_t) ulCode) >> (REGISTER_WIDTH-1)); // slide sign bit across other 63 bits + ulCode >>= (REGISTER_WIDTH - usHuff); + ulCode -= ulTemp >> (REGISTER_WIDTH - usHuff); + ucMaxACCol |= 1<<(*pZig & 7); // keep track of occupied columns + if (*pZig >= 0x20) // if more than 4 rows used in a col, mark it + ucMaxACRow |= 1<<(*pZig & 7); // keep track of the max AC term row + pMCU[*pZig] = (signed short)ulCode; // store AC coefficient (already reordered) + } + ulBitOff += usHuff; // add (SSSS) extra length + pZig++; + } // while + } + else // 10-bit "fast" tables used + { + while (pZig < pEnd) + { + if (ulBitOff >(REGISTER_WIDTH - 17)) // need to get more data + { + pBuf += (ulBitOff >> 3); + ulBitOff &= 7; + ulBits = MOTOLONG(pBuf); + } + ulCode = (ulBits >> (REGISTER_WIDTH - 16 - ulBitOff)) & 0xffff; // get as lower 16 bits + if (ulCode >= 0xfc00) // first 6 bits = 1, use long table + ulCode = (ulCode & 0x7ff); // (ulCode & 0x3ff) + 0x400; + else + ulCode >>= 6; // use lower 10 bits (short table) + usHuff = pFast[ulCode]; + if (usHuff == 0) // invalid code + return -1; + ulBitOff += (usHuff >> 8); // add length + usHuff &= 0xff; // get code (RRRR/SSSS) + if (usHuff == 0) // no more AC components + { + goto mcu_done; + } + if (ulBitOff >(REGISTER_WIDTH - 17)) // need to get more data + { + pBuf += (ulBitOff >> 3); + ulBitOff &= 7; + ulBits = MOTOLONG(pBuf); + } + pZig += (usHuff >> 4); // get the skip amount (RRRR) + usHuff &= 0xf; // get (SSSS) - extra length + if (pZig < pEnd2 && usHuff) + { + ulCode = ulBits << ulBitOff; + ulTemp = ~(uint32_t) (((int32_t) ulCode) >> (REGISTER_WIDTH-1)); // slide sign bit across other 63 bits + ulCode >>= (REGISTER_WIDTH - usHuff); + ulCode -= ulTemp >> (REGISTER_WIDTH - usHuff); + ucMaxACCol |= 1<<(*pZig & 7); // keep track of occupied columns + if (*pZig >= 0x20) // if more than 4 rows used in a col, mark it + ucMaxACRow |= 1<<(*pZig & 7); // keep track of the max AC term row + pMCU[*pZig] = (signed short)ulCode; // store AC coefficient (already reordered) + } + ulBitOff += usHuff; // add (SSSS) extra length + pZig++; + } // while + } // 10-bit tables +mcu_done: + pJPEG->bb.pBuf = pBuf; + pJPEG->iVLCOff = (int)(pBuf - pJPEG->ucFileBuf); + pJPEG->bb.ulBitOff = ulBitOff; + pJPEG->bb.ulBits = ulBits; + pJPEG->ucMaxACCol = ucMaxACCol; + pJPEG->ucMaxACRow = ucMaxACRow; // DEBUG + return 0; +} /* JPEGDecodeMCU() */ +// +// Inverse DCT +// +static void JPEGIDCT(JPEGIMAGE *pJPEG, int iMCUOffset, int iQuantTable, int iACFlags) +{ + int iRow; + unsigned char ucColMask; + int iCol; + signed int tmp6,tmp7,tmp10,tmp11,tmp12,tmp13; + signed int z5,z10,z11,z12,z13; + signed int tmp0,tmp1,tmp2,tmp3,tmp4,tmp5; + signed short *pQuant; + unsigned char *pOutput; + unsigned char ucMaxACRow, ucMaxACCol; + int16_t *pMCUSrc = &pJPEG->sMCUs[iMCUOffset]; + + ucMaxACRow = (unsigned char)(iACFlags >> 8); + ucMaxACCol = iACFlags & 0xff; + + // my shortcut method appears to violate patent 20020080052 + // but the patent is invalidated by prior art: + // http://netilium.org/~mad/dtj/DTJ/DTJK04/ + pQuant = &pJPEG->sQuantTable[iQuantTable * DCTSIZE]; + if (pJPEG->iOptions & JPEG_SCALE_QUARTER) // special case + { + /* Column 0 */ + tmp4 = pMCUSrc[0] * pQuant[0]; + tmp5 = pMCUSrc[8] * pQuant[8]; + tmp0 = tmp4 + tmp5; + tmp2 = tmp4 - tmp5; + /* Column 1 */ + tmp4 = pMCUSrc[1] * pQuant[1]; + tmp5 = pMCUSrc[9] * pQuant[9]; + tmp1 = tmp4 + tmp5; + tmp3 = tmp4 - tmp5; + /* Pass 2: process 2 rows, store into output array. */ + /* Row 0 */ + pOutput = (unsigned char *)pMCUSrc; // store output pixels back into MCU + pOutput[0] = ucRangeTable[(((tmp0 + tmp1)>>5) & 0x3ff)]; + pOutput[1] = ucRangeTable[(((tmp0 - tmp1)>>5) & 0x3ff)]; + /* Row 1 */ + pOutput[2] = ucRangeTable[(((tmp2 + tmp3)>>5) & 0x3ff)]; + pOutput[3] = ucRangeTable[(((tmp2 - tmp3)>>5) & 0x3ff)]; + return; + } + // do columns first + ucColMask = ucMaxACCol | 1; // column 0 must always be calculated + for (iCol = 0; iCol < 8 && ucColMask; iCol++) + { + if (ucColMask & (1<>8); // used to be 362 - 1 (256) + tmp0 = tmp10 + tmp1; + tmp3 = tmp10 - tmp1; + tmp1 = tmp10 + tmp12; + tmp2 = tmp10 - tmp12; + // odd part + tmp4 = pMCUSrc[iCol+8] * pQuant[iCol+8]; // get 1st row + tmp5 = pMCUSrc[iCol+24]; + if (tmp5) // this value is usually 0 + { + tmp5 *= pQuant[iCol+24]; // get 3rd row + tmp7 = tmp4 + tmp5; + tmp11 = (((tmp4 - tmp5) * 362) >> 8); // 362>>8 = 1.414213562 + z5 = (((tmp4-tmp5) * 473) >> 8); // 473>>8 = 1.8477 + tmp12 = ((-tmp5 * -669)>>8) + z5; // -669>>8 = -2.6131259 + tmp6 = tmp12 - tmp7; + tmp5 = tmp11 - tmp6; + tmp10 = ((tmp4 * 277)>>8) - z5; // 277>>8 = 1.08239 + tmp4 = tmp10 + tmp5; + } + else // simpler case when we only have 1 odd row to calculate + { + tmp7 = tmp4; + tmp5 = (145*tmp4) >> 8; + tmp6 = (217*tmp4) >> 8; + tmp4 = (-51*tmp4) >> 8; + } + pMCUSrc[iCol] = (short)(tmp0 + tmp7); // row0 + pMCUSrc[iCol+8] = (short)(tmp1 + tmp6); // row 1 + pMCUSrc[iCol+16] = (short)(tmp2 + tmp5); // row 2 + pMCUSrc[iCol+24] = (short)(tmp3 - tmp4); // row 3 + pMCUSrc[iCol+32] = (short)(tmp3 + tmp4); // row 4 + pMCUSrc[iCol+40] = (short)(tmp2 - tmp5); // row 5 + pMCUSrc[iCol+48] = (short)(tmp1 - tmp6); // row 6 + pMCUSrc[iCol+56] = (short)(tmp0 - tmp7); // row 7 + } + else // need to do full column calculation + { + // even part + tmp0 = pMCUSrc[iCol] * pQuant[iCol]; + tmp2 = pMCUSrc[iCol+32]; // get 4th row + if (tmp2) // 4th row is most likely 0 + { + tmp2 = tmp2 * pQuant[iCol+32]; + tmp10 = tmp0 + tmp2; + tmp11 = tmp0 - tmp2; + } + else + { + tmp10 = tmp11 = tmp0; + } + tmp1 = pMCUSrc[iCol+16] * pQuant[iCol+16]; // get 2nd row + tmp3 = pMCUSrc[iCol+48]; // get 6th row + if (tmp3) // 6th row is most likely 0 + { + tmp3 = tmp3 * pQuant[iCol+48]; + tmp13 = tmp1 + tmp3; + tmp12 = (((tmp1 - tmp3) * 362) >> 8) - tmp13; // 362>>8 = 1.414213562 + } + else + { + tmp13 = tmp1; + tmp12 = ((tmp1*362)>>8) - tmp1; + } + tmp0 = tmp10 + tmp13; + tmp3 = tmp10 - tmp13; + tmp1 = tmp11 + tmp12; + tmp2 = tmp11 - tmp12; + // odd part + tmp5 = pMCUSrc[iCol+24] * pQuant[iCol+24]; // get 3rd row + tmp6 = pMCUSrc[iCol+40]; // get 5th row + if (tmp6) // very likely that row 5 = 0 + { + tmp6 = tmp6 * pQuant[iCol+40]; + z13 = tmp6 + tmp5; + z10 = tmp6 - tmp5; + } + else + { + z13 = tmp5; + z10 = -tmp5; + } + tmp4 = pMCUSrc[iCol+8] * pQuant[iCol+8]; // get 1st row + tmp7 = pMCUSrc[iCol+56]; // get 7th row + if (tmp7) // very likely that row 7 = 0 + { + tmp7 = tmp7 * pQuant[iCol+56]; + z11 = tmp4 + tmp7; + z12 = tmp4 - tmp7; + } + else + { + z11 = z12 = tmp4; + } + tmp7 = z11 + z13; + tmp11 = (((z11 - z13) * 362) >> 8); // 362>>8 = 1.414213562 + z5 = (((z10 + z12) * 473) >> 8); // 473>>8 = 1.8477 + tmp12 = ((z10 * -669)>>8) + z5; // -669>>8 = -2.6131259 + tmp6 = tmp12 - tmp7; + tmp5 = tmp11 - tmp6; + tmp10 = ((z12 * 277)>>8) - z5; // 277>>8 = 1.08239 + tmp4 = tmp10 + tmp5; + pMCUSrc[iCol] = (short)(tmp0 + tmp7); // row0 + pMCUSrc[iCol+8] = (short)(tmp1 + tmp6); // row 1 + pMCUSrc[iCol+16] = (short)(tmp2 + tmp5); // row 2 + pMCUSrc[iCol+24] = (short)(tmp3 - tmp4); // row 3 + pMCUSrc[iCol+32] = (short)(tmp3 + tmp4); // row 4 + pMCUSrc[iCol+40] = (short)(tmp2 - tmp5); // row 5 + pMCUSrc[iCol+48] = (short)(tmp1 - tmp6); // row 6 + pMCUSrc[iCol+56] = (short)(tmp0 - tmp7); // row 7 + } // full calculation needed + } // if column has data in it + } // for each column + // now do rows + pOutput = (unsigned char *)pMCUSrc; // store output pixels back into MCU + for (iRow=0; iRow<64; iRow+=8) // all rows must be calculated + { + // even part + if (ucMaxACCol < 0x10) // quick and dirty calculation (right 4 columns are all 0's) + { + if (ucMaxACCol < 0x04) // very likely case (1 or 2 columns occupied) + { + // even part + tmp0 = tmp1 = tmp2 = tmp3 = pMCUSrc[iRow+0]; + // odd part + tmp7 = pMCUSrc[iRow+1]; + tmp6 = (tmp7 * 217)>>8; // * 0.8477 + tmp5 = (tmp7 * 145)>>8; // * 0.5663 + tmp4 = -((tmp7 * 51)>>8); // * -0.199 + } + else + { + tmp10 = pMCUSrc[iRow+0]; + tmp13 = pMCUSrc[iRow+2]; + tmp12 = ((tmp13 * 106)>>8); // 2-6 * 1.414 + tmp0 = tmp10 + tmp13; + tmp3 = tmp10 - tmp13; + tmp1 = tmp10 + tmp12; + tmp2 = tmp10 - tmp12; + // odd part + z13 = pMCUSrc[iRow+3]; + z11 = pMCUSrc[iRow+1]; + tmp7 = z11 + z13; + tmp11 = ((z11 - z13)*362)>>8; // * 1.414 + z5 = ((z11 - z13)*473)>>8; // * 1.8477 + tmp10 = ((z11*277)>>8) - z5; // * 1.08239 + tmp12 = ((z13*669)>>8) + z5; // * 2.61312 + tmp6 = tmp12 - tmp7; + tmp5 = tmp11 - tmp6; + tmp4 = tmp10 + tmp5; + } + } + else // need to do the full calculation + { + tmp10 = pMCUSrc[iRow+0] + pMCUSrc[iRow+4]; + tmp11 = pMCUSrc[iRow+0] - pMCUSrc[iRow+4]; + tmp13 = pMCUSrc[iRow+2] + pMCUSrc[iRow+6]; + tmp12 = (((pMCUSrc[iRow+2] - pMCUSrc[iRow+6]) * 362)>>8) - tmp13; // 2-6 * 1.414 + tmp0 = tmp10 + tmp13; + tmp3 = tmp10 - tmp13; + tmp1 = tmp11 + tmp12; + tmp2 = tmp11 - tmp12; + // odd part + z13 = pMCUSrc[iRow+5] + pMCUSrc[iRow+3]; + z10 = pMCUSrc[iRow+5] - pMCUSrc[iRow+3]; + z11 = pMCUSrc[iRow+1] + pMCUSrc[iRow+7]; + z12 = pMCUSrc[iRow+1] - pMCUSrc[iRow+7]; + tmp7 = z11 + z13; + tmp11 = ((z11 - z13)*362)>>8; // * 1.414 + z5 = ((z10 + z12)*473)>>8; // * 1.8477 + tmp10 = ((z12*277)>>8) - z5; // * 1.08239 + tmp12 = ((z10*-669)>>8) + z5; // * 2.61312 + tmp6 = tmp12 - tmp7; + tmp5 = tmp11 - tmp6; + tmp4 = tmp10 + tmp5; + } + // final output stage - scale down and range limit + pOutput[0] = ucRangeTable[(((tmp0 + tmp7)>>5) & 0x3ff)]; + pOutput[1] = ucRangeTable[(((tmp1 + tmp6)>>5) & 0x3ff)]; + pOutput[2] = ucRangeTable[(((tmp2 + tmp5)>>5) & 0x3ff)]; + pOutput[3] = ucRangeTable[(((tmp3 - tmp4)>>5) & 0x3ff)]; + pOutput[4] = ucRangeTable[(((tmp3 + tmp4)>>5) & 0x3ff)]; + pOutput[5] = ucRangeTable[(((tmp2 - tmp5)>>5) & 0x3ff)]; + pOutput[6] = ucRangeTable[(((tmp1 - tmp6)>>5) & 0x3ff)]; + pOutput[7] = ucRangeTable[(((tmp0 - tmp7)>>5) & 0x3ff)]; + pOutput += 8; + } // for each row +} /* JPEGIDCT() */ +static void JPEGPutMCU8BitGray(JPEGIMAGE *pJPEG, int x, int iPitch) +{ + int i, j, xcount, ycount; + uint8_t *pDest, *pSrc = (uint8_t *)&pJPEG->sMCUs[0]; + + if (pJPEG->pDitherBuffer) + pDest = &pJPEG->pDitherBuffer[x]; + else + pDest = (uint8_t *)&pJPEG->usPixels[x/2]; + + if (pJPEG->ucSubSample <= 0x11) // single Y + { + if (pJPEG->iOptions & JPEG_SCALE_HALF) // special handling of 1/2 size (pixel averaging) + { + int pix; + for (i=0; i<4; i++) + { + for (j=0; j<4; j++) + { + pix = (pSrc[0] + pSrc[1] + pSrc[8] + pSrc[9] + 2) >> 2; // average 2x2 block + pDest[j] = (uint8_t)pix; + pSrc += 2; + } + pSrc += 8; // skip extra line + pDest += iPitch; + } + return; + } + xcount = ycount = 8; // debug + if (pJPEG->iOptions & JPEG_SCALE_QUARTER) + xcount = ycount = 2; + else if (pJPEG->iOptions & JPEG_SCALE_EIGHTH) + xcount = ycount = 1; + for (i=0; iucSubSample == 0x21) // stacked horizontally + { + if (pJPEG->iOptions & JPEG_SCALE_EIGHTH) + { + // only 2 pixels emitted + pDest[0] = pSrc[0]; + pDest[1] = pSrc[128]; + return; + } /* 1/8 */ + if (pJPEG->iOptions & JPEG_SCALE_HALF) + { + for (i=0; i<4; i++) + { + for (j=0; j<4; j++) + { + int pix; + pix = (pSrc[j*2] + pSrc[j*2+1] + pSrc[j*2 + 8] + pSrc[j*2 + 9] + 2) >> 2; + pDest[j] = (uint8_t)pix; + pix = (pSrc[j*2 + 128] + pSrc[j*2+129] + pSrc[j*2 + 136] + pSrc[j*2 + 137] + 2) >> 2; + pDest[j+4] = (uint8_t)pix; + } + pSrc += 16; + pDest += iPitch; + } + return; + } + if (pJPEG->iOptions & JPEG_SCALE_QUARTER) + { + // each MCU contributes a 2x2 block + pDest[0] = pSrc[0]; // Y0 + pDest[1] = pSrc[1]; + pDest[iPitch] = pSrc[2]; + pDest[iPitch+1] = pSrc[3]; + + pDest[2] = pSrc[128]; // Y` + pDest[3] = pSrc[129]; + pDest[iPitch+2] = pSrc[130]; + pDest[iPitch+3] = pSrc[131]; + return; + } + for (i=0; i<8; i++) + { + for (j=0; j<8; j++) + { + pDest[j] = pSrc[j]; + pDest[j+8] = pSrc[128 + j]; + } + pSrc += 8; + pDest += iPitch; + } + } // 0x21 + if (pJPEG->ucSubSample == 0x12) // stacked vertically + { + if (pJPEG->iOptions & JPEG_SCALE_EIGHTH) + { + // only 2 pixels emitted + pDest[0] = pSrc[0]; + pDest[iPitch] = pSrc[128]; + return; + } /* 1/8 */ + if (pJPEG->iOptions & JPEG_SCALE_HALF) + { + for (i=0; i<4; i++) + { + for (j=0; j<4; j++) + { + int pix; + pix = (pSrc[j*2] + pSrc[j*2+1] + pSrc[j*2 + 8] + pSrc[j*2 + 9] + 2) >> 2; + pDest[j] = (uint8_t)pix; + pix = (pSrc[j*2 + 128] + pSrc[j*2+129] + pSrc[j*2 + 136] + pSrc[j*2 + 137] + 2) >> 2; + pDest[4*iPitch+j] = (uint8_t)pix; + } + pSrc += 16; + pDest += iPitch; + } + return; + } + if (pJPEG->iOptions & JPEG_SCALE_QUARTER) + { + // each MCU contributes a 2x2 block + pDest[0] = pSrc[0]; // Y0 + pDest[1] = pSrc[1]; + pDest[iPitch] = pSrc[2]; + pDest[iPitch+1] = pSrc[3]; + + pDest[iPitch*2] = pSrc[128]; // Y` + pDest[iPitch*2+1] = pSrc[129]; + pDest[iPitch*3] = pSrc[130]; + pDest[iPitch*3+1] = pSrc[131]; + return; + } + for (i=0; i<8; i++) + { + for (j=0; j<8; j++) + { + pDest[j] = pSrc[j]; + pDest[8*iPitch + j] = pSrc[128 + j]; + } + pSrc += 8; + pDest += iPitch; + } + } // 0x12 + if (pJPEG->ucSubSample == 0x22) + { + if (pJPEG->iOptions & JPEG_SCALE_EIGHTH) + { + // each MCU contributes 1 pixel + pDest[0] = pSrc[0]; // Y0 + pDest[1] = pSrc[128]; // Y1 + pDest[iPitch] = pSrc[256]; // Y2 + pDest[iPitch + 1] = pSrc[384]; // Y3 + return; + } + if (pJPEG->iOptions & JPEG_SCALE_QUARTER) + { + // each MCU contributes 2x2 pixels + pDest[0] = pSrc[0]; // Y0 + pDest[1] = pSrc[1]; + pDest[iPitch] = pSrc[2]; + pDest[iPitch+1] = pSrc[3]; + + pDest[2] = pSrc[128]; // Y1 + pDest[3] = pSrc[129]; + pDest[iPitch+2] = pSrc[130]; + pDest[iPitch+3] = pSrc[131]; + + pDest[iPitch*2] = pSrc[256]; // Y2 + pDest[iPitch*2+1] = pSrc[257]; + pDest[iPitch*3] = pSrc[258]; + pDest[iPitch*3+1] = pSrc[259]; + + pDest[iPitch*2+2] = pSrc[384]; // Y3 + pDest[iPitch*2+3] = pSrc[385]; + pDest[iPitch*3+2] = pSrc[386]; + pDest[iPitch*3+3] = pSrc[387]; + return; + } + if (pJPEG->iOptions & JPEG_SCALE_HALF) + { + for (i=0; i<4; i++) + { + for (j=0; j<4; j++) + { + int pix; + pix = (pSrc[j*2] + pSrc[j*2+1] + pSrc[j*2 + 8] + pSrc[j*2 + 9] + 2) >> 2; + pDest[j] = (uint8_t)pix; // Y0 + pix = (pSrc[j*2+128] + pSrc[j*2+129] + pSrc[j*2 + 136] + pSrc[j*2 + 137] + 2) >> 2; + pDest[j+4] = (uint8_t)pix; // Y1 + pix = (pSrc[j*2+256] + pSrc[j*2+257] + pSrc[j*2 + 264] + pSrc[j*2 + 265] + 2) >> 2; + pDest[iPitch*4 + j] = (uint8_t)pix; // Y2 + pix = (pSrc[j*2+384] + pSrc[j*2+385] + pSrc[j*2 + 392] + pSrc[j*2 + 393] + 2) >> 2; + pDest[iPitch*4 + j + 4] = (uint8_t)pix; // Y3 + } + pSrc += 16; + pDest += iPitch; + } + return; + } + for (i=0; i<8; i++) + { + for (j=0; j<8; j++) + { + pDest[j] = pSrc[j]; // Y0 + pDest[j+8] = pSrc[j+128]; // Y1 + pDest[iPitch*8 + j] = pSrc[j+256]; // Y2 + pDest[iPitch*8 + j + 8] = pSrc[j + 384]; // Y3 + } + pSrc += 8; + pDest += iPitch; + } + } // 0x22 +} /* JPEGMPutMCU8BitGray() */ + +static void JPEGPutMCUGray(JPEGIMAGE *pJPEG, int x, int iPitch) +{ + uint16_t *usDest = (uint16_t *)&pJPEG->usPixels[x]; + int i, j, xcount, ycount; + uint8_t *pSrc = (uint8_t *)&pJPEG->sMCUs[0]; + + if (pJPEG->iOptions & JPEG_SCALE_HALF) // special handling of 1/2 size (pixel averaging) + { + int pix; + for (i=0; i<4; i++) + { + if (pJPEG->ucPixelType == RGB565_LITTLE_ENDIAN) + { + for (j=0; j<4; j++) + { + pix = (pSrc[0] + pSrc[1] + pSrc[8] + pSrc[9] + 2) >> 2; // average 2x2 block + usDest[j] = usGrayTo565[pix]; + pSrc += 2; + } + } + else + { + for (j=0; j<4; j++) + { + pix = (pSrc[0] + pSrc[1] + pSrc[8] + pSrc[9] + 2) >> 2; // average 2x2 block + usDest[j] = __builtin_bswap16(usGrayTo565[pix]); + pSrc += 2; + } + } + pSrc += 8; // skip extra line + usDest += iPitch; + } + return; + } + xcount = ycount = 8; // debug + if (pJPEG->iOptions & JPEG_SCALE_QUARTER) + xcount = ycount = 2; + else if (pJPEG->iOptions & JPEG_SCALE_EIGHTH) + xcount = ycount = 1; + for (i=0; iucPixelType == RGB565_LITTLE_ENDIAN) + { + for (j=0; j> 14; // G + ulPixel = __USAT16(ulPixel, 6) << 5; // range limit to 6 bits + ulTmp = __SMLAD(7258, ulCbCr, iY) >> 15; // Blue + ulTmp = __USAT16(ulTmp, 5); // range limit to 5 bits + ulPixel |= ulTmp; // now we have G + B + ulTmp = __SMLAD(5742, ulCbCr >> 16, iY) >> 15; // Red + ulTmp = __USAT16(ulTmp, 5); // range limit to 5 bits + ulPixel |= (ulTmp << 11); // now we have R + G + B + pDest[0] = (uint16_t)ulPixel; +#else + int iCBB, iCBG, iCRG, iCRR; + unsigned short usPixel; + + iCBB = 7258 * (iCb-0x80); + iCBG = -1409 * (iCb-0x80); + iCRG = -2925 * (iCr-0x80); + iCRR = 5742 * (iCr-0x80); + usPixel = usRangeTableB[((iCBB + iY) >> 12) & 0x3ff]; // blue pixel + usPixel |= usRangeTableG[((iCBG + iCRG + iY) >> 12) & 0x3ff]; // green pixel + usPixel |= usRangeTableR[((iCRR + iY) >> 12) & 0x3ff]; // red pixel + pDest[0] = usPixel; +#endif +} /* JPEGPixelLE() */ + +static void JPEGPixelBE(uint16_t *pDest, int iY, int iCb, int iCr) +{ + int iCBB, iCBG, iCRG, iCRR; + unsigned short usPixel; + + iCBB = 7258 * (iCb-0x80); + iCBG = -1409 * (iCb-0x80); + iCRG = -2925 * (iCr-0x80); + iCRR = 5742 * (iCr-0x80); + usPixel = usRangeTableB[((iCBB + iY) >> 12) & 0x3ff]; // blue pixel + usPixel |= usRangeTableG[((iCBG + iCRG + iY) >> 12) & 0x3ff]; // green pixel + usPixel |= usRangeTableR[((iCRR + iY) >> 12) & 0x3ff]; // red pixel + pDest[0] = __builtin_bswap16(usPixel); +} /* JPEGPixelBE() */ + +static void JPEGPixel2LE(uint16_t *pDest, int iY1, int iY2, int iCb, int iCr) +{ + uint32_t ulPixel1, ulPixel2; +// +// Cortex-M4/M7 has some SIMD instructions which can shave a few cycles +// off of this function (e.g. Teensy, Arduino Nano 33 BLE, Portenta, etc) +// +#ifdef HAS_SIMD + uint32_t ulCbCr = (iCb | (iCr << 16)); + uint32_t ulTmp2, ulTmp = -1409 | (-2925 << 16); // for green calc + ulCbCr = __SSUB16(ulCbCr, 0x00800080); // dual 16-bit subtraction + ulPixel1 = __SMLAD(ulCbCr, ulTmp, iY1) >> 14; // G for pixel 1 + ulPixel2 = __SMLAD(ulCbCr, ulTmp, iY2) >> 14; // G for pixel 2 + ulPixel1 |= (ulPixel2 << 16); + ulPixel1 = __USAT16(ulPixel1, 6) << 5; // range limit both to 6 bits + ulTmp = __SMLAD(7258, ulCbCr, iY1) >> 15; // Blue 1 + ulTmp2 = __SMLAD(7258, ulCbCr, iY2) >> 15; // Blue 2 + ulTmp = __USAT16(ulTmp | (ulTmp2 << 16), 5); // range limit both to 5 bits + ulPixel1 |= ulTmp; // now we have G + B + ulTmp = __SMLAD(5742, ulCbCr >> 16, iY1) >> 15; // Red 1 + ulTmp2 = __SMLAD(5742, ulCbCr >> 16, iY2) >> 15; // Red 2 + ulTmp = __USAT16(ulTmp | (ulTmp2 << 16), 5); // range limit both to 5 bits + ulPixel1 |= (ulTmp << 11); // now we have R + G + B + *(uint32_t *)&pDest[0] = ulPixel1; +#else + int iCBB, iCBG, iCRG, iCRR; + iCBB = 7258 * (iCb-0x80); + iCBG = -1409 * (iCb-0x80); + iCRG = -2925 * (iCr-0x80); + iCRR = 5742 * (iCr-0x80); + ulPixel1 = usRangeTableB[((iCBB + iY1) >> 12) & 0x3ff]; // blue pixel + ulPixel1 |= usRangeTableG[((iCBG + iCRG + iY1) >> 12) & 0x3ff]; // green pixel + ulPixel1 |= usRangeTableR[((iCRR + iY1) >> 12) & 0x3ff]; // red pixel + + ulPixel2 = usRangeTableB[((iCBB + iY2) >> 12) & 0x3ff]; // blue pixel + ulPixel2 |= usRangeTableG[((iCBG + iCRG + iY2) >> 12) & 0x3ff]; // green pixel + ulPixel2 |= usRangeTableR[((iCRR + iY2) >> 12) & 0x3ff]; // red pixel + *(uint32_t *)&pDest[0] = (ulPixel1 | (ulPixel2<<16)); +#endif +} /* JPEGPixel2LE() */ + +static void JPEGPixel2BE(uint16_t *pDest, int iY1, int iY2, int iCb, int iCr) +{ + int iCBB, iCBG, iCRG, iCRR; + uint32_t ulPixel1, ulPixel2; + + iCBB = 7258 * (iCb-0x80); + iCBG = -1409 * (iCb-0x80); + iCRG = -2925 * (iCr-0x80); + iCRR = 5742 * (iCr-0x80); + ulPixel1 = usRangeTableB[((iCBB + iY1) >> 12) & 0x3ff]; // blue pixel + ulPixel1 |= usRangeTableG[((iCBG + iCRG + iY1) >> 12) & 0x3ff]; // green pixel + ulPixel1 |= usRangeTableR[((iCRR + iY1) >> 12) & 0x3ff]; // red pixel + + ulPixel2 = usRangeTableB[((iCBB + iY2) >> 12) & 0x3ff]; // blue pixel + ulPixel2 |= usRangeTableG[((iCBG + iCRG + iY2) >> 12) & 0x3ff]; // green pixel + ulPixel2 |= usRangeTableR[((iCRR + iY2) >> 12) & 0x3ff]; // red pixel + *(uint32_t *)&pDest[0] = __builtin_bswap16(ulPixel1) | (__builtin_bswap16(ulPixel2)<<16); +} /* JPEGPixel2BE() */ + +static void JPEGPutMCU11(JPEGIMAGE *pJPEG, int x, int iPitch) +{ + int iCr, iCb; + signed int Y; + int iCol; + int iRow; + uint8_t *pY, *pCr, *pCb; + uint16_t *pOutput = &pJPEG->usPixels[x]; + + pY = (unsigned char *)&pJPEG->sMCUs[0*DCTSIZE]; + pCb = (unsigned char *)&pJPEG->sMCUs[1*DCTSIZE]; + pCr = (unsigned char *)&pJPEG->sMCUs[2*DCTSIZE]; + + if (pJPEG->iOptions & JPEG_SCALE_HALF) + { + for (iRow=0; iRow<4; iRow++) // up to 8 rows to do + { + for (iCol=0; iCol<4; iCol++) // up to 4x2 cols to do + { + iCr = (pCr[0] + pCr[1] + pCr[8] + pCr[9] + 2) >> 2; + iCb = (pCb[0] + pCb[1] + pCb[8] + pCb[9] + 2) >> 2; + Y = (pY[0] + pY[1] + pY[8] + pY[9]) << 10; + if (pJPEG->ucPixelType == RGB565_LITTLE_ENDIAN) + JPEGPixelLE(pOutput+iCol, Y, iCb, iCr); + else + JPEGPixelBE(pOutput+iCol, Y, iCb, iCr); + pCr += 2; + pCb += 2; + pY += 2; + } // for col + pCr += 8; + pCb += 8; + pY += 8; + pOutput += iPitch; + } // for row + return; + } + if (pJPEG->iOptions & JPEG_SCALE_EIGHTH) // special case for 1/8 scaling + { + // only 4 pixels to draw, so no looping needed + iCr = pCr[0]; + iCb = pCb[0]; + Y = (int)(pY[0]) << 12; + if (pJPEG->ucPixelType == RGB565_LITTLE_ENDIAN) + JPEGPixelLE(pOutput, Y, iCb, iCr); + else + JPEGPixelBE(pOutput, Y, iCb, iCr); + return; + } + if (pJPEG->iOptions & JPEG_SCALE_QUARTER) // special case for 1/4 scaling + { + // only 4 pixels to draw, so no looping needed + if (pJPEG->ucPixelType == RGB565_LITTLE_ENDIAN) + { + iCr = *pCr++; + iCb = *pCb++; + Y = (int)(*pY++) << 12; + JPEGPixelLE(pOutput, Y, iCb, iCr); + iCr = *pCr++; + iCb = *pCb++; + Y = (int)(*pY++) << 12; + JPEGPixelLE(pOutput+1, Y, iCb, iCr); + iCr = *pCr++; + iCb = *pCb++; + Y = (int)(*pY++) << 12; + JPEGPixelLE(pOutput+iPitch, Y, iCb, iCr); + iCr = *pCr++; + iCb = *pCb++; + Y = (int)(*pY++) << 12; + JPEGPixelLE(pOutput+1+iPitch, Y, iCb, iCr); + } + else + { + iCr = *pCr++; + iCb = *pCb++; + Y = (int)(*pY++) << 12; + JPEGPixelBE(pOutput, Y, iCb, iCr); + iCr = *pCr++; + iCb = *pCb++; + Y = (int)(*pY++) << 12; + JPEGPixelBE(pOutput+1, Y, iCb, iCr); + iCr = *pCr++; + iCb = *pCb++; + Y = (int)(*pY++) << 12; + JPEGPixelBE(pOutput+iPitch, Y, iCb, iCr); + iCr = *pCr++; + iCb = *pCb++; + Y = (int)(*pY++) << 12; + JPEGPixelBE(pOutput+1+iPitch, Y, iCb, iCr); + } + return; + } + for (iRow=0; iRow<8; iRow++) // up to 8 rows to do + { + if (pJPEG->ucPixelType == RGB565_LITTLE_ENDIAN) + { + for (iCol=0; iCol<8; iCol++) // up to 4x2 cols to do + { + iCr = *pCr++; + iCb = *pCb++; + Y = (int)(*pY++) << 12; + JPEGPixelLE(pOutput+iCol, Y, iCb, iCr); + } // for col + } + else + { + for (iCol=0; iCol<8; iCol++) // up to 4x2 cols to do + { + iCr = *pCr++; + iCb = *pCb++; + Y = (int)(*pY++) << 12; + JPEGPixelBE(pOutput+iCol, Y, iCb, iCr); + } // for col + } + pOutput += iPitch; + } // for row +} /* JPEGPutMCU11() */ + +static void JPEGPutMCU22(JPEGIMAGE *pJPEG, int x, int iPitch) +{ + uint32_t Cr,Cb; + signed int Y1, Y2, Y3, Y4; + int iRow, iCol, iXCount1, iXCount2, iYCount; + unsigned char *pY, *pCr, *pCb; + int bUseOdd1, bUseOdd2; // special case where 24bpp odd sized image can clobber first column + uint16_t *pOutput = &pJPEG->usPixels[x]; + + pY = (unsigned char *)&pJPEG->sMCUs[0*DCTSIZE]; + pCb = (unsigned char *)&pJPEG->sMCUs[4*DCTSIZE]; + pCr = (unsigned char *)&pJPEG->sMCUs[5*DCTSIZE]; + + if (pJPEG->iOptions & JPEG_SCALE_HALF) // special handling of 1/2 size (pixel averaging) + { + for (iRow=0; iRow<4; iRow++) // 16x16 becomes 8x8 of 2x2 pixels + { + for (iCol=0; iCol<4; iCol++) + { + Y1 = (pY[iCol*2] + pY[iCol*2+1] + pY[iCol*2+8] + pY[iCol*2+9]) << 10; + Cb = pCb[iCol]; + Cr = pCr[iCol]; + if (pJPEG->ucPixelType == RGB565_LITTLE_ENDIAN) + JPEGPixelLE(pOutput+iCol, Y1, Cb, Cr); // top left + else + JPEGPixelBE(pOutput+iCol, Y1, Cb, Cr); + Y1 = (pY[iCol*2+(DCTSIZE*2)] + pY[iCol*2+1+(DCTSIZE*2)] + pY[iCol*2+8+(DCTSIZE*2)] + pY[iCol*2+9+(DCTSIZE*2)]) << 10; + Cb = pCb[iCol+4]; + Cr = pCr[iCol+4]; + if (pJPEG->ucPixelType == RGB565_LITTLE_ENDIAN) + JPEGPixelLE(pOutput+iCol+4, Y1, Cb, Cr); // top right + else + JPEGPixelBE(pOutput+iCol+4, Y1, Cb, Cr); + Y1 = (pY[iCol*2+(DCTSIZE*4)] + pY[iCol*2+1+(DCTSIZE*4)] + pY[iCol*2+8+(DCTSIZE*4)] + pY[iCol*2+9+(DCTSIZE*4)]) << 10; + Cb = pCb[iCol+32]; + Cr = pCr[iCol+32]; + if (pJPEG->ucPixelType == RGB565_LITTLE_ENDIAN) + JPEGPixelLE(pOutput+iCol+iPitch*4, Y1, Cb, Cr); // bottom left + else + JPEGPixelBE(pOutput+iCol+iPitch*4, Y1, Cb, Cr); + Y1 = (pY[iCol*2+(DCTSIZE*6)] + pY[iCol*2+1+(DCTSIZE*6)] + pY[iCol*2+8+(DCTSIZE*6)] + pY[iCol*2+9+(DCTSIZE*6)]) << 10; + Cb = pCb[iCol+32+4]; + Cr = pCr[iCol+32+4]; + if (pJPEG->ucPixelType == RGB565_LITTLE_ENDIAN) + JPEGPixelLE(pOutput+iCol+4+iPitch*4, Y1, Cb, Cr); // bottom right + else + JPEGPixelBE(pOutput+iCol+4+iPitch*4, Y1, Cb, Cr); + } + pY += 16; + pCb += 8; + pCr += 8; + pOutput += iPitch; + } + return; + } + if (pJPEG->iOptions & JPEG_SCALE_EIGHTH) + { + Y1 = pY[0] << 12; // scale to level of conversion table + Cb = pCb[0]; + Cr = pCr[0]; + if (pJPEG->ucPixelType == RGB565_LITTLE_ENDIAN) + JPEGPixelLE(pOutput, Y1, Cb, Cr); + else + JPEGPixelBE(pOutput, Y1, Cb, Cr); + // top right block + Y1 = pY[DCTSIZE*2] << 12; // scale to level of conversion table + if (pJPEG->ucPixelType == RGB565_LITTLE_ENDIAN) + JPEGPixelLE(pOutput + 1, Y1, Cb, Cr); + else + JPEGPixelBE(pOutput + 1, Y1, Cb, Cr); + // bottom left block + Y1 = pY[DCTSIZE*4] << 12; // scale to level of conversion table + if (pJPEG->ucPixelType == RGB565_LITTLE_ENDIAN) + JPEGPixelLE(pOutput+iPitch, Y1, Cb, Cr); + else + JPEGPixelBE(pOutput+iPitch, Y1, Cb, Cr); + // bottom right block + Y1 = pY[DCTSIZE*6] << 12; // scale to level of conversion table + if (pJPEG->ucPixelType == RGB565_LITTLE_ENDIAN) + JPEGPixelLE(pOutput+ 1 + iPitch, Y1, Cb, Cr); + else + JPEGPixelBE(pOutput+ 1 + iPitch, Y1, Cb, Cr); + return; + } + if (pJPEG->iOptions & JPEG_SCALE_QUARTER) // special case of 1/4 + { + for (iRow=0; iRow<2; iRow++) + { + if (pJPEG->ucPixelType == RGB565_LITTLE_ENDIAN) + { + for (iCol=0; iCol<2; iCol++) + { + // top left block + Y1 = pY[iCol] << 12; // scale to level of conversion table + Cb = pCb[0]; + Cr = pCr[0]; + JPEGPixelLE(pOutput + iCol, Y1, Cb, Cr); + // top right block + Y1 = pY[iCol+(DCTSIZE*2)] << 12; // scale to level of conversion table + Cb = pCb[1]; + Cr = pCr[1]; + JPEGPixelLE(pOutput + 2+iCol, Y1, Cb, Cr); + // bottom left block + Y1 = pY[iCol+DCTSIZE*4] << 12; // scale to level of conversion table + Cb = pCb[2]; + Cr = pCr[2]; + JPEGPixelLE(pOutput+iPitch*2 + iCol, Y1, Cb, Cr); + // bottom right block + Y1 = pY[iCol+DCTSIZE*6] << 12; // scale to level of conversion table + Cb = pCb[3]; + Cr = pCr[3]; + JPEGPixelLE(pOutput+iPitch*2 + 2+iCol, Y1, Cb, Cr); + } // for each column + } + else + { + for (iCol=0; iCol<2; iCol++) + { + // top left block + Y1 = pY[iCol] << 12; // scale to level of conversion table + Cb = pCb[0]; + Cr = pCr[0]; + JPEGPixelBE(pOutput + iCol, Y1, Cb, Cr); + // top right block + Y1 = pY[iCol+(DCTSIZE*2)] << 12; // scale to level of conversion table + Cb = pCb[1]; + Cr = pCr[1]; + JPEGPixelBE(pOutput + 2+iCol, Y1, Cb, Cr); + // bottom left block + Y1 = pY[iCol+DCTSIZE*4] << 12; // scale to level of conversion table + Cb = pCb[2]; + Cr = pCr[2]; + JPEGPixelBE(pOutput+iPitch*2 + iCol, Y1, Cb, Cr); + // bottom right block + Y1 = pY[iCol+DCTSIZE*6] << 12; // scale to level of conversion table + Cb = pCb[3]; + Cr = pCr[3]; + JPEGPixelBE(pOutput+iPitch*2 + 2+iCol, Y1, Cb, Cr); + } // for each column + } + pY += 2; // skip 1 line of source pixels + pOutput += iPitch; + } + return; + } + /* Convert YCC pixels into RGB pixels and store in output image */ + iYCount = 4; + bUseOdd1 = bUseOdd2 = 1; // assume odd column can be used + if ((x+15) >= pJPEG->iWidth) + { + iCol = (((pJPEG->iWidth & 15)+1) >> 1); + if (iCol >= 4) + { + iXCount1 = 4; + iXCount2 = iCol-4; + if (pJPEG->iWidth & 1 && (iXCount2 * 2) + 8 + (x * 16) > pJPEG->iWidth) + bUseOdd2 = 0; + } + else + { + iXCount1 = iCol; + iXCount2 = 0; + if (pJPEG->iWidth & 1 && (iXCount1 * 2) + (x * 16) > pJPEG->iWidth) + bUseOdd1 = 0; + } + } + else + iXCount1 = iXCount2 = 4; + for (iRow=0; iRowucPixelType == RGB565_LITTLE_ENDIAN) + { + if (bUseOdd1 || iCol != (iXCount1-1)) // only render if it won't go off the right edge + { + JPEGPixel2LE(pOutput + (iCol<<1), Y1, Y2, Cb, Cr); + JPEGPixel2LE(pOutput+iPitch + (iCol<<1), Y3, Y4, Cb, Cr); + } + else + { + JPEGPixelLE(pOutput + (iCol<<1), Y1, Cb, Cr); + JPEGPixelLE(pOutput+iPitch + (iCol<<1), Y3, Cb, Cr); + } + } + else + { + if (bUseOdd1 || iCol != (iXCount1-1)) // only render if it won't go off the right edge + { + JPEGPixel2BE(pOutput + (iCol<<1), Y1, Y2, Cb, Cr); + JPEGPixel2BE(pOutput+iPitch + (iCol<<1), Y3, Y4, Cb, Cr); + } + else + { + JPEGPixelBE(pOutput + (iCol<<1), Y1, Cb, Cr); + JPEGPixelBE(pOutput+iPitch + (iCol<<1), Y3, Cb, Cr); + } + } + // for top right block + if (iCol < iXCount2) + { + Y1 = pY[iCol*2+DCTSIZE*2]; + Y2 = pY[iCol*2+1+DCTSIZE*2]; + Y3 = pY[iCol*2+8+DCTSIZE*2]; + Y4 = pY[iCol*2+9+DCTSIZE*2]; + Y1 <<= 12; // scale to level of conversion table + Y2 <<= 12; + Y3 <<= 12; + Y4 <<= 12; + Cb = pCb[iCol+4]; + Cr = pCr[iCol+4]; + if (pJPEG->ucPixelType == RGB565_LITTLE_ENDIAN) + { + if (bUseOdd2 || iCol != (iXCount2-1)) // only render if it won't go off the right edge + { + JPEGPixel2LE(pOutput + 8+(iCol<<1), Y1, Y2, Cb, Cr); + JPEGPixel2LE(pOutput+iPitch + 8+(iCol<<1), Y3, Y4, Cb, Cr); + } + else + { + JPEGPixelLE(pOutput+ 8+(iCol<<1), Y1, Cb, Cr); + JPEGPixelLE(pOutput+iPitch+ 8+(iCol<<1), Y3, Cb, Cr); + } + } + else + { + if (bUseOdd2 || iCol != (iXCount2-1)) // only render if it won't go off the right edge + { + JPEGPixel2BE(pOutput + 8+(iCol<<1), Y1, Y2, Cb, Cr); + JPEGPixel2BE(pOutput+iPitch + 8+(iCol<<1), Y3, Y4, Cb, Cr); + } + else + { + JPEGPixelBE(pOutput+ 8+(iCol<<1), Y1, Cb, Cr); + JPEGPixelBE(pOutput+iPitch+ 8+(iCol<<1), Y3, Cb, Cr); + } + } + } + // for bottom left block + Y1 = pY[iCol*2+DCTSIZE*4]; + Y2 = pY[iCol*2+1+DCTSIZE*4]; + Y3 = pY[iCol*2+8+DCTSIZE*4]; + Y4 = pY[iCol*2+9+DCTSIZE*4]; + Y1 <<= 12; // scale to level of conversion table + Y2 <<= 12; + Y3 <<= 12; + Y4 <<= 12; + Cb = pCb[iCol+32]; + Cr = pCr[iCol+32]; + if (pJPEG->ucPixelType == RGB565_LITTLE_ENDIAN) + { + if (bUseOdd1 || iCol != (iXCount1-1)) // only render if it won't go off the right edge + { + JPEGPixel2LE(pOutput+iPitch*8+ (iCol<<1), Y1, Y2, Cb, Cr); + JPEGPixel2LE(pOutput+iPitch*9+ (iCol<<1), Y3, Y4, Cb, Cr); + } + else + { + JPEGPixelLE(pOutput+iPitch*8+ (iCol<<1), Y1, Cb, Cr); + JPEGPixelLE(pOutput+iPitch*9+ (iCol<<1), Y3, Cb, Cr); + } + } + else + { + if (bUseOdd1 || iCol != (iXCount1-1)) // only render if it won't go off the right edge + { + JPEGPixel2BE(pOutput+iPitch*8+ (iCol<<1), Y1, Y2, Cb, Cr); + JPEGPixel2BE(pOutput+iPitch*9+ (iCol<<1), Y3, Y4, Cb, Cr); + } + else + { + JPEGPixelBE(pOutput+iPitch*8+ (iCol<<1), Y1, Cb, Cr); + JPEGPixelBE(pOutput+iPitch*9+ (iCol<<1), Y3, Cb, Cr); + } + } + // for bottom right block + if (iCol < iXCount2) + { + Y1 = pY[iCol*2+DCTSIZE*6]; + Y2 = pY[iCol*2+1+DCTSIZE*6]; + Y3 = pY[iCol*2+8+DCTSIZE*6]; + Y4 = pY[iCol*2+9+DCTSIZE*6]; + Y1 <<= 12; // scale to level of conversion table + Y2 <<= 12; + Y3 <<= 12; + Y4 <<= 12; + Cb = pCb[iCol+36]; + Cr = pCr[iCol+36]; + if (pJPEG->ucPixelType == RGB565_LITTLE_ENDIAN) + { + if (bUseOdd2 || iCol != (iXCount2-1)) // only render if it won't go off the right edge + { + JPEGPixel2LE(pOutput+iPitch*8+ 8+(iCol<<1), Y1, Y2, Cb, Cr); + JPEGPixel2LE(pOutput+iPitch*9+ 8+(iCol<<1), Y3, Y4, Cb, Cr); + } + else + { + JPEGPixelLE(pOutput+iPitch*8+ 8+(iCol<<1), Y1, Cb, Cr); + JPEGPixelLE(pOutput+iPitch*9+ 8+(iCol<<1), Y3, Cb, Cr); + } + } + else + { + if (bUseOdd2 || iCol != (iXCount2-1)) // only render if it won't go off the right edge + { + JPEGPixel2BE(pOutput+iPitch*8+ 8+(iCol<<1), Y1, Y2, Cb, Cr); + JPEGPixel2BE(pOutput+iPitch*9+ 8+(iCol<<1), Y3, Y4, Cb, Cr); + } + else + { + JPEGPixelBE(pOutput+iPitch*8+ 8+(iCol<<1), Y1, Cb, Cr); + JPEGPixelBE(pOutput+iPitch*9+ 8+(iCol<<1), Y3, Cb, Cr); + } + } + } + } // for each column + pY += 16; // skip to next line of source pixels + pCb += 8; + pCr += 8; + pOutput += iPitch*2; + } +} /* JPEGPutMCU22() */ + +static void JPEGPutMCU12(JPEGIMAGE *pJPEG, int x, int iPitch) +{ + uint32_t Cr,Cb; + signed int Y1, Y2; + int iRow, iCol, iXCount, iYCount; + uint8_t *pY, *pCr, *pCb; + uint16_t *pOutput = &pJPEG->usPixels[x]; + + pY = (uint8_t *)&pJPEG->sMCUs[0*DCTSIZE]; + pCb = (uint8_t *)&pJPEG->sMCUs[2*DCTSIZE]; + pCr = (uint8_t *)&pJPEG->sMCUs[3*DCTSIZE]; + + if (pJPEG->iOptions & JPEG_SCALE_HALF) + { + for (iRow=0; iRow<4; iRow++) + { + for (iCol=0; iCol<4; iCol++) + { + Y1 = (pY[0] + pY[1] + pY[8] + pY[9]) << 10; + Cb = (pCb[0] + pCb[1] + 1) >> 1; + Cr = (pCr[0] + pCr[1] + 1) >> 1; + if (pJPEG->ucPixelType == RGB565_LITTLE_ENDIAN) + JPEGPixelLE(pOutput+iCol, Y1, Cb, Cr); + else + JPEGPixelBE(pOutput+iCol, Y1, Cb, Cr); + Y1 = (pY[DCTSIZE*2] + pY[DCTSIZE*2+1] + pY[DCTSIZE*2+8] + pY[DCTSIZE*2+9]) << 10; + Cb = (pCb[32] + pCb[33] + 1) >> 1; + Cr = (pCr[32] + pCr[33] + 1) >> 1; + if (pJPEG->ucPixelType == RGB565_LITTLE_ENDIAN) + JPEGPixelLE(pOutput+iCol+iPitch, Y1, Cb, Cr); + else + JPEGPixelBE(pOutput+iCol+iPitch, Y1, Cb, Cr); + pCb += 2; + pCr += 2; + pY += 2; + } + pY += 8; + pOutput += iPitch*2; + } + return; + } + if (pJPEG->iOptions & JPEG_SCALE_EIGHTH) + { + Y1 = pY[0] << 12; + Y2 = pY[DCTSIZE*2] << 12; + Cb = pCb[0]; + Cr = pCr[0]; + if (pJPEG->ucPixelType == RGB565_LITTLE_ENDIAN) + { + JPEGPixelLE(pOutput, Y1, Cb, Cr); + JPEGPixelLE(pOutput + iPitch, Y2, Cb, Cr); + } + else + { + JPEGPixelBE(pOutput, Y1, Cb, Cr); + JPEGPixelBE(pOutput + iPitch, Y2, Cb, Cr); + } + return; + } + if (pJPEG->iOptions & JPEG_SCALE_QUARTER) + { // draw a 2x4 block + Y1 = pY[0] << 12; + Y2 = pY[2] << 12; + Cb = pCb[0]; + Cr = pCr[0]; + if (pJPEG->ucPixelType == RGB565_LITTLE_ENDIAN) + { + JPEGPixelLE(pOutput, Y1, Cb, Cr); + JPEGPixelLE(pOutput + iPitch, Y2, Cb, Cr); + } + else + { + JPEGPixelBE(pOutput, Y1, Cb, Cr); + JPEGPixelBE(pOutput + iPitch, Y2, Cb, Cr); + } + Y1 = pY[1] << 12; + Y2 = pY[3] << 12; + Cb = pCb[1]; + Cr = pCr[1]; + if (pJPEG->ucPixelType == RGB565_LITTLE_ENDIAN) + { + JPEGPixelLE(pOutput + 1, Y1, Cb, Cr); + JPEGPixelLE(pOutput + 1 + iPitch, Y2, Cb, Cr); + } + else + { + JPEGPixelBE(pOutput + 1, Y1, Cb, Cr); + JPEGPixelBE(pOutput + 1 + iPitch, Y2, Cb, Cr); + } + pY += DCTSIZE*2; // next Y block below + Y1 = pY[0] << 12; + Y2 = pY[2] << 12; + Cb = pCb[2]; + Cr = pCr[2]; + if (pJPEG->ucPixelType == RGB565_LITTLE_ENDIAN) + { + JPEGPixelLE(pOutput + iPitch*2, Y1, Cb, Cr); + JPEGPixelLE(pOutput + iPitch*3, Y2, Cb, Cr); + } + else + { + JPEGPixelBE(pOutput + iPitch*2, Y1, Cb, Cr); + JPEGPixelBE(pOutput + iPitch*3, Y2, Cb, Cr); + } + Y1 = pY[1] << 12; + Y2 = pY[3] << 12; + Cb = pCb[3]; + Cr = pCr[3]; + if (pJPEG->ucPixelType == RGB565_LITTLE_ENDIAN) + { + JPEGPixelLE(pOutput + 1 + iPitch*2, Y1, Cb, Cr); + JPEGPixelLE(pOutput + 1 + iPitch*3, Y2, Cb, Cr); + } + else + { + JPEGPixelBE(pOutput + 1 + iPitch*2, Y1, Cb, Cr); + JPEGPixelBE(pOutput + 1 + iPitch*3, Y2, Cb, Cr); + } + return; + } + /* Convert YCC pixels into RGB pixels and store in output image */ + iYCount = 16; + iXCount = 8; + for (iRow=0; iRowucPixelType == RGB565_LITTLE_ENDIAN) + { + JPEGPixelLE(pOutput + iCol, Y1, Cb, Cr); + JPEGPixelLE(pOutput + iPitch + iCol, Y2, Cb, Cr); + } + else + { + JPEGPixelBE(pOutput + iCol, Y1, Cb, Cr); + JPEGPixelBE(pOutput + iPitch + iCol, Y2, Cb, Cr); + } + } + pY += 16; // skip to next 2 lines of source pixels + if (iRow == 6) // next MCU block, skip ahead to correct spot + pY += (128-64); + pCb += 8; + pCr += 8; + pOutput += iPitch*2; // next 2 lines of dest pixels + } +} /* JPEGPutMCU12() */ +static void JPEGPutMCU21(JPEGIMAGE *pJPEG, int x, int iPitch) +{ + int iCr, iCb; + signed int Y1, Y2; + int iCol; + int iRow; + uint8_t *pY, *pCr, *pCb; + uint16_t *pOutput = &pJPEG->usPixels[x]; + + pY = (uint8_t *)&pJPEG->sMCUs[0*DCTSIZE]; + pCb = (uint8_t *)&pJPEG->sMCUs[2*DCTSIZE]; + pCr = (uint8_t *)&pJPEG->sMCUs[3*DCTSIZE]; + + if (pJPEG->iOptions & JPEG_SCALE_HALF) + { + for (iRow=0; iRow<4; iRow++) + { + for (iCol=0; iCol<4; iCol++) + { // left block + iCr = (pCr[0] + pCr[8] + 1) >> 1; + iCb = (pCb[0] + pCb[8] + 1) >> 1; + Y1 = (signed int)(pY[0] + pY[1] + pY[8] + pY[9]) << 10; + if (pJPEG->ucPixelType == RGB565_LITTLE_ENDIAN) + JPEGPixelLE(pOutput+iCol, Y1, iCb, iCr); + else + JPEGPixelBE(pOutput+iCol, Y1, iCb, iCr); + // right block + iCr = (pCr[4] + pCr[12] + 1) >> 1; + iCb = (pCb[4] + pCb[12] + 1) >> 1; + Y1 = (signed int)(pY[128] + pY[129] + pY[136] + pY[137]) << 10; + if (pJPEG->ucPixelType == RGB565_LITTLE_ENDIAN) + JPEGPixelLE(pOutput+iCol+4, Y1, iCb, iCr); + else + JPEGPixelBE(pOutput+iCol+4, Y1, iCb, iCr); + pCb++; + pCr++; + pY += 2; + } + pCb += 12; + pCr += 12; + pY += 8; + pOutput += iPitch; + } + return; + } + if (pJPEG->iOptions & JPEG_SCALE_EIGHTH) + { // draw 2 pixels + iCr = pCr[0]; + iCb = pCb[0]; + Y1 = (signed int)(pY[0]) << 12; + Y2 = (signed int)(pY[DCTSIZE*2]) << 12; + if (pJPEG->ucPixelType == RGB565_LITTLE_ENDIAN) + JPEGPixel2LE(pOutput, Y1, Y2, iCb, iCr); + else + JPEGPixel2BE(pOutput, Y1, Y2, iCb, iCr); + return; + } + if (pJPEG->iOptions & JPEG_SCALE_QUARTER) + { // draw 4x2 pixels + // top left + iCr = pCr[0]; + iCb = pCb[0]; + Y1 = (signed int)(pY[0]) << 12; + Y2 = (signed int)(pY[1]) << 12; + if (pJPEG->ucPixelType == RGB565_LITTLE_ENDIAN) + JPEGPixel2LE(pOutput, Y1, Y2, iCb, iCr); + else + JPEGPixel2BE(pOutput, Y1, Y2, iCb, iCr); + // top right + iCr = pCr[1]; + iCb = pCb[1]; + Y1 = (signed int)pY[DCTSIZE*2] << 12; + Y2 = (signed int)pY[DCTSIZE*2+1] << 12; + if (pJPEG->ucPixelType == RGB565_LITTLE_ENDIAN) + JPEGPixel2LE(pOutput + 2, Y1, Y2, iCb, iCr); + else + JPEGPixel2BE(pOutput + 2, Y1, Y2, iCb, iCr); + // bottom left + iCr = pCr[2]; + iCb = pCb[2]; + Y1 = (signed int)(pY[2]) << 12; + Y2 = (signed int)(pY[3]) << 12; + if (pJPEG->ucPixelType == RGB565_LITTLE_ENDIAN) + JPEGPixel2LE(pOutput + iPitch, Y1, Y2, iCb, iCr); + else + JPEGPixel2BE(pOutput + iPitch, Y1, Y2, iCb, iCr); + // bottom right + iCr = pCr[3]; + iCb = pCb[3]; + Y1 = (signed int)pY[DCTSIZE*2+2] << 12; + Y2 = (signed int)pY[DCTSIZE*2+3] << 12; + if (pJPEG->ucPixelType == RGB565_LITTLE_ENDIAN) + JPEGPixel2LE(pOutput + iPitch + 2, Y1, Y2, iCb, iCr); + else + JPEGPixel2BE(pOutput + iPitch + 2, Y1, Y2, iCb, iCr); + return; + } + /* Convert YCC pixels into RGB pixels and store in output image */ + for (iRow=0; iRow<8; iRow++) // up to 8 rows to do + { + for (iCol=0; iCol<4; iCol++) // up to 4x2 cols to do + { // left block + iCr = *pCr++; + iCb = *pCb++; + Y1 = (signed int)(*pY++) << 12; + Y2 = (signed int)(*pY++) << 12; + if (pJPEG->ucPixelType == RGB565_LITTLE_ENDIAN) + JPEGPixel2LE(pOutput + iCol*2, Y1, Y2, iCb, iCr); + else + JPEGPixel2BE(pOutput + iCol*2, Y1, Y2, iCb, iCr); + // right block + iCr = pCr[3]; + iCb = pCb[3]; + Y1 = (signed int)pY[126] << 12; + Y2 = (signed int)pY[127] << 12; + if (pJPEG->ucPixelType == RGB565_LITTLE_ENDIAN) + JPEGPixel2LE(pOutput + 8 + iCol*2, Y1, Y2, iCb, iCr); + else + JPEGPixel2BE(pOutput + 8 + iCol*2, Y1, Y2, iCb, iCr); + } // for col + pCb += 4; + pCr += 4; + pOutput += iPitch; + } // for row +} /* JPEGPutMCU21() */ + +// Dither the 8-bit gray pixels into 1, 2, or 4-bit gray +static void JPEGDither(JPEGIMAGE *pJPEG, int iWidth, int iHeight) +{ +int x, y, xmask=0, iDestPitch=0; +int32_t cNew, lFErr, v=0, h; +int32_t e1,e2,e3,e4; +uint8_t cOut, ucPixelType; // forward errors for gray +uint8_t *pSrc, *pDest, *errors, *pErrors=NULL, *d, *pPixels; // destination 8bpp image +uint8_t pixelmask=0, shift=0; + + ucPixelType = pJPEG->ucPixelType; + errors = (uint8_t *)pJPEG->usPixels; // plenty of space here + errors[0] = errors[1] = errors[2] = 0; + pDest = pSrc = pJPEG->pDitherBuffer; // write the new pixels over the original + switch (ucPixelType) + { + case FOUR_BIT_DITHERED: + iDestPitch = (iWidth+1)/2; + pixelmask = 0xf0; + shift = 4; + xmask = 1; + break; + case TWO_BIT_DITHERED: + iDestPitch = (iWidth+3)/4; + pixelmask = 0xc0; + shift = 2; + xmask = 3; + break; + case ONE_BIT_DITHERED: + iDestPitch = (iWidth+7)/8; + pixelmask = 0x80; + shift = 1; + xmask = 7; + break; + } + for (y=0; y 255) cNew = 255; // clip to uint8_t + cOut <<= shift; // pack new pixels into a byte + cOut |= (cNew >> (8-shift)); // keep top N bits + if ((x & xmask) == xmask) // store it when the byte is full + { + *d++ = cOut; + cOut = 0; + } + // calculate the Floyd-Steinberg error for this pixel + v = cNew - (cNew & pixelmask); // new error for N-bit gray output (always positive) + h = v >> 1; + e1 = (7*h)>>3; // 7/16 + e2 = h - e1; // 1/16 + e3 = (5*h) >> 3; // 5/16 + e4 = h - e3; // 3/16 + // distribute error to neighbors + lFErr = e1 + pErrors[1]; + pErrors[1] = (uint8_t)e2; + pErrors[0] += e3; + pErrors[-1] += e4; + pErrors++; + } // for x + } // for y +} /* JPEGDither() */ + +// +// Decode the image +// returns 0 for error, 1 for success +// +static int DecodeJPEG(JPEGIMAGE *pJPEG) +{ + int cx, cy, x, y, mcuCX, mcuCY; + int iLum0, iLum1, iLum2, iLum3, iCr, iCb; + signed int iDCPred0, iDCPred1, iDCPred2; + int i, iQuant1, iQuant2, iQuant3, iErr; + uint8_t c; + int iMCUCount, xoff, iPitch, bThumbnail = 0; + int bContinue = 1; // early exit if the DRAW callback wants to stop + uint32_t l, *pl; + unsigned char cDCTable0, cACTable0, cDCTable1, cACTable1, cDCTable2, cACTable2; + JPEGDRAW jd; + int iMaxFill = 16, iScaleShift = 0; + + // Requested the Exif thumbnail + if (pJPEG->iOptions & JPEG_EXIF_THUMBNAIL) + { + if (pJPEG->iThumbData == 0 || pJPEG->iThumbWidth == 0) // doesn't exist + { + pJPEG->iError = JPEG_INVALID_PARAMETER; + return 0; + } + if (!JPEGParseInfo(pJPEG, 1)) // parse the embedded thumbnail file header + return 0; // something went wrong + } + // Fast downscaling options + if (pJPEG->iOptions & JPEG_SCALE_HALF) + iScaleShift = 1; + else if (pJPEG->iOptions & JPEG_SCALE_QUARTER) + { + iScaleShift = 2; + iMaxFill = 1; + } + else if (pJPEG->iOptions & JPEG_SCALE_EIGHTH) + { + iScaleShift = 3; + iMaxFill = 1; + bThumbnail = 1; + } + + // reorder and fix the quantization table for decoding + JPEGFixQuantD(pJPEG); + pJPEG->bb.ulBits = MOTOLONG(&pJPEG->ucFileBuf[0]); // preload first 4 bytes + pJPEG->bb.pBuf = pJPEG->ucFileBuf; + pJPEG->bb.ulBitOff = 0; + + cDCTable0 = pJPEG->JPCI[0].dc_tbl_no; + cACTable0 = pJPEG->JPCI[0].ac_tbl_no; + cDCTable1 = pJPEG->JPCI[1].dc_tbl_no; + cACTable1 = pJPEG->JPCI[1].ac_tbl_no; + cDCTable2 = pJPEG->JPCI[2].dc_tbl_no; + cACTable2 = pJPEG->JPCI[2].ac_tbl_no; + iDCPred0 = iDCPred1 = iDCPred2 = mcuCX = mcuCY = 0; + + switch (pJPEG->ucSubSample) // set up the parameters for the different subsampling options + { + case 0x00: // fake value to handle grayscale + case 0x01: // fake value to handle sRGB/CMYK + case 0x11: + cx = (pJPEG->iWidth + 7) >> 3; // number of MCU blocks + cy = (pJPEG->iHeight + 7) >> 3; + iCr = MCU1; + iCb = MCU2; + mcuCX = mcuCY = 8; + break; + case 0x12: + cx = (pJPEG->iWidth + 7) >> 3; // number of MCU blocks + cy = (pJPEG->iHeight + 15) >> 4; + iCr = MCU2; + iCb = MCU3; + mcuCX = 8; + mcuCY = 16; + break; + case 0x21: + cx = (pJPEG->iWidth + 15) >> 4; // number of MCU blocks + cy = (pJPEG->iHeight + 7) >> 3; + iCr = MCU2; + iCb = MCU3; + mcuCX = 16; + mcuCY = 8; + break; + case 0x22: + cx = (pJPEG->iWidth + 15) >> 4; // number of MCU blocks + cy = (pJPEG->iHeight + 15) >> 4; + iCr = MCU4; + iCb = MCU5; + mcuCX = mcuCY = 16; + break; + default: // to suppress compiler warning + cx = cy = 0; + iCr = iCb = 0; + break; + } + // Scale down the MCUs by the requested amount + mcuCX >>= iScaleShift; + mcuCY >>= iScaleShift; + + iQuant1 = pJPEG->sQuantTable[pJPEG->JPCI[0].quant_tbl_no*DCTSIZE]; // DC quant values + iQuant2 = pJPEG->sQuantTable[pJPEG->JPCI[1].quant_tbl_no*DCTSIZE]; + iQuant3 = pJPEG->sQuantTable[pJPEG->JPCI[2].quant_tbl_no*DCTSIZE]; + // luminance values are always in these positions + iLum0 = MCU0; + iLum1 = MCU1; + iLum2 = MCU2; + iLum3 = MCU3; + iErr = 0; + pJPEG->iResCount = pJPEG->iResInterval; + // Calculate how many MCUs we can fit in the pixel buffer to maximize LCD drawing speed + iMCUCount = MAX_BUFFERED_PIXELS / (mcuCX * mcuCY); + if (pJPEG->ucPixelType == EIGHT_BIT_GRAYSCALE) + iMCUCount *= 2; // each pixel is only 1 byte + if (iMCUCount > cx) + iMCUCount = cx; // don't go wider than the image + if (iMCUCount > pJPEG->iMaxMCUs) // did the user set an upper bound on how many pixels per JPEGDraw callback? + iMCUCount = pJPEG->iMaxMCUs; + if (pJPEG->ucPixelType > EIGHT_BIT_GRAYSCALE) // dithered, override the max MCU count + iMCUCount = cx; // do the whole row + jd.iBpp = 16; + switch (pJPEG->ucPixelType) + { + case EIGHT_BIT_GRAYSCALE: + jd.iBpp = 8; + break; + case FOUR_BIT_DITHERED: + jd.iBpp = 4; + break; + case TWO_BIT_DITHERED: + jd.iBpp = 2; + break; + case ONE_BIT_DITHERED: + jd.iBpp = 1; + break; + } + if (pJPEG->ucPixelType > EIGHT_BIT_GRAYSCALE) + jd.pPixels = (uint16_t *)pJPEG->pDitherBuffer; + else + jd.pPixels = pJPEG->usPixels; + jd.iHeight = mcuCY; + jd.y = pJPEG->iYOffset; + for (y = 0; y < cy && bContinue && iErr == 0; y++, jd.y += mcuCY) + { + jd.x = pJPEG->iXOffset; + xoff = 0; // start of new LCD output group + iPitch = iMCUCount * mcuCX; // pixels per line of LCD buffer + for (x = 0; x < cx && bContinue && iErr == 0; x++) + { + pJPEG->ucACTable = cACTable0; + pJPEG->ucDCTable = cDCTable0; + // do the first luminance component + iErr = JPEGDecodeMCU(pJPEG, iLum0, &iDCPred0); + if (pJPEG->ucMaxACCol == 0 || bThumbnail) // no AC components, save some time + { + pl = (uint32_t *)&pJPEG->sMCUs[iLum0]; + c = ucRangeTable[((iDCPred0 * iQuant1) >> 5) & 0x3ff]; + l = c | ((uint32_t) c << 8) | ((uint32_t) c << 16) | ((uint32_t) c << 24); + // dct stores byte values + for (i = 0; iJPCI[0].quant_tbl_no, (pJPEG->ucMaxACCol | (pJPEG->ucMaxACRow << 8))); // first quantization table + } + // do the second luminance component + if (pJPEG->ucSubSample > 0x11) // subsampling + { + iErr |= JPEGDecodeMCU(pJPEG, iLum1, &iDCPred0); + if (pJPEG->ucMaxACCol == 0 || bThumbnail) // no AC components, save some time + { + c = ucRangeTable[((iDCPred0 * iQuant1) >> 5) & 0x3ff]; + l = c | ((uint32_t) c << 8) | ((uint32_t) c << 16) | ((uint32_t) c << 24); + // dct stores byte values + pl = (uint32_t *)&pJPEG->sMCUs[iLum1]; + for (i = 0; iJPCI[0].quant_tbl_no, (pJPEG->ucMaxACCol | (pJPEG->ucMaxACRow << 8))); // first quantization table + } + if (pJPEG->ucSubSample == 0x22) + { + iErr |= JPEGDecodeMCU(pJPEG, iLum2, &iDCPred0); + if (pJPEG->ucMaxACCol == 0 || bThumbnail) // no AC components, save some time + { + c = ucRangeTable[((iDCPred0 * iQuant1) >> 5) & 0x3ff]; + l = c | ((uint32_t) c << 8) | ((uint32_t) c << 16) | ((uint32_t) c << 24); + // dct stores byte values + pl = (uint32_t *)&pJPEG->sMCUs[iLum2]; + for (i = 0; iJPCI[0].quant_tbl_no, (pJPEG->ucMaxACCol | (pJPEG->ucMaxACRow << 8))); // first quantization table + } + iErr |= JPEGDecodeMCU(pJPEG, iLum3, &iDCPred0); + if (pJPEG->ucMaxACCol == 0 || bThumbnail) // no AC components, save some time + { + c = ucRangeTable[((iDCPred0 * iQuant1) >> 5) & 0x3ff]; + l = c | ((uint32_t) c << 8) | ((uint32_t) c << 16) | ((uint32_t) c << 24); + // dct stores byte values + pl = (uint32_t *)&pJPEG->sMCUs[iLum3]; + for (i = 0; iJPCI[0].quant_tbl_no, (pJPEG->ucMaxACCol | (pJPEG->ucMaxACRow << 8))); // first quantization table + } + } // if 2:2 subsampling + } // if subsampling used + if (pJPEG->ucSubSample && pJPEG->ucNumComponents == 3) // if color (not CMYK) + { + // first chroma + pJPEG->ucACTable = cACTable1; + pJPEG->ucDCTable = cDCTable1; + iErr |= JPEGDecodeMCU(pJPEG, iCr, &iDCPred1); + if (pJPEG->ucMaxACCol == 0 || bThumbnail) // no AC components, save some time + { + c = ucRangeTable[((iDCPred1 * iQuant2) >> 5) & 0x3ff]; + l = c | ((uint32_t) c << 8) | ((uint32_t) c << 16) | ((uint32_t) c << 24); + // dct stores byte values + pl = (uint32_t *)&pJPEG->sMCUs[iCr]; + for (i = 0; iJPCI[1].quant_tbl_no, (pJPEG->ucMaxACCol | (pJPEG->ucMaxACRow << 8))); // second quantization table + } + // second chroma + pJPEG->ucACTable = cACTable2; + pJPEG->ucDCTable = cDCTable2; + iErr |= JPEGDecodeMCU(pJPEG, iCb, &iDCPred2); + if (pJPEG->ucMaxACCol == 0 || bThumbnail) // no AC components, save some time + { + c = ucRangeTable[((iDCPred2 * iQuant3) >> 5) & 0x3ff]; + l = c | ((uint32_t) c << 8) | ((uint32_t) c << 16) | ((uint32_t) c << 24); + // dct stores byte values + pl = (uint32_t *)&pJPEG->sMCUs[iCb]; + for (i = 0; iJPCI[2].quant_tbl_no, (pJPEG->ucMaxACCol | (pJPEG->ucMaxACRow << 8))); + } + } // if color components present + if (pJPEG->ucPixelType >= EIGHT_BIT_GRAYSCALE) + { + JPEGPutMCU8BitGray(pJPEG, xoff, iPitch); + } + else + { + switch (pJPEG->ucSubSample) + { + case 0x00: // grayscale + JPEGPutMCUGray(pJPEG, xoff, iPitch); + break; + case 0x11: + JPEGPutMCU11(pJPEG, xoff, iPitch); + break; + case 0x12: + JPEGPutMCU12(pJPEG, xoff, iPitch); + break; + case 0x21: + JPEGPutMCU21(pJPEG, xoff, iPitch); + break; + case 0x22: + JPEGPutMCU22(pJPEG, xoff, iPitch); + break; + } // switch on color option + } + xoff += mcuCX; + if (xoff == iPitch || x == cx-1) // time to draw + { + xoff = 0; + jd.iWidth = iPitch; // width of each LCD block group + if (pJPEG->ucPixelType > EIGHT_BIT_GRAYSCALE) // dither to 4/2/1 bits + JPEGDither(pJPEG, cx * mcuCX, mcuCY); + if ((jd.y - pJPEG->iYOffset + mcuCY) > (pJPEG->iHeight>>iScaleShift)) { // last row needs to be trimmed + jd.iHeight = (pJPEG->iHeight>>iScaleShift) - (jd.y - pJPEG->iYOffset); + } + bContinue = (*pJPEG->pfnDraw)(&jd); + jd.x += iPitch; + if ((cx - 1 - x) < iMCUCount) // change pitch for the last set of MCUs on this row + iPitch = (cx - 1 - x) * mcuCX; + } + if (pJPEG->iResInterval) + { + if (--pJPEG->iResCount == 0) + { + pJPEG->iResCount = pJPEG->iResInterval; + iDCPred0 = iDCPred1 = iDCPred2 = 0; // reset DC predictors + if (pJPEG->bb.ulBitOff & 7) // need to start at the next even byte + { + pJPEG->bb.ulBitOff += (8 - (pJPEG->bb.ulBitOff & 7)); // new restart interval starts on byte boundary + } + } // if restart interval needs to reset + } // if there is a restart interval + // See if we need to feed it more data + if (pJPEG->iVLCOff >= FILE_HIGHWATER) + JPEGGetMoreData(pJPEG); // need more 'filtered' VLC data + } // for x + } // for y + if (iErr != 0) + pJPEG->iError = JPEG_DECODE_ERROR; + return (iErr == 0); +} /* DecodeJPEG() */ diff --git a/libraries/pico_display/README.md b/libraries/pico_display/README.md index 92400654..648df9ce 100644 --- a/libraries/pico_display/README.md +++ b/libraries/pico_display/README.md @@ -7,11 +7,7 @@ We've included helper functions to handle every aspect of drawing to the screen - [Example Program](#example-program) - [Function Reference](#function-reference) - [PicoGraphics](#picographics) - - [init](#init) - - [set_backlight](#set_backlight) - - [set_led](#set_led) - - [is_pressed](#is_pressed) - - [update](#update) + - [ST7789](#st7789) ## Example Program @@ -19,16 +15,18 @@ The following example sets up Pico Display, displays some basic demo text and gr ```c++ #include "pico_display.hpp" -#include "generic_st7789.hpp" +#include "drivers/st7789/st7789.hpp" +#include "libraries/pico_graphics/pico_graphics.hpp" #include "rgbled.hpp" #include "button.hpp" -uint16_t buffer[PicoDisplay::WIDTH * PicoDisplay::HEIGHT]; +// Display driver +ST7789 st7789(PicoDisplay::WIDTH, PicoDisplay::HEIGHT, ROTATE_0, false, get_spi_pins(BG_SPI_FRONT)); -// Swap WIDTH and HEIGHT to rotate 90 degrees -ST7789Generic display(PicoDisplay::WIDTH, PicoDisplay::HEIGHT, buffer); +// Graphics library - in RGB332 mode you get 256 colours and optional dithering for ~32K RAM. +PicoGraphics_PenRGB332 graphics(st7789.width, st7789.height, nullptr); -// Create an RGB LED +// RGB LED RGBLED led(PicoDisplay::LED_R, PicoDisplay::LED_G, PicoDisplay::LED_B); // And each button @@ -42,7 +40,7 @@ int main() { // set the backlight to a value between 0 and 255 // the backlight is driven via PWM and is gamma corrected by our // library to give a gorgeous linear brightness range. - display.set_backlight(100); + st7789.set_backlight(100); while(true) { // detect if the A button is pressed (could be A, B, X, or Y) @@ -55,24 +53,24 @@ int main() { // set the colour of the pen // parameters are red, green, blue all between 0 and 255 - display.set_pen(30, 40, 50); + graphics.set_pen(30, 40, 50); // fill the screen with the current pen colour - display.clear(); + graphics.clear(); // draw a box to put some text in - display.set_pen(10, 20, 30); + graphics.set_pen(10, 20, 30); Rect text_rect(10, 10, 150, 150); - display.rectangle(text_rect); + graphics.rectangle(text_rect); // write some text inside the box with 10 pixels of margin // automatically word wrapping text_rect.deflate(10); - display.set_pen(110, 120, 130); - display.text("This is a message", Point(text_rect.x, text_rect.y), text_rect.w); + graphics.set_pen(110, 120, 130); + graphics.text("This is a message", Point(text_rect.x, text_rect.y), text_rect.w); // now we've done our drawing let's update the screen - display.update(); + st7789.update(&graphics); } } ``` @@ -83,32 +81,6 @@ int main() { Pico Display uses our Pico Graphics library to draw graphics and text. For more information [read the Pico Graphics function reference.](../pico_graphics/README.md#function-reference). -### configure_display +### ST7789 -Configures an ST7789 display. Done by default, but you can use this to set 180 degree rotation like so: - -```c++ -display.configure_display(true); -``` - -### flip - -Deprecated: calls `configure_display(true);` - -### set_backlight - -Set the display backlight from 0-255. - -```c++ -display.set_backlight(brightness); -``` - -Uses hardware PWM to dim the display backlight, dimming values are gamma-corrected to provide smooth brightness transitions across the full range of intensity. This may result in some low values mapping as "off." - -### update - -To display your changes on Pico Display's screen you need to call `update`: - -```c++ -display.update(); -``` +Pico Display uses the ST7789 display driver to handle the LCD. For more information [read the ST7789 README.](../../drivers/st7789/README.md). \ No newline at end of file diff --git a/libraries/pico_display/pico_display.cmake b/libraries/pico_display/pico_display.cmake index 810ddff8..a50b1a4b 100644 --- a/libraries/pico_display/pico_display.cmake +++ b/libraries/pico_display/pico_display.cmake @@ -1,6 +1,3 @@ -include(${CMAKE_CURRENT_LIST_DIR}/../../drivers/st7789/st7789.cmake) -include(${CMAKE_CURRENT_LIST_DIR}/../pico_graphics/pico_graphics.cmake) - add_library(pico_display INTERFACE) target_sources(pico_display INTERFACE @@ -10,4 +7,4 @@ target_sources(pico_display INTERFACE target_include_directories(pico_display INTERFACE ${CMAKE_CURRENT_LIST_DIR}) # Pull in pico libraries that we need -target_link_libraries(pico_display INTERFACE pico_stdlib hardware_spi hardware_pwm hardware_dma st7789 pico_graphics) \ No newline at end of file +target_link_libraries(pico_display INTERFACE pico_stdlib) \ No newline at end of file diff --git a/libraries/pico_display/pico_display.cpp b/libraries/pico_display/pico_display.cpp index d8d6ac2f..6304d517 100644 --- a/libraries/pico_display/pico_display.cpp +++ b/libraries/pico_display/pico_display.cpp @@ -1,85 +1 @@ -#include -#include - -#include "hardware/gpio.h" // Workaround SDK bug - https://github.com/raspberrypi/pico-sdk/issues/3 -#include "hardware/pwm.h" - -#include "pico_display.hpp" - -namespace pimoroni { - - PicoDisplay::PicoDisplay(uint16_t *buf) - : PicoGraphics(WIDTH, HEIGHT, buf), screen(WIDTH, HEIGHT, false, buf, - PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_FRONT_CS, SPI_DEFAULT_MISO, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, SPI_BG_FRONT_PWM) { - __fb = buf; - } - - PicoDisplay::PicoDisplay(uint16_t *buf, int width, int height) - : PicoGraphics(width, height, buf), screen(width, height, false, buf, - PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_FRONT_CS, SPI_DEFAULT_MISO, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, SPI_BG_FRONT_PWM) { - __fb = buf; - } - - void PicoDisplay::init() { - // setup the rgb led for pwm control - pwm_config cfg = pwm_get_default_config(); - pwm_config_set_output_polarity(&cfg, true, true); - - // red - pwm_set_wrap(pwm_gpio_to_slice_num(LED_R), 65535); - pwm_init(pwm_gpio_to_slice_num(LED_R), &cfg, true); - gpio_set_function(LED_R, GPIO_FUNC_PWM); - - // green - pwm_set_wrap(pwm_gpio_to_slice_num(LED_G), 65535); - pwm_init(pwm_gpio_to_slice_num(LED_G), &cfg, true); - gpio_set_function(LED_G, GPIO_FUNC_PWM); - - // blue - pwm_set_wrap(pwm_gpio_to_slice_num(LED_B), 65535); - pwm_init(pwm_gpio_to_slice_num(LED_B), &cfg, true); - gpio_set_function(LED_B, GPIO_FUNC_PWM); - - // setup button inputs - gpio_set_function(A, GPIO_FUNC_SIO); gpio_set_dir(A, GPIO_IN); gpio_pull_up(A); - gpio_set_function(B, GPIO_FUNC_SIO); gpio_set_dir(B, GPIO_IN); gpio_pull_up(B); - gpio_set_function(X, GPIO_FUNC_SIO); gpio_set_dir(X, GPIO_IN); gpio_pull_up(X); - gpio_set_function(Y, GPIO_FUNC_SIO); gpio_set_dir(Y, GPIO_IN); gpio_pull_up(Y); - } - - void PicoDisplay::update() { - screen.update(); - } - - void PicoDisplay::set_backlight(uint8_t brightness) { - screen.set_backlight(brightness); - } - - void PicoDisplay::set_led(uint8_t r, uint8_t g, uint8_t b) { - // gamma correct the provided 0-255 brightness value onto a - // 0-65535 range for the pwm counter - static const float gamma = 2.8; - - uint16_t value; - - // red - value = (uint16_t)(pow((float)(r) / 255.0f, gamma) * 65535.0f + 0.5f); - pwm_set_gpio_level(LED_R, value); - - // green - value = (uint16_t)(pow((float)(g) / 255.0f, gamma) * 65535.0f + 0.5f); - pwm_set_gpio_level(LED_G, value); - - // blue - value = (uint16_t)(pow((float)(b) / 255.0f, gamma) * 65535.0f + 0.5f); - pwm_set_gpio_level(LED_B, value); - } - - bool PicoDisplay::is_pressed(uint8_t button) { - return !gpio_get(button); - } - - void PicoDisplay::flip() { - screen.flip(); - } -} +#include "pico_display.hpp" \ No newline at end of file diff --git a/libraries/pico_display/pico_display.hpp b/libraries/pico_display/pico_display.hpp index 17bb65a6..61d3539c 100644 --- a/libraries/pico_display/pico_display.hpp +++ b/libraries/pico_display/pico_display.hpp @@ -1,16 +1,12 @@ #pragma once -#include "drivers/st7789/st7789.hpp" -#include "libraries/pico_graphics/pico_graphics.hpp" +#include "pico/stdlib.h" namespace pimoroni { - - class PicoDisplay : public PicoGraphics { + class PicoDisplay { public: static const int WIDTH = 240; static const int HEIGHT = 135; - static const int PORTRAIT_WIDTH = 135; - static const int PORTRAIT_HEIGHT = 240; static const uint8_t A = 12; static const uint8_t B = 13; static const uint8_t X = 14; @@ -18,21 +14,5 @@ namespace pimoroni { static const uint8_t LED_R = 6; static const uint8_t LED_G = 7; static const uint8_t LED_B = 8; - - uint16_t *__fb; - private: - ST7789 screen; - - public: - PicoDisplay(uint16_t *buf); - PicoDisplay(uint16_t *buf, int width, int height); - - void init(); - void update(); - void set_backlight(uint8_t brightness); - void set_led(uint8_t r, uint8_t g, uint8_t b); - bool is_pressed(uint8_t button); - void flip(); }; - } diff --git a/libraries/pico_display_2/README.md b/libraries/pico_display_2/README.md index ee454773..18ad905c 100644 --- a/libraries/pico_display_2/README.md +++ b/libraries/pico_display_2/README.md @@ -2,16 +2,10 @@ Our Pico Display Pack offers a vibrant 1.14" (240x135) IPS LCD screen for your Raspberry Pi Pico it also includes four switches and and an RGB LED! -We've included helper functions to handle every aspect of drawing to the screen and interfacing with the buttons and LED. See the [function reference](#function-reference) for details. - - [Example Program](#example-program) - [Function Reference](#function-reference) - [PicoGraphics](#picographics) - - [init](#init) - - [set_backlight](#set_backlight) - - [set_led](#set_led) - - [is_pressed](#is_pressed) - - [update](#update) + - [ST7789](#st7789) ## Example Program @@ -19,49 +13,62 @@ The following example sets up Pico Display, displays some basic demo text and gr ```c++ #include "pico_display_2.hpp" +#include "drivers/st7789/st7789.hpp" +#include "libraries/pico_graphics/pico_graphics.hpp" +#include "rgbled.hpp" +#include "button.hpp" -using namespace pimoroni; +// Display driver +ST7789 st7789(PicoDisplay2::WIDTH, PicoDisplay2::HEIGHT, ROTATE_0, false, get_spi_pins(BG_SPI_FRONT)); -uint16_t buffer[PicoDisplay2::WIDTH * PicoDisplay2::HEIGHT]; -PicoDisplay2 pico_display(buffer); +// Graphics library - in RGB332 mode you get 256 colours and optional dithering for 75K RAM. +PicoGraphics_PenRGB332 graphics(st7789.width, st7789.height, nullptr); + +// RGB LED +RGBLED led(PicoDisplay2::LED_R, PicoDisplay2::LED_G, PicoDisplay2::LED_B); + +// And each button +Button button_a(PicoDisplay2::A); +Button button_b(PicoDisplay2::B); +Button button_x(PicoDisplay2::X); +Button button_y(PicoDisplay2::Y); int main() { - pico_display.init(); // set the backlight to a value between 0 and 255 // the backlight is driven via PWM and is gamma corrected by our // library to give a gorgeous linear brightness range. - pico_display.set_backlight(100); + st7789.set_backlight(100); while(true) { // detect if the A button is pressed (could be A, B, X, or Y) - if(pico_display.is_pressed(pico_display.A)) { + if(button_a.raw(display.A)) { // make the led glow green // parameters are red, green, blue all between 0 and 255 // these are also gamma corrected - pico_display.set_led(0, 255, 0); + led.set_rgb(0, 255, 0); } // set the colour of the pen // parameters are red, green, blue all between 0 and 255 - pico_display.set_pen(30, 40, 50); + graphics.set_pen(30, 40, 50); // fill the screen with the current pen colour - pico_display.clear(); + graphics.clear(); // draw a box to put some text in - pico_display.set_pen(10, 20, 30); + graphics.set_pen(10, 20, 30); Rect text_rect(10, 10, 150, 150); - pico_display.rectangle(text_rect); + graphics.rectangle(text_rect); // write some text inside the box with 10 pixels of margin // automatically word wrapping text_rect.deflate(10); - pico_display.set_pen(110, 120, 130); - pico_display.text("This is a message", Point(text_rect.x, text_rect.y), text_rect.w); + graphics.set_pen(110, 120, 130); + graphics.text("This is a message", Point(text_rect.x, text_rect.y), text_rect.w); // now we've done our drawing let's update the screen - pico_display.update(); + st7789.update(&graphics); } } ``` @@ -72,52 +79,6 @@ int main() { Pico Display uses our Pico Graphics library to draw graphics and text. For more information [read the Pico Graphics function reference.](../pico_graphics/README.md#function-reference). -### init +### ST7789 -Sets up Pico Display. `init` must be called before any other functions since it configures the required PWM and GPIO: - -```c++ -pico_display.init(); -``` - -### set_backlight - -Set the display backlight from 0-255. - -```c++ -pico_display.set_backlight(brightness); -``` - -Uses hardware PWM to dim the display backlight, dimming values are gamma-corrected to provide smooth brightness transitions across the full range of intensity. This may result in some low values mapping as "off." - -### set_led - -Sets the RGB LED on Pico Display with an RGB triplet: - -```c++ -pico_display.set_led(r, g, b); -``` - -Uses hardware PWM to drive the LED. Values are automatically gamma-corrected to provide smooth brightness transitions and low values may map as "off." - -### is_pressed - -Reads the GPIO pin connected to one of Pico Display's buttons, returning a `bool` - `true` if it's pressed and `false` if it is released. - -```c++ -pico_display.is_pressed(button); -``` - -The button vaule should be a `uint8_t` denoting a pin, and constants `A`, `B`, `X` and `Y` are supplied to make it easier. e: - -```c++ -bool is_a_button_pressed = pico_display.is_pressed(PicoDisplay2::A) -``` - -### update - -To display your changes on Pico Display's screen you need to call `update`: - -```c++ -pico_display.update(); -``` +Pico Display uses the ST7789 display driver to handle the LCD. For more information [read the ST7789 README.](../../drivers/st7789/README.md). \ No newline at end of file diff --git a/libraries/pico_display_2/pico_display_2.cmake b/libraries/pico_display_2/pico_display_2.cmake index 1b75294d..b7348688 100644 --- a/libraries/pico_display_2/pico_display_2.cmake +++ b/libraries/pico_display_2/pico_display_2.cmake @@ -1,14 +1,10 @@ -include(${CMAKE_CURRENT_LIST_DIR}/../../drivers/st7789/st7789.cmake) -include(${CMAKE_CURRENT_LIST_DIR}/../pico_graphics/pico_graphics.cmake) +add_library(pico_display_2 INTERFACE) -set(LIB_NAME pico_display_2) -add_library(${LIB_NAME} INTERFACE) - -target_sources(${LIB_NAME} INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/${LIB_NAME}.cpp +target_sources(pico_display_2 INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/pico_display_2.cpp ) -target_include_directories(${LIB_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR}) +target_include_directories(pico_display_2 INTERFACE ${CMAKE_CURRENT_LIST_DIR}) # Pull in pico libraries that we need -target_link_libraries(${LIB_NAME} INTERFACE pico_stdlib hardware_spi hardware_pwm hardware_dma st7789 pico_graphics) \ No newline at end of file +target_link_libraries(pico_display_2 INTERFACE pico_stdlib) \ No newline at end of file diff --git a/libraries/pico_display_2/pico_display_2.cpp b/libraries/pico_display_2/pico_display_2.cpp index 4738433f..abb3e46b 100644 --- a/libraries/pico_display_2/pico_display_2.cpp +++ b/libraries/pico_display_2/pico_display_2.cpp @@ -1,85 +1 @@ -#include -#include - -#include "hardware/gpio.h" // Workaround SDK bug - https://github.com/raspberrypi/pico-sdk/issues/3 -#include "hardware/pwm.h" - -#include "pico_display_2.hpp" - -namespace pimoroni { - - PicoDisplay2::PicoDisplay2(uint16_t *buf) - : PicoGraphics(WIDTH, HEIGHT, buf), screen(WIDTH, HEIGHT, false, buf, - PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_FRONT_CS, SPI_DEFAULT_MISO, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, SPI_BG_FRONT_PWM) { - __fb = buf; - } - - PicoDisplay2::PicoDisplay2(uint16_t *buf, int width, int height) - : PicoGraphics(width, height, buf), screen(width, height, false, buf, - PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_FRONT_CS, SPI_DEFAULT_MISO, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, SPI_BG_FRONT_PWM) { - __fb = buf; - } - - void PicoDisplay2::init() { - // setup the rgb led for pwm control - pwm_config cfg = pwm_get_default_config(); - pwm_config_set_output_polarity(&cfg, true, true); - - // red - pwm_set_wrap(pwm_gpio_to_slice_num(LED_R), 65535); - pwm_init(pwm_gpio_to_slice_num(LED_R), &cfg, true); - gpio_set_function(LED_R, GPIO_FUNC_PWM); - - // green - pwm_set_wrap(pwm_gpio_to_slice_num(LED_G), 65535); - pwm_init(pwm_gpio_to_slice_num(LED_G), &cfg, true); - gpio_set_function(LED_G, GPIO_FUNC_PWM); - - // blue - pwm_set_wrap(pwm_gpio_to_slice_num(LED_B), 65535); - pwm_init(pwm_gpio_to_slice_num(LED_B), &cfg, true); - gpio_set_function(LED_B, GPIO_FUNC_PWM); - - // setup button inputs - gpio_set_function(A, GPIO_FUNC_SIO); gpio_set_dir(A, GPIO_IN); gpio_pull_up(A); - gpio_set_function(B, GPIO_FUNC_SIO); gpio_set_dir(B, GPIO_IN); gpio_pull_up(B); - gpio_set_function(X, GPIO_FUNC_SIO); gpio_set_dir(X, GPIO_IN); gpio_pull_up(X); - gpio_set_function(Y, GPIO_FUNC_SIO); gpio_set_dir(Y, GPIO_IN); gpio_pull_up(Y); - } - - void PicoDisplay2::update() { - screen.update(); - } - - void PicoDisplay2::set_backlight(uint8_t brightness) { - screen.set_backlight(brightness); - } - - void PicoDisplay2::set_led(uint8_t r, uint8_t g, uint8_t b) { - // gamma correct the provided 0-255 brightness value onto a - // 0-65535 range for the pwm counter - static const float gamma = 2.8; - - uint16_t value; - - // red - value = (uint16_t)(pow((float)(r) / 255.0f, gamma) * 65535.0f + 0.5f); - pwm_set_gpio_level(LED_R, value); - - // green - value = (uint16_t)(pow((float)(g) / 255.0f, gamma) * 65535.0f + 0.5f); - pwm_set_gpio_level(LED_G, value); - - // blue - value = (uint16_t)(pow((float)(b) / 255.0f, gamma) * 65535.0f + 0.5f); - pwm_set_gpio_level(LED_B, value); - } - - bool PicoDisplay2::is_pressed(uint8_t button) { - return !gpio_get(button); - } - - void PicoDisplay2::flip() { - screen.flip(); - } -} +#include "pico_display_2.hpp" \ No newline at end of file diff --git a/libraries/pico_display_2/pico_display_2.hpp b/libraries/pico_display_2/pico_display_2.hpp index 67ceb52c..bc543f0e 100644 --- a/libraries/pico_display_2/pico_display_2.hpp +++ b/libraries/pico_display_2/pico_display_2.hpp @@ -1,16 +1,12 @@ #pragma once -#include "drivers/st7789/st7789.hpp" -#include "libraries/pico_graphics/pico_graphics.hpp" +#include "pico/stdlib.h" namespace pimoroni { - - class PicoDisplay2 : public PicoGraphics { + class PicoDisplay2 { public: static const int WIDTH = 320; static const int HEIGHT = 240; - static const int PORTRAIT_WIDTH = 240; - static const int PORTRAIT_HEIGHT = 320; static const uint8_t A = 12; static const uint8_t B = 13; static const uint8_t X = 14; @@ -18,21 +14,5 @@ namespace pimoroni { static const uint8_t LED_R = 6; static const uint8_t LED_G = 7; static const uint8_t LED_B = 8; - - uint16_t *__fb; - private: - ST7789 screen; - - public: - PicoDisplay2(uint16_t *buf); - PicoDisplay2(uint16_t *buf, int width, int height); - - void init(); - void update(); - void set_backlight(uint8_t brightness); - void set_led(uint8_t r, uint8_t g, uint8_t b); - bool is_pressed(uint8_t button); - void flip(); }; - } diff --git a/libraries/pico_explorer/README.md b/libraries/pico_explorer/README.md index 91589cdd..6c9b0a25 100644 --- a/libraries/pico_explorer/README.md +++ b/libraries/pico_explorer/README.md @@ -11,35 +11,59 @@ We've included helper functions to handle every aspect of drawing to the screen - [Buttons](#buttons) - [ADC Channels](#adc-channels) - [GPIO](#gpio) + - [Motors](#motors) - [Functions](#functions) - - [init](#init) - - [set_motor](#set_motor) - - [get_adc](#get_adc) - - [set_audio_pin](#set_audio_pin) - - [set_tone](#set_tone) - - [is_pressed](#is_pressed) - - [update](#update) + - [Motors](#motors-1) + - [Analog](#analog) + - [Buzzer](#buzzer) + - [Buttons](#buttons-1) + - [ST7789](#st7789) + ## Example Program -The following example sets up Pico Explorer, displays some basic demo text and graphics and will illuminate the RGB LED green if the A button is pressed. +The following example shows how to set up all of Pico Explorers functions: ```c++ #include "pico_explorer.hpp" +#include "drivers/st7789/st7789.hpp" +#include "libraries/pico_graphics/pico_graphics.hpp" +#include "button.hpp" +#include "motor.hpp" +#include "analog.hpp" using namespace pimoroni; -uint16_t buffer[PicoExplorer::WIDTH * PicoExplorer::HEIGHT]; -PicoExplorer pico_explorer(buffer); +// Display driver +ST7789 st7789(PicoExplorer::WIDTH, PicoExplorer::HEIGHT, ROTATE_0, false, get_spi_pins(BG_SPI_FRONT)); + +// Graphics library - in RGB332 mode you get 256 colours and optional dithering for ~56K RAM. +PicoGraphics_PenRGB332 graphics(st7789.width, st7789.height, nullptr); + +// Buttons +Button button_a(PicoExplorer::A); +Button button_b(PicoExplorer::B); +Button button_x(PicoExplorer::X); +Button button_y(PicoExplorer::Y); + +// Motors +Motor motor1(PicoExplorer::MOTOR1_PINS); +Motor motor2(PicoExplorer::MOTOR2_PINS); + +// Analog +Analog adc0(PicoExplorer::ADC0_PIN); +Analog adc1(PicoExplorer::ADC1_PIN); +Analog adc2(PicoExplorer::ADC2_PIN); + int main() { - pico_explorer.init(); + motor1.init(); + motor2.init(); - while(true) { - - // now we've done our drawing let's update the screen - pico_explorer.update(); - } + while(true) { + // update screen + st7789.update(&graphics); + } } ``` @@ -55,132 +79,129 @@ Please note that the backlight on Pico Explorer is not dimmable (we needed the p #### Buttons -The four buttons, A, B, X and Y have corresponding constants set to their respective GPIO pins. For example: +The four buttons, A, B, X and Y have corresponding constants set to their respective GPIO pins. ```c++ -bool a_is_pressed = pico_explorer.is_pressed(pico_explorer.A); +PicoExplorer::A = 12; +PicoExplorer::B = 13; +PicoExplorer::X = 14; +PicoExplorer::Y = 15; ``` #### ADC Channels -The three ADC channels are defined as `ADC0`, `ADC1` and `ADC2`, and should be used with `get_adc`, eg: +The three ADC channels are defined as `ADC0_PIN`, `ADC1_PIN` and `ADC2_PIN`, and should be used with an instance of the `Analog` class: ```c++ -float adc0_reading = pico_explorer.get_adc(pico_explorer.ADC0); +PicoExplorer::ADC0_PIN = 26; +PicoExplorer::ADC1_PIN = 27; +PicoExplorer::ADC2_PIN = 28; ``` #### GPIO -The 8 general purpose IO pins on the lower Pico Explorer are GP0 through GP7, we've created constants for you to identify them easily. You should use Pico SDK's `gpio_get` to read a pin, eg: +The 8 general purpose IO pins on the lower Pico Explorer are GP0 through GP7, we've created constants for you to identify them easily. + +```c++ +PicoExplorer::GP0 = 0; +PicoExplorer::GP1 = 1; +PicoExplorer::GP2 = 2; +PicoExplorer::GP3 = 3; +PicoExplorer::GP4 = 4; +PicoExplorer::GP5 = 5; +PicoExplorer::GP6 = 6; +PicoExplorer::GP7 = 7; +``` + +You should use Pico SDK's `gpio_get` to read a pin, eg: ```c++ bool pin_state = gpio_get(pico_explorer.GP0); ``` +#### Motors + +The two motor channels are defined as pin pairs, and should be used with an instance of the `Motor` class: + +```c++ + PicoExplorer::MOTOR1_PINS = {9, 8}; + PicoExplorer::MOTOR2_PINS = {11, 10}; +``` + ### Functions -#### init +#### Motors -Sets up Pico Explorer. `init` must be called before any other functions since it configures the required PWM and GPIO: +Pico Explorer uses our `Motor` library to drive motors. Motors are driven by PWM via an onboard DRV8833. Constants are provided for both motors, so setup is as easy as: ```c++ -pico_explorer.init(); +#include "motor.hpp" + +using namespace motor; + +Motor motor1(PicoExplorer::MOTOR1_PINS); +Motor motor2(PicoExplorer::MOTOR2_PINS); ``` -#### set_motor +You should also init your motors to set up the required GPIO/PWM on their pins: ```c++ -void PicoExplorer::set_motor(uint8_t channel, uint8_t action, float speed); +motor1.init(); +motor2.init(); ``` -Motors are driven by PWM via an onboard DRV8833, `set_motor` will set the PWM values for the corresponding channel. - -Channel should be one of `MOTOR1` or `MOTOR2`. - -Action should be `FORWARD`, `REVERSE` or `STOP`. - -Speed should be given as a float between `0.0` and `1.0`, eg: +Speed should be given as a float between `-1.0` and `1.0`, eg: ```c++ -pico_explorer.set_motor(pico_explorer.MOTOR1, pico_explorer.FORWARD, 0.5f); -pico_explorer.set_motor(pico_explorer.MOTOR2, pico_explorer.REVERSE, 0.5f); +motor1.speed(1.0) // Full-speed forward +motor1.speed(-1.0) // Full-speed backward ``` And to stop the motor: ```c++ -pico_explorer.set_motor(pico_explorer.MOTOR1, pico_explorer.STOP); -pico_explorer.set_motor(pico_explorer.MOTOR2, pico_explorer.STOP); +motor1.stop() ``` -#### get_adc +#### Analog ```c++ float get_adc(uint8_t channel); ``` -Pico Explorer's ADC channels are connected to Pico's ADC-capable pins 26, 27 and 28 which correspond to channels 0, 1 and 2 respectively. eg: +Pico Explorer's ADC channels are connected to Pico's ADC-capable pins 26, 27 and 28 which correspond to channels 0, 1 and 2 respectively. + +Constants are supplied for these in the PicoExplorer library, so you can create an Analog instance for each: ```c++ -float reading = pico_explorer.get_adc(pico_explorer.ADC0); +#include "analog.hpp" +Analog adc0(PicoExplorer::ADC0_PIN); +Analog adc1(PicoExplorer::ADC1_PIN); +Analog adc2(PicoExplorer::ADC2_PIN); ``` -Will perform a 12-bit ADC read and return the result as a float scaled from `0.0f` to `1.0f`. This value can be plugged directly into a motor, eg: +And read a voltage with: ```c++ -float reading = pico_explorer.get_adc(pico_explorer.ADC0); -pico_explorer.set_motor(pico_explorer.MOTOR1, pico_explorer.FORWARD, reading); +adc0.read_voltage(); ``` -#### set_audio_pin +#### Buzzer + +Note: You must bridge the pin you use over to the `AUDIO` pin on the Pico Explorer header in order to drive the onboard Piezo. + +TODO document buzzer + +#### Buttons ```c++ -void set_audio_pin(uint8_t p); +#include "button.hpp" +Button button_a(PicoExplorer::A); +Button button_b(PicoExplorer::B); +Button button_x(PicoExplorer::X); +Button button_y(PicoExplorer::Y); ``` -`set_audio_pin` configures the PIN that Pico Explorer uses for audio output. It should be one of `GP0` through `GP7`, eg: +### ST7789 -```c++ -pico_explorer.set_audio_pin(pico_explorer.GP0); -``` - -Note: You must bridge this pin over to the `AUDIO` pin on the Pico Explorer header in order to drive the onboard Piezo, eg: - -#### set_tone - -```c++ -void set_tone(uint16_t frequency, float duty = 0.2f); -``` - -`set_tone` will play an audio tone out of your chosen audio pin, if you have bridged that pin to "AUDIO" on the Pico Explorer header then it will sound the onboard Piezo. - -```c++ -uint16_t frequency = 440; -pico_explorer.set_tone(frequency); -``` - -#### is_pressed - -```c++ -bool is_pressed(uint8_t button); -``` - -Reads the GPIO pin connected to one of Pico Explorer's buttons, returning a `bool` - `true` if it's pressed and `false` if it is released. - -```c++ -pico_explorer.is_pressed(button); -``` - -The button vaule should be a `uint8_t` denoting a pin, and constants `A`, `B`, `X` and `Y` are supplied to make it easier. e: - -```c++ -bool is_a_button_pressed = pico_explorer.is_pressed(PicoDisplay::A) -``` - -#### update - -To display your changes on Pico Explorer's screen you need to call `update`: - -```c++ -pico_explorer.update(); -``` \ No newline at end of file +Pico Explorer uses the ST7789 display driver to handle the LCD. For more information [read the ST7789 README.](../../drivers/st7789/README.md). \ No newline at end of file diff --git a/libraries/pico_explorer/pico_explorer.cpp b/libraries/pico_explorer/pico_explorer.cpp index 3cba4710..be8dfc6b 100644 --- a/libraries/pico_explorer/pico_explorer.cpp +++ b/libraries/pico_explorer/pico_explorer.cpp @@ -1,117 +1 @@ -#include -#include - -#include "hardware/gpio.h" -#include "hardware/pwm.h" -#include "hardware/adc.h" - -#include "pico_explorer.hpp" - -const uint8_t MOTOR1N = 8; -const uint8_t MOTOR1P = 9; -const uint8_t MOTOR2N = 10; -const uint8_t MOTOR2P = 11; - -namespace pimoroni { - - PicoExplorer::PicoExplorer(uint16_t *buf) - : PicoGraphics(WIDTH, HEIGHT, buf), - screen(WIDTH, HEIGHT, false, buf, PIMORONI_SPI_DEFAULT_INSTANCE, screen.get_slot_cs(PICO_EXPLORER_ONBOARD), SPI_DEFAULT_MISO, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, screen.get_slot_bl(PICO_EXPLORER_ONBOARD)) { - __fb = buf; - } - - void PicoExplorer::init() { - // setup button inputs - gpio_set_function(A, GPIO_FUNC_SIO); gpio_set_dir(A, GPIO_IN); gpio_pull_up(A); - gpio_set_function(B, GPIO_FUNC_SIO); gpio_set_dir(B, GPIO_IN); gpio_pull_up(B); - gpio_set_function(X, GPIO_FUNC_SIO); gpio_set_dir(X, GPIO_IN); gpio_pull_up(X); - gpio_set_function(Y, GPIO_FUNC_SIO); gpio_set_dir(Y, GPIO_IN); gpio_pull_up(Y); - - // setup ADC channels - adc_init(); - const uint8_t ADC_BASE_PIN = 26; - adc_gpio_init(ADC0 + ADC_BASE_PIN); - adc_gpio_init(ADC1 + ADC_BASE_PIN); - adc_gpio_init(ADC2 + ADC_BASE_PIN); - - // setup motor pins - pwm_config motor_pwm_cfg = pwm_get_default_config(); - pwm_config_set_wrap(&motor_pwm_cfg, 5500); - - pwm_init(pwm_gpio_to_slice_num(MOTOR1N), &motor_pwm_cfg, true); - gpio_set_function(MOTOR1N, GPIO_FUNC_PWM); - - pwm_init(pwm_gpio_to_slice_num(MOTOR1P), &motor_pwm_cfg, true); - gpio_set_function(MOTOR1P, GPIO_FUNC_PWM); - - pwm_init(pwm_gpio_to_slice_num(MOTOR2N), &motor_pwm_cfg, true); - gpio_set_function(MOTOR2N, GPIO_FUNC_PWM); - - pwm_init(pwm_gpio_to_slice_num(MOTOR2P), &motor_pwm_cfg, true); - gpio_set_function(MOTOR2P, GPIO_FUNC_PWM); - } - - void PicoExplorer::update() { - screen.update(); - } - - bool PicoExplorer::is_pressed(uint8_t button) { - return !gpio_get(button); - } - - float PicoExplorer::get_adc(uint8_t channel) { - adc_select_input(channel); - // scale raw 12-bit adc value to 0 .. 1 float - float result = float(adc_read()) / (1 << 12); - // clamp result to 0 .. 1 - result = std::min(1.0f, std::max(0.0f, result)); - return result; - } - - void PicoExplorer::set_motor(uint8_t channel, uint8_t action, float speed) { - uint8_t p = channel == MOTOR1 ? MOTOR1P : MOTOR2P; - uint8_t n = channel == MOTOR1 ? MOTOR1N : MOTOR2N; - - switch(action) { - case FORWARD: { - pwm_set_gpio_level(n, (1 - speed) * 5500); - pwm_set_gpio_level(p, 5500); - break; - } - - case REVERSE: { - pwm_set_gpio_level(n, 5500); - pwm_set_gpio_level(p, (1 - speed) * 5500); - break; - } - - case STOP: { - pwm_set_gpio_level(p, 5500); - pwm_set_gpio_level(n, 5500); - break; - } - } - } - - void PicoExplorer::set_audio_pin(uint pin) { - pwm_config tone_pwm_cfg = pwm_get_default_config(); - - // calculate the pwm wrap value for this frequency - // first we set the clock divider to give us exactly - // ten thousand cycles per second - pwm_config_set_clkdiv(&tone_pwm_cfg, 255); - pwm_init(pwm_gpio_to_slice_num(pin), &tone_pwm_cfg, true); - gpio_set_function(pin, GPIO_FUNC_PWM); - audio_pin = pin; - } - - void PicoExplorer::set_tone(uint16_t frequency, float duty) { - // output a square wave, so 50% duty cycle - if(audio_pin != -1) { - uint16_t pwm_wrap = 490196 / frequency; - pwm_set_wrap(audio_pin, pwm_wrap); - pwm_set_gpio_level(audio_pin, pwm_wrap * duty); - } - } - -} +#include "pico_explorer.hpp" \ No newline at end of file diff --git a/libraries/pico_explorer/pico_explorer.hpp b/libraries/pico_explorer/pico_explorer.hpp index 3c5217ee..3468831a 100644 --- a/libraries/pico_explorer/pico_explorer.hpp +++ b/libraries/pico_explorer/pico_explorer.hpp @@ -1,11 +1,10 @@ #pragma once -#include "drivers/st7789/st7789.hpp" -#include "libraries/pico_graphics/pico_graphics.hpp" +#include "drivers/motor/motor.hpp" namespace pimoroni { - class PicoExplorer : public PicoGraphics { + class PicoExplorer { public: static const int WIDTH = 240; static const int HEIGHT = 240; @@ -18,6 +17,13 @@ namespace pimoroni { static const uint8_t ADC1 = 1; static const uint8_t ADC2 = 2; + static const uint ADC0_PIN = 26; + static const uint ADC1_PIN = 27; + static const uint ADC2_PIN = 28; + + static constexpr pin_pair MOTOR1_PINS{9, 8}; + static constexpr pin_pair MOTOR2_PINS{11, 10}; + static const uint8_t MOTOR1 = 0; static const uint8_t MOTOR2 = 1; @@ -33,25 +39,6 @@ namespace pimoroni { static const uint GP5 = 5; static const uint GP6 = 6; static const uint GP7 = 7; - - uint16_t *__fb; - private: - ST7789 screen; - int8_t audio_pin = -1; - - public: - PicoExplorer(uint16_t *buf); - - void init(); - void update(); - bool is_pressed(uint8_t button); - - float get_adc(uint8_t channel); - - void set_motor(uint8_t channel, uint8_t action, float speed = 0.0f); - - void set_audio_pin(uint pin); - void set_tone(uint16_t frequency, float duty = 0.2f); }; } \ No newline at end of file diff --git a/libraries/pico_graphics/README.md b/libraries/pico_graphics/README.md index 0fd70c3b..b6696b10 100644 --- a/libraries/pico_graphics/README.md +++ b/libraries/pico_graphics/README.md @@ -1,9 +1,12 @@ # Pico Graphics -Pico Graphics is a tiny graphics library for 16-bit RGB565 displays. +Pico Graphics is a tiny graphics library supporting a number of underlying buffer formats including 8-bit paletted (256 colour), 8-bit RGB332 (256 colour), 16-bit RGB565 (65K colour) and 4-bit packed (8 colour). It supports drawing text, primitive and individual pixels and includes basic types such as `rect` and `point` brimming with methods to help you develop games and applications. +- [Overview](#overview) + - [Pen Types](#pen-types) + - [Creating A Pico Graphics Instance](#creating-a-pico-graphics-instance) - [Function Reference](#function-reference) - [Types](#types) - [rect](#rect) @@ -14,11 +17,13 @@ It supports drawing text, primitive and individual pixels and includes basic typ - [rect.inflate & rect.deflate](#rectinflate--rectdeflate) - [point](#point) - [point.clamp](#pointclamp) - - [operators](#operators) - [Pens & Clipping](#pens--clipping) - [set_pen](#set_pen) - [create_pen](#create_pen) - [set_clip & remove_clip](#set_clip--remove_clip) + - [Palette](#palette) + - [update_pen](#update_pen) + - [reset_pen](#reset_pen) - [Pixels](#pixels) - [pixel](#pixel) - [pixel_span](#pixel_span) @@ -28,6 +33,45 @@ It supports drawing text, primitive and individual pixels and includes basic typ - [Text](#text) - [Change Font](#change-font) + +## Overview + +Pico Graphics comes in multiple flavours depending on which underlying buffer type you wish to work with. + +Your buffer doesn't have to be native to your display. For example a 16-bit ST7789 display can work with P4, P8, RGB332 and RGB565 buffers, with palette lookups handled for you on the fly. + +### Pen Types + +* `P4` - 4-bit packed, with an 8 colour palette. This is commonly used for 7/8-colour e-ink displays or driving large displays with few colours. +* `P8` - 8-bit, with a 256 colour palette. Great balance of memory usage versus available colours. You can replace palette entries on the fly. +* `RGB332` - 8-bit, with a fixed 256 colour RGB332 palette. Great for quickly porting an RGB565 app to use less RAM. Limits your colour choices, but is easier to grok. +* `RGB565` - 16-bit, 65K "True Colour." Great for rainbows, gradients and images but comes at the cost of RAM! + +### Creating A Pico Graphics Instance + +To create a Pico Graphics instance to draw into, you should construct an instance of the Pen type class you want to use: + +```c++ +PicoGraphics_PenP4 graphics(WITH, HEIGHT, nullptr); +PicoGraphics_PenP8 graphics(WITH, HEIGHT, nullptr); +PicoGraphics_PenRGB332 graphics(WITH, HEIGHT, nullptr); +PicoGraphics_PenRGB565 graphics(WITH, HEIGHT, nullptr); +``` + +To draw something to a display you should create a display driver instance, eg: + +```c++ +ST7789 st7789(PicoExplorer::WIDTH, PicoExplorer::HEIGHT, ROTATE_0, false, get_spi_pins(BG_SPI_FRONT)); +``` + +And then send it the Pico Graphics instance to draw: + +```c++ +st7789.update(&graphics); +``` + +The driver will check your graphics type and act accordingly. + ## Function Reference ### Types @@ -123,9 +167,10 @@ Would deflate our `box` to start at `11,11` and be 8x8 pixels in size. Since `rectangle` *always* draws a filled rectangle, this can be useful to add an outline of your desired thickness: ```c++ +WHITE = screen.create_pen(255, 255, 255); rect box(10, 10, 100, 100); box.inflate(1); // Inflate our box by 1px on all sides -screen.set_pen(255, 255, 255); // White outline +screen.set_pen(WHITE); // White outline screen.rectangle(box); box.deflate(1); // Return to our original box size screen.set_pen(0, 0, 0); /// Black fill @@ -146,37 +191,31 @@ A point can be clamped within the confines of a `rect`. This is useful for keepi ```c++ point cursor(10, 1000); // A point, far outside the bounds of our screen -cursor.clamp(screen.bounds)); // Clamp to the screen +cursor.clamp(screen.bounds); // Clamp to the screen ``` -##### operators - -TODO - ### Pens & Clipping #### set_pen -In order to draw anything with Pico Graphics you must first set the pen to your desired colour, there are two ways to do this: +In order to draw anything with Pico Graphics you must first set the pen to your desired palette colour: ```c++ -void PicoGraphics::set_pen(uint8_t r, uint8_t g, uint8_t b); -void PicoGraphics::set_pen(uint16_t p); +void PicoGraphics::set_pen(uint8_t p); ``` -The former uses 8-bit R, G and B values which are clipped to 5, 6 and 5 bits respectively to form a 16-bit colour. Internally it uses `create_pen`. +This value represents an index into the internal colour palette, which has 256 entries and defaults to RGB332 giving an approximation of all RGB888 colours. -The latter takes a 16-bit colour directly and is a great way to save a few cycles if you're working with a constant palette of colours. #### create_pen ```c++ -uint16_t PicoGraphics::create_pen(uint8_t r, uint8_t g, uint8_t b); +int PicoGraphics::create_pen(uint8_t r, uint8_t g, uint8_t b); ``` -Create pen takes R, G and B values, clamps them to 5, 6 and 5 bits respectively and joins them into a `uint16_t` pen that represents a single 16-bit colour. +By default create pen takes R, G and B values, clamps them to 3, 3 and 2 bits respectively and returns an index in the RGB332 palette. -Creating your pens up front and storing them as `uint16_t` can speed up switching colours. +You must create pens before using them with `set_pen()` which accepts only a palette index. #### set_clip & remove_clip @@ -189,6 +228,29 @@ void PicoGraphics::remove_clip(); `remove_clip` sets the surface clipping rectangle back to the surface `bounds`. +### Palette + +By default Pico Graphics uses an `RGB332` palette and clamps all pens to their `RGB332` values so it can give you an approximate colour for every `RGB888` value you request. If you don't want to think about colours and palettes you can leave it as is. + +Alternatively `set_palette_mode()` lets you switch into an RGB565 `USER` palette which gives you up to 256 16-bit colours of your choice. + +#### update_pen + +```c++ +int PicoGraphics::update_pen(uint8_t index, uint8_t r, uint8_t g, uint8_t b); +``` + +Modify a palette entry to the given RGB colour (or nearest supported equivilent.) + + +#### reset_pen + +```c++ +void PicoGraphics::reset_pen(uint8_t index); +``` + +Return a palette entry to its default value. Usually black and marked unused. + ### Pixels #### pixel diff --git a/libraries/pico_graphics/pico_graphics.cmake b/libraries/pico_graphics/pico_graphics.cmake index a77cd83f..71d0cf9a 100644 --- a/libraries/pico_graphics/pico_graphics.cmake +++ b/libraries/pico_graphics/pico_graphics.cmake @@ -1,8 +1,13 @@ 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 + ${CMAKE_CURRENT_LIST_DIR}/pico_graphics_pen_rgb565.cpp ) target_include_directories(pico_graphics INTERFACE ${CMAKE_CURRENT_LIST_DIR}) -target_link_libraries(pico_graphics bitmap_fonts) \ No newline at end of file +target_link_libraries(pico_graphics bitmap_fonts hershey_fonts pico_stdlib) \ No newline at end of file diff --git a/libraries/pico_graphics/pico_graphics.cpp b/libraries/pico_graphics/pico_graphics.cpp index 17e09937..4d1e82bc 100644 --- a/libraries/pico_graphics/pico_graphics.cpp +++ b/libraries/pico_graphics/pico_graphics.cpp @@ -1,21 +1,46 @@ #include "pico_graphics.hpp" namespace pimoroni { - PicoGraphics::PicoGraphics(uint16_t width, uint16_t height, uint16_t *frame_buffer) - : frame_buffer(frame_buffer), bounds(0, 0, width, height), clip(0, 0, width, height) { - set_font(&font6); - }; + + int PicoGraphics::update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b) {return -1;}; + int PicoGraphics::reset_pen(uint8_t i) {return -1;}; + int PicoGraphics::create_pen(uint8_t r, uint8_t g, uint8_t b) {return -1;}; + void PicoGraphics::set_pixel_dither(const Point &p, const RGB &c) {}; + void PicoGraphics::set_pixel_dither(const Point &p, const RGB565 &c) {}; + void PicoGraphics::scanline_convert(PenType type, conversion_callback_func callback) {}; + void PicoGraphics::sprite(void* data, const Point &sprite, const Point &dest, const int scale, const int transparent) {}; + + void PicoGraphics::set_dimensions(int width, int height) { + bounds = clip = {0, 0, width, height}; + } + + void PicoGraphics::set_framebuffer(void *frame_buffer) { + this->frame_buffer = frame_buffer; + } void PicoGraphics::set_font(const bitmap::font_t *font){ - this->font = font; + this->bitmap_font = font; + this->hershey_font = nullptr; } - void PicoGraphics::set_pen(uint8_t r, uint8_t g, uint8_t b) { - pen = create_pen(r, g, b); + void PicoGraphics::set_font(const hershey::font_t *font){ + this->bitmap_font = nullptr; + this->hershey_font = font; } - void PicoGraphics::set_pen(Pen p) { - pen = p; + void PicoGraphics::set_font(std::string name){ + if (name == "bitmap6") { + set_font(&font6); + } else if (name == "bitmap8") { + set_font(&font8); + } else if (name == "bitmap14_outline") { + set_font(&font14_outline); + } else { + // check that font exists and assign it + if(hershey::fonts.find(name) != hershey::fonts.end()) { + set_font(hershey::fonts[name]); + } + } } void PicoGraphics::set_clip(const Rect &r) { @@ -25,26 +50,14 @@ namespace pimoroni { void PicoGraphics::remove_clip() { clip = bounds; } - - Pen* PicoGraphics::ptr(const Rect &r) { - return frame_buffer + r.x + r.y * bounds.w; - } - - Pen* PicoGraphics::ptr(const Point &p) { - return frame_buffer + p.x + p.y * bounds.w; - } - - Pen* PicoGraphics::ptr(int32_t x, int32_t y) { - return frame_buffer + x + y * bounds.w; - } - + void PicoGraphics::clear() { rectangle(clip); } void PicoGraphics::pixel(const Point &p) { if(!clip.contains(p)) return; - *ptr(p) = pen; + set_pixel(p); } void PicoGraphics::pixel_span(const Point &p, int32_t l) { @@ -57,10 +70,8 @@ namespace pimoroni { if(clipped.x < clip.x) {l += clipped.x - clip.x; clipped.x = clip.x;} if(clipped.x + l >= clip.x + clip.w) {l = clip.x + clip.w - clipped.x;} - Pen *dest = ptr(clipped); - while(l--) { - *dest++ = pen; - } + Point dest(clipped.x, clipped.y); + set_pixel_span(dest, l); } void PicoGraphics::rectangle(const Rect &r) { @@ -69,15 +80,12 @@ namespace pimoroni { if(clipped.empty()) return; - Pen *dest = ptr(clipped); + Point dest(clipped.x, clipped.y); while(clipped.h--) { // draw span of pixels for this row - for(int32_t i = 0; i < clipped.w; i++) { - *dest++ = pen; - } - + set_pixel_span(dest, clipped.w); // move to next scanline - dest += bounds.w - clipped.w; + dest.y++; } } @@ -109,16 +117,42 @@ namespace pimoroni { } } - void PicoGraphics::character(const char c, const Point &p, uint8_t scale) { - bitmap::character(font, [this](int32_t x, int32_t y, int32_t w, int32_t h){ - rectangle(Rect(x, y, w, h)); - }, c, p.x, p.y, scale); + void PicoGraphics::character(const char c, const Point &p, float s, float a) { + if (bitmap_font) { + bitmap::character(bitmap_font, [this](int32_t x, int32_t y, int32_t w, int32_t h) { + rectangle(Rect(x, y, w, h)); + }, c, p.x, p.y, std::max(1.0f, s)); + return; + } + + if (hershey_font) { + hershey::glyph(hershey_font, [this](int32_t x1, int32_t y1, int32_t x2, int32_t y2) { + line(Point(x1, y1), Point(x2, y2)); + }, c, p.x, p.y, s, a); + return; + } } - void PicoGraphics::text(const std::string &t, const Point &p, int32_t wrap, uint8_t scale) { - bitmap::text(font, [this](int32_t x, int32_t y, int32_t w, int32_t h){ - rectangle(Rect(x, y, w, h)); - }, t, p.x, p.y, wrap, scale); + void PicoGraphics::text(const std::string &t, const Point &p, int32_t wrap, float s, float a, uint8_t letter_spacing) { + if (bitmap_font) { + bitmap::text(bitmap_font, [this](int32_t x, int32_t y, int32_t w, int32_t h) { + rectangle(Rect(x, y, w, h)); + }, t, p.x, p.y, wrap, std::max(1.0f, s), letter_spacing); + return; + } + + if (hershey_font) { + hershey::text(hershey_font, [this](int32_t x1, int32_t y1, int32_t x2, int32_t y2) { + line(Point(x1, y1), Point(x2, y2)); + }, t, p.x, p.y, s, a); + return; + } + } + + int32_t PicoGraphics::measure_text(const std::string &t, float s, uint8_t letter_spacing) { + if (bitmap_font) return bitmap::measure_text(bitmap_font, t, std::max(1.0f, s), letter_spacing); + if (hershey_font) return hershey::measure_text(hershey_font, t, s); + return 0; } int32_t orient2d(Point p1, Point p2, Point p3) { @@ -171,13 +205,13 @@ namespace pimoroni { int32_t w1 = w1row; int32_t w2 = w2row; - Pen *dest = ptr(triangle_bounds.x, triangle_bounds.y + y); + Point dest = Point(triangle_bounds.x, triangle_bounds.y + y); for (int32_t x = 0; x < triangle_bounds.w; x++) { if ((w0 | w1 | w2) >= 0) { - *dest = pen; + set_pixel(dest); } - dest++; + dest.x++; w0 += a12; w1 += a20; @@ -239,24 +273,29 @@ namespace pimoroni { void PicoGraphics::line(Point p1, Point p2) { // fast horizontal line if(p1.y == p2.y) { - int32_t start = std::max(clip.x, std::min(p1.x, p2.x)); - int32_t end = std::min(clip.x + clip.w, std::max(p1.x, p2.x)); + p1 = p1.clamp(clip); + p2 = p2.clamp(clip); + int32_t start = std::min(p1.x, p2.x); + int32_t end = std::max(p1.x, p2.x); pixel_span(Point(start, p1.y), end - start); return; } // fast vertical line if(p1.x == p2.x) { - int32_t start = std::max(clip.y, std::min(p1.y, p2.y)); - int32_t length = std::min(clip.y + clip.h, std::max(p1.y, p2.y)) - start; - Pen *dest = ptr(p1.x, start); + p1 = p1.clamp(clip); + p2 = p2.clamp(clip); + int32_t start = std::min(p1.y, p2.y); + int32_t length = std::max(p1.y, p2.y) - start; + Point dest(p1.x, start); while(length--) { - *dest = pen; - dest += bounds.w; + set_pixel(dest); + dest.y++; } return; } + // general purpose line // lines are either "shallow" or "steep" based on whether the x delta // is greater than the y delta @@ -271,7 +310,8 @@ namespace pimoroni { int32_t x = p1.x; int32_t y = p1.y << 16; while(s--) { - pixel(Point(x, y >> 16)); + Point p(x, y >> 16); + if(clip.contains(p)) set_pixel(p); y += sy; x += sx; } @@ -283,7 +323,8 @@ namespace pimoroni { int32_t y = p1.y; int32_t x = p1.x << 16; while(s--) { - pixel(Point(x >> 16, y)); + Point p(x >> 16, y); + if(clip.contains(p)) set_pixel(p); y += sy; x += sx; } diff --git a/libraries/pico_graphics/pico_graphics.hpp b/libraries/pico_graphics/pico_graphics.hpp index a80857cf..9493acb0 100644 --- a/libraries/pico_graphics/pico_graphics.hpp +++ b/libraries/pico_graphics/pico_graphics.hpp @@ -1,16 +1,90 @@ #pragma once #include +#include #include #include #include +#include + +#include "libraries/hershey_fonts/hershey_fonts.hpp" +#include "libraries/bitmap_fonts/bitmap_fonts.hpp" #include "libraries/bitmap_fonts/font6_data.hpp" +#include "libraries/bitmap_fonts/font8_data.hpp" +#include "libraries/bitmap_fonts/font14_outline_data.hpp" -// a tiny little graphics library for our Pico products -// supports only 16-bit (565) RGB framebuffers +#include "common/pimoroni_common.hpp" + +// A tiny graphics library for our Pico products +// supports: +// - 16-bit (565) RGB +// - 8-bit (332) RGB +// - 8-bit with 16-bit 256 entry palette +// - 4-bit with 16-bit 8 entry palette namespace pimoroni { + typedef uint8_t RGB332; + typedef uint16_t RGB565; + struct RGB { + int16_t r, g, b; - typedef uint16_t Pen; + constexpr RGB() : r(0), g(0), b(0) {} + constexpr RGB(RGB332 c) : + r((c & 0b11100000) >> 0), + g((c & 0b00011100) << 3), + b((c & 0b00000011) << 6) {} + constexpr RGB(RGB565 c) : + r((__builtin_bswap16(c) & 0b1111100000000000) >> 8), + g((__builtin_bswap16(c) & 0b0000011111100000) >> 3), + b((__builtin_bswap16(c) & 0b0000000000011111) << 3) {} + constexpr RGB(uint8_t r, uint8_t g, uint8_t b) : r(r), g(g), b(b) {} + + constexpr RGB operator+ (const RGB& c) const {return RGB(r + c.r, g + c.g, b + c.b);} + constexpr RGB& operator+=(const RGB& c) {r += c.r; g += c.g; b += c.b; return *this;} + constexpr RGB operator- (const RGB& c) const {return RGB(r - c.r, g - c.g, b - c.b);} + + // a rough approximation of how bright a colour is used to compare the + // relative brightness of two colours + int luminance() const { + // weights based on https://www.johndcook.com/blog/2009/08/24/algorithms-convert-color-grayscale/ + return r * 21 + g * 72 + b * 7; + } + + // a relatively low cost approximation of how "different" two colours are + // perceived which avoids expensive colour space conversions. + // described in detail at https://www.compuphase.com/cmetric.htm + int distance(const RGB& c) const { + int rmean = (r + c.r) / 2; + int rx = r - c.r; + int gx = g - c.g; + int bx = b - c.b; + return abs((int)( + (((512 + rmean) * rx * rx) >> 8) + 4 * gx * gx + (((767 - rmean) * bx * bx) >> 8) + )); + } + + int closest(const RGB *palette, size_t len) const { + int d = INT_MAX, m = -1; + for(size_t i = 0; i < len; i++) { + int dc = distance(palette[i]); + if(dc < d) {m = i; d = dc;} + } + return m; + } + + constexpr RGB565 to_rgb565() { + uint16_t p = ((r & 0b11111000) << 8) | + ((g & 0b11111100) << 3) | + ((b & 0b11111000) >> 3); + + return __builtin_bswap16(p); + } + + constexpr RGB565 to_rgb332() { + return (r & 0b11100000) | ((g & 0b11100000) >> 3) | ((b & 0b11000000) >> 6); + } + }; + + typedef int Pen; struct Rect; @@ -22,10 +96,18 @@ namespace pimoroni { inline Point& operator-= (const Point &a) { x -= a.x; y -= a.y; return *this; } inline Point& operator+= (const Point &a) { x += a.x; y += a.y; return *this; } + inline Point& operator/= (const int32_t a) { x /= a; y /= a; return *this; } Point clamp(const Rect &r) const; }; + inline bool operator== (const Point &lhs, const Point &rhs) { return lhs.x == rhs.x && lhs.y == rhs.y; } + inline bool operator!= (const Point &lhs, const Point &rhs) { return !(lhs == rhs); } + inline Point operator- (Point lhs, const Point &rhs) { lhs -= rhs; return lhs; } + inline Point operator- (const Point &rhs) { return Point(-rhs.x, -rhs.y); } + inline Point operator+ (Point lhs, const Point &rhs) { lhs += rhs; return lhs; } + inline Point operator/ (Point lhs, const int32_t a) { lhs /= a; return lhs; } + struct Rect { int32_t x = 0, y = 0, w = 0, h = 0; @@ -43,48 +125,251 @@ namespace pimoroni { void deflate(int32_t v); }; + static const RGB565 rgb332_to_rgb565_lut[256] = { + 0x0000, 0x0800, 0x1000, 0x1800, 0x0001, 0x0801, 0x1001, 0x1801, 0x0002, 0x0802, 0x1002, 0x1802, 0x0003, 0x0803, 0x1003, 0x1803, + 0x0004, 0x0804, 0x1004, 0x1804, 0x0005, 0x0805, 0x1005, 0x1805, 0x0006, 0x0806, 0x1006, 0x1806, 0x0007, 0x0807, 0x1007, 0x1807, + 0x0020, 0x0820, 0x1020, 0x1820, 0x0021, 0x0821, 0x1021, 0x1821, 0x0022, 0x0822, 0x1022, 0x1822, 0x0023, 0x0823, 0x1023, 0x1823, + 0x0024, 0x0824, 0x1024, 0x1824, 0x0025, 0x0825, 0x1025, 0x1825, 0x0026, 0x0826, 0x1026, 0x1826, 0x0027, 0x0827, 0x1027, 0x1827, + 0x0040, 0x0840, 0x1040, 0x1840, 0x0041, 0x0841, 0x1041, 0x1841, 0x0042, 0x0842, 0x1042, 0x1842, 0x0043, 0x0843, 0x1043, 0x1843, + 0x0044, 0x0844, 0x1044, 0x1844, 0x0045, 0x0845, 0x1045, 0x1845, 0x0046, 0x0846, 0x1046, 0x1846, 0x0047, 0x0847, 0x1047, 0x1847, + 0x0060, 0x0860, 0x1060, 0x1860, 0x0061, 0x0861, 0x1061, 0x1861, 0x0062, 0x0862, 0x1062, 0x1862, 0x0063, 0x0863, 0x1063, 0x1863, + 0x0064, 0x0864, 0x1064, 0x1864, 0x0065, 0x0865, 0x1065, 0x1865, 0x0066, 0x0866, 0x1066, 0x1866, 0x0067, 0x0867, 0x1067, 0x1867, + 0x0080, 0x0880, 0x1080, 0x1880, 0x0081, 0x0881, 0x1081, 0x1881, 0x0082, 0x0882, 0x1082, 0x1882, 0x0083, 0x0883, 0x1083, 0x1883, + 0x0084, 0x0884, 0x1084, 0x1884, 0x0085, 0x0885, 0x1085, 0x1885, 0x0086, 0x0886, 0x1086, 0x1886, 0x0087, 0x0887, 0x1087, 0x1887, + 0x00a0, 0x08a0, 0x10a0, 0x18a0, 0x00a1, 0x08a1, 0x10a1, 0x18a1, 0x00a2, 0x08a2, 0x10a2, 0x18a2, 0x00a3, 0x08a3, 0x10a3, 0x18a3, + 0x00a4, 0x08a4, 0x10a4, 0x18a4, 0x00a5, 0x08a5, 0x10a5, 0x18a5, 0x00a6, 0x08a6, 0x10a6, 0x18a6, 0x00a7, 0x08a7, 0x10a7, 0x18a7, + 0x00c0, 0x08c0, 0x10c0, 0x18c0, 0x00c1, 0x08c1, 0x10c1, 0x18c1, 0x00c2, 0x08c2, 0x10c2, 0x18c2, 0x00c3, 0x08c3, 0x10c3, 0x18c3, + 0x00c4, 0x08c4, 0x10c4, 0x18c4, 0x00c5, 0x08c5, 0x10c5, 0x18c5, 0x00c6, 0x08c6, 0x10c6, 0x18c6, 0x00c7, 0x08c7, 0x10c7, 0x18c7, + 0x00e0, 0x08e0, 0x10e0, 0x18e0, 0x00e1, 0x08e1, 0x10e1, 0x18e1, 0x00e2, 0x08e2, 0x10e2, 0x18e2, 0x00e3, 0x08e3, 0x10e3, 0x18e3, + 0x00e4, 0x08e4, 0x10e4, 0x18e4, 0x00e5, 0x08e5, 0x10e5, 0x18e5, 0x00e6, 0x08e6, 0x10e6, 0x18e6, 0x00e7, 0x08e7, 0x10e7, 0x18e7, + }; + class PicoGraphics { public: - uint16_t *frame_buffer; - - Rect bounds; - Rect clip; - - Pen pen; - - const bitmap::font_t *font; - - public: - PicoGraphics(uint16_t width, uint16_t height, uint16_t *frame_buffer); - void set_font(const bitmap::font_t *font); - void set_pen(uint8_t r, uint8_t g, uint8_t b); - void set_pen(Pen p); - - constexpr Pen create_pen(uint8_t r, uint8_t g, uint8_t b) { - uint16_t p = ((r & 0b11111000) << 8) | - ((g & 0b11111100) << 3) | - ((b & 0b11111000) >> 3); - - return __builtin_bswap16(p); + enum PenType { + PEN_1BIT, + PEN_P2, + PEN_P4, + PEN_P8, + PEN_RGB332, + PEN_RGB565 }; + void *frame_buffer; + + PenType pen_type; + Rect bounds; + Rect clip; + + typedef std::function conversion_callback_func; + //typedef std::function scanline_interrupt_func; + + //scanline_interrupt_func scanline_interrupt = nullptr; + + const bitmap::font_t *bitmap_font; + const hershey::font_t *hershey_font; + + static constexpr RGB332 rgb_to_rgb332(uint8_t r, uint8_t g, uint8_t b) { + return RGB(r, g, b).to_rgb332(); + } + + static constexpr RGB565 rgb332_to_rgb565(RGB332 c) { + uint16_t p = ((c & 0b11100000) << 8) | + ((c & 0b00011100) << 6) | + ((c & 0b00000011) << 3); + return __builtin_bswap16(p); + } + + static constexpr RGB565 rgb565_to_rgb332(RGB565 c) { + c = __builtin_bswap16(c); + return ((c & 0b1110000000000000) >> 8) | + ((c & 0b0000011100000000) >> 6) | + ((c & 0b0000000000011000) >> 3); + } + + static constexpr RGB565 rgb_to_rgb565(uint8_t r, uint8_t g, uint8_t b) { + return RGB(r, g, b).to_rgb565(); + } + + static constexpr RGB rgb332_to_rgb(RGB332 c) { + return RGB((RGB332)c); + }; + + static constexpr RGB rgb565_to_rgb(RGB565 c) { + return RGB((RGB565)c); + }; + + PicoGraphics(uint16_t width, uint16_t height, void *frame_buffer) + : frame_buffer(frame_buffer), bounds(0, 0, width, height), clip(0, 0, width, height) { + set_font(&font6); + }; + + virtual void set_pen(uint c) = 0; + virtual void set_pen(uint8_t r, uint8_t g, uint8_t b) = 0; + virtual void set_pixel(const Point &p) = 0; + virtual void set_pixel_span(const Point &p, uint l) = 0; + + virtual int create_pen(uint8_t r, uint8_t g, uint8_t b); + virtual int update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b); + virtual int reset_pen(uint8_t i); + virtual void set_pixel_dither(const Point &p, const RGB &c); + virtual void set_pixel_dither(const Point &p, const RGB565 &c); + virtual void scanline_convert(PenType type, conversion_callback_func callback); + virtual void sprite(void* data, const Point &sprite, const Point &dest, const int scale, const int transparent); + + void set_font(const bitmap::font_t *font); + void set_font(const hershey::font_t *font); + void set_font(std::string font); + + void set_dimensions(int width, int height); + void set_framebuffer(void *frame_buffer); + + void *get_data(); + void get_data(PenType type, uint y, void *row_buf); + void set_clip(const Rect &r); void remove_clip(); - Pen* ptr(const Point &p); - Pen* ptr(const Rect &r); - Pen* ptr(int32_t x, int32_t y); - void clear(); void pixel(const Point &p); void pixel_span(const Point &p, int32_t l); void rectangle(const Rect &r); void circle(const Point &p, int32_t r); - void character(const char c, const Point &p, uint8_t scale = 2); - void text(const std::string &t, const Point &p, int32_t wrap, uint8_t scale = 2); + void character(const char c, const Point &p, float s = 2.0f, float a = 0.0f); + void text(const std::string &t, const Point &p, int32_t wrap, float s = 2.0f, float a = 0.0f, uint8_t letter_spacing = 1); + int32_t measure_text(const std::string &t, float s = 2.0f, uint8_t letter_spacing = 1); void polygon(const std::vector &points); void triangle(Point p1, Point p2, Point p3); 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; + 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: + static const uint palette_size = 16; + uint8_t color; + RGB palette[palette_size]; + bool used[palette_size]; + + const uint pattern[16] = // dither pattern + {0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5}; + std::array, 512> candidate_cache; + bool cache_built = false; + std::array candidates; + + PicoGraphics_PenP4(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; + int update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b) override; + int create_pen(uint8_t r, uint8_t g, uint8_t b) override; + int reset_pen(uint8_t i) override; + + void set_pixel(const Point &p) override; + void set_pixel_span(const Point &p, uint l) override; + void get_dither_candidates(const RGB &col, const RGB *palette, size_t len, std::array &candidates); + void set_pixel_dither(const Point &p, const RGB &c) override; + + void scanline_convert(PenType type, conversion_callback_func callback) override; + static size_t buffer_size(uint w, uint h) { + return w * h / 2; + } + }; + + class PicoGraphics_PenP8 : public PicoGraphics { + public: + static const uint palette_size = 256; + uint8_t color; + RGB palette[palette_size]; + bool used[palette_size]; + + const uint pattern[16] = // dither pattern + {0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5}; + std::array, 512> candidate_cache; + bool cache_built = false; + std::array candidates; + + PicoGraphics_PenP8(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; + int update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b) override; + int create_pen(uint8_t r, uint8_t g, uint8_t b) override; + int reset_pen(uint8_t i) override; + + void set_pixel(const Point &p) override; + void set_pixel_span(const Point &p, uint l) override; + void get_dither_candidates(const RGB &col, const RGB *palette, size_t len, std::array &candidates); + void set_pixel_dither(const Point &p, const RGB &c) override; + + void scanline_convert(PenType type, conversion_callback_func callback) override; + static size_t buffer_size(uint w, uint h) { + return w * h; + } + }; + + class PicoGraphics_PenRGB332 : public PicoGraphics { + public: + RGB332 color; + PicoGraphics_PenRGB332(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; + int create_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; + void set_pixel_dither(const Point &p, const RGB &c) override; + void set_pixel_dither(const Point &p, const RGB565 &c) override; + + void sprite(void* data, const Point &sprite, const Point &dest, const int scale, const int transparent) override; + + void scanline_convert(PenType type, conversion_callback_func callback) override; + static size_t buffer_size(uint w, uint h) { + return w * h; + } + }; + + class PicoGraphics_PenRGB565 : public PicoGraphics { + public: + RGB src_color; + RGB565 color; + PicoGraphics_PenRGB565(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; + int create_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 * sizeof(RGB565); + } + }; + + class DisplayDriver { + public: + uint16_t width; + uint16_t height; + Rotation rotation; + + DisplayDriver(uint16_t width, uint16_t height, Rotation rotation) + : width(width), height(height), rotation(rotation) {}; + + virtual void update(PicoGraphics *display) {}; + virtual void set_backlight(uint8_t brightness) {}; + virtual void cleanup() {}; + }; + } 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..ef0f2d0b --- /dev/null +++ b/libraries/pico_graphics/pico_graphics_pen_1bit.cpp @@ -0,0 +1,60 @@ +#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); + } + + void PicoGraphics_Pen1Bit::set_pixel_span(const Point &p, uint l) { + // 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); + + // TODO: this could trivially be sped up by processing single bits only at + // the start and the end of the span and writing full bytes (8 pixels at + // a time) in the middle portion of the span. would only be more efficient + // for longer spans (probably around 20 pixels or more) + while(l--) { + // forceably clear the bit and then set to the correct value + *f &= ~(1U << bo); + *f |= (color << bo); + + if(bo == 0) { // last bit of this byte? + // move to next byte in framebuffer and reset the bit offset + f++; bo = 8; + } + + bo--; + } + } + +} \ No newline at end of file diff --git a/libraries/pico_graphics/pico_graphics_pen_p4.cpp b/libraries/pico_graphics/pico_graphics_pen_p4.cpp new file mode 100644 index 00000000..11fbeb99 --- /dev/null +++ b/libraries/pico_graphics/pico_graphics_pen_p4.cpp @@ -0,0 +1,154 @@ +#include "pico_graphics.hpp" + +namespace pimoroni { + + PicoGraphics_PenP4::PicoGraphics_PenP4(uint16_t width, uint16_t height, void *frame_buffer) + : PicoGraphics(width, height, frame_buffer) { + this->pen_type = PEN_P4; + if(this->frame_buffer == nullptr) { + this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]); + } + for(auto i = 0u; i < palette_size; i++) { + palette[i] = { + uint8_t(i << 4), + uint8_t(i << 4), + uint8_t(i << 4) + }; + used[i] = false; + } + cache_built = false; + } + void PicoGraphics_PenP4::set_pen(uint c) { + color = c & 0xf; + } + void PicoGraphics_PenP4::set_pen(uint8_t r, uint8_t g, uint8_t b) { + int pen = RGB(r, g, b).closest(palette, palette_size); + if(pen != -1) color = pen; + } + int PicoGraphics_PenP4::update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b) { + i &= 0xf; + used[i] = true; + palette[i] = {r, g, b}; + cache_built = false; + return i; + } + int PicoGraphics_PenP4::create_pen(uint8_t r, uint8_t g, uint8_t b) { + // Create a colour and place it in the palette if there's space + for(auto i = 0u; i < palette_size; i++) { + if(!used[i]) { + palette[i] = {r, g, b}; + used[i] = true; + cache_built = false; + return i; + } + } + return -1; + } + int PicoGraphics_PenP4::reset_pen(uint8_t i) { + palette[i] = {0, 0, 0}; + used[i] = false; + cache_built = false; + return i; + } + void PicoGraphics_PenP4::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 / 2) + (p.y * bounds.w / 2)]; + + uint8_t o = (~p.x & 0b1) * 4; // bit offset within byte + uint8_t m = ~(0b1111 << o); // bit mask for byte + uint8_t b = color << o; // bit value shifted to position + + *f &= m; // clear bits + *f |= b; // set value + } + + void PicoGraphics_PenP4::set_pixel_span(const Point &p, uint l) { + // pointer to byte in framebuffer that contains this pixel + uint8_t *buf = (uint8_t *)frame_buffer; + uint8_t *f = &buf[(p.x / 2) + (p.y * bounds.w / 2)]; + + // doubled up color value, so the color is stored in both nibbles + uint8_t cc = color | (color << 4); + + // handle the first pixel if not byte aligned + if(p.x & 0b1) {*f &= 0b11110000; *f |= (cc & 0b00001111); f++; l--;} + + // write any double nibble pixels + while(l > 1) {*f++ = cc; l -= 2;} + + // handle the last pixel if not byte aligned + if(l) {*f &= 0b00001111; *f |= (cc & 0b11110000);} + } + + void PicoGraphics_PenP4::get_dither_candidates(const RGB &col, const RGB *palette, size_t len, std::array &candidates) { + RGB error; + for(size_t i = 0; i < candidates.size(); i++) { + candidates[i] = (col + error).closest(palette, len); + error += (col - palette[candidates[i]]); + } + + // sort by a rough approximation of luminance, this ensures that neighbouring + // pixels in the dither matrix are at extreme opposites of luminence + // giving a more balanced output + std::sort(candidates.begin(), candidates.end(), [palette](int a, int b) { + return palette[a].luminance() > palette[b].luminance(); + }); + } + + void PicoGraphics_PenP4::set_pixel_dither(const Point &p, const RGB &c) { + if(!bounds.contains(p)) return; + + if(!cache_built) { + for(uint i = 0; i < 512; i++) { + RGB cache_col((i & 0x1C0) >> 1, (i & 0x38) << 2, (i & 0x7) << 5); + get_dither_candidates(cache_col, palette, palette_size, candidate_cache[i]); + } + cache_built = true; + } + + uint cache_key = ((c.r & 0xE0) << 1) | ((c.g & 0xE0) >> 2) | ((c.b & 0xE0) >> 5); + //get_dither_candidates(c, palette, 256, candidates); + + // find the pattern coordinate offset + uint pattern_index = (p.x & 0b11) | ((p.y & 0b11) << 2); + + // set the pixel + //color = candidates[pattern[pattern_index]]; + color = candidate_cache[cache_key][pattern[pattern_index]]; + set_pixel(p); + } + void PicoGraphics_PenP4::scanline_convert(PenType type, conversion_callback_func callback) { + if(type == PEN_RGB565) { + // Cache the RGB888 palette as RGB565 + RGB565 cache[palette_size]; + for(auto i = 0u; i < palette_size; i++) { + cache[i] = palette[i].to_rgb565(); + } + + // Treat our void* frame_buffer as uint8_t + uint8_t *src = (uint8_t *)frame_buffer; + + // Allocate a per-row temporary buffer + uint16_t row_buf[bounds.w]; + for(auto y = 0; y < bounds.h; y++) { + /*if(scanline_interrupt != nullptr) { + scanline_interrupt(y); + // Cache the RGB888 palette as RGB565 + for(auto i = 0u; i < 16; i++) { + cache[i] = palette[i].to_rgb565(); + } + }*/ + + for(auto x = 0; x < bounds.w; x++) { + uint8_t c = src[(bounds.w * y / 2) + (x / 2)]; + uint8_t o = (~x & 0b1) * 4; // bit offset within byte + uint8_t b = (c >> o) & 0xf; // bit value shifted to position + row_buf[x] = cache[b]; + } + // Callback to the driver with the row data + callback(row_buf, bounds.w * sizeof(RGB565)); + } + } + } +} \ No newline at end of file diff --git a/libraries/pico_graphics/pico_graphics_pen_p8.cpp b/libraries/pico_graphics/pico_graphics_pen_p8.cpp new file mode 100644 index 00000000..a2f3aad1 --- /dev/null +++ b/libraries/pico_graphics/pico_graphics_pen_p8.cpp @@ -0,0 +1,123 @@ +#include "pico_graphics.hpp" + +namespace pimoroni { + PicoGraphics_PenP8::PicoGraphics_PenP8(uint16_t width, uint16_t height, void *frame_buffer) + : PicoGraphics(width, height, frame_buffer) { + this->pen_type = PEN_P8; + if(this->frame_buffer == nullptr) { + this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]); + } + for(auto i = 0u; i < palette_size; i++) { + palette[i] = {uint8_t(i), uint8_t(i), uint8_t(i)}; + used[i] = false; + } + cache_built = false; + } + void PicoGraphics_PenP8::set_pen(uint c) { + color = c; + } + void PicoGraphics_PenP8::set_pen(uint8_t r, uint8_t g, uint8_t b) { + int pen = RGB(r, g, b).closest(palette, 16); + if(pen != -1) color = pen; + } + int PicoGraphics_PenP8::update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b) { + i &= 0xff; + used[i] = true; + palette[i] = {r, g, b}; + cache_built = false; + return i; + } + int PicoGraphics_PenP8::create_pen(uint8_t r, uint8_t g, uint8_t b) { + // Create a colour and place it in the palette if there's space + for(auto i = 0u; i < palette_size; i++) { + if(!used[i]) { + palette[i] = {r, g, b}; + used[i] = true; + cache_built = false; + return i; + } + } + return -1; + } + int PicoGraphics_PenP8::reset_pen(uint8_t i) { + palette[i] = {0, 0, 0}; + used[i] = false; + cache_built = false; + return i; + } + void PicoGraphics_PenP8::set_pixel(const Point &p) { + uint8_t *buf = (uint8_t *)frame_buffer; + buf[p.y * bounds.w + p.x] = color; + } + + void PicoGraphics_PenP8::set_pixel_span(const Point &p, uint l) { + // pointer to byte in framebuffer that contains this pixel + uint8_t *buf = (uint8_t *)frame_buffer; + buf = &buf[p.y * bounds.w + p.x]; + + while(l--) { + *buf++ = color; + } + } + + void PicoGraphics_PenP8::get_dither_candidates(const RGB &col, const RGB *palette, size_t len, std::array &candidates) { + RGB error; + for(size_t i = 0; i < candidates.size(); i++) { + candidates[i] = (col + error).closest(palette, len); + error += (col - palette[candidates[i]]); + } + + // sort by a rough approximation of luminance, this ensures that neighbouring + // pixels in the dither matrix are at extreme opposites of luminence + // giving a more balanced output + std::sort(candidates.begin(), candidates.end(), [palette](int a, int b) { + return palette[a].luminance() > palette[b].luminance(); + }); + } + + void PicoGraphics_PenP8::set_pixel_dither(const Point &p, const RGB &c) { + if(!bounds.contains(p)) return; + + if(!cache_built) { + for(uint i = 0; i < 512; i++) { + RGB cache_col((i & 0x1C0) >> 1, (i & 0x38) << 2, (i & 0x7) << 5); + get_dither_candidates(cache_col, palette, palette_size, candidate_cache[i]); + } + cache_built = true; + } + + uint cache_key = ((c.r & 0xE0) << 1) | ((c.g & 0xE0) >> 2) | ((c.b & 0xE0) >> 5); + //get_dither_candidates(c, palette, 256, candidates); + + // find the pattern coordinate offset + uint pattern_index = (p.x & 0b11) | ((p.y & 0b11) << 2); + + // set the pixel + //color = candidates[pattern[pattern_index]]; + color = candidate_cache[cache_key][pattern[pattern_index]]; + set_pixel(p); + } + + void PicoGraphics_PenP8::scanline_convert(PenType type, conversion_callback_func callback) { + if(type == PEN_RGB565) { + // Cache the RGB888 palette as RGB565 + RGB565 cache[palette_size]; + for(auto i = 0u; i < palette_size; i++) { + cache[i] = palette[i].to_rgb565(); + } + + // Treat our void* frame_buffer as uint8_t + uint8_t *src = (uint8_t *)frame_buffer; + + // Allocate a per-row temporary buffer + uint16_t row_buf[bounds.w]; + for(auto y = 0; y < bounds.h; y++) { + for(auto x = 0; x < bounds.w; x++) { + row_buf[x] = cache[src[bounds.w * y + x]]; + } + // Callback to the driver with the row data + callback(row_buf, bounds.w * sizeof(RGB565)); + } + } + } +} \ No newline at end of file diff --git a/libraries/pico_graphics/pico_graphics_pen_rgb332.cpp b/libraries/pico_graphics/pico_graphics_pen_rgb332.cpp new file mode 100644 index 00000000..3489c1d2 --- /dev/null +++ b/libraries/pico_graphics/pico_graphics_pen_rgb332.cpp @@ -0,0 +1,134 @@ +#include "pico_graphics.hpp" +#include + +namespace pimoroni { + PicoGraphics_PenRGB332::PicoGraphics_PenRGB332(uint16_t width, uint16_t height, void *frame_buffer) + : PicoGraphics(width, height, frame_buffer) { + this->pen_type = PEN_RGB332; + if(this->frame_buffer == nullptr) { + this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]); + } + } + void PicoGraphics_PenRGB332::set_pen(uint c) { + color = c; + } + void PicoGraphics_PenRGB332::set_pen(uint8_t r, uint8_t g, uint8_t b) { + color = rgb_to_rgb332(r, g, b); + } + int PicoGraphics_PenRGB332::create_pen(uint8_t r, uint8_t g, uint8_t b) { + return rgb_to_rgb332(r, g, b); + } + void PicoGraphics_PenRGB332::set_pixel(const Point &p) { + uint8_t *buf = (uint8_t *)frame_buffer; + buf[p.y * bounds.w + p.x] = color; + } + void PicoGraphics_PenRGB332::set_pixel_span(const Point &p, uint l) { + // pointer to byte in framebuffer that contains this pixel + uint8_t *buf = (uint8_t *)frame_buffer; + buf = &buf[p.y * bounds.w + p.x]; + + while(l--) { + *buf++ = color; + } + } + void PicoGraphics_PenRGB332::set_pixel_dither(const Point &p, const RGB &c) { + if(!bounds.contains(p)) return; + static uint8_t _odm[16] = { + 0, 8, 2, 10, + 12, 4, 14, 6, + 3, 11, 1, 9, + 15, 7, 13, 5 + }; + + uint8_t _dmv = _odm[(p.x & 0b11) | ((p.y & 0b11) << 2)]; + + uint8_t red = c.r & 0b11000000; // Two bits red + uint8_t red_r = c.r & 0b111111; // Remaining six bits red + red_r >>= 2; // Discard down to four bit + + uint8_t grn = (c.g & 0b11000000) >> 3; // Two bits green + uint8_t grn_r = c.g & 0b111111; // Remaining six bits green + grn_r >>= 2; // Discard down to four bit + + uint8_t blu = (c.b & 0b10000000) >> 6; // One bit blue + uint8_t blu_r = c.b & 0b1111111; // Remaining seven bits green + blu_r >>= 3; // Discard down to four bit + + color = red | grn | blu; + if(red_r > _dmv) color |= 0b00100000; + if(grn_r > _dmv) color |= 0b00000100; + if(blu_r > _dmv) color |= 0b00000001; + + set_pixel(p); + } + void PicoGraphics_PenRGB332::set_pixel_dither(const Point &p, const RGB565 &c) { + if(!bounds.contains(p)) return; + RGB565 cs = __builtin_bswap16(c); + static uint8_t _odm[16] = { + 0, 8, 2, 10, + 12, 4, 14, 6, + 3, 11, 1, 9, + 15, 7, 13, 5 + }; + + uint8_t _dmv = _odm[(p.x & 0b11) | ((p.y & 0b11) << 2)]; + + // RRRRRGGGGGGBBBBB + uint8_t red = (cs & 0b1100000000000000) >> 8; // Two bits grn + uint8_t red_r = (cs & 0b0011100000000000) >> 10; // Four bits cmp + + uint8_t grn = (cs & 0b0000011000000000) >> 6; // Two bit grn + uint8_t grn_r = (cs & 0b0000000111100000) >> 5; // Four bit cmp + + uint8_t blu = (cs & 0b0000000000010000) >> 3; // Two bit blu + uint8_t blu_r = (cs & 0b0000000000001111); // Four bit cmp + + color = red | grn | blu; + // RRRGGGBB + if(red_r > _dmv) color |= 0b00100000; + if(grn_r > _dmv) color |= 0b00000100; + if(blu_r > _dmv) color |= 0b00000001; + + set_pixel(p); + } + void PicoGraphics_PenRGB332::scanline_convert(PenType type, conversion_callback_func callback) { + if(type == PEN_RGB565) { + + // Treat our void* frame_buffer as uint8_t + uint8_t *src = (uint8_t *)frame_buffer; + + // Allocate a per-row temporary buffer + uint16_t row_buf[bounds.w]; + for(auto y = 0; y < bounds.h; y++) { + for(auto x = 0; x < bounds.w; x++) { + row_buf[x] = rgb332_to_rgb565_lut[*src]; + + src++; + } + // Callback to the driver with the row data + callback(row_buf, bounds.w * sizeof(RGB565)); + } + } + } + void PicoGraphics_PenRGB332::sprite(void* data, const Point &sprite, const Point &dest, const int scale, const int transparent) { + //int sprite_x = (sprite & 0x0f) << 3; + //int sprite_y = (sprite & 0xf0) >> 1; + Point s { + sprite.x << 3, + sprite.y << 3 + }; + RGB332 *ptr = (RGB332 *)data; + Point o = {0, 0}; + for(o.y = 0; o.y < 8 * scale; o.y++) { + Point so = { + 0, + o.y / scale + }; + for(o.x = 0; o.x < 8 * scale; o.x++) { + so.x = o.x / scale; + color = ptr[(s.y + so.y) * 128 + (s.x + so.x)]; + if(color != transparent) pixel(dest + o); + } + } + } +} \ No newline at end of file diff --git a/libraries/pico_graphics/pico_graphics_pen_rgb565.cpp b/libraries/pico_graphics/pico_graphics_pen_rgb565.cpp new file mode 100644 index 00000000..37520703 --- /dev/null +++ b/libraries/pico_graphics/pico_graphics_pen_rgb565.cpp @@ -0,0 +1,34 @@ +#include "pico_graphics.hpp" + +namespace pimoroni { + PicoGraphics_PenRGB565::PicoGraphics_PenRGB565(uint16_t width, uint16_t height, void *frame_buffer) + : PicoGraphics(width, height, frame_buffer) { + this->pen_type = PEN_RGB565; + if(this->frame_buffer == nullptr) { + this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]); + } + } + void PicoGraphics_PenRGB565::set_pen(uint c) { + color = c; + } + void PicoGraphics_PenRGB565::set_pen(uint8_t r, uint8_t g, uint8_t b) { + src_color = {r, g, b}; + color = src_color.to_rgb565(); + } + int PicoGraphics_PenRGB565::create_pen(uint8_t r, uint8_t g, uint8_t b) { + return RGB(r, g, b).to_rgb565(); + } + void PicoGraphics_PenRGB565::set_pixel(const Point &p) { + uint16_t *buf = (uint16_t *)frame_buffer; + buf[p.y * bounds.w + p.x] = color; + } + void PicoGraphics_PenRGB565::set_pixel_span(const Point &p, uint l) { + // pointer to byte in framebuffer that contains this pixel + uint16_t *buf = (uint16_t *)frame_buffer; + buf = &buf[p.y * bounds.w + p.x]; + + while(l--) { + *buf++ = color; + } + } +} \ No newline at end of file diff --git a/libraries/pico_graphics/types.cpp b/libraries/pico_graphics/types.cpp index 91d8fb27..53dac95e 100644 --- a/libraries/pico_graphics/types.cpp +++ b/libraries/pico_graphics/types.cpp @@ -12,20 +12,6 @@ namespace pimoroni { ); } - Point operator- (Point lhs, const Point &rhs) { - lhs -= rhs; - return lhs; - } - - Point operator- (const Point &rhs) { - return Point(-rhs.x, -rhs.y); - } - - Point operator+ (Point lhs, const Point &rhs) { - lhs += rhs; - return lhs; - } - bool Rect::empty() const { return w <= 0 || h <= 0; } diff --git a/libraries/tufty2040/CMakeLists.txt b/libraries/tufty2040/CMakeLists.txt new file mode 100644 index 00000000..8f727a06 --- /dev/null +++ b/libraries/tufty2040/CMakeLists.txt @@ -0,0 +1 @@ +include(tufty2040.cmake) \ No newline at end of file diff --git a/libraries/generic_st7789/generic_st7789.cmake b/libraries/tufty2040/tufty2040.cmake similarity index 78% rename from libraries/generic_st7789/generic_st7789.cmake rename to libraries/tufty2040/tufty2040.cmake index f25f5797..ecc4dccb 100644 --- a/libraries/generic_st7789/generic_st7789.cmake +++ b/libraries/tufty2040/tufty2040.cmake @@ -1,11 +1,11 @@ -set(LIB_NAME generic_st7789) -add_library(${LIB_NAME} INTERFACE) - -target_sources(${LIB_NAME} INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/${LIB_NAME}.cpp -) - -target_include_directories(${LIB_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR}) - -# Pull in pico libraries that we need -target_link_libraries(${LIB_NAME} INTERFACE pico_stdlib hardware_spi hardware_pwm hardware_dma st7789 pico_graphics) \ No newline at end of file +set(LIB_NAME tufty2040) +add_library(${LIB_NAME} INTERFACE) + +target_sources(${LIB_NAME} INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/${LIB_NAME}.cpp +) + +target_include_directories(${LIB_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR}) + +# Pull in pico libraries that we need +target_link_libraries(${LIB_NAME} INTERFACE pico_stdlib hardware_pwm) diff --git a/libraries/tufty2040/tufty2040.cpp b/libraries/tufty2040/tufty2040.cpp new file mode 100644 index 00000000..c32350c6 --- /dev/null +++ b/libraries/tufty2040/tufty2040.cpp @@ -0,0 +1,7 @@ +#include +#include + +#include "tufty2040.hpp" + +namespace pimoroni { +} \ No newline at end of file diff --git a/libraries/tufty2040/tufty2040.hpp b/libraries/tufty2040/tufty2040.hpp new file mode 100644 index 00000000..5798171f --- /dev/null +++ b/libraries/tufty2040/tufty2040.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include +#include + +#include "hardware/gpio.h" // Workaround SDK bug - https://github.com/raspberrypi/pico-sdk/issues/3 +#include "hardware/pwm.h" + +namespace pimoroni { + + class Tufty2040 { + public: + static const int WIDTH = 320; + static const int HEIGHT = 240; + static const uint8_t A = 7; + static const uint8_t B = 8; + static const uint8_t C = 9; + static const uint8_t UP = 22; + static const uint8_t DOWN = 6; + static const uint8_t LED = 25; + static const uint8_t BACKLIGHT = 2; + + + static const uint8_t LCD_CS = 10; + static const uint8_t LCD_DC = 11; + static const uint8_t LCD_WR = 12; + static const uint8_t LCD_RD = 13; + static const uint8_t LCD_D0 = 14; + + public: + Tufty2040() { + + gpio_set_function(LCD_D0 + 0, GPIO_FUNC_SIO); gpio_set_dir(LCD_D0 + 0, true); + gpio_set_function(LCD_D0 + 1, GPIO_FUNC_SIO); gpio_set_dir(LCD_D0 + 1, true); + gpio_set_function(LCD_D0 + 2, GPIO_FUNC_SIO); gpio_set_dir(LCD_D0 + 2, true); + gpio_set_function(LCD_D0 + 3, GPIO_FUNC_SIO); gpio_set_dir(LCD_D0 + 3, true); + gpio_set_function(LCD_D0 + 4, GPIO_FUNC_SIO); gpio_set_dir(LCD_D0 + 4, true); + gpio_set_function(LCD_D0 + 5, GPIO_FUNC_SIO); gpio_set_dir(LCD_D0 + 5, true); + gpio_set_function(LCD_D0 + 6, GPIO_FUNC_SIO); gpio_set_dir(LCD_D0 + 6, true); + gpio_set_function(LCD_D0 + 7, GPIO_FUNC_SIO); gpio_set_dir(LCD_D0 + 7, true); + + // led control pin + pwm_config cfg = pwm_get_default_config(); + pwm_set_wrap(pwm_gpio_to_slice_num(LED), 65535); + pwm_init(pwm_gpio_to_slice_num(LED), &cfg, true); + gpio_set_function(LED, GPIO_FUNC_PWM); + led(0); + } + + void led(uint8_t brightness) { + // set the led brightness from 1 to 256 with gamma correction + float gamma = 2.8; + uint16_t v = (uint16_t)(pow((float)(brightness) / 256.0f, gamma) * 65535.0f + 0.5f); + pwm_set_gpio_level(LED, v); + } + + }; + +} diff --git a/micropython/_board/board-fixup.sh b/micropython/_board/board-fixup.sh new file mode 100755 index 00000000..ecc4fd60 --- /dev/null +++ b/micropython/_board/board-fixup.sh @@ -0,0 +1,16 @@ +NAME=$1 +BOARD=$2 +FIXUP_DIR=$3 +MPY_DIR=`pwd` + +echo "Fixup: $NAME $BOARD $MPY_DIR" + +if [[ ! -f "$MPY_DIR/../../lib/pico-sdk/src/boards/include/boards/pimoroni_$NAME.h" ]]; then + echo "Missing Pico SDK board .h. Copying: $FIXUP_DIR/$NAME/pimoroni_$NAME.h to $MPY_DIR/../../lib/pico-sdk/src/boards/include/boards/pimoroni_$NAME.h" + cp "$FIXUP_DIR/$NAME/pimoroni_$NAME.h" "$MPY_DIR/../../lib/pico-sdk/src/boards/include/boards/pimoroni_$NAME.h" +fi + +if [[ ! -d "$MPY_DIR/boards/$BOARD" ]]; then + echo "Missing board dir. Copying: $FIXUP_DIR/$NAME/$BOARD/ to $MPY_DIR/boards/" + cp -r "$FIXUP_DIR/$NAME/$BOARD/" "$MPY_DIR/boards/" +fi \ No newline at end of file diff --git a/micropython/_board/tufty2040/PIMORONI_TUFTY2040/board.json b/micropython/_board/tufty2040/PIMORONI_TUFTY2040/board.json new file mode 100644 index 00000000..c2bfdff0 --- /dev/null +++ b/micropython/_board/tufty2040/PIMORONI_TUFTY2040/board.json @@ -0,0 +1,21 @@ +{ + "deploy": [ + "../deploy.md" + ], + "docs": "", + "features": [ + "SPI Flash", + "STEMMA QT/QWIIC", + "USB-C", + "320*240 LCD", + "Buttons", + "LiPo/Battery Support" + ], + "images": [ + ], + "mcu": "rp2040", + "product": "Tufty2040 (8MiB)", + "thumbnail": "", + "url": "https://shop.pimoroni.com/products/pimoroni-tufty-2040", + "vendor": "Pimoroni" +} diff --git a/micropython/_board/tufty2040/PIMORONI_TUFTY2040/mpconfigboard.cmake b/micropython/_board/tufty2040/PIMORONI_TUFTY2040/mpconfigboard.cmake new file mode 100644 index 00000000..f14f21fd --- /dev/null +++ b/micropython/_board/tufty2040/PIMORONI_TUFTY2040/mpconfigboard.cmake @@ -0,0 +1 @@ +# cmake file for Pimoroni Tufty 2040 diff --git a/micropython/_board/tufty2040/PIMORONI_TUFTY2040/mpconfigboard.h b/micropython/_board/tufty2040/PIMORONI_TUFTY2040/mpconfigboard.h new file mode 100644 index 00000000..1140d377 --- /dev/null +++ b/micropython/_board/tufty2040/PIMORONI_TUFTY2040/mpconfigboard.h @@ -0,0 +1,28 @@ +// https://shop.pimoroni.com/products/pimoroni-tufty-2040 + +#define MICROPY_HW_BOARD_NAME "Pimoroni Tufty 2040" +#define MICROPY_HW_FLASH_STORAGE_BYTES (7 * 1024 * 1024) + +#define MICROPY_HW_USB_VID (0x2E8A) +#define MICROPY_HW_USB_PID (0x1002) + +#define MICROPY_HW_UART1_TX (8) +#define MICROPY_HW_UART1_RX (9) +#define MICROPY_HW_UART1_CTS (10) +#define MICROPY_HW_UART1_RTS (11) + +// Qwiic on I2C0 +#define MICROPY_HW_I2C0_SCL (4) +#define MICROPY_HW_I2C0_SDA (5) + +#define MICROPY_HW_SPI0_SCK (18) +#define MICROPY_HW_SPI0_MOSI (19) +#define MICROPY_HW_SPI0_MISO (16) + +// User LED GPIO25 + +// VBUS_SENSE GPIO24 + +// BAT_SENSE GPIO29 + +// Boot button GPIO23 diff --git a/micropython/_board/tufty2040/pimoroni_tufty2040.h b/micropython/_board/tufty2040/pimoroni_tufty2040.h new file mode 100644 index 00000000..28a6a24f --- /dev/null +++ b/micropython/_board/tufty2040/pimoroni_tufty2040.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +// ----------------------------------------------------- +// NOTE: THIS HEADER IS ALSO INCLUDED BY ASSEMBLER SO +// SHOULD ONLY CONSIST OF PREPROCESSOR DIRECTIVES +// ----------------------------------------------------- + +#ifndef _BOARDS_PIMORONI_TUFTY_2040_H +#define _BOARDS_PIMORONI_TUFTY_2040_H + +// For board detection +#define PIMORONI_TUFTY_2040 + +// --- LED --- +#ifndef PICO_DEFAULT_LED_PIN +#define PICO_DEFAULT_LED_PIN 25 +#endif +// no PICO_DEFAULT_WS2812_PIN + +// --- I2C --- +#ifndef PICO_DEFAULT_I2C +#define PICO_DEFAULT_I2C 0 +#endif +#ifndef PICO_DEFAULT_I2C_SDA_PIN +#define PICO_DEFAULT_I2C_SDA_PIN 4 +#endif +#ifndef PICO_DEFAULT_I2C_SCL_PIN +#define PICO_DEFAULT_I2C_SCL_PIN 5 +#endif + +// --- SPI --- +#ifndef PICO_DEFAULT_SPI +#define PICO_DEFAULT_SPI 0 +#endif +#ifndef PICO_DEFAULT_SPI_SCK_PIN +#define PICO_DEFAULT_SPI_SCK_PIN 18 +#endif +#ifndef PICO_DEFAULT_SPI_TX_PIN +#define PICO_DEFAULT_SPI_TX_PIN 19 +#endif +#ifndef PICO_DEFAULT_SPI_RX_PIN +#define PICO_DEFAULT_SPI_RX_PIN 16 +#endif +#ifndef PICO_DEFAULT_SPI_CSN_PIN +#define PICO_DEFAULT_SPI_CSN_PIN 17 +#endif + +// --- FLASH --- +#define PICO_BOOT_STAGE2_CHOOSE_W25Q080 1 + +#ifndef PICO_FLASH_SPI_CLKDIV +#define PICO_FLASH_SPI_CLKDIV 2 +#endif + +#ifndef PICO_FLASH_SIZE_BYTES +#define PICO_FLASH_SIZE_BYTES (8 * 1024 * 1024) +#endif + +// All boards have B1 RP2040 +#ifndef PICO_RP2040_B0_SUPPORTED +#define PICO_RP2040_B0_SUPPORTED 0 +#endif + +#endif diff --git a/micropython/examples/breakout_as7262/pico_explorer_graph.py b/micropython/examples/breakout_as7262/pico_explorer_graph.py index 43246936..3b46a144 100644 --- a/micropython/examples/breakout_as7262/pico_explorer_graph.py +++ b/micropython/examples/breakout_as7262/pico_explorer_graph.py @@ -1,17 +1,16 @@ from pimoroni_i2c import PimoroniI2C from breakout_as7262 import BreakoutAS7262 -import picoexplorer as display +import picographics import time -width = display.get_width() -height = display.get_height() + +display = picographics.PicoGraphics(picographics.DISPLAY_PICO_EXPLORER) + +width, height = display.get_bounds() bar_width = width // 6 bar_height = height -display_buffer = bytearray(width * height * 2) # 2-bytes per pixel (RGB565) -display.init(display_buffer) - i2c = PimoroniI2C(20, 21) as7 = BreakoutAS7262(i2c) @@ -31,35 +30,44 @@ def draw_bar(v, i): display.rectangle(i * bar_width, current_bar_top, bar_width, current_bar_height - 1) +BLACK = display.create_pen(0, 0, 0) +RED = display.create_pen(255, 0, 0) +ORANGE = display.create_pen(255, 128, 0) +YELLOW = display.create_pen(255, 255, 0) +GREEN = display.create_pen(0, 255, 0) +BLUE = display.create_pen(0, 0, 255) +VIOLET = display.create_pen(255, 0, 255) + + while True: r, o, y, g, b, v = as7.read() m = max(r, o, y, g, b, v) - display.set_pen(0, 0, 0) + display.set_pen(BLACK) display.clear() # Red - display.set_pen(255, 0, 0) + display.set_pen(RED) draw_bar(r / m, 0) # Orange - display.set_pen(255, 128, 0) + display.set_pen(ORANGE) draw_bar(o / m, 1) # Yellow - display.set_pen(255, 255, 0) + display.set_pen(YELLOW) draw_bar(y / m, 2) # Green - display.set_pen(0, 255, 0) + display.set_pen(GREEN) draw_bar(g / m, 3) # Blue - display.set_pen(0, 0, 255) + display.set_pen(BLUE) draw_bar(b / m, 4) # Violet - display.set_pen(255, 0, 255) + display.set_pen(VIOLET) draw_bar(v / m, 5) display.update() diff --git a/micropython/examples/breakout_colourlcd240x240/demo.py b/micropython/examples/breakout_colourlcd240x240/colourlcd240x240_demo.py similarity index 74% rename from micropython/examples/breakout_colourlcd240x240/demo.py rename to micropython/examples/breakout_colourlcd240x240/colourlcd240x240_demo.py index 90ae788c..b614cef9 100644 --- a/micropython/examples/breakout_colourlcd240x240/demo.py +++ b/micropython/examples/breakout_colourlcd240x240/colourlcd240x240_demo.py @@ -1,10 +1,14 @@ import time import random -from st7789 import ST7789 +from st7789 import ST7789, PALETTE_USER, DISPLAY_LCD_240X240 -WIDTH, HEIGHT = 240, 240 +display = ST7789(DISPLAY_LCD_240X240, rotate=0) -display = ST7789(WIDTH, HEIGHT, round=False) +WIDTH, HEIGHT = display.get_bounds() + +# We're creating 100 balls with their own individual colour and 1 BG colour +# for a total of 101 colours, which will all fit in the 256 entry palette! +display.set_palette_mode(PALETTE_USER) display.set_backlight(1.0) @@ -34,8 +38,10 @@ for i in range(0, 100): ) ) +BG = display.create_pen(40, 40, 40) + while True: - display.set_pen(40, 40, 40) + display.set_pen(BG) display.clear() for ball in balls: diff --git a/micropython/examples/motor2040/led_rainbow.py b/micropython/examples/motor2040/led_rainbow.py index e20f1dd6..ce610891 100644 --- a/micropython/examples/motor2040/led_rainbow.py +++ b/micropython/examples/motor2040/led_rainbow.py @@ -30,7 +30,7 @@ led.start() hue = 0.0 # Make rainbows until the user button is pressed -while user_sw.raw() is not True: +while not user_sw.raw(): hue += SPEED / 1000.0 diff --git a/micropython/examples/motor2040/motor_wave.py b/micropython/examples/motor2040/motor_wave.py index 7aab108b..e63e5c09 100644 --- a/micropython/examples/motor2040/motor_wave.py +++ b/micropython/examples/motor2040/motor_wave.py @@ -40,7 +40,7 @@ led.start() offset = 0.0 # Make waves until the user button is pressed -while user_sw.raw() is not True: +while not user_sw.raw(): offset += SPEED / 1000.0 diff --git a/micropython/examples/motor2040/position_control.py b/micropython/examples/motor2040/position_control.py index 8a514738..9ffdb94b 100644 --- a/micropython/examples/motor2040/position_control.py +++ b/micropython/examples/motor2040/position_control.py @@ -66,7 +66,7 @@ start_value = 0.0 end_value = random.uniform(-POSITION_EXTENT, POSITION_EXTENT) # Continually move the motor until the user button is pressed -while user_sw.raw() is not True: +while not user_sw.raw(): # Capture the state of the encoder capture = enc.capture() diff --git a/micropython/examples/motor2040/position_on_velocity_control.py b/micropython/examples/motor2040/position_on_velocity_control.py index 87e5e065..f996dd4b 100644 --- a/micropython/examples/motor2040/position_on_velocity_control.py +++ b/micropython/examples/motor2040/position_on_velocity_control.py @@ -73,7 +73,7 @@ start_value = 0.0 end_value = random.uniform(-POSITION_EXTENT, POSITION_EXTENT) # Continually move the motor until the user button is pressed -while user_sw.raw() is not True: +while not user_sw.raw(): # Capture the state of the encoder capture = enc.capture() diff --git a/micropython/examples/motor2040/position_on_velocity_tuning.py b/micropython/examples/motor2040/position_on_velocity_tuning.py index 993b248a..fac44a76 100644 --- a/micropython/examples/motor2040/position_on_velocity_tuning.py +++ b/micropython/examples/motor2040/position_on_velocity_tuning.py @@ -71,7 +71,7 @@ update = 0 print_count = 0 # Continually move the motor until the user button is pressed -while user_sw.raw() is not True: +while not user_sw.raw(): # Capture the state of the encoder capture = enc.capture() diff --git a/micropython/examples/motor2040/position_tuning.py b/micropython/examples/motor2040/position_tuning.py index 69e0df42..27218259 100644 --- a/micropython/examples/motor2040/position_tuning.py +++ b/micropython/examples/motor2040/position_tuning.py @@ -64,7 +64,7 @@ update = 0 print_count = 0 # Continually move the motor until the user button is pressed -while user_sw.raw() is not True: +while not user_sw.raw(): # Capture the state of the encoder capture = enc.capture() diff --git a/micropython/examples/motor2040/quad_position_wave.py b/micropython/examples/motor2040/quad_position_wave.py index c9a0d6d2..deab932f 100644 --- a/micropython/examples/motor2040/quad_position_wave.py +++ b/micropython/examples/motor2040/quad_position_wave.py @@ -78,7 +78,7 @@ end_value = 270.0 captures = [None] * motor2040.NUM_MOTORS # Continually move the motor until the user button is pressed -while user_sw.raw() is not True: +while not user_sw.raw(): # Capture the state of all the encoders for i in range(motor2040.NUM_MOTORS): diff --git a/micropython/examples/motor2040/quad_velocity_sequence.py b/micropython/examples/motor2040/quad_velocity_sequence.py index c153ad2d..7ef50b22 100644 --- a/micropython/examples/motor2040/quad_velocity_sequence.py +++ b/micropython/examples/motor2040/quad_velocity_sequence.py @@ -102,7 +102,7 @@ sequence = 0 captures = [None] * motor2040.NUM_MOTORS # Continually move the motor until the user button is pressed -while user_sw.raw() is not True: +while not user_sw.raw(): # Capture the state of all the encoders for i in range(motor2040.NUM_MOTORS): diff --git a/micropython/examples/motor2040/reactive_encoder.py b/micropython/examples/motor2040/reactive_encoder.py index 4f1cb7fd..7dbf6ccf 100644 --- a/micropython/examples/motor2040/reactive_encoder.py +++ b/micropython/examples/motor2040/reactive_encoder.py @@ -92,7 +92,7 @@ detent_change(0) # Continually move the motor until the user button is pressed -while user_sw.raw() is not True: +while not user_sw.raw(): # Capture the state of the encoder capture = enc.capture() diff --git a/micropython/examples/motor2040/read_encoders.py b/micropython/examples/motor2040/read_encoders.py index 3d07ef64..7561f6b9 100644 --- a/micropython/examples/motor2040/read_encoders.py +++ b/micropython/examples/motor2040/read_encoders.py @@ -34,7 +34,7 @@ encoders = [Encoder(0, i, ENCODER_PINS[i], counts_per_rev=COUNTS_PER_REV, count_ user_sw = Button(motor2040.USER_SW) # Read the encoders until the user button is pressed -while user_sw.raw() is not True: +while not user_sw.raw(): # Print out the angle of each encoder for i in range(NUM_ENCODERS): diff --git a/micropython/examples/motor2040/read_sensors.py b/micropython/examples/motor2040/read_sensors.py index 7ca4909c..c8caa324 100644 --- a/micropython/examples/motor2040/read_sensors.py +++ b/micropython/examples/motor2040/read_sensors.py @@ -33,7 +33,7 @@ user_sw = Button(motor2040.USER_SW) # Read sensors until the user button is pressed -while user_sw.raw() is not True: +while not user_sw.raw(): # Read each sensor in turn and print its voltage for i in range(len(sensor_addrs)): diff --git a/micropython/examples/motor2040/velocity_control.py b/micropython/examples/motor2040/velocity_control.py index 53d47668..45b160e0 100644 --- a/micropython/examples/motor2040/velocity_control.py +++ b/micropython/examples/motor2040/velocity_control.py @@ -66,7 +66,7 @@ start_value = 0.0 end_value = random.uniform(-VELOCITY_EXTENT, VELOCITY_EXTENT) # Continually move the motor until the user button is pressed -while user_sw.raw() is not True: +while not user_sw.raw(): # Capture the state of the encoder capture = enc.capture() diff --git a/micropython/examples/motor2040/velocity_tuning.py b/micropython/examples/motor2040/velocity_tuning.py index 6f275f3b..071c1789 100644 --- a/micropython/examples/motor2040/velocity_tuning.py +++ b/micropython/examples/motor2040/velocity_tuning.py @@ -64,7 +64,7 @@ update = 0 print_count = 0 # Continually move the motor until the user button is pressed -while user_sw.raw() is not True: +while not user_sw.raw(): # Capture the state of the encoder capture = enc.capture() diff --git a/micropython/examples/pico_display/basic_qrcode.py b/micropython/examples/pico_display/basic_qrcode.py index 6ca46727..a689b764 100644 --- a/micropython/examples/pico_display/basic_qrcode.py +++ b/micropython/examples/pico_display/basic_qrcode.py @@ -1,12 +1,12 @@ import st7789 import qrcode -# Set the display resolution -# in most cases you can swap WIDTH weith HEIGHT for portrait mode -WIDTH, HEIGHT = 240, 135 # Pico Display -# WIDTH, HEIGHT = 320, 240 # Pico Display 2.0 +display = st7789.ST7789(st7789.DISPLAY_PICO_DISPLAY, rotate=0) -display = st7789.ST7789(WIDTH, HEIGHT, rotate180=False) +WIDTH, HEIGHT = display.get_bounds() + +BG = display.create_pen(0, 0, 0) +FG = display.create_pen(128, 128, 128) def measure_qr_code(size, code): @@ -17,9 +17,9 @@ def measure_qr_code(size, code): def draw_qr_code(ox, oy, size, code): size, module_size = measure_qr_code(size, code) - display.set_pen(128, 128, 128) + display.set_pen(FG) display.rectangle(ox, oy, size, size) - display.set_pen(0, 0, 0) + display.set_pen(BG) for x in range(size): for y in range(size): if code.get_module(x, y): @@ -29,14 +29,16 @@ def draw_qr_code(ox, oy, size, code): code = qrcode.QRCode() code.set_text("shop.pimoroni.com") -display.set_pen(128, 128, 128) +display.set_pen(FG) display.clear() -display.set_pen(0, 0, 0) +display.set_pen(BG) -size, module_size = measure_qr_code(HEIGHT, code) +max_size = min(WIDTH, HEIGHT) + +size, module_size = measure_qr_code(max_size, code) left = int((WIDTH // 2) - (size // 2)) top = int((HEIGHT // 2) - (size // 2)) -draw_qr_code(left, top, HEIGHT, code) +draw_qr_code(left, top, max_size, code) display.update() diff --git a/micropython/examples/pico_display/buttons.py b/micropython/examples/pico_display/buttons.py index 874ade1c..0d350f8d 100644 --- a/micropython/examples/pico_display/buttons.py +++ b/micropython/examples/pico_display/buttons.py @@ -4,14 +4,11 @@ import st7789 import utime from pimoroni import Button -# Set the display resolution -# in most cases you can swap WIDTH weith HEIGHT for portrait mode -WIDTH, HEIGHT = 240, 135 # Pico Display -# WIDTH, HEIGHT = 320, 240 # Pico Display 2.0 -display = st7789.ST7789(WIDTH, HEIGHT, rotate180=False) +display = st7789.ST7789(st7789.DISPLAY_PICO_DISPLAY, rotate=0) display.set_backlight(0.5) +WIDTH, HEIGHT = display.get_bounds() button_a = Button(12) button_b = Button(13) diff --git a/micropython/examples/pico_display/demo.py b/micropython/examples/pico_display/demo.py index d240c08e..305e5d4b 100644 --- a/micropython/examples/pico_display/demo.py +++ b/micropython/examples/pico_display/demo.py @@ -2,11 +2,9 @@ import time import random import st7789 -# Set the display resolution, in most cases you can flip these for portrait mode -WIDTH, HEIGHT = 240, 135 # Pico Display -# WIDTH, HEIGHT = 320, 240 # Pico Display 2.0 +display = st7789.ST7789(st7789.DISPLAY_PICO_DISPLAY, rotate=0) -display = st7789.ST7789(WIDTH, HEIGHT, rotate180=False) +WIDTH, HEIGHT = display.get_bounds() display.set_backlight(1.0) diff --git a/micropython/examples/pico_display/rainbow.py b/micropython/examples/pico_display/rainbow.py index 9cb1ee1b..d2af8d85 100644 --- a/micropython/examples/pico_display/rainbow.py +++ b/micropython/examples/pico_display/rainbow.py @@ -4,13 +4,11 @@ import utime import st7789 from pimoroni import RGBLED -# Set the display resolution -# in most cases you can swap WIDTH weith HEIGHT for portrait mode -WIDTH, HEIGHT = 240, 135 # Pico Display -# WIDTH, HEIGHT = 320, 240 # Pico Display 2.0 - -display = st7789.ST7789(WIDTH, HEIGHT, rotate180=False) +display = st7789.ST7789(st7789.DISPLAY_PICO_DISPLAY, rotate=0) display.set_backlight(0.8) +display.set_palette_mode(st7789.PALETTE_USER) + +WIDTH, HEIGHT = display.get_bounds() led = RGBLED(6, 7, 8) @@ -41,13 +39,18 @@ def hsv_to_rgb(h, s, v): h = 0 +BLACK = display.create_pen(0, 0, 0) +RAINBOW = BLACK + 1 # Put RAINBOW right after BLACK in the palette + + while True: h += 1 r, g, b = [int(255 * c) for c in hsv_to_rgb(h / 360.0, 1.0, 1.0)] # rainbow magic led.set_rgb(r, g, b) # Set LED to a converted HSV value - display.set_pen(r, g, b) # Set pen to a converted HSV value + display.set_palette(RAINBOW, st7789.RGB565(r, g, b)) # Create pen with converted HSV value + display.set_pen(RAINBOW) # Set pen display.clear() # Fill the screen with the colour - display.set_pen(0, 0, 0) # Set pen to black + display.set_pen(BLACK) # Set pen to black display.text("pico disco!", 10, 10, 240, 6) # Add some text display.update() # Update the display utime.sleep(1.0 / 60) diff --git a/micropython/examples/pico_display/thermometer.py b/micropython/examples/pico_display/thermometer.py index cd07b565..ea680779 100644 --- a/micropython/examples/pico_display/thermometer.py +++ b/micropython/examples/pico_display/thermometer.py @@ -7,16 +7,18 @@ import utime import st7789 from pimoroni import RGBLED -# Set the display resolution -# in most cases you can swap WIDTH weith HEIGHT for portrait mode -WIDTH, HEIGHT = 135, 240 # Pico Display -# WIDTH, HEIGHT = 320, 240 # Pico Display 2.0 - -display = st7789.ST7789(WIDTH, HEIGHT, rotate180=False) +display = st7789.ST7789(st7789.DISPLAY_PICO_DISPLAY, rotate=0) +display.set_palette_mode(st7789.PALETTE_USER) # Set the display backlight to 50% display.set_backlight(0.5) +WIDTH, HEIGHT = display.get_bounds() + +BLACK = display.create_pen(0, 0, 0) +WHITE = display.create_pen(255, 255, 255) +TEMPERATURE = WHITE + 1 + led = RGBLED(6, 7, 8) # reads from Pico's temp sensor and converts it into a more manageable number @@ -53,7 +55,7 @@ def temperature_to_color(temp): while True: # fills the screen with black - display.set_pen(0, 0, 0) + display.set_pen(BLACK) display.clear() # the following two lines do some maths to convert the number from the temp sensor into celsius @@ -68,8 +70,9 @@ while True: i = 0 for t in temperatures: - # chooses a pen colour based on the temperature - display.set_pen(*temperature_to_color(t)) + # chooses a pen colour based on the temperature and update the palette entry + display.set_palette(TEMPERATURE, st7789.RGB565(*temperature_to_color(t))) + display.set_pen(TEMPERATURE) # draws the reading as a tall, thin rectangle display.rectangle(i, HEIGHT - (round(t) * 4), bar_width, HEIGHT) @@ -82,11 +85,11 @@ while True: led.set_rgb(*temperature_to_color(temperature)) # draws a white background for the text - display.set_pen(255, 255, 255) + display.set_pen(WHITE) display.rectangle(1, 1, 100, 25) # writes the reading as text in the white rectangle - display.set_pen(0, 0, 0) + display.set_pen(BLACK) display.text("{:.2f}".format(temperature) + "c", 3, 3, 0, 3) # time to update the display diff --git a/micropython/examples/pico_explorer/balls_demo.py b/micropython/examples/pico_explorer/balls_demo.py index f086c03a..e87725fe 100644 --- a/micropython/examples/pico_explorer/balls_demo.py +++ b/micropython/examples/pico_explorer/balls_demo.py @@ -1,20 +1,11 @@ -# adapted from demo.py in examples/pico_display -# runs full screen on a pico explorer -# all credit to orignal author(s) - -# I just changed the import statement to import picoexplorer instaead of picodisplay - - import time import random -import picoexplorer as display +import st7789 -width = display.get_width() -height = display.get_height() +display = st7789.ST7789(st7789.DISPLAY_PICO_EXPLORER, rotate=0) +display.set_backlight(1.0) -display_buffer = bytearray(width * height * 2) # 2-bytes per pixel (RGB565) -display.init(display_buffer) - -# display.set_backlight(1.0) +WIDTH, HEIGHT = display.get_bounds() class Ball: @@ -33,8 +24,8 @@ for i in range(0, 100): r = random.randint(0, 10) + 3 balls.append( Ball( - random.randint(r, r + (width - 2 * r)), - random.randint(r, r + (height - 2 * r)), + random.randint(r, r + (WIDTH - 2 * r)), + random.randint(r, r + (HEIGHT - 2 * r)), r, (14 - r) / 2, (14 - r) / 2, @@ -42,17 +33,19 @@ for i in range(0, 100): ) ) +BG = display.create_pen(32, 32, 64) + while True: - display.set_pen(40, 40, 40) + display.set_pen(BG) display.clear() for ball in balls: ball.x += ball.dx ball.y += ball.dy - xmax = width - ball.r + xmax = WIDTH - ball.r xmin = ball.r - ymax = height - ball.r + ymax = HEIGHT - ball.r ymin = ball.r if ball.x < xmin or ball.x > xmax: diff --git a/micropython/examples/pico_explorer/buttons.py b/micropython/examples/pico_explorer/buttons.py index d52528c7..8e49d70a 100644 --- a/micropython/examples/pico_explorer/buttons.py +++ b/micropython/examples/pico_explorer/buttons.py @@ -1,51 +1,66 @@ # This example shows you a simple, non-interrupt way of reading Pico Explorer's buttons with a loop that checks to see if buttons are pressed. -import picoexplorer as display import utime - -# Initialise display with a bytearray display buffer -buf = bytearray(display.get_width() * display.get_height() * 2) -display.init(buf) +import st7789 +import picoexplorer +from pimoroni import Button -# sets up a handy function we can call to clear the screen -def clear(): - display.set_pen(0, 0, 0) - display.clear() - display.update() +display = st7789.ST7789(st7789.DISPLAY_PICO_EXPLORER, rotate=0) +display.set_palette_mode(st7789.PALETTE_USER) +display.set_backlight(1.0) + +button_a = Button(picoexplorer.BUTTON_A) +button_b = Button(picoexplorer.BUTTON_B) +button_x = Button(picoexplorer.BUTTON_X) +button_y = Button(picoexplorer.BUTTON_Y) + +WHITE = display.create_pen(255, 255, 255) +BLACK = display.create_pen(0, 0, 0) +TEAL = display.create_pen(0, 255, 255) +MAGENTA = display.create_pen(255, 0, 255) +YELLOW = display.create_pen(255, 255, 0) +RED = display.create_pen(255, 0, 0) while True: - if display.is_pressed(display.BUTTON_A): # if a button press is detected then... - clear() # clear to black - display.set_pen(255, 255, 255) # change the pen colour + if button_a.is_pressed: # if a button press is detected then... + display.set_pen(BLACK) # set pen to black + display.clear() # clear display to the pen colour + display.set_pen(WHITE) # change the pen colour display.text("Button A pressed", 10, 10, 240, 4) # display some text on the screen display.update() # update the display utime.sleep(1) # pause for a sec - clear() # clear to black again - elif display.is_pressed(display.BUTTON_B): - clear() - display.set_pen(0, 255, 255) + + elif button_b.is_pressed: + display.set_pen(BLACK) + display.clear() + display.set_pen(TEAL) display.text("Button B pressed", 10, 10, 240, 4) display.update() utime.sleep(1) - clear() - elif display.is_pressed(display.BUTTON_X): - clear() - display.set_pen(255, 0, 255) + + elif button_x.is_pressed: + display.set_pen(BLACK) + display.clear() + display.set_pen(MAGENTA) display.text("Button X pressed", 10, 10, 240, 4) display.update() utime.sleep(1) - clear() - elif display.is_pressed(display.BUTTON_Y): - clear() - display.set_pen(255, 255, 0) + + elif button_y.is_pressed: + display.set_pen(BLACK) + display.clear() + display.set_pen(YELLOW) display.text("Button Y pressed", 10, 10, 240, 4) display.update() utime.sleep(1) - clear() + else: - display.set_pen(255, 0, 0) + display.set_pen(BLACK) + display.clear() + display.set_pen(RED) display.text("Press any button!", 10, 10, 240, 4) display.update() + utime.sleep(0.1) # this number is how frequently the Pico checks for button presses diff --git a/micropython/examples/pico_explorer/demo.py b/micropython/examples/pico_explorer/demo.py index 75f362fe..3156091f 100644 --- a/micropython/examples/pico_explorer/demo.py +++ b/micropython/examples/pico_explorer/demo.py @@ -1,79 +1,113 @@ import time -import picoexplorer as explorer +import st7789 +from motor import Motor +import picoexplorer +from pimoroni import Button, Analog, Buzzer -width = explorer.get_width() -height = explorer.get_height() +display = st7789.ST7789(st7789.DISPLAY_PICO_EXPLORER, rotate=0) +display.set_palette_mode(st7789.PALETTE_USER) +display.set_backlight(1.0) -display_buffer = bytearray(width * height * 2) # 2-bytes per pixel (RGB565) -explorer.init(display_buffer) -explorer.set_audio_pin(0) +adc0 = Analog(picoexplorer.ADC0) +adc1 = Analog(picoexplorer.ADC1) +adc2 = Analog(picoexplorer.ADC2) + +button_a = Button(picoexplorer.BUTTON_A) +button_b = Button(picoexplorer.BUTTON_B) +button_x = Button(picoexplorer.BUTTON_X) +button_y = Button(picoexplorer.BUTTON_Y) + +BG = display.create_pen(32, 32, 64) +WHITE = display.create_pen(255, 255, 255) + +ADC0_PEN = display.reserve_palette() +ADC1_PEN = display.reserve_palette() +ADC2_PEN = display.reserve_palette() + +MOTOR1 = Motor(picoexplorer.MOTOR_1) +MOTOR2 = Motor(picoexplorer.MOTOR_2) + +BUZZER = Buzzer(0) i = 1 while True: - explorer.set_pen(120, 40, 60) - explorer.clear() + display.set_pen(BG) + display.clear() - adc0 = int(explorer.get_adc(0) * 120) - adc1 = int(explorer.get_adc(1) * 120) - adc2 = int(explorer.get_adc(2) * 120) + # Read the ADCs + adc0v = int(adc0.read_voltage() / 3.3 * 120) + adc1v = int(adc1.read_voltage() / 3.3 * 120) + adc2v = int(adc2.read_voltage() / 3.3 * 120) - explorer.set_pen(255, 255, 255) + # Update our ADC channel palette colours + display.set_palette(ADC0_PEN, st7789.RGB565(adc0v * 2, 10, 10)) + display.set_palette(ADC1_PEN, st7789.RGB565(10, adc1v * 2, 10)) + display.set_palette(ADC2_PEN, st7789.RGB565(10, 10, adc2v * 2)) - explorer.text("ADC0:", 20, 20, 100) - explorer.text("ADC1:", 20, 40, 100) - explorer.text("ADC2:", 20, 60, 100) + # ADC labels + display.set_pen(WHITE) + display.text("ADC0:", 20, 20, 100) + display.text("ADC1:", 20, 40, 100) + display.text("ADC2:", 20, 60, 100) - explorer.set_pen(adc0 * 2, 0, 0) - explorer.circle(90 + adc0, 26, 10) + # ADC graph + display.set_pen(ADC0_PEN) + display.circle(90 + adc0v, 26, 10) - explorer.set_pen(0, adc1 * 2, 0) - explorer.circle(90 + adc1, 46, 10) + display.set_pen(ADC1_PEN) + display.circle(90 + adc1v, 46, 10) - explorer.set_pen(0, 0, adc2 * 2) - explorer.circle(90 + adc2, 66, 10) + display.set_pen(ADC2_PEN) + display.circle(90 + adc2v, 66, 10) + + # On-board A/B/X/Y buttons + if button_a.is_pressed: + display.set_pen(WHITE) + display.text("Button A pressed", 20, 110, 200) + + elif button_b.is_pressed: + display.set_pen(WHITE) + display.text("Button B pressed", 20, 110, 200) + + elif button_x.is_pressed and button_y.is_pressed: + display.set_pen(WHITE) + display.text("Buttons X and Y pressed", 20, 110, 200) + + elif button_x.is_pressed: + display.set_pen(WHITE) + display.text("Button X pressed", 20, 110, 200) + + elif button_y.is_pressed: + display.set_pen(WHITE) + display.text("Button Y pressed", 20, 110, 200) - # example for the on-board A/B/X/Y buttons - if explorer.is_pressed(explorer.BUTTON_A): - explorer.set_pen(255, 255, 255) - explorer.text("Button A pressed", 20, 110, 200) - elif explorer.is_pressed(explorer.BUTTON_B): - explorer.set_pen(255, 255, 255) - explorer.text("Button B pressed", 20, 110, 200) - elif explorer.is_pressed(explorer.BUTTON_X) and explorer.is_pressed(explorer.BUTTON_Y): - explorer.set_pen(255, 255, 255) - explorer.text("Buttons X and Y pressed", 20, 110, 200) - elif explorer.is_pressed(explorer.BUTTON_X): - explorer.set_pen(255, 255, 255) - explorer.text("Button X pressed", 20, 110, 200) - elif explorer.is_pressed(explorer.BUTTON_Y): - explorer.set_pen(255, 255, 255) - explorer.text("Button Y pressed", 20, 110, 200) else: # no button press was detected - explorer.set_pen(255, 255, 255) - explorer.text("Plug a jumper wire from GP0 to AUDIO to hear noise!", 20, 110, 200) + display.set_pen(WHITE) + display.text("Plug a jumper wire from GP0 to AUDIO to hear noise!", 20, 110, 200) - explorer.set_tone(i) + BUZZER.set_tone(i) + + # Motors + if i > 600: + display.text("Motor 1: Forwards", 20, 180, 200) + MOTOR1.full_positive() + else: + display.text("Motor 1: Backwards", 20, 180, 200) + MOTOR1.full_negative() if i > 600: - explorer.text("Motor 1: Forwards", 20, 180, 200) - explorer.set_motor(0, 0, 1) + display.text("Motor 2: Forwards", 20, 200, 200) + MOTOR2.full_positive() else: - explorer.text("Motor 1: Backwards", 20, 180, 200) - explorer.set_motor(0, 1, 1) - - if i > 600: - explorer.text("Motor 2: Forwards", 20, 200, 200) - explorer.set_motor(1, 0, 1) - else: - explorer.text("Motor 2: Backwards", 20, 200, 200) - explorer.set_motor(1, 1, 1) + display.text("Motor 2: Backwards", 20, 200, 200) + MOTOR2.full_negative() i = i + 20 if i > 1000: i = 1 - explorer.update() + display.update() time.sleep(0.01) diff --git a/micropython/examples/pico_explorer/noise.py b/micropython/examples/pico_explorer/noise.py index 8f094a3f..5db940dc 100644 --- a/micropython/examples/pico_explorer/noise.py +++ b/micropython/examples/pico_explorer/noise.py @@ -3,14 +3,20 @@ # You'll need to connect a jumper wire between GPO and AUDIO on the Explorer Base to hear noise. import utime -import picoexplorer as explorer +import st7789 +from pimoroni import Buzzer -# Set up and initialise Pico Explorer -buf = bytearray(explorer.get_width() * explorer.get_height() * 2) -explorer.init(buf) -# tells Pico Explorer which pin you'll be using for noise -explorer.set_audio_pin(0) +display = st7789.ST7789(st7789.DISPLAY_PICO_EXPLORER, rotate=0) +display.set_palette_mode(st7789.PALETTE_USER) +display.set_backlight(1.0) + +# tCreate a buzzer on pin 0 +# Don't forget t write GP0 to AUDIO! +BUZZER = Buzzer(0) + +BLACK = display.create_pen(0, 0, 0) +GREEN = display.create_pen(0, 255, 0) # this handy list converts notes into frequencies, which you can use with the explorer.set_tone function tones = { @@ -110,17 +116,17 @@ song = ["F6", "F6", "E6", "F6", "F5", "P", "F5", "P", "C6", "AS5", "A5", "C6", " def clear(): # this function clears Pico Explorer's screen to black - explorer.set_pen(0, 0, 0) - explorer.clear() - explorer.update() + display.set_pen(BLACK) + display.clear() + display.update() def playtone(frequency): # this function tells your program how to make noise - explorer.set_tone(frequency) + BUZZER.set_tone(frequency) def bequiet(): # this function tells your program how not to make noise - explorer.set_tone(-1) + BUZZER.set_tone(-1) def playsong(song): # this function plays your song @@ -130,13 +136,13 @@ def playsong(song): # this function plays your song bequiet() else: playtone(tones[song[i]]) - explorer.set_pen(0, 255, 0) # switch to green pen - explorer.rectangle(a, 240 - (int((tones[song[i]]) / 21)), 5, 240) # draw a green bar corresponding to the frequency of the note + display.set_pen(GREEN) # switch to green pen + display.rectangle(a, 240 - (int((tones[song[i]]) / 21)), 5, 240) # draw a green bar corresponding to the frequency of the note a += 7 if a >= 240: # clears the screen if the green bars reach the right hand edge clear() a = 0 - explorer.update() + display.update() utime.sleep(0.15) # change this number if you want to alter how long the notes play for bequiet() diff --git a/micropython/examples/pico_explorer/rainbow.py b/micropython/examples/pico_explorer/rainbow.py index d44bef52..dee58743 100644 --- a/micropython/examples/pico_explorer/rainbow.py +++ b/micropython/examples/pico_explorer/rainbow.py @@ -1,11 +1,19 @@ -# This example borrows a CircuitPython hsv_to_rgb function to cycle through some rainbows on Pico Explorer's screen and RGB LED . If you're into rainbows, HSV (Hue, Saturation, Value) is very useful! +# This example borrows a CircuitPython hsv_to_rgb function to cycle through some rainbows on Pico Explorer's screen. +# If you're into rainbows, HSV (Hue, Saturation, Value) is very useful! import utime -import picoexplorer as display +import st7789 -# Set up and initialise Pico Explorer -buf = bytearray(display.get_width() * display.get_height() * 2) -display.init(buf) + +display = st7789.ST7789(st7789.DISPLAY_PICO_EXPLORER, rotate=0) +display.set_palette_mode(st7789.PALETTE_USER) +display.set_backlight(1.0) + +# Create a text colour +TEXT_COLOR = display.create_pen(0, 0, 0) + +# Reserve a palette entry for our rainbow background colour +RAINBOW = display.reserve_palette() # From CPython Lib/colorsys.py @@ -37,9 +45,10 @@ h = 0 while True: h += 1 r, g, b = [int(255 * c) for c in hsv_to_rgb(h / 360.0, 1.0, 1.0)] # rainbow magic - display.set_pen(r, g, b) # Set pen to a converted HSV value + display.set_palette(RAINBOW, st7789.RGB565(r, g, b)) # Set pen to a converted HSV value + display.set_pen(RAINBOW) display.clear() # Fill the screen with the colour - display.set_pen(0, 0, 0) # Set pen to black + display.set_pen(TEXT_COLOR) display.text("pico disco!", 25, 20, 240, 6) # Add some text display.text("\\o/ \\o/ \\o/ \\o/ \\o/ \\o/ \\o/ \\o/ \\o/", 25, 120, 240, 4) # and some more text display.text("oontz oontz oontz", 25, 220, 240, 2) # and a bit more tiny text diff --git a/micropython/examples/pico_explorer/thermometer.py b/micropython/examples/pico_explorer/thermometer.py index 16c093b1..3b7b5b7c 100644 --- a/micropython/examples/pico_explorer/thermometer.py +++ b/micropython/examples/pico_explorer/thermometer.py @@ -1,20 +1,27 @@ # This example takes the temperature from the Pico's onboard temperature sensor, and displays it on Pico Explorer, along with a little pixelly graph. # It's based on the thermometer example in the "Getting Started with MicroPython on the Raspberry Pi Pico" book, which is a great read if you're a beginner! +import st7789 import machine import utime # Pico Explorer boilerplate -import picoexplorer as display -width = display.get_width() -height = display.get_height() -display_buffer = bytearray(width * height * 2) -display.init(display_buffer) +display = st7789.ST7789(st7789.DISPLAY_PICO_EXPLORER, rotate=0) +display.set_palette_mode(st7789.PALETTE_USER) +display.set_backlight(1.0) + +WIDTH, HEIGHT = display.get_bounds() # reads from Pico's temp sensor and converts it into a more manageable number sensor_temp = machine.ADC(4) conversion_factor = 3.3 / (65535) +BLACK = display.create_pen(0, 0, 0) +WHITE = display.create_pen(255, 255, 255) +RED = display.create_pen(255, 0, 0) +GREEN = display.create_pen(0, 255, 0) +BLUE = display.create_pen(0, 0, 255) + i = 0 while True: @@ -23,27 +30,27 @@ while True: temperature = round(27 - (reading - 0.706) / 0.001721) # this if statement clears the display once the graph reaches the right hand side of the display - if i >= (width + 1): + if i >= WIDTH + 1: i = 0 - display.set_pen(0, 0, 0) + display.set_pen(BLACK) display.clear() # chooses a pen colour based on the temperature - display.set_pen(0, 255, 0) + display.set_pen(GREEN) if temperature > 20: - display.set_pen(255, 0, 0) + display.set_pen(RED) if temperature < 13: - display.set_pen(0, 0, 255) + display.set_pen(BLUE) # draws the reading as a tall, thin rectangle - display.rectangle(i, height - (temperature * 6), 6, height) + display.rectangle(i, HEIGHT - (temperature * 6), 6, HEIGHT) # draws a white background for the text - display.set_pen(255, 255, 255) + display.set_pen(WHITE) display.rectangle(1, 1, 65, 33) # writes the reading as text in the white rectangle - display.set_pen(0, 0, 0) + display.set_pen(BLACK) display.text("{:.0f}".format(temperature) + "c", 3, 3, 0, 4) # time to update the display diff --git a/micropython/examples/pico_explorer/weatherstation_BME280.py b/micropython/examples/pico_explorer/weatherstation_BME280.py index d4e3ad21..3e4d67dc 100644 --- a/micropython/examples/pico_explorer/weatherstation_BME280.py +++ b/micropython/examples/pico_explorer/weatherstation_BME280.py @@ -7,68 +7,68 @@ from pimoroni_i2c import PimoroniI2C from pimoroni import PICO_EXPLORER_I2C_PINS # Pico Explorer boilerplate -import picoexplorer as display -width = display.get_width() -height = display.get_height() -display_buffer = bytearray(width * height * 2) -display.init(display_buffer) +import st7789 +display = st7789.ST7789(st7789.DISPLAY_PICO_EXPLORER, rotate=0) +display.set_palette_mode(st7789.PALETTE_USER) +display.set_backlight(1.0) i2c = PimoroniI2C(**PICO_EXPLORER_I2C_PINS) bme = BreakoutBME280(i2c) # lets set up some pen colours to make drawing easier -tempcolour = display.create_pen(255, 255, 255) # this colour will get changed in a bit -white = display.create_pen(255, 255, 255) -black = display.create_pen(0, 0, 0) -red = display.create_pen(255, 0, 0) +TEMPCOLOUR = display.reserve_palette() # this colour will get changed in a bit +WHITE = display.create_pen(255, 255, 255) +BLACK = display.create_pen(0, 0, 0) +RED = display.create_pen(255, 0, 0) +GREY = display.create_pen(125, 125, 125) # converts the temperature into a barometer-type description and pen colour def describe_temperature(temperature): - global tempcolour + global TEMPCOLOUR if temperature < 10: description = "very cold" - tempcolour = display.create_pen(0, 255, 255) + display.set_palette(TEMPCOLOUR, st7789.RGB565(0, 255, 255)) elif 10 <= temperature < 20: description = "cold" - tempcolour = display.create_pen(0, 0, 255) + display.set_palette(TEMPCOLOUR, st7789.RGB565(0, 0, 255)) elif 20 <= temperature < 25: description = "temperate" - tempcolour = display.create_pen(0, 255, 0) + display.set_palette(TEMPCOLOUR, st7789.RGB565(0, 255, 0)) elif 25 <= temperature < 30: description = "warm" - tempcolour = display.create_pen(255, 255, 0) + display.set_palette(TEMPCOLOUR, st7789.RGB565(255, 255, 0)) elif temperature >= 30: description = "very warm" - tempcolour = display.create_pen(255, 0, 0) + display.set_palette(TEMPCOLOUR, st7789.RGB565(255, 0, 0)) else: description = "" - tempcolour = display.create_pen(0, 0, 0) + display.set_palette(TEMPCOLOUR, st7789.RGB565(0, 0, 0)) return description # comment out the function above and uncomment this one for yorkshire mode """ def describe_temperature(temperature): - global tempcolour + global TEMPCOLOUR if temperature < 10: description = "frozzed" - tempcolour = display.create_pen(0, 255, 255) + display.set_palette(TEMPCOLOUR, st7789.RGB565(0, 255, 255)) elif 10 <= temperature < 20: description = "nithering" - tempcolour = display.create_pen(0, 0, 255) + display.set_palette(TEMPCOLOUR, st7789.RGB565(0, 0, 255)) elif 20 <= temperature < 25: description = "fair t' middlin" - tempcolour = display.create_pen(0, 255, 0) + display.set_palette(TEMPCOLOUR, st7789.RGB565(0, 255, 0)) elif 25 <= temperature < 30: description = "chuffing warm" - tempcolour = display.create_pen(255, 255, 0) + display.set_palette(TEMPCOLOUR, st7789.RGB565(255, 255, 0)) elif temperature >= 30: description = "crackin t' flags" - tempcolour = display.create_pen(255, 0, 0) + display.set_palette(TEMPCOLOUR, st7789.RGB565(255, 0, 0)) else: description = "" - tempcolour = display.create_pen(0, 0, 0) + display.set_palette(TEMPCOLOUR, st7789.RGB565(0, 0, 0)) return description """ @@ -100,18 +100,21 @@ def describe_humidity(humidity): while True: + display.set_pen(BLACK) + display.clear() + # read the sensors temperature, pressure, humidity = bme.read() # pressure comes in pascals which is a reight long number, lets convert it to the more manageable hPa pressurehpa = pressure / 100 # draw a thermometer/barometer thingy - display.set_pen(125, 125, 125) + display.set_pen(GREY) display.circle(190, 190, 40) display.rectangle(180, 45, 20, 140) # switch to red to draw the 'mercury' - display.set_pen(red) + display.set_pen(RED) display.circle(190, 190, 30) thermometerheight = int(120 / 30 * temperature) if thermometerheight > 120: @@ -121,11 +124,11 @@ while True: display.rectangle(186, 50 + 120 - thermometerheight, 10, thermometerheight) # drawing the temperature text - display.set_pen(white) + display.set_pen(WHITE) display.text("temperature:", 10, 10, 240, 3) - display.set_pen(tempcolour) + display.set_pen(TEMPCOLOUR) display.text('{:.1f}'.format(temperature) + 'C', 10, 30, 240, 5) - display.set_pen(white) + display.set_pen(WHITE) display.text(describe_temperature(temperature), 10, 60, 240, 3) # and the pressure text @@ -141,7 +144,5 @@ while True: # time to update the display display.update() - # waits for 1 second and clears to black + # waits for 1 second and clears to BLACK utime.sleep(1) - display.set_pen(black) - display.clear() diff --git a/micropython/examples/pico_explorer/weatherstation_BME68X.py b/micropython/examples/pico_explorer/weatherstation_BME68X.py index 5040cb2d..19535746 100644 --- a/micropython/examples/pico_explorer/weatherstation_BME68X.py +++ b/micropython/examples/pico_explorer/weatherstation_BME68X.py @@ -7,68 +7,68 @@ from pimoroni_i2c import PimoroniI2C from pimoroni import PICO_EXPLORER_I2C_PINS # Pico Explorer boilerplate -import picoexplorer as display -width = display.get_width() -height = display.get_height() -display_buffer = bytearray(width * height * 2) -display.init(display_buffer) +import st7789 +display = st7789.ST7789(st7789.DISPLAY_PICO_EXPLORER, rotate=0) +display.set_palette_mode(st7789.PALETTE_USER) +display.set_backlight(1.0) i2c = PimoroniI2C(**PICO_EXPLORER_I2C_PINS) bme = BreakoutBME68X(i2c) # lets set up some pen colours to make drawing easier -tempcolour = display.create_pen(255, 255, 255) # this colour will get changed in a bit -white = display.create_pen(255, 255, 255) -black = display.create_pen(0, 0, 0) -red = display.create_pen(255, 0, 0) +TEMPCOLOUR = display.reserve_palette() # this colour will get changed in a bit +WHITE = display.create_pen(255, 255, 255) +BLACK = display.create_pen(0, 0, 0) +RED = display.create_pen(255, 0, 0) +GREY = display.create_pen(125, 125, 125) # converts the temperature into a barometer-type description and pen colour def describe_temperature(temperature): - global tempcolour + global TEMPCOLOUR if temperature < 10: description = "very cold" - tempcolour = display.create_pen(0, 255, 255) + display.set_palette(TEMPCOLOUR, st7789.RGB565(0, 255, 255)) elif 10 <= temperature < 20: description = "cold" - tempcolour = display.create_pen(0, 0, 255) + display.set_palette(TEMPCOLOUR, st7789.RGB565(0, 0, 255)) elif 20 <= temperature < 25: description = "temperate" - tempcolour = display.create_pen(0, 255, 0) + display.set_palette(TEMPCOLOUR, st7789.RGB565(0, 255, 0)) elif 25 <= temperature < 30: description = "warm" - tempcolour = display.create_pen(255, 255, 0) + display.set_palette(TEMPCOLOUR, st7789.RGB565(255, 255, 0)) elif temperature >= 30: description = "very warm" - tempcolour = display.create_pen(255, 0, 0) + display.set_palette(TEMPCOLOUR, st7789.RGB565(255, 0, 0)) else: description = "" - tempcolour = display.create_pen(0, 0, 0) + display.set_palette(TEMPCOLOUR, st7789.RGB565(0, 0, 0)) return description # comment out the function above and uncomment this one for yorkshire mode """ def describe_temperature(temperature): - global tempcolour + global TEMPCOLOUR if temperature < 10: description = "frozzed" - tempcolour = display.create_pen(0, 255, 255) + display.set_palette(TEMPCOLOUR, st7789.RGB565(0, 255, 255)) elif 10 <= temperature < 20: description = "nithering" - tempcolour = display.create_pen(0, 0, 255) + display.set_palette(TEMPCOLOUR, st7789.RGB565(0, 0, 255)) elif 20 <= temperature < 25: description = "fair t' middlin" - tempcolour = display.create_pen(0, 255, 0) + display.set_palette(TEMPCOLOUR, st7789.RGB565(0, 255, 0)) elif 25 <= temperature < 30: description = "chuffing warm" - tempcolour = display.create_pen(255, 255, 0) + display.set_palette(TEMPCOLOUR, st7789.RGB565(255, 255, 0)) elif temperature >= 30: description = "crackin t' flags" - tempcolour = display.create_pen(255, 0, 0) + display.set_palette(TEMPCOLOUR, st7789.RGB565(255, 0, 0)) else: description = "" - tempcolour = display.create_pen(0, 0, 0) + display.set_palette(TEMPCOLOUR, st7789.RGB565(0, 0, 0)) return description """ @@ -100,6 +100,9 @@ def describe_humidity(humidity): while True: + display.set_pen(BLACK) + display.clear() + # read the sensors temperature, pressure, humidity, gas_resistance, status, gas_index, meas_index = bme.read() @@ -107,12 +110,12 @@ while True: pressurehpa = pressure / 100 # draw a thermometer/barometer thingy - display.set_pen(125, 125, 125) + display.set_pen(GREY) display.circle(190, 190, 40) display.rectangle(180, 45, 20, 140) # switch to red to draw the 'mercury' - display.set_pen(red) + display.set_pen(RED) display.circle(190, 190, 30) thermometerheight = int(120 / 30 * temperature) if thermometerheight > 120: @@ -122,11 +125,11 @@ while True: display.rectangle(186, 50 + 120 - thermometerheight, 10, thermometerheight) # drawing the temperature text - display.set_pen(white) + display.set_pen(WHITE) display.text("temperature:", 10, 10, 240, 3) - display.set_pen(tempcolour) + display.set_pen(TEMPCOLOUR) display.text('{:.1f}'.format(temperature) + 'C', 10, 30, 240, 5) - display.set_pen(white) + display.set_pen(WHITE) display.text(describe_temperature(temperature), 10, 60, 240, 3) # and the pressure text @@ -142,7 +145,5 @@ while True: # time to update the display display.update() - # waits for 1 second and clears to black + # waits for 1 second and clears to BLACK utime.sleep(1) - display.set_pen(black) - display.clear() diff --git a/micropython/examples/servo2040/current_meter.py b/micropython/examples/servo2040/current_meter.py index 5e4f4220..3082c699 100644 --- a/micropython/examples/servo2040/current_meter.py +++ b/micropython/examples/servo2040/current_meter.py @@ -52,7 +52,7 @@ led_bar.start() servos.enable_all() # Read sensors until the user button is pressed -while user_sw.raw() is not True: +while not user_sw.raw(): # Select the current sense mux.select(servo2040.CURRENT_SENSE_ADDR) diff --git a/micropython/examples/servo2040/led_rainbow.py b/micropython/examples/servo2040/led_rainbow.py index 6f4c4307..8d1da7cc 100644 --- a/micropython/examples/servo2040/led_rainbow.py +++ b/micropython/examples/servo2040/led_rainbow.py @@ -30,7 +30,7 @@ led_bar.start() offset = 0.0 # Make rainbows until the user button is pressed -while user_sw.raw() is not True: +while not user_sw.raw(): offset += SPEED / 1000.0 diff --git a/micropython/examples/servo2040/read_sensors.py b/micropython/examples/servo2040/read_sensors.py index e6c64c66..2b263578 100644 --- a/micropython/examples/servo2040/read_sensors.py +++ b/micropython/examples/servo2040/read_sensors.py @@ -30,7 +30,7 @@ user_sw = Button(servo2040.USER_SW) # Read sensors until the user button is pressed -while user_sw.raw() is not True: +while not user_sw.raw(): # Read each sensor in turn and print its voltage for i in range(len(sensor_addrs)): diff --git a/micropython/examples/servo2040/sensor_feedback.py b/micropython/examples/servo2040/sensor_feedback.py index c781cc92..d4bbd2a8 100644 --- a/micropython/examples/servo2040/sensor_feedback.py +++ b/micropython/examples/servo2040/sensor_feedback.py @@ -41,7 +41,7 @@ led_bar.start() # Read sensors until the user button is pressed -while user_sw.raw() is not True: +while not user_sw.raw(): # Read each sensor in turn and print its voltage for i in range(len(sensor_addrs)): diff --git a/micropython/examples/servo2040/servo_wave.py b/micropython/examples/servo2040/servo_wave.py index 1db21c02..7557042a 100644 --- a/micropython/examples/servo2040/servo_wave.py +++ b/micropython/examples/servo2040/servo_wave.py @@ -41,7 +41,7 @@ led_bar.start() offset = 0.0 # Make waves until the user button is pressed -while user_sw.raw() is not True: +while not user_sw.raw(): offset += SPEED / 1000.0 diff --git a/micropython/examples/servo2040/simple_easing.py b/micropython/examples/servo2040/simple_easing.py index 54647ebc..ae138b57 100644 --- a/micropython/examples/servo2040/simple_easing.py +++ b/micropython/examples/servo2040/simple_easing.py @@ -31,7 +31,7 @@ user_sw = Button(servo2040.USER_SW) update = 0 # Continually move the servo until the user button is pressed -while user_sw.raw() is not True: +while not user_sw.raw(): # Calculate how far along this movement to be percent_along = update / UPDATES_PER_MOVE diff --git a/micropython/examples/tufty2040/awesome_game.py b/micropython/examples/tufty2040/awesome_game.py new file mode 100644 index 00000000..e375d19e --- /dev/null +++ b/micropython/examples/tufty2040/awesome_game.py @@ -0,0 +1,197 @@ +import time +import random +from pimoroni import Button +from picographics import PicoGraphics, DISPLAY_TUFTY_2040 + +display = PicoGraphics(display=DISPLAY_TUFTY_2040) +WIDTH, HEIGHT = display.get_bounds() + +# Load the spritsheets so we can flip between them +tilemap = bytearray(128 * 128) +open("s4m_ur4i-pirate-tilemap.rgb332", "rb").readinto(tilemap) + +character = bytearray(128 * 128) +open("s4m_ur4i-pirate-characters.rgb332", "rb").readinto(character) + +display.set_spritesheet(character) + + +# Buttons +button_a = Button(7, invert=False) +button_b = Button(8, invert=False) +button_c = Button(9, invert=False) +button_up = Button(22, invert=False) +button_down = Button(6, invert=False) + +display.set_backlight(1.0) +display.set_pen(255) +display.clear() + + +class Player(): + def __init__(self): + self.reset() + + def reset(self): + self.x = 150 + self.y = 180 + self.w = 15 + self.h = 30 + self.speed = 10 + self.is_alive = True + self.lives = 3 + self.score = 0 + self.moving = 0 + + def move(self, x, y): + if self.x + x > 0 - self.w and self.x + x < WIDTH - self.w: + self.x += x + self.y += y + + def sprite(self): + display.set_spritesheet(character) + display.sprite(1, 1 if self.moving else 0, self.x, self.y, 4, 0) + + +class Treasure(): + def __init__(self): + self.w = 16 + self.h = 16 + self.randomize() + + def sprite(self): + if not self.enabled: + return + display.set_spritesheet(tilemap) + display.sprite(4, 2, self.x, self.y, 3, 0) + + def randomize(self): + self.enabled = True + self.x = random.randint(15, WIDTH - 60) + self.y = HEIGHT - 50 + + +class Block(): + def __init__(self): + self.w = 16 + self.h = 16 + self.is_alive = True + self.randomize() + + def move(self): + self.y += self.speed + + def sprite(self): + display.set_spritesheet(character) + display.sprite(10, 8, self.x, self.y, 4, 0) + + def randomize(self): + self.last_update = time.time() + self.x = random.randint(10, WIDTH - self.w - 10) + self.y = -self.h + self.speed = random.randint(4, 12) + + +class Game(): + def __init__(self): + self.player = Player() + self.block = [] + self.last_new_block = 0 + + self.treasure = Treasure() + self.last_treasure = 0 + + self.SKY = display.create_pen(72, 180, 224) + + for i in range(5): + self.block.append(Block()) + + def reset(self): + for block in self.block: + block.randomize() + + self.treasure.randomize() + self.player.reset() + + def get_input(self): + if button_c.read(): + self.player.move(self.player.speed, 0) + self.player.moving = 0 + if button_a.read(): + self.player.move(-self.player.speed, 0) + self.player.moving = 1 + + def background(self): + display.set_spritesheet(tilemap) + display.set_pen(self.SKY) + display.clear() + + for i in range(WIDTH / 32): + display.sprite(1, 2, i * 32, 210, 4, 0) + + def draw(self): + self.background() + for block in self.block: + block.sprite() + display.set_pen(255) + display.text("Score: " + str(self.player.score), 10, 10, 320, 2) + self.treasure.sprite() + display.set_pen(0) + self.player.sprite() + display.update() + time.sleep(0.01) + + def check_collision(self, a, b): + return a.x + a.w >= b.x and a.x <= b.x + b.w and a.y + a.h >= b.y and a.y <= b.y + b.h + + def update(self): + for block in self.block: + block.move() + if block.y > HEIGHT: + block.randomize() + + if block.y + block.h >= self.player.y and self.check_collision(self.player, block): + block.randomize() + self.player.is_alive = False + + if self.treasure.enabled: + if self.check_collision(self.player, self.treasure): + self.player.score += 1 + self.treasure.enabled = False + self.last_treasure = time.time() + + if time.time() - self.last_treasure > 2: + if not self.treasure.enabled: + self.treasure.randomize() + + if self.player.lives == 0: + self.player.is_alive = False + + +game = Game() + +while True: + game.background() + display.set_pen(255) + display.text("ARGH!", 40, 35, 200, 10) + display.text("Press B to Start", 80, 150, 180, 2) + display.update() + + while not button_b.read(): + pass + + while game.player.is_alive: + game.get_input() + game.update() + game.draw() + + game.background() + display.set_pen(255) + display.text("OOPS!", 40, 35, 200, 10) + display.text("Your score: " + str(game.player.score), 50, 150, 180, 2) + display.update() + + while not button_b.read(): + pass + + game.reset() diff --git a/micropython/examples/tufty2040/battery.py b/micropython/examples/tufty2040/battery.py new file mode 100644 index 00000000..c881c262 --- /dev/null +++ b/micropython/examples/tufty2040/battery.py @@ -0,0 +1,81 @@ +# This example reads the voltage from a battery connected to Tufty 2040 +# and uses this reading to calculate how much charge is left in the battery. + +from picographics import PicoGraphics, DISPLAY_TUFTY_2040 +from machine import ADC, Pin +import time + +display = PicoGraphics(display=DISPLAY_TUFTY_2040) + +display.set_backlight(0.8) + +# set up the ADCs for measuring battery voltage +vbat_adc = ADC(29) +vref_adc = ADC(28) +vref_en = Pin(27) +vref_en.init(Pin.OUT) +vref_en.value(0) +usb_power = Pin(24, Pin.IN) # reading GP24 tells us whether or not USB power is connected + +# Reference voltages for a full/empty battery, in volts +# the values could vary by battery size/manufacturer so you might need to adjust them +# Values for a Galleon 400mAh LiPo: +full_battery = 3.7 +empty_battery = 2.5 + +# set up some colours to draw with +WHITE = display.create_pen(255, 255, 255) +BLACK = display.create_pen(0, 0, 0) +GREY = display.create_pen(190, 190, 190) +GREEN = display.create_pen(0, 255, 0) +RED = display.create_pen(255, 0, 0) + +display.set_font("bitmap8") + +while True: + # The voltage reference on Tufty means we can measure battery voltage precisely, even when batteries are low. + # Enable the onboard voltage reference + vref_en.value(1) + + # Calculate the logic supply voltage, as will be lower that the usual 3.3V when running off low batteries + vdd = 1.24 * (65535 / vref_adc.read_u16()) + vbat = ( + (vbat_adc.read_u16() / 65535) * 3 * vdd + ) # 3 in this is a gain, not rounding of 3.3V + + # Disable the onboard voltage reference + vref_en.value(0) + + # Print out the voltage + print("Battery Voltage = ", vbat, "V", sep="") + + # convert the raw ADC read into a voltage, and then a percentage + percentage = 100 * ((vbat - empty_battery) / (full_battery - empty_battery)) + if percentage > 100: + percentage = 100 + if percentage < 0: + percentage = 0 + + # draw the battery outline + display.set_pen(BLACK) + display.clear() + display.set_pen(GREY) + display.rectangle(0, 0, 220, 135) + display.rectangle(220, 40, 20, 55) + display.set_pen(WHITE) + display.rectangle(3, 3, 214, 129) + + # draw a green box for the battery level + display.set_pen(GREEN) + display.rectangle(5, 5, int((210 / 100) * percentage), 125) + + # add text + display.set_pen(RED) + if usb_power.value() == 1: # if it's plugged into USB power... + display.text("USB power!", 15, 90, 240, 4) + + display.text('{:.2f}'.format(vbat) + "v", 15, 10, 240, 5) + display.text('{:.0f}%'.format(percentage), 15, 50, 240, 5) + + display.update() + time.sleep(0.5) diff --git a/micropython/examples/tufty2040/button_test.py b/micropython/examples/tufty2040/button_test.py new file mode 100644 index 00000000..e17ef0cc --- /dev/null +++ b/micropython/examples/tufty2040/button_test.py @@ -0,0 +1,76 @@ +# This example shows you a simple, non-interrupt way of reading Tufty 2040's buttons with a loop that checks to see if buttons are pressed. + +import time +from pimoroni import Button +from picographics import PicoGraphics, DISPLAY_TUFTY_2040 + +display = PicoGraphics(display=DISPLAY_TUFTY_2040) + +display.set_backlight(1.0) +display.set_font("bitmap8") + +button_a = Button(7, invert=False) +button_b = Button(8, invert=False) +button_c = Button(9, invert=False) +button_up = Button(22, invert=False) +button_down = Button(6, invert=False) + +WHITE = display.create_pen(255, 255, 255) +BLACK = display.create_pen(0, 0, 0) +TEAL = display.create_pen(0, 255, 255) +MAGENTA = display.create_pen(255, 0, 255) +YELLOW = display.create_pen(255, 255, 0) +RED = display.create_pen(255, 0, 0) +GREEN = display.create_pen(0, 255, 0) + +WIDTH, HEIGHT = display.get_bounds() + +while True: + if button_a.is_pressed: # if a button press is detected then... + display.set_pen(BLACK) # set pen to black + display.clear() # clear display to the pen colour + display.set_pen(WHITE) # change the pen colour + display.text("Button A pressed", 10, 10, WIDTH - 10, 3) # display some text on the screen + display.update() # update the display + time.sleep(1) # pause for a sec + + elif button_b.is_pressed: + display.set_pen(BLACK) + display.clear() + display.set_pen(TEAL) + display.text("Button B pressed", 10, 10, WIDTH - 10, 3) + display.update() + time.sleep(1) + + elif button_c.is_pressed: + display.set_pen(BLACK) + display.clear() + display.set_pen(MAGENTA) + display.text("Button C pressed", 10, 10, WIDTH - 10, 3) + display.update() + time.sleep(1) + + elif button_up.is_pressed: + display.set_pen(BLACK) + display.clear() + display.set_pen(YELLOW) + display.text("Button up pressed", 10, 10, WIDTH - 10, 3) + display.update() + time.sleep(1) + + elif button_down.is_pressed: + display.set_pen(BLACK) + display.clear() + display.set_pen(GREEN) + display.text("Button down pressed", 10, 10, WIDTH - 10, 3) + display.update() + time.sleep(1) + + else: + display.set_pen(BLACK) + display.clear() + display.set_pen(RED) + display.text("Press any button!", 10, 10, WIDTH, 3) + display.update() + + time.sleep(0.1) # this number is how frequently Tufty checks for button presses diff --git a/micropython/examples/tufty2040/clock.py b/micropython/examples/tufty2040/clock.py new file mode 100644 index 00000000..047bb89d --- /dev/null +++ b/micropython/examples/tufty2040/clock.py @@ -0,0 +1,151 @@ +import time +import machine +from picographics import PicoGraphics, DISPLAY_TUFTY_2040 + +rtc = machine.RTC() +display = PicoGraphics(display=DISPLAY_TUFTY_2040) + +# Tufty constants. +A = 7 +B = 8 +C = 15 +UP = 22 +DOWN = 6 +LED = 25 + +WIDTH, HEIGHT = display.get_bounds() +display.set_backlight(1.0) + +# Buttons +button_a = machine.Pin(A, machine.Pin.IN) +button_b = machine.Pin(B, machine.Pin.IN) +button_c = machine.Pin(C, machine.Pin.IN) +button_up = machine.Pin(UP, machine.Pin.IN) +button_down = machine.Pin(DOWN, machine.Pin.IN) + +WHITE = display.create_pen(255, 255, 255) +BLACK = display.create_pen(0, 0, 0) + +PINK = display.create_pen(214, 28, 78) +ORANGE_1 = display.create_pen(247, 126, 33) +ORANGE_2 = display.create_pen(250, 194, 19) + +cursors = ["hour", "minute"] +set_clock = False +cursor = 0 +last = 0 + + +def days_in_month(month, year): + if month == 2 and ((year % 4 == 0 and year % 100 != 0) or year % 400 == 0): + return 29 + return (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)[month - 1] + + +# Button handling function +def button(pin): + global last, set_clock, cursor, year, month, day, hour, minute + + time.sleep(0.01) + if not pin.value(): + return + + if button_a.value() and button_c.value(): + machine.reset() + + adjust = 0 + changed = False + + if pin == button_b: + set_clock = not set_clock + changed = True + if not set_clock: + rtc.datetime((year, month, day, 0, hour, minute, second, 0)) + + if set_clock: + if pin == button_c: + cursor += 1 + cursor %= len(cursors) + + if pin == button_a: + cursor -= 1 + cursor %= len(cursors) + + if pin == button_up: + adjust = 1 + + if pin == button_down: + adjust = -1 + + if cursors[cursor] == "hour": + hour += adjust + hour %= 24 + if cursors[cursor] == "minute": + minute += adjust + minute %= 60 + + if set_clock or changed: + draw_clock() + + +# Register the button handling function with the buttons +button_down.irq(trigger=machine.Pin.IRQ_RISING, handler=button) +button_up.irq(trigger=machine.Pin.IRQ_RISING, handler=button) +button_a.irq(trigger=machine.Pin.IRQ_RISING, handler=button) +button_b.irq(trigger=machine.Pin.IRQ_RISING, handler=button) +button_c.irq(trigger=machine.Pin.IRQ_RISING, handler=button) + + +def draw_clock(): + + display.set_pen(WHITE) + display.clear() + + hr = "{:02}".format(hour) + min = "{:02}".format(minute) + sec = "{:02}".format(second) + + hr_width = display.measure_text(hr, 1) + hr_offset = 15 + + minute_width = display.measure_text(min, 1) + minute_offset = 15 + + second_width = display.measure_text(sec, 1) + second_offset = 5 + + display.set_pen(PINK) + display.rectangle(10, 10, (hour * 13), 60) + display.set_pen(ORANGE_1) + display.rectangle(10, 85, (minute * 5), 60) + display.set_pen(ORANGE_2) + display.rectangle(10, 160, (second * 5), 60) + + display.set_pen(WHITE) + display.text(hr, (hour * 13) - hr_width - hr_offset, 45, 10, 3) + display.text(min, (minute * 5) - minute_width - minute_offset, 120, 10, 3) + display.text(sec, (second * 5) - second_width - second_offset, 202, 10, 2) + + display.set_pen(BLACK) + + if set_clock: + + if cursors[cursor] == "hour": + display.line(5, 10, 5, 70) + if cursors[cursor] == "minute": + display.line(5, 85, 5, 145) + + display.update() + + +year, month, day, wd, hour, minute, second, _ = rtc.datetime() + +last_second = second + +while True: + if not set_clock: + year, month, day, wd, hour, minute, second, _ = rtc.datetime() + if second != last_second: + draw_clock() + last_second = second + time.sleep(0.01) diff --git a/micropython/examples/tufty2040/main.py b/micropython/examples/tufty2040/main.py new file mode 100644 index 00000000..f8c56e19 --- /dev/null +++ b/micropython/examples/tufty2040/main.py @@ -0,0 +1,151 @@ +from picographics import PicoGraphics, DISPLAY_TUFTY_2040, PEN_RGB332 +from os import listdir +import time +import gc +from pimoroni import Button + +display = PicoGraphics(display=DISPLAY_TUFTY_2040, pen_type=PEN_RGB332, rotate=180) + + +def hsv_to_rgb(h, s, v): + if s == 0.0: + return v, v, v + i = int(h * 6.0) + f = (h * 6.0) - i + p = v * (1.0 - s) + q = v * (1.0 - s * f) + t = v * (1.0 - s * (1.0 - f)) + v = int(v * 255) + t = int(t * 255) + p = int(p * 255) + q = int(q * 255) + i = i % 6 + if i == 0: + return v, t, p + if i == 1: + return q, v, p + if i == 2: + return p, v, t + if i == 3: + return p, q, v + if i == 4: + return t, p, v + if i == 5: + return v, p, q + + +def get_applications(): + # fetch a list of the applications that are stored in the filesystem + applications = [] + for file in listdir(): + if file.endswith(".py") and file != "main.py": + # convert the filename from "something_or_other.py" to "Something Or Other" + # via weird incantations and a sprinkling of voodoo + title = " ".join([v[:1].upper() + v[1:] for v in file[:-3].split("_")]) + + applications.append( + { + "file": file, + "title": title + } + ) + + # sort the application list alphabetically by title and return the list + return sorted(applications, key=lambda x: x["title"]) + + +def launch_application(application): + for k in locals().keys(): + if k not in ("gc", "file", "badger_os"): + del locals()[k] + + gc.collect() + + __import__(application["file"]) + + +applications = get_applications() + +button_up = Button(22, invert=False) +button_down = Button(6, invert=False) +button_a = Button(7, invert=False) + +display.set_backlight(1.0) + +WHITE = display.create_pen(255, 255, 255) +BLACK = display.create_pen(0, 0, 0) +RED = display.create_pen(200, 0, 0) + + +def text(text, x, y, pen, s): + display.set_pen(pen) + display.text(text, x, y, -1, s) + + +selected_item = 2 +scroll_position = 2 +target_scroll_position = 2 + +selected_pen = display.create_pen(255, 255, 255) +unselected_pen = display.create_pen(80, 80, 100) +background_pen = display.create_pen(50, 50, 70) +shadow_pen = display.create_pen(0, 0, 0) + +while True: + t = time.ticks_ms() / 1000.0 + + if button_up.read(): + target_scroll_position -= 1 + target_scroll_position = target_scroll_position if target_scroll_position >= 0 else len(applications) - 1 + + if button_down.read(): + target_scroll_position += 1 + target_scroll_position = target_scroll_position if target_scroll_position < len(applications) else 0 + + if button_a.read(): + launch_application(applications[selected_item]) + + display.set_pen(background_pen) + display.clear() + + scroll_position += (target_scroll_position - scroll_position) / 5 + + grid_size = 40 + for y in range(0, 240 / grid_size): + for x in range(0, 320 / grid_size): + h = x + y + int(t * 5) + h = h / 50.0 + r, g, b = hsv_to_rgb(h, .5, 1) + + display.set_pen(display.create_pen(r, g, b)) + display.rectangle(x * grid_size, y * grid_size, grid_size, grid_size) + + # work out which item is selected (closest to the current scroll position) + selected_item = round(target_scroll_position) + + start = time.ticks_ms() + + for list_index, application in enumerate(applications): + distance = list_index - scroll_position + + text_size = 4 if selected_item == list_index else 3 + + # center text horixontally + title_width = display.measure_text(application["title"], text_size) + text_x = int(160 - title_width / 2) + + row_height = text_size * 5 + 20 + + # center list items vertically + text_y = int(120 + distance * row_height - (row_height / 2)) + + # draw the text, selected item brightest and with shadow + if selected_item == list_index: + text(application["title"], text_x + 1, text_y + 1, shadow_pen, text_size) + + text_pen = selected_pen if selected_item == list_index else unselected_pen + text(application["title"], text_x, text_y, text_pen, text_size) + + start = time.ticks_ms() + + display.update() diff --git a/micropython/examples/tufty2040/name_badge.py b/micropython/examples/tufty2040/name_badge.py new file mode 100644 index 00000000..cc0c655f --- /dev/null +++ b/micropython/examples/tufty2040/name_badge.py @@ -0,0 +1,68 @@ +from picographics import PicoGraphics, DISPLAY_TUFTY_2040 + +display = PicoGraphics(display=DISPLAY_TUFTY_2040) +WIDTH, HEIGHT = display.get_bounds() +display.set_backlight(1.0) + +# Tufty constants +A = 7 +B = 8 +C = 15 +UP = 22 +DOWN = 6 +LED = 25 + +WHITE = display.create_pen(255, 255, 255) +BLACK = display.create_pen(0, 0, 0) +RED = display.create_pen(200, 0, 0) + +# Read name from file +try: + file = open("badge.txt", "r") + name = file.readline() + file.close() +except OSError: + name = "open name.txt in thonny to edit badge :)" + + +text_size = 12 +text_x = 0 +text_y = 100 + +# Clear the screen +display.set_pen(WHITE) +display.clear() +display.update() + + +# Draws a blank badge +def draw_badge(): + display.set_pen(RED) + display.rectangle(0, 0, WIDTH, 60) + display.rectangle(0, HEIGHT - 20, WIDTH, 50) + display.set_pen(WHITE) + display.text("HELLO", 125, 5, 0, 3) + display.text("My name is:", 110, 35, 320, 2) + display.update() + + +def calculate_text_size(): + global text_size + name_width = display.measure_text(name, text_size) + # Calculate the width of the text in pixels, adjusts according to the screen width + while name_width > 290: + text_size -= 1 + name_width = display.measure_text(name, text_size) + + # Calculate the margin to be applied on X + margin_x = (WIDTH - name_width) / 2 + + return int(margin_x) + + +draw_badge() +text_x = calculate_text_size() + +display.set_pen(BLACK) +display.text(name, text_x, text_y, 300, text_size) +display.update() diff --git a/micropython/examples/tufty2040/pride_badge.py b/micropython/examples/tufty2040/pride_badge.py new file mode 100644 index 00000000..b50237fa --- /dev/null +++ b/micropython/examples/tufty2040/pride_badge.py @@ -0,0 +1,90 @@ +# A name badge with customisable Pride flag background. + +from picographics import PicoGraphics, DISPLAY_TUFTY_2040 + +display = PicoGraphics(display=DISPLAY_TUFTY_2040) + +WIDTH, HEIGHT = display.get_bounds() + +# List of available pen colours, add more if necessary +RED = display.create_pen(209, 34, 41) +ORANGE = display.create_pen(246, 138, 30) +YELLOW = display.create_pen(255, 216, 0) +GREEN = display.create_pen(0, 121, 64) +INDIGO = display.create_pen(36, 64, 142) +VIOLET = display.create_pen(115, 41, 130) +WHITE = display.create_pen(255, 255, 255) +PINK = display.create_pen(255, 175, 200) +BLUE = display.create_pen(116, 215, 238) +BROWN = display.create_pen(97, 57, 21) +BLACK = display.create_pen(0, 0, 0) +MAGENTA = display.create_pen(255, 33, 140) +CYAN = display.create_pen(33, 177, 255) + +# Uncomment one of these to change flag +# If adding your own, colour order is left to right (or top to bottom) +COLOUR_ORDER = [RED, ORANGE, YELLOW, GREEN, INDIGO, VIOLET] # traditional pride flag +# COLOUR_ORDER = [BLACK, BROWN, RED, ORANGE, YELLOW, GREEN, INDIGO, VIOLET] # Philadelphia pride flag +# COLOUR_ORDER = [BLUE, PINK, WHITE, PINK, BLUE] # trans flag +# COLOUR_ORDER = [MAGENTA, YELLOW, CYAN] # pan flag +# COLOUR_ORDER = [MAGENTA, VIOLET, INDIGO] # bi flag + +# Change this for vertical stripes +STRIPES_DIRECTION = "horizontal" + +# Change details here! Works best with a short, one word name +NAME = "Hel" +PRONOUNS = "She/they" + +# Change the colour of the text (swapping these works better on a light background) +TEXT_COLOUR = WHITE +DROP_SHADOW_COLOUR = BLACK + +# Draw the flag +if STRIPES_DIRECTION == "horizontal": + stripe_width = round(HEIGHT / len(COLOUR_ORDER)) + for x in range(len(COLOUR_ORDER)): + display.set_pen(COLOUR_ORDER[x]) + display.rectangle(0, stripe_width * x, WIDTH, stripe_width) + +if STRIPES_DIRECTION == "vertical": + stripe_width = round(WIDTH / len(COLOUR_ORDER)) + for x in range(len(COLOUR_ORDER)): + display.set_pen(COLOUR_ORDER[x]) + display.rectangle(stripe_width * x, 0, stripe_width, HEIGHT) + +# Set a starting scale for text size. +# This is intentionally bigger than will fit on the screen, we'll shrink it to fit. +name_size = 20 +pronouns_size = 20 + +# These loops adjust the scale of the text until it fits on the screen +while True: + display.set_font("bitmap8") + name_length = display.measure_text(NAME, name_size) + if name_length >= WIDTH - 20: + name_size -= 1 + else: + # comment out this section if you hate drop shadow + DROP_SHADOW_OFFSET = 5 + display.set_pen(DROP_SHADOW_COLOUR) + display.text(NAME, int((WIDTH - name_length) / 2 + 10) - DROP_SHADOW_OFFSET, 10 + DROP_SHADOW_OFFSET, WIDTH, name_size) + + # draw name and stop looping + display.set_pen(TEXT_COLOUR) + display.text(NAME, int((WIDTH - name_length) / 2 + 10), 10, WIDTH, name_size) + break + +while True: + display.set_font("bitmap8") + pronouns_length = display.measure_text(PRONOUNS, pronouns_size) + if pronouns_length >= WIDTH - 60: + pronouns_size -= 1 + else: + # draw pronouns and stop looping + display.set_pen(TEXT_COLOUR) + display.text(PRONOUNS, int((WIDTH - pronouns_length) / 2), 175, WIDTH, pronouns_size) + break + +# Once all the adjusting and drawing is done, update the display. +display.update() diff --git a/micropython/examples/tufty2040/retro_badge.py b/micropython/examples/tufty2040/retro_badge.py new file mode 100644 index 00000000..5f3ff12f --- /dev/null +++ b/micropython/examples/tufty2040/retro_badge.py @@ -0,0 +1,152 @@ +# A retro badge with photo and QR code. +# Copy your image to your Tufty alongside this example - it should be a 120 x 120 jpg. + +from picographics import PicoGraphics, DISPLAY_TUFTY_2040 +from pimoroni import Button +import time +import jpegdec +import qrcode + +display = PicoGraphics(display=DISPLAY_TUFTY_2040) +button_c = Button(9, invert=False) + +WIDTH, HEIGHT = display.get_bounds() + +# Uncomment one of these four colour palettes - find more at lospec.com ! +# Nostalgia colour palette by WildLeoKnight - https://lospec.com/palette-list/nostalgia +LIGHTEST = display.create_pen(208, 208, 88) +LIGHT = display.create_pen(160, 168, 64) +DARK = display.create_pen(112, 128, 40) +DARKEST = display.create_pen(64, 80, 16) + +# 2bit Demichrome colour palette by Space Sandwich - https://lospec.com/palette-list/2bit-demichrome +# LIGHTEST = display.create_pen(233, 239, 236) +# LIGHT = display.create_pen(160, 160, 139) +# DARK = display.create_pen(85, 85, 104) +# DARKEST = display.create_pen(33, 30, 32) + +# CGA PALETTE 1 (HIGH) - https://lospec.com/palette-list/cga-palette-1-high +# LIGHTEST = display.create_pen(255, 255, 255) +# LIGHT = display.create_pen(85, 254, 255) +# DARK = display.create_pen(255, 85, 255) +# DARKEST = display.create_pen(0, 0, 0) + +# Change your badge and QR details here! +COMPANY_NAME = "sciurus cybernetics" +NAME = "M. 'TuFTy'" +BLURB1 = "RP2040 plus 320x240 TFT LCD" +BLURB2 = "Nuts From Trees collector" +BLURB3 = "Will work for peanuts" + +QR_TEXT = "pimoroni.com/tufty2040" + +IMAGE_NAME = "squirrel.jpg" + +# Some constants we'll use for drawing +BORDER_SIZE = 4 +PADDING = 10 +COMPANY_HEIGHT = 40 + + +def draw_badge(): + # draw border + display.set_pen(LIGHTEST) + display.clear() + + # draw background + display.set_pen(DARK) + display.rectangle(BORDER_SIZE, BORDER_SIZE, WIDTH - (BORDER_SIZE * 2), HEIGHT - (BORDER_SIZE * 2)) + + # draw company box + display.set_pen(DARKEST) + display.rectangle(BORDER_SIZE, BORDER_SIZE, WIDTH - (BORDER_SIZE * 2), COMPANY_HEIGHT) + + # draw company text + display.set_pen(LIGHT) + display.set_font("bitmap6") + display.text(COMPANY_NAME, BORDER_SIZE + PADDING, BORDER_SIZE + PADDING, WIDTH, 3) + + # draw name text + display.set_pen(LIGHTEST) + display.set_font("bitmap8") + display.text(NAME, BORDER_SIZE + PADDING, BORDER_SIZE + PADDING + COMPANY_HEIGHT, WIDTH, 5) + + # draws the bullet points + display.set_pen(DARKEST) + display.text("*", BORDER_SIZE + PADDING + 120 + PADDING, 105, 160, 2) + display.text("*", BORDER_SIZE + PADDING + 120 + PADDING, 140, 160, 2) + display.text("*", BORDER_SIZE + PADDING + 120 + PADDING, 175, 160, 2) + + # draws the blurb text (4 - 5 words on each line works best) + display.set_pen(LIGHTEST) + display.text(BLURB1, BORDER_SIZE + PADDING + 135 + PADDING, 105, 160, 2) + display.text(BLURB2, BORDER_SIZE + PADDING + 135 + PADDING, 140, 160, 2) + display.text(BLURB3, BORDER_SIZE + PADDING + 135 + PADDING, 175, 160, 2) + + +def show_photo(): + j = jpegdec.JPEG(display) + + # Open the JPEG file + j.open_file(IMAGE_NAME) + + # Draws a box around the image + display.set_pen(DARKEST) + display.rectangle(PADDING, HEIGHT - ((BORDER_SIZE * 2) + PADDING) - 120, 120 + (BORDER_SIZE * 2), 120 + (BORDER_SIZE * 2)) + + # Decode the JPEG + j.decode(BORDER_SIZE + PADDING, HEIGHT - (BORDER_SIZE + PADDING) - 120) + + # Draw QR button label + display.set_pen(LIGHTEST) + display.text("QR", 240, 215, 160, 2) + + +def measure_qr_code(size, code): + w, h = code.get_size() + module_size = int(size / w) + return module_size * w, module_size + + +def draw_qr_code(ox, oy, size, code): + size, module_size = measure_qr_code(size, code) + display.set_pen(LIGHTEST) + display.rectangle(ox, oy, size, size) + display.set_pen(DARKEST) + for x in range(size): + for y in range(size): + if code.get_module(x, y): + display.rectangle(ox + x * module_size, oy + y * module_size, module_size, module_size) + + +def show_qr(): + display.set_pen(DARK) + display.clear() + + code = qrcode.QRCode() + code.set_text(QR_TEXT) + + size, module_size = measure_qr_code(HEIGHT, code) + left = int((WIDTH // 2) - (size // 2)) + top = int((HEIGHT // 2) - (size // 2)) + draw_qr_code(left, top, HEIGHT, code) + + +# draw the badge for the first time +badge_mode = "photo" +draw_badge() +show_photo() +display.update() + +while True: + if button_c.is_pressed: + if badge_mode == "photo": + badge_mode = "qr" + show_qr() + display.update() + else: + badge_mode = "photo" + draw_badge() + show_photo() + display.update() + time.sleep(1) diff --git a/micropython/examples/tufty2040/s4m_ur4i-pirate-characters.rgb332 b/micropython/examples/tufty2040/s4m_ur4i-pirate-characters.rgb332 new file mode 100644 index 00000000..5c93e9ba Binary files /dev/null and b/micropython/examples/tufty2040/s4m_ur4i-pirate-characters.rgb332 differ diff --git a/micropython/examples/tufty2040/s4m_ur4i-pirate-tilemap.rgb332 b/micropython/examples/tufty2040/s4m_ur4i-pirate-tilemap.rgb332 new file mode 100644 index 00000000..1fabfa95 Binary files /dev/null and b/micropython/examples/tufty2040/s4m_ur4i-pirate-tilemap.rgb332 differ diff --git a/micropython/examples/tufty2040/sketchy_sketch.py b/micropython/examples/tufty2040/sketchy_sketch.py new file mode 100644 index 00000000..18398aa2 --- /dev/null +++ b/micropython/examples/tufty2040/sketchy_sketch.py @@ -0,0 +1,83 @@ +import time +from pimoroni import Button +from picographics import PicoGraphics, DISPLAY_TUFTY_2040, PEN_RGB332 + +display = PicoGraphics(display=DISPLAY_TUFTY_2040, pen_type=PEN_RGB332, rotate=180) + +# Tufty constants +A = 7 +B = 8 +C = 15 +UP = 22 +DOWN = 6 +LED = 25 + +WIDTH, HEIGHT = 320, 240 +display.set_backlight(1.0) + +# Buttons +button_a = Button(7, invert=False) +button_b = Button(8, invert=False) +button_c = Button(9, invert=False) +button_up = Button(22, invert=False) +button_down = Button(6, invert=False) + + +def draw_area(): + display.set_pen(display.create_pen(200, 0, 0)) + display.clear() + + display.set_pen(display.create_pen(255, 215, 0)) + display.text("Sketchy-Sketch", 90, 5, 0, 2) + + # draw main surface + display.set_pen(display.create_pen(205, 205, 205)) + display.rectangle(25, 25, 270, 180) + + # draw knobs + display.set_pen(display.create_pen(150, 50, 50)) + display.circle(25 + 5, 225 + 5, 20) + display.circle(295 + 5, 225 + 5, 20) + display.set_pen(display.create_pen(255, 255, 255)) + display.circle(25 - 1, 225 - 1, 16) + display.circle(295 - 1, 225 - 1, 16) + display.set_pen(display.create_pen(155, 155, 155)) + display.circle(25 + 1, 225 + 1, 16) + display.circle(295 + 1, 225 + 1, 16) + display.set_pen(display.create_pen(205, 205, 205)) + display.circle(25, 225, 15) + display.circle(295, 225, 15) + + +# start position for drawing cursor +position_x = 160 +position_y = 110 + +# draw the sketchy sketch +draw_area() + +while True: + # check for user input and update cursor position as needed + if button_c.raw() and position_x < 290: + position_x += 1 + + if button_a.raw() and position_x > 30: + position_x -= 1 + + if button_up.raw() and position_y > 30: + position_y -= 1 + + if button_down.raw() and position_y < 200: + position_y += 1 + + if button_b.raw(): + draw_area() + + # draw the line + display.set_pen(display.create_pen(50, 50, 50)) + display.circle(position_x, position_y, 2) + + # update the screen contents + display.update() + + time.sleep(0.005) diff --git a/micropython/examples/tufty2040/squirrel.jpg b/micropython/examples/tufty2040/squirrel.jpg new file mode 100644 index 00000000..8d1b2c9e Binary files /dev/null and b/micropython/examples/tufty2040/squirrel.jpg differ diff --git a/micropython/examples/tufty2040/tufty2040_solarized.py b/micropython/examples/tufty2040/tufty2040_solarized.py new file mode 100644 index 00000000..3c97d9b7 --- /dev/null +++ b/micropython/examples/tufty2040/tufty2040_solarized.py @@ -0,0 +1,119 @@ +from picographics import PicoGraphics, DISPLAY_TUFTY_2040, PEN_P4 +from pimoroni import Button +import math +import time + +""" +Tufty2040: Solarized + +Sets up Tufty 2040 in 4-bit colour mode and fills the +16-bit palette with some nice Solarized colours. + +See: https://ethanschoonover.com/solarized/ +""" + + +# Background & Content Tones +BASE_03 = 0 +BASE_02 = 1 +BASE_01 = 2 +BASE_00 = 3 +BASE_0 = 4 +BASE_1 = 5 +BASE_2 = 6 +BASE_3 = 7 + +# Accent Colours +YELLOW = 8 +ORANGE = 9 +RED = 10 +MAGENTA = 11 +VIOLET = 12 +BLUE = 13 +CYAN = 14 +GREEN = 15 + +# Friendly names for light/dark backgrounds +BG_DARK = BASE_03 +BG_LIGHT = BASE_2 + + +def bargraph(ox, oy, w, h, value, colors, pips=32): + step = int(pips / len(colors)) + filled = int(value * pips) + pip_width = int(w / pips) + for i in range(pips): + p = colors[int(i / step)] + lcd.set_pen(p) + x = i * pip_width + if i > filled: + lcd.rectangle(ox + x, oy + h - 1, pip_width - 2, 1) + else: + lcd.rectangle(ox + x, oy, pip_width - 2, h) + + +lcd = PicoGraphics(DISPLAY_TUFTY_2040, pen_type=PEN_P4) + +lcd.set_backlight(0.8) +lcd.set_font("bitmap8") + +button_up = Button(22, invert=False) +button_down = Button(6, invert=False) + + +# Prime the palette with Solarized 16-colours! +lcd.set_palette([ + (0x00, 0x2b, 0x36), # Base 03 + (0x07, 0x36, 0x42), # Base 02 + (0x58, 0x6e, 0x75), # base 01 + (0x65, 0x7b, 0x83), # Base 00 + (0x83, 0x94, 0x96), # Base 0 + (0x93, 0xa1, 0xa1), # Base 1 + (0xee, 0xe8, 0xd5), # Base 2 + (0xfd, 0xf6, 0xe3), # Base 3 + (0xb5, 0x89, 0x00), # Yellow + (0xcb, 0x4b, 0x16), # Orange + (0xdc, 0x32, 0x2f), # Red + (0xd3, 0x36, 0x82), # Magenta + (0x6c, 0x71, 0xc4), # Violet + (0x26, 0x8b, 0xd2), # Blue + (0x2a, 0xa1, 0x98), # Cyan + (0x85, 0x99, 0x00), # Green +]) + +dark_mode = True + + +while True: + if button_down.is_pressed: + dark_mode = True + if button_up.is_pressed: + dark_mode = False + + lcd.set_pen(BG_DARK if dark_mode else BG_LIGHT) + lcd.clear() + + texts = [ + "Vær sÃ¥ god", + "Testing. Testing. 123.", + "Helló Világ", + "Comment ça va", + "meine Straße hinauf", + "It's a spicy 25°C", + "What do you think?", + "éphéméréité" + ] + + for i, pen in enumerate([YELLOW, ORANGE, RED, MAGENTA, VIOLET, BLUE, CYAN, GREEN]): + lcd.set_pen(pen) + lcd.text(texts[i], 10, i * 22 + 10, 320) + + v1 = (math.sin(time.ticks_ms() / 400.0 / math.pi) + 1.0) / 2.0 + v2 = (math.sin(time.ticks_ms() / 650.0 / math.pi) + 1.0) / 2.0 + v3 = (math.sin(time.ticks_ms() / 900.0 / math.pi) + 1.0) / 2.0 + + bargraph(10, 188, 300, 8, v1, [VIOLET, BLUE, CYAN, GREEN]) + bargraph(10, 200, 300, 8, v2, [GREEN, YELLOW, RED, MAGENTA]) + bargraph(10, 212, 300, 8, v3, [CYAN, GREEN, YELLOW, ORANGE]) + + lcd.update() diff --git a/micropython/examples/tufty2040/wavy_message.py b/micropython/examples/tufty2040/wavy_message.py new file mode 100644 index 00000000..c9777423 --- /dev/null +++ b/micropython/examples/tufty2040/wavy_message.py @@ -0,0 +1,64 @@ +from picographics import PicoGraphics, DISPLAY_TUFTY_2040, PEN_RGB332 +import math +import time + +display = PicoGraphics(display=DISPLAY_TUFTY_2040, pen_type=PEN_RGB332, rotate=180) + + +# convert a hue, saturation, and value into rgb values +def hsv_to_rgb(h, s, v): + if s == 0.0: + return v, v, v + i = int(h * 6.0) + f = (h * 6.0) - i + p, q, t = v * (1.0 - s), v * (1.0 - s * f), v * (1.0 - s * (1.0 - f)) + v, t, p, q = int(v * 255), int(t * 255), int(p * 255), int(q * 255) + i = i % 6 + if i == 0: + return v, t, p + if i == 1: + return q, v, p + if i == 2: + return p, v, t + if i == 3: + return p, q, v + if i == 4: + return t, p, v + if i == 5: + return v, p, q + + +display.set_backlight(1.0) + +message = "Tufty 2040 is a hackable, programmable badge with a TFT LCD colour display, powered by the Raspberry Pi RP2040!" +text_size = 10 +message_width = display.measure_text(message, text_size) + +x_scroll = 0 + +while 1: + t = time.ticks_ms() / 1000.0 + display.set_pen(display.create_pen(50, 50, 50)) + display.clear() + + x_scroll -= 10 + if x_scroll < -(message_width + 320 + 100): + x_scroll = 0 + + # for each character we'll calculate a position and colour, then draw it + for i in range(0, len(message)): + cx = int(x_scroll + (i * text_size * 5.5)) + cy = int(80 + math.sin(t * 10 + i) * 20) + + # to speed things up we only bother doing the hardware if the character will be visible on screen + if cx > -50 and cx < 320: + # draw a shadow for the character + display.set_pen(display.create_pen(0, 0, 0)) + display.text(message[i], cx + 15, cy + 15, -1, text_size) + + # generate a rainbow colour that cycles with time + r, g, b = hsv_to_rgb(i / 10 + t / 5, 1, 1) + display.set_pen(display.create_pen(r, g, b)) + display.text(message[i], cx, cy, -1, text_size) + + display.update() diff --git a/micropython/modules/adcfft/adcfft.c b/micropython/modules/adcfft/adcfft.c index 11319a93..669150a2 100644 --- a/micropython/modules/adcfft/adcfft.c +++ b/micropython/modules/adcfft/adcfft.c @@ -33,4 +33,8 @@ const mp_obj_module_t adcfft_user_cmodule = { .globals = (mp_obj_dict_t*)&mp_module_adcfft_globals, }; -MP_REGISTER_MODULE(MP_QSTR_adcfft, adcfft_user_cmodule, MODULE_ADCFFT_ENABLED); \ No newline at end of file +#if MICROPY_VERSION <= 70144 +MP_REGISTER_MODULE(MP_QSTR_adcfft, adcfft_user_cmodule, MODULE_ADCFFT_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_adcfft, adcfft_user_cmodule); +#endif \ No newline at end of file diff --git a/micropython/modules/badger2040-micropython.cmake b/micropython/modules/badger2040-micropython.cmake index 3849e2ea..0bb41c5c 100644 --- a/micropython/modules/badger2040-micropython.cmake +++ b/micropython/modules/badger2040-micropython.cmake @@ -4,14 +4,13 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../../") include(pimoroni_i2c/micropython) +include(pimoroni_bus/micropython) include(breakout_dotmatrix/micropython) include(breakout_encoder/micropython) include(breakout_ioexpander/micropython) include(breakout_ltr559/micropython) -include(breakout_colourlcd160x80/micropython) include(breakout_as7262/micropython) -include(breakout_roundlcd/micropython) include(breakout_rgbmatrix5x5/micropython) include(breakout_matrix11x7/micropython) include(breakout_msa301/micropython) @@ -21,7 +20,6 @@ include(breakout_potentiometer/micropython) include(breakout_rtc/micropython) include(breakout_trackball/micropython) include(breakout_sgp30/micropython) -include(breakout_colourlcd240x240/micropython) include(breakout_bh1745/micropython) include(breakout_bme68x/micropython) include(breakout_bme280/micropython) diff --git a/micropython/modules/badger2040/badger2040.c b/micropython/modules/badger2040/badger2040.c index c95627d5..4aa0dfed 100644 --- a/micropython/modules/badger2040/badger2040.c +++ b/micropython/modules/badger2040/badger2040.c @@ -137,4 +137,8 @@ const mp_obj_module_t badger2040_user_cmodule = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_badger2040_globals, }; +#if MICROPY_VERSION <= 70144 MP_REGISTER_MODULE(MP_QSTR_badger2040, badger2040_user_cmodule, MODULE_BADGER2040_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_badger2040, badger2040_user_cmodule); +#endif diff --git a/micropython/modules/breakout_as7262/breakout_as7262.c b/micropython/modules/breakout_as7262/breakout_as7262.c index 654765df..27e2dd60 100644 --- a/micropython/modules/breakout_as7262/breakout_as7262.c +++ b/micropython/modules/breakout_as7262/breakout_as7262.c @@ -83,6 +83,10 @@ const mp_obj_module_t breakout_as7262_user_cmodule = { }; //////////////////////////////////////////////////////////////////////////////////////////////////// +#if MICROPY_VERSION <= 70144 MP_REGISTER_MODULE(MP_QSTR_breakout_as7262, breakout_as7262_user_cmodule, MODULE_BREAKOUT_AS7262_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_breakout_as7262, breakout_as7262_user_cmodule); +#endif //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/micropython/modules/breakout_bh1745/breakout_bh1745.c b/micropython/modules/breakout_bh1745/breakout_bh1745.c index af4d4f8c..5ad5d233 100644 --- a/micropython/modules/breakout_bh1745/breakout_bh1745.c +++ b/micropython/modules/breakout_bh1745/breakout_bh1745.c @@ -57,6 +57,10 @@ const mp_obj_module_t breakout_bh1745_user_cmodule = { }; //////////////////////////////////////////////////////////////////////////////////////////////////// +#if MICROPY_VERSION <= 70144 MP_REGISTER_MODULE(MP_QSTR_breakout_bh1745, breakout_bh1745_user_cmodule, MODULE_BREAKOUT_LTR559_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_breakout_bh1745, breakout_bh1745_user_cmodule); +#endif //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/micropython/modules/breakout_bme280/breakout_bme280.c b/micropython/modules/breakout_bme280/breakout_bme280.c index 5495d18e..3e45e8c0 100644 --- a/micropython/modules/breakout_bme280/breakout_bme280.c +++ b/micropython/modules/breakout_bme280/breakout_bme280.c @@ -68,6 +68,10 @@ const mp_obj_module_t breakout_bme280_user_cmodule = { }; //////////////////////////////////////////////////////////////////////////////////////////////////// +#if MICROPY_VERSION <= 70144 MP_REGISTER_MODULE(MP_QSTR_breakout_bme280, breakout_bme280_user_cmodule, MODULE_BREAKOUT_BME280_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_breakout_bme280, breakout_bme280_user_cmodule); +#endif //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/micropython/modules/breakout_bme68x/breakout_bme68x.c b/micropython/modules/breakout_bme68x/breakout_bme68x.c index caffab88..2090c572 100644 --- a/micropython/modules/breakout_bme68x/breakout_bme68x.c +++ b/micropython/modules/breakout_bme68x/breakout_bme68x.c @@ -80,6 +80,10 @@ const mp_obj_module_t breakout_bme68x_user_cmodule = { }; //////////////////////////////////////////////////////////////////////////////////////////////////// +#if MICROPY_VERSION <= 70144 MP_REGISTER_MODULE(MP_QSTR_breakout_bme68x, breakout_bme68x_user_cmodule, MODULE_BREAKOUT_BME68X_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_breakout_bme68x, breakout_bme68x_user_cmodule); +#endif //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/micropython/modules/breakout_bmp280/breakout_bmp280.c b/micropython/modules/breakout_bmp280/breakout_bmp280.c index d70c59b0..06cc2e2c 100644 --- a/micropython/modules/breakout_bmp280/breakout_bmp280.c +++ b/micropython/modules/breakout_bmp280/breakout_bmp280.c @@ -68,6 +68,10 @@ const mp_obj_module_t breakout_bmp280_user_cmodule = { }; //////////////////////////////////////////////////////////////////////////////////////////////////// +#if MICROPY_VERSION <= 70144 MP_REGISTER_MODULE(MP_QSTR_breakout_bmp280, breakout_bmp280_user_cmodule, MODULE_BREAKOUT_BMP280_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_breakout_bmp280, breakout_bmp280_user_cmodule); +#endif //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/micropython/modules/breakout_colourlcd160x80/breakout_colourlcd160x80.c b/micropython/modules/breakout_colourlcd160x80/breakout_colourlcd160x80.c deleted file mode 100644 index aab86b02..00000000 --- a/micropython/modules/breakout_colourlcd160x80/breakout_colourlcd160x80.c +++ /dev/null @@ -1,78 +0,0 @@ -#include "breakout_colourlcd160x80.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// BreakoutColourLCD160x80 Class -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/***** Methods *****/ -MP_DEFINE_CONST_FUN_OBJ_1(BreakoutColourLCD160x80_update_obj, BreakoutColourLCD160x80_update); -MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutColourLCD160x80_set_backlight_obj, 1, BreakoutColourLCD160x80_set_backlight); -MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutColourLCD160x80_set_pen_obj, 1, BreakoutColourLCD160x80_set_pen); -MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutColourLCD160x80_create_pen_obj, 1, BreakoutColourLCD160x80_create_pen); -MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutColourLCD160x80_set_clip_obj, 1, BreakoutColourLCD160x80_set_clip); -MP_DEFINE_CONST_FUN_OBJ_1(BreakoutColourLCD160x80_remove_clip_obj, BreakoutColourLCD160x80_remove_clip); -MP_DEFINE_CONST_FUN_OBJ_1(BreakoutColourLCD160x80_clear_obj, BreakoutColourLCD160x80_clear); -MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutColourLCD160x80_pixel_obj, 1, BreakoutColourLCD160x80_pixel); -MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutColourLCD160x80_pixel_span_obj, 1, BreakoutColourLCD160x80_pixel_span); -MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutColourLCD160x80_rectangle_obj, 1, BreakoutColourLCD160x80_rectangle); -MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutColourLCD160x80_circle_obj, 1, BreakoutColourLCD160x80_circle); -MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutColourLCD160x80_character_obj, 1, BreakoutColourLCD160x80_character); -MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutColourLCD160x80_text_obj, 1, BreakoutColourLCD160x80_text); -MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutColourLCD160x80_polygon_obj, 1, BreakoutColourLCD160x80_polygon); -MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutColourLCD160x80_triangle_obj, 1, BreakoutColourLCD160x80_triangle); -MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutColourLCD160x80_line_obj, 1, BreakoutColourLCD160x80_line); - -/***** Binding of Methods *****/ -STATIC const mp_rom_map_elem_t BreakoutColourLCD160x80_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&BreakoutColourLCD160x80_update_obj) }, - { MP_ROM_QSTR(MP_QSTR_set_backlight), MP_ROM_PTR(&BreakoutColourLCD160x80_set_backlight_obj) }, - { MP_ROM_QSTR(MP_QSTR_set_pen), MP_ROM_PTR(&BreakoutColourLCD160x80_set_pen_obj) }, - { MP_ROM_QSTR(MP_QSTR_create_pen), MP_ROM_PTR(&BreakoutColourLCD160x80_create_pen_obj) }, - { MP_ROM_QSTR(MP_QSTR_set_clip), MP_ROM_PTR(&BreakoutColourLCD160x80_set_clip_obj) }, - { MP_ROM_QSTR(MP_QSTR_remove_clip), MP_ROM_PTR(&BreakoutColourLCD160x80_remove_clip_obj) }, - { MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&BreakoutColourLCD160x80_clear_obj) }, - { MP_ROM_QSTR(MP_QSTR_pixel), MP_ROM_PTR(&BreakoutColourLCD160x80_pixel_obj) }, - { MP_ROM_QSTR(MP_QSTR_pixel_span), MP_ROM_PTR(&BreakoutColourLCD160x80_pixel_span_obj) }, - { MP_ROM_QSTR(MP_QSTR_rectangle), MP_ROM_PTR(&BreakoutColourLCD160x80_rectangle_obj) }, - { MP_ROM_QSTR(MP_QSTR_circle), MP_ROM_PTR(&BreakoutColourLCD160x80_circle_obj) }, - { MP_ROM_QSTR(MP_QSTR_character), MP_ROM_PTR(&BreakoutColourLCD160x80_character_obj) }, - { MP_ROM_QSTR(MP_QSTR_text), MP_ROM_PTR(&BreakoutColourLCD160x80_text_obj) }, - { MP_ROM_QSTR(MP_QSTR_polygon), MP_ROM_PTR(&BreakoutColourLCD160x80_polygon_obj) }, - { MP_ROM_QSTR(MP_QSTR_triangle), MP_ROM_PTR(&BreakoutColourLCD160x80_triangle_obj) }, - { MP_ROM_QSTR(MP_QSTR_line), MP_ROM_PTR(&BreakoutColourLCD160x80_line_obj) }, - { MP_ROM_QSTR(MP_QSTR_WIDTH), MP_ROM_INT(WIDTH) }, - { MP_ROM_QSTR(MP_QSTR_HEIGHT), MP_ROM_INT(HEIGHT) }, -}; -STATIC MP_DEFINE_CONST_DICT(BreakoutColourLCD160x80_locals_dict, BreakoutColourLCD160x80_locals_dict_table); - -/***** Class Definition *****/ -const mp_obj_type_t breakout_colourlcd160x80_BreakoutColourLCD160x80_type = { - { &mp_type_type }, - .name = MP_QSTR_breakout_colourlcd160x80, - .print = BreakoutColourLCD160x80_print, - .make_new = BreakoutColourLCD160x80_make_new, - .locals_dict = (mp_obj_dict_t*)&BreakoutColourLCD160x80_locals_dict, -}; - - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// breakout_colourlcd160x80 Module -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/***** Globals Table *****/ -STATIC const mp_map_elem_t breakout_colourlcd160x80_globals_table[] = { - { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_breakout_colourlcd160x80) }, - { MP_OBJ_NEW_QSTR(MP_QSTR_BreakoutColourLCD160x80), (mp_obj_t)&breakout_colourlcd160x80_BreakoutColourLCD160x80_type }, -}; -STATIC MP_DEFINE_CONST_DICT(mp_module_breakout_colourlcd160x80_globals, breakout_colourlcd160x80_globals_table); - -/***** Module Definition *****/ -const mp_obj_module_t breakout_colourlcd160x80_user_cmodule = { - .base = { &mp_type_module }, - .globals = (mp_obj_dict_t*)&mp_module_breakout_colourlcd160x80_globals, -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -MP_REGISTER_MODULE(MP_QSTR_breakout_colourlcd160x80, breakout_colourlcd160x80_user_cmodule, MODULE_BREAKOUT_COLOURLCD160X80_ENABLED); -//////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/micropython/modules/breakout_colourlcd160x80/breakout_colourlcd160x80.cpp b/micropython/modules/breakout_colourlcd160x80/breakout_colourlcd160x80.cpp deleted file mode 100644 index f598b636..00000000 --- a/micropython/modules/breakout_colourlcd160x80/breakout_colourlcd160x80.cpp +++ /dev/null @@ -1,529 +0,0 @@ -#include "libraries/breakout_colourlcd160x80/breakout_colourlcd160x80.hpp" -#include "micropython/modules/util.hpp" - - -using namespace pimoroni; - -extern "C" { -#include "breakout_colourlcd160x80.h" - -/***** Variables Struct *****/ -typedef struct _breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t { - mp_obj_base_t base; - BreakoutColourLCD160x80 *breakout; -} breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t; - -/***** Print *****/ -void BreakoutColourLCD160x80_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { - (void)kind; //Unused input parameter - breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t); - BreakoutColourLCD160x80* breakout = self->breakout; - mp_print_str(print, "BreakoutColourLCD160x80("); - - mp_print_str(print, "spi = "); - mp_obj_print_helper(print, mp_obj_new_int((breakout->get_spi() == spi0) ? 0 : 1), PRINT_REPR); - - mp_print_str(print, ", cs = "); - mp_obj_print_helper(print, mp_obj_new_int(breakout->get_cs()), PRINT_REPR); - - mp_print_str(print, ", dc = "); - mp_obj_print_helper(print, mp_obj_new_int(breakout->get_dc()), PRINT_REPR); - - mp_print_str(print, ", sck = "); - mp_obj_print_helper(print, mp_obj_new_int(breakout->get_sck()), PRINT_REPR); - - mp_print_str(print, ", mosi = "); - mp_obj_print_helper(print, mp_obj_new_int(breakout->get_mosi()), PRINT_REPR); - - mp_print_str(print, ", bl = "); - mp_obj_print_helper(print, mp_obj_new_int(breakout->get_bl()), PRINT_REPR); - - mp_print_str(print, ")"); -} - -/***** Constructor *****/ -mp_obj_t BreakoutColourLCD160x80_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t *self = nullptr; - - if(n_args + n_kw == 2) { - enum { ARG_buffer, ARG_slot }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_slot, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - // Parse args. - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - int slot = args[ARG_slot].u_int; - if(slot == BG_SPI_FRONT || slot == BG_SPI_BACK) { - self = m_new_obj(breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t); - self->base.type = &breakout_colourlcd160x80_BreakoutColourLCD160x80_type; - - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_RW); - - self->breakout = m_new_class(BreakoutColourLCD160x80, (uint16_t *)bufinfo.buf, (BG_SPI_SLOT)slot); - } - else { - mp_raise_ValueError("slot not a valid value. Expected 0 to 1"); - } - } - else { - enum { ARG_buffer, ARG_spi, ARG_cs, ARG_dc, ARG_sck, ARG_mosi, ARG_bl }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_spi, MP_ARG_INT, {.u_int = -1} }, - { MP_QSTR_cs, MP_ARG_INT, {.u_int = ST7735::DEFAULT_CS_PIN} }, - { MP_QSTR_dc, MP_ARG_INT, {.u_int = ST7735::DEFAULT_DC_PIN} }, - { MP_QSTR_sck, MP_ARG_INT, {.u_int = ST7735::DEFAULT_SCK_PIN} }, - { MP_QSTR_mosi, MP_ARG_INT, {.u_int = ST7735::DEFAULT_MOSI_PIN} }, - { MP_QSTR_bl, MP_ARG_INT, {.u_int = ST7735::DEFAULT_BL_PIN} }, - }; - - // Parse args. - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_RW); - - // Get SPI bus. - int spi_id = args[ARG_spi].u_int; - int sck = args[ARG_sck].u_int; - int mosi = args[ARG_mosi].u_int; - - if(spi_id == -1) { - spi_id = (sck >> 3) & 0b1; // If no spi specified, choose the one for the given SCK pin - } - if(spi_id < 0 || spi_id > 1) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("SPI(%d) doesn't exist"), spi_id); - } - - if(!IS_VALID_SCK(spi_id, sck)) { - mp_raise_ValueError(MP_ERROR_TEXT("bad SCK pin")); - } - - if(!IS_VALID_MOSI(spi_id, mosi)) { - mp_raise_ValueError(MP_ERROR_TEXT("bad MOSI pin")); - } - - self = m_new_obj(breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t); - self->base.type = &breakout_colourlcd160x80_BreakoutColourLCD160x80_type; - - spi_inst_t *spi = (spi_id == 0) ? spi0 : spi1; - self->breakout = m_new_class(BreakoutColourLCD160x80, (uint16_t *)bufinfo.buf, spi, - args[ARG_cs].u_int, args[ARG_dc].u_int, sck, mosi, PIN_UNUSED, args[ARG_bl].u_int); - } - - self->breakout->init(); - - return MP_OBJ_FROM_PTR(self); -} - -/***** Methods *****/ -mp_obj_t BreakoutColourLCD160x80_update(mp_obj_t self_in) { - breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t); - self->breakout->update(); - - return mp_const_none; -} - -mp_obj_t BreakoutColourLCD160x80_set_backlight(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_brightness }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_brightness, MP_ARG_REQUIRED | MP_ARG_OBJ }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t); - - float brightness = mp_obj_get_float(args[ARG_brightness].u_obj); - - if(brightness < 0 || brightness > 1.0f) - mp_raise_ValueError("brightness out of range. Expected 0.0 to 1.0"); - else - self->breakout->set_backlight((uint8_t)(brightness * 255.0f)); - - return mp_const_none; -} - -mp_obj_t BreakoutColourLCD160x80_set_pen(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - - if(n_args <= 2) { - enum { ARG_self, ARG_pen }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_pen, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t); - - int pen = args[ARG_pen].u_int; - - if(pen < 0 || pen > 0xffff) - mp_raise_ValueError("p is not a valid pen."); - else - self->breakout->set_pen(pen); - } - else { - enum { ARG_self, ARG_r, ARG_g, ARG_b }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_r, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_g, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_b, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t); - - int r = args[ARG_r].u_int; - int g = args[ARG_g].u_int; - int b = args[ARG_b].u_int; - - if(r < 0 || r > 255) - mp_raise_ValueError("r out of range. Expected 0 to 255"); - else if(g < 0 || g > 255) - mp_raise_ValueError("g out of range. Expected 0 to 255"); - else if(b < 0 || b > 255) - mp_raise_ValueError("b out of range. Expected 0 to 255"); - else - self->breakout->set_pen(r, g, b); - } - - return mp_const_none; -} - -mp_obj_t BreakoutColourLCD160x80_create_pen(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - int pen = 0; - - enum { ARG_self, ARG_r, ARG_g, ARG_b }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_r, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_g, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_b, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t); - - int r = args[ARG_r].u_int; - int g = args[ARG_g].u_int; - int b = args[ARG_b].u_int; - - if(r < 0 || r > 255) - mp_raise_ValueError("r out of range. Expected 0 to 255"); - else if(g < 0 || g > 255) - mp_raise_ValueError("g out of range. Expected 0 to 255"); - else if(b < 0 || b > 255) - mp_raise_ValueError("b out of range. Expected 0 to 255"); - else - pen = self->breakout->create_pen(r, g, b); - - return mp_obj_new_int(pen); -} - -mp_obj_t BreakoutColourLCD160x80_set_clip(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_x, ARG_y, ARG_w, ARG_h }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_w, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_h, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t); - - int x = args[ARG_x].u_int; - int y = args[ARG_y].u_int; - int w = args[ARG_w].u_int; - int h = args[ARG_h].u_int; - - Rect r(x, y, w, h); - self->breakout->set_clip(r); - - return mp_const_none; -} - -mp_obj_t BreakoutColourLCD160x80_remove_clip(mp_obj_t self_in) { - breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t); - self->breakout->remove_clip(); - - return mp_const_none; -} - -mp_obj_t BreakoutColourLCD160x80_clear(mp_obj_t self_in) { - breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t); - self->breakout->clear(); - - return mp_const_none; -} - -mp_obj_t BreakoutColourLCD160x80_pixel(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_x, ARG_y }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t); - - int x = args[ARG_x].u_int; - int y = args[ARG_y].u_int; - - Point p(x, y); - self->breakout->pixel(p); - - return mp_const_none; -} - -mp_obj_t BreakoutColourLCD160x80_pixel_span(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_x, ARG_y, ARG_l }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_l, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t); - - int x = args[ARG_x].u_int; - int y = args[ARG_y].u_int; - int l = args[ARG_l].u_int; - - Point p(x, y); - self->breakout->pixel_span(p, l); - - return mp_const_none; -} - -mp_obj_t BreakoutColourLCD160x80_rectangle(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_x, ARG_y, ARG_w, ARG_h }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_w, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_h, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t); - - int x = args[ARG_x].u_int; - int y = args[ARG_y].u_int; - int w = args[ARG_w].u_int; - int h = args[ARG_h].u_int; - - Rect r(x, y, w, h); - self->breakout->rectangle(r); - - return mp_const_none; -} - -mp_obj_t BreakoutColourLCD160x80_circle(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_x, ARG_y, ARG_r }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_r, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t); - - int x = args[ARG_x].u_int; - int y = args[ARG_y].u_int; - int r = args[ARG_r].u_int; - - Point p(x, y); - self->breakout->circle(p, r); - - return mp_const_none; -} - -mp_obj_t BreakoutColourLCD160x80_character(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_char, ARG_x, ARG_y, ARG_scale }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_char, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_scale, MP_ARG_INT, {.u_int = 2} }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t); - - int c = mp_obj_get_int(args[ARG_char].u_obj); - int x = args[ARG_x].u_int; - int y = args[ARG_y].u_int; - int scale = args[ARG_scale].u_int; - - self->breakout->character((char)c, Point(x, y), scale); - - return mp_const_none; -} - -mp_obj_t BreakoutColourLCD160x80_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_text, ARG_x, ARG_y, ARG_wrap, ARG_scale }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_text, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_wordwrap, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_scale, MP_ARG_INT, {.u_int = 2} }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t); - - mp_obj_t text_obj = args[ARG_text].u_obj; - if(mp_obj_is_str_or_bytes(text_obj)) { - GET_STR_DATA_LEN(text_obj, str, str_len); - - std::string t((const char*)str); - - int x = args[ARG_x].u_int; - int y = args[ARG_y].u_int; - int wrap = args[ARG_wrap].u_int; - int scale = args[ARG_scale].u_int; - - self->breakout->text(t, Point(x, y), wrap, scale); - } - else if(mp_obj_is_float(text_obj)) { - mp_raise_TypeError("can't convert 'float' object to str implicitly"); - } - else if(mp_obj_is_int(text_obj)) { - mp_raise_TypeError("can't convert 'int' object to str implicitly"); - } - else if(mp_obj_is_bool(text_obj)) { - mp_raise_TypeError("can't convert 'bool' object to str implicitly"); - } - else { - mp_raise_TypeError("can't convert object to str implicitly"); - } - - return mp_const_none; -} - -mp_obj_t BreakoutColourLCD160x80_polygon(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - // enum { ARG_self, ARG_x, ARG_y, ARG_r }; - // static const mp_arg_t allowed_args[] = { - // { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - // { MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_INT }, - // { MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT }, - // { MP_QSTR_r, MP_ARG_REQUIRED | MP_ARG_INT }, - // }; - - // mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - // mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - // breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t); - - // int x = args[ARG_x].u_int; - // int y = args[ARG_y].u_int; - // int r = args[ARG_r].u_int; - - // Point p(x, y); - // self->breakout->circle(p, r); - - mp_raise_NotImplementedError("polygon is not implemented. Please avoid using this function for now"); - - return mp_const_none; -} - -mp_obj_t BreakoutColourLCD160x80_triangle(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_x1, ARG_y1, ARG_x2, ARG_y2, ARG_x3, ARG_y3 }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_x2, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y2, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_x3, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y3, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t); - - int x1 = args[ARG_x1].u_int; - int y1 = args[ARG_y1].u_int; - int x2 = args[ARG_x2].u_int; - int y2 = args[ARG_y2].u_int; - int x3 = args[ARG_x3].u_int; - int y3 = args[ARG_y3].u_int; - - Point p1(x1, y1); - Point p2(x2, y2); - Point p3(x3, y3); - self->breakout->triangle(p1, p2, p3); - - return mp_const_none; -} - -mp_obj_t BreakoutColourLCD160x80_line(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_x1, ARG_y1, ARG_x2, ARG_y2 }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_x2, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y2, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_colourlcd160x80_BreakoutColourLCD160x80_obj_t); - - int x1 = args[ARG_x1].u_int; - int y1 = args[ARG_y1].u_int; - int x2 = args[ARG_x2].u_int; - int y2 = args[ARG_y2].u_int; - - Point p1(x1, y1); - Point p2(x2, y2); - self->breakout->line(p1, p2); - - return mp_const_none; -} -} diff --git a/micropython/modules/breakout_colourlcd160x80/breakout_colourlcd160x80.h b/micropython/modules/breakout_colourlcd160x80/breakout_colourlcd160x80.h deleted file mode 100644 index 166c06a4..00000000 --- a/micropython/modules/breakout_colourlcd160x80/breakout_colourlcd160x80.h +++ /dev/null @@ -1,32 +0,0 @@ -// Include MicroPython API. -#include "py/runtime.h" -#include "py/objstr.h" - -/***** Constants *****/ -static const int WIDTH = 160; -static const int HEIGHT = 80; - - -/***** Extern of Class Definition *****/ -extern const mp_obj_type_t breakout_colourlcd160x80_BreakoutColourLCD160x80_type; - -/***** Extern of Class Methods *****/ -extern void BreakoutColourLCD160x80_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind); -extern mp_obj_t BreakoutColourLCD160x80_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args); -extern mp_obj_t BreakoutColourLCD160x80_update(mp_obj_t self_in); -extern mp_obj_t BreakoutColourLCD160x80_set_backlight(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); - -extern mp_obj_t BreakoutColourLCD160x80_set_pen(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t BreakoutColourLCD160x80_create_pen(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t BreakoutColourLCD160x80_set_clip(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t BreakoutColourLCD160x80_remove_clip(mp_obj_t self_in); -extern mp_obj_t BreakoutColourLCD160x80_clear(mp_obj_t self_in); -extern mp_obj_t BreakoutColourLCD160x80_pixel(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t BreakoutColourLCD160x80_pixel_span(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t BreakoutColourLCD160x80_rectangle(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t BreakoutColourLCD160x80_circle(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t BreakoutColourLCD160x80_character(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t BreakoutColourLCD160x80_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t BreakoutColourLCD160x80_polygon(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t BreakoutColourLCD160x80_triangle(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t BreakoutColourLCD160x80_line(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); \ No newline at end of file diff --git a/micropython/modules/breakout_colourlcd160x80/micropython.cmake b/micropython/modules/breakout_colourlcd160x80/micropython.cmake deleted file mode 100644 index f2dde212..00000000 --- a/micropython/modules/breakout_colourlcd160x80/micropython.cmake +++ /dev/null @@ -1,22 +0,0 @@ -set(MOD_NAME breakout_colourlcd160x80) -string(TOUPPER ${MOD_NAME} MOD_NAME_UPPER) -add_library(usermod_${MOD_NAME} INTERFACE) - -target_sources(usermod_${MOD_NAME} INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.c - ${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.cpp - ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/${MOD_NAME}/${MOD_NAME}.cpp - ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/st7735/st7735.cpp - ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/pico_graphics.cpp - ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/types.cpp -) - -target_include_directories(usermod_${MOD_NAME} INTERFACE - ${CMAKE_CURRENT_LIST_DIR} -) - -target_compile_definitions(usermod_${MOD_NAME} INTERFACE - -DMODULE_${MOD_NAME_UPPER}_ENABLED=1 -) - -target_link_libraries(usermod INTERFACE usermod_${MOD_NAME}) \ No newline at end of file diff --git a/micropython/modules/breakout_colourlcd240x240/breakout_colourlcd240x240.c b/micropython/modules/breakout_colourlcd240x240/breakout_colourlcd240x240.c deleted file mode 100644 index 885df270..00000000 --- a/micropython/modules/breakout_colourlcd240x240/breakout_colourlcd240x240.c +++ /dev/null @@ -1,78 +0,0 @@ -#include "breakout_colourlcd240x240.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// BreakoutColourLCD240x240 Class -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/***** Methods *****/ -MP_DEFINE_CONST_FUN_OBJ_1(BreakoutColourLCD240x240_update_obj, BreakoutColourLCD240x240_update); -MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutColourLCD240x240_set_backlight_obj, 1, BreakoutColourLCD240x240_set_backlight); -MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutColourLCD240x240_set_pen_obj, 1, BreakoutColourLCD240x240_set_pen); -MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutColourLCD240x240_create_pen_obj, 1, BreakoutColourLCD240x240_create_pen); -MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutColourLCD240x240_set_clip_obj, 1, BreakoutColourLCD240x240_set_clip); -MP_DEFINE_CONST_FUN_OBJ_1(BreakoutColourLCD240x240_remove_clip_obj, BreakoutColourLCD240x240_remove_clip); -MP_DEFINE_CONST_FUN_OBJ_1(BreakoutColourLCD240x240_clear_obj, BreakoutColourLCD240x240_clear); -MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutColourLCD240x240_pixel_obj, 1, BreakoutColourLCD240x240_pixel); -MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutColourLCD240x240_pixel_span_obj, 1, BreakoutColourLCD240x240_pixel_span); -MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutColourLCD240x240_rectangle_obj, 1, BreakoutColourLCD240x240_rectangle); -MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutColourLCD240x240_circle_obj, 1, BreakoutColourLCD240x240_circle); -MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutColourLCD240x240_character_obj, 1, BreakoutColourLCD240x240_character); -MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutColourLCD240x240_text_obj, 1, BreakoutColourLCD240x240_text); -MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutColourLCD240x240_polygon_obj, 1, BreakoutColourLCD240x240_polygon); -MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutColourLCD240x240_triangle_obj, 1, BreakoutColourLCD240x240_triangle); -MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutColourLCD240x240_line_obj, 1, BreakoutColourLCD240x240_line); - -/***** Binding of Methods *****/ -STATIC const mp_rom_map_elem_t BreakoutColourLCD240x240_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&BreakoutColourLCD240x240_update_obj) }, - { MP_ROM_QSTR(MP_QSTR_set_backlight), MP_ROM_PTR(&BreakoutColourLCD240x240_set_backlight_obj) }, - { MP_ROM_QSTR(MP_QSTR_set_pen), MP_ROM_PTR(&BreakoutColourLCD240x240_set_pen_obj) }, - { MP_ROM_QSTR(MP_QSTR_create_pen), MP_ROM_PTR(&BreakoutColourLCD240x240_create_pen_obj) }, - { MP_ROM_QSTR(MP_QSTR_set_clip), MP_ROM_PTR(&BreakoutColourLCD240x240_set_clip_obj) }, - { MP_ROM_QSTR(MP_QSTR_remove_clip), MP_ROM_PTR(&BreakoutColourLCD240x240_remove_clip_obj) }, - { MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&BreakoutColourLCD240x240_clear_obj) }, - { MP_ROM_QSTR(MP_QSTR_pixel), MP_ROM_PTR(&BreakoutColourLCD240x240_pixel_obj) }, - { MP_ROM_QSTR(MP_QSTR_pixel_span), MP_ROM_PTR(&BreakoutColourLCD240x240_pixel_span_obj) }, - { MP_ROM_QSTR(MP_QSTR_rectangle), MP_ROM_PTR(&BreakoutColourLCD240x240_rectangle_obj) }, - { MP_ROM_QSTR(MP_QSTR_circle), MP_ROM_PTR(&BreakoutColourLCD240x240_circle_obj) }, - { MP_ROM_QSTR(MP_QSTR_character), MP_ROM_PTR(&BreakoutColourLCD240x240_character_obj) }, - { MP_ROM_QSTR(MP_QSTR_text), MP_ROM_PTR(&BreakoutColourLCD240x240_text_obj) }, - { MP_ROM_QSTR(MP_QSTR_polygon), MP_ROM_PTR(&BreakoutColourLCD240x240_polygon_obj) }, - { MP_ROM_QSTR(MP_QSTR_triangle), MP_ROM_PTR(&BreakoutColourLCD240x240_triangle_obj) }, - { MP_ROM_QSTR(MP_QSTR_line), MP_ROM_PTR(&BreakoutColourLCD240x240_line_obj) }, - { MP_ROM_QSTR(MP_QSTR_WIDTH), MP_ROM_INT(WIDTH) }, - { MP_ROM_QSTR(MP_QSTR_HEIGHT), MP_ROM_INT(HEIGHT) }, -}; -STATIC MP_DEFINE_CONST_DICT(BreakoutColourLCD240x240_locals_dict, BreakoutColourLCD240x240_locals_dict_table); - -/***** Class Definition *****/ -const mp_obj_type_t breakout_colourlcd240x240_BreakoutColourLCD240x240_type = { - { &mp_type_type }, - .name = MP_QSTR_breakout_colourlcd240x240, - .print = BreakoutColourLCD240x240_print, - .make_new = BreakoutColourLCD240x240_make_new, - .locals_dict = (mp_obj_dict_t*)&BreakoutColourLCD240x240_locals_dict, -}; - - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// breakout_colourlcd240x240 Module -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/***** Globals Table *****/ -STATIC const mp_map_elem_t breakout_colourlcd240x240_globals_table[] = { - { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_breakout_colourlcd240x240) }, - { MP_OBJ_NEW_QSTR(MP_QSTR_BreakoutColourLCD240x240), (mp_obj_t)&breakout_colourlcd240x240_BreakoutColourLCD240x240_type }, -}; -STATIC MP_DEFINE_CONST_DICT(mp_module_breakout_colourlcd240x240_globals, breakout_colourlcd240x240_globals_table); - -/***** Module Definition *****/ -const mp_obj_module_t breakout_colourlcd240x240_user_cmodule = { - .base = { &mp_type_module }, - .globals = (mp_obj_dict_t*)&mp_module_breakout_colourlcd240x240_globals, -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -MP_REGISTER_MODULE(MP_QSTR_breakout_colourlcd240x240, breakout_colourlcd240x240_user_cmodule, MODULE_BREAKOUT_COLOURLCD240X240_ENABLED); -//////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/micropython/modules/breakout_colourlcd240x240/breakout_colourlcd240x240.cpp b/micropython/modules/breakout_colourlcd240x240/breakout_colourlcd240x240.cpp deleted file mode 100644 index f08e3723..00000000 --- a/micropython/modules/breakout_colourlcd240x240/breakout_colourlcd240x240.cpp +++ /dev/null @@ -1,531 +0,0 @@ -#include "libraries/breakout_colourlcd240x240/breakout_colourlcd240x240.hpp" - -#include "micropython/modules/util.hpp" - - - -using namespace pimoroni; - -extern "C" { -#include "breakout_colourlcd240x240.h" - -/***** Variables Struct *****/ -typedef struct _breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t { - mp_obj_base_t base; - BreakoutColourLCD240x240 *breakout; -} breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t; - -/***** Print *****/ -void BreakoutColourLCD240x240_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { - (void)kind; //Unused input parameter - breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t); - BreakoutColourLCD240x240* breakout = self->breakout; - mp_print_str(print, "BreakoutColourLCD240x240("); - - mp_print_str(print, "spi = "); - mp_obj_print_helper(print, mp_obj_new_int((breakout->get_spi() == spi0) ? 0 : 1), PRINT_REPR); - - mp_print_str(print, ", cs = "); - mp_obj_print_helper(print, mp_obj_new_int(breakout->get_cs()), PRINT_REPR); - - mp_print_str(print, ", dc = "); - mp_obj_print_helper(print, mp_obj_new_int(breakout->get_dc()), PRINT_REPR); - - mp_print_str(print, ", sck = "); - mp_obj_print_helper(print, mp_obj_new_int(breakout->get_sck()), PRINT_REPR); - - mp_print_str(print, ", mosi = "); - mp_obj_print_helper(print, mp_obj_new_int(breakout->get_mosi()), PRINT_REPR); - - mp_print_str(print, ", bl = "); - mp_obj_print_helper(print, mp_obj_new_int(breakout->get_bl()), PRINT_REPR); - - mp_print_str(print, ")"); -} - -/***** Constructor *****/ -mp_obj_t BreakoutColourLCD240x240_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t *self = nullptr; - - if(n_args + n_kw == 2) { - enum { ARG_buffer, ARG_slot }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_slot, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - // Parse args. - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - int slot = args[ARG_slot].u_int; - if(slot == BG_SPI_FRONT || slot == BG_SPI_BACK) { - self = m_new_obj(breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t); - self->base.type = &breakout_colourlcd240x240_BreakoutColourLCD240x240_type; - - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_RW); - - self->breakout = new BreakoutColourLCD240x240((uint16_t *)bufinfo.buf, (BG_SPI_SLOT)slot); - } - else { - mp_raise_ValueError("slot not a valid value. Expected 0 to 1"); - } - } - else { - enum { ARG_buffer, ARG_spi, ARG_cs, ARG_dc, ARG_sck, ARG_mosi, ARG_bl }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_spi, MP_ARG_INT, {.u_int = -1} }, - { MP_QSTR_cs, MP_ARG_INT, {.u_int = pimoroni::SPI_BG_FRONT_CS} }, - { MP_QSTR_dc, MP_ARG_INT, {.u_int = pimoroni::SPI_DEFAULT_MISO} }, - { MP_QSTR_sck, MP_ARG_INT, {.u_int = pimoroni::SPI_DEFAULT_SCK} }, - { MP_QSTR_mosi, MP_ARG_INT, {.u_int = pimoroni::SPI_DEFAULT_MOSI} }, - { MP_QSTR_bl, MP_ARG_INT, {.u_int = pimoroni::SPI_BG_FRONT_PWM} }, - }; - - // Parse args. - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_RW); - - // Get SPI bus. - int spi_id = args[ARG_spi].u_int; - int sck = args[ARG_sck].u_int; - int mosi = args[ARG_mosi].u_int; - - if(spi_id == -1) { - spi_id = (sck >> 3) & 0b1; // If no spi specified, choose the one for the given SCK pin - } - if(spi_id < 0 || spi_id > 1) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("SPI(%d) doesn't exist"), spi_id); - } - - if(!IS_VALID_SCK(spi_id, sck)) { - mp_raise_ValueError(MP_ERROR_TEXT("bad SCK pin")); - } - - if(!IS_VALID_MOSI(spi_id, mosi)) { - mp_raise_ValueError(MP_ERROR_TEXT("bad MOSI pin")); - } - - self = m_new_obj(breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t); - self->base.type = &breakout_colourlcd240x240_BreakoutColourLCD240x240_type; - - spi_inst_t *spi = (spi_id == 0) ? spi0 : spi1; - self->breakout = new BreakoutColourLCD240x240((uint16_t *)bufinfo.buf, spi, - args[ARG_cs].u_int, args[ARG_dc].u_int, sck, mosi, args[ARG_bl].u_int); - } - - self->breakout->init(); - - return MP_OBJ_FROM_PTR(self); -} - -/***** Methods *****/ -mp_obj_t BreakoutColourLCD240x240_update(mp_obj_t self_in) { - breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t); - self->breakout->update(); - - return mp_const_none; -} - -mp_obj_t BreakoutColourLCD240x240_set_backlight(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_brightness }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_brightness, MP_ARG_REQUIRED | MP_ARG_OBJ }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t); - - float brightness = mp_obj_get_float(args[ARG_brightness].u_obj); - - if(brightness < 0 || brightness > 1.0f) - mp_raise_ValueError("brightness out of range. Expected 0.0 to 1.0"); - else - self->breakout->set_backlight((uint8_t)(brightness * 255.0f)); - - return mp_const_none; -} - -mp_obj_t BreakoutColourLCD240x240_set_pen(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - - if(n_args <= 2) { - enum { ARG_self, ARG_pen }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_pen, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t); - - int pen = args[ARG_pen].u_int; - - if(pen < 0 || pen > 0xffff) - mp_raise_ValueError("p is not a valid pen."); - else - self->breakout->set_pen(pen); - } - else { - enum { ARG_self, ARG_r, ARG_g, ARG_b }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_r, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_g, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_b, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t); - - int r = args[ARG_r].u_int; - int g = args[ARG_g].u_int; - int b = args[ARG_b].u_int; - - if(r < 0 || r > 255) - mp_raise_ValueError("r out of range. Expected 0 to 255"); - else if(g < 0 || g > 255) - mp_raise_ValueError("g out of range. Expected 0 to 255"); - else if(b < 0 || b > 255) - mp_raise_ValueError("b out of range. Expected 0 to 255"); - else - self->breakout->set_pen(r, g, b); - } - - return mp_const_none; -} - -mp_obj_t BreakoutColourLCD240x240_create_pen(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - int pen = 0; - - enum { ARG_self, ARG_r, ARG_g, ARG_b }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_r, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_g, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_b, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t); - - int r = args[ARG_r].u_int; - int g = args[ARG_g].u_int; - int b = args[ARG_b].u_int; - - if(r < 0 || r > 255) - mp_raise_ValueError("r out of range. Expected 0 to 255"); - else if(g < 0 || g > 255) - mp_raise_ValueError("g out of range. Expected 0 to 255"); - else if(b < 0 || b > 255) - mp_raise_ValueError("b out of range. Expected 0 to 255"); - else - pen = self->breakout->create_pen(r, g, b); - - return mp_obj_new_int(pen); -} - -mp_obj_t BreakoutColourLCD240x240_set_clip(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_x, ARG_y, ARG_w, ARG_h }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_w, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_h, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t); - - int x = args[ARG_x].u_int; - int y = args[ARG_y].u_int; - int w = args[ARG_w].u_int; - int h = args[ARG_h].u_int; - - Rect r(x, y, w, h); - self->breakout->set_clip(r); - - return mp_const_none; -} - -mp_obj_t BreakoutColourLCD240x240_remove_clip(mp_obj_t self_in) { - breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t); - self->breakout->remove_clip(); - - return mp_const_none; -} - -mp_obj_t BreakoutColourLCD240x240_clear(mp_obj_t self_in) { - breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t); - self->breakout->clear(); - - return mp_const_none; -} - -mp_obj_t BreakoutColourLCD240x240_pixel(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_x, ARG_y }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t); - - int x = args[ARG_x].u_int; - int y = args[ARG_y].u_int; - - Point p(x, y); - self->breakout->pixel(p); - - return mp_const_none; -} - -mp_obj_t BreakoutColourLCD240x240_pixel_span(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_x, ARG_y, ARG_l }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_l, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t); - - int x = args[ARG_x].u_int; - int y = args[ARG_y].u_int; - int l = args[ARG_l].u_int; - - Point p(x, y); - self->breakout->pixel_span(p, l); - - return mp_const_none; -} - -mp_obj_t BreakoutColourLCD240x240_rectangle(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_x, ARG_y, ARG_w, ARG_h }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_w, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_h, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t); - - int x = args[ARG_x].u_int; - int y = args[ARG_y].u_int; - int w = args[ARG_w].u_int; - int h = args[ARG_h].u_int; - - Rect r(x, y, w, h); - self->breakout->rectangle(r); - - return mp_const_none; -} - -mp_obj_t BreakoutColourLCD240x240_circle(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_x, ARG_y, ARG_r }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_r, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t); - - int x = args[ARG_x].u_int; - int y = args[ARG_y].u_int; - int r = args[ARG_r].u_int; - - Point p(x, y); - self->breakout->circle(p, r); - - return mp_const_none; -} - -mp_obj_t BreakoutColourLCD240x240_character(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_char, ARG_x, ARG_y, ARG_scale }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_char, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_scale, MP_ARG_INT, {.u_int = 2} }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t); - - int c = mp_obj_get_int(args[ARG_char].u_obj); - int x = args[ARG_x].u_int; - int y = args[ARG_y].u_int; - int scale = args[ARG_scale].u_int; - - self->breakout->character((char)c, Point(x, y), scale); - - return mp_const_none; -} - -mp_obj_t BreakoutColourLCD240x240_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_text, ARG_x, ARG_y, ARG_wrap, ARG_scale }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_text, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_wordwrap, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_scale, MP_ARG_INT, {.u_int = 2} }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t); - - mp_obj_t text_obj = args[ARG_text].u_obj; - if(mp_obj_is_str_or_bytes(text_obj)) { - GET_STR_DATA_LEN(text_obj, str, str_len); - - std::string t((const char*)str); - - int x = args[ARG_x].u_int; - int y = args[ARG_y].u_int; - int wrap = args[ARG_wrap].u_int; - int scale = args[ARG_scale].u_int; - - self->breakout->text(t, Point(x, y), wrap, scale); - } - else if(mp_obj_is_float(text_obj)) { - mp_raise_TypeError("can't convert 'float' object to str implicitly"); - } - else if(mp_obj_is_int(text_obj)) { - mp_raise_TypeError("can't convert 'int' object to str implicitly"); - } - else if(mp_obj_is_bool(text_obj)) { - mp_raise_TypeError("can't convert 'bool' object to str implicitly"); - } - else { - mp_raise_TypeError("can't convert object to str implicitly"); - } - - return mp_const_none; -} - -mp_obj_t BreakoutColourLCD240x240_polygon(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - // enum { ARG_self, ARG_x, ARG_y, ARG_r }; - // static const mp_arg_t allowed_args[] = { - // { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - // { MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_INT }, - // { MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT }, - // { MP_QSTR_r, MP_ARG_REQUIRED | MP_ARG_INT }, - // }; - - // mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - // mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - // breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t); - - // int x = args[ARG_x].u_int; - // int y = args[ARG_y].u_int; - // int r = args[ARG_r].u_int; - - // Point p(x, y); - // self->breakout->circle(p, r); - - mp_raise_NotImplementedError("polygon is not implemented. Please avoid using this function for now"); - - return mp_const_none; -} - -mp_obj_t BreakoutColourLCD240x240_triangle(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_x1, ARG_y1, ARG_x2, ARG_y2, ARG_x3, ARG_y3 }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_x2, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y2, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_x3, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y3, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t); - - int x1 = args[ARG_x1].u_int; - int y1 = args[ARG_y1].u_int; - int x2 = args[ARG_x2].u_int; - int y2 = args[ARG_y2].u_int; - int x3 = args[ARG_x3].u_int; - int y3 = args[ARG_y3].u_int; - - Point p1(x1, y1); - Point p2(x2, y2); - Point p3(x3, y3); - self->breakout->triangle(p1, p2, p3); - - return mp_const_none; -} - -mp_obj_t BreakoutColourLCD240x240_line(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_x1, ARG_y1, ARG_x2, ARG_y2 }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_x2, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y2, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_colourlcd240x240_BreakoutColourLCD240x240_obj_t); - - int x1 = args[ARG_x1].u_int; - int y1 = args[ARG_y1].u_int; - int x2 = args[ARG_x2].u_int; - int y2 = args[ARG_y2].u_int; - - Point p1(x1, y1); - Point p2(x2, y2); - self->breakout->line(p1, p2); - - return mp_const_none; -} -} diff --git a/micropython/modules/breakout_colourlcd240x240/breakout_colourlcd240x240.h b/micropython/modules/breakout_colourlcd240x240/breakout_colourlcd240x240.h deleted file mode 100644 index 80f0bb47..00000000 --- a/micropython/modules/breakout_colourlcd240x240/breakout_colourlcd240x240.h +++ /dev/null @@ -1,32 +0,0 @@ -// Include MicroPython API. -#include "py/runtime.h" -#include "py/objstr.h" - -/***** Constants *****/ -static const int WIDTH = 240; -static const int HEIGHT = 240; - - -/***** Extern of Class Definition *****/ -extern const mp_obj_type_t breakout_colourlcd240x240_BreakoutColourLCD240x240_type; - -/***** Extern of Class Methods *****/ -extern void BreakoutColourLCD240x240_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind); -extern mp_obj_t BreakoutColourLCD240x240_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args); -extern mp_obj_t BreakoutColourLCD240x240_update(mp_obj_t self_in); -extern mp_obj_t BreakoutColourLCD240x240_set_backlight(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); - -extern mp_obj_t BreakoutColourLCD240x240_set_pen(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t BreakoutColourLCD240x240_create_pen(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t BreakoutColourLCD240x240_set_clip(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t BreakoutColourLCD240x240_remove_clip(mp_obj_t self_in); -extern mp_obj_t BreakoutColourLCD240x240_clear(mp_obj_t self_in); -extern mp_obj_t BreakoutColourLCD240x240_pixel(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t BreakoutColourLCD240x240_pixel_span(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t BreakoutColourLCD240x240_rectangle(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t BreakoutColourLCD240x240_circle(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t BreakoutColourLCD240x240_character(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t BreakoutColourLCD240x240_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t BreakoutColourLCD240x240_polygon(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t BreakoutColourLCD240x240_triangle(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t BreakoutColourLCD240x240_line(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); \ No newline at end of file diff --git a/micropython/modules/breakout_colourlcd240x240/micropython.cmake b/micropython/modules/breakout_colourlcd240x240/micropython.cmake deleted file mode 100644 index 04d91d80..00000000 --- a/micropython/modules/breakout_colourlcd240x240/micropython.cmake +++ /dev/null @@ -1,22 +0,0 @@ -set(MOD_NAME breakout_colourlcd240x240) -string(TOUPPER ${MOD_NAME} MOD_NAME_UPPER) -add_library(usermod_${MOD_NAME} INTERFACE) - -target_sources(usermod_${MOD_NAME} INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.c - ${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.cpp - ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/${MOD_NAME}/${MOD_NAME}.cpp - ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/st7789/st7789.cpp - ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/pico_graphics.cpp - ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/types.cpp -) - -target_include_directories(usermod_${MOD_NAME} INTERFACE - ${CMAKE_CURRENT_LIST_DIR} -) - -target_compile_definitions(usermod_${MOD_NAME} INTERFACE - -DMODULE_${MOD_NAME_UPPER}_ENABLED=1 -) - -target_link_libraries(usermod INTERFACE usermod_${MOD_NAME}) \ No newline at end of file diff --git a/micropython/modules/breakout_dotmatrix/breakout_dotmatrix.c b/micropython/modules/breakout_dotmatrix/breakout_dotmatrix.c index b1d6a6e9..96814529 100644 --- a/micropython/modules/breakout_dotmatrix/breakout_dotmatrix.c +++ b/micropython/modules/breakout_dotmatrix/breakout_dotmatrix.c @@ -54,6 +54,10 @@ const mp_obj_module_t breakout_dotmatrix_user_cmodule = { }; //////////////////////////////////////////////////////////////////////////////////////////////////// +#if MICROPY_VERSION <= 70144 MP_REGISTER_MODULE(MP_QSTR_breakout_dotmatrix, breakout_dotmatrix_user_cmodule, MODULE_BREAKOUT_DOTMATRIX_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_breakout_dotmatrix, breakout_dotmatrix_user_cmodule); +#endif //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/micropython/modules/breakout_encoder/breakout_encoder.c b/micropython/modules/breakout_encoder/breakout_encoder.c index 2db2e1f6..35823915 100644 --- a/micropython/modules/breakout_encoder/breakout_encoder.c +++ b/micropython/modules/breakout_encoder/breakout_encoder.c @@ -60,6 +60,10 @@ const mp_obj_module_t breakout_encoder_user_cmodule = { }; //////////////////////////////////////////////////////////////////////////////////////////////////// +#if MICROPY_VERSION <= 70144 MP_REGISTER_MODULE(MP_QSTR_breakout_encoder, breakout_encoder_user_cmodule, MODULE_BREAKOUT_ENCODER_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_breakout_encoder, breakout_encoder_user_cmodule); +#endif //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/micropython/modules/breakout_icp10125/breakout_icp10125.c b/micropython/modules/breakout_icp10125/breakout_icp10125.c index bc3c415e..2946ab3e 100644 --- a/micropython/modules/breakout_icp10125/breakout_icp10125.c +++ b/micropython/modules/breakout_icp10125/breakout_icp10125.c @@ -48,6 +48,10 @@ const mp_obj_module_t breakout_icp10125_user_cmodule = { }; //////////////////////////////////////////////////////////////////////////////////////////////////// +#if MICROPY_VERSION <= 70144 MP_REGISTER_MODULE(MP_QSTR_breakout_icp10125, breakout_icp10125_user_cmodule, MODULE_BREAKOUT_SGP30_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_breakout_icp10125, breakout_icp10125_user_cmodule); +#endif //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/micropython/modules/breakout_ioexpander/breakout_ioexpander.c b/micropython/modules/breakout_ioexpander/breakout_ioexpander.c index 5e990c97..a9dc0852 100644 --- a/micropython/modules/breakout_ioexpander/breakout_ioexpander.c +++ b/micropython/modules/breakout_ioexpander/breakout_ioexpander.c @@ -89,6 +89,10 @@ const mp_obj_module_t breakout_ioexpander_user_cmodule = { }; //////////////////////////////////////////////////////////////////////////////////////////////////// +#if MICROPY_VERSION <= 70144 MP_REGISTER_MODULE(MP_QSTR_breakout_ioexpander, breakout_ioexpander_user_cmodule, MODULE_BREAKOUT_IOEXPANDER_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_breakout_ioexpander, breakout_ioexpander_user_cmodule); +#endif //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/micropython/modules/breakout_ltr559/breakout_ltr559.c b/micropython/modules/breakout_ltr559/breakout_ltr559.c index 6236cf24..6c8a66bc 100644 --- a/micropython/modules/breakout_ltr559/breakout_ltr559.c +++ b/micropython/modules/breakout_ltr559/breakout_ltr559.c @@ -71,6 +71,10 @@ const mp_obj_module_t breakout_ltr559_user_cmodule = { }; //////////////////////////////////////////////////////////////////////////////////////////////////// +#if MICROPY_VERSION <= 70144 MP_REGISTER_MODULE(MP_QSTR_breakout_ltr559, breakout_ltr559_user_cmodule, MODULE_BREAKOUT_LTR559_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_breakout_ltr559, breakout_ltr559_user_cmodule); +#endif //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/micropython/modules/breakout_matrix11x7/breakout_matrix11x7.c b/micropython/modules/breakout_matrix11x7/breakout_matrix11x7.c index eb17b3e1..ea20a079 100644 --- a/micropython/modules/breakout_matrix11x7/breakout_matrix11x7.c +++ b/micropython/modules/breakout_matrix11x7/breakout_matrix11x7.c @@ -46,6 +46,10 @@ const mp_obj_module_t breakout_matrix11x7_user_cmodule = { }; //////////////////////////////////////////////////////////////////////////////////////////////////// +#if MICROPY_VERSION <= 70144 MP_REGISTER_MODULE(MP_QSTR_breakout_matrix11x7, breakout_matrix11x7_user_cmodule, MODULE_BREAKOUT_MATRIX11X7_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_breakout_matrix11x7, breakout_matrix11x7_user_cmodule); +#endif //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/micropython/modules/breakout_mics6814/breakout_mics6814.c b/micropython/modules/breakout_mics6814/breakout_mics6814.c index 9af5f684..11cb785e 100644 --- a/micropython/modules/breakout_mics6814/breakout_mics6814.c +++ b/micropython/modules/breakout_mics6814/breakout_mics6814.c @@ -74,6 +74,10 @@ const mp_obj_module_t breakout_mics6814_user_cmodule = { }; //////////////////////////////////////////////////////////////////////////////////////////////////// +#if MICROPY_VERSION <= 70144 MP_REGISTER_MODULE(MP_QSTR_breakout_mics6814, breakout_mics6814_user_cmodule, MODULE_BREAKOUT_MICS6814_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_breakout_mics6814, breakout_mics6814_user_cmodule); +#endif //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/micropython/modules/breakout_msa301/breakout_msa301.c b/micropython/modules/breakout_msa301/breakout_msa301.c index 86fad45d..4dc01d7f 100644 --- a/micropython/modules/breakout_msa301/breakout_msa301.c +++ b/micropython/modules/breakout_msa301/breakout_msa301.c @@ -107,6 +107,10 @@ const mp_obj_module_t breakout_msa301_user_cmodule = { }; //////////////////////////////////////////////////////////////////////////////////////////////////// +#if MICROPY_VERSION <= 70144 MP_REGISTER_MODULE(MP_QSTR_breakout_msa301, breakout_msa301_user_cmodule, MODULE_BREAKOUT_MSA301_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_breakout_msa301, breakout_msa301_user_cmodule); +#endif //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/micropython/modules/breakout_pmw3901/breakout_paa5100.c b/micropython/modules/breakout_pmw3901/breakout_paa5100.c new file mode 100644 index 00000000..3935c00e --- /dev/null +++ b/micropython/modules/breakout_pmw3901/breakout_paa5100.c @@ -0,0 +1,64 @@ +#include "breakout_pmw3901.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// BreakoutPMW3901 Class +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/***** Methods *****/ +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutPAA5100_get_id_obj, BreakoutPMW3901_get_id); +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutPAA5100_get_revision_obj, BreakoutPMW3901_get_revision); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutPAA5100_set_rotation_obj, 1, BreakoutPMW3901_set_rotation); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutPAA5100_set_orientation_obj, 1, BreakoutPMW3901_set_orientation); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutPAA5100_get_motion_obj, 1, BreakoutPMW3901_get_motion); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutPAA5100_get_motion_slow_obj, 1, BreakoutPMW3901_get_motion_slow); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutPAA5100_frame_capture_obj, 2, BreakoutPMW3901_frame_capture); + +/***** Binding of Methods *****/ +STATIC const mp_rom_map_elem_t BreakoutPAA5100_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_get_id), MP_ROM_PTR(&BreakoutPAA5100_get_id_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_revision), MP_ROM_PTR(&BreakoutPAA5100_get_revision_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_rotation), MP_ROM_PTR(&BreakoutPAA5100_set_rotation_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_orientation), MP_ROM_PTR(&BreakoutPAA5100_set_orientation_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_motion), MP_ROM_PTR(&BreakoutPAA5100_get_motion_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_motion_slow), MP_ROM_PTR(&BreakoutPAA5100_get_motion_slow_obj) }, + { MP_ROM_QSTR(MP_QSTR_frame_capture), MP_ROM_PTR(&BreakoutPAA5100_frame_capture_obj) }, + { MP_ROM_QSTR(MP_QSTR_DEGREES_0), MP_ROM_INT(0x00) }, + { MP_ROM_QSTR(MP_QSTR_DEGREES_90), MP_ROM_INT(0x01) }, + { MP_ROM_QSTR(MP_QSTR_DEGREES_180), MP_ROM_INT(0x02) }, + { MP_ROM_QSTR(MP_QSTR_DEGREES_270), MP_ROM_INT(0x03) }, + { MP_ROM_QSTR(MP_QSTR_FRAME_SIZE), MP_ROM_INT(35) }, + { MP_ROM_QSTR(MP_QSTR_FRAME_BYTES), MP_ROM_INT(1225) }, +}; +STATIC MP_DEFINE_CONST_DICT(BreakoutPAA5100_locals_dict, BreakoutPAA5100_locals_dict_table); + +/***** Class Definition *****/ +const mp_obj_type_t breakout_paa5100_BreakoutPAA5100_type = { + { &mp_type_type }, + .name = MP_QSTR_BreakoutPAA5100, + .print = BreakoutPMW3901_print, + .make_new = BreakoutPAA5100_make_new, + .locals_dict = (mp_obj_dict_t*)&BreakoutPAA5100_locals_dict, +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// breakout_paa5100 Module +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/***** Globals Table *****/ +STATIC const mp_map_elem_t breakout_paa5100_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_breakout_paa5100) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_BreakoutPAA5100), (mp_obj_t)&breakout_paa5100_BreakoutPAA5100_type }, +}; +STATIC MP_DEFINE_CONST_DICT(mp_module_breakout_paa5100_globals, breakout_paa5100_globals_table); + +/***** Module Definition *****/ +const mp_obj_module_t breakout_paa5100_user_cmodule = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_breakout_paa5100_globals, +}; + +#if MICROPY_VERSION <= 70144 +MP_REGISTER_MODULE(MP_QSTR_breakout_paa5100, breakout_paa5100_user_cmodule, MODULE_BREAKOUT_PMW3901_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_breakout_paa5100, breakout_paa5100_user_cmodule); +#endif \ No newline at end of file diff --git a/micropython/modules/breakout_pmw3901/breakout_pmw3901.c b/micropython/modules/breakout_pmw3901/breakout_pmw3901.c index fabf33fa..24e7382d 100644 --- a/micropython/modules/breakout_pmw3901/breakout_pmw3901.c +++ b/micropython/modules/breakout_pmw3901/breakout_pmw3901.c @@ -57,38 +57,8 @@ const mp_obj_module_t breakout_pmw3901_user_cmodule = { .globals = (mp_obj_dict_t*)&mp_module_breakout_pmw3901_globals, }; -//////////////////////////////////////////////////////////////////////////////////////////////////// -MP_REGISTER_MODULE(MP_QSTR_breakout_pmw3901, breakout_pmw3901_user_cmodule, MODULE_BREAKOUT_PMW3901_ENABLED); -//////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/***** Class Definition *****/ -const mp_obj_type_t breakout_paa5100_BreakoutPAA5100_type = { - { &mp_type_type }, - .name = MP_QSTR_BreakoutPAA5100, - .print = BreakoutPMW3901_print, - .make_new = BreakoutPAA5100_make_new, - .locals_dict = (mp_obj_dict_t*)&BreakoutPMW3901_locals_dict, -}; - - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// breakout_paa5100 Module -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/***** Globals Table *****/ -STATIC const mp_map_elem_t breakout_paa5100_globals_table[] = { - { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_breakout_paa5100) }, - { MP_OBJ_NEW_QSTR(MP_QSTR_BreakoutPAA5100), (mp_obj_t)&breakout_paa5100_BreakoutPAA5100_type }, -}; -STATIC MP_DEFINE_CONST_DICT(mp_module_breakout_paa5100_globals, breakout_paa5100_globals_table); - -/***** Module Definition *****/ -const mp_obj_module_t breakout_paa5100_user_cmodule = { - .base = { &mp_type_module }, - .globals = (mp_obj_dict_t*)&mp_module_breakout_paa5100_globals, -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -MP_REGISTER_MODULE(MP_QSTR_breakout_paa5100, breakout_paa5100_user_cmodule, MODULE_BREAKOUT_PMW3901_ENABLED); -//////////////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file +#if MICROPY_VERSION <= 70144 +MP_REGISTER_MODULE(MP_QSTR_breakout_pmw3901, breakout_pmw3901_user_cmodule, MODULE_BREAKOUT_PMW3901_ENABLED);//, +#else +MP_REGISTER_MODULE(MP_QSTR_breakout_pmw3901, breakout_pmw3901_user_cmodule); +#endif \ No newline at end of file diff --git a/micropython/modules/breakout_pmw3901/micropython.cmake b/micropython/modules/breakout_pmw3901/micropython.cmake index ae38c6df..4bc80ae6 100644 --- a/micropython/modules/breakout_pmw3901/micropython.cmake +++ b/micropython/modules/breakout_pmw3901/micropython.cmake @@ -3,7 +3,8 @@ string(TOUPPER ${MOD_NAME} MOD_NAME_UPPER) add_library(usermod_${MOD_NAME} INTERFACE) target_sources(usermod_${MOD_NAME} INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.c + ${CMAKE_CURRENT_LIST_DIR}/breakout_pmw3901.c + ${CMAKE_CURRENT_LIST_DIR}/breakout_paa5100.c ${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.cpp ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/${MOD_NAME}/${MOD_NAME}.cpp ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/pmw3901/pmw3901.cpp diff --git a/micropython/modules/breakout_potentiometer/breakout_potentiometer.c b/micropython/modules/breakout_potentiometer/breakout_potentiometer.c index 9315f1c7..70d81af4 100644 --- a/micropython/modules/breakout_potentiometer/breakout_potentiometer.c +++ b/micropython/modules/breakout_potentiometer/breakout_potentiometer.c @@ -58,6 +58,10 @@ const mp_obj_module_t breakout_potentiometer_user_cmodule = { }; //////////////////////////////////////////////////////////////////////////////////////////////////// +#if MICROPY_VERSION <= 70144 MP_REGISTER_MODULE(MP_QSTR_breakout_potentiometer, breakout_potentiometer_user_cmodule, MODULE_BREAKOUT_POTENTIOMETER_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_breakout_potentiometer, breakout_potentiometer_user_cmodule); +#endif //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/micropython/modules/breakout_rgbmatrix5x5/breakout_rgbmatrix5x5.c b/micropython/modules/breakout_rgbmatrix5x5/breakout_rgbmatrix5x5.c index 3d8df2f5..f4322a2c 100644 --- a/micropython/modules/breakout_rgbmatrix5x5/breakout_rgbmatrix5x5.c +++ b/micropython/modules/breakout_rgbmatrix5x5/breakout_rgbmatrix5x5.c @@ -46,6 +46,10 @@ const mp_obj_module_t breakout_rgbmatrix5x5_user_cmodule = { }; //////////////////////////////////////////////////////////////////////////////////////////////////// +#if MICROPY_VERSION <= 70144 MP_REGISTER_MODULE(MP_QSTR_breakout_rgbmatrix5x5, breakout_rgbmatrix5x5_user_cmodule, MODULE_BREAKOUT_RGBMATRIX5X5_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_breakout_rgbmatrix5x5, breakout_rgbmatrix5x5_user_cmodule); +#endif //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/micropython/modules/breakout_roundlcd/breakout_roundlcd.c b/micropython/modules/breakout_roundlcd/breakout_roundlcd.c deleted file mode 100644 index 3f597f67..00000000 --- a/micropython/modules/breakout_roundlcd/breakout_roundlcd.c +++ /dev/null @@ -1,78 +0,0 @@ -#include "breakout_roundlcd.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// BreakoutRoundLCD Class -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/***** Methods *****/ -MP_DEFINE_CONST_FUN_OBJ_1(BreakoutRoundLCD_update_obj, BreakoutRoundLCD_update); -MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutRoundLCD_set_backlight_obj, 1, BreakoutRoundLCD_set_backlight); -MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutRoundLCD_set_pen_obj, 1, BreakoutRoundLCD_set_pen); -MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutRoundLCD_create_pen_obj, 1, BreakoutRoundLCD_create_pen); -MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutRoundLCD_set_clip_obj, 1, BreakoutRoundLCD_set_clip); -MP_DEFINE_CONST_FUN_OBJ_1(BreakoutRoundLCD_remove_clip_obj, BreakoutRoundLCD_remove_clip); -MP_DEFINE_CONST_FUN_OBJ_1(BreakoutRoundLCD_clear_obj, BreakoutRoundLCD_clear); -MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutRoundLCD_pixel_obj, 1, BreakoutRoundLCD_pixel); -MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutRoundLCD_pixel_span_obj, 1, BreakoutRoundLCD_pixel_span); -MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutRoundLCD_rectangle_obj, 1, BreakoutRoundLCD_rectangle); -MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutRoundLCD_circle_obj, 1, BreakoutRoundLCD_circle); -MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutRoundLCD_character_obj, 1, BreakoutRoundLCD_character); -MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutRoundLCD_text_obj, 1, BreakoutRoundLCD_text); -MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutRoundLCD_polygon_obj, 1, BreakoutRoundLCD_polygon); -MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutRoundLCD_triangle_obj, 1, BreakoutRoundLCD_triangle); -MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutRoundLCD_line_obj, 1, BreakoutRoundLCD_line); - -/***** Binding of Methods *****/ -STATIC const mp_rom_map_elem_t BreakoutRoundLCD_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&BreakoutRoundLCD_update_obj) }, - { MP_ROM_QSTR(MP_QSTR_set_backlight), MP_ROM_PTR(&BreakoutRoundLCD_set_backlight_obj) }, - { MP_ROM_QSTR(MP_QSTR_set_pen), MP_ROM_PTR(&BreakoutRoundLCD_set_pen_obj) }, - { MP_ROM_QSTR(MP_QSTR_create_pen), MP_ROM_PTR(&BreakoutRoundLCD_create_pen_obj) }, - { MP_ROM_QSTR(MP_QSTR_set_clip), MP_ROM_PTR(&BreakoutRoundLCD_set_clip_obj) }, - { MP_ROM_QSTR(MP_QSTR_remove_clip), MP_ROM_PTR(&BreakoutRoundLCD_remove_clip_obj) }, - { MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&BreakoutRoundLCD_clear_obj) }, - { MP_ROM_QSTR(MP_QSTR_pixel), MP_ROM_PTR(&BreakoutRoundLCD_pixel_obj) }, - { MP_ROM_QSTR(MP_QSTR_pixel_span), MP_ROM_PTR(&BreakoutRoundLCD_pixel_span_obj) }, - { MP_ROM_QSTR(MP_QSTR_rectangle), MP_ROM_PTR(&BreakoutRoundLCD_rectangle_obj) }, - { MP_ROM_QSTR(MP_QSTR_circle), MP_ROM_PTR(&BreakoutRoundLCD_circle_obj) }, - { MP_ROM_QSTR(MP_QSTR_character), MP_ROM_PTR(&BreakoutRoundLCD_character_obj) }, - { MP_ROM_QSTR(MP_QSTR_text), MP_ROM_PTR(&BreakoutRoundLCD_text_obj) }, - { MP_ROM_QSTR(MP_QSTR_polygon), MP_ROM_PTR(&BreakoutRoundLCD_polygon_obj) }, - { MP_ROM_QSTR(MP_QSTR_triangle), MP_ROM_PTR(&BreakoutRoundLCD_triangle_obj) }, - { MP_ROM_QSTR(MP_QSTR_line), MP_ROM_PTR(&BreakoutRoundLCD_line_obj) }, - { MP_ROM_QSTR(MP_QSTR_WIDTH), MP_ROM_INT(WIDTH) }, - { MP_ROM_QSTR(MP_QSTR_HEIGHT), MP_ROM_INT(HEIGHT) }, -}; -STATIC MP_DEFINE_CONST_DICT(BreakoutRoundLCD_locals_dict, BreakoutRoundLCD_locals_dict_table); - -/***** Class Definition *****/ -const mp_obj_type_t breakout_roundlcd_BreakoutRoundLCD_type = { - { &mp_type_type }, - .name = MP_QSTR_BreakoutRoundLCD, - .print = BreakoutRoundLCD_print, - .make_new = BreakoutRoundLCD_make_new, - .locals_dict = (mp_obj_dict_t*)&BreakoutRoundLCD_locals_dict, -}; - - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// breakout_roundlcd Module -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/***** Globals Table *****/ -STATIC const mp_map_elem_t breakout_roundlcd_globals_table[] = { - { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_breakout_roundlcd) }, - { MP_OBJ_NEW_QSTR(MP_QSTR_BreakoutRoundLCD), (mp_obj_t)&breakout_roundlcd_BreakoutRoundLCD_type }, -}; -STATIC MP_DEFINE_CONST_DICT(mp_module_breakout_roundlcd_globals, breakout_roundlcd_globals_table); - -/***** Module Definition *****/ -const mp_obj_module_t breakout_roundlcd_user_cmodule = { - .base = { &mp_type_module }, - .globals = (mp_obj_dict_t*)&mp_module_breakout_roundlcd_globals, -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -MP_REGISTER_MODULE(MP_QSTR_breakout_roundlcd, breakout_roundlcd_user_cmodule, MODULE_BREAKOUT_ROUNDLCD_ENABLED); -//////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/micropython/modules/breakout_roundlcd/breakout_roundlcd.cpp b/micropython/modules/breakout_roundlcd/breakout_roundlcd.cpp deleted file mode 100644 index 282c68f5..00000000 --- a/micropython/modules/breakout_roundlcd/breakout_roundlcd.cpp +++ /dev/null @@ -1,512 +0,0 @@ -#include "libraries/breakout_roundlcd/breakout_roundlcd.hpp" - -#include "micropython/modules/util.hpp" - - - -using namespace pimoroni; - -extern "C" { -#include "breakout_roundlcd.h" - -/***** Variables Struct *****/ -typedef struct _breakout_roundlcd_BreakoutRoundLCD_obj_t { - mp_obj_base_t base; - BreakoutRoundLCD *breakout; -} breakout_roundlcd_BreakoutRoundLCD_obj_t; - -/***** Print *****/ -void BreakoutRoundLCD_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { - (void)kind; //Unused input parameter - breakout_roundlcd_BreakoutRoundLCD_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_roundlcd_BreakoutRoundLCD_obj_t); - BreakoutRoundLCD* breakout = self->breakout; - mp_print_str(print, "BreakoutRoundLCD("); - - mp_print_str(print, "spi = "); - mp_obj_print_helper(print, mp_obj_new_int((breakout->get_spi() == spi0) ? 0 : 1), PRINT_REPR); - - mp_print_str(print, ", cs = "); - mp_obj_print_helper(print, mp_obj_new_int(breakout->get_cs()), PRINT_REPR); - - mp_print_str(print, ", dc = "); - mp_obj_print_helper(print, mp_obj_new_int(breakout->get_dc()), PRINT_REPR); - - mp_print_str(print, ", sck = "); - mp_obj_print_helper(print, mp_obj_new_int(breakout->get_sck()), PRINT_REPR); - - mp_print_str(print, ", mosi = "); - mp_obj_print_helper(print, mp_obj_new_int(breakout->get_mosi()), PRINT_REPR); - - mp_print_str(print, ", bl = "); - mp_obj_print_helper(print, mp_obj_new_int(breakout->get_bl()), PRINT_REPR); - - mp_print_str(print, ")"); -} - -/***** Constructor *****/ -mp_obj_t BreakoutRoundLCD_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - breakout_roundlcd_BreakoutRoundLCD_obj_t *self = nullptr; - - if(n_args + n_kw == 2) { - enum { ARG_buffer, ARG_slot }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_slot, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - // Parse args. - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - int slot = args[ARG_slot].u_int; - if(slot == BG_SPI_FRONT || slot == BG_SPI_BACK) { - self = m_new_obj(breakout_roundlcd_BreakoutRoundLCD_obj_t); - self->base.type = &breakout_roundlcd_BreakoutRoundLCD_type; - - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_RW); - - self->breakout = new BreakoutRoundLCD((uint16_t *)bufinfo.buf, (BG_SPI_SLOT)slot); - } - else { - mp_raise_ValueError("slot not a valid value. Expected 0 to 1"); - } - } - else { - enum { ARG_buffer, ARG_spi, ARG_cs, ARG_dc, ARG_sck, ARG_mosi, ARG_bl }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_spi, MP_ARG_INT, {.u_int = -1} }, - { MP_QSTR_cs, MP_ARG_INT, {.u_int = pimoroni::SPI_BG_FRONT_CS} }, - { MP_QSTR_dc, MP_ARG_INT, {.u_int = pimoroni::SPI_DEFAULT_MISO} }, - { MP_QSTR_sck, MP_ARG_INT, {.u_int = pimoroni::SPI_DEFAULT_SCK} }, - { MP_QSTR_mosi, MP_ARG_INT, {.u_int = pimoroni::SPI_DEFAULT_MOSI} }, - { MP_QSTR_bl, MP_ARG_INT, {.u_int = pimoroni::SPI_BG_FRONT_PWM} }, - }; - - // Parse args. - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_RW); - - // Get SPI bus. - int spi_id = args[ARG_spi].u_int; - int sck = args[ARG_sck].u_int; - int mosi = args[ARG_mosi].u_int; - - if(spi_id == -1) { - spi_id = (sck >> 3) & 0b1; // If no spi specified, choose the one for the given SCK pin - } - if(spi_id < 0 || spi_id > 1) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("SPI(%d) doesn't exist"), spi_id); - } - - if(!IS_VALID_SCK(spi_id, sck)) { - mp_raise_ValueError(MP_ERROR_TEXT("bad SCK pin")); - } - - if(!IS_VALID_MOSI(spi_id, mosi)) { - mp_raise_ValueError(MP_ERROR_TEXT("bad MOSI pin")); - } - - self = m_new_obj(breakout_roundlcd_BreakoutRoundLCD_obj_t); - self->base.type = &breakout_roundlcd_BreakoutRoundLCD_type; - - spi_inst_t *spi = (spi_id == 0) ? spi0 : spi1; - self->breakout = new BreakoutRoundLCD((uint16_t *)bufinfo.buf, spi, - args[ARG_cs].u_int, args[ARG_dc].u_int, sck, mosi, args[ARG_bl].u_int); - } - - self->breakout->init(); - - return MP_OBJ_FROM_PTR(self); -} - -/***** Methods *****/ -mp_obj_t BreakoutRoundLCD_update(mp_obj_t self_in) { - breakout_roundlcd_BreakoutRoundLCD_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_roundlcd_BreakoutRoundLCD_obj_t); - self->breakout->update(); - - return mp_const_none; -} - -mp_obj_t BreakoutRoundLCD_set_backlight(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_brightness }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_brightness, MP_ARG_REQUIRED | MP_ARG_OBJ }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - breakout_roundlcd_BreakoutRoundLCD_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_roundlcd_BreakoutRoundLCD_obj_t); - - float brightness = mp_obj_get_float(args[ARG_brightness].u_obj); - - if(brightness < 0 || brightness > 1.0f) - mp_raise_ValueError("brightness out of range. Expected 0.0 to 1.0"); - else - self->breakout->set_backlight((uint8_t)(brightness * 255.0f)); - - return mp_const_none; -} - -mp_obj_t BreakoutRoundLCD_set_pen(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - - if(n_args <= 2) { - enum { ARG_self, ARG_pen }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_pen, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - breakout_roundlcd_BreakoutRoundLCD_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_roundlcd_BreakoutRoundLCD_obj_t); - - int pen = args[ARG_pen].u_int; - - if(pen < 0 || pen > 0xffff) - mp_raise_ValueError("p is not a valid pen."); - else - self->breakout->set_pen(pen); - } - else { - enum { ARG_self, ARG_r, ARG_g, ARG_b }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_r, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_g, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_b, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - breakout_roundlcd_BreakoutRoundLCD_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_roundlcd_BreakoutRoundLCD_obj_t); - - int r = args[ARG_r].u_int; - int g = args[ARG_g].u_int; - int b = args[ARG_b].u_int; - - if(r < 0 || r > 255) - mp_raise_ValueError("r out of range. Expected 0 to 255"); - else if(g < 0 || g > 255) - mp_raise_ValueError("g out of range. Expected 0 to 255"); - else if(b < 0 || b > 255) - mp_raise_ValueError("b out of range. Expected 0 to 255"); - else - self->breakout->set_pen(r, g, b); - } - - return mp_const_none; -} - -mp_obj_t BreakoutRoundLCD_create_pen(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - int pen = 0; - - enum { ARG_self, ARG_r, ARG_g, ARG_b }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_r, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_g, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_b, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - breakout_roundlcd_BreakoutRoundLCD_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_roundlcd_BreakoutRoundLCD_obj_t); - - int r = args[ARG_r].u_int; - int g = args[ARG_g].u_int; - int b = args[ARG_b].u_int; - - if(r < 0 || r > 255) - mp_raise_ValueError("r out of range. Expected 0 to 255"); - else if(g < 0 || g > 255) - mp_raise_ValueError("g out of range. Expected 0 to 255"); - else if(b < 0 || b > 255) - mp_raise_ValueError("b out of range. Expected 0 to 255"); - else - pen = self->breakout->create_pen(r, g, b); - - return mp_obj_new_int(pen); -} - -mp_obj_t BreakoutRoundLCD_set_clip(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_x, ARG_y, ARG_w, ARG_h }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_w, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_h, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - breakout_roundlcd_BreakoutRoundLCD_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_roundlcd_BreakoutRoundLCD_obj_t); - - int x = args[ARG_x].u_int; - int y = args[ARG_y].u_int; - int w = args[ARG_w].u_int; - int h = args[ARG_h].u_int; - - Rect r(x, y, w, h); - self->breakout->set_clip(r); - - return mp_const_none; -} - -mp_obj_t BreakoutRoundLCD_remove_clip(mp_obj_t self_in) { - breakout_roundlcd_BreakoutRoundLCD_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_roundlcd_BreakoutRoundLCD_obj_t); - self->breakout->remove_clip(); - - return mp_const_none; -} - -mp_obj_t BreakoutRoundLCD_clear(mp_obj_t self_in) { - breakout_roundlcd_BreakoutRoundLCD_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_roundlcd_BreakoutRoundLCD_obj_t); - self->breakout->clear(); - - return mp_const_none; -} - -mp_obj_t BreakoutRoundLCD_pixel(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_x, ARG_y }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - breakout_roundlcd_BreakoutRoundLCD_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_roundlcd_BreakoutRoundLCD_obj_t); - - int x = args[ARG_x].u_int; - int y = args[ARG_y].u_int; - - Point p(x, y); - self->breakout->pixel(p); - - return mp_const_none; -} - -mp_obj_t BreakoutRoundLCD_pixel_span(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_x, ARG_y, ARG_l }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_l, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - breakout_roundlcd_BreakoutRoundLCD_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_roundlcd_BreakoutRoundLCD_obj_t); - - int x = args[ARG_x].u_int; - int y = args[ARG_y].u_int; - int l = args[ARG_l].u_int; - - Point p(x, y); - self->breakout->pixel_span(p, l); - - return mp_const_none; -} - -mp_obj_t BreakoutRoundLCD_rectangle(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_x, ARG_y, ARG_w, ARG_h }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_w, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_h, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - breakout_roundlcd_BreakoutRoundLCD_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_roundlcd_BreakoutRoundLCD_obj_t); - - int x = args[ARG_x].u_int; - int y = args[ARG_y].u_int; - int w = args[ARG_w].u_int; - int h = args[ARG_h].u_int; - - Rect r(x, y, w, h); - self->breakout->rectangle(r); - - return mp_const_none; -} - -mp_obj_t BreakoutRoundLCD_circle(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_x, ARG_y, ARG_r }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_r, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - breakout_roundlcd_BreakoutRoundLCD_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_roundlcd_BreakoutRoundLCD_obj_t); - - int x = args[ARG_x].u_int; - int y = args[ARG_y].u_int; - int r = args[ARG_r].u_int; - - Point p(x, y); - self->breakout->circle(p, r); - - return mp_const_none; -} - -mp_obj_t BreakoutRoundLCD_character(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_char, ARG_x, ARG_y, ARG_scale }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_char, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_scale, MP_ARG_INT, {.u_int = 2} }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - breakout_roundlcd_BreakoutRoundLCD_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_roundlcd_BreakoutRoundLCD_obj_t); - - int c = mp_obj_get_int(args[ARG_char].u_obj); - int x = args[ARG_x].u_int; - int y = args[ARG_y].u_int; - int scale = args[ARG_scale].u_int; - - self->breakout->character((char)c, Point(x, y), scale); - - return mp_const_none; -} - -mp_obj_t BreakoutRoundLCD_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_text, ARG_x, ARG_y, ARG_wrap, ARG_scale }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_text, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_wordwrap, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_scale, MP_ARG_INT, {.u_int = 2} }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - breakout_roundlcd_BreakoutRoundLCD_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_roundlcd_BreakoutRoundLCD_obj_t); - - mp_obj_t text_obj = args[ARG_text].u_obj; - if(mp_obj_is_str_or_bytes(text_obj)) { - GET_STR_DATA_LEN(text_obj, str, str_len); - - std::string t((const char*)str); - - int x = args[ARG_x].u_int; - int y = args[ARG_y].u_int; - int wrap = args[ARG_wrap].u_int; - int scale = args[ARG_scale].u_int; - - self->breakout->text(t, Point(x, y), wrap, scale); - } - else if(mp_obj_is_float(text_obj)) { - mp_raise_TypeError("can't convert 'float' object to str implicitly"); - } - else if(mp_obj_is_int(text_obj)) { - mp_raise_TypeError("can't convert 'int' object to str implicitly"); - } - else if(mp_obj_is_bool(text_obj)) { - mp_raise_TypeError("can't convert 'bool' object to str implicitly"); - } - else { - mp_raise_TypeError("can't convert object to str implicitly"); - } - - return mp_const_none; -} - -mp_obj_t BreakoutRoundLCD_polygon(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - - mp_raise_NotImplementedError("polygon is not implemented. Please avoid using this function for now"); - - return mp_const_none; -} - -mp_obj_t BreakoutRoundLCD_triangle(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_x1, ARG_y1, ARG_x2, ARG_y2, ARG_x3, ARG_y3 }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_x2, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y2, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_x3, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y3, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - breakout_roundlcd_BreakoutRoundLCD_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_roundlcd_BreakoutRoundLCD_obj_t); - - int x1 = args[ARG_x1].u_int; - int y1 = args[ARG_y1].u_int; - int x2 = args[ARG_x2].u_int; - int y2 = args[ARG_y2].u_int; - int x3 = args[ARG_x3].u_int; - int y3 = args[ARG_y3].u_int; - - Point p1(x1, y1); - Point p2(x2, y2); - Point p3(x3, y3); - self->breakout->triangle(p1, p2, p3); - - return mp_const_none; -} - -mp_obj_t BreakoutRoundLCD_line(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_x1, ARG_y1, ARG_x2, ARG_y2 }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_x2, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y2, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - breakout_roundlcd_BreakoutRoundLCD_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_roundlcd_BreakoutRoundLCD_obj_t); - - int x1 = args[ARG_x1].u_int; - int y1 = args[ARG_y1].u_int; - int x2 = args[ARG_x2].u_int; - int y2 = args[ARG_y2].u_int; - - Point p1(x1, y1); - Point p2(x2, y2); - self->breakout->line(p1, p2); - - return mp_const_none; -} -} diff --git a/micropython/modules/breakout_roundlcd/breakout_roundlcd.h b/micropython/modules/breakout_roundlcd/breakout_roundlcd.h deleted file mode 100644 index 8a390d09..00000000 --- a/micropython/modules/breakout_roundlcd/breakout_roundlcd.h +++ /dev/null @@ -1,32 +0,0 @@ -// Include MicroPython API. -#include "py/runtime.h" -#include "py/objstr.h" - -/***** Constants *****/ -static const int WIDTH = 240; -static const int HEIGHT = 240; - - -/***** Extern of Class Definition *****/ -extern const mp_obj_type_t breakout_roundlcd_BreakoutRoundLCD_type; - -/***** Extern of Class Methods *****/ -extern void BreakoutRoundLCD_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind); -extern mp_obj_t BreakoutRoundLCD_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args); -extern mp_obj_t BreakoutRoundLCD_update(mp_obj_t self_in); -extern mp_obj_t BreakoutRoundLCD_set_backlight(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); - -extern mp_obj_t BreakoutRoundLCD_set_pen(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t BreakoutRoundLCD_create_pen(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t BreakoutRoundLCD_set_clip(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t BreakoutRoundLCD_remove_clip(mp_obj_t self_in); -extern mp_obj_t BreakoutRoundLCD_clear(mp_obj_t self_in); -extern mp_obj_t BreakoutRoundLCD_pixel(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t BreakoutRoundLCD_pixel_span(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t BreakoutRoundLCD_rectangle(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t BreakoutRoundLCD_circle(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t BreakoutRoundLCD_character(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t BreakoutRoundLCD_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t BreakoutRoundLCD_polygon(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t BreakoutRoundLCD_triangle(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t BreakoutRoundLCD_line(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); \ No newline at end of file diff --git a/micropython/modules/breakout_roundlcd/micropython.cmake b/micropython/modules/breakout_roundlcd/micropython.cmake deleted file mode 100644 index aeb65d94..00000000 --- a/micropython/modules/breakout_roundlcd/micropython.cmake +++ /dev/null @@ -1,20 +0,0 @@ -set(MOD_NAME breakout_roundlcd) -string(TOUPPER ${MOD_NAME} MOD_NAME_UPPER) -add_library(usermod_${MOD_NAME} INTERFACE) - -target_sources(usermod_${MOD_NAME} INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.c - ${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.cpp - ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/${MOD_NAME}/${MOD_NAME}.cpp - ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/st7789/st7789.cpp -) - -target_include_directories(usermod_${MOD_NAME} INTERFACE - ${CMAKE_CURRENT_LIST_DIR} -) - -target_compile_definitions(usermod_${MOD_NAME} INTERFACE - -DMODULE_${MOD_NAME_UPPER}_ENABLED=1 -) - -target_link_libraries(usermod INTERFACE usermod_${MOD_NAME}) \ No newline at end of file diff --git a/micropython/modules/breakout_rtc/breakout_rtc.c b/micropython/modules/breakout_rtc/breakout_rtc.c index 358c25d4..c91a420d 100644 --- a/micropython/modules/breakout_rtc/breakout_rtc.c +++ b/micropython/modules/breakout_rtc/breakout_rtc.c @@ -161,6 +161,10 @@ const mp_obj_module_t breakout_rtc_user_cmodule = { }; //////////////////////////////////////////////////////////////////////////////////////////////////// +#if MICROPY_VERSION <= 70144 MP_REGISTER_MODULE(MP_QSTR_breakout_rtc, breakout_rtc_user_cmodule, MODULE_BREAKOUT_RTC_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_breakout_rtc, breakout_rtc_user_cmodule); +#endif //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/micropython/modules/breakout_scd41/breakout_scd41.c b/micropython/modules/breakout_scd41/breakout_scd41.c index d8a78021..61a0d8b9 100755 --- a/micropython/modules/breakout_scd41/breakout_scd41.c +++ b/micropython/modules/breakout_scd41/breakout_scd41.c @@ -49,6 +49,10 @@ const mp_obj_module_t scd41_user_cmodule = { }; //////////////////////////////////////////////////////////////////////////////////////////////////// +#if MICROPY_VERSION <= 70144 MP_REGISTER_MODULE(MP_QSTR_breakout_scd41, scd41_user_cmodule, MODULE_BREAKOUT_SCD41_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_breakout_scd41, scd41_user_cmodule); +#endif //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/micropython/modules/breakout_sgp30/breakout_sgp30.c b/micropython/modules/breakout_sgp30/breakout_sgp30.c index 6b3677bd..39ad456f 100644 --- a/micropython/modules/breakout_sgp30/breakout_sgp30.c +++ b/micropython/modules/breakout_sgp30/breakout_sgp30.c @@ -60,6 +60,10 @@ const mp_obj_module_t breakout_sgp30_user_cmodule = { }; //////////////////////////////////////////////////////////////////////////////////////////////////// +#if MICROPY_VERSION <= 70144 MP_REGISTER_MODULE(MP_QSTR_breakout_sgp30, breakout_sgp30_user_cmodule, MODULE_BREAKOUT_SGP30_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_breakout_sgp30, breakout_sgp30_user_cmodule); +#endif //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/micropython/modules/breakout_trackball/breakout_trackball.c b/micropython/modules/breakout_trackball/breakout_trackball.c index fe66ea26..ee118231 100644 --- a/micropython/modules/breakout_trackball/breakout_trackball.c +++ b/micropython/modules/breakout_trackball/breakout_trackball.c @@ -62,6 +62,10 @@ const mp_obj_module_t breakout_trackball_user_cmodule = { }; //////////////////////////////////////////////////////////////////////////////////////////////////// +#if MICROPY_VERSION <= 70144 MP_REGISTER_MODULE(MP_QSTR_breakout_trackball, breakout_trackball_user_cmodule, MODULE_BREAKOUT_TRACKBALL_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_breakout_trackball, breakout_trackball_user_cmodule); +#endif //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/micropython/modules/breakout_vl53l5cx/vl53l5cx.c b/micropython/modules/breakout_vl53l5cx/vl53l5cx.c index fa0725e0..7700add3 100644 --- a/micropython/modules/breakout_vl53l5cx/vl53l5cx.c +++ b/micropython/modules/breakout_vl53l5cx/vl53l5cx.c @@ -80,4 +80,8 @@ const mp_obj_module_t vl53l5cx_user_cmodule = { }; /***** Module Registration: as "breakout_vl53l5cx" *****/ +#if MICROPY_VERSION <= 70144 MP_REGISTER_MODULE(MP_QSTR_breakout_vl53l5cx, vl53l5cx_user_cmodule, MODULE_VL53L5CX_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_breakout_vl53l5cx, vl53l5cx_user_cmodule); +#endif diff --git a/micropython/modules/encoder/encoder.c b/micropython/modules/encoder/encoder.c index 2fedf54f..52f389b2 100644 --- a/micropython/modules/encoder/encoder.c +++ b/micropython/modules/encoder/encoder.c @@ -63,4 +63,8 @@ const mp_obj_module_t encoder_user_cmodule = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_encoder_globals, }; +#if MICROPY_VERSION <= 70144 MP_REGISTER_MODULE(MP_QSTR_encoder, encoder_user_cmodule, MODULE_ENCODER_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_encoder, encoder_user_cmodule); +#endif diff --git a/micropython/modules/hub75/hub75.c b/micropython/modules/hub75/hub75.c index cf11a8eb..39d49f80 100644 --- a/micropython/modules/hub75/hub75.c +++ b/micropython/modules/hub75/hub75.c @@ -74,4 +74,8 @@ const mp_obj_module_t hub75_user_cmodule = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_hub75_globals, }; -MP_REGISTER_MODULE(MP_QSTR_hub75, hub75_user_cmodule, MODULE_HUB75_ENABLED); \ No newline at end of file +#if MICROPY_VERSION <= 70144 +MP_REGISTER_MODULE(MP_QSTR_hub75, hub75_user_cmodule, MODULE_HUB75_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_hub75, hub75_user_cmodule); +#endif \ No newline at end of file diff --git a/micropython/modules/jpegdec/jpegdec.c b/micropython/modules/jpegdec/jpegdec.c new file mode 100644 index 00000000..cebb6fb5 --- /dev/null +++ b/micropython/modules/jpegdec/jpegdec.c @@ -0,0 +1,53 @@ +#include "jpegdec.h" + +STATIC MP_DEFINE_CONST_FUN_OBJ_1(JPEG_del_obj, _JPEG_del); +STATIC MP_DEFINE_CONST_FUN_OBJ_2(JPEG_openRAM_obj, _JPEG_openRAM); +STATIC MP_DEFINE_CONST_FUN_OBJ_2(JPEG_openFILE_obj, _JPEG_openFILE); +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(JPEG_decode_obj, 1, _JPEG_decode); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(JPEG_getWidth_obj, _JPEG_getWidth); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(JPEG_getHeight_obj, _JPEG_getHeight); + +// class +STATIC const mp_rom_map_elem_t JPEG_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&JPEG_del_obj) }, + { MP_ROM_QSTR(MP_QSTR_open_RAM), MP_ROM_PTR(&JPEG_openRAM_obj) }, + { MP_ROM_QSTR(MP_QSTR_open_file), MP_ROM_PTR(&JPEG_openFILE_obj) }, + { MP_ROM_QSTR(MP_QSTR_decode), MP_ROM_PTR(&JPEG_decode_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_width), MP_ROM_PTR(&JPEG_getWidth_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_height), MP_ROM_PTR(&JPEG_getHeight_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_height), MP_ROM_PTR(&JPEG_getHeight_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(JPEG_locals_dict, JPEG_locals_dict_table); + +const mp_obj_type_t JPEG_type = { + { &mp_type_type }, + .name = MP_QSTR_jpegdec, + //.print = _JPEG_print, + .make_new = _JPEG_make_new, + .locals_dict = (mp_obj_dict_t*)&JPEG_locals_dict, +}; + +// module +STATIC const mp_map_elem_t JPEG_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_jpegdec) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_JPEG), (mp_obj_t)&JPEG_type }, + + { MP_ROM_QSTR(MP_QSTR_JPEG_SCALE_FULL), MP_ROM_INT(0) }, + { MP_ROM_QSTR(MP_QSTR_JPEG_SCALE_HALF), MP_ROM_INT(2) }, + { MP_ROM_QSTR(MP_QSTR_JPEG_SCALE_QUARTER), MP_ROM_INT(4) }, + { MP_ROM_QSTR(MP_QSTR_JPEG_SCALE_EIGHTH), MP_ROM_INT(8) }, +}; + +STATIC MP_DEFINE_CONST_DICT(mp_module_JPEG_globals, JPEG_globals_table); + +const mp_obj_module_t JPEG_user_cmodule = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_JPEG_globals, +}; + +#if MICROPY_VERSION <= 70144 +MP_REGISTER_MODULE(MP_QSTR_jpegdec, JPEG_user_cmodule, MODULE_JPEGDEC_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_jpegdec, JPEG_user_cmodule); +#endif \ No newline at end of file diff --git a/micropython/modules/jpegdec/jpegdec.cpp b/micropython/modules/jpegdec/jpegdec.cpp new file mode 100644 index 00000000..9791aa56 --- /dev/null +++ b/micropython/modules/jpegdec/jpegdec.cpp @@ -0,0 +1,198 @@ +#include "libraries/jpegdec/JPEGDEC.h" + +#include "micropython/modules/util.hpp" +#include "libraries/pico_graphics/pico_graphics.hpp" + +using namespace pimoroni; + +extern "C" { +#include "jpegdec.h" +#include "micropython/modules/picographics/picographics.h" +#include "py/stream.h" +#include "py/reader.h" +#include "extmod/vfs.h" + +typedef struct _ModPicoGraphics_obj_t { + mp_obj_base_t base; + PicoGraphics *graphics; + DisplayDriver *display; + void *buffer; +} ModPicoGraphics_obj_t; + +typedef struct _JPEG_obj_t { + mp_obj_base_t base; + JPEGDEC *jpeg; + mp_buffer_info_t buf; + ModPicoGraphics_obj_t *graphics; +} _JPEG_obj_t; + +PicoGraphics *current_graphics = nullptr; + +int JPEGDraw(JPEGDRAW *pDraw) { +#ifdef MICROPY_EVENT_POLL_HOOK +MICROPY_EVENT_POLL_HOOK +#endif + // "pixel" is slow and clipped, + // guaranteeing we wont draw jpeg data out of the framebuffer.. + // Can we clip beforehand and make this faster? + if(pDraw->iBpp == 4) { + 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 / 2]; + p >>= (i & 0b1) ? 0 : 4; + p &= 0xf; + 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++) { + int i = y * pDraw->iWidth + x; + if (current_graphics->pen_type == PicoGraphics::PEN_RGB332) { + //current_graphics->set_pen(RGB((RGB565)pDraw->pPixels[i]).to_rgb332()); + //current_graphics->pixel({pDraw->x + x, pDraw->y + y}); + // TODO make dither optional + current_graphics->set_pixel_dither({pDraw->x + x, pDraw->y + y}, (RGB565)(pDraw->pPixels[i])); + } else if (current_graphics->pen_type == PicoGraphics::PEN_P8 || current_graphics->pen_type == PicoGraphics::PEN_P4) { + current_graphics->set_pixel_dither({pDraw->x + x, pDraw->y + y}, RGB((RGB565)pDraw->pPixels[i])); + } else { + current_graphics->set_pen(pDraw->pPixels[i]); + current_graphics->pixel({pDraw->x + x, pDraw->y + y}); + } + } + } + } + return 1; +} + +mp_obj_t _JPEG_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { + ARG_picographics + }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_picographics, MP_ARG_REQUIRED | MP_ARG_OBJ }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + if(!MP_OBJ_IS_TYPE(args[ARG_picographics].u_obj, &ModPicoGraphics_type)) mp_raise_ValueError(MP_ERROR_TEXT("PicoGraphics Object Required")); + + _JPEG_obj_t *self = m_new_obj_with_finaliser(_JPEG_obj_t); + self->base.type = &JPEG_type; + self->jpeg = m_new_class(JPEGDEC); + self->graphics = (ModPicoGraphics_obj_t *)MP_OBJ_TO_PTR(args[ARG_picographics].u_obj); + return self; +} + +mp_obj_t _JPEG_del(mp_obj_t self_in) { + _JPEG_obj_t *self = MP_OBJ_TO_PTR2(self_in, _JPEG_obj_t); + self->jpeg->close(); + return mp_const_none; +} + +static int _open(_JPEG_obj_t *self) { + int result = self->jpeg->openRAM((uint8_t *)self->buf.buf, self->buf.len, JPEGDraw); + if (result == 1) { + switch(self->graphics->graphics->pen_type) { + case PicoGraphics::PEN_RGB332: + case PicoGraphics::PEN_RGB565: + case PicoGraphics::PEN_P8: + case PicoGraphics::PEN_P4: + self->jpeg->setPixelType(RGB565_BIG_ENDIAN); + break; + // TODO 2-bit is currently unsupported + case PicoGraphics::PEN_P2: + self->jpeg->setPixelType(TWO_BIT_DITHERED); + break; + case PicoGraphics::PEN_1BIT: + self->jpeg->setPixelType(ONE_BIT_DITHERED); + break; + } + } + return result; +} + +// open_FILE +mp_obj_t _JPEG_openFILE(mp_obj_t self_in, mp_obj_t filename) { + _JPEG_obj_t *self = MP_OBJ_TO_PTR2(self_in, _JPEG_obj_t); + mp_obj_t args[2] = { + filename, + MP_OBJ_NEW_QSTR(MP_QSTR_r), + }; + + // Stat the file to get its size + // example tuple response: (32768, 0, 0, 0, 0, 0, 5153, 1654709815, 1654709815, 1654709815) + mp_obj_t stat = mp_vfs_stat(filename); + mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR2(stat, mp_obj_tuple_t); + size_t filesize = mp_obj_get_int(tuple->items[6]); + + self->buf.buf = (void *)m_new(uint8_t, filesize); + mp_obj_t file = mp_vfs_open(MP_ARRAY_SIZE(args), &args[0], (mp_map_t *)&mp_const_empty_map); + int errcode; + self->buf.len = mp_stream_rw(file, self->buf.buf, filesize, &errcode, MP_STREAM_RW_READ | MP_STREAM_RW_ONCE); + if (errcode != 0) { + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Failed to open file!")); + } + return mp_const_true; +} + +// open_RAM +mp_obj_t _JPEG_openRAM(mp_obj_t self_in, mp_obj_t buffer) { + _JPEG_obj_t *self = MP_OBJ_TO_PTR2(self_in, _JPEG_obj_t); + mp_get_buffer_raise(buffer, &self->buf, MP_BUFFER_READ); + return mp_const_true; +} + +// decode +mp_obj_t _JPEG_decode(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_x, ARG_y, ARG_scale }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_x, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_y, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_scale, MP_ARG_INT, {.u_int = 0} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + _JPEG_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _JPEG_obj_t); + + int x = args[ARG_x].u_int; + int y = args[ARG_y].u_int; + int f = args[ARG_scale].u_int; + + if(_open(self) != 1) return mp_const_false; + + // We need to store a pointer to the PicoGraphics surface + // since the JPEGDRAW struct has no userdata + current_graphics = self->graphics->graphics; + int result = -1; + + if(self->graphics->graphics->pen_type == PicoGraphics::PEN_P4) { + uint8_t *buf = new uint8_t[2048]; + result = self->jpeg->decodeDither(x, y, buf, f); + delete[] buf; + } else { + result = self->jpeg->decode(x, y, f); + } + current_graphics = nullptr; + return result == 1 ? mp_const_true : mp_const_false; +} + +// get_width +mp_obj_t _JPEG_getWidth(mp_obj_t self_in) { + _JPEG_obj_t *self = MP_OBJ_TO_PTR2(self_in, _JPEG_obj_t); + return mp_obj_new_int(self->jpeg->getWidth()); +} + +// get_height +mp_obj_t _JPEG_getHeight(mp_obj_t self_in) { + _JPEG_obj_t *self = MP_OBJ_TO_PTR2(self_in, _JPEG_obj_t); + return mp_obj_new_int(self->jpeg->getHeight()); +} + +} diff --git a/micropython/modules/jpegdec/jpegdec.h b/micropython/modules/jpegdec/jpegdec.h new file mode 100644 index 00000000..fd1a59de --- /dev/null +++ b/micropython/modules/jpegdec/jpegdec.h @@ -0,0 +1,12 @@ +#include "py/runtime.h" +#include "py/objstr.h" + +extern const mp_obj_type_t JPEG_type; + +extern mp_obj_t _JPEG_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args); +extern mp_obj_t _JPEG_del(mp_obj_t self_in); +extern mp_obj_t _JPEG_openRAM(mp_obj_t self_in, mp_obj_t buffer); +extern mp_obj_t _JPEG_openFILE(mp_obj_t self_in, mp_obj_t filename); +extern mp_obj_t _JPEG_decode(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t _JPEG_getWidth(mp_obj_t self_in); +extern mp_obj_t _JPEG_getHeight(mp_obj_t self_in); \ No newline at end of file diff --git a/micropython/modules/jpegdec/micropython.cmake b/micropython/modules/jpegdec/micropython.cmake new file mode 100644 index 00000000..2defc4da --- /dev/null +++ b/micropython/modules/jpegdec/micropython.cmake @@ -0,0 +1,19 @@ +add_library(usermod_jpegdec INTERFACE) + +target_sources(usermod_jpegdec INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/jpegdec.c + ${CMAKE_CURRENT_LIST_DIR}/jpegdec.cpp + ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/jpegdec/JPEGDEC.cpp +) + +target_include_directories(usermod_jpegdec INTERFACE + ${CMAKE_CURRENT_LIST_DIR} +) + +target_compile_definitions(usermod_jpegdec INTERFACE + MODULE_JPEGDEC_ENABLED=1 +) + +target_link_libraries(usermod INTERFACE usermod_jpegdec) + +set_source_files_properties(${CMAKE_CURRENT_LIST_DIR}/../../../libraries/jpegdec/JPEGDEC.cpp PROPERTIES COMPILE_FLAGS "-Wno-error=unused-function") diff --git a/micropython/modules/micropython-common.cmake b/micropython/modules/micropython-common.cmake index 7146283f..29ec616d 100644 --- a/micropython/modules/micropython-common.cmake +++ b/micropython/modules/micropython-common.cmake @@ -1,10 +1,10 @@ include(pimoroni_i2c/micropython) +include(pimoroni_bus/micropython) include(breakout_dotmatrix/micropython) include(breakout_encoder/micropython) include(breakout_ioexpander/micropython) include(breakout_ltr559/micropython) -include(breakout_colourlcd160x80/micropython) include(breakout_as7262/micropython) include(breakout_rgbmatrix5x5/micropython) include(breakout_matrix11x7/micropython) @@ -26,9 +26,10 @@ include(breakout_vl53l5cx/micropython) include(pico_scroll/micropython) include(pico_rgb_keypad/micropython) include(pico_unicorn/micropython) -include(pico_explorer/micropython) include(pico_wireless/micropython) +include(pico_explorer/micropython) +include(hershey_fonts/micropython) include(bitmap_fonts/micropython) include(plasma/micropython) @@ -39,10 +40,11 @@ include(encoder/micropython) include(motor/micropython) include(qrcode/micropython/micropython) include(adcfft/micropython) - -include(st7789/micropython) include(pcf85063a/micropython) +include(picographics/micropython) +include(jpegdec/micropython) + include(modules_py/modules_py) function(enable_ulab) diff --git a/micropython/modules/micropython-tiny2040.cmake b/micropython/modules/micropython-tiny2040.cmake index f4e90728..d7f7a2ca 100644 --- a/micropython/modules/micropython-tiny2040.cmake +++ b/micropython/modules/micropython-tiny2040.cmake @@ -5,5 +5,3 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../") list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../../") include(micropython-common) - -enable_ulab() diff --git a/micropython/modules/micropython-tufty2040.cmake b/micropython/modules/micropython-tufty2040.cmake new file mode 100644 index 00000000..d7f7a2ca --- /dev/null +++ b/micropython/modules/micropython-tufty2040.cmake @@ -0,0 +1,7 @@ +include_directories(${CMAKE_CURRENT_LIST_DIR}/../../) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../") +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../../") + +include(micropython-common) diff --git a/micropython/modules/motor/motor.c b/micropython/modules/motor/motor.c index e1d1731d..aef5c0a3 100644 --- a/micropython/modules/motor/motor.c +++ b/micropython/modules/motor/motor.c @@ -270,4 +270,8 @@ const mp_obj_module_t motor_user_cmodule = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_motor_globals, }; +#if MICROPY_VERSION <= 70144 MP_REGISTER_MODULE(MP_QSTR_motor, motor_user_cmodule, MODULE_MOTOR_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_motor, motor_user_cmodule); +#endif diff --git a/micropython/modules/pcf85063a/pcf85063a.c b/micropython/modules/pcf85063a/pcf85063a.c index 14489f15..ebd5e9fa 100644 --- a/micropython/modules/pcf85063a/pcf85063a.c +++ b/micropython/modules/pcf85063a/pcf85063a.c @@ -42,6 +42,20 @@ STATIC const mp_rom_map_elem_t PCF85063A_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_unset_timer), MP_ROM_PTR(&PCF85063A_unset_timer_obj) }, { MP_ROM_QSTR(MP_QSTR_set_clock_output), MP_ROM_PTR(&PCF85063A_set_clock_output_obj) }, + + { MP_ROM_QSTR(MP_QSTR_CLOCK_OUT_32768HZ), MP_ROM_INT(0) }, + { MP_ROM_QSTR(MP_QSTR_CLOCK_OUT_16384HZ), MP_ROM_INT(1) }, + { MP_ROM_QSTR(MP_QSTR_CLOCK_OUT_8192HZ), MP_ROM_INT(2) }, + { MP_ROM_QSTR(MP_QSTR_CLOCK_OUT_4096HZ), MP_ROM_INT(3) }, + { MP_ROM_QSTR(MP_QSTR_CLOCK_OUT_2048HZ), MP_ROM_INT(4) }, + { MP_ROM_QSTR(MP_QSTR_CLOCK_OUT_1024HZ), MP_ROM_INT(5) }, + { MP_ROM_QSTR(MP_QSTR_CLOCK_OUT_1HZ), MP_ROM_INT(6) }, + { MP_ROM_QSTR(MP_QSTR_CLOCK_OUT_OFF), MP_ROM_INT(7) }, + + { MP_ROM_QSTR(MP_QSTR_TIMER_TICK_4096HZ), MP_ROM_INT(0b00) }, + { MP_ROM_QSTR(MP_QSTR_TIMER_TICK_64HZ), MP_ROM_INT(0b01) }, + { MP_ROM_QSTR(MP_QSTR_TIMER_TICK_1HZ), MP_ROM_INT(0b10) }, + { MP_ROM_QSTR(MP_QSTR_TIMER_TICK_1_OVER_60HZ), MP_ROM_INT(0b11) }, }; STATIC MP_DEFINE_CONST_DICT(PCF85063A_locals_dict, PCF85063A_locals_dict_table); @@ -61,21 +75,6 @@ const mp_obj_type_t pcf85063a_PCF85063A_type = { STATIC const mp_map_elem_t pcf85063a_globals_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_pcf85063a) }, { MP_OBJ_NEW_QSTR(MP_QSTR_PCF85063A), (mp_obj_t)&pcf85063a_PCF85063A_type }, - - { MP_ROM_QSTR(MP_QSTR_CLOCK_OUT_32768HZ), MP_ROM_INT(0) }, - { MP_ROM_QSTR(MP_QSTR_CLOCK_OUT_16384HZ), MP_ROM_INT(1) }, - { MP_ROM_QSTR(MP_QSTR_CLOCK_OUT_8192HZ), MP_ROM_INT(2) }, - { MP_ROM_QSTR(MP_QSTR_CLOCK_OUT_4096HZ), MP_ROM_INT(3) }, - { MP_ROM_QSTR(MP_QSTR_CLOCK_OUT_2048HZ), MP_ROM_INT(4) }, - { MP_ROM_QSTR(MP_QSTR_CLOCK_OUT_1024HZ), MP_ROM_INT(5) }, - { MP_ROM_QSTR(MP_QSTR_CLOCK_OUT_1HZ), MP_ROM_INT(6) }, - { MP_ROM_QSTR(MP_QSTR_CLOCK_OUT_OFF), MP_ROM_INT(7) }, - - { MP_ROM_QSTR(MP_QSTR_TIMER_TICK_4096HZ), MP_ROM_INT(0b00) }, - { MP_ROM_QSTR(MP_QSTR_TIMER_TICK_64HZ), MP_ROM_INT(0b01) }, - { MP_ROM_QSTR(MP_QSTR_TIMER_TICK_1HZ), MP_ROM_INT(0b10) }, - { MP_ROM_QSTR(MP_QSTR_TIMER_TICK_1_OVER_60HZ), MP_ROM_INT(0b11) }, - { MP_ROM_QSTR(MP_QSTR_MONDAY), MP_ROM_INT(0) }, { MP_ROM_QSTR(MP_QSTR_TUESDAY), MP_ROM_INT(1) }, { MP_ROM_QSTR(MP_QSTR_WEDNESDAY), MP_ROM_INT(2) }, @@ -93,6 +92,10 @@ const mp_obj_module_t pcf85063a_user_cmodule = { }; //////////////////////////////////////////////////////////////////////////////////////////////////// +#if MICROPY_VERSION <= 70144 MP_REGISTER_MODULE(MP_QSTR_pcf85063a, pcf85063a_user_cmodule, MODULE_BREAKOUT_RTC_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_pcf85063a, pcf85063a_user_cmodule); +#endif //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/micropython/modules/pico_display/README.md b/micropython/modules/pico_display/README.md deleted file mode 100644 index ba25cc6f..00000000 --- a/micropython/modules/pico_display/README.md +++ /dev/null @@ -1,211 +0,0 @@ -# Pico Display Pack - MicroPython - -Pico Display Pack is a vibrant 1.14", 240 x 135 pixel IPS LCD screen for your Raspberry Pi Pico, with four useful buttons and a RGB LED. [Buy one here!](https://shop.pimoroni.com/products/pico-display-pack) - -We've included helper functions to handle every aspect of drawing to the screen and interfacing with the buttons and LED. See the [function reference](#function-reference) for details. Scroll down to the bottom for links to more resources! - -- [Example Program](#example-program) -- [Function Reference](#function-reference) - - [init](#init) - - [set_backlight](#set_backlight) - - [set_led](#set_led) - - [is_pressed](#is_pressed) - - [update](#update) - - [set_pen](#set_pen) - - [create_pen](#create_pen) - - [clear](#clear) - - [pixel](#pixel) - - [pixel_span](#pixel_span) - - [rectangle](#rectangle) - - [circle](#circle) - - [character](#character) - - [text](#text) - - [set_clip](#set_clip) - - [remove_clip](#remove_clip) - - [More Resources](#more-resources) - -## Example Program - -The following example sets up Pico Display, displays some basic demo text and illuminates the RGB LED green when the A button is pressed. - -```python -import utime -import picodisplay - -# Initialise Picodisplay with a bytearray display buffer -buf = bytearray(picodisplay.get_width() * picodisplay.get_height() * 2) -picodisplay.init(buf) -picodisplay.set_backlight(1.0) - -picodisplay.set_pen(255, 0, 0) # Set a red pen -picodisplay.clear() # Clear the display buffer -picodisplay.set_pen(255, 255, 255) # Set a white pen -picodisplay.text("pico display", 10, 10, 240, 6) # Add some text -picodisplay.update() # Update the display with our changes - -picodisplay.set_led(255, 0, 0) # Set the RGB LED to red -utime.sleep(1) # Wait for a second -picodisplay.set_led(0, 255, 0) # Set the RGB LED to green -utime.sleep(1) # Wait for a second -picodisplay.set_led(0, 0, 255) # Set the RGB LED to blue - -while picodisplay.is_pressed(picodisplay.BUTTON_A) == False: - pass - -picodisplay.set_led(0, 255, 0) # Set the RGB LED to green -``` - -## Function Reference - -### init - -Sets up Pico Display. `init` must be called before any other functions since it configures the required PWM and GPIO. `init()` needs a bytearray type display buffer that MicroPython's garbage collection isn't going to eat, make sure you create one and pass it in like so: - -```python -buf = bytearray(picodisplay.get_width() * picodisplay.get_height() * 2) -picodisplay.init(buf) -``` - -### set_backlight - -Sets the display backlight from 0.0 to 1.0. - -```python -picodisplay.set_backlight(brightness) -``` - -Uses hardware PWM to dim the display backlight, dimming values are gamma-corrected to provide smooth brightness transitions across the full range of intensity. This may result in some low values mapping as "off." - -### set_led - -Sets the RGB LED on Pico Display with an RGB triplet. - -```python -picodisplay.set_led(r, g, b) -``` - -Uses hardware PWM to drive the LED. Values are automatically gamma-corrected to provide smooth brightness transitions and low values may map as "off." - -### is_pressed - -Reads the GPIO pin connected to one of Pico Display's buttons, returning `True` if it's pressed and `False` if it is released. - -```python -picodisplay.is_pressed(button) -``` - -The button value should be a number denoting a pin, and constants `picodisplay.BUTTON_A`, `picodisplay.BUTTON_B`, `picodisplay.BUTTON_X` and `picodisplay.BUTTON_Y` are supplied to make it easier. e: - -```python -is_a_button_pressed = picodisplay.is_pressed(picodisplay.BUTTON_A) -``` - -### update - -To display your changes on Pico Display's screen you need to call `update`. - -```python -picodisplay.update() -``` - -### set_pen - -Sets the colour to be used by subsequent calls to drawing functions. The values for `r`, `g` and `b` should be from 0-255 inclusive. - -```python -picodisplay.set_pen(r, g, b) -``` - -### create_pen - -Creates a pen which can be stored as a variable for faster re-use of the same colour through calls to `set_pen`. The values for `r`, `g` and `b` should be from 0-255 inclusive. - -```python -pen_colour = picodisplay.create_pen(r, g, b) -picodisplay.set_pen(penColour) -``` - -### clear - -Fills the display buffer with the currently set pen colour. - -```python -picodisplay.clear() -``` - -### pixel - -Sets a single pixel in the display buffer to the current pen colour. The `x` and `y` parameters determine the X and Y coordinates of the drawn pixel in the buffer. - -```python -picodisplay.pixel(x, y) -``` - -### pixel_span - -Draws a horizontal line of pixels to the buffer. The `x` and `y` parameters specify the coordinates of the first pixel of the line. The `l` parameter describes the length of the line in pixels. This function will only extend the line towards the end of the screen, i.e. the `x` coordinate should specify the left hand extreme of the line. - -```python -picodisplay.pixel_span(x, y, l) -``` - -### rectangle - -Draws a rectangle filled with the current pen colour to the buffer. The `x` and `y` parameters specify the upper left corner of the rectangle, `w` specifies the width in pixels, and `h` the height. - -```python -picodisplay.rectangle(x, y, w, h) -``` - -![Rectangle function explanation image](/micropython/modules/pico_display/images/rectangle.png) - -### circle - -Draws a circle filled with the current pen colour to the buffer. The `x` and `y` parameters specify the centre of the circle, `r` specifies the radius in pixels. - -```python -picodisplay.circle(x, y, r) -``` - -![Circle function explanation image](/micropython/modules/pico_display/images/circle.png) - -### character - -Draws a single character to the display buffer in the current pen colour. The `c` parameter should be the ASCII numerical representation of the character to be printed, `x` and `y` describe the top-left corner of the character's drawing field. The `character` function can also be given an optional 4th parameter, `scale`, describing the scale of the character to be drawn. Default value is 2. - -```python -char_a = ord('a') -picodisplay.character(char_a, x, y) -picodisplay.character(char_a, x, y, scale) -``` - -### text - -Draws a string of text to the display buffer in the current pen colour. The `string` parameter is the string of text to be drawn, and `x` and `y` specify the upper left corner of the drawing field. The `wrap` parameter describes the width, in pixels, after which the next word in the string will be drawn on a new line underneath the current text. This will wrap the string over multiple lines if required. This function also has an optional parameter, `scale`, which describes the size of the characters to be drawn. The default `scale` is 2. - -```python -picodisplay.text(string, x, y, wrap) -picodisplay.text(string, x, y, wrap, scale) -``` - -![Text scale explanation image](/micropython/modules/pico_display/images/text_scale.png) - -### set_clip - -This function defines a rectangular area outside which no drawing actions will take effect. If a drawing action crosses the boundary of the clip then only the pixels inside the clip will be drawn. Note that `clip` does not remove pixels which have already been drawn, it only prevents new pixels being drawn outside the described area. A more visual description of the function of clips can be found below. Only one clip can be active at a time, and defining a new clip replaces any previous clips. The `x` and `y` parameters describe the upper-left corner of the clip area, `w` and `h` describe the width and height in pixels. - -```python -picodisplay.set_clip(x, y, w, h) -``` - -![Clip function explanation image](/micropython/modules/pico_display/images/clip.png) - -### remove_clip - -This function removes any currently implemented clip. - -## More Resources - -- [Display Pack Guide by UnfinishedStuff](https://github.com/UnfinishedStuff/Pimoroni_Pico_Display_Pack_documentation) - more detail on functions and code examples -- [Display Pack Workout by tonygo2](https://www.instructables.com/Pimoroni-Pico-Display-Workout/) - a comprehensive demo -- [picofont by Les Wright](https://github.com/leswright1977/picofont) - implementing custom fonts using pure MicroPython diff --git a/micropython/modules/pico_display/images/circle.png b/micropython/modules/pico_display/images/circle.png deleted file mode 100644 index 4efadd9c..00000000 Binary files a/micropython/modules/pico_display/images/circle.png and /dev/null differ diff --git a/micropython/modules/pico_display/images/clip.png b/micropython/modules/pico_display/images/clip.png deleted file mode 100644 index a2d28094..00000000 Binary files a/micropython/modules/pico_display/images/clip.png and /dev/null differ diff --git a/micropython/modules/pico_display/images/rectangle.png b/micropython/modules/pico_display/images/rectangle.png deleted file mode 100644 index 4e0c3467..00000000 Binary files a/micropython/modules/pico_display/images/rectangle.png and /dev/null differ diff --git a/micropython/modules/pico_display/images/text_scale.png b/micropython/modules/pico_display/images/text_scale.png deleted file mode 100644 index 1f029ed2..00000000 Binary files a/micropython/modules/pico_display/images/text_scale.png and /dev/null differ diff --git a/micropython/modules/pico_display/micropython.cmake b/micropython/modules/pico_display/micropython.cmake deleted file mode 100644 index 5735edeb..00000000 --- a/micropython/modules/pico_display/micropython.cmake +++ /dev/null @@ -1,26 +0,0 @@ -add_library(usermod_pico_display INTERFACE) - -target_sources(usermod_pico_display INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/pico_display.c - ${CMAKE_CURRENT_LIST_DIR}/pico_display.cpp - ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_display/pico_display.cpp - ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/st7789/st7789.cpp - ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/pico_graphics.cpp - ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/types.cpp -) - -target_include_directories(usermod_pico_display INTERFACE - ${CMAKE_CURRENT_LIST_DIR} -) - -target_compile_definitions(usermod_pico_display INTERFACE - MODULE_PICODISPLAY_ENABLED=1 -) - -target_link_libraries(usermod INTERFACE usermod_pico_display) - -set_source_files_properties( - ${CMAKE_CURRENT_LIST_DIR}/pico_display.c - PROPERTIES COMPILE_FLAGS - "-Wno-discarded-qualifiers -Wno-implicit-int" -) diff --git a/micropython/modules/pico_display/pico_display.c b/micropython/modules/pico_display/pico_display.c deleted file mode 100755 index ee0f70c6..00000000 --- a/micropython/modules/pico_display/pico_display.c +++ /dev/null @@ -1,74 +0,0 @@ -#include "pico_display.h" - - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// picodisplay Module -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/***** Module Functions *****/ -STATIC MP_DEFINE_CONST_FUN_OBJ_1(picodisplay_init_obj, picodisplay_init); -STATIC MP_DEFINE_CONST_FUN_OBJ_0(picodisplay_get_width_obj, picodisplay_get_width); -STATIC MP_DEFINE_CONST_FUN_OBJ_0(picodisplay_get_height_obj, picodisplay_get_height); -STATIC MP_DEFINE_CONST_FUN_OBJ_0(picodisplay_update_obj, picodisplay_update); -STATIC MP_DEFINE_CONST_FUN_OBJ_1(picodisplay_set_backlight_obj, picodisplay_set_backlight); -STATIC MP_DEFINE_CONST_FUN_OBJ_3(picodisplay_set_led_obj, picodisplay_set_led); -STATIC MP_DEFINE_CONST_FUN_OBJ_1(picodisplay_is_pressed_obj, picodisplay_is_pressed); -STATIC MP_DEFINE_CONST_FUN_OBJ_0(picodisplay_flip_obj, picodisplay_flip); - -//From PicoGraphics parent class -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(picodisplay_set_pen_obj, 1, 3, picodisplay_set_pen); -STATIC MP_DEFINE_CONST_FUN_OBJ_3(picodisplay_create_pen_obj, picodisplay_create_pen); -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(picodisplay_set_clip_obj, 4, 4, picodisplay_set_clip); -STATIC MP_DEFINE_CONST_FUN_OBJ_0(picodisplay_remove_clip_obj, picodisplay_remove_clip); -STATIC MP_DEFINE_CONST_FUN_OBJ_0(picodisplay_clear_obj, picodisplay_clear); -STATIC MP_DEFINE_CONST_FUN_OBJ_2(picodisplay_pixel_obj, picodisplay_pixel); -STATIC MP_DEFINE_CONST_FUN_OBJ_3(picodisplay_pixel_span_obj, picodisplay_pixel_span); -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(picodisplay_rectangle_obj, 4, 4, picodisplay_rectangle); -STATIC MP_DEFINE_CONST_FUN_OBJ_3(picodisplay_circle_obj, picodisplay_circle); -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(picodisplay_character_obj, 3, 4, picodisplay_character); -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(picodisplay_text_obj, 4, 5, picodisplay_text); - - -/***** Globals Table *****/ -STATIC const mp_map_elem_t picodisplay_globals_table[] = { - { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_picodisplay) }, - { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&picodisplay_init_obj) }, - { MP_ROM_QSTR(MP_QSTR_get_width), MP_ROM_PTR(&picodisplay_get_width_obj) }, - { MP_ROM_QSTR(MP_QSTR_get_height), MP_ROM_PTR(&picodisplay_get_height_obj) }, - { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&picodisplay_update_obj) }, - { MP_ROM_QSTR(MP_QSTR_set_backlight), MP_ROM_PTR(&picodisplay_set_backlight_obj) }, - { MP_ROM_QSTR(MP_QSTR_set_led), MP_ROM_PTR(&picodisplay_set_led_obj) }, - { MP_ROM_QSTR(MP_QSTR_is_pressed), MP_ROM_PTR(&picodisplay_is_pressed_obj) }, - { MP_ROM_QSTR(MP_QSTR_flip), MP_ROM_PTR(&picodisplay_flip_obj) }, - - { MP_ROM_QSTR(MP_QSTR_set_pen), MP_ROM_PTR(&picodisplay_set_pen_obj) }, - { MP_ROM_QSTR(MP_QSTR_create_pen), MP_ROM_PTR(&picodisplay_create_pen_obj) }, - { MP_ROM_QSTR(MP_QSTR_set_clip), MP_ROM_PTR(&picodisplay_set_clip_obj) }, - { MP_ROM_QSTR(MP_QSTR_remove_clip), MP_ROM_PTR(&picodisplay_remove_clip_obj) }, - { MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&picodisplay_clear_obj) }, - { MP_ROM_QSTR(MP_QSTR_pixel), MP_ROM_PTR(&picodisplay_pixel_obj) }, - { MP_ROM_QSTR(MP_QSTR_pixel_span), MP_ROM_PTR(&picodisplay_pixel_span_obj) }, - { MP_ROM_QSTR(MP_QSTR_rectangle), MP_ROM_PTR(&picodisplay_rectangle_obj) }, - { MP_ROM_QSTR(MP_QSTR_circle), MP_ROM_PTR(&picodisplay_circle_obj) }, - { MP_ROM_QSTR(MP_QSTR_character), MP_ROM_PTR(&picodisplay_character_obj) }, - { MP_ROM_QSTR(MP_QSTR_text), MP_ROM_PTR(&picodisplay_text_obj) }, - { MP_ROM_QSTR(MP_QSTR_BUTTON_A), MP_ROM_INT(12) }, - { MP_ROM_QSTR(MP_QSTR_BUTTON_B), MP_ROM_INT(13) }, - { MP_ROM_QSTR(MP_QSTR_BUTTON_X), MP_ROM_INT(14) }, - { MP_ROM_QSTR(MP_QSTR_BUTTON_Y), MP_ROM_INT(15) }, - { MP_ROM_QSTR(MP_QSTR_LED_R), MP_ROM_INT(6) }, - { MP_ROM_QSTR(MP_QSTR_LED_G), MP_ROM_INT(7) }, - { MP_ROM_QSTR(MP_QSTR_LED_B), MP_ROM_INT(8) }, -}; -STATIC MP_DEFINE_CONST_DICT(mp_module_picodisplay_globals, picodisplay_globals_table); - -/***** Module Definition *****/ -const mp_obj_module_t picodisplay_user_cmodule = { - .base = { &mp_type_module }, - .globals = (mp_obj_dict_t*)&mp_module_picodisplay_globals, -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -MP_REGISTER_MODULE(MP_QSTR_picodisplay, picodisplay_user_cmodule, MODULE_PICODISPLAY_ENABLED); -//////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/micropython/modules/pico_display/pico_display.cpp b/micropython/modules/pico_display/pico_display.cpp deleted file mode 100644 index c6b7650d..00000000 --- a/micropython/modules/pico_display/pico_display.cpp +++ /dev/null @@ -1,323 +0,0 @@ -#include "hardware/spi.h" -#include "hardware/sync.h" -#include "pico/binary_info.h" - -#include "libraries/pico_display/pico_display.hpp"zzz - -using namespace pimoroni; - -PicoDisplay *display = nullptr; - - -extern "C" { -#include "pico_display.h" - -#define NOT_INITIALISED_MSG "Cannot call this function, as picodisplay is not initialised. Call picodisplay.init() first." - -mp_obj_t picodisplay_buf_obj; - -mp_obj_t picodisplay_init(mp_obj_t buf_obj) { - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(buf_obj, &bufinfo, MP_BUFFER_RW); - picodisplay_buf_obj = buf_obj; - - // If a display already exists, delete it - if(display != nullptr) { - delete display; - } - - // Create a new display pointing to the newly provided buffer - display = new PicoDisplay((uint16_t *)bufinfo.buf); - display->init(); - - return mp_const_none; -} - -mp_obj_t picodisplay_get_width() { - return mp_obj_new_int(PicoDisplay::WIDTH); -} - -mp_obj_t picodisplay_get_height() { - return mp_obj_new_int(PicoDisplay::HEIGHT); -} - -mp_obj_t picodisplay_update() { - if(display != nullptr) - display->update(); - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_const_none; -} - -mp_obj_t picodisplay_flip() { - if(display != nullptr) - display->flip(); - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_const_none; -} - -mp_obj_t picodisplay_set_backlight(mp_obj_t brightness_obj) { - if(display != nullptr) { - float brightness = mp_obj_get_float(brightness_obj); - - if(brightness < 0 || brightness > 1.0f) - mp_raise_ValueError("brightness out of range. Expected 0.0 to 1.0"); - else - display->set_backlight((uint8_t)(brightness * 255.0f)); - } - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_const_none; -} - -mp_obj_t picodisplay_set_led(mp_obj_t r_obj, mp_obj_t g_obj, mp_obj_t b_obj) { - if(display != nullptr) { - int r = mp_obj_get_int(r_obj); - int g = mp_obj_get_int(g_obj); - int b = mp_obj_get_int(b_obj); - - if(r < 0 || r > 255) - mp_raise_ValueError("r out of range. Expected 0 to 255"); - else if(g < 0 || g > 255) - mp_raise_ValueError("g out of range. Expected 0 to 255"); - else if(b < 0 || b > 255) - mp_raise_ValueError("b out of range. Expected 0 to 255"); - else - display->set_led(r, g, b); - } - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_const_none; -} - -mp_obj_t picodisplay_is_pressed(mp_obj_t button_obj) { - if(display != nullptr) { - return display->is_pressed(mp_obj_get_int(button_obj)) ? mp_const_true : mp_const_false; - } - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - return mp_const_none; -} - -mp_obj_t picodisplay_set_pen(mp_uint_t n_args, const mp_obj_t *args) { - if(display != nullptr) { - switch(n_args) { - case 1: { - int p = mp_obj_get_int(args[0]); - - if(p < 0 || p > 0xffff) - mp_raise_ValueError("p is not a valid pen."); - else - display->set_pen(p); - } break; - - case 3: { - int r = mp_obj_get_int(args[0]); - int g = mp_obj_get_int(args[1]); - int b = mp_obj_get_int(args[2]); - - if(r < 0 || r > 255) - mp_raise_ValueError("r out of range. Expected 0 to 255"); - else if(g < 0 || g > 255) - mp_raise_ValueError("g out of range. Expected 0 to 255"); - else if(b < 0 || b > 255) - mp_raise_ValueError("b out of range. Expected 0 to 255"); - else - display->set_pen(r, g, b); - } break; - - default: { - char *buffer; - buffer = (char*)malloc(100); - sprintf(buffer, "function takes 1 or 3 (r,g,b) positional arguments but %d were given", n_args); - mp_raise_TypeError(buffer); - } break; - } - } - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_const_none; -} - -mp_obj_t picodisplay_create_pen(mp_obj_t r_obj, mp_obj_t g_obj, mp_obj_t b_obj) { - int pen = 0; - - if(display != nullptr) { - int r = mp_obj_get_int(r_obj); - int g = mp_obj_get_int(g_obj); - int b = mp_obj_get_int(b_obj); - - if(r < 0 || r > 255) - mp_raise_ValueError("r out of range. Expected 0 to 255"); - else if(g < 0 || g > 255) - mp_raise_ValueError("g out of range. Expected 0 to 255"); - else if(b < 0 || b > 255) - mp_raise_ValueError("b out of range. Expected 0 to 255"); - else - pen = display->create_pen(r, g, b); - } - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_obj_new_int(pen); -} - -mp_obj_t picodisplay_set_clip(mp_uint_t n_args, const mp_obj_t *args) { - (void)n_args; //Unused input parameter, we know it's 4 - - if(display != nullptr) { - int x = mp_obj_get_int(args[0]); - int y = mp_obj_get_int(args[1]); - int w = mp_obj_get_int(args[2]); - int h = mp_obj_get_int(args[3]); - - Rect r(x, y, w, h); - display->set_clip(r); - } - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_const_none; -} - -mp_obj_t picodisplay_remove_clip() { - if(display != nullptr) - display->remove_clip(); - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_const_none; -} - -mp_obj_t picodisplay_clear() { - if(display != nullptr) - display->clear(); - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_const_none; -} - -mp_obj_t picodisplay_pixel(mp_obj_t x_obj, mp_obj_t y_obj) { - if(display != nullptr) { - int x = mp_obj_get_int(x_obj); - int y = mp_obj_get_int(y_obj); - - Point p(x, y); - display->pixel(p); - } - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_const_none; -} - -mp_obj_t picodisplay_pixel_span(mp_obj_t x_obj, mp_obj_t y_obj, mp_obj_t l_obj) { - if(display != nullptr) { - int x = mp_obj_get_int(x_obj); - int y = mp_obj_get_int(y_obj); - int l = mp_obj_get_int(l_obj); - - Point p(x, y); - display->pixel_span(p, l); - } - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_const_none; -} - -mp_obj_t picodisplay_rectangle(mp_uint_t n_args, const mp_obj_t *args) { - (void)n_args; //Unused input parameter, we know it's 4 - - if(display != nullptr) { - int x = mp_obj_get_int(args[0]); - int y = mp_obj_get_int(args[1]); - int w = mp_obj_get_int(args[2]); - int h = mp_obj_get_int(args[3]); - - Rect r(x, y, w, h); - display->rectangle(r); - } - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_const_none; -} - -mp_obj_t picodisplay_circle(mp_obj_t x_obj, mp_obj_t y_obj, mp_obj_t r_obj) { - if(display != nullptr) { - int x = mp_obj_get_int(x_obj); - int y = mp_obj_get_int(y_obj); - int r = mp_obj_get_int(r_obj); - - Point p(x, y); - display->circle(p, r); - } - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_const_none; -} - -mp_obj_t picodisplay_character(mp_uint_t n_args, const mp_obj_t *args) { - if(display != nullptr) { - int c = mp_obj_get_int(args[0]); - int x = mp_obj_get_int(args[1]); - int y = mp_obj_get_int(args[2]); - - Point p(x, y); - if(n_args == 4) { - int scale = mp_obj_get_int(args[3]); - display->character((char)c, p, scale); - } - else - display->character((char)c, p); - } - - return mp_const_none; -} - -mp_obj_t picodisplay_text(mp_uint_t n_args, const mp_obj_t *args) { - if(display != nullptr) { - if(mp_obj_is_str_or_bytes(args[0])) { - GET_STR_DATA_LEN(args[0], str, str_len); - - std::string t((const char*)str); - - int x = mp_obj_get_int(args[1]); - int y = mp_obj_get_int(args[2]); - int wrap = mp_obj_get_int(args[3]); - - Point p(x, y); - if(n_args == 5) { - int scale = mp_obj_get_int(args[4]); - display->text(t, p, wrap, scale); - } - else - display->text(t, p, wrap); - } - else if(mp_obj_is_float(args[0])) { - mp_raise_TypeError("can't convert 'float' object to str implicitly"); - } - else if(mp_obj_is_int(args[0])) { - mp_raise_TypeError("can't convert 'int' object to str implicitly"); - } - else if(mp_obj_is_bool(args[0])) { - mp_raise_TypeError("can't convert 'bool' object to str implicitly"); - } - else { - mp_raise_TypeError("can't convert object to str implicitly"); - } - } - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_const_none; -} -} diff --git a/micropython/modules/pico_display/pico_display.h b/micropython/modules/pico_display/pico_display.h deleted file mode 100644 index 1234e434..00000000 --- a/micropython/modules/pico_display/pico_display.h +++ /dev/null @@ -1,26 +0,0 @@ -// Include MicroPython API. -#include "py/runtime.h" -#include "py/objstr.h" - -// Declare the functions we'll make available in Python -extern mp_obj_t picodisplay_init(mp_obj_t buf_obj); -extern mp_obj_t picodisplay_get_width(); -extern mp_obj_t picodisplay_get_height(); -extern mp_obj_t picodisplay_set_backlight(mp_obj_t brightness_obj); -extern mp_obj_t picodisplay_update(); -extern mp_obj_t picodisplay_set_led(mp_obj_t r_obj, mp_obj_t g_obj, mp_obj_t b_obj); -extern mp_obj_t picodisplay_is_pressed(mp_obj_t button_obj); -extern mp_obj_t picodisplay_flip(); - -// From PicoGraphics parent class -extern mp_obj_t picodisplay_set_pen(mp_uint_t n_args, const mp_obj_t *args); -extern mp_obj_t picodisplay_create_pen(mp_obj_t r_obj, mp_obj_t g_obj, mp_obj_t b_obj); -extern mp_obj_t picodisplay_set_clip(mp_uint_t n_args, const mp_obj_t *args); -extern mp_obj_t picodisplay_remove_clip(); -extern mp_obj_t picodisplay_clear(); -extern mp_obj_t picodisplay_pixel(mp_obj_t x_obj, mp_obj_t y_obj); -extern mp_obj_t picodisplay_pixel_span(mp_obj_t x_obj, mp_obj_t y_obj, mp_obj_t l_obj); -extern mp_obj_t picodisplay_rectangle(mp_uint_t n_args, const mp_obj_t *args); -extern mp_obj_t picodisplay_circle(mp_obj_t x_obj, mp_obj_t y_obj, mp_obj_t r_obj); -extern mp_obj_t picodisplay_character(mp_uint_t n_args, const mp_obj_t *args); -extern mp_obj_t picodisplay_text(mp_uint_t n_args, const mp_obj_t *args); diff --git a/micropython/modules/pico_display_2/README.md b/micropython/modules/pico_display_2/README.md deleted file mode 100644 index ccf580c4..00000000 --- a/micropython/modules/pico_display_2/README.md +++ /dev/null @@ -1,206 +0,0 @@ -# Pico Display Pack - MicroPython - -Pico Display 2.0" Pack is a vibrant 2.0", 320 x 240 pixel IPS LCD screen for your Raspberry Pi Pico, with four useful buttons and a RGB LED. [Click here](https://shop.pimoroni.com/products/pico-display-pack) to find out more! - -We've included helper functions to handle every aspect of drawing to the screen and interfacing with the buttons and LED. See the [function reference](#function-reference) for details. - -Check out [UnfinishedStuff's excellent Display Pack guide](https://github.com/UnfinishedStuff/Pimoroni_Pico_Display_Pack_documentation) for more detail on the functions and code examples, and [tonygo2's Display Pack Workout](https://www.instructables.com/Pimoroni-Pico-Display-Workout/) for a comprehensive demo! - -- [Example Program](#example-program) -- [Function Reference](#function-reference) - - [init](#init) - - [set_backlight](#set_backlight) - - [set_led](#set_led) - - [is_pressed](#is_pressed) - - [update](#update) - - [set_pen](#set_pen) - - [create_pen](#create_pen) - - [clear](#clear) - - [pixel](#pixel) - - [pixel_span](#pixel_span) - - [rectangle](#rectangle) - - [circle](#circle) - - [character](#character) - - [text](#text) - - [set_clip](#set_clip) - - [remove_clip](#remove_clip) - -## Example Program - -The following example sets up Pico Display, displays some basic demo text and illuminates the RGB LED green when the A button is pressed. - -```python -import utime -import picodisplay2 as picodisplay - -# Initialise Picodisplay with a bytearray display buffer -buf = bytearray(picodisplay.get_width() * picodisplay.get_height() * 2) -picodisplay.init(buf) -picodisplay.set_backlight(1.0) - -picodisplay.set_pen(255, 0, 0) # Set a red pen -picodisplay.clear() # Clear the display buffer -picodisplay.set_pen(255, 255, 255) # Set a white pen -picodisplay.text("pico display", 10, 10, 240, 6) # Add some text -picodisplay.update() # Update the display with our changes - -picodisplay.set_led(255, 0, 0) # Set the RGB LED to red -utime.sleep(1) # Wait for a second -picodisplay.set_led(0, 255, 0) # Set the RGB LED to green -utime.sleep(1) # Wait for a second -picodisplay.set_led(0, 0, 255) # Set the RGB LED to blue - -while picodisplay.is_pressed(picodisplay.BUTTON_A) == False: - pass - -picodisplay.set_led(0, 255, 0) # Set the RGB LED to green -``` - -## Function Reference - -### init - -Sets up Pico Display. `init` must be called before any other functions since it configures the required PWM and GPIO. `init()` needs a bytearray type display buffer that MicroPython's garbage collection isn't going to eat, make sure you create one and pass it in like so: - -```python -buf = bytearray(picodisplay.get_width() * picodisplay.get_height() * 2) -picodisplay.init(buf) -``` - -### set_backlight - -Sets the display backlight from 0.0 to 1.0. - -```python -picodisplay.set_backlight(brightness) -``` - -Uses hardware PWM to dim the display backlight, dimming values are gamma-corrected to provide smooth brightness transitions across the full range of intensity. This may result in some low values mapping as "off." - -### set_led - -Sets the RGB LED on Pico Display with an RGB triplet. - -```python -picodisplay.set_led(r, g, b) -``` - -Uses hardware PWM to drive the LED. Values are automatically gamma-corrected to provide smooth brightness transitions and low values may map as "off." - -### is_pressed - -Reads the GPIO pin connected to one of Pico Display's buttons, returning `True` if it's pressed and `False` if it is released. - -```python -picodisplay.is_pressed(button) -``` - -The button value should be a number denoting a pin, and constants `picodisplay.BUTTON_A`, `picodisplay.BUTTON_B`, `picodisplay.BUTTON_X` and `picodisplay.BUTTON_Y` are supplied to make it easier. e: - -```python -is_a_button_pressed = picodisplay.is_pressed(picodisplay.BUTTON_A) -``` - -### update - -To display your changes on Pico Display's screen you need to call `update`. - -```python -picodisplay.update() -``` - -### set_pen - -Sets the colour to be used by subsequent calls to drawing functions. The values for `r`, `g` and `b` should be from 0-255 inclusive. - -```python -picodisplay.set_pen(r, g, b) -``` - -### create_pen - -Creates a pen which can be stored as a variable for faster re-use of the same colour through calls to `set_pen`. The values for `r`, `g` and `b` should be from 0-255 inclusive. - -```python -pen_colour = picodisplay.create_pen(r, g, b) -picodisplay.set_pen(penColour) -``` - -### clear - -Fills the display buffer with the currently set pen colour. - -```python -picodisplay.clear() -``` - -### pixel - -Sets a single pixel in the display buffer to the current pen colour. The `x` and `y` parameters determine the X and Y coordinates of the drawn pixel in the buffer. - -```python -picodisplay.pixel(x, y) -``` - -### pixel_span - -Draws a horizontal line of pixels to the buffer. The `x` and `y` parameters specify the coordinates of the first pixel of the line. The `l` parameter describes the length of the line in pixels. This function will only extend the line towards the end of the screen, i.e. the `x` coordinate should specify the left hand extreme of the line. - -```python -picodisplay.pixel_span(x, y, l) -``` - -### rectangle - -Draws a rectangle filled with the current pen colour to the buffer. The `x` and `y` parameters specify the upper left corner of the rectangle, `w` specifies the width in pixels, and `h` the height. - -```python -picodisplay.rectangle(x, y, w, h) -``` - -![Rectangle function explanation image](/micropython/modules/pico_display/images/rectangle.png) - -### circle - -Draws a circle filled with the current pen colour to the buffer. The `x` and `y` parameters specify the centre of the circle, `r` specifies the radius in pixels. - -```python -picodisplay.circle(x, y, r) -``` - -![Circle function explanation image](/micropython/modules/pico_display/images/circle.png) - -### character - -Draws a single character to the display buffer in the current pen colour. The `c` parameter should be the ASCII numerical representation of the character to be printed, `x` and `y` describe the top-left corner of the character's drawing field. The `character` function can also be given an optional 4th parameter, `scale`, describing the scale of the character to be drawn. Default value is 2. - -```python -char_a = ord('a') -picodisplay.character(char_a, x, y) -picodisplay.character(char_a, x, y, scale) -``` - -### text - -Draws a string of text to the display buffer in the current pen colour. The `string` parameter is the string of text to be drawn, and `x` and `y` specify the upper left corner of the drawing field. The `wrap` parameter describes the width, in pixels, after which the next word in the string will be drawn on a new line underneath the current text. This will wrap the string over multiple lines if required. This function also has an optional parameter, `scale`, which describes the size of the characters to be drawn. The default `scale` is 2. - -```python -picodisplay.text(string, x, y, wrap) -picodisplay.text(string, x, y, wrap, scale) -``` - -![Text scale explanation image](/micropython/modules/pico_display/images/text_scale.png) - -### set_clip - -This function defines a rectangular area outside which no drawing actions will take effect. If a drawing action crosses the boundary of the clip then only the pixels inside the clip will be drawn. Note that `clip` does not remove pixels which have already been drawn, it only prevents new pixels being drawn outside the described area. A more visual description of the function of clips can be found below. Only one clip can be active at a time, and defining a new clip replaces any previous clips. The `x` and `y` parameters describe the upper-left corner of the clip area, `w` and `h` describe the width and height in pixels. - -```python -picodisplay.set_clip(x, y, w, h) -``` - -![Clip function explanation image](/micropython/modules/pico_display/images/clip.png) - -### remove_clip - -This function removes any currently implemented clip. diff --git a/micropython/modules/pico_display_2/images/circle.png b/micropython/modules/pico_display_2/images/circle.png deleted file mode 100644 index 4efadd9c..00000000 Binary files a/micropython/modules/pico_display_2/images/circle.png and /dev/null differ diff --git a/micropython/modules/pico_display_2/images/clip.png b/micropython/modules/pico_display_2/images/clip.png deleted file mode 100644 index a2d28094..00000000 Binary files a/micropython/modules/pico_display_2/images/clip.png and /dev/null differ diff --git a/micropython/modules/pico_display_2/images/rectangle.png b/micropython/modules/pico_display_2/images/rectangle.png deleted file mode 100644 index 4e0c3467..00000000 Binary files a/micropython/modules/pico_display_2/images/rectangle.png and /dev/null differ diff --git a/micropython/modules/pico_display_2/images/text_scale.png b/micropython/modules/pico_display_2/images/text_scale.png deleted file mode 100644 index 1f029ed2..00000000 Binary files a/micropython/modules/pico_display_2/images/text_scale.png and /dev/null differ diff --git a/micropython/modules/pico_display_2/pico_display_2.c b/micropython/modules/pico_display_2/pico_display_2.c deleted file mode 100755 index f0ca3fe7..00000000 --- a/micropython/modules/pico_display_2/pico_display_2.c +++ /dev/null @@ -1,74 +0,0 @@ -#include "pico_display_2.h" - - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// picodisplay2 Module -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/***** Module Functions *****/ -STATIC MP_DEFINE_CONST_FUN_OBJ_1(picodisplay2_init_obj, picodisplay2_init); -STATIC MP_DEFINE_CONST_FUN_OBJ_0(picodisplay2_get_width_obj, picodisplay2_get_width); -STATIC MP_DEFINE_CONST_FUN_OBJ_0(picodisplay2_get_height_obj, picodisplay2_get_height); -STATIC MP_DEFINE_CONST_FUN_OBJ_0(picodisplay2_update_obj, picodisplay2_update); -STATIC MP_DEFINE_CONST_FUN_OBJ_1(picodisplay2_set_backlight_obj, picodisplay2_set_backlight); -STATIC MP_DEFINE_CONST_FUN_OBJ_3(picodisplay2_set_led_obj, picodisplay2_set_led); -STATIC MP_DEFINE_CONST_FUN_OBJ_1(picodisplay2_is_pressed_obj, picodisplay2_is_pressed); -STATIC MP_DEFINE_CONST_FUN_OBJ_0(picodisplay2_flip_obj, picodisplay2_flip); - -//From PicoGraphics parent class -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(picodisplay2_set_pen_obj, 1, 3, picodisplay2_set_pen); -STATIC MP_DEFINE_CONST_FUN_OBJ_3(picodisplay2_create_pen_obj, picodisplay2_create_pen); -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(picodisplay2_set_clip_obj, 4, 4, picodisplay2_set_clip); -STATIC MP_DEFINE_CONST_FUN_OBJ_0(picodisplay2_remove_clip_obj, picodisplay2_remove_clip); -STATIC MP_DEFINE_CONST_FUN_OBJ_0(picodisplay2_clear_obj, picodisplay2_clear); -STATIC MP_DEFINE_CONST_FUN_OBJ_2(picodisplay2_pixel_obj, picodisplay2_pixel); -STATIC MP_DEFINE_CONST_FUN_OBJ_3(picodisplay2_pixel_span_obj, picodisplay2_pixel_span); -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(picodisplay2_rectangle_obj, 4, 4, picodisplay2_rectangle); -STATIC MP_DEFINE_CONST_FUN_OBJ_3(picodisplay2_circle_obj, picodisplay2_circle); -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(picodisplay2_character_obj, 3, 4, picodisplay2_character); -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(picodisplay2_text_obj, 4, 5, picodisplay2_text); - - -/***** Globals Table *****/ -STATIC const mp_map_elem_t picodisplay2_globals_table[] = { - { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_picodisplay2) }, - { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&picodisplay2_init_obj) }, - { MP_ROM_QSTR(MP_QSTR_get_width), MP_ROM_PTR(&picodisplay2_get_width_obj) }, - { MP_ROM_QSTR(MP_QSTR_get_height), MP_ROM_PTR(&picodisplay2_get_height_obj) }, - { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&picodisplay2_update_obj) }, - { MP_ROM_QSTR(MP_QSTR_set_backlight), MP_ROM_PTR(&picodisplay2_set_backlight_obj) }, - { MP_ROM_QSTR(MP_QSTR_set_led), MP_ROM_PTR(&picodisplay2_set_led_obj) }, - { MP_ROM_QSTR(MP_QSTR_is_pressed), MP_ROM_PTR(&picodisplay2_is_pressed_obj) }, - { MP_ROM_QSTR(MP_QSTR_flip), MP_ROM_PTR(&picodisplay2_flip_obj) }, - - { MP_ROM_QSTR(MP_QSTR_set_pen), MP_ROM_PTR(&picodisplay2_set_pen_obj) }, - { MP_ROM_QSTR(MP_QSTR_create_pen), MP_ROM_PTR(&picodisplay2_create_pen_obj) }, - { MP_ROM_QSTR(MP_QSTR_set_clip), MP_ROM_PTR(&picodisplay2_set_clip_obj) }, - { MP_ROM_QSTR(MP_QSTR_remove_clip), MP_ROM_PTR(&picodisplay2_remove_clip_obj) }, - { MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&picodisplay2_clear_obj) }, - { MP_ROM_QSTR(MP_QSTR_pixel), MP_ROM_PTR(&picodisplay2_pixel_obj) }, - { MP_ROM_QSTR(MP_QSTR_pixel_span), MP_ROM_PTR(&picodisplay2_pixel_span_obj) }, - { MP_ROM_QSTR(MP_QSTR_rectangle), MP_ROM_PTR(&picodisplay2_rectangle_obj) }, - { MP_ROM_QSTR(MP_QSTR_circle), MP_ROM_PTR(&picodisplay2_circle_obj) }, - { MP_ROM_QSTR(MP_QSTR_character), MP_ROM_PTR(&picodisplay2_character_obj) }, - { MP_ROM_QSTR(MP_QSTR_text), MP_ROM_PTR(&picodisplay2_text_obj) }, - { MP_ROM_QSTR(MP_QSTR_BUTTON_A), MP_ROM_INT(12) }, - { MP_ROM_QSTR(MP_QSTR_BUTTON_B), MP_ROM_INT(13) }, - { MP_ROM_QSTR(MP_QSTR_BUTTON_X), MP_ROM_INT(14) }, - { MP_ROM_QSTR(MP_QSTR_BUTTON_Y), MP_ROM_INT(15) }, - { MP_ROM_QSTR(MP_QSTR_LED_R), MP_ROM_INT(6) }, - { MP_ROM_QSTR(MP_QSTR_LED_G), MP_ROM_INT(7) }, - { MP_ROM_QSTR(MP_QSTR_LED_B), MP_ROM_INT(8) }, -}; -STATIC MP_DEFINE_CONST_DICT(mp_module_picodisplay2_globals, picodisplay2_globals_table); - -/***** Module Definition *****/ -const mp_obj_module_t picodisplay2_user_cmodule = { - .base = { &mp_type_module }, - .globals = (mp_obj_dict_t*)&mp_module_picodisplay2_globals, -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -MP_REGISTER_MODULE(MP_QSTR_picodisplay2, picodisplay2_user_cmodule, MODULE_PICO_DISPLAY_2_ENABLED); -//////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/micropython/modules/pico_display_2/pico_display_2.cpp b/micropython/modules/pico_display_2/pico_display_2.cpp deleted file mode 100644 index f33eb065..00000000 --- a/micropython/modules/pico_display_2/pico_display_2.cpp +++ /dev/null @@ -1,323 +0,0 @@ -#include "hardware/spi.h" -#include "hardware/sync.h" -#include "pico/binary_info.h" - -#include "libraries/pico_display_2/pico_display_2.hpp" - -using namespace pimoroni; - -PicoDisplay2 *display2 = nullptr; - - -extern "C" { -#include "pico_display_2.h" - -#define NOT_INITIALISED_MSG "Cannot call this function, as picodisplay2 is not initialised. Call picodisplay2.init() first." - -mp_obj_t picodisplay2_buf_obj; - -mp_obj_t picodisplay2_init(mp_obj_t buf_obj) { - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(buf_obj, &bufinfo, MP_BUFFER_RW); - picodisplay2_buf_obj = buf_obj; - - // If a display already exists, delete it - if(display2 != nullptr) { - delete display2; - } - - // Create a new display pointing to the newly provided buffer - display2 = new PicoDisplay2((uint16_t *)bufinfo.buf); - display2->init(); - - return mp_const_none; -} - -mp_obj_t picodisplay2_get_width() { - return mp_obj_new_int(PicoDisplay2::WIDTH); -} - -mp_obj_t picodisplay2_get_height() { - return mp_obj_new_int(PicoDisplay2::HEIGHT); -} - -mp_obj_t picodisplay2_update() { - if(display2 != nullptr) - display2->update(); - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_const_none; -} - -mp_obj_t picodisplay2_flip() { - if(display2 != nullptr) - display2->flip(); - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_const_none; -} - -mp_obj_t picodisplay2_set_backlight(mp_obj_t brightness_obj) { - if(display2 != nullptr) { - float brightness = mp_obj_get_float(brightness_obj); - - if(brightness < 0 || brightness > 1.0f) - mp_raise_ValueError("brightness out of range. Expected 0.0 to 1.0"); - else - display2->set_backlight((uint8_t)(brightness * 255.0f)); - } - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_const_none; -} - -mp_obj_t picodisplay2_set_led(mp_obj_t r_obj, mp_obj_t g_obj, mp_obj_t b_obj) { - if(display2 != nullptr) { - int r = mp_obj_get_int(r_obj); - int g = mp_obj_get_int(g_obj); - int b = mp_obj_get_int(b_obj); - - if(r < 0 || r > 255) - mp_raise_ValueError("r out of range. Expected 0 to 255"); - else if(g < 0 || g > 255) - mp_raise_ValueError("g out of range. Expected 0 to 255"); - else if(b < 0 || b > 255) - mp_raise_ValueError("b out of range. Expected 0 to 255"); - else - display2->set_led(r, g, b); - } - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_const_none; -} - -mp_obj_t picodisplay2_is_pressed(mp_obj_t button_obj) { - if(display2 != nullptr) { - return display2->is_pressed(mp_obj_get_int(button_obj)) ? mp_const_true : mp_const_false; - } - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - return mp_const_none; -} - -mp_obj_t picodisplay2_set_pen(mp_uint_t n_args, const mp_obj_t *args) { - if(display2 != nullptr) { - switch(n_args) { - case 1: { - int p = mp_obj_get_int(args[0]); - - if(p < 0 || p > 0xffff) - mp_raise_ValueError("p is not a valid pen."); - else - display2->set_pen(p); - } break; - - case 3: { - int r = mp_obj_get_int(args[0]); - int g = mp_obj_get_int(args[1]); - int b = mp_obj_get_int(args[2]); - - if(r < 0 || r > 255) - mp_raise_ValueError("r out of range. Expected 0 to 255"); - else if(g < 0 || g > 255) - mp_raise_ValueError("g out of range. Expected 0 to 255"); - else if(b < 0 || b > 255) - mp_raise_ValueError("b out of range. Expected 0 to 255"); - else - display2->set_pen(r, g, b); - } break; - - default: { - char *buffer; - buffer = (char*)malloc(100); - sprintf(buffer, "function takes 1 or 3 (r,g,b) positional arguments but %d were given", n_args); - mp_raise_TypeError(buffer); - } break; - } - } - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_const_none; -} - -mp_obj_t picodisplay2_create_pen(mp_obj_t r_obj, mp_obj_t g_obj, mp_obj_t b_obj) { - int pen = 0; - - if(display2 != nullptr) { - int r = mp_obj_get_int(r_obj); - int g = mp_obj_get_int(g_obj); - int b = mp_obj_get_int(b_obj); - - if(r < 0 || r > 255) - mp_raise_ValueError("r out of range. Expected 0 to 255"); - else if(g < 0 || g > 255) - mp_raise_ValueError("g out of range. Expected 0 to 255"); - else if(b < 0 || b > 255) - mp_raise_ValueError("b out of range. Expected 0 to 255"); - else - pen = display2->create_pen(r, g, b); - } - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_obj_new_int(pen); -} - -mp_obj_t picodisplay2_set_clip(mp_uint_t n_args, const mp_obj_t *args) { - (void)n_args; //Unused input parameter, we know it's 4 - - if(display2 != nullptr) { - int x = mp_obj_get_int(args[0]); - int y = mp_obj_get_int(args[1]); - int w = mp_obj_get_int(args[2]); - int h = mp_obj_get_int(args[3]); - - Rect r(x, y, w, h); - display2->set_clip(r); - } - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_const_none; -} - -mp_obj_t picodisplay2_remove_clip() { - if(display2 != nullptr) - display2->remove_clip(); - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_const_none; -} - -mp_obj_t picodisplay2_clear() { - if(display2 != nullptr) - display2->clear(); - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_const_none; -} - -mp_obj_t picodisplay2_pixel(mp_obj_t x_obj, mp_obj_t y_obj) { - if(display2 != nullptr) { - int x = mp_obj_get_int(x_obj); - int y = mp_obj_get_int(y_obj); - - Point p(x, y); - display2->pixel(p); - } - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_const_none; -} - -mp_obj_t picodisplay2_pixel_span(mp_obj_t x_obj, mp_obj_t y_obj, mp_obj_t l_obj) { - if(display2 != nullptr) { - int x = mp_obj_get_int(x_obj); - int y = mp_obj_get_int(y_obj); - int l = mp_obj_get_int(l_obj); - - Point p(x, y); - display2->pixel_span(p, l); - } - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_const_none; -} - -mp_obj_t picodisplay2_rectangle(mp_uint_t n_args, const mp_obj_t *args) { - (void)n_args; //Unused input parameter, we know it's 4 - - if(display2 != nullptr) { - int x = mp_obj_get_int(args[0]); - int y = mp_obj_get_int(args[1]); - int w = mp_obj_get_int(args[2]); - int h = mp_obj_get_int(args[3]); - - Rect r(x, y, w, h); - display2->rectangle(r); - } - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_const_none; -} - -mp_obj_t picodisplay2_circle(mp_obj_t x_obj, mp_obj_t y_obj, mp_obj_t r_obj) { - if(display2 != nullptr) { - int x = mp_obj_get_int(x_obj); - int y = mp_obj_get_int(y_obj); - int r = mp_obj_get_int(r_obj); - - Point p(x, y); - display2->circle(p, r); - } - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_const_none; -} - -mp_obj_t picodisplay2_character(mp_uint_t n_args, const mp_obj_t *args) { - if(display2 != nullptr) { - int c = mp_obj_get_int(args[0]); - int x = mp_obj_get_int(args[1]); - int y = mp_obj_get_int(args[2]); - - Point p(x, y); - if(n_args == 4) { - int scale = mp_obj_get_int(args[3]); - display2->character((char)c, p, scale); - } - else - display2->character((char)c, p); - } - - return mp_const_none; -} - -mp_obj_t picodisplay2_text(mp_uint_t n_args, const mp_obj_t *args) { - if(display2 != nullptr) { - if(mp_obj_is_str_or_bytes(args[0])) { - GET_STR_DATA_LEN(args[0], str, str_len); - - std::string t((const char*)str); - - int x = mp_obj_get_int(args[1]); - int y = mp_obj_get_int(args[2]); - int wrap = mp_obj_get_int(args[3]); - - Point p(x, y); - if(n_args == 5) { - int scale = mp_obj_get_int(args[4]); - display2->text(t, p, wrap, scale); - } - else - display2->text(t, p, wrap); - } - else if(mp_obj_is_float(args[0])) { - mp_raise_TypeError("can't convert 'float' object to str implicitly"); - } - else if(mp_obj_is_int(args[0])) { - mp_raise_TypeError("can't convert 'int' object to str implicitly"); - } - else if(mp_obj_is_bool(args[0])) { - mp_raise_TypeError("can't convert 'bool' object to str implicitly"); - } - else { - mp_raise_TypeError("can't convert object to str implicitly"); - } - } - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_const_none; -} -} diff --git a/micropython/modules/pico_display_2/pico_display_2.h b/micropython/modules/pico_display_2/pico_display_2.h deleted file mode 100644 index bccd69dd..00000000 --- a/micropython/modules/pico_display_2/pico_display_2.h +++ /dev/null @@ -1,26 +0,0 @@ -// Include MicroPython API. -#include "py/runtime.h" -#include "py/objstr.h" - -// Declare the functions we'll make available in Python -extern mp_obj_t picodisplay2_init(mp_obj_t buf_obj); -extern mp_obj_t picodisplay2_get_width(); -extern mp_obj_t picodisplay2_get_height(); -extern mp_obj_t picodisplay2_set_backlight(mp_obj_t brightness_obj); -extern mp_obj_t picodisplay2_update(); -extern mp_obj_t picodisplay2_set_led(mp_obj_t r_obj, mp_obj_t g_obj, mp_obj_t b_obj); -extern mp_obj_t picodisplay2_is_pressed(mp_obj_t button_obj); -extern mp_obj_t picodisplay2_flip(); - -// From PicoGraphics parent class -extern mp_obj_t picodisplay2_set_pen(mp_uint_t n_args, const mp_obj_t *args); -extern mp_obj_t picodisplay2_create_pen(mp_obj_t r_obj, mp_obj_t g_obj, mp_obj_t b_obj); -extern mp_obj_t picodisplay2_set_clip(mp_uint_t n_args, const mp_obj_t *args); -extern mp_obj_t picodisplay2_remove_clip(); -extern mp_obj_t picodisplay2_clear(); -extern mp_obj_t picodisplay2_pixel(mp_obj_t x_obj, mp_obj_t y_obj); -extern mp_obj_t picodisplay2_pixel_span(mp_obj_t x_obj, mp_obj_t y_obj, mp_obj_t l_obj); -extern mp_obj_t picodisplay2_rectangle(mp_uint_t n_args, const mp_obj_t *args); -extern mp_obj_t picodisplay2_circle(mp_obj_t x_obj, mp_obj_t y_obj, mp_obj_t r_obj); -extern mp_obj_t picodisplay2_character(mp_uint_t n_args, const mp_obj_t *args); -extern mp_obj_t picodisplay2_text(mp_uint_t n_args, const mp_obj_t *args); diff --git a/micropython/modules/pico_explorer/micropython.cmake b/micropython/modules/pico_explorer/micropython.cmake index 1df96c1a..6e9dca8d 100644 --- a/micropython/modules/pico_explorer/micropython.cmake +++ b/micropython/modules/pico_explorer/micropython.cmake @@ -2,11 +2,6 @@ add_library(usermod_pico_explorer INTERFACE) target_sources(usermod_pico_explorer INTERFACE ${CMAKE_CURRENT_LIST_DIR}/pico_explorer.c - ${CMAKE_CURRENT_LIST_DIR}/pico_explorer.cpp - ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_explorer/pico_explorer.cpp - ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/st7789/st7789.cpp - ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/pico_graphics.cpp - ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/types.cpp ) target_include_directories(usermod_pico_explorer INTERFACE @@ -22,5 +17,5 @@ target_link_libraries(usermod INTERFACE usermod_pico_explorer) set_source_files_properties( ${CMAKE_CURRENT_LIST_DIR}/pico_explorer.c PROPERTIES COMPILE_FLAGS - "-Wno-discarded-qualifiers -Wno-implicit-int" + "-Wno-discarded-qualifiers" ) diff --git a/micropython/modules/pico_explorer/pico_explorer.c b/micropython/modules/pico_explorer/pico_explorer.c index 6d8e05fb..1db88642 100644 --- a/micropython/modules/pico_explorer/pico_explorer.c +++ b/micropython/modules/pico_explorer/pico_explorer.c @@ -1,131 +1,46 @@ #include "pico_explorer.h" -/***** Constants *****/ -enum buttons -{ - BUTTON_A = 0, - BUTTON_B, - BUTTON_X, - BUTTON_Y, +const mp_rom_obj_tuple_t PicoExplorer_MOTOR1_pins = { + {&mp_type_tuple}, 2, { MP_ROM_INT(9), MP_ROM_INT(8) }, }; -enum adcs -{ - ADC0 = 0, - ADC1, - ADC2, +const mp_rom_obj_tuple_t PicoExplorer_MOTOR2_pins = { + {&mp_type_tuple}, 2, { MP_ROM_INT(11), MP_ROM_INT(10) }, }; -enum motors -{ - MOTOR1 = 0, - MOTOR2, -}; - -enum motions -{ - FORWARD = 0, - REVERSE, - STOP, -}; - -enum gps -{ - GP0 = 0, - GP1, - GP2, - GP3, - GP4, - GP5, - GP6, - GP7, -}; - - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// picoexplorer Module -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/***** Module Functions *****/ -STATIC MP_DEFINE_CONST_FUN_OBJ_1(picoexplorer_init_obj, picoexplorer_init); -STATIC MP_DEFINE_CONST_FUN_OBJ_0(picoexplorer_get_width_obj, picoexplorer_get_width); -STATIC MP_DEFINE_CONST_FUN_OBJ_0(picoexplorer_get_height_obj, picoexplorer_get_height); -STATIC MP_DEFINE_CONST_FUN_OBJ_0(picoexplorer_update_obj, picoexplorer_update); -STATIC MP_DEFINE_CONST_FUN_OBJ_1(picoexplorer_is_pressed_obj, picoexplorer_is_pressed); -STATIC MP_DEFINE_CONST_FUN_OBJ_1(picoexplorer_get_adc_obj, picoexplorer_get_adc); -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(picoexplorer_set_motor_obj, 2, 3, picoexplorer_set_motor); -STATIC MP_DEFINE_CONST_FUN_OBJ_1(picoexplorer_set_audio_pin_obj, picoexplorer_set_audio_pin); -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(picoexplorer_set_tone_obj, 1, 2, picoexplorer_set_tone); - -//From PicoGraphics parent class -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(picoexplorer_set_pen_obj, 1, 3, picoexplorer_set_pen); -STATIC MP_DEFINE_CONST_FUN_OBJ_3(picoexplorer_create_pen_obj, picoexplorer_create_pen); -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(picoexplorer_set_clip_obj, 4, 4, picoexplorer_set_clip); -STATIC MP_DEFINE_CONST_FUN_OBJ_0(picoexplorer_remove_clip_obj, picoexplorer_remove_clip); -STATIC MP_DEFINE_CONST_FUN_OBJ_0(picoexplorer_clear_obj, picoexplorer_clear); -STATIC MP_DEFINE_CONST_FUN_OBJ_2(picoexplorer_pixel_obj, picoexplorer_pixel); -STATIC MP_DEFINE_CONST_FUN_OBJ_3(picoexplorer_pixel_span_obj, picoexplorer_pixel_span); -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(picoexplorer_rectangle_obj, 4, 4, picoexplorer_rectangle); -STATIC MP_DEFINE_CONST_FUN_OBJ_3(picoexplorer_circle_obj, picoexplorer_circle); -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(picoexplorer_character_obj, 3, 4, picoexplorer_character); -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(picoexplorer_text_obj, 4, 5, picoexplorer_text); - - -/***** Globals Table *****/ STATIC const mp_map_elem_t picoexplorer_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_picoexplorer) }, - { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&picoexplorer_init_obj) }, - { MP_ROM_QSTR(MP_QSTR_get_width), MP_ROM_PTR(&picoexplorer_get_width_obj) }, - { MP_ROM_QSTR(MP_QSTR_get_height), MP_ROM_PTR(&picoexplorer_get_height_obj) }, - { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&picoexplorer_update_obj) }, - { MP_ROM_QSTR(MP_QSTR_is_pressed), MP_ROM_PTR(&picoexplorer_is_pressed_obj) }, - { MP_ROM_QSTR(MP_QSTR_get_adc), MP_ROM_PTR(&picoexplorer_get_adc_obj) }, - { MP_ROM_QSTR(MP_QSTR_set_motor), MP_ROM_PTR(&picoexplorer_set_motor_obj) }, - { MP_ROM_QSTR(MP_QSTR_set_audio_pin), MP_ROM_PTR(&picoexplorer_set_audio_pin_obj) }, - { MP_ROM_QSTR(MP_QSTR_set_tone), MP_ROM_PTR(&picoexplorer_set_tone_obj) }, - { MP_ROM_QSTR(MP_QSTR_set_pen), MP_ROM_PTR(&picoexplorer_set_pen_obj) }, - { MP_ROM_QSTR(MP_QSTR_create_pen), MP_ROM_PTR(&picoexplorer_create_pen_obj) }, - { MP_ROM_QSTR(MP_QSTR_set_clip), MP_ROM_PTR(&picoexplorer_set_clip_obj) }, - { MP_ROM_QSTR(MP_QSTR_remove_clip), MP_ROM_PTR(&picoexplorer_remove_clip_obj) }, - { MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&picoexplorer_clear_obj) }, - { MP_ROM_QSTR(MP_QSTR_pixel), MP_ROM_PTR(&picoexplorer_pixel_obj) }, - { MP_ROM_QSTR(MP_QSTR_pixel_span), MP_ROM_PTR(&picoexplorer_pixel_span_obj) }, - { MP_ROM_QSTR(MP_QSTR_rectangle), MP_ROM_PTR(&picoexplorer_rectangle_obj) }, - { MP_ROM_QSTR(MP_QSTR_circle), MP_ROM_PTR(&picoexplorer_circle_obj) }, - { MP_ROM_QSTR(MP_QSTR_character), MP_ROM_PTR(&picoexplorer_character_obj) }, - { MP_ROM_QSTR(MP_QSTR_text), MP_ROM_PTR(&picoexplorer_text_obj) }, - { MP_ROM_QSTR(MP_QSTR_BUTTON_A), MP_ROM_INT(BUTTON_A) }, - { MP_ROM_QSTR(MP_QSTR_BUTTON_B), MP_ROM_INT(BUTTON_B) }, - { MP_ROM_QSTR(MP_QSTR_BUTTON_X), MP_ROM_INT(BUTTON_X) }, - { MP_ROM_QSTR(MP_QSTR_BUTTON_Y), MP_ROM_INT(BUTTON_Y) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_A), MP_ROM_INT(12) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_B), MP_ROM_INT(13) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_X), MP_ROM_INT(14) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_Y), MP_ROM_INT(15) }, - { MP_ROM_QSTR(MP_QSTR_ADC0), MP_ROM_INT(ADC0) }, - { MP_ROM_QSTR(MP_QSTR_ADC1), MP_ROM_INT(ADC1) }, - { MP_ROM_QSTR(MP_QSTR_ADC2), MP_ROM_INT(ADC2) }, - { MP_ROM_QSTR(MP_QSTR_MOTOR1), MP_ROM_INT(MOTOR1) }, - { MP_ROM_QSTR(MP_QSTR_MOTOR2), MP_ROM_INT(MOTOR2) }, - { MP_ROM_QSTR(MP_QSTR_FORWARD), MP_ROM_INT(FORWARD) }, - { MP_ROM_QSTR(MP_QSTR_REVERSE), MP_ROM_INT(REVERSE) }, - { MP_ROM_QSTR(MP_QSTR_STOP), MP_ROM_INT(STOP) }, - { MP_ROM_QSTR(MP_QSTR_GP0), MP_ROM_INT(GP0) }, - { MP_ROM_QSTR(MP_QSTR_GP1), MP_ROM_INT(GP1) }, - { MP_ROM_QSTR(MP_QSTR_GP2), MP_ROM_INT(GP2) }, - { MP_ROM_QSTR(MP_QSTR_GP3), MP_ROM_INT(GP3) }, - { MP_ROM_QSTR(MP_QSTR_GP4), MP_ROM_INT(GP4) }, - { MP_ROM_QSTR(MP_QSTR_GP5), MP_ROM_INT(GP5) }, - { MP_ROM_QSTR(MP_QSTR_GP6), MP_ROM_INT(GP6) }, - { MP_ROM_QSTR(MP_QSTR_GP7), MP_ROM_INT(GP7) }, + { MP_ROM_QSTR(MP_QSTR_ADC0), MP_ROM_INT(26) }, + { MP_ROM_QSTR(MP_QSTR_ADC1), MP_ROM_INT(27) }, + { MP_ROM_QSTR(MP_QSTR_ADC2), MP_ROM_INT(28) }, + + { MP_ROM_QSTR(MP_QSTR_MOTOR_1), MP_ROM_PTR(&PicoExplorer_MOTOR1_pins) }, + { MP_ROM_QSTR(MP_QSTR_MOTOR_2), MP_ROM_PTR(&PicoExplorer_MOTOR2_pins) }, + + { MP_ROM_QSTR(MP_QSTR_GP0), MP_ROM_INT(0) }, + { MP_ROM_QSTR(MP_QSTR_GP1), MP_ROM_INT(1) }, + { MP_ROM_QSTR(MP_QSTR_GP2), MP_ROM_INT(2) }, + { MP_ROM_QSTR(MP_QSTR_GP3), MP_ROM_INT(3) }, + { MP_ROM_QSTR(MP_QSTR_GP4), MP_ROM_INT(4) }, + { MP_ROM_QSTR(MP_QSTR_GP5), MP_ROM_INT(5) }, + { MP_ROM_QSTR(MP_QSTR_GP6), MP_ROM_INT(6) }, + { MP_ROM_QSTR(MP_QSTR_GP7), MP_ROM_INT(7) }, }; STATIC MP_DEFINE_CONST_DICT(mp_module_picoexplorer_globals, picoexplorer_globals_table); -/***** Module Definition *****/ const mp_obj_module_t picoexplorer_user_cmodule = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_picoexplorer_globals, }; -//////////////////////////////////////////////////////////////////////////////////////////////////// +#if MICROPY_VERSION <= 70144 MP_REGISTER_MODULE(MP_QSTR_picoexplorer, picoexplorer_user_cmodule, MODULE_PICOEXPLORER_ENABLED); -//////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file +#else +MP_REGISTER_MODULE(MP_QSTR_picoexplorer, picoexplorer_user_cmodule); +#endif \ No newline at end of file diff --git a/micropython/modules/pico_explorer/pico_explorer.cpp b/micropython/modules/pico_explorer/pico_explorer.cpp deleted file mode 100644 index 1e0e8815..00000000 --- a/micropython/modules/pico_explorer/pico_explorer.cpp +++ /dev/null @@ -1,371 +0,0 @@ -#include "hardware/spi.h" -#include "hardware/sync.h" -#include "pico/binary_info.h" - -#include "libraries/pico_explorer/pico_explorer.hpp" - -using namespace pimoroni; - -PicoExplorer *explorer = nullptr; - - -extern "C" { -#include "pico_explorer.h" - -#define NOT_INITIALISED_MSG "Cannot call this function, as picoexplorer is not initialised. Call picoexplorer.init() first." - -mp_obj_t picoexplorer_buf_obj; - -mp_obj_t picoexplorer_init(mp_obj_t buf_obj) { - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(buf_obj, &bufinfo, MP_BUFFER_RW); - picoexplorer_buf_obj = buf_obj; - - // If a display already exists, delete it - if(explorer != nullptr) { - delete explorer; - } - - // Create a new display pointing to the newly provided buffer - explorer = new PicoExplorer((uint16_t *)bufinfo.buf); - explorer->init(); - - return mp_const_none; -} - -mp_obj_t picoexplorer_get_width() { - return mp_obj_new_int(PicoExplorer::WIDTH); -} - -mp_obj_t picoexplorer_get_height() { - return mp_obj_new_int(PicoExplorer::HEIGHT); -} - -mp_obj_t picoexplorer_update() { - if(explorer != nullptr) - explorer->update(); - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_const_none; -} - -mp_obj_t picoexplorer_is_pressed(mp_obj_t button_obj) { - bool buttonPressed = false; - - if(explorer != nullptr) { - int buttonID = mp_obj_get_int(button_obj); - switch(buttonID) { - case 0: - buttonPressed = explorer->is_pressed(PicoExplorer::A); - break; - - case 1: - buttonPressed = explorer->is_pressed(PicoExplorer::B); - break; - - case 2: - buttonPressed = explorer->is_pressed(PicoExplorer::X); - break; - - case 3: - buttonPressed = explorer->is_pressed(PicoExplorer::Y); - break; - - default: - mp_raise_ValueError("button not valid. Expected 0 to 3"); - break; - } - } - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return buttonPressed ? mp_const_true : mp_const_false; -} - -extern mp_obj_t picoexplorer_get_adc(mp_obj_t channel_obj) { - float reading = 0.0f; - - if(explorer != nullptr) { - int channel = mp_obj_get_int(channel_obj); - if(channel < 0 || channel > 2) - mp_raise_ValueError("adc channel not valid. Expected 0 to 2"); - else - reading = explorer->get_adc(channel); - } - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_obj_new_float(reading); -} - -extern mp_obj_t picoexplorer_set_motor(mp_uint_t n_args, const mp_obj_t *args) { - if(explorer != nullptr) { - int channel = mp_obj_get_int(args[0]); - int action = mp_obj_get_int(args[1]); - - if(channel < 0 || channel > 1) - mp_raise_ValueError("motor channel not valid. Expected 0 to 1"); - else if(action < 0 || action > 2) - mp_raise_ValueError("motor action not valid. Expected 0 to 2"); - else { - if(n_args == 3) { - float speed = mp_obj_get_float(args[2]); - explorer->set_motor(channel, action, speed); - } - else - explorer->set_motor(channel, action); - } - } - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_const_none; -} - -extern mp_obj_t picoexplorer_set_audio_pin(mp_obj_t pin_obj) { - if(explorer != nullptr) { - int pin = mp_obj_get_int(pin_obj); - explorer->set_audio_pin(pin); - } - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - return mp_const_none; -} - -extern mp_obj_t picoexplorer_set_tone(mp_uint_t n_args, const mp_obj_t *args) { - if(explorer != nullptr) { - int frequency = mp_obj_get_int(args[0]); - - if(n_args == 2) { - float duty = mp_obj_get_int(args[1]); - explorer->set_tone(frequency, duty); - } - else - explorer->set_tone(frequency); - } - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_const_none; -} - -mp_obj_t picoexplorer_set_pen(mp_uint_t n_args, const mp_obj_t *args) { - if(explorer != nullptr) { - switch(n_args) { - case 1: { - int p = mp_obj_get_int(args[0]); - - if(p < 0 || p > 0xffff) - mp_raise_ValueError("p is not a valid pen."); - else - explorer->set_pen(p); - } break; - - case 3: { - int r = mp_obj_get_int(args[0]); - int g = mp_obj_get_int(args[1]); - int b = mp_obj_get_int(args[2]); - - if(r < 0 || r > 255) - mp_raise_ValueError("r out of range. Expected 0 to 255"); - else if(g < 0 || g > 255) - mp_raise_ValueError("g out of range. Expected 0 to 255"); - else if(b < 0 || b > 255) - mp_raise_ValueError("b out of range. Expected 0 to 255"); - else - explorer->set_pen(r, g, b); - } break; - - default: { - char *buffer; - buffer = (char*)malloc(100); - sprintf(buffer, "function takes 1 or 3 (r,g,b) positional arguments but %d were given", n_args); - mp_raise_TypeError(buffer); - free(buffer); - } break; - } - } - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_const_none; -} - -mp_obj_t picoexplorer_create_pen(mp_obj_t r_obj, mp_obj_t g_obj, mp_obj_t b_obj) { - int pen = 0; - - if(explorer != nullptr) { - int r = mp_obj_get_int(r_obj); - int g = mp_obj_get_int(g_obj); - int b = mp_obj_get_int(b_obj); - - if(r < 0 || r > 255) - mp_raise_ValueError("r out of range. Expected 0 to 255"); - else if(g < 0 || g > 255) - mp_raise_ValueError("g out of range. Expected 0 to 255"); - else if(b < 0 || b > 255) - mp_raise_ValueError("b out of range. Expected 0 to 255"); - else - pen = explorer->create_pen(r, g, b); - } - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_obj_new_int(pen); -} - -mp_obj_t picoexplorer_set_clip(mp_uint_t n_args, const mp_obj_t *args) { - (void)n_args; //Unused input parameter, we know it's 4 - - if(explorer != nullptr) { - int x = mp_obj_get_int(args[0]); - int y = mp_obj_get_int(args[1]); - int w = mp_obj_get_int(args[2]); - int h = mp_obj_get_int(args[3]); - - Rect r(x, y, w, h); - explorer->set_clip(r); - } - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_const_none; -} - -mp_obj_t picoexplorer_remove_clip() { - if(explorer != nullptr) - explorer->remove_clip(); - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - return mp_const_none; -} - -mp_obj_t picoexplorer_clear() { - if(explorer != nullptr) - explorer->clear(); - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - return mp_const_none; -} - -mp_obj_t picoexplorer_pixel(mp_obj_t x_obj, mp_obj_t y_obj) { - if(explorer != nullptr) { - int x = mp_obj_get_int(x_obj); - int y = mp_obj_get_int(y_obj); - - Point p(x, y); - explorer->pixel(p); - } - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_const_none; -} - -mp_obj_t picoexplorer_pixel_span(mp_obj_t x_obj, mp_obj_t y_obj, mp_obj_t l_obj) { - if(explorer != nullptr) { - int x = mp_obj_get_int(x_obj); - int y = mp_obj_get_int(y_obj); - int l = mp_obj_get_int(l_obj); - - Point p(x, y); - explorer->pixel_span(p, l); - } - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_const_none; -} - -mp_obj_t picoexplorer_rectangle(mp_uint_t n_args, const mp_obj_t *args) { - (void)n_args; //Unused input parameter, we know it's 4 - - if(explorer != nullptr) { - int x = mp_obj_get_int(args[0]); - int y = mp_obj_get_int(args[1]); - int w = mp_obj_get_int(args[2]); - int h = mp_obj_get_int(args[3]); - - Rect r(x, y, w, h); - explorer->rectangle(r); - } - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_const_none; -} - -mp_obj_t picoexplorer_circle(mp_obj_t x_obj, mp_obj_t y_obj, mp_obj_t r_obj) { - if(explorer != nullptr) { - int x = mp_obj_get_int(x_obj); - int y = mp_obj_get_int(y_obj); - int r = mp_obj_get_int(r_obj); - - Point p(x, y); - explorer->circle(p, r); - } - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_const_none; -} - -mp_obj_t picoexplorer_character(mp_uint_t n_args, const mp_obj_t *args) { - if(explorer != nullptr) { - int c = mp_obj_get_int(args[0]); - int x = mp_obj_get_int(args[1]); - int y = mp_obj_get_int(args[2]); - - Point p(x, y); - if(n_args == 4) { - int scale = mp_obj_get_int(args[3]); - explorer->character((char)c, p, scale); - } - else - explorer->character((char)c, p); - } - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_const_none; -} - -mp_obj_t picoexplorer_text(mp_uint_t n_args, const mp_obj_t *args) { - if(explorer != nullptr) { - if(mp_obj_is_str_or_bytes(args[0])) { - GET_STR_DATA_LEN(args[0], str, str_len); - - std::string t((const char*)str); - - int x = mp_obj_get_int(args[1]); - int y = mp_obj_get_int(args[2]); - int wrap = mp_obj_get_int(args[3]); - - Point p(x, y); - if(n_args == 5) { - int scale = mp_obj_get_int(args[4]); - explorer->text(t, p, wrap, scale); - } - else - explorer->text(t, p, wrap); - } - else if(mp_obj_is_float(args[0])) { - mp_raise_TypeError("can't convert 'float' object to str implicitly"); - } - else if(mp_obj_is_int(args[0])) { - mp_raise_TypeError("can't convert 'int' object to str implicitly"); - } - else if(mp_obj_is_bool(args[0])) { - mp_raise_TypeError("can't convert 'bool' object to str implicitly"); - } - else { - mp_raise_TypeError("can't convert object to str implicitly"); - } - } - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); - - return mp_const_none; -} -} \ No newline at end of file diff --git a/micropython/modules/pico_explorer/pico_explorer.h b/micropython/modules/pico_explorer/pico_explorer.h index c7e65605..87a476d8 100644 --- a/micropython/modules/pico_explorer/pico_explorer.h +++ b/micropython/modules/pico_explorer/pico_explorer.h @@ -1,27 +1,2 @@ -// Include MicroPython API. #include "py/runtime.h" -#include "py/objstr.h" - -// Declare the functions we'll make available in Python -extern mp_obj_t picoexplorer_init(mp_obj_t buf_obj); -extern mp_obj_t picoexplorer_get_width(); -extern mp_obj_t picoexplorer_get_height(); -extern mp_obj_t picoexplorer_update(); -extern mp_obj_t picoexplorer_is_pressed(mp_obj_t button_obj); -extern mp_obj_t picoexplorer_get_adc(mp_obj_t channel_obj); -extern mp_obj_t picoexplorer_set_motor(mp_uint_t n_args, const mp_obj_t *args); -extern mp_obj_t picoexplorer_set_audio_pin(mp_obj_t pin_obj); -extern mp_obj_t picoexplorer_set_tone(mp_uint_t n_args, const mp_obj_t *args); - -// From PicoGraphics parent class -extern mp_obj_t picoexplorer_set_pen(mp_uint_t n_args, const mp_obj_t *args); -extern mp_obj_t picoexplorer_create_pen(mp_obj_t r_obj, mp_obj_t g_obj, mp_obj_t b_obj); -extern mp_obj_t picoexplorer_set_clip(mp_uint_t n_args, const mp_obj_t *args); -extern mp_obj_t picoexplorer_remove_clip(); -extern mp_obj_t picoexplorer_clear(); -extern mp_obj_t picoexplorer_pixel(mp_obj_t x_obj, mp_obj_t y_obj); -extern mp_obj_t picoexplorer_pixel_span(mp_obj_t x_obj, mp_obj_t y_obj, mp_obj_t l_obj); -extern mp_obj_t picoexplorer_rectangle(mp_uint_t n_args, const mp_obj_t *args); -extern mp_obj_t picoexplorer_circle(mp_obj_t x_obj, mp_obj_t y_obj, mp_obj_t r_obj); -extern mp_obj_t picoexplorer_character(mp_uint_t n_args, const mp_obj_t *args); -extern mp_obj_t picoexplorer_text(mp_uint_t n_args, const mp_obj_t *args); \ No newline at end of file +#include "py/objstr.h" \ No newline at end of file diff --git a/micropython/modules/pico_rgb_keypad/pico_rgb_keypad.c b/micropython/modules/pico_rgb_keypad/pico_rgb_keypad.c index f9bf8778..dfe3967a 100644 --- a/micropython/modules/pico_rgb_keypad/pico_rgb_keypad.c +++ b/micropython/modules/pico_rgb_keypad/pico_rgb_keypad.c @@ -39,6 +39,10 @@ const mp_obj_module_t picokeypad_user_cmodule = { }; //////////////////////////////////////////////////////////////////////////////////////////////////// +#if MICROPY_VERSION <= 70144 MP_REGISTER_MODULE(MP_QSTR_picokeypad, picokeypad_user_cmodule, MODULE_PICOKEYPAD_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_picokeypad, picokeypad_user_cmodule); +#endif //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/micropython/modules/pico_scroll/pico_scroll.c b/micropython/modules/pico_scroll/pico_scroll.c index f9412abd..7727a9a4 100644 --- a/micropython/modules/pico_scroll/pico_scroll.c +++ b/micropython/modules/pico_scroll/pico_scroll.c @@ -55,6 +55,10 @@ const mp_obj_module_t picoscroll_user_cmodule = { }; //////////////////////////////////////////////////////////////////////////////////////////////////// +#if MICROPY_VERSION <= 70144 MP_REGISTER_MODULE(MP_QSTR_picoscroll, picoscroll_user_cmodule, MODULE_PICOSCROLL_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_picoscroll, picoscroll_user_cmodule); +#endif //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/micropython/modules/pico_unicorn/pico_unicorn.c b/micropython/modules/pico_unicorn/pico_unicorn.c index b3cad262..6b4ad999 100755 --- a/micropython/modules/pico_unicorn/pico_unicorn.c +++ b/micropython/modules/pico_unicorn/pico_unicorn.c @@ -50,6 +50,10 @@ const mp_obj_module_t picounicorn_user_cmodule = { }; //////////////////////////////////////////////////////////////////////////////////////////////////// +#if MICROPY_VERSION <= 70144 MP_REGISTER_MODULE(MP_QSTR_picounicorn, picounicorn_user_cmodule, MODULE_PICOUNICORN_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_picounicorn, picounicorn_user_cmodule); +#endif //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/micropython/modules/pico_wireless/pico_wireless.c b/micropython/modules/pico_wireless/pico_wireless.c index 4863580e..55d2e81d 100644 --- a/micropython/modules/pico_wireless/pico_wireless.c +++ b/micropython/modules/pico_wireless/pico_wireless.c @@ -177,6 +177,10 @@ const mp_obj_module_t picowireless_user_cmodule = { }; //////////////////////////////////////////////////////////////////////////////////////////////////// +#if MICROPY_VERSION <= 70144 MP_REGISTER_MODULE(MP_QSTR_picowireless, picowireless_user_cmodule, MODULE_PICO_WIRELESS_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_picowireless, picowireless_user_cmodule); +#endif //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/micropython/modules/picographics/README.md b/micropython/modules/picographics/README.md new file mode 100644 index 00000000..81bd8915 --- /dev/null +++ b/micropython/modules/picographics/README.md @@ -0,0 +1,423 @@ +# Pico Graphics + +Pico Graphics is our unified graphics and display library for driving displays from your Pico in MicroPython. + +Pico Graphics replaces the individual drivers for displays- if you're been using breakout_colorlcd, ST7789 then you'll need to update your code! + +- [Setting up Pico Graphics](#setting-up-pico-graphics) + - [Supported Displays](#supported-displays) + - [Supported Graphics Modes (Pen Type)](#supported-graphics-modes-pen-type) + - [Supported Rotations](#supported-rotations) + - [Custom Pins](#custom-pins) +- [Function Reference](#function-reference) + - [General](#general) + - [Creating & Setting Pens](#creating--setting-pens) + - [Controlling The Backlight](#controlling-the-backlight) + - [Clipping](#clipping) + - [Clear](#clear) + - [Update](#update) + - [Text](#text) + - [Changing The Font](#changing-the-font) + - [Drawing Text](#drawing-text) + - [Basic Shapes](#basic-shapes) + - [Line](#line) + - [Circle](#circle) + - [Rectangle](#rectangle) + - [Triangle](#triangle) + - [Polygon](#polygon) + - [Pixels](#pixels) + - [Palette Management](#palette-management) + - [Utility Functions](#utility-functions) + - [Sprites](#sprites) + - [Loading Sprites](#loading-sprites) + - [Drawing Sprites](#drawing-sprites) + - [JPEG Files](#jpeg-files) + +## Setting up Pico Graphics + +You must construct an instance of PicoGraphics with your desired display: + +```python +from picographics import PicoGraphics, DISPLAY_LCD_160X80 + +display = PicoGraphics(display=DISPLAY_LCD_160X80) +``` + +Bear in mind that MicroPython has only 192K of RAM available- a 320x240 pixel display in RGB565 mode uses 150K! + +### Supported Displays + +* Pico Display - 240x135 SPI LCD - `DISPLAY_PICO_DISPLAY` +* Pico Display 2 - 320x240 SPI LCD - `DISPLAY_PICO_DISPLAY_2` +* Tufty 2040 - 320x240 Parallel LCD - `DISPLAY_TUFTY_2040` +* Pico Explorer - 240x240 SPI LCD - `DISPLAY_PICO_EXPLORER` +* Enviro Plus - 240x240 SPI LCD - `DISPLAY_ENVIRO_PLUS` +* 240x240 Round SPI LCD Breakout - `DISPLAY_ROUND_LCD_240X240` +* 240x240 Square SPI LCD Breakout - `DISPLAY_LCD_240X240` +* 160x80 SPI LCD Breakout - `DISPLAY_LCD_160X80` + +### Supported Graphics Modes (Pen Type) + +* 4-bit - `PEN_P4` - 16-colour palette of your choice +* 8-bit - `PEN_P8` - 256-colour palette of your choice +* 8-bit RGB332 - `PEN_RGB332` - 256 fixed colours (3 bits red, 3 bits green, 2 bits blue) +* 16-bit RGB565 - `PEN_RGB565` - 64K colours at the cost of RAM. (5 bits red, 6 bits green, 5 bits blue) + +These offer a tradeoff between RAM usage and available colours. In most cases you would probably use `RGB332` since it offers the easiest tradeoff. It's also the default. + +Eg: + +```python +display = PicoGraphics(display=PICO_DISPLAY, pen_type=PEN_RGB332) +``` + +### Supported Rotations + +All SPI LCDs support 0, 90, 180 and 270 degree rotations. + +Eg: + +```python +display = PicoGraphics(display=PICO_DISPLAY, roation=90) +``` + +### Custom Pins + +The `pimoroni_bus` library includes `SPIBus` for SPI LCDs and `ParallelBus` for Parallel LCDs (like Tufty 2040). + +In most cases you'll never need to use these, but they come in useful if you're wiring breakouts to your Pico or using multiple LCDs. + +A custom SPI bus: + +```python +from pimoroni_bus import SPIBus +from picographics import PicoGraphics, DISPLAY_PICO_EXPLORER, PEN_RGB332 + +spibus = SPIBus(cs=17, dc=16, sck=18, mosi=19) + +display = PicoGraphics(display=DISPLAY_PICO_EXPLORER, bus=spibus, pen_type=PEN_RGB332) +``` + +## Function Reference + +### General + +#### Creating & Setting Pens + +Create a pen colour for drawing into a screen: + +```python +my_pen = display.create_pen(r, g, b) +``` + +In RGB565 and RGB332 modes this packs the given RGB into an integer representing a colour in these formats and returns the result. + +In P4 and P8 modes this will consume one palette entry, or return an error if your palette is full. Palette colours are stored as RGB and converted when they are displayed on screen. + +To tell PicoGraphics which pen to use: + +```python +display.set_pen(my_pen) +``` + +This will be either an RGB332 or RGB565 colour, or a palette index. + +#### Controlling The Backlight + +You can set the display backlight brightness between `0.0` and `1.0`: + +```python +display.set_backlight(0.5) +``` + +#### Clipping + +Set the clipping bounds for drawing: + +```python +display.set_clip(x, y, w, h) +``` + +Remove the clipping bounds: + +```python +display.remove_clip() +``` + +#### Clear + +Clear the display to the current pen colour: + +```python +display.clear() +``` + +This is equivilent to: + +```python +w, h = display.get_bounds() +display.rectangle(0, 0, w, h) +``` + +You can clear portions of the screen with rectangles to save time redrawing things like JPEGs or complex graphics. + +#### Update + +Send the contents of your Pico Graphics buffer to your screen: + +```python +display.update() +``` + +### Text + +#### Changing The Font + +Change the font: + +```python +display.set_font(font) +``` + +Bitmap fonts. +These are aligned from their top-left corner. + +* `bitmap6` +* `bitmap8` +* `bitmap14_outline` + +Vector (Hershey) fonts. +These are aligned to their midline. + +* `sans` +* `gothic` +* `cursive` +* `serif_italic` +* `serif` + + +#### Drawing Text + +Write some text: + +```python +display.text(text, x, y, wordwrap, scale, angle, spacing) +``` + +* `text` - the text string to draw +* `x` - the destination X coordinate +* `y` - the destination Y coordinate +* `wordwrap` - number of pixels width before trying to break text into multiple lines +* `scale` - size +* `angle` - rotation angle (Vector only!) +* `spacing` - letter spacing + +Text scale can be a whole number (integer) for Bitmap fonts, or a decimal (float) for Vector (Hershey) fonts. + +For example: + +```python +display.set_font("bitmap8") +display.text("Hello World", 0, 0, scale=2) +``` + +Draws "Hello World" in a 16px tall, 2x scaled version of the `bitmap8` font. + +Sometimes you might want to measure a text string for centering or alignment on screen, you can do this with: + +```python +width = measure_text(text, scale, spacing) +``` + +The height of each Bitmap font is explicit in its name. + +Write a single character: + +```python +display.character(char, x, y, scale) +``` + +### Basic Shapes + +#### Line + +To draw a line: + +```python +display.line(x1, y1, x2, y2) +``` + +The X1/Y1 and X2/Y2 coordinates describe the start and end of the line repsectively. + +#### Circle + +To draw a circle: + +```python +display.circle(x, y, r) +``` + +* `x` - the destination X coordinate +* `y` - the destination Y coordinate +* `r` - the radius + +The X/Y coordinates describe the center of your circle. + +#### Rectangle + +```python +display.rectangle(x, y, w, h) +``` + +* `x` - the destination X coordinate +* `y` - the destination Y coordinate +* `w` - the width +* `h` - the eight + +#### Triangle + +```python +display.triangle(x1, y1, x2, y2, x3, y3) +``` + +The three pairs of X/Y coordinates describe each point of the triangle. + +#### Polygon + +To draw other shapes, you can provide a list of points to `polygon`: + +```python +display.polygon([ + (0, 10), + (20, 10), + (20, 0), + (30, 20), + (20, 30), + (20, 20), + (0, 20), +]) +``` + +### Pixels + +Setting individual pixels is slow, but you can do it with: + +```python +display.pixel(x, y) +``` + +You can set a horiontal span of pixels a little faster with: + +```python +pixel_span(x, y, length) +``` + +### Palette Management + +Intended for P4 and P8 modes. + +You have a 16-color and 256-color palette respectively. + +Set n elements in the palette from a list of RGB tuples: + +```python +set_palette([ + (r, g, b), + (r, g, b), + (r, g, b) +]) +``` + +Update an entry in the P4 or P8 palette with the given colour. + +```python +update_pen(index, r, g, b) +``` + +This is stored internally as RGB and converted to whatever format your screen requires when displayed. + +Reset a pen back to its default value (black, marked unused): + +```python +reset_pen(index) +``` + +#### Utility Functions + +Sometimes it can be useful to convert between colour formats: + +* `RGB332_to_RGB` +* `RGB_to_RGB332` +* `RGB565_to_RGB` +* `RGB_to_RGB565` + + +### Sprites + +Pico Display has very limited support for sprites in RGB332 mode. + +Sprites must be 8x8 pixels arranged in a 128x128 pixel spritesheet. 1-bit transparency is handled by electing a single colour to skip over. + +We've prepared some RGB332-compatible sprite assets for you, but you can use `spritesheet-to-rgb332.py ` to convert your own. + +#### Loading Sprites + +Use Thonny to upload your `spritesheet.rgb332` file onto your Pico. Then load it into Pico Graphics: + +```python +display.load_spritesheet("s4m_ur4i-dingbads.rgb332") +``` + +#### Drawing Sprites + +And finally display a sprite: + +```python +display.sprite(0, 0, 0, 0) +``` + +These arguments for `sprite` are as follows: + +1. Sprite X position (from 0-15) - this selects the horizontal location of an 8x8 sprite from your 128x128 pixel spritesheet. +2. Sprite Y position (from 0-15) +3. Destination X - where to draw on your screen horizontally +4. Destination Y = where to draw on your screen vertically +5. Scale (optional) - an integer scale value, 1 = 8x8, 2 = 16x16 etc. +6. Transparent (optional) - specify a colour to treat as transparent + +### JPEG Files + +We've included BitBank's JPEGDEC - https://github.com/bitbank2/JPEGDEC - so you can display JPEG files on your LCDs. + +Eg: + +```python +import picographics +import jpegdec + +display = picographics.PicoGraphics(display=picographics.DISPLAY_PICO_EXPLORER) + +# Create a new JPEG decoder for our PicoGraphics +j = jpegdec.JPEG(display) + +# Open the JPEG file +j.open_file("filename.jpeg") + +# Decode the JPEG +j.decode(0, 0, jpegdec.JPEG_SCALE_FULL) + +# Display the result +display.update() +``` + +JPEG files must be small enough to load into RAM for decoding, and must *not* be progressive. + +JPEG files will be automatically dithered in RGB332 mode. + +In P4 and P8 modes JPEGs are dithered to your custom colour paleete. Their appearance of an image will vary based on the colours you choose. + +The arguments for `decode` are as follows: + +1. Decode X - where to place the decoded JPEG on screen +2. Decode Y +3. Flags - one of `JPEG_SCALE_FULL`, `JPEG_SCALE_HALF`, `JPEG_SCALE_QUARTER` or `JPEG_SCALE_EIGHTH` \ No newline at end of file diff --git a/micropython/modules/picographics/micropython.cmake b/micropython/modules/picographics/micropython.cmake new file mode 100644 index 00000000..9a1675af --- /dev/null +++ b/micropython/modules/picographics/micropython.cmake @@ -0,0 +1,36 @@ +set(MOD_NAME picographics) +string(TOUPPER ${MOD_NAME} MOD_NAME_UPPER) +add_library(usermod_${MOD_NAME} INTERFACE) + +target_sources(usermod_${MOD_NAME} INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.c + ${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.cpp + ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/st7789/st7789.cpp + ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/st7735/st7735.cpp + ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/sh1107/sh1107.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_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 + ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/pico_graphics_pen_rgb565.cpp + ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/types.cpp +) + +pico_generate_pio_header(usermod_${MOD_NAME} ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/st7789/st7789_parallel.pio) + +target_include_directories(usermod_${MOD_NAME} INTERFACE + ${CMAKE_CURRENT_LIST_DIR} +) + +target_compile_definitions(usermod_${MOD_NAME} INTERFACE + -DMODULE_${MOD_NAME_UPPER}_ENABLED=1 +) + +target_link_libraries(usermod INTERFACE usermod_${MOD_NAME}) + +set_source_files_properties( + ${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.c + PROPERTIES COMPILE_FLAGS + "-Wno-discarded-qualifiers" +) \ No newline at end of file diff --git a/micropython/modules/picographics/picographics.c b/micropython/modules/picographics/picographics.c new file mode 100644 index 00000000..63bafca8 --- /dev/null +++ b/micropython/modules/picographics/picographics.c @@ -0,0 +1,140 @@ +#include "picographics.h" + +// Module functions +STATIC MP_DEFINE_CONST_FUN_OBJ_3(ModPicoGraphics_module_RGB_to_RGB332_obj, ModPicoGraphics_module_RGB_to_RGB332); +STATIC MP_DEFINE_CONST_FUN_OBJ_3(ModPicoGraphics_module_RGB_to_RGB565_obj, ModPicoGraphics_module_RGB_to_RGB565); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(ModPicoGraphics_module_RGB332_to_RGB_obj, ModPicoGraphics_module_RGB332_to_RGB); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(ModPicoGraphics_module_RGB565_to_RGB_obj, ModPicoGraphics_module_RGB565_to_RGB); +STATIC MP_DEFINE_CONST_FUN_OBJ_2(ModPicoGraphics_get_required_buffer_size_obj, ModPicoGraphics_get_required_buffer_size); + +// Class Methods +MP_DEFINE_CONST_FUN_OBJ_1(ModPicoGraphics_update_obj, ModPicoGraphics_update); +MP_DEFINE_CONST_FUN_OBJ_2(ModPicoGraphics_set_backlight_obj, ModPicoGraphics_set_backlight); + +// Palette management +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ModPicoGraphics_update_pen_obj, 5, 5, ModPicoGraphics_update_pen); +MP_DEFINE_CONST_FUN_OBJ_2(ModPicoGraphics_reset_pen_obj, ModPicoGraphics_reset_pen); +MP_DEFINE_CONST_FUN_OBJ_KW(ModPicoGraphics_set_palette_obj, 2, ModPicoGraphics_set_palette); + +// Pen +MP_DEFINE_CONST_FUN_OBJ_2(ModPicoGraphics_set_pen_obj, ModPicoGraphics_set_pen); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ModPicoGraphics_create_pen_obj, 4, 4, ModPicoGraphics_create_pen); + +// Primitives +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ModPicoGraphics_set_clip_obj, 5, 5, ModPicoGraphics_set_clip); +MP_DEFINE_CONST_FUN_OBJ_1(ModPicoGraphics_remove_clip_obj, ModPicoGraphics_remove_clip); +MP_DEFINE_CONST_FUN_OBJ_1(ModPicoGraphics_clear_obj, ModPicoGraphics_clear); +MP_DEFINE_CONST_FUN_OBJ_3(ModPicoGraphics_pixel_obj, ModPicoGraphics_pixel); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ModPicoGraphics_pixel_span_obj, 4, 4, ModPicoGraphics_pixel_span); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ModPicoGraphics_rectangle_obj, 5, 5, ModPicoGraphics_rectangle); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ModPicoGraphics_circle_obj, 4, 4, ModPicoGraphics_circle); +MP_DEFINE_CONST_FUN_OBJ_KW(ModPicoGraphics_character_obj, 1, ModPicoGraphics_character); +MP_DEFINE_CONST_FUN_OBJ_KW(ModPicoGraphics_text_obj, 1, ModPicoGraphics_text); +MP_DEFINE_CONST_FUN_OBJ_KW(ModPicoGraphics_measure_text_obj, 1, ModPicoGraphics_measure_text); +MP_DEFINE_CONST_FUN_OBJ_KW(ModPicoGraphics_polygon_obj, 2, ModPicoGraphics_polygon); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ModPicoGraphics_triangle_obj, 7, 7, ModPicoGraphics_triangle); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ModPicoGraphics_line_obj, 5, 5, ModPicoGraphics_line); + +// Sprites +MP_DEFINE_CONST_FUN_OBJ_2(ModPicoGraphics_set_spritesheet_obj, ModPicoGraphics_set_spritesheet); +MP_DEFINE_CONST_FUN_OBJ_2(ModPicoGraphics_load_spritesheet_obj, ModPicoGraphics_load_spritesheet); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ModPicoGraphics_sprite_obj, 5, 7, ModPicoGraphics_sprite); + +// Utility +//MP_DEFINE_CONST_FUN_OBJ_2(ModPicoGraphics_set_scanline_callback_obj, ModPicoGraphics_set_scanline_callback); +MP_DEFINE_CONST_FUN_OBJ_1(ModPicoGraphics_get_bounds_obj, ModPicoGraphics_get_bounds); +MP_DEFINE_CONST_FUN_OBJ_2(ModPicoGraphics_set_font_obj, ModPicoGraphics_set_font); +MP_DEFINE_CONST_FUN_OBJ_2(ModPicoGraphics_set_framebuffer_obj, ModPicoGraphics_set_framebuffer); + + +MP_DEFINE_CONST_FUN_OBJ_1(ModPicoGraphics__del__obj, ModPicoGraphics__del__); + + +STATIC const mp_rom_map_elem_t ModPicoGraphics_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_pixel), MP_ROM_PTR(&ModPicoGraphics_pixel_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_pen), MP_ROM_PTR(&ModPicoGraphics_set_pen_obj) }, + { MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&ModPicoGraphics_clear_obj) }, + + { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&ModPicoGraphics_update_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_clip), MP_ROM_PTR(&ModPicoGraphics_set_clip_obj) }, + { MP_ROM_QSTR(MP_QSTR_remove_clip), MP_ROM_PTR(&ModPicoGraphics_remove_clip_obj) }, + { MP_ROM_QSTR(MP_QSTR_pixel_span), MP_ROM_PTR(&ModPicoGraphics_pixel_span_obj) }, + { MP_ROM_QSTR(MP_QSTR_rectangle), MP_ROM_PTR(&ModPicoGraphics_rectangle_obj) }, + { MP_ROM_QSTR(MP_QSTR_circle), MP_ROM_PTR(&ModPicoGraphics_circle_obj) }, + { MP_ROM_QSTR(MP_QSTR_character), MP_ROM_PTR(&ModPicoGraphics_character_obj) }, + { MP_ROM_QSTR(MP_QSTR_text), MP_ROM_PTR(&ModPicoGraphics_text_obj) }, + { MP_ROM_QSTR(MP_QSTR_measure_text), MP_ROM_PTR(&ModPicoGraphics_measure_text_obj) }, + { MP_ROM_QSTR(MP_QSTR_polygon), MP_ROM_PTR(&ModPicoGraphics_polygon_obj) }, + { MP_ROM_QSTR(MP_QSTR_triangle), MP_ROM_PTR(&ModPicoGraphics_triangle_obj) }, + { MP_ROM_QSTR(MP_QSTR_line), MP_ROM_PTR(&ModPicoGraphics_line_obj) }, + + { MP_ROM_QSTR(MP_QSTR_set_spritesheet), MP_ROM_PTR(&ModPicoGraphics_set_spritesheet_obj) }, + { MP_ROM_QSTR(MP_QSTR_load_spritesheet), MP_ROM_PTR(&ModPicoGraphics_load_spritesheet_obj) }, + { MP_ROM_QSTR(MP_QSTR_sprite), MP_ROM_PTR(&ModPicoGraphics_sprite_obj) }, + + { MP_ROM_QSTR(MP_QSTR_create_pen), MP_ROM_PTR(&ModPicoGraphics_create_pen_obj) }, + { MP_ROM_QSTR(MP_QSTR_update_pen), MP_ROM_PTR(&ModPicoGraphics_update_pen_obj) }, + { MP_ROM_QSTR(MP_QSTR_reset_pen), MP_ROM_PTR(&ModPicoGraphics_reset_pen_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_palette), MP_ROM_PTR(&ModPicoGraphics_set_palette_obj) }, + + { MP_ROM_QSTR(MP_QSTR_set_backlight), MP_ROM_PTR(&ModPicoGraphics_set_backlight_obj) }, + + //{ MP_ROM_QSTR(MP_QSTR_set_scanline_callback), MP_ROM_PTR(&ModPicoGraphics_set_scanline_callback_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_bounds), MP_ROM_PTR(&ModPicoGraphics_get_bounds_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_font), MP_ROM_PTR(&ModPicoGraphics_set_font_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_framebuffer), MP_ROM_PTR(&ModPicoGraphics_set_framebuffer_obj) }, + + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&ModPicoGraphics__del__obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(ModPicoGraphics_locals_dict, ModPicoGraphics_locals_dict_table); + +/***** Class Definition *****/ +const mp_obj_type_t ModPicoGraphics_type = { + { &mp_type_type }, + .name = MP_QSTR_picographics, + .make_new = ModPicoGraphics_make_new, + .buffer_p = { .get_buffer = ModPicoGraphics_get_framebuffer }, + .locals_dict = (mp_obj_dict_t*)&ModPicoGraphics_locals_dict, +}; + +/***** Module Globals *****/ +STATIC const mp_map_elem_t picographics_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_picographics) }, + { MP_ROM_QSTR(MP_QSTR_PicoGraphics), (mp_obj_t)&ModPicoGraphics_type }, + { MP_ROM_QSTR(MP_QSTR_get_buffer_size), MP_ROM_PTR(&ModPicoGraphics_get_required_buffer_size_obj) }, + + // Colour conversion + { MP_ROM_QSTR(MP_QSTR_RGB_to_RGB332), MP_ROM_PTR(&ModPicoGraphics_module_RGB_to_RGB332_obj) }, + { MP_ROM_QSTR(MP_QSTR_RGB_to_RGB565), MP_ROM_PTR(&ModPicoGraphics_module_RGB_to_RGB565_obj) }, + { MP_ROM_QSTR(MP_QSTR_RGB332_to_RGB), MP_ROM_PTR(&ModPicoGraphics_module_RGB332_to_RGB_obj) }, + { MP_ROM_QSTR(MP_QSTR_RGB565_to_RGB), MP_ROM_PTR(&ModPicoGraphics_module_RGB565_to_RGB_obj) }, + + { MP_ROM_QSTR(MP_QSTR_DISPLAY_LCD_240X240), MP_ROM_INT(DISPLAY_LCD_240X240) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_ROUND_LCD_240X240), MP_ROM_INT(DISPLAY_ROUND_LCD_240X240) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_PICO_DISPLAY), MP_ROM_INT(DISPLAY_PICO_DISPLAY) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_PICO_DISPLAY_2), MP_ROM_INT(DISPLAY_PICO_DISPLAY_2) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_PICO_EXPLORER), MP_ROM_INT(DISPLAY_PICO_EXPLORER) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_TUFTY_2040), MP_ROM_INT(DISPLAY_TUFTY_2040) }, + { 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_PEN_1BIT), MP_ROM_INT(PEN_1BIT) }, + { MP_ROM_QSTR(MP_QSTR_PEN_P4), MP_ROM_INT(PEN_P4) }, + { MP_ROM_QSTR(MP_QSTR_PEN_P8), MP_ROM_INT(PEN_P8) }, + { MP_ROM_QSTR(MP_QSTR_PEN_RGB332), MP_ROM_INT(PEN_RGB332) }, + { MP_ROM_QSTR(MP_QSTR_PEN_RGB565), MP_ROM_INT(PEN_RGB565) }, +}; +STATIC MP_DEFINE_CONST_DICT(mp_module_picographics_globals, picographics_globals_table); + +/***** Module Definition *****/ +const mp_obj_module_t picographics_user_cmodule = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_picographics_globals, +}; + +#if MICROPY_VERSION <= 70144 +MP_REGISTER_MODULE(MP_QSTR_picographics, picographics_user_cmodule, MODULE_PICOGRAPHICS_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_picographics, picographics_user_cmodule); +#endif \ No newline at end of file diff --git a/micropython/modules/picographics/picographics.cpp b/micropython/modules/picographics/picographics.cpp new file mode 100644 index 00000000..abffeb59 --- /dev/null +++ b/micropython/modules/picographics/picographics.cpp @@ -0,0 +1,760 @@ +#include "drivers/st7789/st7789.hpp" +#include "drivers/st7735/st7735.hpp" +#include "drivers/sh1107/sh1107.hpp" +#include "libraries/pico_graphics/pico_graphics.hpp" +#include "common/pimoroni_common.hpp" +#include "common/pimoroni_bus.hpp" +#include "common/pimoroni_i2c.hpp" + +#include "micropython/modules/util.hpp" + +using namespace pimoroni; + +extern "C" { +#include "picographics.h" +#include "micropython/modules/pimoroni_bus/pimoroni_bus.h" +#include "pimoroni_i2c.h" +#include "py/stream.h" +#include "py/reader.h" +#include "extmod/vfs.h" + +std::string mp_obj_to_string_r(const mp_obj_t &obj) { + if(mp_obj_is_str_or_bytes(obj)) { + GET_STR_DATA_LEN(obj, str, str_len); + return (const char*)str; + } + mp_raise_TypeError("can't convert object to str implicitly"); +} + +typedef struct _ModPicoGraphics_obj_t { + mp_obj_base_t base; + PicoGraphics *graphics; + DisplayDriver *display; + void *spritedata; + void *buffer; + //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) { + switch(display) { + case DISPLAY_PICO_DISPLAY: + width = 240; + height = 135; + // Portrait to match labelling + if(rotate == -1) rotate = (int)Rotation::ROTATE_270; + break; + case DISPLAY_PICO_DISPLAY_2: + case DISPLAY_TUFTY_2040: + width = 320; + height = 240; + // Tufty display is upside-down + if(rotate == -1) rotate = (int)Rotation::ROTATE_180; + break; + case DISPLAY_PICO_EXPLORER: + case DISPLAY_LCD_240X240: + case DISPLAY_ENVIRO_PLUS: + width = 240; + height = 240; + break; + case DISPLAY_ROUND_LCD_240X240: + width = 240; + height = 240; + break; + case DISPLAY_LCD_160X80: + width = 160; + height = 80; + break; + case DISPLAY_I2C_OLED_128X128: + width = 128; + height = 128; + if(rotate == -1) rotate = (int)Rotation::ROTATE_0; + break; + default: + return false; + } + return true; +} + +size_t get_required_buffer_size(PicoGraphicsPenType pen_type, uint width, uint height) { + switch(pen_type) { + case PEN_1BIT: + return PicoGraphics_Pen1Bit::buffer_size(width, height); + case PEN_P4: + return PicoGraphics_PenP4::buffer_size(width, height); + case PEN_P8: + return PicoGraphics_PenP8::buffer_size(width, height); + case PEN_RGB332: + return PicoGraphics_PenRGB332::buffer_size(width, height); + case PEN_RGB565: + return PicoGraphics_PenRGB565::buffer_size(width, height); + default: + return 0; + } +} + +mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + ModPicoGraphics_obj_t *self = nullptr; + + enum { ARG_display, ARG_rotate, ARG_bus, ARG_buffer, ARG_pen_type }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_display, MP_ARG_INT | MP_ARG_REQUIRED }, + { 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 } }, + }; + + // Parse args. + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + self = m_new_obj_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 rotate = args[ARG_rotate].u_int; + if(!get_display_settings(display, width, height, rotate)) mp_raise_ValueError("Unsupported display!"); + if(rotate == -1) rotate = (int)Rotation::ROTATE_0; + + // Try to create an appropriate display driver + if (display == DISPLAY_TUFTY_2040) { + if (args[ARG_bus].u_obj == mp_const_none) { + self->display = m_new_class(ST7789, width, height, (Rotation)rotate, {10, 11, 12, 13, 14, 2}); + } else if (mp_obj_is_type(args[ARG_bus].u_obj, &ParallelPins_type)) { + _PimoroniBus_obj_t *bus = (_PimoroniBus_obj_t *)MP_OBJ_TO_PTR(args[ARG_bus].u_obj); + self->display = m_new_class(ST7789, width, height, (Rotation)rotate, *(ParallelPins *)(bus->pins)); + } else { + mp_raise_ValueError("ParallelBus expected!"); + } + } else if (display == DISPLAY_LCD_160X80) { + if (args[ARG_bus].u_obj == mp_const_none) { + self->display = m_new_class(ST7735, width, height, get_spi_pins(BG_SPI_FRONT)); + } else if (mp_obj_is_type(args[ARG_bus].u_obj, &SPIPins_type)) { + _PimoroniBus_obj_t *bus = (_PimoroniBus_obj_t *)MP_OBJ_TO_PTR(args[ARG_bus].u_obj); + self->display = m_new_class(ST7735, width, height, *(SPIPins *)(bus->pins)); + } else { + mp_raise_ValueError("SPIBus expected!"); + } + } else if (display == DISPLAY_I2C_OLED_128X128) { + if (args[ARG_bus].u_obj == mp_const_none || mp_obj_is_type(args[ARG_bus].u_obj, &PimoroniI2C_type)) { + _PimoroniI2C_obj_t *i2c = PimoroniI2C_from_machine_i2c_or_native(args[ARG_bus].u_obj); + self->display = m_new_class(SH1107, width, height, *(pimoroni::I2C *)(i2c->i2c)); + } else { + mp_raise_ValueError("I2C bus 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)); + } else if (mp_obj_is_type(args[ARG_bus].u_obj, &SPIPins_type)) { + _PimoroniBus_obj_t *bus = (_PimoroniBus_obj_t *)MP_OBJ_TO_PTR(args[ARG_bus].u_obj); + self->display = m_new_class(ST7789, width, height, (Rotation)rotate, round, *(SPIPins *)(bus->pins)); + } else { + mp_raise_ValueError("SPIBus expected!"); + } + } + + // Create or fetch buffer + size_t required_size = get_required_buffer_size((PicoGraphicsPenType)pen_type, width, height); + if(required_size == 0) mp_raise_ValueError("Unsupported pen type!"); + + if (args[ARG_buffer].u_obj != mp_const_none) { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_RW); + self->buffer = bufinfo.buf; + if(bufinfo.len < (size_t)(required_size)) { + mp_raise_ValueError("Supplied buffer is too small!"); + } + } else { + self->buffer = m_new(uint8_t, required_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) { + case PEN_1BIT: + 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); + break; + case PEN_P8: + self->graphics = m_new_class(PicoGraphics_PenP8, self->display->width, self->display->height, self->buffer); + break; + case PEN_RGB332: + self->graphics = m_new_class(PicoGraphics_PenRGB332, self->display->width, self->display->height, self->buffer); + break; + case PEN_RGB565: + self->graphics = m_new_class(PicoGraphics_PenRGB565, self->display->width, self->display->height, self->buffer); + break; + default: + break; + } + + //self->scanline_callback = mp_const_none; + + self->spritedata = nullptr; + + // Clear the buffer + self->graphics->set_pen(0); + self->graphics->clear(); + + // Update the LCD from the graphics library + self->display->update(self->graphics); + + return MP_OBJ_FROM_PTR(self); +} + +mp_obj_t ModPicoGraphics__del__(mp_obj_t self_in) { + ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t); + self->display->cleanup(); + return mp_const_none; +} + +mp_obj_t ModPicoGraphics_set_spritesheet(mp_obj_t self_in, mp_obj_t spritedata) { + ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t); + if(spritedata == mp_const_none) { + self->spritedata = nullptr; + } else { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(spritedata, &bufinfo, MP_BUFFER_RW); + + int required_size = get_required_buffer_size((PicoGraphicsPenType)self->graphics->pen_type, 128, 128); + + if(bufinfo.len != (size_t)(required_size)) { + mp_raise_ValueError("Spritesheet the wrong size!"); + } + + self->spritedata = bufinfo.buf; + } + return mp_const_none; +} + +mp_obj_t ModPicoGraphics_load_spritesheet(mp_obj_t self_in, mp_obj_t filename) { + ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t); + mp_obj_t args[2] = { + filename, + MP_OBJ_NEW_QSTR(MP_QSTR_r), + }; + + // Stat the file to get its size + // example tuple response: (32768, 0, 0, 0, 0, 0, 5153, 1654709815, 1654709815, 1654709815) + mp_obj_t stat = mp_vfs_stat(filename); + mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR2(stat, mp_obj_tuple_t); + size_t filesize = mp_obj_get_int(tuple->items[6]); + + mp_buffer_info_t bufinfo; + bufinfo.buf = (void *)m_new(uint8_t, filesize); + mp_obj_t file = mp_vfs_open(MP_ARRAY_SIZE(args), &args[0], (mp_map_t *)&mp_const_empty_map); + int errcode; + bufinfo.len = mp_stream_rw(file, bufinfo.buf, filesize, &errcode, MP_STREAM_RW_READ | MP_STREAM_RW_ONCE); + if (errcode != 0) { + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Failed to open sprite file!")); + } + + self->spritedata = bufinfo.buf; + + return mp_const_none; +} + +mp_obj_t ModPicoGraphics_sprite(size_t n_args, const mp_obj_t *args) { + enum { ARG_self, ARG_sprite_x, ARG_sprite_y, ARG_x, ARG_y, ARG_scale, ARG_transparent }; + + ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self], ModPicoGraphics_obj_t); + + if(self->spritedata == nullptr) return mp_const_false; + + int scale = 1; + int transparent = 0; + + if(n_args >= 6) scale = mp_obj_get_int(args[ARG_scale]); + if(n_args >= 7) transparent = mp_obj_get_int(args[ARG_transparent]); + + self->graphics->sprite( + self->spritedata, + {mp_obj_get_int(args[ARG_sprite_x]), mp_obj_get_int(args[ARG_sprite_y])}, + {mp_obj_get_int(args[ARG_x]), mp_obj_get_int(args[ARG_y])}, + scale, + transparent + ); + + return mp_const_true; +} + +mp_obj_t ModPicoGraphics_set_font(mp_obj_t self_in, mp_obj_t font) { + ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t); + self->graphics->set_font(mp_obj_to_string_r(font)); + return mp_const_none; +} + +mp_int_t ModPicoGraphics_get_framebuffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { + ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t); + (void)flags; + bufinfo->buf = self->graphics->frame_buffer; + bufinfo->len = get_required_buffer_size((PicoGraphicsPenType)self->graphics->pen_type, self->graphics->bounds.w, self->graphics->bounds.h); + bufinfo->typecode = 'B'; + return 0; +} + +mp_obj_t ModPicoGraphics_set_framebuffer(mp_obj_t self_in, mp_obj_t framebuffer) { + ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t); + + if (framebuffer == mp_const_none) { + m_del(uint8_t, self->buffer, self->graphics->bounds.w * self->graphics->bounds.h); + self->buffer = nullptr; + self->graphics->set_framebuffer(nullptr); + } else { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(framebuffer, &bufinfo, MP_BUFFER_RW); + + // This should have no effect if you try to replace the framebuffer with itself + // but it will free up a buffer that has no other references + if(self->buffer != nullptr) { + m_del(uint8_t, self->buffer, self->graphics->bounds.w * self->graphics->bounds.h); + } + + self->buffer = bufinfo.buf; + self->graphics->set_framebuffer(self->buffer); + } + return mp_const_none; +} + +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); + if(required_size == 0) mp_raise_ValueError("Unsupported pen type!"); + + return mp_obj_new_int(required_size); +} + +mp_obj_t ModPicoGraphics_get_bounds(mp_obj_t self_in) { + ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t); + mp_obj_t tuple[2] = { + mp_obj_new_int(self->graphics->bounds.w), + mp_obj_new_int(self->graphics->bounds.h) + }; + return mp_obj_new_tuple(2, tuple); +} + +/* +mp_obj_t ModPicoGraphics_set_scanline_callback(mp_obj_t self_in, mp_obj_t cb_in) { + ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t); + self->scanline_callback = cb_in; + return mp_const_none; +} +*/ + +mp_obj_t ModPicoGraphics_update(mp_obj_t self_in) { + ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t); +/* + if(self->scanline_callback != mp_const_none) { + self->graphics->scanline_interrupt = [self](int y){ + mp_obj_t args[] = { + mp_obj_new_int(y) + }; + mp_call_function_n_kw(self->scanline_callback, MP_ARRAY_SIZE(args), 0, args); + }; + } else { + self->graphics->scanline_interrupt = nullptr; + } +*/ + self->display->update(self->graphics); + + return mp_const_none; +} + +mp_obj_t ModPicoGraphics_set_backlight(mp_obj_t self_in, mp_obj_t brightness) { + ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t); + + float b = mp_obj_get_float(brightness); + + if(b < 0 || b > 1.0f) mp_raise_ValueError("brightness out of range. Expected 0.0 to 1.0"); + + self->display->set_backlight((uint8_t)(b * 255.0f)); + + return mp_const_none; +} + +mp_obj_t ModPicoGraphics_module_RGB332_to_RGB(mp_obj_t rgb332) { + RGB c((RGB332)mp_obj_get_int(rgb332)); + mp_obj_t t[] = { + mp_obj_new_int(c.r), + mp_obj_new_int(c.g), + mp_obj_new_int(c.b), + }; + return mp_obj_new_tuple(3, t); +} + +mp_obj_t ModPicoGraphics_module_RGB565_to_RGB(mp_obj_t rgb565) { + RGB c((RGB565)mp_obj_get_int(rgb565)); + mp_obj_t t[] = { + mp_obj_new_int(c.r), + mp_obj_new_int(c.g), + mp_obj_new_int(c.b), + }; + return mp_obj_new_tuple(3, t); +} + +mp_obj_t ModPicoGraphics_module_RGB_to_RGB332(mp_obj_t r, mp_obj_t g, mp_obj_t b) { + return mp_obj_new_int(RGB( + mp_obj_get_int(r), + mp_obj_get_int(g), + mp_obj_get_int(b) + ).to_rgb332()); +} + +mp_obj_t ModPicoGraphics_module_RGB_to_RGB565(mp_obj_t r, mp_obj_t g, mp_obj_t b) { + return mp_obj_new_int(RGB( + mp_obj_get_int(r), + mp_obj_get_int(g), + mp_obj_get_int(b) + ).to_rgb565()); +} + +mp_obj_t ModPicoGraphics_set_pen(mp_obj_t self_in, mp_obj_t pen) { + ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t); + + self->graphics->set_pen(mp_obj_get_int(pen)); + + return mp_const_none; +} + +mp_obj_t ModPicoGraphics_reset_pen(mp_obj_t self_in, mp_obj_t pen) { + ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t); + + self->graphics->reset_pen(mp_obj_get_int(pen)); + + return mp_const_none; +} + +mp_obj_t ModPicoGraphics_update_pen(size_t n_args, const mp_obj_t *args) { + enum { ARG_self, ARG_i, ARG_r, ARG_g, ARG_b }; + + ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self], ModPicoGraphics_obj_t); + + self->graphics->update_pen( + mp_obj_get_int(args[ARG_i]) & 0xff, + mp_obj_get_int(args[ARG_r]) & 0xff, + mp_obj_get_int(args[ARG_g]) & 0xff, + mp_obj_get_int(args[ARG_b]) & 0xff + ); + + return mp_const_none; +} + +mp_obj_t ModPicoGraphics_create_pen(size_t n_args, const mp_obj_t *args) { + enum { ARG_self, ARG_r, ARG_g, ARG_b }; + + ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self], ModPicoGraphics_obj_t); + + int result = self->graphics->create_pen( + mp_obj_get_int(args[ARG_r]) & 0xff, + mp_obj_get_int(args[ARG_g]) & 0xff, + mp_obj_get_int(args[ARG_b]) & 0xff + ); + + if (result == -1) mp_raise_ValueError("create_pen failed. No matching colour or space in palette!"); + + return mp_obj_new_int(result); +} + +mp_obj_t ModPicoGraphics_set_palette(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + size_t num_tuples = n_args - 1; + const mp_obj_t *tuples = pos_args + 1; + + ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(pos_args[0], ModPicoGraphics_obj_t); + + // Check if there is only one argument, which might be a list + if(n_args == 2) { + if(mp_obj_is_type(pos_args[1], &mp_type_list)) { + mp_obj_list_t *points = MP_OBJ_TO_PTR2(pos_args[1], mp_obj_list_t); + + if(points->len <= 0) mp_raise_ValueError("set_palette(): cannot provide an empty list"); + + num_tuples = points->len; + tuples = points->items; + } + else { + mp_raise_TypeError("set_palette(): can't convert object to list"); + } + } + + for(size_t i = 0; i < num_tuples; i++) { + mp_obj_t obj = tuples[i]; + if(!mp_obj_is_type(obj, &mp_type_tuple)) mp_raise_ValueError("set_palette(): can't convert object to tuple"); + + mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR2(obj, mp_obj_tuple_t); + + if(tuple->len != 3) mp_raise_ValueError("set_palette(): tuple must contain R, G, B values"); + + self->graphics->update_pen( + i, + mp_obj_get_int(tuple->items[0]), + mp_obj_get_int(tuple->items[1]), + mp_obj_get_int(tuple->items[2]) + ); + } + + return mp_const_none; +} + +mp_obj_t ModPicoGraphics_set_clip(size_t n_args, const mp_obj_t *args) { + enum { ARG_self, ARG_x, ARG_y, ARG_w, ARG_h }; + + ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self], ModPicoGraphics_obj_t); + + self->graphics->set_clip({ + mp_obj_get_int(args[ARG_x]), + mp_obj_get_int(args[ARG_y]), + mp_obj_get_int(args[ARG_w]), + mp_obj_get_int(args[ARG_h]) + }); + + return mp_const_none; +} + +mp_obj_t ModPicoGraphics_remove_clip(mp_obj_t self_in) { + ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t); + + self->graphics->remove_clip(); + + return mp_const_none; +} + +mp_obj_t ModPicoGraphics_clear(mp_obj_t self_in) { + ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t); + + self->graphics->clear(); + + return mp_const_none; +} + +mp_obj_t ModPicoGraphics_pixel(mp_obj_t self_in, mp_obj_t x, mp_obj_t y) { + ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t); + + self->graphics->pixel({ + mp_obj_get_int(x), + mp_obj_get_int(y) + }); + + return mp_const_none; +} + +mp_obj_t ModPicoGraphics_pixel_span(size_t n_args, const mp_obj_t *args) { + enum { ARG_self, ARG_x, ARG_y, ARG_l }; + + ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self], ModPicoGraphics_obj_t); + + self->graphics->pixel_span({ + mp_obj_get_int(args[ARG_x]), + mp_obj_get_int(args[ARG_y]) + }, mp_obj_get_int(args[ARG_l])); + + return mp_const_none; +} + +mp_obj_t ModPicoGraphics_rectangle(size_t n_args, const mp_obj_t *args) { + enum { ARG_self, ARG_x, ARG_y, ARG_w, ARG_h }; + + ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self], ModPicoGraphics_obj_t); + + self->graphics->rectangle({ + mp_obj_get_int(args[ARG_x]), + mp_obj_get_int(args[ARG_y]), + mp_obj_get_int(args[ARG_w]), + mp_obj_get_int(args[ARG_h]) + }); + + return mp_const_none; +} + +mp_obj_t ModPicoGraphics_circle(size_t n_args, const mp_obj_t *args) { + enum { ARG_self, ARG_x, ARG_y, ARG_r }; + + ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self], ModPicoGraphics_obj_t); + + self->graphics->circle({ + mp_obj_get_int(args[ARG_x]), + mp_obj_get_int(args[ARG_y]) + }, mp_obj_get_int(args[ARG_r])); + + return mp_const_none; +} + +mp_obj_t ModPicoGraphics_character(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_char, ARG_x, ARG_y, ARG_scale }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_char, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_scale, MP_ARG_INT, {.u_int = 2} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, ModPicoGraphics_obj_t); + + int c = mp_obj_get_int(args[ARG_char].u_obj); + int x = args[ARG_x].u_int; + int y = args[ARG_y].u_int; + int scale = args[ARG_scale].u_int; + + self->graphics->character((char)c, Point(x, y), scale); + + return mp_const_none; +} + +mp_obj_t ModPicoGraphics_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_text, ARG_x, ARG_y, ARG_wrap, ARG_scale, ARG_angle, ARG_spacing }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_text, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_wordwrap, MP_ARG_INT, {.u_int = __INT32_MAX__} }, // Sort-of a fudge, but wide-enough to avoid wrapping on any display size + { MP_QSTR_scale, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_angle, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_spacing, MP_ARG_INT, {.u_int = 1} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, ModPicoGraphics_obj_t); + + mp_obj_t text_obj = args[ARG_text].u_obj; + + if(!mp_obj_is_str_or_bytes(text_obj)) mp_raise_TypeError("text: string required"); + + GET_STR_DATA_LEN(text_obj, str, str_len); + + std::string t((const char*)str); + + int x = args[ARG_x].u_int; + int y = args[ARG_y].u_int; + int wrap = args[ARG_wrap].u_int; + float scale = args[ARG_scale].u_obj == mp_const_none ? 2.0f : mp_obj_get_float(args[ARG_scale].u_obj); + int angle = args[ARG_angle].u_int; + int letter_spacing = args[ARG_spacing].u_int; + + self->graphics->text(t, Point(x, y), wrap, scale, angle, letter_spacing); + + return mp_const_none; +} + +mp_obj_t ModPicoGraphics_measure_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_text, ARG_scale, ARG_spacing }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_text, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_scale, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_spacing, MP_ARG_INT, {.u_int = 1} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, ModPicoGraphics_obj_t); + + mp_obj_t text_obj = args[ARG_text].u_obj; + + if(!mp_obj_is_str_or_bytes(text_obj)) mp_raise_TypeError("text: string required"); + + GET_STR_DATA_LEN(text_obj, str, str_len); + + std::string t((const char*)str); + + float scale = args[ARG_scale].u_obj == mp_const_none ? 2.0f : mp_obj_get_float(args[ARG_scale].u_obj); + int letter_spacing = args[ARG_spacing].u_int; + + int width = self->graphics->measure_text(t, scale, letter_spacing); + + return mp_obj_new_int(width); +} + +mp_obj_t ModPicoGraphics_polygon(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + size_t num_tuples = n_args - 1; + const mp_obj_t *tuples = pos_args + 1; + + ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(pos_args[0], ModPicoGraphics_obj_t); + + // Check if there is only one argument, which might be a list + if(n_args == 2) { + if(mp_obj_is_type(pos_args[1], &mp_type_list)) { + mp_obj_list_t *points = MP_OBJ_TO_PTR2(pos_args[1], mp_obj_list_t); + + if(points->len <= 0) mp_raise_ValueError("poly(): cannot provide an empty list"); + + num_tuples = points->len; + tuples = points->items; + } + else { + mp_raise_TypeError("poly(): can't convert object to list"); + } + } + + if(num_tuples > 0) { + std::vector points; + for(size_t i = 0; i < num_tuples; i++) { + mp_obj_t obj = tuples[i]; + if(!mp_obj_is_type(obj, &mp_type_tuple)) mp_raise_ValueError("poly(): can't convert object to tuple"); + + mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR2(obj, mp_obj_tuple_t); + + if(tuple->len != 2) mp_raise_ValueError("poly(): tuple must only contain two numbers"); + + points.push_back({ + mp_obj_get_int(tuple->items[0]), + mp_obj_get_int(tuple->items[1]) + }); + } + self->graphics->polygon(points); + } + + return mp_const_none; +} + +mp_obj_t ModPicoGraphics_triangle(size_t n_args, const mp_obj_t *args) { + enum { ARG_self, ARG_x1, ARG_y1, ARG_x2, ARG_y2, ARG_x3, ARG_y3 }; + + ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self], ModPicoGraphics_obj_t); + + self->graphics->triangle( + {mp_obj_get_int(args[ARG_x1]), + mp_obj_get_int(args[ARG_y1])}, + {mp_obj_get_int(args[ARG_x2]), + mp_obj_get_int(args[ARG_y2])}, + {mp_obj_get_int(args[ARG_x3]), + mp_obj_get_int(args[ARG_y3])} + ); + + return mp_const_none; +} + +mp_obj_t ModPicoGraphics_line(size_t n_args, const mp_obj_t *args) { + enum { ARG_self, ARG_x1, ARG_y1, ARG_x2, ARG_y2 }; + + ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self], ModPicoGraphics_obj_t); + + self->graphics->line( + {mp_obj_get_int(args[ARG_x1]), + mp_obj_get_int(args[ARG_y1])}, + {mp_obj_get_int(args[ARG_x2]), + mp_obj_get_int(args[ARG_y2])} + ); + + return mp_const_none; +} +} diff --git a/micropython/modules/picographics/picographics.h b/micropython/modules/picographics/picographics.h new file mode 100644 index 00000000..e081ed83 --- /dev/null +++ b/micropython/modules/picographics/picographics.h @@ -0,0 +1,78 @@ +#include "py/runtime.h" +#include "py/objstr.h" + +enum PicoGraphicsDisplay { + DISPLAY_LCD_240X240=0, + DISPLAY_ROUND_LCD_240X240, + DISPLAY_PICO_DISPLAY, + DISPLAY_PICO_DISPLAY_2, + DISPLAY_PICO_EXPLORER, + DISPLAY_TUFTY_2040, + DISPLAY_ENVIRO_PLUS, + DISPLAY_LCD_160X80, + DISPLAY_I2C_OLED_128X128 +}; + +enum PicoGraphicsPenType { + PEN_1BIT = 0, + PEN_P2, + PEN_P4, + PEN_P8, + PEN_RGB332, + PEN_RGB565 +}; + +// Type +extern const mp_obj_type_t ModPicoGraphics_type; + +// Module functions +extern mp_obj_t ModPicoGraphics_module_RGB_to_RGB332(mp_obj_t r, mp_obj_t g, mp_obj_t b); +extern mp_obj_t ModPicoGraphics_module_RGB_to_RGB565(mp_obj_t r, mp_obj_t g, mp_obj_t b); +extern mp_obj_t ModPicoGraphics_module_RGB332_to_RGB(mp_obj_t rgb332); +extern mp_obj_t ModPicoGraphics_module_RGB565_to_RGB(mp_obj_t rgb565); +extern mp_obj_t ModPicoGraphics_get_required_buffer_size(mp_obj_t display_in, mp_obj_t pen_type_in); + +// Class methods +extern mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args); + +extern mp_obj_t ModPicoGraphics_update(mp_obj_t self_in); +extern mp_obj_t ModPicoGraphics_set_backlight(mp_obj_t self_in, mp_obj_t brightness); + +// Palette management +extern mp_obj_t ModPicoGraphics_update_pen(size_t n_args, const mp_obj_t *args); +extern mp_obj_t ModPicoGraphics_reset_pen(mp_obj_t self_in, mp_obj_t pen); +extern mp_obj_t ModPicoGraphics_set_palette(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); + +// Pen +extern mp_obj_t ModPicoGraphics_set_pen(mp_obj_t self_in, mp_obj_t pen); +extern mp_obj_t ModPicoGraphics_create_pen(size_t n_args, const mp_obj_t *args); + +// Primitives +extern mp_obj_t ModPicoGraphics_set_clip(size_t n_args, const mp_obj_t *args); +extern mp_obj_t ModPicoGraphics_remove_clip(mp_obj_t self_in); +extern mp_obj_t ModPicoGraphics_clear(mp_obj_t self_in); +extern mp_obj_t ModPicoGraphics_pixel(mp_obj_t self_in, mp_obj_t x, mp_obj_t y); +extern mp_obj_t ModPicoGraphics_pixel_span(size_t n_args, const mp_obj_t *args); +extern mp_obj_t ModPicoGraphics_rectangle(size_t n_args, const mp_obj_t *args); +extern mp_obj_t ModPicoGraphics_circle(size_t n_args, const mp_obj_t *args); +extern mp_obj_t ModPicoGraphics_character(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t ModPicoGraphics_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t ModPicoGraphics_measure_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t ModPicoGraphics_polygon(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t ModPicoGraphics_triangle(size_t n_args, const mp_obj_t *args); +extern mp_obj_t ModPicoGraphics_line(size_t n_args, const mp_obj_t *args); + +// Sprites +extern mp_obj_t ModPicoGraphics_set_spritesheet(mp_obj_t self_in, mp_obj_t spritedata); +extern mp_obj_t ModPicoGraphics_load_spritesheet(mp_obj_t self_in, mp_obj_t filename); +extern mp_obj_t ModPicoGraphics_sprite(size_t n_args, const mp_obj_t *args); + +// Utility +//extern mp_obj_t ModPicoGraphics_set_scanline_callback(mp_obj_t self_in, mp_obj_t cb_in); +extern mp_obj_t ModPicoGraphics_set_font(mp_obj_t self_in, mp_obj_t font); +extern mp_obj_t ModPicoGraphics_get_bounds(mp_obj_t self_in); +extern mp_obj_t ModPicoGraphics_set_framebuffer(mp_obj_t self_in, mp_obj_t framebuffer); + +extern mp_int_t ModPicoGraphics_get_framebuffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags); + +extern mp_obj_t ModPicoGraphics__del__(mp_obj_t self_in); \ No newline at end of file diff --git a/micropython/modules/picographics/spritesheet-to-rgb332.py b/micropython/modules/picographics/spritesheet-to-rgb332.py new file mode 100644 index 00000000..9eaff382 --- /dev/null +++ b/micropython/modules/picographics/spritesheet-to-rgb332.py @@ -0,0 +1,37 @@ +#!/bin/env python3 +from PIL import Image +import numpy +import sys +import pathlib + +# Run with `./filename.py source-image.jpg` +IMAGE_PATH = pathlib.Path(sys.argv[1]) +OUTPUT_PATH = IMAGE_PATH.with_suffix(".rgb332") + + +def image_to_data(image): + """Generator function to convert a PIL image to 16-bit 565 RGB bytes.""" + # NumPy is much faster at doing this. NumPy code provided by: + # Keith (https://www.blogger.com/profile/02555547344016007163) + pb = numpy.array(image.convert('RGBA')).astype('uint16') + + r = (pb[:, :, 0] & 0b11100000) >> 0 + g = (pb[:, :, 1] & 0b11100000) >> 3 + b = (pb[:, :, 2] & 0b11000000) >> 6 + a = pb[:, :, 3] # Discard + + # AAAA RRRR GGGG BBBB + color = r | g | b + return color.astype("uint8").flatten().tobytes() + + +img = Image.open(IMAGE_PATH) +w, h = img.size +data = image_to_data(img) + +print(f"Converted: {w}x{h} {len(data)} bytes") + +with open(OUTPUT_PATH, "wb") as f: + f.write(data) + +print(f"Written to: {OUTPUT_PATH}") \ No newline at end of file diff --git a/micropython/modules/pico_display_2/micropython.cmake b/micropython/modules/pimoroni_bus/micropython.cmake similarity index 60% rename from micropython/modules/pico_display_2/micropython.cmake rename to micropython/modules/pimoroni_bus/micropython.cmake index bb710909..c019b8b5 100644 --- a/micropython/modules/pico_display_2/micropython.cmake +++ b/micropython/modules/pimoroni_bus/micropython.cmake @@ -1,18 +1,16 @@ -set(MOD_NAME pico_display_2) +set(MOD_NAME pimoroni_bus) string(TOUPPER ${MOD_NAME} MOD_NAME_UPPER) add_library(usermod_${MOD_NAME} INTERFACE) target_sources(usermod_${MOD_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.c ${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.cpp - ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/${MOD_NAME}/${MOD_NAME}.cpp - ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/st7789/st7789.cpp - ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/pico_graphics.cpp - ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/types.cpp + ${CMAKE_CURRENT_LIST_DIR}/../../../common/pimoroni_bus.cpp ) target_include_directories(usermod_${MOD_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}/../../../common/ ) target_compile_definitions(usermod_${MOD_NAME} INTERFACE @@ -24,5 +22,5 @@ target_link_libraries(usermod INTERFACE usermod_${MOD_NAME}) set_source_files_properties( ${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.c PROPERTIES COMPILE_FLAGS - "-Wno-discarded-qualifiers -Wno-implicit-int" -) + "-Wno-discarded-qualifiers" +) \ No newline at end of file diff --git a/micropython/modules/pimoroni_bus/pimoroni_bus.c b/micropython/modules/pimoroni_bus/pimoroni_bus.c new file mode 100644 index 00000000..ac336e0b --- /dev/null +++ b/micropython/modules/pimoroni_bus/pimoroni_bus.c @@ -0,0 +1,36 @@ +#include "pimoroni_bus.h" + +MP_DEFINE_CONST_FUN_OBJ_1(SPISlot_obj, SPISlot); + +const mp_obj_type_t SPIPins_type = { + { &mp_type_type }, + .name = MP_QSTR_SPIBus, + .print = PimoroniBus_print, + .make_new = SPIPins_make_new +}; + +const mp_obj_type_t ParallelPins_type = { + { &mp_type_type }, + .name = MP_QSTR_ParallelBus, + .print = PimoroniBus_print, + .make_new = ParallelPins_make_new +}; + +STATIC const mp_map_elem_t pimoroni_bus_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_pimoroni_bus) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_SPIBus), (mp_obj_t)&SPIPins_type }, + { MP_OBJ_NEW_QSTR(MP_QSTR_ParallelBus), (mp_obj_t)&ParallelPins_type }, + { MP_ROM_QSTR(MP_QSTR_SPISlot), MP_ROM_PTR(&SPISlot_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(mp_module_pimoroni_bus_globals, pimoroni_bus_globals_table); + +const mp_obj_module_t pimoroni_bus_user_cmodule = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_pimoroni_bus_globals, +}; + +#if MICROPY_VERSION <= 70144 +MP_REGISTER_MODULE(MP_QSTR_pimoroni_bus, pimoroni_bus_user_cmodule, MODULE_PIMORONI_BUS_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_pimoroni_bus, pimoroni_bus_user_cmodule); +#endif \ No newline at end of file diff --git a/micropython/modules/pimoroni_bus/pimoroni_bus.cpp b/micropython/modules/pimoroni_bus/pimoroni_bus.cpp new file mode 100644 index 00000000..095de541 --- /dev/null +++ b/micropython/modules/pimoroni_bus/pimoroni_bus.cpp @@ -0,0 +1,123 @@ +#include "common/pimoroni_bus.hpp" +#include "micropython/modules/util.hpp" +#include + +using namespace pimoroni; + +extern "C" { +#include "pimoroni_bus.h" +#include "py/mperrno.h" + +void PimoroniBus_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + _PimoroniBus_obj_t *self = MP_OBJ_TO_PTR2(self_in, _PimoroniBus_obj_t); + if(self->base.type == &SPIPins_type) { + SPIPins *pins = (SPIPins *)self->pins; + mp_print_str(print, "SPIBus("); + mp_obj_print_helper(print, mp_obj_new_int((pins->spi == spi0) ? 0 : 1), PRINT_REPR); + mp_print_str(print, ", "); + mp_obj_print_helper(print, mp_obj_new_int(pins->cs), PRINT_REPR); + mp_print_str(print, ", "); + mp_obj_print_helper(print, mp_obj_new_int(pins->sck), PRINT_REPR); + mp_print_str(print, ", "); + mp_obj_print_helper(print, mp_obj_new_int(pins->mosi), PRINT_REPR); + mp_print_str(print, ", "); + mp_obj_print_helper(print, mp_obj_new_int(pins->miso == PIN_UNUSED ? -1 : pins->miso), PRINT_REPR); + mp_print_str(print, ", "); + mp_obj_print_helper(print, mp_obj_new_int(pins->dc == PIN_UNUSED ? -1 : pins->dc), PRINT_REPR); + mp_print_str(print, ", "); + mp_obj_print_helper(print, mp_obj_new_int(pins->bl == PIN_UNUSED ? -1 : pins->bl), PRINT_REPR); + mp_print_str(print, ")"); + } else { + ParallelPins *pins = (ParallelPins *)self->pins; + mp_print_str(print, "ParallelBus("); + mp_obj_print_helper(print, mp_obj_new_int(pins->cs), PRINT_REPR); + mp_print_str(print, ", "); + mp_obj_print_helper(print, mp_obj_new_int(pins->dc), PRINT_REPR); + mp_print_str(print, ", "); + mp_obj_print_helper(print, mp_obj_new_int(pins->wr_sck), PRINT_REPR); + mp_print_str(print, ", "); + mp_obj_print_helper(print, mp_obj_new_int(pins->rd_sck), PRINT_REPR); + mp_print_str(print, ", "); + mp_obj_print_helper(print, mp_obj_new_int(pins->d0), PRINT_REPR); + mp_print_str(print, ", "); + mp_obj_print_helper(print, mp_obj_new_int(pins->bl == PIN_UNUSED ? -1 : pins->bl), PRINT_REPR); + mp_print_str(print, ")"); + } + (void)kind; +} + +mp_obj_t SPIPins_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_cs, ARG_dc, ARG_sck, ARG_mosi, ARG_miso, ARG_bl }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_cs, MP_ARG_INT, {.u_int = SPI_BG_FRONT_CS} }, + { MP_QSTR_dc, MP_ARG_INT, {.u_int = SPI_DEFAULT_MISO} }, + { MP_QSTR_sck, MP_ARG_INT, {.u_int = SPI_DEFAULT_SCK} }, + { MP_QSTR_mosi, MP_ARG_INT, {.u_int = SPI_DEFAULT_MOSI} }, + { MP_QSTR_miso, MP_ARG_INT, {.u_int = PIN_UNUSED} }, + { MP_QSTR_bl, MP_ARG_INT, {.u_int = PIN_UNUSED} }, + }; + + // Parse args. + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + _PimoroniBus_obj_t *self = m_new_obj(_PimoroniBus_obj_t); + self->base.type = &SPIPins_type; + self->pins = new(m_new(SPIPins, 1)) SPIPins{ + ((args[ARG_sck].u_int >> 3) & 0b1) == 0 ? spi0 : spi1, + (uint)args[ARG_cs].u_int, + (uint)args[ARG_sck].u_int, + (uint)args[ARG_mosi].u_int, + (uint)args[ARG_miso].u_int, + (uint)args[ARG_dc].u_int, + (uint)args[ARG_bl].u_int + }; + + return MP_OBJ_FROM_PTR(self); +} + +// Factory function. +// Returns an "SPIBus" instance for the given SPI slot. +mp_obj_t SPISlot(mp_obj_t slot_in) { + int slot = mp_obj_get_int(slot_in); + + _PimoroniBus_obj_t *obj = m_new_obj(_PimoroniBus_obj_t); + obj->base.type = &SPIPins_type; + + SPIPins slot_pins = get_spi_pins((BG_SPI_SLOT)slot); + + obj->pins = new(m_new(SPIPins, 1)) SPIPins(slot_pins); + + return MP_OBJ_FROM_PTR(obj); +} + +mp_obj_t ParallelPins_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_cs, ARG_dc, ARG_wr_sck, ARG_rd_sck, ARG_d0, ARG_bl }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_cs, MP_ARG_INT, {.u_int = 10} }, + { MP_QSTR_dc, MP_ARG_INT, {.u_int = 11} }, + { MP_QSTR_wr_sck, MP_ARG_INT, {.u_int = 12} }, + { MP_QSTR_rd_sck, MP_ARG_INT, {.u_int = 13} }, + { MP_QSTR_d0, MP_ARG_INT, {.u_int = 14} }, + { MP_QSTR_bl, MP_ARG_INT, {.u_int = 2} }, + }; + + // Parse args. + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + _PimoroniBus_obj_t *self = m_new_obj(_PimoroniBus_obj_t); + self->base.type = &ParallelPins_type; + self->pins = new(m_new(ParallelPins, 1)) ParallelPins{ + (uint)args[ARG_cs].u_int, + (uint)args[ARG_dc].u_int, + (uint)args[ARG_wr_sck].u_int, + (uint)args[ARG_rd_sck].u_int, + (uint)args[ARG_d0].u_int, + args[ARG_bl].u_int == -1 ? PIN_UNUSED : (uint)args[ARG_bl].u_int + }; + + return MP_OBJ_FROM_PTR(self); +} + +} \ No newline at end of file diff --git a/micropython/modules/pimoroni_bus/pimoroni_bus.h b/micropython/modules/pimoroni_bus/pimoroni_bus.h new file mode 100644 index 00000000..3a7e4430 --- /dev/null +++ b/micropython/modules/pimoroni_bus/pimoroni_bus.h @@ -0,0 +1,16 @@ +#include "py/runtime.h" +#include "hardware/spi.h" + +extern const mp_obj_type_t SPIPins_type; +extern const mp_obj_type_t ParallelPins_type; + +typedef struct _PimoroniBus_obj_t { + mp_obj_base_t base; + void *pins; +} _PimoroniBus_obj_t; + +extern void PimoroniBus_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind); +extern mp_obj_t SPIPins_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args); +extern mp_obj_t ParallelPins_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args); + +extern mp_obj_t SPISlot(mp_obj_t self_in); \ No newline at end of file diff --git a/micropython/modules/pimoroni_i2c/pimoroni_i2c.c b/micropython/modules/pimoroni_i2c/pimoroni_i2c.c index 6070e51f..69550bc8 100644 --- a/micropython/modules/pimoroni_i2c/pimoroni_i2c.c +++ b/micropython/modules/pimoroni_i2c/pimoroni_i2c.c @@ -49,6 +49,10 @@ const mp_obj_module_t pimoroni_i2c_user_cmodule = { }; //////////////////////////////////////////////////////////////////////////////////////////////////// +#if MICROPY_VERSION <= 70144 MP_REGISTER_MODULE(MP_QSTR_pimoroni_i2c, pimoroni_i2c_user_cmodule, MODULE_PIMORONI_I2C_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_pimoroni_i2c, pimoroni_i2c_user_cmodule); +#endif //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/micropython/modules/plasma/plasma.c b/micropython/modules/plasma/plasma.c index 8d473061..80d94c2a 100644 --- a/micropython/modules/plasma/plasma.c +++ b/micropython/modules/plasma/plasma.c @@ -111,4 +111,8 @@ const mp_obj_module_t plasma_user_cmodule = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_plasma_globals, }; +#if MICROPY_VERSION <= 70144 MP_REGISTER_MODULE(MP_QSTR_plasma, plasma_user_cmodule, MODULE_PLASMA_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_plasma, plasma_user_cmodule); +#endif diff --git a/micropython/modules/qrcode b/micropython/modules/qrcode index b5afd3c0..db9477e1 160000 --- a/micropython/modules/qrcode +++ b/micropython/modules/qrcode @@ -1 +1 @@ -Subproject commit b5afd3c031c3f6ddd89e73833a1c8e551134e291 +Subproject commit db9477e1bf46b252bbd8ed8bc0f374c564d0ad00 diff --git a/micropython/modules/servo/servo.c b/micropython/modules/servo/servo.c index c3ee6d83..9e393f4a 100644 --- a/micropython/modules/servo/servo.c +++ b/micropython/modules/servo/servo.c @@ -256,4 +256,8 @@ const mp_obj_module_t servo_user_cmodule = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_servo_globals, }; +#if MICROPY_VERSION <= 70144 MP_REGISTER_MODULE(MP_QSTR_servo, servo_user_cmodule, MODULE_SERVO_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_servo, servo_user_cmodule); +#endif diff --git a/micropython/modules/st7789/micropython.cmake b/micropython/modules/st7789/micropython.cmake deleted file mode 100644 index 0da1fa86..00000000 --- a/micropython/modules/st7789/micropython.cmake +++ /dev/null @@ -1,22 +0,0 @@ -set(MOD_NAME st7789) -string(TOUPPER ${MOD_NAME} MOD_NAME_UPPER) -add_library(usermod_${MOD_NAME} INTERFACE) - -target_sources(usermod_${MOD_NAME} INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.c - ${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.cpp - ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/generic_st7789/generic_st7789.cpp - ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/st7789/st7789.cpp - ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/pico_graphics.cpp - ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/types.cpp -) - -target_include_directories(usermod_${MOD_NAME} INTERFACE - ${CMAKE_CURRENT_LIST_DIR} -) - -target_compile_definitions(usermod_${MOD_NAME} INTERFACE - -DMODULE_${MOD_NAME_UPPER}_ENABLED=1 -) - -target_link_libraries(usermod INTERFACE usermod_${MOD_NAME}) \ No newline at end of file diff --git a/micropython/modules/st7789/st7789.c b/micropython/modules/st7789/st7789.c deleted file mode 100644 index 783831f2..00000000 --- a/micropython/modules/st7789/st7789.c +++ /dev/null @@ -1,62 +0,0 @@ -#include "st7789.h" - -MP_DEFINE_CONST_FUN_OBJ_1(GenericST7789_update_obj, GenericST7789_update); -MP_DEFINE_CONST_FUN_OBJ_KW(GenericST7789_set_backlight_obj, 1, GenericST7789_set_backlight); -MP_DEFINE_CONST_FUN_OBJ_KW(GenericST7789_set_pen_obj, 1, GenericST7789_set_pen); -MP_DEFINE_CONST_FUN_OBJ_KW(GenericST7789_create_pen_obj, 1, GenericST7789_create_pen); -MP_DEFINE_CONST_FUN_OBJ_KW(GenericST7789_set_clip_obj, 1, GenericST7789_set_clip); -MP_DEFINE_CONST_FUN_OBJ_1(GenericST7789_remove_clip_obj, GenericST7789_remove_clip); -MP_DEFINE_CONST_FUN_OBJ_1(GenericST7789_clear_obj, GenericST7789_clear); -MP_DEFINE_CONST_FUN_OBJ_KW(GenericST7789_pixel_obj, 1, GenericST7789_pixel); -MP_DEFINE_CONST_FUN_OBJ_KW(GenericST7789_pixel_span_obj, 1, GenericST7789_pixel_span); -MP_DEFINE_CONST_FUN_OBJ_KW(GenericST7789_rectangle_obj, 1, GenericST7789_rectangle); -MP_DEFINE_CONST_FUN_OBJ_KW(GenericST7789_circle_obj, 1, GenericST7789_circle); -MP_DEFINE_CONST_FUN_OBJ_KW(GenericST7789_character_obj, 1, GenericST7789_character); -MP_DEFINE_CONST_FUN_OBJ_KW(GenericST7789_text_obj, 1, GenericST7789_text); -MP_DEFINE_CONST_FUN_OBJ_KW(GenericST7789_polygon_obj, 2, GenericST7789_polygon); -MP_DEFINE_CONST_FUN_OBJ_KW(GenericST7789_triangle_obj, 1, GenericST7789_triangle); -MP_DEFINE_CONST_FUN_OBJ_KW(GenericST7789_line_obj, 1, GenericST7789_line); - -STATIC const mp_rom_map_elem_t GenericST7789_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&GenericST7789_update_obj) }, - { MP_ROM_QSTR(MP_QSTR_set_backlight), MP_ROM_PTR(&GenericST7789_set_backlight_obj) }, - { MP_ROM_QSTR(MP_QSTR_set_pen), MP_ROM_PTR(&GenericST7789_set_pen_obj) }, - { MP_ROM_QSTR(MP_QSTR_create_pen), MP_ROM_PTR(&GenericST7789_create_pen_obj) }, - { MP_ROM_QSTR(MP_QSTR_set_clip), MP_ROM_PTR(&GenericST7789_set_clip_obj) }, - { MP_ROM_QSTR(MP_QSTR_remove_clip), MP_ROM_PTR(&GenericST7789_remove_clip_obj) }, - { MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&GenericST7789_clear_obj) }, - { MP_ROM_QSTR(MP_QSTR_pixel), MP_ROM_PTR(&GenericST7789_pixel_obj) }, - { MP_ROM_QSTR(MP_QSTR_pixel_span), MP_ROM_PTR(&GenericST7789_pixel_span_obj) }, - { MP_ROM_QSTR(MP_QSTR_rectangle), MP_ROM_PTR(&GenericST7789_rectangle_obj) }, - { MP_ROM_QSTR(MP_QSTR_circle), MP_ROM_PTR(&GenericST7789_circle_obj) }, - { MP_ROM_QSTR(MP_QSTR_character), MP_ROM_PTR(&GenericST7789_character_obj) }, - { MP_ROM_QSTR(MP_QSTR_text), MP_ROM_PTR(&GenericST7789_text_obj) }, - { MP_ROM_QSTR(MP_QSTR_polygon), MP_ROM_PTR(&GenericST7789_polygon_obj) }, - { MP_ROM_QSTR(MP_QSTR_triangle), MP_ROM_PTR(&GenericST7789_triangle_obj) }, - { MP_ROM_QSTR(MP_QSTR_line), MP_ROM_PTR(&GenericST7789_line_obj) }, -}; -STATIC MP_DEFINE_CONST_DICT(GenericST7789_locals_dict, GenericST7789_locals_dict_table); - -/***** Class Definition *****/ -const mp_obj_type_t GenericST7789_type = { - { &mp_type_type }, - .name = MP_QSTR_st7789, - .print = GenericST7789_print, - .make_new = GenericST7789_make_new, - .locals_dict = (mp_obj_dict_t*)&GenericST7789_locals_dict, -}; - -/***** Module Globals *****/ -STATIC const mp_map_elem_t st7789_globals_table[] = { - { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_st7789) }, - { MP_OBJ_NEW_QSTR(MP_QSTR_ST7789), (mp_obj_t)&GenericST7789_type }, -}; -STATIC MP_DEFINE_CONST_DICT(mp_module_st7789_globals, st7789_globals_table); - -/***** Module Definition *****/ -const mp_obj_module_t st7789_user_cmodule = { - .base = { &mp_type_module }, - .globals = (mp_obj_dict_t*)&mp_module_st7789_globals, -}; - -MP_REGISTER_MODULE(MP_QSTR_st7789, st7789_user_cmodule, MODULE_ST7789_ENABLED); \ No newline at end of file diff --git a/micropython/modules/st7789/st7789.cpp b/micropython/modules/st7789/st7789.cpp deleted file mode 100644 index 7d6f0a27..00000000 --- a/micropython/modules/st7789/st7789.cpp +++ /dev/null @@ -1,555 +0,0 @@ -#include "libraries/generic_st7789/generic_st7789.hpp" - -#include "micropython/modules/util.hpp" - - - -using namespace pimoroni; - -extern "C" { -#include "st7789.h" - -/***** Variables Struct *****/ -typedef struct _GenericST7789_obj_t { - mp_obj_base_t base; - ST7789Generic *st7789; - uint16_t *buffer; -} GenericST7789_obj_t; - -/***** Print *****/ -void GenericST7789_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { - (void)kind; //Unused input parameter - GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(self_in, GenericST7789_obj_t); - - mp_print_str(print, "ST7789("); - - mp_print_str(print, "spi = "); - mp_obj_print_helper(print, mp_obj_new_int((self->st7789->get_spi() == spi0) ? 0 : 1), PRINT_REPR); - - mp_print_str(print, ", cs = "); - mp_obj_print_helper(print, mp_obj_new_int(self->st7789->get_cs()), PRINT_REPR); - - mp_print_str(print, ", dc = "); - mp_obj_print_helper(print, mp_obj_new_int(self->st7789->get_dc()), PRINT_REPR); - - mp_print_str(print, ", sck = "); - mp_obj_print_helper(print, mp_obj_new_int(self->st7789->get_sck()), PRINT_REPR); - - mp_print_str(print, ", mosi = "); - mp_obj_print_helper(print, mp_obj_new_int(self->st7789->get_mosi()), PRINT_REPR); - - mp_print_str(print, ", bl = "); - mp_obj_print_helper(print, mp_obj_new_int(self->st7789->get_bl()), PRINT_REPR); - - mp_print_str(print, ")"); -} - -/***** Constructor *****/ -mp_obj_t GenericST7789_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - GenericST7789_obj_t *self = nullptr; - - enum { ARG_width, ARG_height, ARG_round, ARG_rotate180, ARG_slot, ARG_buffer, ARG_spi, ARG_cs, ARG_dc, ARG_sck, ARG_mosi, ARG_bl }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_width, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_height, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_round, MP_ARG_OBJ, {.u_obj = mp_const_false} }, - { MP_QSTR_rotate180, MP_ARG_OBJ, {.u_obj = mp_const_false} }, - { MP_QSTR_slot, MP_ARG_INT, {.u_int = -1} }, - { MP_QSTR_buffer, MP_ARG_OBJ, {.u_obj = mp_const_none} }, - { MP_QSTR_spi, MP_ARG_INT, {.u_int = -1} }, - { MP_QSTR_cs, MP_ARG_INT, {.u_int = pimoroni::SPI_BG_FRONT_CS} }, - { MP_QSTR_dc, MP_ARG_INT, {.u_int = pimoroni::SPI_DEFAULT_MISO} }, - { MP_QSTR_sck, MP_ARG_INT, {.u_int = pimoroni::SPI_DEFAULT_SCK} }, - { MP_QSTR_mosi, MP_ARG_INT, {.u_int = pimoroni::SPI_DEFAULT_MOSI} }, - { MP_QSTR_bl, MP_ARG_INT, {.u_int = pimoroni::SPI_BG_FRONT_PWM} }, - }; - - // Parse args. - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - self = m_new_obj(GenericST7789_obj_t); - self->base.type = &GenericST7789_type; - - bool rotate180 = args[ARG_rotate180].u_obj == mp_const_true; - bool round = args[ARG_round].u_obj == mp_const_true; - int width = args[ARG_width].u_int; - int height = args[ARG_height].u_int; - - if (args[ARG_buffer].u_obj != mp_const_none) { - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_RW); - self->buffer = (uint16_t *)bufinfo.buf; - if(bufinfo.len < (size_t)(width * height * 2)) { - mp_raise_ValueError("Supplied buffer is too small!"); - } - } else { - self->buffer = m_new(uint16_t, width * height); - } - - if(args[ARG_slot].u_int != -1) { - BG_SPI_SLOT slot = (BG_SPI_SLOT)args[ARG_slot].u_int; - self->st7789 = m_new_class(ST7789Generic, width, height, round, self->buffer, slot); - if (rotate180) { - self->st7789->configure_display(true); - } - } else { - // Get SPI bus. - int spi_id = args[ARG_spi].u_int; - int sck = args[ARG_sck].u_int; - int mosi = args[ARG_mosi].u_int; - int dc = args[ARG_dc].u_int; - int cs = args[ARG_cs].u_int; - int bl = args[ARG_bl].u_int; - - if(spi_id == -1) { - spi_id = (sck >> 3) & 0b1; // If no spi specified, choose the one for the given SCK pin - } - if(spi_id < 0 || spi_id > 1) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("SPI(%d) doesn't exist"), spi_id); - } - - if(!IS_VALID_SCK(spi_id, sck)) { - mp_raise_ValueError(MP_ERROR_TEXT("bad SCK pin")); - } - - if(!IS_VALID_MOSI(spi_id, mosi)) { - mp_raise_ValueError(MP_ERROR_TEXT("bad MOSI pin")); - } - spi_inst_t *spi = (spi_id == 0) ? spi0 : spi1; - self->st7789 = m_new_class(ST7789Generic, width, height, round, self->buffer, - spi, cs, dc, sck, mosi, bl); - if (rotate180) { - self->st7789->configure_display(true); - } - } - - return MP_OBJ_FROM_PTR(self); -} - -/***** Methods *****/ -mp_obj_t GenericST7789_update(mp_obj_t self_in) { - GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(self_in, GenericST7789_obj_t); - self->st7789->update(); - - return mp_const_none; -} - -mp_obj_t GenericST7789_set_backlight(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_brightness }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_brightness, MP_ARG_REQUIRED | MP_ARG_OBJ }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, GenericST7789_obj_t); - - float brightness = mp_obj_get_float(args[ARG_brightness].u_obj); - - if(brightness < 0 || brightness > 1.0f) - mp_raise_ValueError("brightness out of range. Expected 0.0 to 1.0"); - else - self->st7789->set_backlight((uint8_t)(brightness * 255.0f)); - - return mp_const_none; -} - -mp_obj_t GenericST7789_set_pen(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - - if(n_args <= 2) { - enum { ARG_self, ARG_pen }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_pen, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, GenericST7789_obj_t); - - int pen = args[ARG_pen].u_int; - - if(pen < 0 || pen > 0xffff) - mp_raise_ValueError("p is not a valid pen."); - else - self->st7789->set_pen(pen); - } - else { - enum { ARG_self, ARG_r, ARG_g, ARG_b }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_r, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_g, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_b, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, GenericST7789_obj_t); - - int r = args[ARG_r].u_int; - int g = args[ARG_g].u_int; - int b = args[ARG_b].u_int; - - if(r < 0 || r > 255) - mp_raise_ValueError("r out of range. Expected 0 to 255"); - else if(g < 0 || g > 255) - mp_raise_ValueError("g out of range. Expected 0 to 255"); - else if(b < 0 || b > 255) - mp_raise_ValueError("b out of range. Expected 0 to 255"); - else - self->st7789->set_pen(r, g, b); - } - - return mp_const_none; -} - -mp_obj_t GenericST7789_create_pen(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - int pen = 0; - - enum { ARG_self, ARG_r, ARG_g, ARG_b }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_r, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_g, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_b, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, GenericST7789_obj_t); - - int r = args[ARG_r].u_int; - int g = args[ARG_g].u_int; - int b = args[ARG_b].u_int; - - if(r < 0 || r > 255) - mp_raise_ValueError("r out of range. Expected 0 to 255"); - else if(g < 0 || g > 255) - mp_raise_ValueError("g out of range. Expected 0 to 255"); - else if(b < 0 || b > 255) - mp_raise_ValueError("b out of range. Expected 0 to 255"); - else - pen = self->st7789->create_pen(r, g, b); - - return mp_obj_new_int(pen); -} - -mp_obj_t GenericST7789_set_clip(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_x, ARG_y, ARG_w, ARG_h }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_w, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_h, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, GenericST7789_obj_t); - - int x = args[ARG_x].u_int; - int y = args[ARG_y].u_int; - int w = args[ARG_w].u_int; - int h = args[ARG_h].u_int; - - Rect r(x, y, w, h); - self->st7789->set_clip(r); - - return mp_const_none; -} - -mp_obj_t GenericST7789_remove_clip(mp_obj_t self_in) { - GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(self_in, GenericST7789_obj_t); - self->st7789->remove_clip(); - - return mp_const_none; -} - -mp_obj_t GenericST7789_clear(mp_obj_t self_in) { - GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(self_in, GenericST7789_obj_t); - self->st7789->clear(); - - return mp_const_none; -} - -mp_obj_t GenericST7789_pixel(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_x, ARG_y }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, GenericST7789_obj_t); - - int x = args[ARG_x].u_int; - int y = args[ARG_y].u_int; - - Point p(x, y); - self->st7789->pixel(p); - - return mp_const_none; -} - -mp_obj_t GenericST7789_pixel_span(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_x, ARG_y, ARG_l }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_l, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, GenericST7789_obj_t); - - int x = args[ARG_x].u_int; - int y = args[ARG_y].u_int; - int l = args[ARG_l].u_int; - - Point p(x, y); - self->st7789->pixel_span(p, l); - - return mp_const_none; -} - -mp_obj_t GenericST7789_rectangle(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_x, ARG_y, ARG_w, ARG_h }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_w, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_h, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, GenericST7789_obj_t); - - int x = args[ARG_x].u_int; - int y = args[ARG_y].u_int; - int w = args[ARG_w].u_int; - int h = args[ARG_h].u_int; - - Rect r(x, y, w, h); - self->st7789->rectangle(r); - - return mp_const_none; -} - -mp_obj_t GenericST7789_circle(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_x, ARG_y, ARG_r }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_r, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, GenericST7789_obj_t); - - int x = args[ARG_x].u_int; - int y = args[ARG_y].u_int; - int r = args[ARG_r].u_int; - - Point p(x, y); - self->st7789->circle(p, r); - - return mp_const_none; -} - -mp_obj_t GenericST7789_character(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_char, ARG_x, ARG_y, ARG_scale }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_char, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_scale, MP_ARG_INT, {.u_int = 2} }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, GenericST7789_obj_t); - - int c = mp_obj_get_int(args[ARG_char].u_obj); - int x = args[ARG_x].u_int; - int y = args[ARG_y].u_int; - int scale = args[ARG_scale].u_int; - - self->st7789->character((char)c, Point(x, y), scale); - - return mp_const_none; -} - -mp_obj_t GenericST7789_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_text, ARG_x, ARG_y, ARG_wrap, ARG_scale }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_text, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_wordwrap, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_scale, MP_ARG_INT, {.u_int = 2} }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, GenericST7789_obj_t); - - mp_obj_t text_obj = args[ARG_text].u_obj; - if(mp_obj_is_str_or_bytes(text_obj)) { - GET_STR_DATA_LEN(text_obj, str, str_len); - - std::string t((const char*)str); - - int x = args[ARG_x].u_int; - int y = args[ARG_y].u_int; - int wrap = args[ARG_wrap].u_int; - int scale = args[ARG_scale].u_int; - - self->st7789->text(t, Point(x, y), wrap, scale); - } - else if(mp_obj_is_float(text_obj)) { - mp_raise_TypeError("can't convert 'float' object to str implicitly"); - } - else if(mp_obj_is_int(text_obj)) { - mp_raise_TypeError("can't convert 'int' object to str implicitly"); - } - else if(mp_obj_is_bool(text_obj)) { - mp_raise_TypeError("can't convert 'bool' object to str implicitly"); - } - else { - mp_raise_TypeError("can't convert object to str implicitly"); - } - - return mp_const_none; -} - -mp_obj_t GenericST7789_polygon(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - size_t num_tuples = n_args - 1; - const mp_obj_t *tuples = pos_args + 1; - - GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(pos_args[0], GenericST7789_obj_t); - - // Check if there is only one argument, which might be a list - if(n_args == 2) { - if(mp_obj_is_type(pos_args[1], &mp_type_list)) { - mp_obj_list_t *points = MP_OBJ_TO_PTR2(pos_args[1], mp_obj_list_t); - if(points->len > 0) { - num_tuples = points->len; - tuples = points->items; - } - else { - mp_raise_ValueError("poly(): cannot provide an empty list"); - } - } - else { - mp_raise_TypeError("poly(): can't convert object to list"); - } - } - - if(num_tuples > 0) { - std::vector points; - for(size_t i = 0; i < num_tuples; i++) { - mp_obj_t obj = tuples[i]; - if(!mp_obj_is_type(obj, &mp_type_tuple)) { - mp_raise_ValueError("poly(): can't convert object to tuple"); - } - else { - mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR2(obj, mp_obj_tuple_t); - if(tuple->len != 2) { - mp_raise_ValueError("poly(): tuple must only contain two numbers"); - } - points.push_back(Point( - mp_obj_get_int(tuple->items[0]), - mp_obj_get_int(tuple->items[1])) - ); - } - } - self->st7789->polygon(points); - } - - return mp_const_none; -} - -mp_obj_t GenericST7789_triangle(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_x1, ARG_y1, ARG_x2, ARG_y2, ARG_x3, ARG_y3 }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_x2, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y2, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_x3, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y3, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, GenericST7789_obj_t); - - int x1 = args[ARG_x1].u_int; - int y1 = args[ARG_y1].u_int; - int x2 = args[ARG_x2].u_int; - int y2 = args[ARG_y2].u_int; - int x3 = args[ARG_x3].u_int; - int y3 = args[ARG_y3].u_int; - - Point p1(x1, y1); - Point p2(x2, y2); - Point p3(x3, y3); - self->st7789->triangle(p1, p2, p3); - - return mp_const_none; -} - -mp_obj_t GenericST7789_line(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_x1, ARG_y1, ARG_x2, ARG_y2 }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_x2, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y2, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, GenericST7789_obj_t); - - int x1 = args[ARG_x1].u_int; - int y1 = args[ARG_y1].u_int; - int x2 = args[ARG_x2].u_int; - int y2 = args[ARG_y2].u_int; - - Point p1(x1, y1); - Point p2(x2, y2); - self->st7789->line(p1, p2); - - return mp_const_none; -} -} diff --git a/micropython/modules/st7789/st7789.h b/micropython/modules/st7789/st7789.h deleted file mode 100644 index ecbdba10..00000000 --- a/micropython/modules/st7789/st7789.h +++ /dev/null @@ -1,27 +0,0 @@ -// Include MicroPython API. -#include "py/runtime.h" -#include "py/objstr.h" - -/***** Extern of Class Definition *****/ -extern const mp_obj_type_t GenericST7789_type; - -/***** Extern of Class Methods *****/ -extern void GenericST7789_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind); -extern mp_obj_t GenericST7789_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args); -extern mp_obj_t GenericST7789_update(mp_obj_t self_in); -extern mp_obj_t GenericST7789_set_backlight(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); - -extern mp_obj_t GenericST7789_set_pen(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t GenericST7789_create_pen(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t GenericST7789_set_clip(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t GenericST7789_remove_clip(mp_obj_t self_in); -extern mp_obj_t GenericST7789_clear(mp_obj_t self_in); -extern mp_obj_t GenericST7789_pixel(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t GenericST7789_pixel_span(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t GenericST7789_rectangle(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t GenericST7789_circle(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t GenericST7789_character(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t GenericST7789_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t GenericST7789_polygon(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t GenericST7789_triangle(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t GenericST7789_line(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); \ No newline at end of file diff --git a/micropython/modules/ulab b/micropython/modules/ulab index d4383449..00139bdd 160000 --- a/micropython/modules/ulab +++ b/micropython/modules/ulab @@ -1 +1 @@ -Subproject commit d438344943ab4383dae86b4620075ea877dec535 +Subproject commit 00139bdd9684bd9d10b0d89fa8fbb0906f2d05af diff --git a/micropython/modules_py/modules_py.cmake b/micropython/modules_py/modules_py.cmake index 3b18ff34..a3b0e7c8 100644 --- a/micropython/modules_py/modules_py.cmake +++ b/micropython/modules_py/modules_py.cmake @@ -16,5 +16,5 @@ add_library(usermod_modules_py INTERFACE) target_link_libraries(usermod INTERFACE usermod_modules_py) # .py files to copy from modules_py to ports/rp2/modules -copy_module(usermod_modules_py ${CMAKE_CURRENT_LIST_DIR}/picosystem.py picosystem) +#copy_module(usermod_modules_py ${CMAKE_CURRENT_LIST_DIR}/picosystem.py picosystem) copy_module(usermod_modules_py ${CMAKE_CURRENT_LIST_DIR}/pimoroni.py pimoroni) diff --git a/micropython/modules_py/pimoroni.py b/micropython/modules_py/pimoroni.py index a92919da..8c8504f8 100644 --- a/micropython/modules_py/pimoroni.py +++ b/micropython/modules_py/pimoroni.py @@ -136,6 +136,10 @@ class Button: else: return self.pin.value() + @property + def is_pressed(self): + return self.raw() + class RGBLED: def __init__(self, r, g, b, invert=True): @@ -178,3 +182,17 @@ class PID: self._last_value = value return (error * self.kp) + (self._error_sum * self.ki) - (rate_error * self.kd) + + +class Buzzer: + def __init__(self, pin): + self.pwm = PWM(Pin(pin)) + + def set_tone(self, freq, duty=0.5): + if freq < 50.0: # uh... https://github.com/micropython/micropython/blob/af64c2ddbd758ab6bac0fcca94c66d89046663be/ports/rp2/machine_pwm.c#L105-L119 + self.pwm.duty_u16(0) + return False + + self.pwm.freq(freq) + self.pwm.duty_u16(int(65535 * duty)) + return True