mirror of
https://github.com/espruino/Espruino.git
synced 2025-12-08 19:06:15 +00:00
205 lines
7.8 KiB
C
205 lines
7.8 KiB
C
/*
|
|
* This file is part of Espruino, a JavaScript interpreter for Microcontrollers
|
|
*
|
|
* Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
* Trigger wheel functionality
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
#include "trigger.h"
|
|
#include "jsinteractive.h"
|
|
#include "jshardware.h"
|
|
#include "jstimer.h"
|
|
|
|
const int TRIGGER_LOOKAHEAD = 4;
|
|
|
|
typedef struct {
|
|
TriggerError flag;
|
|
const char *str;
|
|
} TriggerErrorStr;
|
|
|
|
TriggerErrorStr triggerErrorStrings[] = {
|
|
{ TRIGERR_TRIG_IN_FUTURE , "TRIG_IN_FUTURE" },
|
|
{ TRIGERR_TRIG_IN_PAST , "TRIG_IN_PAST" },
|
|
{ TRIGERR_MISSED_TOOTH , "MISSED_TOOTH" },
|
|
{ TRIGERR_MISSED_TRIG_TOOTH , "MISSED_TRIG_TOOTH" },
|
|
{ TRIGERR_WHEEL_MISSED_TOOTH , "WHEEL_MISSED_TOOTH" },
|
|
{ TRIGERR_WHEEL_GAINED_TOOTH , "WHEEL_GAINED_TOOTH" },
|
|
{ TRIGERR_WHEEL_MISSED_TRIG_TOOTH, "WHEEL_MISSED_TRIG_TOOTH" },
|
|
{ TRIGERR_SHORT_TOOTH , "SHORT_TOOTH" },
|
|
{ TRIGERR_TRIG_TOOTH_CHANGED , "TRIG_TOOTH_CHANGED" },
|
|
{ TRIGERR_INVALID_ARG , "INVALID_ARG" },
|
|
{ TRIGERR_WRONG_TIME , "WRONG_TIME" },
|
|
{ TRIGERR_TIMER_FULL , "TIMER_FULL" }
|
|
};
|
|
|
|
const char *trigGetErrorString(TriggerError flag) {
|
|
int i, l = sizeof(triggerErrorStrings) / sizeof(TriggerErrorStr);
|
|
for (i=0;i<l;i++)
|
|
if (triggerErrorStrings[i].flag == flag)
|
|
return triggerErrorStrings[i].str;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/** TODO:
|
|
|
|
Log errors (error flag?) when timing is wrong
|
|
Issue using with save()
|
|
Needs to have a minRPM to detect when wheel is stationary
|
|
*/
|
|
|
|
TriggerStruct mainTrigger = { (Pin)-1/*pin*/};
|
|
|
|
void trigOnTimingPulse(TriggerStruct *data, JsSysTime pulseTime) {
|
|
JsSysTime currentTime = jshGetSystemTime();
|
|
uint32_t timerOffset = jstGetUtilTimerOffset();
|
|
int timeDiff = (int)(pulseTime - data->lastTime);
|
|
if (timeDiff < 0) {
|
|
data->errors |= TRIGERR_WRONG_TIME;
|
|
timeDiff = 0;
|
|
// jsiConsolePrintf("0x%Lx 0x%Lx 0x%Lx\n",data->lastTime2, data->lastTime, pulseTime);
|
|
pulseTime = data->lastTime + data->avrTrigger; // just make it up and hope!
|
|
}
|
|
pulseTime -= currentTime;
|
|
// it's been too long since the last tooth (we were stationary?)
|
|
// clip and make sure we do a quick average
|
|
if (timeDiff > data->maxTooth) {
|
|
timeDiff = data->maxTooth;
|
|
data->teethSinceStart = 0;
|
|
}
|
|
|
|
data->lastTime2 = data->lastTime;
|
|
data->lastTime = pulseTime;
|
|
|
|
unsigned char teeth = 1;
|
|
// running average...
|
|
if (data->teethSinceStart < 8) {
|
|
// Fast average if we're just starting off...
|
|
data->avrTrigger = (data->avrTrigger + (unsigned int)timeDiff) >> 1;
|
|
data->avrTooth = data->avrTrigger;
|
|
} else {
|
|
// Otherwise figure out how many teeth
|
|
teeth = (unsigned char)(((((unsigned int)timeDiff<<1) / data->avrTrigger) + 1) >> 1); // round to find out # of teeth
|
|
if (teeth<1) {
|
|
data->errors |= TRIGERR_SHORT_TOOTH;
|
|
teeth=1;
|
|
}
|
|
// and do slow averages
|
|
data->avrTrigger = (data->avrTrigger*7 + (unsigned int)timeDiff) >> 3;
|
|
data->avrTooth = (data->avrTooth*7 + (unsigned int)timeDiff/teeth) >> 3;
|
|
}
|
|
if (data->teethSinceStart<0xFFFFFFFF) data->teethSinceStart++;
|
|
|
|
// move tooth count
|
|
unsigned char lastTooth = data->currTooth;
|
|
data->currTooth = (unsigned char)(data->currTooth + teeth);
|
|
// handle trigger tooth
|
|
if (teeth > data->teethMissing) {
|
|
if (teeth != data->teethMissing+1) data->errors |= TRIGERR_MISSED_TRIG_TOOTH;
|
|
|
|
if (data->currTooth == data->teethTotal) {
|
|
/* just what we expect - set back to 0.
|
|
* Also reset wrongTriggerTeeth as we know all is good*/
|
|
data->wrongTriggerTeeth = 0;
|
|
data->currTooth = 0;
|
|
} else {
|
|
// Something has gone wrong - we got a trigger tooth when we didn't expect one
|
|
if (data->currTooth < data->teethTotal) {
|
|
data->errors |= TRIGERR_WHEEL_MISSED_TOOTH;
|
|
} else { // data->currTooth > expectedTooth
|
|
data->errors |= TRIGERR_WHEEL_GAINED_TOOTH;
|
|
}
|
|
// increment counter
|
|
data->wrongTriggerTeeth++;
|
|
/* if we've had the wrong trigger tooth for two consecutive revs,
|
|
* change over. */
|
|
if (data->wrongTriggerTeeth > 1) {
|
|
data->wrongTriggerTeeth = 0;
|
|
data->currTooth = 0;
|
|
data->errors |= TRIGERR_TRIG_TOOTH_CHANGED;
|
|
}
|
|
}
|
|
} else {
|
|
// just a normal tooth event
|
|
if (teeth!=1) data->errors |= TRIGERR_MISSED_TOOTH;
|
|
}
|
|
// handle roll-over
|
|
if (data->teethTotal>0) { // sanity check to stop endless loop if misconfigured
|
|
while (data->currTooth >= data->teethTotal) {
|
|
data->errors |= TRIGERR_WHEEL_MISSED_TRIG_TOOTH;
|
|
data->currTooth = (unsigned char)(data->currTooth + data->teethTotal);
|
|
}
|
|
}
|
|
|
|
|
|
// handle sending events
|
|
if (data->teethSinceStart > data->teethTotal) {
|
|
// TODO: teethSinceStart>10 && hadTrigger?
|
|
// don't start firing events until we actually know where we are!!
|
|
|
|
unsigned char currTooth = data->currTooth;
|
|
if (currTooth < lastTooth) currTooth = (unsigned char)(data->currTooth + data->teethTotal);
|
|
int tooth;
|
|
|
|
for (tooth=lastTooth+TRIGGER_LOOKAHEAD;tooth<currTooth+TRIGGER_LOOKAHEAD;tooth++) {
|
|
// actually we want to check a few teeth into the future to give us time
|
|
int i;
|
|
for (i=0;i<TRIGGER_TRIGGERS_COUNT;i++) {
|
|
TriggerPointStruct *trig = &data->triggers[i];
|
|
if (trig->tooth != TRIGGERPOINT_TOOTH_DISABLE) {
|
|
if ((trig->tooth == tooth) || ((trig->tooth+data->teethTotal) == tooth)) { // because we wrap
|
|
// doAtTime(data->triggers[i].toothFraction * data->avrTooth >> 8);
|
|
JsSysTime trigTime = pulseTime +
|
|
((trig->toothFraction * data->avrTooth) >> 8) +
|
|
(((int)tooth-(int)currTooth) * (int)data->avrTooth);
|
|
if (trigTime > pulseTime + jshGetTimeFromMilliseconds(500)) {
|
|
trigTime = jshGetTimeFromMilliseconds(500);
|
|
data->errors |= TRIGERR_TRIG_IN_FUTURE;
|
|
}
|
|
if (trigTime < currentTime) {
|
|
data->errors |= TRIGERR_TRIG_IN_PAST;
|
|
//jsiConsolePrint("Trigger already passed\n");
|
|
}
|
|
|
|
if (!jstPinOutputAtTime(trigTime, &timerOffset, trig->pins, TRIGGERPOINT_TRIGGERS_COUNT, 0xFF))
|
|
data->errors |= TRIGERR_TIMER_FULL;
|
|
if (trig->pulseLength>0) {
|
|
if (!jstPinOutputAtTime(trigTime+trig->pulseLength, &timerOffset, trig->pins, TRIGGERPOINT_TRIGGERS_COUNT, 0))
|
|
data->errors |= TRIGERR_TIMER_FULL;
|
|
}
|
|
// trigger fired, so update it
|
|
trig->tooth = trig->newTooth;
|
|
trig->toothFraction = trig->newToothFraction;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Actually handle a trigger event, and do stuff if it is for us */
|
|
bool trigHandleEXTI(IOEventFlags channel, JsSysTime time) {
|
|
if (mainTrigger.sensorPin!=PIN_UNDEFINED && jshIsEventForPin(channel, mainTrigger.sensorPin)) {
|
|
// jshPinOutput(JSH_PORTB_OFFSET + 4, event.flags & EV_EXTI_IS_HIGH);
|
|
|
|
if (!(channel & EV_EXTI_IS_HIGH)) // we only care about falling edges
|
|
trigOnTimingPulse(&mainTrigger, time);
|
|
return true; // return true anyway, so stop this being added to our event queue
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/** At a certain time, get which tooth number we're on */
|
|
JsVarFloat trigGetToothAtTime(TriggerStruct *data, JsSysTime time) {
|
|
JsVarFloat tooth = data->currTooth + ((JsVarFloat)(time - data->lastTime) / ((data->avrTooth>1)?data->avrTooth:0));
|
|
return wrapAround(tooth, data->teethTotal);
|
|
}
|
|
|
|
|