From 14b04d1ea8a6d1fc8ea7a239a47e39dd887ffd95 Mon Sep 17 00:00:00 2001 From: Joseph Coffland Date: Mon, 2 Apr 2018 16:15:38 -0700 Subject: [PATCH] Faster switching of large GCode files in Web. + - Fixed reported gcode line off by one. + - Disable MDI while running. + - Stablized direction pin output during slow moves. --- CHANGELOG.md | 4 +++ src/avr/Makefile.common | 4 +-- src/avr/src/commands.c | 4 +-- src/avr/src/config.h | 2 +- src/avr/src/exec.c | 4 +-- src/avr/src/exec.h | 2 +- src/avr/src/io.c | 2 +- src/avr/src/jog.c | 6 ++-- src/avr/src/line.c | 19 ++++++------ src/avr/src/main.c | 2 +- src/avr/src/motor.c | 8 ++--- src/avr/src/status.h | 2 +- src/avr/src/stepper.c | 2 +- src/avr/src/type.c | 2 +- src/avr/src/vars.c | 2 +- src/avr/step-test/step-test.c | 45 ++++++++++++++++++++++++---- src/jade/templates/control-view.jade | 4 +-- src/js/control-view.js | 11 +++++-- src/js/gcode-viewer.js | 30 +++++++++++-------- src/py/bbctrl/CommandQueue.py | 21 ++++++++++--- src/py/bbctrl/Mach.py | 12 +++++--- src/py/bbctrl/Planner.py | 12 ++++---- src/py/bbctrl/State.py | 4 +-- 23 files changed, 135 insertions(+), 69 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b68f7e5..4c5895c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ Buildbotics CNC Controller Firmware Changelog - Implemented M70-M73 modal state save/restore. - Added support for modbus VFDs. - Start Huanyang spindle with out first pressing Start button on VFD. + - Faster switching of large GCode files in Web. + - Fixed reported gcode line off by one. + - Disable MDI while running. + - Stablized direction pin output during slow moves. ## v0.3.20 - Eliminated drift caused by miscounting half microsteps. diff --git a/src/avr/Makefile.common b/src/avr/Makefile.common index 5810090..65c9480 100644 --- a/src/avr/Makefile.common +++ b/src/avr/Makefile.common @@ -64,11 +64,11 @@ size: $(PROJECT).elf done data-usage: $(PROJECT).elf - avr-nm -S --size-sort -t decimal $(PROJECT).elf | grep ' [BbDd] ' + avr-nm -CS --size-sort -t decimal $(PROJECT).elf | grep ' [BbDd] ' prog-usage: $(PROJECT).elf - avr-nm -S --size-sort -t decimal $(PROJECT).elf | grep -v ' [BbDd] ' + avr-nm -CS --size-sort -t decimal $(PROJECT).elf | grep -v ' [BbDd] ' # Program reset: diff --git a/src/avr/src/commands.c b/src/avr/src/commands.c index fea4a80..8796b09 100644 --- a/src/avr/src/commands.c +++ b/src/avr/src/commands.c @@ -46,7 +46,7 @@ stat_t command_dwell(char *cmd) { static stat_t _dwell_exec() { - exec_set_cb(0); + exec_set_cb(0); // Immediately clear the callback return STAT_OK; } @@ -56,7 +56,7 @@ unsigned command_dwell_size() {return sizeof(float);} void command_dwell_exec(void *seconds) { st_prep_dwell(*(float *)seconds); - exec_set_cb(_dwell_exec); // Necessary evil + exec_set_cb(_dwell_exec); // Command must set an exec callback } diff --git a/src/avr/src/config.h b/src/avr/src/config.h index 648e499..483d6cf 100644 --- a/src/avr/src/config.h +++ b/src/avr/src/config.h @@ -217,7 +217,7 @@ enum { #define VELOCITY_MULTIPLIER 1000.0 #define ACCEL_MULTIPLIER 1000000.0 #define JERK_MULTIPLIER 1000000.0 -#define SYNC_QUEUE_SIZE 4096 +#define SYNC_QUEUE_SIZE 2048 #define EXEC_FILL_TARGET 8 #define EXEC_DELAY 250 // ms #define JOG_STOPPING_UNDERSHOOT 1 // % of stopping distance diff --git a/src/avr/src/exec.c b/src/avr/src/exec.c index e4231a6..3daad84 100644 --- a/src/avr/src/exec.c +++ b/src/avr/src/exec.c @@ -101,7 +101,7 @@ void exec_set_jerk(float j) {ex.jerk = j;} void exec_set_cb(exec_cb_t cb) {ex.cb = cb;} -stat_t exec_move_to_target(float time, const float target[]) { +void exec_move_to_target(float time, const float target[]) { ASSERT(isfinite(time)); ASSERT(isfinite(target[AXIS_X]) && isfinite(target[AXIS_Y]) && isfinite(target[AXIS_Z]) && isfinite(target[AXIS_A]) && @@ -112,8 +112,6 @@ stat_t exec_move_to_target(float time, const float target[]) { // Call the stepper prep function st_prep_line(time, target); - - return STAT_OK; } diff --git a/src/avr/src/exec.h b/src/avr/src/exec.h index db445b6..6dfb930 100644 --- a/src/avr/src/exec.h +++ b/src/avr/src/exec.h @@ -50,5 +50,5 @@ void exec_set_jerk(float j); void exec_set_cb(exec_cb_t cb); -stat_t exec_move_to_target(float time, const float target[]); +void exec_move_to_target(float time, const float target[]); stat_t exec_next(); diff --git a/src/avr/src/io.c b/src/avr/src/io.c index 42b52ce..6cb8146 100644 --- a/src/avr/src/io.c +++ b/src/avr/src/io.c @@ -66,7 +66,7 @@ void io_rtc_callback() { } else result = analog_get(active_cmd.port); // TODO find a better way to send this - printf("{\"result\": %f}\n", (double)result); + printf_P(PSTR("{\"result\": %f}\n"), (double)result); active_cmd.port = -1; } } diff --git a/src/avr/src/jog.c b/src/avr/src/jog.c index 7a7537a..adec1ac 100644 --- a/src/avr/src/jog.c +++ b/src/avr/src/jog.c @@ -109,7 +109,9 @@ stat_t jog_exec() { // Set velocity and target exec_set_velocity(sqrt(velocity_sqr)); - return exec_move_to_target(SEGMENT_TIME, target); + exec_move_to_target(SEGMENT_TIME, target); + + return STAT_OK; } @@ -122,7 +124,7 @@ void jog_stop() { stat_t command_jog(char *cmd) { - // Ignore jog commands when not READY or JOGGING + // Ignore jog commands when not READY and not JOGGING if (state_get() != STATE_READY && state_get() != STATE_JOGGING) return STAT_NOP; diff --git a/src/avr/src/line.c b/src/avr/src/line.c index 2260f1d..9ecd623 100644 --- a/src/avr/src/line.c +++ b/src/avr/src/line.c @@ -144,13 +144,13 @@ static void _done() { } -static stat_t _move(float t, float target[AXES], float v, float a) { +static void _move(float t, float target[AXES], float v, float a) { exec_set_velocity(v); exec_set_acceleration(a); if (seek_switch_found()) state_seek_hold(); - return exec_move_to_target(t, target); + exec_move_to_target(t, target); } @@ -198,7 +198,8 @@ static stat_t _pause() { float oldAccel = exec_get_acceleration(); exec_set_jerk(oldAccel == a ? 0 : (oldAccel < a ? j : -j)); - return _move(SEGMENT_TIME, target, v, a); + _move(SEGMENT_TIME, target, v, a); + return STAT_OK; } @@ -243,18 +244,16 @@ static stat_t _line_exec() { } // Do move & update exec - stat_t status; - if (lastSection && l.stop_section) // Stop exactly on target to correct for floating-point errors - status = _move(seg_time, l.line.target, 0, 0); + _move(seg_time, l.line.target, 0, 0); else { // Compute target position from distance float target[AXES]; _segment_target(target, d); - status = _move(seg_time, target, v, a); + _move(seg_time, target, v, a); l.dist = d; } @@ -264,13 +263,13 @@ static stat_t _line_exec() { _done(); } - return status; + return STAT_OK; } void _print_vector(const char *name, float v[4]) { - printf("%s %f %f %f %f\n", - name, (double)v[0], (double)v[1], (double)v[2], (double)v[3]); + printf_P(PSTR("%s %f %f %f %f\n"), + name, (double)v[0], (double)v[1], (double)v[2], (double)v[3]); } diff --git a/src/avr/src/main.c b/src/avr/src/main.c index 94b3da7..f93bcf5 100644 --- a/src/avr/src/main.c +++ b/src/avr/src/main.c @@ -72,7 +72,7 @@ int main() { sei(); // enable interrupts // Splash - fprintf_P(stdout, PSTR("\n{\"firmware\":\"Buildbotics AVR\"}\n")); + printf_P(PSTR("\n{\"firmware\":\"Buildbotics AVR\"}\n")); // Main loop while (true) { diff --git a/src/avr/src/motor.c b/src/avr/src/motor.c index 5e804fb..1023fb0 100644 --- a/src/avr/src/motor.c +++ b/src/avr/src/motor.c @@ -259,16 +259,16 @@ void motor_load_move(int motor) { motor_end_move(motor); - // Set direction, compensating for polarity + if (!m->timer_period) return; // Leave clock stopped + + // Set direction, compensating for polarity but only when moving const bool dir = m->negative ^ m->reverse; if (dir != IN_PIN(m->dir_pin)) { SET_PIN(m->dir_pin, dir); - // Make sure there is plenty of time between direction change and next step. + // Need at least 200ns between direction change and next step. if (m->timer->CCA < m->timer->CNT) m->timer->CNT = m->timer->CCA + 1; } - if (!m->timer_period) return; // Leave clock stopped - // Reset DMA step counter m->dma->CTRLA &= ~DMA_CH_ENABLE_bm; m->dma->TRFCNT = 0xffff; diff --git a/src/avr/src/status.h b/src/avr/src/status.h index d1cab7b..00a130e 100644 --- a/src/avr/src/status.h +++ b/src/avr/src/status.h @@ -92,7 +92,7 @@ stat_t status_alarm(const char *location, stat_t status, const char *msg); #ifdef DEBUG #define DEBUG_CALL(FMT, ...) \ - printf("%s(" FMT ")\n", __FUNCTION__, ##__VA_ARGS__) + printf_P(PSTR("%s(" FMT ")\n"), __FUNCTION__, ##__VA_ARGS__) #else // DEBUG #define DEBUG_CALL(...) #endif // DEBUG diff --git a/src/avr/src/stepper.c b/src/avr/src/stepper.c index 984245d..793bd1b 100644 --- a/src/avr/src/stepper.c +++ b/src/avr/src/stepper.c @@ -70,7 +70,7 @@ static volatile stepper_t st = {0}; void stepper_init() { // Motor enable - OUTSET_PIN(MOTOR_ENABLE_PIN); // Low (disabled) + OUTCLR_PIN(MOTOR_ENABLE_PIN); // Lo (disabled) DIRSET_PIN(MOTOR_ENABLE_PIN); // Output // Setup step timer diff --git a/src/avr/src/type.c b/src/avr/src/type.c index 73c01eb..aa92dd4 100644 --- a/src/avr/src/type.c +++ b/src/avr/src/type.c @@ -95,7 +95,7 @@ void type_print_f32(float x) { break; } - printf("%s", buf); + printf(buf); } } diff --git a/src/avr/src/vars.c b/src/avr/src/vars.c index d925a29..3e9bda6 100644 --- a/src/avr/src/vars.c +++ b/src/avr/src/vars.c @@ -294,7 +294,7 @@ stat_t vars_print(const char *name) { var_info_t info; if (!_find_var(name, &info)) return STAT_UNRECOGNIZED_NAME; - printf("{\"%s\":", info.name); + printf_P(PSTR("{\"%s\":"), info.name); type_print(info.type, _get(info.type, info.index, info.get)); putchar('}'); putchar('\n'); diff --git a/src/avr/step-test/step-test.c b/src/avr/step-test/step-test.c index 2a452e8..db267bf 100644 --- a/src/avr/step-test/step-test.c +++ b/src/avr/step-test/step-test.c @@ -47,6 +47,7 @@ static struct { uint8_t dir_pin; TC0_t *timer; volatile int16_t high; + volatile bool reading; } channel[4] = { {STEP_X_PIN, DIR_X_PIN, &TCC0, 0}, @@ -69,6 +70,7 @@ void channel_reset(int i) {channel[i].timer->CNT = channel[i].high = 0;} void channel_overflow(int i) { if (IN_PIN(channel[i].dir_pin)) channel[i].high--; else channel[i].high++; + channel[i].reading = false; } @@ -87,15 +89,39 @@ void channel_update_dir(int i) { ISR(PORTE_INT0_vect) {for (int i = 0; i < 4; i++) channel_update_dir(i);} +int32_t __attribute__ ((noinline)) _channel_read(int i) { + return (int32_t)channel[i].high << 16 | channel[i].timer->CNT; +} + + int32_t channel_read(int i) { while (true) { - int32_t x = (int32_t)channel[i].high << 16 | channel[i].timer->CNT; - int32_t y = (int32_t)channel[i].high << 16 | channel[i].timer->CNT; - if (x == y) return x; + channel[i].reading = true; + + int32_t x = _channel_read(i); + int32_t y = _channel_read(i); + + if (x != y || !channel[i].reading) continue; + + channel[i].reading = false; + return x; } } +void channel_update_enable(int i) { + if (IN_PIN(MOTOR_ENABLE_PIN)) + channel[i].timer->CTRLA = TC_CLKSEL_EVCH0_gc + i; + else channel[i].timer->CTRLA = 0; +} + + +ISR(PORTF_INT0_vect) { + for (int i = 0; i < 4; i++) channel_update_enable(i); + if (!IN_PIN(MOTOR_ENABLE_PIN)) reset = 2; +} + + ISR(PORTC_INT0_vect) {reset = 32;} @@ -142,7 +168,7 @@ void channel_init(int i) { PINCTRL_PIN(dir_pin) = PORT_SRLEN_bm | PORT_ISC_BOTHEDGES_gc; // Dir change interrupt - PIN_PORT(dir_pin)->INTCTRL |= PORT_INT0LVL_MED_gc; + PIN_PORT(dir_pin)->INTCTRL |= PORT_INT0LVL_HI_gc; PIN_PORT(dir_pin)->INT0MASK |= PIN_BM(dir_pin); // Events @@ -150,7 +176,7 @@ void channel_init(int i) { EVSYS_CHCTRL(i) = EVSYS_DIGFILT_8SAMPLES_gc; // Clock - channel[i].timer->CTRLA = TC_CLKSEL_EVCH0_gc + i; + channel_update_enable(i); channel[i].timer->INTCTRLA = TC_OVFINTLVL_HI_gc; // Set initial clock direction @@ -163,8 +189,17 @@ static void init() { hw_init(); usart_init(); + + // Motor channels for (int i = 0; i < 4; i++) channel_init(i); + // Motor enable + DIRCLR_PIN(MOTOR_ENABLE_PIN); + PINCTRL_PIN(MOTOR_ENABLE_PIN) = + PORT_SRLEN_bm | PORT_ISC_BOTHEDGES_gc | PORT_OPC_PULLUP_gc; + PIN_PORT(MOTOR_ENABLE_PIN)->INTCTRL |= PORT_INT0LVL_HI_gc; + PIN_PORT(MOTOR_ENABLE_PIN)->INT0MASK |= PIN_BM(MOTOR_ENABLE_PIN); + // Configure report clock TCC1.INTCTRLA = TC_OVFINTLVL_LO_gc; TCC1.PER = F_CPU / 256 * 0.01; // 10ms diff --git a/src/jade/templates/control-view.jade b/src/jade/templates/control-view.jade index cb61a9d..6ebb988 100644 --- a/src/jade/templates/control-view.jade +++ b/src/jade/templates/control-view.jade @@ -234,7 +234,7 @@ script#control-view-template(type="text/x-template") section#content2.tab-content .mdi.pure-form(title="Manual GCode entry.") - button.pure-button( + button.pure-button(:disabled="!can_mdi", title="{{is_running ? 'Pause' : 'Start'}} command.", @click="mdi_start_pause") .fa(:class="is_running ? 'fa-pause' : 'fa-play'") @@ -242,7 +242,7 @@ script#control-view-template(type="text/x-template") button.pure-button(title="Stop command.", @click="stop") .fa.fa-stop - input(v-model="mdi", @keyup.enter="submit_mdi") + input(v-model="mdi", :disabled="!can_mdi", @keyup.enter="submit_mdi") .history(:class="{placeholder: !history}") span(v-if="!history.length") MDI history displays here. diff --git a/src/js/control-view.js b/src/js/control-view.js index f4d0be7..d3d07a7 100644 --- a/src/js/control-view.js +++ b/src/js/control-view.js @@ -98,9 +98,14 @@ module.exports = { }, - is_stopping: function() {return this.mach_state == 'STOPPING'}, - is_holding: function() {return this.mach_state == 'HOLDING'}, - is_ready: function() {return this.mach_state == 'READY'}, + is_stopping: function () {return this.mach_state == 'STOPPING'}, + is_holding: function () {return this.mach_state == 'HOLDING'}, + is_ready: function () {return this.mach_state == 'READY'}, + + + can_mdi: function () { + return this.state.cycle == 'idle' || this.state.cycle == 'mdi'; + }, reason: function () { diff --git a/src/js/gcode-viewer.js b/src/js/gcode-viewer.js index ca92b7c..d8584a8 100644 --- a/src/js/gcode-viewer.js +++ b/src/js/gcode-viewer.js @@ -55,6 +55,22 @@ module.exports = { }, + ready: function () { + this.clusterize = new Clusterize({ + rows: [], + scrollElem: $(this.$el).find('.clusterize-scroll')[0], + contentElem: $(this.$el).find('.clusterize-content')[0], + callbacks: {clusterChanged: this.highlight} + }); + }, + + + attached: function () { + if (typeof this.clusterize != 'undefined') + this.clusterize.refresh(true); + }, + + methods: { load: function (file) { if (file == this.file) return; @@ -71,13 +87,7 @@ module.exports = { lines[i] + ''; } - this.clusterize = new Clusterize({ - rows: lines, - scrollElem: $(this.$el).find('.clusterize-scroll')[0], - contentElem: $(this.$el).find('.clusterize-content')[0], - callbacks: {clusterChanged: this.highlight} - }); - + this.clusterize.update(lines); this.empty = false; Vue.nextTick(this.update_line); @@ -89,9 +99,7 @@ module.exports = { this.empty = true; this.file = ''; this.line = -1; - - if (typeof this.clusterize != 'undefined') - this.clusterize.destroy(); + this.clusterize.clear(); }, @@ -118,8 +126,6 @@ module.exports = { } else line = this.line; - if (typeof this.clusterize == 'undefined') return; - var totalLines = this.clusterize.getRowsAmount(); if (line <= 0) line = 1; diff --git a/src/py/bbctrl/CommandQueue.py b/src/py/bbctrl/CommandQueue.py index cd5cd54..bb0e2a1 100644 --- a/src/py/bbctrl/CommandQueue.py +++ b/src/py/bbctrl/CommandQueue.py @@ -59,16 +59,16 @@ class CommandQueue(): return id - def enqueue(self, id, immediate, cb, *args, **kwargs): - log.info('add(#%d, %s) releaseID=%d', id, immediate, self.releaseID) + def enqueue(self, id, synchronized, cb, *args, **kwargs): + log.info('add(#%d, %s) releaseID=%d', id, synchronized, self.releaseID) self.lastEnqueueID = id - self.q.append((id, immediate, cb, args, kwargs)) + self.q.append([id, synchronized, False, cb, args, kwargs]) self._release() def _release(self): while len(self.q): - id, immediate, cb, args, kwargs = self.q[0] + id, synchronized, immediate, cb, args, kwargs = self.q[0] # Execute commands <= releaseID and consecutive immediate commands if not immediate and self.releaseID < id: return @@ -89,3 +89,16 @@ class CommandQueue(): self.releaseID = id self._release() + + + def finalize(self): + # Mark trailing non-synchronized commands immediate + i = len(self.q) + + while i: + i -= 1 + id, synchronized, immediate, cb, args, kwargs = self.q[i] + if synchronized: break + self.q[i][2] = True + + self._release() diff --git a/src/py/bbctrl/Mach.py b/src/py/bbctrl/Mach.py index b162dba..35029b4 100644 --- a/src/py/bbctrl/Mach.py +++ b/src/py/bbctrl/Mach.py @@ -138,19 +138,23 @@ class Mach(Comm): super().i2c_command(Cmd.UNPAUSE) + def _reset(self): + self.planner.reset() + self.ctrl.state.reset() + + @overrides(Comm) def comm_next(self): if self.planner.is_running(): return self.planner.next() @overrides(Comm) - def comm_error(self): self.planner.reset() + def comm_error(self): self._reset() @overrides(Comm) def connect(self): - self.ctrl.state.reset() - self.planner.reset() + self._reset() super().connect() @@ -231,7 +235,7 @@ class Mach(Comm): def clear(self): if self._get_state() == 'ESTOPPED': - self.ctrl.state.reset() + self._reset() super().clear() diff --git a/src/py/bbctrl/Planner.py b/src/py/bbctrl/Planner.py index 14b7bcd..3ce23d6 100644 --- a/src/py/bbctrl/Planner.py +++ b/src/py/bbctrl/Planner.py @@ -167,7 +167,7 @@ class Planner(): def _enqueue_set_cmd(self, id, name, value): log.info('set(#%d, %s, %s)', id, name, value) - self.cmdq.enqueue(id, True, self.ctrl.state.set, name, value) + self.cmdq.enqueue(id, False, self.ctrl.state.set, name, value) def __encode(self, block): @@ -185,7 +185,7 @@ class Planner(): if name == 'message': self.cmdq.enqueue( - id, True, self.ctrl.msgs.broadcast, {'message': value}) + id, False, self.ctrl.msgs.broadcast, {'message': value}) if name in ['line', 'tool']: self._enqueue_set_cmd(id, name, value) @@ -224,7 +224,7 @@ class Planner(): cmd = self.__encode(block) if cmd is not None: - self.cmdq.enqueue(block['id'], False, None) + self.cmdq.enqueue(block['id'], True, None) return Cmd.set('id', block['id']) + '\n' + cmd @@ -275,16 +275,16 @@ class Planner(): self.reset() - def has_move(self): return self.planner.has_more() - - def next(self): try: while self.planner.has_more(): cmd = self.planner.next() cmd = self._encode(cmd) + if not self.planner.is_running(): self.cmdq.finalize() if cmd is not None: return cmd + self.cmdq.finalize() + except Exception as e: log.exception(e) self.reset() diff --git a/src/py/bbctrl/State.py b/src/py/bbctrl/State.py index ad630a0..5c390a6 100644 --- a/src/py/bbctrl/State.py +++ b/src/py/bbctrl/State.py @@ -54,11 +54,11 @@ class State(object): for i in range(4): # Add home direction callbacks self.set_callback(str(i) + 'hd', - lambda name, i = i: self.motor_home_direction(i)) + lambda name, i = i: self.motor_home_direction(i)) # Add home position callbacks self.set_callback(str(i) + 'hp', - lambda name, i = i: self.motor_home_position(i)) + lambda name, i = i: self.motor_home_position(i)) self.reset() -- 2.27.0