Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CAN bus host & ADXL support #27547

Open
wants to merge 13 commits into
base: bugfix-2.1.x
Choose a base branch
from
4 changes: 4 additions & 0 deletions Marlin/Configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@
//#define RS485_BUS_BUFFER_SIZE 128
#endif

// Enable CAN bus support and protocol
//#define CAN_MASTER
//#define CAN_TOOLHEAD

// Enable the Bluetooth serial interface on AT90USB devices
//#define BLUETOOTH

Expand Down
782 changes: 782 additions & 0 deletions Marlin/src/HAL/STM32/CAN.cpp

Large diffs are not rendered by default.

588 changes: 588 additions & 0 deletions Marlin/src/HAL/STM32/FDCAN.cpp

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions Marlin/src/HAL/STM32/Servo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@

#include "Servo.h"

#if ENABLED(CAN_MASTER)
#include "../shared/CAN.h"
#endif

static uint_fast8_t servoCount = 0;
static libServo *servos[NUM_SERVOS] = {0};
constexpr millis_t servoDelay[] = SERVO_DELAY;
Expand Down Expand Up @@ -71,6 +75,18 @@ int8_t libServo::attach(const int pin, const int min, const int max) {
}

void libServo::move(const int value) {

#if ENABLED(CAN_MASTER) // Forward direct Servo command to head
constexpr int angles[2] = Z_SERVO_ANGLES;
// Translate M280 S10 to M401, M280 S90 to M402
if (value == angles[0])
CAN_Send_Gcode_2params('M', 401, 0, 0, 0, 0); // Deploy Angle: Send "M401" instead, enables interrupt etc.
else if (value == angles[1])
CAN_Send_Gcode_2params('M', 402, 0, 0, 0, 0); // Stow Angle: Send "M402" instead, enables interrupt etc.
else
CAN_Send_Gcode_2params('M', 280, 'S', value, 'P', 0); // M280 S[value] P0
#endif

if (attach(0) >= 0) {
stm32_servo.write(value);
safe_delay(delay);
Expand Down
50 changes: 50 additions & 0 deletions Marlin/src/HAL/shared/CAN.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2024 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

#include "../../inc/MarlinConfigPre.h"

//#define CAN_DEBUG // Define to show gcodes send to HEAD

#define SOUND_OK 880
#define SOUND_ERROR 40

extern uint32_t CAN_io_state; // CAN virtual IO variable

#define CAN_IO_MASK 0b11111 // Masks for the 5 virtual IO bits
#define CAN_PROBE_MASK 1 // Virtual IO bit
#define CAN_FILAMENT_MASK 2 // Virtual IO bit
#define CAN_X_ENDSTOP_MASK 4 // Virtual IO bit
#define CAN_Y_ENDSTOP_MASK 8 // Virtual IO bit
#define CAN_Z_ENDSTOP_MAS 16 // Virtual IO bit
#define CAN_STRING_MESSAGE_MASK 32 // Signals the head sends a string message
#define CAN_REQUEST_SETUP_MASK 64 // Signals the head requests setup information
#define CAN_TMC_OT_MASK 128 // Signals the head signals a TMC Over Temp error
#define CAN_CAN_REQUEST_TIME_SYNC_MASK 256 // Signals the head requested a time sync
#define CAN_ERROR_MASK 512 // Signals the head encountered an error

HAL_StatusTypeDef CAN1_Start(); // FUNCTION PROTOTYPES
HAL_StatusTypeDef CAN1_Stop();
HAL_StatusTypeDef CAN_Send_Gcode();
HAL_StatusTypeDef CAN_Send_Gcode_2params(uint32_t Gcode_type, uint32_t Gcode_no, uint32_t parameter1, float value1, uint32_t parameter2, float value2);
void CAN_Send_Setup(); // Send host configuration to head
void CAN_Idle(); // Idle CAN task
48 changes: 48 additions & 0 deletions Marlin/src/HAL/shared/FDCAN.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#pragma once

// IMPORTANT NOTES
// ===============
// In \Users\<username>\.platformio\packages\framework-arduinoststm32@4.20600.231001\libraries\SrcWrapper\src\HardwareTimer.cpp
// Add function "__weak" in front of "void TIM16_IRQHandler(void)"
// NOTE: Every CAN message is accepted in FIFO0 if: 1. No Standard/Extended filters are set. 2. No global filters is set.

#define CAN_IO_MASK 0b11111 // Virtual IO Mask, 5 bits are used for IO signalling
#define CAN_PROBE_BIT 0 // Virtual IO bit
#define CAN_FILAMENT_BIT 1 // Virtual IO bit
#define CAN_X_ENDSTOP_BIT 2 // Virtual IO bit
#define CAN_Y_ENDSTOP_BIT 3 // Virtual IO bit
#define CAN_Z_ENDSTOP_BIT 4 // Virtual IO bit
#define CAN_STRING_MESSAGE_BIT 5 // Signals the head sends a string message
#define CAN_REQUEST_SETUP_BIT 6 // Signals the head requests setup information
#define CAN_TMC_OT_BIT 7 // Signals the head encountered a TMC Over Temp error
#define CAN_REQUEST_TIME_SYNC_BIT 8 // Signals a request for time sync
#define CAN_ERROR_BIT 9 // Signals the head encountered an error

#define GCODE_TYPE_D 0
#define GCODE_TYPE_G 1
#define GCODE_TYPE_M 2
#define GCODE_TYPE_T 3

#define IDENTIFIER_PARAMTER1_OFFSET 0
#define IDENTIFIER_PARAMTER2_OFFSET 5
#define IDENTIFIER_GCODE_NUMBER_OFFSET 10
#define IDENTIFIER_GCODE_TYPE_OFFSET 23
#define IDENTIFIER_PARAMETER_COUNT_OFFSET 25

#define DATALENGTH_OFFSET 16

#define IDENTIFIER_PARAMETER_COUNT_MASK 0b111
#define IDENTIFIER_PARAMETER_MASK 0b11111
#define IDENTIFIER_GCODE_TYPE_MASK 0b11 // GCODE TYPE
#define IDENTIFIER_GCODE_NUMBER_MASK 0b1111111111111 // GCODE NUMBER ONLY
#define IDENTIFIER_GCODE_MASK 0b111111111111111 // GCODE TYPE AND NUMBER

#define STDID_FIFO_BIT 0b10000000000

// ERROR CODES
#define CAN_ERROR_FIFO_OVERFLOW 1
#define CAN_ERROR_INCOMPLETE_GCODE_RECEIVED 2
#define CAN_ERROR_MARLIN_CMD_BUFFER_OVERFLOW 4

HAL_StatusTypeDef CAN_Send_String(const char * message); // Send CAN string to host
HAL_StatusTypeDef FDCAN2_Start(void); // Start the CAN bus
34 changes: 33 additions & 1 deletion Marlin/src/MarlinCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@
#include "HAL/shared/esp_wifi.h"
#include "HAL/shared/cpu_exception/exception_hook.h"

#if ENABLED(CAN_MASTER)
#include "HAL/shared/CAN.h"
#endif

#if ENABLED(CAN_TOOLHEAD)
#include "HAL/shared/FDCAN.h"
#endif

#if ENABLED(WIFISUPPORT)
#include "HAL/shared/esp_wifi.h"
#endif
Expand Down Expand Up @@ -882,6 +890,16 @@ void idle(const bool no_stepper_sleep/*=false*/) {
// Manage Fixed-time Motion Control
TERN_(FT_MOTION, ftMotion.loop());

#if ENABLED(CAN_MASTER)
void CAN_idle(); // Function Prototype
CAN_idle(); // Call CAN idle task
#endif

#if ENABLED(CAN_TOOLHEAD)
void FDCAN_idle(); // Function prototype
FDCAN_idle(); // Call FDCAN idle task
#endif // CAN_TOOLHEAD

IDLE_DONE:
TERN_(MARLIN_DEV_MODE, idle_depth--);

Expand Down Expand Up @@ -1190,7 +1208,21 @@ void setup() {
while (!MYSERIAL3.connected() && PENDING(millis(), serial_connect_timeout)) { /*nada*/ }
#endif
#endif
SERIAL_ECHOLNPGM("start");
SERIAL_ECHOLNPGM("start\n");

#if ENABLED(CAN_MASTER)
SERIAL_ECHOLN(
F(">>> CAN1 Start: "),
CAN1_Start() == HAL_OK ? F("OK") : F("FAILED!")
);
#endif

#if ENABLED(CAN_TOOLHEAD)
SERIAL_ECHOLN(
F(">>> FDCAN2 Start: "),
FDCAN2_Start() == HAL_OK ? F("OK") : F("FAILED!")
);
#endif

// Set up these pins early to prevent suicide
#if HAS_KILL
Expand Down
12 changes: 11 additions & 1 deletion Marlin/src/feature/runout.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@
#include "pause.h" // for did_pause_print
#include "../MarlinCore.h" // for printingIsActive()

#if ENABLED(CAN_MASTER)
#include "../HAL/shared/CAN.h"
#endif

#include "../inc/MarlinConfig.h"

#if ENABLED(EXTENSIBLE_UI)
Expand All @@ -53,6 +57,12 @@

#define FILAMENT_IS_OUT() (READ(FIL_RUNOUT_PIN) == FIL_RUNOUT_STATE)

#if ENABLED(CAN_MASTER)
#define RUNOUT_STATE(N) bool(CAN_io_state & CAN_FILAMENT_MASK) // CAN Virtual Filament Runout pin
#else
#define RUNOUT_STATE(N) READ(FIL_RUNOUT##N##_PIN) // DIO Filament Runout pin
#endif

typedef Flags<
#if NUM_MOTION_SENSORS > NUM_RUNOUT_SENSORS
NUM_MOTION_SENSORS
Expand Down Expand Up @@ -207,7 +217,7 @@ class FilamentSensorBase {

// Return a bitmask of runout pin states
static uint8_t poll_runout_pins() {
#define _OR_RUNOUT(N) | (READ(FIL_RUNOUT##N##_PIN) ? _BV((N) - 1) : 0)
#define _OR_RUNOUT(N) | (RUNOUT_STATE(N) ? _BV((N) - 1) : 0)
return (0 REPEAT_1(NUM_RUNOUT_SENSORS, _OR_RUNOUT));
#undef _OR_RUNOUT
}
Expand Down
14 changes: 14 additions & 0 deletions Marlin/src/gcode/gcode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ GcodeSuite gcode;
#include "../feature/fancheck.h"
#endif

#if ENABLED(CAN_MASTER)
#include "../HAL/shared/CAN.h"
#include "../libs/buzzer.h"
#endif

#include "../MarlinCore.h" // for idle, kill

// Inactivity shutdown
Expand Down Expand Up @@ -323,6 +328,15 @@ void GcodeSuite::process_parsed_command(const bool no_ok/*=false*/) {

KEEPALIVE_STATE(IN_HANDLER);

#if ENABLED(CAN_MASTER)
if (CAN_Send_Gcode() != HAL_OK) { // Send command to head
SERIAL_ECHOLN(F("Error: CAN failed to send \""), parser.command_ptr, '"');
#ifndef CAN_DEBUG
BUZZ(1, SOUND_ERROR);
#endif
}
#endif

/**
* Block all Gcodes except M511 Unlock Printer, if printer is locked
* Will still block Gcodes if M511 is disabled, in which case the printer should be unlocked via LCD Menu
Expand Down
61 changes: 51 additions & 10 deletions Marlin/src/gcode/temp/M306.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,16 +57,34 @@ void GcodeSuite::M306() {

#if ENABLED(MPC_AUTOTUNE)
if (parser.seen_test('T')) {
Temperature::MPCTuningType tuning_type;
const uint8_t type = parser.byteval('S', 0);
switch (type) {
case 1: tuning_type = Temperature::MPCTuningType::FORCE_DIFFERENTIAL; break;
case 2: tuning_type = Temperature::MPCTuningType::FORCE_ASYMPTOTIC; break;
default: tuning_type = Temperature::MPCTuningType::AUTO; break;
}
LCD_MESSAGE(MSG_MPC_AUTOTUNE);
thermalManager.MPC_autotune(e, tuning_type);
ui.reset_status();

#if ENABLED(CAN_MASTER) // MPC Autotune info

SERIAL_ECHOLNPGM(
">>> Forwarding M306 to head board\n"
">>> Store MPC setup in the host Configuration.h or use M500\n"
">>> MPC heater power is: ", p_float_t(MPC_HEATER_POWER, 1), " Watts\n"
">>> Please wait for the auto tune results..."
);

#else

Temperature::MPCTuningType tuning_type;
const uint8_t type = parser.byteval('S', 0);
switch (type) {
case 1: tuning_type = Temperature::MPCTuningType::FORCE_DIFFERENTIAL; break;
case 2: tuning_type = Temperature::MPCTuningType::FORCE_ASYMPTOTIC; break;
default: tuning_type = Temperature::MPCTuningType::AUTO; break;
}
LCD_MESSAGE(MSG_MPC_AUTOTUNE);
thermalManager.MPC_autotune(e, tuning_type);
ui.reset_status();

#if ENABLED(CAN_TOOLHEAD)
M306_report(true); // Report M306 settings to CAN host
#endif
#endif

return;
}
#endif
Expand All @@ -91,6 +109,11 @@ void GcodeSuite::M306_report(const bool forReplay/*=true*/) {
TERN_(MARLIN_SMALL_BUILD, return);

report_heading(forReplay, F("Model predictive control"));

#if ENABLED(CAN_MASTER) // MPC Autotune info
if (forReplay) SERIAL_ECHOLNPGM(">>> Host M306 MPC settings:");
#endif

HOTEND_LOOP() {
report_echo_start(forReplay);
MPC_t &mpc = thermalManager.temp_hotend[e].mpc;
Expand All @@ -105,6 +128,24 @@ void GcodeSuite::M306_report(const bool forReplay/*=true*/) {
#endif
SERIAL_ECHOLNPGM(" H", p_float_t(mpc.filament_heat_capacity_permm, 4));
}

#if ENABLED(CAN_TOOLHEAD) // Report M306 Autotune results to host
if (forReplay) {
MPC_t &mpc = thermalManager.temp_hotend[0].mpc;
MString<100> buffer(F("M306 E0 P"), p_float_t(mpc.heater_power, 2),
" C", p_float_t(mpc.block_heat_capacity, 2),
" R", p_float_t(mpc.sensor_responsiveness, 4),
" A", p_float_t(mpc.ambient_xfer_coeff_fan0, 4),
#if ENABLED(MPC_INCLUDE_FAN)
" F", p_float_t(mpc.fanCoefficient(), 4),
#endif
" H", p_float_t(mpc.filament_heat_capacity_permm, 4),
'\n');

HAL_StatusTypeDef CAN_Send_String(const char * message); // Function Prototype
CAN_Send_String(buffer);
}
#endif // CAN_TOOLHEAD
}

#endif // MPCTEMP
21 changes: 20 additions & 1 deletion Marlin/src/module/endstops.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@
#include "probe.h"
#endif

#if HAS_FILAMENT_SENSOR && !MULTI_FILAMENT_SENSOR
#include "../feature/runout.h"
#endif

#define DEBUG_OUT ALL(USE_SENSORLESS, DEBUG_LEVELING_FEATURE)
#include "../core/debug_out.h"

Expand All @@ -77,6 +81,12 @@ Endstops::endstop_mask_t Endstops::live_state = 0;
#else
#define READ_ENDSTOP(P) READ(P)
#endif
#elif ENABLED(CAN_MASTER) // Read virtual CAN IO Probe status if needed
#if HAS_BED_PROBE
#define READ_ENDSTOP(P) ((P == Z_MIN_PIN) ? PROBE_READ() : READ(P))
#else
#define READ_ENDSTOP(P) READ(P)
#endif
#else
#define READ_ENDSTOP(P) READ(P)
#endif
Expand Down Expand Up @@ -525,8 +535,11 @@ void __O2 Endstops::report_states() {
print_es_state(extDigitalRead(pin) != state);
}
#undef _CASE_RUNOUT

#elif HAS_FILAMENT_SENSOR
print_es_state(READ(FIL_RUNOUT1_PIN) != FIL_RUNOUT1_STATE, F(STR_FILAMENT));

print_es_state(RUNOUT_STATE(1) != FIL_RUNOUT1_STATE, F(STR_FILAMENT));

#endif

TERN_(BLTOUCH, bltouch._reset_SW_mode());
Expand Down Expand Up @@ -664,6 +677,12 @@ void Endstops::update() {
// When closing the gap check the enabled probe
if (probe_switch_activated())
UPDATE_LIVE_STATE(Z, TERN(USE_Z_MIN_PROBE, MIN_PROBE, MIN));

#if ENABLED(CAN_TOOLHEAD)
HAL_StatusTypeDef CAN_Send_Message(bool TempUpdate); // Function Prototype
CAN_Send_Message(false); // Send Virtual IO update without temperature report
#endif // CAN_TOOLHEAD

#endif

#if USE_Z_MAX
Expand Down
Loading
Loading