1

I try to implement TFT control using PIO. I have written 4 PIO state machines for every sync signal of my TFT (CLOCK, DE, HSYNC, VSYNC). 3 of them work seamless, but if I add the VSYNC module, the whole Pico freezes. It doesn't change pins and doesn't blink with a LED using repeating timer. Here is how my initialization looks like:

PIO pio = pio0;

uint h_offset = pio_add_program(pio, &hsync_program);
uint v_offset = pio_add_program(pio, &vsync_program);
uint c_offset = pio_add_program(pio, &clock_program);
uint d_offset = pio_add_program(pio, &de_program);

hsync_program_init(pio, 0, h_offset, HSYNC_PIN);
vsync_program_init(pio, 1, v_offset, VSYNC_PIN);
clock_program_init(pio, 2, c_offset, CLOCK_PIN);
de_program_init(pio, 3, d_offset, DE_PIN);

pio_sm_set_enabled(pio, 0, true);
pio_sm_set_enabled(pio, 1, true);
pio_sm_set_enabled(pio, 2, true);
pio_sm_set_enabled(pio, 3, true);
pio_sm_put_blocking(pio, 0, TFT_WIDTH);
pio_sm_put_blocking(pio, 1, TFT_HEIGHT);

Here is the content of the vsync.pio:

.define v_back_porch  12
.define v_front_porch 8
.define v_sync_len    4

.program vsync
    pull block
    mov y, osr
.wrap_target
    set pins, 0
    set x, v_sync_len
vactive:
    wait 1 irq 2
    jmp x-- vactive
; back porch
    set pins, 1
    set x, (v_back_porch - v_sync_len)
 vbporch:
     wait 1 irq 2
     jmp x-- vbporch
; main cycle
    mov x, y
vmain:
    wait 1 irq 2
    jmp x-- vmain
    set x, v_front_porch
vfporch:
    wait 1 irq 2
    jmp x-- vfporch
; set sync interrupt for RGB
;    irq 3
.wrap             ; sync forever!

% c-sdk {
// this is a raw helper function for use by the user which sets up the GPIO output, and configures the SM to output on a particular pin

void vsync_program_init(PIO pio, uint sm, uint offset, uint pin) {
   pio_gpio_init(pio, pin);
   pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);
   pio_sm_config c = vsync_program_get_default_config(offset);
   sm_config_set_set_pins(&c, pin, 1);
   pio_sm_init(pio, sm, offset, &c);
}
%}

If I remove every line of code for the vsync state machine, everything works and generates exactly that, what I want. After adding uint v_offset = pio_add_program(pio, &vsync_program); it doesn't work anymore. There are no errors or comments during the compilation. I have already tried almost everything. It seems, that registers x and y are faulty, but I can't make a counter without using them. I have pretty the same code in the hsync.pio, but I don't have any problems with it. Here is a compiled result for the vsync.pio:

static const uint16_t vsync_program_instructions[] = {
    0x80a0, //  0: pull   block                      
    0xa047, //  1: mov    y, osr                     
            //     .wrap_target
    0xe000, //  2: set    pins, 0                    
    0xe024, //  3: set    x, 4                       
    0x20c2, //  4: wait   1 irq, 2                   
    0x0044, //  5: jmp    x--, 4                     
    0xe001, //  6: set    pins, 1                    
    0xe028, //  7: set    x, 8                       
    0x20c2, //  8: wait   1 irq, 2                   
    0x0048, //  9: jmp    x--, 8                     
    0xa022, // 10: mov    x, y                       
    0x20c2, // 11: wait   1 irq, 2                   
    0x004b, // 12: jmp    x--, 11                    
    0xe028, // 13: set    x, 8                       
    0x20c2, // 14: wait   1 irq, 2                   
    0x004e, // 15: jmp    x--, 14                    
            //     .wrap
};

and for the hsync.pio to compare:

static const uint16_t hsync_program_instructions[] = {
    0x80a0, //  0: pull   block                      
    0xa047, //  1: mov    y, osr                     
            //     .wrap_target
    0xe000, //  2: set    pins, 0                    
    0xe022, //  3: set    x, 2                       
    0x20c0, //  4: wait   1 irq, 0                   
    0x0044, //  5: jmp    x--, 4                     
    0xe001, //  6: set    pins, 1                    
    0xe022, //  7: set    x, 2                       
    0x20c0, //  8: wait   1 irq, 0                   
    0x0048, //  9: jmp    x--, 8                     
    0xc001, // 10: irq    nowait 1                   
    0xa022, // 11: mov    x, y                       
    0x20c0, // 12: wait   1 irq, 0                   
    0x004c, // 13: jmp    x--, 12                    
    0xc001, // 14: irq    nowait 1                   
    0xe024, // 15: set    x, 4                       
    0x20c0, // 16: wait   1 irq, 0                   
    0x0050, // 17: jmp    x--, 16                    
    0xc002, // 18: irq    nowait 2                   
            //     .wrap
};

I don't see any significant differences there. What could be the reasong for such a behavior?

  • I have investigated more and found out, that the size of the vsync.pio is to blame. It works with 6 commands and stop to work with 7 commands. It's far away from 32 commands for every state machine from the Pico datasheet. It's exactly 31 commands for ALL state machines (18+3+4+6) What is the reason? – Alex Kiselev Jun 21 '22 at 14:49

1 Answers1

3

It's very pitty, but every PIO instance (not every state machine in the instance!) allows 32 instructions only. So I have to use PIO1, but there is another problem - IRQs from one PIO don't interact with another PIO.

The structure of the Pico PIO makes me really sad :(