Merge pull request #400 from pimoroni/driver/uc8151-picographics

PicoGraphics support for UC8151-based displays
This commit is contained in:
Philip Howard 2022-06-24 11:18:58 +01:00 committed by GitHub
commit 892e2fa594
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 1074 additions and 149 deletions

View File

@ -28,6 +28,7 @@ add_subdirectory(icp10125)
add_subdirectory(scd4x)
add_subdirectory(hub75)
add_subdirectory(uc8151)
add_subdirectory(uc8151_legacy)
add_subdirectory(pwm)
add_subdirectory(servo)
add_subdirectory(encoder)

View File

@ -7,4 +7,4 @@ target_sources(${DRIVER_NAME} INTERFACE
target_include_directories(${DRIVER_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR})
# Pull in pico libraries that we need
target_link_libraries(${DRIVER_NAME} INTERFACE pico_stdlib hardware_spi)
target_link_libraries(${DRIVER_NAME} INTERFACE pico_stdlib hardware_spi pimoroni_bus pico_graphics)

View File

@ -49,6 +49,7 @@ namespace pimoroni {
};
bool UC8151::is_busy() {
if(BUSY == PIN_UNUSED) return false;
return !gpio_get(BUSY);
}
@ -59,6 +60,7 @@ namespace pimoroni {
}
void UC8151::reset() {
if(RESET == PIN_UNUSED) return;
gpio_put(RESET, 0); sleep_ms(10);
gpio_put(RESET, 1); sleep_ms(10);
busy_wait();
@ -323,17 +325,16 @@ namespace pimoroni {
void UC8151::setup(uint8_t speed) {
reset();
_update_speed = speed;
update_speed = speed;
if(speed == 0) {
command(PSR, {
RES_128x296 | LUT_OTP | FORMAT_BW | SHIFT_RIGHT | BOOSTER_ON | RESET_NONE
});
} else {
command(PSR, {
RES_128x296 | LUT_REG | FORMAT_BW | SHIFT_RIGHT | BOOSTER_ON | RESET_NONE
});
}
uint8_t psr_setting = RES_128x296 | FORMAT_BW | BOOSTER_ON | RESET_NONE;
psr_setting |= speed == 0 ? LUT_OTP : LUT_REG;
psr_setting |= rotation == ROTATE_180 ? SHIFT_LEFT | SCAN_UP : SHIFT_RIGHT | SCAN_DOWN;
command(PSR, 1, &psr_setting);
switch(speed) {
case 0:
// Note: the defult luts are built in so we don't really need to flash them here
@ -379,7 +380,7 @@ namespace pimoroni {
});
command(TCON, {0x22}); // tcon setting
command(CDI, {(uint8_t)(inverted ? 0b01'01'1100 : 0b01'00'1100)}); // vcom and data interval
command(CDI, {(uint8_t)(inverted ? 0b10'01'1100 : 0b01'00'1100)}); // vcom and data interval
command(PLL, {
HZ_100
@ -446,40 +447,16 @@ namespace pimoroni {
command(reg, values.size(), (uint8_t *)values.begin());
}
void UC8151::pixel(int x, int y, int v) {
// bounds check
if(x < 0 || y < 0 || x >= width || y >= height) return;
// pointer to byte in framebuffer that contains this pixel
uint8_t *p = &frame_buffer[(y / 8) + (x * (height / 8))];
uint8_t o = 7 - (y & 0b111); // bit offset within byte
uint8_t m = ~(1 << o); // bit mask for byte
uint8_t b = (v == 0 ? 0 : 1) << o; // bit value shifted to position
*p &= m; // clear bit
*p |= b; // set bit value
}
uint8_t* UC8151::get_frame_buffer() {
return frame_buffer;
}
void UC8151::invert(bool inv) {
inverted = inv;
command(CDI, {(uint8_t)(inverted ? 0b01'01'1100 : 0b01'00'1100)}); // vcom and data interval
}
void UC8151::update_speed(uint8_t speed) {
void UC8151::set_update_speed(uint8_t speed) {
setup(speed);
}
uint8_t UC8151::update_speed() {
return _update_speed;
uint8_t UC8151::get_update_speed() {
return update_speed;
}
uint32_t UC8151::update_time() {
switch(_update_speed) {
switch(update_speed) {
case 0:
return 4500;
case 1:
@ -493,28 +470,29 @@ namespace pimoroni {
}
}
void UC8151::partial_update(int x, int y, int w, int h, bool blocking) {
// y is given in columns ("banks"), which are groups of 8 horiontal pixels
// x is given in pixels
void UC8151::partial_update(PicoGraphics *graphics, Rect region) {
// region.y is given in columns ("banks"), which are groups of 8 horiontal pixels
// region.x is given in pixels
uint8_t *fb = (uint8_t *)graphics->frame_buffer;
if(blocking) {
busy_wait();
}
int cols = h / 8;
int y1 = y / 8;
//int y2 = y1 + cols;
int cols = region.h / 8;
int y1 = region.y / 8;
int rows = w;
int x1 = x;
//int x2 = x + rows;
int rows = region.w;
int x1 = region.x;
uint8_t partial_window[7] = {
(uint8_t)(y),
(uint8_t)(y + h - 1),
(uint8_t)(x >> 8),
(uint8_t)(x & 0xff),
(uint8_t)((x + w - 1) >> 8),
(uint8_t)((x + w - 1) & 0xff),
(uint8_t)(region.y),
(uint8_t)(region.y + region.h - 1),
(uint8_t)(region.x >> 8),
(uint8_t)(region.x & 0xff),
(uint8_t)((region.x + region.w - 1) >> 8),
(uint8_t)((region.x + region.w - 1) & 0xff),
0b00000001 // PT_SCAN
};
command(PON); // turn on
@ -526,7 +504,7 @@ namespace pimoroni {
for (auto dx = 0; dx < rows; dx++) {
int sx = dx + x1;
int sy = y1;
data(cols, &frame_buffer[sy + (sx * (height / 8))]);
data(cols, &fb[sy + (sx * (height / 8))]);
}
command(DSP); // data stop
@ -539,7 +517,9 @@ namespace pimoroni {
}
}
void UC8151::update(bool blocking) {
void UC8151::update(PicoGraphics *graphics) {
uint8_t *fb = (uint8_t *)graphics->frame_buffer;
if(blocking) {
busy_wait();
}
@ -548,7 +528,7 @@ namespace pimoroni {
command(PTOU); // disable partial mode
command(DTM2, (width * height) / 8, frame_buffer); // transmit framebuffer
command(DTM2, (width * height) / 8, fb); // transmit framebuffer
command(DSP); // data stop
command(DRF); // start display refresh

View File

@ -6,11 +6,13 @@
#include "hardware/spi.h"
#include "hardware/gpio.h"
#include "../../common/pimoroni_common.hpp"
#include "common/pimoroni_common.hpp"
#include "common/pimoroni_bus.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
namespace pimoroni {
class UC8151 {
class UC8151 : public DisplayDriver {
enum PSR_FLAGS {
RES_96x230 = 0b00000000,
RES_96x252 = 0b01000000,
@ -125,90 +127,66 @@ namespace pimoroni {
// Variables
//--------------------------------------------------
private:
// screen properties
uint16_t width;
uint16_t height;
// highest possible resolution is 160x296 which at 1 bit per pixel
// requires 5920 bytes of frame buffer
//uint8_t frame_buffer[5920] = {0};
uint8_t *frame_buffer;
spi_inst_t *spi = PIMORONI_SPI_DEFAULT_INSTANCE;
// interface pins with our standard defaults where appropriate
uint CS = SPI_BG_FRONT_CS;
uint DC = SPI_DEFAULT_MISO;
uint DC = 20;
uint SCK = SPI_DEFAULT_SCK;
uint MOSI = SPI_DEFAULT_MOSI;
uint BUSY = PIN_UNUSED;
uint RESET = PIN_UNUSED;
bool inverted = false;
uint8_t _update_speed = 0;
uint8_t update_speed = 0;
bool inverted = true; // Makes 0 black and 1 white, as is foretold.
bool blocking = true;
public:
UC8151(uint16_t width, uint16_t height) :
width(width), height(height), frame_buffer(new uint8_t[width * height / 8]) {
}
UC8151(uint16_t width, uint16_t height, uint8_t *frame_buffer) :
width(width), height(height), frame_buffer(frame_buffer) {
}
UC8151(uint16_t width, uint16_t height,
spi_inst_t *spi,
uint CS, uint DC, uint SCK, uint MOSI,
uint BUSY = PIN_UNUSED, uint RESET = PIN_UNUSED) :
width(width), height(height),
frame_buffer(new uint8_t[width * height / 8]),
spi(spi),
CS(CS), DC(DC), SCK(SCK), MOSI(MOSI), BUSY(BUSY), RESET(RESET) {}
UC8151(uint16_t width, uint16_t height,
uint8_t *frame_buffer,
spi_inst_t *spi,
uint CS, uint DC, uint SCK, uint MOSI,
uint BUSY = PIN_UNUSED, uint RESET = PIN_UNUSED) :
width(width), height(height),
frame_buffer(frame_buffer),
spi(spi),
CS(CS), DC(DC), SCK(SCK), MOSI(MOSI), BUSY(BUSY), RESET(RESET) {}
UC8151(uint16_t width, uint16_t height, Rotation rotate) : UC8151(width, height, rotate, {PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_FRONT_CS, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, PIN_UNUSED, 20, PIN_UNUSED}) {};
UC8151(uint16_t width, uint16_t height, Rotation rotate, SPIPins pins, uint busy=26, uint reset=21) :
DisplayDriver(width, height, rotate),
spi(pins.spi),
CS(pins.cs), DC(pins.dc), SCK(pins.sck), MOSI(pins.mosi), BUSY(busy), RESET(reset) {
init();
}
//--------------------------------------------------
// Methods
//--------------------------------------------------
public:
void init();
void busy_wait();
bool is_busy();
void reset();
void setup(uint8_t speed=0);
void power_off();
// DisplayDriver API
bool is_busy() override;
void update(PicoGraphics *graphics) override;
void partial_update(PicoGraphics *graphics, Rect region) override;
// UC8151 Specific
void default_luts();
void medium_luts();
void fast_luts();
void turbo_luts();
void set_update_speed(uint8_t speed);
uint8_t get_update_speed();
uint32_t update_time();
private:
void init();
void setup(uint8_t speed=0);
void read(uint8_t reg, size_t len, uint8_t *data);
void command(uint8_t reg, size_t len, const uint8_t *data);
void command(uint8_t reg, std::initializer_list<uint8_t> values);
void command(uint8_t reg) {command(reg, 0, nullptr);};
void data(size_t len, const uint8_t *data);
void invert(bool invert);
void update_speed(uint8_t speed);
uint8_t update_speed();
uint32_t update_time();
void update(bool blocking = true);
void partial_update(int x, int y, int w, int h, bool blocking = true);
void off();
void pixel(int x, int y, int v);
uint8_t* get_frame_buffer();
};
}

View File

@ -0,0 +1 @@
include(uc8151_legacy.cmake)

View File

@ -0,0 +1,10 @@
set(DRIVER_NAME uc8151_legacy)
add_library(${DRIVER_NAME} INTERFACE)
target_sources(${DRIVER_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}/${DRIVER_NAME}.cpp)
target_include_directories(${DRIVER_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR})
# Pull in pico libraries that we need
target_link_libraries(${DRIVER_NAME} INTERFACE pico_stdlib hardware_spi)

View File

@ -0,0 +1,568 @@
#include "uc8151_legacy.hpp"
#include <cstdlib>
#include <math.h>
namespace pimoroni {
enum reg {
PSR = 0x00,
PWR = 0x01,
POF = 0x02,
PFS = 0x03,
PON = 0x04,
PMES = 0x05,
BTST = 0x06,
DSLP = 0x07,
DTM1 = 0x10,
DSP = 0x11,
DRF = 0x12,
DTM2 = 0x13,
LUT_VCOM = 0x20,
LUT_WW = 0x21,
LUT_BW = 0x22,
LUT_WB = 0x23,
LUT_BB = 0x24,
PLL = 0x30,
TSC = 0x40,
TSE = 0x41,
TSR = 0x43,
TSW = 0x42,
CDI = 0x50,
LPD = 0x51,
TCON = 0x60,
TRES = 0x61,
REV = 0x70,
FLG = 0x71,
AMV = 0x80,
VV = 0x81,
VDCS = 0x82,
PTL = 0x90,
PTIN = 0x91,
PTOU = 0x92,
PGM = 0xa0,
APG = 0xa1,
ROTP = 0xa2,
CCSET = 0xe0,
PWS = 0xe3,
TSSET = 0xe5
};
bool UC8151_Legacy::is_busy() {
return !gpio_get(BUSY);
}
void UC8151_Legacy::busy_wait() {
while(is_busy()) {
tight_loop_contents();
}
}
void UC8151_Legacy::reset() {
gpio_put(RESET, 0); sleep_ms(10);
gpio_put(RESET, 1); sleep_ms(10);
busy_wait();
}
void UC8151_Legacy::default_luts() {
command(LUT_VCOM, {
0x00, 0x64, 0x64, 0x37, 0x00, 0x01,
0x00, 0x8c, 0x8c, 0x00, 0x00, 0x04,
0x00, 0x64, 0x64, 0x37, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00
});
command(LUT_WW, {
0x54, 0x64, 0x64, 0x37, 0x00, 0x01,
0x60, 0x8c, 0x8c, 0x00, 0x00, 0x04,
0xa8, 0x64, 0x64, 0x37, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
});
command(LUT_BW, {
0x54, 0x64, 0x64, 0x37, 0x00, 0x01,
0x60, 0x8c, 0x8c, 0x00, 0x00, 0x04,
0xa8, 0x64, 0x64, 0x37, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
});
command(LUT_WB, {
0xa8, 0x64, 0x64, 0x37, 0x00, 0x01,
0x60, 0x8c, 0x8c, 0x00, 0x00, 0x04,
0x54, 0x64, 0x64, 0x37, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
});
command(LUT_BB, {
0xa8, 0x64, 0x64, 0x37, 0x00, 0x01,
0x60, 0x8c, 0x8c, 0x00, 0x00, 0x04,
0x54, 0x64, 0x64, 0x37, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
});
busy_wait();
}
void UC8151_Legacy::medium_luts() {
command(LUT_VCOM, {
0x00, 0x16, 0x16, 0x0d, 0x00, 0x01,
0x00, 0x23, 0x23, 0x00, 0x00, 0x02,
0x00, 0x16, 0x16, 0x0d, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00
});
command(LUT_WW, {
0x54, 0x16, 0x16, 0x0d, 0x00, 0x01,
0x60, 0x23, 0x23, 0x00, 0x00, 0x02,
0xa8, 0x16, 0x16, 0x0d, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
});
command(LUT_BW, {
0x54, 0x16, 0x16, 0x0d, 0x00, 0x01,
0x60, 0x23, 0x23, 0x00, 0x00, 0x02,
0xa8, 0x16, 0x16, 0x0d, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
});
command(LUT_WB, {
0xa8, 0x16, 0x16, 0x0d, 0x00, 0x01,
0x60, 0x23, 0x23, 0x00, 0x00, 0x02,
0x54, 0x16, 0x16, 0x0d, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
});
command(LUT_BB, {
0xa8, 0x16, 0x16, 0x0d, 0x00, 0x01,
0x60, 0x23, 0x23, 0x00, 0x00, 0x02,
0x54, 0x16, 0x16, 0x0d, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
});
busy_wait();
}
void UC8151_Legacy::fast_luts() {
// 0x3c, 0x00, 0x2b, 0x2b, 0x24, 0x1a, ????
command(LUT_VCOM, {
0x00, 0x04, 0x04, 0x07, 0x00, 0x01,
0x00, 0x0c, 0x0c, 0x00, 0x00, 0x02,
0x00, 0x04, 0x04, 0x07, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00
});
command(LUT_WW, {
0x54, 0x04, 0x04, 0x07, 0x00, 0x01,
0x60, 0x0c, 0x0c, 0x00, 0x00, 0x02,
0xa8, 0x04, 0x04, 0x07, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
});
command(LUT_BW, {
0x54, 0x04, 0x04, 0x07, 0x00, 0x01,
0x60, 0x0c, 0x0c, 0x00, 0x00, 0x02,
0xa8, 0x04, 0x04, 0x07, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
});
command(LUT_WB, {
0xa8, 0x04, 0x04, 0x07, 0x00, 0x01,
0x60, 0x0c, 0x0c, 0x00, 0x00, 0x02,
0x54, 0x04, 0x04, 0x07, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
});
command(LUT_BB, {
0xa8, 0x04, 0x04, 0x07, 0x00, 0x01,
0x60, 0x0c, 0x0c, 0x00, 0x00, 0x02,
0x54, 0x04, 0x04, 0x07, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
});
command(PLL, {
HZ_200
});
busy_wait();
}
void UC8151_Legacy::turbo_luts() {
// 0x3c, 0x00, 0x2b, 0x2b, 0x24, 0x1a, ????
command(LUT_VCOM, {
0x00, 0x01, 0x01, 0x02, 0x00, 0x01,
0x00, 0x02, 0x02, 0x00, 0x00, 0x02,
0x00, 0x02, 0x02, 0x03, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00
});
command(LUT_WW, {
0x54, 0x01, 0x01, 0x02, 0x00, 0x01,
0x60, 0x02, 0x02, 0x00, 0x00, 0x02,
0xa8, 0x02, 0x02, 0x03, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
});
command(LUT_BW, {
0x54, 0x01, 0x01, 0x02, 0x00, 0x01,
0x60, 0x02, 0x02, 0x00, 0x00, 0x02,
0xa8, 0x02, 0x02, 0x03, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
});
command(LUT_WB, {
0xa8, 0x01, 0x01, 0x02, 0x00, 0x01,
0x60, 0x02, 0x02, 0x00, 0x00, 0x02,
0x54, 0x02, 0x02, 0x03, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
});
command(LUT_BB, {
0xa8, 0x01, 0x01, 0x02, 0x00, 0x01,
0x60, 0x02, 0x02, 0x00, 0x00, 0x02,
0x54, 0x02, 0x02, 0x03, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
});
command(PLL, {
HZ_200
});
busy_wait();
}
void UC8151_Legacy::init() {
// configure spi interface and pins
spi_init(spi, 12'000'000);
gpio_set_function(DC, GPIO_FUNC_SIO);
gpio_set_dir(DC, GPIO_OUT);
gpio_set_function(CS, GPIO_FUNC_SIO);
gpio_set_dir(CS, GPIO_OUT);
gpio_put(CS, 1);
gpio_set_function(RESET, GPIO_FUNC_SIO);
gpio_set_dir(RESET, GPIO_OUT);
gpio_put(RESET, 1);
gpio_set_function(BUSY, GPIO_FUNC_SIO);
gpio_set_dir(BUSY, GPIO_IN);
gpio_set_pulls(BUSY, true, false);
gpio_set_function(SCK, GPIO_FUNC_SPI);
gpio_set_function(MOSI, GPIO_FUNC_SPI);
setup();
};
void UC8151_Legacy::setup(uint8_t speed) {
reset();
_update_speed = speed;
if(speed == 0) {
command(PSR, {
RES_128x296 | LUT_OTP | FORMAT_BW | SHIFT_RIGHT | BOOSTER_ON | RESET_NONE
});
} else {
command(PSR, {
RES_128x296 | LUT_REG | FORMAT_BW | SHIFT_RIGHT | BOOSTER_ON | RESET_NONE
});
}
switch(speed) {
case 0:
// Note: the defult luts are built in so we don't really need to flash them here
// they are preserved above for posterity and reference mostly.
break;
case 1:
medium_luts();
break;
case 2:
fast_luts();
break;
case 3:
turbo_luts();
break;
default:
break;
}
command(PWR, {
VDS_INTERNAL | VDG_INTERNAL,
VCOM_VD | VGHL_16V,
0b101011,
0b101011,
0b101011
});
command(PON); // power on
busy_wait();
// booster soft start configuration
command(BTST, {
START_10MS | STRENGTH_3 | OFF_6_58US,
START_10MS | STRENGTH_3 | OFF_6_58US,
START_10MS | STRENGTH_3 | OFF_6_58US
});
command(PFS, {
FRAMES_1
});
command(TSE, {
TEMP_INTERNAL | OFFSET_0
});
command(TCON, {0x22}); // tcon setting
command(CDI, {(uint8_t)(inverted ? 0b01'01'1100 : 0b01'00'1100)}); // vcom and data interval
command(PLL, {
HZ_100
});
command(POF);
busy_wait();
}
void UC8151_Legacy::power_off() {
command(POF);
}
void UC8151_Legacy::read(uint8_t reg, size_t len, uint8_t *data) {
gpio_put(CS, 0);
gpio_put(DC, 0); // command mode
spi_write_blocking(spi, &reg, 1);
if(len > 0) {
gpio_put(DC, 1); // data mode
gpio_set_function(SCK, GPIO_FUNC_SIO);
gpio_set_dir(SCK, GPIO_OUT);
gpio_set_function(MOSI, GPIO_FUNC_SIO);
gpio_set_dir(MOSI, GPIO_IN);
for(auto i = 0u; i < len; i++) {
int byte = i / 8;
int bit = i % 8;
gpio_put(SCK, true);
bool value = gpio_get(MOSI);
data[byte] |= value << (7-bit);
gpio_put(SCK, false);
}
gpio_set_function(SCK, GPIO_FUNC_SPI);
gpio_set_function(MOSI, GPIO_FUNC_SPI);
}
gpio_put(CS, 1);
}
void UC8151_Legacy::command(uint8_t reg, size_t len, const uint8_t *data) {
gpio_put(CS, 0);
gpio_put(DC, 0); // command mode
spi_write_blocking(spi, &reg, 1);
if(len > 0) {
gpio_put(DC, 1); // data mode
spi_write_blocking(spi, (const uint8_t*)data, len);
}
gpio_put(CS, 1);
}
void UC8151_Legacy::data(size_t len, const uint8_t *data) {
gpio_put(CS, 0);
gpio_put(DC, 1); // data mode
spi_write_blocking(spi, (const uint8_t*)data, len);
gpio_put(CS, 1);
}
void UC8151_Legacy::command(uint8_t reg, std::initializer_list<uint8_t> values) {
command(reg, values.size(), (uint8_t *)values.begin());
}
void UC8151_Legacy::pixel(int x, int y, int v) {
// bounds check
if(x < 0 || y < 0 || x >= width || y >= height) return;
// pointer to byte in framebuffer that contains this pixel
uint8_t *p = &frame_buffer[(y / 8) + (x * (height / 8))];
uint8_t o = 7 - (y & 0b111); // bit offset within byte
uint8_t m = ~(1 << o); // bit mask for byte
uint8_t b = (v == 0 ? 0 : 1) << o; // bit value shifted to position
*p &= m; // clear bit
*p |= b; // set bit value
}
uint8_t* UC8151_Legacy::get_frame_buffer() {
return frame_buffer;
}
void UC8151_Legacy::invert(bool inv) {
inverted = inv;
command(CDI, {(uint8_t)(inverted ? 0b01'01'1100 : 0b01'00'1100)}); // vcom and data interval
}
void UC8151_Legacy::update_speed(uint8_t speed) {
setup(speed);
}
uint8_t UC8151_Legacy::update_speed() {
return _update_speed;
}
uint32_t UC8151_Legacy::update_time() {
switch(_update_speed) {
case 0:
return 4500;
case 1:
return 2000;
case 2:
return 800;
case 3:
return 250;
default:
return 4500;
}
}
void UC8151_Legacy::partial_update(int x, int y, int w, int h, bool blocking) {
// y is given in columns ("banks"), which are groups of 8 horiontal pixels
// x is given in pixels
if(blocking) {
busy_wait();
}
int cols = h / 8;
int y1 = y / 8;
//int y2 = y1 + cols;
int rows = w;
int x1 = x;
//int x2 = x + rows;
uint8_t partial_window[7] = {
(uint8_t)(y),
(uint8_t)(y + h - 1),
(uint8_t)(x >> 8),
(uint8_t)(x & 0xff),
(uint8_t)((x + w - 1) >> 8),
(uint8_t)((x + w - 1) & 0xff),
0b00000001 // PT_SCAN
};
command(PON); // turn on
command(PTIN); // enable partial mode
command(PTL, sizeof(partial_window), partial_window);
command(DTM2);
for (auto dx = 0; dx < rows; dx++) {
int sx = dx + x1;
int sy = y1;
data(cols, &frame_buffer[sy + (sx * (height / 8))]);
}
command(DSP); // data stop
command(DRF); // start display refresh
if(blocking) {
busy_wait();
command(POF); // turn off
}
}
void UC8151_Legacy::update(bool blocking) {
if(blocking) {
busy_wait();
}
command(PON); // turn on
command(PTOU); // disable partial mode
command(DTM2, (width * height) / 8, frame_buffer); // transmit framebuffer
command(DSP); // data stop
command(DRF); // start display refresh
if(blocking) {
busy_wait();
command(POF); // turn off
}
}
void UC8151_Legacy::off() {
busy_wait();
command(POF); // turn off
}
}

View File

@ -0,0 +1,214 @@
#pragma once
#include <initializer_list>
#include "pico/stdlib.h"
#include "hardware/spi.h"
#include "hardware/gpio.h"
#include "../../common/pimoroni_common.hpp"
namespace pimoroni {
class UC8151_Legacy {
enum PSR_FLAGS {
RES_96x230 = 0b00000000,
RES_96x252 = 0b01000000,
RES_128x296 = 0b10000000,
RES_160x296 = 0b11000000,
LUT_OTP = 0b00000000,
LUT_REG = 0b00100000,
FORMAT_BWR = 0b00000000,
FORMAT_BW = 0b00010000,
SCAN_DOWN = 0b00000000,
SCAN_UP = 0b00001000,
SHIFT_LEFT = 0b00000000,
SHIFT_RIGHT = 0b00000100,
BOOSTER_OFF = 0b00000000,
BOOSTER_ON = 0b00000010,
RESET_SOFT = 0b00000000,
RESET_NONE = 0b00000001
};
enum PWR_FLAGS_1 {
VDS_EXTERNAL = 0b00000000,
VDS_INTERNAL = 0b00000010,
VDG_EXTERNAL = 0b00000000,
VDG_INTERNAL = 0b00000001
};
enum PWR_FLAGS_2 {
VCOM_VD = 0b00000000,
VCOM_VG = 0b00000100,
VGHL_16V = 0b00000000,
VGHL_15V = 0b00000001,
VGHL_14V = 0b00000010,
VGHL_13V = 0b00000011
};
enum BOOSTER_FLAGS {
START_10MS = 0b00000000,
START_20MS = 0b01000000,
START_30MS = 0b10000000,
START_40MS = 0b11000000,
STRENGTH_1 = 0b00000000,
STRENGTH_2 = 0b00001000,
STRENGTH_3 = 0b00010000,
STRENGTH_4 = 0b00011000,
STRENGTH_5 = 0b00100000,
STRENGTH_6 = 0b00101000,
STRENGTH_7 = 0b00110000,
STRENGTH_8 = 0b00111000,
OFF_0_27US = 0b00000000,
OFF_0_34US = 0b00000001,
OFF_0_40US = 0b00000010,
OFF_0_54US = 0b00000011,
OFF_0_80US = 0b00000100,
OFF_1_54US = 0b00000101,
OFF_3_34US = 0b00000110,
OFF_6_58US = 0b00000111
};
enum PFS_FLAGS {
FRAMES_1 = 0b00000000,
FRAMES_2 = 0b00010000,
FRAMES_3 = 0b00100000,
FRAMES_4 = 0b00110000
};
enum TSE_FLAGS {
TEMP_INTERNAL = 0b00000000,
TEMP_EXTERNAL = 0b10000000,
OFFSET_0 = 0b00000000,
OFFSET_1 = 0b00000001,
OFFSET_2 = 0b00000010,
OFFSET_3 = 0b00000011,
OFFSET_4 = 0b00000100,
OFFSET_5 = 0b00000101,
OFFSET_6 = 0b00000110,
OFFSET_7 = 0b00000111,
OFFSET_MIN_8 = 0b00001000,
OFFSET_MIN_7 = 0b00001001,
OFFSET_MIN_6 = 0b00001010,
OFFSET_MIN_5 = 0b00001011,
OFFSET_MIN_4 = 0b00001100,
OFFSET_MIN_3 = 0b00001101,
OFFSET_MIN_2 = 0b00001110,
OFFSET_MIN_1 = 0b00001111
};
enum PLL_FLAGS {
// other frequency options exist but there doesn't seem to be much
// point in including them - this is a fair range of options...
HZ_29 = 0b00111111,
HZ_33 = 0b00111110,
HZ_40 = 0b00111101,
HZ_50 = 0b00111100,
HZ_67 = 0b00111011,
HZ_100 = 0b00111010,
HZ_200 = 0b00111001
};
//--------------------------------------------------
// Variables
//--------------------------------------------------
private:
// screen properties
uint16_t width;
uint16_t height;
// highest possible resolution is 160x296 which at 1 bit per pixel
// requires 5920 bytes of frame buffer
//uint8_t frame_buffer[5920] = {0};
uint8_t *frame_buffer;
spi_inst_t *spi = PIMORONI_SPI_DEFAULT_INSTANCE;
// interface pins with our standard defaults where appropriate
uint CS = SPI_BG_FRONT_CS;
uint DC = SPI_DEFAULT_MISO;
uint SCK = SPI_DEFAULT_SCK;
uint MOSI = SPI_DEFAULT_MOSI;
uint BUSY = PIN_UNUSED;
uint RESET = PIN_UNUSED;
bool inverted = false;
uint8_t _update_speed = 0;
public:
UC8151_Legacy(uint16_t width, uint16_t height) :
width(width), height(height), frame_buffer(new uint8_t[width * height / 8]) {
}
UC8151_Legacy(uint16_t width, uint16_t height, uint8_t *frame_buffer) :
width(width), height(height), frame_buffer(frame_buffer) {
}
UC8151_Legacy(uint16_t width, uint16_t height,
spi_inst_t *spi,
uint CS, uint DC, uint SCK, uint MOSI,
uint BUSY = PIN_UNUSED, uint RESET = PIN_UNUSED) :
width(width), height(height),
frame_buffer(new uint8_t[width * height / 8]),
spi(spi),
CS(CS), DC(DC), SCK(SCK), MOSI(MOSI), BUSY(BUSY), RESET(RESET) {}
UC8151_Legacy(uint16_t width, uint16_t height,
uint8_t *frame_buffer,
spi_inst_t *spi,
uint CS, uint DC, uint SCK, uint MOSI,
uint BUSY = PIN_UNUSED, uint RESET = PIN_UNUSED) :
width(width), height(height),
frame_buffer(frame_buffer),
spi(spi),
CS(CS), DC(DC), SCK(SCK), MOSI(MOSI), BUSY(BUSY), RESET(RESET) {}
//--------------------------------------------------
// Methods
//--------------------------------------------------
public:
void init();
void busy_wait();
bool is_busy();
void reset();
void setup(uint8_t speed=0);
void power_off();
void default_luts();
void medium_luts();
void fast_luts();
void turbo_luts();
void read(uint8_t reg, size_t len, uint8_t *data);
void command(uint8_t reg, size_t len, const uint8_t *data);
void command(uint8_t reg, std::initializer_list<uint8_t> values);
void command(uint8_t reg) {command(reg, 0, nullptr);};
void data(size_t len, const uint8_t *data);
void invert(bool invert);
void update_speed(uint8_t speed);
uint8_t update_speed();
uint32_t update_time();
void update(bool blocking = true);
void partial_update(int x, int y, int w, int h, bool blocking = true);
void off();
void pixel(int x, int y, int v);
uint8_t* get_frame_buffer();
};
}

View File

@ -43,6 +43,8 @@ add_subdirectory(pico_trackball_display)
add_subdirectory(pico_audio)
add_subdirectory(pico_wireless)
add_subdirectory(inky_pack)
add_subdirectory(plasma2040)
add_subdirectory(badger2040)
add_subdirectory(tufty2040)

View File

@ -0,0 +1,12 @@
set(OUTPUT_NAME inky_pack_demo)
add_executable(
${OUTPUT_NAME}
inky_pack_demo.cpp
)
# Pull in pico libraries that we need
target_link_libraries(${OUTPUT_NAME} pico_stdlib hardware_spi hardware_pwm hardware_dma rgbled button uc8151 pico_graphics)
# create map/bin/hex file etc.
pico_add_extra_outputs(${OUTPUT_NAME})

View File

@ -0,0 +1,57 @@
#include <string.h>
#include <math.h>
#include <vector>
#include <cstdlib>
#include "drivers/uc8151/uc8151.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
#include "button.hpp"
using namespace pimoroni;
enum Pin {
A = 12,
B = 13,
C = 14,
D = 15,
E = 11,
UP = 15, // alias for D
DOWN = 11, // alias for E
USER = 23,
CS = 17,
CLK = 18,
MOSI = 19,
DC = 20,
RESET = 21,
BUSY = 26,
VBUS_DETECT = 24,
LED = 25,
BATTERY = 29,
ENABLE_3V3 = 10
};
UC8151 uc8151(296, 128, ROTATE_0);
PicoGraphics_Pen1BitY graphics(uc8151.width, uc8151.height, nullptr);
Button button_a(Pin::A);
Button button_b(Pin::B);
Button button_c(Pin::C);
Button button_d(Pin::D);
Button button_e(Pin::E);
int main() {
graphics.set_pen(0);
graphics.clear();
graphics.set_pen(1);
graphics.set_font("bitmap8");
graphics.text("Hello World", {0, 0}, 296);
graphics.text("Has this worked?!", {0, 16}, 296);
uc8151.update(&graphics);
return 0;
}

View File

@ -8,4 +8,4 @@ target_sources(${LIB_NAME} INTERFACE
target_include_directories(${LIB_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR})
# Pull in pico libraries that we need
target_link_libraries(${LIB_NAME} INTERFACE bitmap_fonts hershey_fonts pico_stdlib hardware_pwm uc8151)
target_link_libraries(${LIB_NAME} INTERFACE bitmap_fonts hershey_fonts pico_stdlib hardware_pwm uc8151_legacy)

View File

@ -64,7 +64,7 @@ namespace pimoroni {
gpio_set_function(LED, GPIO_FUNC_PWM);
led(0);
uc8151.init();
uc8151_legacy.init();
// TODO: set default image?
}
@ -126,7 +126,7 @@ namespace pimoroni {
void Badger2040::clear() {
const uint32_t column_len = 128 / 8;
const uint32_t buf_len = column_len * 296;
uint8_t* buf = uc8151.get_frame_buffer();
uint8_t* buf = uc8151_legacy.get_frame_buffer();
if (_pen == 0) {
memset(buf, 0xff, buf_len);
@ -145,12 +145,12 @@ namespace pimoroni {
void Badger2040::pixel(int32_t x, int32_t y) {
if(_thickness == 1) {
uc8151.pixel(x, y, _dither_value(x, y, _pen));
uc8151_legacy.pixel(x, y, _dither_value(x, y, _pen));
}else{
uint8_t ht = _thickness / 2;
for(int sy = 0; sy < _thickness; sy++) {
for(int sx = 0; sx < _thickness; sx++) {
uc8151.pixel(x + sx - ht, y + sy - ht, _dither_value(x + sx - ht, y + sy - ht, _pen));
uc8151_legacy.pixel(x + sx - ht, y + sy - ht, _dither_value(x + sx - ht, y + sy - ht, _pen));
}
}
}
@ -163,7 +163,7 @@ namespace pimoroni {
// Display an image that fills the screen (296*128)
void Badger2040::image(const uint8_t* data) {
uint8_t* ptr = uc8151.get_frame_buffer();
uint8_t* ptr = uc8151_legacy.get_frame_buffer();
for (uint32_t x = 0; x < 296; ++x) {
// extract bitmask for this pixel
@ -205,7 +205,7 @@ namespace pimoroni {
uint32_t bm = 0b10000000 >> ((x + sx) & 0b111);
// draw the pixel
uc8151.pixel(dx + x, dy + y, data[o] & bm);
uc8151_legacy.pixel(dx + x, dy + y, data[o] & bm);
}
}
}
@ -234,7 +234,7 @@ namespace pimoroni {
if (h >= 8) {
// Directly write to the frame buffer when clearing a large area
uint8_t* buf = uc8151.get_frame_buffer();
uint8_t* buf = uc8151_legacy.get_frame_buffer();
for(int cx = x; cx < x + w; cx++) {
uint8_t* buf_ptr = &buf[cx * 16 + y / 8];
@ -253,7 +253,7 @@ namespace pimoroni {
else {
for(int cx = x; cx < x + w; cx++) {
for(int cy = y; cy < y + h; cy++) {
uc8151.pixel(cx, cy, _dither_value(cx, cy, _pen));
uc8151_legacy.pixel(cx, cy, _dither_value(cx, cy, _pen));
}
}
}
@ -283,11 +283,11 @@ namespace pimoroni {
}
void Badger2040::debug_command(uint8_t reg, size_t len, const uint8_t *data) {
uc8151.command(reg, len, data);
uc8151_legacy.command(reg, len, data);
}
void Badger2040::dump_otp(uint8_t *data) {
uc8151.read(0xa2, 0xFFF, data);
uc8151_legacy.read(0xa2, 0xFFF, data);
}
void Badger2040::update_button_states() {
@ -301,31 +301,31 @@ namespace pimoroni {
}
bool Badger2040::is_busy() {
return uc8151.is_busy();
return uc8151_legacy.is_busy();
}
void Badger2040::power_off() {
uc8151.power_off();
uc8151_legacy.power_off();
}
void Badger2040::invert(bool invert) {
uc8151.invert(invert);
uc8151_legacy.invert(invert);
}
void Badger2040::update_speed(uint8_t speed) {
uc8151.update_speed(speed);
uc8151_legacy.update_speed(speed);
}
uint32_t Badger2040::update_time() {
return uc8151.update_time();
return uc8151_legacy.update_time();
}
void Badger2040::partial_update(int x, int y, int w, int h, bool blocking) {
uc8151.partial_update(x, y, w, h, blocking);
uc8151_legacy.partial_update(x, y, w, h, blocking);
}
void Badger2040::update(bool blocking) {
uc8151.update(blocking);
uc8151_legacy.update(blocking);
}
const hershey::font_glyph_t* Badger2040::glyph_data(unsigned char c) {

View File

@ -2,7 +2,7 @@
#include <string>
#include "drivers/uc8151/uc8151.hpp"
#include "drivers/uc8151_legacy/uc8151_legacy.hpp"
#include "libraries/hershey_fonts/hershey_fonts.hpp"
#include "libraries/bitmap_fonts/bitmap_fonts.hpp"
@ -14,7 +14,7 @@ namespace pimoroni {
class Badger2040 {
protected:
UC8151 uc8151;
UC8151_Legacy uc8151_legacy;
const hershey::font_t *_font = &hershey::futural;
const bitmap::font_t *_bitmap_font = nullptr;
uint8_t _pen = 0;
@ -25,11 +25,11 @@ namespace pimoroni {
public:
Badger2040()
: uc8151(296, 128, spi0, CS, DC, CLK, MOSI, BUSY, RESET) {
: uc8151_legacy(296, 128, spi0, CS, DC, CLK, MOSI, BUSY, RESET) {
};
// Constructor for Python-managed buffer
Badger2040(uint8_t *framebuffer)
: uc8151(296, 128, framebuffer, spi0, CS, DC, CLK, MOSI, BUSY, RESET) {
: uc8151_legacy(296, 128, framebuffer, spi0, CS, DC, CLK, MOSI, BUSY, RESET) {
};
void init();
void update(bool blocking=false);

View File

@ -2,6 +2,7 @@ add_library(pico_graphics
${CMAKE_CURRENT_LIST_DIR}/types.cpp
${CMAKE_CURRENT_LIST_DIR}/pico_graphics.cpp
${CMAKE_CURRENT_LIST_DIR}/pico_graphics_pen_1bit.cpp
${CMAKE_CURRENT_LIST_DIR}/pico_graphics_pen_1bitY.cpp
${CMAKE_CURRENT_LIST_DIR}/pico_graphics_pen_p4.cpp
${CMAKE_CURRENT_LIST_DIR}/pico_graphics_pen_p8.cpp
${CMAKE_CURRENT_LIST_DIR}/pico_graphics_pen_rgb332.cpp

View File

@ -259,6 +259,21 @@ namespace pimoroni {
}
};
class PicoGraphics_Pen1BitY : public PicoGraphics {
public:
uint8_t color;
PicoGraphics_Pen1BitY(uint16_t width, uint16_t height, void *frame_buffer);
void set_pen(uint c) override;
void set_pen(uint8_t r, uint8_t g, uint8_t b) override;
void set_pixel(const Point &p) override;
void set_pixel_span(const Point &p, uint l) override;
static size_t buffer_size(uint w, uint h) {
return w * h / 8;
}
};
class PicoGraphics_PenP4 : public PicoGraphics {
public:
@ -368,7 +383,9 @@ namespace pimoroni {
: width(width), height(height), rotation(rotation) {};
virtual void update(PicoGraphics *display) {};
virtual void partial_update(PicoGraphics *display, Rect region) {};
virtual void set_backlight(uint8_t brightness) {};
virtual bool is_busy() {return false;};
virtual void cleanup() {};
};

View File

@ -0,0 +1,43 @@
#include "pico_graphics.hpp"
namespace pimoroni {
PicoGraphics_Pen1BitY::PicoGraphics_Pen1BitY(uint16_t width, uint16_t height, void *frame_buffer)
: PicoGraphics(width, height, frame_buffer) {
this->pen_type = PEN_1BIT;
if(this->frame_buffer == nullptr) {
this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]);
}
}
void PicoGraphics_Pen1BitY::set_pen(uint c) {
color = c != 0 ? 1 : 0;
}
void PicoGraphics_Pen1BitY::set_pen(uint8_t r, uint8_t g, uint8_t b) {
color = r != 0 || g != 0 || b != 0 ? 1 : 0;
}
void PicoGraphics_Pen1BitY::set_pixel(const Point &p) {
// pointer to byte in framebuffer that contains this pixel
uint8_t *buf = (uint8_t *)frame_buffer;
uint8_t *f = &buf[(p.y / 8) + (p.x * bounds.h / 8)];
uint bo = 7 - (p.y & 0b111);
// forceably clear the bit
*f &= ~(1U << bo);
// set pixel
*f |= (color << bo);
}
void PicoGraphics_Pen1BitY::set_pixel_span(const Point &p, uint l) {
Point po(p);
while(l--) {
set_pixel(po);
po.x++;
}
}
}

View File

@ -7,13 +7,13 @@ target_sources(usermod_${MOD_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.cpp
${CMAKE_CURRENT_LIST_DIR}/badgerinit.S
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/badger2040/badger2040.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../drivers/uc8151/uc8151.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../drivers/uc8151_legacy/uc8151_legacy.cpp
)
target_include_directories(usermod_${MOD_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/badger2040/
${CMAKE_CURRENT_LIST_DIR}/../../../drivers/uc8151/
${CMAKE_CURRENT_LIST_DIR}/../../../drivers/uc8151_legacy/
)
target_compile_definitions(usermod_${MOD_NAME} INTERFACE

View File

@ -47,6 +47,18 @@ MICROPY_EVENT_POLL_HOOK
current_graphics->pixel({pDraw->x + x, pDraw->y + y});
}
}
} else if(pDraw->iBpp == 1) {
uint8_t *pixels = (uint8_t *)pDraw->pPixels;
for(int y = 0; y < pDraw->iHeight; y++) {
for(int x = 0; x < pDraw->iWidth; x++) {
int i = y * pDraw->iWidth + x;
uint8_t p = pixels[i / 8];
p >>= 7 - (i & 0b111);
p &= 0x1;
current_graphics->set_pen(p);
current_graphics->pixel({pDraw->x + x, pDraw->y + y});
}
}
} else {
for(int y = 0; y < pDraw->iHeight; y++) {
for(int x = 0; x < pDraw->iWidth; x++) {
@ -107,9 +119,9 @@ static int _open(_JPEG_obj_t *self) {
case PicoGraphics::PEN_P2:
self->jpeg->setPixelType(TWO_BIT_DITHERED);
break;
case PicoGraphics::PEN_1BIT:
self->jpeg->setPixelType(ONE_BIT_DITHERED);
break;
case PicoGraphics::PEN_1BIT:
self->jpeg->setPixelType(ONE_BIT_DITHERED);
break;
}
}
return result;
@ -172,7 +184,7 @@ mp_obj_t _JPEG_decode(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args
current_graphics = self->graphics->graphics;
int result = -1;
if(self->graphics->graphics->pen_type == PicoGraphics::PEN_P4) {
if(self->graphics->graphics->pen_type == PicoGraphics::PEN_P4 || self->graphics->graphics->pen_type == PicoGraphics::PEN_1BIT) {
uint8_t *buf = new uint8_t[2048];
result = self->jpeg->decodeDither(x, y, buf, f);
delete[] buf;

View File

@ -8,8 +8,10 @@ target_sources(usermod_${MOD_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}/../../../drivers/st7789/st7789.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../drivers/st7735/st7735.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../drivers/sh1107/sh1107.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../drivers/uc8151/uc8151.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/pico_graphics.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/pico_graphics_pen_1bit.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/pico_graphics_pen_1bitY.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/pico_graphics_pen_p4.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/pico_graphics_pen_p8.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/pico_graphics_pen_rgb332.cpp

View File

@ -118,6 +118,7 @@ STATIC const mp_map_elem_t picographics_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_ENVIRO_PLUS), MP_ROM_INT(DISPLAY_ENVIRO_PLUS) },
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_LCD_160X80), MP_ROM_INT(DISPLAY_LCD_160X80) },
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_I2C_OLED_128X128), MP_ROM_INT(DISPLAY_I2C_OLED_128X128) },
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_INKY_PACK), MP_ROM_INT(DISPLAY_INKY_PACK) },
{ MP_ROM_QSTR(MP_QSTR_PEN_1BIT), MP_ROM_INT(PEN_1BIT) },
{ MP_ROM_QSTR(MP_QSTR_PEN_P4), MP_ROM_INT(PEN_P4) },

View File

@ -1,6 +1,7 @@
#include "drivers/st7789/st7789.hpp"
#include "drivers/st7735/st7735.hpp"
#include "drivers/sh1107/sh1107.hpp"
#include "drivers/uc8151/uc8151.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
#include "common/pimoroni_common.hpp"
#include "common/pimoroni_bus.hpp"
@ -35,13 +36,14 @@ typedef struct _ModPicoGraphics_obj_t {
//mp_obj_t scanline_callback; // Not really feasible in MicroPython
} ModPicoGraphics_obj_t;
bool get_display_settings(PicoGraphicsDisplay display, int &width, int &height, int &rotate) {
bool get_display_settings(PicoGraphicsDisplay display, int &width, int &height, int &rotate, int &pen_type) {
switch(display) {
case DISPLAY_PICO_DISPLAY:
width = 240;
height = 135;
// Portrait to match labelling
if(rotate == -1) rotate = (int)Rotation::ROTATE_270;
if(pen_type == -1) pen_type = PEN_RGB332;
break;
case DISPLAY_PICO_DISPLAY_2:
case DISPLAY_TUFTY_2040:
@ -49,25 +51,36 @@ bool get_display_settings(PicoGraphicsDisplay display, int &width, int &height,
height = 240;
// Tufty display is upside-down
if(rotate == -1) rotate = (int)Rotation::ROTATE_180;
if(pen_type == -1) pen_type = PEN_RGB332;
break;
case DISPLAY_PICO_EXPLORER:
case DISPLAY_LCD_240X240:
case DISPLAY_ENVIRO_PLUS:
width = 240;
height = 240;
if(pen_type == -1) pen_type = PEN_RGB332;
break;
case DISPLAY_ROUND_LCD_240X240:
width = 240;
height = 240;
if(pen_type == -1) pen_type = PEN_RGB332;
break;
case DISPLAY_LCD_160X80:
width = 160;
height = 80;
if(pen_type == -1) pen_type = PEN_RGB332;
break;
case DISPLAY_I2C_OLED_128X128:
width = 128;
height = 128;
if(rotate == -1) rotate = (int)Rotation::ROTATE_0;
if(pen_type == -1) pen_type = PEN_1BIT;
break;
case DISPLAY_INKY_PACK:
width = 296;
height = 128;
if(rotate == -1) rotate = (int)Rotation::ROTATE_0;
if(pen_type == -1) pen_type = PEN_1BIT;
break;
default:
return false;
@ -101,7 +114,7 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size
{ MP_QSTR_rotate, MP_ARG_INT, { .u_int = -1 } },
{ MP_QSTR_bus, MP_ARG_OBJ, { .u_obj = mp_const_none } },
{ MP_QSTR_buffer, MP_ARG_OBJ, { .u_obj = mp_const_none } },
{ MP_QSTR_pen_type, MP_ARG_INT, { .u_int = PEN_RGB332 } },
{ MP_QSTR_pen_type, MP_ARG_INT, { .u_int = -1 } },
};
// Parse args.
@ -111,15 +124,14 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size
self = m_new_obj_with_finaliser(ModPicoGraphics_obj_t);
self->base.type = &ModPicoGraphics_type;
PicoGraphicsPenType pen_type = (PicoGraphicsPenType)args[ARG_pen_type].u_int;
PicoGraphicsDisplay display = (PicoGraphicsDisplay)args[ARG_display].u_int;
bool round = display == DISPLAY_ROUND_LCD_240X240;
int width = 0;
int height = 0;
int pen_type = args[ARG_pen_type].u_int;
int rotate = args[ARG_rotate].u_int;
if(!get_display_settings(display, width, height, rotate)) mp_raise_ValueError("Unsupported display!");
if(!get_display_settings(display, width, height, rotate, pen_type)) mp_raise_ValueError("Unsupported display!");
if(rotate == -1) rotate = (int)Rotation::ROTATE_0;
// Try to create an appropriate display driver
@ -148,6 +160,15 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size
} else {
mp_raise_ValueError("I2C bus expected!");
}
} else if (display == DISPLAY_INKY_PACK) {
if (args[ARG_bus].u_obj == mp_const_none) {
self->display = m_new_class(UC8151, width, height, (Rotation)rotate);
} else if (mp_obj_is_type(args[ARG_bus].u_obj, &SPIPins_type)) {
_PimoroniBus_obj_t *bus = (_PimoroniBus_obj_t *)MP_OBJ_TO_PTR(args[ARG_bus].u_obj);
self->display = m_new_class(UC8151, width, height, (Rotation)rotate, *(SPIPins *)(bus->pins));
} else {
mp_raise_ValueError("SPIBus expected!");
}
} else {
if (args[ARG_bus].u_obj == mp_const_none) {
self->display = m_new_class(ST7789, width, height, (Rotation)rotate, round, get_spi_pins(BG_SPI_FRONT));
@ -176,9 +197,13 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size
// Create an instance of the graphics library
// use the *driver* width/height because they may have been swapped due to rotation
switch(pen_type) {
switch((PicoGraphicsPenType)pen_type) {
case PEN_1BIT:
self->graphics = m_new_class(PicoGraphics_Pen1Bit, self->display->width, self->display->height, self->buffer);
if (display == DISPLAY_INKY_PACK) {
self->graphics = m_new_class(PicoGraphics_Pen1BitY, self->display->width, self->display->height, self->buffer);
} else {
self->graphics = m_new_class(PicoGraphics_Pen1Bit, self->display->width, self->display->height, self->buffer);
}
break;
case PEN_P4:
self->graphics = m_new_class(PicoGraphics_PenP4, self->display->width, self->display->height, self->buffer);
@ -325,13 +350,13 @@ mp_obj_t ModPicoGraphics_set_framebuffer(mp_obj_t self_in, mp_obj_t framebuffer)
}
mp_obj_t ModPicoGraphics_get_required_buffer_size(mp_obj_t display_in, mp_obj_t pen_type_in) {
PicoGraphicsPenType pen_type = (PicoGraphicsPenType)mp_obj_get_int(pen_type_in);
PicoGraphicsDisplay display = (PicoGraphicsDisplay)mp_obj_get_int(display_in);
int width = 0;
int height = 0;
int rotation = 0;
if(!get_display_settings(display, width, height, rotation)) mp_raise_ValueError("Unsupported display!");
size_t required_size = get_required_buffer_size(pen_type, width, height);
int pen_type = mp_obj_get_int(pen_type_in);
if(!get_display_settings(display, width, height, rotation, pen_type)) mp_raise_ValueError("Unsupported display!");
size_t required_size = get_required_buffer_size((PicoGraphicsPenType)pen_type, width, height);
if(required_size == 0) mp_raise_ValueError("Unsupported pen type!");
return mp_obj_new_int(required_size);

View File

@ -10,7 +10,8 @@ enum PicoGraphicsDisplay {
DISPLAY_TUFTY_2040,
DISPLAY_ENVIRO_PLUS,
DISPLAY_LCD_160X80,
DISPLAY_I2C_OLED_128X128
DISPLAY_I2C_OLED_128X128,
DISPLAY_INKY_PACK
};
enum PicoGraphicsPenType {