class MyStepper {
public:
const byte Apin, _Apin, Bpin, _Bpin;
unsigned long lastMoveUs = 0;
int location = 0;
int zero = 0;
int target = 0;
const unsigned long maxSpeedUs;
MyStepper(
const byte ApinAttach, const byte _ApinAttach,
const byte BpinAttach, const byte _BpinAttach,
const unsigned long maxSpeedUs) :
Apin(ApinAttach), _Apin(_ApinAttach), Bpin(BpinAttach), _Bpin(_BpinAttach), maxSpeedUs(maxSpeedUs)
{}
void setup() {
pinMode(Apin, OUTPUT);
pinMode(_Apin, OUTPUT);
pinMode(Bpin, OUTPUT);
pinMode(_Bpin, OUTPUT);
lastMoveUs = micros();
}
void loop() {
if (location == target) {
return;
}
unsigned long nowUs = micros();
if (nowUs - lastMoveUs < maxSpeedUs) return;
lastMoveUs = nowUs;
location += target > location ? 1 : -1;
switch (location & 7) {
case 0:
digitalWrite(Apin, HIGH);
digitalWrite(_Apin, LOW);
digitalWrite(Bpin, LOW);
digitalWrite(_Bpin, LOW);
break;
case 1:
digitalWrite(Apin, HIGH);
digitalWrite(_Apin, LOW);
digitalWrite(Bpin, HIGH);
digitalWrite(_Bpin, LOW);
break;
case 2:
digitalWrite(Apin, LOW);
digitalWrite(_Apin, LOW);
digitalWrite(Bpin, HIGH);
digitalWrite(_Bpin, LOW);
break;
case 3:
digitalWrite(Apin, LOW);
digitalWrite(_Apin, HIGH);
digitalWrite(Bpin, HIGH);
digitalWrite(_Bpin, LOW);
break;
case 4:
digitalWrite(Apin, LOW);
digitalWrite(_Apin, HIGH);
digitalWrite(Bpin, LOW);
digitalWrite(_Bpin, LOW);
break;
case 5:
digitalWrite(Apin, LOW);
digitalWrite(_Apin, HIGH);
digitalWrite(Bpin, LOW);
digitalWrite(_Bpin, HIGH);
break;
case 6:
digitalWrite(Apin, LOW);
digitalWrite(_Apin, LOW);
digitalWrite(Bpin, LOW);
digitalWrite(_Bpin, HIGH);
break;
case 7:
digitalWrite(Apin, HIGH);
digitalWrite(_Apin, LOW);
digitalWrite(Bpin, LOW);
digitalWrite(_Bpin, HIGH);
break;
}
}
inline int getTarget() {
return target - zero;
}
void moveTo(int _target) {
target = _target + zero;
}
void zeroHere() {
zero = target;
}
inline boolean isMoving() {
return location != target;
}
};
class Sampler {
public:
static const int RING_BUFFER_SIZE = 50;
const float LOWPASS_DAMPING;
const float MAX_FREQ;
const int STEPS;
const float NEEDLE_HYSTERESIS;
const unsigned long ZERO_SPEED_us;
MyStepper &stepper;
volatile long mostRecentPulseUs;
volatile long ringBufferUs[RING_BUFFER_SIZE];
volatile int ringBufferPos = 0;
volatile float avgPulseWidthUs;
float avgPulseWidthCpyUs;
float frequencyHz;
Sampler(const float LOWPASS_DAMPING, const float MAX_FREQ, const float MIN_FREQ, const int STEPS, const float NEEDLE_HYSTERESIS, MyStepper &stepper ) :
LOWPASS_DAMPING(LOWPASS_DAMPING) ,
MAX_FREQ(MAX_FREQ),
STEPS(STEPS),
NEEDLE_HYSTERESIS(NEEDLE_HYSTERESIS),
stepper(stepper),
ZERO_SPEED_us(1000000 / MIN_FREQ)
{
}
void pulseISR() {
if (++ringBufferPos >= RING_BUFFER_SIZE) {
ringBufferPos = 0;
}
mostRecentPulseUs = micros();
unsigned long pulseWidthUs = mostRecentPulseUs - ringBufferUs[ringBufferPos];
ringBufferUs[ringBufferPos] = mostRecentPulseUs;
avgPulseWidthUs = LOWPASS_DAMPING * avgPulseWidthUs + (1 - LOWPASS_DAMPING) * (float)pulseWidthUs / RING_BUFFER_SIZE;
}
void setup() {
}
void loop() {
noInterrupts();
if (micros() - mostRecentPulseUs > ZERO_SPEED_us && avgPulseWidthUs <= ZERO_SPEED_us) {
avgPulseWidthUs = ZERO_SPEED_us * 2.0;
}
avgPulseWidthCpyUs = avgPulseWidthUs;
interrupts();
double newTarget;
if (avgPulseWidthCpyUs == 0 || avgPulseWidthCpyUs >= ZERO_SPEED_us)
frequencyHz = 0;
else
frequencyHz = 100000.0 / avgPulseWidthCpyUs;
newTarget = frequencyHz / MAX_FREQ * STEPS;
if (newTarget < 0) newTarget = 0;
else if (newTarget > STEPS) newTarget = STEPS;
if (newTarget < stepper.getTarget() - NEEDLE_HYSTERESIS || newTarget > stepper.getTarget() + 1 + NEEDLE_HYSTERESIS) {
stepper.moveTo(newTarget);
}
}
};
class TurnPinOnIfSamplerOverLimit {
public:
const float lowerBoundHz;
const float upperBoundHz;
Sampler &sampler;
const byte pin;
boolean state;
TurnPinOnIfSamplerOverLimit( const float lowerBoundHz,
const float upperBoundHz,
Sampler &sampler,
const byte pin) :
lowerBoundHz(lowerBoundHz),
upperBoundHz(upperBoundHz),
sampler(sampler),
pin(pin)
{}
void setup() {
pinMode(pin, OUTPUT);
state = digitalRead(pin) != LOW;
}
void loop() {
if (state && sampler.frequencyHz < lowerBoundHz) {
state = false;
digitalWrite(pin, LOW);
}
else if (!state && sampler.frequencyHz >= upperBoundHz) {
state = true;
digitalWrite(pin, HIGH);
}
}
};
class TurnPinOnWhenFallingThroughWindow {
public:
const float lowerBoundHz;
const float upperBoundHz;
Sampler &sampler;
const byte pin;
enum State {
ABOVE_WINDOW = 3, IN_WINDOW = 2, BELOW_WINDOW = 1
} state;
TurnPinOnWhenFallingThroughWindow( const float lowerBoundHz,
const float upperBoundHz,
Sampler &sampler,
const byte pin) :
lowerBoundHz(lowerBoundHz),
upperBoundHz(upperBoundHz),
sampler(sampler),
pin(pin)
{}
void setup() {a
pinMode(pin, OUTPUT);
digitalWrite(pin, LOW);
state = BELOW_WINDOW;
}
void loop() {
if (sampler.frequencyHz < lowerBoundHz) {
if (state != BELOW_WINDOW) {
digitalWrite(pin, LOW);
}
state = BELOW_WINDOW;
}
else if (sampler.frequencyHz > upperBoundHz) {
if (state != ABOVE_WINDOW) {
digitalWrite(pin, LOW);
}
state = ABOVE_WINDOW;
}
else {
switch (state) {
case BELOW_WINDOW:
digitalWrite(pin, LOW);
break;
case IN_WINDOW:
break;
case ABOVE_WINDOW:
digitalWrite(pin, HIGH);
break;
}
state = IN_WINDOW;
}
}
};
MyStepper tacho(5, 7, 8, 6, 750);
MyStepper speedo(9, 11, 12, 10, 3000);
Sampler tachoSampler(
.75,
166,
1,
630*2,
.6,
tacho
);
Sampler speedoSampler(
.75,
2050,
512,
48 * 2 * 3 / 4,
.6,
speedo
);
void pin2ISR() {
tachoSampler.pulseISR();
}
void pin3ISR() {
speedoSampler.pulseISR();
}
TurnPinOnIfSamplerOverLimit speedoLimit(950, 1000, speedoSampler, 4);
TurnPinOnWhenFallingThroughWindow tachoLimit(160, 165, tachoSampler, 13);
void setup() {
tacho.setup();
tachoSampler.setup();
pinMode(2, INPUT);
attachInterrupt(digitalPinToInterrupt(2),pin2ISR, RISING);
speedo.setup();
speedoSampler.setup();
pinMode(3, INPUT);
attachInterrupt(digitalPinToInterrupt(3), pin3ISR, RISING);
speedoLimit.setup();
tachoLimit.setup();
tacho.moveTo(tachoSampler.STEPS * 5 / 5);
speedo.moveTo(speedoSampler.STEPS * 5 / 4);
while (tacho.isMoving() || speedo.isMoving()) {
tacho.loop();
speedo.loop();
}
tacho.zeroHere();
speedo.zeroHere();
tacho.moveTo(-tachoSampler.STEPS);
speedo.moveTo(-speedoSampler.STEPS);
while (tacho.isMoving() || speedo.isMoving()) {
tacho.loop();
speedo.loop();
}
tacho.zeroHere();
speedo.zeroHere();
}
void loop() {
tacho.loop();
tachoSampler.loop();
speedo.loop();
speedoSampler.loop();
speedoLimit.loop();
tachoLimit.loop();
}