硬汉嵌入式论坛

 找回密码
 立即注册
查看: 1214|回复: 4
收起左侧

一个 可编程IO 例程 Pico-DMX

[复制链接]

31

主题

208

回帖

301

积分

高级会员

积分
301
发表于 2022-2-22 11:30:53 | 显示全部楼层 |阅读模式
Pico-DMX 可编程IO  库 能  同步输出 8 个域  DMX512  信号,效果 堪比 高档 MCU 使用 8 串口 DMA 发送。
https://www.armbbs.cn/forum.php? ... &extra=page%3D1
-----------------------------------------------------------------------------------------------------------------------------
;program DmxInput.pio

; Author: Jostein Løwer, github: jostlowe
; SPDX-License-Identifier: BSD-3-Clause
;
; PIO program for inputting the DMX lighting protocol.
; (Almost) compliant with ANSI E1.11-2008 (R2018)
; The program assumes a PIO clock frequency of exactly 1MHz

.program DmxInput
.define dmx_bit 4                     ; As DMX has a baudrate of 250.000kBaud, a single bit is 4us

break_reset:
    set x, 29                         ; Setup a counter to count the iterations on break_loop

break_loop:                           ; Break loop lasts for 8us. The entire break must be minimum 30*3us = 90us
    jmp pin break_reset               ; Go back to start if pin goes high during the break
    jmp x-- break_loop   [1]          ; Decrease the counter and go back to break loop if x>0 so that the break is not done
    wait 1 pin 0                      ; Stall until line goes high for the Mark-After-Break (MAB)

.wrap_target
    wait 0 pin 0                      ; Stall until start bit is asserted
    set x, 7             [dmx_bit]    ; Preload bit counter, then delay until halfway through

bitloop:
    in pins, 1                        ; Shift data bit into ISR
    jmp x-- bitloop      [dmx_bit-2]  ; Loop 8 times, each loop iteration is 4us
    wait 1 pin 0                      ; Wait for pin to go high for stop bits
    in NULL, 24                       ; Push 24 more bits into the ISR so that our one byte is at the position where the DMA expects it
    push                              ; Should probably do error checking on the stop bits some time in the future....

.wrap
-----------------------------------------------------------------------------------------------------------------------------
;program DmxOutput.pio
; Author: Jostein Løwer, github: jostlowe
; SPDX-License-Identifier: BSD-3-Clause
;
; PIO program for outputting the DMX lighting protocol.
; Compliant with ANSI E1.11-2008 (R2018)
; The program assumes a PIO clock frequency of exactly 1MHz

.program DmxOutput
.side_set 1 opt


; Assert break condition
    set x, 21   side 0     ; Preload bit counter, assert break condition for 176us
breakloop:                 ; This loop will run 22 times
    jmp x-- breakloop [7]  ; Each loop iteration is 8 cycles.


; Assert start condition
    nop [7]    side 1      ; Assert MAB. 8 cycles nop and 8 cycles stop-bits = 16us


; Send data frame
.wrap_target
    pull       side 1 [7]  ; Assert 2 stop bits, or stall with line in idle state
    set x, 7   side 0 [3]  ; Preload bit counter, assert start bit for 4 clocks
bitloop:                   ; This loop will run 8 times (8n1 UART)
    out pins, 1            ; Shift 1 bit from OSR to the first OUT pin
    jmp x-- bitloop   [2]  ; Each loop iteration is 4 cycles.
.wrap

-----------------------------------------------------------------------------------------------------------------------------




Pico-DMX.zip

196.72 KB, 下载次数: 13

可编程IO 例程

回复

使用道具 举报

31

主题

208

回帖

301

积分

高级会员

积分
301
 楼主| 发表于 2022-2-22 11:31:03 | 显示全部楼层
/*
* Copyright (c) 2021 Jostein Løwer
*
* SPDX-License-Identifier: BSD-3-Clause
*/

#include "DmxInput.h"
#include "DmxInput.pio.h"
#ifdef ARDUINO
#include <clocks.h>
#include <irq.h>
#include <Arduino.h> // REMOVE ME
#else
#include "pico/time.h"
#include "hardware/clocks.h"
#include "hardware/irq.h"
#endif

bool prgm_loaded[] = {false,false};
volatile uint prgm_offsets[] = {0,0};
/*
This array tells the interrupt handler which instance has interrupted.
The interrupt handler has only the ints0 register to go on, so this array needs as many spots as there are DMA channels.
*/
#define NUM_DMA_CHANS 12
volatile DmxInput *active_inputs[NUM_DMA_CHANS] = {nullptr};

DmxInput::return_code DmxInput::begin(uint pin, uint start_channel, uint num_channels, PIO pio)
{
    uint pio_ind = pio_get_index(pio);
    if(!prgm_loaded[pio_ind]) {
        /*
        Attempt to load the DMX PIO assembly program into the PIO program memory
        */

        if (!pio_can_add_program(pio, &DmxInput_program))
        {
            return ERR_INSUFFICIENT_PRGM_MEM;
        }
        prgm_offsets[pio_ind] = pio_add_program(pio, &DmxInput_program);
        prgm_loaded[pio_ind] = true;
    }

    /*
    Attempt to claim an unused State Machine into the PIO program memory
    */

    int sm = pio_claim_unused_sm(pio, false);
    if (sm == -1)
    {
        return ERR_NO_SM_AVAILABLE;
    }

    // Set this pin's GPIO function (connect PIO to the pad)
    pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, false);
    pio_gpio_init(pio, pin);
    gpio_pull_up(pin);

    // Generate the default PIO state machine config provided by pioasm
    pio_sm_config sm_conf = DmxInput_program_get_default_config(prgm_offsets[pio_ind]);
    sm_config_set_in_pins(&sm_conf, pin); // for WAIT, IN
    sm_config_set_jmp_pin(&sm_conf, pin); // for JMP

    // Setup the side-set pins for the PIO state machine
    // Shift to right, autopush disabled
    sm_config_set_in_shift(&sm_conf, true, false, 8);
    // Deeper FIFO as we're not doing any TX
    sm_config_set_fifo_join(&sm_conf, PIO_FIFO_JOIN_RX);

    // Setup the clock divider to run the state machine at exactly 1MHz
    uint clk_div = clock_get_hz(clk_sys) / DMX_SM_FREQ;
    sm_config_set_clkdiv(&sm_conf, clk_div);

    // Load our configuration, jump to the start of the program and run the State Machine
    pio_sm_init(pio, sm, prgm_offsets[pio_ind], &sm_conf);
    //sm_config_set_in_shift(&c, true, false, n_bits)

    //pio_sm_put_blocking(pio, sm, (start_channel + num_channels) - 1);

    _pio = pio;
    _sm = sm;
    _pin = pin;
    _start_channel = start_channel;
    _num_channels = num_channels;
    _buf = nullptr;
    _cb = nullptr;

    _dma_chan = dma_claim_unused_channel(true);


    if (active_inputs[_dma_chan] != nullptr) {
        return ERR_NO_SM_AVAILABLE;
    }
    active_inputs[_dma_chan] = this;

    return SUCCESS;
}

void DmxInput::read(volatile uint8_t *buffer)
{
    if(_buf==nullptr) {
        read_async(buffer);
    }
    unsigned long start = _last_packet_timestamp;
    while(_last_packet_timestamp == start) {
        tight_loop_contents();
    }
}

void dmxinput_dma_handler() {
    for(int i=0;i<NUM_DMA_CHANS;i++) {
        if(active_inputs[i]!=nullptr && (dma_hw->ints0 & (1u<<i))) {
            dma_hw->ints0 = 1u << i;
            volatile DmxInput *instance = active_inputs[i];
            dma_channel_set_write_addr(i, instance->_buf, true);
            pio_sm_exec(instance->_pio, instance->_sm, pio_encode_jmp(prgm_offsets[pio_get_index(instance->_pio)]));
            pio_sm_clear_fifos(instance->_pio, instance->_sm);
#ifdef ARDUINO
            instance->_last_packet_timestamp = millis();
#else
            instance->_last_packet_timestamp = to_ms_since_boot(get_absolute_time());
#endif
            // Trigger the callback if we have one
            if (instance->_cb != nullptr) {
                (*(instance->_cb))((DmxInput*)instance);
            }
        }
    }
}

void DmxInput::read_async(volatile uint8_t *buffer, void (*inputUpdatedCallback)(DmxInput*)) {

    _buf = buffer;
    if (inputUpdatedCallback!=nullptr) {
        _cb = inputUpdatedCallback;
    }

    pio_sm_set_enabled(_pio, _sm, false);

    // Reset the PIO state machine to a consistent state. Clear the buffers and registers
    pio_sm_restart(_pio, _sm);

    //setup dma
    dma_channel_config cfg = dma_channel_get_default_config(_dma_chan);

    // Reading from constant address, writing to incrementing byte addresses
    channel_config_set_transfer_data_size(&cfg, DMA_SIZE_8);
    channel_config_set_read_increment(&cfg, false);
    channel_config_set_write_increment(&cfg, true);

    // Pace transfers based on DREQ_PIO0_RX0 (or whichever pio and sm we are using)
    channel_config_set_dreq(&cfg, pio_get_dreq(_pio, _sm, false));

    //channel_config_set_ring(&cfg, true, 5);
    dma_channel_configure(
        _dma_chan,
        &cfg,
        NULL,    // dst
        &_pio->rxf[_sm],  // src
        DMXINPUT_BUFFER_SIZE(_start_channel, _num_channels),  // transfer count,
        false
    );

    dma_channel_set_irq0_enabled(_dma_chan, true);
    irq_set_exclusive_handler(DMA_IRQ_0, dmxinput_dma_handler);
    irq_set_enabled(DMA_IRQ_0, true);

    //aaand start!
    dma_channel_set_write_addr(_dma_chan, buffer, true);
    pio_sm_exec(_pio, _sm, pio_encode_jmp(prgm_offsets[pio_get_index(_pio)]));
    pio_sm_clear_fifos(_pio, _sm);
#ifdef ARDUINO
    _last_packet_timestamp = millis();
#else
    _last_packet_timestamp = to_ms_since_boot(get_absolute_time());
#endif

    pio_sm_set_enabled(_pio, _sm, true);
}

unsigned long DmxInput::latest_packet_timestamp() {
    return _last_packet_timestamp;
}

uint DmxInput::pin() {
    return _pin;
}

void DmxInput::end()
{
    // Stop the PIO state machine
    pio_sm_set_enabled(_pio, _sm, false);

    // Remove the PIO DMX program from the PIO program memory
    uint pio_id = pio_get_index(_pio);
    bool inuse = false;
    for(uint i=0;i<NUM_DMA_CHANS;i++) {
        if(i==_dma_chan) {
            continue;
        }
        if(pio_id == pio_get_index(active_inputs[i]->_pio)) {
            inuse = true;
            break;
        }
    }
    if(!inuse) {
        prgm_loaded[pio_id] = false;
        pio_remove_program(_pio, &DmxInput_program, prgm_offsets[pio_id]);
        prgm_offsets[pio_id]=0;
    }

    // Unclaim the sm
    pio_sm_unclaim(_pio, _sm);

    dma_channel_unclaim(_dma_chan);
    active_inputs[_dma_chan] = nullptr;

    _buf = nullptr;
}
回复

使用道具 举报

31

主题

208

回帖

301

积分

高级会员

积分
301
 楼主| 发表于 2022-2-22 11:31:29 | 显示全部楼层
/*
* Copyright (c) 2021 Jostein L&#248;wer
*
* SPDX-License-Identifier: BSD-3-Clause
*/

#include "DmxOutput.h"
#include "DmxOutput.pio.h"
#ifdef ARDUINO
#include <clocks.h>
#include <irq.h>
#else
#include "hardware/clocks.h"
#include "hardware/irq.h"
#endif

DmxOutput::return_code DmxOutput::begin(uint pin, PIO pio)
{
    /*
    Attempt to load the DMX PIO assembly program
    into the PIO program memory
    */

    if (!pio_can_add_program(pio, &DmxOutput_program))
    {
        return ERR_INSUFFICIENT_PRGM_MEM;
    }
    uint prgm_offset = pio_add_program(pio, &DmxOutput_program);

    /*
    Attempt to claim an unused State Machine
    into the PIO program memory
    */

    int sm = pio_claim_unused_sm(pio, false);
    if (sm == -1)
    {
        return ERR_NO_SM_AVAILABLE;
    }

    // Set this pin's GPIO function (connect PIO to the pad)
    pio_sm_set_pins_with_mask(pio, sm, 1u << pin, 1u << pin);
    pio_sm_set_pindirs_with_mask(pio, sm, 1u << pin, 1u << pin);
    pio_gpio_init(pio, pin);

    // Generate the default PIO state machine config provided by pioasm
    pio_sm_config sm_conf = DmxOutput_program_get_default_config(prgm_offset);

    // Setup the side-set pins for the PIO state machine
    sm_config_set_out_pins(&sm_conf, pin, 1);
    sm_config_set_sideset_pins(&sm_conf, pin);

    // Setup the clock divider to run the state machine at exactly 1MHz
    uint clk_div = clock_get_hz(clk_sys) / DMX_SM_FREQ;
    sm_config_set_clkdiv(&sm_conf, clk_div);

    // Load our configuration, jump to the start of the program and run the State Machine
    pio_sm_init(pio, sm, prgm_offset, &sm_conf);
    pio_sm_set_enabled(pio, sm, true);

    // Claim an unused DMA channel.
    // The channel is kept througout the lifetime of the DMX source
    int dma = dma_claim_unused_channel(false);

    if (dma == -1)
        return ERR_NO_DMA_AVAILABLE;

    // Get the default DMA config for our claimed channel
    dma_channel_config dma_conf = dma_channel_get_default_config(dma);

    // Set the DMA to move one byte per DREQ signal
    channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_8);

    // Setup the DREQ so that the DMA only moves data when there
    // is available room in the TXF buffer of our PIO state machine
    channel_config_set_dreq(&dma_conf, pio_get_dreq(pio, sm, true));

    // Setup the DMA to write to the TXF buffer of the PIO state machine
    dma_channel_set_write_addr(dma, &pio->txf[sm], false);

    // Apply the config
    dma_channel_set_config(dma, &dma_conf, false);

    // Set member values of C++ class
    _prgm_offset = prgm_offset;
    _pio = pio;
    _sm = sm;
    _pin = pin;
    _dma = dma;

    return SUCCESS;
}

void DmxOutput::write(uint8_t *universe, uint length)
{

    // Temporarily disable the PIO state machine
    pio_sm_set_enabled(_pio, _sm, false);

    // Reset the PIO state machine to a consistent state. Clear the buffers and registers
    pio_sm_restart(_pio, _sm);

    // Start the DMX PIO program from the beginning
    pio_sm_exec(_pio, _sm, pio_encode_jmp(_prgm_offset));

    // Restart the PIO state machinge
    pio_sm_set_enabled(_pio, _sm, true);

    // Start the DMA transfer
    dma_channel_transfer_from_buffer_now(_dma, universe, length);
}

bool DmxOutput::busy()
{
    if (dma_channel_is_busy(_dma))
        return true;

    return !pio_sm_is_tx_fifo_empty(_pio, _sm);
}

/*
void Dmx::await()
{
    dma_channel_wait_for_finish_blocking(_dma);

    while (!pio_sm_is_tx_fifo_empty(_pio, _sm))
    {
    }
}
*/

void DmxOutput::end()
{
    // Stop the PIO state machine
    pio_sm_set_enabled(_pio, _sm, false);

    // Remove the PIO DMX program from the PIO program memory
    pio_remove_program(_pio, &DmxOutput_program, _prgm_offset);

    // Unclaim the DMA channel
    dma_channel_unclaim(_dma);

    // Unclaim the sm
    pio_sm_unclaim(_pio, _sm);
}
回复

使用道具 举报

31

主题

208

回帖

301

积分

高级会员

积分
301
 楼主| 发表于 2022-2-22 11:33:08 | 显示全部楼层
本帖最后由 Hp_2018 于 2022-2-22 11:41 编辑

Pico-DMX  是  Arduino 一个库。一个很完善的例程 有 PIO  还有 DMA !
PIO   C++  源码 有望能移植到  MDK!
源码
https://github.com/jostlowe/Pico-DMX


回复

使用道具 举报

31

主题

208

回帖

301

积分

高级会员

积分
301
 楼主| 发表于 2022-2-22 11:39:08 | 显示全部楼层
        if (!pio_can_add_program(pio, &DmxInput_program))
        {
            return ERR_INSUFFICIENT_PRGM_MEM;
        }
        prgm_offsets[pio_ind] = pio_add_program(pio, &DmxInput_program);
        prgm_loaded[pio_ind] = true;
    }

这算是动态加载 DmxInput_program.PIO 程序 吗?

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2024-5-14 16:45 , Processed in 0.172866 second(s), 26 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表