## v0.3.21
- Implemented M70-M73 modal state save/restore.
- Added support for modbus VFDs.
+ - Start Huanyang spindle with out first pressing Start button on VFD.
## v0.3.20
- Eliminated drift caused by miscounting half microsteps.
{
"name": "bbctrl",
- "version": "0.3.21",
+ "version": "0.3.21.1",
"homepage": "http://buildbotics.com/",
"repository": "https://github.com/buildbotics/bbctrl-firmware",
"license": "GPL-3.0+",
CLOCK = 32000000
# SRC
-SRC = $(wildcard src/*.c) $(wildcard src/*.cpp)
-OBJ = $(patsubst src/%.cpp,build/%.o,$(patsubst src/%.c,build/%.o,$(SRC)))
+SRC = $(wildcard src/*.c) $(wildcard src/*.cpp) $(wildcard src/vfd/*.c)
+OBJ := $(patsubst src/%.c,build/%.o,$(SRC))
+OBJ := $(patsubst src/%.cpp,build/%.o,$(OBJ))
+OBJ := $(patsubst src/vfd/%.c,build/vfd/%.o,$(OBJ))
JSON = vars command messages
JSON := $(patsubst %,build/%.json,$(JSON))
+++ /dev/null
-/******************************************************************************\
-
- This file is part of the Buildbotics firmware.
-
- Copyright (c) 2015 - 2018, Buildbotics LLC
- All rights reserved.
-
- This file ("the software") is free software: you can redistribute it
- and/or modify it under the terms of the GNU General Public License,
- version 2 as published by the Free Software Foundation. You should
- have received a copy of the GNU General Public License, version 2
- along with the software. If not, see <http://www.gnu.org/licenses/>.
-
- The software is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with the software. If not, see
- <http://www.gnu.org/licenses/>.
-
- For information regarding this software email:
- "Joseph Coffland" <joseph@buildbotics.com>
-
-\******************************************************************************/
-
-#include "huanyang.h"
-#include "config.h"
-#include "modbus.h"
-#include "report.h"
-
-#include <string.h>
-#include <math.h>
-
-
-/*
- Huanyang is not quite Modbus compliant.
-
- Message format is:
-
- [id][func][length][data][checksum]
-
- Where:
-
- id - 1-byte Peer ID
- func - 1-byte One of hy_func_t
- length - 1-byte Length data in bytes
- data - length bytes - Command arguments
- checksum - 16-bit CRC: x^16 + x^15 + x^2 + 1 (0xa001) initial: 0xffff
-*/
-
-
-// See VFD manual pg56 3.1.3
-typedef enum {
- HUANYANG_FUNC_READ = 1, // [len=1][hy_addr_t]
- HUANYANG_FUNC_WRITE, // [len=3][hy_addr_t][data]
- HUANYANG_CTRL_WRITE, // [len=1][hy_ctrl_state_t]
- HUANYANG_CTRL_READ, // [len=1][hy_ctrl_addr_t]
- HUANYANG_FREQ_WRITE, // [len=2][freq]
- HUANYANG_RESERVED_1,
- HUANYANG_RESERVED_2,
- HUANYANG_LOOP_TEST,
-} hy_func_t;
-
-
-// Sent in HUANYANG_CTRL_WRITE
-// See VFD manual pg57 3.1.3.c
-typedef enum {
- HUANYANG_RUN = 1 << 0,
- HUANYANG_FORWARD = 1 << 1,
- HUANYANG_REVERSE = 1 << 2,
- HUANYANG_STOP = 1 << 3,
- HUANYANG_REV_FWD = 1 << 4,
- HUANYANG_JOG = 1 << 5,
- HUANYANG_JOG_FORWARD = 1 << 6,
- HUANYANG_JOG_REVERSE = 1 << 7,
-} hy_ctrl_state_t;
-
-
-// Returned by HUANYANG_CTRL_WRITE
-// See VFD manual pg57 3.1.3.c
-typedef enum {
- HUANYANG_STATUS_RUN = 1 << 0,
- HUANYANG_STATUS_JOG = 1 << 1,
- HUANYANG_STATUS_COMMAND_REV = 1 << 2,
- HUANYANG_STATUS_RUNNING = 1 << 3,
- HUANYANG_STATUS_JOGGING = 1 << 4,
- HUANYANG_STATUS_JOGGING_REV = 1 << 5,
- HUANYANG_STATUS_BRAKING = 1 << 6,
- HUANYANG_STATUS_TRACK_START = 1 << 7,
-} hy_ctrl_status_t;
-
-
-// Sent in HUANYANG_CTRL_READ
-// See VFD manual pg57 3.1.3.d
-typedef enum {
- HUANYANG_TARGET_FREQ,
- HUANYANG_ACTUAL_FREQ,
- HUANYANG_ACTUAL_CURRENT,
- HUANYANG_ACTUAL_RPM,
- HUANYANG_DCV,
- HUANYANG_ACV,
- HUANYANG_COUNTER,
- HUANYANG_TEMPERATURE,
-} hy_ctrl_addr_t;
-
-
-static struct {
- uint8_t id;
-
- uint8_t state;
- hy_func_t func;
- uint8_t data[4];
- uint8_t bytes;
- uint8_t response;
-
- bool shutdown;
- bool changed;
- float speed;
-
- float actual_freq;
- float actual_current;
- uint16_t actual_rpm;
- uint16_t temperature;
-
- float max_freq;
- float min_freq;
- uint16_t rated_rpm;
-
- uint8_t status;
-} hy = {1}; // Default ID
-
-
-static void _func_read(hy_addr_t addr) {
- hy.func = HUANYANG_FUNC_READ;
- hy.bytes = 2;
- hy.response = 4;
- hy.data[0] = 1;
- hy.data[1] = addr;
-}
-
-
-static void _ctrl_write(hy_ctrl_state_t state) {
- hy.func = HUANYANG_CTRL_WRITE;
- hy.bytes = 2;
- hy.response = 2;
- hy.data[0] = 1;
- hy.data[1] = state;
-}
-
-
-static void _ctrl_read(hy_ctrl_addr_t addr) {
- hy.func = HUANYANG_CTRL_READ;
- hy.bytes = 2;
- hy.response = 4;
- hy.data[0] = 1;
- hy.data[1] = addr;
-}
-
-
-static void _freq_write(uint16_t freq) {
- hy.func = HUANYANG_FREQ_WRITE;
- hy.bytes = 3;
- hy.response = 3;
- hy.data[0] = 2;
- hy.data[1] = freq >> 8;
- hy.data[2] = freq;
-}
-
-
-static void _func_read_response(hy_addr_t addr, uint16_t value) {
- switch (addr) {
- case HY_PD005_MAX_FREQUENCY: hy.max_freq = value * 0.01; break;
- case HY_PD011_FREQUENCY_LOWER_LIMIT: hy.min_freq = value * 0.01; break;
- case HY_PD144_RATED_MOTOR_RPM: hy.rated_rpm = value; break;
- default: break;
- }
-}
-
-
-static void _ctrl_read_response(hy_ctrl_addr_t addr, uint16_t value) {
- switch (addr) {
- case HUANYANG_ACTUAL_FREQ: hy.actual_freq = value * 0.01; break;
- case HUANYANG_ACTUAL_CURRENT: hy.actual_current = value * 0.01; break;
- case HUANYANG_ACTUAL_RPM: hy.actual_rpm = value; break;
- case HUANYANG_TEMPERATURE: hy.temperature = value; break;
- default: break;
- }
-}
-
-
-static uint16_t _read_word(const uint8_t *data) {
- return (uint16_t)data[0] << 8 | data[1];
-}
-
-
-static void _handle_response(hy_func_t func, const uint8_t *data) {
- switch (func) {
- case HUANYANG_FUNC_READ:
- _func_read_response((hy_addr_t)*data, _read_word(data + 1));
- break;
-
- case HUANYANG_FUNC_WRITE: break;
- case HUANYANG_CTRL_WRITE: hy.status = *data; break;
-
- case HUANYANG_CTRL_READ:
- _ctrl_read_response((hy_ctrl_addr_t)*data, _read_word(data + 1));
- break;
-
- case HUANYANG_FREQ_WRITE: break;
- default: break;
- }
-}
-
-
-static void _next_command();
-
-
-static void _reset(bool halt) {
- // Save settings
- uint8_t id = hy.id;
- float speed = hy.speed;
-
- // Clear state
- memset(&hy, 0, sizeof(hy));
-
- // Restore settings
- hy.id = id;
- hy.speed = speed;
- hy.changed = true;
-
- if (!halt) _next_command();
-}
-
-
-static void _modbus_cb(uint8_t slave, uint8_t func, uint8_t bytes,
- const uint8_t *data) {
- if (!data) _reset(true);
-
- else if (bytes == *data + 1) {
- _handle_response((hy_func_t)func, data + 1);
-
- if (func == HUANYANG_CTRL_WRITE && hy.shutdown) {
- _reset(true);
- modbus_deinit();
- return;
- }
-
- // Next command
- if (++hy.state == 9) {
- hy.state = 5;
- report_request();
- }
-
- // Update freq
- if (hy.shutdown || (hy.changed && 4 < hy.state)) hy.state = 0;
- }
-
- _next_command();
-}
-
-
-static void _next_command() {
- switch (hy.state) {
- case 0: { // Update direction
- hy_ctrl_state_t state = HUANYANG_STOP;
- if (!hy.shutdown) {
- if (0 < hy.speed) state = HUANYANG_RUN;
- else if (hy.speed < 0)
- state = (hy_ctrl_state_t)(HUANYANG_RUN | HUANYANG_REV_FWD);
- }
-
- _ctrl_write(state);
- break;
- }
-
- case 1: _func_read(HY_PD005_MAX_FREQUENCY); break;
- case 2: _func_read(HY_PD011_FREQUENCY_LOWER_LIMIT); break;
- case 3: _func_read(HY_PD144_RATED_MOTOR_RPM); break;
-
- case 4: { // Update freqency
- // Compute frequency in Hz
- float freq = fabs(hy.speed * 50 / hy.rated_rpm);
-
- // Clamp frequency
- if (hy.max_freq < freq) freq = hy.max_freq;
- if (freq < hy.min_freq) freq = hy.min_freq;
-
- // Frequency write command
- _freq_write(freq * 100);
- hy.changed = false;
- break;
- }
-
- case 5: _ctrl_read(HUANYANG_ACTUAL_FREQ); break;
- case 6: _ctrl_read(HUANYANG_ACTUAL_CURRENT); break;
- case 7: _ctrl_read(HUANYANG_ACTUAL_RPM); break;
- case 8: _ctrl_read(HUANYANG_TEMPERATURE); break;
- }
-
- // Send command
- modbus_func(hy.id, hy.func, hy.bytes, hy.data, hy.response, _modbus_cb);
-}
-
-
-void hy_init() {
- modbus_init();
- _reset(false);
-}
-
-
-void hy_deinit() {hy.shutdown = true;}
-
-
-void hy_set(float speed) {
- if (hy.speed != speed) {
- hy.speed = speed;
- hy.changed = true;
- }
-}
-
-
-void hy_stop() {
- hy_set(0);
- _reset(false);
-}
-
-
-uint8_t get_hy_id() {return hy.id;}
-void set_hy_id(uint8_t value) {hy.id = value;}
-float get_hy_freq() {return hy.actual_freq;}
-float get_hy_current() {return hy.actual_current;}
-uint16_t get_hy_rpm() {return hy.actual_rpm;}
-uint16_t get_hy_temp() {return hy.temperature;}
-float get_hy_max_freq() {return hy.max_freq;}
-float get_hy_min_freq() {return hy.min_freq;}
-uint16_t get_hy_rated_rpm() {return hy.rated_rpm;}
-float get_hy_status() {return hy.status;}
+++ /dev/null
-/******************************************************************************\
-
- This file is part of the Buildbotics firmware.
-
- Copyright (c) 2015 - 2018, Buildbotics LLC
- All rights reserved.
-
- This file ("the software") is free software: you can redistribute it
- and/or modify it under the terms of the GNU General Public License,
- version 2 as published by the Free Software Foundation. You should
- have received a copy of the GNU General Public License, version 2
- along with the software. If not, see <http://www.gnu.org/licenses/>.
-
- The software is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with the software. If not, see
- <http://www.gnu.org/licenses/>.
-
- For information regarding this software email:
- "Joseph Coffland" <joseph@buildbotics.com>
-
-\******************************************************************************/
-
-#pragma once
-
-#include "spindle.h"
-
-
-void hy_init();
-void hy_deinit();
-void hy_set(float speed);
-void hy_stop();
-
-
-/// See Huanyang VFD user manual
-typedef enum {
- HY_PD000_PARAMETER_LOCK,
- HY_PD001_SOURCE_OF_OPERATION_COMMANDS,
- HY_PD002_SOURCE_OF_OPERATING_FREQUENCY,
- HY_PD003_MAIN_FREQUENCY,
- HY_PD004_BASE_FREQUENCY,
- HY_PD005_MAX_FREQUENCY,
- HY_PD006_INTERMEDIATE_FREQUENCY,
- HY_PD007_MIN_FREQUENCY,
- HY_PD008_MAX_VOLTAGE,
- HY_PD009_INTERMEDIATE_VOLTAGE,
- HY_PD010_MIN_VOLTAGE,
- HY_PD011_FREQUENCY_LOWER_LIMIT,
- HY_PD012_RESERVED,
- HY_PD013_PARAMETER_RESET,
- HY_PD014_ACCEL_TIME_1,
- HY_PD015_DECEL_TIME_1,
- HY_PD016_ACCEL_TIME_2,
- HY_PD017_DECEL_TIME_2,
- HY_PD018_ACCEL_TIME_3,
- HY_PD019_DECEL_TIME_3,
- HY_PD020_ACCEL_TIME_4,
- HY_PD021_DECEL_TIME_4,
- HY_PD022_FACTORY_RESERVED,
- HY_PD023_REV_ROTATION_SELECT,
- HY_PD024_STOP_KEY,
- HY_PD025_STARTING_MODE,
- HY_PD026_STOPPING_MODE,
- HY_PD027_STARTING_FREQUENCY,
- HY_PD028_STOPPING_FREQUENCY,
- HY_PD029_DC_BRAKING_TIME_AT_START,
- HY_PD030_DC_BRAKING_TIME_AT_STOP,
- HY_PD031_DC_BRAKING_VOLTAGE_LEVEL,
- HY_PD032_FREQUENCY_TRACK_TIME,
- HY_PD033_CURRENT_LEVEL_FOR_FREQUENCY_TRACK,
- HY_PD034_VOLTAGE_RISE_TIME_FOR_FREQUENCY_TRACK,
- HY_PD035_FREQUENCY_STEP_LENGTH,
- HY_PD036,
- HY_PD037,
- HY_PD038,
- HY_PD039,
- HY_PD040,
- HY_PD041_CARRIER_FREQUENCY,
- HY_PD042_JOGGING_FREQUENCY,
- HY_PD043_S_CURVE_TIME,
- HY_PD044_MULTI_INPUT_FOR,
- HY_PD045_MULTI_INPUT_REV,
- HY_PD046_MULTI_INPUT_RST,
- HY_PD047_MULTI_INPUT_SPH,
- HY_PD048_MULTI_INPUT_SPM,
- HY_PD049_MULTI_INPUT_SPL,
- HY_PD050_MULTI_OUTPUT_DRV,
- HY_PD051_MULTI_OUTPUT_UPF,
- HY_PD052_MULTI_OUTPUT_FA_FB_FC,
- HY_PD053_MULTI_OUTPUT_KA_KB,
- HY_PD054_MULTI_OUTPUT_AM,
- HY_PD055_AM_ANALOG_OUTPUT_GAIN,
- HY_PD056_SKIP_FREQUENCY_1,
- HY_PD057_SKIP_FREQUENCY_2,
- HY_PD058_SKIP_FREQUENCY_3,
- HY_PD059_SKIP_FREQUENCY_RANGE,
- HY_PD060_UNIFORM_FREQUENCY_1,
- HY_PD061_UNIFORM_FREQUENCY_2,
- HY_PD062_UNIFORM_FREQUENCY_RANGE,
- HY_PD063_TIMER_1_TIME,
- HY_PD064_TIMER_2_TIME,
- HY_PD065_COUNTING_VALUE,
- HY_PD066_INTERMEDIATE_COUNTER,
- HY_PD067,
- HY_PD068,
- HY_PD069,
- HY_PD070_ANALOG_INPUT,
- HY_PD071_ANALOG_FILTERING_CONSTANT,
- HY_PD072_HIGHER_ANALOG_FREQUENCY,
- HY_PD073_LOWER_ANALOG_FREQUENCY,
- HY_PD074_BIAS_DIRECTION_AT_HIGHER_FREQUENCY,
- HY_PD075_BIAS_DIRECTION_AT_LOWER_FREQUENCY,
- HY_PD076_ANALOG_NEGATIVE_BIAS_REVERSE,
- HY_PD077_UP_DOWN_FUNCTION,
- HY_PD078_UP_DOWN_SPEED,
- HY_PD079,
- HY_PD080_PLC_OPERATION,
- HY_PD081_AUTO_PLC,
- HY_PD082_PLC_RUNNING_DIRECTION,
- HY_PD083,
- HY_PD084_PLC_RAMP_TIME,
- HY_PD085,
- HY_PD086_FREQUENCY_2,
- HY_PD087_FREQUENCY_3,
- HY_PD088_FREQUENCY_4,
- HY_PD089_FREQUENCY_5,
- HY_PD090_FREQUENCY_6,
- HY_PD091_FREQUENCY_7,
- HY_PD092_FREQUENCY_8,
- HY_PD093,
- HY_PD094,
- HY_PD095,
- HY_PD096,
- HY_PD097,
- HY_PD098,
- HY_PD099,
- HY_PD100,
- HY_PD101_TIMER_1,
- HY_PD102_TIMER_2,
- HY_PD103_TIMER_3,
- HY_PD104_TIMER_4,
- HY_PD105_TIMER_5,
- HY_PD106_TIMER_6,
- HY_PD107_TIMER_7,
- HY_PD108_TIMER_8,
- HY_PD109,
- HY_PD110,
- HY_PD111,
- HY_PD112,
- HY_PD113,
- HY_PD114,
- HY_PD115,
- HY_PD116,
- HY_PD117_AUTOPLC_MEMORY_FUNCTION,
- HY_PD118_OVER_VOLTAGE_STALL_PREVENTION,
- HY_PD119_STALL_PREVENTION_LEVEL_AT_RAMP_UP,
- HY_PD120_STALL_PREVENTION_LEVEL_AT_CONSTANT_SPEED,
- HY_PD121_DECEL_TIME_FOR_STALL_PREVENTION_AT_CONSTANT_SPEED,
- HY_PD122_STALL_PREVENTION_LEVEL_AT_DECELERATION,
- HY_PD123_OVER_TORQUE_DETECT_MODE,
- HY_PD124_OVER_TORQUE_DETECT_LEVEL,
- HY_PD125_OVER_TORQUE_DETECT_TIME,
- HY_PD126,
- HY_PD127,
- HY_PD128,
- HY_PD129,
- HY_PD130_NUMBER_OF_AUXILIARY_PUMP,
- HY_PD131_CONTINUOUS_RUNNING_TIME_OF_AUXILIARY_PUMPS,
- HY_PD132_INTERLOCKING_TIME_OF_AUXILIARY_PUMP,
- HY_PD133_HIGH_SPEED_RUNNING_TIME,
- HY_PD134_LOW_SPEED_RUNNING_TIME,
- HY_PD135_STOPPING_VOLTAGE_LEVEL,
- HY_PD136_LASTING_TIME_OF_STOPPING_VOLTAGE_LEVEL,
- HY_PD137_WAKEUP_VOLTAGE_LEVEL,
- HY_PD138_SLEEP_FREQUENCY,
- HY_PD139_LASTING_TIME_OF_SLEEP_FREQUENCY,
- HY_PD140,
- HY_PD141_RATED_MOTOR_VOLTAGE,
- HY_PD142_RATED_MOTOR_CURRENT,
- HY_PD143_MOTOR_POLE_NUMBER,
- HY_PD144_RATED_MOTOR_RPM,
- HY_PD145_AUTO_TORQUE_COMPENSATION,
- HY_PD146_MOTOR_NO_LOAD_CURRENT,
- HY_PD147_MOTOR_SLIP_COMPENSATION,
- HY_PD148,
- HY_PD149,
- HY_PD150_AUTO_VOLTAGE_REGULATION,
- HY_PD151_AUTO_ENERGY_SAVING,
- HY_PD152_FAULT_RESTART_TIME,
- HY_PD153_RESTART_AFTER_INSTANTANEOUS_STOP,
- HY_PD154_ALLOWABLE_POWER_BREAKDOWN_TIME,
- HY_PD155_NUMBER_OF_ABNORMAL_RESTART,
- HY_PD156_PROPORTIONAL_CONSTANT,
- HY_PD157_INTEGRAL_TIME,
- HY_PD158_DIFFERENTIAL_TIME,
- HY_PD159_TARGET_VALUE,
- HY_PD160_PID_TARGET_VALUE,
- HY_PD161_PID_UPPER_LIMIT,
- HY_PD162_PID_LOWER_LIMIT,
- HY_PD163_COMMUNICATION_ADDRESSES,
- HY_PD164_COMMUNICATION_BAUD_RATE,
- HY_PD165_COMMUNICATION_DATA_METHOD,
- HY_PD166,
- HY_PD167,
- HY_PD168,
- HY_PD169,
- HY_PD170_DISPLAY_ITEMS,
- HY_PD171_DISPLAY_ITEMS_OPEN,
- HY_PD172_FAULT_CLEAR,
- HY_PD173,
- HY_PD174_RATED_CURRENT_OF_INVERTER,
- HY_PD175_INVERTER_MODEL,
- HY_PD176_INVERTER_FREQUENCY_STANDARD,
- HY_PD177_FAULT_RECORD_1,
- HY_PD178_FAULT_RECORD_2,
- HY_PD179_FAULT_RECORD_3,
- HY_PD180_FAULT_RECORD_4,
- HY_PD181_SOFTWARE_VERSION,
- HY_PD182_MANUFACTURE_DATE,
- HY_PD183_SERIAL_NO,
-} hy_addr_t;
#include "stepper.h"
#include "motor.h"
#include "switch.h"
-#include "spindle.h"
#include "usart.h"
#include "drv8711.h"
#include "vars.h"
stepper_init(); // steppers
motor_init(); // motors
switch_init(); // switches
- spindle_init(); // spindles
exec_init(); // motion exec
vars_init(); // configuration variables
estop_init(); // emergency stop handler
static struct {
bool debug;
- uint8_t baud;
+ uint8_t id;
+ baud_t baud;
+ parity_t parity;
+ stop_t stop;
uint8_t bytes;
uint8_t command[MODBUS_BUF_SIZE];
modbus_cb_t receive_cb;
+ uint16_t addr;
+ modbus_read_cb_t read_cb;
+ modbus_write_cb_t write_cb;
+
uint32_t last;
uint8_t retry;
bool connected;
bool busy;
-} mb = {false, USART_BAUD_9600};
+} mb = {false, 1, USART_BAUD_9600, USART_NONE, USART_1STOP};
static uint16_t _crc16(const uint8_t *buffer, unsigned length) {
mb.last = 0; // Clear timeout timer
mb.retry = 0; // Reset retry counter
-
- if (mb.receive_cb)
- mb.receive_cb(mb.response[0], mb.response[1], mb.response_length - 4,
- mb.response + 2);
-
mb.connected = true;
mb.busy = false;
+
+ if (mb.receive_cb)
+ mb.receive_cb(mb.response[1], mb.response_length - 4, mb.response + 2);
}
}
+void _read_cb(uint8_t func, uint8_t bytes, const uint8_t *data) {
+ if (func == MODBUS_READ_OUTPUT_REG && bytes == 3 && data[0] == 2) {
+ uint16_t value = (uint16_t)data[1] << 8 | data[2];
+ if (mb.read_cb) mb.read_cb(true, mb.addr, value);
+ return;
+ }
+
+ if (mb.read_cb) mb.read_cb(false, mb.addr, 0);
+}
+
+
+void _write_cb(uint8_t func, uint8_t bytes, const uint8_t *data) {
+ if (func == MODBUS_WRITE_OUTPUT_REG && bytes == 4) {
+ uint16_t addr = (uint16_t)data[0] << 8 | data[1];
+
+ if (addr == mb.addr) {
+ if (mb.write_cb) mb.write_cb(true, mb.addr);
+ return;
+ }
+ }
+
+ if (mb.write_cb) mb.write_cb(false, mb.addr);
+}
+
+
static void _reset() {
_set_dre_interrupt(false);
_set_txc_interrupt(false);
// Save settings
bool debug = mb.debug;
- uint8_t baud = mb.baud;
+ uint8_t id = mb.id;
+ baud_t baud = mb.baud;
+ parity_t parity = mb.parity;
+ stop_t stop = mb.stop;
// Clear state
memset(&mb, 0, sizeof(mb));
// Restore settings
mb.debug = debug;
+ mb.id = id;
mb.baud = baud;
+ mb.parity = parity;
+ mb.stop = stop;
}
if (mb.debug) STATUS_DEBUG("modbus: timedout");
modbus_cb_t cb = mb.receive_cb;
- uint8_t id = mb.command[0];
uint8_t func = mb.command[1];
_reset();
// Notify caller
- if (cb) cb(id, func, 0, 0);
+ if (cb) cb(func, 0, 0);
}
OUTSET_PIN(RS485_RW_PIN); // High
DIRSET_PIN(RS485_RW_PIN); // Output
- usart_set_port_baud(&RS485_PORT, mb.baud);
-
- // No parity, 8 data bits, 1 stop bit
- RS485_PORT.CTRLC = USART_CMODE_ASYNCHRONOUS_gc | USART_PMODE_DISABLED_gc |
- USART_CHSIZE_8BIT_gc;
-
- // Enable receiver and transmitter
- RS485_PORT.CTRLB |= USART_RXEN_bm | USART_TXEN_bm;
-
- // TODO remove this
- PINCTRL_PIN(RS485_RO_PIN) ^= PORT_INVEN_bm;
- PINCTRL_PIN(RS485_DI_PIN) ^= PORT_INVEN_bm;
+ usart_init_port(&RS485_PORT, mb.baud, mb.parity, USART_8BITS, mb.stop);
_reset();
}
bool modbus_busy() {return mb.busy;}
-void modbus_func(uint8_t slave, uint8_t func, uint8_t send, const uint8_t *data,
+void modbus_func(uint8_t func, uint8_t send, const uint8_t *data,
uint8_t receive, modbus_cb_t receive_cb) {
ASSERT(send + 4 <= MODBUS_BUF_SIZE);
ASSERT(receive + 4 <= MODBUS_BUF_SIZE);
- _reset();
-
- mb.command[0] = slave;
+ mb.command[0] = mb.id;
mb.command[1] = func;
memcpy(mb.command + 2, data, send);
uint16_t crc = _crc16(mb.command, send + 2);
mb.command[send + 2] = crc;
mb.command[send + 3] = crc >> 8;
+ mb.bytes = 0;
mb.command_length = send + 4;
mb.response_length = receive + 4;
mb.receive_cb = receive_cb;
+ mb.last = 0;
+ mb.retry = 0;
_set_dre_interrupt(true);
}
+void modbus_read(uint16_t addr, modbus_read_cb_t cb) {
+ mb.read_cb = cb;
+ mb.addr = addr;
+ uint8_t cmd[4] = {(uint8_t)(addr >> 8), (uint8_t)addr, 0, 1};
+ modbus_func(MODBUS_READ_OUTPUT_REG, 4, cmd, 3, _read_cb);
+}
+
+
+void modbus_write(uint16_t addr, uint16_t value, modbus_write_cb_t cb) {
+ mb.write_cb = cb;
+ mb.addr = addr;
+ uint8_t cmd[4] = {(uint8_t)(addr >> 8), (uint8_t)addr, (uint8_t)(value >> 8),
+ (uint8_t)value};
+ modbus_func(MODBUS_WRITE_OUTPUT_REG, 4, cmd, 4, _write_cb);
+}
+
+
void modbus_rtc_callback() {
if (!mb.last || !rtc_expired(mb.last + MODBUS_TIMEOUT)) return;
// Variable callbacks
bool get_modbus_debug() {return mb.debug;}
void set_modbus_debug(bool value) {mb.debug = value;}
+uint8_t get_modbus_id() {return mb.id;}
+void set_modbus_id(uint8_t id) {mb.id = id;}
uint8_t get_modbus_baud() {return mb.baud;}
void set_modbus_baud(uint8_t baud) {
- mb.baud = baud;
- usart_set_port_baud(&RS485_PORT, baud);
+ mb.baud = (baud_t)baud;
+ usart_set_baud(&RS485_PORT, mb.baud);
+}
+
+
+uint8_t get_modbus_parity() {return mb.parity;}
+
+
+void set_modbus_parity(uint8_t parity) {
+ mb.parity = (parity_t)parity;
+ usart_set_parity(&RS485_PORT, mb.parity);
+}
+
+
+uint8_t get_modbus_stop() {return mb.stop;}
+
+
+void set_modbus_stop(uint8_t stop) {
+ mb.stop = (stop_t)stop;
+ usart_set_stop(&RS485_PORT, mb.stop);
}
} modbus_base_addrs_t;
-typedef void (*modbus_cb_t)(uint8_t slave, uint8_t func, uint8_t bytes,
- const uint8_t *data);
+typedef void (*modbus_cb_t)(uint8_t func, uint8_t bytes, const uint8_t *data);
+typedef void (*modbus_read_cb_t)(bool ok, uint16_t addr, uint16_t value);
+typedef void (*modbus_write_cb_t)(bool ok, uint16_t addr);
void modbus_init();
void modbus_deinit();
bool modbus_busy();
-void modbus_func(uint8_t slave, uint8_t func, uint8_t send, const uint8_t *data,
- uint8_t receive, modbus_cb_t receive_cb);
+void modbus_func(uint8_t func, uint8_t send, const uint8_t *data,
+ uint8_t receive, modbus_cb_t cb);
+void modbus_read(uint16_t addr, modbus_read_cb_t cb);
+void modbus_write(uint16_t addr, uint16_t value, modbus_write_cb_t cb);
void modbus_rtc_callback();
typedef struct {
uint16_t freq; // base frequency for PWM driver, in Hz
- float min_rpm;
- float max_rpm;
float min_duty;
float max_duty;
float duty;
float speed = spindle.speed;
// Disable
- if (speed <= spindle.min_rpm || estop_triggered()) {
+ if (!speed || estop_triggered()) {
TIMER_PWM.CTRLA = 0;
OUTCLR_PIN(SPIN_PWM_PIN);
_set_enable(false);
_set_enable(true);
// 100% duty
- if (spindle.max_rpm <= speed && spindle.max_duty == 1) {
+ if (speed == 1 && spindle.max_duty == 1) {
TIMER_PWM.CTRLB = 0;
OUTSET_PIN(SPIN_PWM_PIN);
return;
}
- // Clamp speed
- if (spindle.max_rpm < speed) speed = spindle.max_rpm;
-
// Set clock period and optimal prescaler value
float prescale = (float)(F_CPU >> 16) / spindle.freq;
if (prescale <= 1) {
} else TIMER_PWM.CTRLA = 0;
- // Map RPM to duty cycle
+ // Compute duty cycle
spindle.duty =
- (speed - spindle.min_rpm) / (spindle.max_rpm - spindle.min_rpm) *
- (spindle.max_duty - spindle.min_duty) + spindle.min_duty;
+ speed * (spindle.max_duty - spindle.min_duty) + spindle.min_duty;
// Configure clock
TIMER_PWM.CTRLB = TC1_CCAEN_bm | TC_WGMODE_SINGLESLOPE_gc;
}
+float pwm_spindle_get() {return spindle.speed;}
void pwm_spindle_stop() {pwm_spindle_set(0);}
-// TODO these need more effort and should work with the huanyang spindle too
-float get_max_spin() {return spindle.max_rpm;}
-void set_max_spin(float value) {spindle.max_rpm = value; _update_pwm();}
-float get_min_spin() {return spindle.min_rpm;}
-void set_min_spin(float value) {spindle.min_rpm = value; _update_pwm();}
+// Var callbacks
float get_pwm_min_duty() {return spindle.min_duty * 100;}
void pwm_spindle_init();
void pwm_spindle_deinit();
void pwm_spindle_set(float speed);
+float pwm_spindle_get();
void pwm_spindle_stop();
#include "spindle.h"
#include "config.h"
-#include "pwm_spindle.h"
-#include "huanyang.h"
#include "pgmspace.h"
+#include <math.h>
+
typedef enum {
SPINDLE_TYPE_DISABLED,
- SPINDLE_TYPE_PWM,
- SPINDLE_TYPE_HUANYANG,
+#define SPINDLE(NAME) SPINDLE_TYPE_##NAME,
+#include "spindle.def"
+#undef SPINDLE
} spindle_type_t;
+// Function decls
+#define SPINDLE(NAME) \
+ void NAME##_init(); \
+ void NAME##_deinit(); \
+ void NAME##_set(float speed); \
+ float NAME##_get(); \
+ void NAME##_stop();
+#include "spindle.def"
+#undef SPINDLE
+
+
typedef struct {
spindle_type_t type;
float speed;
bool reversed;
+ float min_rpm;
+ float max_rpm;
} spindle_t;
static spindle_t spindle = {SPINDLE_TYPE_DISABLED,};
-void spindle_init() {}
-
-
void spindle_set_speed(float speed) {
spindle.speed = speed;
+ bool negative = speed < 0;
+ if (spindle.max_rpm <= fabs(speed)) speed = negative ? -1 : 1;
+ else if (fabs(speed) < spindle.min_rpm) speed = 0;
+ else speed /= spindle.max_rpm;
+
if (spindle.reversed) speed = -speed;
switch (spindle.type) {
case SPINDLE_TYPE_DISABLED: break;
- case SPINDLE_TYPE_PWM: pwm_spindle_set(speed); break;
- case SPINDLE_TYPE_HUANYANG: hy_set(speed); break;
+#define SPINDLE(NAME) case SPINDLE_TYPE_##NAME: NAME##_set(speed); break;
+#include "spindle.def"
+#undef SPINDLE
}
}
-float spindle_get_speed() {return spindle.speed;}
+float spindle_get_speed() {
+ float speed = 0;
+
+ switch (spindle.type) {
+ case SPINDLE_TYPE_DISABLED: break;
+#define SPINDLE(NAME) case SPINDLE_TYPE_##NAME: speed = NAME##_get(); break;
+#include "spindle.def"
+#undef SPINDLE
+ }
+
+ return speed * spindle.max_rpm;
+}
void spindle_stop() {
switch (spindle.type) {
case SPINDLE_TYPE_DISABLED: break;
- case SPINDLE_TYPE_PWM: pwm_spindle_stop(); break;
- case SPINDLE_TYPE_HUANYANG: hy_stop(); break;
+#define SPINDLE(NAME) case SPINDLE_TYPE_##NAME: NAME##_stop(); break;
+#include "spindle.def"
+#undef SPINDLE
}
}
+bool spindle_is_reversed() {return spindle.reversed;}
+static void _update_speed() {spindle_set_speed(spindle.speed);}
+
+
+// Var callbacks
uint8_t get_spindle_type() {return spindle.type;}
switch (spindle.type) {
case SPINDLE_TYPE_DISABLED: break;
- case SPINDLE_TYPE_PWM: pwm_spindle_deinit(); break;
- case SPINDLE_TYPE_HUANYANG: hy_deinit(); break;
+#define SPINDLE(NAME) case SPINDLE_TYPE_##NAME: NAME##_deinit(); break;
+#include "spindle.def"
+#undef SPINDLE
}
spindle.type = (spindle_type_t)value;
switch (spindle.type) {
case SPINDLE_TYPE_DISABLED: break;
- case SPINDLE_TYPE_PWM: pwm_spindle_init(); break;
- case SPINDLE_TYPE_HUANYANG: hy_init(); break;
+#define SPINDLE(NAME) case SPINDLE_TYPE_##NAME: NAME##_init(); break;
+#include "spindle.def"
+#undef SPINDLE
}
spindle_set_speed(speed);
}
-bool spindle_is_reversed() {return spindle.reversed;}
+void set_speed(float speed) {spindle_set_speed(speed);}
+float get_speed() {return spindle_get_speed();}
bool get_spin_reversed() {return spindle.reversed;}
}
-// Var callbacks
-void set_speed(float speed) {spindle_set_speed(speed);}
-float get_speed() {return spindle_get_speed();}
+float get_max_spin() {return spindle.max_rpm;}
+void set_max_spin(float value) {spindle.max_rpm = value; _update_speed();}
+float get_min_spin() {return spindle.min_rpm;}
+void set_min_spin(float value) {spindle.min_rpm = value; _update_speed();}
--- /dev/null
+/******************************************************************************\
+
+ This file is part of the Buildbotics firmware.
+
+ Copyright (c) 2015 - 2018, Buildbotics LLC
+ All rights reserved.
+
+ This file ("the software") is free software: you can redistribute it
+ and/or modify it under the terms of the GNU General Public License,
+ version 2 as published by the Free Software Foundation. You should
+ have received a copy of the GNU General Public License, version 2
+ along with the software. If not, see <http://www.gnu.org/licenses/>.
+
+ The software is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the software. If not, see
+ <http://www.gnu.org/licenses/>.
+
+ For information regarding this software email:
+ "Joseph Coffland" <joseph@buildbotics.com>
+
+\******************************************************************************/
+
+SPINDLE(pwm_spindle)
+SPINDLE(huanyang)
+SPINDLE(yl600)
#include <stdbool.h>
-void spindle_init();
+
void spindle_set_speed(float speed);
float spindle_get_speed();
void spindle_stop();
}
-void usart_init(void) {
+static void _set_baud(USART_t *port, uint16_t bsel, uint8_t bscale) {
+ port->BAUDCTRLB = (uint8_t)((bscale << 4) | (bsel >> 8));
+ port->BAUDCTRLA = bsel;
+ port->CTRLB |= USART_CLK2X_bm;
+}
+
+
+void usart_set_baud(USART_t *port, baud_t baud) {
+ // The BSEL / BSCALE values provided below assume a 32 Mhz clock
+ // With CTRLB CLK2X is set
+ // See http://www.avrcalc.elektronik-projekt.de/xmega/baud_rate_calculator
+
+ switch (baud) {
+ case USART_BAUD_9600: _set_baud(port, 3325, 0b1101); break;
+ case USART_BAUD_19200: _set_baud(port, 3317, 0b1100); break;
+ case USART_BAUD_38400: _set_baud(port, 3301, 0b1011); break;
+ case USART_BAUD_57600: _set_baud(port, 1095, 0b1100); break;
+ case USART_BAUD_115200: _set_baud(port, 1079, 0b1011); break;
+ case USART_BAUD_230400: _set_baud(port, 1047, 0b1010); break;
+ case USART_BAUD_460800: _set_baud(port, 983, 0b1001); break;
+ case USART_BAUD_921600: _set_baud(port, 107, 0b1011); break;
+ case USART_BAUD_500000: _set_baud(port, 1, 0b0010); break;
+ case USART_BAUD_1000000: _set_baud(port, 1, 0b0001); break;
+ }
+}
+
+
+void usart_set_parity(USART_t *port, parity_t parity) {
+ uint8_t reg = port->CTRLC & ~USART_PMODE_gm;
+
+ switch (parity) {
+ case USART_NONE: reg |= USART_PMODE_DISABLED_gc; break;
+ case USART_EVEN: reg |= USART_PMODE_EVEN_gc; break;
+ case USART_ODD: reg |= USART_PMODE_ODD_gc; break;
+ }
+
+ port->CTRLC = reg;
+}
+
+
+void usart_set_stop(USART_t *port, stop_t stop) {
+ switch (stop) {
+ case USART_1STOP: port->CTRLC &= ~USART_SBMODE_bm; break;
+ case USART_2STOP: port->CTRLC |= USART_SBMODE_bm; break;
+ }
+}
+
+
+void usart_set_bits(USART_t *port, bits_t bits) {
+ uint8_t reg = port->CTRLC & ~USART_CHSIZE_gm;
+
+ switch (bits) {
+ case USART_5BITS: reg |= USART_CHSIZE_5BIT_gc; break;
+ case USART_6BITS: reg |= USART_CHSIZE_6BIT_gc; break;
+ case USART_7BITS: reg |= USART_CHSIZE_7BIT_gc; break;
+ case USART_8BITS: reg |= USART_CHSIZE_8BIT_gc; break;
+ case USART_9BITS: reg |= USART_CHSIZE_9BIT_gc; break;
+ }
+
+ port->CTRLC = reg;
+}
+
+
+void usart_init_port(USART_t *port, baud_t baud, parity_t parity, bits_t bits,
+ stop_t stop) {
+ // Set baud rate
+ usart_set_baud(port, baud);
+
+ // Async, no parity, 8 data bits, 1 stop bit
+ port->CTRLC = USART_CMODE_ASYNCHRONOUS_gc;
+ usart_set_parity(port, parity);
+ usart_set_bits(port, bits);
+ usart_set_stop(port, stop);
+
+ // Configure receiver and transmitter
+ port->CTRLB |= USART_RXEN_bm | USART_TXEN_bm;
+}
+
+
+void usart_init() {
// Setup ring buffer
tx_buf_init();
rx_buf_init();
DIRSET_PIN(SERIAL_TX_PIN); // Tx Output
DIRCLR_PIN(SERIAL_RX_PIN); // Rx Input
- // Set baud rate
- usart_set_baud(SERIAL_BAUD);
-
- // No parity, 8 data bits, 1 stop bit
- SERIAL_PORT.CTRLC = USART_CMODE_ASYNCHRONOUS_gc | USART_PMODE_DISABLED_gc |
- USART_CHSIZE_8BIT_gc;
-
- // Configure receiver and transmitter
- SERIAL_PORT.CTRLB |= USART_RXEN_bm | USART_TXEN_bm;
+ // Configure port
+ usart_init_port(&SERIAL_PORT, SERIAL_BAUD, USART_NONE, USART_8BITS,
+ USART_1STOP);
PMIC.CTRL |= PMIC_HILVLEN_bm; // Interrupt level on
}
-static void _set_baud(USART_t *port, uint16_t bsel, uint8_t bscale) {
- port->BAUDCTRLB = (uint8_t)((bscale << 4) | (bsel >> 8));
- port->BAUDCTRLA = bsel;
- port->CTRLB |= USART_CLK2X_bm;
-}
-
-
-void usart_set_port_baud(USART_t *port, int baud) {
- // The BSEL / BSCALE values provided below assume a 32 Mhz clock
- // With CTRLB CLK2X is set
- // See http://www.avrcalc.elektronik-projekt.de/xmega/baud_rate_calculator
-
- switch (baud) {
- case USART_BAUD_9600: _set_baud(port, 3325, 0b1101); break;
- case USART_BAUD_19200: _set_baud(port, 3317, 0b1100); break;
- case USART_BAUD_38400: _set_baud(port, 3301, 0b1011); break;
- case USART_BAUD_57600: _set_baud(port, 1095, 0b1100); break;
- case USART_BAUD_115200: _set_baud(port, 1079, 0b1011); break;
- case USART_BAUD_230400: _set_baud(port, 1047, 0b1010); break;
- case USART_BAUD_460800: _set_baud(port, 983, 0b1001); break;
- case USART_BAUD_921600: _set_baud(port, 107, 0b1011); break;
- case USART_BAUD_500000: _set_baud(port, 1, 0b0010); break;
- case USART_BAUD_1000000: _set_baud(port, 1, 0b0001); break;
- }
-}
-
-
-void usart_set_baud(int baud) {
- usart_set_port_baud(&SERIAL_PORT, baud);
-}
-
void usart_set(int flag, bool enable) {
if (enable) usart_flags |= flag;
#define USART_RX_RING_BUF_SIZE 256
-enum {
+typedef enum {
USART_BAUD_9600,
USART_BAUD_19200,
USART_BAUD_38400,
USART_BAUD_921600,
USART_BAUD_500000,
USART_BAUD_1000000
-};
+} baud_t;
+
enum {
USART_CRLF = 1 << 0,
USART_FLUSH = 1 << 3,
};
+
+typedef enum {
+ USART_NONE,
+ USART_EVEN,
+ USART_ODD,
+} parity_t;
+
+
+typedef enum {
+ USART_1STOP,
+ USART_2STOP,
+} stop_t;
+
+
+typedef enum {
+ USART_5BITS,
+ USART_6BITS,
+ USART_7BITS,
+ USART_8BITS,
+ USART_9BITS,
+} bits_t;
+
+
+void usart_set_baud(USART_t *port, baud_t baud);
+void usart_set_parity(USART_t *port, parity_t parity);
+void usart_set_stop(USART_t *port, stop_t stop);
+void usart_set_bits(USART_t *port, bits_t bits);
+void usart_init_port(USART_t *port, baud_t baud, parity_t parity, bits_t bits,
+ stop_t stop);
+
void usart_init();
-void usart_set_port_baud(USART_t *port, int baud);
-void usart_set_baud(int baud);
void usart_set(int flag, bool enable);
bool usart_is_set(int flags);
void usart_putc(char c);
// Spindle
VAR(spindle_type, st, u8, 0, 1, 1, "DISABLED=0, PWM=1 or HUANYANG=2")
+VAR(speed, s, f32, 0, 1, 1, "Current spindle speed")
VAR(spin_reversed, sr, b8, 0, 1, 1, "Reverse spin")
VAR(max_spin, sx, f32, 0, 1, 1, "Maximum spindle speed")
VAR(min_spin, sm, f32, 0, 1, 1, "Minimum spindle speed")
+
+// PWM spindle
+VAR(pwm_invert, pi, b8, 0, 1, 1, "Inverted spindle PWM")
VAR(pwm_min_duty, nd, f32, 0, 1, 1, "Minimum PWM duty cycle")
VAR(pwm_max_duty, md, f32, 0, 1, 1, "Maximum PWM duty cycle")
VAR(pwm_duty, pd, f32, 0, 0, 1, "Current PWM duty cycle")
VAR(pwm_freq, sf, u16, 0, 1, 1, "Spindle PWM frequency in Hz")
-// PWM spindle
-VAR(pwm_invert, pi, b8, 0, 1, 1, "Inverted spindle PWM")
-
// Modbus spindle
VAR(modbus_debug, hb, b8, 0, 1, 1, "Modbus debugging")
+VAR(modbus_id, hi, u8, 0, 1, 1, "Modbus ID")
VAR(modbus_baud, mb, u8, 0, 1, 1, "Modbus BAUD rate")
+VAR(modbus_parity, ma, u8, 0, 1, 1, "Modbus parity")
+VAR(modbus_stop, ms, u8, 0, 1, 1, "Modbus stop bits")
VAR(modbus_connected, he, b8, 0, 0, 1, "Modbus connected")
// Huanyang spindle
-VAR(hy_id, hi, u8, 0, 1, 1, "Modbus ID")
-VAR(hy_freq, hz, f32, 0, 0, 1, "Modbus actual freq")
-VAR(hy_current, hc, f32, 0, 0, 1, "Modbus actual current")
-VAR(hy_rpm, hr, u16, 0, 0, 1, "Modbus actual RPM")
-VAR(hy_temp, ht, u16, 0, 0, 1, "Modbus temperature")
-VAR(hy_max_freq, hx, f32, 0, 0, 1, "Modbus max freq")
-VAR(hy_min_freq, hm, f32, 0, 0, 1, "Modbus min freq")
-VAR(hy_rated_rpm, hq, u16, 0, 0, 1, "Modbus rated RPM")
-VAR(hy_status, hs, u8, 0, 0, 1, "Modbus status flags")
+VAR(hy_freq, hz, f32, 0, 0, 1, "Huanyang actual freq")
+VAR(hy_current, hc, f32, 0, 0, 1, "Huanyang actual current")
+VAR(hy_temp, ht, u16, 0, 0, 1, "Huanyang temperature")
+VAR(hy_max_freq, hx, f32, 0, 0, 1, "Huanyang max freq")
+VAR(hy_min_freq, hm, f32, 0, 0, 1, "Huanyang min freq")
+VAR(hy_rated_rpm, hq, u16, 0, 0, 1, "Huanyang rated RPM")
+VAR(hy_status, hs, u8, 0, 0, 1, "Huanyang status flags")
// Machine state
VAR(id, id, u32, 0, 1, 1, "Last executed command ID")
-VAR(speed, s, f32, 0, 1, 1, "Current spindle speed")
VAR(feed_override, fo, f32, 0, 1, 1, "Feed rate override")
VAR(speed_override, so, f32, 0, 1, 1, "Spindle speed override")
VAR(optional_pause, op, b8, 0, 1, 1, "Optional pause state")
--- /dev/null
+/******************************************************************************\
+
+ This file is part of the Buildbotics firmware.
+
+ Copyright (c) 2015 - 2018, Buildbotics LLC
+ All rights reserved.
+
+ This file ("the software") is free software: you can redistribute it
+ and/or modify it under the terms of the GNU General Public License,
+ version 2 as published by the Free Software Foundation. You should
+ have received a copy of the GNU General Public License, version 2
+ along with the software. If not, see <http://www.gnu.org/licenses/>.
+
+ The software is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the software. If not, see
+ <http://www.gnu.org/licenses/>.
+
+ For information regarding this software email:
+ "Joseph Coffland" <joseph@buildbotics.com>
+
+\******************************************************************************/
+
+#include "huanyang.h"
+#include "config.h"
+#include "modbus.h"
+#include "report.h"
+
+#include <string.h>
+#include <math.h>
+
+
+/*
+ Huanyang is not quite Modbus compliant.
+
+ Message format is:
+
+ [id][func][length][data][checksum]
+
+ Where:
+
+ id - 1-byte Peer ID
+ func - 1-byte One of hy_func_t
+ length - 1-byte Length data in bytes
+ data - length bytes - Command arguments
+ checksum - 16-bit CRC: x^16 + x^15 + x^2 + 1 (0xa001) initial: 0xffff
+*/
+
+
+// See VFD manual pg56 3.1.3
+typedef enum {
+ HUANYANG_FUNC_READ = 1, // [len=1][hy_addr_t]
+ HUANYANG_FUNC_WRITE, // [len=3][hy_addr_t][data]
+ HUANYANG_CTRL_WRITE, // [len=1][hy_ctrl_state_t]
+ HUANYANG_CTRL_READ, // [len=1][hy_ctrl_addr_t]
+ HUANYANG_FREQ_WRITE, // [len=2][freq]
+ HUANYANG_RESERVED_1,
+ HUANYANG_RESERVED_2,
+ HUANYANG_LOOP_TEST,
+} hy_func_t;
+
+
+// Sent in HUANYANG_CTRL_WRITE
+// See VFD manual pg57 3.1.3.c
+typedef enum {
+ HUANYANG_RUN = 1 << 0,
+ HUANYANG_FORWARD = 1 << 1,
+ HUANYANG_REVERSE = 1 << 2,
+ HUANYANG_STOP = 1 << 3,
+ HUANYANG_REV_FWD = 1 << 4,
+ HUANYANG_JOG = 1 << 5,
+ HUANYANG_JOG_FORWARD = 1 << 6,
+ HUANYANG_JOG_REVERSE = 1 << 7,
+} hy_ctrl_state_t;
+
+
+// Returned by HUANYANG_CTRL_WRITE
+// See VFD manual pg57 3.1.3.c
+typedef enum {
+ HUANYANG_STATUS_RUN = 1 << 0,
+ HUANYANG_STATUS_JOG = 1 << 1,
+ HUANYANG_STATUS_COMMAND_REV = 1 << 2,
+ HUANYANG_STATUS_RUNNING = 1 << 3,
+ HUANYANG_STATUS_JOGGING = 1 << 4,
+ HUANYANG_STATUS_JOGGING_REV = 1 << 5,
+ HUANYANG_STATUS_BRAKING = 1 << 6,
+ HUANYANG_STATUS_TRACK_START = 1 << 7,
+} hy_ctrl_status_t;
+
+
+// Sent in HUANYANG_CTRL_READ
+// See VFD manual pg57 3.1.3.d
+typedef enum {
+ HUANYANG_TARGET_FREQ,
+ HUANYANG_ACTUAL_FREQ,
+ HUANYANG_ACTUAL_CURRENT,
+ HUANYANG_ACTUAL_RPM,
+ HUANYANG_DCV,
+ HUANYANG_ACV,
+ HUANYANG_COUNTER,
+ HUANYANG_TEMPERATURE,
+} hy_ctrl_addr_t;
+
+
+static struct {
+ uint8_t state;
+ hy_func_t func;
+ uint8_t data[4];
+ uint8_t bytes;
+ uint8_t response;
+
+ bool shutdown;
+ bool changed;
+ float speed;
+
+ float actual_freq;
+ float actual_current;
+ uint16_t actual_rpm;
+ uint16_t temperature;
+
+ float max_freq;
+ float min_freq;
+ uint16_t rated_rpm;
+
+ uint8_t status;
+} hy = {0};
+
+
+static void _func_read(hy_addr_t addr) {
+ hy.func = HUANYANG_FUNC_READ;
+ hy.bytes = 2;
+ hy.response = 4;
+ hy.data[0] = 1;
+ hy.data[1] = addr;
+}
+
+
+static void _ctrl_write(hy_ctrl_state_t state) {
+ hy.func = HUANYANG_CTRL_WRITE;
+ hy.bytes = 2;
+ hy.response = 2;
+ hy.data[0] = 1;
+ hy.data[1] = state;
+}
+
+
+static void _ctrl_read(hy_ctrl_addr_t addr) {
+ hy.func = HUANYANG_CTRL_READ;
+ hy.bytes = 2;
+ hy.response = 4;
+ hy.data[0] = 1;
+ hy.data[1] = addr;
+}
+
+
+static void _freq_write(uint16_t freq) {
+ hy.func = HUANYANG_FREQ_WRITE;
+ hy.bytes = 3;
+ hy.response = 3;
+ hy.data[0] = 2;
+ hy.data[1] = freq >> 8;
+ hy.data[2] = freq;
+}
+
+
+static void _func_read_response(hy_addr_t addr, uint16_t value) {
+ switch (addr) {
+ case HY_PD005_MAX_FREQUENCY: hy.max_freq = value * 0.01; break;
+ case HY_PD011_FREQUENCY_LOWER_LIMIT: hy.min_freq = value * 0.01; break;
+ case HY_PD144_RATED_MOTOR_RPM: hy.rated_rpm = value; break;
+ default: break;
+ }
+}
+
+
+static void _ctrl_read_response(hy_ctrl_addr_t addr, uint16_t value) {
+ switch (addr) {
+ case HUANYANG_ACTUAL_FREQ: hy.actual_freq = value * 0.01; break;
+ case HUANYANG_ACTUAL_CURRENT: hy.actual_current = value * 0.01; break;
+ case HUANYANG_ACTUAL_RPM: hy.actual_rpm = value; break;
+ case HUANYANG_TEMPERATURE: hy.temperature = value; break;
+ default: break;
+ }
+}
+
+
+static uint16_t _read_word(const uint8_t *data) {
+ return (uint16_t)data[0] << 8 | data[1];
+}
+
+
+static void _handle_response(hy_func_t func, const uint8_t *data) {
+ switch (func) {
+ case HUANYANG_FUNC_READ:
+ _func_read_response((hy_addr_t)*data, _read_word(data + 1));
+ break;
+
+ case HUANYANG_FUNC_WRITE: break;
+ case HUANYANG_CTRL_WRITE: hy.status = *data; break;
+
+ case HUANYANG_CTRL_READ:
+ _ctrl_read_response((hy_ctrl_addr_t)*data, _read_word(data + 1));
+ break;
+
+ case HUANYANG_FREQ_WRITE: break;
+ default: break;
+ }
+}
+
+
+static void _next_command();
+
+
+static void _reset(bool halt) {
+ // Save settings
+ float speed = hy.speed;
+
+ // Clear state
+ memset(&hy, 0, sizeof(hy));
+
+ // Restore settings
+ hy.speed = speed;
+ hy.changed = true;
+
+ if (!halt) _next_command();
+}
+
+
+static void _modbus_cb(uint8_t func, uint8_t bytes, const uint8_t *data) {
+ if (!data) _reset(true);
+
+ else if (bytes == *data + 1) {
+ _handle_response((hy_func_t)func, data + 1);
+
+ if (func == HUANYANG_CTRL_WRITE && hy.shutdown) {
+ _reset(true);
+ modbus_deinit();
+ return;
+ }
+
+ // Next command
+ if (++hy.state == 9) {
+ hy.state = 5;
+ report_request();
+ }
+
+ // Update freq
+ if (hy.shutdown || (hy.changed && 4 < hy.state)) hy.state = 0;
+ }
+
+ _next_command();
+}
+
+
+static void _next_command() {
+ switch (hy.state) {
+ case 0: { // Update direction
+ hy_ctrl_state_t state = HUANYANG_STOP;
+ if (!hy.shutdown) {
+ if (0 < hy.speed)
+ state = (hy_ctrl_state_t)(HUANYANG_RUN | HUANYANG_FORWARD);
+ else if (hy.speed < 0)
+ state = (hy_ctrl_state_t)(HUANYANG_RUN | HUANYANG_REV_FWD);
+ }
+
+ _ctrl_write(state);
+ break;
+ }
+
+ case 1: _func_read(HY_PD005_MAX_FREQUENCY); break;
+ case 2: _func_read(HY_PD011_FREQUENCY_LOWER_LIMIT); break;
+ case 3: _func_read(HY_PD144_RATED_MOTOR_RPM); break;
+
+ case 4: { // Update freqency
+ // Compute frequency in Hz
+ float freq = fabs(hy.speed * hy.max_freq);
+
+ // Frequency write command
+ _freq_write(freq * 100);
+ hy.changed = false;
+ break;
+ }
+
+ case 5: _ctrl_read(HUANYANG_ACTUAL_FREQ); break;
+ case 6: _ctrl_read(HUANYANG_ACTUAL_CURRENT); break;
+ case 7: _ctrl_read(HUANYANG_ACTUAL_RPM); break;
+ case 8: _ctrl_read(HUANYANG_TEMPERATURE); break;
+ }
+
+ // Send command
+ modbus_func(hy.func, hy.bytes, hy.data, hy.response, _modbus_cb);
+}
+
+
+void huanyang_init() {
+ modbus_init();
+ _reset(false);
+}
+
+
+void huanyang_deinit() {hy.shutdown = true;}
+
+
+void huanyang_set(float speed) {
+ if (hy.speed != speed) {
+ hy.speed = speed;
+ hy.changed = true;
+ }
+}
+
+
+float huanyang_get() {return hy.actual_freq / hy.max_freq;}
+
+
+void huanyang_stop() {
+ huanyang_set(0);
+ _reset(false);
+}
+
+
+float get_hy_freq() {return hy.actual_freq;}
+float get_hy_current() {return hy.actual_current;}
+uint16_t get_hy_temp() {return hy.temperature;}
+float get_hy_max_freq() {return hy.max_freq;}
+float get_hy_min_freq() {return hy.min_freq;}
+uint16_t get_hy_rated_rpm() {return hy.rated_rpm;}
+float get_hy_status() {return hy.status;}
--- /dev/null
+/******************************************************************************\
+
+ This file is part of the Buildbotics firmware.
+
+ Copyright (c) 2015 - 2018, Buildbotics LLC
+ All rights reserved.
+
+ This file ("the software") is free software: you can redistribute it
+ and/or modify it under the terms of the GNU General Public License,
+ version 2 as published by the Free Software Foundation. You should
+ have received a copy of the GNU General Public License, version 2
+ along with the software. If not, see <http://www.gnu.org/licenses/>.
+
+ The software is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the software. If not, see
+ <http://www.gnu.org/licenses/>.
+
+ For information regarding this software email:
+ "Joseph Coffland" <joseph@buildbotics.com>
+
+\******************************************************************************/
+
+#pragma once
+
+
+void huanyang_init();
+void huanyang_deinit();
+void huanyang_set(float speed);
+float huanyang_get();
+void huanyang_stop();
+
+
+/// See Huanyang VFD user manual
+typedef enum {
+ HY_PD000_PARAMETER_LOCK,
+ HY_PD001_SOURCE_OF_OPERATION_COMMANDS,
+ HY_PD002_SOURCE_OF_OPERATING_FREQUENCY,
+ HY_PD003_MAIN_FREQUENCY,
+ HY_PD004_BASE_FREQUENCY,
+ HY_PD005_MAX_FREQUENCY,
+ HY_PD006_INTERMEDIATE_FREQUENCY,
+ HY_PD007_MIN_FREQUENCY,
+ HY_PD008_MAX_VOLTAGE,
+ HY_PD009_INTERMEDIATE_VOLTAGE,
+ HY_PD010_MIN_VOLTAGE,
+ HY_PD011_FREQUENCY_LOWER_LIMIT,
+ HY_PD012_RESERVED,
+ HY_PD013_PARAMETER_RESET,
+ HY_PD014_ACCEL_TIME_1,
+ HY_PD015_DECEL_TIME_1,
+ HY_PD016_ACCEL_TIME_2,
+ HY_PD017_DECEL_TIME_2,
+ HY_PD018_ACCEL_TIME_3,
+ HY_PD019_DECEL_TIME_3,
+ HY_PD020_ACCEL_TIME_4,
+ HY_PD021_DECEL_TIME_4,
+ HY_PD022_FACTORY_RESERVED,
+ HY_PD023_REV_ROTATION_SELECT,
+ HY_PD024_STOP_KEY,
+ HY_PD025_STARTING_MODE,
+ HY_PD026_STOPPING_MODE,
+ HY_PD027_STARTING_FREQUENCY,
+ HY_PD028_STOPPING_FREQUENCY,
+ HY_PD029_DC_BRAKING_TIME_AT_START,
+ HY_PD030_DC_BRAKING_TIME_AT_STOP,
+ HY_PD031_DC_BRAKING_VOLTAGE_LEVEL,
+ HY_PD032_FREQUENCY_TRACK_TIME,
+ HY_PD033_CURRENT_LEVEL_FOR_FREQUENCY_TRACK,
+ HY_PD034_VOLTAGE_RISE_TIME_FOR_FREQUENCY_TRACK,
+ HY_PD035_FREQUENCY_STEP_LENGTH,
+ HY_PD036,
+ HY_PD037,
+ HY_PD038,
+ HY_PD039,
+ HY_PD040,
+ HY_PD041_CARRIER_FREQUENCY,
+ HY_PD042_JOGGING_FREQUENCY,
+ HY_PD043_S_CURVE_TIME,
+ HY_PD044_MULTI_INPUT_FOR,
+ HY_PD045_MULTI_INPUT_REV,
+ HY_PD046_MULTI_INPUT_RST,
+ HY_PD047_MULTI_INPUT_SPH,
+ HY_PD048_MULTI_INPUT_SPM,
+ HY_PD049_MULTI_INPUT_SPL,
+ HY_PD050_MULTI_OUTPUT_DRV,
+ HY_PD051_MULTI_OUTPUT_UPF,
+ HY_PD052_MULTI_OUTPUT_FA_FB_FC,
+ HY_PD053_MULTI_OUTPUT_KA_KB,
+ HY_PD054_MULTI_OUTPUT_AM,
+ HY_PD055_AM_ANALOG_OUTPUT_GAIN,
+ HY_PD056_SKIP_FREQUENCY_1,
+ HY_PD057_SKIP_FREQUENCY_2,
+ HY_PD058_SKIP_FREQUENCY_3,
+ HY_PD059_SKIP_FREQUENCY_RANGE,
+ HY_PD060_UNIFORM_FREQUENCY_1,
+ HY_PD061_UNIFORM_FREQUENCY_2,
+ HY_PD062_UNIFORM_FREQUENCY_RANGE,
+ HY_PD063_TIMER_1_TIME,
+ HY_PD064_TIMER_2_TIME,
+ HY_PD065_COUNTING_VALUE,
+ HY_PD066_INTERMEDIATE_COUNTER,
+ HY_PD067,
+ HY_PD068,
+ HY_PD069,
+ HY_PD070_ANALOG_INPUT,
+ HY_PD071_ANALOG_FILTERING_CONSTANT,
+ HY_PD072_HIGHER_ANALOG_FREQUENCY,
+ HY_PD073_LOWER_ANALOG_FREQUENCY,
+ HY_PD074_BIAS_DIRECTION_AT_HIGHER_FREQUENCY,
+ HY_PD075_BIAS_DIRECTION_AT_LOWER_FREQUENCY,
+ HY_PD076_ANALOG_NEGATIVE_BIAS_REVERSE,
+ HY_PD077_UP_DOWN_FUNCTION,
+ HY_PD078_UP_DOWN_SPEED,
+ HY_PD079,
+ HY_PD080_PLC_OPERATION,
+ HY_PD081_AUTO_PLC,
+ HY_PD082_PLC_RUNNING_DIRECTION,
+ HY_PD083,
+ HY_PD084_PLC_RAMP_TIME,
+ HY_PD085,
+ HY_PD086_FREQUENCY_2,
+ HY_PD087_FREQUENCY_3,
+ HY_PD088_FREQUENCY_4,
+ HY_PD089_FREQUENCY_5,
+ HY_PD090_FREQUENCY_6,
+ HY_PD091_FREQUENCY_7,
+ HY_PD092_FREQUENCY_8,
+ HY_PD093,
+ HY_PD094,
+ HY_PD095,
+ HY_PD096,
+ HY_PD097,
+ HY_PD098,
+ HY_PD099,
+ HY_PD100,
+ HY_PD101_TIMER_1,
+ HY_PD102_TIMER_2,
+ HY_PD103_TIMER_3,
+ HY_PD104_TIMER_4,
+ HY_PD105_TIMER_5,
+ HY_PD106_TIMER_6,
+ HY_PD107_TIMER_7,
+ HY_PD108_TIMER_8,
+ HY_PD109,
+ HY_PD110,
+ HY_PD111,
+ HY_PD112,
+ HY_PD113,
+ HY_PD114,
+ HY_PD115,
+ HY_PD116,
+ HY_PD117_AUTOPLC_MEMORY_FUNCTION,
+ HY_PD118_OVER_VOLTAGE_STALL_PREVENTION,
+ HY_PD119_STALL_PREVENTION_LEVEL_AT_RAMP_UP,
+ HY_PD120_STALL_PREVENTION_LEVEL_AT_CONSTANT_SPEED,
+ HY_PD121_DECEL_TIME_FOR_STALL_PREVENTION_AT_CONSTANT_SPEED,
+ HY_PD122_STALL_PREVENTION_LEVEL_AT_DECELERATION,
+ HY_PD123_OVER_TORQUE_DETECT_MODE,
+ HY_PD124_OVER_TORQUE_DETECT_LEVEL,
+ HY_PD125_OVER_TORQUE_DETECT_TIME,
+ HY_PD126,
+ HY_PD127,
+ HY_PD128,
+ HY_PD129,
+ HY_PD130_NUMBER_OF_AUXILIARY_PUMP,
+ HY_PD131_CONTINUOUS_RUNNING_TIME_OF_AUXILIARY_PUMPS,
+ HY_PD132_INTERLOCKING_TIME_OF_AUXILIARY_PUMP,
+ HY_PD133_HIGH_SPEED_RUNNING_TIME,
+ HY_PD134_LOW_SPEED_RUNNING_TIME,
+ HY_PD135_STOPPING_VOLTAGE_LEVEL,
+ HY_PD136_LASTING_TIME_OF_STOPPING_VOLTAGE_LEVEL,
+ HY_PD137_WAKEUP_VOLTAGE_LEVEL,
+ HY_PD138_SLEEP_FREQUENCY,
+ HY_PD139_LASTING_TIME_OF_SLEEP_FREQUENCY,
+ HY_PD140,
+ HY_PD141_RATED_MOTOR_VOLTAGE,
+ HY_PD142_RATED_MOTOR_CURRENT,
+ HY_PD143_MOTOR_POLE_NUMBER,
+ HY_PD144_RATED_MOTOR_RPM,
+ HY_PD145_AUTO_TORQUE_COMPENSATION,
+ HY_PD146_MOTOR_NO_LOAD_CURRENT,
+ HY_PD147_MOTOR_SLIP_COMPENSATION,
+ HY_PD148,
+ HY_PD149,
+ HY_PD150_AUTO_VOLTAGE_REGULATION,
+ HY_PD151_AUTO_ENERGY_SAVING,
+ HY_PD152_FAULT_RESTART_TIME,
+ HY_PD153_RESTART_AFTER_INSTANTANEOUS_STOP,
+ HY_PD154_ALLOWABLE_POWER_BREAKDOWN_TIME,
+ HY_PD155_NUMBER_OF_ABNORMAL_RESTART,
+ HY_PD156_PROPORTIONAL_CONSTANT,
+ HY_PD157_INTEGRAL_TIME,
+ HY_PD158_DIFFERENTIAL_TIME,
+ HY_PD159_TARGET_VALUE,
+ HY_PD160_PID_TARGET_VALUE,
+ HY_PD161_PID_UPPER_LIMIT,
+ HY_PD162_PID_LOWER_LIMIT,
+ HY_PD163_COMMUNICATION_ADDRESSES,
+ HY_PD164_COMMUNICATION_BAUD_RATE,
+ HY_PD165_COMMUNICATION_DATA_METHOD,
+ HY_PD166,
+ HY_PD167,
+ HY_PD168,
+ HY_PD169,
+ HY_PD170_DISPLAY_ITEMS,
+ HY_PD171_DISPLAY_ITEMS_OPEN,
+ HY_PD172_FAULT_CLEAR,
+ HY_PD173,
+ HY_PD174_RATED_CURRENT_OF_INVERTER,
+ HY_PD175_INVERTER_MODEL,
+ HY_PD176_INVERTER_FREQUENCY_STANDARD,
+ HY_PD177_FAULT_RECORD_1,
+ HY_PD178_FAULT_RECORD_2,
+ HY_PD179_FAULT_RECORD_3,
+ HY_PD180_FAULT_RECORD_4,
+ HY_PD181_SOFTWARE_VERSION,
+ HY_PD182_MANUFACTURE_DATE,
+ HY_PD183_SERIAL_NO,
+} hy_addr_t;
--- /dev/null
+/******************************************************************************\
+
+ This file is part of the Buildbotics firmware.
+
+ Copyright (c) 2015 - 2018, Buildbotics LLC
+ All rights reserved.
+
+ This file ("the software") is free software: you can redistribute it
+ and/or modify it under the terms of the GNU General Public License,
+ version 2 as published by the Free Software Foundation. You should
+ have received a copy of the GNU General Public License, version 2
+ along with the software. If not, see <http://www.gnu.org/licenses/>.
+
+ The software is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the software. If not, see
+ <http://www.gnu.org/licenses/>.
+
+ For information regarding this software email:
+ "Joseph Coffland" <joseph@buildbotics.com>
+
+\******************************************************************************/
+
+
+#include "modbus.h"
+
+#include <math.h>
+
+
+#define P(H, L) ((H) << 8 | (L))
+
+
+enum {
+ P00_00_MAIN_FREQ = P(0, 0),
+ P00_01_START_STOP_SOURCE = P(0, 1),
+ P00_04_HIGEST_OUTPUT_FREQ = P(0, 4),
+ P01_00_DIRECTION = P(1, 0),
+ P07_00_FREQ_1 = P(7, 0),
+ P07_08_FREQ_SOURCE_1 = P(7, 8),
+};
+
+
+static struct {
+ uint8_t state;
+ bool changed;
+ bool shutdown;
+
+ float speed;
+ uint16_t max_freq;
+ float actual_speed;
+} s = {0};
+
+
+static void _next_command();
+
+
+static uint16_t _speed2freq(float speed) {return fabs(speed) * s.max_freq;}
+static float _freq2speed(uint16_t freq) {return (float)freq / s.max_freq;}
+
+
+static void _next_state(bool ok) {
+ if (!ok) s.state = 0;
+
+ else if (s.changed) {
+ s.changed = false;
+ s.state = 0;
+
+ } else if (++s.state == 5) s.state = 4;
+
+ _next_command();
+}
+
+
+static void _read_cb(bool ok, uint16_t addr, uint16_t value) {
+ if (ok)
+ switch (addr) {
+ case P00_04_HIGEST_OUTPUT_FREQ: s.max_freq = value; break;
+ case P07_00_FREQ_1: s.actual_speed = _freq2speed(value); break;
+ }
+
+ _next_state(ok);
+}
+
+
+static void _write_cb(bool ok, uint16_t addr) {_next_state(ok);}
+
+
+static void _next_command() {
+ if (s.shutdown) {
+ modbus_deinit();
+ return;
+ }
+
+ // TODO spin direction
+ switch (s.state) {
+ case 0: modbus_read(P00_04_HIGEST_OUTPUT_FREQ, _read_cb); break;
+ case 1: modbus_write(P00_01_START_STOP_SOURCE, 1, _write_cb); break;
+ case 2: modbus_write(P07_08_FREQ_SOURCE_1, 1, _write_cb); break;
+ case 3: modbus_write(P07_00_FREQ_1, _speed2freq(s.speed), _write_cb); break;
+ case 4: modbus_read(P07_00_FREQ_1, _read_cb); break;
+ }
+}
+
+
+void yl600_init() {
+ modbus_init();
+ s.shutdown = false;
+ _next_command();
+}
+
+
+void yl600_deinit() {s.shutdown = true;}
+
+
+void yl600_set(float speed) {
+ if (s.speed != speed) {
+ s.speed = speed;
+ s.changed = true;
+ }
+}
+
+
+float yl600_get() {return s.actual_speed;}
+void yl600_stop() {yl600_set(0);}
th Current
tr
- td {{state['hr']}} RPM
+ td {{state['s']}} RPM
th Speed
th.separator
- td {{state['ht']}} ℃
- th Temp
+ td
+ th
table.legend
tr
templated-input(v-for="templ in template['pwm-spindle']",
:name="$key", :model.sync="pwmSpindle[$key]", :template="templ")
- div(v-if="get_type() == 'HUANYANG'")
- h2 Huanyang VFD
- form.pure-form.pure-form-aligned
- fieldset
- templated-input(v-for="templ in template['huanyang-spindle']",
- :name="$key", :model.sync="huanyangSpindle[$key]",
- :template="templ")
-
- div(v-if="get_type() == 'MODBUS'")
- h2 Modbus Compatiable VFD
+ div(v-if="get_type() != 'DISABLED' && get_type() != 'PWM'")
+ h2 Modbus Configuration
form.pure-form.pure-form-aligned
fieldset
templated-input(v-for="templ in template['modbus-spindle']",
return {
tool: {},
pwmSpindle: {},
- huanyangSpindle: {},
modbusSpindle: {}
}
},
this.$set('tool["' + key + '"]', template[key].default);
this.update_tool('pwm');
- this.update_tool('huanyang');
this.update_tool('modbus');
}.bind(this));
}
"tool": {
"spindle-type": {
"type": "enum",
- "values": ["Disabled", "PWM", "Huanyang", "Modbus"],
+ "values": ["Disabled", "PWM", "Huanyang", "YL600"],
"default": "Disabled",
"code": "st"
},
"default": "false",
"code": "sr"
},
+ "max-spin": {
+ "type": "float",
+ "unit": "RPM",
+ "min": 0,
+ "default": 10000,
+ "code": "sx"
+ },
+ "min-spin": {
+ "type": "float",
+ "unit": "RPM",
+ "min": 0,
+ "default": 0,
+ "code": "sm"
+ },
"tool-enable-mode": {
"type": "enum",
"values": ["disabled", "lo-hi", "hi-lo", "tri-lo", "tri-hi", "lo-tri",
}
},
- "huanyang-spindle": {
+ "modbus-spindle": {
"bus-id": {
"type": "int",
"default": "1",
"code": "hi"
- }
- },
-
- "modbus-spindle": {
- "modbus-id": {
- "type": "int",
+ },
+ "baud": {
+ "type": "enum",
+ "values": ["9600", "19200", "38400", "57600", "115200"],
+ "default": "9600",
+ "code": "mb"
+ },
+ "parity": {
+ "type": "enum",
+ "values": ["None", "Even", "Odd"],
+ "default": "None",
+ "code": "ma"
+ },
+ "stop-bits": {
+ "type": "enum",
+ "values": ["1", "2"],
"default": "1",
- "code": "bi"
+ "code": "ms"
}
},
"pwm-spindle": {
- "max-spin": {
- "type": "float",
- "unit": "RPM",
- "min": 0,
- "default": 10000,
- "code": "sx"
- },
- "min-spin": {
- "type": "float",
- "unit": "RPM",
- "min": 0,
- "default": 0,
- "code": "sm"
- },
"pwm-inverted": {
"type": "bool",
"default": "false",