rt-thread/components/external/espruino/src/jswrap_io.c

438 lines
20 KiB
C

/*
* This file is part of Espruino, a JavaScript interpreter for Microcontrollers
*
* Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* ----------------------------------------------------------------------------
* This file is designed to be parsed during the build process
*
* JavaScript Hardware IO Functions
* ----------------------------------------------------------------------------
*/
#include "jswrap_io.h"
#include "jsvar.h"
/*JSON{ "type":"function", "name" : "peek8",
"description" : [ "Read 8 bits of memory at the given location - DANGEROUS!" ],
"generate_full" : "(JsVarInt)*(unsigned char*)jsvGetInteger(addr)",
"params" : [ [ "addr", "int", "The address in memory to read"] ],
"return" : ["int", "The value of memory at the given location"]
}*/
/*JSON{ "type":"function", "name" : "poke8",
"description" : [ "Write 8 bits of memory at the given location - VERY DANGEROUS!" ],
"generate_full" : "(*(unsigned char*)jsvGetInteger(addr)) = (unsigned char)jsvGetInteger(value)",
"params" : [ [ "addr", "int", "The address in memory to write"],
[ "value", "int", "The value to write"] ]
}*/
/*JSON{ "type":"function", "name" : "peek16",
"description" : [ "Read 16 bits of memory at the given location - DANGEROUS!" ],
"generate_full" : "(JsVarInt)*(unsigned short*)jsvGetInteger(addr)",
"params" : [ [ "addr", "int", "The address in memory to read"] ],
"return" : ["int", "The value of memory at the given location"]
}*/
/*JSON{ "type":"function", "name" : "poke16",
"description" : [ "Write 16 bits of memory at the given location - VERY DANGEROUS!" ],
"generate_full" : "(*(unsigned short*)jsvGetInteger(addr)) = (unsigned short)jsvGetInteger(value)",
"params" : [ [ "addr", "int", "The address in memory to write"],
[ "value", "int", "The value to write"] ]
}*/
/*JSON{ "type":"function", "name" : "peek32",
"description" : [ "Read 32 bits of memory at the given location - DANGEROUS!" ],
"generate_full" : "(JsVarInt)*(unsigned int*)jsvGetInteger(addr)",
"params" : [ [ "addr", "int", "The address in memory to read"] ],
"return" : ["int", "The value of memory at the given location"]
}*/
/*JSON{ "type":"function", "name" : "poke32",
"description" : [ "Write 32 bits of memory at the given location - VERY DANGEROUS!" ],
"generate_full" : "(*(unsigned int*)jsvGetInteger(addr)) = (unsigned int)jsvGetInteger(value)",
"params" : [ [ "addr", "int", "The address in memory to write"],
[ "value", "int", "The value to write"] ]
}*/
/*JSON{ "type":"function", "name" : "analogRead",
"description" : ["Get the analog value of the given pin",
"This is different to Arduino which only returns an integer between 0 and 1023",
"However only pins connected to an ADC will work (see the datasheet)"],
"generate" : "jshPinAnalog",
"params" : [ [ "pin", "pin", "The pin to use"] ],
"return" : ["float", "The analog Value of the Pin between 0 and 1"]
}*/
/*JSON{ "type":"function", "name" : "analogWrite",
"description" : "Set the analog Value of a pin. It will be output using PWM",
"generate" : "jswrap_io_analogWrite",
"params" : [ [ "pin", "pin", "The pin to use"],
[ "value", "float", "A value between 0 and 1"],
[ "options", "JsVar", ["An object containing options.",
"Currently only freq (pulse frequency in Hz) is available: ```analogWrite(LED1,0.5,{ freq : 10 });``` ",
"Note that specifying a frequency will force PWM output, even if the pin has a DAC"] ] ]
}*/
void jswrap_io_analogWrite(Pin pin, JsVarFloat value, JsVar *options) {
JsVarFloat freq = 0;
if (jsvIsObject(options)) {
freq = jsvGetFloatAndUnLock(jsvObjectGetChild(options, "freq", 0));
}
jshPinAnalogOutput(pin, value, freq);
}
/*JSON{ "type":"function", "name" : "digitalPulse",
"description" : ["Pulse the pin with the value for the given time in milliseconds",
"eg. ```pulse(A0,1,5);``` pulses A0 high for 5ms",
"digitalPulse is for SHORT pulses that need to be very accurate. If you're doing anything over a few milliseconds, use setTimeout instead" ],
"generate" : "jswrap_io_digitalPulse",
"params" : [ [ "pin", "pin", "The pin to use"],
[ "value", "bool", "Whether to pulse high (true) or low (false)"],
[ "time", "float", "A time in milliseconds"] ]
}*/
void jswrap_io_digitalPulse(Pin pin, bool value, JsVarFloat time) {
if (time<=0) {
jsWarn("Pulse Time given for digitalPulse is less that or equal to 0");
} else {
//jsPrintInt((JsVarInt)(time*1000));
jshPinPulse(pin, value, time);
}
}
/*JSON{ "type":"function", "name" : "digitalWrite",
"description" : ["Set the digital value of the given pin",
"If pin is an array of pins, eg. ```[A2,A1,A0]``` the value will be treated as an integer where the first array element is the MSB" ],
"generate" : "jswrap_io_digitalWrite",
"params" : [ [ "pin", "JsVar", "The pin to use"],
[ "value", "int", "Whether to pulse high (true) or low (false)"] ]
}*/
void jswrap_io_digitalWrite(JsVar *pinVar, JsVarInt value) {
if (jsvIsArray(pinVar)) {
JsVarRef pinName = pinVar->lastChild; // NOTE: start at end and work back!
while (pinName) {
JsVar *pinNamePtr = jsvLock(pinName);
JsVar *pinPtr = jsvSkipName(pinNamePtr);
jshPinOutput(jshGetPinFromVar(pinPtr), value&1);
jsvUnLock(pinPtr);
pinName = pinNamePtr->prevSibling;
jsvUnLock(pinNamePtr);
value = value>>1; // next bit down
}
} else {
Pin pin = jshGetPinFromVar(pinVar);
jshPinOutput(pin, value!=0);
}
}
/*JSON{ "type":"function", "name" : "digitalRead",
"description" : ["Get the digital value of the given pin",
"If pin is an array of pins, eg. ```[A2,A1,A0]``` the value will be treated as an integer where the first array element is the MSB" ],
"generate" : "jswrap_io_digitalRead",
"params" : [ [ "pin", "JsVar", "The pin to use"] ],
"return" : ["int", "The digital Value of the Pin"]
}*/
JsVarInt jswrap_io_digitalRead(JsVar *pinVar) {
if (jsvIsArray(pinVar)) {
int pins = 0;
JsVarInt value = 0;
JsVarRef pinName = pinVar->firstChild;
while (pinName) {
JsVar *pinNamePtr = jsvLock(pinName);
JsVar *pinPtr = jsvSkipName(pinNamePtr);
value = (value<<1) | jshPinInput(jshGetPinFromVar(pinPtr));
jsvUnLock(pinPtr);
pinName = pinNamePtr->nextSibling;
jsvUnLock(pinNamePtr);
pins++;
}
if (pins==0) return 0; // return undefined if array empty
return value;
} else {
Pin pin = jshGetPinFromVar(pinVar);
return jshPinInput(pin);
}
}
/*JSON{ "type":"function", "name" : "pinMode",
"description" : ["Set the mode of the given pin - note that digitalRead/digitalWrite/etc set this automatically unless pinMode has been called first. If you want digitalRead/etc to set the pin mode automatically after you have called pinMode, simply call it again with no mode argument: ```pinMode(pin)```" ],
"generate" : "jswrap_io_pinMode",
"params" : [ [ "pin", "pin", "The pin to use"], [ "mode", "JsVar", "The mode - a string that is either 'input', 'output', 'input_pullup', or 'input_pulldown'. Do not include this argument if you want to revert to automatic pin mode setting."] ]
}*/
void jswrap_io_pinMode(Pin pin, JsVar *mode) {
if (!jshIsPinValid(pin)) {
jsError("Invalid pin");
return;
}
JshPinState m = JSHPINSTATE_UNDEFINED;
if (jsvIsString(mode)) {
if (jsvIsStringEqual(mode, "input")) m = JSHPINSTATE_GPIO_IN;
if (jsvIsStringEqual(mode, "input_pullup")) m = JSHPINSTATE_GPIO_IN_PULLUP;
if (jsvIsStringEqual(mode, "input_pulldown")) m = JSHPINSTATE_GPIO_IN_PULLDOWN;
if (jsvIsStringEqual(mode, "output")) m = JSHPINSTATE_GPIO_OUT;
}
if (m != JSHPINSTATE_UNDEFINED) {
jshSetPinStateIsManual(pin, true);
jshPinSetState(pin, m);
} else {
jshSetPinStateIsManual(pin, false);
if (!jsvIsUndefined(mode)) {
jsError("Unknown pin mode");
}
}
}
/*XXXX{ "type":"function", "name" : "bitBang",
"description" : ["bitBang out a message in a one-wire style BROKEN CURRENTLY" ],
"generate" : "jshBitBang",
"params" : [ [ "pin", "pin", "The pin to use"],
[ "t0h", "float", "The time (in milliseconds) to spend high for a 0"],
[ "t0l", "float", "The time (in milliseconds) to spend low for a 0"],
[ "t1h", "float", "The time (in milliseconds) to spend high for a 1"],
[ "t1l", "float", "The time (in milliseconds) to spend low for a 1"],
[ "data", "JsVar", "A string representing the data"] ]
}*/
/*JSON{ "type":"function", "name" : "setInterval",
"description" : ["Call the function specified REPEATEDLY after the timeout in milliseconds.",
"The function that is being called may also take an argument, which is an object containing a field called 'time' (the time in seconds at which the timer happened)",
"for example: ```setInterval(function (e) { print(e.time); }, 1000);```",
"This can also be removed using clearInterval" ],
"generate" : "jswrap_interface_setInterval",
"params" : [ [ "function", "JsVarName", "A Function or String to be executed"],
[ "timeout", "float", "The time between calls to the function" ] ],
"return" : ["JsVar", "An ID that can be passed to clearInterval"]
}*/
/*JSON{ "type":"function", "name" : "setTimeout",
"description" : ["Call the function specified ONCE after the timeout in milliseconds.",
"The function that is being called may also take an argument, which is an object containing a field called 'time' (the time in seconds at which the timer happened)",
"for example: ```setTimeout(function (e) { print(e.time); }, 1000);```",
"This can also be removed using clearTimeout" ],
"generate" : "jswrap_interface_setTimeout",
"params" : [ [ "function", "JsVarName", "A Function or String to be executed"],
[ "timeout", "float", "The time until the function will be executed" ] ],
"return" : ["JsVar", "An ID that can be passed to clearTimeout"]
}*/
JsVar *_jswrap_interface_setTimeoutOrInterval(JsVar *func, JsVarFloat interval, bool isTimeout) {
JsVar *skippedFunc = jsvSkipName(func);
JsVar *itemIndex = 0;
if (!jsvIsFunction(skippedFunc) && !jsvIsString(skippedFunc)) {
jsError("Function or String not supplied!");
} else {
// Create a new timer
JsVar *timerPtr = jsvNewWithFlags(JSV_OBJECT);
if (interval<TIMER_MIN_INTERVAL) interval=TIMER_MIN_INTERVAL;
JsVar *v;
v = jsvNewFromInteger(jshGetSystemTime() + jshGetTimeFromMilliseconds(interval));
jsvUnLock(jsvAddNamedChild(timerPtr, v, "time"));
jsvUnLock(v);
v = jsvNewFromFloat(interval);
jsvUnLock(jsvAddNamedChild(timerPtr, v, "interval"));
jsvUnLock(v);
v = jsvNewFromBool(!isTimeout);
jsvUnLock(jsvAddNamedChild(timerPtr, v, "recur"));
jsvUnLock(v);
jsvUnLock(jsvAddNamedChild(timerPtr, func, "callback"));
//jsPrint("TIMER BEFORE ADD\n"); jsvTrace(timerArray,5);
JsVar *timerArrayPtr = jsvLock(timerArray);
itemIndex = jsvNewFromInteger(jsvArrayPushWithInitialSize(timerArrayPtr, timerPtr, 1) - 1);
//jsPrint("TIMER AFTER ADD\n"); jsvTrace(timerArray,5);
jsvUnLock(timerArrayPtr);
jsvUnLock(timerPtr);
}
jsvUnLock(skippedFunc);
//jsvTrace(jsiGetParser()->root, 0);
return itemIndex;
}
JsVar *jswrap_interface_setInterval(JsVar *func, JsVarFloat timeout) {
return _jswrap_interface_setTimeoutOrInterval(func, timeout, false);
}
JsVar *jswrap_interface_setTimeout(JsVar *func, JsVarFloat timeout) {
return _jswrap_interface_setTimeoutOrInterval(func, timeout, true);
}
/*JSON{ "type":"function", "name" : "setWatch",
"description" : ["Call the function specified when the pin changes",
"The function may also take an argument, which is an object containing a field called 'time', which is the time in seconds at which the pin changed state, and 'state', which is the current state of the pin",
" This can also be removed using clearWatch" ],
"generate" : "jswrap_interface_setWatch",
"params" : [ [ "function", "JsVarName", "A Function or String to be executed"],
[ "pin", "pin", "The pin to watch" ],
[ "options", "JsVar", ["If this is a boolean or integer, it determines whether to call this once (false = default) or every time a change occurs (true)",
"If this is an object, it can contain the following information: ```{ repeat: true/false(default), edge:'rising'/'falling'/'both'(default)}```" ] ] ],
"return" : ["JsVar", "An ID that can be passed to clearWatch"]
}*/
JsVar *jswrap_interface_setWatch(JsVar *funcVar, Pin pin, JsVar *repeatOrObject) {
bool repeat = false;
int edge = 0;
if (jsvIsObject(repeatOrObject)) {
JsVar *v;
repeat = jsvGetBoolAndUnLock(jsvObjectGetChild(repeatOrObject, "repeat", 0));
v = jsvObjectGetChild(repeatOrObject, "edge", 0);
if (jsvIsString(v)) {
if (jsvIsStringEqual(v, "rising")) edge=1;
else if (jsvIsStringEqual(v, "falling")) edge=-1;
else if (jsvIsStringEqual(v, "both")) edge=0;
else jsWarn("'edge' in setWatch should be a string - either 'rising', 'falling' or 'both'");
} else if (!jsvIsUndefined(v))
jsWarn("'edge' in setWatch should be a string - either 'rising', 'falling' or 'both'");
jsvUnLock(v);
} else
repeat = jsvGetBool(repeatOrObject);
JsVarInt itemIndex = -1;
JsVar *skippedFunc = jsvSkipName(funcVar);
if (!jsvIsFunction(skippedFunc) && !jsvIsString(skippedFunc)) {
jsError("Function or String not supplied!");
} else {
// Create a new watch
JsVar *watchPtr = jsvNewWithFlags(JSV_OBJECT);
JsVar *v;
v = jsvNewFromPin(pin);
jsvUnLock(jsvAddNamedChild(watchPtr, v, "pin"));
jsvUnLock(v);
v = jsvNewFromBool(repeat);
jsvUnLock(jsvAddNamedChild(watchPtr, v, "recur"));
jsvUnLock(v);
v = jsvNewFromInteger(edge);
jsvUnLock(jsvAddNamedChild(watchPtr, v, "edge"));
jsvUnLock(v);
jsvUnLock(jsvAddNamedChild(watchPtr, funcVar, "callback"));
JsVar *watchArrayPtr = jsvLock(watchArray);
itemIndex = jsvArrayPushWithInitialSize(watchArrayPtr, watchPtr, 1) - 1;
jsvUnLock(watchArrayPtr);
jsvUnLock(watchPtr);
jshPinWatch(pin, true);
}
jsvUnLock(skippedFunc);
return (itemIndex>=0) ? jsvNewFromInteger(itemIndex) : 0/*undefined*/;
}
/*JSON{ "type":"function", "name" : "clearInterval",
"description" : ["Clear the Interval that was created with setInterval, for example:",
"```var id = setInterval(function () { print('foo'); }, 1000);```",
"```clearInterval(id);```",
"If no argument is supplied, all timers and intervals are stopped" ],
"generate" : "jswrap_interface_clearInterval",
"params" : [ [ "id", "JsVar", "The id returned by a previous call to setInterval"] ]
}*/
/*JSON{ "type":"function", "name" : "clearTimeout",
"description" : ["Clear the Timeout that was created with setTimeout, for example:",
"```var id = setTimeout(function () { print('foo'); }, 1000);```",
"```clearTimeout(id);```",
"If no argument is supplied, all timers and intervals are stopped" ],
"generate" : "jswrap_interface_clearTimeout",
"params" : [ [ "id", "JsVar", "The id returned by a previous call to setTimeout"] ]
}*/
void _jswrap_interface_clearTimeoutOrInterval(JsVar *idVar, bool isTimeout) {
if (jsvIsUndefined(idVar)) {
JsVar *timerArrayPtr = jsvLock(timerArray);
jsvRemoveAllChildren(timerArrayPtr);
jsvUnLock(timerArrayPtr);
} else {
JsVar *child = jsvIsBasic(idVar) ? jsvFindChildFromVarRef(timerArray, idVar, false) : 0;
if (child) {
JsVar *timerArrayPtr = jsvLock(timerArray);
jsvRemoveChild(timerArrayPtr, child);
jsvUnLock(child);
jsvUnLock(timerArrayPtr);
} else {
jsError(isTimeout ? "Unknown Timeout" : "Unknown Interval");
}
}
}
void jswrap_interface_clearInterval(JsVar *idVar) {
_jswrap_interface_clearTimeoutOrInterval(idVar, false);
}
void jswrap_interface_clearTimeout(JsVar *idVar) {
_jswrap_interface_clearTimeoutOrInterval(idVar, true);
}
/*JSON{ "type":"function", "name" : "changeInterval",
"description" : ["Change the Interval on a callback created with setInterval, for example:",
"```var id = setInterval(function () { print('foo'); }, 1000); // every second```",
"```changeInterval(id, 1500); // now runs every 1.5 seconds```",
"This takes effect the text time the callback is called (so it is not immediate)."],
"generate" : "jswrap_interface_changeInterval",
"params" : [ [ "id", "JsVar", "The id returned by a previous call to setInterval"],
[ "time","float","The new time period in ms" ] ]
}*/
void jswrap_interface_changeInterval(JsVar *idVar, JsVarFloat interval) {
if (interval<TIMER_MIN_INTERVAL) interval=TIMER_MIN_INTERVAL;
JsVar *timerName = jsvIsBasic(idVar) ? jsvFindChildFromVarRef(timerArray, idVar, false) : 0;
if (timerName) {
JsVar *timer = jsvSkipNameAndUnLock(timerName);
JsVar *v;
v = jsvNewFromFloat(interval);
jsvUnLock(jsvSetNamedChild(timer, v, "interval"));
jsvUnLock(v);
v = jsvNewFromInteger(jshGetSystemTime() + jshGetTimeFromMilliseconds(interval));
jsvUnLock(jsvSetNamedChild(timer, v, "time"));
jsvUnLock(v);
jsvUnLock(timer);
// timerName already unlocked
} else {
jsError("Unknown Interval");
}
}
/*JSON{ "type":"function", "name" : "clearWatch",
"description" : [ "Clear the Watch that was created with setWatch. If no parameter is supplied, all watches will be removed." ],
"generate" : "jswrap_interface_clearWatch",
"params" : [ [ "id", "JsVar", "The id returned by a previous call to setWatch"] ]
}*/
void jswrap_interface_clearWatch(JsVar *idVar) {
if (jsvIsUndefined(idVar)) {
JsVar *watchArrayPtr = jsvLock(watchArray);
// unwatch all pins
JsVarRef watch = watchArrayPtr->firstChild;
while (watch) {
JsVar *watchNamePtr = jsvLock(watch); // effectively the array index
JsVar *pinVar = jsvSkipNameAndUnLock(jsvFindChildFromStringRef(watchNamePtr->firstChild, "pin", false));
jshPinWatch(jshGetPinFromVar(pinVar), false); // 'unwatch' pin because we know that we're removing ALL watches
jsvUnLock(pinVar);
watch = watchNamePtr->nextSibling;
jsvUnLock(watchNamePtr);
}
// remove all items
jsvRemoveAllChildren(watchArrayPtr);
jsvUnLock(watchArrayPtr);
} else {
JsVar *watchNamePtr = jsvFindChildFromVarRef(watchArray, idVar, false);
if (watchNamePtr) { // child is a 'name'
JsVar *pinVar = jsvSkipNameAndUnLock(jsvFindChildFromStringRef(watchNamePtr->firstChild, "pin", false));
Pin pin = jshGetPinFromVar(pinVar);
jsvUnLock(pinVar);
JsVar *watchArrayPtr = jsvLock(watchArray);
jsvRemoveChild(watchArrayPtr, watchNamePtr);
jsvUnLock(watchNamePtr);
// Now check if this pin is still being watched
bool stillWatched = false;
JsArrayIterator it;
jsvArrayIteratorNew(&it, watchArrayPtr);
while (jsvArrayIteratorHasElement(&it)) {
JsVar *watchPtr = jsvArrayIteratorGetElement(&it);
JsVar *pinVar = jsvObjectGetChild(watchPtr, "pin", 0);
if (jshGetPinFromVar(pinVar) == pin)
stillWatched = true;
jsvUnLock(pinVar);
jsvUnLock(watchPtr);
jsvArrayIteratorNext(&it);
}
jsvArrayIteratorFree(&it);
jsvUnLock(watchArrayPtr);
if (!stillWatched)
jshPinWatch(pin, false); // 'unwatch' pin
} else {
jsError("Unknown Watch");
}
}
}