1
0
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:
odaki
2020-04-25 21:16:16 +09:00
parent d1ef2d620b
commit 5f16050312
4 changed files with 290 additions and 151 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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
/*

View File

@@ -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
}
}