Merge remote-tracking branch 'upstream/dev' into dev
This commit is contained in:
commit
93a762a714
|
@ -1,5 +1,8 @@
|
|||
// 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
|
||||
#define SHA1_DIGEST_LENGTH 20
|
||||
#define SHA1_DIGEST_STRING_LENGTH 41
|
||||
|
@ -38,6 +41,15 @@ extern unsigned char * base64_decode(const unsigned char *src, size_t len, size_
|
|||
|
||||
extern void mem_init(void * start_addr);
|
||||
|
||||
// Interrupt Service Routine functions
|
||||
typedef void (*ets_isr_fn) (void *arg, uint32_t sp);
|
||||
extern int ets_isr_attach (unsigned int interrupt, ets_isr_fn, void *arg);
|
||||
extern void ets_isr_mask (unsigned intr);
|
||||
extern void ets_isr_unmask (unsigned intr);
|
||||
|
||||
// Cycle-counter
|
||||
extern unsigned int xthal_get_ccount (void);
|
||||
extern int xthal_set_ccompare (unsigned int timer_number, unsigned int compare_value);
|
||||
|
||||
// 2, 3 = reset (module dependent?), 4 = wdt
|
||||
int rtc_get_reset_reason (void);
|
||||
|
@ -94,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);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
#ifndef RTC_ACCESS_H
|
||||
#define RTC_ACCESS_H
|
||||
|
||||
#include <c_types.h>
|
||||
|
||||
#define RTC_MMIO_BASE 0x60000700
|
||||
#define RTC_USER_MEM_BASE 0x60001200
|
||||
#define RTC_USER_MEM_NUM_DWORDS 128
|
||||
#define RTC_TARGET_ADDR 0x04
|
||||
#define RTC_COUNTER_ADDR 0x1c
|
||||
|
||||
static inline uint32_t rtc_mem_read(uint32_t addr)
|
||||
{
|
||||
return ((uint32_t*)RTC_USER_MEM_BASE)[addr];
|
||||
}
|
||||
|
||||
static inline void rtc_mem_write(uint32_t addr, uint32_t val)
|
||||
{
|
||||
((uint32_t*)RTC_USER_MEM_BASE)[addr]=val;
|
||||
}
|
||||
|
||||
static inline uint64_t rtc_make64(uint32_t high, uint32_t low)
|
||||
{
|
||||
return (((uint64_t)high)<<32)|low;
|
||||
}
|
||||
|
||||
static inline uint64_t rtc_mem_read64(uint32_t addr)
|
||||
{
|
||||
return rtc_make64(rtc_mem_read(addr+1),rtc_mem_read(addr));
|
||||
}
|
||||
|
||||
static inline void rtc_mem_write64(uint32_t addr, uint64_t val)
|
||||
{
|
||||
rtc_mem_write(addr+1,val>>32);
|
||||
rtc_mem_write(addr,val&0xffffffff);
|
||||
}
|
||||
|
||||
static inline void rtc_memw(void)
|
||||
{
|
||||
asm volatile ("memw");
|
||||
}
|
||||
|
||||
static inline void rtc_reg_write(uint32_t addr, uint32_t val)
|
||||
{
|
||||
rtc_memw();
|
||||
addr+=RTC_MMIO_BASE;
|
||||
*((volatile uint32_t*)addr)=val;
|
||||
rtc_memw();
|
||||
}
|
||||
|
||||
static inline uint32_t rtc_reg_read(uint32_t addr)
|
||||
{
|
||||
addr+=RTC_MMIO_BASE;
|
||||
rtc_memw();
|
||||
return *((volatile uint32_t*)addr);
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,490 @@
|
|||
/*
|
||||
* 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>
|
||||
*/
|
||||
#ifndef _RTCFIFO_H_
|
||||
#define _RTCFIFO_H_
|
||||
#include "rtcaccess.h"
|
||||
#include "rtctime.h"
|
||||
|
||||
// 1: measurement alignment, in microseconds
|
||||
// 2: timestamp for next sample (seconds). For sensors which sense during the sleep phase. Set to
|
||||
// 0 to indicate no sample waiting. Simply do not use for sensors which deliver values prior to
|
||||
// deep sleep.
|
||||
|
||||
// 3: Number of samples to take before doing a "real" boot. Decremented as samples are obtained
|
||||
// 4: Reload value for (10). Needs to be applied by the firmware in the real boot (rtc_restart_samples_to_take())
|
||||
//
|
||||
// 5: FIFO location. First FIFO address in bits 0:7, first non-FIFO address in bits 8:15.
|
||||
// Number of tag spaces in bits 16:23
|
||||
// 6: Number of samples in FIFO.
|
||||
// 7: FIFO tail (where next sample will be written. Increments by 1 for each sample)
|
||||
// 8: FIFO head (where next sample will be read. Increments by 1 for each sample)
|
||||
// 9: FIFO head timestamp. Used and maintained when pulling things off the FIFO. This is the timestamp of the
|
||||
// most recent sample pulled off; I.e. the head samples timestamp is this plus that sample's delta_t
|
||||
// 10: FIFO tail timestamp. Used and maintained when adding things to the FIFO. This is the timestamp of the
|
||||
// most recent sample to have been added. I.e. a new sample's delta-t is calculated relative to this
|
||||
// (9/10) are meaningless when (3) is zero
|
||||
//
|
||||
|
||||
#define RTC_FIFO_BASE 10
|
||||
#define RTC_FIFO_MAGIC 0x44695553
|
||||
|
||||
// RTCFIFO storage
|
||||
#define RTC_FIFO_MAGIC_POS (RTC_FIFO_BASE+0)
|
||||
#define RTC_ALIGNMENT_POS (RTC_FIFO_BASE+1)
|
||||
#define RTC_TIMESTAMP_POS (RTC_FIFO_BASE+2)
|
||||
|
||||
#define RTC_SAMPLESTOTAKE_POS (RTC_FIFO_BASE+3)
|
||||
#define RTC_SAMPLESPERBOOT_POS (RTC_FIFO_BASE+4)
|
||||
|
||||
#define RTC_FIFOLOC_POS (RTC_FIFO_BASE+5)
|
||||
#define RTC_FIFOCOUNT_POS (RTC_FIFO_BASE+6)
|
||||
#define RTC_FIFOTAIL_POS (RTC_FIFO_BASE+7)
|
||||
#define RTC_FIFOHEAD_POS (RTC_FIFO_BASE+8)
|
||||
#define RTC_FIFOTAIL_T_POS (RTC_FIFO_BASE+9)
|
||||
#define RTC_FIFOHEAD_T_POS (RTC_FIFO_BASE+10)
|
||||
|
||||
// 32-127: FIFO space. Consisting of a number of tag spaces (see 4), followed by data entries.
|
||||
// Data entries consist of:
|
||||
// Bits 28:31 -> tag index. 0-15
|
||||
// Bits 25:27 -> decimals
|
||||
// Bits 16:24 -> delta-t in seconds from previous entry
|
||||
// Bits 0:15 -> sample value
|
||||
|
||||
#define RTC_DEFAULT_FIFO_START 32
|
||||
#define RTC_DEFAULT_FIFO_END 128
|
||||
#define RTC_DEFAULT_TAGCOUNT 5
|
||||
#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
|
||||
{
|
||||
uint32_t timestamp;
|
||||
uint32_t value;
|
||||
uint32_t decimals;
|
||||
uint32_t tag;
|
||||
} sample_t;
|
||||
|
||||
static inline void rtc_fifo_clear_content(void);
|
||||
|
||||
static inline uint32_t rtc_fifo_get_tail(void)
|
||||
{
|
||||
return rtc_mem_read(RTC_FIFOTAIL_POS);
|
||||
}
|
||||
|
||||
static inline void rtc_fifo_put_tail(uint32_t val)
|
||||
{
|
||||
rtc_mem_write(RTC_FIFOTAIL_POS,val);
|
||||
}
|
||||
|
||||
static inline uint32_t rtc_fifo_get_head(void)
|
||||
{
|
||||
return rtc_mem_read(RTC_FIFOHEAD_POS);
|
||||
}
|
||||
|
||||
static inline void rtc_fifo_put_head(uint32_t val)
|
||||
{
|
||||
rtc_mem_write(RTC_FIFOHEAD_POS,val);
|
||||
}
|
||||
|
||||
|
||||
static inline uint32_t rtc_fifo_get_tail_t(void)
|
||||
{
|
||||
return rtc_mem_read(RTC_FIFOTAIL_T_POS);
|
||||
}
|
||||
|
||||
static inline void rtc_fifo_put_tail_t(uint32_t val)
|
||||
{
|
||||
rtc_mem_write(RTC_FIFOTAIL_T_POS,val);
|
||||
}
|
||||
|
||||
static inline uint32_t rtc_fifo_get_head_t(void)
|
||||
{
|
||||
return rtc_mem_read(RTC_FIFOHEAD_T_POS);
|
||||
}
|
||||
|
||||
static inline void rtc_fifo_put_head_t(uint32_t val)
|
||||
{
|
||||
rtc_mem_write(RTC_FIFOHEAD_T_POS,val);
|
||||
}
|
||||
|
||||
|
||||
static inline uint32_t rtc_fifo_get_count(void)
|
||||
{
|
||||
return rtc_mem_read(RTC_FIFOCOUNT_POS);
|
||||
}
|
||||
|
||||
static inline void rtc_fifo_put_count(uint32_t val)
|
||||
{
|
||||
rtc_mem_write(RTC_FIFOCOUNT_POS,val);
|
||||
}
|
||||
|
||||
static inline uint32_t rtc_fifo_get_tagcount(void)
|
||||
{
|
||||
return (rtc_mem_read(RTC_FIFOLOC_POS)>>16)&0xff;
|
||||
}
|
||||
|
||||
static inline uint32_t rtc_fifo_get_tagpos(void)
|
||||
{
|
||||
return (rtc_mem_read(RTC_FIFOLOC_POS)>>0)&0xff;
|
||||
}
|
||||
|
||||
static inline uint32_t rtc_fifo_get_last(void)
|
||||
{
|
||||
return (rtc_mem_read(RTC_FIFOLOC_POS)>>8)&0xff;
|
||||
}
|
||||
|
||||
static inline uint32_t rtc_fifo_get_first(void)
|
||||
{
|
||||
return rtc_fifo_get_tagpos()+rtc_fifo_get_tagcount();
|
||||
}
|
||||
|
||||
static inline void rtc_fifo_put_loc(uint32_t first, uint32_t last, uint32_t tagcount)
|
||||
{
|
||||
rtc_mem_write(RTC_FIFOLOC_POS,first+(last<<8)+(tagcount<<16));
|
||||
}
|
||||
|
||||
static inline uint32_t rtc_fifo_normalise_index(uint32_t index)
|
||||
{
|
||||
if (index>=rtc_fifo_get_last())
|
||||
index=rtc_fifo_get_first();
|
||||
return index;
|
||||
}
|
||||
|
||||
static inline void rtc_fifo_increment_count(void)
|
||||
{
|
||||
rtc_fifo_put_count(rtc_fifo_get_count()+1);
|
||||
}
|
||||
|
||||
static inline void rtc_fifo_decrement_count(void)
|
||||
{
|
||||
rtc_fifo_put_count(rtc_fifo_get_count()-1);
|
||||
}
|
||||
|
||||
static inline uint32_t rtc_get_samples_to_take(void)
|
||||
{
|
||||
return rtc_mem_read(RTC_SAMPLESTOTAKE_POS);
|
||||
}
|
||||
|
||||
static inline void rtc_put_samples_to_take(uint32_t val)
|
||||
{
|
||||
rtc_mem_write(RTC_SAMPLESTOTAKE_POS,val);
|
||||
}
|
||||
|
||||
static inline void rtc_decrement_samples_to_take(void)
|
||||
{
|
||||
uint32_t stt=rtc_get_samples_to_take();
|
||||
if (stt)
|
||||
rtc_put_samples_to_take(stt-1);
|
||||
}
|
||||
|
||||
static inline void rtc_restart_samples_to_take(void)
|
||||
{
|
||||
rtc_put_samples_to_take(rtc_mem_read(RTC_SAMPLESPERBOOT_POS));
|
||||
}
|
||||
|
||||
static inline uint32_t rtc_fifo_get_value(uint32_t entry)
|
||||
{
|
||||
return entry&0xffff;
|
||||
}
|
||||
|
||||
static inline uint32_t rtc_fifo_get_decimals(uint32_t entry)
|
||||
{
|
||||
return (entry>>25)&0x07;
|
||||
}
|
||||
|
||||
static inline uint32_t rtc_fifo_get_deltat(uint32_t entry)
|
||||
{
|
||||
return (entry>>16)&0x1ff;
|
||||
}
|
||||
|
||||
static inline uint32_t rtc_fifo_get_tagindex(uint32_t entry)
|
||||
{
|
||||
return (entry>>28)&0x0f;
|
||||
}
|
||||
|
||||
static inline uint32_t rtc_fifo_get_tag_from_entry(uint32_t entry)
|
||||
{
|
||||
uint32_t index=rtc_fifo_get_tagindex(entry);
|
||||
uint32_t tags_at=rtc_fifo_get_tagpos();
|
||||
return rtc_mem_read(tags_at+index);
|
||||
}
|
||||
|
||||
static inline void rtc_fifo_fill_sample(sample_t* dst, uint32_t entry, uint32_t timestamp)
|
||||
{
|
||||
dst->timestamp=timestamp;
|
||||
dst->value=rtc_fifo_get_value(entry);
|
||||
dst->decimals=rtc_fifo_get_decimals(entry);
|
||||
dst->tag=rtc_fifo_get_tag_from_entry(entry);
|
||||
}
|
||||
|
||||
|
||||
// returns 1 if sample popped, 0 if not
|
||||
static inline int8_t rtc_fifo_pop_sample(sample_t* dst)
|
||||
{
|
||||
uint32_t count=rtc_fifo_get_count();
|
||||
|
||||
if (count==0)
|
||||
return 0;
|
||||
uint32_t head=rtc_fifo_get_head();
|
||||
uint32_t timestamp=rtc_fifo_get_head_t();
|
||||
uint32_t entry=rtc_mem_read(head);
|
||||
timestamp+=rtc_fifo_get_deltat(entry);
|
||||
rtc_fifo_fill_sample(dst,entry,timestamp);
|
||||
|
||||
head=rtc_fifo_normalise_index(head+1);
|
||||
|
||||
rtc_fifo_put_head(head);
|
||||
rtc_fifo_put_head_t(timestamp);
|
||||
rtc_fifo_decrement_count();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// returns 1 if sample is available, 0 if not
|
||||
static inline int8_t rtc_fifo_peek_sample(sample_t* dst, uint32_t from_top)
|
||||
{
|
||||
if (rtc_fifo_get_count()<=from_top)
|
||||
return 0;
|
||||
uint32_t head=rtc_fifo_get_head();
|
||||
uint32_t entry=rtc_mem_read(head);
|
||||
uint32_t timestamp=rtc_fifo_get_head_t();
|
||||
timestamp+=rtc_fifo_get_deltat(entry);
|
||||
|
||||
while (from_top--)
|
||||
{
|
||||
head=rtc_fifo_normalise_index(head+1);
|
||||
entry=rtc_mem_read(head);
|
||||
timestamp+=rtc_fifo_get_deltat(entry);
|
||||
}
|
||||
|
||||
rtc_fifo_fill_sample(dst,entry,timestamp);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline void rtc_fifo_drop_samples(uint32_t from_top)
|
||||
{
|
||||
uint32_t count=rtc_fifo_get_count();
|
||||
|
||||
if (count<=from_top)
|
||||
from_top=count;
|
||||
uint32_t head=rtc_fifo_get_head();
|
||||
uint32_t head_t=rtc_fifo_get_head_t();
|
||||
|
||||
while (from_top--)
|
||||
{
|
||||
uint32_t entry=rtc_mem_read(head);
|
||||
head_t+=rtc_fifo_get_deltat(entry);
|
||||
head=rtc_fifo_normalise_index(head+1);
|
||||
rtc_fifo_decrement_count();
|
||||
}
|
||||
rtc_fifo_put_head(head);
|
||||
rtc_fifo_put_head_t(head_t);
|
||||
}
|
||||
|
||||
static inline int rtc_fifo_find_tag_index(uint32_t tag)
|
||||
{
|
||||
uint32_t tags_at=rtc_fifo_get_tagpos();
|
||||
uint32_t count=rtc_fifo_get_tagcount();
|
||||
uint32_t i;
|
||||
|
||||
for (i=0;i<count;i++)
|
||||
{
|
||||
uint32_t stag=rtc_mem_read(tags_at+i);
|
||||
|
||||
if (stag==tag)
|
||||
return i;
|
||||
if (stag==0)
|
||||
{
|
||||
rtc_mem_write(tags_at+i,tag);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int32_t rtc_fifo_delta_t(uint32_t t, uint32_t ref_t)
|
||||
{
|
||||
uint32_t delta=t-ref_t;
|
||||
if (delta>0x1ff)
|
||||
return -1;
|
||||
return delta;
|
||||
}
|
||||
|
||||
static uint32_t rtc_fifo_construct_entry(uint32_t val, uint32_t tagindex, uint32_t decimals, uint32_t deltat)
|
||||
{
|
||||
return (val & 0xffff) + ((deltat & 0x1ff) <<16) +
|
||||
((decimals & 0x7)<<25) + ((tagindex & 0xf)<<28);
|
||||
}
|
||||
|
||||
static inline void rtc_fifo_store_sample(const sample_t* s)
|
||||
{
|
||||
uint32_t head=rtc_fifo_get_head();
|
||||
uint32_t tail=rtc_fifo_get_tail();
|
||||
uint32_t count=rtc_fifo_get_count();
|
||||
int32_t tagindex=rtc_fifo_find_tag_index(s->tag);
|
||||
|
||||
if (count==0)
|
||||
{
|
||||
rtc_fifo_put_head_t(s->timestamp);
|
||||
rtc_fifo_put_tail_t(s->timestamp);
|
||||
}
|
||||
uint32_t tail_t=rtc_fifo_get_tail_t();
|
||||
int32_t deltat=rtc_fifo_delta_t(s->timestamp,tail_t);
|
||||
|
||||
if (tagindex<0 || deltat<0)
|
||||
{ // We got something that doesn't fit into the scheme. Might be a long delay, might
|
||||
// be some sort of dynamic change. In order to go on, we need to start over....
|
||||
// ets_printf("deltat is %d, tagindex is %d\n",deltat,tagindex);
|
||||
|
||||
rtc_fifo_clear_content();
|
||||
rtc_fifo_put_head_t(s->timestamp);
|
||||
rtc_fifo_put_tail_t(s->timestamp);
|
||||
head=rtc_fifo_get_head();
|
||||
tail=rtc_fifo_get_tail();
|
||||
count=rtc_fifo_get_count();
|
||||
tagindex=rtc_fifo_find_tag_index(s->tag); // This should work now
|
||||
if (tagindex<0)
|
||||
return; // Uh-oh! This should never happen
|
||||
}
|
||||
|
||||
if (head==tail && count>0)
|
||||
{ // Full! Need to remove a sample
|
||||
sample_t dummy;
|
||||
rtc_fifo_pop_sample(&dummy);
|
||||
}
|
||||
|
||||
rtc_mem_write(tail++,rtc_fifo_construct_entry(s->value,tagindex,s->decimals,deltat));
|
||||
rtc_fifo_put_tail(rtc_fifo_normalise_index(tail));
|
||||
rtc_fifo_put_tail_t(s->timestamp);
|
||||
rtc_fifo_increment_count();
|
||||
}
|
||||
|
||||
static uint32_t rtc_fifo_make_tag(const uint8_t* s)
|
||||
{
|
||||
uint32_t tag=0;
|
||||
int i;
|
||||
for (i=0;i<4;i++)
|
||||
{
|
||||
if (!s[i])
|
||||
break;
|
||||
tag+=((uint32_t)(s[i]&0xff))<<(i*8);
|
||||
}
|
||||
return tag;
|
||||
}
|
||||
|
||||
static void rtc_fifo_tag_to_string(uint32_t tag, uint8_t s[5])
|
||||
{
|
||||
int i;
|
||||
s[4]=0;
|
||||
for (i=0;i<4;i++)
|
||||
s[i]=(tag>>(8*i))&0xff;
|
||||
}
|
||||
|
||||
static inline uint32_t rtc_fifo_get_divisor(const sample_t* s)
|
||||
{
|
||||
uint8_t decimals=s->decimals;
|
||||
uint32_t div=1;
|
||||
while (decimals--)
|
||||
div*=10;
|
||||
return div;
|
||||
}
|
||||
|
||||
static inline void rtc_fifo_clear_tags(void)
|
||||
{
|
||||
uint32_t tags_at=rtc_fifo_get_tagpos();
|
||||
uint32_t count=rtc_fifo_get_tagcount();
|
||||
while (count--)
|
||||
rtc_mem_write(tags_at++,0);
|
||||
}
|
||||
|
||||
static inline void rtc_fifo_clear_content(void)
|
||||
{
|
||||
uint32_t first=rtc_fifo_get_first();
|
||||
rtc_fifo_put_tail(first);
|
||||
rtc_fifo_put_head(first);
|
||||
rtc_fifo_put_count(0);
|
||||
rtc_fifo_put_tail_t(0);
|
||||
rtc_fifo_put_head_t(0);
|
||||
rtc_fifo_clear_tags();
|
||||
}
|
||||
|
||||
static inline void rtc_fifo_init(uint32_t first, uint32_t last, uint32_t tagcount)
|
||||
{
|
||||
rtc_fifo_put_loc(first,last,tagcount);
|
||||
rtc_fifo_clear_content();
|
||||
}
|
||||
|
||||
static inline void rtc_fifo_init_default(uint32_t tagcount)
|
||||
{
|
||||
if (tagcount==0)
|
||||
tagcount=RTC_DEFAULT_TAGCOUNT;
|
||||
|
||||
rtc_fifo_init(RTC_DEFAULT_FIFO_START,RTC_DEFAULT_FIFO_END,tagcount);
|
||||
}
|
||||
|
||||
|
||||
static inline uint8_t rtc_fifo_check_magic(void)
|
||||
{
|
||||
if (rtc_mem_read(RTC_FIFO_MAGIC_POS)==RTC_FIFO_MAGIC)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void rtc_fifo_set_magic(void)
|
||||
{
|
||||
rtc_mem_write(RTC_FIFO_MAGIC_POS,RTC_FIFO_MAGIC);
|
||||
}
|
||||
|
||||
static inline void rtc_fifo_unset_magic(void)
|
||||
{
|
||||
rtc_mem_write(RTC_FIFO_MAGIC_POS,0);
|
||||
}
|
||||
|
||||
static inline void rtc_fifo_deep_sleep_until_sample(uint32_t min_sleep_us)
|
||||
{
|
||||
uint32_t align=rtc_mem_read(RTC_ALIGNMENT_POS);
|
||||
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)
|
||||
{
|
||||
rtc_mem_write(RTC_SAMPLESPERBOOT_POS,samples_per_boot);
|
||||
rtc_mem_write(RTC_ALIGNMENT_POS,us_per_sample);
|
||||
|
||||
rtc_put_samples_to_take(0);
|
||||
rtc_fifo_init_default(tagcount);
|
||||
rtc_fifo_set_magic();
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* 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 Johny Mattsson <jmattsson@dius.com.au>
|
||||
*/
|
||||
#ifndef _RTCTIME_H_
|
||||
#define _RTCTIME_H_
|
||||
|
||||
/* We don't want to expose the raw rtctime interface as it is heavily
|
||||
* '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 <c_types.h>
|
||||
|
||||
#ifndef _RTCTIME_INTERNAL_H_
|
||||
struct rtc_timeval
|
||||
{
|
||||
uint32_t tv_sec;
|
||||
uint32_t tv_usec;
|
||||
};
|
||||
#endif
|
||||
|
||||
void TEXT_SECTION_ATTR rtctime_early_startup (void);
|
||||
void rtctime_late_startup (void);
|
||||
void rtctime_gettimeofday (struct rtc_timeval *tv);
|
||||
void rtctime_settimeofday (const struct rtc_timeval *tv);
|
||||
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);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,737 @@
|
|||
/*
|
||||
* 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
|
|
@ -35,6 +35,10 @@
|
|||
#define LUA_USE_MODULES_CRYPTO
|
||||
#define LUA_USE_MODULES_RC
|
||||
#define LUA_USE_MODULES_DHT
|
||||
#define LUA_USE_MODULES_RTCMEM
|
||||
#define LUA_USE_MODULES_RTCTIME
|
||||
#define LUA_USE_MODULES_RTCFIFO
|
||||
#define LUA_USE_MODULES_SNTP
|
||||
|
||||
#endif /* LUA_USE_MODULES */
|
||||
|
||||
|
|
|
@ -181,6 +181,38 @@
|
|||
#define ROM_MODULES_DHT
|
||||
#endif
|
||||
|
||||
#if defined(LUA_USE_MODULES_RTCMEM)
|
||||
#define MODULES_RTCMEM "rtcmem"
|
||||
#define ROM_MODULES_RTCMEM \
|
||||
_ROM(MODULES_RTCMEM, luaopen_rtcmem, rtcmem_map)
|
||||
#else
|
||||
#define ROM_MODULES_RTCMEM
|
||||
#endif
|
||||
|
||||
#if defined(LUA_USE_MODULES_RTCTIME)
|
||||
#define MODULES_RTCTIME "rtctime"
|
||||
#define ROM_MODULES_RTCTIME \
|
||||
_ROM(MODULES_RTCTIME, luaopen_rtctime, rtctime_map)
|
||||
#else
|
||||
#define ROM_MODULES_RTCTIME
|
||||
#endif
|
||||
|
||||
#if defined(LUA_USE_MODULES_RTCFIFO)
|
||||
#define MODULES_RTCFIFO "rtcfifo"
|
||||
#define ROM_MODULES_RTCFIFO \
|
||||
_ROM(MODULES_RTCFIFO, luaopen_rtcfifo, rtcfifo_map)
|
||||
#else
|
||||
#define ROM_MODULES_RTCFIFO
|
||||
#endif
|
||||
|
||||
#if defined(LUA_USE_MODULES_SNTP)
|
||||
#define MODULES_SNTP "sntp"
|
||||
#define ROM_MODULES_SNTP \
|
||||
_ROM(MODULES_SNTP, luaopen_sntp, sntp_map)
|
||||
#else
|
||||
#define ROM_MODULES_SNTP
|
||||
#endif
|
||||
|
||||
#define LUA_MODULES_ROM \
|
||||
ROM_MODULES_GPIO \
|
||||
ROM_MODULES_PWM \
|
||||
|
@ -203,6 +235,10 @@
|
|||
ROM_MODULES_CJSON \
|
||||
ROM_MODULES_CRYPTO \
|
||||
ROM_MODULES_RC \
|
||||
ROM_MODULES_DHT
|
||||
ROM_MODULES_DHT \
|
||||
ROM_MODULES_RTCMEM \
|
||||
ROM_MODULES_RTCTIME \
|
||||
ROM_MODULES_RTCFIFO \
|
||||
ROM_MODULES_SNTP \
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,192 @@
|
|||
// Module for RTC sample FIFO storage
|
||||
|
||||
#include "lauxlib.h"
|
||||
#include "user_modules.h"
|
||||
#include "rtc/rtctime.h"
|
||||
#define RTCTIME_SLEEP_ALIGNED rtctime_deep_sleep_until_aligned_us
|
||||
#include "rtc/rtcfifo.h"
|
||||
|
||||
// rtcfifo.prepare ([{sensor_count=n, interval_us=m, storage_begin=x, storage_end=y}])
|
||||
static int rtcfifo_prepare (lua_State *L)
|
||||
{
|
||||
uint32_t sensor_count = RTC_DEFAULT_TAGCOUNT;
|
||||
uint32_t interval_us = 0;
|
||||
int first = -1, last = -1;
|
||||
|
||||
if (lua_istable (L, 1))
|
||||
{
|
||||
#ifdef LUA_USE_MODULES_RTCTIME
|
||||
lua_getfield (L, 1, "interval_us");
|
||||
if (lua_isnumber (L, -1))
|
||||
interval_us = lua_tonumber (L, -1);
|
||||
lua_pop (L, 1);
|
||||
#endif
|
||||
|
||||
lua_getfield (L, 1, "sensor_count");
|
||||
if (lua_isnumber (L, -1))
|
||||
sensor_count = lua_tonumber (L, -1);
|
||||
lua_pop (L, 1);
|
||||
|
||||
lua_getfield (L, 1, "storage_begin");
|
||||
if (lua_isnumber (L, -1))
|
||||
first = lua_tonumber (L, -1);
|
||||
lua_pop (L, 1);
|
||||
lua_getfield (L, 1, "storage_end");
|
||||
if (lua_isnumber (L, -1))
|
||||
last = lua_tonumber (L, -1);
|
||||
lua_pop (L, 1);
|
||||
}
|
||||
else if (!lua_isnone (L, 1))
|
||||
return luaL_error (L, "expected table as arg #1");
|
||||
|
||||
rtc_fifo_prepare (0, interval_us, sensor_count);
|
||||
|
||||
if (first != -1 && last != -1)
|
||||
rtc_fifo_put_loc (first, last, sensor_count);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// ready = rtcfifo.ready ()
|
||||
static int rtcfifo_ready (lua_State *L)
|
||||
{
|
||||
lua_pushnumber (L, rtc_fifo_check_magic ());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void check_fifo_magic (lua_State *L)
|
||||
{
|
||||
if (!rtc_fifo_check_magic ())
|
||||
luaL_error (L, "rtcfifo not prepared!");
|
||||
}
|
||||
|
||||
|
||||
// rtcfifo.put (timestamp, value, decimals, sensor_name)
|
||||
static int rtcfifo_put (lua_State *L)
|
||||
{
|
||||
check_fifo_magic (L);
|
||||
|
||||
sample_t s;
|
||||
s.timestamp = luaL_checknumber (L, 1);
|
||||
s.value = luaL_checknumber (L, 2);
|
||||
s.decimals = luaL_checknumber (L, 3);
|
||||
size_t len;
|
||||
const char *str = luaL_checklstring (L, 4, &len);
|
||||
union {
|
||||
uint32_t u;
|
||||
char s[4];
|
||||
} conv = { 0 };
|
||||
strncpy (conv.s, str, len > 4 ? 4 : len);
|
||||
s.tag = conv.u;
|
||||
|
||||
rtc_fifo_store_sample (&s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int extract_sample (lua_State *L, const sample_t *s)
|
||||
{
|
||||
lua_pushnumber (L, s->timestamp);
|
||||
lua_pushnumber (L, s->value);
|
||||
lua_pushnumber (L, s->decimals);
|
||||
union {
|
||||
uint32_t u;
|
||||
char s[4];
|
||||
} conv = { s->tag };
|
||||
if (conv.s[3] == 0)
|
||||
lua_pushstring (L, conv.s);
|
||||
else
|
||||
lua_pushlstring (L, conv.s, 4);
|
||||
return 4;
|
||||
}
|
||||
|
||||
|
||||
// timestamp, value, decimals, sensor_name = rtcfifo.pop ()
|
||||
static int rtcfifo_pop (lua_State *L)
|
||||
{
|
||||
check_fifo_magic (L);
|
||||
|
||||
sample_t s;
|
||||
if (!rtc_fifo_pop_sample (&s))
|
||||
return 0;
|
||||
else
|
||||
return extract_sample (L, &s);
|
||||
}
|
||||
|
||||
|
||||
// timestamp, value, decimals, sensor_name = rtcfifo.peek ([offset])
|
||||
static int rtcfifo_peek (lua_State *L)
|
||||
{
|
||||
check_fifo_magic (L);
|
||||
|
||||
sample_t s;
|
||||
uint32_t offs = 0;
|
||||
if (lua_isnumber (L, 1))
|
||||
offs = lua_tonumber (L, 1);
|
||||
if (!rtc_fifo_peek_sample (&s, offs))
|
||||
return 0;
|
||||
else
|
||||
return extract_sample (L, &s);
|
||||
}
|
||||
|
||||
|
||||
// rtcfifo.drop (num)
|
||||
static int rtcfifo_drop (lua_State *L)
|
||||
{
|
||||
check_fifo_magic (L);
|
||||
|
||||
rtc_fifo_drop_samples (luaL_checknumber (L, 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// num = rtcfifo.count ()
|
||||
static int rtcfifo_count (lua_State *L)
|
||||
{
|
||||
check_fifo_magic (L);
|
||||
|
||||
lua_pushnumber (L, rtc_fifo_get_count ());
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
#ifdef LUA_USE_MODULES_RTCTIME
|
||||
// rtcfifo.dsleep_until_sample (min_sleep_us)
|
||||
static int rtcfifo_dsleep_until_sample (lua_State *L)
|
||||
{
|
||||
check_fifo_magic (L);
|
||||
|
||||
uint32_t min_us = luaL_checknumber (L, 1);
|
||||
rtc_fifo_deep_sleep_until_sample (min_us); // no return
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Module function map
|
||||
#define MIN_OPT_LEVEL 2
|
||||
#include "lrodefs.h"
|
||||
const LUA_REG_TYPE rtcfifo_map[] =
|
||||
{
|
||||
{ LSTRKEY("prepare"), LFUNCVAL(rtcfifo_prepare) },
|
||||
{ LSTRKEY("ready"), LFUNCVAL(rtcfifo_ready) },
|
||||
{ LSTRKEY("put"), LFUNCVAL(rtcfifo_put) },
|
||||
{ LSTRKEY("pop"), LFUNCVAL(rtcfifo_pop) },
|
||||
{ LSTRKEY("peek"), LFUNCVAL(rtcfifo_peek) },
|
||||
{ LSTRKEY("drop"), LFUNCVAL(rtcfifo_drop) },
|
||||
{ LSTRKEY("count"), LFUNCVAL(rtcfifo_count) },
|
||||
#ifdef LUA_USE_MODULES_RTCTIME
|
||||
{ LSTRKEY("dsleep_until_sample"), LFUNCVAL(rtcfifo_dsleep_until_sample) },
|
||||
#endif
|
||||
{ LNILKEY, LNILVAL }
|
||||
};
|
||||
|
||||
LUALIB_API int luaopen_rtcfifo (lua_State *L)
|
||||
{
|
||||
#if LUA_OPTIMIZE_MEMORY > 0
|
||||
return 0;
|
||||
#else
|
||||
luaL_register (L, AUXLIB_RTCFIFO, rtcfifo_map);
|
||||
return 1;
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
// Module for RTC user memory access
|
||||
|
||||
#include "lauxlib.h"
|
||||
#include "rtc/rtcaccess.h"
|
||||
|
||||
static int rtcmem_read32 (lua_State *L)
|
||||
{
|
||||
int idx = luaL_checknumber (L, 1);
|
||||
int n = 1;
|
||||
if (lua_isnumber (L, 2))
|
||||
n = lua_tonumber (L, 2);
|
||||
|
||||
if (!lua_checkstack (L, n))
|
||||
return 0;
|
||||
|
||||
int ret = 0;
|
||||
while (n > 0 && idx >= 0 && idx < RTC_USER_MEM_NUM_DWORDS)
|
||||
{
|
||||
lua_pushinteger (L, rtc_mem_read (idx++));
|
||||
--n;
|
||||
++ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int rtcmem_write32 (lua_State *L)
|
||||
{
|
||||
int idx = luaL_checknumber (L, 1);
|
||||
int n = lua_gettop (L) - 1;
|
||||
luaL_argcheck (
|
||||
L, idx + n <= RTC_USER_MEM_NUM_DWORDS, 1, "RTC mem would overrun");
|
||||
int src = 2;
|
||||
while (n-- > 0)
|
||||
{
|
||||
rtc_mem_write (idx++, lua_tonumber (L, src++));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Module function map
|
||||
#define MIN_OPT_LEVEL 2
|
||||
#include "lrodefs.h"
|
||||
const LUA_REG_TYPE rtcmem_map[] =
|
||||
{
|
||||
{ LSTRKEY("read32"), LFUNCVAL(rtcmem_read32) },
|
||||
{ LSTRKEY("write32"), LFUNCVAL(rtcmem_write32) },
|
||||
{ LNILKEY, LNILVAL }
|
||||
};
|
||||
|
||||
LUALIB_API int luaopen_rtcmem (lua_State *L)
|
||||
{
|
||||
#if LUA_OPTIMIZE_MEMORY > 0
|
||||
return 0;
|
||||
#else
|
||||
luaL_register (L, AUXLIB_RTCMEM, rtcmem_map);
|
||||
return 1;
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
// Module for RTC time keeping
|
||||
|
||||
#include "lauxlib.h"
|
||||
|
||||
#include "rtc/rtctime_internal.h"
|
||||
#include "rtc/rtctime.h"
|
||||
|
||||
|
||||
// ******* 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 ())
|
||||
rtc_time_prepare ();
|
||||
|
||||
uint32_t sec = luaL_checknumber (L, 1);
|
||||
uint32_t usec = 0;
|
||||
if (lua_isnumber (L, 2))
|
||||
usec = lua_tonumber (L, 2);
|
||||
|
||||
struct rtc_timeval tv = { sec, usec };
|
||||
rtctime_settimeofday (&tv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// sec, usec = rtctime.get ()
|
||||
static int rtctime_get (lua_State *L)
|
||||
{
|
||||
struct rtc_timeval tv;
|
||||
rtctime_gettimeofday (&tv);
|
||||
lua_pushnumber (L, tv.tv_sec);
|
||||
lua_pushnumber (L, tv.tv_usec);
|
||||
return 2;
|
||||
}
|
||||
|
||||
static void do_sleep_opt (lua_State *L, int idx)
|
||||
{
|
||||
if (lua_isnumber (L, idx))
|
||||
{
|
||||
uint32_t opt = lua_tonumber (L, idx);
|
||||
if (opt < 0 || opt > 4)
|
||||
luaL_error (L, "unknown sleep option");
|
||||
deep_sleep_set_option (opt);
|
||||
}
|
||||
}
|
||||
|
||||
// rtctime.dsleep (usec, option)
|
||||
static int rtctime_dsleep (lua_State *L)
|
||||
{
|
||||
uint32_t us = luaL_checknumber (L, 1);
|
||||
do_sleep_opt (L, 2);
|
||||
rtctime_deep_sleep_us (us); // does not return
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// rtctime.dsleep_aligned (aligned_usec, min_usec, option)
|
||||
static int rtctime_dsleep_aligned (lua_State *L)
|
||||
{
|
||||
if (!rtctime_have_time ())
|
||||
return luaL_error (L, "time not available, unable to align");
|
||||
|
||||
uint32_t align_us = luaL_checknumber (L, 1);
|
||||
uint32_t min_us = luaL_checknumber (L, 2);
|
||||
do_sleep_opt (L, 3);
|
||||
rtctime_deep_sleep_until_aligned_us (align_us, min_us); // does not return
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Module function map
|
||||
#define MIN_OPT_LEVEL 2
|
||||
#include "lrodefs.h"
|
||||
const LUA_REG_TYPE rtctime_map[] =
|
||||
{
|
||||
{ LSTRKEY("set"), LFUNCVAL(rtctime_set) },
|
||||
{ LSTRKEY("get"), LFUNCVAL(rtctime_get) },
|
||||
{ LSTRKEY("dsleep"), LFUNCVAL(rtctime_dsleep) },
|
||||
{ LSTRKEY("dsleep_aligned"), LFUNCVAL(rtctime_dsleep_aligned) },
|
||||
{ LNILKEY, LNILVAL }
|
||||
};
|
||||
|
||||
LUALIB_API int luaopen_rtctime (lua_State *L)
|
||||
{
|
||||
#if LUA_OPTIMIZE_MEMORY > 0
|
||||
return 0;
|
||||
#else
|
||||
luaL_register (L, AUXLIB_RTCTIME, rtctime_map);
|
||||
return 1;
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,369 @@
|
|||
/*
|
||||
* 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 Johny Mattsson <jmattsson@dius.com.au>
|
||||
*/
|
||||
|
||||
// Module for Simple Network Time Protocol (SNTP)
|
||||
|
||||
#include "lauxlib.h"
|
||||
#include "os_type.h"
|
||||
#include "osapi.h"
|
||||
#include "lwip/udp.h"
|
||||
#include "c_stdlib.h"
|
||||
#include "user_modules.h"
|
||||
|
||||
#ifdef LUA_USE_MODULES_RTCTIME
|
||||
#include "rtc/rtctime.h"
|
||||
#endif
|
||||
|
||||
#define NTP_PORT 123
|
||||
#define NTP_ANYCAST_ADDR(dst) IP4_ADDR(dst, 224, 0, 1, 1)
|
||||
|
||||
#define MAX_ATTEMPTS 5
|
||||
|
||||
#if 0
|
||||
# define sntp_dbg(...) c_printf(__VA_ARGS__)
|
||||
#else
|
||||
# define sntp_dbg(...)
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t sec;
|
||||
uint32_t frac;
|
||||
} ntp_timestamp_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t mode : 3;
|
||||
uint8_t ver : 3;
|
||||
uint8_t LI : 2;
|
||||
uint8_t stratum;
|
||||
uint8_t poll;
|
||||
uint8_t precision;
|
||||
uint32_t root_delay;
|
||||
uint32_t root_dispersion;
|
||||
uint32_t refid;
|
||||
ntp_timestamp_t ref;
|
||||
ntp_timestamp_t origin;
|
||||
ntp_timestamp_t recv;
|
||||
ntp_timestamp_t xmit;
|
||||
} ntp_frame_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
struct udp_pcb *pcb;
|
||||
ntp_timestamp_t cookie;
|
||||
os_timer_t timer;
|
||||
int sync_cb_ref;
|
||||
int err_cb_ref;
|
||||
uint8_t attempts;
|
||||
} sntp_state_t;
|
||||
|
||||
static sntp_state_t *state;
|
||||
static ip_addr_t server;
|
||||
|
||||
static void cleanup (lua_State *L)
|
||||
{
|
||||
os_timer_disarm (&state->timer);
|
||||
udp_remove (state->pcb);
|
||||
luaL_unref (L, LUA_REGISTRYINDEX, state->sync_cb_ref);
|
||||
luaL_unref (L, LUA_REGISTRYINDEX, state->err_cb_ref);
|
||||
os_free (state);
|
||||
state = 0;
|
||||
}
|
||||
|
||||
|
||||
static void handle_error (lua_State *L)
|
||||
{
|
||||
sntp_dbg("sntp: handle_error\n");
|
||||
if (state->err_cb_ref != LUA_NOREF)
|
||||
{
|
||||
lua_rawgeti (L, LUA_REGISTRYINDEX, state->err_cb_ref);
|
||||
cleanup (L);
|
||||
lua_call (L, 0, 0);
|
||||
}
|
||||
else
|
||||
cleanup (L);
|
||||
}
|
||||
|
||||
|
||||
static void sntp_dosend (lua_State *L)
|
||||
{
|
||||
++state->attempts;
|
||||
sntp_dbg("sntp: attempt %d\n", state->attempts);
|
||||
|
||||
struct pbuf *p = pbuf_alloc (PBUF_TRANSPORT, sizeof (ntp_frame_t), PBUF_RAM);
|
||||
if (!p)
|
||||
handle_error (L);
|
||||
|
||||
ntp_frame_t req;
|
||||
os_memset (&req, 0, sizeof (req));
|
||||
req.ver = 4;
|
||||
req.mode = 3; // client
|
||||
#ifdef LUA_USE_MODULES_RTCTIME
|
||||
struct rtc_timeval tv;
|
||||
rtctime_gettimeofday (&tv);
|
||||
req.xmit.sec = htonl (tv.tv_sec);
|
||||
req.xmit.frac = htonl (tv.tv_usec);
|
||||
#else
|
||||
req.xmit.frac = htonl (system_get_time ());
|
||||
#endif
|
||||
state->cookie = req.xmit;
|
||||
|
||||
os_memcpy (p->payload, &req, sizeof (req));
|
||||
int ret = udp_sendto (state->pcb, p, &server, NTP_PORT);
|
||||
sntp_dbg("sntp: send: %d\n", ret);
|
||||
pbuf_free (p);
|
||||
if (ret != ERR_OK)
|
||||
handle_error (L);
|
||||
}
|
||||
|
||||
|
||||
static void on_timeout (void *arg)
|
||||
{
|
||||
sntp_dbg("sntp: timer\n");
|
||||
lua_State *L = arg;
|
||||
if (state->attempts >= MAX_ATTEMPTS)
|
||||
handle_error (L);
|
||||
else
|
||||
sntp_dosend (L);
|
||||
}
|
||||
|
||||
|
||||
static void on_recv (void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, uint16_t port)
|
||||
{
|
||||
(void)port;
|
||||
sntp_dbg("sntp: on_recv\n");
|
||||
|
||||
lua_State *L = arg;
|
||||
|
||||
if (!state || state->pcb != pcb)
|
||||
{
|
||||
// "impossible", but don't leak if it did happen somehow...
|
||||
udp_remove (pcb);
|
||||
pbuf_free (p);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
if (p->len < sizeof (ntp_frame_t))
|
||||
{
|
||||
pbuf_free (p);
|
||||
return; // not an ntp frame, ignore
|
||||
}
|
||||
|
||||
// make sure we have an aligned copy to work from
|
||||
ntp_frame_t ntp;
|
||||
os_memcpy (&ntp, p->payload, sizeof (ntp));
|
||||
pbuf_free (p);
|
||||
sntp_dbg("sntp: transmit timestamp: %u, %u\n", ntp.xmit.sec, ntp.xmit.frac);
|
||||
|
||||
// sanity checks before we touch our clocks
|
||||
ip_addr_t anycast;
|
||||
NTP_ANYCAST_ADDR(&anycast);
|
||||
if (server.addr != anycast.addr && server.addr != addr->addr)
|
||||
return; // unknown sender, ignore
|
||||
|
||||
if (ntp.origin.sec != state->cookie.sec ||
|
||||
ntp.origin.frac != state->cookie.frac)
|
||||
return; // unsolicited message, ignore
|
||||
|
||||
if (ntp.LI == 3)
|
||||
return; // server clock not synchronized (why did it even respond?!)
|
||||
|
||||
server.addr = addr->addr;
|
||||
ntp.origin.sec = ntohl (ntp.origin.sec);
|
||||
ntp.origin.frac = ntohl (ntp.origin.frac);
|
||||
ntp.recv.sec = ntohl (ntp.recv.sec);
|
||||
ntp.recv.frac = ntohl (ntp.recv.frac);
|
||||
ntp.xmit.sec = ntohl (ntp.xmit.sec);
|
||||
ntp.xmit.frac = ntohl (ntp.xmit.frac);
|
||||
|
||||
const uint32_t UINT32_MAXI = (uint32_t)-1;
|
||||
const uint64_t MICROSECONDS = 1000000ull;
|
||||
const uint32_t NTP_TO_UNIX_EPOCH = 2208988800ul;
|
||||
|
||||
bool have_cb = (state->sync_cb_ref != LUA_NOREF);
|
||||
|
||||
// if we have rtctime, do higher resolution delta calc, else just use
|
||||
// the transmit timestamp
|
||||
#ifdef LUA_USE_MODULES_RTCTIME
|
||||
struct rtc_timeval tv;
|
||||
|
||||
rtctime_gettimeofday (&tv);
|
||||
ntp_timestamp_t dest;
|
||||
dest.sec = tv.tv_sec;
|
||||
dest.frac = (MICROSECONDS * tv.tv_usec) / UINT32_MAXI;
|
||||
|
||||
// Compensation as per RFC2030
|
||||
int64_t delta_s = (((int64_t)ntp.recv.sec - ntp.origin.sec) +
|
||||
((int64_t)ntp.xmit.sec - dest.sec)) / 2;
|
||||
|
||||
int64_t delta_f = (((int64_t)ntp.recv.frac - ntp.origin.frac) +
|
||||
((int64_t)ntp.xmit.frac - dest.frac)) / 2;
|
||||
|
||||
dest.sec += delta_s;
|
||||
if (delta_f + dest.frac < 0)
|
||||
{
|
||||
delta_f += UINT32_MAXI;
|
||||
--dest.sec;
|
||||
}
|
||||
else if (delta_f + dest.frac > UINT32_MAXI)
|
||||
{
|
||||
delta_f -= UINT32_MAXI;
|
||||
++dest.sec;
|
||||
}
|
||||
dest.frac += delta_f;
|
||||
|
||||
tv.tv_sec = dest.sec - NTP_TO_UNIX_EPOCH;
|
||||
tv.tv_usec = (MICROSECONDS * dest.frac) / UINT32_MAXI;
|
||||
rtctime_settimeofday (&tv);
|
||||
|
||||
if (have_cb)
|
||||
{
|
||||
lua_rawgeti (L, LUA_REGISTRYINDEX, state->sync_cb_ref);
|
||||
lua_pushnumber (L, tv.tv_sec);
|
||||
lua_pushnumber (L, tv.tv_usec);
|
||||
}
|
||||
#else
|
||||
if (have_cb)
|
||||
{
|
||||
lua_rawgeti (L, LUA_REGISTRYINDEX, state->sync_cb_ref);
|
||||
lua_pushnumber (L, ntp.xmit.sec - NTP_TO_UNIX_EPOCH);
|
||||
lua_pushnumber (L, (MICROSECONDS * ntp.xmit.frac) / UINT32_MAXI);
|
||||
}
|
||||
#endif
|
||||
|
||||
cleanup (L);
|
||||
|
||||
if (have_cb)
|
||||
{
|
||||
lua_pushstring (L, ipaddr_ntoa (&server));
|
||||
lua_call (L, 3, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// sntp.sync (server or nil, syncfn or nil, errfn or nil)
|
||||
static int sntp_sync (lua_State *L)
|
||||
{
|
||||
// default to anycast address, then allow last server to stick
|
||||
if (server.addr == IPADDR_ANY)
|
||||
NTP_ANYCAST_ADDR(&server);
|
||||
|
||||
const char *errmsg = 0;
|
||||
#define sync_err(x) do { errmsg = x; goto error; } while (0)
|
||||
|
||||
if (state)
|
||||
return luaL_error (L, "sync in progress");
|
||||
|
||||
state = (sntp_state_t *)c_malloc (sizeof (sntp_state_t));
|
||||
if (!state)
|
||||
sync_err ("out of memory");
|
||||
|
||||
memset (state, 0, sizeof (sntp_state_t));
|
||||
|
||||
state->sync_cb_ref = LUA_NOREF;
|
||||
state->err_cb_ref = LUA_NOREF;
|
||||
|
||||
state->pcb = udp_new ();
|
||||
if (!state->pcb)
|
||||
sync_err ("out of memory");
|
||||
|
||||
if (udp_bind (state->pcb, IP_ADDR_ANY, NTP_PORT) != ERR_OK)
|
||||
sync_err ("ntp port in use");
|
||||
|
||||
udp_recv (state->pcb, on_recv, L);
|
||||
|
||||
// use last server, unless new one specified
|
||||
if (!lua_isnoneornil (L, 1))
|
||||
{
|
||||
if (!ipaddr_aton (luaL_checkstring (L, 1), &server))
|
||||
sync_err ("bad IP address");
|
||||
}
|
||||
|
||||
if (!lua_isnoneornil (L, 2))
|
||||
{
|
||||
lua_pushvalue (L, 2);
|
||||
state->sync_cb_ref = luaL_ref (L, LUA_REGISTRYINDEX);
|
||||
}
|
||||
else
|
||||
state->sync_cb_ref = LUA_NOREF;
|
||||
|
||||
if (!lua_isnoneornil (L, 3))
|
||||
{
|
||||
lua_pushvalue (L, 3);
|
||||
state->err_cb_ref = luaL_ref (L, LUA_REGISTRYINDEX);
|
||||
}
|
||||
else
|
||||
state->err_cb_ref = LUA_NOREF;
|
||||
|
||||
os_timer_disarm (&state->timer);
|
||||
os_timer_setfn (&state->timer, on_timeout, L);
|
||||
os_timer_arm (&state->timer, 1000, 1);
|
||||
|
||||
state->attempts = 0;
|
||||
sntp_dosend (L);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
if (state)
|
||||
{
|
||||
if (state->pcb)
|
||||
udp_remove (state->pcb);
|
||||
c_free (state);
|
||||
state = 0;
|
||||
}
|
||||
return luaL_error (L, errmsg);
|
||||
}
|
||||
|
||||
|
||||
// Module function map
|
||||
#define MIN_OPT_LEVEL 2
|
||||
#include "lrodefs.h"
|
||||
const LUA_REG_TYPE sntp_map[] =
|
||||
{
|
||||
{ LSTRKEY("sync"), LFUNCVAL(sntp_sync) },
|
||||
{ LNILKEY, LNILVAL }
|
||||
};
|
||||
|
||||
LUALIB_API int luaopen_sntp (lua_State *L)
|
||||
{
|
||||
#if LUA_OPTIMIZE_MEMORY > 0
|
||||
return 0;
|
||||
#else
|
||||
luaL_register (L, AUXLIB_SNTP, sntp_map);
|
||||
return 1;
|
||||
#endif
|
||||
}
|
|
@ -1105,7 +1105,6 @@ static const LUA_REG_TYPE wifi_station_map[] =
|
|||
{ LSTRKEY( "connect" ), LFUNCVAL ( wifi_station_connect4lua ) },
|
||||
{ LSTRKEY( "disconnect" ), LFUNCVAL ( wifi_station_disconnect4lua ) },
|
||||
{ LSTRKEY( "autoconnect" ), LFUNCVAL ( wifi_station_setauto ) },
|
||||
{ LSTRKEY( "getconfig" ), LFUNCVAL( wifi_ap_getconfig ) },
|
||||
{ LSTRKEY( "getip" ), LFUNCVAL ( wifi_station_getip ) },
|
||||
{ LSTRKEY( "setip" ), LFUNCVAL ( wifi_station_setip ) },
|
||||
{ LSTRKEY( "getbroadcast" ), LFUNCVAL ( wifi_station_getbroadcast) },
|
||||
|
@ -1133,6 +1132,7 @@ static const LUA_REG_TYPE wifi_ap_map[] =
|
|||
{ LSTRKEY( "getmac" ), LFUNCVAL ( wifi_ap_getmac ) },
|
||||
{ LSTRKEY( "setmac" ), LFUNCVAL ( wifi_ap_setmac ) },
|
||||
{ LSTRKEY( "getclient" ), LFUNCVAL ( wifi_ap_listclient ) },
|
||||
{ LSTRKEY( "getconfig" ), LFUNCVAL( wifi_ap_getconfig ) },
|
||||
#if LUA_OPTIMIZE_MEMORY > 0
|
||||
{ LSTRKEY( "dhcp" ), LROVAL( wifi_ap_dhcp_map ) },
|
||||
|
||||
|
|
|
@ -17,11 +17,16 @@
|
|||
#include "flash_fs.h"
|
||||
#include "user_interface.h"
|
||||
#include "user_exceptions.h"
|
||||
#include "user_modules.h"
|
||||
|
||||
#include "ets_sys.h"
|
||||
#include "driver/uart.h"
|
||||
#include "mem.h"
|
||||
|
||||
#ifdef LUA_USE_MODULES_RTCTIME
|
||||
#include "rtc/rtctime.h"
|
||||
#endif
|
||||
|
||||
#define SIG_LUA 0
|
||||
#define TASK_QUEUE_LEN 4
|
||||
os_event_t *taskQueue;
|
||||
|
@ -31,12 +36,16 @@ os_event_t *taskQueue;
|
|||
* by the time it is invoked the irom has not yet been mapped. This naturally
|
||||
* also goes for anything the trampoline itself calls.
|
||||
*/
|
||||
void user_start_trampoline (void) TEXT_SECTION_ATTR;
|
||||
void user_start_trampoline (void)
|
||||
void TEXT_SECTION_ATTR user_start_trampoline (void)
|
||||
{
|
||||
__real__xtos_set_exception_handler (
|
||||
EXCCAUSE_LOAD_STORE_ERROR, load_non_32_wide_handler);
|
||||
|
||||
#ifdef LUA_USE_MODULES_RTCTIME
|
||||
// Note: Keep this as close to call_user_start() as possible, since it
|
||||
// is where the cpu clock actually gets bumped to 80MHz.
|
||||
rtctime_early_startup ();
|
||||
#endif
|
||||
call_user_start ();
|
||||
}
|
||||
|
||||
|
@ -141,6 +150,9 @@ void nodemcu_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());
|
||||
// system_print_meminfo();
|
||||
// os_printf("Heap size::%d.\n",system_get_free_heap_size());
|
||||
|
|
|
@ -85,7 +85,7 @@ typedef enum {
|
|||
#endif /* ICACHE_FLASH */
|
||||
|
||||
#define TEXT_SECTION_ATTR __attribute__((section(".text")))
|
||||
#define RAM_CONST_ATTR __attribute__((section(".text")))
|
||||
#define RAM_CONST_ATTR __attribute__((section(".rodata")))
|
||||
|
||||
#ifndef __cplusplus
|
||||
typedef unsigned char bool;
|
||||
|
|
Loading…
Reference in New Issue