mirror of
https://github.com/espruino/Espruino.git
synced 2025-12-08 19:06:15 +00:00
Timers now have unique IDs (#1444)
Added `require("timer")` to access timers, removed `E.dumpTimers` and `Pin.writeAtTime` (fix #2586)
Added ability to execute JS from an a timer with `require("timer").add({type:"EXEC",...)`
This commit is contained in:
parent
ebb469bb75
commit
43943b58ac
@ -2,6 +2,9 @@
|
||||
Bangle.js: Updated built-in Layout.js with some minor fixes
|
||||
STM32: Ensure setDeepSleep(1) will only sleep if there are no active PWM outputs
|
||||
Pin.getInfo().negated now set if pin negated in firmware
|
||||
Timers now have unique IDs (#1444)
|
||||
Added `require("timer")` to access timers, removed `E.dumpTimers` and `Pin.writeAtTime` (fix #2586)
|
||||
Added ability to execute JS from an a timer with `require("timer").add({type:"EXEC",...)`
|
||||
Bangle.js: Add Bangle.setOptions({stepCounterDisabled:bool}) to disable the step counter
|
||||
Date: fix parsing of ISO8601 timezones (+HHMM worked, but +HH:MM and +HH added) (fix #2669)
|
||||
|
||||
|
||||
1
Makefile
1
Makefile
@ -270,6 +270,7 @@ src/jswrap_json.c \
|
||||
src/jswrap_number.c \
|
||||
src/jswrap_object.c \
|
||||
src/jswrap_regexp.c \
|
||||
src/jswrap_timer.c \
|
||||
src/jswrap_string.c \
|
||||
src/jswrap_modules.c \
|
||||
src/jswrap_math.c
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
[
|
||||
{"class":"OneWire","name":"*"},
|
||||
{"class":"SPI","name":"send4bit"},
|
||||
{"class":"E","name":"dumpTimers"},
|
||||
{"class":"E","name":"enableWatchdog"},
|
||||
{"class":"E","name":"getClock"},
|
||||
{"class":"E","name":"setBootCode"},
|
||||
{"class":"Pin","name":"writeAtTime"}
|
||||
{"class":"timer","name":"*"}
|
||||
]
|
||||
|
||||
@ -470,6 +470,11 @@ bool CALLED_FROM_INTERRUPT jshPushEvent(IOEventFlags evt, uint8_t *data, unsigne
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Push a Custom IO event into the ioBuffer (designed to be called from IRQ), returns true on success, Calls jshHadEvent();
|
||||
bool CALLED_FROM_INTERRUPT jshPushCustomEvent(IOCustomEventFlags customFlags) {
|
||||
jshPushEvent(EV_CUSTOM, (uint8_t*)&customFlags, sizeof(customFlags));
|
||||
}
|
||||
|
||||
/// Try and handle events in the IRQ itself. true if handled and shouldn't go in queue
|
||||
static bool jshPushIOCharEventsHandler(IOEventFlags channel, char *data, unsigned int count) {
|
||||
// Check for a CTRL+C
|
||||
|
||||
@ -87,7 +87,8 @@ typedef enum {
|
||||
#ifdef BLUETOOTH
|
||||
EV_BLUETOOTH_PENDING, // Tasks that came from the Bluetooth Stack in an IRQ
|
||||
#endif
|
||||
EV_CUSTOM, ///< Custom event (See IOCustomEventFlags)
|
||||
EV_RUN_INTERRUPT_JS, ///< Run some JavaScript code. See EXEC_RUN_INTERRUPT_JS. data is JsVarRef of code to run
|
||||
EV_CUSTOM, ///< Custom event (First byte is IOCustomEventFlags to determine the event type)
|
||||
#ifdef BANGLEJS
|
||||
EV_BANGLEJS, // sent whenever Bangle.js-specific data needs to be queued
|
||||
#endif
|
||||
@ -131,10 +132,13 @@ typedef enum {
|
||||
/** Event types for EV_CUSTOM */
|
||||
typedef enum {
|
||||
EVC_NONE,
|
||||
EVC_TIMER_FINISHED,
|
||||
EVC_TIMER_BUFFER_FLIP,
|
||||
#ifdef NRF52_SERIES
|
||||
EVC_LPCOMP, // jswrap_espruino: E.setComparator / E.on("comparator" event
|
||||
#endif
|
||||
EVC_TYPE_MASK = 255,
|
||||
EVC_DATA_SHIFT = 8,
|
||||
EVC_DATA_LPCOMP_UP = 256
|
||||
} PACKED_FLAGS IOCustomEventFlags;
|
||||
|
||||
@ -185,6 +189,8 @@ typedef enum {
|
||||
|
||||
/// Push an IO event (max IOEVENT_MAX_LEN) into the ioBuffer (designed to be called from IRQ), returns true on success, Calls jshHadEvent();
|
||||
bool CALLED_FROM_INTERRUPT jshPushEvent(IOEventFlags evt, uint8_t *data, unsigned int length);
|
||||
/// Push a Custom IO event into the ioBuffer (designed to be called from IRQ), returns true on success, Calls jshHadEvent();
|
||||
bool CALLED_FROM_INTERRUPT jshPushCustomEvent(IOCustomEventFlags customFlags);
|
||||
/// Add this IO event to the IO event queue. Calls jshHadEvent();
|
||||
void jshPushIOEvent(IOEventFlags channel, JsSysTime time);
|
||||
/// Signal an IO watch event as having happened. Calls jshHadEvent();
|
||||
@ -196,7 +202,7 @@ void jshPushIOCharEvents(IOEventFlags channel, char *data, unsigned int count);
|
||||
|
||||
/// pop an IO event, returns EV_NONE on failure. data must be IOEVENT_MAX_LEN bytes
|
||||
IOEventFlags jshPopIOEvent(uint8_t *data, unsigned int *length);
|
||||
// pop an IO event of type eventType, returns true on success. data must be IOEVENT_MAX_LEN bytes
|
||||
// pop an IO event of type eventType, returns event type on success,EV_NONE on failure. data must be IOEVENT_MAX_LEN bytes
|
||||
IOEventFlags jshPopIOEventOfType(IOEventFlags eventType, uint8_t *data, unsigned int *length);
|
||||
/// Do we have any events pending? Will jshPopIOEvent return true?
|
||||
bool jshHasEvents();
|
||||
|
||||
@ -2204,6 +2204,20 @@ void jsiHandleIOEventForConsole(uint8_t *eventData, int eventLen) {
|
||||
jsiSetBusy(BUSY_INTERACTIVE, false);
|
||||
}
|
||||
|
||||
/** This is called if a EV_RUN_INTERRUPT_JS is received, or when a EXEC_RUN_INTERRUPT_JS is set.
|
||||
It executes JavaScript code that was pushed to the queue by a require("timer").add({type:"EXEC", fn:myFunction... */
|
||||
static void jsiOnRunInterruptJSEvent(const uint8_t *eventData, unsigned int eventLen) {
|
||||
for (unsigned int i=0;i<eventLen-1;i+=2) {
|
||||
JsVarRef codeRef = *(JsVarRef *)&eventData[i];
|
||||
if (codeRef) {
|
||||
JsVar *code = jsvLock(codeRef);
|
||||
if (!jsvIsFunction(code)) return; // invalid code - maybe things got moved?
|
||||
jsvUnLock(jspExecuteFunction(code, execInfo.root, 0, NULL));
|
||||
jsvUnLock(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void jsiIdle() {
|
||||
// This is how many times we have been here and not done anything.
|
||||
// It will be zeroed if we do stuff later
|
||||
@ -2248,7 +2262,10 @@ void jsiIdle() {
|
||||
}
|
||||
jsvUnLock(usartClass);
|
||||
#endif
|
||||
} else if (eventType == EV_RUN_INTERRUPT_JS) {
|
||||
jsiOnRunInterruptJSEvent(eventData, eventLen);
|
||||
} else if (eventType == EV_CUSTOM) {
|
||||
jstOnCustomEvent(eventFlags, eventData, eventLen);
|
||||
jswOnCustomEvent(eventFlags, eventData, eventLen);
|
||||
#ifdef BLUETOOTH
|
||||
} else if (eventType == EV_BLUETOOTH_PENDING) {
|
||||
@ -3000,3 +3017,13 @@ void jsiDebuggerLine(JsVar *line) {
|
||||
jslSetLex(oldLex);
|
||||
}
|
||||
#endif // USE_DEBUGGER
|
||||
|
||||
/** This is called from the parser if EXEC_RUN_INTERRUPT_JS is set.
|
||||
It executes JavaScript code that was pushed to the queue by require("timer").add({type:"EXEC", fn:myFunction... */
|
||||
void jsiRunInterruptingJS() {
|
||||
uint8_t data[8];
|
||||
memset(data, 0, sizeof(data));
|
||||
if (jshPopIOEventOfType(EV_RUN_INTERRUPT_JS, data, sizeof(data)))
|
||||
jsiOnRunInterruptJSEvent(data, sizeof(data));
|
||||
}
|
||||
|
||||
|
||||
@ -201,5 +201,8 @@ extern void jsiTimersChanged(); // Flag timers changed so we can skip out of the
|
||||
extern void jsiDebuggerLoop(); ///< Enter the debugger loop
|
||||
#endif
|
||||
|
||||
/** This is called from the parser if EXEC_RUN_INTERRUPT_JS is set.
|
||||
It executes JavaScript code that was pushed to the queue by require("timer").add({type:"EXEC", fn:myFunction... */
|
||||
void jsiRunInterruptingJS();
|
||||
|
||||
#endif /* JSINTERACTIVE_H_ */
|
||||
|
||||
@ -2959,6 +2959,10 @@ NO_INLINE JsVar *jspeStatement() {
|
||||
jsiDebuggerLoop();
|
||||
}
|
||||
#endif
|
||||
if (execInfo.execute&EXEC_RUN_INTERRUPT_JS) {
|
||||
execInfo.execute&=~EXEC_RUN_INTERRUPT_JS;
|
||||
jsiRunInterruptingJS();
|
||||
}
|
||||
if (lex->tk==LEX_ID ||
|
||||
lex->tk==LEX_INT ||
|
||||
lex->tk==LEX_FLOAT ||
|
||||
|
||||
@ -98,7 +98,7 @@ typedef enum {
|
||||
EXEC_INTERRUPTED = 16, ///< true if execution has been interrupted
|
||||
EXEC_EXCEPTION = 32, ///< we had an exception, so don't execute until we hit a try/catch block
|
||||
EXEC_ERROR = 64,
|
||||
// 128 is free now
|
||||
EXEC_RUN_INTERRUPT_JS = 128, ///< when set, we should stop our execution to run some JavaScript code (used for immediate timers)
|
||||
|
||||
EXEC_FOR_INIT = 256, ///< when in for initialiser parsing - hack to avoid getting confused about multiple use for IN
|
||||
EXEC_IN_LOOP = 512, ///< when in a loop, set this - we can then block break/continue outside it
|
||||
|
||||
336
src/jstimer.c
336
src/jstimer.c
@ -16,7 +16,9 @@
|
||||
#include "jsinteractive.h"
|
||||
|
||||
/// Data for our tasks (eg when, what they are, etc)
|
||||
UtilTimerTask utilTimerTasks[UTILTIMERTASK_TASKS];
|
||||
UtilTimerTask utilTimerTaskInfo[UTILTIMERTASK_TASKS];
|
||||
/// List of tasks in time order (implemented as a circular buffer)
|
||||
uint8_t utilTimerTasks[UTILTIMERTASK_TASKS];
|
||||
/// queue beginning (push tasks on here)
|
||||
volatile unsigned char utilTimerTasksHead = 0;
|
||||
/// queue end (tasks pop off here as they are complete)
|
||||
@ -45,7 +47,7 @@ static void jstUtilTimerSetupBuffer(UtilTimerTask *task) {
|
||||
}
|
||||
}
|
||||
|
||||
static void jstUtilTimerInterruptHandlerNextByte(UtilTimerTask *task) {
|
||||
static void jstUtilTimerInterruptHandlerNextByte(UtilTimerTask *task, uint8_t timerId) {
|
||||
// move to next element in var
|
||||
task->data.buffer.charIdx++;
|
||||
if (task->data.buffer.charIdx >= task->data.buffer.endIdx) {
|
||||
@ -66,6 +68,8 @@ static void jstUtilTimerInterruptHandlerNextByte(UtilTimerTask *task) {
|
||||
task->data.buffer.currentBuffer = t;
|
||||
// Setup new buffer
|
||||
jstUtilTimerSetupBuffer(task);
|
||||
// notify rest of interpreter
|
||||
jshPushCustomEvent(EVC_TIMER_BUFFER_FLIP | (timerId<<EVC_DATA_SHIFT));
|
||||
} else {
|
||||
task->data.buffer.var = 0;
|
||||
// No more data - make sure we don't repeat!
|
||||
@ -83,6 +87,17 @@ static inline unsigned char *jstUtilTimerInterruptHandlerByte(UtilTimerTask *tas
|
||||
}
|
||||
#endif
|
||||
|
||||
// Handle sending an event when the task is finished
|
||||
static void jstUtilTimerTaskIsFinished(int timerId) {
|
||||
UtilTimerTask *task = &utilTimerTaskInfo[timerId];
|
||||
if (UET_EVENT_SEND_TIMER_FINISHED(task->type)) {
|
||||
task->type |= UET_FINISHED;
|
||||
jshPushCustomEvent(EVC_TIMER_FINISHED | (timerId<<EVC_DATA_SHIFT));
|
||||
} else {
|
||||
task->type = UET_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
void jstUtilTimerInterruptHandler() {
|
||||
/* Note: we're using 32 bit times here, even though the real time counter is 64 bit. We
|
||||
* just make sure nothing is scheduled that far in the future */
|
||||
@ -93,13 +108,14 @@ void jstUtilTimerInterruptHandler() {
|
||||
current time. */
|
||||
int t = utilTimerTasksTail;
|
||||
while (t!=utilTimerTasksHead) {
|
||||
utilTimerTasks[t].time -= utilTimerPeriod;
|
||||
utilTimerTaskInfo[utilTimerTasks[t]].time -= utilTimerPeriod;
|
||||
t = (t+1) & (UTILTIMERTASK_TASKS-1);
|
||||
}
|
||||
utilTimerOffset += utilTimerPeriod;
|
||||
// Check timers and execute any timers that are due
|
||||
while (utilTimerTasksTail!=utilTimerTasksHead && utilTimerTasks[utilTimerTasksTail].time <= 0) {
|
||||
UtilTimerTask *task = &utilTimerTasks[utilTimerTasksTail];
|
||||
while (utilTimerTasksTail!=utilTimerTasksHead && utilTimerTaskInfo[utilTimerTasks[utilTimerTasksTail]].time <= 0) {
|
||||
uint8_t timerId = utilTimerTasks[utilTimerTasksTail];
|
||||
UtilTimerTask *task = &utilTimerTaskInfo[timerId];
|
||||
void (*executeFn)(JsSysTime time, void* userdata) = 0;
|
||||
void *executeData = 0;
|
||||
switch (task->type) {
|
||||
@ -119,15 +135,15 @@ void jstUtilTimerInterruptHandler() {
|
||||
if (!task->data.buffer.var) break;
|
||||
int v = jshPinAnalogFast(task->data.buffer.pin);
|
||||
*jstUtilTimerInterruptHandlerByte(task) = (unsigned char)v; // LSB first
|
||||
jstUtilTimerInterruptHandlerNextByte(task);
|
||||
jstUtilTimerInterruptHandlerNextByte(task, timerId);
|
||||
*jstUtilTimerInterruptHandlerByte(task) = (unsigned char)(v >> 8);
|
||||
jstUtilTimerInterruptHandlerNextByte(task);
|
||||
jstUtilTimerInterruptHandlerNextByte(task, timerId);
|
||||
break;
|
||||
}
|
||||
case UET_READ_BYTE: {
|
||||
if (!task->data.buffer.var) break;
|
||||
*jstUtilTimerInterruptHandlerByte(task) = (unsigned char)(jshPinAnalogFast(task->data.buffer.pin) >> 8);
|
||||
jstUtilTimerInterruptHandlerNextByte(task);
|
||||
jstUtilTimerInterruptHandlerNextByte(task, timerId);
|
||||
break;
|
||||
}
|
||||
case UET_WRITE_SHORT:
|
||||
@ -137,19 +153,19 @@ void jstUtilTimerInterruptHandler() {
|
||||
int sum;
|
||||
if (task->type == UET_WRITE_SHORT) {
|
||||
sum = *jstUtilTimerInterruptHandlerByte(task); // LSB first
|
||||
jstUtilTimerInterruptHandlerNextByte(task);
|
||||
jstUtilTimerInterruptHandlerNextByte(task, timerId);
|
||||
} else {
|
||||
sum = 0;
|
||||
}
|
||||
sum |= (unsigned short)(*jstUtilTimerInterruptHandlerByte(task) << 8);
|
||||
jstUtilTimerInterruptHandlerNextByte(task);
|
||||
jstUtilTimerInterruptHandlerNextByte(task, timerId);
|
||||
task->data.buffer.currentValue = (unsigned short)sum;
|
||||
// now search for other tasks writing to this pin... (polyphony)
|
||||
int t = (utilTimerTasksTail+1) & (UTILTIMERTASK_TASKS-1);
|
||||
while (t!=utilTimerTasksHead) {
|
||||
if (UET_IS_BUFFER_WRITE_EVENT(utilTimerTasks[t].type) &&
|
||||
utilTimerTasks[t].data.buffer.pin == task->data.buffer.pin)
|
||||
sum += ((int)(unsigned int)utilTimerTasks[t].data.buffer.currentValue) - 32768;
|
||||
if (UET_IS_BUFFER_WRITE_EVENT(utilTimerTaskInfo[utilTimerTasks[t]].type) &&
|
||||
utilTimerTaskInfo[utilTimerTasks[t]].data.buffer.pin == task->data.buffer.pin)
|
||||
sum += ((int)(unsigned int)utilTimerTaskInfo[utilTimerTasks[t]].data.buffer.currentValue) - 32768;
|
||||
t = (t+1) & (UTILTIMERTASK_TASKS-1);
|
||||
}
|
||||
// saturate
|
||||
@ -194,31 +210,30 @@ void jstUtilTimerInterruptHandler() {
|
||||
if (task->repeatInterval) {
|
||||
// update time (we know time > task->time)
|
||||
task->time += task->repeatInterval;
|
||||
|
||||
// do an in-place bubble sort to ensure that times are still in the right order
|
||||
unsigned char ta = utilTimerTasksTail;
|
||||
unsigned char tb = (ta+1) & (UTILTIMERTASK_TASKS-1);
|
||||
while (tb != utilTimerTasksHead) {
|
||||
if (utilTimerTasks[ta].time > utilTimerTasks[tb].time) {
|
||||
UtilTimerTask task = utilTimerTasks[ta];
|
||||
if (utilTimerTaskInfo[utilTimerTasks[ta]].time > utilTimerTaskInfo[utilTimerTasks[tb]].time) {
|
||||
uint8_t idx = utilTimerTasks[ta];
|
||||
utilTimerTasks[ta] = utilTimerTasks[tb];
|
||||
utilTimerTasks[tb] = task;
|
||||
utilTimerTasks[tb] = idx;
|
||||
}
|
||||
ta = tb;
|
||||
tb = (tb+1) & (UTILTIMERTASK_TASKS-1);
|
||||
}
|
||||
} else {
|
||||
// Otherwise no repeat - just go straight to the next one!
|
||||
// Otherwise no repeat - mark this as done and go straight to the next one!
|
||||
jstUtilTimerTaskIsFinished(timerId);
|
||||
utilTimerTasksTail = (utilTimerTasksTail+1) & (UTILTIMERTASK_TASKS-1);
|
||||
}
|
||||
|
||||
// execute the function if we had one (we do this now, because if we did it earlier we'd have to cope with everything changing)
|
||||
if (executeFn) executeFn(jshGetSystemTime(), executeData);
|
||||
}
|
||||
|
||||
// re-schedule the timer if there is something left to do
|
||||
if (utilTimerTasksTail != utilTimerTasksHead) {
|
||||
utilTimerPeriod = utilTimerTasks[utilTimerTasksTail].time;
|
||||
utilTimerPeriod = utilTimerTaskInfo[utilTimerTasks[utilTimerTasksTail]].time;
|
||||
if (utilTimerPeriod<0) utilTimerPeriod=0;
|
||||
jshUtilTimerReschedule(utilTimerPeriod);
|
||||
} else {
|
||||
@ -253,32 +268,41 @@ static bool utilTimerIsFull() {
|
||||
|
||||
/* Restart the utility timer with the right period. This should not normally
|
||||
need to be called by anything outside jstimer.c */
|
||||
void jstRestartUtilTimer() {
|
||||
utilTimerPeriod = utilTimerTasks[utilTimerTasksTail].time;
|
||||
void jstRestartUtilTimer() {
|
||||
utilTimerPeriod = utilTimerTaskInfo[utilTimerTasks[utilTimerTasksTail]].time;
|
||||
utilTimerSetTime = jshGetSystemTime();
|
||||
if (utilTimerPeriod<0) utilTimerPeriod=0;
|
||||
jshUtilTimerStart(utilTimerPeriod);
|
||||
}
|
||||
|
||||
/// Get the index of next free task slot, or -1 if none free
|
||||
int utilTimerGetUnusedIndex(bool wait) {
|
||||
if (wait)
|
||||
WAIT_UNTIL(!utilTimerIsFull(), "Utility Timer");
|
||||
for (int i=0;i<UTILTIMERTASK_TASKS;i++)
|
||||
if (utilTimerTaskInfo[i].type == UET_NONE)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** Queue a task up to be executed when a timer fires... return false on failure.
|
||||
* task.time is the delay at which to execute the task. If timerOffset!==NULL then
|
||||
* task.time is relative to the time at which timerOffset=jstGetUtilTimerOffset().
|
||||
* This allows pulse trains/etc to be scheduled in sync.
|
||||
*/
|
||||
bool utilTimerInsertTask(UtilTimerTask *task, uint32_t *timerOffset) {
|
||||
// check if queue is full or not
|
||||
if (utilTimerIsFull()) return false;
|
||||
jshInterruptOff();
|
||||
void utilTimerInsertTask(uint8_t taskIdx, uint32_t *timerOffset) {
|
||||
assert(!utilTimerIsFull()); // queue should not be full since we had to allocate a task to get the index
|
||||
UtilTimerTask *task = &utilTimerTaskInfo[taskIdx];
|
||||
|
||||
jshInterruptOff();
|
||||
// See above - keep times in sync
|
||||
if (timerOffset)
|
||||
task->time += (int)*timerOffset - (int)utilTimerOffset;
|
||||
|
||||
// How long was it since the timer was last scheduled? Update existing tasks #2575
|
||||
uint32_t timePassed = jshGetSystemTime() - utilTimerSetTime;
|
||||
int timePassed = utilTimerOn ? (jshGetSystemTime() - utilTimerSetTime) : 0;
|
||||
// find out where to insert
|
||||
unsigned char insertPos = utilTimerTasksTail;
|
||||
while (insertPos != utilTimerTasksHead && utilTimerTasks[insertPos].time < (task->time+timePassed))
|
||||
while (insertPos != utilTimerTasksHead && utilTimerTaskInfo[utilTimerTasks[insertPos]].time < (task->time+timePassed))
|
||||
insertPos = (insertPos+1) & (UTILTIMERTASK_TASKS-1);
|
||||
bool haveChangedTimer = insertPos==utilTimerTasksTail;
|
||||
//jsiConsolePrintf("Insert at %d, Tail is %d\n",insertPos,utilTimerTasksTail);
|
||||
@ -295,17 +319,16 @@ bool utilTimerInsertTask(UtilTimerTask *task, uint32_t *timerOffset) {
|
||||
if (haveChangedTimer) { // timer will change - update all tasks
|
||||
i = utilTimerTasksTail;
|
||||
while (i != utilTimerTasksHead) {
|
||||
if (utilTimerTasks[i].time > timePassed)
|
||||
utilTimerTasks[i].time -= timePassed;
|
||||
if (utilTimerTaskInfo[utilTimerTasks[i]].time > timePassed)
|
||||
utilTimerTaskInfo[utilTimerTasks[i]].time -= timePassed;
|
||||
else
|
||||
utilTimerTasks[i].time = 0;
|
||||
utilTimerTaskInfo[utilTimerTasks[i]].time = 0;
|
||||
i = (i+1) & (UTILTIMERTASK_TASKS-1);
|
||||
}
|
||||
} else // timer hasn't changed, we have to update our task's time
|
||||
task->time += timePassed;
|
||||
// add new item
|
||||
utilTimerTasks[insertPos] = *task;
|
||||
|
||||
utilTimerTasks[insertPos] = taskIdx;
|
||||
//jsiConsolePrint("Head is %d\n", utilTimerTasksHead);
|
||||
// now set up timer if not already set up...
|
||||
if (!utilTimerOn || haveChangedTimer) {
|
||||
@ -313,19 +336,27 @@ bool utilTimerInsertTask(UtilTimerTask *task, uint32_t *timerOffset) {
|
||||
jstRestartUtilTimer();
|
||||
}
|
||||
jshInterruptOn();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Remove the task that that 'checkCallback' returns true for. Returns false if none found
|
||||
bool utilTimerRemoveTask(bool (checkCallback)(UtilTimerTask *task, void* data), void *checkCallbackData) {
|
||||
/// Find a task that 'checkCallback' returns true for. Returns -1 if none found
|
||||
int utilTimerFindTask(bool (checkCallback)(UtilTimerTask *task, void* data), void *checkCallbackData) {
|
||||
for (int i=0;i<UTILTIMERTASK_TASKS;i++)
|
||||
if (checkCallback(&utilTimerTaskInfo[i], checkCallbackData))
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// Remove the task with the given ID. Also sets type to UET_NONE. Returns false if none found
|
||||
bool utilTimerRemoveTask(int id) {
|
||||
jshInterruptOff();
|
||||
jstUtilTimerTaskIsFinished(id); // queue events for task finished
|
||||
unsigned char ptr = utilTimerTasksHead;
|
||||
if (ptr != utilTimerTasksTail) {
|
||||
unsigned char endPtr = ((utilTimerTasksTail+UTILTIMERTASK_TASKS-1) & (UTILTIMERTASK_TASKS-1));
|
||||
ptr = (ptr+UTILTIMERTASK_TASKS-1) & (UTILTIMERTASK_TASKS-1);
|
||||
// now we're at the last timer task - work back until we've just gone back past utilTimerTasksTail
|
||||
while (ptr != endPtr) {
|
||||
if (checkCallback(&utilTimerTasks[ptr], checkCallbackData)) {
|
||||
if (utilTimerTasks[ptr]==id) {
|
||||
// shift tail back along
|
||||
unsigned char next = (ptr+UTILTIMERTASK_TASKS-1) & (UTILTIMERTASK_TASKS-1);
|
||||
while (next!=endPtr) {
|
||||
@ -335,6 +366,7 @@ bool utilTimerRemoveTask(bool (checkCallback)(UtilTimerTask *task, void* data),
|
||||
}
|
||||
// move 'end' pointer back
|
||||
utilTimerTasksTail = (utilTimerTasksTail+1) & (UTILTIMERTASK_TASKS-1);
|
||||
utilTimerTaskInfo[id].type = UET_NONE;
|
||||
jshInterruptOn();
|
||||
return true;
|
||||
}
|
||||
@ -347,14 +379,15 @@ bool utilTimerRemoveTask(bool (checkCallback)(UtilTimerTask *task, void* data),
|
||||
|
||||
/// If 'checkCallback' returns true for a task, set 'task' to it and return true. Returns false if none found
|
||||
bool utilTimerGetLastTask(bool (checkCallback)(UtilTimerTask *task, void* data), void *checkCallbackData, UtilTimerTask *task) {
|
||||
// FIXME: we should return the task index here, and just look over utilTimerTaskInfo outside of an IRQ
|
||||
jshInterruptOff();
|
||||
unsigned char ptr = utilTimerTasksHead;
|
||||
if (ptr != utilTimerTasksTail) {
|
||||
ptr = (ptr+UTILTIMERTASK_TASKS-1) & (UTILTIMERTASK_TASKS-1);
|
||||
// now we're at the last timer task - work back until we've just gone back past utilTimerTasksTail
|
||||
while (ptr != ((utilTimerTasksTail+UTILTIMERTASK_TASKS-1) & (UTILTIMERTASK_TASKS-1))) {
|
||||
if (checkCallback(&utilTimerTasks[ptr], checkCallbackData)) {
|
||||
*task = utilTimerTasks[ptr];
|
||||
if (checkCallback(&utilTimerTaskInfo[utilTimerTasks[ptr]], checkCallbackData)) {
|
||||
*task = utilTimerTaskInfo[utilTimerTasks[ptr]];
|
||||
jshInterruptOn();
|
||||
return true;
|
||||
}
|
||||
@ -399,18 +432,6 @@ static bool jstExecuteTaskChecker(UtilTimerTask *task, void *data) {
|
||||
return memcmp(&task->data.execute, (UtilTimerTaskExec*)data, sizeof(UtilTimerTaskExec))==0;
|
||||
}
|
||||
|
||||
#ifdef ESPR_USE_STEPPER_TIMER
|
||||
// data = *Pin[4]
|
||||
static bool jstStepTaskChecker(UtilTimerTask *task, void *data) {
|
||||
if (task->type != UET_STEP) return false;
|
||||
Pin *pins = (Pin*)data;
|
||||
for (int i=0;i<4;i++)
|
||||
if (task->data.step.pins[i] != pins[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
@ -429,17 +450,18 @@ bool jstGetLastBufferTimerTask(JsVar *var, UtilTimerTask *task) {
|
||||
|
||||
bool jstPinOutputAtTime(JsSysTime time, uint32_t *timerOffset, Pin *pins, int pinCount, uint8_t value) {
|
||||
assert(pinCount<=UTILTIMERTASK_PIN_COUNT);
|
||||
UtilTimerTask task;
|
||||
task.time = (int)time;
|
||||
task.repeatInterval = 0;
|
||||
task.type = UET_SET;
|
||||
int idx = utilTimerGetUnusedIndex(true/*wait*/);
|
||||
if (idx<0) return false; // no free tasks even after waiting
|
||||
UtilTimerTask *task = &utilTimerTaskInfo[idx];
|
||||
task->type = UET_SET;
|
||||
task->time = (int)time;
|
||||
task->repeatInterval = 0;
|
||||
int i;
|
||||
for (i=0;i<UTILTIMERTASK_PIN_COUNT;i++)
|
||||
task.data.set.pins[i] = (Pin)((i<pinCount) ? pins[i] : PIN_UNDEFINED);
|
||||
task.data.set.value = value;
|
||||
|
||||
WAIT_UNTIL(!utilTimerIsFull(), "Utility Timer");
|
||||
return utilTimerInsertTask(&task, timerOffset);
|
||||
task->data.set.pins[i] = (Pin)((i<pinCount) ? pins[i] : PIN_UNDEFINED);
|
||||
task->data.set.value = value;
|
||||
utilTimerInsertTask(idx, timerOffset);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Do software PWM on the given pin, using the timer IRQs
|
||||
@ -447,7 +469,9 @@ bool jstPinPWM(JsVarFloat freq, JsVarFloat dutyCycle, Pin pin) {
|
||||
// if anything is wrong, exit now
|
||||
if (dutyCycle<=0 || dutyCycle>=1 || freq<=0) {
|
||||
// remove any timer tasks
|
||||
while (utilTimerRemoveTask(jstPinTaskChecker, (void*)&pin));
|
||||
int taskID;
|
||||
while ((taskID = utilTimerFindTask(jstPinTaskChecker, (void*)&pin)) >= 0)
|
||||
utilTimerRemoveTask(taskID);
|
||||
// Now set pin to the correct state and exit
|
||||
jshPinSetValue(pin, dutyCycle >= 0.5);
|
||||
return false;
|
||||
@ -468,11 +492,11 @@ bool jstPinPWM(JsVarFloat freq, JsVarFloat dutyCycle, Pin pin) {
|
||||
ptr = (ptr+UTILTIMERTASK_TASKS-1) & (UTILTIMERTASK_TASKS-1);
|
||||
// now we're at the last timer task - work back until we've just gone back past utilTimerTasksTail
|
||||
while (ptr != ((utilTimerTasksTail+UTILTIMERTASK_TASKS-1) & (UTILTIMERTASK_TASKS-1))) {
|
||||
if (jstPinTaskChecker(&utilTimerTasks[ptr], (void*)&pin)) {
|
||||
if (utilTimerTasks[ptr].data.set.value)
|
||||
ptaskon = &utilTimerTasks[ptr];
|
||||
if (jstPinTaskChecker(&utilTimerTaskInfo[utilTimerTasks[ptr]], (void*)&pin)) {
|
||||
if (utilTimerTaskInfo[utilTimerTasks[ptr]].data.set.value)
|
||||
ptaskon = &utilTimerTaskInfo[utilTimerTasks[ptr]];
|
||||
else
|
||||
ptaskoff = &utilTimerTasks[ptr];
|
||||
ptaskoff = &utilTimerTaskInfo[utilTimerTasks[ptr]];
|
||||
}
|
||||
ptr = (ptr+UTILTIMERTASK_TASKS-1) & (UTILTIMERTASK_TASKS-1);
|
||||
}
|
||||
@ -495,48 +519,57 @@ bool jstPinPWM(JsVarFloat freq, JsVarFloat dutyCycle, Pin pin) {
|
||||
|
||||
/// Remove any tasks using the given pin (if they existed)
|
||||
if (ptaskon || ptaskoff) {
|
||||
while (utilTimerRemoveTask(jstPinTaskChecker, (void*)&pin));
|
||||
while (jstStopPinTimerTask(pin));
|
||||
}
|
||||
UtilTimerTask taskon, taskoff;
|
||||
taskon.data.set.value = 1;
|
||||
taskoff.data.set.value = 0;
|
||||
taskon.time = (int)period;
|
||||
taskoff.time = (int)pulseLength;
|
||||
taskon.repeatInterval = (unsigned int)period;
|
||||
taskoff.repeatInterval = (unsigned int)period;
|
||||
taskon.type = UET_SET;
|
||||
taskoff.type = UET_SET;
|
||||
taskon.data.set.pins[0] = pin;
|
||||
taskoff.data.set.pins[0] = pin;
|
||||
|
||||
int onidx = utilTimerGetUnusedIndex(true/*wait*/);
|
||||
if (onidx<0) return false; // no free tasks
|
||||
UtilTimerTask *taskon = &utilTimerTaskInfo[onidx];
|
||||
taskon->type = UET_SET;
|
||||
int offidx = utilTimerGetUnusedIndex(true/*wait*/);
|
||||
if (offidx<0) {
|
||||
taskon->type = UET_NONE; // free last task
|
||||
return false; // no free tasks
|
||||
}
|
||||
UtilTimerTask *taskoff = &utilTimerTaskInfo[offidx];
|
||||
taskoff->type = UET_SET;
|
||||
taskon->data.set.value = 1;
|
||||
taskoff->data.set.value = 0;
|
||||
taskon->time = (int)period;
|
||||
taskoff->time = (int)pulseLength;
|
||||
taskon->repeatInterval = (unsigned int)period;
|
||||
taskoff->repeatInterval = (unsigned int)period;
|
||||
taskon->data.set.pins[0] = pin;
|
||||
taskoff->data.set.pins[0] = pin;
|
||||
int i;
|
||||
for (i=1;i<UTILTIMERTASK_PIN_COUNT;i++) {
|
||||
taskon.data.set.pins[i] = PIN_UNDEFINED;
|
||||
taskoff.data.set.pins[i] = PIN_UNDEFINED;
|
||||
taskon->data.set.pins[i] = PIN_UNDEFINED;
|
||||
taskoff->data.set.pins[i] = PIN_UNDEFINED;
|
||||
}
|
||||
|
||||
// first task is to turn on
|
||||
jshPinSetValue(pin, 1);
|
||||
uint32_t timerOffset = jstGetUtilTimerOffset();
|
||||
// now start the 2 PWM tasks
|
||||
WAIT_UNTIL(!utilTimerIsFull(), "Utility Timer");
|
||||
if (!utilTimerInsertTask(&taskon, &timerOffset)) return false;
|
||||
WAIT_UNTIL(!utilTimerIsFull(), "Utility Timer");
|
||||
return utilTimerInsertTask(&taskoff, &timerOffset);
|
||||
utilTimerInsertTask(onidx, &timerOffset);
|
||||
utilTimerInsertTask(offidx, &timerOffset);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Execute the given function repeatedly after the given time period. If period=0, don't repeat. True on success or false on failure to schedule
|
||||
* See utilTimerInsertTask for notes on timerOffset
|
||||
*/
|
||||
bool jstExecuteFn(UtilTimerTaskExecFn fn, void *userdata, JsSysTime startTime, uint32_t period, uint32_t *timerOffset) {
|
||||
UtilTimerTask task;
|
||||
task.time = (int)startTime;
|
||||
task.repeatInterval = period;
|
||||
task.type = UET_EXECUTE;
|
||||
task.data.execute.fn = fn;
|
||||
task.data.execute.userdata = userdata;
|
||||
|
||||
WAIT_UNTIL(!utilTimerIsFull(), "Utility Timer");
|
||||
return utilTimerInsertTask(&task, timerOffset);
|
||||
int idx = utilTimerGetUnusedIndex(true/*wait*/);
|
||||
if (idx<0) return false; // no free tasks even after waiting
|
||||
UtilTimerTask *task = &utilTimerTaskInfo[idx];
|
||||
task->time = (int)startTime;
|
||||
task->repeatInterval = period;
|
||||
task->type = UET_EXECUTE;
|
||||
task->data.execute.fn = fn;
|
||||
task->data.execute.userdata = userdata;
|
||||
utilTimerInsertTask(idx, timerOffset);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Stop executing the given function
|
||||
@ -544,37 +577,39 @@ bool jstStopExecuteFn(UtilTimerTaskExecFn fn, void *userdata) {
|
||||
UtilTimerTaskExec e;
|
||||
e.fn = fn;
|
||||
e.userdata = userdata;
|
||||
return utilTimerRemoveTask(jstExecuteTaskChecker, (void*)&e);
|
||||
return utilTimerRemoveTask(utilTimerFindTask(jstExecuteTaskChecker, (void*)&e));
|
||||
}
|
||||
|
||||
/// Set the utility timer so we're woken up in whatever time period
|
||||
bool jstSetWakeUp(JsSysTime period) {
|
||||
UtilTimerTask task;
|
||||
task.time = (int)period;
|
||||
task.repeatInterval = 0;
|
||||
task.type = UET_WAKEUP;
|
||||
|
||||
bool hasTimer = false;
|
||||
int wakeupTime = (int)period;
|
||||
int nextTime;
|
||||
|
||||
|
||||
// work out if we're waiting for a timer,
|
||||
// and if so, when it's going to be
|
||||
jshInterruptOff();
|
||||
if (utilTimerTasksTail!=utilTimerTasksHead) {
|
||||
hasTimer = true;
|
||||
nextTime = utilTimerTasks[utilTimerTasksTail].time;
|
||||
nextTime = utilTimerTaskInfo[utilTimerTasks[utilTimerTasksTail]].time;
|
||||
}
|
||||
jshInterruptOn();
|
||||
|
||||
if (hasTimer && task.time >= nextTime) {
|
||||
if (hasTimer && wakeupTime >= nextTime) {
|
||||
// we already had a timer, and it's going to wake us up sooner.
|
||||
// don't create a WAKEUP timer task
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ok = utilTimerInsertTask(&task, NULL);
|
||||
// We wait until the timer is out of the reload event, because the reload event itself would wake us up
|
||||
return ok;
|
||||
int idx = utilTimerGetUnusedIndex(false/*don't wait*/);
|
||||
if (idx<0) return false; // no free tasks!
|
||||
UtilTimerTask *task = &utilTimerTaskInfo[idx];
|
||||
task->type = UET_WAKEUP;
|
||||
task->time = wakeupTime;
|
||||
task->repeatInterval = 0;
|
||||
utilTimerInsertTask(idx, NULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** If the first timer task is a wakeup task, remove it. This stops
|
||||
@ -585,7 +620,7 @@ void jstClearWakeUp() {
|
||||
jshInterruptOff();
|
||||
// while the first item is a wakeup, remove it
|
||||
while (utilTimerTasksTail!=utilTimerTasksHead &&
|
||||
utilTimerTasks[utilTimerTasksTail].type == UET_WAKEUP) {
|
||||
utilTimerTaskInfo[utilTimerTasks[utilTimerTasksTail]].type == UET_WAKEUP) {
|
||||
utilTimerTasksTail = (utilTimerTasksTail+1) & (UTILTIMERTASK_TASKS-1);
|
||||
removedTimer = true;
|
||||
}
|
||||
@ -597,37 +632,39 @@ void jstClearWakeUp() {
|
||||
|
||||
#ifndef SAVE_ON_FLASH
|
||||
|
||||
bool jstStartSignal(JsSysTime startTime, JsSysTime period, Pin pin, Pin npin, JsVar *currentData, JsVar *nextData, UtilTimerEventType type) {
|
||||
int jstStartSignal(JsSysTime startTime, JsSysTime period, Pin pin, Pin npin, JsVar *currentData, JsVar *nextData, UtilTimerEventType type) {
|
||||
assert(jsvIsString(currentData));
|
||||
assert(jsvIsUndefined(nextData) || jsvIsString(nextData));
|
||||
if (!jshIsPinValid(pin)) return false;
|
||||
UtilTimerTask task;
|
||||
task.repeatInterval = (unsigned int)period;
|
||||
task.time = (int)(startTime + period);
|
||||
task.type = type;
|
||||
if (!jshIsPinValid(pin)) return -1;
|
||||
int idx = utilTimerGetUnusedIndex(true/*wait*/);
|
||||
if (idx<0) return -1; // no free tasks!
|
||||
UtilTimerTask *task = &utilTimerTaskInfo[idx];
|
||||
task->type = type;
|
||||
task->repeatInterval = (unsigned int)period;
|
||||
task->time = (int)(startTime + period);
|
||||
if (UET_IS_BUFFER_WRITE_EVENT(type)) {
|
||||
task.data.buffer.pin = pin;
|
||||
task.data.buffer.npin = npin;
|
||||
task->data.buffer.pin = pin;
|
||||
task->data.buffer.npin = npin;
|
||||
} else if (UET_IS_BUFFER_READ_EVENT(type)) {
|
||||
#ifndef LINUX
|
||||
if (pinInfo[pin].analog == JSH_ANALOG_NONE) return false; // no analog...
|
||||
if (pinInfo[pin].analog == JSH_ANALOG_NONE) return -1; // no analog...
|
||||
#endif
|
||||
task.data.buffer.pin = pin;
|
||||
task->data.buffer.pin = pin;
|
||||
} else {
|
||||
assert(0);
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
task.data.buffer.currentBuffer = jsvGetRef(currentData);
|
||||
task->data.buffer.currentBuffer = jsvGetRef(currentData);
|
||||
if (nextData) {
|
||||
// then we're repeating!
|
||||
task.data.buffer.nextBuffer = jsvGetRef(nextData);
|
||||
task->data.buffer.nextBuffer = jsvGetRef(nextData);
|
||||
} else {
|
||||
// then we're not repeating
|
||||
task.data.buffer.nextBuffer = 0;
|
||||
task->data.buffer.nextBuffer = 0;
|
||||
}
|
||||
jstUtilTimerSetupBuffer(&task);
|
||||
|
||||
return utilTimerInsertTask(&task, NULL);
|
||||
jstUtilTimerSetupBuffer(task);
|
||||
utilTimerInsertTask(idx, NULL);
|
||||
return idx;
|
||||
}
|
||||
|
||||
|
||||
@ -635,15 +672,15 @@ bool jstStartSignal(JsSysTime startTime, JsSysTime period, Pin pin, Pin npin, Js
|
||||
/// Remove the task that uses the buffer 'var'
|
||||
bool jstStopBufferTimerTask(JsVar *var) {
|
||||
JsVarRef ref = jsvGetRef(var);
|
||||
return utilTimerRemoveTask(jstBufferTaskChecker, (void*)&ref);
|
||||
return utilTimerRemoveTask(utilTimerFindTask(jstBufferTaskChecker, (void*)&ref));
|
||||
}
|
||||
|
||||
#endif
|
||||
/// Remove the task that uses the given pin
|
||||
bool jstStopPinTimerTask(Pin pin) {
|
||||
return utilTimerRemoveTask(jstPinTaskChecker, (void*)&pin);
|
||||
return utilTimerRemoveTask(utilTimerFindTask(jstPinTaskChecker, (void*)&pin));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
void jstReset() {
|
||||
jshUtilTimerDisable();
|
||||
@ -660,46 +697,13 @@ void jstSystemTimeChanged(JsSysTime diff) {
|
||||
// we don't actually care since timers should hopefully just run based on delays
|
||||
}
|
||||
|
||||
void jstDumpUtilityTimers() {
|
||||
int i;
|
||||
UtilTimerTask uTimerTasks[UTILTIMERTASK_TASKS];
|
||||
jshInterruptOff();
|
||||
for (i=0;i<UTILTIMERTASK_TASKS;i++)
|
||||
uTimerTasks[i] = utilTimerTasks[i];
|
||||
unsigned char uTimerTasksHead = utilTimerTasksHead;
|
||||
unsigned char uTimerTasksTail = utilTimerTasksTail;
|
||||
jshInterruptOn();
|
||||
|
||||
jsiConsolePrintf("Util Timer %s\n", utilTimerOn?"on":"off");
|
||||
unsigned char t = uTimerTasksTail;
|
||||
bool hadTimers = false;
|
||||
while (t!=uTimerTasksHead) {
|
||||
hadTimers = true;
|
||||
|
||||
UtilTimerTask task = uTimerTasks[t];
|
||||
jsiConsolePrintf("%08d us, repeat %08d us : ", (int)(1000*jshGetMillisecondsFromTime(task.time)), (int)(1000*jshGetMillisecondsFromTime(task.repeatInterval)));
|
||||
|
||||
switch (task.type) {
|
||||
case UET_WAKEUP : jsiConsolePrintf("WKUP\n"); break;
|
||||
case UET_SET : jsiConsolePrintf("SET ");
|
||||
for (i=0;i<UTILTIMERTASK_PIN_COUNT;i++)
|
||||
if (task.data.set.pins[i] != PIN_UNDEFINED)
|
||||
jsiConsolePrintf("%p=%d,", task.data.set.pins[i], (task.data.set.value>>i)&1);
|
||||
jsiConsolePrintf("\n");
|
||||
break;
|
||||
#ifndef SAVE_ON_FLASH
|
||||
case UET_WRITE_BYTE : jsiConsolePrintf("WR8\n"); break;
|
||||
case UET_READ_BYTE : jsiConsolePrintf("RD8\n"); break;
|
||||
case UET_WRITE_SHORT : jsiConsolePrintf("WR16\n"); break;
|
||||
case UET_READ_SHORT : jsiConsolePrintf("RD16\n"); break;
|
||||
#endif
|
||||
case UET_EXECUTE : jsiConsolePrintf("EXEC %x(%x)\n", task.data.execute.fn, task.data.execute.userdata); break;
|
||||
default : jsiConsolePrintf("?[%d]\n", task.type); break;
|
||||
/// Called from the idle loop if a custom event is received (could be for the timer)
|
||||
void jstOnCustomEvent(IOEventFlags eventFlags, uint8_t *data, int dataLen) {
|
||||
IOCustomEventFlags customFlags = *(IOCustomEventFlags*)data;
|
||||
if ((customFlags&EVC_TYPE_MASK)==EVC_TIMER_FINISHED) {
|
||||
int id = customFlags >> EVC_DATA_SHIFT;
|
||||
if (utilTimerTaskInfo[id].type & UET_FINISHED) {
|
||||
utilTimerTaskInfo[id].type = UET_NONE;
|
||||
}
|
||||
|
||||
t = (t+1) & (UTILTIMERTASK_TASKS-1);
|
||||
}
|
||||
if (!hadTimers)
|
||||
jsiConsolePrintf("No Timers found.\n");
|
||||
|
||||
}
|
||||
|
||||
@ -32,6 +32,7 @@ typedef enum {
|
||||
#ifdef ESPR_USE_STEPPER_TIMER
|
||||
UET_STEP, ///< Write stepper motor
|
||||
#endif
|
||||
UET_FINISHED = 16, ///< OR this into a timer task to flag it as finished (it's then cleaned up outside the IRQ)
|
||||
} PACKED_FLAGS UtilTimerEventType;
|
||||
|
||||
#define UET_IS_SET_EVENT(T) (\
|
||||
@ -55,6 +56,23 @@ typedef enum {
|
||||
|
||||
#define UTILTIMERTASK_PIN_COUNT (8)
|
||||
|
||||
#ifdef ESPR_USE_STEPPER_TIMER
|
||||
#define UET_IS_STEPPER(T) ((T)==UET_STEP)
|
||||
#define UET_PIN_COUNT(T) (((T)==UET_STEP) ? 4 : UTILTIMERTASK_PIN_COUNT)
|
||||
#else
|
||||
#define UET_IS_STEPPER(T) ((T)==UET_SET)
|
||||
#define UET_PIN_COUNT(T) UTILTIMERTASK_PIN_COUNT
|
||||
#endif
|
||||
|
||||
#define UET_EVENT_HAS_PINS(T) (((T)==UET_SET) || UET_IS_STEPPER(T))
|
||||
|
||||
// Should we send EVC_TIMER_FINISHED for an event? Some like pin events are fast so we don't want to fill our buffer with them
|
||||
#define UET_EVENT_SEND_TIMER_FINISHED(T) (\
|
||||
((T)==UET_EXECUTE) || \
|
||||
UET_IS_BUFFER_EVENT(T) || \
|
||||
UET_IS_STEPPER(T) \
|
||||
)
|
||||
|
||||
typedef struct UtilTimerTaskSet {
|
||||
Pin pins[UTILTIMERTASK_PIN_COUNT]; ///< pins to set (must be in same location as UtilTimerTaskStep.pins)
|
||||
uint8_t value; ///< value to set pins to
|
||||
@ -108,6 +126,10 @@ typedef struct UtilTimerTask {
|
||||
UtilTimerEventType type; // the type of this task - do we set pin(s) or read/write data
|
||||
} PACKED_FLAGS UtilTimerTask;
|
||||
|
||||
/// Data for our tasks (eg when, what they are, etc)
|
||||
extern UtilTimerTask utilTimerTaskInfo[UTILTIMERTASK_TASKS];
|
||||
|
||||
/// Called from the utility timer interrupt to handle timer events
|
||||
void jstUtilTimerInterruptHandler();
|
||||
|
||||
/// Wait until the utility timer is totally empty (use with care as timers can repeat)
|
||||
@ -149,8 +171,8 @@ bool jstSetWakeUp(JsSysTime period);
|
||||
* before the wakeup event */
|
||||
void jstClearWakeUp();
|
||||
|
||||
/// Start writing a string out at the given period between samples. 'time' is the time relative to the current time (0 = now). pin_neg is optional pin for writing opposite of signal to
|
||||
bool jstStartSignal(JsSysTime startTime, JsSysTime period, Pin pin, Pin npin, JsVar *currentData, JsVar *nextData, UtilTimerEventType type);
|
||||
/// Start writing a string out at the given period between samples. 'time' is the time relative to the current time (0 = now). pin_neg is optional pin for writing opposite of signal to. Returns -1 on failure or timer ID on success
|
||||
int jstStartSignal(JsSysTime startTime, JsSysTime period, Pin pin, Pin npin, JsVar *currentData, JsVar *nextData, UtilTimerEventType type);
|
||||
|
||||
/// Remove the task that uses the buffer 'var'
|
||||
bool jstStopBufferTimerTask(JsVar *var);
|
||||
@ -165,26 +187,31 @@ void jstReset();
|
||||
This should be done with interrupts off */
|
||||
void jstSystemTimeChanged(JsSysTime diff);
|
||||
|
||||
/// Dump the current list of timers
|
||||
void jstDumpUtilityTimers();
|
||||
|
||||
/* Restart the utility timer with the right period. This should not normally
|
||||
need to be called by anything outside jstimer.c */
|
||||
void jstRestartUtilTimer();
|
||||
void jstRestartUtilTimer();
|
||||
|
||||
/// Get the index of next free task slot, or -1 if none free. If 'wait=true' will wait until one is free
|
||||
int utilTimerGetUnusedIndex(bool wait);
|
||||
|
||||
/** Queue a task up to be executed when a timer fires... return false on failure.
|
||||
* task.time is the delay at which to execute the task. If timerOffset!==NULL then
|
||||
* task.time is relative to the time at which timerOffset=jstGetUtilTimerOffset().
|
||||
* This allows pulse trains/etc to be scheduled in sync.
|
||||
*/
|
||||
bool utilTimerInsertTask(UtilTimerTask *task, uint32_t *timerOffset);
|
||||
void utilTimerInsertTask(uint8_t taskIdx, uint32_t *timerOffset);
|
||||
|
||||
/// Remove the task that that 'checkCallback' returns true for. Returns false if none found
|
||||
bool utilTimerRemoveTask(bool (checkCallback)(UtilTimerTask *task, void* data), void *checkCallbackData);
|
||||
/// Find a task that 'checkCallback' returns true for. Returns -1 if none found
|
||||
int utilTimerFindTask(bool (checkCallback)(UtilTimerTask *task, void* data), void *checkCallbackData);
|
||||
|
||||
/// Remove the task with the given ID. Also sets type to UET_NONE. Returns false if none found
|
||||
bool utilTimerRemoveTask(int id);
|
||||
|
||||
/// If 'checkCallback' returns true for a task, set 'task' to it and return true. Returns false if none found
|
||||
bool utilTimerGetLastTask(bool (checkCallback)(UtilTimerTask *task, void* data), void *checkCallbackData, UtilTimerTask *task);
|
||||
|
||||
/// Called from the idle loop if a custom event is received (could be for the timer)
|
||||
void jstOnCustomEvent(IOEventFlags eventFlags, uint8_t *data, int dataLen);
|
||||
|
||||
#endif /* JSTIMER_H_ */
|
||||
|
||||
|
||||
12
src/jsvar.c
12
src/jsvar.c
@ -2684,6 +2684,10 @@ bool jsvIsStringIEqualAndUnLock(JsVar *var, const char *str) {
|
||||
jsvUnLock(var);
|
||||
return b;
|
||||
}
|
||||
// Also see jsvIsBasicVarEqual
|
||||
bool jsvIsStringIEqual(JsVar *var, const char *str) {
|
||||
return jsvIsStringEqualOrStartsWithOffset(var, str, false, 0, true);
|
||||
}
|
||||
|
||||
|
||||
/** Compare 2 strings, starting from the given character positions. equalAtEndOfString means that
|
||||
@ -3529,6 +3533,12 @@ void jsvSetArrayItem(JsVar *arr, JsVarInt index, JsVar *item) {
|
||||
jsvUnLock(indexVar);
|
||||
}
|
||||
|
||||
void jsvRemoveArrayItem(JsVar *arr, JsVarInt index) {
|
||||
JsVar *indexVar = jsvGetArrayIndex(arr, index);
|
||||
if (indexVar)
|
||||
jsvRemoveChildAndUnLock(arr, indexVar);
|
||||
}
|
||||
|
||||
// Get all elements from arr and put them in itemPtr (unless it'd overflow).
|
||||
// Makes sure all of itemPtr either contains a JsVar or 0
|
||||
void jsvGetArrayItems(JsVar *arr, unsigned int itemCount, JsVar **itemPtr) {
|
||||
@ -4617,6 +4627,8 @@ bool jsvReadConfigObject(JsVar *object, jsvConfigObject *configs, int nConfigs)
|
||||
case JSV_STRING_0:
|
||||
case JSV_ARRAY:
|
||||
case JSV_FUNCTION:
|
||||
if (*((JsVar**)configs[i].ptr))
|
||||
jsvUnLock(*((JsVar**)configs[i].ptr));
|
||||
*((JsVar**)configs[i].ptr) = jsvLockAgain(val); break;
|
||||
#ifndef ESPR_EMBED
|
||||
case JSV_PIN: *((Pin*)configs[i].ptr) = jshGetPinFromVar(val); break;
|
||||
|
||||
@ -527,6 +527,7 @@ bool jsvIsStringEqualOrStartsWithOffset(JsVar *var, const char *str, bool isStar
|
||||
*/
|
||||
bool jsvIsStringEqualOrStartsWith(JsVar *var, const char *str, bool isStartsWith);
|
||||
bool jsvIsStringEqual(JsVar *var, const char *str); ///< is string equal. see jsvIsStringEqualOrStartsWith
|
||||
bool jsvIsStringIEqual(JsVar *var, const char *str); ///< is string equal (ignoring case). see jsvIsStringEqualOrStartsWithOffset
|
||||
bool jsvIsStringIEqualAndUnLock(JsVar *var, const char *str); ///< is string equal (ignoring case). see jsvIsStringEqualOrStartsWithOffset
|
||||
int jsvCompareString(JsVar *va, JsVar *vb, size_t starta, size_t startb, bool equalAtEndOfString); ///< Compare 2 strings, starting from the given character positions
|
||||
/// Return a new string containing just the characters that are shared between two strings.
|
||||
@ -739,6 +740,7 @@ JsVar *jsvGetArrayIndex(const JsVar *arr, JsVarInt index); ///< Get a 'name' at
|
||||
JsVar *jsvGetArrayItem(const JsVar *arr, JsVarInt index); ///< Get an item at the specified index in the array if it exists (and lock it)
|
||||
JsVar *jsvGetLastArrayItem(const JsVar *arr); ///< Returns the last item in the given array (with string OR numeric index)
|
||||
void jsvSetArrayItem(JsVar *arr, JsVarInt index, JsVar *item); ///< Set an array item at the specified index in the array
|
||||
void jsvRemoveArrayItem(JsVar *arr, JsVarInt index); ///< Remove an item from the array (does not change the array length)
|
||||
void jsvGetArrayItems(JsVar *arr, unsigned int itemCount, JsVar **itemPtr); ///< Get all elements from arr and put them in itemPtr (unless it'd overflow). Makes sure all of itemPtr either contains a JsVar or 0
|
||||
JsVar *jsvGetIndexOfFull(JsVar *arr, JsVar *value, bool matchExact, bool matchIntegerIndices, int startIdx); ///< Get the index of the value in the array (matchExact==use pointer not equality check, matchIntegerIndices = don't check non-integers)
|
||||
JsVar *jsvGetIndexOf(JsVar *arr, JsVar *value, bool matchExact); ///< Get the index of the value in the array or object (matchExact==use pointer, not equality check)
|
||||
|
||||
@ -1529,20 +1529,6 @@ int jswrap_espruino_reverseByte(int v) {
|
||||
return (((b * 0x0802LU & 0x22110LU) | (b * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16) & 0xFF;
|
||||
}
|
||||
|
||||
|
||||
/*JSON{
|
||||
"type" : "staticmethod",
|
||||
"class" : "E",
|
||||
"name" : "dumpTimers",
|
||||
"ifndef" : "SAVE_ON_FLASH",
|
||||
"generate" : "jswrap_espruino_dumpTimers"
|
||||
}
|
||||
Output the current list of Utility Timer Tasks - for debugging only
|
||||
*/
|
||||
void jswrap_espruino_dumpTimers() {
|
||||
jstDumpUtilityTimers();
|
||||
}
|
||||
|
||||
/*JSON{
|
||||
"type" : "staticmethod",
|
||||
"class" : "E",
|
||||
|
||||
@ -51,7 +51,6 @@ void jswrap_espruino_setConsole(JsVar *device, JsVar *options);
|
||||
JsVar *jswrap_espruino_getConsole();
|
||||
|
||||
int jswrap_espruino_reverseByte(int v);
|
||||
void jswrap_espruino_dumpTimers();
|
||||
void jswrap_espruino_dumpLockedVars();
|
||||
void jswrap_espruino_dumpFreeList();
|
||||
void jswrap_e_dumpFragmentation();
|
||||
|
||||
@ -269,19 +269,20 @@ void jswrap_io_digitalPulse(Pin pin, bool value, JsVar *times) {
|
||||
uint32_t timerOffset = jstGetUtilTimerOffset();
|
||||
bool hasTimer = jstGetLastPinTimerTask(pin, &task);
|
||||
if (!hasTimer) task.time = 0;
|
||||
// set first edge
|
||||
if (!hasTimer)
|
||||
jshPinOutput(pin, value);
|
||||
// now start either one or a series of pulses
|
||||
if (jsvIsNumeric(times)) {
|
||||
JsVarFloat pulseTime = jsvGetFloat(times);
|
||||
if (pulseTime<0 || isnan(pulseTime)) {
|
||||
jsExceptionHere(JSET_ERROR, "Pulse Time is less than 0 or not a number");
|
||||
} else if (pulseTime>0) {
|
||||
if (!hasTimer) jshPinOutput(pin, value);
|
||||
task.time += jshGetTimeFromMilliseconds(pulseTime);
|
||||
jstPinOutputAtTime(task.time, &timerOffset, &pin, 1, !value);
|
||||
} else jstUtilTimerWaitEmpty(); // time==0
|
||||
} else if (jsvIsIterable(times)) {
|
||||
// iterable, so output a square wave
|
||||
if (!hasTimer) jshPinOutput(pin, value);
|
||||
JsvIterator it;
|
||||
jsvIteratorNew(&it, times, JSIF_EVERY_ARRAY_ELEMENT);
|
||||
while (jsvIteratorHasElement(&it)) {
|
||||
|
||||
@ -131,29 +131,6 @@ void jswrap_pin_write(
|
||||
jshPinOutput(pin, value);
|
||||
}
|
||||
|
||||
/*JSON{
|
||||
"type" : "method",
|
||||
"class" : "Pin",
|
||||
"name" : "writeAtTime",
|
||||
"ifndef" : "SAVE_ON_FLASH",
|
||||
"generate" : "jswrap_pin_writeAtTime",
|
||||
"params" : [
|
||||
["value", "bool", "Whether to set output high (true/1) or low (false/0)"],
|
||||
["time", "float", "Time at which to write (in seconds)"]
|
||||
]
|
||||
}
|
||||
Sets the output state of the pin to the parameter given at the specified time.
|
||||
|
||||
**Note:** this **doesn't** change the mode of the pin to an output. To do that,
|
||||
you need to use `pin.write(0)` or `pinMode(pin, 'output')` first.
|
||||
*/
|
||||
void jswrap_pin_writeAtTime(JsVar *parent, bool value, JsVarFloat time) {
|
||||
Pin pin = jshGetPinFromVar(parent);
|
||||
JsSysTime sTime = jshGetTimeFromMilliseconds(time*1000) - jshGetSystemTime();
|
||||
jstPinOutputAtTime(sTime, NULL, &pin, 1, value);
|
||||
}
|
||||
|
||||
|
||||
/*JSON{
|
||||
"type" : "method",
|
||||
"class" : "Pin",
|
||||
|
||||
@ -86,66 +86,51 @@ static bool jswrap_stepper_getPattern(JsVar *stepper, uint8_t *pattern) {
|
||||
return ok;
|
||||
}
|
||||
|
||||
JsVar *_jswrap_stepper_getById(int id) {
|
||||
JsVar *steppers = jsvObjectGetChild(execInfo.hiddenRoot, JSI_STEPPER_NAME, JSV_ARRAY);
|
||||
if (!steppers) return 0;
|
||||
return jsvGetArrayItem(steppers, id);
|
||||
}
|
||||
|
||||
/*JSON{
|
||||
"type" : "idle",
|
||||
"generate" : "jswrap_stepper_idle",
|
||||
"type" : "EV_CUSTOM",
|
||||
"generate" : "jswrap_stepper_eventHandler",
|
||||
"ifdef" : "ESPR_USE_STEPPER_TIMER"
|
||||
}*/
|
||||
bool jswrap_stepper_idle() {
|
||||
JsVar *steppers = jsvObjectGetChildIfExists(execInfo.hiddenRoot, JSI_STEPPER_NAME);
|
||||
if (steppers) {
|
||||
JsvObjectIterator it;
|
||||
jsvObjectIteratorNew(&it, steppers);
|
||||
while (jsvObjectIteratorHasValue(&it)) {
|
||||
JsVar *stepper = jsvObjectIteratorGetValue(&it);
|
||||
bool running = jsvObjectGetBoolChild(stepper, "running");
|
||||
Pin pins[4];
|
||||
if (running) {
|
||||
UtilTimerTask task;
|
||||
// Search for a timer task
|
||||
if (!jswrap_stepper_getPins(stepper, pins) || !jstGetLastPinTimerTask(pins[0], &task)) {
|
||||
// if the task is now gone...
|
||||
jsiQueueObjectCallbacks(stepper, JS_EVENT_PREFIX"finish", NULL, 0);
|
||||
// Update current position
|
||||
jsvObjectSetChildAndUnLock(stepper, "pos", jsvNewFromInteger(
|
||||
jsvObjectGetIntegerChild(stepper, "pos")+
|
||||
jsvObjectGetIntegerChild(stepper, "_direction")));
|
||||
jsvObjectRemoveChild(stepper, "_direction");
|
||||
if (jsvObjectGetBoolChild(stepper, "_turnOff")) {
|
||||
Pin pins[4];
|
||||
int offpattern = jsvObjectGetIntegerChild(stepper, "offpattern");
|
||||
if (jswrap_stepper_getPins(stepper, pins)) {
|
||||
for (int i=0;i<4;i++)
|
||||
jshPinSetValue(pins[i], (offpattern>>i)&1);
|
||||
}
|
||||
}
|
||||
jsvObjectRemoveChild(stepper, "_turnOff");
|
||||
// set running flag
|
||||
running = false;
|
||||
jsvObjectSetChildAndUnLock(stepper, "running", jsvNewFromBool(running));
|
||||
JsVar *promise = jsvObjectGetChildIfExists(stepper, "promise");
|
||||
if (promise) {
|
||||
jsvObjectRemoveChild(stepper, "promise");
|
||||
jspromise_resolve(promise, 0);
|
||||
jsvUnLock(promise);
|
||||
}
|
||||
} else {
|
||||
// If the timer task is still there, don't do anything
|
||||
// note... we could fire off a 'stepping' event?
|
||||
}
|
||||
*/
|
||||
void jswrap_stepper_eventHandler(IOEventFlags eventFlags, uint8_t *data, int length) {
|
||||
IOCustomEventFlags customFlags = *(IOCustomEventFlags*)data;
|
||||
if ((customFlags&EVC_TYPE_MASK)==EVC_TIMER_FINISHED) {
|
||||
int id = customFlags >> EVC_DATA_SHIFT;
|
||||
JsVar *stepper = _jswrap_stepper_getById(id);
|
||||
if (stepper) {
|
||||
jsiQueueObjectCallbacks(stepper, JS_EVENT_PREFIX"finish", NULL, 0);
|
||||
// Update current position
|
||||
jsvObjectSetChildAndUnLock(stepper, "pos", jsvNewFromInteger(
|
||||
jsvObjectGetIntegerChild(stepper, "pos")+
|
||||
jsvObjectGetIntegerChild(stepper, "_direction")));
|
||||
jsvObjectRemoveChild(stepper, "_direction");
|
||||
if (jsvObjectGetBoolChild(stepper, "_turnOff")) {
|
||||
Pin pins[4];
|
||||
int offpattern = jsvObjectGetIntegerChild(stepper, "offpattern");
|
||||
if (jswrap_stepper_getPins(stepper, pins)) {
|
||||
for (int i=0;i<4;i++)
|
||||
jshPinSetValue(pins[i], (offpattern>>i)&1);
|
||||
}
|
||||
}
|
||||
jsvUnLock(stepper);
|
||||
// if not running, remove stepper from this list
|
||||
if (!running)
|
||||
jsvObjectIteratorRemoveAndGotoNext(&it, steppers);
|
||||
else
|
||||
jsvObjectIteratorNext(&it);
|
||||
jsvObjectRemoveChild(stepper, "_turnOff");
|
||||
jsvObjectSetChildAndUnLock(stepper, "running", jsvNewFromBool(false));
|
||||
JsVar *promise = jsvObjectGetChildIfExists(stepper, "promise");
|
||||
if (promise) {
|
||||
jsvObjectRemoveChild(stepper, "promise");
|
||||
jspromise_resolve(promise, 0);
|
||||
jsvUnLock(promise);
|
||||
}
|
||||
JsVar *steppers = jsvObjectGetChild(execInfo.hiddenRoot, JSI_STEPPER_NAME, JSV_ARRAY);
|
||||
if (steppers) jsvRemoveArrayItem(steppers, id);
|
||||
jsvUnLock2(stepper, steppers);
|
||||
}
|
||||
jsvObjectIteratorFree(&it);
|
||||
jsvUnLock(steppers);
|
||||
}
|
||||
return false; // no need to stay awake - an IRQ will wake us
|
||||
}
|
||||
|
||||
/*JSON{
|
||||
@ -163,7 +148,7 @@ void jswrap_stepper_kill() { // be sure to remove all stepper instances...
|
||||
bool running = jsvObjectGetBoolChild(stepper, "running");
|
||||
if (running) {
|
||||
Pin pins[4];
|
||||
if (!jswrap_stepper_getPins(stepper, pins) || !jstStopPinTimerTask(pins[0]))
|
||||
if (!jswrap_stepper_getPins(stepper, pins) || !jstStopPinTimerTask(pins[0])) // FIXME: use timer index
|
||||
jsExceptionHere(JSET_ERROR, "Stepper couldn't be stopped");
|
||||
}
|
||||
jsvUnLock(stepper);
|
||||
@ -318,32 +303,33 @@ JsVar *jswrap_stepper_moveTo(JsVar *stepper, int position, JsVar *options) {
|
||||
JsVar *promise = jspromise_create();
|
||||
jsvObjectSetChild(stepper, "promise", promise);
|
||||
|
||||
UtilTimerTask task;
|
||||
task.time = 0;
|
||||
task.repeatInterval = jshGetTimeFromMilliseconds(1000.0 / freq);
|
||||
task.type = UET_STEP;
|
||||
jswrap_stepper_getPins(stepper, task.data.step.pins);
|
||||
task.data.step.steps = direction;
|
||||
task.data.step.pIndex = currentPos&7;
|
||||
task.data.step.pattern[0] = 0b00010010;
|
||||
task.data.step.pattern[1] = 0b01001000;
|
||||
task.data.step.pattern[2] = 0b00010010;
|
||||
task.data.step.pattern[3] = 0b01001000;
|
||||
jswrap_stepper_getPattern(stepper, task.data.step.pattern);
|
||||
|
||||
if (!utilTimerInsertTask(&task, NULL)) {
|
||||
jsExceptionHere(JSET_ERROR, "Failed to add timer task");
|
||||
jsvUnLock(promise);
|
||||
int idx = utilTimerGetUnusedIndex(true/*wait*/);
|
||||
if (idx<0) {
|
||||
jsvUnLock(promise); // WAIT_UNTIL already errored
|
||||
return 0;
|
||||
}
|
||||
UtilTimerTask *task = &utilTimerTaskInfo[idx];
|
||||
task->type = UET_STEP;
|
||||
task->time = 0;
|
||||
task->repeatInterval = jshGetTimeFromMilliseconds(1000.0 / freq);
|
||||
jswrap_stepper_getPins(stepper, task->data.step.pins);
|
||||
task->data.step.steps = direction;
|
||||
task->data.step.pIndex = currentPos&7;
|
||||
task->data.step.pattern[0] = 0b00010010;
|
||||
task->data.step.pattern[1] = 0b01001000;
|
||||
task->data.step.pattern[2] = 0b00010010;
|
||||
task->data.step.pattern[3] = 0b01001000;
|
||||
jswrap_stepper_getPattern(stepper, task->data.step.pattern);
|
||||
utilTimerInsertTask(idx, NULL);
|
||||
// And finally set it up
|
||||
jsvObjectSetChildAndUnLock(stepper, "timer", jsvNewFromInteger(idx));
|
||||
jsvObjectSetChildAndUnLock(stepper, "running", jsvNewFromBool(true));
|
||||
jsvObjectSetChildAndUnLock(stepper, "_direction", jsvNewFromInteger(direction));
|
||||
jsvObjectSetChildAndUnLock(stepper, "_turnOff", jsvNewFromBool(turnOff));
|
||||
// Add to our list of active steppers
|
||||
JsVar *steppers = jsvObjectGetChild(execInfo.hiddenRoot, JSI_STEPPER_NAME, JSV_ARRAY);
|
||||
if (steppers) {
|
||||
jsvArrayPush(steppers, stepper);
|
||||
jsvSetArrayItem(steppers, idx, stepper);
|
||||
jsvUnLock(steppers);
|
||||
}
|
||||
return promise;
|
||||
@ -373,7 +359,7 @@ void jswrap_stepper_stop(JsVar *stepper, JsVar *options) {
|
||||
bool turnOff = jsvObjectGetBoolChild(options, "turnOff");
|
||||
if (turnOff)
|
||||
jsvObjectSetChildAndUnLock(stepper, "_turnOff", jsvNewFromBool(turnOff));
|
||||
// the _idle handler will see _turnOff and will turn off the stepper
|
||||
// the event handler will see _turnOff and will turn off the stepper
|
||||
}
|
||||
|
||||
Pin pins[4];
|
||||
@ -386,12 +372,10 @@ void jswrap_stepper_stop(JsVar *stepper, JsVar *options) {
|
||||
jsvObjectGetIntegerChild(stepper, "_direction") -
|
||||
task.data.step.steps));
|
||||
}
|
||||
ok = jstStopPinTimerTask(pins[0]);
|
||||
ok = jstStopPinTimerTask(pins[0]); // FIXME: use timer index
|
||||
}
|
||||
if (!ok)
|
||||
jsExceptionHere(JSET_ERROR, "Stepper couldn't be stopped");
|
||||
// now run idle loop as this will issue the finish event and will clean up
|
||||
jswrap_stepper_idle();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -16,8 +16,8 @@
|
||||
|
||||
#include "jshardware.h"
|
||||
|
||||
bool jswrap_stepper_idle();
|
||||
void jswrap_stepper_kill();
|
||||
void jswrap_stepper_eventHandler(IOEventFlags eventFlags, uint8_t *data, int length);
|
||||
JsVar *jswrap_stepper_constructor(JsVar *options);
|
||||
JsVar *jswrap_stepper_moveTo(JsVar *stepper, int position, JsVar *options);
|
||||
void jswrap_stepper_stop(JsVar *stepper, JsVar *options);
|
||||
|
||||
299
src/jswrap_timer.c
Normal file
299
src/jswrap_timer.c
Normal file
@ -0,0 +1,299 @@
|
||||
/*
|
||||
* 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/.
|
||||
*
|
||||
* ----------------------------------------------------------------------------
|
||||
* This file is designed to be parsed during the build process
|
||||
*
|
||||
* JavaScript methods for accessing the built-in timer
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
#include "jswrap_timer.h"
|
||||
#include "jsvar.h"
|
||||
#include "jstimer.h"
|
||||
#include "jsinteractive.h"
|
||||
|
||||
/*JSON{
|
||||
"type" : "library",
|
||||
"ifndef" : "SAVE_ON_FLASH",
|
||||
"class" : "timer"
|
||||
}
|
||||
(2v29+ only) This class allows Espruino to control stepper motors.
|
||||
|
||||
```
|
||||
require("timer").list()
|
||||
// [ { id: 0, type: 'SET', pins: [ D2, D3, D4, D5 ], value: 0, time: 10 }, ... ]
|
||||
```
|
||||
|
||||
This replaces `E.dumpTimers()` and `Pin.writeAtTime`
|
||||
*/
|
||||
|
||||
static void jswrap_timer_queue_interrupt_js(JsSysTime time, void* userdata) {
|
||||
JsVarRef code = (JsVarRef)(size_t)userdata;
|
||||
jshPushIOCharEvents(EV_RUN_INTERRUPT_JS, (char*)&code, sizeof(code));
|
||||
execInfo.execute |= EXEC_RUN_INTERRUPT_JS;
|
||||
jshHadEvent();
|
||||
}
|
||||
|
||||
|
||||
/*JSON{
|
||||
"type" : "staticmethod",
|
||||
"class" : "timer",
|
||||
"name" : "list",
|
||||
"ifndef" : "SAVE_ON_FLASH",
|
||||
"generate" : "jswrap_timer_list",
|
||||
"return" : ["JsVar","Return a list of objects representing active timers"],
|
||||
"return_object" : "Array"
|
||||
}
|
||||
See `require("timer").get` for details of the fields in each timer.
|
||||
*/
|
||||
JsVar *jswrap_timer_list() {
|
||||
JsVar *arr = jsvNewEmptyArray();
|
||||
for (int idx=0;idx<UTILTIMERTASK_TASKS;idx++) {
|
||||
JsVar *obj = jswrap_timer_get(idx);
|
||||
if (obj) {
|
||||
jsvSetArrayItem(arr, idx, obj);
|
||||
jsvUnLock(obj);
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
|
||||
/*JSON{
|
||||
"type" : "staticmethod",
|
||||
"class" : "timer",
|
||||
"name" : "get",
|
||||
"ifndef" : "SAVE_ON_FLASH",
|
||||
"generate" : "jswrap_timer_get",
|
||||
"params" : [
|
||||
["timerID","int","The ID of the timer to get"]
|
||||
],
|
||||
"return" : ["JsVar","An object describing the added timer, with an `id` field added."]
|
||||
}
|
||||
Returns:
|
||||
|
||||
```
|
||||
{
|
||||
id : int, // timer ID (corresponds to array index)
|
||||
type : string, // type of timer (eg 'SET/EXEC/STEP/WR8/WR16/RD8/RD16')
|
||||
time : float, // time (in milliseconds) when the timer will next fire
|
||||
interval : float, // (optional) if the timer repeats, the interval (in milliseconds
|
||||
// the following fields are only present on devices with enough flash memory)
|
||||
pins : [ Pin, ... ], // (for SET/STEP) the pins used
|
||||
value : int, // (for SET) the value being set
|
||||
ptr : int, // (for EXEC) pointer to the function being executed
|
||||
userdata : int, // (for EXEC) userdata pointer
|
||||
buffer : JsVar, // (for WR8/WR16/RD8/RD16) the buffer being used
|
||||
buffer2 : JsVar // (for WR8/WR16/RD8/RD16) the second buffer being used (if any)
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** `time` is set when the timer was last serviced, so if you set a 1 second timer
|
||||
and then look at it after 500ms, it will still show as 1000ms (unless another timer
|
||||
as been serviced before).
|
||||
*/
|
||||
JsVar *jswrap_timer_get(int id) {
|
||||
if (id<0 || id>=UTILTIMERTASK_TASKS) {
|
||||
return 0;
|
||||
}
|
||||
jshInterruptOff();
|
||||
UtilTimerTask task = utilTimerTaskInfo[id];
|
||||
jshInterruptOn();
|
||||
if (task.type == UET_NONE) return 0;
|
||||
JsVar *obj = jsvNewObject();
|
||||
jsvObjectSetChildAndUnLock(obj, "id", jsvNewFromInteger(id));
|
||||
const char *typeStr = NULL;
|
||||
switch (task.type) {
|
||||
case UET_NONE: assert(0); break;
|
||||
case UET_WAKEUP : typeStr="WKUP"; break;
|
||||
case UET_SET :
|
||||
typeStr="SET";
|
||||
#ifndef SAVE_ON_FLASH
|
||||
jsvObjectSetChildAndUnLock(obj, "value", jsvNewFromInteger(task.data.set.value));
|
||||
#endif
|
||||
break;
|
||||
#ifndef SAVE_ON_FLASH
|
||||
case UET_WRITE_BYTE : typeStr="WR8"; break;
|
||||
case UET_READ_BYTE : typeStr="RD8"; break;
|
||||
case UET_WRITE_SHORT : typeStr="WR16"; break;
|
||||
case UET_READ_SHORT : typeStr="RD16"; break;
|
||||
#endif
|
||||
#ifdef ESPR_USE_STEPPER_TIMER
|
||||
case UET_STEP : typeStr="STEP"; break;
|
||||
#endif
|
||||
case UET_EXECUTE :
|
||||
typeStr="EXEC";
|
||||
#ifndef SAVE_ON_FLASH
|
||||
if (task.data.execute.fn == jswrap_timer_queue_interrupt_js) {
|
||||
JsVar *fn = jsvLock((JsVarRef)(size_t)task.data.execute.userdata);
|
||||
jsvObjectSetChildAndUnLock(obj, "fn", fn);
|
||||
} else {
|
||||
jsvObjectSetChildAndUnLock(obj, "ptr", jsvNewFromInteger((size_t)task.data.execute.fn));
|
||||
jsvObjectSetChildAndUnLock(obj, "userdata", jsvNewFromInteger((size_t)task.data.execute.userdata));
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
#ifndef SAVE_ON_FLASH
|
||||
if (UET_EVENT_HAS_PINS(task.type)) {
|
||||
JsVar *pinsArr = jsvNewEmptyArray();
|
||||
int pinCount = UET_PIN_COUNT(task.type);
|
||||
for (int i=0;i<UTILTIMERTASK_PIN_COUNT;i++)
|
||||
if (task.data.set.pins[i] != PIN_UNDEFINED)
|
||||
jsvArrayPushAndUnLock(pinsArr, jsvNewFromPin(task.data.set.pins[i]));
|
||||
jsvObjectSetChildAndUnLock(obj, "pins", pinsArr);
|
||||
}
|
||||
if (UET_IS_BUFFER_EVENT(task.type)) {
|
||||
jsvObjectSetChildAndUnLock(obj, "buffer", jsvLock(task.data.buffer.currentBuffer));
|
||||
if (task.data.buffer.nextBuffer)
|
||||
jsvObjectSetChildAndUnLock(obj, "buffer2", jsvLock(task.data.buffer.nextBuffer));
|
||||
}
|
||||
#endif
|
||||
#ifdef ESPR_USE_STEPPER_TIMER
|
||||
if (task.type == UET_STEP) {
|
||||
jsvObjectSetChildAndUnLock(obj, "steps", jsvNewFromInteger(task.data.step.steps));
|
||||
jsvObjectSetChildAndUnLock(obj, "stepIdx", jsvNewFromInteger(task.data.step.pIndex));
|
||||
}
|
||||
#endif
|
||||
jsvObjectSetChildAndUnLock(obj, "type", typeStr ? jsvNewFromString(typeStr) : jsvNewFromInteger(task.type));
|
||||
jsvObjectSetChildAndUnLock(obj, "time", jsvNewFromFloat(jshGetMillisecondsFromTime(task.time)));
|
||||
if (task.repeatInterval)
|
||||
jsvObjectSetChildAndUnLock(obj, "interval", jsvNewFromFloat(jshGetMillisecondsFromTime(task.repeatInterval)));
|
||||
return obj;
|
||||
}
|
||||
|
||||
/*JSON{
|
||||
"type" : "staticmethod",
|
||||
"class" : "timer",
|
||||
"name" : "add",
|
||||
"ifndef" : "SAVE_ON_FLASH",
|
||||
"generate" : "jswrap_timer_add",
|
||||
"params" : [
|
||||
["timer","JsVar","An object describing the timer to add. See below."]
|
||||
],
|
||||
"return" : ["int","Return the ID of the added timer"]
|
||||
}
|
||||
|
||||
To set one or more pins at a specific point in the future:
|
||||
|
||||
```
|
||||
require("timer").add({
|
||||
type : "SET",
|
||||
pin : Pin, // required
|
||||
pin2 : Pin, // optional
|
||||
pin3 : Pin, // optional
|
||||
pin4 : Pin, // optional
|
||||
value : int, // required
|
||||
time : float, // time (in milliseconds) when the timer will first fire
|
||||
interval : float // (optional) if the timer repeats, the interval (in milliseconds)
|
||||
})
|
||||
// eg. set LED2 in 1 second (note: LED2 should already be an output)
|
||||
require("timer").add({
|
||||
type: "SET",
|
||||
pin: LED2, value: 1,
|
||||
time: 1000
|
||||
});
|
||||
```
|
||||
|
||||
To execute some code at a specific point in the future:
|
||||
|
||||
```
|
||||
require("timer").add({
|
||||
type : "EXEC",
|
||||
ptr : int, userdata : int, // required - pointer to native function(time:uint64, userdate:int) to call, and uint32 userdata to pass to function
|
||||
fn : JsVar, // alternative to ptr/userdata - a JS function to call (note: this function must be referenced elsewhere)
|
||||
time : float, // time (in milliseconds) when the timer will first fire
|
||||
interval : float // (optional) if the timer repeats, the interval (in milliseconds)
|
||||
})
|
||||
// eg. execute myFunction in 100ms, then 200ms thereafter
|
||||
require("timer").add({
|
||||
type:"EXEC", fn:myFunction,
|
||||
time:100,
|
||||
interval:200,
|
||||
});
|
||||
```
|
||||
*/
|
||||
int jswrap_timer_add(JsVar *timer) {
|
||||
JsVarFloat time=0, interval=0;
|
||||
JsVar *type = 0, *fn = 0;
|
||||
int value=0,ptr=0,userdata=0;
|
||||
Pin pins[UTILTIMERTASK_PIN_COUNT] = {PIN_UNDEFINED, PIN_UNDEFINED, PIN_UNDEFINED, PIN_UNDEFINED};
|
||||
jsvConfigObject configs[] = {
|
||||
{"type", JSV_STRING_0, &type},
|
||||
{"time", JSV_FLOAT, &time},
|
||||
{"interval", JSV_FLOAT, &interval},
|
||||
{"value", JSV_INTEGER, &value},
|
||||
{"fn", JSV_OBJECT, &fn},
|
||||
{"ptr", JSV_INTEGER, &ptr},
|
||||
{"userdata", JSV_INTEGER, &userdata},
|
||||
{"pin", JSV_PIN, &pins[0]},
|
||||
{"pin2", JSV_PIN, &pins[1]},
|
||||
{"pin3", JSV_PIN, &pins[2]},
|
||||
{"pin4", JSV_PIN, &pins[3]}
|
||||
};
|
||||
if (!jsvReadConfigObject(timer, configs, sizeof(configs) / sizeof(jsvConfigObject))) {
|
||||
return -1;
|
||||
}
|
||||
UtilTimerEventType evtType = UET_NONE;
|
||||
if (jsvIsStringIEqual(type, "SET")) {
|
||||
if (pins[0] != PIN_UNDEFINED) evtType = UET_SET;
|
||||
else jsExceptionHere(JSET_ERROR, "`pin` required for SET timer");
|
||||
} else if (jsvIsStringIEqual(type, "EXEC")) {
|
||||
if (ptr || jsvIsFunction(fn)) evtType = UET_EXECUTE;
|
||||
else jsExceptionHere(JSET_ERROR, "`ptr` or `fn` required for EXEC timer");
|
||||
} else jsExceptionHere(JSET_ERROR, "Unsupported timer type %q", type);
|
||||
jsvUnLock(type);
|
||||
if (evtType == UET_NONE) {
|
||||
jsvUnLock(fn);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int idx = utilTimerGetUnusedIndex(true/*wait*/);
|
||||
if (idx<0) return -1; // no free tasks!
|
||||
UtilTimerTask *task = &utilTimerTaskInfo[idx];
|
||||
task->time = (int)jshGetTimeFromMilliseconds(time);
|
||||
task->repeatInterval = (unsigned int)jshGetTimeFromMilliseconds(interval);
|
||||
if (evtType == UET_SET) {
|
||||
for (int i=0;i<UTILTIMERTASK_PIN_COUNT;i++)
|
||||
task->data.set.pins[i] = pins[i];
|
||||
task->data.set.value = value;
|
||||
} else if (evtType == UET_EXECUTE) {
|
||||
if (jsvIsFunction(fn)) { // if a function is passed we use EXEC_RUN_INTERRUPT_JS
|
||||
if (jsvGetRefs(fn) == 0) {
|
||||
jsExceptionHere(JSET_ERROR, "Function passed to timer must be referenced elsewhere");
|
||||
jsvUnLock(fn);
|
||||
return -1;
|
||||
}
|
||||
ptr = (size_t)jswrap_timer_queue_interrupt_js;
|
||||
userdata = jsvGetRef(fn);
|
||||
}
|
||||
task->data.execute.fn = (UtilTimerTaskExecFn)(size_t)ptr;
|
||||
task->data.execute.userdata = (void*)(size_t)userdata;
|
||||
}
|
||||
task->type = evtType; // set type here, as we may have returned with error earlier
|
||||
jsvUnLock(fn);
|
||||
utilTimerInsertTask(idx, NULL);
|
||||
return idx;
|
||||
}
|
||||
|
||||
/*JSON{
|
||||
"type" : "staticmethod",
|
||||
"class" : "timer",
|
||||
"name" : "remove",
|
||||
"ifndef" : "SAVE_ON_FLASH",
|
||||
"generate" : "jswrap_timer_remove",
|
||||
"params" : [
|
||||
["timerID","int","The ID of the timer to remove"]
|
||||
]
|
||||
}
|
||||
*/
|
||||
void jswrap_timer_remove(int id) {
|
||||
if (!utilTimerRemoveTask(id))
|
||||
jsExceptionHere(JSET_ERROR, "No timer with ID %d", id);
|
||||
}
|
||||
22
src/jswrap_timer.h
Normal file
22
src/jswrap_timer.h
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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/.
|
||||
*
|
||||
* ----------------------------------------------------------------------------
|
||||
* This file is designed to be parsed during the build process
|
||||
*
|
||||
* JavaScript methods for accessing the built-in timer
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
#include "jsvar.h"
|
||||
|
||||
|
||||
JsVar *jswrap_timer_list();
|
||||
JsVar *jswrap_timer_get(int id);
|
||||
int jswrap_timer_add(JsVar *timer);
|
||||
void jswrap_timer_remove(int id);
|
||||
@ -66,62 +66,6 @@ static JsVar *jswrap_waveform_getBuffer(JsVar *waveform, int bufferNumber, bool
|
||||
}
|
||||
|
||||
|
||||
/*JSON{
|
||||
"type" : "idle",
|
||||
"generate" : "jswrap_waveform_idle",
|
||||
"ifndef" : "SAVE_ON_FLASH"
|
||||
}*/
|
||||
bool jswrap_waveform_idle() {
|
||||
JsVar *waveforms = jsvObjectGetChildIfExists(execInfo.hiddenRoot, JSI_WAVEFORM_NAME);
|
||||
if (waveforms) {
|
||||
JsvObjectIterator it;
|
||||
jsvObjectIteratorNew(&it, waveforms);
|
||||
while (jsvObjectIteratorHasValue(&it)) {
|
||||
JsVar *waveform = jsvObjectIteratorGetValue(&it);
|
||||
|
||||
bool running = jsvObjectGetBoolChild(waveform, "running");
|
||||
if (running) {
|
||||
JsVar *buffer = jswrap_waveform_getBuffer(waveform,0,0);
|
||||
UtilTimerTask task;
|
||||
// Search for a timer task
|
||||
if (!jstGetLastBufferTimerTask(buffer, &task)) {
|
||||
// if the timer task is now gone...
|
||||
JsVar *arrayBuffer = jsvObjectGetChildIfExists(waveform, "buffer");
|
||||
jsiQueueObjectCallbacks(waveform, JS_EVENT_PREFIX"finish", &arrayBuffer, 1);
|
||||
jsvUnLock(arrayBuffer);
|
||||
running = false;
|
||||
jsvObjectSetChildAndUnLock(waveform, "running", jsvNewFromBool(running));
|
||||
} else {
|
||||
// If the timer task is still there...
|
||||
if (task.data.buffer.nextBuffer &&
|
||||
task.data.buffer.nextBuffer != task.data.buffer.currentBuffer) {
|
||||
// if it is a double-buffered task
|
||||
int currentBuffer = (jsvGetRef(buffer)==task.data.buffer.currentBuffer) ? 0 : 1;
|
||||
int oldBuffer = jsvGetIntegerAndUnLock(jsvObjectGetChild(waveform, "currentBuffer", JSV_INTEGER));
|
||||
if (oldBuffer != currentBuffer) {
|
||||
// buffers have changed - fire off a 'buffer' event with the buffer that needs to be filled
|
||||
jsvObjectSetChildAndUnLock(waveform, "currentBuffer", jsvNewFromInteger(currentBuffer));
|
||||
JsVar *arrayBuffer = jsvObjectGetChildIfExists(waveform, (currentBuffer==0) ? "buffer2" : "buffer");
|
||||
jsiQueueObjectCallbacks(waveform, JS_EVENT_PREFIX"buffer", &arrayBuffer, 1);
|
||||
jsvUnLock(arrayBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
jsvUnLock(buffer);
|
||||
}
|
||||
jsvUnLock(waveform);
|
||||
// if not running, remove waveform from this list
|
||||
if (!running)
|
||||
jsvObjectIteratorRemoveAndGotoNext(&it, waveforms);
|
||||
else
|
||||
jsvObjectIteratorNext(&it);
|
||||
}
|
||||
jsvObjectIteratorFree(&it);
|
||||
jsvUnLock(waveforms);
|
||||
}
|
||||
return false; // no need to stay awake - an IRQ will wake us
|
||||
}
|
||||
|
||||
/*JSON{
|
||||
"type" : "kill",
|
||||
"generate" : "jswrap_waveform_kill",
|
||||
@ -289,8 +233,11 @@ static void jswrap_waveform_start(JsVar *waveform, Pin pin, JsVarFloat freq, JsV
|
||||
|
||||
|
||||
// And finally set it up
|
||||
if (!jstStartSignal(startTime, jshGetTimeFromMilliseconds(1000.0 / freq), pin, npin, buffer, repeat?(buffer2?buffer2:buffer):0, eventType))
|
||||
int timerId = jstStartSignal(startTime, jshGetTimeFromMilliseconds(1000.0 / freq), pin, npin, buffer, repeat?(buffer2?buffer2:buffer):0, eventType);
|
||||
if (timerId<0)
|
||||
jsWarn("Unable to schedule a timer");
|
||||
else
|
||||
jsvObjectSetChildAndUnLock(waveform, "freq", jsvNewFromInteger(timerId));
|
||||
jsvUnLock2(buffer,buffer2);
|
||||
|
||||
jsvObjectSetChildAndUnLock(waveform, "running", jsvNewFromBool(true));
|
||||
@ -298,7 +245,7 @@ static void jswrap_waveform_start(JsVar *waveform, Pin pin, JsVarFloat freq, JsV
|
||||
// Add to our list of active waveforms
|
||||
JsVar *waveforms = jsvObjectGetChild(execInfo.hiddenRoot, JSI_WAVEFORM_NAME, JSV_ARRAY);
|
||||
if (waveforms) {
|
||||
jsvArrayPush(waveforms, waveform);
|
||||
jsvSetArrayItem(waveforms, timerId, waveform);
|
||||
jsvUnLock(waveforms);
|
||||
}
|
||||
}
|
||||
@ -378,8 +325,57 @@ void jswrap_waveform_stop(JsVar *waveform) {
|
||||
jsExceptionHere(JSET_ERROR, "Waveform couldn't be stopped");
|
||||
}
|
||||
jsvUnLock(buffer);
|
||||
// now run idle loop as this will issue the finish event and will clean up
|
||||
jswrap_waveform_idle();
|
||||
}
|
||||
|
||||
JsVar *_jswrap_waveform_getById(int id) {
|
||||
JsVar *waveforms = jsvObjectGetChild(execInfo.hiddenRoot, JSI_WAVEFORM_NAME, JSV_ARRAY);
|
||||
if (!waveforms) return 0;
|
||||
return jsvGetArrayItem(waveforms, id);
|
||||
}
|
||||
|
||||
/*JSON{
|
||||
"type" : "EV_CUSTOM",
|
||||
"#if" : "!defined(SAVE_ON_FLASH)",
|
||||
"generate" : "jswrap_waveform_eventHandler"
|
||||
}
|
||||
*/
|
||||
void jswrap_waveform_eventHandler(IOEventFlags eventFlags, uint8_t *data, int length) {
|
||||
IOCustomEventFlags customFlags = *(IOCustomEventFlags*)data;
|
||||
int id = customFlags >> EVC_DATA_SHIFT;
|
||||
if ((customFlags&EVC_TYPE_MASK)==EVC_TIMER_FINISHED) {
|
||||
JsVar *waveform = _jswrap_waveform_getById(id);
|
||||
if (waveform) {
|
||||
jsvObjectSetChildAndUnLock(waveform, "running", jsvNewFromBool(false));
|
||||
JsVar *arrayBuffer = jsvObjectGetChildIfExists(waveform, "buffer");
|
||||
jsiQueueObjectCallbacks(waveform, JS_EVENT_PREFIX"finish", &arrayBuffer, 1);
|
||||
jsvUnLock(arrayBuffer);
|
||||
JsVar *waveforms = jsvObjectGetChild(execInfo.hiddenRoot, JSI_WAVEFORM_NAME, JSV_ARRAY);
|
||||
if (waveforms) jsvRemoveArrayItem(waveforms, id);
|
||||
jsvUnLock2(waveform, waveforms);
|
||||
}
|
||||
}
|
||||
if ((customFlags&EVC_TYPE_MASK) == EVC_TIMER_BUFFER_FLIP) {
|
||||
JsVar *waveform = _jswrap_waveform_getById(id);
|
||||
if (waveform) {
|
||||
UtilTimerTask *task = &utilTimerTaskInfo[id];
|
||||
if (task->data.buffer.nextBuffer &&
|
||||
task->data.buffer.nextBuffer != task->data.buffer.currentBuffer) {
|
||||
// if it is a double-buffered task
|
||||
JsVar *buffer = jswrap_waveform_getBuffer(waveform,0,0);
|
||||
int currentBuffer = (jsvGetRef(buffer)==task->data.buffer.currentBuffer) ? 0 : 1;
|
||||
jsvUnLock(buffer);
|
||||
int oldBuffer = jsvGetIntegerAndUnLock(jsvObjectGetChild(waveform, "currentBuffer", JSV_INTEGER));
|
||||
if (oldBuffer != currentBuffer) {
|
||||
// buffers have changed - fire off a 'buffer' event with the buffer that needs to be filled
|
||||
jsvObjectSetChildAndUnLock(waveform, "currentBuffer", jsvNewFromInteger(currentBuffer));
|
||||
JsVar *arrayBuffer = jsvObjectGetChildIfExists(waveform, (currentBuffer==0) ? "buffer2" : "buffer");
|
||||
jsiQueueObjectCallbacks(waveform, JS_EVENT_PREFIX"buffer", &arrayBuffer, 1);
|
||||
jsvUnLock(arrayBuffer);
|
||||
}
|
||||
}
|
||||
jsvUnLock(waveform);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@ -16,8 +16,8 @@
|
||||
|
||||
#include "jshardware.h"
|
||||
|
||||
bool jswrap_waveform_idle();
|
||||
void jswrap_waveform_kill();
|
||||
void jswrap_waveform_eventHandler(IOEventFlags eventFlags, uint8_t *data, int length);
|
||||
JsVar *jswrap_waveform_constructor(JsVar *samples, JsVar *options);
|
||||
void jswrap_waveform_startOutput(JsVar *waveform, Pin pin, JsVarFloat freq, JsVar *options);
|
||||
void jswrap_waveform_startInput(JsVar *waveform, Pin pin, JsVarFloat freq, JsVar *options);
|
||||
|
||||
@ -1096,7 +1096,7 @@ void jshSetSystemTime(JsSysTime time) {
|
||||
|
||||
/// Convert a time in Milliseconds to one in ticks.
|
||||
JsSysTime jshGetTimeFromMilliseconds(JsVarFloat ms) {
|
||||
return (JsSysTime) (ms * (SYSCLK_FREQ / 1000.0));
|
||||
return (JsSysTime)((ms * (SYSCLK_FREQ / 1000.0)) + 0.5);
|
||||
}
|
||||
|
||||
/// Convert ticks to a time in Milliseconds.
|
||||
@ -2956,13 +2956,11 @@ void jsvGetProcessorPowerUsage(JsVar *devices) {
|
||||
void COMP_LPCOMP_IRQHandler() {
|
||||
if (nrf_lpcomp_event_check(NRF_LPCOMP_EVENT_UP) && nrf_lpcomp_int_enable_check(LPCOMP_INTENSET_UP_Msk)) {
|
||||
nrf_lpcomp_event_clear(NRF_LPCOMP_EVENT_UP);
|
||||
IOCustomEventFlags customFlags = EVC_LPCOMP | EVC_DATA_LPCOMP_UP;
|
||||
jshPushEvent(EV_CUSTOM, (uint8_t*)&customFlags, sizeof(customFlags));
|
||||
jshPushCustomEvent(EVC_LPCOMP | EVC_DATA_LPCOMP_UP);
|
||||
}
|
||||
if (nrf_lpcomp_event_check(NRF_LPCOMP_EVENT_DOWN) && nrf_lpcomp_int_enable_check(LPCOMP_INTENSET_DOWN_Msk)) {
|
||||
nrf_lpcomp_event_clear(NRF_LPCOMP_EVENT_DOWN);
|
||||
IOCustomEventFlags customFlags = EVC_LPCOMP;
|
||||
jshPushEvent(EV_CUSTOM, (uint8_t*)&customFlags, sizeof(customFlags));
|
||||
jshPushCustomEvent(EVC_LPCOMP);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user