Simulator: Virtual servos, BLTouch (#27779)

Co-authored-by: Christopher Pepper <p3p@p3psoft.co.uk>
This commit is contained in:
ellensp 2025-04-19 12:43:31 +12:00 committed by GitHub
parent 3c32770faf
commit d009c48b11
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 206 additions and 97 deletions

View file

@ -52,7 +52,9 @@ uint8_t _getc();
// ------------------------
#define CPU_32_BIT
#define SHARED_SERVOS HAS_SERVOS // Use shared/servos.cpp
class Servo;
typedef Servo hal_servo_t;
#define F_CPU 100000000
#define SystemCoreClock F_CPU
@ -232,8 +234,10 @@ public:
* No option to invert the duty cycle [default = false]
* No option to change the scale of the provided value to enable finer PWM duty control [default = 255]
*/
static void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t=255, const bool=false) {
analogWrite(pin, v);
static void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size=255, const bool invert=false) {
auto value = map(v, 0, v_size, 0, UINT16_MAX);
value = invert ? UINT16_MAX - value : value;
analogWrite(pin, value);
}
static void set_pwm_frequency(const pin_t, int) {}

View file

@ -0,0 +1,104 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program 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.
*
* This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include "../platforms.h"
#ifdef __PLAT_NATIVE_SIM__
#include "../../inc/MarlinConfig.h"
#if HAS_SERVOS
#include "Servo.h"
//#define DEBUG_SERVOS
#define DEBUG_OUT ENABLED(DEBUG_SERVOS)
#include "../../../core/debug_out.h"
uint8_t ServoCount = 0; // the total number of attached servos
Servo::Servo() {
// Constructor stub
DEBUG_ECHOLNPGM("Debug Servo: constructor");
this->servoIndex = ServoCount++; // assign a servo index to this instance
}
uint8_t Servo::attach(int pin) {
// Attach stub
DEBUG_ECHOLNPGM("Debug Servo: attach to pin ", pin, " servo index ", this->servoIndex);
return attach(pin, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH);
}
uint8_t Servo::attach(int pin, int min, int max) {
// Attach with min and max stub
DEBUG_ECHOLNPGM("Debug Servo: attach to pin ", pin, " with min ", min, " and max ", max);
if (pin > 0) servo_pin = pin;
return this->servoIndex;
}
void Servo::detach() {
// Detach stub
DEBUG_ECHOLNPGM("Debug Servo: detach");
}
// If value is < 200 it is treated as an angle, otherwise as pulse width in microseconds
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)
value = map(constrain(value, 0, 180), 0, 180, SERVO_MIN_US(min), SERVO_MAX_US(max));
}
writeMicroseconds(value);
DEBUG_ECHOLNPGM("Debug Servo: write ", value);
}
void Servo::writeMicroseconds(int value) {
// Simulate the servo movement
this->value = value;
hal.set_pwm_duty(pin_t(this->servo_pin), (float(value) / 20000) * UINT16_MAX, UINT16_MAX);
DEBUG_ECHOLNPGM("Debug Servo: write microseconds ", value);
}
int Servo::read() {
// Read stub
DEBUG_ECHOLNPGM("Debug Servo: read ", this->value);
return this->value;
}
int Servo::readMicroseconds() {
// Read microseconds stub
DEBUG_ECHOLNPGM("Debug Servo: read microseconds");
return 0;
}
bool Servo::attached() {
// Attached stub
DEBUG_ECHOLNPGM("Debug Servo: attached");
return false;
}
int Servo::move(const unsigned char cmd) {
// Move stub
DEBUG_ECHOLNPGM("Debug Servo: move ", cmd);
write(cmd);
return 0;
}
#endif // HAS_SERVOS
#endif // __PLAT_NATIVE_SIM__

View file

@ -0,0 +1,48 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program 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.
*
* This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#pragma once
#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 DEFAULT_PULSE_WIDTH 1500 // default pulse width when servo is attached
#define SERVO_MIN_US(v) (MIN_PULSE_WIDTH - (v) * 4) // minimum value in uS for this servo
#define SERVO_MAX_US(v) (MAX_PULSE_WIDTH - (v) * 4) // maximum value in uS for this servo
class Servo {
public:
Servo();
uint8_t attach(int pin); // Attach the given pin to the next free channel, set pinMode, return channel number or INVALID_SERVO if failure
uint8_t attach(int pin, int min, int max); // As above but also set min and max values for writes.
void detach();
void write(int value); // If value is < 200 it's treated as an angle, otherwise as pulse width in microseconds
void writeMicroseconds(int value); // Write pulse width in microseconds
int read(); // Return current pulse width as an angle between 0 and 180 degrees
int readMicroseconds(); // Return current pulse width in microseconds for this servo
bool attached(); // Return true if this servo is attached, otherwise false
int move (const unsigned char cmd);
private:
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 max; // Maximum is this value times 4 added to MAX_PULSE_WIDTH
int value; // Pulse width in microseconds for this servo
int servo_pin = 0; // pin number for this servo
};

View file

@ -0,0 +1,30 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program 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.
*
* This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#pragma once
#error "ENDSTOP_INTERRUPTS_FEATURE is not supported in this simulation environment."
void setup_endstop_interrupts() {
// This function is a stub for setting up endstop interrupts.
// Since this is a simulation environment, actual hardware interrupts
// are not applicable. Add any necessary simulation-specific logic here.
}

View file

@ -1,79 +0,0 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program 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.
*
* This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#pragma once
/**
* servo.h - Interrupt driven Servo library for Arduino using 16 bit timers- Version 2
* Copyright (c) 2009 Michael Margolis. All right reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* 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,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* Based on "servo.h - Interrupt driven Servo library for Arduino using 16 bit timers -
* Version 2 Copyright (c) 2009 Michael Margolis. All right reserved.
*
* The only modification was to update/delete macros to match the LPC176x.
*
*/
#include <stdint.h>
// Macros
//values in microseconds
#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 DEFAULT_PULSE_WIDTH 1500 // default pulse width when servo is attached
#define REFRESH_INTERVAL 20000 // minimum time to refresh servos in microseconds
#define MAX_SERVOS 4
#define INVALID_SERVO 255 // flag indicating an invalid servo index
// Types
typedef struct {
uint8_t nbr : 8 ; // a pin number from 0 to 254 (255 signals invalid pin)
uint8_t isActive : 1 ; // true if this channel is enabled, pin not pulsed if false
} ServoPin_t;
typedef struct {
ServoPin_t Pin;
unsigned int pulse_width; // pulse width in microseconds
} ServoInfo_t;
// Global variables
extern uint8_t ServoCount;
extern ServoInfo_t servo_info[MAX_SERVOS];

View file

@ -59,7 +59,7 @@
* write() - Sets the servo angle in degrees. (invalid angle that is valid as pulse in microseconds is treated as microseconds)
* writeMicroseconds() - Sets the servo pulse width in microseconds
* read() - Gets the last written servo pulse width as an angle between 0 and 180.
* readMicroseconds() - Gets the last written servo pulse width in microseconds. (was read_us() in first release)
* readMicroseconds() - Gets the last written servo pulse width in microseconds.
* attached() - Returns true if there is a servo attached.
* detach() - Stops an attached servos from pulsing its i/o pin.
* move(angle) - Sequence of attach(0), write(angle),
@ -86,6 +86,8 @@
#include "../ESP32/Servo.h"
#elif defined(__PLAT_RP2040__)
#include "../RP2040/Servo.h"
#elif defined(__PLAT_NATIVE_SIM__)
#include "../NATIVE_SIM/Servo.h"
#else
#include <stdint.h>
@ -100,22 +102,22 @@
class Servo {
public:
Servo();
int8_t attach(const int pin); // attach the given pin to the next free channel, set pinMode, return channel number (-1 on fail)
int8_t attach(const int pin, const int min, const int max); // as above but also sets min and max values for writes.
int8_t attach(const int pin); // Attach the given pin to the next free channel, set pinMode, return channel number (-1 on fail)
int8_t attach(const int pin, const int min, const int max); // As above but also set min and max values for writes.
void detach();
void write(int value); // if value is < 200 it is treated as an angle, otherwise as pulse width in microseconds
void writeMicroseconds(int value); // write pulse width in microseconds
void move(const int value); // attach the servo, then move to value
// if value is < 200 it is treated as an angle, otherwise as pulse width in microseconds
// if DEACTIVATE_SERVOS_AFTER_MOVE wait SERVO_DELAY, then detach
int read(); // returns current pulse width as an angle between 0 and 180 degrees
int readMicroseconds(); // returns current pulse width in microseconds for this servo (was read_us() in first release)
bool attached(); // return true if this servo is attached, otherwise false
void write(int value); // If value is < 200 it is treated as an angle, otherwise as pulse width in microseconds
void writeMicroseconds(int value); // Write pulse width in microseconds
void move(const int value); // Attach the servo, then move to value
// If value is < 200 it is treated as an angle, otherwise as pulse width in microseconds
// If DEACTIVATE_SERVOS_AFTER_MOVE wait SERVO_DELAY, then detach
int read(); // Return current pulse width as an angle between 0 and 180 degrees
int readMicroseconds(); // Return current pulse width in microseconds for this servo
bool attached(); // Return true if this servo is attached, otherwise false
private:
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 max; // maximum is this value times 4 added to MAX_PULSE_WIDTH
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 max; // Maximum is this value times 4 added to MAX_PULSE_WIDTH
};
#endif

View file

@ -58,7 +58,7 @@ debug_build_flags = -fstack-protector-strong -g -g3 -ggdb
lib_compat_mode = off
build_src_filter = ${common.default_src_filter} +<src/HAL/NATIVE_SIM>
lib_deps = ${common.lib_deps}
MarlinSimUI=https://github.com/p3p/MarlinSimUI/archive/afe7c1c293.zip
MarlinSimUI=https://github.com/p3p/MarlinSimUI/archive/6ea016e104.zip
Adafruit NeoPixel=https://github.com/p3p/Adafruit_NeoPixel/archive/c6b319f447.zip
LiquidCrystal=https://github.com/p3p/LiquidCrystal/archive/322fb5fc23.zip
extra_scripts = ${common.extra_scripts}