From ad278757cc3ff77948a8f27f65cbdcade14afbb3 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Fri, 15 Nov 2024 14:47:43 +0000 Subject: [PATCH] nRF52: Add E.setComparator to enable interrupts from LPCOMP --- ChangeLog | 1 + src/jsdevices.h | 10 +++++ src/jshardware.h | 2 + src/jsinteractive.c | 14 +++++++ src/jswrap_espruino.c | 35 +++++++++++++++++ src/jswrap_espruino.h | 1 + targets/nrf5x/jshardware.c | 80 +++++++++++++++++++++++++++++++++++++- 7 files changed, 142 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 928452881..0e0dd90f4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -57,6 +57,7 @@ Bangle.js: .setUI now only clears back widget if it hasn't been hidden by widget_utils Fix UtilTimer timings when new task added infront of existing tasks (fix #2575) Graphics: Fix issue where drawLine for 2px horizontal lines only drew a 1px dot + nRF52: Add E.setComparator to enable interrupts from LPCOMP 2v24 : Bangle.js2: Add 'Bangle.touchRd()', 'Bangle.touchWr()' Bangle.js2: After Bangle.showTestScreen, put Bangle.js into a hard off state (not soft off) diff --git a/src/jsdevices.h b/src/jsdevices.h index 6eaa2144d..203d8fdac 100644 --- a/src/jsdevices.h +++ b/src/jsdevices.h @@ -37,6 +37,7 @@ typedef enum { EV_NONE, EV_EXTI0, ///< External Interrupt EV_EXTI_MAX = EV_EXTI0 + ESPR_EXTI_COUNT - 1, + EV_CUSTOM, ///< Custom event (See IOCustomEventFlags) EV_SERIAL_START, EV_LOOPBACKA = EV_SERIAL_START, EV_LOOPBACKB, @@ -129,6 +130,15 @@ typedef enum { #define DEVICE_SANITY_CHECK() if (EV_TYPE_MASK>63) jsError("DEVICE_SANITY_CHECK failed") +/** Event types for EV_CUSTOM */ +typedef enum { + EVC_NONE, +#ifdef NRF52_SERIES + EVC_LPCOMP, +#endif + EVC_TYPE_MASK = 255, + EVC_DATA_LPCOMP_UP = 256 +} PACKED_FLAGS IOCustomEventFlags; /// True is the device is a serial device (could be a USART, Bluetooth, USB, etc) #define DEVICE_IS_SERIAL(X) (((X)>=EV_SERIAL_START) && ((X)<=EV_SERIAL_MAX)) diff --git a/src/jshardware.h b/src/jshardware.h index 60d311706..d2cd1a078 100644 --- a/src/jshardware.h +++ b/src/jshardware.h @@ -374,6 +374,8 @@ void jshClearUSBIdleTimeout(); #if defined(NRF51_SERIES) || defined(NRF52_SERIES) /// Called when we have had an event that means we should execute JS extern void jshHadEvent(); +/// Enable/disable(if level==NAN) the LPCOMP comparator +bool jshSetComparator(Pin pin, JsVarFloat level); #else #define jshHadEvent() /* We should ensure we exit idle mode */ #endif diff --git a/src/jsinteractive.c b/src/jsinteractive.c index 6c9b8633a..4ada7bf3a 100644 --- a/src/jsinteractive.c +++ b/src/jsinteractive.c @@ -2152,6 +2152,20 @@ void jsiIdle() { jsiExecuteEventCallbackName(usartClass, JS_EVENT_PREFIX"parity", 0, 0); } jsvUnLock(usartClass); +#endif + } else if (eventType == EV_CUSTOM) { + // TODO: maybe we should handle this with jswrapper.c? +#if defined(NRF52_SERIES) && !defined(SAVE_ON_FLASH) + // see jshSetComparator / E.setComparator + int type = event.data.time & EVC_TYPE_MASK; + if (type == EVC_LPCOMP) { + JsVar *arg = jsvNewFromInteger((event.data.time & EVC_DATA_LPCOMP_UP) ? 1 : -1); + jsiExecuteEventCallbackOn("E",JS_EVENT_PREFIX"comparator",1,&arg); + jsvUnLock(arg); + } +#endif +#ifdef NRF52_SERIES + jsiConsolePrintf("EV_CUSTOM %d\n", event.data.time); #endif #ifdef BLUETOOTH } else if ((eventType == EV_BLUETOOTH_PENDING) || (eventType == EV_BLUETOOTH_PENDING_DATA)) { diff --git a/src/jswrap_espruino.c b/src/jswrap_espruino.c index 9c95726d0..4dc04b5d9 100644 --- a/src/jswrap_espruino.c +++ b/src/jswrap_espruino.c @@ -615,6 +615,7 @@ void jswrap_espruino_FFT(JsVar *arrReal, JsVar *arrImag, bool inverse) { } #endif //!ESP8266 + /*JSON{ "type" : "staticmethod", "ifndef" : "SAVE_ON_FLASH", @@ -683,6 +684,40 @@ void jswrap_espruino_kickWatchdog() { jshKickWatchDog(); } +/*JSON{ + "type" : "staticmethod", + "#if" : "defined(NRF52) && !defined(SAVE_ON_FLASH)", + "class" : "E", + "name" : "setComparator", + "generate" : "jswrap_espruino_setComparator", + "params" : [ + ["pin","pin","The `Pin` to enable the comparator on"], + ["level","float","The level to trigger on, or `undefined` to disable. (see below for [Jolt.js](https://www.espruino.com/Jolt.js))"] + ] +} +(Added 2v25) Enable the nRF52 chip's `LPCOMP` hardware. When enabled, it creates an `E.on("comparator", ...)` +event whenever the pin supplied rises or falls past the setpoint given. + +```JS +E.setComparator(D28, 8/16); // compare with VDD/2 +E.on("comparator", e => { + print(e); // 1 for up, or -1 for down +}); +``` + +**Note:** There is just one LPCOMP, so you can only enable the comparator on one pin. + +**On [Jolt.js](https://www.espruino.com/Jolt.js):** when using `E.setComparator` on the analog pins on the +Terminal block (`H0`/`H2`/`H4`/`H8`), the `level` you give needs to be in volts. Because the comparator only +works in 16 steps, you can only detect multiples of 1.37v (1.37/2.74/4.11/etc) + + */ +void jswrap_espruino_setComparator(Pin pin, JsVarFloat level) { +#if defined(NRF52_SERIES) && !defined(SAVE_ON_FLASH) + jshSetComparator(pin, level); +#endif +} + /// Return an array of errors based on the current flags JsVar *jswrap_espruino_getErrorFlagArray(JsErrorFlags flags) { JsVar *arr = jsvNewEmptyArray(); diff --git a/src/jswrap_espruino.h b/src/jswrap_espruino.h index 539f7292b..249025358 100644 --- a/src/jswrap_espruino.h +++ b/src/jswrap_espruino.h @@ -30,6 +30,7 @@ void jswrap_espruino_FFT(JsVar *arrReal, JsVar *arrImag, bool inverse); void jswrap_espruino_enableWatchdog(JsVarFloat time, JsVar *isAuto); void jswrap_espruino_kickWatchdog(); +void jswrap_espruino_setComparator(Pin pin, JsVarFloat level); /// Return an array of errors based on the current flags JsVar *jswrap_espruino_getErrorFlagArray(JsErrorFlags flags); JsVar *jswrap_espruino_getErrorFlags(); diff --git a/targets/nrf5x/jshardware.c b/targets/nrf5x/jshardware.c index 170c1dfb9..b6232ff37 100644 --- a/targets/nrf5x/jshardware.c +++ b/targets/nrf5x/jshardware.c @@ -100,6 +100,10 @@ void app_error_fault_handler(uint32_t id, uint32_t pc, uint32_t info) { #include "jswrap_microbit.h" #endif +#if defined(NRF52_SERIES) && !defined(SAVE_ON_FLASH) +#include "nrf_lpcomp.h" +#endif + #ifndef SAVE_ON_FLASH // Enable 7 bit UART (this must be done in software) #define ESPR_UART_7BIT 1 @@ -2911,4 +2915,78 @@ void jsvGetProcessorPowerUsage(JsVar *devices) { pwmOn = true; if (pwmOn) jsvObjectSetChildAndUnLock(devices, "PWM", jsvNewFromInteger(200)); -} \ No newline at end of file +} + + +#if defined(NRF52_SERIES) && !defined(SAVE_ON_FLASH) + + +void COMP_LPCOMP_IRQHandler() { + if (nrf_lpcomp_event_check(NRF_LPCOMP_EVENT_UP) && nrf_lpcomp_int_enable_check(LPCOMP_INTENSET_UP_Msk)) { + nrf_lpcomp_event_clear(NRF_LPCOMP_EVENT_UP); + jshPushIOEvent(EV_CUSTOM, EVC_LPCOMP | EVC_DATA_LPCOMP_UP); + jshHadEvent(); + } + 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); + jshPushIOEvent(EV_CUSTOM, EVC_LPCOMP); + jshHadEvent(); + } +} + +/// Enable/disable(if level==NAN) the LPCOMP comparator +bool jshSetComparator(Pin pin, JsVarFloat level) { + if (!isfinite(level)) { + NVIC_DisableIRQ(LPCOMP_IRQn); + nrf_lpcomp_disable(); + nrf_lpcomp_task_trigger(NRF_LPCOMP_TASK_STOP); + return true; + } + if (!jshIsPinValid(pin) || pinInfo[pin].analog==JSH_ANALOG_NONE) { + jsExceptionHere(JSET_ERROR, "Pin for LPCOMP must be an analog pin"); + return false; + } + +#ifdef JOLTJS + // Bit of a hack for Jolt.js where we have a 39k + 220k potential divider + // Multiply up so we return the actual voltage -> 3.3*(220+39)/39 = 21.915 + if ((pinInfo[pin].port & JSH_PORT_MASK)==JSH_PORTH) + level = level / 21.915; +#endif + int ilevel = (int)((level*16)+0.5); + if (ilevel<1) ilevel=1; + if (ilevel>15) ilevel=15; + // allow LPCOMP_REFSEL_REFSEL_ARef? + const nrf_lpcomp_ref_t refs[] = {0, + LPCOMP_REFSEL_REFSEL_Ref1_16Vdd, + LPCOMP_REFSEL_REFSEL_Ref1_8Vdd, + LPCOMP_REFSEL_REFSEL_Ref3_16Vdd, + LPCOMP_REFSEL_REFSEL_Ref2_8Vdd, + LPCOMP_REFSEL_REFSEL_Ref5_16Vdd, + LPCOMP_REFSEL_REFSEL_Ref3_8Vdd, + LPCOMP_REFSEL_REFSEL_Ref7_16Vdd, + LPCOMP_REFSEL_REFSEL_Ref4_8Vdd, + LPCOMP_REFSEL_REFSEL_Ref9_16Vdd, + LPCOMP_REFSEL_REFSEL_Ref5_8Vdd, + LPCOMP_REFSEL_REFSEL_Ref11_16Vdd, + LPCOMP_REFSEL_REFSEL_Ref6_8Vdd, + LPCOMP_REFSEL_REFSEL_Ref13_16Vdd, + LPCOMP_REFSEL_REFSEL_Ref7_8Vdd, + LPCOMP_REFSEL_REFSEL_Ref15_16Vdd + }; + nrf_lpcomp_config_t config; + config.reference = refs[ilevel]; + config.detection = NRF_LPCOMP_DETECT_CROSS; + config.hyst = NRF_LPCOMP_HYST_50mV; + nrf_lpcomp_configure(&config); + nrf_lpcomp_input_select(pinInfo[pin].analog & JSH_MASK_ANALOG_CH); + nrf_lpcomp_int_enable(LPCOMP_INTENSET_UP_Msk|LPCOMP_INTENSET_DOWN_Msk); + nrf_lpcomp_shorts_enable(NRF_LPCOMP_SHORT_READY_SAMPLE_MASK); + NVIC_SetPriority(LPCOMP_IRQn, 3); // low - don't mess with BLE + NVIC_ClearPendingIRQ(LPCOMP_IRQn); + NVIC_EnableIRQ(LPCOMP_IRQn); + nrf_lpcomp_enable(); + nrf_lpcomp_task_trigger(NRF_LPCOMP_TASK_START); + return true; +} +#endif \ No newline at end of file