Last active
November 27, 2023 22:14
-
-
Save pinski1/6552054d6d738eaf9bb94b8d5970e7e5 to your computer and use it in GitHub Desktop.
Code for a ZTW Spider Lite 18A v2 brushless ESC into a solenoid driver
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** ZTW Spider Lite v2 ESC to Solenoid Driver | |
ZTW Spider Lite 18A v2 uses the ATMega8A and | |
none of the MOSFET enable pins are PWM capable. | |
Pin out: | |
https://github.com/bitdump/BLHeli/blob/master/Atmel/ZTW_Spider_Lite_18Av2.inc | |
To Do: | |
[ ] Monitor VCC via PIN_MUX_V | |
[ ] Use soft PWM to put valve into low power mode | |
[ ] Use varying `durationRC` to set valve duration | |
**/ | |
#include <elapsedMillis.h> | |
/** Settings **/ | |
#define MAX_MISSING_RC (22000UL*3) // max time between pulses, μs | |
#define LOOP_RATE (10000) // loop duration (100Hz, μs | |
#define THRESHOLD_SOL (1780) // solenoid threshold, μs | |
#define SOL_DURATION_TOTAL (100000) // how long to open for, μs | |
#define DEADTIME_LOW (10) | |
#define DEADTIME_HIGH (8) | |
#define CHARGE_PUMP_REFRESH (500) // time to let the charge pump refresh | |
#define VERSION_MAJOR (0) | |
#define VERSION_MINOR (2) | |
#define VERSION_PATCH (0) | |
/** Pin Map **/ | |
#define PIN_RC_IN (2) // PD2, INT0 | |
#define PIN_AP_FET (17) // PC3 (no PWM) | |
#define PIN_AN_FET (5) // PD5 | |
#define PIN_CP_FET (4) // PD4 (no PWM) | |
#define PIN_CN_FET (8) // PB0 | |
#define PIN_MUX_V (A2) // PC2, 220kΩ from Vbat, 51kΩ to GND, 10.10V → 1.900V at ADC2 | |
#define PIN_MUX_T (A1) // PC1, 10kΩ NTC to 5V, 820Ω to GND, at ADC1 | |
/** Globals **/ | |
elapsedMicros timerLoop; // timer for loop | |
elapsedMicros timerSol; // timer for solenoid | |
elapsedMicros timerRC; // timer for RC signals | |
volatile long durationRC; // RC pulse length | |
volatile int lastPinRC; // last RC pin state | |
enum states {sFailsafe, sIdle, sActive} currentState; | |
void setup(void) { | |
delay(10); | |
// configure pins | |
pinMode(PIN_RC_IN, INPUT); | |
pinMode(PIN_AP_FET, OUTPUT); | |
pinMode(PIN_AN_FET, OUTPUT); | |
pinMode(PIN_CP_FET, OUTPUT); | |
pinMode(PIN_CN_FET, OUTPUT); | |
pinMode(PIN_MUX_V, INPUT); | |
pinMode(PIN_MUX_T, INPUT); | |
// set starting pin states | |
digitalWrite(PIN_AP_FET, LOW); | |
digitalWrite(PIN_AN_FET, LOW); | |
digitalWrite(PIN_CP_FET, LOW); | |
digitalWrite(PIN_CN_FET, LOW); | |
// set up interrupt for RC signals | |
attachInterrupt(digitalPinToInterrupt(PIN_RC_IN), handlerRC, CHANGE); | |
// setup the counters | |
timerRC = MAX_MISSING_RC + 5; // set to 'safe' | |
lastPinRC = LOW; | |
currentState = sFailsafe; | |
durationRC = 0x00UL; | |
timerLoop = 0x00UL; | |
} | |
void loop(void) { | |
if (timerLoop >= LOOP_RATE) { | |
timerLoop -= LOOP_RATE; | |
if (timerRC >= MAX_MISSING_RC) currentState = sFailsafe; | |
switch (currentState) { | |
case sIdle: | |
if (durationRC > THRESHOLD_SOL) { | |
setSolenoid(HIGH); | |
timerSol = 0x00UL; | |
currentState = sActive; | |
} | |
else setSolenoid(LOW); | |
break; | |
case sActive: | |
if (timerSol >= SOL_DURATION_TOTAL) { | |
setSolenoid(LOW); | |
} | |
if (durationRC < THRESHOLD_SOL) { | |
setSolenoid(LOW); | |
currentState = sIdle; | |
} | |
break; | |
case sFailsafe: | |
default: | |
setSolenoid(LOW); | |
if (durationRC > 500 && durationRC < 2500) currentState = sIdle; | |
} | |
} | |
} | |
void handlerRC(void) { | |
if (digitalRead(PIN_RC_IN) == HIGH && lastPinRC == LOW) { | |
// rising edge | |
timerRC = 0x00UL; | |
} | |
else if (digitalRead(PIN_RC_IN) == LOW && lastPinRC == HIGH) { | |
//falling edge | |
durationRC = timerRC; | |
} | |
lastPinRC = digitalRead(PIN_RC_IN); | |
} | |
void setSolenoid(int value) { | |
// channel A will be set to GND permanently | |
// channel C will be set to GND/VBAT based on `value` | |
digitalWrite(PIN_AN_FET, HIGH); | |
if (value == HIGH) { | |
if (digitalRead(PIN_CP_FET) != HIGH) { | |
digitalWrite(PIN_CN_FET, LOW); | |
delayMicroseconds(DEADTIME_HIGH / 2); | |
digitalWrite(PIN_CP_FET, HIGH); | |
} | |
else { | |
// else already set! | |
// but need to drop it down to allow the charge pump to refill | |
digitalWrite(PIN_CP_FET, LOW); | |
delayMicroseconds(CHARGE_PUMP_REFRESH); | |
digitalWrite(PIN_CP_FET, HIGH); | |
} | |
} | |
else { | |
if (digitalRead(PIN_CN_FET) != HIGH) { | |
digitalWrite(PIN_CP_FET, LOW); | |
delayMicroseconds(DEADTIME_LOW / 2); | |
digitalWrite(PIN_CN_FET, HIGH); | |
} | |
// else already set! | |
} | |
return; | |
} | |
unsigned int getVoltage(void) { | |
const unsigned long lsb2MicroVolt = 25971; // ((5000000μV ÷ 1023)/51kΩ)*(220kΩ + 51kΩ) | |
unsigned int voltage = analogRead(PIN_MUX_V) * lsb2MicroVolt; | |
voltage = (voltage + 500) / 1000; // convert to millivolts as accurately as possible | |
return voltage; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment