Merge pull request #663 from pimoroni/feature/inky73

Support for Inky Frame 7.3" (using direct pen into PSRAM framebuffer)
This commit is contained in:
Philip Howard 2023-02-21 12:48:56 +00:00 committed by GitHub
commit 93ac854672
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 919 additions and 25 deletions

View File

@ -39,4 +39,7 @@ add_subdirectory(vl53l5cx)
add_subdirectory(pcf85063a)
add_subdirectory(pms5003)
add_subdirectory(sh1107)
add_subdirectory(st7567)
add_subdirectory(st7567)
add_subdirectory(psram_display)
add_subdirectory(inky73)
add_subdirectory(shiftregister)

View File

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

View File

@ -0,0 +1,10 @@
set(DRIVER_NAME inky73)
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 shiftregister)

196
drivers/inky73/inky73.cpp Normal file
View File

@ -0,0 +1,196 @@
#include "inky73.hpp"
#include <cstdlib>
#include <math.h>
#include <string.h>
namespace pimoroni {
enum reg {
PSR = 0x00,
PWR = 0x01,
POF = 0x02,
PFS = 0x03,
PON = 0x04,
BTST1 = 0x05,
BTST2 = 0x06,
DSLP = 0x07,
BTST3 = 0x08,
DTM1 = 0x10,
DSP = 0x11,
DRF = 0x12,
IPC = 0x13,
PLL = 0x30,
TSC = 0x40,
TSE = 0x41,
TSW = 0x42,
TSR = 0x43,
CDI = 0x50,
LPD = 0x51,
TCON = 0x60,
TRES = 0x61,
DAM = 0x65,
REV = 0x70,
FLG = 0x71,
AMV = 0x80,
VV = 0x81,
VDCS = 0x82,
T_VDCS = 0x84,
AGID = 0x86,
CMDH = 0xAA,
CCSET =0xE0,
PWS = 0xE3,
TSSET = 0xE6 // E5 or E6
};
bool Inky73::is_busy() {
return !(sr.read() & 128);
}
void Inky73::busy_wait() {
while(is_busy()) {
tight_loop_contents();
}
}
void Inky73::reset() {
gpio_put(RESET, 0); sleep_ms(10);
gpio_put(RESET, 1); sleep_ms(10);
busy_wait();
}
void Inky73::init() {
// configure spi interface and pins
spi_init(spi, 20'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(SCK, GPIO_FUNC_SPI);
gpio_set_function(MOSI, GPIO_FUNC_SPI);
};
void Inky73::setup() {
reset();
busy_wait();
command(CMDH, {0x49, 0x55, 0x20, 0x08, 0x09, 0x18});
command(PWR, {0x3F, 0x00, 0x32, 0x2A, 0x0E, 0x2A});
if (rotation == ROTATE_0) {
command(PSR, {0x53, 0x69});
} else {
command(PSR, {0x5F, 0x69});
}
//command(PSR, {0x5F, 0x69});
command(PFS, {0x00, 0x54, 0x00, 0x44});
command(BTST1, {0x40, 0x1F, 0x1F, 0x2C});
command(BTST2, {0x6F, 0x1F, 0x16, 0x25});
command(BTST3, {0x6F, 0x1F, 0x1F, 0x22});
command(IPC, {0x00, 0x04});
command(PLL, {0x02});
command(TSE, {0x00});
command(CDI, {0x3F});
command(TCON, {0x02, 0x00});
command(TRES, {0x03, 0x20, 0x01, 0xE0});
command(VDCS, {0x1E});
command(T_VDCS, {0x00});
command(AGID, {0x00});
command(PWS, {0x2F});
command(CCSET, {0x00});
command(TSSET, {0x00});
}
void Inky73::set_blocking(bool blocking) {
this->blocking = blocking;
}
void Inky73::power_off() {
busy_wait();
command(POF); // turn off
}
void Inky73::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 Inky73::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 Inky73::command(uint8_t reg, std::initializer_list<uint8_t> values) {
command(reg, values.size(), (uint8_t *)values.begin());
}
void Inky73::update(PicoGraphics *graphics) {
if(graphics->pen_type != PicoGraphics::PEN_INKY7) return; // Incompatible buffer
if(blocking) {
busy_wait();
}
setup();
gpio_put(CS, 0);
uint8_t reg = DTM1;
gpio_put(DC, 0); // command mode
spi_write_blocking(spi, &reg, 1);
gpio_put(DC, 1); // data mode
uint totalLength = 0;
gpio_put(CS, 1);
graphics->frame_convert(PicoGraphics::PEN_INKY7, [this, &totalLength](void *buf, size_t length) {
if (length > 0) {
gpio_put(CS, 0);
spi_write_blocking(spi, (const uint8_t*)buf, length);
totalLength += length;
gpio_put(CS, 1);
}
});
gpio_put(DC, 0); // data mode
gpio_put(CS, 1);
busy_wait();
command(PON, {0}); // turn on
busy_wait();
command(DRF, {0}); // start display refresh
busy_wait();
if(blocking) {
busy_wait();
command(POF); // turn off
}
}
bool Inky73::is_pressed(Button button) {
return sr.read() & button;
}
}

94
drivers/inky73/inky73.hpp Normal file
View File

@ -0,0 +1,94 @@
#pragma once
#include <initializer_list>
#include "pico/stdlib.h"
#include "hardware/spi.h"
#include "hardware/gpio.h"
#include "common/pimoroni_common.hpp"
#include "common/pimoroni_bus.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
#include "drivers/shiftregister/shiftregister.hpp"
namespace pimoroni {
class Inky73 : public DisplayDriver {
//--------------------------------------------------
// Variables
//--------------------------------------------------
private:
spi_inst_t *spi = PIMORONI_SPI_DEFAULT_INSTANCE;
// interface pins with our standard defaults where appropriate
uint CS = SPI_BG_FRONT_CS;
uint DC = 28; // 27;
uint SCK = SPI_DEFAULT_SCK;
uint MOSI = SPI_DEFAULT_MOSI;
uint RESET = 27; //25;
uint SR_CLOCK = 8;
uint SR_LATCH = 9;
uint SR_DATA = 10;
bool blocking = false;
ShiftRegister<uint8_t> sr = ShiftRegister<uint8_t>(SR_CLOCK, SR_LATCH, SR_DATA);
public:
enum Button : uint8_t {
BUTTON_A = 1,
BUTTON_B = 2,
BUTTON_C = 4,
BUTTON_D = 8,
BUTTON_E = 16
};
enum colour : uint8_t {
BLACK = 0,
WHITE = 1,
GREEN = 2,
BLUE = 3,
RED = 4,
YELLOW = 5,
ORANGE = 6,
CLEAN = 7
};
Inky73(uint16_t width, uint16_t height) : Inky73(width, height, ROTATE_0, {PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_FRONT_CS, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, PIN_UNUSED, 28, PIN_UNUSED}) {};
Inky73(uint16_t width, uint16_t height, SPIPins pins, uint reset=27) : Inky73(width, height, ROTATE_0, pins, reset) {};
Inky73(uint16_t width, uint16_t height, Rotation rotation, SPIPins pins, uint reset=27) :
DisplayDriver(width, height, rotation),
spi(pins.spi),
CS(pins.cs), DC(pins.dc), SCK(pins.sck), MOSI(pins.mosi), RESET(reset) {
init();
}
//--------------------------------------------------
// Methods
//--------------------------------------------------
public:
void busy_wait();
void reset();
void power_off();
bool is_busy() override;
void update(PicoGraphics *graphics) override;
void set_blocking(bool blocking);
bool is_pressed(Button button);
private:
void init();
void setup();
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, const uint8_t data) {command(reg, 0, &data);};
void command(uint8_t reg) {command(reg, 0, nullptr);};
void data(size_t len, const uint8_t *data);
};
}

View File

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

View File

@ -0,0 +1,10 @@
set(DRIVER_NAME psram_display)
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,75 @@
#include "psram_display.hpp"
#include <cstdlib>
#include <math.h>
#include <string.h>
namespace pimoroni {
enum reg {
WRITE = 0x02,
READ = 0x03,
RESET_ENABLE = 0x66,
RESET = 0x99
};
void PSRamDisplay::init() {
uint baud = spi_init(spi, 31'250'000);
printf("PSRam connected at %u\n", baud);
gpio_set_function(CS, GPIO_FUNC_SIO);
gpio_set_dir(CS, GPIO_OUT);
gpio_put(CS, 1);
gpio_set_function(SCK, GPIO_FUNC_SPI);
gpio_set_function(MOSI, GPIO_FUNC_SPI);
gpio_set_function(MISO, GPIO_FUNC_SPI);
gpio_put(CS, 0);
uint8_t command_buffer[2] = {RESET_ENABLE, RESET};
spi_write_blocking(spi, command_buffer, 2);
gpio_put(CS, 1);
}
void PSRamDisplay::write(uint32_t address, size_t len, const uint8_t *data)
{
gpio_put(CS, 0);
uint8_t command_buffer[4] = {WRITE, (uint8_t)((address >> 16) & 0xFF), (uint8_t)((address >> 8) & 0xFF), (uint8_t)(address & 0xFF)};
spi_write_blocking(spi, command_buffer, 4);
spi_write_blocking(spi, data, len);
gpio_put(CS, 1);
}
void PSRamDisplay::write(uint32_t address, size_t len, const uint8_t byte)
{
gpio_put(CS, 0);
uint8_t command_buffer[4] = {WRITE, (uint8_t)((address >> 16) & 0xFF), (uint8_t)((address >> 8) & 0xFF), (uint8_t)(address & 0xFF)};
spi_write_blocking(spi, command_buffer, 4);
SpiSetBlocking(byte, len);
gpio_put(CS, 1);
}
void PSRamDisplay::read(uint32_t address, size_t len, uint8_t *data)
{
gpio_put(CS, 0);
uint8_t command_buffer[4] = {READ, (uint8_t)((address >> 16) & 0xFF), (uint8_t)((address >> 8) & 0xFF), (uint8_t)(address & 0xFF)};
spi_write_blocking(spi, command_buffer, 4);
spi_read_blocking(spi, 0, data, len);
gpio_put(CS, 1);
}
void PSRamDisplay::write_pixel(const Point &p, uint8_t colour)
{
write(pointToAddress(p), 1, colour);
}
void PSRamDisplay::write_pixel_span(const Point &p, uint l, uint8_t colour)
{
write(pointToAddress(p), l, colour);
}
void PSRamDisplay::read_pixel_span(const Point &p, uint l, uint8_t *data)
{
read(pointToAddress(p), l, data);
}
}

View File

@ -0,0 +1,128 @@
#pragma once
#include <cstring>
#include "pico/stdlib.h"
#include "hardware/spi.h"
#include "hardware/gpio.h"
#include "common/pimoroni_common.hpp"
#include "common/pimoroni_bus.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
// 8 MB PSRam
namespace pimoroni {
class PSRamDisplay : public IDirectDisplayDriver<uint8_t> {
//--------------------------------------------------
// Variables
//--------------------------------------------------
private:
spi_inst_t *spi = PIMORONI_SPI_DEFAULT_INSTANCE;
// interface pins with our standard defaults where appropriate
uint CS = 3;
uint DC = PIN_UNUSED;
uint SCK = SPI_DEFAULT_SCK;
uint MOSI = SPI_DEFAULT_MOSI;
uint MISO = SPI_DEFAULT_MISO;
uint32_t start_address = 0;
uint16_t width = 0;
uint16_t height = 0;
absolute_time_t timeout;
bool blocking = false;
public:
enum colour : uint8_t {
BLACK = 0,
WHITE = 1,
GREEN = 2,
BLUE = 3,
RED = 4,
YELLOW = 5,
ORANGE = 6,
CLEAN = 7
};
PSRamDisplay(uint16_t width, uint16_t height) : PSRamDisplay(width, height, {PIMORONI_SPI_DEFAULT_INSTANCE, 3, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, PIN_UNUSED, PIN_UNUSED, PIN_UNUSED}) {};
PSRamDisplay(uint16_t width, uint16_t height, SPIPins pins) :
spi(pins.spi),
CS(pins.cs), DC(pins.dc), SCK(pins.sck), MOSI(pins.mosi),
width(width), height(height) {
init();
}
//--------------------------------------------------
// Methods
//--------------------------------------------------
public:
void test(void){
char writeBuffer[1024];
char readBuffer[1024];
uint mb = 8;
for(uint k = 0; k < 1024*mb; k++)
{
sprintf(writeBuffer, "%u", k);
write(k*1024, strlen(writeBuffer)+1, (uint8_t *)writeBuffer);
}
bool bSame = true;
for(uint k = 0; k < 1024*mb && bSame; k++)
{
sprintf(writeBuffer, "%u", k);
read(k*1024, strlen(writeBuffer)+1, (uint8_t *)readBuffer);
bSame = strcmp(writeBuffer, readBuffer) ==0;
printf("[%u] %s == %s ? %s\n", k, writeBuffer, readBuffer, bSame ? "Success" : "Failure");
}
}
void write_pixel(const Point &p, uint8_t colour) override;
void write_pixel_span(const Point &p, uint l, uint8_t colour) override;
void read_pixel_span(const Point &p, uint l, uint8_t *data) override;
int __not_in_flash_func(SpiSetBlocking)(const uint16_t uSrc, size_t uLen)
{
invalid_params_if(SPI, 0 > (int)uLen);
// Deliberately overflow FIFO, then clean up afterward, to minimise amount
// of APB polling required per halfword
for (size_t i = 0; i < uLen; ++i) {
while (!spi_is_writable(spi))
tight_loop_contents();
spi_get_hw(spi)->dr = uSrc;
}
while (spi_is_readable(spi))
(void)spi_get_hw(spi)->dr;
while (spi_get_hw(spi)->sr & SPI_SSPSR_BSY_BITS)
tight_loop_contents();
while (spi_is_readable(spi))
(void)spi_get_hw(spi)->dr;
// Don't leave overrun flag set
spi_get_hw(spi)->icr = SPI_SSPICR_RORIC_BITS;
return (int)uLen;
}
private:
void init();
void write(uint32_t address, size_t len, const uint8_t *data);
void write(uint32_t address, size_t len, const uint8_t byte);
void read(uint32_t address, size_t len, uint8_t *data);
uint32_t pointToAddress(const Point &p)
{
return start_address + (p.y * width) + p.x;
}
};
}

View File

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

View File

@ -0,0 +1,10 @@
set(DRIVER_NAME shiftregister)
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)

View File

@ -0,0 +1,5 @@
#include "shiftregister.hpp"
namespace pimoroni {
}

View File

@ -0,0 +1,48 @@
#pragma once
#include "pico/stdlib.h"
#include "hardware/spi.h"
#include "hardware/gpio.h"
namespace pimoroni {
template<typename T> class ShiftRegister {
private:
uint CLOCK = 0;
uint LATCH = 0;
uint DATA = 0;
public:
ShiftRegister(uint clock, uint latch, uint data) :
CLOCK(clock),
LATCH(latch),
DATA(data) {
gpio_init(CLOCK);
gpio_set_function(CLOCK, GPIO_FUNC_SIO);
gpio_set_dir(CLOCK, GPIO_OUT);
gpio_init(LATCH);
gpio_set_function(LATCH, GPIO_FUNC_SIO);
gpio_set_dir(LATCH, GPIO_OUT);
gpio_init(DATA);
gpio_set_function(DATA, GPIO_FUNC_SIO);
gpio_set_dir(DATA, GPIO_IN);
}
T read() {
gpio_put(LATCH, 0);
__asm("NOP;");
gpio_put(LATCH, 1);
__asm("NOP;");
T out = 0;
for (auto i = 0u; i < sizeof(T) * 8; i++) {
out <<= 1;
out |= gpio_get(DATA);
gpio_put(CLOCK, 1);
__asm("NOP;");
gpio_put(CLOCK, 0);
__asm("NOP;");
}
return out;
}
};
}

View File

@ -1,3 +1,4 @@
include(inky_frame_jpeg_image.cmake)
include(inky_frame_sleepy_head.cmake)
include(inky_frame_day_planner.cmake)
include(inky_frame_day_planner.cmake)
include(inky_frame7_test.cmake)

View File

@ -0,0 +1,14 @@
set(OUTPUT_NAME inky_frame7_test)
add_executable(
${OUTPUT_NAME}
inky_frame7_test.cpp
)
# Pull in pico libraries that we need
target_link_libraries(${OUTPUT_NAME} pico_stdlib psram_display inky73 inky_frame hardware_pwm hardware_spi hardware_i2c hardware_rtc fatfs sdcard pico_graphics)
pico_enable_stdio_usb(${OUTPUT_NAME} 0)
# create map/bin/hex file etc.
pico_add_extra_outputs(${OUTPUT_NAME})

View File

@ -0,0 +1,49 @@
#include <cstdio>
#include <math.h>
#include <stdio.h>
#include "drivers/psram_display/psram_display.hpp"
#include "drivers/inky73/inky73.hpp"
using namespace pimoroni;
uint LED_PIN = 8;
int main() {
stdio_init_all();
gpio_init(LED_PIN);
gpio_set_function(LED_PIN, GPIO_FUNC_SIO);
gpio_set_dir(LED_PIN, GPIO_OUT);
PSRamDisplay ramDisplay(800, 480);
PicoGraphics_PenInky7 graphics(800, 480, ramDisplay);
Inky73 inky7(800,400);
while (true) {
while(!inky7.is_pressed(Inky73::BUTTON_A)) {
sleep_ms(10);
}
graphics.set_pen(1);
graphics.clear();
for(int i =0 ; i < 100 ; i++)
{
uint size = 25 + (rand() % 50);
uint x = rand() % graphics.bounds.w;
uint y = rand() % graphics.bounds.h;
graphics.set_pen(0);
graphics.circle(Point(x, y), size);
graphics.set_pen(2+(i%5));
graphics.circle(Point(x, y), size-2);
}
gpio_put(LED_PIN, 1);
inky7.update(&graphics);
gpio_put(LED_PIN, 0);
}
return 0;
}

View File

@ -9,6 +9,7 @@ add_library(pico_graphics
${CMAKE_CURRENT_LIST_DIR}/pico_graphics_pen_rgb332.cpp
${CMAKE_CURRENT_LIST_DIR}/pico_graphics_pen_rgb565.cpp
${CMAKE_CURRENT_LIST_DIR}/pico_graphics_pen_rgb888.cpp
${CMAKE_CURRENT_LIST_DIR}/pico_graphics_pen_inky7.cpp
)
target_include_directories(pico_graphics INTERFACE ${CMAKE_CURRENT_LIST_DIR})

View File

@ -168,6 +168,7 @@ namespace pimoroni {
PEN_RGB332,
PEN_RGB565,
PEN_RGB888,
PEN_INKY7
};
void *frame_buffer;
@ -486,4 +487,62 @@ namespace pimoroni {
virtual void cleanup() {};
};
template<typename T> class IDirectDisplayDriver {
public:
virtual void write_pixel(const Point &p, T colour) = 0;
virtual void write_pixel_span(const Point &p, uint l, T colour) = 0;
virtual void read_pixel(const Point &p, T &data) {};
virtual void read_pixel_span(const Point &p, uint l, T *data) {};
};
class PicoGraphics_PenInky7 : public PicoGraphics {
public:
static const uint16_t palette_size = 8;
RGB palette[8] = {
/*
{0x2b, 0x2a, 0x37},
{0xdc, 0xcb, 0xba},
{0x35, 0x56, 0x33},
{0x33, 0x31, 0x47},
{0x9c, 0x3b, 0x2e},
{0xd3, 0xa9, 0x34},
{0xab, 0x58, 0x37},
{0xb2, 0x8e, 0x67}
*/
{ 0, 0, 0}, // black
{255, 255, 255}, // white
{ 0, 255, 0}, // green
{ 0, 0, 255}, // blue
{255, 0, 0}, // red
{255, 255, 0}, // yellow
{255, 128, 0}, // orange
{220, 180, 200} // clean / taupe?!
};
std::array<std::array<uint8_t, 16>, 512> candidate_cache;
bool cache_built = false;
std::array<uint8_t, 16> candidates;
RGB src_color;
RGB565 color;
IDirectDisplayDriver<uint8_t> &driver;
PicoGraphics_PenInky7(uint16_t width, uint16_t height, IDirectDisplayDriver<uint8_t> &direct_display_driver);
void set_pen(uint c) override;
void set_pen(uint8_t r, uint8_t g, uint8_t b) override;
void set_thickness(uint t) 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 get_dither_candidates(const RGB &col, const RGB *palette, size_t len, std::array<uint8_t, 16> &candidates);
void set_pixel_dither(const Point &p, const RGB &c) override;
void frame_convert(PenType type, conversion_callback_func callback) override;
static size_t buffer_size(uint w, uint h) {
return w * h;
}
};
}

View File

@ -0,0 +1,92 @@
#include "pico_graphics.hpp"
namespace pimoroni {
PicoGraphics_PenInky7::PicoGraphics_PenInky7(uint16_t width, uint16_t height, IDirectDisplayDriver<uint8_t> &direct_display_driver)
: PicoGraphics(width, height, nullptr),
driver(direct_display_driver) {
this->pen_type = PEN_INKY7;
}
void PicoGraphics_PenInky7::set_pen(uint c) {
color = c & 0x7;
}
void PicoGraphics_PenInky7::set_pen(uint8_t r, uint8_t g, uint8_t b) {
}
int PicoGraphics_PenInky7::create_pen(uint8_t r, uint8_t g, uint8_t b) {
return 0;
}
void PicoGraphics_PenInky7::set_pixel(const Point &p) {
driver.write_pixel(p, color);
}
void PicoGraphics_PenInky7::set_pixel_span(const Point &p, uint l) {
driver.write_pixel_span(p, l, color);
}
void PicoGraphics_PenInky7::get_dither_candidates(const RGB &col, const RGB *palette, size_t len, std::array<uint8_t, 16> &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_PenInky7::set_pixel_dither(const Point &p, const RGB &c) {
if(!bounds.contains(p)) return;
if(!cache_built) {
for(uint i = 0; i < 512; i++) {
uint r = (i & 0x1c0) >> 1;
uint g = (i & 0x38) << 2;
uint b = (i & 0x7) << 5;
RGB cache_col(
r | (r >> 3) | (r >> 6),
g | (g >> 3) | (g >> 6),
b | (b >> 3) | (b >> 6)
);
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][dither16_pattern[pattern_index]];
set_pixel(p);
}
void PicoGraphics_PenInky7::frame_convert(PenType type, conversion_callback_func callback) {
if(type == PEN_INKY7) {
uint byte_count = bounds.w/2;
uint8_t buffer[bounds.w];
for(int32_t r = 0; r < bounds.h; r++)
{
driver.read_pixel_span(Point(0, r), bounds.w, buffer);
// for(int y=0; y < 800; y++)
// buffer[y] = rand() % 7;
// convert byte storage to nibble storage
uint8_t *pDst = buffer;
uint8_t *pSrc = buffer;
for(uint c = 0; c < byte_count; c++)
{
uint8_t nibble = ((*pSrc++) << 4);
nibble |= ((*pSrc++) & 0xf);
*pDst++ = nibble;
}
callback(buffer, byte_count);
}
}
}
}

View File

@ -16,6 +16,6 @@ if [[ ! -d "$MPY_DIR/boards/$BOARD" ]] && [[ -d "$FIXUP_DIR/$NAME/$BOARD/" ]]; t
fi
if [[ -f "$FIXUP_DIR/$NAME/fixup.sh" ]]; then
echo "Running custom fixup[.sh"
echo "Running custom fixup.sh"
bash "$FIXUP_DIR/$NAME/fixup.sh" "$FIXUP_DIR/$NAME" "$MPY_DIR"
fi
fi

View File

@ -17,9 +17,6 @@
// Debug level (0-4) 1=warning, 2=info, 3=debug, 4=verbose
// #define MODUSSL_MBEDTLS_DEBUG_LEVEL 1
#define MICROPY_HW_PIN_CYW43_COUNT CYW43_WL_GPIO_COUNT
#ifdef CYW43_WL_GPIO_LED_PIN
#define MICROPY_HW_PIN_CYW43_LED_PIN_NUM CYW43_WL_GPIO_LED_PIN
#endif
#define MICROPY_HW_PIN_EXT_COUNT CYW43_WL_GPIO_COUNT
#define MICROPY_HW_PIN_RESERVED(i) ((i) == CYW43_PIN_WL_HOST_WAKE || (i) == CYW43_PIN_WL_REG_ON)

View File

@ -0,0 +1,30 @@
GP0,GPIO0
GP1,GPIO1
GP2,GPIO2
GP3,GPIO3
GP4,GPIO4
GP5,GPIO5
GP6,GPIO6
GP7,GPIO7
GP8,GPIO8
GP9,GPIO9
GP10,GPIO10
GP11,GPIO11
GP12,GPIO12
GP13,GPIO13
GP14,GPIO14
GP15,GPIO15
GP16,GPIO16
GP17,GPIO17
GP18,GPIO18
GP19,GPIO19
GP20,GPIO20
GP21,GPIO21
GP22,GPIO22
GP26,GPIO26
GP27,GPIO27
GP28,GPIO28
WL_GPIO0,EXT_GPIO0
WL_GPIO1,EXT_GPIO1
WL_GPIO2,EXT_GPIO2
LED,EXT_GPIO0
1 GP0 GPIO0
2 GP1 GPIO1
3 GP2 GPIO2
4 GP3 GPIO3
5 GP4 GPIO4
6 GP5 GPIO5
7 GP6 GPIO6
8 GP7 GPIO7
9 GP8 GPIO8
10 GP9 GPIO9
11 GP10 GPIO10
12 GP11 GPIO11
13 GP12 GPIO12
14 GP13 GPIO13
15 GP14 GPIO14
16 GP15 GPIO15
17 GP16 GPIO16
18 GP17 GPIO17
19 GP18 GPIO18
20 GP19 GPIO19
21 GP20 GPIO20
22 GP21 GPIO21
23 GP22 GPIO22
24 GP26 GPIO26
25 GP27 GPIO27
26 GP28 GPIO28
27 WL_GPIO0 EXT_GPIO0
28 WL_GPIO1 EXT_GPIO1
29 WL_GPIO2 EXT_GPIO2
30 LED EXT_GPIO0

View File

@ -3,4 +3,5 @@ include("../manifest.py")
require("mip")
require("ntptime")
require("urequests")
require("urllib.urequest")
require("umqtt.simple")

View File

@ -17,9 +17,6 @@
// Debug level (0-4) 1=warning, 2=info, 3=debug, 4=verbose
// #define MODUSSL_MBEDTLS_DEBUG_LEVEL 1
#define MICROPY_HW_PIN_CYW43_COUNT CYW43_WL_GPIO_COUNT
#ifdef CYW43_WL_GPIO_LED_PIN
#define MICROPY_HW_PIN_CYW43_LED_PIN_NUM CYW43_WL_GPIO_LED_PIN
#endif
#define MICROPY_HW_PIN_EXT_COUNT CYW43_WL_GPIO_COUNT
#define MICROPY_HW_PIN_RESERVED(i) ((i) == CYW43_PIN_WL_HOST_WAKE || (i) == CYW43_PIN_WL_REG_ON)

View File

@ -0,0 +1,30 @@
GP0,GPIO0
GP1,GPIO1
GP2,GPIO2
GP3,GPIO3
GP4,GPIO4
GP5,GPIO5
GP6,GPIO6
GP7,GPIO7
GP8,GPIO8
GP9,GPIO9
GP10,GPIO10
GP11,GPIO11
GP12,GPIO12
GP13,GPIO13
GP14,GPIO14
GP15,GPIO15
GP16,GPIO16
GP17,GPIO17
GP18,GPIO18
GP19,GPIO19
GP20,GPIO20
GP21,GPIO21
GP22,GPIO22
GP26,GPIO26
GP27,GPIO27
GP28,GPIO28
WL_GPIO0,EXT_GPIO0
WL_GPIO1,EXT_GPIO1
WL_GPIO2,EXT_GPIO2
LED,EXT_GPIO0
1 GP0 GPIO0
2 GP1 GPIO1
3 GP2 GPIO2
4 GP3 GPIO3
5 GP4 GPIO4
6 GP5 GPIO5
7 GP6 GPIO6
8 GP7 GPIO7
9 GP8 GPIO8
10 GP9 GPIO9
11 GP10 GPIO10
12 GP11 GPIO11
13 GP12 GPIO12
14 GP13 GPIO13
15 GP14 GPIO14
16 GP15 GPIO15
17 GP16 GPIO16
18 GP17 GPIO17
19 GP18 GPIO18
20 GP19 GPIO19
21 GP20 GPIO20
22 GP21 GPIO21
23 GP22 GPIO22
24 GP26 GPIO26
25 GP27 GPIO27
26 GP28 GPIO28
27 WL_GPIO0 EXT_GPIO0
28 WL_GPIO1 EXT_GPIO1
29 WL_GPIO2 EXT_GPIO2
30 LED EXT_GPIO0

View File

@ -145,7 +145,10 @@ MICROPY_EVENT_POLL_HOOK
} else if (current_graphics->pen_type == PicoGraphics::PEN_RGB888) {
current_graphics->set_pen(RGB((RGB565)pDraw->pPixels[i]).to_rgb888());
current_graphics->pixel({pDraw->x + x, pDraw->y + y});
} else if (current_graphics->pen_type == PicoGraphics::PEN_P8 || current_graphics->pen_type == PicoGraphics::PEN_P4 || current_graphics->pen_type == PicoGraphics::PEN_3BIT) {
} else if (current_graphics->pen_type == PicoGraphics::PEN_P8
|| current_graphics->pen_type == PicoGraphics::PEN_P4
|| current_graphics->pen_type == PicoGraphics::PEN_3BIT
|| current_graphics->pen_type == PicoGraphics::PEN_INKY7) {
current_graphics->set_pixel_dither({pDraw->x + x, pDraw->y + y}, RGB((RGB565)pDraw->pPixels[i]));
} else {
current_graphics->set_pen(pDraw->pPixels[i]);
@ -262,6 +265,7 @@ mp_obj_t _JPEG_decode(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args
case PicoGraphics::PEN_P8:
case PicoGraphics::PEN_P4:
case PicoGraphics::PEN_3BIT:
case PicoGraphics::PEN_INKY7:
self->jpeg->setPixelType(RGB565_BIG_ENDIAN);
break;
// TODO 2-bit is currently unsupported

View File

@ -11,6 +11,9 @@ target_sources(usermod_${MOD_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}/../../../drivers/uc8151/uc8151.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../drivers/uc8159/uc8159.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../drivers/st7567/st7567.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../drivers/inky73/inky73.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../drivers/shiftregister/shiftregister.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../drivers/psram_display/psram_display.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
@ -20,6 +23,7 @@ target_sources(usermod_${MOD_NAME} INTERFACE
${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/pico_graphics_pen_rgb888.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/pico_graphics_pen_inky7.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/types.cpp
)

View File

@ -150,6 +150,7 @@ STATIC const mp_map_elem_t picographics_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_INTERSTATE75_128X64), MP_ROM_INT(DISPLAY_INTERSTATE75_128X64) },
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_INTERSTATE75_192X64), MP_ROM_INT(DISPLAY_INTERSTATE75_192X64) },
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_INTERSTATE75_256X64), MP_ROM_INT(DISPLAY_INTERSTATE75_256X64) },
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_INKY_FRAME_7), MP_ROM_INT(DISPLAY_INKY_FRAME_7) },
{ MP_ROM_QSTR(MP_QSTR_PEN_1BIT), MP_ROM_INT(PEN_1BIT) },

View File

@ -4,6 +4,8 @@
#include "drivers/uc8151/uc8151.hpp"
#include "drivers/uc8159/uc8159.hpp"
#include "drivers/st7567/st7567.hpp"
#include "drivers/inky73/inky73.hpp"
#include "drivers/psram_display/psram_display.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
#include "common/pimoroni_common.hpp"
#include "common/pimoroni_bus.hpp"
@ -192,6 +194,14 @@ bool get_display_settings(PicoGraphicsDisplay display, int &width, int &height,
if(rotate == -1) rotate = (int)Rotation::ROTATE_0;
if(pen_type == -1) pen_type = PEN_RGB888;
break;
case DISPLAY_INKY_FRAME_7:
width = 800;
height = 480;
bus_type = BUS_SPI;
// Portrait to match labelling
if(rotate == -1) rotate = (int)Rotation::ROTATE_0;
if(pen_type == -1) pen_type = PEN_INKY7;
break;
default:
return false;
}
@ -214,6 +224,8 @@ size_t get_required_buffer_size(PicoGraphicsPenType pen_type, uint width, uint h
return PicoGraphics_PenRGB565::buffer_size(width, height);
case PEN_RGB888:
return PicoGraphics_PenRGB888::buffer_size(width, height);
case PEN_INKY7:
return PicoGraphics_PenInky7::buffer_size(width, height);
default:
return 0;
}
@ -276,7 +288,7 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size
self->i2c = (_PimoroniI2C_obj_t *)MP_OBJ_TO_PTR(PimoroniI2C_make_new(&PimoroniI2C_type, 0, 0, nullptr));
i2c_bus = (pimoroni::I2C *)(self->i2c->i2c);
} else if (bus_type == BUS_SPI) {
if(display == DISPLAY_INKY_FRAME || display == DISPLAY_INKY_FRAME_4) {
if(display == DISPLAY_INKY_FRAME || display == DISPLAY_INKY_FRAME_4 || display == DISPLAY_INKY_FRAME_7) {
spi_bus = {PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_FRONT_CS, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, PIN_UNUSED, 28, PIN_UNUSED};
} else if (display == DISPLAY_INKY_PACK) {
spi_bus = {PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_FRONT_CS, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, PIN_UNUSED, 20, PIN_UNUSED};
@ -292,6 +304,11 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size
// TODO grab BUSY and RESET from ARG_extra_pins
self->display = m_new_class(UC8159, width, height, (Rotation)rotate, spi_bus);
} else if (display == DISPLAY_INKY_FRAME_7) {
pen_type = PEN_INKY7;
// TODO grab BUSY and RESET from ARG_extra_pins
self->display = m_new_class(Inky73, width, height, (Rotation)rotate, spi_bus);
} else if (display == DISPLAY_TUFTY_2040) {
self->display = m_new_class(ST7789, width, height, (Rotation)rotate, parallel_bus);
@ -324,15 +341,19 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size
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!");
}
if(pen_type == PEN_INKY7) {
self->buffer = m_new_class(PSRamDisplay, width, height);
} else {
self->buffer = m_new(uint8_t, required_size);
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
@ -363,6 +384,9 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size
case PEN_RGB888:
self->graphics = m_new_class(PicoGraphics_PenRGB888, self->display->width, self->display->height, self->buffer);
break;
case PEN_INKY7:
self->graphics = m_new_class(PicoGraphics_PenInky7, self->display->width, self->display->height, *(IDirectDisplayDriver<uint8_t> *)self->buffer);
break;
default:
break;
}
@ -376,7 +400,7 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size
self->graphics->clear();
// Update the LCD from the graphics library
if (display != DISPLAY_INKY_FRAME && display != DISPLAY_INKY_FRAME_4 && display != DISPLAY_INKY_PACK) {
if (display != DISPLAY_INKY_FRAME && display != DISPLAY_INKY_FRAME_4 && display != DISPLAY_INKY_PACK && display != DISPLAY_INKY_FRAME_7) {
self->display->update(self->graphics);
}

View File

@ -23,7 +23,8 @@ enum PicoGraphicsDisplay {
DISPLAY_INTERSTATE75_64X64,
DISPLAY_INTERSTATE75_128X64,
DISPLAY_INTERSTATE75_192X64,
DISPLAY_INTERSTATE75_256X64,
DISPLAY_INTERSTATE75_256X64,
DISPLAY_INKY_FRAME_7,
};
enum PicoGraphicsPenType {
@ -34,7 +35,8 @@ enum PicoGraphicsPenType {
PEN_P8,
PEN_RGB332,
PEN_RGB565,
PEN_RGB888
PEN_RGB888,
PEN_INKY7,
};
enum PicoGraphicsBusType {

View File

@ -14,6 +14,9 @@ LED_C = 13
LED_D = 14
LED_E = 15
LED_BUSY = 6
LED_WIFI = 7
SHIFT_STATE = get_shift_state()
reset_shift_state()
@ -66,3 +69,6 @@ button_b = Button(sr, 6, LED_B)
button_c = Button(sr, 5, LED_C)
button_d = Button(sr, 4, LED_D)
button_e = Button(sr, 3, LED_E)
led_busy = Pin(LED_BUSY, Pin.OUT)
led_wifi = Pin(LED_WIFI, Pin.OUT)