From ef969ffe0a2464a83935de5e87fe7203e04baad3 Mon Sep 17 00:00:00 2001 From: rondlh <77279634+rondlh@users.noreply.github.com> Date: Wed, 27 Nov 2024 00:54:56 +0800 Subject: [PATCH 01/12] Added CAN host support Added experimental CAN host support to communicate with BTT EBB42 V1.2 CAN head board running Marlin --- Marlin/Configuration.h | 4 +- Marlin/src/HAL/STM32/CAN.cpp | 752 ++++++++++++++++++++++++++++++ Marlin/src/HAL/STM32/CAN.h | 28 ++ Marlin/src/HAL/STM32/Servo.cpp | 15 + Marlin/src/MarlinCore.cpp | 13 +- Marlin/src/feature/pause.cpp | 5 + Marlin/src/feature/runout.h | 10 + Marlin/src/gcode/gcode.cpp | 15 + Marlin/src/gcode/temp/M306.cpp | 15 + Marlin/src/module/endstops.cpp | 12 + Marlin/src/module/probe.h | 23 +- Marlin/src/module/temperature.cpp | 13 + ini/stm32f4.ini | 5 + platformio.ini | 2 +- 14 files changed, 906 insertions(+), 6 deletions(-) create mode 100644 Marlin/src/HAL/STM32/CAN.cpp create mode 100644 Marlin/src/HAL/STM32/CAN.h diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h index c45c75a39d96..d4ba2265dd75 100644 --- a/Marlin/Configuration.h +++ b/Marlin/Configuration.h @@ -37,6 +37,8 @@ */ #define CONFIGURATION_H_VERSION 02010300 +// ENABLE CAN SUPPORT HERE +// #define CAN_MASTER //=========================================================================== //============================= Getting Started ============================= //=========================================================================== @@ -68,7 +70,7 @@ // Choose the name from boards.h that matches your setup #ifndef MOTHERBOARD - #define MOTHERBOARD BOARD_RAMPS_14_EFB + #define MOTHERBOARD #define MOTHERBOARD BOARD_MKS_MONSTER8_V2 #endif // @section serial diff --git a/Marlin/src/HAL/STM32/CAN.cpp b/Marlin/src/HAL/STM32/CAN.cpp new file mode 100644 index 000000000000..52cfeca1d2d7 --- /dev/null +++ b/Marlin/src/HAL/STM32/CAN.cpp @@ -0,0 +1,752 @@ +// IRON NOTES: +// NOTE 1: For MKS Monster 8 V1/V2 on Arduino use: Board "Generic STM32F4 series", Board part number "Generic F407VETx" +// NOTE 2: Make sure to define "HAL_CAN_MODULE_ENABLED". In VSCode add "build_flags = -D HAL_CAN_MODULE_ENABLED" in platformio.ini. +// For Arduino IDE include "hal_conf_extra.h" holding "#define HAL_CAN_MODULE_ENABLED" +// NOTE 3: To accept all CAN messages, enable 1 filter (FilterBank = 0) in "FilterMode = CAN_FILTERMODE_IDMASK", mask and ID = 0 (0=don't care) +// NOTE 4: Serial communication in ISR causes issues! Hangs etc. so avoid this! +// NOTE 5: A FIFO storage cell is called a Mailbox in STM32F4xx, FIFO0 and FiFO1 can hold 3 CAN message each. +// NOTE 6: The filter ID/mask numbers (LOW/HIGH) do not directly relate to the message ID numbers (See Figure 342 in RM0090) + +#include "../../inc/MarlinConfig.h" + +#ifdef CAN_MASTER + #include "../platforms.h" + #include "../../gcode/gcode.h" + #include "../../module/temperature.h" + #include "../../module/motion.h" // For current_position variable + #include "../../module/planner.h" // For steps/mm parameters variables + #include "../../feature/tmc_util.h" + #include "../../module/stepper.h" + #include "../../module/endstops.h" + #include "../../feature/controllerfan.h" // For controllerFan settings + #include "../../libs/numtostr.h" // For float to string conversion + + // #define CAN_DEBUG // Define to show gcodes send to HEAD + + #define CAN_EXTENDED_ID CAN_ID_EXT + #define CAN_STANDARD_ID CAN_ID_STD + + #define STDID_FIFO_BIT 0b10000000000 + #define EXTID_FIFO_BIT 0x10000000 + + #define CAN_IO_MASK 0b11111 // Masks for the 5 virtual IO bits (see below) + #define GCODE_NUMBER_MASK 0b1111111111111 + #define PARAMETER_MASK 0b11111 + #define PARAMETER_COUNT_MASK 0b111 + #define GCODE_TYPE_MASK 0b11 + #define CAN_PROBE_MASK 1 // Virtual IO bit for probe + #define CAN_FILAMENT_MASK 2 // Virtual IO bit for filament + #define CAN_X_ENDSTOP_MASK 4 // Virtual IO bit for X-endstop + #define CAN_Y_ENDSTOP_MASK 8 // Virtual IO bit for Y-endstop + #define CAN_Z_ENDSTOP_MASK 16 // Virtual IO bit for Z-endstop + #define CAN_STRING_MESSAGE_MASK 32 // Signals the head sent 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_E0_TARGET_MASK 256 // Signals E0 or E1 + #define CAN_ERROR_MASK 512 // Signals the head encountered an error + + #define PARAMETER1_OFFSET 0 + #define PARAMETER2_OFFSET 5 + #define GCODE_NUMBER_OFFSET 10 + #define GCODE_TYPE_OFFSET 23 + #define PARAMETER_COUNT_OFFSET 25 + + #define GCODE_TYPE_D 0 + #define GCODE_TYPE_G 1 + #define GCODE_TYPE_M 2 + #define GCODE_TYPE_T 3 + +extern "C" void CAN1_RX0_IRQHandler(void); // Override weak CAN FIFO0 interrupt handler +extern "C" void CAN1_RX1_IRQHandler(void); // Override weak CAN FIFO1 interrupt handler +extern "C" void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan); // Override weak CAN interrupt callback for FIFO0 +extern "C" void HAL_CAN_RxFifo1MsgPendingCallback(CAN_HandleTypeDef *hcan); // Override weak CAN interrupt callback for FIFO1 +extern "C" void HAL_CAN_ErrorCallback(CAN_HandleTypeDef *hcan); // Override weak CAN interrupt callback + +CAN_HandleTypeDef hcan1 = { 0 }; // The CAN1 handle +CAN_TxHeaderTypeDef TxHeader = { 0 }; // Header to send a CAN message +volatile uint32_t CAN_io_state = 0; // Virtual IO state variable +volatile bool CAN_head_error = 0; // Register if an error was reported by the head +volatile bool CAN_head_setup_request = false; // Signals the head requesting setup information +volatile uint32_t gcode_counter = 0; // Count amount of gcodes received +volatile uint32_t HAL_CAN_error_code = 0; // Record a host CAN error message + +volatile bool FirstE0Error = true; // First CAN bus error, show warning only once +volatile bool string_message_complete = false; // Signals a complete string message was received +volatile uint32_t string_message_index = 0; // Index into the CAN string that is being received +uint32_t Last_CAN_Temp_Report = 0; // Track when the last head temperature report was received +uint32_t Last_CAN_Error_Message = 0; // Track when the last CAN error messaget was shown +char string_message[128] = "\0"; // CAN string message buffer for incoming message, max 128 characters + +void CAN1_RX0_IRQHandler(void) // CAN FIFO0 Interrupt handler overrides standard weak CAN1_RX0_IRQHandler +{ + HAL_CAN_IRQHandler(&hcan1); // Forwarded for callbacks --> HAL_CAN_RxFifo0MsgPendingCallback/HAL_CAN_ErrorCallback +// OR +// HAL_CAN_RxFifo0MsgPendingCallback(&hcan1); // Call the required callback directly, faster but no error reporting +} + +void CAN1_RX1_IRQHandler(void) // CAN FIFO1 Interrupt handler overrides standard weak CAN1_RX0_IRQHandler +{ + HAL_CAN_IRQHandler(&hcan1); // Forwarded for callbacks --> HAL_CAN_RxFifo1MsgPendingCallback/HAL_CAN_ErrorCallback +// OR +// HAL_CAN_RxFifo1MsgPendingCallback(&hcan1); // Call the required callback directly, faster but no error reporting +} + +// Send specified Gcode with max 2 parameters and 2 values via CAN bus +HAL_StatusTypeDef CAN_Send_Gcode_2params(uint32_t Gcode_type, uint32_t Gcode_no, uint32_t parameter1, float value1, uint32_t parameter2, float value2) +{ + switch (Gcode_type) + { + case 'D': Gcode_type = GCODE_TYPE_D; + break; + + case 'G': Gcode_type = GCODE_TYPE_G; + break; + + case 'M': Gcode_type = GCODE_TYPE_M; + +#ifdef CAN_DEBUG + SERIAL_ECHOPGM(">>> CAN TO HEAD: M", Gcode_no); + if (parameter1) { + SERIAL_CHAR(' ', parameter1); + if (value1 == int(value1)) + SERIAL_ECHO(i16tostr3left(value1)); // Integer value + else + SERIAL_ECHO(p_float_t(value1, 4)); // Float with 4 digits + } + + if (parameter2) { + SERIAL_CHAR(' ', parameter2); + if (value2 == int(value2)) + SERIAL_ECHO(i16tostr3left(value2)); // Integer value + else + SERIAL_ECHO(p_float_t(value2, 4)); // Float with 4 digits + } + SERIAL_EOL(); +#endif + + break; + + case 'T': Gcode_type = GCODE_TYPE_T; + break; + + default: return HAL_ERROR; // UNKNOWN GCODE TYPE + } + + HAL_StatusTypeDef status = HAL_OK; + uint32_t TxMailbox; // Stores which Mailbox (0-2) is used to store the Sent TX message + + if (parameter1 > 31) + parameter1 -= 64; // Format 'A' = 1, 'B' = 2, etc. + + if (parameter2 > 31) + parameter2 -= 64; // Format 'A' = 1, 'B' = 2, etc. + + TxHeader.IDE = CAN_EXTENDED_ID; + TxHeader.DLC = 4 * (!!parameter1 + !!parameter2); // Amount of bytes to send (4 or 8) + TxHeader.StdId = (TxHeader.StdId ^ STDID_FIFO_BIT); // Toggle FIFO bit 10, keep FIFO toggling in sync + TxHeader.ExtId = ((TxHeader.ExtId ^ EXTID_FIFO_BIT) & EXTID_FIFO_BIT) + // Toggle FIFO bit 28 + ((TxHeader.DLC >> 2) << PARAMETER_COUNT_OFFSET) + // Data bytes (4 or 8) + (Gcode_type << GCODE_TYPE_OFFSET) + // G/M/T/D-code + ((Gcode_no & GCODE_NUMBER_MASK) << GCODE_NUMBER_OFFSET) + // Gcode number + ((parameter2 & PARAMETER_MASK) << PARAMETER2_OFFSET) + // First parameter + ((parameter1 & PARAMETER_MASK) << PARAMETER1_OFFSET); // Second parameter + uint8_t CAN_tx_buffer[8]; // 8 bytes CAN data TX buffer + float * fp = (float *)CAN_tx_buffer; // Point to TX buffer + *fp++ = value1; + *fp-- = value2; + + uint32_t ms = millis(); // Don't send too fast! + while ((HAL_CAN_GetTxMailboxesFreeLevel(&hcan1) == 0) && (millis() - ms < 25)) { } // BLOCKING! Wait max 25ms! + // SERIAL_ECHOLNPGM(">>> Waited1: ", millis() - ms, " FreeTX: ", HAL_CAN_GetTxMailboxesFreeLevel(&hcan1)); // IRON, DEBUGGING + status = HAL_CAN_AddTxMessage(&hcan1, &TxHeader, CAN_tx_buffer, &TxMailbox); // Send message + + if (status == HAL_OK) // Count sent gcode messages + gcode_counter++; + + return status; +} + +HAL_StatusTypeDef CAN_Send_Setup() // Send setup to HEAD +{ + CAN_head_setup_request = false; + SERIAL_ECHOLNPGM(">>> CAN: SENDING CONFIG TO HEAD ====="); +// NOTE: Sending many command too fast will cause a Marlin command buffer overrun at the head, add delays if needed +// CAN_Send_Gcode_2params('M', 104, 'S', 0, 0, 0); // M104 S0, switch off hotend heating, NOT NEEDED ANYMORE +// CAN_Send_Gcode_2params('M', 107, 0, 0, 0, 0); // M107, switch off cooling fan, NOT NEEDED ANYMORE +// CAN_Send_Gcode_2params('M', 18, 0, 0, 0, 0); // M18, switch off steppers, NOT NEEDED ANYMORE + + // M306 MPC settings (managed by host) + MPC_t &mpc = thermalManager.temp_hotend[0].mpc; + + CAN_Send_Gcode_2params('M', 306, 'A', mpc.ambient_xfer_coeff_fan0, 'C', mpc.block_heat_capacity); // M306 R A + CAN_Send_Gcode_2params('M', 306, 'F', mpc.fanCoefficient(), 'H', mpc.filament_heat_capacity_permm); // M306 F H + CAN_Send_Gcode_2params('M', 306, 'P', mpc.heater_power, 'R', mpc.sensor_responsiveness); // M306 P C + +// CAN_Send_Gcode_2params('M', 150, 0, 0, 0, 0); // M150, SWITCH NEOPIXEL OFF + +/* + extern Planner planner; // M92 Steps per mm + CAN_Send_Gcode_2params('M', 92, 'X', planner.settings.axis_steps_per_mm[X_AXIS], 'Y', planner.settings.axis_steps_per_mm[Y_AXIS]); + CAN_Send_Gcode_2params('M', 92, 'Z', planner.settings.axis_steps_per_mm[Z_AXIS], 'E', planner.settings.axis_steps_per_mm[E_AXIS]); + + // M200 Set filament diameter + CAN_Send_Gcode_2params('M', 200, 'S', parser.volumetric_enabled, 'D', LINEAR_UNIT(planner.filament_size[0])); + #if ENABLED(VOLUMETRIC_EXTRUDER_LIMIT) + CAN_Send_Gcode_2params('M', 200, 'L', LINEAR_UNIT(planner.volumetric_extruder_limit[0]) + #endif + + // M201 Max acceleration + CAN_Send_Gcode_2params('M', 201, 'X', planner.settings.max_acceleration_mm_per_s2[X_AXIS], 'Y', planner.settings.max_acceleration_mm_per_s2[Y_AXIS]); + CAN_Send_Gcode_2params('M', 201, 'Z', planner.settings.max_acceleration_mm_per_s2[Z_AXIS], 'E', planner.settings.max_acceleration_mm_per_s2[E_AXIS]); + + // M203 Max feedrate + CAN_Send_Gcode_2params('M', 203, 'X', planner.settings.max_feedrate_mm_s[X_AXIS], 'Y', planner.settings.max_feedrate_mm_s[Y_AXIS]); + CAN_Send_Gcode_2params('M', 203, 'Z', planner.settings.max_feedrate_mm_s[Z_AXIS], 'E', planner.settings.max_feedrate_mm_s[E_AXIS]); + + // M204 Accelerations in units/sec^2, ENABLED BECAUSE IT INFORMS THE HEAD THE CONFIGURATION WAS SENT + CAN_Send_Gcode_2params('M', 204, 'P', planner.settings.acceleration, 'R', planner.settings.retract_acceleration); + CAN_Send_Gcode_2params('M', 204, 'T', planner.settings.travel_acceleration, 0, 0); + + // M205 + #ifdef CLASSIC_JERK + CAN_Send_Gcode_2params('M', 205,'S')) planner.settings.min_feedrate_mm_s, 'T')) planner.settings.min_travel_feedrate_mm_s); + CAN_Send_Gcode_2params('M', 205, M205_MIN_SEG_TIME_PARAM, planner.settings.min_segment_time_us, 'J', planner.junction_deviation_mm); + CAN_Send_Gcode_2params('M', 205, 'X', LINEAR_UNIT(planner.max_jerk.x), 'Y', LINEAR_UNIT(planner.max_jerk.y)); + CAN_Send_Gcode_2params('M', 205, 'Z', LINEAR_UNIT(planner.max_jerk.z), 'E', LINEAR_UNIT(planner.max_jerk.e)); + CAN_Send_Gcode_2params('M', 205, 'J', LINEAR_UNIT(planner.junction_deviation_mm), 0, 0); + #endif + + // M206 Home offset + #ifndef NO_HOME_OFFSETS + _CAN_Send_Gcode_2params('M', 206, 'X', LINEAR_UNIT(home_offset.x), 'Y', LINEAR_UNIT(home_offset.y)); + CAN_Send_Gcode_2params('M', 206, 'Z', LINEAR_UNIT(home_offset.z), 0, 0); + #endif + + // M207 Set Firmware Retraction + // M208 - Firmware Recover + // M209 - Set Auto Retract + + // M220 Speed/feedrate + CAN_Send_Gcode_2params('M', 220, 'S', feedrate_percentage, 0, 0); + + // M221 Flow percentage + CAN_Send_Gcode_2params('M', 221, 'T', 0, 'S', planner.flow_percentage[0]); + // CAN_Send_Gcode_2params('M', 221, 'T', 1, 'S', planner.flow_percentage[1]); // For 2nd extruder + + // M302 Cold extrude settings + #if ENABLED(PREVENT_COLD_EXTRUSION) + CAN_Send_Gcode_2params('M', 302, 'P', '0' + thermalManager.allow_cold_extrude, 'S', thermalManager.extrude_min_temp); // P0 enable cold extrusion checking, P1 = disabled, S=Minimum temperature + #endif + + // M569 TMC Driver StealthChop/SpreadCycle + CAN_Send_Gcode_2params('M', 569, 'S', stepperE0.get_stored_stealthChop(), 'E', 0); // M569 S[0/1] E + + // M592 Nonlinear Extrusion Control + + // M916 TMC Motor current + CAN_Send_Gcode_2params('M', 906, 'E', stepperE0.getMilliamps(), 0, 0); + + // M919 TMC Chopper timing for E only + CAN_Send_Gcode_2params('M', 919, 'O', off, 'P' , Hysteresis End); + CAN_Send_Gcode_2params('M', 919, 'S', Hysteresis Start, 0, 0); +*/ +/* +CAN_Send_Gcode_2params('M', 710, 'E', 1, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P +CAN_Send_Gcode_2params('M', 710, 'E', 2, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P +CAN_Send_Gcode_2params('M', 710, 'E', 3, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P +CAN_Send_Gcode_2params('M', 710, 'E', 4, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P +CAN_Send_Gcode_2params('M', 710, 'E', 5, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P +CAN_Send_Gcode_2params('M', 710, 'E', 6, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P +CAN_Send_Gcode_2params('M', 710, 'E', 7, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P +CAN_Send_Gcode_2params('M', 710, 'E', 8, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P +CAN_Send_Gcode_2params('M', 710, 'E', 9, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P +CAN_Send_Gcode_2params('M', 710, 'E', 10, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P +CAN_Send_Gcode_2params('M', 710, 'E', 11, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P +*/ + // IRON, M710 SIGNALS TO THE HEAD THAT THE CAN CONFIGURATION IS COMPLETE, USE IT AS THE LAST GCODE TO SEND + return CAN_Send_Gcode_2params('M', 710, 'E', int(controllerFan.settings.extruder_auto_fan_speed), 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P +} + +void CAN_Idle() // Tasks that cannot be done in the ISR +{ + if (CAN_head_setup_request) // The head requested the setup data + CAN_Send_Setup(); + + if (string_message_complete) // Received string message is complete + { + BUZZ(1, SOUND_OK); + SERIAL_ECHOPGM(">>> CAN MSG FROM HEAD: "); + for (uint32_t i = 0; i < string_message_index; i++) + SERIAL_CHAR(string_message[i]); // Show received string message, ends on '\n' + + string_message_complete = false; + string_message_index = 0; + } + + if ((hcan1.ErrorCode || CAN_head_error || HAL_CAN_error_code) && (millis() - Last_CAN_Error_Message > 6000)) + { + BUZZ(1, SOUND_ERROR); // Warn with sound + if (CAN_head_error) + SERIAL_ECHOLNPGM(">>> CAN ERROR REPORTED BY HEAD"); + CAN_head_error = false; // Reset, but will be repeated by the head + + if (HAL_CAN_error_code) + SERIAL_ECHOLNPGM(">>> HAL CAN ERROR REPORTED: ", HAL_CAN_error_code); + + if (hcan1.ErrorCode) + SERIAL_ECHOLNPGM(">>> hcan1.ErrorCode=", hcan1.ErrorCode); + + Last_CAN_Error_Message = millis(); + } + + if ((millis() - Last_CAN_Temp_Report) > 10000) // IRON, ERROR, NO TEMP UPDATE RECEIVED IN 10 SECONDS, KILL PRINTER + { + Last_CAN_Temp_Report = millis(); + if (FirstE0Error) // Send error notification + { + BUZZ(1, SOUND_ERROR); // Warn with sound + SERIAL_ECHOLNPGM("Error: NO CAN E0 TEMP UPDATES!"); + } + else // Send only error message + SERIAL_ECHOLNPGM(">>> CAN ERROR, NO E0 TEMP UPDATES!"); + + FirstE0Error = false; // Warn only once + + #ifndef IRON_DEBUG // Only kill if not debugging + kill(F("CAN ERROR, NO E0 TEMPERATURE UPDATES")); + #endif + } +} + +HAL_StatusTypeDef CAN_Send_Gcode() // Forward a Marlin Gcode via CAN (uses parser.command_letter, Gcode_no, parser.value_float()) +{ // Send a Gcode to the head with parameters and values + // Gcode starts with extended frame which can send the Gcode with max 2 parameters and values. + // Extended frames are send to complete all parameters and values (max 2 per extended message). + // 1. Analyze Gcode command + // 2. Ignore gcodes that do not need to be forwarded + // 3. Send parameters and values + // char s[] = "G0 X123.45678 Y124.45678 Z125.45678 E126.45678 F127.45678\n"; + + HAL_StatusTypeDef status = HAL_OK; + uint32_t TxMailbox; // Stores which Mailbox (0-2) is used to store the Sent TX message + + if (parser.command_letter != 'M') // Only forward Mxxx Gcode to head + return HAL_OK; + + uint32_t Gcode_type = GCODE_TYPE_M; // M-code, fixed for now + uint32_t Gcode_no = parser.codenum; + + if (Gcode_no == 109) // Convert M109(Hotend wait) to M104 (no wait) to keep the head responsive + Gcode_no = 104; + + if ((Gcode_no == 501) || (Gcode_no == 502)) // M501=Restore settings, M502=Factory defaults + CAN_head_setup_request = true; // Also update settings for the head + + if ((Gcode_no != 104) && // Set hotend target temp + (Gcode_no != 106) && // Set cooling fan speed + (Gcode_no != 107) && // Cooling fan off + (Gcode_no != 150) && // Set NeoPixel values +// (Gcode_no != 108) && // Break and Continue + (Gcode_no != 280) && // Servo position + (Gcode_no != 306) && // MPC settings/tuning + (Gcode_no != 710) && // Control fan PWM + (Gcode_no != 997)) // Reboot + return HAL_OK; // Nothing to do + + uint32_t index; + uint32_t parameter_counter = 0; + char letters[] = "XYZEFPSABCHIJKLOQRTUVW"; // All possible parameters (22), defines scan order, no "D G M N", includes 'T' for autotune (M306 T) + static uint32_t parameters[8] = { 0 }; // Store found parameters, send max 7 parameters (send in pairs, so reserve 8), CodeA=1 (ASCII65), CodeE=5, CodeF=6, CodeX=88-64=24, CodeY=89-64=25, CodeZ=90-64=26 + static float values[8] = { 0 }; // Store found values, send max 7 parameters (send in pairs, so reserve 8) + + uint8_t CAN_tx_buffer[8]; // 8 bytes CAN data TX buffer + + /* + switch (parser.command_letter) // Filter/adjust Gcodes + { + case 'G': Gcode_type = GCODE_TYPE_G; + switch (Gcode_no) + { + case 12: break; // No Nozzle cleaning support needed on head + case 29: case 34: return HAL_OK; // No bedleveling/Z-syncing on head + break; + } + break; + + case 'M': Gcode_type = GCODE_TYPE_M; + switch (Gcode_no) + { // Save Prog mem: M112, M48, M85, M105, M114, M155, M500, M501, M502, M503, M226, M422 + case 109: Gcode_no = 104; break; // Replace M109 with M104 + case 112: Gcode_no = 104; break; // Don't shutdown board, should stop heating with "M104" + + case 20: case 21: case 22: case 23: case 24: case 25: case 26: case 524: case 540: case 928: + case 27: case 28: case 29: case 30: case 32: case 33: case 34: // No SD file commands + case 43: // No pin debug + case 48: // No repeatability test + case 85: // No inactivity shutdown + case 100: // No show free memory support + case 108: // Break and Continue + case 105: // No temperature reporting + case 114: // Don't report position + case 117: case 118: case 119: // Don't send strings + case 140: case 190: // Ignore bed temp commands + case 150: // Set NeoPixel values + case 154: // No auto position reporting + case 155: // No tempeature reporting + case 226: // Wait for pin state + case 240: // No camera support + case 250: // No LCD contrast support + case 260: case 261: // No I2C on head + case 280: // Don't send servo angle, done via Servo.cpp already + case 290: // No baby stepping + case 300: // No tones + // case 303: // No PID autotune (done on HEAD) + case 304: // No bed PID settings + // case 306: // MPC autotune (done on HEAD) + case 350: case 351: // No live microstepping adjustment + case 380: case 381: // No solenoid support + case 401: case 402: // No probe deploy/stow, done via M280 servo angles + case 412: // Filament runout sensor done by MASTER + case 420: case 421: // No bed leveling state + case 423: // No X Twist Compensation + case 425: // No backlash compensation + case 500: case 501: case 502: case 503: case 504: case 910: // No EEPROM on head, remove M50x commands to save Prog mem + case 510: case 511: case 512: // No locking of the machine + case 605: // No IDEX commands + case 810: case 811: case 812: case 813: case 814: case 815: case 816: case 817: case 818: case 819: + case 851: // + case 871: // No Probe temp config + case 876: // No handle prompt response + case 913: // No Set Hybrid Threshold Speed + case 914: // No TMC Bump Sensitivity + case 997: // No remote reset + case 998: // No ESP3D reset + return HAL_OK; // NO CAM MESSAGE + } + break; + + case 'T': Gcode_type = GCODE_TYPE_T; + switch (Gcode_no) + { + case 0: case 1: + break; + } + break; + + case 'D': Gcode_type = GCODE_TYPE_D; + switch (Gcode_no) + { + case 0: case 1: + break; + } + break; + default: return HAL_OK; // Invalid command, nothing to do + } +*/ + + #ifdef CAN_DEBUG + SERIAL_ECHOPGM(">>> CAN GCODE TO HEAD: "); // IRON, DEBUGGING + SERIAL_CHAR(parser.command_letter); + SERIAL_ECHO(Gcode_no); // IRON, DEBUGGING + #endif + + if (strlen(parser.command_ptr) > 4) // "M107\0", ONLY SCAN FOR PARAMETERS IF STRING IS LONG ENOUGH + for (index = 0; index < sizeof(letters); index++) // Scan parameters + { + if (parser.seen(letters[index])) + { + parameters[parameter_counter] = letters[index] - 64; // Store parameter letter, A=1, B=2... + + #ifdef CAN_DEBUG + SERIAL_CHAR(' ', letters[index]); // IRON, DEBUGGING + #endif + + if (parser.has_value()) // Check if there is a value + { + values[parameter_counter++] = parser.value_float(); + + #ifdef CAN_DEBUG + if (values[parameter_counter - 1] == int(values[parameter_counter - 1])) + SERIAL_ECHO(i16tostr3left(values[parameter_counter - 1])); // Integer value + else + SERIAL_ECHO(p_float_t(values[parameter_counter - 1], 4)); // Float with 4 digits + #endif + + } + else // No value for parameter + values[parameter_counter++] = NAN; // Not A Number, indicates no parameter value is present + } + + if (parameter_counter == 8) + { + parameter_counter--; // Max is 7 parameters + SERIAL_ECHOPGM("\nError: TOO MANY PARAMETERS (> 7): "); + SERIAL_ECHOLN_P(parser.command_ptr); + BUZZ(1, SOUND_ERROR); + break; + } + } + #ifdef CAN_DEBUG + SERIAL_EOL(); + #endif + + parameters[parameter_counter] = 0; // Set next parameter to 0 (0=no parameter), send in pairs + index = 0; + float * fp = (float *)CAN_tx_buffer; // Points to TX buffer + + if ((Gcode_no == 710) && (parameters[0] == 3)) // "M710 C" INDICATES REQUEST FOR GCODE COUNT + SERIAL_ECHOLNPGM(">>> GCODES SENT: ", gcode_counter); + + gcode_counter++; + + TxHeader.IDE = CAN_EXTENDED_ID; // Start with EXTENDED_ID then send STANDARD_ID if needed + //TxHeader.ExtId &= EXTID_FIFO_BIT; // Clear ID, keep FIFO bit + TxHeader.ExtId = (TxHeader.ExtId & EXTID_FIFO_BIT) + // KEEP FIFO BIT + ((parameter_counter & PARAMETER_COUNT_MASK) << PARAMETER_COUNT_OFFSET) + // Parameter count + ((Gcode_type & GCODE_TYPE_MASK) << GCODE_TYPE_OFFSET) + // GCODE TYPE (G/M/T/D) + ((Gcode_no & GCODE_NUMBER_MASK) << GCODE_NUMBER_OFFSET) + // GCODE NUMBER + ((parameters[1] & PARAMETER_MASK) << PARAMETER2_OFFSET) + // PARAMETER2 + ((parameters[0] & PARAMETER_MASK) << PARAMETER1_OFFSET); // PARAMETER1 + uint32_t ms = millis(); // Record message send start time + do + { + TxHeader.DLC = MIN(8, (parameter_counter - index) << 2); // Maximum 8 bytes, 4 bytes if there is only 1 parameter, 8 bytes if there are 2 + //TxHeader.StdId = (TxHeader.StdId ^ STDID_FIFO_BIT) & STDID_FIFO_BIT; // Toggle FIFO bit 10, clear other bits + TxHeader.StdId = ((TxHeader.StdId ^ STDID_FIFO_BIT) & STDID_FIFO_BIT) + // Toggle FIFO bit + ((parameters[index + 1] & PARAMETER_MASK) << PARAMETER2_OFFSET) + // Parameter 2 + ((parameters[index ] & PARAMETER_MASK) << PARAMETER1_OFFSET); // Parameter 1 + TxHeader.ExtId ^= EXTID_FIFO_BIT; // Toggle FIFO bit 28 + *fp++ = values[index++]; // Copy first parameter value to data, move pointer to next 4 bytes + *fp-- = values[index++]; // Copy 2nd parameter value to data, move pointer to beginning of data array for next round + + while ((HAL_CAN_GetTxMailboxesFreeLevel(&hcan1) == 0) && (millis() - ms < 50)) { } // BLOCKING! Wait max 50ms + status = HAL_CAN_AddTxMessage(&hcan1, &TxHeader, CAN_tx_buffer, &TxMailbox); // Send message + + if (status != HAL_OK) + return status; + + TxHeader.IDE = CAN_STANDARD_ID; // All following messages have standard ID for parameter values, 11 bits identifier + } while (index < parameter_counter); + + return HAL_OK; +} + +void CAN_Send_Position() // Send the X, Y, Z and E position to the HEAD +{ + CAN_Send_Gcode_2params('G', 92, 'X', current_position.x, 'Y', current_position.y); // M92 X Y + CAN_Send_Gcode_2params('G', 92, 'Z', current_position.z, 'E', current_position.e); // M92 E Z +} + +void HAL_CAN_MspInit(CAN_HandleTypeDef* canHandle) // Called by HAL_CAN_Init +{ + GPIO_InitTypeDef GPIO_InitStruct = {0}; + RCC_PeriphCLKInitTypeDef PeriphClkInit = {0}; + + if (canHandle->Instance == CAN1) + { + RCC_PeriphCLKInitTypeDef periphClkInit = { }; + HAL_RCCEx_GetPeriphCLKConfig(&periphClkInit); + periphClkInit.PeriphClockSelection |= RCC_APB1ENR_CAN1EN; // DONE IN __HAL_RCC_CAN1_CLK_ENABLE? + + if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) + Error_Handler(); + + // CAN1 clock enable + if (__HAL_RCC_CAN1_IS_CLK_DISABLED()); // Enable CAN1 clock + __HAL_RCC_CAN1_CLK_ENABLE(); // Enable CAN1 clock + + if (__HAL_RCC_GPIOB_IS_CLK_DISABLED()) // Should be enabled by Marlin already + __HAL_RCC_GPIOB_CLK_ENABLE(); // Enable GPIO B clock + // CAN1 GPIO Configuration + // PB8 ------> CAN1_RX + // PB9 ------> CAN1_TX + + GPIO_InitStruct.Pin = GPIO_PIN_8 | GPIO_PIN_9; // Pin PB8 and Pin PB9 + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; + GPIO_InitStruct.Alternate = GPIO_AF9_CAN1; // Alternate function 9 + HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); // Port B + + // CAN1 interrupt Init + HAL_NVIC_SetPriority(CAN1_RX0_IRQn, 1, 1); + HAL_NVIC_EnableIRQ(CAN1_RX0_IRQn); // Enable CAN FIFO1 interrupt handler + + HAL_NVIC_SetPriority(CAN1_RX1_IRQn, 1, 1); + HAL_NVIC_EnableIRQ(CAN1_RX1_IRQn); // Enable CAN FIFO1 interrupt handler + } +} + +HAL_StatusTypeDef CAN1_Stop(void) +{ + return HAL_CAN_Stop(&hcan1); +} + +HAL_StatusTypeDef CAN1_Start(void) +{ + HAL_StatusTypeDef status = HAL_OK; + + // Init TxHeader with constant values + TxHeader.ExtId = 0; + TxHeader.StdId = 0; + TxHeader.RTR = CAN_RTR_DATA; // Data transmission type: CAN_RTR_DATA / CAN_RTR_REMOTE + TxHeader.TransmitGlobalTime = DISABLE; // Put timestamp in Data[6-7], requires Time Triggered Communication Mode + + // CAN baud rate = clock frequency / clock divider / prescaler / (1 + TSG1 + TSG2) + // Baud rate = 42M / 3 / 1 / (1 + 11 + 2) = 1M baud + // Baud rate = 42M / 3 / 2 / (1 + 11 + 2) = 500k baud + // Baud rate = 42M / 3 / 4 / (1 + 11 + 2) = 250k baud + hcan1.Instance = CAN1; + hcan1.Init.Prescaler = 3; // 1-1024, 42MHz peripheral clock / 3 --> 14MHz -> 1M baud. 6 --> 500K baud. 12 --> 250K baud. + hcan1.Init.AutoBusOff = DISABLE; // DISABLE: Software controlled Bus-off. ENABLE: Automatic hardware controlled (no send/receive) + hcan1.Init.AutoWakeUp = ENABLE; // ENABLE: Automatic hardware controlled bus wakeup. DISABLE: Software controlled bus wakeup. + hcan1.Init.AutoRetransmission = ENABLE; // DISABLE / ENABLE, resend if transmission failed, but locks up if communication fails/cable not connected!!!!!!!!!!!!!!!!! + hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ; // CAN_SJW_1TQ + hcan1.Init.TimeSeg1 = CAN_BS1_11TQ; // CAN_BS1_11TQ + hcan1.Init.TimeSeg2 = CAN_BS2_2TQ; // CAN_BS2_2TQ + hcan1.Init.Mode = CAN_MODE_NORMAL; // CAN_MODE_NORMAL / CAN_MODE_SILENT / CAN_MODE_LOOPBACK / CAN_MODE_SILENT_LOOPBACK + hcan1.Init.TimeTriggeredMode = DISABLE; // TTCAN is used to assign timeslot to the devices for real time applications + hcan1.Init.ReceiveFifoLocked = DISABLE; // Handle RX FIFO overruns. DISABLE: Overwrite previous message with new one. ENABLE: Discard the new message. + hcan1.Init.TransmitFifoPriority = ENABLE; // Handle TX FIFO send order. ENABLE: Chronologically. DISABLE: Transmit lower ID number first. + + status = HAL_CAN_Init(&hcan1); // Calls HAL_CAN_MspInit + if (status != HAL_OK) + return status; + + CAN_FilterTypeDef sFilterConfig; + + // Catch CAN messags with highest bit of StdId set in FIFO0 + sFilterConfig.FilterBank = 0; // This filter bank ID number (0-13 for single CAN instances) + sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; // Accept if "Received ID" & Mask = ID, CAN_FILTERMODE_IDMASK / CAN_FILTERMODE_IDLIST (See Figure 342 in RM0090) + sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; // CAN_FILTERSCALE_16BIT / CAN_FILTERSCALE_32BIT (See Figure 342 in RM0090) + sFilterConfig.FilterIdHigh = 0b1000000000000000; // ID MSB: (0-0xFFFF) (StdId[10-0] [ExtId17-13]) (See Figure 342 in RM0090) + sFilterConfig.FilterIdLow = 0; // ID LSB: (0-0xFFFF) ([ExtId12-0][IDE][RTR] 0) (0="don't care") + sFilterConfig.FilterMaskIdHigh = 0b1000000000000000; // Mask MSB: (0-0xFFFF) (StdId[10-0] [ExtId17-13]) (See Figure 342 in RM0090) + sFilterConfig.FilterMaskIdLow = 0; // Mask LSB: (0-0xFFFF) ([ExtId12-0][IDE][RTR] 0) (0="don't care") + sFilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO0; // Store message in FIFO1 (CAN_FILTER_FIFO0 / CAN_FILTER_FIFO1) + sFilterConfig.FilterActivation = CAN_FILTER_ENABLE; // CAN_FILTER_ENABLE / CAN_FILTER_DISABLE + sFilterConfig.SlaveStartFilterBank = 14; // Start bank number for CAN slave instance (not used in single CAN setups) + HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig); + + // Catch all remaining CAN messages in FIFO1 + sFilterConfig.FilterBank = 1; // This filter bank ID number (0-13 for single CAN instances) + sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; // CAN_FILTERMODE_IDMASK / CAN_FILTERMODE_IDLIST (See Figure 342 in RM0090) + sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; // CAN_FILTERSCALE_16BIT / CAN_FILTERSCALE_32BIT (See Figure 342 in RM0090) + sFilterConfig.FilterIdHigh = 0b0000; // ID MSB: (0-0xFFFF) (StdId[10-0] [ExtId17-13]) (See Figure 342 in RM0090) + sFilterConfig.FilterIdLow = 0x0000; // ID LSB: (0-0xFFFF) ([ExtId12-0][IDE][RTR] 0) (0="don't care") + sFilterConfig.FilterMaskIdHigh = 0b0000; // Mask MSB: (0-0xFFFF) (StdId[10-0] [ExtId17-13]) (See Figure 342 in RM0090) + sFilterConfig.FilterMaskIdLow = 0x0000; // Mask LSB: (0-0xFFFF) ([ExtId12-0][IDE][RTR] 0) (0="don't care") + sFilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO1; // Store message in FIFO0 (CAN_FILTER_FIFO0 / CAN_FILTER_FIFO1) + sFilterConfig.FilterActivation = CAN_FILTER_ENABLE; // CAN_FILTER_ENABLE / CAN_FILTER_DISABLE + sFilterConfig.SlaveStartFilterBank = 14; // Start bank number for CAN slave instance (not used in single CAN setups) + HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig); + + // Activate RX FIFO0/FIFO1 new message interrupt + HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING); // Calls CAN1_RX0_IRQHandler -> HAL_CAN_IRQHandler -> HAL_CAN_RxFifo0MsgPendingCallback + HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO1_MSG_PENDING); // Calls CAN1_RX1_IRQHandler -> HAL_CAN_IRQHandler -> HAL_CAN_RxFifo1MsgPendingCallback + +// Activate RX FIFO0/FIFO1 overrun interrupt + HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_OVERRUN); // Calls CAN1_RX0_IRQHandler -> HAL_CAN_ErrorCallback + HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO1_OVERRUN); // Calls CAN1_RX1_IRQHandler -> HAL_CAN_ErrorCallback + + HAL_CAN_ActivateNotification(&hcan1, CAN_IT_ERROR); // Calls CAN1_RX0_IRQHandler -> HAL_CAN_ErrorCallback + + status = HAL_CAN_Start(&hcan1); // Start the CAN1 module + if (status != HAL_OK) + return status; + + return CAN_Send_Gcode_2params('M', 997, 0, 0, 0, 0); // M997, reset head at host startup +} + +void HAL_CAN_RxFifoMsgPending(CAN_HandleTypeDef *hcan, uint32_t RxFifo) // ISR! New FIFO 0/1 message interrupt handler +{ + CAN_RxHeaderTypeDef RxHeader; // RX Header buffer for FIFO0 + uint8_t CAN_RX_buffer_Fifo[8]; // CAN MESSAGE DATA BUFFER + + if (HAL_CAN_GetRxMessage(&hcan1, RxFifo, &RxHeader, CAN_RX_buffer_Fifo) == HAL_OK) // Get message from CAN_RX_FIFO0 + { + if ((RxHeader.StdId & CAN_IO_MASK) != CAN_io_state) // First handle time critical IO update + { + CAN_io_state = (RxHeader.StdId & CAN_IO_MASK); + endstops.update(); + } + + if (RxHeader.StdId & CAN_STRING_MESSAGE_MASK) // Head sends a string message + { + char * CAN_RX_p = (char *)CAN_RX_buffer_Fifo; + for (uint32_t i = 0; i < RxHeader.DLC; i++) + { + string_message[string_message_index++ % 128] = CAN_RX_p[i]; // Copy message to global buffer + + if (CAN_RX_p[i] == '\n') + { + string_message_complete = true; // Print buffer + string_message[string_message_index % 128] = 0; // Close string with \0 + } + } + } + else + if (RxHeader.DLC == 4) // FIFO0, head sent a temperature update (DLC = Data Length Code == 4 bytes) + { + float * fp = (float *)CAN_RX_buffer_Fifo; // FIFO0 + thermalManager.temp_hotend[0].celsius = *fp; // Set E0 hotend temperature + Last_CAN_Temp_Report = millis(); + FirstE0Error = true; // Reset error status + } + + CAN_head_setup_request = (RxHeader.StdId & CAN_REQUEST_SETUP_MASK) > 0; // FIFO0, head signals request for data + CAN_head_error = (RxHeader.StdId & CAN_ERROR_MASK) > 0; // FIFO0, head signals an error + } +} + +void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) // ISR! New FIFO0 message interrupt handler +{ + HAL_CAN_RxFifoMsgPending(hcan, CAN_RX_FIFO0); +} + +void HAL_CAN_RxFifo1MsgPendingCallback(CAN_HandleTypeDef *hcan) // ISR! New FIFO1 message interrupt handler +{ + HAL_CAN_RxFifoMsgPending(hcan, CAN_RX_FIFO1); +} + +void HAL_CAN_ErrorCallback(CAN_HandleTypeDef *hcan) // Interrupt handler for any CAN error +{ + HAL_CAN_error_code = hcan->ErrorCode; // Store the received error code +} + +/* +CAN Bus Control functions + HAL_StatusTypeDef HAL_CAN_Start(CAN_HandleTypeDef *hcan) Start the CAN module + HAL_StatusTypeDef HAL_CAN_Stop(CAN_HandleTypeDef *hcan) Stop the CAN module + HAL_StatusTypeDef HAL_CAN_RequestSleep(CAN_HandleTypeDef *hcan) Request sleep mode entry. + HAL_StatusTypeDef HAL_CAN_WakeUp(CAN_HandleTypeDef *hcan) Wake up from sleep mode. + uint32_t HAL_CAN_IsSleepActive(CAN_HandleTypeDef *hcan) Check is sleep mode is active. + HAL_StatusTypeDef HAL_CAN_AddTxMessage(CAN_HandleTypeDef *hcan, CAN_TxHeaderTypeDef *pHeader, uint8_t aData[], uint32_t *pTxMailbox) Add a message to the Tx mailboxes and activate the corresponding transmission request + HAL_StatusTypeDef HAL_CAN_AbortTxRequest(CAN_HandleTypeDef *hcan, uint32_t TxMailboxes) Abort transmission request + uint32_t HAL_CAN_GetTxMailboxesFreeLevel(CAN_HandleTypeDef *hcan) Return Tx mailboxes free level + uint32_t HAL_CAN_IsTxMessagePending(CAN_HandleTypeDef *hcan, uint32_t TxMailboxes);Check if a transmission request is pending on the selected Tx mailbox + uint32_t HAL_CAN_GetTxTimestamp(CAN_HandleTypeDef *hcan, uint32_t TxMailbox) Return Tx Timestamp + HAL_StatusTypeDef HAL_CAN_GetRxMessage(CAN_HandleTypeDef *hcan, uint32_t RxFifo, CAN_RxHeaderTypeDef *pHeader, uint8_t aData[]) Get a CAN frame from the Rx FIFO + uint32_t HAL_CAN_GetRxFifoFillLevel(CAN_HandleTypeDef *hcan, uint32_t RxFifo) Return Rx FIFO fill level + +CAN INTERRUPT FUNCTIONS (See STM32F4xx_hal_can.c) + HAL_StatusTypeDef HAL_CAN_ActivateNotification(CAN_HandleTypeDef *hcan, uint32_t ActiveITs) // Enable interrupts + HAL_StatusTypeDef HAL_CAN_DeactivateNotification(CAN_HandleTypeDef *hcan, uint32_t InactiveITs) // Disable interrupts + +CAN WEAK CALLBACKS WHEN USING STANDARD WEAK CAN1_RX0_IRQHandler------------> INTERRUPTS + __weak void HAL_CAN_TxMailbox0CompleteCallback(CAN_HandleTypeDef *hcan) // CAN_IT_TX_MAILBOX_EMPTY + __weak void HAL_CAN_TxMailbox1CompleteCallback(CAN_HandleTypeDef *hcan) // CAN_IT_TX_MAILBOX_EMPTY + __weak void HAL_CAN_TxMailbox2CompleteCallback(CAN_HandleTypeDef *hcan) // CAN_IT_TX_MAILBOX_EMPTY + __weak void HAL_CAN_TxMailbox0AbortCallback(CAN_HandleTypeDef *hcan) // CAN_IT_TX_MAILBOX_EMPTY + __weak void HAL_CAN_TxMailbox1AbortCallback(CAN_HandleTypeDef *hcan) // CAN_IT_TX_MAILBOX_EMPTY + __weak void HAL_CAN_TxMailbox2AbortCallback(CAN_HandleTypeDef *hcan) // CAN_IT_TX_MAILBOX_EMPTY + __weak void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) // CAN_IT_RX_FIFO0_MSG_PENDING + __weak void HAL_CAN_RxFifo0FullCallback(CAN_HandleTypeDef *hcan) // CAN_IT_RX_FIFO0_FULL + __weak void HAL_CAN_RxFifo1MsgPendingCallback(CAN_HandleTypeDef *hcan) // CAN_IT_RX_FIFO1_MSG_PENDING + __weak void HAL_CAN_RxFifo1FullCallback(CAN_HandleTypeDef *hcan) // CAN_IT_RX_FIFO1_FULL + __weak void HAL_CAN_SleepCallback(CAN_HandleTypeDef *hcan) // CAN_IT_SLEEP_ACK + __weak void HAL_CAN_WakeUpFromRxMsgCallback(CAN_HandleTypeDef *hcan) // CAN_IT_WAKEUP + __weak void HAL_CAN_ErrorCallback(CAN_HandleTypeDef *hcan) // CAN_IT_ERROR +*/ + +#endif // CAN_MASTER \ No newline at end of file diff --git a/Marlin/src/HAL/STM32/CAN.h b/Marlin/src/HAL/STM32/CAN.h new file mode 100644 index 000000000000..32dc76a2ec21 --- /dev/null +++ b/Marlin/src/HAL/STM32/CAN.h @@ -0,0 +1,28 @@ +#pragma once + +#include "../../inc/MarlinConfig.h" + +#ifdef CAN_MASTER + + 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_E0_TARGET_MASK 256 // Signals E0 or E1 + #define CAN_ERROR_MASK 512 // Signals the head encountered an error + + HAL_StatusTypeDef CAN1_Start(void); // FUNCTION PROTOTYPES + HAL_StatusTypeDef CAN1_Stop(void); + HAL_StatusTypeDef CAN_Send_Gcode(void); + 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 + +#endif // CAN_MASTER \ No newline at end of file diff --git a/Marlin/src/HAL/STM32/Servo.cpp b/Marlin/src/HAL/STM32/Servo.cpp index 4f026ffc6df4..dc6888ff9d11 100644 --- a/Marlin/src/HAL/STM32/Servo.cpp +++ b/Marlin/src/HAL/STM32/Servo.cpp @@ -71,6 +71,21 @@ int8_t libServo::attach(const int pin, const int min, const int max) { } void libServo::move(const int value) { + +#ifdef CAN_MASTER // IRON, FORWARD UNDERWATER SERVO COMMAND TO HEAD + int angles[2] = Z_SERVO_ANGLES; + + // Translate M280 S10 to M401, M280 S90 to M402 + HAL_StatusTypeDef CAN_Send_Gcode_2params(uint32_t Gcode_type, uint32_t Gcode_no, uint32_t parameter1, float value1, uint32_t parameter2, float value2); // PROTOTYPE + if (value == angles[0]) // Deploy angle + CAN_Send_Gcode_2params('M', 401, 0, 0, 0, 0); // IRON, send "M401" instead, enables interrupt etc. + else + if (value == angles[1]) // Stow angle + CAN_Send_Gcode_2params('M', 402, 0, 0, 0, 0); // IRON, send "M401" instead, enables interrupt etc. + else + CAN_Send_Gcode_2params('M', 280, 'S', value, 'P', 0); // M280 S[value] P0 +#endif // IRON + if (attach(0) >= 0) { stm32_servo.write(value); safe_delay(delay); diff --git a/Marlin/src/MarlinCore.cpp b/Marlin/src/MarlinCore.cpp index 140423f25c76..3b73fd8b5014 100644 --- a/Marlin/src/MarlinCore.cpp +++ b/Marlin/src/MarlinCore.cpp @@ -34,6 +34,10 @@ #include "HAL/shared/esp_wifi.h" #include "HAL/shared/cpu_exception/exception_hook.h" +#ifdef CAN_MASTER // IRON, ADDED + #include HAL_PATH(., CAN.h) +#endif + #if ENABLED(WIFISUPPORT) #include "HAL/shared/esp_wifi.h" #endif @@ -1190,7 +1194,14 @@ void setup() { while (!MYSERIAL3.connected() && PENDING(millis(), serial_connect_timeout)) { /*nada*/ } #endif #endif - SERIAL_ECHOLNPGM("start"); + SERIAL_ECHOLNPGM("\nstart\n"); // IRON, ADDED \n + +#ifdef CAN_MASTER // IRON, REPORT CAN START STATUS + if (CAN1_Start() == HAL_OK) + SERIAL_ECHOLNPGM(">>> CAN1 Start: OK"); + else + SERIAL_ECHOLNPGM(">>> CAN1 Start: FAILED!"); +#endif // Set up these pins early to prevent suicide #if HAS_KILL diff --git a/Marlin/src/feature/pause.cpp b/Marlin/src/feature/pause.cpp index 3589847761c5..3af6b59709e4 100644 --- a/Marlin/src/feature/pause.cpp +++ b/Marlin/src/feature/pause.cpp @@ -225,8 +225,13 @@ bool load_filament(const_float_t slow_load_length/*=0*/, const_float_t fast_load switch (active_extruder) { REPEAT_1(NUM_RUNOUT_SENSORS, _CASE_INSERTED) } + #else + #ifdef CAN_MASTER // IRON, USE VIRTUAL CAN FILAMENT RUNOUT STATUS + if ((!!(CAN_io_state & CAN_FILAMENT_MASK)) != FIL_RUNOUT_STATE) + wait_for_user = false; #else if (!FILAMENT_IS_OUT()) wait_for_user = false; + #endif // IRON #endif #endif idle_no_sleep(); diff --git a/Marlin/src/feature/runout.h b/Marlin/src/feature/runout.h index 52a9020830b7..8ecb94a21b7f 100644 --- a/Marlin/src/feature/runout.h +++ b/Marlin/src/feature/runout.h @@ -33,6 +33,10 @@ #include "pause.h" // for did_pause_print #include "../MarlinCore.h" // for printingIsActive() +#ifdef CAN_MASTER // IRON, ADDED + #include HAL_PATH(.., CAN.h) +#endif + #include "../inc/MarlinConfig.h" #if ENABLED(EXTENSIBLE_UI) @@ -207,7 +211,13 @@ class FilamentSensorBase { // Return a bitmask of runout pin states static uint8_t poll_runout_pins() { + +#ifdef CAN_MASTER // IRON, VIRTUAL FILAMENT RUNOUT PIN + #define _OR_RUNOUT(N) | ((CAN_io_state & CAN_FILAMENT_MASK) ? _BV((N) - 1) : 0) +#else #define _OR_RUNOUT(N) | (READ(FIL_RUNOUT##N##_PIN) ? _BV((N) - 1) : 0) +#endif // IRON + return (0 REPEAT_1(NUM_RUNOUT_SENSORS, _OR_RUNOUT)); #undef _OR_RUNOUT } diff --git a/Marlin/src/gcode/gcode.cpp b/Marlin/src/gcode/gcode.cpp index 9fed4dcada3f..53d84ac27cb4 100644 --- a/Marlin/src/gcode/gcode.cpp +++ b/Marlin/src/gcode/gcode.cpp @@ -28,6 +28,10 @@ #include "gcode.h" GcodeSuite gcode; +#ifdef CAN_MASTER // IRON + #include HAL_PATH(.., CAN.h) +#endif + #if ENABLED(WIFI_CUSTOM_COMMAND) extern bool wifi_custom_command(char * const command_ptr); #endif @@ -323,6 +327,17 @@ void GcodeSuite::process_parsed_command(const bool no_ok/*=false*/) { KEEPALIVE_STATE(IN_HANDLER); +#ifdef CAN_MASTER + if (CAN_Send_Gcode() != HAL_OK) // IRON, SEND COMMAND TO HEAD + { + SERIAL_ECHOPGM("Error: FAILED TO SEND GCODE CAN COMMAND TO HEAD: "); + SERIAL_ECHOLN_P(parser.command_ptr); + #ifndef IRON_DEBUG + BUZZ(1, SOUND_ERROR); + #endif + } +#endif // IRON + /** * 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 diff --git a/Marlin/src/gcode/temp/M306.cpp b/Marlin/src/gcode/temp/M306.cpp index 12e175420dc2..57ed2518ff9e 100644 --- a/Marlin/src/gcode/temp/M306.cpp +++ b/Marlin/src/gcode/temp/M306.cpp @@ -27,6 +27,7 @@ #include "../gcode.h" #include "../../lcd/marlinui.h" #include "../../module/temperature.h" +#include "../../libs/numtostr.h" // IRON, ADDED /** * M306: MPC settings and autotune @@ -57,6 +58,13 @@ void GcodeSuite::M306() { #if ENABLED(MPC_AUTOTUNE) if (parser.seen_test('T')) { + +#ifdef CAN_MASTER // IRON, MPC AUTOTUNE INFO + SERIAL_ECHOLNPGM(">>> Forwarding M306 to head board"); + SERIAL_ECHOLNPGM(">>> Store MPC setup in the host Configuration.h or use M500"); + SERIAL_ECHOLNPGM(">>> MPC heater power is: ", ftostr31ns(MPC_HEATER_POWER), " Watt"); + SERIAL_ECHOLNPGM(">>> Please wait for the auto tune results..."); +#else Temperature::MPCTuningType tuning_type; const uint8_t type = parser.byteval('S', 0); switch (type) { @@ -67,6 +75,7 @@ void GcodeSuite::M306() { LCD_MESSAGE(MSG_MPC_AUTOTUNE); thermalManager.MPC_autotune(e, tuning_type); ui.reset_status(); +#endif // IRON return; } #endif @@ -91,6 +100,12 @@ void GcodeSuite::M306_report(const bool forReplay/*=true*/) { TERN_(MARLIN_SMALL_BUILD, return); report_heading(forReplay, F("Model predictive control")); + +#ifdef CAN_MASTER // IRON, 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; diff --git a/Marlin/src/module/endstops.cpp b/Marlin/src/module/endstops.cpp index 85e021d661ea..5ce8f43b3551 100644 --- a/Marlin/src/module/endstops.cpp +++ b/Marlin/src/module/endstops.cpp @@ -78,7 +78,13 @@ Endstops::endstop_mask_t Endstops::live_state = 0; #define READ_ENDSTOP(P) READ(P) #endif #else + +#ifdef CAN_MASTER // IRON, READ VIRTUAL CAN IO PROBE STATUS IF NEEDED + #define READ_ENDSTOP(P) ((P == Z_MIN_PIN) ? ((CAN_io_state & CAN_PROBE_MASK)) : READ(P)) +#else #define READ_ENDSTOP(P) READ(P) +#endif // IRON, ADDED + #endif #if ENDSTOP_NOISE_THRESHOLD @@ -526,7 +532,13 @@ void __O2 Endstops::report_states() { } #undef _CASE_RUNOUT #elif HAS_FILAMENT_SENSOR + + #ifdef CAN_MASTER // IRON + print_es_state((!!(CAN_io_state & CAN_FILAMENT_MASK)) != FIL_RUNOUT1_STATE, F(STR_FILAMENT)); + #else print_es_state(READ(FIL_RUNOUT1_PIN) != FIL_RUNOUT1_STATE, F(STR_FILAMENT)); + #endif // IRON + #endif TERN_(BLTOUCH, bltouch._reset_SW_mode()); diff --git a/Marlin/src/module/probe.h b/Marlin/src/module/probe.h index 08a02b4d4004..e849a28324f5 100644 --- a/Marlin/src/module/probe.h +++ b/Marlin/src/module/probe.h @@ -29,6 +29,10 @@ #include "motion.h" +#ifdef CAN_MASTER // IRON + #include HAL_PATH(.., CAN.h) +#endif + #if ENABLED(BLTOUCH) #include "../feature/bltouch.h" #endif @@ -48,10 +52,23 @@ #if ENABLED(BD_SENSOR) #define PROBE_READ() bdp_state #elif USE_Z_MIN_PROBE - #define PROBE_READ() READ(Z_MIN_PROBE_PIN) + + #ifdef CAN_MASTER + #define PROBE_READ() ((CAN_io_state & CAN_PROBE_MASK)) // IRON, READ VIRTUAL IO + #else + #define PROBE_READ() READ(Z_MIN_PROBE_PIN) + #endif // IRON + #else - #define PROBE_READ() READ(Z_MIN_PIN) -#endif + + #ifdef CAN_MASTER // IRON, VIRTUAL IO + #define PROBE_READ() ((CAN_io_state & CAN_PROBE_BIT)) // IRON, READ VIRTUAL IO + #else + #define PROBE_READ() READ(Z_MIN_PIN) + #endif + +#endif // IRON + #if USE_Z_MIN_PROBE #define PROBE_HIT_STATE Z_MIN_PROBE_ENDSTOP_HIT_STATE #else diff --git a/Marlin/src/module/temperature.cpp b/Marlin/src/module/temperature.cpp index 96295d7db586..1f78f4f9ffb5 100644 --- a/Marlin/src/module/temperature.cpp +++ b/Marlin/src/module/temperature.cpp @@ -2734,9 +2734,11 @@ void Temperature::updateTemperaturesFromRawValues() { temp_bed.setraw(read_max_tc_bed()); #endif +#ifndef CAN_MASTER // IRON, NOT FOR MASTER, DON'T READ TEMPERATURE FROM SENSOR, GET IT VIA CAN BUS FROM HEAD #if HAS_HOTEND HOTEND_LOOP() temp_hotend[e].celsius = analog_to_celsius_hotend(temp_hotend[e].getraw(), e); #endif +#endif // IRON TERN_(HAS_HEATED_BED, temp_bed.celsius = analog_to_celsius_bed(temp_bed.getraw())); TERN_(HAS_TEMP_CHAMBER, temp_chamber.celsius = analog_to_celsius_chamber(temp_chamber.getraw())); @@ -2750,6 +2752,9 @@ void Temperature::updateTemperaturesFromRawValues() { TERN_(HAS_POWER_MONITOR, power_monitor.capture_values()); #if HAS_HOTEND + +#ifndef CAN_MASTER // IRON, ONLY FOR HEAD, NO TEMP SAMPLING ON MASTER + #define _TEMPDIR(N) TEMP_SENSOR_IS_ANY_MAX_TC(N) ? 0 : TEMPDIR(N), static constexpr int8_t temp_dir[HOTENDS] = { REPEAT(HOTENDS, _TEMPDIR) }; @@ -2775,6 +2780,7 @@ void Temperature::updateTemperaturesFromRawValues() { TERN_(MULTI_MAX_CONSECUTIVE_LOW_TEMP_ERR, consecutive_low_temperature_error[e] = 0); } } +#endif // IRON, !CAN_MASTER #endif // HAS_HOTEND @@ -3375,6 +3381,13 @@ void Temperature::disable_all_heaters() { TERN_(PROBING_HEATERS_OFF, pause_heaters(false)); #if HAS_HOTEND + +#ifdef CAN_MASTER // IRON, SHUTDOWN HOTEND IN HEAD TOO + CAN_Send_Gcode_2params('M', 104, 'S', 0, 0, 0); // IRON, M104 S0, switch off hotend heating + CAN_Send_Gcode_2params('M', 107, 0, 0, 0, 0); // IRON, M107, switch off part cooling fan + CAN_Send_Gcode_2params('M', 150, 'R', 255, 0, 0); // IRON, M150 R255, SET NEOPIXEL TO RED +#endif + HOTEND_LOOP() { setTargetHotend(0, e); temp_hotend[e].soft_pwm_amount = 0; diff --git a/ini/stm32f4.ini b/ini/stm32f4.ini index 7fdbc6980477..b90b46ae7375 100644 --- a/ini/stm32f4.ini +++ b/ini/stm32f4.ini @@ -619,6 +619,11 @@ build_flags = ${stm_flash_drive.build_flags} ${stm32f4_I2C1_CAN.build_flag extends = env:mks_monster8_usb_flash_drive build_flags = ${env:mks_monster8_usb_flash_drive.build_flags} -DUSBD_USE_CDC_MSC +[env:mks_monster8_usb_flash_drive_msc_CAN] +extends = env:mks_monster8_usb_flash_drive +build_flags = ${env:mks_monster8_usb_flash_drive.build_flags} + -DUSBD_USE_CDC_MSC + -DHAL_CAN_MODULE_ENABLED build_unflags = -DUSBD_USE_CDC # diff --git a/platformio.ini b/platformio.ini index ed1670dc6daf..88e468c49e28 100644 --- a/platformio.ini +++ b/platformio.ini @@ -13,7 +13,7 @@ [platformio] src_dir = Marlin boards_dir = buildroot/share/PlatformIO/boards -default_envs = mega2560 +default_envs = #define MOTHERBOARD BOARD_MKS_MONSTER8_V2 include_dir = Marlin extra_configs = Marlin/config.ini From 264727331fa31c38b41f940390a957ab78a76308 Mon Sep 17 00:00:00 2001 From: rondlh <77279634+rondlh@users.noreply.github.com> Date: Wed, 27 Nov 2024 00:57:09 +0800 Subject: [PATCH 02/12] Configuration.h MOTHERBOARD fix Fix typo in Configuration.,h for MOTHERBOARD --- Marlin/Configuration.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h index d4ba2265dd75..35ca67c0f578 100644 --- a/Marlin/Configuration.h +++ b/Marlin/Configuration.h @@ -70,7 +70,7 @@ // Choose the name from boards.h that matches your setup #ifndef MOTHERBOARD - #define MOTHERBOARD #define MOTHERBOARD BOARD_MKS_MONSTER8_V2 + #define MOTHERBOARD BOARD_MKS_MONSTER8_V2 #endif // @section serial From 9508283c77df4a8ea52e93a345693f09aa8fbdc5 Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Tue, 26 Nov 2024 15:05:34 -0600 Subject: [PATCH 03/12] clean up --- Marlin/Configuration.h | 7 +- Marlin/src/HAL/STM32/CAN.cpp | 513 +++++++++--------- Marlin/src/HAL/STM32/CAN.h | 28 - Marlin/src/HAL/STM32/Servo.cpp | 27 +- Marlin/src/HAL/shared/CAN.h | 50 ++ Marlin/src/MarlinCore.cpp | 19 +- Marlin/src/feature/pause.cpp | 5 - Marlin/src/feature/runout.h | 18 +- Marlin/src/gcode/gcode.cpp | 25 +- Marlin/src/gcode/temp/M306.cpp | 51 +- Marlin/src/module/endstops.cpp | 23 +- Marlin/src/module/probe.h | 24 +- Marlin/src/module/temperature.cpp | 26 +- Marlin/src/pins/pins.h | 2 +- Marlin/src/pins/stm32g0/pins_BTT_EBB42_V1_1.h | 2 +- ini/stm32f4.ini | 1 + platformio.ini | 2 +- 17 files changed, 418 insertions(+), 405 deletions(-) delete mode 100644 Marlin/src/HAL/STM32/CAN.h create mode 100644 Marlin/src/HAL/shared/CAN.h diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h index 35ca67c0f578..2cdffe5179ac 100644 --- a/Marlin/Configuration.h +++ b/Marlin/Configuration.h @@ -37,8 +37,6 @@ */ #define CONFIGURATION_H_VERSION 02010300 -// ENABLE CAN SUPPORT HERE -// #define CAN_MASTER //=========================================================================== //============================= Getting Started ============================= //=========================================================================== @@ -70,7 +68,7 @@ // Choose the name from boards.h that matches your setup #ifndef MOTHERBOARD - #define MOTHERBOARD BOARD_MKS_MONSTER8_V2 + #define MOTHERBOARD BOARD_RAMPS_14_EFB #endif // @section serial @@ -126,6 +124,9 @@ //#define RS485_BUS_BUFFER_SIZE 128 #endif +// Enable CAN bus support and protocol +//#define CAN_MASTER + // Enable the Bluetooth serial interface on AT90USB devices //#define BLUETOOTH diff --git a/Marlin/src/HAL/STM32/CAN.cpp b/Marlin/src/HAL/STM32/CAN.cpp index 52cfeca1d2d7..89292f986d3f 100644 --- a/Marlin/src/HAL/STM32/CAN.cpp +++ b/Marlin/src/HAL/STM32/CAN.cpp @@ -1,63 +1,85 @@ -// IRON NOTES: -// NOTE 1: For MKS Monster 8 V1/V2 on Arduino use: Board "Generic STM32F4 series", Board part number "Generic F407VETx" -// NOTE 2: Make sure to define "HAL_CAN_MODULE_ENABLED". In VSCode add "build_flags = -D HAL_CAN_MODULE_ENABLED" in platformio.ini. -// For Arduino IDE include "hal_conf_extra.h" holding "#define HAL_CAN_MODULE_ENABLED" -// NOTE 3: To accept all CAN messages, enable 1 filter (FilterBank = 0) in "FilterMode = CAN_FILTERMODE_IDMASK", mask and ID = 0 (0=don't care) -// NOTE 4: Serial communication in ISR causes issues! Hangs etc. so avoid this! -// NOTE 5: A FIFO storage cell is called a Mailbox in STM32F4xx, FIFO0 and FiFO1 can hold 3 CAN message each. -// NOTE 6: The filter ID/mask numbers (LOW/HIGH) do not directly relate to the message ID numbers (See Figure 342 in RM0090) - -#include "../../inc/MarlinConfig.h" - -#ifdef CAN_MASTER - #include "../platforms.h" - #include "../../gcode/gcode.h" - #include "../../module/temperature.h" - #include "../../module/motion.h" // For current_position variable - #include "../../module/planner.h" // For steps/mm parameters variables - #include "../../feature/tmc_util.h" - #include "../../module/stepper.h" - #include "../../module/endstops.h" - #include "../../feature/controllerfan.h" // For controllerFan settings - #include "../../libs/numtostr.h" // For float to string conversion - - // #define CAN_DEBUG // Define to show gcodes send to HEAD - - #define CAN_EXTENDED_ID CAN_ID_EXT - #define CAN_STANDARD_ID CAN_ID_STD - - #define STDID_FIFO_BIT 0b10000000000 - #define EXTID_FIFO_BIT 0x10000000 - - #define CAN_IO_MASK 0b11111 // Masks for the 5 virtual IO bits (see below) - #define GCODE_NUMBER_MASK 0b1111111111111 - #define PARAMETER_MASK 0b11111 - #define PARAMETER_COUNT_MASK 0b111 - #define GCODE_TYPE_MASK 0b11 - #define CAN_PROBE_MASK 1 // Virtual IO bit for probe - #define CAN_FILAMENT_MASK 2 // Virtual IO bit for filament - #define CAN_X_ENDSTOP_MASK 4 // Virtual IO bit for X-endstop - #define CAN_Y_ENDSTOP_MASK 8 // Virtual IO bit for Y-endstop - #define CAN_Z_ENDSTOP_MASK 16 // Virtual IO bit for Z-endstop - #define CAN_STRING_MESSAGE_MASK 32 // Signals the head sent 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_E0_TARGET_MASK 256 // Signals E0 or E1 - #define CAN_ERROR_MASK 512 // Signals the head encountered an error - - #define PARAMETER1_OFFSET 0 - #define PARAMETER2_OFFSET 5 - #define GCODE_NUMBER_OFFSET 10 - #define GCODE_TYPE_OFFSET 23 - #define PARAMETER_COUNT_OFFSET 25 - - #define GCODE_TYPE_D 0 - #define GCODE_TYPE_G 1 - #define GCODE_TYPE_M 2 - #define GCODE_TYPE_T 3 - -extern "C" void CAN1_RX0_IRQHandler(void); // Override weak CAN FIFO0 interrupt handler -extern "C" void CAN1_RX1_IRQHandler(void); // Override weak CAN FIFO1 interrupt handler +/** + * 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 . + * + */ + +/** + * Contributor Notes: + * NOTE 1: For MKS Monster 8 V1/V2 on Arduino use: Board "Generic STM32F4 series", Board part number "Generic F407VETx" + * NOTE 2: Requires `HAL_CAN_MODULE_ENABLED`, e.g., with `-DHAL_CAN_MODULE_ENABLED` + * For Arduino IDE use "hal_conf_extra.h" with `#define HAL_CAN_MODULE_ENABLED` + * NOTE 3: To accept all CAN messages, enable 1 filter (FilterBank = 0) in "FilterMode = CAN_FILTERMODE_IDMASK", mask and ID = 0 (0=don't care) + * NOTE 4: Serial communication in ISR causes issues! Hangs etc. so avoid this! + * NOTE 5: A FIFO storage cell is called a "Mailbox" in STM32F4xx, FIFO0 and FiFO1 can hold 3 CAN messages each. + * NOTE 6: The filter ID/mask numbers (LOW/HIGH) do not directly relate to the message ID numbers (See Figure 342 in RM0090) + */ + +#include "../../inc/MarlinConfigPre.h" + +#if ENABLED(CAN_MASTER) + +#include "../platforms.h" +#include "../../gcode/parser.h" +#include "../../module/temperature.h" +#include "../../module/motion.h" // For current_position variable +#include "../../module/planner.h" // For steps/mm parameters variables +#include "../../feature/tmc_util.h" +#include "../../module/endstops.h" +#include "../../feature/controllerfan.h" // For controllerFan settings +#include "../../libs/numtostr.h" // For float to string conversion + +#define CAN_EXTENDED_ID CAN_ID_EXT +#define CAN_STANDARD_ID CAN_ID_STD + +#define STDID_FIFO_BIT 0b10000000000 +#define EXTID_FIFO_BIT 0x10000000 + +#define CAN_IO_MASK 0b11111 // Masks for the 5 virtual IO bits (see below) +#define GCODE_NUMBER_MASK 0b1111111111111 +#define PARAMETER_MASK 0b11111 +#define PARAMETER_COUNT_MASK 0b111 +#define GCODE_TYPE_MASK 0b11 +#define CAN_PROBE_MASK 1 // Virtual IO bit for probe +#define CAN_FILAMENT_MASK 2 // Virtual IO bit for filament +#define CAN_X_ENDSTOP_MASK 4 // Virtual IO bit for X-endstop +#define CAN_Y_ENDSTOP_MASK 8 // Virtual IO bit for Y-endstop +#define CAN_Z_ENDSTOP_MASK 16 // Virtual IO bit for Z-endstop +#define CAN_STRING_MESSAGE_MASK 32 // Signals the head sent 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_E0_TARGET_MASK 256 // Signals E0 or E1 +#define CAN_ERROR_MASK 512 // Signals the head encountered an error + +#define PARAMETER1_OFFSET 0 +#define PARAMETER2_OFFSET 5 +#define GCODE_NUMBER_OFFSET 10 +#define GCODE_TYPE_OFFSET 23 +#define PARAMETER_COUNT_OFFSET 25 + +#define GCODE_TYPE_D 0 +#define GCODE_TYPE_G 1 +#define GCODE_TYPE_M 2 +#define GCODE_TYPE_T 3 + +extern "C" void CAN1_RX0_IRQHandler(); // Override weak CAN FIFO0 interrupt handler +extern "C" void CAN1_RX1_IRQHandler(); // Override weak CAN FIFO1 interrupt handler extern "C" void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan); // Override weak CAN interrupt callback for FIFO0 extern "C" void HAL_CAN_RxFifo1MsgPendingCallback(CAN_HandleTypeDef *hcan); // Override weak CAN interrupt callback for FIFO1 extern "C" void HAL_CAN_ErrorCallback(CAN_HandleTypeDef *hcan); // Override weak CAN interrupt callback @@ -73,71 +95,70 @@ volatile uint32_t HAL_CAN_error_code = 0; // Record a host CAN error messa volatile bool FirstE0Error = true; // First CAN bus error, show warning only once volatile bool string_message_complete = false; // Signals a complete string message was received volatile uint32_t string_message_index = 0; // Index into the CAN string that is being received -uint32_t Last_CAN_Temp_Report = 0; // Track when the last head temperature report was received +uint32_t Next_CAN_Temp_Report = 0; // Track when the next head temperature report will be received uint32_t Last_CAN_Error_Message = 0; // Track when the last CAN error messaget was shown char string_message[128] = "\0"; // CAN string message buffer for incoming message, max 128 characters -void CAN1_RX0_IRQHandler(void) // CAN FIFO0 Interrupt handler overrides standard weak CAN1_RX0_IRQHandler -{ +void CAN1_RX0_IRQHandler() { // CAN FIFO0 Interrupt handler overrides standard weak CAN1_RX0_IRQHandler HAL_CAN_IRQHandler(&hcan1); // Forwarded for callbacks --> HAL_CAN_RxFifo0MsgPendingCallback/HAL_CAN_ErrorCallback -// OR -// HAL_CAN_RxFifo0MsgPendingCallback(&hcan1); // Call the required callback directly, faster but no error reporting + // OR + //HAL_CAN_RxFifo0MsgPendingCallback(&hcan1); // Call the required callback directly, faster but no error reporting } -void CAN1_RX1_IRQHandler(void) // CAN FIFO1 Interrupt handler overrides standard weak CAN1_RX0_IRQHandler -{ +void CAN1_RX1_IRQHandler() { // CAN FIFO1 Interrupt handler overrides standard weak CAN1_RX0_IRQHandler HAL_CAN_IRQHandler(&hcan1); // Forwarded for callbacks --> HAL_CAN_RxFifo1MsgPendingCallback/HAL_CAN_ErrorCallback -// OR -// HAL_CAN_RxFifo1MsgPendingCallback(&hcan1); // Call the required callback directly, faster but no error reporting + // OR + //HAL_CAN_RxFifo1MsgPendingCallback(&hcan1); // Call the required callback directly, faster but no error reporting } // Send specified Gcode with max 2 parameters and 2 values via CAN bus -HAL_StatusTypeDef CAN_Send_Gcode_2params(uint32_t Gcode_type, uint32_t Gcode_no, uint32_t parameter1, float value1, uint32_t parameter2, float value2) -{ - switch (Gcode_type) - { - case 'D': Gcode_type = GCODE_TYPE_D; - break; - - case 'G': Gcode_type = GCODE_TYPE_G; - break; - - case 'M': Gcode_type = GCODE_TYPE_M; +HAL_StatusTypeDef CAN_Send_Gcode_2params(uint32_t Gcode_type, uint32_t Gcode_no, uint32_t parameter1, float value1, uint32_t parameter2, float value2) { + switch (Gcode_type) { + case 'D': + Gcode_type = GCODE_TYPE_D; + break; -#ifdef CAN_DEBUG - SERIAL_ECHOPGM(">>> CAN TO HEAD: M", Gcode_no); - if (parameter1) { - SERIAL_CHAR(' ', parameter1); - if (value1 == int(value1)) - SERIAL_ECHO(i16tostr3left(value1)); // Integer value - else - SERIAL_ECHO(p_float_t(value1, 4)); // Float with 4 digits - } + case 'G': + Gcode_type = GCODE_TYPE_G; + break; - if (parameter2) { - SERIAL_CHAR(' ', parameter2); - if (value2 == int(value2)) - SERIAL_ECHO(i16tostr3left(value2)); // Integer value - else - SERIAL_ECHO(p_float_t(value2, 4)); // Float with 4 digits - } - SERIAL_EOL(); -#endif + case 'M': + Gcode_type = GCODE_TYPE_M; + + #ifdef CAN_DEBUG + SERIAL_ECHOPGM(">>> CAN TO HEAD: M", Gcode_no); + if (parameter1) { + SERIAL_CHAR(' ', parameter1); + if (value1 == int(value1)) + SERIAL_ECHO(i16tostr3left(value1)); // Integer value + else + SERIAL_ECHO(p_float_t(value1, 4)); // Float with 4 digits + } + + if (parameter2) { + SERIAL_CHAR(' ', parameter2); + if (value2 == int(value2)) + SERIAL_ECHO(i16tostr3left(value2)); // Integer value + else + SERIAL_ECHO(p_float_t(value2, 4)); // Float with 4 digits + } + SERIAL_EOL(); + #endif // CAN_DEBUG + + break; - break; - case 'T': Gcode_type = GCODE_TYPE_T; - break; - + break; + default: return HAL_ERROR; // UNKNOWN GCODE TYPE } HAL_StatusTypeDef status = HAL_OK; - uint32_t TxMailbox; // Stores which Mailbox (0-2) is used to store the Sent TX message + uint32_t TxMailbox; // Stores which Mailbox (0-2) is used to store the Sent TX message if (parameter1 > 31) parameter1 -= 64; // Format 'A' = 1, 'B' = 2, etc. - + if (parameter2 > 31) parameter2 -= 64; // Format 'A' = 1, 'B' = 2, etc. @@ -146,18 +167,18 @@ HAL_StatusTypeDef CAN_Send_Gcode_2params(uint32_t Gcode_type, uint32_t Gcode_no, TxHeader.StdId = (TxHeader.StdId ^ STDID_FIFO_BIT); // Toggle FIFO bit 10, keep FIFO toggling in sync TxHeader.ExtId = ((TxHeader.ExtId ^ EXTID_FIFO_BIT) & EXTID_FIFO_BIT) + // Toggle FIFO bit 28 ((TxHeader.DLC >> 2) << PARAMETER_COUNT_OFFSET) + // Data bytes (4 or 8) - (Gcode_type << GCODE_TYPE_OFFSET) + // G/M/T/D-code + (Gcode_type << GCODE_TYPE_OFFSET) + // G/M/T/D-code ((Gcode_no & GCODE_NUMBER_MASK) << GCODE_NUMBER_OFFSET) + // Gcode number ((parameter2 & PARAMETER_MASK) << PARAMETER2_OFFSET) + // First parameter - ((parameter1 & PARAMETER_MASK) << PARAMETER1_OFFSET); // Second parameter + ((parameter1 & PARAMETER_MASK) << PARAMETER1_OFFSET); // Second parameter uint8_t CAN_tx_buffer[8]; // 8 bytes CAN data TX buffer - float * fp = (float *)CAN_tx_buffer; // Point to TX buffer + float * fp = (float *)CAN_tx_buffer; // Point to TX buffer *fp++ = value1; *fp-- = value2; - - uint32_t ms = millis(); // Don't send too fast! + + const uint32_t ms = millis(); // Don't send too fast! while ((HAL_CAN_GetTxMailboxesFreeLevel(&hcan1) == 0) && (millis() - ms < 25)) { } // BLOCKING! Wait max 25ms! - // SERIAL_ECHOLNPGM(">>> Waited1: ", millis() - ms, " FreeTX: ", HAL_CAN_GetTxMailboxesFreeLevel(&hcan1)); // IRON, DEBUGGING + //SERIAL_ECHOLNPGM(">>> Waited1: ", millis() - ms, " FreeTX: ", HAL_CAN_GetTxMailboxesFreeLevel(&hcan1)); // IRON, DEBUGGING status = HAL_CAN_AddTxMessage(&hcan1, &TxHeader, CAN_tx_buffer, &TxMailbox); // Send message if (status == HAL_OK) // Count sent gcode messages @@ -166,25 +187,26 @@ HAL_StatusTypeDef CAN_Send_Gcode_2params(uint32_t Gcode_type, uint32_t Gcode_no, return status; } -HAL_StatusTypeDef CAN_Send_Setup() // Send setup to HEAD -{ +HAL_StatusTypeDef CAN_Send_Setup() { // Send setup to HEAD CAN_head_setup_request = false; SERIAL_ECHOLNPGM(">>> CAN: SENDING CONFIG TO HEAD ====="); -// NOTE: Sending many command too fast will cause a Marlin command buffer overrun at the head, add delays if needed -// CAN_Send_Gcode_2params('M', 104, 'S', 0, 0, 0); // M104 S0, switch off hotend heating, NOT NEEDED ANYMORE -// CAN_Send_Gcode_2params('M', 107, 0, 0, 0, 0); // M107, switch off cooling fan, NOT NEEDED ANYMORE -// CAN_Send_Gcode_2params('M', 18, 0, 0, 0, 0); // M18, switch off steppers, NOT NEEDED ANYMORE - - // M306 MPC settings (managed by host) - MPC_t &mpc = thermalManager.temp_hotend[0].mpc; - - CAN_Send_Gcode_2params('M', 306, 'A', mpc.ambient_xfer_coeff_fan0, 'C', mpc.block_heat_capacity); // M306 R A - CAN_Send_Gcode_2params('M', 306, 'F', mpc.fanCoefficient(), 'H', mpc.filament_heat_capacity_permm); // M306 F H - CAN_Send_Gcode_2params('M', 306, 'P', mpc.heater_power, 'R', mpc.sensor_responsiveness); // M306 P C + // NOTE: Sending many command too fast will cause a Marlin command buffer overrun at the head, add delays if needed + //CAN_Send_Gcode_2params('M', 104, 'S', 0, 0, 0); // M104 S0, switch off hotend heating, NOT NEEDED ANYMORE + //CAN_Send_Gcode_2params('M', 107, 0, 0, 0, 0); // M107, switch off cooling fan, NOT NEEDED ANYMORE + //CAN_Send_Gcode_2params('M', 18, 0, 0, 0, 0); // M18, switch off steppers, NOT NEEDED ANYMORE + + #if ENABLED(MPCTEMP) + // M306 MPC settings (managed by host) + MPC_t &mpc = thermalManager.temp_hotend[0].mpc; + + CAN_Send_Gcode_2params('M', 306, 'A', mpc.ambient_xfer_coeff_fan0, 'C', mpc.block_heat_capacity); // M306 R A + CAN_Send_Gcode_2params('M', 306, 'F', mpc.fanCoefficient(), 'H', mpc.filament_heat_capacity_permm); // M306 F H + CAN_Send_Gcode_2params('M', 306, 'P', mpc.heater_power, 'R', mpc.sensor_responsiveness); // M306 P C + #endif -// CAN_Send_Gcode_2params('M', 150, 0, 0, 0, 0); // M150, SWITCH NEOPIXEL OFF + //CAN_Send_Gcode_2params('M', 150, 0, 0, 0, 0); // M150, SWITCH NEOPIXEL OFF -/* + /* extern Planner planner; // M92 Steps per mm CAN_Send_Gcode_2params('M', 92, 'X', planner.settings.axis_steps_per_mm[X_AXIS], 'Y', planner.settings.axis_steps_per_mm[Y_AXIS]); CAN_Send_Gcode_2params('M', 92, 'Z', planner.settings.axis_steps_per_mm[Z_AXIS], 'E', planner.settings.axis_steps_per_mm[E_AXIS]); @@ -198,7 +220,7 @@ HAL_StatusTypeDef CAN_Send_Setup() // Send setup to HEAD // M201 Max acceleration CAN_Send_Gcode_2params('M', 201, 'X', planner.settings.max_acceleration_mm_per_s2[X_AXIS], 'Y', planner.settings.max_acceleration_mm_per_s2[Y_AXIS]); CAN_Send_Gcode_2params('M', 201, 'Z', planner.settings.max_acceleration_mm_per_s2[Z_AXIS], 'E', planner.settings.max_acceleration_mm_per_s2[E_AXIS]); - + // M203 Max feedrate CAN_Send_Gcode_2params('M', 203, 'X', planner.settings.max_feedrate_mm_s[X_AXIS], 'Y', planner.settings.max_feedrate_mm_s[Y_AXIS]); CAN_Send_Gcode_2params('M', 203, 'Z', planner.settings.max_feedrate_mm_s[Z_AXIS], 'E', planner.settings.max_feedrate_mm_s[E_AXIS]); @@ -208,16 +230,16 @@ HAL_StatusTypeDef CAN_Send_Setup() // Send setup to HEAD CAN_Send_Gcode_2params('M', 204, 'T', planner.settings.travel_acceleration, 0, 0); // M205 - #ifdef CLASSIC_JERK + #if ENABLED(CLASSIC_JERK) CAN_Send_Gcode_2params('M', 205,'S')) planner.settings.min_feedrate_mm_s, 'T')) planner.settings.min_travel_feedrate_mm_s); CAN_Send_Gcode_2params('M', 205, M205_MIN_SEG_TIME_PARAM, planner.settings.min_segment_time_us, 'J', planner.junction_deviation_mm); CAN_Send_Gcode_2params('M', 205, 'X', LINEAR_UNIT(planner.max_jerk.x), 'Y', LINEAR_UNIT(planner.max_jerk.y)); CAN_Send_Gcode_2params('M', 205, 'Z', LINEAR_UNIT(planner.max_jerk.z), 'E', LINEAR_UNIT(planner.max_jerk.e)); - CAN_Send_Gcode_2params('M', 205, 'J', LINEAR_UNIT(planner.junction_deviation_mm), 0, 0); + CAN_Send_Gcode_2params('M', 205, 'J', LINEAR_UNIT(planner.junction_deviation_mm), 0, 0); #endif // M206 Home offset - #ifndef NO_HOME_OFFSETS + #if DISABLED(NO_HOME_OFFSETS) _CAN_Send_Gcode_2params('M', 206, 'X', LINEAR_UNIT(home_offset.x), 'Y', LINEAR_UNIT(home_offset.y)); CAN_Send_Gcode_2params('M', 206, 'Z', LINEAR_UNIT(home_offset.z), 0, 0); #endif @@ -225,14 +247,14 @@ HAL_StatusTypeDef CAN_Send_Setup() // Send setup to HEAD // M207 Set Firmware Retraction // M208 - Firmware Recover // M209 - Set Auto Retract - + // M220 Speed/feedrate CAN_Send_Gcode_2params('M', 220, 'S', feedrate_percentage, 0, 0); - + // M221 Flow percentage CAN_Send_Gcode_2params('M', 221, 'T', 0, 'S', planner.flow_percentage[0]); // CAN_Send_Gcode_2params('M', 221, 'T', 1, 'S', planner.flow_percentage[1]); // For 2nd extruder - + // M302 Cold extrude settings #if ENABLED(PREVENT_COLD_EXTRUSION) CAN_Send_Gcode_2params('M', 302, 'P', '0' + thermalManager.allow_cold_extrude, 'S', thermalManager.extrude_min_temp); // P0 enable cold extrusion checking, P1 = disabled, S=Minimum temperature @@ -249,31 +271,32 @@ HAL_StatusTypeDef CAN_Send_Setup() // Send setup to HEAD // M919 TMC Chopper timing for E only CAN_Send_Gcode_2params('M', 919, 'O', off, 'P' , Hysteresis End); CAN_Send_Gcode_2params('M', 919, 'S', Hysteresis Start, 0, 0); -*/ -/* -CAN_Send_Gcode_2params('M', 710, 'E', 1, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P -CAN_Send_Gcode_2params('M', 710, 'E', 2, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P -CAN_Send_Gcode_2params('M', 710, 'E', 3, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P -CAN_Send_Gcode_2params('M', 710, 'E', 4, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P -CAN_Send_Gcode_2params('M', 710, 'E', 5, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P -CAN_Send_Gcode_2params('M', 710, 'E', 6, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P -CAN_Send_Gcode_2params('M', 710, 'E', 7, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P -CAN_Send_Gcode_2params('M', 710, 'E', 8, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P -CAN_Send_Gcode_2params('M', 710, 'E', 9, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P -CAN_Send_Gcode_2params('M', 710, 'E', 10, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P -CAN_Send_Gcode_2params('M', 710, 'E', 11, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P -*/ - // IRON, M710 SIGNALS TO THE HEAD THAT THE CAN CONFIGURATION IS COMPLETE, USE IT AS THE LAST GCODE TO SEND - return CAN_Send_Gcode_2params('M', 710, 'E', int(controllerFan.settings.extruder_auto_fan_speed), 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P + */ + #if USE_CONTROLLER_FAN + /* + CAN_Send_Gcode_2params('M', 710, 'E', 1, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P + CAN_Send_Gcode_2params('M', 710, 'E', 2, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P + CAN_Send_Gcode_2params('M', 710, 'E', 3, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P + CAN_Send_Gcode_2params('M', 710, 'E', 4, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P + CAN_Send_Gcode_2params('M', 710, 'E', 5, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P + CAN_Send_Gcode_2params('M', 710, 'E', 6, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P + CAN_Send_Gcode_2params('M', 710, 'E', 7, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P + CAN_Send_Gcode_2params('M', 710, 'E', 8, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P + CAN_Send_Gcode_2params('M', 710, 'E', 9, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P + CAN_Send_Gcode_2params('M', 710, 'E', 10, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P + CAN_Send_Gcode_2params('M', 710, 'E', 11, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P + */ + // IRON, M710 SIGNALS TO THE HEAD THAT THE CAN CONFIGURATION IS COMPLETE, USE IT AS THE LAST GCODE TO SEND + return CAN_Send_Gcode_2params('M', 710, 'E', int(controllerFan.settings.extruder_auto_fan_speed), 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P + #endif + return HAL_OK; } -void CAN_Idle() // Tasks that cannot be done in the ISR -{ +void CAN_Idle() { // Tasks that cannot be done in the ISR if (CAN_head_setup_request) // The head requested the setup data CAN_Send_Setup(); - - if (string_message_complete) // Received string message is complete - { + + if (string_message_complete) { // Received string message is complete BUZZ(1, SOUND_OK); SERIAL_ECHOPGM(">>> CAN MSG FROM HEAD: "); for (uint32_t i = 0; i < string_message_index; i++) @@ -283,15 +306,13 @@ void CAN_Idle() // Tasks that cannot be done in the ISR string_message_index = 0; } - if ((hcan1.ErrorCode || CAN_head_error || HAL_CAN_error_code) && (millis() - Last_CAN_Error_Message > 6000)) - { - BUZZ(1, SOUND_ERROR); // Warn with sound - if (CAN_head_error) - SERIAL_ECHOLNPGM(">>> CAN ERROR REPORTED BY HEAD"); + if ((hcan1.ErrorCode || CAN_head_error || HAL_CAN_error_code) && (millis() - Last_CAN_Error_Message > 6000)) { + BUZZ(1, SOUND_ERROR); + if (CAN_head_error) SERIAL_ECHOLNPGM(">>> CAN Error reported by head"); CAN_head_error = false; // Reset, but will be repeated by the head if (HAL_CAN_error_code) - SERIAL_ECHOLNPGM(">>> HAL CAN ERROR REPORTED: ", HAL_CAN_error_code); + SERIAL_ECHOLNPGM(">>> HAL CAN Error reported: ", HAL_CAN_error_code); if (hcan1.ErrorCode) SERIAL_ECHOLNPGM(">>> hcan1.ErrorCode=", hcan1.ErrorCode); @@ -299,27 +320,25 @@ void CAN_Idle() // Tasks that cannot be done in the ISR Last_CAN_Error_Message = millis(); } - if ((millis() - Last_CAN_Temp_Report) > 10000) // IRON, ERROR, NO TEMP UPDATE RECEIVED IN 10 SECONDS, KILL PRINTER - { - Last_CAN_Temp_Report = millis(); - if (FirstE0Error) // Send error notification - { + if (ELAPSED(millis(), Next_CAN_Temp_Report)) { // IRON, ERROR, NO TEMP UPDATE RECEIVED IN 10 SECONDS, KILL PRINTER + Next_CAN_Temp_Report = millis() + 10000; + if (FirstE0Error) { // Send error notification BUZZ(1, SOUND_ERROR); // Warn with sound - SERIAL_ECHOLNPGM("Error: NO CAN E0 TEMP UPDATES!"); + SERIAL_ECHOLNPGM("Error: No CAN E0 temp updates!"); } else // Send only error message - SERIAL_ECHOLNPGM(">>> CAN ERROR, NO E0 TEMP UPDATES!"); + SERIAL_ECHOLNPGM(">>> CAN ERROR, No E0 temp updates!"); FirstE0Error = false; // Warn only once - #ifndef IRON_DEBUG // Only kill if not debugging - kill(F("CAN ERROR, NO E0 TEMPERATURE UPDATES")); - #endif + #ifndef CAN_DEBUG // Only kill if not debugging + kill(F("CAN ERROR, NO E0 TEMPERATURE UPDATES")); + #endif } } -HAL_StatusTypeDef CAN_Send_Gcode() // Forward a Marlin Gcode via CAN (uses parser.command_letter, Gcode_no, parser.value_float()) -{ // Send a Gcode to the head with parameters and values +HAL_StatusTypeDef CAN_Send_Gcode() { // Forward a Marlin Gcode via CAN (uses parser.command_letter, Gcode_no, parser.value_float()) + // Send a Gcode to the head with parameters and values // Gcode starts with extended frame which can send the Gcode with max 2 parameters and values. // Extended frames are send to complete all parameters and values (max 2 per extended message). // 1. Analyze Gcode command @@ -328,25 +347,25 @@ HAL_StatusTypeDef CAN_Send_Gcode() // Forward a Marlin Gcode via CAN (uses parse // char s[] = "G0 X123.45678 Y124.45678 Z125.45678 E126.45678 F127.45678\n"; HAL_StatusTypeDef status = HAL_OK; - uint32_t TxMailbox; // Stores which Mailbox (0-2) is used to store the Sent TX message - + uint32_t TxMailbox; // Stores which Mailbox (0-2) is used to store the Sent TX message + if (parser.command_letter != 'M') // Only forward Mxxx Gcode to head return HAL_OK; - + uint32_t Gcode_type = GCODE_TYPE_M; // M-code, fixed for now uint32_t Gcode_no = parser.codenum; if (Gcode_no == 109) // Convert M109(Hotend wait) to M104 (no wait) to keep the head responsive Gcode_no = 104; - + if ((Gcode_no == 501) || (Gcode_no == 502)) // M501=Restore settings, M502=Factory defaults CAN_head_setup_request = true; // Also update settings for the head if ((Gcode_no != 104) && // Set hotend target temp (Gcode_no != 106) && // Set cooling fan speed (Gcode_no != 107) && // Cooling fan off - (Gcode_no != 150) && // Set NeoPixel values -// (Gcode_no != 108) && // Break and Continue + (Gcode_no != 150) && // Set NeoPixel values + //(Gcode_no != 108) && // Break and Continue (Gcode_no != 280) && // Servo position (Gcode_no != 306) && // MPC settings/tuning (Gcode_no != 710) && // Control fan PWM @@ -414,7 +433,7 @@ HAL_StatusTypeDef CAN_Send_Gcode() // Forward a Marlin Gcode via CAN (uses parse case 510: case 511: case 512: // No locking of the machine case 605: // No IDEX commands case 810: case 811: case 812: case 813: case 814: case 815: case 816: case 817: case 818: case 819: - case 851: // + case 851: // case 871: // No Probe temp config case 876: // No handle prompt response case 913: // No Set Hybrid Threshold Speed @@ -424,7 +443,7 @@ HAL_StatusTypeDef CAN_Send_Gcode() // Forward a Marlin Gcode via CAN (uses parse return HAL_OK; // NO CAM MESSAGE } break; - + case 'T': Gcode_type = GCODE_TYPE_T; switch (Gcode_no) { @@ -432,7 +451,7 @@ HAL_StatusTypeDef CAN_Send_Gcode() // Forward a Marlin Gcode via CAN (uses parse break; } break; - + case 'D': Gcode_type = GCODE_TYPE_D; switch (Gcode_no) { @@ -442,7 +461,7 @@ HAL_StatusTypeDef CAN_Send_Gcode() // Forward a Marlin Gcode via CAN (uses parse break; default: return HAL_OK; // Invalid command, nothing to do } -*/ + */ #ifdef CAN_DEBUG SERIAL_ECHOPGM(">>> CAN GCODE TO HEAD: "); // IRON, DEBUGGING @@ -451,37 +470,32 @@ HAL_StatusTypeDef CAN_Send_Gcode() // Forward a Marlin Gcode via CAN (uses parse #endif if (strlen(parser.command_ptr) > 4) // "M107\0", ONLY SCAN FOR PARAMETERS IF STRING IS LONG ENOUGH - for (index = 0; index < sizeof(letters); index++) // Scan parameters - { - if (parser.seen(letters[index])) - { + for (index = 0; index < sizeof(letters); index++) { // Scan parameters + if (parser.seen(letters[index])) { parameters[parameter_counter] = letters[index] - 64; // Store parameter letter, A=1, B=2... - - #ifdef CAN_DEBUG - SERIAL_CHAR(' ', letters[index]); // IRON, DEBUGGING - #endif - if (parser.has_value()) // Check if there is a value - { + #ifdef CAN_DEBUG + SERIAL_CHAR(' ', letters[index]); // IRON, DEBUGGING + #endif + + if (parser.has_value()) { // Check if there is a value values[parameter_counter++] = parser.value_float(); - #ifdef CAN_DEBUG - if (values[parameter_counter - 1] == int(values[parameter_counter - 1])) - SERIAL_ECHO(i16tostr3left(values[parameter_counter - 1])); // Integer value - else - SERIAL_ECHO(p_float_t(values[parameter_counter - 1], 4)); // Float with 4 digits - #endif + #ifdef CAN_DEBUG + if (values[parameter_counter - 1] == int(values[parameter_counter - 1])) + SERIAL_ECHO(i16tostr3left(values[parameter_counter - 1])); // Integer value + else + SERIAL_ECHO(p_float_t(values[parameter_counter - 1], 4)); // Float with 4 digits + #endif } else // No value for parameter - values[parameter_counter++] = NAN; // Not A Number, indicates no parameter value is present + values[parameter_counter++] = NAN; // Not A Number, indicates no parameter value is present } - - if (parameter_counter == 8) - { + + if (parameter_counter == 8) { // Max 8 parameters parameter_counter--; // Max is 7 parameters - SERIAL_ECHOPGM("\nError: TOO MANY PARAMETERS (> 7): "); - SERIAL_ECHOLN_P(parser.command_ptr); + SERIAL_ECHOLNPGM("\nError: TOO MANY PARAMETERS (> 7): ", parser.command_ptr); BUZZ(1, SOUND_ERROR); break; } @@ -492,13 +506,13 @@ HAL_StatusTypeDef CAN_Send_Gcode() // Forward a Marlin Gcode via CAN (uses parse parameters[parameter_counter] = 0; // Set next parameter to 0 (0=no parameter), send in pairs index = 0; - float * fp = (float *)CAN_tx_buffer; // Points to TX buffer - + float * fp = (float *)CAN_tx_buffer; // Points to TX buffer + if ((Gcode_no == 710) && (parameters[0] == 3)) // "M710 C" INDICATES REQUEST FOR GCODE COUNT SERIAL_ECHOLNPGM(">>> GCODES SENT: ", gcode_counter); - - gcode_counter++; - + + gcode_counter++; + TxHeader.IDE = CAN_EXTENDED_ID; // Start with EXTENDED_ID then send STANDARD_ID if needed //TxHeader.ExtId &= EXTID_FIFO_BIT; // Clear ID, keep FIFO bit TxHeader.ExtId = (TxHeader.ExtId & EXTID_FIFO_BIT) + // KEEP FIFO BIT @@ -508,8 +522,7 @@ HAL_StatusTypeDef CAN_Send_Gcode() // Forward a Marlin Gcode via CAN (uses parse ((parameters[1] & PARAMETER_MASK) << PARAMETER2_OFFSET) + // PARAMETER2 ((parameters[0] & PARAMETER_MASK) << PARAMETER1_OFFSET); // PARAMETER1 uint32_t ms = millis(); // Record message send start time - do - { + do { TxHeader.DLC = MIN(8, (parameter_counter - index) << 2); // Maximum 8 bytes, 4 bytes if there is only 1 parameter, 8 bytes if there are 2 //TxHeader.StdId = (TxHeader.StdId ^ STDID_FIFO_BIT) & STDID_FIFO_BIT; // Toggle FIFO bit 10, clear other bits TxHeader.StdId = ((TxHeader.StdId ^ STDID_FIFO_BIT) & STDID_FIFO_BIT) + // Toggle FIFO bit @@ -518,32 +531,29 @@ HAL_StatusTypeDef CAN_Send_Gcode() // Forward a Marlin Gcode via CAN (uses parse TxHeader.ExtId ^= EXTID_FIFO_BIT; // Toggle FIFO bit 28 *fp++ = values[index++]; // Copy first parameter value to data, move pointer to next 4 bytes *fp-- = values[index++]; // Copy 2nd parameter value to data, move pointer to beginning of data array for next round - + while ((HAL_CAN_GetTxMailboxesFreeLevel(&hcan1) == 0) && (millis() - ms < 50)) { } // BLOCKING! Wait max 50ms status = HAL_CAN_AddTxMessage(&hcan1, &TxHeader, CAN_tx_buffer, &TxMailbox); // Send message if (status != HAL_OK) return status; - + TxHeader.IDE = CAN_STANDARD_ID; // All following messages have standard ID for parameter values, 11 bits identifier } while (index < parameter_counter); - + return HAL_OK; } -void CAN_Send_Position() // Send the X, Y, Z and E position to the HEAD -{ +void CAN_Send_Position() { // Send the X, Y, Z and E position to the HEAD CAN_Send_Gcode_2params('G', 92, 'X', current_position.x, 'Y', current_position.y); // M92 X Y CAN_Send_Gcode_2params('G', 92, 'Z', current_position.z, 'E', current_position.e); // M92 E Z } -void HAL_CAN_MspInit(CAN_HandleTypeDef* canHandle) // Called by HAL_CAN_Init -{ +void HAL_CAN_MspInit(CAN_HandleTypeDef* canHandle) { // Called by HAL_CAN_Init GPIO_InitTypeDef GPIO_InitStruct = {0}; RCC_PeriphCLKInitTypeDef PeriphClkInit = {0}; - if (canHandle->Instance == CAN1) - { + if (canHandle->Instance == CAN1) { RCC_PeriphCLKInitTypeDef periphClkInit = { }; HAL_RCCEx_GetPeriphCLKConfig(&periphClkInit); periphClkInit.PeriphClockSelection |= RCC_APB1ENR_CAN1EN; // DONE IN __HAL_RCC_CAN1_CLK_ENABLE? @@ -560,7 +570,7 @@ void HAL_CAN_MspInit(CAN_HandleTypeDef* canHandle) // Called by HAL_CAN_Init // CAN1 GPIO Configuration // PB8 ------> CAN1_RX // PB9 ------> CAN1_TX - + GPIO_InitStruct.Pin = GPIO_PIN_8 | GPIO_PIN_9; // Pin PB8 and Pin PB9 GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; @@ -569,21 +579,19 @@ void HAL_CAN_MspInit(CAN_HandleTypeDef* canHandle) // Called by HAL_CAN_Init HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); // Port B // CAN1 interrupt Init - HAL_NVIC_SetPriority(CAN1_RX0_IRQn, 1, 1); + HAL_NVIC_SetPriority(CAN1_RX0_IRQn, 1, 1); HAL_NVIC_EnableIRQ(CAN1_RX0_IRQn); // Enable CAN FIFO1 interrupt handler - HAL_NVIC_SetPriority(CAN1_RX1_IRQn, 1, 1); + HAL_NVIC_SetPriority(CAN1_RX1_IRQn, 1, 1); HAL_NVIC_EnableIRQ(CAN1_RX1_IRQn); // Enable CAN FIFO1 interrupt handler } } -HAL_StatusTypeDef CAN1_Stop(void) -{ +HAL_StatusTypeDef CAN1_Stop() { return HAL_CAN_Stop(&hcan1); } -HAL_StatusTypeDef CAN1_Start(void) -{ +HAL_StatusTypeDef CAN1_Start() { HAL_StatusTypeDef status = HAL_OK; // Init TxHeader with constant values @@ -601,7 +609,7 @@ HAL_StatusTypeDef CAN1_Start(void) hcan1.Init.AutoBusOff = DISABLE; // DISABLE: Software controlled Bus-off. ENABLE: Automatic hardware controlled (no send/receive) hcan1.Init.AutoWakeUp = ENABLE; // ENABLE: Automatic hardware controlled bus wakeup. DISABLE: Software controlled bus wakeup. hcan1.Init.AutoRetransmission = ENABLE; // DISABLE / ENABLE, resend if transmission failed, but locks up if communication fails/cable not connected!!!!!!!!!!!!!!!!! - hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ; // CAN_SJW_1TQ + hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ; // CAN_SJW_1TQ hcan1.Init.TimeSeg1 = CAN_BS1_11TQ; // CAN_BS1_11TQ hcan1.Init.TimeSeg2 = CAN_BS2_2TQ; // CAN_BS2_2TQ hcan1.Init.Mode = CAN_MODE_NORMAL; // CAN_MODE_NORMAL / CAN_MODE_SILENT / CAN_MODE_LOOPBACK / CAN_MODE_SILENT_LOOPBACK @@ -612,7 +620,7 @@ HAL_StatusTypeDef CAN1_Start(void) status = HAL_CAN_Init(&hcan1); // Calls HAL_CAN_MspInit if (status != HAL_OK) return status; - + CAN_FilterTypeDef sFilterConfig; // Catch CAN messags with highest bit of StdId set in FIFO0 @@ -621,7 +629,7 @@ HAL_StatusTypeDef CAN1_Start(void) sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; // CAN_FILTERSCALE_16BIT / CAN_FILTERSCALE_32BIT (See Figure 342 in RM0090) sFilterConfig.FilterIdHigh = 0b1000000000000000; // ID MSB: (0-0xFFFF) (StdId[10-0] [ExtId17-13]) (See Figure 342 in RM0090) sFilterConfig.FilterIdLow = 0; // ID LSB: (0-0xFFFF) ([ExtId12-0][IDE][RTR] 0) (0="don't care") - sFilterConfig.FilterMaskIdHigh = 0b1000000000000000; // Mask MSB: (0-0xFFFF) (StdId[10-0] [ExtId17-13]) (See Figure 342 in RM0090) + sFilterConfig.FilterMaskIdHigh = 0b1000000000000000; // Mask MSB: (0-0xFFFF) (StdId[10-0] [ExtId17-13]) (See Figure 342 in RM0090) sFilterConfig.FilterMaskIdLow = 0; // Mask LSB: (0-0xFFFF) ([ExtId12-0][IDE][RTR] 0) (0="don't care") sFilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO0; // Store message in FIFO1 (CAN_FILTER_FIFO0 / CAN_FILTER_FIFO1) sFilterConfig.FilterActivation = CAN_FILTER_ENABLE; // CAN_FILTER_ENABLE / CAN_FILTER_DISABLE @@ -634,7 +642,7 @@ HAL_StatusTypeDef CAN1_Start(void) sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; // CAN_FILTERSCALE_16BIT / CAN_FILTERSCALE_32BIT (See Figure 342 in RM0090) sFilterConfig.FilterIdHigh = 0b0000; // ID MSB: (0-0xFFFF) (StdId[10-0] [ExtId17-13]) (See Figure 342 in RM0090) sFilterConfig.FilterIdLow = 0x0000; // ID LSB: (0-0xFFFF) ([ExtId12-0][IDE][RTR] 0) (0="don't care") - sFilterConfig.FilterMaskIdHigh = 0b0000; // Mask MSB: (0-0xFFFF) (StdId[10-0] [ExtId17-13]) (See Figure 342 in RM0090) + sFilterConfig.FilterMaskIdHigh = 0b0000; // Mask MSB: (0-0xFFFF) (StdId[10-0] [ExtId17-13]) (See Figure 342 in RM0090) sFilterConfig.FilterMaskIdLow = 0x0000; // Mask LSB: (0-0xFFFF) ([ExtId12-0][IDE][RTR] 0) (0="don't care") sFilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO1; // Store message in FIFO0 (CAN_FILTER_FIFO0 / CAN_FILTER_FIFO1) sFilterConfig.FilterActivation = CAN_FILTER_ENABLE; // CAN_FILTER_ENABLE / CAN_FILTER_DISABLE @@ -644,10 +652,10 @@ HAL_StatusTypeDef CAN1_Start(void) // Activate RX FIFO0/FIFO1 new message interrupt HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING); // Calls CAN1_RX0_IRQHandler -> HAL_CAN_IRQHandler -> HAL_CAN_RxFifo0MsgPendingCallback HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO1_MSG_PENDING); // Calls CAN1_RX1_IRQHandler -> HAL_CAN_IRQHandler -> HAL_CAN_RxFifo1MsgPendingCallback - + // Activate RX FIFO0/FIFO1 overrun interrupt HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_OVERRUN); // Calls CAN1_RX0_IRQHandler -> HAL_CAN_ErrorCallback - HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO1_OVERRUN); // Calls CAN1_RX1_IRQHandler -> HAL_CAN_ErrorCallback + HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO1_OVERRUN); // Calls CAN1_RX1_IRQHandler -> HAL_CAN_ErrorCallback HAL_CAN_ActivateNotification(&hcan1, CAN_IT_ERROR); // Calls CAN1_RX0_IRQHandler -> HAL_CAN_ErrorCallback @@ -658,39 +666,31 @@ HAL_StatusTypeDef CAN1_Start(void) return CAN_Send_Gcode_2params('M', 997, 0, 0, 0, 0); // M997, reset head at host startup } -void HAL_CAN_RxFifoMsgPending(CAN_HandleTypeDef *hcan, uint32_t RxFifo) // ISR! New FIFO 0/1 message interrupt handler -{ +void HAL_CAN_RxFifoMsgPending(CAN_HandleTypeDef *hcan, uint32_t RxFifo) { // ISR! New FIFO 0/1 message interrupt handler CAN_RxHeaderTypeDef RxHeader; // RX Header buffer for FIFO0 uint8_t CAN_RX_buffer_Fifo[8]; // CAN MESSAGE DATA BUFFER - if (HAL_CAN_GetRxMessage(&hcan1, RxFifo, &RxHeader, CAN_RX_buffer_Fifo) == HAL_OK) // Get message from CAN_RX_FIFO0 - { - if ((RxHeader.StdId & CAN_IO_MASK) != CAN_io_state) // First handle time critical IO update - { + if (HAL_CAN_GetRxMessage(&hcan1, RxFifo, &RxHeader, CAN_RX_buffer_Fifo) == HAL_OK) { // Get message from CAN_RX_FIFO0 + if ((RxHeader.StdId & CAN_IO_MASK) != CAN_io_state) { // First handle time critical IO update CAN_io_state = (RxHeader.StdId & CAN_IO_MASK); endstops.update(); } - if (RxHeader.StdId & CAN_STRING_MESSAGE_MASK) // Head sends a string message - { + if (RxHeader.StdId & CAN_STRING_MESSAGE_MASK) { // Head sends a string message char * CAN_RX_p = (char *)CAN_RX_buffer_Fifo; - for (uint32_t i = 0; i < RxHeader.DLC; i++) - { + for (uint32_t i = 0; i < RxHeader.DLC; i++) { string_message[string_message_index++ % 128] = CAN_RX_p[i]; // Copy message to global buffer - - if (CAN_RX_p[i] == '\n') - { + + if (CAN_RX_p[i] == '\n') { string_message_complete = true; // Print buffer string_message[string_message_index % 128] = 0; // Close string with \0 } } } - else - if (RxHeader.DLC == 4) // FIFO0, head sent a temperature update (DLC = Data Length Code == 4 bytes) - { + else if (RxHeader.DLC == 4) { // FIFO0, head sent a temperature update (DLC = Data Length Code == 4 bytes) float * fp = (float *)CAN_RX_buffer_Fifo; // FIFO0 thermalManager.temp_hotend[0].celsius = *fp; // Set E0 hotend temperature - Last_CAN_Temp_Report = millis(); + Next_CAN_Temp_Report = millis() + 10000; FirstE0Error = true; // Reset error status } @@ -699,18 +699,15 @@ void HAL_CAN_RxFifoMsgPending(CAN_HandleTypeDef *hcan, uint32_t RxFifo) // ISR! } } -void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) // ISR! New FIFO0 message interrupt handler -{ - HAL_CAN_RxFifoMsgPending(hcan, CAN_RX_FIFO0); +void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) { // ISR! New FIFO0 message interrupt handler + HAL_CAN_RxFifoMsgPending(hcan, CAN_RX_FIFO0); } -void HAL_CAN_RxFifo1MsgPendingCallback(CAN_HandleTypeDef *hcan) // ISR! New FIFO1 message interrupt handler -{ - HAL_CAN_RxFifoMsgPending(hcan, CAN_RX_FIFO1); +void HAL_CAN_RxFifo1MsgPendingCallback(CAN_HandleTypeDef *hcan) { // ISR! New FIFO1 message interrupt handler + HAL_CAN_RxFifoMsgPending(hcan, CAN_RX_FIFO1); } -void HAL_CAN_ErrorCallback(CAN_HandleTypeDef *hcan) // Interrupt handler for any CAN error -{ +void HAL_CAN_ErrorCallback(CAN_HandleTypeDef *hcan) { // Interrupt handler for any CAN error HAL_CAN_error_code = hcan->ErrorCode; // Store the received error code } @@ -749,4 +746,4 @@ CAN WEAK CALLBACKS WHEN USING STANDARD WEAK CAN1_RX0_IRQHandler------------> INT __weak void HAL_CAN_ErrorCallback(CAN_HandleTypeDef *hcan) // CAN_IT_ERROR */ -#endif // CAN_MASTER \ No newline at end of file +#endif // CAN_MASTER diff --git a/Marlin/src/HAL/STM32/CAN.h b/Marlin/src/HAL/STM32/CAN.h deleted file mode 100644 index 32dc76a2ec21..000000000000 --- a/Marlin/src/HAL/STM32/CAN.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "../../inc/MarlinConfig.h" - -#ifdef CAN_MASTER - - 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_E0_TARGET_MASK 256 // Signals E0 or E1 - #define CAN_ERROR_MASK 512 // Signals the head encountered an error - - HAL_StatusTypeDef CAN1_Start(void); // FUNCTION PROTOTYPES - HAL_StatusTypeDef CAN1_Stop(void); - HAL_StatusTypeDef CAN_Send_Gcode(void); - 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 - -#endif // CAN_MASTER \ No newline at end of file diff --git a/Marlin/src/HAL/STM32/Servo.cpp b/Marlin/src/HAL/STM32/Servo.cpp index dc6888ff9d11..5ef3e62f1c6a 100644 --- a/Marlin/src/HAL/STM32/Servo.cpp +++ b/Marlin/src/HAL/STM32/Servo.cpp @@ -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; @@ -72,19 +76,16 @@ int8_t libServo::attach(const int pin, const int min, const int max) { void libServo::move(const int value) { -#ifdef CAN_MASTER // IRON, FORWARD UNDERWATER SERVO COMMAND TO HEAD - int angles[2] = Z_SERVO_ANGLES; - - // Translate M280 S10 to M401, M280 S90 to M402 - HAL_StatusTypeDef CAN_Send_Gcode_2params(uint32_t Gcode_type, uint32_t Gcode_no, uint32_t parameter1, float value1, uint32_t parameter2, float value2); // PROTOTYPE - if (value == angles[0]) // Deploy angle - CAN_Send_Gcode_2params('M', 401, 0, 0, 0, 0); // IRON, send "M401" instead, enables interrupt etc. - else - if (value == angles[1]) // Stow angle - CAN_Send_Gcode_2params('M', 402, 0, 0, 0, 0); // IRON, send "M401" instead, enables interrupt etc. - else - CAN_Send_Gcode_2params('M', 280, 'S', value, 'P', 0); // M280 S[value] P0 -#endif // IRON + #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); diff --git a/Marlin/src/HAL/shared/CAN.h b/Marlin/src/HAL/shared/CAN.h new file mode 100644 index 000000000000..c8e410241293 --- /dev/null +++ b/Marlin/src/HAL/shared/CAN.h @@ -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 . + * + */ +#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_E0_TARGET_MASK 256 // Signals E0 or E1 +#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 diff --git a/Marlin/src/MarlinCore.cpp b/Marlin/src/MarlinCore.cpp index 3b73fd8b5014..d04600383323 100644 --- a/Marlin/src/MarlinCore.cpp +++ b/Marlin/src/MarlinCore.cpp @@ -34,8 +34,8 @@ #include "HAL/shared/esp_wifi.h" #include "HAL/shared/cpu_exception/exception_hook.h" -#ifdef CAN_MASTER // IRON, ADDED - #include HAL_PATH(., CAN.h) +#if ENABLED(CAN_MASTER) + #include "HAL/shared/CAN.h" #endif #if ENABLED(WIFISUPPORT) @@ -1194,14 +1194,15 @@ void setup() { while (!MYSERIAL3.connected() && PENDING(millis(), serial_connect_timeout)) { /*nada*/ } #endif #endif - SERIAL_ECHOLNPGM("\nstart\n"); // IRON, ADDED \n -#ifdef CAN_MASTER // IRON, REPORT CAN START STATUS - if (CAN1_Start() == HAL_OK) - SERIAL_ECHOLNPGM(">>> CAN1 Start: OK"); - else - SERIAL_ECHOLNPGM(">>> CAN1 Start: FAILED!"); -#endif + SERIAL_ECHOLNPGM(TERN(CAN_MASTER, "\nstart\n", "start")); + + #if ENABLED(CAN_MASTER) + SERIAL_ECHOLN( + F(">>> CAN1 Start: "), + CAN1_Start() == HAL_OK ? F("OK") : F("FAILED!") + ); + #endif // Set up these pins early to prevent suicide #if HAS_KILL diff --git a/Marlin/src/feature/pause.cpp b/Marlin/src/feature/pause.cpp index 3af6b59709e4..3589847761c5 100644 --- a/Marlin/src/feature/pause.cpp +++ b/Marlin/src/feature/pause.cpp @@ -225,13 +225,8 @@ bool load_filament(const_float_t slow_load_length/*=0*/, const_float_t fast_load switch (active_extruder) { REPEAT_1(NUM_RUNOUT_SENSORS, _CASE_INSERTED) } - #else - #ifdef CAN_MASTER // IRON, USE VIRTUAL CAN FILAMENT RUNOUT STATUS - if ((!!(CAN_io_state & CAN_FILAMENT_MASK)) != FIL_RUNOUT_STATE) - wait_for_user = false; #else if (!FILAMENT_IS_OUT()) wait_for_user = false; - #endif // IRON #endif #endif idle_no_sleep(); diff --git a/Marlin/src/feature/runout.h b/Marlin/src/feature/runout.h index 8ecb94a21b7f..8cbad8e61eb2 100644 --- a/Marlin/src/feature/runout.h +++ b/Marlin/src/feature/runout.h @@ -33,8 +33,8 @@ #include "pause.h" // for did_pause_print #include "../MarlinCore.h" // for printingIsActive() -#ifdef CAN_MASTER // IRON, ADDED - #include HAL_PATH(.., CAN.h) +#if ENABLED(CAN_MASTER) + #include "../HAL/shared/CAN.h" #endif #include "../inc/MarlinConfig.h" @@ -57,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 @@ -211,13 +217,7 @@ class FilamentSensorBase { // Return a bitmask of runout pin states static uint8_t poll_runout_pins() { - -#ifdef CAN_MASTER // IRON, VIRTUAL FILAMENT RUNOUT PIN - #define _OR_RUNOUT(N) | ((CAN_io_state & CAN_FILAMENT_MASK) ? _BV((N) - 1) : 0) -#else - #define _OR_RUNOUT(N) | (READ(FIL_RUNOUT##N##_PIN) ? _BV((N) - 1) : 0) -#endif // IRON - + #define _OR_RUNOUT(N) | (RUNOUT_STATE(N) ? _BV((N) - 1) : 0) return (0 REPEAT_1(NUM_RUNOUT_SENSORS, _OR_RUNOUT)); #undef _OR_RUNOUT } diff --git a/Marlin/src/gcode/gcode.cpp b/Marlin/src/gcode/gcode.cpp index 53d84ac27cb4..3744a384886b 100644 --- a/Marlin/src/gcode/gcode.cpp +++ b/Marlin/src/gcode/gcode.cpp @@ -28,10 +28,6 @@ #include "gcode.h" GcodeSuite gcode; -#ifdef CAN_MASTER // IRON - #include HAL_PATH(.., CAN.h) -#endif - #if ENABLED(WIFI_CUSTOM_COMMAND) extern bool wifi_custom_command(char * const command_ptr); #endif @@ -73,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 @@ -327,16 +328,14 @@ void GcodeSuite::process_parsed_command(const bool no_ok/*=false*/) { KEEPALIVE_STATE(IN_HANDLER); -#ifdef CAN_MASTER - if (CAN_Send_Gcode() != HAL_OK) // IRON, SEND COMMAND TO HEAD - { - SERIAL_ECHOPGM("Error: FAILED TO SEND GCODE CAN COMMAND TO HEAD: "); - SERIAL_ECHOLN_P(parser.command_ptr); - #ifndef IRON_DEBUG - BUZZ(1, SOUND_ERROR); + #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 - } -#endif // IRON /** * Block all Gcodes except M511 Unlock Printer, if printer is locked diff --git a/Marlin/src/gcode/temp/M306.cpp b/Marlin/src/gcode/temp/M306.cpp index 57ed2518ff9e..4b4b69c287dc 100644 --- a/Marlin/src/gcode/temp/M306.cpp +++ b/Marlin/src/gcode/temp/M306.cpp @@ -27,7 +27,6 @@ #include "../gcode.h" #include "../../lcd/marlinui.h" #include "../../module/temperature.h" -#include "../../libs/numtostr.h" // IRON, ADDED /** * M306: MPC settings and autotune @@ -59,23 +58,30 @@ void GcodeSuite::M306() { #if ENABLED(MPC_AUTOTUNE) if (parser.seen_test('T')) { -#ifdef CAN_MASTER // IRON, MPC AUTOTUNE INFO - SERIAL_ECHOLNPGM(">>> Forwarding M306 to head board"); - SERIAL_ECHOLNPGM(">>> Store MPC setup in the host Configuration.h or use M500"); - SERIAL_ECHOLNPGM(">>> MPC heater power is: ", ftostr31ns(MPC_HEATER_POWER), " Watt"); - SERIAL_ECHOLNPGM(">>> 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(); -#endif // IRON + #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(); + + #endif + return; } #endif @@ -101,11 +107,10 @@ void GcodeSuite::M306_report(const bool forReplay/*=true*/) { report_heading(forReplay, F("Model predictive control")); -#ifdef CAN_MASTER // IRON, MPC AUTOTUNE INFO - if (forReplay) - SERIAL_ECHOLNPGM(">>> HOST M306 MPC SETTINGS:"); -#endif - + #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; diff --git a/Marlin/src/module/endstops.cpp b/Marlin/src/module/endstops.cpp index 5ce8f43b3551..dcb10c014edd 100644 --- a/Marlin/src/module/endstops.cpp +++ b/Marlin/src/module/endstops.cpp @@ -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" @@ -77,14 +81,14 @@ Endstops::endstop_mask_t Endstops::live_state = 0; #else #define READ_ENDSTOP(P) READ(P) #endif -#else - -#ifdef CAN_MASTER // IRON, READ VIRTUAL CAN IO PROBE STATUS IF NEEDED - #define READ_ENDSTOP(P) ((P == Z_MIN_PIN) ? ((CAN_io_state & CAN_PROBE_MASK)) : READ(P)) +#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 // IRON, ADDED - #endif #if ENDSTOP_NOISE_THRESHOLD @@ -531,13 +535,10 @@ void __O2 Endstops::report_states() { print_es_state(extDigitalRead(pin) != state); } #undef _CASE_RUNOUT + #elif HAS_FILAMENT_SENSOR - #ifdef CAN_MASTER // IRON - print_es_state((!!(CAN_io_state & CAN_FILAMENT_MASK)) != FIL_RUNOUT1_STATE, F(STR_FILAMENT)); - #else - print_es_state(READ(FIL_RUNOUT1_PIN) != FIL_RUNOUT1_STATE, F(STR_FILAMENT)); - #endif // IRON + print_es_state(RUNOUT_STATE(1) != FIL_RUNOUT1_STATE, F(STR_FILAMENT)); #endif diff --git a/Marlin/src/module/probe.h b/Marlin/src/module/probe.h index e849a28324f5..41d59bb91afb 100644 --- a/Marlin/src/module/probe.h +++ b/Marlin/src/module/probe.h @@ -29,8 +29,8 @@ #include "motion.h" -#ifdef CAN_MASTER // IRON - #include HAL_PATH(.., CAN.h) +#if ENABLED(CAN_MASTER) + #include "../HAL/shared/CAN.h" #endif #if ENABLED(BLTOUCH) @@ -51,23 +51,13 @@ #if ENABLED(BD_SENSOR) #define PROBE_READ() bdp_state +#elif ENABLED(CAN_MASTER) + #define PROBE_READ() bool(CAN_io_state & CAN_PROBE_MASK) #elif USE_Z_MIN_PROBE - - #ifdef CAN_MASTER - #define PROBE_READ() ((CAN_io_state & CAN_PROBE_MASK)) // IRON, READ VIRTUAL IO - #else - #define PROBE_READ() READ(Z_MIN_PROBE_PIN) - #endif // IRON - + #define PROBE_READ() READ(Z_MIN_PROBE_PIN) #else - - #ifdef CAN_MASTER // IRON, VIRTUAL IO - #define PROBE_READ() ((CAN_io_state & CAN_PROBE_BIT)) // IRON, READ VIRTUAL IO - #else - #define PROBE_READ() READ(Z_MIN_PIN) - #endif - -#endif // IRON + #define PROBE_READ() READ(Z_MIN_PIN) +#endif #if USE_Z_MIN_PROBE #define PROBE_HIT_STATE Z_MIN_PROBE_ENDSTOP_HIT_STATE diff --git a/Marlin/src/module/temperature.cpp b/Marlin/src/module/temperature.cpp index 1f78f4f9ffb5..fa984176d8a5 100644 --- a/Marlin/src/module/temperature.cpp +++ b/Marlin/src/module/temperature.cpp @@ -50,6 +50,10 @@ #include "motion.h" #endif +#if ENABLED(CAN_MASTER) + #include "../HAL/shared/CAN.h" +#endif + #if ENABLED(DWIN_CREALITY_LCD) #include "../lcd/e3v2/creality/dwin.h" #elif ENABLED(SOVOL_SV06_RTS) @@ -2734,11 +2738,9 @@ void Temperature::updateTemperaturesFromRawValues() { temp_bed.setraw(read_max_tc_bed()); #endif -#ifndef CAN_MASTER // IRON, NOT FOR MASTER, DON'T READ TEMPERATURE FROM SENSOR, GET IT VIA CAN BUS FROM HEAD - #if HAS_HOTEND + #if HAS_HOTEND && DISABLED(CAN_MASTER) // For CAN Master we'll get temperature from CAN bus HOTEND_LOOP() temp_hotend[e].celsius = analog_to_celsius_hotend(temp_hotend[e].getraw(), e); #endif -#endif // IRON TERN_(HAS_HEATED_BED, temp_bed.celsius = analog_to_celsius_bed(temp_bed.getraw())); TERN_(HAS_TEMP_CHAMBER, temp_chamber.celsius = analog_to_celsius_chamber(temp_chamber.getraw())); @@ -2751,9 +2753,7 @@ void Temperature::updateTemperaturesFromRawValues() { TERN_(FILAMENT_WIDTH_SENSOR, filwidth.update_measured_mm()); TERN_(HAS_POWER_MONITOR, power_monitor.capture_values()); - #if HAS_HOTEND - -#ifndef CAN_MASTER // IRON, ONLY FOR HEAD, NO TEMP SAMPLING ON MASTER + #if HAS_HOTEND && DISABLED(CAN_MASTER) // Only for Head, no temp sampling on Master #define _TEMPDIR(N) TEMP_SENSOR_IS_ANY_MAX_TC(N) ? 0 : TEMPDIR(N), static constexpr int8_t temp_dir[HOTENDS] = { REPEAT(HOTENDS, _TEMPDIR) }; @@ -2780,9 +2780,8 @@ void Temperature::updateTemperaturesFromRawValues() { TERN_(MULTI_MAX_CONSECUTIVE_LOW_TEMP_ERR, consecutive_low_temperature_error[e] = 0); } } -#endif // IRON, !CAN_MASTER - #endif // HAS_HOTEND + #endif // HAS_HOTEND && !CAN_MASTER #if ENABLED(THERMAL_PROTECTION_BED) if (TP_CMP(BED, temp_bed.getraw(), temp_sensor_range_bed.raw_max)) @@ -3382,16 +3381,17 @@ void Temperature::disable_all_heaters() { #if HAS_HOTEND -#ifdef CAN_MASTER // IRON, SHUTDOWN HOTEND IN HEAD TOO - CAN_Send_Gcode_2params('M', 104, 'S', 0, 0, 0); // IRON, M104 S0, switch off hotend heating - CAN_Send_Gcode_2params('M', 107, 0, 0, 0, 0); // IRON, M107, switch off part cooling fan - CAN_Send_Gcode_2params('M', 150, 'R', 255, 0, 0); // IRON, M150 R255, SET NEOPIXEL TO RED -#endif + #if ENABLED(CAN_MASTER) // Shut down the hotend in the head too + CAN_Send_Gcode_2params('M', 104, 'S', 0, 0, 0); // M104 S0 .... Switch off hotend heating + CAN_Send_Gcode_2params('M', 107, 0, 0, 0, 0); // M107 ....... Switch off part cooling fan + CAN_Send_Gcode_2params('M', 150, 'R', 255, 0, 0); // M150 R255 .. Set NeoPixel to red + #endif HOTEND_LOOP() { setTargetHotend(0, e); temp_hotend[e].soft_pwm_amount = 0; } + #endif #if HAS_TEMP_HOTEND diff --git a/Marlin/src/pins/pins.h b/Marlin/src/pins/pins.h index eb26103380c1..f9b0ee25235a 100644 --- a/Marlin/src/pins/pins.h +++ b/Marlin/src/pins/pins.h @@ -800,7 +800,7 @@ #elif MB(MKS_MONSTER8_V1) #include "stm32f4/pins_MKS_MONSTER8_V1.h" // STM32F4 env:mks_monster8 env:mks_monster8_usb_flash_drive env:mks_monster8_usb_flash_drive_msc #elif MB(MKS_MONSTER8_V2) - #include "stm32f4/pins_MKS_MONSTER8_V2.h" // STM32F4 env:mks_monster8 env:mks_monster8_usb_flash_drive env:mks_monster8_usb_flash_drive_msc + #include "stm32f4/pins_MKS_MONSTER8_V2.h" // STM32F4 env:mks_monster8 env:mks_monster8_usb_flash_drive env:mks_monster8_usb_flash_drive_msc env:mks_monster8_usb_flash_drive_msc_CAN #elif MB(ANET_ET4) #include "stm32f4/pins_ANET_ET4.h" // STM32F4 env:Anet_ET4_no_bootloader env:Anet_ET4_OpenBLT #elif MB(ANET_ET4P) diff --git a/Marlin/src/pins/stm32g0/pins_BTT_EBB42_V1_1.h b/Marlin/src/pins/stm32g0/pins_BTT_EBB42_V1_1.h index 2d6cdb73f43c..fef28f3f3682 100644 --- a/Marlin/src/pins/stm32g0/pins_BTT_EBB42_V1_1.h +++ b/Marlin/src/pins/stm32g0/pins_BTT_EBB42_V1_1.h @@ -27,7 +27,7 @@ * This board definition is to facilitate support for a Filament Extrusion * devices, used to convert waste plastic into 3D printable filament. * This board is NOT a general 3D printing controller; it is NOT supported - * as a toolboard via CANBUS (as it was originally designed) or any device + * as a toolboard via CAN bus (as it was originally designed) or any device * that requires kinematics. */ diff --git a/ini/stm32f4.ini b/ini/stm32f4.ini index b90b46ae7375..fbe68125092c 100644 --- a/ini/stm32f4.ini +++ b/ini/stm32f4.ini @@ -619,6 +619,7 @@ build_flags = ${stm_flash_drive.build_flags} ${stm32f4_I2C1_CAN.build_flag extends = env:mks_monster8_usb_flash_drive build_flags = ${env:mks_monster8_usb_flash_drive.build_flags} -DUSBD_USE_CDC_MSC + [env:mks_monster8_usb_flash_drive_msc_CAN] extends = env:mks_monster8_usb_flash_drive build_flags = ${env:mks_monster8_usb_flash_drive.build_flags} diff --git a/platformio.ini b/platformio.ini index 88e468c49e28..ed1670dc6daf 100644 --- a/platformio.ini +++ b/platformio.ini @@ -13,7 +13,7 @@ [platformio] src_dir = Marlin boards_dir = buildroot/share/PlatformIO/boards -default_envs = #define MOTHERBOARD BOARD_MKS_MONSTER8_V2 +default_envs = mega2560 include_dir = Marlin extra_configs = Marlin/config.ini From 5b97aefb5b7d636a89288749cb228be13352f99e Mon Sep 17 00:00:00 2001 From: rondlh <77279634+rondlh@users.noreply.github.com> Date: Sat, 30 Nov 2024 05:54:26 +0800 Subject: [PATCH 04/12] Added FDCAN tool head part Added FDCAN tool head part Added NTP style CAN bus time sync (test) --- Marlin/Configuration.h | 1 + Marlin/src/HAL/STM32/CAN.cpp | 83 ++- Marlin/src/HAL/STM32/FDCAN.cpp | 588 ++++++++++++++++++ Marlin/src/HAL/shared/CAN.h | 22 +- Marlin/src/HAL/shared/FDCAN.h | 48 ++ Marlin/src/MarlinCore.cpp | 24 +- Marlin/src/gcode/temp/M306.cpp | 21 + Marlin/src/module/endstops.cpp | 6 + Marlin/src/module/temperature.cpp | 7 + Marlin/src/pins/pins.h | 4 +- .../pins/stm32f4/pins_MKS_MONSTER8_common.h | 11 +- Marlin/src/pins/stm32g0/pins_BTT_EBB42_V1_1.h | 3 + ini/stm32g0.ini | 16 + 13 files changed, 792 insertions(+), 42 deletions(-) create mode 100644 Marlin/src/HAL/STM32/FDCAN.cpp create mode 100644 Marlin/src/HAL/shared/FDCAN.h diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h index 2cdffe5179ac..e315634fbb30 100644 --- a/Marlin/Configuration.h +++ b/Marlin/Configuration.h @@ -126,6 +126,7 @@ // Enable CAN bus support and protocol //#define CAN_MASTER +//#define CAN_TOOLHEAD // Enable the Bluetooth serial interface on AT90USB devices //#define BLUETOOTH diff --git a/Marlin/src/HAL/STM32/CAN.cpp b/Marlin/src/HAL/STM32/CAN.cpp index 89292f986d3f..27c3bb85d7ae 100644 --- a/Marlin/src/HAL/STM32/CAN.cpp +++ b/Marlin/src/HAL/STM32/CAN.cpp @@ -64,7 +64,7 @@ #define CAN_STRING_MESSAGE_MASK 32 // Signals the head sent 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_E0_TARGET_MASK 256 // Signals E0 or E1 + #define CAN_REQUEST_TIME_SYNC_MASK 256 // Signals E0 or E1 #define CAN_ERROR_MASK 512 // Signals the head encountered an error #define PARAMETER1_OFFSET 0 @@ -78,6 +78,8 @@ #define GCODE_TYPE_M 2 #define GCODE_TYPE_T 3 +#define GCODE_TIME_SYNC_NO 7777 // Just a unique unused number M7777 + extern "C" void CAN1_RX0_IRQHandler(); // Override weak CAN FIFO0 interrupt handler extern "C" void CAN1_RX1_IRQHandler(); // Override weak CAN FIFO1 interrupt handler extern "C" void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan); // Override weak CAN interrupt callback for FIFO0 @@ -88,7 +90,9 @@ CAN_HandleTypeDef hcan1 = { 0 }; // The CAN1 handle CAN_TxHeaderTypeDef TxHeader = { 0 }; // Header to send a CAN message volatile uint32_t CAN_io_state = 0; // Virtual IO state variable volatile bool CAN_head_error = 0; // Register if an error was reported by the head -volatile bool CAN_head_setup_request = false; // Signals the head requesting setup information +volatile bool CAN_head_setup_request = false; // Signals the head requested setup information +volatile bool CAN_time_sync_request = false; // Signals the head requested a time sync +volatile uint32_t time_sync_request_time = 0; // Record the time the time sync request was received volatile uint32_t gcode_counter = 0; // Count amount of gcodes received volatile uint32_t HAL_CAN_error_code = 0; // Record a host CAN error message @@ -111,6 +115,37 @@ void CAN1_RX1_IRQHandler() { // CAN FIFO1 Interrupt handler overrides standard w //HAL_CAN_RxFifo1MsgPendingCallback(&hcan1); // Call the required callback directly, faster but no error reporting } +// Send sync timestamp +HAL_StatusTypeDef CAN_Send_Timestamp() // Request receive timestamp + request response timestamp +{ + HAL_StatusTypeDef status = HAL_OK; + uint32_t TxMailbox; // Stores which Mailbox (0-2) is used to store the Sent TX message + + TxHeader.IDE = CAN_EXTENDED_ID; + TxHeader.DLC = 8; // Send sync time t1(receive time, uint32_t) and t2(response time, uint32_t) + TxHeader.StdId = (TxHeader.StdId ^ STDID_FIFO_BIT); // Toggle FIFO bit 10, keep FIFO toggling in sync + TxHeader.ExtId = ((TxHeader.ExtId ^ EXTID_FIFO_BIT) & EXTID_FIFO_BIT) + // Toggle FIFO bit 28 + ((TxHeader.DLC >> 2) << PARAMETER_COUNT_OFFSET) + // Data bytes (4 or 8) + (GCODE_TYPE_M << GCODE_TYPE_OFFSET) + // G/M/T/D-code + ((GCODE_TIME_SYNC_NO & GCODE_NUMBER_MASK) << GCODE_NUMBER_OFFSET) + // Gcode number + ((2 & PARAMETER_MASK) << PARAMETER2_OFFSET) + // Second parameter + ((1 & PARAMETER_MASK) << PARAMETER1_OFFSET); // First parameter + uint8_t CAN_tx_buffer[8]; // 8 bytes CAN data TX buffer + uint32_t * uint32p = (uint32_t *)CAN_tx_buffer; // Point to TX buffer + *uint32p++ = time_sync_request_time; + + uint32_t ms = millis(); // Wait until the FIFO is empty + while ((HAL_CAN_GetTxMailboxesFreeLevel(&hcan1) < 3) && (millis() - ms < 25)) { } // BLOCKING! Wait max 25ms! + + *uint32p = micros(); // Only record the response time at the last possible moment + status = HAL_CAN_AddTxMessage(&hcan1, &TxHeader, CAN_tx_buffer, &TxMailbox); // Send message + + if (status == HAL_OK) // Count sent gcode messages + gcode_counter++; + + return status; +} + // Send specified Gcode with max 2 parameters and 2 values via CAN bus HAL_StatusTypeDef CAN_Send_Gcode_2params(uint32_t Gcode_type, uint32_t Gcode_no, uint32_t parameter1, float value1, uint32_t parameter2, float value2) { switch (Gcode_type) { @@ -169,12 +204,12 @@ HAL_StatusTypeDef CAN_Send_Gcode_2params(uint32_t Gcode_type, uint32_t Gcode_no, ((TxHeader.DLC >> 2) << PARAMETER_COUNT_OFFSET) + // Data bytes (4 or 8) (Gcode_type << GCODE_TYPE_OFFSET) + // G/M/T/D-code ((Gcode_no & GCODE_NUMBER_MASK) << GCODE_NUMBER_OFFSET) + // Gcode number - ((parameter2 & PARAMETER_MASK) << PARAMETER2_OFFSET) + // First parameter - ((parameter1 & PARAMETER_MASK) << PARAMETER1_OFFSET); // Second parameter + ((parameter2 & PARAMETER_MASK) << PARAMETER2_OFFSET) + // Second parameter + ((parameter1 & PARAMETER_MASK) << PARAMETER1_OFFSET); // First parameter uint8_t CAN_tx_buffer[8]; // 8 bytes CAN data TX buffer float * fp = (float *)CAN_tx_buffer; // Point to TX buffer *fp++ = value1; - *fp-- = value2; + *fp = value2; const uint32_t ms = millis(); // Don't send too fast! while ((HAL_CAN_GetTxMailboxesFreeLevel(&hcan1) == 0) && (millis() - ms < 25)) { } // BLOCKING! Wait max 25ms! @@ -272,27 +307,20 @@ HAL_StatusTypeDef CAN_Send_Setup() { // Send setup to HEAD CAN_Send_Gcode_2params('M', 919, 'O', off, 'P' , Hysteresis End); CAN_Send_Gcode_2params('M', 919, 'S', Hysteresis Start, 0, 0); */ - #if USE_CONTROLLER_FAN - /* - CAN_Send_Gcode_2params('M', 710, 'E', 1, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P - CAN_Send_Gcode_2params('M', 710, 'E', 2, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P - CAN_Send_Gcode_2params('M', 710, 'E', 3, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P - CAN_Send_Gcode_2params('M', 710, 'E', 4, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P - CAN_Send_Gcode_2params('M', 710, 'E', 5, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P - CAN_Send_Gcode_2params('M', 710, 'E', 6, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P - CAN_Send_Gcode_2params('M', 710, 'E', 7, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P - CAN_Send_Gcode_2params('M', 710, 'E', 8, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P - CAN_Send_Gcode_2params('M', 710, 'E', 9, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P - CAN_Send_Gcode_2params('M', 710, 'E', 10, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P - CAN_Send_Gcode_2params('M', 710, 'E', 11, 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P - */ - // IRON, M710 SIGNALS TO THE HEAD THAT THE CAN CONFIGURATION IS COMPLETE, USE IT AS THE LAST GCODE TO SEND - return CAN_Send_Gcode_2params('M', 710, 'E', int(controllerFan.settings.extruder_auto_fan_speed), 'P', int(controllerFan.settings.probing_auto_fan_speed)); // M710 E P + #if ENABLED(USE_CONTROLLER_FAN) + // M710 SIGNALS TO THE HEAD THAT THE CAN CONFIGURATION IS COMPLETE, USE IT AS THE LAST GCODE TO SEND + return CAN_Send_Gcode_2params('M', 710, 'E', controllerFan.settings.extruder_auto_fan_speed, 0, 0); // M710 E #endif return HAL_OK; } -void CAN_Idle() { // Tasks that cannot be done in the ISR +void CAN_idle() { // Tasks that cannot be done in the ISR + if (CAN_time_sync_request) { + if (CAN_Send_Timestamp() == HAL_OK) { + CAN_time_sync_request = false; + } + } + if (CAN_head_setup_request) // The head requested the setup data CAN_Send_Setup(); @@ -493,8 +521,8 @@ HAL_StatusTypeDef CAN_Send_Gcode() { // Forward a Marlin Gcode via CAN (uses par values[parameter_counter++] = NAN; // Not A Number, indicates no parameter value is present } - if (parameter_counter == 8) { // Max 8 parameters - parameter_counter--; // Max is 7 parameters + if (parameter_counter == 8) { // Max is 7 parameters + parameter_counter--; SERIAL_ECHOLNPGM("\nError: TOO MANY PARAMETERS (> 7): ", parser.command_ptr); BUZZ(1, SOUND_ERROR); break; @@ -694,7 +722,12 @@ void HAL_CAN_RxFifoMsgPending(CAN_HandleTypeDef *hcan, uint32_t RxFifo) { // ISR FirstE0Error = true; // Reset error status } - CAN_head_setup_request = (RxHeader.StdId & CAN_REQUEST_SETUP_MASK) > 0; // FIFO0, head signals request for data + if (RxHeader.StdId & CAN_REQUEST_TIME_SYNC_MASK) {; // FIFO0, head signals request for time stamp + time_sync_request_time = micros(); // Record the time sync request receive time + CAN_time_sync_request = true; + } + + CAN_head_setup_request = (RxHeader.StdId & CAN_REQUEST_SETUP_MASK) > 0; // FIFO0, head signals request for setup data CAN_head_error = (RxHeader.StdId & CAN_ERROR_MASK) > 0; // FIFO0, head signals an error } } diff --git a/Marlin/src/HAL/STM32/FDCAN.cpp b/Marlin/src/HAL/STM32/FDCAN.cpp new file mode 100644 index 000000000000..a942acdbe7c7 --- /dev/null +++ b/Marlin/src/HAL/STM32/FDCAN.cpp @@ -0,0 +1,588 @@ +/** + * 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 . + * + */ + +#include "../../inc/MarlinConfigPre.h" + +#if ENABLED(CAN_TOOLHEAD) + +#include "../platforms.h" +#include "../../gcode/gcode.h" +#include "../../gcode/parser.h" +#include "../../gcode/queue.h" +#include "../../module/temperature.h" +#include "../../libs/numtostr.h" +#include "../../inc/MarlinConfig.h" +#include "../../feature/controllerfan.h" +#include "../../core/serial.h" + +#include "../SHARED/FDCAN.h" + +#define CAN_DEBUG // Enable to show CAN debug messages + +#define FDCAN_RX_FIFO0_MASK (FDCAN_IR_RF0L | FDCAN_IR_RF0F | FDCAN_IR_RF0N) // Fifo 0: Message lost | Fifo full | New message +#define FDCAN_RX_FIFO1_MASK (FDCAN_IR_RF1L | FDCAN_IR_RF1F | FDCAN_IR_RF1N) // Fifo 1: Message lost | Fifo full | New message +#define FDCAN_RX_FIFO_MASK (FDCAN_RX_FIFO0_MASK | FDCAN_RX_FIFO1_MASK) // Fifo : Message lost | Fifo full | New message +#define HAL_TIM_FLAG_ALL (TIM_FLAG_CC1 | TIM_FLAG_CC2 | TIM_FLAG_CC3 | TIM_FLAG_CC4 | TIM_FLAG_UPDATE | TIM_FLAG_BREAK | TIM_FLAG_BREAK2 | TIM_FLAG_TRIGGER | TIM_FLAG_COM) + +#define CAN_TIMESTAMP_NO 7777 + +// IMPORTANT NOTE +// =============== +// 1. In \Users\\.platformio\packages\framework-arduinoststm32@4.20600.231001\libraries\SrcWrapper\src\HardwareTimer.cpp +// Add function "__weak" in front of "void TIM16_IRQHandler(void)" + +FDCAN_HandleTypeDef hfdcan2; + +bool Head_Not_configured = true; // Check if the configuration was send to the head +char gcode_type[4] = { 'D', 'G', 'M', 'D' }; // This way an extended ID is always > 2048 +//char * codeP; // code can walk along with progress in can_gcode +//char can_gcode[MAX_CMD_SIZE + 20]; +char can_gcode_buffer[MAX_CMD_SIZE + 20]; // Gcode that is being received in several CAN messages +volatile uint32_t CAN_Error_Code = 0; // Signals an CAN error occured, report to host +volatile uint32_t Old_Error_Code = 0; // Remember last error code so messages don't repeat +volatile uint32_t gcode_counter = 0; // Count received gcode lines (debugging) +volatile uint32_t CAN_message_counter = 0; // Count sent CAN message (debugging) +volatile bool send_gcode_count = false; +volatile uint32_t t[5] = { 0, 0, 0, 0, 0 }; // Timestamps for time sync +volatile bool CAN_request_time_sync = false; // Request a timestamp +volatile uint32_t time_offset = 0; // Time offset in micro seconds in relation to host time + +extern "C" void TIM16_IRQHandler(void); +extern "C" void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs); +extern "C" void HAL_FDCAN_RxFifo1Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo1ITs); + +void CAN_Add_Parameters(uint32_t parameter, float value, char *codeP) { // Add received CAN parameters to can_gcode string, used in ISR! + *codeP++ = char(parameter + 64); // Add parameter 'X' + + if (!isnan(value)) { // No value behind parameter if value is "Not A Number" + if (value < 0.0) { + *codeP++ = '-'; // Add minus sign to gcode string + value = -value; // Make value positive + } + + // Convert float to string + uint32_t i1 = int(value); // Get integer part of float + itoa(i1, codeP, 10); // Assume 1-4 digits + codeP += strlen(codeP); // Adjust pointer + value = (value - i1); // Get fractional part + + uint32_t j = 0; + uint32_t flag = 0; + for (int i = 0; i < 5; i++) { // 6 digits, 1 extra for rounding + value *= 10.0; + i1 = int(value); + j = (10 * j) + i1; + value = value - i1; + } + + if (value >= 0.5) + j++; // Round up + + if (j) { + *codeP++ = '.'; + if (j < 10000) { + *codeP++ = '0'; + if (j < 1000) { + *codeP++ = '0'; + if (j < 100) { + *codeP++ = '0'; + if (j < 10) + *codeP++ = '0'; + } + } + } + + while (!(j % 10)) // Remove trailing zeros, 120 --> 12 + j = j / 10; + + itoa(j, codeP, 10); + } + } +} + +HAL_StatusTypeDef CAN_Receive(uint32_t Fifo) { // ISR! Process received CAN message in interrupt handler! + HAL_StatusTypeDef status = HAL_OK; + static volatile uint32_t parameter_counter = 0; + char * codeP = can_gcode_buffer; // Put gcode pointer to start of can_gcode_buffer; + uint8_t can_rxbuf[8]; + + FDCAN_RxHeaderTypeDef CanRxHeader; // Receive CAN message buffer + + status = HAL_FDCAN_GetRxMessage(&hfdcan2, Fifo, &CanRxHeader, can_rxbuf); + if (status != HAL_OK) + return status; + + uint32_t identifier = CanRxHeader.Identifier; + if (CanRxHeader.IdType == FDCAN_EXTENDED_ID) { // Receiving new gcode + + if (((identifier >> IDENTIFIER_GCODE_NUMBER_OFFSET) & IDENTIFIER_GCODE_MASK) == ((GCODE_TYPE_M << (IDENTIFIER_GCODE_TYPE_OFFSET - IDENTIFIER_GCODE_NUMBER_OFFSET)) + CAN_TIMESTAMP_NO)) { // TIME SYNC RESPONSE MESSAGE + uint32_t *uint32p = (uint32_t *)can_rxbuf; + t[3] = micros(); // Record time sync response message receive time + t[1] = *uint32p++; + t[2] = *uint32p; + return status; + } + + // Check for M997 reset command + if (((identifier >> IDENTIFIER_GCODE_NUMBER_OFFSET) & IDENTIFIER_GCODE_MASK) == ((GCODE_TYPE_M << (IDENTIFIER_GCODE_TYPE_OFFSET - IDENTIFIER_GCODE_NUMBER_OFFSET)) + 997)) { // CODE IS M997, RESTART HEAD UNCONDITIONALLY + flashFirmware(0); + while (1); + } + + if (((identifier >> IDENTIFIER_GCODE_NUMBER_OFFSET) & IDENTIFIER_GCODE_MASK) == ((GCODE_TYPE_M << (IDENTIFIER_GCODE_TYPE_OFFSET - IDENTIFIER_GCODE_NUMBER_OFFSET)) + 115)) + CAN_request_time_sync = true; // M115 --> Request a time sync from the host + + memset(can_gcode_buffer, 0, MAX_CMD_SIZE); // Clear buffer + + if (parameter_counter) // Previous gcode must be complete, no more pending parameters + CAN_Error_Code |= CAN_ERROR_INCOMPLETE_GCODE_RECEIVED; + + parameter_counter = (identifier >> IDENTIFIER_PARAMETER_COUNT_OFFSET) & IDENTIFIER_PARAMETER_COUNT_MASK; // Get parameter_counter, bits 25, 26, 27 of 29 bit extended identifier + + *codeP++ = gcode_type[(identifier >> IDENTIFIER_GCODE_TYPE_OFFSET) & IDENTIFIER_GCODE_TYPE_MASK]; // "G" + itoa((identifier >> IDENTIFIER_GCODE_NUMBER_OFFSET) & IDENTIFIER_GCODE_NUMBER_MASK, codeP, 10); // G"92" + + // Check if head configuration was received, marked by M710 command + if (((identifier >> IDENTIFIER_GCODE_NUMBER_OFFSET) & IDENTIFIER_GCODE_MASK) == ((GCODE_TYPE_M << (IDENTIFIER_GCODE_TYPE_OFFSET - IDENTIFIER_GCODE_NUMBER_OFFSET)) + 710)) { // CODE IS M710, M=2, INDICATES CONFIGURATION WAS RECEIVED + Head_Not_configured = false; // HEAD SETUP COMPLETE + send_gcode_count = true; // Report the current received gcode count to the host (debugging) + } + } + + // If present, add parameters to the received gcode + if (parameter_counter && CanRxHeader.DataLength && ((identifier >> IDENTIFIER_PARAMTER1_OFFSET) & IDENTIFIER_PARAMETER_MASK)) { // Get 1st parameter, make sure it's not empty. + codeP += strlen(codeP); // There is already data in the buffer, adjust the pointer + CAN_Add_Parameters(identifier & IDENTIFIER_PARAMETER_MASK, *(float*)(can_rxbuf), codeP); + parameter_counter--; + + if (parameter_counter && (CanRxHeader.DataLength == FDCAN_DLC_BYTES_8) && ((identifier >> IDENTIFIER_PARAMTER2_OFFSET) & IDENTIFIER_PARAMETER_MASK)) { // Get 2nd parameter, make sure it's not empty. + codeP += strlen(codeP); + CAN_Add_Parameters((identifier >> IDENTIFIER_PARAMTER2_OFFSET) & IDENTIFIER_PARAMETER_MASK, *(float*)(can_rxbuf + 4), codeP); + parameter_counter--; + } + } + + if (parameter_counter == 0) { // Gcode is complete, process the gcode + gcode_counter++; + + uint32_t ms = millis(); + bool queue_status; + do { // BLOCKING! Wait for free Marlin command buffer for max 50ms + queue_status = queue.enqueue_one(can_gcode_buffer); + } + while (!queue_status && (millis() - ms < 50)); + + if (!queue_status) // Could not store the received CAN command in Marlin command buffer + CAN_Error_Code |= CAN_ERROR_MARLIN_CMD_BUFFER_OVERFLOW; + } + + return HAL_OK; +} + +void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs) { // ISR! Override "weak" function + if (RxFifo0ITs & FDCAN_IT_RX_FIFO0_MESSAGE_LOST) { // Check the error status first, it might be cleared by reading a message + CAN_Error_Code |= CAN_ERROR_FIFO_OVERFLOW; + __HAL_FDCAN_CLEAR_FLAG(hfdcan, FDCAN_IT_RX_FIFO0_MESSAGE_LOST); // Clear interrupt flag + } + + if (RxFifo0ITs & FDCAN_IT_RX_FIFO0_NEW_MESSAGE) + CAN_Receive(FDCAN_RX_FIFO0); +} + +void HAL_FDCAN_RxFifo1Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo1ITs) { // ISR! Override "weak" function + if (RxFifo1ITs & FDCAN_IT_RX_FIFO1_MESSAGE_LOST) { // Check the error status first, it might be cleared by reading a message + CAN_Error_Code |= CAN_ERROR_FIFO_OVERFLOW; + __HAL_FDCAN_CLEAR_FLAG(hfdcan, FDCAN_IT_RX_FIFO1_MESSAGE_LOST); // Clear interrupt flag + } + +if (RxFifo1ITs & FDCAN_IT_RX_FIFO1_NEW_MESSAGE) + CAN_Receive(FDCAN_RX_FIFO1); +} + +void TIM16_IRQHandler(void) { // FDCAN INTERRUPT HANDLER, OVERRIDE WEAK FUNCTION + if ((HardwareTimer_Handle[TIMER16_INDEX]) && (HardwareTimer_Handle[TIMER16_INDEX]->handle.Instance->SR & HAL_TIM_FLAG_ALL)) // Interrupt was caused by timer + HAL_TIM_IRQHandler(&HardwareTimer_Handle[TIMER16_INDEX]->handle); // Forward to timer interrupt handler + + uint32_t RxFifoITs = hfdcan2.Instance->IR & FDCAN_RX_FIFO_MASK; + RxFifoITs &= hfdcan2.Instance->IE; + + if (RxFifoITs) // Any Fifo read interrupts? + HAL_FDCAN_IRQHandler(&hfdcan2); // Forward call to FDCAN interrupt handler +} + +void HAL_FDCAN_MspInit(FDCAN_HandleTypeDef* fdcanHandle) { // Called automatically, configure GPIO for FDCAN, enable interrupts + GPIO_InitTypeDef GPIO_InitStruct = {0}; + RCC_PeriphCLKInitTypeDef PeriphClkInit = {0}; + + PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_FDCAN; + PeriphClkInit.FdcanClockSelection = RCC_FDCANCLKSOURCE_PCLK1; + + if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) + Error_Handler(); + + // FDCAN1/2 clock enable (1 clock for both CAN devices) + if (__HAL_RCC_FDCAN_IS_CLK_DISABLED()) + __HAL_RCC_FDCAN_CLK_ENABLE(); + + if (__HAL_RCC_GPIOB_IS_CLK_DISABLED()) + __HAL_RCC_GPIOB_CLK_ENABLE(); // ENABLE GPIO B CLOCK + // FDCAN2 GPIO Configuration + // PB0 ------> FDCAN2_RX + // PB1 ------> FDCAN2_TX + + GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1; // Pin PB0 and Pin PB1 + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; + GPIO_InitStruct.Alternate = GPIO_AF3_FDCAN2; + HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); // Port B + + // FDCAN2 interrupt Init + HAL_NVIC_SetPriority(TIM16_FDCAN_IT0_IRQn, 1, 1); // Set interrupt priority + HAL_NVIC_EnableIRQ(TIM16_FDCAN_IT0_IRQn); // Enable interrupt handler +} + +/* +uint32_t ClockDivider FDCAN kernel clock divider (common to all FDCAN instances, applied only at initialisation of first FDCAN instance) + 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30 +uint32_t FrameFormat FDCAN_FRAME_CLASSIC / FDCAN_FRAME_FD_NO_BRS / FDCAN_FRAME_FD_BRS (BRS=Bit Rate Switch) +uint32_t Mode; FDCAN_MODE_NORMAL / FDCAN_MODE_RESTRICTED_OPERATION / FDCAN_MODE_BUS_MONITORING / FDCAN_MODE_INTERNAL_LOOPBACK / FDCAN_MODE_EXTERNAL_LOOPBACK +FunctionalState AutoRetransmission Retransmit if fault is deteced, values: ENABLE / DISABLE +FunctionalState TransmitPause Waits for 2 bit times before transmitting the next frame to allow other notes to cut in, values: ENABLE / DISABLE +FunctionalState ProtocolException Protocol Exception Handling, values: ENABLE(start bus integration) or DISABLE(send error frame) +uint32_t NominalPrescaler Clock divider for generating the bit time quanta (1-512) +uint32_t NominalSyncJumpWidth Maximum number of time quanta the FDCAN allows to lengthen or shorten a bit to perform resynchronization (1-128) +uint32_t NominalTimeSeg1 Number of time quanta in Bit Segment 1 (2-256) +uint32_t NominalTimeSeg2 Number of time quanta in Bit Segment 2 (2-128) +uint32_t DataPrescaler Clock divider for generating the data bit time quanta (1-32) +uint32_t DataSyncJumpWidth Max of time quanta allowed to lengthen or shorten a data bit to perform resynchronization (1-16) +uint32_t DataTimeSeg1 Number of time quanta in Data Bit Segment 1. (1-32) +uint32_t DataTimeSeg2 Number of time quanta in Data Bit Segment 2. (1-16) +uint32_t StdFiltersNbr Number of standard Message ID filters. (0-28) +uint32_t ExtFiltersNbr Number of extended Message ID filters. (0-8) +uint32_t TxFifoQueueMode Tx FIFO/Queue Mode selection FDCAN_TX_FIFO_OPERATION / FDCAN_TX_QUEUE_OPERATION +*/ + +HAL_StatusTypeDef FDCAN2_Start(void) { // START THE FDCAN BUS + HAL_StatusTypeDef status = HAL_OK; + + // FDCAN baud rate = Device Clock Frequency / Clock Divider / Prescaler / SJW + TSG1 + TSG2) + // Baud rate = 64M / 1 / 4 / (1 + 10 + 5) = 1M + // Baud rate = 64M / 1 / 8 / (1 + 10 + 5) = 500k + // http://www.bittiming.can-wiki.info + // https://www.kvaser.com/support/calculators/can-fd-bit-timing-calculator + hfdcan2.Instance = FDCAN2; // FDCAN2 device + hfdcan2.Init.ClockDivider = FDCAN_CLOCK_DIV1; // Clock divider 1 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 + hfdcan2.Init.FrameFormat = FDCAN_FRAME_CLASSIC; // FDCAN_FRAME_CLASSIC / FDCAN_FRAME_FD_BRS / FDCAN_FRAME_FD_NO_BRS (Bit Rate Switching) + hfdcan2.Init.Mode = FDCAN_MODE_NORMAL; // FDCAN_MODE_NORMAL / FDCAN_MODE_EXTERNAL_LOOPBACK / FDCAN_MODE_INTERNAL_LOOPBACK / FDCAN_MODE_BUS_MONITORING / FDCAN_MODE_RESTRICTED_OPERATION + hfdcan2.Init.AutoRetransmission = DISABLE; // Auto Retransmission of message if error occured, can cause lockup + hfdcan2.Init.TransmitPause = DISABLE; // Transmit Pause to allow for other device to send message + hfdcan2.Init.ProtocolException = DISABLE; // ProtocolException + + hfdcan2.Init.NominalPrescaler = hfdcan2.Init.DataPrescaler = 4; // Arbitration/data clock prescaler (classic only) + hfdcan2.Init.NominalSyncJumpWidth = hfdcan2.Init.DataSyncJumpWidth = 1; // Arbitration/data sync jump width + hfdcan2.Init.NominalTimeSeg1 = hfdcan2.Init.DataTimeSeg1 = 10; // Arbitration/data period 1 + hfdcan2.Init.NominalTimeSeg2 = hfdcan2.Init.DataTimeSeg2 = 5; // Arbitration period 2 + + hfdcan2.Init.StdFiltersNbr = 2; // Number of standard frame filters + hfdcan2.Init.ExtFiltersNbr = 2; // Number of extended frame filters + hfdcan2.Init.TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION; // Queue mode: FDCAN_TX_FIFO_OPERATION / FDCAN_TX_QUEUE_OPERATION + + status = HAL_FDCAN_Init(&hfdcan2); + if (status != HAL_OK) + return status; + + FDCAN_FilterTypeDef sFilterConfig; // Configure RX message filter + + sFilterConfig.IdType = FDCAN_EXTENDED_ID; // Filter to FIFO1 if higest ID bit is set + sFilterConfig.FilterIndex = 0; // Exteneded filter ID 0 + sFilterConfig.FilterType = FDCAN_FILTER_MASK; // FDCAN_FILTER_MASK / FDCAN_FILTER_RANGE + sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO1; // Filter to FIFO1 + sFilterConfig.FilterID1 = 0x10000000; // Range: 0 - 0x1FFF FFFF, 0=don'tcare + sFilterConfig.FilterID2 = 0x10000000; // Range: 0 - 0x1FFF FFFF, 0=don'tcare + HAL_FDCAN_ConfigFilter(&hfdcan2, &sFilterConfig); + + sFilterConfig.IdType = FDCAN_EXTENDED_ID; // Filter all remaining messages to FIFO0 + sFilterConfig.FilterIndex = 1; // Exteneded filter ID 1 + sFilterConfig.FilterType = FDCAN_FILTER_MASK; // FDCAN_FILTER_MASK / FDCAN_FILTER_RANGE + sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0; // Remaining messages go to FIFO1 + sFilterConfig.FilterID1 = 0; // Range: 0 - 0x1FFF FFFF, 0=don'tcare + sFilterConfig.FilterID2 = 0; // Range: 0 - 0x1FFF FFFF, 0=don'tcare + HAL_FDCAN_ConfigFilter(&hfdcan2, &sFilterConfig); + + sFilterConfig.IdType = FDCAN_STANDARD_ID; // Filter to FIFO1 if higest ID bit is set + sFilterConfig.FilterIndex = 0; // Standard filter ID 0 + sFilterConfig.FilterType = FDCAN_FILTER_MASK; // FDCAN_FILTER_MASK / FDCAN_FILTER_RANGE + sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO1; + sFilterConfig.FilterID1 = 0b10000000000; // Range: 0 - 0x7FF, 0=don't care + sFilterConfig.FilterID2 = 0b10000000000; // Range: 0 - 0x7FF, 0=don't care + HAL_FDCAN_ConfigFilter(&hfdcan2, &sFilterConfig); + + sFilterConfig.IdType = FDCAN_STANDARD_ID; // Filter all remaining messages to FIFO0 + sFilterConfig.FilterIndex = 1; // Standard filter ID 1 + sFilterConfig.FilterType = FDCAN_FILTER_MASK; // FDCAN_FILTER_MASK / FDCAN_FILTER_RANGE + sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0; // Remaining to FIFO1 + sFilterConfig.FilterID1 = 0; // Range: 0 - 0x7FF, 0=don't care + sFilterConfig.FilterID2 = 0; // Range: 0 - 0x7FF, 0=don't care + HAL_FDCAN_ConfigFilter(&hfdcan2, &sFilterConfig); + +// Configure global filter +// status = HAL_FDCAN_ConfigGlobalFilter(&hfdcan2, FDCAN_REJECT, FDCAN_REJECT, FDCAN_REJECT_REMOTE, FDCAN_REJECT_REMOTE); + + // Start the FDCAN module + status = HAL_FDCAN_Start(&hfdcan2); // ===== START FDCAN2 DEVICE ===== + + // Activate RX FIFO0 new message interrupt + if (status == HAL_OK) + status = HAL_FDCAN_ActivateNotification(&hfdcan2, FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0); // Calls TIM16_IRQHandler + + // Activate RX FIFO0 message lost interrupt + if (status == HAL_OK) + status = HAL_FDCAN_ActivateNotification(&hfdcan2, FDCAN_IT_RX_FIFO0_MESSAGE_LOST, 0); // Calls TIM16_IRQHandler + + // Activate RX FIFO1 new message interrupt + if (status == HAL_OK) + status = HAL_FDCAN_ActivateNotification(&hfdcan2, FDCAN_IT_RX_FIFO1_NEW_MESSAGE, 0); // Calls TIM16_IRQHandler + + // Activate RX FIFO1 message lost interrupt + if (status == HAL_OK) + status = HAL_FDCAN_ActivateNotification(&hfdcan2, FDCAN_IT_RX_FIFO1_MESSAGE_LOST, 0); // Calls TIM16_IRQHandler + + return status; +} + +/* ---INTERRUPT HANDLERS------------------------------------------------------------- +void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs); +void HAL_FDCAN_RxFifo1Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo1ITs); +void HAL_FDCAN_RxBufferNewMessageCallback(FDCAN_HandleTypeDef *hfdcan); +void HAL_FDCAN_HighPriorityMessageCallback(FDCAN_HandleTypeDef *hfdcan); + +void HAL_FDCAN_TxEventFifoCallback(FDCAN_HandleTypeDef *hfdcan, uint32_t TxEventFifoITs); +void HAL_FDCAN_TxFifoEmptyCallback(FDCAN_HandleTypeDef *hfdcan); +void HAL_FDCAN_TxBufferCompleteCallback(FDCAN_HandleTypeDef *hfdcan, uint32_t BufferIndexes); +void HAL_FDCAN_TxBufferAbortCallback(FDCAN_HandleTypeDef *hfdcan, uint32_t BufferIndexes); + +void HAL_FDCAN_ErrorCallback(FDCAN_HandleTypeDef *hfdcan); +void HAL_FDCAN_ErrorStatusCallback(FDCAN_HandleTypeDef *hfdcan, uint32_t ErrorStatusITs); + +void HAL_FDCAN_ClockCalibrationCallback(FDCAN_HandleTypeDef *hfdcan, uint32_t ClkCalibrationITs); +void HAL_FDCAN_TimestampWraparoundCallback(FDCAN_HandleTypeDef *hfdcan); +void HAL_FDCAN_TimeoutOccurredCallback(FDCAN_HandleTypeDef *hfdcan); +void HAL_FDCAN_TT_ScheduleSyncCallback(FDCAN_HandleTypeDef *hfdcan, uint32_t TTSchedSyncITs); +void HAL_FDCAN_TT_TimeMarkCallback(FDCAN_HandleTypeDef *hfdcan, uint32_t TTTimeMarkITs); +void HAL_FDCAN_TT_StopWatchCallback(FDCAN_HandleTypeDef *hfdcan, uint32_t SWTime, uint32_t SWCycleCount); +void HAL_FDCAN_TT_GlobalTimeCallback(FDCAN_HandleTypeDef *hfdcan, uint32_t TTGlobTimeITs); */ + +HAL_StatusTypeDef CAN_Send_Message(bool TempUpdate) { // Called from temperature ISR! + // Send a IO/temp report from the HEAD to the HOST + HAL_StatusTypeDef status = HAL_OK; + FDCAN_TxHeaderTypeDef CanTxHeader; // Transmit CAN message buffer + + // Initialize standard TxHeader values + CanTxHeader.FDFormat = FDCAN_CLASSIC_CAN; // FDCAN_CLASSIC_CAN / FDCAN_FD_CAN + CanTxHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS; // FDCAN_NO_TX_EVENTS / FDCAN_STORE_TX_EVENTS + CanTxHeader.MessageMarker = 0; // 0-0xFF for tracking messages in FIFO buffer + CanTxHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE; // FDCAN_ESI_PASSIVE / FDCAN_ESI_ACTIVE ??? + CanTxHeader.TxFrameType = FDCAN_DATA_FRAME; // FDCAN_DATA_FRAME / FDCAN_REMOTE_FRAME + CanTxHeader.IdType = FDCAN_STANDARD_ID; // FDCAN_STANDARD_ID / FDCAN_EXTENDED_ID + CanTxHeader.BitRateSwitch = FDCAN_BRS_OFF; // FDCAN_BRS_OFF / FDCAN_BRS_ON + + uint8_t can_tx_buffer[8]; + if (TempUpdate) { // Add temperature to CAN message, 4 bytes + float * fp = (float *)can_tx_buffer; // Point to CAN tx buffer + *fp = thermalManager.degHotend(0); // Copy temp to can_tx_buffer + CanTxHeader.DataLength = FDCAN_DLC_BYTES_4; // Hotend temp in data only, bytes, FDCAN_DLC_BYTES_4 = (4 << 16)! + } + else + CanTxHeader.DataLength = FDCAN_DLC_BYTES_0; // 0 data byte CAN message + bool request_time_sync = CAN_request_time_sync; + uint32_t VirtualIO = (Head_Not_configured << CAN_REQUEST_SETUP_BIT) | // Report Head is not configured + (READ(Z_MIN_PROBE_PIN) << CAN_PROBE_BIT) | // Report probe status + (READ(FILAMENT_RUNOUT_PIN) << CAN_FILAMENT_BIT) | // Report filament detector status + (request_time_sync << CAN_REQUEST_TIME_SYNC_BIT) | + ((!!CAN_Error_Code) << CAN_ERROR_BIT); // Report error (if any) + // IRON, ADD PINS YOU WANT TO MONITOR HERE + + CanTxHeader.Identifier = ((CanTxHeader.Identifier ^ STDID_FIFO_BIT) & STDID_FIFO_BIT) + VirtualIO; // Toggle FIFO bit for receiver filtering + uint32_t ms = millis(); + while ((HAL_FDCAN_GetTxFifoFreeLevel(&hfdcan2) == 0) && (millis() - ms < 25)) { } // BLOCKING! Wait max 50ms for free TX FIFO slot + + if (request_time_sync) { // For time sync, wait until all message TX slots are empty before sending + CAN_request_time_sync = false; + ms = millis(); + while ((HAL_FDCAN_GetTxFifoFreeLevel(&hfdcan2) < 3) && (millis() - ms < 25)) { } // BLOCKING! Wait max 50ms for free TX FIFO slot + t[0] = micros(); // Store time sync message send time + } + status = HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan2, &CanTxHeader, can_tx_buffer); // Send FDCAN message + CAN_message_counter++; + return status; +} + +HAL_StatusTypeDef CAN_Send_String(const char * message) { // Send string message to host, string should end on '\n' + HAL_StatusTypeDef status = HAL_OK; + FDCAN_TxHeaderTypeDef CanTxHeader; // Transmit CAN message buffer + + // Initialize standard TxHeader values + CanTxHeader.FDFormat = FDCAN_CLASSIC_CAN; // FDCAN_CLASSIC_CAN / FDCAN_FD_CAN + CanTxHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS; // FDCAN_NO_TX_EVENTS / FDCAN_STORE_TX_EVENTS + CanTxHeader.MessageMarker = 0; // 0-0xFF for tracking messages in FIFO buffer + CanTxHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE; // FDCAN_ESI_PASSIVE / FDCAN_ESI_ACTIVE ??? + CanTxHeader.TxFrameType = FDCAN_DATA_FRAME; // FDCAN_DATA_FRAME / FDCAN_REMOTE_FRAME + CanTxHeader.IdType = FDCAN_STANDARD_ID; // FDCAN_STANDARD_ID / FDCAN_EXTENDED_ID + CanTxHeader.BitRateSwitch = FDCAN_BRS_OFF; // FDCAN_BRS_OFF / FDCAN_BRS_ON + + uint8_t can_tx_buffer[8]; + int remaining = strlen(message); // Length of string + int index = 0; + + #ifdef CAN_DEBUG + SERIAL_ECHOPGM(">>> SENDING STRING MESSAGE TO HOST: ", message); // IRON, SHOULD HAVE '\n' AT THE END, DEBUGGING + #endif + + while (remaining > 0) { // Keep sending message if there are more characters to send + int c = MIN(8, remaining); // Max 8 character at a time + remaining -= c; // Reduce character counter + CanTxHeader.DataLength = (c << DATALENGTH_OFFSET); // Max message length is 8 bytes, offset is 16 bits into the DataLength variable + + for (int i = 0; i < c; i++) + can_tx_buffer[i] = message[index++]; // Copy string data to CAN buffer + + uint32_t VirtualIO = (READ(Z_MIN_PROBE_PIN) << CAN_PROBE_BIT) | // Report probe status + (READ(FILAMENT_RUNOUT_PIN) << CAN_FILAMENT_BIT) | // Report filament detector status + ((!!CAN_Error_Code) << CAN_ERROR_BIT) | // Report error (if any) + (1 << CAN_STRING_MESSAGE_BIT); // Set string message bit + + CanTxHeader.Identifier = ((CanTxHeader.Identifier ^ 0b10000000000) & 0b10000000000) + VirtualIO; // Toggle FIFO bit for receiver filtering + uint32_t ms = millis(); + while ((HAL_FDCAN_GetTxFifoFreeLevel(&hfdcan2) < 2) && (millis() - ms < 50)) { } // BLOCKING! Wait for free TX FIFO slot, with 1 spare slot for urgent messages + + status = HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan2, &CanTxHeader, can_tx_buffer); // Send message + CAN_message_counter++; + } + + return status; +} + +uint32_t last_led_flash = 0; // IRON, for error LED flashing +uint32_t last_error_message = 0; +uint32_t oldExtuder_auto_fan_speed = controllerFan.settings.extruder_auto_fan_speed; + +void FDCAN_idle() { // Called from MarlinCore.cpp +// heart beat signal, send and receive +// error monitoring (hotend auto fan speed) + + if (oldExtuder_auto_fan_speed != controllerFan.settings.extruder_auto_fan_speed) { + controllerFan.settings.extruder_auto_fan_speed = MAX(controllerFan.settings.extruder_auto_fan_speed, PROBING_AUTO_FAN_SPEED); // Should not be required, safeguard! + oldExtuder_auto_fan_speed = controllerFan.settings.extruder_auto_fan_speed; + } + + if (send_gcode_count) { + MString<24> buffer(F("GCODES="), gcode_counter, '\n'); + CAN_Send_String(buffer); // Report number of gcodes received from host + send_gcode_count = false; + SERIAL_ECHOLNPGM(">>> CAN messages sent: ", CAN_message_counter); + } + // NTP style time sync + // t[0] = local time sync request time + // t[1] = host time sync receive time + // t[2] = host time sync reply time + // t[3] = local time stamp receive time + if (t[3] != 0) { + t[0] += time_offset; // Adjust local time stamps with time_offset + t[3] += time_offset; // Adjust local time stamps with time_offset + uint32_t local_time_adjustment = (t[1] - t[0] + t[2] - t[3]) >> 1; + time_offset += local_time_adjustment; + uint32_t Round_trip_delay = (t[3] - t[0] - t[2] + t[1]); + SERIAL_ECHOLNPGM("t0: ", t[0], " us"); + SERIAL_ECHOLNPGM("t1: ", t[1], " us"); + SERIAL_ECHOLNPGM("t2: ", t[2], " us"); + SERIAL_ECHOLNPGM("t3: ", t[3], " us"); + SERIAL_ECHOPGM("Local time adjustment: ", local_time_adjustment, " us"); + if (t[4]) { + SERIAL_ECHOLNPGM(" after ", ftostr42_52(float(t[0] - t[4]) / 1000000.0), " seconds"); + SERIAL_ECHOLNPGM("Drift: ", local_time_adjustment / (float(t[0] - t[4]) / 1000000.0), " us/s"); + } + else + SERIAL_EOL(); + + SERIAL_ECHOLNPGM("Time offset: ", time_offset, " us"); + SERIAL_ECHOLNPGM("Round_trip_delay: ", Round_trip_delay, " us"); + SERIAL_ECHOLNPGM("Host response time: ", (t[2] - t[1]), " us"); + + t[4] = t[0]; // Store previous time sync request time + t[3] = 0; + } + + if (hfdcan2.ErrorCode) { + uint32_t ms = millis(); + + if ((ms - last_error_message) > 20000) { // 20 seconds repeat + MString<40> buffer(F("Error: fdcan2.ErrorCode="), hfdcan2.ErrorCode, '\n'); + CAN_Send_String(buffer); + last_error_message = ms; + } + } + + if (CAN_Error_Code) { + uint32_t ms = millis(); + + if ((ms - last_led_flash) > 100) { // Flash LED fast on error + digitalToggle(LED_PIN); + last_led_flash = ms; + } + + if (CAN_Error_Code != Old_Error_Code) { // Show message on new error code + switch(CAN_Error_Code) { + case CAN_ERROR_FIFO_OVERFLOW: + CAN_Send_String("Error: HEAD CAN FIFO OVERFLOW\n"); + SERIAL_ECHOLNPGM(">>> ERROR: HEAD CAN FIFO OVERFLOW"); + break; + + case CAN_ERROR_INCOMPLETE_GCODE_RECEIVED: + CAN_Send_String("Error: HEAD INCOMPLETE GCODE MESSAGE\n"); + SERIAL_ECHOLNPGM(">>> ERROR: HEAD INCOMPLETE GCODE MESSAGE"); + break; + + case CAN_ERROR_MARLIN_CMD_BUFFER_OVERFLOW: + CAN_Send_String("Error: HEAD MARLIN CMD BUF OVERFLOW\n"); + SERIAL_ECHOLNPGM(">>> ERROR: HEAD MARLIN CMD BUF OVERFLOW"); + break; + + default: { } + } // Switch + + Old_Error_Code = CAN_Error_Code; + } // New error code + } // CAN_Error_Code +} // CAN_idle() + +/* +HAL_StatusTypeDef HAL_FDCAN_ConfigInterruptLines(FDCAN_HandleTypeDef *hfdcan, uint32_t ITList, uint32_t InterruptLine); +HAL_StatusTypeDef HAL_FDCAN_TT_ConfigInterruptLines(FDCAN_HandleTypeDef *hfdcan, uint32_t TTITList, uint32_t InterruptLine); +HAL_StatusTypeDef HAL_FDCAN_ActivateNotification(FDCAN_HandleTypeDef *hfdcan, uint32_t ActiveITs, uint32_t BufferIndexes); +HAL_StatusTypeDef HAL_FDCAN_DeactivateNotification(FDCAN_HandleTypeDef *hfdcan, uint32_t InactiveITs); +HAL_StatusTypeDef HAL_FDCAN_TT_ActivateNotification(FDCAN_HandleTypeDef *hfdcan, uint32_t ActiveTTITs); +HAL_StatusTypeDef HAL_FDCAN_TT_DeactivateNotification(FDCAN_HandleTypeDef *hfdcan, uint32_t InactiveTTITs); +void HAL_FDCAN_IRQHandler(FDCAN_HandleTypeDef *hfdcan); +*/ + +#endif // CAN_TOOLHEAD \ No newline at end of file diff --git a/Marlin/src/HAL/shared/CAN.h b/Marlin/src/HAL/shared/CAN.h index c8e410241293..c15bbb854364 100644 --- a/Marlin/src/HAL/shared/CAN.h +++ b/Marlin/src/HAL/shared/CAN.h @@ -30,17 +30,17 @@ 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_E0_TARGET_MASK 256 // Signals E0 or E1 -#define CAN_ERROR_MASK 512 // Signals the head encountered an error +#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(); diff --git a/Marlin/src/HAL/shared/FDCAN.h b/Marlin/src/HAL/shared/FDCAN.h new file mode 100644 index 000000000000..6a8712deea01 --- /dev/null +++ b/Marlin/src/HAL/shared/FDCAN.h @@ -0,0 +1,48 @@ +#pragma once + +// IMPORTANT NOTES +// =============== +// In \Users\\.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 \ No newline at end of file diff --git a/Marlin/src/MarlinCore.cpp b/Marlin/src/MarlinCore.cpp index d04600383323..60fbcdac23d7 100644 --- a/Marlin/src/MarlinCore.cpp +++ b/Marlin/src/MarlinCore.cpp @@ -38,6 +38,10 @@ #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 @@ -886,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--); @@ -1194,8 +1208,7 @@ void setup() { while (!MYSERIAL3.connected() && PENDING(millis(), serial_connect_timeout)) { /*nada*/ } #endif #endif - - SERIAL_ECHOLNPGM(TERN(CAN_MASTER, "\nstart\n", "start")); + SERIAL_ECHOLNPGM("start\n"); #if ENABLED(CAN_MASTER) SERIAL_ECHOLN( @@ -1204,6 +1217,13 @@ void setup() { ); #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 SETUP_LOG("KILL_PIN"); diff --git a/Marlin/src/gcode/temp/M306.cpp b/Marlin/src/gcode/temp/M306.cpp index 4b4b69c287dc..511106201fe0 100644 --- a/Marlin/src/gcode/temp/M306.cpp +++ b/Marlin/src/gcode/temp/M306.cpp @@ -80,6 +80,9 @@ void GcodeSuite::M306() { 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; @@ -125,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 diff --git a/Marlin/src/module/endstops.cpp b/Marlin/src/module/endstops.cpp index dcb10c014edd..b4b629d5b804 100644 --- a/Marlin/src/module/endstops.cpp +++ b/Marlin/src/module/endstops.cpp @@ -677,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 diff --git a/Marlin/src/module/temperature.cpp b/Marlin/src/module/temperature.cpp index fa984176d8a5..b2b80893cedd 100644 --- a/Marlin/src/module/temperature.cpp +++ b/Marlin/src/module/temperature.cpp @@ -4431,6 +4431,13 @@ void Temperature::isr() { // Poll endstops state, if required endstops.poll(); +#if ENABLED(CAN_TOOLHEAD) + HAL_StatusTypeDef CAN_Send_Message(bool TempUpdate); // Function Prototype + static uint32_t loopCounter = 0; + if ((loopCounter++ % 512) == 0) // Update E0 Temp every 512ms + CAN_Send_Message(true); // Send temp report with IO report +#endif // CAN_TOOLHEAD + // Periodically call the planner timer service routine planner.isr(); } diff --git a/Marlin/src/pins/pins.h b/Marlin/src/pins/pins.h index f9b0ee25235a..905646394a6c 100644 --- a/Marlin/src/pins/pins.h +++ b/Marlin/src/pins/pins.h @@ -562,7 +562,7 @@ // #elif MB(BTT_EBB42_V1_1) - #include "stm32g0/pins_BTT_EBB42_V1_1.h" // STM32G0 env:BTT_EBB42_V1_1_filament_extruder + #include "stm32g0/pins_BTT_EBB42_V1_1.h" // STM32G0 env:BTT_EBB42_V1_1_filament_extruder env:BTT_EBB42_V1_1_filament_extruder_CAN #elif MB(BTT_SKR_MINI_E3_V3_0) #include "stm32g0/pins_BTT_SKR_MINI_E3_V3_0.h" // STM32G0 env:STM32G0B1RE_btt env:STM32G0B1RE_btt_xfer #elif MB(BTT_MANTA_E3_EZ_V1_0) @@ -798,7 +798,7 @@ #elif MB(MKS_ROBIN_NANO_V3_1) #include "stm32f4/pins_MKS_ROBIN_NANO_V3.h" // STM32F4 env:mks_robin_nano_v3_1 env:mks_robin_nano_v3_1_usb_flash_drive env:mks_robin_nano_v3_1_usb_flash_drive_msc #elif MB(MKS_MONSTER8_V1) - #include "stm32f4/pins_MKS_MONSTER8_V1.h" // STM32F4 env:mks_monster8 env:mks_monster8_usb_flash_drive env:mks_monster8_usb_flash_drive_msc + #include "stm32f4/pins_MKS_MONSTER8_V1.h" // STM32F4 env:mks_monster8 env:mks_monster8_usb_flash_drive env:mks_monster8_usb_flash_drive_msc env:mks_monster8_usb_flash_drive_msc_CAN #elif MB(MKS_MONSTER8_V2) #include "stm32f4/pins_MKS_MONSTER8_V2.h" // STM32F4 env:mks_monster8 env:mks_monster8_usb_flash_drive env:mks_monster8_usb_flash_drive_msc env:mks_monster8_usb_flash_drive_msc_CAN #elif MB(ANET_ET4) diff --git a/Marlin/src/pins/stm32f4/pins_MKS_MONSTER8_common.h b/Marlin/src/pins/stm32f4/pins_MKS_MONSTER8_common.h index b15c7d7288c6..fefc8dccdbcc 100644 --- a/Marlin/src/pins/stm32f4/pins_MKS_MONSTER8_common.h +++ b/Marlin/src/pins/stm32f4/pins_MKS_MONSTER8_common.h @@ -43,8 +43,15 @@ //#define FLASH_EEPROM_EMULATION // Use Flash-based EEPROM emulation #define I2C_EEPROM // Need use jumpers set i2c for EEPROM #define MARLIN_EEPROM_SIZE 0x1000 // 4K -#define I2C_SCL_PIN PB8 // I2C_SCL and CAN_RX -#define I2C_SDA_PIN PB9 // I2C_SDA and CAN_TX + +#if ENALBED(CAN_MASTER) // Onboard I2C EEPROM is disconnected by enabling CAN bus jumpers +// NOTE: 2 Patchwires are required to reconnect the I2C EEPROM +// #define I2C_SCL_PIN PA8 // 3Dtouch servo control pin +// #define I2C_SDA_PIN PC9 // SPI3_CS TFT Card chip select pin +#else + #define I2C_SCL_PIN PB8 // I2C_SCL to CAN_RX + #define I2C_SDA_PIN PB9 // I2C_SDA to CAN_TX +#endif // // Servos diff --git a/Marlin/src/pins/stm32g0/pins_BTT_EBB42_V1_1.h b/Marlin/src/pins/stm32g0/pins_BTT_EBB42_V1_1.h index fef28f3f3682..53f88673d58a 100644 --- a/Marlin/src/pins/stm32g0/pins_BTT_EBB42_V1_1.h +++ b/Marlin/src/pins/stm32g0/pins_BTT_EBB42_V1_1.h @@ -147,3 +147,6 @@ #define BTN_EN2 PB5 #define BTN_ENC PB6 #endif + +#define CAN_RX PB0 +#define CAN_TX PB1 diff --git a/ini/stm32g0.ini b/ini/stm32g0.ini index 4a9d1e4f7ae5..c2365353b655 100644 --- a/ini/stm32g0.ini +++ b/ini/stm32g0.ini @@ -40,6 +40,22 @@ board_upload.offset_address = 0x08000000 build_flags = ${stm32_variant.build_flags} ${stm32g0_I2C2.build_flags} -flto -Wl,--no-warn-rwx-segment + +[env:BTT_EBB42_V1_1_filament_extruder_CAN] +extends = stm32_variant +platform = ststm32@17.1.0 +platform_packages = framework-arduinoststm32@~4.20600.231001 + toolchain-gccarmnoneeabi@1.120301.0 + +board = marlin_BTT_EBB42_V1_1 +board_build.offset = 0x0000 +board_upload.offset_address = 0x08000000 +build_flags = ${stm32_variant.build_flags} ${stm32g0_I2C2.build_flags} + -flto + -Wl,--no-warn-rwx-segment + -DHAL_FDCAN_MODULE_ENABLED + -DTIMER_SERIAL=TIM4 + debug_tool = stlink upload_protocol = dfu upload_command = dfu-util -a 0 -s 0x08000000:leave -D "$SOURCE" From a0ce07afe648edbffe0ebc6260aa4bb841ae7f2f Mon Sep 17 00:00:00 2001 From: rondlh <77279634+rondlh@users.noreply.github.com> Date: Sun, 1 Dec 2024 00:21:02 +0800 Subject: [PATCH 05/12] Rename BTT EBB42 environment, drop "ilament_extruder" from the CAN environment Remove unneeded line from the build_flags: "-DTIMER_SERIAL=TIM4" --- Marlin/src/pins/pins.h | 2 +- ini/stm32g0.ini | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Marlin/src/pins/pins.h b/Marlin/src/pins/pins.h index 905646394a6c..89602e2916d1 100644 --- a/Marlin/src/pins/pins.h +++ b/Marlin/src/pins/pins.h @@ -562,7 +562,7 @@ // #elif MB(BTT_EBB42_V1_1) - #include "stm32g0/pins_BTT_EBB42_V1_1.h" // STM32G0 env:BTT_EBB42_V1_1_filament_extruder env:BTT_EBB42_V1_1_filament_extruder_CAN + #include "stm32g0/pins_BTT_EBB42_V1_1.h" // STM32G0 env:BTT_EBB42_V1_1_filament_extruder env:BTT_EBB42_V1_1_FDCAN #elif MB(BTT_SKR_MINI_E3_V3_0) #include "stm32g0/pins_BTT_SKR_MINI_E3_V3_0.h" // STM32G0 env:STM32G0B1RE_btt env:STM32G0B1RE_btt_xfer #elif MB(BTT_MANTA_E3_EZ_V1_0) diff --git a/ini/stm32g0.ini b/ini/stm32g0.ini index c2365353b655..70f275eb4e40 100644 --- a/ini/stm32g0.ini +++ b/ini/stm32g0.ini @@ -41,7 +41,7 @@ build_flags = ${stm32_variant.build_flags} ${stm32g0_I2C2.build_ -flto -Wl,--no-warn-rwx-segment -[env:BTT_EBB42_V1_1_filament_extruder_CAN] +[env:BTT_EBB42_V1_1_FDCAN] extends = stm32_variant platform = ststm32@17.1.0 platform_packages = framework-arduinoststm32@~4.20600.231001 @@ -54,7 +54,6 @@ build_flags = ${stm32_variant.build_flags} ${stm32g0_I2C2.build_ -flto -Wl,--no-warn-rwx-segment -DHAL_FDCAN_MODULE_ENABLED - -DTIMER_SERIAL=TIM4 debug_tool = stlink upload_protocol = dfu From dd5a2ff60f0baa51524944b32612e1c62c874c1f Mon Sep 17 00:00:00 2001 From: thisiskeithb <13375512+thisiskeithb@users.noreply.github.com> Date: Sat, 30 Nov 2024 11:33:22 -0800 Subject: [PATCH 06/12] Use BTT EBB32 base/common environment ... and reorder so CAN is primary --- Marlin/src/pins/pins.h | 2 +- ini/stm32g0.ini | 33 +++++++++++++++------------------ 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/Marlin/src/pins/pins.h b/Marlin/src/pins/pins.h index 89602e2916d1..516fe1d71a22 100644 --- a/Marlin/src/pins/pins.h +++ b/Marlin/src/pins/pins.h @@ -562,7 +562,7 @@ // #elif MB(BTT_EBB42_V1_1) - #include "stm32g0/pins_BTT_EBB42_V1_1.h" // STM32G0 env:BTT_EBB42_V1_1_filament_extruder env:BTT_EBB42_V1_1_FDCAN + #include "stm32g0/pins_BTT_EBB42_V1_1.h" // STM32G0 env:BTT_EBB42_V1_1_FDCAN env:BTT_EBB42_V1_1_filament_extruder #elif MB(BTT_SKR_MINI_E3_V3_0) #include "stm32g0/pins_BTT_SKR_MINI_E3_V3_0.h" // STM32G0 env:STM32G0B1RE_btt env:STM32G0B1RE_btt_xfer #elif MB(BTT_MANTA_E3_EZ_V1_0) diff --git a/ini/stm32g0.ini b/ini/stm32g0.ini index 70f275eb4e40..6bbb06ca742d 100644 --- a/ini/stm32g0.ini +++ b/ini/stm32g0.ini @@ -26,10 +26,9 @@ build_flags = -DPIN_WIRE_SCL=PB3 -DPIN_WIRE_SDA=PB4 # -# BigTreeTech EBB42 V1.1 (STM32G0B1CBT6 ARM Cortex-M0+) -# This board is being used to control Filament extruders. This is not supported for 3D printing, as it has no kinematics control +# BTT EBB32 V1.1 base # -[env:BTT_EBB42_V1_1_filament_extruder] +[BTT_EBB42_V1_1_common] extends = stm32_variant platform = ststm32@17.1.0 platform_packages = framework-arduinoststm32@~4.20600.231001 @@ -40,25 +39,23 @@ board_upload.offset_address = 0x08000000 build_flags = ${stm32_variant.build_flags} ${stm32g0_I2C2.build_flags} -flto -Wl,--no-warn-rwx-segment - -[env:BTT_EBB42_V1_1_FDCAN] -extends = stm32_variant -platform = ststm32@17.1.0 -platform_packages = framework-arduinoststm32@~4.20600.231001 - toolchain-gccarmnoneeabi@1.120301.0 - -board = marlin_BTT_EBB42_V1_1 -board_build.offset = 0x0000 -board_upload.offset_address = 0x08000000 -build_flags = ${stm32_variant.build_flags} ${stm32g0_I2C2.build_flags} - -flto - -Wl,--no-warn-rwx-segment - -DHAL_FDCAN_MODULE_ENABLED - debug_tool = stlink upload_protocol = dfu upload_command = dfu-util -a 0 -s 0x08000000:leave -D "$SOURCE" +# +# BigTreeTech EBB42 V1.1 (STM32G0B1CBT6 ARM Cortex-M0+) as a CAN toolboard +# +[env:BTT_EBB42_V1_1_FDCAN] +extends = BTT_EBB42_V1_1_common +build_flags = -DHAL_FDCAN_MODULE_ENABLED + +# +# BigTreeTech EBB42 V1.1 (STM32G0B1CBT6 ARM Cortex-M0+) as a filament extruder (no kinematics control) +# +[env:BTT_EBB42_V1_1_filament_extruder] +extends = BTT_EBB42_V1_1_common + # # BigTreeTech SKR Mini E3 V3.0 (STM32G0B0RET6 / STM32G0B1RET6 ARM Cortex-M0+) # From 73bb491eb780d25ed6f9c0db763aa3b48468512d Mon Sep 17 00:00:00 2001 From: thisiskeithb <13375512+thisiskeithb@users.noreply.github.com> Date: Sat, 30 Nov 2024 11:38:18 -0800 Subject: [PATCH 07/12] Format MONSTER8 pins --- Marlin/src/pins/stm32f4/pins_MKS_MONSTER8_common.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Marlin/src/pins/stm32f4/pins_MKS_MONSTER8_common.h b/Marlin/src/pins/stm32f4/pins_MKS_MONSTER8_common.h index fefc8dccdbcc..422374ac80b0 100644 --- a/Marlin/src/pins/stm32f4/pins_MKS_MONSTER8_common.h +++ b/Marlin/src/pins/stm32f4/pins_MKS_MONSTER8_common.h @@ -44,13 +44,13 @@ #define I2C_EEPROM // Need use jumpers set i2c for EEPROM #define MARLIN_EEPROM_SIZE 0x1000 // 4K -#if ENALBED(CAN_MASTER) // Onboard I2C EEPROM is disconnected by enabling CAN bus jumpers -// NOTE: 2 Patchwires are required to reconnect the I2C EEPROM -// #define I2C_SCL_PIN PA8 // 3Dtouch servo control pin -// #define I2C_SDA_PIN PC9 // SPI3_CS TFT Card chip select pin +#if ENALBED(CAN_MASTER) // Onboard I2C EEPROM is disconnected by enabling CAN bus jumpers +// NOTE: 2 Patch wires are required to reconnect the I2C EEPROM +//#define I2C_SCL_PIN PA8 // BLTouch servo control pin +//#define I2C_SDA_PIN PC9 // SPI3_CS TFT Card chip select pin #else - #define I2C_SCL_PIN PB8 // I2C_SCL to CAN_RX - #define I2C_SDA_PIN PB9 // I2C_SDA to CAN_TX + #define I2C_SCL_PIN PB8 // I2C_SCL to CAN_RX + #define I2C_SDA_PIN PB9 // I2C_SDA to CAN_TX #endif // From 20ea87f47f4549bbce7452e395a5a9ffbf1f07a5 Mon Sep 17 00:00:00 2001 From: rondlh <77279634+rondlh@users.noreply.github.com> Date: Sun, 1 Dec 2024 23:50:21 +0800 Subject: [PATCH 08/12] env:BTT_EBB42_V1_1_FDCAN build_flags fix Fixes an issue in the env:BTT_EBB42_V1_1_FDCAN build_flags --- ini/stm32g0.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ini/stm32g0.ini b/ini/stm32g0.ini index 6bbb06ca742d..bf20b68f9b7a 100644 --- a/ini/stm32g0.ini +++ b/ini/stm32g0.ini @@ -48,8 +48,8 @@ upload_command = dfu-util -a 0 -s 0x08000000:leave -D "$SOURCE" # [env:BTT_EBB42_V1_1_FDCAN] extends = BTT_EBB42_V1_1_common -build_flags = -DHAL_FDCAN_MODULE_ENABLED - +build_flags = ${BTT_EBB42_V1_1_common.build_flags} + -DHAL_FDCAN_MODULE_ENABLED # # BigTreeTech EBB42 V1.1 (STM32G0B1CBT6 ARM Cortex-M0+) as a filament extruder (no kinematics control) # From adf012a7426d8a22bbdfa65d6da909b01fe643d6 Mon Sep 17 00:00:00 2001 From: rondlh <77279634+rondlh@users.noreply.github.com> Date: Fri, 6 Dec 2024 09:08:21 +0800 Subject: [PATCH 09/12] Add clock difference drift calculation Show predicted time sync adjustment based on previous measurement --- Marlin/src/HAL/STM32/FDCAN.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Marlin/src/HAL/STM32/FDCAN.cpp b/Marlin/src/HAL/STM32/FDCAN.cpp index a942acdbe7c7..de066696faa8 100644 --- a/Marlin/src/HAL/STM32/FDCAN.cpp +++ b/Marlin/src/HAL/STM32/FDCAN.cpp @@ -65,6 +65,7 @@ volatile bool send_gcode_count = false; volatile uint32_t t[5] = { 0, 0, 0, 0, 0 }; // Timestamps for time sync volatile bool CAN_request_time_sync = false; // Request a timestamp volatile uint32_t time_offset = 0; // Time offset in micro seconds in relation to host time +float drift = 0; // Time sync calculated drift between host and tool head extern "C" void TIM16_IRQHandler(void); extern "C" void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs); @@ -516,10 +517,14 @@ void FDCAN_idle() { // Called from MarlinCore.cpp SERIAL_ECHOLNPGM("t1: ", t[1], " us"); SERIAL_ECHOLNPGM("t2: ", t[2], " us"); SERIAL_ECHOLNPGM("t3: ", t[3], " us"); + if (drift) + SERIAL_ECHOLNPGM("Predicted time adjustment: ", drift * float(t[0] - t[4]) / 1000000.0 , " us"); + SERIAL_ECHOPGM("Local time adjustment: ", local_time_adjustment, " us"); if (t[4]) { SERIAL_ECHOLNPGM(" after ", ftostr42_52(float(t[0] - t[4]) / 1000000.0), " seconds"); - SERIAL_ECHOLNPGM("Drift: ", local_time_adjustment / (float(t[0] - t[4]) / 1000000.0), " us/s"); + drift = local_time_adjustment / (float(t[0] - t[4]) / 1000000.0); // Calculate drift again + SERIAL_ECHOLNPGM("Drift: ", drift, " us/s"); } else SERIAL_EOL(); From 9af20e07e469095a6354fed34cdc0f29bd150cf3 Mon Sep 17 00:00:00 2001 From: rondlh <77279634+rondlh@users.noreply.github.com> Date: Mon, 9 Dec 2024 11:15:21 +0800 Subject: [PATCH 10/12] Merge upstream changes to endstops.cpp affecting CAN bus virtual IO Counter changes to endstops.cpp so CAN bus virtual IO still works --- Marlin/src/feature/runout.h | 4 ++-- Marlin/src/module/endstops.cpp | 28 ++++++++++++++-------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Marlin/src/feature/runout.h b/Marlin/src/feature/runout.h index 8cbad8e61eb2..aab51cc9ac8e 100644 --- a/Marlin/src/feature/runout.h +++ b/Marlin/src/feature/runout.h @@ -55,11 +55,11 @@ #define HAS_FILAMENT_SWITCH 1 #endif -#define FILAMENT_IS_OUT() (READ(FIL_RUNOUT_PIN) == FIL_RUNOUT_STATE) - #if ENABLED(CAN_MASTER) + #define FILAMENT_IS_OUT() (bool(CAN_io_state & CAN_FILAMENT_MASK) == FIL_RUNOUT_STATE) #define RUNOUT_STATE(N) bool(CAN_io_state & CAN_FILAMENT_MASK) // CAN Virtual Filament Runout pin #else + #define FILAMENT_IS_OUT() (READ(FIL_RUNOUT_PIN) == FIL_RUNOUT_STATE) #define RUNOUT_STATE(N) READ(FIL_RUNOUT##N##_PIN) // DIO Filament Runout pin #endif diff --git a/Marlin/src/module/endstops.cpp b/Marlin/src/module/endstops.cpp index b4b629d5b804..83450bb73482 100644 --- a/Marlin/src/module/endstops.cpp +++ b/Marlin/src/module/endstops.cpp @@ -27,9 +27,10 @@ #include "endstops.h" #include "stepper.h" -#include "../sd/cardreader.h" -#include "temperature.h" -#include "../lcd/marlinui.h" +#if HAS_STATUS_MESSAGE + #include "../lcd/marlinui.h" +#endif + #if ENABLED(SOVOL_SV06_RTS) #include "../lcd/sovol_rts/sovol_rts.h" #endif @@ -44,6 +45,8 @@ #if ENABLED(SD_ABORT_ON_ENDSTOP_HIT) #include "printcounter.h" // for print_job_timer + #include "temperature.h" + #include "../sd/cardreader.h" #endif #if ENABLED(BLTOUCH) @@ -54,12 +57,12 @@ #include "../feature/joystick.h" #endif -#if HAS_BED_PROBE - #include "probe.h" +#if HAS_FILAMENT_SENSOR + #include "../feature/runout.h" #endif -#if HAS_FILAMENT_SENSOR && !MULTI_FILAMENT_SENSOR - #include "../feature/runout.h" +#if HAS_BED_PROBE + #include "probe.h" #endif #define DEBUG_OUT ALL(USE_SENSORLESS, DEBUG_LEVELING_FEATURE) @@ -385,13 +388,13 @@ void Endstops::event_handler() { #endif SERIAL_EOL(); - TERN_(HAS_STATUS_MESSAGE, + #if HAS_STATUS_MESSAGE ui.status_printf(0, F(S_FMT GANG_N_1(NUM_AXES, " %c") " %c"), GET_TEXT_F(MSG_LCD_ENDSTOPS), NUM_AXIS_LIST_(chrX, chrY, chrZ, chrI, chrJ, chrK, chrU, chrV, chrW) chrP - ) - ); + ); + #endif #if ENABLED(SD_ABORT_ON_ENDSTOP_HIT) if (planner.abort_on_endstop_hit) { @@ -535,11 +538,8 @@ void __O2 Endstops::report_states() { print_es_state(extDigitalRead(pin) != state); } #undef _CASE_RUNOUT - #elif HAS_FILAMENT_SENSOR - - print_es_state(RUNOUT_STATE(1) != FIL_RUNOUT1_STATE, F(STR_FILAMENT)); - + print_es_state(!FILAMENT_IS_OUT(), F(STR_FILAMENT)); #endif TERN_(BLTOUCH, bltouch._reset_SW_mode()); From 45ebd127946ae77ed97bfcefe429b69989846be4 Mon Sep 17 00:00:00 2001 From: rondlh <77279634+rondlh@users.noreply.github.com> Date: Sat, 14 Dec 2024 02:22:31 +0800 Subject: [PATCH 11/12] Undo Filament sensor missing text bugfix to solve merge conflict OLD: print_es_state(!FILAMENT_IS_OUT(), F(STR_FILAMENT)); NEW: print_es_state(!FILAMENT_IS_OUT()); OLD is actually correct. --- Marlin/src/module/endstops.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Marlin/src/module/endstops.cpp b/Marlin/src/module/endstops.cpp index 83450bb73482..97cbde21242a 100644 --- a/Marlin/src/module/endstops.cpp +++ b/Marlin/src/module/endstops.cpp @@ -539,7 +539,7 @@ void __O2 Endstops::report_states() { } #undef _CASE_RUNOUT #elif HAS_FILAMENT_SENSOR - print_es_state(!FILAMENT_IS_OUT(), F(STR_FILAMENT)); + print_es_state(!FILAMENT_IS_OUT()); #endif TERN_(BLTOUCH, bltouch._reset_SW_mode()); From 8d493540ee0832cce16089c3a938c94e744c5349 Mon Sep 17 00:00:00 2001 From: rondlh <77279634+rondlh@users.noreply.github.com> Date: Sat, 14 Dec 2024 06:13:54 +0800 Subject: [PATCH 12/12] Added basic support for the ADXL345 3-axis accelerometer WIP! --- Marlin/Configuration_adv.h | 6 ++ Marlin/src/MarlinCore.cpp | 35 ++++--- .../src/feature/accelerometer/acc_adxl345.cpp | 73 ++++++++++++++ .../src/feature/accelerometer/acc_adxl345.h | 98 +++++++++++++++++++ ini/features.ini | 1 + 5 files changed, 199 insertions(+), 14 deletions(-) create mode 100644 Marlin/src/feature/accelerometer/acc_adxl345.cpp create mode 100644 Marlin/src/feature/accelerometer/acc_adxl345.h diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h index 564f44df606d..e5c6563064cc 100644 --- a/Marlin/Configuration_adv.h +++ b/Marlin/Configuration_adv.h @@ -3496,6 +3496,12 @@ #endif // HAS_TRINAMIC_CONFIG +// @section spibus + +// This feature is EXPERIMENTAL +// Warning: Enabling the sensor will probably conflict with the onboard SD Card +//#define HAS_ADXL345_ACCELEROMETER; // uncomment to enable the 3-axis accelerometer + // @section i2cbus // diff --git a/Marlin/src/MarlinCore.cpp b/Marlin/src/MarlinCore.cpp index 60fbcdac23d7..307f45f34011 100644 --- a/Marlin/src/MarlinCore.cpp +++ b/Marlin/src/MarlinCore.cpp @@ -42,6 +42,10 @@ #include "HAL/shared/FDCAN.h" #endif +#if ENABLED(HAS_ADXL345_ACCELEROMETER) + #include "feature/accelerometer/acc_adxl345.h" +#endif + #if ENABLED(WIFISUPPORT) #include "HAL/shared/esp_wifi.h" #endif @@ -1210,20 +1214,6 @@ void setup() { #endif 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 SETUP_LOG("KILL_PIN"); @@ -1268,6 +1258,23 @@ void setup() { SETUP_RUN(hal.init()); + #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 + #if ENABLED(HAS_ADXL345_ACCELEROMETER) + adxl345.begin(); + #endif + // Init and disable SPI thermocouples; this is still needed #if TEMP_SENSOR_IS_MAX_TC(0) || (TEMP_SENSOR_IS_MAX_TC(REDUNDANT) && REDUNDANT_TEMP_MATCH(SOURCE, E0)) OUT_WRITE(TEMP_0_CS_PIN, HIGH); // Disable diff --git a/Marlin/src/feature/accelerometer/acc_adxl345.cpp b/Marlin/src/feature/accelerometer/acc_adxl345.cpp new file mode 100644 index 000000000000..2b061915d41d --- /dev/null +++ b/Marlin/src/feature/accelerometer/acc_adxl345.cpp @@ -0,0 +1,73 @@ +/*************************************************************** + * + * ADXL345 3-AXIS ACCELEROMETER ON SPI BUS + * 4-WIRE SPI COMMUNICATION + * Define: SD_SS_PIN, SD_SCK_PIN, SD_MISO_PIN, SD_MOSI_PIN + * + ****************************************************************/ + +#include "../../inc/MarlinConfig.h" + +#if ENABLED(HAS_ADXL345_ACCELEROMETER) + +#include "acc_ADXL345.h" + +#include "../../MarlinCore.h" +#include "../../HAL/shared/Delay.h" + +#include "SPI.h" +extern SPIClass SPI; + +ADXL345 adxl345; + +void ADXL345::begin() { + pinMode(SD_SS_PIN, OUTPUT); // set ADXL345 Chip Select pin to output mode + pinMode(SD_SS_PIN, HIGH); // set ADXL345 Chip Select to HIGH before configuratin SPI + spiBegin(); // sets Chip Select to HIGH (again) + spiInit(SPI_HALF_SPEED); // calls SPI.begin(), sets speed to 4MHz, max is 5MHz for ADXL345 + SPI.setDataMode(SPI_MODE3); // ADXL345 uses SPI_MODE3 to communicate (CPOL=1, CPHA = 1) + + // set range to 2g (4wire SPI, right justify data, 10-bit resolution) + writeRegister(ADXL345_DATA_FORMAT_REG, ADXL345_DATA_RANGE_2G); + + // enable measurement mode, use streaming mode + writeRegister(ADXL345_POWER_CTL_REG, ADXL345_POWER_CTL_MEASURE | ADXL345_FIFO_CTL_MODE_STREAM); + + // set to 100Hz sampling rate + writeRegister(ADXL345_RATE_REG, ADXL345_RATE_100HZ); +} + +void ADXL345::end() { // put device in standby mode + writeRegister(ADXL345_POWER_CTL_REG, ADXL345_POWER_CTL_STANDBY); +} + +void ADXL345::writeRegister(uint8_t registerAddress, uint8_t data) { + digitalWrite(SD_SS_PIN, LOW); // set Chip Select to LOW to start the write + spiSend(registerAddress); // send the register address + spiSend(data); // send the data + digitalWrite(SD_SS_PIN, HIGH); // set Chip Select to HIGH to complete the write +} + +void ADXL345::readRegister(uint8_t registerAddress, int numBytes, uint8_t * buffer) { + uint8_t address = registerAddress | 0x80; // set read bit + if (numBytes > 1) + address = address | 0x40; // also set multi-byte read if needed + + digitalWrite(SD_SS_PIN, LOW); // set Chip Select to LOW to start the read + spiSend(address); // send the register address + for (int i = 0; i < numBytes; i++) + buffer[i] = spiRec(); // read the data + digitalWrite(SD_SS_PIN, HIGH); // set Chip Select to HIGH to complete the read + delayMicroseconds(5); // allow 5us for the FIFO/registers to update (see datasheet) +} + +// get a accleration measurement for the X, Y and Z axis +void ADXL345::readMeasurement(ADXL345_measurement_t *acceleration) { + readRegister(ADXL345_DATA_X0_REG, 6, (uint8_t*)acceleration); +} + +void ADXL345::select(const bool select) { + WRITE(SD_SS_PIN, !select); // ADXL345 is selected on LOW +} + +#endif // HAS_ADXL345_ACCELEROMETER \ No newline at end of file diff --git a/Marlin/src/feature/accelerometer/acc_adxl345.h b/Marlin/src/feature/accelerometer/acc_adxl345.h new file mode 100644 index 000000000000..d890cf2dd937 --- /dev/null +++ b/Marlin/src/feature/accelerometer/acc_adxl345.h @@ -0,0 +1,98 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2020 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 . + * + */ +#pragma once + +// ADXL345 registers and defines +#define ADXL345_DEV_ID_REG 0x00 + +#define ADXL345_OFFSET_X_REG 0x1E // int8_t, 15.6mg/LSB (0x7F=2G) +#define ADXL345_OFFSET_Y_REG 0x1F // int8_t, 15.6mg/LSB (0x7F=2G) +#define ADXL345_OFFSET_Z_REG 0x20 // int8_t, 15.6mg/LSB (0x7F=2G) + +#define ADXL345_RATE_REG 0x2C // Sample rate register 0.1 - 3200Hz +#define ADXL345_RATE_0_1HZ 0x0 +#define ADXL345_RATE_0_2HZ 0x1 +#define ADXL345_RATE_0_39HZ 0x2 +#define ADXL345_RATE_0_78HZ 0x3 +#define ADXL345_RATE_1_56HZ 0x4 +#define ADXL345_RATE_3_13HZ 0x5 +#define ADXL345_RATE_6_25HZ 0x6 +#define ADXL345_RATE_12_5HZ 0x7 +#define ADXL345_RATE_25HZ 0x8 +#define ADXL345_RATE_50HZ 0x9 +#define ADXL345_RATE_100HZ 0xA +#define ADXL345_RATE_200HZ 0xB +#define ADXL345_RATE_400HZ 0xC +#define ADXL345_RATE_800HZ 0xD +#define ADXL345_RATE_1600HZ 0xE +#define ADXL345_RATE_3200HZ 0xF + +#define ADXL345_POWER_CTL_REG 0x2D // Power control register +#define ADXL345_POWER_CTL_STANDBY 0x00 +#define ADXL345_POWER_CTL_MEASURE 0x08 + +#define ADXL345_DATA_FORMAT_REG 0x31 // Data format register +#define ADXL345_DATA_SELFTEST_BIT 0x80 +#define ADXL345_DATA_3WIRE_BIT 0x40 +#define ADXL345_DATA_FULLRES_BIT 0x08 +#define ADXL345_DATA_MSB_BIT 0x04 +#define ADXL345_DATA_RANGE_MASK 3 +#define ADXL345_DATA_RANGE_2G 0 +#define ADXL345_DATA_RANGE_4G 1 +#define ADXL345_DATA_RANGE_8G 2 +#define ADXL345_DATA_RANGE_16G 3 + +#define ADXL345_DATA_X0_REG 0x32 // Measurement data registers +#define ADXL345_DATA_X1_REG 0x33 +#define ADXL345_DATA_Y0_REG 0x34 +#define ADXL345_DATA_Y1_REG 0x35 +#define ADXL345_DATA_Z0_REG 0x36 +#define ADXL345_DATA_Z1_REG 0x37 + +#define ADXL345_FIFO_CTL_REG 0x38 // FIFO control register +#define ADXL345_FIFO_CTL_MODE_BYPASS 0x00 +#define ADXL345_FIFO_CTL_MODE_ENABLED 0x40 +#define ADXL345_FIFO_CTL_MODE_STREAM 0x80 +#define ADXL345_FIFO_CTL_MODE_TRIGGER 0xC0 +#define ADXL345_FIFO_CTL_SAMPLE_COUNT_MASK 0x1F + +#define ADXL345_FIFO_STATUS_REG 0x39 // FIFO status register +#define ADXL345_FIFO_STATUS_ENTRIES_MASK 0x3F + +struct ADXL345_measurement_t { + int16_t x; + int16_t y; + int16_t z; +}; + +class ADXL345 { + public: + void begin(); + void end(); + void readMeasurement(ADXL345_measurement_t * acceleration); + void writeRegister(uint8_t registerAddress, uint8_t data); + void readRegister(uint8_t registerAddress, int numBytes, uint8_t * buffer); + private: + void select(const bool select); +}; + +extern ADXL345 adxl345; \ No newline at end of file diff --git a/ini/features.ini b/ini/features.ini index b2e9d6884e75..4d6ad7b55641 100644 --- a/ini/features.ini +++ b/ini/features.ini @@ -12,6 +12,7 @@ # The order of the features matters for source-filter resolution inside of common-dependencies.py. [features] +HAS_ADXL345_ACCELEROMETER = build_src_filter=+ YHCB2004 = LiquidCrystal_AIP31068=https://github.com/ellensp/LiquidCrystal_AIP31068/archive/3fc43b7.zip, red-scorp/SoftSPIB@^1.1.1 HAS_TFT_LVGL_UI = lvgl=https://github.com/makerbase-mks/LVGL-6.1.1-MKS/archive/a3ebe98bc6.zip build_src_filter=+