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
|
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
|
STM32: Ensure setDeepSleep(1) will only sleep if there are no active PWM outputs
|
||||||
Pin.getInfo().negated now set if pin negated in firmware
|
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
|
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)
|
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_number.c \
|
||||||
src/jswrap_object.c \
|
src/jswrap_object.c \
|
||||||
src/jswrap_regexp.c \
|
src/jswrap_regexp.c \
|
||||||
|
src/jswrap_timer.c \
|
||||||
src/jswrap_string.c \
|
src/jswrap_string.c \
|
||||||
src/jswrap_modules.c \
|
src/jswrap_modules.c \
|
||||||
src/jswrap_math.c
|
src/jswrap_math.c
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
[
|
[
|
||||||
{"class":"OneWire","name":"*"},
|
{"class":"OneWire","name":"*"},
|
||||||
{"class":"SPI","name":"send4bit"},
|
{"class":"SPI","name":"send4bit"},
|
||||||
{"class":"E","name":"dumpTimers"},
|
|
||||||
{"class":"E","name":"enableWatchdog"},
|
{"class":"E","name":"enableWatchdog"},
|
||||||
{"class":"E","name":"getClock"},
|
{"class":"E","name":"getClock"},
|
||||||
{"class":"E","name":"setBootCode"},
|
{"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;
|
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
|
/// 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) {
|
static bool jshPushIOCharEventsHandler(IOEventFlags channel, char *data, unsigned int count) {
|
||||||
// Check for a CTRL+C
|
// Check for a CTRL+C
|
||||||
|
|||||||
@ -87,7 +87,8 @@ typedef enum {
|
|||||||
#ifdef BLUETOOTH
|
#ifdef BLUETOOTH
|
||||||
EV_BLUETOOTH_PENDING, // Tasks that came from the Bluetooth Stack in an IRQ
|
EV_BLUETOOTH_PENDING, // Tasks that came from the Bluetooth Stack in an IRQ
|
||||||
#endif
|
#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
|
#ifdef BANGLEJS
|
||||||
EV_BANGLEJS, // sent whenever Bangle.js-specific data needs to be queued
|
EV_BANGLEJS, // sent whenever Bangle.js-specific data needs to be queued
|
||||||
#endif
|
#endif
|
||||||
@ -131,10 +132,13 @@ typedef enum {
|
|||||||
/** Event types for EV_CUSTOM */
|
/** Event types for EV_CUSTOM */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
EVC_NONE,
|
EVC_NONE,
|
||||||
|
EVC_TIMER_FINISHED,
|
||||||
|
EVC_TIMER_BUFFER_FLIP,
|
||||||
#ifdef NRF52_SERIES
|
#ifdef NRF52_SERIES
|
||||||
EVC_LPCOMP, // jswrap_espruino: E.setComparator / E.on("comparator" event
|
EVC_LPCOMP, // jswrap_espruino: E.setComparator / E.on("comparator" event
|
||||||
#endif
|
#endif
|
||||||
EVC_TYPE_MASK = 255,
|
EVC_TYPE_MASK = 255,
|
||||||
|
EVC_DATA_SHIFT = 8,
|
||||||
EVC_DATA_LPCOMP_UP = 256
|
EVC_DATA_LPCOMP_UP = 256
|
||||||
} PACKED_FLAGS IOCustomEventFlags;
|
} 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();
|
/// 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);
|
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();
|
/// Add this IO event to the IO event queue. Calls jshHadEvent();
|
||||||
void jshPushIOEvent(IOEventFlags channel, JsSysTime time);
|
void jshPushIOEvent(IOEventFlags channel, JsSysTime time);
|
||||||
/// Signal an IO watch event as having happened. Calls jshHadEvent();
|
/// 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
|
/// pop an IO event, returns EV_NONE on failure. data must be IOEVENT_MAX_LEN bytes
|
||||||
IOEventFlags jshPopIOEvent(uint8_t *data, unsigned int *length);
|
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);
|
IOEventFlags jshPopIOEventOfType(IOEventFlags eventType, uint8_t *data, unsigned int *length);
|
||||||
/// Do we have any events pending? Will jshPopIOEvent return true?
|
/// Do we have any events pending? Will jshPopIOEvent return true?
|
||||||
bool jshHasEvents();
|
bool jshHasEvents();
|
||||||
|
|||||||
@ -2204,6 +2204,20 @@ void jsiHandleIOEventForConsole(uint8_t *eventData, int eventLen) {
|
|||||||
jsiSetBusy(BUSY_INTERACTIVE, false);
|
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() {
|
void jsiIdle() {
|
||||||
// This is how many times we have been here and not done anything.
|
// This is how many times we have been here and not done anything.
|
||||||
// It will be zeroed if we do stuff later
|
// It will be zeroed if we do stuff later
|
||||||
@ -2248,7 +2262,10 @@ void jsiIdle() {
|
|||||||
}
|
}
|
||||||
jsvUnLock(usartClass);
|
jsvUnLock(usartClass);
|
||||||
#endif
|
#endif
|
||||||
|
} else if (eventType == EV_RUN_INTERRUPT_JS) {
|
||||||
|
jsiOnRunInterruptJSEvent(eventData, eventLen);
|
||||||
} else if (eventType == EV_CUSTOM) {
|
} else if (eventType == EV_CUSTOM) {
|
||||||
|
jstOnCustomEvent(eventFlags, eventData, eventLen);
|
||||||
jswOnCustomEvent(eventFlags, eventData, eventLen);
|
jswOnCustomEvent(eventFlags, eventData, eventLen);
|
||||||
#ifdef BLUETOOTH
|
#ifdef BLUETOOTH
|
||||||
} else if (eventType == EV_BLUETOOTH_PENDING) {
|
} else if (eventType == EV_BLUETOOTH_PENDING) {
|
||||||
@ -3000,3 +3017,13 @@ void jsiDebuggerLine(JsVar *line) {
|
|||||||
jslSetLex(oldLex);
|
jslSetLex(oldLex);
|
||||||
}
|
}
|
||||||
#endif // USE_DEBUGGER
|
#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
|
extern void jsiDebuggerLoop(); ///< Enter the debugger loop
|
||||||
#endif
|
#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_ */
|
#endif /* JSINTERACTIVE_H_ */
|
||||||
|
|||||||
@ -2959,6 +2959,10 @@ NO_INLINE JsVar *jspeStatement() {
|
|||||||
jsiDebuggerLoop();
|
jsiDebuggerLoop();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
if (execInfo.execute&EXEC_RUN_INTERRUPT_JS) {
|
||||||
|
execInfo.execute&=~EXEC_RUN_INTERRUPT_JS;
|
||||||
|
jsiRunInterruptingJS();
|
||||||
|
}
|
||||||
if (lex->tk==LEX_ID ||
|
if (lex->tk==LEX_ID ||
|
||||||
lex->tk==LEX_INT ||
|
lex->tk==LEX_INT ||
|
||||||
lex->tk==LEX_FLOAT ||
|
lex->tk==LEX_FLOAT ||
|
||||||
|
|||||||
@ -98,7 +98,7 @@ typedef enum {
|
|||||||
EXEC_INTERRUPTED = 16, ///< true if execution has been interrupted
|
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_EXCEPTION = 32, ///< we had an exception, so don't execute until we hit a try/catch block
|
||||||
EXEC_ERROR = 64,
|
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_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
|
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"
|
#include "jsinteractive.h"
|
||||||
|
|
||||||
/// Data for our tasks (eg when, what they are, etc)
|
/// 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)
|
/// queue beginning (push tasks on here)
|
||||||
volatile unsigned char utilTimerTasksHead = 0;
|
volatile unsigned char utilTimerTasksHead = 0;
|
||||||
/// queue end (tasks pop off here as they are complete)
|
/// 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
|
// move to next element in var
|
||||||
task->data.buffer.charIdx++;
|
task->data.buffer.charIdx++;
|
||||||
if (task->data.buffer.charIdx >= task->data.buffer.endIdx) {
|
if (task->data.buffer.charIdx >= task->data.buffer.endIdx) {
|
||||||
@ -66,6 +68,8 @@ static void jstUtilTimerInterruptHandlerNextByte(UtilTimerTask *task) {
|
|||||||
task->data.buffer.currentBuffer = t;
|
task->data.buffer.currentBuffer = t;
|
||||||
// Setup new buffer
|
// Setup new buffer
|
||||||
jstUtilTimerSetupBuffer(task);
|
jstUtilTimerSetupBuffer(task);
|
||||||
|
// notify rest of interpreter
|
||||||
|
jshPushCustomEvent(EVC_TIMER_BUFFER_FLIP | (timerId<<EVC_DATA_SHIFT));
|
||||||
} else {
|
} else {
|
||||||
task->data.buffer.var = 0;
|
task->data.buffer.var = 0;
|
||||||
// No more data - make sure we don't repeat!
|
// No more data - make sure we don't repeat!
|
||||||
@ -83,6 +87,17 @@ static inline unsigned char *jstUtilTimerInterruptHandlerByte(UtilTimerTask *tas
|
|||||||
}
|
}
|
||||||
#endif
|
#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() {
|
void jstUtilTimerInterruptHandler() {
|
||||||
/* Note: we're using 32 bit times here, even though the real time counter is 64 bit. We
|
/* 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 */
|
* just make sure nothing is scheduled that far in the future */
|
||||||
@ -93,13 +108,14 @@ void jstUtilTimerInterruptHandler() {
|
|||||||
current time. */
|
current time. */
|
||||||
int t = utilTimerTasksTail;
|
int t = utilTimerTasksTail;
|
||||||
while (t!=utilTimerTasksHead) {
|
while (t!=utilTimerTasksHead) {
|
||||||
utilTimerTasks[t].time -= utilTimerPeriod;
|
utilTimerTaskInfo[utilTimerTasks[t]].time -= utilTimerPeriod;
|
||||||
t = (t+1) & (UTILTIMERTASK_TASKS-1);
|
t = (t+1) & (UTILTIMERTASK_TASKS-1);
|
||||||
}
|
}
|
||||||
utilTimerOffset += utilTimerPeriod;
|
utilTimerOffset += utilTimerPeriod;
|
||||||
// Check timers and execute any timers that are due
|
// Check timers and execute any timers that are due
|
||||||
while (utilTimerTasksTail!=utilTimerTasksHead && utilTimerTasks[utilTimerTasksTail].time <= 0) {
|
while (utilTimerTasksTail!=utilTimerTasksHead && utilTimerTaskInfo[utilTimerTasks[utilTimerTasksTail]].time <= 0) {
|
||||||
UtilTimerTask *task = &utilTimerTasks[utilTimerTasksTail];
|
uint8_t timerId = utilTimerTasks[utilTimerTasksTail];
|
||||||
|
UtilTimerTask *task = &utilTimerTaskInfo[timerId];
|
||||||
void (*executeFn)(JsSysTime time, void* userdata) = 0;
|
void (*executeFn)(JsSysTime time, void* userdata) = 0;
|
||||||
void *executeData = 0;
|
void *executeData = 0;
|
||||||
switch (task->type) {
|
switch (task->type) {
|
||||||
@ -119,15 +135,15 @@ void jstUtilTimerInterruptHandler() {
|
|||||||
if (!task->data.buffer.var) break;
|
if (!task->data.buffer.var) break;
|
||||||
int v = jshPinAnalogFast(task->data.buffer.pin);
|
int v = jshPinAnalogFast(task->data.buffer.pin);
|
||||||
*jstUtilTimerInterruptHandlerByte(task) = (unsigned char)v; // LSB first
|
*jstUtilTimerInterruptHandlerByte(task) = (unsigned char)v; // LSB first
|
||||||
jstUtilTimerInterruptHandlerNextByte(task);
|
jstUtilTimerInterruptHandlerNextByte(task, timerId);
|
||||||
*jstUtilTimerInterruptHandlerByte(task) = (unsigned char)(v >> 8);
|
*jstUtilTimerInterruptHandlerByte(task) = (unsigned char)(v >> 8);
|
||||||
jstUtilTimerInterruptHandlerNextByte(task);
|
jstUtilTimerInterruptHandlerNextByte(task, timerId);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case UET_READ_BYTE: {
|
case UET_READ_BYTE: {
|
||||||
if (!task->data.buffer.var) break;
|
if (!task->data.buffer.var) break;
|
||||||
*jstUtilTimerInterruptHandlerByte(task) = (unsigned char)(jshPinAnalogFast(task->data.buffer.pin) >> 8);
|
*jstUtilTimerInterruptHandlerByte(task) = (unsigned char)(jshPinAnalogFast(task->data.buffer.pin) >> 8);
|
||||||
jstUtilTimerInterruptHandlerNextByte(task);
|
jstUtilTimerInterruptHandlerNextByte(task, timerId);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case UET_WRITE_SHORT:
|
case UET_WRITE_SHORT:
|
||||||
@ -137,19 +153,19 @@ void jstUtilTimerInterruptHandler() {
|
|||||||
int sum;
|
int sum;
|
||||||
if (task->type == UET_WRITE_SHORT) {
|
if (task->type == UET_WRITE_SHORT) {
|
||||||
sum = *jstUtilTimerInterruptHandlerByte(task); // LSB first
|
sum = *jstUtilTimerInterruptHandlerByte(task); // LSB first
|
||||||
jstUtilTimerInterruptHandlerNextByte(task);
|
jstUtilTimerInterruptHandlerNextByte(task, timerId);
|
||||||
} else {
|
} else {
|
||||||
sum = 0;
|
sum = 0;
|
||||||
}
|
}
|
||||||
sum |= (unsigned short)(*jstUtilTimerInterruptHandlerByte(task) << 8);
|
sum |= (unsigned short)(*jstUtilTimerInterruptHandlerByte(task) << 8);
|
||||||
jstUtilTimerInterruptHandlerNextByte(task);
|
jstUtilTimerInterruptHandlerNextByte(task, timerId);
|
||||||
task->data.buffer.currentValue = (unsigned short)sum;
|
task->data.buffer.currentValue = (unsigned short)sum;
|
||||||
// now search for other tasks writing to this pin... (polyphony)
|
// now search for other tasks writing to this pin... (polyphony)
|
||||||
int t = (utilTimerTasksTail+1) & (UTILTIMERTASK_TASKS-1);
|
int t = (utilTimerTasksTail+1) & (UTILTIMERTASK_TASKS-1);
|
||||||
while (t!=utilTimerTasksHead) {
|
while (t!=utilTimerTasksHead) {
|
||||||
if (UET_IS_BUFFER_WRITE_EVENT(utilTimerTasks[t].type) &&
|
if (UET_IS_BUFFER_WRITE_EVENT(utilTimerTaskInfo[utilTimerTasks[t]].type) &&
|
||||||
utilTimerTasks[t].data.buffer.pin == task->data.buffer.pin)
|
utilTimerTaskInfo[utilTimerTasks[t]].data.buffer.pin == task->data.buffer.pin)
|
||||||
sum += ((int)(unsigned int)utilTimerTasks[t].data.buffer.currentValue) - 32768;
|
sum += ((int)(unsigned int)utilTimerTaskInfo[utilTimerTasks[t]].data.buffer.currentValue) - 32768;
|
||||||
t = (t+1) & (UTILTIMERTASK_TASKS-1);
|
t = (t+1) & (UTILTIMERTASK_TASKS-1);
|
||||||
}
|
}
|
||||||
// saturate
|
// saturate
|
||||||
@ -194,31 +210,30 @@ void jstUtilTimerInterruptHandler() {
|
|||||||
if (task->repeatInterval) {
|
if (task->repeatInterval) {
|
||||||
// update time (we know time > task->time)
|
// update time (we know time > task->time)
|
||||||
task->time += task->repeatInterval;
|
task->time += task->repeatInterval;
|
||||||
|
|
||||||
// do an in-place bubble sort to ensure that times are still in the right order
|
// do an in-place bubble sort to ensure that times are still in the right order
|
||||||
unsigned char ta = utilTimerTasksTail;
|
unsigned char ta = utilTimerTasksTail;
|
||||||
unsigned char tb = (ta+1) & (UTILTIMERTASK_TASKS-1);
|
unsigned char tb = (ta+1) & (UTILTIMERTASK_TASKS-1);
|
||||||
while (tb != utilTimerTasksHead) {
|
while (tb != utilTimerTasksHead) {
|
||||||
if (utilTimerTasks[ta].time > utilTimerTasks[tb].time) {
|
if (utilTimerTaskInfo[utilTimerTasks[ta]].time > utilTimerTaskInfo[utilTimerTasks[tb]].time) {
|
||||||
UtilTimerTask task = utilTimerTasks[ta];
|
uint8_t idx = utilTimerTasks[ta];
|
||||||
utilTimerTasks[ta] = utilTimerTasks[tb];
|
utilTimerTasks[ta] = utilTimerTasks[tb];
|
||||||
utilTimerTasks[tb] = task;
|
utilTimerTasks[tb] = idx;
|
||||||
}
|
}
|
||||||
ta = tb;
|
ta = tb;
|
||||||
tb = (tb+1) & (UTILTIMERTASK_TASKS-1);
|
tb = (tb+1) & (UTILTIMERTASK_TASKS-1);
|
||||||
}
|
}
|
||||||
} else {
|
} 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);
|
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)
|
// 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);
|
if (executeFn) executeFn(jshGetSystemTime(), executeData);
|
||||||
}
|
}
|
||||||
|
|
||||||
// re-schedule the timer if there is something left to do
|
// re-schedule the timer if there is something left to do
|
||||||
if (utilTimerTasksTail != utilTimerTasksHead) {
|
if (utilTimerTasksTail != utilTimerTasksHead) {
|
||||||
utilTimerPeriod = utilTimerTasks[utilTimerTasksTail].time;
|
utilTimerPeriod = utilTimerTaskInfo[utilTimerTasks[utilTimerTasksTail]].time;
|
||||||
if (utilTimerPeriod<0) utilTimerPeriod=0;
|
if (utilTimerPeriod<0) utilTimerPeriod=0;
|
||||||
jshUtilTimerReschedule(utilTimerPeriod);
|
jshUtilTimerReschedule(utilTimerPeriod);
|
||||||
} else {
|
} else {
|
||||||
@ -253,32 +268,41 @@ static bool utilTimerIsFull() {
|
|||||||
|
|
||||||
/* Restart the utility timer with the right period. This should not normally
|
/* Restart the utility timer with the right period. This should not normally
|
||||||
need to be called by anything outside jstimer.c */
|
need to be called by anything outside jstimer.c */
|
||||||
void jstRestartUtilTimer() {
|
void jstRestartUtilTimer() {
|
||||||
utilTimerPeriod = utilTimerTasks[utilTimerTasksTail].time;
|
utilTimerPeriod = utilTimerTaskInfo[utilTimerTasks[utilTimerTasksTail]].time;
|
||||||
utilTimerSetTime = jshGetSystemTime();
|
utilTimerSetTime = jshGetSystemTime();
|
||||||
if (utilTimerPeriod<0) utilTimerPeriod=0;
|
if (utilTimerPeriod<0) utilTimerPeriod=0;
|
||||||
jshUtilTimerStart(utilTimerPeriod);
|
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.
|
/** 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 the delay at which to execute the task. If timerOffset!==NULL then
|
||||||
* task.time is relative to the time at which timerOffset=jstGetUtilTimerOffset().
|
* task.time is relative to the time at which timerOffset=jstGetUtilTimerOffset().
|
||||||
* This allows pulse trains/etc to be scheduled in sync.
|
* 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) {
|
||||||
// check if queue is full or not
|
assert(!utilTimerIsFull()); // queue should not be full since we had to allocate a task to get the index
|
||||||
if (utilTimerIsFull()) return false;
|
UtilTimerTask *task = &utilTimerTaskInfo[taskIdx];
|
||||||
jshInterruptOff();
|
|
||||||
|
|
||||||
|
jshInterruptOff();
|
||||||
// See above - keep times in sync
|
// See above - keep times in sync
|
||||||
if (timerOffset)
|
if (timerOffset)
|
||||||
task->time += (int)*timerOffset - (int)utilTimerOffset;
|
task->time += (int)*timerOffset - (int)utilTimerOffset;
|
||||||
|
|
||||||
// How long was it since the timer was last scheduled? Update existing tasks #2575
|
// 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
|
// find out where to insert
|
||||||
unsigned char insertPos = utilTimerTasksTail;
|
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);
|
insertPos = (insertPos+1) & (UTILTIMERTASK_TASKS-1);
|
||||||
bool haveChangedTimer = insertPos==utilTimerTasksTail;
|
bool haveChangedTimer = insertPos==utilTimerTasksTail;
|
||||||
//jsiConsolePrintf("Insert at %d, Tail is %d\n",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
|
if (haveChangedTimer) { // timer will change - update all tasks
|
||||||
i = utilTimerTasksTail;
|
i = utilTimerTasksTail;
|
||||||
while (i != utilTimerTasksHead) {
|
while (i != utilTimerTasksHead) {
|
||||||
if (utilTimerTasks[i].time > timePassed)
|
if (utilTimerTaskInfo[utilTimerTasks[i]].time > timePassed)
|
||||||
utilTimerTasks[i].time -= timePassed;
|
utilTimerTaskInfo[utilTimerTasks[i]].time -= timePassed;
|
||||||
else
|
else
|
||||||
utilTimerTasks[i].time = 0;
|
utilTimerTaskInfo[utilTimerTasks[i]].time = 0;
|
||||||
i = (i+1) & (UTILTIMERTASK_TASKS-1);
|
i = (i+1) & (UTILTIMERTASK_TASKS-1);
|
||||||
}
|
}
|
||||||
} else // timer hasn't changed, we have to update our task's time
|
} else // timer hasn't changed, we have to update our task's time
|
||||||
task->time += timePassed;
|
task->time += timePassed;
|
||||||
// add new item
|
// add new item
|
||||||
utilTimerTasks[insertPos] = *task;
|
utilTimerTasks[insertPos] = taskIdx;
|
||||||
|
|
||||||
//jsiConsolePrint("Head is %d\n", utilTimerTasksHead);
|
//jsiConsolePrint("Head is %d\n", utilTimerTasksHead);
|
||||||
// now set up timer if not already set up...
|
// now set up timer if not already set up...
|
||||||
if (!utilTimerOn || haveChangedTimer) {
|
if (!utilTimerOn || haveChangedTimer) {
|
||||||
@ -313,19 +336,27 @@ bool utilTimerInsertTask(UtilTimerTask *task, uint32_t *timerOffset) {
|
|||||||
jstRestartUtilTimer();
|
jstRestartUtilTimer();
|
||||||
}
|
}
|
||||||
jshInterruptOn();
|
jshInterruptOn();
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove the task that that 'checkCallback' returns true for. Returns false if none found
|
/// Find a task that 'checkCallback' returns true for. Returns -1 if none found
|
||||||
bool utilTimerRemoveTask(bool (checkCallback)(UtilTimerTask *task, void* data), void *checkCallbackData) {
|
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();
|
jshInterruptOff();
|
||||||
|
jstUtilTimerTaskIsFinished(id); // queue events for task finished
|
||||||
unsigned char ptr = utilTimerTasksHead;
|
unsigned char ptr = utilTimerTasksHead;
|
||||||
if (ptr != utilTimerTasksTail) {
|
if (ptr != utilTimerTasksTail) {
|
||||||
unsigned char endPtr = ((utilTimerTasksTail+UTILTIMERTASK_TASKS-1) & (UTILTIMERTASK_TASKS-1));
|
unsigned char endPtr = ((utilTimerTasksTail+UTILTIMERTASK_TASKS-1) & (UTILTIMERTASK_TASKS-1));
|
||||||
ptr = (ptr+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
|
// now we're at the last timer task - work back until we've just gone back past utilTimerTasksTail
|
||||||
while (ptr != endPtr) {
|
while (ptr != endPtr) {
|
||||||
if (checkCallback(&utilTimerTasks[ptr], checkCallbackData)) {
|
if (utilTimerTasks[ptr]==id) {
|
||||||
// shift tail back along
|
// shift tail back along
|
||||||
unsigned char next = (ptr+UTILTIMERTASK_TASKS-1) & (UTILTIMERTASK_TASKS-1);
|
unsigned char next = (ptr+UTILTIMERTASK_TASKS-1) & (UTILTIMERTASK_TASKS-1);
|
||||||
while (next!=endPtr) {
|
while (next!=endPtr) {
|
||||||
@ -335,6 +366,7 @@ bool utilTimerRemoveTask(bool (checkCallback)(UtilTimerTask *task, void* data),
|
|||||||
}
|
}
|
||||||
// move 'end' pointer back
|
// move 'end' pointer back
|
||||||
utilTimerTasksTail = (utilTimerTasksTail+1) & (UTILTIMERTASK_TASKS-1);
|
utilTimerTasksTail = (utilTimerTasksTail+1) & (UTILTIMERTASK_TASKS-1);
|
||||||
|
utilTimerTaskInfo[id].type = UET_NONE;
|
||||||
jshInterruptOn();
|
jshInterruptOn();
|
||||||
return true;
|
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
|
/// 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) {
|
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();
|
jshInterruptOff();
|
||||||
unsigned char ptr = utilTimerTasksHead;
|
unsigned char ptr = utilTimerTasksHead;
|
||||||
if (ptr != utilTimerTasksTail) {
|
if (ptr != utilTimerTasksTail) {
|
||||||
ptr = (ptr+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
|
// 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))) {
|
while (ptr != ((utilTimerTasksTail+UTILTIMERTASK_TASKS-1) & (UTILTIMERTASK_TASKS-1))) {
|
||||||
if (checkCallback(&utilTimerTasks[ptr], checkCallbackData)) {
|
if (checkCallback(&utilTimerTaskInfo[utilTimerTasks[ptr]], checkCallbackData)) {
|
||||||
*task = utilTimerTasks[ptr];
|
*task = utilTimerTaskInfo[utilTimerTasks[ptr]];
|
||||||
jshInterruptOn();
|
jshInterruptOn();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -399,18 +432,6 @@ static bool jstExecuteTaskChecker(UtilTimerTask *task, void *data) {
|
|||||||
return memcmp(&task->data.execute, (UtilTimerTaskExec*)data, sizeof(UtilTimerTaskExec))==0;
|
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) {
|
bool jstPinOutputAtTime(JsSysTime time, uint32_t *timerOffset, Pin *pins, int pinCount, uint8_t value) {
|
||||||
assert(pinCount<=UTILTIMERTASK_PIN_COUNT);
|
assert(pinCount<=UTILTIMERTASK_PIN_COUNT);
|
||||||
UtilTimerTask task;
|
int idx = utilTimerGetUnusedIndex(true/*wait*/);
|
||||||
task.time = (int)time;
|
if (idx<0) return false; // no free tasks even after waiting
|
||||||
task.repeatInterval = 0;
|
UtilTimerTask *task = &utilTimerTaskInfo[idx];
|
||||||
task.type = UET_SET;
|
task->type = UET_SET;
|
||||||
|
task->time = (int)time;
|
||||||
|
task->repeatInterval = 0;
|
||||||
int i;
|
int i;
|
||||||
for (i=0;i<UTILTIMERTASK_PIN_COUNT;i++)
|
for (i=0;i<UTILTIMERTASK_PIN_COUNT;i++)
|
||||||
task.data.set.pins[i] = (Pin)((i<pinCount) ? pins[i] : PIN_UNDEFINED);
|
task->data.set.pins[i] = (Pin)((i<pinCount) ? pins[i] : PIN_UNDEFINED);
|
||||||
task.data.set.value = value;
|
task->data.set.value = value;
|
||||||
|
utilTimerInsertTask(idx, timerOffset);
|
||||||
WAIT_UNTIL(!utilTimerIsFull(), "Utility Timer");
|
return true;
|
||||||
return utilTimerInsertTask(&task, timerOffset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do software PWM on the given pin, using the timer IRQs
|
// 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 anything is wrong, exit now
|
||||||
if (dutyCycle<=0 || dutyCycle>=1 || freq<=0) {
|
if (dutyCycle<=0 || dutyCycle>=1 || freq<=0) {
|
||||||
// remove any timer tasks
|
// 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
|
// Now set pin to the correct state and exit
|
||||||
jshPinSetValue(pin, dutyCycle >= 0.5);
|
jshPinSetValue(pin, dutyCycle >= 0.5);
|
||||||
return false;
|
return false;
|
||||||
@ -468,11 +492,11 @@ bool jstPinPWM(JsVarFloat freq, JsVarFloat dutyCycle, Pin pin) {
|
|||||||
ptr = (ptr+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
|
// 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))) {
|
while (ptr != ((utilTimerTasksTail+UTILTIMERTASK_TASKS-1) & (UTILTIMERTASK_TASKS-1))) {
|
||||||
if (jstPinTaskChecker(&utilTimerTasks[ptr], (void*)&pin)) {
|
if (jstPinTaskChecker(&utilTimerTaskInfo[utilTimerTasks[ptr]], (void*)&pin)) {
|
||||||
if (utilTimerTasks[ptr].data.set.value)
|
if (utilTimerTaskInfo[utilTimerTasks[ptr]].data.set.value)
|
||||||
ptaskon = &utilTimerTasks[ptr];
|
ptaskon = &utilTimerTaskInfo[utilTimerTasks[ptr]];
|
||||||
else
|
else
|
||||||
ptaskoff = &utilTimerTasks[ptr];
|
ptaskoff = &utilTimerTaskInfo[utilTimerTasks[ptr]];
|
||||||
}
|
}
|
||||||
ptr = (ptr+UTILTIMERTASK_TASKS-1) & (UTILTIMERTASK_TASKS-1);
|
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)
|
/// Remove any tasks using the given pin (if they existed)
|
||||||
if (ptaskon || ptaskoff) {
|
if (ptaskon || ptaskoff) {
|
||||||
while (utilTimerRemoveTask(jstPinTaskChecker, (void*)&pin));
|
while (jstStopPinTimerTask(pin));
|
||||||
}
|
}
|
||||||
UtilTimerTask taskon, taskoff;
|
|
||||||
taskon.data.set.value = 1;
|
int onidx = utilTimerGetUnusedIndex(true/*wait*/);
|
||||||
taskoff.data.set.value = 0;
|
if (onidx<0) return false; // no free tasks
|
||||||
taskon.time = (int)period;
|
UtilTimerTask *taskon = &utilTimerTaskInfo[onidx];
|
||||||
taskoff.time = (int)pulseLength;
|
taskon->type = UET_SET;
|
||||||
taskon.repeatInterval = (unsigned int)period;
|
int offidx = utilTimerGetUnusedIndex(true/*wait*/);
|
||||||
taskoff.repeatInterval = (unsigned int)period;
|
if (offidx<0) {
|
||||||
taskon.type = UET_SET;
|
taskon->type = UET_NONE; // free last task
|
||||||
taskoff.type = UET_SET;
|
return false; // no free tasks
|
||||||
taskon.data.set.pins[0] = pin;
|
}
|
||||||
taskoff.data.set.pins[0] = pin;
|
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;
|
int i;
|
||||||
for (i=1;i<UTILTIMERTASK_PIN_COUNT;i++) {
|
for (i=1;i<UTILTIMERTASK_PIN_COUNT;i++) {
|
||||||
taskon.data.set.pins[i] = PIN_UNDEFINED;
|
taskon->data.set.pins[i] = PIN_UNDEFINED;
|
||||||
taskoff.data.set.pins[i] = PIN_UNDEFINED;
|
taskoff->data.set.pins[i] = PIN_UNDEFINED;
|
||||||
}
|
}
|
||||||
|
|
||||||
// first task is to turn on
|
// first task is to turn on
|
||||||
jshPinSetValue(pin, 1);
|
jshPinSetValue(pin, 1);
|
||||||
uint32_t timerOffset = jstGetUtilTimerOffset();
|
uint32_t timerOffset = jstGetUtilTimerOffset();
|
||||||
// now start the 2 PWM tasks
|
// now start the 2 PWM tasks
|
||||||
WAIT_UNTIL(!utilTimerIsFull(), "Utility Timer");
|
utilTimerInsertTask(onidx, &timerOffset);
|
||||||
if (!utilTimerInsertTask(&taskon, &timerOffset)) return false;
|
utilTimerInsertTask(offidx, &timerOffset);
|
||||||
WAIT_UNTIL(!utilTimerIsFull(), "Utility Timer");
|
return true;
|
||||||
return utilTimerInsertTask(&taskoff, &timerOffset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 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
|
/** 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
|
* See utilTimerInsertTask for notes on timerOffset
|
||||||
*/
|
*/
|
||||||
bool jstExecuteFn(UtilTimerTaskExecFn fn, void *userdata, JsSysTime startTime, uint32_t period, uint32_t *timerOffset) {
|
bool jstExecuteFn(UtilTimerTaskExecFn fn, void *userdata, JsSysTime startTime, uint32_t period, uint32_t *timerOffset) {
|
||||||
UtilTimerTask task;
|
int idx = utilTimerGetUnusedIndex(true/*wait*/);
|
||||||
task.time = (int)startTime;
|
if (idx<0) return false; // no free tasks even after waiting
|
||||||
task.repeatInterval = period;
|
UtilTimerTask *task = &utilTimerTaskInfo[idx];
|
||||||
task.type = UET_EXECUTE;
|
task->time = (int)startTime;
|
||||||
task.data.execute.fn = fn;
|
task->repeatInterval = period;
|
||||||
task.data.execute.userdata = userdata;
|
task->type = UET_EXECUTE;
|
||||||
|
task->data.execute.fn = fn;
|
||||||
WAIT_UNTIL(!utilTimerIsFull(), "Utility Timer");
|
task->data.execute.userdata = userdata;
|
||||||
return utilTimerInsertTask(&task, timerOffset);
|
utilTimerInsertTask(idx, timerOffset);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stop executing the given function
|
/// Stop executing the given function
|
||||||
@ -544,37 +577,39 @@ bool jstStopExecuteFn(UtilTimerTaskExecFn fn, void *userdata) {
|
|||||||
UtilTimerTaskExec e;
|
UtilTimerTaskExec e;
|
||||||
e.fn = fn;
|
e.fn = fn;
|
||||||
e.userdata = userdata;
|
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
|
/// Set the utility timer so we're woken up in whatever time period
|
||||||
bool jstSetWakeUp(JsSysTime period) {
|
bool jstSetWakeUp(JsSysTime period) {
|
||||||
UtilTimerTask task;
|
|
||||||
task.time = (int)period;
|
|
||||||
task.repeatInterval = 0;
|
|
||||||
task.type = UET_WAKEUP;
|
|
||||||
|
|
||||||
bool hasTimer = false;
|
bool hasTimer = false;
|
||||||
|
int wakeupTime = (int)period;
|
||||||
int nextTime;
|
int nextTime;
|
||||||
|
|
||||||
|
|
||||||
// work out if we're waiting for a timer,
|
// work out if we're waiting for a timer,
|
||||||
// and if so, when it's going to be
|
// and if so, when it's going to be
|
||||||
jshInterruptOff();
|
jshInterruptOff();
|
||||||
if (utilTimerTasksTail!=utilTimerTasksHead) {
|
if (utilTimerTasksTail!=utilTimerTasksHead) {
|
||||||
hasTimer = true;
|
hasTimer = true;
|
||||||
nextTime = utilTimerTasks[utilTimerTasksTail].time;
|
nextTime = utilTimerTaskInfo[utilTimerTasks[utilTimerTasksTail]].time;
|
||||||
}
|
}
|
||||||
jshInterruptOn();
|
jshInterruptOn();
|
||||||
|
|
||||||
if (hasTimer && task.time >= nextTime) {
|
if (hasTimer && wakeupTime >= nextTime) {
|
||||||
// we already had a timer, and it's going to wake us up sooner.
|
// we already had a timer, and it's going to wake us up sooner.
|
||||||
// don't create a WAKEUP timer task
|
// don't create a WAKEUP timer task
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ok = utilTimerInsertTask(&task, NULL);
|
int idx = utilTimerGetUnusedIndex(false/*don't wait*/);
|
||||||
// We wait until the timer is out of the reload event, because the reload event itself would wake us up
|
if (idx<0) return false; // no free tasks!
|
||||||
return ok;
|
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
|
/** If the first timer task is a wakeup task, remove it. This stops
|
||||||
@ -585,7 +620,7 @@ void jstClearWakeUp() {
|
|||||||
jshInterruptOff();
|
jshInterruptOff();
|
||||||
// while the first item is a wakeup, remove it
|
// while the first item is a wakeup, remove it
|
||||||
while (utilTimerTasksTail!=utilTimerTasksHead &&
|
while (utilTimerTasksTail!=utilTimerTasksHead &&
|
||||||
utilTimerTasks[utilTimerTasksTail].type == UET_WAKEUP) {
|
utilTimerTaskInfo[utilTimerTasks[utilTimerTasksTail]].type == UET_WAKEUP) {
|
||||||
utilTimerTasksTail = (utilTimerTasksTail+1) & (UTILTIMERTASK_TASKS-1);
|
utilTimerTasksTail = (utilTimerTasksTail+1) & (UTILTIMERTASK_TASKS-1);
|
||||||
removedTimer = true;
|
removedTimer = true;
|
||||||
}
|
}
|
||||||
@ -597,37 +632,39 @@ void jstClearWakeUp() {
|
|||||||
|
|
||||||
#ifndef SAVE_ON_FLASH
|
#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(jsvIsString(currentData));
|
||||||
assert(jsvIsUndefined(nextData) || jsvIsString(nextData));
|
assert(jsvIsUndefined(nextData) || jsvIsString(nextData));
|
||||||
if (!jshIsPinValid(pin)) return false;
|
if (!jshIsPinValid(pin)) return -1;
|
||||||
UtilTimerTask task;
|
int idx = utilTimerGetUnusedIndex(true/*wait*/);
|
||||||
task.repeatInterval = (unsigned int)period;
|
if (idx<0) return -1; // no free tasks!
|
||||||
task.time = (int)(startTime + period);
|
UtilTimerTask *task = &utilTimerTaskInfo[idx];
|
||||||
task.type = type;
|
task->type = type;
|
||||||
|
task->repeatInterval = (unsigned int)period;
|
||||||
|
task->time = (int)(startTime + period);
|
||||||
if (UET_IS_BUFFER_WRITE_EVENT(type)) {
|
if (UET_IS_BUFFER_WRITE_EVENT(type)) {
|
||||||
task.data.buffer.pin = pin;
|
task->data.buffer.pin = pin;
|
||||||
task.data.buffer.npin = npin;
|
task->data.buffer.npin = npin;
|
||||||
} else if (UET_IS_BUFFER_READ_EVENT(type)) {
|
} else if (UET_IS_BUFFER_READ_EVENT(type)) {
|
||||||
#ifndef LINUX
|
#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
|
#endif
|
||||||
task.data.buffer.pin = pin;
|
task->data.buffer.pin = pin;
|
||||||
} else {
|
} else {
|
||||||
assert(0);
|
assert(0);
|
||||||
return false;
|
return -1;
|
||||||
}
|
}
|
||||||
task.data.buffer.currentBuffer = jsvGetRef(currentData);
|
task->data.buffer.currentBuffer = jsvGetRef(currentData);
|
||||||
if (nextData) {
|
if (nextData) {
|
||||||
// then we're repeating!
|
// then we're repeating!
|
||||||
task.data.buffer.nextBuffer = jsvGetRef(nextData);
|
task->data.buffer.nextBuffer = jsvGetRef(nextData);
|
||||||
} else {
|
} else {
|
||||||
// then we're not repeating
|
// then we're not repeating
|
||||||
task.data.buffer.nextBuffer = 0;
|
task->data.buffer.nextBuffer = 0;
|
||||||
}
|
}
|
||||||
jstUtilTimerSetupBuffer(&task);
|
jstUtilTimerSetupBuffer(task);
|
||||||
|
utilTimerInsertTask(idx, NULL);
|
||||||
return utilTimerInsertTask(&task, 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'
|
/// Remove the task that uses the buffer 'var'
|
||||||
bool jstStopBufferTimerTask(JsVar *var) {
|
bool jstStopBufferTimerTask(JsVar *var) {
|
||||||
JsVarRef ref = jsvGetRef(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
|
/// Remove the task that uses the given pin
|
||||||
bool jstStopPinTimerTask(Pin pin) {
|
bool jstStopPinTimerTask(Pin pin) {
|
||||||
return utilTimerRemoveTask(jstPinTaskChecker, (void*)&pin);
|
return utilTimerRemoveTask(utilTimerFindTask(jstPinTaskChecker, (void*)&pin));
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void jstReset() {
|
void jstReset() {
|
||||||
jshUtilTimerDisable();
|
jshUtilTimerDisable();
|
||||||
@ -660,46 +697,13 @@ void jstSystemTimeChanged(JsSysTime diff) {
|
|||||||
// we don't actually care since timers should hopefully just run based on delays
|
// we don't actually care since timers should hopefully just run based on delays
|
||||||
}
|
}
|
||||||
|
|
||||||
void jstDumpUtilityTimers() {
|
/// Called from the idle loop if a custom event is received (could be for the timer)
|
||||||
int i;
|
void jstOnCustomEvent(IOEventFlags eventFlags, uint8_t *data, int dataLen) {
|
||||||
UtilTimerTask uTimerTasks[UTILTIMERTASK_TASKS];
|
IOCustomEventFlags customFlags = *(IOCustomEventFlags*)data;
|
||||||
jshInterruptOff();
|
if ((customFlags&EVC_TYPE_MASK)==EVC_TIMER_FINISHED) {
|
||||||
for (i=0;i<UTILTIMERTASK_TASKS;i++)
|
int id = customFlags >> EVC_DATA_SHIFT;
|
||||||
uTimerTasks[i] = utilTimerTasks[i];
|
if (utilTimerTaskInfo[id].type & UET_FINISHED) {
|
||||||
unsigned char uTimerTasksHead = utilTimerTasksHead;
|
utilTimerTaskInfo[id].type = UET_NONE;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
t = (t+1) & (UTILTIMERTASK_TASKS-1);
|
|
||||||
}
|
}
|
||||||
if (!hadTimers)
|
|
||||||
jsiConsolePrintf("No Timers found.\n");
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,6 +32,7 @@ typedef enum {
|
|||||||
#ifdef ESPR_USE_STEPPER_TIMER
|
#ifdef ESPR_USE_STEPPER_TIMER
|
||||||
UET_STEP, ///< Write stepper motor
|
UET_STEP, ///< Write stepper motor
|
||||||
#endif
|
#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;
|
} PACKED_FLAGS UtilTimerEventType;
|
||||||
|
|
||||||
#define UET_IS_SET_EVENT(T) (\
|
#define UET_IS_SET_EVENT(T) (\
|
||||||
@ -55,6 +56,23 @@ typedef enum {
|
|||||||
|
|
||||||
#define UTILTIMERTASK_PIN_COUNT (8)
|
#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 {
|
typedef struct UtilTimerTaskSet {
|
||||||
Pin pins[UTILTIMERTASK_PIN_COUNT]; ///< pins to set (must be in same location as UtilTimerTaskStep.pins)
|
Pin pins[UTILTIMERTASK_PIN_COUNT]; ///< pins to set (must be in same location as UtilTimerTaskStep.pins)
|
||||||
uint8_t value; ///< value to set pins to
|
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
|
UtilTimerEventType type; // the type of this task - do we set pin(s) or read/write data
|
||||||
} PACKED_FLAGS UtilTimerTask;
|
} 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();
|
void jstUtilTimerInterruptHandler();
|
||||||
|
|
||||||
/// Wait until the utility timer is totally empty (use with care as timers can repeat)
|
/// 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 */
|
* before the wakeup event */
|
||||||
void jstClearWakeUp();
|
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
|
/// 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
|
||||||
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);
|
||||||
|
|
||||||
/// Remove the task that uses the buffer 'var'
|
/// Remove the task that uses the buffer 'var'
|
||||||
bool jstStopBufferTimerTask(JsVar *var);
|
bool jstStopBufferTimerTask(JsVar *var);
|
||||||
@ -165,26 +187,31 @@ void jstReset();
|
|||||||
This should be done with interrupts off */
|
This should be done with interrupts off */
|
||||||
void jstSystemTimeChanged(JsSysTime diff);
|
void jstSystemTimeChanged(JsSysTime diff);
|
||||||
|
|
||||||
/// Dump the current list of timers
|
|
||||||
void jstDumpUtilityTimers();
|
|
||||||
|
|
||||||
/* Restart the utility timer with the right period. This should not normally
|
/* Restart the utility timer with the right period. This should not normally
|
||||||
need to be called by anything outside jstimer.c */
|
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.
|
/** 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 the delay at which to execute the task. If timerOffset!==NULL then
|
||||||
* task.time is relative to the time at which timerOffset=jstGetUtilTimerOffset().
|
* task.time is relative to the time at which timerOffset=jstGetUtilTimerOffset().
|
||||||
* This allows pulse trains/etc to be scheduled in sync.
|
* 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
|
/// Find a task that 'checkCallback' returns true for. Returns -1 if none found
|
||||||
bool utilTimerRemoveTask(bool (checkCallback)(UtilTimerTask *task, void* data), void *checkCallbackData);
|
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
|
/// 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);
|
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_ */
|
#endif /* JSTIMER_H_ */
|
||||||
|
|
||||||
|
|||||||
12
src/jsvar.c
12
src/jsvar.c
@ -2684,6 +2684,10 @@ bool jsvIsStringIEqualAndUnLock(JsVar *var, const char *str) {
|
|||||||
jsvUnLock(var);
|
jsvUnLock(var);
|
||||||
return b;
|
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
|
/** 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);
|
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).
|
// 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
|
// Makes sure all of itemPtr either contains a JsVar or 0
|
||||||
void jsvGetArrayItems(JsVar *arr, unsigned int itemCount, JsVar **itemPtr) {
|
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_STRING_0:
|
||||||
case JSV_ARRAY:
|
case JSV_ARRAY:
|
||||||
case JSV_FUNCTION:
|
case JSV_FUNCTION:
|
||||||
|
if (*((JsVar**)configs[i].ptr))
|
||||||
|
jsvUnLock(*((JsVar**)configs[i].ptr));
|
||||||
*((JsVar**)configs[i].ptr) = jsvLockAgain(val); break;
|
*((JsVar**)configs[i].ptr) = jsvLockAgain(val); break;
|
||||||
#ifndef ESPR_EMBED
|
#ifndef ESPR_EMBED
|
||||||
case JSV_PIN: *((Pin*)configs[i].ptr) = jshGetPinFromVar(val); break;
|
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 jsvIsStringEqualOrStartsWith(JsVar *var, const char *str, bool isStartsWith);
|
||||||
bool jsvIsStringEqual(JsVar *var, const char *str); ///< is string equal. see jsvIsStringEqualOrStartsWith
|
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
|
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
|
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.
|
/// 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 *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)
|
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 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
|
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 *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)
|
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;
|
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{
|
/*JSON{
|
||||||
"type" : "staticmethod",
|
"type" : "staticmethod",
|
||||||
"class" : "E",
|
"class" : "E",
|
||||||
|
|||||||
@ -51,7 +51,6 @@ void jswrap_espruino_setConsole(JsVar *device, JsVar *options);
|
|||||||
JsVar *jswrap_espruino_getConsole();
|
JsVar *jswrap_espruino_getConsole();
|
||||||
|
|
||||||
int jswrap_espruino_reverseByte(int v);
|
int jswrap_espruino_reverseByte(int v);
|
||||||
void jswrap_espruino_dumpTimers();
|
|
||||||
void jswrap_espruino_dumpLockedVars();
|
void jswrap_espruino_dumpLockedVars();
|
||||||
void jswrap_espruino_dumpFreeList();
|
void jswrap_espruino_dumpFreeList();
|
||||||
void jswrap_e_dumpFragmentation();
|
void jswrap_e_dumpFragmentation();
|
||||||
|
|||||||
@ -269,19 +269,20 @@ void jswrap_io_digitalPulse(Pin pin, bool value, JsVar *times) {
|
|||||||
uint32_t timerOffset = jstGetUtilTimerOffset();
|
uint32_t timerOffset = jstGetUtilTimerOffset();
|
||||||
bool hasTimer = jstGetLastPinTimerTask(pin, &task);
|
bool hasTimer = jstGetLastPinTimerTask(pin, &task);
|
||||||
if (!hasTimer) task.time = 0;
|
if (!hasTimer) task.time = 0;
|
||||||
|
// set first edge
|
||||||
|
if (!hasTimer)
|
||||||
|
jshPinOutput(pin, value);
|
||||||
// now start either one or a series of pulses
|
// now start either one or a series of pulses
|
||||||
if (jsvIsNumeric(times)) {
|
if (jsvIsNumeric(times)) {
|
||||||
JsVarFloat pulseTime = jsvGetFloat(times);
|
JsVarFloat pulseTime = jsvGetFloat(times);
|
||||||
if (pulseTime<0 || isnan(pulseTime)) {
|
if (pulseTime<0 || isnan(pulseTime)) {
|
||||||
jsExceptionHere(JSET_ERROR, "Pulse Time is less than 0 or not a number");
|
jsExceptionHere(JSET_ERROR, "Pulse Time is less than 0 or not a number");
|
||||||
} else if (pulseTime>0) {
|
} else if (pulseTime>0) {
|
||||||
if (!hasTimer) jshPinOutput(pin, value);
|
|
||||||
task.time += jshGetTimeFromMilliseconds(pulseTime);
|
task.time += jshGetTimeFromMilliseconds(pulseTime);
|
||||||
jstPinOutputAtTime(task.time, &timerOffset, &pin, 1, !value);
|
jstPinOutputAtTime(task.time, &timerOffset, &pin, 1, !value);
|
||||||
} else jstUtilTimerWaitEmpty(); // time==0
|
} else jstUtilTimerWaitEmpty(); // time==0
|
||||||
} else if (jsvIsIterable(times)) {
|
} else if (jsvIsIterable(times)) {
|
||||||
// iterable, so output a square wave
|
// iterable, so output a square wave
|
||||||
if (!hasTimer) jshPinOutput(pin, value);
|
|
||||||
JsvIterator it;
|
JsvIterator it;
|
||||||
jsvIteratorNew(&it, times, JSIF_EVERY_ARRAY_ELEMENT);
|
jsvIteratorNew(&it, times, JSIF_EVERY_ARRAY_ELEMENT);
|
||||||
while (jsvIteratorHasElement(&it)) {
|
while (jsvIteratorHasElement(&it)) {
|
||||||
|
|||||||
@ -131,29 +131,6 @@ void jswrap_pin_write(
|
|||||||
jshPinOutput(pin, value);
|
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{
|
/*JSON{
|
||||||
"type" : "method",
|
"type" : "method",
|
||||||
"class" : "Pin",
|
"class" : "Pin",
|
||||||
|
|||||||
@ -86,66 +86,51 @@ static bool jswrap_stepper_getPattern(JsVar *stepper, uint8_t *pattern) {
|
|||||||
return ok;
|
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{
|
/*JSON{
|
||||||
"type" : "idle",
|
"type" : "EV_CUSTOM",
|
||||||
"generate" : "jswrap_stepper_idle",
|
"generate" : "jswrap_stepper_eventHandler",
|
||||||
"ifdef" : "ESPR_USE_STEPPER_TIMER"
|
"ifdef" : "ESPR_USE_STEPPER_TIMER"
|
||||||
}*/
|
}
|
||||||
bool jswrap_stepper_idle() {
|
*/
|
||||||
JsVar *steppers = jsvObjectGetChildIfExists(execInfo.hiddenRoot, JSI_STEPPER_NAME);
|
void jswrap_stepper_eventHandler(IOEventFlags eventFlags, uint8_t *data, int length) {
|
||||||
if (steppers) {
|
IOCustomEventFlags customFlags = *(IOCustomEventFlags*)data;
|
||||||
JsvObjectIterator it;
|
if ((customFlags&EVC_TYPE_MASK)==EVC_TIMER_FINISHED) {
|
||||||
jsvObjectIteratorNew(&it, steppers);
|
int id = customFlags >> EVC_DATA_SHIFT;
|
||||||
while (jsvObjectIteratorHasValue(&it)) {
|
JsVar *stepper = _jswrap_stepper_getById(id);
|
||||||
JsVar *stepper = jsvObjectIteratorGetValue(&it);
|
if (stepper) {
|
||||||
bool running = jsvObjectGetBoolChild(stepper, "running");
|
jsiQueueObjectCallbacks(stepper, JS_EVENT_PREFIX"finish", NULL, 0);
|
||||||
Pin pins[4];
|
// Update current position
|
||||||
if (running) {
|
jsvObjectSetChildAndUnLock(stepper, "pos", jsvNewFromInteger(
|
||||||
UtilTimerTask task;
|
jsvObjectGetIntegerChild(stepper, "pos")+
|
||||||
// Search for a timer task
|
jsvObjectGetIntegerChild(stepper, "_direction")));
|
||||||
if (!jswrap_stepper_getPins(stepper, pins) || !jstGetLastPinTimerTask(pins[0], &task)) {
|
jsvObjectRemoveChild(stepper, "_direction");
|
||||||
// if the task is now gone...
|
if (jsvObjectGetBoolChild(stepper, "_turnOff")) {
|
||||||
jsiQueueObjectCallbacks(stepper, JS_EVENT_PREFIX"finish", NULL, 0);
|
Pin pins[4];
|
||||||
// Update current position
|
int offpattern = jsvObjectGetIntegerChild(stepper, "offpattern");
|
||||||
jsvObjectSetChildAndUnLock(stepper, "pos", jsvNewFromInteger(
|
if (jswrap_stepper_getPins(stepper, pins)) {
|
||||||
jsvObjectGetIntegerChild(stepper, "pos")+
|
for (int i=0;i<4;i++)
|
||||||
jsvObjectGetIntegerChild(stepper, "_direction")));
|
jshPinSetValue(pins[i], (offpattern>>i)&1);
|
||||||
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?
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jsvUnLock(stepper);
|
jsvObjectRemoveChild(stepper, "_turnOff");
|
||||||
// if not running, remove stepper from this list
|
jsvObjectSetChildAndUnLock(stepper, "running", jsvNewFromBool(false));
|
||||||
if (!running)
|
JsVar *promise = jsvObjectGetChildIfExists(stepper, "promise");
|
||||||
jsvObjectIteratorRemoveAndGotoNext(&it, steppers);
|
if (promise) {
|
||||||
else
|
jsvObjectRemoveChild(stepper, "promise");
|
||||||
jsvObjectIteratorNext(&it);
|
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{
|
/*JSON{
|
||||||
@ -163,7 +148,7 @@ void jswrap_stepper_kill() { // be sure to remove all stepper instances...
|
|||||||
bool running = jsvObjectGetBoolChild(stepper, "running");
|
bool running = jsvObjectGetBoolChild(stepper, "running");
|
||||||
if (running) {
|
if (running) {
|
||||||
Pin pins[4];
|
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");
|
jsExceptionHere(JSET_ERROR, "Stepper couldn't be stopped");
|
||||||
}
|
}
|
||||||
jsvUnLock(stepper);
|
jsvUnLock(stepper);
|
||||||
@ -318,32 +303,33 @@ JsVar *jswrap_stepper_moveTo(JsVar *stepper, int position, JsVar *options) {
|
|||||||
JsVar *promise = jspromise_create();
|
JsVar *promise = jspromise_create();
|
||||||
jsvObjectSetChild(stepper, "promise", promise);
|
jsvObjectSetChild(stepper, "promise", promise);
|
||||||
|
|
||||||
UtilTimerTask task;
|
int idx = utilTimerGetUnusedIndex(true/*wait*/);
|
||||||
task.time = 0;
|
if (idx<0) {
|
||||||
task.repeatInterval = jshGetTimeFromMilliseconds(1000.0 / freq);
|
jsvUnLock(promise); // WAIT_UNTIL already errored
|
||||||
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);
|
|
||||||
return 0;
|
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
|
// And finally set it up
|
||||||
|
jsvObjectSetChildAndUnLock(stepper, "timer", jsvNewFromInteger(idx));
|
||||||
jsvObjectSetChildAndUnLock(stepper, "running", jsvNewFromBool(true));
|
jsvObjectSetChildAndUnLock(stepper, "running", jsvNewFromBool(true));
|
||||||
jsvObjectSetChildAndUnLock(stepper, "_direction", jsvNewFromInteger(direction));
|
jsvObjectSetChildAndUnLock(stepper, "_direction", jsvNewFromInteger(direction));
|
||||||
jsvObjectSetChildAndUnLock(stepper, "_turnOff", jsvNewFromBool(turnOff));
|
jsvObjectSetChildAndUnLock(stepper, "_turnOff", jsvNewFromBool(turnOff));
|
||||||
// Add to our list of active steppers
|
// Add to our list of active steppers
|
||||||
JsVar *steppers = jsvObjectGetChild(execInfo.hiddenRoot, JSI_STEPPER_NAME, JSV_ARRAY);
|
JsVar *steppers = jsvObjectGetChild(execInfo.hiddenRoot, JSI_STEPPER_NAME, JSV_ARRAY);
|
||||||
if (steppers) {
|
if (steppers) {
|
||||||
jsvArrayPush(steppers, stepper);
|
jsvSetArrayItem(steppers, idx, stepper);
|
||||||
jsvUnLock(steppers);
|
jsvUnLock(steppers);
|
||||||
}
|
}
|
||||||
return promise;
|
return promise;
|
||||||
@ -373,7 +359,7 @@ void jswrap_stepper_stop(JsVar *stepper, JsVar *options) {
|
|||||||
bool turnOff = jsvObjectGetBoolChild(options, "turnOff");
|
bool turnOff = jsvObjectGetBoolChild(options, "turnOff");
|
||||||
if (turnOff)
|
if (turnOff)
|
||||||
jsvObjectSetChildAndUnLock(stepper, "_turnOff", jsvNewFromBool(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];
|
Pin pins[4];
|
||||||
@ -386,12 +372,10 @@ void jswrap_stepper_stop(JsVar *stepper, JsVar *options) {
|
|||||||
jsvObjectGetIntegerChild(stepper, "_direction") -
|
jsvObjectGetIntegerChild(stepper, "_direction") -
|
||||||
task.data.step.steps));
|
task.data.step.steps));
|
||||||
}
|
}
|
||||||
ok = jstStopPinTimerTask(pins[0]);
|
ok = jstStopPinTimerTask(pins[0]); // FIXME: use timer index
|
||||||
}
|
}
|
||||||
if (!ok)
|
if (!ok)
|
||||||
jsExceptionHere(JSET_ERROR, "Stepper couldn't be stopped");
|
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"
|
#include "jshardware.h"
|
||||||
|
|
||||||
bool jswrap_stepper_idle();
|
|
||||||
void jswrap_stepper_kill();
|
void jswrap_stepper_kill();
|
||||||
|
void jswrap_stepper_eventHandler(IOEventFlags eventFlags, uint8_t *data, int length);
|
||||||
JsVar *jswrap_stepper_constructor(JsVar *options);
|
JsVar *jswrap_stepper_constructor(JsVar *options);
|
||||||
JsVar *jswrap_stepper_moveTo(JsVar *stepper, int position, JsVar *options);
|
JsVar *jswrap_stepper_moveTo(JsVar *stepper, int position, JsVar *options);
|
||||||
void jswrap_stepper_stop(JsVar *stepper, 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{
|
/*JSON{
|
||||||
"type" : "kill",
|
"type" : "kill",
|
||||||
"generate" : "jswrap_waveform_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
|
// 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");
|
jsWarn("Unable to schedule a timer");
|
||||||
|
else
|
||||||
|
jsvObjectSetChildAndUnLock(waveform, "freq", jsvNewFromInteger(timerId));
|
||||||
jsvUnLock2(buffer,buffer2);
|
jsvUnLock2(buffer,buffer2);
|
||||||
|
|
||||||
jsvObjectSetChildAndUnLock(waveform, "running", jsvNewFromBool(true));
|
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
|
// Add to our list of active waveforms
|
||||||
JsVar *waveforms = jsvObjectGetChild(execInfo.hiddenRoot, JSI_WAVEFORM_NAME, JSV_ARRAY);
|
JsVar *waveforms = jsvObjectGetChild(execInfo.hiddenRoot, JSI_WAVEFORM_NAME, JSV_ARRAY);
|
||||||
if (waveforms) {
|
if (waveforms) {
|
||||||
jsvArrayPush(waveforms, waveform);
|
jsvSetArrayItem(waveforms, timerId, waveform);
|
||||||
jsvUnLock(waveforms);
|
jsvUnLock(waveforms);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -378,8 +325,57 @@ void jswrap_waveform_stop(JsVar *waveform) {
|
|||||||
jsExceptionHere(JSET_ERROR, "Waveform couldn't be stopped");
|
jsExceptionHere(JSET_ERROR, "Waveform couldn't be stopped");
|
||||||
}
|
}
|
||||||
jsvUnLock(buffer);
|
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
|
#endif
|
||||||
|
|
||||||
|
|||||||
@ -16,8 +16,8 @@
|
|||||||
|
|
||||||
#include "jshardware.h"
|
#include "jshardware.h"
|
||||||
|
|
||||||
bool jswrap_waveform_idle();
|
|
||||||
void jswrap_waveform_kill();
|
void jswrap_waveform_kill();
|
||||||
|
void jswrap_waveform_eventHandler(IOEventFlags eventFlags, uint8_t *data, int length);
|
||||||
JsVar *jswrap_waveform_constructor(JsVar *samples, JsVar *options);
|
JsVar *jswrap_waveform_constructor(JsVar *samples, JsVar *options);
|
||||||
void jswrap_waveform_startOutput(JsVar *waveform, Pin pin, JsVarFloat freq, 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);
|
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.
|
/// Convert a time in Milliseconds to one in ticks.
|
||||||
JsSysTime jshGetTimeFromMilliseconds(JsVarFloat ms) {
|
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.
|
/// Convert ticks to a time in Milliseconds.
|
||||||
@ -2956,13 +2956,11 @@ void jsvGetProcessorPowerUsage(JsVar *devices) {
|
|||||||
void COMP_LPCOMP_IRQHandler() {
|
void COMP_LPCOMP_IRQHandler() {
|
||||||
if (nrf_lpcomp_event_check(NRF_LPCOMP_EVENT_UP) && nrf_lpcomp_int_enable_check(LPCOMP_INTENSET_UP_Msk)) {
|
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);
|
nrf_lpcomp_event_clear(NRF_LPCOMP_EVENT_UP);
|
||||||
IOCustomEventFlags customFlags = EVC_LPCOMP | EVC_DATA_LPCOMP_UP;
|
jshPushCustomEvent(EVC_LPCOMP | EVC_DATA_LPCOMP_UP);
|
||||||
jshPushEvent(EV_CUSTOM, (uint8_t*)&customFlags, sizeof(customFlags));
|
|
||||||
}
|
}
|
||||||
if (nrf_lpcomp_event_check(NRF_LPCOMP_EVENT_DOWN) && nrf_lpcomp_int_enable_check(LPCOMP_INTENSET_DOWN_Msk)) {
|
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);
|
nrf_lpcomp_event_clear(NRF_LPCOMP_EVENT_DOWN);
|
||||||
IOCustomEventFlags customFlags = EVC_LPCOMP;
|
jshPushCustomEvent(EVC_LPCOMP);
|
||||||
jshPushEvent(EV_CUSTOM, (uint8_t*)&customFlags, sizeof(customFlags));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user