From e8cf0bbfccb944d1cda8a8da3111e0d77f2993f1 Mon Sep 17 00:00:00 2001 From: marcosprojects Date: Thu, 25 Mar 2021 20:53:09 +0100 Subject: [PATCH] Yalang YL620 VFD (#838) * New SpindleType YL620 Files for new SpindleType Yalang 620. So far the contents are a duplicate of H2ASpindle.h and H2ASpindle.cpp * Added register documentation and implemented read and write data packets * Some fixes, mostly regarding RX packet length --- Grbl_Esp32/src/Spindles/Spindle.cpp | 5 + Grbl_Esp32/src/Spindles/Spindle.h | 1 + Grbl_Esp32/src/Spindles/YL620Spindle.cpp | 241 +++++++++++++++++++++++ Grbl_Esp32/src/Spindles/YL620Spindle.h | 43 ++++ 4 files changed, 290 insertions(+) create mode 100644 Grbl_Esp32/src/Spindles/YL620Spindle.cpp create mode 100644 Grbl_Esp32/src/Spindles/YL620Spindle.h diff --git a/Grbl_Esp32/src/Spindles/Spindle.cpp b/Grbl_Esp32/src/Spindles/Spindle.cpp index b9cfed56..7dfd8f6b 100644 --- a/Grbl_Esp32/src/Spindles/Spindle.cpp +++ b/Grbl_Esp32/src/Spindles/Spindle.cpp @@ -38,6 +38,7 @@ #include "H2ASpindle.h" #include "BESCSpindle.h" #include "10vSpindle.h" +#include "YL620Spindle.h" namespace Spindles { // An instance of each type of spindle is created here. @@ -51,6 +52,7 @@ namespace Spindles { H2A h2a; BESC besc; _10v _10v; + YL620 yl620; void Spindle::select() { switch (static_cast(spindle_type->get())) { @@ -78,6 +80,9 @@ namespace Spindles { case SpindleType::H2A: spindle = &h2a; break; + case SpindleType::YL620: + spindle = &yl620; + break; case SpindleType::NONE: default: spindle = &null; diff --git a/Grbl_Esp32/src/Spindles/Spindle.h b/Grbl_Esp32/src/Spindles/Spindle.h index 2f507767..200e99cd 100644 --- a/Grbl_Esp32/src/Spindles/Spindle.h +++ b/Grbl_Esp32/src/Spindles/Spindle.h @@ -38,6 +38,7 @@ enum class SpindleType : int8_t { BESC, _10V, H2A, + YL620, }; #include "../Grbl.h" diff --git a/Grbl_Esp32/src/Spindles/YL620Spindle.cpp b/Grbl_Esp32/src/Spindles/YL620Spindle.cpp new file mode 100644 index 00000000..9387f563 --- /dev/null +++ b/Grbl_Esp32/src/Spindles/YL620Spindle.cpp @@ -0,0 +1,241 @@ +#include "YL620Spindle.h" + +/* + YL620Spindle.cpp + + This is for a Yalang YL620/YL620-A VFD based spindle to be controlled via RS485 Modbus RTU. + + Part of Grbl_ESP32 + 2021 - Marco Wagner + + Grbl is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + Grbl is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with Grbl. If not, see . + + WARNING!!!! + VFDs are very dangerous. They have high voltages and are very powerful + Remove power before changing bits. + + ============================================================================================================= + + Configuration required for the YL620 + + Parameter number Description Value + ------------------------------------------------------------------------------- + P00.00 Main frequency 400.00Hz (match to your spindle) + P00.01 Command source 3 + + P03.00 RS485 Baud rate 3 (9600) + P03.01 RS485 address 1 + P03.02 RS485 protocol 2 + P03.08 Frequency given lower limit 100.0Hz (match to your spindle cooling-type) + + =============================================================================================================== + + RS485 communication is standard Modbus RTU + + Therefore, the following operation codes are relevant: + 0x03: read single holding register + 0x06: write single holding register + + Holding register address Description + --------------------------------------------------------------------------- + 0x0000 main frequency + 0x0308 frequency given lower limit + + 0x2000 command register (further information below) + 0x2001 Modbus485 frequency command (x0.1Hz => 2500 = 250.0Hz) + + 0x200A Target frequency + 0x200B Output frequency + 0x200C Output current + + + Command register at holding address 0x2000 + -------------------------------------------------------------------------- + bit 1:0 b00: No function + b01: shutdown command + b10: start command + b11: Jog command + bit 3:2 reserved + bit 5:4 b00: No function + b01: Forward command + b10: Reverse command + b11: change direction + bit 7:6 b00: No function + b01: reset an error flag + b10: reset all error flags + b11: reserved +*/ + +#include + +namespace Spindles { + void YL620::default_modbus_settings(uart_config_t& uart) { + // sets the uart to 9600 8N1 + VFD::default_modbus_settings(uart); + + uart.baud_rate = 9600; + uart.data_bits = UART_DATA_8_BITS; + uart.parity = UART_PARITY_DISABLE; + uart.stop_bits = UART_STOP_BITS_1; + } + + void YL620::direction_command(SpindleState mode, ModbusCommand& data) { + // NOTE: data length is excluding the CRC16 checksum. + data.tx_length = 6; + data.rx_length = 6; + + // data.msg[0] is omitted (modbus address is filled in later) + data.msg[1] = 0x06; // 06: write output register + data.msg[2] = 0x20; // 0x2000: command register address + data.msg[3] = 0x00; + + data.msg[4] = 0x00; // High-Byte of command always 0x00 + switch (mode) { + case SpindleState::Cw: + data.msg[5] = 0x12; // Start in forward direction + break; + case SpindleState::Ccw: + data.msg[5] = 0x22; // Start in reverse direction + break; + default: // SpindleState::Disable + data.msg[5] = 0x01; // Disable spindle + break; + } + } + + void YL620::set_speed_command(uint32_t rpm, ModbusCommand& data) { + // NOTE: data length is excluding the CRC16 checksum. + data.tx_length = 6; + data.rx_length = 6; + + // We have to know the max RPM before we can set the current RPM: + auto max_rpm = this->_max_rpm; + auto max_frequency = this->_maxFrequency; + + uint16_t freqFromRPM = (uint16_t(rpm) * uint16_t(max_frequency)) / uint16_t(max_rpm); + + #ifdef VFD_DEBUG_MODE + grbl_msg_sendf(CLIENT_SERIAL, MsgLevel::Info, "For %d RPM the output frequency is set to %d Hz*10", int(rpm), int(freqFromRPM)); + #endif + + data.msg[1] = 0x06; + data.msg[2] = 0x20; + data.msg[3] = 0x01; + data.msg[4] = uint8_t(freqFromRPM >> 8); + data.msg[5] = uint8_t(freqFromRPM & 0xFF); + } + + VFD::response_parser YL620::initialization_sequence(int index, ModbusCommand& data) { + if (index == -1) { + + // NOTE: data length is excluding the CRC16 checksum. + data.tx_length = 6; + data.rx_length = 5; + + data.msg[1] = 0x03; + data.msg[2] = 0x03; + data.msg[3] = 0x08; + data.msg[4] = 0x00; + data.msg[5] = 0x01; + + // Recv: 01 03 02 03 E8 xx xx + // -- -- = 1000 + return [](const uint8_t* response, Spindles::VFD* vfd) -> bool { + auto yl620 = static_cast(vfd); + yl620->_minFrequency = (uint16_t(response[3]) << 8) | uint16_t(response[4]); + + #ifdef VFD_DEBUG_MODE + grbl_msg_sendf(CLIENT_SERIAL, MsgLevel::Info, "YL620 allows minimum frequency of %d Hz", int(yl620->_minFrequency)); + #endif + + return true; + }; + } + else if (index == -2) { + // NOTE: data length is excluding the CRC16 checksum. + data.tx_length = 6; + data.rx_length = 5; + + data.msg[1] = 0x03; + data.msg[2] = 0x00; + data.msg[3] = 0x00; + data.msg[4] = 0x00; + data.msg[5] = 0x01; + + // Recv: 01 03 02 0F A0 xx xx + // -- -- = 4000 + return [](const uint8_t* response, Spindles::VFD* vfd) -> bool { + auto yl620 = static_cast(vfd); + yl620->_maxFrequency = (uint16_t(response[3]) << 8) | uint16_t(response[4]); + + vfd->_min_rpm = uint32_t(yl620->_minFrequency) * uint32_t(vfd->_max_rpm) / uint32_t(yl620->_maxFrequency); // 1000 * 24000 / 4000 = 6000 RPM. + + + #ifdef VFD_DEBUG_MODE + grbl_msg_sendf(CLIENT_SERIAL, MsgLevel::Info, "YL620 allows maximum frequency of %d Hz", int(yl620->_maxFrequency)); + grbl_msg_sendf(CLIENT_SERIAL, MsgLevel::Info, "Configured maxRPM of %d RPM results in minRPM of %d RPM", int(vfd->_max_rpm), int(vfd->_min_rpm)); + #endif + + return true; + }; + } + else { + return nullptr; + } + } + + VFD::response_parser YL620::get_current_rpm(ModbusCommand& data) { + // NOTE: data length is excluding the CRC16 checksum. + data.tx_length = 6; + data.rx_length = 5; + + // Send: 01 03 200B 0001 + data.msg[1] = 0x03; + data.msg[2] = 0x20; + data.msg[3] = 0x0B; + data.msg[4] = 0x00; + data.msg[5] = 0x01; + + // Recv: 01 03 02 05 DC xx xx + // ---- = 1500 + return [](const uint8_t* response, Spindles::VFD* vfd) -> bool { + uint16_t freq = (uint16_t(response[3]) << 8) | uint16_t(response[4]); + + auto yl620 = static_cast(vfd); + + uint16_t rpm = freq * uint16_t(vfd->_max_rpm) / uint16_t(yl620->_maxFrequency); + + // Set current RPM value? Somewhere? + vfd->_sync_rpm = rpm; + return true; + }; + } + + VFD::response_parser YL620::get_current_direction(ModbusCommand& data) { + // NOTE: data length is excluding the CRC16 checksum. + data.tx_length = 6; + data.rx_length = 5; + + // Send: 01 03 20 00 00 01 + data.msg[1] = 0x03; + data.msg[2] = 0x20; + data.msg[3] = 0x00; + data.msg[4] = 0x00; + data.msg[5] = 0x01; + + // Receive: 01 03 02 00 0A xx xx + // ----- status is in 00 0A bit 5:4 + + // TODO: What are we going to do with this? Update sys.spindle_speed? Update vfd state? + return [](const uint8_t* response, Spindles::VFD* vfd) -> bool { return true; }; + } +} diff --git a/Grbl_Esp32/src/Spindles/YL620Spindle.h b/Grbl_Esp32/src/Spindles/YL620Spindle.h new file mode 100644 index 00000000..4e32046b --- /dev/null +++ b/Grbl_Esp32/src/Spindles/YL620Spindle.h @@ -0,0 +1,43 @@ +#pragma once + +#include "VFDSpindle.h" + +/* + YL620Spindle.h + + Part of Grbl_ESP32 + 2021 - Marco Wagner + + Grbl is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + Grbl is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with Grbl. If not, see . + +*/ + +namespace Spindles { + class YL620 : public VFD { + protected: + uint16_t _minFrequency = 0; // frequency lower limit. Factor 10 of actual frequency + uint16_t _maxFrequency = 4000; // max frequency the VFD will allow. Normally 400.0. Factor 10 of actual frequency + + void default_modbus_settings(uart_config_t& uart) override; + + void direction_command(SpindleState mode, ModbusCommand& data) override; + void set_speed_command(uint32_t rpm, ModbusCommand& data) override; + + response_parser initialization_sequence(int index, ModbusCommand& data) override; + response_parser get_current_rpm(ModbusCommand& data) override; + response_parser get_current_direction(ModbusCommand& data) override; + response_parser get_status_ok(ModbusCommand& data) override { return nullptr; } + + bool supports_actual_rpm() const override { return true; } + bool safety_polling() const override { return false; } + }; +}