6

RP2040(树莓派Pico) PIO – 实例分析&编程

 3 years ago
source link: https://www.taterli.com/7580/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

RP2040(树莓派Pico) PIO – 实例分析&编程

  • TaterLi
  • 2021年2月4日2021年2月4日

这次拿来开刀的是WS2812,具体代码可见 => https://github.com/raspberrypi/pico-examples/blob/master/pio/ws2812/ws2812.pio

.program ws2812
.side_set 1

.define public T1 2
.define public T2 5
.define public T3 3

.lang_opt python sideset_init = pico.PIO.OUT_HIGH
.lang_opt python out_init     = pico.PIO.OUT_HIGH
.lang_opt python out_shiftdir = 1


.wrap_target
bitloop:
    out x, 1       side 0 [T3 - 1] ; 先从移位寄存器取出1个bit到x,并且数据引脚为0,保持T3个周期.
    jmp !x do_zero side 1 [T1 - 1] ; 如果取出的这个bit是0,则跳转到设置0,跳转前保持1状态T1个周期.
do_one:
    jmp  bitloop   side 1 [T2 - 1] ; 如果上面没跳转就会到这里设置1,保持高T2个周期.
do_zero:
    nop            side 0 [T2 - 1] ; 如果跳转了,就保持T2个周期的低.
.wrap

其中wrap_target到wrap包裹的部分,是整个函数的实体,假设PUSH进去的是0xAA(10101010b),其实际时序就会如下,记住命令本身也占一个周期:

[T3T1T2]…. => [0001111111][0001100000]……..

注意他实际上被捕获到是:

(LLL)[HHHHHHHLLL][HHLLLLLLLL]….

这样只要每周期是120ns,就可以满足时许的要求(似乎有些超出),发送0要求([200 ~ 500]ns + [650 ~ 950]ns),发送1要求([550 ~ 850]ns + [450 ~ 750]ns).

(LLL)[840ns H + 360 ns L][240 ns H + 960 ns L]

由于一次传输32B,所以最后的8B就没有意义了,要连续传输必须用其他方法.

官方这个程序是简洁,但是不够完美,时许吻合度不够,而且一旦空闲就会复位,我决定重写一个我自己的版本,虽然没那么精简,就开始试试.

我期望的目标还是120ns一个周期,但是0编码成[HHHLLLLLL],1编码成[HHHHHHLLLLL]

.wrap_target
bitloop:
    out x, 1       side 0 [3] ; Side-set still takes place when instruction stalls
    jmp !x do_zero side 1 [2] ; Branch on the bit we shifted out. Positive pulse
do_one:
    nop            side 1 [2] ; Continue driving high, for a long pulse
    jmp  bitloop   side 0 ; Padding zero
do_zero:
    nop            side 0 [1] ; Or drive low, for a short pulse
.wrap

测试结果:

程序中除了汇编部分,还有入口部分.

static inline void ws2812_program_init(PIO pio, uint sm, uint offset, uint pin, float freq, bool rgbw) {

    pio_gpio_init(pio, pin);
    pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);

    pio_sm_config c = ws2812_program_get_default_config(offset);
    sm_config_set_sideset_pins(&c, pin);
    sm_config_set_out_shift(&c, false, true, rgbw ? 32 : 24);
    sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);

    int cycles_per_bit = 11;
    float div = clock_get_hz(clk_sys) / (freq * cycles_per_bit);
    sm_config_set_clkdiv(&c, div);

    pio_sm_init(pio, sm, offset, &c);
    pio_sm_set_enabled(pio, sm, true);
}

就刚才我们硬编码了Timing,所以这里也要做相应修改,比如cycles_per_bit应该改成11,这样才能刚好满足,这里还演示了如何设置sideset引脚,在主程序中还用pio_sm_put_blocking写入移位寄存器.

到此为止,就掌握了PIO的基本使用,总体来说,因为编程语言因素,还是有点吃力的,但是由于时间确定性和自由度,这一定会成为一个Magic外设.

发表评论 取消回复

邮箱地址不会被公开。 必填项已用*标注

评论

名称 *

电子邮件 *

站点


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK