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

Drivers: Bit reverse in PIO, use new RGB565 pinout. #3

Merged
merged 1 commit into from
Oct 10, 2024
Merged
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
82 changes: 38 additions & 44 deletions drivers/st7701/st7701.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,18 +91,6 @@ namespace pimoroni {

static ST7701* st7701_inst;

uint pio_get_irq_num(PIO pio, uint irq_num) {
/*
#define PIO0_IRQ_0 15
#define PIO0_IRQ_1 16
#define PIO1_IRQ_0 17
#define PIO1_IRQ_1 18
#define PIO2_IRQ_0 19
#define PIO2_IRQ_1 20
*/
return pio_get_index(pio) * 2 + PIO0_IRQ_0 + irq_num;
}

// This ISR is triggered whenever the timing SM's FIFO is not full
void __no_inline_not_in_flash_func(timing_isr)() {
st7701_inst->drive_timing();
Expand All @@ -115,15 +103,15 @@ void __no_inline_not_in_flash_func(ST7701::drive_timing)()
switch (timing_phase) {
case 0:
// Front Porch
instr = 0x4000A042u; // HSYNC high, NOP
instr = 0x4000B042u; // HSYNC high, NOP
if (timing_row >= TIMING_V_PULSE) instr |= 0x80000000u; // VSYNC high if not in VSYNC pulse
instr |= (TIMING_H_FRONT - 3) << 16;
pio_sm_put(st_pio, timing_sm, instr);
break;

case 1:
// HSYNC
instr = 0x0000A042u; // HSYNC low, NOP
instr = 0x0000B042u; // HSYNC low, NOP
if (timing_row >= TIMING_V_PULSE) instr |= 0x80000000u; // VSYNC high if not in VSYNC pulse
instr |= (TIMING_H_PULSE - 3) << 16;
pio_sm_put(st_pio, timing_sm, instr);
Expand All @@ -133,8 +121,8 @@ void __no_inline_not_in_flash_func(ST7701::drive_timing)()
// Back Porch, trigger pixel channels if in display window
instr = 0x40000000u; // HSYNC high
if (timing_row >= TIMING_V_PULSE) instr |= 0x80000000u; // VSYNC high if not in VSYNC pulse
if (timing_row >= TIMING_V_BACK && timing_row < TIMING_V_DISPLAY) instr |= 0xC004u; // IRQ 4, triggers the data SM
else instr |= 0xA042u; // NOP
if (timing_row >= TIMING_V_BACK && timing_row < TIMING_V_DISPLAY) instr |= 0xD004u; // IRQ 4, triggers the data SM
else instr |= 0xB042u; // NOP
instr |= (TIMING_H_BACK - 3) << 16;
pio_sm_put(st_pio, timing_sm, instr);
//printf(".\n");
Expand All @@ -143,9 +131,9 @@ void __no_inline_not_in_flash_func(ST7701::drive_timing)()
case 3:
// Display, trigger next frame at frame end
instr = 0x40000000u; // HSYNC high
if (timing_row == TIMING_V_DISPLAY) instr |= 0xC001u; // irq 1, to trigger queueing DMA for a new frame
else if (timing_row >= TIMING_V_BACK - 1 && timing_row < TIMING_V_DISPLAY) instr |= 0xC000u; // irq 0, to trigger queueing DMA for a new line
else instr |= 0xA042u; // NOP
if (timing_row == TIMING_V_DISPLAY) instr |= 0xD001u; // irq 1, to trigger queueing DMA for a new frame
else if (timing_row >= TIMING_V_BACK - 1 && timing_row < TIMING_V_DISPLAY) instr |= 0xD000u; // irq 0, to trigger queueing DMA for a new line
else instr |= 0xB042u; // NOP
if (timing_row >= TIMING_V_PULSE) instr |= 0x80000000u; // VSYNC high if not in VSYNC pulse
instr |= (TIMING_H_DISPLAY - 3) << 16;
pio_sm_put(st_pio, timing_sm, instr);
Expand All @@ -172,12 +160,14 @@ void __no_inline_not_in_flash_func(ST7701::handle_end_of_line())
void __no_inline_not_in_flash_func(ST7701::start_line_xfer())
{
hw_clear_bits(&st_pio->irq, 0x1);

dma_channel_abort(st_dma);
dma_channel_wait_for_finish_blocking(st_dma);
pio_sm_set_enabled(st_pio, parallel_sm, false);
pio_sm_clear_fifos(st_pio, parallel_sm);
pio_sm_exec_wait_blocking(st_pio, parallel_sm, pio_encode_jmp(parallel_offset));
pio_sm_exec_wait_blocking(st_pio, parallel_sm, pio_encode_mov(pio_osr, pio_null));
pio_sm_exec_wait_blocking(st_pio, parallel_sm, pio_encode_out(pio_null, 32));
pio_sm_exec_wait_blocking(st_pio, parallel_sm, pio_encode_jmp(parallel_offset));
pio_sm_set_enabled(st_pio, parallel_sm, true);

if ((intptr_t)framebuffer >= 0x20000000) {
Expand Down Expand Up @@ -292,22 +282,27 @@ void __no_inline_not_in_flash_func(ST7701::fill_next_line()) {
pio_gpio_init(st_pio, lcd_de);
pio_gpio_init(st_pio, lcd_dot_clk);

for(auto i = 0u; i < 18; i++) {
//gpio_set_function(d0 + i, GPIO_FUNC_SIO);
//gpio_set_dir(d0 + i, GPIO_OUT);
//gpio_init(d0 + 0); gpio_set_dir(d0 + i, GPIO_OUT);
for(auto i = 0u; i < 16; i++) {
pio_gpio_init(st_pio, d0 + i);
}
for(auto i = 16u; i < 18; i++) {
gpio_init(d0 + i);
gpio_set_dir(d0 + i, GPIO_OUT);
gpio_put(d0 + i, false);
}

pio_sm_set_consecutive_pindirs(st_pio, parallel_sm, d0, 18, true);
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 = st7701_parallel_program_get_default_config(parallel_offset);
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);

sm_config_set_out_pins(&c, d0, 18);
sm_config_set_out_pins(&c, d0, 16);
sm_config_set_sideset_pins(&c, lcd_de);
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
sm_config_set_out_shift(&c, true, true, 32);
sm_config_set_in_shift(&c, false, false, 32);

// Determine clock divider
uint32_t max_pio_clk;
Expand All @@ -325,7 +320,7 @@ void __no_inline_not_in_flash_func(ST7701::fill_next_line()) {

pio_sm_init(st_pio, parallel_sm, parallel_offset, &c);
pio_sm_exec(st_pio, parallel_sm, pio_encode_out(pio_y, 32));
pio_sm_put(st_pio, parallel_sm, width-1);
pio_sm_put(st_pio, parallel_sm, (width >> 1) - 1);
pio_sm_set_enabled(st_pio, parallel_sm, true);

c = st7701_timing_program_get_default_config(timing_offset);
Expand All @@ -344,6 +339,7 @@ void __no_inline_not_in_flash_func(ST7701::fill_next_line()) {
channel_config_set_transfer_data_size(&config, DMA_SIZE_32);
channel_config_set_high_priority(&config, true);
channel_config_set_dreq(&config, pio_get_dreq(st_pio, parallel_sm, true));
channel_config_set_bswap(&config, true);
dma_channel_configure(st_dma, &config, &st_pio->txf[parallel_sm], line_buffer, width >> 1, false);

hw_set_bits(&bus_ctrl_hw->priority, (BUSCTRL_BUS_PRIORITY_PROC1_BITS | BUSCTRL_BUS_PRIORITY_DMA_R_BITS | BUSCTRL_BUS_PRIORITY_DMA_W_BITS));
Expand All @@ -362,19 +358,17 @@ void __no_inline_not_in_flash_func(ST7701::fill_next_line()) {
// Setup timing
hw_set_bits(&st_pio->inte1, 0x010 << timing_sm); // TX not full
// Remove the MicroPython handler if it's set
current = irq_get_exclusive_handler(PIO2_IRQ_1);
if(current) irq_remove_handler(PIO2_IRQ_1, current);
irq_set_exclusive_handler(PIO2_IRQ_1, timing_isr);
//irq_add_shared_handler(PIO1_IRQ_1, timing_isr, PICO_SHARED_IRQ_HANDLER_HIGHEST_ORDER_PRIORITY);
irq_set_enabled(PIO2_IRQ_1, true);
current = irq_get_exclusive_handler(pio_get_irq_num(st_pio, 1));
if(current) irq_remove_handler(pio_get_irq_num(st_pio, 1), current);
irq_set_exclusive_handler(pio_get_irq_num(st_pio, 1), timing_isr);
irq_set_enabled(pio_get_irq_num(st_pio, 1), true);

hw_set_bits(&st_pio->inte0, 0x300); // IRQ 0
// Remove the MicroPython handler if it's set
current = irq_get_exclusive_handler(PIO2_IRQ_0);
if(current) irq_remove_handler(PIO2_IRQ_0, current);
irq_set_exclusive_handler(PIO2_IRQ_0, end_of_line_isr);
//irq_add_shared_handler(PIO1_IRQ_0, end_of_line_isr, PICO_SHARED_IRQ_HANDLER_HIGHEST_ORDER_PRIORITY);
irq_set_enabled(PIO2_IRQ_0, true);
current = irq_get_exclusive_handler(pio_get_irq_num(st_pio, 0));
if(current) irq_remove_handler(pio_get_irq_num(st_pio, 0), current);
irq_set_exclusive_handler(pio_get_irq_num(st_pio, 0), end_of_line_isr);
irq_set_enabled(pio_get_irq_num(st_pio, 0), true);
}

void ST7701::common_init() {
Expand Down Expand Up @@ -473,13 +467,13 @@ void __no_inline_not_in_flash_func(ST7701::fill_next_line()) {
void ST7701::cleanup() {
irq_handler_t current;

irq_set_enabled(PIO2_IRQ_0, false);
current = irq_get_exclusive_handler(PIO2_IRQ_0);
if(current) irq_remove_handler(PIO2_IRQ_0, current);
irq_set_enabled(pio_get_irq_num(st_pio, 0), false);
current = irq_get_exclusive_handler(pio_get_irq_num(st_pio, 0));
if(current) irq_remove_handler(pio_get_irq_num(st_pio, 0), current);

irq_set_enabled(PIO2_IRQ_1, false);
current = irq_get_exclusive_handler(PIO2_IRQ_1);
if(current) irq_remove_handler(PIO2_IRQ_1, current);
irq_set_enabled(pio_get_irq_num(st_pio, 1), false);
current = irq_get_exclusive_handler(pio_get_irq_num(st_pio, 1));
if(current) irq_remove_handler(pio_get_irq_num(st_pio, 1), current);

irq_set_enabled(LOW_PRIO_IRQ0, false);

Expand Down
29 changes: 16 additions & 13 deletions drivers/st7701/st7701_parallel.pio
Original file line number Diff line number Diff line change
@@ -1,29 +1,32 @@
; Output 18 bit parallel RGB666 data every clock
; Output 16 bit parallel RGB565 data every 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
mov x, y side 0 ; y needs to be set to width-1 at init time
wait 1 irq 4 side 0 ; wait for the irq from the timing SM
nop side 1 [1] ; This reduces the offset of the image on the screen from 4 to 3 pixels
loop: ; Not really sure why the offset is there, enable should only have 5ns setup
out pins, 16 side 1
jmp x-- loop side 1
mov pins, null side 1
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
mov x, y side 0 ; y needs to be set to width-1 at init time
mov x, y side 0 ; y needs to be set to (width/2)-1 at init time
wait 1 irq 4 side 0 ; wait for the irq from the timing SM
nop side 1 [1] ; This reduces the offset of the image on the screen from 4 to 3 pixels
loop: ; Not really sure why the offset is there, enable should only have 5ns setup
out pins, 16 side 1 [2]
loop:
out isr, 32 side 1
mov pins, ::isr side 1 [1]
in null, 16 side 1 [1]
mov pins, ::isr side 1 [1]
jmp x-- loop side 1
mov pins, null side 1
.wrap
.wrap
8 changes: 4 additions & 4 deletions drivers/st7701/st7701_timing.pio
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
; 30: HSync
; 29-16: Delay duration in pixel clocks, total loop duration is delay + 3
; 15-0: Instruction to run after delay, sensible options are:
; nop : 0xa042 ; Do nothing
; irq n : 0xc00n ; Signal to CPU or a data PIO
; nop : 0xb042 ; Do nothing
; irq n : 0xd00n ; Signal to CPU or a data PIO
; Side set is data clock, which runs continuously
.program st7701_timing
.side_set 1
.origin 0

.wrap_target
out pins, 2 side 0 ; Set VS & HS
Expand All @@ -16,5 +17,4 @@ sync_loop:
nop side 0
jmp x--, sync_loop side 1
out exec, 16 side 0
nop side 1
.wrap
.wrap
14 changes: 0 additions & 14 deletions modules/c/presto/presto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,24 +146,10 @@ mp_int_t Presto_get_framebuffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_
return 0;
}

static inline int32_t reverse(uint32_t x) {
x = ((x >> 1) & 0x55555555u) | ((x & 0x55555555u) << 1);
x = ((x >> 2) & 0x33333333u) | ((x & 0x33333333u) << 2);
x = ((x >> 4) & 0x0f0f0f0fu) | ((x & 0x0f0f0f0fu) << 4);
x = ((x << 1) & 0xffc0ffc0) | (x & 0x003f003f); // drop the high B bit and merge
return x;
}

extern mp_obj_t Presto_update(mp_obj_t self_in, mp_obj_t graphics_in) {
_Presto_obj_t *self = MP_OBJ_TO_PTR2(self_in, _Presto_obj_t);
ModPicoGraphics_obj_t *picographics = MP_OBJ_TO_PTR2(graphics_in, ModPicoGraphics_obj_t);

uint32_t *p = (uint32_t *)self->next_fb;
for(uint32_t i = 0; i < 28800; i++) {
*p = reverse(*p);
p++;
}

self->presto->set_framebuffer(self->next_fb);
std::swap(self->next_fb, self->curr_fb);
picographics->graphics->set_framebuffer((void *)self->next_fb);
Expand Down