Merge pull request #527 from pimoroni/driver/inky-frame-4.0

Support for Inky Frame 4.0"
This commit is contained in:
Philip Howard 2022-10-06 12:09:17 +01:00 committed by GitHub
commit 1ac55c49ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 236 additions and 111 deletions

View File

@ -88,16 +88,35 @@ namespace pimoroni {
reset();
busy_wait();
command(0x00, {0xE3, 0x08});
command(0x01, {0x37, 0x00, 0x23, 0x23});
command(0x03, {0x00});
command(0x06, {0xC7, 0xC7, 0x1D});
command(0x30, {0x3C});
command(0x40, {0x00});
command(0x50, {0x37});
command(0x60, {0x22});
command(0x61, {0x02, 0x58, 0x01, 0xC0});
command(0xE3, {0xAA});
uint8_t dimensions[4] = {
uint8_t(width >> 8),
uint8_t(width),
uint8_t(height >> 8),
uint8_t(height)
};
if (width == 600) {
if (rotation == ROTATE_0) {
command(PSR, {0xE3, 0x08});
} else {
command(PSR, {0xEF, 0x08});
}
} else {
if (rotation == ROTATE_0) {
command(PSR, {0xA3, 0x08});
} else {
command(PSR, {0xAF, 0x08});
}
}
command(PWR, {0x37, 0x00, 0x23, 0x23});
command(PFS, {0x00});
command(BTST, {0xC7, 0xC7, 0x1D});
command(PLL, {0x3C});
command(TSC, {0x00});
command(CDI, {0x37});
command(TCON, {0x22});
command(TRES, 4, dimensions);
command(PWS, {0xAA});
sleep_ms(100);
@ -154,6 +173,15 @@ namespace pimoroni {
spi_write_blocking(spi, &reg, 1);
gpio_put(DC, 1); // data mode
// HACK: Output 48 rows of data since our buffer is 400px tall
// but the display has no offset configuration and H/V scan
// are reversed.
// Any garbage data will do.
// 2px per byte, so we need width * 24 bytes
if(height == 400 && rotation == ROTATE_0) {
spi_write_blocking(spi, (uint8_t *)graphics->frame_buffer, width * 24);
}
graphics->frame_convert(PicoGraphics::PEN_P4, [this](void *buf, size_t length) {
if (length > 0) {
spi_write_blocking(spi, (const uint8_t*)buf, length);

View File

@ -42,10 +42,12 @@ namespace pimoroni {
CLEAN = 7
};
UC8159(uint16_t width, uint16_t height) : UC8159(width, height, {PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_FRONT_CS, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, PIN_UNUSED, 28, PIN_UNUSED}) {};
UC8159(uint16_t width, uint16_t height) : UC8159(width, height, ROTATE_0, {PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_FRONT_CS, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, PIN_UNUSED, 28, PIN_UNUSED}) {};
UC8159(uint16_t width, uint16_t height, SPIPins pins, uint busy=PIN_UNUSED, uint reset=27) :
DisplayDriver(width, height, ROTATE_0),
UC8159(uint16_t width, uint16_t height, SPIPins pins, uint busy=PIN_UNUSED, uint reset=27) : UC8159(width, height, ROTATE_0, pins, busy, reset) {};
UC8159(uint16_t width, uint16_t height, Rotation rotation, SPIPins pins, uint busy=PIN_UNUSED, uint reset=27) :
DisplayDriver(width, height, rotation),
spi(pins.spi),
CS(pins.cs), DC(pins.dc), SCK(pins.sck), MOSI(pins.mosi), BUSY(busy), RESET(reset) {
init();

View File

@ -8,7 +8,7 @@
using namespace pimoroni;
InkyFrame inky;
InkyFrame inky(640, 400);
datetime_t now;
datetime_t today;
@ -79,7 +79,7 @@ int days_in_month(datetime_t &dt)
void center_text(std::string message, int y, float scale = 1.0f) {
int32_t tw = inky.measure_text(message, scale);
inky.text(message, {(600 / 2) - (tw / 2), y}, scale);
inky.text(message, {(inky.width / 2) - (tw / 2), y}, scale);
}
void center_text(std::string message, int x, int y, int w, float scale = 1.0f) {
@ -94,7 +94,7 @@ void render_calendar_view() {
//inky.text_aspect(1.1f);
inky.set_pen(InkyFrame::RED);
inky.rectangle({0, 0, width, 448});
inky.rectangle({0, 0, width, inky.height});
inky.set_pen(InkyFrame::WHITE);
@ -343,7 +343,7 @@ void render_calendar_entries() {
int spacing = 5;
int xoff = 240 + spacing;
int yoff = spacing;
int width = 600 - xoff - spacing;
int width = inky.width - xoff - spacing;
int row_height = 50;
//inky.text_tracking(1.0f);

View File

@ -142,7 +142,7 @@ namespace pimoroni {
// Display an image that fills the screen (286*128)
void InkyFrame::image(const uint8_t* data) {
image(data, WIDTH, 0, 0, WIDTH, HEIGHT, 0, 0);
image(data, width, 0, 0, width, height, 0, 0);
}
// Display an image smaller than the screen (sw*sh) at dx, dy

View File

@ -89,14 +89,26 @@ namespace pimoroni {
I2C i2c;
PCF85063A rtc;
int width;
int height;
[[deprecated("Use instance variable width.")]]
static const int WIDTH = 600;
[[deprecated("Use instance variable height.")]]
static const int HEIGHT = 448;
InkyFrame() :
PicoGraphics_Pen3Bit(WIDTH, HEIGHT, nullptr),
uc8159(WIDTH, HEIGHT, {spi0, EINK_CS, CLK, MOSI, PIN_UNUSED, EINK_DC, PIN_UNUSED}),
// Default 5.7" constructor
InkyFrame() : InkyFrame(600, 448) {};
// 600x448 for 5.7"
// 640x400 for 4.0"
InkyFrame(int width, int height) :
PicoGraphics_Pen3Bit(width, height, nullptr),
uc8159(width, height, {spi0, EINK_CS, CLK, MOSI, PIN_UNUSED, EINK_DC, PIN_UNUSED}),
i2c(4, 5),
rtc(&i2c) {
rtc(&i2c),
width(width),
height(height) {
}
void init();

View File

@ -0,0 +1,85 @@
# This example shows you a simple, non-interrupt way of reading Inky Frame's buttons with a loop that checks to see if buttons are pressed.
from pimoroni import ShiftRegister
from picographics import PicoGraphics, DISPLAY_INKY_FRAME as DISPLAY # 5.7"
# from picographics import PicoGraphics, DISPLAY_INKY_FRAME_4 as DISPLAY # 4.0"
from machine import Pin
display = PicoGraphics(display=DISPLAY)
display.set_font("bitmap8")
# Inky Frame uses a shift register to read the buttons
SR_CLOCK = 8
SR_LATCH = 9
SR_OUT = 10
sr = ShiftRegister(SR_CLOCK, SR_LATCH, SR_OUT)
# set up the button LEDs
button_a_led = Pin(11, Pin.OUT)
button_b_led = Pin(12, Pin.OUT)
button_c_led = Pin(13, Pin.OUT)
button_d_led = Pin(14, Pin.OUT)
button_e_led = Pin(15, Pin.OUT)
# a handy function we can call to clear the screen
# display.set_pen(1) is white and display.set_pen(0) is black
def clear():
display.set_pen(1)
display.clear()
# set up
clear()
display.set_pen(0)
display.text("Press any button!", 10, 10, scale=4)
display.update()
while True:
button_a_led.off()
button_b_led.off()
button_c_led.off()
button_d_led.off()
button_e_led.off()
# read the shift register
# we can tell which button has been pressed by checking if a specific bit is 0 or 1
result = sr.read()
button_a = sr[7]
button_b = sr[6]
button_c = sr[5]
button_d = sr[4]
button_e = sr[3]
if button_a == 1: # if a button press is detected then...
button_a_led.on()
clear() # clear to white
display.set_pen(4) # change the pen colour
display.text("Button A pressed", 10, 10, scale=4) # display some text on the screen
display.update() # update the display
elif button_b == 1:
button_b_led.on()
clear()
display.set_pen(6)
display.text("Button B pressed", 10, 50, scale=4)
display.update()
elif button_c == 1:
button_c_led.on()
clear()
display.set_pen(5)
display.text("Button C pressed", 10, 90, scale=4)
display.update()
elif button_d == 1:
button_d_led.on()
clear()
display.set_pen(2)
display.text("Button D pressed", 10, 130, scale=4)
display.update()
elif button_e == 1:
button_e_led.on()
clear()
display.set_pen(3)
display.text("Button E pressed", 10, 170, scale=4)
display.update()

View File

@ -1,12 +1,10 @@
# This example shows you a simple, non-interrupt way of reading Inky Frame's buttons with a loop that checks to see if buttons are pressed.
# This example allows you to test Inky Frame's buttons
# It does not update the screen.
from pimoroni import ShiftRegister
from picographics import PicoGraphics, DISPLAY_INKY_FRAME
from machine import Pin
import time
display = PicoGraphics(display=DISPLAY_INKY_FRAME)
display.set_font("bitmap8")
# Inky Frame uses a shift register to read the buttons
SR_CLOCK = 8
@ -15,70 +13,47 @@ SR_OUT = 10
sr = ShiftRegister(SR_CLOCK, SR_LATCH, SR_OUT)
# set up the button LEDs
button_a_led = Pin(11, Pin.OUT)
button_b_led = Pin(12, Pin.OUT)
button_c_led = Pin(13, Pin.OUT)
button_d_led = Pin(14, Pin.OUT)
button_e_led = Pin(15, Pin.OUT)
# Simple class to debounce button input and handle LED
class Button:
def __init__(self, idx, led, debounce=50):
self.led = Pin(led, Pin.OUT) # LEDs are just regular IOs
self.led.on()
self._idx = idx
self._debounce_time = debounce
self._changed = time.ticks_ms()
self._last_value = None
def debounced(self):
return time.ticks_ms() - self._changed > self._debounce_time
def get(self, sr):
value = sr[self._idx]
if value != self._last_value and self.debounced():
self._last_value = value
self._changed = time.ticks_ms()
return value
# a handy function we can call to clear the screen
# display.set_pen(1) is white and display.set_pen(0) is black
def clear():
display.set_pen(1)
display.clear()
button_a = Button(7, 11)
button_b = Button(6, 12)
button_c = Button(5, 13)
button_d = Button(4, 14)
button_e = Button(3, 15)
# set up
clear()
display.set_pen(0)
display.text("Press any button!", 10, 10, scale=4)
display.update()
while True:
button_a_led.off()
button_b_led.off()
button_c_led.off()
button_d_led.off()
button_e_led.off()
sr.read()
# read the shift register
# we can tell which button has been pressed by checking if a specific bit is 0 or 1
result = sr.read()
button_a = sr[7]
button_b = sr[6]
button_c = sr[5]
button_d = sr[4]
button_e = sr[3]
if button_a.get(sr):
button_a.led.toggle()
if button_b.get(sr):
button_b.led.toggle()
if button_c.get(sr):
button_c.led.toggle()
if button_d.get(sr):
button_d.led.toggle()
if button_e.get(sr):
button_e.led.toggle()
if button_a == 1: # if a button press is detected then...
button_a_led.on()
clear() # clear to white
display.set_pen(4) # change the pen colour
display.text("Button A pressed", 10, 10, scale=4) # display some text on the screen
display.update() # update the display
elif button_b == 1:
button_b_led.on()
clear()
display.set_pen(6)
display.text("Button B pressed", 10, 50, scale=4)
display.update()
elif button_c == 1:
button_c_led.on()
clear()
display.set_pen(5)
display.text("Button C pressed", 10, 90, scale=4)
display.update()
elif button_d == 1:
button_d_led.on()
clear()
display.set_pen(2)
display.text("Button D pressed", 10, 130, scale=4)
display.update()
elif button_e == 1:
button_e_led.on()
clear()
display.set_pen(3)
display.text("Button E pressed", 10, 170, scale=4)
display.update()
time.sleep(1.0 / 60) # Poll 60 times/second

View File

@ -4,7 +4,8 @@ from network_manager import NetworkManager
import uasyncio
import ujson
from urllib import urequest
from picographics import PicoGraphics, DISPLAY_INKY_FRAME
from picographics import PicoGraphics, DISPLAY_INKY_FRAME as DISPLAY # 5.7"
# from picographics import PicoGraphics, DISPLAY_INKY_FRAME_4 as DISPLAY # 4.0"
from machine import Pin
from pimoroni_i2c import PimoroniI2C
from pcf85063a import PCF85063A
@ -38,7 +39,7 @@ def status_handler(mode, status, ip):
network_manager = NetworkManager(WIFI_CONFIG.COUNTRY, status_handler=status_handler)
gc.collect()
graphics = PicoGraphics(DISPLAY_INKY_FRAME)
graphics = PicoGraphics(DISPLAY)
WIDTH, HEIGHT = graphics.get_bounds()
gc.collect()

View File

@ -1,4 +1,5 @@
from picographics import PicoGraphics, DISPLAY_INKY_FRAME
from picographics import PicoGraphics, DISPLAY_INKY_FRAME as DISPLAY # 5.7"
# from picographics import PicoGraphics, DISPLAY_INKY_FRAME_4 as DISPLAY # 4.0"
from network_manager import NetworkManager
import uasyncio
from urllib import urequest
@ -27,7 +28,7 @@ URL = "http://feeds.bbci.co.uk/news/technology/rss.xml"
# Frequent updates will reduce battery life!
UPDATE_INTERVAL = 60 * 1
graphics = PicoGraphics(DISPLAY_INKY_FRAME)
graphics = PicoGraphics(DISPLAY)
WIDTH, HEIGHT = graphics.get_bounds()
graphics.set_font("bitmap8")
code = qrcode.QRCode()

View File

@ -8,7 +8,8 @@ import sdcard
import WIFI_CONFIG
from urllib import urequest
from network_manager import NetworkManager
from picographics import PicoGraphics, DISPLAY_INKY_FRAME as DISPLAY
from picographics import PicoGraphics, DISPLAY_INKY_FRAME as DISPLAY # 5.7"
# from picographics import PicoGraphics, DISPLAY_INKY_FRAME_4 as DISPLAY # 4.0"
"""
random placekitten (from a very small set)

View File

@ -5,7 +5,8 @@ import uasyncio
import WIFI_CONFIG
from urllib import urequest
from network_manager import NetworkManager
from picographics import PicoGraphics, DISPLAY_INKY_FRAME
from picographics import PicoGraphics, DISPLAY_INKY_FRAME as DISPLAY # 5.7"
# from picographics import PicoGraphics, DISPLAY_INKY_FRAME_4 as DISPLAY # 4.0"
ENDPOINT = "https://en.wikiquote.org/w/api.php?format=json&action=expandtemplates&prop=wikitext&text={{{{Wikiquote:Quote%20of%20the%20day/{3}%20{2},%20{0}}}}}"
@ -31,7 +32,7 @@ def status_handler(mode, status, ip):
network_manager = NetworkManager(WIFI_CONFIG.COUNTRY, status_handler=status_handler)
gc.collect()
graphics = PicoGraphics(DISPLAY_INKY_FRAME)
graphics = PicoGraphics(DISPLAY)
WIDTH, HEIGHT = graphics.get_bounds()
graphics.set_font("bitmap8")
gc.collect()

View File

@ -6,7 +6,8 @@ import jpegdec
import WIFI_CONFIG
import uasyncio
from network_manager import NetworkManager
from picographics import PicoGraphics, DISPLAY_INKY_FRAME as DISPLAY
from picographics import PicoGraphics, DISPLAY_INKY_FRAME as DISPLAY # 5.7"
# from picographics import PicoGraphics, DISPLAY_INKY_FRAME_4 as DISPLAY # 4.0"
from urllib import urequest
@ -27,7 +28,7 @@ WIDTH, HEIGHT = graphics.get_bounds()
FILENAME = "/sd/random-joke.jpg"
JOKE_IDS = "https://pimoroni.github.io/feed2image/jokeapi-ids.txt"
JOKE_IMG = "https://pimoroni.github.io/feed2image/jokeapi-{}-600x448.jpg"
JOKE_IMG = "https://pimoroni.github.io/feed2image/jokeapi-{}-{}x{}.jpg"
import sdcard # noqa: E402 - putting this at the top causes an MBEDTLS OOM error!?
sd_spi = machine.SPI(0, sck=machine.Pin(18, machine.Pin.OUT), mosi=machine.Pin(19, machine.Pin.OUT), miso=machine.Pin(16, machine.Pin.OUT))
@ -59,7 +60,7 @@ socket.close()
print("Random joke ID: {}".format(random_joke_id))
url = JOKE_IMG.format(random_joke_id)
url = JOKE_IMG.format(random_joke_id, WIDTH, HEIGHT)
socket = urequest.urlopen(url)

View File

@ -1,6 +1,5 @@
import gc
import uos
import random
import machine
import jpegdec
import uasyncio
@ -8,7 +7,8 @@ import sdcard
import WIFI_CONFIG
from urllib import urequest
from network_manager import NetworkManager
from picographics import PicoGraphics, DISPLAY_INKY_FRAME as DISPLAY
from picographics import PicoGraphics, DISPLAY_INKY_FRAME as DISPLAY # 5.7"
# from picographics import PicoGraphics, DISPLAY_INKY_FRAME_4 as DISPLAY # 4.0"
"""
xkcd daily
@ -46,7 +46,10 @@ uos.mount(sd, "/sd")
gc.collect() # Claw back some RAM!
url = ENDPOINT.format(WIDTH, HEIGHT + random.randint(0, 10))
url = ENDPOINT
if (WIDTH, HEIGHT) != (600, 448):
url = url.replace("xkcd-", f"xkcd-{WIDTH}x{HEIGHT}-")
socket = urequest.urlopen(url)

View File

@ -18,10 +18,16 @@ led_e = PWM(Pin(15))
led_activity.freq(1000)
leds = [led_activity, led_connect, led_a, led_b, led_c, led_d, led_e]
n = 0
while True:
for duty in range(65025):
led_activity.duty_u16(duty)
sleep(0.0001)
for duty in range(65025, 0, -1):
led_activity.duty_u16(duty)
sleep(0.0001)
for _ in range(2):
for duty in range(65025, 2):
leds[n].duty_u16(duty)
sleep(0.0001)
for duty in range(65025, 0, -2):
leds[n].duty_u16(duty)
sleep(0.0001)
n += 1
n %= len(leds)

View File

@ -124,6 +124,7 @@ STATIC const mp_map_elem_t picographics_globals_table[] = {
{ 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_DISPLAY_INKY_FRAME), MP_ROM_INT(DISPLAY_INKY_FRAME) },
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_INKY_FRAME_4), MP_ROM_INT(DISPLAY_INKY_FRAME_4) },
{ 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

@ -105,6 +105,13 @@ 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_P4;
break;
case DISPLAY_INKY_FRAME_4:
width = 640;
height = 400;
bus_type = BUS_SPI;
if(rotate == -1) rotate = (int)Rotation::ROTATE_0;
if(pen_type == -1) pen_type = PEN_P4;
break;
default:
return false;
}
@ -187,7 +194,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) {
if(display == DISPLAY_INKY_FRAME || display == DISPLAY_INKY_FRAME_4) {
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};
@ -196,10 +203,10 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size
}
// Try to create an appropriate display driver
if (display == DISPLAY_INKY_FRAME) {
if (display == DISPLAY_INKY_FRAME || display == DISPLAY_INKY_FRAME_4) {
pen_type = PEN_3BIT; // FORCE to 3BIT
// TODO grab BUSY and RESET from ARG_extra_pins
self->display = m_new_class(UC8159, width, height, spi_bus);
self->display = m_new_class(UC8159, width, height, (Rotation)rotate, spi_bus);
} else if (display == DISPLAY_TUFTY_2040) {
self->display = m_new_class(ST7789, width, height, (Rotation)rotate, parallel_bus);
@ -273,7 +280,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_PACK) {
if (display != DISPLAY_INKY_FRAME && display != DISPLAY_INKY_FRAME_4 && display != DISPLAY_INKY_PACK) {
self->display->update(self->graphics);
}

View File

@ -12,7 +12,8 @@ enum PicoGraphicsDisplay {
DISPLAY_LCD_160X80,
DISPLAY_I2C_OLED_128X128,
DISPLAY_INKY_PACK,
DISPLAY_INKY_FRAME
DISPLAY_INKY_FRAME,
DISPLAY_INKY_FRAME_4
};
enum PicoGraphicsPenType {