mirror of
https://github.com/esp8266/Arduino.git
synced 2025-04-19 23:22:16 +03:00
Allman now (#6080)
* switch restyle script for CI * remove confirmation * restyle with allman
This commit is contained in:
parent
625c3a62c4
commit
98125f8860
@ -15,7 +15,7 @@
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
*/
|
||||
|
||||
/*
|
||||
This class allows to explore all configured IP addresses
|
||||
@ -94,8 +94,8 @@ namespace AddressListImplementation
|
||||
|
||||
struct netifWrapper
|
||||
{
|
||||
netifWrapper (netif* netif) : _netif(netif), _num(-1) {}
|
||||
netifWrapper (const netifWrapper& o) : _netif(o._netif), _num(o._num) {}
|
||||
netifWrapper(netif* netif) : _netif(netif), _num(-1) {}
|
||||
netifWrapper(const netifWrapper& o) : _netif(o._netif), _num(o._num) {}
|
||||
|
||||
netifWrapper& operator= (const netifWrapper& o)
|
||||
{
|
||||
@ -110,25 +110,64 @@ struct netifWrapper
|
||||
}
|
||||
|
||||
// address properties
|
||||
IPAddress addr () const { return ipFromNetifNum(); }
|
||||
bool isLegacy () const { return _num == 0; }
|
||||
bool isLocal () const { return addr().isLocal(); }
|
||||
bool isV4 () const { return addr().isV4(); }
|
||||
bool isV6 () const { return !addr().isV4(); }
|
||||
String toString() const { return addr().toString(); }
|
||||
IPAddress addr() const
|
||||
{
|
||||
return ipFromNetifNum();
|
||||
}
|
||||
bool isLegacy() const
|
||||
{
|
||||
return _num == 0;
|
||||
}
|
||||
bool isLocal() const
|
||||
{
|
||||
return addr().isLocal();
|
||||
}
|
||||
bool isV4() const
|
||||
{
|
||||
return addr().isV4();
|
||||
}
|
||||
bool isV6() const
|
||||
{
|
||||
return !addr().isV4();
|
||||
}
|
||||
String toString() const
|
||||
{
|
||||
return addr().toString();
|
||||
}
|
||||
|
||||
// related to legacy address (_num=0, ipv4)
|
||||
IPAddress ipv4 () const { return _netif->ip_addr; }
|
||||
IPAddress netmask () const { return _netif->netmask; }
|
||||
IPAddress gw () const { return _netif->gw; }
|
||||
IPAddress ipv4() const
|
||||
{
|
||||
return _netif->ip_addr;
|
||||
}
|
||||
IPAddress netmask() const
|
||||
{
|
||||
return _netif->netmask;
|
||||
}
|
||||
IPAddress gw() const
|
||||
{
|
||||
return _netif->gw;
|
||||
}
|
||||
|
||||
// common to all addresses of this interface
|
||||
String ifname () const { return String(_netif->name[0]) + _netif->name[1]; }
|
||||
const char* ifhostname () const { return _netif->hostname?: emptyString.c_str(); }
|
||||
const char* ifmac () const { return (const char*)_netif->hwaddr; }
|
||||
int ifnumber () const { return _netif->num; }
|
||||
String ifname() const
|
||||
{
|
||||
return String(_netif->name[0]) + _netif->name[1];
|
||||
}
|
||||
const char* ifhostname() const
|
||||
{
|
||||
return _netif->hostname ? : emptyString.c_str();
|
||||
}
|
||||
const char* ifmac() const
|
||||
{
|
||||
return (const char*)_netif->hwaddr;
|
||||
}
|
||||
int ifnumber() const
|
||||
{
|
||||
return _netif->num;
|
||||
}
|
||||
|
||||
const ip_addr_t* ipFromNetifNum () const
|
||||
const ip_addr_t* ipFromNetifNum() const
|
||||
{
|
||||
#if LWIP_IPV6
|
||||
return _num ? &_netif->ip6_addr[_num - 1] : &_netif->ip_addr;
|
||||
@ -150,8 +189,8 @@ struct netifWrapper
|
||||
class AddressListIterator
|
||||
{
|
||||
public:
|
||||
AddressListIterator (const netifWrapper& o) : netIf(o) {}
|
||||
AddressListIterator (netif* netif) : netIf(netif)
|
||||
AddressListIterator(const netifWrapper& o) : netIf(o) {}
|
||||
AddressListIterator(netif* netif) : netIf(netif)
|
||||
{
|
||||
// This constructor is called with lwIP's global netif_list, or
|
||||
// nullptr. operator++() is designed to loop through _configured_
|
||||
@ -160,13 +199,29 @@ public:
|
||||
(void)operator++();
|
||||
}
|
||||
|
||||
const netifWrapper& operator* () const { return netIf; }
|
||||
const netifWrapper* operator-> () const { return &netIf; }
|
||||
const netifWrapper& operator* () const
|
||||
{
|
||||
return netIf;
|
||||
}
|
||||
const netifWrapper* operator-> () const
|
||||
{
|
||||
return &netIf;
|
||||
}
|
||||
|
||||
bool operator== (AddressListIterator& o) { return netIf.equal(*o); }
|
||||
bool operator!= (AddressListIterator& o) { return !netIf.equal(*o); }
|
||||
bool operator== (AddressListIterator& o)
|
||||
{
|
||||
return netIf.equal(*o);
|
||||
}
|
||||
bool operator!= (AddressListIterator& o)
|
||||
{
|
||||
return !netIf.equal(*o);
|
||||
}
|
||||
|
||||
AddressListIterator& operator= (const AddressListIterator& o) { netIf = o.netIf; return *this; }
|
||||
AddressListIterator& operator= (const AddressListIterator& o)
|
||||
{
|
||||
netIf = o.netIf;
|
||||
return *this;
|
||||
}
|
||||
|
||||
AddressListIterator operator++ (int)
|
||||
{
|
||||
@ -188,8 +243,10 @@ public:
|
||||
}
|
||||
if (!ip_addr_isany(netIf.ipFromNetifNum()))
|
||||
// found an initialized address
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -202,13 +259,25 @@ class AddressList
|
||||
public:
|
||||
using const_iterator = const AddressListIterator;
|
||||
|
||||
const_iterator begin () const { return const_iterator(netif_list); }
|
||||
const_iterator end () const { return const_iterator(nullptr); }
|
||||
const_iterator begin() const
|
||||
{
|
||||
return const_iterator(netif_list);
|
||||
}
|
||||
const_iterator end() const
|
||||
{
|
||||
return const_iterator(nullptr);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
inline AddressList::const_iterator begin (const AddressList& a) { return a.begin(); }
|
||||
inline AddressList::const_iterator end (const AddressList& a) { return a.end(); }
|
||||
inline AddressList::const_iterator begin(const AddressList& a)
|
||||
{
|
||||
return a.begin();
|
||||
}
|
||||
inline AddressList::const_iterator end(const AddressList& a)
|
||||
{
|
||||
return a.end();
|
||||
}
|
||||
|
||||
|
||||
} // AddressListImplementation
|
||||
|
@ -15,7 +15,7 @@
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
*/
|
||||
|
||||
#ifndef Arduino_h
|
||||
#define Arduino_h
|
||||
@ -86,7 +86,8 @@ extern "C" {
|
||||
#define EXTERNAL 0
|
||||
|
||||
//timer dividers
|
||||
enum TIM_DIV_ENUM {
|
||||
enum TIM_DIV_ENUM
|
||||
{
|
||||
TIM_DIV1 = 0, //80MHz (80 ticks/us - 104857.588 us max)
|
||||
TIM_DIV16 = 1, //5MHz (5 ticks/us - 1677721.4 us max)
|
||||
TIM_DIV256 = 3 //312.5Khz (1 tick = 3.2us - 26843542.4 us max)
|
||||
|
@ -15,7 +15,7 @@
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
*/
|
||||
|
||||
#ifndef client_h
|
||||
#define client_h
|
||||
@ -23,13 +23,14 @@
|
||||
#include "Stream.h"
|
||||
#include "IPAddress.h"
|
||||
|
||||
class Client: public Stream {
|
||||
class Client: public Stream
|
||||
{
|
||||
|
||||
public:
|
||||
virtual int connect(IPAddress ip, uint16_t port) =0;
|
||||
virtual int connect(const char *host, uint16_t port) =0;
|
||||
virtual size_t write(uint8_t) =0;
|
||||
virtual size_t write(const uint8_t *buf, size_t size) =0;
|
||||
public:
|
||||
virtual int connect(IPAddress ip, uint16_t port) = 0;
|
||||
virtual int connect(const char *host, uint16_t port) = 0;
|
||||
virtual size_t write(uint8_t) = 0;
|
||||
virtual size_t write(const uint8_t *buf, size_t size) = 0;
|
||||
virtual int available() = 0;
|
||||
virtual int read() = 0;
|
||||
virtual int read(uint8_t *buf, size_t size) = 0;
|
||||
@ -38,12 +39,14 @@ class Client: public Stream {
|
||||
virtual void stop() = 0;
|
||||
virtual uint8_t connected() = 0;
|
||||
virtual operator bool() = 0;
|
||||
protected:
|
||||
uint8_t* rawIPAddress(IPAddress& addr) {
|
||||
protected:
|
||||
uint8_t* rawIPAddress(IPAddress& addr)
|
||||
{
|
||||
return addr.raw_address();
|
||||
}
|
||||
#if LWIP_VERSION_MAJOR != 1
|
||||
const uint8_t* rawIPAddress(const IPAddress& addr) {
|
||||
const uint8_t* rawIPAddress(const IPAddress& addr)
|
||||
{
|
||||
return addr.raw_address();
|
||||
}
|
||||
#endif
|
||||
|
@ -16,7 +16,7 @@
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
*/
|
||||
|
||||
#include "umm_malloc/umm_malloc.h"
|
||||
#include "umm_malloc/umm_malloc_cfg.h"
|
||||
@ -33,11 +33,17 @@ void EspClass::getHeapStats(uint32_t* hfree, uint16_t* hmax, uint8_t* hfrag)
|
||||
uint8_t block_size = umm_block_size();
|
||||
uint32_t fh = ummHeapInfo.freeBlocks * block_size;
|
||||
if (hfree)
|
||||
{
|
||||
*hfree = fh;
|
||||
}
|
||||
if (hmax)
|
||||
{
|
||||
*hmax = ummHeapInfo.maxFreeContiguousBlocks * block_size;
|
||||
}
|
||||
if (hfrag)
|
||||
{
|
||||
*hfrag = 100 - (sqrt32(ummHeapInfo.freeSize2) * 100) / fh;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t EspClass::getHeapFragmentation()
|
||||
|
@ -16,7 +16,7 @@
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <user_interface.h>
|
||||
|
@ -16,7 +16,7 @@
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
*/
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "flash_utils.h"
|
||||
@ -30,7 +30,7 @@
|
||||
extern "C" {
|
||||
#include "user_interface.h"
|
||||
|
||||
extern struct rst_info resetInfo;
|
||||
extern struct rst_info resetInfo;
|
||||
}
|
||||
|
||||
|
||||
@ -38,45 +38,54 @@ extern struct rst_info resetInfo;
|
||||
|
||||
|
||||
/**
|
||||
* User-defined Literals
|
||||
* usage:
|
||||
*
|
||||
* uint32_t = test = 10_MHz; // --> 10000000
|
||||
*/
|
||||
User-defined Literals
|
||||
usage:
|
||||
|
||||
unsigned long long operator"" _kHz(unsigned long long x) {
|
||||
uint32_t = test = 10_MHz; // --> 10000000
|
||||
*/
|
||||
|
||||
unsigned long long operator"" _kHz(unsigned long long x)
|
||||
{
|
||||
return x * 1000;
|
||||
}
|
||||
|
||||
unsigned long long operator"" _MHz(unsigned long long x) {
|
||||
unsigned long long operator"" _MHz(unsigned long long x)
|
||||
{
|
||||
return x * 1000 * 1000;
|
||||
}
|
||||
|
||||
unsigned long long operator"" _GHz(unsigned long long x) {
|
||||
unsigned long long operator"" _GHz(unsigned long long x)
|
||||
{
|
||||
return x * 1000 * 1000 * 1000;
|
||||
}
|
||||
|
||||
unsigned long long operator"" _kBit(unsigned long long x) {
|
||||
unsigned long long operator"" _kBit(unsigned long long x)
|
||||
{
|
||||
return x * 1024;
|
||||
}
|
||||
|
||||
unsigned long long operator"" _MBit(unsigned long long x) {
|
||||
unsigned long long operator"" _MBit(unsigned long long x)
|
||||
{
|
||||
return x * 1024 * 1024;
|
||||
}
|
||||
|
||||
unsigned long long operator"" _GBit(unsigned long long x) {
|
||||
unsigned long long operator"" _GBit(unsigned long long x)
|
||||
{
|
||||
return x * 1024 * 1024 * 1024;
|
||||
}
|
||||
|
||||
unsigned long long operator"" _kB(unsigned long long x) {
|
||||
unsigned long long operator"" _kB(unsigned long long x)
|
||||
{
|
||||
return x * 1024;
|
||||
}
|
||||
|
||||
unsigned long long operator"" _MB(unsigned long long x) {
|
||||
unsigned long long operator"" _MB(unsigned long long x)
|
||||
{
|
||||
return x * 1024 * 1024;
|
||||
}
|
||||
|
||||
unsigned long long operator"" _GB(unsigned long long x) {
|
||||
unsigned long long operator"" _GB(unsigned long long x)
|
||||
{
|
||||
return x * 1024 * 1024 * 1024;
|
||||
}
|
||||
|
||||
@ -128,58 +137,64 @@ void EspClass::deepSleepInstant(uint64_t time_us, WakeMode mode)
|
||||
uint64_t EspClass::deepSleepMax()
|
||||
{
|
||||
//cali*(2^31-1)/(2^12)
|
||||
return (uint64_t)system_rtc_clock_cali_proc()*(0x80000000-1)/(0x1000);
|
||||
return (uint64_t)system_rtc_clock_cali_proc() * (0x80000000 - 1) / (0x1000);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
Layout of RTC Memory is as follows:
|
||||
Ref: Espressif doc 2C-ESP8266_Non_OS_SDK_API_Reference, section 3.3.23 (system_rtc_mem_write)
|
||||
Layout of RTC Memory is as follows:
|
||||
Ref: Espressif doc 2C-ESP8266_Non_OS_SDK_API_Reference, section 3.3.23 (system_rtc_mem_write)
|
||||
|
||||
|<------system data (256 bytes)------->|<-----------------user data (512 bytes)--------------->|
|
||||
|<------system data (256 bytes)------->|<-----------------user data (512 bytes)--------------->|
|
||||
|
||||
SDK function signature:
|
||||
bool system_rtc_mem_read (
|
||||
SDK function signature:
|
||||
bool system_rtc_mem_read (
|
||||
uint32 des_addr,
|
||||
void * src_addr,
|
||||
uint32 save_size
|
||||
)
|
||||
)
|
||||
|
||||
The system data section can't be used by the user, so:
|
||||
des_addr must be >=64 (i.e.: 256/4) and <192 (i.e.: 768/4)
|
||||
src_addr is a pointer to data
|
||||
save_size is the number of bytes to write
|
||||
The system data section can't be used by the user, so:
|
||||
des_addr must be >=64 (i.e.: 256/4) and <192 (i.e.: 768/4)
|
||||
src_addr is a pointer to data
|
||||
save_size is the number of bytes to write
|
||||
|
||||
For the method interface:
|
||||
offset is the user block number (block size is 4 bytes) must be >= 0 and <128
|
||||
data is a pointer to data, 4-byte aligned
|
||||
size is number of bytes in the block pointed to by data
|
||||
For the method interface:
|
||||
offset is the user block number (block size is 4 bytes) must be >= 0 and <128
|
||||
data is a pointer to data, 4-byte aligned
|
||||
size is number of bytes in the block pointed to by data
|
||||
|
||||
Same for write
|
||||
Same for write
|
||||
|
||||
Note: If the Updater class is in play, e.g.: the application uses OTA, the eboot
|
||||
command will be stored into the first 128 bytes of user data, then it will be
|
||||
retrieved by eboot on boot. That means that user data present there will be lost.
|
||||
Ref:
|
||||
- discussion in PR #5330.
|
||||
- https://github.com/esp8266/esp8266-wiki/wiki/Memory-Map#memmory-mapped-io-registers
|
||||
- Arduino/bootloaders/eboot/eboot_command.h RTC_MEM definition
|
||||
Note: If the Updater class is in play, e.g.: the application uses OTA, the eboot
|
||||
command will be stored into the first 128 bytes of user data, then it will be
|
||||
retrieved by eboot on boot. That means that user data present there will be lost.
|
||||
Ref:
|
||||
- discussion in PR #5330.
|
||||
- https://github.com/esp8266/esp8266-wiki/wiki/Memory-Map#memmory-mapped-io-registers
|
||||
- Arduino/bootloaders/eboot/eboot_command.h RTC_MEM definition
|
||||
*/
|
||||
|
||||
bool EspClass::rtcUserMemoryRead(uint32_t offset, uint32_t *data, size_t size)
|
||||
{
|
||||
if (offset * 4 + size > 512 || size == 0) {
|
||||
if (offset * 4 + size > 512 || size == 0)
|
||||
{
|
||||
return false;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return system_rtc_mem_read(64 + offset, data, size);
|
||||
}
|
||||
}
|
||||
|
||||
bool EspClass::rtcUserMemoryWrite(uint32_t offset, uint32_t *data, size_t size)
|
||||
{
|
||||
if (offset * 4 + size > 512 || size == 0) {
|
||||
if (offset * 4 + size > 512 || size == 0)
|
||||
{
|
||||
return false;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return system_rtc_mem_write(64 + offset, data, size);
|
||||
}
|
||||
}
|
||||
@ -235,7 +250,8 @@ extern "C" const char* core_release;
|
||||
|
||||
String EspClass::getCoreVersion()
|
||||
{
|
||||
if (core_release != NULL) {
|
||||
if (core_release != NULL)
|
||||
{
|
||||
return String(core_release);
|
||||
}
|
||||
char buf[12];
|
||||
@ -267,7 +283,8 @@ uint8_t EspClass::getCpuFreqMHz(void)
|
||||
uint32_t EspClass::getFlashChipId(void)
|
||||
{
|
||||
static uint32_t flash_chip_id = 0;
|
||||
if (flash_chip_id == 0) {
|
||||
if (flash_chip_id == 0)
|
||||
{
|
||||
flash_chip_id = spi_flash_get_id();
|
||||
}
|
||||
return flash_chip_id;
|
||||
@ -288,7 +305,8 @@ uint32_t EspClass::getFlashChipSize(void)
|
||||
uint32_t data;
|
||||
uint8_t * bytes = (uint8_t *) &data;
|
||||
// read first 4 byte (magic byte + flash config)
|
||||
if(spi_flash_read(0x0000, &data, 4) == SPI_FLASH_RESULT_OK) {
|
||||
if (spi_flash_read(0x0000, &data, 4) == SPI_FLASH_RESULT_OK)
|
||||
{
|
||||
return magicFlashChipSize((bytes[3] & 0xf0) >> 4);
|
||||
}
|
||||
return 0;
|
||||
@ -299,7 +317,8 @@ uint32_t EspClass::getFlashChipSpeed(void)
|
||||
uint32_t data;
|
||||
uint8_t * bytes = (uint8_t *) &data;
|
||||
// read first 4 byte (magic byte + flash config)
|
||||
if(spi_flash_read(0x0000, &data, 4) == SPI_FLASH_RESULT_OK) {
|
||||
if (spi_flash_read(0x0000, &data, 4) == SPI_FLASH_RESULT_OK)
|
||||
{
|
||||
return magicFlashChipSpeed(bytes[3] & 0x0F);
|
||||
}
|
||||
return 0;
|
||||
@ -311,14 +330,17 @@ FlashMode_t EspClass::getFlashChipMode(void)
|
||||
uint32_t data;
|
||||
uint8_t * bytes = (uint8_t *) &data;
|
||||
// read first 4 byte (magic byte + flash config)
|
||||
if(spi_flash_read(0x0000, &data, 4) == SPI_FLASH_RESULT_OK) {
|
||||
if (spi_flash_read(0x0000, &data, 4) == SPI_FLASH_RESULT_OK)
|
||||
{
|
||||
mode = magicFlashChipMode(bytes[2]);
|
||||
}
|
||||
return mode;
|
||||
}
|
||||
|
||||
uint32_t EspClass::magicFlashChipSize(uint8_t byte) {
|
||||
switch(byte & 0x0F) {
|
||||
uint32_t EspClass::magicFlashChipSize(uint8_t byte)
|
||||
{
|
||||
switch (byte & 0x0F)
|
||||
{
|
||||
case 0x0: // 4 Mbit (512KB)
|
||||
return (512_kB);
|
||||
case 0x1: // 2 MBit (256KB)
|
||||
@ -338,8 +360,10 @@ uint32_t EspClass::magicFlashChipSize(uint8_t byte) {
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t EspClass::magicFlashChipSpeed(uint8_t byte) {
|
||||
switch(byte & 0x0F) {
|
||||
uint32_t EspClass::magicFlashChipSpeed(uint8_t byte)
|
||||
{
|
||||
switch (byte & 0x0F)
|
||||
{
|
||||
case 0x0: // 40 MHz
|
||||
return (40_MHz);
|
||||
case 0x1: // 26 MHz
|
||||
@ -353,30 +377,34 @@ uint32_t EspClass::magicFlashChipSpeed(uint8_t byte) {
|
||||
}
|
||||
}
|
||||
|
||||
FlashMode_t EspClass::magicFlashChipMode(uint8_t byte) {
|
||||
FlashMode_t EspClass::magicFlashChipMode(uint8_t byte)
|
||||
{
|
||||
FlashMode_t mode = (FlashMode_t) byte;
|
||||
if(mode > FM_DOUT) {
|
||||
if (mode > FM_DOUT)
|
||||
{
|
||||
mode = FM_UNKNOWN;
|
||||
}
|
||||
return mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Infos from
|
||||
* http://www.wlxmall.com/images/stock_item/att/A1010004.pdf
|
||||
* http://www.gigadevice.com/product-series/5.html?locale=en_US
|
||||
* http://www.elinux.org/images/f/f5/Winbond-w25q32.pdf
|
||||
*/
|
||||
uint32_t EspClass::getFlashChipSizeByChipId(void) {
|
||||
Infos from
|
||||
http://www.wlxmall.com/images/stock_item/att/A1010004.pdf
|
||||
http://www.gigadevice.com/product-series/5.html?locale=en_US
|
||||
http://www.elinux.org/images/f/f5/Winbond-w25q32.pdf
|
||||
*/
|
||||
uint32_t EspClass::getFlashChipSizeByChipId(void)
|
||||
{
|
||||
uint32_t chipId = getFlashChipId();
|
||||
/**
|
||||
* Chip ID
|
||||
* 00 - always 00 (Chip ID use only 3 byte)
|
||||
* 17 - ? looks like 2^xx is size in Byte ? //todo: find docu to this
|
||||
* 40 - ? may be Speed ? //todo: find docu to this
|
||||
* C8 - manufacturer ID
|
||||
Chip ID
|
||||
00 - always 00 (Chip ID use only 3 byte)
|
||||
17 - ? looks like 2^xx is size in Byte ? //todo: find docu to this
|
||||
40 - ? may be Speed ? //todo: find docu to this
|
||||
C8 - manufacturer ID
|
||||
*/
|
||||
switch(chipId) {
|
||||
switch (chipId)
|
||||
{
|
||||
|
||||
// GigaDevice
|
||||
case 0x1740C8: // GD25Q64B
|
||||
@ -422,47 +450,71 @@ uint32_t EspClass::getFlashChipSizeByChipId(void) {
|
||||
}
|
||||
|
||||
/**
|
||||
* check the Flash settings from IDE against the Real flash size
|
||||
* @param needsEquals (return only true it equals)
|
||||
* @return ok or not
|
||||
*/
|
||||
bool EspClass::checkFlashConfig(bool needsEquals) {
|
||||
if(needsEquals) {
|
||||
if(getFlashChipRealSize() == getFlashChipSize()) {
|
||||
check the Flash settings from IDE against the Real flash size
|
||||
@param needsEquals (return only true it equals)
|
||||
@return ok or not
|
||||
*/
|
||||
bool EspClass::checkFlashConfig(bool needsEquals)
|
||||
{
|
||||
if (needsEquals)
|
||||
{
|
||||
if (getFlashChipRealSize() == getFlashChipSize())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if(getFlashChipRealSize() >= getFlashChipSize()) {
|
||||
}
|
||||
else
|
||||
{
|
||||
if (getFlashChipRealSize() >= getFlashChipSize())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
String EspClass::getResetReason(void) {
|
||||
String EspClass::getResetReason(void)
|
||||
{
|
||||
char buff[32];
|
||||
if (resetInfo.reason == REASON_DEFAULT_RST) { // normal startup by power on
|
||||
if (resetInfo.reason == REASON_DEFAULT_RST) // normal startup by power on
|
||||
{
|
||||
strcpy_P(buff, PSTR("Power on"));
|
||||
} else if (resetInfo.reason == REASON_WDT_RST) { // hardware watch dog reset
|
||||
}
|
||||
else if (resetInfo.reason == REASON_WDT_RST) // hardware watch dog reset
|
||||
{
|
||||
strcpy_P(buff, PSTR("Hardware Watchdog"));
|
||||
} else if (resetInfo.reason == REASON_EXCEPTION_RST) { // exception reset, GPIO status won’t change
|
||||
}
|
||||
else if (resetInfo.reason == REASON_EXCEPTION_RST) // exception reset, GPIO status won’t change
|
||||
{
|
||||
strcpy_P(buff, PSTR("Exception"));
|
||||
} else if (resetInfo.reason == REASON_SOFT_WDT_RST) { // software watch dog reset, GPIO status won’t change
|
||||
}
|
||||
else if (resetInfo.reason == REASON_SOFT_WDT_RST) // software watch dog reset, GPIO status won’t change
|
||||
{
|
||||
strcpy_P(buff, PSTR("Software Watchdog"));
|
||||
} else if (resetInfo.reason == REASON_SOFT_RESTART) { // software restart ,system_restart , GPIO status won’t change
|
||||
}
|
||||
else if (resetInfo.reason == REASON_SOFT_RESTART) // software restart ,system_restart , GPIO status won’t change
|
||||
{
|
||||
strcpy_P(buff, PSTR("Software/System restart"));
|
||||
} else if (resetInfo.reason == REASON_DEEP_SLEEP_AWAKE) { // wake up from deep-sleep
|
||||
}
|
||||
else if (resetInfo.reason == REASON_DEEP_SLEEP_AWAKE) // wake up from deep-sleep
|
||||
{
|
||||
strcpy_P(buff, PSTR("Deep-Sleep Wake"));
|
||||
} else if (resetInfo.reason == REASON_EXT_SYS_RST) { // external system reset
|
||||
}
|
||||
else if (resetInfo.reason == REASON_EXT_SYS_RST) // external system reset
|
||||
{
|
||||
strcpy_P(buff, PSTR("External System"));
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
strcpy_P(buff, PSTR("Unknown"));
|
||||
}
|
||||
return String(buff);
|
||||
}
|
||||
|
||||
String EspClass::getResetInfo(void) {
|
||||
if(resetInfo.reason != 0) {
|
||||
String EspClass::getResetInfo(void)
|
||||
{
|
||||
if (resetInfo.reason != 0)
|
||||
{
|
||||
char buff[200];
|
||||
sprintf(&buff[0], "Fatal exception:%d flag:%d (%s) epc1:0x%08x epc2:0x%08x epc3:0x%08x excvaddr:0x%08x depc:0x%08x", resetInfo.exccause, resetInfo.reason, (resetInfo.reason == 0 ? "DEFAULT" : resetInfo.reason == 1 ? "WDT" : resetInfo.reason == 2 ? "EXCEPTION" : resetInfo.reason == 3 ? "SOFT_WDT" : resetInfo.reason == 4 ? "SOFT_RESTART" : resetInfo.reason == 5 ? "DEEP_SLEEP_AWAKE" : resetInfo.reason == 6 ? "EXT_SYS_RST" : "???"), resetInfo.epc1, resetInfo.epc2, resetInfo.epc3, resetInfo.excvaddr, resetInfo.depc);
|
||||
return String(buff);
|
||||
@ -470,16 +522,20 @@ String EspClass::getResetInfo(void) {
|
||||
return String("flag: 0");
|
||||
}
|
||||
|
||||
struct rst_info * EspClass::getResetInfoPtr(void) {
|
||||
struct rst_info * EspClass::getResetInfoPtr(void)
|
||||
{
|
||||
return &resetInfo;
|
||||
}
|
||||
|
||||
bool EspClass::eraseConfig(void) {
|
||||
bool EspClass::eraseConfig(void)
|
||||
{
|
||||
const size_t cfgSize = 0x4000;
|
||||
size_t cfgAddr = ESP.getFlashChipSize() - cfgSize;
|
||||
|
||||
for (size_t offset = 0; offset < cfgSize; offset += SPI_FLASH_SEC_SIZE) {
|
||||
if (!flashEraseSector((cfgAddr + offset) / SPI_FLASH_SEC_SIZE)) {
|
||||
for (size_t offset = 0; offset < cfgSize; offset += SPI_FLASH_SEC_SIZE)
|
||||
{
|
||||
if (!flashEraseSector((cfgAddr + offset) / SPI_FLASH_SEC_SIZE))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -487,14 +543,18 @@ bool EspClass::eraseConfig(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t EspClass::getSketchSize() {
|
||||
uint32_t EspClass::getSketchSize()
|
||||
{
|
||||
static uint32_t result = 0;
|
||||
if (result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
image_header_t image_header;
|
||||
uint32_t pos = APP_START_OFFSET;
|
||||
if (spi_flash_read(pos, (uint32_t*) &image_header, sizeof(image_header))) {
|
||||
if (spi_flash_read(pos, (uint32_t*) &image_header, sizeof(image_header)))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
pos += sizeof(image_header);
|
||||
@ -506,7 +566,8 @@ uint32_t EspClass::getSketchSize() {
|
||||
++section_index)
|
||||
{
|
||||
section_header_t section_header = {0, 0};
|
||||
if (spi_flash_read(pos, (uint32_t*) §ion_header, sizeof(section_header))) {
|
||||
if (spi_flash_read(pos, (uint32_t*) §ion_header, sizeof(section_header)))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
pos += sizeof(section_header);
|
||||
@ -521,7 +582,8 @@ uint32_t EspClass::getSketchSize() {
|
||||
|
||||
extern "C" uint32_t _SPIFFS_start;
|
||||
|
||||
uint32_t EspClass::getFreeSketchSpace() {
|
||||
uint32_t EspClass::getFreeSketchSpace()
|
||||
{
|
||||
|
||||
uint32_t usedSize = getSketchSize();
|
||||
// round one sector up
|
||||
@ -534,51 +596,70 @@ uint32_t EspClass::getFreeSketchSpace() {
|
||||
return freeSpaceEnd - freeSpaceStart;
|
||||
}
|
||||
|
||||
bool EspClass::updateSketch(Stream& in, uint32_t size, bool restartOnFail, bool restartOnSuccess) {
|
||||
if(!Update.begin(size)){
|
||||
bool EspClass::updateSketch(Stream& in, uint32_t size, bool restartOnFail, bool restartOnSuccess)
|
||||
{
|
||||
if (!Update.begin(size))
|
||||
{
|
||||
#ifdef DEBUG_SERIAL
|
||||
DEBUG_SERIAL.print("Update ");
|
||||
Update.printError(DEBUG_SERIAL);
|
||||
#endif
|
||||
if(restartOnFail) ESP.restart();
|
||||
if (restartOnFail)
|
||||
{
|
||||
ESP.restart();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if(Update.writeStream(in) != size){
|
||||
if (Update.writeStream(in) != size)
|
||||
{
|
||||
#ifdef DEBUG_SERIAL
|
||||
DEBUG_SERIAL.print("Update ");
|
||||
Update.printError(DEBUG_SERIAL);
|
||||
#endif
|
||||
if(restartOnFail) ESP.restart();
|
||||
if (restartOnFail)
|
||||
{
|
||||
ESP.restart();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!Update.end()){
|
||||
if (!Update.end())
|
||||
{
|
||||
#ifdef DEBUG_SERIAL
|
||||
DEBUG_SERIAL.print("Update ");
|
||||
Update.printError(DEBUG_SERIAL);
|
||||
#endif
|
||||
if(restartOnFail) ESP.restart();
|
||||
if (restartOnFail)
|
||||
{
|
||||
ESP.restart();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_SERIAL
|
||||
DEBUG_SERIAL.println("Update SUCCESS");
|
||||
#endif
|
||||
if(restartOnSuccess) ESP.restart();
|
||||
if (restartOnSuccess)
|
||||
{
|
||||
ESP.restart();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static const int FLASH_INT_MASK = ((B10 << 8) | B00111010);
|
||||
|
||||
bool EspClass::flashEraseSector(uint32_t sector) {
|
||||
bool EspClass::flashEraseSector(uint32_t sector)
|
||||
{
|
||||
int rc = spi_flash_erase_sector(sector);
|
||||
return rc == 0;
|
||||
}
|
||||
|
||||
#if PUYA_SUPPORT
|
||||
static int spi_flash_write_puya(uint32_t offset, uint32_t *data, size_t size) {
|
||||
if (data == nullptr) {
|
||||
static int spi_flash_write_puya(uint32_t offset, uint32_t *data, size_t size)
|
||||
{
|
||||
if (data == nullptr)
|
||||
{
|
||||
return 1; // SPI_FLASH_RESULT_ERR
|
||||
}
|
||||
// PUYA flash chips need to read existing data, update in memory and write modified data again.
|
||||
@ -586,29 +667,37 @@ static int spi_flash_write_puya(uint32_t offset, uint32_t *data, size_t size) {
|
||||
int rc = 0;
|
||||
uint32_t* ptr = data;
|
||||
|
||||
if (flash_write_puya_buf == nullptr) {
|
||||
if (flash_write_puya_buf == nullptr)
|
||||
{
|
||||
flash_write_puya_buf = (uint32_t*) malloc(PUYA_BUFFER_SIZE);
|
||||
// No need to ever free this, since the flash chip will never change at runtime.
|
||||
if (flash_write_puya_buf == nullptr) {
|
||||
if (flash_write_puya_buf == nullptr)
|
||||
{
|
||||
// Memory could not be allocated.
|
||||
return 1; // SPI_FLASH_RESULT_ERR
|
||||
}
|
||||
}
|
||||
size_t bytesLeft = size;
|
||||
uint32_t pos = offset;
|
||||
while (bytesLeft > 0 && rc == 0) {
|
||||
while (bytesLeft > 0 && rc == 0)
|
||||
{
|
||||
size_t bytesNow = bytesLeft;
|
||||
if (bytesNow > PUYA_BUFFER_SIZE) {
|
||||
if (bytesNow > PUYA_BUFFER_SIZE)
|
||||
{
|
||||
bytesNow = PUYA_BUFFER_SIZE;
|
||||
bytesLeft -= PUYA_BUFFER_SIZE;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
bytesLeft = 0;
|
||||
}
|
||||
rc = spi_flash_read(pos, flash_write_puya_buf, bytesNow);
|
||||
if (rc != 0) {
|
||||
if (rc != 0)
|
||||
{
|
||||
return rc;
|
||||
}
|
||||
for (size_t i = 0; i < bytesNow / 4; ++i) {
|
||||
for (size_t i = 0; i < bytesNow / 4; ++i)
|
||||
{
|
||||
flash_write_puya_buf[i] &= *ptr;
|
||||
++ptr;
|
||||
}
|
||||
@ -619,10 +708,12 @@ static int spi_flash_write_puya(uint32_t offset, uint32_t *data, size_t size) {
|
||||
}
|
||||
#endif
|
||||
|
||||
bool EspClass::flashWrite(uint32_t offset, uint32_t *data, size_t size) {
|
||||
bool EspClass::flashWrite(uint32_t offset, uint32_t *data, size_t size)
|
||||
{
|
||||
int rc = 0;
|
||||
#if PUYA_SUPPORT
|
||||
if (getFlashChipVendorId() == SPI_FLASH_VENDOR_PUYA) {
|
||||
if (getFlashChipVendorId() == SPI_FLASH_VENDOR_PUYA)
|
||||
{
|
||||
rc = spi_flash_write_puya(offset, data, size);
|
||||
}
|
||||
else
|
||||
@ -633,7 +724,8 @@ bool EspClass::flashWrite(uint32_t offset, uint32_t *data, size_t size) {
|
||||
return rc == 0;
|
||||
}
|
||||
|
||||
bool EspClass::flashRead(uint32_t offset, uint32_t *data, size_t size) {
|
||||
bool EspClass::flashRead(uint32_t offset, uint32_t *data, size_t size)
|
||||
{
|
||||
int rc = spi_flash_read(offset, (uint32_t*) data, size);
|
||||
return rc == 0;
|
||||
}
|
||||
@ -641,21 +733,25 @@ bool EspClass::flashRead(uint32_t offset, uint32_t *data, size_t size) {
|
||||
String EspClass::getSketchMD5()
|
||||
{
|
||||
static String result;
|
||||
if (result.length()) {
|
||||
if (result.length())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
uint32_t lengthLeft = getSketchSize();
|
||||
const size_t bufSize = 512;
|
||||
std::unique_ptr<uint8_t[]> buf(new uint8_t[bufSize]);
|
||||
uint32_t offset = 0;
|
||||
if(!buf.get()) {
|
||||
if (!buf.get())
|
||||
{
|
||||
return String();
|
||||
}
|
||||
MD5Builder md5;
|
||||
md5.begin();
|
||||
while( lengthLeft > 0) {
|
||||
while (lengthLeft > 0)
|
||||
{
|
||||
size_t readBytes = (lengthLeft < bufSize) ? lengthLeft : bufSize;
|
||||
if (!flashRead(offset, reinterpret_cast<uint32_t*>(buf.get()), (readBytes + 3) & ~3)) {
|
||||
if (!flashRead(offset, reinterpret_cast<uint32_t*>(buf.get()), (readBytes + 3) & ~3))
|
||||
{
|
||||
return String();
|
||||
}
|
||||
md5.add(buf.get(), readBytes);
|
||||
|
@ -16,7 +16,7 @@
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
*/
|
||||
|
||||
#ifndef ESP_H
|
||||
#define ESP_H
|
||||
@ -24,17 +24,18 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
#ifndef PUYA_SUPPORT
|
||||
#define PUYA_SUPPORT 0
|
||||
#define PUYA_SUPPORT 0
|
||||
#endif
|
||||
#ifndef PUYA_BUFFER_SIZE
|
||||
// Good alternative for buffer size is: SPI_FLASH_SEC_SIZE (= 4k)
|
||||
// Always use a multiple of flash page size (256 bytes)
|
||||
#define PUYA_BUFFER_SIZE 256
|
||||
// Good alternative for buffer size is: SPI_FLASH_SEC_SIZE (= 4k)
|
||||
// Always use a multiple of flash page size (256 bytes)
|
||||
#define PUYA_BUFFER_SIZE 256
|
||||
#endif
|
||||
|
||||
// Vendor IDs taken from Flashrom project
|
||||
// https://review.coreboot.org/cgit/flashrom.git/tree/flashchips.h?h=1.0.x
|
||||
typedef enum {
|
||||
typedef enum
|
||||
{
|
||||
SPI_FLASH_VENDOR_ALLIANCE = 0x52, /* Alliance Semiconductor */
|
||||
SPI_FLASH_VENDOR_AMD = 0x01, /* AMD */
|
||||
SPI_FLASH_VENDOR_AMIC = 0x37, /* AMIC */
|
||||
@ -70,9 +71,10 @@ typedef enum {
|
||||
} SPI_FLASH_VENDOR_t;
|
||||
|
||||
/**
|
||||
* AVR macros for WDT managment
|
||||
*/
|
||||
typedef enum {
|
||||
AVR macros for WDT managment
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
WDTO_0MS = 0, //!< WDTO_0MS
|
||||
WDTO_15MS = 15, //!< WDTO_15MS
|
||||
WDTO_30MS = 30, //!< WDTO_30MS
|
||||
@ -94,7 +96,8 @@ typedef enum {
|
||||
#define cli() ets_intr_lock() // IRQ Disable
|
||||
#define sei() ets_intr_unlock() // IRQ Enable
|
||||
|
||||
enum RFMode {
|
||||
enum RFMode
|
||||
{
|
||||
RF_DEFAULT = 0, // RF_CAL or not after deep-sleep wake up, depends on init data byte 108.
|
||||
RF_CAL = 1, // RF_CAL after deep-sleep wake up, there will be large current.
|
||||
RF_NO_CAL = 2, // no RF_CAL after deep-sleep wake up, there will only be small current.
|
||||
@ -111,7 +114,8 @@ enum RFMode {
|
||||
#define WAKE_NO_RFCAL RF_NO_CAL
|
||||
#define WAKE_RF_DISABLED RF_DISABLED
|
||||
|
||||
enum ADCMode {
|
||||
enum ADCMode
|
||||
{
|
||||
ADC_TOUT = 33,
|
||||
ADC_TOUT_3V3 = 33,
|
||||
ADC_VCC = 255,
|
||||
@ -120,7 +124,8 @@ enum ADCMode {
|
||||
|
||||
#define ADC_MODE(mode) int __get_adc_mode(void) { return (int) (mode); }
|
||||
|
||||
typedef enum {
|
||||
typedef enum
|
||||
{
|
||||
FM_QIO = 0x00,
|
||||
FM_QOUT = 0x01,
|
||||
FM_DIO = 0x02,
|
||||
@ -128,8 +133,9 @@ typedef enum {
|
||||
FM_UNKNOWN = 0xff
|
||||
} FlashMode_t;
|
||||
|
||||
class EspClass {
|
||||
public:
|
||||
class EspClass
|
||||
{
|
||||
public:
|
||||
// TODO: figure out how to set WDT timeout
|
||||
void wdtEnable(uint32_t timeout_ms = 0);
|
||||
// note: setting the timeout value is not implemented at the moment
|
||||
@ -210,7 +216,7 @@ class EspClass {
|
||||
uint32_t EspClass::getCycleCount()
|
||||
{
|
||||
uint32_t ccount;
|
||||
__asm__ __volatile__("esync; rsr %0,ccount":"=a" (ccount));
|
||||
__asm__ __volatile__("esync; rsr %0,ccount":"=a"(ccount));
|
||||
return ccount;
|
||||
}
|
||||
#endif
|
||||
|
@ -16,7 +16,7 @@
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
*/
|
||||
|
||||
#include "FS.h"
|
||||
#include "FSImpl.h"
|
||||
@ -25,49 +25,68 @@ using namespace fs;
|
||||
|
||||
static bool sflags(const char* mode, OpenMode& om, AccessMode& am);
|
||||
|
||||
size_t File::write(uint8_t c) {
|
||||
size_t File::write(uint8_t c)
|
||||
{
|
||||
if (!_p)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _p->write(&c, 1);
|
||||
}
|
||||
|
||||
size_t File::write(const uint8_t *buf, size_t size) {
|
||||
size_t File::write(const uint8_t *buf, size_t size)
|
||||
{
|
||||
if (!_p)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _p->write(buf, size);
|
||||
}
|
||||
|
||||
int File::available() {
|
||||
int File::available()
|
||||
{
|
||||
if (!_p)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return _p->size() - _p->position();
|
||||
}
|
||||
|
||||
int File::read() {
|
||||
int File::read()
|
||||
{
|
||||
if (!_p)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint8_t result;
|
||||
if (_p->read(&result, 1) != 1) {
|
||||
if (_p->read(&result, 1) != 1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t File::read(uint8_t* buf, size_t size) {
|
||||
size_t File::read(uint8_t* buf, size_t size)
|
||||
{
|
||||
if (!_p)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return _p->read(buf, size);
|
||||
}
|
||||
|
||||
int File::peek() {
|
||||
int File::peek()
|
||||
{
|
||||
if (!_p)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t curPos = _p->position();
|
||||
int result = read();
|
||||
@ -75,90 +94,126 @@ int File::peek() {
|
||||
return result;
|
||||
}
|
||||
|
||||
void File::flush() {
|
||||
void File::flush()
|
||||
{
|
||||
if (!_p)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_p->flush();
|
||||
}
|
||||
|
||||
bool File::seek(uint32_t pos, SeekMode mode) {
|
||||
bool File::seek(uint32_t pos, SeekMode mode)
|
||||
{
|
||||
if (!_p)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return _p->seek(pos, mode);
|
||||
}
|
||||
|
||||
size_t File::position() const {
|
||||
size_t File::position() const
|
||||
{
|
||||
if (!_p)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _p->position();
|
||||
}
|
||||
|
||||
size_t File::size() const {
|
||||
size_t File::size() const
|
||||
{
|
||||
if (!_p)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _p->size();
|
||||
}
|
||||
|
||||
void File::close() {
|
||||
if (_p) {
|
||||
void File::close()
|
||||
{
|
||||
if (_p)
|
||||
{
|
||||
_p->close();
|
||||
_p = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
File::operator bool() const {
|
||||
File::operator bool() const
|
||||
{
|
||||
return !!_p;
|
||||
}
|
||||
|
||||
bool File::truncate(uint32_t size) {
|
||||
bool File::truncate(uint32_t size)
|
||||
{
|
||||
if (!_p)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return _p->truncate(size);
|
||||
}
|
||||
|
||||
const char* File::name() const {
|
||||
const char* File::name() const
|
||||
{
|
||||
if (!_p)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return _p->name();
|
||||
}
|
||||
|
||||
const char* File::fullName() const {
|
||||
const char* File::fullName() const
|
||||
{
|
||||
if (!_p)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return _p->fullName();
|
||||
}
|
||||
|
||||
bool File::isFile() const {
|
||||
bool File::isFile() const
|
||||
{
|
||||
if (!_p)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return _p->isFile();
|
||||
}
|
||||
|
||||
bool File::isDirectory() const {
|
||||
bool File::isDirectory() const
|
||||
{
|
||||
if (!_p)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return _p->isDirectory();
|
||||
}
|
||||
|
||||
void File::rewindDirectory() {
|
||||
if (!_fakeDir) {
|
||||
void File::rewindDirectory()
|
||||
{
|
||||
if (!_fakeDir)
|
||||
{
|
||||
_fakeDir = std::make_shared<Dir>(_baseFS->openDir(fullName()));
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
_fakeDir->rewind();
|
||||
}
|
||||
}
|
||||
|
||||
File File::openNextFile() {
|
||||
if (!_fakeDir) {
|
||||
File File::openNextFile()
|
||||
{
|
||||
if (!_fakeDir)
|
||||
{
|
||||
_fakeDir = std::make_shared<Dir>(_baseFS->openDir(fullName()));
|
||||
}
|
||||
_fakeDir->next();
|
||||
@ -169,25 +224,28 @@ String File::readString()
|
||||
{
|
||||
String ret;
|
||||
ret.reserve(size() - position());
|
||||
char temp[256+1];
|
||||
int countRead = readBytes(temp, sizeof(temp)-1);
|
||||
char temp[256 + 1];
|
||||
int countRead = readBytes(temp, sizeof(temp) - 1);
|
||||
while (countRead > 0)
|
||||
{
|
||||
temp[countRead] = 0;
|
||||
ret += temp;
|
||||
countRead = readBytes(temp, sizeof(temp)-1);
|
||||
countRead = readBytes(temp, sizeof(temp) - 1);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
File Dir::openFile(const char* mode) {
|
||||
if (!_impl) {
|
||||
File Dir::openFile(const char* mode)
|
||||
{
|
||||
if (!_impl)
|
||||
{
|
||||
return File();
|
||||
}
|
||||
|
||||
OpenMode om;
|
||||
AccessMode am;
|
||||
if (!sflags(mode, om, am)) {
|
||||
if (!sflags(mode, om, am))
|
||||
{
|
||||
DEBUGV("Dir::openFile: invalid mode `%s`\r\n", mode);
|
||||
return File();
|
||||
}
|
||||
@ -195,201 +253,252 @@ File Dir::openFile(const char* mode) {
|
||||
return File(_impl->openFile(om, am), _baseFS);
|
||||
}
|
||||
|
||||
String Dir::fileName() {
|
||||
if (!_impl) {
|
||||
String Dir::fileName()
|
||||
{
|
||||
if (!_impl)
|
||||
{
|
||||
return String();
|
||||
}
|
||||
|
||||
return _impl->fileName();
|
||||
}
|
||||
|
||||
size_t Dir::fileSize() {
|
||||
if (!_impl) {
|
||||
size_t Dir::fileSize()
|
||||
{
|
||||
if (!_impl)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _impl->fileSize();
|
||||
}
|
||||
|
||||
bool Dir::isFile() const {
|
||||
bool Dir::isFile() const
|
||||
{
|
||||
if (!_impl)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return _impl->isFile();
|
||||
}
|
||||
|
||||
bool Dir::isDirectory() const {
|
||||
bool Dir::isDirectory() const
|
||||
{
|
||||
if (!_impl)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return _impl->isDirectory();
|
||||
}
|
||||
|
||||
bool Dir::next() {
|
||||
if (!_impl) {
|
||||
bool Dir::next()
|
||||
{
|
||||
if (!_impl)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return _impl->next();
|
||||
}
|
||||
|
||||
bool Dir::rewind() {
|
||||
if (!_impl) {
|
||||
bool Dir::rewind()
|
||||
{
|
||||
if (!_impl)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return _impl->rewind();
|
||||
}
|
||||
|
||||
bool FS::setConfig(const FSConfig &cfg) {
|
||||
if (!_impl) {
|
||||
bool FS::setConfig(const FSConfig &cfg)
|
||||
{
|
||||
if (!_impl)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return _impl->setConfig(cfg);
|
||||
}
|
||||
|
||||
bool FS::begin() {
|
||||
if (!_impl) {
|
||||
bool FS::begin()
|
||||
{
|
||||
if (!_impl)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return _impl->begin();
|
||||
}
|
||||
|
||||
void FS::end() {
|
||||
if (_impl) {
|
||||
void FS::end()
|
||||
{
|
||||
if (_impl)
|
||||
{
|
||||
_impl->end();
|
||||
}
|
||||
}
|
||||
|
||||
bool FS::gc() {
|
||||
if (!_impl) {
|
||||
bool FS::gc()
|
||||
{
|
||||
if (!_impl)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return _impl->gc();
|
||||
}
|
||||
|
||||
bool FS::format() {
|
||||
if (!_impl) {
|
||||
bool FS::format()
|
||||
{
|
||||
if (!_impl)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return _impl->format();
|
||||
}
|
||||
|
||||
bool FS::info(FSInfo& info){
|
||||
if (!_impl) {
|
||||
bool FS::info(FSInfo& info)
|
||||
{
|
||||
if (!_impl)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return _impl->info(info);
|
||||
}
|
||||
|
||||
File FS::open(const String& path, const char* mode) {
|
||||
File FS::open(const String& path, const char* mode)
|
||||
{
|
||||
return open(path.c_str(), mode);
|
||||
}
|
||||
|
||||
File FS::open(const char* path, const char* mode) {
|
||||
if (!_impl) {
|
||||
File FS::open(const char* path, const char* mode)
|
||||
{
|
||||
if (!_impl)
|
||||
{
|
||||
return File();
|
||||
}
|
||||
|
||||
OpenMode om;
|
||||
AccessMode am;
|
||||
if (!sflags(mode, om, am)) {
|
||||
if (!sflags(mode, om, am))
|
||||
{
|
||||
DEBUGV("FS::open: invalid mode `%s`\r\n", mode);
|
||||
return File();
|
||||
}
|
||||
return File(_impl->open(path, om, am), this);
|
||||
}
|
||||
|
||||
bool FS::exists(const char* path) {
|
||||
if (!_impl) {
|
||||
bool FS::exists(const char* path)
|
||||
{
|
||||
if (!_impl)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return _impl->exists(path);
|
||||
}
|
||||
|
||||
bool FS::exists(const String& path) {
|
||||
bool FS::exists(const String& path)
|
||||
{
|
||||
return exists(path.c_str());
|
||||
}
|
||||
|
||||
Dir FS::openDir(const char* path) {
|
||||
if (!_impl) {
|
||||
Dir FS::openDir(const char* path)
|
||||
{
|
||||
if (!_impl)
|
||||
{
|
||||
return Dir();
|
||||
}
|
||||
DirImplPtr p = _impl->openDir(path);
|
||||
return Dir(p, this);
|
||||
}
|
||||
|
||||
Dir FS::openDir(const String& path) {
|
||||
Dir FS::openDir(const String& path)
|
||||
{
|
||||
return openDir(path.c_str());
|
||||
}
|
||||
|
||||
bool FS::remove(const char* path) {
|
||||
if (!_impl) {
|
||||
bool FS::remove(const char* path)
|
||||
{
|
||||
if (!_impl)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return _impl->remove(path);
|
||||
}
|
||||
|
||||
bool FS::remove(const String& path) {
|
||||
bool FS::remove(const String& path)
|
||||
{
|
||||
return remove(path.c_str());
|
||||
}
|
||||
|
||||
bool FS::rmdir(const char* path) {
|
||||
if (!_impl) {
|
||||
bool FS::rmdir(const char* path)
|
||||
{
|
||||
if (!_impl)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return _impl->rmdir(path);
|
||||
}
|
||||
|
||||
bool FS::rmdir(const String& path) {
|
||||
bool FS::rmdir(const String& path)
|
||||
{
|
||||
return rmdir(path.c_str());
|
||||
}
|
||||
|
||||
bool FS::mkdir(const char* path) {
|
||||
if (!_impl) {
|
||||
bool FS::mkdir(const char* path)
|
||||
{
|
||||
if (!_impl)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return _impl->mkdir(path);
|
||||
}
|
||||
|
||||
bool FS::mkdir(const String& path) {
|
||||
bool FS::mkdir(const String& path)
|
||||
{
|
||||
return mkdir(path.c_str());
|
||||
}
|
||||
|
||||
bool FS::rename(const char* pathFrom, const char* pathTo) {
|
||||
if (!_impl) {
|
||||
bool FS::rename(const char* pathFrom, const char* pathTo)
|
||||
{
|
||||
if (!_impl)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return _impl->rename(pathFrom, pathTo);
|
||||
}
|
||||
|
||||
bool FS::rename(const String& pathFrom, const String& pathTo) {
|
||||
bool FS::rename(const String& pathFrom, const String& pathTo)
|
||||
{
|
||||
return rename(pathFrom.c_str(), pathTo.c_str());
|
||||
}
|
||||
|
||||
|
||||
|
||||
static bool sflags(const char* mode, OpenMode& om, AccessMode& am) {
|
||||
switch (mode[0]) {
|
||||
static bool sflags(const char* mode, OpenMode& om, AccessMode& am)
|
||||
{
|
||||
switch (mode[0])
|
||||
{
|
||||
case 'r':
|
||||
am = AM_READ;
|
||||
om = OM_DEFAULT;
|
||||
break;
|
||||
case 'w':
|
||||
am = AM_WRITE;
|
||||
om = (OpenMode) (OM_CREATE | OM_TRUNCATE);
|
||||
om = (OpenMode)(OM_CREATE | OM_TRUNCATE);
|
||||
break;
|
||||
case 'a':
|
||||
am = AM_WRITE;
|
||||
om = (OpenMode) (OM_CREATE | OM_APPEND);
|
||||
om = (OpenMode)(OM_CREATE | OM_APPEND);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
switch(mode[1]) {
|
||||
switch (mode[1])
|
||||
{
|
||||
case '+':
|
||||
am = (AccessMode) (AM_WRITE | AM_READ);
|
||||
am = (AccessMode)(AM_WRITE | AM_READ);
|
||||
break;
|
||||
case 0:
|
||||
break;
|
||||
@ -403,7 +512,7 @@ static bool sflags(const char* mode, OpenMode& om, AccessMode& am) {
|
||||
#if defined(FS_FREESTANDING_FUNCTIONS)
|
||||
|
||||
/*
|
||||
TODO: move these functions to public API:
|
||||
TODO: move these functions to public API:
|
||||
*/
|
||||
File open(const char* path, const char* mode);
|
||||
File open(String& path, const char* mode);
|
||||
@ -417,7 +526,8 @@ bool mount<FS>(FS& fs, const char* mountPoint);
|
||||
*/
|
||||
|
||||
|
||||
struct MountEntry {
|
||||
struct MountEntry
|
||||
{
|
||||
FSImplPtr fs;
|
||||
String path;
|
||||
MountEntry* next;
|
||||
@ -426,9 +536,11 @@ struct MountEntry {
|
||||
static MountEntry* s_mounted = nullptr;
|
||||
|
||||
template<>
|
||||
bool mount<FS>(FS& fs, const char* mountPoint) {
|
||||
bool mount<FS>(FS& fs, const char* mountPoint)
|
||||
{
|
||||
FSImplPtr p = fs._impl;
|
||||
if (!p || !p->mount()) {
|
||||
if (!p || !p->mount())
|
||||
{
|
||||
DEBUGV("FSImpl mount failed\r\n");
|
||||
return false;
|
||||
}
|
||||
@ -447,31 +559,39 @@ bool mount<FS>(FS& fs, const char* mountPoint) {
|
||||
/*
|
||||
iterate over MountEntries and look for the ones which match the path
|
||||
*/
|
||||
File open(const char* path, const char* mode) {
|
||||
File open(const char* path, const char* mode)
|
||||
{
|
||||
OpenMode om;
|
||||
AccessMode am;
|
||||
if (!sflags(mode, om, am)) {
|
||||
if (!sflags(mode, om, am))
|
||||
{
|
||||
DEBUGV("open: invalid mode `%s`\r\n", mode);
|
||||
return File();
|
||||
}
|
||||
|
||||
for (MountEntry* entry = s_mounted; entry; entry = entry->next) {
|
||||
for (MountEntry* entry = s_mounted; entry; entry = entry->next)
|
||||
{
|
||||
size_t offset = entry->path.length();
|
||||
if (strstr(path, entry->path.c_str())) {
|
||||
if (strstr(path, entry->path.c_str()))
|
||||
{
|
||||
File result = entry->fs->open(path + offset);
|
||||
if (result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return File();
|
||||
}
|
||||
|
||||
File open(const String& path, const char* mode) {
|
||||
File open(const String& path, const char* mode)
|
||||
{
|
||||
return FS::open(path.c_str(), mode);
|
||||
}
|
||||
|
||||
Dir openDir(const String& path) {
|
||||
Dir openDir(const String& path)
|
||||
{
|
||||
return openDir(path.c_str());
|
||||
}
|
||||
#endif
|
||||
|
@ -16,7 +16,7 @@
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
*/
|
||||
|
||||
#ifndef FS_H
|
||||
#define FS_H
|
||||
@ -24,7 +24,8 @@
|
||||
#include <memory>
|
||||
#include <Arduino.h>
|
||||
|
||||
namespace fs {
|
||||
namespace fs
|
||||
{
|
||||
|
||||
class File;
|
||||
class Dir;
|
||||
@ -40,7 +41,8 @@ typedef std::shared_ptr<DirImpl> DirImplPtr;
|
||||
template <typename Tfs>
|
||||
bool mount(Tfs& fs, const char* mountPoint);
|
||||
|
||||
enum SeekMode {
|
||||
enum SeekMode
|
||||
{
|
||||
SeekSet = 0,
|
||||
SeekCur = 1,
|
||||
SeekEnd = 2
|
||||
@ -60,12 +62,14 @@ public:
|
||||
int read() override;
|
||||
int peek() override;
|
||||
void flush() override;
|
||||
size_t readBytes(char *buffer, size_t length) override {
|
||||
size_t readBytes(char *buffer, size_t length) override
|
||||
{
|
||||
return read((uint8_t*)buffer, length);
|
||||
}
|
||||
size_t read(uint8_t* buf, size_t size);
|
||||
bool seek(uint32_t pos, SeekMode mode);
|
||||
bool seek(uint32_t pos) {
|
||||
bool seek(uint32_t pos)
|
||||
{
|
||||
return seek(pos, SeekSet);
|
||||
}
|
||||
size_t position() const;
|
||||
@ -80,17 +84,20 @@ public:
|
||||
bool isDirectory() const;
|
||||
|
||||
// Arduino "class SD" methods for compatibility
|
||||
template<typename T> size_t write(T &src){
|
||||
template<typename T> size_t write(T &src)
|
||||
{
|
||||
uint8_t obuf[256];
|
||||
size_t doneLen = 0;
|
||||
size_t sentLen;
|
||||
int i;
|
||||
|
||||
while (src.available() > sizeof(obuf)){
|
||||
while (src.available() > sizeof(obuf))
|
||||
{
|
||||
src.read(obuf, sizeof(obuf));
|
||||
sentLen = write(obuf, sizeof(obuf));
|
||||
doneLen = doneLen + sentLen;
|
||||
if(sentLen != sizeof(obuf)){
|
||||
if (sentLen != sizeof(obuf))
|
||||
{
|
||||
return doneLen;
|
||||
}
|
||||
}
|
||||
@ -116,7 +123,8 @@ protected:
|
||||
FS *_baseFS;
|
||||
};
|
||||
|
||||
class Dir {
|
||||
class Dir
|
||||
{
|
||||
public:
|
||||
Dir(DirImplPtr impl = DirImplPtr(), FS *baseFS = nullptr): _impl(impl), _baseFS(baseFS) { }
|
||||
|
||||
@ -135,7 +143,8 @@ protected:
|
||||
FS *_baseFS;
|
||||
};
|
||||
|
||||
struct FSInfo {
|
||||
struct FSInfo
|
||||
{
|
||||
size_t totalBytes;
|
||||
size_t usedBytes;
|
||||
size_t blockSize;
|
||||
@ -147,13 +156,15 @@ struct FSInfo {
|
||||
class FSConfig
|
||||
{
|
||||
public:
|
||||
FSConfig(bool autoFormat = true) {
|
||||
FSConfig(bool autoFormat = true)
|
||||
{
|
||||
_type = FSConfig::fsid::FSId;
|
||||
_autoFormat = autoFormat;
|
||||
}
|
||||
|
||||
enum fsid { FSId = 0x00000000 };
|
||||
FSConfig setAutoFormat(bool val = true) {
|
||||
FSConfig setAutoFormat(bool val = true)
|
||||
{
|
||||
_autoFormat = val;
|
||||
return *this;
|
||||
}
|
||||
@ -165,7 +176,8 @@ public:
|
||||
class SPIFFSConfig : public FSConfig
|
||||
{
|
||||
public:
|
||||
SPIFFSConfig(bool autoFormat = true) {
|
||||
SPIFFSConfig(bool autoFormat = true)
|
||||
{
|
||||
_type = SPIFFSConfig::fsid::FSId;
|
||||
_autoFormat = autoFormat;
|
||||
}
|
||||
|
@ -16,16 +16,18 @@
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
*/
|
||||
#ifndef FSIMPL_H
|
||||
#define FSIMPL_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace fs {
|
||||
namespace fs
|
||||
{
|
||||
|
||||
class FileImpl {
|
||||
class FileImpl
|
||||
{
|
||||
public:
|
||||
virtual ~FileImpl() { }
|
||||
virtual size_t write(const uint8_t *buf, size_t size) = 0;
|
||||
@ -42,20 +44,23 @@ public:
|
||||
virtual bool isDirectory() const = 0;
|
||||
};
|
||||
|
||||
enum OpenMode {
|
||||
enum OpenMode
|
||||
{
|
||||
OM_DEFAULT = 0,
|
||||
OM_CREATE = 1,
|
||||
OM_APPEND = 2,
|
||||
OM_TRUNCATE = 4
|
||||
};
|
||||
|
||||
enum AccessMode {
|
||||
enum AccessMode
|
||||
{
|
||||
AM_READ = 1,
|
||||
AM_WRITE = 2,
|
||||
AM_RW = AM_READ | AM_WRITE
|
||||
};
|
||||
|
||||
class DirImpl {
|
||||
class DirImpl
|
||||
{
|
||||
public:
|
||||
virtual ~DirImpl() { }
|
||||
virtual FileImplPtr openFile(OpenMode openMode, AccessMode accessMode) = 0;
|
||||
@ -67,9 +72,10 @@ public:
|
||||
virtual bool rewind() = 0;
|
||||
};
|
||||
|
||||
class FSImpl {
|
||||
class FSImpl
|
||||
{
|
||||
public:
|
||||
virtual ~FSImpl () { }
|
||||
virtual ~FSImpl() { }
|
||||
virtual bool setConfig(const FSConfig &cfg) = 0;
|
||||
virtual bool begin() = 0;
|
||||
virtual void end() = 0;
|
||||
@ -82,7 +88,10 @@ public:
|
||||
virtual bool remove(const char* path) = 0;
|
||||
virtual bool mkdir(const char* path) = 0;
|
||||
virtual bool rmdir(const char* path) = 0;
|
||||
virtual bool gc() { return true; } // May not be implemented in all file systems.
|
||||
virtual bool gc()
|
||||
{
|
||||
return true; // May not be implemented in all file systems.
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace fs
|
||||
|
@ -8,7 +8,7 @@ typedef void (*voidFuncPtr)(void);
|
||||
typedef void (*voidFuncPtrArg)(void*);
|
||||
|
||||
// Helper functions for Functional interrupt routines
|
||||
extern "C" void ICACHE_RAM_ATTR __attachInterruptArg(uint8_t pin, voidFuncPtr userFunc, void*fp , int mode);
|
||||
extern "C" void ICACHE_RAM_ATTR __attachInterruptArg(uint8_t pin, voidFuncPtr userFunc, void*fp, int mode);
|
||||
|
||||
|
||||
void ICACHE_RAM_ATTR interruptFunctional(void* arg)
|
||||
@ -16,8 +16,8 @@ void ICACHE_RAM_ATTR interruptFunctional(void* arg)
|
||||
ArgStructure* localArg = (ArgStructure*)arg;
|
||||
if (localArg->functionInfo->reqScheduledFunction)
|
||||
{
|
||||
schedule_function(std::bind(localArg->functionInfo->reqScheduledFunction,InterruptInfo(*(localArg->interruptInfo))));
|
||||
// scheduledInterrupts->scheduleFunctionReg(std::bind(localArg->functionInfo->reqScheduledFunction,InterruptInfo(*(localArg->interruptInfo))), false, true);
|
||||
schedule_function(std::bind(localArg->functionInfo->reqScheduledFunction, InterruptInfo(*(localArg->interruptInfo))));
|
||||
// scheduledInterrupts->scheduleFunctionReg(std::bind(localArg->functionInfo->reqScheduledFunction,InterruptInfo(*(localArg->interruptInfo))), false, true);
|
||||
}
|
||||
if (localArg->functionInfo->reqFunction)
|
||||
{
|
||||
@ -49,7 +49,7 @@ void attachInterrupt(uint8_t pin, std::function<void(void)> intRoutine, int mode
|
||||
as->interruptInfo = ii;
|
||||
as->functionInfo = fi;
|
||||
|
||||
__attachInterruptArg (pin, (voidFuncPtr)interruptFunctional, as, mode);
|
||||
__attachInterruptArg(pin, (voidFuncPtr)interruptFunctional, as, mode);
|
||||
}
|
||||
|
||||
void attachScheduledInterrupt(uint8_t pin, std::function<void(InterruptInfo)> scheduledIntRoutine, int mode)
|
||||
@ -67,5 +67,5 @@ void attachScheduledInterrupt(uint8_t pin, std::function<void(InterruptInfo)> sc
|
||||
as->interruptInfo = ii;
|
||||
as->functionInfo = fi;
|
||||
|
||||
__attachInterruptArg (pin, (voidFuncPtr)interruptFunctional, as, mode);
|
||||
__attachInterruptArg(pin, (voidFuncPtr)interruptFunctional, as, mode);
|
||||
}
|
||||
|
@ -13,18 +13,21 @@ extern "C" {
|
||||
|
||||
// Structures for communication
|
||||
|
||||
struct InterruptInfo {
|
||||
struct InterruptInfo
|
||||
{
|
||||
uint8_t pin = 0;
|
||||
uint8_t value = 0;
|
||||
uint32_t micro = 0;
|
||||
};
|
||||
|
||||
struct FunctionInfo {
|
||||
struct FunctionInfo
|
||||
{
|
||||
std::function<void(void)> reqFunction = nullptr;
|
||||
std::function<void(InterruptInfo)> reqScheduledFunction = nullptr;
|
||||
};
|
||||
|
||||
struct ArgStructure {
|
||||
struct ArgStructure
|
||||
{
|
||||
InterruptInfo* interruptInfo = nullptr;
|
||||
FunctionInfo* functionInfo = nullptr;
|
||||
};
|
||||
|
@ -21,7 +21,7 @@
|
||||
Modified 31 March 2015 by Markus Sattler (rewrite the code for UART0 + UART1 support in ESP8266)
|
||||
Modified 25 April 2015 by Thomas Flayols (add configuration different from 8N1 in ESP8266)
|
||||
Modified 3 May 2015 by Hristo Gochkov (change register access methods)
|
||||
*/
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
@ -52,7 +52,8 @@ void HardwareSerial::begin(unsigned long baud, SerialConfig config, SerialMode m
|
||||
|
||||
void HardwareSerial::end()
|
||||
{
|
||||
if(uart_get_debug() == _uart_nr) {
|
||||
if (uart_get_debug() == _uart_nr)
|
||||
{
|
||||
uart_set_debug(UART_NO);
|
||||
}
|
||||
|
||||
@ -60,10 +61,14 @@ void HardwareSerial::end()
|
||||
_uart = NULL;
|
||||
}
|
||||
|
||||
size_t HardwareSerial::setRxBufferSize(size_t size){
|
||||
if(_uart) {
|
||||
size_t HardwareSerial::setRxBufferSize(size_t size)
|
||||
{
|
||||
if (_uart)
|
||||
{
|
||||
_rx_size = uart_resize_rx_buffer(_uart, size);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
_rx_size = size;
|
||||
}
|
||||
return _rx_size;
|
||||
@ -71,18 +76,26 @@ size_t HardwareSerial::setRxBufferSize(size_t size){
|
||||
|
||||
void HardwareSerial::setDebugOutput(bool en)
|
||||
{
|
||||
if(!_uart) {
|
||||
if (!_uart)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(en) {
|
||||
if(uart_tx_enabled(_uart)) {
|
||||
if (en)
|
||||
{
|
||||
if (uart_tx_enabled(_uart))
|
||||
{
|
||||
uart_set_debug(_uart_nr);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
uart_set_debug(UART_NO);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
// disable debug for this interface
|
||||
if(uart_get_debug() == _uart_nr) {
|
||||
if (uart_get_debug() == _uart_nr)
|
||||
{
|
||||
uart_set_debug(UART_NO);
|
||||
}
|
||||
}
|
||||
@ -91,7 +104,8 @@ void HardwareSerial::setDebugOutput(bool en)
|
||||
int HardwareSerial::available(void)
|
||||
{
|
||||
int result = static_cast<int>(uart_rx_available(_uart));
|
||||
if (!result) {
|
||||
if (!result)
|
||||
{
|
||||
optimistic_yield(10000);
|
||||
}
|
||||
return result;
|
||||
@ -99,7 +113,8 @@ int HardwareSerial::available(void)
|
||||
|
||||
void HardwareSerial::flush()
|
||||
{
|
||||
if(!_uart || !uart_tx_enabled(_uart)) {
|
||||
if (!_uart || !uart_tx_enabled(_uart))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@ -123,8 +138,10 @@ unsigned long HardwareSerial::detectBaudrate(time_t timeoutMillis)
|
||||
{
|
||||
time_t startMillis = millis();
|
||||
unsigned long detectedBaudrate;
|
||||
while ((time_t) millis() - startMillis < timeoutMillis) {
|
||||
if ((detectedBaudrate = testBaudrate())) {
|
||||
while ((time_t) millis() - startMillis < timeoutMillis)
|
||||
{
|
||||
if ((detectedBaudrate = testBaudrate()))
|
||||
{
|
||||
break;
|
||||
}
|
||||
yield();
|
||||
@ -143,7 +160,9 @@ size_t HardwareSerial::readBytes(char* buffer, size_t size)
|
||||
size_t avail;
|
||||
while ((avail = available()) == 0 && !timeOut);
|
||||
if (avail == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
got += read(buffer + got, std::min(size - got, avail));
|
||||
}
|
||||
return got;
|
||||
|
@ -22,7 +22,7 @@
|
||||
Modified 18 December 2014 by Ivan Grokhotkov (esp8266 platform support)
|
||||
Modified 31 March 2015 by Markus Sattler (rewrite the code for UART0 + UART1 support in ESP8266)
|
||||
Modified 25 April 2015 by Thomas Flayols (add configuration different from 8N1 in ESP8266)
|
||||
*/
|
||||
*/
|
||||
|
||||
#ifndef HardwareSerial_h
|
||||
#define HardwareSerial_h
|
||||
@ -32,7 +32,8 @@
|
||||
#include "Stream.h"
|
||||
#include "uart.h"
|
||||
|
||||
enum SerialConfig {
|
||||
enum SerialConfig
|
||||
{
|
||||
SERIAL_5N1 = UART_5N1,
|
||||
SERIAL_6N1 = UART_6N1,
|
||||
SERIAL_7N1 = UART_7N1,
|
||||
@ -59,7 +60,8 @@ enum SerialConfig {
|
||||
SERIAL_8O2 = UART_8O2,
|
||||
};
|
||||
|
||||
enum SerialMode {
|
||||
enum SerialMode
|
||||
{
|
||||
SERIAL_FULL = UART_FULL,
|
||||
SERIAL_RX_ONLY = UART_RX_ONLY,
|
||||
SERIAL_TX_ONLY = UART_TX_ONLY
|
||||
@ -104,8 +106,8 @@ public:
|
||||
}
|
||||
|
||||
/*
|
||||
* Toggle between use of GPIO1 and GPIO2 as TX on UART 0.
|
||||
* Note: UART 1 can't be used if GPIO2 is used with UART 0!
|
||||
Toggle between use of GPIO1 and GPIO2 as TX on UART 0.
|
||||
Note: UART 1 can't be used if GPIO2 is used with UART 0!
|
||||
*/
|
||||
void set_tx(uint8_t tx_pin)
|
||||
{
|
||||
@ -113,8 +115,8 @@ public:
|
||||
}
|
||||
|
||||
/*
|
||||
* UART 0 possible options are (1, 3), (2, 3) or (15, 13)
|
||||
* UART 1 allows only TX on 2 if UART 0 is not (2, 3)
|
||||
UART 0 possible options are (1, 3), (2, 3) or (15, 13)
|
||||
UART 1 allows only TX on 2 if UART 0 is not (2, 3)
|
||||
*/
|
||||
void pins(uint8_t tx, uint8_t rx)
|
||||
{
|
||||
|
@ -15,7 +15,7 @@
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <IPAddress.h>
|
||||
@ -27,15 +27,18 @@ IPAddress::IPAddress(const IPAddress& from)
|
||||
ip_addr_copy(_ip, from._ip);
|
||||
}
|
||||
|
||||
IPAddress::IPAddress() {
|
||||
IPAddress::IPAddress()
|
||||
{
|
||||
_ip = *IP_ANY_TYPE; // lwIP's v4-or-v6 generic address
|
||||
}
|
||||
|
||||
bool IPAddress::isSet () const {
|
||||
bool IPAddress::isSet() const
|
||||
{
|
||||
return !ip_addr_isany(&_ip);
|
||||
}
|
||||
|
||||
IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet) {
|
||||
IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet)
|
||||
{
|
||||
setV4();
|
||||
(*this)[0] = first_octet;
|
||||
(*this)[1] = second_octet;
|
||||
@ -43,12 +46,14 @@ IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_oc
|
||||
(*this)[3] = fourth_octet;
|
||||
}
|
||||
|
||||
void IPAddress::ctor32(uint32_t address) {
|
||||
void IPAddress::ctor32(uint32_t address)
|
||||
{
|
||||
setV4();
|
||||
v4() = address;
|
||||
}
|
||||
|
||||
IPAddress::IPAddress(const uint8_t *address) {
|
||||
IPAddress::IPAddress(const uint8_t *address)
|
||||
{
|
||||
setV4();
|
||||
(*this)[0] = address[0];
|
||||
(*this)[1] = address[1];
|
||||
@ -56,8 +61,10 @@ IPAddress::IPAddress(const uint8_t *address) {
|
||||
(*this)[3] = address[3];
|
||||
}
|
||||
|
||||
bool IPAddress::fromString(const char *address) {
|
||||
if (!fromString4(address)) {
|
||||
bool IPAddress::fromString(const char *address)
|
||||
{
|
||||
if (!fromString4(address))
|
||||
{
|
||||
#if LWIP_IPV6
|
||||
return fromString6(address);
|
||||
#else
|
||||
@ -67,7 +74,8 @@ bool IPAddress::fromString(const char *address) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IPAddress::fromString4(const char *address) {
|
||||
bool IPAddress::fromString4(const char *address)
|
||||
{
|
||||
// TODO: (IPv4) add support for "a", "a.b", "a.b.c" formats
|
||||
|
||||
uint16_t acc = 0; // Accumulator
|
||||
@ -79,14 +87,16 @@ bool IPAddress::fromString4(const char *address) {
|
||||
if (c >= '0' && c <= '9')
|
||||
{
|
||||
acc = acc * 10 + (c - '0');
|
||||
if (acc > 255) {
|
||||
if (acc > 255)
|
||||
{
|
||||
// Value out of [0..255] range
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (c == '.')
|
||||
{
|
||||
if (dots == 3) {
|
||||
if (dots == 3)
|
||||
{
|
||||
// Too much dots (there must be 3 dots)
|
||||
return false;
|
||||
}
|
||||
@ -100,7 +110,8 @@ bool IPAddress::fromString4(const char *address) {
|
||||
}
|
||||
}
|
||||
|
||||
if (dots != 3) {
|
||||
if (dots != 3)
|
||||
{
|
||||
// Too few dots (there must be 3 dots)
|
||||
return false;
|
||||
}
|
||||
@ -110,52 +121,71 @@ bool IPAddress::fromString4(const char *address) {
|
||||
return true;
|
||||
}
|
||||
|
||||
IPAddress& IPAddress::operator=(const uint8_t *address) {
|
||||
IPAddress& IPAddress::operator=(const uint8_t *address)
|
||||
{
|
||||
setV4();
|
||||
v4() = *reinterpret_cast<const uint32_t*>(address);
|
||||
return *this;
|
||||
}
|
||||
|
||||
IPAddress& IPAddress::operator=(uint32_t address) {
|
||||
IPAddress& IPAddress::operator=(uint32_t address)
|
||||
{
|
||||
setV4();
|
||||
v4() = address;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool IPAddress::operator==(const uint8_t* addr) const {
|
||||
bool IPAddress::operator==(const uint8_t* addr) const
|
||||
{
|
||||
return isV4() && v4() == *reinterpret_cast<const uint32_t*>(addr);
|
||||
}
|
||||
|
||||
size_t IPAddress::printTo(Print& p) const {
|
||||
size_t IPAddress::printTo(Print& p) const
|
||||
{
|
||||
size_t n = 0;
|
||||
|
||||
if (!isSet())
|
||||
{
|
||||
return p.print(F("(IP unset)"));
|
||||
}
|
||||
|
||||
#if LWIP_IPV6
|
||||
if (isV6()) {
|
||||
if (isV6())
|
||||
{
|
||||
int count0 = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
uint16_t bit = PP_NTOHS(raw6()[i]);
|
||||
if (bit || count0 < 0) {
|
||||
if (bit || count0 < 0)
|
||||
{
|
||||
n += p.printf("%x", bit);
|
||||
if (count0 > 0)
|
||||
// no more hiding 0
|
||||
{
|
||||
count0 = -8;
|
||||
} else
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
count0++;
|
||||
}
|
||||
if ((i != 7 && count0 < 2) || count0 == 7)
|
||||
{
|
||||
n += p.print(':');
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
#endif
|
||||
|
||||
for(int i = 0; i < 4; i++) {
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
n += p.print((*this)[i], DEC);
|
||||
if (i != 3)
|
||||
{
|
||||
n += p.print('.');
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
@ -164,7 +194,9 @@ String IPAddress::toString() const
|
||||
StreamString sstr;
|
||||
#if LWIP_IPV6
|
||||
if (isV6())
|
||||
{
|
||||
sstr.reserve(40); // 8 shorts x 4 chars each + 7 colons + nullterm
|
||||
}
|
||||
else
|
||||
#endif
|
||||
sstr.reserve(16); // 4 bytes with 3 chars max + 3 dots + nullterm, or '(IP unset)'
|
||||
@ -172,22 +204,25 @@ String IPAddress::toString() const
|
||||
return sstr;
|
||||
}
|
||||
|
||||
bool IPAddress::isValid(const String& arg) {
|
||||
bool IPAddress::isValid(const String& arg)
|
||||
{
|
||||
return IPAddress().fromString(arg);
|
||||
}
|
||||
|
||||
bool IPAddress::isValid(const char* arg) {
|
||||
bool IPAddress::isValid(const char* arg)
|
||||
{
|
||||
return IPAddress().fromString(arg);
|
||||
}
|
||||
|
||||
CONST IPAddress INADDR_ANY; // generic "0.0.0.0" for IPv4 & IPv6
|
||||
const IPAddress INADDR_NONE(255,255,255,255);
|
||||
const IPAddress INADDR_NONE(255, 255, 255, 255);
|
||||
|
||||
/**************************************/
|
||||
|
||||
#if LWIP_IPV6
|
||||
|
||||
bool IPAddress::fromString6(const char *address) {
|
||||
bool IPAddress::fromString6(const char *address)
|
||||
{
|
||||
// TODO: test test test
|
||||
|
||||
uint32_t acc = 0; // Accumulator
|
||||
@ -196,45 +231,65 @@ bool IPAddress::fromString6(const char *address) {
|
||||
while (*address)
|
||||
{
|
||||
char c = tolower(*address++);
|
||||
if (isalnum(c)) {
|
||||
if (isalnum(c))
|
||||
{
|
||||
if (c >= 'a')
|
||||
{
|
||||
c -= 'a' - '0' - 10;
|
||||
}
|
||||
acc = acc * 16 + (c - '0');
|
||||
if (acc > 0xffff)
|
||||
// Value out of range
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (c == ':') {
|
||||
if (*address == ':') {
|
||||
}
|
||||
else if (c == ':')
|
||||
{
|
||||
if (*address == ':')
|
||||
{
|
||||
if (doubledots >= 0)
|
||||
// :: allowed once
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// remember location
|
||||
doubledots = dots + !!acc;
|
||||
address++;
|
||||
}
|
||||
if (dots == 7)
|
||||
// too many separators
|
||||
{
|
||||
return false;
|
||||
}
|
||||
raw6()[dots++] = PP_HTONS(acc);
|
||||
acc = 0;
|
||||
}
|
||||
else
|
||||
// Invalid char
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (doubledots == -1 && dots != 7)
|
||||
// Too few separators
|
||||
{
|
||||
return false;
|
||||
}
|
||||
raw6()[dots++] = PP_HTONS(acc);
|
||||
|
||||
if (doubledots != -1) {
|
||||
if (doubledots != -1)
|
||||
{
|
||||
for (int i = dots - doubledots - 1; i >= 0; i--)
|
||||
{
|
||||
raw6()[8 - dots + doubledots + i] = raw6()[doubledots + i];
|
||||
}
|
||||
for (int i = doubledots; i < 8 - dots + doubledots; i++)
|
||||
{
|
||||
raw6()[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
setV6();
|
||||
return true;
|
||||
|
@ -15,7 +15,7 @@
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
*/
|
||||
|
||||
#ifndef IPAddress_h
|
||||
#define IPAddress_h
|
||||
@ -54,8 +54,9 @@ struct ip_addr: ipv4_addr { };
|
||||
// fully backward compatible with legacy IPv4-only Arduino's
|
||||
// with unchanged footprint when IPv6 is disabled
|
||||
|
||||
class IPAddress: public Printable {
|
||||
private:
|
||||
class IPAddress: public Printable
|
||||
{
|
||||
private:
|
||||
|
||||
ip_addr_t _ip;
|
||||
|
||||
@ -63,72 +64,119 @@ class IPAddress: public Printable {
|
||||
// to the internal structure rather than a copy of the address this function should only
|
||||
// be used when you know that the usage of the returned uint8_t* will be transient and not
|
||||
// stored.
|
||||
uint8_t* raw_address() {
|
||||
uint8_t* raw_address()
|
||||
{
|
||||
return reinterpret_cast<uint8_t*>(&v4());
|
||||
}
|
||||
const uint8_t* raw_address() const {
|
||||
const uint8_t* raw_address() const
|
||||
{
|
||||
return reinterpret_cast<const uint8_t*>(&v4());
|
||||
}
|
||||
|
||||
void ctor32 (uint32_t);
|
||||
void ctor32(uint32_t);
|
||||
|
||||
public:
|
||||
public:
|
||||
// Constructors
|
||||
IPAddress();
|
||||
IPAddress(const IPAddress& from);
|
||||
IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet);
|
||||
IPAddress(uint32_t address) { ctor32(address); }
|
||||
IPAddress(u32_t address) { ctor32(address); }
|
||||
IPAddress(int address) { ctor32(address); }
|
||||
IPAddress(uint32_t address)
|
||||
{
|
||||
ctor32(address);
|
||||
}
|
||||
IPAddress(u32_t address)
|
||||
{
|
||||
ctor32(address);
|
||||
}
|
||||
IPAddress(int address)
|
||||
{
|
||||
ctor32(address);
|
||||
}
|
||||
IPAddress(const uint8_t *address);
|
||||
|
||||
bool fromString(const char *address);
|
||||
bool fromString(const String &address) { return fromString(address.c_str()); }
|
||||
bool fromString(const String &address)
|
||||
{
|
||||
return fromString(address.c_str());
|
||||
}
|
||||
|
||||
// Overloaded cast operator to allow IPAddress objects to be used where a pointer
|
||||
// to a four-byte uint8_t array is expected
|
||||
operator uint32_t() const { return isV4()? v4(): (uint32_t)0; }
|
||||
operator uint32_t() { return isV4()? v4(): (uint32_t)0; }
|
||||
operator u32_t() const { return isV4()? v4(): (u32_t)0; }
|
||||
operator u32_t() { return isV4()? v4(): (u32_t)0; }
|
||||
operator uint32_t() const
|
||||
{
|
||||
return isV4() ? v4() : (uint32_t)0;
|
||||
}
|
||||
operator uint32_t()
|
||||
{
|
||||
return isV4() ? v4() : (uint32_t)0;
|
||||
}
|
||||
operator u32_t() const
|
||||
{
|
||||
return isV4() ? v4() : (u32_t)0;
|
||||
}
|
||||
operator u32_t()
|
||||
{
|
||||
return isV4() ? v4() : (u32_t)0;
|
||||
}
|
||||
|
||||
bool isSet () const;
|
||||
operator bool () const { return isSet(); } // <-
|
||||
operator bool () { return isSet(); } // <- both are needed
|
||||
bool isSet() const;
|
||||
operator bool () const
|
||||
{
|
||||
return isSet(); // <-
|
||||
}
|
||||
operator bool ()
|
||||
{
|
||||
return isSet(); // <- both are needed
|
||||
}
|
||||
|
||||
// generic IPv4 wrapper to uint32-view like arduino loves to see it
|
||||
const u32_t& v4() const { return ip_2_ip4(&_ip)->addr; } // for raw_address(const)
|
||||
u32_t& v4() { return ip_2_ip4(&_ip)->addr; }
|
||||
const u32_t& v4() const
|
||||
{
|
||||
return ip_2_ip4(&_ip)->addr; // for raw_address(const)
|
||||
}
|
||||
u32_t& v4()
|
||||
{
|
||||
return ip_2_ip4(&_ip)->addr;
|
||||
}
|
||||
|
||||
bool operator==(const IPAddress& addr) const {
|
||||
bool operator==(const IPAddress& addr) const
|
||||
{
|
||||
return ip_addr_cmp(&_ip, &addr._ip);
|
||||
}
|
||||
bool operator!=(const IPAddress& addr) const {
|
||||
bool operator!=(const IPAddress& addr) const
|
||||
{
|
||||
return !ip_addr_cmp(&_ip, &addr._ip);
|
||||
}
|
||||
bool operator==(uint32_t addr) const {
|
||||
bool operator==(uint32_t addr) const
|
||||
{
|
||||
return isV4() && v4() == addr;
|
||||
}
|
||||
bool operator==(u32_t addr) const {
|
||||
bool operator==(u32_t addr) const
|
||||
{
|
||||
return isV4() && v4() == addr;
|
||||
}
|
||||
bool operator!=(uint32_t addr) const {
|
||||
bool operator!=(uint32_t addr) const
|
||||
{
|
||||
return !(isV4() && v4() == addr);
|
||||
}
|
||||
bool operator!=(u32_t addr) const {
|
||||
bool operator!=(u32_t addr) const
|
||||
{
|
||||
return !(isV4() && v4() == addr);
|
||||
}
|
||||
bool operator==(const uint8_t* addr) const;
|
||||
|
||||
int operator>>(int n) const {
|
||||
return isV4()? v4() >> n: 0;
|
||||
int operator>>(int n) const
|
||||
{
|
||||
return isV4() ? v4() >> n : 0;
|
||||
}
|
||||
|
||||
// Overloaded index operator to allow getting and setting individual octets of the address
|
||||
uint8_t operator[](int index) const {
|
||||
return isV4()? *(raw_address() + index): 0;
|
||||
uint8_t operator[](int index) const
|
||||
{
|
||||
return isV4() ? *(raw_address() + index) : 0;
|
||||
}
|
||||
uint8_t& operator[](int index) {
|
||||
uint8_t& operator[](int index)
|
||||
{
|
||||
setV4();
|
||||
return *(raw_address() + index);
|
||||
}
|
||||
@ -158,28 +206,78 @@ class IPAddress: public Printable {
|
||||
/*
|
||||
lwIP address compatibility
|
||||
*/
|
||||
IPAddress(const ipv4_addr& fw_addr) { setV4(); v4() = fw_addr.addr; }
|
||||
IPAddress(const ipv4_addr* fw_addr) { setV4(); v4() = fw_addr->addr; }
|
||||
IPAddress(const ipv4_addr& fw_addr)
|
||||
{
|
||||
setV4();
|
||||
v4() = fw_addr.addr;
|
||||
}
|
||||
IPAddress(const ipv4_addr* fw_addr)
|
||||
{
|
||||
setV4();
|
||||
v4() = fw_addr->addr;
|
||||
}
|
||||
|
||||
IPAddress& operator=(const ipv4_addr& fw_addr) { setV4(); v4() = fw_addr.addr; return *this; }
|
||||
IPAddress& operator=(const ipv4_addr* fw_addr) { setV4(); v4() = fw_addr->addr; return *this; }
|
||||
IPAddress& operator=(const ipv4_addr& fw_addr)
|
||||
{
|
||||
setV4();
|
||||
v4() = fw_addr.addr;
|
||||
return *this;
|
||||
}
|
||||
IPAddress& operator=(const ipv4_addr* fw_addr)
|
||||
{
|
||||
setV4();
|
||||
v4() = fw_addr->addr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator ip_addr_t () const { return _ip; }
|
||||
operator const ip_addr_t*() const { return &_ip; }
|
||||
operator ip_addr_t*() { return &_ip; }
|
||||
operator ip_addr_t () const
|
||||
{
|
||||
return _ip;
|
||||
}
|
||||
operator const ip_addr_t*() const
|
||||
{
|
||||
return &_ip;
|
||||
}
|
||||
operator ip_addr_t*()
|
||||
{
|
||||
return &_ip;
|
||||
}
|
||||
|
||||
bool isV4() const { return IP_IS_V4_VAL(_ip); }
|
||||
void setV4() { IP_SET_TYPE_VAL(_ip, IPADDR_TYPE_V4); }
|
||||
bool isV4() const
|
||||
{
|
||||
return IP_IS_V4_VAL(_ip);
|
||||
}
|
||||
void setV4()
|
||||
{
|
||||
IP_SET_TYPE_VAL(_ip, IPADDR_TYPE_V4);
|
||||
}
|
||||
|
||||
bool isLocal () const { return ip_addr_islinklocal(&_ip); }
|
||||
bool isLocal() const
|
||||
{
|
||||
return ip_addr_islinklocal(&_ip);
|
||||
}
|
||||
|
||||
#if LWIP_IPV6
|
||||
|
||||
IPAddress(const ip_addr_t& lwip_addr) { ip_addr_copy(_ip, lwip_addr); }
|
||||
IPAddress(const ip_addr_t* lwip_addr) { ip_addr_copy(_ip, *lwip_addr); }
|
||||
IPAddress(const ip_addr_t& lwip_addr)
|
||||
{
|
||||
ip_addr_copy(_ip, lwip_addr);
|
||||
}
|
||||
IPAddress(const ip_addr_t* lwip_addr)
|
||||
{
|
||||
ip_addr_copy(_ip, *lwip_addr);
|
||||
}
|
||||
|
||||
IPAddress& operator=(const ip_addr_t& lwip_addr) { ip_addr_copy(_ip, lwip_addr); return *this; }
|
||||
IPAddress& operator=(const ip_addr_t* lwip_addr) { ip_addr_copy(_ip, *lwip_addr); return *this; }
|
||||
IPAddress& operator=(const ip_addr_t& lwip_addr)
|
||||
{
|
||||
ip_addr_copy(_ip, lwip_addr);
|
||||
return *this;
|
||||
}
|
||||
IPAddress& operator=(const ip_addr_t* lwip_addr)
|
||||
{
|
||||
ip_addr_copy(_ip, *lwip_addr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
uint16_t* raw6()
|
||||
{
|
||||
@ -189,31 +287,49 @@ class IPAddress: public Printable {
|
||||
|
||||
const uint16_t* raw6() const
|
||||
{
|
||||
return isV6()? reinterpret_cast<const uint16_t*>(ip_2_ip6(&_ip)): nullptr;
|
||||
return isV6() ? reinterpret_cast<const uint16_t*>(ip_2_ip6(&_ip)) : nullptr;
|
||||
}
|
||||
|
||||
// when not IPv6, ip_addr_t == ip4_addr_t so this one would be ambiguous
|
||||
// required otherwise
|
||||
operator const ip4_addr_t*() const { return isV4()? ip_2_ip4(&_ip): nullptr; }
|
||||
operator const ip4_addr_t*() const
|
||||
{
|
||||
return isV4() ? ip_2_ip4(&_ip) : nullptr;
|
||||
}
|
||||
|
||||
bool isV6() const { return IP_IS_V6_VAL(_ip); }
|
||||
void setV6() { IP_SET_TYPE_VAL(_ip, IPADDR_TYPE_V6); }
|
||||
bool isV6() const
|
||||
{
|
||||
return IP_IS_V6_VAL(_ip);
|
||||
}
|
||||
void setV6()
|
||||
{
|
||||
IP_SET_TYPE_VAL(_ip, IPADDR_TYPE_V6);
|
||||
}
|
||||
|
||||
protected:
|
||||
protected:
|
||||
bool fromString6(const char *address);
|
||||
|
||||
#else
|
||||
|
||||
// allow portable code when IPv6 is not enabled
|
||||
|
||||
uint16_t* raw6() { return nullptr; }
|
||||
const uint16_t* raw6() const { return nullptr; }
|
||||
bool isV6() const { return false; }
|
||||
uint16_t* raw6()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
const uint16_t* raw6() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
bool isV6() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
void setV6() { }
|
||||
|
||||
#endif
|
||||
|
||||
protected:
|
||||
protected:
|
||||
bool fromString4(const char *address);
|
||||
|
||||
};
|
||||
|
@ -1,60 +1,72 @@
|
||||
#include <Arduino.h>
|
||||
#include <MD5Builder.h>
|
||||
|
||||
uint8_t hex_char_to_byte(uint8_t c){
|
||||
uint8_t hex_char_to_byte(uint8_t c)
|
||||
{
|
||||
return (c >= 'a' && c <= 'f') ? (c - ((uint8_t)'a' - 0xa)) :
|
||||
(c >= 'A' && c <= 'F') ? (c - ((uint8_t)'A' - 0xA)) :
|
||||
(c >= '0' && c<= '9') ? (c - (uint8_t)'0') : 0;
|
||||
(c >= '0' && c <= '9') ? (c - (uint8_t)'0') : 0;
|
||||
}
|
||||
|
||||
void MD5Builder::begin(void){
|
||||
void MD5Builder::begin(void)
|
||||
{
|
||||
memset(_buf, 0x00, 16);
|
||||
MD5Init(&_ctx);
|
||||
}
|
||||
|
||||
void MD5Builder::add(const uint8_t * data, const uint16_t len){
|
||||
void MD5Builder::add(const uint8_t * data, const uint16_t len)
|
||||
{
|
||||
MD5Update(&_ctx, data, len);
|
||||
}
|
||||
|
||||
void MD5Builder::addHexString(const char * data){
|
||||
void MD5Builder::addHexString(const char * data)
|
||||
{
|
||||
uint16_t i, len = strlen(data);
|
||||
uint8_t * tmp = (uint8_t*)malloc(len/2);
|
||||
if(tmp == NULL) {
|
||||
uint8_t * tmp = (uint8_t*)malloc(len / 2);
|
||||
if (tmp == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
for(i=0; i<len; i+=2) {
|
||||
for (i = 0; i < len; i += 2)
|
||||
{
|
||||
uint8_t high = hex_char_to_byte(data[i]);
|
||||
uint8_t low = hex_char_to_byte(data[i+1]);
|
||||
tmp[i/2] = (high & 0x0F) << 4 | (low & 0x0F);
|
||||
uint8_t low = hex_char_to_byte(data[i + 1]);
|
||||
tmp[i / 2] = (high & 0x0F) << 4 | (low & 0x0F);
|
||||
}
|
||||
add(tmp, len/2);
|
||||
add(tmp, len / 2);
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
bool MD5Builder::addStream(Stream & stream, const size_t maxLen){
|
||||
bool MD5Builder::addStream(Stream & stream, const size_t maxLen)
|
||||
{
|
||||
const int buf_size = 512;
|
||||
int maxLengthLeft = maxLen;
|
||||
uint8_t * buf = (uint8_t*) malloc(buf_size);
|
||||
|
||||
if(!buf) {
|
||||
if (!buf)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int bytesAvailable = stream.available();
|
||||
while((bytesAvailable > 0) && (maxLengthLeft > 0)) {
|
||||
while ((bytesAvailable > 0) && (maxLengthLeft > 0))
|
||||
{
|
||||
|
||||
// determine number of bytes to read
|
||||
int readBytes = bytesAvailable;
|
||||
if(readBytes > maxLengthLeft) {
|
||||
if (readBytes > maxLengthLeft)
|
||||
{
|
||||
readBytes = maxLengthLeft ; // read only until max_len
|
||||
}
|
||||
if(readBytes > buf_size) {
|
||||
if (readBytes > buf_size)
|
||||
{
|
||||
readBytes = buf_size; // not read more the buffer can handle
|
||||
}
|
||||
|
||||
// read data and check if we got something
|
||||
int numBytesRead = stream.readBytes(buf, readBytes);
|
||||
if(numBytesRead< 1) {
|
||||
if (numBytesRead < 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -71,21 +83,26 @@ bool MD5Builder::addStream(Stream & stream, const size_t maxLen){
|
||||
return true;
|
||||
}
|
||||
|
||||
void MD5Builder::calculate(void){
|
||||
void MD5Builder::calculate(void)
|
||||
{
|
||||
MD5Final(_buf, &_ctx);
|
||||
}
|
||||
|
||||
void MD5Builder::getBytes(uint8_t * output){
|
||||
void MD5Builder::getBytes(uint8_t * output)
|
||||
{
|
||||
memcpy(output, _buf, 16);
|
||||
}
|
||||
|
||||
void MD5Builder::getChars(char * output){
|
||||
for(uint8_t i = 0; i < 16; i++) {
|
||||
void MD5Builder::getChars(char * output)
|
||||
{
|
||||
for (uint8_t i = 0; i < 16; i++)
|
||||
{
|
||||
sprintf(output + (i * 2), "%02x", _buf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
String MD5Builder::toString(void){
|
||||
String MD5Builder::toString(void)
|
||||
{
|
||||
char out[33];
|
||||
getChars(out);
|
||||
return String(out);
|
||||
|
@ -25,19 +25,35 @@
|
||||
#include <Stream.h>
|
||||
#include "md5.h"
|
||||
|
||||
class MD5Builder {
|
||||
private:
|
||||
class MD5Builder
|
||||
{
|
||||
private:
|
||||
md5_context_t _ctx;
|
||||
uint8_t _buf[16];
|
||||
public:
|
||||
public:
|
||||
void begin(void);
|
||||
void add(const uint8_t * data, const uint16_t len);
|
||||
void add(const char * data){ add((const uint8_t*)data, strlen(data)); }
|
||||
void add(char * data){ add((const char*)data); }
|
||||
void add(const String& data){ add(data.c_str()); }
|
||||
void add(const char * data)
|
||||
{
|
||||
add((const uint8_t*)data, strlen(data));
|
||||
}
|
||||
void add(char * data)
|
||||
{
|
||||
add((const char*)data);
|
||||
}
|
||||
void add(const String& data)
|
||||
{
|
||||
add(data.c_str());
|
||||
}
|
||||
void addHexString(const char * data);
|
||||
void addHexString(char * data){ addHexString((const char*)data); }
|
||||
void addHexString(const String& data){ addHexString(data.c_str()); }
|
||||
void addHexString(char * data)
|
||||
{
|
||||
addHexString((const char*)data);
|
||||
}
|
||||
void addHexString(const String& data)
|
||||
{
|
||||
addHexString(data.c_str());
|
||||
}
|
||||
bool addStream(Stream & stream, const size_t maxLen);
|
||||
void calculate(void);
|
||||
void getBytes(uint8_t * output);
|
||||
|
@ -21,7 +21,7 @@
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
*/
|
||||
|
||||
#include <limits>
|
||||
|
||||
@ -44,13 +44,19 @@ struct DoNothing
|
||||
|
||||
struct YieldOrSkip
|
||||
{
|
||||
static void execute() {delay(0);}
|
||||
static void execute()
|
||||
{
|
||||
delay(0);
|
||||
}
|
||||
};
|
||||
|
||||
template <unsigned long delayMs>
|
||||
struct YieldAndDelayMs
|
||||
{
|
||||
static void execute() {delay(delayMs);}
|
||||
static void execute()
|
||||
{
|
||||
delay(delayMs);
|
||||
}
|
||||
};
|
||||
|
||||
} //YieldPolicy
|
||||
@ -63,7 +69,10 @@ struct TimeSourceMillis
|
||||
// time policy in milli-seconds based on millis()
|
||||
|
||||
using timeType = decltype(millis());
|
||||
static timeType time() {return millis();}
|
||||
static timeType time()
|
||||
{
|
||||
return millis();
|
||||
}
|
||||
static constexpr timeType ticksPerSecond = 1000;
|
||||
static constexpr timeType ticksPerSecondMax = 1000;
|
||||
};
|
||||
@ -75,13 +84,16 @@ struct TimeSourceCycles
|
||||
// (every loop, every yield)
|
||||
|
||||
using timeType = decltype(ESP.getCycleCount());
|
||||
static timeType time() {return ESP.getCycleCount();}
|
||||
static timeType time()
|
||||
{
|
||||
return ESP.getCycleCount();
|
||||
}
|
||||
static constexpr timeType ticksPerSecond = F_CPU; // 80'000'000 or 160'000'000 Hz
|
||||
static constexpr timeType ticksPerSecondMax = 160000000; // 160MHz
|
||||
};
|
||||
|
||||
template <typename TimeSourceType, unsigned long long second_th>
|
||||
// "second_th" units of timeType for one second
|
||||
// "second_th" units of timeType for one second
|
||||
struct TimeUnit
|
||||
{
|
||||
using timeType = typename TimeSourceType::timeType;
|
||||
@ -89,28 +101,30 @@ struct TimeUnit
|
||||
#if __GNUC__ < 5
|
||||
// gcc-4.8 cannot compile the constexpr-only version of this function
|
||||
// using #defines instead luckily works
|
||||
static constexpr timeType computeRangeCompensation ()
|
||||
static constexpr timeType computeRangeCompensation()
|
||||
{
|
||||
#define number_of_secondTh_in_one_tick ((1.0 * second_th) / ticksPerSecond)
|
||||
#define fractional (number_of_secondTh_in_one_tick - (long)number_of_secondTh_in_one_tick)
|
||||
#define number_of_secondTh_in_one_tick ((1.0 * second_th) / ticksPerSecond)
|
||||
#define fractional (number_of_secondTh_in_one_tick - (long)number_of_secondTh_in_one_tick)
|
||||
|
||||
return ({
|
||||
fractional == 0?
|
||||
1: // no need for compensation
|
||||
return (
|
||||
{
|
||||
fractional == 0 ?
|
||||
1 : // no need for compensation
|
||||
(number_of_secondTh_in_one_tick / fractional) + 0.5; // scalar multiplier allowing exact division
|
||||
});
|
||||
|
||||
#undef number_of_secondTh_in_one_tick
|
||||
#undef fractional
|
||||
#undef number_of_secondTh_in_one_tick
|
||||
#undef fractional
|
||||
}
|
||||
#else
|
||||
static constexpr timeType computeRangeCompensation ()
|
||||
static constexpr timeType computeRangeCompensation()
|
||||
{
|
||||
return (
|
||||
{
|
||||
return ({
|
||||
constexpr double number_of_secondTh_in_one_tick = (1.0 * second_th) / ticksPerSecond;
|
||||
constexpr double fractional = number_of_secondTh_in_one_tick - (long)number_of_secondTh_in_one_tick;
|
||||
fractional == 0?
|
||||
1: // no need for compensation
|
||||
fractional == 0 ?
|
||||
1 : // no need for compensation
|
||||
(number_of_secondTh_in_one_tick / fractional) + 0.5; // scalar multiplier allowing exact division
|
||||
});
|
||||
}
|
||||
@ -125,9 +139,18 @@ struct TimeUnit
|
||||
// std::numeric_limits<timeType>::max() is reserved
|
||||
static constexpr timeType timeMax = (std::numeric_limits<timeType>::max() - 1) / user2UnitMultiplierMax;
|
||||
|
||||
static timeType toTimeTypeUnit (const timeType userUnit) {return (userUnit * user2UnitMultiplier) / user2UnitDivider;}
|
||||
static timeType toUserUnit (const timeType internalUnit) {return (internalUnit * user2UnitDivider) / user2UnitMultiplier;}
|
||||
static timeType time () {return TimeSourceType::time();}
|
||||
static timeType toTimeTypeUnit(const timeType userUnit)
|
||||
{
|
||||
return (userUnit * user2UnitMultiplier) / user2UnitDivider;
|
||||
}
|
||||
static timeType toUserUnit(const timeType internalUnit)
|
||||
{
|
||||
return (internalUnit * user2UnitDivider) / user2UnitMultiplier;
|
||||
}
|
||||
static timeType time()
|
||||
{
|
||||
return TimeSourceType::time();
|
||||
}
|
||||
};
|
||||
|
||||
using TimeMillis = TimeUnit< TimeSourceMillis, 1000 >;
|
||||
@ -157,8 +180,10 @@ public:
|
||||
bool expired()
|
||||
{
|
||||
YieldPolicyT::execute(); //in case of DoNothing: gets optimized away
|
||||
if(PeriodicT) //in case of false: gets optimized away
|
||||
if (PeriodicT) //in case of false: gets optimized away
|
||||
{
|
||||
return expiredRetrigger();
|
||||
}
|
||||
return expiredOneShot();
|
||||
}
|
||||
|
||||
@ -168,12 +193,12 @@ public:
|
||||
return expired();
|
||||
}
|
||||
|
||||
bool canExpire () const
|
||||
bool canExpire() const
|
||||
{
|
||||
return !_neverExpires;
|
||||
}
|
||||
|
||||
bool canWait () const
|
||||
bool canWait() const
|
||||
{
|
||||
return _timeout != alwaysExpired;
|
||||
}
|
||||
@ -190,7 +215,7 @@ public:
|
||||
_start = TimePolicyT::time();
|
||||
}
|
||||
|
||||
void resetToNeverExpires ()
|
||||
void resetToNeverExpires()
|
||||
{
|
||||
_timeout = alwaysExpired + 1; // because canWait() has precedence
|
||||
_neverExpires = true;
|
||||
@ -222,10 +247,12 @@ protected:
|
||||
bool expiredRetrigger()
|
||||
{
|
||||
if (!canWait())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
timeType current = TimePolicyT::time();
|
||||
if(checkExpired(current))
|
||||
if (checkExpired(current))
|
||||
{
|
||||
unsigned long n = (current - _start) / _timeout; //how many _timeouts periods have elapsed, will usually be 1 (current - _start >= _timeout)
|
||||
_start += n * _timeout;
|
||||
@ -277,10 +304,10 @@ using periodicFastNs = polledTimeout::timeoutTemplate<true, YieldPolicy::DoNothi
|
||||
|
||||
|
||||
/* A 1-shot timeout that auto-yields when in CONT can be built as follows:
|
||||
* using oneShotYieldMs = esp8266::polledTimeout::timeoutTemplate<false, esp8266::polledTimeout::YieldPolicy::YieldOrSkip>;
|
||||
*
|
||||
* Other policies can be implemented by the user, e.g.: simple yield that panics in SYS, and the polledTimeout types built as needed as shown above, without modifying this file.
|
||||
*/
|
||||
using oneShotYieldMs = esp8266::polledTimeout::timeoutTemplate<false, esp8266::polledTimeout::YieldPolicy::YieldOrSkip>;
|
||||
|
||||
Other policies can be implemented by the user, e.g.: simple yield that panics in SYS, and the polledTimeout types built as needed as shown above, without modifying this file.
|
||||
*/
|
||||
|
||||
}//esp8266
|
||||
|
||||
|
@ -19,7 +19,7 @@
|
||||
Modified 23 November 2006 by David A. Mellis
|
||||
Modified December 2014 by Ivan Grokhotkov
|
||||
Modified May 2015 by Michael C. Miller - esp8266 progmem support
|
||||
*/
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
@ -32,21 +32,25 @@
|
||||
// Public Methods //////////////////////////////////////////////////////////////
|
||||
|
||||
/* default implementation: may be overridden */
|
||||
size_t Print::write(const uint8_t *buffer, size_t size) {
|
||||
size_t Print::write(const uint8_t *buffer, size_t size)
|
||||
{
|
||||
|
||||
#ifdef DEBUG_ESP_CORE
|
||||
static char not_the_best_way [] PROGMEM STORE_ATTR = "Print::write(data,len) should be overridden for better efficiency\r\n";
|
||||
static bool once = false;
|
||||
if (!once) {
|
||||
if (!once)
|
||||
{
|
||||
once = true;
|
||||
os_printf_plus(not_the_best_way);
|
||||
}
|
||||
#endif
|
||||
|
||||
size_t n = 0;
|
||||
while (size--) {
|
||||
while (size--)
|
||||
{
|
||||
size_t ret = write(*buffer++);
|
||||
if (ret == 0) {
|
||||
if (ret == 0)
|
||||
{
|
||||
// Write of last byte didn't complete, abort additional processing
|
||||
break;
|
||||
}
|
||||
@ -55,16 +59,19 @@ size_t Print::write(const uint8_t *buffer, size_t size) {
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::printf(const char *format, ...) {
|
||||
size_t Print::printf(const char *format, ...)
|
||||
{
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
char temp[64];
|
||||
char* buffer = temp;
|
||||
size_t len = vsnprintf(temp, sizeof(temp), format, arg);
|
||||
va_end(arg);
|
||||
if (len > sizeof(temp) - 1) {
|
||||
if (len > sizeof(temp) - 1)
|
||||
{
|
||||
buffer = new char[len + 1];
|
||||
if (!buffer) {
|
||||
if (!buffer)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
va_start(arg, format);
|
||||
@ -72,22 +79,26 @@ size_t Print::printf(const char *format, ...) {
|
||||
va_end(arg);
|
||||
}
|
||||
len = write((const uint8_t*) buffer, len);
|
||||
if (buffer != temp) {
|
||||
if (buffer != temp)
|
||||
{
|
||||
delete[] buffer;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t Print::printf_P(PGM_P format, ...) {
|
||||
size_t Print::printf_P(PGM_P format, ...)
|
||||
{
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
char temp[64];
|
||||
char* buffer = temp;
|
||||
size_t len = vsnprintf_P(temp, sizeof(temp), format, arg);
|
||||
va_end(arg);
|
||||
if (len > sizeof(temp) - 1) {
|
||||
if (len > sizeof(temp) - 1)
|
||||
{
|
||||
buffer = new char[len + 1];
|
||||
if (!buffer) {
|
||||
if (!buffer)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
va_start(arg, format);
|
||||
@ -95,143 +106,181 @@ size_t Print::printf_P(PGM_P format, ...) {
|
||||
va_end(arg);
|
||||
}
|
||||
len = write((const uint8_t*) buffer, len);
|
||||
if (buffer != temp) {
|
||||
if (buffer != temp)
|
||||
{
|
||||
delete[] buffer;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t Print::print(const __FlashStringHelper *ifsh) {
|
||||
size_t Print::print(const __FlashStringHelper *ifsh)
|
||||
{
|
||||
PGM_P p = reinterpret_cast<PGM_P>(ifsh);
|
||||
|
||||
size_t n = 0;
|
||||
while (1) {
|
||||
while (1)
|
||||
{
|
||||
uint8_t c = pgm_read_byte(p++);
|
||||
if (c == 0) break;
|
||||
if (c == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
n += write(c);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::print(const String &s) {
|
||||
size_t Print::print(const String &s)
|
||||
{
|
||||
return write(s.c_str(), s.length());
|
||||
}
|
||||
|
||||
size_t Print::print(const char str[]) {
|
||||
size_t Print::print(const char str[])
|
||||
{
|
||||
return write(str);
|
||||
}
|
||||
|
||||
size_t Print::print(char c) {
|
||||
size_t Print::print(char c)
|
||||
{
|
||||
return write(c);
|
||||
}
|
||||
|
||||
size_t Print::print(unsigned char b, int base) {
|
||||
size_t Print::print(unsigned char b, int base)
|
||||
{
|
||||
return print((unsigned long) b, base);
|
||||
}
|
||||
|
||||
size_t Print::print(int n, int base) {
|
||||
size_t Print::print(int n, int base)
|
||||
{
|
||||
return print((long) n, base);
|
||||
}
|
||||
|
||||
size_t Print::print(unsigned int n, int base) {
|
||||
size_t Print::print(unsigned int n, int base)
|
||||
{
|
||||
return print((unsigned long) n, base);
|
||||
}
|
||||
|
||||
size_t Print::print(long n, int base) {
|
||||
if(base == 0) {
|
||||
size_t Print::print(long n, int base)
|
||||
{
|
||||
if (base == 0)
|
||||
{
|
||||
return write(n);
|
||||
} else if(base == 10) {
|
||||
if(n < 0) {
|
||||
}
|
||||
else if (base == 10)
|
||||
{
|
||||
if (n < 0)
|
||||
{
|
||||
int t = print('-');
|
||||
n = -n;
|
||||
return printNumber(n, 10) + t;
|
||||
}
|
||||
return printNumber(n, 10);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return printNumber(n, base);
|
||||
}
|
||||
}
|
||||
|
||||
size_t Print::print(unsigned long n, int base) {
|
||||
if(base == 0)
|
||||
size_t Print::print(unsigned long n, int base)
|
||||
{
|
||||
if (base == 0)
|
||||
{
|
||||
return write(n);
|
||||
}
|
||||
else
|
||||
{
|
||||
return printNumber(n, base);
|
||||
}
|
||||
}
|
||||
|
||||
size_t Print::print(double n, int digits) {
|
||||
size_t Print::print(double n, int digits)
|
||||
{
|
||||
return printFloat(n, digits);
|
||||
}
|
||||
|
||||
size_t Print::println(const __FlashStringHelper *ifsh) {
|
||||
size_t Print::println(const __FlashStringHelper *ifsh)
|
||||
{
|
||||
size_t n = print(ifsh);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::print(const Printable& x) {
|
||||
size_t Print::print(const Printable& x)
|
||||
{
|
||||
return x.printTo(*this);
|
||||
}
|
||||
|
||||
size_t Print::println(void) {
|
||||
size_t Print::println(void)
|
||||
{
|
||||
return print("\r\n");
|
||||
}
|
||||
|
||||
size_t Print::println(const String &s) {
|
||||
size_t Print::println(const String &s)
|
||||
{
|
||||
size_t n = print(s);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(const char c[]) {
|
||||
size_t Print::println(const char c[])
|
||||
{
|
||||
size_t n = print(c);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(char c) {
|
||||
size_t Print::println(char c)
|
||||
{
|
||||
size_t n = print(c);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(unsigned char b, int base) {
|
||||
size_t Print::println(unsigned char b, int base)
|
||||
{
|
||||
size_t n = print(b, base);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(int num, int base) {
|
||||
size_t Print::println(int num, int base)
|
||||
{
|
||||
size_t n = print(num, base);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(unsigned int num, int base) {
|
||||
size_t Print::println(unsigned int num, int base)
|
||||
{
|
||||
size_t n = print(num, base);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(long num, int base) {
|
||||
size_t Print::println(long num, int base)
|
||||
{
|
||||
size_t n = print(num, base);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(unsigned long num, int base) {
|
||||
size_t Print::println(unsigned long num, int base)
|
||||
{
|
||||
size_t n = print(num, base);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(double num, int digits) {
|
||||
size_t Print::println(double num, int digits)
|
||||
{
|
||||
size_t n = print(num, digits);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(const Printable& x) {
|
||||
size_t Print::println(const Printable& x)
|
||||
{
|
||||
size_t n = print(x);
|
||||
n += println();
|
||||
return n;
|
||||
@ -239,48 +288,64 @@ size_t Print::println(const Printable& x) {
|
||||
|
||||
// Private Methods /////////////////////////////////////////////////////////////
|
||||
|
||||
size_t Print::printNumber(unsigned long n, uint8_t base) {
|
||||
size_t Print::printNumber(unsigned long n, uint8_t base)
|
||||
{
|
||||
char buf[8 * sizeof(long) + 1]; // Assumes 8-bit chars plus zero byte.
|
||||
char *str = &buf[sizeof(buf) - 1];
|
||||
|
||||
*str = '\0';
|
||||
|
||||
// prevent crash if called with base == 1
|
||||
if(base < 2)
|
||||
if (base < 2)
|
||||
{
|
||||
base = 10;
|
||||
}
|
||||
|
||||
do {
|
||||
do
|
||||
{
|
||||
unsigned long m = n;
|
||||
n /= base;
|
||||
char c = m - base * n;
|
||||
*--str = c < 10 ? c + '0' : c + 'A' - 10;
|
||||
} while(n);
|
||||
} while (n);
|
||||
|
||||
return write(str);
|
||||
}
|
||||
|
||||
size_t Print::printFloat(double number, uint8_t digits) {
|
||||
size_t Print::printFloat(double number, uint8_t digits)
|
||||
{
|
||||
size_t n = 0;
|
||||
|
||||
if(isnan(number))
|
||||
if (isnan(number))
|
||||
{
|
||||
return print("nan");
|
||||
if(isinf(number))
|
||||
}
|
||||
if (isinf(number))
|
||||
{
|
||||
return print("inf");
|
||||
if(number > 4294967040.0)
|
||||
}
|
||||
if (number > 4294967040.0)
|
||||
{
|
||||
return print("ovf"); // constant determined empirically
|
||||
if(number < -4294967040.0)
|
||||
}
|
||||
if (number < -4294967040.0)
|
||||
{
|
||||
return print("ovf"); // constant determined empirically
|
||||
}
|
||||
|
||||
// Handle negative numbers
|
||||
if(number < 0.0) {
|
||||
if (number < 0.0)
|
||||
{
|
||||
n += print('-');
|
||||
number = -number;
|
||||
}
|
||||
|
||||
// Round correctly so that print(1.999, 2) prints as "2.00"
|
||||
double rounding = 0.5;
|
||||
for(uint8_t i = 0; i < digits; ++i)
|
||||
for (uint8_t i = 0; i < digits; ++i)
|
||||
{
|
||||
rounding /= 10.0;
|
||||
}
|
||||
|
||||
number += rounding;
|
||||
|
||||
@ -290,12 +355,14 @@ size_t Print::printFloat(double number, uint8_t digits) {
|
||||
n += print(int_part);
|
||||
|
||||
// Print the decimal point, but only if there are digits beyond
|
||||
if(digits > 0) {
|
||||
if (digits > 0)
|
||||
{
|
||||
n += print(".");
|
||||
}
|
||||
|
||||
// Extract digits from the remainder one at a time
|
||||
while(digits-- > 0) {
|
||||
while (digits-- > 0)
|
||||
{
|
||||
remainder *= 10.0;
|
||||
int toPrint = int(remainder);
|
||||
n += print(toPrint);
|
||||
|
@ -15,7 +15,7 @@
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
*/
|
||||
|
||||
#ifndef Print_h
|
||||
#define Print_h
|
||||
@ -31,46 +31,73 @@
|
||||
#define OCT 8
|
||||
#define BIN 2
|
||||
|
||||
class Print {
|
||||
private:
|
||||
class Print
|
||||
{
|
||||
private:
|
||||
int write_error;
|
||||
size_t printNumber(unsigned long, uint8_t);
|
||||
size_t printFloat(double, uint8_t);
|
||||
protected:
|
||||
void setWriteError(int err = 1) {
|
||||
protected:
|
||||
void setWriteError(int err = 1)
|
||||
{
|
||||
write_error = err;
|
||||
}
|
||||
public:
|
||||
public:
|
||||
Print() :
|
||||
write_error(0) {
|
||||
write_error(0)
|
||||
{
|
||||
}
|
||||
|
||||
int getWriteError() {
|
||||
int getWriteError()
|
||||
{
|
||||
return write_error;
|
||||
}
|
||||
void clearWriteError() {
|
||||
void clearWriteError()
|
||||
{
|
||||
setWriteError(0);
|
||||
}
|
||||
|
||||
virtual size_t write(uint8_t) = 0;
|
||||
size_t write(const char *str) {
|
||||
if(str == NULL)
|
||||
size_t write(const char *str)
|
||||
{
|
||||
if (str == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return write((const uint8_t *) str, strlen(str));
|
||||
}
|
||||
virtual size_t write(const uint8_t *buffer, size_t size);
|
||||
size_t write(const char *buffer, size_t size) {
|
||||
size_t write(const char *buffer, size_t size)
|
||||
{
|
||||
return write((const uint8_t *) buffer, size);
|
||||
}
|
||||
// These handle ambiguity for write(0) case, because (0) can be a pointer or an integer
|
||||
size_t write(short t) { return write((uint8_t)t); }
|
||||
size_t write(unsigned short t) { return write((uint8_t)t); }
|
||||
size_t write(int t) { return write((uint8_t)t); }
|
||||
size_t write(unsigned int t) { return write((uint8_t)t); }
|
||||
size_t write(long t) { return write((uint8_t)t); }
|
||||
size_t write(unsigned long t) { return write((uint8_t)t); }
|
||||
size_t write(short t)
|
||||
{
|
||||
return write((uint8_t)t);
|
||||
}
|
||||
size_t write(unsigned short t)
|
||||
{
|
||||
return write((uint8_t)t);
|
||||
}
|
||||
size_t write(int t)
|
||||
{
|
||||
return write((uint8_t)t);
|
||||
}
|
||||
size_t write(unsigned int t)
|
||||
{
|
||||
return write((uint8_t)t);
|
||||
}
|
||||
size_t write(long t)
|
||||
{
|
||||
return write((uint8_t)t);
|
||||
}
|
||||
size_t write(unsigned long t)
|
||||
{
|
||||
return write((uint8_t)t);
|
||||
}
|
||||
|
||||
size_t printf(const char * format, ...) __attribute__ ((format (printf, 2, 3)));
|
||||
size_t printf(const char * format, ...) __attribute__((format(printf, 2, 3)));
|
||||
size_t printf_P(PGM_P format, ...) __attribute__((format(printf, 2, 3)));
|
||||
size_t print(const __FlashStringHelper *);
|
||||
size_t print(const String &);
|
||||
|
@ -15,7 +15,7 @@
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
*/
|
||||
|
||||
#ifndef Printable_h
|
||||
#define Printable_h
|
||||
@ -28,10 +28,11 @@ class Print;
|
||||
By deriving from Printable and implementing the printTo method, it will then be possible
|
||||
for users to print out instances of this class by passing them into the usual
|
||||
Print::print and Print::println methods.
|
||||
*/
|
||||
*/
|
||||
|
||||
class Printable {
|
||||
public:
|
||||
class Printable
|
||||
{
|
||||
public:
|
||||
virtual size_t printTo(Print& p) const = 0;
|
||||
};
|
||||
|
||||
|
@ -14,18 +14,22 @@ static scheduled_fn_t* sLastUnused = 0;
|
||||
|
||||
static int sCount = 0;
|
||||
|
||||
static scheduled_fn_t* get_fn() {
|
||||
static scheduled_fn_t* get_fn()
|
||||
{
|
||||
scheduled_fn_t* result = NULL;
|
||||
// try to get an item from unused items list
|
||||
if (sFirstUnused) {
|
||||
if (sFirstUnused)
|
||||
{
|
||||
result = sFirstUnused;
|
||||
sFirstUnused = result->mNext;
|
||||
if (sFirstUnused == NULL) {
|
||||
if (sFirstUnused == NULL)
|
||||
{
|
||||
sLastUnused = NULL;
|
||||
}
|
||||
}
|
||||
// if no unused items, and count not too high, allocate a new one
|
||||
else if (sCount != SCHEDULED_FN_MAX_COUNT) {
|
||||
else if (sCount != SCHEDULED_FN_MAX_COUNT)
|
||||
{
|
||||
result = new scheduled_fn_t;
|
||||
result->mNext = NULL;
|
||||
++sCount;
|
||||
@ -35,10 +39,12 @@ static scheduled_fn_t* get_fn() {
|
||||
|
||||
static void recycle_fn(scheduled_fn_t* fn)
|
||||
{
|
||||
if (!sLastUnused) {
|
||||
if (!sLastUnused)
|
||||
{
|
||||
sFirstUnused = fn;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
sLastUnused->mNext = fn;
|
||||
}
|
||||
fn->mNext = NULL;
|
||||
@ -48,15 +54,18 @@ static void recycle_fn(scheduled_fn_t* fn)
|
||||
bool schedule_function(std::function<void(void)> fn)
|
||||
{
|
||||
scheduled_fn_t* item = get_fn();
|
||||
if (!item) {
|
||||
if (!item)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
item->mFunc = fn;
|
||||
item->mNext = NULL;
|
||||
if (!sFirst) {
|
||||
if (!sFirst)
|
||||
{
|
||||
sFirst = item;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
sLast->mNext = item;
|
||||
}
|
||||
sLast = item;
|
||||
@ -68,7 +77,8 @@ void run_scheduled_functions()
|
||||
scheduled_fn_t* rFirst = sFirst;
|
||||
sFirst = NULL;
|
||||
sLast = NULL;
|
||||
while (rFirst) {
|
||||
while (rFirst)
|
||||
{
|
||||
scheduled_fn_t* item = rFirst;
|
||||
rFirst = item->mNext;
|
||||
item->mFunc();
|
||||
|
@ -1,15 +1,15 @@
|
||||
/*
|
||||
* ScheduledFunctions.cpp
|
||||
*
|
||||
* Created on: 27 apr. 2018
|
||||
* Author: Herman
|
||||
*/
|
||||
ScheduledFunctions.cpp
|
||||
|
||||
Created on: 27 apr. 2018
|
||||
Author: Herman
|
||||
*/
|
||||
#include "ScheduledFunctions.h"
|
||||
|
||||
std::list<ScheduledFunctions::ScheduledElement> ScheduledFunctions::scheduledFunctions;
|
||||
|
||||
ScheduledFunctions::ScheduledFunctions()
|
||||
:ScheduledFunctions(UINT_MAX)
|
||||
: ScheduledFunctions(UINT_MAX)
|
||||
{
|
||||
}
|
||||
|
||||
@ -18,7 +18,8 @@ ScheduledFunctions::ScheduledFunctions(unsigned int reqMax)
|
||||
maxElements = reqMax;
|
||||
}
|
||||
|
||||
ScheduledFunctions::~ScheduledFunctions() {
|
||||
ScheduledFunctions::~ScheduledFunctions()
|
||||
{
|
||||
}
|
||||
|
||||
ScheduledRegistration ScheduledFunctions::insertElement(ScheduledElement se, bool front)
|
||||
@ -51,7 +52,7 @@ std::list<ScheduledFunctions::ScheduledElement>::iterator ScheduledFunctions::er
|
||||
|
||||
bool ScheduledFunctions::scheduleFunction(ScheduledFunction sf, bool continuous, bool front)
|
||||
{
|
||||
return (insertElement({this,continuous,nullptr,sf}, front) == nullptr);
|
||||
return (insertElement({this, continuous, nullptr, sf}, front) == nullptr);
|
||||
}
|
||||
|
||||
bool ScheduledFunctions::scheduleFunction(ScheduledFunction sf)
|
||||
@ -59,9 +60,9 @@ bool ScheduledFunctions::scheduleFunction(ScheduledFunction sf)
|
||||
return scheduleFunction(sf, false, false);
|
||||
}
|
||||
|
||||
ScheduledRegistration ScheduledFunctions::scheduleFunctionReg (ScheduledFunction sf, bool continuous, bool front)
|
||||
ScheduledRegistration ScheduledFunctions::scheduleFunctionReg(ScheduledFunction sf, bool continuous, bool front)
|
||||
{
|
||||
return insertElement({this,continuous,std::make_shared<int>(1),sf},front);
|
||||
return insertElement({this, continuous, std::make_shared<int>(1), sf}, front);
|
||||
}
|
||||
|
||||
void ScheduledFunctions::runScheduledFunctions()
|
||||
|
@ -1,9 +1,9 @@
|
||||
/*
|
||||
* ScheduledFunctions.h
|
||||
*
|
||||
* Created on: 27 apr. 2018
|
||||
* Author: Herman
|
||||
*/
|
||||
ScheduledFunctions.h
|
||||
|
||||
Created on: 27 apr. 2018
|
||||
Author: Herman
|
||||
*/
|
||||
#include "Arduino.h"
|
||||
#include "Schedule.h"
|
||||
|
||||
@ -18,7 +18,8 @@
|
||||
typedef std::function<void(void)> ScheduledFunction;
|
||||
typedef std::shared_ptr<void> ScheduledRegistration;
|
||||
|
||||
class ScheduledFunctions {
|
||||
class ScheduledFunctions
|
||||
{
|
||||
|
||||
public:
|
||||
ScheduledFunctions();
|
||||
@ -37,7 +38,7 @@ public:
|
||||
std::list<ScheduledElement>::iterator eraseElement(std::list<ScheduledElement>::iterator);
|
||||
bool scheduleFunction(ScheduledFunction sf, bool continuous, bool front);
|
||||
bool scheduleFunction(ScheduledFunction sf);
|
||||
ScheduledRegistration scheduleFunctionReg (ScheduledFunction sf, bool continuous, bool front);
|
||||
ScheduledRegistration scheduleFunctionReg(ScheduledFunction sf, bool continuous, bool front);
|
||||
static void runScheduledFunctions();
|
||||
void removeFunction(ScheduledRegistration sr);
|
||||
|
||||
|
@ -15,16 +15,17 @@
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
*/
|
||||
|
||||
#ifndef server_h
|
||||
#define server_h
|
||||
|
||||
#include "Print.h"
|
||||
|
||||
class Server: public Print {
|
||||
public:
|
||||
virtual void begin() =0;
|
||||
class Server: public Print
|
||||
{
|
||||
public:
|
||||
virtual void begin() = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -31,97 +31,111 @@
|
||||
|
||||
extern "C" {
|
||||
|
||||
uint32_t *stack_thunk_ptr = NULL;
|
||||
uint32_t *stack_thunk_top = NULL;
|
||||
uint32_t *stack_thunk_save = NULL; /* Saved A1 while in BearSSL */
|
||||
uint32_t stack_thunk_refcnt = 0;
|
||||
uint32_t *stack_thunk_ptr = NULL;
|
||||
uint32_t *stack_thunk_top = NULL;
|
||||
uint32_t *stack_thunk_save = NULL; /* Saved A1 while in BearSSL */
|
||||
uint32_t stack_thunk_refcnt = 0;
|
||||
|
||||
#define _stackSize (5600/4)
|
||||
#define _stackPaint 0xdeadbeef
|
||||
|
||||
/* Add a reference, and allocate the stack if necessary */
|
||||
void stack_thunk_add_ref()
|
||||
{
|
||||
/* Add a reference, and allocate the stack if necessary */
|
||||
void stack_thunk_add_ref()
|
||||
{
|
||||
stack_thunk_refcnt++;
|
||||
if (stack_thunk_refcnt == 1) {
|
||||
if (stack_thunk_refcnt == 1)
|
||||
{
|
||||
stack_thunk_ptr = (uint32_t *)malloc(_stackSize * sizeof(uint32_t));
|
||||
stack_thunk_top = stack_thunk_ptr + _stackSize - 1;
|
||||
stack_thunk_save = NULL;
|
||||
stack_thunk_repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Drop a reference, and free stack if no more in use */
|
||||
void stack_thunk_del_ref()
|
||||
{
|
||||
if (stack_thunk_refcnt == 0) {
|
||||
/* Drop a reference, and free stack if no more in use */
|
||||
void stack_thunk_del_ref()
|
||||
{
|
||||
if (stack_thunk_refcnt == 0)
|
||||
{
|
||||
/* Error! */
|
||||
return;
|
||||
}
|
||||
stack_thunk_refcnt--;
|
||||
if (!stack_thunk_refcnt) {
|
||||
if (!stack_thunk_refcnt)
|
||||
{
|
||||
free(stack_thunk_ptr);
|
||||
stack_thunk_ptr = NULL;
|
||||
stack_thunk_top = NULL;
|
||||
stack_thunk_save = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void stack_thunk_repaint()
|
||||
{
|
||||
for (int i=0; i < _stackSize; i++) {
|
||||
void stack_thunk_repaint()
|
||||
{
|
||||
for (int i = 0; i < _stackSize; i++)
|
||||
{
|
||||
stack_thunk_ptr[i] = _stackPaint;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Simple accessor functions used by postmortem */
|
||||
uint32_t stack_thunk_get_refcnt() {
|
||||
/* Simple accessor functions used by postmortem */
|
||||
uint32_t stack_thunk_get_refcnt()
|
||||
{
|
||||
return stack_thunk_refcnt;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t stack_thunk_get_stack_top() {
|
||||
uint32_t stack_thunk_get_stack_top()
|
||||
{
|
||||
return (uint32_t)stack_thunk_top;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t stack_thunk_get_stack_bot() {
|
||||
uint32_t stack_thunk_get_stack_bot()
|
||||
{
|
||||
return (uint32_t)stack_thunk_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t stack_thunk_get_cont_sp() {
|
||||
uint32_t stack_thunk_get_cont_sp()
|
||||
{
|
||||
return (uint32_t)stack_thunk_save;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the number of bytes ever used since the stack was created */
|
||||
uint32_t stack_thunk_get_max_usage()
|
||||
{
|
||||
/* Return the number of bytes ever used since the stack was created */
|
||||
uint32_t stack_thunk_get_max_usage()
|
||||
{
|
||||
uint32_t cnt = 0;
|
||||
|
||||
/* No stack == no usage by definition! */
|
||||
if (!stack_thunk_ptr) {
|
||||
if (!stack_thunk_ptr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (cnt=0; (cnt < _stackSize) && (stack_thunk_ptr[cnt] == _stackPaint); cnt++) {
|
||||
for (cnt = 0; (cnt < _stackSize) && (stack_thunk_ptr[cnt] == _stackPaint); cnt++)
|
||||
{
|
||||
/* Noop, all work done in for() */
|
||||
}
|
||||
return 4 * (_stackSize - cnt);
|
||||
}
|
||||
}
|
||||
|
||||
/* Print the stack from the first used 16-byte chunk to the top, decodable by the exception decoder */
|
||||
void stack_thunk_dump_stack()
|
||||
{
|
||||
/* Print the stack from the first used 16-byte chunk to the top, decodable by the exception decoder */
|
||||
void stack_thunk_dump_stack()
|
||||
{
|
||||
uint32_t *pos = stack_thunk_top;
|
||||
while (pos < stack_thunk_ptr) {
|
||||
while (pos < stack_thunk_ptr)
|
||||
{
|
||||
if ((pos[0] != _stackPaint) || (pos[1] != _stackPaint) || (pos[2] != _stackPaint) || (pos[3] != _stackPaint))
|
||||
{
|
||||
break;
|
||||
}
|
||||
pos += 4;
|
||||
}
|
||||
ets_printf(">>>stack>>>\n");
|
||||
while (pos < stack_thunk_ptr) {
|
||||
while (pos < stack_thunk_ptr)
|
||||
{
|
||||
ets_printf("%08x: %08x %08x %08x %08x\n", (int32_t)pos, pos[0], pos[1], pos[2], pos[3]);
|
||||
pos += 4;
|
||||
}
|
||||
ets_printf("<<<stack<<<\n");
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
Created July 2011
|
||||
parsing functions based on TextFinder library by Michael Margolis
|
||||
*/
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Stream.h>
|
||||
@ -26,43 +26,59 @@
|
||||
#define NO_SKIP_CHAR 1 // a magic char not found in a valid ASCII numeric field
|
||||
|
||||
// private method to read stream with timeout
|
||||
int Stream::timedRead() {
|
||||
int Stream::timedRead()
|
||||
{
|
||||
int c;
|
||||
_startMillis = millis();
|
||||
do {
|
||||
do
|
||||
{
|
||||
c = read();
|
||||
if(c >= 0)
|
||||
if (c >= 0)
|
||||
{
|
||||
return c;
|
||||
}
|
||||
yield();
|
||||
} while(millis() - _startMillis < _timeout);
|
||||
} while (millis() - _startMillis < _timeout);
|
||||
return -1; // -1 indicates timeout
|
||||
}
|
||||
|
||||
// private method to peek stream with timeout
|
||||
int Stream::timedPeek() {
|
||||
int Stream::timedPeek()
|
||||
{
|
||||
int c;
|
||||
_startMillis = millis();
|
||||
do {
|
||||
do
|
||||
{
|
||||
c = peek();
|
||||
if(c >= 0)
|
||||
if (c >= 0)
|
||||
{
|
||||
return c;
|
||||
}
|
||||
yield();
|
||||
} while(millis() - _startMillis < _timeout);
|
||||
} while (millis() - _startMillis < _timeout);
|
||||
return -1; // -1 indicates timeout
|
||||
}
|
||||
|
||||
// returns peek of the next digit in the stream or -1 if timeout
|
||||
// discards non-numeric characters
|
||||
int Stream::peekNextDigit() {
|
||||
int Stream::peekNextDigit()
|
||||
{
|
||||
int c;
|
||||
while(1) {
|
||||
while (1)
|
||||
{
|
||||
c = timedPeek();
|
||||
if(c < 0)
|
||||
if (c < 0)
|
||||
{
|
||||
return c; // timeout
|
||||
if(c == '-')
|
||||
}
|
||||
if (c == '-')
|
||||
{
|
||||
return c;
|
||||
if(c >= '0' && c <= '9')
|
||||
}
|
||||
if (c >= '0' && c <= '9')
|
||||
{
|
||||
return c;
|
||||
}
|
||||
read(); // discard non-numeric
|
||||
}
|
||||
}
|
||||
@ -76,95 +92,125 @@ void Stream::setTimeout(unsigned long timeout) // sets the maximum number of mi
|
||||
}
|
||||
|
||||
// find returns true if the target string is found
|
||||
bool Stream::find(const char *target) {
|
||||
bool Stream::find(const char *target)
|
||||
{
|
||||
return findUntil(target, (char*) "");
|
||||
}
|
||||
|
||||
// reads data from the stream until the target string of given length is found
|
||||
// returns true if target string is found, false if timed out
|
||||
bool Stream::find(const char *target, size_t length) {
|
||||
bool Stream::find(const char *target, size_t length)
|
||||
{
|
||||
return findUntil(target, length, NULL, 0);
|
||||
}
|
||||
|
||||
// as find but search ends if the terminator string is found
|
||||
bool Stream::findUntil(const char *target, const char *terminator) {
|
||||
bool Stream::findUntil(const char *target, const char *terminator)
|
||||
{
|
||||
return findUntil(target, strlen(target), terminator, strlen(terminator));
|
||||
}
|
||||
|
||||
// reads data from the stream until the target string of the given length is found
|
||||
// search terminated if the terminator string is found
|
||||
// returns true if target string is found, false if terminated or timed out
|
||||
bool Stream::findUntil(const char *target, size_t targetLen, const char *terminator, size_t termLen) {
|
||||
bool Stream::findUntil(const char *target, size_t targetLen, const char *terminator, size_t termLen)
|
||||
{
|
||||
size_t index = 0; // maximum target string length is 64k bytes!
|
||||
size_t termIndex = 0;
|
||||
int c;
|
||||
|
||||
if(*target == 0)
|
||||
if (*target == 0)
|
||||
{
|
||||
return true; // return true if target is a null string
|
||||
while((c = timedRead()) > 0) {
|
||||
}
|
||||
while ((c = timedRead()) > 0)
|
||||
{
|
||||
|
||||
if(c != target[index])
|
||||
if (c != target[index])
|
||||
{
|
||||
index = 0; // reset index if any char does not match
|
||||
}
|
||||
|
||||
if(c == target[index]) {
|
||||
if (c == target[index])
|
||||
{
|
||||
//////Serial.print("found "); Serial.write(c); Serial.print("index now"); Serial.println(index+1);
|
||||
if(++index >= targetLen) { // return true if all chars in the target match
|
||||
if (++index >= targetLen) // return true if all chars in the target match
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(termLen > 0 && c == terminator[termIndex]) {
|
||||
if(++termIndex >= termLen)
|
||||
if (termLen > 0 && c == terminator[termIndex])
|
||||
{
|
||||
if (++termIndex >= termLen)
|
||||
{
|
||||
return false; // return false if terminate string found before target string
|
||||
} else
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
termIndex = 0;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// returns the first valid (long) integer value from the current position.
|
||||
// initial characters that are not digits (or the minus sign) are skipped
|
||||
// function is terminated by the first character that is not a digit.
|
||||
long Stream::parseInt() {
|
||||
long Stream::parseInt()
|
||||
{
|
||||
return parseInt(NO_SKIP_CHAR); // terminate on first non-digit character (or timeout)
|
||||
}
|
||||
|
||||
// as above but a given skipChar is ignored
|
||||
// this allows format characters (typically commas) in values to be ignored
|
||||
long Stream::parseInt(char skipChar) {
|
||||
long Stream::parseInt(char skipChar)
|
||||
{
|
||||
boolean isNegative = false;
|
||||
long value = 0;
|
||||
int c;
|
||||
|
||||
c = peekNextDigit();
|
||||
// ignore non numeric leading characters
|
||||
if(c < 0)
|
||||
if (c < 0)
|
||||
{
|
||||
return 0; // zero returned if timeout
|
||||
}
|
||||
|
||||
do {
|
||||
if(c == skipChar)
|
||||
do
|
||||
{
|
||||
if (c == skipChar)
|
||||
; // ignore this charactor
|
||||
else if(c == '-')
|
||||
else if (c == '-')
|
||||
{
|
||||
isNegative = true;
|
||||
else if(c >= '0' && c <= '9') // is c a digit?
|
||||
}
|
||||
else if (c >= '0' && c <= '9') // is c a digit?
|
||||
{
|
||||
value = value * 10 + c - '0';
|
||||
}
|
||||
read(); // consume the character we got with peek
|
||||
c = timedPeek();
|
||||
} while((c >= '0' && c <= '9') || c == skipChar);
|
||||
} while ((c >= '0' && c <= '9') || c == skipChar);
|
||||
|
||||
if(isNegative)
|
||||
if (isNegative)
|
||||
{
|
||||
value = -value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
// as parseInt but returns a floating point value
|
||||
float Stream::parseFloat() {
|
||||
float Stream::parseFloat()
|
||||
{
|
||||
return parseFloat(NO_SKIP_CHAR);
|
||||
}
|
||||
|
||||
// as above but the given skipChar is ignored
|
||||
// this allows format characters (typically commas) in values to be ignored
|
||||
float Stream::parseFloat(char skipChar) {
|
||||
float Stream::parseFloat(char skipChar)
|
||||
{
|
||||
boolean isNegative = false;
|
||||
boolean isFraction = false;
|
||||
long value = 0;
|
||||
@ -173,31 +219,47 @@ float Stream::parseFloat(char skipChar) {
|
||||
|
||||
c = peekNextDigit();
|
||||
// ignore non numeric leading characters
|
||||
if(c < 0)
|
||||
if (c < 0)
|
||||
{
|
||||
return 0; // zero returned if timeout
|
||||
}
|
||||
|
||||
do {
|
||||
if(c == skipChar)
|
||||
do
|
||||
{
|
||||
if (c == skipChar)
|
||||
; // ignore
|
||||
else if(c == '-')
|
||||
else if (c == '-')
|
||||
{
|
||||
isNegative = true;
|
||||
else if(c == '.')
|
||||
}
|
||||
else if (c == '.')
|
||||
{
|
||||
isFraction = true;
|
||||
else if(c >= '0' && c <= '9') { // is c a digit?
|
||||
}
|
||||
else if (c >= '0' && c <= '9') // is c a digit?
|
||||
{
|
||||
value = value * 10 + c - '0';
|
||||
if(isFraction)
|
||||
if (isFraction)
|
||||
{
|
||||
fraction *= 0.1;
|
||||
}
|
||||
}
|
||||
read(); // consume the character we got with peek
|
||||
c = timedPeek();
|
||||
} while((c >= '0' && c <= '9') || c == '.' || c == skipChar);
|
||||
} while ((c >= '0' && c <= '9') || c == '.' || c == skipChar);
|
||||
|
||||
if(isNegative)
|
||||
if (isNegative)
|
||||
{
|
||||
value = -value;
|
||||
if(isFraction)
|
||||
}
|
||||
if (isFraction)
|
||||
{
|
||||
return value * fraction;
|
||||
}
|
||||
else
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
// read characters from stream into buffer
|
||||
@ -205,12 +267,16 @@ float Stream::parseFloat(char skipChar) {
|
||||
// returns the number of characters placed in the buffer
|
||||
// the buffer is NOT null terminated.
|
||||
//
|
||||
size_t Stream::readBytes(char *buffer, size_t length) {
|
||||
size_t Stream::readBytes(char *buffer, size_t length)
|
||||
{
|
||||
size_t count = 0;
|
||||
while(count < length) {
|
||||
while (count < length)
|
||||
{
|
||||
int c = timedRead();
|
||||
if(c < 0)
|
||||
if (c < 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
*buffer++ = (char) c;
|
||||
count++;
|
||||
}
|
||||
@ -221,34 +287,44 @@ size_t Stream::readBytes(char *buffer, size_t length) {
|
||||
// terminates if length characters have been read, timeout, or if the terminator character detected
|
||||
// returns the number of characters placed in the buffer (0 means no valid data found)
|
||||
|
||||
size_t Stream::readBytesUntil(char terminator, char *buffer, size_t length) {
|
||||
if(length < 1)
|
||||
size_t Stream::readBytesUntil(char terminator, char *buffer, size_t length)
|
||||
{
|
||||
if (length < 1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
size_t index = 0;
|
||||
while(index < length) {
|
||||
while (index < length)
|
||||
{
|
||||
int c = timedRead();
|
||||
if(c < 0 || c == terminator)
|
||||
if (c < 0 || c == terminator)
|
||||
{
|
||||
break;
|
||||
}
|
||||
*buffer++ = (char) c;
|
||||
index++;
|
||||
}
|
||||
return index; // return number of characters, not including null terminator
|
||||
}
|
||||
|
||||
String Stream::readString() {
|
||||
String Stream::readString()
|
||||
{
|
||||
String ret;
|
||||
int c = timedRead();
|
||||
while(c >= 0) {
|
||||
while (c >= 0)
|
||||
{
|
||||
ret += (char) c;
|
||||
c = timedRead();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
String Stream::readStringUntil(char terminator) {
|
||||
String Stream::readStringUntil(char terminator)
|
||||
{
|
||||
String ret;
|
||||
int c = timedRead();
|
||||
while(c >= 0 && c != terminator) {
|
||||
while (c >= 0 && c != terminator)
|
||||
{
|
||||
ret += (char) c;
|
||||
c = timedRead();
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
parsing functions based on TextFinder library by Michael Margolis
|
||||
*/
|
||||
*/
|
||||
|
||||
#ifndef Stream_h
|
||||
#define Stream_h
|
||||
@ -33,50 +33,59 @@
|
||||
#define getFloat(skipChar) parseFloat(skipChar)
|
||||
#define getString( pre_string, post_string, buffer, length)
|
||||
readBytesBetween( pre_string, terminator, buffer, length)
|
||||
*/
|
||||
*/
|
||||
|
||||
class Stream: public Print {
|
||||
protected:
|
||||
class Stream: public Print
|
||||
{
|
||||
protected:
|
||||
unsigned long _timeout; // number of milliseconds to wait for the next char before aborting timed read
|
||||
unsigned long _startMillis; // used for timeout measurement
|
||||
int timedRead(); // private method to read stream with timeout
|
||||
int timedPeek(); // private method to peek stream with timeout
|
||||
int peekNextDigit(); // returns the next numeric digit in the stream or -1 if timeout
|
||||
|
||||
public:
|
||||
public:
|
||||
virtual int available() = 0;
|
||||
virtual int read() = 0;
|
||||
virtual int peek() = 0;
|
||||
|
||||
Stream() {
|
||||
Stream()
|
||||
{
|
||||
_timeout = 1000;
|
||||
}
|
||||
|
||||
// parsing methods
|
||||
// parsing methods
|
||||
|
||||
void setTimeout(unsigned long timeout); // sets maximum milliseconds to wait for stream data, default is 1 second
|
||||
|
||||
bool find(const char *target); // reads data from the stream until the target string is found
|
||||
bool find(uint8_t *target) {
|
||||
bool find(uint8_t *target)
|
||||
{
|
||||
return find((char *) target);
|
||||
}
|
||||
// returns true if target string is found, false if timed out (see setTimeout)
|
||||
|
||||
bool find(const char *target, size_t length); // reads data from the stream until the target string of given length is found
|
||||
bool find(const uint8_t *target, size_t length) {
|
||||
bool find(const uint8_t *target, size_t length)
|
||||
{
|
||||
return find((char *) target, length);
|
||||
}
|
||||
// returns true if target string is found, false if timed out
|
||||
|
||||
bool find(char target) { return find (&target, 1); }
|
||||
bool find(char target)
|
||||
{
|
||||
return find(&target, 1);
|
||||
}
|
||||
|
||||
bool findUntil(const char *target, const char *terminator); // as find but search ends if the terminator string is found
|
||||
bool findUntil(const uint8_t *target, const char *terminator) {
|
||||
bool findUntil(const uint8_t *target, const char *terminator)
|
||||
{
|
||||
return findUntil((char *) target, terminator);
|
||||
}
|
||||
|
||||
bool findUntil(const char *target, size_t targetLen, const char *terminate, size_t termLen); // as above but search ends if the terminate string is found
|
||||
bool findUntil(const uint8_t *target, size_t targetLen, const char *terminate, size_t termLen) {
|
||||
bool findUntil(const uint8_t *target, size_t targetLen, const char *terminate, size_t termLen)
|
||||
{
|
||||
return findUntil((char *) target, targetLen, terminate, termLen);
|
||||
}
|
||||
|
||||
@ -87,14 +96,16 @@ class Stream: public Print {
|
||||
float parseFloat(); // float version of parseInt
|
||||
|
||||
virtual size_t readBytes(char *buffer, size_t length); // read chars from stream into buffer
|
||||
virtual size_t readBytes(uint8_t *buffer, size_t length) {
|
||||
virtual size_t readBytes(uint8_t *buffer, size_t length)
|
||||
{
|
||||
return readBytes((char *) buffer, length);
|
||||
}
|
||||
// terminates if length characters have been read or timeout (see setTimeout)
|
||||
// returns the number of characters placed in the buffer (0 means no valid data found)
|
||||
|
||||
size_t readBytesUntil(char terminator, char *buffer, size_t length); // as readBytes with terminator character
|
||||
size_t readBytesUntil(char terminator, uint8_t *buffer, size_t length) {
|
||||
size_t readBytesUntil(char terminator, uint8_t *buffer, size_t length)
|
||||
{
|
||||
return readBytesUntil(terminator, (char *) buffer, length);
|
||||
}
|
||||
// terminates if length characters have been read, timeout, or if the terminator character detected
|
||||
@ -104,7 +115,7 @@ class Stream: public Print {
|
||||
virtual String readString();
|
||||
String readStringUntil(char terminator);
|
||||
|
||||
protected:
|
||||
protected:
|
||||
long parseInt(char skipChar); // as above but the given skipChar is ignored
|
||||
// as above but the given skipChar is ignored
|
||||
// this allows format characters (typically commas) in values to be ignored
|
||||
|
@ -18,16 +18,19 @@
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "StreamString.h"
|
||||
|
||||
size_t StreamString::write(const uint8_t *data, size_t size) {
|
||||
if(size && data) {
|
||||
size_t StreamString::write(const uint8_t *data, size_t size)
|
||||
{
|
||||
if (size && data)
|
||||
{
|
||||
const unsigned int newlen = length() + size;
|
||||
if(reserve(newlen + 1)) {
|
||||
memcpy((void *) (wbuffer() + len()), (const void *) data, size);
|
||||
if (reserve(newlen + 1))
|
||||
{
|
||||
memcpy((void *)(wbuffer() + len()), (const void *) data, size);
|
||||
setLen(newlen);
|
||||
*(wbuffer() + newlen) = 0x00; // add null for string end
|
||||
return size;
|
||||
@ -36,16 +39,20 @@ size_t StreamString::write(const uint8_t *data, size_t size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t StreamString::write(uint8_t data) {
|
||||
size_t StreamString::write(uint8_t data)
|
||||
{
|
||||
return concat((char) data);
|
||||
}
|
||||
|
||||
int StreamString::available() {
|
||||
int StreamString::available()
|
||||
{
|
||||
return length();
|
||||
}
|
||||
|
||||
int StreamString::read() {
|
||||
if(length()) {
|
||||
int StreamString::read()
|
||||
{
|
||||
if (length())
|
||||
{
|
||||
char c = charAt(0);
|
||||
remove(0, 1);
|
||||
return c;
|
||||
@ -54,14 +61,17 @@ int StreamString::read() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int StreamString::peek() {
|
||||
if(length()) {
|
||||
int StreamString::peek()
|
||||
{
|
||||
if (length())
|
||||
{
|
||||
char c = charAt(0);
|
||||
return c;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void StreamString::flush() {
|
||||
void StreamString::flush()
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,8 @@
|
||||
#define STREAMSTRING_H_
|
||||
|
||||
|
||||
class StreamString: public Stream, public String {
|
||||
class StreamString: public Stream, public String
|
||||
{
|
||||
public:
|
||||
size_t write(const uint8_t *buffer, size_t size) override;
|
||||
size_t write(uint8_t data) override;
|
||||
|
@ -28,8 +28,10 @@
|
||||
static uint32_t _toneMap = 0;
|
||||
|
||||
|
||||
static void _startTone(uint8_t _pin, uint32_t high, uint32_t low, unsigned long duration) {
|
||||
if (_pin > 16) {
|
||||
static void _startTone(uint8_t _pin, uint32_t high, uint32_t low, unsigned long duration)
|
||||
{
|
||||
if (_pin > 16)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@ -38,16 +40,21 @@ static void _startTone(uint8_t _pin, uint32_t high, uint32_t low, unsigned long
|
||||
high = std::max(high, (uint32_t)100);
|
||||
low = std::max(low, (uint32_t)100);
|
||||
|
||||
if (startWaveform(_pin, high, low, (uint32_t) duration * 1000)) {
|
||||
if (startWaveform(_pin, high, low, (uint32_t) duration * 1000))
|
||||
{
|
||||
_toneMap |= 1 << _pin;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void tone(uint8_t _pin, unsigned int frequency, unsigned long duration) {
|
||||
if (frequency == 0) {
|
||||
void tone(uint8_t _pin, unsigned int frequency, unsigned long duration)
|
||||
{
|
||||
if (frequency == 0)
|
||||
{
|
||||
noTone(_pin);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t period = 1000000L / frequency;
|
||||
uint32_t high = period / 2;
|
||||
uint32_t low = period - high;
|
||||
@ -58,10 +65,14 @@ void tone(uint8_t _pin, unsigned int frequency, unsigned long duration) {
|
||||
|
||||
// Separate tone(float) to hopefully not pull in floating point libs unless
|
||||
// it's called with a float.
|
||||
void tone(uint8_t _pin, double frequency, unsigned long duration) {
|
||||
if (frequency < 1.0) { // FP means no exact comparisons
|
||||
void tone(uint8_t _pin, double frequency, unsigned long duration)
|
||||
{
|
||||
if (frequency < 1.0) // FP means no exact comparisons
|
||||
{
|
||||
noTone(_pin);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
double period = 1000000.0 / frequency;
|
||||
uint32_t high = (uint32_t)((period / 2.0) + 0.5);
|
||||
uint32_t low = (uint32_t)(period + 0.5) - high;
|
||||
@ -71,14 +82,17 @@ void tone(uint8_t _pin, double frequency, unsigned long duration) {
|
||||
|
||||
|
||||
// Fix ambiguous tone() binding when adding in a duration
|
||||
void tone(uint8_t _pin, int frequency, unsigned long duration) {
|
||||
void tone(uint8_t _pin, int frequency, unsigned long duration)
|
||||
{
|
||||
// Call the unsigned int version of the function explicitly
|
||||
tone(_pin, (unsigned int)frequency, duration);
|
||||
}
|
||||
|
||||
|
||||
void noTone(uint8_t _pin) {
|
||||
if (_pin > 16) {
|
||||
void noTone(uint8_t _pin)
|
||||
{
|
||||
if (_pin > 16)
|
||||
{
|
||||
return;
|
||||
}
|
||||
stopWaveform(_pin);
|
||||
|
@ -1,36 +1,36 @@
|
||||
/*
|
||||
* Udp.cpp: Library to send/receive UDP packets.
|
||||
*
|
||||
* NOTE: UDP is fast, but has some important limitations (thanks to Warren Gray for mentioning these)
|
||||
* 1) UDP does not guarantee the order in which assembled UDP packets are received. This
|
||||
* might not happen often in practice, but in larger network topologies, a UDP
|
||||
* packet can be received out of sequence.
|
||||
* 2) UDP does not guard against lost packets - so packets *can* disappear without the sender being
|
||||
* aware of it. Again, this may not be a concern in practice on small local networks.
|
||||
* For more information, see http://www.cafeaulait.org/course/week12/35.html
|
||||
*
|
||||
* MIT License:
|
||||
* Copyright (c) 2008 Bjoern Hartmann
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* bjoern@cs.stanford.edu 12/30/2008
|
||||
*/
|
||||
Udp.cpp: Library to send/receive UDP packets.
|
||||
|
||||
NOTE: UDP is fast, but has some important limitations (thanks to Warren Gray for mentioning these)
|
||||
1) UDP does not guarantee the order in which assembled UDP packets are received. This
|
||||
might not happen often in practice, but in larger network topologies, a UDP
|
||||
packet can be received out of sequence.
|
||||
2) UDP does not guard against lost packets - so packets *can* disappear without the sender being
|
||||
aware of it. Again, this may not be a concern in practice on small local networks.
|
||||
For more information, see http://www.cafeaulait.org/course/week12/35.html
|
||||
|
||||
MIT License:
|
||||
Copyright (c) 2008 Bjoern Hartmann
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
bjoern@cs.stanford.edu 12/30/2008
|
||||
*/
|
||||
|
||||
#ifndef udp_h
|
||||
#define udp_h
|
||||
@ -38,56 +38,59 @@
|
||||
#include <Stream.h>
|
||||
#include <IPAddress.h>
|
||||
|
||||
class UDP: public Stream {
|
||||
class UDP: public Stream
|
||||
{
|
||||
|
||||
public:
|
||||
public:
|
||||
virtual ~UDP() {};
|
||||
virtual uint8_t begin(uint16_t) =0; // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use
|
||||
virtual void stop() =0; // Finish with the UDP socket
|
||||
virtual uint8_t begin(uint16_t) = 0; // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use
|
||||
virtual void stop() = 0; // Finish with the UDP socket
|
||||
|
||||
// Sending UDP packets
|
||||
|
||||
// Start building up a packet to send to the remote host specific in ip and port
|
||||
// Returns 1 if successful, 0 if there was a problem with the supplied IP address or port
|
||||
virtual int beginPacket(IPAddress ip, uint16_t port) =0;
|
||||
virtual int beginPacket(IPAddress ip, uint16_t port) = 0;
|
||||
// Start building up a packet to send to the remote host specific in host and port
|
||||
// Returns 1 if successful, 0 if there was a problem resolving the hostname or port
|
||||
virtual int beginPacket(const char *host, uint16_t port) =0;
|
||||
virtual int beginPacket(const char *host, uint16_t port) = 0;
|
||||
// Finish off this packet and send it
|
||||
// Returns 1 if the packet was sent successfully, 0 if there was an error
|
||||
virtual int endPacket() =0;
|
||||
virtual int endPacket() = 0;
|
||||
// Write a single byte into the packet
|
||||
virtual size_t write(uint8_t) =0;
|
||||
virtual size_t write(uint8_t) = 0;
|
||||
// Write size bytes from buffer into the packet
|
||||
virtual size_t write(const uint8_t *buffer, size_t size) =0;
|
||||
virtual size_t write(const uint8_t *buffer, size_t size) = 0;
|
||||
|
||||
// Start processing the next available incoming packet
|
||||
// Returns the size of the packet in bytes, or 0 if no packets are available
|
||||
virtual int parsePacket() =0;
|
||||
virtual int parsePacket() = 0;
|
||||
// Number of bytes remaining in the current packet
|
||||
virtual int available() =0;
|
||||
virtual int available() = 0;
|
||||
// Read a single byte from the current packet
|
||||
virtual int read() =0;
|
||||
virtual int read() = 0;
|
||||
// Read up to len bytes from the current packet and place them into buffer
|
||||
// Returns the number of bytes read, or 0 if none are available
|
||||
virtual int read(unsigned char* buffer, size_t len) =0;
|
||||
virtual int read(unsigned char* buffer, size_t len) = 0;
|
||||
// Read up to len characters from the current packet and place them into buffer
|
||||
// Returns the number of characters read, or 0 if none are available
|
||||
virtual int read(char* buffer, size_t len) =0;
|
||||
virtual int read(char* buffer, size_t len) = 0;
|
||||
// Return the next byte from the current packet without moving on to the next byte
|
||||
virtual int peek() =0;
|
||||
virtual void flush() =0; // Finish reading the current packet
|
||||
virtual int peek() = 0;
|
||||
virtual void flush() = 0; // Finish reading the current packet
|
||||
|
||||
// Return the IP address of the host who sent the current incoming packet
|
||||
virtual IPAddress remoteIP() =0;
|
||||
virtual IPAddress remoteIP() = 0;
|
||||
// Return the port of the host who sent the current incoming packet
|
||||
virtual uint16_t remotePort() =0;
|
||||
protected:
|
||||
uint8_t* rawIPAddress(IPAddress& addr) {
|
||||
virtual uint16_t remotePort() = 0;
|
||||
protected:
|
||||
uint8_t* rawIPAddress(IPAddress& addr)
|
||||
{
|
||||
return addr.raw_address();
|
||||
}
|
||||
#if LWIP_VERSION_MAJOR != 1
|
||||
const uint8_t* rawIPAddress(const IPAddress& addr) {
|
||||
const uint8_t* rawIPAddress(const IPAddress& addr)
|
||||
{
|
||||
return addr.raw_address();
|
||||
}
|
||||
#endif
|
||||
|
@ -8,50 +8,54 @@
|
||||
|
||||
#include <Updater_Signing.h>
|
||||
#ifndef ARDUINO_SIGNING
|
||||
#define ARDUINO_SIGNING 0
|
||||
#define ARDUINO_SIGNING 0
|
||||
#endif
|
||||
|
||||
#if ARDUINO_SIGNING
|
||||
#include "../../libraries/ESP8266WiFi/src/BearSSLHelpers.h"
|
||||
static BearSSL::PublicKey signPubKey(signing_pubkey);
|
||||
static BearSSL::HashSHA256 hash;
|
||||
static BearSSL::SigningVerifier sign(&signPubKey);
|
||||
#include "../../libraries/ESP8266WiFi/src/BearSSLHelpers.h"
|
||||
static BearSSL::PublicKey signPubKey(signing_pubkey);
|
||||
static BearSSL::HashSHA256 hash;
|
||||
static BearSSL::SigningVerifier sign(&signPubKey);
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
#include "c_types.h"
|
||||
#include "spi_flash.h"
|
||||
#include "user_interface.h"
|
||||
#include "c_types.h"
|
||||
#include "spi_flash.h"
|
||||
#include "user_interface.h"
|
||||
}
|
||||
|
||||
extern "C" uint32_t _SPIFFS_start;
|
||||
|
||||
UpdaterClass::UpdaterClass()
|
||||
: _async(false)
|
||||
, _error(0)
|
||||
, _buffer(0)
|
||||
, _bufferLen(0)
|
||||
, _size(0)
|
||||
, _startAddress(0)
|
||||
, _currentAddress(0)
|
||||
, _command(U_FLASH)
|
||||
, _hash(nullptr)
|
||||
, _verify(nullptr)
|
||||
, _progress_callback(nullptr)
|
||||
: _async(false)
|
||||
, _error(0)
|
||||
, _buffer(0)
|
||||
, _bufferLen(0)
|
||||
, _size(0)
|
||||
, _startAddress(0)
|
||||
, _currentAddress(0)
|
||||
, _command(U_FLASH)
|
||||
, _hash(nullptr)
|
||||
, _verify(nullptr)
|
||||
, _progress_callback(nullptr)
|
||||
{
|
||||
#if ARDUINO_SIGNING
|
||||
installSignature(&hash, &sign);
|
||||
#endif
|
||||
}
|
||||
|
||||
UpdaterClass& UpdaterClass::onProgress(THandlerFunction_Progress fn) {
|
||||
UpdaterClass& UpdaterClass::onProgress(THandlerFunction_Progress fn)
|
||||
{
|
||||
_progress_callback = fn;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void UpdaterClass::_reset() {
|
||||
void UpdaterClass::_reset()
|
||||
{
|
||||
if (_buffer)
|
||||
{
|
||||
delete[] _buffer;
|
||||
}
|
||||
_buffer = 0;
|
||||
_bufferLen = 0;
|
||||
_startAddress = 0;
|
||||
@ -59,13 +63,16 @@ void UpdaterClass::_reset() {
|
||||
_size = 0;
|
||||
_command = U_FLASH;
|
||||
|
||||
if(_ledPin != -1) {
|
||||
if (_ledPin != -1)
|
||||
{
|
||||
digitalWrite(_ledPin, !_ledOn); // off
|
||||
}
|
||||
}
|
||||
|
||||
bool UpdaterClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) {
|
||||
if(_size > 0){
|
||||
bool UpdaterClass::begin(size_t size, int command, int ledPin, uint8_t ledOn)
|
||||
{
|
||||
if (_size > 0)
|
||||
{
|
||||
#ifdef DEBUG_UPDATER
|
||||
DEBUG_UPDATER.println(F("[begin] already running"));
|
||||
#endif
|
||||
@ -81,23 +88,27 @@ bool UpdaterClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) {
|
||||
https://github.com/esp8266/Arduino/issues/1017#issuecomment-200605576
|
||||
*/
|
||||
int boot_mode = (GPI >> 16) & 0xf;
|
||||
if (boot_mode == 1) {
|
||||
if (boot_mode == 1)
|
||||
{
|
||||
_setError(UPDATE_ERROR_BOOTSTRAP);
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_UPDATER
|
||||
if (command == U_SPIFFS) {
|
||||
if (command == U_SPIFFS)
|
||||
{
|
||||
DEBUG_UPDATER.println(F("[begin] Update SPIFFS."));
|
||||
}
|
||||
#endif
|
||||
|
||||
if(size == 0) {
|
||||
if (size == 0)
|
||||
{
|
||||
_setError(UPDATE_ERROR_SIZE);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!ESP.checkFlashConfig(false)) {
|
||||
if (!ESP.checkFlashConfig(false))
|
||||
{
|
||||
_setError(UPDATE_ERROR_FLASH_CONFIG);
|
||||
return false;
|
||||
}
|
||||
@ -108,7 +119,8 @@ bool UpdaterClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) {
|
||||
wifi_set_sleep_type(NONE_SLEEP_T);
|
||||
|
||||
uintptr_t updateStartAddress = 0;
|
||||
if (command == U_FLASH) {
|
||||
if (command == U_FLASH)
|
||||
{
|
||||
//size of current sketch rounded to a sector
|
||||
size_t currentSketchSize = (ESP.getSketchSize() + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1));
|
||||
//address of the end of the space available for sketch and update
|
||||
@ -116,7 +128,7 @@ bool UpdaterClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) {
|
||||
//size of the update rounded to a sector
|
||||
size_t roundedSize = (size + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1));
|
||||
//address where we will start writing the update
|
||||
updateStartAddress = (updateEndAddress > roundedSize)? (updateEndAddress - roundedSize) : 0;
|
||||
updateStartAddress = (updateEndAddress > roundedSize) ? (updateEndAddress - roundedSize) : 0;
|
||||
|
||||
#ifdef DEBUG_UPDATER
|
||||
DEBUG_UPDATER.printf_P(PSTR("[begin] roundedSize: 0x%08zX (%zd)\n"), roundedSize, roundedSize);
|
||||
@ -125,15 +137,18 @@ bool UpdaterClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) {
|
||||
#endif
|
||||
|
||||
//make sure that the size of both sketches is less than the total space (updateEndAddress)
|
||||
if(updateStartAddress < currentSketchSize) {
|
||||
if (updateStartAddress < currentSketchSize)
|
||||
{
|
||||
_setError(UPDATE_ERROR_SPACE);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (command == U_SPIFFS) {
|
||||
else if (command == U_SPIFFS)
|
||||
{
|
||||
updateStartAddress = (uintptr_t)&_SPIFFS_start - 0x40200000;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
// unknown command
|
||||
#ifdef DEBUG_UPDATER
|
||||
DEBUG_UPDATER.println(F("[begin] Unknown update command."));
|
||||
@ -145,9 +160,12 @@ bool UpdaterClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) {
|
||||
_startAddress = updateStartAddress;
|
||||
_currentAddress = _startAddress;
|
||||
_size = size;
|
||||
if (ESP.getFreeHeap() > 2 * FLASH_SECTOR_SIZE) {
|
||||
if (ESP.getFreeHeap() > 2 * FLASH_SECTOR_SIZE)
|
||||
{
|
||||
_bufferSize = FLASH_SECTOR_SIZE;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
_bufferSize = 256;
|
||||
}
|
||||
_buffer = new uint8_t[_bufferSize];
|
||||
@ -159,14 +177,16 @@ bool UpdaterClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) {
|
||||
DEBUG_UPDATER.printf_P(PSTR("[begin] _size: 0x%08zX (%zd)\n"), _size, _size);
|
||||
#endif
|
||||
|
||||
if (!_verify) {
|
||||
if (!_verify)
|
||||
{
|
||||
_md5.begin();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UpdaterClass::setMD5(const char * expected_md5){
|
||||
if(strlen(expected_md5) != 32)
|
||||
bool UpdaterClass::setMD5(const char * expected_md5)
|
||||
{
|
||||
if (strlen(expected_md5) != 32)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -174,15 +194,18 @@ bool UpdaterClass::setMD5(const char * expected_md5){
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UpdaterClass::end(bool evenIfRemaining){
|
||||
if(_size == 0){
|
||||
bool UpdaterClass::end(bool evenIfRemaining)
|
||||
{
|
||||
if (_size == 0)
|
||||
{
|
||||
#ifdef DEBUG_UPDATER
|
||||
DEBUG_UPDATER.println(F("no update"));
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
if(hasError() || (!isFinished() && !evenIfRemaining)){
|
||||
if (hasError() || (!isFinished() && !evenIfRemaining))
|
||||
{
|
||||
#ifdef DEBUG_UPDATER
|
||||
DEBUG_UPDATER.printf_P(PSTR("premature end: res:%u, pos:%zu/%zu\n"), getError(), progress(), _size);
|
||||
#endif
|
||||
@ -191,20 +214,24 @@ bool UpdaterClass::end(bool evenIfRemaining){
|
||||
return false;
|
||||
}
|
||||
|
||||
if(evenIfRemaining) {
|
||||
if(_bufferLen > 0) {
|
||||
if (evenIfRemaining)
|
||||
{
|
||||
if (_bufferLen > 0)
|
||||
{
|
||||
_writeBuffer();
|
||||
}
|
||||
_size = progress();
|
||||
}
|
||||
|
||||
uint32_t sigLen = 0;
|
||||
if (_verify) {
|
||||
if (_verify)
|
||||
{
|
||||
ESP.flashRead(_startAddress + _size - sizeof(uint32_t), &sigLen, sizeof(uint32_t));
|
||||
#ifdef DEBUG_UPDATER
|
||||
DEBUG_UPDATER.printf_P(PSTR("[Updater] sigLen: %d\n"), sigLen);
|
||||
#endif
|
||||
if (sigLen != _verify->length()) {
|
||||
if (sigLen != _verify->length())
|
||||
{
|
||||
_setError(UPDATE_ERROR_SIGN);
|
||||
_reset();
|
||||
return false;
|
||||
@ -217,7 +244,8 @@ bool UpdaterClass::end(bool evenIfRemaining){
|
||||
#endif
|
||||
// Calculate the MD5 and hash using proper size
|
||||
uint8_t buff[128];
|
||||
for(int i = 0; i < binSize; i += sizeof(buff)) {
|
||||
for (int i = 0; i < binSize; i += sizeof(buff))
|
||||
{
|
||||
ESP.flashRead(_startAddress + i, (uint32_t *)buff, sizeof(buff));
|
||||
size_t read = std::min((int)sizeof(buff), binSize - i);
|
||||
_hash->add(buff, read);
|
||||
@ -226,11 +254,15 @@ bool UpdaterClass::end(bool evenIfRemaining){
|
||||
#ifdef DEBUG_UPDATER
|
||||
unsigned char *ret = (unsigned char *)_hash->hash();
|
||||
DEBUG_UPDATER.printf_P(PSTR("[Updater] Computed Hash:"));
|
||||
for (int i=0; i<_hash->len(); i++) DEBUG_UPDATER.printf(" %02x", ret[i]);
|
||||
for (int i = 0; i < _hash->len(); i++)
|
||||
{
|
||||
DEBUG_UPDATER.printf(" %02x", ret[i]);
|
||||
}
|
||||
DEBUG_UPDATER.printf("\n");
|
||||
#endif
|
||||
uint8_t *sig = (uint8_t*)malloc(sigLen);
|
||||
if (!sig) {
|
||||
if (!sig)
|
||||
{
|
||||
_setError(UPDATE_ERROR_SIGN);
|
||||
_reset();
|
||||
return false;
|
||||
@ -238,12 +270,14 @@ bool UpdaterClass::end(bool evenIfRemaining){
|
||||
ESP.flashRead(_startAddress + binSize, (uint32_t *)sig, sigLen);
|
||||
#ifdef DEBUG_UPDATER
|
||||
DEBUG_UPDATER.printf_P(PSTR("[Updater] Received Signature:"));
|
||||
for (size_t i=0; i<sigLen; i++) {
|
||||
for (size_t i = 0; i < sigLen; i++)
|
||||
{
|
||||
DEBUG_UPDATER.printf(" %02x", sig[i]);
|
||||
}
|
||||
DEBUG_UPDATER.printf("\n");
|
||||
#endif
|
||||
if (!_verify->verify(_hash, (void *)sig, sigLen)) {
|
||||
if (!_verify->verify(_hash, (void *)sig, sigLen))
|
||||
{
|
||||
_setError(UPDATE_ERROR_SIGN);
|
||||
_reset();
|
||||
return false;
|
||||
@ -251,24 +285,32 @@ bool UpdaterClass::end(bool evenIfRemaining){
|
||||
#ifdef DEBUG_UPDATER
|
||||
DEBUG_UPDATER.printf_P(PSTR("[Updater] Signature matches\n"));
|
||||
#endif
|
||||
} else if (_target_md5.length()) {
|
||||
}
|
||||
else if (_target_md5.length())
|
||||
{
|
||||
_md5.calculate();
|
||||
if (strcasecmp(_target_md5.c_str(), _md5.toString().c_str())) {
|
||||
if (strcasecmp(_target_md5.c_str(), _md5.toString().c_str()))
|
||||
{
|
||||
_setError(UPDATE_ERROR_MD5);
|
||||
_reset();
|
||||
return false;
|
||||
}
|
||||
#ifdef DEBUG_UPDATER
|
||||
else DEBUG_UPDATER.printf_P(PSTR("MD5 Success: %s\n"), _target_md5.c_str());
|
||||
else
|
||||
{
|
||||
DEBUG_UPDATER.printf_P(PSTR("MD5 Success: %s\n"), _target_md5.c_str());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if(!_verifyEnd()) {
|
||||
if (!_verifyEnd())
|
||||
{
|
||||
_reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_command == U_FLASH) {
|
||||
if (_command == U_FLASH)
|
||||
{
|
||||
eboot_command ebcmd;
|
||||
ebcmd.action = ACTION_COPY_RAW;
|
||||
ebcmd.args[0] = _startAddress;
|
||||
@ -279,7 +321,8 @@ bool UpdaterClass::end(bool evenIfRemaining){
|
||||
#ifdef DEBUG_UPDATER
|
||||
DEBUG_UPDATER.printf_P(PSTR("Staged: address:0x%08X, size:0x%08zX\n"), _startAddress, _size);
|
||||
}
|
||||
else if (_command == U_SPIFFS) {
|
||||
else if (_command == U_SPIFFS)
|
||||
{
|
||||
DEBUG_UPDATER.printf_P(PSTR("SPIFFS: address:0x%08X, size:0x%08zX\n"), _startAddress, _size);
|
||||
#endif
|
||||
}
|
||||
@ -288,14 +331,19 @@ bool UpdaterClass::end(bool evenIfRemaining){
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UpdaterClass::_writeBuffer(){
|
||||
#define FLASH_MODE_PAGE 0
|
||||
#define FLASH_MODE_OFFSET 2
|
||||
bool UpdaterClass::_writeBuffer()
|
||||
{
|
||||
#define FLASH_MODE_PAGE 0
|
||||
#define FLASH_MODE_OFFSET 2
|
||||
|
||||
bool eraseResult = true, writeResult = true;
|
||||
if (_currentAddress % FLASH_SECTOR_SIZE == 0) {
|
||||
if(!_async) yield();
|
||||
eraseResult = ESP.flashEraseSector(_currentAddress/FLASH_SECTOR_SIZE);
|
||||
if (_currentAddress % FLASH_SECTOR_SIZE == 0)
|
||||
{
|
||||
if (!_async)
|
||||
{
|
||||
yield();
|
||||
}
|
||||
eraseResult = ESP.flashEraseSector(_currentAddress / FLASH_SECTOR_SIZE);
|
||||
}
|
||||
|
||||
// If the flash settings don't match what we already have, modify them.
|
||||
@ -304,26 +352,34 @@ bool UpdaterClass::_writeBuffer(){
|
||||
bool modifyFlashMode = false;
|
||||
FlashMode_t flashMode = FM_QIO;
|
||||
FlashMode_t bufferFlashMode = FM_QIO;
|
||||
if (_currentAddress == _startAddress + FLASH_MODE_PAGE) {
|
||||
if (_currentAddress == _startAddress + FLASH_MODE_PAGE)
|
||||
{
|
||||
flashMode = ESP.getFlashChipMode();
|
||||
#ifdef DEBUG_UPDATER
|
||||
#ifdef DEBUG_UPDATER
|
||||
DEBUG_UPDATER.printf_P(PSTR("Header: 0x%1X %1X %1X %1X\n"), _buffer[0], _buffer[1], _buffer[2], _buffer[3]);
|
||||
#endif
|
||||
#endif
|
||||
bufferFlashMode = ESP.magicFlashChipMode(_buffer[FLASH_MODE_OFFSET]);
|
||||
if (bufferFlashMode != flashMode) {
|
||||
#ifdef DEBUG_UPDATER
|
||||
if (bufferFlashMode != flashMode)
|
||||
{
|
||||
#ifdef DEBUG_UPDATER
|
||||
DEBUG_UPDATER.printf_P(PSTR("Set flash mode from 0x%1X to 0x%1X\n"), bufferFlashMode, flashMode);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
_buffer[FLASH_MODE_OFFSET] = flashMode;
|
||||
modifyFlashMode = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (eraseResult) {
|
||||
if(!_async) yield();
|
||||
if (eraseResult)
|
||||
{
|
||||
if (!_async)
|
||||
{
|
||||
yield();
|
||||
}
|
||||
writeResult = ESP.flashWrite(_currentAddress, (uint32_t*) _buffer, _bufferLen);
|
||||
} else { // if erase was unsuccessful
|
||||
}
|
||||
else // if erase was unsuccessful
|
||||
{
|
||||
_currentAddress = (_startAddress + _size);
|
||||
_setError(UPDATE_ERROR_ERASE);
|
||||
return false;
|
||||
@ -331,16 +387,19 @@ bool UpdaterClass::_writeBuffer(){
|
||||
|
||||
// Restore the old flash mode, if we modified it.
|
||||
// Ensures that the MD5 hash will still match what was sent.
|
||||
if (modifyFlashMode) {
|
||||
if (modifyFlashMode)
|
||||
{
|
||||
_buffer[FLASH_MODE_OFFSET] = bufferFlashMode;
|
||||
}
|
||||
|
||||
if (!writeResult) {
|
||||
if (!writeResult)
|
||||
{
|
||||
_currentAddress = (_startAddress + _size);
|
||||
_setError(UPDATE_ERROR_WRITE);
|
||||
return false;
|
||||
}
|
||||
if (!_verify) {
|
||||
if (!_verify)
|
||||
{
|
||||
_md5.add(_buffer, _bufferLen);
|
||||
}
|
||||
_currentAddress += _bufferLen;
|
||||
@ -348,11 +407,15 @@ bool UpdaterClass::_writeBuffer(){
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t UpdaterClass::write(uint8_t *data, size_t len) {
|
||||
if(hasError() || !isRunning())
|
||||
size_t UpdaterClass::write(uint8_t *data, size_t len)
|
||||
{
|
||||
if (hasError() || !isRunning())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(len > remaining()){
|
||||
if (len > remaining())
|
||||
{
|
||||
//len = remaining();
|
||||
//fail instead
|
||||
_setError(UPDATE_ERROR_SPACE);
|
||||
@ -361,56 +424,72 @@ size_t UpdaterClass::write(uint8_t *data, size_t len) {
|
||||
|
||||
size_t left = len;
|
||||
|
||||
while((_bufferLen + left) > _bufferSize) {
|
||||
while ((_bufferLen + left) > _bufferSize)
|
||||
{
|
||||
size_t toBuff = _bufferSize - _bufferLen;
|
||||
memcpy(_buffer + _bufferLen, data + (len - left), toBuff);
|
||||
_bufferLen += toBuff;
|
||||
if(!_writeBuffer()){
|
||||
if (!_writeBuffer())
|
||||
{
|
||||
return len - left;
|
||||
}
|
||||
left -= toBuff;
|
||||
if(!_async) yield();
|
||||
if (!_async)
|
||||
{
|
||||
yield();
|
||||
}
|
||||
}
|
||||
//lets see whats left
|
||||
memcpy(_buffer + _bufferLen, data + (len - left), left);
|
||||
_bufferLen += left;
|
||||
if(_bufferLen == remaining()){
|
||||
if (_bufferLen == remaining())
|
||||
{
|
||||
//we are at the end of the update, so should write what's left to flash
|
||||
if(!_writeBuffer()){
|
||||
if (!_writeBuffer())
|
||||
{
|
||||
return len - left;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
bool UpdaterClass::_verifyHeader(uint8_t data) {
|
||||
if(_command == U_FLASH) {
|
||||
bool UpdaterClass::_verifyHeader(uint8_t data)
|
||||
{
|
||||
if (_command == U_FLASH)
|
||||
{
|
||||
// check for valid first magic byte (is always 0xE9)
|
||||
if(data != 0xE9) {
|
||||
if (data != 0xE9)
|
||||
{
|
||||
_currentAddress = (_startAddress + _size);
|
||||
_setError(UPDATE_ERROR_MAGIC_BYTE);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else if(_command == U_SPIFFS) {
|
||||
}
|
||||
else if (_command == U_SPIFFS)
|
||||
{
|
||||
// no check of SPIFFS possible with first byte.
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UpdaterClass::_verifyEnd() {
|
||||
if(_command == U_FLASH) {
|
||||
bool UpdaterClass::_verifyEnd()
|
||||
{
|
||||
if (_command == U_FLASH)
|
||||
{
|
||||
|
||||
uint8_t buf[4];
|
||||
if(!ESP.flashRead(_startAddress, (uint32_t *) &buf[0], 4)) {
|
||||
if (!ESP.flashRead(_startAddress, (uint32_t *) &buf[0], 4))
|
||||
{
|
||||
_currentAddress = (_startAddress);
|
||||
_setError(UPDATE_ERROR_READ);
|
||||
return false;
|
||||
}
|
||||
|
||||
// check for valid first magic byte
|
||||
if(buf[0] != 0xE9) {
|
||||
if (buf[0] != 0xE9)
|
||||
{
|
||||
_currentAddress = (_startAddress);
|
||||
_setError(UPDATE_ERROR_MAGIC_BYTE);
|
||||
return false;
|
||||
@ -419,113 +498,161 @@ bool UpdaterClass::_verifyEnd() {
|
||||
uint32_t bin_flash_size = ESP.magicFlashChipSize((buf[3] & 0xf0) >> 4);
|
||||
|
||||
// check if new bin fits to SPI flash
|
||||
if(bin_flash_size > ESP.getFlashChipRealSize()) {
|
||||
if (bin_flash_size > ESP.getFlashChipRealSize())
|
||||
{
|
||||
_currentAddress = (_startAddress);
|
||||
_setError(UPDATE_ERROR_NEW_FLASH_CONFIG);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
} else if(_command == U_SPIFFS) {
|
||||
}
|
||||
else if (_command == U_SPIFFS)
|
||||
{
|
||||
// SPIFFS is already over written checks make no sense any more.
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t UpdaterClass::writeStream(Stream &data) {
|
||||
size_t UpdaterClass::writeStream(Stream &data)
|
||||
{
|
||||
size_t written = 0;
|
||||
size_t toRead = 0;
|
||||
if(hasError() || !isRunning())
|
||||
if (hasError() || !isRunning())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(!_verifyHeader(data.peek())) {
|
||||
if (!_verifyHeader(data.peek()))
|
||||
{
|
||||
#ifdef DEBUG_UPDATER
|
||||
printError(DEBUG_UPDATER);
|
||||
#endif
|
||||
_reset();
|
||||
return 0;
|
||||
}
|
||||
if (_progress_callback) {
|
||||
if (_progress_callback)
|
||||
{
|
||||
_progress_callback(0, _size);
|
||||
}
|
||||
if(_ledPin != -1) {
|
||||
if (_ledPin != -1)
|
||||
{
|
||||
pinMode(_ledPin, OUTPUT);
|
||||
}
|
||||
|
||||
while(remaining()) {
|
||||
if(_ledPin != -1) {
|
||||
while (remaining())
|
||||
{
|
||||
if (_ledPin != -1)
|
||||
{
|
||||
digitalWrite(_ledPin, _ledOn); // Switch LED on
|
||||
}
|
||||
size_t bytesToRead = _bufferSize - _bufferLen;
|
||||
if(bytesToRead > remaining()) {
|
||||
if (bytesToRead > remaining())
|
||||
{
|
||||
bytesToRead = remaining();
|
||||
}
|
||||
toRead = data.readBytes(_buffer + _bufferLen, bytesToRead);
|
||||
if(toRead == 0) { //Timeout
|
||||
if (toRead == 0) //Timeout
|
||||
{
|
||||
delay(100);
|
||||
toRead = data.readBytes(_buffer + _bufferLen, bytesToRead);
|
||||
if(toRead == 0) { //Timeout
|
||||
if (toRead == 0) //Timeout
|
||||
{
|
||||
_currentAddress = (_startAddress + _size);
|
||||
_setError(UPDATE_ERROR_STREAM);
|
||||
_reset();
|
||||
return written;
|
||||
}
|
||||
}
|
||||
if(_ledPin != -1) {
|
||||
if (_ledPin != -1)
|
||||
{
|
||||
digitalWrite(_ledPin, !_ledOn); // Switch LED off
|
||||
}
|
||||
_bufferLen += toRead;
|
||||
if((_bufferLen == remaining() || _bufferLen == _bufferSize) && !_writeBuffer())
|
||||
if ((_bufferLen == remaining() || _bufferLen == _bufferSize) && !_writeBuffer())
|
||||
{
|
||||
return written;
|
||||
}
|
||||
written += toRead;
|
||||
if(_progress_callback) {
|
||||
if (_progress_callback)
|
||||
{
|
||||
_progress_callback(progress(), _size);
|
||||
}
|
||||
yield();
|
||||
}
|
||||
if(_progress_callback) {
|
||||
if (_progress_callback)
|
||||
{
|
||||
_progress_callback(progress(), _size);
|
||||
}
|
||||
return written;
|
||||
}
|
||||
|
||||
void UpdaterClass::_setError(int error){
|
||||
void UpdaterClass::_setError(int error)
|
||||
{
|
||||
_error = error;
|
||||
#ifdef DEBUG_UPDATER
|
||||
printError(DEBUG_UPDATER);
|
||||
#endif
|
||||
}
|
||||
|
||||
void UpdaterClass::printError(Print &out){
|
||||
void UpdaterClass::printError(Print &out)
|
||||
{
|
||||
out.printf_P(PSTR("ERROR[%u]: "), _error);
|
||||
if(_error == UPDATE_ERROR_OK){
|
||||
if (_error == UPDATE_ERROR_OK)
|
||||
{
|
||||
out.println(F("No Error"));
|
||||
} else if(_error == UPDATE_ERROR_WRITE){
|
||||
}
|
||||
else if (_error == UPDATE_ERROR_WRITE)
|
||||
{
|
||||
out.println(F("Flash Write Failed"));
|
||||
} else if(_error == UPDATE_ERROR_ERASE){
|
||||
}
|
||||
else if (_error == UPDATE_ERROR_ERASE)
|
||||
{
|
||||
out.println(F("Flash Erase Failed"));
|
||||
} else if(_error == UPDATE_ERROR_READ){
|
||||
}
|
||||
else if (_error == UPDATE_ERROR_READ)
|
||||
{
|
||||
out.println(F("Flash Read Failed"));
|
||||
} else if(_error == UPDATE_ERROR_SPACE){
|
||||
}
|
||||
else if (_error == UPDATE_ERROR_SPACE)
|
||||
{
|
||||
out.println(F("Not Enough Space"));
|
||||
} else if(_error == UPDATE_ERROR_SIZE){
|
||||
}
|
||||
else if (_error == UPDATE_ERROR_SIZE)
|
||||
{
|
||||
out.println(F("Bad Size Given"));
|
||||
} else if(_error == UPDATE_ERROR_STREAM){
|
||||
}
|
||||
else if (_error == UPDATE_ERROR_STREAM)
|
||||
{
|
||||
out.println(F("Stream Read Timeout"));
|
||||
} else if(_error == UPDATE_ERROR_MD5){
|
||||
}
|
||||
else if (_error == UPDATE_ERROR_MD5)
|
||||
{
|
||||
out.printf_P(PSTR("MD5 Failed: expected:%s, calculated:%s\n"), _target_md5.c_str(), _md5.toString().c_str());
|
||||
} else if(_error == UPDATE_ERROR_SIGN){
|
||||
}
|
||||
else if (_error == UPDATE_ERROR_SIGN)
|
||||
{
|
||||
out.println(F("Signature verification failed"));
|
||||
} else if(_error == UPDATE_ERROR_FLASH_CONFIG){
|
||||
}
|
||||
else if (_error == UPDATE_ERROR_FLASH_CONFIG)
|
||||
{
|
||||
out.printf_P(PSTR("Flash config wrong real: %d IDE: %d\n"), ESP.getFlashChipRealSize(), ESP.getFlashChipSize());
|
||||
} else if(_error == UPDATE_ERROR_NEW_FLASH_CONFIG){
|
||||
}
|
||||
else if (_error == UPDATE_ERROR_NEW_FLASH_CONFIG)
|
||||
{
|
||||
out.printf_P(PSTR("new Flash config wrong real: %d\n"), ESP.getFlashChipRealSize());
|
||||
} else if(_error == UPDATE_ERROR_MAGIC_BYTE){
|
||||
}
|
||||
else if (_error == UPDATE_ERROR_MAGIC_BYTE)
|
||||
{
|
||||
out.println(F("Magic byte is wrong, not 0xE9"));
|
||||
} else if (_error == UPDATE_ERROR_BOOTSTRAP){
|
||||
}
|
||||
else if (_error == UPDATE_ERROR_BOOTSTRAP)
|
||||
{
|
||||
out.println(F("Invalid bootstrapping state, reset ESP8266 before updating"));
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
out.println(F("UNKNOWN"));
|
||||
}
|
||||
}
|
||||
|
@ -31,8 +31,9 @@
|
||||
#endif
|
||||
|
||||
// Abstract class to implement whatever signing hash desired
|
||||
class UpdaterHashClass {
|
||||
public:
|
||||
class UpdaterHashClass
|
||||
{
|
||||
public:
|
||||
virtual void begin() = 0;
|
||||
virtual void add(const void *data, uint32_t len) = 0;
|
||||
virtual void end() = 0;
|
||||
@ -41,20 +42,26 @@ class UpdaterHashClass {
|
||||
};
|
||||
|
||||
// Abstract class to implement a signature verifier
|
||||
class UpdaterVerifyClass {
|
||||
public:
|
||||
class UpdaterVerifyClass
|
||||
{
|
||||
public:
|
||||
virtual uint32_t length() = 0; // How many bytes of signature are expected
|
||||
virtual bool verify(UpdaterHashClass *hash, const void *signature, uint32_t signatureLen) = 0; // Verify, return "true" on success
|
||||
};
|
||||
|
||||
class UpdaterClass {
|
||||
public:
|
||||
class UpdaterClass
|
||||
{
|
||||
public:
|
||||
typedef std::function<void(size_t, size_t)> THandlerFunction_Progress;
|
||||
|
||||
UpdaterClass();
|
||||
|
||||
/* Optionally add a cryptographic signature verification hash and method */
|
||||
void installSignature(UpdaterHashClass *hash, UpdaterVerifyClass *verify) { _hash = hash; _verify = verify; }
|
||||
void installSignature(UpdaterHashClass *hash, UpdaterVerifyClass *verify)
|
||||
{
|
||||
_hash = hash;
|
||||
_verify = verify;
|
||||
}
|
||||
|
||||
/*
|
||||
Call this to check the space needed for the update
|
||||
@ -65,7 +72,10 @@ class UpdaterClass {
|
||||
/*
|
||||
Run Updater from asynchronous callbacs
|
||||
*/
|
||||
void runAsync(bool async){ _async = async; }
|
||||
void runAsync(bool async)
|
||||
{
|
||||
_async = async;
|
||||
}
|
||||
|
||||
/*
|
||||
Writes a buffer to the flash and increments the address
|
||||
@ -107,12 +117,18 @@ class UpdaterClass {
|
||||
/*
|
||||
returns the MD5 String of the sucessfully ended firmware
|
||||
*/
|
||||
String md5String(void){ return _md5.toString(); }
|
||||
String md5String(void)
|
||||
{
|
||||
return _md5.toString();
|
||||
}
|
||||
|
||||
/*
|
||||
populated the result with the md5 bytes of the sucessfully ended firmware
|
||||
*/
|
||||
void md5(uint8_t * result){ return _md5.getBytes(result); }
|
||||
void md5(uint8_t * result)
|
||||
{
|
||||
return _md5.getBytes(result);
|
||||
}
|
||||
|
||||
/*
|
||||
This callback will be called when Updater is receiving data
|
||||
@ -120,14 +136,38 @@ class UpdaterClass {
|
||||
UpdaterClass& onProgress(THandlerFunction_Progress fn);
|
||||
|
||||
//Helpers
|
||||
uint8_t getError(){ return _error; }
|
||||
void clearError(){ _error = UPDATE_ERROR_OK; }
|
||||
bool hasError(){ return _error != UPDATE_ERROR_OK; }
|
||||
bool isRunning(){ return _size > 0; }
|
||||
bool isFinished(){ return _currentAddress == (_startAddress + _size); }
|
||||
size_t size(){ return _size; }
|
||||
size_t progress(){ return _currentAddress - _startAddress; }
|
||||
size_t remaining(){ return _size - (_currentAddress - _startAddress); }
|
||||
uint8_t getError()
|
||||
{
|
||||
return _error;
|
||||
}
|
||||
void clearError()
|
||||
{
|
||||
_error = UPDATE_ERROR_OK;
|
||||
}
|
||||
bool hasError()
|
||||
{
|
||||
return _error != UPDATE_ERROR_OK;
|
||||
}
|
||||
bool isRunning()
|
||||
{
|
||||
return _size > 0;
|
||||
}
|
||||
bool isFinished()
|
||||
{
|
||||
return _currentAddress == (_startAddress + _size);
|
||||
}
|
||||
size_t size()
|
||||
{
|
||||
return _size;
|
||||
}
|
||||
size_t progress()
|
||||
{
|
||||
return _currentAddress - _startAddress;
|
||||
}
|
||||
size_t remaining()
|
||||
{
|
||||
return _size - (_currentAddress - _startAddress);
|
||||
}
|
||||
|
||||
/*
|
||||
Template to write from objects that expose
|
||||
@ -136,42 +176,56 @@ class UpdaterClass {
|
||||
writes only what is available
|
||||
*/
|
||||
template<typename T>
|
||||
size_t write(T &data){
|
||||
size_t write(T &data)
|
||||
{
|
||||
size_t written = 0;
|
||||
if (hasError() || !isRunning())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t available = data.available();
|
||||
while(available) {
|
||||
if(_bufferLen + available > remaining()){
|
||||
while (available)
|
||||
{
|
||||
if (_bufferLen + available > remaining())
|
||||
{
|
||||
available = remaining() - _bufferLen;
|
||||
}
|
||||
if(_bufferLen + available > _bufferSize) {
|
||||
if (_bufferLen + available > _bufferSize)
|
||||
{
|
||||
size_t toBuff = _bufferSize - _bufferLen;
|
||||
data.read(_buffer + _bufferLen, toBuff);
|
||||
_bufferLen += toBuff;
|
||||
if(!_writeBuffer())
|
||||
if (!_writeBuffer())
|
||||
{
|
||||
return written;
|
||||
}
|
||||
written += toBuff;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
data.read(_buffer + _bufferLen, available);
|
||||
_bufferLen += available;
|
||||
written += available;
|
||||
if(_bufferLen == remaining()) {
|
||||
if(!_writeBuffer()) {
|
||||
if (_bufferLen == remaining())
|
||||
{
|
||||
if (!_writeBuffer())
|
||||
{
|
||||
return written;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(remaining() == 0)
|
||||
if (remaining() == 0)
|
||||
{
|
||||
return written;
|
||||
}
|
||||
delay(1);
|
||||
available = data.available();
|
||||
}
|
||||
return written;
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
void _reset();
|
||||
bool _writeBuffer();
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
*/
|
||||
|
||||
#ifndef Character_h
|
||||
#define Character_h
|
||||
@ -46,79 +46,93 @@ inline int toUpperCase(int c) __attribute__((always_inline));
|
||||
|
||||
// Checks for an alphanumeric character.
|
||||
// It is equivalent to (isalpha(c) || isdigit(c)).
|
||||
inline boolean isAlphaNumeric(int c) {
|
||||
inline boolean isAlphaNumeric(int c)
|
||||
{
|
||||
return (isalnum(c) == 0 ? false : true);
|
||||
}
|
||||
|
||||
// Checks for an alphabetic character.
|
||||
// It is equivalent to (isupper(c) || islower(c)).
|
||||
inline boolean isAlpha(int c) {
|
||||
inline boolean isAlpha(int c)
|
||||
{
|
||||
return (isalpha(c) == 0 ? false : true);
|
||||
}
|
||||
|
||||
// Checks whether c is a 7-bit unsigned char value
|
||||
// that fits into the ASCII character set.
|
||||
inline boolean isAscii(int c) {
|
||||
return ( isascii (c) == 0 ? false : true);
|
||||
inline boolean isAscii(int c)
|
||||
{
|
||||
return (isascii(c) == 0 ? false : true);
|
||||
}
|
||||
|
||||
// Checks for a blank character, that is, a space or a tab.
|
||||
inline boolean isWhitespace(int c) {
|
||||
inline boolean isWhitespace(int c)
|
||||
{
|
||||
return (isblank(c) == 0 ? false : true);
|
||||
}
|
||||
|
||||
// Checks for a control character.
|
||||
inline boolean isControl(int c) {
|
||||
inline boolean isControl(int c)
|
||||
{
|
||||
return (iscntrl(c) == 0 ? false : true);
|
||||
}
|
||||
|
||||
// Checks for a digit (0 through 9).
|
||||
inline boolean isDigit(int c) {
|
||||
inline boolean isDigit(int c)
|
||||
{
|
||||
return (isdigit(c) == 0 ? false : true);
|
||||
}
|
||||
|
||||
// Checks for any printable character except space.
|
||||
inline boolean isGraph(int c) {
|
||||
inline boolean isGraph(int c)
|
||||
{
|
||||
return (isgraph(c) == 0 ? false : true);
|
||||
}
|
||||
|
||||
// Checks for a lower-case character.
|
||||
inline boolean isLowerCase(int c) {
|
||||
inline boolean isLowerCase(int c)
|
||||
{
|
||||
return (islower(c) == 0 ? false : true);
|
||||
}
|
||||
|
||||
// Checks for any printable character including space.
|
||||
inline boolean isPrintable(int c) {
|
||||
inline boolean isPrintable(int c)
|
||||
{
|
||||
return (isprint(c) == 0 ? false : true);
|
||||
}
|
||||
|
||||
// Checks for any printable character which is not a space
|
||||
// or an alphanumeric character.
|
||||
inline boolean isPunct(int c) {
|
||||
inline boolean isPunct(int c)
|
||||
{
|
||||
return (ispunct(c) == 0 ? false : true);
|
||||
}
|
||||
|
||||
// Checks for white-space characters. For the avr-libc library,
|
||||
// these are: space, formfeed ('\f'), newline ('\n'), carriage
|
||||
// return ('\r'), horizontal tab ('\t'), and vertical tab ('\v').
|
||||
inline boolean isSpace(int c) {
|
||||
inline boolean isSpace(int c)
|
||||
{
|
||||
return (isspace(c) == 0 ? false : true);
|
||||
}
|
||||
|
||||
// Checks for an uppercase letter.
|
||||
inline boolean isUpperCase(int c) {
|
||||
inline boolean isUpperCase(int c)
|
||||
{
|
||||
return (isupper(c) == 0 ? false : true);
|
||||
}
|
||||
|
||||
// Checks for a hexadecimal digits, i.e. one of 0 1 2 3 4 5 6 7
|
||||
// 8 9 a b c d e f A B C D E F.
|
||||
inline boolean isHexadecimalDigit(int c) {
|
||||
inline boolean isHexadecimalDigit(int c)
|
||||
{
|
||||
return (isxdigit(c) == 0 ? false : true);
|
||||
}
|
||||
|
||||
// Converts c to a 7-bit unsigned char value that fits into the
|
||||
// ASCII character set, by clearing the high-order bits.
|
||||
inline int toAscii(int c) {
|
||||
inline int toAscii(int c)
|
||||
{
|
||||
return toascii(c);
|
||||
}
|
||||
|
||||
@ -128,12 +142,14 @@ inline int toAscii(int c) {
|
||||
// characters.
|
||||
|
||||
// Converts the letter c to lower case, if possible.
|
||||
inline int toLowerCase(int c) {
|
||||
inline int toLowerCase(int c)
|
||||
{
|
||||
return tolower(c);
|
||||
}
|
||||
|
||||
// Converts the letter c to upper case, if possible.
|
||||
inline int toUpperCase(int c) {
|
||||
inline int toUpperCase(int c)
|
||||
{
|
||||
return toupper(c);
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@
|
||||
Boston, MA 02111-1307 USA
|
||||
|
||||
$Id$
|
||||
*/
|
||||
*/
|
||||
|
||||
extern "C" {
|
||||
#include <stdlib.h>
|
||||
@ -30,15 +30,19 @@ extern "C" {
|
||||
|
||||
static bool s_randomSeedCalled = false;
|
||||
|
||||
void randomSeed(unsigned long seed) {
|
||||
if(seed != 0) {
|
||||
void randomSeed(unsigned long seed)
|
||||
{
|
||||
if (seed != 0)
|
||||
{
|
||||
srand(seed);
|
||||
s_randomSeedCalled = true;
|
||||
}
|
||||
}
|
||||
|
||||
long random(long howbig) {
|
||||
if(howbig == 0) {
|
||||
long random(long howbig)
|
||||
{
|
||||
if (howbig == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
// if randomSeed was called, fall back to software PRNG
|
||||
@ -46,41 +50,51 @@ long random(long howbig) {
|
||||
return val % howbig;
|
||||
}
|
||||
|
||||
long random(long howsmall, long howbig) {
|
||||
if(howsmall >= howbig) {
|
||||
long random(long howsmall, long howbig)
|
||||
{
|
||||
if (howsmall >= howbig)
|
||||
{
|
||||
return howsmall;
|
||||
}
|
||||
long diff = howbig - howsmall;
|
||||
return random(diff) + howsmall;
|
||||
}
|
||||
|
||||
long secureRandom(long howbig) {
|
||||
if(howbig == 0) {
|
||||
long secureRandom(long howbig)
|
||||
{
|
||||
if (howbig == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return RANDOM_REG32 % howbig;
|
||||
}
|
||||
|
||||
long secureRandom(long howsmall, long howbig) {
|
||||
if(howsmall >= howbig) {
|
||||
long secureRandom(long howsmall, long howbig)
|
||||
{
|
||||
if (howsmall >= howbig)
|
||||
{
|
||||
return howsmall;
|
||||
}
|
||||
long diff = howbig - howsmall;
|
||||
return secureRandom(diff) + howsmall;
|
||||
}
|
||||
|
||||
long map(long x, long in_min, long in_max, long out_min, long out_max) {
|
||||
long map(long x, long in_min, long in_max, long out_min, long out_max)
|
||||
{
|
||||
long divisor = (in_max - in_min);
|
||||
if(divisor == 0){
|
||||
if (divisor == 0)
|
||||
{
|
||||
return -1; //AVR returns -1, SAM returns 0
|
||||
}
|
||||
return (x - in_min) * (out_max - out_min) / divisor + out_min;
|
||||
}
|
||||
|
||||
unsigned int makeWord(unsigned int w) {
|
||||
unsigned int makeWord(unsigned int w)
|
||||
{
|
||||
return w;
|
||||
}
|
||||
|
||||
unsigned int makeWord(unsigned char h, unsigned char l) {
|
||||
unsigned int makeWord(unsigned char h, unsigned char l)
|
||||
{
|
||||
return (h << 8) | l;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -17,7 +17,7 @@
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
*/
|
||||
|
||||
#ifndef String_class_h
|
||||
#define String_class_h
|
||||
@ -39,15 +39,17 @@ class __FlashStringHelper;
|
||||
#define F(string_literal) (FPSTR(PSTR(string_literal)))
|
||||
|
||||
// The string class
|
||||
class String {
|
||||
class String
|
||||
{
|
||||
// use a function pointer to allow for "if (s)" without the
|
||||
// complications of an operator bool(). for more information, see:
|
||||
// http://www.artima.com/cppsource/safebool.html
|
||||
typedef void (String::*StringIfHelperType)() const;
|
||||
void StringIfHelper() const {
|
||||
void StringIfHelper() const
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
public:
|
||||
// constructors
|
||||
// creates a copy of the initial value.
|
||||
// if the initial value is null or invalid, or if memory allocation
|
||||
@ -75,10 +77,14 @@ class String {
|
||||
// is left unchanged). reserve(0), if successful, will validate an
|
||||
// invalid string (i.e., "if (s)" will be true afterwards)
|
||||
unsigned char reserve(unsigned int size);
|
||||
inline unsigned int length(void) const {
|
||||
if(buffer()) {
|
||||
inline unsigned int length(void) const
|
||||
{
|
||||
if (buffer())
|
||||
{
|
||||
return len();
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -113,47 +119,58 @@ class String {
|
||||
|
||||
// if there's not enough memory for the concatenated value, the string
|
||||
// will be left unchanged (but this isn't signalled in any way)
|
||||
String & operator +=(const String &rhs) {
|
||||
String & operator +=(const String &rhs)
|
||||
{
|
||||
concat(rhs);
|
||||
return (*this);
|
||||
}
|
||||
String & operator +=(const char *cstr) {
|
||||
String & operator +=(const char *cstr)
|
||||
{
|
||||
concat(cstr);
|
||||
return (*this);
|
||||
}
|
||||
String & operator +=(char c) {
|
||||
String & operator +=(char c)
|
||||
{
|
||||
concat(c);
|
||||
return (*this);
|
||||
}
|
||||
String & operator +=(unsigned char num) {
|
||||
String & operator +=(unsigned char num)
|
||||
{
|
||||
concat(num);
|
||||
return (*this);
|
||||
}
|
||||
String & operator +=(int num) {
|
||||
String & operator +=(int num)
|
||||
{
|
||||
concat(num);
|
||||
return (*this);
|
||||
}
|
||||
String & operator +=(unsigned int num) {
|
||||
String & operator +=(unsigned int num)
|
||||
{
|
||||
concat(num);
|
||||
return (*this);
|
||||
}
|
||||
String & operator +=(long num) {
|
||||
String & operator +=(long num)
|
||||
{
|
||||
concat(num);
|
||||
return (*this);
|
||||
}
|
||||
String & operator +=(unsigned long num) {
|
||||
String & operator +=(unsigned long num)
|
||||
{
|
||||
concat(num);
|
||||
return (*this);
|
||||
}
|
||||
String & operator +=(float num) {
|
||||
String & operator +=(float num)
|
||||
{
|
||||
concat(num);
|
||||
return (*this);
|
||||
}
|
||||
String & operator +=(double num) {
|
||||
String & operator +=(double num)
|
||||
{
|
||||
concat(num);
|
||||
return (*this);
|
||||
}
|
||||
String & operator += (const __FlashStringHelper *str){
|
||||
String & operator += (const __FlashStringHelper *str)
|
||||
{
|
||||
concat(str);
|
||||
return (*this);
|
||||
}
|
||||
@ -171,22 +188,27 @@ class String {
|
||||
friend StringSumHelper & operator +(const StringSumHelper &lhs, const __FlashStringHelper *rhs);
|
||||
|
||||
// comparison (only works w/ Strings and "strings")
|
||||
operator StringIfHelperType() const {
|
||||
operator StringIfHelperType() const
|
||||
{
|
||||
return buffer() ? &String::StringIfHelper : 0;
|
||||
}
|
||||
int compareTo(const String &s) const;
|
||||
unsigned char equals(const String &s) const;
|
||||
unsigned char equals(const char *cstr) const;
|
||||
unsigned char operator ==(const String &rhs) const {
|
||||
unsigned char operator ==(const String &rhs) const
|
||||
{
|
||||
return equals(rhs);
|
||||
}
|
||||
unsigned char operator ==(const char *cstr) const {
|
||||
unsigned char operator ==(const char *cstr) const
|
||||
{
|
||||
return equals(cstr);
|
||||
}
|
||||
unsigned char operator !=(const String &rhs) const {
|
||||
unsigned char operator !=(const String &rhs) const
|
||||
{
|
||||
return !equals(rhs);
|
||||
}
|
||||
unsigned char operator !=(const char *cstr) const {
|
||||
unsigned char operator !=(const char *cstr) const
|
||||
{
|
||||
return !equals(cstr);
|
||||
}
|
||||
unsigned char operator <(const String &rhs) const;
|
||||
@ -205,14 +227,30 @@ class String {
|
||||
char operator [](unsigned int index) const;
|
||||
char& operator [](unsigned int index);
|
||||
void getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index = 0) const;
|
||||
void toCharArray(char *buf, unsigned int bufsize, unsigned int index = 0) const {
|
||||
void toCharArray(char *buf, unsigned int bufsize, unsigned int index = 0) const
|
||||
{
|
||||
getBytes((unsigned char *) buf, bufsize, index);
|
||||
}
|
||||
const char* c_str() const { return buffer(); }
|
||||
char* begin() { return wbuffer(); }
|
||||
char* end() { return wbuffer() + length(); }
|
||||
const char* begin() const { return c_str(); }
|
||||
const char* end() const { return c_str() + length(); }
|
||||
const char* c_str() const
|
||||
{
|
||||
return buffer();
|
||||
}
|
||||
char* begin()
|
||||
{
|
||||
return wbuffer();
|
||||
}
|
||||
char* end()
|
||||
{
|
||||
return wbuffer() + length();
|
||||
}
|
||||
const char* begin() const
|
||||
{
|
||||
return c_str();
|
||||
}
|
||||
const char* end() const
|
||||
{
|
||||
return c_str() + length();
|
||||
}
|
||||
|
||||
// search
|
||||
int indexOf(char ch) const;
|
||||
@ -223,7 +261,8 @@ class String {
|
||||
int lastIndexOf(char ch, unsigned int fromIndex) const;
|
||||
int lastIndexOf(const String &str) const;
|
||||
int lastIndexOf(const String &str, unsigned int fromIndex) const;
|
||||
String substring(unsigned int beginIndex) const {
|
||||
String substring(unsigned int beginIndex) const
|
||||
{
|
||||
return substring(beginIndex, len());
|
||||
}
|
||||
;
|
||||
@ -243,9 +282,10 @@ class String {
|
||||
float toFloat(void) const;
|
||||
double toDouble(void) const;
|
||||
|
||||
protected:
|
||||
protected:
|
||||
// Contains the string info when we're not in SSO mode
|
||||
struct _ptr {
|
||||
struct _ptr
|
||||
{
|
||||
char * buff;
|
||||
uint16_t cap;
|
||||
uint16_t len;
|
||||
@ -256,23 +296,60 @@ class String {
|
||||
// This allows strings up up to 12 (11 + \0 termination) without any extra space.
|
||||
enum { SSOSIZE = sizeof(struct _ptr) + 4 }; // Characters to allocate space for SSO, must be 12 or more
|
||||
enum { CAPACITY_MAX = 65535 }; // If size of capacity changed, be sure to update this enum
|
||||
union {
|
||||
union
|
||||
{
|
||||
struct _ptr ptr;
|
||||
char sso_buf[SSOSIZE];
|
||||
};
|
||||
// Accessor functions
|
||||
inline bool sso() const { return sso_buf[SSOSIZE - 1] == 0; }
|
||||
inline unsigned int len() const { return sso() ? strlen(sso_buf) : ptr.len; }
|
||||
inline unsigned int capacity() const { return sso() ? SSOSIZE - 1 : ptr.cap; }
|
||||
inline void setSSO(bool sso) { sso_buf[SSOSIZE - 1] = sso ? 0x00 : 0xff; }
|
||||
inline void setLen(int len) { if (!sso()) ptr.len = len; }
|
||||
inline void setCapacity(int cap) { if (!sso()) ptr.cap = cap; }
|
||||
inline void setBuffer(char *buff) { if (!sso()) ptr.buff = buff; }
|
||||
inline bool sso() const
|
||||
{
|
||||
return sso_buf[SSOSIZE - 1] == 0;
|
||||
}
|
||||
inline unsigned int len() const
|
||||
{
|
||||
return sso() ? strlen(sso_buf) : ptr.len;
|
||||
}
|
||||
inline unsigned int capacity() const
|
||||
{
|
||||
return sso() ? SSOSIZE - 1 : ptr.cap;
|
||||
}
|
||||
inline void setSSO(bool sso)
|
||||
{
|
||||
sso_buf[SSOSIZE - 1] = sso ? 0x00 : 0xff;
|
||||
}
|
||||
inline void setLen(int len)
|
||||
{
|
||||
if (!sso())
|
||||
{
|
||||
ptr.len = len;
|
||||
}
|
||||
}
|
||||
inline void setCapacity(int cap)
|
||||
{
|
||||
if (!sso())
|
||||
{
|
||||
ptr.cap = cap;
|
||||
}
|
||||
}
|
||||
inline void setBuffer(char *buff)
|
||||
{
|
||||
if (!sso())
|
||||
{
|
||||
ptr.buff = buff;
|
||||
}
|
||||
}
|
||||
// Buffer accessor functions
|
||||
inline const char *buffer() const { return (const char *)(sso() ? sso_buf : ptr.buff); }
|
||||
inline char *wbuffer() const { return sso() ? const_cast<char *>(sso_buf) : ptr.buff; } // Writable version of buffer
|
||||
inline const char *buffer() const
|
||||
{
|
||||
return (const char *)(sso() ? sso_buf : ptr.buff);
|
||||
}
|
||||
inline char *wbuffer() const
|
||||
{
|
||||
return sso() ? const_cast<char *>(sso_buf) : ptr.buff; // Writable version of buffer
|
||||
}
|
||||
|
||||
protected:
|
||||
protected:
|
||||
void init(void);
|
||||
void invalidate(void);
|
||||
unsigned char changeBuffer(unsigned int maxStrLen);
|
||||
@ -286,37 +363,48 @@ class String {
|
||||
#endif
|
||||
};
|
||||
|
||||
class StringSumHelper: public String {
|
||||
public:
|
||||
class StringSumHelper: public String
|
||||
{
|
||||
public:
|
||||
StringSumHelper(const String &s) :
|
||||
String(s) {
|
||||
String(s)
|
||||
{
|
||||
}
|
||||
StringSumHelper(const char *p) :
|
||||
String(p) {
|
||||
String(p)
|
||||
{
|
||||
}
|
||||
StringSumHelper(char c) :
|
||||
String(c) {
|
||||
String(c)
|
||||
{
|
||||
}
|
||||
StringSumHelper(unsigned char num) :
|
||||
String(num) {
|
||||
String(num)
|
||||
{
|
||||
}
|
||||
StringSumHelper(int num) :
|
||||
String(num) {
|
||||
String(num)
|
||||
{
|
||||
}
|
||||
StringSumHelper(unsigned int num) :
|
||||
String(num) {
|
||||
String(num)
|
||||
{
|
||||
}
|
||||
StringSumHelper(long num) :
|
||||
String(num) {
|
||||
String(num)
|
||||
{
|
||||
}
|
||||
StringSumHelper(unsigned long num) :
|
||||
String(num) {
|
||||
String(num)
|
||||
{
|
||||
}
|
||||
StringSumHelper(float num) :
|
||||
String(num) {
|
||||
String(num)
|
||||
{
|
||||
}
|
||||
StringSumHelper(double num) :
|
||||
String(num) {
|
||||
String(num)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
@ -28,8 +28,8 @@ using __cxxabiv1::__guard;
|
||||
extern void *umm_last_fail_alloc_addr;
|
||||
extern int umm_last_fail_alloc_size;
|
||||
|
||||
extern "C" void __cxa_pure_virtual(void) __attribute__ ((__noreturn__));
|
||||
extern "C" void __cxa_deleted_virtual(void) __attribute__ ((__noreturn__));
|
||||
extern "C" void __cxa_pure_virtual(void) __attribute__((__noreturn__));
|
||||
extern "C" void __cxa_deleted_virtual(void) __attribute__((__noreturn__));
|
||||
|
||||
void __cxa_pure_virtual(void)
|
||||
{
|
||||
@ -41,7 +41,8 @@ void __cxa_deleted_virtual(void)
|
||||
panic();
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
typedef struct
|
||||
{
|
||||
uint8_t guard;
|
||||
uint8_t ps;
|
||||
} guard_t;
|
||||
@ -49,7 +50,8 @@ typedef struct {
|
||||
extern "C" int __cxa_guard_acquire(__guard* pg)
|
||||
{
|
||||
uint8_t ps = xt_rsil(15);
|
||||
if (reinterpret_cast<guard_t*>(pg)->guard) {
|
||||
if (reinterpret_cast<guard_t*>(pg)->guard)
|
||||
{
|
||||
xt_wsr_ps(ps);
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,26 +1,26 @@
|
||||
/**
|
||||
* base64.cpp
|
||||
*
|
||||
* Created on: 09.12.2015
|
||||
*
|
||||
* Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
* This file is part of the ESP8266 core for Arduino.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
base64.cpp
|
||||
|
||||
Created on: 09.12.2015
|
||||
|
||||
Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
This file is part of the ESP8266 core for Arduino.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#include "Arduino.h"
|
||||
extern "C" {
|
||||
@ -30,19 +30,21 @@ extern "C" {
|
||||
#include "base64.h"
|
||||
|
||||
/**
|
||||
* convert input data to base64
|
||||
* @param data uint8_t *
|
||||
* @param length size_t
|
||||
* @return String
|
||||
*/
|
||||
String base64::encode(uint8_t * data, size_t length, bool doNewLines) {
|
||||
convert input data to base64
|
||||
@param data uint8_t
|
||||
@param length size_t
|
||||
@return String
|
||||
*/
|
||||
String base64::encode(uint8_t * data, size_t length, bool doNewLines)
|
||||
{
|
||||
// base64 needs more size then the source data, use cencode.h macros
|
||||
size_t size = ((doNewLines ? base64_encode_expected_len(length)
|
||||
: base64_encode_expected_len_nonewlines(length)) + 1);
|
||||
char * buffer = (char *) malloc(size);
|
||||
if(buffer) {
|
||||
if (buffer)
|
||||
{
|
||||
base64_encodestate _state;
|
||||
if(doNewLines)
|
||||
if (doNewLines)
|
||||
{
|
||||
base64_init_encodestate(&_state);
|
||||
}
|
||||
@ -61,11 +63,12 @@ String base64::encode(uint8_t * data, size_t length, bool doNewLines) {
|
||||
}
|
||||
|
||||
/**
|
||||
* convert input data to base64
|
||||
* @param text String
|
||||
* @return String
|
||||
*/
|
||||
String base64::encode(String text, bool doNewLines) {
|
||||
convert input data to base64
|
||||
@param text String
|
||||
@return String
|
||||
*/
|
||||
String base64::encode(String text, bool doNewLines)
|
||||
{
|
||||
return base64::encode((uint8_t *) text.c_str(), text.length(), doNewLines);
|
||||
}
|
||||
|
||||
|
@ -1,38 +1,39 @@
|
||||
/**
|
||||
* base64.h
|
||||
*
|
||||
* Created on: 09.12.2015
|
||||
*
|
||||
* Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
* This file is part of the ESP8266 core for Arduino.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
base64.h
|
||||
|
||||
Created on: 09.12.2015
|
||||
|
||||
Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
This file is part of the ESP8266 core for Arduino.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#ifndef CORE_BASE64_H_
|
||||
#define CORE_BASE64_H_
|
||||
|
||||
class base64 {
|
||||
public:
|
||||
class base64
|
||||
{
|
||||
public:
|
||||
// NOTE: The default behaviour of backend (lib64)
|
||||
// is to add a newline every 72 (encoded) characters output.
|
||||
// This may 'break' longer uris and json variables
|
||||
static String encode(uint8_t * data, size_t length, bool doNewLines = true);
|
||||
static String encode(String text, bool doNewLines = true);
|
||||
private:
|
||||
private:
|
||||
};
|
||||
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
*/
|
||||
|
||||
#ifndef Binary_h
|
||||
#define Binary_h
|
||||
|
@ -16,41 +16,48 @@
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
*/
|
||||
|
||||
#include "cbuf.h"
|
||||
#include "c_types.h"
|
||||
|
||||
cbuf::cbuf(size_t size) :
|
||||
next(NULL), _size(size), _buf(new char[size]), _bufend(_buf + size), _begin(_buf), _end(_begin) {
|
||||
next(NULL), _size(size), _buf(new char[size]), _bufend(_buf + size), _begin(_buf), _end(_begin)
|
||||
{
|
||||
}
|
||||
|
||||
cbuf::~cbuf() {
|
||||
cbuf::~cbuf()
|
||||
{
|
||||
delete[] _buf;
|
||||
}
|
||||
|
||||
size_t cbuf::resizeAdd(size_t addSize) {
|
||||
size_t cbuf::resizeAdd(size_t addSize)
|
||||
{
|
||||
return resize(_size + addSize);
|
||||
}
|
||||
|
||||
size_t cbuf::resize(size_t newSize) {
|
||||
size_t cbuf::resize(size_t newSize)
|
||||
{
|
||||
|
||||
size_t bytes_available = available();
|
||||
|
||||
// not lose any data
|
||||
// if data can be lost use remove or flush before resize
|
||||
if((newSize <= bytes_available) || (newSize == _size)) {
|
||||
if ((newSize <= bytes_available) || (newSize == _size))
|
||||
{
|
||||
return _size;
|
||||
}
|
||||
|
||||
char *newbuf = new char[newSize];
|
||||
char *oldbuf = _buf;
|
||||
|
||||
if(!newbuf) {
|
||||
if (!newbuf)
|
||||
{
|
||||
return _size;
|
||||
}
|
||||
|
||||
if(_buf) {
|
||||
if (_buf)
|
||||
{
|
||||
read(newbuf, bytes_available);
|
||||
memset((newbuf + bytes_available), 0x00, (newSize - bytes_available));
|
||||
}
|
||||
@ -66,37 +73,47 @@ size_t cbuf::resize(size_t newSize) {
|
||||
return _size;
|
||||
}
|
||||
|
||||
size_t ICACHE_RAM_ATTR cbuf::available() const {
|
||||
if(_end >= _begin) {
|
||||
size_t ICACHE_RAM_ATTR cbuf::available() const
|
||||
{
|
||||
if (_end >= _begin)
|
||||
{
|
||||
return _end - _begin;
|
||||
}
|
||||
return _size - (_begin - _end);
|
||||
}
|
||||
|
||||
size_t cbuf::size() {
|
||||
size_t cbuf::size()
|
||||
{
|
||||
return _size;
|
||||
}
|
||||
|
||||
size_t cbuf::room() const {
|
||||
if(_end >= _begin) {
|
||||
size_t cbuf::room() const
|
||||
{
|
||||
if (_end >= _begin)
|
||||
{
|
||||
return _size - (_end - _begin) - 1;
|
||||
}
|
||||
return _begin - _end - 1;
|
||||
}
|
||||
|
||||
int cbuf::peek() {
|
||||
if(empty())
|
||||
int cbuf::peek()
|
||||
{
|
||||
if (empty())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return static_cast<int>(*_begin);
|
||||
}
|
||||
|
||||
size_t cbuf::peek(char *dst, size_t size) {
|
||||
size_t cbuf::peek(char *dst, size_t size)
|
||||
{
|
||||
size_t bytes_available = available();
|
||||
size_t size_to_read = (size < bytes_available) ? size : bytes_available;
|
||||
size_t size_read = size_to_read;
|
||||
char * begin = _begin;
|
||||
if(_end < _begin && size_to_read > (size_t) (_bufend - _begin)) {
|
||||
if (_end < _begin && size_to_read > (size_t)(_bufend - _begin))
|
||||
{
|
||||
size_t top_size = _bufend - _begin;
|
||||
memcpy(dst, _begin, top_size);
|
||||
begin = _buf;
|
||||
@ -107,20 +124,25 @@ size_t cbuf::peek(char *dst, size_t size) {
|
||||
return size_read;
|
||||
}
|
||||
|
||||
int ICACHE_RAM_ATTR cbuf::read() {
|
||||
if(empty())
|
||||
int ICACHE_RAM_ATTR cbuf::read()
|
||||
{
|
||||
if (empty())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
char result = *_begin;
|
||||
_begin = wrap_if_bufend(_begin + 1);
|
||||
return static_cast<int>(result);
|
||||
}
|
||||
|
||||
size_t cbuf::read(char* dst, size_t size) {
|
||||
size_t cbuf::read(char* dst, size_t size)
|
||||
{
|
||||
size_t bytes_available = available();
|
||||
size_t size_to_read = (size < bytes_available) ? size : bytes_available;
|
||||
size_t size_read = size_to_read;
|
||||
if(_end < _begin && size_to_read > (size_t) (_bufend - _begin)) {
|
||||
if (_end < _begin && size_to_read > (size_t)(_bufend - _begin))
|
||||
{
|
||||
size_t top_size = _bufend - _begin;
|
||||
memcpy(dst, _begin, top_size);
|
||||
_begin = _buf;
|
||||
@ -132,20 +154,25 @@ size_t cbuf::read(char* dst, size_t size) {
|
||||
return size_read;
|
||||
}
|
||||
|
||||
size_t ICACHE_RAM_ATTR cbuf::write(char c) {
|
||||
if(full())
|
||||
size_t ICACHE_RAM_ATTR cbuf::write(char c)
|
||||
{
|
||||
if (full())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
*_end = c;
|
||||
_end = wrap_if_bufend(_end + 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t cbuf::write(const char* src, size_t size) {
|
||||
size_t cbuf::write(const char* src, size_t size)
|
||||
{
|
||||
size_t bytes_available = room();
|
||||
size_t size_to_write = (size < bytes_available) ? size : bytes_available;
|
||||
size_t size_written = size_to_write;
|
||||
if(_end >= _begin && size_to_write > (size_t) (_bufend - _end)) {
|
||||
if (_end >= _begin && size_to_write > (size_t)(_bufend - _end))
|
||||
{
|
||||
size_t top_size = _bufend - _end;
|
||||
memcpy(_end, src, top_size);
|
||||
_end = _buf;
|
||||
@ -157,19 +184,23 @@ size_t cbuf::write(const char* src, size_t size) {
|
||||
return size_written;
|
||||
}
|
||||
|
||||
void cbuf::flush() {
|
||||
void cbuf::flush()
|
||||
{
|
||||
_begin = _buf;
|
||||
_end = _buf;
|
||||
}
|
||||
|
||||
size_t cbuf::remove(size_t size) {
|
||||
size_t cbuf::remove(size_t size)
|
||||
{
|
||||
size_t bytes_available = available();
|
||||
if(size >= bytes_available) {
|
||||
if (size >= bytes_available)
|
||||
{
|
||||
flush();
|
||||
return 0;
|
||||
}
|
||||
size_t size_to_remove = (size < bytes_available) ? size : bytes_available;
|
||||
if(_end < _begin && size_to_remove > (size_t) (_bufend - _begin)) {
|
||||
if (_end < _begin && size_to_remove > (size_t)(_bufend - _begin))
|
||||
{
|
||||
size_t top_size = _bufend - _begin;
|
||||
_begin = _buf;
|
||||
size_to_remove -= top_size;
|
||||
|
@ -16,7 +16,7 @@
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
*/
|
||||
|
||||
#ifndef __cbuf_h
|
||||
#define __cbuf_h
|
||||
@ -25,8 +25,9 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
class cbuf {
|
||||
public:
|
||||
class cbuf
|
||||
{
|
||||
public:
|
||||
cbuf(size_t size);
|
||||
~cbuf();
|
||||
|
||||
@ -37,11 +38,13 @@ class cbuf {
|
||||
|
||||
size_t room() const;
|
||||
|
||||
inline bool empty() const {
|
||||
inline bool empty() const
|
||||
{
|
||||
return _begin == _end;
|
||||
}
|
||||
|
||||
inline bool full() const {
|
||||
inline bool full() const
|
||||
{
|
||||
return wrap_if_bufend(_end + 1) == _begin;
|
||||
}
|
||||
|
||||
@ -59,8 +62,9 @@ class cbuf {
|
||||
|
||||
cbuf *next;
|
||||
|
||||
private:
|
||||
inline char* wrap_if_bufend(char* ptr) const {
|
||||
private:
|
||||
inline char* wrap_if_bufend(char* ptr) const
|
||||
{
|
||||
return (ptr == _bufend) ? _buf : ptr;
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
*/
|
||||
|
||||
#ifndef CONT_H_
|
||||
#define CONT_H_
|
||||
@ -31,7 +31,8 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct cont_ {
|
||||
typedef struct cont_
|
||||
{
|
||||
void (*pc_ret)(void);
|
||||
unsigned* sp_ret;
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
*/
|
||||
|
||||
#include "cont.h"
|
||||
#include <stddef.h>
|
||||
@ -27,7 +27,8 @@ extern "C" {
|
||||
|
||||
#define CONT_STACKGUARD 0xfeefeffe
|
||||
|
||||
void cont_init(cont_t* cont) {
|
||||
void cont_init(cont_t* cont)
|
||||
{
|
||||
memset(cont, 0, sizeof(cont_t));
|
||||
|
||||
cont->stack_guard1 = CONT_STACKGUARD;
|
||||
@ -36,48 +37,54 @@ void cont_init(cont_t* cont) {
|
||||
cont->struct_start = (unsigned*) cont;
|
||||
|
||||
// fill stack with magic values to check high water mark
|
||||
for(int pos = 0; pos < (int)(sizeof(cont->stack) / 4); pos++)
|
||||
for (int pos = 0; pos < (int)(sizeof(cont->stack) / 4); pos++)
|
||||
{
|
||||
cont->stack[pos] = CONT_STACKGUARD;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ICACHE_RAM_ATTR cont_check(cont_t* cont) {
|
||||
if(cont->stack_guard1 != CONT_STACKGUARD || cont->stack_guard2 != CONT_STACKGUARD) return 1;
|
||||
int ICACHE_RAM_ATTR cont_check(cont_t* cont)
|
||||
{
|
||||
if (cont->stack_guard1 != CONT_STACKGUARD || cont->stack_guard2 != CONT_STACKGUARD)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// No need for this to be in IRAM, not expected to be IRQ called
|
||||
int cont_get_free_stack(cont_t* cont) {
|
||||
// No need for this to be in IRAM, not expected to be IRQ called
|
||||
int cont_get_free_stack(cont_t* cont)
|
||||
{
|
||||
uint32_t *head = cont->stack;
|
||||
int freeWords = 0;
|
||||
|
||||
while(*head == CONT_STACKGUARD)
|
||||
while (*head == CONT_STACKGUARD)
|
||||
{
|
||||
head++;
|
||||
freeWords++;
|
||||
}
|
||||
|
||||
return freeWords * 4;
|
||||
}
|
||||
}
|
||||
|
||||
bool ICACHE_RAM_ATTR cont_can_yield(cont_t* cont) {
|
||||
bool ICACHE_RAM_ATTR cont_can_yield(cont_t* cont)
|
||||
{
|
||||
return !ETS_INTR_WITHINISR() &&
|
||||
cont->pc_ret != 0 && cont->pc_yield == 0;
|
||||
}
|
||||
}
|
||||
|
||||
// No need for this to be in IRAM, not expected to be IRQ called
|
||||
void cont_repaint_stack(cont_t *cont)
|
||||
{
|
||||
// No need for this to be in IRAM, not expected to be IRQ called
|
||||
void cont_repaint_stack(cont_t *cont)
|
||||
{
|
||||
register uint32_t *sp asm("a1");
|
||||
// Ensure 64 bytes adjacent to the current SP don't get touched to endure
|
||||
// we don't accidentally trounce over locals or IRQ temps.
|
||||
// Fill stack with magic values
|
||||
for ( uint32_t *pos = sp - 16; pos >= &cont->stack[0]; pos-- )
|
||||
for (uint32_t *pos = sp - 16; pos >= &cont->stack[0]; pos--)
|
||||
{
|
||||
*pos = CONT_STACKGUARD;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -1,22 +1,22 @@
|
||||
/*
|
||||
* This is the original app_entry() not providing extra 4K heap, but allowing
|
||||
* the use of WPS.
|
||||
*
|
||||
* see comments in core_esp8266_main.cpp's app_entry()
|
||||
*
|
||||
*/
|
||||
This is the original app_entry() not providing extra 4K heap, but allowing
|
||||
the use of WPS.
|
||||
|
||||
see comments in core_esp8266_main.cpp's app_entry()
|
||||
|
||||
*/
|
||||
|
||||
#include <c_types.h>
|
||||
#include "cont.h"
|
||||
#include "coredecls.h"
|
||||
|
||||
void disable_extra4k_at_link_time (void)
|
||||
void disable_extra4k_at_link_time(void)
|
||||
{
|
||||
/*
|
||||
* does nothing
|
||||
* allows overriding the core_esp8266_main.cpp's app_entry()
|
||||
* by this one below, at link time
|
||||
*
|
||||
does nothing
|
||||
allows overriding the core_esp8266_main.cpp's app_entry()
|
||||
by this one below, at link time
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
@ -25,7 +25,7 @@ void disable_extra4k_at_link_time (void)
|
||||
extern "C" void call_user_start();
|
||||
|
||||
/* this is the default NONOS-SDK user's heap location */
|
||||
static cont_t g_cont __attribute__ ((aligned (16)));
|
||||
static cont_t g_cont __attribute__((aligned(16)));
|
||||
|
||||
extern "C" void ICACHE_RAM_ATTR app_entry_redefinable(void)
|
||||
{
|
||||
|
@ -17,7 +17,7 @@
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
@ -25,67 +25,74 @@
|
||||
|
||||
extern "C" {
|
||||
|
||||
static uint32_t crc_update(uint32_t crc, const uint8_t *data, size_t length)
|
||||
{
|
||||
static uint32_t crc_update(uint32_t crc, const uint8_t *data, size_t length)
|
||||
{
|
||||
uint32_t i;
|
||||
bool bit;
|
||||
uint8_t c;
|
||||
|
||||
while (length--) {
|
||||
while (length--)
|
||||
{
|
||||
c = *data++;
|
||||
for (i = 0x80; i > 0; i >>= 1) {
|
||||
for (i = 0x80; i > 0; i >>= 1)
|
||||
{
|
||||
bit = crc & 0x80000000;
|
||||
if (c & i) {
|
||||
if (c & i)
|
||||
{
|
||||
bit = !bit;
|
||||
}
|
||||
crc <<= 1;
|
||||
if (bit) {
|
||||
if (bit)
|
||||
{
|
||||
crc ^= 0x04c11db7;
|
||||
}
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t eboot_command_calculate_crc32(const struct eboot_command* cmd)
|
||||
{
|
||||
static uint32_t eboot_command_calculate_crc32(const struct eboot_command* cmd)
|
||||
{
|
||||
return crc_update(0xffffffff, (const uint8_t*) cmd,
|
||||
offsetof(struct eboot_command, crc32));
|
||||
}
|
||||
}
|
||||
|
||||
int eboot_command_read(struct eboot_command* cmd)
|
||||
{
|
||||
int eboot_command_read(struct eboot_command* cmd)
|
||||
{
|
||||
const uint32_t dw_count = sizeof(struct eboot_command) / sizeof(uint32_t);
|
||||
uint32_t* dst = (uint32_t *) cmd;
|
||||
for (uint32_t i = 0; i < dw_count; ++i) {
|
||||
for (uint32_t i = 0; i < dw_count; ++i)
|
||||
{
|
||||
dst[i] = RTC_MEM[i];
|
||||
}
|
||||
|
||||
uint32_t crc32 = eboot_command_calculate_crc32(cmd);
|
||||
if ((cmd->magic & EBOOT_MAGIC_MASK) != EBOOT_MAGIC ||
|
||||
cmd->crc32 != crc32) {
|
||||
cmd->crc32 != crc32)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void eboot_command_write(struct eboot_command* cmd)
|
||||
{
|
||||
void eboot_command_write(struct eboot_command* cmd)
|
||||
{
|
||||
cmd->magic = EBOOT_MAGIC;
|
||||
cmd->crc32 = eboot_command_calculate_crc32(cmd);
|
||||
|
||||
const uint32_t dw_count = sizeof(struct eboot_command) / sizeof(uint32_t);
|
||||
const uint32_t* src = (const uint32_t *) cmd;
|
||||
for (uint32_t i = 0; i < dw_count; ++i) {
|
||||
for (uint32_t i = 0; i < dw_count; ++i)
|
||||
{
|
||||
RTC_MEM[i] = src[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void eboot_command_clear()
|
||||
{
|
||||
void eboot_command_clear()
|
||||
{
|
||||
RTC_MEM[offsetof(struct eboot_command, magic) / sizeof(uint32_t)] = 0;
|
||||
RTC_MEM[offsetof(struct eboot_command, crc32) / sizeof(uint32_t)] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -18,7 +18,7 @@
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
*/
|
||||
|
||||
|
||||
#ifndef CORE_ESP8266_FEATURES_H
|
||||
|
@ -17,7 +17,7 @@
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
*/
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
@ -27,9 +27,10 @@
|
||||
|
||||
extern "C" {
|
||||
|
||||
int SPIEraseAreaEx(const uint32_t start, const uint32_t size)
|
||||
{
|
||||
if ((start & (FLASH_SECTOR_SIZE - 1)) != 0) {
|
||||
int SPIEraseAreaEx(const uint32_t start, const uint32_t size)
|
||||
{
|
||||
if ((start & (FLASH_SECTOR_SIZE - 1)) != 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -38,29 +39,35 @@ int SPIEraseAreaEx(const uint32_t start, const uint32_t size)
|
||||
uint32_t sector_count = (size + FLASH_SECTOR_SIZE - 1) / FLASH_SECTOR_SIZE;
|
||||
const uint32_t end = current_sector + sector_count;
|
||||
|
||||
for (; current_sector < end && (current_sector & (sectors_per_block-1));
|
||||
++current_sector, --sector_count) {
|
||||
if (SPIEraseSector(current_sector)) {
|
||||
for (; current_sector < end && (current_sector & (sectors_per_block - 1));
|
||||
++current_sector, --sector_count)
|
||||
{
|
||||
if (SPIEraseSector(current_sector))
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
for (;current_sector + sectors_per_block <= end;
|
||||
for (; current_sector + sectors_per_block <= end;
|
||||
current_sector += sectors_per_block,
|
||||
sector_count -= sectors_per_block) {
|
||||
if (SPIEraseBlock(current_sector / sectors_per_block)) {
|
||||
sector_count -= sectors_per_block)
|
||||
{
|
||||
if (SPIEraseBlock(current_sector / sectors_per_block))
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
for (; current_sector < end;
|
||||
++current_sector, --sector_count) {
|
||||
if (SPIEraseSector(current_sector)) {
|
||||
++current_sector, --sector_count)
|
||||
{
|
||||
if (SPIEraseSector(current_sector))
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -31,18 +31,19 @@ extern "C" {
|
||||
#define SLC_BUF_CNT (8) // Number of buffers in the I2S circular buffer
|
||||
#define SLC_BUF_LEN (64) // Length of one buffer, in 32-bit words.
|
||||
|
||||
// We use a queue to keep track of the DMA buffers that are empty. The ISR
|
||||
// will push buffers to the back of the queue, the I2S transmitter will pull
|
||||
// them from the front and fill them. For ease, the queue will contain
|
||||
// *pointers* to the DMA buffers, not the data itself. The queue depth is
|
||||
// one smaller than the amount of buffers we have, because there's always a
|
||||
// buffer that is being used by the DMA subsystem *right now* and we don't
|
||||
// want to be able to write to that simultaneously.
|
||||
// We use a queue to keep track of the DMA buffers that are empty. The ISR
|
||||
// will push buffers to the back of the queue, the I2S transmitter will pull
|
||||
// them from the front and fill them. For ease, the queue will contain
|
||||
// *pointers* to the DMA buffers, not the data itself. The queue depth is
|
||||
// one smaller than the amount of buffers we have, because there's always a
|
||||
// buffer that is being used by the DMA subsystem *right now* and we don't
|
||||
// want to be able to write to that simultaneously.
|
||||
|
||||
// For RX, it's a little different. The buffers in i2s_slc_queue are
|
||||
// placed onto the list when they're filled by DMA
|
||||
// For RX, it's a little different. The buffers in i2s_slc_queue are
|
||||
// placed onto the list when they're filled by DMA
|
||||
|
||||
typedef struct slc_queue_item {
|
||||
typedef struct slc_queue_item
|
||||
{
|
||||
uint32_t blocksize : 12;
|
||||
uint32_t datalen : 12;
|
||||
uint32_t unused : 5;
|
||||
@ -51,31 +52,32 @@ typedef struct slc_queue_item {
|
||||
volatile uint32_t owner : 1; // DMA can change this value
|
||||
uint32_t * buf_ptr;
|
||||
struct slc_queue_item * next_link_ptr;
|
||||
} slc_queue_item_t;
|
||||
} slc_queue_item_t;
|
||||
|
||||
typedef struct i2s_state {
|
||||
typedef struct i2s_state
|
||||
{
|
||||
uint32_t * slc_queue[SLC_BUF_CNT];
|
||||
volatile uint8_t slc_queue_len;
|
||||
uint32_t * slc_buf_pntr[SLC_BUF_CNT]; // Pointer to the I2S DMA buffer data
|
||||
slc_queue_item_t slc_items[SLC_BUF_CNT]; // I2S DMA buffer descriptors
|
||||
uint32_t * curr_slc_buf; // Current buffer for writing
|
||||
uint32_t curr_slc_buf_pos; // Position in the current buffer
|
||||
void (*callback) (void);
|
||||
void (*callback)(void);
|
||||
// Callback function should be defined as 'void ICACHE_RAM_ATTR function_name()',
|
||||
// and be placed in IRAM for faster execution. Avoid long computational tasks in this
|
||||
// function, use it to set flags and process later.
|
||||
} i2s_state_t;
|
||||
} i2s_state_t;
|
||||
|
||||
// RX = I2S receive (i.e. microphone), TX = I2S transmit (i.e. DAC)
|
||||
static i2s_state_t *rx = NULL;
|
||||
static i2s_state_t *tx = NULL;
|
||||
// RX = I2S receive (i.e. microphone), TX = I2S transmit (i.e. DAC)
|
||||
static i2s_state_t *rx = NULL;
|
||||
static i2s_state_t *tx = NULL;
|
||||
|
||||
// Last I2S sample rate requested
|
||||
static uint32_t _i2s_sample_rate;
|
||||
// Last I2S sample rate requested
|
||||
static uint32_t _i2s_sample_rate;
|
||||
|
||||
// IOs used for I2S. Not defined in i2s.h, unfortunately.
|
||||
// Note these are internal GPIO numbers and not pins on an
|
||||
// Arduino board. Users need to verify their particular wiring.
|
||||
// IOs used for I2S. Not defined in i2s.h, unfortunately.
|
||||
// Note these are internal GPIO numbers and not pins on an
|
||||
// Arduino board. Users need to verify their particular wiring.
|
||||
#define I2SO_DATA 3
|
||||
#define I2SO_BCK 15
|
||||
#define I2SO_WS 2
|
||||
@ -83,119 +85,150 @@ static uint32_t _i2s_sample_rate;
|
||||
#define I2SI_BCK 13
|
||||
#define I2SI_WS 14
|
||||
|
||||
static bool _i2s_is_full(const i2s_state_t *ch) {
|
||||
if (!ch) {
|
||||
static bool _i2s_is_full(const i2s_state_t *ch)
|
||||
{
|
||||
if (!ch)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return (ch->curr_slc_buf_pos==SLC_BUF_LEN || ch->curr_slc_buf==NULL) && (ch->slc_queue_len == 0);
|
||||
}
|
||||
return (ch->curr_slc_buf_pos == SLC_BUF_LEN || ch->curr_slc_buf == NULL) && (ch->slc_queue_len == 0);
|
||||
}
|
||||
|
||||
bool i2s_is_full() {
|
||||
return _i2s_is_full( tx );
|
||||
}
|
||||
bool i2s_is_full()
|
||||
{
|
||||
return _i2s_is_full(tx);
|
||||
}
|
||||
|
||||
bool i2s_rx_is_full() {
|
||||
return _i2s_is_full( rx );
|
||||
}
|
||||
bool i2s_rx_is_full()
|
||||
{
|
||||
return _i2s_is_full(rx);
|
||||
}
|
||||
|
||||
static bool _i2s_is_empty(const i2s_state_t *ch) {
|
||||
if (!ch) {
|
||||
static bool _i2s_is_empty(const i2s_state_t *ch)
|
||||
{
|
||||
if (!ch)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return (ch->slc_queue_len >= SLC_BUF_CNT-1);
|
||||
}
|
||||
return (ch->slc_queue_len >= SLC_BUF_CNT - 1);
|
||||
}
|
||||
|
||||
bool i2s_is_empty() {
|
||||
return _i2s_is_empty( tx );
|
||||
}
|
||||
bool i2s_is_empty()
|
||||
{
|
||||
return _i2s_is_empty(tx);
|
||||
}
|
||||
|
||||
bool i2s_rx_is_empty() {
|
||||
return _i2s_is_empty( rx );
|
||||
}
|
||||
bool i2s_rx_is_empty()
|
||||
{
|
||||
return _i2s_is_empty(rx);
|
||||
}
|
||||
|
||||
static uint16_t _i2s_available(const i2s_state_t *ch) {
|
||||
if (!ch) {
|
||||
static uint16_t _i2s_available(const i2s_state_t *ch)
|
||||
{
|
||||
if (!ch)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return (SLC_BUF_CNT - ch->slc_queue_len) * SLC_BUF_LEN;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t i2s_available(){
|
||||
return _i2s_available( tx );
|
||||
}
|
||||
uint16_t i2s_available()
|
||||
{
|
||||
return _i2s_available(tx);
|
||||
}
|
||||
|
||||
uint16_t i2s_rx_available(){
|
||||
return _i2s_available( rx );
|
||||
}
|
||||
uint16_t i2s_rx_available()
|
||||
{
|
||||
return _i2s_available(rx);
|
||||
}
|
||||
|
||||
// Pop the top off of the queue and return it
|
||||
static uint32_t * ICACHE_RAM_ATTR i2s_slc_queue_next_item(i2s_state_t *ch) {
|
||||
// Pop the top off of the queue and return it
|
||||
static uint32_t * ICACHE_RAM_ATTR i2s_slc_queue_next_item(i2s_state_t *ch)
|
||||
{
|
||||
uint8_t i;
|
||||
uint32_t *item = ch->slc_queue[0];
|
||||
ch->slc_queue_len--;
|
||||
for ( i = 0; i < ch->slc_queue_len; i++) {
|
||||
ch->slc_queue[i] = ch->slc_queue[i+1];
|
||||
for (i = 0; i < ch->slc_queue_len; i++)
|
||||
{
|
||||
ch->slc_queue[i] = ch->slc_queue[i + 1];
|
||||
}
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
// Append an item to the end of the queue from receive
|
||||
static void ICACHE_RAM_ATTR i2s_slc_queue_append_item(i2s_state_t *ch, uint32_t *item) {
|
||||
// Append an item to the end of the queue from receive
|
||||
static void ICACHE_RAM_ATTR i2s_slc_queue_append_item(i2s_state_t *ch, uint32_t *item)
|
||||
{
|
||||
// Shift everything up, except for the one corresponding to this item
|
||||
for (int i=0, dest=0; i < ch->slc_queue_len; i++) {
|
||||
if (ch->slc_queue[i] != item) {
|
||||
for (int i = 0, dest = 0; i < ch->slc_queue_len; i++)
|
||||
{
|
||||
if (ch->slc_queue[i] != item)
|
||||
{
|
||||
ch->slc_queue[dest++] = ch->slc_queue[i];
|
||||
}
|
||||
}
|
||||
if (ch->slc_queue_len < SLC_BUF_CNT - 1) {
|
||||
if (ch->slc_queue_len < SLC_BUF_CNT - 1)
|
||||
{
|
||||
ch->slc_queue[ch->slc_queue_len++] = item;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
ch->slc_queue[ch->slc_queue_len] = item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ICACHE_RAM_ATTR i2s_slc_isr(void) {
|
||||
static void ICACHE_RAM_ATTR i2s_slc_isr(void)
|
||||
{
|
||||
ETS_SLC_INTR_DISABLE();
|
||||
uint32_t slc_intr_status = SLCIS;
|
||||
SLCIC = 0xFFFFFFFF;
|
||||
if (slc_intr_status & SLCIRXEOF) {
|
||||
if (slc_intr_status & SLCIRXEOF)
|
||||
{
|
||||
slc_queue_item_t *finished_item = (slc_queue_item_t *)SLCRXEDA;
|
||||
// Zero the buffer so it is mute in case of underflow
|
||||
ets_memset((void *)finished_item->buf_ptr, 0x00, SLC_BUF_LEN * 4);
|
||||
if (tx->slc_queue_len >= SLC_BUF_CNT-1) {
|
||||
if (tx->slc_queue_len >= SLC_BUF_CNT - 1)
|
||||
{
|
||||
// All buffers are empty. This means we have an underflow
|
||||
i2s_slc_queue_next_item(tx); // Free space for finished_item
|
||||
}
|
||||
tx->slc_queue[tx->slc_queue_len++] = finished_item->buf_ptr;
|
||||
if (tx->callback) {
|
||||
if (tx->callback)
|
||||
{
|
||||
tx->callback();
|
||||
}
|
||||
}
|
||||
if (slc_intr_status & SLCITXEOF) {
|
||||
if (slc_intr_status & SLCITXEOF)
|
||||
{
|
||||
slc_queue_item_t *finished_item = (slc_queue_item_t *)SLCTXEDA;
|
||||
// Set owner back to 1 (SW) or else RX stops. TX has no such restriction.
|
||||
finished_item->owner = 1;
|
||||
i2s_slc_queue_append_item(rx, finished_item->buf_ptr);
|
||||
if (rx->callback) {
|
||||
if (rx->callback)
|
||||
{
|
||||
rx->callback();
|
||||
}
|
||||
}
|
||||
ETS_SLC_INTR_ENABLE();
|
||||
}
|
||||
}
|
||||
|
||||
void i2s_set_callback(void (*callback) (void)) {
|
||||
void i2s_set_callback(void (*callback)(void))
|
||||
{
|
||||
tx->callback = callback;
|
||||
}
|
||||
}
|
||||
|
||||
void i2s_rx_set_callback(void (*callback) (void)) {
|
||||
void i2s_rx_set_callback(void (*callback)(void))
|
||||
{
|
||||
rx->callback = callback;
|
||||
}
|
||||
}
|
||||
|
||||
static bool _alloc_channel(i2s_state_t *ch) {
|
||||
static bool _alloc_channel(i2s_state_t *ch)
|
||||
{
|
||||
ch->slc_queue_len = 0;
|
||||
for (int x=0; x<SLC_BUF_CNT; x++) {
|
||||
for (int x = 0; x < SLC_BUF_CNT; x++)
|
||||
{
|
||||
ch->slc_buf_pntr[x] = (uint32_t *)malloc(SLC_BUF_LEN * sizeof(ch->slc_buf_pntr[0][0]));
|
||||
if (!ch->slc_buf_pntr[x]) {
|
||||
if (!ch->slc_buf_pntr[x])
|
||||
{
|
||||
// OOM, the upper layer will free up any partially allocated channels.
|
||||
return false;
|
||||
}
|
||||
@ -208,19 +241,24 @@ static bool _alloc_channel(i2s_state_t *ch) {
|
||||
ch->slc_items[x].datalen = SLC_BUF_LEN * 4;
|
||||
ch->slc_items[x].blocksize = SLC_BUF_LEN * 4;
|
||||
ch->slc_items[x].buf_ptr = (uint32_t*)&ch->slc_buf_pntr[x][0];
|
||||
ch->slc_items[x].next_link_ptr = (x<(SLC_BUF_CNT-1))?(&ch->slc_items[x+1]):(&ch->slc_items[0]);
|
||||
ch->slc_items[x].next_link_ptr = (x < (SLC_BUF_CNT - 1)) ? (&ch->slc_items[x + 1]) : (&ch->slc_items[0]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool i2s_slc_begin() {
|
||||
if (tx) {
|
||||
if (!_alloc_channel(tx)) {
|
||||
static bool i2s_slc_begin()
|
||||
{
|
||||
if (tx)
|
||||
{
|
||||
if (!_alloc_channel(tx))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (rx) {
|
||||
if (!_alloc_channel(rx)) {
|
||||
if (rx)
|
||||
{
|
||||
if (!_alloc_channel(rx))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -242,67 +280,87 @@ static bool i2s_slc_begin() {
|
||||
//an error at us otherwise. Just feed it any random descriptor.
|
||||
SLCTXL &= ~(SLCTXLAM << SLCTXLA); // clear TX descriptor address
|
||||
SLCRXL &= ~(SLCRXLAM << SLCRXLA); // clear RX descriptor address
|
||||
if (!rx) {
|
||||
if (!rx)
|
||||
{
|
||||
SLCTXL |= (uint32)&tx->slc_items[1] << SLCTXLA; // Set fake (unused) RX descriptor address
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
SLCTXL |= (uint32)&rx->slc_items[0] << SLCTXLA; // Set real RX address
|
||||
}
|
||||
if (!tx) {
|
||||
if (!tx)
|
||||
{
|
||||
SLCRXL |= (uint32)&rx->slc_items[1] << SLCRXLA; // Set fake (unused) TX descriptor address
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
SLCRXL |= (uint32)&tx->slc_items[0] << SLCRXLA; // Set real TX address
|
||||
}
|
||||
|
||||
ETS_SLC_INTR_ATTACH(i2s_slc_isr, NULL);
|
||||
SLCIE = (tx?SLCIRXEOF:0) | (rx?SLCITXEOF:0); // Enable appropriate EOF IRQ
|
||||
SLCIE = (tx ? SLCIRXEOF : 0) | (rx ? SLCITXEOF : 0); // Enable appropriate EOF IRQ
|
||||
|
||||
ETS_SLC_INTR_ENABLE();
|
||||
|
||||
// Start transmission ("TX" DMA always needed to be enabled)
|
||||
SLCTXL |= SLCTXLS;
|
||||
if (tx) {
|
||||
if (tx)
|
||||
{
|
||||
SLCRXL |= SLCRXLS;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static void i2s_slc_end(){
|
||||
static void i2s_slc_end()
|
||||
{
|
||||
ETS_SLC_INTR_DISABLE();
|
||||
SLCIC = 0xFFFFFFFF;
|
||||
SLCIE = 0;
|
||||
SLCTXL &= ~(SLCTXLAM << SLCTXLA); // clear TX descriptor address
|
||||
SLCRXL &= ~(SLCRXLAM << SLCRXLA); // clear RX descriptor address
|
||||
|
||||
for (int x = 0; x<SLC_BUF_CNT; x++) {
|
||||
if (tx) {
|
||||
for (int x = 0; x < SLC_BUF_CNT; x++)
|
||||
{
|
||||
if (tx)
|
||||
{
|
||||
free(tx->slc_buf_pntr[x]);
|
||||
tx->slc_buf_pntr[x] = NULL;
|
||||
}
|
||||
if (rx) {
|
||||
if (rx)
|
||||
{
|
||||
free(rx->slc_buf_pntr[x]);
|
||||
rx->slc_buf_pntr[x] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// These routines push a single, 32-bit sample to the I2S buffers. Call at (on average)
|
||||
// at least the current sample rate.
|
||||
static bool _i2s_write_sample(uint32_t sample, bool nb) {
|
||||
if (!tx) {
|
||||
// These routines push a single, 32-bit sample to the I2S buffers. Call at (on average)
|
||||
// at least the current sample rate.
|
||||
static bool _i2s_write_sample(uint32_t sample, bool nb)
|
||||
{
|
||||
if (!tx)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tx->curr_slc_buf_pos==SLC_BUF_LEN || tx->curr_slc_buf==NULL) {
|
||||
if (tx->slc_queue_len == 0) {
|
||||
if (nb) {
|
||||
if (tx->curr_slc_buf_pos == SLC_BUF_LEN || tx->curr_slc_buf == NULL)
|
||||
{
|
||||
if (tx->slc_queue_len == 0)
|
||||
{
|
||||
if (nb)
|
||||
{
|
||||
// Don't wait if nonblocking, just notify upper levels
|
||||
return false;
|
||||
}
|
||||
while (1) {
|
||||
if (tx->slc_queue_len > 0) {
|
||||
while (1)
|
||||
{
|
||||
if (tx->slc_queue_len > 0)
|
||||
{
|
||||
break;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
optimistic_yield(10000);
|
||||
}
|
||||
}
|
||||
@ -310,48 +368,60 @@ static bool _i2s_write_sample(uint32_t sample, bool nb) {
|
||||
ETS_SLC_INTR_DISABLE();
|
||||
tx->curr_slc_buf = (uint32_t *)i2s_slc_queue_next_item(tx);
|
||||
ETS_SLC_INTR_ENABLE();
|
||||
tx->curr_slc_buf_pos=0;
|
||||
tx->curr_slc_buf_pos = 0;
|
||||
}
|
||||
tx->curr_slc_buf[tx->curr_slc_buf_pos++]=sample;
|
||||
tx->curr_slc_buf[tx->curr_slc_buf_pos++] = sample;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool i2s_write_sample(uint32_t sample) {
|
||||
bool i2s_write_sample(uint32_t sample)
|
||||
{
|
||||
return _i2s_write_sample(sample, false);
|
||||
}
|
||||
}
|
||||
|
||||
bool i2s_write_sample_nb(uint32_t sample) {
|
||||
bool i2s_write_sample_nb(uint32_t sample)
|
||||
{
|
||||
return _i2s_write_sample(sample, true);
|
||||
}
|
||||
}
|
||||
|
||||
bool i2s_write_lr(int16_t left, int16_t right){
|
||||
bool i2s_write_lr(int16_t left, int16_t right)
|
||||
{
|
||||
int sample = right & 0xFFFF;
|
||||
sample = sample << 16;
|
||||
sample |= left & 0xFFFF;
|
||||
return i2s_write_sample(sample);
|
||||
}
|
||||
}
|
||||
|
||||
// writes a buffer of frames into the DMA memory, returns the amount of frames written
|
||||
// A frame is just a int16_t for mono, for stereo a frame is two int16_t, one for each channel.
|
||||
static uint16_t _i2s_write_buffer(int16_t *frames, uint16_t frame_count, bool mono, bool nb) {
|
||||
uint16_t frames_written=0;
|
||||
// writes a buffer of frames into the DMA memory, returns the amount of frames written
|
||||
// A frame is just a int16_t for mono, for stereo a frame is two int16_t, one for each channel.
|
||||
static uint16_t _i2s_write_buffer(int16_t *frames, uint16_t frame_count, bool mono, bool nb)
|
||||
{
|
||||
uint16_t frames_written = 0;
|
||||
|
||||
while(frame_count>0) {
|
||||
while (frame_count > 0)
|
||||
{
|
||||
|
||||
// make sure we have room in the current buffer
|
||||
if (tx->curr_slc_buf_pos==SLC_BUF_LEN || tx->curr_slc_buf==NULL) {
|
||||
if (tx->curr_slc_buf_pos == SLC_BUF_LEN || tx->curr_slc_buf == NULL)
|
||||
{
|
||||
// no room in the current buffer? if there are no buffers available then exit
|
||||
if (tx->slc_queue_len == 0)
|
||||
{
|
||||
if (nb) {
|
||||
if (nb)
|
||||
{
|
||||
// if nonblocking just return the number of frames written so far
|
||||
break;
|
||||
}
|
||||
else {
|
||||
while (1) {
|
||||
if (tx->slc_queue_len > 0) {
|
||||
else
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
if (tx->slc_queue_len > 0)
|
||||
{
|
||||
break;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
optimistic_yield(10000);
|
||||
}
|
||||
}
|
||||
@ -362,7 +432,7 @@ static uint16_t _i2s_write_buffer(int16_t *frames, uint16_t frame_count, bool mo
|
||||
ETS_SLC_INTR_DISABLE();
|
||||
tx->curr_slc_buf = (uint32_t *)i2s_slc_queue_next_item(tx);
|
||||
ETS_SLC_INTR_ENABLE();
|
||||
tx->curr_slc_buf_pos=0;
|
||||
tx->curr_slc_buf_pos = 0;
|
||||
}
|
||||
|
||||
//space available in the current buffer
|
||||
@ -370,15 +440,18 @@ static uint16_t _i2s_write_buffer(int16_t *frames, uint16_t frame_count, bool mo
|
||||
|
||||
uint16_t fc = (available < frame_count) ? available : frame_count;
|
||||
|
||||
if (mono) {
|
||||
for(uint16_t i=0;i<fc;i++){
|
||||
if (mono)
|
||||
{
|
||||
for (uint16_t i = 0; i < fc; i++)
|
||||
{
|
||||
uint16_t v = (uint16_t)(*frames++);
|
||||
tx->curr_slc_buf[tx->curr_slc_buf_pos++] = (v << 16) | v;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(uint16_t i=0;i<fc;i++){
|
||||
for (uint16_t i = 0; i < fc; i++)
|
||||
{
|
||||
uint16_t v1 = (uint16_t)(*frames++);
|
||||
uint16_t v2 = (uint16_t)(*frames++);
|
||||
tx->curr_slc_buf[tx->curr_slc_buf_pos++] = (v1 << 16) | v2;
|
||||
@ -389,29 +462,50 @@ static uint16_t _i2s_write_buffer(int16_t *frames, uint16_t frame_count, bool mo
|
||||
frames_written += fc;
|
||||
}
|
||||
return frames_written;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t i2s_write_buffer_mono_nb(int16_t *frames, uint16_t frame_count) { return _i2s_write_buffer(frames, frame_count, true, true); }
|
||||
uint16_t i2s_write_buffer_mono_nb(int16_t *frames, uint16_t frame_count)
|
||||
{
|
||||
return _i2s_write_buffer(frames, frame_count, true, true);
|
||||
}
|
||||
|
||||
uint16_t i2s_write_buffer_mono(int16_t *frames, uint16_t frame_count) { return _i2s_write_buffer(frames, frame_count, true, false); }
|
||||
uint16_t i2s_write_buffer_mono(int16_t *frames, uint16_t frame_count)
|
||||
{
|
||||
return _i2s_write_buffer(frames, frame_count, true, false);
|
||||
}
|
||||
|
||||
uint16_t i2s_write_buffer_nb(int16_t *frames, uint16_t frame_count) { return _i2s_write_buffer(frames, frame_count, false, true); }
|
||||
uint16_t i2s_write_buffer_nb(int16_t *frames, uint16_t frame_count)
|
||||
{
|
||||
return _i2s_write_buffer(frames, frame_count, false, true);
|
||||
}
|
||||
|
||||
uint16_t i2s_write_buffer(int16_t *frames, uint16_t frame_count) { return _i2s_write_buffer(frames, frame_count, false, false); }
|
||||
uint16_t i2s_write_buffer(int16_t *frames, uint16_t frame_count)
|
||||
{
|
||||
return _i2s_write_buffer(frames, frame_count, false, false);
|
||||
}
|
||||
|
||||
bool i2s_read_sample(int16_t *left, int16_t *right, bool blocking) {
|
||||
if (!rx) {
|
||||
bool i2s_read_sample(int16_t *left, int16_t *right, bool blocking)
|
||||
{
|
||||
if (!rx)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (rx->curr_slc_buf_pos==SLC_BUF_LEN || rx->curr_slc_buf==NULL) {
|
||||
if (rx->slc_queue_len == 0) {
|
||||
if (!blocking) {
|
||||
if (rx->curr_slc_buf_pos == SLC_BUF_LEN || rx->curr_slc_buf == NULL)
|
||||
{
|
||||
if (rx->slc_queue_len == 0)
|
||||
{
|
||||
if (!blocking)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
while (1) {
|
||||
if (rx->slc_queue_len > 0){
|
||||
while (1)
|
||||
{
|
||||
if (rx->slc_queue_len > 0)
|
||||
{
|
||||
break;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
optimistic_yield(10000);
|
||||
}
|
||||
}
|
||||
@ -419,36 +513,43 @@ bool i2s_read_sample(int16_t *left, int16_t *right, bool blocking) {
|
||||
ETS_SLC_INTR_DISABLE();
|
||||
rx->curr_slc_buf = (uint32_t *)i2s_slc_queue_next_item(rx);
|
||||
ETS_SLC_INTR_ENABLE();
|
||||
rx->curr_slc_buf_pos=0;
|
||||
rx->curr_slc_buf_pos = 0;
|
||||
}
|
||||
|
||||
uint32_t sample = rx->curr_slc_buf[rx->curr_slc_buf_pos++];
|
||||
if (left) {
|
||||
if (left)
|
||||
{
|
||||
*left = sample & 0xffff;
|
||||
}
|
||||
if (right) {
|
||||
if (right)
|
||||
{
|
||||
*right = sample >> 16;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void i2s_set_rate(uint32_t rate) { //Rate in HZ
|
||||
if (rate == _i2s_sample_rate) {
|
||||
void i2s_set_rate(uint32_t rate) //Rate in HZ
|
||||
{
|
||||
if (rate == _i2s_sample_rate)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_i2s_sample_rate = rate;
|
||||
|
||||
uint32_t scaled_base_freq = I2SBASEFREQ/32;
|
||||
uint32_t scaled_base_freq = I2SBASEFREQ / 32;
|
||||
float delta_best = scaled_base_freq;
|
||||
|
||||
uint8_t sbd_div_best=1;
|
||||
uint8_t scd_div_best=1;
|
||||
for (uint8_t i=1; i<64; i++) {
|
||||
for (uint8_t j=i; j<64; j++) {
|
||||
float new_delta = fabs(((float)scaled_base_freq/i/j) - rate);
|
||||
if (new_delta < delta_best){
|
||||
uint8_t sbd_div_best = 1;
|
||||
uint8_t scd_div_best = 1;
|
||||
for (uint8_t i = 1; i < 64; i++)
|
||||
{
|
||||
for (uint8_t j = i; j < 64; j++)
|
||||
{
|
||||
float new_delta = fabs(((float)scaled_base_freq / i / j) - rate);
|
||||
if (new_delta < delta_best)
|
||||
{
|
||||
delta_best = new_delta;
|
||||
sbd_div_best = i;
|
||||
scd_div_best = j;
|
||||
@ -456,10 +557,11 @@ void i2s_set_rate(uint32_t rate) { //Rate in HZ
|
||||
}
|
||||
}
|
||||
|
||||
i2s_set_dividers( sbd_div_best, scd_div_best );
|
||||
}
|
||||
i2s_set_dividers(sbd_div_best, scd_div_best);
|
||||
}
|
||||
|
||||
void i2s_set_dividers(uint8_t div1, uint8_t div2) {
|
||||
void i2s_set_dividers(uint8_t div1, uint8_t div2)
|
||||
{
|
||||
// Ensure dividers fit in bit fields
|
||||
div1 &= I2SBDM;
|
||||
div2 &= I2SCDM;
|
||||
@ -472,20 +574,25 @@ void i2s_set_dividers(uint8_t div1, uint8_t div2) {
|
||||
// I2SRMS, I2STMS = 1-bit delay from WS to MSB (I2S format)
|
||||
// div1, div2 = Set I2S WS clock frequency. BCLK seems to be generated from 32x this
|
||||
I2SC |= I2SRF | I2SMR | I2SRMS | I2STMS | (div1 << I2SBD) | (div2 << I2SCD);
|
||||
}
|
||||
}
|
||||
|
||||
float i2s_get_real_rate(){
|
||||
return (float)I2SBASEFREQ/32/((I2SC>>I2SBD) & I2SBDM)/((I2SC >> I2SCD) & I2SCDM);
|
||||
}
|
||||
float i2s_get_real_rate()
|
||||
{
|
||||
return (float)I2SBASEFREQ / 32 / ((I2SC >> I2SBD) & I2SBDM) / ((I2SC >> I2SCD) & I2SCDM);
|
||||
}
|
||||
|
||||
bool i2s_rxtx_begin(bool enableRx, bool enableTx) {
|
||||
if (tx || rx) {
|
||||
bool i2s_rxtx_begin(bool enableRx, bool enableTx)
|
||||
{
|
||||
if (tx || rx)
|
||||
{
|
||||
i2s_end(); // Stop and free any ongoing stuff
|
||||
}
|
||||
|
||||
if (enableTx) {
|
||||
if (enableTx)
|
||||
{
|
||||
tx = (i2s_state_t*)calloc(1, sizeof(*tx));
|
||||
if (!tx) {
|
||||
if (!tx)
|
||||
{
|
||||
// Nothing to clean up yet
|
||||
return false; // OOM Error!
|
||||
}
|
||||
@ -493,9 +600,11 @@ bool i2s_rxtx_begin(bool enableRx, bool enableTx) {
|
||||
pinMode(I2SO_DATA, FUNCTION_1);
|
||||
pinMode(I2SO_BCK, FUNCTION_1);
|
||||
}
|
||||
if (enableRx) {
|
||||
if (enableRx)
|
||||
{
|
||||
rx = (i2s_state_t*)calloc(1, sizeof(*rx));
|
||||
if (!rx) {
|
||||
if (!rx)
|
||||
{
|
||||
i2s_end(); // Clean up any TX or pin changes
|
||||
return false; // OOM error!
|
||||
}
|
||||
@ -508,7 +617,8 @@ bool i2s_rxtx_begin(bool enableRx, bool enableTx) {
|
||||
}
|
||||
|
||||
_i2s_sample_rate = 0;
|
||||
if (!i2s_slc_begin()) {
|
||||
if (!i2s_slc_begin())
|
||||
{
|
||||
// OOM in SLC memory allocations, tear it all down and abort!
|
||||
i2s_end();
|
||||
return false;
|
||||
@ -532,21 +642,24 @@ bool i2s_rxtx_begin(bool enableRx, bool enableTx) {
|
||||
|
||||
i2s_set_rate(44100);
|
||||
|
||||
if (rx) {
|
||||
if (rx)
|
||||
{
|
||||
// Need to prime the # of samples to receive in the engine
|
||||
I2SRXEN = SLC_BUF_LEN;
|
||||
}
|
||||
|
||||
I2SC |= (rx?I2SRXS:0) | (tx?I2STXS:0); // Start transmission/reception
|
||||
I2SC |= (rx ? I2SRXS : 0) | (tx ? I2STXS : 0); // Start transmission/reception
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void i2s_begin() {
|
||||
void i2s_begin()
|
||||
{
|
||||
i2s_rxtx_begin(false, true);
|
||||
}
|
||||
}
|
||||
|
||||
void i2s_end() {
|
||||
void i2s_end()
|
||||
{
|
||||
// Disable any I2S send or receive
|
||||
// ? Maybe not needed since we're resetting on the next line...
|
||||
I2SC &= ~(I2STXS | I2SRXS);
|
||||
@ -558,20 +671,22 @@ void i2s_end() {
|
||||
|
||||
i2s_slc_end();
|
||||
|
||||
if (tx) {
|
||||
if (tx)
|
||||
{
|
||||
pinMode(I2SO_DATA, INPUT);
|
||||
pinMode(I2SO_BCK, INPUT);
|
||||
pinMode(I2SO_WS, INPUT);
|
||||
free(tx);
|
||||
tx = NULL;
|
||||
}
|
||||
if (rx) {
|
||||
if (rx)
|
||||
{
|
||||
pinMode(I2SI_DATA, INPUT);
|
||||
pinMode(I2SI_BCK, INPUT);
|
||||
pinMode(I2SI_WS, INPUT);
|
||||
free(rx);
|
||||
rx = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -18,7 +18,7 @@
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
*/
|
||||
|
||||
//This may be used to change user task stack size:
|
||||
//#define CONT_STACKSIZE 4096
|
||||
@ -49,9 +49,9 @@ extern void (*__init_array_end)(void);
|
||||
struct rst_info resetInfo;
|
||||
|
||||
/* Not static, used in core_esp8266_postmortem.c and other places.
|
||||
* Placed into noinit section because we assign value to this variable
|
||||
* before .bss is zero-filled, and need to preserve the value.
|
||||
*/
|
||||
Placed into noinit section because we assign value to this variable
|
||||
before .bss is zero-filled, and need to preserve the value.
|
||||
*/
|
||||
cont_t* g_pcont __attribute__((section(".noinit")));
|
||||
|
||||
/* Event queue used by the main (arduino) task */
|
||||
@ -62,8 +62,8 @@ static uint32_t s_micros_at_task_start;
|
||||
|
||||
|
||||
extern "C" {
|
||||
extern const uint32_t __attribute__((section(".ver_number"))) core_version = ARDUINO_ESP8266_GIT_VER;
|
||||
const char* core_release =
|
||||
extern const uint32_t __attribute__((section(".ver_number"))) core_version = ARDUINO_ESP8266_GIT_VER;
|
||||
const char* core_release =
|
||||
#ifdef ARDUINO_ESP8266_RELEASE
|
||||
ARDUINO_ESP8266_RELEASE;
|
||||
#else
|
||||
@ -72,11 +72,13 @@ const char* core_release =
|
||||
} // extern "C"
|
||||
|
||||
void initVariant() __attribute__((weak));
|
||||
void initVariant() {
|
||||
void initVariant()
|
||||
{
|
||||
}
|
||||
|
||||
void preloop_update_frequency() __attribute__((weak));
|
||||
void preloop_update_frequency() {
|
||||
void preloop_update_frequency()
|
||||
{
|
||||
#if defined(F_CPU) && (F_CPU == 160000000L)
|
||||
REG_SET_BIT(0x3ff00014, BIT(0));
|
||||
ets_update_cpu_frequency(160);
|
||||
@ -84,29 +86,36 @@ void preloop_update_frequency() {
|
||||
}
|
||||
|
||||
|
||||
extern "C" void esp_yield() {
|
||||
if (cont_can_yield(g_pcont)) {
|
||||
extern "C" void esp_yield()
|
||||
{
|
||||
if (cont_can_yield(g_pcont))
|
||||
{
|
||||
cont_yield(g_pcont);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void esp_schedule() {
|
||||
extern "C" void esp_schedule()
|
||||
{
|
||||
ets_post(LOOP_TASK_PRIORITY, 0, 0);
|
||||
}
|
||||
|
||||
extern "C" void __yield() {
|
||||
if (cont_can_yield(g_pcont)) {
|
||||
extern "C" void __yield()
|
||||
{
|
||||
if (cont_can_yield(g_pcont))
|
||||
{
|
||||
esp_schedule();
|
||||
esp_yield();
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
panic();
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void yield(void) __attribute__ ((weak, alias("__yield")));
|
||||
extern "C" void yield(void) __attribute__((weak, alias("__yield")));
|
||||
|
||||
extern "C" void optimistic_yield(uint32_t interval_us) {
|
||||
extern "C" void optimistic_yield(uint32_t interval_us)
|
||||
{
|
||||
if (cont_can_yield(g_pcont) &&
|
||||
(system_get_time() - s_micros_at_task_start) > interval_us)
|
||||
{
|
||||
@ -114,10 +123,12 @@ extern "C" void optimistic_yield(uint32_t interval_us) {
|
||||
}
|
||||
}
|
||||
|
||||
static void loop_wrapper() {
|
||||
static void loop_wrapper()
|
||||
{
|
||||
static bool setup_done = false;
|
||||
preloop_update_frequency();
|
||||
if(!setup_done) {
|
||||
if (!setup_done)
|
||||
{
|
||||
setup();
|
||||
setup_done = true;
|
||||
}
|
||||
@ -126,56 +137,72 @@ static void loop_wrapper() {
|
||||
esp_schedule();
|
||||
}
|
||||
|
||||
static void loop_task(os_event_t *events) {
|
||||
static void loop_task(os_event_t *events)
|
||||
{
|
||||
(void) events;
|
||||
s_micros_at_task_start = system_get_time();
|
||||
cont_run(g_pcont, &loop_wrapper);
|
||||
if (cont_check(g_pcont) != 0) {
|
||||
if (cont_check(g_pcont) != 0)
|
||||
{
|
||||
panic();
|
||||
}
|
||||
}
|
||||
extern "C" {
|
||||
|
||||
struct object { long placeholder[ 10 ]; };
|
||||
void __register_frame_info (const void *begin, struct object *ob);
|
||||
extern char __eh_frame[];
|
||||
struct object
|
||||
{
|
||||
long placeholder[ 10 ];
|
||||
};
|
||||
void __register_frame_info(const void *begin, struct object *ob);
|
||||
extern char __eh_frame[];
|
||||
}
|
||||
|
||||
static void do_global_ctors(void) {
|
||||
static void do_global_ctors(void)
|
||||
{
|
||||
static struct object ob;
|
||||
__register_frame_info( __eh_frame, &ob );
|
||||
__register_frame_info(__eh_frame, &ob);
|
||||
|
||||
void (**p)(void) = &__init_array_end;
|
||||
while (p != &__init_array_start)
|
||||
{
|
||||
(*--p)();
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
extern void __unhandled_exception(const char *str);
|
||||
extern void __unhandled_exception(const char *str);
|
||||
|
||||
static void __unhandled_exception_cpp()
|
||||
{
|
||||
static void __unhandled_exception_cpp()
|
||||
{
|
||||
#ifndef __EXCEPTIONS
|
||||
abort();
|
||||
#else
|
||||
static bool terminating;
|
||||
if (terminating)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
terminating = true;
|
||||
/* Use a trick from vterminate.cc to get any std::exception what() */
|
||||
try {
|
||||
try
|
||||
{
|
||||
__throw_exception_again;
|
||||
} catch (const std::exception& e) {
|
||||
__unhandled_exception( e.what() );
|
||||
} catch (...) {
|
||||
__unhandled_exception( "" );
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
__unhandled_exception(e.what());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
__unhandled_exception("");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void init_done() {
|
||||
void init_done()
|
||||
{
|
||||
system_set_os_print(1);
|
||||
gdb_init();
|
||||
std::set_terminate(__unhandled_exception_cpp);
|
||||
@ -184,13 +211,13 @@ void init_done() {
|
||||
}
|
||||
|
||||
/* This is the entry point of the application.
|
||||
* It gets called on the default stack, which grows down from the top
|
||||
* of DRAM area.
|
||||
* .bss has not been zeroed out yet, but .data and .rodata are in place.
|
||||
* Cache is not enabled, so only ROM and IRAM functions can be called.
|
||||
* Peripherals (except for SPI0 and UART0) are not initialized.
|
||||
* This function does not return.
|
||||
*/
|
||||
It gets called on the default stack, which grows down from the top
|
||||
of DRAM area.
|
||||
.bss has not been zeroed out yet, but .data and .rodata are in place.
|
||||
Cache is not enabled, so only ROM and IRAM functions can be called.
|
||||
Peripherals (except for SPI0 and UART0) are not initialized.
|
||||
This function does not return.
|
||||
*/
|
||||
/*
|
||||
A bit of explanation for this entry point:
|
||||
|
||||
@ -240,20 +267,21 @@ extern "C" void ICACHE_RAM_ATTR app_entry_redefinable(void)
|
||||
call_user_start();
|
||||
}
|
||||
|
||||
static void ICACHE_RAM_ATTR app_entry_custom (void) __attribute__((weakref("app_entry_redefinable")));
|
||||
static void ICACHE_RAM_ATTR app_entry_custom(void) __attribute__((weakref("app_entry_redefinable")));
|
||||
|
||||
extern "C" void ICACHE_RAM_ATTR app_entry (void)
|
||||
extern "C" void ICACHE_RAM_ATTR app_entry(void)
|
||||
{
|
||||
return app_entry_custom();
|
||||
}
|
||||
|
||||
extern "C" void preinit (void) __attribute__((weak));
|
||||
extern "C" void preinit (void)
|
||||
extern "C" void preinit(void) __attribute__((weak));
|
||||
extern "C" void preinit(void)
|
||||
{
|
||||
/* do nothing by default */
|
||||
}
|
||||
|
||||
extern "C" void user_init(void) {
|
||||
extern "C" void user_init(void)
|
||||
{
|
||||
struct rst_info *rtc_info_ptr = system_get_rst_info();
|
||||
memcpy((void *) &resetInfo, (void *) rtc_info_ptr, sizeof(resetInfo));
|
||||
|
||||
|
@ -20,7 +20,7 @@
|
||||
|
||||
Modified 03 April 2015 by Markus Sattler
|
||||
|
||||
*/
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -31,22 +31,27 @@
|
||||
|
||||
extern "C" {
|
||||
|
||||
char* ltoa(long value, char* result, int base) {
|
||||
char* ltoa(long value, char* result, int base)
|
||||
{
|
||||
return itoa((int)value, result, base);
|
||||
}
|
||||
}
|
||||
|
||||
char* ultoa(unsigned long value, char* result, int base) {
|
||||
char* ultoa(unsigned long value, char* result, int base)
|
||||
{
|
||||
return utoa((unsigned int)value, result, base);
|
||||
}
|
||||
}
|
||||
|
||||
char * dtostrf(double number, signed char width, unsigned char prec, char *s) {
|
||||
char * dtostrf(double number, signed char width, unsigned char prec, char *s)
|
||||
{
|
||||
bool negative = false;
|
||||
|
||||
if (isnan(number)) {
|
||||
if (isnan(number))
|
||||
{
|
||||
strcpy(s, "nan");
|
||||
return s;
|
||||
}
|
||||
if (isinf(number)) {
|
||||
if (isinf(number))
|
||||
{
|
||||
strcpy(s, "inf");
|
||||
return s;
|
||||
}
|
||||
@ -54,12 +59,14 @@ char * dtostrf(double number, signed char width, unsigned char prec, char *s) {
|
||||
char* out = s;
|
||||
|
||||
int fillme = width; // how many cells to fill for the integer part
|
||||
if (prec > 0) {
|
||||
fillme -= (prec+1);
|
||||
if (prec > 0)
|
||||
{
|
||||
fillme -= (prec + 1);
|
||||
}
|
||||
|
||||
// Handle negative numbers
|
||||
if (number < 0.0) {
|
||||
if (number < 0.0)
|
||||
{
|
||||
negative = true;
|
||||
fillme--;
|
||||
number = -number;
|
||||
@ -69,7 +76,9 @@ char * dtostrf(double number, signed char width, unsigned char prec, char *s) {
|
||||
// I optimized out most of the divisions
|
||||
double rounding = 2.0;
|
||||
for (uint8_t i = 0; i < prec; ++i)
|
||||
{
|
||||
rounding *= 10.0;
|
||||
}
|
||||
rounding = 1.0 / rounding;
|
||||
|
||||
number += rounding;
|
||||
@ -77,7 +86,8 @@ char * dtostrf(double number, signed char width, unsigned char prec, char *s) {
|
||||
// Figure out how big our number really is
|
||||
double tenpow = 1.0;
|
||||
int digitcount = 1;
|
||||
while (number >= 10.0 * tenpow) {
|
||||
while (number >= 10.0 * tenpow)
|
||||
{
|
||||
tenpow *= 10.0;
|
||||
digitcount++;
|
||||
}
|
||||
@ -86,21 +96,30 @@ char * dtostrf(double number, signed char width, unsigned char prec, char *s) {
|
||||
fillme -= digitcount;
|
||||
|
||||
// Pad unused cells with spaces
|
||||
while (fillme-- > 0) {
|
||||
while (fillme-- > 0)
|
||||
{
|
||||
*out++ = ' ';
|
||||
}
|
||||
|
||||
// Handle negative sign
|
||||
if (negative) *out++ = '-';
|
||||
if (negative)
|
||||
{
|
||||
*out++ = '-';
|
||||
}
|
||||
|
||||
// Print the digits, and if necessary, the decimal point
|
||||
digitcount += prec;
|
||||
int8_t digit = 0;
|
||||
while (digitcount-- > 0) {
|
||||
while (digitcount-- > 0)
|
||||
{
|
||||
digit = (int8_t)number;
|
||||
if (digit > 9) digit = 9; // insurance
|
||||
if (digit > 9)
|
||||
{
|
||||
digit = 9; // insurance
|
||||
}
|
||||
*out++ = (char)('0' | digit);
|
||||
if ((digitcount == prec) && (prec > 0)) {
|
||||
if ((digitcount == prec) && (prec > 0))
|
||||
{
|
||||
*out++ = '.';
|
||||
}
|
||||
number -= digit;
|
||||
@ -110,6 +129,6 @@ char * dtostrf(double number, signed char width, unsigned char prec, char *s) {
|
||||
// make sure the string is terminated
|
||||
*out = 0;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -17,7 +17,7 @@
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
@ -30,8 +30,8 @@
|
||||
|
||||
extern "C" {
|
||||
|
||||
static const uint8_t ICACHE_FLASH_ATTR phy_init_data[128] =
|
||||
{
|
||||
static const uint8_t ICACHE_FLASH_ATTR phy_init_data[128] =
|
||||
{
|
||||
/*[0] =*/ 5, // Reserved, do not change
|
||||
/*[1] =*/ 8, // Reserved, do not change
|
||||
/*[2] =*/ 4, // Reserved, do not change
|
||||
@ -49,15 +49,15 @@ static const uint8_t ICACHE_FLASH_ATTR phy_init_data[128] =
|
||||
/*[14] =*/ 5, // Reserved, do not change
|
||||
/*[15] =*/ 5, // Reserved, do not change
|
||||
/*[16] =*/ 4, // Reserved, do not change
|
||||
/*[17] =*/ (uint8_t)-2, // Reserved, do not change
|
||||
/*[18] =*/ (uint8_t)-3, // Reserved, do not change
|
||||
/*[19] =*/ (uint8_t)-1, // Reserved, do not change
|
||||
/*[20] =*/ (uint8_t)-16, // Reserved, do not change
|
||||
/*[21] =*/ (uint8_t)-16, // Reserved, do not change
|
||||
/*[22] =*/ (uint8_t)-16, // Reserved, do not change
|
||||
/*[23] =*/ (uint8_t)-32, // Reserved, do not change
|
||||
/*[24] =*/ (uint8_t)-32, // Reserved, do not change
|
||||
/*[25] =*/ (uint8_t)-32, // Reserved, do not change
|
||||
/*[17] =*/ (uint8_t) -2, // Reserved, do not change
|
||||
/*[18] =*/ (uint8_t) -3, // Reserved, do not change
|
||||
/*[19] =*/ (uint8_t) -1, // Reserved, do not change
|
||||
/*[20] =*/ (uint8_t) -16, // Reserved, do not change
|
||||
/*[21] =*/ (uint8_t) -16, // Reserved, do not change
|
||||
/*[22] =*/ (uint8_t) -16, // Reserved, do not change
|
||||
/*[23] =*/ (uint8_t) -32, // Reserved, do not change
|
||||
/*[24] =*/ (uint8_t) -32, // Reserved, do not change
|
||||
/*[25] =*/ (uint8_t) -32, // Reserved, do not change
|
||||
|
||||
/*[26] =*/ 225, // spur_freq_cfg, spur_freq=spur_freq_cfg/spur_freq_cfg_div
|
||||
/*[27] =*/ 10, // spur_freq_cfg_div
|
||||
@ -90,11 +90,11 @@ static const uint8_t ICACHE_FLASH_ATTR phy_init_data[128] =
|
||||
// 0: 40MHz
|
||||
// 1: 26MHz
|
||||
// 2: 24MHz
|
||||
#if F_CRYSTAL == 40000000
|
||||
#if F_CRYSTAL == 40000000
|
||||
/*[48] =*/ 0,
|
||||
#else
|
||||
#else
|
||||
/*[48] =*/ 1,
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*[49] =*/ 0,
|
||||
|
||||
@ -286,60 +286,61 @@ static const uint8_t ICACHE_FLASH_ATTR phy_init_data[128] =
|
||||
// 2: RF init no RF CAL, using all RF CAL data in flash, it takes about 2ms for RF init (same as 0?!)
|
||||
// 3: RF init do all RF CAL, it takes about 200ms for RF init
|
||||
/*[114] =*/ 1
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
// These functions will be overriden from C++ code.
|
||||
// Unfortunately, we can't use extern "C" because Arduino preprocessor
|
||||
// doesn't generate forward declarations for extern "C" functions correctly,
|
||||
// so we use mangled names here.
|
||||
// These functions will be overriden from C++ code.
|
||||
// Unfortunately, we can't use extern "C" because Arduino preprocessor
|
||||
// doesn't generate forward declarations for extern "C" functions correctly,
|
||||
// so we use mangled names here.
|
||||
#define __get_adc_mode _Z14__get_adc_modev
|
||||
#define __get_rf_mode _Z13__get_rf_modev
|
||||
#define __run_user_rf_pre_init _Z22__run_user_rf_pre_initv
|
||||
|
||||
static bool spoof_init_data = false;
|
||||
static bool spoof_init_data = false;
|
||||
|
||||
extern int __real_spi_flash_read(uint32_t addr, uint32_t* dst, size_t size);
|
||||
extern int ICACHE_RAM_ATTR __wrap_spi_flash_read(uint32_t addr, uint32_t* dst, size_t size);
|
||||
extern int __get_adc_mode();
|
||||
extern int __real_spi_flash_read(uint32_t addr, uint32_t* dst, size_t size);
|
||||
extern int ICACHE_RAM_ATTR __wrap_spi_flash_read(uint32_t addr, uint32_t* dst, size_t size);
|
||||
extern int __get_adc_mode();
|
||||
|
||||
extern int ICACHE_RAM_ATTR __wrap_spi_flash_read(uint32_t addr, uint32_t* dst, size_t size)
|
||||
{
|
||||
if (!spoof_init_data || size != 128) {
|
||||
extern int ICACHE_RAM_ATTR __wrap_spi_flash_read(uint32_t addr, uint32_t* dst, size_t size)
|
||||
{
|
||||
if (!spoof_init_data || size != 128)
|
||||
{
|
||||
return __real_spi_flash_read(addr, dst, size);
|
||||
}
|
||||
|
||||
memcpy(dst, phy_init_data, sizeof(phy_init_data));
|
||||
((uint8_t*)dst)[107] = __get_adc_mode();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
extern int __get_rf_mode(void) __attribute__((weak));
|
||||
extern int __get_rf_mode(void)
|
||||
{
|
||||
extern int __get_rf_mode(void) __attribute__((weak));
|
||||
extern int __get_rf_mode(void)
|
||||
{
|
||||
return -1; // mode not set
|
||||
}
|
||||
}
|
||||
|
||||
extern int __get_adc_mode(void) __attribute__((weak));
|
||||
extern int __get_adc_mode(void)
|
||||
{
|
||||
extern int __get_adc_mode(void) __attribute__((weak));
|
||||
extern int __get_adc_mode(void)
|
||||
{
|
||||
return 33; // default ADC mode
|
||||
}
|
||||
}
|
||||
|
||||
extern void __run_user_rf_pre_init(void) __attribute__((weak));
|
||||
extern void __run_user_rf_pre_init(void)
|
||||
{
|
||||
extern void __run_user_rf_pre_init(void) __attribute__((weak));
|
||||
extern void __run_user_rf_pre_init(void)
|
||||
{
|
||||
return; // default do noting
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t user_rf_cal_sector_set(void)
|
||||
{
|
||||
uint32_t user_rf_cal_sector_set(void)
|
||||
{
|
||||
spoof_init_data = true;
|
||||
return flashchip->chip_size/SPI_FLASH_SEC_SIZE - 4;
|
||||
}
|
||||
return flashchip->chip_size / SPI_FLASH_SEC_SIZE - 4;
|
||||
}
|
||||
|
||||
void user_rf_pre_init()
|
||||
{
|
||||
void user_rf_pre_init()
|
||||
{
|
||||
// *((volatile uint32_t*) 0x60000710) = 0;
|
||||
spoof_init_data = false;
|
||||
volatile uint32_t* rtc_reg = (volatile uint32_t*) 0x60001000;
|
||||
@ -347,13 +348,14 @@ void user_rf_pre_init()
|
||||
|
||||
system_set_os_print(0);
|
||||
int rf_mode = __get_rf_mode();
|
||||
if (rf_mode >= 0) {
|
||||
if (rf_mode >= 0)
|
||||
{
|
||||
system_phy_set_rfoption(rf_mode);
|
||||
}
|
||||
__run_user_rf_pre_init();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ICACHE_RAM_ATTR user_spi_flash_dio_to_qio_pre_init() {}
|
||||
void ICACHE_RAM_ATTR user_spi_flash_dio_to_qio_pre_init() {}
|
||||
|
||||
};
|
||||
|
@ -16,7 +16,7 @@
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
*/
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
@ -35,58 +35,63 @@
|
||||
|
||||
extern "C" {
|
||||
|
||||
extern void __real_system_restart_local();
|
||||
extern void __real_system_restart_local();
|
||||
|
||||
// These will be pointers to PROGMEM const strings
|
||||
static const char* s_panic_file = 0;
|
||||
static int s_panic_line = 0;
|
||||
static const char* s_panic_func = 0;
|
||||
static const char* s_panic_what = 0;
|
||||
// These will be pointers to PROGMEM const strings
|
||||
static const char* s_panic_file = 0;
|
||||
static int s_panic_line = 0;
|
||||
static const char* s_panic_func = 0;
|
||||
static const char* s_panic_what = 0;
|
||||
|
||||
static bool s_abort_called = false;
|
||||
static const char* s_unhandled_exception = NULL;
|
||||
static bool s_abort_called = false;
|
||||
static const char* s_unhandled_exception = NULL;
|
||||
|
||||
void abort() __attribute__((noreturn));
|
||||
static void uart_write_char_d(char c);
|
||||
static void uart0_write_char_d(char c);
|
||||
static void uart1_write_char_d(char c);
|
||||
static void print_stack(uint32_t start, uint32_t end);
|
||||
void abort() __attribute__((noreturn));
|
||||
static void uart_write_char_d(char c);
|
||||
static void uart0_write_char_d(char c);
|
||||
static void uart1_write_char_d(char c);
|
||||
static void print_stack(uint32_t start, uint32_t end);
|
||||
|
||||
// From UMM, the last caller of a malloc/realloc/calloc which failed:
|
||||
extern void *umm_last_fail_alloc_addr;
|
||||
extern int umm_last_fail_alloc_size;
|
||||
// From UMM, the last caller of a malloc/realloc/calloc which failed:
|
||||
extern void *umm_last_fail_alloc_addr;
|
||||
extern int umm_last_fail_alloc_size;
|
||||
|
||||
static void raise_exception() __attribute__((noreturn));
|
||||
static void raise_exception() __attribute__((noreturn));
|
||||
|
||||
extern void __custom_crash_callback( struct rst_info * rst_info, uint32_t stack, uint32_t stack_end ) {
|
||||
extern void __custom_crash_callback(struct rst_info * rst_info, uint32_t stack, uint32_t stack_end)
|
||||
{
|
||||
(void) rst_info;
|
||||
(void) stack;
|
||||
(void) stack_end;
|
||||
}
|
||||
}
|
||||
|
||||
extern void custom_crash_callback( struct rst_info * rst_info, uint32_t stack, uint32_t stack_end ) __attribute__ ((weak, alias("__custom_crash_callback")));
|
||||
extern void custom_crash_callback(struct rst_info * rst_info, uint32_t stack, uint32_t stack_end) __attribute__((weak, alias("__custom_crash_callback")));
|
||||
|
||||
|
||||
// Prints need to use our library function to allow for file and function
|
||||
// to be safely accessed from flash. This function encapsulates snprintf()
|
||||
// [which by definition will 0-terminate] and dumping to the UART
|
||||
static void ets_printf_P(const char *str, ...) {
|
||||
// Prints need to use our library function to allow for file and function
|
||||
// to be safely accessed from flash. This function encapsulates snprintf()
|
||||
// [which by definition will 0-terminate] and dumping to the UART
|
||||
static void ets_printf_P(const char *str, ...)
|
||||
{
|
||||
char destStr[160];
|
||||
char *c = destStr;
|
||||
va_list argPtr;
|
||||
va_start(argPtr, str);
|
||||
vsnprintf(destStr, sizeof(destStr), str, argPtr);
|
||||
va_end(argPtr);
|
||||
while (*c) {
|
||||
while (*c)
|
||||
{
|
||||
ets_putc(*(c++));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void __wrap_system_restart_local() {
|
||||
void __wrap_system_restart_local()
|
||||
{
|
||||
register uint32_t sp asm("a1");
|
||||
uint32_t sp_dump = sp;
|
||||
|
||||
if (gdb_present()) {
|
||||
if (gdb_present())
|
||||
{
|
||||
/* When GDBStub is present, exceptions are handled by GDBStub,
|
||||
but Soft WDT will still call this function.
|
||||
Trigger an exception to break into GDB.
|
||||
@ -108,28 +113,34 @@ void __wrap_system_restart_local() {
|
||||
// TODO: ets_install_putc1 definition is wrong in ets_sys.h, need cast
|
||||
ets_install_putc1((void *)&uart_write_char_d);
|
||||
|
||||
if (s_panic_line) {
|
||||
if (s_panic_line)
|
||||
{
|
||||
ets_printf_P(PSTR("\nPanic %S:%d %S"), s_panic_file, s_panic_line, s_panic_func);
|
||||
if (s_panic_what) {
|
||||
if (s_panic_what)
|
||||
{
|
||||
ets_printf_P(PSTR(": Assertion '%S' failed."), s_panic_what);
|
||||
}
|
||||
ets_putc('\n');
|
||||
}
|
||||
else if (s_unhandled_exception) {
|
||||
else if (s_unhandled_exception)
|
||||
{
|
||||
ets_printf_P(PSTR("\nUnhandled C++ exception: %S\n"), s_unhandled_exception);
|
||||
}
|
||||
else if (s_abort_called) {
|
||||
else if (s_abort_called)
|
||||
{
|
||||
ets_printf_P(PSTR("\nAbort called\n"));
|
||||
}
|
||||
else if (rst_info.reason == REASON_EXCEPTION_RST) {
|
||||
else if (rst_info.reason == REASON_EXCEPTION_RST)
|
||||
{
|
||||
ets_printf_P(PSTR("\nException (%d):\nepc1=0x%08x epc2=0x%08x epc3=0x%08x excvaddr=0x%08x depc=0x%08x\n"),
|
||||
rst_info.exccause, rst_info.epc1, rst_info.epc2, rst_info.epc3, rst_info.excvaddr, rst_info.depc);
|
||||
}
|
||||
else if (rst_info.reason == REASON_SOFT_WDT_RST) {
|
||||
else if (rst_info.reason == REASON_SOFT_WDT_RST)
|
||||
{
|
||||
ets_printf_P(PSTR("\nSoft WDT reset\n"));
|
||||
}
|
||||
|
||||
uint32_t cont_stack_start = (uint32_t) &(g_pcont->stack);
|
||||
uint32_t cont_stack_start = (uint32_t) & (g_pcont->stack);
|
||||
uint32_t cont_stack_end = (uint32_t) g_pcont->stack_end;
|
||||
uint32_t stack_end;
|
||||
|
||||
@ -137,19 +148,23 @@ void __wrap_system_restart_local() {
|
||||
// and everything up to __wrap_system_restart_local
|
||||
// (determined empirically, might break)
|
||||
uint32_t offset = 0;
|
||||
if (rst_info.reason == REASON_SOFT_WDT_RST) {
|
||||
if (rst_info.reason == REASON_SOFT_WDT_RST)
|
||||
{
|
||||
offset = 0x1b0;
|
||||
}
|
||||
else if (rst_info.reason == REASON_EXCEPTION_RST) {
|
||||
else if (rst_info.reason == REASON_EXCEPTION_RST)
|
||||
{
|
||||
offset = 0x1a0;
|
||||
}
|
||||
else if (rst_info.reason == REASON_WDT_RST) {
|
||||
else if (rst_info.reason == REASON_WDT_RST)
|
||||
{
|
||||
offset = 0x10;
|
||||
}
|
||||
|
||||
ets_printf_P(PSTR("\n>>>stack>>>\n"));
|
||||
|
||||
if (sp_dump > stack_thunk_get_stack_bot() && sp_dump <= stack_thunk_get_stack_top()) {
|
||||
if (sp_dump > stack_thunk_get_stack_bot() && sp_dump <= stack_thunk_get_stack_top())
|
||||
{
|
||||
// BearSSL we dump the BSSL second stack and then reset SP back to the main cont stack
|
||||
ets_printf_P(PSTR("\nctx: bearssl\nsp: %08x end: %08x offset: %04x\n"), sp_dump, stack_thunk_get_stack_top(), offset);
|
||||
print_stack(sp_dump + offset, stack_thunk_get_stack_top());
|
||||
@ -157,11 +172,13 @@ void __wrap_system_restart_local() {
|
||||
sp_dump = stack_thunk_get_cont_sp();
|
||||
}
|
||||
|
||||
if (sp_dump > cont_stack_start && sp_dump < cont_stack_end) {
|
||||
if (sp_dump > cont_stack_start && sp_dump < cont_stack_end)
|
||||
{
|
||||
ets_printf_P(PSTR("\nctx: cont\n"));
|
||||
stack_end = cont_stack_end;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
ets_printf_P(PSTR("\nctx: sys\n"));
|
||||
stack_end = 0x3fffffb0;
|
||||
// it's actually 0x3ffffff0, but the stuff below ets_run
|
||||
@ -175,83 +192,96 @@ void __wrap_system_restart_local() {
|
||||
ets_printf_P(PSTR("<<<stack<<<\n"));
|
||||
|
||||
// Use cap-X formatting to ensure the standard EspExceptionDecoder doesn't match the address
|
||||
if (umm_last_fail_alloc_addr) {
|
||||
if (umm_last_fail_alloc_addr)
|
||||
{
|
||||
ets_printf_P(PSTR("\nlast failed alloc call: %08X(%d)\n"), (uint32_t)umm_last_fail_alloc_addr, umm_last_fail_alloc_size);
|
||||
}
|
||||
|
||||
custom_crash_callback( &rst_info, sp_dump + offset, stack_end );
|
||||
custom_crash_callback(&rst_info, sp_dump + offset, stack_end);
|
||||
|
||||
ets_delay_us(10000);
|
||||
__real_system_restart_local();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void print_stack(uint32_t start, uint32_t end) {
|
||||
for (uint32_t pos = start; pos < end; pos += 0x10) {
|
||||
static void print_stack(uint32_t start, uint32_t end)
|
||||
{
|
||||
for (uint32_t pos = start; pos < end; pos += 0x10)
|
||||
{
|
||||
uint32_t* values = (uint32_t*)(pos);
|
||||
|
||||
// rough indicator: stack frames usually have SP saved as the second word
|
||||
bool looksLikeStackFrame = (values[2] == pos + 0x10);
|
||||
|
||||
ets_printf_P(PSTR("%08x: %08x %08x %08x %08x %c\n"),
|
||||
pos, values[0], values[1], values[2], values[3], (looksLikeStackFrame)?'<':' ');
|
||||
pos, values[0], values[1], values[2], values[3], (looksLikeStackFrame) ? '<' : ' ');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void uart_write_char_d(char c) {
|
||||
static void uart_write_char_d(char c)
|
||||
{
|
||||
uart0_write_char_d(c);
|
||||
uart1_write_char_d(c);
|
||||
}
|
||||
}
|
||||
|
||||
static void uart0_write_char_d(char c) {
|
||||
static void uart0_write_char_d(char c)
|
||||
{
|
||||
while (((USS(0) >> USTXC) & 0xff)) { }
|
||||
|
||||
if (c == '\n') {
|
||||
if (c == '\n')
|
||||
{
|
||||
USF(0) = '\r';
|
||||
}
|
||||
USF(0) = c;
|
||||
}
|
||||
}
|
||||
|
||||
static void uart1_write_char_d(char c) {
|
||||
static void uart1_write_char_d(char c)
|
||||
{
|
||||
while (((USS(1) >> USTXC) & 0xff) >= 0x7e) { }
|
||||
|
||||
if (c == '\n') {
|
||||
if (c == '\n')
|
||||
{
|
||||
USF(1) = '\r';
|
||||
}
|
||||
USF(1) = c;
|
||||
}
|
||||
}
|
||||
|
||||
static void raise_exception() {
|
||||
__asm__ __volatile__ ("syscall");
|
||||
static void raise_exception()
|
||||
{
|
||||
__asm__ __volatile__("syscall");
|
||||
while (1); // never reached, needed to satisfy "noreturn" attribute
|
||||
}
|
||||
}
|
||||
|
||||
void abort() {
|
||||
void abort()
|
||||
{
|
||||
s_abort_called = true;
|
||||
raise_exception();
|
||||
}
|
||||
}
|
||||
|
||||
void __unhandled_exception(const char *str) {
|
||||
void __unhandled_exception(const char *str)
|
||||
{
|
||||
s_unhandled_exception = str;
|
||||
raise_exception();
|
||||
}
|
||||
}
|
||||
|
||||
void __assert_func(const char *file, int line, const char *func, const char *what) {
|
||||
void __assert_func(const char *file, int line, const char *func, const char *what)
|
||||
{
|
||||
s_panic_file = file;
|
||||
s_panic_line = line;
|
||||
s_panic_func = func;
|
||||
s_panic_what = what;
|
||||
gdb_do_break(); /* if GDB is not present, this is a no-op */
|
||||
raise_exception();
|
||||
}
|
||||
}
|
||||
|
||||
void __panic_func(const char* file, int line, const char* func) {
|
||||
void __panic_func(const char* file, int line, const char* func)
|
||||
{
|
||||
s_panic_file = file;
|
||||
s_panic_line = line;
|
||||
s_panic_func = func;
|
||||
s_panic_what = 0;
|
||||
gdb_do_break(); /* if GDB is not present, this is a no-op */
|
||||
raise_exception();
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -25,23 +25,23 @@
|
||||
|
||||
extern "C" {
|
||||
|
||||
unsigned int preferred_si2c_clock = 100000;
|
||||
unsigned int preferred_si2c_clock = 100000;
|
||||
#include "twi_util.h"
|
||||
|
||||
#include "ets_sys.h"
|
||||
|
||||
unsigned char twi_dcount = 18;
|
||||
static unsigned char twi_sda, twi_scl;
|
||||
static uint32_t twi_clockStretchLimit;
|
||||
static unsigned char twi_addr = 0;
|
||||
unsigned char twi_dcount = 18;
|
||||
static unsigned char twi_sda, twi_scl;
|
||||
static uint32_t twi_clockStretchLimit;
|
||||
static unsigned char twi_addr = 0;
|
||||
|
||||
// modes (private)
|
||||
// modes (private)
|
||||
#define TWIPM_UNKNOWN 0
|
||||
#define TWIPM_IDLE 1
|
||||
#define TWIPM_ADDRESSED 2
|
||||
#define TWIPM_WAIT 3
|
||||
|
||||
// states (private)
|
||||
// states (private)
|
||||
#define TWIP_UNKNOWN 0
|
||||
#define TWIP_IDLE 1
|
||||
#define TWIP_START 2
|
||||
@ -59,37 +59,37 @@ static unsigned char twi_addr = 0;
|
||||
#define TWIP_WRITE 14
|
||||
#define TWIP_BUS_ERR 15
|
||||
|
||||
static volatile uint8_t twip_mode = TWIPM_IDLE;
|
||||
static volatile uint8_t twip_state = TWIP_IDLE;
|
||||
static volatile uint8_t twip_status = TW_NO_INFO;
|
||||
static volatile uint8_t bitCount = 0;
|
||||
static volatile uint8_t twip_mode = TWIPM_IDLE;
|
||||
static volatile uint8_t twip_state = TWIP_IDLE;
|
||||
static volatile uint8_t twip_status = TW_NO_INFO;
|
||||
static volatile uint8_t bitCount = 0;
|
||||
|
||||
#define TWDR twi_data
|
||||
static volatile uint8_t twi_data = 0x00;
|
||||
static volatile uint8_t twi_ack = 0;
|
||||
static volatile uint8_t twi_ack_rec = 0;
|
||||
static volatile int twi_timeout_ms = 10;
|
||||
static volatile uint8_t twi_data = 0x00;
|
||||
static volatile uint8_t twi_ack = 0;
|
||||
static volatile uint8_t twi_ack_rec = 0;
|
||||
static volatile int twi_timeout_ms = 10;
|
||||
|
||||
#define TWI_READY 0
|
||||
#define TWI_MRX 1
|
||||
#define TWI_MTX 2
|
||||
#define TWI_SRX 3
|
||||
#define TWI_STX 4
|
||||
static volatile uint8_t twi_state = TWI_READY;
|
||||
static volatile uint8_t twi_error = 0xFF;
|
||||
static volatile uint8_t twi_state = TWI_READY;
|
||||
static volatile uint8_t twi_error = 0xFF;
|
||||
|
||||
static uint8_t twi_txBuffer[TWI_BUFFER_LENGTH];
|
||||
static volatile uint8_t twi_txBufferIndex;
|
||||
static volatile uint8_t twi_txBufferLength;
|
||||
static uint8_t twi_txBuffer[TWI_BUFFER_LENGTH];
|
||||
static volatile uint8_t twi_txBufferIndex;
|
||||
static volatile uint8_t twi_txBufferLength;
|
||||
|
||||
static uint8_t twi_rxBuffer[TWI_BUFFER_LENGTH];
|
||||
static volatile uint8_t twi_rxBufferIndex;
|
||||
static uint8_t twi_rxBuffer[TWI_BUFFER_LENGTH];
|
||||
static volatile uint8_t twi_rxBufferIndex;
|
||||
|
||||
static void (*twi_onSlaveTransmit)(void);
|
||||
static void (*twi_onSlaveReceive)(uint8_t*, size_t);
|
||||
static void (*twi_onSlaveTransmit)(void);
|
||||
static void (*twi_onSlaveReceive)(uint8_t*, size_t);
|
||||
|
||||
static void onSclChange(void);
|
||||
static void onSdaChange(void);
|
||||
static void onSclChange(void);
|
||||
static void onSdaChange(void);
|
||||
|
||||
#define EVENTTASK_QUEUE_SIZE 1
|
||||
#define EVENTTASK_QUEUE_PRIO 2
|
||||
@ -98,10 +98,10 @@ static void onSdaChange(void);
|
||||
#define TWI_SIG_RX (TWI_SIG_RANGE + 0x01)
|
||||
#define TWI_SIG_TX (TWI_SIG_RANGE + 0x02)
|
||||
|
||||
static ETSEvent eventTaskQueue[EVENTTASK_QUEUE_SIZE];
|
||||
static void eventTask(ETSEvent *e);
|
||||
static ETSTimer timer;
|
||||
static void onTimer(void *unused);
|
||||
static ETSEvent eventTaskQueue[EVENTTASK_QUEUE_SIZE];
|
||||
static void eventTask(ETSEvent *e);
|
||||
static ETSTimer timer;
|
||||
static void onTimer(void *unused);
|
||||
|
||||
#define SDA_LOW() (GPES = (1 << twi_sda)) //Enable SDA (becomes output and since GPO is 0 for the pin, it will pull the line low)
|
||||
#define SDA_HIGH() (GPEC = (1 << twi_sda)) //Disable SDA (becomes input and since it has pullup it will go high)
|
||||
@ -120,33 +120,77 @@ static void onTimer(void *unused);
|
||||
#define TWI_CLOCK_STRETCH_MULTIPLIER 6
|
||||
#endif
|
||||
|
||||
void twi_setClock(unsigned int freq){
|
||||
void twi_setClock(unsigned int freq)
|
||||
{
|
||||
preferred_si2c_clock = freq;
|
||||
#if F_CPU == FCPU80
|
||||
if(freq <= 50000) twi_dcount = 38;//about 50KHz
|
||||
else if(freq <= 100000) twi_dcount = 19;//about 100KHz
|
||||
else if(freq <= 200000) twi_dcount = 8;//about 200KHz
|
||||
else if(freq <= 300000) twi_dcount = 3;//about 300KHz
|
||||
else if(freq <= 400000) twi_dcount = 1;//about 400KHz
|
||||
else twi_dcount = 1;//about 400KHz
|
||||
if (freq <= 50000)
|
||||
{
|
||||
twi_dcount = 38; //about 50KHz
|
||||
}
|
||||
else if (freq <= 100000)
|
||||
{
|
||||
twi_dcount = 19; //about 100KHz
|
||||
}
|
||||
else if (freq <= 200000)
|
||||
{
|
||||
twi_dcount = 8; //about 200KHz
|
||||
}
|
||||
else if (freq <= 300000)
|
||||
{
|
||||
twi_dcount = 3; //about 300KHz
|
||||
}
|
||||
else if (freq <= 400000)
|
||||
{
|
||||
twi_dcount = 1; //about 400KHz
|
||||
}
|
||||
else
|
||||
{
|
||||
twi_dcount = 1; //about 400KHz
|
||||
}
|
||||
#else
|
||||
if(freq <= 50000) twi_dcount = 64;//about 50KHz
|
||||
else if(freq <= 100000) twi_dcount = 32;//about 100KHz
|
||||
else if(freq <= 200000) twi_dcount = 14;//about 200KHz
|
||||
else if(freq <= 300000) twi_dcount = 8;//about 300KHz
|
||||
else if(freq <= 400000) twi_dcount = 5;//about 400KHz
|
||||
else if(freq <= 500000) twi_dcount = 3;//about 500KHz
|
||||
else if(freq <= 600000) twi_dcount = 2;//about 600KHz
|
||||
else twi_dcount = 1;//about 700KHz
|
||||
if (freq <= 50000)
|
||||
{
|
||||
twi_dcount = 64; //about 50KHz
|
||||
}
|
||||
else if (freq <= 100000)
|
||||
{
|
||||
twi_dcount = 32; //about 100KHz
|
||||
}
|
||||
else if (freq <= 200000)
|
||||
{
|
||||
twi_dcount = 14; //about 200KHz
|
||||
}
|
||||
else if (freq <= 300000)
|
||||
{
|
||||
twi_dcount = 8; //about 300KHz
|
||||
}
|
||||
else if (freq <= 400000)
|
||||
{
|
||||
twi_dcount = 5; //about 400KHz
|
||||
}
|
||||
else if (freq <= 500000)
|
||||
{
|
||||
twi_dcount = 3; //about 500KHz
|
||||
}
|
||||
else if (freq <= 600000)
|
||||
{
|
||||
twi_dcount = 2; //about 600KHz
|
||||
}
|
||||
else
|
||||
{
|
||||
twi_dcount = 1; //about 700KHz
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void twi_setClockStretchLimit(uint32_t limit){
|
||||
void twi_setClockStretchLimit(uint32_t limit)
|
||||
{
|
||||
twi_clockStretchLimit = limit * TWI_CLOCK_STRETCH_MULTIPLIER;
|
||||
}
|
||||
}
|
||||
|
||||
void twi_init(unsigned char sda, unsigned char scl)
|
||||
{
|
||||
void twi_init(unsigned char sda, unsigned char scl)
|
||||
{
|
||||
// set timer function
|
||||
ets_timer_setfn(&timer, onTimer, NULL);
|
||||
|
||||
@ -165,39 +209,44 @@ void twi_init(unsigned char sda, unsigned char scl)
|
||||
attachInterrupt(scl, onSclChange, CHANGE);
|
||||
attachInterrupt(sda, onSdaChange, CHANGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void twi_setAddress(uint8_t address)
|
||||
{
|
||||
void twi_setAddress(uint8_t address)
|
||||
{
|
||||
// set twi slave address (skip over R/W bit)
|
||||
twi_addr = address << 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void ICACHE_RAM_ATTR twi_delay(unsigned char v){
|
||||
static void ICACHE_RAM_ATTR twi_delay(unsigned char v)
|
||||
{
|
||||
unsigned int i;
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
|
||||
unsigned int reg;
|
||||
for (i = 0; i < v; i++) {
|
||||
for (i = 0; i < v; i++)
|
||||
{
|
||||
reg = GPI;
|
||||
}
|
||||
(void)reg;
|
||||
#pragma GCC diagnostic pop
|
||||
}
|
||||
}
|
||||
|
||||
static bool twi_write_start(void) {
|
||||
static bool twi_write_start(void)
|
||||
{
|
||||
SCL_HIGH();
|
||||
SDA_HIGH();
|
||||
if (SDA_READ() == 0) {
|
||||
if (SDA_READ() == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
twi_delay(twi_dcount);
|
||||
SDA_LOW();
|
||||
twi_delay(twi_dcount);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool twi_write_stop(void){
|
||||
static bool twi_write_stop(void)
|
||||
{
|
||||
uint32_t i = 0;
|
||||
SCL_LOW();
|
||||
SDA_LOW();
|
||||
@ -208,164 +257,225 @@ static bool twi_write_stop(void){
|
||||
SDA_HIGH();
|
||||
twi_delay(twi_dcount);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool twi_write_bit(bool bit) {
|
||||
static bool twi_write_bit(bool bit)
|
||||
{
|
||||
uint32_t i = 0;
|
||||
SCL_LOW();
|
||||
if (bit) SDA_HIGH();
|
||||
else SDA_LOW();
|
||||
twi_delay(twi_dcount+1);
|
||||
if (bit)
|
||||
{
|
||||
SDA_HIGH();
|
||||
}
|
||||
else
|
||||
{
|
||||
SDA_LOW();
|
||||
}
|
||||
twi_delay(twi_dcount + 1);
|
||||
SCL_HIGH();
|
||||
while (SCL_READ() == 0 && (i++) < twi_clockStretchLimit);// Clock stretching
|
||||
twi_delay(twi_dcount);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool twi_read_bit(void) {
|
||||
static bool twi_read_bit(void)
|
||||
{
|
||||
uint32_t i = 0;
|
||||
SCL_LOW();
|
||||
SDA_HIGH();
|
||||
twi_delay(twi_dcount+2);
|
||||
twi_delay(twi_dcount + 2);
|
||||
SCL_HIGH();
|
||||
while (SCL_READ() == 0 && (i++) < twi_clockStretchLimit);// Clock stretching
|
||||
bool bit = SDA_READ();
|
||||
twi_delay(twi_dcount);
|
||||
return bit;
|
||||
}
|
||||
}
|
||||
|
||||
static bool twi_write_byte(unsigned char byte) {
|
||||
static bool twi_write_byte(unsigned char byte)
|
||||
{
|
||||
unsigned char bit;
|
||||
for (bit = 0; bit < 8; bit++) {
|
||||
for (bit = 0; bit < 8; bit++)
|
||||
{
|
||||
twi_write_bit(byte & 0x80);
|
||||
byte <<= 1;
|
||||
}
|
||||
return !twi_read_bit();//NACK/ACK
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned char twi_read_byte(bool nack) {
|
||||
static unsigned char twi_read_byte(bool nack)
|
||||
{
|
||||
unsigned char byte = 0;
|
||||
unsigned char bit;
|
||||
for (bit = 0; bit < 8; bit++) byte = (byte << 1) | twi_read_bit();
|
||||
for (bit = 0; bit < 8; bit++)
|
||||
{
|
||||
byte = (byte << 1) | twi_read_bit();
|
||||
}
|
||||
twi_write_bit(nack);
|
||||
return byte;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char twi_writeTo(unsigned char address, unsigned char * buf, unsigned int len, unsigned char sendStop){
|
||||
unsigned char twi_writeTo(unsigned char address, unsigned char * buf, unsigned int len, unsigned char sendStop)
|
||||
{
|
||||
unsigned int i;
|
||||
if(!twi_write_start()) return 4;//line busy
|
||||
if(!twi_write_byte(((address << 1) | 0) & 0xFF)) {
|
||||
if (sendStop) twi_write_stop();
|
||||
if (!twi_write_start())
|
||||
{
|
||||
return 4; //line busy
|
||||
}
|
||||
if (!twi_write_byte(((address << 1) | 0) & 0xFF))
|
||||
{
|
||||
if (sendStop)
|
||||
{
|
||||
twi_write_stop();
|
||||
}
|
||||
return 2; //received NACK on transmit of address
|
||||
}
|
||||
for(i=0; i<len; i++) {
|
||||
if(!twi_write_byte(buf[i])) {
|
||||
if (sendStop) twi_write_stop();
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
if (!twi_write_byte(buf[i]))
|
||||
{
|
||||
if (sendStop)
|
||||
{
|
||||
twi_write_stop();
|
||||
}
|
||||
return 3;//received NACK on transmit of data
|
||||
}
|
||||
}
|
||||
if(sendStop) twi_write_stop();
|
||||
if (sendStop)
|
||||
{
|
||||
twi_write_stop();
|
||||
}
|
||||
i = 0;
|
||||
while(SDA_READ() == 0 && (i++) < 10){
|
||||
while (SDA_READ() == 0 && (i++) < 10)
|
||||
{
|
||||
SCL_LOW();
|
||||
twi_delay(twi_dcount);
|
||||
SCL_HIGH();
|
||||
unsigned int t=0; while(SCL_READ()==0 && (t++)<twi_clockStretchLimit); // twi_clockStretchLimit
|
||||
unsigned int t = 0; while (SCL_READ() == 0 && (t++) < twi_clockStretchLimit); // twi_clockStretchLimit
|
||||
twi_delay(twi_dcount);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char twi_readFrom(unsigned char address, unsigned char* buf, unsigned int len, unsigned char sendStop){
|
||||
unsigned char twi_readFrom(unsigned char address, unsigned char* buf, unsigned int len, unsigned char sendStop)
|
||||
{
|
||||
unsigned int i;
|
||||
if(!twi_write_start()) return 4;//line busy
|
||||
if(!twi_write_byte(((address << 1) | 1) & 0xFF)) {
|
||||
if (sendStop) twi_write_stop();
|
||||
if (!twi_write_start())
|
||||
{
|
||||
return 4; //line busy
|
||||
}
|
||||
if (!twi_write_byte(((address << 1) | 1) & 0xFF))
|
||||
{
|
||||
if (sendStop)
|
||||
{
|
||||
twi_write_stop();
|
||||
}
|
||||
return 2;//received NACK on transmit of address
|
||||
}
|
||||
for(i=0; i<(len-1); i++) buf[i] = twi_read_byte(false);
|
||||
buf[len-1] = twi_read_byte(true);
|
||||
if(sendStop) twi_write_stop();
|
||||
for (i = 0; i < (len - 1); i++)
|
||||
{
|
||||
buf[i] = twi_read_byte(false);
|
||||
}
|
||||
buf[len - 1] = twi_read_byte(true);
|
||||
if (sendStop)
|
||||
{
|
||||
twi_write_stop();
|
||||
}
|
||||
i = 0;
|
||||
while(SDA_READ() == 0 && (i++) < 10){
|
||||
while (SDA_READ() == 0 && (i++) < 10)
|
||||
{
|
||||
SCL_LOW();
|
||||
twi_delay(twi_dcount);
|
||||
SCL_HIGH();
|
||||
unsigned int t=0; while(SCL_READ()==0 && (t++)<twi_clockStretchLimit); // twi_clockStretchLimit
|
||||
unsigned int t = 0; while (SCL_READ() == 0 && (t++) < twi_clockStretchLimit); // twi_clockStretchLimit
|
||||
twi_delay(twi_dcount);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t twi_status() {
|
||||
uint8_t twi_status()
|
||||
{
|
||||
if (SCL_READ() == 0)
|
||||
{
|
||||
return I2C_SCL_HELD_LOW; // SCL held low by another device, no procedure available to recover
|
||||
}
|
||||
|
||||
int clockCount = 20;
|
||||
while (SDA_READ() == 0 && clockCount-- > 0) { // if SDA low, read the bits slaves have to sent to a max
|
||||
while (SDA_READ() == 0 && clockCount-- > 0) // if SDA low, read the bits slaves have to sent to a max
|
||||
{
|
||||
twi_read_bit();
|
||||
if (SCL_READ() == 0) {
|
||||
if (SCL_READ() == 0)
|
||||
{
|
||||
return I2C_SCL_HELD_LOW_AFTER_READ; // I2C bus error. SCL held low beyond slave clock stretch time
|
||||
}
|
||||
}
|
||||
if (SDA_READ() == 0)
|
||||
{
|
||||
return I2C_SDA_HELD_LOW; // I2C bus error. SDA line held low by slave/another_master after n bits.
|
||||
}
|
||||
|
||||
if (!twi_write_start())
|
||||
{
|
||||
return I2C_SDA_HELD_LOW_AFTER_INIT; // line busy. SDA again held low by another device. 2nd master?
|
||||
}
|
||||
|
||||
return I2C_OK;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t twi_transmit(const uint8_t* data, uint8_t length)
|
||||
{
|
||||
uint8_t twi_transmit(const uint8_t* data, uint8_t length)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
// ensure data will fit into buffer
|
||||
if (length > TWI_BUFFER_LENGTH) {
|
||||
if (length > TWI_BUFFER_LENGTH)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
// ensure we are currently a slave transmitter
|
||||
if (twi_state != TWI_STX) {
|
||||
if (twi_state != TWI_STX)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
// set length and copy data into tx buffer
|
||||
twi_txBufferLength = length;
|
||||
for (i = 0; i < length; ++i) {
|
||||
for (i = 0; i < length; ++i)
|
||||
{
|
||||
twi_txBuffer[i] = data[i];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void twi_attachSlaveRxEvent( void (*function)(uint8_t*, size_t) )
|
||||
{
|
||||
void twi_attachSlaveRxEvent(void (*function)(uint8_t*, size_t))
|
||||
{
|
||||
twi_onSlaveReceive = function;
|
||||
}
|
||||
}
|
||||
|
||||
void twi_attachSlaveTxEvent( void (*function)(void) )
|
||||
{
|
||||
void twi_attachSlaveTxEvent(void (*function)(void))
|
||||
{
|
||||
twi_onSlaveTransmit = function;
|
||||
}
|
||||
}
|
||||
|
||||
void ICACHE_RAM_ATTR twi_reply(uint8_t ack)
|
||||
{
|
||||
void ICACHE_RAM_ATTR twi_reply(uint8_t ack)
|
||||
{
|
||||
// transmit master read ready signal, with or without ack
|
||||
if (ack) {
|
||||
if (ack)
|
||||
{
|
||||
//TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA);
|
||||
SCL_HIGH(); // _BV(TWINT)
|
||||
twi_ack = 1; // _BV(TWEA)
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
//TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT);
|
||||
SCL_HIGH(); // _BV(TWINT)
|
||||
twi_ack = 0; // ~_BV(TWEA)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ICACHE_RAM_ATTR twi_stop(void)
|
||||
{
|
||||
void ICACHE_RAM_ATTR twi_stop(void)
|
||||
{
|
||||
// send stop condition
|
||||
//TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTO);
|
||||
SCL_HIGH(); // _BV(TWINT)
|
||||
@ -374,10 +484,10 @@ void ICACHE_RAM_ATTR twi_stop(void)
|
||||
SDA_HIGH(); // _BV(TWSTO)
|
||||
// update twi state
|
||||
twi_state = TWI_READY;
|
||||
}
|
||||
}
|
||||
|
||||
void ICACHE_RAM_ATTR twi_releaseBus(void)
|
||||
{
|
||||
void ICACHE_RAM_ATTR twi_releaseBus(void)
|
||||
{
|
||||
// release bus
|
||||
//TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT);
|
||||
SCL_HIGH(); // _BV(TWINT)
|
||||
@ -386,12 +496,13 @@ void ICACHE_RAM_ATTR twi_releaseBus(void)
|
||||
|
||||
// update twi state
|
||||
twi_state = TWI_READY;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ICACHE_RAM_ATTR twi_onTwipEvent(uint8_t status)
|
||||
{
|
||||
switch(status) {
|
||||
void ICACHE_RAM_ATTR twi_onTwipEvent(uint8_t status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
// Slave Receiver
|
||||
case TW_SR_SLA_ACK: // addressed, returned ack
|
||||
case TW_SR_GCALL_ACK: // addressed generally, returned ack
|
||||
@ -406,18 +517,22 @@ void ICACHE_RAM_ATTR twi_onTwipEvent(uint8_t status)
|
||||
case TW_SR_DATA_ACK: // data received, returned ack
|
||||
case TW_SR_GCALL_DATA_ACK: // data received generally, returned ack
|
||||
// if there is still room in the rx buffer
|
||||
if(twi_rxBufferIndex < TWI_BUFFER_LENGTH){
|
||||
if (twi_rxBufferIndex < TWI_BUFFER_LENGTH)
|
||||
{
|
||||
// put byte in buffer and ack
|
||||
twi_rxBuffer[twi_rxBufferIndex++] = TWDR;
|
||||
twi_reply(1);
|
||||
}else{
|
||||
}
|
||||
else
|
||||
{
|
||||
// otherwise nack
|
||||
twi_reply(0);
|
||||
}
|
||||
break;
|
||||
case TW_SR_STOP: // stop or repeated start condition received
|
||||
// put a null char after data if there's room
|
||||
if(twi_rxBufferIndex < TWI_BUFFER_LENGTH){
|
||||
if (twi_rxBufferIndex < TWI_BUFFER_LENGTH)
|
||||
{
|
||||
twi_rxBuffer[twi_rxBufferIndex] = '\0';
|
||||
}
|
||||
// callback to user-defined callback over event task to allow for non-RAM-residing code
|
||||
@ -459,9 +574,12 @@ void ICACHE_RAM_ATTR twi_onTwipEvent(uint8_t status)
|
||||
twi_data <<= 1;
|
||||
|
||||
// if there is more to send, ack, otherwise nack
|
||||
if(twi_txBufferIndex < twi_txBufferLength){
|
||||
if (twi_txBufferIndex < twi_txBufferLength)
|
||||
{
|
||||
twi_reply(1);
|
||||
}else{
|
||||
}
|
||||
else
|
||||
{
|
||||
twi_reply(0);
|
||||
}
|
||||
break;
|
||||
@ -479,22 +597,23 @@ void ICACHE_RAM_ATTR twi_onTwipEvent(uint8_t status)
|
||||
twi_stop();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ICACHE_RAM_ATTR onTimer(void *unused)
|
||||
{
|
||||
void ICACHE_RAM_ATTR onTimer(void *unused)
|
||||
{
|
||||
(void)unused;
|
||||
twi_releaseBus();
|
||||
twip_status = TW_BUS_ERROR;
|
||||
twi_onTwipEvent(twip_status);
|
||||
twip_mode = TWIPM_WAIT;
|
||||
twip_state = TWIP_BUS_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
static void eventTask(ETSEvent *e)
|
||||
{
|
||||
static void eventTask(ETSEvent *e)
|
||||
{
|
||||
|
||||
if (e == NULL) {
|
||||
if (e == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@ -504,7 +623,8 @@ static void eventTask(ETSEvent *e)
|
||||
twi_onSlaveTransmit();
|
||||
|
||||
// if they didn't change buffer & length, initialize it
|
||||
if (twi_txBufferLength == 0) {
|
||||
if (twi_txBufferLength == 0)
|
||||
{
|
||||
twi_txBufferLength = 1;
|
||||
twi_txBuffer[0] = 0x00;
|
||||
}
|
||||
@ -520,10 +640,10 @@ static void eventTask(ETSEvent *e)
|
||||
twi_onSlaveReceive(twi_rxBuffer, e->par);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ICACHE_RAM_ATTR onSclChange(void)
|
||||
{
|
||||
void ICACHE_RAM_ATTR onSclChange(void)
|
||||
{
|
||||
static uint8_t sda;
|
||||
static uint8_t scl;
|
||||
|
||||
@ -544,35 +664,53 @@ void ICACHE_RAM_ATTR onSclChange(void)
|
||||
case TWIP_REP_START:
|
||||
case TWIP_SLA_W:
|
||||
case TWIP_READ:
|
||||
if (!scl) {
|
||||
if (!scl)
|
||||
{
|
||||
// ignore
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
bitCount--;
|
||||
twi_data <<= 1;
|
||||
twi_data |= sda;
|
||||
|
||||
if (bitCount != 0) {
|
||||
if (bitCount != 0)
|
||||
{
|
||||
// continue
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
twip_state = TWIP_SEND_ACK;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case TWIP_SEND_ACK:
|
||||
if (scl) {
|
||||
if (scl)
|
||||
{
|
||||
// ignore
|
||||
} else {
|
||||
if (twip_mode == TWIPM_IDLE) {
|
||||
if ((twi_data & 0xFE) != twi_addr) {
|
||||
}
|
||||
else
|
||||
{
|
||||
if (twip_mode == TWIPM_IDLE)
|
||||
{
|
||||
if ((twi_data & 0xFE) != twi_addr)
|
||||
{
|
||||
// ignore
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
SDA_LOW();
|
||||
}
|
||||
} else {
|
||||
if (!twi_ack) {
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!twi_ack)
|
||||
{
|
||||
// ignore
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
SDA_LOW();
|
||||
}
|
||||
}
|
||||
@ -581,37 +719,52 @@ void ICACHE_RAM_ATTR onSclChange(void)
|
||||
break;
|
||||
|
||||
case TWIP_WAIT_ACK:
|
||||
if (scl) {
|
||||
if (scl)
|
||||
{
|
||||
// ignore
|
||||
} else {
|
||||
if (twip_mode == TWIPM_IDLE) {
|
||||
if ((twi_data & 0xFE) != twi_addr) {
|
||||
}
|
||||
else
|
||||
{
|
||||
if (twip_mode == TWIPM_IDLE)
|
||||
{
|
||||
if ((twi_data & 0xFE) != twi_addr)
|
||||
{
|
||||
SDA_HIGH();
|
||||
twip_state = TWIP_WAIT_STOP;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
SCL_LOW(); // clock stretching
|
||||
SDA_HIGH();
|
||||
twip_mode = TWIPM_ADDRESSED;
|
||||
if (!(twi_data & 0x01)) {
|
||||
if (!(twi_data & 0x01))
|
||||
{
|
||||
twip_status = TW_SR_SLA_ACK;
|
||||
twi_onTwipEvent(twip_status);
|
||||
bitCount = 8;
|
||||
twip_state = TWIP_SLA_W;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
twip_status = TW_ST_SLA_ACK;
|
||||
twi_onTwipEvent(twip_status);
|
||||
twip_state = TWIP_SLA_R;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
SCL_LOW(); // clock stretching
|
||||
SDA_HIGH();
|
||||
if (!twi_ack) {
|
||||
if (!twi_ack)
|
||||
{
|
||||
twip_status = TW_SR_DATA_NACK;
|
||||
twi_onTwipEvent(twip_status);
|
||||
twip_mode = TWIPM_WAIT;
|
||||
twip_state = TWIP_WAIT_STOP;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
twip_status = TW_SR_DATA_ACK;
|
||||
twi_onTwipEvent(twip_status);
|
||||
bitCount = 8;
|
||||
@ -623,49 +776,67 @@ void ICACHE_RAM_ATTR onSclChange(void)
|
||||
|
||||
case TWIP_SLA_R:
|
||||
case TWIP_WRITE:
|
||||
if (scl) {
|
||||
if (scl)
|
||||
{
|
||||
// ignore
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
bitCount--;
|
||||
(twi_data & 0x80) ? SDA_HIGH() : SDA_LOW();
|
||||
twi_data <<= 1;
|
||||
|
||||
if (bitCount != 0) {
|
||||
if (bitCount != 0)
|
||||
{
|
||||
// continue
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
twip_state = TWIP_REC_ACK;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case TWIP_REC_ACK:
|
||||
if (scl) {
|
||||
if (scl)
|
||||
{
|
||||
// ignore
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
SDA_HIGH();
|
||||
twip_state = TWIP_READ_ACK;
|
||||
}
|
||||
break;
|
||||
|
||||
case TWIP_READ_ACK:
|
||||
if (!scl) {
|
||||
if (!scl)
|
||||
{
|
||||
// ignore
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
twi_ack_rec = !sda;
|
||||
twip_state = TWIP_RWAIT_ACK;
|
||||
}
|
||||
break;
|
||||
|
||||
case TWIP_RWAIT_ACK:
|
||||
if (scl) {
|
||||
if (scl)
|
||||
{
|
||||
// ignore
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
SCL_LOW(); // clock stretching
|
||||
if (twi_ack && twi_ack_rec) {
|
||||
if (twi_ack && twi_ack_rec)
|
||||
{
|
||||
twip_status = TW_ST_DATA_ACK;
|
||||
twi_onTwipEvent(twip_status);
|
||||
twip_state = TWIP_WRITE;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
// we have no more data to send and/or the master doesn't want anymore
|
||||
twip_status = twi_ack_rec ? TW_ST_LAST_DATA : TW_ST_DATA_NACK;
|
||||
twi_onTwipEvent(twip_status);
|
||||
@ -678,10 +849,10 @@ void ICACHE_RAM_ATTR onSclChange(void)
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ICACHE_RAM_ATTR onSdaChange(void)
|
||||
{
|
||||
void ICACHE_RAM_ATTR onSdaChange(void)
|
||||
{
|
||||
static uint8_t sda;
|
||||
static uint8_t scl;
|
||||
sda = SDA_READ();
|
||||
@ -690,11 +861,16 @@ void ICACHE_RAM_ATTR onSdaChange(void)
|
||||
switch (twip_state)
|
||||
{
|
||||
case TWIP_IDLE:
|
||||
if (!scl) {
|
||||
if (!scl)
|
||||
{
|
||||
// DATA - ignore
|
||||
} else if (sda) {
|
||||
}
|
||||
else if (sda)
|
||||
{
|
||||
// STOP - ignore
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
// START
|
||||
bitCount = 8;
|
||||
twip_state = TWIP_START;
|
||||
@ -711,9 +887,12 @@ void ICACHE_RAM_ATTR onSdaChange(void)
|
||||
case TWIP_READ_ACK:
|
||||
case TWIP_RWAIT_ACK:
|
||||
case TWIP_WRITE:
|
||||
if (!scl) {
|
||||
if (!scl)
|
||||
{
|
||||
// DATA - ignore
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
// START or STOP
|
||||
SDA_HIGH(); // Should not be necessary
|
||||
twip_status = TW_BUS_ERROR;
|
||||
@ -725,21 +904,30 @@ void ICACHE_RAM_ATTR onSdaChange(void)
|
||||
|
||||
case TWIP_WAIT_STOP:
|
||||
case TWIP_BUS_ERR:
|
||||
if (!scl) {
|
||||
if (!scl)
|
||||
{
|
||||
// DATA - ignore
|
||||
} else {
|
||||
if (sda) {
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sda)
|
||||
{
|
||||
// STOP
|
||||
SCL_LOW(); // clock stretching
|
||||
ets_timer_disarm(&timer);
|
||||
twip_state = TWIP_IDLE;
|
||||
twip_mode = TWIPM_IDLE;
|
||||
SCL_HIGH();
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
// START
|
||||
if (twip_state == TWIP_BUS_ERR) {
|
||||
if (twip_state == TWIP_BUS_ERR)
|
||||
{
|
||||
// ignore
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
bitCount = 8;
|
||||
twip_state = TWIP_REP_START;
|
||||
ets_timer_arm_new(&timer, twi_timeout_ms, false, true); // Once, ms
|
||||
@ -750,27 +938,36 @@ void ICACHE_RAM_ATTR onSdaChange(void)
|
||||
|
||||
case TWIP_SLA_W:
|
||||
case TWIP_READ:
|
||||
if (!scl) {
|
||||
if (!scl)
|
||||
{
|
||||
// DATA - ignore
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
// START or STOP
|
||||
if (bitCount != 7) {
|
||||
if (bitCount != 7)
|
||||
{
|
||||
// inside byte transfer - error
|
||||
twip_status = TW_BUS_ERROR;
|
||||
twi_onTwipEvent(twip_status);
|
||||
twip_mode = TWIPM_WAIT;
|
||||
twip_state = TWIP_BUS_ERR;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
// during first bit in byte transfer - ok
|
||||
SCL_LOW(); // clock stretching
|
||||
twip_status = TW_SR_STOP;
|
||||
twi_onTwipEvent(twip_status);
|
||||
if (sda) {
|
||||
if (sda)
|
||||
{
|
||||
// STOP
|
||||
ets_timer_disarm(&timer);
|
||||
twip_state = TWIP_IDLE;
|
||||
twip_mode = TWIPM_IDLE;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
// START
|
||||
bitCount = 8;
|
||||
ets_timer_arm_new(&timer, twi_timeout_ms, false, true); // Once, ms
|
||||
@ -784,6 +981,6 @@ void ICACHE_RAM_ATTR onSdaChange(void)
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -23,156 +23,162 @@
|
||||
|
||||
extern "C" {
|
||||
|
||||
// definitions in esp8266_peri.h style
|
||||
// definitions in esp8266_peri.h style
|
||||
#define GPSD ESP8266_REG(0x368) // GPIO_SIGMA_DELTA register @ 0x600000368
|
||||
#define GPSDT 0 // target, 8 bits
|
||||
#define GPSDP 8 // prescaler, 8 bits
|
||||
#define GPSDE 16 // enable
|
||||
|
||||
void sigmaDeltaSetPrescaler(uint8_t prescaler); // avoids compiler warning
|
||||
void sigmaDeltaSetPrescaler(uint8_t prescaler); // avoids compiler warning
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : sigmaDeltaEnable
|
||||
* Description : enable the internal sigma delta source
|
||||
* Parameters : none
|
||||
* Returns : none
|
||||
*******************************************************************************/
|
||||
void ICACHE_FLASH_ATTR sigmaDeltaEnable()
|
||||
{
|
||||
/******************************************************************************
|
||||
FunctionName : sigmaDeltaEnable
|
||||
Description : enable the internal sigma delta source
|
||||
Parameters : none
|
||||
Returns : none
|
||||
*******************************************************************************/
|
||||
void ICACHE_FLASH_ATTR sigmaDeltaEnable()
|
||||
{
|
||||
GPSD = (0 << GPSDT) | (0 << GPSDP) | (1 << GPSDE); //SIGMA_DELTA_TARGET(0) | SIGMA_DELTA_PRESCALER(0) | SIGMA_DELTA_ENABLE(ENABLED)
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : sigmaDeltaDisable
|
||||
* Description : stop the internal sigma delta source
|
||||
* Parameters : none
|
||||
* Returns : none
|
||||
*******************************************************************************/
|
||||
void ICACHE_FLASH_ATTR sigmaDeltaDisable()
|
||||
{
|
||||
/******************************************************************************
|
||||
FunctionName : sigmaDeltaDisable
|
||||
Description : stop the internal sigma delta source
|
||||
Parameters : none
|
||||
Returns : none
|
||||
*******************************************************************************/
|
||||
void ICACHE_FLASH_ATTR sigmaDeltaDisable()
|
||||
{
|
||||
GPSD = (0 << GPSDT) | (0 << GPSDP) | (0 << GPSDE); //SIGMA_DELTA_TARGET(0) | SIGMA_DELTA_PRESCALER(0) | SIGMA_DELTA_ENABLE(DISABLED)
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : sigmaDeltaAttachPin
|
||||
* Description : connects the sigma delta source to a physical output pin
|
||||
* Parameters : pin (0..15), channel = unused, for compatibility with ESP32
|
||||
* Returns : none
|
||||
*******************************************************************************/
|
||||
void ICACHE_FLASH_ATTR sigmaDeltaAttachPin(uint8_t pin, uint8_t channel)
|
||||
{
|
||||
/******************************************************************************
|
||||
FunctionName : sigmaDeltaAttachPin
|
||||
Description : connects the sigma delta source to a physical output pin
|
||||
Parameters : pin (0..15), channel = unused, for compatibility with ESP32
|
||||
Returns : none
|
||||
*******************************************************************************/
|
||||
void ICACHE_FLASH_ATTR sigmaDeltaAttachPin(uint8_t pin, uint8_t channel)
|
||||
{
|
||||
(void) channel;
|
||||
// make the chosen pin an output pin
|
||||
pinMode (pin, OUTPUT);
|
||||
if (pin < 16) {
|
||||
pinMode(pin, OUTPUT);
|
||||
if (pin < 16)
|
||||
{
|
||||
// set its source to the sigma delta source
|
||||
GPC(pin) |= (1 << GPCS); //SOURCE 0:GPIO_DATA,1:SigmaDelta
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : sigmaDeltaDetachPin
|
||||
* Description : disconnects the sigma delta source from a physical output pin
|
||||
* Parameters : pin (0..16)
|
||||
* Returns : none
|
||||
*******************************************************************************/
|
||||
void ICACHE_FLASH_ATTR sigmaDeltaDetachPin(uint8_t pin)
|
||||
{
|
||||
if (pin < 16) {
|
||||
/******************************************************************************
|
||||
FunctionName : sigmaDeltaDetachPin
|
||||
Description : disconnects the sigma delta source from a physical output pin
|
||||
Parameters : pin (0..16)
|
||||
Returns : none
|
||||
*******************************************************************************/
|
||||
void ICACHE_FLASH_ATTR sigmaDeltaDetachPin(uint8_t pin)
|
||||
{
|
||||
if (pin < 16)
|
||||
{
|
||||
// set its source to the sigma delta source
|
||||
GPC(pin) &= ~(1 << GPCS); //SOURCE 0:GPIO_DATA,1:SigmaDelta
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : sigmaDeltaIsPinAttached
|
||||
* Description : query if pin is attached
|
||||
* Parameters : pin (0..16)
|
||||
* Returns : bool
|
||||
*******************************************************************************/
|
||||
bool ICACHE_FLASH_ATTR sigmaDeltaIsPinAttached(uint8_t pin)
|
||||
{
|
||||
if (pin < 16) {
|
||||
/******************************************************************************
|
||||
FunctionName : sigmaDeltaIsPinAttached
|
||||
Description : query if pin is attached
|
||||
Parameters : pin (0..16)
|
||||
Returns : bool
|
||||
*******************************************************************************/
|
||||
bool ICACHE_FLASH_ATTR sigmaDeltaIsPinAttached(uint8_t pin)
|
||||
{
|
||||
if (pin < 16)
|
||||
{
|
||||
// set its source to the sigma delta source
|
||||
return (GPC(pin) & (1 << GPCS)); //SOURCE 0:GPIO_DATA,1:SigmaDelta
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : sigmaDeltaSetup
|
||||
* Description : start the sigma delta generator with the chosen parameters
|
||||
* Parameters : channel = unused (for compatibility with ESP32),
|
||||
* freq : 1220-312500 (lowest frequency in the output signal's spectrum)
|
||||
* Returns : uint32_t the actual frequency, closest to the input parameter
|
||||
*******************************************************************************/
|
||||
uint32_t ICACHE_FLASH_ATTR sigmaDeltaSetup(uint8_t channel, uint32_t freq)
|
||||
{
|
||||
/******************************************************************************
|
||||
FunctionName : sigmaDeltaSetup
|
||||
Description : start the sigma delta generator with the chosen parameters
|
||||
Parameters : channel = unused (for compatibility with ESP32),
|
||||
freq : 1220-312500 (lowest frequency in the output signal's spectrum)
|
||||
Returns : uint32_t the actual frequency, closest to the input parameter
|
||||
*******************************************************************************/
|
||||
uint32_t ICACHE_FLASH_ATTR sigmaDeltaSetup(uint8_t channel, uint32_t freq)
|
||||
{
|
||||
(void) channel;
|
||||
|
||||
uint32_t prescaler = ((uint32_t)10000000/(freq*32)) - 1;
|
||||
uint32_t prescaler = ((uint32_t)10000000 / (freq * 32)) - 1;
|
||||
|
||||
if(prescaler > 0xFF) {
|
||||
if (prescaler > 0xFF)
|
||||
{
|
||||
prescaler = 0xFF;
|
||||
}
|
||||
sigmaDeltaEnable();
|
||||
sigmaDeltaSetPrescaler ((uint8_t) prescaler);
|
||||
sigmaDeltaSetPrescaler((uint8_t) prescaler);
|
||||
|
||||
return 10000000/((prescaler + 1) * 32);
|
||||
}
|
||||
return 10000000 / ((prescaler + 1) * 32);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : sigmaDeltaWrite
|
||||
* Description : set the duty cycle for the sigma-delta source
|
||||
* Parameters : uint8 duty, 0-255, duty cycle = target/256,
|
||||
* channel = unused, for compatibility with ESP32
|
||||
* Returns : none
|
||||
*******************************************************************************/
|
||||
void ICACHE_FLASH_ATTR sigmaDeltaWrite(uint8_t channel, uint8_t duty)
|
||||
{
|
||||
/******************************************************************************
|
||||
FunctionName : sigmaDeltaWrite
|
||||
Description : set the duty cycle for the sigma-delta source
|
||||
Parameters : uint8 duty, 0-255, duty cycle = target/256,
|
||||
channel = unused, for compatibility with ESP32
|
||||
Returns : none
|
||||
*******************************************************************************/
|
||||
void ICACHE_FLASH_ATTR sigmaDeltaWrite(uint8_t channel, uint8_t duty)
|
||||
{
|
||||
uint32_t reg = GPSD;
|
||||
(void) channel;
|
||||
|
||||
reg = (reg & ~(0xFF << GPSDT)) | ((duty & 0xFF) << GPSDT);
|
||||
GPSD = reg;
|
||||
|
||||
}
|
||||
/******************************************************************************
|
||||
* FunctionName : sigmaDeltaRead
|
||||
* Description : get the duty cycle for the sigma-delta source
|
||||
* Parameters : channel = unused, for compatibility with ESP32
|
||||
* Returns : uint8_t duty cycle value 0..255
|
||||
*******************************************************************************/
|
||||
uint8_t ICACHE_FLASH_ATTR sigmaDeltaRead(uint8_t channel)
|
||||
{
|
||||
}
|
||||
/******************************************************************************
|
||||
FunctionName : sigmaDeltaRead
|
||||
Description : get the duty cycle for the sigma-delta source
|
||||
Parameters : channel = unused, for compatibility with ESP32
|
||||
Returns : uint8_t duty cycle value 0..255
|
||||
*******************************************************************************/
|
||||
uint8_t ICACHE_FLASH_ATTR sigmaDeltaRead(uint8_t channel)
|
||||
{
|
||||
(void) channel;
|
||||
return (uint8_t)((GPSD >> GPSDT) & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : sigmaDeltaSetPrescaler
|
||||
* Description : set the clock divider for the sigma-delta source
|
||||
* Parameters : uint8 prescaler, 0-255, divides the 80MHz base clock by this amount
|
||||
* Returns : none
|
||||
*******************************************************************************/
|
||||
void ICACHE_FLASH_ATTR sigmaDeltaSetPrescaler(uint8_t prescaler)
|
||||
{
|
||||
/******************************************************************************
|
||||
FunctionName : sigmaDeltaSetPrescaler
|
||||
Description : set the clock divider for the sigma-delta source
|
||||
Parameters : uint8 prescaler, 0-255, divides the 80MHz base clock by this amount
|
||||
Returns : none
|
||||
*******************************************************************************/
|
||||
void ICACHE_FLASH_ATTR sigmaDeltaSetPrescaler(uint8_t prescaler)
|
||||
{
|
||||
uint32_t reg = GPSD;
|
||||
|
||||
reg = (reg & ~(0xFF << GPSDP)) | ((prescaler & 0xFF) << GPSDP);
|
||||
GPSD = reg;
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : sigmaDeltaGetPrescaler
|
||||
* Description : get the prescaler value from the GPIO_SIGMA_DELTA register
|
||||
* Parameters : none
|
||||
* Returns : uint8 prescaler, CLK_DIV , 0-255
|
||||
*******************************************************************************/
|
||||
uint8_t ICACHE_FLASH_ATTR sigmaDeltaGetPrescaler(void)
|
||||
{
|
||||
/******************************************************************************
|
||||
FunctionName : sigmaDeltaGetPrescaler
|
||||
Description : get the prescaler value from the GPIO_SIGMA_DELTA register
|
||||
Parameters : none
|
||||
Returns : uint8 prescaler, CLK_DIV , 0-255
|
||||
*******************************************************************************/
|
||||
uint8_t ICACHE_FLASH_ATTR sigmaDeltaGetPrescaler(void)
|
||||
{
|
||||
return (uint8_t)((GPSD >> GPSDP) & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -26,82 +26,101 @@
|
||||
|
||||
extern "C" {
|
||||
|
||||
// ------------------------------------------------------------------ -
|
||||
// timer 1
|
||||
// ------------------------------------------------------------------ -
|
||||
// timer 1
|
||||
|
||||
static volatile timercallback timer1_user_cb = NULL;
|
||||
static volatile timercallback timer1_user_cb = NULL;
|
||||
|
||||
void ICACHE_RAM_ATTR timer1_isr_handler(void *para){
|
||||
void ICACHE_RAM_ATTR timer1_isr_handler(void *para)
|
||||
{
|
||||
(void) para;
|
||||
if ((T1C & ((1 << TCAR) | (1 << TCIT))) == 0) TEIE &= ~TEIE1;//edge int disable
|
||||
if ((T1C & ((1 << TCAR) | (1 << TCIT))) == 0)
|
||||
{
|
||||
TEIE &= ~TEIE1; //edge int disable
|
||||
}
|
||||
T1I = 0;
|
||||
if (timer1_user_cb) {
|
||||
if (timer1_user_cb)
|
||||
{
|
||||
// to make ISR compatible to Arduino AVR model where interrupts are disabled
|
||||
// we disable them before we call the client ISR
|
||||
uint32_t savedPS = xt_rsil(15); // stop other interrupts
|
||||
timer1_user_cb();
|
||||
xt_wsr_ps(savedPS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ICACHE_RAM_ATTR timer1_isr_init(){
|
||||
void ICACHE_RAM_ATTR timer1_isr_init()
|
||||
{
|
||||
ETS_FRC_TIMER1_INTR_ATTACH(timer1_isr_handler, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void timer1_attachInterrupt(timercallback userFunc) {
|
||||
void timer1_attachInterrupt(timercallback userFunc)
|
||||
{
|
||||
timer1_user_cb = userFunc;
|
||||
ETS_FRC1_INTR_ENABLE();
|
||||
}
|
||||
}
|
||||
|
||||
void ICACHE_RAM_ATTR timer1_detachInterrupt() {
|
||||
void ICACHE_RAM_ATTR timer1_detachInterrupt()
|
||||
{
|
||||
timer1_user_cb = 0;
|
||||
TEIE &= ~TEIE1;//edge int disable
|
||||
ETS_FRC1_INTR_DISABLE();
|
||||
}
|
||||
}
|
||||
|
||||
void timer1_enable(uint8_t divider, uint8_t int_type, uint8_t reload){
|
||||
void timer1_enable(uint8_t divider, uint8_t int_type, uint8_t reload)
|
||||
{
|
||||
T1C = (1 << TCTE) | ((divider & 3) << TCPD) | ((int_type & 1) << TCIT) | ((reload & 1) << TCAR);
|
||||
T1I = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ICACHE_RAM_ATTR timer1_write(uint32_t ticks){
|
||||
T1L = ((ticks)& 0x7FFFFF);
|
||||
if ((T1C & (1 << TCIT)) == 0) TEIE |= TEIE1;//edge int enable
|
||||
}
|
||||
void ICACHE_RAM_ATTR timer1_write(uint32_t ticks)
|
||||
{
|
||||
T1L = ((ticks) & 0x7FFFFF);
|
||||
if ((T1C & (1 << TCIT)) == 0)
|
||||
{
|
||||
TEIE |= TEIE1; //edge int enable
|
||||
}
|
||||
}
|
||||
|
||||
void ICACHE_RAM_ATTR timer1_disable(){
|
||||
void ICACHE_RAM_ATTR timer1_disable()
|
||||
{
|
||||
T1C = 0;
|
||||
T1I = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// timer 0
|
||||
//-------------------------------------------------------------------
|
||||
// timer 0
|
||||
|
||||
static volatile timercallback timer0_user_cb = NULL;
|
||||
static volatile timercallback timer0_user_cb = NULL;
|
||||
|
||||
void ICACHE_RAM_ATTR timer0_isr_handler(void* para){
|
||||
void ICACHE_RAM_ATTR timer0_isr_handler(void* para)
|
||||
{
|
||||
(void) para;
|
||||
if (timer0_user_cb) {
|
||||
if (timer0_user_cb)
|
||||
{
|
||||
// to make ISR compatible to Arduino AVR model where interrupts are disabled
|
||||
// we disable them before we call the client ISR
|
||||
uint32_t savedPS = xt_rsil(15); // stop other interrupts
|
||||
timer0_user_cb();
|
||||
xt_wsr_ps(savedPS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void timer0_isr_init(){
|
||||
void timer0_isr_init()
|
||||
{
|
||||
ETS_CCOMPARE0_INTR_ATTACH(timer0_isr_handler, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void timer0_attachInterrupt(timercallback userFunc) {
|
||||
void timer0_attachInterrupt(timercallback userFunc)
|
||||
{
|
||||
timer0_user_cb = userFunc;
|
||||
ETS_CCOMPARE0_ENABLE();
|
||||
}
|
||||
}
|
||||
|
||||
void ICACHE_RAM_ATTR timer0_detachInterrupt() {
|
||||
void ICACHE_RAM_ATTR timer0_detachInterrupt()
|
||||
{
|
||||
timer0_user_cb = NULL;
|
||||
ETS_CCOMPARE0_DISABLE();
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -33,60 +33,61 @@
|
||||
extern "C++"
|
||||
{
|
||||
|
||||
// Following constexpr functions are compiled and executed
|
||||
// *after* pre-processing and *during* compilation
|
||||
//
|
||||
// Their result is treated like a numeric constant in final binary code.
|
||||
// git tags must be in the form:
|
||||
// - <major>.<minor>.<revision> (2.4.2) (2.5.0)
|
||||
// - <major>.<minor>.<revision>-rc<rc> (2.5.0-rc1) (2.5.0-rc2)
|
||||
//
|
||||
// "git describe" = ARDUINO_ESP8266_GIT_DESC will thus be in the form:
|
||||
// - <tag> (2.4.2) (2.5.0)
|
||||
// - <tag>-<numberOfCommits>-g<git-hash> (2.4.2-91-gcb05b86d) (2.5.0-rc3-1-gcb05b86d)
|
||||
//
|
||||
// Examples:
|
||||
// case 2.4.2 (fresh version/tag)
|
||||
// esp8266CoreVersionSubRevision() is 0 Numeric is: 20402000
|
||||
// case 2.4.2-91-gcb05b86d:
|
||||
// esp8266CoreVersionSubRevision() is -91 Numeric is: 20402091
|
||||
// case 2.5.0-rc3-1-gcb05b86d:
|
||||
// esp8266CoreVersionSubRevision() is 3 Numeric is: 20499903
|
||||
// case 2.5.0:
|
||||
// esp8266CoreVersionSubRevision() is 0 Numeric is: 20500000
|
||||
//
|
||||
// Using esp8266::coreVersionNumeric() in a portable way:
|
||||
//
|
||||
// #if HAS_ESP8266_VERSION_NUMERIC
|
||||
// if (esp8266::coreVersionNumeric() >= 20500042)
|
||||
// {
|
||||
// // modern api can be used
|
||||
// }
|
||||
// else
|
||||
// #endif
|
||||
// {
|
||||
// // code using older api
|
||||
// // (will not be compiled in when newer api is usable)
|
||||
// }
|
||||
// Following constexpr functions are compiled and executed
|
||||
// *after* pre-processing and *during* compilation
|
||||
//
|
||||
// Their result is treated like a numeric constant in final binary code.
|
||||
// git tags must be in the form:
|
||||
// - <major>.<minor>.<revision> (2.4.2) (2.5.0)
|
||||
// - <major>.<minor>.<revision>-rc<rc> (2.5.0-rc1) (2.5.0-rc2)
|
||||
//
|
||||
// "git describe" = ARDUINO_ESP8266_GIT_DESC will thus be in the form:
|
||||
// - <tag> (2.4.2) (2.5.0)
|
||||
// - <tag>-<numberOfCommits>-g<git-hash> (2.4.2-91-gcb05b86d) (2.5.0-rc3-1-gcb05b86d)
|
||||
//
|
||||
// Examples:
|
||||
// case 2.4.2 (fresh version/tag)
|
||||
// esp8266CoreVersionSubRevision() is 0 Numeric is: 20402000
|
||||
// case 2.4.2-91-gcb05b86d:
|
||||
// esp8266CoreVersionSubRevision() is -91 Numeric is: 20402091
|
||||
// case 2.5.0-rc3-1-gcb05b86d:
|
||||
// esp8266CoreVersionSubRevision() is 3 Numeric is: 20499903
|
||||
// case 2.5.0:
|
||||
// esp8266CoreVersionSubRevision() is 0 Numeric is: 20500000
|
||||
//
|
||||
// Using esp8266::coreVersionNumeric() in a portable way:
|
||||
//
|
||||
// #if HAS_ESP8266_VERSION_NUMERIC
|
||||
// if (esp8266::coreVersionNumeric() >= 20500042)
|
||||
// {
|
||||
// // modern api can be used
|
||||
// }
|
||||
// else
|
||||
// #endif
|
||||
// {
|
||||
// // code using older api
|
||||
// // (will not be compiled in when newer api is usable)
|
||||
// }
|
||||
|
||||
namespace conststr {
|
||||
namespace conststr {
|
||||
|
||||
constexpr
|
||||
bool isDecimal (const char c)
|
||||
{
|
||||
constexpr
|
||||
bool isDecimal(const char c)
|
||||
{
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
}
|
||||
|
||||
template<unsigned N> constexpr
|
||||
bool isMinus (const char (&arr) [N], unsigned i)
|
||||
{
|
||||
return arr[i] == '-' && isDecimal(arr[i+1]);
|
||||
}
|
||||
template<unsigned N> constexpr
|
||||
bool isMinus(const char (&arr) [N], unsigned i)
|
||||
{
|
||||
return arr[i] == '-' && isDecimal(arr[i + 1]);
|
||||
}
|
||||
|
||||
template<unsigned N> constexpr
|
||||
int atoi (const char (&arr) [N], unsigned i)
|
||||
{
|
||||
return ({ // <= c++11 requires a "return statement"
|
||||
template<unsigned N> constexpr
|
||||
int atoi(const char (&arr) [N], unsigned i)
|
||||
{
|
||||
return ( // <= c++11 requires a "return statement"
|
||||
{
|
||||
int ret = 0;
|
||||
int sign = 1;
|
||||
if (arr[i] == '-')
|
||||
@ -95,75 +96,78 @@ int atoi (const char (&arr) [N], unsigned i)
|
||||
i++;
|
||||
}
|
||||
while (isDecimal(arr[i]))
|
||||
ret = 10*ret + arr[i++] - '0';
|
||||
ret = 10 * ret + arr[i++] - '0';
|
||||
ret * sign;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
template<unsigned N> constexpr
|
||||
int parseNthInteger (const char (&arr) [N], unsigned f)
|
||||
{
|
||||
return ({ // <= c++11 requires a "return statement"
|
||||
template<unsigned N> constexpr
|
||||
int parseNthInteger(const char (&arr) [N], unsigned f)
|
||||
{
|
||||
return ( // <= c++11 requires a "return statement"
|
||||
{
|
||||
unsigned i = 0;
|
||||
while (f && arr[i])
|
||||
{
|
||||
if (isMinus(arr, i))
|
||||
{
|
||||
i++;
|
||||
}
|
||||
for (; isDecimal(arr[i]); i++);
|
||||
f--;
|
||||
for (; arr[i] && !isMinus(arr, i) && !isDecimal(arr[i]); i++);
|
||||
}
|
||||
atoi(arr, i);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}; // namespace conststr
|
||||
}; // namespace conststr
|
||||
|
||||
namespace esp8266 {
|
||||
namespace esp8266 {
|
||||
|
||||
/*
|
||||
* version major
|
||||
/*
|
||||
version major
|
||||
*/
|
||||
constexpr
|
||||
int coreVersionMajor ()
|
||||
{
|
||||
constexpr
|
||||
int coreVersionMajor()
|
||||
{
|
||||
return conststr::parseNthInteger(__STR(ARDUINO_ESP8266_GIT_DESC), 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* version minor
|
||||
/*
|
||||
version minor
|
||||
*/
|
||||
constexpr
|
||||
int coreVersionMinor ()
|
||||
{
|
||||
constexpr
|
||||
int coreVersionMinor()
|
||||
{
|
||||
return conststr::parseNthInteger(__STR(ARDUINO_ESP8266_GIT_DESC), 1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* version revision
|
||||
/*
|
||||
version revision
|
||||
*/
|
||||
constexpr
|
||||
int coreVersionRevision ()
|
||||
{
|
||||
constexpr
|
||||
int coreVersionRevision()
|
||||
{
|
||||
return conststr::parseNthInteger(__STR(ARDUINO_ESP8266_GIT_DESC), 2);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* git commit number since last tag (negative)
|
||||
* or RC-number (positive)
|
||||
/*
|
||||
git commit number since last tag (negative)
|
||||
or RC-number (positive)
|
||||
*/
|
||||
constexpr
|
||||
int coreVersionSubRevision ()
|
||||
{
|
||||
constexpr
|
||||
int coreVersionSubRevision()
|
||||
{
|
||||
return conststr::parseNthInteger(__STR(ARDUINO_ESP8266_GIT_DESC), 3);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* unique revision indentifier (never decreases)
|
||||
/*
|
||||
unique revision indentifier (never decreases)
|
||||
*/
|
||||
constexpr
|
||||
int coreVersionNumeric ()
|
||||
{
|
||||
constexpr
|
||||
int coreVersionNumeric()
|
||||
{
|
||||
return coreVersionMajor() * 10000000
|
||||
+ coreVersionMinor() * 100000
|
||||
+ coreVersionRevision() * 1000
|
||||
@ -172,9 +176,9 @@ int coreVersionNumeric ()
|
||||
coreVersionSubRevision() ?
|
||||
coreVersionSubRevision() - 100 :
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
}; // namespace esp8266
|
||||
}; // namespace esp8266
|
||||
|
||||
} // extern "C++"
|
||||
#endif // __cplusplus
|
||||
|
@ -43,76 +43,86 @@
|
||||
|
||||
extern "C" {
|
||||
|
||||
// Maximum delay between IRQs
|
||||
// Maximum delay between IRQs
|
||||
#define MAXIRQUS (10000)
|
||||
|
||||
// Set/clear GPIO 0-15 by bitmask
|
||||
// Set/clear GPIO 0-15 by bitmask
|
||||
#define SetGPIO(a) do { GPOS = a; } while (0)
|
||||
#define ClearGPIO(a) do { GPOC = a; } while (0)
|
||||
|
||||
// Waveform generator can create tones, PWM, and servos
|
||||
typedef struct {
|
||||
// Waveform generator can create tones, PWM, and servos
|
||||
typedef struct
|
||||
{
|
||||
uint32_t nextServiceCycle; // ESP cycle timer when a transition required
|
||||
uint32_t expiryCycle; // For time-limited waveform, the cycle when this waveform must stop
|
||||
uint32_t nextTimeHighCycles; // Copy over low->high to keep smooth waveform
|
||||
uint32_t nextTimeLowCycles; // Copy over high->low to keep smooth waveform
|
||||
} Waveform;
|
||||
} Waveform;
|
||||
|
||||
static Waveform waveform[17]; // State of all possible pins
|
||||
static volatile uint32_t waveformState = 0; // Is the pin high or low, updated in NMI so no access outside the NMI code
|
||||
static volatile uint32_t waveformEnabled = 0; // Is it actively running, updated in NMI so no access outside the NMI code
|
||||
static Waveform waveform[17]; // State of all possible pins
|
||||
static volatile uint32_t waveformState = 0; // Is the pin high or low, updated in NMI so no access outside the NMI code
|
||||
static volatile uint32_t waveformEnabled = 0; // Is it actively running, updated in NMI so no access outside the NMI code
|
||||
|
||||
// Enable lock-free by only allowing updates to waveformState and waveformEnabled from IRQ service routine
|
||||
static volatile uint32_t waveformToEnable = 0; // Message to the NMI handler to start a waveform on a inactive pin
|
||||
static volatile uint32_t waveformToDisable = 0; // Message to the NMI handler to disable a pin from waveform generation
|
||||
// Enable lock-free by only allowing updates to waveformState and waveformEnabled from IRQ service routine
|
||||
static volatile uint32_t waveformToEnable = 0; // Message to the NMI handler to start a waveform on a inactive pin
|
||||
static volatile uint32_t waveformToDisable = 0; // Message to the NMI handler to disable a pin from waveform generation
|
||||
|
||||
static uint32_t (*timer1CB)() = NULL;
|
||||
static uint32_t (*timer1CB)() = NULL;
|
||||
|
||||
|
||||
// Non-speed critical bits
|
||||
// Non-speed critical bits
|
||||
#pragma GCC optimize ("Os")
|
||||
|
||||
static inline ICACHE_RAM_ATTR uint32_t GetCycleCount() {
|
||||
static inline ICACHE_RAM_ATTR uint32_t GetCycleCount()
|
||||
{
|
||||
uint32_t ccount;
|
||||
__asm__ __volatile__("esync; rsr %0,ccount":"=a"(ccount));
|
||||
return ccount;
|
||||
}
|
||||
}
|
||||
|
||||
// Interrupt on/off control
|
||||
static ICACHE_RAM_ATTR void timer1Interrupt();
|
||||
static bool timerRunning = false;
|
||||
// Interrupt on/off control
|
||||
static ICACHE_RAM_ATTR void timer1Interrupt();
|
||||
static bool timerRunning = false;
|
||||
|
||||
static void initTimer() {
|
||||
static void initTimer()
|
||||
{
|
||||
timer1_disable();
|
||||
ETS_FRC_TIMER1_INTR_ATTACH(NULL, NULL);
|
||||
ETS_FRC_TIMER1_NMI_INTR_ATTACH(timer1Interrupt);
|
||||
timer1_enable(TIM_DIV1, TIM_EDGE, TIM_SINGLE);
|
||||
timerRunning = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void ICACHE_RAM_ATTR deinitTimer() {
|
||||
static void ICACHE_RAM_ATTR deinitTimer()
|
||||
{
|
||||
ETS_FRC_TIMER1_NMI_INTR_ATTACH(NULL);
|
||||
timer1_disable();
|
||||
timer1_isr_init();
|
||||
timerRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Set a callback. Pass in NULL to stop it
|
||||
void setTimer1Callback(uint32_t (*fn)()) {
|
||||
// Set a callback. Pass in NULL to stop it
|
||||
void setTimer1Callback(uint32_t (*fn)())
|
||||
{
|
||||
timer1CB = fn;
|
||||
if (!timerRunning && fn) {
|
||||
if (!timerRunning && fn)
|
||||
{
|
||||
initTimer();
|
||||
timer1_write(microsecondsToClockCycles(1)); // Cause an interrupt post-haste
|
||||
} else if (timerRunning && !fn && !waveformEnabled) {
|
||||
}
|
||||
else if (timerRunning && !fn && !waveformEnabled)
|
||||
{
|
||||
deinitTimer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start up a waveform on a pin, or change the current one. Will change to the new
|
||||
// waveform smoothly on next low->high transition. For immediate change, stopWaveform()
|
||||
// first, then it will immediately begin.
|
||||
int startWaveform(uint8_t pin, uint32_t timeHighUS, uint32_t timeLowUS, uint32_t runTimeUS) {
|
||||
if ((pin > 16) || isFlashInterfacePin(pin)) {
|
||||
// Start up a waveform on a pin, or change the current one. Will change to the new
|
||||
// waveform smoothly on next low->high transition. For immediate change, stopWaveform()
|
||||
// first, then it will immediately begin.
|
||||
int startWaveform(uint8_t pin, uint32_t timeHighUS, uint32_t timeLowUS, uint32_t runTimeUS)
|
||||
{
|
||||
if ((pin > 16) || isFlashInterfacePin(pin))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Waveform *wave = &waveform[pin];
|
||||
@ -120,88 +130,105 @@ int startWaveform(uint8_t pin, uint32_t timeHighUS, uint32_t timeLowUS, uint32_t
|
||||
wave->nextTimeHighCycles = microsecondsToClockCycles(timeHighUS);
|
||||
wave->nextTimeLowCycles = microsecondsToClockCycles(timeLowUS);
|
||||
wave->expiryCycle = runTimeUS ? GetCycleCount() + microsecondsToClockCycles(runTimeUS) : 0;
|
||||
if (runTimeUS && !wave->expiryCycle) {
|
||||
if (runTimeUS && !wave->expiryCycle)
|
||||
{
|
||||
wave->expiryCycle = 1; // expiryCycle==0 means no timeout, so avoid setting it
|
||||
}
|
||||
|
||||
uint32_t mask = 1<<pin;
|
||||
if (!(waveformEnabled & mask)) {
|
||||
uint32_t mask = 1 << pin;
|
||||
if (!(waveformEnabled & mask))
|
||||
{
|
||||
// Actually set the pin high or low in the IRQ service to guarantee times
|
||||
wave->nextServiceCycle = GetCycleCount() + microsecondsToClockCycles(1);
|
||||
waveformToEnable |= mask;
|
||||
if (!timerRunning) {
|
||||
if (!timerRunning)
|
||||
{
|
||||
initTimer();
|
||||
timer1_write(microsecondsToClockCycles(10));
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ensure timely service....
|
||||
if (T1L > microsecondsToClockCycles(10)) {
|
||||
if (T1L > microsecondsToClockCycles(10))
|
||||
{
|
||||
timer1_write(microsecondsToClockCycles(10));
|
||||
}
|
||||
}
|
||||
while (waveformToEnable) {
|
||||
while (waveformToEnable)
|
||||
{
|
||||
delay(0); // Wait for waveform to update
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Speed critical bits
|
||||
// Speed critical bits
|
||||
#pragma GCC optimize ("O2")
|
||||
// Normally would not want two copies like this, but due to different
|
||||
// optimization levels the inline attribute gets lost if we try the
|
||||
// other version.
|
||||
// Normally would not want two copies like this, but due to different
|
||||
// optimization levels the inline attribute gets lost if we try the
|
||||
// other version.
|
||||
|
||||
static inline ICACHE_RAM_ATTR uint32_t GetCycleCountIRQ() {
|
||||
static inline ICACHE_RAM_ATTR uint32_t GetCycleCountIRQ()
|
||||
{
|
||||
uint32_t ccount;
|
||||
__asm__ __volatile__("rsr %0,ccount":"=a"(ccount));
|
||||
return ccount;
|
||||
}
|
||||
}
|
||||
|
||||
static inline ICACHE_RAM_ATTR uint32_t min_u32(uint32_t a, uint32_t b) {
|
||||
if (a < b) {
|
||||
static inline ICACHE_RAM_ATTR uint32_t min_u32(uint32_t a, uint32_t b)
|
||||
{
|
||||
if (a < b)
|
||||
{
|
||||
return a;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
||||
// Stops a waveform on a pin
|
||||
int ICACHE_RAM_ATTR stopWaveform(uint8_t pin) {
|
||||
// Stops a waveform on a pin
|
||||
int ICACHE_RAM_ATTR stopWaveform(uint8_t pin)
|
||||
{
|
||||
// Can't possibly need to stop anything if there is no timer active
|
||||
if (!timerRunning) {
|
||||
if (!timerRunning)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// If user sends in a pin >16 but <32, this will always point to a 0 bit
|
||||
// If they send >=32, then the shift will result in 0 and it will also return false
|
||||
uint32_t mask = 1<<pin;
|
||||
if (!(waveformEnabled & mask)) {
|
||||
uint32_t mask = 1 << pin;
|
||||
if (!(waveformEnabled & mask))
|
||||
{
|
||||
return false; // It's not running, nothing to do here
|
||||
}
|
||||
waveformToDisable |= mask;
|
||||
// Ensure timely service....
|
||||
if (T1L > microsecondsToClockCycles(10)) {
|
||||
if (T1L > microsecondsToClockCycles(10))
|
||||
{
|
||||
timer1_write(microsecondsToClockCycles(10));
|
||||
}
|
||||
while (waveformToDisable) {
|
||||
while (waveformToDisable)
|
||||
{
|
||||
/* no-op */ // Can't delay() since stopWaveform may be called from an IRQ
|
||||
}
|
||||
if (!waveformEnabled && !timer1CB) {
|
||||
if (!waveformEnabled && !timer1CB)
|
||||
{
|
||||
deinitTimer();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// The SDK and hardware take some time to actually get to our NMI code, so
|
||||
// decrement the next IRQ's timer value by a bit so we can actually catch the
|
||||
// real CPU cycle counter we want for the waveforms.
|
||||
// The SDK and hardware take some time to actually get to our NMI code, so
|
||||
// decrement the next IRQ's timer value by a bit so we can actually catch the
|
||||
// real CPU cycle counter we want for the waveforms.
|
||||
#if F_CPU == 80000000
|
||||
#define DELTAIRQ (microsecondsToClockCycles(3))
|
||||
#define DELTAIRQ (microsecondsToClockCycles(3))
|
||||
#else
|
||||
#define DELTAIRQ (microsecondsToClockCycles(2))
|
||||
#define DELTAIRQ (microsecondsToClockCycles(2))
|
||||
#endif
|
||||
|
||||
|
||||
static ICACHE_RAM_ATTR void timer1Interrupt() {
|
||||
static ICACHE_RAM_ATTR void timer1Interrupt()
|
||||
{
|
||||
// Optimize the NMI inner loop by keeping track of the min and max GPIO that we
|
||||
// are generating. In the common case (1 PWM) these may be the same pin and
|
||||
// we can avoid looking at the other pins.
|
||||
@ -211,7 +238,8 @@ static ICACHE_RAM_ATTR void timer1Interrupt() {
|
||||
uint32_t nextEventCycles = microsecondsToClockCycles(MAXIRQUS);
|
||||
uint32_t timeoutCycle = GetCycleCountIRQ() + microsecondsToClockCycles(14);
|
||||
|
||||
if (waveformToEnable || waveformToDisable) {
|
||||
if (waveformToEnable || waveformToDisable)
|
||||
{
|
||||
// Handle enable/disable requests from main app.
|
||||
waveformEnabled = (waveformEnabled & ~waveformToDisable) | waveformToEnable; // Set the requested waveforms on/off
|
||||
waveformState &= ~waveformToEnable; // And clear the state of any just started
|
||||
@ -224,14 +252,18 @@ static ICACHE_RAM_ATTR void timer1Interrupt() {
|
||||
}
|
||||
|
||||
bool done = false;
|
||||
if (waveformEnabled) {
|
||||
do {
|
||||
if (waveformEnabled)
|
||||
{
|
||||
do
|
||||
{
|
||||
nextEventCycles = microsecondsToClockCycles(MAXIRQUS);
|
||||
for (int i = startPin; i <= endPin; i++) {
|
||||
uint32_t mask = 1<<i;
|
||||
for (int i = startPin; i <= endPin; i++)
|
||||
{
|
||||
uint32_t mask = 1 << i;
|
||||
|
||||
// If it's not on, ignore!
|
||||
if (!(waveformEnabled & mask)) {
|
||||
if (!(waveformEnabled & mask))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -239,14 +271,19 @@ static ICACHE_RAM_ATTR void timer1Interrupt() {
|
||||
uint32_t now = GetCycleCountIRQ();
|
||||
|
||||
// Disable any waveforms that are done
|
||||
if (wave->expiryCycle) {
|
||||
if (wave->expiryCycle)
|
||||
{
|
||||
int32_t expiryToGo = wave->expiryCycle - now;
|
||||
if (expiryToGo < 0) {
|
||||
if (expiryToGo < 0)
|
||||
{
|
||||
// Done, remove!
|
||||
waveformEnabled &= ~mask;
|
||||
if (i == 16) {
|
||||
if (i == 16)
|
||||
{
|
||||
GP16O &= ~1;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
ClearGPIO(mask);
|
||||
}
|
||||
continue;
|
||||
@ -255,26 +292,38 @@ static ICACHE_RAM_ATTR void timer1Interrupt() {
|
||||
|
||||
// Check for toggles
|
||||
int32_t cyclesToGo = wave->nextServiceCycle - now;
|
||||
if (cyclesToGo < 0) {
|
||||
if (cyclesToGo < 0)
|
||||
{
|
||||
waveformState ^= mask;
|
||||
if (waveformState & mask) {
|
||||
if (i == 16) {
|
||||
if (waveformState & mask)
|
||||
{
|
||||
if (i == 16)
|
||||
{
|
||||
GP16O |= 1; // GPIO16 write slow as it's RMW
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
SetGPIO(mask);
|
||||
}
|
||||
wave->nextServiceCycle = now + wave->nextTimeHighCycles;
|
||||
nextEventCycles = min_u32(nextEventCycles, wave->nextTimeHighCycles);
|
||||
} else {
|
||||
if (i == 16) {
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i == 16)
|
||||
{
|
||||
GP16O &= ~1; // GPIO16 write slow as it's RMW
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
ClearGPIO(mask);
|
||||
}
|
||||
wave->nextServiceCycle = now + wave->nextTimeLowCycles;
|
||||
nextEventCycles = min_u32(nextEventCycles, wave->nextTimeLowCycles);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t deltaCycles = wave->nextServiceCycle - now;
|
||||
nextEventCycles = min_u32(nextEventCycles, deltaCycles);
|
||||
}
|
||||
@ -288,11 +337,13 @@ static ICACHE_RAM_ATTR void timer1Interrupt() {
|
||||
} while (!done);
|
||||
} // if (waveformEnabled)
|
||||
|
||||
if (timer1CB) {
|
||||
if (timer1CB)
|
||||
{
|
||||
nextEventCycles = min_u32(nextEventCycles, timer1CB());
|
||||
}
|
||||
|
||||
if (nextEventCycles < microsecondsToClockCycles(10)) {
|
||||
if (nextEventCycles < microsecondsToClockCycles(10))
|
||||
{
|
||||
nextEventCycles = microsecondsToClockCycles(10);
|
||||
}
|
||||
nextEventCycles -= DELTAIRQ;
|
||||
@ -304,6 +355,6 @@ static ICACHE_RAM_ATTR void timer1Interrupt() {
|
||||
T1L = nextEventCycles; // Already know we're in range by MAXIRQUS
|
||||
#endif
|
||||
TEIE |= TEIE1; // Edge int enable
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -17,7 +17,7 @@
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
*/
|
||||
|
||||
#include "wiring_private.h"
|
||||
#include "ets_sys.h"
|
||||
@ -27,143 +27,153 @@
|
||||
|
||||
extern "C" {
|
||||
|
||||
extern void ets_delay_us(uint32_t us);
|
||||
extern void esp_schedule();
|
||||
extern void esp_yield();
|
||||
extern void ets_delay_us(uint32_t us);
|
||||
extern void esp_schedule();
|
||||
extern void esp_yield();
|
||||
|
||||
static os_timer_t delay_timer;
|
||||
static os_timer_t micros_overflow_timer;
|
||||
static uint32_t micros_at_last_overflow_tick = 0;
|
||||
static uint32_t micros_overflow_count = 0;
|
||||
static os_timer_t delay_timer;
|
||||
static os_timer_t micros_overflow_timer;
|
||||
static uint32_t micros_at_last_overflow_tick = 0;
|
||||
static uint32_t micros_overflow_count = 0;
|
||||
#define ONCE 0
|
||||
#define REPEAT 1
|
||||
|
||||
void delay_end(void* arg) {
|
||||
void delay_end(void* arg)
|
||||
{
|
||||
(void) arg;
|
||||
esp_schedule();
|
||||
}
|
||||
}
|
||||
|
||||
void delay(unsigned long ms) {
|
||||
if(ms) {
|
||||
void delay(unsigned long ms)
|
||||
{
|
||||
if (ms)
|
||||
{
|
||||
os_timer_setfn(&delay_timer, (os_timer_func_t*) &delay_end, 0);
|
||||
os_timer_arm(&delay_timer, ms, ONCE);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
esp_schedule();
|
||||
}
|
||||
esp_yield();
|
||||
if(ms) {
|
||||
if (ms)
|
||||
{
|
||||
os_timer_disarm(&delay_timer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void micros_overflow_tick(void* arg) {
|
||||
void micros_overflow_tick(void* arg)
|
||||
{
|
||||
(void) arg;
|
||||
uint32_t m = system_get_time();
|
||||
if(m < micros_at_last_overflow_tick)
|
||||
if (m < micros_at_last_overflow_tick)
|
||||
{
|
||||
++micros_overflow_count;
|
||||
}
|
||||
micros_at_last_overflow_tick = m;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// millis() 'magic multiplier' approximation
|
||||
//
|
||||
// This function corrects the cumlative (296us / usec overflow) drift
|
||||
// seen in the orignal 'millis()' function.
|
||||
//
|
||||
// Input:
|
||||
// 'm' - 32-bit usec counter, 0 <= m <= 0xFFFFFFFF
|
||||
// 'c' - 32-bit usec overflow counter 0 <= c < 0x00400000
|
||||
// Output:
|
||||
// Returns milliseconds in modulo 0x1,0000,0000 (0 to 0xFFFFFFFF)
|
||||
//
|
||||
// Notes:
|
||||
//
|
||||
// 1) This routine approximates the 64-bit integer division,
|
||||
//
|
||||
// quotient = ( 2^32 c + m ) / 1000,
|
||||
//
|
||||
// through the use of 'magic' multipliers. A slow division is replaced by
|
||||
// a faster multiply using a scaled multiplicative inverse of the divisor:
|
||||
//
|
||||
// quotient =~ ( 2^32 c + m ) * k, where k = Ceiling[ 2^n / 1000 ]
|
||||
//
|
||||
// The precision difference between multiplier and divisor sets the
|
||||
// upper-bound of the dividend which can be successfully divided.
|
||||
//
|
||||
// For this application, n = 64, and the divisor (1000) has 10-bits of
|
||||
// precision. This sets the dividend upper-bound to (64 - 10) = 54 bits,
|
||||
// and that of 'c' to (54 - 32) = 22 bits. This corresponds to a value
|
||||
// for 'c' = 0x0040,0000 , or +570 years of usec counter overflows.
|
||||
//
|
||||
// 2) A distributed multiply with offset-summing is used find k( 2^32 c + m ):
|
||||
//
|
||||
// prd = (2^32 kh + kl) * ( 2^32 c + m )
|
||||
// = 2^64 kh c + 2^32 kl c + 2^32 kh m + kl m
|
||||
// (d) (c) (b) (a)
|
||||
//
|
||||
// Graphically, the offset-sums align in little endian like this:
|
||||
// LS -> MS
|
||||
// 32 64 96 128
|
||||
// | a[-1] | a[0] | a[1] | a[2] |
|
||||
// | m kl | 0 | 0 | a[-1] not needed
|
||||
// | | m kh | |
|
||||
// | | c kl | | a[1] holds the result
|
||||
// | | | c kh | a[2] can be discarded
|
||||
//
|
||||
// As only the high-word of 'm kl' and low-word of 'c kh' contribute to the
|
||||
// overall result, only (2) 32-bit words are needed for the accumulator.
|
||||
//
|
||||
// 3) As C++ does not intrinsically test for addition overflows, one must
|
||||
// code specifically to detect them. This approximation skips these
|
||||
// overflow checks for speed, hence the sum,
|
||||
//
|
||||
// highword( m kl ) + m kh + c kl < (2^64-1), MUST NOT OVERFLOW.
|
||||
//
|
||||
// To meet this criteria, not only do we have to pick 'k' to achieve our
|
||||
// desired precision, we also have to split 'k' appropriately to avoid
|
||||
// any addition overflows.
|
||||
//
|
||||
// 'k' should be also chosen to align the various products on byte
|
||||
// boundaries to avoid any 64-bit shifts before additions, as they incur
|
||||
// major time penalties. The 'k' chosen for this specific division by 1000
|
||||
// was picked primarily to avoid shifts as well as for precision.
|
||||
//
|
||||
// For the reasons list above, this routine is NOT a general one.
|
||||
// Changing divisors could break the overflow requirement and force
|
||||
// picking a 'k' split which requires shifts before additions.
|
||||
//
|
||||
// ** Test THOROUGHLY after making changes **
|
||||
//
|
||||
// 4) Results of time benchmarks run on an ESP8266 Huzzah feather are:
|
||||
//
|
||||
// usec x Orig Comment
|
||||
// Orig: 3.18 1.00 Original code
|
||||
// Corr: 13.21 4.15 64-bit reference code
|
||||
// Test: 4.60 1.45 64-bit magic multiply, 4x32
|
||||
//
|
||||
// The magic multiplier routine runs ~3x faster than the reference. Execution
|
||||
// times can vary considerably with the numbers being multiplied, so one
|
||||
// should derate this factor to around 2x, worst case.
|
||||
//
|
||||
// Reference function: corrected millis(), 64-bit arithmetic,
|
||||
// truncated to 32-bits by return
|
||||
// unsigned long ICACHE_RAM_ATTR millis_corr_DEBUG( void )
|
||||
// {
|
||||
// // Get usec system time, usec overflow conter
|
||||
// ......
|
||||
// return ( (c * 4294967296 + m) / 1000 ); // 64-bit division is SLOW
|
||||
// } //millis_corr
|
||||
//
|
||||
// 5) See this link for a good discussion on magic multipliers:
|
||||
// http://ridiculousfish.com/blog/posts/labor-of-division-episode-i.html
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
// millis() 'magic multiplier' approximation
|
||||
//
|
||||
// This function corrects the cumlative (296us / usec overflow) drift
|
||||
// seen in the orignal 'millis()' function.
|
||||
//
|
||||
// Input:
|
||||
// 'm' - 32-bit usec counter, 0 <= m <= 0xFFFFFFFF
|
||||
// 'c' - 32-bit usec overflow counter 0 <= c < 0x00400000
|
||||
// Output:
|
||||
// Returns milliseconds in modulo 0x1,0000,0000 (0 to 0xFFFFFFFF)
|
||||
//
|
||||
// Notes:
|
||||
//
|
||||
// 1) This routine approximates the 64-bit integer division,
|
||||
//
|
||||
// quotient = ( 2^32 c + m ) / 1000,
|
||||
//
|
||||
// through the use of 'magic' multipliers. A slow division is replaced by
|
||||
// a faster multiply using a scaled multiplicative inverse of the divisor:
|
||||
//
|
||||
// quotient =~ ( 2^32 c + m ) * k, where k = Ceiling[ 2^n / 1000 ]
|
||||
//
|
||||
// The precision difference between multiplier and divisor sets the
|
||||
// upper-bound of the dividend which can be successfully divided.
|
||||
//
|
||||
// For this application, n = 64, and the divisor (1000) has 10-bits of
|
||||
// precision. This sets the dividend upper-bound to (64 - 10) = 54 bits,
|
||||
// and that of 'c' to (54 - 32) = 22 bits. This corresponds to a value
|
||||
// for 'c' = 0x0040,0000 , or +570 years of usec counter overflows.
|
||||
//
|
||||
// 2) A distributed multiply with offset-summing is used find k( 2^32 c + m ):
|
||||
//
|
||||
// prd = (2^32 kh + kl) * ( 2^32 c + m )
|
||||
// = 2^64 kh c + 2^32 kl c + 2^32 kh m + kl m
|
||||
// (d) (c) (b) (a)
|
||||
//
|
||||
// Graphically, the offset-sums align in little endian like this:
|
||||
// LS -> MS
|
||||
// 32 64 96 128
|
||||
// | a[-1] | a[0] | a[1] | a[2] |
|
||||
// | m kl | 0 | 0 | a[-1] not needed
|
||||
// | | m kh | |
|
||||
// | | c kl | | a[1] holds the result
|
||||
// | | | c kh | a[2] can be discarded
|
||||
//
|
||||
// As only the high-word of 'm kl' and low-word of 'c kh' contribute to the
|
||||
// overall result, only (2) 32-bit words are needed for the accumulator.
|
||||
//
|
||||
// 3) As C++ does not intrinsically test for addition overflows, one must
|
||||
// code specifically to detect them. This approximation skips these
|
||||
// overflow checks for speed, hence the sum,
|
||||
//
|
||||
// highword( m kl ) + m kh + c kl < (2^64-1), MUST NOT OVERFLOW.
|
||||
//
|
||||
// To meet this criteria, not only do we have to pick 'k' to achieve our
|
||||
// desired precision, we also have to split 'k' appropriately to avoid
|
||||
// any addition overflows.
|
||||
//
|
||||
// 'k' should be also chosen to align the various products on byte
|
||||
// boundaries to avoid any 64-bit shifts before additions, as they incur
|
||||
// major time penalties. The 'k' chosen for this specific division by 1000
|
||||
// was picked primarily to avoid shifts as well as for precision.
|
||||
//
|
||||
// For the reasons list above, this routine is NOT a general one.
|
||||
// Changing divisors could break the overflow requirement and force
|
||||
// picking a 'k' split which requires shifts before additions.
|
||||
//
|
||||
// ** Test THOROUGHLY after making changes **
|
||||
//
|
||||
// 4) Results of time benchmarks run on an ESP8266 Huzzah feather are:
|
||||
//
|
||||
// usec x Orig Comment
|
||||
// Orig: 3.18 1.00 Original code
|
||||
// Corr: 13.21 4.15 64-bit reference code
|
||||
// Test: 4.60 1.45 64-bit magic multiply, 4x32
|
||||
//
|
||||
// The magic multiplier routine runs ~3x faster than the reference. Execution
|
||||
// times can vary considerably with the numbers being multiplied, so one
|
||||
// should derate this factor to around 2x, worst case.
|
||||
//
|
||||
// Reference function: corrected millis(), 64-bit arithmetic,
|
||||
// truncated to 32-bits by return
|
||||
// unsigned long ICACHE_RAM_ATTR millis_corr_DEBUG( void )
|
||||
// {
|
||||
// // Get usec system time, usec overflow conter
|
||||
// ......
|
||||
// return ( (c * 4294967296 + m) / 1000 ); // 64-bit division is SLOW
|
||||
// } //millis_corr
|
||||
//
|
||||
// 5) See this link for a good discussion on magic multipliers:
|
||||
// http://ridiculousfish.com/blog/posts/labor-of-division-episode-i.html
|
||||
//
|
||||
|
||||
#define MAGIC_1E3_wLO 0x4bc6a7f0 // LS part
|
||||
#define MAGIC_1E3_wHI 0x00418937 // MS part, magic multiplier
|
||||
|
||||
unsigned long ICACHE_RAM_ATTR millis()
|
||||
{
|
||||
union {
|
||||
unsigned long ICACHE_RAM_ATTR millis()
|
||||
{
|
||||
union
|
||||
{
|
||||
uint64_t q; // Accumulator, 64-bit, little endian
|
||||
uint32_t a[2]; // ..........., 32-bit segments
|
||||
} acc;
|
||||
@ -177,41 +187,45 @@ unsigned long ICACHE_RAM_ATTR millis()
|
||||
// (a) Init. low-acc with high-word of 1st product. The right-shift
|
||||
// falls on a byte boundary, hence is relatively quick.
|
||||
|
||||
acc.q = ( (uint64_t)( m * (uint64_t)MAGIC_1E3_wLO ) >> 32 );
|
||||
acc.q = ((uint64_t)(m * (uint64_t)MAGIC_1E3_wLO) >> 32);
|
||||
|
||||
// (b) Offset sum, low-acc
|
||||
acc.q += ( m * (uint64_t)MAGIC_1E3_wHI );
|
||||
acc.q += (m * (uint64_t)MAGIC_1E3_wHI);
|
||||
|
||||
// (c) Offset sum, low-acc
|
||||
acc.q += ( c * (uint64_t)MAGIC_1E3_wLO );
|
||||
acc.q += (c * (uint64_t)MAGIC_1E3_wLO);
|
||||
|
||||
// (d) Truncated sum, high-acc
|
||||
acc.a[1] += (uint32_t)( c * (uint64_t)MAGIC_1E3_wHI );
|
||||
acc.a[1] += (uint32_t)(c * (uint64_t)MAGIC_1E3_wHI);
|
||||
|
||||
return ( acc.a[1] ); // Extract result, high-acc
|
||||
return (acc.a[1]); // Extract result, high-acc
|
||||
|
||||
} //millis
|
||||
} //millis
|
||||
|
||||
unsigned long ICACHE_RAM_ATTR micros() {
|
||||
unsigned long ICACHE_RAM_ATTR micros()
|
||||
{
|
||||
return system_get_time();
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t ICACHE_RAM_ATTR micros64() {
|
||||
uint64_t ICACHE_RAM_ATTR micros64()
|
||||
{
|
||||
uint32_t low32_us = system_get_time();
|
||||
uint32_t high32_us = micros_overflow_count + ((low32_us < micros_at_last_overflow_tick) ? 1 : 0);
|
||||
uint64_t duration64_us = (uint64_t)high32_us << 32 | low32_us;
|
||||
return duration64_us;
|
||||
}
|
||||
}
|
||||
|
||||
void ICACHE_RAM_ATTR delayMicroseconds(unsigned int us) {
|
||||
void ICACHE_RAM_ATTR delayMicroseconds(unsigned int us)
|
||||
{
|
||||
os_delay_us(us);
|
||||
}
|
||||
}
|
||||
|
||||
void init() {
|
||||
void init()
|
||||
{
|
||||
initPins();
|
||||
timer1_isr_init();
|
||||
os_timer_setfn(µs_overflow_timer, (os_timer_func_t*) µs_overflow_tick, 0);
|
||||
os_timer_arm(µs_overflow_timer, 60000, REPEAT);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -28,15 +28,16 @@
|
||||
|
||||
extern "C" {
|
||||
|
||||
extern int __analogRead(uint8_t pin)
|
||||
{
|
||||
extern int __analogRead(uint8_t pin)
|
||||
{
|
||||
// accept both A0 constant and ADC channel number
|
||||
if(pin == 17 || pin == 0) {
|
||||
if (pin == 17 || pin == 0)
|
||||
{
|
||||
return system_adc_read();
|
||||
}
|
||||
return digitalRead(pin) * 1023;
|
||||
}
|
||||
}
|
||||
|
||||
extern int analogRead(uint8_t pin) __attribute__ ((weak, alias("__analogRead")));
|
||||
extern int analogRead(uint8_t pin) __attribute__((weak, alias("__analogRead")));
|
||||
|
||||
};
|
||||
|
@ -29,121 +29,183 @@
|
||||
|
||||
extern "C" {
|
||||
|
||||
uint8_t esp8266_gpioToFn[16] = {0x34, 0x18, 0x38, 0x14, 0x3C, 0x40, 0x1C, 0x20, 0x24, 0x28, 0x2C, 0x30, 0x04, 0x08, 0x0C, 0x10};
|
||||
uint8_t esp8266_gpioToFn[16] = {0x34, 0x18, 0x38, 0x14, 0x3C, 0x40, 0x1C, 0x20, 0x24, 0x28, 0x2C, 0x30, 0x04, 0x08, 0x0C, 0x10};
|
||||
|
||||
extern void __pinMode(uint8_t pin, uint8_t mode) {
|
||||
if(pin < 16){
|
||||
if(mode == SPECIAL){
|
||||
extern void __pinMode(uint8_t pin, uint8_t mode)
|
||||
{
|
||||
if (pin < 16)
|
||||
{
|
||||
if (mode == SPECIAL)
|
||||
{
|
||||
GPC(pin) = (GPC(pin) & (0xF << GPCI)); //SOURCE(GPIO) | DRIVER(NORMAL) | INT_TYPE(UNCHANGED) | WAKEUP_ENABLE(DISABLED)
|
||||
GPEC = (1 << pin); //Disable
|
||||
GPF(pin) = GPFFS(GPFFS_BUS(pin));//Set mode to BUS (RX0, TX0, TX1, SPI, HSPI or CLK depending in the pin)
|
||||
if(pin == 3) GPF(pin) |= (1 << GPFPU);//enable pullup on RX
|
||||
} else if(mode & FUNCTION_0){
|
||||
if (pin == 3)
|
||||
{
|
||||
GPF(pin) |= (1 << GPFPU); //enable pullup on RX
|
||||
}
|
||||
}
|
||||
else if (mode & FUNCTION_0)
|
||||
{
|
||||
GPC(pin) = (GPC(pin) & (0xF << GPCI)); //SOURCE(GPIO) | DRIVER(NORMAL) | INT_TYPE(UNCHANGED) | WAKEUP_ENABLE(DISABLED)
|
||||
GPEC = (1 << pin); //Disable
|
||||
GPF(pin) = GPFFS((mode >> 4) & 0x07);
|
||||
if(pin == 13 && mode == FUNCTION_4) GPF(pin) |= (1 << GPFPU);//enable pullup on RX
|
||||
} else if(mode == OUTPUT || mode == OUTPUT_OPEN_DRAIN){
|
||||
if (pin == 13 && mode == FUNCTION_4)
|
||||
{
|
||||
GPF(pin) |= (1 << GPFPU); //enable pullup on RX
|
||||
}
|
||||
}
|
||||
else if (mode == OUTPUT || mode == OUTPUT_OPEN_DRAIN)
|
||||
{
|
||||
GPF(pin) = GPFFS(GPFFS_GPIO(pin));//Set mode to GPIO
|
||||
GPC(pin) = (GPC(pin) & (0xF << GPCI)); //SOURCE(GPIO) | DRIVER(NORMAL) | INT_TYPE(UNCHANGED) | WAKEUP_ENABLE(DISABLED)
|
||||
if(mode == OUTPUT_OPEN_DRAIN) GPC(pin) |= (1 << GPCD);
|
||||
if (mode == OUTPUT_OPEN_DRAIN)
|
||||
{
|
||||
GPC(pin) |= (1 << GPCD);
|
||||
}
|
||||
GPES = (1 << pin); //Enable
|
||||
} else if(mode == INPUT || mode == INPUT_PULLUP){
|
||||
}
|
||||
else if (mode == INPUT || mode == INPUT_PULLUP)
|
||||
{
|
||||
GPF(pin) = GPFFS(GPFFS_GPIO(pin));//Set mode to GPIO
|
||||
GPEC = (1 << pin); //Disable
|
||||
GPC(pin) = (GPC(pin) & (0xF << GPCI)) | (1 << GPCD); //SOURCE(GPIO) | DRIVER(OPEN_DRAIN) | INT_TYPE(UNCHANGED) | WAKEUP_ENABLE(DISABLED)
|
||||
if(mode == INPUT_PULLUP) {
|
||||
if (mode == INPUT_PULLUP)
|
||||
{
|
||||
GPF(pin) |= (1 << GPFPU); // Enable Pullup
|
||||
}
|
||||
} else if(mode == WAKEUP_PULLUP || mode == WAKEUP_PULLDOWN){
|
||||
}
|
||||
else if (mode == WAKEUP_PULLUP || mode == WAKEUP_PULLDOWN)
|
||||
{
|
||||
GPF(pin) = GPFFS(GPFFS_GPIO(pin));//Set mode to GPIO
|
||||
GPEC = (1 << pin); //Disable
|
||||
if(mode == WAKEUP_PULLUP) {
|
||||
if (mode == WAKEUP_PULLUP)
|
||||
{
|
||||
GPF(pin) |= (1 << GPFPU); // Enable Pullup
|
||||
GPC(pin) = (1 << GPCD) | (4 << GPCI) | (1 << GPCWE); //SOURCE(GPIO) | DRIVER(OPEN_DRAIN) | INT_TYPE(LOW) | WAKEUP_ENABLE(ENABLED)
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
GPF(pin) |= (1 << GPFPD); // Enable Pulldown
|
||||
GPC(pin) = (1 << GPCD) | (5 << GPCI) | (1 << GPCWE); //SOURCE(GPIO) | DRIVER(OPEN_DRAIN) | INT_TYPE(HIGH) | WAKEUP_ENABLE(ENABLED)
|
||||
}
|
||||
}
|
||||
} else if(pin == 16){
|
||||
}
|
||||
else if (pin == 16)
|
||||
{
|
||||
GPF16 = GP16FFS(GPFFS_GPIO(pin));//Set mode to GPIO
|
||||
GPC16 = 0;
|
||||
if(mode == INPUT || mode == INPUT_PULLDOWN_16){
|
||||
if(mode == INPUT_PULLDOWN_16){
|
||||
if (mode == INPUT || mode == INPUT_PULLDOWN_16)
|
||||
{
|
||||
if (mode == INPUT_PULLDOWN_16)
|
||||
{
|
||||
GPF16 |= (1 << GP16FPD);//Enable Pulldown
|
||||
}
|
||||
GP16E &= ~1;
|
||||
} else if(mode == OUTPUT){
|
||||
}
|
||||
else if (mode == OUTPUT)
|
||||
{
|
||||
GP16E |= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern void ICACHE_RAM_ATTR __digitalWrite(uint8_t pin, uint8_t val) {
|
||||
stopWaveform(pin);
|
||||
if(pin < 16){
|
||||
if(val) GPOS = (1 << pin);
|
||||
else GPOC = (1 << pin);
|
||||
} else if(pin == 16){
|
||||
if(val) GP16O |= 1;
|
||||
else GP16O &= ~1;
|
||||
}
|
||||
}
|
||||
|
||||
extern int ICACHE_RAM_ATTR __digitalRead(uint8_t pin) {
|
||||
if(pin < 16){
|
||||
extern void ICACHE_RAM_ATTR __digitalWrite(uint8_t pin, uint8_t val)
|
||||
{
|
||||
stopWaveform(pin);
|
||||
if (pin < 16)
|
||||
{
|
||||
if (val)
|
||||
{
|
||||
GPOS = (1 << pin);
|
||||
}
|
||||
else
|
||||
{
|
||||
GPOC = (1 << pin);
|
||||
}
|
||||
}
|
||||
else if (pin == 16)
|
||||
{
|
||||
if (val)
|
||||
{
|
||||
GP16O |= 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
GP16O &= ~1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern int ICACHE_RAM_ATTR __digitalRead(uint8_t pin)
|
||||
{
|
||||
if (pin < 16)
|
||||
{
|
||||
return GPIP(pin);
|
||||
} else if(pin == 16){
|
||||
}
|
||||
else if (pin == 16)
|
||||
{
|
||||
return GP16I & 0x01;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
GPIO INTERRUPTS
|
||||
*/
|
||||
*/
|
||||
|
||||
typedef void (*voidFuncPtr)(void);
|
||||
typedef void (*voidFuncPtrArg)(void*);
|
||||
typedef void (*voidFuncPtr)(void);
|
||||
typedef void (*voidFuncPtrArg)(void*);
|
||||
|
||||
typedef struct {
|
||||
typedef struct
|
||||
{
|
||||
uint8_t mode;
|
||||
void (*fn)(void);
|
||||
void * arg;
|
||||
} interrupt_handler_t;
|
||||
} interrupt_handler_t;
|
||||
|
||||
//duplicate from functionalInterrupt.h keep in sync
|
||||
typedef struct InterruptInfo {
|
||||
//duplicate from functionalInterrupt.h keep in sync
|
||||
typedef struct InterruptInfo
|
||||
{
|
||||
uint8_t pin;
|
||||
uint8_t value;
|
||||
uint32_t micro;
|
||||
} InterruptInfo;
|
||||
} InterruptInfo;
|
||||
|
||||
typedef struct {
|
||||
typedef struct
|
||||
{
|
||||
InterruptInfo* interruptInfo;
|
||||
void* functionInfo;
|
||||
} ArgStructure;
|
||||
} ArgStructure;
|
||||
|
||||
static interrupt_handler_t interrupt_handlers[16];
|
||||
static uint32_t interrupt_reg = 0;
|
||||
static interrupt_handler_t interrupt_handlers[16];
|
||||
static uint32_t interrupt_reg = 0;
|
||||
|
||||
void ICACHE_RAM_ATTR interrupt_handler(void *arg) {
|
||||
void ICACHE_RAM_ATTR interrupt_handler(void *arg)
|
||||
{
|
||||
(void) arg;
|
||||
uint32_t status = GPIE;
|
||||
GPIEC = status;//clear them interrupts
|
||||
uint32_t levels = GPI;
|
||||
if(status == 0 || interrupt_reg == 0) return;
|
||||
if (status == 0 || interrupt_reg == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
ETS_GPIO_INTR_DISABLE();
|
||||
int i = 0;
|
||||
uint32_t changedbits = status & interrupt_reg;
|
||||
while(changedbits){
|
||||
while(!(changedbits & (1 << i))) i++;
|
||||
while (changedbits)
|
||||
{
|
||||
while (!(changedbits & (1 << i)))
|
||||
{
|
||||
i++;
|
||||
}
|
||||
changedbits &= ~(1 << i);
|
||||
interrupt_handler_t *handler = &interrupt_handlers[i];
|
||||
if (handler->fn &&
|
||||
(handler->mode == CHANGE ||
|
||||
(handler->mode & 1) == !!(levels & (1 << i)))) {
|
||||
(handler->mode & 1) == !!(levels & (1 << i))))
|
||||
{
|
||||
// to make ISR compatible to Arduino AVR model where interrupts are disabled
|
||||
// we disable them before we call the client ISR
|
||||
uint32_t savedPS = xt_rsil(15); // stop other interrupts
|
||||
@ -166,11 +228,12 @@ void ICACHE_RAM_ATTR interrupt_handler(void *arg) {
|
||||
}
|
||||
}
|
||||
ETS_GPIO_INTR_ENABLE();
|
||||
}
|
||||
}
|
||||
|
||||
extern void cleanupFunctional(void* arg);
|
||||
extern void cleanupFunctional(void* arg);
|
||||
|
||||
extern void ICACHE_RAM_ATTR __attachInterruptArg(uint8_t pin, voidFuncPtr userFunc, void *arg, int mode) {
|
||||
extern void ICACHE_RAM_ATTR __attachInterruptArg(uint8_t pin, voidFuncPtr userFunc, void *arg, int mode)
|
||||
{
|
||||
|
||||
// #5780
|
||||
// https://github.com/esp8266/esp8266-wiki/wiki/Memory-Map
|
||||
@ -181,7 +244,8 @@ extern void ICACHE_RAM_ATTR __attachInterruptArg(uint8_t pin, voidFuncPtr userFu
|
||||
abort();
|
||||
}
|
||||
|
||||
if(pin < 16) {
|
||||
if (pin < 16)
|
||||
{
|
||||
ETS_GPIO_INTR_DISABLE();
|
||||
interrupt_handler_t *handler = &interrupt_handlers[pin];
|
||||
handler->mode = mode;
|
||||
@ -198,15 +262,17 @@ extern void ICACHE_RAM_ATTR __attachInterruptArg(uint8_t pin, voidFuncPtr userFu
|
||||
ETS_GPIO_INTR_ATTACH(interrupt_handler, &interrupt_reg);
|
||||
ETS_GPIO_INTR_ENABLE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern void ICACHE_RAM_ATTR __attachInterrupt(uint8_t pin, voidFuncPtr userFunc, int mode )
|
||||
{
|
||||
__attachInterruptArg (pin, userFunc, 0, mode);
|
||||
}
|
||||
extern void ICACHE_RAM_ATTR __attachInterrupt(uint8_t pin, voidFuncPtr userFunc, int mode)
|
||||
{
|
||||
__attachInterruptArg(pin, userFunc, 0, mode);
|
||||
}
|
||||
|
||||
extern void ICACHE_RAM_ATTR __detachInterrupt(uint8_t pin) {
|
||||
if(pin < 16) {
|
||||
extern void ICACHE_RAM_ATTR __detachInterrupt(uint8_t pin)
|
||||
{
|
||||
if (pin < 16)
|
||||
{
|
||||
ETS_GPIO_INTR_DISABLE();
|
||||
GPC(pin) &= ~(0xF << GPCI);//INT mode disabled
|
||||
GPIEC = (1 << pin); //Clear Interrupt for this pin
|
||||
@ -220,29 +286,34 @@ extern void ICACHE_RAM_ATTR __detachInterrupt(uint8_t pin) {
|
||||
}
|
||||
handler->arg = 0;
|
||||
if (interrupt_reg)
|
||||
{
|
||||
ETS_GPIO_INTR_ENABLE();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void initPins() {
|
||||
void initPins()
|
||||
{
|
||||
//Disable UART interrupts
|
||||
system_set_os_print(0);
|
||||
U0IE = 0;
|
||||
U1IE = 0;
|
||||
|
||||
for (int i = 0; i <= 5; ++i) {
|
||||
for (int i = 0; i <= 5; ++i)
|
||||
{
|
||||
pinMode(i, INPUT);
|
||||
}
|
||||
// pins 6-11 are used for the SPI flash interface
|
||||
for (int i = 12; i <= 16; ++i) {
|
||||
for (int i = 12; i <= 16; ++i)
|
||||
{
|
||||
pinMode(i, INPUT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern void pinMode(uint8_t pin, uint8_t mode) __attribute__ ((weak, alias("__pinMode")));
|
||||
extern void digitalWrite(uint8_t pin, uint8_t val) __attribute__ ((weak, alias("__digitalWrite")));
|
||||
extern int digitalRead(uint8_t pin) __attribute__ ((weak, alias("__digitalRead")));
|
||||
extern void attachInterrupt(uint8_t pin, voidFuncPtr handler, int mode) __attribute__ ((weak, alias("__attachInterrupt")));
|
||||
extern void detachInterrupt(uint8_t pin) __attribute__ ((weak, alias("__detachInterrupt")));
|
||||
extern void pinMode(uint8_t pin, uint8_t mode) __attribute__((weak, alias("__pinMode")));
|
||||
extern void digitalWrite(uint8_t pin, uint8_t val) __attribute__((weak, alias("__digitalWrite")));
|
||||
extern int digitalRead(uint8_t pin) __attribute__((weak, alias("__digitalRead")));
|
||||
extern void attachInterrupt(uint8_t pin, voidFuncPtr handler, int mode) __attribute__((weak, alias("__attachInterrupt")));
|
||||
extern void detachInterrupt(uint8_t pin) __attribute__((weak, alias("__detachInterrupt")));
|
||||
|
||||
};
|
||||
|
@ -24,7 +24,7 @@
|
||||
|
||||
extern "C" {
|
||||
|
||||
extern uint32_t xthal_get_ccount();
|
||||
extern uint32_t xthal_get_ccount();
|
||||
|
||||
#define WAIT_FOR_PIN_STATE(state) \
|
||||
while (digitalRead(pin) != (state)) { \
|
||||
@ -34,11 +34,12 @@ extern uint32_t xthal_get_ccount();
|
||||
optimistic_yield(5000); \
|
||||
}
|
||||
|
||||
// max timeout is 27 seconds at 160MHz clock and 54 seconds at 80MHz clock
|
||||
unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout)
|
||||
{
|
||||
// max timeout is 27 seconds at 160MHz clock and 54 seconds at 80MHz clock
|
||||
unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout)
|
||||
{
|
||||
const uint32_t max_timeout_us = clockCyclesToMicroseconds(UINT_MAX);
|
||||
if (timeout > max_timeout_us) {
|
||||
if (timeout > max_timeout_us)
|
||||
{
|
||||
timeout = max_timeout_us;
|
||||
}
|
||||
const uint32_t timeout_cycles = microsecondsToClockCycles(timeout);
|
||||
@ -48,11 +49,11 @@ unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout)
|
||||
const uint32_t pulse_start_cycle_count = xthal_get_ccount();
|
||||
WAIT_FOR_PIN_STATE(!state);
|
||||
return clockCyclesToMicroseconds(xthal_get_ccount() - pulse_start_cycle_count);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout)
|
||||
{
|
||||
unsigned long pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout)
|
||||
{
|
||||
return pulseIn(pin, state, timeout);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -26,34 +26,47 @@
|
||||
|
||||
extern "C" {
|
||||
|
||||
static uint32_t analogMap = 0;
|
||||
static int32_t analogScale = PWMRANGE;
|
||||
static uint16_t analogFreq = 1000;
|
||||
static uint32_t analogMap = 0;
|
||||
static int32_t analogScale = PWMRANGE;
|
||||
static uint16_t analogFreq = 1000;
|
||||
|
||||
extern void __analogWriteRange(uint32_t range) {
|
||||
if (range > 0) {
|
||||
extern void __analogWriteRange(uint32_t range)
|
||||
{
|
||||
if (range > 0)
|
||||
{
|
||||
analogScale = range;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern void __analogWriteFreq(uint32_t freq) {
|
||||
if (freq < 100) {
|
||||
extern void __analogWriteFreq(uint32_t freq)
|
||||
{
|
||||
if (freq < 100)
|
||||
{
|
||||
analogFreq = 100;
|
||||
} else if (freq > 40000) {
|
||||
}
|
||||
else if (freq > 40000)
|
||||
{
|
||||
analogFreq = 40000;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
analogFreq = freq;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern void __analogWrite(uint8_t pin, int val) {
|
||||
if (pin > 16) {
|
||||
extern void __analogWrite(uint8_t pin, int val)
|
||||
{
|
||||
if (pin > 16)
|
||||
{
|
||||
return;
|
||||
}
|
||||
uint32_t analogPeriod = 1000000L / analogFreq;
|
||||
if (val < 0) {
|
||||
if (val < 0)
|
||||
{
|
||||
val = 0;
|
||||
} else if (val > analogScale) {
|
||||
}
|
||||
else if (val > analogScale)
|
||||
{
|
||||
val = analogScale;
|
||||
}
|
||||
|
||||
@ -61,21 +74,27 @@ extern void __analogWrite(uint8_t pin, int val) {
|
||||
uint32_t high = (analogPeriod * val) / analogScale;
|
||||
uint32_t low = analogPeriod - high;
|
||||
pinMode(pin, OUTPUT);
|
||||
if (low == 0) {
|
||||
if (low == 0)
|
||||
{
|
||||
stopWaveform(pin);
|
||||
digitalWrite(pin, HIGH);
|
||||
} else if (high == 0) {
|
||||
}
|
||||
else if (high == 0)
|
||||
{
|
||||
stopWaveform(pin);
|
||||
digitalWrite(pin, LOW);
|
||||
} else {
|
||||
if (startWaveform(pin, high, low, 0)) {
|
||||
}
|
||||
else
|
||||
{
|
||||
if (startWaveform(pin, high, low, 0))
|
||||
{
|
||||
analogMap |= (1 << pin);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern void analogWrite(uint8_t pin, int val) __attribute__((weak, alias("__analogWrite")));
|
||||
extern void analogWriteFreq(uint32_t freq) __attribute__((weak, alias("__analogWriteFreq")));
|
||||
extern void analogWriteRange(uint32_t range) __attribute__((weak, alias("__analogWriteRange")));
|
||||
extern void analogWrite(uint8_t pin, int val) __attribute__((weak, alias("__analogWrite")));
|
||||
extern void analogWriteFreq(uint32_t freq) __attribute__((weak, alias("__analogWriteFreq")));
|
||||
extern void analogWriteRange(uint32_t range) __attribute__((weak, alias("__analogWriteRange")));
|
||||
|
||||
};
|
||||
|
@ -23,38 +23,50 @@
|
||||
Boston, MA 02111-1307 USA
|
||||
|
||||
$Id: wiring.c 248 2007-02-03 15:36:30Z mellis $
|
||||
*/
|
||||
*/
|
||||
|
||||
#include "wiring_private.h"
|
||||
extern "C" {
|
||||
|
||||
uint8_t shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder) {
|
||||
uint8_t shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder)
|
||||
{
|
||||
uint8_t value = 0;
|
||||
uint8_t i;
|
||||
|
||||
for(i = 0; i < 8; ++i) {
|
||||
for (i = 0; i < 8; ++i)
|
||||
{
|
||||
digitalWrite(clockPin, HIGH);
|
||||
if(bitOrder == LSBFIRST)
|
||||
if (bitOrder == LSBFIRST)
|
||||
{
|
||||
value |= digitalRead(dataPin) << i;
|
||||
}
|
||||
else
|
||||
{
|
||||
value |= digitalRead(dataPin) << (7 - i);
|
||||
}
|
||||
digitalWrite(clockPin, LOW);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val) {
|
||||
void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
for(i = 0; i < 8; i++) {
|
||||
if(bitOrder == LSBFIRST)
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
if (bitOrder == LSBFIRST)
|
||||
{
|
||||
digitalWrite(dataPin, !!(val & (1 << i)));
|
||||
}
|
||||
else
|
||||
{
|
||||
digitalWrite(dataPin, !!(val & (1 << (7 - i))));
|
||||
}
|
||||
|
||||
digitalWrite(clockPin, HIGH);
|
||||
digitalWrite(clockPin, LOW);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -15,11 +15,11 @@ extern bool timeshift64_is_set;
|
||||
|
||||
void esp_yield();
|
||||
void esp_schedule();
|
||||
void tune_timeshift64 (uint64_t now_us);
|
||||
void settimeofday_cb (void (*cb)(void));
|
||||
void disable_extra4k_at_link_time (void) __attribute__((noinline));
|
||||
void tune_timeshift64(uint64_t now_us);
|
||||
void settimeofday_cb(void (*cb)(void));
|
||||
void disable_extra4k_at_link_time(void) __attribute__((noinline));
|
||||
|
||||
uint32_t sqrt32 (uint32_t n);
|
||||
uint32_t sqrt32(uint32_t n);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -16,16 +16,19 @@
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
*/
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "debug.h"
|
||||
|
||||
void ICACHE_RAM_ATTR hexdump(const void *mem, uint32_t len, uint8_t cols) {
|
||||
void ICACHE_RAM_ATTR hexdump(const void *mem, uint32_t len, uint8_t cols)
|
||||
{
|
||||
const uint8_t* src = (const uint8_t*) mem;
|
||||
os_printf("\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)", (ptrdiff_t)src, len, len);
|
||||
for(uint32_t i = 0; i < len; i++) {
|
||||
if(i % cols == 0) {
|
||||
for (uint32_t i = 0; i < len; i++)
|
||||
{
|
||||
if (i % cols == 0)
|
||||
{
|
||||
os_printf("\n[0x%08X] 0x%08X: ", (ptrdiff_t)src, i);
|
||||
yield();
|
||||
}
|
||||
|
@ -9,7 +9,8 @@ extern "C" {
|
||||
|
||||
#define RTC_MEM ((volatile uint32_t*)0x60001200)
|
||||
|
||||
enum action_t {
|
||||
enum action_t
|
||||
{
|
||||
ACTION_COPY_RAW = 0x00000001,
|
||||
ACTION_LOAD_APP = 0xffffffff
|
||||
};
|
||||
@ -17,7 +18,8 @@ enum action_t {
|
||||
#define EBOOT_MAGIC 0xeb001000
|
||||
#define EBOOT_MAGIC_MASK 0xfffff000
|
||||
|
||||
struct eboot_command {
|
||||
struct eboot_command
|
||||
{
|
||||
uint32_t magic;
|
||||
enum action_t action;
|
||||
uint32_t args[29];
|
||||
|
@ -27,9 +27,9 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
/* Definitions are placed in eboot. Include them here rather than duplicate them.
|
||||
* Also, prefer to have eboot standalone as much as possible and have the core depend on it
|
||||
* rather than have eboot depend on the core.
|
||||
*/
|
||||
Also, prefer to have eboot standalone as much as possible and have the core depend on it
|
||||
rather than have eboot depend on the core.
|
||||
*/
|
||||
#include <../../bootloaders/eboot/flash.h>
|
||||
|
||||
|
||||
|
@ -26,19 +26,19 @@
|
||||
same stub can be used for gdb_present. */
|
||||
extern "C" {
|
||||
|
||||
static bool ICACHE_RAM_ATTR __gdb_no_op()
|
||||
{
|
||||
static bool ICACHE_RAM_ATTR __gdb_no_op()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void gdb_init(void) __attribute__ ((weak, alias("__gdb_no_op")));
|
||||
void gdb_do_break(void) __attribute__ ((weak, alias("__gdb_no_op")));
|
||||
bool gdb_present(void) __attribute__ ((weak, alias("__gdb_no_op")));
|
||||
bool gdbstub_has_putc1_control(void) __attribute__ ((weak, alias("__gdb_no_op")));
|
||||
void gdbstub_set_putc1_callback(void (*func)(char)) __attribute__ ((weak, alias("__gdb_no_op")));
|
||||
bool gdbstub_has_uart_isr_control(void) __attribute__ ((weak, alias("__gdb_no_op")));
|
||||
void gdbstub_set_uart_isr_callback(void (*func)(void*, uint8_t), void* arg) __attribute__ ((weak, alias("__gdb_no_op")));
|
||||
void gdbstub_write_char(char c) __attribute__ ((weak, alias("__gdb_no_op")));
|
||||
void gdbstub_write(const char* buf, size_t size) __attribute__ ((weak, alias("__gdb_no_op")));
|
||||
void gdb_init(void) __attribute__((weak, alias("__gdb_no_op")));
|
||||
void gdb_do_break(void) __attribute__((weak, alias("__gdb_no_op")));
|
||||
bool gdb_present(void) __attribute__((weak, alias("__gdb_no_op")));
|
||||
bool gdbstub_has_putc1_control(void) __attribute__((weak, alias("__gdb_no_op")));
|
||||
void gdbstub_set_putc1_callback(void (*func)(char)) __attribute__((weak, alias("__gdb_no_op")));
|
||||
bool gdbstub_has_uart_isr_control(void) __attribute__((weak, alias("__gdb_no_op")));
|
||||
void gdbstub_set_uart_isr_callback(void (*func)(void*, uint8_t), void* arg) __attribute__((weak, alias("__gdb_no_op")));
|
||||
void gdbstub_write_char(char c) __attribute__((weak, alias("__gdb_no_op")));
|
||||
void gdbstub_write(const char* buf, size_t size) __attribute__((weak, alias("__gdb_no_op")));
|
||||
|
||||
};
|
||||
|
@ -24,97 +24,97 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Initialize GDB stub, if present
|
||||
*
|
||||
* By default, this function is a no-op. When GDBStub library is linked,
|
||||
* this function is overriden and does necessary initialization of that library.
|
||||
* Called early at startup.
|
||||
*/
|
||||
@brief Initialize GDB stub, if present
|
||||
|
||||
By default, this function is a no-op. When GDBStub library is linked,
|
||||
this function is overriden and does necessary initialization of that library.
|
||||
Called early at startup.
|
||||
*/
|
||||
void gdb_init(void);
|
||||
|
||||
/**
|
||||
* @brief Break into GDB, if present
|
||||
*
|
||||
* By default, this function is a no-op. When GDBStub library is linked,
|
||||
* this function is overriden and triggers entry into the debugger, which
|
||||
* looks like a breakpoint hit.
|
||||
*/
|
||||
@brief Break into GDB, if present
|
||||
|
||||
By default, this function is a no-op. When GDBStub library is linked,
|
||||
this function is overriden and triggers entry into the debugger, which
|
||||
looks like a breakpoint hit.
|
||||
*/
|
||||
void gdb_do_break(void);
|
||||
|
||||
/**
|
||||
* @brief Check if GDB stub is present.
|
||||
*
|
||||
* By default, this function returns false. When GDBStub library is linked,
|
||||
* this function is overriden and returns true. Can be used to check whether
|
||||
* GDB is used.
|
||||
*
|
||||
* @return true if GDB stub is present
|
||||
*/
|
||||
@brief Check if GDB stub is present.
|
||||
|
||||
By default, this function returns false. When GDBStub library is linked,
|
||||
this function is overriden and returns true. Can be used to check whether
|
||||
GDB is used.
|
||||
|
||||
@return true if GDB stub is present
|
||||
*/
|
||||
bool gdb_present(void);
|
||||
|
||||
// If gdbstub has these set true, then we will disable our own
|
||||
// usage of them, but use gdbstub's callbacks for them instead
|
||||
/**
|
||||
* @brief Check if GDB is installing a putc1 callback.
|
||||
*
|
||||
* By default, this function returns false. When GDBStub library is linked,
|
||||
* this function is overriden and returns true.
|
||||
*
|
||||
* @return true if GDB is installing a putc1 callback
|
||||
*/
|
||||
@brief Check if GDB is installing a putc1 callback.
|
||||
|
||||
By default, this function returns false. When GDBStub library is linked,
|
||||
this function is overriden and returns true.
|
||||
|
||||
@return true if GDB is installing a putc1 callback
|
||||
*/
|
||||
bool gdbstub_has_putc1_control(void);
|
||||
|
||||
/**
|
||||
* @brief Register a putc1 callback with GDB.
|
||||
* @param func function GDB will proxy putc1 data to
|
||||
*
|
||||
* By default, this function is a no-op. When GDBStub library is linked,
|
||||
* this function is overriden and sets GDB stub's secondary putc1 callback to
|
||||
* func. When GDB stub is linked, but a GDB session is not current attached,
|
||||
* then GDB stub will pass putc1 chars directly to this function.
|
||||
*/
|
||||
@brief Register a putc1 callback with GDB.
|
||||
@param func function GDB will proxy putc1 data to
|
||||
|
||||
By default, this function is a no-op. When GDBStub library is linked,
|
||||
this function is overriden and sets GDB stub's secondary putc1 callback to
|
||||
func. When GDB stub is linked, but a GDB session is not current attached,
|
||||
then GDB stub will pass putc1 chars directly to this function.
|
||||
*/
|
||||
void gdbstub_set_putc1_callback(void (*func)(char));
|
||||
|
||||
/**
|
||||
* @brief Check if GDB is installing a uart0 isr callback.
|
||||
*
|
||||
* By default, this function returns false. When GDBStub library is linked,
|
||||
* this function is overriden and returns true.
|
||||
*
|
||||
* @return true if GDB is installing a uart0 isr callback
|
||||
*/
|
||||
@brief Check if GDB is installing a uart0 isr callback.
|
||||
|
||||
By default, this function returns false. When GDBStub library is linked,
|
||||
this function is overriden and returns true.
|
||||
|
||||
@return true if GDB is installing a uart0 isr callback
|
||||
*/
|
||||
bool gdbstub_has_uart_isr_control(void);
|
||||
|
||||
/**
|
||||
* @brief Register a uart0 isr callback with GDB.
|
||||
* @param func function GDB will proxy uart0 isr data to
|
||||
*
|
||||
* By default, this function is a no-op. When GDBStub library is linked,
|
||||
* this function is overriden and sets GDB stub's secondary uart0 isr callback
|
||||
* to func. When GDB stub is linked, but a GDB session is not current attached,
|
||||
* then GDB stub will pass uart0 isr data back to this function.
|
||||
*/
|
||||
@brief Register a uart0 isr callback with GDB.
|
||||
@param func function GDB will proxy uart0 isr data to
|
||||
|
||||
By default, this function is a no-op. When GDBStub library is linked,
|
||||
this function is overriden and sets GDB stub's secondary uart0 isr callback
|
||||
to func. When GDB stub is linked, but a GDB session is not current attached,
|
||||
then GDB stub will pass uart0 isr data back to this function.
|
||||
*/
|
||||
void gdbstub_set_uart_isr_callback(void (*func)(void*, uint8_t), void* arg);
|
||||
|
||||
/**
|
||||
* @brief Write a character for output to a GDB session on uart0.
|
||||
* @param c character to write
|
||||
*
|
||||
* By default, this function is a no-op. When GDBStub library is linked,
|
||||
* this function is overriden and writes a char to either the GDB session on
|
||||
* uart0 or directly to uart0 if not GDB session is attached.
|
||||
*/
|
||||
@brief Write a character for output to a GDB session on uart0.
|
||||
@param c character to write
|
||||
|
||||
By default, this function is a no-op. When GDBStub library is linked,
|
||||
this function is overriden and writes a char to either the GDB session on
|
||||
uart0 or directly to uart0 if not GDB session is attached.
|
||||
*/
|
||||
void gdbstub_write_char(char c);
|
||||
|
||||
/**
|
||||
* @brief Write a char buffer for output to a GDB session on uart0.
|
||||
* @param buf buffer of data to write
|
||||
* @param size length of buffer
|
||||
*
|
||||
* By default, this function is a no-op. When GDBStub library is linked,
|
||||
* this function is overriden and writes a buffer to either the GDB session on
|
||||
* uart0 or directly to uart0 if not GDB session is attached.
|
||||
*/
|
||||
@brief Write a char buffer for output to a GDB session on uart0.
|
||||
@param buf buffer of data to write
|
||||
@param size length of buffer
|
||||
|
||||
By default, this function is a no-op. When GDBStub library is linked,
|
||||
this function is overriden and writes a buffer to either the GDB session on
|
||||
uart0 or directly to uart0 if not GDB session is attached.
|
||||
*/
|
||||
void gdbstub_write(const char* buf, size_t size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -1,7 +1,7 @@
|
||||
/* heap.c - overrides of SDK heap handling functions
|
||||
* Copyright (c) 2016 Ivan Grokhotkov. All rights reserved.
|
||||
* This file is distributed under MIT license.
|
||||
*/
|
||||
Copyright (c) 2016 Ivan Grokhotkov. All rights reserved.
|
||||
This file is distributed under MIT license.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "umm_malloc/umm_malloc.h"
|
||||
@ -10,186 +10,201 @@
|
||||
|
||||
extern "C" {
|
||||
|
||||
// Debugging helper, last allocation which returned NULL
|
||||
void *umm_last_fail_alloc_addr = NULL;
|
||||
int umm_last_fail_alloc_size = 0;
|
||||
// Debugging helper, last allocation which returned NULL
|
||||
void *umm_last_fail_alloc_addr = NULL;
|
||||
int umm_last_fail_alloc_size = 0;
|
||||
|
||||
void* _malloc_r(struct _reent* unused, size_t size)
|
||||
{
|
||||
void* _malloc_r(struct _reent* unused, size_t size)
|
||||
{
|
||||
(void) unused;
|
||||
void *ret = malloc(size);
|
||||
if (0 != size && 0 == ret) {
|
||||
if (0 != size && 0 == ret)
|
||||
{
|
||||
umm_last_fail_alloc_addr = __builtin_return_address(0);
|
||||
umm_last_fail_alloc_size = size;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
void _free_r(struct _reent* unused, void* ptr)
|
||||
{
|
||||
void _free_r(struct _reent* unused, void* ptr)
|
||||
{
|
||||
(void) unused;
|
||||
return free(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
void* _realloc_r(struct _reent* unused, void* ptr, size_t size)
|
||||
{
|
||||
void* _realloc_r(struct _reent* unused, void* ptr, size_t size)
|
||||
{
|
||||
(void) unused;
|
||||
void *ret = realloc(ptr, size);
|
||||
if (0 != size && 0 == ret) {
|
||||
if (0 != size && 0 == ret)
|
||||
{
|
||||
umm_last_fail_alloc_addr = __builtin_return_address(0);
|
||||
umm_last_fail_alloc_size = size;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
void* _calloc_r(struct _reent* unused, size_t count, size_t size)
|
||||
{
|
||||
void* _calloc_r(struct _reent* unused, size_t count, size_t size)
|
||||
{
|
||||
(void) unused;
|
||||
void *ret = calloc(count, size);
|
||||
if (0 != (count * size) && 0 == ret) {
|
||||
if (0 != (count * size) && 0 == ret)
|
||||
{
|
||||
umm_last_fail_alloc_addr = __builtin_return_address(0);
|
||||
umm_last_fail_alloc_size = count * size;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
void ICACHE_RAM_ATTR vPortFree(void *ptr, const char* file, int line)
|
||||
{
|
||||
void ICACHE_RAM_ATTR vPortFree(void *ptr, const char* file, int line)
|
||||
{
|
||||
(void) file;
|
||||
(void) line;
|
||||
free(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ESP_OOM
|
||||
|
||||
void* ICACHE_RAM_ATTR pvPortMalloc(size_t size, const char* file, int line)
|
||||
{
|
||||
void* ICACHE_RAM_ATTR pvPortMalloc(size_t size, const char* file, int line)
|
||||
{
|
||||
return malloc_loc(size, file, line);
|
||||
}
|
||||
}
|
||||
|
||||
void* ICACHE_RAM_ATTR pvPortCalloc(size_t count, size_t size, const char* file, int line)
|
||||
{
|
||||
void* ICACHE_RAM_ATTR pvPortCalloc(size_t count, size_t size, const char* file, int line)
|
||||
{
|
||||
return calloc_loc(count, size, file, line);
|
||||
}
|
||||
}
|
||||
|
||||
void* ICACHE_RAM_ATTR pvPortRealloc(void *ptr, size_t size, const char* file, int line)
|
||||
{
|
||||
void* ICACHE_RAM_ATTR pvPortRealloc(void *ptr, size_t size, const char* file, int line)
|
||||
{
|
||||
return realloc_loc(ptr, size, file, line);
|
||||
}
|
||||
}
|
||||
|
||||
void* ICACHE_RAM_ATTR pvPortZalloc(size_t size, const char* file, int line)
|
||||
{
|
||||
void* ICACHE_RAM_ATTR pvPortZalloc(size_t size, const char* file, int line)
|
||||
{
|
||||
return calloc_loc(1, size, file, line);
|
||||
}
|
||||
}
|
||||
|
||||
#undef malloc
|
||||
#undef calloc
|
||||
#undef realloc
|
||||
|
||||
static const char oom_fmt[] PROGMEM STORE_ATTR = ":oom(%d)@?\n";
|
||||
static const char oom_fmt_1[] PROGMEM STORE_ATTR = ":oom(%d)@";
|
||||
static const char oom_fmt_2[] PROGMEM STORE_ATTR = ":%d\n";
|
||||
static const char oom_fmt[] PROGMEM STORE_ATTR = ":oom(%d)@?\n";
|
||||
static const char oom_fmt_1[] PROGMEM STORE_ATTR = ":oom(%d)@";
|
||||
static const char oom_fmt_2[] PROGMEM STORE_ATTR = ":%d\n";
|
||||
|
||||
void* malloc (size_t s)
|
||||
{
|
||||
void* malloc(size_t s)
|
||||
{
|
||||
void* ret = umm_malloc(s);
|
||||
if (!ret)
|
||||
{
|
||||
os_printf(oom_fmt, (int)s);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
void* calloc (size_t n, size_t s)
|
||||
{
|
||||
void* calloc(size_t n, size_t s)
|
||||
{
|
||||
void* ret = umm_calloc(n, s);
|
||||
if (!ret)
|
||||
{
|
||||
os_printf(oom_fmt, (int)s);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
void* realloc (void* p, size_t s)
|
||||
{
|
||||
void* realloc(void* p, size_t s)
|
||||
{
|
||||
void* ret = umm_realloc(p, s);
|
||||
if (!ret)
|
||||
{
|
||||
os_printf(oom_fmt, (int)s);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
void print_loc (size_t s, const char* file, int line)
|
||||
{
|
||||
void print_loc(size_t s, const char* file, int line)
|
||||
{
|
||||
os_printf(oom_fmt_1, (int)s);
|
||||
os_printf(file);
|
||||
os_printf(oom_fmt_2, line);
|
||||
}
|
||||
}
|
||||
|
||||
void* malloc_loc (size_t s, const char* file, int line)
|
||||
{
|
||||
void* malloc_loc(size_t s, const char* file, int line)
|
||||
{
|
||||
void* ret = umm_malloc(s);
|
||||
if (!ret)
|
||||
{
|
||||
print_loc(s, file, line);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
void* calloc_loc (size_t n, size_t s, const char* file, int line)
|
||||
{
|
||||
void* calloc_loc(size_t n, size_t s, const char* file, int line)
|
||||
{
|
||||
void* ret = umm_calloc(n, s);
|
||||
if (!ret)
|
||||
{
|
||||
print_loc(s, file, line);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
void* realloc_loc (void* p, size_t s, const char* file, int line)
|
||||
{
|
||||
void* realloc_loc(void* p, size_t s, const char* file, int line)
|
||||
{
|
||||
void* ret = umm_realloc(p, s);
|
||||
if (!ret)
|
||||
{
|
||||
print_loc(s, file, line);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void* ICACHE_RAM_ATTR pvPortMalloc(size_t size, const char* file, int line)
|
||||
{
|
||||
void* ICACHE_RAM_ATTR pvPortMalloc(size_t size, const char* file, int line)
|
||||
{
|
||||
(void) file;
|
||||
(void) line;
|
||||
return malloc(size);
|
||||
}
|
||||
}
|
||||
|
||||
void* ICACHE_RAM_ATTR pvPortCalloc(size_t count, size_t size, const char* file, int line)
|
||||
{
|
||||
void* ICACHE_RAM_ATTR pvPortCalloc(size_t count, size_t size, const char* file, int line)
|
||||
{
|
||||
(void) file;
|
||||
(void) line;
|
||||
return calloc(count, size);
|
||||
}
|
||||
}
|
||||
|
||||
void* ICACHE_RAM_ATTR pvPortRealloc(void *ptr, size_t size, const char* file, int line)
|
||||
{
|
||||
void* ICACHE_RAM_ATTR pvPortRealloc(void *ptr, size_t size, const char* file, int line)
|
||||
{
|
||||
(void) file;
|
||||
(void) line;
|
||||
return realloc(ptr, size);
|
||||
}
|
||||
}
|
||||
|
||||
void* ICACHE_RAM_ATTR pvPortZalloc(size_t size, const char* file, int line)
|
||||
{
|
||||
void* ICACHE_RAM_ATTR pvPortZalloc(size_t size, const char* file, int line)
|
||||
{
|
||||
(void) file;
|
||||
(void) line;
|
||||
return calloc(1, size);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // !defined(DEBUG_ESP_OOM)
|
||||
|
||||
size_t xPortGetFreeHeapSize(void)
|
||||
{
|
||||
size_t xPortGetFreeHeapSize(void)
|
||||
{
|
||||
return umm_free_heap_size();
|
||||
}
|
||||
}
|
||||
|
||||
size_t ICACHE_RAM_ATTR xPortWantedSizeAlign(size_t size)
|
||||
{
|
||||
size_t ICACHE_RAM_ATTR xPortWantedSizeAlign(size_t size)
|
||||
{
|
||||
return (size + 3) & ~((size_t) 3);
|
||||
}
|
||||
}
|
||||
|
||||
void system_show_malloc(void)
|
||||
{
|
||||
void system_show_malloc(void)
|
||||
{
|
||||
umm_info(NULL, 1);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -22,18 +22,18 @@
|
||||
#define I2S_h
|
||||
|
||||
/*
|
||||
How does this work? Basically, to get sound, you need to:
|
||||
- Connect an I2S codec to the I2S pins on the ESP.
|
||||
- Start up a thread that's going to do the sound output
|
||||
- Call i2s_begin()
|
||||
- Call i2s_set_rate() with the sample rate you want.
|
||||
- Generate sound and call i2s_write_sample() with 32-bit samples.
|
||||
The 32bit samples basically are 2 16-bit signed values (the analog values for
|
||||
the left and right channel) concatenated as (Rout<<16)+Lout
|
||||
How does this work? Basically, to get sound, you need to:
|
||||
- Connect an I2S codec to the I2S pins on the ESP.
|
||||
- Start up a thread that's going to do the sound output
|
||||
- Call i2s_begin()
|
||||
- Call i2s_set_rate() with the sample rate you want.
|
||||
- Generate sound and call i2s_write_sample() with 32-bit samples.
|
||||
The 32bit samples basically are 2 16-bit signed values (the analog values for
|
||||
the left and right channel) concatenated as (Rout<<16)+Lout
|
||||
|
||||
i2s_write_sample will block when you're sending data too quickly, so you can just
|
||||
generate and push data as fast as you can and i2s_write_sample will regulate the
|
||||
speed.
|
||||
i2s_write_sample will block when you're sending data too quickly, so you can just
|
||||
generate and push data as fast as you can and i2s_write_sample will regulate the
|
||||
speed.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
@ -56,8 +56,8 @@ bool i2s_rx_is_full();
|
||||
bool i2s_rx_is_empty();
|
||||
uint16_t i2s_available();// returns the number of samples than can be written before blocking
|
||||
uint16_t i2s_rx_available();// returns the number of samples than can be written before blocking
|
||||
void i2s_set_callback(void (*callback) (void));
|
||||
void i2s_rx_set_callback(void (*callback) (void));
|
||||
void i2s_set_callback(void (*callback)(void));
|
||||
void i2s_rx_set_callback(void (*callback)(void));
|
||||
|
||||
// writes a buffer of frames into the DMA memory, returns the amount of frames written
|
||||
// A frame is just a int16_t for mono, for stereo a frame is two int16_t, one for each channel.
|
||||
|
@ -21,17 +21,21 @@ extern "C" {
|
||||
//}
|
||||
//
|
||||
|
||||
class InterruptLock {
|
||||
class InterruptLock
|
||||
{
|
||||
public:
|
||||
InterruptLock() {
|
||||
InterruptLock()
|
||||
{
|
||||
_state = xt_rsil(15);
|
||||
}
|
||||
|
||||
~InterruptLock() {
|
||||
~InterruptLock()
|
||||
{
|
||||
xt_wsr_ps(_state);
|
||||
}
|
||||
|
||||
uint32_t savedInterruptLevel() const {
|
||||
uint32_t savedInterruptLevel() const
|
||||
{
|
||||
return _state & 0x0f;
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
/*
|
||||
cdecoder.c - c source to a base64 decoding algorithm implementation
|
||||
cdecoder.c - c source to a base64 decoding algorithm implementation
|
||||
|
||||
This is part of the libb64 project, and has been placed in the public domain.
|
||||
For details, see http://sourceforge.net/projects/libb64
|
||||
This is part of the libb64 project, and has been placed in the public domain.
|
||||
For details, see http://sourceforge.net/projects/libb64
|
||||
*/
|
||||
|
||||
#include <pgmspace.h>
|
||||
@ -11,31 +11,41 @@ For details, see http://sourceforge.net/projects/libb64
|
||||
|
||||
extern "C" {
|
||||
|
||||
static int base64_decode_value_signed(int8_t value_in){
|
||||
static const int8_t decoding[] PROGMEM = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51};
|
||||
static int base64_decode_value_signed(int8_t value_in)
|
||||
{
|
||||
static const int8_t decoding[] PROGMEM = {62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51};
|
||||
static const int8_t decoding_size = sizeof(decoding);
|
||||
value_in -= 43;
|
||||
if (value_in < 0 || value_in > decoding_size) return -1;
|
||||
return pgm_read_byte( &decoding[(int)value_in] );
|
||||
}
|
||||
if (value_in < 0 || value_in > decoding_size)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return pgm_read_byte(&decoding[(int)value_in]);
|
||||
}
|
||||
|
||||
void base64_init_decodestate(base64_decodestate* state_in){
|
||||
void base64_init_decodestate(base64_decodestate* state_in)
|
||||
{
|
||||
state_in->step = step_a;
|
||||
state_in->plainchar = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int base64_decode_block_signed(const int8_t* code_in, const int length_in, int8_t* plaintext_out, base64_decodestate* state_in){
|
||||
static int base64_decode_block_signed(const int8_t* code_in, const int length_in, int8_t* plaintext_out, base64_decodestate* state_in)
|
||||
{
|
||||
const int8_t* codechar = code_in;
|
||||
int8_t* plainchar = plaintext_out;
|
||||
int8_t fragment;
|
||||
|
||||
*plainchar = state_in->plainchar;
|
||||
|
||||
switch (state_in->step){
|
||||
while (1){
|
||||
switch (state_in->step)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
case step_a:
|
||||
do {
|
||||
if (codechar == code_in+length_in){
|
||||
do
|
||||
{
|
||||
if (codechar == code_in + length_in)
|
||||
{
|
||||
state_in->step = step_a;
|
||||
state_in->plainchar = *plainchar;
|
||||
return plainchar - plaintext_out;
|
||||
@ -44,8 +54,10 @@ static int base64_decode_block_signed(const int8_t* code_in, const int length_in
|
||||
} while (fragment < 0);
|
||||
*plainchar = (fragment & 0x03f) << 2;
|
||||
case step_b:
|
||||
do {
|
||||
if (codechar == code_in+length_in){
|
||||
do
|
||||
{
|
||||
if (codechar == code_in + length_in)
|
||||
{
|
||||
state_in->step = step_b;
|
||||
state_in->plainchar = *plainchar;
|
||||
return plainchar - plaintext_out;
|
||||
@ -55,8 +67,10 @@ static int base64_decode_block_signed(const int8_t* code_in, const int length_in
|
||||
*plainchar++ |= (fragment & 0x030) >> 4;
|
||||
*plainchar = (fragment & 0x00f) << 4;
|
||||
case step_c:
|
||||
do {
|
||||
if (codechar == code_in+length_in){
|
||||
do
|
||||
{
|
||||
if (codechar == code_in + length_in)
|
||||
{
|
||||
state_in->step = step_c;
|
||||
state_in->plainchar = *plainchar;
|
||||
return plainchar - plaintext_out;
|
||||
@ -66,8 +80,10 @@ static int base64_decode_block_signed(const int8_t* code_in, const int length_in
|
||||
*plainchar++ |= (fragment & 0x03c) >> 2;
|
||||
*plainchar = (fragment & 0x003) << 6;
|
||||
case step_d:
|
||||
do {
|
||||
if (codechar == code_in+length_in){
|
||||
do
|
||||
{
|
||||
if (codechar == code_in + length_in)
|
||||
{
|
||||
state_in->step = step_d;
|
||||
state_in->plainchar = *plainchar;
|
||||
return plainchar - plaintext_out;
|
||||
@ -79,26 +95,33 @@ static int base64_decode_block_signed(const int8_t* code_in, const int length_in
|
||||
}
|
||||
/* control should not reach here */
|
||||
return plainchar - plaintext_out;
|
||||
}
|
||||
}
|
||||
|
||||
static int base64_decode_chars_signed(const int8_t* code_in, const int length_in, int8_t* plaintext_out){
|
||||
static int base64_decode_chars_signed(const int8_t* code_in, const int length_in, int8_t* plaintext_out)
|
||||
{
|
||||
base64_decodestate _state;
|
||||
base64_init_decodestate(&_state);
|
||||
int len = base64_decode_block_signed(code_in, length_in, plaintext_out, &_state);
|
||||
if(len > 0) plaintext_out[len] = 0;
|
||||
if (len > 0)
|
||||
{
|
||||
plaintext_out[len] = 0;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
}
|
||||
|
||||
int base64_decode_value(char value_in){
|
||||
int base64_decode_value(char value_in)
|
||||
{
|
||||
return base64_decode_value_signed(*((int8_t *) &value_in));
|
||||
}
|
||||
}
|
||||
|
||||
int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in){
|
||||
int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in)
|
||||
{
|
||||
return base64_decode_block_signed((int8_t *) code_in, length_in, (int8_t *) plaintext_out, state_in);
|
||||
}
|
||||
}
|
||||
|
||||
int base64_decode_chars(const char* code_in, const int length_in, char* plaintext_out){
|
||||
int base64_decode_chars(const char* code_in, const int length_in, char* plaintext_out)
|
||||
{
|
||||
return base64_decode_chars_signed((int8_t *) code_in, length_in, (int8_t *) plaintext_out);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -1,8 +1,8 @@
|
||||
/*
|
||||
cdecode.h - c header for a base64 decoding algorithm
|
||||
cdecode.h - c header for a base64 decoding algorithm
|
||||
|
||||
This is part of the libb64 project, and has been placed in the public domain.
|
||||
For details, see http://sourceforge.net/projects/libb64
|
||||
This is part of the libb64 project, and has been placed in the public domain.
|
||||
For details, see http://sourceforge.net/projects/libb64
|
||||
*/
|
||||
|
||||
#ifndef BASE64_CDECODE_H
|
||||
@ -14,11 +14,13 @@ For details, see http://sourceforge.net/projects/libb64
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
typedef enum
|
||||
{
|
||||
step_a, step_b, step_c, step_d
|
||||
} base64_decodestep;
|
||||
|
||||
typedef struct {
|
||||
typedef struct
|
||||
{
|
||||
base64_decodestep step;
|
||||
char plainchar;
|
||||
} base64_decodestate;
|
||||
|
@ -1,8 +1,8 @@
|
||||
/*
|
||||
cencoder.c - c source to a base64 encoding algorithm implementation
|
||||
cencoder.c - c source to a base64 encoding algorithm implementation
|
||||
|
||||
This is part of the libb64 project, and has been placed in the public domain.
|
||||
For details, see http://sourceforge.net/projects/libb64
|
||||
This is part of the libb64 project, and has been placed in the public domain.
|
||||
For details, see http://sourceforge.net/projects/libb64
|
||||
*/
|
||||
|
||||
#include <pgmspace.h>
|
||||
@ -10,26 +10,33 @@ For details, see http://sourceforge.net/projects/libb64
|
||||
|
||||
extern "C" {
|
||||
|
||||
void base64_init_encodestate(base64_encodestate* state_in){
|
||||
void base64_init_encodestate(base64_encodestate* state_in)
|
||||
{
|
||||
state_in->step = step_A;
|
||||
state_in->result = 0;
|
||||
state_in->stepcount = 0;
|
||||
state_in->stepsnewline = BASE64_CHARS_PER_LINE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void base64_init_encodestate_nonewlines(base64_encodestate* state_in){
|
||||
void base64_init_encodestate_nonewlines(base64_encodestate* state_in)
|
||||
{
|
||||
base64_init_encodestate(state_in);
|
||||
state_in->stepsnewline = -1;
|
||||
}
|
||||
}
|
||||
|
||||
char base64_encode_value(char value_in){
|
||||
char base64_encode_value(char value_in)
|
||||
{
|
||||
static const char encoding[] PROGMEM = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
if (value_in > 63) return '=';
|
||||
return pgm_read_byte( &encoding[(int)value_in] );
|
||||
}
|
||||
if (value_in > 63)
|
||||
{
|
||||
return '=';
|
||||
}
|
||||
return pgm_read_byte(&encoding[(int)value_in]);
|
||||
}
|
||||
|
||||
int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in){
|
||||
int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in)
|
||||
{
|
||||
const char* plainchar = plaintext_in;
|
||||
const char* const plaintextend = plaintext_in + length_in;
|
||||
char* codechar = code_out;
|
||||
@ -38,10 +45,13 @@ int base64_encode_block(const char* plaintext_in, int length_in, char* code_out,
|
||||
|
||||
result = state_in->result;
|
||||
|
||||
switch (state_in->step){
|
||||
while (1){
|
||||
switch (state_in->step)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
case step_A:
|
||||
if (plainchar == plaintextend){
|
||||
if (plainchar == plaintextend)
|
||||
{
|
||||
state_in->result = result;
|
||||
state_in->step = step_A;
|
||||
return codechar - code_out;
|
||||
@ -51,7 +61,8 @@ int base64_encode_block(const char* plaintext_in, int length_in, char* code_out,
|
||||
*codechar++ = base64_encode_value(result);
|
||||
result = (fragment & 0x003) << 4;
|
||||
case step_B:
|
||||
if (plainchar == plaintextend){
|
||||
if (plainchar == plaintextend)
|
||||
{
|
||||
state_in->result = result;
|
||||
state_in->step = step_B;
|
||||
return codechar - code_out;
|
||||
@ -61,7 +72,8 @@ int base64_encode_block(const char* plaintext_in, int length_in, char* code_out,
|
||||
*codechar++ = base64_encode_value(result);
|
||||
result = (fragment & 0x00f) << 2;
|
||||
case step_C:
|
||||
if (plainchar == plaintextend){
|
||||
if (plainchar == plaintextend)
|
||||
{
|
||||
state_in->result = result;
|
||||
state_in->step = step_C;
|
||||
return codechar - code_out;
|
||||
@ -73,7 +85,8 @@ int base64_encode_block(const char* plaintext_in, int length_in, char* code_out,
|
||||
*codechar++ = base64_encode_value(result);
|
||||
|
||||
++(state_in->stepcount);
|
||||
if ((state_in->stepcount == BASE64_CHARS_PER_LINE/4) && (state_in->stepsnewline > 0)){
|
||||
if ((state_in->stepcount == BASE64_CHARS_PER_LINE / 4) && (state_in->stepsnewline > 0))
|
||||
{
|
||||
*codechar++ = '\n';
|
||||
state_in->stepcount = 0;
|
||||
}
|
||||
@ -81,12 +94,14 @@ int base64_encode_block(const char* plaintext_in, int length_in, char* code_out,
|
||||
}
|
||||
/* control should not reach here */
|
||||
return codechar - code_out;
|
||||
}
|
||||
}
|
||||
|
||||
int base64_encode_blockend(char* code_out, base64_encodestate* state_in){
|
||||
int base64_encode_blockend(char* code_out, base64_encodestate* state_in)
|
||||
{
|
||||
char* codechar = code_out;
|
||||
|
||||
switch (state_in->step){
|
||||
switch (state_in->step)
|
||||
{
|
||||
case step_B:
|
||||
*codechar++ = base64_encode_value(state_in->result);
|
||||
*codechar++ = '=';
|
||||
@ -102,13 +117,14 @@ int base64_encode_blockend(char* code_out, base64_encodestate* state_in){
|
||||
*codechar = 0x00;
|
||||
|
||||
return codechar - code_out;
|
||||
}
|
||||
}
|
||||
|
||||
int base64_encode_chars(const char* plaintext_in, int length_in, char* code_out){
|
||||
int base64_encode_chars(const char* plaintext_in, int length_in, char* code_out)
|
||||
{
|
||||
base64_encodestate _state;
|
||||
base64_init_encodestate(&_state);
|
||||
int len = base64_encode_block(plaintext_in, length_in, code_out, &_state);
|
||||
return len + base64_encode_blockend((code_out + len), &_state);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -1,8 +1,8 @@
|
||||
/*
|
||||
cencode.h - c header for a base64 encoding algorithm
|
||||
cencode.h - c header for a base64 encoding algorithm
|
||||
|
||||
This is part of the libb64 project, and has been placed in the public domain.
|
||||
For details, see http://sourceforge.net/projects/libb64
|
||||
This is part of the libb64 project, and has been placed in the public domain.
|
||||
For details, see http://sourceforge.net/projects/libb64
|
||||
*/
|
||||
|
||||
#ifndef BASE64_CENCODE_H
|
||||
@ -19,11 +19,13 @@ For details, see http://sourceforge.net/projects/libb64
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
typedef enum
|
||||
{
|
||||
step_A, step_B, step_C
|
||||
} base64_encodestep;
|
||||
|
||||
typedef struct {
|
||||
typedef struct
|
||||
{
|
||||
base64_encodestep step;
|
||||
char result;
|
||||
int stepcount;
|
||||
|
@ -21,7 +21,7 @@
|
||||
|
||||
Modified 03 April 2015 by Markus Sattler
|
||||
|
||||
*/
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
@ -47,88 +47,103 @@
|
||||
|
||||
extern "C" {
|
||||
|
||||
int ICACHE_RAM_ATTR _open_r (struct _reent* unused, const char *ptr, int mode) {
|
||||
int ICACHE_RAM_ATTR _open_r(struct _reent* unused, const char *ptr, int mode)
|
||||
{
|
||||
(void)unused;
|
||||
(void)ptr;
|
||||
(void)mode;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int ICACHE_RAM_ATTR _close_r(struct _reent* unused, int file) {
|
||||
int ICACHE_RAM_ATTR _close_r(struct _reent* unused, int file)
|
||||
{
|
||||
(void)unused;
|
||||
(void)file;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int ICACHE_RAM_ATTR _fstat_r(struct _reent* unused, int file, struct stat *st) {
|
||||
int ICACHE_RAM_ATTR _fstat_r(struct _reent* unused, int file, struct stat *st)
|
||||
{
|
||||
(void)unused;
|
||||
(void)file;
|
||||
st->st_mode = S_IFCHR;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int ICACHE_RAM_ATTR _lseek_r(struct _reent* unused, int file, int ptr, int dir) {
|
||||
int ICACHE_RAM_ATTR _lseek_r(struct _reent* unused, int file, int ptr, int dir)
|
||||
{
|
||||
(void)unused;
|
||||
(void)file;
|
||||
(void)ptr;
|
||||
(void)dir;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int ICACHE_RAM_ATTR _read_r(struct _reent* unused, int file, char *ptr, int len) {
|
||||
int ICACHE_RAM_ATTR _read_r(struct _reent* unused, int file, char *ptr, int len)
|
||||
{
|
||||
(void)unused;
|
||||
(void)file;
|
||||
(void)ptr;
|
||||
(void)len;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int ICACHE_RAM_ATTR _write_r(struct _reent* r, int file, char *ptr, int len) {
|
||||
int ICACHE_RAM_ATTR _write_r(struct _reent* r, int file, char *ptr, int len)
|
||||
{
|
||||
(void) r;
|
||||
int pos = len;
|
||||
if (file == STDOUT_FILENO) {
|
||||
while(pos--) {
|
||||
if (file == STDOUT_FILENO)
|
||||
{
|
||||
while (pos--)
|
||||
{
|
||||
ets_putc(*ptr);
|
||||
++ptr;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
}
|
||||
|
||||
int ICACHE_RAM_ATTR _putc_r(struct _reent* r, int c, FILE* file) __attribute__((weak));
|
||||
int ICACHE_RAM_ATTR _putc_r(struct _reent* r, int c, FILE* file) __attribute__((weak));
|
||||
|
||||
int ICACHE_RAM_ATTR _putc_r(struct _reent* r, int c, FILE* file) {
|
||||
int ICACHE_RAM_ATTR _putc_r(struct _reent* r, int c, FILE* file)
|
||||
{
|
||||
(void) r;
|
||||
if (file->_file == STDOUT_FILENO) {
|
||||
if (file->_file == STDOUT_FILENO)
|
||||
{
|
||||
return ets_putc(c);
|
||||
}
|
||||
return EOF;
|
||||
}
|
||||
}
|
||||
|
||||
int ICACHE_RAM_ATTR puts(const char * str) {
|
||||
int ICACHE_RAM_ATTR puts(const char * str)
|
||||
{
|
||||
char c;
|
||||
while((c = *str) != 0) {
|
||||
while ((c = *str) != 0)
|
||||
{
|
||||
ets_putc(c);
|
||||
++str;
|
||||
}
|
||||
ets_putc('\n');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#undef putchar
|
||||
int ICACHE_RAM_ATTR putchar(int c) {
|
||||
int ICACHE_RAM_ATTR putchar(int c)
|
||||
{
|
||||
ets_putc(c);
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
void _exit(int status) {
|
||||
void _exit(int status)
|
||||
{
|
||||
(void) status;
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
int atexit(void (*func)()) {
|
||||
int atexit(void (*func)())
|
||||
{
|
||||
(void) func;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -27,15 +27,16 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
typedef struct
|
||||
{
|
||||
uint32_t state[4];
|
||||
uint32_t count[2];
|
||||
uint8_t buffer[64];
|
||||
} md5_context_t;
|
||||
|
||||
extern void MD5Init (md5_context_t *);
|
||||
extern void MD5Update (md5_context_t *, const uint8_t *, const uint16_t);
|
||||
extern void MD5Final (uint8_t [16], md5_context_t *);
|
||||
extern void MD5Init(md5_context_t *);
|
||||
extern void MD5Update(md5_context_t *, const uint8_t *, const uint16_t);
|
||||
extern void MD5Final(uint8_t [16], md5_context_t *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
|
@ -17,29 +17,29 @@
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
* Info Sigma delta module
|
||||
/*******************************************************************************
|
||||
Info Sigma delta module
|
||||
|
||||
This module controls the esp8266 internal sigma delta source
|
||||
Each pin can be connected to the sigma delta source
|
||||
The target duty and frequency can be modified via the register GPIO_SIGMA_DELTA
|
||||
This module controls the esp8266 internal sigma delta source
|
||||
Each pin can be connected to the sigma delta source
|
||||
The target duty and frequency can be modified via the register GPIO_SIGMA_DELTA
|
||||
|
||||
THE TARGET FREQUENCY IS DEFINED AS:
|
||||
THE TARGET FREQUENCY IS DEFINED AS:
|
||||
|
||||
FREQ = 80,000,000/prescaler * target /256 HZ, 0<target<128
|
||||
FREQ = 80,000,000/prescaler * (256-target) /256 HZ, 128<target<256
|
||||
target: duty cycle,range 0-255
|
||||
prescaler: is a clock divider, range 0-255
|
||||
so the target and prescale will both affect the freq.
|
||||
CPU_FREQ has no influence on the sigma delta frequency.
|
||||
FREQ = 80,000,000/prescaler * target /256 HZ, 0<target<128
|
||||
FREQ = 80,000,000/prescaler * (256-target) /256 HZ, 128<target<256
|
||||
target: duty cycle,range 0-255
|
||||
prescaler: is a clock divider, range 0-255
|
||||
so the target and prescale will both affect the freq.
|
||||
CPU_FREQ has no influence on the sigma delta frequency.
|
||||
|
||||
Usage :
|
||||
1. sigmaDeltaSetup(0,f) : activate the sigma delta source with frequency f and default duty cycle (0)
|
||||
2. sigmaDeltaAttachPin(pin), any pin 0..15, TBC if gpio16 supports sigma-delta source
|
||||
Usage :
|
||||
1. sigmaDeltaSetup(0,f) : activate the sigma delta source with frequency f and default duty cycle (0)
|
||||
2. sigmaDeltaAttachPin(pin), any pin 0..15, TBC if gpio16 supports sigma-delta source
|
||||
This will set the pin to NORMAL output mode (pinMode(pin,OUTPUT))
|
||||
3. sigmaDeltaWrite(0,dc) : set the output signal duty cycle, duty cycle = dc/256
|
||||
3. sigmaDeltaWrite(0,dc) : set the output signal duty cycle, duty cycle = dc/256
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
|
@ -1,41 +1,41 @@
|
||||
/*
|
||||
* sntp-lwip2.c - ESP8266-specific functions for SNTP and lwIP-v2
|
||||
* Copyright (c) 2015 Espressif (license is tools/sdk/lwip/src/core/sntp.c's)
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. 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.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
|
||||
*
|
||||
*
|
||||
* History:
|
||||
* This code is extracted from lwip1.4-espressif's sntp.c
|
||||
* which is a patched version of the original lwip1's sntp.
|
||||
* (check the mix-up in tools/sdk/lwip/src/core/sntp.c)
|
||||
* It is moved here as-is and cleaned for maintainability and
|
||||
* because it does not belong to lwip.
|
||||
*
|
||||
* TODOs:
|
||||
* settimeofday(): handle tv->tv_usec
|
||||
* sntp_mktm_r(): review, fix DST handling (this one is currently untouched from lwip-1.4)
|
||||
* implement adjtime()
|
||||
*/
|
||||
sntp-lwip2.c - ESP8266-specific functions for SNTP and lwIP-v2
|
||||
Copyright (c) 2015 Espressif (license is tools/sdk/lwip/src/core/sntp.c's)
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. 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.
|
||||
3. The name of the author may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
|
||||
|
||||
|
||||
History:
|
||||
This code is extracted from lwip1.4-espressif's sntp.c
|
||||
which is a patched version of the original lwip1's sntp.
|
||||
(check the mix-up in tools/sdk/lwip/src/core/sntp.c)
|
||||
It is moved here as-is and cleaned for maintainability and
|
||||
because it does not belong to lwip.
|
||||
|
||||
TODOs:
|
||||
settimeofday(): handle tv->tv_usec
|
||||
sntp_mktm_r(): review, fix DST handling (this one is currently untouched from lwip-1.4)
|
||||
implement adjtime()
|
||||
*/
|
||||
|
||||
#include <lwip/init.h>
|
||||
#include <sys/time.h>
|
||||
@ -45,28 +45,28 @@
|
||||
|
||||
extern "C" {
|
||||
|
||||
static void (*_settimeofday_cb)(void) = NULL;
|
||||
static void (*_settimeofday_cb)(void) = NULL;
|
||||
|
||||
void settimeofday_cb (void (*cb)(void))
|
||||
{
|
||||
void settimeofday_cb(void (*cb)(void))
|
||||
{
|
||||
_settimeofday_cb = cb;
|
||||
}
|
||||
}
|
||||
|
||||
#if LWIP_VERSION_MAJOR == 1
|
||||
|
||||
#include <pgmspace.h>
|
||||
|
||||
static const char stod14[] PROGMEM = "settimeofday() can't set time!\n";
|
||||
bool sntp_set_timezone(sint8 timezone);
|
||||
bool sntp_set_timezone_in_seconds(sint32 timezone)
|
||||
{
|
||||
return sntp_set_timezone((sint8)(timezone/(60*60))); //TODO: move this to the same file as sntp_set_timezone() in lwip1.4, and implement correctly over there.
|
||||
}
|
||||
static const char stod14[] PROGMEM = "settimeofday() can't set time!\n";
|
||||
bool sntp_set_timezone(sint8 timezone);
|
||||
bool sntp_set_timezone_in_seconds(sint32 timezone)
|
||||
{
|
||||
return sntp_set_timezone((sint8)(timezone / (60 * 60))); //TODO: move this to the same file as sntp_set_timezone() in lwip1.4, and implement correctly over there.
|
||||
}
|
||||
|
||||
void sntp_set_daylight(int daylight);
|
||||
void sntp_set_daylight(int daylight);
|
||||
|
||||
int settimeofday(const struct timeval* tv, const struct timezone* tz)
|
||||
{
|
||||
int settimeofday(const struct timeval* tv, const struct timezone* tz)
|
||||
{
|
||||
if (tz) /*before*/
|
||||
{
|
||||
sntp_set_timezone_in_seconds(tz->tz_minuteswest * 60);
|
||||
@ -84,7 +84,7 @@ int settimeofday(const struct timeval* tv, const struct timezone* tz)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // lwip 1.4 only
|
||||
|
||||
@ -92,12 +92,12 @@ int settimeofday(const struct timeval* tv, const struct timezone* tz)
|
||||
|
||||
#include <lwip/apps/sntp.h>
|
||||
|
||||
static uint32 realtime_stamp = 0;
|
||||
static uint16 dst = 0;
|
||||
static sint32 time_zone = 8 * (60 * 60); // espressif HQ's default timezone
|
||||
LOCAL os_timer_t sntp_timer;
|
||||
static uint32 realtime_stamp = 0;
|
||||
static uint16 dst = 0;
|
||||
static sint32 time_zone = 8 * (60 * 60); // espressif HQ's default timezone
|
||||
LOCAL os_timer_t sntp_timer;
|
||||
|
||||
/*****************************************/
|
||||
/*****************************************/
|
||||
#define SECSPERMIN 60L
|
||||
#define MINSPERHOUR 60L
|
||||
#define HOURSPERDAY 24L
|
||||
@ -115,20 +115,22 @@ LOCAL os_timer_t sntp_timer;
|
||||
|
||||
#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
|
||||
|
||||
int __tznorth;
|
||||
int __tzyear;
|
||||
char reult[100];
|
||||
static const int mon_lengths[2][12] = {
|
||||
int __tznorth;
|
||||
int __tzyear;
|
||||
char reult[100];
|
||||
static const int mon_lengths[2][12] =
|
||||
{
|
||||
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
|
||||
{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
|
||||
} ;
|
||||
} ;
|
||||
|
||||
static const int year_lengths[2] = {
|
||||
static const int year_lengths[2] =
|
||||
{
|
||||
365,
|
||||
366
|
||||
} ;
|
||||
struct tm
|
||||
{
|
||||
} ;
|
||||
struct tm
|
||||
{
|
||||
int tm_sec;
|
||||
int tm_min;
|
||||
int tm_hour;
|
||||
@ -138,11 +140,11 @@ struct tm
|
||||
int tm_wday;
|
||||
int tm_yday;
|
||||
int tm_isdst;
|
||||
};
|
||||
};
|
||||
|
||||
struct tm res_buf;
|
||||
typedef struct __tzrule_struct
|
||||
{
|
||||
struct tm res_buf;
|
||||
typedef struct __tzrule_struct
|
||||
{
|
||||
char ch;
|
||||
int m;
|
||||
int n;
|
||||
@ -150,12 +152,12 @@ typedef struct __tzrule_struct
|
||||
int s;
|
||||
time_t change;
|
||||
int offset;
|
||||
} __tzrule_type;
|
||||
} __tzrule_type;
|
||||
|
||||
__tzrule_type sntp__tzrule[2];
|
||||
struct tm *
|
||||
sntp_mktm_r(const time_t * tim_p ,struct tm *res ,int is_gmtime)
|
||||
{
|
||||
__tzrule_type sntp__tzrule[2];
|
||||
struct tm *
|
||||
sntp_mktm_r(const time_t * tim_p, struct tm *res, int is_gmtime)
|
||||
{
|
||||
long days, rem;
|
||||
time_t lcltime;
|
||||
int y;
|
||||
@ -179,14 +181,16 @@ sntp_mktm_r(const time_t * tim_p ,struct tm *res ,int is_gmtime)
|
||||
}
|
||||
|
||||
/* compute hour, min, and sec */
|
||||
res->tm_hour = (int) (rem / SECSPERHOUR);
|
||||
res->tm_hour = (int)(rem / SECSPERHOUR);
|
||||
rem %= SECSPERHOUR;
|
||||
res->tm_min = (int) (rem / SECSPERMIN);
|
||||
res->tm_sec = (int) (rem % SECSPERMIN);
|
||||
res->tm_min = (int)(rem / SECSPERMIN);
|
||||
res->tm_sec = (int)(rem % SECSPERMIN);
|
||||
|
||||
/* compute day of week */
|
||||
if ((res->tm_wday = ((EPOCH_WDAY + days) % DAYSPERWEEK)) < 0)
|
||||
{
|
||||
res->tm_wday += DAYSPERWEEK;
|
||||
}
|
||||
|
||||
/* compute year & day of year */
|
||||
y = EPOCH_YEAR;
|
||||
@ -196,7 +200,9 @@ sntp_mktm_r(const time_t * tim_p ,struct tm *res ,int is_gmtime)
|
||||
{
|
||||
yleap = isleap(y);
|
||||
if (days < year_lengths[yleap])
|
||||
{
|
||||
break;
|
||||
}
|
||||
y++;
|
||||
days -= year_lengths[yleap];
|
||||
}
|
||||
@ -215,7 +221,9 @@ sntp_mktm_r(const time_t * tim_p ,struct tm *res ,int is_gmtime)
|
||||
res->tm_yday = days;
|
||||
ip = mon_lengths[yleap];
|
||||
for (res->tm_mon = 0; days >= ip[res->tm_mon]; ++res->tm_mon)
|
||||
{
|
||||
days -= ip[res->tm_mon];
|
||||
}
|
||||
res->tm_mday = days + 1;
|
||||
|
||||
if (!is_gmtime)
|
||||
@ -223,17 +231,17 @@ sntp_mktm_r(const time_t * tim_p ,struct tm *res ,int is_gmtime)
|
||||
int offset;
|
||||
int hours, mins, secs;
|
||||
|
||||
// TZ_LOCK;
|
||||
// if (_daylight)
|
||||
// {
|
||||
// if (y == __tzyear || __tzcalc_limits (y))
|
||||
// res->tm_isdst = (__tznorth
|
||||
// ? (*tim_p >= __tzrule[0].change && *tim_p < __tzrule[1].change)
|
||||
// : (*tim_p >= __tzrule[0].change || *tim_p < __tzrule[1].change));
|
||||
// else
|
||||
// res->tm_isdst = -1;
|
||||
// }
|
||||
// else
|
||||
// TZ_LOCK;
|
||||
// if (_daylight)
|
||||
// {
|
||||
// if (y == __tzyear || __tzcalc_limits (y))
|
||||
// res->tm_isdst = (__tznorth
|
||||
// ? (*tim_p >= __tzrule[0].change && *tim_p < __tzrule[1].change)
|
||||
// : (*tim_p >= __tzrule[0].change || *tim_p < __tzrule[1].change));
|
||||
// else
|
||||
// res->tm_isdst = -1;
|
||||
// }
|
||||
// else
|
||||
res->tm_isdst = -1;
|
||||
|
||||
offset = (res->tm_isdst == 1 ? sntp__tzrule[1].offset : sntp__tzrule[0].offset);
|
||||
@ -273,7 +281,9 @@ sntp_mktm_r(const time_t * tim_p ,struct tm *res ,int is_gmtime)
|
||||
++res->tm_yday;
|
||||
++res->tm_wday;
|
||||
if (res->tm_wday > 6)
|
||||
{
|
||||
res->tm_wday = 0;
|
||||
}
|
||||
++res->tm_mday;
|
||||
res->tm_hour -= HOURSPERDAY;
|
||||
if (res->tm_mday > ip[res->tm_mon])
|
||||
@ -293,7 +303,9 @@ sntp_mktm_r(const time_t * tim_p ,struct tm *res ,int is_gmtime)
|
||||
res->tm_yday -= 1;
|
||||
res->tm_wday -= 1;
|
||||
if (res->tm_wday < 0)
|
||||
{
|
||||
res->tm_wday = 6;
|
||||
}
|
||||
res->tm_mday -= 1;
|
||||
res->tm_hour += 24;
|
||||
if (res->tm_mday == 0)
|
||||
@ -308,33 +320,37 @@ sntp_mktm_r(const time_t * tim_p ,struct tm *res ,int is_gmtime)
|
||||
res->tm_mday = ip[res->tm_mon];
|
||||
}
|
||||
}
|
||||
// TZ_UNLOCK;
|
||||
// TZ_UNLOCK;
|
||||
}
|
||||
else
|
||||
{
|
||||
res->tm_isdst = 0;
|
||||
// os_printf("res %d %d %d %d %d\n",res->tm_year,res->tm_mon,res->tm_mday,res->tm_yday,res->tm_hour);
|
||||
}
|
||||
// os_printf("res %d %d %d %d %d\n",res->tm_year,res->tm_mon,res->tm_mday,res->tm_yday,res->tm_hour);
|
||||
return (res);
|
||||
}
|
||||
struct tm *
|
||||
sntp_localtime_r(const time_t * tim_p ,
|
||||
}
|
||||
struct tm *
|
||||
sntp_localtime_r(const time_t * tim_p,
|
||||
struct tm *res)
|
||||
{
|
||||
return sntp_mktm_r (tim_p, res, 0);
|
||||
}
|
||||
{
|
||||
return sntp_mktm_r(tim_p, res, 0);
|
||||
}
|
||||
|
||||
struct tm *
|
||||
sntp_localtime(const time_t * tim_p)
|
||||
{
|
||||
return sntp_localtime_r (tim_p, &res_buf);
|
||||
}
|
||||
struct tm *
|
||||
sntp_localtime(const time_t * tim_p)
|
||||
{
|
||||
return sntp_localtime_r(tim_p, &res_buf);
|
||||
}
|
||||
|
||||
int sntp__tzcalc_limits(int year)
|
||||
{
|
||||
int sntp__tzcalc_limits(int year)
|
||||
{
|
||||
int days, year_days, years;
|
||||
int i, j;
|
||||
|
||||
if (year < EPOCH_YEAR)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
__tzyear = year;
|
||||
|
||||
@ -347,9 +363,13 @@ int sntp__tzcalc_limits(int year)
|
||||
for (i = 0; i < 2; ++i)
|
||||
{
|
||||
if (sntp__tzrule[i].ch == 'J')
|
||||
{
|
||||
days = year_days + sntp__tzrule[i].d + (isleap(year) && sntp__tzrule[i].d >= 60);
|
||||
}
|
||||
else if (sntp__tzrule[i].ch == 'D')
|
||||
{
|
||||
days = year_days + sntp__tzrule[i].d;
|
||||
}
|
||||
else
|
||||
{
|
||||
int yleap = isleap(year);
|
||||
@ -359,17 +379,23 @@ int sntp__tzcalc_limits(int year)
|
||||
days = year_days;
|
||||
|
||||
for (j = 1; j < sntp__tzrule[i].m; ++j)
|
||||
days += ip[j-1];
|
||||
{
|
||||
days += ip[j - 1];
|
||||
}
|
||||
|
||||
m_wday = (EPOCH_WDAY + days) % DAYSPERWEEK;
|
||||
|
||||
wday_diff = sntp__tzrule[i].d - m_wday;
|
||||
if (wday_diff < 0)
|
||||
{
|
||||
wday_diff += DAYSPERWEEK;
|
||||
}
|
||||
m_day = (sntp__tzrule[i].n - 1) * DAYSPERWEEK + wday_diff;
|
||||
|
||||
while (m_day >= ip[j-1])
|
||||
while (m_day >= ip[j - 1])
|
||||
{
|
||||
m_day -= DAYSPERWEEK;
|
||||
}
|
||||
|
||||
days += m_day;
|
||||
}
|
||||
@ -381,89 +407,92 @@ int sntp__tzcalc_limits(int year)
|
||||
__tznorth = (sntp__tzrule[0].change < sntp__tzrule[1].change);
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
char* sntp_asctime_r(struct tm *tim_p ,char *result)
|
||||
{
|
||||
static const char day_name[7][4] = {
|
||||
char* sntp_asctime_r(struct tm *tim_p, char *result)
|
||||
{
|
||||
static const char day_name[7][4] =
|
||||
{
|
||||
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
|
||||
};
|
||||
static const char mon_name[12][4] = {
|
||||
static const char mon_name[12][4] =
|
||||
{
|
||||
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
|
||||
};
|
||||
os_sprintf (result, "%s %s %02d %02d:%02d:%02d %02d\n",
|
||||
os_sprintf(result, "%s %s %02d %02d:%02d:%02d %02d\n",
|
||||
day_name[tim_p->tm_wday],
|
||||
mon_name[tim_p->tm_mon],
|
||||
tim_p->tm_mday, tim_p->tm_hour, tim_p->tm_min,
|
||||
tim_p->tm_sec, 1900 + tim_p->tm_year);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
char* sntp_asctime(struct tm *tim_p)
|
||||
{
|
||||
return sntp_asctime_r (tim_p, reult);
|
||||
}
|
||||
char* sntp_asctime(struct tm *tim_p)
|
||||
{
|
||||
return sntp_asctime_r(tim_p, reult);
|
||||
}
|
||||
|
||||
uint32 ICACHE_RAM_ATTR sntp_get_current_timestamp(void)
|
||||
{
|
||||
uint32 ICACHE_RAM_ATTR sntp_get_current_timestamp(void)
|
||||
{
|
||||
return realtime_stamp;
|
||||
}
|
||||
}
|
||||
|
||||
char* sntp_get_real_time(time_t t)
|
||||
{
|
||||
return sntp_asctime(sntp_localtime (&t));
|
||||
}
|
||||
char* sntp_get_real_time(time_t t)
|
||||
{
|
||||
return sntp_asctime(sntp_localtime(&t));
|
||||
}
|
||||
|
||||
/* Returns the set timezone in seconds. If the timezone was set as seconds, the fractional part is floored. */
|
||||
sint32 sntp_get_timezone_in_seconds(void)
|
||||
{
|
||||
/* Returns the set timezone in seconds. If the timezone was set as seconds, the fractional part is floored. */
|
||||
sint32 sntp_get_timezone_in_seconds(void)
|
||||
{
|
||||
return time_zone;
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns the set timezone in hours. If the timezone was set as seconds, the fractional part is floored. */
|
||||
sint8 sntp_get_timezone(void)
|
||||
{
|
||||
/* Returns the set timezone in hours. If the timezone was set as seconds, the fractional part is floored. */
|
||||
sint8 sntp_get_timezone(void)
|
||||
{
|
||||
return (sint8)(time_zone / (60 * 60));
|
||||
}
|
||||
}
|
||||
|
||||
/* Sets the timezone in hours. Internally, the timezone is converted to seconds. */
|
||||
bool sntp_set_timezone_in_seconds(sint32 timezone)
|
||||
{
|
||||
if(timezone >= (-11 * (60 * 60)) || timezone <= (13 * (60 * 60))) {
|
||||
/* Sets the timezone in hours. Internally, the timezone is converted to seconds. */
|
||||
bool sntp_set_timezone_in_seconds(sint32 timezone)
|
||||
{
|
||||
if (timezone >= (-11 * (60 * 60)) || timezone <= (13 * (60 * 60)))
|
||||
{
|
||||
time_zone = timezone;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Sets the timezone in hours. Internally, the timezone is converted to seconds. */
|
||||
bool sntp_set_timezone(sint8 timezone)
|
||||
{
|
||||
/* Sets the timezone in hours. Internally, the timezone is converted to seconds. */
|
||||
bool sntp_set_timezone(sint8 timezone)
|
||||
{
|
||||
return sntp_set_timezone_in_seconds((sint32)timezone * 60 * 60);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void sntp_set_daylight(int daylight)
|
||||
{
|
||||
void sntp_set_daylight(int daylight)
|
||||
{
|
||||
dst = daylight;
|
||||
}
|
||||
}
|
||||
|
||||
void ICACHE_RAM_ATTR sntp_time_inc (void)
|
||||
{
|
||||
void ICACHE_RAM_ATTR sntp_time_inc(void)
|
||||
{
|
||||
realtime_stamp++;
|
||||
}
|
||||
}
|
||||
|
||||
static void sntp_set_system_time (uint32_t t)
|
||||
{
|
||||
static void sntp_set_system_time(uint32_t t)
|
||||
{
|
||||
realtime_stamp = t + time_zone + dst;
|
||||
os_timer_disarm(&sntp_timer);
|
||||
os_timer_setfn(&sntp_timer, (os_timer_func_t *)sntp_time_inc, NULL);
|
||||
os_timer_arm(&sntp_timer, 1000, 1);
|
||||
}
|
||||
}
|
||||
|
||||
int settimeofday(const struct timeval* tv, const struct timezone* tz)
|
||||
{
|
||||
int settimeofday(const struct timeval* tv, const struct timezone* tz)
|
||||
{
|
||||
if (tz) /*before*/
|
||||
{
|
||||
sntp_set_timezone_in_seconds(tz->tz_minuteswest * 60);
|
||||
@ -478,10 +507,12 @@ int settimeofday(const struct timeval* tv, const struct timezone* tz)
|
||||
sntp_set_system_time(tv->tv_sec);
|
||||
|
||||
if (_settimeofday_cb)
|
||||
{
|
||||
_settimeofday_cb();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // lwip2 only
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
extern "C" {
|
||||
|
||||
bool sntp_set_timezone_in_seconds(sint32 timezone);
|
||||
bool sntp_set_timezone_in_seconds(sint32 timezone);
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
/*
|
||||
* spiffs.h
|
||||
*
|
||||
* Created on: May 26, 2013
|
||||
* Author: petera
|
||||
*/
|
||||
spiffs.h
|
||||
|
||||
Created on: May 26, 2013
|
||||
Author: petera
|
||||
*/
|
||||
|
||||
#ifndef SPIFFS_H_
|
||||
#define SPIFFS_H_
|
||||
@ -99,14 +99,16 @@ typedef s32_t (*spiffs_erase)(u32_t addr, u32_t size);
|
||||
#endif // SPIFFS_HAL_CALLBACK_EXTRA
|
||||
|
||||
/* file system check callback report operation */
|
||||
typedef enum {
|
||||
typedef enum
|
||||
{
|
||||
SPIFFS_CHECK_LOOKUP = 0,
|
||||
SPIFFS_CHECK_INDEX,
|
||||
SPIFFS_CHECK_PAGE
|
||||
} spiffs_check_type;
|
||||
|
||||
/* file system check callback report type */
|
||||
typedef enum {
|
||||
typedef enum
|
||||
{
|
||||
SPIFFS_CHECK_PROGRESS = 0,
|
||||
SPIFFS_CHECK_ERROR,
|
||||
SPIFFS_CHECK_FIX_INDEX,
|
||||
@ -126,7 +128,8 @@ typedef void (*spiffs_check_callback)(spiffs_check_type type, spiffs_check_repor
|
||||
#endif // SPIFFS_HAL_CALLBACK_EXTRA
|
||||
|
||||
/* file system listener callback operation */
|
||||
typedef enum {
|
||||
typedef enum
|
||||
{
|
||||
/* the file has been created */
|
||||
SPIFFS_CB_CREATED = 0,
|
||||
/* the file has been updated or moved to another page */
|
||||
@ -197,7 +200,8 @@ typedef void (*spiffs_file_callback)(struct spiffs_t *fs, spiffs_fileop_type op,
|
||||
// phys structs
|
||||
|
||||
// spiffs spi configuration struct
|
||||
typedef struct {
|
||||
typedef struct
|
||||
{
|
||||
// physical read function
|
||||
spiffs_read hal_read_f;
|
||||
// physical write function
|
||||
@ -228,7 +232,8 @@ typedef struct {
|
||||
#endif
|
||||
} spiffs_config;
|
||||
|
||||
typedef struct spiffs_t {
|
||||
typedef struct spiffs_t
|
||||
{
|
||||
// file system configuration
|
||||
spiffs_config cfg;
|
||||
// number of logical blocks
|
||||
@ -294,7 +299,8 @@ typedef struct spiffs_t {
|
||||
} spiffs;
|
||||
|
||||
/* spiffs file status struct */
|
||||
typedef struct {
|
||||
typedef struct
|
||||
{
|
||||
spiffs_obj_id obj_id;
|
||||
u32_t size;
|
||||
spiffs_obj_type type;
|
||||
@ -305,7 +311,8 @@ typedef struct {
|
||||
#endif
|
||||
} spiffs_stat;
|
||||
|
||||
struct spiffs_dirent {
|
||||
struct spiffs_dirent
|
||||
{
|
||||
spiffs_obj_id obj_id;
|
||||
u8_t name[SPIFFS_OBJ_NAME_LEN];
|
||||
spiffs_obj_type type;
|
||||
@ -316,7 +323,8 @@ struct spiffs_dirent {
|
||||
#endif
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
typedef struct
|
||||
{
|
||||
spiffs *fs;
|
||||
spiffs_block_ix block;
|
||||
int entry;
|
||||
@ -324,7 +332,8 @@ typedef struct {
|
||||
|
||||
#if SPIFFS_IX_MAP
|
||||
|
||||
typedef struct {
|
||||
typedef struct
|
||||
{
|
||||
// buffer with looked up data pixes
|
||||
spiffs_page_ix *map_buf;
|
||||
// precise file byte offset
|
||||
@ -341,443 +350,443 @@ typedef struct {
|
||||
|
||||
#if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0
|
||||
/**
|
||||
* Special function. This takes a spiffs config struct and returns the number
|
||||
* of blocks this file system was formatted with. This function relies on
|
||||
* that following info is set correctly in given config struct:
|
||||
*
|
||||
* phys_addr, log_page_size, and log_block_size.
|
||||
*
|
||||
* Also, hal_read_f must be set in the config struct.
|
||||
*
|
||||
* One must be sure of the correct page size and that the physical address is
|
||||
* correct in the probed file system when calling this function. It is not
|
||||
* checked if the phys_addr actually points to the start of the file system,
|
||||
* so one might get a false positive if entering a phys_addr somewhere in the
|
||||
* middle of the file system at block boundary. In addition, it is not checked
|
||||
* if the page size is actually correct. If it is not, weird file system sizes
|
||||
* will be returned.
|
||||
*
|
||||
* If this function detects a file system it returns the assumed file system
|
||||
* size, which can be used to set the phys_size.
|
||||
*
|
||||
* Otherwise, it returns an error indicating why it is not regarded as a file
|
||||
* system.
|
||||
*
|
||||
* Note: this function is not protected with SPIFFS_LOCK and SPIFFS_UNLOCK
|
||||
* macros. It returns the error code directly, instead of as read by
|
||||
* SPIFFS_errno.
|
||||
*
|
||||
* @param config essential parts of the physical and logical
|
||||
* configuration of the file system.
|
||||
*/
|
||||
Special function. This takes a spiffs config struct and returns the number
|
||||
of blocks this file system was formatted with. This function relies on
|
||||
that following info is set correctly in given config struct:
|
||||
|
||||
phys_addr, log_page_size, and log_block_size.
|
||||
|
||||
Also, hal_read_f must be set in the config struct.
|
||||
|
||||
One must be sure of the correct page size and that the physical address is
|
||||
correct in the probed file system when calling this function. It is not
|
||||
checked if the phys_addr actually points to the start of the file system,
|
||||
so one might get a false positive if entering a phys_addr somewhere in the
|
||||
middle of the file system at block boundary. In addition, it is not checked
|
||||
if the page size is actually correct. If it is not, weird file system sizes
|
||||
will be returned.
|
||||
|
||||
If this function detects a file system it returns the assumed file system
|
||||
size, which can be used to set the phys_size.
|
||||
|
||||
Otherwise, it returns an error indicating why it is not regarded as a file
|
||||
system.
|
||||
|
||||
Note: this function is not protected with SPIFFS_LOCK and SPIFFS_UNLOCK
|
||||
macros. It returns the error code directly, instead of as read by
|
||||
SPIFFS_errno.
|
||||
|
||||
@param config essential parts of the physical and logical
|
||||
configuration of the file system.
|
||||
*/
|
||||
s32_t SPIFFS_probe_fs(spiffs_config *config);
|
||||
#endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0
|
||||
|
||||
/**
|
||||
* Initializes the file system dynamic parameters and mounts the filesystem.
|
||||
* If SPIFFS_USE_MAGIC is enabled the mounting may fail with SPIFFS_ERR_NOT_A_FS
|
||||
* if the flash does not contain a recognizable file system.
|
||||
* In this case, SPIFFS_format must be called prior to remounting.
|
||||
* @param fs the file system struct
|
||||
* @param config the physical and logical configuration of the file system
|
||||
* @param work a memory work buffer comprising 2*config->log_page_size
|
||||
* bytes used throughout all file system operations
|
||||
* @param fd_space memory for file descriptors
|
||||
* @param fd_space_size memory size of file descriptors
|
||||
* @param cache memory for cache, may be null
|
||||
* @param cache_size memory size of cache
|
||||
* @param check_cb_f callback function for reporting during consistency checks
|
||||
*/
|
||||
Initializes the file system dynamic parameters and mounts the filesystem.
|
||||
If SPIFFS_USE_MAGIC is enabled the mounting may fail with SPIFFS_ERR_NOT_A_FS
|
||||
if the flash does not contain a recognizable file system.
|
||||
In this case, SPIFFS_format must be called prior to remounting.
|
||||
@param fs the file system struct
|
||||
@param config the physical and logical configuration of the file system
|
||||
@param work a memory work buffer comprising 2*config->log_page_size
|
||||
bytes used throughout all file system operations
|
||||
@param fd_space memory for file descriptors
|
||||
@param fd_space_size memory size of file descriptors
|
||||
@param cache memory for cache, may be null
|
||||
@param cache_size memory size of cache
|
||||
@param check_cb_f callback function for reporting during consistency checks
|
||||
*/
|
||||
s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work,
|
||||
u8_t *fd_space, u32_t fd_space_size,
|
||||
void *cache, u32_t cache_size,
|
||||
spiffs_check_callback check_cb_f);
|
||||
|
||||
/**
|
||||
* Unmounts the file system. All file handles will be flushed of any
|
||||
* cached writes and closed.
|
||||
* @param fs the file system struct
|
||||
*/
|
||||
Unmounts the file system. All file handles will be flushed of any
|
||||
cached writes and closed.
|
||||
@param fs the file system struct
|
||||
*/
|
||||
void SPIFFS_unmount(spiffs *fs);
|
||||
|
||||
/**
|
||||
* Creates a new file.
|
||||
* @param fs the file system struct
|
||||
* @param path the path of the new file
|
||||
* @param mode ignored, for posix compliance
|
||||
*/
|
||||
Creates a new file.
|
||||
@param fs the file system struct
|
||||
@param path the path of the new file
|
||||
@param mode ignored, for posix compliance
|
||||
*/
|
||||
s32_t SPIFFS_creat(spiffs *fs, const char *path, spiffs_mode mode);
|
||||
|
||||
/**
|
||||
* Opens/creates a file.
|
||||
* @param fs the file system struct
|
||||
* @param path the path of the new file
|
||||
* @param flags the flags for the open command, can be combinations of
|
||||
* SPIFFS_O_APPEND, SPIFFS_O_TRUNC, SPIFFS_O_CREAT, SPIFFS_O_RDONLY,
|
||||
* SPIFFS_O_WRONLY, SPIFFS_O_RDWR, SPIFFS_O_DIRECT, SPIFFS_O_EXCL
|
||||
* @param mode ignored, for posix compliance
|
||||
*/
|
||||
Opens/creates a file.
|
||||
@param fs the file system struct
|
||||
@param path the path of the new file
|
||||
@param flags the flags for the open command, can be combinations of
|
||||
SPIFFS_O_APPEND, SPIFFS_O_TRUNC, SPIFFS_O_CREAT, SPIFFS_O_RDONLY,
|
||||
SPIFFS_O_WRONLY, SPIFFS_O_RDWR, SPIFFS_O_DIRECT, SPIFFS_O_EXCL
|
||||
@param mode ignored, for posix compliance
|
||||
*/
|
||||
spiffs_file SPIFFS_open(spiffs *fs, const char *path, spiffs_flags flags, spiffs_mode mode);
|
||||
|
||||
/**
|
||||
* Opens a file by given dir entry.
|
||||
* Optimization purposes, when traversing a file system with SPIFFS_readdir
|
||||
* a normal SPIFFS_open would need to traverse the filesystem again to find
|
||||
* the file, whilst SPIFFS_open_by_dirent already knows where the file resides.
|
||||
* @param fs the file system struct
|
||||
* @param e the dir entry to the file
|
||||
* @param flags the flags for the open command, can be combinations of
|
||||
* SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY,
|
||||
* SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT.
|
||||
* SPIFFS_CREAT will have no effect in this case.
|
||||
* @param mode ignored, for posix compliance
|
||||
*/
|
||||
Opens a file by given dir entry.
|
||||
Optimization purposes, when traversing a file system with SPIFFS_readdir
|
||||
a normal SPIFFS_open would need to traverse the filesystem again to find
|
||||
the file, whilst SPIFFS_open_by_dirent already knows where the file resides.
|
||||
@param fs the file system struct
|
||||
@param e the dir entry to the file
|
||||
@param flags the flags for the open command, can be combinations of
|
||||
SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY,
|
||||
SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT.
|
||||
SPIFFS_CREAT will have no effect in this case.
|
||||
@param mode ignored, for posix compliance
|
||||
*/
|
||||
spiffs_file SPIFFS_open_by_dirent(spiffs *fs, struct spiffs_dirent *e, spiffs_flags flags, spiffs_mode mode);
|
||||
|
||||
/**
|
||||
* Opens a file by given page index.
|
||||
* Optimization purposes, opens a file by directly pointing to the page
|
||||
* index in the spi flash.
|
||||
* If the page index does not point to a file header SPIFFS_ERR_NOT_A_FILE
|
||||
* is returned.
|
||||
* @param fs the file system struct
|
||||
* @param page_ix the page index
|
||||
* @param flags the flags for the open command, can be combinations of
|
||||
* SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY,
|
||||
* SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT.
|
||||
* SPIFFS_CREAT will have no effect in this case.
|
||||
* @param mode ignored, for posix compliance
|
||||
*/
|
||||
Opens a file by given page index.
|
||||
Optimization purposes, opens a file by directly pointing to the page
|
||||
index in the spi flash.
|
||||
If the page index does not point to a file header SPIFFS_ERR_NOT_A_FILE
|
||||
is returned.
|
||||
@param fs the file system struct
|
||||
@param page_ix the page index
|
||||
@param flags the flags for the open command, can be combinations of
|
||||
SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY,
|
||||
SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT.
|
||||
SPIFFS_CREAT will have no effect in this case.
|
||||
@param mode ignored, for posix compliance
|
||||
*/
|
||||
spiffs_file SPIFFS_open_by_page(spiffs *fs, spiffs_page_ix page_ix, spiffs_flags flags, spiffs_mode mode);
|
||||
|
||||
/**
|
||||
* Reads from given filehandle.
|
||||
* @param fs the file system struct
|
||||
* @param fh the filehandle
|
||||
* @param buf where to put read data
|
||||
* @param len how much to read
|
||||
* @returns number of bytes read, or -1 if error
|
||||
*/
|
||||
Reads from given filehandle.
|
||||
@param fs the file system struct
|
||||
@param fh the filehandle
|
||||
@param buf where to put read data
|
||||
@param len how much to read
|
||||
@returns number of bytes read, or -1 if error
|
||||
*/
|
||||
s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len);
|
||||
|
||||
/**
|
||||
* Writes to given filehandle.
|
||||
* @param fs the file system struct
|
||||
* @param fh the filehandle
|
||||
* @param buf the data to write
|
||||
* @param len how much to write
|
||||
* @returns number of bytes written, or -1 if error
|
||||
*/
|
||||
Writes to given filehandle.
|
||||
@param fs the file system struct
|
||||
@param fh the filehandle
|
||||
@param buf the data to write
|
||||
@param len how much to write
|
||||
@returns number of bytes written, or -1 if error
|
||||
*/
|
||||
s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len);
|
||||
|
||||
/**
|
||||
* Moves the read/write file offset. Resulting offset is returned or negative if error.
|
||||
* lseek(fs, fd, 0, SPIFFS_SEEK_CUR) will thus return current offset.
|
||||
* @param fs the file system struct
|
||||
* @param fh the filehandle
|
||||
* @param offs how much/where to move the offset
|
||||
* @param whence if SPIFFS_SEEK_SET, the file offset shall be set to offset bytes
|
||||
* if SPIFFS_SEEK_CUR, the file offset shall be set to its current location plus offset
|
||||
* if SPIFFS_SEEK_END, the file offset shall be set to the size of the file plus offse, which should be negative
|
||||
*/
|
||||
Moves the read/write file offset. Resulting offset is returned or negative if error.
|
||||
lseek(fs, fd, 0, SPIFFS_SEEK_CUR) will thus return current offset.
|
||||
@param fs the file system struct
|
||||
@param fh the filehandle
|
||||
@param offs how much/where to move the offset
|
||||
@param whence if SPIFFS_SEEK_SET, the file offset shall be set to offset bytes
|
||||
if SPIFFS_SEEK_CUR, the file offset shall be set to its current location plus offset
|
||||
if SPIFFS_SEEK_END, the file offset shall be set to the size of the file plus offse, which should be negative
|
||||
*/
|
||||
s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence);
|
||||
|
||||
/**
|
||||
* Removes a file by path
|
||||
* @param fs the file system struct
|
||||
* @param path the path of the file to remove
|
||||
*/
|
||||
Removes a file by path
|
||||
@param fs the file system struct
|
||||
@param path the path of the file to remove
|
||||
*/
|
||||
s32_t SPIFFS_remove(spiffs *fs, const char *path);
|
||||
|
||||
/**
|
||||
* Removes a file by filehandle
|
||||
* @param fs the file system struct
|
||||
* @param fh the filehandle of the file to remove
|
||||
*/
|
||||
Removes a file by filehandle
|
||||
@param fs the file system struct
|
||||
@param fh the filehandle of the file to remove
|
||||
*/
|
||||
s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh);
|
||||
|
||||
/**
|
||||
* Gets file status by path
|
||||
* @param fs the file system struct
|
||||
* @param path the path of the file to stat
|
||||
* @param s the stat struct to populate
|
||||
*/
|
||||
Gets file status by path
|
||||
@param fs the file system struct
|
||||
@param path the path of the file to stat
|
||||
@param s the stat struct to populate
|
||||
*/
|
||||
s32_t SPIFFS_stat(spiffs *fs, const char *path, spiffs_stat *s);
|
||||
|
||||
/**
|
||||
* Gets file status by filehandle
|
||||
* @param fs the file system struct
|
||||
* @param fh the filehandle of the file to stat
|
||||
* @param s the stat struct to populate
|
||||
*/
|
||||
Gets file status by filehandle
|
||||
@param fs the file system struct
|
||||
@param fh the filehandle of the file to stat
|
||||
@param s the stat struct to populate
|
||||
*/
|
||||
s32_t SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s);
|
||||
|
||||
/**
|
||||
* Flushes all pending write operations from cache for given file
|
||||
* @param fs the file system struct
|
||||
* @param fh the filehandle of the file to flush
|
||||
*/
|
||||
Flushes all pending write operations from cache for given file
|
||||
@param fs the file system struct
|
||||
@param fh the filehandle of the file to flush
|
||||
*/
|
||||
s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh);
|
||||
|
||||
/**
|
||||
* Closes a filehandle. If there are pending write operations, these are finalized before closing.
|
||||
* @param fs the file system struct
|
||||
* @param fh the filehandle of the file to close
|
||||
*/
|
||||
Closes a filehandle. If there are pending write operations, these are finalized before closing.
|
||||
@param fs the file system struct
|
||||
@param fh the filehandle of the file to close
|
||||
*/
|
||||
s32_t SPIFFS_close(spiffs *fs, spiffs_file fh);
|
||||
|
||||
/**
|
||||
* Renames a file
|
||||
* @param fs the file system struct
|
||||
* @param old path of file to rename
|
||||
* @param newPath new path of file
|
||||
*/
|
||||
Renames a file
|
||||
@param fs the file system struct
|
||||
@param old path of file to rename
|
||||
@param newPath new path of file
|
||||
*/
|
||||
s32_t SPIFFS_rename(spiffs *fs, const char *old, const char *newPath);
|
||||
|
||||
#if SPIFFS_OBJ_META_LEN
|
||||
/**
|
||||
* Updates file's metadata
|
||||
* @param fs the file system struct
|
||||
* @param path path to the file
|
||||
* @param meta new metadata. must be SPIFFS_OBJ_META_LEN bytes long.
|
||||
*/
|
||||
Updates file's metadata
|
||||
@param fs the file system struct
|
||||
@param path path to the file
|
||||
@param meta new metadata. must be SPIFFS_OBJ_META_LEN bytes long.
|
||||
*/
|
||||
s32_t SPIFFS_update_meta(spiffs *fs, const char *name, const void *meta);
|
||||
|
||||
/**
|
||||
* Updates file's metadata
|
||||
* @param fs the file system struct
|
||||
* @param fh file handle of the file
|
||||
* @param meta new metadata. must be SPIFFS_OBJ_META_LEN bytes long.
|
||||
*/
|
||||
Updates file's metadata
|
||||
@param fs the file system struct
|
||||
@param fh file handle of the file
|
||||
@param meta new metadata. must be SPIFFS_OBJ_META_LEN bytes long.
|
||||
*/
|
||||
s32_t SPIFFS_fupdate_meta(spiffs *fs, spiffs_file fh, const void *meta);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Returns last error of last file operation.
|
||||
* @param fs the file system struct
|
||||
*/
|
||||
Returns last error of last file operation.
|
||||
@param fs the file system struct
|
||||
*/
|
||||
s32_t SPIFFS_errno(spiffs *fs);
|
||||
|
||||
/**
|
||||
* Clears last error.
|
||||
* @param fs the file system struct
|
||||
*/
|
||||
Clears last error.
|
||||
@param fs the file system struct
|
||||
*/
|
||||
void SPIFFS_clearerr(spiffs *fs);
|
||||
|
||||
/**
|
||||
* Opens a directory stream corresponding to the given name.
|
||||
* The stream is positioned at the first entry in the directory.
|
||||
* On hydrogen builds the name argument is ignored as hydrogen builds always correspond
|
||||
* to a flat file structure - no directories.
|
||||
* @param fs the file system struct
|
||||
* @param name the name of the directory
|
||||
* @param d pointer the directory stream to be populated
|
||||
*/
|
||||
Opens a directory stream corresponding to the given name.
|
||||
The stream is positioned at the first entry in the directory.
|
||||
On hydrogen builds the name argument is ignored as hydrogen builds always correspond
|
||||
to a flat file structure - no directories.
|
||||
@param fs the file system struct
|
||||
@param name the name of the directory
|
||||
@param d pointer the directory stream to be populated
|
||||
*/
|
||||
spiffs_DIR *SPIFFS_opendir(spiffs *fs, const char *name, spiffs_DIR *d);
|
||||
|
||||
/**
|
||||
* Closes a directory stream
|
||||
* @param d the directory stream to close
|
||||
*/
|
||||
Closes a directory stream
|
||||
@param d the directory stream to close
|
||||
*/
|
||||
s32_t SPIFFS_closedir(spiffs_DIR *d);
|
||||
|
||||
/**
|
||||
* Reads a directory into given spifs_dirent struct.
|
||||
* @param d pointer to the directory stream
|
||||
* @param e the dirent struct to be populated
|
||||
* @returns null if error or end of stream, else given dirent is returned
|
||||
*/
|
||||
Reads a directory into given spifs_dirent struct.
|
||||
@param d pointer to the directory stream
|
||||
@param e the dirent struct to be populated
|
||||
@returns null if error or end of stream, else given dirent is returned
|
||||
*/
|
||||
struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e);
|
||||
|
||||
/**
|
||||
* Runs a consistency check on given filesystem.
|
||||
* @param fs the file system struct
|
||||
*/
|
||||
Runs a consistency check on given filesystem.
|
||||
@param fs the file system struct
|
||||
*/
|
||||
s32_t SPIFFS_check(spiffs *fs);
|
||||
|
||||
/**
|
||||
* Returns number of total bytes available and number of used bytes.
|
||||
* This is an estimation, and depends on if there a many files with little
|
||||
* data or few files with much data.
|
||||
* NB: If used number of bytes exceeds total bytes, a SPIFFS_check should
|
||||
* run. This indicates a power loss in midst of things. In worst case
|
||||
* (repeated powerlosses in mending or gc) you might have to delete some files.
|
||||
*
|
||||
* @param fs the file system struct
|
||||
* @param total total number of bytes in filesystem
|
||||
* @param used used number of bytes in filesystem
|
||||
*/
|
||||
Returns number of total bytes available and number of used bytes.
|
||||
This is an estimation, and depends on if there a many files with little
|
||||
data or few files with much data.
|
||||
NB: If used number of bytes exceeds total bytes, a SPIFFS_check should
|
||||
run. This indicates a power loss in midst of things. In worst case
|
||||
(repeated powerlosses in mending or gc) you might have to delete some files.
|
||||
|
||||
@param fs the file system struct
|
||||
@param total total number of bytes in filesystem
|
||||
@param used used number of bytes in filesystem
|
||||
*/
|
||||
s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used);
|
||||
|
||||
/**
|
||||
* Formats the entire file system. All data will be lost.
|
||||
* The filesystem must not be mounted when calling this.
|
||||
*
|
||||
* NB: formatting is awkward. Due to backwards compatibility, SPIFFS_mount
|
||||
* MUST be called prior to formatting in order to configure the filesystem.
|
||||
* If SPIFFS_mount succeeds, SPIFFS_unmount must be called before calling
|
||||
* SPIFFS_format.
|
||||
* If SPIFFS_mount fails, SPIFFS_format can be called directly without calling
|
||||
* SPIFFS_unmount first.
|
||||
*
|
||||
* @param fs the file system struct
|
||||
*/
|
||||
Formats the entire file system. All data will be lost.
|
||||
The filesystem must not be mounted when calling this.
|
||||
|
||||
NB: formatting is awkward. Due to backwards compatibility, SPIFFS_mount
|
||||
MUST be called prior to formatting in order to configure the filesystem.
|
||||
If SPIFFS_mount succeeds, SPIFFS_unmount must be called before calling
|
||||
SPIFFS_format.
|
||||
If SPIFFS_mount fails, SPIFFS_format can be called directly without calling
|
||||
SPIFFS_unmount first.
|
||||
|
||||
@param fs the file system struct
|
||||
*/
|
||||
s32_t SPIFFS_format(spiffs *fs);
|
||||
|
||||
/**
|
||||
* Returns nonzero if spiffs is mounted, or zero if unmounted.
|
||||
* @param fs the file system struct
|
||||
*/
|
||||
Returns nonzero if spiffs is mounted, or zero if unmounted.
|
||||
@param fs the file system struct
|
||||
*/
|
||||
u8_t SPIFFS_mounted(spiffs *fs);
|
||||
|
||||
/**
|
||||
* Tries to find a block where most or all pages are deleted, and erase that
|
||||
* block if found. Does not care for wear levelling. Will not move pages
|
||||
* around.
|
||||
* If parameter max_free_pages are set to 0, only blocks with only deleted
|
||||
* pages will be selected.
|
||||
*
|
||||
* NB: the garbage collector is automatically called when spiffs needs free
|
||||
* pages. The reason for this function is to give possibility to do background
|
||||
* tidying when user knows the system is idle.
|
||||
*
|
||||
* Use with care.
|
||||
*
|
||||
* Setting max_free_pages to anything larger than zero will eventually wear
|
||||
* flash more as a block containing free pages can be erased.
|
||||
*
|
||||
* Will set err_no to SPIFFS_OK if a block was found and erased,
|
||||
* SPIFFS_ERR_NO_DELETED_BLOCK if no matching block was found,
|
||||
* or other error.
|
||||
*
|
||||
* @param fs the file system struct
|
||||
* @param max_free_pages maximum number allowed free pages in block
|
||||
*/
|
||||
Tries to find a block where most or all pages are deleted, and erase that
|
||||
block if found. Does not care for wear levelling. Will not move pages
|
||||
around.
|
||||
If parameter max_free_pages are set to 0, only blocks with only deleted
|
||||
pages will be selected.
|
||||
|
||||
NB: the garbage collector is automatically called when spiffs needs free
|
||||
pages. The reason for this function is to give possibility to do background
|
||||
tidying when user knows the system is idle.
|
||||
|
||||
Use with care.
|
||||
|
||||
Setting max_free_pages to anything larger than zero will eventually wear
|
||||
flash more as a block containing free pages can be erased.
|
||||
|
||||
Will set err_no to SPIFFS_OK if a block was found and erased,
|
||||
SPIFFS_ERR_NO_DELETED_BLOCK if no matching block was found,
|
||||
or other error.
|
||||
|
||||
@param fs the file system struct
|
||||
@param max_free_pages maximum number allowed free pages in block
|
||||
*/
|
||||
s32_t SPIFFS_gc_quick(spiffs *fs, u16_t max_free_pages);
|
||||
|
||||
/**
|
||||
* Will try to make room for given amount of bytes in the filesystem by moving
|
||||
* pages and erasing blocks.
|
||||
* If it is physically impossible, err_no will be set to SPIFFS_ERR_FULL. If
|
||||
* there already is this amount (or more) of free space, SPIFFS_gc will
|
||||
* silently return. It is recommended to call SPIFFS_info before invoking
|
||||
* this method in order to determine what amount of bytes to give.
|
||||
*
|
||||
* NB: the garbage collector is automatically called when spiffs needs free
|
||||
* pages. The reason for this function is to give possibility to do background
|
||||
* tidying when user knows the system is idle.
|
||||
*
|
||||
* Use with care.
|
||||
*
|
||||
* @param fs the file system struct
|
||||
* @param size amount of bytes that should be freed
|
||||
*/
|
||||
Will try to make room for given amount of bytes in the filesystem by moving
|
||||
pages and erasing blocks.
|
||||
If it is physically impossible, err_no will be set to SPIFFS_ERR_FULL. If
|
||||
there already is this amount (or more) of free space, SPIFFS_gc will
|
||||
silently return. It is recommended to call SPIFFS_info before invoking
|
||||
this method in order to determine what amount of bytes to give.
|
||||
|
||||
NB: the garbage collector is automatically called when spiffs needs free
|
||||
pages. The reason for this function is to give possibility to do background
|
||||
tidying when user knows the system is idle.
|
||||
|
||||
Use with care.
|
||||
|
||||
@param fs the file system struct
|
||||
@param size amount of bytes that should be freed
|
||||
*/
|
||||
s32_t SPIFFS_gc(spiffs *fs, u32_t size);
|
||||
|
||||
/**
|
||||
* Check if EOF reached.
|
||||
* @param fs the file system struct
|
||||
* @param fh the filehandle of the file to check
|
||||
*/
|
||||
Check if EOF reached.
|
||||
@param fs the file system struct
|
||||
@param fh the filehandle of the file to check
|
||||
*/
|
||||
s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh);
|
||||
|
||||
/**
|
||||
* Get position in file.
|
||||
* @param fs the file system struct
|
||||
* @param fh the filehandle of the file to check
|
||||
*/
|
||||
Get position in file.
|
||||
@param fs the file system struct
|
||||
@param fh the filehandle of the file to check
|
||||
*/
|
||||
s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh);
|
||||
|
||||
/**
|
||||
* Registers a callback function that keeps track on operations on file
|
||||
* headers. Do note, that this callback is called from within internal spiffs
|
||||
* mechanisms. Any operations on the actual file system being callbacked from
|
||||
* in this callback will mess things up for sure - do not do this.
|
||||
* This can be used to track where files are and move around during garbage
|
||||
* collection, which in turn can be used to build location tables in ram.
|
||||
* Used in conjuction with SPIFFS_open_by_page this may improve performance
|
||||
* when opening a lot of files.
|
||||
* Must be invoked after mount.
|
||||
*
|
||||
* @param fs the file system struct
|
||||
* @param cb_func the callback on file operations
|
||||
*/
|
||||
Registers a callback function that keeps track on operations on file
|
||||
headers. Do note, that this callback is called from within internal spiffs
|
||||
mechanisms. Any operations on the actual file system being callbacked from
|
||||
in this callback will mess things up for sure - do not do this.
|
||||
This can be used to track where files are and move around during garbage
|
||||
collection, which in turn can be used to build location tables in ram.
|
||||
Used in conjuction with SPIFFS_open_by_page this may improve performance
|
||||
when opening a lot of files.
|
||||
Must be invoked after mount.
|
||||
|
||||
@param fs the file system struct
|
||||
@param cb_func the callback on file operations
|
||||
*/
|
||||
s32_t SPIFFS_set_file_callback_func(spiffs *fs, spiffs_file_callback cb_func);
|
||||
|
||||
#if SPIFFS_IX_MAP
|
||||
|
||||
/**
|
||||
* Maps the first level index lookup to a given memory map.
|
||||
* This will make reading big files faster, as the memory map will be used for
|
||||
* looking up data pages instead of searching for the indices on the physical
|
||||
* medium. When mapping, all affected indicies are found and the information is
|
||||
* copied to the array.
|
||||
* Whole file or only parts of it may be mapped. The index map will cover file
|
||||
* contents from argument offset until and including arguments (offset+len).
|
||||
* It is valid to map a longer range than the current file size. The map will
|
||||
* then be populated when the file grows.
|
||||
* On garbage collections and file data page movements, the map array will be
|
||||
* automatically updated. Do not tamper with the map array, as this contains
|
||||
* the references to the data pages. Modifying it from outside will corrupt any
|
||||
* future readings using this file descriptor.
|
||||
* The map will no longer be used when the file descriptor closed or the file
|
||||
* is unmapped.
|
||||
* This can be useful to get faster and more deterministic timing when reading
|
||||
* large files, or when seeking and reading a lot within a file.
|
||||
* @param fs the file system struct
|
||||
* @param fh the file handle of the file to map
|
||||
* @param map a spiffs_ix_map struct, describing the index map
|
||||
* @param offset absolute file offset where to start the index map
|
||||
* @param len length of the mapping in actual file bytes
|
||||
* @param map_buf the array buffer for the look up data - number of required
|
||||
* elements in the array can be derived from function
|
||||
* SPIFFS_bytes_to_ix_map_entries given the length
|
||||
*/
|
||||
Maps the first level index lookup to a given memory map.
|
||||
This will make reading big files faster, as the memory map will be used for
|
||||
looking up data pages instead of searching for the indices on the physical
|
||||
medium. When mapping, all affected indicies are found and the information is
|
||||
copied to the array.
|
||||
Whole file or only parts of it may be mapped. The index map will cover file
|
||||
contents from argument offset until and including arguments (offset+len).
|
||||
It is valid to map a longer range than the current file size. The map will
|
||||
then be populated when the file grows.
|
||||
On garbage collections and file data page movements, the map array will be
|
||||
automatically updated. Do not tamper with the map array, as this contains
|
||||
the references to the data pages. Modifying it from outside will corrupt any
|
||||
future readings using this file descriptor.
|
||||
The map will no longer be used when the file descriptor closed or the file
|
||||
is unmapped.
|
||||
This can be useful to get faster and more deterministic timing when reading
|
||||
large files, or when seeking and reading a lot within a file.
|
||||
@param fs the file system struct
|
||||
@param fh the file handle of the file to map
|
||||
@param map a spiffs_ix_map struct, describing the index map
|
||||
@param offset absolute file offset where to start the index map
|
||||
@param len length of the mapping in actual file bytes
|
||||
@param map_buf the array buffer for the look up data - number of required
|
||||
elements in the array can be derived from function
|
||||
SPIFFS_bytes_to_ix_map_entries given the length
|
||||
*/
|
||||
s32_t SPIFFS_ix_map(spiffs *fs, spiffs_file fh, spiffs_ix_map *map,
|
||||
u32_t offset, u32_t len, spiffs_page_ix *map_buf);
|
||||
|
||||
/**
|
||||
* Unmaps the index lookup from this filehandle. All future readings will
|
||||
* proceed as normal, requiring reading of the first level indices from
|
||||
* physical media.
|
||||
* The map and map buffer given in function SPIFFS_ix_map will no longer be
|
||||
* referenced by spiffs.
|
||||
* It is not strictly necessary to unmap a file before closing it, as closing
|
||||
* a file will automatically unmap it.
|
||||
* @param fs the file system struct
|
||||
* @param fh the file handle of the file to unmap
|
||||
*/
|
||||
Unmaps the index lookup from this filehandle. All future readings will
|
||||
proceed as normal, requiring reading of the first level indices from
|
||||
physical media.
|
||||
The map and map buffer given in function SPIFFS_ix_map will no longer be
|
||||
referenced by spiffs.
|
||||
It is not strictly necessary to unmap a file before closing it, as closing
|
||||
a file will automatically unmap it.
|
||||
@param fs the file system struct
|
||||
@param fh the file handle of the file to unmap
|
||||
*/
|
||||
s32_t SPIFFS_ix_unmap(spiffs *fs, spiffs_file fh);
|
||||
|
||||
/**
|
||||
* Moves the offset for the index map given in function SPIFFS_ix_map. Parts or
|
||||
* all of the map buffer will repopulated.
|
||||
* @param fs the file system struct
|
||||
* @param fh the mapped file handle of the file to remap
|
||||
* @param offset new absolute file offset where to start the index map
|
||||
*/
|
||||
Moves the offset for the index map given in function SPIFFS_ix_map. Parts or
|
||||
all of the map buffer will repopulated.
|
||||
@param fs the file system struct
|
||||
@param fh the mapped file handle of the file to remap
|
||||
@param offset new absolute file offset where to start the index map
|
||||
*/
|
||||
s32_t SPIFFS_ix_remap(spiffs *fs, spiffs_file fh, u32_t offs);
|
||||
|
||||
/**
|
||||
* Utility function to get number of spiffs_page_ix entries a map buffer must
|
||||
* contain on order to map given amount of file data in bytes.
|
||||
* See function SPIFFS_ix_map and SPIFFS_ix_map_entries_to_bytes.
|
||||
* @param fs the file system struct
|
||||
* @param bytes number of file data bytes to map
|
||||
* @return needed number of elements in a spiffs_page_ix array needed to
|
||||
* map given amount of bytes in a file
|
||||
*/
|
||||
Utility function to get number of spiffs_page_ix entries a map buffer must
|
||||
contain on order to map given amount of file data in bytes.
|
||||
See function SPIFFS_ix_map and SPIFFS_ix_map_entries_to_bytes.
|
||||
@param fs the file system struct
|
||||
@param bytes number of file data bytes to map
|
||||
@return needed number of elements in a spiffs_page_ix array needed to
|
||||
map given amount of bytes in a file
|
||||
*/
|
||||
s32_t SPIFFS_bytes_to_ix_map_entries(spiffs *fs, u32_t bytes);
|
||||
|
||||
/**
|
||||
* Utility function to amount of file data bytes that can be mapped when
|
||||
* mapping a file with buffer having given number of spiffs_page_ix entries.
|
||||
* See function SPIFFS_ix_map and SPIFFS_bytes_to_ix_map_entries.
|
||||
* @param fs the file system struct
|
||||
* @param map_page_ix_entries number of entries in a spiffs_page_ix array
|
||||
* @return amount of file data in bytes that can be mapped given a map
|
||||
* buffer having given amount of spiffs_page_ix entries
|
||||
*/
|
||||
Utility function to amount of file data bytes that can be mapped when
|
||||
mapping a file with buffer having given number of spiffs_page_ix entries.
|
||||
See function SPIFFS_ix_map and SPIFFS_bytes_to_ix_map_entries.
|
||||
@param fs the file system struct
|
||||
@param map_page_ix_entries number of entries in a spiffs_page_ix array
|
||||
@return amount of file data in bytes that can be mapped given a map
|
||||
buffer having given amount of spiffs_page_ix entries
|
||||
*/
|
||||
s32_t SPIFFS_ix_map_entries_to_bytes(spiffs *fs, u32_t map_page_ix_entries);
|
||||
|
||||
#endif // SPIFFS_IX_MAP
|
||||
@ -785,24 +794,24 @@ s32_t SPIFFS_ix_map_entries_to_bytes(spiffs *fs, u32_t map_page_ix_entries);
|
||||
|
||||
#if SPIFFS_TEST_VISUALISATION
|
||||
/**
|
||||
* Prints out a visualization of the filesystem.
|
||||
* @param fs the file system struct
|
||||
*/
|
||||
Prints out a visualization of the filesystem.
|
||||
@param fs the file system struct
|
||||
*/
|
||||
s32_t SPIFFS_vis(spiffs *fs);
|
||||
#endif
|
||||
|
||||
#if SPIFFS_BUFFER_HELP
|
||||
/**
|
||||
* Returns number of bytes needed for the filedescriptor buffer given
|
||||
* amount of file descriptors.
|
||||
*/
|
||||
Returns number of bytes needed for the filedescriptor buffer given
|
||||
amount of file descriptors.
|
||||
*/
|
||||
u32_t SPIFFS_buffer_bytes_for_filedescs(spiffs *fs, u32_t num_descs);
|
||||
|
||||
#if SPIFFS_CACHE
|
||||
/**
|
||||
* Returns number of bytes needed for the cache buffer given
|
||||
* amount of cache pages.
|
||||
*/
|
||||
Returns number of bytes needed for the cache buffer given
|
||||
amount of cache pages.
|
||||
*/
|
||||
u32_t SPIFFS_buffer_bytes_for_cache(spiffs *fs, u32_t num_pages);
|
||||
#endif
|
||||
#endif
|
||||
|
@ -1,9 +1,9 @@
|
||||
/*
|
||||
* spiffs_cache.c
|
||||
*
|
||||
* Created on: Jun 23, 2013
|
||||
* Author: petera
|
||||
*/
|
||||
spiffs_cache.c
|
||||
|
||||
Created on: Jun 23, 2013
|
||||
Author: petera
|
||||
*/
|
||||
|
||||
#include "spiffs.h"
|
||||
#include "spiffs_nucleus.h"
|
||||
@ -12,16 +12,22 @@
|
||||
|
||||
extern "C" {
|
||||
|
||||
// returns cached page for give page index, or null if no such cached page
|
||||
static spiffs_cache_page *spiffs_cache_page_get(spiffs *fs, spiffs_page_ix pix) {
|
||||
// returns cached page for give page index, or null if no such cached page
|
||||
static spiffs_cache_page *spiffs_cache_page_get(spiffs *fs, spiffs_page_ix pix)
|
||||
{
|
||||
spiffs_cache *cache = spiffs_get_cache(fs);
|
||||
if ((cache->cpage_use_map & cache->cpage_use_mask) == 0) return 0;
|
||||
if ((cache->cpage_use_map & cache->cpage_use_mask) == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int i;
|
||||
for (i = 0; i < cache->cpage_count; i++) {
|
||||
for (i = 0; i < cache->cpage_count; i++)
|
||||
{
|
||||
spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i);
|
||||
if ((cache->cpage_use_map & (1<<i)) &&
|
||||
if ((cache->cpage_use_map & (1 << i)) &&
|
||||
(cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 &&
|
||||
cp->pix == pix ) {
|
||||
cp->pix == pix)
|
||||
{
|
||||
//SPIFFS_CACHE_DBG("CACHE_GET: have cache page " _SPIPRIi " for " _SPIPRIpg"\n", i, pix);
|
||||
cp->last_access = cache->last_access;
|
||||
return cp;
|
||||
@ -29,26 +35,31 @@ static spiffs_cache_page *spiffs_cache_page_get(spiffs *fs, spiffs_page_ix pix)
|
||||
}
|
||||
//SPIFFS_CACHE_DBG("CACHE_GET: no cache for " _SPIPRIpg"\n", pix);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// frees cached page
|
||||
static s32_t spiffs_cache_page_free(spiffs *fs, int ix, u8_t write_back) {
|
||||
// frees cached page
|
||||
static s32_t spiffs_cache_page_free(spiffs *fs, int ix, u8_t write_back)
|
||||
{
|
||||
s32_t res = SPIFFS_OK;
|
||||
spiffs_cache *cache = spiffs_get_cache(fs);
|
||||
spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, ix);
|
||||
if (cache->cpage_use_map & (1<<ix)) {
|
||||
if (cache->cpage_use_map & (1 << ix))
|
||||
{
|
||||
if (write_back &&
|
||||
(cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 &&
|
||||
(cp->flags & SPIFFS_CACHE_FLAG_DIRTY)) {
|
||||
(cp->flags & SPIFFS_CACHE_FLAG_DIRTY))
|
||||
{
|
||||
u8_t *mem = spiffs_get_cache_page(fs, cache, ix);
|
||||
SPIFFS_CACHE_DBG("CACHE_FREE: write cache page " _SPIPRIi " pix " _SPIPRIpg "\n", ix, cp->pix);
|
||||
res = SPIFFS_HAL_WRITE(fs, SPIFFS_PAGE_TO_PADDR(fs, cp->pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), mem);
|
||||
}
|
||||
|
||||
#if SPIFFS_CACHE_WR
|
||||
if (cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) {
|
||||
if (cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR)
|
||||
{
|
||||
SPIFFS_CACHE_DBG("CACHE_FREE: free cache page " _SPIPRIi " objid " _SPIPRIid "\n", ix, cp->obj_id);
|
||||
} else
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
SPIFFS_CACHE_DBG("CACHE_FREE: free cache page " _SPIPRIi " pix " _SPIPRIpg "\n", ix, cp->pix);
|
||||
@ -58,14 +69,16 @@ static s32_t spiffs_cache_page_free(spiffs *fs, int ix, u8_t write_back) {
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
// removes the oldest accessed cached page
|
||||
static s32_t spiffs_cache_page_remove_oldest(spiffs *fs, u8_t flag_mask, u8_t flags) {
|
||||
// removes the oldest accessed cached page
|
||||
static s32_t spiffs_cache_page_remove_oldest(spiffs *fs, u8_t flag_mask, u8_t flags)
|
||||
{
|
||||
s32_t res = SPIFFS_OK;
|
||||
spiffs_cache *cache = spiffs_get_cache(fs);
|
||||
|
||||
if ((cache->cpage_use_map & cache->cpage_use_mask) != cache->cpage_use_mask) {
|
||||
if ((cache->cpage_use_map & cache->cpage_use_mask) != cache->cpage_use_mask)
|
||||
{
|
||||
// at least one free cpage
|
||||
return SPIFFS_OK;
|
||||
}
|
||||
@ -74,34 +87,41 @@ static s32_t spiffs_cache_page_remove_oldest(spiffs *fs, u8_t flag_mask, u8_t fl
|
||||
int i;
|
||||
int cand_ix = -1;
|
||||
u32_t oldest_val = 0;
|
||||
for (i = 0; i < cache->cpage_count; i++) {
|
||||
for (i = 0; i < cache->cpage_count; i++)
|
||||
{
|
||||
spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i);
|
||||
if ((cache->last_access - cp->last_access) > oldest_val &&
|
||||
(cp->flags & flag_mask) == flags) {
|
||||
(cp->flags & flag_mask) == flags)
|
||||
{
|
||||
oldest_val = cache->last_access - cp->last_access;
|
||||
cand_ix = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (cand_ix >= 0) {
|
||||
if (cand_ix >= 0)
|
||||
{
|
||||
res = spiffs_cache_page_free(fs, cand_ix, 1);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
// allocates a new cached page and returns it, or null if all cache pages are busy
|
||||
static spiffs_cache_page *spiffs_cache_page_allocate(spiffs *fs) {
|
||||
// allocates a new cached page and returns it, or null if all cache pages are busy
|
||||
static spiffs_cache_page *spiffs_cache_page_allocate(spiffs *fs)
|
||||
{
|
||||
spiffs_cache *cache = spiffs_get_cache(fs);
|
||||
if (cache->cpage_use_map == 0xffffffff) {
|
||||
if (cache->cpage_use_map == 0xffffffff)
|
||||
{
|
||||
// out of cache memory
|
||||
return 0;
|
||||
}
|
||||
int i;
|
||||
for (i = 0; i < cache->cpage_count; i++) {
|
||||
if ((cache->cpage_use_map & (1<<i)) == 0) {
|
||||
for (i = 0; i < cache->cpage_count; i++)
|
||||
{
|
||||
if ((cache->cpage_use_map & (1 << i)) == 0)
|
||||
{
|
||||
spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i);
|
||||
cache->cpage_use_map |= (1<<i);
|
||||
cache->cpage_use_map |= (1 << i);
|
||||
cp->last_access = cache->last_access;
|
||||
//SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page " _SPIPRIi"\n", i);
|
||||
return cp;
|
||||
@ -109,32 +129,36 @@ static spiffs_cache_page *spiffs_cache_page_allocate(spiffs *fs) {
|
||||
}
|
||||
// out of cache entries
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// drops the cache page for give page index
|
||||
void spiffs_cache_drop_page(spiffs *fs, spiffs_page_ix pix) {
|
||||
// drops the cache page for give page index
|
||||
void spiffs_cache_drop_page(spiffs *fs, spiffs_page_ix pix)
|
||||
{
|
||||
spiffs_cache_page *cp = spiffs_cache_page_get(fs, pix);
|
||||
if (cp) {
|
||||
if (cp)
|
||||
{
|
||||
spiffs_cache_page_free(fs, cp->ix, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
// ------------------------------
|
||||
|
||||
// reads from spi flash or the cache
|
||||
s32_t spiffs_phys_rd(
|
||||
// reads from spi flash or the cache
|
||||
s32_t spiffs_phys_rd(
|
||||
spiffs *fs,
|
||||
u8_t op,
|
||||
spiffs_file fh,
|
||||
u32_t addr,
|
||||
u32_t len,
|
||||
u8_t *dst) {
|
||||
u8_t *dst)
|
||||
{
|
||||
(void)fh;
|
||||
s32_t res = SPIFFS_OK;
|
||||
spiffs_cache *cache = spiffs_get_cache(fs);
|
||||
spiffs_cache_page *cp = spiffs_cache_page_get(fs, SPIFFS_PADDR_TO_PAGE(fs, addr));
|
||||
cache->last_access++;
|
||||
if (cp) {
|
||||
if (cp)
|
||||
{
|
||||
// we've already got one, you see
|
||||
#if SPIFFS_CACHE_STATS
|
||||
fs->cache_hits++;
|
||||
@ -142,8 +166,11 @@ s32_t spiffs_phys_rd(
|
||||
cp->last_access = cache->last_access;
|
||||
u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix);
|
||||
_SPIFFS_MEMCPY(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len);
|
||||
} else {
|
||||
if ((op & SPIFFS_OP_TYPE_MASK) == SPIFFS_OP_T_OBJ_LU2) {
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((op & SPIFFS_OP_TYPE_MASK) == SPIFFS_OP_T_OBJ_LU2)
|
||||
{
|
||||
// for second layer lookup functions, we do not cache in order to prevent shredding
|
||||
return SPIFFS_HAL_READ(fs, addr, len, dst);
|
||||
}
|
||||
@ -155,7 +182,8 @@ s32_t spiffs_phys_rd(
|
||||
res = spiffs_cache_page_remove_oldest(fs, SPIFFS_CACHE_FLAG_TYPE_WR, 0);
|
||||
|
||||
cp = spiffs_cache_page_allocate(fs);
|
||||
if (cp) {
|
||||
if (cp)
|
||||
{
|
||||
cp->flags = SPIFFS_CACHE_FLAG_WRTHRU;
|
||||
cp->pix = SPIFFS_PADDR_TO_PAGE(fs, addr);
|
||||
SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page " _SPIPRIi " for pix " _SPIPRIpg "\n", cp->ix, cp->pix);
|
||||
@ -164,43 +192,50 @@ s32_t spiffs_phys_rd(
|
||||
addr - SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr),
|
||||
SPIFFS_CFG_LOG_PAGE_SZ(fs),
|
||||
spiffs_get_cache_page(fs, cache, cp->ix));
|
||||
if (res2 != SPIFFS_OK) {
|
||||
if (res2 != SPIFFS_OK)
|
||||
{
|
||||
// honor read failure before possible write failure (bad idea?)
|
||||
res = res2;
|
||||
}
|
||||
u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix);
|
||||
_SPIFFS_MEMCPY(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
// this will never happen, last resort for sake of symmetry
|
||||
s32_t res2 = SPIFFS_HAL_READ(fs, addr, len, dst);
|
||||
if (res2 != SPIFFS_OK) {
|
||||
if (res2 != SPIFFS_OK)
|
||||
{
|
||||
// honor read failure before possible write failure (bad idea?)
|
||||
res = res2;
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
// writes to spi flash and/or the cache
|
||||
s32_t spiffs_phys_wr(
|
||||
// writes to spi flash and/or the cache
|
||||
s32_t spiffs_phys_wr(
|
||||
spiffs *fs,
|
||||
u8_t op,
|
||||
spiffs_file fh,
|
||||
u32_t addr,
|
||||
u32_t len,
|
||||
u8_t *src) {
|
||||
u8_t *src)
|
||||
{
|
||||
(void)fh;
|
||||
spiffs_page_ix pix = SPIFFS_PADDR_TO_PAGE(fs, addr);
|
||||
spiffs_cache *cache = spiffs_get_cache(fs);
|
||||
spiffs_cache_page *cp = spiffs_cache_page_get(fs, pix);
|
||||
|
||||
if (cp && (op & SPIFFS_OP_COM_MASK) != SPIFFS_OP_C_WRTHRU) {
|
||||
if (cp && (op & SPIFFS_OP_COM_MASK) != SPIFFS_OP_C_WRTHRU)
|
||||
{
|
||||
// have a cache page
|
||||
// copy in data to cache page
|
||||
|
||||
if ((op & SPIFFS_OP_COM_MASK) == SPIFFS_OP_C_DELE &&
|
||||
(op & SPIFFS_OP_TYPE_MASK) != SPIFFS_OP_T_OBJ_LU) {
|
||||
(op & SPIFFS_OP_TYPE_MASK) != SPIFFS_OP_T_OBJ_LU)
|
||||
{
|
||||
// page is being deleted, wipe from cache - unless it is a lookup page
|
||||
spiffs_cache_page_free(fs, cp->ix, 0);
|
||||
return SPIFFS_HAL_WRITE(fs, addr, len, src);
|
||||
@ -212,49 +247,60 @@ s32_t spiffs_phys_wr(
|
||||
cache->last_access++;
|
||||
cp->last_access = cache->last_access;
|
||||
|
||||
if (cp->flags & SPIFFS_CACHE_FLAG_WRTHRU) {
|
||||
if (cp->flags & SPIFFS_CACHE_FLAG_WRTHRU)
|
||||
{
|
||||
// page is being updated, no write-cache, just pass thru
|
||||
return SPIFFS_HAL_WRITE(fs, addr, len, src);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return SPIFFS_OK;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
// no cache page, no write cache - just write thru
|
||||
return SPIFFS_HAL_WRITE(fs, addr, len, src);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if SPIFFS_CACHE_WR
|
||||
// returns the cache page that this fd refers, or null if no cache page
|
||||
spiffs_cache_page *spiffs_cache_page_get_by_fd(spiffs *fs, spiffs_fd *fd) {
|
||||
// returns the cache page that this fd refers, or null if no cache page
|
||||
spiffs_cache_page *spiffs_cache_page_get_by_fd(spiffs *fs, spiffs_fd *fd)
|
||||
{
|
||||
spiffs_cache *cache = spiffs_get_cache(fs);
|
||||
|
||||
if ((cache->cpage_use_map & cache->cpage_use_mask) == 0) {
|
||||
if ((cache->cpage_use_map & cache->cpage_use_mask) == 0)
|
||||
{
|
||||
// all cpages free, no cpage cannot be assigned to obj_id
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i;
|
||||
for (i = 0; i < cache->cpage_count; i++) {
|
||||
for (i = 0; i < cache->cpage_count; i++)
|
||||
{
|
||||
spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i);
|
||||
if ((cache->cpage_use_map & (1<<i)) &&
|
||||
if ((cache->cpage_use_map & (1 << i)) &&
|
||||
(cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) &&
|
||||
cp->obj_id == fd->obj_id) {
|
||||
cp->obj_id == fd->obj_id)
|
||||
{
|
||||
return cp;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// allocates a new cache page and refers this to given fd - flushes an old cache
|
||||
// page if all cache is busy
|
||||
spiffs_cache_page *spiffs_cache_page_allocate_by_fd(spiffs *fs, spiffs_fd *fd) {
|
||||
// allocates a new cache page and refers this to given fd - flushes an old cache
|
||||
// page if all cache is busy
|
||||
spiffs_cache_page *spiffs_cache_page_allocate_by_fd(spiffs *fs, spiffs_fd *fd)
|
||||
{
|
||||
// before this function is called, it is ensured that there is no already existing
|
||||
// cache page with same object id
|
||||
spiffs_cache_page_remove_oldest(fs, SPIFFS_CACHE_FLAG_TYPE_WR, 0);
|
||||
spiffs_cache_page *cp = spiffs_cache_page_allocate(fs);
|
||||
if (cp == 0) {
|
||||
if (cp == 0)
|
||||
{
|
||||
// could not get cache page
|
||||
return 0;
|
||||
}
|
||||
@ -264,37 +310,51 @@ spiffs_cache_page *spiffs_cache_page_allocate_by_fd(spiffs *fs, spiffs_fd *fd) {
|
||||
fd->cache_page = cp;
|
||||
SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page " _SPIPRIi " for fd " _SPIPRIfd ":" _SPIPRIid "\n", cp->ix, fd->file_nbr, fd->obj_id);
|
||||
return cp;
|
||||
}
|
||||
}
|
||||
|
||||
// unrefers all fds that this cache page refers to and releases the cache page
|
||||
void spiffs_cache_fd_release(spiffs *fs, spiffs_cache_page *cp) {
|
||||
if (cp == 0) return;
|
||||
// unrefers all fds that this cache page refers to and releases the cache page
|
||||
void spiffs_cache_fd_release(spiffs *fs, spiffs_cache_page *cp)
|
||||
{
|
||||
if (cp == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
u32_t i;
|
||||
spiffs_fd *fds = (spiffs_fd *)fs->fd_space;
|
||||
for (i = 0; i < fs->fd_count; i++) {
|
||||
for (i = 0; i < fs->fd_count; i++)
|
||||
{
|
||||
spiffs_fd *cur_fd = &fds[i];
|
||||
if (cur_fd->file_nbr != 0 && cur_fd->cache_page == cp) {
|
||||
if (cur_fd->file_nbr != 0 && cur_fd->cache_page == cp)
|
||||
{
|
||||
cur_fd->cache_page = 0;
|
||||
}
|
||||
}
|
||||
spiffs_cache_page_free(fs, cp->ix, 0);
|
||||
|
||||
cp->obj_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// initializes the cache
|
||||
void spiffs_cache_init(spiffs *fs) {
|
||||
if (fs->cache == 0) return;
|
||||
// initializes the cache
|
||||
void spiffs_cache_init(spiffs *fs)
|
||||
{
|
||||
if (fs->cache == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
u32_t sz = fs->cache_size;
|
||||
u32_t cache_mask = 0;
|
||||
int i;
|
||||
int cache_entries =
|
||||
(sz - sizeof(spiffs_cache)) / (SPIFFS_CACHE_PAGE_SIZE(fs));
|
||||
if (cache_entries <= 0) return;
|
||||
if (cache_entries <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < cache_entries; i++) {
|
||||
for (i = 0; i < cache_entries; i++)
|
||||
{
|
||||
cache_mask <<= 1;
|
||||
cache_mask |= 1;
|
||||
}
|
||||
@ -313,10 +373,11 @@ void spiffs_cache_init(spiffs *fs) {
|
||||
memset(c->cpages, 0, c->cpage_count * SPIFFS_CACHE_PAGE_SIZE(fs));
|
||||
|
||||
c->cpage_use_map &= ~(c->cpage_use_mask);
|
||||
for (i = 0; i < cache.cpage_count; i++) {
|
||||
for (i = 0; i < cache.cpage_count; i++)
|
||||
{
|
||||
spiffs_get_cache_page_hdr(fs, c, i)->ix = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
#endif // SPIFFS_CACHE
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,9 +1,9 @@
|
||||
/*
|
||||
* spiffs_config.h
|
||||
*
|
||||
* Created on: Jul 3, 2013
|
||||
* Author: petera
|
||||
*/
|
||||
spiffs_config.h
|
||||
|
||||
Created on: Jul 3, 2013
|
||||
Author: petera
|
||||
*/
|
||||
|
||||
#ifndef SPIFFS_CONFIG_H_
|
||||
#define SPIFFS_CONFIG_H_
|
||||
|
@ -5,12 +5,13 @@
|
||||
|
||||
extern "C" {
|
||||
|
||||
// Erases a logical block and updates the erase counter.
|
||||
// If cache is enabled, all pages that might be cached in this block
|
||||
// is dropped.
|
||||
static s32_t spiffs_gc_erase_block(
|
||||
// Erases a logical block and updates the erase counter.
|
||||
// If cache is enabled, all pages that might be cached in this block
|
||||
// is dropped.
|
||||
static s32_t spiffs_gc_erase_block(
|
||||
spiffs *fs,
|
||||
spiffs_block_ix bix) {
|
||||
spiffs_block_ix bix)
|
||||
{
|
||||
s32_t res;
|
||||
|
||||
SPIFFS_GC_DBG("gc: erase block " _SPIPRIbl "\n", bix);
|
||||
@ -20,19 +21,21 @@ static s32_t spiffs_gc_erase_block(
|
||||
#if SPIFFS_CACHE
|
||||
{
|
||||
u32_t i;
|
||||
for (i = 0; i < SPIFFS_PAGES_PER_BLOCK(fs); i++) {
|
||||
for (i = 0; i < SPIFFS_PAGES_PER_BLOCK(fs); i++)
|
||||
{
|
||||
spiffs_cache_drop_page(fs, SPIFFS_PAGE_FOR_BLOCK(fs, bix) + i);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
// Searches for blocks where all entries are deleted - if one is found,
|
||||
// the block is erased. Compared to the non-quick gc, the quick one ensures
|
||||
// that no updates are needed on existing objects on pages that are erased.
|
||||
s32_t spiffs_gc_quick(
|
||||
spiffs *fs, u16_t max_free_pages) {
|
||||
// Searches for blocks where all entries are deleted - if one is found,
|
||||
// the block is erased. Compared to the non-quick gc, the quick one ensures
|
||||
// that no updates are needed on existing objects on pages that are erased.
|
||||
s32_t spiffs_gc_quick(
|
||||
spiffs *fs, u16_t max_free_pages)
|
||||
{
|
||||
s32_t res = SPIFFS_OK;
|
||||
u32_t blocks = fs->block_count;
|
||||
spiffs_block_ix cur_block = 0;
|
||||
@ -49,32 +52,41 @@ s32_t spiffs_gc_quick(
|
||||
|
||||
// find fully deleted blocks
|
||||
// check each block
|
||||
while (res == SPIFFS_OK && blocks--) {
|
||||
while (res == SPIFFS_OK && blocks--)
|
||||
{
|
||||
u16_t deleted_pages_in_block = 0;
|
||||
u16_t free_pages_in_block = 0;
|
||||
|
||||
int obj_lookup_page = 0;
|
||||
// check each object lookup page
|
||||
while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
|
||||
while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs))
|
||||
{
|
||||
int entry_offset = obj_lookup_page * entries_per_page;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
|
||||
0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
// check each entry
|
||||
while (res == SPIFFS_OK &&
|
||||
cur_entry - entry_offset < entries_per_page &&
|
||||
cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
|
||||
spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
|
||||
if (obj_id == SPIFFS_OBJ_ID_DELETED) {
|
||||
cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)))
|
||||
{
|
||||
spiffs_obj_id obj_id = obj_lu_buf[cur_entry - entry_offset];
|
||||
if (obj_id == SPIFFS_OBJ_ID_DELETED)
|
||||
{
|
||||
deleted_pages_in_block++;
|
||||
} else if (obj_id == SPIFFS_OBJ_ID_FREE) {
|
||||
}
|
||||
else if (obj_id == SPIFFS_OBJ_ID_FREE)
|
||||
{
|
||||
// kill scan, go for next block
|
||||
free_pages_in_block++;
|
||||
if (free_pages_in_block > max_free_pages) {
|
||||
if (free_pages_in_block > max_free_pages)
|
||||
{
|
||||
obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs);
|
||||
res = 1; // kill object lu loop
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
// kill scan, go for next block
|
||||
obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs);
|
||||
res = 1; // kill object lu loop
|
||||
@ -84,11 +96,15 @@ s32_t spiffs_gc_quick(
|
||||
} // per entry
|
||||
obj_lookup_page++;
|
||||
} // per object lookup page
|
||||
if (res == 1) res = SPIFFS_OK;
|
||||
if (res == 1)
|
||||
{
|
||||
res = SPIFFS_OK;
|
||||
}
|
||||
|
||||
if (res == SPIFFS_OK &&
|
||||
deleted_pages_in_block + free_pages_in_block == SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs) &&
|
||||
free_pages_in_block <= max_free_pages) {
|
||||
deleted_pages_in_block + free_pages_in_block == SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs) &&
|
||||
free_pages_in_block <= max_free_pages)
|
||||
{
|
||||
// found a fully deleted block
|
||||
fs->stats_p_deleted -= deleted_pages_in_block;
|
||||
res = spiffs_gc_erase_block(fs, cur_block);
|
||||
@ -100,43 +116,48 @@ s32_t spiffs_gc_quick(
|
||||
cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs);
|
||||
} // per block
|
||||
|
||||
if (res == SPIFFS_OK) {
|
||||
if (res == SPIFFS_OK)
|
||||
{
|
||||
res = SPIFFS_ERR_NO_DELETED_BLOCKS;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
// Checks if garbage collecting is necessary. If so a candidate block is found,
|
||||
// cleansed and erased
|
||||
s32_t spiffs_gc_check(
|
||||
// Checks if garbage collecting is necessary. If so a candidate block is found,
|
||||
// cleansed and erased
|
||||
s32_t spiffs_gc_check(
|
||||
spiffs *fs,
|
||||
u32_t len) {
|
||||
u32_t len)
|
||||
{
|
||||
s32_t res;
|
||||
s32_t free_pages =
|
||||
(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count-2)
|
||||
(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2)
|
||||
- fs->stats_p_allocated - fs->stats_p_deleted;
|
||||
int tries = 0;
|
||||
|
||||
if (fs->free_blocks > 3 &&
|
||||
(s32_t)len < free_pages * (s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) {
|
||||
(s32_t)len < free_pages * (s32_t)SPIFFS_DATA_PAGE_SIZE(fs))
|
||||
{
|
||||
return SPIFFS_OK;
|
||||
}
|
||||
|
||||
u32_t needed_pages = (len + SPIFFS_DATA_PAGE_SIZE(fs) - 1) / SPIFFS_DATA_PAGE_SIZE(fs);
|
||||
// if (fs->free_blocks <= 2 && (s32_t)needed_pages > free_pages) {
|
||||
// SPIFFS_GC_DBG("gc: full freeblk:" _SPIPRIi " needed:" _SPIPRIi " free:" _SPIPRIi " dele:" _SPIPRIi "\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted);
|
||||
// return SPIFFS_ERR_FULL;
|
||||
// }
|
||||
if ((s32_t)needed_pages > (s32_t)(free_pages + fs->stats_p_deleted)) {
|
||||
// if (fs->free_blocks <= 2 && (s32_t)needed_pages > free_pages) {
|
||||
// SPIFFS_GC_DBG("gc: full freeblk:" _SPIPRIi " needed:" _SPIPRIi " free:" _SPIPRIi " dele:" _SPIPRIi "\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted);
|
||||
// return SPIFFS_ERR_FULL;
|
||||
// }
|
||||
if ((s32_t)needed_pages > (s32_t)(free_pages + fs->stats_p_deleted))
|
||||
{
|
||||
SPIFFS_GC_DBG("gc_check: full freeblk:" _SPIPRIi " needed:" _SPIPRIi " free:" _SPIPRIi " dele:" _SPIPRIi "\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted);
|
||||
return SPIFFS_ERR_FULL;
|
||||
}
|
||||
|
||||
do {
|
||||
do
|
||||
{
|
||||
SPIFFS_GC_DBG("\ngc_check #" _SPIPRIi": run gc free_blocks:" _SPIPRIi " pfree:" _SPIPRIi " pallo:" _SPIPRIi " pdele:" _SPIPRIi " [" _SPIPRIi"] len:" _SPIPRIi " of " _SPIPRIi "\n",
|
||||
tries,
|
||||
fs->free_blocks, free_pages, fs->stats_p_allocated, fs->stats_p_deleted, (free_pages+fs->stats_p_allocated+fs->stats_p_deleted),
|
||||
len, (u32_t)(free_pages*SPIFFS_DATA_PAGE_SIZE(fs)));
|
||||
fs->free_blocks, free_pages, fs->stats_p_allocated, fs->stats_p_deleted, (free_pages + fs->stats_p_allocated + fs->stats_p_deleted),
|
||||
len, (u32_t)(free_pages * SPIFFS_DATA_PAGE_SIZE(fs)));
|
||||
|
||||
spiffs_block_ix *cands;
|
||||
int count;
|
||||
@ -145,7 +166,8 @@ s32_t spiffs_gc_check(
|
||||
// if the fs is crammed, ignore block age when selecting candidate - kind of a bad state
|
||||
res = spiffs_gc_find_candidate(fs, &cands, &count, free_pages <= 0);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
if (count == 0) {
|
||||
if (count == 0)
|
||||
{
|
||||
SPIFFS_GC_DBG("gc_check: no candidates, return\n");
|
||||
return (s32_t)needed_pages < free_pages ? SPIFFS_OK : SPIFFS_ERR_FULL;
|
||||
}
|
||||
@ -157,9 +179,12 @@ s32_t spiffs_gc_check(
|
||||
//SPIFFS_GC_DBG("gcing: cleaning block " _SPIPRIi "\n", cand);
|
||||
res = spiffs_gc_clean(fs, cand);
|
||||
fs->cleaning = 0;
|
||||
if (res < 0) {
|
||||
if (res < 0)
|
||||
{
|
||||
SPIFFS_GC_DBG("gc_check: cleaning block " _SPIPRIi ", result " _SPIPRIi "\n", cand, res);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
SPIFFS_GC_DBG("gc_check: cleaning block " _SPIPRIi ", result " _SPIPRIi "\n", cand, res);
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
@ -174,19 +199,21 @@ s32_t spiffs_gc_check(
|
||||
(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2)
|
||||
- fs->stats_p_allocated - fs->stats_p_deleted;
|
||||
|
||||
if (prev_free_pages <= 0 && prev_free_pages == free_pages) {
|
||||
if (prev_free_pages <= 0 && prev_free_pages == free_pages)
|
||||
{
|
||||
// abort early to reduce wear, at least tried once
|
||||
SPIFFS_GC_DBG("gc_check: early abort, no result on gc when fs crammed\n");
|
||||
break;
|
||||
}
|
||||
|
||||
} while (++tries < SPIFFS_GC_MAX_RUNS && (fs->free_blocks <= 2 ||
|
||||
(s32_t)len > free_pages*(s32_t)SPIFFS_DATA_PAGE_SIZE(fs)));
|
||||
(s32_t)len > free_pages * (s32_t)SPIFFS_DATA_PAGE_SIZE(fs)));
|
||||
|
||||
free_pages =
|
||||
(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2)
|
||||
- fs->stats_p_allocated - fs->stats_p_deleted;
|
||||
if ((s32_t)len > free_pages*(s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) {
|
||||
if ((s32_t)len > free_pages * (s32_t)SPIFFS_DATA_PAGE_SIZE(fs))
|
||||
{
|
||||
res = SPIFFS_ERR_FULL;
|
||||
}
|
||||
|
||||
@ -195,12 +222,13 @@ s32_t spiffs_gc_check(
|
||||
fs->free_blocks, free_pages, tries, res);
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
// Updates page statistics for a block that is about to be erased
|
||||
s32_t spiffs_gc_erase_page_stats(
|
||||
// Updates page statistics for a block that is about to be erased
|
||||
s32_t spiffs_gc_erase_page_stats(
|
||||
spiffs *fs,
|
||||
spiffs_block_ix bix) {
|
||||
spiffs_block_ix bix)
|
||||
{
|
||||
s32_t res = SPIFFS_OK;
|
||||
int obj_lookup_page = 0;
|
||||
int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
|
||||
@ -210,18 +238,25 @@ s32_t spiffs_gc_erase_page_stats(
|
||||
u32_t allo = 0;
|
||||
|
||||
// check each object lookup page
|
||||
while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
|
||||
while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs))
|
||||
{
|
||||
int entry_offset = obj_lookup_page * entries_per_page;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
|
||||
0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
// check each entry
|
||||
while (res == SPIFFS_OK &&
|
||||
cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
|
||||
spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
|
||||
if (obj_id == SPIFFS_OBJ_ID_FREE) {
|
||||
} else if (obj_id == SPIFFS_OBJ_ID_DELETED) {
|
||||
cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)))
|
||||
{
|
||||
spiffs_obj_id obj_id = obj_lu_buf[cur_entry - entry_offset];
|
||||
if (obj_id == SPIFFS_OBJ_ID_FREE)
|
||||
{
|
||||
}
|
||||
else if (obj_id == SPIFFS_OBJ_ID_DELETED)
|
||||
{
|
||||
dele++;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
allo++;
|
||||
}
|
||||
cur_entry++;
|
||||
@ -232,14 +267,15 @@ s32_t spiffs_gc_erase_page_stats(
|
||||
fs->stats_p_allocated -= allo;
|
||||
fs->stats_p_deleted -= dele;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
// Finds block candidates to erase
|
||||
s32_t spiffs_gc_find_candidate(
|
||||
// Finds block candidates to erase
|
||||
s32_t spiffs_gc_find_candidate(
|
||||
spiffs *fs,
|
||||
spiffs_block_ix **block_candidates,
|
||||
int *candidate_count,
|
||||
char fs_crammed) {
|
||||
char fs_crammed)
|
||||
{
|
||||
s32_t res = SPIFFS_OK;
|
||||
u32_t blocks = fs->block_count;
|
||||
spiffs_block_ix cur_block = 0;
|
||||
@ -248,7 +284,7 @@ s32_t spiffs_gc_find_candidate(
|
||||
int cur_entry = 0;
|
||||
|
||||
// using fs->work area as sorted candidate memory, (spiffs_block_ix)cand_bix/(s32_t)score
|
||||
int max_candidates = MIN(fs->block_count, (SPIFFS_CFG_LOG_PAGE_SZ(fs)-8)/(sizeof(spiffs_block_ix) + sizeof(s32_t)));
|
||||
int max_candidates = MIN(fs->block_count, (SPIFFS_CFG_LOG_PAGE_SZ(fs) - 8) / (sizeof(spiffs_block_ix) + sizeof(s32_t)));
|
||||
*candidate_count = 0;
|
||||
memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs));
|
||||
|
||||
@ -264,39 +300,51 @@ s32_t spiffs_gc_find_candidate(
|
||||
int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
|
||||
|
||||
// check each block
|
||||
while (res == SPIFFS_OK && blocks--) {
|
||||
while (res == SPIFFS_OK && blocks--)
|
||||
{
|
||||
u16_t deleted_pages_in_block = 0;
|
||||
u16_t used_pages_in_block = 0;
|
||||
|
||||
int obj_lookup_page = 0;
|
||||
// check each object lookup page
|
||||
while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
|
||||
while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs))
|
||||
{
|
||||
int entry_offset = obj_lookup_page * entries_per_page;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
|
||||
0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
// check each entry
|
||||
while (res == SPIFFS_OK &&
|
||||
cur_entry - entry_offset < entries_per_page &&
|
||||
cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
|
||||
spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
|
||||
if (obj_id == SPIFFS_OBJ_ID_FREE) {
|
||||
cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)))
|
||||
{
|
||||
spiffs_obj_id obj_id = obj_lu_buf[cur_entry - entry_offset];
|
||||
if (obj_id == SPIFFS_OBJ_ID_FREE)
|
||||
{
|
||||
// when a free entry is encountered, scan logic ensures that all following entries are free also
|
||||
res = 1; // kill object lu loop
|
||||
break;
|
||||
} else if (obj_id == SPIFFS_OBJ_ID_DELETED) {
|
||||
}
|
||||
else if (obj_id == SPIFFS_OBJ_ID_DELETED)
|
||||
{
|
||||
deleted_pages_in_block++;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
used_pages_in_block++;
|
||||
}
|
||||
cur_entry++;
|
||||
} // per entry
|
||||
obj_lookup_page++;
|
||||
} // per object lookup page
|
||||
if (res == 1) res = SPIFFS_OK;
|
||||
if (res == 1)
|
||||
{
|
||||
res = SPIFFS_OK;
|
||||
}
|
||||
|
||||
// calculate score and insert into candidate table
|
||||
// stoneage sort, but probably not so many blocks
|
||||
if (res == SPIFFS_OK /*&& deleted_pages_in_block > 0*/) {
|
||||
if (res == SPIFFS_OK /*&& deleted_pages_in_block > 0*/)
|
||||
{
|
||||
// read erase count
|
||||
spiffs_obj_id erase_count;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_C_READ | SPIFFS_OP_T_OBJ_LU2, 0,
|
||||
@ -305,9 +353,12 @@ s32_t spiffs_gc_find_candidate(
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
spiffs_obj_id erase_age;
|
||||
if (fs->max_erase_count > erase_count) {
|
||||
if (fs->max_erase_count > erase_count)
|
||||
{
|
||||
erase_age = fs->max_erase_count - erase_count;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
erase_age = SPIFFS_OBJ_ID_FREE - (erase_count - fs->max_erase_count);
|
||||
}
|
||||
|
||||
@ -317,14 +368,19 @@ s32_t spiffs_gc_find_candidate(
|
||||
erase_age * (fs_crammed ? 0 : SPIFFS_GC_HEUR_W_ERASE_AGE);
|
||||
int cand_ix = 0;
|
||||
SPIFFS_GC_DBG("gc_check: bix:" _SPIPRIbl" del:" _SPIPRIi " use:" _SPIPRIi " score:" _SPIPRIi "\n", cur_block, deleted_pages_in_block, used_pages_in_block, score);
|
||||
while (cand_ix < max_candidates) {
|
||||
if (cand_blocks[cand_ix] == (spiffs_block_ix)-1) {
|
||||
while (cand_ix < max_candidates)
|
||||
{
|
||||
if (cand_blocks[cand_ix] == (spiffs_block_ix) - 1)
|
||||
{
|
||||
cand_blocks[cand_ix] = cur_block;
|
||||
cand_scores[cand_ix] = score;
|
||||
break;
|
||||
} else if (cand_scores[cand_ix] < score) {
|
||||
}
|
||||
else if (cand_scores[cand_ix] < score)
|
||||
{
|
||||
int reorder_cand_ix = max_candidates - 2;
|
||||
while (reorder_cand_ix >= cand_ix) {
|
||||
while (reorder_cand_ix >= cand_ix)
|
||||
{
|
||||
cand_blocks[reorder_cand_ix + 1] = cand_blocks[reorder_cand_ix];
|
||||
cand_scores[reorder_cand_ix + 1] = cand_scores[reorder_cand_ix];
|
||||
reorder_cand_ix--;
|
||||
@ -344,16 +400,18 @@ s32_t spiffs_gc_find_candidate(
|
||||
} // per block
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
typedef enum
|
||||
{
|
||||
FIND_OBJ_DATA,
|
||||
MOVE_OBJ_DATA,
|
||||
MOVE_OBJ_IX,
|
||||
FINISHED
|
||||
} spiffs_gc_clean_state;
|
||||
} spiffs_gc_clean_state;
|
||||
|
||||
typedef struct {
|
||||
typedef struct
|
||||
{
|
||||
spiffs_gc_clean_state state;
|
||||
spiffs_obj_id cur_obj_id;
|
||||
spiffs_span_ix cur_objix_spix;
|
||||
@ -361,22 +419,23 @@ typedef struct {
|
||||
spiffs_page_ix cur_data_pix;
|
||||
int stored_scan_entry_index;
|
||||
u8_t obj_id_found;
|
||||
} spiffs_gc;
|
||||
} spiffs_gc;
|
||||
|
||||
// Empties given block by moving all data into free pages of another block
|
||||
// Strategy:
|
||||
// loop:
|
||||
// scan object lookup for object data pages
|
||||
// for first found id, check spix and load corresponding object index page to memory
|
||||
// push object scan lookup entry index
|
||||
// rescan object lookup, find data pages with same id and referenced by same object index
|
||||
// move data page, update object index in memory
|
||||
// when reached end of lookup, store updated object index
|
||||
// pop object scan lookup entry index
|
||||
// repeat loop until end of object lookup
|
||||
// scan object lookup again for remaining object index pages, move to new page in other block
|
||||
//
|
||||
s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
|
||||
// Empties given block by moving all data into free pages of another block
|
||||
// Strategy:
|
||||
// loop:
|
||||
// scan object lookup for object data pages
|
||||
// for first found id, check spix and load corresponding object index page to memory
|
||||
// push object scan lookup entry index
|
||||
// rescan object lookup, find data pages with same id and referenced by same object index
|
||||
// move data page, update object index in memory
|
||||
// when reached end of lookup, store updated object index
|
||||
// pop object scan lookup entry index
|
||||
// repeat loop until end of object lookup
|
||||
// scan object lookup again for remaining object index pages, move to new page in other block
|
||||
//
|
||||
s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix)
|
||||
{
|
||||
s32_t res = SPIFFS_OK;
|
||||
const int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
|
||||
// this is the global localizer being pushed and popped
|
||||
@ -392,14 +451,16 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
|
||||
memset(&gc, 0, sizeof(spiffs_gc));
|
||||
gc.state = FIND_OBJ_DATA;
|
||||
|
||||
if (fs->free_cursor_block_ix == bix) {
|
||||
if (fs->free_cursor_block_ix == bix)
|
||||
{
|
||||
// move free cursor to next block, cannot use free pages from the block we want to clean
|
||||
fs->free_cursor_block_ix = (bix+1)%fs->block_count;
|
||||
fs->free_cursor_block_ix = (bix + 1) % fs->block_count;
|
||||
fs->free_cursor_obj_lu_entry = 0;
|
||||
SPIFFS_GC_DBG("gc_clean: move free cursor to block " _SPIPRIbl "\n", fs->free_cursor_block_ix);
|
||||
}
|
||||
|
||||
while (res == SPIFFS_OK && gc.state != FINISHED) {
|
||||
while (res == SPIFFS_OK && gc.state != FINISHED)
|
||||
{
|
||||
SPIFFS_GC_DBG("gc_clean: state = " _SPIPRIi " entry:" _SPIPRIi "\n", gc.state, cur_entry);
|
||||
gc.obj_id_found = 0; // reset (to no found data page)
|
||||
|
||||
@ -407,23 +468,27 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
|
||||
int obj_lookup_page = cur_entry / entries_per_page;
|
||||
u8_t scan = 1;
|
||||
// check each object lookup page
|
||||
while (scan && res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
|
||||
while (scan && res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs))
|
||||
{
|
||||
int entry_offset = obj_lookup_page * entries_per_page;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
|
||||
0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
|
||||
SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
// check each object lookup entry
|
||||
while (scan && res == SPIFFS_OK &&
|
||||
cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
|
||||
spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
|
||||
cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)))
|
||||
{
|
||||
spiffs_obj_id obj_id = obj_lu_buf[cur_entry - entry_offset];
|
||||
cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, cur_entry);
|
||||
|
||||
// act upon object id depending on gc state
|
||||
switch (gc.state) {
|
||||
switch (gc.state)
|
||||
{
|
||||
case FIND_OBJ_DATA:
|
||||
// find a data page
|
||||
if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE &&
|
||||
((obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0)) {
|
||||
((obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0))
|
||||
{
|
||||
// found a data page, stop scanning and handle in switch case below
|
||||
SPIFFS_GC_DBG("gc_clean: FIND_DATA state:" _SPIPRIi " - found obj id " _SPIPRIid"\n", gc.state, obj_id);
|
||||
gc.obj_id_found = 1;
|
||||
@ -435,17 +500,22 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
|
||||
case MOVE_OBJ_DATA:
|
||||
// evacuate found data pages for corresponding object index we have in memory,
|
||||
// update memory representation
|
||||
if (obj_id == gc.cur_obj_id) {
|
||||
if (obj_id == gc.cur_obj_id)
|
||||
{
|
||||
spiffs_page_header p_hdr;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA found data page " _SPIPRIid":" _SPIPRIsp" @ " _SPIPRIpg"\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix);
|
||||
if (SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix) != gc.cur_objix_spix) {
|
||||
if (SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix) != gc.cur_objix_spix)
|
||||
{
|
||||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA no objix spix match, take in another run\n");
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
spiffs_page_ix new_data_pix;
|
||||
if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) {
|
||||
if (p_hdr.flags & SPIFFS_PH_FLAG_DELET)
|
||||
{
|
||||
// move page
|
||||
res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_data_pix);
|
||||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA move objix " _SPIPRIid":" _SPIPRIsp" page " _SPIPRIpg" to " _SPIPRIpg"\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix, new_data_pix);
|
||||
@ -455,7 +525,9 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
|
||||
0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
|
||||
SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
// page is deleted but not deleted in lookup, scrap it -
|
||||
// might seem unnecessary as we will erase this block, but
|
||||
// we might get aborted
|
||||
@ -465,11 +537,14 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
|
||||
new_data_pix = SPIFFS_OBJ_ID_FREE;
|
||||
}
|
||||
// update memory representation of object index page with new data page
|
||||
if (gc.cur_objix_spix == 0) {
|
||||
if (gc.cur_objix_spix == 0)
|
||||
{
|
||||
// update object index header page
|
||||
((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[p_hdr.span_ix] = new_data_pix;
|
||||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page " _SPIPRIpg" to objix_hdr entry " _SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix));
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
// update object index page
|
||||
((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)] = new_data_pix;
|
||||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page " _SPIPRIpg" to objix entry " _SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix));
|
||||
@ -480,7 +555,8 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
|
||||
case MOVE_OBJ_IX:
|
||||
// find and evacuate object index pages
|
||||
if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE &&
|
||||
(obj_id & SPIFFS_OBJ_ID_IX_FLAG)) {
|
||||
(obj_id & SPIFFS_OBJ_ID_IX_FLAG))
|
||||
{
|
||||
// found an index object id
|
||||
spiffs_page_header p_hdr;
|
||||
spiffs_page_ix new_pix;
|
||||
@ -488,7 +564,8 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) {
|
||||
if (p_hdr.flags & SPIFFS_PH_FLAG_DELET)
|
||||
{
|
||||
// move page
|
||||
res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_pix);
|
||||
SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX move objix " _SPIPRIid":" _SPIPRIsp" page " _SPIPRIpg" to " _SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix, new_pix);
|
||||
@ -500,13 +577,16 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
|
||||
0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
|
||||
SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
// page is deleted but not deleted in lookup, scrap it -
|
||||
// might seem unnecessary as we will erase this block, but
|
||||
// we might get aborted
|
||||
SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX wipe objix " _SPIPRIid":" _SPIPRIsp" page " _SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix);
|
||||
res = spiffs_page_delete(fs, cur_pix);
|
||||
if (res == SPIFFS_OK) {
|
||||
if (res == SPIFFS_OK)
|
||||
{
|
||||
spiffs_cb_object_event(fs, (spiffs_page_object_ix *)0,
|
||||
SPIFFS_EV_IX_DEL, obj_id, p_hdr.span_ix, cur_pix, 0);
|
||||
}
|
||||
@ -522,12 +602,17 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
|
||||
} // per entry
|
||||
obj_lookup_page++; // no need to check scan variable here, obj_lookup_page is set in start of loop
|
||||
} // per object lookup page
|
||||
if (res != SPIFFS_OK) break;
|
||||
if (res != SPIFFS_OK)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// state finalization and switch
|
||||
switch (gc.state) {
|
||||
switch (gc.state)
|
||||
{
|
||||
case FIND_OBJ_DATA:
|
||||
if (gc.obj_id_found) {
|
||||
if (gc.obj_id_found)
|
||||
{
|
||||
// handle found data page -
|
||||
// find out corresponding obj ix page and load it to memory
|
||||
spiffs_page_header p_hdr;
|
||||
@ -541,7 +626,8 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
|
||||
gc.cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix);
|
||||
SPIFFS_GC_DBG("gc_clean: FIND_DATA find objix span_ix:" _SPIPRIsp"\n", gc.cur_objix_spix);
|
||||
res = spiffs_obj_lu_find_id_and_span(fs, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix, 0, &objix_pix);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
if (res == SPIFFS_ERR_NOT_FOUND)
|
||||
{
|
||||
// on borked systems we might get an ERR_NOT_FOUND here -
|
||||
// this is handled by simply deleting the page as it is not referenced
|
||||
// from anywhere
|
||||
@ -562,25 +648,31 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
|
||||
// check must run or lot of data may be lost
|
||||
SPIFFS_VALIDATE_OBJIX(objix->p_hdr, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix);
|
||||
gc.cur_objix_pix = objix_pix;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
// no more data pages found, passed thru all block, start evacuating object indices
|
||||
gc.state = MOVE_OBJ_IX;
|
||||
cur_entry = 0; // restart entry scan index
|
||||
}
|
||||
break;
|
||||
case MOVE_OBJ_DATA: {
|
||||
case MOVE_OBJ_DATA:
|
||||
{
|
||||
// store modified objix (hdr) page residing in memory now that all
|
||||
// data pages belonging to this object index and residing in the block
|
||||
// we want to evacuate
|
||||
spiffs_page_ix new_objix_pix;
|
||||
gc.state = FIND_OBJ_DATA;
|
||||
cur_entry = gc.stored_scan_entry_index; // pop cursor
|
||||
if (gc.cur_objix_spix == 0) {
|
||||
if (gc.cur_objix_spix == 0)
|
||||
{
|
||||
// store object index header page
|
||||
res = spiffs_object_update_index_hdr(fs, 0, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_pix, fs->work, 0, 0, 0, &new_objix_pix);
|
||||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix_hdr page, " _SPIPRIpg":" _SPIPRIsp"\n", new_objix_pix, 0);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
// store object index page
|
||||
res = spiffs_page_move(fs, 0, fs->work, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, gc.cur_objix_pix, &new_objix_pix);
|
||||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix page, " _SPIPRIpg":" _SPIPRIsp"\n", new_objix_pix, objix->p_hdr.span_ix);
|
||||
@ -603,7 +695,7 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
|
||||
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,112 +1,112 @@
|
||||
/*
|
||||
* spiffs_nucleus.h
|
||||
*
|
||||
* Created on: Jun 15, 2013
|
||||
* Author: petera
|
||||
*/
|
||||
spiffs_nucleus.h
|
||||
|
||||
Created on: Jun 15, 2013
|
||||
Author: petera
|
||||
*/
|
||||
|
||||
/* SPIFFS layout
|
||||
*
|
||||
* spiffs is designed for following spi flash characteristics:
|
||||
* - only big areas of data (blocks) can be erased
|
||||
* - erasing resets all bits in a block to ones
|
||||
* - writing pulls ones to zeroes
|
||||
* - zeroes cannot be pulled to ones, without erase
|
||||
* - wear leveling
|
||||
*
|
||||
* spiffs is also meant to be run on embedded, memory constraint devices.
|
||||
*
|
||||
* Entire area is divided in blocks. Entire area is also divided in pages.
|
||||
* Each block contains same number of pages. A page cannot be erased, but a
|
||||
* block can be erased.
|
||||
*
|
||||
* Entire area must be block_size * x
|
||||
* page_size must be block_size / (2^y) where y > 2
|
||||
*
|
||||
* ex: area = 1024*1024 bytes, block size = 65536 bytes, page size = 256 bytes
|
||||
*
|
||||
* BLOCK 0 PAGE 0 object lookup 1
|
||||
* PAGE 1 object lookup 2
|
||||
* ...
|
||||
* PAGE n-1 object lookup n
|
||||
* PAGE n object data 1
|
||||
* PAGE n+1 object data 2
|
||||
* ...
|
||||
* PAGE n+m-1 object data m
|
||||
*
|
||||
* BLOCK 1 PAGE n+m object lookup 1
|
||||
* PAGE n+m+1 object lookup 2
|
||||
* ...
|
||||
* PAGE 2n+m-1 object lookup n
|
||||
* PAGE 2n+m object data 1
|
||||
* PAGE 2n+m object data 2
|
||||
* ...
|
||||
* PAGE 2n+2m-1 object data m
|
||||
* ...
|
||||
*
|
||||
* n is number of object lookup pages, which is number of pages needed to index all pages
|
||||
* in a block by object id
|
||||
* : block_size / page_size * sizeof(obj_id) / page_size
|
||||
* m is number data pages, which is number of pages in block minus number of lookup pages
|
||||
* : block_size / page_size - block_size / page_size * sizeof(obj_id) / page_size
|
||||
* thus, n+m is total number of pages in a block
|
||||
* : block_size / page_size
|
||||
*
|
||||
* ex: n = 65536/256*2/256 = 2, m = 65536/256 - 2 = 254 => n+m = 65536/256 = 256
|
||||
*
|
||||
* Object lookup pages contain object id entries. Each entry represent the corresponding
|
||||
* data page.
|
||||
* Assuming a 16 bit object id, an object id being 0xffff represents a free page.
|
||||
* An object id being 0x0000 represents a deleted page.
|
||||
*
|
||||
* ex: page 0 : lookup : 0008 0001 0aaa ffff ffff ffff ffff ffff ..
|
||||
* page 1 : lookup : ffff ffff ffff ffff ffff ffff ffff ffff ..
|
||||
* page 2 : data : data for object id 0008
|
||||
* page 3 : data : data for object id 0001
|
||||
* page 4 : data : data for object id 0aaa
|
||||
* ...
|
||||
*
|
||||
*
|
||||
* Object data pages can be either object index pages or object content.
|
||||
* All object data pages contains a data page header, containing object id and span index.
|
||||
* The span index denotes the object page ordering amongst data pages with same object id.
|
||||
* This applies to both object index pages (when index spans more than one page of entries),
|
||||
* and object data pages.
|
||||
* An object index page contains page entries pointing to object content page. The entry index
|
||||
* in a object index page correlates to the span index in the actual object data page.
|
||||
* The first object index page (span index 0) is called object index header page, and also
|
||||
* contains object flags (directory/file), size, object name etc.
|
||||
*
|
||||
* ex:
|
||||
* BLOCK 1
|
||||
* PAGE 256: objectl lookup page 1
|
||||
* [*123] [ 123] [ 123] [ 123]
|
||||
* [ 123] [*123] [ 123] [ 123]
|
||||
* [free] [free] [free] [free] ...
|
||||
* PAGE 257: objectl lookup page 2
|
||||
* [free] [free] [free] [free] ...
|
||||
* PAGE 258: object index page (header)
|
||||
* obj.id:0123 span.ix:0000 flags:INDEX
|
||||
* size:1600 name:ex.txt type:file
|
||||
* [259] [260] [261] [262]
|
||||
* PAGE 259: object data page
|
||||
* obj.id:0123 span.ix:0000 flags:DATA
|
||||
* PAGE 260: object data page
|
||||
* obj.id:0123 span.ix:0001 flags:DATA
|
||||
* PAGE 261: object data page
|
||||
* obj.id:0123 span.ix:0002 flags:DATA
|
||||
* PAGE 262: object data page
|
||||
* obj.id:0123 span.ix:0003 flags:DATA
|
||||
* PAGE 263: object index page
|
||||
* obj.id:0123 span.ix:0001 flags:INDEX
|
||||
* [264] [265] [fre] [fre]
|
||||
* [fre] [fre] [fre] [fre]
|
||||
* PAGE 264: object data page
|
||||
* obj.id:0123 span.ix:0004 flags:DATA
|
||||
* PAGE 265: object data page
|
||||
* obj.id:0123 span.ix:0005 flags:DATA
|
||||
*
|
||||
*/
|
||||
|
||||
spiffs is designed for following spi flash characteristics:
|
||||
- only big areas of data (blocks) can be erased
|
||||
- erasing resets all bits in a block to ones
|
||||
- writing pulls ones to zeroes
|
||||
- zeroes cannot be pulled to ones, without erase
|
||||
- wear leveling
|
||||
|
||||
spiffs is also meant to be run on embedded, memory constraint devices.
|
||||
|
||||
Entire area is divided in blocks. Entire area is also divided in pages.
|
||||
Each block contains same number of pages. A page cannot be erased, but a
|
||||
block can be erased.
|
||||
|
||||
Entire area must be block_size * x
|
||||
page_size must be block_size / (2^y) where y > 2
|
||||
|
||||
ex: area = 1024*1024 bytes, block size = 65536 bytes, page size = 256 bytes
|
||||
|
||||
BLOCK 0 PAGE 0 object lookup 1
|
||||
PAGE 1 object lookup 2
|
||||
...
|
||||
PAGE n-1 object lookup n
|
||||
PAGE n object data 1
|
||||
PAGE n+1 object data 2
|
||||
...
|
||||
PAGE n+m-1 object data m
|
||||
|
||||
BLOCK 1 PAGE n+m object lookup 1
|
||||
PAGE n+m+1 object lookup 2
|
||||
...
|
||||
PAGE 2n+m-1 object lookup n
|
||||
PAGE 2n+m object data 1
|
||||
PAGE 2n+m object data 2
|
||||
...
|
||||
PAGE 2n+2m-1 object data m
|
||||
...
|
||||
|
||||
n is number of object lookup pages, which is number of pages needed to index all pages
|
||||
in a block by object id
|
||||
: block_size / page_size * sizeof(obj_id) / page_size
|
||||
m is number data pages, which is number of pages in block minus number of lookup pages
|
||||
: block_size / page_size - block_size / page_size * sizeof(obj_id) / page_size
|
||||
thus, n+m is total number of pages in a block
|
||||
: block_size / page_size
|
||||
|
||||
ex: n = 65536/256*2/256 = 2, m = 65536/256 - 2 = 254 => n+m = 65536/256 = 256
|
||||
|
||||
Object lookup pages contain object id entries. Each entry represent the corresponding
|
||||
data page.
|
||||
Assuming a 16 bit object id, an object id being 0xffff represents a free page.
|
||||
An object id being 0x0000 represents a deleted page.
|
||||
|
||||
ex: page 0 : lookup : 0008 0001 0aaa ffff ffff ffff ffff ffff ..
|
||||
page 1 : lookup : ffff ffff ffff ffff ffff ffff ffff ffff ..
|
||||
page 2 : data : data for object id 0008
|
||||
page 3 : data : data for object id 0001
|
||||
page 4 : data : data for object id 0aaa
|
||||
...
|
||||
|
||||
|
||||
Object data pages can be either object index pages or object content.
|
||||
All object data pages contains a data page header, containing object id and span index.
|
||||
The span index denotes the object page ordering amongst data pages with same object id.
|
||||
This applies to both object index pages (when index spans more than one page of entries),
|
||||
and object data pages.
|
||||
An object index page contains page entries pointing to object content page. The entry index
|
||||
in a object index page correlates to the span index in the actual object data page.
|
||||
The first object index page (span index 0) is called object index header page, and also
|
||||
contains object flags (directory/file), size, object name etc.
|
||||
|
||||
ex:
|
||||
BLOCK 1
|
||||
PAGE 256: objectl lookup page 1
|
||||
[*123] [ 123] [ 123] [ 123]
|
||||
[ 123] [*123] [ 123] [ 123]
|
||||
[free] [free] [free] [free] ...
|
||||
PAGE 257: objectl lookup page 2
|
||||
[free] [free] [free] [free] ...
|
||||
PAGE 258: object index page (header)
|
||||
obj.id:0123 span.ix:0000 flags:INDEX
|
||||
size:1600 name:ex.txt type:file
|
||||
[259] [260] [261] [262]
|
||||
PAGE 259: object data page
|
||||
obj.id:0123 span.ix:0000 flags:DATA
|
||||
PAGE 260: object data page
|
||||
obj.id:0123 span.ix:0001 flags:DATA
|
||||
PAGE 261: object data page
|
||||
obj.id:0123 span.ix:0002 flags:DATA
|
||||
PAGE 262: object data page
|
||||
obj.id:0123 span.ix:0003 flags:DATA
|
||||
PAGE 263: object index page
|
||||
obj.id:0123 span.ix:0001 flags:INDEX
|
||||
[264] [265] [fre] [fre]
|
||||
[fre] [fre] [fre] [fre]
|
||||
PAGE 264: object data page
|
||||
obj.id:0123 span.ix:0004 flags:DATA
|
||||
PAGE 265: object data page
|
||||
obj.id:0123 span.ix:0005 flags:DATA
|
||||
|
||||
*/
|
||||
#ifndef SPIFFS_NUCLEUS_H_
|
||||
#define SPIFFS_NUCLEUS_H_
|
||||
|
||||
@ -148,14 +148,14 @@ extern "C" {
|
||||
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
/* For GCC and clang */
|
||||
/* For GCC and clang */
|
||||
#define SPIFFS_PACKED __attribute__((packed))
|
||||
#elif defined(__ICCARM__) || defined(__CC_ARM)
|
||||
/* For IAR ARM and Keil MDK-ARM compilers */
|
||||
/* For IAR ARM and Keil MDK-ARM compilers */
|
||||
#define SPIFFS_PACKED
|
||||
|
||||
#else
|
||||
/* Unknown compiler */
|
||||
/* Unknown compiler */
|
||||
#define SPIFFS_PACKED
|
||||
#endif
|
||||
|
||||
@ -342,7 +342,7 @@ extern "C" {
|
||||
if (((ph).flags & SPIFFS_PH_FLAG_INDEX) != 0) return SPIFFS_ERR_NOT_INDEX; \
|
||||
if (((objid) & SPIFFS_OBJ_ID_IX_FLAG) == 0) return SPIFFS_ERR_NOT_INDEX; \
|
||||
if ((ph).span_ix != (spix)) return SPIFFS_ERR_INDEX_SPAN_MISMATCH;
|
||||
//if ((spix) == 0 && ((ph).flags & SPIFFS_PH_FLAG_IXDELE) == 0) return SPIFFS_ERR_DELETED;
|
||||
//if ((spix) == 0 && ((ph).flags & SPIFFS_PH_FLAG_IXDELE) == 0) return SPIFFS_ERR_DELETED;
|
||||
|
||||
#define SPIFFS_VALIDATE_DATA(ph, objid, spix) \
|
||||
if (((ph).flags & SPIFFS_PH_FLAG_USED) != 0) return SPIFFS_ERR_IS_FREE; \
|
||||
@ -402,22 +402,26 @@ extern "C" {
|
||||
((u8_t *)(&((c)->cpages[(ix) * SPIFFS_CACHE_PAGE_SIZE(fs)])) + sizeof(spiffs_cache_page))
|
||||
|
||||
// cache page struct
|
||||
typedef struct {
|
||||
typedef struct
|
||||
{
|
||||
// cache flags
|
||||
u8_t flags;
|
||||
// cache page index
|
||||
u8_t ix;
|
||||
// last access of this cache page
|
||||
u32_t last_access;
|
||||
union {
|
||||
union
|
||||
{
|
||||
// type read cache
|
||||
struct {
|
||||
struct
|
||||
{
|
||||
// read cache page index
|
||||
spiffs_page_ix pix;
|
||||
};
|
||||
#if SPIFFS_CACHE_WR
|
||||
// type write cache
|
||||
struct {
|
||||
struct
|
||||
{
|
||||
// write cache
|
||||
spiffs_obj_id obj_id;
|
||||
// offset in cache page
|
||||
@ -430,7 +434,8 @@ typedef struct {
|
||||
} spiffs_cache_page;
|
||||
|
||||
// cache struct
|
||||
typedef struct {
|
||||
typedef struct
|
||||
{
|
||||
u8_t cpage_count;
|
||||
u32_t last_access;
|
||||
u32_t cpage_use_map;
|
||||
@ -442,7 +447,8 @@ typedef struct {
|
||||
|
||||
|
||||
// spiffs nucleus file descriptor
|
||||
typedef struct {
|
||||
typedef struct
|
||||
{
|
||||
// the filesystem of this descriptor
|
||||
spiffs *fs;
|
||||
// number of file descriptor - if 0, the file descriptor is closed
|
||||
@ -484,7 +490,8 @@ typedef struct {
|
||||
// page header, part of each page except object lookup pages
|
||||
// NB: this is always aligned when the data page is an object index,
|
||||
// as in this case struct spiffs_page_object_ix is used
|
||||
typedef struct SPIFFS_PACKED {
|
||||
typedef struct SPIFFS_PACKED
|
||||
{
|
||||
// object id
|
||||
spiffs_obj_id obj_id;
|
||||
// object span index
|
||||
@ -496,13 +503,13 @@ typedef struct SPIFFS_PACKED {
|
||||
// object index header page header
|
||||
typedef struct SPIFFS_PACKED
|
||||
#if SPIFFS_ALIGNED_OBJECT_INDEX_TABLES
|
||||
__attribute(( aligned(sizeof(spiffs_page_ix)) ))
|
||||
__attribute((aligned(sizeof(spiffs_page_ix))))
|
||||
#endif
|
||||
{
|
||||
// common page header
|
||||
spiffs_page_header p_hdr;
|
||||
// alignment
|
||||
u8_t _align[4 - ((sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3))];
|
||||
u8_t _align[4 - ((sizeof(spiffs_page_header) & 3) == 0 ? 4 : (sizeof(spiffs_page_header) & 3))];
|
||||
// size of object
|
||||
u32_t size;
|
||||
// type of object
|
||||
@ -516,9 +523,10 @@ typedef struct SPIFFS_PACKED
|
||||
} spiffs_page_object_ix_header;
|
||||
|
||||
// object index page header
|
||||
typedef struct SPIFFS_PACKED {
|
||||
typedef struct SPIFFS_PACKED
|
||||
{
|
||||
spiffs_page_header p_hdr;
|
||||
u8_t _align[4 - ((sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3))];
|
||||
u8_t _align[4 - ((sizeof(spiffs_page_header) & 3) == 0 ? 4 : (sizeof(spiffs_page_header) & 3))];
|
||||
} spiffs_page_object_ix;
|
||||
|
||||
// callback func for object lookup visitor
|
||||
|
@ -20,31 +20,35 @@
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
*/
|
||||
#include "spiffs_api.h"
|
||||
|
||||
using namespace fs;
|
||||
|
||||
FileImplPtr SPIFFSImpl::open(const char* path, OpenMode openMode, AccessMode accessMode)
|
||||
{
|
||||
if (!isSpiffsFilenameValid(path)) {
|
||||
if (!isSpiffsFilenameValid(path))
|
||||
{
|
||||
DEBUGV("SPIFFSImpl::open: invalid path=`%s` \r\n", path);
|
||||
return FileImplPtr();
|
||||
}
|
||||
int mode = getSpiffsMode(openMode, accessMode);
|
||||
int fd = SPIFFS_open(&_fs, path, mode, 0);
|
||||
if (fd < 0 && _fs.err_code == SPIFFS_ERR_DELETED && (openMode & OM_CREATE)) {
|
||||
if (fd < 0 && _fs.err_code == SPIFFS_ERR_DELETED && (openMode & OM_CREATE))
|
||||
{
|
||||
DEBUGV("SPIFFSImpl::open: fd=%d path=`%s` openMode=%d accessMode=%d err=%d, trying to remove\r\n",
|
||||
fd, path, openMode, accessMode, _fs.err_code);
|
||||
auto rc = SPIFFS_remove(&_fs, path);
|
||||
if (rc != SPIFFS_OK) {
|
||||
if (rc != SPIFFS_OK)
|
||||
{
|
||||
DEBUGV("SPIFFSImpl::open: SPIFFS_ERR_DELETED, but failed to remove path=`%s` openMode=%d accessMode=%d err=%d\r\n",
|
||||
path, openMode, accessMode, _fs.err_code);
|
||||
return FileImplPtr();
|
||||
}
|
||||
fd = SPIFFS_open(&_fs, path, mode, 0);
|
||||
}
|
||||
if (fd < 0) {
|
||||
if (fd < 0)
|
||||
{
|
||||
DEBUGV("SPIFFSImpl::open: fd=%d path=`%s` openMode=%d accessMode=%d err=%d\r\n",
|
||||
fd, path, openMode, accessMode, _fs.err_code);
|
||||
return FileImplPtr();
|
||||
@ -54,7 +58,8 @@ FileImplPtr SPIFFSImpl::open(const char* path, OpenMode openMode, AccessMode acc
|
||||
|
||||
bool SPIFFSImpl::exists(const char* path)
|
||||
{
|
||||
if (!isSpiffsFilenameValid(path)) {
|
||||
if (!isSpiffsFilenameValid(path))
|
||||
{
|
||||
DEBUGV("SPIFFSImpl::exists: invalid path=`%s` \r\n", path);
|
||||
return false;
|
||||
}
|
||||
@ -65,13 +70,15 @@ bool SPIFFSImpl::exists(const char* path)
|
||||
|
||||
DirImplPtr SPIFFSImpl::openDir(const char* path)
|
||||
{
|
||||
if (strlen(path) > 0 && !isSpiffsFilenameValid(path)) {
|
||||
if (strlen(path) > 0 && !isSpiffsFilenameValid(path))
|
||||
{
|
||||
DEBUGV("SPIFFSImpl::openDir: invalid path=`%s` \r\n", path);
|
||||
return DirImplPtr();
|
||||
}
|
||||
spiffs_DIR dir;
|
||||
spiffs_DIR* result = SPIFFS_opendir(&_fs, path, &dir);
|
||||
if (!result) {
|
||||
if (!result)
|
||||
{
|
||||
DEBUGV("SPIFFSImpl::openDir: path=`%s` err=%d\r\n", path, _fs.err_code);
|
||||
return DirImplPtr();
|
||||
}
|
||||
@ -81,19 +88,24 @@ DirImplPtr SPIFFSImpl::openDir(const char* path)
|
||||
int getSpiffsMode(OpenMode openMode, AccessMode accessMode)
|
||||
{
|
||||
int mode = 0;
|
||||
if (openMode & OM_CREATE) {
|
||||
if (openMode & OM_CREATE)
|
||||
{
|
||||
mode |= SPIFFS_CREAT;
|
||||
}
|
||||
if (openMode & OM_APPEND) {
|
||||
if (openMode & OM_APPEND)
|
||||
{
|
||||
mode |= SPIFFS_APPEND;
|
||||
}
|
||||
if (openMode & OM_TRUNCATE) {
|
||||
if (openMode & OM_TRUNCATE)
|
||||
{
|
||||
mode |= SPIFFS_TRUNC;
|
||||
}
|
||||
if (accessMode & AM_READ) {
|
||||
if (accessMode & AM_READ)
|
||||
{
|
||||
mode |= SPIFFS_RDONLY;
|
||||
}
|
||||
if (accessMode & AM_WRITE) {
|
||||
if (accessMode & AM_WRITE)
|
||||
{
|
||||
mode |= SPIFFS_WRONLY;
|
||||
}
|
||||
return mode;
|
||||
@ -101,7 +113,8 @@ int getSpiffsMode(OpenMode openMode, AccessMode accessMode)
|
||||
|
||||
bool isSpiffsFilenameValid(const char* name)
|
||||
{
|
||||
if (name == nullptr) {
|
||||
if (name == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto len = strlen(name);
|
||||
|
@ -23,15 +23,15 @@
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
*/
|
||||
#include <limits>
|
||||
#include "FS.h"
|
||||
#undef max
|
||||
#undef min
|
||||
#include "FSImpl.h"
|
||||
extern "C" {
|
||||
#include "spiffs/spiffs.h"
|
||||
#include "spiffs/spiffs_nucleus.h"
|
||||
#include "spiffs/spiffs.h"
|
||||
#include "spiffs/spiffs_nucleus.h"
|
||||
};
|
||||
#include "debug.h"
|
||||
#include "flash_utils.h"
|
||||
@ -79,16 +79,19 @@ public:
|
||||
|
||||
bool rename(const char* pathFrom, const char* pathTo) override
|
||||
{
|
||||
if (!isSpiffsFilenameValid(pathFrom)) {
|
||||
if (!isSpiffsFilenameValid(pathFrom))
|
||||
{
|
||||
DEBUGV("SPIFFSImpl::rename: invalid pathFrom=`%s`\r\n", pathFrom);
|
||||
return false;
|
||||
}
|
||||
if (!isSpiffsFilenameValid(pathTo)) {
|
||||
if (!isSpiffsFilenameValid(pathTo))
|
||||
{
|
||||
DEBUGV("SPIFFSImpl::rename: invalid pathTo=`%s` \r\n", pathTo);
|
||||
return false;
|
||||
}
|
||||
auto rc = SPIFFS_rename(&_fs, pathFrom, pathTo);
|
||||
if (rc != SPIFFS_OK) {
|
||||
if (rc != SPIFFS_OK)
|
||||
{
|
||||
DEBUGV("SPIFFS_rename: rc=%d, from=`%s`, to=`%s`\r\n", rc,
|
||||
pathFrom, pathTo);
|
||||
return false;
|
||||
@ -104,7 +107,8 @@ public:
|
||||
info.maxPathLength = SPIFFS_OBJ_NAME_LEN;
|
||||
uint32_t totalBytes, usedBytes;
|
||||
auto rc = SPIFFS_info(&_fs, &totalBytes, &usedBytes);
|
||||
if (rc != SPIFFS_OK) {
|
||||
if (rc != SPIFFS_OK)
|
||||
{
|
||||
DEBUGV("SPIFFS_info: rc=%d, err=%d\r\n", rc, _fs.err_code);
|
||||
return false;
|
||||
}
|
||||
@ -115,12 +119,14 @@ public:
|
||||
|
||||
bool remove(const char* path) override
|
||||
{
|
||||
if (!isSpiffsFilenameValid(path)) {
|
||||
if (!isSpiffsFilenameValid(path))
|
||||
{
|
||||
DEBUGV("SPIFFSImpl::remove: invalid path=`%s`\r\n", path);
|
||||
return false;
|
||||
}
|
||||
auto rc = SPIFFS_remove(&_fs, path);
|
||||
if (rc != SPIFFS_OK) {
|
||||
if (rc != SPIFFS_OK)
|
||||
{
|
||||
DEBUGV("SPIFFS_remove: rc=%d path=`%s`\r\n", rc, path);
|
||||
return false;
|
||||
}
|
||||
@ -141,7 +147,8 @@ public:
|
||||
|
||||
bool setConfig(const FSConfig &cfg) override
|
||||
{
|
||||
if ((cfg._type != SPIFFSConfig::fsid::FSId) || (SPIFFS_mounted(&_fs) != 0)) {
|
||||
if ((cfg._type != SPIFFSConfig::fsid::FSId) || (SPIFFS_mounted(&_fs) != 0))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_cfg = *static_cast<const SPIFFSConfig *>(&cfg);
|
||||
@ -150,31 +157,39 @@ public:
|
||||
|
||||
bool begin() override
|
||||
{
|
||||
if (SPIFFS_mounted(&_fs) != 0) {
|
||||
if (SPIFFS_mounted(&_fs) != 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (_size == 0) {
|
||||
if (_size == 0)
|
||||
{
|
||||
DEBUGV("SPIFFS size is zero");
|
||||
return false;
|
||||
}
|
||||
if (_tryMount()) {
|
||||
if (_tryMount())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (_cfg._autoFormat) {
|
||||
if (_cfg._autoFormat)
|
||||
{
|
||||
auto rc = SPIFFS_format(&_fs);
|
||||
if (rc != SPIFFS_OK) {
|
||||
if (rc != SPIFFS_OK)
|
||||
{
|
||||
DEBUGV("SPIFFS_format: rc=%d, err=%d\r\n", rc, _fs.err_code);
|
||||
return false;
|
||||
}
|
||||
return _tryMount();
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void end() override
|
||||
{
|
||||
if (SPIFFS_mounted(&_fs) == 0) {
|
||||
if (SPIFFS_mounted(&_fs) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
SPIFFS_unmount(&_fs);
|
||||
@ -185,23 +200,27 @@ public:
|
||||
|
||||
bool format() override
|
||||
{
|
||||
if (_size == 0) {
|
||||
if (_size == 0)
|
||||
{
|
||||
DEBUGV("SPIFFS size is zero");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool wasMounted = (SPIFFS_mounted(&_fs) != 0);
|
||||
|
||||
if (_tryMount()) {
|
||||
if (_tryMount())
|
||||
{
|
||||
SPIFFS_unmount(&_fs);
|
||||
}
|
||||
auto rc = SPIFFS_format(&_fs);
|
||||
if (rc != SPIFFS_OK) {
|
||||
if (rc != SPIFFS_OK)
|
||||
{
|
||||
DEBUGV("SPIFFS_format: rc=%d, err=%d\r\n", rc, _fs.err_code);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (wasMounted) {
|
||||
if (wasMounted)
|
||||
{
|
||||
return _tryMount();
|
||||
}
|
||||
|
||||
@ -210,7 +229,7 @@ public:
|
||||
|
||||
bool gc() override
|
||||
{
|
||||
return SPIFFS_gc_quick( &_fs, 0 ) == SPIFFS_OK;
|
||||
return SPIFFS_gc_quick(&_fs, 0) == SPIFFS_OK;
|
||||
}
|
||||
|
||||
protected:
|
||||
@ -237,22 +256,26 @@ protected:
|
||||
config.log_page_size = _pageSize;
|
||||
|
||||
|
||||
if (((uint32_t) std::numeric_limits<spiffs_block_ix>::max()) < (_size / _blockSize)) {
|
||||
if (((uint32_t) std::numeric_limits<spiffs_block_ix>::max()) < (_size / _blockSize))
|
||||
{
|
||||
DEBUGV("spiffs_block_ix type too small");
|
||||
abort();
|
||||
}
|
||||
|
||||
if (((uint32_t) std::numeric_limits<spiffs_page_ix>::max()) < (_size / _pageSize)) {
|
||||
if (((uint32_t) std::numeric_limits<spiffs_page_ix>::max()) < (_size / _pageSize))
|
||||
{
|
||||
DEBUGV("spiffs_page_ix type too small");
|
||||
abort();
|
||||
}
|
||||
|
||||
if (((uint32_t) std::numeric_limits<spiffs_obj_id>::max()) < (2 + (_size / (2*_pageSize))*2)) {
|
||||
if (((uint32_t) std::numeric_limits<spiffs_obj_id>::max()) < (2 + (_size / (2 * _pageSize)) * 2))
|
||||
{
|
||||
DEBUGV("spiffs_obj_id type too small");
|
||||
abort();
|
||||
}
|
||||
|
||||
if (((uint32_t) std::numeric_limits<spiffs_span_ix>::max()) < (_size / _pageSize - 1)) {
|
||||
if (((uint32_t) std::numeric_limits<spiffs_span_ix>::max()) < (_size / _pageSize - 1))
|
||||
{
|
||||
DEBUGV("spiffs_span_ix type too small");
|
||||
abort();
|
||||
}
|
||||
@ -267,7 +290,8 @@ protected:
|
||||
size_t fdsBufSize = SPIFFS_buffer_bytes_for_filedescs(&_fs, _maxOpenFds);
|
||||
size_t cacheBufSize = SPIFFS_buffer_bytes_for_cache(&_fs, _maxOpenFds);
|
||||
|
||||
if (!_workBuf) {
|
||||
if (!_workBuf)
|
||||
{
|
||||
DEBUGV("SPIFFSImpl: allocating %zd+%zd+%zd=%zd bytes\r\n",
|
||||
workBufSize, fdsBufSize, cacheBufSize,
|
||||
workBufSize + fdsBufSize + cacheBufSize);
|
||||
@ -340,7 +364,8 @@ public:
|
||||
CHECKFD();
|
||||
|
||||
auto result = SPIFFS_write(_fs->getFs(), _fd, (void*) buf, size);
|
||||
if (result < 0) {
|
||||
if (result < 0)
|
||||
{
|
||||
DEBUGV("SPIFFS_write rc=%d\r\n", result);
|
||||
return 0;
|
||||
}
|
||||
@ -352,7 +377,8 @@ public:
|
||||
{
|
||||
CHECKFD();
|
||||
auto result = SPIFFS_read(_fs->getFs(), _fd, (void*) buf, size);
|
||||
if (result < 0) {
|
||||
if (result < 0)
|
||||
{
|
||||
DEBUGV("SPIFFS_read rc=%d\r\n", result);
|
||||
return 0;
|
||||
}
|
||||
@ -365,7 +391,8 @@ public:
|
||||
CHECKFD();
|
||||
|
||||
auto rc = SPIFFS_fflush(_fs->getFs(), _fd);
|
||||
if (rc < 0) {
|
||||
if (rc < 0)
|
||||
{
|
||||
DEBUGV("SPIFFS_fflush rc=%d\r\n", rc);
|
||||
}
|
||||
_written = true;
|
||||
@ -376,11 +403,13 @@ public:
|
||||
CHECKFD();
|
||||
|
||||
int32_t offset = static_cast<int32_t>(pos);
|
||||
if (mode == SeekEnd) {
|
||||
if (mode == SeekEnd)
|
||||
{
|
||||
offset = -offset;
|
||||
}
|
||||
auto rc = SPIFFS_lseek(_fs->getFs(), _fd, offset, (int) mode);
|
||||
if (rc < 0) {
|
||||
if (rc < 0)
|
||||
{
|
||||
DEBUGV("SPIFFS_lseek rc=%d\r\n", rc);
|
||||
return false;
|
||||
}
|
||||
@ -393,7 +422,8 @@ public:
|
||||
CHECKFD();
|
||||
|
||||
auto result = SPIFFS_lseek(_fs->getFs(), _fd, 0, SPIFFS_SEEK_CUR);
|
||||
if (result < 0) {
|
||||
if (result < 0)
|
||||
{
|
||||
DEBUGV("SPIFFS_tell rc=%d\r\n", result);
|
||||
return 0;
|
||||
}
|
||||
@ -404,7 +434,8 @@ public:
|
||||
size_t size() const override
|
||||
{
|
||||
CHECKFD();
|
||||
if (_written) {
|
||||
if (_written)
|
||||
{
|
||||
_getStat();
|
||||
}
|
||||
return _stat.size;
|
||||
@ -414,9 +445,12 @@ public:
|
||||
{
|
||||
CHECKFD();
|
||||
spiffs_fd *sfd;
|
||||
if (spiffs_fd_get(_fs->getFs(), _fd, &sfd) == SPIFFS_OK) {
|
||||
if (spiffs_fd_get(_fs->getFs(), _fd, &sfd) == SPIFFS_OK)
|
||||
{
|
||||
return SPIFFS_OK == spiffs_object_truncate(sfd, size, 0);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -458,7 +492,8 @@ protected:
|
||||
{
|
||||
CHECKFD();
|
||||
auto rc = SPIFFS_fstat(_fs->getFs(), _fd, &_stat);
|
||||
if (rc != SPIFFS_OK) {
|
||||
if (rc != SPIFFS_OK)
|
||||
{
|
||||
DEBUGV("SPIFFS_fstat rc=%d\r\n", rc);
|
||||
memset(&_stat, 0, sizeof(_stat));
|
||||
}
|
||||
@ -490,13 +525,15 @@ public:
|
||||
|
||||
FileImplPtr openFile(OpenMode openMode, AccessMode accessMode) override
|
||||
{
|
||||
if (!_valid) {
|
||||
if (!_valid)
|
||||
{
|
||||
return FileImplPtr();
|
||||
}
|
||||
int mode = getSpiffsMode(openMode, accessMode);
|
||||
auto fs = _fs->getFs();
|
||||
spiffs_file fd = SPIFFS_open_by_dirent(fs, &_dirent, mode, 0);
|
||||
if (fd < 0) {
|
||||
if (fd < 0)
|
||||
{
|
||||
DEBUGV("SPIFFSDirImpl::openFile: fd=%d path=`%s` openMode=%d accessMode=%d err=%d\r\n",
|
||||
fd, _dirent.name, openMode, accessMode, fs->err_code);
|
||||
return FileImplPtr();
|
||||
@ -506,7 +543,8 @@ public:
|
||||
|
||||
const char* fileName() override
|
||||
{
|
||||
if (!_valid) {
|
||||
if (!_valid)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -515,7 +553,8 @@ public:
|
||||
|
||||
size_t fileSize() override
|
||||
{
|
||||
if (!_valid) {
|
||||
if (!_valid)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -537,10 +576,11 @@ public:
|
||||
bool next() override
|
||||
{
|
||||
const int n = _pattern.length();
|
||||
do {
|
||||
do
|
||||
{
|
||||
spiffs_dirent* result = SPIFFS_readdir(&_dir, &_dirent);
|
||||
_valid = (result != nullptr);
|
||||
} while(_valid && strncmp((const char*) _dirent.name, _pattern.c_str(), n) != 0);
|
||||
} while (_valid && strncmp((const char*) _dirent.name, _pattern.c_str(), n) != 0);
|
||||
return _valid;
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <stdlib.h>
|
||||
@ -33,29 +33,33 @@ extern "C" {
|
||||
We take care of this by reading first and last words separately and memcpy
|
||||
relevant bytes into result buffer.
|
||||
|
||||
alignment: 012301230123012301230123
|
||||
bytes requested: -------***********------
|
||||
read directly: --------xxxxxxxx--------
|
||||
read pre: ----aaaa----------------
|
||||
read post: ----------------bbbb----
|
||||
alignedBegin: ^
|
||||
alignedEnd: ^
|
||||
alignment: 012301230123012301230123
|
||||
bytes requested: -------***********------
|
||||
read directly: --------xxxxxxxx--------
|
||||
read pre: ----aaaa----------------
|
||||
read post: ----------------bbbb----
|
||||
alignedBegin: ^
|
||||
alignedEnd: ^
|
||||
*/
|
||||
|
||||
int32_t spiffs_hal_read(uint32_t addr, uint32_t size, uint8_t *dst) {
|
||||
int32_t spiffs_hal_read(uint32_t addr, uint32_t size, uint8_t *dst)
|
||||
{
|
||||
optimistic_yield(10000);
|
||||
|
||||
uint32_t result = SPIFFS_OK;
|
||||
uint32_t alignedBegin = (addr + 3) & (~3);
|
||||
uint32_t alignedEnd = (addr + size) & (~3);
|
||||
if (alignedEnd < alignedBegin) {
|
||||
if (alignedEnd < alignedBegin)
|
||||
{
|
||||
alignedEnd = alignedBegin;
|
||||
}
|
||||
|
||||
if (addr < alignedBegin) {
|
||||
if (addr < alignedBegin)
|
||||
{
|
||||
uint32_t nb = alignedBegin - addr;
|
||||
uint32_t tmp;
|
||||
if (!ESP.flashRead(alignedBegin - 4, &tmp, 4)) {
|
||||
if (!ESP.flashRead(alignedBegin - 4, &tmp, 4))
|
||||
{
|
||||
DEBUGV("_spif_read(%d) addr=%x size=%x ab=%x ae=%x\r\n",
|
||||
__LINE__, addr, size, alignedBegin, alignedEnd);
|
||||
return SPIFFS_ERR_INTERNAL;
|
||||
@ -63,19 +67,23 @@ int32_t spiffs_hal_read(uint32_t addr, uint32_t size, uint8_t *dst) {
|
||||
memcpy(dst, ((uint8_t*) &tmp) + 4 - nb, nb);
|
||||
}
|
||||
|
||||
if (alignedEnd != alignedBegin) {
|
||||
if (!ESP.flashRead(alignedBegin, (uint32_t*) (dst + alignedBegin - addr),
|
||||
alignedEnd - alignedBegin)) {
|
||||
if (alignedEnd != alignedBegin)
|
||||
{
|
||||
if (!ESP.flashRead(alignedBegin, (uint32_t*)(dst + alignedBegin - addr),
|
||||
alignedEnd - alignedBegin))
|
||||
{
|
||||
DEBUGV("_spif_read(%d) addr=%x size=%x ab=%x ae=%x\r\n",
|
||||
__LINE__, addr, size, alignedBegin, alignedEnd);
|
||||
return SPIFFS_ERR_INTERNAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (addr + size > alignedEnd) {
|
||||
if (addr + size > alignedEnd)
|
||||
{
|
||||
uint32_t nb = addr + size - alignedEnd;
|
||||
uint32_t tmp;
|
||||
if (!ESP.flashRead(alignedEnd, &tmp, 4)) {
|
||||
if (!ESP.flashRead(alignedEnd, &tmp, 4))
|
||||
{
|
||||
DEBUGV("_spif_read(%d) addr=%x size=%x ab=%x ae=%x\r\n",
|
||||
__LINE__, addr, size, alignedBegin, alignedEnd);
|
||||
return SPIFFS_ERR_INTERNAL;
|
||||
@ -99,45 +107,55 @@ int32_t spiffs_hal_read(uint32_t addr, uint32_t size, uint8_t *dst) {
|
||||
|
||||
static const int UNALIGNED_WRITE_BUFFER_SIZE = 512;
|
||||
|
||||
int32_t spiffs_hal_write(uint32_t addr, uint32_t size, uint8_t *src) {
|
||||
int32_t spiffs_hal_write(uint32_t addr, uint32_t size, uint8_t *src)
|
||||
{
|
||||
optimistic_yield(10000);
|
||||
|
||||
uint32_t alignedBegin = (addr + 3) & (~3);
|
||||
uint32_t alignedEnd = (addr + size) & (~3);
|
||||
if (alignedEnd < alignedBegin) {
|
||||
if (alignedEnd < alignedBegin)
|
||||
{
|
||||
alignedEnd = alignedBegin;
|
||||
}
|
||||
|
||||
if (addr < alignedBegin) {
|
||||
if (addr < alignedBegin)
|
||||
{
|
||||
uint32_t ofs = alignedBegin - addr;
|
||||
uint32_t nb = (size < ofs) ? size : ofs;
|
||||
uint8_t tmp[4] __attribute__((aligned(4))) = {0xff, 0xff, 0xff, 0xff};
|
||||
memcpy(tmp + 4 - ofs, src, nb);
|
||||
if (!ESP.flashWrite(alignedBegin - 4, (uint32_t*) tmp, 4)) {
|
||||
if (!ESP.flashWrite(alignedBegin - 4, (uint32_t*) tmp, 4))
|
||||
{
|
||||
DEBUGV("_spif_write(%d) addr=%x size=%x ab=%x ae=%x\r\n",
|
||||
__LINE__, addr, size, alignedBegin, alignedEnd);
|
||||
return SPIFFS_ERR_INTERNAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (alignedEnd != alignedBegin) {
|
||||
uint32_t* srcLeftover = (uint32_t*) (src + alignedBegin - addr);
|
||||
if (alignedEnd != alignedBegin)
|
||||
{
|
||||
uint32_t* srcLeftover = (uint32_t*)(src + alignedBegin - addr);
|
||||
uint32_t srcAlign = ((uint32_t) srcLeftover) & 3;
|
||||
if (!srcAlign) {
|
||||
if (!srcAlign)
|
||||
{
|
||||
if (!ESP.flashWrite(alignedBegin, (uint32_t*) srcLeftover,
|
||||
alignedEnd - alignedBegin)) {
|
||||
alignedEnd - alignedBegin))
|
||||
{
|
||||
DEBUGV("_spif_write(%d) addr=%x size=%x ab=%x ae=%x\r\n",
|
||||
__LINE__, addr, size, alignedBegin, alignedEnd);
|
||||
return SPIFFS_ERR_INTERNAL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
uint8_t buf[UNALIGNED_WRITE_BUFFER_SIZE];
|
||||
for (uint32_t sizeLeft = alignedEnd - alignedBegin; sizeLeft; ) {
|
||||
for (uint32_t sizeLeft = alignedEnd - alignedBegin; sizeLeft;)
|
||||
{
|
||||
size_t willCopy = std::min(sizeLeft, sizeof(buf));
|
||||
memcpy(buf, srcLeftover, willCopy);
|
||||
|
||||
if (!ESP.flashWrite(alignedBegin, (uint32_t*) buf, willCopy)) {
|
||||
if (!ESP.flashWrite(alignedBegin, (uint32_t*) buf, willCopy))
|
||||
{
|
||||
DEBUGV("_spif_write(%d) addr=%x size=%x ab=%x ae=%x\r\n",
|
||||
__LINE__, addr, size, alignedBegin, alignedEnd);
|
||||
return SPIFFS_ERR_INTERNAL;
|
||||
@ -150,12 +168,14 @@ int32_t spiffs_hal_write(uint32_t addr, uint32_t size, uint8_t *src) {
|
||||
}
|
||||
}
|
||||
|
||||
if (addr + size > alignedEnd) {
|
||||
if (addr + size > alignedEnd)
|
||||
{
|
||||
uint32_t nb = addr + size - alignedEnd;
|
||||
uint32_t tmp = 0xffffffff;
|
||||
memcpy(&tmp, src + size - nb, nb);
|
||||
|
||||
if (!ESP.flashWrite(alignedEnd, &tmp, 4)) {
|
||||
if (!ESP.flashWrite(alignedEnd, &tmp, 4))
|
||||
{
|
||||
DEBUGV("_spif_write(%d) addr=%x size=%x ab=%x ae=%x\r\n",
|
||||
__LINE__, addr, size, alignedBegin, alignedEnd);
|
||||
return SPIFFS_ERR_INTERNAL;
|
||||
@ -165,17 +185,21 @@ int32_t spiffs_hal_write(uint32_t addr, uint32_t size, uint8_t *src) {
|
||||
return SPIFFS_OK;
|
||||
}
|
||||
|
||||
int32_t spiffs_hal_erase(uint32_t addr, uint32_t size) {
|
||||
int32_t spiffs_hal_erase(uint32_t addr, uint32_t size)
|
||||
{
|
||||
if ((size & (SPI_FLASH_SEC_SIZE - 1)) != 0 ||
|
||||
(addr & (SPI_FLASH_SEC_SIZE - 1)) != 0) {
|
||||
(addr & (SPI_FLASH_SEC_SIZE - 1)) != 0)
|
||||
{
|
||||
DEBUGV("_spif_erase called with addr=%x, size=%d\r\n", addr, size);
|
||||
abort();
|
||||
}
|
||||
const uint32_t sector = addr / SPI_FLASH_SEC_SIZE;
|
||||
const uint32_t sectorCount = size / SPI_FLASH_SEC_SIZE;
|
||||
for (uint32_t i = 0; i < sectorCount; ++i) {
|
||||
for (uint32_t i = 0; i < sectorCount; ++i)
|
||||
{
|
||||
optimistic_yield(10000);
|
||||
if (!ESP.flashEraseSector(sector + i)) {
|
||||
if (!ESP.flashEraseSector(sector + i))
|
||||
{
|
||||
DEBUGV("_spif_erase addr=%x size=%d i=%d\r\n", addr, size, i);
|
||||
return SPIFFS_ERR_INTERNAL;
|
||||
}
|
||||
|
@ -4,8 +4,8 @@
|
||||
|
||||
extern "C" {
|
||||
|
||||
uint32_t sqrt32 (uint32_t n)
|
||||
{
|
||||
uint32_t sqrt32(uint32_t n)
|
||||
{
|
||||
// http://www.codecodex.com/wiki/Calculate_an_integer_square_root#C
|
||||
// Another very fast algorithm donated by Tristan.Muntsinger@gmail.com
|
||||
// (note: tested across the full 32 bits range, see comment below)
|
||||
@ -15,27 +15,31 @@ uint32_t sqrt32 (uint32_t n)
|
||||
unsigned int c = 0x8000;
|
||||
unsigned int g = 0x8000;
|
||||
|
||||
for(;;)
|
||||
for (;;)
|
||||
{
|
||||
if (g * g > n)
|
||||
{
|
||||
if (g*g > n)
|
||||
g ^= c;
|
||||
}
|
||||
c >>= 1;
|
||||
if (!c)
|
||||
{
|
||||
return g;
|
||||
}
|
||||
g |= c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* tested with:
|
||||
*
|
||||
/*
|
||||
tested with:
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
|
||||
int main (void)
|
||||
{
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
|
||||
int main (void)
|
||||
{
|
||||
for (uint32_t i = 0; ++i; )
|
||||
{
|
||||
uint32_t sr = sqrt32(i);
|
||||
@ -52,9 +56,9 @@ int main (void)
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
*
|
||||
*/
|
||||
|
||||
};
|
||||
|
@ -23,7 +23,7 @@
|
||||
#define STDLIB_NONISO_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"{
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int atoi(const char *s);
|
||||
@ -32,15 +32,15 @@ long atol(const char* s);
|
||||
|
||||
double atof(const char* s);
|
||||
|
||||
char* itoa (int val, char *s, int radix);
|
||||
char* itoa(int val, char *s, int radix);
|
||||
|
||||
char* ltoa (long val, char *s, int radix);
|
||||
char* ltoa(long val, char *s, int radix);
|
||||
|
||||
char* utoa (unsigned int val, char *s, int radix);
|
||||
char* utoa(unsigned int val, char *s, int radix);
|
||||
|
||||
char* ultoa (unsigned long val, char *s, int radix);
|
||||
char* ultoa(unsigned long val, char *s, int radix);
|
||||
|
||||
char* dtostrf (double val, signed char width, unsigned char prec, char *s);
|
||||
char* dtostrf(double val, signed char width, unsigned char prec, char *s);
|
||||
|
||||
void reverse(char* begin, char* end);
|
||||
|
||||
|
@ -1,20 +1,20 @@
|
||||
/*
|
||||
* time.c - ESP8266-specific functions for SNTP
|
||||
* Copyright (c) 2015 Peter Dobler. All rights reserved.
|
||||
* This file is part of the esp8266 core for Arduino environment.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU Lesser General Public License for more details.
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
time.c - ESP8266-specific functions for SNTP
|
||||
Copyright (c) 2015 Peter Dobler. All rights reserved.
|
||||
This file is part of the esp8266 core for Arduino environment.
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU Lesser General Public License for more details.
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
@ -28,41 +28,42 @@ extern "C" {
|
||||
|
||||
#ifndef _TIMEVAL_DEFINED
|
||||
#define _TIMEVAL_DEFINED
|
||||
struct timeval {
|
||||
struct timeval
|
||||
{
|
||||
time_t tv_sec;
|
||||
suseconds_t tv_usec;
|
||||
};
|
||||
};
|
||||
#endif
|
||||
|
||||
extern char* sntp_asctime(const struct tm *t);
|
||||
extern struct tm* sntp_localtime(const time_t *clock);
|
||||
extern uint64_t micros64();
|
||||
extern void sntp_set_daylight(int daylight);
|
||||
extern char* sntp_asctime(const struct tm *t);
|
||||
extern struct tm* sntp_localtime(const time_t *clock);
|
||||
extern uint64_t micros64();
|
||||
extern void sntp_set_daylight(int daylight);
|
||||
|
||||
// time gap in seconds from 01.01.1900 (NTP time) to 01.01.1970 (UNIX time)
|
||||
// time gap in seconds from 01.01.1900 (NTP time) to 01.01.1970 (UNIX time)
|
||||
#define DIFF1900TO1970 2208988800UL
|
||||
|
||||
bool timeshift64_is_set = false;
|
||||
static uint64_t timeshift64 = 0;
|
||||
bool timeshift64_is_set = false;
|
||||
static uint64_t timeshift64 = 0;
|
||||
|
||||
void tune_timeshift64 (uint64_t now_us)
|
||||
{
|
||||
void tune_timeshift64(uint64_t now_us)
|
||||
{
|
||||
timeshift64 = now_us - micros64();
|
||||
timeshift64_is_set = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void setServer(int id, const char* name_or_ip)
|
||||
{
|
||||
static void setServer(int id, const char* name_or_ip)
|
||||
{
|
||||
if (name_or_ip)
|
||||
{
|
||||
//TODO: check whether server is given by name or IP
|
||||
sntp_setservername(id, (char*) name_or_ip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void configTime(int timezone_sec, int daylightOffset_sec, const char* server1, const char* server2, const char* server3)
|
||||
{
|
||||
void configTime(int timezone_sec, int daylightOffset_sec, const char* server1, const char* server2, const char* server3)
|
||||
{
|
||||
sntp_stop();
|
||||
|
||||
setServer(0, server1);
|
||||
@ -72,40 +73,42 @@ void configTime(int timezone_sec, int daylightOffset_sec, const char* server1, c
|
||||
sntp_set_timezone_in_seconds(timezone_sec);
|
||||
sntp_set_daylight(daylightOffset_sec);
|
||||
sntp_init();
|
||||
}
|
||||
}
|
||||
|
||||
int clock_gettime(clockid_t unused, struct timespec *tp)
|
||||
{
|
||||
int clock_gettime(clockid_t unused, struct timespec *tp)
|
||||
{
|
||||
(void) unused;
|
||||
uint64_t m = micros64();
|
||||
tp->tv_sec = m / 1000000;
|
||||
tp->tv_nsec = (m % 1000000) * 1000;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
time_t time(time_t * t)
|
||||
{
|
||||
time_t time(time_t * t)
|
||||
{
|
||||
time_t seconds = sntp_get_current_timestamp();
|
||||
if (t)
|
||||
{
|
||||
*t = seconds;
|
||||
}
|
||||
return seconds;
|
||||
}
|
||||
}
|
||||
|
||||
int _gettimeofday_r(struct _reent* unused, struct timeval *tp, void *tzp)
|
||||
{
|
||||
int _gettimeofday_r(struct _reent* unused, struct timeval *tp, void *tzp)
|
||||
{
|
||||
(void) unused;
|
||||
(void) tzp;
|
||||
if (tp)
|
||||
{
|
||||
if (!timeshift64_is_set)
|
||||
{
|
||||
tune_timeshift64(sntp_get_current_timestamp() * 1000000ULL);
|
||||
}
|
||||
uint64_t currentTime_us = timeshift64 + micros64();
|
||||
tp->tv_sec = currentTime_us / 1000000ULL;
|
||||
tp->tv_usec = currentTime_us % 1000000ULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -48,8 +48,8 @@ uint8_t twi_status();
|
||||
|
||||
uint8_t twi_transmit(const uint8_t*, uint8_t);
|
||||
|
||||
void twi_attachSlaveRxEvent( void (*)(uint8_t*, size_t) );
|
||||
void twi_attachSlaveTxEvent( void (*)(void) );
|
||||
void twi_attachSlaveRxEvent(void (*)(uint8_t*, size_t));
|
||||
void twi_attachSlaveTxEvent(void (*)(void));
|
||||
void twi_reply(uint8_t);
|
||||
//void twi_stop(void);
|
||||
void twi_releaseBus(void);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user