From b72cd4c727a71c90fd0040fa7392c6616f8d8a81 Mon Sep 17 00:00:00 2001 From: Mitch Bradley Date: Fri, 16 Oct 2020 14:13:21 -1000 Subject: [PATCH] Use less memory (#644) a) closeFile() now does SD.end() to release memory after running a file from SD. b) Several task stacks are smaller c) All tasks now check their free space if DEBUG_REPORT_STACK_FREE is defined. platformio.ini has a commented-out line that can be uncommented to turn that on. d) Similarly, platformio.ini can turn on DEBUG_REPORT_HEAP_SIZE e) Fixed a small leak that occurred when listing local files. With these changes, the heap size tends to hover around 53K, dropping to about 37K when running a file from SD. --- Grbl_Esp32/src/I2SOut.cpp | 251 ++++++++++++----------- Grbl_Esp32/src/Limits.cpp | 13 +- Grbl_Esp32/src/Motors/Servo.cpp | 10 +- Grbl_Esp32/src/Motors/TrinamicDriver.cpp | 9 +- Grbl_Esp32/src/Report.cpp | 10 + Grbl_Esp32/src/Report.h | 2 + Grbl_Esp32/src/SDCard.cpp | 3 +- Grbl_Esp32/src/Serial.cpp | 36 +++- Grbl_Esp32/src/Spindles/VFDSpindle.cpp | 3 + Grbl_Esp32/src/System.cpp | 3 + Grbl_Esp32/src/System.h | 2 - Grbl_Esp32/src/WebUI/WebSettings.cpp | 52 +++-- platformio.ini | 2 + 13 files changed, 221 insertions(+), 175 deletions(-) diff --git a/Grbl_Esp32/src/I2SOut.cpp b/Grbl_Esp32/src/I2SOut.cpp index 471731d0..d41fed02 100644 --- a/Grbl_Esp32/src/I2SOut.cpp +++ b/Grbl_Esp32/src/I2SOut.cpp @@ -42,19 +42,27 @@ */ #include "Config.h" -# include -# include -# include -# include -# include +// This block of #includes is necessary for Report.h +#include "Error.h" +#include "WebUI/Authentication.h" +#include "WebUI/ESPResponse.h" +#include "Probe.h" +#include "System.h" +#include "Report.h" -# include +#include +#include +#include +#include +#include -# include "Pins.h" -# include "I2SOut.h" +#include + +#include "Pins.h" +#include "I2SOut.h" // Always enable I2S streaming logic -# define USE_I2S_OUT_STREAM_IMPL +#define USE_I2S_OUT_STREAM_IMPL // // Configrations for DMA connected I2S @@ -76,7 +84,7 @@ const int I2S_SAMPLE_SIZE = 4; /* 4 bytes, const int DMA_SAMPLE_COUNT = I2S_OUT_DMABUF_LEN / I2S_SAMPLE_SIZE; /* number of samples per buffer */ const int SAMPLE_SAFE_COUNT = (20 / I2S_OUT_USEC_PER_PULSE); /* prevent buffer overrun (GRBL's $0 should be less than or equal 20) */ -# ifdef USE_I2S_OUT_STREAM_IMPL +#ifdef USE_I2S_OUT_STREAM_IMPL typedef struct { uint32_t** buffers; uint32_t* current; @@ -87,39 +95,39 @@ typedef struct { static i2s_out_dma_t o_dma; static intr_handle_t i2s_out_isr_handle; -# endif +#endif // output value static atomic_uint_least32_t i2s_out_port_data = ATOMIC_VAR_INIT(0); // inner lock static portMUX_TYPE i2s_out_spinlock = portMUX_INITIALIZER_UNLOCKED; -# define I2S_OUT_ENTER_CRITICAL() \ - do { \ - if (xPortInIsrContext()) { \ - portENTER_CRITICAL_ISR(&i2s_out_spinlock); \ - } else { \ - portENTER_CRITICAL(&i2s_out_spinlock); \ - } \ - } while (0) -# define I2S_OUT_EXIT_CRITICAL() \ - do { \ - if (xPortInIsrContext()) { \ - portEXIT_CRITICAL_ISR(&i2s_out_spinlock); \ - } else { \ - portEXIT_CRITICAL(&i2s_out_spinlock); \ - } \ - } while (0) -# define I2S_OUT_ENTER_CRITICAL_ISR() portENTER_CRITICAL_ISR(&i2s_out_spinlock) -# define I2S_OUT_EXIT_CRITICAL_ISR() portEXIT_CRITICAL_ISR(&i2s_out_spinlock) +#define I2S_OUT_ENTER_CRITICAL() \ + do { \ + if (xPortInIsrContext()) { \ + portENTER_CRITICAL_ISR(&i2s_out_spinlock); \ + } else { \ + portENTER_CRITICAL(&i2s_out_spinlock); \ + } \ + } while (0) +#define I2S_OUT_EXIT_CRITICAL() \ + do { \ + if (xPortInIsrContext()) { \ + portEXIT_CRITICAL_ISR(&i2s_out_spinlock); \ + } else { \ + portEXIT_CRITICAL(&i2s_out_spinlock); \ + } \ + } while (0) +#define I2S_OUT_ENTER_CRITICAL_ISR() portENTER_CRITICAL_ISR(&i2s_out_spinlock) +#define I2S_OUT_EXIT_CRITICAL_ISR() portEXIT_CRITICAL_ISR(&i2s_out_spinlock) static int i2s_out_initialized = 0; -# ifdef USE_I2S_OUT_STREAM_IMPL +#ifdef USE_I2S_OUT_STREAM_IMPL static volatile uint32_t i2s_out_pulse_period; static uint32_t i2s_out_remain_time_until_next_pulse; // Time remaining until the next pulse (μsec) static volatile i2s_out_pulse_func_t i2s_out_pulse_func; -# endif +#endif static uint8_t i2s_out_ws_pin = 255; static uint8_t i2s_out_bck_pin = 255; @@ -129,24 +137,24 @@ static volatile i2s_out_pulser_status_t i2s_out_pulser_status = PASSTHROUGH; // outer lock static portMUX_TYPE i2s_out_pulser_spinlock = portMUX_INITIALIZER_UNLOCKED; -# define I2S_OUT_PULSER_ENTER_CRITICAL() \ - do { \ - if (xPortInIsrContext()) { \ - portENTER_CRITICAL_ISR(&i2s_out_pulser_spinlock); \ - } else { \ - portENTER_CRITICAL(&i2s_out_pulser_spinlock); \ - } \ - } while (0) -# define I2S_OUT_PULSER_EXIT_CRITICAL() \ - do { \ - if (xPortInIsrContext()) { \ - portEXIT_CRITICAL_ISR(&i2s_out_pulser_spinlock); \ - } else { \ - portEXIT_CRITICAL(&i2s_out_pulser_spinlock); \ - } \ - } while (0) -# define I2S_OUT_PULSER_ENTER_CRITICAL_ISR() portENTER_CRITICAL_ISR(&i2s_out_pulser_spinlock) -# define I2S_OUT_PULSER_EXIT_CRITICAL_ISR() portEXIT_CRITICAL_ISR(&i2s_out_pulser_spinlock) +#define I2S_OUT_PULSER_ENTER_CRITICAL() \ + do { \ + if (xPortInIsrContext()) { \ + portENTER_CRITICAL_ISR(&i2s_out_pulser_spinlock); \ + } else { \ + portENTER_CRITICAL(&i2s_out_pulser_spinlock); \ + } \ + } while (0) +#define I2S_OUT_PULSER_EXIT_CRITICAL() \ + do { \ + if (xPortInIsrContext()) { \ + portEXIT_CRITICAL_ISR(&i2s_out_pulser_spinlock); \ + } else { \ + portEXIT_CRITICAL(&i2s_out_pulser_spinlock); \ + } \ + } while (0) +#define I2S_OUT_PULSER_ENTER_CRITICAL_ISR() portENTER_CRITICAL_ISR(&i2s_out_pulser_spinlock) +#define I2S_OUT_PULSER_EXIT_CRITICAL_ISR() portEXIT_CRITICAL_ISR(&i2s_out_pulser_spinlock) // // Internal functions @@ -161,13 +169,13 @@ static inline void gpio_matrix_out_check(uint8_t gpio, uint32_t signal_idx, bool } static inline void i2s_out_single_data() { -# if I2S_OUT_NUM_BITS == 16 +#if I2S_OUT_NUM_BITS == 16 uint32_t port_data = atomic_load(&i2s_out_port_data); port_data <<= 16; // Shift needed. This specification is not spelled out in the manual. I2S0.conf_single_data = port_data; // Apply port data in real-time (static I2S) -# else +#else I2S0.conf_single_data = atomic_load(&i2s_out_port_data); // Apply port data in real-time (static I2S) -# endif +#endif } static inline void i2s_out_reset_fifo_without_lock() { @@ -183,7 +191,7 @@ static void IRAM_ATTR i2s_out_reset_fifo() { I2S_OUT_EXIT_CRITICAL(); } -# ifdef USE_I2S_OUT_STREAM_IMPL +#ifdef USE_I2S_OUT_STREAM_IMPL static int IRAM_ATTR i2s_clear_dma_buffer(lldesc_t* dma_desc, uint32_t port_data) { uint32_t* buf = (uint32_t*)dma_desc->buf; for (int i = 0; i < DMA_SAMPLE_COUNT; i++) { @@ -210,7 +218,7 @@ static int IRAM_ATTR i2s_clear_o_dma_buffers(uint32_t port_data) { } return 0; } -# endif +#endif static int IRAM_ATTR i2s_out_gpio_attach(uint8_t ws, uint8_t bck, uint8_t data) { // Route the i2s pins to the appropriate GPIO @@ -243,13 +251,13 @@ static int IRAM_ATTR i2s_out_gpio_shiftout(uint32_t port_data) { static int IRAM_ATTR i2s_out_stop() { I2S_OUT_ENTER_CRITICAL(); -# ifdef USE_I2S_OUT_STREAM_IMPL +#ifdef USE_I2S_OUT_STREAM_IMPL // 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) -# endif +#endif // stop TX module I2S0.conf.tx_start = 0; @@ -269,10 +277,10 @@ static int IRAM_ATTR i2s_out_stop() { uint32_t port_data = atomic_load(&i2s_out_port_data); // current expanded port value i2s_out_gpio_shiftout(port_data); -# ifdef USE_I2S_OUT_STREAM_IMPL +#ifdef USE_I2S_OUT_STREAM_IMPL //clear pending interrupt I2S0.int_clr.val = I2S0.int_st.val; -# endif +#endif I2S_OUT_EXIT_CRITICAL(); return 0; } @@ -296,7 +304,7 @@ static int IRAM_ATTR i2s_out_start() { I2S0.conf.rx_reset = 1; I2S0.conf.rx_reset = 0; -# ifdef USE_I2S_OUT_STREAM_IMPL +#ifdef USE_I2S_OUT_STREAM_IMPL // reset DMA I2S0.lc_conf.in_rst = 1; I2S0.lc_conf.in_rst = 0; @@ -304,13 +312,13 @@ static int IRAM_ATTR i2s_out_start() { I2S0.lc_conf.out_rst = 0; I2S0.out_link.addr = (uint32_t)o_dma.desc[0]; -# endif +#endif // reset FIFO i2s_out_reset_fifo_without_lock(); // start DMA link -# ifdef USE_I2S_OUT_STREAM_IMPL +#ifdef USE_I2S_OUT_STREAM_IMPL if (i2s_out_pulser_status == PASSTHROUGH) { I2S0.conf_chan.tx_chan_mod = 3; // 3:right+constant 4:left+constant (when tx_msb_right = 1) I2S0.conf_single_data = port_data; @@ -318,17 +326,17 @@ static int IRAM_ATTR i2s_out_start() { I2S0.conf_chan.tx_chan_mod = 4; // 3:right+constant 4:left+constant (when tx_msb_right = 1) I2S0.conf_single_data = 0; } -# endif +#endif I2S0.conf1.tx_stop_en = 1; // BCK and WCK are suppressed while FIFO is empty -# ifdef USE_I2S_OUT_STREAM_IMPL +#ifdef USE_I2S_OUT_STREAM_IMPL // 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; -# endif +#endif I2S0.conf.tx_start = 1; // Wait for the first FIFO data to prevent the unintentional generation of 0 data ets_delay_us(20); @@ -339,7 +347,7 @@ static int IRAM_ATTR i2s_out_start() { return 0; } -# ifdef USE_I2S_OUT_STREAM_IMPL +#ifdef USE_I2S_OUT_STREAM_IMPL // Fill out one DMA buffer // Call with the I2S_OUT_PULSER lock acquired. // Note that the lock is temporarily released while calling the callback function. @@ -516,15 +524,18 @@ static void IRAM_ATTR i2sOutTask(void* parameter) { o_dma.rw_pos = 0; // If someone calls i2s_out_push_sample, make sure there is no buffer overflow } I2S_OUT_PULSER_EXIT_CRITICAL(); // Unlock pulser status + + static UBaseType_t uxHighWaterMark = 0; + reportTaskStackSize(uxHighWaterMark); } } -# endif +#endif // // External funtions // void IRAM_ATTR i2s_out_delay() { -# ifdef USE_I2S_OUT_STREAM_IMPL +#ifdef USE_I2S_OUT_STREAM_IMPL I2S_OUT_PULSER_ENTER_CRITICAL(); if (i2s_out_pulser_status == PASSTHROUGH) { // Depending on the timing, it may not be reflected immediately, @@ -536,9 +547,9 @@ void IRAM_ATTR i2s_out_delay() { delay(I2S_OUT_DELAY_MS); } I2S_OUT_PULSER_EXIT_CRITICAL(); -# else +#else ets_delay_us(I2S_OUT_USEC_PER_PULSE * 2); -# endif +#endif } void IRAM_ATTR i2s_out_write(uint8_t pin, uint8_t val) { @@ -548,15 +559,15 @@ void IRAM_ATTR i2s_out_write(uint8_t pin, uint8_t val) { } else { atomic_fetch_and(&i2s_out_port_data, ~bit); } -# ifdef USE_I2S_OUT_STREAM_IMPL +#ifdef USE_I2S_OUT_STREAM_IMPL // It needs a lock for access, but I've given up because I need speed. // This is not a problem as long as there is no overlap between the status change and digitalWrite(). if (i2s_out_pulser_status == PASSTHROUGH) { i2s_out_single_data(); } -# else +#else i2s_out_single_data(); -# endif +#endif } uint8_t IRAM_ATTR i2s_out_read(uint8_t pin) { @@ -565,9 +576,9 @@ uint8_t IRAM_ATTR i2s_out_read(uint8_t pin) { } uint32_t IRAM_ATTR i2s_out_push_sample(uint32_t usec) { - uint32_t num = usec/I2S_OUT_USEC_PER_PULSE; + uint32_t num = usec / I2S_OUT_USEC_PER_PULSE; -# ifdef USE_I2S_OUT_STREAM_IMPL +#ifdef USE_I2S_OUT_STREAM_IMPL if (num > SAMPLE_SAFE_COUNT) { return 0; } @@ -579,9 +590,9 @@ uint32_t IRAM_ATTR i2s_out_push_sample(uint32_t usec) { n++; } while (n < num); return n; -# else +#else return 0; -# endif +#endif } i2s_out_pulser_status_t IRAM_ATTR i2s_out_get_pulser_status() { @@ -593,7 +604,7 @@ i2s_out_pulser_status_t IRAM_ATTR i2s_out_get_pulser_status() { int IRAM_ATTR i2s_out_set_passthrough() { I2S_OUT_PULSER_ENTER_CRITICAL(); -# ifdef USE_I2S_OUT_STREAM_IMPL +#ifdef USE_I2S_OUT_STREAM_IMPL // Triggers a change of mode if it is compiled to use I2S stream. // The mode is not changed directly by this function. // Pull the trigger @@ -604,17 +615,17 @@ int IRAM_ATTR i2s_out_set_passthrough() { // (i2sOutTask() -> stepper_pulse_func() -> st_go_idle() -> Stepper_Timer_Stop() -> this function) // And only i2sOutTask() can change the state to PASSTHROUGH. // So, to change the state, you need to return to i2sOutTask() as soon as possible. -# else +#else // If it is compiled to not use I2S streams, change the mode directly. i2s_out_pulser_status = PASSTHROUGH; -# endif +#endif I2S_OUT_PULSER_EXIT_CRITICAL(); return 0; } int IRAM_ATTR i2s_out_set_stepping() { I2S_OUT_PULSER_ENTER_CRITICAL(); -# ifdef USE_I2S_OUT_STREAM_IMPL +#ifdef USE_I2S_OUT_STREAM_IMPL if (i2s_out_pulser_status == STEPPING) { // Re-entered (fail safe) I2S_OUT_PULSER_EXIT_CRITICAL(); @@ -650,31 +661,31 @@ int IRAM_ATTR i2s_out_set_stepping() { // because the process in i2s_out_start() is different depending on the status. i2s_out_pulser_status = STEPPING; i2s_out_start(); -# else +#else i2s_out_pulser_status = STEPPING; -# endif +#endif I2S_OUT_PULSER_EXIT_CRITICAL(); return 0; } int IRAM_ATTR i2s_out_set_pulse_period(uint32_t period) { -# ifdef USE_I2S_OUT_STREAM_IMPL +#ifdef USE_I2S_OUT_STREAM_IMPL i2s_out_pulse_period = period; -# endif +#endif return 0; } int IRAM_ATTR i2s_out_set_pulse_callback(i2s_out_pulse_func_t func) { -# ifdef USE_I2S_OUT_STREAM_IMPL +#ifdef USE_I2S_OUT_STREAM_IMPL i2s_out_pulse_func = func; -# endif +#endif return 0; } int IRAM_ATTR i2s_out_reset() { I2S_OUT_PULSER_ENTER_CRITICAL(); i2s_out_stop(); -# ifdef USE_I2S_OUT_STREAM_IMPL +#ifdef USE_I2S_OUT_STREAM_IMPL if (i2s_out_pulser_status == STEPPING) { uint32_t port_data = atomic_load(&i2s_out_port_data); i2s_clear_o_dma_buffers(port_data); @@ -682,7 +693,7 @@ int IRAM_ATTR i2s_out_reset() { i2s_clear_o_dma_buffers(0); i2s_out_pulser_status = PASSTHROUGH; } -# endif +#endif // You need to set the status before calling i2s_out_start() // because the process in i2s_out_start() is different depending on the status. i2s_out_start(); @@ -733,7 +744,7 @@ int IRAM_ATTR i2s_out_init(i2s_out_init_t& init_param) { * M = 2 */ -# ifdef USE_I2S_OUT_STREAM_IMPL +#ifdef USE_I2S_OUT_STREAM_IMPL // Allocate the array of pointers to the buffers o_dma.buffers = (uint32_t**)malloc(sizeof(uint32_t*) * I2S_OUT_DMABUF_COUNT); if (o_dma.buffers == nullptr) { @@ -770,7 +781,7 @@ int IRAM_ATTR i2s_out_init(i2s_out_init_t& init_param) { // Set the first DMA descriptor I2S0.out_link.addr = (uint32_t)o_dma.desc[0]; -# endif +#endif // stop i2s I2S0.out_link.stop = 1; @@ -806,9 +817,9 @@ int IRAM_ATTR i2s_out_init(i2s_out_init_t& init_param) { I2S0.lc_conf.outdscr_burst_en = 0; I2S0.lc_conf.out_no_restart_clr = 0; I2S0.lc_conf.indscr_burst_en = 0; -# ifdef USE_I2S_OUT_STREAM_IMPL +#ifdef USE_I2S_OUT_STREAM_IMPL I2S0.lc_conf.out_eof_mode = 1; // I2S_OUT_EOF_INT generated when DMA has popped all data from the FIFO; -# endif +#endif I2S0.conf2.lcd_en = 0; I2S0.conf2.camera_en = 0; I2S0.pdm_conf.pcm2pdm_conv_en = 0; @@ -816,7 +827,7 @@ int IRAM_ATTR i2s_out_init(i2s_out_init_t& init_param) { I2S0.fifo_conf.dscr_en = 0; -# ifdef USE_I2S_OUT_STREAM_IMPL +#ifdef USE_I2S_OUT_STREAM_IMPL if (i2s_out_pulser_status == STEPPING) { // Stream output mode I2S0.conf_chan.tx_chan_mod = 4; // 3:right+constant 4:left+constant (when tx_msb_right = 1) @@ -826,31 +837,31 @@ int IRAM_ATTR i2s_out_init(i2s_out_init_t& init_param) { I2S0.conf_chan.tx_chan_mod = 3; // 3:right+constant 4:left+constant (when tx_msb_right = 1) I2S0.conf_single_data = init_param.init_val; } -# else +#else // For the static output mode I2S0.conf_chan.tx_chan_mod = 3; // 3:right+constant 4:left+constant (when tx_msb_right = 1) I2S0.conf_single_data = init_param.init_val; // initial constant value -# endif -# if I2S_OUT_NUM_BITS == 16 +#endif +#if I2S_OUT_NUM_BITS == 16 I2S0.fifo_conf.tx_fifo_mod = 0; // 0: 16-bit dual channel data, 3: 32-bit single channel data I2S0.fifo_conf.rx_fifo_mod = 0; // 0: 16-bit dual channel data, 3: 32-bit single channel data I2S0.sample_rate_conf.tx_bits_mod = 16; // default is 16-bits I2S0.sample_rate_conf.rx_bits_mod = 16; // default is 16-bits -# else +#else I2S0.fifo_conf.tx_fifo_mod = 3; // 0: 16-bit dual channel data, 3: 32-bit single channel data I2S0.fifo_conf.rx_fifo_mod = 3; // 0: 16-bit dual channel data, 3: 32-bit single channel data // Data width is 32-bit. Forgetting this setting will result in a 16-bit transfer. I2S0.sample_rate_conf.tx_bits_mod = 32; I2S0.sample_rate_conf.rx_bits_mod = 32; -# endif +#endif I2S0.conf.tx_mono = 0; // Set this bit to enable transmitter’s mono mode in PCM standard mode. I2S0.conf_chan.rx_chan_mod = 1; // 1: right+right I2S0.conf.rx_mono = 0; -# ifdef USE_I2S_OUT_STREAM_IMPL +#ifdef USE_I2S_OUT_STREAM_IMPL I2S0.fifo_conf.dscr_en = 1; //connect DMA to fifo -# endif +#endif I2S0.conf.tx_start = 0; I2S0.conf.rx_start = 0; @@ -858,9 +869,9 @@ int IRAM_ATTR i2s_out_init(i2s_out_init_t& init_param) { I2S0.conf.tx_right_first = 0; // Setting this bit allows the right-channel data to be sent first. I2S0.conf.tx_slave_mod = 0; // Master -# ifdef USE_I2S_OUT_STREAM_IMPL +#ifdef USE_I2S_OUT_STREAM_IMPL I2S0.fifo_conf.tx_fifo_mod_force_en = 1; //The bit should always be set to 1. -# endif +#endif I2S0.pdm_conf.rx_pdm_en = 0; // Set this bit to enable receiver’s PDM mode. I2S0.pdm_conf.tx_pdm_en = 0; // Set this bit to enable transmitter’s PDM mode. @@ -877,13 +888,13 @@ int IRAM_ATTR i2s_out_init(i2s_out_init_t& init_param) { // set clock (fi2s) 160MHz / 5 I2S0.clkm_conf.clka_en = 0; // Use 160 MHz PLL_D2_CLK as reference // N + b/a = 0 -# if I2S_OUT_NUM_BITS == 16 +#if I2S_OUT_NUM_BITS == 16 // N = 10 I2S0.clkm_conf.clkm_div_num = 10; // minimum value of 2, reset value of 4, max 256 (I²S clock divider’s integral value) -# else +#else // N = 5 I2S0.clkm_conf.clkm_div_num = 5; // minimum value of 2, reset value of 4, max 256 (I²S clock divider’s integral value) -# endif +#endif // b/a = 0 I2S0.clkm_conf.clkm_div_b = 0; // 0 at reset I2S0.clkm_conf.clkm_div_a = 0; // 0 at reset, what about divide by 0? (not an issue) @@ -893,7 +904,7 @@ int IRAM_ATTR i2s_out_init(i2s_out_init_t& init_param) { I2S0.sample_rate_conf.tx_bck_div_num = 2; // minimum value of 2 defaults to 6 I2S0.sample_rate_conf.rx_bck_div_num = 2; -# ifdef USE_I2S_OUT_STREAM_IMPL +#ifdef USE_I2S_OUT_STREAM_IMPL // Enable TX interrupts (DMA Interrupts) I2S0.int_ena.out_eof = 1; // Triggered when rxlink has finished sending a packet. I2S0.int_ena.out_dscr_err = 0; // Triggered when invalid rxlink descriptors are encountered. @@ -907,7 +918,7 @@ int IRAM_ATTR i2s_out_init(i2s_out_init_t& init_param) { // Create the task that will feed the buffer xTaskCreatePinnedToCore(i2sOutTask, "I2SOutTask", - 1024 * 10, + 4096, NULL, 1, nullptr, @@ -917,7 +928,7 @@ int IRAM_ATTR i2s_out_init(i2s_out_init_t& init_param) { // Allocate and Enable the I2S interrupt esp_intr_alloc(ETS_I2S0_INTR_SOURCE, 0, i2s_out_intr_handler, nullptr, &i2s_out_isr_handle); esp_intr_enable(i2s_out_isr_handle); -# endif +#endif // Remember GPIO pin numbers i2s_out_ws_pin = init_param.ws_pin; @@ -931,18 +942,18 @@ int IRAM_ATTR i2s_out_init(i2s_out_init_t& init_param) { return 0; } -# ifndef I2S_OUT_WS -# define I2S_OUT_WS GPIO_NUM_17 -# endif -# ifndef I2S_OUT_BCK -# define I2S_OUT_BCK GPIO_NUM_22 -# endif -# ifndef I2S_OUT_DATA -# define I2S_OUT_DATA GPIO_NUM_21 -# endif -# ifndef I2S_OUT_INIT_VAL -# define I2S_OUT_INIT_VAL 0 -# endif +#ifndef I2S_OUT_WS +# define I2S_OUT_WS GPIO_NUM_17 +#endif +#ifndef I2S_OUT_BCK +# define I2S_OUT_BCK GPIO_NUM_22 +#endif +#ifndef I2S_OUT_DATA +# define I2S_OUT_DATA GPIO_NUM_21 +#endif +#ifndef I2S_OUT_INIT_VAL +# define I2S_OUT_INIT_VAL 0 +#endif /* Initialize I2S out by default parameters. diff --git a/Grbl_Esp32/src/Limits.cpp b/Grbl_Esp32/src/Limits.cpp index bbab9989..2aed18b7 100644 --- a/Grbl_Esp32/src/Limits.cpp +++ b/Grbl_Esp32/src/Limits.cpp @@ -330,11 +330,8 @@ void limits_init() { } if (limit_sw_queue == NULL) { - grbl_msg_sendf(CLIENT_SERIAL, - MsgLevel::Info, - "%s limit switch on pin %s", - reportAxisNameMsg(axis, gang_index), - pinName(pin).c_str()); + grbl_msg_sendf( + CLIENT_SERIAL, MsgLevel::Info, "%s limit switch on pin %s", reportAxisNameMsg(axis, gang_index), pinName(pin).c_str()); } } } @@ -427,17 +424,19 @@ void limitCheckTask(void* pvParameters) { mc_reset(); // Initiate system kill. sys_rt_exec_alarm = ExecAlarm::HardLimit; // Indicate hard limit critical event } + static UBaseType_t uxHighWaterMark = 0; + reportTaskStackSize(uxHighWaterMark); } } float limitsMaxPosition(uint8_t axis) { - float mpos = axis_settings[axis]->home_mpos->get(); + float mpos = axis_settings[axis]->home_mpos->get(); return bitnum_istrue(homing_dir_mask->get(), axis) ? mpos + axis_settings[axis]->max_travel->get() : mpos; } float limitsMinPosition(uint8_t axis) { - float mpos = axis_settings[axis]->home_mpos->get(); + float mpos = axis_settings[axis]->home_mpos->get(); return bitnum_istrue(homing_dir_mask->get(), axis) ? mpos : mpos - axis_settings[axis]->max_travel->get(); } diff --git a/Grbl_Esp32/src/Motors/Servo.cpp b/Grbl_Esp32/src/Motors/Servo.cpp index 1e0fa2b7..65e213a1 100644 --- a/Grbl_Esp32/src/Motors/Servo.cpp +++ b/Grbl_Esp32/src/Motors/Servo.cpp @@ -60,18 +60,14 @@ namespace Motors { xLastWakeTime = xTaskGetTickCount(); // Initialise the xLastWakeTime variable with the current time. vTaskDelay(2000); // initial delay while (true) { // don't ever return from this or the task dies - - // static UBaseType_t uxHighWaterMark = 0; - // if (uxHighWaterMark != uxTaskGetStackHighWaterMark(NULL)) { - // uxHighWaterMark = uxTaskGetStackHighWaterMark(NULL); - // grbl_msg_sendf(CLIENT_SERIAL, MsgLevel::Info, "Servo Task Min Stack Space: %d", uxHighWaterMark); - // } - for (Servo* p = List; p; p = p->link) { p->update(); } vTaskDelayUntil(&xLastWakeTime, xUpdate); + + static UBaseType_t uxHighWaterMark = 0; + reportTaskStackSize(uxHighWaterMark); } } diff --git a/Grbl_Esp32/src/Motors/TrinamicDriver.cpp b/Grbl_Esp32/src/Motors/TrinamicDriver.cpp index c100b1de..d2c6e595 100644 --- a/Grbl_Esp32/src/Motors/TrinamicDriver.cpp +++ b/Grbl_Esp32/src/Motors/TrinamicDriver.cpp @@ -360,13 +360,10 @@ namespace Motors { } // sys.state } // if mask - // static UBaseType_t uxHighWaterMark = 0; - // if (uxHighWaterMark != uxTaskGetStackHighWaterMark(NULL)) { - // uxHighWaterMark = uxTaskGetStackHighWaterMark(NULL); - // grbl_msg_sendf(CLIENT_SERIAL, MsgLevel::Info, "SG Task Stack Space: %d", uxHighWaterMark); - // } - vTaskDelayUntil(&xLastWakeTime, xreadSg); + + static UBaseType_t uxHighWaterMark = 0; + reportTaskStackSize(uxHighWaterMark); } } } diff --git a/Grbl_Esp32/src/Report.cpp b/Grbl_Esp32/src/Report.cpp index 532ab295..27ba9ea8 100644 --- a/Grbl_Esp32/src/Report.cpp +++ b/Grbl_Esp32/src/Report.cpp @@ -938,3 +938,13 @@ char* reportAxisNameMsg(uint8_t axis) { sprintf(name, "%c Axis", report_get_axis_letter(axis)); return name; } + +void reportTaskStackSize(UBaseType_t& saved) { +#ifdef DEBUG_REPORT_STACK_FREE + UBaseType_t newHighWater = uxTaskGetStackHighWaterMark(NULL); + if (newHighWater != saved) { + saved = newHighWater; + grbl_msg_sendf(CLIENT_SERIAL, MsgLevel::Info, "%s Min Stack Space: %d", pcTaskGetTaskName(NULL), saved); + } +#endif +} diff --git a/Grbl_Esp32/src/Report.h b/Grbl_Esp32/src/Report.h index 6b49ca93..5b128b5d 100644 --- a/Grbl_Esp32/src/Report.h +++ b/Grbl_Esp32/src/Report.h @@ -127,3 +127,5 @@ char report_get_axis_letter(uint8_t axis); char* reportAxisLimitsMsg(uint8_t axis); char* reportAxisNameMsg(uint8_t axis); char* reportAxisNameMsg(uint8_t axis, uint8_t dual_axis); + +void reportTaskStackSize(UBaseType_t& saved); diff --git a/Grbl_Esp32/src/SDCard.cpp b/Grbl_Esp32/src/SDCard.cpp index f675c870..c2d8d3c1 100644 --- a/Grbl_Esp32/src/SDCard.cpp +++ b/Grbl_Esp32/src/SDCard.cpp @@ -81,6 +81,7 @@ boolean closeFile() { SD_ready_next = false; sd_current_line_number = 0; myFile.close(); + SD.end(); return true; } @@ -147,7 +148,7 @@ uint8_t get_sd_state(bool refresh) { sd_state = SDCARD_NOT_PRESENT; //using default value for speed ? should be parameter //refresh content if card was removed - if (SD.begin((GRBL_SPI_SS == -1) ? SS : GRBL_SPI_SS, SPI, GRBL_SPI_FREQ)) { + if (SD.begin((GRBL_SPI_SS == -1) ? SS : GRBL_SPI_SS, SPI, GRBL_SPI_FREQ, "/sd", 2)) { if (SD.cardSize() > 0) { sd_state = SDCARD_IDLE; } diff --git a/Grbl_Esp32/src/Serial.cpp b/Grbl_Esp32/src/Serial.cpp index f3c75676..caa78bb6 100644 --- a/Grbl_Esp32/src/Serial.cpp +++ b/Grbl_Esp32/src/Serial.cpp @@ -68,7 +68,27 @@ uint8_t serial_get_rx_buffer_available(uint8_t client) { return client_buffer[client].availableforwrite(); } +void heapCheckTask(void* pvParameters) { + static uint32_t heapSize = 0; + while (true) { + uint32_t newHeapSize = xPortGetFreeHeapSize(); + if (newHeapSize != heapSize) { + heapSize = newHeapSize; + grbl_msg_sendf(CLIENT_SERIAL, MsgLevel::Info, "heap %d", heapSize); + } + vTaskDelay(3000 / portTICK_RATE_MS); // Yield to other tasks + + static UBaseType_t uxHighWaterMark = 0; + reportTaskStackSize(uxHighWaterMark); + } +} + void serial_init() { +#ifdef DEBUG_REPORT_HEAP_SIZE + // For a 2000-word stack, uxTaskGetStackHighWaterMark reports 288 words available + xTaskCreatePinnedToCore(heapCheckTask, "heapTask", 2000, NULL, 1, NULL, 1); +#endif + Serial.begin(BAUD_RATE); Serial.setRxBufferSize(256); // reset all buffers @@ -76,9 +96,11 @@ void serial_init() { grbl_send(CLIENT_SERIAL, "\r\n"); // create some white space after ESP32 boot info serialCheckTaskHandle = 0; // create a task to check for incoming data + // For a 4096-word stack, uxTaskGetStackHighWaterMark reports 244 words available + // after WebUI attaches. xTaskCreatePinnedToCore(serialCheckTask, // task "serialCheckTask", // name for task - 8192, // size of task stack + 4096, // size of task stack NULL, // parameters 1, // priority &serialCheckTaskHandle, @@ -89,9 +111,10 @@ void serial_init() { // this task runs and checks for data on all interfaces // REaltime stuff is acted upon, then characters are added to the appropriate buffer void serialCheckTask(void* pvParameters) { - uint8_t data = 0; - uint8_t client = CLIENT_ALL; // who sent the data - while (true) { // run continuously + uint8_t data = 0; + uint8_t client = CLIENT_ALL; // who sent the data + static UBaseType_t uxHighWaterMark = 0; + while (true) { // run continuously while (any_client_has_data()) { if (Serial.available()) { client = CLIENT_SERIAL; @@ -149,7 +172,10 @@ void serialCheckTask(void* pvParameters) { WebUI::Serial2Socket.handle_flush(); #endif vTaskDelay(1 / portTICK_RATE_MS); // Yield to other tasks - } // while(true) + + static UBaseType_t uxHighWaterMark = 0; + reportTaskStackSize(uxHighWaterMark); + } // while(true) } void serial_reset_read_buffer(uint8_t client) { diff --git a/Grbl_Esp32/src/Spindles/VFDSpindle.cpp b/Grbl_Esp32/src/Spindles/VFDSpindle.cpp index a5e75fcb..3a1305a6 100644 --- a/Grbl_Esp32/src/Spindles/VFDSpindle.cpp +++ b/Grbl_Esp32/src/Spindles/VFDSpindle.cpp @@ -185,6 +185,9 @@ namespace Spindles { // Wait a bit before we retry. Set the delay to poll-rate. Not sure // if we should use a different value... vTaskDelay(VFD_RS485_POLL_RATE); + + static UBaseType_t uxHighWaterMark = 0; + reportTaskStackSize(uxHighWaterMark); } } diff --git a/Grbl_Esp32/src/System.cpp b/Grbl_Esp32/src/System.cpp index 7686a819..d8d18d00 100644 --- a/Grbl_Esp32/src/System.cpp +++ b/Grbl_Esp32/src/System.cpp @@ -123,6 +123,9 @@ void controlCheckTask(void* pvParameters) { system_exec_control_pin(pins); } debouncing = false; + + static UBaseType_t uxHighWaterMark = 0; + reportTaskStackSize(uxHighWaterMark); } } #endif diff --git a/Grbl_Esp32/src/System.h b/Grbl_Esp32/src/System.h index 83e376e0..e1ea051a 100644 --- a/Grbl_Esp32/src/System.h +++ b/Grbl_Esp32/src/System.h @@ -20,8 +20,6 @@ along with Grbl. If not, see . */ -#include "Grbl.h" - // System states. The state variable primarily tracks the individual functions // of Grbl to manage each without overlapping. It is also used as a messaging flag for // critical events. diff --git a/Grbl_Esp32/src/WebUI/WebSettings.cpp b/Grbl_Esp32/src/WebUI/WebSettings.cpp index 6bd60972..09539a77 100644 --- a/Grbl_Esp32/src/WebUI/WebSettings.cpp +++ b/Grbl_Esp32/src/WebUI/WebSettings.cpp @@ -600,9 +600,9 @@ namespace WebUI { #ifdef ENABLE_WIFI static Error listAPs(char* parameter, AuthenticationLevel auth_level) { // ESP410 - JSONencoder* j = new JSONencoder(espresponse->client() != CLIENT_WEBUI); - j->begin(); - j->begin_array("AP_LIST"); + JSONencoder j(espresponse->client() != CLIENT_WEBUI); + j.begin(); + j.begin_array("AP_LIST"); // An initial async scanNetworks was issued at startup, so there // is a good chance that scan information is already available. int n = WiFi.scanComplete(); @@ -614,12 +614,12 @@ namespace WebUI { break; default: for (int i = 0; i < n; ++i) { - j->begin_object(); - j->member("SSID", WiFi.SSID(i)); - j->member("SIGNAL", wifi_config.getSignal(WiFi.RSSI(i))); - j->member("IS_PROTECTED", WiFi.encryptionType(i) != WIFI_AUTH_OPEN); + j.begin_object(); + j.member("SSID", WiFi.SSID(i)); + j.member("SIGNAL", wifi_config.getSignal(WiFi.RSSI(i))); + j.member("IS_PROTECTED", WiFi.encryptionType(i) != WIFI_AUTH_OPEN); // j->member("IS_PROTECTED", WiFi.encryptionType(i) == WIFI_AUTH_OPEN ? "0" : "1"); - j->end_object(); + j.end_object(); } WiFi.scanDelete(); // Restart the scan in async mode so new data will be available @@ -630,9 +630,8 @@ namespace WebUI { } break; } - j->end_array(); - webPrint(j->end()); - delete j; + j.end_array(); + webPrint(j.end()); if (espresponse->client() != CLIENT_WEBUI) { espresponse->println(""); } @@ -657,17 +656,16 @@ namespace WebUI { } static Error listSettings(char* parameter, AuthenticationLevel auth_level) { // ESP400 - JSONencoder* j = new JSONencoder(espresponse->client() != CLIENT_WEBUI); - j->begin(); - j->begin_array("EEPROM"); + JSONencoder j(espresponse->client() != CLIENT_WEBUI); + j.begin(); + j.begin_array("EEPROM"); for (Setting* js = Setting::List; js; js = js->next()) { if (js->getType() == WEBSET) { - js->addWebui(j); + js->addWebui(&j); } } - j->end_array(); - webPrint(j->end()); - delete j; + j.end_array(); + webPrint(j.end()); return Error::Ok; } @@ -833,15 +831,15 @@ namespace WebUI { } static Error listLocalFilesJSON(char* parameter, AuthenticationLevel auth_level) { // No ESP command - JSONencoder* j = new JSONencoder(espresponse->client() != CLIENT_WEBUI); - j->begin(); - j->begin_array("files"); - listDirJSON(SPIFFS, "/", 4, j); - j->end_array(); - j->member("total", SPIFFS.totalBytes()); - j->member("used", SPIFFS.usedBytes()); - j->member("occupation", String(100 * SPIFFS.usedBytes() / SPIFFS.totalBytes())); - webPrint(j->end()); + JSONencoder j(espresponse->client() != CLIENT_WEBUI); + j.begin(); + j.begin_array("files"); + listDirJSON(SPIFFS, "/", 4, &j); + j.end_array(); + j.member("total", SPIFFS.totalBytes()); + j.member("used", SPIFFS.usedBytes()); + j.member("occupation", String(100 * SPIFFS.usedBytes() / SPIFFS.totalBytes())); + webPrint(j.end()); if (espresponse->client() != CLIENT_WEBUI) { webPrintln(""); } diff --git a/platformio.ini b/platformio.ini index 11ad3c32..cdd2e8a0 100644 --- a/platformio.ini +++ b/platformio.ini @@ -28,6 +28,8 @@ build_flags = -DCORE_DEBUG_LEVEL=0 -Wno-unused-variable -Wno-unused-function + ;-DDEBUG_REPORT_HEAP_SIZE + ;-DDEBUG_REPORT_STACK_FREE [env] lib_deps =