diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000000..bed3c7cdbb
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,7 @@
+# VESC firmware uses tab indentation and is NOT clang-formatted upstream
+# (vedderb/bldc). DisableFormat makes clang-format leave files untouched, so
+# editors with "format on save" stop churning whitespace in shared files -
+# which otherwise produces huge noisy diffs and constant merge conflicts.
+#
+# Remove this file only if you deliberately want to reformat the whole tree.
+DisableFormat: true
diff --git a/applications/app_ppm_adc_brake.c b/applications/app_ppm_adc_brake.c
new file mode 100644
index 0000000000..72cb4a1025
--- /dev/null
+++ b/applications/app_ppm_adc_brake.c
@@ -0,0 +1,557 @@
+/*
+ Copyright 2024 - Custom app for VESC firmware
+
+ This file is part of the VESC firmware.
+
+ The VESC firmware 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.
+
+ The VESC firmware 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 .
+ */
+
+/*
+ * Custom control: PPM throttle + ADC2 brake + ADC1 gain + reverse button.
+ *
+ * A current-control scheme modelled on the ADC "Current Reverse, ADC2 Brake,
+ * Button" mode, but the throttle comes from the PPM/servo input instead of ADC1,
+ * and ADC1 is repurposed as a 0..1 gain knob.
+ *
+ * Inputs
+ * ------
+ * Throttle : PPM pulse on the ICU pin (HW_ICU_GPIO / HW_ICU_PIN), 0..1
+ * Gain : analog voltage on ADC1 (HW_ADC_EXT / ADC_IND_EXT), 0..1
+ * Brake : analog voltage on ADC2 (HW_ADC_EXT2 / ADC_IND_EXT2), 0..1
+ * Reverse : digital button on UART RX (HW_UART_RX_PORT / HW_UART_RX_PIN)
+ * Cruise : digital button on UART TX (HW_UART_TX_PORT / HW_UART_TX_PIN) (optional)
+ *
+ * Configuration (all from the normal VESC Tool pages, no new fields)
+ * ------------------------------------------------------------------
+ * Throttle deadband / curve / ramp : PPM page (appconf.app_ppm_conf)
+ * Gain voltage range / filter / inv: ADC page voltage_start/end, use_filter,
+ * voltage_inverted
+ * Brake voltage range / filter / inv: ADC page voltage2_start/end, use_filter,
+ * voltage2_inverted
+ * Brake ramp : ADC page ramp_time_pos / ramp_time_neg
+ * Safe-start mode : PPM page safe_start
+ *
+ * Signal pipeline (per loop, ~100 Hz)
+ * -----------------------------------
+ * throttle = map(PPM) -> 0..1
+ * throttle = deadband + throttle_curve(throttle) (PPM page; pre-gain)
+ * throttle_stick = throttle (saved for safe-start idle)
+ * gain = map(ADC1) -> 0..1 (utils_map + truncate caps it)
+ * brake = map(ADC2) -> 0..1
+ * throttle *= gain ; brake *= gain (ADC1 scales BOTH drive and brake)
+ *
+ * Curve and safe-start idle are computed on the PRE-gain stick, so the gain
+ * knob changes power but never reshapes the curve nor fakes "throttle idle".
+ *
+ * Arbitration & output (single signed command, one ramp tracker)
+ * --------------------------------------------------------------
+ * - BRAKE HAS PRIORITY: if brake > threshold, target = -brake and the PPM
+ * throttle is ignored entirely. Otherwise target = +throttle.
+ * - One ramp tracker on the overall signed output. The ramp time constant is
+ * taken from the *currently active* input (PPM page when driving, ADC page
+ * when braking); pos = |output| rising, neg = |output| falling. Ramp 0 =>
+ * instant. So full-throttle then stab-brake ramps +1 -> -1 on the brake
+ * ramp; releasing the brake ramps back toward throttle on the throttle ramp.
+ * - cmd < 0 => BRAKE path: mc_interface_set_brake_current_rel(|cmd|).
+ * Respects the proportional brake and BYPASSES safe-start and the
+ * PPM signal-loss timeout (braking is always allowed).
+ * - cmd >= 0 => DRIVE path: blocked by PPM signal-loss timeout and by
+ * safe-start; reverse button negates; optional cruise control;
+ * then mc_interface_set_current_rel(cmd).
+ *
+ * Safe-start (PPM throttle only)
+ * ------------------------------
+ * ms_without_power counts UP only while the throttle is idle, and is reset to 0
+ * on boot and on fault (unless SAFE_START_NO_FAULT). Driving does NOT reset it.
+ * Driving is blocked until the throttle has been idle for MIN_MS_WITHOUT_POWER,
+ * i.e. you must release the throttle after boot/fault before it will drive.
+ * Braking is never gated by safe-start.
+ */
+
+#include "app.h"
+#include "ch.h"
+#include "hal.h"
+
+#include "mc_interface.h"
+#include "utils_math.h"
+#include "utils_sys.h"
+#include "servo_dec.h"
+#include "comm_can.h"
+#include "hw.h"
+#include "commands.h"
+#include "terminal.h"
+#include "timeout.h"
+
+#include
+
+// Reverse / cruise button pins (separate from the PPM/ICU pin)
+#define REV_BUTTON_PORT HW_UART_RX_PORT
+#define REV_BUTTON_PIN HW_UART_RX_PIN
+#define CC_BUTTON_PORT HW_UART_TX_PORT
+#define CC_BUTTON_PIN HW_UART_TX_PIN
+
+// Buttons are active-low (pressed = pin pulled to GND). Set to 0 if active-high.
+#define BUTTONS_ACTIVE_LOW 1
+
+// Set to 1 to enable cruise-control (hold CC button with no throttle -> hold rpm)
+#define ENABLE_CRUISE_CONTROL 1
+
+// ADC1 gain knob. 1 = throttle/brake scaled by ADC1 (the intended behaviour).
+// Set to 0 to bypass it (gain forced to 1.0) for bench-testing PPM-only when
+// nothing is wired to ADC1 - otherwise a low/unwired ADC1 zeroes the throttle.
+#ifndef USE_ADC1_GAIN
+#define USE_ADC1_GAIN 1
+#endif
+
+// ADC2 brake. 1 = proportional brake from ADC2. Set to 0 when ADC2 is not wired
+// so a FLOATING brake pin can't trigger spurious braking (brake has priority and
+// would otherwise block the throttle and inject brake current at standstill).
+#ifndef USE_ADC2_BRAKE
+#define USE_ADC2_BRAKE 1
+#endif
+
+// The power-ratio (ADC1 gain) and the reverse direction are only re-sampled
+// while the motor is at STANDSTILL and the throttle is released, then held for
+// the whole ride so they can't change mid-drive. "Standstill" = |erpm| below the
+// PPM page's "Max ERPM for direction" (max_erpm_for_dir), or this if that is 0.
+#define LATCH_ERPM_FALLBACK 2000.0
+
+#define MIN_MS_WITHOUT_POWER 500.0
+#define FILTER_SAMPLES 5
+#define RPM_FILTER_SAMPLES 8
+#define MAX_CAN_AGE 0.1
+
+// Threads
+static THD_FUNCTION(ppm_adc_brake_thread, arg);
+static THD_WORKING_AREA(ppm_adc_brake_thread_wa, 2048);
+
+// Private variables
+static volatile bool stop_now = true;
+static volatile bool is_running = false;
+static volatile app_configuration appconf;
+static volatile float ms_without_power = 0.0;
+static volatile float input_filtered_brake = 0.0;
+static volatile float input_filtered_gain = 0.0;
+// Control state that must survive between loop iterations but be reset on each
+// app (re)start so a reconfigure never acts on a stale command.
+static volatile float output_ramp = 0.0; // ramped signed output (-brake..+drive)
+static volatile systime_t last_ramp_time = 0; // timestamp for the ramp dt
+static volatile bool cc_was_pid = false; // cruise-control latch
+static volatile float cc_pid_rpm = 0.0; // cruise-control held rpm
+static volatile bool ppm_rx = false; // set by servo_func on each PPM pulse
+static volatile float latched_gain = 1.0; // power ratio (ADC1) held during a ride
+static volatile bool latched_rev = false; // reverse direction held during a ride
+
+// Latest computed values, exposed via the "ppm_adc_dbg" terminal command.
+static volatile float dbg_ppm = 0.0; // raw PPM servo value (-1..1)
+static volatile float dbg_gain = 0.0; // ADC1 gain (0..1)
+static volatile float dbg_throttle = 0.0; // throttle after gain (0..1)
+static volatile float dbg_brake = 0.0; // brake after gain (0..1)
+static volatile float dbg_cmd = 0.0; // signed ramped output (-brake..+drive)
+static volatile bool dbg_armed = false; // safe-start armed (drive allowed by safe-start)
+static volatile bool dbg_ppm_ok = false; // valid PPM signal present this loop
+
+// Experiment-plot streaming (toggled via the "ppm_adc_plot" terminal command).
+static volatile bool plot_enabled = false;
+static volatile float plot_sample = 0.0; // plot x-axis (seconds)
+
+static void servo_func(void) {
+ // Called by servodec on each decoded PPM pulse (interrupt context).
+ ppm_rx = true;
+}
+
+// Terminal command: prints the LIVE values the control loop is actually using.
+// VESC Tool's ADC RT tab reads the dormant ADC-app telemetry (0 when a custom
+// app runs), so use this instead to see the real ADC pins. Type: ppm_adc_dbg
+static void terminal_dbg(int argc, const char **argv) {
+ (void)argc; (void)argv;
+ commands_printf("--- app_ppm_adc_brake ---");
+ commands_printf("PPM servo : %.3f", (double)dbg_ppm);
+ commands_printf("ADC1 gain pin : %.3f V (range %.2f..%.2f V)",
+ (double)ADC_VOLTS(ADC_IND_EXT),
+ (double)appconf.app_adc_conf.voltage_start,
+ (double)appconf.app_adc_conf.voltage_end);
+ commands_printf("ADC2 brake pin : %.3f V (range %.2f..%.2f V)",
+ (double)ADC_VOLTS(ADC_IND_EXT2),
+ (double)appconf.app_adc_conf.voltage2_start,
+ (double)appconf.app_adc_conf.voltage2_end);
+ commands_printf("gain=%.3f throttle=%.3f brake=%.3f cmd=%.3f",
+ (double)dbg_gain, (double)dbg_throttle,
+ (double)dbg_brake, (double)dbg_cmd);
+ commands_printf("PPM signal=%s safe-start armed=%s ms_idle=%.0f/%.0f",
+ dbg_ppm_ok ? "OK" : "LOST",
+ dbg_armed ? "YES" : "NO (blocked)",
+ (double)ms_without_power, (double)MIN_MS_WITHOUT_POWER);
+ commands_printf("latched reverse=%d latched gain=%.3f (sampled only at standstill+idle)",
+ latched_rev, (double)latched_gain);
+ bool can_drive = dbg_ppm_ok && dbg_armed && !app_is_output_disabled();
+ commands_printf("can drive now=%s output_disabled=%d fault=%d [GAIN=%d BRAKE=%d]",
+ can_drive ? "YES" : "NO",
+ app_is_output_disabled(), mc_interface_get_fault(),
+ USE_ADC1_GAIN, USE_ADC2_BRAKE);
+}
+
+// Terminal command: start/stop streaming live curves to VESC Tool's
+// Realtime Data -> Experiment plot. "ppm_adc_plot 1" on, "ppm_adc_plot 0" off,
+// no arg = toggle. Self-contained: no shared-code (app_adc/commands) changes.
+static void terminal_plot(int argc, const char **argv) {
+ bool en = !plot_enabled; // toggle by default
+ if (argc == 2) {
+ en = (argv[1][0] != '0'); // "0" -> off, anything else -> on
+ }
+
+ if (en && !plot_enabled) {
+ commands_init_plot("Time (s)", "Value");
+ commands_plot_add_graph("ADC1 V"); // graph 0
+ commands_plot_add_graph("ADC2 V"); // graph 1
+ commands_plot_add_graph("throttle"); // graph 2
+ commands_plot_add_graph("cmd"); // graph 3 (+drive / -brake)
+ commands_plot_add_graph("armed"); // graph 4 (1=safe-start armed)
+ plot_sample = 0.0;
+ }
+
+ plot_enabled = en;
+ commands_printf(en ?
+ "ppm_adc plot: ON (open Realtime Data -> Experiment)" :
+ "ppm_adc plot: OFF");
+}
+
+static inline bool read_button(ioportid_t port, int pin) {
+ bool b = palReadPad(port, pin);
+#if BUTTONS_ACTIVE_LOW
+ b = !b;
+#endif
+ return b;
+}
+
+void app_custom_start(void) {
+ // ADC1 (gain) and ADC2 (brake) pins -> analog input
+ palSetPadMode(HW_ADC_EXT_GPIO, HW_ADC_EXT_PIN, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(HW_ADC_EXT2_GPIO, HW_ADC_EXT2_PIN, PAL_MODE_INPUT_ANALOG);
+
+ // Button pins -> digital input (pull-up because buttons are active-low)
+ palSetPadMode(REV_BUTTON_PORT, REV_BUTTON_PIN, PAL_MODE_INPUT_PULLUP);
+ palSetPadMode(CC_BUTTON_PORT, CC_BUTTON_PIN, PAL_MODE_INPUT_PULLUP);
+
+ // Start PPM/servo decoding on the ICU pin
+ servodec_init(servo_func);
+
+ terminal_register_command_callback(
+ "ppm_adc_dbg",
+ "Print live PPM/ADC1/ADC2/gain/brake/throttle for app_ppm_adc_brake",
+ 0,
+ terminal_dbg);
+ terminal_register_command_callback(
+ "ppm_adc_plot",
+ "Stream ADC1/ADC2/gain/throttle/brake to the Experiment plot (1=on 0=off, toggle if none)",
+ "[0/1]",
+ terminal_plot);
+
+ stop_now = false;
+ // Reset all persistent control state so a (re)start never acts on stale values.
+ ms_without_power = 0.0;
+ output_ramp = 0.0;
+ last_ramp_time = chVTGetSystemTimeX();
+ cc_was_pid = false;
+ input_filtered_brake = 0.0;
+ input_filtered_gain = 0.0;
+ plot_enabled = false;
+ ppm_rx = false;
+ latched_gain = 1.0;
+ latched_rev = false;
+ // Set is_running here (not just in the thread) so a configure() call that
+ // arrives right after start() applies the servo pulse options immediately.
+ is_running = true;
+ chThdCreateStatic(ppm_adc_brake_thread_wa, sizeof(ppm_adc_brake_thread_wa),
+ NORMALPRIO, ppm_adc_brake_thread, NULL);
+}
+
+void app_custom_stop(void) {
+ servodec_stop();
+ terminal_unregister_callback(terminal_dbg);
+ terminal_unregister_callback(terminal_plot);
+
+ stop_now = true;
+ while (is_running) {
+ chThdSleepMilliseconds(1);
+ }
+}
+
+void app_custom_configure(app_configuration *conf) {
+ appconf = *conf;
+
+ // Apply PPM pulse range to the servo decoder. Guard with is_running because
+ // app.c calls app_custom_configure() on every config change regardless of
+ // which app is active, and servodec must not be touched unless WE started it.
+ if (is_running) {
+ servodec_set_pulse_options(conf->app_ppm_conf.pulse_start,
+ conf->app_ppm_conf.pulse_end,
+ conf->app_ppm_conf.median_filter);
+ }
+
+ ms_without_power = 0.0;
+}
+
+static THD_FUNCTION(ppm_adc_brake_thread, arg) {
+ (void)arg;
+
+ chRegSetThreadName("App PPM+ADC Brake");
+ is_running = true;
+
+ const float sleep_time_s = 0.01; // 10 ms loop
+
+ for (;;) {
+ if (stop_now) {
+ is_running = false;
+ return;
+ }
+
+ chThdSleepMilliseconds(10);
+
+ const volatile ppm_config *pconf = &appconf.app_ppm_conf;
+ const volatile adc_config *aconf = &appconf.app_adc_conf;
+
+ // Feed the global timeout on every received PPM pulse (like the stock PPM
+ // app). Without this, if the global timeout ever latches during the boot/
+ // arming window our drive gate bails to brake WITHOUT resetting it, so it
+ // stays latched until an external timeout_reset() (VESC Tool COMM_ALIVE)
+ // -- the "only works after connecting VESC Tool" bug.
+ if (ppm_rx) {
+ ppm_rx = false;
+ timeout_reset();
+ }
+
+ // ---- 1) Throttle from PPM ----
+ // servodec maps pulse_start..pulse_end -> -1..1. Use the FULL travel as
+ // 0..1 throttle. (The old truncate(0,1) discarded the lower half, so the
+ // throttle only responded past mid-stick - "press deep".)
+ // With no valid PPM signal force 0 (servo_pos defaults to 0 -> would map
+ // to 0.5); this also lets safe-start arm during the no-signal boot window.
+ bool ppm_signal_ok = servodec_get_time_since_update() <= timeout_get_timeout_msec();
+ float throttle = ppm_signal_ok ? (servodec_get_servo(0) + 1.0) * 0.5 : 0.0;
+ utils_truncate_number(&throttle, 0.0, 1.0);
+
+ // Deadband + throttle curve on the RAW stick (PPM-page settings). Done
+ // before the ADC1 gain so the curve shape and the safe-start idle check
+ // are independent of the gain knob (gain=0 must not look like "idle").
+ utils_deadband(&throttle, pconf->hyst, 1.0);
+ throttle = utils_throttle_curve(throttle, pconf->throttle_exp,
+ pconf->throttle_exp_brake, pconf->throttle_exp_mode);
+ float throttle_stick = throttle; // pre-gain, for safe-start idle detection
+
+ // At rest = motor near standstill AND throttle released. The power ratio
+ // (ADC1) and reverse are only re-sampled while at rest, then latched for
+ // the whole ride so they can't change mid-drive.
+ float latch_erpm = pconf->max_erpm_for_dir > 1.0 ?
+ pconf->max_erpm_for_dir : LATCH_ERPM_FALLBACK;
+ bool at_rest = fabsf(mc_interface_get_rpm()) < latch_erpm &&
+ throttle_stick < 0.001;
+
+ // ---- 1b) ADC1 = 0..1 gain knob (ADC-page voltage_start/end mapping) ----
+ // utils_map() does NOT clamp on its own, so the truncate is the 0..1 cap.
+ // With USE_ADC1_GAIN=0 the gain is forced to 1.0 (bench-test PPM-only).
+ float gain = 1.0;
+#if USE_ADC1_GAIN
+ gain = ADC_VOLTS(ADC_IND_EXT);
+ UTILS_LP_MOVING_AVG_APPROX(input_filtered_gain, gain, FILTER_SAMPLES);
+ if (aconf->use_filter) {
+ gain = input_filtered_gain;
+ }
+ gain = utils_map(gain, aconf->voltage_start, aconf->voltage_end, 0.0, 1.0);
+ utils_truncate_number(&gain, 0.0, 1.0); // <-- the actual 0..1 cap
+ if (aconf->voltage_inverted) {
+ gain = 1.0 - gain;
+ }
+#endif
+ // Only sample the power ratio at standstill+idle; otherwise hold it.
+ if (at_rest) {
+ latched_gain = gain;
+ }
+ gain = latched_gain;
+
+ throttle *= gain; // master gain scales the (curved) throttle
+
+ // ---- 2) Brake from ADC2 (ADC-page voltage2_start/end), also gain-scaled ----
+ float brake = 0.0;
+#if USE_ADC2_BRAKE
+ brake = ADC_VOLTS(ADC_IND_EXT2);
+ UTILS_LP_MOVING_AVG_APPROX(input_filtered_brake, brake, FILTER_SAMPLES);
+ if (aconf->use_filter) {
+ brake = input_filtered_brake;
+ }
+ brake = utils_map(brake, aconf->voltage2_start, aconf->voltage2_end, 0.0, 1.0);
+ utils_truncate_number(&brake, 0.0, 1.0);
+ if (aconf->voltage2_inverted) {
+ brake = 1.0 - brake;
+ }
+
+ brake *= gain; // scale brake by the same ADC1 knob
+#endif
+
+ // ---- 3) Buttons ----
+ // Reverse is only re-sampled at standstill+idle, then latched for the ride.
+ if (at_rest) {
+ latched_rev = read_button(REV_BUTTON_PORT, REV_BUTTON_PIN);
+ }
+ bool rev_button = latched_rev;
+ bool cc_button = read_button(CC_BUTTON_PORT, CC_BUTTON_PIN);
+
+ // Brake has priority: when the ADC2 brake is pressed we respect it and
+ // ignore the PPM throttle entirely.
+ const float brake_thres = 0.001;
+ bool brake_active = brake > brake_thres;
+
+ // ---- 4) Single ramp on the overall (signed) output ----
+ // Target is negative for brake, positive for drive. The ramp time
+ // constant comes from whichever input is currently active: the PPM page
+ // for throttle, the ADC page for brake. pos = magnitude rising,
+ // neg = magnitude falling (same convention as the stock apps). So if you
+ // hold full throttle and stab the brake, the output ramps from +1 toward
+ // -1 using the *brake* ramp; release the brake and it ramps back toward
+ // the throttle using the *throttle* ramp. A ramp of 0 means instant.
+ float target = brake_active ? -brake : throttle;
+ float ramp_pos = brake_active ? aconf->ramp_time_pos : pconf->ramp_time_pos;
+ float ramp_neg = brake_active ? aconf->ramp_time_neg : pconf->ramp_time_neg;
+
+ const float dt_ms = (float)ST2MS(chVTTimeElapsedSinceX(last_ramp_time));
+ last_ramp_time = chVTGetSystemTimeX();
+
+ float ramp_time = fabsf(target) > fabsf(output_ramp) ? ramp_pos : ramp_neg;
+ if (ramp_time > 0.01) {
+ float step = output_ramp;
+ utils_step_towards(&step, target, dt_ms / (ramp_time * 1000.0));
+ output_ramp = step;
+ } else {
+ output_ramp = target;
+ }
+ float cmd = output_ramp;
+
+ // ---- Safe-start arming (PPM throttle only) ----
+ // Counts up ONLY while the throttle is idle; reset to 0 on boot/fault.
+ // Driving does NOT reset it (matches the stock apps), so once you've held
+ // idle long enough you stay armed for the session until a fault occurs.
+ if (mc_interface_get_fault() != FAULT_CODE_NONE &&
+ pconf->safe_start != SAFE_START_NO_FAULT) {
+ ms_without_power = 0.0;
+ }
+ // Idle = stick released (pre-gain), so the gain knob can't fake idle.
+ if (throttle_stick < 0.001) {
+ ms_without_power += 1000.0 * sleep_time_s;
+ }
+ dbg_ppm_ok = ppm_signal_ok;
+ dbg_armed = !(ms_without_power < MIN_MS_WITHOUT_POWER && pconf->safe_start);
+
+ // Expose live values for the ppm_adc_dbg terminal command
+ dbg_ppm = servodec_get_servo(0);
+ dbg_gain = gain;
+ dbg_throttle = throttle;
+ dbg_brake = brake;
+ dbg_cmd = cmd;
+
+ // Stream live curves to the VESC Tool Experiment plot when enabled.
+ // Throttled to ~20 Hz (loop is 100 Hz) to keep the comm link light.
+ // Graphs: ADC1 V, ADC2 V, throttle, cmd (+drive/-brake), armed (0/1).
+ if (plot_enabled) {
+ static int plot_div = 0;
+ if (++plot_div >= 5) {
+ plot_div = 0;
+ commands_plot_set_graph(0);
+ commands_send_plot_points(plot_sample, ADC_VOLTS(ADC_IND_EXT));
+ commands_plot_set_graph(1);
+ commands_send_plot_points(plot_sample, ADC_VOLTS(ADC_IND_EXT2));
+ commands_plot_set_graph(2);
+ commands_send_plot_points(plot_sample, throttle);
+ commands_plot_set_graph(3);
+ commands_send_plot_points(plot_sample, cmd);
+ commands_plot_set_graph(4);
+ commands_send_plot_points(plot_sample, dbg_armed ? 1.0 : 0.0);
+ plot_sample += 0.05;
+ }
+ }
+
+ // Output disabled by another part of the firmware (e.g. detection)
+ if (app_is_output_disabled()) {
+ continue;
+ }
+
+ // ---- 5) Brake path: respect the brake, bypass safe-start & PPM timeout ----
+ if (cmd < 0.0) {
+ float current_rel = fabsf(cmd);
+ timeout_reset();
+ mc_interface_set_brake_current_rel(current_rel);
+
+ if (pconf->multi_esc) {
+ for (int i = 0; i < CAN_STATUS_MSGS_TO_STORE; i++) {
+ can_status_msg *msg = comm_can_get_status_msg_index(i);
+ if (msg->id >= 0 && UTILS_AGE_S(msg->rx_time) < MAX_CAN_AGE) {
+ comm_can_set_current_brake_rel(msg->id, current_rel);
+ }
+ }
+ }
+ continue;
+ }
+
+ // ---- 6) Drive path (PPM throttle) ----
+ float current_rel = cmd;
+ if (rev_button) {
+ current_rel = -current_rel; // reverse: drive the other way
+ }
+
+ // PPM signal lost / timeout -> brake and bail (only blocks driving)
+ if (timeout_has_timeout() ||
+ servodec_get_time_since_update() > timeout_get_timeout_msec()) {
+ mc_interface_set_brake_current(timeout_get_brake_current());
+ continue;
+ }
+
+ // Safe-start: require the PPM throttle idle for a while after boot/fault
+ // before driving. Does not apply to braking (handled above).
+ if (ms_without_power < MIN_MS_WITHOUT_POWER && pconf->safe_start) {
+ mc_interface_set_brake_current(timeout_get_brake_current());
+ continue;
+ }
+
+ timeout_reset();
+
+#if ENABLE_CRUISE_CONTROL
+ // Cruise control: hold CC button with throttle released -> hold rpm
+ static float rpm_filtered = 0.0;
+ UTILS_LP_MOVING_AVG_APPROX(rpm_filtered, mc_interface_get_rpm(), RPM_FILTER_SAMPLES);
+
+ if (cc_button && fabsf(cmd) < 0.001) {
+ if (!cc_was_pid) {
+ cc_was_pid = true;
+ cc_pid_rpm = rpm_filtered;
+ }
+ mc_interface_set_pid_speed(cc_pid_rpm);
+ continue;
+ }
+ cc_was_pid = false;
+#else
+ (void)cc_button;
+#endif
+
+ // ---- 7) Apply drive current ----
+ mc_interface_set_current_rel(current_rel);
+
+ if (pconf->multi_esc) {
+ for (int i = 0; i < CAN_STATUS_MSGS_TO_STORE; i++) {
+ can_status_msg *msg = comm_can_get_status_msg_index(i);
+ if (msg->id >= 0 && UTILS_AGE_S(msg->rx_time) < MAX_CAN_AGE) {
+ comm_can_set_current_rel(msg->id, current_rel);
+ }
+ }
+ }
+ }
+}
diff --git a/hwconf/IVY_Esk8/650_330/hw_650_330.c b/hwconf/IVY_Esk8/650_330/hw_650_330.c
new file mode 100644
index 0000000000..5c627bc0e5
--- /dev/null
+++ b/hwconf/IVY_Esk8/650_330/hw_650_330.c
@@ -0,0 +1,270 @@
+/*
+ Copyright 2018 Benjamin Vedder benjamin@vedder.se
+ 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 .
+ */
+
+// V2
+
+#include "hw.h"
+#include "hw_650_330.h"
+
+#include "ch.h"
+#include "hal.h"
+#include "stm32f4xx_conf.h"
+#include "utils.h"
+#include
+#include "mc_interface.h"
+
+// Variables
+static volatile bool i2c_running = false;
+static mutex_t shutdown_mutex;
+static float bt_diff = 0.0;
+
+// I2C configuration
+static const I2CConfig i2cfg = {
+ OPMODE_I2C,
+ 100000,
+ STD_DUTY_CYCLE};
+
+void hw_init_gpio(void)
+{
+ chMtxObjectInit(&shutdown_mutex);
+ // GPIO clock enable
+ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
+ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
+ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
+ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
+
+ // LEDs
+ palSetPadMode(LED_GREEN_GPIO, LED_GREEN_PIN,
+ PAL_MODE_OUTPUT_PUSHPULL |
+ PAL_STM32_OSPEED_HIGHEST);
+ palSetPadMode(LED_RED_GPIO, LED_RED_PIN,
+ PAL_MODE_OUTPUT_PUSHPULL |
+ PAL_STM32_OSPEED_HIGHEST);
+
+ // GPIOA Configuration: Channel 1 to 3 as alternate function push-pull
+ palSetPadMode(GPIOA, 8, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING);
+ palSetPadMode(GPIOA, 9, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING);
+ palSetPadMode(GPIOA, 10, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING);
+
+ palSetPadMode(GPIOB, 13, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING);
+ palSetPadMode(GPIOB, 14, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING);
+ palSetPadMode(GPIOB, 15, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING);
+
+ // Hall sensors
+ palSetPadMode(HW_HALL_ENC_GPIO1, HW_HALL_ENC_PIN1, PAL_MODE_INPUT_PULLUP);
+ palSetPadMode(HW_HALL_ENC_GPIO2, HW_HALL_ENC_PIN2, PAL_MODE_INPUT_PULLUP);
+ palSetPadMode(HW_HALL_ENC_GPIO3, HW_HALL_ENC_PIN3, PAL_MODE_INPUT_PULLUP);
+
+#ifdef HW_HAS_PHASE_FILTERS
+ // Phase filters
+ palSetPadMode(PHASE_FILTER_GPIO, PHASE_FILTER_PIN,
+ PAL_MODE_OUTPUT_PUSHPULL |
+ PAL_STM32_OSPEED_HIGHEST);
+ PHASE_FILTER_OFF();
+#endif
+
+#ifdef HW_HAS_CURR_FILTERS
+ // Current filter
+ palSetPadMode(GPIOD, 2,
+ PAL_MODE_OUTPUT_PUSHPULL |
+ PAL_STM32_OSPEED_HIGHEST);
+
+ CURRENT_FILTER_OFF();
+#endif
+
+ // AUX pin
+ // AUX_OFF();
+ // palSetPadMode(AUX_GPIO, AUX_PIN,
+ // PAL_MODE_OUTPUT_PUSHPULL |
+ // PAL_STM32_OSPEED_HIGHEST);
+
+ // ADC Pins
+ palSetPadMode(GPIOA, 0, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOA, 1, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOA, 2, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOA, 3, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOA, 5, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOA, 6, PAL_MODE_INPUT_ANALOG);
+
+ palSetPadMode(GPIOB, 0, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOB, 1, PAL_MODE_INPUT_ANALOG);
+
+ palSetPadMode(GPIOC, 0, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOC, 1, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOC, 2, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOC, 3, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOC, 4, PAL_MODE_INPUT_ANALOG);
+}
+
+void hw_setup_adc_channels(void)
+{
+ // ADC1 regular channels
+ ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 2, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 3, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 4, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC1, ADC_Channel_Vrefint, 5, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 6, ADC_SampleTime_15Cycles);
+
+ // ADC2 regular channels
+ ADC_RegularChannelConfig(ADC2, ADC_Channel_1, 1, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC2, ADC_Channel_11, 2, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC2, ADC_Channel_6, 3, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC2, ADC_Channel_15, 4, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC2, ADC_Channel_0, 5, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC2, ADC_Channel_9, 6, ADC_SampleTime_15Cycles);
+
+ // ADC3 regular channels
+ ADC_RegularChannelConfig(ADC3, ADC_Channel_2, 1, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC3, ADC_Channel_12, 2, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC3, ADC_Channel_3, 3, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC3, ADC_Channel_13, 4, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC3, ADC_Channel_1, 5, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC3, ADC_Channel_2, 6, ADC_SampleTime_15Cycles);
+
+ // Injected channels
+ ADC_InjectedChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC2, ADC_Channel_11, 1, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC3, ADC_Channel_12, 1, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC1, ADC_Channel_10, 2, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC2, ADC_Channel_11, 2, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC3, ADC_Channel_12, 2, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC1, ADC_Channel_10, 3, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC2, ADC_Channel_11, 3, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC3, ADC_Channel_12, 3, ADC_SampleTime_15Cycles);
+}
+
+void hw_start_i2c(void)
+{
+ i2cAcquireBus(&HW_I2C_DEV);
+
+ if (!i2c_running)
+ {
+ palSetPadMode(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN,
+ PAL_MODE_ALTERNATE(HW_I2C_GPIO_AF) |
+ PAL_STM32_OTYPE_OPENDRAIN |
+ PAL_STM32_OSPEED_MID1 |
+ PAL_STM32_PUDR_PULLUP);
+ palSetPadMode(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN,
+ PAL_MODE_ALTERNATE(HW_I2C_GPIO_AF) |
+ PAL_STM32_OTYPE_OPENDRAIN |
+ PAL_STM32_OSPEED_MID1 |
+ PAL_STM32_PUDR_PULLUP);
+
+ i2cStart(&HW_I2C_DEV, &i2cfg);
+ i2c_running = true;
+ }
+
+ i2cReleaseBus(&HW_I2C_DEV);
+}
+
+void hw_stop_i2c(void)
+{
+ i2cAcquireBus(&HW_I2C_DEV);
+
+ if (i2c_running)
+ {
+ palSetPadMode(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN, PAL_MODE_INPUT);
+ palSetPadMode(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN, PAL_MODE_INPUT);
+
+ i2cStop(&HW_I2C_DEV);
+ i2c_running = false;
+ }
+
+ i2cReleaseBus(&HW_I2C_DEV);
+}
+
+/**
+ * Try to restore the i2c bus
+ */
+void hw_try_restore_i2c(void)
+{
+ if (i2c_running)
+ {
+ i2cAcquireBus(&HW_I2C_DEV);
+
+ palSetPadMode(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN,
+ PAL_STM32_OTYPE_OPENDRAIN |
+ PAL_STM32_OSPEED_MID1 |
+ PAL_STM32_PUDR_PULLUP);
+
+ palSetPadMode(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN,
+ PAL_STM32_OTYPE_OPENDRAIN |
+ PAL_STM32_OSPEED_MID1 |
+ PAL_STM32_PUDR_PULLUP);
+
+ palSetPad(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN);
+ palSetPad(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN);
+
+ chThdSleep(1);
+
+ for (int i = 0; i < 16; i++)
+ {
+ palClearPad(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN);
+ chThdSleep(1);
+ palSetPad(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN);
+ chThdSleep(1);
+ }
+
+ // Generate start then stop condition
+ palClearPad(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN);
+ chThdSleep(1);
+ palClearPad(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN);
+ chThdSleep(1);
+ palSetPad(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN);
+ chThdSleep(1);
+ palSetPad(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN);
+
+ palSetPadMode(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN,
+ PAL_MODE_ALTERNATE(HW_I2C_GPIO_AF) |
+ PAL_STM32_OTYPE_OPENDRAIN |
+ PAL_STM32_OSPEED_MID1 |
+ PAL_STM32_PUDR_PULLUP);
+
+ palSetPadMode(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN,
+ PAL_MODE_ALTERNATE(HW_I2C_GPIO_AF) |
+ PAL_STM32_OTYPE_OPENDRAIN |
+ PAL_STM32_OSPEED_MID1 |
+ PAL_STM32_PUDR_PULLUP);
+
+ HW_I2C_DEV.state = I2C_STOP;
+ i2cStart(&HW_I2C_DEV, &i2cfg);
+
+ i2cReleaseBus(&HW_I2C_DEV);
+ }
+}
+
+bool hw_sample_shutdown_button(void)
+{
+ chMtxLock(&shutdown_mutex);
+
+ bt_diff = 0.0;
+
+ for (int i = 0; i < 3; i++)
+ {
+ palSetPadMode(HW_SHUTDOWN_GPIO, HW_SHUTDOWN_PIN, PAL_MODE_INPUT_ANALOG);
+ chThdSleep(5);
+ float val1 = ADC_VOLTS(ADC_IND_SHUTDOWN);
+ chThdSleepMilliseconds(1);
+ float val2 = ADC_VOLTS(ADC_IND_SHUTDOWN);
+ palSetPadMode(HW_SHUTDOWN_GPIO, HW_SHUTDOWN_PIN, PAL_MODE_OUTPUT_PUSHPULL);
+ chThdSleepMilliseconds(1);
+
+ bt_diff += (val1 - val2);
+ }
+
+ chMtxUnlock(&shutdown_mutex);
+
+ return (bt_diff > 0.12);
+}
diff --git a/hwconf/IVY_Esk8/650_330/hw_650_330.h b/hwconf/IVY_Esk8/650_330/hw_650_330.h
new file mode 100644
index 0000000000..075fd342d9
--- /dev/null
+++ b/hwconf/IVY_Esk8/650_330/hw_650_330.h
@@ -0,0 +1,312 @@
+/*
+ Copyright 2018 Benjamin Vedder benjamin@vedder.se
+
+ This file is part of the VESC firmware.
+
+ The VESC firmware 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.
+
+ The VESC firmware 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 .
+*/
+
+#ifndef HW_650_330_H_
+
+#define HW_650_330_H_
+
+#define HW_NAME "650_330"
+
+// Default setting overrides
+#define APPCONF_APP_TO_USE APP_NONE
+// #define APPCONF_CONTROLLER_ID 2
+#define APPCONF_TIMEOUT_MSEC 500
+#define APPCONF_TIMEOUT_BRAKE_CURRENT 0
+#define APPCONF_PERMANENT_UART_ENABLED false
+#define APPCONF_SHUTDOWN_MODE SHUTDOWN_MODE_ALWAYS_ON
+#define APPCONF_CAN_BAUD_RATE CAN_BAUD_1M
+#define APPCONF_CAN_STATUS_RATE_1 100
+#define APPCONF_CAN_STATUS_RATE_2 5
+#define APPCONF_CAN_STATUS_MSGS_R1 0x09
+#define APPCONF_CAN_STATUS_MSGS_R2 0x10
+// Common PID-parameters
+#define MCCONF_SP_PID_LOOP_RATE PID_RATE_5000_HZ // PID loop rate
+// Speed PID parameters
+#define MCCONF_S_PID_KP 0.003 // Proportional gain
+#define MCCONF_S_PID_KI 0.009 // Integral gain
+#define MCCONF_S_PID_KD 0.00002 // Derivative gain
+#define MCCONF_S_PID_KD_FILTER 0.2 // Derivative filter
+#define MCCONF_S_PID_MIN_RPM 300.0 // Minimum allowed RPM
+#define MCCONF_S_PID_ALLOW_BRAKING true // Allow braking in speed control mode
+#define MCCONF_S_PID_RAMP_ERPMS_S 2000.0 // Speed input ramping, in ERPM/s
+#define MCCONF_S_PID_SPEED_SOURCE S_PID_SPEED_SRC_PLL
+// Position PID parameters
+#define MCCONF_P_PID_KP 0.03 // Proportional gain
+#define MCCONF_P_PID_KI 0.0 // Integral gain
+#define MCCONF_P_PID_KD 0.00000 // Derivative gain
+#define MCCONF_P_PID_KD_PROC 0.00035 // Derivative gain process
+#define MCCONF_P_PID_KD_FILTER 0.2 // Derivative filter
+#define MCCONF_P_PID_ANG_DIV 10.0 // Divide angle by this value
+#define MCCONF_P_PID_GAIN_DEC_ANGLE 500.0 // Decrease PID-gains when the error is below this value
+#define MCCONF_P_PID_OFFSET 0.0 // Angle offset
+
+#ifndef MCCONF_L_MIN_VOLTAGE
+#define MCCONF_L_MIN_VOLTAGE 30.0 // Minimum input voltage
+#endif
+#ifndef MCCONF_L_MAX_VOLTAGE
+#define MCCONF_L_MAX_VOLTAGE 600.0 // Maximum input voltage
+#endif
+#endif
+#ifndef MCCONF_DEFAULT_MOTOR_TYPE
+#define MCCONF_DEFAULT_MOTOR_TYPE MOTOR_TYPE_FOC
+#endif
+#ifndef MCCONF_FOC_F_ZV
+#define MCCONF_FOC_F_ZV 30000.0
+#endif
+#ifndef MCCONF_L_MAX_ABS_CURRENT
+#define MCCONF_L_MAX_ABS_CURRENT 330.0 // The maximum absolute current above which a fault is generated
+#endif
+#ifndef MCCONF_FOC_SAMPLE_V0_V7
+#define MCCONF_FOC_SAMPLE_V0_V7 true // Run control loop in both v0 and v7 (requires phase shunts)
+#endif
+#ifndef MCCONF_L_IN_CURRENT_MAX
+#define MCCONF_L_IN_CURRENT_MAX 200.0 // Input current limit in Amperes (Upper)
+#ifndef MCCONF_L_IN_CURRENT_MIN
+#define MCCONF_L_IN_CURRENT_MIN -200.0 // Input current limit in Amperes (Lower)
+#endif
+
+// Override dead time. See the stm32f4 reference manual for calculating this value.
+#define HW_DEAD_TIME_NSEC 1800.0
+
+// HW properties
+#define HW_HAS_3_SHUNTS
+// #define HW_HAS_PHASE_SHUNTS
+// #define HW_HAS_PHASE_FILTERS
+// #define HW_HAS_CURR_FILTERS
+
+// Macros
+#define LED_GREEN_GPIO GPIOB
+#define LED_GREEN_PIN 5
+#define LED_RED_GPIO GPIOB
+#define LED_RED_PIN 7
+
+#define LED_GREEN_ON() palSetPad(LED_GREEN_GPIO, LED_GREEN_PIN)
+#define LED_GREEN_OFF() palClearPad(LED_GREEN_GPIO, LED_GREEN_PIN)
+#define LED_RED_ON() palSetPad(LED_RED_GPIO, LED_RED_PIN)
+#define LED_RED_OFF() palClearPad(LED_RED_GPIO, LED_RED_PIN)
+
+#ifdef HW_HAS_PHASE_FILTERS
+#define PHASE_FILTER_GPIO GPIOC
+#define PHASE_FILTER_PIN 9
+#define PHASE_FILTER_ON() palSetPad(PHASE_FILTER_GPIO, PHASE_FILTER_PIN)
+#define PHASE_FILTER_OFF() palClearPad(PHASE_FILTER_GPIO, PHASE_FILTER_PIN)
+#endif
+
+// Shutdown pin
+#define HW_SHUTDOWN_GPIO GPIOC
+#define HW_SHUTDOWN_PIN 5
+#define HW_SHUTDOWN_HOLD_ON() palSetPad(HW_SHUTDOWN_GPIO, HW_SHUTDOWN_PIN)
+#define HW_SHUTDOWN_HOLD_OFF() palClearPad(HW_SHUTDOWN_GPIO, HW_SHUTDOWN_PIN)
+#define HW_SAMPLE_SHUTDOWN() hw_sample_shutdown_button()
+
+// Hold shutdown pin early to wake up on short pulses
+#define HW_EARLY_INIT() \
+ palSetPadMode(HW_SHUTDOWN_GPIO, HW_SHUTDOWN_PIN, PAL_MODE_OUTPUT_PUSHPULL); \
+ HW_SHUTDOWN_HOLD_ON();
+
+#ifdef HW_HAS_CURR_FILTERS
+#define CURRENT_FILTER_ON() palSetPad(GPIOD, 2)
+#define CURRENT_FILTER_OFF() palClearPad(GPIOD, 2)
+#endif
+
+/*
+ * ADC Vector
+ *
+ * 0 (1): IN0 SENS1
+ * 1 (2): IN1 SENS2
+ * 2 (3): IN2 SENS3
+ * 3 (1): IN10 CURR1
+ * 4 (2): IN11 CURR2
+ * 5 (3): IN12 CURR3
+ * 6 (1): IN5 ADC_EXT1
+ * 7 (2): IN6 ADC_EXT2
+ * 8 (3): IN3 TEMP_MOS
+ * 9 (1): IN14 TEMP_MOTOR
+ * 10 (2): IN15 SHUTDOWN
+ * 11 (3): IN13 AN_IN
+ * 12 (1): Vrefint
+ * 13 (2): IN0 SENS1
+ * 14 (3): IN1 SENS2
+ * 15 (1): IN8 TEMP_MOS_2
+ * 16 (2): IN9 TEMP_MOS_3
+ * 17 (3): IN3 SENS3
+ */
+
+#define HW_ADC_CHANNELS 18
+#define HW_ADC_INJ_CHANNELS 3
+#define HW_ADC_NBR_CONV 6
+
+// ADC Indexes
+#define ADC_IND_SENS1 0
+#define ADC_IND_SENS2 1
+#define ADC_IND_SENS3 2
+#define ADC_IND_CURR1 3
+#define ADC_IND_CURR2 4
+#define ADC_IND_CURR3 5
+#define ADC_IND_VIN_SENS 11
+#define ADC_IND_EXT 6
+#define ADC_IND_EXT2 7
+#define ADC_IND_SHUTDOWN 10
+#define ADC_IND_TEMP_MOS 8
+#define ADC_IND_TEMP_MOTOR 9
+#define ADC_IND_VREFINT 12
+
+// ADC macros and settings
+
+// Component parameters (can be overridden)
+#ifndef V_REG
+#define V_REG 3.30
+#endif
+
+// The voltage dividing acquisition circuit on the Makerbase VESC motherboard is 560K and 21.5K resistors.
+#ifndef VIN_R1
+#define VIN_R1 1410000.0
+#endif
+#ifndef VIN_R2
+#define VIN_R2 5000.0
+#endif
+
+#ifndef CURRENT_AMP_GAIN
+#define CURRENT_AMP_GAIN (20.0)
+#endif
+#ifndef CURRENT_SHUNT_RES
+#define CURRENT_SHUNT_RES (0.0005)
+#endif
+
+// Input voltage
+#define GET_INPUT_VOLTAGE() ((V_REG / 4095.0) * (float)ADC_Value[ADC_IND_VIN_SENS] * ((VIN_R1 + VIN_R2) / VIN_R2))
+
+// NTC Termistors
+// #define NTC_RES(adc_val) ((4095.0 * 10000.0) / adc_val - 10000.0)
+#define NTC_RES(adc_val) ((4095.0 * 10000.0) / adc_val - 10000.0)
+#define NTC_TEMP(adc_ind) (1.0 / ((logf(NTC_RES(ADC_Value[adc_ind]) / 10000.0) / 3435.0) + (1.0 / 298.15)) - 273.15)
+
+#define NTC_RES_MOTOR(adc_val) (10000.0 / ((4095.0 / (float)adc_val) - 1.0)) // Motor temp sensor on low side
+#define NTC_TEMP_MOTOR(beta) (1.0 / ((logf(NTC_RES_MOTOR(ADC_Value[ADC_IND_TEMP_MOTOR]) / 10000.0) / beta) + (1.0 / 298.15)) - 273.15)
+
+// Voltage on ADC channel
+#define ADC_VOLTS(ch) ((float)ADC_Value[ch] / 4096.0 * V_REG)
+
+// Double samples in beginning and end for positive current measurement.
+// Useful when the shunt sense traces have noise that causes offset.
+#ifndef CURR1_DOUBLE_SAMPLE
+#define CURR1_DOUBLE_SAMPLE 0
+#endif
+#ifndef CURR2_DOUBLE_SAMPLE
+#define CURR2_DOUBLE_SAMPLE 0
+#endif
+#ifndef CURR3_DOUBLE_SAMPLE
+#define CURR3_DOUBLE_SAMPLE 0
+#endif
+
+// COMM-port ADC GPIOs
+#define HW_ADC_EXT_GPIO GPIOA
+#define HW_ADC_EXT_PIN 5
+#define HW_ADC_EXT2_GPIO GPIOA
+#define HW_ADC_EXT2_PIN 6
+
+// UART Peripheral
+#define HW_UART_DEV SD3
+#define HW_UART_GPIO_AF GPIO_AF_USART3
+#define HW_UART_TX_PORT GPIOB
+#define HW_UART_TX_PIN 10
+#define HW_UART_RX_PORT GPIOB
+#define HW_UART_RX_PIN 11
+
+// Permanent UART Peripheral (for NRF51)
+#define HW_UART_P_BAUD 115200
+#define HW_UART_P_DEV SD4
+#define HW_UART_P_GPIO_AF GPIO_AF_UART4
+#define HW_UART_P_TX_PORT GPIOC
+#define HW_UART_P_TX_PIN 10
+#define HW_UART_P_RX_PORT GPIOC
+#define HW_UART_P_RX_PIN 11
+
+// ICU Peripheral for servo decoding
+#define HW_USE_SERVO_TIM4
+#define HW_ICU_TIMER TIM4
+#define HW_ICU_TIM_CLK_EN() RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE)
+#define HW_ICU_DEV ICUD4
+#define HW_ICU_CHANNEL ICU_CHANNEL_1
+#define HW_ICU_GPIO_AF GPIO_AF_TIM4
+#define HW_ICU_GPIO GPIOB
+#define HW_ICU_PIN 6
+
+// I2C Peripheral
+#define HW_I2C_DEV I2CD2
+#define HW_I2C_GPIO_AF GPIO_AF_I2C2
+#define HW_I2C_SCL_PORT GPIOB
+#define HW_I2C_SCL_PIN 10
+#define HW_I2C_SDA_PORT GPIOB
+#define HW_I2C_SDA_PIN 11
+
+// Hall/encoder pins
+#define HW_HALL_ENC_GPIO1 GPIOC
+#define HW_HALL_ENC_PIN1 6
+#define HW_HALL_ENC_GPIO2 GPIOC
+#define HW_HALL_ENC_PIN2 7
+#define HW_HALL_ENC_GPIO3 GPIOC
+#define HW_HALL_ENC_PIN3 8
+#define HW_ENC_TIM TIM3
+#define HW_ENC_TIM_AF GPIO_AF_TIM3
+#define HW_ENC_TIM_CLK_EN() RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE)
+#define HW_ENC_EXTI_PORTSRC EXTI_PortSourceGPIOC
+#define HW_ENC_EXTI_PINSRC EXTI_PinSource8
+#define HW_ENC_EXTI_CH EXTI9_5_IRQn
+#define HW_ENC_EXTI_LINE EXTI_Line8
+#define HW_ENC_EXTI_ISR_VEC EXTI9_5_IRQHandler
+#define HW_ENC_TIM_ISR_CH TIM3_IRQn
+#define HW_ENC_TIM_ISR_VEC TIM3_IRQHandler
+
+// SPI pins
+#define HW_SPI_DEV SPID1
+#define HW_SPI_GPIO_AF GPIO_AF_SPI1
+#define HW_SPI_PORT_NSS GPIOA
+#define HW_SPI_PIN_NSS 4
+#define HW_SPI_PORT_SCK GPIOA
+#define HW_SPI_PIN_SCK 5
+#define HW_SPI_PORT_MOSI GPIOA
+#define HW_SPI_PIN_MOSI 7
+#define HW_SPI_PORT_MISO GPIOA
+#define HW_SPI_PIN_MISO 6
+
+// Measurement macros
+#define ADC_V_L1 ADC_Value[ADC_IND_SENS1]
+#define ADC_V_L2 ADC_Value[ADC_IND_SENS2]
+#define ADC_V_L3 ADC_Value[ADC_IND_SENS3]
+#define ADC_V_ZERO (ADC_Value[ADC_IND_VIN_SENS] / 2)
+
+// Macros
+#define READ_HALL1() palReadPad(HW_HALL_ENC_GPIO1, HW_HALL_ENC_PIN1)
+#define READ_HALL2() palReadPad(HW_HALL_ENC_GPIO2, HW_HALL_ENC_PIN2)
+#define READ_HALL3() palReadPad(HW_HALL_ENC_GPIO3, HW_HALL_ENC_PIN3)
+
+// Setting limits
+#define HW_LIM_CURRENT -330.0, 330.0
+#define HW_LIM_CURRENT_IN -200.0, 200.0
+#define HW_LIM_CURRENT_ABS 0.0, 330
+#define HW_LIM_VIN 30.0, 650.0
+#define HW_LIM_ERPM -200e3, 200e3
+#define HW_LIM_DUTY_MIN 0.0, 0.1
+#define HW_LIM_DUTY_MAX 0.0, 0.99
+#define HW_LIM_TEMP_FET -40.0, 110.0
+
+// HW-specific functions
+bool hw_sample_shutdown_button(void);
+#endif /* HW_650_330_H_ */
diff --git a/hwconf/IVY_Esk8/650_330/hw_650_330_no_limits.h b/hwconf/IVY_Esk8/650_330/hw_650_330_no_limits.h
new file mode 100644
index 0000000000..ba7dd0bbe6
--- /dev/null
+++ b/hwconf/IVY_Esk8/650_330/hw_650_330_no_limits.h
@@ -0,0 +1,27 @@
+/*
+ Copyright 2018 Benjamin Vedder benjamin@vedder.se
+
+ This file is part of the VESC firmware.
+
+ The VESC firmware 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.
+
+ The VESC firmware 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 .
+*/
+
+#ifndef HW_650_330_NO_LIMITS_H_
+#define HW_650_330_NO_LIMITS_H_
+
+#define DISABLE_HW_LIMITS
+
+#include "hw_650_330.h"
+
+#endif /* HW_650_330_NO_LIMITS_H_ */
diff --git a/hwconf/IVY_Esk8/IVY_100V_HP/hw_IVY_100V_HP.c b/hwconf/IVY_Esk8/IVY_100V_HP/hw_IVY_100V_HP.c
new file mode 100644
index 0000000000..703b3aa3ce
--- /dev/null
+++ b/hwconf/IVY_Esk8/IVY_100V_HP/hw_IVY_100V_HP.c
@@ -0,0 +1,270 @@
+/*
+ Copyright 2018 Benjamin Vedder benjamin@vedder.se
+ 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 .
+ */
+
+// V2
+
+#include "hw.h"
+#include "hw_IVY_100V_HP.h"
+
+#include "ch.h"
+#include "hal.h"
+#include "stm32f4xx_conf.h"
+#include "utils.h"
+#include
+#include "mc_interface.h"
+
+// Variables
+static volatile bool i2c_running = false;
+static mutex_t shutdown_mutex;
+static float bt_diff = 0.0;
+
+// I2C configuration
+static const I2CConfig i2cfg = {
+ OPMODE_I2C,
+ 100000,
+ STD_DUTY_CYCLE};
+
+void hw_init_gpio(void)
+{
+ chMtxObjectInit(&shutdown_mutex);
+ // GPIO clock enable
+ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
+ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
+ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
+ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
+
+ // LEDs
+ palSetPadMode(LED_GREEN_GPIO, LED_GREEN_PIN,
+ PAL_MODE_OUTPUT_PUSHPULL |
+ PAL_STM32_OSPEED_HIGHEST);
+ palSetPadMode(LED_RED_GPIO, LED_RED_PIN,
+ PAL_MODE_OUTPUT_PUSHPULL |
+ PAL_STM32_OSPEED_HIGHEST);
+
+ // GPIOA Configuration: Channel 1 to 3 as alternate function push-pull
+ palSetPadMode(GPIOA, 8, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING);
+ palSetPadMode(GPIOA, 9, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING);
+ palSetPadMode(GPIOA, 10, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING);
+
+ palSetPadMode(GPIOB, 13, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING);
+ palSetPadMode(GPIOB, 14, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING);
+ palSetPadMode(GPIOB, 15, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING);
+
+ // Hall sensors
+ palSetPadMode(HW_HALL_ENC_GPIO1, HW_HALL_ENC_PIN1, PAL_MODE_INPUT_PULLUP);
+ palSetPadMode(HW_HALL_ENC_GPIO2, HW_HALL_ENC_PIN2, PAL_MODE_INPUT_PULLUP);
+ palSetPadMode(HW_HALL_ENC_GPIO3, HW_HALL_ENC_PIN3, PAL_MODE_INPUT_PULLUP);
+
+#ifdef HW_HAS_PHASE_FILTERS
+ // Phase filters
+ palSetPadMode(PHASE_FILTER_GPIO, PHASE_FILTER_PIN,
+ PAL_MODE_OUTPUT_PUSHPULL |
+ PAL_STM32_OSPEED_HIGHEST);
+ PHASE_FILTER_OFF();
+#endif
+
+#ifdef HW_HAS_CURR_FILTERS
+ // Current filter
+ palSetPadMode(GPIOD, 2,
+ PAL_MODE_OUTPUT_PUSHPULL |
+ PAL_STM32_OSPEED_HIGHEST);
+
+ CURRENT_FILTER_OFF();
+#endif
+
+ // AUX pin
+ // AUX_OFF();
+ // palSetPadMode(AUX_GPIO, AUX_PIN,
+ // PAL_MODE_OUTPUT_PUSHPULL |
+ // PAL_STM32_OSPEED_HIGHEST);
+
+ // ADC Pins
+ palSetPadMode(GPIOA, 0, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOA, 1, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOA, 2, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOA, 3, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOA, 5, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOA, 6, PAL_MODE_INPUT_ANALOG);
+
+ palSetPadMode(GPIOB, 0, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOB, 1, PAL_MODE_INPUT_ANALOG);
+
+ palSetPadMode(GPIOC, 0, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOC, 1, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOC, 2, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOC, 3, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOC, 4, PAL_MODE_INPUT_ANALOG);
+}
+
+void hw_setup_adc_channels(void)
+{
+ // ADC1 regular channels
+ ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 2, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 3, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 4, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC1, ADC_Channel_Vrefint, 5, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 6, ADC_SampleTime_15Cycles);
+
+ // ADC2 regular channels
+ ADC_RegularChannelConfig(ADC2, ADC_Channel_1, 1, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC2, ADC_Channel_11, 2, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC2, ADC_Channel_6, 3, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC2, ADC_Channel_15, 4, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC2, ADC_Channel_0, 5, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC2, ADC_Channel_9, 6, ADC_SampleTime_15Cycles);
+
+ // ADC3 regular channels
+ ADC_RegularChannelConfig(ADC3, ADC_Channel_2, 1, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC3, ADC_Channel_12, 2, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC3, ADC_Channel_3, 3, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC3, ADC_Channel_13, 4, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC3, ADC_Channel_1, 5, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC3, ADC_Channel_2, 6, ADC_SampleTime_15Cycles);
+
+ // Injected channels
+ ADC_InjectedChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC2, ADC_Channel_11, 1, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC3, ADC_Channel_12, 1, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC1, ADC_Channel_10, 2, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC2, ADC_Channel_11, 2, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC3, ADC_Channel_12, 2, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC1, ADC_Channel_10, 3, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC2, ADC_Channel_11, 3, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC3, ADC_Channel_12, 3, ADC_SampleTime_15Cycles);
+}
+
+void hw_start_i2c(void)
+{
+ i2cAcquireBus(&HW_I2C_DEV);
+
+ if (!i2c_running)
+ {
+ palSetPadMode(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN,
+ PAL_MODE_ALTERNATE(HW_I2C_GPIO_AF) |
+ PAL_STM32_OTYPE_OPENDRAIN |
+ PAL_STM32_OSPEED_MID1 |
+ PAL_STM32_PUDR_PULLUP);
+ palSetPadMode(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN,
+ PAL_MODE_ALTERNATE(HW_I2C_GPIO_AF) |
+ PAL_STM32_OTYPE_OPENDRAIN |
+ PAL_STM32_OSPEED_MID1 |
+ PAL_STM32_PUDR_PULLUP);
+
+ i2cStart(&HW_I2C_DEV, &i2cfg);
+ i2c_running = true;
+ }
+
+ i2cReleaseBus(&HW_I2C_DEV);
+}
+
+void hw_stop_i2c(void)
+{
+ i2cAcquireBus(&HW_I2C_DEV);
+
+ if (i2c_running)
+ {
+ palSetPadMode(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN, PAL_MODE_INPUT);
+ palSetPadMode(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN, PAL_MODE_INPUT);
+
+ i2cStop(&HW_I2C_DEV);
+ i2c_running = false;
+ }
+
+ i2cReleaseBus(&HW_I2C_DEV);
+}
+
+/**
+ * Try to restore the i2c bus
+ */
+void hw_try_restore_i2c(void)
+{
+ if (i2c_running)
+ {
+ i2cAcquireBus(&HW_I2C_DEV);
+
+ palSetPadMode(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN,
+ PAL_STM32_OTYPE_OPENDRAIN |
+ PAL_STM32_OSPEED_MID1 |
+ PAL_STM32_PUDR_PULLUP);
+
+ palSetPadMode(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN,
+ PAL_STM32_OTYPE_OPENDRAIN |
+ PAL_STM32_OSPEED_MID1 |
+ PAL_STM32_PUDR_PULLUP);
+
+ palSetPad(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN);
+ palSetPad(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN);
+
+ chThdSleep(1);
+
+ for (int i = 0; i < 16; i++)
+ {
+ palClearPad(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN);
+ chThdSleep(1);
+ palSetPad(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN);
+ chThdSleep(1);
+ }
+
+ // Generate start then stop condition
+ palClearPad(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN);
+ chThdSleep(1);
+ palClearPad(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN);
+ chThdSleep(1);
+ palSetPad(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN);
+ chThdSleep(1);
+ palSetPad(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN);
+
+ palSetPadMode(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN,
+ PAL_MODE_ALTERNATE(HW_I2C_GPIO_AF) |
+ PAL_STM32_OTYPE_OPENDRAIN |
+ PAL_STM32_OSPEED_MID1 |
+ PAL_STM32_PUDR_PULLUP);
+
+ palSetPadMode(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN,
+ PAL_MODE_ALTERNATE(HW_I2C_GPIO_AF) |
+ PAL_STM32_OTYPE_OPENDRAIN |
+ PAL_STM32_OSPEED_MID1 |
+ PAL_STM32_PUDR_PULLUP);
+
+ HW_I2C_DEV.state = I2C_STOP;
+ i2cStart(&HW_I2C_DEV, &i2cfg);
+
+ i2cReleaseBus(&HW_I2C_DEV);
+ }
+}
+
+bool hw_sample_shutdown_button(void)
+{
+ chMtxLock(&shutdown_mutex);
+
+ bt_diff = 0.0;
+
+ for (int i = 0; i < 3; i++)
+ {
+ palSetPadMode(HW_SHUTDOWN_GPIO, HW_SHUTDOWN_PIN, PAL_MODE_INPUT_ANALOG);
+ chThdSleep(5);
+ float val1 = ADC_VOLTS(ADC_IND_SHUTDOWN);
+ chThdSleepMilliseconds(1);
+ float val2 = ADC_VOLTS(ADC_IND_SHUTDOWN);
+ palSetPadMode(HW_SHUTDOWN_GPIO, HW_SHUTDOWN_PIN, PAL_MODE_OUTPUT_PUSHPULL);
+ chThdSleepMilliseconds(1);
+
+ bt_diff += (val1 - val2);
+ }
+
+ chMtxUnlock(&shutdown_mutex);
+
+ return (bt_diff > 0.12);
+}
diff --git a/hwconf/IVY_Esk8/IVY_100V_HP/hw_IVY_100V_HP.h b/hwconf/IVY_Esk8/IVY_100V_HP/hw_IVY_100V_HP.h
new file mode 100644
index 0000000000..2d1a3edd7e
--- /dev/null
+++ b/hwconf/IVY_Esk8/IVY_100V_HP/hw_IVY_100V_HP.h
@@ -0,0 +1,282 @@
+/*
+ Copyright 2018 Benjamin Vedder benjamin@vedder.se
+
+ This file is part of the VESC firmware.
+
+ The VESC firmware 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.
+
+ The VESC firmware 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 .
+*/
+
+#ifndef HW_IVY_100V_HP_H_
+#define HW_IVY_100V_HP_H_
+
+#define HW_NAME "IVY_100V_HP"
+
+// Default setting overrides
+#define APPCONF_APP_TO_USE APP_NONE
+// #define APPCONF_CONTROLLER_ID 2
+#define APPCONF_TIMEOUT_MSEC 500
+#define APPCONF_TIMEOUT_BRAKE_CURRENT 0
+#define APPCONF_PERMANENT_UART_ENABLED false
+#define APPCONF_SHUTDOWN_MODE SHUTDOWN_MODE_ALWAYS_ON
+#define APPCONF_CAN_BAUD_RATE CAN_BAUD_1M
+#define APPCONF_CAN_STATUS_RATE_1 100
+#define APPCONF_CAN_STATUS_RATE_2 5
+#define APPCONF_CAN_STATUS_MSGS_R1 0x09
+#define APPCONF_CAN_STATUS_MSGS_R2 0x10
+// Common PID-parameters
+#define MCCONF_SP_PID_LOOP_RATE PID_RATE_5000_HZ // PID loop rate
+// Speed PID parameters
+#define MCCONF_S_PID_KP 0.003 // Proportional gain
+#define MCCONF_S_PID_KI 0.009 // Integral gain
+#define MCCONF_S_PID_KD 0.00002 // Derivative gain
+#define MCCONF_S_PID_KD_FILTER 0.2 // Derivative filter
+#define MCCONF_S_PID_MIN_RPM 300.0 // Minimum allowed RPM
+#define MCCONF_S_PID_ALLOW_BRAKING true // Allow braking in speed control mode
+#define MCCONF_S_PID_RAMP_ERPMS_S 2000.0 // Speed input ramping, in ERPM/s
+#define MCCONF_S_PID_SPEED_SOURCE S_PID_SPEED_SRC_PLL
+// Position PID parameters
+#define MCCONF_P_PID_KP 0.03 // Proportional gain
+#define MCCONF_P_PID_KI 0.0 // Integral gain
+#define MCCONF_P_PID_KD 0.00000 // Derivative gain
+#define MCCONF_P_PID_KD_PROC 0.00035 // Derivative gain process
+#define MCCONF_P_PID_KD_FILTER 0.2 // Derivative filter
+#define MCCONF_P_PID_ANG_DIV 10.0 // Divide angle by this value
+#define MCCONF_P_PID_GAIN_DEC_ANGLE 500.0 // Decrease PID-gains when the error is below this value
+#define MCCONF_P_PID_OFFSET 0.0 // Angle offset
+
+#define MCCONF_L_MIN_VOLTAGE 20.0 // Minimum input voltage
+#define MCCONF_L_MAX_VOLTAGE 95.0 // Maximum input voltage
+#define MCCONF_DEFAULT_MOTOR_TYPE MOTOR_TYPE_FOC
+
+#define MCCONF_L_MAX_ABS_CURRENT 320.0 // The maximum absolute current above which a fault is generated
+#define MCCONF_L_IN_CURRENT_MAX 200.0 // Input current limit in Amperes (Upper)
+#define MCCONF_L_IN_CURRENT_MIN -200.0 // Input current limit in Amperes (Lower)
+
+#define MCCONF_FOC_F_ZV 15000.0
+#define MCCONF_FOC_CONTROL_SAMPLE_MODE FOC_CONTROL_SAMPLE_MODE_V0_V7
+#define MCCONF_FOC_SAMPLE_V0_V7 true // Run control loop in both v0 and v7 (requires phase shunts)
+
+// Override dead time. See the stm32f4 reference manual for calculating this value.
+#define HW_DEAD_TIME_NSEC 700.0
+
+// HW properties
+#define HW_HAS_3_SHUNTS
+// #define HW_HAS_PHASE_SHUNTS
+#define HW_HAS_PHASE_FILTERS
+#define HW_HAS_CURR_FILTERS
+
+// Macros
+#define LED_GREEN_GPIO GPIOB
+#define LED_GREEN_PIN 5
+#define LED_RED_GPIO GPIOB
+#define LED_RED_PIN 7
+
+#define LED_GREEN_ON() palSetPad(LED_GREEN_GPIO, LED_GREEN_PIN)
+#define LED_GREEN_OFF() palClearPad(LED_GREEN_GPIO, LED_GREEN_PIN)
+#define LED_RED_ON() palSetPad(LED_RED_GPIO, LED_RED_PIN)
+#define LED_RED_OFF() palClearPad(LED_RED_GPIO, LED_RED_PIN)
+
+#ifdef HW_HAS_PHASE_FILTERS
+#define PHASE_FILTER_GPIO GPIOC
+#define PHASE_FILTER_PIN 9
+#define PHASE_FILTER_ON() palSetPad(PHASE_FILTER_GPIO, PHASE_FILTER_PIN)
+#define PHASE_FILTER_OFF() palClearPad(PHASE_FILTER_GPIO, PHASE_FILTER_PIN)
+#endif
+
+// Shutdown pin
+#define HW_SHUTDOWN_GPIO GPIOC
+#define HW_SHUTDOWN_PIN 5
+#define HW_SHUTDOWN_HOLD_ON() palSetPad(HW_SHUTDOWN_GPIO, HW_SHUTDOWN_PIN)
+#define HW_SHUTDOWN_HOLD_OFF() palClearPad(HW_SHUTDOWN_GPIO, HW_SHUTDOWN_PIN)
+#define HW_SAMPLE_SHUTDOWN() hw_sample_shutdown_button()
+
+// Hold shutdown pin early to wake up on short pulses
+#define HW_EARLY_INIT() \
+ palSetPadMode(HW_SHUTDOWN_GPIO, HW_SHUTDOWN_PIN, PAL_MODE_OUTPUT_PUSHPULL); \
+ HW_SHUTDOWN_HOLD_ON();
+
+#ifdef HW_HAS_CURR_FILTERS
+#define CURRENT_FILTER_ON() palSetPad(GPIOD, 2)
+#define CURRENT_FILTER_OFF() palClearPad(GPIOD, 2)
+#endif
+
+/*
+ * ADC Vector
+ *
+ * 0 (1): IN0 SENS1
+ * 1 (2): IN1 SENS2
+ * 2 (3): IN2 SENS3
+ * 3 (1): IN10 CURR1
+ * 4 (2): IN11 CURR2
+ * 5 (3): IN12 CURR3
+ * 6 (1): IN5 ADC_EXT1
+ * 7 (2): IN6 ADC_EXT2
+ * 8 (3): IN3 TEMP_MOS
+ * 9 (1): IN14 TEMP_MOTOR
+ * 10 (2): IN15 SHUTDOWN
+ * 11 (3): IN13 AN_IN
+ * 12 (1): Vrefint
+ * 13 (2): IN0 SENS1
+ * 14 (3): IN1 SENS2
+ * 15 (1): IN8 TEMP_MOS_2
+ * 16 (2): IN9 TEMP_MOS_3
+ * 17 (3): IN3 SENS3
+ */
+
+#define HW_ADC_CHANNELS 18
+#define HW_ADC_INJ_CHANNELS 3
+#define HW_ADC_NBR_CONV 6
+
+// ADC Indexes
+#define ADC_IND_SENS1 0
+#define ADC_IND_SENS2 1
+#define ADC_IND_SENS3 2
+#define ADC_IND_CURR1 3
+#define ADC_IND_CURR2 4
+#define ADC_IND_CURR3 5
+#define ADC_IND_VIN_SENS 11
+#define ADC_IND_EXT 6
+#define ADC_IND_EXT2 7
+#define ADC_IND_SHUTDOWN 10
+#define ADC_IND_TEMP_MOS 8
+#define ADC_IND_TEMP_MOTOR 9
+#define ADC_IND_VREFINT 12
+
+// ADC macros and settings
+
+// Component parameters
+#define V_REG 3.30
+
+// The voltage dividing acquisition circuit on the Makerbase VESC motherboard is 560K and 21.5K resistors.
+#define VIN_R1 750000.0
+#define VIN_R2 22000.0
+
+#define CURRENT_AMP_GAIN 20.0
+#define CURRENT_SHUNT_RES (0.0005 / 2.0)
+
+// Input voltage
+#define GET_INPUT_VOLTAGE() ((V_REG / 4095.0) * (float)ADC_Value[ADC_IND_VIN_SENS] * ((VIN_R1 + VIN_R2) / VIN_R2))
+
+// NTC Termistors
+// #define NTC_RES(adc_val) ((4095.0 * 10000.0) / adc_val - 10000.0)
+#define NTC_RES(adc_val) ((4095.0 * 10000.0) / adc_val - 10000.0)
+#define NTC_TEMP(adc_ind) (1.0 / ((logf(NTC_RES(ADC_Value[adc_ind]) / 10000.0) / 3435.0) + (1.0 / 298.15)) - 273.15)
+
+#define NTC_RES_MOTOR(adc_val) (10000.0 / ((4095.0 / (float)adc_val) - 1.0)) // Motor temp sensor on low side
+#define NTC_TEMP_MOTOR(beta) (1.0 / ((logf(NTC_RES_MOTOR(ADC_Value[ADC_IND_TEMP_MOTOR]) / 10000.0) / beta) + (1.0 / 298.15)) - 273.15)
+
+// Voltage on ADC channel
+#define ADC_VOLTS(ch) ((float)ADC_Value[ch] / 4096.0 * V_REG)
+
+// Double samples in beginning and end for positive current measurement.
+// Useful when the shunt sense traces have noise that causes offset.
+#define CURR1_DOUBLE_SAMPLE 0
+#define CURR2_DOUBLE_SAMPLE 0
+#define CURR3_DOUBLE_SAMPLE 0
+
+// COMM-port ADC GPIOs
+#define HW_ADC_EXT_GPIO GPIOA
+#define HW_ADC_EXT_PIN 5
+#define HW_ADC_EXT2_GPIO GPIOA
+#define HW_ADC_EXT2_PIN 6
+
+// UART Peripheral
+#define HW_UART_DEV SD3
+#define HW_UART_GPIO_AF GPIO_AF_USART3
+#define HW_UART_TX_PORT GPIOB
+#define HW_UART_TX_PIN 10
+#define HW_UART_RX_PORT GPIOB
+#define HW_UART_RX_PIN 11
+
+// Permanent UART Peripheral (for NRF51)
+#define HW_UART_P_BAUD 115200
+#define HW_UART_P_DEV SD4
+#define HW_UART_P_GPIO_AF GPIO_AF_UART4
+#define HW_UART_P_TX_PORT GPIOC
+#define HW_UART_P_TX_PIN 10
+#define HW_UART_P_RX_PORT GPIOC
+#define HW_UART_P_RX_PIN 11
+
+// ICU Peripheral for servo decoding
+#define HW_USE_SERVO_TIM4
+#define HW_ICU_TIMER TIM4
+#define HW_ICU_TIM_CLK_EN() RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE)
+#define HW_ICU_DEV ICUD4
+#define HW_ICU_CHANNEL ICU_CHANNEL_1
+#define HW_ICU_GPIO_AF GPIO_AF_TIM4
+#define HW_ICU_GPIO GPIOB
+#define HW_ICU_PIN 6
+
+// I2C Peripheral
+#define HW_I2C_DEV I2CD2
+#define HW_I2C_GPIO_AF GPIO_AF_I2C2
+#define HW_I2C_SCL_PORT GPIOB
+#define HW_I2C_SCL_PIN 10
+#define HW_I2C_SDA_PORT GPIOB
+#define HW_I2C_SDA_PIN 11
+
+// Hall/encoder pins
+#define HW_HALL_ENC_GPIO1 GPIOC
+#define HW_HALL_ENC_PIN1 6
+#define HW_HALL_ENC_GPIO2 GPIOC
+#define HW_HALL_ENC_PIN2 7
+#define HW_HALL_ENC_GPIO3 GPIOC
+#define HW_HALL_ENC_PIN3 8
+#define HW_ENC_TIM TIM3
+#define HW_ENC_TIM_AF GPIO_AF_TIM3
+#define HW_ENC_TIM_CLK_EN() RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE)
+#define HW_ENC_EXTI_PORTSRC EXTI_PortSourceGPIOC
+#define HW_ENC_EXTI_PINSRC EXTI_PinSource8
+#define HW_ENC_EXTI_CH EXTI9_5_IRQn
+#define HW_ENC_EXTI_LINE EXTI_Line8
+#define HW_ENC_EXTI_ISR_VEC EXTI9_5_IRQHandler
+#define HW_ENC_TIM_ISR_CH TIM3_IRQn
+#define HW_ENC_TIM_ISR_VEC TIM3_IRQHandler
+
+// SPI pins
+#define HW_SPI_DEV SPID1
+#define HW_SPI_GPIO_AF GPIO_AF_SPI1
+#define HW_SPI_PORT_NSS GPIOA
+#define HW_SPI_PIN_NSS 4
+#define HW_SPI_PORT_SCK GPIOA
+#define HW_SPI_PIN_SCK 5
+#define HW_SPI_PORT_MOSI GPIOA
+#define HW_SPI_PIN_MOSI 7
+#define HW_SPI_PORT_MISO GPIOA
+#define HW_SPI_PIN_MISO 6
+
+// Measurement macros
+#define ADC_V_L1 ADC_Value[ADC_IND_SENS1]
+#define ADC_V_L2 ADC_Value[ADC_IND_SENS2]
+#define ADC_V_L3 ADC_Value[ADC_IND_SENS3]
+#define ADC_V_ZERO (ADC_Value[ADC_IND_VIN_SENS] / 2)
+
+// Macros
+#define READ_HALL1() palReadPad(HW_HALL_ENC_GPIO1, HW_HALL_ENC_PIN1)
+#define READ_HALL2() palReadPad(HW_HALL_ENC_GPIO2, HW_HALL_ENC_PIN2)
+#define READ_HALL3() palReadPad(HW_HALL_ENC_GPIO3, HW_HALL_ENC_PIN3)
+
+// Setting limits
+#define HW_LIM_CURRENT -300.0, 300.0
+#define HW_LIM_CURRENT_IN -280.0, 280.0
+#define HW_LIM_CURRENT_ABS 0.0, 320
+#define HW_LIM_VIN 12.0, 90.0
+#define HW_LIM_ERPM -200e3, 200e3
+#define HW_LIM_DUTY_MIN 0.0, 0.1
+#define HW_LIM_DUTY_MAX 0.0, 0.99
+#define HW_LIM_TEMP_FET -40.0, 110.0
+
+// HW-specific functions
+bool hw_sample_shutdown_button(void);
+#endif /* HW_IVY_100V_HP_H_ */
diff --git a/hwconf/IVY_Esk8/IVY_100V_HP/hw_IVY_100V_HP_no_limits.h b/hwconf/IVY_Esk8/IVY_100V_HP/hw_IVY_100V_HP_no_limits.h
new file mode 100644
index 0000000000..d242df06d6
--- /dev/null
+++ b/hwconf/IVY_Esk8/IVY_100V_HP/hw_IVY_100V_HP_no_limits.h
@@ -0,0 +1,27 @@
+/*
+ Copyright 2018 Benjamin Vedder benjamin@vedder.se
+
+ This file is part of the VESC firmware.
+
+ The VESC firmware 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.
+
+ The VESC firmware 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 .
+*/
+
+#ifndef HW_IVY_100V_HP_NO_LIMITS_H_
+#define HW_IVY_100V_HP_NO_LIMITS_H_
+
+#define DISABLE_HW_LIMITS
+
+#include "hw_IVY_100V_HP.h"
+
+#endif /* HW_IVY_100V_HP_NO_LIMITS_H_ */
diff --git a/hwconf/IVY_Esk8/IVY_100V_MINI/hw_IVY_100V_MINI.c b/hwconf/IVY_Esk8/IVY_100V_MINI/hw_IVY_100V_MINI.c
new file mode 100644
index 0000000000..238d3a2e2c
--- /dev/null
+++ b/hwconf/IVY_Esk8/IVY_100V_MINI/hw_IVY_100V_MINI.c
@@ -0,0 +1,270 @@
+/*
+ Copyright 2018 Benjamin Vedder benjamin@vedder.se
+ 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 .
+ */
+
+// V2
+
+#include "hw.h"
+#include "hw_IVY_100V_MINI.h"
+
+#include "ch.h"
+#include "hal.h"
+#include "stm32f4xx_conf.h"
+#include "utils.h"
+#include
+#include "mc_interface.h"
+
+// Variables
+static volatile bool i2c_running = false;
+static mutex_t shutdown_mutex;
+static float bt_diff = 0.0;
+
+// I2C configuration
+static const I2CConfig i2cfg = {
+ OPMODE_I2C,
+ 100000,
+ STD_DUTY_CYCLE};
+
+void hw_init_gpio(void)
+{
+ chMtxObjectInit(&shutdown_mutex);
+ // GPIO clock enable
+ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
+ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
+ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
+ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
+
+ // LEDs
+ palSetPadMode(LED_GREEN_GPIO, LED_GREEN_PIN,
+ PAL_MODE_OUTPUT_PUSHPULL |
+ PAL_STM32_OSPEED_HIGHEST);
+ palSetPadMode(LED_RED_GPIO, LED_RED_PIN,
+ PAL_MODE_OUTPUT_PUSHPULL |
+ PAL_STM32_OSPEED_HIGHEST);
+
+ // GPIOA Configuration: Channel 1 to 3 as alternate function push-pull
+ palSetPadMode(GPIOA, 8, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING);
+ palSetPadMode(GPIOA, 9, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING);
+ palSetPadMode(GPIOA, 10, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING);
+
+ palSetPadMode(GPIOB, 13, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING);
+ palSetPadMode(GPIOB, 14, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING);
+ palSetPadMode(GPIOB, 15, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING);
+
+ // Hall sensors
+ palSetPadMode(HW_HALL_ENC_GPIO1, HW_HALL_ENC_PIN1, PAL_MODE_INPUT_PULLUP);
+ palSetPadMode(HW_HALL_ENC_GPIO2, HW_HALL_ENC_PIN2, PAL_MODE_INPUT_PULLUP);
+ palSetPadMode(HW_HALL_ENC_GPIO3, HW_HALL_ENC_PIN3, PAL_MODE_INPUT_PULLUP);
+
+#ifdef HW_HAS_PHASE_FILTERS
+ // Phase filters
+ palSetPadMode(PHASE_FILTER_GPIO, PHASE_FILTER_PIN,
+ PAL_MODE_OUTPUT_PUSHPULL |
+ PAL_STM32_OSPEED_HIGHEST);
+ PHASE_FILTER_OFF();
+#endif
+
+#ifdef HW_HAS_CURR_FILTERS
+ // Current filter
+ palSetPadMode(GPIOD, 2,
+ PAL_MODE_OUTPUT_PUSHPULL |
+ PAL_STM32_OSPEED_HIGHEST);
+
+ CURRENT_FILTER_OFF();
+#endif
+
+ // AUX pin
+ // AUX_OFF();
+ // palSetPadMode(AUX_GPIO, AUX_PIN,
+ // PAL_MODE_OUTPUT_PUSHPULL |
+ // PAL_STM32_OSPEED_HIGHEST);
+
+ // ADC Pins
+ palSetPadMode(GPIOA, 0, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOA, 1, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOA, 2, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOA, 3, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOA, 5, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOA, 6, PAL_MODE_INPUT_ANALOG);
+
+ palSetPadMode(GPIOB, 0, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOB, 1, PAL_MODE_INPUT_ANALOG);
+
+ palSetPadMode(GPIOC, 0, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOC, 1, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOC, 2, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOC, 3, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOC, 4, PAL_MODE_INPUT_ANALOG);
+}
+
+void hw_setup_adc_channels(void)
+{
+ // ADC1 regular channels
+ ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 2, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 3, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 4, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC1, ADC_Channel_Vrefint, 5, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 6, ADC_SampleTime_15Cycles);
+
+ // ADC2 regular channels
+ ADC_RegularChannelConfig(ADC2, ADC_Channel_1, 1, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC2, ADC_Channel_11, 2, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC2, ADC_Channel_6, 3, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC2, ADC_Channel_15, 4, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC2, ADC_Channel_0, 5, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC2, ADC_Channel_9, 6, ADC_SampleTime_15Cycles);
+
+ // ADC3 regular channels
+ ADC_RegularChannelConfig(ADC3, ADC_Channel_2, 1, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC3, ADC_Channel_12, 2, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC3, ADC_Channel_3, 3, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC3, ADC_Channel_13, 4, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC3, ADC_Channel_1, 5, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC3, ADC_Channel_2, 6, ADC_SampleTime_15Cycles);
+
+ // Injected channels
+ ADC_InjectedChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC2, ADC_Channel_11, 1, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC3, ADC_Channel_12, 1, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC1, ADC_Channel_10, 2, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC2, ADC_Channel_11, 2, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC3, ADC_Channel_12, 2, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC1, ADC_Channel_10, 3, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC2, ADC_Channel_11, 3, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC3, ADC_Channel_12, 3, ADC_SampleTime_15Cycles);
+}
+
+void hw_start_i2c(void)
+{
+ i2cAcquireBus(&HW_I2C_DEV);
+
+ if (!i2c_running)
+ {
+ palSetPadMode(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN,
+ PAL_MODE_ALTERNATE(HW_I2C_GPIO_AF) |
+ PAL_STM32_OTYPE_OPENDRAIN |
+ PAL_STM32_OSPEED_MID1 |
+ PAL_STM32_PUDR_PULLUP);
+ palSetPadMode(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN,
+ PAL_MODE_ALTERNATE(HW_I2C_GPIO_AF) |
+ PAL_STM32_OTYPE_OPENDRAIN |
+ PAL_STM32_OSPEED_MID1 |
+ PAL_STM32_PUDR_PULLUP);
+
+ i2cStart(&HW_I2C_DEV, &i2cfg);
+ i2c_running = true;
+ }
+
+ i2cReleaseBus(&HW_I2C_DEV);
+}
+
+void hw_stop_i2c(void)
+{
+ i2cAcquireBus(&HW_I2C_DEV);
+
+ if (i2c_running)
+ {
+ palSetPadMode(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN, PAL_MODE_INPUT);
+ palSetPadMode(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN, PAL_MODE_INPUT);
+
+ i2cStop(&HW_I2C_DEV);
+ i2c_running = false;
+ }
+
+ i2cReleaseBus(&HW_I2C_DEV);
+}
+
+/**
+ * Try to restore the i2c bus
+ */
+void hw_try_restore_i2c(void)
+{
+ if (i2c_running)
+ {
+ i2cAcquireBus(&HW_I2C_DEV);
+
+ palSetPadMode(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN,
+ PAL_STM32_OTYPE_OPENDRAIN |
+ PAL_STM32_OSPEED_MID1 |
+ PAL_STM32_PUDR_PULLUP);
+
+ palSetPadMode(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN,
+ PAL_STM32_OTYPE_OPENDRAIN |
+ PAL_STM32_OSPEED_MID1 |
+ PAL_STM32_PUDR_PULLUP);
+
+ palSetPad(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN);
+ palSetPad(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN);
+
+ chThdSleep(1);
+
+ for (int i = 0; i < 16; i++)
+ {
+ palClearPad(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN);
+ chThdSleep(1);
+ palSetPad(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN);
+ chThdSleep(1);
+ }
+
+ // Generate start then stop condition
+ palClearPad(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN);
+ chThdSleep(1);
+ palClearPad(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN);
+ chThdSleep(1);
+ palSetPad(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN);
+ chThdSleep(1);
+ palSetPad(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN);
+
+ palSetPadMode(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN,
+ PAL_MODE_ALTERNATE(HW_I2C_GPIO_AF) |
+ PAL_STM32_OTYPE_OPENDRAIN |
+ PAL_STM32_OSPEED_MID1 |
+ PAL_STM32_PUDR_PULLUP);
+
+ palSetPadMode(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN,
+ PAL_MODE_ALTERNATE(HW_I2C_GPIO_AF) |
+ PAL_STM32_OTYPE_OPENDRAIN |
+ PAL_STM32_OSPEED_MID1 |
+ PAL_STM32_PUDR_PULLUP);
+
+ HW_I2C_DEV.state = I2C_STOP;
+ i2cStart(&HW_I2C_DEV, &i2cfg);
+
+ i2cReleaseBus(&HW_I2C_DEV);
+ }
+}
+
+bool hw_sample_shutdown_button(void)
+{
+ chMtxLock(&shutdown_mutex);
+
+ bt_diff = 0.0;
+
+ for (int i = 0; i < 3; i++)
+ {
+ palSetPadMode(HW_SHUTDOWN_GPIO, HW_SHUTDOWN_PIN, PAL_MODE_INPUT_ANALOG);
+ chThdSleep(5);
+ float val1 = ADC_VOLTS(ADC_IND_SHUTDOWN);
+ chThdSleepMilliseconds(1);
+ float val2 = ADC_VOLTS(ADC_IND_SHUTDOWN);
+ palSetPadMode(HW_SHUTDOWN_GPIO, HW_SHUTDOWN_PIN, PAL_MODE_OUTPUT_PUSHPULL);
+ chThdSleepMilliseconds(1);
+
+ bt_diff += (val1 - val2);
+ }
+
+ chMtxUnlock(&shutdown_mutex);
+
+ return (bt_diff > 0.12);
+}
diff --git a/hwconf/IVY_Esk8/IVY_100V_MINI/hw_IVY_100V_MINI.h b/hwconf/IVY_Esk8/IVY_100V_MINI/hw_IVY_100V_MINI.h
new file mode 100644
index 0000000000..2124e1f02a
--- /dev/null
+++ b/hwconf/IVY_Esk8/IVY_100V_MINI/hw_IVY_100V_MINI.h
@@ -0,0 +1,282 @@
+/*
+ Copyright 2018 Benjamin Vedder benjamin@vedder.se
+
+ This file is part of the VESC firmware.
+
+ The VESC firmware 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.
+
+ The VESC firmware 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 .
+*/
+
+#ifndef HW_IVY_100V_MINI_H_
+#define HW_IVY_100V_MINI_H_
+
+#define HW_NAME "IVY_100V_MINI"
+
+// Default setting overrides
+#define APPCONF_APP_TO_USE APP_NONE
+// #define APPCONF_CONTROLLER_ID 2
+#define APPCONF_TIMEOUT_MSEC 500
+#define APPCONF_TIMEOUT_BRAKE_CURRENT 0
+#define APPCONF_PERMANENT_UART_ENABLED false
+#define APPCONF_SHUTDOWN_MODE SHUTDOWN_MODE_ALWAYS_ON
+#define APPCONF_CAN_BAUD_RATE CAN_BAUD_1M
+#define APPCONF_CAN_STATUS_RATE_1 100
+#define APPCONF_CAN_STATUS_RATE_2 5
+#define APPCONF_CAN_STATUS_MSGS_R1 0x09
+#define APPCONF_CAN_STATUS_MSGS_R2 0x10
+// Common PID-parameters
+#define MCCONF_SP_PID_LOOP_RATE PID_RATE_5000_HZ // PID loop rate
+// Speed PID parameters
+#define MCCONF_S_PID_KP 0.003 // Proportional gain
+#define MCCONF_S_PID_KI 0.009 // Integral gain
+#define MCCONF_S_PID_KD 0.00002 // Derivative gain
+#define MCCONF_S_PID_KD_FILTER 0.2 // Derivative filter
+#define MCCONF_S_PID_MIN_RPM 300.0 // Minimum allowed RPM
+#define MCCONF_S_PID_ALLOW_BRAKING true // Allow braking in speed control mode
+#define MCCONF_S_PID_RAMP_ERPMS_S 2000.0 // Speed input ramping, in ERPM/s
+#define MCCONF_S_PID_SPEED_SOURCE S_PID_SPEED_SRC_PLL
+// Position PID parameters
+#define MCCONF_P_PID_KP 0.03 // Proportional gain
+#define MCCONF_P_PID_KI 0.0 // Integral gain
+#define MCCONF_P_PID_KD 0.0 // Derivative gain
+#define MCCONF_P_PID_KD_PROC 0.00035 // Derivative gain process
+#define MCCONF_P_PID_KD_FILTER 0.2 // Derivative filter
+#define MCCONF_P_PID_ANG_DIV 10.0 // Divide angle by this value
+#define MCCONF_P_PID_GAIN_DEC_ANGLE 500.0 // Decrease PID-gains when the error is below this value
+#define MCCONF_P_PID_OFFSET 0.0 // Angle offset
+
+#define MCCONF_L_MIN_VOLTAGE 20.0 // Minimum input voltage
+#define MCCONF_L_MAX_VOLTAGE 90.0 // Maximum input voltage
+#define MCCONF_DEFAULT_MOTOR_TYPE MOTOR_TYPE_FOC
+
+#define MCCONF_L_MAX_ABS_CURRENT 160.0 // The maximum absolute current above which a fault is generated
+#define MCCONF_L_IN_CURRENT_MAX 110.0 // Input current limit in Amperes (Upper)
+#define MCCONF_L_IN_CURRENT_MIN -110.0 // Input current limit in Amperes (Lower)
+
+#define MCCONF_FOC_F_ZV 30000.0
+#define MCCONF_FOC_CONTROL_SAMPLE_MODE FOC_CONTROL_SAMPLE_MODE_V0_V7
+#define MCCONF_FOC_SAMPLE_V0_V7 true // Run control loop in both v0 and v7 (requires phase shunts)
+
+// Override dead time. See the stm32f4 reference manual for calculating this value.
+#define HW_DEAD_TIME_NSEC 700.0
+
+// HW properties
+#define HW_HAS_3_SHUNTS
+// #define HW_HAS_PHASE_SHUNTS
+// #define HW_HAS_PHASE_FILTERS
+// #define HW_HAS_CURR_FILTERS
+
+// Macros
+#define LED_GREEN_GPIO GPIOB
+#define LED_GREEN_PIN 5
+#define LED_RED_GPIO GPIOB
+#define LED_RED_PIN 7
+
+#define LED_GREEN_ON() palSetPad(LED_GREEN_GPIO, LED_GREEN_PIN)
+#define LED_GREEN_OFF() palClearPad(LED_GREEN_GPIO, LED_GREEN_PIN)
+#define LED_RED_ON() palSetPad(LED_RED_GPIO, LED_RED_PIN)
+#define LED_RED_OFF() palClearPad(LED_RED_GPIO, LED_RED_PIN)
+
+#ifdef HW_HAS_PHASE_FILTERS
+#define PHASE_FILTER_GPIO GPIOC
+#define PHASE_FILTER_PIN 9
+#define PHASE_FILTER_ON() palSetPad(PHASE_FILTER_GPIO, PHASE_FILTER_PIN)
+#define PHASE_FILTER_OFF() palClearPad(PHASE_FILTER_GPIO, PHASE_FILTER_PIN)
+#endif
+
+// Shutdown pin
+#define HW_SHUTDOWN_GPIO GPIOC
+#define HW_SHUTDOWN_PIN 5
+#define HW_SHUTDOWN_HOLD_ON() palSetPad(HW_SHUTDOWN_GPIO, HW_SHUTDOWN_PIN)
+#define HW_SHUTDOWN_HOLD_OFF() palClearPad(HW_SHUTDOWN_GPIO, HW_SHUTDOWN_PIN)
+#define HW_SAMPLE_SHUTDOWN() hw_sample_shutdown_button()
+
+// Hold shutdown pin early to wake up on short pulses
+#define HW_EARLY_INIT() \
+ palSetPadMode(HW_SHUTDOWN_GPIO, HW_SHUTDOWN_PIN, PAL_MODE_OUTPUT_PUSHPULL); \
+ HW_SHUTDOWN_HOLD_ON();
+
+#ifdef HW_HAS_CURR_FILTERS
+#define CURRENT_FILTER_ON() palSetPad(GPIOD, 2)
+#define CURRENT_FILTER_OFF() palClearPad(GPIOD, 2)
+#endif
+
+/*
+ * ADC Vector
+ *
+ * 0 (1): IN0 SENS1
+ * 1 (2): IN1 SENS2
+ * 2 (3): IN2 SENS3
+ * 3 (1): IN10 CURR1
+ * 4 (2): IN11 CURR2
+ * 5 (3): IN12 CURR3
+ * 6 (1): IN5 ADC_EXT1
+ * 7 (2): IN6 ADC_EXT2
+ * 8 (3): IN3 TEMP_MOS
+ * 9 (1): IN14 TEMP_MOTOR
+ * 10 (2): IN15 SHUTDOWN
+ * 11 (3): IN13 AN_IN
+ * 12 (1): Vrefint
+ * 13 (2): IN0 SENS1
+ * 14 (3): IN1 SENS2
+ * 15 (1): IN8 TEMP_MOS_2
+ * 16 (2): IN9 TEMP_MOS_3
+ * 17 (3): IN3 SENS3
+ */
+
+#define HW_ADC_CHANNELS 18
+#define HW_ADC_INJ_CHANNELS 3
+#define HW_ADC_NBR_CONV 6
+
+// ADC Indexes
+#define ADC_IND_SENS1 0
+#define ADC_IND_SENS2 1
+#define ADC_IND_SENS3 2
+#define ADC_IND_CURR1 3
+#define ADC_IND_CURR2 4
+#define ADC_IND_CURR3 5
+#define ADC_IND_VIN_SENS 11
+#define ADC_IND_EXT 6
+#define ADC_IND_EXT2 7
+#define ADC_IND_SHUTDOWN 10
+#define ADC_IND_TEMP_MOS 8
+#define ADC_IND_TEMP_MOTOR 9
+#define ADC_IND_VREFINT 12
+
+// ADC macros and settings
+
+// Component parameters (can be overridden)
+#define V_REG 3.30
+
+// The voltage dividing acquisition circuit on the Makerbase VESC motherboard is 73.2K and 2.2K resistors.
+#define VIN_R1 73200
+#define VIN_R2 2200.0
+
+#define CURRENT_AMP_GAIN (-20.0)
+#define CURRENT_SHUNT_RES (0.0005)
+
+// Input voltage
+#define GET_INPUT_VOLTAGE() ((V_REG / 4095.0) * (float)ADC_Value[ADC_IND_VIN_SENS] * ((VIN_R1 + VIN_R2) / VIN_R2))
+
+// NTC Termistors
+// #define NTC_RES(adc_val) ((4095.0 * 10000.0) / adc_val - 10000.0)
+#define NTC_RES(adc_val) ((4095.0 * 10000.0) / adc_val - 10000.0)
+#define NTC_TEMP(adc_ind) (1.0 / ((logf(NTC_RES(ADC_Value[adc_ind]) / 10000.0) / 3435.0) + (1.0 / 298.15)) - 273.15)
+
+#define NTC_RES_MOTOR(adc_val) (10000.0 / ((4095.0 / (float)adc_val) - 1.0)) // Motor temp sensor on low side
+#define NTC_TEMP_MOTOR(beta) (1.0 / ((logf(NTC_RES_MOTOR(ADC_Value[ADC_IND_TEMP_MOTOR]) / 10000.0) / beta) + (1.0 / 298.15)) - 273.15)
+
+// Voltage on ADC channel
+#define ADC_VOLTS(ch) ((float)ADC_Value[ch] / 4096.0 * V_REG)
+
+// Double samples in beginning and end for positive current measurement.
+// Useful when the shunt sense traces have noise that causes offset.
+#define CURR1_DOUBLE_SAMPLE 0
+#define CURR2_DOUBLE_SAMPLE 0
+#define CURR3_DOUBLE_SAMPLE 0
+
+// COMM-port ADC GPIOs
+#define HW_ADC_EXT_GPIO GPIOA
+#define HW_ADC_EXT_PIN 5
+#define HW_ADC_EXT2_GPIO GPIOA
+#define HW_ADC_EXT2_PIN 6
+
+// UART Peripheral
+#define HW_UART_DEV SD3
+#define HW_UART_GPIO_AF GPIO_AF_USART3
+#define HW_UART_TX_PORT GPIOB
+#define HW_UART_TX_PIN 10
+#define HW_UART_RX_PORT GPIOB
+#define HW_UART_RX_PIN 11
+
+// Permanent UART Peripheral (for NRF51)
+#define HW_UART_P_BAUD 115200
+#define HW_UART_P_DEV SD4
+#define HW_UART_P_GPIO_AF GPIO_AF_UART4
+#define HW_UART_P_TX_PORT GPIOC
+#define HW_UART_P_TX_PIN 10
+#define HW_UART_P_RX_PORT GPIOC
+#define HW_UART_P_RX_PIN 11
+
+// ICU Peripheral for servo decoding
+#define HW_USE_SERVO_TIM4
+#define HW_ICU_TIMER TIM4
+#define HW_ICU_TIM_CLK_EN() RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE)
+#define HW_ICU_DEV ICUD4
+#define HW_ICU_CHANNEL ICU_CHANNEL_1
+#define HW_ICU_GPIO_AF GPIO_AF_TIM4
+#define HW_ICU_GPIO GPIOB
+#define HW_ICU_PIN 6
+
+// I2C Peripheral
+#define HW_I2C_DEV I2CD2
+#define HW_I2C_GPIO_AF GPIO_AF_I2C2
+#define HW_I2C_SCL_PORT GPIOB
+#define HW_I2C_SCL_PIN 10
+#define HW_I2C_SDA_PORT GPIOB
+#define HW_I2C_SDA_PIN 11
+
+// Hall/encoder pins
+#define HW_HALL_ENC_GPIO1 GPIOC
+#define HW_HALL_ENC_PIN1 6
+#define HW_HALL_ENC_GPIO2 GPIOC
+#define HW_HALL_ENC_PIN2 7
+#define HW_HALL_ENC_GPIO3 GPIOC
+#define HW_HALL_ENC_PIN3 8
+#define HW_ENC_TIM TIM3
+#define HW_ENC_TIM_AF GPIO_AF_TIM3
+#define HW_ENC_TIM_CLK_EN() RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE)
+#define HW_ENC_EXTI_PORTSRC EXTI_PortSourceGPIOC
+#define HW_ENC_EXTI_PINSRC EXTI_PinSource8
+#define HW_ENC_EXTI_CH EXTI9_5_IRQn
+#define HW_ENC_EXTI_LINE EXTI_Line8
+#define HW_ENC_EXTI_ISR_VEC EXTI9_5_IRQHandler
+#define HW_ENC_TIM_ISR_CH TIM3_IRQn
+#define HW_ENC_TIM_ISR_VEC TIM3_IRQHandler
+
+// SPI pins
+#define HW_SPI_DEV SPID1
+#define HW_SPI_GPIO_AF GPIO_AF_SPI1
+#define HW_SPI_PORT_NSS GPIOA
+#define HW_SPI_PIN_NSS 4
+#define HW_SPI_PORT_SCK GPIOA
+#define HW_SPI_PIN_SCK 5
+#define HW_SPI_PORT_MOSI GPIOA
+#define HW_SPI_PIN_MOSI 7
+#define HW_SPI_PORT_MISO GPIOA
+#define HW_SPI_PIN_MISO 6
+
+// Measurement macros
+#define ADC_V_L1 ADC_Value[ADC_IND_SENS1]
+#define ADC_V_L2 ADC_Value[ADC_IND_SENS2]
+#define ADC_V_L3 ADC_Value[ADC_IND_SENS3]
+#define ADC_V_ZERO (ADC_Value[ADC_IND_VIN_SENS] / 2)
+
+// Macros
+#define READ_HALL1() palReadPad(HW_HALL_ENC_GPIO1, HW_HALL_ENC_PIN1)
+#define READ_HALL2() palReadPad(HW_HALL_ENC_GPIO2, HW_HALL_ENC_PIN2)
+#define READ_HALL3() palReadPad(HW_HALL_ENC_GPIO3, HW_HALL_ENC_PIN3)
+
+// Setting limits
+#define HW_LIM_CURRENT -110.0, 110.0
+#define HW_LIM_CURRENT_IN -100.0, 100.0
+#define HW_LIM_CURRENT_ABS 0.0, 160
+#define HW_LIM_VIN 12.0, 95.0
+#define HW_LIM_ERPM -200e3, 200e3
+#define HW_LIM_DUTY_MIN 0.0, 0.1
+#define HW_LIM_DUTY_MAX 0.0, 0.99
+#define HW_LIM_TEMP_FET -40.0, 110.0
+
+// HW-specific functions
+bool hw_sample_shutdown_button(void);
+#endif /* HW_IVY_100V_MINI_H_ */
diff --git a/hwconf/IVY_Esk8/IVY_100V_MINI/hw_IVY_100V_MINI_no_limits.h b/hwconf/IVY_Esk8/IVY_100V_MINI/hw_IVY_100V_MINI_no_limits.h
new file mode 100644
index 0000000000..9366c95bd8
--- /dev/null
+++ b/hwconf/IVY_Esk8/IVY_100V_MINI/hw_IVY_100V_MINI_no_limits.h
@@ -0,0 +1,27 @@
+/*
+ Copyright 2018 Benjamin Vedder benjamin@vedder.se
+
+ This file is part of the VESC firmware.
+
+ The VESC firmware 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.
+
+ The VESC firmware 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 .
+*/
+
+#ifndef HW_IVY_100V_MINI_NO_LIMITS_H_
+#define HW_IVY_100V_MINI_NO_LIMITS_H_
+
+#define DISABLE_HW_LIMITS
+
+#include "hw_IVY_100V_MINI.h"
+
+#endif /* HW_IVY_100V_MINI_NO_LIMITS_H_ */
diff --git a/hwconf/IVY_Esk8/IVY_80V_HP/hw_IVY_80V_HP.c b/hwconf/IVY_Esk8/IVY_80V_HP/hw_IVY_80V_HP.c
new file mode 100644
index 0000000000..dcf08d1d65
--- /dev/null
+++ b/hwconf/IVY_Esk8/IVY_80V_HP/hw_IVY_80V_HP.c
@@ -0,0 +1,270 @@
+/*
+ Copyright 2018 Benjamin Vedder benjamin@vedder.se
+ 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 .
+ */
+
+// V2
+
+#include "hw.h"
+#include "hw_IVY_80V_HP.h"
+
+#include "ch.h"
+#include "hal.h"
+#include "stm32f4xx_conf.h"
+#include "utils.h"
+#include
+#include "mc_interface.h"
+
+// Variables
+static volatile bool i2c_running = false;
+static mutex_t shutdown_mutex;
+static float bt_diff = 0.0;
+
+// I2C configuration
+static const I2CConfig i2cfg = {
+ OPMODE_I2C,
+ 100000,
+ STD_DUTY_CYCLE};
+
+void hw_init_gpio(void)
+{
+ chMtxObjectInit(&shutdown_mutex);
+ // GPIO clock enable
+ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
+ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
+ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
+ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
+
+ // LEDs
+ palSetPadMode(LED_GREEN_GPIO, LED_GREEN_PIN,
+ PAL_MODE_OUTPUT_PUSHPULL |
+ PAL_STM32_OSPEED_HIGHEST);
+ palSetPadMode(LED_RED_GPIO, LED_RED_PIN,
+ PAL_MODE_OUTPUT_PUSHPULL |
+ PAL_STM32_OSPEED_HIGHEST);
+
+ // GPIOA Configuration: Channel 1 to 3 as alternate function push-pull
+ palSetPadMode(GPIOA, 8, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING);
+ palSetPadMode(GPIOA, 9, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING);
+ palSetPadMode(GPIOA, 10, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING);
+
+ palSetPadMode(GPIOB, 13, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING);
+ palSetPadMode(GPIOB, 14, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING);
+ palSetPadMode(GPIOB, 15, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING);
+
+ // Hall sensors
+ palSetPadMode(HW_HALL_ENC_GPIO1, HW_HALL_ENC_PIN1, PAL_MODE_INPUT_PULLUP);
+ palSetPadMode(HW_HALL_ENC_GPIO2, HW_HALL_ENC_PIN2, PAL_MODE_INPUT_PULLUP);
+ palSetPadMode(HW_HALL_ENC_GPIO3, HW_HALL_ENC_PIN3, PAL_MODE_INPUT_PULLUP);
+
+#ifdef HW_HAS_PHASE_FILTERS
+ // Phase filters
+ palSetPadMode(PHASE_FILTER_GPIO, PHASE_FILTER_PIN,
+ PAL_MODE_OUTPUT_PUSHPULL |
+ PAL_STM32_OSPEED_HIGHEST);
+ PHASE_FILTER_OFF();
+#endif
+
+#ifdef HW_HAS_CURR_FILTERS
+ // Current filter
+ palSetPadMode(GPIOD, 2,
+ PAL_MODE_OUTPUT_PUSHPULL |
+ PAL_STM32_OSPEED_HIGHEST);
+
+ CURRENT_FILTER_OFF();
+#endif
+
+ // AUX pin
+ // AUX_OFF();
+ // palSetPadMode(AUX_GPIO, AUX_PIN,
+ // PAL_MODE_OUTPUT_PUSHPULL |
+ // PAL_STM32_OSPEED_HIGHEST);
+
+ // ADC Pins
+ palSetPadMode(GPIOA, 0, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOA, 1, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOA, 2, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOA, 3, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOA, 5, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOA, 6, PAL_MODE_INPUT_ANALOG);
+
+ palSetPadMode(GPIOB, 0, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOB, 1, PAL_MODE_INPUT_ANALOG);
+
+ palSetPadMode(GPIOC, 0, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOC, 1, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOC, 2, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOC, 3, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOC, 4, PAL_MODE_INPUT_ANALOG);
+}
+
+void hw_setup_adc_channels(void)
+{
+ // ADC1 regular channels
+ ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 2, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 3, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 4, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC1, ADC_Channel_Vrefint, 5, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 6, ADC_SampleTime_15Cycles);
+
+ // ADC2 regular channels
+ ADC_RegularChannelConfig(ADC2, ADC_Channel_1, 1, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC2, ADC_Channel_11, 2, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC2, ADC_Channel_6, 3, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC2, ADC_Channel_15, 4, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC2, ADC_Channel_0, 5, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC2, ADC_Channel_9, 6, ADC_SampleTime_15Cycles);
+
+ // ADC3 regular channels
+ ADC_RegularChannelConfig(ADC3, ADC_Channel_2, 1, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC3, ADC_Channel_12, 2, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC3, ADC_Channel_3, 3, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC3, ADC_Channel_13, 4, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC3, ADC_Channel_1, 5, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC3, ADC_Channel_2, 6, ADC_SampleTime_15Cycles);
+
+ // Injected channels
+ ADC_InjectedChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC2, ADC_Channel_11, 1, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC3, ADC_Channel_12, 1, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC1, ADC_Channel_10, 2, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC2, ADC_Channel_11, 2, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC3, ADC_Channel_12, 2, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC1, ADC_Channel_10, 3, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC2, ADC_Channel_11, 3, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC3, ADC_Channel_12, 3, ADC_SampleTime_15Cycles);
+}
+
+void hw_start_i2c(void)
+{
+ i2cAcquireBus(&HW_I2C_DEV);
+
+ if (!i2c_running)
+ {
+ palSetPadMode(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN,
+ PAL_MODE_ALTERNATE(HW_I2C_GPIO_AF) |
+ PAL_STM32_OTYPE_OPENDRAIN |
+ PAL_STM32_OSPEED_MID1 |
+ PAL_STM32_PUDR_PULLUP);
+ palSetPadMode(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN,
+ PAL_MODE_ALTERNATE(HW_I2C_GPIO_AF) |
+ PAL_STM32_OTYPE_OPENDRAIN |
+ PAL_STM32_OSPEED_MID1 |
+ PAL_STM32_PUDR_PULLUP);
+
+ i2cStart(&HW_I2C_DEV, &i2cfg);
+ i2c_running = true;
+ }
+
+ i2cReleaseBus(&HW_I2C_DEV);
+}
+
+void hw_stop_i2c(void)
+{
+ i2cAcquireBus(&HW_I2C_DEV);
+
+ if (i2c_running)
+ {
+ palSetPadMode(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN, PAL_MODE_INPUT);
+ palSetPadMode(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN, PAL_MODE_INPUT);
+
+ i2cStop(&HW_I2C_DEV);
+ i2c_running = false;
+ }
+
+ i2cReleaseBus(&HW_I2C_DEV);
+}
+
+/**
+ * Try to restore the i2c bus
+ */
+void hw_try_restore_i2c(void)
+{
+ if (i2c_running)
+ {
+ i2cAcquireBus(&HW_I2C_DEV);
+
+ palSetPadMode(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN,
+ PAL_STM32_OTYPE_OPENDRAIN |
+ PAL_STM32_OSPEED_MID1 |
+ PAL_STM32_PUDR_PULLUP);
+
+ palSetPadMode(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN,
+ PAL_STM32_OTYPE_OPENDRAIN |
+ PAL_STM32_OSPEED_MID1 |
+ PAL_STM32_PUDR_PULLUP);
+
+ palSetPad(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN);
+ palSetPad(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN);
+
+ chThdSleep(1);
+
+ for (int i = 0; i < 16; i++)
+ {
+ palClearPad(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN);
+ chThdSleep(1);
+ palSetPad(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN);
+ chThdSleep(1);
+ }
+
+ // Generate start then stop condition
+ palClearPad(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN);
+ chThdSleep(1);
+ palClearPad(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN);
+ chThdSleep(1);
+ palSetPad(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN);
+ chThdSleep(1);
+ palSetPad(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN);
+
+ palSetPadMode(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN,
+ PAL_MODE_ALTERNATE(HW_I2C_GPIO_AF) |
+ PAL_STM32_OTYPE_OPENDRAIN |
+ PAL_STM32_OSPEED_MID1 |
+ PAL_STM32_PUDR_PULLUP);
+
+ palSetPadMode(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN,
+ PAL_MODE_ALTERNATE(HW_I2C_GPIO_AF) |
+ PAL_STM32_OTYPE_OPENDRAIN |
+ PAL_STM32_OSPEED_MID1 |
+ PAL_STM32_PUDR_PULLUP);
+
+ HW_I2C_DEV.state = I2C_STOP;
+ i2cStart(&HW_I2C_DEV, &i2cfg);
+
+ i2cReleaseBus(&HW_I2C_DEV);
+ }
+}
+
+bool hw_sample_shutdown_button(void)
+{
+ chMtxLock(&shutdown_mutex);
+
+ bt_diff = 0.0;
+
+ for (int i = 0; i < 3; i++)
+ {
+ palSetPadMode(HW_SHUTDOWN_GPIO, HW_SHUTDOWN_PIN, PAL_MODE_INPUT_ANALOG);
+ chThdSleep(5);
+ float val1 = ADC_VOLTS(ADC_IND_SHUTDOWN);
+ chThdSleepMilliseconds(1);
+ float val2 = ADC_VOLTS(ADC_IND_SHUTDOWN);
+ palSetPadMode(HW_SHUTDOWN_GPIO, HW_SHUTDOWN_PIN, PAL_MODE_OUTPUT_PUSHPULL);
+ chThdSleepMilliseconds(1);
+
+ bt_diff += (val1 - val2);
+ }
+
+ chMtxUnlock(&shutdown_mutex);
+
+ return (bt_diff > 0.12);
+}
diff --git a/hwconf/IVY_Esk8/IVY_80V_HP/hw_IVY_80V_HP.h b/hwconf/IVY_Esk8/IVY_80V_HP/hw_IVY_80V_HP.h
new file mode 100644
index 0000000000..ebd48c1a02
--- /dev/null
+++ b/hwconf/IVY_Esk8/IVY_80V_HP/hw_IVY_80V_HP.h
@@ -0,0 +1,283 @@
+/*
+ Copyright 2018 Benjamin Vedder benjamin@vedder.se
+
+ This file is part of the VESC firmware.
+
+ The VESC firmware 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.
+
+ The VESC firmware 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 .
+*/
+
+#ifndef HW_IVY_80V_HP_H_
+
+#define HW_IVY_80V_HP_H_
+
+#define HW_NAME "IVY_80V_HP"
+
+// Default setting overrides
+#define APPCONF_APP_TO_USE APP_NONE
+// #define APPCONF_CONTROLLER_ID 2
+#define APPCONF_TIMEOUT_MSEC 500
+#define APPCONF_TIMEOUT_BRAKE_CURRENT 0
+#define APPCONF_PERMANENT_UART_ENABLED false
+#define APPCONF_SHUTDOWN_MODE SHUTDOWN_MODE_ALWAYS_ON
+#define APPCONF_CAN_BAUD_RATE CAN_BAUD_1M
+#define APPCONF_CAN_STATUS_RATE_1 100
+#define APPCONF_CAN_STATUS_RATE_2 5
+#define APPCONF_CAN_STATUS_MSGS_R1 0x09
+#define APPCONF_CAN_STATUS_MSGS_R2 0x10
+// Common PID-parameters
+#define MCCONF_SP_PID_LOOP_RATE PID_RATE_5000_HZ // PID loop rate
+// Speed PID parameters
+#define MCCONF_S_PID_KP 0.003 // Proportional gain
+#define MCCONF_S_PID_KI 0.009 // Integral gain
+#define MCCONF_S_PID_KD 0.00002 // Derivative gain
+#define MCCONF_S_PID_KD_FILTER 0.2 // Derivative filter
+#define MCCONF_S_PID_MIN_RPM 300.0 // Minimum allowed RPM
+#define MCCONF_S_PID_ALLOW_BRAKING true // Allow braking in speed control mode
+#define MCCONF_S_PID_RAMP_ERPMS_S 2000.0 // Speed input ramping, in ERPM/s
+#define MCCONF_S_PID_SPEED_SOURCE S_PID_SPEED_SRC_PLL
+// Position PID parameters
+#define MCCONF_P_PID_KP 0.03 // Proportional gain
+#define MCCONF_P_PID_KI 0.0 // Integral gain
+#define MCCONF_P_PID_KD 0.00000 // Derivative gain
+#define MCCONF_P_PID_KD_PROC 0.00035 // Derivative gain process
+#define MCCONF_P_PID_KD_FILTER 0.2 // Derivative filter
+#define MCCONF_P_PID_ANG_DIV 10.0 // Divide angle by this value
+#define MCCONF_P_PID_GAIN_DEC_ANGLE 500.0 // Decrease PID-gains when the error is below this value
+#define MCCONF_P_PID_OFFSET 0.0 // Angle offset
+
+#define MCCONF_L_MIN_VOLTAGE 20.0 // Minimum input voltage
+#define MCCONF_L_MAX_VOLTAGE 70.0 // Maximum input voltage
+#define MCCONF_DEFAULT_MOTOR_TYPE MOTOR_TYPE_FOC
+
+#define MCCONF_L_MAX_ABS_CURRENT 320.0 // The maximum absolute current above which a fault is generated
+#define MCCONF_L_IN_CURRENT_MAX 200.0 // Input current limit in Amperes (Upper)
+#define MCCONF_L_IN_CURRENT_MIN -200.0 // Input current limit in Amperes (Lower)
+
+#define MCCONF_FOC_F_ZV 30000.0
+#define MCCONF_FOC_CONTROL_SAMPLE_MODE FOC_CONTROL_SAMPLE_MODE_V0
+#define MCCONF_FOC_SAMPLE_V0_V7 false // Run control loop in both v0 and v7 (requires phase shunts)
+
+// Override dead time. See the stm32f4 reference manual for calculating this value.
+#define HW_DEAD_TIME_NSEC 600.0
+
+// HW properties
+#define HW_HAS_3_SHUNTS
+#define HW_HAS_PHASE_SHUNTS
+#define HW_HAS_PHASE_FILTERS
+#define HW_HAS_CURR_FILTERS
+
+// Macros
+#define LED_GREEN_GPIO GPIOB
+#define LED_GREEN_PIN 5
+#define LED_RED_GPIO GPIOB
+#define LED_RED_PIN 7
+
+#define LED_GREEN_ON() palSetPad(LED_GREEN_GPIO, LED_GREEN_PIN)
+#define LED_GREEN_OFF() palClearPad(LED_GREEN_GPIO, LED_GREEN_PIN)
+#define LED_RED_ON() palSetPad(LED_RED_GPIO, LED_RED_PIN)
+#define LED_RED_OFF() palClearPad(LED_RED_GPIO, LED_RED_PIN)
+
+#ifdef HW_HAS_PHASE_FILTERS
+#define PHASE_FILTER_GPIO GPIOC
+#define PHASE_FILTER_PIN 9
+#define PHASE_FILTER_ON() palSetPad(PHASE_FILTER_GPIO, PHASE_FILTER_PIN)
+#define PHASE_FILTER_OFF() palClearPad(PHASE_FILTER_GPIO, PHASE_FILTER_PIN)
+#endif
+
+// Shutdown pin
+#define HW_SHUTDOWN_GPIO GPIOC
+#define HW_SHUTDOWN_PIN 5
+#define HW_SHUTDOWN_HOLD_ON() palSetPad(HW_SHUTDOWN_GPIO, HW_SHUTDOWN_PIN)
+#define HW_SHUTDOWN_HOLD_OFF() palClearPad(HW_SHUTDOWN_GPIO, HW_SHUTDOWN_PIN)
+#define HW_SAMPLE_SHUTDOWN() hw_sample_shutdown_button()
+
+// Hold shutdown pin early to wake up on short pulses
+#define HW_EARLY_INIT() \
+ palSetPadMode(HW_SHUTDOWN_GPIO, HW_SHUTDOWN_PIN, PAL_MODE_OUTPUT_PUSHPULL); \
+ HW_SHUTDOWN_HOLD_ON();
+
+#ifdef HW_HAS_CURR_FILTERS
+#define CURRENT_FILTER_ON() palSetPad(GPIOD, 2)
+#define CURRENT_FILTER_OFF() palClearPad(GPIOD, 2)
+#endif
+
+/*
+ * ADC Vector
+ *
+ * 0 (1): IN0 SENS1
+ * 1 (2): IN1 SENS2
+ * 2 (3): IN2 SENS3
+ * 3 (1): IN10 CURR1
+ * 4 (2): IN11 CURR2
+ * 5 (3): IN12 CURR3
+ * 6 (1): IN5 ADC_EXT1
+ * 7 (2): IN6 ADC_EXT2
+ * 8 (3): IN3 TEMP_MOS
+ * 9 (1): IN14 TEMP_MOTOR
+ * 10 (2): IN15 SHUTDOWN
+ * 11 (3): IN13 AN_IN
+ * 12 (1): Vrefint
+ * 13 (2): IN0 SENS1
+ * 14 (3): IN1 SENS2
+ * 15 (1): IN8 TEMP_MOS_2
+ * 16 (2): IN9 TEMP_MOS_3
+ * 17 (3): IN3 SENS3
+ */
+
+#define HW_ADC_CHANNELS 18
+#define HW_ADC_INJ_CHANNELS 3
+#define HW_ADC_NBR_CONV 6
+
+// ADC Indexes
+#define ADC_IND_SENS1 0
+#define ADC_IND_SENS2 1
+#define ADC_IND_SENS3 2
+#define ADC_IND_CURR1 3
+#define ADC_IND_CURR2 4
+#define ADC_IND_CURR3 5
+#define ADC_IND_VIN_SENS 11
+#define ADC_IND_EXT 6
+#define ADC_IND_EXT2 7
+#define ADC_IND_SHUTDOWN 10
+#define ADC_IND_TEMP_MOS 8
+#define ADC_IND_TEMP_MOTOR 9
+#define ADC_IND_VREFINT 12
+
+// ADC macros and settings
+
+// Component parameters (can be overridden)
+#define V_REG 3.30
+
+// The voltage dividing acquisition circuit on the Makerbase VESC motherboard is 560K and 21.5K resistors.
+#define VIN_R1 560000.0
+#define VIN_R2 22000.0
+
+#define CURRENT_AMP_GAIN 20.0
+#define CURRENT_SHUNT_RES (0.0005 / 2.0)
+
+// Input voltage
+#define GET_INPUT_VOLTAGE() ((V_REG / 4095.0) * (float)ADC_Value[ADC_IND_VIN_SENS] * ((VIN_R1 + VIN_R2) / VIN_R2))
+
+// NTC Termistors
+// #define NTC_RES(adc_val) ((4095.0 * 10000.0) / adc_val - 10000.0)
+#define NTC_RES(adc_val) ((4095.0 * 10000.0) / adc_val - 10000.0)
+#define NTC_TEMP(adc_ind) (1.0 / ((logf(NTC_RES(ADC_Value[adc_ind]) / 10000.0) / 3435.0) + (1.0 / 298.15)) - 273.15)
+
+#define NTC_RES_MOTOR(adc_val) (10000.0 / ((4095.0 / (float)adc_val) - 1.0)) // Motor temp sensor on low side
+#define NTC_TEMP_MOTOR(beta) (1.0 / ((logf(NTC_RES_MOTOR(ADC_Value[ADC_IND_TEMP_MOTOR]) / 10000.0) / beta) + (1.0 / 298.15)) - 273.15)
+
+// Voltage on ADC channel
+#define ADC_VOLTS(ch) ((float)ADC_Value[ch] / 4096.0 * V_REG)
+
+// Double samples in beginning and end for positive current measurement.
+// Useful when the shunt sense traces have noise that causes offset.
+#define CURR1_DOUBLE_SAMPLE 0
+#define CURR2_DOUBLE_SAMPLE 0
+#define CURR3_DOUBLE_SAMPLE 0
+
+// COMM-port ADC GPIOs
+#define HW_ADC_EXT_GPIO GPIOA
+#define HW_ADC_EXT_PIN 5
+#define HW_ADC_EXT2_GPIO GPIOA
+#define HW_ADC_EXT2_PIN 6
+
+// UART Peripheral
+#define HW_UART_DEV SD3
+#define HW_UART_GPIO_AF GPIO_AF_USART3
+#define HW_UART_TX_PORT GPIOB
+#define HW_UART_TX_PIN 10
+#define HW_UART_RX_PORT GPIOB
+#define HW_UART_RX_PIN 11
+
+// Permanent UART Peripheral (for NRF51)
+#define HW_UART_P_BAUD 115200
+#define HW_UART_P_DEV SD4
+#define HW_UART_P_GPIO_AF GPIO_AF_UART4
+#define HW_UART_P_TX_PORT GPIOC
+#define HW_UART_P_TX_PIN 10
+#define HW_UART_P_RX_PORT GPIOC
+#define HW_UART_P_RX_PIN 11
+
+// ICU Peripheral for servo decoding
+#define HW_USE_SERVO_TIM4
+#define HW_ICU_TIMER TIM4
+#define HW_ICU_TIM_CLK_EN() RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE)
+#define HW_ICU_DEV ICUD4
+#define HW_ICU_CHANNEL ICU_CHANNEL_1
+#define HW_ICU_GPIO_AF GPIO_AF_TIM4
+#define HW_ICU_GPIO GPIOB
+#define HW_ICU_PIN 6
+
+// I2C Peripheral
+#define HW_I2C_DEV I2CD2
+#define HW_I2C_GPIO_AF GPIO_AF_I2C2
+#define HW_I2C_SCL_PORT GPIOB
+#define HW_I2C_SCL_PIN 10
+#define HW_I2C_SDA_PORT GPIOB
+#define HW_I2C_SDA_PIN 11
+
+// Hall/encoder pins
+#define HW_HALL_ENC_GPIO1 GPIOC
+#define HW_HALL_ENC_PIN1 6
+#define HW_HALL_ENC_GPIO2 GPIOC
+#define HW_HALL_ENC_PIN2 7
+#define HW_HALL_ENC_GPIO3 GPIOC
+#define HW_HALL_ENC_PIN3 8
+#define HW_ENC_TIM TIM3
+#define HW_ENC_TIM_AF GPIO_AF_TIM3
+#define HW_ENC_TIM_CLK_EN() RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE)
+#define HW_ENC_EXTI_PORTSRC EXTI_PortSourceGPIOC
+#define HW_ENC_EXTI_PINSRC EXTI_PinSource8
+#define HW_ENC_EXTI_CH EXTI9_5_IRQn
+#define HW_ENC_EXTI_LINE EXTI_Line8
+#define HW_ENC_EXTI_ISR_VEC EXTI9_5_IRQHandler
+#define HW_ENC_TIM_ISR_CH TIM3_IRQn
+#define HW_ENC_TIM_ISR_VEC TIM3_IRQHandler
+
+// SPI pins
+#define HW_SPI_DEV SPID1
+#define HW_SPI_GPIO_AF GPIO_AF_SPI1
+#define HW_SPI_PORT_NSS GPIOA
+#define HW_SPI_PIN_NSS 4
+#define HW_SPI_PORT_SCK GPIOA
+#define HW_SPI_PIN_SCK 5
+#define HW_SPI_PORT_MOSI GPIOA
+#define HW_SPI_PIN_MOSI 7
+#define HW_SPI_PORT_MISO GPIOA
+#define HW_SPI_PIN_MISO 6
+
+// Measurement macros
+#define ADC_V_L1 ADC_Value[ADC_IND_SENS1]
+#define ADC_V_L2 ADC_Value[ADC_IND_SENS2]
+#define ADC_V_L3 ADC_Value[ADC_IND_SENS3]
+#define ADC_V_ZERO (ADC_Value[ADC_IND_VIN_SENS] / 2)
+
+// Macros
+#define READ_HALL1() palReadPad(HW_HALL_ENC_GPIO1, HW_HALL_ENC_PIN1)
+#define READ_HALL2() palReadPad(HW_HALL_ENC_GPIO2, HW_HALL_ENC_PIN2)
+#define READ_HALL3() palReadPad(HW_HALL_ENC_GPIO3, HW_HALL_ENC_PIN3)
+
+// Setting limits
+#define HW_LIM_CURRENT -300.0, 300.0
+#define HW_LIM_CURRENT_IN -280.0, 280.0
+#define HW_LIM_CURRENT_ABS 0.0, 320
+#define HW_LIM_VIN 12.0, 75.0
+#define HW_LIM_ERPM -200e3, 200e3
+#define HW_LIM_DUTY_MIN 0.0, 0.1
+#define HW_LIM_DUTY_MAX 0.0, 0.99
+#define HW_LIM_TEMP_FET -40.0, 100.0
+
+// HW-specific functions
+bool hw_sample_shutdown_button(void);
+#endif /* HW_IVY_80V_HP_H_ */
diff --git a/hwconf/IVY_Esk8/IVY_80V_HP/hw_IVY_80V_HP_no_limits.h b/hwconf/IVY_Esk8/IVY_80V_HP/hw_IVY_80V_HP_no_limits.h
new file mode 100644
index 0000000000..bad3b6423f
--- /dev/null
+++ b/hwconf/IVY_Esk8/IVY_80V_HP/hw_IVY_80V_HP_no_limits.h
@@ -0,0 +1,27 @@
+/*
+ Copyright 2018 Benjamin Vedder benjamin@vedder.se
+
+ This file is part of the VESC firmware.
+
+ The VESC firmware 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.
+
+ The VESC firmware 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 .
+*/
+
+#ifndef HW_IVY_80V_HP_NO_LIMITS_H_
+#define HW_IVY_80V_HP_NO_LIMITS_H_
+
+#define DISABLE_HW_LIMITS
+
+#include "hw_IVY_80V_HP.h"
+
+#endif /* HW_IVY_80V_HP_NO_LIMITS_H_ */
diff --git a/hwconf/IVY_Esk8/IVY_80V_MINI/hw_IVY_80V_MINI.c b/hwconf/IVY_Esk8/IVY_80V_MINI/hw_IVY_80V_MINI.c
new file mode 100644
index 0000000000..2e07dc0288
--- /dev/null
+++ b/hwconf/IVY_Esk8/IVY_80V_MINI/hw_IVY_80V_MINI.c
@@ -0,0 +1,270 @@
+/*
+ Copyright 2018 Benjamin Vedder benjamin@vedder.se
+ 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 .
+ */
+
+// V2
+
+#include "hw.h"
+#include "hw_IVY_80V_MINI.h"
+
+#include "ch.h"
+#include "hal.h"
+#include "stm32f4xx_conf.h"
+#include "utils.h"
+#include
+#include "mc_interface.h"
+
+// Variables
+static volatile bool i2c_running = false;
+static mutex_t shutdown_mutex;
+static float bt_diff = 0.0;
+
+// I2C configuration
+static const I2CConfig i2cfg = {
+ OPMODE_I2C,
+ 100000,
+ STD_DUTY_CYCLE};
+
+void hw_init_gpio(void)
+{
+ chMtxObjectInit(&shutdown_mutex);
+ // GPIO clock enable
+ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
+ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
+ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
+ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
+
+ // LEDs
+ palSetPadMode(LED_GREEN_GPIO, LED_GREEN_PIN,
+ PAL_MODE_OUTPUT_PUSHPULL |
+ PAL_STM32_OSPEED_HIGHEST);
+ palSetPadMode(LED_RED_GPIO, LED_RED_PIN,
+ PAL_MODE_OUTPUT_PUSHPULL |
+ PAL_STM32_OSPEED_HIGHEST);
+
+ // GPIOA Configuration: Channel 1 to 3 as alternate function push-pull
+ palSetPadMode(GPIOA, 8, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING);
+ palSetPadMode(GPIOA, 9, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING);
+ palSetPadMode(GPIOA, 10, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING);
+
+ palSetPadMode(GPIOB, 13, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING);
+ palSetPadMode(GPIOB, 14, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING);
+ palSetPadMode(GPIOB, 15, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING);
+
+ // Hall sensors
+ palSetPadMode(HW_HALL_ENC_GPIO1, HW_HALL_ENC_PIN1, PAL_MODE_INPUT_PULLUP);
+ palSetPadMode(HW_HALL_ENC_GPIO2, HW_HALL_ENC_PIN2, PAL_MODE_INPUT_PULLUP);
+ palSetPadMode(HW_HALL_ENC_GPIO3, HW_HALL_ENC_PIN3, PAL_MODE_INPUT_PULLUP);
+
+#ifdef HW_HAS_PHASE_FILTERS
+ // Phase filters
+ palSetPadMode(PHASE_FILTER_GPIO, PHASE_FILTER_PIN,
+ PAL_MODE_OUTPUT_PUSHPULL |
+ PAL_STM32_OSPEED_HIGHEST);
+ PHASE_FILTER_OFF();
+#endif
+
+#ifdef HW_HAS_CURR_FILTERS
+ // Current filter
+ palSetPadMode(GPIOD, 2,
+ PAL_MODE_OUTPUT_PUSHPULL |
+ PAL_STM32_OSPEED_HIGHEST);
+
+ CURRENT_FILTER_OFF();
+#endif
+
+ // AUX pin
+ // AUX_OFF();
+ // palSetPadMode(AUX_GPIO, AUX_PIN,
+ // PAL_MODE_OUTPUT_PUSHPULL |
+ // PAL_STM32_OSPEED_HIGHEST);
+
+ // ADC Pins
+ palSetPadMode(GPIOA, 0, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOA, 1, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOA, 2, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOA, 3, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOA, 5, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOA, 6, PAL_MODE_INPUT_ANALOG);
+
+ palSetPadMode(GPIOB, 0, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOB, 1, PAL_MODE_INPUT_ANALOG);
+
+ palSetPadMode(GPIOC, 0, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOC, 1, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOC, 2, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOC, 3, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOC, 4, PAL_MODE_INPUT_ANALOG);
+}
+
+void hw_setup_adc_channels(void)
+{
+ // ADC1 regular channels
+ ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 2, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 3, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 4, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC1, ADC_Channel_Vrefint, 5, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 6, ADC_SampleTime_15Cycles);
+
+ // ADC2 regular channels
+ ADC_RegularChannelConfig(ADC2, ADC_Channel_1, 1, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC2, ADC_Channel_11, 2, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC2, ADC_Channel_6, 3, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC2, ADC_Channel_15, 4, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC2, ADC_Channel_0, 5, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC2, ADC_Channel_9, 6, ADC_SampleTime_15Cycles);
+
+ // ADC3 regular channels
+ ADC_RegularChannelConfig(ADC3, ADC_Channel_2, 1, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC3, ADC_Channel_12, 2, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC3, ADC_Channel_3, 3, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC3, ADC_Channel_13, 4, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC3, ADC_Channel_1, 5, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC3, ADC_Channel_2, 6, ADC_SampleTime_15Cycles);
+
+ // Injected channels
+ ADC_InjectedChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC2, ADC_Channel_11, 1, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC3, ADC_Channel_12, 1, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC1, ADC_Channel_10, 2, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC2, ADC_Channel_11, 2, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC3, ADC_Channel_12, 2, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC1, ADC_Channel_10, 3, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC2, ADC_Channel_11, 3, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC3, ADC_Channel_12, 3, ADC_SampleTime_15Cycles);
+}
+
+void hw_start_i2c(void)
+{
+ i2cAcquireBus(&HW_I2C_DEV);
+
+ if (!i2c_running)
+ {
+ palSetPadMode(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN,
+ PAL_MODE_ALTERNATE(HW_I2C_GPIO_AF) |
+ PAL_STM32_OTYPE_OPENDRAIN |
+ PAL_STM32_OSPEED_MID1 |
+ PAL_STM32_PUDR_PULLUP);
+ palSetPadMode(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN,
+ PAL_MODE_ALTERNATE(HW_I2C_GPIO_AF) |
+ PAL_STM32_OTYPE_OPENDRAIN |
+ PAL_STM32_OSPEED_MID1 |
+ PAL_STM32_PUDR_PULLUP);
+
+ i2cStart(&HW_I2C_DEV, &i2cfg);
+ i2c_running = true;
+ }
+
+ i2cReleaseBus(&HW_I2C_DEV);
+}
+
+void hw_stop_i2c(void)
+{
+ i2cAcquireBus(&HW_I2C_DEV);
+
+ if (i2c_running)
+ {
+ palSetPadMode(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN, PAL_MODE_INPUT);
+ palSetPadMode(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN, PAL_MODE_INPUT);
+
+ i2cStop(&HW_I2C_DEV);
+ i2c_running = false;
+ }
+
+ i2cReleaseBus(&HW_I2C_DEV);
+}
+
+/**
+ * Try to restore the i2c bus
+ */
+void hw_try_restore_i2c(void)
+{
+ if (i2c_running)
+ {
+ i2cAcquireBus(&HW_I2C_DEV);
+
+ palSetPadMode(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN,
+ PAL_STM32_OTYPE_OPENDRAIN |
+ PAL_STM32_OSPEED_MID1 |
+ PAL_STM32_PUDR_PULLUP);
+
+ palSetPadMode(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN,
+ PAL_STM32_OTYPE_OPENDRAIN |
+ PAL_STM32_OSPEED_MID1 |
+ PAL_STM32_PUDR_PULLUP);
+
+ palSetPad(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN);
+ palSetPad(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN);
+
+ chThdSleep(1);
+
+ for (int i = 0; i < 16; i++)
+ {
+ palClearPad(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN);
+ chThdSleep(1);
+ palSetPad(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN);
+ chThdSleep(1);
+ }
+
+ // Generate start then stop condition
+ palClearPad(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN);
+ chThdSleep(1);
+ palClearPad(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN);
+ chThdSleep(1);
+ palSetPad(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN);
+ chThdSleep(1);
+ palSetPad(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN);
+
+ palSetPadMode(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN,
+ PAL_MODE_ALTERNATE(HW_I2C_GPIO_AF) |
+ PAL_STM32_OTYPE_OPENDRAIN |
+ PAL_STM32_OSPEED_MID1 |
+ PAL_STM32_PUDR_PULLUP);
+
+ palSetPadMode(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN,
+ PAL_MODE_ALTERNATE(HW_I2C_GPIO_AF) |
+ PAL_STM32_OTYPE_OPENDRAIN |
+ PAL_STM32_OSPEED_MID1 |
+ PAL_STM32_PUDR_PULLUP);
+
+ HW_I2C_DEV.state = I2C_STOP;
+ i2cStart(&HW_I2C_DEV, &i2cfg);
+
+ i2cReleaseBus(&HW_I2C_DEV);
+ }
+}
+
+bool hw_sample_shutdown_button(void)
+{
+ chMtxLock(&shutdown_mutex);
+
+ bt_diff = 0.0;
+
+ for (int i = 0; i < 3; i++)
+ {
+ palSetPadMode(HW_SHUTDOWN_GPIO, HW_SHUTDOWN_PIN, PAL_MODE_INPUT_ANALOG);
+ chThdSleep(5);
+ float val1 = ADC_VOLTS(ADC_IND_SHUTDOWN);
+ chThdSleepMilliseconds(1);
+ float val2 = ADC_VOLTS(ADC_IND_SHUTDOWN);
+ palSetPadMode(HW_SHUTDOWN_GPIO, HW_SHUTDOWN_PIN, PAL_MODE_OUTPUT_PUSHPULL);
+ chThdSleepMilliseconds(1);
+
+ bt_diff += (val1 - val2);
+ }
+
+ chMtxUnlock(&shutdown_mutex);
+
+ return (bt_diff > 0.12);
+}
diff --git a/hwconf/IVY_Esk8/IVY_80V_MINI/hw_IVY_80V_MINI.h b/hwconf/IVY_Esk8/IVY_80V_MINI/hw_IVY_80V_MINI.h
new file mode 100644
index 0000000000..f9938fb274
--- /dev/null
+++ b/hwconf/IVY_Esk8/IVY_80V_MINI/hw_IVY_80V_MINI.h
@@ -0,0 +1,282 @@
+/*
+ Copyright 2018 Benjamin Vedder benjamin@vedder.se
+
+ This file is part of the VESC firmware.
+
+ The VESC firmware 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.
+
+ The VESC firmware 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 .
+*/
+
+#ifndef HW_IVY_80V_MINI_H_
+#define HW_IVY_80V_MINI_H_
+
+#define HW_NAME "IVY_80V_MINI"
+
+// Default setting overrides
+#define APPCONF_APP_TO_USE APP_NONE
+// #define APPCONF_CONTROLLER_ID 2
+#define APPCONF_TIMEOUT_MSEC 500
+#define APPCONF_TIMEOUT_BRAKE_CURRENT 0
+#define APPCONF_PERMANENT_UART_ENABLED false
+#define APPCONF_SHUTDOWN_MODE SHUTDOWN_MODE_ALWAYS_ON
+#define APPCONF_CAN_BAUD_RATE CAN_BAUD_1M
+#define APPCONF_CAN_STATUS_RATE_1 100
+#define APPCONF_CAN_STATUS_RATE_2 5
+#define APPCONF_CAN_STATUS_MSGS_R1 0x09
+#define APPCONF_CAN_STATUS_MSGS_R2 0x10
+// Common PID-parameters
+#define MCCONF_SP_PID_LOOP_RATE PID_RATE_5000_HZ // PID loop rate
+// Speed PID parameters
+#define MCCONF_S_PID_KP 0.003 // Proportional gain
+#define MCCONF_S_PID_KI 0.009 // Integral gain
+#define MCCONF_S_PID_KD 0.00002 // Derivative gain
+#define MCCONF_S_PID_KD_FILTER 0.2 // Derivative filter
+#define MCCONF_S_PID_MIN_RPM 300.0 // Minimum allowed RPM
+#define MCCONF_S_PID_ALLOW_BRAKING true // Allow braking in speed control mode
+#define MCCONF_S_PID_RAMP_ERPMS_S 2000.0 // Speed input ramping, in ERPM/s
+#define MCCONF_S_PID_SPEED_SOURCE S_PID_SPEED_SRC_PLL
+// Position PID parameters
+#define MCCONF_P_PID_KP 0.03 // Proportional gain
+#define MCCONF_P_PID_KI 0.0 // Integral gain
+#define MCCONF_P_PID_KD 0.0 // Derivative gain
+#define MCCONF_P_PID_KD_PROC 0.00035 // Derivative gain process
+#define MCCONF_P_PID_KD_FILTER 0.2 // Derivative filter
+#define MCCONF_P_PID_ANG_DIV 10.0 // Divide angle by this value
+#define MCCONF_P_PID_GAIN_DEC_ANGLE 500.0 // Decrease PID-gains when the error is below this value
+#define MCCONF_P_PID_OFFSET 0.0 // Angle offset
+
+#define MCCONF_L_MIN_VOLTAGE 20.0 // Minimum input voltage
+#define MCCONF_L_MAX_VOLTAGE 87.0 // Maximum input voltage
+#define MCCONF_DEFAULT_MOTOR_TYPE MOTOR_TYPE_FOC
+
+#define MCCONF_L_MAX_ABS_CURRENT 160.0 // The maximum absolute current above which a fault is generated
+#define MCCONF_L_IN_CURRENT_MAX 110.0 // Input current limit in Amperes (Upper)
+#define MCCONF_L_IN_CURRENT_MIN -110.0 // Input current limit in Amperes (Lower)
+
+#define MCCONF_FOC_F_ZV 30000.0
+#define MCCONF_FOC_CONTROL_SAMPLE_MODE FOC_CONTROL_SAMPLE_MODE_V0_V7
+#define MCCONF_FOC_SAMPLE_V0_V7 true // Run control loop in both v0 and v7 (requires phase shunts)
+
+// Override dead time. See the stm32f4 reference manual for calculating this value.
+#define HW_DEAD_TIME_NSEC 600.0
+
+// HW properties
+#define HW_HAS_3_SHUNTS
+// #define HW_HAS_PHASE_SHUNTS
+// #define HW_HAS_PHASE_FILTERS
+// #define HW_HAS_CURR_FILTERS
+
+// Macros
+#define LED_GREEN_GPIO GPIOB
+#define LED_GREEN_PIN 5
+#define LED_RED_GPIO GPIOB
+#define LED_RED_PIN 7
+
+#define LED_GREEN_ON() palSetPad(LED_GREEN_GPIO, LED_GREEN_PIN)
+#define LED_GREEN_OFF() palClearPad(LED_GREEN_GPIO, LED_GREEN_PIN)
+#define LED_RED_ON() palSetPad(LED_RED_GPIO, LED_RED_PIN)
+#define LED_RED_OFF() palClearPad(LED_RED_GPIO, LED_RED_PIN)
+
+#ifdef HW_HAS_PHASE_FILTERS
+#define PHASE_FILTER_GPIO GPIOC
+#define PHASE_FILTER_PIN 9
+#define PHASE_FILTER_ON() palSetPad(PHASE_FILTER_GPIO, PHASE_FILTER_PIN)
+#define PHASE_FILTER_OFF() palClearPad(PHASE_FILTER_GPIO, PHASE_FILTER_PIN)
+#endif
+
+// Shutdown pin
+#define HW_SHUTDOWN_GPIO GPIOC
+#define HW_SHUTDOWN_PIN 5
+#define HW_SHUTDOWN_HOLD_ON() palSetPad(HW_SHUTDOWN_GPIO, HW_SHUTDOWN_PIN)
+#define HW_SHUTDOWN_HOLD_OFF() palClearPad(HW_SHUTDOWN_GPIO, HW_SHUTDOWN_PIN)
+#define HW_SAMPLE_SHUTDOWN() hw_sample_shutdown_button()
+
+// Hold shutdown pin early to wake up on short pulses
+#define HW_EARLY_INIT() \
+ palSetPadMode(HW_SHUTDOWN_GPIO, HW_SHUTDOWN_PIN, PAL_MODE_OUTPUT_PUSHPULL); \
+ HW_SHUTDOWN_HOLD_ON();
+
+#ifdef HW_HAS_CURR_FILTERS
+#define CURRENT_FILTER_ON() palSetPad(GPIOD, 2)
+#define CURRENT_FILTER_OFF() palClearPad(GPIOD, 2)
+#endif
+
+/*
+ * ADC Vector
+ *
+ * 0 (1): IN0 SENS1
+ * 1 (2): IN1 SENS2
+ * 2 (3): IN2 SENS3
+ * 3 (1): IN10 CURR1
+ * 4 (2): IN11 CURR2
+ * 5 (3): IN12 CURR3
+ * 6 (1): IN5 ADC_EXT1
+ * 7 (2): IN6 ADC_EXT2
+ * 8 (3): IN3 TEMP_MOS
+ * 9 (1): IN14 TEMP_MOTOR
+ * 10 (2): IN15 SHUTDOWN
+ * 11 (3): IN13 AN_IN
+ * 12 (1): Vrefint
+ * 13 (2): IN0 SENS1
+ * 14 (3): IN1 SENS2
+ * 15 (1): IN8 TEMP_MOS_2
+ * 16 (2): IN9 TEMP_MOS_3
+ * 17 (3): IN3 SENS3
+ */
+
+#define HW_ADC_CHANNELS 18
+#define HW_ADC_INJ_CHANNELS 3
+#define HW_ADC_NBR_CONV 6
+
+// ADC Indexes
+#define ADC_IND_SENS1 0
+#define ADC_IND_SENS2 1
+#define ADC_IND_SENS3 2
+#define ADC_IND_CURR1 3
+#define ADC_IND_CURR2 4
+#define ADC_IND_CURR3 5
+#define ADC_IND_VIN_SENS 11
+#define ADC_IND_EXT 6
+#define ADC_IND_EXT2 7
+#define ADC_IND_SHUTDOWN 10
+#define ADC_IND_TEMP_MOS 8
+#define ADC_IND_TEMP_MOTOR 9
+#define ADC_IND_VREFINT 12
+
+// ADC macros and settings
+
+// Component parameters (can be overridden)
+#define V_REG 3.30
+
+// The voltage dividing acquisition circuit on the Makerbase VESC motherboard is 560K and 21.5K resistors.
+#define VIN_R1 560000.0
+#define VIN_R2 22000.0
+
+#define CURRENT_AMP_GAIN (-20.0)
+#define CURRENT_SHUNT_RES (0.0005)
+
+// Input voltage
+#define GET_INPUT_VOLTAGE() ((V_REG / 4095.0) * (float)ADC_Value[ADC_IND_VIN_SENS] * ((VIN_R1 + VIN_R2) / VIN_R2))
+
+// NTC Termistors
+// #define NTC_RES(adc_val) ((4095.0 * 10000.0) / adc_val - 10000.0)
+#define NTC_RES(adc_val) ((4095.0 * 10000.0) / adc_val - 10000.0)
+#define NTC_TEMP(adc_ind) (1.0 / ((logf(NTC_RES(ADC_Value[adc_ind]) / 10000.0) / 3435.0) + (1.0 / 298.15)) - 273.15)
+
+#define NTC_RES_MOTOR(adc_val) (10000.0 / ((4095.0 / (float)adc_val) - 1.0)) // Motor temp sensor on low side
+#define NTC_TEMP_MOTOR(beta) (1.0 / ((logf(NTC_RES_MOTOR(ADC_Value[ADC_IND_TEMP_MOTOR]) / 10000.0) / beta) + (1.0 / 298.15)) - 273.15)
+
+// Voltage on ADC channel
+#define ADC_VOLTS(ch) ((float)ADC_Value[ch] / 4096.0 * V_REG)
+
+// Double samples in beginning and end for positive current measurement.
+// Useful when the shunt sense traces have noise that causes offset.
+#define CURR1_DOUBLE_SAMPLE 0
+#define CURR2_DOUBLE_SAMPLE 0
+#define CURR3_DOUBLE_SAMPLE 0
+
+// COMM-port ADC GPIOs
+#define HW_ADC_EXT_GPIO GPIOA
+#define HW_ADC_EXT_PIN 5
+#define HW_ADC_EXT2_GPIO GPIOA
+#define HW_ADC_EXT2_PIN 6
+
+// UART Peripheral
+#define HW_UART_DEV SD3
+#define HW_UART_GPIO_AF GPIO_AF_USART3
+#define HW_UART_TX_PORT GPIOB
+#define HW_UART_TX_PIN 10
+#define HW_UART_RX_PORT GPIOB
+#define HW_UART_RX_PIN 11
+
+// Permanent UART Peripheral (for NRF51)
+#define HW_UART_P_BAUD 115200
+#define HW_UART_P_DEV SD4
+#define HW_UART_P_GPIO_AF GPIO_AF_UART4
+#define HW_UART_P_TX_PORT GPIOC
+#define HW_UART_P_TX_PIN 10
+#define HW_UART_P_RX_PORT GPIOC
+#define HW_UART_P_RX_PIN 11
+
+// ICU Peripheral for servo decoding
+#define HW_USE_SERVO_TIM4
+#define HW_ICU_TIMER TIM4
+#define HW_ICU_TIM_CLK_EN() RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE)
+#define HW_ICU_DEV ICUD4
+#define HW_ICU_CHANNEL ICU_CHANNEL_1
+#define HW_ICU_GPIO_AF GPIO_AF_TIM4
+#define HW_ICU_GPIO GPIOB
+#define HW_ICU_PIN 6
+
+// I2C Peripheral
+#define HW_I2C_DEV I2CD2
+#define HW_I2C_GPIO_AF GPIO_AF_I2C2
+#define HW_I2C_SCL_PORT GPIOB
+#define HW_I2C_SCL_PIN 10
+#define HW_I2C_SDA_PORT GPIOB
+#define HW_I2C_SDA_PIN 11
+
+// Hall/encoder pins
+#define HW_HALL_ENC_GPIO1 GPIOC
+#define HW_HALL_ENC_PIN1 6
+#define HW_HALL_ENC_GPIO2 GPIOC
+#define HW_HALL_ENC_PIN2 7
+#define HW_HALL_ENC_GPIO3 GPIOC
+#define HW_HALL_ENC_PIN3 8
+#define HW_ENC_TIM TIM3
+#define HW_ENC_TIM_AF GPIO_AF_TIM3
+#define HW_ENC_TIM_CLK_EN() RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE)
+#define HW_ENC_EXTI_PORTSRC EXTI_PortSourceGPIOC
+#define HW_ENC_EXTI_PINSRC EXTI_PinSource8
+#define HW_ENC_EXTI_CH EXTI9_5_IRQn
+#define HW_ENC_EXTI_LINE EXTI_Line8
+#define HW_ENC_EXTI_ISR_VEC EXTI9_5_IRQHandler
+#define HW_ENC_TIM_ISR_CH TIM3_IRQn
+#define HW_ENC_TIM_ISR_VEC TIM3_IRQHandler
+
+// SPI pins
+#define HW_SPI_DEV SPID1
+#define HW_SPI_GPIO_AF GPIO_AF_SPI1
+#define HW_SPI_PORT_NSS GPIOA
+#define HW_SPI_PIN_NSS 4
+#define HW_SPI_PORT_SCK GPIOA
+#define HW_SPI_PIN_SCK 5
+#define HW_SPI_PORT_MOSI GPIOA
+#define HW_SPI_PIN_MOSI 7
+#define HW_SPI_PORT_MISO GPIOA
+#define HW_SPI_PIN_MISO 6
+
+// Measurement macros
+#define ADC_V_L1 ADC_Value[ADC_IND_SENS1]
+#define ADC_V_L2 ADC_Value[ADC_IND_SENS2]
+#define ADC_V_L3 ADC_Value[ADC_IND_SENS3]
+#define ADC_V_ZERO (ADC_Value[ADC_IND_VIN_SENS] / 2)
+
+// Macros
+#define READ_HALL1() palReadPad(HW_HALL_ENC_GPIO1, HW_HALL_ENC_PIN1)
+#define READ_HALL2() palReadPad(HW_HALL_ENC_GPIO2, HW_HALL_ENC_PIN2)
+#define READ_HALL3() palReadPad(HW_HALL_ENC_GPIO3, HW_HALL_ENC_PIN3)
+
+// Setting limits
+#define HW_LIM_CURRENT -110.0, 110.0
+#define HW_LIM_CURRENT_IN -100.0, 100.0
+#define HW_LIM_CURRENT_ABS 0.0, 160
+#define HW_LIM_VIN 12.0, 87.0
+#define HW_LIM_ERPM -200e3, 200e3
+#define HW_LIM_DUTY_MIN 0.0, 0.1
+#define HW_LIM_DUTY_MAX 0.0, 0.99
+#define HW_LIM_TEMP_FET -40.0, 110.0
+
+// HW-specific functions
+bool hw_sample_shutdown_button(void);
+#endif /* HW_IVY_80V_MINI_H_ */
diff --git a/hwconf/IVY_Esk8/IVY_80V_MINI/hw_IVY_80V_MINI_no_limits.h b/hwconf/IVY_Esk8/IVY_80V_MINI/hw_IVY_80V_MINI_no_limits.h
new file mode 100644
index 0000000000..69174aa3c9
--- /dev/null
+++ b/hwconf/IVY_Esk8/IVY_80V_MINI/hw_IVY_80V_MINI_no_limits.h
@@ -0,0 +1,27 @@
+/*
+ Copyright 2018 Benjamin Vedder benjamin@vedder.se
+
+ This file is part of the VESC firmware.
+
+ The VESC firmware 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.
+
+ The VESC firmware 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 .
+*/
+
+#ifndef HW_IVY_80V_MINI_NO_LIMITS_H_
+#define HW_IVY_80V_MINI_NO_LIMITS_H_
+
+#define DISABLE_HW_LIMITS
+
+#include "hw_IVY_80V_MINI.h"
+
+#endif /* HW_IVY_80V_MINI_NO_LIMITS_H_ */
diff --git a/hwconf/IVY_Esk8/IVY_Test/hw_IVY_Test.c b/hwconf/IVY_Esk8/IVY_Test/hw_IVY_Test.c
new file mode 100644
index 0000000000..ee49e25ddd
--- /dev/null
+++ b/hwconf/IVY_Esk8/IVY_Test/hw_IVY_Test.c
@@ -0,0 +1,364 @@
+/*
+ Copyright 2018 Benjamin Vedder benjamin@vedder.se
+ 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 .
+ */
+
+// V2
+
+#include "hw.h"
+#include "hw_IVY_Test.h"
+
+#include "ch.h"
+#include "hal.h"
+#include "stm32f4xx_conf.h"
+#include "utils.h"
+#include
+#include "mc_interface.h"
+
+// Variables
+static volatile bool i2c_running = false;
+static mutex_t shutdown_mutex;
+static float bt_diff = 0.0;
+
+// I2C configuration
+static const I2CConfig i2cfg = {
+ OPMODE_I2C,
+ 100000,
+ STD_DUTY_CYCLE};
+
+void hw_init_gpio(void)
+{
+ chMtxObjectInit(&shutdown_mutex);
+ // GPIO clock enable
+ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
+ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
+ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
+ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
+
+ // LEDs
+ palSetPadMode(LED_GREEN_GPIO, LED_GREEN_PIN,
+ PAL_MODE_OUTPUT_PUSHPULL |
+ PAL_STM32_OSPEED_HIGHEST);
+ palSetPadMode(LED_RED_GPIO, LED_RED_PIN,
+ PAL_MODE_OUTPUT_PUSHPULL |
+ PAL_STM32_OSPEED_HIGHEST);
+
+ // GPIOA Configuration: Channel 1 to 3 as alternate function push-pull
+ palSetPadMode(GPIOA, 8, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING);
+ palSetPadMode(GPIOA, 9, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING);
+ palSetPadMode(GPIOA, 10, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING);
+
+ palSetPadMode(GPIOB, 13, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING);
+ palSetPadMode(GPIOB, 14, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING);
+ palSetPadMode(GPIOB, 15, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING);
+
+ // Hall sensors
+ palSetPadMode(HW_HALL_ENC_GPIO1, HW_HALL_ENC_PIN1, PAL_MODE_INPUT_PULLUP);
+ palSetPadMode(HW_HALL_ENC_GPIO2, HW_HALL_ENC_PIN2, PAL_MODE_INPUT_PULLUP);
+ palSetPadMode(HW_HALL_ENC_GPIO3, HW_HALL_ENC_PIN3, PAL_MODE_INPUT_PULLUP);
+
+#ifdef HW_HAS_PHASE_FILTERS
+ // Phase filters
+ palSetPadMode(PHASE_FILTER_GPIO, PHASE_FILTER_PIN,
+ PAL_MODE_OUTPUT_PUSHPULL |
+ PAL_STM32_OSPEED_HIGHEST);
+ PHASE_FILTER_OFF();
+#endif
+
+#ifdef HW_HAS_CURR_FILTERS
+ // Current filter
+ palSetPadMode(GPIOD, 2,
+ PAL_MODE_OUTPUT_PUSHPULL |
+ PAL_STM32_OSPEED_HIGHEST);
+
+ CURRENT_FILTER_OFF();
+#endif
+
+ // AUX pin
+ // AUX_OFF();
+ // palSetPadMode(AUX_GPIO, AUX_PIN,
+ // PAL_MODE_OUTPUT_PUSHPULL |
+ // PAL_STM32_OSPEED_HIGHEST);
+
+ // ADC Pins
+ palSetPadMode(GPIOA, 0, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOA, 1, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOA, 2, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOA, 3, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOA, 5, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOA, 6, PAL_MODE_INPUT_ANALOG);
+
+ palSetPadMode(GPIOB, 0, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOB, 1, PAL_MODE_INPUT_ANALOG);
+
+ palSetPadMode(GPIOC, 0, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOC, 1, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOC, 2, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOC, 3, PAL_MODE_INPUT_ANALOG);
+ palSetPadMode(GPIOC, 4, PAL_MODE_INPUT_ANALOG);
+}
+
+void hw_setup_adc_channels(void)
+{
+ // ADC1 regular channels
+ ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 2, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 3, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 4, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC1, ADC_Channel_Vrefint, 5, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 6, ADC_SampleTime_15Cycles);
+
+ // ADC2 regular channels
+ ADC_RegularChannelConfig(ADC2, ADC_Channel_1, 1, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC2, ADC_Channel_11, 2, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC2, ADC_Channel_6, 3, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC2, ADC_Channel_15, 4, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC2, ADC_Channel_0, 5, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC2, ADC_Channel_9, 6, ADC_SampleTime_15Cycles);
+
+ // ADC3 regular channels
+ ADC_RegularChannelConfig(ADC3, ADC_Channel_2, 1, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC3, ADC_Channel_12, 2, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC3, ADC_Channel_3, 3, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC3, ADC_Channel_13, 4, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC3, ADC_Channel_1, 5, ADC_SampleTime_15Cycles);
+ ADC_RegularChannelConfig(ADC3, ADC_Channel_2, 6, ADC_SampleTime_15Cycles);
+
+ // Injected channels
+ ADC_InjectedChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC2, ADC_Channel_11, 1, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC3, ADC_Channel_12, 1, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC1, ADC_Channel_10, 2, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC2, ADC_Channel_11, 2, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC3, ADC_Channel_12, 2, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC1, ADC_Channel_10, 3, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC2, ADC_Channel_11, 3, ADC_SampleTime_15Cycles);
+ ADC_InjectedChannelConfig(ADC3, ADC_Channel_12, 3, ADC_SampleTime_15Cycles);
+}
+
+void hw_start_i2c(void)
+{
+ i2cAcquireBus(&HW_I2C_DEV);
+
+ if (!i2c_running)
+ {
+ palSetPadMode(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN,
+ PAL_MODE_ALTERNATE(HW_I2C_GPIO_AF) |
+ PAL_STM32_OTYPE_OPENDRAIN |
+ PAL_STM32_OSPEED_MID1 |
+ PAL_STM32_PUDR_PULLUP);
+ palSetPadMode(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN,
+ PAL_MODE_ALTERNATE(HW_I2C_GPIO_AF) |
+ PAL_STM32_OTYPE_OPENDRAIN |
+ PAL_STM32_OSPEED_MID1 |
+ PAL_STM32_PUDR_PULLUP);
+
+ i2cStart(&HW_I2C_DEV, &i2cfg);
+ i2c_running = true;
+ }
+
+ i2cReleaseBus(&HW_I2C_DEV);
+}
+
+void hw_stop_i2c(void)
+{
+ i2cAcquireBus(&HW_I2C_DEV);
+
+ if (i2c_running)
+ {
+ palSetPadMode(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN, PAL_MODE_INPUT);
+ palSetPadMode(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN, PAL_MODE_INPUT);
+
+ i2cStop(&HW_I2C_DEV);
+ i2c_running = false;
+ }
+
+ i2cReleaseBus(&HW_I2C_DEV);
+}
+
+/**
+ * Try to restore the i2c bus
+ */
+void hw_try_restore_i2c(void)
+{
+ if (i2c_running)
+ {
+ i2cAcquireBus(&HW_I2C_DEV);
+
+ palSetPadMode(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN,
+ PAL_STM32_OTYPE_OPENDRAIN |
+ PAL_STM32_OSPEED_MID1 |
+ PAL_STM32_PUDR_PULLUP);
+
+ palSetPadMode(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN,
+ PAL_STM32_OTYPE_OPENDRAIN |
+ PAL_STM32_OSPEED_MID1 |
+ PAL_STM32_PUDR_PULLUP);
+
+ palSetPad(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN);
+ palSetPad(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN);
+
+ chThdSleep(1);
+
+ for (int i = 0; i < 16; i++)
+ {
+ palClearPad(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN);
+ chThdSleep(1);
+ palSetPad(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN);
+ chThdSleep(1);
+ }
+
+ // Generate start then stop condition
+ palClearPad(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN);
+ chThdSleep(1);
+ palClearPad(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN);
+ chThdSleep(1);
+ palSetPad(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN);
+ chThdSleep(1);
+ palSetPad(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN);
+
+ palSetPadMode(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN,
+ PAL_MODE_ALTERNATE(HW_I2C_GPIO_AF) |
+ PAL_STM32_OTYPE_OPENDRAIN |
+ PAL_STM32_OSPEED_MID1 |
+ PAL_STM32_PUDR_PULLUP);
+
+ palSetPadMode(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN,
+ PAL_MODE_ALTERNATE(HW_I2C_GPIO_AF) |
+ PAL_STM32_OTYPE_OPENDRAIN |
+ PAL_STM32_OSPEED_MID1 |
+ PAL_STM32_PUDR_PULLUP);
+
+ HW_I2C_DEV.state = I2C_STOP;
+ i2cStart(&HW_I2C_DEV, &i2cfg);
+
+ i2cReleaseBus(&HW_I2C_DEV);
+ }
+}
+
+#ifdef HW_HAS_SHUTDOWN_SWITCH
+bool hw_sample_shutdown_button(void)
+{
+ chMtxLock(&shutdown_mutex);
+
+ bt_diff = 0.0;
+
+ for (int i = 0; i < 3; i++)
+ {
+ palSetPadMode(HW_SHUTDOWN_GPIO, HW_SHUTDOWN_PIN, PAL_MODE_INPUT_ANALOG);
+ chThdSleep(5);
+ float val1 = ADC_VOLTS(ADC_IND_SHUTDOWN);
+ chThdSleepMilliseconds(1);
+ float val2 = ADC_VOLTS(ADC_IND_SHUTDOWN);
+ palSetPadMode(HW_SHUTDOWN_GPIO, HW_SHUTDOWN_PIN, PAL_MODE_OUTPUT_PUSHPULL);
+ chThdSleepMilliseconds(1);
+
+ bt_diff += (val1 - val2);
+ }
+
+ chMtxUnlock(&shutdown_mutex);
+
+ return (bt_diff > 0.12);
+}
+#elif defined ADC_IND_IN_CURR
+
+#define IN_CURRENT_SHUNT_RES (0.0005)
+#define IN_CURRENT_SHUNT_GAIN (20.0)
+
+typedef struct KFP_t
+{
+ double LastP; // 上次估算协方差 初始化值为0.02
+ double Now_P; // 当前估算协方差 初始化值为0
+ double out; // 卡尔曼滤波器输出 初始化值为0
+ double Kg; // 卡尔曼增益 初始化值为0
+ double Q; // 过程噪声协方差 初始化值为0.001,越大越信任输入
+ double R; // 观测噪声协方差 初始化值为0.543,越大收敛越慢
+} KFP; // Kalman Filter parameter
+
+static volatile KFP input_current_KFP = {
+ .LastP = 0.02,
+ .Now_P = 0.0,
+ .out = 0.0,
+ .Kg = 0.0,
+ .Q = 0.0005,
+ .R = 0.6};
+
+/**
+ *卡尔曼滤波器
+ *@param KFP *kfp 卡尔曼结构体参数
+ * float input 需要滤波的参数的测量值(即传感器的采集值)
+ *@return 滤波后的参数(最优值)
+ */
+float kalmanFilter(KFP *kfp, double input)
+{
+ if (isnan(input))
+ return input;
+ // 预测协方差方程:k时刻系统估算协方差 = k-1时刻的系统协方差 + 过程噪声协方差
+ kfp->Now_P = kfp->LastP + kfp->Q;
+ // 卡尔曼增益方程:卡尔曼增益 = k时刻系统估算协方差 / (k时刻系统估算协方差 + 观测噪声协方差)
+ kfp->Kg = kfp->Now_P / (kfp->Now_P + kfp->R);
+ if (isnan(kfp->Kg))
+ kfp->Kg = 0;
+ // 更新最优值方程:k时刻状态变量的最优值 = 状态变量的预测值 + 卡尔曼增益 * (测量值 - 状态变量的预测值)
+ kfp->out = kfp->out + kfp->Kg * (input - kfp->out); // 因为这一次的预测值就是上一次的输出值
+ // 更新协方差方程: 本次的系统协方差付给 kfp->LastP 为下一次运算准备。
+ kfp->LastP = (1 - kfp->Kg) * kfp->Now_P;
+ return kfp->out;
+}
+
+float hw_read_input_current(void)
+{
+ return (V_REG / 4095.0) * 1000 * ADC_Value[ADC_IND_IN_CURR];
+#ifdef CALIB_IN_CURR_OFFSET
+ return (V_REG / 4095.0) *
+ kalmanFilter(&input_current_KFP, (ADC_Value[ADC_IND_IN_CURR] - input_current_sensor_offset)) /
+ IN_CURRENT_SHUNT_GAIN / IN_CURRENT_SHUNT_RES;
+#else
+ return (V_REG / 4095.0) *
+ kalmanFilter(&input_current_KFP, (ADC_Value[ADC_IND_IN_CURR] - 4096 / 2)) /
+ IN_CURRENT_SHUNT_GAIN / IN_CURRENT_SHUNT_RES;
+#endif
+}
+
+#ifdef CALIB_IN_CURR_OFFSET
+static volatile float input_current_sensor_offset = 1.65;
+static volatile int16_t input_current_sensor_offset_samples = -1;
+static volatile uint32_t input_current_sensor_offset_sum = 0;
+void hw_get_input_current_offset(void)
+{
+
+ if (input_current_sensor_offset_samples > -1)
+ {
+
+ if (input_current_sensor_offset_samples >= 1000)
+ {
+ input_current_sensor_offset = (((float)input_current_sensor_offset_sum) /
+ input_current_sensor_offset_samples);
+ // Reset the sum and samples
+ input_current_sensor_offset_sum = 0;
+ input_current_sensor_offset_samples = -1;
+ }
+ else
+ {
+ input_current_sensor_offset_sum += ADC_Value[ADC_IND_IN_CURR];
+ input_current_sensor_offset_samples++;
+ }
+ }
+}
+
+void hw_start_input_current_sensor_offset_measurement(void)
+{
+ input_current_sensor_offset_samples = 0;
+ input_current_sensor_offset_sum = 0;
+}
+#endif
+#endif
\ No newline at end of file
diff --git a/hwconf/IVY_Esk8/IVY_Test/hw_IVY_Test.h b/hwconf/IVY_Esk8/IVY_Test/hw_IVY_Test.h
new file mode 100644
index 0000000000..5312c3143e
--- /dev/null
+++ b/hwconf/IVY_Esk8/IVY_Test/hw_IVY_Test.h
@@ -0,0 +1,314 @@
+/*
+ Copyright 2018 Benjamin Vedder benjamin@vedder.se
+
+ This file is part of the VESC firmware.
+
+ The VESC firmware 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.
+
+ The VESC firmware 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 .
+*/
+
+#ifndef HW_IVY_Test_H_
+
+#define HW_IVY_Test_H_
+
+#define HW_NAME "IVY_Test"
+
+// Default setting overrides
+#define APPCONF_APP_TO_USE APP_NONE
+// #define APPCONF_CONTROLLER_ID 2
+#define APPCONF_TIMEOUT_MSEC 500
+#define APPCONF_TIMEOUT_BRAKE_CURRENT 0
+#define APPCONF_PERMANENT_UART_ENABLED false
+#define APPCONF_SHUTDOWN_MODE SHUTDOWN_MODE_ALWAYS_ON
+#define APPCONF_CAN_BAUD_RATE CAN_BAUD_1M
+#define APPCONF_CAN_STATUS_RATE_1 100
+#define APPCONF_CAN_STATUS_RATE_2 5
+#define APPCONF_CAN_STATUS_MSGS_R1 0x09
+#define APPCONF_CAN_STATUS_MSGS_R2 0x10
+// Common PID-parameters
+#define MCCONF_SP_PID_LOOP_RATE PID_RATE_5000_HZ // PID loop rate
+// Speed PID parameters
+#define MCCONF_S_PID_KP 0.003 // Proportional gain
+#define MCCONF_S_PID_KI 0.009 // Integral gain
+#define MCCONF_S_PID_KD 0.00002 // Derivative gain
+#define MCCONF_S_PID_KD_FILTER 0.2 // Derivative filter
+#define MCCONF_S_PID_MIN_RPM 300.0 // Minimum allowed RPM
+#define MCCONF_S_PID_ALLOW_BRAKING true // Allow braking in speed control mode
+#define MCCONF_S_PID_RAMP_ERPMS_S 2000.0 // Speed input ramping, in ERPM/s
+#define MCCONF_S_PID_SPEED_SOURCE S_PID_SPEED_SRC_PLL
+// Position PID parameters
+#define MCCONF_P_PID_KP 0.03 // Proportional gain
+#define MCCONF_P_PID_KI 0.0 // Integral gain
+#define MCCONF_P_PID_KD 0.00000 // Derivative gain
+#define MCCONF_P_PID_KD_PROC 0.00035 // Derivative gain process
+#define MCCONF_P_PID_KD_FILTER 0.2 // Derivative filter
+#define MCCONF_P_PID_ANG_DIV 10.0 // Divide angle by this value
+#define MCCONF_P_PID_GAIN_DEC_ANGLE 500.0 // Decrease PID-gains when the error is below this value
+#define MCCONF_P_PID_OFFSET 0.0 // Angle offset
+
+#define MCCONF_L_MIN_VOLTAGE 20.0 // Minimum input voltage
+#define MCCONF_L_MAX_VOLTAGE 80.0 // Maximum input voltage
+#define MCCONF_DEFAULT_MOTOR_TYPE MOTOR_TYPE_FOC
+
+#define MCCONF_L_MAX_ABS_CURRENT 164.0 // The maximum absolute current above which a fault is generated
+#define MCCONF_L_IN_CURRENT_MAX 70.0 // Input current limit in Amperes (Upper)
+#define MCCONF_L_IN_CURRENT_MIN -70.0 // Input current limit in Amperes (Lower)
+
+#define MCCONF_FOC_F_ZV 30000.0
+#define MCCONF_FOC_CONTROL_SAMPLE_MODE FOC_CONTROL_SAMPLE_MODE_V0
+#define MCCONF_FOC_SAMPLE_V0_V7 false // Run control loop in both v0 and v7 (requires phase shunts)
+
+// Override dead time. See the stm32f4 reference manual for calculating this value.
+#define HW_DEAD_TIME_NSEC 1000.0
+
+// 二选一
+// #define HW_HAS_SHUTDOWN_SWITCH
+// #define HW_HAS_INPUT_CURRENT_SENSOR
+
+// HW properties
+#define HW_HAS_3_SHUNTS // 三相电流采样
+// #define HW_HAS_PHASE_SHUNTS //相电流采样,禁用为低端测流
+// #define HW_HAS_PHASE_FILTERS //相电压滤波
+// #define HW_HAS_CURR_FILTERS //相电流滤波
+
+// Macros
+#define LED_GREEN_GPIO GPIOB
+#define LED_GREEN_PIN 5
+#define LED_RED_GPIO GPIOB
+#define LED_RED_PIN 7
+
+#define LED_GREEN_ON() palSetPad(LED_GREEN_GPIO, LED_GREEN_PIN)
+#define LED_GREEN_OFF() palClearPad(LED_GREEN_GPIO, LED_GREEN_PIN)
+#define LED_RED_ON() palSetPad(LED_RED_GPIO, LED_RED_PIN)
+#define LED_RED_OFF() palClearPad(LED_RED_GPIO, LED_RED_PIN)
+
+#ifdef HW_HAS_PHASE_FILTERS
+#define PHASE_FILTER_GPIO GPIOC
+#define PHASE_FILTER_PIN 9
+#define PHASE_FILTER_ON() palSetPad(PHASE_FILTER_GPIO, PHASE_FILTER_PIN)
+#define PHASE_FILTER_OFF() palClearPad(PHASE_FILTER_GPIO, PHASE_FILTER_PIN)
+#endif
+
+#ifdef HW_HAS_CURR_FILTERS
+#define CURRENT_FILTER_ON() palSetPad(GPIOD, 2)
+#define CURRENT_FILTER_OFF() palClearPad(GPIOD, 2)
+#endif
+
+#ifdef HW_HAS_SHUTDOWN_SWITCH
+// Shutdown pin
+#define HW_SHUTDOWN_GPIO GPIOC
+#define HW_SHUTDOWN_PIN 5
+#define HW_SHUTDOWN_HOLD_ON() palSetPad(HW_SHUTDOWN_GPIO, HW_SHUTDOWN_PIN)
+#define HW_SHUTDOWN_HOLD_OFF() palClearPad(HW_SHUTDOWN_GPIO, HW_SHUTDOWN_PIN)
+#define HW_SAMPLE_SHUTDOWN() hw_sample_shutdown_button()
+
+// Hold shutdown pin early to wake up on short pulses
+#define HW_EARLY_INIT() \
+ palSetPadMode(HW_SHUTDOWN_GPIO, HW_SHUTDOWN_PIN, PAL_MODE_OUTPUT_PUSHPULL); \
+ HW_SHUTDOWN_HOLD_ON();
+#endif
+
+/*
+ * ADC Vector
+ *
+ * 0 (1): IN0 SENS1
+ * 1 (2): IN1 SENS2
+ * 2 (3): IN2 SENS3
+ * 3 (1): IN10 CURR1
+ * 4 (2): IN11 CURR2
+ * 5 (3): IN12 CURR3
+ * 6 (1): IN5 ADC_EXT1
+ * 7 (2): IN6 ADC_EXT2
+ * 8 (3): IN3 TEMP_MOS
+ * 9 (1): IN14 TEMP_MOTOR
+ * 10 (2): IN15 SHUTDOWN
+ * 11 (3): IN13 AN_IN
+ * 12 (1): Vrefint
+ * 13 (2): IN0 SENS1
+ * 14 (3): IN1 SENS2
+ * 15 (1): IN8 TEMP_MOS_2
+ * 16 (2): IN9 TEMP_MOS_3
+ * 17 (3): IN3 SENS3
+ */
+
+#define HW_ADC_CHANNELS 18
+#define HW_ADC_INJ_CHANNELS 3
+#define HW_ADC_NBR_CONV 6
+
+// ADC Indexes
+#define ADC_IND_SENS1 0
+#define ADC_IND_SENS2 1
+#define ADC_IND_SENS3 2
+#define ADC_IND_CURR1 3
+#define ADC_IND_CURR2 4
+#define ADC_IND_CURR3 5
+#define ADC_IND_EXT 6
+#define ADC_IND_EXT2 7
+#define ADC_IND_TEMP_MOS 8
+#define ADC_IND_TEMP_MOTOR 9
+#ifdef HW_HAS_SHUTDOWN_SWITCH
+#define ADC_IND_SHUTDOWN 10
+#elif defined HW_HAS_INPUT_CURRENT_SENSOR
+#define ADC_IND_IN_CURR 10
+// #define CALIB_IN_CURR_OFFSET
+#endif
+#define ADC_IND_VIN_SENS 11
+#define ADC_IND_VREFINT 12
+
+// ADC macros and settings
+
+// Component parameters (can be overridden)
+#define V_REG 3.30
+
+// The voltage dividing acquisition circuit on the Makerbase VESC motherboard is 560K and 21.5K resistors.
+#define VIN_R1 560000.0
+#define VIN_R2 22000.0
+
+#define CURRENT_AMP_GAIN (20.0)
+#define CURRENT_SHUNT_RES (0.0005)
+
+// Input voltage
+#define GET_INPUT_VOLTAGE() ((V_REG / 4095.0) * (float)ADC_Value[ADC_IND_VIN_SENS] * ((VIN_R1 + VIN_R2) / VIN_R2))
+
+#ifdef ADC_IND_IN_CURR
+// Input current
+#define CURRENT_AMP_GAIN (20.0)
+#define CURRENT_SHUNT_RES (0.0005)
+#define GET_INPUT_CURRENT() hw_read_input_current()
+#ifdef CALIB_IN_CURR_OFFSET
+#define GET_INPUT_CURRENT_OFFSET() hw_get_input_current_offset()
+#define MEASURE_INPUT_CURRENT_OFFSET() hw_start_input_current_sensor_offset_measurement()
+#else
+#define GET_INPUT_CURRENT_OFFSET()
+#define MEASURE_INPUT_CURRENT_OFFSET()
+#endif
+#endif
+
+// NTC Termistors
+// #define NTC_RES(adc_val) ((4095.0 * 10000.0) / adc_val - 10000.0)
+#define NTC_RES(adc_val) ((4095.0 * 10000.0) / adc_val - 10000.0)
+#define NTC_TEMP(adc_ind) (1.0 / ((logf(NTC_RES(ADC_Value[adc_ind]) / 10000.0) / 3435.0) + (1.0 / 298.15)) - 273.15)
+
+#define NTC_RES_MOTOR(adc_val) (10000.0 / ((4095.0 / (float)adc_val) - 1.0)) // Motor temp sensor on low side
+#define NTC_TEMP_MOTOR(beta) (1.0 / ((logf(NTC_RES_MOTOR(ADC_Value[ADC_IND_TEMP_MOTOR]) / 10000.0) / beta) + (1.0 / 298.15)) - 273.15)
+
+// Voltage on ADC channel
+#define ADC_VOLTS(ch) ((float)ADC_Value[ch] / 4096.0 * V_REG)
+
+// Double samples in beginning and end for positive current measurement.
+// Useful when the shunt sense traces have noise that causes offset.
+#define CURR1_DOUBLE_SAMPLE 0
+#define CURR2_DOUBLE_SAMPLE 0
+#define CURR3_DOUBLE_SAMPLE 0
+
+// COMM-port ADC GPIOs
+#define HW_ADC_EXT_GPIO GPIOA
+#define HW_ADC_EXT_PIN 5
+#define HW_ADC_EXT2_GPIO GPIOA
+#define HW_ADC_EXT2_PIN 6
+
+// UART Peripheral
+#define HW_UART_DEV SD3
+#define HW_UART_GPIO_AF GPIO_AF_USART3
+#define HW_UART_TX_PORT GPIOB
+#define HW_UART_TX_PIN 10
+#define HW_UART_RX_PORT GPIOB
+#define HW_UART_RX_PIN 11
+
+// Permanent UART Peripheral (for NRF51)
+#define HW_UART_P_BAUD 115200
+#define HW_UART_P_DEV SD4
+#define HW_UART_P_GPIO_AF GPIO_AF_UART4
+#define HW_UART_P_TX_PORT GPIOC
+#define HW_UART_P_TX_PIN 10
+#define HW_UART_P_RX_PORT GPIOC
+#define HW_UART_P_RX_PIN 11
+
+// ICU Peripheral for servo decoding
+#define HW_USE_SERVO_TIM4
+#define HW_ICU_TIMER TIM4
+#define HW_ICU_TIM_CLK_EN() RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE)
+#define HW_ICU_DEV ICUD4
+#define HW_ICU_CHANNEL ICU_CHANNEL_1
+#define HW_ICU_GPIO_AF GPIO_AF_TIM4
+#define HW_ICU_GPIO GPIOB
+#define HW_ICU_PIN 6
+
+// I2C Peripheral
+#define HW_I2C_DEV I2CD2
+#define HW_I2C_GPIO_AF GPIO_AF_I2C2
+#define HW_I2C_SCL_PORT GPIOB
+#define HW_I2C_SCL_PIN 10
+#define HW_I2C_SDA_PORT GPIOB
+#define HW_I2C_SDA_PIN 11
+
+// Hall/encoder pins
+#define HW_HALL_ENC_GPIO1 GPIOC
+#define HW_HALL_ENC_PIN1 6
+#define HW_HALL_ENC_GPIO2 GPIOC
+#define HW_HALL_ENC_PIN2 7
+#define HW_HALL_ENC_GPIO3 GPIOC
+#define HW_HALL_ENC_PIN3 8
+#define HW_ENC_TIM TIM3
+#define HW_ENC_TIM_AF GPIO_AF_TIM3
+#define HW_ENC_TIM_CLK_EN() RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE)
+#define HW_ENC_EXTI_PORTSRC EXTI_PortSourceGPIOC
+#define HW_ENC_EXTI_PINSRC EXTI_PinSource8
+#define HW_ENC_EXTI_CH EXTI9_5_IRQn
+#define HW_ENC_EXTI_LINE EXTI_Line8
+#define HW_ENC_EXTI_ISR_VEC EXTI9_5_IRQHandler
+#define HW_ENC_TIM_ISR_CH TIM3_IRQn
+#define HW_ENC_TIM_ISR_VEC TIM3_IRQHandler
+
+// SPI pins
+#define HW_SPI_DEV SPID1
+#define HW_SPI_GPIO_AF GPIO_AF_SPI1
+#define HW_SPI_PORT_NSS GPIOA
+#define HW_SPI_PIN_NSS 4
+#define HW_SPI_PORT_SCK GPIOA
+#define HW_SPI_PIN_SCK 5
+#define HW_SPI_PORT_MOSI GPIOA
+#define HW_SPI_PIN_MOSI 7
+#define HW_SPI_PORT_MISO GPIOA
+#define HW_SPI_PIN_MISO 6
+
+// Measurement macros
+#define ADC_V_L1 ADC_Value[ADC_IND_SENS1]
+#define ADC_V_L2 ADC_Value[ADC_IND_SENS2]
+#define ADC_V_L3 ADC_Value[ADC_IND_SENS3]
+#define ADC_V_ZERO (ADC_Value[ADC_IND_VIN_SENS] / 2)
+
+// Macros
+#define READ_HALL1() palReadPad(HW_HALL_ENC_GPIO1, HW_HALL_ENC_PIN1)
+#define READ_HALL2() palReadPad(HW_HALL_ENC_GPIO2, HW_HALL_ENC_PIN2)
+#define READ_HALL3() palReadPad(HW_HALL_ENC_GPIO3, HW_HALL_ENC_PIN3)
+
+// Setting limits
+#define HW_LIM_CURRENT -100.0, 100.0
+#define HW_LIM_CURRENT_IN -100.0, 100.0
+#define HW_LIM_CURRENT_ABS 0.0, 200
+#define HW_LIM_VIN 12.0, 85.0
+#define HW_LIM_ERPM -200e3, 200e3
+#define HW_LIM_DUTY_MIN 0.0, 0.1
+#define HW_LIM_DUTY_MAX 0.0, 0.99
+#define HW_LIM_TEMP_FET -40.0, 110.0
+
+// HW-specific functions
+#ifdef HW_HAS_SHUTDOWN_SWITCH
+bool hw_sample_shutdown_button(void);
+#elif defined ADC_IND_IN_CURR
+float hw_read_input_current(void);
+void hw_get_input_current_offset(void);
+void hw_start_input_current_sensor_offset_measurement(void);
+#endif
+#endif /* HW_IVY_Test_H_ */
diff --git a/hwconf/IVY_Esk8/IVY_Test/hw_IVY_Test_no_limits.h b/hwconf/IVY_Esk8/IVY_Test/hw_IVY_Test_no_limits.h
new file mode 100644
index 0000000000..957b2d1893
--- /dev/null
+++ b/hwconf/IVY_Esk8/IVY_Test/hw_IVY_Test_no_limits.h
@@ -0,0 +1,27 @@
+/*
+ Copyright 2018 Benjamin Vedder benjamin@vedder.se
+
+ This file is part of the VESC firmware.
+
+ The VESC firmware 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.
+
+ The VESC firmware 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 .
+*/
+
+#ifndef HW_IVY_Test_NO_LIMITS_H_
+#define HW_IVY_Test_NO_LIMITS_H_
+
+#define DISABLE_HW_LIMITS
+
+#include "hw_IVY_Test.h"
+
+#endif /* HW_IVY_Test_NO_LIMITS_H_ */
diff --git a/hwconf/makerbase/100_300_HP/hw_mksesc_100_300_hp.h b/hwconf/makerbase/100_300_HP/hw_mksesc_100_300_hp.h
index cae0e87181..e28eed8e9e 100644
--- a/hwconf/makerbase/100_300_HP/hw_mksesc_100_300_hp.h
+++ b/hwconf/makerbase/100_300_HP/hw_mksesc_100_300_hp.h
@@ -244,14 +244,14 @@
#define READ_HALL3() palReadPad(HW_HALL_ENC_GPIO3, HW_HALL_ENC_PIN3)
// Override dead time. See the stm32f4 reference manual for calculating this value.
-#define HW_DEAD_TIME_NSEC 1000.0
+#define HW_DEAD_TIME_NSEC 400.0
// Default setting overrides
#ifndef MCCONF_L_MIN_VOLTAGE
-#define MCCONF_L_MIN_VOLTAGE 12.0 // Minimum input voltage
+#define MCCONF_L_MIN_VOLTAGE 20.0 // Minimum input voltage
#endif
#ifndef MCCONF_L_MAX_VOLTAGE
-#define MCCONF_L_MAX_VOLTAGE 110.0 // Maximum input voltage
+#define MCCONF_L_MAX_VOLTAGE 100.0 // Maximum input voltage
#endif
#ifndef MCCONF_DEFAULT_MOTOR_TYPE
#define MCCONF_DEFAULT_MOTOR_TYPE MOTOR_TYPE_FOC
@@ -273,17 +273,20 @@
#endif
// Setting limits
-#define HW_LIM_CURRENT -500.0, 500.0
+#define HW_LIM_CURRENT -1200.0, 1200.0
#define HW_LIM_CURRENT_IN -500.0, 500.0
-#define HW_LIM_CURRENT_ABS 0.0, 650
-#define HW_LIM_VIN 6.0, 120.0
+#define HW_LIM_CURRENT_ABS 0.0, 1500
+#define HW_LIM_VIN 20.0, 120.0
#define HW_LIM_ERPM -200e3, 200e3
#define HW_LIM_DUTY_MIN 0.0, 0.1
#define HW_LIM_DUTY_MAX 0.0, 0.99
-#define HW_LIM_TEMP_FET -40.0, 110.0
+#define HW_LIM_TEMP_FET -40.0, 90.0
// HW-specific functions
float hw100_300_get_temp(void);
bool hw_sample_shutdown_button(void);
+// Custom app: PPM throttle + ADC2 brake + ADC1 gain + reverse button
+#define APP_CUSTOM_TO_USE "applications/app_ppm_adc_brake.c"
+
#endif /* HW_MKSESC_100_300_HP_H_ */