Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Full resolution display support #7

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 33 additions & 66 deletions drivers/st7701/st7701.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,6 @@ void ST7701::start_frame_xfer()
if (next_framebuffer) {
framebuffer = next_framebuffer;
next_framebuffer = nullptr;

init_framebuffer();
}

next_line_addr = 0;
Expand All @@ -192,19 +190,6 @@ void ST7701::start_frame_xfer()
__sev();
}

void ST7701::init_framebuffer() {
// If framebuffer not in cache XIP memory, do nothing.
if ((intptr_t)framebuffer >= 0x14000000) return;

// Flush the frame buffer from cache
uint8_t* framebuffer_maintenance = (uint8_t*)framebuffer + 0x8000000;
for (int i = 3; i < width*height << 1; i += 8)
framebuffer_maintenance[i] = 0; // Clean

// Switch to uncached reads
framebuffer += 0x2000000;
}

ST7701::ST7701(uint16_t width, uint16_t height, Rotation rotation, SPIPins control_pins, uint16_t* framebuffer,
uint d0, uint hsync, uint vsync, uint lcd_de, uint lcd_dot_clk) :
DisplayDriver(width, height, rotation),
Expand All @@ -219,18 +204,10 @@ void ST7701::init_framebuffer() {
void ST7701::init() {
irq_handler_t current = nullptr;

init_framebuffer();

st_pio = pio1;
parallel_sm = pio_claim_unused_sm(st_pio, true);

if (width == 480) parallel_offset = pio_add_program(st_pio, &st7701_parallel_program);
else if (width == 240) parallel_offset = pio_add_program(st_pio, &st7701_parallel_double_program);
else {
printf("Unsupported width\n");
return;
}

parallel_offset = pio_add_program(st_pio, &st7701_parallel_program);
if (height == 240) row_shift = 1;

timing_sm = pio_claim_unused_sm(st_pio, true);
Expand Down Expand Up @@ -268,9 +245,7 @@ void ST7701::init_framebuffer() {
pio_sm_set_consecutive_pindirs(st_pio, parallel_sm, d0, 16, true);
pio_sm_set_consecutive_pindirs(st_pio, parallel_sm, hsync, 4, true);

pio_sm_config c;
if (width == 480) c = st7701_parallel_program_get_default_config(parallel_offset);
else c = st7701_parallel_double_program_get_default_config(parallel_offset);
pio_sm_config c = st7701_parallel_program_get_default_config(parallel_offset);

sm_config_set_out_pins(&c, d0, 16);
sm_config_set_sideset_pins(&c, lcd_de);
Expand All @@ -279,18 +254,18 @@ void ST7701::init_framebuffer() {
sm_config_set_in_shift(&c, false, false, 32);

// Determine clock divider
uint32_t max_pio_clk;
if ((intptr_t)framebuffer >= 0x14000000 || width <= 240) {
// Frame buffer in internal RAM or pixel doubling, go as fast as possible
max_pio_clk = 34 * MHZ;
uint32_t max_pio_clk = 34 * MHZ;
const uint32_t sys_clk_hz = clock_get_hz(clk_sys);
uint32_t clk_div = (sys_clk_hz + max_pio_clk - 1) / max_pio_clk;
if (width == 480) {
// Parallel output SM must run at double the rate of the timing SM for full res
if (clk_div & 1) clk_div += 1;
sm_config_set_clkdiv(&c, clk_div >> 1);
}
else {
// Frame buffer in PSRAM and not pixel doubling, slow down a bit
max_pio_clk = 25 * MHZ;
else
{
sm_config_set_clkdiv(&c, clk_div);
}
const uint32_t sys_clk_hz = clock_get_hz(clk_sys);
const uint32_t clk_div = (sys_clk_hz + max_pio_clk - 1) / max_pio_clk;
sm_config_set_clkdiv(&c, clk_div);

pio_sm_init(st_pio, parallel_sm, parallel_offset, &c);
pio_sm_exec(st_pio, parallel_sm, pio_encode_out(pio_y, 32));
Expand Down Expand Up @@ -349,17 +324,11 @@ void ST7701::init_framebuffer() {
// if a backlight pin is provided then set it up for
// pwm control
if(lcd_bl != PIN_UNUSED) {
#if 0
pwm_config cfg = pwm_get_default_config();
pwm_set_wrap(pwm_gpio_to_slice_num(lcd_bl), 65535);
pwm_config_set_wrap(&cfg, BACKLIGHT_PWM_TOP);
pwm_init(pwm_gpio_to_slice_num(lcd_bl), &cfg, true);
gpio_set_function(lcd_bl, GPIO_FUNC_PWM);
set_backlight(0); // Turn backlight off initially to avoid nasty surprises
#else
gpio_init(lcd_bl);
gpio_set_dir(lcd_bl, GPIO_OUT);
gpio_put(lcd_bl, 0);
#endif
}

command(reg::SWRESET);
Expand Down Expand Up @@ -430,11 +399,7 @@ void ST7701::init_framebuffer() {
if(lcd_bl != PIN_UNUSED) {
//update(); // Send the new buffer to the display to clear any previous content
sleep_ms(50); // Wait for the update to apply
#if 0
set_backlight(255); // Turn backlight on now surprises have passed
#else
gpio_put(lcd_bl, 1);
#endif
}
}

Expand Down Expand Up @@ -529,31 +494,33 @@ void ST7701::init_framebuffer() {
}

void ST7701::update(PicoGraphics *graphics) {
//uint8_t cmd = reg::RAMWR;

// TODO: Where's my buffer at? Where's my buffer at?
// I left it out back in the PSRAM chip, y'all
// And now I cannot find it even though it's 60k tall

if(graphics->pen_type == PicoGraphics::PEN_RGB565) { // Display buffer is screen native
// command(cmd, width * height * sizeof(uint16_t), (const char*)graphics->frame_buffer);
if(graphics->pen_type == PicoGraphics::PEN_RGB565 && graphics->layers == 1) { // Display buffer is screen native
memcpy(framebuffer, graphics->frame_buffer, width * height * sizeof(uint16_t));
} else {
/*graphics->frame_convert(PicoGraphics::PEN_RGB565, [this](void *data, size_t length) {
uint8_t* frame_ptr = (uint8_t*)framebuffer;
graphics->frame_convert(PicoGraphics::PEN_RGB565, [this, &frame_ptr](void *data, size_t length) {
if (length > 0) {
write_blocking_dma((const uint8_t*)data, length);
}
else {
dma_channel_wait_for_finish_blocking(st_dma);
memcpy(frame_ptr, data, length);
frame_ptr += length;
}
});*/
});
}
}

void ST7701::partial_update(PicoGraphics *graphics, Rect region) {
if(graphics->pen_type == PicoGraphics::PEN_RGB565 && graphics->layers == 1) { // Display buffer is screen native
for (int y = region.y; y < region.y + region.h; ++y) {
memcpy(&framebuffer[y * width + region.x], (uint16_t*)graphics->frame_buffer + y * width + region.x, region.w * sizeof(uint16_t));
}
}
}

void ST7701::set_backlight(uint8_t brightness) {
// gamma correct the provided 0-255 brightness value onto a
// 0-65535 range for the pwm counter
float gamma = 2.8;
uint16_t value = (uint16_t)(pow((float)(brightness) / 255.0f, gamma) * 65535.0f + 0.5f);
// At least on my hardware this gives reasonable control over the possible range of backlight brightness
uint16_t value;
if (brightness == 0) value = 0;
else if (brightness == 255) value = BACKLIGHT_PWM_TOP;
else value = 181 + (brightness * brightness) / 85;
pwm_set_gpio_level(lcd_bl, value);
}

Expand Down
3 changes: 2 additions & 1 deletion drivers/st7701/st7701.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ namespace pimoroni {
uint lcd_dot_clk = 22;

static const uint32_t SPI_BAUD = 8'000'000;
static const uint32_t BACKLIGHT_PWM_TOP = 6200;

public:
// Parallel init
Expand All @@ -56,6 +57,7 @@ namespace pimoroni {
void init();
void cleanup() override;
void update(PicoGraphics *graphics) override;
void partial_update(PicoGraphics *display, Rect region) override;
void set_backlight(uint8_t brightness) override;

void set_framebuffer(uint16_t* next_fb) {
Expand All @@ -77,7 +79,6 @@ namespace pimoroni {

void start_line_xfer();
void start_frame_xfer();
void init_framebuffer();

// Timing status
uint16_t timing_row = 0;
Expand Down
16 changes: 1 addition & 15 deletions drivers/st7701/st7701_parallel.pio
Original file line number Diff line number Diff line change
@@ -1,22 +1,8 @@
; Output 16 bit parallel RGB565 data every clock
; Output 16 bit parallel, bit reversed, RGB565 data every 4th clock
; Wait for irq 4 from the timing SM between each row
; Side-set is data enable

.program st7701_parallel
.side_set 1

mov pins, null side 0
wait 1 irq 4 side 0
.wrap_target
out isr, 32 side 1
mov pins, ::isr side 1
in null, 16 side 1
mov pins, ::isr side 1
.wrap

; Output 16 bit parallel RGB565 data every other clock

.program st7701_parallel_double
.side_set 1

.wrap_target
Expand Down
138 changes: 138 additions & 0 deletions examples/vector_clock_full.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import presto

import time
import gc

from picographics import PicoGraphics, DISPLAY_PRESTO_FULL_RES
from picovector import PicoVector, Polygon, RegularPolygon, Rectangle, ANTIALIAS_X4, ANTIALIAS_X16

machine.freq(264000000)

presto = presto.Presto(full_res=True)

display = PicoGraphics(DISPLAY_PRESTO_FULL_RES)

vector = PicoVector(display)
vector.set_antialiasing(ANTIALIAS_X4)

RED = display.create_pen(200, 0, 0)
BLACK = display.create_pen(0, 0, 0)
GREY = display.create_pen(200, 200, 200)
WHITE = display.create_pen(255, 255, 255)

"""
# Redefine colours for a Blue clock
RED = display.create_pen(200, 0, 0)
BLACK = display.create_pen(135, 159, 169)
GREY = display.create_pen(10, 40, 50)
WHITE = display.create_pen(14, 60, 76)
"""

WIDTH, HEIGHT = display.get_bounds()

hub = RegularPolygon(int(WIDTH / 2), int(HEIGHT / 2), 24, 5)

face = RegularPolygon(int(WIDTH / 2), int(HEIGHT / 2), 48, int(HEIGHT / 2))

print(time.localtime())

last_second = None

def fade_in():
for j in range(101):
presto.set_backlight(j*0.01)
time.sleep_us(700)

def fade_out():
for j in range(101):
presto.set_backlight(1 - j*0.01)
time.sleep_us(700)

while True:
t_start = time.ticks_ms()
year, month, day, hour, minute, second, _, _ = time.localtime()

if last_second == second:
time.sleep_ms(10)
continue

last_second = second

fade_out()

display.set_pen(0)
display.clear()

display.set_pen(BLACK)
display.circle(int(WIDTH / 2), int(HEIGHT / 2), int(HEIGHT / 2))
display.set_pen(WHITE)
display.circle(int(WIDTH / 2), int(HEIGHT / 2), int(HEIGHT / 2) - 4)

display.set_pen(GREY)

for a in range(60):
tick_mark = Rectangle(int(WIDTH / 2) - 3, 10, 6, int(HEIGHT / 48))
vector.rotate(tick_mark, 360 / 60.0 * a, int(WIDTH / 2), int(HEIGHT / 2))
vector.translate(tick_mark, 0, 2)
vector.draw(tick_mark)

for a in range(12):
hour_mark = Rectangle(int(WIDTH / 2) - 5, 10, 10, int(HEIGHT / 10))
vector.rotate(hour_mark, 360 / 12.0 * a, int(WIDTH / 2), int(HEIGHT / 2))
vector.translate(hour_mark, 0, 2)
vector.draw(hour_mark)

angle_second = second * 6
second_hand_length = int(HEIGHT / 2) - int(HEIGHT / 8)
second_hand = Polygon((-2, -second_hand_length), (-2, int(HEIGHT / 8)), (2, int(HEIGHT / 8)), (2, -second_hand_length))
vector.rotate(second_hand, angle_second, 0, 0)
vector.translate(second_hand, int(WIDTH / 2), int(HEIGHT / 2) + 5)

angle_minute = minute * 6
angle_minute += second / 10.0
minute_hand_length = int(HEIGHT / 2) - int(HEIGHT / 24)
minute_hand = Polygon((-5, -minute_hand_length), (-10, int(HEIGHT / 16)), (10, int(HEIGHT / 16)), (5, -minute_hand_length))
vector.rotate(minute_hand, angle_minute, 0, 0)
vector.translate(minute_hand, int(WIDTH / 2), int(HEIGHT / 2) + 5)

angle_hour = (hour % 12) * 30
angle_hour += minute / 2
hour_hand_length = int(HEIGHT / 2) - int(HEIGHT / 8)
hour_hand = Polygon((-5, -hour_hand_length), (-10, int(HEIGHT / 16)), (10, int(HEIGHT / 16)), (5, -hour_hand_length))
vector.rotate(hour_hand, angle_hour, 0, 0)
vector.translate(hour_hand, int(WIDTH / 2), int(HEIGHT / 2) + 5)

display.set_pen(GREY)

vector.draw(minute_hand)
vector.draw(hour_hand)
vector.draw(second_hand)

display.set_pen(BLACK)

for a in range(60):
tick_mark = Rectangle(int(WIDTH / 2) - 3, 10, 6, int(HEIGHT / 48))
vector.rotate(tick_mark, 360 / 60.0 * a, int(WIDTH / 2), int(HEIGHT / 2))
vector.draw(tick_mark)

for a in range(12):
hour_mark = Rectangle(int(WIDTH / 2) - 5, 10, 10, int(HEIGHT / 10))
vector.rotate(hour_mark, 360 / 12.0 * a, int(WIDTH / 2), int(HEIGHT / 2))
vector.draw(hour_mark)

vector.translate(minute_hand, 0, -5)
vector.translate(hour_hand, 0, -5)
vector.draw(minute_hand)
vector.draw(hour_hand)

display.set_pen(RED)
vector.translate(second_hand, 0, -5)
vector.draw(second_hand)
vector.draw(hub)

presto.update(display)
fade_in()
gc.collect()

t_end = time.ticks_ms()
print(f"Took {t_end - t_start}ms")
10 changes: 8 additions & 2 deletions modules/c/presto/presto.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,21 @@

MP_DEFINE_CONST_FUN_OBJ_1(Presto___del___obj, Presto___del__);
MP_DEFINE_CONST_FUN_OBJ_2(Presto_update_obj, Presto_update);
MP_DEFINE_CONST_FUN_OBJ_KW(Presto_partial_update_obj, 5, Presto_partial_update);
MP_DEFINE_CONST_FUN_OBJ_2(Presto_set_backlight_obj, Presto_set_backlight);

/***** Binding of Methods *****/

static const mp_rom_map_elem_t Presto_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&Presto___del___obj) },
{ MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&Presto_update_obj) },
{ MP_ROM_QSTR(MP_QSTR_partial_update), MP_ROM_PTR(&Presto_partial_update_obj) },
{ MP_ROM_QSTR(MP_QSTR_set_backlight), MP_ROM_PTR(&Presto_set_backlight_obj) },

{ MP_ROM_QSTR(MP_QSTR_WIDTH), MP_ROM_INT(WIDTH) },
{ MP_ROM_QSTR(MP_QSTR_HEIGHT), MP_ROM_INT(HEIGHT) },
{ MP_ROM_QSTR(MP_QSTR_WIDTH), MP_ROM_INT(WIDTH/2) },
{ MP_ROM_QSTR(MP_QSTR_HEIGHT), MP_ROM_INT(HEIGHT/2) },
{ MP_ROM_QSTR(MP_QSTR_FULL_WIDTH), MP_ROM_INT(WIDTH) },
{ MP_ROM_QSTR(MP_QSTR_FULL_HEIGHT), MP_ROM_INT(HEIGHT) },
};

static MP_DEFINE_CONST_DICT(Presto_locals_dict, Presto_locals_dict_table);
Expand Down
Loading
Loading