mirror of
https://github.com/bdring/Grbl_Esp32.git
synced 2025-09-06 20:30:56 +02:00
Try to implement the I2S reset mechanism(not yet works)
I started implementing the reset process for the I2S I/O expander. It's still not enough.
This commit is contained in:
@@ -94,11 +94,11 @@
|
|||||||
// The 1 bits in LIMIT_MASK set the axes that have limit switches
|
// The 1 bits in LIMIT_MASK set the axes that have limit switches
|
||||||
// For example, if the Y axis has no limit switches but the
|
// For example, if the Y axis has no limit switches but the
|
||||||
// X, Z, A and B axes do, the LIMIT_MASK value would be B11101
|
// X, Z, A and B axes do, the LIMIT_MASK value would be B11101
|
||||||
#define LIMIT_MASK B0111
|
//#define LIMIT_MASK B0111
|
||||||
|
|
||||||
#define X_LIMIT_PIN GPIO_NUM_34
|
//#define X_LIMIT_PIN GPIO_NUM_34
|
||||||
#define Y_LIMIT_PIN GPIO_NUM_35
|
//#define Y_LIMIT_PIN GPIO_NUM_35
|
||||||
#define Z_LIMIT_PIN GPIO_NUM_32
|
//#define Z_LIMIT_PIN GPIO_NUM_32
|
||||||
|
|
||||||
// Common enable for all steppers. If it is okay to leave
|
// Common enable for all steppers. If it is okay to leave
|
||||||
// your drivers enabled at all times, you can leave
|
// your drivers enabled at all times, you can leave
|
||||||
@@ -125,7 +125,7 @@
|
|||||||
// RESET, FEED_HOLD, and CYCLE_START can control GCode execution at
|
// RESET, FEED_HOLD, and CYCLE_START can control GCode execution at
|
||||||
// the push of a button.
|
// the push of a button.
|
||||||
|
|
||||||
// #define CONTROL_RESET_PIN GPIO_NUM_34 // labeled Reset, needs external pullup
|
#define CONTROL_RESET_PIN GPIO_NUM_34 // labeled Reset, needs external pullup
|
||||||
// #define CONTROL_FEED_HOLD_PIN GPIO_NUM_36 // labeled Hold, needs external pullup
|
// #define CONTROL_FEED_HOLD_PIN GPIO_NUM_36 // labeled Hold, needs external pullup
|
||||||
// #define CONTROL_CYCLE_START_PIN GPIO_NUM_39 // labeled Start, needs external pullup
|
// #define CONTROL_CYCLE_START_PIN GPIO_NUM_39 // labeled Start, needs external pullup
|
||||||
|
|
||||||
@@ -185,7 +185,7 @@
|
|||||||
// The control pins with names that begin with CONTROL_ are
|
// The control pins with names that begin with CONTROL_ are
|
||||||
// ignored by default, to avoid noise problems. To make them
|
// ignored by default, to avoid noise problems. To make them
|
||||||
// work, you must undefine IGNORE_CONTROL_PINS
|
// work, you must undefine IGNORE_CONTROL_PINS
|
||||||
// #undef IGNORE_CONTROL_PINS
|
#undef IGNORE_CONTROL_PINS
|
||||||
|
|
||||||
// If some of the control pin switches are normally closed
|
// If some of the control pin switches are normally closed
|
||||||
// (the default is normally open), you can invert some of them
|
// (the default is normally open), you can invert some of them
|
||||||
|
@@ -91,10 +91,16 @@ static atomic_uint_least32_t i2s_port_data = ATOMIC_VAR_INIT(0);
|
|||||||
#define I2S_ENTER_CRITICAL() portENTER_CRITICAL(&i2s_spinlock)
|
#define I2S_ENTER_CRITICAL() portENTER_CRITICAL(&i2s_spinlock)
|
||||||
#define I2S_EXIT_CRITICAL() portEXIT_CRITICAL(&i2s_spinlock)
|
#define I2S_EXIT_CRITICAL() portEXIT_CRITICAL(&i2s_spinlock)
|
||||||
|
|
||||||
|
static int i2s_ioexpander_initialized = 0;
|
||||||
|
|
||||||
static volatile uint32_t i2s_ioexpander_pulse_period;
|
static volatile uint32_t i2s_ioexpander_pulse_period;
|
||||||
static uint32_t i2s_ioexpander_remain_time_until_next_pulse; // Time remaining until the next pulse (μsec)
|
static uint32_t i2s_ioexpander_remain_time_until_next_pulse; // Time remaining until the next pulse (μsec)
|
||||||
static volatile i2s_ioexpander_pulse_phase_func_t i2s_ioexpander_pulse_phase_func;
|
static volatile i2s_ioexpander_pulse_phase_func_t i2s_ioexpander_pulse_phase_func;
|
||||||
|
|
||||||
|
static uint8_t i2s_ioexpander_ws_pin = -1;
|
||||||
|
static uint8_t i2s_ioexpander_bck_pin = -1;
|
||||||
|
static uint8_t i2s_ioexpander_data_pin = -1;
|
||||||
|
|
||||||
enum i2s_ioexpander_status_t {
|
enum i2s_ioexpander_status_t {
|
||||||
UNKNOWN = 0,
|
UNKNOWN = 0,
|
||||||
RUNNING,
|
RUNNING,
|
||||||
@@ -103,7 +109,248 @@ enum i2s_ioexpander_status_t {
|
|||||||
static volatile i2s_ioexpander_status_t i2s_ioexpander_status;
|
static volatile i2s_ioexpander_status_t i2s_ioexpander_status;
|
||||||
|
|
||||||
//
|
//
|
||||||
// External funtions (without init function)
|
// Internal functions
|
||||||
|
//
|
||||||
|
static inline void gpio_matrix_out_check(uint32_t gpio, uint32_t signal_idx, bool out_inv, bool oen_inv) {
|
||||||
|
//if pin = -1, do not need to configure
|
||||||
|
if (gpio != -1) {
|
||||||
|
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[gpio], PIN_FUNC_GPIO);
|
||||||
|
gpio_set_direction((gpio_num_t)gpio, (gpio_mode_t)GPIO_MODE_DEF_OUTPUT);
|
||||||
|
gpio_matrix_out(gpio, signal_idx, out_inv, oen_inv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void i2s_reset_fifo_without_lock() {
|
||||||
|
I2S0.conf.rx_fifo_reset = 1;
|
||||||
|
I2S0.conf.rx_fifo_reset = 0;
|
||||||
|
I2S0.conf.tx_fifo_reset = 1;
|
||||||
|
I2S0.conf.tx_fifo_reset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void i2s_reset_fifo() {
|
||||||
|
I2S_ENTER_CRITICAL();
|
||||||
|
i2s_reset_fifo_without_lock();
|
||||||
|
I2S_EXIT_CRITICAL();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int i2s_clear_dma_buffers() {
|
||||||
|
if (!i2s_ioexpander_initialized) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int buf_idx = 0; buf_idx < DMA_BUF_COUNT; buf_idx++) {
|
||||||
|
// Clear the DMA buffer
|
||||||
|
uint32_t port_data = atomic_load(&i2s_port_data);
|
||||||
|
for (int i = 0; i < DMA_SAMPLE_COUNT; i++) {
|
||||||
|
dma.buffers[buf_idx][i] = port_data;
|
||||||
|
}
|
||||||
|
// Initialize DMA descriptor
|
||||||
|
dma.desc[buf_idx]->owner = 1;
|
||||||
|
dma.desc[buf_idx]->eof = 1; // set to 1 will trigger the interrupt
|
||||||
|
dma.desc[buf_idx]->sosf = 0;
|
||||||
|
dma.desc[buf_idx]->length = DMA_BUF_LEN;
|
||||||
|
dma.desc[buf_idx]->size = DMA_BUF_LEN;
|
||||||
|
dma.desc[buf_idx]->buf = (uint8_t *) dma.buffers[buf_idx];
|
||||||
|
dma.desc[buf_idx]->offset = 0;
|
||||||
|
dma.desc[buf_idx]->empty = (uint32_t)((buf_idx < (DMA_BUF_COUNT - 1)) ? (dma.desc[buf_idx + 1]) : dma.desc[0]);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int i2s_gpio_attach(uint8_t ws, uint8_t bck, uint8_t data) {
|
||||||
|
// Route the i2s pins to the appropriate GPIO
|
||||||
|
gpio_matrix_out_check(data, I2S0O_DATA_OUT23_IDX, 0, 0);
|
||||||
|
gpio_matrix_out_check(bck, I2S0O_BCK_OUT_IDX, 0, 0);
|
||||||
|
gpio_matrix_out_check(ws, I2S0O_WS_OUT_IDX, 0, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define I2S_IOEXP_DETACH_PORT_IDX 0x100
|
||||||
|
|
||||||
|
static int i2s_gpio_detach(uint8_t ws, uint8_t bck, uint8_t data) {
|
||||||
|
// Route the i2s pins to the appropriate GPIO
|
||||||
|
gpio_matrix_out_check(ws, I2S_IOEXP_DETACH_PORT_IDX, 0, 0);
|
||||||
|
gpio_matrix_out_check(bck, I2S_IOEXP_DETACH_PORT_IDX, 0, 0);
|
||||||
|
gpio_matrix_out_check(data, I2S_IOEXP_DETACH_PORT_IDX, 0, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int i2s_gpio_shiftout(uint32_t port_data) {
|
||||||
|
digitalWrite(i2s_ioexpander_ws_pin, LOW);
|
||||||
|
for (int i = 0; i < 32; i++) {
|
||||||
|
// XXX do not use raw defines for machine
|
||||||
|
digitalWrite(i2s_ioexpander_data_pin, !!(port_data & (1 << (31 - i))));
|
||||||
|
digitalWrite(i2s_ioexpander_bck_pin, HIGH);
|
||||||
|
digitalWrite(i2s_ioexpander_bck_pin, LOW);
|
||||||
|
}
|
||||||
|
digitalWrite(i2s_ioexpander_ws_pin, HIGH); // Latch
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int i2s_stop() {
|
||||||
|
I2S_ENTER_CRITICAL();
|
||||||
|
// Stop FIFO DMA
|
||||||
|
I2S0.out_link.stop = 1;
|
||||||
|
|
||||||
|
// Disconnect DMA from FIFO
|
||||||
|
I2S0.fifo_conf.dscr_en = 0; //Unset this bit to disable I2S DMA mode. (R/W)
|
||||||
|
|
||||||
|
// stop TX module
|
||||||
|
I2S0.conf.tx_start = 0;
|
||||||
|
|
||||||
|
// Force WS to LOW before detach
|
||||||
|
// This operation prevents unintended WS edge trigger when detach
|
||||||
|
digitalWrite(i2s_ioexpander_ws_pin, LOW);
|
||||||
|
|
||||||
|
// Now, detach GPIO pin from I2S
|
||||||
|
i2s_gpio_detach(i2s_ioexpander_ws_pin, i2s_ioexpander_bck_pin, i2s_ioexpander_data_pin);
|
||||||
|
|
||||||
|
// Force BCK to LOW
|
||||||
|
// After the TX module is stopped, BCK always seems to be in LOW.
|
||||||
|
// However, I'm going to do it manually to ensure the BCK's LOW.
|
||||||
|
digitalWrite(i2s_ioexpander_bck_pin, LOW);
|
||||||
|
|
||||||
|
// Transmit recovery data to 74HC595
|
||||||
|
uint32_t port_data = atomic_load(&i2s_port_data); // current expanded port value
|
||||||
|
i2s_gpio_shiftout(port_data);
|
||||||
|
|
||||||
|
//clear pending interrupt
|
||||||
|
I2S0.int_clr.val = I2S0.int_st.val;
|
||||||
|
I2S_EXIT_CRITICAL();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int i2s_start() {
|
||||||
|
if (!i2s_ioexpander_initialized) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// Transmit recovery data to 74HC595
|
||||||
|
uint32_t port_data = atomic_load(&i2s_port_data); // current expanded port value
|
||||||
|
i2s_gpio_shiftout(port_data);
|
||||||
|
|
||||||
|
// Attach I2S to specified GPIO pin
|
||||||
|
i2s_gpio_attach(i2s_ioexpander_ws_pin, i2s_ioexpander_bck_pin, i2s_ioexpander_data_pin);
|
||||||
|
//start DMA link
|
||||||
|
I2S_ENTER_CRITICAL();
|
||||||
|
i2s_reset_fifo_without_lock();
|
||||||
|
//reset DMA
|
||||||
|
I2S0.lc_conf.in_rst = 1;
|
||||||
|
I2S0.lc_conf.in_rst = 0;
|
||||||
|
I2S0.lc_conf.out_rst = 1;
|
||||||
|
I2S0.lc_conf.out_rst = 0;
|
||||||
|
|
||||||
|
I2S0.conf.tx_reset = 1;
|
||||||
|
I2S0.conf.tx_reset = 0;
|
||||||
|
I2S0.conf.rx_reset = 1;
|
||||||
|
I2S0.conf.rx_reset = 0;
|
||||||
|
|
||||||
|
I2S0.out_link.addr = (uint32_t)dma.desc[0];
|
||||||
|
|
||||||
|
// Connect DMA to FIFO
|
||||||
|
I2S0.fifo_conf.dscr_en = 1; // Set this bit to enable I2S DMA mode. (R/W)
|
||||||
|
|
||||||
|
I2S0.int_clr.val = 0xFFFFFFFF;
|
||||||
|
I2S0.out_link.start = 1;
|
||||||
|
I2S0.conf.tx_start = 1;
|
||||||
|
|
||||||
|
I2S_EXIT_CRITICAL();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// I2S DMA Interrupts handler
|
||||||
|
//
|
||||||
|
static void IRAM_ATTR i2s_intr_handler_default(void *arg) {
|
||||||
|
lldesc_t *finish_desc;
|
||||||
|
portBASE_TYPE high_priority_task_awoken = pdFALSE;
|
||||||
|
|
||||||
|
if (I2S0.int_st.out_eof) {
|
||||||
|
// Get the descriptor of the last item in the linkedlist
|
||||||
|
finish_desc = (lldesc_t*) I2S0.out_eof_des_addr;
|
||||||
|
|
||||||
|
// If the queue is full it's because we have an underflow,
|
||||||
|
// more than buf_count isr without new data, remove the front buffer
|
||||||
|
if (xQueueIsQueueFullFromISR(dma.queue)) {
|
||||||
|
lldesc_t *front_desc;
|
||||||
|
// Remove a descriptor from the DMA complete event queue
|
||||||
|
xQueueReceiveFromISR(dma.queue, &front_desc, &high_priority_task_awoken);
|
||||||
|
uint32_t port_data = atomic_load(&i2s_port_data);
|
||||||
|
for (int i = 0; i < DMA_SAMPLE_COUNT; i++) {
|
||||||
|
front_desc->buf[i] = port_data;
|
||||||
|
}
|
||||||
|
front_desc->length = DMA_BUF_LEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send a DMA complete event to the I2S bitstreamer task with finished buffer
|
||||||
|
xQueueSendFromISR(dma.queue, &finish_desc, &high_priority_task_awoken);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (high_priority_task_awoken == pdTRUE) portYIELD_FROM_ISR();
|
||||||
|
|
||||||
|
// clear interrupt
|
||||||
|
I2S0.int_clr.val = I2S0.int_st.val; //clear pending interrupt
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// I2S bitstream generator task
|
||||||
|
//
|
||||||
|
static void IRAM_ATTR i2sIOExpanderTask(void* parameter) {
|
||||||
|
lldesc_t *dma_desc;
|
||||||
|
while (1) {
|
||||||
|
// Wait a DMA complete event from I2S isr
|
||||||
|
// (Block until a DMA transfer has complete)
|
||||||
|
xQueueReceive(dma.queue, &dma_desc, portMAX_DELAY);
|
||||||
|
dma.current = (uint32_t*)(dma_desc->buf);
|
||||||
|
// It reuses the oldest (just transferred) buffer with the name "current"
|
||||||
|
// and fills the buffer for later DMA.
|
||||||
|
if (i2s_ioexpander_status == RUNNING) {
|
||||||
|
//
|
||||||
|
// Fillout the buffer for pulse
|
||||||
|
//
|
||||||
|
// To avoid buffer overflow, all of the maximum pulse width (normaly about 10us)
|
||||||
|
// is adjusted to be in a single buffer.
|
||||||
|
// DMA_SAMPLE_SAFE_COUNT is referred to as the margin value.
|
||||||
|
// Therefore, if a buffer is close to full and it is time to generate a pulse,
|
||||||
|
// the generation of the buffer is interrupted (the buffer length is shortened slightly)
|
||||||
|
// and the pulse generation is postponed until the next buffer is filled.
|
||||||
|
//
|
||||||
|
dma.rw_pos = 0;
|
||||||
|
while (dma.rw_pos < (DMA_SAMPLE_COUNT - SAMPLE_SAFE_COUNT)) {
|
||||||
|
// no data to read (buffer empty)
|
||||||
|
if (i2s_ioexpander_remain_time_until_next_pulse < I2S_IOEXP_USEC_PER_PULSE) {
|
||||||
|
// fillout future DMA buffer (tail of the DMA buffer chains)
|
||||||
|
if (i2s_ioexpander_pulse_phase_func != NULL) {
|
||||||
|
(*i2s_ioexpander_pulse_phase_func)(); // should be pushed into buffer max DMA_SAMPLE_SAFE_COUNT
|
||||||
|
i2s_ioexpander_remain_time_until_next_pulse = i2s_ioexpander_pulse_period;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// no pulse data in push buffer (pulse off or idle or callback is not defined)
|
||||||
|
dma.current[dma.rw_pos++] = atomic_load(&i2s_port_data);
|
||||||
|
if (i2s_ioexpander_remain_time_until_next_pulse >= I2S_IOEXP_USEC_PER_PULSE) {
|
||||||
|
i2s_ioexpander_remain_time_until_next_pulse -= I2S_IOEXP_USEC_PER_PULSE;
|
||||||
|
} else {
|
||||||
|
i2s_ioexpander_remain_time_until_next_pulse = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// set filled length to the DMA descriptor
|
||||||
|
dma_desc->length = dma.rw_pos * I2S_SAMPLE_SIZE;
|
||||||
|
} else {
|
||||||
|
// Stepper paused unknown
|
||||||
|
// (just set current I/O port bits to the buffer)
|
||||||
|
uint32_t port_data = atomic_load(&i2s_port_data);
|
||||||
|
for (int i = 0; i < DMA_SAMPLE_COUNT; i++) {
|
||||||
|
dma.current[i] = port_data;
|
||||||
|
}
|
||||||
|
dma.rw_pos = DMA_SAMPLE_COUNT;
|
||||||
|
dma_desc->length = DMA_BUF_LEN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// External funtions
|
||||||
//
|
//
|
||||||
void IRAM_ATTR i2s_ioexpander_write(uint8_t pin, uint8_t val) {
|
void IRAM_ATTR i2s_ioexpander_write(uint8_t pin, uint8_t val) {
|
||||||
uint32_t bit = 1UL << pin;
|
uint32_t bit = 1UL << pin;
|
||||||
@@ -153,158 +400,27 @@ int i2s_ioexpander_register_pulse_callback(i2s_ioexpander_pulse_phase_func_t fun
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
int i2s_ioexpander_reset() {
|
||||||
// Internal functions (and init function)
|
i2s_stop();
|
||||||
//
|
i2s_clear_dma_buffers();
|
||||||
static inline void gpio_matrix_out_check(uint32_t gpio, uint32_t signal_idx, bool out_inv, bool oen_inv) {
|
i2s_start();
|
||||||
//if pin = -1, do not need to configure
|
|
||||||
if (gpio != -1) {
|
|
||||||
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[gpio], PIN_FUNC_GPIO);
|
|
||||||
gpio_set_direction((gpio_num_t)gpio, (gpio_mode_t)GPIO_MODE_DEF_OUTPUT);
|
|
||||||
gpio_matrix_out(gpio, signal_idx, out_inv, oen_inv);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void i2s_reset_fifo_without_lock() {
|
|
||||||
I2S0.conf.rx_fifo_reset = 1;
|
|
||||||
I2S0.conf.rx_fifo_reset = 0;
|
|
||||||
I2S0.conf.tx_fifo_reset = 1;
|
|
||||||
I2S0.conf.tx_fifo_reset = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void i2s_reset_fifo() {
|
|
||||||
I2S_ENTER_CRITICAL();
|
|
||||||
i2s_reset_fifo_without_lock();
|
|
||||||
I2S_EXIT_CRITICAL();
|
|
||||||
}
|
|
||||||
|
|
||||||
static int i2s_start() {
|
|
||||||
//start DMA link
|
|
||||||
I2S_ENTER_CRITICAL();
|
|
||||||
i2s_reset_fifo_without_lock();
|
|
||||||
|
|
||||||
//reset dma
|
|
||||||
I2S0.lc_conf.in_rst = 1;
|
|
||||||
I2S0.lc_conf.in_rst = 0;
|
|
||||||
I2S0.lc_conf.out_rst = 1;
|
|
||||||
I2S0.lc_conf.out_rst = 0;
|
|
||||||
|
|
||||||
I2S0.conf.tx_reset = 1;
|
|
||||||
I2S0.conf.tx_reset = 0;
|
|
||||||
I2S0.conf.rx_reset = 1;
|
|
||||||
I2S0.conf.rx_reset = 0;
|
|
||||||
|
|
||||||
I2S0.int_clr.val = 0xFFFFFFFF;
|
|
||||||
I2S0.out_link.start = 1;
|
|
||||||
I2S0.conf.tx_start = 1;
|
|
||||||
I2S_EXIT_CRITICAL();
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// I2S DMA Interrupts handler
|
|
||||||
//
|
|
||||||
static void IRAM_ATTR i2s_intr_handler_default(void *arg) {
|
|
||||||
lldesc_t *finish_desc;
|
|
||||||
portBASE_TYPE high_priority_task_awoken = pdFALSE;
|
|
||||||
|
|
||||||
if (I2S0.int_st.out_eof) {
|
|
||||||
// Get the descriptor of the last item in the linkedlist
|
|
||||||
finish_desc = (lldesc_t*) I2S0.out_eof_des_addr;
|
|
||||||
|
|
||||||
// If the queue is full it's because we have an underflow,
|
|
||||||
// more than buf_count isr without new data, remove the front buffer
|
|
||||||
if (xQueueIsQueueFullFromISR(dma.queue)) {
|
|
||||||
uint32_t *finished_buffer;
|
|
||||||
xQueueReceiveFromISR(dma.queue, &finished_buffer, &high_priority_task_awoken);
|
|
||||||
// This will avoid any kind of noise that may get introduced due to transmission
|
|
||||||
// of previous data from tx descriptor on I2S line.
|
|
||||||
uint32_t port_data = atomic_load(&i2s_port_data);
|
|
||||||
for (int i = 0; i < DMA_SAMPLE_COUNT; i++) {
|
|
||||||
finished_buffer[i] = port_data;
|
|
||||||
}
|
|
||||||
finish_desc->length = DMA_BUF_LEN;
|
|
||||||
}
|
|
||||||
// Send a DMA complete event to the I2S bitstreamer task with finished buffer
|
|
||||||
xQueueSendFromISR(dma.queue, &finish_desc, &high_priority_task_awoken);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (high_priority_task_awoken == pdTRUE) portYIELD_FROM_ISR();
|
|
||||||
|
|
||||||
// clear interrupt
|
|
||||||
I2S0.int_clr.val = I2S0.int_st.val; //clear pending interrupt
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// I2S bitstream generator task
|
|
||||||
//
|
|
||||||
static void IRAM_ATTR i2sIOExpanderTask(void* parameter) {
|
|
||||||
lldesc_t *dma_desc;
|
|
||||||
while (1) {
|
|
||||||
// Wait a DMA complete event from I2S isr
|
|
||||||
// (Block until a DMA transfer has complete)
|
|
||||||
xQueueReceive(dma.queue, &dma_desc, portMAX_DELAY);
|
|
||||||
dma.current = (uint32_t*)(dma_desc->buf);
|
|
||||||
// It reuses the oldest (just transferred) buffer with the name "current"
|
|
||||||
// and fills the buffer for later DMA.
|
|
||||||
if (i2s_ioexpander_status == RUNNING) {
|
|
||||||
//
|
|
||||||
// Fillout the buffer for pulse
|
|
||||||
//
|
|
||||||
// To avoid buffer overflow, all of the maximum pulse width (normaly about 10us)
|
|
||||||
// is adjusted to be in a single buffer.
|
|
||||||
// DMA_SAMPLE_SAFE_COUNT is referred to as the margin value.
|
|
||||||
// Therefore, if a buffer is close to full and it is time to generate a pulse,
|
|
||||||
// the generation of the buffer is interrupted (the buffer length is shortened slightly)
|
|
||||||
// and the pulse generation is postponed until the next buffer is filled.
|
|
||||||
//
|
|
||||||
dma.rw_pos = 0;
|
|
||||||
while (dma.rw_pos < (DMA_SAMPLE_COUNT - SAMPLE_SAFE_COUNT)) {
|
|
||||||
// no data to read (buffer empty)
|
|
||||||
if (i2s_ioexpander_remain_time_until_next_pulse < I2S_IOEXP_USEC_PER_PULSE) {
|
|
||||||
// fillout future DMA buffer (tail of the DMA buffer chains)
|
|
||||||
if (i2s_ioexpander_pulse_phase_func != NULL) {
|
|
||||||
(*i2s_ioexpander_pulse_phase_func)(); // should be pushed into buffer max DMA_SAMPLE_SAFE_COUNT
|
|
||||||
i2s_ioexpander_remain_time_until_next_pulse = i2s_ioexpander_pulse_period;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// no pulse data in push buffer (pulse off or idle or callback is not defined)
|
|
||||||
dma.current[dma.rw_pos++] = atomic_load(&i2s_port_data);
|
|
||||||
if (i2s_ioexpander_remain_time_until_next_pulse >= I2S_IOEXP_USEC_PER_PULSE) {
|
|
||||||
i2s_ioexpander_remain_time_until_next_pulse -= I2S_IOEXP_USEC_PER_PULSE;
|
|
||||||
} else {
|
|
||||||
i2s_ioexpander_remain_time_until_next_pulse = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// set filled length to the DMA descriptor
|
|
||||||
dma_desc->length = dma.rw_pos * I2S_SAMPLE_SIZE;
|
|
||||||
} else {
|
|
||||||
// Stepper not started or stopped
|
|
||||||
// (just set current I/O port bits to the buffer)
|
|
||||||
uint32_t port_data = atomic_load(&i2s_port_data);
|
|
||||||
for (int i = 0; i < DMA_SAMPLE_COUNT; i++) {
|
|
||||||
dma.current[i] = port_data;
|
|
||||||
}
|
|
||||||
dma.rw_pos = DMA_SAMPLE_COUNT;
|
|
||||||
dma_desc->length = DMA_BUF_LEN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Initialize funtion (external function)
|
// Initialize funtion (external function)
|
||||||
//
|
//
|
||||||
int i2s_ioexpander_init(i2s_ioexpander_init_t &init_param) {
|
int i2s_ioexpander_init(i2s_ioexpander_init_t &init_param) {
|
||||||
|
if (i2s_ioexpander_initialized) {
|
||||||
|
// already initialized
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
// To make sure hardware is enabled before any hardware register operations.
|
// To make sure hardware is enabled before any hardware register operations.
|
||||||
periph_module_reset(PERIPH_I2S0_MODULE);
|
periph_module_reset(PERIPH_I2S0_MODULE);
|
||||||
periph_module_enable(PERIPH_I2S0_MODULE);
|
periph_module_enable(PERIPH_I2S0_MODULE);
|
||||||
|
|
||||||
// Route the i2s pins to the appropriate GPIO
|
// Route the i2s pins to the appropriate GPIO
|
||||||
gpio_matrix_out_check(init_param.data_pin, I2S0O_DATA_OUT23_IDX, 0, 0);
|
i2s_gpio_attach(init_param.ws_pin, init_param.bck_pin, init_param.data_pin);
|
||||||
gpio_matrix_out_check(init_param.bck_pin, I2S0O_BCK_OUT_IDX, 0, 0);
|
|
||||||
gpio_matrix_out_check(init_param.ws_pin, I2S0O_WS_OUT_IDX, 0, 0);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Each i2s transfer will take
|
* Each i2s transfer will take
|
||||||
@@ -390,9 +506,9 @@ int i2s_ioexpander_init(i2s_ioexpander_init_t &init_param) {
|
|||||||
I2S0.conf.rx_reset = 0;
|
I2S0.conf.rx_reset = 0;
|
||||||
|
|
||||||
//reset dma
|
//reset dma
|
||||||
I2S0.lc_conf.in_rst = 1;
|
I2S0.lc_conf.in_rst = 1; // Set this bit to reset in DMA FSM. (R/W)
|
||||||
I2S0.lc_conf.in_rst = 0;
|
I2S0.lc_conf.in_rst = 0;
|
||||||
I2S0.lc_conf.out_rst = 1;
|
I2S0.lc_conf.out_rst = 1; // Set this bit to reset out DMA FSM. (R/W)
|
||||||
I2S0.lc_conf.out_rst = 0;
|
I2S0.lc_conf.out_rst = 0;
|
||||||
|
|
||||||
//Enable and configure DMA
|
//Enable and configure DMA
|
||||||
@@ -426,7 +542,7 @@ int i2s_ioexpander_init(i2s_ioexpander_init_t &init_param) {
|
|||||||
I2S0.conf.rx_start = 0;
|
I2S0.conf.rx_start = 0;
|
||||||
|
|
||||||
I2S0.conf.tx_msb_right = 1; // Set this bit to place right-channel data at the MSB in the transmit FIFO.
|
I2S0.conf.tx_msb_right = 1; // Set this bit to place right-channel data at the MSB in the transmit FIFO.
|
||||||
I2S0.conf.tx_right_first = 1; // Set this bit to transmit right-channel data first.
|
I2S0.conf.tx_right_first = 0; // Set this bit to transmit right-channel data first.
|
||||||
|
|
||||||
I2S0.conf.tx_slave_mod = 0; // Master
|
I2S0.conf.tx_slave_mod = 0; // Master
|
||||||
I2S0.fifo_conf.tx_fifo_mod_force_en = 1; //The bit should always be set to 1.
|
I2S0.fifo_conf.tx_fifo_mod_force_en = 1; //The bit should always be set to 1.
|
||||||
@@ -484,9 +600,16 @@ int i2s_ioexpander_init(i2s_ioexpander_init_t &init_param) {
|
|||||||
esp_intr_alloc(ETS_I2S0_INTR_SOURCE, 0, i2s_intr_handler_default, nullptr, &i2s_isr_handle);
|
esp_intr_alloc(ETS_I2S0_INTR_SOURCE, 0, i2s_intr_handler_default, nullptr, &i2s_isr_handle);
|
||||||
esp_intr_enable(i2s_isr_handle);
|
esp_intr_enable(i2s_isr_handle);
|
||||||
|
|
||||||
|
// Remember GPIO pin numbers
|
||||||
|
i2s_ioexpander_ws_pin = init_param.ws_pin;
|
||||||
|
i2s_ioexpander_bck_pin = init_param.bck_pin;
|
||||||
|
i2s_ioexpander_data_pin = init_param.data_pin;
|
||||||
|
i2s_ioexpander_initialized = 1;
|
||||||
|
|
||||||
// Start the I2S peripheral
|
// Start the I2S peripheral
|
||||||
i2s_start();
|
i2s_start();
|
||||||
i2s_ioexpander_status = RUNNING;
|
i2s_ioexpander_status = RUNNING;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@@ -93,7 +93,9 @@ typedef struct {
|
|||||||
bck _~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~...
|
bck _~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~...
|
||||||
data vutsrqponmlkjihgfedcba9876543210vutsrqponmlkjihgfedcba9876543210____...
|
data vutsrqponmlkjihgfedcba9876543210vutsrqponmlkjihgfedcba9876543210____...
|
||||||
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||||
^Latches the X bits when ws is switched to High
|
^
|
||||||
|
Latches the X bits when ws is switched to High
|
||||||
|
|
||||||
bit0:Extended GPIO 128, 1: Extended GPIO 129, ..., v: Extended GPIO 159
|
bit0:Extended GPIO 128, 1: Extended GPIO 129, ..., v: Extended GPIO 159
|
||||||
(data at LEFT Channel will ignored by shift-register IC)
|
(data at LEFT Channel will ignored by shift-register IC)
|
||||||
*/
|
*/
|
||||||
@@ -107,6 +109,8 @@ typedef struct {
|
|||||||
/*
|
/*
|
||||||
Initialize I2S and DMA for the stepper bitstreamer
|
Initialize I2S and DMA for the stepper bitstreamer
|
||||||
use I2S0, I2S0 isr, DMA, and FIFO(xQueue).
|
use I2S0, I2S0 isr, DMA, and FIFO(xQueue).
|
||||||
|
|
||||||
|
return -1 ... already initialized
|
||||||
*/
|
*/
|
||||||
int i2s_ioexpander_init(i2s_ioexpander_init_t &init_param);
|
int i2s_ioexpander_init(i2s_ioexpander_init_t &init_param);
|
||||||
|
|
||||||
@@ -166,6 +170,15 @@ int i2s_ioexpander_set_pulse_period(uint32_t period);
|
|||||||
*/
|
*/
|
||||||
int i2s_ioexpander_register_pulse_callback(i2s_ioexpander_pulse_phase_func_t func);
|
int i2s_ioexpander_register_pulse_callback(i2s_ioexpander_pulse_phase_func_t func);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Reset i2s I/O expander
|
||||||
|
- Stop ISR/DMA
|
||||||
|
- Clear DMA buffer with the current expanded GPIO bits
|
||||||
|
- Retart ISR/DMA
|
||||||
|
*/
|
||||||
|
int i2s_ioexpander_reset();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@@ -449,6 +449,9 @@ void mc_reset() {
|
|||||||
}
|
}
|
||||||
#ifdef USE_GANGED_AXES
|
#ifdef USE_GANGED_AXES
|
||||||
ganged_mode = SQUARING_MODE_DUAL; // in case an error occurred during squaring
|
ganged_mode = SQUARING_MODE_DUAL; // in case an error occurred during squaring
|
||||||
|
#endif
|
||||||
|
#ifdef USE_I2S_IOEXPANDER
|
||||||
|
i2s_ioexpander_reset();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user