/*
simulator.c - functions to simulate how the buffer is emptied and the
stepper interrupt is called
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 .
*/
#include
#include
#include
#include
#include "../stepper.h"
#include "../planner.h"
#include "../nuts_bolts.h"
#include "simulator.h"
#include "avr/interrupt.h" //for registers and isr declarations.
#include "eeprom.h"
int block_position[]= {0,0,0}; //step count after most recently planned block
uint32_t block_number= 0;
sim_vars_t sim={0};
//local prototypes
void print_steps(bool force);
//setup
void init_simulator(float time_multiplier) {
//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
sim.next_print_time = args.step_time;
sim.speedup = time_multiplier;
sim.baud_ticks = (int)((double)F_CPU*8/BAUD_RATE); //ticks per byte
}
//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.
}
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)
{
if (block_index == 0) { block_index = BLOCK_BUFFER_SIZE; }
block_index--;
return(block_index);
}
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() {
plan_block_t *b;
static plan_block_t *last_block;
b= plan_get_recent_block();
if(b!=last_block && b!=NULL) {
int i;
for (i=0;idirection_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;
}
}
//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;
}
}