Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ruanhaishen
Nodemcu Firmware
Commits
dd38a0a0
Commit
dd38a0a0
authored
Jul 06, 2015
by
Johny Mattsson
Browse files
Imported reworked rtctime support.
parent
09410d55
Changes
9
Hide whitespace changes
Inline
Side-by-side
app/include/rom.h
View file @
dd38a0a0
// Headers to the various functions in the rom (as we discover them)
// Headers to the various functions in the rom (as we discover them)
#ifndef _ROM_H_
#define _ROM_H_
// SHA1 is assumed to match the netbsd sha1.h headers
// SHA1 is assumed to match the netbsd sha1.h headers
#define SHA1_DIGEST_LENGTH 20
#define SHA1_DIGEST_LENGTH 20
#define SHA1_DIGEST_STRING_LENGTH 41
#define SHA1_DIGEST_STRING_LENGTH 41
...
@@ -103,3 +106,4 @@ typedef void (*exception_handler_fn) (struct exception_frame *ef, uint32_t cause
...
@@ -103,3 +106,4 @@ typedef void (*exception_handler_fn) (struct exception_frame *ef, uint32_t cause
*/
*/
exception_handler_fn
_xtos_set_exception_handler
(
uint32_t
cause
,
exception_handler_fn
handler
);
exception_handler_fn
_xtos_set_exception_handler
(
uint32_t
cause
,
exception_handler_fn
handler
);
#endif
app/include/rtc/rtcaccess.h
View file @
dd38a0a0
...
@@ -9,14 +9,12 @@
...
@@ -9,14 +9,12 @@
#define RTC_TARGET_ADDR 0x04
#define RTC_TARGET_ADDR 0x04
#define RTC_COUNTER_ADDR 0x1c
#define RTC_COUNTER_ADDR 0x1c
#define EARLY_ENTRY_ATTR __attribute__((section(".text")))
static
inline
uint32_t
rtc_mem_read
(
uint32_t
addr
)
static
inline
uint32_t
EARLY_ENTRY_ATTR
rtc_mem_read
(
uint32_t
addr
)
{
{
return
((
uint32_t
*
)
RTC_USER_MEM_BASE
)[
addr
];
return
((
uint32_t
*
)
RTC_USER_MEM_BASE
)[
addr
];
}
}
static
inline
void
EARLY_ENTRY_ATTR
rtc_mem_write
(
uint32_t
addr
,
uint32_t
val
)
static
inline
void
rtc_mem_write
(
uint32_t
addr
,
uint32_t
val
)
{
{
((
uint32_t
*
)
RTC_USER_MEM_BASE
)[
addr
]
=
val
;
((
uint32_t
*
)
RTC_USER_MEM_BASE
)[
addr
]
=
val
;
}
}
...
...
app/include/rtc/rtcfifo.h
View file @
dd38a0a0
...
@@ -55,7 +55,7 @@
...
@@ -55,7 +55,7 @@
// (9/10) are meaningless when (3) is zero
// (9/10) are meaningless when (3) is zero
//
//
#define RTC_FIFO_BASE
8
#define RTC_FIFO_BASE
10
#define RTC_FIFO_MAGIC 0x44695553
#define RTC_FIFO_MAGIC 0x44695553
// RTCFIFO storage
// RTCFIFO storage
...
@@ -85,6 +85,10 @@
...
@@ -85,6 +85,10 @@
#define RTC_DEFAULT_TAGCOUNT 5
#define RTC_DEFAULT_TAGCOUNT 5
#define RTC_DEFAULT_FIFO_LOC (RTC_DEFAULT_FIFO_START + (RTC_DEFAULT_FIFO_END<<8) + (RTC_DEFAULT_TAGCOUNT<<16))
#define RTC_DEFAULT_FIFO_LOC (RTC_DEFAULT_FIFO_START + (RTC_DEFAULT_FIFO_END<<8) + (RTC_DEFAULT_TAGCOUNT<<16))
#ifndef RTCTIME_SLEEP_ALIGNED
# define RTCTIME_SLEEP_ALIGNED rtc_time_deep_sleep_until_aligned
#endif
typedef
struct
typedef
struct
{
{
uint32_t
timestamp
;
uint32_t
timestamp
;
...
@@ -340,7 +344,8 @@ static int32_t rtc_fifo_delta_t(uint32_t t, uint32_t ref_t)
...
@@ -340,7 +344,8 @@ static int32_t rtc_fifo_delta_t(uint32_t t, uint32_t ref_t)
static
uint32_t
rtc_fifo_construct_entry
(
uint32_t
val
,
uint32_t
tagindex
,
uint32_t
decimals
,
uint32_t
deltat
)
static
uint32_t
rtc_fifo_construct_entry
(
uint32_t
val
,
uint32_t
tagindex
,
uint32_t
decimals
,
uint32_t
deltat
)
{
{
return
val
+
(
deltat
<<
16
)
+
(
decimals
<<
25
)
+
(
tagindex
<<
28
);
return
(
val
&
0xffff
)
+
((
deltat
&
0x1ff
)
<<
16
)
+
((
decimals
&
0x7
)
<<
25
)
+
((
tagindex
&
0xf
)
<<
28
);
}
}
static
inline
void
rtc_fifo_store_sample
(
const
sample_t
*
s
)
static
inline
void
rtc_fifo_store_sample
(
const
sample_t
*
s
)
...
@@ -467,10 +472,10 @@ static inline void rtc_fifo_unset_magic(void)
...
@@ -467,10 +472,10 @@ static inline void rtc_fifo_unset_magic(void)
rtc_mem_write
(
RTC_FIFO_MAGIC_POS
,
0
);
rtc_mem_write
(
RTC_FIFO_MAGIC_POS
,
0
);
}
}
static
inline
void
rtc_fifo_deep_sleep_until_sample
(
uint32_t
min_sleep_us
,
uint32_t
mhz
)
static
inline
void
rtc_fifo_deep_sleep_until_sample
(
uint32_t
min_sleep_us
)
{
{
uint32_t
align
=
rtc_mem_read
(
RTC_ALIGNMENT_POS
);
uint32_t
align
=
rtc_mem_read
(
RTC_ALIGNMENT_POS
);
rtc_time_deep_sleep_until_aligned
(
align
,
min_sleep_us
,
mhz
);
RTCTIME_SLEEP_ALIGNED
(
align
,
min_sleep_us
);
}
}
static
inline
void
rtc_fifo_prepare
(
uint32_t
samples_per_boot
,
uint32_t
us_per_sample
,
uint32_t
tagcount
)
static
inline
void
rtc_fifo_prepare
(
uint32_t
samples_per_boot
,
uint32_t
us_per_sample
,
uint32_t
tagcount
)
...
...
app/include/rtc/rtctime.h
View file @
dd38a0a0
...
@@ -28,485 +28,32 @@
...
@@ -28,485 +28,32 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* @author Bernd Meyer <bmeyer@dius.com.au>
* @author Johny Mattsson <jmattsson@dius.com.au>
* @author Johny Mattsson <jmattsson@dius.com.au>
*/
*/
#ifndef _RTCTIME_H_
#define _RTCTIME_H_
#ifndef RTCTIME_H
/* We don't want to expose the raw rtctime interface as it is heavily
#define RTCTIME_H
* 'static inline' and used by a few things, so instead we wrap the
* relevant functions and expose these instead, through the rtctime.c module.
#include "rtcaccess.h"
*/
// Layout of the RTC storage space:
//
// 0: Magic. If set to RTC_TIME_MAGIC, the rest is valid. If not, continue to proper boot
//
// 1: cycle counter offset, lower 32 bit
// 2: cycle counter offset, upper 32 bit
//
// 3: cached result of sleep clock calibration. Has the format of system_rtc_clock_cali_proc(),
// or 0 if not available (see 4/5 below)
// 4: Number of microseconds we tried to sleep, or 0 if we didn't sleep since last calibration, ffffffff if invalid
// 5: Number of RTC cycles we decided to sleep, or 0 if we didn't sleep since last calibration, ffffffff if invalid
// 6: Number of microseconds which we add to (1/2) to avoid time going backwards
// 7: microsecond value returned in the last gettimeofday() to "user space".
//
// (1:2) set to 0 if no time information is available.
// Entries 4-7 are needed because the RTC cycles/second appears quite temperature dependent,
// and thus is heavily influenced by what else the chip is doing. As such, any calibration against
// the crystal-provided clock (which necessarily would have to happen while the chip is active and
// burning a few milliwatts) will be significantly different from the actual frequency during deep
// sleep.
// Thus, in order to calibrate for deep sleep conditions, we keep track of total sleep microseconds
// and total sleep clock cycles between settimeofday() calls (which presumably are NTP driven), and
// adjust the calibration accordingly on each settimeofday(). This will also track frequency changes
// due to ambient temperature changes.
// 6/7 get used when a settimeofday() would result in turning back time. As that can cause all sorts
// of ugly issues, we *do* adjust (1/2), but compensate by making the same adjustment to (6). Then each
// time gettimeofday() is called, we inspect (7) and determine how much time has passed since the last
// call (yes, this gets it wrong if more than a second has passed, but not in a way that causes issues)
// and try to take up to 6% of that time away from (6) until (6) reaches 0. Also, whenever we go to
// deep sleep, we add (6) to the sleep time, thus catching up all in one go.
// Note that for calculating the next sample-aligned wakeup, we need to use the post-adjustment
// timeofday(), but for calculating actual sleep time, we use the pre-adjustment one, thus bringing
// things back into line.
//
#define RTC_TIME_BASE 0 // Where the RTC timekeeping block starts in RTC user memory slots
#define RTC_TIME_MAGIC 0x44695573
#define RTC_TIME_MAGIC_SLEEP 0x64697573
// What rate we run the CPU at most of the time, and thus the rate at which we keep our time data
#define CPU_DEFAULT_MHZ 80
#define CPU_BOOTUP_MHZ 52
#define RTC_TIME_CCOMPARE_INT 6 // Interrupt cause for CCOMPARE0 match
// RTCTIME storage
#define RTC_TIME_MAGIC_POS (RTC_TIME_BASE+0)
#define RTC_CYCLEOFFSETL_POS (RTC_TIME_BASE+1)
#define RTC_CYCLEOFFSETH_POS (RTC_TIME_BASE+2)
#define RTC_SLEEPTOTALUS_POS (RTC_TIME_BASE+3)
#define RTC_SLEEPTOTALCYCLES_POS (RTC_TIME_BASE+4)
#define RTC_TODOFFSETUS_POS (RTC_TIME_BASE+5)
#define RTC_LASTTODUS_POS (RTC_TIME_BASE+6)
#define RTC_CALIBRATION_POS (RTC_TIME_BASE+7)
static
inline
uint64_t
rtc_time_get_now_us_adjusted
(
uint32_t
mhz
);
#include <c_types.h>
#ifndef _RTCTIME_INTERNAL_H_
struct
rtc_timeval
struct
rtc_timeval
{
{
uint32_t
tv_sec
;
uint32_t
tv_sec
;
uint32_t
tv_usec
;
uint32_t
tv_usec
;
};
};
static
inline
bool
EARLY_ENTRY_ATTR
rtc_time_check_sleep_magic
(
void
)
{
if
(
rtc_mem_read
(
RTC_TIME_MAGIC_POS
)
==
RTC_TIME_MAGIC_SLEEP
)
return
1
;
return
0
;
}
static
inline
bool
EARLY_ENTRY_ATTR
rtc_time_check_wake_magic
(
void
)
{
if
(
rtc_mem_read
(
RTC_TIME_MAGIC_POS
)
==
RTC_TIME_MAGIC
)
return
1
;
return
0
;
}
static
inline
bool
EARLY_ENTRY_ATTR
rtc_time_check_magic
(
void
)
{
return
rtc_time_check_wake_magic
()
||
rtc_time_check_sleep_magic
();
}
static
inline
void
EARLY_ENTRY_ATTR
rtc_time_set_wake_magic
(
void
)
{
rtc_mem_write
(
RTC_TIME_MAGIC_POS
,
RTC_TIME_MAGIC
);
}
static
inline
void
rtc_time_set_sleep_magic
(
void
)
{
rtc_mem_write
(
RTC_TIME_MAGIC_POS
,
RTC_TIME_MAGIC_SLEEP
);
}
static
inline
void
rtc_time_unset_magic
(
void
)
{
rtc_mem_write
(
RTC_TIME_MAGIC_POS
,
0
);
}
static
inline
uint32_t
rtc_time_read_raw
(
void
)
{
return
rtc_reg_read
(
RTC_COUNTER_ADDR
);
}
static
inline
uint32_t
rtc_time_read_raw_ccount
(
void
)
{
return
xthal_get_ccount
();
}
static
inline
uint64_t
rtc_time_unix_ccount
(
uint32_t
mhz
)
{
// Do *not* cache this, as it can change before the read(s) inside the loop
if
(
!
rtc_mem_read64
(
RTC_CYCLEOFFSETL_POS
))
return
0
;
uint64_t
result
=
0
;
// Need to be careful here of race conditions
while
(
!
result
)
{
uint32_t
before
=
rtc_time_read_raw_ccount
();
uint64_t
base
=
rtc_mem_read64
(
RTC_CYCLEOFFSETL_POS
);
uint32_t
after
=
rtc_time_read_raw_ccount
();
if
(
before
<
after
)
{
uint64_t
ccount80
;
if
(
mhz
==
CPU_DEFAULT_MHZ
)
ccount80
=
((
uint64_t
)
before
+
after
)
/
2
;
else
ccount80
=
((
uint64_t
)
before
+
after
)
*
CPU_DEFAULT_MHZ
/
(
2
*
mhz
);
result
=
base
+
ccount80
;
}
}
return
result
;
}
static
inline
uint64_t
rtc_time_unix_us
(
uint32_t
mhz
)
{
return
rtc_time_unix_ccount
(
mhz
)
/
CPU_DEFAULT_MHZ
;
}
static
inline
void
rtc_time_register_time_reached
(
uint32_t
s
,
uint32_t
us
)
{
rtc_mem_write
(
RTC_LASTTODUS_POS
,
us
);
}
static
inline
uint32_t
rtc_time_us_since_time_reached
(
uint32_t
s
,
uint32_t
us
)
{
uint32_t
lastus
=
rtc_mem_read
(
RTC_LASTTODUS_POS
);
if
(
us
<
lastus
)
us
+=
1000000
;
return
us
-
lastus
;
}
// A small sanity check so sleep times go completely nuts if someone
// has provided wrong timestamps to gettimeofday.
static
inline
bool
rtc_time_calibration_is_sane
(
uint32_t
cali
)
{
return
(
cali
>=
(
4
<<
12
))
&&
(
cali
<=
(
10
<<
12
));
}
static
inline
void
rtc_time_settimeofday
(
const
struct
rtc_timeval
*
tv
)
{
if
(
!
rtc_time_check_magic
())
return
;
uint32_t
sleep_us
=
rtc_mem_read
(
RTC_SLEEPTOTALUS_POS
);
uint32_t
sleep_cycles
=
rtc_mem_read
(
RTC_SLEEPTOTALCYCLES_POS
);
// At this point, the CPU clock will definitely be at the default rate (nodemcu fully booted)
uint64_t
now_esp_us
=
rtc_time_get_now_us_adjusted
(
CPU_DEFAULT_MHZ
);
uint64_t
now_ntp_us
=
((
uint64_t
)
tv
->
tv_sec
)
*
1000000
+
tv
->
tv_usec
;
int64_t
diff_us
=
now_esp_us
-
now_ntp_us
;
// Store the *actual* time.
uint64_t
target_ccount
=
now_ntp_us
*
CPU_DEFAULT_MHZ
;
// again, be mindful of race conditions
while
(
1
)
{
uint32_t
before
=
rtc_time_read_raw_ccount
();
rtc_mem_write64
(
RTC_CYCLEOFFSETL_POS
,
target_ccount
-
before
);
uint32_t
after
=
rtc_time_read_raw_ccount
();
if
(
before
<
after
)
break
;
}
// calibrate sleep period based on difference between expected time and actual time
if
(
sleep_us
>
0
&&
sleep_us
<
0xffffffff
&&
sleep_cycles
>
0
&&
sleep_cycles
<
0xffffffff
)
{
uint64_t
actual_sleep_us
=
sleep_us
-
diff_us
;
uint32_t
cali
=
(
actual_sleep_us
<<
12
)
/
sleep_cycles
;
if
(
rtc_time_calibration_is_sane
(
cali
))
rtc_mem_write
(
RTC_CALIBRATION_POS
,
cali
);
}
rtc_mem_write
(
RTC_SLEEPTOTALUS_POS
,
0
);
rtc_mem_write
(
RTC_SLEEPTOTALCYCLES_POS
,
0
);
// Deal with time adjustment if necessary
if
(
diff_us
>
0
)
// Time went backwards. Avoid that....
{
if
(
diff_us
>
0xffffffffULL
)
diff_us
=
0xffffffffULL
;
now_ntp_us
+=
diff_us
;
}
else
diff_us
=
0
;
rtc_mem_write
(
RTC_TODOFFSETUS_POS
,
diff_us
);
uint32_t
now_s
=
now_ntp_us
/
1000000
;
uint32_t
now_us
=
now_ntp_us
%
1000000
;
rtc_time_register_time_reached
(
now_s
,
now_us
);
}
static
inline
uint32_t
rtc_time_get_calibration
(
void
)
{
uint32_t
cal
=
rtc_time_check_magic
()
?
rtc_mem_read
(
RTC_CALIBRATION_POS
)
:
0
;
if
(
!
cal
)
{
// Make a first guess, most likely to be rather bad, but better then nothing.
#ifndef BOOTLOADER_CODE // This will pull in way too much of the system for the bootloader to handle.
ets_delay_us
(
200
);
cal
=
system_rtc_clock_cali_proc
();
rtc_mem_write
(
RTC_CALIBRATION_POS
,
cal
);
#else
cal
=
6
<<
12
;
#endif
#endif
}
return
cal
;
}
static
inline
void
rtc_time_invalidate_calibration
(
void
)
{
rtc_mem_write
(
RTC_CALIBRATION_POS
,
0
);
}
static
inline
uint64_t
rtc_time_us_to_ticks
(
uint64_t
us
)
void
TEXT_SECTION_ATTR
rtctime_early_startup
(
void
);
{
void
rtctime_late_startup
(
void
);
uint32_t
cal
=
rtc_time_get_calibration
();
void
rtctime_gettimeofday
(
struct
rtc_timeval
*
tv
);
void
rtctime_settimeofday
(
const
struct
rtc_timeval
*
tv
);
return
(
us
<<
12
)
/
cal
;
bool
rtctime_have_time
(
void
);
}
void
rtctime_deep_sleep_us
(
uint32_t
us
);
void
rtctime_deep_sleep_until_aligned_us
(
uint32_t
align_us
,
uint32_t
min_us
);
static
inline
uint64_t
rtc_time_get_now_us_raw
(
uint32_t
mhz
)
{
if
(
!
rtc_time_check_magic
())
return
0
;
return
rtc_time_unix_us
(
mhz
);
}
static
inline
uint64_t
rtc_time_get_now_us_adjusted
(
uint32_t
mhz
)
{
uint64_t
raw
=
rtc_time_get_now_us_raw
(
mhz
);
if
(
!
raw
)
return
0
;
return
raw
+
rtc_mem_read
(
RTC_TODOFFSETUS_POS
);
}
static
inline
void
rtc_time_gettimeofday
(
struct
rtc_timeval
*
tv
,
uint32_t
mhz
)
{
uint64_t
now
=
rtc_time_get_now_us_adjusted
(
mhz
);
uint32_t
sec
=
now
/
1000000
;
uint32_t
usec
=
now
%
1000000
;
uint32_t
to_adjust
=
rtc_mem_read
(
RTC_TODOFFSETUS_POS
);
if
(
to_adjust
)
{
uint32_t
us_passed
=
rtc_time_us_since_time_reached
(
sec
,
usec
);
uint32_t
adjust
=
us_passed
>>
4
;
if
(
adjust
)
{
if
(
adjust
>
to_adjust
)
adjust
=
to_adjust
;
to_adjust
-=
adjust
;
now
-=
adjust
;
now
/
1000000
;
now
%
1000000
;
rtc_mem_write
(
RTC_TODOFFSETUS_POS
,
to_adjust
);
}
}
tv
->
tv_sec
=
sec
;
tv
->
tv_usec
=
usec
;
rtc_time_register_time_reached
(
sec
,
usec
);
}
static
inline
void
rtc_time_add_sleep_tracking
(
uint32_t
us
,
uint32_t
cycles
)
{
if
(
rtc_time_check_magic
())
{
// us is the one that will grow faster...
uint32_t
us_before
=
rtc_mem_read
(
RTC_SLEEPTOTALUS_POS
);
uint32_t
us_after
=
us_before
+
us
;
uint32_t
cycles_after
=
rtc_mem_read
(
RTC_SLEEPTOTALCYCLES_POS
)
+
cycles
;
if
(
us_after
<
us_before
)
// Give up if it would cause an overflow
{
us_after
=
cycles_after
=
0xffffffff
;
}
rtc_mem_write
(
RTC_SLEEPTOTALUS_POS
,
us_after
);
rtc_mem_write
(
RTC_SLEEPTOTALCYCLES_POS
,
cycles_after
);
}
}
static
void
rtc_time_enter_deep_sleep_us
(
uint32_t
us
)
{
if
(
rtc_time_check_wake_magic
())
rtc_time_set_sleep_magic
();
rtc_reg_write
(
0
,
0
);
rtc_reg_write
(
0
,
rtc_reg_read
(
0
)
&
0xffffbfff
);
rtc_reg_write
(
0
,
rtc_reg_read
(
0
)
|
0x30
);
rtc_reg_write
(
0x44
,
4
);
rtc_reg_write
(
0x0c
,
0x00010010
);
rtc_reg_write
(
0x48
,(
rtc_reg_read
(
0x48
)
&
0xffff01ff
)
|
0x0000fc00
);
rtc_reg_write
(
0x48
,(
rtc_reg_read
(
0x48
)
&
0xfffffe00
)
|
0x00000080
);
rtc_reg_write
(
RTC_TARGET_ADDR
,
rtc_time_read_raw
()
+
136
);
rtc_reg_write
(
0x18
,
8
);
rtc_reg_write
(
0x08
,
0x00100010
);
ets_delay_us
(
20
);
rtc_reg_write
(
0x9c
,
17
);
rtc_reg_write
(
0xa0
,
3
);
rtc_reg_write
(
0x0c
,
0x640c8
);
rtc_reg_write
(
0
,
rtc_reg_read
(
0
)
&
0xffffffcf
);
uint32_t
cycles
=
rtc_time_us_to_ticks
(
us
);
rtc_time_add_sleep_tracking
(
us
,
cycles
);
rtc_reg_write
(
RTC_TARGET_ADDR
,
rtc_time_read_raw
()
+
cycles
);
rtc_reg_write
(
0x9c
,
17
);
rtc_reg_write
(
0xa0
,
3
);
// Clear bit 0 of DPORT 0x04. Doesn't seem to be necessary
// wm(0x3fff0004,bitrm(0x3fff0004),0xfffffffe));
rtc_reg_write
(
0x40
,
-
1
);
rtc_reg_write
(
0x44
,
32
);
rtc_reg_write
(
0x10
,
0
);
rtc_reg_write
(
0x18
,
8
);
rtc_reg_write
(
0x08
,
0x00100000
);
// go to sleep
}
static
inline
void
rtc_time_deep_sleep_us
(
uint32_t
us
,
uint32_t
mhz
)
{
if
(
rtc_time_check_magic
())
{
uint32_t
to_adjust
=
rtc_mem_read
(
RTC_TODOFFSETUS_POS
);
if
(
to_adjust
)
{
us
+=
to_adjust
;
rtc_mem_write
(
RTC_TODOFFSETUS_POS
,
0
);
}
uint64_t
now
=
rtc_time_get_now_us_raw
(
mhz
);
// Now the same as _adjusted()
if
(
now
)
{
// Need to maintain the clock first. When we wake up, counter will be 0
uint64_t
wakeup
=
now
+
us
;
uint64_t
wakeup_cycles
=
wakeup
*
CPU_DEFAULT_MHZ
;
rtc_mem_write64
(
RTC_CYCLEOFFSETL_POS
,
wakeup_cycles
);
}
}
rtc_time_enter_deep_sleep_us
(
us
);
}
static
inline
void
rtc_time_deep_sleep_until_aligned
(
uint32_t
align
,
uint32_t
min_sleep_us
,
uint32_t
mhz
)
{
uint64_t
now
=
rtc_time_get_now_us_adjusted
(
mhz
);
uint64_t
then
=
now
+
min_sleep_us
;
if
(
align
)
{
then
+=
align
-
1
;
then
-=
(
then
%
align
);
}
rtc_time_deep_sleep_us
(
then
-
now
,
mhz
);
}
static
inline
void
EARLY_ENTRY_ATTR
rtc_time_reset
(
bool
clear_cali
)
{
rtc_mem_write64
(
RTC_CYCLEOFFSETL_POS
,
0
);
rtc_mem_write
(
RTC_SLEEPTOTALUS_POS
,
0
);
rtc_mem_write
(
RTC_SLEEPTOTALCYCLES_POS
,
0
);
rtc_mem_write
(
RTC_TODOFFSETUS_POS
,
0
);
rtc_mem_write
(
RTC_LASTTODUS_POS
,
0
);
if
(
clear_cali
)
rtc_mem_write
(
RTC_CALIBRATION_POS
,
0
);
}
static
inline
void
EARLY_ENTRY_ATTR
rtc_time_register_bootup
(
void
)
{
uint32_t
reset_reason
=
rtc_get_reset_reason
();
#ifndef BOOTLOADER_CODE
static
const
bool
erase_calibration
=
true
;
#else
// In the boot loader, any leftover calibration is going to be better than anything we can
// come up with....
static
const
bool
erase_calibration
=
false
;
#endif
if
(
rtc_time_check_sleep_magic
())
{
if
(
reset_reason
!=
2
)
// This was *not* a proper wakeup from a deep sleep. All our time keeping is f*cked!
rtc_time_reset
(
erase_calibration
);
// Possibly keep the calibration, it should still be good
rtc_time_set_wake_magic
();
return
;
}
if
(
rtc_time_check_wake_magic
())
{
// This was *not* a proper wakeup from rtc-time initiated deep sleep. All our time keeping is f*cked!
rtc_time_reset
(
erase_calibration
);
// Possibly keep the calibration, it should still be good
}
}
static
inline
void
EARLY_ENTRY_ATTR
rtc_time_switch_to_default_clock
(
uint32_t
mhz
)
{
if
(
rtc_time_check_magic
())
{
uint64_t
cycles
=
rtc_time_read_raw_ccount
();
uint64_t
missing_cycles
=
cycles
*
(
CPU_DEFAULT_MHZ
-
mhz
)
/
mhz
;
uint64_t
offset
=
rtc_mem_read64
(
RTC_CYCLEOFFSETL_POS
);
if
(
offset
)
{
rtc_mem_write64
(
RTC_CYCLEOFFSETL_POS
,
offset
+
missing_cycles
);
}
}
}
static
inline
void
rtc_time_ccount_wrap_handler
(
void
*
dst_v
,
uint32_t
sp
)
{
uint32_t
off_h
=
rtc_mem_read
(
RTC_CYCLEOFFSETH_POS
);
if
(
rtc_time_check_magic
()
&&
off_h
)
{
rtc_mem_write
(
RTC_CYCLEOFFSETH_POS
,
off_h
+
1
);
}
xthal_set_ccompare
(
0
,
0
);
// This resets the interrupt condition
}
static
inline
void
EARLY_ENTRY_ATTR
rtc_time_install_wrap_handler
(
void
)
{
xthal_set_ccompare
(
0
,
0
);
// Recognise a ccounter wraparound
ets_isr_attach
(
RTC_TIME_CCOMPARE_INT
,
rtc_time_ccount_wrap_handler
,
NULL
);
ets_isr_unmask
(
1
<<
RTC_TIME_CCOMPARE_INT
);
}
// Call this from the nodemcu entry point, i.e. just before we switch from 52MHz to 80MHz
static
inline
void
EARLY_ENTRY_ATTR
rtc_time_switch_clocks
(
void
)
{
rtc_time_install_wrap_handler
();
rtc_time_switch_to_default_clock
(
CPU_BOOTUP_MHZ
);
}
static
inline
bool
rtc_time_have_time
(
void
)
{
return
(
rtc_time_check_magic
()
&&
rtc_mem_read64
(
RTC_CYCLEOFFSETL_POS
)
!=
0
);
}
static
inline
void
rtc_time_prepare
(
void
)
{
rtc_time_reset
(
true
);
rtc_time_set_wake_magic
();
}
#endif
#endif
app/include/rtc/rtctime_internal.h
0 → 100644
View file @
dd38a0a0
/*
* Copyright 2015 Dius Computing Pty Ltd. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
* - Neither the name of the copyright holders nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @author Bernd Meyer <bmeyer@dius.com.au>
* @author Johny Mattsson <jmattsson@dius.com.au>
*/
#ifndef _RTCTIME_INTERNAL_H_
#define _RTCTIME_INTERNAL_H_
/*
* The ESP8266 has four distinct power states:
*
* 1) Active --- CPU and modem are powered and running
* 2) Modem Sleep --- CPU is active, but the RF section is powered down
* 3) Light Sleep --- CPU is halted, RF section is powered down. CPU gets reactivated by interrupt
* 4) Deep Sleep --- CPU and RF section are powered down, restart requires a full reset
*
* There are also three (relevant) sources of time information
*
* A) CPU Cycle Counter --- this is incremented at the CPU frequency in modes (1) and (2), but is
* halted in state (3), and gets reset in state (4). Highly precise 32 bit counter
* which overflows roughly every minute. Starts counting as soon as the CPU becomes
* active after a reset. Can cause an interrupt when it hits a particular value;
* This interrupt (and the register that determines the comparison value) are not
* used by the system software, and are available for user code to use.
*
* B) Free Running Counter 2 --- This is a peripheral which gets configured to run at 1/256th of the
* CPU frequency. It is also active in states (1) and (2), and is halted in state (3).
* However, the ESP system code will adjust its value across periods of Light Sleep
* that it initiates, so *in effect*, this counter kind-of remains active in (3).
* While in states (1) and (2), it is as precise as the CPU Cycle. While in state (3),
* however, it is only as precise as the system's knowledge of how long the sleep
* period was. This knowledge is limited (it is based on (C), see below).
* The Free Running Counter 2 is a 32 bit counter which overflows roughly every
* 4 hours, and typically has a resolution of 3.2us. It starts counting as soon as
* it gets configured, which is considerably *after* the time of reset, and in fact
* is not done by the ESP boot loader, but rather by the loaded-from-SPI-flash system
* code. This means it is not yet running when the boot loader calls the configured
* entry point, and the time between reset and the counter starting to run depends on
* the size of code/data to be copied into RAM from the flash.
* The FRC2 is also used by the system software for its internal time keeping, i.e. for
* dealing with any registered ETS_Timers (and derived-from-them timer functionality).
*
* C) "Real Time Clock" --- This peripheral runs from an internal low power RC oscillator, at a frequency
* somewhere in the 120-200kHz range. It keeps running in all power states, and is in
* fact the time source responsible for generating an interrupt (state (3)) or reset
* (state (4)) to end Light and Deep Sleep periods. However, it *does* get reset to
* zero after a reset, even one it caused itself.
* The major issue with the RTC is that it is not using a crystal (support for an
* external 32.768kHz crystal was planned at one point, but was removed from the
* final ESP8266 design), and thus the frequency of the oscillator is dependent on
* a number of parameters, including the chip temperature. The ESP's system software
* contains code to "calibrate" exactly how long one cycle of the oscillator is, and
* uses that calibration to work out how many cycles to sleep for modes (3) and (4).
* However, once the chip has entered a low power state, it quickly cools down, which
* results in the oscillator running faster than during calibration, leading to early
* wakeups. This effect is small (even in relative terms) for short sleep periods (because
* the temperature does not change much over a few hundred milliseconds), but can get
* quite large for extended sleeps.
*
* For added fun, a typical ESP8266 module starts up running the CPU (and thus the cycle counter) at 52MHz,
* but usually this will be switched to 80MHz on application startup, and can later be switched to 160MHz
* under user control. Meanwhile, the FRC2 is usually kept running at 80MHz/256, regardless of the CPU
* clock.
*
*
*
* The code in this file implements a best-effort time keeping solution for the ESP. It keeps track of time
* by switching between various time sources. All state is kept in RAM associated with the RTC, which is
* maintained across Deep Sleep periods.
*
* Internally, time is managed in units of cycles of a (hypothetical) 2080MHz clock, e.g. in units
* of 0.4807692307ns. The reason for this choice is that this covers both the FRC2 and the cycle
* counter periods, while running at 52MHz, 80MHz or 160MHz.
*
* At any given time, the time status indicates whether the FRC2 or the Cycle Counter is the current time
* source, how many unit cycles each LSB of the chosen time source "is worth", and what the unix time in
* unit cycles was when the time source was at 0.
* Given that either time source overflows its 32 bit counter in a relatively short time, the code also
* maintains a "last read 32 bit value" for the selected time source, and on each subsequent read will
* check for overflow and, if necessary, adjust the unix-time-at-time-source-being-zero appropriately.
* In order to avoid missing overflows, a timer gets installed which requests time every 40 seconds.
*
* To avoid race conditions, *none* of the code here must be called from an interrupt context unless
* the user can absolutely guarantee that there will never be a clock source rollover (which can be the
* case for sensor applications that only stay awake for a few seconds). And even then, do so at your
* own risk.
*
*
* Deep sleep is handled by moving the time offset forward *before* the sleep to the scheduled wakeup
* time. Due to the nature of the RTC, the actual wakeup time may be a little bit different, but
* it's the best that can be done. The code attempts to come up with a better calibration value if
* authoritative time is available both before and after a sleep; This works reasonably well, but of
* course is still merely a guess, which may well be somewhat wrong.
*
*/
#include <osapi.h>
#include <ets_sys.h>
#include "rom.h"
#include "rtcaccess.h"
// Layout of the RTC storage space:
//
// 0: Magic, and time source. Meaningful values are
// * RTC_TIME_MAGIC_SLEEP: Indicates that the device went to sleep under RTCTIME control.
// This is the magic expected on deep sleep wakeup; Any other status means we lost track
// of time, and whatever time offset is stored in state is invalid and must be cleared.
// * RTC_TIME_MAGIC_CCOUNT: Time offset is relative to the Cycle Counter.
// * RTC_TIME_MAGIC_FRC2: Time offset is relative to the Free Running Counter.
// Any values other than these indicate that RTCTIME is not in use and no state is available, nor should
// RTCTIME make any changes to any of the RTC memory space.
//
// 1/2: UNIX time in Unit Cycles when time source had value 0 (64 bit, lower 32 bit in 1, upper in 2).
// If 0, then time is unknown.
// 3: Last used value of time source (32 bit unsigned). If current time source is less, then a rollover happened
// 4: Length of a time source cycle in Unit Cycles.
// 5: cached result of sleep clock calibration. Has the format of system_rtc_clock_cali_proc(),
// or 0 if not available (see 6/7 below)
// 6: Number of microseconds we tried to sleep, or 0 if we didn't sleep since last calibration, ffffffff if invalid
// 7: Number of RTC cycles we decided to sleep, or 0 if we didn't sleep since last calibration, ffffffff if invalid
// 8: Number of microseconds which we add to (1/2) to avoid time going backwards
// 9: microsecond value returned in the last gettimeofday() to "user space".
//
// Entries 6-9 are needed because the RTC cycles/second appears quite temperature dependent,
// and thus is heavily influenced by what else the chip is doing. As such, any calibration against
// the crystal-provided clock (which necessarily would have to happen while the chip is active and
// burning a few milliwatts) will be significantly different from the actual frequency during deep
// sleep.
// Thus, in order to calibrate for deep sleep conditions, we keep track of total sleep microseconds
// and total sleep clock cycles between settimeofday() calls (which presumably are NTP driven), and
// adjust the calibration accordingly on each settimeofday(). This will also track frequency changes
// due to ambient temperature changes.
// 8/9 get used when a settimeofday() would result in turning back time. As that can cause all sorts
// of ugly issues, we *do* adjust (1/2), but compensate by making the same adjustment to (8). Then each
// time gettimeofday() is called, we inspect (9) and determine how much time has passed since the last
// call (yes, this gets it wrong if more than a second has passed, but not in a way that causes issues)
// and try to take up to 6% of that time away from (8) until (8) reaches 0. Also, whenever we go to
// deep sleep, we add (8) to the sleep time, thus catching up all in one go.
// Note that for calculating the next sample-aligned wakeup, we need to use the post-adjustment
// timeofday(), but for calculating actual sleep time, we use the pre-adjustment one, thus bringing
// things back into line.
//
#define RTC_TIME_BASE 0 // Where the RTC timekeeping block starts in RTC user memory slots
#define RTC_TIME_MAGIC_CCOUNT 0x44695573
#define RTC_TIME_MAGIC_FRC2 (RTC_TIME_MAGIC_CCOUNT+1)
#define RTC_TIME_MAGIC_SLEEP (RTC_TIME_MAGIC_CCOUNT+2)
#define UNITCYCLE_MHZ 2080
#define CPU_OVERCLOCK_MHZ 160
#define CPU_DEFAULT_MHZ 80
#define CPU_BOOTUP_MHZ 52
// RTCTIME storage
#define RTC_TIME_MAGIC_POS (RTC_TIME_BASE+0)
#define RTC_CYCLEOFFSETL_POS (RTC_TIME_BASE+1)
#define RTC_CYCLEOFFSETH_POS (RTC_TIME_BASE+2)
#define RTC_LASTSOURCEVAL_POS (RTC_TIME_BASE+3)
#define RTC_SOURCECYCLEUNITS_POS (RTC_TIME_BASE+4)
#define RTC_CALIBRATION_POS (RTC_TIME_BASE+5)
#define RTC_SLEEPTOTALUS_POS (RTC_TIME_BASE+6)
#define RTC_SLEEPTOTALCYCLES_POS (RTC_TIME_BASE+7)
#define RTC_TODOFFSETUS_POS (RTC_TIME_BASE+8)
#define RTC_LASTTODUS_POS (RTC_TIME_BASE+9)
struct
rtc_timeval
{
uint32_t
tv_sec
;
uint32_t
tv_usec
;
};
static
inline
uint64_t
rtc_time_get_now_us_adjusted
();
static
inline
uint32_t
rtc_time_get_magic
(
void
)
{
return
rtc_mem_read
(
RTC_TIME_MAGIC_POS
);
}
static
inline
bool
rtc_time_check_sleep_magic
(
void
)
{
uint32_t
magic
=
rtc_time_get_magic
();
return
(
magic
==
RTC_TIME_MAGIC_SLEEP
);
}
static
inline
bool
rtc_time_check_wake_magic
(
void
)
{
uint32_t
magic
=
rtc_time_get_magic
();
return
(
magic
==
RTC_TIME_MAGIC_FRC2
||
magic
==
RTC_TIME_MAGIC_CCOUNT
);
}
static
inline
bool
rtc_time_check_magic
(
void
)
{
uint32_t
magic
=
rtc_time_get_magic
();
return
(
magic
==
RTC_TIME_MAGIC_FRC2
||
magic
==
RTC_TIME_MAGIC_CCOUNT
||
magic
==
RTC_TIME_MAGIC_SLEEP
);
}
static
inline
void
rtc_time_set_magic
(
uint32_t
new_magic
)
{
rtc_mem_write
(
RTC_TIME_MAGIC_POS
,
new_magic
);
}
static
inline
void
rtc_time_set_sleep_magic
(
void
)
{
rtc_time_set_magic
(
RTC_TIME_MAGIC_SLEEP
);
}
static
inline
void
rtc_time_set_ccount_magic
(
void
)
{
rtc_time_set_magic
(
RTC_TIME_MAGIC_CCOUNT
);
}
static
inline
void
rtc_time_set_frc2_magic
(
void
)
{
rtc_time_set_magic
(
RTC_TIME_MAGIC_FRC2
);
}
static
inline
void
rtc_time_unset_magic
(
void
)
{
rtc_mem_write
(
RTC_TIME_MAGIC_POS
,
0
);
}
static
inline
uint32_t
rtc_time_read_raw
(
void
)
{
return
rtc_reg_read
(
RTC_COUNTER_ADDR
);
}
static
inline
uint32_t
rtc_time_read_raw_ccount
(
void
)
{
return
xthal_get_ccount
();
}
static
inline
uint32_t
rtc_time_read_raw_frc2
(
void
)
{
return
NOW
();
}
// Get us the number of Unit Cycles that have elapsed since the source was 0.
// Note: This may in fact adjust the stored cycles-when-source-was-0 entry, so
// we need to make sure we call this before reading that entry
static
inline
uint64_t
rtc_time_source_offset
(
void
)
{
uint32_t
magic
=
rtc_time_get_magic
();
uint32_t
raw
=
0
;
switch
(
magic
)
{
case
RTC_TIME_MAGIC_CCOUNT
:
raw
=
rtc_time_read_raw_ccount
();
break
;
case
RTC_TIME_MAGIC_FRC2
:
raw
=
rtc_time_read_raw_frc2
();
break
;
default:
return
0
;
// We are not in a position to offer time
}
uint32_t
multiplier
=
rtc_mem_read
(
RTC_SOURCECYCLEUNITS_POS
);
uint32_t
previous
=
rtc_mem_read
(
RTC_LASTSOURCEVAL_POS
);
if
(
raw
<
previous
)
{
// We had a rollover.
uint64_t
to_add
=
(
1ULL
<<
32
)
*
multiplier
;
uint64_t
base
=
rtc_mem_read64
(
RTC_CYCLEOFFSETL_POS
);
if
(
base
)
rtc_mem_write64
(
RTC_CYCLEOFFSETL_POS
,
base
+
to_add
);
}
rtc_mem_write
(
RTC_LASTSOURCEVAL_POS
,
raw
);
return
((
uint64_t
)
raw
)
*
multiplier
;
}
static
inline
uint64_t
rtc_time_unix_unitcycles
(
void
)
{
// Note: The order of these two must be maintained, as the first call might change the outcome of the second
uint64_t
offset
=
rtc_time_source_offset
();
uint64_t
base
=
rtc_mem_read64
(
RTC_CYCLEOFFSETL_POS
);
if
(
!
base
)
return
0
;
// No known time
return
base
+
offset
;
}
static
inline
uint64_t
rtc_time_unix_us
(
void
)
{
return
rtc_time_unix_unitcycles
()
/
UNITCYCLE_MHZ
;
}
static
inline
void
rtc_time_register_time_reached
(
uint32_t
s
,
uint32_t
us
)
{
rtc_mem_write
(
RTC_LASTTODUS_POS
,
us
);
}
static
inline
uint32_t
rtc_time_us_since_time_reached
(
uint32_t
s
,
uint32_t
us
)
{
uint32_t
lastus
=
rtc_mem_read
(
RTC_LASTTODUS_POS
);
if
(
us
<
lastus
)
us
+=
1000000
;
return
us
-
lastus
;
}
// A small sanity check so sleep times go completely nuts if someone
// has provided wrong timestamps to gettimeofday.
static
inline
bool
rtc_time_calibration_is_sane
(
uint32_t
cali
)
{
return
(
cali
>=
(
4
<<
12
))
&&
(
cali
<=
(
10
<<
12
));
}
static
inline
uint32_t
rtc_time_get_calibration
(
void
)
{
uint32_t
cal
=
rtc_time_check_magic
()
?
rtc_mem_read
(
RTC_CALIBRATION_POS
)
:
0
;
if
(
!
cal
)
{
// Make a first guess, most likely to be rather bad, but better then nothing.
#ifndef BOOTLOADER_CODE // This will pull in way too much of the system for the bootloader to handle.
ets_delay_us
(
200
);
cal
=
system_rtc_clock_cali_proc
();
rtc_mem_write
(
RTC_CALIBRATION_POS
,
cal
);
#else
cal
=
6
<<
12
;
#endif
}
return
cal
;
}
static
inline
void
rtc_time_invalidate_calibration
(
void
)
{
rtc_mem_write
(
RTC_CALIBRATION_POS
,
0
);
}
static
inline
uint64_t
rtc_time_us_to_ticks
(
uint64_t
us
)
{
uint32_t
cal
=
rtc_time_get_calibration
();
return
(
us
<<
12
)
/
cal
;
}
static
inline
uint64_t
rtc_time_get_now_us_raw
(
void
)
{
if
(
!
rtc_time_check_magic
())
return
0
;
return
rtc_time_unix_us
();
}
static
inline
uint64_t
rtc_time_get_now_us_adjusted
(
void
)
{
uint64_t
raw
=
rtc_time_get_now_us_raw
();
if
(
!
raw
)
return
0
;
return
raw
+
rtc_mem_read
(
RTC_TODOFFSETUS_POS
);
}
static
inline
void
rtc_time_add_sleep_tracking
(
uint32_t
us
,
uint32_t
cycles
)
{
if
(
rtc_time_check_magic
())
{
// us is the one that will grow faster...
uint32_t
us_before
=
rtc_mem_read
(
RTC_SLEEPTOTALUS_POS
);
uint32_t
us_after
=
us_before
+
us
;
uint32_t
cycles_after
=
rtc_mem_read
(
RTC_SLEEPTOTALCYCLES_POS
)
+
cycles
;
if
(
us_after
<
us_before
)
// Give up if it would cause an overflow
{
us_after
=
cycles_after
=
0xffffffff
;
}
rtc_mem_write
(
RTC_SLEEPTOTALUS_POS
,
us_after
);
rtc_mem_write
(
RTC_SLEEPTOTALCYCLES_POS
,
cycles_after
);
}
}
static
void
rtc_time_enter_deep_sleep_us
(
uint32_t
us
)
{
if
(
rtc_time_check_wake_magic
())
rtc_time_set_sleep_magic
();
rtc_reg_write
(
0
,
0
);
rtc_reg_write
(
0
,
rtc_reg_read
(
0
)
&
0xffffbfff
);
rtc_reg_write
(
0
,
rtc_reg_read
(
0
)
|
0x30
);
rtc_reg_write
(
0x44
,
4
);
rtc_reg_write
(
0x0c
,
0x00010010
);
rtc_reg_write
(
0x48
,(
rtc_reg_read
(
0x48
)
&
0xffff01ff
)
|
0x0000fc00
);
rtc_reg_write
(
0x48
,(
rtc_reg_read
(
0x48
)
&
0xfffffe00
)
|
0x00000080
);
rtc_reg_write
(
RTC_TARGET_ADDR
,
rtc_time_read_raw
()
+
136
);
rtc_reg_write
(
0x18
,
8
);
rtc_reg_write
(
0x08
,
0x00100010
);
ets_delay_us
(
20
);
rtc_reg_write
(
0x9c
,
17
);
rtc_reg_write
(
0xa0
,
3
);
rtc_reg_write
(
0x0c
,
0x640c8
);
rtc_reg_write
(
0
,
rtc_reg_read
(
0
)
&
0xffffffcf
);
uint32_t
cycles
=
rtc_time_us_to_ticks
(
us
);
rtc_time_add_sleep_tracking
(
us
,
cycles
);
rtc_reg_write
(
RTC_TARGET_ADDR
,
rtc_time_read_raw
()
+
cycles
);
rtc_reg_write
(
0x9c
,
17
);
rtc_reg_write
(
0xa0
,
3
);
// Clear bit 0 of DPORT 0x04. Doesn't seem to be necessary
// wm(0x3fff0004,bitrm(0x3fff0004),0xfffffffe));
rtc_reg_write
(
0x40
,
-
1
);
rtc_reg_write
(
0x44
,
32
);
rtc_reg_write
(
0x10
,
0
);
rtc_reg_write
(
0x18
,
8
);
rtc_reg_write
(
0x08
,
0x00100000
);
// go to sleep
}
static
inline
void
rtc_time_deep_sleep_us
(
uint32_t
us
)
{
if
(
rtc_time_check_magic
())
{
uint32_t
to_adjust
=
rtc_mem_read
(
RTC_TODOFFSETUS_POS
);
if
(
to_adjust
)
{
us
+=
to_adjust
;
rtc_mem_write
(
RTC_TODOFFSETUS_POS
,
0
);
}
uint64_t
now
=
rtc_time_get_now_us_raw
();
// Now the same as _adjusted()
if
(
now
)
{
// Need to maintain the clock first. When we wake up, counter will be 0
uint64_t
wakeup
=
now
+
us
;
uint64_t
wakeup_cycles
=
wakeup
*
UNITCYCLE_MHZ
;
rtc_mem_write64
(
RTC_CYCLEOFFSETL_POS
,
wakeup_cycles
);
}
}
rtc_time_enter_deep_sleep_us
(
us
);
}
static
inline
void
rtc_time_deep_sleep_until_aligned
(
uint32_t
align
,
uint32_t
min_sleep_us
)
{
uint64_t
now
=
rtc_time_get_now_us_adjusted
();
uint64_t
then
=
now
+
min_sleep_us
;
if
(
align
)
{
then
+=
align
-
1
;
then
-=
(
then
%
align
);
}
rtc_time_deep_sleep_us
(
then
-
now
);
}
static
inline
void
rtc_time_reset
(
bool
clear_cali
)
{
rtc_mem_write64
(
RTC_CYCLEOFFSETL_POS
,
0
);
rtc_mem_write
(
RTC_SLEEPTOTALUS_POS
,
0
);
rtc_mem_write
(
RTC_SLEEPTOTALCYCLES_POS
,
0
);
rtc_mem_write
(
RTC_TODOFFSETUS_POS
,
0
);
rtc_mem_write
(
RTC_LASTTODUS_POS
,
0
);
rtc_mem_write
(
RTC_SOURCECYCLEUNITS_POS
,
0
);
rtc_mem_write
(
RTC_LASTSOURCEVAL_POS
,
0
);
if
(
clear_cali
)
rtc_mem_write
(
RTC_CALIBRATION_POS
,
0
);
}
static
inline
bool
rtc_time_have_time
(
void
)
{
return
(
rtc_time_check_magic
()
&&
rtc_mem_read64
(
RTC_CYCLEOFFSETL_POS
)
!=
0
);
}
static
inline
void
rtc_time_select_frc2_source
()
{
// FRC2 always runs at 1/256th of the default 80MHz clock, even if the actual clock is different
uint32_t
new_multiplier
=
(
256
*
UNITCYCLE_MHZ
+
CPU_DEFAULT_MHZ
/
2
)
/
CPU_DEFAULT_MHZ
;
uint64_t
now
;
uint32_t
before
;
uint32_t
after
;
// Deal with race condition here...
do
{
before
=
rtc_time_read_raw_frc2
();
now
=
rtc_time_unix_unitcycles
();
after
=
rtc_time_read_raw_frc2
();
}
while
(
before
>
after
);
if
(
rtc_time_have_time
())
{
uint64_t
offset
=
(
uint64_t
)
after
*
new_multiplier
;
rtc_mem_write64
(
RTC_CYCLEOFFSETL_POS
,
now
-
offset
);
rtc_mem_write
(
RTC_LASTSOURCEVAL_POS
,
after
);
}
rtc_mem_write
(
RTC_SOURCECYCLEUNITS_POS
,
new_multiplier
);
rtc_mem_write
(
RTC_TIME_MAGIC_POS
,
RTC_TIME_MAGIC_FRC2
);
}
static
inline
void
rtc_time_select_ccount_source
(
uint32_t
mhz
,
bool
first
)
{
uint32_t
new_multiplier
=
(
UNITCYCLE_MHZ
+
mhz
/
2
)
/
mhz
;
// Check that
if
(
new_multiplier
*
mhz
!=
UNITCYCLE_MHZ
)
ets_printf
(
"Trying to use unsuitable frequency: %dMHz
\n
"
,
mhz
);
if
(
first
)
{
// The ccounter has been running at this rate since startup, and the offset is set accordingly
rtc_mem_write
(
RTC_LASTSOURCEVAL_POS
,
0
);
rtc_mem_write
(
RTC_SOURCECYCLEUNITS_POS
,
new_multiplier
);
rtc_mem_write
(
RTC_TIME_MAGIC_POS
,
RTC_TIME_MAGIC_CCOUNT
);
return
;
}
uint64_t
now
;
uint32_t
before
;
uint32_t
after
;
// Deal with race condition here...
do
{
before
=
rtc_time_read_raw_ccount
();
now
=
rtc_time_unix_unitcycles
();
after
=
rtc_time_read_raw_ccount
();
}
while
(
before
>
after
);
if
(
rtc_time_have_time
())
{
uint64_t
offset
=
(
uint64_t
)
after
*
new_multiplier
;
rtc_mem_write64
(
RTC_CYCLEOFFSETL_POS
,
now
-
offset
);
rtc_mem_write
(
RTC_LASTSOURCEVAL_POS
,
after
);
}
rtc_mem_write
(
RTC_SOURCECYCLEUNITS_POS
,
new_multiplier
);
rtc_mem_write
(
RTC_TIME_MAGIC_POS
,
RTC_TIME_MAGIC_CCOUNT
);
}
static
inline
void
rtc_time_switch_to_ccount_frequency
(
uint32_t
mhz
)
{
if
(
rtc_time_check_magic
())
rtc_time_select_ccount_source
(
mhz
,
false
);
}
static
inline
void
rtc_time_switch_to_system_clock
(
void
)
{
if
(
rtc_time_check_magic
())
rtc_time_select_frc2_source
();
}
static
inline
void
rtc_time_tmrfn
(
void
*
arg
)
{
rtc_time_source_offset
();
}
static
inline
void
rtc_time_install_timer
(
void
)
{
static
ETSTimer
tmr
;
os_timer_setfn
(
&
tmr
,
rtc_time_tmrfn
,
NULL
);
os_timer_arm
(
&
tmr
,
10000
,
1
);
}
#if 0 // Kept around for reference....
static inline void rtc_time_ccount_wrap_handler(void* dst_v, uint32_t sp)
{
uint32_t off_h=rtc_mem_read(RTC_CYCLEOFFSETH_POS);
if (rtc_time_check_magic() && off_h)
{
rtc_mem_write(RTC_CYCLEOFFSETH_POS,off_h+1);
}
xthal_set_ccompare(0,0); // This resets the interrupt condition
}
static inline void rtc_time_install_wrap_handler(void)
{
xthal_set_ccompare(0,0); // Recognise a ccounter wraparound
ets_isr_attach(RTC_TIME_CCOMPARE_INT,rtc_time_ccount_wrap_handler,NULL);
ets_isr_unmask(1<<RTC_TIME_CCOMPARE_INT);
}
#endif
// This switches from MAGIC_SLEEP to MAGIC_CCOUNT, with ccount running at bootup frequency (i.e. 52MHz).
// To be called as early as possible, potententially as the first thing in an overridden entry point.
static
inline
void
rtc_time_register_bootup
(
void
)
{
uint32_t
reset_reason
=
rtc_get_reset_reason
();
#ifndef BOOTLOADER_CODE
static
const
bool
erase_calibration
=
true
;
#else
// In the boot loader, any leftover calibration is going to be better than anything we can
// come up with....
static
const
bool
erase_calibration
=
false
;
#endif
if
(
rtc_time_check_sleep_magic
())
{
if
(
reset_reason
!=
2
)
// This was *not* a proper wakeup from a deep sleep. All our time keeping is f*cked!
rtc_time_reset
(
erase_calibration
);
// Possibly keep the calibration, it should still be good
rtc_time_select_ccount_source
(
CPU_BOOTUP_MHZ
,
true
);
return
;
}
if
(
rtc_time_check_magic
())
{
// We did not go to sleep properly. All our time keeping is f*cked!
rtc_time_reset
(
erase_calibration
);
// Possibly keep the calibration, it should still be good
}
}
// Call this from the nodemcu entry point, i.e. just before we switch from 52MHz to 80MHz
static
inline
void
rtc_time_switch_clocks
(
void
)
{
rtc_time_switch_to_ccount_frequency
(
CPU_DEFAULT_MHZ
);
}
// Call this exactly once, from user_init, i.e. once the operating system is up and running
static
inline
void
rtc_time_switch_system
(
void
)
{
rtc_time_install_timer
();
rtc_time_switch_to_system_clock
();
}
static
inline
void
rtc_time_prepare
(
void
)
{
rtc_time_reset
(
true
);
rtc_time_select_frc2_source
();
}
static
inline
void
rtc_time_gettimeofday
(
struct
rtc_timeval
*
tv
)
{
uint64_t
now
=
rtc_time_get_now_us_adjusted
();
uint32_t
sec
=
now
/
1000000
;
uint32_t
usec
=
now
%
1000000
;
uint32_t
to_adjust
=
rtc_mem_read
(
RTC_TODOFFSETUS_POS
);
if
(
to_adjust
)
{
uint32_t
us_passed
=
rtc_time_us_since_time_reached
(
sec
,
usec
);
uint32_t
adjust
=
us_passed
>>
4
;
if
(
adjust
)
{
if
(
adjust
>
to_adjust
)
adjust
=
to_adjust
;
to_adjust
-=
adjust
;
now
-=
adjust
;
now
/
1000000
;
now
%
1000000
;
rtc_mem_write
(
RTC_TODOFFSETUS_POS
,
to_adjust
);
}
}
tv
->
tv_sec
=
sec
;
tv
->
tv_usec
=
usec
;
rtc_time_register_time_reached
(
sec
,
usec
);
}
static
inline
void
rtc_time_settimeofday
(
const
struct
rtc_timeval
*
tv
)
{
if
(
!
rtc_time_check_magic
())
return
;
uint32_t
sleep_us
=
rtc_mem_read
(
RTC_SLEEPTOTALUS_POS
);
uint32_t
sleep_cycles
=
rtc_mem_read
(
RTC_SLEEPTOTALCYCLES_POS
);
// At this point, the CPU clock will definitely be at the default rate (nodemcu fully booted)
uint64_t
now_esp_us
=
rtc_time_get_now_us_adjusted
();
uint64_t
now_ntp_us
=
((
uint64_t
)
tv
->
tv_sec
)
*
1000000
+
tv
->
tv_usec
;
int64_t
diff_us
=
now_esp_us
-
now_ntp_us
;
// Store the *actual* time.
uint64_t
target_unitcycles
=
now_ntp_us
*
UNITCYCLE_MHZ
;
uint64_t
sourcecycles
=
rtc_time_source_offset
();
rtc_mem_write64
(
RTC_CYCLEOFFSETL_POS
,
target_unitcycles
-
sourcecycles
);
// calibrate sleep period based on difference between expected time and actual time
if
(
sleep_us
>
0
&&
sleep_us
<
0xffffffff
&&
sleep_cycles
>
0
&&
sleep_cycles
<
0xffffffff
)
{
uint64_t
actual_sleep_us
=
sleep_us
-
diff_us
;
uint32_t
cali
=
(
actual_sleep_us
<<
12
)
/
sleep_cycles
;
if
(
rtc_time_calibration_is_sane
(
cali
))
rtc_mem_write
(
RTC_CALIBRATION_POS
,
cali
);
}
rtc_mem_write
(
RTC_SLEEPTOTALUS_POS
,
0
);
rtc_mem_write
(
RTC_SLEEPTOTALCYCLES_POS
,
0
);
// Deal with time adjustment if necessary
if
(
diff_us
>
0
)
// Time went backwards. Avoid that....
{
if
(
diff_us
>
0xffffffffULL
)
diff_us
=
0xffffffffULL
;
now_ntp_us
+=
diff_us
;
}
else
diff_us
=
0
;
rtc_mem_write
(
RTC_TODOFFSETUS_POS
,
diff_us
);
uint32_t
now_s
=
now_ntp_us
/
1000000
;
uint32_t
now_us
=
now_ntp_us
%
1000000
;
rtc_time_register_time_reached
(
now_s
,
now_us
);
}
#endif
app/modules/rtcfifo.c
View file @
dd38a0a0
...
@@ -2,6 +2,8 @@
...
@@ -2,6 +2,8 @@
#include "lauxlib.h"
#include "lauxlib.h"
#include "user_modules.h"
#include "user_modules.h"
#include "rtc/rtctime.h"
#define RTCTIME_SLEEP_ALIGNED rtctime_deep_sleep_until_aligned_us
#include "rtc/rtcfifo.h"
#include "rtc/rtcfifo.h"
// rtcfifo.prepare ([{sensor_count=n, interval_us=m, storage_begin=x, storage_end=y}])
// rtcfifo.prepare ([{sensor_count=n, interval_us=m, storage_begin=x, storage_end=y}])
...
@@ -67,12 +69,8 @@ static int rtcfifo_put (lua_State *L)
...
@@ -67,12 +69,8 @@ static int rtcfifo_put (lua_State *L)
sample_t
s
;
sample_t
s
;
s
.
timestamp
=
luaL_checknumber
(
L
,
1
);
s
.
timestamp
=
luaL_checknumber
(
L
,
1
);
double
val
=
luaL_checknumber
(
L
,
2
);
s
.
val
ue
=
luaL_checknumber
(
L
,
2
);
s
.
decimals
=
luaL_checknumber
(
L
,
3
);
s
.
decimals
=
luaL_checknumber
(
L
,
3
);
uint32_t
i
=
s
.
decimals
;
while
(
i
--
)
val
*=
10
;
s
.
value
=
val
;
size_t
len
;
size_t
len
;
const
char
*
str
=
luaL_checklstring
(
L
,
4
,
&
len
);
const
char
*
str
=
luaL_checklstring
(
L
,
4
,
&
len
);
union
{
union
{
...
@@ -90,11 +88,7 @@ static int rtcfifo_put (lua_State *L)
...
@@ -90,11 +88,7 @@ static int rtcfifo_put (lua_State *L)
static
int
extract_sample
(
lua_State
*
L
,
const
sample_t
*
s
)
static
int
extract_sample
(
lua_State
*
L
,
const
sample_t
*
s
)
{
{
lua_pushnumber
(
L
,
s
->
timestamp
);
lua_pushnumber
(
L
,
s
->
timestamp
);
double
val
=
s
->
value
;
lua_pushnumber
(
L
,
s
->
value
);
int
i
;
for
(
i
=
0
;
i
<
s
->
decimals
;
++
i
)
val
/=
10
;
lua_pushnumber
(
L
,
val
);
lua_pushnumber
(
L
,
s
->
decimals
);
lua_pushnumber
(
L
,
s
->
decimals
);
union
{
union
{
uint32_t
u
;
uint32_t
u
;
...
@@ -164,7 +158,7 @@ static int rtcfifo_dsleep_until_sample (lua_State *L)
...
@@ -164,7 +158,7 @@ static int rtcfifo_dsleep_until_sample (lua_State *L)
check_fifo_magic
(
L
);
check_fifo_magic
(
L
);
uint32_t
min_us
=
luaL_checknumber
(
L
,
1
);
uint32_t
min_us
=
luaL_checknumber
(
L
,
1
);
rtc_fifo_deep_sleep_until_sample
(
min_us
,
CPU_DEFAULT_MHZ
);
// no return
rtc_fifo_deep_sleep_until_sample
(
min_us
);
// no return
return
0
;
return
0
;
}
}
#endif
#endif
...
...
app/modules/rtctime.c
View file @
dd38a0a0
// Module for RTC time keeping
// Module for RTC time keeping
#include "lauxlib.h"
#include "lauxlib.h"
#include "rtc/rtctime_internal.h"
#include "rtc/rtctime.h"
#include "rtc/rtctime.h"
// rtctime.settimeofday (sec, usec)
static
int
rtctime_settimeofday
(
lua_State
*
L
)
// ******* C API functions *************
void
rtctime_early_startup
(
void
)
{
Cache_Read_Enable
(
0
,
0
,
1
);
rtc_time_register_bootup
();
rtc_time_switch_clocks
();
Cache_Read_Disable
();
}
void
rtctime_late_startup
(
void
)
{
rtc_time_switch_system
();
}
void
rtctime_gettimeofday
(
struct
rtc_timeval
*
tv
)
{
rtc_time_gettimeofday
(
tv
);
}
void
rtctime_settimeofday
(
const
struct
rtc_timeval
*
tv
)
{
if
(
!
rtc_time_check_magic
())
rtc_time_prepare
();
rtc_time_settimeofday
(
tv
);
}
bool
rtctime_have_time
(
void
)
{
return
rtc_time_have_time
();
}
void
rtctime_deep_sleep_us
(
uint32_t
us
)
{
rtc_time_deep_sleep_us
(
us
);
}
void
rtctime_deep_sleep_until_aligned_us
(
uint32_t
align_us
,
uint32_t
min_us
)
{
rtc_time_deep_sleep_until_aligned
(
align_us
,
min_us
);
}
// ******* Lua API functions *************
// rtctime.set (sec, usec)
static
int
rtctime_set
(
lua_State
*
L
)
{
{
if
(
!
rtc_time_check_magic
())
if
(
!
rtc_time_check_magic
())
rtc_time_prepare
();
rtc_time_prepare
();
...
@@ -15,16 +64,16 @@ static int rtctime_settimeofday (lua_State *L)
...
@@ -15,16 +64,16 @@ static int rtctime_settimeofday (lua_State *L)
usec
=
lua_tonumber
(
L
,
2
);
usec
=
lua_tonumber
(
L
,
2
);
struct
rtc_timeval
tv
=
{
sec
,
usec
};
struct
rtc_timeval
tv
=
{
sec
,
usec
};
rtc
_
time_settimeofday
(
&
tv
);
rtctime_settimeofday
(
&
tv
);
return
0
;
return
0
;
}
}
// sec, usec = rtctime.get
timeofday
()
// sec, usec = rtctime.get ()
static
int
rtctime_get
timeofday
(
lua_State
*
L
)
static
int
rtctime_get
(
lua_State
*
L
)
{
{
struct
rtc_timeval
tv
;
struct
rtc_timeval
tv
;
rtc
_
time_gettimeofday
(
&
tv
,
CPU_DEFAULT_MHZ
);
rtctime_gettimeofday
(
&
tv
);
lua_pushnumber
(
L
,
tv
.
tv_sec
);
lua_pushnumber
(
L
,
tv
.
tv_sec
);
lua_pushnumber
(
L
,
tv
.
tv_usec
);
lua_pushnumber
(
L
,
tv
.
tv_usec
);
return
2
;
return
2
;
...
@@ -46,8 +95,7 @@ static int rtctime_dsleep (lua_State *L)
...
@@ -46,8 +95,7 @@ static int rtctime_dsleep (lua_State *L)
{
{
uint32_t
us
=
luaL_checknumber
(
L
,
1
);
uint32_t
us
=
luaL_checknumber
(
L
,
1
);
do_sleep_opt
(
L
,
2
);
do_sleep_opt
(
L
,
2
);
// does not return
rtctime_deep_sleep_us
(
us
);
// does not return
rtc_time_deep_sleep_us
(
us
,
CPU_DEFAULT_MHZ
);
return
0
;
return
0
;
}
}
...
@@ -55,14 +103,13 @@ static int rtctime_dsleep (lua_State *L)
...
@@ -55,14 +103,13 @@ static int rtctime_dsleep (lua_State *L)
// rtctime.dsleep_aligned (aligned_usec, min_usec, option)
// rtctime.dsleep_aligned (aligned_usec, min_usec, option)
static
int
rtctime_dsleep_aligned
(
lua_State
*
L
)
static
int
rtctime_dsleep_aligned
(
lua_State
*
L
)
{
{
if
(
!
rtc
_
time_have_time
())
if
(
!
rtctime_have_time
())
return
luaL_error
(
L
,
"time not available, unable to align"
);
return
luaL_error
(
L
,
"time not available, unable to align"
);
uint32_t
align_us
=
luaL_checknumber
(
L
,
1
);
uint32_t
align_us
=
luaL_checknumber
(
L
,
1
);
uint32_t
min_us
=
luaL_checknumber
(
L
,
2
);
uint32_t
min_us
=
luaL_checknumber
(
L
,
2
);
do_sleep_opt
(
L
,
3
);
do_sleep_opt
(
L
,
3
);
// does not return
rtctime_deep_sleep_until_aligned_us
(
align_us
,
min_us
);
// does not return
rtc_time_deep_sleep_until_aligned
(
align_us
,
min_us
,
CPU_DEFAULT_MHZ
);
return
0
;
return
0
;
}
}
...
@@ -72,8 +119,8 @@ static int rtctime_dsleep_aligned (lua_State *L)
...
@@ -72,8 +119,8 @@ static int rtctime_dsleep_aligned (lua_State *L)
#include "lrodefs.h"
#include "lrodefs.h"
const
LUA_REG_TYPE
rtctime_map
[]
=
const
LUA_REG_TYPE
rtctime_map
[]
=
{
{
{
LSTRKEY
(
"set
timeofday
"
),
LFUNCVAL
(
rtctime_set
timeofday
)
},
{
LSTRKEY
(
"set"
),
LFUNCVAL
(
rtctime_set
)
},
{
LSTRKEY
(
"get
timeofday
"
),
LFUNCVAL
(
rtctime_get
timeofday
)
},
{
LSTRKEY
(
"get"
),
LFUNCVAL
(
rtctime_get
)
},
{
LSTRKEY
(
"dsleep"
),
LFUNCVAL
(
rtctime_dsleep
)
},
{
LSTRKEY
(
"dsleep"
),
LFUNCVAL
(
rtctime_dsleep
)
},
{
LSTRKEY
(
"dsleep_aligned"
),
LFUNCVAL
(
rtctime_dsleep_aligned
)
},
{
LSTRKEY
(
"dsleep_aligned"
),
LFUNCVAL
(
rtctime_dsleep_aligned
)
},
{
LNILKEY
,
LNILVAL
}
{
LNILKEY
,
LNILVAL
}
...
...
app/modules/sntp.c
View file @
dd38a0a0
...
@@ -131,7 +131,7 @@ static void sntp_dosend (lua_State *L)
...
@@ -131,7 +131,7 @@ static void sntp_dosend (lua_State *L)
req
.
mode
=
3
;
// client
req
.
mode
=
3
;
// client
#ifdef LUA_USE_MODULES_RTCTIME
#ifdef LUA_USE_MODULES_RTCTIME
struct
rtc_timeval
tv
;
struct
rtc_timeval
tv
;
rtc
_
time_gettimeofday
(
&
tv
,
CPU_DEFAULT_MHZ
);
rtctime_gettimeofday
(
&
tv
);
req
.
xmit
.
sec
=
htonl
(
tv
.
tv_sec
);
req
.
xmit
.
sec
=
htonl
(
tv
.
tv_sec
);
req
.
xmit
.
frac
=
htonl
(
tv
.
tv_usec
);
req
.
xmit
.
frac
=
htonl
(
tv
.
tv_usec
);
#else
#else
...
@@ -221,7 +221,7 @@ static void on_recv (void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_a
...
@@ -221,7 +221,7 @@ static void on_recv (void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_a
#ifdef LUA_USE_MODULES_RTCTIME
#ifdef LUA_USE_MODULES_RTCTIME
struct
rtc_timeval
tv
;
struct
rtc_timeval
tv
;
rtc
_
time_gettimeofday
(
&
tv
,
CPU_DEFAULT_MHZ
);
rtctime_gettimeofday
(
&
tv
);
ntp_timestamp_t
dest
;
ntp_timestamp_t
dest
;
dest
.
sec
=
tv
.
tv_sec
;
dest
.
sec
=
tv
.
tv_sec
;
dest
.
frac
=
(
MICROSECONDS
*
tv
.
tv_usec
)
/
UINT32_MAXI
;
dest
.
frac
=
(
MICROSECONDS
*
tv
.
tv_usec
)
/
UINT32_MAXI
;
...
@@ -248,8 +248,7 @@ static void on_recv (void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_a
...
@@ -248,8 +248,7 @@ static void on_recv (void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_a
tv
.
tv_sec
=
dest
.
sec
-
NTP_TO_UNIX_EPOCH
;
tv
.
tv_sec
=
dest
.
sec
-
NTP_TO_UNIX_EPOCH
;
tv
.
tv_usec
=
(
MICROSECONDS
*
dest
.
frac
)
/
UINT32_MAXI
;
tv
.
tv_usec
=
(
MICROSECONDS
*
dest
.
frac
)
/
UINT32_MAXI
;
rtc_time_set_wake_magic
();
rtctime_settimeofday
(
&
tv
);
rtc_time_settimeofday
(
&
tv
);
if
(
have_cb
)
if
(
have_cb
)
{
{
...
...
app/user/user_main.c
View file @
dd38a0a0
...
@@ -36,18 +36,15 @@ os_event_t *taskQueue;
...
@@ -36,18 +36,15 @@ os_event_t *taskQueue;
* by the time it is invoked the irom has not yet been mapped. This naturally
* by the time it is invoked the irom has not yet been mapped. This naturally
* also goes for anything the trampoline itself calls.
* also goes for anything the trampoline itself calls.
*/
*/
void
user_start_trampoline
(
void
)
TEXT_SECTION_ATTR
;
void
TEXT_SECTION_ATTR
user_start_trampoline
(
void
)
void
user_start_trampoline
(
void
)
{
{
__real__xtos_set_exception_handler
(
__real__xtos_set_exception_handler
(
EXCCAUSE_LOAD_STORE_ERROR
,
load_non_32_wide_handler
);
EXCCAUSE_LOAD_STORE_ERROR
,
load_non_32_wide_handler
);
#ifdef LUA_USE_MODULES_RTCTIME
#ifdef LUA_USE_MODULES_RTCTIME
rtc_time_register_bootup
();
// Note: Keep this as close to call_user_start() as possible, since it
// Note: Keep this as close to call_user_start() as possible, since it
// is where the cpu clock actually gets bumped to 80MHz.
// is where the cpu clock actually gets bumped to 80MHz.
rtc
_
time_
switch_clocks
();
rtctime_
early_startup
();
#endif
#endif
call_user_start
();
call_user_start
();
}
}
...
@@ -153,6 +150,9 @@ void nodemcu_init(void)
...
@@ -153,6 +150,9 @@ void nodemcu_init(void)
*******************************************************************************/
*******************************************************************************/
void
user_init
(
void
)
void
user_init
(
void
)
{
{
#ifdef LUA_USE_MODULES_RTCTIME
rtctime_late_startup
();
#endif
// NODE_DBG("SDK version:%s\n", system_get_sdk_version());
// NODE_DBG("SDK version:%s\n", system_get_sdk_version());
// system_print_meminfo();
// system_print_meminfo();
// os_printf("Heap size::%d.\n",system_get_free_heap_size());
// os_printf("Heap size::%d.\n",system_get_free_heap_size());
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment