ftdi code finished-ish, but not working yet
This commit is contained in:
parent
0c10da762a
commit
22e8f70e30
|
@ -51,6 +51,7 @@ if(FAMILY STREQUAL "rp2040")
|
|||
else()
|
||||
#pico_enable_stdio_uart(${PROJECT} 1)
|
||||
endif()
|
||||
# TODO: separate flag for disabling this one?
|
||||
pico_enable_stdio_uart(${PROJECT} 1)
|
||||
pico_enable_stdio_usb(${PROJECT} 0)
|
||||
else()
|
||||
|
@ -96,7 +97,8 @@ target_sources(${PROJECT} PUBLIC
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_default/tempsensor.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_jscan/jscan_hw.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_sump/sump_hw.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_ftdi/base.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_ftdi/ftdi_hw.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_ftdi/ftdi_proto.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_ftdi/uart.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_ftdi/mpsse.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_ftdi/asyncbb.c
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#else
|
||||
#define CFG_TUD_CDC 2
|
||||
#endif
|
||||
#define CFG_TUD_VENDOR 2
|
||||
#define CFG_TUD_VENDOR 3
|
||||
|
||||
/* don't access storage for RAM-only builds */
|
||||
#if !PICO_NO_FLASH
|
||||
|
|
|
@ -0,0 +1,315 @@
|
|||
// vim: set et:
|
||||
|
||||
/* include order matters here */
|
||||
#include <hardware/dma.h>
|
||||
#include <hardware/gpio.h>
|
||||
#include <hardware/irq.h>
|
||||
#include <hardware/pio.h>
|
||||
#include <hardware/structs/dma.h>
|
||||
|
||||
#include "m_ftdi/ftdi_hw.h"
|
||||
|
||||
struct ftdi_hw itf_bsp_data[2];
|
||||
|
||||
static int piosm_to_dreq(int pio, int sm) {
|
||||
return DREQ_PIO0_TX0 + pio * 8 + sm;
|
||||
}
|
||||
|
||||
static void ftdihw_dma_isr();
|
||||
|
||||
void ftdihw_init(struct ftdi_hw* fr, struct ftdi_interface* itf) {
|
||||
memset(fr, 0, sizeof *fr);
|
||||
|
||||
fr->itf = itf;
|
||||
fr->pio = ftdihw_itf_to_pio(itf);
|
||||
fr->pinbase = ftdihw_itf_to_base(itf);
|
||||
|
||||
fr->rx.prg_off = 0xff;
|
||||
fr->rx.piosm = 0xff;
|
||||
fr->rx.dmach = 0xff;
|
||||
fr->tx.prg_off = 0xff;
|
||||
fr->tx.piosm = 0xff;
|
||||
fr->tx.dmach = 0xff;
|
||||
|
||||
// we start with nothing to write out, but we can start filling a buffer already
|
||||
fr->rx.dmabuf_dend = sizeof(fr->dma_in_buf) - 1;
|
||||
|
||||
irq_set_enabled(DMA_IRQ_0 + (fr->itf->index & 1), false);
|
||||
irq_set_exclusive_handler(DMA_IRQ_0 + (fr->itf->index & 1), ftdihw_dma_isr);
|
||||
irq_set_enabled(DMA_IRQ_0 + (fr->itf->index & 1), true);
|
||||
}
|
||||
void ftdihw_deinit(struct ftdi_hw* hw) {
|
||||
irq_set_enabled(DMA_IRQ_0 + (hw->itf->index & 1), false);
|
||||
irq_remove_handler(DMA_IRQ_0 + (hw->itf->index & 1), ftdihw_dma_isr);
|
||||
}
|
||||
|
||||
bool ftdihw_dma_ch_init(struct ftdi_hw_ch* ch, struct ftdi_hw* hw, const void* prg) {
|
||||
int off, sm, dmach;
|
||||
sm = pio_claim_unused_sm(hw->pio, false);
|
||||
if (sm == -1) return false;
|
||||
|
||||
dmach = dma_claim_unused_channel(false);
|
||||
if (dmach == -1) {
|
||||
pio_sm_unclaim(hw->pio, sm);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!pio_can_add_program(hw->pio, prg)) {
|
||||
dma_channel_unclaim(dmach);
|
||||
pio_sm_unclaim(hw->pio, sm);
|
||||
return false;
|
||||
}
|
||||
|
||||
off = pio_add_program(hw->pio, prg);
|
||||
|
||||
ch->prg = prg;
|
||||
ch->prg_off = off;
|
||||
ch->piosm = sm;
|
||||
ch->dmach = dmach;
|
||||
|
||||
return true;
|
||||
}
|
||||
void ftdihw_dma_ch_deinit(struct ftdi_hw_ch* ch, struct ftdi_hw* hw) {
|
||||
dma_channel_unclaim(ch->dmach);
|
||||
pio_sm_set_enabled(hw->pio, ch->piosm, false);
|
||||
pio_sm_unclaim(hw->pio, ch->piosm);
|
||||
pio_remove_program(hw->pio, ch->prg, ch->prg_off);
|
||||
|
||||
ch->dmach = 0xff;
|
||||
ch->piosm = 0xff;
|
||||
ch->prg_off = 0xff;
|
||||
ch->prg = NULL;
|
||||
}
|
||||
|
||||
void ftdihw_dma_rx_setup(struct ftdi_hw* hw, bool start) {
|
||||
dma_irqn_set_channel_enabled(hw->itf->index & 1, hw->rx.dmach, false);
|
||||
|
||||
if (hw->rx.dmabuf_dend == hw->rx.dmabuf_dstart)
|
||||
--hw->rx.dmabuf_dend; // mod 256 automatically
|
||||
|
||||
dma_channel_config dcfg = dma_channel_get_default_config(hw->rx.dmach);
|
||||
channel_config_set_read_increment(&dcfg, false);
|
||||
channel_config_set_write_increment(&dcfg, true);
|
||||
channel_config_set_dreq(&dcfg, piosm_to_dreq(hw->itf->index, hw->rx.piosm));
|
||||
channel_config_set_transfer_data_size(&dcfg, DMA_SIZE_8);
|
||||
channel_config_set_ring(&dcfg, true, 8); // 1<<8 -sized ring buffer on write end
|
||||
dma_channel_configure(hw->rx.dmach, &dcfg,
|
||||
&hw->dma_in_buf[hw->rx.dmabuf_dstart], &hw->pio->rxf[hw->rx.piosm],
|
||||
(hw->rx.dmabuf_dend - hw->rx.dmabuf_dstart) % sizeof(hw->dma_in_buf), start);
|
||||
|
||||
dma_irqn_set_channel_enabled(hw->itf->index & 1, hw->rx.dmach, true);
|
||||
}
|
||||
void ftdihw_dma_rx_stop(struct ftdi_hw* hw) {
|
||||
dma_irqn_set_channel_enabled(hw->itf->index & 1, hw->rx.dmach, false);
|
||||
dma_channel_abort(hw->rx.dmach);
|
||||
}
|
||||
|
||||
void ftdihw_dma_tx_setup(struct ftdi_hw* hw, bool start) {
|
||||
dma_irqn_set_channel_enabled(hw->itf->index & 1, hw->tx.dmach, false);
|
||||
|
||||
if (hw->tx.dmabuf_dend == hw->tx.dmabuf_dstart)
|
||||
--hw->tx.dmabuf_dend; // mod 256 automatically
|
||||
|
||||
dma_channel_config dcfg = dma_channel_get_default_config(hw->tx.dmach);
|
||||
channel_config_set_read_increment(&dcfg, true);
|
||||
channel_config_set_write_increment(&dcfg, false);
|
||||
channel_config_set_dreq(&dcfg, piosm_to_dreq(hw->itf->index, hw->tx.piosm));
|
||||
channel_config_set_transfer_data_size(&dcfg, DMA_SIZE_8);
|
||||
channel_config_set_ring(&dcfg, false, 8); // 1<<8 -sized ring buffer on read end
|
||||
dma_channel_configure(hw->tx.dmach, &dcfg,
|
||||
&hw->pio->txf[hw->tx.piosm], &hw->dma_out_buf[hw->tx.dmabuf_dstart],
|
||||
(hw->tx.dmabuf_dend - hw->tx.dmabuf_dstart) % sizeof(hw->dma_out_buf), start);
|
||||
|
||||
dma_irqn_set_channel_enabled(hw->itf->index & 1, hw->tx.dmach, true);
|
||||
}
|
||||
void ftdihw_dma_tx_stop(struct ftdi_hw* hw) {
|
||||
dma_irqn_set_channel_enabled(hw->itf->index & 1, hw->tx.dmach, false);
|
||||
dma_channel_abort(hw->tx.dmach);
|
||||
}
|
||||
|
||||
size_t ftdihw_dma_read(struct ftdi_hw* hw, uint8_t* dest, size_t maxsize) {
|
||||
// DMA is sending data from PIO to between dstart and dend, and is planned
|
||||
// to continue up to dataend. when it finishes an xfer (between dstart and
|
||||
// dend), it looks at dataend to see if it should continue sending more. if
|
||||
// not, it pauses itself as tehre's no more work to be done, and sets a
|
||||
// FIFO overrun flag in the FTDI error things
|
||||
//
|
||||
// here we read from dataend to dstart, careful not to touch the area the
|
||||
// DMA is currently writing to (while holding the channel paused), and then
|
||||
// moving the dataend marker. the channel is always reenabled, as either it
|
||||
// was busy and now it has more space to put data in, or needs to be
|
||||
// restarted as there is space now
|
||||
//
|
||||
// pausing the DMA channel is ok as it'll pause the PIO FIFO a bit, but
|
||||
// nothing drastic
|
||||
|
||||
if (maxsize == 0) return 0;
|
||||
|
||||
size_t rv = 0;
|
||||
|
||||
// TODO: make time between pause & resume shorter by moving stuff around?
|
||||
bool wasbusy = dma_hw->ch[hw->rx.dmach].ctrl_trig & DMA_CH0_CTRL_TRIG_BUSY_BITS;
|
||||
hw_clear_bits(&dma_hw->ch[hw->rx.dmach].ctrl_trig, DMA_CH0_CTRL_TRIG_EN_BITS); // pause (also prevents IRQ)
|
||||
|
||||
// dstart can get modified by the IRQ; IRQ reads dataend
|
||||
uint8_t dstart = hw->rx.dmabuf_dstart, dataend = hw->rx.dmabuf_dataend;
|
||||
if (dataend > dstart) dstart += sizeof(hw->dma_in_buf);
|
||||
// nothing ready yet - bail out
|
||||
if (dstart == (dataend + 1) % sizeof(hw->dma_in_buf)) {
|
||||
goto END;
|
||||
}
|
||||
|
||||
__compiler_memory_barrier();
|
||||
|
||||
// copy from data in ringbuffer that was read in by PIO
|
||||
rv = (size_t)dstart - (size_t)dataend - 1;
|
||||
if (rv > maxsize) rv = maxsize;
|
||||
for (size_t i = 0; i < rv; ++i) {
|
||||
dest[i] = hw->dma_in_buf[(dataend + i) % sizeof(hw->dma_in_buf)];
|
||||
}
|
||||
uint8_t dataend_new = (dataend + rv) % sizeof(hw->dma_in_buf);
|
||||
|
||||
hw->rx.dmabuf_dataend = dataend_new;
|
||||
|
||||
hw->itf->modemstat &= ~sio_modem_fifoerr;
|
||||
|
||||
END:
|
||||
if (!wasbusy) {
|
||||
hw->rx.dmabuf_dstart = hw->rx.dmabuf_dend; // not required to set in DMA hw, but need to keep track of it in code
|
||||
hw->rx.dmabuf_dend = hw->rx.dmabuf_dataend;
|
||||
ftdihw_dma_rx_setup(hw, true);
|
||||
} else // if already busy easlier, simply reenable
|
||||
hw_set_bits(&dma_hw->ch[hw->rx.dmach].ctrl_trig, DMA_CH0_CTRL_TRIG_EN_BITS); // resume (also reenables IRQ)
|
||||
return rv;
|
||||
return rv;
|
||||
}
|
||||
bool ftdihw_dma_write(struct ftdi_hw* hw, const uint8_t* src, size_t size) {
|
||||
// DMA is sending data between dstart and dend to PIO, and is planned to
|
||||
// continue up to dataend. when it finishes an xfer (between dstart and
|
||||
// dend), it looks at dataned to see if it should continue sending more. if
|
||||
// not, it pauses itself as there's no more work to be done
|
||||
//
|
||||
// here we insert some data in the ring buffer, careful to not overwrite
|
||||
// what the DMA is currently transferring (while holding the channel
|
||||
// paused), and then moving the dataend marker. the channel is always
|
||||
// reenabled, as either it was busy and now has more work to do, or needs
|
||||
// to be restarted as there's data now
|
||||
//
|
||||
// pausing the DMA channel is ok as it'll pause the PIO FIFO a bit, but
|
||||
// nothing drastic
|
||||
|
||||
if (size >= 255) return false; // don't even bother
|
||||
|
||||
bool rv = true;
|
||||
|
||||
// TODO: make time between pause & resume shorter by moving stuff around?
|
||||
bool wasbusy = dma_hw->ch[hw->tx.dmach].ctrl_trig & DMA_CH0_CTRL_TRIG_BUSY_BITS;
|
||||
hw_clear_bits(&dma_hw->ch[hw->tx.dmach].ctrl_trig, DMA_CH0_CTRL_TRIG_EN_BITS); // pause (also prevents IRQ)
|
||||
|
||||
// dstart can get modified by the IRQ; IRQ reads dataend
|
||||
uint8_t dstart = hw->tx.dmabuf_dstart, dataend = hw->tx.dmabuf_dataend;
|
||||
if (dataend > dstart) dstart += sizeof(hw->dma_out_buf);
|
||||
// no space to put it in - overrun error
|
||||
if ((size_t)dstart - (size_t)dataend < size + 1) {
|
||||
hw->itf->modemstat |= sio_modem_fifoerr;
|
||||
rv = false;
|
||||
goto END;
|
||||
}
|
||||
|
||||
__compiler_memory_barrier();
|
||||
|
||||
// copy data to buffer, to be copied next
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
hw->dma_out_buf[(dataend + i) % sizeof(hw->dma_out_buf)] = src[i];
|
||||
}
|
||||
uint8_t dataend_new = (dataend + size) % sizeof(hw->dma_out_buf);
|
||||
|
||||
hw->tx.dmabuf_dataend = dataend_new;
|
||||
|
||||
hw->itf->modemstat &= ~sio_modem_temt;
|
||||
|
||||
END:
|
||||
if (!wasbusy) {
|
||||
hw->tx.dmabuf_dstart = hw->tx.dmabuf_dend; // not required to set in DMA hw, but need to keep track of it in code
|
||||
hw->tx.dmabuf_dend = hw->tx.dmabuf_dataend;
|
||||
ftdihw_dma_tx_setup(hw, true);
|
||||
} else // if already busy easlier, simply reenable
|
||||
hw_set_bits(&dma_hw->ch[hw->tx.dmach].ctrl_trig, DMA_CH0_CTRL_TRIG_EN_BITS); // resume (also reenables IRQ)
|
||||
return rv;
|
||||
}
|
||||
|
||||
void ftdihw_dma_rx_flush(struct ftdi_hw* hw) {
|
||||
ftdihw_dma_rx_stop(hw);
|
||||
hw->rx.dmabuf_dstart = 0;
|
||||
hw->rx.dmabuf_dend = 0;
|
||||
hw->rx.dmabuf_dataend = 0;
|
||||
}
|
||||
void ftdihw_dma_tx_flush(struct ftdi_hw* hw) {
|
||||
ftdihw_dma_tx_stop(hw);
|
||||
hw->tx.dmabuf_dstart = 0;
|
||||
hw->tx.dmabuf_dend = 0;
|
||||
hw->tx.dmabuf_dataend = 0;
|
||||
}
|
||||
|
||||
static void ftdihw_dma_rx_irq(struct ftdi_hw* hw) {
|
||||
// an rx DMA transfer has finished. if in the meantime (between DMA xfer
|
||||
// start and now) more room has become available, restart the DMA channel
|
||||
// to write to the new space instead. otherwise, if there's no more space
|
||||
// left, we're in a data overrun condition, so don't restart, and set the
|
||||
// relevant FTDI error flags
|
||||
|
||||
uint8_t dend = hw->rx.dmabuf_dend, dataend = hw->rx.dmabuf_dataend;
|
||||
|
||||
if (dend == dataend) {
|
||||
// data overrun, stop stuff (until read() restarts the DMA)
|
||||
hw->itf->modemstat |= sio_modem_fifoerr;
|
||||
} else {
|
||||
hw->rx.dmabuf_dstart = dend;
|
||||
hw->rx.dmabuf_dend = dataend;
|
||||
ftdihw_dma_rx_setup(hw, true);
|
||||
}
|
||||
}
|
||||
static void ftdihw_dma_tx_irq(struct ftdi_hw* hw) {
|
||||
// a tx DMA transfer has finished. if in the meantile (between DMA xfer
|
||||
// start and now) more data has become available, restart the DMA channel
|
||||
// to read from the new data instead. otherwise, if there's no more data to
|
||||
// be read, we have nothing to do and we can leave the DMA channel in its
|
||||
// idle state.
|
||||
|
||||
uint8_t dend = hw->tx.dmabuf_dend, dataend = hw->tx.dmabuf_dataend;
|
||||
|
||||
if (dend == dataend) {
|
||||
// nothing to do
|
||||
hw->itf->modemstat |= sio_modem_temt;
|
||||
} else {
|
||||
hw->tx.dmabuf_dstart = dend;
|
||||
hw->tx.dmabuf_dend = dataend;
|
||||
ftdihw_dma_tx_setup(hw, true);
|
||||
}
|
||||
}
|
||||
|
||||
static void ftdihw_dma_isr() {
|
||||
// interrupt service routine: dispatch to functions above depending on INTS0
|
||||
uint32_t flags = dma_hw->ints0;
|
||||
uint32_t allflg = 1u << itf_bsp_data[0].rx.dmach;
|
||||
allflg |= 1u << itf_bsp_data[0].tx.dmach;
|
||||
allflg |= 1u << itf_bsp_data[1].rx.dmach;
|
||||
allflg |= 1u << itf_bsp_data[1].tx.dmach;
|
||||
|
||||
for (size_t i = 0; (i < 2) && (flags & allflg); ++i) {
|
||||
uint32_t flg = 1u << itf_bsp_data[i].rx.dmach;
|
||||
if (flags & flg) {
|
||||
flags &= ~flg;
|
||||
dma_hw->ints0 = flg; // ack int
|
||||
ftdihw_dma_rx_irq(&itf_bsp_data[i]);
|
||||
}
|
||||
|
||||
flg = 1u << itf_bsp_data[i].tx.dmach;
|
||||
if (flags & flg) {
|
||||
flags &= ~flg;
|
||||
dma_hw->ints0 = flg; // ack int
|
||||
ftdihw_dma_tx_irq(&itf_bsp_data[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
// vim: set et:
|
||||
|
||||
#ifndef FTDI_BASE_H_
|
||||
#define FTDI_BASE_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "m_ftdi/pinout.h"
|
||||
#include "m_ftdi/ftdi.h"
|
||||
|
||||
struct ftdi_hw_ch {
|
||||
const void* prg;
|
||||
|
||||
uint8_t prg_off, piosm, dmach;
|
||||
|
||||
volatile uint8_t dmabuf_dstart, dmabuf_dend, dmabuf_dataend;
|
||||
};
|
||||
|
||||
struct ftdi_hw {
|
||||
struct ftdi_interface* itf;
|
||||
PIO pio;
|
||||
|
||||
struct ftdi_hw_ch rx, tx;
|
||||
uint8_t pinbase;
|
||||
|
||||
volatile uint8_t dma_in_buf[256/*CFG_TUD_VENDOR_TX_BUFSIZE*/];
|
||||
volatile uint8_t dma_out_buf[256/*CFG_TUD_VENDOR_RX_BUFSIZE*/];
|
||||
};
|
||||
|
||||
extern struct ftdi_hw itf_bsp_data[2];
|
||||
|
||||
void ftdihw_init(struct ftdi_hw* fr, struct ftdi_interface* itf);
|
||||
void ftdihw_deinit(struct ftdi_hw* fr);
|
||||
|
||||
bool ftdihw_dma_ch_init(struct ftdi_hw_ch* ch, struct ftdi_hw* fr, const void* prg);
|
||||
void ftdihw_dma_ch_deinit(struct ftdi_hw_ch* fr, struct ftdi_hw* hw);
|
||||
|
||||
void ftdihw_dma_rx_setup(struct ftdi_hw* fr, bool start);
|
||||
void ftdihw_dma_rx_stop(struct ftdi_hw* fr);
|
||||
|
||||
void ftdihw_dma_tx_setup(struct ftdi_hw* fr, bool start);
|
||||
void ftdihw_dma_tx_stop(struct ftdi_hw* fr);
|
||||
|
||||
size_t ftdihw_dma_read(struct ftdi_hw* fr, uint8_t* dest, size_t maxsize);
|
||||
bool ftdihw_dma_write(struct ftdi_hw* fr, const uint8_t* src, size_t size);
|
||||
|
||||
void ftdihw_dma_rx_flush(struct ftdi_hw* fr);
|
||||
void ftdihw_dma_tx_flush(struct ftdi_hw* fr);
|
||||
|
||||
static inline int ftdihw_idx_to_base(int itf_idx) {
|
||||
return itf_idx ? PINOUT_ITF_B_BASE : PINOUT_ITF_A_BASE;
|
||||
}
|
||||
static inline int ftdihw_itf_to_base(struct ftdi_interface* itf) {
|
||||
return ftdihw_idx_to_base(itf->index);
|
||||
}
|
||||
|
||||
static inline PIO ftdihw_idx_to_pio(int itf_idx) {
|
||||
return itf_idx ? PINOUT_ITF_A_PIO : PINOUT_ITF_B_PIO;
|
||||
}
|
||||
static inline PIO ftdihw_itf_to_pio(struct ftdi_interface* itf) {
|
||||
return ftdihw_idx_to_pio(itf->index);
|
||||
}
|
||||
|
||||
static inline struct ftdi_hw* ftdihw_idx_to_hw(int itf_idx) {
|
||||
return &itf_bsp_data[itf_idx & 1];
|
||||
}
|
||||
static inline struct ftdi_hw* ftdihw_itf_to_hw(struct ftdi_interface* itf) {
|
||||
return ftdihw_idx_to_hw(itf->index);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,6 +1,9 @@
|
|||
// vim: set et:
|
||||
|
||||
#include <hardware/pio.h>
|
||||
|
||||
#include "m_ftdi/ftdi.h"
|
||||
#include "m_ftdi/ftdi_hw.h"
|
||||
|
||||
static void init_mode(struct ftdi_interface* itf, enum ftdi_mode mode) {
|
||||
switch (mode) {
|
||||
|
@ -28,10 +31,18 @@ static void deinit_mode(struct ftdi_interface* itf, enum ftdi_mode mode) {
|
|||
}
|
||||
|
||||
void ftdi_if_init(struct ftdi_interface* itf) {
|
||||
struct ftdi_hw* hw = ftdihw_itf_to_hw(itf);
|
||||
|
||||
ftdihw_init(hw, itf);
|
||||
|
||||
init_mode(itf, ftdi_if_get_mode(itf));
|
||||
}
|
||||
void ftdi_if_deinit(struct ftdi_interface* itf) {
|
||||
deinit_mode(itf, ftdi_if_get_mode(itf));
|
||||
|
||||
struct ftdi_hw* hw = ftdihw_itf_to_hw(itf);
|
||||
|
||||
ftdihw_deinit(hw);
|
||||
}
|
||||
|
||||
void ftdi_if_set_modemctrl(struct ftdi_interface* itf, uint8_t mask, uint8_t data) {
|
||||
|
@ -78,8 +89,12 @@ void ftdi_if_set_bitbang(struct ftdi_interface* itf, uint8_t dirmask,
|
|||
}
|
||||
|
||||
void ftdi_if_sio_reset(struct ftdi_interface* itf) { (void)itf; /* TODO: ? */ }
|
||||
void ftdi_if_sio_tciflush(struct ftdi_interface* itf) { (void)itf; /* TODO: ? */ }
|
||||
void ftdi_if_sio_tcoflush(struct ftdi_interface* itf) { (void)itf; /* TODO: ? */ }
|
||||
void ftdi_if_sio_tciflush(struct ftdi_interface* itf) {
|
||||
(void)itf; /* TODO: ? */
|
||||
}
|
||||
void ftdi_if_sio_tcoflush(struct ftdi_interface* itf) {
|
||||
(void)itf; /* TODO: ? */
|
||||
}
|
||||
void ftdi_if_set_latency(struct ftdi_interface* itf, uint8_t latency) { (void)itf; (void)latency; /* TODO: ? */ }
|
||||
uint8_t ftdi_if_get_latency(struct ftdi_interface* itf) { return itf->latency; /* TODO: ? */ }
|
||||
|
|
@ -2,6 +2,8 @@
|
|||
#ifndef BSP_PINOUT_M_FTDI_H_
|
||||
#define BSP_PINOUT_M_FTDI_H_
|
||||
|
||||
/* NOTE: no C code here! needs to be include-able from PIO asm! */
|
||||
|
||||
#define PINOUT_ITF_A_BASE 2
|
||||
#define PINOUT_ITF_B_BASE 14
|
||||
|
||||
|
@ -113,21 +115,5 @@
|
|||
#define PINOUT_CPUFIFO_nRD_OFF 10
|
||||
#define PINOUT_CPUFIFO_nWR_OFF 11
|
||||
|
||||
struct ftdi_interface;
|
||||
|
||||
static inline int PINOUT_idx_to_base(int itf_idx) {
|
||||
return itf_idx ? PINOUT_ITF_B_BASE : PINOUT_ITF_A_BASE;
|
||||
}
|
||||
static inline int PINOUT_itf_to_base(struct ftdi_interface* itf) {
|
||||
return PINOUT_idx_to_base(*(int*)itf); // can't access "index" directly here, so, shrug
|
||||
}
|
||||
|
||||
static inline void* PINOUT_idx_to_pio(int itf_idx) {
|
||||
return itf_idx ? PINOUT_ITF_A_PIO : PINOUT_ITF_B_PIO;
|
||||
}
|
||||
static inline void* PINOUT_itf_to_pio(struct ftdi_interface* itf) {
|
||||
return PINOUT_idx_to_pio(*(int*)itf); // can't access "index" directly here, so, shrug
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -1,23 +1,18 @@
|
|||
// vim: set et:
|
||||
|
||||
#include "m_ftdi/ftdi.h"
|
||||
|
||||
#include <hardware/dma.h>
|
||||
#include <hardware/gpio.h>
|
||||
#include <hardware/pio.h>
|
||||
#include <hardware/timer.h>
|
||||
#include <hardware/structs/dma.h>
|
||||
|
||||
#include "m_ftdi/pinout.h"
|
||||
#include "m_ftdi/ftdi_hw.h"
|
||||
|
||||
#include "ftdi_uart_rx.pio.h"
|
||||
#include "ftdi_uart_tx.pio.h"
|
||||
|
||||
struct chdat {
|
||||
uint8_t off, sm, dmach;
|
||||
};
|
||||
struct uart_state {
|
||||
uint32_t baudrate;
|
||||
struct chdat rx, tx;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
|
@ -28,113 +23,83 @@ static struct uart_state state[2] = {
|
|||
|
||||
#define STATEOF(itf) (state[(itf)->index & 1])
|
||||
|
||||
static bool init_sm(PIO pio, struct chdat* d, const pio_program_t* prg) {
|
||||
int off, sm, dmach;
|
||||
sm = pio_claim_unused_sm(pio, false);
|
||||
if (sm == -1) return false;
|
||||
|
||||
dmach = dma_claim_unused_channel(false);
|
||||
if (dmach == -1) {
|
||||
pio_sm_unclaim(pio, sm);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!pio_can_add_program(pio, prg)) {
|
||||
dma_channel_unclaim(dmach);
|
||||
pio_sm_unclaim(pio, sm);
|
||||
return false;
|
||||
}
|
||||
|
||||
off = pio_add_program(pio, prg);
|
||||
|
||||
d->off = off;
|
||||
d->sm = sm;
|
||||
d->dmach = dmach;
|
||||
|
||||
return true;
|
||||
}
|
||||
static void deinit_sm(PIO pio, struct chdat* d, const pio_program_t* prg) {
|
||||
dma_channel_unclaim(d->dmach);
|
||||
pio_sm_set_enabled(pio, d->sm, false);
|
||||
pio_sm_unclaim(pio, d->sm);
|
||||
pio_remove_program(pio, prg, d->off);
|
||||
}
|
||||
|
||||
static void setup_dmach(struct ftdi_interface* itf, struct chdat* d) {
|
||||
// set up PIO->dma_in_buf DMA:
|
||||
// * src=pio dst=buf 8bit 256words pacing=pio
|
||||
// * IRQ: set overrun bit
|
||||
// * start it
|
||||
// * SETUP AT MODE ENTER, STOP AT MODE EXIT
|
||||
//
|
||||
// set up dma_out_buf->PIO DMA:
|
||||
// * src=buf dst=pio 8bit <num>words pacing=pio
|
||||
// * ~~no IRQ I think?~~ IRQ: next ringbuffer part (see below)
|
||||
// * DO NOT start it on mode enter!
|
||||
// * STOP AT MODE EXIT
|
||||
//
|
||||
// read routine:
|
||||
// * abort DMA
|
||||
// * copy data from dma_in_buf (read xfer len? == remaining of 256 bytes)
|
||||
// * resetup & start DMA:
|
||||
//
|
||||
// write routine:
|
||||
// * if DMA running: set overrun bit, bail out?
|
||||
// * should use ringbuffer-like structure
|
||||
// * pointers: dma start, dma end, data end (after dma, contiguous)
|
||||
// * dma end can be calculated from DMA MMIO, but, race conditions so no
|
||||
// * use DMA IRQ for next block (and wraparound: dma cannot do wraparound manually)
|
||||
// * do not start next block if data end == dma start
|
||||
// * can we set DMA xfer len while in-flight? datasheet p92 2.5.1: nope. sad
|
||||
// * only bail out when data end == dma start - 1
|
||||
// * copy data to dma_out_buf
|
||||
// * set up & start DMA
|
||||
//
|
||||
// * what with buffers larger than 256 bytes?
|
||||
// * what is the actual FTDI buffer size??
|
||||
// * which bits get set on errors?
|
||||
// * do TCIFLUSH/TCOFLUSH influence these buffers, or only the USB proto
|
||||
// handling buffers?
|
||||
}
|
||||
static void stop_dmach(struct chdat* d) {
|
||||
|
||||
}
|
||||
// set up PIO->dma_in_buf DMA:
|
||||
// * src=pio dst=buf 8bit 256words pacing=pio
|
||||
// * IRQ: set overrun bit
|
||||
// * start it
|
||||
// * SETUP AT MODE ENTER, STOP AT MODE EXIT
|
||||
//
|
||||
// set up dma_out_buf->PIO DMA:
|
||||
// * src=buf dst=pio 8bit <num>words pacing=pio
|
||||
// * ~~no IRQ I think?~~ IRQ: next ringbuffer part (see below)
|
||||
// * DO NOT start it on mode enter!
|
||||
// * STOP AT MODE EXIT
|
||||
//
|
||||
// read routine:
|
||||
// * abort DMA
|
||||
// * copy data from dma_in_buf (read xfer len? == remaining of 256 bytes)
|
||||
// * resetup & start DMA:
|
||||
//
|
||||
// write routine:
|
||||
// * if DMA running: set overrun bit, bail out?
|
||||
// * should use ringbuffer-like structure
|
||||
// * pointers: dma start, dma end, data end (after dma, contiguous)
|
||||
// * dma end can be calculated from DMA MMIO, but, race conditions so no
|
||||
// * use DMA IRQ for next block (and wraparound: dma cannot do wraparound manually)
|
||||
// * do not start next block if data end == dma start
|
||||
// * can we set DMA xfer len while in-flight? datasheet p92 2.5.1: nope. sad
|
||||
// * only bail out when data end == dma start - 1
|
||||
// * copy data to dma_out_buf
|
||||
// * set up & start DMA
|
||||
//
|
||||
// * what with buffers larger than 256 bytes?
|
||||
// * just drop for now ig
|
||||
// * what is the actual FTDI buffer size??
|
||||
// * device-dependent so aaaa
|
||||
// * which bits get set on errors?
|
||||
// * do TCIFLUSH/TCOFLUSH influence these buffers, or only the USB proto
|
||||
// handling buffers?
|
||||
|
||||
void ftdi_if_uart_init(struct ftdi_interface* itf) {
|
||||
if (STATEOF(itf).enabled) return; // shrug
|
||||
|
||||
PIO pio = PINOUT_itf_to_pio(itf);
|
||||
int pin_rx = PINOUT_itf_to_base(itf) + PINOUT_UART_RXD_OFF,
|
||||
pin_tx = PINOUT_itf_to_base(itf) + PINOUT_UART_TXD_OFF;
|
||||
struct ftdi_hw* hw = ftdihw_itf_to_hw(itf);
|
||||
|
||||
if (!init_sm(pio, &STATEOF(itf).rx, &ftdi_uart_rx_program)) return;
|
||||
if (!init_sm(pio, &STATEOF(itf).tx, &ftdi_uart_tx_program)) return;
|
||||
if (!ftdihw_dma_ch_init(&hw->rx, hw, &ftdi_uart_rx_program)) return;
|
||||
if (!ftdihw_dma_ch_init(&hw->tx, hw, &ftdi_uart_tx_program)) {
|
||||
ftdihw_dma_ch_deinit(&hw->rx, hw);
|
||||
return;
|
||||
}
|
||||
|
||||
ftdi_uart_rx_program_init(pio, STATEOF(itf).rx.sm, STATEOF(itf).rx.off,
|
||||
int pin_rx = hw->pinbase + PINOUT_UART_RXD_OFF,
|
||||
pin_tx = hw->pinbase + PINOUT_UART_TXD_OFF;
|
||||
|
||||
ftdi_uart_rx_program_init(hw->pio, hw->rx.piosm, hw->rx.prg_off,
|
||||
pin_rx, STATEOF(itf).baudrate);
|
||||
ftdi_uart_tx_program_init(pio, STATEOF(itf).tx.sm, STATEOF(itf).tx.off,
|
||||
ftdi_uart_tx_program_init(hw->pio, hw->tx.piosm, hw->tx.prg_off,
|
||||
pin_tx, STATEOF(itf).baudrate);
|
||||
|
||||
gpio_set_function(pin_rx, GPIO_FUNC_PIO0 + itf->index);
|
||||
gpio_set_function(pin_tx, GPIO_FUNC_PIO0 + itf->index);
|
||||
|
||||
/*dma_channel_start(STATEOF(itf).rx.dmach);
|
||||
dma_channel_start(STATEOF(itf).tx.dmach);*/
|
||||
ftdihw_dma_rx_setup(hw, true);
|
||||
ftdihw_dma_tx_setup(hw, false);
|
||||
|
||||
STATEOF(itf).enabled = true;
|
||||
}
|
||||
void ftdi_if_uart_deinit(struct ftdi_interface* itf) {
|
||||
if (!STATEOF(itf).enabled) return; // shrug
|
||||
|
||||
PIO pio = PINOUT_itf_to_pio(itf);
|
||||
int pin_rx = PINOUT_itf_to_base(itf) + PINOUT_UART_RXD_OFF,
|
||||
pin_tx = PINOUT_itf_to_base(itf) + PINOUT_UART_TXD_OFF;
|
||||
struct ftdi_hw* hw = ftdihw_itf_to_hw(itf);
|
||||
|
||||
dma_channel_abort(STATEOF(itf).rx.dmach);
|
||||
dma_channel_abort(STATEOF(itf).tx.dmach);
|
||||
int pin_rx = hw->pinbase + PINOUT_UART_RXD_OFF,
|
||||
pin_tx = hw->pinbase + PINOUT_UART_TXD_OFF;
|
||||
|
||||
deinit_sm(pio, &STATEOF(itf).rx, &ftdi_uart_rx_program);
|
||||
deinit_sm(pio, &STATEOF(itf).tx, &ftdi_uart_tx_program);
|
||||
ftdihw_dma_rx_flush(hw);
|
||||
ftdihw_dma_tx_flush(hw);
|
||||
|
||||
ftdihw_dma_ch_deinit(&hw->rx, hw);
|
||||
ftdihw_dma_ch_deinit(&hw->tx, hw);
|
||||
|
||||
gpio_set_function(pin_rx, GPIO_FUNC_NULL);
|
||||
gpio_set_function(pin_tx, GPIO_FUNC_NULL);
|
||||
|
@ -146,14 +111,13 @@ void ftdi_if_uart_deinit(struct ftdi_interface* itf) {
|
|||
void ftdi_if_uart_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate) {
|
||||
if (!STATEOF(itf).enabled) return;
|
||||
|
||||
PIO pio = PINOUT_itf_to_pio(itf);
|
||||
int pin_rx = PINOUT_itf_to_base(itf) + PINOUT_UART_RXD_OFF,
|
||||
pin_tx = PINOUT_itf_to_base(itf) + PINOUT_UART_TXD_OFF;
|
||||
struct ftdi_hw* hw = ftdihw_itf_to_hw(itf);
|
||||
|
||||
ftdi_uart_rx_program_init(pio, STATEOF(itf).rx.sm, STATEOF(itf).rx.off,
|
||||
pin_rx, baudrate);
|
||||
ftdi_uart_tx_program_init(pio, STATEOF(itf).tx.sm, STATEOF(itf).tx.off,
|
||||
pin_tx, baudrate);
|
||||
int pin_rx = ftdihw_itf_to_base(itf) + PINOUT_UART_RXD_OFF,
|
||||
pin_tx = ftdihw_itf_to_base(itf) + PINOUT_UART_TXD_OFF;
|
||||
|
||||
ftdi_uart_rx_program_init(hw->pio, hw->rx.piosm, hw->rx.prg_off, pin_rx, baudrate);
|
||||
ftdi_uart_tx_program_init(hw->pio, hw->tx.piosm, hw->tx.prg_off, pin_tx, baudrate);
|
||||
|
||||
STATEOF(itf).baudrate = baudrate;
|
||||
}
|
||||
|
@ -167,32 +131,13 @@ void ftdi_if_set_lineprop(struct ftdi_interface* itf, enum ftdi_sio_lineprop lin
|
|||
}
|
||||
|
||||
void ftdi_if_uart_write(struct ftdi_interface* itf, const uint8_t* data, size_t datasize) {
|
||||
// for writes, we can simply do a blocking DMA for now
|
||||
// TODO: rewrite to use background DMA I guess
|
||||
// (TODO: what if prev DMA still busy? --> overrun error!)
|
||||
// TODO: ^ will need status bits!
|
||||
struct ftdi_hw* hw = ftdihw_itf_to_hw(itf);
|
||||
|
||||
PIO pio = PINOUT_itf_to_pio(itf);
|
||||
|
||||
dma_channel_config dcfg = dma_channel_get_default_config(STATEOF(itf).tx.dmach);
|
||||
channel_config_set_read_increment(&dcfg, true);
|
||||
channel_config_set_write_increment(&dcfg, false);
|
||||
channel_config_set_dreq(&dcfg,
|
||||
DREQ_PIO0_TX0 + itf->index*8/*PIO num*/ + STATEOF(itf).tx.sm);
|
||||
channel_config_set_transfer_data_size(&dcfg, DMA_SIZE_8);
|
||||
dma_channel_configure(STATEOF(itf).tx.dmach, &dcfg,
|
||||
&pio->txf[STATEOF(itf).tx.sm], data, datasize, true);
|
||||
|
||||
// TODO: not this
|
||||
dma_channel_wait_for_finish_blocking(STATEOF(itf).tx.dmach);
|
||||
ftdihw_dma_write(hw, data, datasize);
|
||||
}
|
||||
size_t ftdi_if_uart_read(struct ftdi_interface* itf, uint8_t* data, size_t maxsize) {
|
||||
(void)itf; (void)data; (void)maxsize;
|
||||
struct ftdi_hw* hw = ftdihw_itf_to_hw(itf);
|
||||
|
||||
// TODO: background thing going to a buffer which is then collected later
|
||||
// on by this function, needs some buffer mgmt
|
||||
// TODO: handle overruns
|
||||
|
||||
return 0;
|
||||
return ftdihw_dma_read(hw, data, maxsize);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// vim: set et:
|
||||
|
||||
#include "tusb_config.h"
|
||||
#include <tusb.h>
|
||||
|
||||
#include "mode.h"
|
||||
|
@ -19,20 +20,25 @@ static cothread_t ftdithread_ifa, ftdithread_ifb;
|
|||
static uint8_t ftdistack_ifa[THREAD_STACK_SIZE>>1], ftdistack_ifb[THREAD_STACK_SIZE>>1];
|
||||
|
||||
static void ftdi_thread_fn_ifa(void) {
|
||||
printf("fn ifa thread!\n");
|
||||
|
||||
while (1) {
|
||||
ftdi_task_ifa();
|
||||
//ftdi_task_ifa();
|
||||
thread_yield();
|
||||
}
|
||||
}
|
||||
static void ftdi_thread_fn_ifb(void) {
|
||||
printf("fn ifb thread!\n");
|
||||
while (1) {
|
||||
ftdi_task_ifb();
|
||||
//ftdi_task_ifb();
|
||||
thread_yield();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void enter_cb(void) {
|
||||
printf("mode5 enter begin\n");
|
||||
|
||||
#ifdef USE_USBCDC_FOR_STDIO
|
||||
stdio_usb_set_itf_num(CDC_N_STDIO);
|
||||
#endif
|
||||
|
@ -43,20 +49,25 @@ static void enter_cb(void) {
|
|||
ftdithread_ifb = co_derive(ftdistack_ifb, sizeof ftdistack_ifb, ftdi_thread_fn_ifb);
|
||||
#endif
|
||||
|
||||
if (!data_dirty) {
|
||||
/*if (!data_dirty) {
|
||||
struct mode_info mi = storage_mode_get_info(5);
|
||||
if (mi.size != 0 && mi.version == 0x0010) {
|
||||
storage_mode_read(5, ftdi_eeprom, 0, sizeof ftdi_eeprom);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
//ftdi_init();
|
||||
printf("mode5 enter end\n");
|
||||
}
|
||||
static void leave_cb(void) {
|
||||
printf("mode5 leave\n");
|
||||
#ifdef DBOARD_HAS_FTDI
|
||||
ftdi_deinit();
|
||||
//ftdi_deinit();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void task_cb(void) {
|
||||
printf("mode5 task\n");
|
||||
#ifdef DBOARD_HAS_FTDI
|
||||
tud_task();
|
||||
thread_enter(ftdithread_ifa);
|
||||
|
@ -66,7 +77,14 @@ static void task_cb(void) {
|
|||
}
|
||||
|
||||
static void handle_cmd_cb(uint8_t cmd) {
|
||||
printf("mode5 handlecmd %02x\n", cmd);
|
||||
uint8_t resp = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case mode_cmd_get_features:
|
||||
resp = 0; // TODO: what do we put here?
|
||||
vnd_cfg_write_resp(cfg_resp_ok, 1, &resp);
|
||||
break;
|
||||
default:
|
||||
vnd_cfg_write_strf(cfg_resp_illcmd, "unknown mode5 command %02x", cmd);
|
||||
break;
|
||||
|
@ -184,6 +202,7 @@ static const uint8_t* my_descriptor_device_cb(void) {
|
|||
|
||||
#if CFG_TUD_CDC > 0
|
||||
static void my_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* line_coding) {
|
||||
printf("mode5 linecoding %02x, %lu\n", itf, line_coding->bit_rate);
|
||||
switch (itf) {
|
||||
#ifdef USE_USBCDC_FOR_STDIO
|
||||
case CDC_N_STDIO:
|
||||
|
@ -225,7 +244,7 @@ struct mode m_05_ftdi = {
|
|||
},
|
||||
|
||||
#ifdef DBOARD_HAS_FTDI
|
||||
.tud_descriptor_device_cb = my_descriptor_device_cb,
|
||||
//.tud_descriptor_device_cb = my_descriptor_device_cb,
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_CDC > 0
|
||||
|
@ -233,7 +252,7 @@ struct mode m_05_ftdi = {
|
|||
#endif
|
||||
|
||||
#ifdef DBOARD_HAS_FTDI
|
||||
.tud_vendor_control_xfer_cb = ftdi_control_xfer_cb,
|
||||
//.tud_vendor_control_xfer_cb = ftdi_control_xfer_cb,
|
||||
#endif
|
||||
};
|
||||
// clang-format on
|
||||
|
|
|
@ -75,6 +75,8 @@ int __builtin_ctz(unsigned int v);
|
|||
|
||||
uint16_t ftdi_eeprom[128];
|
||||
|
||||
struct ftdi_interface ftdi_ifa, ftdi_ifb;
|
||||
|
||||
void ftdi_init(void) {
|
||||
// init eeprom defaults
|
||||
memset(ftdi_eeprom, 0xff, sizeof ftdi_eeprom);
|
||||
|
@ -97,6 +99,8 @@ void ftdi_init(void) {
|
|||
ftdi_ifb.lineprop = sio_bits_8 | sio_stop_1; // 8n1
|
||||
ftdi_ifa.index = 0;
|
||||
ftdi_ifb.index = 1;
|
||||
ftdi_ifa.modemstat = sio_modem_temt;
|
||||
ftdi_ifb.modemstat = sio_modem_temt;
|
||||
ftdi_if_init(&ftdi_ifa);
|
||||
ftdi_if_init(&ftdi_ifb);
|
||||
}
|
||||
|
|
|
@ -259,9 +259,6 @@ struct ftdi_interface {
|
|||
uint8_t readbuf [CFG_TUD_VENDOR_TX_BUFSIZE];
|
||||
uint8_t bufbuf [CFG_TUD_VENDOR_RX_BUFSIZE]; // for buffered IO
|
||||
uint32_t rxavail, rxpos;
|
||||
|
||||
uint8_t dma_in_buf[CFG_TUD_VENDOR_TX_BUFSIZE];
|
||||
uint8_t dma_out_buf[CFG_TUD_VENDOR_RX_BUFSIZE];
|
||||
};
|
||||
|
||||
extern struct ftdi_interface ftdi_ifa, ftdi_ifb;
|
||||
|
|
|
@ -25,6 +25,8 @@ static void vndcfg_thread_fn(void) {
|
|||
}
|
||||
|
||||
int main() {
|
||||
stdio_uart_init();
|
||||
|
||||
thread_init();
|
||||
|
||||
board_init(); // tinyusb hardware support function
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#include "mode.h"
|
||||
#include "storage.h"
|
||||
|
||||
extern struct mode m_01_default, m_03_jscan, m_04_sump;
|
||||
extern struct mode m_01_default, m_03_jscan, m_04_sump, m_05_ftdi;
|
||||
|
||||
// clang-format off
|
||||
const struct mode* const mode_list[16] = {
|
||||
|
@ -16,6 +16,7 @@ const struct mode* const mode_list[16] = {
|
|||
NULL, // mode 2 (hw chip programming stuff) not implemented yet
|
||||
&m_03_jscan,
|
||||
&m_04_sump,
|
||||
&m_05_ftdi,
|
||||
NULL, // terminating entry
|
||||
};
|
||||
// clang-format on
|
||||
|
@ -99,6 +100,8 @@ void modes_switch(uint8_t newmode) {
|
|||
// maybe wait a second or so for the host to notice this
|
||||
sleep_ms(500/2);
|
||||
|
||||
//printf("disconnect\n");
|
||||
|
||||
if (newmode == 0) bsp_reset_bootloader();
|
||||
|
||||
// now apply the new tusb settings
|
||||
|
@ -164,11 +167,15 @@ void modes_switch(uint8_t newmode) {
|
|||
// clang-format on
|
||||
}
|
||||
|
||||
//printf("reconnect\n");
|
||||
|
||||
// and reconnect
|
||||
tud_connect();
|
||||
sleep_ms(500/2);
|
||||
//while (!tud_mounted()) sleep_ms(5);
|
||||
|
||||
//printf("enter\n");
|
||||
|
||||
if (mode_current) mode_current->enter();
|
||||
}
|
||||
|
||||
|
|
|
@ -13,5 +13,7 @@ void stdio_usb_set_itf_num(int itf);
|
|||
void stdio_usb_line_coding_cb(cdc_line_coding_t const* line_coding);
|
||||
#endif
|
||||
|
||||
void stdio_uart_init(void);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
Loading…
Reference in New Issue