Commit b3cca544 authored by MagoKimbra's avatar MagoKimbra

Add Cartesian Correction

Add Hysteresis correction
Add Zwobble correction
parent c1e40d24
...@@ -52,6 +52,11 @@ ...@@ -52,6 +52,11 @@
* M84 - Disable steppers until next move, or use S[seconds] to specify an inactivity timeout, after which the steppers will be disabled. S0 to disable the timeout. * M84 - Disable steppers until next move, or use S[seconds] to specify an inactivity timeout, after which the steppers will be disabled. S0 to disable the timeout.
* M85 - Set inactivity shutdown timer with parameter S[seconds]. To disable set zero (default) * M85 - Set inactivity shutdown timer with parameter S[seconds]. To disable set zero (default)
* M92 - Set axis_steps_per_unit - same syntax as G92 * M92 - Set axis_steps_per_unit - same syntax as G92
* M96 - Print ZWobble value
* M97 - Set ZWobble parameter M97 A<Amplitude_in_mm> W<period_in_mm> P<phase_in_degrees>
* M98 - Print Hysteresis value
* M99 - Set Hysteresis parameter M99 X<in mm> Y<in mm> Z<in mm> E<in mm>
* M100 - Watch Free Memory (For Debugging Only)
* M104 - Set extruder target temp * M104 - Set extruder target temp
* M105 - Read current temp * M105 - Read current temp
* M106 - Fan on * M106 - Fan on
......
### Version 4.2.7 ### Version 4.2.7
* Add M906 Set motor Currents for ALLIGATOR board * Add M906 Set motor Currents for ALLIGATOR board
* Add M408 JSON OUTPUT * Add M408 JSON OUTPUT
* Add Cartesian Correction Hysteresis and Zwooble
* Bug fix
### Version 4.2.6 ### Version 4.2.6
* Bug Fix * Bug Fix
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
* - Axis accelleration * - Axis accelleration
* - Homing feedrate * - Homing feedrate
* - Hotend offset * - Hotend offset
* - Cartesian Correction
* *
* Basic-settings can be found in Configuration_Basic.h * Basic-settings can be found in Configuration_Basic.h
* Feature-settings can be found in Configuration_Feature.h * Feature-settings can be found in Configuration_Feature.h
...@@ -423,4 +424,37 @@ ...@@ -423,4 +424,37 @@
//#define HOTEND_OFFSET_Z {0.0, 0.0, 0.0, 0.0} // (in mm) for each hotend, offset of the hotend on the Z axis //#define HOTEND_OFFSET_Z {0.0, 0.0, 0.0, 0.0} // (in mm) for each hotend, offset of the hotend on the Z axis
/*****************************************************************************************/ /*****************************************************************************************/
/*****************************************************************************************
******************************** CARTESIAN CORRECTION ***********************************
*****************************************************************************************
* *
* New functions, Hysteresis and Zwobble. *
* *
* Hysteresis: *
* These are the extra distances that are performed when an axis changes direction *
* to compensate for any mechanical hysteresis your printer has. *
* Set the parameters width M99 X<in mm> Y<in mm> Z<in mm> E<in mm> *
* *
* ZWobble: *
* How to use it: *
* Set the parameters with M97 A<Amplitude_in_mm> W<period_in_mm> P<phase_in_degrees> *
* KNOWN LIMITATION (by design): if you redefine the Z value during your print *
* (with a G92 for example), the correction *will* screw up *
* How does it work? *
* This class compensates for a wobble of the Z axis that makes the translation *
* rod movement->bed (extruder) movement nonlinear. *
* Instead of assuming Zactual = Zrod, the function assumes that *
* Zaxtual = Zrod + A*sin(w*Zrod + phase). Since the user wants to specify Zactual, *
* we need to invert the formula to obtain Zrod, which is the value that will serve *
* as the input of the motor. *
* *
*****************************************************************************************/
//define HYSTERESIS
//define ZWOBBLE
#define DEFAULT_HYSTERESIS_MM 0, 0, 0, 0 // X, Y, Z, E hysteresis in mm.
#define DEFAULT_ZWOBBLE 0, 0, 0 // A, W, P
/*****************************************************************************************/
#endif #endif
\ No newline at end of file
...@@ -90,6 +90,10 @@ ...@@ -90,6 +90,10 @@
* or use S<seconds> to specify an inactivity timeout, after which the steppers will be disabled. S0 to disable the timeout. * or use S<seconds> to specify an inactivity timeout, after which the steppers will be disabled. S0 to disable the timeout.
* M85 - Set inactivity shutdown timer with parameter S<seconds>. To disable set zero (default) * M85 - Set inactivity shutdown timer with parameter S<seconds>. To disable set zero (default)
* M92 - Set axis_steps_per_unit - same syntax as G92 * M92 - Set axis_steps_per_unit - same syntax as G92
* M96 - Print ZWobble value
* M97 - Set ZWobble parameter M97 A<Amplitude_in_mm> W<period_in_mm> P<phase_in_degrees>
* M98 - Print Hysteresis value
* M99 - Set Hysteresis parameter M99 X<in mm> Y<in mm> Z<in mm> E<in mm>
* M100 - Watch Free Memory (For Debugging Only) * M100 - Watch Free Memory (For Debugging Only)
* M104 - Set extruder target temp * M104 - Set extruder target temp
* M105 - Read current temp * M105 - Read current temp
......
...@@ -46,39 +46,23 @@ ...@@ -46,39 +46,23 @@
#include "module/motion/stepper.h" #include "module/motion/stepper.h"
#include "module/motion/stepper_indirection.h" #include "module/motion/stepper_indirection.h"
#include "module/motion/planner.h" #include "module/motion/planner.h"
#include "module/motion/vector_3.h"
#include "module/motion/qr_solve.h"
#include "module/motion/cartesian_correction.h"
#include "module/temperature/temperature.h" #include "module/temperature/temperature.h"
#include "module/temperature/thermistortables.h" #include "module/temperature/thermistortables.h"
#include "module/lcd/ultralcd.h" #include "module/lcd/ultralcd.h"
#include "module/lcd/buzzer.h"
#include "module/nextion/Nextion_lcd.h" #include "module/nextion/Nextion_lcd.h"
#include "module/sd/cardreader.h" #include "module/sd/cardreader.h"
#include "module/servo/servo.h"
#if ENABLED(AUTO_BED_LEVELING_FEATURE) #include "module/watchdog/watchdog.h"
#include "module/motion/vector_3.h" #include "module/blinkm/blinkm.h"
#if ENABLED(AUTO_BED_LEVELING_GRID)
#include "module/motion/qr_solve.h"
#endif
#endif
#if MB(ALLIGATOR) #if MB(ALLIGATOR)
#include "module/alligator/external_dac.h" #include "module/alligator/external_dac.h"
#endif #endif
#if ENABLED(USE_WATCHDOG)
#include "module/watchdog/watchdog.h"
#endif
#if HAS(BUZZER)
#include "module/lcd/buzzer.h"
#endif
#if ENABLED(BLINKM)
#include "module/blinkm/blinkm.h"
#endif
#if HAS(SERVOS)
#include "module/servo/servo.h"
#endif
#if HAS(DIGIPOTSS) #if HAS(DIGIPOTSS)
#include <SPI.h> #include <SPI.h>
#endif #endif
...@@ -87,4 +71,8 @@ ...@@ -87,4 +71,8 @@
#include "module/fwtest/firmware_test.h" #include "module/fwtest/firmware_test.h"
#endif #endif
#if ENABLED(RFID_MODULE)
#include "module/mfrc522/MFRC522_serial.h"
#endif
#endif #endif
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#ifndef _FASTIO_ARDUINO_H #ifndef _FASTIO_ARDUINO_H
#define _FASTIO_ARDUINO_H #define _FASTIO_ARDUINO_H
#include <avr/io.h>
/* /*
utility functions utility functions
......
...@@ -4843,6 +4843,51 @@ inline void gcode_M92() { ...@@ -4843,6 +4843,51 @@ inline void gcode_M92() {
plan_set_position(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]); plan_set_position(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]);
} }
#if ENABLED(ZWOBBLE)
/**
* M96: Print ZWobble value
*/
inline void gcode_M96() {
zwobble.ReportToSerial();
}
/**
* M97: Set ZWobble value
*/
inline void gcode_M97() {
float zVal = -1, hVal = -1, lVal = -1;
if (code_seen('A')) zwobble.setAmplitude(code_value());
if (code_seen('W')) zwobble.setPeriod(code_value());
if (code_seen('P')) zwobble.setPhase(code_value());
if (code_seen('Z')) zVal = code_value();
if (code_seen('H')) hVal = code_value();
if (code_seen('L')) lVal = code_value();
if (zVal >= 0 && hVal >= 0) zwobble.setSample(zVal, hVal);
if (zVal >= 0 && lVal >= 0) zwobble.setScaledSample(zVal, lVal);
if (lVal > 0 && hVal > 0) zwobble.setScalingFactor(hVal/lVal);
}
#endif // ZWOBBLE
#if ENABLED(HYSTERESIS)
/**
* M98: Print Hysteresis value
*/
inline void gcode_M98() {
hysteresis.ReportToSerial();
}
/**
* M99: Set Hysteresis value
*/
inline void gcode_M99() {
for(uint8_t i = 0; i < NUM_AXIS; i++) {
if (code_seen(axis_codes[i]))
hysteresis.SetAxis(i, code_value());
}
}
#endif // HYSTERESIS
/** /**
* M100 Free Memory Watcher * M100 Free Memory Watcher
* *
...@@ -6470,7 +6515,7 @@ inline void gcode_M428() { ...@@ -6470,7 +6515,7 @@ inline void gcode_M428() {
sync_plan_position(); sync_plan_position();
#endif #endif
ECHO_LM(DB, "Offset applied."); ECHO_LM(DB, "Offset applied.");
LCD_ALERTMESSAGEPGM("Offset applied."); LCD_MESSAGEPGM("Offset applied.");
#if HAS(BUZZER) #if HAS(BUZZER)
enqueuecommands_P(PSTR("M300 S659 P200\nM300 S698 P200")); enqueuecommands_P(PSTR("M300 S659 P200\nM300 S698 P200"));
#endif #endif
...@@ -7455,6 +7500,26 @@ void process_next_command() { ...@@ -7455,6 +7500,26 @@ void process_next_command() {
gcode_M85(); break; gcode_M85(); break;
case 92: // M92 Set the steps-per-unit for one or more axes case 92: // M92 Set the steps-per-unit for one or more axes
gcode_M92(); break; gcode_M92(); break;
#if ENABLED(ZWOBBLE)
case 96: // M96 Print ZWobble value
gcode_M96(); break;
case 97: // M97 Set ZWobble parameter
gcode_M97(); break;
#endif
#if ENABLED(HYSTERESIS)
case 98: // M98 Print Hysteresis value
gcode_M98(); break;
case 99: // M99 Set Hysteresis parameter
gcode_M99(); break;
#endif
#if ENABLED(M100_FREE_MEMORY_WATCHER)
case 100:
gcode_M100(); break;
#endif
case 104: // M104 case 104: // M104
gcode_M104(); break; gcode_M104(); break;
case 105: // M105 Read current temperature case 105: // M105 Read current temperature
...@@ -7469,12 +7534,6 @@ void process_next_command() { ...@@ -7469,12 +7534,6 @@ void process_next_command() {
case 109: // M109 Wait for temperature case 109: // M109 Wait for temperature
gcode_M109(); break; gcode_M109(); break;
#if ENABLED(M100_FREE_MEMORY_WATCHER)
case 100:
gcode_M100(); break;
#endif
case 110: break; // M110: Set line number - don't show "unknown command" case 110: break; // M110: Set line number - don't show "unknown command"
case 111: // M111 Set debug level case 111: // M111 Set debug level
gcode_M111(); break; gcode_M111(); break;
......
/* /**
blinkm.cpp - Library for controlling a BlinkM over i2c * blinkm.cpp - Library for controlling a BlinkM over i2c
Created by Tim Koster, August 21 2013. * Created by Tim Koster, August 21 2013.
*/ */
#include "../../base.h" #include "../../base.h"
#if ENABLED(BLINKM) #if ENABLED(BLINKM)
#include "blinkm.h"
#include <Wire.h>
#include "blinkm.h" void SendColors(byte red, byte grn, byte blu) {
#include <Wire.h>
void SendColors(byte red, byte grn, byte blu) {
Wire.begin(); Wire.begin();
Wire.beginTransmission(0x09); Wire.beginTransmission(0x09);
Wire.write('o'); //to disable ongoing script, only needs to be used once Wire.write('o'); //to disable ongoing script, only needs to be used once
...@@ -18,7 +18,6 @@ void SendColors(byte red, byte grn, byte blu) { ...@@ -18,7 +18,6 @@ void SendColors(byte red, byte grn, byte blu) {
Wire.write(grn); Wire.write(grn);
Wire.write(blu); Wire.write(blu);
Wire.endTransmission(); Wire.endTransmission();
} }
#endif // BLINKM #endif // BLINKM
...@@ -4,8 +4,10 @@ ...@@ -4,8 +4,10 @@
*/ */
#ifndef BLINKM_H #ifndef BLINKM_H
#define BLINKM_H #define BLINKM_H
void SendColors(byte red, byte grn, byte blu); #if ENABLED(BLINKM)
void SendColors(byte red, byte grn, byte blu);
#endif
#endif #endif
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#define ER "Error: " // error for host #define ER "Error: " // error for host
#define WT "Wait" // wait for host #define WT "Wait" // wait for host
#define DB "Echo: " // message for user #define DB "Echo: " // message for user
#define DEB "Debug: " // message for debug
#define CFG "Config: " // config for host #define CFG "Config: " // config for host
#define INFO "Info: " // info for host #define INFO "Info: " // info for host
#define RESEND "Resend: " // resend for host #define RESEND "Resend: " // resend for host
......
...@@ -35,8 +35,8 @@ static void ST7920_SWSPI_SND_8BIT(uint8_t val) { ...@@ -35,8 +35,8 @@ static void ST7920_SWSPI_SND_8BIT(uint8_t val) {
} }
} }
#define ST7920_CS() {WRITE(ST7920_CS_PIN,1);u8g_10MicroDelay();} #define ST7920_CS() {WRITE(ST7920_CS_PIN, 1); u8g_10MicroDelay();}
#define ST7920_NCS() {WRITE(ST7920_CS_PIN,0);} #define ST7920_NCS() {WRITE(ST7920_CS_PIN, 0);}
#define ST7920_SET_CMD() {ST7920_SWSPI_SND_8BIT(0xf8);u8g_10MicroDelay();} #define ST7920_SET_CMD() {ST7920_SWSPI_SND_8BIT(0xf8);u8g_10MicroDelay();}
#define ST7920_SET_DAT() {ST7920_SWSPI_SND_8BIT(0xfa);u8g_10MicroDelay();} #define ST7920_SET_DAT() {ST7920_SWSPI_SND_8BIT(0xfa);u8g_10MicroDelay();}
#define ST7920_WRITE_BYTE(a) {ST7920_SWSPI_SND_8BIT((uint8_t)((a)&0xf0u));ST7920_SWSPI_SND_8BIT((uint8_t)((a)<<4u));u8g_10MicroDelay();} #define ST7920_WRITE_BYTE(a) {ST7920_SWSPI_SND_8BIT((uint8_t)((a)&0xf0u));ST7920_SWSPI_SND_8BIT((uint8_t)((a)<<4u));u8g_10MicroDelay();}
......
/**
* cartesian_correction.cpp
* A class that manages hysteresis by inserting extra plan_buffer_line when necessary
* A class that manages ZWobble
*
* Copyright (c) 2016 MagoKimbra
* Copyright (c) 2013 Francesco Santini
* Copyright (c) 2012 Neil James Martin
*
* 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 "../../base.h"
#include "cartesian_correction.h"
#ifdef HYSTERESIS
//===========================================================================
Hysteresis hysteresis(DEFAULT_HYSTERESIS_MM);
float axis_shift[NUM_AXIS] = { 0.0f, 0.0f, 0.0f, 0.0f };
//===========================================================================
Hysteresis::Hysteresis(float x_mm, float y_mm, float z_mm, float e_mm) {
m_prev_direction_bits = 0;
Set(x_mm, y_mm, z_mm, e_mm);
}
//===========================================================================
void Hysteresis::Set(float x_mm, float y_mm, float z_mm, float e_mm) {
m_hysteresis_mm[X_AXIS] = x_mm;
m_hysteresis_mm[Y_AXIS] = y_mm;
m_hysteresis_mm[Z_AXIS] = z_mm;
m_hysteresis_mm[E_AXIS] = e_mm;
m_hysteresis_bits = ((m_hysteresis_mm[X_AXIS] != 0.0f) ? (1 << X_AXIS) : 0)
| ((m_hysteresis_mm[Y_AXIS] != 0.0f) ? (1 << Y_AXIS) : 0)
| ((m_hysteresis_mm[Z_AXIS] != 0.0f) ? (1 << Z_AXIS) : 0)
| ((m_hysteresis_mm[E_AXIS] != 0.0f) ? (1 << E_AXIS) : 0);
calcSteps();
}
//===========================================================================
void Hysteresis::SetAxis(uint8_t axis, float mm) {
m_hysteresis_mm[axis] = mm;
if(mm != 0.0f) m_hysteresis_bits |= ( 1 << axis);
else m_hysteresis_bits &= ~( 1 << axis);
calcSteps();
ReportToSerial();
}
//===========================================================================
void Hysteresis::calcSteps() {
for (uint8_t i = 0; i < NUM_AXIS; i++)
m_hysteresis_steps[i] = (long)(m_hysteresis_mm[i] * axis_steps_per_unit[i]);
}
//===========================================================================
void Hysteresis::ReportToSerial() {
ECHO_SMV(DB, "Hysteresis X", m_hysteresis_mm[X_AXIS]);
ECHO_MV(" Y", m_hysteresis_mm[Y_AXIS]);
ECHO_MV(" Z", m_hysteresis_mm[Z_AXIS]);
ECHO_MV(" E", m_hysteresis_mm[E_AXIS]);
ECHO_MV(" SHIFTS: x=", axis_shift[X_AXIS]);
ECHO_MV(" y=", axis_shift[Y_AXIS]);
ECHO_MV(" z=", axis_shift[Z_AXIS]);
ECHO_EMV(" e=", axis_shift[E_AXIS]);
}
//===========================================================================
// direction 0: positive, 1: negative
uint8_t calc_direction_bits(const long* current_position, const long* destination) {
unsigned char direction_bits = 0;
if (destination[X_AXIS] < current_position[X_AXIS])
direction_bits |= (1 << X_AXIS);
if (destination[Y_AXIS] < current_position[Y_AXIS])
direction_bits |= (1 << Y_AXIS);
if (destination[Z_AXIS] < current_position[Z_AXIS])
direction_bits |= (1 << Z_AXIS);
if (destination[E_AXIS] < current_position[E_AXIS])
direction_bits |= (1 << E_AXIS);
return direction_bits;
}
//===========================================================================
uint8_t calc_move_bits(const long* current_position, const long* destination) {
uint8_t move_bits = 0;
if (destination[X_AXIS] != current_position[X_AXIS])
move_bits |= (1 << X_AXIS);
if (destination[Y_AXIS] != current_position[Y_AXIS])
move_bits |= (1 << Y_AXIS);
if (destination[Z_AXIS] != current_position[Z_AXIS])
move_bits |= (1 << Z_AXIS);
if (destination[E_AXIS] != current_position[E_AXIS])
move_bits |= (1 << E_AXIS);
return move_bits;
}
//===========================================================================
// insert a plan_buffer_line if required to handle any hysteresis
void Hysteresis::InsertCorrection(const float x, const float y, const float z, const float e) {
long destination[NUM_AXIS] = {x * axis_steps_per_unit[X_AXIS], y * axis_steps_per_unit[Y_AXIS], z * axis_steps_per_unit[Z_AXIS], e * axis_steps_per_unit[E_AXIS + active_extruder]};
uint8_t direction_bits = calc_direction_bits(position, destination);
uint8_t move_bits = calc_move_bits(position, destination);
// if the direction has changed in any of the axis that need hysteresis corrections...
uint8_t direction_change_bits = (direction_bits ^ m_prev_direction_bits) & move_bits;
if( (direction_change_bits & m_hysteresis_bits) != 0 ) {
// calculate the position to move to that will fix the hysteresis
for(uint8_t axis = 0; axis < NUM_AXIS; axis++) {
// if this axis changed direction...
if(direction_change_bits & (1 << axis)) {
long fix = (((direction_bits & (1 << axis)) != 0) ? -m_hysteresis_steps[axis] : m_hysteresis_steps[axis]);
//... add the hysteresis: move the current position in the opposite direction so that the next travel move is longer
position[axis] -= fix;
axis_shift[axis] += fix;
}
}
}
m_prev_direction_bits = (direction_bits & move_bits) | (m_prev_direction_bits & ~move_bits);
}
#endif // HYSTERESIS
#ifdef ZWOBBLE
// minimum distance within which two distances in mm are considered equal
#define TOLERANCE_MM 0.01
#define DISTANCE(_A,_B) abs((_A) - (_B))
#define EQUAL_WITHIN_TOLERANCE(_A, _B) (DISTANCE(_A, _B) < TOLERANCE_MM)
#define TWOPI 6.28318530718
#define ZACTUAL_IS_SCALED(_I) (zLut[_I][1] < 0)
#define ZACTUAL(_I) (zLut[_I][1] < 0 ? -zLut[_I][1] * m_scalingFactor : zLut[_I][1])
#define ZROD(_I) zLut[_I][0]
#define SET_ZACTUAL(_I,_V) zLut[_I][1] = _V
#define SET_ZROD(_I,_V) zLut[_I][0] = _V
//===========================================================================
ZWobble zwobble(DEFAULT_ZWOBBLE);
//===========================================================================
ZWobble::ZWobble(float _amplitude, float _period, float _phase) :
m_consistent(false),
lastZ(-1.0),
lastZRod(-1.0),
m_scalingFactor(1.0),
m_sinusoidal(true) { Set(_amplitude, _period, _phase); }
//===========================================================================
void ZWobble::Set(float _amplitude, float _period, float _phase) {
setAmplitude(_amplitude);
setPeriod(_period);
setPhase(_phase);
}
//===========================================================================
bool ZWobble::areParametersConsistent() {
if (!m_sinusoidal) {
m_consistent = true; // parameters are always consistent if lut is not sinusoidal
return true; // if the model is not sinusoidal, then don't check for consistency
}
// m_amplitude*m_puls must be less than 1 in order for the function to be invertible (otherwise it would mean that the wobble is so much that the axis goes back)
if (m_puls <= 0 || m_amplitude <= 0 || (m_amplitude * m_puls) >= 1) {
m_consistent = false;
return false;
}
m_consistent = true;
return true;
}
//===========================================================================
void ZWobble::setScaledSample(float zRod, float zScaledLength) {
// We want to be able to correct scaling factor or set it before/after samples, so (ICK) store scaled samples as negative numbers
setSample(zRod, -zScaledLength);
// Make sure we have a non-zero scaling factor
if (!m_scalingFactor) {
m_scalingFactor = 1.0;
}
// Find two scaled samples close to period
float period = TWOPI / m_puls;
int s1 = -1, s2 = -1;
for (int i = 0; i < lutSize; i++) {
if (ZACTUAL_IS_SCALED(i)) {
s1 = s2;
s2 = i;
if (ZROD(s2) >= period) break;
}
}
// Calculate scaling factor so zact[period] = zrod[period]
if (s2 >= 0 && ZACTUAL(s2)) {
// Case 1 - Only one sample
if (s1 < 0) {
m_scalingFactor *= ZROD(s2) / ZACTUAL(s2);
}
// Case 2 - Samples bracketing period (s1 - p - s2): average zact
else if (ZROD(s2) > period) {
float gap1 = period - ZROD(s1);
float gap2 = ZROD(s2) - period;
float zActPeriod = ZACTUAL(s1) + (ZACTUAL(s2) - ZACTUAL(s1)) * gap1 / (gap1 + gap2);
m_scalingFactor *= period / zActPeriod;
}
// Case 3 - Both samples before period (s1 - s2 - p): extrapolate zact
else {
float gap1 = ZROD(s2) - ZROD(s1);
float gap2 = period - ZROD(s2);
float zActPeriod = ZACTUAL(s2) + (ZACTUAL(s2) - ZACTUAL(s1)) * gap2 / gap1;
m_scalingFactor *= period / zActPeriod;
}
}
}
//===========================================================================
void ZWobble::setScalingFactor(float zActualPerScaledLength) {
m_scalingFactor = zActualPerScaledLength;
}
//===========================================================================
void ZWobble::setSample(float zRod, float zActual) {
if (debugLevel & DEBUG_DEBUG) {
ECHO_SMV(DB, "New sample Rod: ", zRod);
ECHO_EMV(" Act: ", zActual);
}
if (m_puls <= 0) {
ECHO_LM(DB, "You must define a period first (M97 W...)");
return;
}
if (m_sinusoidal) {
m_sinusoidal = false;
calculateLut(); // initializes the LUT to linear
}
insertInLut(zRod, zActual);
}
//===========================================================================
void ZWobble::insertInLut(float zRod, float zActual) {
// check if the given zRod alread exists in LUT
for (int i = 0; i < lutSize; i++) {
if (EQUAL_WITHIN_TOLERANCE(zRod, ZROD(i))) {
// replace value
SET_ZROD(i, zRod);
SET_ZACTUAL(i, zActual);
return;
}
}
// ok the value does not exist: is there still space in LUT? Insert it
if (lutSize < STEPS_IN_ZLUT) {
int zPlace = -1;
for (int i = 0; i < lutSize; i++) {
if (ZROD(i) > zRod) {
zPlace = i;
break;
}
}
// shift samples after zPlace
for (int i = lutSize; i > zPlace; i--) {
SET_ZROD(i, ZROD(i - 1));
SET_ZACTUAL(i, ZACTUAL(i - 1));
}
lutSize++; // increase lutSize
// insert sample
SET_ZROD(zPlace, zRod);
SET_ZACTUAL(zPlace, zActual);
return;
}
else {
// lutSize == STEPS_IN_ZLUT: replace the closest point with the new sample
int zPlace = 0;
float dist = DISTANCE(zRod, ZROD(zPlace));
for (int i = 1; i < lutSize; i++) {
if (DISTANCE(zRod, ZROD(i)) < dist) {
zPlace = i;
dist = DISTANCE(zRod, ZROD(i));
}
}
SET_ZROD(zPlace, zRod);
SET_ZACTUAL(zPlace, zActual);
}
}
//===========================================================================
void ZWobble::initLinearLut() {
float period = TWOPI / m_puls;
lutSize = 2; // only 2 samples originally
SET_ZROD(0, 0);
SET_ZACTUAL(0, 0);
SET_ZROD(1, period);
SET_ZACTUAL(1, period);
}
//===========================================================================
// calculate the ZRod -> Zactual LUT using the model Zactual = Zrod + sin(w*Zrod) - this will actually only be used for one period
void ZWobble::calculateLut() {
lastZ = -1.0;
lastZRod = -1.0; // reinitialize memorized Z values since we are changing the model
if (!areParametersConsistent()) return;
if (!m_sinusoidal) {
initLinearLut();
return; // if the model is not sinusoidal, initializes LUT to linear
}
lutSize = STEPS_IN_ZLUT;
float period = TWOPI / m_puls;
// divide the period in STEPS_IN_ZLUT steps
float lutStep = period / STEPS_IN_ZLUT;
for (int i = 0; i < STEPS_IN_ZLUT; i++) {
float zRod = lutStep * i;
SET_ZROD(i, zRod);
SET_ZACTUAL(i, zRod + m_amplitude * sin(m_puls * zRod));
}
}
//===========================================================================
void ZWobble::setAmplitude(float _amplitude) {
m_amplitude = _amplitude;
m_sinusoidal = true; // setAmplitude sets to sinusoidal by default
calculateLut();
}
//===========================================================================
void ZWobble::setPeriod(float _period) {
if (_period <= 0) return;
m_puls = TWOPI/_period;
calculateLut();
}
//===========================================================================
void ZWobble::setPhase(float _phase ) {
// poor man's modulo operation
while (_phase > 0) _phase -= 360;
while (_phase < 0) _phase += 360;
// phase now will be between 0 and 360
m_phase = (_phase * M_PI / 180); // convert phase to radians
}
//===========================================================================
void ZWobble::ReportToSerial() {
if (!m_sinusoidal)
ECHO_SM(DB, "Custom wobble function");
else {
ECHO_SMV(DB, "ZWobble Amp(A): ", m_amplitude);
}
ECHO_MV(" phase(P): ", m_phase);
ECHO_MV(" period(W): ", TWOPI / m_puls);
ECHO_MV(" puls: ", m_puls);
if (!areParametersConsistent())
ECHO_M(" Warning! Inconsistent parameters!");
ECHO_E;
if (!m_sinusoidal) {
// print out the LUT
for (int i = 0; i < lutSize; i++) {
ECHO_SMV(DB, "Rod: ", ZROD(i));
ECHO_MV(" Act: ", ZACTUAL(i));
int delta = (ZACTUAL(i) - ZROD(i)) * 200 + 20;
for (int j = 0; j < delta; j++) {
ECHO_M(" ");
}
ECHO_EM(" +");
}
}
}
//===========================================================================
float ZWobble::findInLut(float z) {
int i = 0;
if (z >= ZACTUAL(lutSize - 1))
return ZROD(lutSize - 1);
if (z <= ZACTUAL(0))
return ZROD(0);
for (i = 0; i < lutSize; i++) {
if (ZACTUAL(i) > z)
break;
}
float invZDist = 1 / (ZACTUAL(i) - ZACTUAL(i-1)); // distance between Z steps
float interpZ = (ZROD(i - 1) * (ZACTUAL(i) - z) + ZROD(i) * (z - ZACTUAL(i - 1))) * invZDist; // linear interpolation between neighboring Z values
return interpZ;
}
//===========================================================================
// Find the Z value to be given to the "rod" in order to obtain the desired Z
float ZWobble::findZRod(float z) {
int nCycle = 0;
float identicalZ = -m_phase / m_puls;
// find the last point in which the two Z are identical: this happens every (2kPI-phase)/w
while (identicalZ <= z) identicalZ = (TWOPI * (++nCycle) - m_phase) / m_puls;
// find Z again using the previous cycle
identicalZ = (TWOPI *(nCycle - 1) - m_phase) / m_puls;
float deltaZa = z - identicalZ;
// find deltaZRod by linear interpolation of the lookup table
float deltaZrod = findInLut(deltaZa);
return identicalZ + deltaZrod;
}
//===========================================================================
// insert a plan_buffer_line if required to handle any hysteresis
void ZWobble::InsertCorrection(const float targetZ) {
if (!m_consistent) return; // don't go through consistency checks all the time; just check one bool
float originZ = (float)position[Z_AXIS] / axis_steps_per_unit[Z_AXIS];
if (originZ < ZWOBBLE_MIN_Z || targetZ < ZWOBBLE_MIN_Z) return;
if (debugLevel & DEBUG_DEBUG) {
ECHO_SMV(DB, "Origin: ", originZ);
ECHO_MV(" Target: ", targetZ);
}
if (EQUAL_WITHIN_TOLERANCE(originZ, targetZ)) return; // if there is no Z move, do nothing
float originZRod;
// there is a high chance that the origin Z is the same as the last target Z: skip one iteration of the algorithm if possible
if (originZ == lastZ)
originZRod = lastZRod;
else
originZRod = findZRod(originZ);
if (debugLevel & DEBUG_DEBUG)
ECHO_MV(" Origin rod: ", originZRod);
float targetZRod = findZRod(targetZ);
if (debugLevel & DEBUG_DEBUG)
ECHO_MV(" Target Rod: ", targetZRod);
// difference in steps between the correct movement (originZRod->targetZRod) and the planned movement
long stepDiff = lround((targetZRod - originZRod) * axis_steps_per_unit[Z_AXIS]) - (lround(targetZ * axis_steps_per_unit[Z_AXIS]) - position[Z_AXIS]);
if (debugLevel & DEBUG_DEBUG)
ECHO_EMV(" stepDiff: ", stepDiff);
lastZ = targetZ;
lastZRod = targetZRod;
// don't adjust if target posizion is less than 0
if (position[Z_AXIS] - stepDiff > 0)
position[Z_AXIS] -= stepDiff;
}
#endif
/**
* cartesian_correction.h
* A class that manages hysteresis by inserting extra plan_buffer_line when necessary
* A class that manages ZWobble
*
* Copyright (c) 2016 MagoKimbra
* Copyright (c) 2013 Francesco Santini
* Copyright (c) 2012 Neil James Martin
*
* 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/>.
*/
#ifndef _CARTESIAN_CORRECTION_H
#define _CARTESIAN_CORRECTION_H
#ifdef HYSTERESIS
//===========================================================================
class Hysteresis {
public:
Hysteresis(float x_mm, float y_mm, float z_mm, float e_mm);
void Set(float x_mm, float y_mm, float z_mm, float e_mm);
void SetAxis(uint8_t axis, float mm);
void ReportToSerial();
void InsertCorrection(const float x, const float y, const float z, const float e);
private:
void calcSteps();
float m_hysteresis_mm[NUM_AXIS];
long m_hysteresis_steps[NUM_AXIS];
uint8_t m_prev_direction_bits;
uint8_t m_hysteresis_bits;
};
//===========================================================================
extern Hysteresis hysteresis;
#endif // HYSTERESIS
#ifdef ZWOBBLE
#define STEPS_IN_ZLUT 50
#define ZWOBBLE_MIN_Z 0.1
//===========================================================================
class ZWobble {
public:
ZWobble(float _amplitude, float _period, float _phase);
void Set(float _amplitude, float _period, float _phase);
void ReportToSerial();
void InsertCorrection(const float targetZ);
void setAmplitude(float _amplitude);
void setPeriod(float _period);
void setPhase(float _phase);
void setSample(float zRod, float zActual);
void setScaledSample(float zRod, float zScaledLength);
void setScalingFactor(float zActualPerScaledLength);
private:
float m_amplitude, m_puls, m_phase;
bool m_consistent;
int lutSize;
float zLut[STEPS_IN_ZLUT][2];
void calculateLut();
void initLinearLut();
void insertInLut(float, float);
float findInLut(float);
float findZRod(float);
bool areParametersConsistent();
float lastZ, lastZRod;
float m_scalingFactor;
bool m_sinusoidal;
};
//===========================================================================
extern ZWobble zwobble;
#endif // ZWOBBLE
#endif // _CARTESIAN_CORRECTION_H
...@@ -487,6 +487,16 @@ float junction_deviation = 0.1; ...@@ -487,6 +487,16 @@ float junction_deviation = 0.1;
void plan_buffer_line(const float& x, const float& y, const float& z, const float& e, float feed_rate, const uint8_t extruder, const uint8_t driver) void plan_buffer_line(const float& x, const float& y, const float& z, const float& e, float feed_rate, const uint8_t extruder, const uint8_t driver)
#endif // AUTO_BED_LEVELING_FEATURE #endif // AUTO_BED_LEVELING_FEATURE
{ {
#if ENABLED(ZWOBBLE)
// Calculate ZWobble
zwobble.InsertCorrection(z);
#endif
#if ENABLED(HYSTERESIS)
// Calculate Hysteresis
hysteresis.InsertCorrection(x, y, z, e);
#endif
// Calculate the buffer head after we push this byte // Calculate the buffer head after we push this byte
int next_buffer_head = next_block_index(block_buffer_head); int next_buffer_head = next_block_index(block_buffer_head);
......
...@@ -140,6 +140,7 @@ extern float max_z_jerk; ...@@ -140,6 +140,7 @@ extern float max_z_jerk;
extern float max_e_jerk[EXTRUDERS]; extern float max_e_jerk[EXTRUDERS];
extern float mintravelfeedrate; extern float mintravelfeedrate;
extern unsigned long axis_steps_per_sqr_second[3 + EXTRUDERS]; extern unsigned long axis_steps_per_sqr_second[3 + EXTRUDERS];
extern long position[NUM_AXIS];
#if ENABLED(AUTOTEMP) #if ENABLED(AUTOTEMP)
extern bool autotemp_enabled; extern bool autotemp_enabled;
......
#include "../../base.h" #include "../../base.h"
#if ENABLED(AUTO_BED_LEVELING_FEATURE) && ENABLED(AUTO_BED_LEVELING_GRID) #if ENABLED(AUTO_BED_LEVELING_FEATURE) && ENABLED(AUTO_BED_LEVELING_GRID)
#include "qr_solve.h"
#include <stdlib.h>
#include <math.h>
//# include "r8lib.h"
#include "qr_solve.h" int i4_min(int i1, int i2)
#include <stdlib.h>
#include <math.h>
//# include "r8lib.h" /******************************************************************************/
/*
int i4_min(int i1, int i2)
/******************************************************************************/
/*
Purpose: Purpose:
I4_MIN returns the smaller of two I4's. I4_MIN returns the smaller of two I4's.
...@@ -33,15 +31,15 @@ int i4_min(int i1, int i2) ...@@ -33,15 +31,15 @@ int i4_min(int i1, int i2)
Input, int I1, I2, two integers to be compared. Input, int I1, I2, two integers to be compared.
Output, int I4_MIN, the smaller of I1 and I2. Output, int I4_MIN, the smaller of I1 and I2.
*/ */
{ {
return (i1 < i2) ? i1 : i2; return (i1 < i2) ? i1 : i2;
} }
double r8_epsilon(void) double r8_epsilon(void)
/******************************************************************************/ /******************************************************************************/
/* /*
Purpose: Purpose:
R8_EPSILON returns the R8 round off unit. R8_EPSILON returns the R8 round off unit.
...@@ -69,16 +67,16 @@ double r8_epsilon(void) ...@@ -69,16 +67,16 @@ double r8_epsilon(void)
Parameters: Parameters:
Output, double R8_EPSILON, the R8 round-off unit. Output, double R8_EPSILON, the R8 round-off unit.
*/ */
{ {
const double value = 2.220446049250313E-016; const double value = 2.220446049250313E-016;
return value; return value;
} }
double r8_max(double x, double y) double r8_max(double x, double y)
/******************************************************************************/ /******************************************************************************/
/* /*
Purpose: Purpose:
R8_MAX returns the maximum of two R8's. R8_MAX returns the maximum of two R8's.
...@@ -100,15 +98,15 @@ double r8_max(double x, double y) ...@@ -100,15 +98,15 @@ double r8_max(double x, double y)
Input, double X, Y, the quantities to compare. Input, double X, Y, the quantities to compare.
Output, double R8_MAX, the maximum of X and Y. Output, double R8_MAX, the maximum of X and Y.
*/ */
{ {
return (y < x) ? x : y; return (y < x) ? x : y;
} }
double r8_abs(double x) double r8_abs(double x)
/******************************************************************************/ /******************************************************************************/
/* /*
Purpose: Purpose:
R8_ABS returns the absolute value of an R8. R8_ABS returns the absolute value of an R8.
...@@ -130,15 +128,15 @@ double r8_abs(double x) ...@@ -130,15 +128,15 @@ double r8_abs(double x)
Input, double X, the quantity whose absolute value is desired. Input, double X, the quantity whose absolute value is desired.
Output, double R8_ABS, the absolute value of X. Output, double R8_ABS, the absolute value of X.
*/ */
{ {
return (x < 0.0) ? -x : x; return (x < 0.0) ? -x : x;
} }
double r8_sign(double x) double r8_sign(double x)
/******************************************************************************/ /******************************************************************************/
/* /*
Purpose: Purpose:
R8_SIGN returns the sign of an R8. R8_SIGN returns the sign of an R8.
...@@ -160,15 +158,15 @@ double r8_sign(double x) ...@@ -160,15 +158,15 @@ double r8_sign(double x)
Input, double X, the number whose sign is desired. Input, double X, the number whose sign is desired.
Output, double R8_SIGN, the sign of X. Output, double R8_SIGN, the sign of X.
*/ */
{ {
return (x < 0.0) ? -1.0 : 1.0; return (x < 0.0) ? -1.0 : 1.0;
} }
double r8mat_amax(int m, int n, double a[]) double r8mat_amax(int m, int n, double a[])
/******************************************************************************/ /******************************************************************************/
/* /*
Purpose: Purpose:
R8MAT_AMAX returns the maximum absolute value entry of an R8MAT. R8MAT_AMAX returns the maximum absolute value entry of an R8MAT.
...@@ -199,8 +197,8 @@ double r8mat_amax(int m, int n, double a[]) ...@@ -199,8 +197,8 @@ double r8mat_amax(int m, int n, double a[])
Input, double A[M*N], the M by N matrix. Input, double A[M*N], the M by N matrix.
Output, double R8MAT_AMAX, the maximum absolute value entry of A. Output, double R8MAT_AMAX, the maximum absolute value entry of A.
*/ */
{ {
double value = r8_abs(a[0 + 0 * m]); double value = r8_abs(a[0 + 0 * m]);
for (int j = 0; j < n; j++) { for (int j = 0; j < n; j++) {
for (int i = 0; i < m; i++) { for (int i = 0; i < m; i++) {
...@@ -208,12 +206,12 @@ double r8mat_amax(int m, int n, double a[]) ...@@ -208,12 +206,12 @@ double r8mat_amax(int m, int n, double a[])
} }
} }
return value; return value;
} }
void r8mat_copy(double a2[], int m, int n, double a1[]) void r8mat_copy(double a2[], int m, int n, double a1[])
/******************************************************************************/ /******************************************************************************/
/* /*
Purpose: Purpose:
R8MAT_COPY_NEW copies one R8MAT to a "new" R8MAT. R8MAT_COPY_NEW copies one R8MAT to a "new" R8MAT.
...@@ -242,20 +240,20 @@ void r8mat_copy(double a2[], int m, int n, double a1[]) ...@@ -242,20 +240,20 @@ void r8mat_copy(double a2[], int m, int n, double a1[])
Input, double A1[M*N], the matrix to be copied. Input, double A1[M*N], the matrix to be copied.
Output, double R8MAT_COPY_NEW[M*N], the copy of A1. Output, double R8MAT_COPY_NEW[M*N], the copy of A1.
*/ */
{ {
for (int j = 0; j < n; j++) { for (int j = 0; j < n; j++) {
for (int i = 0; i < m; i++) for (int i = 0; i < m; i++)
a2[i + j * m] = a1[i + j * m]; a2[i + j * m] = a1[i + j * m];
} }
} }
/******************************************************************************/ /******************************************************************************/
void daxpy(int n, double da, double dx[], int incx, double dy[], int incy) void daxpy(int n, double da, double dx[], int incx, double dy[], int incy)
/******************************************************************************/ /******************************************************************************/
/* /*
Purpose: Purpose:
DAXPY computes constant times a vector plus a vector. DAXPY computes constant times a vector plus a vector.
...@@ -302,8 +300,8 @@ void daxpy(int n, double da, double dx[], int incx, double dy[], int incy) ...@@ -302,8 +300,8 @@ void daxpy(int n, double da, double dx[], int incx, double dy[], int incy)
On output, DY[*] has been replaced by DY[*] + DA * DX[*]. On output, DY[*] has been replaced by DY[*] + DA * DX[*].
Input, int INCY, the increment between successive entries of DY. Input, int INCY, the increment between successive entries of DY.
*/ */
{ {
if (n <= 0 || da == 0.0) return; if (n <= 0 || da == 0.0) return;
int i, ix, iy, m; int i, ix, iy, m;
...@@ -340,13 +338,13 @@ void daxpy(int n, double da, double dx[], int incx, double dy[], int incy) ...@@ -340,13 +338,13 @@ void daxpy(int n, double da, double dx[], int incx, double dy[], int incy)
dy[i + 3] = dy[i + 3] + da * dx[i + 3]; dy[i + 3] = dy[i + 3] + da * dx[i + 3];
} }
} }
} }
/******************************************************************************/ /******************************************************************************/
double ddot(int n, double dx[], int incx, double dy[], int incy) double ddot(int n, double dx[], int incx, double dy[], int incy)
/******************************************************************************/ /******************************************************************************/
/* /*
Purpose: Purpose:
DDOT forms the dot product of two vectors. DDOT forms the dot product of two vectors.
...@@ -393,8 +391,8 @@ double ddot(int n, double dx[], int incx, double dy[], int incy) ...@@ -393,8 +391,8 @@ double ddot(int n, double dx[], int incx, double dy[], int incy)
Output, double DDOT, the sum of the product of the corresponding Output, double DDOT, the sum of the product of the corresponding
entries of DX and DY. entries of DX and DY.
*/ */
{ {
if (n <= 0) return 0.0; if (n <= 0) return 0.0;
...@@ -430,13 +428,13 @@ double ddot(int n, double dx[], int incx, double dy[], int incy) ...@@ -430,13 +428,13 @@ double ddot(int n, double dx[], int incx, double dy[], int incy)
} }
} }
return dtemp; return dtemp;
} }
/******************************************************************************/ /******************************************************************************/
double dnrm2(int n, double x[], int incx) double dnrm2(int n, double x[], int incx)
/******************************************************************************/ /******************************************************************************/
/* /*
Purpose: Purpose:
DNRM2 returns the euclidean norm of a vector. DNRM2 returns the euclidean norm of a vector.
...@@ -478,8 +476,8 @@ double dnrm2(int n, double x[], int incx) ...@@ -478,8 +476,8 @@ double dnrm2(int n, double x[], int incx)
Input, int INCX, the increment between successive entries of X. Input, int INCX, the increment between successive entries of X.
Output, double DNRM2, the Euclidean norm of X. Output, double DNRM2, the Euclidean norm of X.
*/ */
{ {
double norm; double norm;
if (n < 1 || incx < 1) if (n < 1 || incx < 1)
norm = 0.0; norm = 0.0;
...@@ -503,14 +501,14 @@ double dnrm2(int n, double x[], int incx) ...@@ -503,14 +501,14 @@ double dnrm2(int n, double x[], int incx)
norm = scale * sqrt(ssq); norm = scale * sqrt(ssq);
} }
return norm; return norm;
} }
/******************************************************************************/ /******************************************************************************/
void dqrank(double a[], int lda, int m, int n, double tol, int* kr, void dqrank(double a[], int lda, int m, int n, double tol, int* kr,
int jpvt[], double qraux[]) int jpvt[], double qraux[])
/******************************************************************************/ /******************************************************************************/
/* /*
Purpose: Purpose:
DQRANK computes the QR factorization of a rectangular matrix. DQRANK computes the QR factorization of a rectangular matrix.
...@@ -579,8 +577,8 @@ void dqrank(double a[], int lda, int m, int n, double tol, int* kr, ...@@ -579,8 +577,8 @@ void dqrank(double a[], int lda, int m, int n, double tol, int* kr,
Output, double QRAUX[N], will contain extra information defining Output, double QRAUX[N], will contain extra information defining
the QR factorization. the QR factorization.
*/ */
{ {
double work[n]; double work[n];
for (int i = 0; i < n; i++) for (int i = 0; i < n; i++)
...@@ -597,14 +595,14 @@ void dqrank(double a[], int lda, int m, int n, double tol, int* kr, ...@@ -597,14 +595,14 @@ void dqrank(double a[], int lda, int m, int n, double tol, int* kr,
return; return;
*kr = j + 1; *kr = j + 1;
} }
} }
/******************************************************************************/ /******************************************************************************/
void dqrdc(double a[], int lda, int n, int p, double qraux[], int jpvt[], void dqrdc(double a[], int lda, int n, int p, double qraux[], int jpvt[],
double work[], int job) double work[], int job)
/******************************************************************************/ /******************************************************************************/
/* /*
Purpose: Purpose:
DQRDC computes the QR factorization of a real rectangular matrix. DQRDC computes the QR factorization of a real rectangular matrix.
...@@ -679,8 +677,8 @@ void dqrdc(double a[], int lda, int n, int p, double qraux[], int jpvt[], ...@@ -679,8 +677,8 @@ void dqrdc(double a[], int lda, int n, int p, double qraux[], int jpvt[],
Input, int JOB, initiates column pivoting. Input, int JOB, initiates column pivoting.
0, no pivoting is done. 0, no pivoting is done.
nonzero, pivoting is done. nonzero, pivoting is done.
*/ */
{ {
int jp; int jp;
int j; int j;
int lup; int lup;
...@@ -791,14 +789,14 @@ void dqrdc(double a[], int lda, int n, int p, double qraux[], int jpvt[], ...@@ -791,14 +789,14 @@ void dqrdc(double a[], int lda, int n, int p, double qraux[], int jpvt[],
} }
} }
} }
} }
/******************************************************************************/ /******************************************************************************/
int dqrls(double a[], int lda, int m, int n, double tol, int* kr, double b[], int dqrls(double a[], int lda, int m, int n, double tol, int* kr, double b[],
double x[], double rsd[], int jpvt[], double qraux[], int itask) double x[], double rsd[], int jpvt[], double qraux[], int itask)
/******************************************************************************/ /******************************************************************************/
/* /*
Purpose: Purpose:
DQRLS factors and solves a linear system in the least squares sense. DQRLS factors and solves a linear system in the least squares sense.
...@@ -900,8 +898,8 @@ int dqrls(double a[], int lda, int m, int n, double tol, int* kr, double b[], ...@@ -900,8 +898,8 @@ int dqrls(double a[], int lda, int m, int n, double tol, int* kr, double b[],
-1: LDA < M (fatal error) -1: LDA < M (fatal error)
-2: N < 1 (fatal error) -2: N < 1 (fatal error)
-3: ITASK < 1 (fatal error) -3: ITASK < 1 (fatal error)
*/ */
{ {
int ind; int ind;
if (lda < m) { if (lda < m) {
/*fprintf ( stderr, "\n" ); /*fprintf ( stderr, "\n" );
...@@ -938,14 +936,14 @@ int dqrls(double a[], int lda, int m, int n, double tol, int* kr, double b[], ...@@ -938,14 +936,14 @@ int dqrls(double a[], int lda, int m, int n, double tol, int* kr, double b[],
*/ */
dqrlss(a, lda, m, n, *kr, b, x, rsd, jpvt, qraux); dqrlss(a, lda, m, n, *kr, b, x, rsd, jpvt, qraux);
return ind; return ind;
} }
/******************************************************************************/ /******************************************************************************/
void dqrlss(double a[], int lda, int m, int n, int kr, double b[], double x[], void dqrlss(double a[], int lda, int m, int n, int kr, double b[], double x[],
double rsd[], int jpvt[], double qraux[]) double rsd[], int jpvt[], double qraux[])
/******************************************************************************/ /******************************************************************************/
/* /*
Purpose: Purpose:
DQRLSS solves a linear system in a least squares sense. DQRLSS solves a linear system in a least squares sense.
...@@ -1013,8 +1011,8 @@ void dqrlss(double a[], int lda, int m, int n, int kr, double b[], double x[], ...@@ -1013,8 +1011,8 @@ void dqrlss(double a[], int lda, int m, int n, int kr, double b[], double x[],
Input, double QRAUX[N], auxiliary information from DQRANK Input, double QRAUX[N], auxiliary information from DQRANK
defining the QR factorization. defining the QR factorization.
*/ */
{ {
int i; int i;
int info; int info;
int j; int j;
...@@ -1048,14 +1046,14 @@ void dqrlss(double a[], int lda, int m, int n, int kr, double b[], double x[], ...@@ -1048,14 +1046,14 @@ void dqrlss(double a[], int lda, int m, int n, int kr, double b[], double x[],
} }
} }
} }
} }
/******************************************************************************/ /******************************************************************************/
int dqrsl(double a[], int lda, int n, int k, double qraux[], double y[], int dqrsl(double a[], int lda, int n, int k, double qraux[], double y[],
double qy[], double qty[], double b[], double rsd[], double ab[], int job) double qy[], double qty[], double b[], double rsd[], double ab[], int job)
/******************************************************************************/ /******************************************************************************/
/* /*
Purpose: Purpose:
DQRSL computes transformations, projections, and least squares solutions. DQRSL computes transformations, projections, and least squares solutions.
...@@ -1188,8 +1186,8 @@ int dqrsl(double a[], int lda, int n, int k, double qraux[], double y[], ...@@ -1188,8 +1186,8 @@ int dqrsl(double a[], int lda, int n, int k, double qraux[], double y[],
Output, int DQRSL, is zero unless the computation of B has Output, int DQRSL, is zero unless the computation of B has
been requested and R is exactly singular. In this case, INFO is the been requested and R is exactly singular. In this case, INFO is the
index of the first zero diagonal element of R, and B is left unaltered. index of the first zero diagonal element of R, and B is left unaltered.
*/ */
{ {
int cab; int cab;
int cb; int cb;
int cqty; int cqty;
...@@ -1341,15 +1339,15 @@ int dqrsl(double a[], int lda, int n, int k, double qraux[], double y[], ...@@ -1341,15 +1339,15 @@ int dqrsl(double a[], int lda, int n, int k, double qraux[], double y[],
} }
} }
return info; return info;
} }
/******************************************************************************/ /******************************************************************************/
/******************************************************************************/ /******************************************************************************/
void dscal(int n, double sa, double x[], int incx) void dscal(int n, double sa, double x[], int incx)
/******************************************************************************/ /******************************************************************************/
/* /*
Purpose: Purpose:
DSCAL scales a vector by a constant. DSCAL scales a vector by a constant.
...@@ -1387,8 +1385,8 @@ void dscal(int n, double sa, double x[], int incx) ...@@ -1387,8 +1385,8 @@ void dscal(int n, double sa, double x[], int incx)
Input/output, double X[*], the vector to be scaled. Input/output, double X[*], the vector to be scaled.
Input, int INCX, the increment between successive entries of X. Input, int INCX, the increment between successive entries of X.
*/ */
{ {
int i; int i;
int ix; int ix;
int m; int m;
...@@ -1417,14 +1415,14 @@ void dscal(int n, double sa, double x[], int incx) ...@@ -1417,14 +1415,14 @@ void dscal(int n, double sa, double x[], int incx)
ix = ix + incx; ix = ix + incx;
} }
} }
} }
/******************************************************************************/ /******************************************************************************/
void dswap(int n, double x[], int incx, double y[], int incy) void dswap(int n, double x[], int incx, double y[], int incy)
/******************************************************************************/ /******************************************************************************/
/* /*
Purpose: Purpose:
DSWAP interchanges two vectors. DSWAP interchanges two vectors.
...@@ -1464,8 +1462,8 @@ void dswap(int n, double x[], int incx, double y[], int incy) ...@@ -1464,8 +1462,8 @@ void dswap(int n, double x[], int incx, double y[], int incy)
Input/output, double Y[*], one of the vectors to swap. Input/output, double Y[*], one of the vectors to swap.
Input, int INCY, the increment between successive elements of Y. Input, int INCY, the increment between successive elements of Y.
*/ */
{ {
if (n <= 0) return; if (n <= 0) return;
int i, ix, iy, m; int i, ix, iy, m;
...@@ -1501,15 +1499,15 @@ void dswap(int n, double x[], int incx, double y[], int incy) ...@@ -1501,15 +1499,15 @@ void dswap(int n, double x[], int incx, double y[], int incy)
iy = iy + incy; iy = iy + incy;
} }
} }
} }
/******************************************************************************/ /******************************************************************************/
/******************************************************************************/ /******************************************************************************/
void qr_solve(double x[], int m, int n, double a[], double b[]) void qr_solve(double x[], int m, int n, double a[], double b[])
/******************************************************************************/ /******************************************************************************/
/* /*
Purpose: Purpose:
QR_SOLVE solves a linear system in the least squares sense. QR_SOLVE solves a linear system in the least squares sense.
...@@ -1554,8 +1552,8 @@ void qr_solve(double x[], int m, int n, double a[], double b[]) ...@@ -1554,8 +1552,8 @@ void qr_solve(double x[], int m, int n, double a[], double b[])
Input, double B[M], the right hand side. Input, double B[M], the right hand side.
Output, double QR_SOLVE[N], the least squares solution. Output, double QR_SOLVE[N], the least squares solution.
*/ */
{ {
double a_qr[n * m], qraux[n], r[m], tol; double a_qr[n * m], qraux[n], r[m], tol;
int ind, itask, jpvt[n], kr, lda; int ind, itask, jpvt[n], kr, lda;
...@@ -1565,7 +1563,7 @@ void qr_solve(double x[], int m, int n, double a[], double b[]) ...@@ -1565,7 +1563,7 @@ void qr_solve(double x[], int m, int n, double a[], double b[])
itask = 1; itask = 1;
ind = dqrls ( a_qr, lda, m, n, tol, &kr, b, x, r, jpvt, qraux, itask ); UNUSED(ind); ind = dqrls ( a_qr, lda, m, n, tol, &kr, b, x, r, jpvt, qraux, itask ); UNUSED(ind);
} }
/******************************************************************************/ /******************************************************************************/
#endif #endif
#if ENABLED(AUTO_BED_LEVELING_GRID) #if ENABLED(AUTO_BED_LEVELING_GRID)
void daxpy(int n, double da, double dx[], int incx, double dy[], int incy); void daxpy(int n, double da, double dx[], int incx, double dy[], int incy);
double ddot(int n, double dx[], int incx, double dy[], int incy); double ddot(int n, double dx[], int incx, double dy[], int incy);
double dnrm2(int n, double x[], int incx); double dnrm2(int n, double x[], int incx);
void dqrank(double a[], int lda, int m, int n, double tol, int* kr, void dqrank(double a[], int lda, int m, int n, double tol, int* kr,
int jpvt[], double qraux[]); int jpvt[], double qraux[]);
void dqrdc(double a[], int lda, int n, int p, double qraux[], int jpvt[], void dqrdc(double a[], int lda, int n, int p, double qraux[], int jpvt[],
double work[], int job); double work[], int job);
int dqrls(double a[], int lda, int m, int n, double tol, int* kr, double b[], int dqrls(double a[], int lda, int m, int n, double tol, int* kr, double b[],
double x[], double rsd[], int jpvt[], double qraux[], int itask); double x[], double rsd[], int jpvt[], double qraux[], int itask);
void dqrlss(double a[], int lda, int m, int n, int kr, double b[], double x[], void dqrlss(double a[], int lda, int m, int n, int kr, double b[], double x[],
double rsd[], int jpvt[], double qraux[]); double rsd[], int jpvt[], double qraux[]);
int dqrsl(double a[], int lda, int n, int k, double qraux[], double y[], int dqrsl(double a[], int lda, int n, int k, double qraux[], double y[],
double qy[], double qty[], double b[], double rsd[], double ab[], int job); double qy[], double qty[], double b[], double rsd[], double ab[], int job);
void dscal(int n, double sa, double x[], int incx); void dscal(int n, double sa, double x[], int incx);
void dswap(int n, double x[], int incx, double y[], int incy); void dswap(int n, double x[], int incx, double y[], int incy);
void qr_solve(double x[], int m, int n, double a[], double b[]); void qr_solve(double x[], int m, int n, double a[], double b[]);
#endif #endif
/* /**
vector_3.cpp - Vector library for bed leveling * vector_3.cpp - Vector library for bed leveling
Copyright (c) 2012 Lars Brubaker. All right reserved. * Copyright (c) 2012 Lars Brubaker. All right reserved.
*
This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either * License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. * version 2.1 of the License, or (at your option) any later version.
*
This library is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details. * Lesser General Public License for more details.
*
You should have received a copy of the GNU Lesser General Public * You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software * License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#include "../../base.h" #include "../../base.h"
#include <math.h>
#if ENABLED(AUTO_BED_LEVELING_FEATURE) #if ENABLED(AUTO_BED_LEVELING_FEATURE)
#include "vector_3.h" #include <math.h>
#include "vector_3.h"
vector_3::vector_3() : x(0), y(0), z(0) { } vector_3::vector_3() : x(0), y(0), z(0) { }
vector_3::vector_3(float x_, float y_, float z_) : x(x_), y(y_), z(z_) { } vector_3::vector_3(float x_, float y_, float z_) : x(x_), y(y_), z(z_) { }
vector_3 vector_3::cross(vector_3 left, vector_3 right) { vector_3 vector_3::cross(vector_3 left, vector_3 right) {
return vector_3(left.y * right.z - left.z * right.y, return vector_3(left.y * right.z - left.z * right.y,
left.z * right.x - left.x * right.z, left.z * right.x - left.x * right.z,
left.x * right.y - left.y * right.x); left.x * right.y - left.y * right.x);
} }
vector_3 vector_3::operator+(vector_3 v) { return vector_3((x + v.x), (y + v.y), (z + v.z)); } vector_3 vector_3::operator+(vector_3 v) { return vector_3((x + v.x), (y + v.y), (z + v.z)); }
vector_3 vector_3::operator-(vector_3 v) { return vector_3((x - v.x), (y - v.y), (z - v.z)); } vector_3 vector_3::operator-(vector_3 v) { return vector_3((x - v.x), (y - v.y), (z - v.z)); }
vector_3 vector_3::get_normal() { vector_3 vector_3::get_normal() {
vector_3 normalized = vector_3(x, y, z); vector_3 normalized = vector_3(x, y, z);
normalized.normalize(); normalized.normalize();
return normalized; return normalized;
} }
float vector_3::get_length() { return sqrt((x * x) + (y * y) + (z * z)); } float vector_3::get_length() { return sqrt((x * x) + (y * y) + (z * z)); }
void vector_3::normalize() { void vector_3::normalize() {
float length = get_length(); float length = get_length();
x /= length; x /= length;
y /= length; y /= length;
z /= length; z /= length;
} }
void vector_3::apply_rotation(matrix_3x3 matrix) { void vector_3::apply_rotation(matrix_3x3 matrix) {
float resultX = x * matrix.matrix[0][0] + y * matrix.matrix[1][0] + z * matrix.matrix[2][0]; float resultX = x * matrix.matrix[0][0] + y * matrix.matrix[1][0] + z * matrix.matrix[2][0];
float resultY = x * matrix.matrix[0][1] + y * matrix.matrix[1][1] + z * matrix.matrix[2][1]; float resultY = x * matrix.matrix[0][1] + y * matrix.matrix[1][1] + z * matrix.matrix[2][1];
float resultZ = x * matrix.matrix[0][2] + y * matrix.matrix[1][2] + z * matrix.matrix[2][2]; float resultZ = x * matrix.matrix[0][2] + y * matrix.matrix[1][2] + z * matrix.matrix[2][2];
x = resultX; x = resultX;
y = resultY; y = resultY;
z = resultZ; z = resultZ;
} }
void vector_3::debug(const char title[]) { void vector_3::debug(const char title[]) {
ECHO_ST(DB, title); ECHO_ST(DB, title);
ECHO_MV(" x: ", x, 6); ECHO_MV(" x: ", x, 6);
ECHO_MV(" y: ", y, 6); ECHO_MV(" y: ", y, 6);
ECHO_EMV(" z: ", z, 6); ECHO_EMV(" z: ", z, 6);
} }
void apply_rotation_xyz(matrix_3x3 matrix, float& x, float& y, float& z) { void apply_rotation_xyz(matrix_3x3 matrix, float& x, float& y, float& z) {
vector_3 vector = vector_3(x, y, z); vector_3 vector = vector_3(x, y, z);
vector.apply_rotation(matrix); vector.apply_rotation(matrix);
x = vector.x; x = vector.x;
y = vector.y; y = vector.y;
z = vector.z; z = vector.z;
} }
matrix_3x3 matrix_3x3::create_from_rows(vector_3 row_0, vector_3 row_1, vector_3 row_2) { matrix_3x3 matrix_3x3::create_from_rows(vector_3 row_0, vector_3 row_1, vector_3 row_2) {
//row_0.debug("row_0"); //row_0.debug("row_0");
//row_1.debug("row_1"); //row_1.debug("row_1");
//row_2.debug("row_2"); //row_2.debug("row_2");
...@@ -85,15 +85,15 @@ matrix_3x3 matrix_3x3::create_from_rows(vector_3 row_0, vector_3 row_1, vector_3 ...@@ -85,15 +85,15 @@ matrix_3x3 matrix_3x3::create_from_rows(vector_3 row_0, vector_3 row_1, vector_3
new_matrix.matrix[2][0] = row_2.x; new_matrix.matrix[2][1] = row_2.y; new_matrix.matrix[2][2] = row_2.z; new_matrix.matrix[2][0] = row_2.x; new_matrix.matrix[2][1] = row_2.y; new_matrix.matrix[2][2] = row_2.z;
//new_matrix.debug("new_matrix"); //new_matrix.debug("new_matrix");
return new_matrix; return new_matrix;
} }
void matrix_3x3::set_to_identity() { void matrix_3x3::set_to_identity() {
matrix[0][0] = 1; matrix[0][1] = 0; matrix[0][2] = 0; matrix[0][0] = 1; matrix[0][1] = 0; matrix[0][2] = 0;
matrix[1][0] = 0; matrix[1][1] = 1; matrix[1][2] = 0; matrix[1][0] = 0; matrix[1][1] = 1; matrix[1][2] = 0;
matrix[2][0] = 0; matrix[2][1] = 0; matrix[2][2] = 1; matrix[2][0] = 0; matrix[2][1] = 0; matrix[2][2] = 1;
} }
matrix_3x3 matrix_3x3::create_look_at(vector_3 target) { matrix_3x3 matrix_3x3::create_look_at(vector_3 target) {
vector_3 z_row = target.get_normal(); vector_3 z_row = target.get_normal();
vector_3 x_row = vector_3(1, 0, -target.x / target.z).get_normal(); vector_3 x_row = vector_3(1, 0, -target.x / target.z).get_normal();
vector_3 y_row = vector_3::cross(z_row, x_row).get_normal(); vector_3 y_row = vector_3::cross(z_row, x_row).get_normal();
...@@ -107,17 +107,17 @@ matrix_3x3 matrix_3x3::create_look_at(vector_3 target) { ...@@ -107,17 +107,17 @@ matrix_3x3 matrix_3x3::create_look_at(vector_3 target) {
// rot.debug("rot"); // rot.debug("rot");
return rot; return rot;
} }
matrix_3x3 matrix_3x3::transpose(matrix_3x3 original) { matrix_3x3 matrix_3x3::transpose(matrix_3x3 original) {
matrix_3x3 new_matrix; matrix_3x3 new_matrix;
new_matrix.matrix[0][0] = original.matrix[0][0]; new_matrix.matrix[0][1] = original.matrix[1][0]; new_matrix.matrix[0][2] = original.matrix[2][0]; new_matrix.matrix[0][0] = original.matrix[0][0]; new_matrix.matrix[0][1] = original.matrix[1][0]; new_matrix.matrix[0][2] = original.matrix[2][0];
new_matrix.matrix[1][0] = original.matrix[0][1]; new_matrix.matrix[1][1] = original.matrix[1][1]; new_matrix.matrix[1][2] = original.matrix[2][1]; new_matrix.matrix[1][0] = original.matrix[0][1]; new_matrix.matrix[1][1] = original.matrix[1][1]; new_matrix.matrix[1][2] = original.matrix[2][1];
new_matrix.matrix[2][0] = original.matrix[0][2]; new_matrix.matrix[2][1] = original.matrix[1][2]; new_matrix.matrix[2][2] = original.matrix[2][2]; new_matrix.matrix[2][0] = original.matrix[0][2]; new_matrix.matrix[2][1] = original.matrix[1][2]; new_matrix.matrix[2][2] = original.matrix[2][2];
return new_matrix; return new_matrix;
} }
void matrix_3x3::debug(const char title[]) { void matrix_3x3::debug(const char title[]) {
ECHO_LT(DB, title); ECHO_LT(DB, title);
for (uint8_t i = 0; i < 3; i++) { for (uint8_t i = 0; i < 3; i++) {
ECHO_S(DB); ECHO_S(DB);
...@@ -128,6 +128,6 @@ void matrix_3x3::debug(const char title[]) { ...@@ -128,6 +128,6 @@ void matrix_3x3::debug(const char title[]) {
} }
ECHO_E; ECHO_E;
} }
} }
#endif // AUTO_BED_LEVELING_FEATURE #endif // AUTO_BED_LEVELING_FEATURE
/* /**
vector_3.cpp - Vector library for bed leveling * vector_3.cpp - Vector library for bed leveling
Copyright (c) 2012 Lars Brubaker. All right reserved. * Copyright (c) 2012 Lars Brubaker. All right reserved.
*
This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either * License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. * version 2.1 of the License, or (at your option) any later version.
*
This library is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details. * Lesser General Public License for more details.
*
You should have received a copy of the GNU Lesser General Public * You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software * License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef VECTOR_3_H #ifndef VECTOR_3_H
#define VECTOR_3_H #define VECTOR_3_H
#if ENABLED(AUTO_BED_LEVELING_FEATURE) #if ENABLED(AUTO_BED_LEVELING_FEATURE)
class matrix_3x3; class matrix_3x3;
struct vector_3 { struct vector_3 {
float x, y, z; float x, y, z;
vector_3(); vector_3();
...@@ -40,9 +40,9 @@ struct vector_3 { ...@@ -40,9 +40,9 @@ struct vector_3 {
void debug(const char title[]); void debug(const char title[]);
void apply_rotation(matrix_3x3 matrix); void apply_rotation(matrix_3x3 matrix);
}; };
struct matrix_3x3 { struct matrix_3x3 {
float matrix[3][3]; float matrix[3][3];
static matrix_3x3 create_from_rows(vector_3 row_0, vector_3 row_1, vector_3 row_2); static matrix_3x3 create_from_rows(vector_3 row_0, vector_3 row_1, vector_3 row_2);
...@@ -50,12 +50,10 @@ struct matrix_3x3 { ...@@ -50,12 +50,10 @@ struct matrix_3x3 {
static matrix_3x3 transpose(matrix_3x3 original); static matrix_3x3 transpose(matrix_3x3 original);
void set_to_identity(); void set_to_identity();
void debug(const char title[]); void debug(const char title[]);
}; };
void apply_rotation_xyz(matrix_3x3 rotationMatrix, float& x, float& y, float& z); void apply_rotation_xyz(matrix_3x3 rotationMatrix, float& x, float& y, float& z);
#endif // AUTO_BED_LEVELING_FEATURE
#endif // AUTO_BED_LEVELING_FEATURE
#endif // VECTOR_3_H #endif // VECTOR_3_H
/* /**
Copyright (c) 2013 Arduino LLC. All right reserved. * Copyright (c) 2013 Arduino LLC. All right reserved.
*
This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either * License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. * version 2.1 of the License, or (at your option) any later version.
*
This library is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details. * Lesser General Public License for more details.
*
You should have received a copy of the GNU Lesser General Public * You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software * License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
/* /**
* Defines for 16 bit timers used with Servo library * Defines for 16 bit timers used with Servo library
* *
* If _useTimerX is defined then TimerX is a 16 bit timer on the current board * If _useTimerX is defined then TimerX is a 16 bit timer on the current board
...@@ -51,4 +51,3 @@ typedef enum { _timer3, _Nbr_16timers } timer16_Sequence_t; ...@@ -51,4 +51,3 @@ typedef enum { _timer3, _Nbr_16timers } timer16_Sequence_t;
#else // everything else #else // everything else
typedef enum { _Nbr_16timers } timer16_Sequence_t; typedef enum { _Nbr_16timers } timer16_Sequence_t;
#endif #endif
/* /**
Copyright (c) 2013 Arduino LLC. All right reserved. * Copyright (c) 2013 Arduino LLC. All right reserved.
*
This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either * License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. * version 2.1 of the License, or (at your option) any later version.
*
This library is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details. * Lesser General Public License for more details.
*
You should have received a copy of the GNU Lesser General Public * You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software * License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#include "../../base.h" #include "../../base.h"
#if HAS(SERVOS) #if HAS(SERVOS)
#include "servo.h" #include "servo.h"
#define usToTicks(_us) (( clockCyclesPerMicrosecond()* _us) / 8) // converts microseconds to tick (assumes prescale of 8) // 12 Aug 2009 #define usToTicks(_us) (( clockCyclesPerMicrosecond()* _us) / 8) // converts microseconds to tick (assumes prescale of 8) // 12 Aug 2009
#define ticksToUs(_ticks) (( (unsigned)_ticks * 8)/ clockCyclesPerMicrosecond() ) // converts from ticks back to microseconds #define ticksToUs(_ticks) (( (unsigned)_ticks * 8)/ clockCyclesPerMicrosecond() ) // converts from ticks back to microseconds
#define TRIM_DURATION 2 // compensation ticks to trim adjust for digitalWrite delays #define TRIM_DURATION 2 // compensation ticks to trim adjust for digitalWrite delays
static servo_t servos[MAX_SERVOS]; // static array of servo structures static servo_t servos[MAX_SERVOS]; // static array of servo structures
static volatile int8_t Channel[_Nbr_16timers ]; // counter for the servo being pulsed for each timer (or -1 if refresh interval) static volatile int8_t Channel[_Nbr_16timers ]; // counter for the servo being pulsed for each timer (or -1 if refresh interval)
uint8_t ServoCount = 0; // the total number of attached servos uint8_t ServoCount = 0; // the total number of attached servos
// convenience macros // convenience macros
#define SERVO_INDEX_TO_TIMER(_servo_nbr) ((timer16_Sequence_t)(_servo_nbr / SERVOS_PER_TIMER)) // returns the timer controlling this servo #define SERVO_INDEX_TO_TIMER(_servo_nbr) ((timer16_Sequence_t)(_servo_nbr / SERVOS_PER_TIMER)) // returns the timer controlling this servo
#define SERVO_INDEX_TO_CHANNEL(_servo_nbr) (_servo_nbr % SERVOS_PER_TIMER) // returns the index of the servo on this timer #define SERVO_INDEX_TO_CHANNEL(_servo_nbr) (_servo_nbr % SERVOS_PER_TIMER) // returns the index of the servo on this timer
#define SERVO_INDEX(_timer,_channel) ((_timer*SERVOS_PER_TIMER) + _channel) // macro to access servo index by timer and channel #define SERVO_INDEX(_timer,_channel) ((_timer*SERVOS_PER_TIMER) + _channel) // macro to access servo index by timer and channel
#define SERVO(_timer,_channel) (servos[SERVO_INDEX(_timer,_channel)]) // macro to access servo class by timer and channel #define SERVO(_timer,_channel) (servos[SERVO_INDEX(_timer,_channel)]) // macro to access servo class by timer and channel
#define SERVO_MIN() (MIN_PULSE_WIDTH - this->min * 4) // minimum value in uS for this servo #define SERVO_MIN() (MIN_PULSE_WIDTH - this->min * 4) // minimum value in uS for this servo
#define SERVO_MAX() (MAX_PULSE_WIDTH - this->max * 4) // maximum value in uS for this servo #define SERVO_MAX() (MAX_PULSE_WIDTH - this->max * 4) // maximum value in uS for this servo
/************ static functions common to all instances ***********************/ /************ static functions common to all instances ***********************/
static inline void handle_interrupts(timer16_Sequence_t timer, volatile uint16_t *TCNTn, volatile uint16_t* OCRnA) static inline void handle_interrupts(timer16_Sequence_t timer, volatile uint16_t *TCNTn, volatile uint16_t* OCRnA)
{ {
if( Channel[timer] < 0 ) if( Channel[timer] < 0 )
*TCNTn = 0; // channel set to -1 indicated that refresh interval completed so reset the timer *TCNTn = 0; // channel set to -1 indicated that refresh interval completed so reset the timer
else{ else{
...@@ -65,95 +65,95 @@ static inline void handle_interrupts(timer16_Sequence_t timer, volatile uint16_t ...@@ -65,95 +65,95 @@ static inline void handle_interrupts(timer16_Sequence_t timer, volatile uint16_t
*OCRnA = *TCNTn + 4; // at least REFRESH_INTERVAL has elapsed *OCRnA = *TCNTn + 4; // at least REFRESH_INTERVAL has elapsed
Channel[timer] = -1; // this will get incremented at the end of the refresh period to start again at the first channel Channel[timer] = -1; // this will get incremented at the end of the refresh period to start again at the first channel
} }
} }
#ifndef WIRING // Wiring pre-defines signal handlers so don't define any if compiling for the Wiring platform #ifndef WIRING // Wiring pre-defines signal handlers so don't define any if compiling for the Wiring platform
// Interrupt handlers for Arduino // Interrupt handlers for Arduino
#if defined(_useTimer1) #if defined(_useTimer1)
SIGNAL (TIMER1_COMPA_vect) SIGNAL (TIMER1_COMPA_vect)
{ {
handle_interrupts(_timer1, &TCNT1, &OCR1A); handle_interrupts(_timer1, &TCNT1, &OCR1A);
} }
#endif #endif
#if defined(_useTimer3) #if defined(_useTimer3)
SIGNAL (TIMER3_COMPA_vect) SIGNAL (TIMER3_COMPA_vect)
{ {
handle_interrupts(_timer3, &TCNT3, &OCR3A); handle_interrupts(_timer3, &TCNT3, &OCR3A);
} }
#endif #endif
#if defined(_useTimer4) #if defined(_useTimer4)
SIGNAL (TIMER4_COMPA_vect) SIGNAL (TIMER4_COMPA_vect)
{ {
handle_interrupts(_timer4, &TCNT4, &OCR4A); handle_interrupts(_timer4, &TCNT4, &OCR4A);
} }
#endif #endif
#if defined(_useTimer5) #if defined(_useTimer5)
SIGNAL (TIMER5_COMPA_vect) SIGNAL (TIMER5_COMPA_vect)
{ {
handle_interrupts(_timer5, &TCNT5, &OCR5A); handle_interrupts(_timer5, &TCNT5, &OCR5A);
} }
#endif #endif
#elif defined WIRING #elif defined WIRING
// Interrupt handlers for Wiring // Interrupt handlers for Wiring
#if defined(_useTimer1) #if defined(_useTimer1)
void Timer1Service() void Timer1Service()
{ {
handle_interrupts(_timer1, &TCNT1, &OCR1A); handle_interrupts(_timer1, &TCNT1, &OCR1A);
} }
#endif #endif
#if defined(_useTimer3) #if defined(_useTimer3)
void Timer3Service() void Timer3Service()
{ {
handle_interrupts(_timer3, &TCNT3, &OCR3A); handle_interrupts(_timer3, &TCNT3, &OCR3A);
} }
#endif #endif
#endif #endif
static void initISR(timer16_Sequence_t timer) static void initISR(timer16_Sequence_t timer)
{ {
#if defined (_useTimer1) #if defined (_useTimer1)
if(timer == _timer1) { if(timer == _timer1) {
TCCR1A = 0; // normal counting mode TCCR1A = 0; // normal counting mode
TCCR1B = _BV(CS11); // set prescaler of 8 TCCR1B = _BV(CS11); // set prescaler of 8
TCNT1 = 0; // clear the timer count TCNT1 = 0; // clear the timer count
#if defined(__AVR_ATmega8__)|| defined(__AVR_ATmega128__) #if defined(__AVR_ATmega8__)|| defined(__AVR_ATmega128__)
TIFR |= _BV(OCF1A); // clear any pending interrupts; TIFR |= _BV(OCF1A); // clear any pending interrupts;
TIMSK |= _BV(OCIE1A) ; // enable the output compare interrupt TIMSK |= _BV(OCIE1A) ; // enable the output compare interrupt
#else #else
// here if not ATmega8 or ATmega128 // here if not ATmega8 or ATmega128
TIFR1 |= _BV(OCF1A); // clear any pending interrupts; TIFR1 |= _BV(OCF1A); // clear any pending interrupts;
TIMSK1 |= _BV(OCIE1A) ; // enable the output compare interrupt TIMSK1 |= _BV(OCIE1A) ; // enable the output compare interrupt
#endif #endif
#if defined(WIRING) #if defined(WIRING)
timerAttach(TIMER1OUTCOMPAREA_INT, Timer1Service); timerAttach(TIMER1OUTCOMPAREA_INT, Timer1Service);
#endif #endif
} }
#endif #endif
#if defined (_useTimer3) #if defined (_useTimer3)
if(timer == _timer3) { if(timer == _timer3) {
TCCR3A = 0; // normal counting mode TCCR3A = 0; // normal counting mode
TCCR3B = _BV(CS31); // set prescaler of 8 TCCR3B = _BV(CS31); // set prescaler of 8
TCNT3 = 0; // clear the timer count TCNT3 = 0; // clear the timer count
#if defined(__AVR_ATmega128__) #if defined(__AVR_ATmega128__)
TIFR |= _BV(OCF3A); // clear any pending interrupts; TIFR |= _BV(OCF3A); // clear any pending interrupts;
ETIMSK |= _BV(OCIE3A); // enable the output compare interrupt ETIMSK |= _BV(OCIE3A); // enable the output compare interrupt
#else #else
TIFR3 = _BV(OCF3A); // clear any pending interrupts; TIFR3 = _BV(OCF3A); // clear any pending interrupts;
TIMSK3 = _BV(OCIE3A) ; // enable the output compare interrupt TIMSK3 = _BV(OCIE3A) ; // enable the output compare interrupt
#endif #endif
#if defined(WIRING) #if defined(WIRING)
timerAttach(TIMER3OUTCOMPAREA_INT, Timer3Service); // for Wiring platform only timerAttach(TIMER3OUTCOMPAREA_INT, Timer3Service); // for Wiring platform only
#endif #endif
} }
#endif #endif
#if defined (_useTimer4) #if defined (_useTimer4)
if(timer == _timer4) { if(timer == _timer4) {
TCCR4A = 0; // normal counting mode TCCR4A = 0; // normal counting mode
TCCR4B = _BV(CS41); // set prescaler of 8 TCCR4B = _BV(CS41); // set prescaler of 8
...@@ -161,9 +161,9 @@ static void initISR(timer16_Sequence_t timer) ...@@ -161,9 +161,9 @@ static void initISR(timer16_Sequence_t timer)
TIFR4 = _BV(OCF4A); // clear any pending interrupts; TIFR4 = _BV(OCF4A); // clear any pending interrupts;
TIMSK4 = _BV(OCIE4A) ; // enable the output compare interrupt TIMSK4 = _BV(OCIE4A) ; // enable the output compare interrupt
} }
#endif #endif
#if defined (_useTimer5) #if defined (_useTimer5)
if(timer == _timer5) { if(timer == _timer5) {
TCCR5A = 0; // normal counting mode TCCR5A = 0; // normal counting mode
TCCR5B = _BV(CS51); // set prescaler of 8 TCCR5B = _BV(CS51); // set prescaler of 8
...@@ -171,13 +171,13 @@ static void initISR(timer16_Sequence_t timer) ...@@ -171,13 +171,13 @@ static void initISR(timer16_Sequence_t timer)
TIFR5 = _BV(OCF5A); // clear any pending interrupts; TIFR5 = _BV(OCF5A); // clear any pending interrupts;
TIMSK5 = _BV(OCIE5A) ; // enable the output compare interrupt TIMSK5 = _BV(OCIE5A) ; // enable the output compare interrupt
} }
#endif #endif
} }
static void finISR(timer16_Sequence_t timer) static void finISR(timer16_Sequence_t timer)
{ {
//disable use of the given timer //disable use of the given timer
#if defined WIRING // Wiring #if defined WIRING // Wiring
if(timer == _timer1) { if(timer == _timer1) {
#if defined(__AVR_ATmega1281__)||defined(__AVR_ATmega2561__) #if defined(__AVR_ATmega1281__)||defined(__AVR_ATmega2561__)
TIMSK1 &= ~_BV(OCIE1A) ; // disable timer 1 output compare interrupt TIMSK1 &= ~_BV(OCIE1A) ; // disable timer 1 output compare interrupt
...@@ -194,25 +194,25 @@ static void finISR(timer16_Sequence_t timer) ...@@ -194,25 +194,25 @@ static void finISR(timer16_Sequence_t timer)
#endif #endif
timerDetach(TIMER3OUTCOMPAREA_INT); timerDetach(TIMER3OUTCOMPAREA_INT);
} }
#else #else
//For arduino - in future: call here to a currently undefined function to reset the timer //For arduino - in future: call here to a currently undefined function to reset the timer
#endif #endif
} }
static boolean isTimerActive(timer16_Sequence_t timer) static boolean isTimerActive(timer16_Sequence_t timer)
{ {
// returns true if any servo is active on this timer // returns true if any servo is active on this timer
for(uint8_t channel=0; channel < SERVOS_PER_TIMER; channel++) { for(uint8_t channel=0; channel < SERVOS_PER_TIMER; channel++) {
if(SERVO(timer,channel).Pin.isActive == true) if(SERVO(timer,channel).Pin.isActive == true)
return true; return true;
} }
return false; return false;
} }
/****************** end of static functions ******************************/ /****************** end of static functions ******************************/
Servo::Servo() { Servo::Servo() {
if (ServoCount < MAX_SERVOS) { if (ServoCount < MAX_SERVOS) {
this->servoIndex = ServoCount++; // assign a servo index to this instance this->servoIndex = ServoCount++; // assign a servo index to this instance
servos[this->servoIndex].ticks = usToTicks(DEFAULT_PULSE_WIDTH); // store default values servos[this->servoIndex].ticks = usToTicks(DEFAULT_PULSE_WIDTH); // store default values
...@@ -220,13 +220,13 @@ Servo::Servo() { ...@@ -220,13 +220,13 @@ Servo::Servo() {
else { else {
this->servoIndex = INVALID_SERVO; // too many servos this->servoIndex = INVALID_SERVO; // too many servos
} }
} }
uint8_t Servo::attach(int pin) { uint8_t Servo::attach(int pin) {
return this->attach(pin, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH); return this->attach(pin, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH);
} }
uint8_t Servo::attach(int pin, int min, int max) { uint8_t Servo::attach(int pin, int min, int max) {
if (this->servoIndex >= MAX_SERVOS) return -1; if (this->servoIndex >= MAX_SERVOS) return -1;
...@@ -243,22 +243,22 @@ uint8_t Servo::attach(int pin, int min, int max) { ...@@ -243,22 +243,22 @@ uint8_t Servo::attach(int pin, int min, int max) {
servos[this->servoIndex].Pin.isActive = true; // this must be set after the check for isTimerActive servos[this->servoIndex].Pin.isActive = true; // this must be set after the check for isTimerActive
return this->servoIndex; return this->servoIndex;
} }
void Servo::detach() { void Servo::detach() {
servos[this->servoIndex].Pin.isActive = false; servos[this->servoIndex].Pin.isActive = false;
timer16_Sequence_t timer = SERVO_INDEX_TO_TIMER(servoIndex); timer16_Sequence_t timer = SERVO_INDEX_TO_TIMER(servoIndex);
if (!isTimerActive(timer)) finISR(timer); if (!isTimerActive(timer)) finISR(timer);
} }
void Servo::write(int value) { void Servo::write(int value) {
if (value < MIN_PULSE_WIDTH) { // treat values less than 544 as angles in degrees (valid values in microseconds are handled as microseconds) if (value < MIN_PULSE_WIDTH) { // treat values less than 544 as angles in degrees (valid values in microseconds are handled as microseconds)
value = map(constrain(value, 0, 180), 0, 180, SERVO_MIN(), SERVO_MAX()); value = map(constrain(value, 0, 180), 0, 180, SERVO_MIN(), SERVO_MAX());
} }
this->writeMicroseconds(value); this->writeMicroseconds(value);
} }
void Servo::writeMicroseconds(int value) { void Servo::writeMicroseconds(int value) {
// calculate and store the values for the given channel // calculate and store the values for the given channel
byte channel = this->servoIndex; byte channel = this->servoIndex;
if (channel < MAX_SERVOS) { // ensure channel is valid if (channel < MAX_SERVOS) { // ensure channel is valid
...@@ -267,21 +267,21 @@ void Servo::writeMicroseconds(int value) { ...@@ -267,21 +267,21 @@ void Servo::writeMicroseconds(int value) {
value = usToTicks(value); // convert to ticks after compensating for interrupt overhead value = usToTicks(value); // convert to ticks after compensating for interrupt overhead
CRITICAL_SECTION_START; CRITICAL_SECTION_START;
servo_info[channel].ticks = value; servos[channel].ticks = value;
CRITICAL_SECTION_END; CRITICAL_SECTION_END;
} }
} }
// return the value as degrees // return the value as degrees
int Servo::read() { return map(this->readMicroseconds() + 1, SERVO_MIN(), SERVO_MAX(), 0, 180); } int Servo::read() { return map(this->readMicroseconds() + 1, SERVO_MIN(), SERVO_MAX(), 0, 180); }
int Servo::readMicroseconds() { int Servo::readMicroseconds() {
return (this->servoIndex == INVALID_SERVO) ? 0 : ticksToUs(servos[this->servoIndex].ticks) + TRIM_DURATION; return (this->servoIndex == INVALID_SERVO) ? 0 : ticksToUs(servos[this->servoIndex].ticks) + TRIM_DURATION;
} }
bool Servo::attached() { return servos[this->servoIndex].Pin.isActive; } bool Servo::attached() { return servos[this->servoIndex].Pin.isActive; }
void Servo::move(int value) { void Servo::move(int value) {
if (this->attach(0) >= 0) { if (this->attach(0) >= 0) {
this->write(value); this->write(value);
#if ENABLED(DEACTIVATE_SERVOS_AFTER_MOVE) #if ENABLED(DEACTIVATE_SERVOS_AFTER_MOVE)
...@@ -289,6 +289,6 @@ void Servo::move(int value) { ...@@ -289,6 +289,6 @@ void Servo::move(int value) {
this->detach(); this->detach();
#endif #endif
} }
} }
#endif #endif
...@@ -45,12 +45,14 @@ ...@@ -45,12 +45,14 @@
detach() - Stops an attached servos from pulsing its i/o pin. detach() - Stops an attached servos from pulsing its i/o pin.
*/ */
#ifndef SERVO_H #ifndef _SERVO_H
#define SERVO_H #define _SERVO_H
#include <inttypes.h> #if HAS(SERVOS)
#include <inttypes.h>
#include "ServoTimers.h"
/* /*
* Defines for 16 bit timers used with Servo library * Defines for 16 bit timers used with Servo library
* *
* If _useTimerX is defined then TimerX is a 16 bit timer on the current board * If _useTimerX is defined then TimerX is a 16 bit timer on the current board
...@@ -58,32 +60,29 @@ ...@@ -58,32 +60,29 @@
* _Nbr_16timers indicates how many 16 bit timers are available. * _Nbr_16timers indicates how many 16 bit timers are available.
*/ */
// Architecture specific include #define Servo_VERSION 2 // software version of this library
#include "ServoTimers.h"
#define Servo_VERSION 2 // software version of this library
#define MIN_PULSE_WIDTH 544 // the shortest pulse sent to a servo #define MIN_PULSE_WIDTH 544 // the shortest pulse sent to a servo
#define MAX_PULSE_WIDTH 2400 // the longest pulse sent to a servo #define MAX_PULSE_WIDTH 2400 // the longest pulse sent to a servo
#define DEFAULT_PULSE_WIDTH 1500 // default pulse width when servo is attached #define DEFAULT_PULSE_WIDTH 1500 // default pulse width when servo is attached
#define REFRESH_INTERVAL 20000 // minumim time to refresh servos in microseconds #define REFRESH_INTERVAL 20000 // minumim time to refresh servos in microseconds
#define SERVOS_PER_TIMER 12 // the maximum number of servos controlled by one timer #define SERVOS_PER_TIMER 12 // the maximum number of servos controlled by one timer
#define MAX_SERVOS (_Nbr_16timers * SERVOS_PER_TIMER) #define MAX_SERVOS (_Nbr_16timers * SERVOS_PER_TIMER)
#define INVALID_SERVO 255 // flag indicating an invalid servo index #define INVALID_SERVO 255 // flag indicating an invalid servo index
typedef struct { typedef struct {
uint8_t nbr :6 ; // a pin number from 0 to 63 uint8_t nbr :6 ; // a pin number from 0 to 63
uint8_t isActive :1 ; // true if this channel is enabled, pin not pulsed if false uint8_t isActive :1 ; // true if this channel is enabled, pin not pulsed if false
} ServoPin_t; } ServoPin_t;
typedef struct { typedef struct {
ServoPin_t Pin; ServoPin_t Pin;
volatile unsigned int ticks; volatile unsigned int ticks;
} servo_t; } servo_t;
class Servo { class Servo {
public: public:
Servo(); Servo();
uint8_t attach(int pin); // attach the given pin to the next free channel, sets pinMode, returns channel number or 0 if failure uint8_t attach(int pin); // attach the given pin to the next free channel, sets pinMode, returns channel number or 0 if failure
...@@ -102,6 +101,7 @@ class Servo { ...@@ -102,6 +101,7 @@ class Servo {
uint8_t servoIndex; // index into the channel data for this servo uint8_t servoIndex; // index into the channel data for this servo
int8_t min; // minimum is this value times 4 added to MIN_PULSE_WIDTH int8_t min; // minimum is this value times 4 added to MIN_PULSE_WIDTH
int8_t max; // maximum is this value times 4 added to MAX_PULSE_WIDTH int8_t max; // maximum is this value times 4 added to MAX_PULSE_WIDTH
}; };
#endif #endif // SERVOS
#endif // _SERVO_H
#include "../../base.h" #include "../../base.h"
#if ENABLED(USE_WATCHDOG) #if ENABLED(USE_WATCHDOG)
#include "watchdog.h"
#include "watchdog.h" // Initialize watchdog with a 4 sec interrupt time
void watchdog_init() {
// Initialize watchdog with a 4 sec interrupt time
void watchdog_init() {
#if ENABLED(WATCHDOG_RESET_MANUAL) #if ENABLED(WATCHDOG_RESET_MANUAL)
// We enable the watchdog timer, but only for the interrupt. // We enable the watchdog timer, but only for the interrupt.
// Take care, as this requires the correct order of operation, with interrupts disabled. See the datasheet of any AVR chip for details. // Take care, as this requires the correct order of operation, with interrupts disabled. See the datasheet of any AVR chip for details.
...@@ -15,19 +14,19 @@ void watchdog_init() { ...@@ -15,19 +14,19 @@ void watchdog_init() {
#else #else
wdt_enable(WDTO_4S); wdt_enable(WDTO_4S);
#endif #endif
} }
//=========================================================================== //===========================================================================
//=================================== ISR =================================== //=================================== ISR ===================================
//=========================================================================== //===========================================================================
// Watchdog timer interrupt, called if main program blocks >1sec and manual reset is enabled. // Watchdog timer interrupt, called if main program blocks >1sec and manual reset is enabled.
#if ENABLED(WATCHDOG_RESET_MANUAL) #if ENABLED(WATCHDOG_RESET_MANUAL)
ISR(WDT_vect) { ISR(WDT_vect) {
ECHO_LM(ER, "Something is wrong, please turn off the printer."); ECHO_LM(ER, "Something is wrong, please turn off the printer.");
kill(PSTR("ERR:Please Reset")); //kill blocks //16 characters so it fits on a 16x2 display kill(PSTR("ERR:Please Reset")); //kill blocks //16 characters so it fits on a 16x2 display
while (1); //wait for user or serial reset while (1); //wait for user or serial reset
} }
#endif //WATCHDOG_RESET_MANUAL #endif //WATCHDOG_RESET_MANUAL
#endif //USE_WATCHDOG #endif //USE_WATCHDOG
#ifndef WATCHDOG_H #ifndef WATCHDOG_H
#define WATCHDOG_H #define WATCHDOG_H
#include <avr/wdt.h>
#include <avr/wdt.h> // Initialize watchdog with a 4 second interrupt time
void watchdog_init();
// Initialize watchdog with a 4 second interrupt time
void watchdog_init();
// Reset watchdog. MUST be called at least every 4 seconds after the
// first watchdog_init or AVR will go into emergency procedures.
inline void watchdog_reset() { wdt_reset(); }
// Reset watchdog. MUST be called at least every 4 seconds after the
// first watchdog_init or AVR will go into emergency procedures.
inline void watchdog_reset() { wdt_reset(); }
#endif #endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment