/
jshardware.c
1277 lines (1101 loc) · 39.4 KB
/
jshardware.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* This file is part of Espruino, a JavaScript interpreter for Microcontrollers
*
* Copyright (C) 2015 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
*
* Contains ESP8266 board specific functions.
* ----------------------------------------------------------------------------
*/
#include <ets_sys.h>
#include <osapi.h>
#include <os_type.h>
#include <c_types.h>
#include <user_interface.h>
#include <espconn.h>
#include <gpio.h>
#include <mem.h>
#include <espmissingincludes.h>
#include <uart.h>
#include <i2c_master.h>
#include <pwm.h>
#include <spi.h> // Include the MetalPhreak/ESP8266_SPI_Library headers.
#define _GCC_WRAP_STDINT_H
typedef long long int64_t;
#include "jshardware.h"
#include "jsutils.h"
#include "jstimer.h"
#include "jsparse.h"
#include "jsinteractive.h"
#include "jspininfo.h"
#include "jswrap_esp8266.h"
#include <jswrap_esp8266_network.h>
// The maximum time that we can safely delay/block without risking a watch dog
// timer error or other undesirable WiFi interaction. The time is measured in
// microseconds.
#define MAX_SLEEP_TIME_US 3000
// Save-to-flash uses 12KB at 0x78000
// The jshFlash functions use memory-mapped reads to access the first 1MB
// of flash and refuse to go beyond that. Writing uses the SDK functions and is also
// limited to the first MB.
#define FLASH_MAX (1024*1024)
#define FLASH_MMAP 0x40200000
#define FLASH_PAGE_SHIFT 12 // 4KB
#define FLASH_PAGE (1<<FLASH_PAGE_SHIFT)
// Address in RTC RAM where we save the time
#define RTC_TIME_ADDR (256/4) // start of "user data" in RTC RAM
static bool g_spiInitialized = false;
static int g_lastSPIRead = -1;
// Hardware PWM is broken
#ifdef HARDWARE_PWM
struct PWMRecord {
bool enabled; //!< Has this PWM been enabled previously?
};
static uint32 g_pwmFreq;
static struct PWMRecord g_PWMRecords[JSH_PIN_COUNT];
#else
// Whether a pin is being used for soft PWM or not
BITFIELD_DECL(jshPinSoftPWM, JSH_PIN_COUNT);
#endif
static uint8 g_pinState[JSH_PIN_COUNT];
/**
* Convert a pin id to the corresponding Pin Event id.
*/
static IOEventFlags pinToEV_EXTI(
Pin pin // !< The pin to map to the event id.
) {
// Map pin 0 to EV_EXTI0
// Map pin 1 to EV_EXTI1
// ...
// Map pin x to ECEXTIx
return (IOEventFlags)(EV_EXTI0 + pin);
}
// forward declaration
static void systemTimeInit(void);
static void utilTimerInit(void);
static void intrHandlerCB(uint32 interruptMask, void *arg);
/**
* Initialize the ESP8266 hardware environment.
*
* TODO: we should move stuff from user_main.c here
*/
void jshInit() {
// A call to jshInitDevices is architected as something we have to do.
os_printf("> jshInit\n");
// Initialize the ESP8266 GPIO subsystem.
gpio_init();
systemTimeInit();
utilTimerInit();
jshInitDevices();
// sanity check for pin function enum to catch ordering changes
if (JSHPINSTATE_I2C != 12 || JSHPINSTATE_GPIO_IN_PULLDOWN != 5 || JSHPINSTATE_MASK != 15) {
jsError("JshPinState #defines have changed, please update pinStateToString()");
}
// Register a callback function to be called for a GPIO interrupt
gpio_intr_handler_register(intrHandlerCB, NULL);
ETS_GPIO_INTR_ENABLE();
#ifndef HARDWARE_PWM
BITFIELD_CLEAR(jshPinSoftPWM);
#endif
// Initialize something for each of the possible pins.
for (int i=0; i<JSH_PIN_COUNT; i++) {
#ifdef HARDWARE_PWM
// For each of the PWM records, flag the PWM as having been not initialized.
g_PWMRecords[i].enabled = false;
#endif
g_pinState[i] = 0;
}
os_printf("< jshInit\n");
} // End of jshInit
/**
* Handle a GPIO interrupt.
* We have arrived in this callback function because the state of a GPIO pin has changed
* and it is time to record that change.
*/
static void CALLED_FROM_INTERRUPT intrHandlerCB(
uint32 interruptMask, //!< A mask indicating which GPIOs have changed.
void *arg //!< Optional argument.
) {
// Given the interrupt mask, we as if bit "x" is on. If it is, then that is defined as meaning
// that the state of GPIO "x" has changed so we want to raised an event that indicates that this
// has happened...
// Once we have handled the interrupt flags, we need to acknowledge the interrupts so
// that the ESP8266 will once again cause future interrupts to be processed.
//os_printf_plus(">> intrHandlerCB\n");
gpio_intr_ack(interruptMask);
// We have a mask of interrupts that have happened. Go through each bit in the mask
// and, if it is on, then an interrupt has occurred on the corresponding pin.
int pin;
for (pin=0; pin<JSH_PIN_COUNT; pin++) {
if ((interruptMask & (1<<pin)) != 0) {
// Pin has changed so push the event that says pin has changed.
jshPushIOWatchEvent(pinToEV_EXTI(pin));
gpio_pin_intr_state_set(GPIO_ID_PIN(pin), GPIO_PIN_INTR_ANYEDGE);
}
}
//os_printf_plus("<< intrHandlerCB\n");
}
/**
* Reset the Espruino environment.
*/
void jshReset() {
jshResetDevices();
os_printf("> jshReset\n");
// Set all GPIO pins to be input with pull-up
jshPinSetState(0, JSHPINSTATE_GPIO_IN_PULLUP);
//jshPinSetState(2, JSHPINSTATE_GPIO_IN_PULLUP); // used for debug output
jshPinSetState(4, JSHPINSTATE_GPIO_IN_PULLUP);
jshPinSetState(5, JSHPINSTATE_GPIO_IN_PULLUP);
jshPinSetState(12, JSHPINSTATE_GPIO_IN_PULLUP);
jshPinSetState(13, JSHPINSTATE_GPIO_IN_PULLUP);
jshPinSetState(14, JSHPINSTATE_GPIO_IN_PULLUP);
jshPinSetState(15, JSHPINSTATE_GPIO_IN_PULLUP);
g_spiInitialized = false; // Flag the hardware SPI interface as un-initialized.
g_lastSPIRead = -1;
extern void user_uart_init(void); // in user_main.c
user_uart_init();
jswrap_ESP8266_wifi_reset(); // reset the wifi
os_printf("< jshReset\n");
}
/**
* Re-init the esp8266 stuff after a soft-reset
*/
void jshSoftInit() {
jswrap_ESP8266_wifi_soft_init();
}
/**
* Handle whatever needs to be done in the idle loop when there's nothing to do.
*
* Nothing is needed on the esp8266. The watchdog timer is taken care of by the SDK.
*/
void jshIdle() {
}
// esp8266 chips don't have a serial number but they do have a MAC address
int jshGetSerialNumber(unsigned char *data, int maxChars) {
assert(maxChars >= 6); // it's 32
wifi_get_macaddr(0, data);
return 6;
}
//===== Interrupts and sleeping
void jshInterruptOff() { ets_intr_lock(); }
void jshInterruptOn() { ets_intr_unlock(); }
/// Enter simple sleep mode (can be woken up by interrupts). Returns true on success
bool jshSleep(JsSysTime timeUntilWake) {
int time = (int) timeUntilWake;
//os_printf("jshSleep %lld\n", timeUntilWake);
// **** TODO: fix this, this is garbage, we need to tell the idle loop to suspend
//jshDelayMicroseconds(time);
return true;
} // End of jshSleep
/**
* Delay (blocking) for the supplied number of microseconds.
* Note that for the ESP8266 we must NOT CPU block for more than
* 10 milliseconds or else we may starve the WiFi subsystem.
*/
void jshDelayMicroseconds(int microsec) {
// Keep things simple and make the user responsible if they sleep for too long...
if (microsec > 0) {
//os_printf("Delay %d us\n", microsec);
os_delay_us(microsec);
}
} // End of jshDelayMicroseconds
//===== PIN mux =====
static uint32 g_PERIPHS[] = {
PERIPHS_IO_MUX_GPIO0_U, // 00
PERIPHS_IO_MUX_U0TXD_U, // 01
PERIPHS_IO_MUX_GPIO2_U, // 02
PERIPHS_IO_MUX_U0RXD_U, // 03
PERIPHS_IO_MUX_GPIO4_U, // 04
PERIPHS_IO_MUX_GPIO5_U, // 05
PERIPHS_IO_MUX_SD_CLK_U, // 06
PERIPHS_IO_MUX_SD_DATA0_U, // 07
PERIPHS_IO_MUX_SD_DATA1_U, // 08
PERIPHS_IO_MUX_SD_DATA2_U, // 09
PERIPHS_IO_MUX_SD_DATA3_U, // 10
PERIPHS_IO_MUX_SD_CMD_U, // 11
PERIPHS_IO_MUX_MTDI_U, // 12
PERIPHS_IO_MUX_MTCK_U, // 13
PERIPHS_IO_MUX_MTMS_U, // 14
PERIPHS_IO_MUX_MTDO_U // 15
};
/**
* Return the function value to select GPIO for a pin
*/
static uint32 g_pinGPIOFunc[] = {
FUNC_GPIO0, // 00
FUNC_GPIO1, // 01
FUNC_GPIO2, // 02
FUNC_GPIO3, // 03
FUNC_GPIO4, // 04
FUNC_GPIO5, // 05
3, // 06
3, // 07
3, // 08
FUNC_GPIO9, // 09
FUNC_GPIO10, // 10
3, // 11
FUNC_GPIO12, // 12
FUNC_GPIO13, // 13
FUNC_GPIO14, // 14
FUNC_GPIO15 // 15
};
/**
* Return the function value to select Alternate Function for a pin
*/
static uint8 pinAFFunc[] = {
4 /*CLK_OUT*/, FUNC_U0TXD, FUNC_U1TXD_BK, 0 /*U0RXD*/,
0 /*NOOP*/, 0 /*NOOP*/, 0, 0,
0, 0, 0, 0, // protected pins
2 /*SPI_Q*/, 2 /*SPI_D*/, 2 /*SPI_CLK*/, 2 /*SPI_CS*/,
};
/**
* Convert a pin state to a string representation.
* This is used during debugging to log a meaningful value instead of a
* numeric that would then just have to be decoded.
*/
static char *pinStateToString(JshPinState state) {
static char *states[] = {
"UNDEFINED", "GPIO_OUT", "GPIO_OUT_OPENDRAIN",
"GPIO_IN", "GPIO_IN_PULLUP", "GPIO_IN_PULLDOWN",
"ADC_IN", "AF_OUT", "AF_OUT_OPENDRAIN",
"USART_IN", "USART_OUT", "DAC_OUT", "I2C",
};
return states[state];
}
static void jshDebugPin(Pin pin) {
os_printf("PIN: %d out=%ld enable=%ld in=%ld\n",
pin, (GPIO_REG_READ(GPIO_OUT_ADDRESS)>>pin)&1, (GPIO_REG_READ(GPIO_ENABLE_ADDRESS)>>pin)&1,
(GPIO_REG_READ(GPIO_IN_ADDRESS)>>pin)&1);
uint32_t gpio_pin = GPIO_REG_READ(GPIO_PIN_ADDR(pin));
uint32_t mux = READ_PERI_REG(PERIPHS_IO_MUX + 4*pin);
os_printf(" dr=%s src=%s func=%ld pull-up=%ld oe=%ld\n",
gpio_pin & 4 ? "open-drain" : "totem-pole",
gpio_pin & 1 ? "sigma-delta" : "gpio",
(mux>>2)&1 | (mux&3), (mux>>7)&1, mux&1);
}
/**
* Set the state of the specific pin.
*
* The possible states are:
*
* JSHPINSTATE_UNDEFINED
* JSHPINSTATE_GPIO_OUT
* JSHPINSTATE_GPIO_OUT_OPENDRAIN
* JSHPINSTATE_GPIO_IN
* JSHPINSTATE_GPIO_IN_PULLUP
* JSHPINSTATE_GPIO_IN_PULLDOWN
* JSHPINSTATE_ADC_IN
* JSHPINSTATE_AF_OUT
* JSHPINSTATE_AF_OUT_OPENDRAIN
* JSHPINSTATE_USART_IN
* JSHPINSTATE_USART_OUT
* JSHPINSTATE_DAC_OUT
* JSHPINSTATE_I2C
*
* This function is exposed indirectly through the exposed global function called
* `pinMode()`. For example, `pinMode(pin, "input")` will set the given pin to input.
*/
void jshPinSetState(
Pin pin, //!< The pin to have its state changed.
JshPinState state //!< The new desired state of the pin.
) {
/* Make sure we kill software PWM if we set the pin state
* after we've started it */
if (BITFIELD_GET(jshPinSoftPWM, pin)) {
BITFIELD_SET(jshPinSoftPWM, pin, 0);
jstPinPWM(0,0,pin);
}
//os_printf("> ESP8266: jshPinSetState %d, %s, pup=%d, od=%d\n",
// pin, pinStateToString(state), JSHPINSTATE_IS_PULLUP(state), JSHPINSTATE_IS_OPENDRAIN(state));
assert(pin < JSH_PIN_COUNT);
if (pin >= 6 && pin <= 11) {
jsError("Cannot change pins used for flash chip");
return; // these pins are used for the flash chip
}
int periph = g_PERIPHS[pin];
// set the pin mux function
switch (state) {
case JSHPINSTATE_GPIO_OUT:
case JSHPINSTATE_GPIO_OUT_OPENDRAIN:
case JSHPINSTATE_GPIO_IN:
case JSHPINSTATE_GPIO_IN_PULLUP:
case JSHPINSTATE_I2C:
PIN_FUNC_SELECT(periph, g_pinGPIOFunc[pin]); // set the pin mux to GPIO
break;
case JSHPINSTATE_AF_OUT:
case JSHPINSTATE_AF_OUT_OPENDRAIN:
PIN_FUNC_SELECT(periph, pinAFFunc[pin]); // set the pin to the alternate function
break;
case JSHPINSTATE_USART_IN:
case JSHPINSTATE_USART_OUT:
if (pin == 1 || pin == 3) PIN_FUNC_SELECT(periph, 0);
else PIN_FUNC_SELECT(periph, 4); // works for many pins...
break;
default:
jsError("Pin state not supported");
return;
}
// enable/disable pull-up
if (JSHPINSTATE_IS_PULLUP(state)) {
PIN_PULLUP_EN(periph);
} else {
PIN_PULLUP_DIS(periph);
}
// enable/disable output and choose open-drain/totem-pole
if (!JSHPINSTATE_IS_OUTPUT(state)) {
GPIO_REG_WRITE(GPIO_ENABLE_W1TC_ADDRESS, 1<<pin); // disable output
GPIO_REG_WRITE(GPIO_PIN_ADDR(pin), GPIO_REG_READ(GPIO_PIN_ADDR(pin)) & ~4); // totem-pole
} else if (JSHPINSTATE_IS_OPENDRAIN(state)) {
GPIO_REG_WRITE(GPIO_ENABLE_W1TS_ADDRESS, 1<<pin); // enable output
GPIO_REG_WRITE(GPIO_PIN_ADDR(pin), GPIO_REG_READ(GPIO_PIN_ADDR(pin)) | 4); // open-drain
} else {
GPIO_REG_WRITE(GPIO_ENABLE_W1TS_ADDRESS, 1<<pin); // enable output
GPIO_REG_WRITE(GPIO_PIN_ADDR(pin), GPIO_REG_READ(GPIO_PIN_ADDR(pin)) & ~4); // totem-pole
}
//jshDebugPin(pin);
g_pinState[pin] = state; // remember what we set this to...
}
/**
* Return the current state of the selected pin.
* \return The current state of the selected pin.
*/
JshPinState jshPinGetState(Pin pin) {
//os_printf("> ESP8266: jshPinGetState %d\n", pin);
return g_pinState[pin];
}
//===== GPIO and PIN stuff =====
/**
* Set the value of the corresponding pin.
*/
void jshPinSetValue(
Pin pin, //!< The pin to have its value changed.
bool value //!< The new value of the pin.
) {
//os_printf("> ESP8266: jshPinSetValue pin=%d, value=%d\n", pin, value);
GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, (value&1)<<pin);
GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, (!value)<<pin);
//jshDebugPin(pin);
}
/**
* Get the value of the corresponding pin.
* \return The current value of the pin.
*/
bool CALLED_FROM_INTERRUPT jshPinGetValue( // can be called at interrupt time
Pin pin //!< The pin to have its value read.
) {
//os_printf("> ESP8266: jshPinGetValue pin=%d, value=%d\n", pin, GPIO_INPUT_GET(pin));
return GPIO_INPUT_GET(pin);
}
JsVarFloat jshPinAnalog(Pin pin) {
//os_printf("> ESP8266: jshPinAnalog: pin=%d\n", pin);
return (JsVarFloat)system_adc_read() / 1023.0;
}
int jshPinAnalogFast(Pin pin) {
//os_printf("> ESP8266: jshPinAnalogFast: pin=%d\n", pin);
return (int)system_adc_read() << 6; // left-align to 16 bits
}
/**
* Set the output PWM value.
*/
JshPinFunction jshPinAnalogOutput(Pin pin, JsVarFloat value, JsVarFloat freq, JshAnalogOutputFlags flags) { // if freq<=0, the default is used
if (value<0) value=0;
if (value>1) value=1;
if (!isfinite(freq)) freq=0;
#ifdef HARDWARE_PWM
os_printf("> jshPinAnalogOutput - jshPinAnalogOutput: pin=%d, value(x100)=%d, freq=%d\n", pin, (int)(value*100), (int)freq);
// Check that the value is between 0.0 and 1.0
if (value < 0 || value > 1.0) {
return 0;
}
// If PWM for the pin has not previously been enabled, enable it now.
if (g_PWMRecords[pin].enabled == false) {
g_PWMRecords[pin].enabled = true;
g_pwmFreq = (uint32)freq;
// Set the default frequency to 1KHz if no supplied frequency.
if (g_pwmFreq == 0) {
g_pwmFreq = 1000;
}
// Initialize the PWM subsystem
uint32 duty = 0;
uint32 pinInfoList[3] = {g_PERIPHS[pin], g_pinGPIOFunc[pin], pin};
pwm_init(g_pwmFreq * 1000000, &duty, 1, &pinInfoList);
// Start the PWM subsystem
pwm_start();
}
// If the period/frequency has changed, update the period.
if ((uint32)freq != 0 && (uint32)freq != g_pwmFreq) {
g_pwmFreq = (uint32)freq;
pwm_set_period(g_pwmFreq * 1000000);
}
uint32 duty = value * 1000000 / 0.045 / g_pwmFreq;
os_printf(" - Duty: %d (units of 45 nsecs)\n", duty);
pwm_set_duty(duty, 0);
#else
// Software PWM
if (jshIsPinValid(pin)/* && (flags&(JSAOF_ALLOW_SOFTWARE|JSAOF_FORCE_SOFTWARE))*/) {
/* we set the bit field here so that if the user changes the pin state
* later on, we can get rid of the IRQs */
if (!jshGetPinStateIsManual(pin)) {
BITFIELD_SET(jshPinSoftPWM, pin, 0);
jshPinSetState(pin, JSHPINSTATE_GPIO_OUT);
}
BITFIELD_SET(jshPinSoftPWM, pin, 1);
if (freq<=0) freq=50;
jstPinPWM(freq, value, pin);
return 0;
}
// Otherwise
//if (jshIsPinValid(pin))
// jsiConsolePrint("You need to use analogWrite(pin, val, {soft:true}) for Software PWM on this pin\n");
return 0;
#endif
}
/**
*
*/
void jshSetOutputValue(JshPinFunction func, int value) {
os_printf("ESP8266: jshSetOutputValue %d %d - unimplemented\n", func, value);
}
/**
*
*/
void jshEnableWatchDog(JsVarFloat timeout) {
os_printf("ESP8266: jshEnableWatchDog %0.3f\n", timeout);
}
/**
* Get the state of the pin associated with the event flag.
*/
bool CALLED_FROM_INTERRUPT jshGetWatchedPinState(IOEventFlags eventFlag) { // can be called at interrupt time
//os_printf("> jshGetWatchedPinState eventFlag=%d\n", eventFlag);
if (eventFlag > EV_EXTI_MAX || eventFlag < EV_EXTI0) {
os_printf(" - Error ... eventFlag out of range\n");
jsError("eventFlag out of range");
//os_printf("< jshGetWatchedPinState\n");
return false;
}
bool currentPinValue = jshPinGetValue((Pin)(eventFlag-EV_EXTI0));
//os_printf("< jshGetWatchedPinState = %d\n", currentPinValue);
return currentPinValue;
}
/**
* Set the value of the pin to be the value supplied and then wait for
* a given period and set the pin value again to be the opposite.
*/
void jshPinPulse(
Pin pin, //!< The pin to be pulsed.
bool pulsePolarity, //!< The value to be pulsed into the pin.
JsVarFloat pulseTime //!< The duration in milliseconds to hold the pin.
) {
#if 0
// Implementation using the utility timer. This doesn't work well on the esp8266, because the
// utility timer uses tasks and these are not pre-emptible. So the timer won't actually fire
// until the main espruino task becomes idle, which messes up timings. It also locks-up things
// when someone defines a pulse train that is longer than the timer queue, because then the
// main task busy-waits for the timer queue to drain a bit, which never happens.
if (!jshIsPinValid(pin)) {
jsExceptionHere(JSET_ERROR, "Invalid pin!");
return;
}
if (pulseTime <= 0) {
// just wait for everything to complete [??? what does this mean ???]
jstUtilTimerWaitEmpty();
return;
} else {
// find out if we already had a timer scheduled
UtilTimerTask task;
if (!jstGetLastPinTimerTask(pin, &task)) {
// no timer - just start the pulse now!
jshPinOutput(pin, pulsePolarity);
task.time = jshGetSystemTime();
}
// Now set the end of the pulse to happen on a timer
jstPinOutputAtTime(task.time + jshGetTimeFromMilliseconds(pulseTime), &pin, 1, !pulsePolarity);
}
#endif
#if 1
// Implementation using busy-waiting. Ugly and if the pulse train exceeds 10ms one risks WDT
// resets, but it actually works...
jshPinOutput(pin, pulsePolarity);
jshDelayMicroseconds(jshGetTimeFromMilliseconds(pulseTime)-6); // -6 adjustment is for overhead
jshPinSetValue(pin, !pulsePolarity);
#endif
}
/**
* Determine whether the pin can be watchable.
* \return Returns true if the pin is wathchable.
*/
bool jshCanWatch(
Pin pin //!< The pin that we are asking whether or not we can watch it.
) {
// As of right now, let us assume that all pins on an ESP8266 are watchable.
os_printf("> jshCanWatch: pin=%d\n", pin);
os_printf("< jshCanWatch = true\n");
return true;
}
/**
* Do what ever is necessary to watch a pin.
* \return The event flag for this pin.
*/
IOEventFlags jshPinWatch(
Pin pin, //!< The pin to be watched.
bool shouldWatch //!< True for watching and false for unwatching.
) {
//os_printf("> jshPinWatch: pin=%d, shouldWatch=%d\n", pin, shouldWatch);
if (jshIsPinValid(pin)) {
ETS_GPIO_INTR_DISABLE();
if (shouldWatch) {
// Start watching the given pin ... First we ask ourselves if the
// pin state has been set manually. If it has not been set manually,
// then set the pin state to input.
if (jshGetPinStateIsManual(pin) == false) {
jshPinSetState(pin, JSHPINSTATE_GPIO_IN);
}
gpio_pin_intr_state_set(GPIO_ID_PIN(pin), GPIO_PIN_INTR_ANYEDGE);
} else {
// Stop watching the given pin
gpio_pin_intr_state_set(GPIO_ID_PIN(pin), GPIO_PIN_INTR_DISABLE);
}
ETS_GPIO_INTR_ENABLE();
} else {
jsError("Invalid pin");
//os_printf("< jshPinWatch: Invalid pin\n");
return EV_NONE;
}
//os_printf("< jshPinWatch\n");
return pinToEV_EXTI(pin);
}
/**
*
*/
JshPinFunction jshGetCurrentPinFunction(Pin pin) {
//os_printf("jshGetCurrentPinFunction %d\n", pin);
return JSH_NOTHING;
}
/**
* Determine if a given event is associated with a given pin.
* \return True if the event is associated with the pin and false otherwise.
*/
bool jshIsEventForPin(
IOEvent *event, //!< The event that has been detected.
Pin pin //!< The identity of a pin.
) {
return IOEVENTFLAGS_GETTYPE(event->flags) == pinToEV_EXTI(pin);
}
//===== USART and Serial =====
void jshUSARTSetup(IOEventFlags device, JshUSARTInfo *inf) {
uint8 uart_no;
if (device == EV_SERIAL1) uart_no = UART0;
else if (device == EV_SERIAL2) uart_no = UART1;
else {
jsError("jshUSARTSetup Unknown device %d\n", device);
return;
}
extern void uart_config(uint8 uart_no);
extern UartDevice UartDev;
UartDev.baut_rate = inf->baudRate;
if (inf->bytesize == 5)
UartDev.data_bits = FIVE_BITS;
else if (inf->bytesize == 6)
UartDev.data_bits = SIX_BITS;
else if (inf->bytesize == 7)
UartDev.data_bits = SEVEN_BITS;
else if (inf->bytesize == 8)
UartDev.data_bits = EIGHT_BITS;
else assert(0);
if (inf->parity == 1) {
UartDev.exist_parity = STICK_PARITY_EN;
UartDev.parity = EVEN_BITS;
} else if (inf->parity == 2) {
UartDev.exist_parity = STICK_PARITY_EN;
UartDev.parity = ODD_BITS;
} else {
UartDev.exist_parity = STICK_PARITY_DIS;
UartDev.parity = NONE_BITS;
}
if (inf->stopbits == 1)
UartDev.stop_bits = ONE_STOP_BIT;
else if (inf->stopbits == 2)
UartDev.stop_bits = TWO_STOP_BIT;
else assert(0);
UartDev.flow_ctrl = NONE_CTRL;
uart_config(uart_no);
}
bool jshIsUSBSERIALConnected() {
return false; // "On non-USB boards this just returns false"
}
/**
* Kick a device into action (if required).
*
* For instance we may need
* to set up interrupts. In this ESP8266 implementation, we transmit all the
* data that can be found associated with the device.
*/
void jshUSARTKick(
IOEventFlags device //!< The device to be kicked.
) {
// transmit anything that is sitting in the uart output buffers
if (device == EV_SERIAL1 || device == EV_SERIAL2) {
// Get the next character to transmit. We will have reached the end when
// the value of the character to transmit is -1.
int c = jshGetCharToTransmit(device);
uint8_t uart = device == EV_SERIAL1 ? 0 : 1;
while (c >= 0) {
uart_tx_one_char(uart, c);
c = jshGetCharToTransmit(device);
}
}
}
//===== SPI =====
/**
* Initialize the hardware SPI device.
* On the ESP8266, hardware SPI is implemented via a set of pins defined
* as follows:
*
* | GPIO | NodeMCU | Name | Function |
* |--------|---------|-------|----------|
* | GPIO12 | D6 | HMISO | MISO |
* | GPIO13 | D7 | HMOSI | MOSI |
* | GPIO14 | D5 | HSCLK | CLK |
* | GPIO15 | D8 | HCS | CS |
*
*/
void jshSPISetup(
IOEventFlags device, //!< The identity of the SPI device being initialized.
JshSPIInfo *inf //!< Flags for the SPI device.
) {
if (device != EV_SPI1) {
jsExceptionHere(JSET_ERROR, "Only SPI1 supported");
return;
}
//os_printf("jshSPISetup - jshSPISetup: device=%d\n", device);
spi_init(HSPI, inf->baudRate); // Initialize the hardware SPI components.
g_spiInitialized = true;
g_lastSPIRead = -1;
if (inf != NULL) {
os_printf("baudRate=%d, baudRateSpec=%d, pinSCK=%d, pinMISO=%d, pinMOSI=%d, spiMode=%d, spiMSB=%d\n",
inf->baudRate, inf->baudRateSpec, inf->pinSCK, inf->pinMISO, inf->pinMOSI, inf->spiMode, inf->spiMSB);
}
}
/** Send data through the given SPI device (if data>=0), and return the result
* of the previous send (or -1). If data<0, no data is sent and the function
* waits for data to be returned */
int jshSPISend(
IOEventFlags device, //!< The identity of the SPI device through which data is being sent.
int data //!< The data to be sent or an indication that no data is to be sent.
) {
if (device != EV_SPI1) {
return -1;
}
//os_printf("> jshSPISend - device=%d, data=%x\n", device, data);
int retData = g_lastSPIRead;
if (data >=0) {
g_lastSPIRead = spi_transaction(HSPI, 8, (uint32)data);
} else {
g_lastSPIRead = -1;
}
//os_printf("< jshSPISend\n");
return retData;
}
/**
* Send 16 bit data through the given SPI device.
*/
void jshSPISend16(
IOEventFlags device, //!< Unknown
int data //!< Unknown
) {
//os_printf("> jshSPISend16 - device=%d, data=%x\n", device, data);
//jshSPISend(device, data >> 8);
//jshSPISend(device, data & 255);
if (device != EV_SPI1) {
return;
}
spi_transaction(HSPI, 16, (uint32)data);
//os_printf("< jshSPISend16\n");
}
/**
* Set whether to send 16 bits or 8 over SPI.
*/
void jshSPISet16(
IOEventFlags device, //!< Unknown
bool is16 //!< Unknown
) {
//os_printf("> jshSPISet16 - device=%d, is16=%d\n", device, is16);
//os_printf("< jshSPISet16\n");
}
/**
* Wait until SPI send is finished.
*/
void jshSPIWait(
IOEventFlags device //!< Unknown
) {
//os_printf("> jshSPIWait - device=%d\n", device);
while(spi_busy(HSPI)) ;
//os_printf("< jshSPIWait\n");
}
/** Set whether to use the receive interrupt or not */
void jshSPISetReceive(IOEventFlags device, bool isReceive) {
//os_printf("> jshSPISetReceive - device=%d, isReceive=%d\n", device, isReceive);
//os_printf("< jshSPISetReceive\n");
}
//===== I2C =====
/** Set-up I2C master for ESP8266, default pins are SCL:14, SDA:2. Only device I2C1 is supported
* and only master mode. */
void jshI2CSetup(IOEventFlags device, JshI2CInfo *info) {
//os_printf("> jshI2CSetup: SCL=%d SDA=%d bitrate=%d\n",
// info->pinSCL, info->pinSDA, info->bitrate);
if (device != EV_I2C1) {
jsError("Only I2C1 supported"); return; }
Pin scl = info->pinSCL !=PIN_UNDEFINED ? info->pinSCL : 14;
Pin sda = info->pinSDA !=PIN_UNDEFINED ? info->pinSDA : 2;
jshPinSetState(scl, JSHPINSTATE_I2C);
jshPinSetState(sda, JSHPINSTATE_I2C);
i2c_master_gpio_init(scl, sda, info->bitrate);
//os_printf("< jshI2CSetup\n");
}
void jshI2CWrite(IOEventFlags device, unsigned char address, int nBytes,
const unsigned char *data, bool sendStop) {
//os_printf("ESP8266: jshI2CWrite 0x%x %dbytes %s\n", address, nBytes, sendStop?"stop":"");
if (device != EV_I2C1) return; // we only support one i2c device
uint8 ack;
i2c_master_start(); // start the transaction
i2c_master_writeByte((address<<1)|0); // send address and r/w
ack = i2c_master_getAck(); // get ack bit from slave
//os_printf("I2C: ack=%d\n", ack);
if (!ack) goto error;
while (nBytes--) {
i2c_master_writeByte(*data++); // send data byte
ack = i2c_master_getAck(); // get ack bit from slave
if (!ack) goto error;
}
if (sendStop) i2c_master_stop();
return;
error:
i2c_master_stop();
jsError("No ACK");
}
void jshI2CRead(IOEventFlags device, unsigned char address, int nBytes,
unsigned char *data, bool sendStop) {
//os_printf("ESP8266: jshI2CRead 0x%x %dbytes %s\n", address, nBytes, sendStop?"stop":"");
if (device != EV_I2C1) return; // we only support one i2c device
uint8 ack;
i2c_master_start(); // start the transaction
i2c_master_writeByte((address<<1)|1); // send address and r/w
ack = i2c_master_getAck(); // get ack bit from slave
if (!ack) goto error;
while (nBytes--) {
*data++ = i2c_master_readByte(); // recv data byte
i2c_master_setAck(nBytes == 0); // send ack or no-ack for last byte
}
if (sendStop) i2c_master_stop();
return;
error:
i2c_master_stop();
jsError("No ACK");
}
//===== System time stuff =====
/* The esp8266 has two notions of system time implemented in the SDK by system_get_time()
* and system_get_rtc_time(). The former has 1us granularity and comes off the CPU cycle
* counter, the latter has approx 57us granularity (need to check) and comes off the RTC
* clock. Both are 32-bit counters and thus need some form of roll-over handling in software
* to produce a JsSysTime.
*
* It seems pretty clear from the API and the calibration concepts that the RTC runs off an
* internal RC oscillator or something similar and the SDK provides functions to calibrate
* it WRT the crystal oscillator, i.e., to get the current clock ratio. The only benefit of
* RTC timer is that it keeps running when in light sleep mode. (It also keeps running in
* deep sleep mode since it can be used to exit deep sleep but some brilliant engineer at
* espressif decided to reset the RTC timer when coming out of deep sleep so the time is
* actually lost!)
*
* It seems that the best course of action is to use the system timer for jshGetSystemTime()
* and related functions and to use the rtc timer only to preserve time during light sleep.
*/
/**
* Given a time in milliseconds as float, get us the value in microsecond
*/
JsSysTime jshGetTimeFromMilliseconds(JsVarFloat ms) {
// os_printf("jshGetTimeFromMilliseconds %d, %f\n", (JsSysTime)(ms * 1000.0), ms);
return (JsSysTime) (ms * 1000.0 + 0.5);
}
/**
* Given a time in microseconds, get us the value in milliseconds (float)
*/
JsVarFloat jshGetMillisecondsFromTime(JsSysTime time) {
// os_printf("jshGetMillisecondsFromTime %d, %f\n", time, (JsVarFloat)time / 1000.0);
return (JsVarFloat) time / 1000.0;
}
// Structure to hold a timestamp in us since the epoch, plus the system timer value at that time
// stamp. The crc field is used when saving this to RTC RAM
typedef struct {
JsSysTime timeStamp; // UTC time at time stamp
uint32_t hwTimeStamp; // time in hw register at time stamp
uint32_t cksum; // checksum to check validity when loading from RTC RAM
} espTimeStamp;
static espTimeStamp sysTimeStamp; // last time stamp off system_get_time()
static espTimeStamp rtcTimeStamp; // last time stamp off system_get_rtc_time()
// Given a time stamp and a new value for the HW clock calculate the new time and update accordingly
static void updateTime(espTimeStamp *stamp, uint32_t clock) {
uint32_t delta = clock - stamp->hwTimeStamp;
stamp->timeStamp += (JsSysTime)delta;
stamp->hwTimeStamp = clock;
}
// Save the current RTC timestamp to RTC RAM so we don't loose track of time during a reset
// or sleep
static void saveTime() {
// calculate checksum
rtcTimeStamp.cksum = 0xdeadbeef ^ rtcTimeStamp.hwTimeStamp ^
(uint32_t)(rtcTimeStamp.timeStamp & 0xffffffff) ^
(uint32_t)(rtcTimeStamp.timeStamp >> 32);
system_rtc_mem_write(RTC_TIME_ADDR, &rtcTimeStamp, sizeof(rtcTimeStamp));
// Debug
//os_printf("RTC write: %lu %lu 0x%08x\n", (uint32_t)(rtcTimeStamp.timeStamp/1000000),
// rtcTimeStamp.hwTimeStamp, (int)rtcTimeStamp.cksum);
}
/**
* Return the current time in microseconds.
*/
JsSysTime CALLED_FROM_INTERRUPT jshGetSystemTime() { // in us -- can be called at interrupt time
return sysTimeStamp.timeStamp + (JsSysTime)(system_get_time() - sysTimeStamp.hwTimeStamp);
}