Total rework of simulator for dev branch. Create separate thread for interrupt processes. Tick-accurate simulation of timers. Non-blocking character input for running in realtime mode. Decouple hardware sim from grbl code as much as possible. Expanded command line options. Provisions for cross-platform solution.
This commit is contained in:
parent
92d6c2bca5
commit
8c9f3bca65
2
.gitignore
vendored
2
.gitignore
vendored
@ -3,3 +3,5 @@
|
||||
*.elf
|
||||
*.DS_Store
|
||||
*.d
|
||||
*~
|
||||
\#*\#
|
||||
|
2
config.h
2
config.h
@ -84,7 +84,7 @@
|
||||
// parser state depending on user preferences.
|
||||
#define N_STARTUP_LINE 2 // Integer (1-3)
|
||||
|
||||
// Allows GRBL to tranck and report gcode line numbers. Enabling this means that the planning buffer
|
||||
// Allows GRBL to track and report gcode line numbers. Enabling this means that the planning buffer
|
||||
// goes from 18 or 16 to make room for the additional line number data in the plan_block_t struct
|
||||
// #define USE_LINE_NUMBERS // Disabled by default. Uncomment to enable.
|
||||
|
||||
|
@ -46,7 +46,8 @@ void coolant_stop()
|
||||
void coolant_run(uint8_t mode)
|
||||
{
|
||||
if (sys.state == STATE_CHECK_MODE) { return; }
|
||||
|
||||
|
||||
protocol_auto_cycle_start();
|
||||
protocol_buffer_synchronize(); // Ensure coolant turns on when specified in program.
|
||||
if (mode == COOLANT_FLOOD_ENABLE) {
|
||||
COOLANT_FLOOD_PORT |= (1 << COOLANT_FLOOD_BIT);
|
||||
|
4
gcode.c
4
gcode.c
@ -62,7 +62,7 @@ void gc_sync_position()
|
||||
{
|
||||
uint8_t i;
|
||||
for (i=0; i<N_AXIS; i++) {
|
||||
gc_state.position[i] = sys.position[i]/settings.steps_per_mm[i];
|
||||
gc_state.position[i] = sys.position[i]/settings.steps_per_mm[i];
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,7 +75,7 @@ static uint8_t gc_check_same_position(float *pos_a, float *pos_b)
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
|
||||
|
||||
// Executes one line of 0-terminated G-Code. The line is assumed to contain only uppercase
|
||||
// characters and signed floating point values (no whitespace). Comments and block delete
|
||||
// characters have been removed. In this function, all units and positions are converted and
|
||||
|
1
serial.c
1
serial.c
@ -178,6 +178,7 @@ ISR(SERIAL_RX)
|
||||
#endif
|
||||
|
||||
}
|
||||
//TODO: some sort of alarm on overflow?
|
||||
}
|
||||
}
|
||||
|
||||
|
1
sim/.gitignore
vendored
1
sim/.gitignore
vendored
@ -1 +1,2 @@
|
||||
grbl_sim.exe
|
||||
*.dat
|
||||
|
21
sim/Makefile
21
sim/Makefile
@ -15,11 +15,13 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Grbl. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
OBJECTS = main.o simulator.o runtime.o ../protocol.o ../planner.o ../settings.o ../print.o ../nuts_bolts.o eeprom.o serial.o avr/pgmspace.o avr/interrupt.o util/delay.o util/floatunsisf.o ../stepper.o ../gcode.o ../spindle_control.o ../motion_control.o ../limits.o ../report.o ../coolant_control.o
|
||||
PLATFORM = LINUX
|
||||
|
||||
OBJECTS = main.o simulator.o serial.o ../main.o ../protocol.o ../planner.o ../settings.o ../print.o ../nuts_bolts.o eeprom.o ../serial.o avr/pgmspace.o avr/interrupt.o avr/io.o util/delay.o util/floatunsisf.o ../stepper.o ../gcode.o ../spindle_control.o ../motion_control.o ../limits.o ../report.o ../coolant_control.o ../probe.o ../system.o kbhit.o platform_$(PLATFORM).o
|
||||
CLOCK = 16000000
|
||||
EXE_NAME = grbl_sim.exe
|
||||
COMPILE = $(CC) -Wall -Os -DF_CPU=$(CLOCK) -include config.h -I.
|
||||
|
||||
COMPILE = $(CC) -Wall -g -DF_CPU=$(CLOCK) -include config.h -I. -DPLAT_$(PLATFORM)
|
||||
LINUX_LIBRARIES = -lrt -pthread
|
||||
# symbolic targets:
|
||||
all: main
|
||||
|
||||
@ -30,15 +32,18 @@ clean:
|
||||
|
||||
# file targets:
|
||||
main: $(OBJECTS)
|
||||
$(COMPILE) -o $(EXE_NAME) $(OBJECTS) -lm
|
||||
$(COMPILE) -o $(EXE_NAME) $(OBJECTS) -lm -lrt $($(PLATFORM)_LIBRARIES)
|
||||
|
||||
%.o: %.c
|
||||
$(COMPILE) -c $< -o $@
|
||||
|
||||
../protocol.o: ../protocol.c
|
||||
$(COMPILE) -include rename_execute_runtime.h -c $< -o $@
|
||||
$(COMPILE) -c $< -o $@
|
||||
|
||||
../planner.o: ../planner.c
|
||||
$(COMPILE) -include planner_inject_accessors.c -c $< -o $@
|
||||
|
||||
../serial.o: ../serial.c
|
||||
$(COMPILE) -include serial_hooks.h -c $< -o $@
|
||||
|
||||
../main.o: ../main.c
|
||||
$(COMPILE) -include rename_main.h -c $< -o $@
|
||||
|
||||
|
||||
|
@ -21,23 +21,115 @@
|
||||
*/
|
||||
|
||||
#include "interrupt.h"
|
||||
#include "io.h"
|
||||
|
||||
//pseudo-Interrupt vector table
|
||||
isr_fp compa_vect[6]={0};
|
||||
isr_fp compb_vect[6]={0};
|
||||
isr_fp ovf_vect[6]={0};
|
||||
|
||||
|
||||
void sei() {io.sreg|=SEI;}
|
||||
void cli() {io.sreg&=~SEI;}
|
||||
|
||||
|
||||
|
||||
int16_t sim_scaling[8]={0,1,8,64,256,1024,1,1}; //clock scalars
|
||||
//Timer/Counter modes: these are incomplete, but enough for this application
|
||||
enum sim_wgm_mode {
|
||||
wgm_NORMAL,
|
||||
wgm_CTC,
|
||||
wgm_FAST_PWM,
|
||||
wgm_PHASE_PWM,
|
||||
wgm_PH_F_PWM,
|
||||
wgm_RESERVED
|
||||
};
|
||||
|
||||
enum sim_wgm_mode sim_wgm0[4] = {wgm_NORMAL,wgm_PHASE_PWM,wgm_CTC,wgm_FAST_PWM};
|
||||
enum sim_wgm_mode sim_wgmN[8] = {wgm_NORMAL,wgm_PHASE_PWM,wgm_PHASE_PWM,wgm_PH_F_PWM,
|
||||
wgm_CTC, wgm_FAST_PWM, wgm_FAST_PWM, wgm_FAST_PWM};
|
||||
|
||||
void timer_interrupts() {
|
||||
int i;
|
||||
uint8_t ien = io.sreg&SEI; //interrupts enabled?
|
||||
io.prescaler++;
|
||||
|
||||
//all clocks
|
||||
for (i=0;i<2;i++){
|
||||
|
||||
uint8_t cs = io.tccrb[i]&7; //clock select bits
|
||||
int16_t increment = sim_scaling[cs];
|
||||
//check scaling to see if timer fires
|
||||
if (increment && (io.prescaler&(increment-1))==0) {
|
||||
|
||||
//select waveform generation mode
|
||||
enum sim_wgm_mode mode;
|
||||
if (i==0 || i==2) { //(T0 and T2 are different from rest)
|
||||
uint8_t wgm = io.tccra[i]&3; //look at low 2 bits
|
||||
mode = sim_wgm0[wgm];
|
||||
}
|
||||
else {
|
||||
uint8_t wgm = ((io.tccrb[i]&8)>>1) | (io.tccra[i]&3); //only using 3 bits for now
|
||||
mode = sim_wgmN[wgm];
|
||||
}
|
||||
|
||||
//tick
|
||||
io.tcnt[i]++;
|
||||
//comparators
|
||||
if ((io.timsk[i]&(1<<SIM_OCA)) && io.tcnt[i]==io.ocra[i]) io.tifr[i]|=(1<<SIM_OCA);
|
||||
if ((io.timsk[i]&(1<<SIM_OCB)) && io.tcnt[i]==io.ocrb[i]) io.tifr[i]|=(1<<SIM_OCB);
|
||||
if ((io.timsk[i]&(1<<SIM_OCC)) && io.tcnt[i]==io.ocrc[i]) io.tifr[i]|=(1<<SIM_OCC);
|
||||
|
||||
|
||||
switch (mode) {
|
||||
case wgm_NORMAL: //Normal mode
|
||||
if (i==0) io.tcnt[i]&=0xFF; //timer0 is 8 bit;
|
||||
if (i==2) io.tcnt[i]&=0xFF; //timer2 is 8 bit;
|
||||
if (io.tcnt[i]==0) io.tifr[i]|=(1<<SIM_TOV);
|
||||
break;
|
||||
|
||||
case wgm_CTC: //CTC mode
|
||||
if (io.tcnt[i]==io.ocra[i]) io.tcnt[i]=0;
|
||||
break;
|
||||
default: //unsupported
|
||||
break;
|
||||
}
|
||||
//call any triggered interupts
|
||||
if (ien && io.tifr[i]) {
|
||||
if (compa_vect[i] && (io.tifr[i]&(1<<SIM_OCA))) {
|
||||
compa_vect[i]();
|
||||
io.tifr[i]&=~(1<<SIM_OCA);
|
||||
//TODO: insert port_monitor call here
|
||||
}
|
||||
if (compb_vect[i] && (io.tifr[i]&(1<<SIM_OCB))) {
|
||||
compb_vect[i]();
|
||||
io.tifr[i]&=~(1<<SIM_OCB);
|
||||
}
|
||||
if (ovf_vect[i] && (io.tifr[i]&(1<<SIM_TOV))) {
|
||||
ovf_vect[i]();
|
||||
io.tifr[i]&=~(1<<SIM_TOV);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//// TODO for more complete timer sim.
|
||||
// pwm modes. (only used for variable spindle, I think).
|
||||
// -- would require fixing wgm mode for Timers1..5
|
||||
// -- phase correct modes need updown counter.
|
||||
// output pins (also only for variable spindle, I think).
|
||||
|
||||
//// Other chip features not needed yet for grbl:
|
||||
// writes to TCNT0 prevent compare match (need write detector.)
|
||||
// force output compare (unused)
|
||||
// input capture (unused and how would we signal it?)
|
||||
// define the other output compare registers.
|
||||
// usercode can clear unhandled interrupt flags by writing 1.
|
||||
// --(this may be impossible, since bit was 1 before the write.)
|
||||
// prescaler reset.
|
||||
// maybe need to cli on interrupt entry
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// dummy register variables
|
||||
uint16_t timsk0;
|
||||
uint16_t timsk1;
|
||||
uint16_t timsk2;
|
||||
uint16_t ocr1a;
|
||||
uint16_t ocr2a;
|
||||
uint16_t tcnt0;
|
||||
uint16_t tcnt2;
|
||||
uint16_t tccr0b;
|
||||
uint16_t tccr2b;
|
||||
uint16_t tccr1b;
|
||||
uint16_t tccr0a;
|
||||
uint16_t tccr1a;
|
||||
uint16_t tccr2a;
|
||||
uint16_t pcmsk0;
|
||||
uint16_t pcicr;
|
||||
|
||||
void sei() {};
|
||||
void cli() {};
|
@ -24,60 +24,33 @@
|
||||
#ifndef interrupt_h
|
||||
#define interrupt_h
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
// dummy register variables
|
||||
extern uint16_t timsk0;
|
||||
extern uint16_t timsk1;
|
||||
extern uint16_t timsk2;
|
||||
extern uint16_t tcnt0;
|
||||
extern uint16_t tcnt2;
|
||||
extern uint16_t tccr0b;
|
||||
extern uint16_t tccr0a;
|
||||
extern uint16_t tccr2a;
|
||||
extern uint16_t tccr2b;
|
||||
extern uint16_t tccr1b;
|
||||
extern uint16_t tccr1a;
|
||||
extern uint16_t ocr1a;
|
||||
extern uint16_t ocr2a;
|
||||
extern uint16_t pcmsk0;
|
||||
extern uint16_t pcicr;
|
||||
|
||||
// macros to turn avr interrupts into regular functions
|
||||
#define TIMER1_COMPA_vect
|
||||
//#define TIMER1_COMPA_vect
|
||||
#define ISR(a) void interrupt_ ## a ()
|
||||
|
||||
// enable interrupts does nothing in the simulation environment
|
||||
// Stub of the timer interrupt functions we need
|
||||
void interrupt_TIMER0_COMPA_vect();
|
||||
void interrupt_TIMER1_COMPA_vect();
|
||||
void interrupt_TIMER0_OVF_vect();
|
||||
void interrupt_SERIAL_UDRE();
|
||||
void interrupt_SERIAL_RX();
|
||||
|
||||
|
||||
//pseudo-Interrupt vector table
|
||||
typedef void(*isr_fp)(void);
|
||||
extern isr_fp compa_vect[6];
|
||||
extern isr_fp compb_vect[6];
|
||||
extern isr_fp ovf_vect[6];
|
||||
|
||||
|
||||
// enable interrupts now does something in the simulation environment
|
||||
#define SEI 0x80
|
||||
void sei();
|
||||
void cli();
|
||||
|
||||
// dummy macros for interrupt related registers
|
||||
#define TIMSK0 timsk0
|
||||
#define TIMSK1 timsk1
|
||||
#define TIMSK2 timsk2
|
||||
#define OCR1A ocr1a
|
||||
#define OCR2A ocr2a
|
||||
#define OCIE1A 0
|
||||
#define OCIE2A 0
|
||||
#define TCNT0 tcnt0
|
||||
#define TCNT2 tcnt2
|
||||
#define TCCR0B tccr0b
|
||||
#define TCCR0A tccr0a
|
||||
#define TCCR1A tccr1a
|
||||
#define TCCR1B tccr1b
|
||||
#define TCCR2A tccr2a
|
||||
#define TCCR2B tccr2b
|
||||
#define CS21 0
|
||||
#define CS10 0
|
||||
#define WGM13 0
|
||||
#define WGM12 0
|
||||
#define WGM11 0
|
||||
#define WGM10 0
|
||||
#define WGM21 0
|
||||
#define COM1A0 0
|
||||
#define COM1B0 0
|
||||
#define TOIE0 0
|
||||
#define TOIE2 0
|
||||
#define PCICR pcicr
|
||||
//simulate timer operation
|
||||
void timer_interrupts();
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
4
sim/avr/io.c
Normal file
4
sim/avr/io.c
Normal file
@ -0,0 +1,4 @@
|
||||
#include "io.h"
|
||||
|
||||
// dummy register variables
|
||||
volatile io_sim_t io={{0}};
|
182
sim/avr/io.h
182
sim/avr/io.h
@ -1,5 +1,6 @@
|
||||
/*
|
||||
io.h - dummy replacement for the avr include of the same name
|
||||
interrupt.h - replacement for the avr include of the same name to provide
|
||||
dummy register variables and macros
|
||||
|
||||
Part of Grbl Simulator
|
||||
|
||||
@ -19,3 +20,182 @@
|
||||
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef io_h
|
||||
#define io_h
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
union hilo16 {
|
||||
uint16_t w;
|
||||
struct {
|
||||
uint8_t l; //TODO: check that these are right order on x86. Doesn't matter for current usage, but might someday
|
||||
uint8_t h;
|
||||
};
|
||||
};
|
||||
|
||||
enum {
|
||||
SIM_A, SIM_B, SIM_C, SIM_D, SIM_E,
|
||||
SIM_F, SIM_G, SIM_H, SIM_J, SIM_K, SIM_L,
|
||||
SIM_PORT_COUNT
|
||||
};
|
||||
|
||||
#define SIM_N_TIMERS 6
|
||||
|
||||
|
||||
// dummy register variables
|
||||
typedef struct io_sim {
|
||||
uint8_t ddr[SIM_PORT_COUNT];
|
||||
uint8_t port[SIM_PORT_COUNT];
|
||||
uint8_t pin[SIM_PORT_COUNT];
|
||||
uint8_t timsk[SIM_N_TIMERS];
|
||||
uint16_t ocra[SIM_N_TIMERS];
|
||||
uint16_t ocrb[SIM_N_TIMERS];
|
||||
uint16_t ocrc[SIM_N_TIMERS];
|
||||
uint16_t tcnt[SIM_N_TIMERS]; //tcint0 is really only 8bit
|
||||
uint8_t tccra[SIM_N_TIMERS];
|
||||
uint8_t tccrb[SIM_N_TIMERS];
|
||||
uint8_t tifr[SIM_N_TIMERS];
|
||||
uint8_t pcicr;
|
||||
uint8_t pcmsk[3];
|
||||
uint8_t ucsr0[3];
|
||||
uint8_t udr[3];
|
||||
union hilo16 ubrr0;
|
||||
|
||||
uint16_t prescaler; //continuously running
|
||||
uint8_t sreg;
|
||||
|
||||
|
||||
} io_sim_t;
|
||||
volatile extern io_sim_t io;
|
||||
|
||||
|
||||
|
||||
|
||||
// dummy macros for interrupt related registers
|
||||
#define PORTA io.port[SIM_A]
|
||||
#define PORTB io.port[SIM_B]
|
||||
#define PORTC io.port[SIM_C]
|
||||
#define PORTD io.port[SIM_D]
|
||||
#define PORTE io.port[SIM_E]
|
||||
#define PORTF io.port[SIM_F]
|
||||
#define PORTG io.port[SIM_G]
|
||||
#define PORTH io.port[SIM_H]
|
||||
#define PORTJ io.port[SIM_J]
|
||||
#define PORTK io.port[SIM_K]
|
||||
#define PORTL io.port[SIM_L]
|
||||
|
||||
#define DDRA io.ddr[SIM_A]
|
||||
#define DDRB io.ddr[SIM_B]
|
||||
#define DDRC io.ddr[SIM_C]
|
||||
#define DDRD io.ddr[SIM_D]
|
||||
#define DDRE io.ddr[SIM_E]
|
||||
#define DDRF io.ddr[SIM_F]
|
||||
#define DDRG io.ddr[SIM_G]
|
||||
#define DDRH io.ddr[SIM_H]
|
||||
#define DDRJ io.ddr[SIM_J]
|
||||
|
||||
#define PINA io.pin[SIM_A]
|
||||
#define PINB io.pin[SIM_B]
|
||||
#define PINC io.pin[SIM_C]
|
||||
#define PIND io.pin[SIM_D]
|
||||
#define PINE io.pin[SIM_E]
|
||||
#define PINF io.pin[SIM_F]
|
||||
#define PING io.pin[SIM_G]
|
||||
#define PINH io.pin[SIM_H]
|
||||
#define PINJ io.pin[SIM_J]
|
||||
#define PINK io.pin[SIM_K]
|
||||
#define PINL io.pin[SIM_L]
|
||||
|
||||
|
||||
#define TIMSK0 io.timsk[0]
|
||||
#define TIMSK1 io.timsk[1]
|
||||
#define TIMSK2 io.timsk[2]
|
||||
#define TIMSK3 io.timsk[3]
|
||||
#define TIMSK4 io.timsk[4]
|
||||
#define TIMSK5 io.timsk[5]
|
||||
|
||||
|
||||
#define SIM_TOV 0
|
||||
#define SIM_OCA 1
|
||||
#define SIM_OCB 2
|
||||
#define SIM_OCC 3
|
||||
#define SIM_ICI 5
|
||||
|
||||
#define OCIE0A SIM_OCA
|
||||
#define OCIE0B SIM_OCB
|
||||
#define TOIE0 SIM_TOV
|
||||
|
||||
#define ICIE1 SIM_ICI
|
||||
#define OCIE1C SIM_OCC
|
||||
#define OCIE1B SIM_OCB
|
||||
#define OCIE1A SIM_OCA
|
||||
#define TOIE1 SIM_ICI
|
||||
|
||||
#define ICIE2 SIM_ICI
|
||||
#define OCIE2C SIM_OCC
|
||||
#define OCIE2B SIM_OCB
|
||||
#define OCIE2A SIM_OCA
|
||||
#define TOIE2 SIM_TOV
|
||||
|
||||
#define OCR0A io.ocra[0]
|
||||
#define OCR1A io.ocra[1]
|
||||
#define OCR2A io.ocra[2]
|
||||
//There are more..
|
||||
|
||||
|
||||
#define TCNT0 io.tcnt[0]
|
||||
#define TCNT1 io.tcnt[1]
|
||||
#define TCNT2 io.tcnt[2]
|
||||
|
||||
#define TCCR0B io.tccra[0]
|
||||
#define TCCR0A io.tccrb[0]
|
||||
#define TCCR1A io.tccra[1]
|
||||
#define TCCR1B io.tccrb[1]
|
||||
#define TCCR2A io.tccra[2]
|
||||
#define TCCR2B io.tccrb[2]
|
||||
|
||||
#define CS00 0
|
||||
#define CS01 1
|
||||
#define CS12 2
|
||||
#define CS11 1
|
||||
#define CS10 0
|
||||
#define CS21 1
|
||||
|
||||
#define WGM13 4
|
||||
#define WGM12 3
|
||||
#define WGM11 1
|
||||
#define WGM10 0
|
||||
#define WGM21 1
|
||||
|
||||
#define COM1A1 7
|
||||
#define COM1A0 6
|
||||
#define COM1B1 5
|
||||
#define COM1B0 4
|
||||
#define COM1C1 3
|
||||
#define COM1C0 2
|
||||
|
||||
|
||||
#define PCICR io.pcicr
|
||||
#define PCIE0 0
|
||||
#define PCIE1 1
|
||||
|
||||
//serial channel
|
||||
#define UCSR0A io.ucsr0[SIM_A]
|
||||
#define UCSR0B io.ucsr0[SIM_B]
|
||||
#define UDR0 io.udr[0]
|
||||
#define UDRIE0 0
|
||||
#define RXCIE0 1
|
||||
#define RXEN0 2
|
||||
#define TXEN0 3
|
||||
#define U2X0 4
|
||||
#define UBRR0H io.ubrr0.h
|
||||
#define UBRR0L io.ubrr0.l
|
||||
|
||||
#define PCMSK0 io.pcmsk[0]
|
||||
#define PCMSK1 io.pcmsk[1]
|
||||
#define PCMSK2 io.pcmsk[2]
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
1
sim/avr/wdt.h
Normal file
1
sim/avr/wdt.h
Normal file
@ -0,0 +1 @@
|
||||
uint16_t wdt;
|
91
sim/config.h
91
sim/config.h
@ -24,94 +24,5 @@
|
||||
#include "../config.h"
|
||||
#include <inttypes.h>
|
||||
|
||||
|
||||
// dummy register variables implemented in simulator.c
|
||||
extern uint8_t stepping_ddr;
|
||||
extern uint8_t stepping_port;
|
||||
extern uint8_t spindle_ddr;
|
||||
extern uint8_t spindle_port;
|
||||
extern uint8_t limit_ddr;
|
||||
extern uint8_t limit_port;
|
||||
extern uint8_t limit_int_reg;
|
||||
extern uint8_t pinout_ddr;
|
||||
extern uint8_t pinout_port;
|
||||
extern uint8_t pinout_int_reg;
|
||||
extern uint8_t coolant_flood_ddr;
|
||||
extern uint8_t coolant_flood_port;
|
||||
|
||||
// ReDefine pin-assignments
|
||||
#undef STEPPING_DDR
|
||||
#define STEPPING_DDR stepping_ddr
|
||||
#undef STEPPING_PORT
|
||||
#define STEPPING_PORT stepping_port
|
||||
#undef X_STEP_BIT
|
||||
#define X_STEP_BIT 2 // Uno Digital Pin 2
|
||||
#undef Y_STEP_BIT
|
||||
#define Y_STEP_BIT 3 // Uno Digital Pin 3
|
||||
#undef Z_STEP_BIT
|
||||
#define Z_STEP_BIT 4 // Uno Digital Pin 4
|
||||
#undef X_DIRECTION_BIT
|
||||
#define X_DIRECTION_BIT 5 // Uno Digital Pin 5
|
||||
#undef Y_DIRECTION_BIT
|
||||
#define Y_DIRECTION_BIT 6 // Uno Digital Pin 6
|
||||
#undef Z_DIRECTION_BIT
|
||||
#define Z_DIRECTION_BIT 7 // Uno Digital Pin 7
|
||||
|
||||
#undef STEPPERS_DISABLE_DDR
|
||||
#define STEPPERS_DISABLE_DDR stepping_ddr
|
||||
#undef STEPPERS_DISABLE_PORT
|
||||
#define STEPPERS_DISABLE_PORT stepping_port
|
||||
#undef STEPPERS_DISABLE_BIT
|
||||
#define STEPPERS_DISABLE_BIT 0 // Uno Digital Pin 8
|
||||
|
||||
#undef LIMIT_DDR
|
||||
#define LIMIT_DDR limit_ddr
|
||||
#undef LIMIT_PORT
|
||||
#define LIMIT_PORT limit_port
|
||||
#undef LIMIT_PIN
|
||||
#define LIMIT_PIN limit_port
|
||||
#undef X_LIMIT_BIT
|
||||
#define X_LIMIT_BIT 1 // Uno Digital Pin 9
|
||||
#undef Y_LIMIT_BIT
|
||||
#define Y_LIMIT_BIT 2 // Uno Digital Pin 10
|
||||
#undef Z_LIMIT_BIT
|
||||
#define Z_LIMIT_BIT 3 // Uno Digital Pin 11
|
||||
#undef LIMIT_INT
|
||||
#define LIMIT_INT 0
|
||||
#undef LIMIT_PCMSK
|
||||
#define LIMIT_PCMSK limit_int_reg
|
||||
|
||||
#undef SPINDLE_ENABLE_DDR
|
||||
#define SPINDLE_ENABLE_DDR spindle_ddr
|
||||
#undef SPINDLE_ENABLE_PORT
|
||||
#define SPINDLE_ENABLE_PORT spindle_port
|
||||
#undef SPINDLE_ENABLE_BIT
|
||||
#define SPINDLE_ENABLE_BIT 4 // Uno Digital Pin 12
|
||||
|
||||
#undef SPINDLE_DIRECTION_DDR
|
||||
#define SPINDLE_DIRECTION_DDR spindle_ddr
|
||||
#undef SPINDLE_DIRECTION_PORT
|
||||
#define SPINDLE_DIRECTION_PORT spindle_port
|
||||
#undef SPINDLE_DIRECTION_BIT
|
||||
#define SPINDLE_DIRECTION_BIT 5 // Uno Digital Pin 13
|
||||
|
||||
#undef PINOUT_DDR
|
||||
#define PINOUT_DDR pinout_ddr
|
||||
#undef PINOUT_PORT
|
||||
#define PINOUT_PORT pinout_port
|
||||
#undef PINOUT_PIN
|
||||
#define PINOUT_PIN pinout_port
|
||||
#undef PINOUT_PCMSK
|
||||
#define PINOUT_PCMSK pinout_int_reg
|
||||
#undef PINOUT_INT
|
||||
#define PINOUT_INT 0
|
||||
|
||||
#undef COOLANT_FLOOD_DDR
|
||||
#define COOLANT_FLOOD_DDR coolant_flood_ddr
|
||||
#undef COOLANT_FLOOD_PORT
|
||||
#define COOLANT_FLOOD_PORT coolant_flood_port
|
||||
#undef COOLANT_FLOOD_BIT
|
||||
#define COOLANT_FLOOD_BIT 0
|
||||
|
||||
|
||||
#define AUTO_REPORT_MOVE_DONE
|
||||
#endif
|
||||
|
61
sim/eeprom.c
61
sim/eeprom.c
@ -21,18 +21,75 @@
|
||||
*/
|
||||
|
||||
// These are never called in the simulator
|
||||
#include <stdio.h>
|
||||
#define MAX_EEPROM_SIZE 4096 //4KB EEPROM in Mega
|
||||
|
||||
|
||||
FILE* eeprom_create_empty_file(){
|
||||
FILE* fp = fopen("EEPROM.DAT","w+b");
|
||||
int i;
|
||||
if (fp){
|
||||
for(i=0;i<MAX_EEPROM_SIZE;i++){
|
||||
fputc(0,fp);
|
||||
}
|
||||
fseek(fp,0,SEEK_SET);
|
||||
}
|
||||
return fp;
|
||||
}
|
||||
|
||||
|
||||
FILE* eeprom_fp(){
|
||||
static FILE* EEPROM_FP = NULL;
|
||||
static int tried= 0;
|
||||
if (!EEPROM_FP && !tried){
|
||||
tried = 1;
|
||||
EEPROM_FP = fopen("EEPROM.DAT","r+b");
|
||||
if (!EEPROM_FP) {
|
||||
EEPROM_FP = eeprom_create_empty_file();
|
||||
}
|
||||
}
|
||||
return EEPROM_FP;
|
||||
}
|
||||
|
||||
void eeprom_close(){
|
||||
FILE* fp = eeprom_fp();
|
||||
fclose(fp);
|
||||
}
|
||||
unsigned char eeprom_get_char( unsigned int addr ) {
|
||||
return 0;
|
||||
|
||||
FILE* fp = eeprom_fp();
|
||||
if (fseek(fp,addr,SEEK_SET)) { return 0; } //no such address
|
||||
return fgetc(fp);
|
||||
}
|
||||
|
||||
void eeprom_put_char( unsigned int addr, unsigned char new_value ) {
|
||||
FILE* fp = eeprom_fp();
|
||||
if (fseek(fp,addr,SEEK_SET)) { return; } //no such address
|
||||
fputc(new_value, fp);
|
||||
}
|
||||
|
||||
// Extensions added as part of Grbl
|
||||
// KEEP IN SYNC WITH ../eeprom.c
|
||||
|
||||
void memcpy_to_eeprom_with_checksum(unsigned int destination, char *source, unsigned int size) {
|
||||
unsigned char checksum = 0;
|
||||
for(; size > 0; size--) {
|
||||
checksum = (checksum << 1) || (checksum >> 7);
|
||||
checksum += *source;
|
||||
eeprom_put_char(destination++, *(source++));
|
||||
}
|
||||
eeprom_put_char(destination, checksum);
|
||||
}
|
||||
|
||||
int memcpy_from_eeprom_with_checksum(char *destination, unsigned int source, unsigned int size) {
|
||||
return 0;
|
||||
unsigned char data, checksum = 0;
|
||||
for(; size > 0; size--) {
|
||||
data = eeprom_get_char(source++);
|
||||
checksum = (checksum << 1) || (checksum >> 7);
|
||||
checksum += data;
|
||||
*(destination++) = data;
|
||||
}
|
||||
return(checksum == eeprom_get_char(source));
|
||||
}
|
||||
|
||||
// end of file
|
||||
|
3
sim/eeprom.h
Normal file
3
sim/eeprom.h
Normal file
@ -0,0 +1,3 @@
|
||||
|
||||
#include "../eeprom.h"
|
||||
void eeprom_close();
|
6
sim/kbhit.c
Normal file
6
sim/kbhit.c
Normal file
@ -0,0 +1,6 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "kbhit.h"
|
||||
|
||||
|
@ -1,38 +1,39 @@
|
||||
/*
|
||||
runtime.c - replacement for the modul of the same name in grbl
|
||||
Run time commands are not processed in the simulator.
|
||||
Instead, the execute_runtime() is used as a hook to handle stepper simulation
|
||||
and printing of simulation results.
|
||||
|
||||
Part of Grbl Simulator
|
||||
|
||||
Copyright (c) 2012 Jens Geisler
|
||||
|
||||
Grbl is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Grbl is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "simulator.h"
|
||||
#include <stdio.h>
|
||||
|
||||
void orig_protocol_execute_runtime(void);
|
||||
|
||||
// replacement for original execute_runtime as a hook to print blocks as they are generated
|
||||
// and to control simulation of buffered blocks
|
||||
void protocol_execute_runtime(void) {
|
||||
orig_protocol_execute_runtime();
|
||||
//printf("printBlock():\n");
|
||||
printBlock();
|
||||
//printf("handle_buffer():\n");
|
||||
handle_buffer();
|
||||
}
|
||||
/*
|
||||
kbhit.h - keyboard hit detection - used in serial port replacement for grbl sim
|
||||
|
||||
linux kbhit taken from http://cboard.cprogramming.com/c-programming/63166-kbhit-linux.html.
|
||||
By 'thanatos':http://cboard.cprogramming.com/member.php?u=380
|
||||
|
||||
Part of Grbl Simulator
|
||||
|
||||
Copyright (c) 2014 Adam Shelly
|
||||
|
||||
Grbl is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Grbl is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//if linux
|
||||
#include <stdio.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int kbhit(void);
|
||||
void enable_kbhit(int);
|
||||
|
||||
|
||||
//else
|
||||
//#include <conio.h>
|
||||
//#define enable_kbhit(e)
|
||||
//#define kbhit _kbhit
|
||||
//endif
|
||||
|
148
sim/main.c
148
sim/main.c
@ -34,18 +34,106 @@
|
||||
#include "simulator.h"
|
||||
|
||||
|
||||
arg_vars_t args;
|
||||
const char* progname;
|
||||
|
||||
int usage(const char* badarg){
|
||||
if (badarg){
|
||||
printf("Unrecognized option %s\n",badarg);
|
||||
}
|
||||
printf("Usage: \n"
|
||||
"%s [options] [time_step] [block_file]\n"
|
||||
" Options:\n"
|
||||
" -r <report time> : minimum time step for printing stepper values. Default=0=no print.\n"
|
||||
" -t <time factor> : multiplier to realtime clock. Default=1. (needs work)\n"
|
||||
" -g <response file> : file to report responses from grbl. default = stdout\n"
|
||||
" -b <block file> : file to report each block executed. default = stdout\n"
|
||||
" -s <step file> : file to report each step executed. default = stderr\n"
|
||||
" -c<comment_char> : character to print before each line from grbl. default = '#'\n"
|
||||
" -n : no comments before grbl response lines.\n"
|
||||
" -h : this help.\n"
|
||||
"\n <time_step> and <block_file> can be specifed with option flags or positional parameters\n"
|
||||
"\n ^-F to shutdown cleanly\n\n",
|
||||
progname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//prototype for renamed original main function
|
||||
int avr_main(void);
|
||||
//wrapper for thread interface
|
||||
PLAT_THREAD_FUNC(avr_main_thread,exit){
|
||||
avr_main();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
uint32_t tick_rate=1;
|
||||
int positional_args=0;
|
||||
|
||||
//defaults
|
||||
args.step_out_file = stderr;
|
||||
args.block_out_file = stdout;
|
||||
args.grbl_out_file = stdout;
|
||||
args.comment_char = '#';
|
||||
|
||||
args.step_time = 0.0;
|
||||
// Get the minimum time step for printing stepper values.
|
||||
// If not given or the command line cannot be parsed to a float than
|
||||
// step_time= 0.0; This means to not print stepper values at all
|
||||
if(argc>1) {
|
||||
argv++;
|
||||
step_time= atof(*argv);
|
||||
|
||||
progname = argv[0];
|
||||
while (argc>1) {
|
||||
argv++;argc--;
|
||||
if (argv[0][0] == '-'){
|
||||
switch(argv[0][1]){
|
||||
case 'c': //set Comment char
|
||||
args.comment_char = argv[0][2];
|
||||
break;
|
||||
case 'n': //No comment char on grbl responses
|
||||
args.comment_char = 0;
|
||||
break;
|
||||
case 't': //Tick rate
|
||||
argv++;argc--;
|
||||
tick_rate = atof(*argv);
|
||||
break;
|
||||
case 'b': //Block file
|
||||
argv++;argc--;
|
||||
args.block_out_file = fopen(*argv,"w");
|
||||
break;
|
||||
case 's': //Step out file.
|
||||
argv++;argc--;
|
||||
args.step_out_file = fopen(*argv,"w");
|
||||
break;
|
||||
case 'g': //Grbl output
|
||||
argv++;argc--;
|
||||
args.grbl_out_file = fopen(*argv,"w");
|
||||
break;
|
||||
case 'r': //step_time for Reporting
|
||||
argv++;argc--;
|
||||
args.step_time= atof(*argv);
|
||||
break;
|
||||
case 'h':
|
||||
return usage(NULL);
|
||||
default:
|
||||
return usage(*argv);
|
||||
}
|
||||
}
|
||||
else { //handle old positional argument interface
|
||||
positional_args++;
|
||||
switch(positional_args){
|
||||
case 1:
|
||||
args.step_time= atof(*argv);
|
||||
break;
|
||||
case 2: //block out and grbl out to same file, like before.
|
||||
args.block_out_file = fopen(*argv,"w");
|
||||
args.grbl_out_file = args.block_out_file;
|
||||
break;
|
||||
default:
|
||||
return usage(*argv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Setup output file handles. Block info goes to stdout. Stepper values go to stderr.
|
||||
block_out_file= stdout;
|
||||
step_out_file= stderr;
|
||||
// Make sure the output streams are flushed immediately.
|
||||
// This is important when using the simulator inside another application in parallel
|
||||
// to the real grbl.
|
||||
@ -53,36 +141,26 @@ int main(int argc, char *argv[]) {
|
||||
// does not know line buffered streams. So for now we stick to flushing every character.
|
||||
//setvbuf(stdout, NULL, _IONBF, 1);
|
||||
//setvbuf(stderr, NULL, _IONBF, 1);
|
||||
|
||||
st_init(); // Setup stepper pins and interrupt timers
|
||||
memset(&sys, 0, sizeof(sys)); // Clear all system variables
|
||||
//settings_reset(); TODO: implement read_settings from eeprom
|
||||
settings_init();
|
||||
protocol_init(); // Clear incoming line data
|
||||
//printf("plan_init():\n");
|
||||
plan_init(); // Clear block buffer and planner variables
|
||||
//printf("gc_init():\n");
|
||||
gc_init(); // Set g-code parser to default state
|
||||
//printf("spindle_init():\n");
|
||||
spindle_init();
|
||||
//printf("limits_init():\n");
|
||||
limits_init();
|
||||
//printf("coolant_init():\n");
|
||||
coolant_init();
|
||||
//printf("st_reset():\n");
|
||||
st_reset(); // Clear stepper subsystem variables.
|
||||
sys.auto_start = true; // runtime commands are not processed. We start to simulate immediately
|
||||
|
||||
// Main loop of command processing until EOF is encountered
|
||||
//printf("protocol_process():\n");
|
||||
protocol_process();
|
||||
|
||||
// flush the block buffer and print stepper info on any pending blocks
|
||||
plan_synchronize();
|
||||
//( Files are now closed cleanly when sim gets EOF or CTRL-F.)
|
||||
platform_init();
|
||||
|
||||
init_simulator(tick_rate);
|
||||
|
||||
//launch a thread with the original grbl code.
|
||||
plat_thread_t*th = platform_start_thread(avr_main_thread);
|
||||
if (!th){
|
||||
printf("Fatal: Unable to start hardware thread.\n");
|
||||
exit(-5);
|
||||
}
|
||||
|
||||
//All the stream io and interrupt happen in this thread.
|
||||
sim_loop();
|
||||
|
||||
platform_kill_thread(th); //need force kill since original main has no return.
|
||||
|
||||
// Graceful exit
|
||||
fclose(block_out_file);
|
||||
fclose(step_out_file);
|
||||
|
||||
shutdown_simulator(0);
|
||||
platform_terminate();
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
#include "../planner.h"
|
||||
|
||||
static block_t block_buffer[BLOCK_BUFFER_SIZE]; // A ring buffer for motion instructions
|
||||
block_t *get_block_buffer() { return block_buffer; }
|
||||
static plan_block_t block_buffer[BLOCK_BUFFER_SIZE]; // A ring buffer for motion instructions
|
||||
plan_block_t *get_block_buffer() { return block_buffer; }
|
||||
|
||||
static volatile uint8_t block_buffer_head; // Index of the next block to be pushed
|
||||
static uint8_t block_buffer_head; // Index of the next block to be pushed
|
||||
uint8_t get_block_buffer_head() { return block_buffer_head; }
|
||||
|
||||
static volatile uint8_t block_buffer_tail; // Index of the next block to be pushed
|
||||
static uint8_t block_buffer_tail; // Index of the next block to be pushed
|
||||
uint8_t get_block_buffer_tail() { return block_buffer_tail; }
|
||||
|
23
sim/platform.h
Normal file
23
sim/platform.h
Normal file
@ -0,0 +1,23 @@
|
||||
#ifndef platform_h
|
||||
|
||||
#ifdef PLAT_LINUX
|
||||
#include "platform_linux.h"
|
||||
#else
|
||||
#include "platform_windows.h"
|
||||
#endif
|
||||
|
||||
#define platform_h
|
||||
|
||||
void platform_init();
|
||||
void platform_terminate();
|
||||
|
||||
plat_thread_t* platform_start_thread(plat_threadfunc_t func);
|
||||
void platform_stop_thread(plat_thread_t* thread);
|
||||
void platform_kill_thread(plat_thread_t* thread);
|
||||
|
||||
uint32_t platform_ns(); //monotonically increasing nanoseconds since program start.
|
||||
void platform_sleep(long microsec); //sleep for suggested time in microsec.
|
||||
|
||||
uint8_t platform_poll_stdin(); //non-blocking stdin read - returns 0 if no char present, 0xFF for EOF
|
||||
|
||||
#endif
|
116
sim/platform_LINUX.c
Normal file
116
sim/platform_LINUX.c
Normal file
@ -0,0 +1,116 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <termios.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include "platform.h"
|
||||
|
||||
#define MS_PER_SEC 1000000
|
||||
|
||||
|
||||
//any platform-specific setup that must be done before sim starts here
|
||||
void platform_init()
|
||||
{
|
||||
}
|
||||
|
||||
//cleanup int here;
|
||||
void platform_terminate()
|
||||
{
|
||||
}
|
||||
|
||||
//returns a free-running 32 bit nanosecond counter which rolls over
|
||||
uint32_t platform_ns()
|
||||
{
|
||||
static uint32_t gTimeBase = 0;
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC,&ts);
|
||||
if (gTimeBase== 0){gTimeBase=ts.tv_nsec;}
|
||||
return ts.tv_nsec-gTimeBase;
|
||||
}
|
||||
|
||||
//sleep in microseconds
|
||||
void platform_sleep(long microsec)
|
||||
{
|
||||
struct timespec ts={0};
|
||||
while (microsec >= MS_PER_SEC){
|
||||
ts.tv_sec++;
|
||||
microsec-=MS_PER_SEC;
|
||||
}
|
||||
ts.tv_nsec = microsec*1000;
|
||||
nanosleep(&ts,NULL);
|
||||
}
|
||||
|
||||
#define SIM_ECHO_TERMINAL 1 //use this to make grbl_sim act like a serial terminal with local echo on.
|
||||
|
||||
//set terminal to allow kbhit detection
|
||||
void enable_kbhit(int dir)
|
||||
{
|
||||
static struct termios oldt, newt;
|
||||
|
||||
if ( dir == 1 )
|
||||
{
|
||||
tcgetattr( STDIN_FILENO, &oldt);
|
||||
newt = oldt;
|
||||
newt.c_lflag &= ~( ICANON );
|
||||
if (!SIM_ECHO_TERMINAL) {
|
||||
newt.c_lflag &= ~( ECHO );
|
||||
}
|
||||
tcsetattr( STDIN_FILENO, TCSANOW, &newt);
|
||||
}
|
||||
else
|
||||
tcsetattr( STDIN_FILENO, TCSANOW, &oldt);
|
||||
}
|
||||
|
||||
//detect key pressed
|
||||
int kbhit (void)
|
||||
{
|
||||
struct timeval tv={0};
|
||||
fd_set rdfs={{0}};
|
||||
int retval;
|
||||
|
||||
/* tv.tv_sec = 0; */
|
||||
/* tv.tv_usec = 0; */
|
||||
|
||||
/* FD_ZERO(&rdfs); */
|
||||
FD_SET (STDIN_FILENO, &rdfs);
|
||||
|
||||
select(STDIN_FILENO+1, &rdfs, NULL, NULL, &tv);
|
||||
retval = FD_ISSET(STDIN_FILENO, &rdfs);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
|
||||
plat_thread_t* platform_start_thread(plat_threadfunc_t threadfunc) {
|
||||
plat_thread_t* th = malloc(sizeof(plat_thread_t));
|
||||
if (pthread_create(&th->tid, NULL, threadfunc, &th->exit)){
|
||||
free(th);
|
||||
return NULL;
|
||||
}
|
||||
return th;
|
||||
}
|
||||
|
||||
//ask thread to exit nicely, wait
|
||||
void platform_stop_thread(plat_thread_t* th){
|
||||
th->exit = 1;
|
||||
pthread_join(th->tid,NULL);
|
||||
}
|
||||
|
||||
//force-kill thread
|
||||
void platform_kill_thread(plat_thread_t* th){
|
||||
th->exit = 1;
|
||||
pthread_cancel(th->tid);
|
||||
}
|
||||
|
||||
//return char if one available.
|
||||
uint8_t platform_poll_stdin() {
|
||||
uint8_t char_in=0;
|
||||
enable_kbhit(1);
|
||||
if ( kbhit()) {
|
||||
char_in = getchar();
|
||||
}
|
||||
enable_kbhit(0);
|
||||
return char_in;
|
||||
}
|
72
sim/platform_WINDOWS.c
Normal file
72
sim/platform_WINDOWS.c
Normal file
@ -0,0 +1,72 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <conio.h>
|
||||
#include <windows.h>
|
||||
#include "platform.h"
|
||||
|
||||
#define NS_PER_SEC 1000000000
|
||||
#define MICRO_PER_MILLI 1000
|
||||
|
||||
double ns_per_perfcount;
|
||||
|
||||
//any platform-specific setup that must be done before sim starts here
|
||||
void platform_init()
|
||||
{
|
||||
__int64 counts_per_sec;
|
||||
QueryPerformanceFrequency((LARGE_INTEGER*)&countsPerSec);
|
||||
ns_per_perfcount = (float)NS_PER_SEC / counts_per_sec;
|
||||
}
|
||||
|
||||
//cleanup int here;
|
||||
void platform_terminate()
|
||||
{
|
||||
}
|
||||
|
||||
//returns a free-running 32 bit nanosecond counter which rolls over
|
||||
uint32_t platform_ns()
|
||||
{
|
||||
static uint32_t gTimeBase = 0;
|
||||
__int64 counts;
|
||||
uint32_t ns;
|
||||
QueryPerformanceCounter((LARGE_INTEGER*)&counts);
|
||||
ns = (counts*ns_per_perfcount);
|
||||
|
||||
if (gTimeBase== 0){gTimeBase=ns;}
|
||||
return ns-gTimeBase;
|
||||
}
|
||||
|
||||
//sleep in microseconds
|
||||
void platform_sleep(long microsec)
|
||||
{
|
||||
Sleep(microsec/MICRO_PER_MILLI);
|
||||
}
|
||||
|
||||
|
||||
//create a thread
|
||||
plat_thread_t* platform_start_thread(plat_threadfunc_t threadfunc) {
|
||||
plat_thread_t* th = malloc(sizeof(plat_thread_t));
|
||||
th->tid = CreateThread(NULL,0,threadfunc,&th->exit,0);
|
||||
if (!th->tid){
|
||||
free(th);
|
||||
return NULL;
|
||||
}
|
||||
return th;
|
||||
}
|
||||
|
||||
//ask thread to exit nicely, wait
|
||||
void platform_stop_thread(plat_thread_t* th){
|
||||
th->exit = 1;
|
||||
WaitForSingleObject(th->tid,INFINITE);
|
||||
}
|
||||
|
||||
//force-kill thread
|
||||
void platform_kill_thread(plat_thread_t* th){
|
||||
th->exit = 1;
|
||||
TerminateThread(th->tid);
|
||||
}
|
||||
|
||||
//return char if one available.
|
||||
uint8_t platform_poll_stdin() {
|
||||
return _kbhit();
|
||||
}
|
16
sim/platform_linux.h
Normal file
16
sim/platform_linux.h
Normal file
@ -0,0 +1,16 @@
|
||||
#ifdef platform_h
|
||||
#error "platform implementation already defined"
|
||||
#else
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
pthread_t tid;
|
||||
int exit;
|
||||
} plat_thread_t;
|
||||
|
||||
typedef void*(*plat_threadfunc_t)(void*);
|
||||
#define PLAT_THREAD_FUNC(name,arg) void* name(void* arg)
|
||||
|
||||
#endif
|
16
sim/platform_windows.h
Normal file
16
sim/platform_windows.h
Normal file
@ -0,0 +1,16 @@
|
||||
#ifdef platform_h
|
||||
#error "platform implementation already defined"
|
||||
#else
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
HANDLE tid;
|
||||
int exit;
|
||||
} plat_thread_t;
|
||||
|
||||
typedef DWORD WINAPI(*plat_threadfunc_t)(LPVOID);
|
||||
#define PLAT_THREAD_FUNC(name,arg) DWORD WINAPI name(LPVOID arg)
|
||||
|
||||
#endif
|
@ -1,2 +0,0 @@
|
||||
#define protocol_execute_runtime orig_protocol_execute_runtime
|
||||
//void __attribute__((weak)) protocol_execute_runtime(void);
|
1
sim/rename_main.h
Normal file
1
sim/rename_main.h
Normal file
@ -0,0 +1 @@
|
||||
#define main avr_main
|
65
sim/serial.c
65
sim/serial.c
@ -26,28 +26,51 @@
|
||||
#include "simulator.h"
|
||||
#include <stdio.h>
|
||||
|
||||
void serial_write(uint8_t data) {
|
||||
printBlock();
|
||||
if(print_comment && data!='\n' && data!='\r') {
|
||||
fprintf(block_out_file, "# ");
|
||||
print_comment= 0;
|
||||
}
|
||||
if(data=='\n' || data=='\r')
|
||||
print_comment= 1;
|
||||
|
||||
fprintf(block_out_file, "%c", data);
|
||||
|
||||
// Indicate the end of processing a command. See simulator.c for details
|
||||
runtime_second_call= 0;
|
||||
}
|
||||
#include "kbhit.h"
|
||||
|
||||
|
||||
//prototypes for overridden functions
|
||||
uint8_t orig_serial_read();
|
||||
|
||||
//used to inject a sleep in grbl main loop,
|
||||
// ensures hardware simulator gets some cycles in "parallel"
|
||||
uint8_t serial_read() {
|
||||
int c;
|
||||
if((c = fgetc(stdin)) != EOF) {
|
||||
serial_write(c);
|
||||
return c;
|
||||
}
|
||||
|
||||
return SERIAL_NO_DATA;
|
||||
platform_sleep(0);
|
||||
return orig_serial_read();
|
||||
}
|
||||
|
||||
|
||||
void simulate_write_interrupt(){
|
||||
while (UCSR0B & (1<<UDRIE0)){
|
||||
interrupt_SERIAL_UDRE();
|
||||
grbl_out(UDR0);
|
||||
}
|
||||
}
|
||||
|
||||
void simulate_read_interrupt(){
|
||||
uint8_t char_in = platform_poll_stdin();
|
||||
if (char_in) {
|
||||
UDR0 = char_in;
|
||||
//EOF or CTRL-F to exit
|
||||
if (UDR0 == EOF || UDR0 == 0xFF || UDR0 == 0x06 ) {
|
||||
sim.exit = 1;
|
||||
}
|
||||
//debugging
|
||||
if (UDR0 == '%') {
|
||||
printf("%ld %f\n",sim.masterclock,(double)sim.sim_time);
|
||||
}
|
||||
interrupt_SERIAL_RX();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extern volatile uint8_t rx_buffer_head;
|
||||
extern volatile uint8_t rx_buffer_tail;
|
||||
void simulate_serial(){
|
||||
simulate_write_interrupt();
|
||||
uint8_t head = rx_buffer_head+1;
|
||||
if (head==RX_BUFFER_SIZE) { head = 0; }
|
||||
if (head!=rx_buffer_tail) {
|
||||
simulate_read_interrupt();
|
||||
}
|
||||
}
|
||||
|
2
sim/serial_hooks.h
Normal file
2
sim/serial_hooks.h
Normal file
@ -0,0 +1,2 @@
|
||||
#define serial_read orig_serial_read
|
||||
|
2
sim/sim.sh
Executable file
2
sim/sim.sh
Executable file
@ -0,0 +1,2 @@
|
||||
./grbl_sim.exe 0.01 <HelloWorld.nc >HelloWorld.dat 2> HelloWorldSteps.dat
|
||||
gnuplot -persist gnuplot.plt
|
297
sim/simulator.c
297
sim/simulator.c
@ -28,93 +28,146 @@
|
||||
#include "../planner.h"
|
||||
#include "../nuts_bolts.h"
|
||||
#include "simulator.h"
|
||||
|
||||
// This variable is needed to determine if execute_runtime() is called in a loop
|
||||
// waiting for the buffer to empty, as in plan_synchronize()
|
||||
// it is reset in serial_write() because this is certainly called at the end of
|
||||
// every command processing
|
||||
int runtime_second_call= 0;
|
||||
#include "avr/interrupt.h" //for registers and isr declarations.
|
||||
#include "eeprom.h"
|
||||
|
||||
|
||||
// Current time of the stepper simulation
|
||||
double sim_time= 0.0;
|
||||
// Next time the status of stepper values should be printed
|
||||
double next_print_time= 0.0;
|
||||
// Minimum time step for printing stepper values. Given by user via command line
|
||||
double step_time= 0.0;
|
||||
|
||||
// global system variable structure for position etc.
|
||||
system_t sys;
|
||||
int block_position[]= {0,0,0};
|
||||
uint8_t print_comment= 1;
|
||||
uint8_t end_of_block= 1;
|
||||
int block_position[]= {0,0,0}; //step count after most recently planned block
|
||||
uint32_t block_number= 0;
|
||||
|
||||
// Output file handles set by main program
|
||||
FILE *block_out_file;
|
||||
FILE *step_out_file;
|
||||
sim_vars_t sim={0};
|
||||
|
||||
// dummy port variables
|
||||
uint8_t stepping_ddr;
|
||||
uint8_t stepping_port;
|
||||
uint8_t spindle_ddr;
|
||||
uint8_t spindle_port;
|
||||
uint8_t limit_ddr;
|
||||
uint8_t limit_port;
|
||||
uint8_t limit_int_reg;
|
||||
uint8_t pinout_ddr;
|
||||
uint8_t pinout_port;
|
||||
uint8_t pinout_int_reg;
|
||||
uint8_t coolant_flood_ddr;
|
||||
uint8_t coolant_flood_port;
|
||||
|
||||
extern block_t block_buffer[BLOCK_BUFFER_SIZE]; // A ring buffer for motion instructions
|
||||
extern uint8_t block_buffer_head; // Index of the next block to be pushed
|
||||
extern uint8_t block_buffer_tail; // Index of the block to process now
|
||||
//local prototypes
|
||||
void print_steps(bool force);
|
||||
|
||||
|
||||
// Stub of the timer interrupt function from stepper.c
|
||||
void interrupt_TIMER2_COMPA_vect();
|
||||
//setup
|
||||
void init_simulator(float time_multiplier) {
|
||||
|
||||
// Call the stepper interrupt until one block is finished
|
||||
void sim_stepper() {
|
||||
//printf("sim_stepper()\n");
|
||||
block_t *current_block= plan_get_current_block();
|
||||
//register the interrupt handlers we actually use.
|
||||
compa_vect[1] = interrupt_TIMER1_COMPA_vect;
|
||||
ovf_vect[0] = interrupt_TIMER0_OVF_vect;
|
||||
#ifdef STEP_PULSE_DELAY
|
||||
compa_vect[0] = interrupt_TIMER0_COMPA_vect;
|
||||
#endif
|
||||
|
||||
// If the block buffer is empty, call the stepper interrupt one last time
|
||||
// to let it handle sys.cycle_start etc.
|
||||
if(current_block==NULL) {
|
||||
interrupt_TIMER2_COMPA_vect();
|
||||
return;
|
||||
}
|
||||
sim.next_print_time = args.step_time;
|
||||
sim.speedup = time_multiplier;
|
||||
sim.baud_ticks = (int)((double)F_CPU*8/BAUD_RATE); //ticks per byte
|
||||
}
|
||||
|
||||
while(current_block==plan_get_current_block()) {
|
||||
sim_time+= get_step_time();
|
||||
interrupt_TIMER2_COMPA_vect();
|
||||
|
||||
// Check to see if we should print some info
|
||||
if(step_time>0.0) {
|
||||
if(sim_time>=next_print_time) {
|
||||
if(end_of_block) {
|
||||
end_of_block= 0;
|
||||
fprintf(step_out_file, "# block number %d\n", block_number);
|
||||
}
|
||||
fprintf(step_out_file, "%20.15f, %d, %d, %d\n", sim_time, sys.position[X_AXIS], sys.position[Y_AXIS], sys.position[Z_AXIS]);
|
||||
|
||||
// Make sure the simulation time doesn't get ahead of next_print_time
|
||||
while(next_print_time<sim_time) next_print_time+= step_time;
|
||||
}
|
||||
}
|
||||
//shutdown simulator - close open files
|
||||
int shutdown_simulator(uint8_t exitflag) {
|
||||
fclose(args.block_out_file);
|
||||
print_steps(1);
|
||||
fclose(args.step_out_file);
|
||||
fclose(args.grbl_out_file);
|
||||
eeprom_close();
|
||||
return 1/(!exitflag); //force exception, since avr_main() has no returns.
|
||||
}
|
||||
|
||||
}
|
||||
// always print stepper values at the end of a block
|
||||
if(step_time>0.0) {
|
||||
fprintf(step_out_file, "%20.15f, %d, %d, %d\n", sim_time, sys.position[X_AXIS], sys.position[Y_AXIS], sys.position[Z_AXIS]);
|
||||
end_of_block= 1;
|
||||
block_number++;
|
||||
|
||||
|
||||
void simulate_hardware(bool do_serial){
|
||||
|
||||
//do one tick
|
||||
sim.masterclock++;
|
||||
sim.sim_time = (float)sim.masterclock/F_CPU;
|
||||
|
||||
timer_interrupts();
|
||||
|
||||
if (do_serial) simulate_serial();
|
||||
|
||||
//TODO:
|
||||
// check limit pins, call pinchange interrupt if enabled
|
||||
// can ignore pinout int vect - hw start/hold not supported
|
||||
|
||||
}
|
||||
|
||||
//runs the hardware simulator at the desired rate until sim.exit is set
|
||||
void sim_loop(){
|
||||
uint64_t simulated_ticks=0;
|
||||
uint32_t ns_prev = platform_ns();
|
||||
uint64_t next_byte_tick = F_CPU; //wait 1 sec before reading IO.
|
||||
|
||||
while (!sim.exit || sys.state>2 ) { //don't quit until idle
|
||||
|
||||
if (sim.speedup) {
|
||||
//calculate how many ticks to do.
|
||||
uint32_t ns_now = platform_ns();
|
||||
uint32_t ns_elapsed = (ns_now-ns_prev)*sim.speedup; //todo: try multipling nsnow
|
||||
simulated_ticks += F_CPU/1e9*ns_elapsed;
|
||||
ns_prev = ns_now;
|
||||
}
|
||||
else {
|
||||
simulated_ticks++; //as fast as possible
|
||||
}
|
||||
|
||||
while (sim.masterclock < simulated_ticks){
|
||||
|
||||
//only read serial port as fast as the baud rate allows
|
||||
bool read_serial = (sim.masterclock >= next_byte_tick);
|
||||
|
||||
//do low level hardware
|
||||
simulate_hardware(read_serial);
|
||||
|
||||
//print the steps.
|
||||
//For further decoupling, could maintain own counter of STEP_PORT pulses,
|
||||
// print that instead of sys.position.
|
||||
print_steps(0);
|
||||
|
||||
if (read_serial){
|
||||
next_byte_tick+=sim.baud_ticks;
|
||||
//recent block can only change after input, so check here.
|
||||
printBlock();
|
||||
}
|
||||
|
||||
//TODO:
|
||||
// set limit pins based on position,
|
||||
// set probe pin when probing.
|
||||
// if VARIABLE_SPINDLE, measure pwm pin to report speed?
|
||||
}
|
||||
|
||||
platform_sleep(0); //yield
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//show current position in steps
|
||||
void print_steps(bool force)
|
||||
{
|
||||
static plan_block_t* printed_block = NULL;
|
||||
plan_block_t* current_block = plan_get_current_block();
|
||||
|
||||
if (sim.next_print_time == 0.0) { return; } //no printing
|
||||
if (current_block != printed_block ) {
|
||||
//new block.
|
||||
if (block_number) { //print values from the end of prev block
|
||||
fprintf(args.step_out_file, "%20.15f %d, %d, %d\n", sim.sim_time, sys.position[X_AXIS], sys.position[Y_AXIS], sys.position[Z_AXIS]);
|
||||
}
|
||||
printed_block = current_block;
|
||||
if (current_block == NULL) { return; }
|
||||
// print header
|
||||
fprintf(args.step_out_file, "# block number %d\n", block_number++);
|
||||
}
|
||||
//print at correct interval while executing block
|
||||
else if ((current_block && sim.sim_time>=sim.next_print_time) || force ) {
|
||||
fprintf(args.step_out_file, "%20.15f %d, %d, %d\n", sim.sim_time, sys.position[X_AXIS], sys.position[Y_AXIS], sys.position[Z_AXIS]);
|
||||
fflush(args.step_out_file);
|
||||
|
||||
//make sure the simulation time doesn't get ahead of next_print_time
|
||||
while (sim.next_print_time<=sim.sim_time) sim.next_print_time += args.step_time;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Functions for peeking inside planner state:
|
||||
plan_block_t *get_block_buffer();
|
||||
uint8_t get_block_buffer_head();
|
||||
uint8_t get_block_buffer_tail();
|
||||
|
||||
// Returns the index of the previous block in the ring buffer
|
||||
uint8_t prev_block_index(uint8_t block_index)
|
||||
{
|
||||
@ -123,95 +176,49 @@ uint8_t prev_block_index(uint8_t block_index)
|
||||
return(block_index);
|
||||
}
|
||||
|
||||
block_t *get_block_buffer();
|
||||
uint8_t get_block_buffer_head();
|
||||
uint8_t get_block_buffer_tail();
|
||||
|
||||
block_t *plan_get_recent_block() {
|
||||
plan_block_t *plan_get_recent_block() {
|
||||
if (get_block_buffer_head() == get_block_buffer_tail()) { return(NULL); }
|
||||
return(get_block_buffer()+prev_block_index(get_block_buffer_head()));
|
||||
}
|
||||
|
||||
|
||||
// Print information about the most recently inserted block
|
||||
// but only once!
|
||||
void printBlock() {
|
||||
block_t *b;
|
||||
static block_t *last_block;
|
||||
|
||||
//printf("printBlock()\n");
|
||||
plan_block_t *b;
|
||||
static plan_block_t *last_block;
|
||||
|
||||
b= plan_get_recent_block();
|
||||
if(b!=last_block && b!=NULL) {
|
||||
//fprintf(block_out_file,"%s\n", line);
|
||||
//fprintf(block_out_file," block: ");
|
||||
if(b->direction_bits & (1<<X_DIRECTION_BIT)) block_position[0]-= b->steps_x;
|
||||
else block_position[0]+= b->steps_x;
|
||||
fprintf(block_out_file,"%d, ", block_position[0]);
|
||||
|
||||
if(b->direction_bits & (1<<Y_DIRECTION_BIT)) block_position[1]-= b->steps_y;
|
||||
else block_position[1]+= b->steps_y;
|
||||
fprintf(block_out_file,"%d, ", block_position[1]);
|
||||
|
||||
if(b->direction_bits & (1<<Z_DIRECTION_BIT)) block_position[2]-= b->steps_z;
|
||||
else block_position[2]+= b->steps_z;
|
||||
fprintf(block_out_file,"%d, ", block_position[2]);
|
||||
|
||||
fprintf(block_out_file,"%f", b->entry_speed_sqr);
|
||||
fprintf(block_out_file,"\n");
|
||||
int i;
|
||||
for (i=0;i<N_AXIS;i++){
|
||||
if(b->direction_bits & get_direction_mask(i)) block_position[i]-= b->steps[i];
|
||||
else block_position[i]+= b->steps[i];
|
||||
fprintf(args.block_out_file,"%d, ", block_position[i]);
|
||||
}
|
||||
fprintf(args.block_out_file,"%f", b->entry_speed_sqr);
|
||||
fprintf(args.block_out_file,"\n");
|
||||
fflush(args.block_out_file); //TODO: needed?
|
||||
|
||||
last_block= b;
|
||||
}
|
||||
}
|
||||
|
||||
// The simulator assumes that grbl is fast enough to keep the buffer full.
|
||||
// Thus, the stepper interrupt is only called when the buffer is full and then only to
|
||||
// finish one block.
|
||||
// Only when plan_synchronize() wait for the whole buffer to clear, the stepper interrupt
|
||||
// to finish all pending moves.
|
||||
void handle_buffer() {
|
||||
// runtime_second_call is reset by serial_write() after every command.
|
||||
// Only when execute_runtime() is called repeatedly by plan_synchronize()
|
||||
// runtime_second_call will be incremented above 2
|
||||
//printf("handle_buffer()\n");
|
||||
if(plan_check_full_buffer() || runtime_second_call>2) {
|
||||
sim_stepper(step_out_file);
|
||||
} else {
|
||||
runtime_second_call++;
|
||||
|
||||
//printer for grbl serial port output
|
||||
void grbl_out(uint8_t data){
|
||||
static uint8_t buf[128]={0};
|
||||
static uint8_t len=0;
|
||||
static bool continuation = 0;
|
||||
|
||||
buf[len++]=data;
|
||||
if(data=='\n' || data=='\r' || len>=127) {
|
||||
if (args.comment_char && !continuation){
|
||||
fprintf(args.grbl_out_file,"%c ",args.comment_char);
|
||||
}
|
||||
buf[len]=0;
|
||||
fprintf(args.grbl_out_file,"%s",buf);
|
||||
continuation = (len>=128); //print comment on next line unless we are only printing to avoid buffer overflow)
|
||||
len=0;
|
||||
}
|
||||
}
|
||||
|
||||
double get_step_time() {
|
||||
/* code for the old stepper algorithm
|
||||
uint16_t ceiling;
|
||||
uint16_t prescaler;
|
||||
uint32_t actual_cycles;
|
||||
uint8_t invalid_prescaler= 0;
|
||||
|
||||
prescaler= ((TCCR1B>>CS10) & 0x07) - 1;
|
||||
ceiling= OCR1A;
|
||||
|
||||
switch(prescaler) {
|
||||
case 0:
|
||||
actual_cycles= ceiling;
|
||||
break;
|
||||
case 1:
|
||||
actual_cycles= ceiling * 8L;
|
||||
break;
|
||||
case 2:
|
||||
actual_cycles = ceiling * 64L;
|
||||
break;
|
||||
case 3:
|
||||
actual_cycles = ceiling * 256L;
|
||||
break;
|
||||
case 4:
|
||||
actual_cycles = ceiling * 1024L;
|
||||
break;
|
||||
default:
|
||||
invalid_prescaler= 1;
|
||||
}
|
||||
|
||||
if(invalid_prescaler) return 12345.0;
|
||||
else return (double)actual_cycles/F_CPU;*/
|
||||
return (double)((ocr2a+1)*8)/(double)(F_CPU);
|
||||
}
|
||||
|
@ -25,36 +25,56 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include "../nuts_bolts.h"
|
||||
#include "../system.h"
|
||||
#include "platform.h"
|
||||
|
||||
// Output file handles
|
||||
extern FILE *block_out_file;
|
||||
extern FILE *step_out_file;
|
||||
//simulation globals
|
||||
typedef struct sim_vars {
|
||||
uint64_t masterclock;
|
||||
double sim_time; //current time of the simulation
|
||||
uint8_t started; //don't start timers until first char recieved.
|
||||
uint8_t exit;
|
||||
float speedup;
|
||||
int32_t baud_ticks;
|
||||
double next_print_time;
|
||||
|
||||
// This variable is needed to determine if execute_runtime() is called in a loop
|
||||
// waiting for the buffer to empty, as in plan_synchronize()
|
||||
extern int runtime_second_call;
|
||||
} sim_vars_t;
|
||||
extern sim_vars_t sim;
|
||||
|
||||
|
||||
typedef struct arg_vars {
|
||||
// Output file handles
|
||||
FILE *block_out_file;
|
||||
FILE *step_out_file;
|
||||
FILE *grbl_out_file;
|
||||
// Minimum time step for printing stepper values. //Given by user via command line
|
||||
double step_time;
|
||||
//char to prefix comments; default '#'
|
||||
uint8_t comment_char;
|
||||
|
||||
} arg_vars_t;
|
||||
extern arg_vars_t args;
|
||||
|
||||
// Minimum time step for printing stepper values. Given by user via command line
|
||||
extern double step_time;
|
||||
|
||||
// global system variable structure for position etc.
|
||||
extern system_t sys;
|
||||
extern uint8_t print_comment;
|
||||
|
||||
// setup avr simulation
|
||||
void init_simulator(float time_multiplier);
|
||||
|
||||
//shutdown simulator - close open files
|
||||
int shutdown_simulator(uint8_t exitflag);
|
||||
|
||||
//simulates the hardware until sim.exit is set.
|
||||
void sim_loop();
|
||||
|
||||
// Call the stepper interrupt until one block is finished
|
||||
void sim_stepper();
|
||||
|
||||
// Check if buffer is full or if plan_synchronize() wants to clear the buffer
|
||||
void handle_buffer();
|
||||
void simulate_serial();
|
||||
|
||||
// Print information about the most recently inserted block
|
||||
void printBlock();
|
||||
|
||||
// Calculate the time between stepper interrupt calls from TCCR1B and OCR1A AVR registers
|
||||
// which are set in config_step_timer in stepper.c
|
||||
// This reconstructs the stepper-internal value of variable st.cycles_per_step_event
|
||||
// The reconstruction is done to truely decouple the simulator from the actual grbl code
|
||||
double get_step_time();
|
||||
//printer for grbl serial port output
|
||||
void grbl_out(uint8_t char_out);
|
||||
|
||||
#endif
|
||||
|
@ -60,6 +60,7 @@ void spindle_run(uint8_t direction, float rpm)
|
||||
if (sys.state == STATE_CHECK_MODE) { return; }
|
||||
|
||||
// Empty planner buffer to ensure spindle is set when programmed.
|
||||
protocol_auto_cycle_start();
|
||||
protocol_buffer_synchronize();
|
||||
|
||||
// Halt or set spindle direction and rpm.
|
||||
|
16
stepper.c
16
stepper.c
@ -76,6 +76,9 @@ typedef struct {
|
||||
#else
|
||||
uint8_t prescaler; // Without AMASS, a prescaler is required to adjust for slow timing.
|
||||
#endif
|
||||
#ifdef AUTO_REPORT_MOVE_DONE
|
||||
uint8_t block_end; //true for last segment of a block
|
||||
#endif
|
||||
} segment_t;
|
||||
static segment_t segment_buffer[SEGMENT_BUFFER_SIZE];
|
||||
|
||||
@ -242,7 +245,7 @@ void st_go_idle()
|
||||
is usually not a physical problem at higher frequencies, although audible.
|
||||
To improve Bresenham multi-axis performance, Grbl uses what we call an Adaptive Multi-Axis
|
||||
Step Smoothing (AMASS) algorithm, which does what the name implies. At lower step frequencies,
|
||||
AMASS artificially increases the Bresenham resolution without effecting the algorithm's
|
||||
AMASS artificially increases the Bresenham resolution without affecting the algorithm's
|
||||
innate exactness. AMASS adapts its resolution levels automatically depending on the step
|
||||
frequency to be executed, meaning that for even lower step frequencies the step smoothing
|
||||
level increases. Algorithmically, AMASS is acheived by a simple bit-shifting of the Bresenham
|
||||
@ -281,7 +284,7 @@ void st_go_idle()
|
||||
// with probing and homing cycles that require true real-time positions.
|
||||
ISR(TIMER1_COMPA_vect)
|
||||
{
|
||||
// SPINDLE_ENABLE_PORT ^= 1<<SPINDLE_ENABLE_BIT; // Debug: Used to time ISR
|
||||
// TIMING_ENABLE_PORT ^= 1<<TIMING_ENABLE_BIT; // Debug: Used to time ISR
|
||||
if (busy) { return; } // The busy-flag is used to avoid reentering this interrupt
|
||||
|
||||
// Set the direction pins a couple of nanoseconds before we step the steppers
|
||||
@ -395,6 +398,9 @@ ISR(TIMER1_COMPA_vect)
|
||||
st.step_count--; // Decrement step events count
|
||||
if (st.step_count == 0) {
|
||||
// Segment is complete. Discard current segment and advance segment indexing.
|
||||
#ifdef AUTO_REPORT_MOVE_DONE
|
||||
sys.execute |= st.exec_segment->block_end;
|
||||
#endif
|
||||
st.exec_segment = NULL;
|
||||
if ( ++segment_buffer_tail == SEGMENT_BUFFER_SIZE) { segment_buffer_tail = 0; }
|
||||
}
|
||||
@ -793,8 +799,14 @@ void st_prep_buffer()
|
||||
// Normal operation. Block incomplete. Distance remaining in block to be executed.
|
||||
pl_block->millimeters = mm_remaining;
|
||||
prep.steps_remaining = steps_remaining;
|
||||
#ifdef AUTO_REPORT_MOVE_DONE
|
||||
prep_segment->block_end = 0;
|
||||
#endif
|
||||
} else {
|
||||
// End of planner block or forced-termination. No more distance to be executed.
|
||||
#ifdef AUTO_REPORT_MOVE_DONE
|
||||
prep_segment->block_end = EXEC_STATUS_REPORT; // force move-done report
|
||||
#endif
|
||||
if (mm_remaining > 0.0) { // At end of forced-termination.
|
||||
// Reset prep parameters for resuming and then bail.
|
||||
// NOTE: Currently only feed holds qualify for this scenario. May change with overrides.
|
||||
|
Loading…
Reference in New Issue
Block a user