Skip to content

Commit

Permalink
Drivers: Bit reverse in PIO, use new RGB565 pinout.
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaelBell committed Sep 11, 2024
1 parent 2d9a93d commit bd35057
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 75 deletions.
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

0 comments on commit bd35057

Please sign in to comment.