mirror of
https://github.com/bdring/Grbl_Esp32.git
synced 2025-09-01 02:21:46 +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
|
||||
// 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
|
||||
#define LIMIT_MASK B0111
|
||||
//#define LIMIT_MASK B0111
|
||||
|
||||
#define X_LIMIT_PIN GPIO_NUM_34
|
||||
#define Y_LIMIT_PIN GPIO_NUM_35
|
||||
#define Z_LIMIT_PIN GPIO_NUM_32
|
||||
//#define X_LIMIT_PIN GPIO_NUM_34
|
||||
//#define Y_LIMIT_PIN GPIO_NUM_35
|
||||
//#define Z_LIMIT_PIN GPIO_NUM_32
|
||||
|
||||
// Common enable for all steppers. If it is okay to 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
|
||||
// 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_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
|
||||
// ignored by default, to avoid noise problems. To make them
|
||||
// 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
|
||||
// (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_EXIT_CRITICAL() portEXIT_CRITICAL(&i2s_spinlock)
|
||||
|
||||
static int i2s_ioexpander_initialized = 0;
|
||||
|
||||
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 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 {
|
||||
UNKNOWN = 0,
|
||||
RUNNING,
|
||||
@@ -103,7 +109,248 @@ enum i2s_ioexpander_status_t {
|
||||
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) {
|
||||
uint32_t bit = 1UL << pin;
|
||||
@@ -153,158 +400,27 @@ int i2s_ioexpander_register_pulse_callback(i2s_ioexpander_pulse_phase_func_t fun
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
// Internal functions (and init function)
|
||||
//
|
||||
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_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();
|
||||
|
||||
int i2s_ioexpander_reset() {
|
||||
i2s_stop();
|
||||
i2s_clear_dma_buffers();
|
||||
i2s_start();
|
||||
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)
|
||||
//
|
||||
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.
|
||||
periph_module_reset(PERIPH_I2S0_MODULE);
|
||||
periph_module_enable(PERIPH_I2S0_MODULE);
|
||||
|
||||
// Route the i2s pins to the appropriate GPIO
|
||||
gpio_matrix_out_check(init_param.data_pin, I2S0O_DATA_OUT23_IDX, 0, 0);
|
||||
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);
|
||||
i2s_gpio_attach(init_param.ws_pin, init_param.bck_pin, init_param.data_pin);
|
||||
|
||||
/**
|
||||
* Each i2s transfer will take
|
||||
@@ -390,9 +506,9 @@ int i2s_ioexpander_init(i2s_ioexpander_init_t &init_param) {
|
||||
I2S0.conf.rx_reset = 0;
|
||||
|
||||
//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.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;
|
||||
|
||||
//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.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.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_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
|
||||
i2s_start();
|
||||
i2s_ioexpander_status = RUNNING;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
@@ -93,7 +93,9 @@ typedef struct {
|
||||
bck _~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~...
|
||||
data vutsrqponmlkjihgfedcba9876543210vutsrqponmlkjihgfedcba9876543210____...
|
||||
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
|
||||
(data at LEFT Channel will ignored by shift-register IC)
|
||||
*/
|
||||
@@ -107,6 +109,8 @@ typedef struct {
|
||||
/*
|
||||
Initialize I2S and DMA for the stepper bitstreamer
|
||||
use I2S0, I2S0 isr, DMA, and FIFO(xQueue).
|
||||
|
||||
return -1 ... already initialized
|
||||
*/
|
||||
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);
|
||||
|
||||
|
||||
/*
|
||||
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
|
||||
|
||||
/*
|
||||
|
@@ -449,6 +449,9 @@ void mc_reset() {
|
||||
}
|
||||
#ifdef USE_GANGED_AXES
|
||||
ganged_mode = SQUARING_MODE_DUAL; // in case an error occurred during squaring
|
||||
#endif
|
||||
#ifdef USE_I2S_IOEXPANDER
|
||||
i2s_ioexpander_reset();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user