mirror of
https://github.com/synthetos/g2.git
synced 2026-02-06 11:11:57 +08:00
339 lines
12 KiB
C++
339 lines
12 KiB
C++
/*
|
|
* spindle.cpp - canonical machine spindle driver
|
|
* This file is part of the g2core project
|
|
*
|
|
* Copyright (c) 2010 - 2017 Alden S. Hart, Jr.
|
|
*
|
|
* This file ("the software") is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License, version 2 as published by the
|
|
* Free Software Foundation. You should have received a copy of the GNU General Public
|
|
* License, version 2 along with the software. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* As a special exception, you may use this file as part of a software library without
|
|
* restriction. Specifically, if other files instantiate templates or use macros or
|
|
* inline functions from this file, or you compile this file and link it with other
|
|
* files to produce an executable, this file does not by itself cause the resulting
|
|
* executable to be covered by the GNU General Public License. This exception does not
|
|
* however invalidate any other reasons why the executable file might be covered by the
|
|
* GNU General Public License.
|
|
*
|
|
* THE SOFTWARE IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT WITHOUT ANY
|
|
* WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
|
* SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
|
|
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
#include "g2core.h" // #1 dependency order
|
|
#include "config.h" // #2
|
|
#include "canonical_machine.h" // #3
|
|
#include "text_parser.h" // #4
|
|
|
|
#include "spindle.h"
|
|
#include "planner.h"
|
|
#include "hardware.h"
|
|
#include "pwm.h"
|
|
#include "util.h"
|
|
|
|
/**** Allocate structures ****/
|
|
|
|
cmSpindleton_t spindle;
|
|
|
|
/**** Static functions ****/
|
|
|
|
static void _exec_spindle_speed(float *value, bool *flag);
|
|
static void _exec_spindle_control(float *value, bool *flag);
|
|
static float _get_spindle_pwm (cmSpindleEnable enable, cmSpindleDir direction);
|
|
|
|
/*
|
|
* spindle_init()
|
|
* spindle_reset() - stop spindle, set speed to zero, and reset values
|
|
*/
|
|
void spindle_init()
|
|
{
|
|
if( pwm.c[PWM_1].frequency < 0 ) {
|
|
pwm.c[PWM_1].frequency = 0;
|
|
}
|
|
pwm_set_freq(PWM_1, pwm.c[PWM_1].frequency);
|
|
pwm_set_duty(PWM_1, pwm.c[PWM_1].phase_off);
|
|
}
|
|
|
|
void spindle_reset()
|
|
{
|
|
float value[AXES] = { 0,0,0,0,0,0 }; // set spindle speed to zero
|
|
bool flags[] = { 1,0,0,0,0,0 };
|
|
_exec_spindle_speed(value, flags);
|
|
cm_spindle_off_immediate(); // turn spindle off
|
|
}
|
|
|
|
/*
|
|
* cm_set_spindle_speed() - queue the S parameter to the planner buffer
|
|
* _exec_spindle_speed() - spindle speed callback from planner queue
|
|
*/
|
|
|
|
stat_t cm_set_spindle_speed(float speed)
|
|
{
|
|
// if (speed > cfg.max_spindle speed) { return (STAT_MAX_SPINDLE_SPEED_EXCEEDED);}
|
|
|
|
float value[AXES] = { speed, 0,0,0,0,0 };
|
|
bool flags[] = { 1,0,0,0,0,0 };
|
|
mp_queue_command(_exec_spindle_speed, value, flags);
|
|
return (STAT_OK);
|
|
}
|
|
|
|
static void _exec_spindle_speed(float *value, bool *flag)
|
|
{
|
|
if (flag[0]) {
|
|
spindle.speed = value[0];
|
|
}
|
|
|
|
if (flag[1]) {
|
|
spindle.direction = (cmSpindleDir)value[1];
|
|
}
|
|
|
|
// update spindle speed if we're running
|
|
pwm_set_duty(PWM_1, _get_spindle_pwm(spindle.enable, spindle.direction));
|
|
}
|
|
|
|
/*
|
|
* cm_spindle_off_immediate() - turn on/off spindle w/o planning
|
|
* cm_spindle_optional_pause() - pause spindle immediately if option is true
|
|
* cm_spindle_resume() - restart a paused spindle with an optional dwell
|
|
*/
|
|
|
|
void cm_spindle_off_immediate()
|
|
{
|
|
spindle.enable = SPINDLE_OFF;
|
|
float value[] = { (float)SPINDLE_OFF, 0,0,0,0,0 };
|
|
bool flags[] = { 1,0,0,0,0,0 };
|
|
_exec_spindle_control(value, flags);
|
|
}
|
|
|
|
void cm_spindle_optional_pause(bool option)
|
|
{
|
|
if (option && spindle.enable == SPINDLE_ON) {
|
|
cm_spindle_off_immediate();
|
|
spindle.enable = SPINDLE_PAUSE;
|
|
}
|
|
}
|
|
|
|
void cm_spindle_resume(float dwell_seconds)
|
|
{
|
|
if(spindle.enable == SPINDLE_PAUSE) {
|
|
spindle.enable = SPINDLE_ON;
|
|
mp_request_out_of_band_dwell(dwell_seconds);
|
|
float value[] = { (float)SPINDLE_ON, (float)spindle.direction, 0,0,0,0 };
|
|
bool flags[] = { 1,0,0,0,0,0 };
|
|
_exec_spindle_control(value, flags);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* cm_spindle_control() - queue the spindle command to the planner buffer. Observe PAUSE
|
|
* _exec_spindle_control() - actually execute the spindle command
|
|
*/
|
|
|
|
stat_t cm_spindle_control(uint8_t control) // requires SPINDLE_CONTROL_xxx style args
|
|
{
|
|
if (control == SPINDLE_CONTROL_OFF) {
|
|
spindle.enable = SPINDLE_OFF;
|
|
} else {
|
|
spindle.enable = SPINDLE_ON;
|
|
if (control == SPINDLE_CONTROL_CW) {
|
|
spindle.direction = SPINDLE_CW;
|
|
} else {
|
|
spindle.direction = SPINDLE_CCW;
|
|
}
|
|
}
|
|
|
|
float value[] = { (float)spindle.enable, (float)spindle.direction, 0,0,0,0 };
|
|
bool flags[] = { 1,1,0,0,0,0 };
|
|
mp_queue_command(_exec_spindle_control, value, flags);
|
|
return(STAT_OK);
|
|
}
|
|
|
|
#define _set_spindle_enable_bit_hi() spindle_enable_pin.set()
|
|
#define _set_spindle_enable_bit_lo() spindle_enable_pin.clear()
|
|
#define _set_spindle_direction_bit_hi() spindle_dir_pin.set()
|
|
#define _set_spindle_direction_bit_lo() spindle_dir_pin.clear()
|
|
|
|
static void _exec_spindle_control(float *value, bool *flag)
|
|
{
|
|
// set the direction first
|
|
if (flag[1])
|
|
{
|
|
spindle.direction = (cmSpindleDir)value[1]; // record spindle direction in the struct
|
|
if (spindle.direction ^ spindle.dir_polarity) {
|
|
_set_spindle_direction_bit_hi();
|
|
} else {
|
|
_set_spindle_direction_bit_lo();
|
|
}
|
|
}
|
|
|
|
if (flag[0])
|
|
{
|
|
// set on/off. Mask out PAUSE and consider it OFF
|
|
spindle.enable = (cmSpindleEnable)value[0]; // record spindle enable in the struct
|
|
if ((spindle.enable & 0x01) ^ spindle.enable_polarity) {
|
|
_set_spindle_enable_bit_lo();
|
|
} else {
|
|
_set_spindle_enable_bit_hi();
|
|
}
|
|
}
|
|
|
|
pwm_set_duty(PWM_1, _get_spindle_pwm(spindle.enable, spindle.direction));
|
|
}
|
|
|
|
/*
|
|
* _get_spindle_pwm() - return PWM phase (duty cycle) for dir and speed
|
|
*/
|
|
|
|
static float _get_spindle_pwm (cmSpindleEnable enable, cmSpindleDir direction)
|
|
{
|
|
float speed_lo=0, speed_hi=0, phase_lo=0, phase_hi=0;
|
|
if (direction == SPINDLE_CW ) {
|
|
speed_lo = pwm.c[PWM_1].cw_speed_lo;
|
|
speed_hi = pwm.c[PWM_1].cw_speed_hi;
|
|
phase_lo = pwm.c[PWM_1].cw_phase_lo;
|
|
phase_hi = pwm.c[PWM_1].cw_phase_hi;
|
|
} else { // if (direction == SPINDLE_CCW ) {
|
|
speed_lo = pwm.c[PWM_1].ccw_speed_lo;
|
|
speed_hi = pwm.c[PWM_1].ccw_speed_hi;
|
|
phase_lo = pwm.c[PWM_1].ccw_phase_lo;
|
|
phase_hi = pwm.c[PWM_1].ccw_phase_hi;
|
|
}
|
|
|
|
if (enable == SPINDLE_ON) {
|
|
// clamp spindle speed to lo/hi range
|
|
if (spindle.speed < speed_lo) {
|
|
spindle.speed = speed_lo;
|
|
}
|
|
if (spindle.speed > speed_hi) {
|
|
spindle.speed = speed_hi;
|
|
}
|
|
// normalize speed to [0..1]
|
|
float speed = (spindle.speed - speed_lo) / (speed_hi - speed_lo);
|
|
return (speed * (phase_hi - phase_lo)) + phase_lo;
|
|
} else {
|
|
return pwm.c[PWM_1].phase_off;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* cm_spindle_override_control()
|
|
* cm_start_spindle_override()
|
|
* cm_end_spindle_override()
|
|
*/
|
|
|
|
void cm_start_spindle_override(const float ramp_time, const float override_factor)
|
|
{
|
|
return;
|
|
}
|
|
|
|
void cm_end_spindle_override(const float ramp_time)
|
|
{
|
|
return;
|
|
}
|
|
|
|
stat_t cm_sso_control(const float P_word, const bool P_flag) // M51
|
|
{
|
|
bool new_enable = true;
|
|
bool new_override = false;
|
|
if (P_flag) { // if parameter is present in Gcode block
|
|
if (fp_ZERO(P_word)) {
|
|
new_enable = false; // P0 disables override
|
|
} else {
|
|
if (P_word < SPINDLE_OVERRIDE_MIN) {
|
|
return (STAT_INPUT_LESS_THAN_MIN_VALUE);
|
|
}
|
|
if (P_word > SPINDLE_OVERRIDE_MAX) {
|
|
return (STAT_INPUT_EXCEEDS_MAX_VALUE);
|
|
}
|
|
spindle.sso_factor = P_word; // P word is valid, store it.
|
|
new_override = true;
|
|
}
|
|
}
|
|
if (cm->gmx.m48_enable) { // if master enable is ON
|
|
if (new_enable && (new_override || !spindle.sso_enable)) { // 3 cases to start a ramp
|
|
cm_start_spindle_override(SPINDLE_OVERRIDE_RAMP_TIME, spindle.sso_factor);
|
|
} else if (spindle.sso_enable && !new_enable) { // case to turn off the ramp
|
|
cm_end_spindle_override(SPINDLE_OVERRIDE_RAMP_TIME);
|
|
}
|
|
}
|
|
spindle.sso_enable = new_enable; // always update the enable state
|
|
return (STAT_OK);
|
|
}
|
|
|
|
/****************************
|
|
* END OF SPINDLE FUNCTIONS *
|
|
****************************/
|
|
|
|
/***********************************************************************************
|
|
* CONFIGURATION AND INTERFACE FUNCTIONS
|
|
* Functions to get and set variables from the cfgArray table
|
|
***********************************************************************************/
|
|
|
|
/*
|
|
* cm_set_dir() - a cheat to set direction w/o using the M commands
|
|
*
|
|
* This is provided as a way to set and clear spindle direction without using M commands
|
|
* It's here because disabling a spindle (M5) does not change the direction, only the enable.
|
|
*/
|
|
|
|
stat_t cm_set_dir(nvObj_t *nv)
|
|
{
|
|
set_01(nv);
|
|
float value[] = { (float)spindle.enable, (float)spindle.direction, 0,0,0,0 };
|
|
bool flags[] = { 1,1,0,0,0,0 };
|
|
_exec_spindle_control(value, flags);
|
|
return (STAT_OK);
|
|
}
|
|
|
|
|
|
/*
|
|
* cm_set_sso() - set spindle speed feedrate override factor
|
|
*/
|
|
|
|
stat_t cm_set_sso(nvObj_t *nv)
|
|
{
|
|
if (nv->value < SPINDLE_OVERRIDE_MIN) {
|
|
return (STAT_INPUT_LESS_THAN_MIN_VALUE);
|
|
}
|
|
if (nv->value > SPINDLE_OVERRIDE_MAX) {
|
|
return (STAT_INPUT_EXCEEDS_MAX_VALUE);
|
|
}
|
|
set_flt(nv);
|
|
return(STAT_OK);
|
|
}
|
|
|
|
/***********************************************************************************
|
|
* TEXT MODE SUPPORT
|
|
* Functions to print variables from the cfgArray table
|
|
***********************************************************************************/
|
|
|
|
#ifdef __TEXT_MODE
|
|
|
|
const char fmt_spep[] = "[spep] spindle enable polarity%5d [0=active_low,1=active_high]\n";
|
|
const char fmt_spdp[] = "[spdp] spindle direction polarity%2d [0=CW_low,1=CW_high]\n";
|
|
const char fmt_spph[] = "[spph] spindle pause on hold%7d [0=no,1=pause_on_hold]\n";
|
|
const char fmt_spdw[] = "[spdw] spindle dwell time%12.1f seconds\n";
|
|
const char fmt_ssoe[] ="[ssoe] spindle speed override ena%2d [0=disable,1=enable]\n";
|
|
const char fmt_sso[] ="[sso] spindle speed override%11.3f [0.050 < sso < 2.000]\n";
|
|
const char fmt_spe[] = "Spindle Enable:%7d [0=OFF,1=ON,2=PAUSE]\n";
|
|
const char fmt_spd[] = "Spindle Direction:%4d [0=CW,1=CCW]\n";
|
|
const char fmt_sps[] = "Spindle Speed: %7.0f rpm\n";
|
|
|
|
void cm_print_spep(nvObj_t *nv) { text_print(nv, fmt_spep);} // TYPE_INT
|
|
void cm_print_spdp(nvObj_t *nv) { text_print(nv, fmt_spdp);} // TYPE_INT
|
|
void cm_print_spph(nvObj_t *nv) { text_print(nv, fmt_spph);} // TYPE_INT
|
|
void cm_print_spdw(nvObj_t *nv) { text_print(nv, fmt_spdw);} // TYPE_FLOAT
|
|
void cm_print_ssoe(nvObj_t *nv) { text_print(nv, fmt_ssoe);} // TYPE INT
|
|
void cm_print_sso(nvObj_t *nv) { text_print(nv, fmt_sso);} // TYPE FLOAT
|
|
void cm_print_spe(nvObj_t *nv) { text_print(nv, fmt_spe);} // TYPE_INT
|
|
void cm_print_spd(nvObj_t *nv) { text_print(nv, fmt_spd);} // TYPE_INT
|
|
void cm_print_sps(nvObj_t *nv) { text_print(nv, fmt_sps);} // TYPE_FLOAT
|
|
|
|
#endif // __TEXT_MODE
|