forked from espruino/Espruino
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtrigger.c
More file actions
205 lines (181 loc) · 7.73 KB
/
Copy pathtrigger.c
File metadata and controls
205 lines (181 loc) · 7.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
/*
* 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();
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!
}
// 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, trig->pins, TRIGGERPOINT_TRIGGERS_COUNT, 0xFF))
data->errors |= TRIGERR_TIMER_FULL;
if (trig->pulseLength>0) {
if (!jstPinOutputAtTime(trigTime+trig->pulseLength, 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) {
IOEvent event;
event.flags = channel;
if (mainTrigger.sensorPin!=PIN_UNDEFINED && jshIsEventForPin(&event, mainTrigger.sensorPin)) {
// jshPinOutput(JSH_PORTB_OFFSET + 4, event.flags & EV_EXTI_IS_HIGH);
if (!(event.flags & 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);
}