diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..0e82a1e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,51 @@ +## Contributing to SAMDUE_TimerInterrupt + +### Reporting Bugs + +Please report bugs in SAMDUE_TimerInterrupt if you find them. + +However, before reporting a bug please check through the following: + +* [Existing Open Issues](https://github.com/khoih-prog/SAMDUE_TimerInterrupt/issues) - someone might have already encountered this. + +If you don't find anything, please [open a new issue](https://github.com/khoih-prog/SAMDUE_TimerInterrupt/issues/new). + +### How to submit a bug report + +Please ensure to specify the following: + +* Arduino IDE version (e.g. 1.8.13) or Platform.io version +* `SAMDUE` Core Version (e.g. Arduino SAMDUE_ core v1.6.12) +* Contextual information (e.g. what you were trying to achieve) +* Simplest possible steps to reproduce +* Anything that might be relevant in your opinion, such as: + * Operating system (Windows, Ubuntu, etc.) and the output of `uname -a` + * Network configuration + + +### Example + +``` +Arduino IDE version: 1.8.13 +Arduino SAMDUE Core Version 1.6.12 +OS: Ubuntu 20.04 LTS +Linux xy-Inspiron-3593 5.4.0-51-generic #56-Ubuntu SMP Mon Oct 5 14:28:49 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux + +Context: +I encountered a crash while trying to use the Timer Interrupt. + +Steps to reproduce: +1. ... +2. ... +3. ... +4. ... +``` +### Sending Feature Requests + +Feel free to post feature requests. It's helpful if you can explain exactly why the feature would be useful. + +There are usually some outstanding feature requests in the [existing issues list](https://github.com/khoih-prog/SAMDUE_TimerInterrupt/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement), feel free to add comments to them. + +### Sending Pull Requests + +Pull Requests with changes and fixes are also welcome! diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4a9150f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Khoi Hoang + +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. diff --git a/LibraryPatches/Ethernet/src/Ethernet.cpp b/LibraryPatches/Ethernet/src/Ethernet.cpp new file mode 100644 index 0000000..0f3db05 --- /dev/null +++ b/LibraryPatches/Ethernet/src/Ethernet.cpp @@ -0,0 +1,320 @@ +/**************************************************************************************************************************** + Ethernet.cpp + + EthernetWebServer is a library for the Ethernet shields to run WebServer + + Based on and modified from ESP8266 https://github.com/esp8266/Arduino/releases + Built by Khoi Hoang https://github.com/khoih-prog/EthernetWebServer + Licensed under MIT license + Version: 1.0.9 + + Copyright 2018 Paul Stoffregen + + 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. + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.0 K Hoang 13/02/2020 Initial coding for Arduino Mega, Teensy, etc to support Ethernetx libraries + 1.0.1 K Hoang 20/02/2020 Add support to lambda functions + 1.0.2 K Hoang 20/02/2020 Add support to UIPEthernet library for ENC28J60 + 1.0.3 K Hoang 23/02/2020 Add support to SAM DUE / SAMD21 boards + 1.0.4 K Hoang 16/04/2020 Add support to SAMD51 boards + 1.0.5 K Hoang 24/04/2020 Add support to nRF52 boards, such as AdaFruit Feather nRF52832, nRF52840 Express, BlueFruit Sense, + Itsy-Bitsy nRF52840 Express, Metro nRF52840 Express, NINA_B30_ublox, etc. + More Custom Ethernet libraries supported such as Ethernet2, Ethernet3, EthernetLarge + 1.0.6 K Hoang 27/04/2020 Add support to ESP32/ESP8266 boards + 1.0.7 K Hoang 30/04/2020 Add ENC28J60 support to ESP32/ESP8266 boards + 1.0.8 K Hoang 12/05/2020 Fix W5x00 support for ESP8266 boards. + 1.0.9 K Hoang 15/05/2020 Add EthernetWrapper.h for easier W5x00 support as well as more Ethernet libs in the future. + *****************************************************************************************************************************/ + +#include +#include "Ethernet.h" +#include "utility/w5100.h" +#include "Dhcp.h" + +#define ETHERNET_DEBUG 1 + + +IPAddress EthernetClass::_dnsServerAddress; +DhcpClass* EthernetClass::_dhcp = NULL; + +// KH +void EthernetClass::setRstPin(uint8_t pinRST) +{ + _pinRST = pinRST; + pinMode(_pinRST, OUTPUT); + digitalWrite(_pinRST, HIGH); +} +void EthernetClass::setCsPin(uint8_t pinCS) +{ + _pinCS = pinCS; + W5100.setSS(pinCS); + +#if ( ETHERNET_DEBUG > 0 ) + Serial.print("Input pinCS = "); + Serial.println(pinCS); + Serial.print("_pinCS = "); + Serial.println(_pinCS); +#endif +} + +void EthernetClass::initMaxSockNum(uint8_t maxSockNum) +{ + _maxSockNum = maxSockNum; +} + +uint8_t EthernetClass::softreset() +{ + return W5100.softReset(); +} + +void EthernetClass::hardreset() +{ + if(_pinRST != 0) + { + digitalWrite(_pinRST, LOW); + delay(1); + digitalWrite(_pinRST, HIGH); + delay(150); + } +} + +int EthernetClass::begin(uint8_t *mac, unsigned long timeout, unsigned long responseTimeout) +{ + DhcpClass s_dhcp; + _dhcp = &s_dhcp; + +#if ( ETHERNET_DEBUG > 0 ) + Serial.print("_pinCS = "); + Serial.print(_pinCS); +#endif + + // Initialise the basic info + if (W5100.init() == 0) + return 0; + + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + W5100.setMACAddress(mac); + W5100.setIPAddress(IPAddress(0,0,0,0).raw_address()); + SPI.endTransaction(); + + // Now try to get our config info from a DHCP server + int ret = _dhcp->beginWithDHCP(mac, timeout, responseTimeout); + if (ret == 1) + { + // We've successfully found a DHCP server and got our configuration + // info, so set things accordingly + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + W5100.setIPAddress(_dhcp->getLocalIp().raw_address()); + W5100.setGatewayIp(_dhcp->getGatewayIp().raw_address()); + W5100.setSubnetMask(_dhcp->getSubnetMask().raw_address()); + SPI.endTransaction(); + _dnsServerAddress = _dhcp->getDnsServerIp(); + socketPortRand(micros()); + } + return ret; +} + +void EthernetClass::begin(uint8_t *mac, IPAddress ip) +{ + // Assume the DNS server will be the machine on the same network as the local IP + // but with last octet being '1' + IPAddress dns = ip; + dns[3] = 1; + begin(mac, ip, dns); +} + +void EthernetClass::begin(uint8_t *mac, IPAddress ip, IPAddress dns) +{ + // Assume the gateway will be the machine on the same network as the local IP + // but with last octet being '1' + IPAddress gateway = ip; + gateway[3] = 1; + begin(mac, ip, dns, gateway); +} + +void EthernetClass::begin(uint8_t *mac, IPAddress ip, IPAddress dns, IPAddress gateway) +{ + IPAddress subnet(255, 255, 255, 0); + begin(mac, ip, dns, gateway, subnet); +} + +void EthernetClass::begin(uint8_t *mac, IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet) +{ + // Initialise the basic info + if (W5100.init() == 0) + return; + + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + W5100.setMACAddress(mac); + +#if ( defined(ESP8266) || defined(ESP32) ) + W5100.setIPAddress(ip.raw_address()); + W5100.setGatewayIp(gateway.raw_address()); + W5100.setSubnetMask(subnet.raw_address()); +#elif (ARDUINO > 106 || TEENSYDUINO > 121) + W5100.setIPAddress(ip._address.bytes); + W5100.setGatewayIp(gateway._address.bytes); + W5100.setSubnetMask(subnet._address.bytes); +#else + W5100.setIPAddress(ip._address); + W5100.setGatewayIp(gateway._address); + W5100.setSubnetMask(subnet._address); +#endif + + SPI.endTransaction(); + _dnsServerAddress = dns; +} + +void EthernetClass::init(uint8_t sspin) +{ + W5100.setSS(sspin); +} + +EthernetLinkStatus EthernetClass::linkStatus() +{ + switch (W5100.getLinkStatus()) { + case UNKNOWN: return Unknown; + case LINK_ON: return LinkON; + case LINK_OFF: return LinkOFF; + default: return Unknown; + } +} + +EthernetHardwareStatus EthernetClass::hardwareStatus() +{ + switch (W5100.getChip()) { + case 51: return EthernetW5100; + case 52: return EthernetW5200; + case 55: return EthernetW5500; + default: return EthernetNoHardware; + } +} + +int EthernetClass::maintain() +{ + int rc = DHCP_CHECK_NONE; + if (_dhcp != NULL) { + // we have a pointer to dhcp, use it + rc = _dhcp->checkLease(); + switch (rc) + { + case DHCP_CHECK_NONE: + //nothing done + break; + case DHCP_CHECK_RENEW_OK: + case DHCP_CHECK_REBIND_OK: + //we might have got a new IP. + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + W5100.setIPAddress(_dhcp->getLocalIp().raw_address()); + W5100.setGatewayIp(_dhcp->getGatewayIp().raw_address()); + W5100.setSubnetMask(_dhcp->getSubnetMask().raw_address()); + SPI.endTransaction(); + _dnsServerAddress = _dhcp->getDnsServerIp(); + break; + default: + //this is actually an error, it will retry though + break; + } + } + return rc; +} + + +void EthernetClass::MACAddress(uint8_t *mac_address) +{ + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + W5100.getMACAddress(mac_address); + SPI.endTransaction(); +} + +IPAddress EthernetClass::localIP() +{ + IPAddress ret; + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + W5100.getIPAddress(ret.raw_address()); + SPI.endTransaction(); + return ret; +} + +IPAddress EthernetClass::subnetMask() +{ + IPAddress ret; + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + W5100.getSubnetMask(ret.raw_address()); + SPI.endTransaction(); + return ret; +} + +IPAddress EthernetClass::gatewayIP() +{ + IPAddress ret; + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + W5100.getGatewayIp(ret.raw_address()); + SPI.endTransaction(); + return ret; +} + +void EthernetClass::setMACAddress(const uint8_t *mac_address) +{ + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + W5100.setMACAddress(mac_address); + SPI.endTransaction(); +} + +void EthernetClass::setLocalIP(const IPAddress local_ip) +{ + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + IPAddress ip = local_ip; + W5100.setIPAddress(ip.raw_address()); + SPI.endTransaction(); +} + +void EthernetClass::setSubnetMask(const IPAddress subnet) +{ + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + IPAddress ip = subnet; + W5100.setSubnetMask(ip.raw_address()); + SPI.endTransaction(); +} + +void EthernetClass::setGatewayIP(const IPAddress gateway) +{ + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + IPAddress ip = gateway; + W5100.setGatewayIp(ip.raw_address()); + SPI.endTransaction(); +} + +void EthernetClass::setRetransmissionTimeout(uint16_t milliseconds) +{ + if (milliseconds > 6553) milliseconds = 6553; + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + W5100.setRetransmissionTime(milliseconds * 10); + SPI.endTransaction(); +} + +void EthernetClass::setRetransmissionCount(uint8_t num) +{ + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + W5100.setRetransmissionCount(num); + SPI.endTransaction(); +} + +EthernetClass Ethernet; diff --git a/LibraryPatches/Ethernet/src/Ethernet.h b/LibraryPatches/Ethernet/src/Ethernet.h new file mode 100644 index 0000000..6fee77f --- /dev/null +++ b/LibraryPatches/Ethernet/src/Ethernet.h @@ -0,0 +1,366 @@ +/**************************************************************************************************************************** + Ethernet.h + + EthernetWebServer is a library for the Ethernet shields to run WebServer + + Based on and modified from ESP8266 https://github.com/esp8266/Arduino/releases + Built by Khoi Hoang https://github.com/khoih-prog/EthernetWebServer + Licensed under MIT license + Version: 1.0.9 + + Copyright 2018 Paul Stoffregen + + 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. + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.0 K Hoang 13/02/2020 Initial coding for Arduino Mega, Teensy, etc to support Ethernetx libraries + 1.0.1 K Hoang 20/02/2020 Add support to lambda functions + 1.0.2 K Hoang 20/02/2020 Add support to UIPEthernet library for ENC28J60 + 1.0.3 K Hoang 23/02/2020 Add support to SAM DUE / SAMD21 boards + 1.0.4 K Hoang 16/04/2020 Add support to SAMD51 boards + 1.0.5 K Hoang 24/04/2020 Add support to nRF52 boards, such as AdaFruit Feather nRF52832, nRF52840 Express, BlueFruit Sense, + Itsy-Bitsy nRF52840 Express, Metro nRF52840 Express, NINA_B30_ublox, etc. + More Custom Ethernet libraries supported such as Ethernet2, Ethernet3, EthernetLarge + 1.0.6 K Hoang 27/04/2020 Add support to ESP32/ESP8266 boards + 1.0.7 K Hoang 30/04/2020 Add ENC28J60 support to ESP32/ESP8266 boards + 1.0.8 K Hoang 12/05/2020 Fix W5x00 support for ESP8266 boards. + 1.0.9 K Hoang 15/05/2020 Add EthernetWrapper.h for easier W5x00 support as well as more Ethernet libs in the future. + *****************************************************************************************************************************/ + +#ifndef ethernet_h_ +#define ethernet_h_ + +// All symbols exposed to Arduino sketches are contained in this header file +// +// Older versions had much of this stuff in EthernetClient.h, EthernetServer.h, +// and socket.h. Including headers in different order could cause trouble, so +// these "friend" classes are now defined in the same header file. socket.h +// was removed to avoid possible conflict with the C library header files. + + +// Configure the maximum number of sockets to support. W5100 chips can have +// up to 4 sockets. W5200 & W5500 can have up to 8 sockets. Several bytes +// of RAM are used for each socket. Reducing the maximum can save RAM, but +// you are limited to fewer simultaneous connections. +#if defined(RAMEND) && defined(RAMSTART) && ((RAMEND - RAMSTART) <= 2048) +#define MAX_SOCK_NUM 2 //Reduce MAX_SOCK_NUM to 2 from 4, to increase buffer from 2k to 4K +#else +#define MAX_SOCK_NUM 4 //Reduce MAX_SOCK_NUM to 4 from 8, to increase buffer from 2k to 4K +#endif + +// By default, each socket uses 2K buffers inside the Wiznet chip. If +// MAX_SOCK_NUM is set to fewer than the chip's maximum, uncommenting +// this will use larger buffers within the Wiznet chip. Large buffers +// can really help with UDP protocols like Artnet. In theory larger +// buffers should allow faster TCP over high-latency links, but this +// does not always seem to work in practice (maybe Wiznet bugs?) +// +//KH, to increase buffer size for W5x00 to min 4K +#define ETHERNET_LARGE_BUFFERS + + +#include +#include "Client.h" +#include "Server.h" +#include "Udp.h" + +enum EthernetLinkStatus { + Unknown, + LinkON, + LinkOFF +}; + +enum EthernetHardwareStatus { + EthernetNoHardware, + EthernetW5100, + EthernetW5200, + EthernetW5500 +}; + +class EthernetUDP; +class EthernetClient; +class EthernetServer; +class DhcpClass; + +class EthernetClass { +private: + static IPAddress _dnsServerAddress; + static DhcpClass* _dhcp; +public: + // KH + uint8_t _maxSockNum; + uint8_t _pinCS; + uint8_t _pinRST; + + void setRstPin(uint8_t pinRST = 9); // for WIZ550io or USR-ES1, must set befor Ethernet.begin + void setCsPin(uint8_t pinCS = 10); // must set befor Ethernet.begin + + // Initialize with less sockets but more RX/TX Buffer + // maxSockNum = 1 Socket 0 -> RX/TX Buffer 16k + // maxSockNum = 2 Socket 0, 1 -> RX/TX Buffer 8k + // maxSockNum = 4 Socket 0...3 -> RX/TX Buffer 4k + // maxSockNum = 8 (Standard) all sockets -> RX/TX Buffer 2k + // be carefull of the MAX_SOCK_NUM, because in the moment it can't dynamicly changed + void initMaxSockNum(uint8_t maxSockNum = 8); + + uint8_t softreset(); // can set only after Ethernet.begin + void hardreset(); // You need to set the Rst pin + + // Initialise the Ethernet shield to use the provided MAC address and + // gain the rest of the configuration through DHCP. + // Returns 0 if the DHCP configuration failed, and 1 if it succeeded + int begin(uint8_t *mac, unsigned long timeout = 60000, unsigned long responseTimeout = 4000); + int maintain(); + EthernetLinkStatus linkStatus(); + EthernetHardwareStatus hardwareStatus(); + + // Manual configuration + void begin(uint8_t *mac, IPAddress ip); + void begin(uint8_t *mac, IPAddress ip, IPAddress dns); + void begin(uint8_t *mac, IPAddress ip, IPAddress dns, IPAddress gateway); + void begin(uint8_t *mac, IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet); + void init(uint8_t sspin = 10); + + void MACAddress(uint8_t *mac_address); + IPAddress localIP(); + IPAddress subnetMask(); + IPAddress gatewayIP(); + IPAddress dnsServerIP() { return _dnsServerAddress; } + + void setMACAddress(const uint8_t *mac_address); + void setLocalIP(const IPAddress local_ip); + void setSubnetMask(const IPAddress subnet); + void setGatewayIP(const IPAddress gateway); + void setDnsServerIP(const IPAddress dns_server) { _dnsServerAddress = dns_server; } + void setRetransmissionTimeout(uint16_t milliseconds); + void setRetransmissionCount(uint8_t num); + + friend class EthernetClient; + friend class EthernetServer; + friend class EthernetUDP; +private: + // Opens a socket(TCP or UDP or IP_RAW mode) + uint8_t socketBegin(uint8_t protocol, uint16_t port); + uint8_t socketBeginMulticast(uint8_t protocol, IPAddress ip,uint16_t port); + uint8_t socketStatus(uint8_t s); + // Close socket + void socketClose(uint8_t s); + // Establish TCP connection (Active connection) + void socketConnect(uint8_t s, uint8_t * addr, uint16_t port); + // disconnect the connection + void socketDisconnect(uint8_t s); + // Establish TCP connection (Passive connection) + uint8_t socketListen(uint8_t s); + // Send data (TCP) + uint16_t socketSend(uint8_t s, const uint8_t * buf, uint16_t len); + uint16_t socketSendAvailable(uint8_t s); + // Receive data (TCP) + int socketRecv(uint8_t s, uint8_t * buf, int16_t len); + uint16_t socketRecvAvailable(uint8_t s); + uint8_t socketPeek(uint8_t s); + // sets up a UDP datagram, the data for which will be provided by one + // or more calls to bufferData and then finally sent with sendUDP. + // return true if the datagram was successfully set up, or false if there was an error + bool socketStartUDP(uint8_t s, uint8_t* addr, uint16_t port); + // copy up to len bytes of data from buf into a UDP datagram to be + // sent later by sendUDP. Allows datagrams to be built up from a series of bufferData calls. + // return Number of bytes successfully buffered + uint16_t socketBufferData(uint8_t s, uint16_t offset, const uint8_t* buf, uint16_t len); + // Send a UDP datagram built up from a sequence of startUDP followed by one or more + // calls to bufferData. + // return true if the datagram was successfully sent, or false if there was an error + bool socketSendUDP(uint8_t s); + // Initialize the "random" source port number + void socketPortRand(uint16_t n); +}; + +extern EthernetClass Ethernet; + + +#define UDP_TX_PACKET_MAX_SIZE 24 + +class EthernetUDP : public UDP { +private: + uint16_t _port; // local port to listen on + IPAddress _remoteIP; // remote IP address for the incoming packet whilst it's being processed + uint16_t _remotePort; // remote port for the incoming packet whilst it's being processed + uint16_t _offset; // offset into the packet being sent + +protected: + uint8_t sockindex; + uint16_t _remaining; // remaining bytes of incoming packet yet to be processed + +public: + EthernetUDP() : sockindex(MAX_SOCK_NUM) {} // Constructor + virtual uint8_t begin(uint16_t); // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use + virtual uint8_t beginMulticast(IPAddress, uint16_t); // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use + virtual void stop(); // 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); + // 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); + // Finish off this packet and send it + // Returns 1 if the packet was sent successfully, 0 if there was an error + virtual int endPacket(); + // Write a single byte into the packet + virtual size_t write(uint8_t); + // Write size bytes from buffer into the packet + virtual size_t write(const uint8_t *buffer, size_t size); + + using Print::write; + + // 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(); + // Number of bytes remaining in the current packet + virtual int available(); + // Read a single byte from the current packet + virtual int read(); + // 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); + // 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) { return read((unsigned char*)buffer, len); }; + // Return the next byte from the current packet without moving on to the next byte + virtual int peek(); + virtual void flush(); // Finish reading the current packet + + // Return the IP address of the host who sent the current incoming packet + virtual IPAddress remoteIP() { return _remoteIP; }; + // Return the port of the host who sent the current incoming packet + virtual uint16_t remotePort() { return _remotePort; }; + virtual uint16_t localPort() { return _port; } +}; + + + + +class EthernetClient : public Client { +public: + + EthernetClient() : sockindex(MAX_SOCK_NUM), _timeout(5000) { } + EthernetClient(uint8_t s) : sockindex(s), _timeout(5000) { } + + uint8_t status(); + virtual int connect(IPAddress ip, uint16_t port); + virtual int connect(const char *host, uint16_t port); + virtual int availableForWrite(void); + virtual size_t write(uint8_t); + virtual size_t write(const uint8_t *buf, size_t size); + + virtual int available(); + virtual int read(); + virtual int read(uint8_t *buf, size_t size); + virtual int peek(); + virtual void flush(); + virtual void stop(); + virtual uint8_t connected(); + virtual operator bool() { return sockindex < MAX_SOCK_NUM; } + virtual bool operator==(const bool value) { return bool() == value; } + virtual bool operator!=(const bool value) { return bool() != value; } + virtual bool operator==(const EthernetClient&); + virtual bool operator!=(const EthernetClient& rhs) { return !this->operator==(rhs); } + uint8_t getSocketNumber() const { return sockindex; } + virtual uint16_t localPort(); + virtual IPAddress remoteIP(); + virtual uint16_t remotePort(); + virtual void setConnectionTimeout(uint16_t timeout) { _timeout = timeout; } + + friend class EthernetServer; + + using Print::write; + +private: + uint8_t sockindex; // MAX_SOCK_NUM means client not in use + uint16_t _timeout; +}; + + +class EthernetServer : public Server { +private: + uint16_t _port; +public: + EthernetServer(uint16_t port) : _port(port) { } + EthernetClient available(); + EthernetClient accept(); + virtual void begin(); + virtual size_t write(uint8_t); + virtual size_t write(const uint8_t *buf, size_t size); + virtual operator bool(); + using Print::write; + //void statusreport(); + + // TODO: make private when socket allocation moves to EthernetClass + static uint16_t server_port[MAX_SOCK_NUM]; +}; + + +class DhcpClass { +private: + uint32_t _dhcpInitialTransactionId; + uint32_t _dhcpTransactionId; + uint8_t _dhcpMacAddr[6]; +#ifdef __arm__ + uint8_t _dhcpLocalIp[4] __attribute__((aligned(4))); + uint8_t _dhcpSubnetMask[4] __attribute__((aligned(4))); + uint8_t _dhcpGatewayIp[4] __attribute__((aligned(4))); + uint8_t _dhcpDhcpServerIp[4] __attribute__((aligned(4))); + uint8_t _dhcpDnsServerIp[4] __attribute__((aligned(4))); +#else + uint8_t _dhcpLocalIp[4]; + uint8_t _dhcpSubnetMask[4]; + uint8_t _dhcpGatewayIp[4]; + uint8_t _dhcpDhcpServerIp[4]; + uint8_t _dhcpDnsServerIp[4]; +#endif + uint32_t _dhcpLeaseTime; + uint32_t _dhcpT1, _dhcpT2; + uint32_t _renewInSec; + uint32_t _rebindInSec; + unsigned long _timeout; + unsigned long _responseTimeout; + unsigned long _lastCheckLeaseMillis; + uint8_t _dhcp_state; + EthernetUDP _dhcpUdpSocket; + + int request_DHCP_lease(); + void reset_DHCP_lease(); + void presend_DHCP(); + void send_DHCP_MESSAGE(uint8_t, uint16_t); + void printByte(char *, uint8_t); + + uint8_t parseDHCPResponse(unsigned long responseTimeout, uint32_t& transactionId); +public: + IPAddress getLocalIp(); + IPAddress getSubnetMask(); + IPAddress getGatewayIp(); + IPAddress getDhcpServerIp(); + IPAddress getDnsServerIp(); + + int beginWithDHCP(uint8_t *, unsigned long timeout = 60000, unsigned long responseTimeout = 4000); + int checkLease(); +}; + +#endif diff --git a/LibraryPatches/Ethernet/src/EthernetServer.cpp b/LibraryPatches/Ethernet/src/EthernetServer.cpp new file mode 100644 index 0000000..2f6b8fb --- /dev/null +++ b/LibraryPatches/Ethernet/src/EthernetServer.cpp @@ -0,0 +1,244 @@ +/**************************************************************************************************************************** + EthernetServer.cpp + + EthernetWebServer is a library for the Ethernet shields to run WebServer + + Based on and modified from ESP8266 https://github.com/esp8266/Arduino/releases + Built by Khoi Hoang https://github.com/khoih-prog/EthernetWebServer + Licensed under MIT license + Version: 1.0.9 + + Copyright 2018 Paul Stoffregen + + 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. + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.0 K Hoang 13/02/2020 Initial coding for Arduino Mega, Teensy, etc to support Ethernetx libraries + 1.0.1 K Hoang 20/02/2020 Add support to lambda functions + 1.0.2 K Hoang 20/02/2020 Add support to UIPEthernet library for ENC28J60 + 1.0.3 K Hoang 23/02/2020 Add support to SAM DUE / SAMD21 boards + 1.0.4 K Hoang 16/04/2020 Add support to SAMD51 boards + 1.0.5 K Hoang 24/04/2020 Add support to nRF52 boards, such as AdaFruit Feather nRF52832, nRF52840 Express, BlueFruit Sense, + Itsy-Bitsy nRF52840 Express, Metro nRF52840 Express, NINA_B30_ublox, etc. + More Custom Ethernet libraries supported such as Ethernet2, Ethernet3, EthernetLarge + 1.0.6 K Hoang 27/04/2020 Add support to ESP32/ESP8266 boards + 1.0.7 K Hoang 30/04/2020 Add ENC28J60 support to ESP32/ESP8266 boards + 1.0.8 K Hoang 12/05/2020 Fix W5x00 support for ESP8266 boards. + 1.0.9 K Hoang 15/05/2020 Add EthernetWrapper.h for easier W5x00 support as well as more Ethernet libs in the future. + *****************************************************************************************************************************/ + +#include +#include "Ethernet.h" +#include "utility/w5100.h" + +uint16_t EthernetServer::server_port[MAX_SOCK_NUM]; + + +void EthernetServer::begin() +{ + uint8_t sockindex = Ethernet.socketBegin(SnMR::TCP, _port); + if (sockindex < MAX_SOCK_NUM) + { + if (Ethernet.socketListen(sockindex)) + { + server_port[sockindex] = _port; + } + else + { + Ethernet.socketDisconnect(sockindex); + } + } +} + +EthernetClient EthernetServer::available() +{ + bool listening = false; + uint8_t sockindex = MAX_SOCK_NUM; + uint8_t chip, maxindex=MAX_SOCK_NUM; + + chip = W5100.getChip(); + + if (!chip) + return EthernetClient(MAX_SOCK_NUM); + + //KH, set W5100 to max 2 sockets to increase buffer size + if (chip == 51) + { +#ifdef ETHERNET_LARGE_BUFFERS + maxindex = 2; // W5100 chip never supports more than 4 sockets +#else + maxindex = 4; // W5100 chip never supports more than 4 sockets. Original +#endif + } + + for (uint8_t i=0; i < maxindex; i++) + { + if (server_port[i] == _port) + { + uint8_t stat = Ethernet.socketStatus(i); + if (stat == SnSR::ESTABLISHED || stat == SnSR::CLOSE_WAIT) + { + if (Ethernet.socketRecvAvailable(i) > 0) + { + sockindex = i; + } + else + { + // remote host closed connection, our end still open + if (stat == SnSR::CLOSE_WAIT) + { + Ethernet.socketDisconnect(i); + // status becomes LAST_ACK for short time + } + } + } + else if (stat == SnSR::LISTEN) + { + listening = true; + } + else if (stat == SnSR::CLOSED) + { + server_port[i] = 0; + } + } + } + + if (!listening) + { + begin(); + } + + return EthernetClient(sockindex); +} + +EthernetClient EthernetServer::accept() +{ + bool listening = false; + uint8_t sockindex = MAX_SOCK_NUM; + uint8_t chip, maxindex=MAX_SOCK_NUM; + + chip = W5100.getChip(); + + if (!chip) + return EthernetClient(MAX_SOCK_NUM); + + //KH, set W5100 to max 2 sockets to increase buffer size + if (chip == 51) + { +#ifdef ETHERNET_LARGE_BUFFERS + maxindex = 2; // W5100 chip never supports more than 4 sockets +#else + maxindex = 4; // W5100 chip never supports more than 4 sockets. Original +#endif + } + + for (uint8_t i=0; i < maxindex; i++) + { + if (server_port[i] == _port) + { + uint8_t stat = Ethernet.socketStatus(i); + + if (sockindex == MAX_SOCK_NUM && (stat == SnSR::ESTABLISHED || stat == SnSR::CLOSE_WAIT)) + { + // Return the connected client even if no data received. + // Some protocols like FTP expect the server to send the + // first data. + sockindex = i; + server_port[i] = 0; // only return the client once + } + else if (stat == SnSR::LISTEN) + { + listening = true; + } + else if (stat == SnSR::CLOSED) + { + server_port[i] = 0; + } + } + } + + if (!listening) + begin(); + + return EthernetClient(sockindex); +} + +EthernetServer::operator bool() +{ + uint8_t maxindex=MAX_SOCK_NUM; + + //KH, set W5100 to max 2 sockets to increase buffer size + if (W5100.getChip() == 51) + { +#ifdef ETHERNET_LARGE_BUFFERS + maxindex = 2; // W5100 chip never supports more than 4 sockets +#else + maxindex = 4; // W5100 chip never supports more than 4 sockets. Original +#endif + } + + for (uint8_t i=0; i < maxindex; i++) + { + if (server_port[i] == _port) + { + if (Ethernet.socketStatus(i) == SnSR::LISTEN) + { + return true; // server is listening for incoming clients + } + } + } + return false; +} + +size_t EthernetServer::write(uint8_t b) +{ + return write(&b, 1); +} + +size_t EthernetServer::write(const uint8_t *buffer, size_t size) +{ + uint8_t chip, maxindex=MAX_SOCK_NUM; + + chip = W5100.getChip(); + if (!chip) return 0; + + //KH, set W5100 to max 2 sockets to increase buffer size + if (chip == 51) + { +#ifdef ETHERNET_LARGE_BUFFERS + maxindex = 2; // W5100 chip never supports more than 4 sockets +#else + maxindex = 4; // W5100 chip never supports more than 4 sockets. Original +#endif + } + + available(); + + for (uint8_t i=0; i < maxindex; i++) + { + if (server_port[i] == _port) + { + if (Ethernet.socketStatus(i) == SnSR::ESTABLISHED) + { + Ethernet.socketSend(i, buffer, size); + } + } + } + return size; +} diff --git a/LibraryPatches/Ethernet/src/utility/w5100.cpp b/LibraryPatches/Ethernet/src/utility/w5100.cpp new file mode 100644 index 0000000..5cf9cf7 --- /dev/null +++ b/LibraryPatches/Ethernet/src/utility/w5100.cpp @@ -0,0 +1,711 @@ +/**************************************************************************************************************************** + w5100.cpp - Driver for W5x00 + + EthernetWebServer is a library for the Ethernet shields to run WebServer + + Based on and modified from ESP8266 https://github.com/esp8266/Arduino/releases + Built by Khoi Hoang https://github.com/khoih-prog/EthernetWebServer + Licensed under MIT license + Version: 1.0.9 + + Copyright 2018 Paul Stoffregen + + 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. + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.0 K Hoang 13/02/2020 Initial coding for Arduino Mega, Teensy, etc to support Ethernetx libraries + 1.0.1 K Hoang 20/02/2020 Add support to lambda functions + 1.0.2 K Hoang 20/02/2020 Add support to UIPEthernet library for ENC28J60 + 1.0.3 K Hoang 23/02/2020 Add support to SAM DUE / SAMD21 boards + 1.0.4 K Hoang 16/04/2020 Add support to SAMD51 boards + 1.0.5 K Hoang 24/04/2020 Add support to nRF52 boards, such as AdaFruit Feather nRF52832, nRF52840 Express, BlueFruit Sense, + Itsy-Bitsy nRF52840 Express, Metro nRF52840 Express, NINA_B30_ublox, etc. + More Custom Ethernet libraries supported such as Ethernet2, Ethernet3, EthernetLarge + 1.0.6 K Hoang 27/04/2020 Add support to ESP32/ESP8266 boards + 1.0.7 K Hoang 30/04/2020 Add ENC28J60 support to ESP32/ESP8266 boards + 1.0.8 K Hoang 12/05/2020 Fix W5x00 support for ESP8266 boards. + 1.0.9 K Hoang 15/05/2020 Add EthernetWrapper.h for easier W5x00 support as well as more Ethernet libs in the future. + *****************************************************************************************************************************/ + +#include +#include "Ethernet.h" +#include "w5100.h" + +#define W5100_DEBUG 1 + +/***************************************************/ +/** Default SS pin setting **/ +/***************************************************/ + +// If variant.h or other headers specifically define the +// default SS pin for ethernet, use it. +#if defined(PIN_SPI_SS_ETHERNET_LIB) + +#define SS_PIN_DEFAULT PIN_SPI_SS_ETHERNET_LIB +//KH +#warning w5100.cpp Use PIN_SPI_SS_ETHERNET_LIB defined, change SS_PIN_DEFAULT to PIN_SPI_SS_ETHERNET_LIB + +// MKR boards default to pin 5 for MKR ETH +// Pins 8-10 are MOSI/SCK/MISO on MRK, so don't use pin 10 +#elif defined(USE_ARDUINO_MKR_PIN_LAYOUT) || defined(ARDUINO_SAMD_MKRZERO) || defined(ARDUINO_SAMD_MKR1000) || defined(ARDUINO_SAMD_MKRFox1200) || defined(ARDUINO_SAMD_MKRGSM1400) || defined(ARDUINO_SAMD_MKRWAN1300) + +#define SS_PIN_DEFAULT 5 +//KH +#warning w5100.cpp Use MKR, change SS_PIN_DEFAULT to 5 + +// For boards using AVR, assume shields with SS on pin 10 +// will be used. This allows for Arduino Mega (where +// SS is pin 53) and Arduino Leonardo (where SS is pin 17) +// to work by default with Arduino Ethernet Shield R2 & R3. +#elif defined(__AVR__) + +#define SS_PIN_DEFAULT 10 +//KH +#warning w5100.cpp Use __AVR__, change SS_PIN_DEFAULT to 10 + +// If variant.h or other headers define these names +// use them if none of the other cases match +#elif defined(PIN_SPI_SS) + +#if defined(__SAMD21G18A__) +//10 - 2 (6 conflict) all not OK for Nano 33 IoT !!! SPI corrupted??? +#warning w5100.cpp Use __SAMD21G18A__, change SS_PIN_DEFAULT to 10 +#define SS_PIN_DEFAULT 10 +#else +#define SS_PIN_DEFAULT PIN_SPI_SS + +//KH +#warning w5100.cpp Use PIN_SPI_SS defined, change SS_PIN_DEFAULT to PIN_SPI_SS +#endif + +#elif defined(CORE_SS0_PIN) +#define SS_PIN_DEFAULT CORE_SS0_PIN + +//KH +#warning w5100.cpp Use CORE_SS0_PIN defined, change SS_PIN_DEFAULT to CORE_SS0_PIN + +//KH for ESP32 +#elif defined(ESP32) +//pin SS already defined in ESP32 as pin 5, don't use this as conflict with SPIFFS, EEPROM, etc. +// Use in GPIO22 +#warning w5100.cpp Use ESP32, change SS_PIN_DEFAULT to GPIO22, MOSI(23), MISO(19), SCK(18) +#define SS_PIN_DEFAULT 22 //SS +/////// + +//KH for ESP8266 +#elif defined(ESP8266) +//pin SS already defined in ESP8266 as pin 15. Conflict => Move to pin GPIO4 (D2) +#warning w5100.cpp Use ESP8266, change SS_PIN_DEFAULT to SS(4), MOSI(13), MISO(12), SCK(14) +#define SS_PIN_DEFAULT D2 // GPIO4, SS + +/////// + +// As a final fallback, use pin 10 +#else +#define SS_PIN_DEFAULT 10 + +//KH +#warning w5100.cpp Use fallback, change SS_PIN_DEFAULT to 10 + +#endif + +// W5100 controller instance +uint8_t W5100Class::chip = 0; +uint8_t W5100Class::CH_BASE_MSB; +uint8_t W5100Class::ss_pin = SS_PIN_DEFAULT; +#ifdef ETHERNET_LARGE_BUFFERS +uint16_t W5100Class::SSIZE = 2048; +uint16_t W5100Class::SMASK = 0x07FF; +#endif +W5100Class W5100; + +// pointers and bitmasks for optimized SS pin +#if defined(__AVR__) + volatile uint8_t * W5100Class::ss_pin_reg; + uint8_t W5100Class::ss_pin_mask; +#elif defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK66FX1M0__) || defined(__MK64FX512__) + volatile uint8_t * W5100Class::ss_pin_reg; +#elif defined(__IMXRT1062__) + volatile uint32_t * W5100Class::ss_pin_reg; + uint32_t W5100Class::ss_pin_mask; +#elif defined(__MKL26Z64__) + volatile uint8_t * W5100Class::ss_pin_reg; + uint8_t W5100Class::ss_pin_mask; +#elif defined(__SAM3X8E__) || defined(__SAM3A8C__) || defined(__SAM3A4C__) + volatile uint32_t * W5100Class::ss_pin_reg; + uint32_t W5100Class::ss_pin_mask; +#elif defined(__PIC32MX__) + volatile uint32_t * W5100Class::ss_pin_reg; + uint32_t W5100Class::ss_pin_mask; +#elif defined(ARDUINO_ARCH_ESP8266) + volatile uint32_t * W5100Class::ss_pin_reg; + uint32_t W5100Class::ss_pin_mask; +#elif defined(__SAMD21G18A__) + volatile uint32_t * W5100Class::ss_pin_reg; + uint32_t W5100Class::ss_pin_mask; + #warning w5100.cpp Use __SAMD21G18A__ +#endif + +// KH +uint8_t W5100Class::init(uint8_t socketNumbers, uint8_t new_ss_pin) +{ + uint8_t i; + + if (initialized) return 1; + + // Many Ethernet shields have a CAT811 or similar reset chip + // connected to W5100 or W5200 chips. The W5200 will not work at + // all, and may even drive its MISO pin, until given an active low + // reset pulse! The CAT811 has a 240 ms typical pulse length, and + // a 400 ms worst case maximum pulse length. MAX811 has a worst + // case maximum 560 ms pulse length. This delay is meant to wait + // until the reset pulse is ended. If your hardware has a shorter + // reset time, this can be edited or removed. + delay(560); + + //W5100Class::ss_pin = new_ss_pin; + +#if ( W5100_DEBUG > 0 ) + //KH + Serial.print("\nW5100 init, using SS_PIN_DEFAULT = "); + Serial.print(SS_PIN_DEFAULT); + Serial.print(", new ss_pin = "); + Serial.print(new_ss_pin); + Serial.print(", W5100Class::ss_pin = "); + Serial.println(W5100Class::ss_pin); +#endif + + SPI.begin(); + + initSS(); + resetSS(); + + // From #define SPI_ETHERNET_SETTINGS SPISettings(14000000, MSBFIRST, SPI_MODE0) + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + + // Attempt W5200 detection first, because W5200 does not properly + // reset its SPI state when CS goes high (inactive). Communication + // from detecting the other chips can leave the W5200 in a state + // where it won't recover, unless given a reset pulse. + if (isW5200()) + { + CH_BASE_MSB = 0x40; +#ifdef ETHERNET_LARGE_BUFFERS +#if MAX_SOCK_NUM <= 1 + SSIZE = 16384; +#elif MAX_SOCK_NUM <= 2 + SSIZE = 8192; +#elif MAX_SOCK_NUM <= 4 + SSIZE = 4096; +#else + SSIZE = 2048; +#endif + SMASK = SSIZE - 1; +#endif + for (i=0; i> 10); + writeSnTX_SIZE(i, SSIZE >> 10); + } + for (; i<8; i++) + { + writeSnRX_SIZE(i, 0); + writeSnTX_SIZE(i, 0); + } + +#if ( W5100_DEBUG > 0 ) + Serial.print("W5100::init: W5200, SSIZE ="); + Serial.println(SSIZE); +#endif + + // Try W5500 next. Wiznet finally seems to have implemented + // SPI well with this chip. It appears to be very resilient, + // so try it after the fragile W5200 + } else if (isW5500()) + { + CH_BASE_MSB = 0x10; +#ifdef ETHERNET_LARGE_BUFFERS +#if MAX_SOCK_NUM <= 1 + SSIZE = 16384; +#elif MAX_SOCK_NUM <= 2 + SSIZE = 8192; +#elif MAX_SOCK_NUM <= 4 + SSIZE = 4096; +#else + SSIZE = 2048; +#endif + SMASK = SSIZE - 1; + for (i=0; i> 10); + writeSnTX_SIZE(i, SSIZE >> 10); + } + for (; i<8; i++) + { + writeSnRX_SIZE(i, 0); + writeSnTX_SIZE(i, 0); + } +#endif + +#if ( W5100_DEBUG > 0 ) + Serial.print("W5100::init: W5500, SSIZE ="); + Serial.println(SSIZE); +#endif + + // Try W5100 last. This simple chip uses fixed 4 byte frames + // for every 8 bit access. Terribly inefficient, but so simple + // it recovers from "hearing" unsuccessful W5100 or W5200 + // communication. W5100 is also the only chip without a VERSIONR + // register for identification, so we check this last. + } else if (isW5100()) + { + CH_BASE_MSB = 0x04; + +#ifdef ETHERNET_LARGE_BUFFERS + + #if MAX_SOCK_NUM <= 1 + SSIZE = 8192; + writeTMSR(0x03); + writeRMSR(0x03); + #else + SSIZE = 4096; + writeTMSR(0x0A); + writeRMSR(0x0A); + #endif + + SMASK = SSIZE - 1; +#else + + writeTMSR(0x55); + writeRMSR(0x55); +#endif + +#if ( W5100_DEBUG > 0 ) + Serial.print("W5100::init: W5100, SSIZE ="); + Serial.println(SSIZE); +#endif + + // No hardware seems to be present. Or it could be a W5200 + // that's heard other SPI communication if its chip select + // pin wasn't high when a SD card or other SPI chip was used. + } + else + { +#if ( W5100_DEBUG > 0 ) + Serial.println("no chip :-("); +#endif + + chip = 0; + SPI.endTransaction(); + return 0; // no known chip is responding :-( + } + + SPI.endTransaction(); + initialized = true; + return 1; // successful init +} + +// Soft reset the Wiznet chip, by writing to its MR register reset bit +uint8_t W5100Class::softReset(void) +{ + uint16_t count=0; + +#if ( W5100_DEBUG > 1 ) + Serial.println("Wiznet soft reset"); +#endif + + // write to reset bit + writeMR(0x80); + // then wait for soft reset to complete + do + { + uint8_t mr = readMR(); + +#if ( W5100_DEBUG > 2 ) + Serial.print("mr="); + Serial.println(mr, HEX); +#endif + + if (mr == 0) + return 1; + + delay(1); + } while (++count < 20); + return 0; +} + +uint8_t W5100Class::isW5100(void) +{ + chip = 51; + +#if ( W5100_DEBUG > 1 ) + Serial.println("W5100.cpp: detect W5100 chip"); +#endif + + if (!softReset()) + return 0; + + writeMR(0x10); + if (readMR() != 0x10) + return 0; + + writeMR(0x12); + if (readMR() != 0x12) + return 0; + + writeMR(0x00); + if (readMR() != 0x00) + return 0; + +#if ( W5100_DEBUG > 1 ) + Serial.println("chip is W5100"); +#endif + + return 1; +} + +uint8_t W5100Class::isW5200(void) +{ + chip = 52; + +#if ( W5100_DEBUG > 1 ) + Serial.println("W5100.cpp: detect W5200 chip"); +#endif + + if (!softReset()) + return 0; + + writeMR(0x08); + if (readMR() != 0x08) + return 0; + + writeMR(0x10); + if (readMR() != 0x10) + return 0; + + writeMR(0x00); + if (readMR() != 0x00) + return 0; + + int ver = readVERSIONR_W5200(); + +#if ( W5100_DEBUG > 1 ) + Serial.print("version="); + Serial.println(ver); +#endif + + if (ver != 3) + return 0; + +#if ( W5100_DEBUG > 1 ) + Serial.println("chip is W5200"); +#endif + + return 1; +} + +uint8_t W5100Class::isW5500(void) +{ + chip = 55; + +#if ( W5100_DEBUG > 1 ) + Serial.println("W5100.cpp: detect W5500 chip"); +#endif + + if (!softReset()) + return 0; + + writeMR(0x08); + if (readMR() != 0x08) + return 0; + + writeMR(0x10); + if (readMR() != 0x10) + return 0; + + writeMR(0x00); + if (readMR() != 0x00) + return 0; + + int ver = readVERSIONR_W5500(); + +#if ( W5100_DEBUG > 1 ) + Serial.print("version="); + Serial.println(ver); +#endif + + if (ver != 4) + return 0; + +#if ( W5100_DEBUG > 1 ) + Serial.println("chip is W5500"); +#endif + + return 1; +} + +W5100Linkstatus W5100Class::getLinkStatus() +{ + uint8_t phystatus; + + // KH + if (!initialized) return UNKNOWN; + + switch (chip) + { + case 52: + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + phystatus = readPSTATUS_W5200(); + SPI.endTransaction(); + if (phystatus & 0x20) + return LINK_ON; + + return LINK_OFF; + + case 55: + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + phystatus = readPHYCFGR_W5500(); + SPI.endTransaction(); + if (phystatus & 0x01) + return LINK_ON; + + return LINK_OFF; + + default: + return UNKNOWN; + } +} + +uint16_t W5100Class::write(uint16_t addr, const uint8_t *buf, uint16_t len) +{ + uint8_t cmd[8]; + + if (chip == 51) + { + for (uint16_t i=0; i> 8); + SPI.transfer(addr & 0xFF); + addr++; + SPI.transfer(buf[i]); + resetSS(); + } + } + else if (chip == 52) + { + setSS(); + cmd[0] = addr >> 8; + cmd[1] = addr & 0xFF; + cmd[2] = ((len >> 8) & 0x7F) | 0x80; + cmd[3] = len & 0xFF; + SPI.transfer(cmd, 4); + +#ifdef SPI_HAS_TRANSFER_BUF + SPI.transfer(buf, NULL, len); +#else + // TODO: copy 8 bytes at a time to cmd[] and block transfer + for (uint16_t i=0; i < len; i++) + { + SPI.transfer(buf[i]); + } +#endif + resetSS(); + } + else + { + // chip == 55 + setSS(); + if (addr < 0x100) + { + // common registers 00nn + cmd[0] = 0; + cmd[1] = addr & 0xFF; + cmd[2] = 0x04; + } + else if (addr < 0x8000) + { + // socket registers 10nn, 11nn, 12nn, 13nn, etc + cmd[0] = 0; + cmd[1] = addr & 0xFF; + cmd[2] = ((addr >> 3) & 0xE0) | 0x0C; + } + else if (addr < 0xC000) + { + // transmit buffers 8000-87FF, 8800-8FFF, 9000-97FF, etc + // 10## #nnn nnnn nnnn + cmd[0] = addr >> 8; + cmd[1] = addr & 0xFF; + #if defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 1 + cmd[2] = 0x14; // 16K buffers + #elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 2 + cmd[2] = ((addr >> 8) & 0x20) | 0x14; // 8K buffers + #elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 4 + cmd[2] = ((addr >> 7) & 0x60) | 0x14; // 4K buffers + #else + cmd[2] = ((addr >> 6) & 0xE0) | 0x14; // 2K buffers + #endif + } + else + { + // receive buffers + cmd[0] = addr >> 8; + cmd[1] = addr & 0xFF; + #if defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 1 + cmd[2] = 0x1C; // 16K buffers + #elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 2 + cmd[2] = ((addr >> 8) & 0x20) | 0x1C; // 8K buffers + #elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 4 + cmd[2] = ((addr >> 7) & 0x60) | 0x1C; // 4K buffers + #else + cmd[2] = ((addr >> 6) & 0xE0) | 0x1C; // 2K buffers + #endif + } + + if (len <= 5) + { + for (uint8_t i=0; i < len; i++) + { + cmd[i + 3] = buf[i]; + } + + SPI.transfer(cmd, len + 3); + } + else + { + SPI.transfer(cmd, 3); +#ifdef SPI_HAS_TRANSFER_BUF + SPI.transfer(buf, NULL, len); +#else + // TODO: copy 8 bytes at a time to cmd[] and block transfer + for (uint16_t i=0; i < len; i++) + { + SPI.transfer(buf[i]); + } +#endif + } + resetSS(); + } + return len; +} + +uint16_t W5100Class::read(uint16_t addr, uint8_t *buf, uint16_t len) +{ + uint8_t cmd[4]; + + if (chip == 51) + { + for (uint16_t i=0; i < len; i++) + { + setSS(); + #if 1 + SPI.transfer(0x0F); + SPI.transfer(addr >> 8); + SPI.transfer(addr & 0xFF); + addr++; + buf[i] = SPI.transfer(0); + #else + cmd[0] = 0x0F; + cmd[1] = addr >> 8; + cmd[2] = addr & 0xFF; + cmd[3] = 0; + SPI.transfer(cmd, 4); // TODO: why doesn't this work? + buf[i] = cmd[3]; + addr++; + #endif + resetSS(); + } + } + else if (chip == 52) + { + setSS(); + cmd[0] = addr >> 8; + cmd[1] = addr & 0xFF; + cmd[2] = (len >> 8) & 0x7F; + cmd[3] = len & 0xFF; + SPI.transfer(cmd, 4); + memset(buf, 0, len); + SPI.transfer(buf, len); + resetSS(); + } + else + { + // chip == 55 + setSS(); + + if (addr < 0x100) + { + // common registers 00nn + cmd[0] = 0; + cmd[1] = addr & 0xFF; + cmd[2] = 0x00; + } + else if (addr < 0x8000) + { + // socket registers 10nn, 11nn, 12nn, 13nn, etc + cmd[0] = 0; + cmd[1] = addr & 0xFF; + cmd[2] = ((addr >> 3) & 0xE0) | 0x08; + } + else if (addr < 0xC000) + { + // transmit buffers 8000-87FF, 8800-8FFF, 9000-97FF, etc + // 10## #nnn nnnn nnnn + cmd[0] = addr >> 8; + cmd[1] = addr & 0xFF; + #if defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 1 + cmd[2] = 0x10; // 16K buffers + #elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 2 + cmd[2] = ((addr >> 8) & 0x20) | 0x10; // 8K buffers + #elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 4 + cmd[2] = ((addr >> 7) & 0x60) | 0x10; // 4K buffers + #else + cmd[2] = ((addr >> 6) & 0xE0) | 0x10; // 2K buffers + #endif + } else + { + // receive buffers + cmd[0] = addr >> 8; + cmd[1] = addr & 0xFF; + #if defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 1 + cmd[2] = 0x18; // 16K buffers + #elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 2 + cmd[2] = ((addr >> 8) & 0x20) | 0x18; // 8K buffers + #elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 4 + cmd[2] = ((addr >> 7) & 0x60) | 0x18; // 4K buffers + #else + cmd[2] = ((addr >> 6) & 0xE0) | 0x18; // 2K buffers + #endif + } + SPI.transfer(cmd, 3); + memset(buf, 0, len); + SPI.transfer(buf, len); + resetSS(); + } + return len; +} + +void W5100Class::execCmdSn(SOCKET s, SockCMD _cmd) +{ + // Send command to socket + writeSnCR(s, _cmd); + // Wait for command to complete + while (readSnCR(s)) ; +} diff --git a/LibraryPatches/Ethernet/src/utility/w5100.h b/LibraryPatches/Ethernet/src/utility/w5100.h new file mode 100644 index 0000000..47b72d7 --- /dev/null +++ b/LibraryPatches/Ethernet/src/utility/w5100.h @@ -0,0 +1,638 @@ +/**************************************************************************************************************************** + w5100.h - Driver for W5x00 + + EthernetWebServer is a library for the Ethernet shields to run WebServer + + Based on and modified from ESP8266 https://github.com/esp8266/Arduino/releases + Built by Khoi Hoang https://github.com/khoih-prog/EthernetWebServer + Licensed under MIT license + Version: 1.0.9 + + Copyright 2018 Paul Stoffregen + + 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. + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.0 K Hoang 13/02/2020 Initial coding for Arduino Mega, Teensy, etc to support Ethernetx libraries + 1.0.1 K Hoang 20/02/2020 Add support to lambda functions + 1.0.2 K Hoang 20/02/2020 Add support to UIPEthernet library for ENC28J60 + 1.0.3 K Hoang 23/02/2020 Add support to SAM DUE / SAMD21 boards + 1.0.4 K Hoang 16/04/2020 Add support to SAMD51 boards + 1.0.5 K Hoang 24/04/2020 Add support to nRF52 boards, such as AdaFruit Feather nRF52832, nRF52840 Express, BlueFruit Sense, + Itsy-Bitsy nRF52840 Express, Metro nRF52840 Express, NINA_B30_ublox, etc. + More Custom Ethernet libraries supported such as Ethernet2, Ethernet3, EthernetLarge + 1.0.6 K Hoang 27/04/2020 Add support to ESP32/ESP8266 boards + 1.0.7 K Hoang 30/04/2020 Add ENC28J60 support to ESP32/ESP8266 boards + 1.0.8 K Hoang 12/05/2020 Fix W5x00 support for ESP8266 boards. + 1.0.9 K Hoang 15/05/2020 Add EthernetWrapper.h for easier W5x00 support as well as more Ethernet libs in the future. + *****************************************************************************************************************************/ + +// w5100.h contains private W5x00 hardware "driver" level definitions +// which are not meant to be exposed to other libraries or Arduino users + +#ifndef W5100_H_INCLUDED +#define W5100_H_INCLUDED + +#include +#include + +#ifndef USE_W5100 +#define USE_W5100 false +#else +#define USE_W5100 true +#endif + +#if !USE_W5100 + +// Safe for W5200 and W5500, but also tested OK on W5100 +// Use 14MHz if you know your W5100 can't run +// Higher SPI clock results in faster transfer to hosts on a LAN +// or with very low packet latency. With ordinary internet latency, +// the TCP window size & packet loss determine your overall speed. +#warning Use 25MHz clock for W5200/W5500. Not for W5100 +#define SPI_ETHERNET_SETTINGS SPISettings(25000000, MSBFIRST, SPI_MODE0) + +#else + +// Safe for all chips but too slow +#define SPI_ETHERNET_SETTINGS SPISettings(14000000, MSBFIRST, SPI_MODE0) +#warning Use 14MHz clock for W5100/W5200/W5500. Slow. + +#endif + +// Require Ethernet.h, because we need MAX_SOCK_NUM +#ifndef ethernet_h_ +#error "Ethernet.h must be included before w5100.h" +#endif + +// Arduino 101's SPI can not run faster than 8 MHz. +#if defined(ARDUINO_ARCH_ARC32) +#undef SPI_ETHERNET_SETTINGS +#define SPI_ETHERNET_SETTINGS SPISettings(8000000, MSBFIRST, SPI_MODE0) +#endif + +// Arduino Zero can't use W5100-based shields faster than 8 MHz +// https://github.com/arduino-libraries/Ethernet/issues/37#issuecomment-408036848 +// W5500 does seem to work at 12 MHz. Delete this if only using W5500 +#if defined(__SAMD21G18A__) +#undef SPI_ETHERNET_SETTINGS +//#warning Use SAMD21 architecture SPISettings(8000000, MSBFIRST, SPI_MODE3) => IP OK +#warning Use SAMD21 architecture SPISettings(30000000, MSBFIRST, SPI_MODE3) => IP OK +// Still not working !!! Original SPI_MODE0 not OK at all +//#define SPI_ETHERNET_SETTINGS SPISettings(8000000, MSBFIRST, SPI_MODE3) +#define SPI_ETHERNET_SETTINGS SPISettings(30000000, MSBFIRST, SPI_MODE3) +#endif + +typedef uint8_t SOCKET; + +class SnMR { +public: + static const uint8_t CLOSE = 0x00; + static const uint8_t TCP = 0x21; + static const uint8_t UDP = 0x02; + static const uint8_t IPRAW = 0x03; + static const uint8_t MACRAW = 0x04; + static const uint8_t PPPOE = 0x05; + static const uint8_t ND = 0x20; + static const uint8_t MULTI = 0x80; +}; + +enum SockCMD { + Sock_OPEN = 0x01, + Sock_LISTEN = 0x02, + Sock_CONNECT = 0x04, + Sock_DISCON = 0x08, + Sock_CLOSE = 0x10, + Sock_SEND = 0x20, + Sock_SEND_MAC = 0x21, + Sock_SEND_KEEP = 0x22, + Sock_RECV = 0x40 +}; + +class SnIR { +public: + static const uint8_t SEND_OK = 0x10; + static const uint8_t TIMEOUT = 0x08; + static const uint8_t RECV = 0x04; + static const uint8_t DISCON = 0x02; + static const uint8_t CON = 0x01; +}; + +class SnSR { +public: + static const uint8_t CLOSED = 0x00; + static const uint8_t INIT = 0x13; + static const uint8_t LISTEN = 0x14; + static const uint8_t SYNSENT = 0x15; + static const uint8_t SYNRECV = 0x16; + static const uint8_t ESTABLISHED = 0x17; + static const uint8_t FIN_WAIT = 0x18; + static const uint8_t CLOSING = 0x1A; + static const uint8_t TIME_WAIT = 0x1B; + static const uint8_t CLOSE_WAIT = 0x1C; + static const uint8_t LAST_ACK = 0x1D; + static const uint8_t UDP = 0x22; + static const uint8_t IPRAW = 0x32; + static const uint8_t MACRAW = 0x42; + static const uint8_t PPPOE = 0x5F; +}; + +class IPPROTO { +public: + static const uint8_t IP = 0; + static const uint8_t ICMP = 1; + static const uint8_t IGMP = 2; + static const uint8_t GGP = 3; + static const uint8_t TCP = 6; + static const uint8_t PUP = 12; + static const uint8_t UDP = 17; + static const uint8_t IDP = 22; + static const uint8_t ND = 77; + static const uint8_t RAW = 255; +}; + +enum W5100Linkstatus { + UNKNOWN, + LINK_ON, + LINK_OFF +}; + +class W5100Class { + +public: + // KH + uint8_t init(uint8_t socketNumbers = MAX_SOCK_NUM, uint8_t new_ss_pin = 10); + + inline void setGatewayIp(const uint8_t * addr) { writeGAR(addr); } + inline void getGatewayIp(uint8_t * addr) { readGAR(addr); } + + inline void setSubnetMask(const uint8_t * addr) { writeSUBR(addr); } + inline void getSubnetMask(uint8_t * addr) { readSUBR(addr); } + + inline void setMACAddress(const uint8_t * addr) { writeSHAR(addr); } + inline void getMACAddress(uint8_t * addr) { readSHAR(addr); } + + inline void setIPAddress(const uint8_t * addr) { writeSIPR(addr); } + inline void getIPAddress(uint8_t * addr) { readSIPR(addr); } + + inline void setRetransmissionTime(uint16_t timeout) { writeRTR(timeout); } + inline void setRetransmissionCount(uint8_t retry) { writeRCR(retry); } + + static void execCmdSn(SOCKET s, SockCMD _cmd); + + + // W5100 Registers + // --------------- +//private: +public: + static uint16_t write(uint16_t addr, const uint8_t *buf, uint16_t len); + + static uint8_t write(uint16_t addr, uint8_t data) + { + return write(addr, &data, 1); + } + + static uint16_t read(uint16_t addr, uint8_t *buf, uint16_t len); + + static uint8_t read(uint16_t addr) + { + uint8_t data; + read(addr, &data, 1); + return data; + } + +#define __GP_REGISTER8(name, address) \ + static inline void write##name(uint8_t _data) { \ + write(address, _data); \ + } \ + static inline uint8_t read##name() { \ + return read(address); \ + } +#define __GP_REGISTER16(name, address) \ + static void write##name(uint16_t _data) { \ + uint8_t buf[2]; \ + buf[0] = _data >> 8; \ + buf[1] = _data & 0xFF; \ + write(address, buf, 2); \ + } \ + static uint16_t read##name() { \ + uint8_t buf[2]; \ + read(address, buf, 2); \ + return (buf[0] << 8) | buf[1]; \ + } +#define __GP_REGISTER_N(name, address, size) \ + static uint16_t write##name(const uint8_t *_buff) { \ + return write(address, _buff, size); \ + } \ + static uint16_t read##name(uint8_t *_buff) { \ + return read(address, _buff, size); \ + } + + W5100Linkstatus getLinkStatus(); + +public: + __GP_REGISTER8 (MR, 0x0000); // Mode + __GP_REGISTER_N(GAR, 0x0001, 4); // Gateway IP address + __GP_REGISTER_N(SUBR, 0x0005, 4); // Subnet mask address + __GP_REGISTER_N(SHAR, 0x0009, 6); // Source MAC address + __GP_REGISTER_N(SIPR, 0x000F, 4); // Source IP address + __GP_REGISTER8 (IR, 0x0015); // Interrupt + __GP_REGISTER8 (IMR, 0x0016); // Interrupt Mask + __GP_REGISTER16(RTR, 0x0017); // Timeout address + __GP_REGISTER8 (RCR, 0x0019); // Retry count + __GP_REGISTER8 (RMSR, 0x001A); // Receive memory size (W5100 only) + __GP_REGISTER8 (TMSR, 0x001B); // Transmit memory size (W5100 only) + __GP_REGISTER8 (PATR, 0x001C); // Authentication type address in PPPoE mode + __GP_REGISTER8 (PTIMER, 0x0028); // PPP LCP Request Timer + __GP_REGISTER8 (PMAGIC, 0x0029); // PPP LCP Magic Number + __GP_REGISTER_N(UIPR, 0x002A, 4); // Unreachable IP address in UDP mode (W5100 only) + __GP_REGISTER16(UPORT, 0x002E); // Unreachable Port address in UDP mode (W5100 only) + __GP_REGISTER8 (VERSIONR_W5200,0x001F); // Chip Version Register (W5200 only) + __GP_REGISTER8 (VERSIONR_W5500,0x0039); // Chip Version Register (W5500 only) + __GP_REGISTER8 (PSTATUS_W5200, 0x0035); // PHY Status + __GP_REGISTER8 (PHYCFGR_W5500, 0x002E); // PHY Configuration register, default: 10111xxx + + +#undef __GP_REGISTER8 +#undef __GP_REGISTER16 +#undef __GP_REGISTER_N + + // W5100 Socket registers + // ---------------------- +private: + static uint16_t CH_BASE(void) { + //if (chip == 55) return 0x1000; + //if (chip == 52) return 0x4000; + //return 0x0400; + return CH_BASE_MSB << 8; + } + static uint8_t CH_BASE_MSB; // 1 redundant byte, saves ~80 bytes code on AVR + static const uint16_t CH_SIZE = 0x0100; + + static inline uint8_t readSn(SOCKET s, uint16_t addr) + { + return read(CH_BASE() + s * CH_SIZE + addr); + } + static inline uint8_t writeSn(SOCKET s, uint16_t addr, uint8_t data) + { + return write(CH_BASE() + s * CH_SIZE + addr, data); + } + static inline uint16_t readSn(SOCKET s, uint16_t addr, uint8_t *buf, uint16_t len) + { + return read(CH_BASE() + s * CH_SIZE + addr, buf, len); + } + static inline uint16_t writeSn(SOCKET s, uint16_t addr, uint8_t *buf, uint16_t len) + { + return write(CH_BASE() + s * CH_SIZE + addr, buf, len); + } + +#define __SOCKET_REGISTER8(name, address) \ + static inline void write##name(SOCKET _s, uint8_t _data) { \ + writeSn(_s, address, _data); \ + } \ + static inline uint8_t read##name(SOCKET _s) { \ + return readSn(_s, address); \ + } +#define __SOCKET_REGISTER16(name, address) \ + static void write##name(SOCKET _s, uint16_t _data) { \ + uint8_t buf[2]; \ + buf[0] = _data >> 8; \ + buf[1] = _data & 0xFF; \ + writeSn(_s, address, buf, 2); \ + } \ + static uint16_t read##name(SOCKET _s) { \ + uint8_t buf[2]; \ + readSn(_s, address, buf, 2); \ + return (buf[0] << 8) | buf[1]; \ + } +#define __SOCKET_REGISTER_N(name, address, size) \ + static uint16_t write##name(SOCKET _s, uint8_t *_buff) { \ + return writeSn(_s, address, _buff, size); \ + } \ + static uint16_t read##name(SOCKET _s, uint8_t *_buff) { \ + return readSn(_s, address, _buff, size); \ + } + +public: + __SOCKET_REGISTER8(SnMR, 0x0000) // Mode + __SOCKET_REGISTER8(SnCR, 0x0001) // Command + __SOCKET_REGISTER8(SnIR, 0x0002) // Interrupt + __SOCKET_REGISTER8(SnSR, 0x0003) // Status + __SOCKET_REGISTER16(SnPORT, 0x0004) // Source Port + __SOCKET_REGISTER_N(SnDHAR, 0x0006, 6) // Destination Hardw Addr + __SOCKET_REGISTER_N(SnDIPR, 0x000C, 4) // Destination IP Addr + __SOCKET_REGISTER16(SnDPORT, 0x0010) // Destination Port + __SOCKET_REGISTER16(SnMSSR, 0x0012) // Max Segment Size + __SOCKET_REGISTER8(SnPROTO, 0x0014) // Protocol in IP RAW Mode + __SOCKET_REGISTER8(SnTOS, 0x0015) // IP TOS + __SOCKET_REGISTER8(SnTTL, 0x0016) // IP TTL + __SOCKET_REGISTER8(SnRX_SIZE, 0x001E) // RX Memory Size (W5200 only) + __SOCKET_REGISTER8(SnTX_SIZE, 0x001F) // RX Memory Size (W5200 only) + __SOCKET_REGISTER16(SnTX_FSR, 0x0020) // TX Free Size + __SOCKET_REGISTER16(SnTX_RD, 0x0022) // TX Read Pointer + __SOCKET_REGISTER16(SnTX_WR, 0x0024) // TX Write Pointer + __SOCKET_REGISTER16(SnRX_RSR, 0x0026) // RX Free Size + __SOCKET_REGISTER16(SnRX_RD, 0x0028) // RX Read Pointer + __SOCKET_REGISTER16(SnRX_WR, 0x002A) // RX Write Pointer (supported?) + +#undef __SOCKET_REGISTER8 +#undef __SOCKET_REGISTER16 +#undef __SOCKET_REGISTER_N + + +private: + // KH + bool initialized = false; + static uint8_t chip; + static uint8_t ss_pin; + + static uint8_t isW5100(void); + static uint8_t isW5200(void); + static uint8_t isW5500(void); + +public: + // KH + static uint8_t softReset(void); + static uint8_t getChip(void) { return chip; } +#ifdef ETHERNET_LARGE_BUFFERS + static uint16_t SSIZE; + static uint16_t SMASK; +#else + static const uint16_t SSIZE = 2048; + static const uint16_t SMASK = 0x07FF; +#endif + static uint16_t SBASE(uint8_t socknum) + { + if (chip == 51) + { + return socknum * SSIZE + 0x4000; + } + else + { + return socknum * SSIZE + 0x8000; + } + } + + static uint16_t RBASE(uint8_t socknum) + { + if (chip == 51) { + return socknum * SSIZE + 0x6000; + } + else + { + return socknum * SSIZE + 0xC000; + } + } + + static bool hasOffsetAddressMapping(void) + { + if (chip == 55) + return true; + + return false; + } + + static void setSS(uint8_t pin) { ss_pin = pin; } + +private: +#if defined(__AVR__) + +#warning Use AVR architecture + + static volatile uint8_t *ss_pin_reg; + static uint8_t ss_pin_mask; + + inline static void initSS() + { + ss_pin_reg = portOutputRegister(digitalPinToPort(ss_pin)); + ss_pin_mask = digitalPinToBitMask(ss_pin); + pinMode(ss_pin, OUTPUT); + } + + inline static void setSS() + { + *(ss_pin_reg) &= ~ss_pin_mask; + } + + inline static void resetSS() + { + *(ss_pin_reg) |= ss_pin_mask; + } +#elif defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK66FX1M0__) || defined(__MK64FX512__) + +#warning Use MK architecture + + static volatile uint8_t *ss_pin_reg; + + inline static void initSS() + { + ss_pin_reg = portOutputRegister(ss_pin); + pinMode(ss_pin, OUTPUT); + } + + inline static void setSS() + { + *(ss_pin_reg+256) = 1; + } + + inline static void resetSS() + { + *(ss_pin_reg+128) = 1; + } +#elif defined(__IMXRT1062__) + +#warning Use Teensy architecture + + static volatile uint32_t *ss_pin_reg; + static uint32_t ss_pin_mask; + + inline static void initSS() + { + ss_pin_reg = portOutputRegister(digitalPinToPort(ss_pin)); + ss_pin_mask = digitalPinToBitMask(ss_pin); + pinMode(ss_pin, OUTPUT); + } + + inline static void setSS() + { + *(ss_pin_reg+34) = ss_pin_mask; + } + + inline static void resetSS() + { + *(ss_pin_reg+33) = ss_pin_mask; + } +#elif defined(__MKL26Z64__) + static volatile uint8_t *ss_pin_reg; + static uint8_t ss_pin_mask; + inline static void initSS() + { + ss_pin_reg = portOutputRegister(digitalPinToPort(ss_pin)); + ss_pin_mask = digitalPinToBitMask(ss_pin); + pinMode(ss_pin, OUTPUT); + } + + inline static void setSS() + { + *(ss_pin_reg+8) = ss_pin_mask; + } + + inline static void resetSS() + { + *(ss_pin_reg+4) = ss_pin_mask; + } +#elif defined(__SAM3X8E__) || defined(__SAM3A8C__) || defined(__SAM3A4C__) + +#warning Use SAM3 architecture + + static volatile uint32_t *ss_pin_reg; + static uint32_t ss_pin_mask; + + inline static void initSS() + { + ss_pin_reg = &(digitalPinToPort(ss_pin)->PIO_PER); + ss_pin_mask = digitalPinToBitMask(ss_pin); + pinMode(ss_pin, OUTPUT); + } + + inline static void setSS() + { + *(ss_pin_reg+13) = ss_pin_mask; + } + + inline static void resetSS() + { + *(ss_pin_reg+12) = ss_pin_mask; + } +#elif defined(__PIC32MX__) + static volatile uint32_t *ss_pin_reg; + static uint32_t ss_pin_mask; + + inline static void initSS() + { + ss_pin_reg = portModeRegister(digitalPinToPort(ss_pin)); + ss_pin_mask = digitalPinToBitMask(ss_pin); + pinMode(ss_pin, OUTPUT); + } + + inline static void setSS() + { + *(ss_pin_reg+8+1) = ss_pin_mask; + } + + inline static void resetSS() + { + *(ss_pin_reg+8+2) = ss_pin_mask; + } + +#elif defined(ARDUINO_ARCH_ESP8266) + +#warning Use ARDUINO_ARCH_ESP8266 architecture + + static volatile uint32_t *ss_pin_reg; + static uint32_t ss_pin_mask; + + inline static void initSS() + { + ss_pin_reg = (volatile uint32_t*)GPO; + ss_pin_mask = 1 << ss_pin; + pinMode(ss_pin, OUTPUT); + } + + inline static void setSS() + { + GPOC = ss_pin_mask; + } + + inline static void resetSS() + { + GPOS = ss_pin_mask; + } + +#elif defined(__SAMD21G18A__) + +#warning Use SAMD21 architecture + + static volatile uint32_t *ss_pin_reg; + static uint32_t ss_pin_mask; + + inline static void initSS() + { + ss_pin_reg = portModeRegister(digitalPinToPort(ss_pin)); + ss_pin_mask = digitalPinToBitMask(ss_pin); + pinMode(ss_pin, OUTPUT); + } + + inline static void setSS() + { + *(ss_pin_reg+5) = ss_pin_mask; + } + + inline static void resetSS() + { + *(ss_pin_reg+6) = ss_pin_mask; + } +#else + +#warning Use Default architecture + + inline static void initSS() + { + pinMode(ss_pin, OUTPUT); + } + + inline static void setSS() + { + digitalWrite(ss_pin, LOW); + } + + inline static void resetSS() + { + digitalWrite(ss_pin, HIGH); + } +#endif +}; + +extern W5100Class W5100; + +#endif + +#ifndef UTIL_H +#define UTIL_H + +#ifndef htons +#define htons(x) ( (((x)<<8)&0xFF00) | (((x)>>8)&0xFF) ) +#endif + +#ifndef ntohs +#define ntohs(x) htons(x) +#endif + +#ifndef htonl +#define htonl(x) ( ((x)<<24 & 0xFF000000UL) | \ + ((x)<< 8 & 0x00FF0000UL) | \ + ((x)>> 8 & 0x0000FF00UL) | \ + ((x)>>24 & 0x000000FFUL) ) +#endif + +#ifndef ntohl +#define ntohl(x) htonl(x) +#endif + +#endif //W5100_H_INCLUDED diff --git a/LibraryPatches/Ethernet2/src/Ethernet2.cpp b/LibraryPatches/Ethernet2/src/Ethernet2.cpp new file mode 100644 index 0000000..ba24292 --- /dev/null +++ b/LibraryPatches/Ethernet2/src/Ethernet2.cpp @@ -0,0 +1,247 @@ +/* + modified 12 Aug 2013 + by Soohwan Kim (suhwan@wiznet.co.kr) + +- 10 Apr. 2015 + Added support for Arduino Ethernet Shield 2 + by Arduino.org team + + */ + +#include "Ethernet2.h" +#include "Dhcp.h" + +// XXX: don't make assumptions about the value of MAX_SOCK_NUM. +uint8_t EthernetClass::_state[MAX_SOCK_NUM] = { 0, }; +uint16_t EthernetClass::_server_port[MAX_SOCK_NUM] = { 0, }; + + + +#if defined(WIZ550io_WITH_MACADDRESS) +int EthernetClass::begin(void) +{ + // KH mod to work with new func void MACAddress(uint8_t *mac_address); and SinricPro v2.5.1+ + // Now store to private var _mac_address + //byte mac_address[6] ={0,}; + ////// + + if (_dhcp != NULL) { + delete _dhcp; + } + _dhcp = new DhcpClass(); + + // Initialise the basic info + w5500.init(w5500_cspin); + w5500.setIPAddress(IPAddress(0,0,0,0).raw_address()); + + // KH mod + w5500.getMACAddress(_mac_address); + + // Now try to get our config info from a DHCP server + int ret = _dhcp->beginWithDHCP(_mac_address); + ////// + + if(ret == 1) + { + // We've successfully found a DHCP server and got our configuration info, so set things + // accordingly + w5500.setIPAddress(_dhcp->getLocalIp().raw_address()); + w5500.setGatewayIp(_dhcp->getGatewayIp().raw_address()); + w5500.setSubnetMask(_dhcp->getSubnetMask().raw_address()); + _dnsServerAddress = _dhcp->getDnsServerIp(); + _dnsDomainName = _dhcp->getDnsDomainName(); + _hostName = _dhcp->getHostName(); + } + + return ret; +} + +void EthernetClass::begin(IPAddress local_ip) +{ + // Assume the DNS server will be the machine on the same network as the local IP + // but with last octet being '1' + IPAddress dns_server = local_ip; + dns_server[3] = 1; + begin(local_ip, dns_server); +} + +void EthernetClass::begin(IPAddress local_ip, IPAddress dns_server) +{ + // Assume the gateway will be the machine on the same network as the local IP + // but with last octet being '1' + IPAddress gateway = local_ip; + gateway[3] = 1; + begin(local_ip, dns_server, gateway); +} + +void EthernetClass::begin(IPAddress local_ip, IPAddress dns_server, IPAddress gateway) +{ + IPAddress subnet(255, 255, 255, 0); + begin(local_ip, dns_server, gateway, subnet); +} + +void EthernetClass::begin(IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet) +{ + w5500.init(w5500_cspin); + w5500.setIPAddress(local_ip.raw_address()); + w5500.setGatewayIp(gateway.raw_address()); + w5500.setSubnetMask(subnet.raw_address()); + _dnsServerAddress = dns_server; +} +#else +int EthernetClass::begin(uint8_t *mac_address) +{ + // KH mod to work with new func void MACAddress(uint8_t *mac_address); and SinricPro v2.5.1+ + // Now store to private var + //uint8_t _mac_address[6] ={0,}; + memcpy(_mac_address, mac_address, sizeof(_mac_address)); + ////// + + if (_dhcp != NULL) { + delete _dhcp; + } + _dhcp = new DhcpClass(); + // Initialise the basic info + w5500.init(w5500_cspin); + w5500.setMACAddress(mac_address); + w5500.setIPAddress(IPAddress(0,0,0,0).raw_address()); + + // Now try to get our config info from a DHCP server + int ret = _dhcp->beginWithDHCP(mac_address); + if(ret == 1) + { + // We've successfully found a DHCP server and got our configuration info, so set things + // accordingly + w5500.setIPAddress(_dhcp->getLocalIp().raw_address()); + w5500.setGatewayIp(_dhcp->getGatewayIp().raw_address()); + w5500.setSubnetMask(_dhcp->getSubnetMask().raw_address()); + _dnsServerAddress = _dhcp->getDnsServerIp(); + _dnsDomainName = _dhcp->getDnsDomainName(); + _hostName = _dhcp->getHostName(); + } + + return ret; +} + +void EthernetClass::begin(uint8_t *mac_address, IPAddress local_ip) +{ + // KH mod to work with new func void MACAddress(uint8_t *mac_address); and SinricPro v2.5.1+ + // Now store to private var + //uint8_t _mac_address[6] ={0,}; + memcpy(_mac_address, mac_address, sizeof(_mac_address)); + ////// + + // Assume the DNS server will be the machine on the same network as the local IP + // but with last octet being '1' + IPAddress dns_server = local_ip; + dns_server[3] = 1; + begin(mac_address, local_ip, dns_server); +} + +void EthernetClass::begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server) +{ + // KH mod to work with new func void MACAddress(uint8_t *mac_address); and SinricPro v2.5.1+ + // Now store to private var + //uint8_t _mac_address[6] ={0,}; + memcpy(_mac_address, mac_address, sizeof(_mac_address)); + ////// + + // Assume the gateway will be the machine on the same network as the local IP + // but with last octet being '1' + IPAddress gateway = local_ip; + gateway[3] = 1; + begin(mac_address, local_ip, dns_server, gateway); +} + +void EthernetClass::begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway) +{ + // KH mod to work with new func void MACAddress(uint8_t *mac_address); and SinricPro v2.5.1+ + // Now store to private var + //uint8_t _mac_address[6] ={0,}; + memcpy(_mac_address, mac_address, sizeof(_mac_address)); + ////// + + IPAddress subnet(255, 255, 255, 0); + begin(mac_address, local_ip, dns_server, gateway, subnet); +} + +void EthernetClass::begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet) +{ + // KH mod to work with new func void MACAddress(uint8_t *mac_address); and SinricPro v2.5.1+ + // Now store to private var + //uint8_t _mac_address[6] ={0,}; + memcpy(_mac_address, mac_address, sizeof(_mac_address)); + ////// + + w5500.init(w5500_cspin); + w5500.setMACAddress(mac_address); + w5500.setIPAddress(local_ip.raw_address()); + w5500.setGatewayIp(gateway.raw_address()); + w5500.setSubnetMask(subnet.raw_address()); + _dnsServerAddress = dns_server; +} + +#endif + +int EthernetClass::maintain(){ + int rc = DHCP_CHECK_NONE; + if(_dhcp != NULL){ + //we have a pointer to dhcp, use it + rc = _dhcp->checkLease(); + switch ( rc ){ + case DHCP_CHECK_NONE: + //nothing done + break; + case DHCP_CHECK_RENEW_OK: + case DHCP_CHECK_REBIND_OK: + //we might have got a new IP. + w5500.setIPAddress(_dhcp->getLocalIp().raw_address()); + w5500.setGatewayIp(_dhcp->getGatewayIp().raw_address()); + w5500.setSubnetMask(_dhcp->getSubnetMask().raw_address()); + _dnsServerAddress = _dhcp->getDnsServerIp(); + _dnsDomainName = _dhcp->getDnsDomainName(); + _hostName = _dhcp->getHostName(); + break; + default: + //this is actually a error, it will retry though + break; + } + } + return rc; +} + +IPAddress EthernetClass::localIP() +{ + IPAddress ret; + w5500.getIPAddress(ret.raw_address()); + return ret; +} + +IPAddress EthernetClass::subnetMask() +{ + IPAddress ret; + w5500.getSubnetMask(ret.raw_address()); + return ret; +} + +IPAddress EthernetClass::gatewayIP() +{ + IPAddress ret; + w5500.getGatewayIp(ret.raw_address()); + return ret; +} + +IPAddress EthernetClass::dnsServerIP() +{ + return _dnsServerAddress; +} + +char* EthernetClass::dnsDomainName(){ + return _dnsDomainName; +} + +char* EthernetClass::hostName(){ + return _hostName; +} + +EthernetClass Ethernet; diff --git a/LibraryPatches/Ethernet2/src/Ethernet2.h b/LibraryPatches/Ethernet2/src/Ethernet2.h new file mode 100644 index 0000000..79ac906 --- /dev/null +++ b/LibraryPatches/Ethernet2/src/Ethernet2.h @@ -0,0 +1,87 @@ +/* + modified 12 Aug 2013 + by Soohwan Kim (suhwan@wiznet.co.kr) + + - 10 Apr. 2015 + Added support for Arduino Ethernet Shield 2 + by Arduino.org team + + */ +#ifndef ethernet_h +#define ethernet_h + +#include +#include "utility/w5500.h" +#include "IPAddress.h" +#include "EthernetClient.h" +#include "EthernetServer.h" +#include "Dhcp.h" + + + +class EthernetClass { +private: + IPAddress _dnsServerAddress; + char* _dnsDomainName; + char* _hostName; + DhcpClass* _dhcp; + + // KH add to work with new func void MACAddress(uint8_t *mac_address); and SinricPro v2.5.1+ + uint8_t _mac_address[6] ={0,}; + ////// + +public: + uint8_t w5500_cspin; + + static uint8_t _state[MAX_SOCK_NUM]; + static uint16_t _server_port[MAX_SOCK_NUM]; + + EthernetClass() { _dhcp = NULL; w5500_cspin = 10; } + void init(uint8_t _cspin = 10) { w5500_cspin = _cspin; } + +#if defined(WIZ550io_WITH_MACADDRESS) + // Initialize function when use the ioShield serise (included WIZ550io) + // WIZ550io has a MAC address which is written after reset. + // Default IP, Gateway and subnet address are also writen. + // so, It needs some initial time. please refer WIZ550io Datasheet in details. + int begin(void); + void begin(IPAddress local_ip); + void begin(IPAddress local_ip, IPAddress dns_server); + void begin(IPAddress local_ip, IPAddress dns_server, IPAddress gateway); + void begin(IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet); +#else + // Initialize the Ethernet shield to use the provided MAC address and gain the rest of the + // configuration through DHCP. + // Returns 0 if the DHCP configuration failed, and 1 if it succeeded + int begin(uint8_t *mac_address); + void begin(uint8_t *mac_address, IPAddress local_ip); + void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server); + void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway); + void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet); + +#endif + + int maintain(); + + // KH add to have similar function to Ethernet lib + // Certainly we can use void macAddress(uint8_t mac[]) to read from W5x00. + void MACAddress(uint8_t *mac_address) + { + memcpy(mac_address, _mac_address, sizeof(_mac_address)); + } + ////// + + IPAddress localIP(); + IPAddress subnetMask(); + IPAddress gatewayIP(); + IPAddress dnsServerIP(); + char* dnsDomainName(); + char* hostName(); + + friend class EthernetClient; + friend class EthernetServer; +}; + +extern EthernetClass Ethernet; + +#endif diff --git a/LibraryPatches/Ethernet2/src/EthernetUdp2.cpp b/LibraryPatches/Ethernet2/src/EthernetUdp2.cpp new file mode 100644 index 0000000..d8671cd --- /dev/null +++ b/LibraryPatches/Ethernet2/src/EthernetUdp2.cpp @@ -0,0 +1,290 @@ +/* + * Udp.cpp: Library to send/receive UDP packets with the Arduino ethernet shield. + * This version only offers minimal wrapping of socket.c/socket.h + * Drop Udp.h/.cpp into the Ethernet library directory at hardware/libraries/Ethernet/ + * + * 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 + * + * - 10 Apr. 2015 + * Added support for Arduino Ethernet Shield 2 + * by Arduino.org team + */ + +#include "utility/w5500.h" +#include "utility/socket.h" +#include "Ethernet2.h" +#include "Udp.h" +#include "Dns.h" + +#define ETHERNET2_DEBUG 0 + +/* Constructor */ +EthernetUDP::EthernetUDP() : _sock(MAX_SOCK_NUM) {} + +/* Start EthernetUDP socket, listening at local port PORT */ +uint8_t EthernetUDP::begin(uint16_t port) { + if (_sock != MAX_SOCK_NUM) + return 0; + + for (int i = 0; i < MAX_SOCK_NUM; i++) { + uint8_t s = w5500.readSnSR(i); + if (s == SnSR::CLOSED || s == SnSR::FIN_WAIT) { + _sock = i; + break; + } + } + + if (_sock == MAX_SOCK_NUM) + return 0; + + _port = port; + _remaining = 0; + socket(_sock, SnMR::UDP, _port, 0); + + return 1; +} + +//KH, to add Multicast support +/* Start EthernetUDP socket, listening at local port PORT */ +uint8_t EthernetUDP::beginMulticast(IPAddress ip, uint16_t port) +{ + if (_sock != MAX_SOCK_NUM) + return 0; + + for (int i = 0; i < MAX_SOCK_NUM; i++) { + uint8_t s = w5500.readSnSR(i); + if (s == SnSR::CLOSED || s == SnSR::FIN_WAIT) { + _sock = i; + break; + } + } + + if (_sock == MAX_SOCK_NUM) + return 0; + + // Calculate MAC address from Multicast IP Address + byte mac[] = { 0x01, 0x00, 0x5E, 0x00, 0x00, 0x00 }; + + mac[3] = ip[1] & 0x7F; + mac[4] = ip[2]; + mac[5] = ip[3]; + + w5500.writeSnDIPR(_sock, rawIPAddress(ip)); //239.255.0.1 + w5500.writeSnDPORT(_sock, port); + w5500.writeSnDHAR(_sock,mac); + + _remaining = 0; + socket(_sock, SnMR::UDP, port, SnMR::MULTI); + return 1; +} +////// + + + +/* return number of bytes available in the current packet, + will return zero if parsePacket hasn't been called yet */ +int EthernetUDP::available() { + return _remaining; +} + +/* Release any resources being used by this EthernetUDP instance */ +void EthernetUDP::stop() +{ + if (_sock == MAX_SOCK_NUM) + return; + + close(_sock); + + EthernetClass::_server_port[_sock] = 0; + _sock = MAX_SOCK_NUM; +} + +int EthernetUDP::beginPacket(const char *host, uint16_t port) +{ + // Look up the host first + int ret = 0; + DNSClient dns; + IPAddress remote_addr; + + dns.begin(Ethernet.dnsServerIP()); + ret = dns.getHostByName(host, remote_addr); + if (ret == 1) { + return beginPacket(remote_addr, port); + } else { + return ret; + } +} + +int EthernetUDP::beginPacket(IPAddress ip, uint16_t port) +{ + _offset = 0; + +// KH debug +#if (ETHERNET2_DEBUG > 1) + Serial.print("Ethernet2UDP::beginPacket: ip="); + Serial.print(ip); + Serial.print(", port="); + Serial.println(port); +#endif + + return startUDP(_sock, rawIPAddress(ip), port); +} + +int EthernetUDP::endPacket() +{ + return sendUDP(_sock); +} + +size_t EthernetUDP::write(uint8_t byte) +{ + return write(&byte, 1); +} + +size_t EthernetUDP::write(const uint8_t *buffer, size_t size) +{ + // KH debug +#if (ETHERNET2_DEBUG > 1) + Serial.print("Ethernet2UDP:write, size="); + Serial.println(size); +#endif + + uint16_t bytes_written = bufferData(_sock, _offset, buffer, size); + _offset += bytes_written; + + // KH debug +#if (ETHERNET2_DEBUG > 1) + Serial.print("Ethernet2UDP: bytes written="); + Serial.println(bytes_written); +#endif + + return bytes_written; +} + +int EthernetUDP::parsePacket() +{ + // discard any remaining bytes in the last packet + flush(); + + if (w5500.getRXReceivedSize(_sock) > 0) + { + //HACK - hand-parse the UDP packet using TCP recv method + uint8_t tmpBuf[8]; + int ret =0; + //read 8 header bytes and get IP and port from it + ret = recv(_sock,tmpBuf,8); + if (ret > 0) + { + _remoteIP = tmpBuf; + _remotePort = tmpBuf[4]; + _remotePort = (_remotePort << 8) + tmpBuf[5]; + _remaining = tmpBuf[6]; + _remaining = (_remaining << 8) + tmpBuf[7]; + + // When we get here, any remaining bytes are the data + ret = _remaining; + } + + // KH debug +#if (ETHERNET2_DEBUG > 1) + Serial.print("Ethernet2UDP:parsePacket OK, datasize="); + Serial.println(ret); +#endif + + return ret; + } + // There aren't any packets available + return 0; +} + +int EthernetUDP::read() +{ + uint8_t byte; + + if ((_remaining > 0) && (recv(_sock, &byte, 1) > 0)) + { + // We read things without any problems + _remaining--; + return byte; + } + + // If we get here, there's no data available + return -1; +} + +int EthernetUDP::read(unsigned char* buffer, size_t len) +{ + + if (_remaining > 0) + { + + int got; + + if (_remaining <= len) + { + // data should fit in the buffer + got = recv(_sock, buffer, _remaining); + } + else + { + // too much data for the buffer, + // grab as much as will fit + got = recv(_sock, buffer, len); + } + + if (got > 0) + { + _remaining -= got; + return got; + } + + } + + // If we get here, there's no data available or recv failed + return -1; + +} + +int EthernetUDP::peek() +{ + uint8_t b; + // Unlike recv, peek doesn't check to see if there's any data available, so we must. + // If the user hasn't called parsePacket yet then return nothing otherwise they + // may get the UDP header + if (!_remaining) + return -1; + ::peek(_sock, &b); + return b; +} + +void EthernetUDP::flush() +{ + // could this fail (loop endlessly) if _remaining > 0 and recv in read fails? + // should only occur if recv fails after telling us the data is there, lets + // hope the w5500 always behaves :) + + while (_remaining) + { + read(); + } +} + diff --git a/LibraryPatches/Ethernet2/src/EthernetUdp2.h b/LibraryPatches/Ethernet2/src/EthernetUdp2.h new file mode 100644 index 0000000..fe8d110 --- /dev/null +++ b/LibraryPatches/Ethernet2/src/EthernetUdp2.h @@ -0,0 +1,110 @@ +/* + * Udp.cpp: Library to send/receive UDP packets with the Arduino ethernet shield. + * This version only offers minimal wrapping of socket.c/socket.h + * Drop Udp.h/.cpp into the Ethernet library directory at hardware/libraries/Ethernet/ + * + * 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 + * + * - 10 Apr. 2015 + * Added support for Arduino Ethernet Shield 2 + * by Arduino.org team + * + */ + + +#ifndef ethernetudp_h +#define ethernetudp_h + +#include + +#define UDP_TX_PACKET_MAX_SIZE 24 + +class EthernetUDP : public UDP { +private: + uint8_t _sock; // socket ID for Wiz5100 + uint16_t _port; // local port to listen on + IPAddress _remoteIP; // remote IP address for the incoming packet whilst it's being processed + uint16_t _remotePort; // remote port for the incoming packet whilst it's being processed + uint16_t _offset; // offset into the packet being sent + uint16_t _remaining; // remaining bytes of incoming packet yet to be processed + +public: + EthernetUDP(); // Constructor + virtual uint8_t begin(uint16_t); // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use + + //KH, to add Multicast support + virtual uint8_t beginMulticast(IPAddress, uint16_t); // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use + ////// + + virtual void stop(); // 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); + // 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); + // Finish off this packet and send it + // Returns 1 if the packet was sent successfully, 0 if there was an error + virtual int endPacket(); + // Write a single byte into the packet + virtual size_t write(uint8_t); + // Write size bytes from buffer into the packet + virtual size_t write(const uint8_t *buffer, size_t size); + + using Print::write; + + // 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(); + // Number of bytes remaining in the current packet + virtual int available(); + // Read a single byte from the current packet + virtual int read(); + // 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); + // 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) { return read((unsigned char*)buffer, len); }; + // Return the next byte from the current packet without moving on to the next byte + virtual int peek(); + virtual void flush(); // Finish reading the current packet + + // Return the IP address of the host who sent the current incoming packet + virtual IPAddress remoteIP() { return _remoteIP; }; + // Return the port of the host who sent the current incoming packet + virtual uint16_t remotePort() { return _remotePort; }; +}; + +#endif diff --git a/LibraryPatches/Ethernet3/src/Ethernet3.cpp b/LibraryPatches/Ethernet3/src/Ethernet3.cpp new file mode 100644 index 0000000..23ad359 --- /dev/null +++ b/LibraryPatches/Ethernet3/src/Ethernet3.cpp @@ -0,0 +1,443 @@ +/* + modified 12 Aug 2013 + by Soohwan Kim (suhwan@wiznet.co.kr) + +- 10 Apr. 2015 + Added support for Arduino Ethernet Shield 2 + by Arduino.org team + + */ + +#include "Ethernet3.h" +#include "Dhcp.h" + +// XXX: don't make assumptions about the value of MAX_SOCK_NUM. +uint8_t EthernetClass::_state[MAX_SOCK_NUM] = { 0, }; +uint16_t EthernetClass::_server_port[MAX_SOCK_NUM] = { 0, }; + +void EthernetClass::setRstPin(uint8_t pinRST) { + _pinRST = pinRST; + pinMode(_pinRST, OUTPUT); + digitalWrite(_pinRST, HIGH); + } +void EthernetClass::setCsPin(uint8_t pinCS) { + _pinCS = pinCS; + } + +void EthernetClass::init(uint8_t maxSockNum) { + _maxSockNum = maxSockNum; + } + +uint8_t EthernetClass::softreset() { + return w5500.softReset(); + } + +void EthernetClass::hardreset() { + if(_pinRST != 0) { + digitalWrite(_pinRST, LOW); + delay(1); + digitalWrite(_pinRST, HIGH); + delay(150); + } + } + +#if defined(WIZ550io_WITH_MACADDRESS) + +int EthernetClass::begin(void) +{ + // KH mod to work with new func void MACAddress(uint8_t *mac_address); and SinricPro v2.5.1+ + // Now store to private var _mac_address + //uint8_t mac_address[6] ={0,}; + ////// + + _dhcp = new DhcpClass(); + + // Initialise the basic info + w5500.init(_maxSockNum, _pinCS); + w5500.setIPAddress(IPAddress(0,0,0,0).raw_address()); + + // KH mod + w5500.getMACAddress(_mac_address); + ////// + + if (strlen(_customHostname) != 0) + { + _dhcp->setCustomHostname(_customHostname); + } + + // Now try to get our config info from a DHCP server + // KH mod + int ret = _dhcp->beginWithDHCP(_mac_address); + ////// + + if(ret == 1) + { + // We've successfully found a DHCP server and got our configuration info, so set things + // accordingly + w5500.setIPAddress(_dhcp->getLocalIp().raw_address()); + w5500.setGatewayIp(_dhcp->getGatewayIp().raw_address()); + w5500.setSubnetMask(_dhcp->getSubnetMask().raw_address()); + _dnsServerAddress = _dhcp->getDnsServerIp(); + } + return ret; +} + +void EthernetClass::begin(IPAddress local_ip) +{ + IPAddress subnet(255, 255, 255, 0); + begin(local_ip, subnet); +} + +void EthernetClass::begin(IPAddress local_ip, IPAddress subnet) +{ + // Assume the gateway will be the machine on the same network as the local IP + // but with last octet being '1' + IPAddress gateway = local_ip; + gateway[3] = 1; + begin(local_ip, subnet, gateway); +} + +void EthernetClass::begin(IPAddress local_ip, IPAddress subnet, IPAddress gateway) +{ + // Assume the DNS server will be the machine on the same network as the local IP + // but with last octet being '1' + IPAddress dns_server = local_ip; + dns_server[3] = 1; + begin(local_ip, subnet, gateway, dns_server); +} + +void EthernetClass::begin(IPAddress local_ip, IPAddress subnet, IPAddress gateway, IPAddress dns_server) +{ + w5500.init(_maxSockNum, _pinCS); + w5500.setIPAddress(local_ip.raw_address()); + w5500.setGatewayIp(gateway.raw_address()); + w5500.setSubnetMask(subnet.raw_address()); + _dnsServerAddress = dns_server; +} + +#else +int EthernetClass::begin(uint8_t *mac_address) +{ + // KH mod to work with new func void MACAddress(uint8_t *mac_address); and SinricPro v2.5.1+ + // Now store to private var + //uint8_t _mac_address[6] ={0,}; + memcpy(_mac_address, mac_address, sizeof(_mac_address)); + ////// + + _dhcp = new DhcpClass(); + // Initialise the basic info + w5500.init(_maxSockNum, _pinCS); + w5500.setMACAddress(mac_address); + w5500.setIPAddress(IPAddress(0,0,0,0).raw_address()); + + if (strlen(_customHostname) != 0) + { + _dhcp->setCustomHostname(_customHostname); + } + + // Now try to get our config info from a DHCP server + int ret = _dhcp->beginWithDHCP(mac_address); + if(ret == 1) + { + // We've successfully found a DHCP server and got our configuration info, so set things + // accordingly + w5500.setIPAddress(_dhcp->getLocalIp().raw_address()); + w5500.setGatewayIp(_dhcp->getGatewayIp().raw_address()); + w5500.setSubnetMask(_dhcp->getSubnetMask().raw_address()); + _dnsServerAddress = _dhcp->getDnsServerIp(); + } + + return ret; +} + +void EthernetClass::begin(uint8_t *mac_address, IPAddress local_ip) +{ + // KH mod to work with new func void MACAddress(uint8_t *mac_address); and SinricPro v2.5.1+ + // Now store to private var + //uint8_t _mac_address[6] ={0,}; + memcpy(_mac_address, mac_address, sizeof(_mac_address)); + ////// + + IPAddress subnet(255, 255, 255, 0); + begin(mac_address, local_ip, subnet); +} + +void EthernetClass::begin(uint8_t *mac_address, IPAddress local_ip, IPAddress subnet) +{ + // KH mod to work with new func void MACAddress(uint8_t *mac_address); and SinricPro v2.5.1+ + // Now store to private var + //uint8_t _mac_address[6] ={0,}; + memcpy(_mac_address, mac_address, sizeof(_mac_address)); + ////// + + // Assume the gateway will be the machine on the same network as the local IP + // but with last octet being '1' + IPAddress gateway = local_ip; + gateway[3] = 1; + begin(mac_address, local_ip, subnet, gateway); +} + +void EthernetClass::begin(uint8_t *mac_address, IPAddress local_ip, IPAddress subnet, IPAddress gateway) +{ + // KH mod to work with new func void MACAddress(uint8_t *mac_address); and SinricPro v2.5.1+ + // Now store to private var + //uint8_t _mac_address[6] ={0,}; + memcpy(_mac_address, mac_address, sizeof(_mac_address)); + ////// + + // Assume the DNS server will be the machine on the same network as the local IP + // but with last octet being '1' + IPAddress dns_server = local_ip; + dns_server[3] = 1; + begin(mac_address, local_ip, subnet, gateway, dns_server); +} + +void EthernetClass::begin(uint8_t *mac_address, IPAddress local_ip, IPAddress subnet, IPAddress gateway, IPAddress dns_server) +{ + // KH mod to work with new func void MACAddress(uint8_t *mac_address); and SinricPro v2.5.1+ + // Now store to private var + //uint8_t _mac_address[6] ={0,}; + memcpy(_mac_address, mac_address, sizeof(_mac_address)); + ////// + + w5500.init(_maxSockNum, _pinCS); + w5500.setMACAddress(mac_address); + w5500.setIPAddress(local_ip.raw_address()); + w5500.setGatewayIp(gateway.raw_address()); + w5500.setSubnetMask(subnet.raw_address()); + _dnsServerAddress = dns_server; +} + +#endif + +int EthernetClass::maintain(){ + int rc = DHCP_CHECK_NONE; + if(_dhcp != NULL){ + //we have a pointer to dhcp, use it + rc = _dhcp->checkLease(); + switch ( rc ){ + case DHCP_CHECK_NONE: + //nothing done + break; + case DHCP_CHECK_RENEW_OK: + case DHCP_CHECK_REBIND_OK: + //we might have got a new IP. + w5500.setIPAddress(_dhcp->getLocalIp().raw_address()); + w5500.setGatewayIp(_dhcp->getGatewayIp().raw_address()); + w5500.setSubnetMask(_dhcp->getSubnetMask().raw_address()); + _dnsServerAddress = _dhcp->getDnsServerIp(); + break; + default: + //this is actually a error, it will retry though + break; + } + } + return rc; +} + +void EthernetClass::WoL(bool wol) { + uint8_t val = w5500.readMR(); + bitWrite(val, 5, wol); + w5500.writeMR(val); + } + +bool EthernetClass::WoL() { + uint8_t val = w5500.readMR(); + return bitRead(val, 5); + } + +void EthernetClass::phyMode(phyMode_t mode) { + uint8_t val = w5500.getPHYCFGR(); + bitWrite(val, 6, 1); + if (mode == HALF_DUPLEX_10) { + bitWrite(val, 3, 0); + bitWrite(val, 4, 0); + bitWrite(val, 5, 0); + w5500.setPHYCFGR(val); + } + else if (mode == FULL_DUPLEX_10) { + bitWrite(val, 3, 1); + bitWrite(val, 4, 0); + bitWrite(val, 5, 0); + w5500.setPHYCFGR(val); + } + else if (mode == HALF_DUPLEX_100) { + bitWrite(val, 3, 0); + bitWrite(val, 4, 1); + bitWrite(val, 5, 0); + w5500.setPHYCFGR(val); + } + else if (mode == FULL_DUPLEX_100) { + bitWrite(val, 3, 1); + bitWrite(val, 4, 1); + bitWrite(val, 5, 0); + w5500.setPHYCFGR(val); + } + else if (mode == FULL_DUPLEX_100_AUTONEG) { + bitWrite(val, 3, 0); + bitWrite(val, 4, 0); + bitWrite(val, 5, 1); + w5500.setPHYCFGR(val); + } + else if (mode == POWER_DOWN) { + bitWrite(val, 3, 0); + bitWrite(val, 4, 1); + bitWrite(val, 5, 1); + w5500.setPHYCFGR(val); + } + else if (mode == ALL_AUTONEG) { + bitWrite(val, 3, 1); + bitWrite(val, 4, 1); + bitWrite(val, 5, 1); + w5500.setPHYCFGR(val); + } + bitWrite(val, 7, 0); + w5500.setPHYCFGR(val); + bitWrite(val, 7, 1); + w5500.setPHYCFGR(val); + } + +void EthernetClass::setHostname(char* hostname) { + memset(_customHostname, 0, 32); + memcpy((void*)_customHostname, (void*)hostname, strlen(hostname) >= 31 ? 31 : strlen(hostname)); + } + +uint8_t EthernetClass::phyState() { + return w5500.getPHYCFGR(); + } + +uint8_t EthernetClass::link() { + return bitRead(w5500.getPHYCFGR(), 0); + } + +const char* EthernetClass::linkReport() +{ + if (bitRead(w5500.getPHYCFGR(), 0) == 1) + return "LINK"; + else + return "NO LINK"; + } + +uint8_t EthernetClass::speed() +{ + if (bitRead(w5500.getPHYCFGR(), 0) == 1) + { + if (bitRead(w5500.getPHYCFGR(), 1) == 1) + return 100; + + if (bitRead(w5500.getPHYCFGR(), 1) == 0) + return 10; + + // KH add to fix compile error in some boards + return 0; + } + else + return 0; +} + +const char* EthernetClass::speedReport() +{ + if (bitRead(w5500.getPHYCFGR(), 0) == 1) + { + if (bitRead(w5500.getPHYCFGR(), 1) == 1) + return "100 MB"; + + if (bitRead(w5500.getPHYCFGR(), 1) == 0) + return "10 MB"; + + // KH add to fix compile error in some boards + return "NO LINK"; + } + else + return "NO LINK"; +} + +uint8_t EthernetClass::duplex() +{ + if(bitRead(w5500.getPHYCFGR(), 0) == 1) + { + if (bitRead(w5500.getPHYCFGR(), 2) == 1) + return 2; + + if (bitRead(w5500.getPHYCFGR(), 2) == 0) + return 1; + + // KH add to fix compile error in some boards + return 0; + } + else + return 0; +} + +const char* EthernetClass::duplexReport() +{ + if (bitRead(w5500.getPHYCFGR(), 0) == 1) + { + if(bitRead(w5500.getPHYCFGR(), 2) == 1) + return "FULL DUPLEX"; + + if (bitRead(w5500.getPHYCFGR(), 2) == 0) + return "HALF DUPLEX"; + + // KH add to fix compile error in some boards + return "NO LINK"; + } + else + return "NO LINK"; +} + +void EthernetClass::setRtTimeOut(uint16_t timeout) { + w5500.setRetransmissionTime(timeout); + } + +uint16_t EthernetClass::getRtTimeOut() { + return w5500.getRetransmissionTime(); + } + +void EthernetClass::setRtCount(uint8_t count) { + w5500.setRetransmissionCount(count); + } + +uint8_t EthernetClass::getRtCount() { + return w5500.getRetransmissionCount(); + } + +void EthernetClass::macAddress(uint8_t mac[]) { + w5500.getMACAddress(mac); + } + +const char* EthernetClass::macAddressReport() { + uint8_t mac[6]; + static char str[18]; + w5500.getMACAddress(mac); + sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + return str; + } + +IPAddress EthernetClass::localIP() +{ + IPAddress ret; + w5500.getIPAddress(ret.raw_address()); + return ret; +} + +IPAddress EthernetClass::subnetMask() +{ + IPAddress ret; + w5500.getSubnetMask(ret.raw_address()); + return ret; +} + +IPAddress EthernetClass::gatewayIP() +{ + IPAddress ret; + w5500.getGatewayIp(ret.raw_address()); + return ret; +} + +IPAddress EthernetClass::dnsServerIP() +{ + return _dnsServerAddress; +} + +EthernetClass Ethernet; diff --git a/LibraryPatches/Ethernet3/src/Ethernet3.h b/LibraryPatches/Ethernet3/src/Ethernet3.h new file mode 100644 index 0000000..afa7db4 --- /dev/null +++ b/LibraryPatches/Ethernet3/src/Ethernet3.h @@ -0,0 +1,127 @@ +/* + modified 12 Aug 2013 + by Soohwan Kim (suhwan@wiznet.co.kr) + + - 10 Apr. 2015 + Added support for Arduino Ethernet Shield 2 + by Arduino.org team + + */ +#ifndef ethernet3_h +#define ethernet3_h + +#include +#include "utility/w5500.h" +#include "IPAddress.h" +#include "EthernetClient.h" +#include "EthernetServer.h" +#include "Dhcp.h" + +enum phyMode_t { + HALF_DUPLEX_10, + FULL_DUPLEX_10, + HALF_DUPLEX_100, + FULL_DUPLEX_100, + FULL_DUPLEX_100_AUTONEG, + POWER_DOWN, + ALL_AUTONEG + }; + +class EthernetClass { +private: + IPAddress _dnsServerAddress; + DhcpClass* _dhcp; + char _customHostname[32]; + + // KH add to work with new func void MACAddress(uint8_t *mac_address); and SinricPro v2.5.1+ + uint8_t _mac_address[6] ={0,}; + ////// + +public: + uint8_t _maxSockNum; + uint8_t _pinCS; + uint8_t _pinRST; + + static uint8_t _state[MAX_SOCK_NUM]; + static uint16_t _server_port[MAX_SOCK_NUM]; + + EthernetClass() { _dhcp = NULL; _pinCS = 10; _maxSockNum = 8; } + + void setRstPin(uint8_t pinRST = 9); // for WIZ550io or USR-ES1, must set befor Ethernet.begin + void setCsPin(uint8_t pinCS = 10); // must set befor Ethernet.begin + + // Initialize with less sockets but more RX/TX Buffer + // maxSockNum = 1 Socket 0 -> RX/TX Buffer 16k + // maxSockNum = 2 Socket 0, 1 -> RX/TX Buffer 8k + // maxSockNum = 4 Socket 0...3 -> RX/TX Buffer 4k + // maxSockNum = 8 (Standard) all sockets -> RX/TX Buffer 2k + // be carefull of the MAX_SOCK_NUM, because in the moment it can't dynamicly changed + void init(uint8_t maxSockNum = 8); + + uint8_t softreset(); // can set only after Ethernet.begin + void hardreset(); // You need to set the Rst pin + +#if defined(WIZ550io_WITH_MACADDRESS) + + // Initialize function when use the ioShield serise (included WIZ550io) + // WIZ550io has a MAC address which is written after reset. + // Default IP, Gateway and subnet address are also writen. + // so, It needs some initial time. please refer WIZ550io Datasheet in details. + int begin(void); + void begin(IPAddress local_ip); + void begin(IPAddress local_ip, IPAddress subnet); + void begin(IPAddress local_ip, IPAddress subnet, IPAddress gateway); + void begin(IPAddress local_ip, IPAddress subnet, IPAddress gateway, IPAddress dns_server); +#else + // Initialize the Ethernet shield to use the provided MAC address and gain the rest of the + // configuration through DHCP. + // Returns 0 if the DHCP configuration failed, and 1 if it succeeded + int begin(uint8_t *mac_address); + void begin(uint8_t *mac_address, IPAddress local_ip); + void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress subnet); + void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress subnet, IPAddress gateway); + void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress subnet, IPAddress gateway, IPAddress dns_server); +#endif + + int maintain(); + void WoL(bool wol); // set Wake on LAN + bool WoL(); // get the WoL state + void phyMode(phyMode_t mode); // set PHYCFGR + uint8_t phyState(); // returns the PHYCFGR + uint8_t link(); // returns the linkstate, 1 = linked, 0 = no link + const char* linkReport(); // returns the linkstate as a string + uint8_t speed(); // returns speed in MB/s + const char* speedReport(); // returns speed as a string + uint8_t duplex(); // returns duplex mode 0 = no link, 1 = Half Duplex, 2 = Full Duplex + const char* duplexReport(); // returns duplex mode as a string + + void setRtTimeOut(uint16_t timeout = 2000); // set the retransmission timout *100us + uint16_t getRtTimeOut(); // get the retransmission timout + void setRtCount(uint8_t count = 8); // set the retransmission count + uint8_t getRtCount(); // get the retransmission count + + void macAddress(uint8_t mac[]); // get the MAC Address + const char* macAddressReport(); // returns the the MAC Address as a string + + void setHostname(char* hostname); + + // KH add to have similar function to Ethernet lib + // Certainly we can use void macAddress(uint8_t mac[]) to read from W5x00. + void MACAddress(uint8_t *mac_address) + { + memcpy(mac_address, _mac_address, sizeof(_mac_address)); + } + ////// + + IPAddress localIP(); + IPAddress subnetMask(); + IPAddress gatewayIP(); + IPAddress dnsServerIP(); + + friend class EthernetClient; + friend class EthernetServer; +}; + +extern EthernetClass Ethernet; + +#endif diff --git a/LibraryPatches/EthernetLarge/src/EthernetLarge.cpp b/LibraryPatches/EthernetLarge/src/EthernetLarge.cpp new file mode 100644 index 0000000..4357b3c --- /dev/null +++ b/LibraryPatches/EthernetLarge/src/EthernetLarge.cpp @@ -0,0 +1,318 @@ +/**************************************************************************************************************************** + EthernetLarge.cpp + + EthernetWebServer is a library for the Ethernet shields to run WebServer + + Based on and modified from ESP8266 https://github.com/esp8266/Arduino/releases + Built by Khoi Hoang https://github.com/khoih-prog/EthernetWebServer + Licensed under MIT license + Version: 1.0.9 + + Copyright 2018 Paul Stoffregen + + 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. + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.0 K Hoang 13/02/2020 Initial coding for Arduino Mega, Teensy, etc to support Ethernetx libraries + 1.0.1 K Hoang 20/02/2020 Add support to lambda functions + 1.0.2 K Hoang 20/02/2020 Add support to UIPEthernet library for ENC28J60 + 1.0.3 K Hoang 23/02/2020 Add support to SAM DUE / SAMD21 boards + 1.0.4 K Hoang 16/04/2020 Add support to SAMD51 boards + 1.0.5 K Hoang 24/04/2020 Add support to nRF52 boards, such as AdaFruit Feather nRF52832, nRF52840 Express, BlueFruit Sense, + Itsy-Bitsy nRF52840 Express, Metro nRF52840 Express, NINA_B30_ublox, etc. + More Custom Ethernet libraries supported such as Ethernet2, Ethernet3, EthernetLarge + 1.0.6 K Hoang 27/04/2020 Add support to ESP32/ESP8266 boards + 1.0.7 K Hoang 30/04/2020 Add ENC28J60 support to ESP32/ESP8266 boards + 1.0.8 K Hoang 12/05/2020 Fix W5x00 support for ESP8266 boards. + 1.0.9 K Hoang 15/05/2020 Add EthernetWrapper.h for easier W5x00 support as well as more Ethernet libs in the future. + *****************************************************************************************************************************/ + +#include +#include "EthernetLarge.h" +#include "utility/w5100.h" +#include "Dhcp.h" + +#define ETHERNET_DEBUG 1 + +IPAddress EthernetClass::_dnsServerAddress; +DhcpClass* EthernetClass::_dhcp = NULL; + +// KH +void EthernetClass::setRstPin(uint8_t pinRST) +{ + _pinRST = pinRST; + pinMode(_pinRST, OUTPUT); + digitalWrite(_pinRST, HIGH); +} +void EthernetClass::setCsPin(uint8_t pinCS) +{ + _pinCS = pinCS; + W5100.setSS(pinCS); + +#if ( ETHERNET_DEBUG > 0 ) + Serial.print("Input pinCS = "); + Serial.println(pinCS); + Serial.print("_pinCS = "); + Serial.println(_pinCS); +#endif +} + +void EthernetClass::initMaxSockNum(uint8_t maxSockNum) +{ + _maxSockNum = maxSockNum; +} + +uint8_t EthernetClass::softreset() +{ + return W5100.softReset(); +} + +void EthernetClass::hardreset() +{ + if(_pinRST != 0) + { + digitalWrite(_pinRST, LOW); + delay(1); + digitalWrite(_pinRST, HIGH); + delay(150); + } +} + +int EthernetClass::begin(uint8_t *mac, unsigned long timeout, unsigned long responseTimeout) +{ + static DhcpClass s_dhcp; + _dhcp = &s_dhcp; + +#if ( ETHERNET_DEBUG > 0 ) + Serial.print("_pinCS = "); + Serial.print(_pinCS); +#endif + + // Initialise the basic info + if (W5100.init() == 0) + return 0; + + + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + W5100.setMACAddress(mac); + W5100.setIPAddress(IPAddress(0,0,0,0).raw_address()); + SPI.endTransaction(); + + // Now try to get our config info from a DHCP server + int ret = _dhcp->beginWithDHCP(mac, timeout, responseTimeout); + if (ret == 1) { + // We've successfully found a DHCP server and got our configuration + // info, so set things accordingly + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + W5100.setIPAddress(_dhcp->getLocalIp().raw_address()); + W5100.setGatewayIp(_dhcp->getGatewayIp().raw_address()); + W5100.setSubnetMask(_dhcp->getSubnetMask().raw_address()); + SPI.endTransaction(); + _dnsServerAddress = _dhcp->getDnsServerIp(); + socketPortRand(micros()); + } + return ret; +} + +void EthernetClass::begin(uint8_t *mac, IPAddress ip) +{ + // Assume the DNS server will be the machine on the same network as the local IP + // but with last octet being '1' + IPAddress dns = ip; + dns[3] = 1; + begin(mac, ip, dns); +} + +void EthernetClass::begin(uint8_t *mac, IPAddress ip, IPAddress dns) +{ + // Assume the gateway will be the machine on the same network as the local IP + // but with last octet being '1' + IPAddress gateway = ip; + gateway[3] = 1; + begin(mac, ip, dns, gateway); +} + +void EthernetClass::begin(uint8_t *mac, IPAddress ip, IPAddress dns, IPAddress gateway) +{ + IPAddress subnet(255, 255, 255, 0); + begin(mac, ip, dns, gateway, subnet); +} + +void EthernetClass::begin(uint8_t *mac, IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet) +{ + // Initialise the basic info + if (W5100.init() == 0) + return; + + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + W5100.setMACAddress(mac); +#if ( defined(ESP8266) || defined(ESP32) ) + W5100.setIPAddress(&ip[0]); + W5100.setGatewayIp(&gateway[0]); + W5100.setSubnetMask(&subnet[0]); +#elif ARDUINO > 106 || TEENSYDUINO > 121 + W5100.setIPAddress(ip._address.bytes); + W5100.setGatewayIp(gateway._address.bytes); + W5100.setSubnetMask(subnet._address.bytes); +#else + W5100.setIPAddress(ip._address); + W5100.setGatewayIp(gateway._address); + W5100.setSubnetMask(subnet._address); +#endif + SPI.endTransaction(); + _dnsServerAddress = dns; +} + +void EthernetClass::init(uint8_t sspin) +{ + W5100.setSS(sspin); +} + +EthernetLinkStatus EthernetClass::linkStatus() +{ + switch (W5100.getLinkStatus()) { + case UNKNOWN: return Unknown; + case LINK_ON: return LinkON; + case LINK_OFF: return LinkOFF; + default: return Unknown; + } +} + +EthernetHardwareStatus EthernetClass::hardwareStatus() +{ + switch (W5100.getChip()) { + case 51: return EthernetW5100; + case 52: return EthernetW5200; + case 55: return EthernetW5500; + default: return EthernetNoHardware; + } +} + +int EthernetClass::maintain() +{ + int rc = DHCP_CHECK_NONE; + if (_dhcp != NULL) { + // we have a pointer to dhcp, use it + rc = _dhcp->checkLease(); + switch (rc) + { + case DHCP_CHECK_NONE: + //nothing done + break; + case DHCP_CHECK_RENEW_OK: + case DHCP_CHECK_REBIND_OK: + //we might have got a new IP. + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + W5100.setIPAddress(_dhcp->getLocalIp().raw_address()); + W5100.setGatewayIp(_dhcp->getGatewayIp().raw_address()); + W5100.setSubnetMask(_dhcp->getSubnetMask().raw_address()); + SPI.endTransaction(); + _dnsServerAddress = _dhcp->getDnsServerIp(); + break; + default: + //this is actually an error, it will retry though + break; + } + } + return rc; +} + + +void EthernetClass::MACAddress(uint8_t *mac_address) +{ + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + W5100.getMACAddress(mac_address); + SPI.endTransaction(); +} + +IPAddress EthernetClass::localIP() +{ + IPAddress ret; + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + W5100.getIPAddress(ret.raw_address()); + SPI.endTransaction(); + return ret; +} + +IPAddress EthernetClass::subnetMask() +{ + IPAddress ret; + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + W5100.getSubnetMask(ret.raw_address()); + SPI.endTransaction(); + return ret; +} + +IPAddress EthernetClass::gatewayIP() +{ + IPAddress ret; + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + W5100.getGatewayIp(ret.raw_address()); + SPI.endTransaction(); + return ret; +} + +void EthernetClass::setMACAddress(const uint8_t *mac_address) +{ + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + W5100.setMACAddress(mac_address); + SPI.endTransaction(); +} + +void EthernetClass::setLocalIP(const IPAddress local_ip) +{ + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + IPAddress ip = local_ip; + W5100.setIPAddress(ip.raw_address()); + SPI.endTransaction(); +} + +void EthernetClass::setSubnetMask(const IPAddress subnet) +{ + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + IPAddress ip = subnet; + W5100.setSubnetMask(ip.raw_address()); + SPI.endTransaction(); +} + +void EthernetClass::setGatewayIP(const IPAddress gateway) +{ + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + IPAddress ip = gateway; + W5100.setGatewayIp(ip.raw_address()); + SPI.endTransaction(); +} + +void EthernetClass::setRetransmissionTimeout(uint16_t milliseconds) +{ + if (milliseconds > 6553) milliseconds = 6553; + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + W5100.setRetransmissionTime(milliseconds * 10); + SPI.endTransaction(); +} + +void EthernetClass::setRetransmissionCount(uint8_t num) +{ + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + W5100.setRetransmissionCount(num); + SPI.endTransaction(); +} + + +EthernetClass Ethernet; diff --git a/LibraryPatches/EthernetLarge/src/EthernetLarge.h b/LibraryPatches/EthernetLarge/src/EthernetLarge.h new file mode 100644 index 0000000..4a18a2d --- /dev/null +++ b/LibraryPatches/EthernetLarge/src/EthernetLarge.h @@ -0,0 +1,362 @@ +/**************************************************************************************************************************** + EthernetLarge.h + + EthernetWebServer is a library for the Ethernet shields to run WebServer + + Based on and modified from ESP8266 https://github.com/esp8266/Arduino/releases + Built by Khoi Hoang https://github.com/khoih-prog/EthernetWebServer + Licensed under MIT license + Version: 1.0.9 + + Copyright 2018 Paul Stoffregen + + 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. + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.0 K Hoang 13/02/2020 Initial coding for Arduino Mega, Teensy, etc to support Ethernetx libraries + 1.0.1 K Hoang 20/02/2020 Add support to lambda functions + 1.0.2 K Hoang 20/02/2020 Add support to UIPEthernet library for ENC28J60 + 1.0.3 K Hoang 23/02/2020 Add support to SAM DUE / SAMD21 boards + 1.0.4 K Hoang 16/04/2020 Add support to SAMD51 boards + 1.0.5 K Hoang 24/04/2020 Add support to nRF52 boards, such as AdaFruit Feather nRF52832, nRF52840 Express, BlueFruit Sense, + Itsy-Bitsy nRF52840 Express, Metro nRF52840 Express, NINA_B30_ublox, etc. + More Custom Ethernet libraries supported such as Ethernet2, Ethernet3, EthernetLarge + 1.0.6 K Hoang 27/04/2020 Add support to ESP32/ESP8266 boards + 1.0.7 K Hoang 30/04/2020 Add ENC28J60 support to ESP32/ESP8266 boards + 1.0.8 K Hoang 12/05/2020 Fix W5x00 support for ESP8266 boards. + 1.0.9 K Hoang 15/05/2020 Add EthernetWrapper.h for easier W5x00 support as well as more Ethernet libs in the future. + *****************************************************************************************************************************/ + +#ifndef ethernet_h_ +#define ethernet_h_ + +// All symbols exposed to Arduino sketches are contained in this header file +// +// Older versions had much of this stuff in EthernetClient.h, EthernetServer.h, +// and socket.h. Including headers in different order could cause trouble, so +// these "friend" classes are now defined in the same header file. socket.h +// was removed to avoid possible conflict with the C library header files. + + +// Configure the maximum number of sockets to support. W5100 chips can have +// up to 4 sockets. W5200 & W5500 can have up to 8 sockets. Several bytes +// of RAM are used for each socket. Reducing the maximum can save RAM, but +// you are limited to fewer simultaneous connections. +#define MAX_SOCK_NUM 2 + +// By default, each socket uses 2K buffers inside the Wiznet chip. If +// MAX_SOCK_NUM is set to fewer than the chip's maximum, uncommenting +// this will use larger buffers within the Wiznet chip. Large buffers +// can really help with UDP protocols like Artnet. In theory larger +// buffers should allow faster TCP over high-latency links, but this +// does not always seem to work in practice (maybe Wiznet bugs?) +#define ETHERNET_LARGE_BUFFERS + + +#include +#include "Client.h" +#include "Server.h" +#include "Udp.h" + +enum EthernetLinkStatus { + Unknown, + LinkON, + LinkOFF +}; + +enum EthernetHardwareStatus { + EthernetNoHardware, + EthernetW5100, + EthernetW5200, + EthernetW5500 +}; + +class EthernetUDP; +class EthernetClient; +class EthernetServer; +class DhcpClass; + +class EthernetClass { +private: + static IPAddress _dnsServerAddress; + static DhcpClass* _dhcp; +public: + // KH + uint8_t _maxSockNum; + uint8_t _pinCS; + uint8_t _pinRST; + + void setRstPin(uint8_t pinRST = 9); // for WIZ550io or USR-ES1, must set befor Ethernet.begin + void setCsPin(uint8_t pinCS = 10); // must set befor Ethernet.begin + + // Initialize with less sockets but more RX/TX Buffer + // maxSockNum = 1 Socket 0 -> RX/TX Buffer 16k + // maxSockNum = 2 Socket 0, 1 -> RX/TX Buffer 8k + // maxSockNum = 4 Socket 0...3 -> RX/TX Buffer 4k + // maxSockNum = 8 (Standard) all sockets -> RX/TX Buffer 2k + // be carefull of the MAX_SOCK_NUM, because in the moment it can't dynamicly changed + void initMaxSockNum(uint8_t maxSockNum = 8); + + uint8_t softreset(); // can set only after Ethernet.begin + void hardreset(); // You need to set the Rst pin + + // Initialise the Ethernet shield to use the provided MAC address and + // gain the rest of the configuration through DHCP. + // Returns 0 if the DHCP configuration failed, and 1 if it succeeded + int begin(uint8_t *mac, unsigned long timeout = 60000, unsigned long responseTimeout = 4000); + int maintain(); + EthernetLinkStatus linkStatus(); + EthernetHardwareStatus hardwareStatus(); + + // Manual configuration + void begin(uint8_t *mac, IPAddress ip); + void begin(uint8_t *mac, IPAddress ip, IPAddress dns); + void begin(uint8_t *mac, IPAddress ip, IPAddress dns, IPAddress gateway); + void begin(uint8_t *mac, IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet); + void init(uint8_t sspin = 10); + + void MACAddress(uint8_t *mac_address); + IPAddress localIP(); + IPAddress subnetMask(); + IPAddress gatewayIP(); + IPAddress dnsServerIP() { return _dnsServerAddress; } + + void setMACAddress(const uint8_t *mac_address); + void setLocalIP(const IPAddress local_ip); + void setSubnetMask(const IPAddress subnet); + void setGatewayIP(const IPAddress gateway); + void setDnsServerIP(const IPAddress dns_server) { _dnsServerAddress = dns_server; } + void setRetransmissionTimeout(uint16_t milliseconds); + void setRetransmissionCount(uint8_t num); + + friend class EthernetClient; + friend class EthernetServer; + friend class EthernetUDP; +private: + // Opens a socket(TCP or UDP or IP_RAW mode) + uint8_t socketBegin(uint8_t protocol, uint16_t port); + uint8_t socketBeginMulticast(uint8_t protocol, IPAddress ip,uint16_t port); + uint8_t socketStatus(uint8_t s); + // Close socket + void socketClose(uint8_t s); + // Establish TCP connection (Active connection) + void socketConnect(uint8_t s, uint8_t * addr, uint16_t port); + // disconnect the connection + void socketDisconnect(uint8_t s); + // Establish TCP connection (Passive connection) + uint8_t socketListen(uint8_t s); + // Send data (TCP) + uint16_t socketSend(uint8_t s, const uint8_t * buf, uint16_t len); + uint16_t socketSendAvailable(uint8_t s); + // Receive data (TCP) + int socketRecv(uint8_t s, uint8_t * buf, int16_t len); + uint16_t socketRecvAvailable(uint8_t s); + uint8_t socketPeek(uint8_t s); + // sets up a UDP datagram, the data for which will be provided by one + // or more calls to bufferData and then finally sent with sendUDP. + // return true if the datagram was successfully set up, or false if there was an error + bool socketStartUDP(uint8_t s, uint8_t* addr, uint16_t port); + // copy up to len bytes of data from buf into a UDP datagram to be + // sent later by sendUDP. Allows datagrams to be built up from a series of bufferData calls. + // return Number of bytes successfully buffered + uint16_t socketBufferData(uint8_t s, uint16_t offset, const uint8_t* buf, uint16_t len); + // Send a UDP datagram built up from a sequence of startUDP followed by one or more + // calls to bufferData. + // return true if the datagram was successfully sent, or false if there was an error + bool socketSendUDP(uint8_t s); + // Initialize the "random" source port number + void socketPortRand(uint16_t n); +}; + +extern EthernetClass Ethernet; + + +#define UDP_TX_PACKET_MAX_SIZE 24 + +class EthernetUDP : public UDP { +private: + uint16_t _port; // local port to listen on + IPAddress _remoteIP; // remote IP address for the incoming packet whilst it's being processed + uint16_t _remotePort; // remote port for the incoming packet whilst it's being processed + uint16_t _offset; // offset into the packet being sent + +protected: + uint8_t sockindex; + uint16_t _remaining; // remaining bytes of incoming packet yet to be processed + +public: + EthernetUDP() : sockindex(MAX_SOCK_NUM) {} // Constructor + virtual uint8_t begin(uint16_t); // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use + virtual uint8_t beginMulticast(IPAddress, uint16_t); // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use + virtual void stop(); // 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); + // 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); + // Finish off this packet and send it + // Returns 1 if the packet was sent successfully, 0 if there was an error + virtual int endPacket(); + // Write a single byte into the packet + virtual size_t write(uint8_t); + // Write size bytes from buffer into the packet + virtual size_t write(const uint8_t *buffer, size_t size); + + using Print::write; + + // 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(); + // Number of bytes remaining in the current packet + virtual int available(); + // Read a single byte from the current packet + virtual int read(); + // 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); + // 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) { return read((unsigned char*)buffer, len); }; + // Return the next byte from the current packet without moving on to the next byte + virtual int peek(); + virtual void flush(); // Finish reading the current packet + + // Return the IP address of the host who sent the current incoming packet + virtual IPAddress remoteIP() { return _remoteIP; }; + // Return the port of the host who sent the current incoming packet + virtual uint16_t remotePort() { return _remotePort; }; + virtual uint16_t localPort() { return _port; } +}; + + + + +class EthernetClient : public Client { +public: + EthernetClient() : sockindex(MAX_SOCK_NUM), _timeout(1000) { } + EthernetClient(uint8_t s) : sockindex(s), _timeout(1000) { } + + uint8_t status(); + virtual int connect(IPAddress ip, uint16_t port); + virtual int connect(const char *host, uint16_t port); + virtual int availableForWrite(void); + virtual size_t write(uint8_t); + virtual size_t write(const uint8_t *buf, size_t size); + virtual int available(); + virtual int read(); + virtual int read(uint8_t *buf, size_t size); + virtual int peek(); + virtual void flush(); + virtual void stop(); + virtual uint8_t connected(); + virtual operator bool() { return sockindex < MAX_SOCK_NUM; } + virtual bool operator==(const bool value) { return bool() == value; } + virtual bool operator!=(const bool value) { return bool() != value; } + virtual bool operator==(const EthernetClient&); + virtual bool operator!=(const EthernetClient& rhs) { return !this->operator==(rhs); } + uint8_t getSocketNumber() const { return sockindex; } + virtual uint16_t localPort(); + virtual IPAddress remoteIP(); + virtual uint16_t remotePort(); + virtual void setConnectionTimeout(uint16_t timeout) { _timeout = timeout; } + + friend class EthernetServer; + + using Print::write; + +private: + uint8_t sockindex; // MAX_SOCK_NUM means client not in use + uint16_t _timeout; +}; + + +class EthernetServer : public Server { +private: + uint16_t _port; +public: + EthernetServer(uint16_t port) : _port(port) { } + EthernetClient available(); + EthernetClient accept(); + virtual void begin(); + virtual size_t write(uint8_t); + virtual size_t write(const uint8_t *buf, size_t size); + virtual operator bool(); + using Print::write; + //void statusreport(); + + // TODO: make private when socket allocation moves to EthernetClass + static uint16_t server_port[MAX_SOCK_NUM]; +}; + + +class DhcpClass { +private: + uint32_t _dhcpInitialTransactionId; + uint32_t _dhcpTransactionId; + uint8_t _dhcpMacAddr[6]; +#ifdef __arm__ + uint8_t _dhcpLocalIp[4] __attribute__((aligned(4))); + uint8_t _dhcpSubnetMask[4] __attribute__((aligned(4))); + uint8_t _dhcpGatewayIp[4] __attribute__((aligned(4))); + uint8_t _dhcpDhcpServerIp[4] __attribute__((aligned(4))); + uint8_t _dhcpDnsServerIp[4] __attribute__((aligned(4))); +#else + uint8_t _dhcpLocalIp[4]; + uint8_t _dhcpSubnetMask[4]; + uint8_t _dhcpGatewayIp[4]; + uint8_t _dhcpDhcpServerIp[4]; + uint8_t _dhcpDnsServerIp[4]; +#endif + uint32_t _dhcpLeaseTime; + uint32_t _dhcpT1, _dhcpT2; + uint32_t _renewInSec; + uint32_t _rebindInSec; + unsigned long _timeout; + unsigned long _responseTimeout; + unsigned long _lastCheckLeaseMillis; + uint8_t _dhcp_state; + EthernetUDP _dhcpUdpSocket; + + int request_DHCP_lease(); + void reset_DHCP_lease(); + void presend_DHCP(); + void send_DHCP_MESSAGE(uint8_t, uint16_t); + void printByte(char *, uint8_t); + + uint8_t parseDHCPResponse(unsigned long responseTimeout, uint32_t& transactionId); +public: + IPAddress getLocalIp(); + IPAddress getSubnetMask(); + IPAddress getGatewayIp(); + IPAddress getDhcpServerIp(); + IPAddress getDnsServerIp(); + + int beginWithDHCP(uint8_t *, unsigned long timeout = 60000, unsigned long responseTimeout = 4000); + int checkLease(); +}; + + + + + +#endif diff --git a/LibraryPatches/EthernetLarge/src/EthernetServer.cpp b/LibraryPatches/EthernetLarge/src/EthernetServer.cpp new file mode 100644 index 0000000..a77b569 --- /dev/null +++ b/LibraryPatches/EthernetLarge/src/EthernetServer.cpp @@ -0,0 +1,240 @@ +/**************************************************************************************************************************** + EthernetServer.cpp + + EthernetWebServer is a library for the Ethernet shields to run WebServer + + Based on and modified from ESP8266 https://github.com/esp8266/Arduino/releases + Built by Khoi Hoang https://github.com/khoih-prog/EthernetWebServer + Licensed under MIT license + Version: 1.0.9 + + Copyright 2018 Paul Stoffregen + + 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. + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.0 K Hoang 13/02/2020 Initial coding for Arduino Mega, Teensy, etc to support Ethernetx libraries + 1.0.1 K Hoang 20/02/2020 Add support to lambda functions + 1.0.2 K Hoang 20/02/2020 Add support to UIPEthernet library for ENC28J60 + 1.0.3 K Hoang 23/02/2020 Add support to SAM DUE / SAMD21 boards + 1.0.4 K Hoang 16/04/2020 Add support to SAMD51 boards + 1.0.5 K Hoang 24/04/2020 Add support to nRF52 boards, such as AdaFruit Feather nRF52832, nRF52840 Express, BlueFruit Sense, + Itsy-Bitsy nRF52840 Express, Metro nRF52840 Express, NINA_B30_ublox, etc. + More Custom Ethernet libraries supported such as Ethernet2, Ethernet3, EthernetLarge + 1.0.6 K Hoang 27/04/2020 Add support to ESP32/ESP8266 boards + 1.0.7 K Hoang 30/04/2020 Add ENC28J60 support to ESP32/ESP8266 boards + 1.0.8 K Hoang 12/05/2020 Fix W5x00 support for ESP8266 boards. + 1.0.9 K Hoang 15/05/2020 Add EthernetWrapper.h for easier W5x00 support as well as more Ethernet libs in the future. + *****************************************************************************************************************************/ + +#include +#include "EthernetLarge.h" +#include "utility/w5100.h" + +uint16_t EthernetServer::server_port[MAX_SOCK_NUM]; + + +void EthernetServer::begin() +{ + uint8_t sockindex = Ethernet.socketBegin(SnMR::TCP, _port); + if (sockindex < MAX_SOCK_NUM) { + if (Ethernet.socketListen(sockindex)) { + server_port[sockindex] = _port; + } else { + Ethernet.socketDisconnect(sockindex); + } + } +} + +EthernetClient EthernetServer::available() +{ + bool listening = false; + uint8_t sockindex = MAX_SOCK_NUM; + uint8_t chip, maxindex=MAX_SOCK_NUM; + + chip = W5100.getChip(); + if (!chip) return EthernetClient(MAX_SOCK_NUM); +#if MAX_SOCK_NUM > 4 + if (chip == 51) maxindex = 4; // W5100 chip never supports more than 4 sockets +#endif + + for (uint8_t i=0; i < maxindex; i++) + { + if (server_port[i] == _port) + { + uint8_t stat = Ethernet.socketStatus(i); + if (stat == SnSR::ESTABLISHED || stat == SnSR::CLOSE_WAIT) + { + if (Ethernet.socketRecvAvailable(i) > 0) + { + sockindex = i; + } + else + { + // remote host closed connection, our end still open + if (stat == SnSR::CLOSE_WAIT) + { + Ethernet.socketDisconnect(i); + // status becomes LAST_ACK for short time + } + } + } + else if (stat == SnSR::LISTEN) + { + listening = true; + } + else if (stat == SnSR::CLOSED) + { + server_port[i] = 0; + } + } + } + + if (!listening) + { + begin(); + } + + return EthernetClient(sockindex); +} + +EthernetClient EthernetServer::accept() +{ + bool listening = false; + uint8_t sockindex = MAX_SOCK_NUM; + uint8_t chip, maxindex=MAX_SOCK_NUM; + + chip = W5100.getChip(); + if (!chip) return EthernetClient(MAX_SOCK_NUM); +#if MAX_SOCK_NUM > 4 + if (chip == 51) maxindex = 4; // W5100 chip never supports more than 4 sockets +#endif + + for (uint8_t i=0; i < maxindex; i++) + { + if (server_port[i] == _port) + { + uint8_t stat = Ethernet.socketStatus(i); + + if (sockindex == MAX_SOCK_NUM && (stat == SnSR::ESTABLISHED || stat == SnSR::CLOSE_WAIT)) + { + // Return the connected client even if no data received. + // Some protocols like FTP expect the server to send the + // first data. + sockindex = i; + server_port[i] = 0; // only return the client once + } + else if (stat == SnSR::LISTEN) + { + listening = true; + } + else if (stat == SnSR::CLOSED) + { + server_port[i] = 0; + } + } + } + + if (!listening) + begin(); + + return EthernetClient(sockindex); +} + +EthernetServer::operator bool() +{ + uint8_t maxindex=MAX_SOCK_NUM; +#if MAX_SOCK_NUM > 4 + if (W5100.getChip() == 51) maxindex = 4; // W5100 chip never supports more than 4 sockets +#endif + + for (uint8_t i=0; i < maxindex; i++) + { + if (server_port[i] == _port) + { + if (Ethernet.socketStatus(i) == SnSR::LISTEN) + { + return true; // server is listening for incoming clients + } + } + } + return false; +} + +#if 0 +void EthernetServer::statusreport() +{ + Serial.printf("EthernetServer, port=%d\n", _port); + for (uint8_t i=0; i < MAX_SOCK_NUM; i++) { + uint16_t port = server_port[i]; + uint8_t stat = Ethernet.socketStatus(i); + const char *name; + switch (stat) { + case 0x00: name = "CLOSED"; break; + case 0x13: name = "INIT"; break; + case 0x14: name = "LISTEN"; break; + case 0x15: name = "SYNSENT"; break; + case 0x16: name = "SYNRECV"; break; + case 0x17: name = "ESTABLISHED"; break; + case 0x18: name = "FIN_WAIT"; break; + case 0x1A: name = "CLOSING"; break; + case 0x1B: name = "TIME_WAIT"; break; + case 0x1C: name = "CLOSE_WAIT"; break; + case 0x1D: name = "LAST_ACK"; break; + case 0x22: name = "UDP"; break; + case 0x32: name = "IPRAW"; break; + case 0x42: name = "MACRAW"; break; + case 0x5F: name = "PPPOE"; break; + default: name = "???"; + } + int avail = Ethernet.socketRecvAvailable(i); + Serial.printf(" %d: port=%d, status=%s (0x%02X), avail=%d\n", + i, port, name, stat, avail); + } +} +#endif + +size_t EthernetServer::write(uint8_t b) +{ + return write(&b, 1); +} + +size_t EthernetServer::write(const uint8_t *buffer, size_t size) +{ + uint8_t chip, maxindex=MAX_SOCK_NUM; + + chip = W5100.getChip(); + if (!chip) return 0; +#if MAX_SOCK_NUM > 4 + if (chip == 51) maxindex = 4; // W5100 chip never supports more than 4 sockets +#endif + available(); + + for (uint8_t i=0; i < maxindex; i++) + { + if (server_port[i] == _port) + { + if (Ethernet.socketStatus(i) == SnSR::ESTABLISHED) + { + Ethernet.socketSend(i, buffer, size); + } + } + } + return size; +} diff --git a/LibraryPatches/EthernetLarge/src/utility/w5100.cpp b/LibraryPatches/EthernetLarge/src/utility/w5100.cpp new file mode 100644 index 0000000..2e2e5a6 --- /dev/null +++ b/LibraryPatches/EthernetLarge/src/utility/w5100.cpp @@ -0,0 +1,703 @@ +/**************************************************************************************************************************** + w5100.cpp - Driver for W5x00 + + EthernetWebServer is a library for the Ethernet shields to run WebServer + + Based on and modified from ESP8266 https://github.com/esp8266/Arduino/releases + Built by Khoi Hoang https://github.com/khoih-prog/EthernetWebServer + Licensed under MIT license + Version: 1.0.9 + + Copyright 2018 Paul Stoffregen + Copyright (c) 2010 by Cristian Maglie + + This file is free software; you can redistribute it and/or modify + it under the terms of either the GNU General Public License version 2 + or the GNU Lesser General Public License version 2.1, both as + published by the Free Software Foundation. + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.0 K Hoang 13/02/2020 Initial coding for Arduino Mega, Teensy, etc to support Ethernetx libraries + 1.0.1 K Hoang 20/02/2020 Add support to lambda functions + 1.0.2 K Hoang 20/02/2020 Add support to UIPEthernet library for ENC28J60 + 1.0.3 K Hoang 23/02/2020 Add support to SAM DUE / SAMD21 boards + 1.0.4 K Hoang 16/04/2020 Add support to SAMD51 boards + 1.0.5 K Hoang 24/04/2020 Add support to nRF52 boards, such as AdaFruit Feather nRF52832, nRF52840 Express, BlueFruit Sense, + Itsy-Bitsy nRF52840 Express, Metro nRF52840 Express, NINA_B30_ublox, etc. + More Custom Ethernet libraries supported such as Ethernet2, Ethernet3, EthernetLarge + 1.0.6 K Hoang 27/04/2020 Add support to ESP32/ESP8266 boards + 1.0.7 K Hoang 30/04/2020 Add ENC28J60 support to ESP32/ESP8266 boards + 1.0.8 K Hoang 12/05/2020 Fix W5x00 support for ESP8266 boards. + 1.0.9 K Hoang 15/05/2020 Add EthernetWrapper.h for easier W5x00 support as well as more Ethernet libs in the future. + *****************************************************************************************************************************/ + +#include +#include "EthernetLarge.h" +#include "w5100.h" + +#define W5100_DEBUG 1 + +/***************************************************/ +/** Default SS pin setting **/ +/***************************************************/ + +// If variant.h or other headers specifically define the +// default SS pin for ethernet, use it. +#if defined(PIN_SPI_SS_ETHERNET_LIB) + +#define SS_PIN_DEFAULT PIN_SPI_SS_ETHERNET_LIB +//KH +#warning w5100.cpp Use PIN_SPI_SS_ETHERNET_LIB defined, change SS_PIN_DEFAULT to PIN_SPI_SS_ETHERNET_LIB + +// MKR boards default to pin 5 for MKR ETH +// Pins 8-10 are MOSI/SCK/MISO on MRK, so don't use pin 10 +#elif defined(USE_ARDUINO_MKR_PIN_LAYOUT) || defined(ARDUINO_SAMD_MKRZERO) || defined(ARDUINO_SAMD_MKR1000) || defined(ARDUINO_SAMD_MKRFox1200) || defined(ARDUINO_SAMD_MKRGSM1400) || defined(ARDUINO_SAMD_MKRWAN1300) + +#define SS_PIN_DEFAULT 5 +//KH +#warning w5100.cpp Use MKR, change SS_PIN_DEFAULT to 5 + +// For boards using AVR, assume shields with SS on pin 10 +// will be used. This allows for Arduino Mega (where +// SS is pin 53) and Arduino Leonardo (where SS is pin 17) +// to work by default with Arduino Ethernet Shield R2 & R3. +#elif defined(__AVR__) + +#define SS_PIN_DEFAULT 10 +//KH +#warning w5100.cpp Use __AVR__, change SS_PIN_DEFAULT to 10 + +// If variant.h or other headers define these names +// use them if none of the other cases match +#elif defined(PIN_SPI_SS) + +#if defined(__SAMD21G18A__) +//10 - 2 (6 conflict) all not OK for Nano 33 IoT !!! SPI corrupted??? +#warning w5100.cpp Use __SAMD21G18A__, change SS_PIN_DEFAULT to 10 +#define SS_PIN_DEFAULT 10 +#else +#define SS_PIN_DEFAULT PIN_SPI_SS + +//KH +#warning w5100.cpp Use PIN_SPI_SS defined, change SS_PIN_DEFAULT to PIN_SPI_SS +#endif + +#elif defined(CORE_SS0_PIN) +#define SS_PIN_DEFAULT CORE_SS0_PIN + +//KH +#warning w5100.cpp Use CORE_SS0_PIN defined, change SS_PIN_DEFAULT to CORE_SS0_PIN + +//KH for ESP32 +#elif defined(ESP32) +//pin SS already defined in ESP32 as pin 5, don't use this as conflict with SPIFFS, EEPROM, etc. +// Use in GPIO22 +#warning w5100.cpp Use ESP32, change SS_PIN_DEFAULT to GPIO22, MOSI(23), MISO(19), SCK(18) +#define SS_PIN_DEFAULT 22 //SS +/////// + +//KH for ESP8266 +#elif defined(ESP8266) +//pin SS already defined in ESP8266 as pin 15. Conflict => Move to pin GPIO4 (D2) +#warning w5100.cpp Use ESP8266, change SS_PIN_DEFAULT to SS(4), MOSI(13), MISO(12), SCK(14) +#define SS_PIN_DEFAULT D2 // GPIO4, SS + +/////// + +// As a final fallback, use pin 10 +#else +#define SS_PIN_DEFAULT 10 + +//KH +#warning w5100.cpp Use fallback, change SS_PIN_DEFAULT to 10 + +#endif + +// W5100 controller instance +uint8_t W5100Class::chip = 0; +uint8_t W5100Class::CH_BASE_MSB; +uint8_t W5100Class::ss_pin = SS_PIN_DEFAULT; +#ifdef ETHERNET_LARGE_BUFFERS +uint16_t W5100Class::SSIZE = 2048; +uint16_t W5100Class::SMASK = 0x07FF; +#endif +W5100Class W5100; + +// pointers and bitmasks for optimized SS pin +#if defined(__AVR__) + volatile uint8_t * W5100Class::ss_pin_reg; + uint8_t W5100Class::ss_pin_mask; +#elif defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK66FX1M0__) || defined(__MK64FX512__) + volatile uint8_t * W5100Class::ss_pin_reg; +#elif defined(__IMXRT1062__) + volatile uint32_t * W5100Class::ss_pin_reg; + uint32_t W5100Class::ss_pin_mask; +#elif defined(__MKL26Z64__) + volatile uint8_t * W5100Class::ss_pin_reg; + uint8_t W5100Class::ss_pin_mask; +#elif defined(__SAM3X8E__) || defined(__SAM3A8C__) || defined(__SAM3A4C__) + volatile uint32_t * W5100Class::ss_pin_reg; + uint32_t W5100Class::ss_pin_mask; +#elif defined(__PIC32MX__) + volatile uint32_t * W5100Class::ss_pin_reg; + uint32_t W5100Class::ss_pin_mask; +#elif defined(ARDUINO_ARCH_ESP8266) + volatile uint32_t * W5100Class::ss_pin_reg; + uint32_t W5100Class::ss_pin_mask; +#elif defined(__SAMD21G18A__) + volatile uint32_t * W5100Class::ss_pin_reg; + uint32_t W5100Class::ss_pin_mask; + #warning w5100.cpp Use __SAMD21G18A__ +#endif + +// KH +uint8_t W5100Class::init(uint8_t socketNumbers, uint8_t new_ss_pin) +{ + // KH + uint8_t i; + + if (initialized) return 1; + + // Many Ethernet shields have a CAT811 or similar reset chip + // connected to W5100 or W5200 chips. The W5200 will not work at + // all, and may even drive its MISO pin, until given an active low + // reset pulse! The CAT811 has a 240 ms typical pulse length, and + // a 400 ms worst case maximum pulse length. MAX811 has a worst + // case maximum 560 ms pulse length. This delay is meant to wait + // until the reset pulse is ended. If your hardware has a shorter + // reset time, this can be edited or removed. + delay(560); + + //W5100Class::ss_pin = new_ss_pin; + +#if ( W5100_DEBUG > 0 ) + //KH + Serial.print("\nW5100 init, using SS_PIN_DEFAULT = "); + Serial.print(SS_PIN_DEFAULT); + Serial.print(", new ss_pin = "); + Serial.print(new_ss_pin); + Serial.print(", W5100Class::ss_pin = "); + Serial.println(W5100Class::ss_pin); +#endif + + SPI.begin(); + + initSS(); + resetSS(); + + // From #define SPI_ETHERNET_SETTINGS SPISettings(14000000, MSBFIRST, SPI_MODE0) + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + + // Attempt W5200 detection first, because W5200 does not properly + // reset its SPI state when CS goes high (inactive). Communication + // from detecting the other chips can leave the W5200 in a state + // where it won't recover, unless given a reset pulse. + if (isW5200()) + { + CH_BASE_MSB = 0x40; +#ifdef ETHERNET_LARGE_BUFFERS +#if MAX_SOCK_NUM <= 1 + SSIZE = 16384; +#elif MAX_SOCK_NUM <= 2 + SSIZE = 8192; +#elif MAX_SOCK_NUM <= 4 + SSIZE = 4096; +#else + SSIZE = 2048; +#endif + SMASK = SSIZE - 1; +#endif + for (i=0; i> 10); + writeSnTX_SIZE(i, SSIZE >> 10); + } + for (; i<8; i++) + { + writeSnRX_SIZE(i, 0); + writeSnTX_SIZE(i, 0); + } + +#if ( W5100_DEBUG > 0 ) + Serial.print("W5100::init: W5200, SSIZE ="); + Serial.println(SSIZE); +#endif + + // Try W5500 next. Wiznet finally seems to have implemented + // SPI well with this chip. It appears to be very resilient, + // so try it after the fragile W5200 + } + else if (isW5500()) + { + CH_BASE_MSB = 0x10; +#ifdef ETHERNET_LARGE_BUFFERS +#if MAX_SOCK_NUM <= 1 + SSIZE = 16384; +#elif MAX_SOCK_NUM <= 2 + SSIZE = 8192; +#elif MAX_SOCK_NUM <= 4 + SSIZE = 4096; +#else + SSIZE = 2048; +#endif + SMASK = SSIZE - 1; + for (i=0; i> 10); + writeSnTX_SIZE(i, SSIZE >> 10); + } + for (; i<8; i++) + { + writeSnRX_SIZE(i, 0); + writeSnTX_SIZE(i, 0); + } +#endif + +#if ( W5100_DEBUG > 0 ) + Serial.print("W5100::init: W5500, SSIZE ="); + Serial.println(SSIZE); +#endif + + + // Try W5100 last. This simple chip uses fixed 4 byte frames + // for every 8 bit access. Terribly inefficient, but so simple + // it recovers from "hearing" unsuccessful W5100 or W5200 + // communication. W5100 is also the only chip without a VERSIONR + // register for identification, so we check this last. + } else if (isW5100()) + { + CH_BASE_MSB = 0x04; +#ifdef ETHERNET_LARGE_BUFFERS +#if MAX_SOCK_NUM <= 1 + SSIZE = 8192; + writeTMSR(0x03); + writeRMSR(0x03); +#elif MAX_SOCK_NUM <= 2 + SSIZE = 4096; + writeTMSR(0x0A); + writeRMSR(0x0A); +#else + SSIZE = 2048; + writeTMSR(0x55); + writeRMSR(0x55); +#endif + SMASK = SSIZE - 1; +#else + writeTMSR(0x55); + writeRMSR(0x55); +#endif + +#if ( W5100_DEBUG > 0 ) + Serial.print("W5100::init: W5100, SSIZE ="); + Serial.println(SSIZE); +#endif + + // No hardware seems to be present. Or it could be a W5200 + // that's heard other SPI communication if its chip select + // pin wasn't high when a SD card or other SPI chip was used. + } + else + { +#if ( W5100_DEBUG > 0 ) + Serial.println("no chip :-("); +#endif + + chip = 0; + SPI.endTransaction(); + return 0; // no known chip is responding :-( + } + SPI.endTransaction(); + initialized = true; + return 1; // successful init +} + +// Soft reset the Wiznet chip, by writing to its MR register reset bit +uint8_t W5100Class::softReset(void) +{ + uint16_t count=0; + +#if ( W5100_DEBUG > 1 ) + Serial.println("EthernetLarge:Wiznet soft reset"); +#endif + + // write to reset bit + writeMR(0x80); + // then wait for soft reset to complete + do + { + uint8_t mr = readMR(); + +#if ( W5100_DEBUG > 2 ) + Serial.print("mr="); + Serial.println(mr, HEX); +#endif + + if (mr == 0) + return 1; + + delay(1); + } while (++count < 20); + return 0; +} + + +uint8_t W5100Class::isW5100(void) +{ + chip = 51; + +#if ( W5100_DEBUG > 1 ) + Serial.println("W5100.cpp: detect W5100 chip"); +#endif + + if (!softReset()) + return 0; + + writeMR(0x10); + if (readMR() != 0x10) + return 0; + + writeMR(0x12); + if (readMR() != 0x12) + return 0; + + writeMR(0x00); + if (readMR() != 0x00) + return 0; + +#if ( W5100_DEBUG > 1 ) + Serial.println("chip is W5100"); +#endif + + return 1; +} + +uint8_t W5100Class::isW5200(void) +{ + chip = 52; + +#if ( W5100_DEBUG > 1 ) + Serial.println("W5100.cpp: detect W5200 chip"); +#endif + + if (!softReset()) + return 0; + + writeMR(0x08); + if (readMR() != 0x08) + return 0; + + writeMR(0x10); + if (readMR() != 0x10) + return 0; + + writeMR(0x00); + if (readMR() != 0x00) + return 0; + + int ver = readVERSIONR_W5200(); + +#if ( W5100_DEBUG > 1 ) + Serial.print("version="); + Serial.println(ver); +#endif + + if (ver != 3) + return 0; + +#if ( W5100_DEBUG > 1 ) + Serial.println("chip is W5200"); +#endif + + return 1; +} + +uint8_t W5100Class::isW5500(void) +{ + chip = 55; + +#if ( W5100_DEBUG > 1 ) + Serial.println("W5100.cpp: detect W5500 chip"); +#endif + + if (!softReset()) + return 0; + + writeMR(0x08); + if (readMR() != 0x08) + return 0; + + writeMR(0x10); + if (readMR() != 0x10) + return 0; + + writeMR(0x00); + if (readMR() != 0x00) + return 0; + + int ver = readVERSIONR_W5500(); + +#if ( W5100_DEBUG > 1 ) + Serial.print("version="); + Serial.println(ver); +#endif + + if (ver != 4) + return 0; + +#if ( W5100_DEBUG > 1 ) + Serial.println("chip is W5500"); +#endif + + return 1; +} + +W5100Linkstatus W5100Class::getLinkStatus() +{ + uint8_t phystatus; + + // KH + if (!initialized) return UNKNOWN; + + switch (chip) + { + case 52: + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + phystatus = readPSTATUS_W5200(); + SPI.endTransaction(); + if (phystatus & 0x20) + return LINK_ON; + + return LINK_OFF; + + case 55: + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + phystatus = readPHYCFGR_W5500(); + SPI.endTransaction(); + if (phystatus & 0x01) + return LINK_ON; + + return LINK_OFF; + + default: + return UNKNOWN; + } +} + +uint16_t W5100Class::write(uint16_t addr, const uint8_t *buf, uint16_t len) +{ + uint8_t cmd[8]; + + if (chip == 51) + { + for (uint16_t i=0; i> 8); + SPI.transfer(addr & 0xFF); + addr++; + SPI.transfer(buf[i]); + resetSS(); + } + } + else if (chip == 52) + { + setSS(); + cmd[0] = addr >> 8; + cmd[1] = addr & 0xFF; + cmd[2] = ((len >> 8) & 0x7F) | 0x80; + cmd[3] = len & 0xFF; + SPI.transfer(cmd, 4); + +#ifdef SPI_HAS_TRANSFER_BUF + SPI.transfer(buf, NULL, len); +#else + // TODO: copy 8 bytes at a time to cmd[] and block transfer + for (uint16_t i=0; i < len; i++) + { + SPI.transfer(buf[i]); + } +#endif + resetSS(); + } + else + { + // chip == 55 + setSS(); + if (addr < 0x100) + { + // common registers 00nn + cmd[0] = 0; + cmd[1] = addr & 0xFF; + cmd[2] = 0x04; + } + else if (addr < 0x8000) + { + // socket registers 10nn, 11nn, 12nn, 13nn, etc + cmd[0] = 0; + cmd[1] = addr & 0xFF; + cmd[2] = ((addr >> 3) & 0xE0) | 0x0C; + } + else if (addr < 0xC000) + { + // transmit buffers 8000-87FF, 8800-8FFF, 9000-97FF, etc + // 10## #nnn nnnn nnnn + cmd[0] = addr >> 8; + cmd[1] = addr & 0xFF; + #if defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 1 + cmd[2] = 0x14; // 16K buffers + #elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 2 + cmd[2] = ((addr >> 8) & 0x20) | 0x14; // 8K buffers + #elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 4 + cmd[2] = ((addr >> 7) & 0x60) | 0x14; // 4K buffers + #else + cmd[2] = ((addr >> 6) & 0xE0) | 0x14; // 2K buffers + #endif + } + else + { + // receive buffers + cmd[0] = addr >> 8; + cmd[1] = addr & 0xFF; + #if defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 1 + cmd[2] = 0x1C; // 16K buffers + #elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 2 + cmd[2] = ((addr >> 8) & 0x20) | 0x1C; // 8K buffers + #elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 4 + cmd[2] = ((addr >> 7) & 0x60) | 0x1C; // 4K buffers + #else + cmd[2] = ((addr >> 6) & 0xE0) | 0x1C; // 2K buffers + #endif + } + + if (len <= 5) + { + for (uint8_t i=0; i < len; i++) + { + cmd[i + 3] = buf[i]; + } + + SPI.transfer(cmd, len + 3); + } + else + { + SPI.transfer(cmd, 3); +#ifdef SPI_HAS_TRANSFER_BUF + SPI.transfer(buf, NULL, len); +#else + // TODO: copy 8 bytes at a time to cmd[] and block transfer + for (uint16_t i=0; i < len; i++) + { + SPI.transfer(buf[i]); + } +#endif + } + resetSS(); + } + return len; +} + +uint16_t W5100Class::read(uint16_t addr, uint8_t *buf, uint16_t len) +{ + uint8_t cmd[4]; + + if (chip == 51) + { + for (uint16_t i=0; i < len; i++) + { + setSS(); + #if 1 + SPI.transfer(0x0F); + SPI.transfer(addr >> 8); + SPI.transfer(addr & 0xFF); + addr++; + buf[i] = SPI.transfer(0); + #else + cmd[0] = 0x0F; + cmd[1] = addr >> 8; + cmd[2] = addr & 0xFF; + cmd[3] = 0; + SPI.transfer(cmd, 4); // TODO: why doesn't this work? + buf[i] = cmd[3]; + addr++; + #endif + resetSS(); + } + } + else if (chip == 52) + { + setSS(); + cmd[0] = addr >> 8; + cmd[1] = addr & 0xFF; + cmd[2] = (len >> 8) & 0x7F; + cmd[3] = len & 0xFF; + SPI.transfer(cmd, 4); + memset(buf, 0, len); + SPI.transfer(buf, len); + resetSS(); + } + else + { + // chip == 55 + setSS(); + + if (addr < 0x100) + { + // common registers 00nn + cmd[0] = 0; + cmd[1] = addr & 0xFF; + cmd[2] = 0x00; + } + else if (addr < 0x8000) + { + // socket registers 10nn, 11nn, 12nn, 13nn, etc + cmd[0] = 0; + cmd[1] = addr & 0xFF; + cmd[2] = ((addr >> 3) & 0xE0) | 0x08; + } + else if (addr < 0xC000) + { + // transmit buffers 8000-87FF, 8800-8FFF, 9000-97FF, etc + // 10## #nnn nnnn nnnn + cmd[0] = addr >> 8; + cmd[1] = addr & 0xFF; + #if defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 1 + cmd[2] = 0x10; // 16K buffers + #elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 2 + cmd[2] = ((addr >> 8) & 0x20) | 0x10; // 8K buffers + #elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 4 + cmd[2] = ((addr >> 7) & 0x60) | 0x10; // 4K buffers + #else + cmd[2] = ((addr >> 6) & 0xE0) | 0x10; // 2K buffers + #endif + } else + { + // receive buffers + cmd[0] = addr >> 8; + cmd[1] = addr & 0xFF; + #if defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 1 + cmd[2] = 0x18; // 16K buffers + #elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 2 + cmd[2] = ((addr >> 8) & 0x20) | 0x18; // 8K buffers + #elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 4 + cmd[2] = ((addr >> 7) & 0x60) | 0x18; // 4K buffers + #else + cmd[2] = ((addr >> 6) & 0xE0) | 0x18; // 2K buffers + #endif + } + SPI.transfer(cmd, 3); + memset(buf, 0, len); + SPI.transfer(buf, len); + resetSS(); + } + return len; +} + +void W5100Class::execCmdSn(SOCKET s, SockCMD _cmd) +{ + // Send command to socket + writeSnCR(s, _cmd); + // Wait for command to complete + while (readSnCR(s)) ; +} diff --git a/LibraryPatches/EthernetLarge/src/utility/w5100.h b/LibraryPatches/EthernetLarge/src/utility/w5100.h new file mode 100644 index 0000000..e5a71c0 --- /dev/null +++ b/LibraryPatches/EthernetLarge/src/utility/w5100.h @@ -0,0 +1,632 @@ +/**************************************************************************************************************************** + w5100.cpp - Driver for W5x00 + + EthernetWebServer is a library for the Ethernet shields to run WebServer + + Based on and modified from ESP8266 https://github.com/esp8266/Arduino/releases + Built by Khoi Hoang https://github.com/khoih-prog/EthernetWebServer + Licensed under MIT license + Version: 1.0.9 + + Copyright 2018 Paul Stoffregen + Copyright (c) 2010 by Cristian Maglie + + This file is free software; you can redistribute it and/or modify + it under the terms of either the GNU General Public License version 2 + or the GNU Lesser General Public License version 2.1, both as + published by the Free Software Foundation. + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.0 K Hoang 13/02/2020 Initial coding for Arduino Mega, Teensy, etc to support Ethernetx libraries + 1.0.1 K Hoang 20/02/2020 Add support to lambda functions + 1.0.2 K Hoang 20/02/2020 Add support to UIPEthernet library for ENC28J60 + 1.0.3 K Hoang 23/02/2020 Add support to SAM DUE / SAMD21 boards + 1.0.4 K Hoang 16/04/2020 Add support to SAMD51 boards + 1.0.5 K Hoang 24/04/2020 Add support to nRF52 boards, such as AdaFruit Feather nRF52832, nRF52840 Express, BlueFruit Sense, + Itsy-Bitsy nRF52840 Express, Metro nRF52840 Express, NINA_B30_ublox, etc. + More Custom Ethernet libraries supported such as Ethernet2, Ethernet3, EthernetLarge + 1.0.6 K Hoang 27/04/2020 Add support to ESP32/ESP8266 boards + 1.0.7 K Hoang 30/04/2020 Add ENC28J60 support to ESP32/ESP8266 boards + 1.0.8 K Hoang 12/05/2020 Fix W5x00 support for ESP8266 boards. + 1.0.9 K Hoang 15/05/2020 Add EthernetWrapper.h for easier W5x00 support as well as more Ethernet libs in the future. + *****************************************************************************************************************************/ + +// w5100.h contains private W5x00 hardware "driver" level definitions +// which are not meant to be exposed to other libraries or Arduino users + +#ifndef W5100_H_INCLUDED +#define W5100_H_INCLUDED + +#include +#include + +#ifndef USE_W5100 +#define USE_W5100 false +#else +#define USE_W5100 true +#endif + +#if !USE_W5100 + +// Safe for W5200 and W5500, but also tested OK on W5100 +// Use 14MHz if you know your W5100 can't run +// Higher SPI clock results in faster transfer to hosts on a LAN +// or with very low packet latency. With ordinary internet latency, +// the TCP window size & packet loss determine your overall speed. +#warning Use 25MHz clock for W5200/W5500. Not for W5100 +#define SPI_ETHERNET_SETTINGS SPISettings(25000000, MSBFIRST, SPI_MODE0) + +#else + +// Safe for all chips but too slow +#define SPI_ETHERNET_SETTINGS SPISettings(14000000, MSBFIRST, SPI_MODE0) +#warning Use 14MHz clock for W5100/W5200/W5500. Slow. + +#endif + + +// Require Ethernet.h, because we need MAX_SOCK_NUM +#ifndef ethernet_h_ +#error "EthernetLarge.h must be included before w5100.h" +#endif + + +// Arduino 101's SPI can not run faster than 8 MHz. +#if defined(ARDUINO_ARCH_ARC32) +#undef SPI_ETHERNET_SETTINGS +#define SPI_ETHERNET_SETTINGS SPISettings(8000000, MSBFIRST, SPI_MODE0) +#endif + +// Arduino Zero can't use W5100-based shields faster than 8 MHz +// https://github.com/arduino-libraries/Ethernet/issues/37#issuecomment-408036848 +// W5500 does seem to work at 12 MHz. Delete this if only using W5500 +#if defined(__SAMD21G18A__) +#undef SPI_ETHERNET_SETTINGS +//#warning Use SAMD21 architecture SPISettings(8000000, MSBFIRST, SPI_MODE3) => IP OK +#warning Use SAMD21 architecture SPISettings(30000000, MSBFIRST, SPI_MODE3) => IP OK +// Still not working !!! Original SPI_MODE0 not OK at all +//#define SPI_ETHERNET_SETTINGS SPISettings(8000000, MSBFIRST, SPI_MODE3) +#define SPI_ETHERNET_SETTINGS SPISettings(30000000, MSBFIRST, SPI_MODE3) +#endif + + +typedef uint8_t SOCKET; + +class SnMR { +public: + static const uint8_t CLOSE = 0x00; + static const uint8_t TCP = 0x21; + static const uint8_t UDP = 0x02; + static const uint8_t IPRAW = 0x03; + static const uint8_t MACRAW = 0x04; + static const uint8_t PPPOE = 0x05; + static const uint8_t ND = 0x20; + static const uint8_t MULTI = 0x80; +}; + +enum SockCMD { + Sock_OPEN = 0x01, + Sock_LISTEN = 0x02, + Sock_CONNECT = 0x04, + Sock_DISCON = 0x08, + Sock_CLOSE = 0x10, + Sock_SEND = 0x20, + Sock_SEND_MAC = 0x21, + Sock_SEND_KEEP = 0x22, + Sock_RECV = 0x40 +}; + +class SnIR { +public: + static const uint8_t SEND_OK = 0x10; + static const uint8_t TIMEOUT = 0x08; + static const uint8_t RECV = 0x04; + static const uint8_t DISCON = 0x02; + static const uint8_t CON = 0x01; +}; + +class SnSR { +public: + static const uint8_t CLOSED = 0x00; + static const uint8_t INIT = 0x13; + static const uint8_t LISTEN = 0x14; + static const uint8_t SYNSENT = 0x15; + static const uint8_t SYNRECV = 0x16; + static const uint8_t ESTABLISHED = 0x17; + static const uint8_t FIN_WAIT = 0x18; + static const uint8_t CLOSING = 0x1A; + static const uint8_t TIME_WAIT = 0x1B; + static const uint8_t CLOSE_WAIT = 0x1C; + static const uint8_t LAST_ACK = 0x1D; + static const uint8_t UDP = 0x22; + static const uint8_t IPRAW = 0x32; + static const uint8_t MACRAW = 0x42; + static const uint8_t PPPOE = 0x5F; +}; + +class IPPROTO { +public: + static const uint8_t IP = 0; + static const uint8_t ICMP = 1; + static const uint8_t IGMP = 2; + static const uint8_t GGP = 3; + static const uint8_t TCP = 6; + static const uint8_t PUP = 12; + static const uint8_t UDP = 17; + static const uint8_t IDP = 22; + static const uint8_t ND = 77; + static const uint8_t RAW = 255; +}; + +enum W5100Linkstatus { + UNKNOWN, + LINK_ON, + LINK_OFF +}; + +class W5100Class { + +public: + // KH + uint8_t init(uint8_t socketNumbers = MAX_SOCK_NUM, uint8_t new_ss_pin = 10); + + inline void setGatewayIp(const uint8_t * addr) { writeGAR(addr); } + inline void getGatewayIp(uint8_t * addr) { readGAR(addr); } + + inline void setSubnetMask(const uint8_t * addr) { writeSUBR(addr); } + inline void getSubnetMask(uint8_t * addr) { readSUBR(addr); } + + inline void setMACAddress(const uint8_t * addr) { writeSHAR(addr); } + inline void getMACAddress(uint8_t * addr) { readSHAR(addr); } + + inline void setIPAddress(const uint8_t * addr) { writeSIPR(addr); } + inline void getIPAddress(uint8_t * addr) { readSIPR(addr); } + + inline void setRetransmissionTime(uint16_t timeout) { writeRTR(timeout); } + inline void setRetransmissionCount(uint8_t retry) { writeRCR(retry); } + + static void execCmdSn(SOCKET s, SockCMD _cmd); + + + // W5100 Registers + // --------------- +//private: +public: + static uint16_t write(uint16_t addr, const uint8_t *buf, uint16_t len); + + static uint8_t write(uint16_t addr, uint8_t data) + { + return write(addr, &data, 1); + } + + static uint16_t read(uint16_t addr, uint8_t *buf, uint16_t len); + + static uint8_t read(uint16_t addr) + { + uint8_t data; + read(addr, &data, 1); + return data; + } + +#define __GP_REGISTER8(name, address) \ + static inline void write##name(uint8_t _data) { \ + write(address, _data); \ + } \ + static inline uint8_t read##name() { \ + return read(address); \ + } +#define __GP_REGISTER16(name, address) \ + static void write##name(uint16_t _data) { \ + uint8_t buf[2]; \ + buf[0] = _data >> 8; \ + buf[1] = _data & 0xFF; \ + write(address, buf, 2); \ + } \ + static uint16_t read##name() { \ + uint8_t buf[2]; \ + read(address, buf, 2); \ + return (buf[0] << 8) | buf[1]; \ + } +#define __GP_REGISTER_N(name, address, size) \ + static uint16_t write##name(const uint8_t *_buff) { \ + return write(address, _buff, size); \ + } \ + static uint16_t read##name(uint8_t *_buff) { \ + return read(address, _buff, size); \ + } + + // KH + W5100Linkstatus getLinkStatus(); + + +public: + __GP_REGISTER8 (MR, 0x0000); // Mode + __GP_REGISTER_N(GAR, 0x0001, 4); // Gateway IP address + __GP_REGISTER_N(SUBR, 0x0005, 4); // Subnet mask address + __GP_REGISTER_N(SHAR, 0x0009, 6); // Source MAC address + __GP_REGISTER_N(SIPR, 0x000F, 4); // Source IP address + __GP_REGISTER8 (IR, 0x0015); // Interrupt + __GP_REGISTER8 (IMR, 0x0016); // Interrupt Mask + __GP_REGISTER16(RTR, 0x0017); // Timeout address + __GP_REGISTER8 (RCR, 0x0019); // Retry count + __GP_REGISTER8 (RMSR, 0x001A); // Receive memory size (W5100 only) + __GP_REGISTER8 (TMSR, 0x001B); // Transmit memory size (W5100 only) + __GP_REGISTER8 (PATR, 0x001C); // Authentication type address in PPPoE mode + __GP_REGISTER8 (PTIMER, 0x0028); // PPP LCP Request Timer + __GP_REGISTER8 (PMAGIC, 0x0029); // PPP LCP Magic Number + __GP_REGISTER_N(UIPR, 0x002A, 4); // Unreachable IP address in UDP mode (W5100 only) + __GP_REGISTER16(UPORT, 0x002E); // Unreachable Port address in UDP mode (W5100 only) + __GP_REGISTER8 (VERSIONR_W5200,0x001F); // Chip Version Register (W5200 only) + __GP_REGISTER8 (VERSIONR_W5500,0x0039); // Chip Version Register (W5500 only) + __GP_REGISTER8 (PSTATUS_W5200, 0x0035); // PHY Status + __GP_REGISTER8 (PHYCFGR_W5500, 0x002E); // PHY Configuration register, default: 10111xxx + + +#undef __GP_REGISTER8 +#undef __GP_REGISTER16 +#undef __GP_REGISTER_N + + // W5100 Socket registers + // ---------------------- +private: + static uint16_t CH_BASE(void) { + //if (chip == 55) return 0x1000; + //if (chip == 52) return 0x4000; + //return 0x0400; + return CH_BASE_MSB << 8; + } + static uint8_t CH_BASE_MSB; // 1 redundant byte, saves ~80 bytes code on AVR + static const uint16_t CH_SIZE = 0x0100; + + static inline uint8_t readSn(SOCKET s, uint16_t addr) + { + return read(CH_BASE() + s * CH_SIZE + addr); + } + static inline uint8_t writeSn(SOCKET s, uint16_t addr, uint8_t data) + { + return write(CH_BASE() + s * CH_SIZE + addr, data); + } + static inline uint16_t readSn(SOCKET s, uint16_t addr, uint8_t *buf, uint16_t len) + { + return read(CH_BASE() + s * CH_SIZE + addr, buf, len); + } + static inline uint16_t writeSn(SOCKET s, uint16_t addr, uint8_t *buf, uint16_t len) + { + return write(CH_BASE() + s * CH_SIZE + addr, buf, len); + } + +#define __SOCKET_REGISTER8(name, address) \ + static inline void write##name(SOCKET _s, uint8_t _data) { \ + writeSn(_s, address, _data); \ + } \ + static inline uint8_t read##name(SOCKET _s) { \ + return readSn(_s, address); \ + } +#define __SOCKET_REGISTER16(name, address) \ + static void write##name(SOCKET _s, uint16_t _data) { \ + uint8_t buf[2]; \ + buf[0] = _data >> 8; \ + buf[1] = _data & 0xFF; \ + writeSn(_s, address, buf, 2); \ + } \ + static uint16_t read##name(SOCKET _s) { \ + uint8_t buf[2]; \ + readSn(_s, address, buf, 2); \ + return (buf[0] << 8) | buf[1]; \ + } +#define __SOCKET_REGISTER_N(name, address, size) \ + static uint16_t write##name(SOCKET _s, uint8_t *_buff) { \ + return writeSn(_s, address, _buff, size); \ + } \ + static uint16_t read##name(SOCKET _s, uint8_t *_buff) { \ + return readSn(_s, address, _buff, size); \ + } + +public: + __SOCKET_REGISTER8(SnMR, 0x0000) // Mode + __SOCKET_REGISTER8(SnCR, 0x0001) // Command + __SOCKET_REGISTER8(SnIR, 0x0002) // Interrupt + __SOCKET_REGISTER8(SnSR, 0x0003) // Status + __SOCKET_REGISTER16(SnPORT, 0x0004) // Source Port + __SOCKET_REGISTER_N(SnDHAR, 0x0006, 6) // Destination Hardw Addr + __SOCKET_REGISTER_N(SnDIPR, 0x000C, 4) // Destination IP Addr + __SOCKET_REGISTER16(SnDPORT, 0x0010) // Destination Port + __SOCKET_REGISTER16(SnMSSR, 0x0012) // Max Segment Size + __SOCKET_REGISTER8(SnPROTO, 0x0014) // Protocol in IP RAW Mode + __SOCKET_REGISTER8(SnTOS, 0x0015) // IP TOS + __SOCKET_REGISTER8(SnTTL, 0x0016) // IP TTL + __SOCKET_REGISTER8(SnRX_SIZE, 0x001E) // RX Memory Size (W5200 only) + __SOCKET_REGISTER8(SnTX_SIZE, 0x001F) // RX Memory Size (W5200 only) + __SOCKET_REGISTER16(SnTX_FSR, 0x0020) // TX Free Size + __SOCKET_REGISTER16(SnTX_RD, 0x0022) // TX Read Pointer + __SOCKET_REGISTER16(SnTX_WR, 0x0024) // TX Write Pointer + __SOCKET_REGISTER16(SnRX_RSR, 0x0026) // RX Free Size + __SOCKET_REGISTER16(SnRX_RD, 0x0028) // RX Read Pointer + __SOCKET_REGISTER16(SnRX_WR, 0x002A) // RX Write Pointer (supported?) + +#undef __SOCKET_REGISTER8 +#undef __SOCKET_REGISTER16 +#undef __SOCKET_REGISTER_N + + +private: + // KH + bool initialized = false; + static uint8_t chip; + static uint8_t ss_pin; + + static uint8_t isW5100(void); + static uint8_t isW5200(void); + static uint8_t isW5500(void); + +public: + // KH + static uint8_t softReset(void); + static uint8_t getChip(void) { return chip; } +#ifdef ETHERNET_LARGE_BUFFERS + static uint16_t SSIZE; + static uint16_t SMASK; +#else + static const uint16_t SSIZE = 2048; + static const uint16_t SMASK = 0x07FF; +#endif + static uint16_t SBASE(uint8_t socknum) + { + if (chip == 51) + { + return socknum * SSIZE + 0x4000; + } + else + { + return socknum * SSIZE + 0x8000; + } + } + + static uint16_t RBASE(uint8_t socknum) + { + if (chip == 51) { + return socknum * SSIZE + 0x6000; + } + else + { + return socknum * SSIZE + 0xC000; + } + } + + static bool hasOffsetAddressMapping(void) + { + if (chip == 55) + return true; + + return false; + } + + static void setSS(uint8_t pin) { ss_pin = pin; } + +private: +#if defined(__AVR__) + +#warning Use AVR architecture + + static volatile uint8_t *ss_pin_reg; + static uint8_t ss_pin_mask; + + inline static void initSS() + { + ss_pin_reg = portOutputRegister(digitalPinToPort(ss_pin)); + ss_pin_mask = digitalPinToBitMask(ss_pin); + pinMode(ss_pin, OUTPUT); + } + + inline static void setSS() + { + *(ss_pin_reg) &= ~ss_pin_mask; + } + + inline static void resetSS() + { + *(ss_pin_reg) |= ss_pin_mask; + } +#elif defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK66FX1M0__) || defined(__MK64FX512__) + +#warning Use MK architecture + + static volatile uint8_t *ss_pin_reg; + + inline static void initSS() + { + ss_pin_reg = portOutputRegister(ss_pin); + pinMode(ss_pin, OUTPUT); + } + + inline static void setSS() + { + *(ss_pin_reg+256) = 1; + } + + inline static void resetSS() + { + *(ss_pin_reg+128) = 1; + } +#elif defined(__IMXRT1062__) + +#warning Use Teensy architecture + + static volatile uint32_t *ss_pin_reg; + static uint32_t ss_pin_mask; + + inline static void initSS() + { + ss_pin_reg = portOutputRegister(digitalPinToPort(ss_pin)); + ss_pin_mask = digitalPinToBitMask(ss_pin); + pinMode(ss_pin, OUTPUT); + } + + inline static void setSS() + { + *(ss_pin_reg+34) = ss_pin_mask; + } + + inline static void resetSS() + { + *(ss_pin_reg+33) = ss_pin_mask; + } +#elif defined(__MKL26Z64__) + static volatile uint8_t *ss_pin_reg; + static uint8_t ss_pin_mask; + inline static void initSS() + { + ss_pin_reg = portOutputRegister(digitalPinToPort(ss_pin)); + ss_pin_mask = digitalPinToBitMask(ss_pin); + pinMode(ss_pin, OUTPUT); + } + + inline static void setSS() + { + *(ss_pin_reg+8) = ss_pin_mask; + } + + inline static void resetSS() + { + *(ss_pin_reg+4) = ss_pin_mask; + } +#elif defined(__SAM3X8E__) || defined(__SAM3A8C__) || defined(__SAM3A4C__) + +#warning Use SAM3 architecture + + static volatile uint32_t *ss_pin_reg; + static uint32_t ss_pin_mask; + + inline static void initSS() + { + ss_pin_reg = &(digitalPinToPort(ss_pin)->PIO_PER); + ss_pin_mask = digitalPinToBitMask(ss_pin); + pinMode(ss_pin, OUTPUT); + } + + inline static void setSS() + { + *(ss_pin_reg+13) = ss_pin_mask; + } + + inline static void resetSS() + { + *(ss_pin_reg+12) = ss_pin_mask; + } +#elif defined(__PIC32MX__) + static volatile uint32_t *ss_pin_reg; + static uint32_t ss_pin_mask; + + inline static void initSS() + { + ss_pin_reg = portModeRegister(digitalPinToPort(ss_pin)); + ss_pin_mask = digitalPinToBitMask(ss_pin); + pinMode(ss_pin, OUTPUT); + } + + inline static void setSS() + { + *(ss_pin_reg+8+1) = ss_pin_mask; + } + + inline static void resetSS() + { + *(ss_pin_reg+8+2) = ss_pin_mask; + } + +#elif defined(ARDUINO_ARCH_ESP8266) + +#warning Use ARDUINO_ARCH_ESP8266 architecture + + static volatile uint32_t *ss_pin_reg; + static uint32_t ss_pin_mask; + + inline static void initSS() + { + ss_pin_reg = (volatile uint32_t*)GPO; + ss_pin_mask = 1 << ss_pin; + pinMode(ss_pin, OUTPUT); + } + + inline static void setSS() + { + GPOC = ss_pin_mask; + } + + inline static void resetSS() + { + GPOS = ss_pin_mask; + } + +#elif defined(__SAMD21G18A__) + +#warning Use SAMD21 architecture + + static volatile uint32_t *ss_pin_reg; + static uint32_t ss_pin_mask; + + inline static void initSS() + { + ss_pin_reg = portModeRegister(digitalPinToPort(ss_pin)); + ss_pin_mask = digitalPinToBitMask(ss_pin); + pinMode(ss_pin, OUTPUT); + } + + inline static void setSS() + { + *(ss_pin_reg+5) = ss_pin_mask; + } + + inline static void resetSS() + { + *(ss_pin_reg+6) = ss_pin_mask; + } +#else + +#warning Use Default architecture + + inline static void initSS() + { + pinMode(ss_pin, OUTPUT); + } + + inline static void setSS() + { + digitalWrite(ss_pin, LOW); + } + + inline static void resetSS() + { + digitalWrite(ss_pin, HIGH); + } +#endif +}; + +extern W5100Class W5100; + +#endif + +#ifndef UTIL_H +#define UTIL_H + +#ifndef htons +#define htons(x) ( (((x)<<8)&0xFF00) | (((x)>>8)&0xFF) ) +#endif + +#ifndef ntohs +#define ntohs(x) htons(x) +#endif + +#ifndef htonl +#define htonl(x) ( ((x)<<24 & 0xFF000000UL) | \ + ((x)<< 8 & 0x00FF0000UL) | \ + ((x)>> 8 & 0x0000FF00UL) | \ + ((x)>>24 & 0x000000FFUL) ) +#endif + +#ifndef ntohl +#define ntohl(x) htonl(x) +#endif + +#endif //W5100_H_INCLUDED diff --git a/LibraryPatches/UIPEthernet-2.0.9/UIPEthernet.cpp b/LibraryPatches/UIPEthernet-2.0.9/UIPEthernet.cpp new file mode 100644 index 0000000..2af3cfb --- /dev/null +++ b/LibraryPatches/UIPEthernet-2.0.9/UIPEthernet.cpp @@ -0,0 +1,625 @@ +/* + UIPEthernet.cpp - Arduino implementation of a uIP wrapper class. + Copyright (c) 2013 Norbert Truchsess + All rights reserved. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#if defined(ARDUINO) + #include +#endif +#if defined(__MBED__) + #include + #include "mbed/millis.h" +#endif +#include "UIPEthernet.h" +#include "utility/logging.h" +#include "utility/Enc28J60Network.h" + +#include "UIPUdp.h" + +extern "C" +{ +#include "utility/uipopt.h" +#include "utility/uip.h" +#include "utility/uip_arp.h" +} + +#define ETH_HDR ((struct uip_eth_hdr *)&uip_buf[0]) + +memhandle UIPEthernetClass::in_packet(NOBLOCK); +memhandle UIPEthernetClass::uip_packet(NOBLOCK); +uint8_t UIPEthernetClass::uip_hdrlen(0); +uint8_t UIPEthernetClass::packetstate(0); + +unsigned long UIPEthernetClass::periodic_timer; + +IPAddress UIPEthernetClass::_dnsServerAddress; +#if UIP_UDP + DhcpClass* UIPEthernetClass::_dhcp(NULL); + static DhcpClass s_dhcp; // Placing this instance here is saving 40K to final *.bin (see bug below) +#endif + +// Because uIP isn't encapsulated within a class we have to use global +// variables, so we can only have one TCP/IP stack per program. + +UIPEthernetClass::UIPEthernetClass() +{ +} + +void UIPEthernetClass::init(const uint8_t pin) +{ + ENC28J60ControlCS = pin; +} + +#if UIP_UDP +int +UIPEthernetClass::begin(const uint8_t* mac) +{ + // KH mod to work with new func void MACAddress(uint8_t *mac_address); and SinricPro v2.5.1+ + // Now store to private var _mac_address + //uint8_t _mac_address[6] ={0,}; + memcpy(_mac_address, mac, sizeof(_mac_address)); + ////// + + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("UIPEthernetClass::begin(const uint8_t* mac) DEBUG_V3:Function started")); + #endif + //static DhcpClass s_dhcp; // <-- this is a bug ! + // I leave it there commented for history. It is bring all GCC "new" memory allocation code, making the *.bin almost 40K bigger. I've move it globally. + _dhcp = &s_dhcp; + // Initialise the basic info + netInit(mac); + + // Now try to get our config info from a DHCP server + int ret = _dhcp->beginWithDHCP((uint8_t*)mac); + if(ret == 1) + { + // We've successfully found a DHCP server and got our configuration info, so set things + // accordingly + configure(_dhcp->getLocalIp(),_dhcp->getDnsServerIp(),_dhcp->getGatewayIp(),_dhcp->getSubnetMask()); + } + return ret; +} +#endif + +void +UIPEthernetClass::begin(const uint8_t* mac, IPAddress ip) +{ + // KH mod to work with new func void MACAddress(uint8_t *mac_address); and SinricPro v2.5.1+ + // Now store to private var _mac_address + //uint8_t _mac_address[6] ={0,}; + memcpy(_mac_address, mac, sizeof(_mac_address)); + ////// + + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("UIPEthernetClass::begin(const uint8_t* mac, IPAddress ip) DEBUG_V3:Function started")); + #endif + IPAddress dns = ip; + dns[3] = 1; + begin(mac, ip, dns); +} + +void +UIPEthernetClass::begin(const uint8_t* mac, IPAddress ip, IPAddress dns) +{ + // KH mod to work with new func void MACAddress(uint8_t *mac_address); and SinricPro v2.5.1+ + // Now store to private var _mac_address + //uint8_t _mac_address[6] ={0,}; + memcpy(_mac_address, mac, sizeof(_mac_address)); + ////// + + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("UIPEthernetClass::begin(const uint8_t* mac, IPAddress ip, IPAddress dns) DEBUG_V3:Function started")); + #endif + IPAddress gateway = ip; + gateway[3] = 1; + begin(mac, ip, dns, gateway); +} + +void +UIPEthernetClass::begin(const uint8_t* mac, IPAddress ip, IPAddress dns, IPAddress gateway) +{ + // KH mod to work with new func void MACAddress(uint8_t *mac_address); and SinricPro v2.5.1+ + // Now store to private var _mac_address + //uint8_t _mac_address[6] ={0,}; + memcpy(_mac_address, mac, sizeof(_mac_address)); + ////// + + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("UIPEthernetClass::begin(const uint8_t* mac, IPAddress ip, IPAddress dns, IPAddress gateway) DEBUG_V3:Function started")); + #endif + IPAddress subnet(255, 255, 255, 0); + begin(mac, ip, dns, gateway, subnet); +} + +void +UIPEthernetClass::begin(const uint8_t* mac, IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet) +{ + // KH mod to work with new func void MACAddress(uint8_t *mac_address); and SinricPro v2.5.1+ + // Now store to private var _mac_address + //uint8_t _mac_address[6] ={0,}; + memcpy(_mac_address, mac, sizeof(_mac_address)); + ////// + + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("UIPEthernetClass::begin(const uint8_t* mac, IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet) DEBUG_V3:Function started")); + #endif + netInit(mac); + configure(ip,dns,gateway,subnet); +} + +int UIPEthernetClass::maintain(){ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("UIPEthernetClass::maintain() DEBUG_V3:Function started")); + #endif + tick(); + int rc = DHCP_CHECK_NONE; +#if UIP_UDP + if(_dhcp != NULL){ + //we have a pointer to dhcp, use it + rc = _dhcp->checkLease(); + switch ( rc ){ + case DHCP_CHECK_NONE: + //nothing done + break; + case DHCP_CHECK_RENEW_OK: + case DHCP_CHECK_REBIND_OK: + //we might have got a new IP. + configure(_dhcp->getLocalIp(),_dhcp->getDnsServerIp(),_dhcp->getGatewayIp(),_dhcp->getSubnetMask()); + break; + default: + //this is actually a error, it will retry though + break; + } + } + return rc; +#endif +} + +EthernetLinkStatus UIPEthernetClass::linkStatus() +{ + if (!Enc28J60Network::geterevid()) + return Unknown; + return Enc28J60Network::linkStatus() ? LinkON : LinkOFF; +} + +EthernetHardwareStatus UIPEthernetClass::hardwareStatus() { + if (!Enc28J60Network::geterevid()) + return EthernetNoHardware; + return EthernetENC28J60; +} + +IPAddress UIPEthernetClass::localIP() +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("UIPEthernetClass::localIP() DEBUG_V3:Function started")); + #endif + IPAddress ret; + uip_ipaddr_t a; + uip_gethostaddr(a); + return ip_addr_uip(a); +} + +IPAddress UIPEthernetClass::subnetMask() +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("UIPEthernetClass::subnetMask() DEBUG_V3:Function started")); + #endif + IPAddress ret; + uip_ipaddr_t a; + uip_getnetmask(a); + return ip_addr_uip(a); +} + +IPAddress UIPEthernetClass::gatewayIP() +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("UIPEthernetClass::gatewayIP() DEBUG_V3:Function started")); + #endif + IPAddress ret; + uip_ipaddr_t a; + uip_getdraddr(a); + return ip_addr_uip(a); +} + +IPAddress UIPEthernetClass::dnsServerIP() +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("UIPEthernetClass::dnsServerIP() DEBUG_V3:Function started")); + #endif + return _dnsServerAddress; +} + +void +UIPEthernetClass::tick() +{ +#if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("UIPEthernetClass::tick() DEBUG_V3:Function started")); +#endif +if (Enc28J60Network::geterevid()==0) + { + #if ACTLOGLEVEL>=LOG_ERR + LogObject.uart_send_strln(F("UIPEthernetClass::tick() ERROR:EREVID=0 -> Not found ENC28j60 device !! Function ended !!")); + #endif + return; + } +#if defined(ESP8266) + wdt_reset(); +#endif + if (in_packet == NOBLOCK) + { + in_packet = Enc28J60Network::receivePacket(); + #if ACTLOGLEVEL>=LOG_DEBUG + if (in_packet != NOBLOCK) + { + LogObject.uart_send_str(F("UIPEthernetClass::tick() DEBUG:receivePacket: ")); + LogObject.uart_send_decln(in_packet); + } + #endif + } + if (in_packet != NOBLOCK) + { + packetstate = UIPETHERNET_FREEPACKET; + uip_len = Enc28J60Network::blockSize(in_packet); + if (uip_len > 0) + { + Enc28J60Network::readPacket(in_packet,0,(uint8_t*)uip_buf,UIP_BUFSIZE); + if (ETH_HDR ->type == HTONS(UIP_ETHTYPE_IP)) + { + uip_packet = in_packet; //required for upper_layer_checksum of in_packet! + #if ACTLOGLEVEL>=LOG_DEBUG + LogObject.uart_send_str(F("UIPEthernetClass::tick() DEBUG:readPacket type IP, uip_len: ")); + LogObject.uart_send_decln(uip_len); + #endif + uip_arp_ipin(); + uip_input(); + if (uip_len > 0) + { + uip_arp_out(); + network_send(); + } + } + else if (ETH_HDR ->type == HTONS(UIP_ETHTYPE_ARP)) + { + #if ACTLOGLEVEL>=LOG_DEBUG + LogObject.uart_send_str(F("UIPEthernetClass::tick() DEBUG:readPacket type ARP, uip_len: ")); + LogObject.uart_send_decln(uip_len); + #endif + uip_arp_arpin(); + if (uip_len > 0) + { + network_send(); + } + } + } + if (in_packet != NOBLOCK && (packetstate & UIPETHERNET_FREEPACKET)) + { + #if ACTLOGLEVEL>=LOG_DEBUG + LogObject.uart_send_str(F("UIPEthernetClass::tick() DEBUG:freeing packet: ")); + LogObject.uart_send_decln(in_packet); + #endif + Enc28J60Network::freePacket(); + in_packet = NOBLOCK; + } + } + + unsigned long now = millis(); + +#if UIP_CLIENT_TIMER >= 0 + bool periodic = (long)( now - periodic_timer ) >= 0; + for (int i = 0; i < UIP_CONNS; i++) + { +#else + if ((long)( now - periodic_timer ) >= 0) + { + periodic_timer = now + UIP_PERIODIC_TIMER; + + for (int i = 0; i < UIP_CONNS; i++) + { +#endif + + uip_conn = &uip_conns[i]; + +#if UIP_CLIENT_TIMER >= 0 + if (periodic) + { +#endif + + uip_process(UIP_TIMER); + +#if UIP_CLIENT_TIMER >= 0 + } + else + { + if (((uip_userdata_t*)uip_conn->appstate)!=NULL) + { + if ((long)( now - ((uip_userdata_t*)uip_conn->appstate)->timer) >= 0) + { + uip_process(UIP_POLL_REQUEST); + ((uip_userdata_t*)uip_conn->appstate)->timer = millis() + UIP_CLIENT_TIMER; + } + else + { + continue; + } + } + else + continue; + } +#endif + // If the above function invocation resulted in data that + // should be sent out on the Enc28J60Network, the global variable + // uip_len is set to a value > 0. + if (uip_len > 0) + { + uip_arp_out(); + network_send(); + } + } +#if UIP_CLIENT_TIMER >= 0 + if (periodic) + { + periodic_timer = now + UIP_PERIODIC_TIMER; +#endif +#if UIP_UDP + for (int i = 0; i < UIP_UDP_CONNS; i++) + { + uip_udp_periodic(i); + // If the above function invocation resulted in data that + // should be sent out on the Enc28J60Network, the global variable + // uip_len is set to a value > 0. */ + if (uip_len > 0) + { + UIPUDP::_send((uip_udp_userdata_t *)(uip_udp_conns[i].appstate)); + } + } +#endif /* UIP_UDP */ + } +} + +bool UIPEthernetClass::network_send() +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("UIPEthernetClass::network_send() DEBUG_V3:Function started")); + #endif + if (packetstate & UIPETHERNET_SENDPACKET) + { +#if ACTLOGLEVEL>=LOG_DEBUG + LogObject.uart_send_str(F("UIPEthernetClass::network_send() DEBUG:uip_packet: ")); + LogObject.uart_send_dec(uip_packet); + LogObject.uart_send_str(F(", hdrlen: ")); + LogObject.uart_send_decln(uip_hdrlen); +#endif + Enc28J60Network::writePacket(uip_packet, UIP_SENDBUFFER_OFFSET,uip_buf,uip_hdrlen); + packetstate &= ~ UIPETHERNET_SENDPACKET; + goto sendandfree; + } + uip_packet = Enc28J60Network::allocBlock(uip_len + UIP_SENDBUFFER_OFFSET + UIP_SENDBUFFER_PADDING); + if (uip_packet != NOBLOCK) + { +#if ACTLOGLEVEL>=LOG_DEBUG + LogObject.uart_send_str(F("UIPEthernetClass::network_send() DEBUG:uip_buf (uip_len): ")); + LogObject.uart_send_dec(uip_len); + LogObject.uart_send_str(F(", packet: ")); + LogObject.uart_send_decln(uip_packet); +#endif + Enc28J60Network::writePacket(uip_packet, UIP_SENDBUFFER_OFFSET,uip_buf,uip_len); + goto sendandfree; + } + return false; +sendandfree: + bool success = Enc28J60Network::sendPacket(uip_packet); + Enc28J60Network::freeBlock(uip_packet); + uip_packet = NOBLOCK; + return success; +} + +void UIPEthernetClass::netInit(const uint8_t* mac) { + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("UIPEthernetClass::netInit(const uint8_t* mac) DEBUG_V3:Function started")); + #endif + periodic_timer = millis() + UIP_PERIODIC_TIMER; + + Enc28J60Network::init((uint8_t*)mac); + uip_seteth_addr(mac); + + uip_init(); + uip_arp_init(); +} + +void UIPEthernetClass::configure(IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet) { + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("UIPEthernetClass::configure(IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet) DEBUG_V3:Function started")); + #endif + uip_ipaddr_t ipaddr; + + uip_ip_addr(ipaddr, ip); + uip_sethostaddr(ipaddr); + + uip_ip_addr(ipaddr, gateway); + uip_setdraddr(ipaddr); + + uip_ip_addr(ipaddr, subnet); + uip_setnetmask(ipaddr); + + _dnsServerAddress = dns; +} + +UIPEthernetClass UIPEthernet; + +/*---------------------------------------------------------------------------*/ +uint16_t +UIPEthernetClass::chksum(uint16_t sum, const uint8_t *data, uint16_t len) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("UIPEthernetClass::chksum(uint16_t sum, const uint8_t *data, uint16_t len) DEBUG_V3:Function started")); + #endif + uint16_t t; + const uint8_t *dataptr; + const uint8_t *last_byte; + + dataptr = data; + last_byte = data + len - 1; + + while(dataptr < last_byte) { /* At least two more bytes */ + t = (dataptr[0] << 8) + dataptr[1]; + sum += t; + if(sum < t) { + sum++; /* carry */ + } + dataptr += 2; + } + + if(dataptr == last_byte) { + t = (dataptr[0] << 8) + 0; + sum += t; + if(sum < t) { + sum++; /* carry */ + } + } + + /* Return sum in host byte order. */ + return sum; +} + +/*---------------------------------------------------------------------------*/ + +uint16_t +UIPEthernetClass::ipchksum(void) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("UIPEthernetClass::ipchksum(void) DEBUG_V3:Function started")); + #endif + uint16_t sum; + + sum = chksum(0, &uip_buf[UIP_LLH_LEN], UIP_IPH_LEN); + return (sum == 0) ? 0xffff : htons(sum); +} + +/*---------------------------------------------------------------------------*/ +uint16_t +#if UIP_UDP +UIPEthernetClass::upper_layer_chksum(uint8_t proto) +#else +uip_tcpchksum(void) +#endif +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + #if UIP_UDP + LogObject.uart_send_strln(F("UIPEthernetClass::upper_layer_chksum(uint8_t proto) DEBUG_V3:Function started")); + #else + LogObject.uart_send_strln(F("uip_tcpchksum(void) INFO:Function started")); + #endif + #endif + uint16_t upper_layer_len; + uint16_t sum; + +#if UIP_CONF_IPV6 + upper_layer_len = (((u16_t)(BUF->len[0]) << 8) + BUF->len[1]); +#else /* UIP_CONF_IPV6 */ + upper_layer_len = (((u16_t)(BUF->len[0]) << 8) + BUF->len[1]) - UIP_IPH_LEN; +#endif /* UIP_CONF_IPV6 */ + + /* First sum pseudoheader. */ + + /* IP protocol and length fields. This addition cannot carry. */ +#if UIP_UDP + sum = upper_layer_len + proto; +#else + sum = upper_layer_len + UIP_PROTO_TCP; +#endif + /* Sum IP source and destination addresses. */ + sum = UIPEthernetClass::chksum(sum, (u8_t *)&BUF->srcipaddr[0], 2 * sizeof(uip_ipaddr_t)); + + uint8_t upper_layer_memlen; +#if UIP_UDP + switch(proto) + { +// case UIP_PROTO_ICMP: +// case UIP_PROTO_ICMP6: +// upper_layer_memlen = upper_layer_len; +// break; + case UIP_PROTO_UDP: + upper_layer_memlen = UIP_UDPH_LEN; + break; + default: +// case UIP_PROTO_TCP: +#endif + upper_layer_memlen = (BUF->tcpoffset >> 4) << 2; +#if UIP_UDP + break; + } +#endif + sum = UIPEthernetClass::chksum(sum, &uip_buf[UIP_IPH_LEN + UIP_LLH_LEN], upper_layer_memlen); +#if ACTLOGLEVEL>=LOG_DEBUG + #if UIP_UDP + LogObject.uart_send_str(F("UIPEthernetClass::upper_layer_chksum(uint8_t proto) DEBUG:uip_buf[")); + #else + LogObject.uart_send_str(F("uip_tcpchksum(void) DEBUG:uip_buf[")); + #endif + LogObject.uart_send_dec(UIP_IPH_LEN + UIP_LLH_LEN); + LogObject.uart_send_str(F("-")); + LogObject.uart_send_dec(UIP_IPH_LEN + UIP_LLH_LEN + upper_layer_memlen); + LogObject.uart_send_str(F("]: ")); + LogObject.uart_send_hexln(htons(sum)); +#endif + if (upper_layer_memlen < upper_layer_len) + { + sum = Enc28J60Network::chksum( + sum, + UIPEthernetClass::uip_packet, + (UIPEthernetClass::packetstate & UIPETHERNET_SENDPACKET ? UIP_IPH_LEN + UIP_LLH_LEN + UIP_SENDBUFFER_OFFSET : UIP_IPH_LEN + UIP_LLH_LEN) + upper_layer_memlen, + upper_layer_len - upper_layer_memlen + ); +#if ACTLOGLEVEL>=LOG_DEBUG + #if UIP_UDP + LogObject.uart_send_str(F("UIPEthernetClass::upper_layer_chksum(uint8_t proto) DEBUG:uip_packet(")); + #else + LogObject.uart_send_str(F("uip_tcpchksum(void) DEBUG:uip_packet(")); + #endif + LogObject.uart_send_dec(UIPEthernetClass::uip_packet); + LogObject.uart_send_str(F(")[")); + LogObject.uart_send_dec(UIP_IPH_LEN + UIP_LLH_LEN + upper_layer_memlen); + LogObject.uart_send_str(F("-")); + LogObject.uart_send_dec(UIP_IPH_LEN + UIP_LLH_LEN + upper_layer_len); + LogObject.uart_send_str(F("]: ")); + LogObject.uart_send_hexln(htons(sum)); +#endif + } + return (sum == 0) ? 0xffff : htons(sum); +} + +uint16_t +uip_ipchksum(void) +{ + return UIPEthernet.ipchksum(); +} + +#if UIP_UDP +uint16_t +uip_tcpchksum(void) +{ + uint16_t sum = UIPEthernet.upper_layer_chksum(UIP_PROTO_TCP); + return sum; +} + +uint16_t +uip_udpchksum(void) +{ + uint16_t sum = UIPEthernet.upper_layer_chksum(UIP_PROTO_UDP); + return sum; +} +#endif diff --git a/LibraryPatches/UIPEthernet-2.0.9/UIPEthernet.h b/LibraryPatches/UIPEthernet-2.0.9/UIPEthernet.h new file mode 100644 index 0000000..4d3e0eb --- /dev/null +++ b/LibraryPatches/UIPEthernet-2.0.9/UIPEthernet.h @@ -0,0 +1,166 @@ +/* + UIPEthernet.h - Arduino implementation of a uIP wrapper class. + Copyright (c) 2013 Norbert Truchsess + All rights reserved. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#ifndef UIPETHERNET_H +#define UIPETHERNET_H + +#include "ethernet_comp.h" +#if defined(__MBED__) + #include +#endif +#if defined(ARDUINO) + #include + #if defined(__STM32F3__) || (!defined(ARDUINO_ARCH_STM32) && defined(STM32F3)) || defined(__RFduino__) + #include "mbed/IPAddress.h" + #else + #include "IPAddress.h" + #endif +#endif +#include "utility/Enc28J60Network.h" +#include "utility/uipopt.h" +#include "Dhcp.h" +#if UIP_UDP + #include "UIPUdp.h" +#endif +#include "UIPClient.h" +#include "UIPServer.h" + +extern "C" +{ +#include "utility/uip.h" +} + +#define UIPETHERNET_FREEPACKET 1 +#define UIPETHERNET_SENDPACKET 2 +#define UIPETHERNET_BUFFERREAD 4 + +#define uip_ip_addr(addr, ip) do { \ + ((u16_t *)(addr))[0] = HTONS(((ip[0]) << 8) | (ip[1])); \ + ((u16_t *)(addr))[1] = HTONS(((ip[2]) << 8) | (ip[3])); \ + } while(0) + +#define ip_addr_uip(a) IPAddress(a[0] & 0xFF, a[0] >> 8 , a[1] & 0xFF, a[1] >> 8) //TODO this is not IPV6 capable + +#define uip_seteth_addr(eaddr) do {uip_ethaddr.addr[0] = eaddr[0]; \ + uip_ethaddr.addr[1] = eaddr[1];\ + uip_ethaddr.addr[2] = eaddr[2];\ + uip_ethaddr.addr[3] = eaddr[3];\ + uip_ethaddr.addr[4] = eaddr[4];\ + uip_ethaddr.addr[5] = eaddr[5];} while(0) + +#define BUF ((struct uip_tcpip_hdr *)&uip_buf[UIP_LLH_LEN]) + +enum EthernetLinkStatus { + Unknown, + LinkON, + LinkOFF +}; + +enum EthernetHardwareStatus { + EthernetNoHardware, + EthernetW5100, + EthernetW5200, + EthernetW5500, + EthernetENC28J60 = 10 +}; + +class UIPEthernetClass +{ +public: + UIPEthernetClass(); + + void init(const uint8_t pin); + + int begin(const uint8_t* mac); + void begin(const uint8_t* mac, IPAddress ip); + void begin(const uint8_t* mac, IPAddress ip, IPAddress dns); + void begin(const uint8_t* mac, IPAddress ip, IPAddress dns, IPAddress gateway); + void begin(const uint8_t* mac, IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet); + + // maintain() must be called at regular intervals to process the incoming serial + // data and issue IP events to the sketch. It does not return until all IP + // events have been processed. Renews dhcp-lease if required. + int maintain(); + + EthernetLinkStatus linkStatus(); + EthernetHardwareStatus hardwareStatus(); + + // KH add to have similar function to Ethernet lib + // Certainly we can use void macAddress(uint8_t mac[]) to read from W5x00. + void MACAddress(uint8_t *mac_address) + { + memcpy(mac_address, _mac_address, sizeof(_mac_address)); + } + ////// + + IPAddress localIP(); + IPAddress subnetMask(); + IPAddress gatewayIP(); + IPAddress dnsServerIP(); + +private: + + // KH add to work with new func void MACAddress(uint8_t *mac_address); and SinricPro v2.5.1+ + uint8_t _mac_address[6] ={0,}; + ////// + + static memhandle in_packet; + static memhandle uip_packet; + static uint8_t uip_hdrlen; + static uint8_t packetstate; + + static IPAddress _dnsServerAddress; + #if UIP_UDP + static DhcpClass* _dhcp; + #endif + static unsigned long periodic_timer; + + static void netInit(const uint8_t* mac); + static void configure(IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet); + + static void tick(); + + static bool network_send(); + + friend class UIPServer; + + friend class UIPClient; + + friend class UIPUDP; + + static uint16_t chksum(uint16_t sum, const uint8_t* data, uint16_t len); + static uint16_t ipchksum(void); +#if UIP_UDP + static uint16_t upper_layer_chksum(uint8_t proto); +#endif + friend uint16_t uip_ipchksum(void); + friend uint16_t uip_tcpchksum(void); + friend uint16_t uip_udpchksum(void); + + friend void uipclient_appcall(void); + friend void uipudp_appcall(void); + +#if UIP_CONF_IPV6 + uint16_t uip_icmp6chksum(void); +#endif /* UIP_CONF_IPV6 */ +}; + +extern UIPEthernetClass UIPEthernet; + +#endif diff --git a/LibraryPatches/UIPEthernet-2.0.9/utility/Enc28J60Network.cpp b/LibraryPatches/UIPEthernet-2.0.9/utility/Enc28J60Network.cpp new file mode 100644 index 0000000..8c405d6 --- /dev/null +++ b/LibraryPatches/UIPEthernet-2.0.9/utility/Enc28J60Network.cpp @@ -0,0 +1,1216 @@ +/* + Enc28J60NetworkClass.h + UIPEthernet network driver for Microchip ENC28J60 Ethernet Interface. + + Copyright (c) 2013 Norbert Truchsess + All rights reserved. + + based on enc28j60.c file from the AVRlib library by Pascal Stang. + For AVRlib See http://www.procyonengineering.com/ + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include "Enc28J60Network.h" +#if defined(ARDUINO) + #include "Arduino.h" +#endif +#if defined(__MBED__) + #include + #include "mbed/millis.h" + #define delay(x) wait_ms(x) +#endif +#include "logging.h" + +// KH, For nRF52 +//#define ENC28J60_USE_SPILIB true + +uint8_t ENC28J60ControlCS = ENC28J60_CONTROL_CS; + +#if ENC28J60_USE_SPILIB + #if defined(ARDUINO) + #if defined(STM32F2) + #include + #elif (defined(ARDUINO_ARCH_STM32) || !defined(STM32F3)) && !defined(__STM32F4__) + #include + extern SPIClass SPI; + //#elif defined(ARDUINO_ARCH_AMEBA) + //SPIClass SPI((void *)(&spi_obj), 11, 12, 13, 10); + //SPI _spi(SPI_MOSI,SPI_MISO,SPI_SCK,ENC28J60ControlCS); + #else + #include "HardwareSPI.h" + extern HardwareSPI SPI(1); + #endif + #endif + #if defined(__MBED__) + SPI _spi(SPI_MOSI,SPI_MISO,SPI_SCK); + DigitalOut _cs(ENC28J60ControlCS); + Serial LogObject(SERIAL_TX,SERIAL_RX); + #endif +#endif + +extern "C" { + #if defined(ARDUINO_ARCH_AVR) + // AVR-specific code + #include + #elif defined(ARDUINO_ARCH_SAM) + // SAM-specific code + #elif defined(ARDUINO_ARCH_SAMD) + // SAMD-specific code + #else + // generic, non-platform specific code + #endif +#include "enc28j60.h" +#include "uip.h" +} + +#if defined(ARDUINO) + // set CS to 0 = active + #define CSACTIVE digitalWrite(ENC28J60ControlCS, LOW) + // set CS to 1 = passive + #define CSPASSIVE digitalWrite(ENC28J60ControlCS, HIGH) +#endif +#if defined(__MBED__) + // set CS to 0 = active + #define CSACTIVE _cs=0 + // set CS to 1 = passive + #define CSPASSIVE _cs=1 +#endif + +// +#if defined(ARDUINO_ARCH_AVR) +#define waitspi() while(!(SPSR&(1<=LOG_DEBUG + LogObject.uart_send_str(F("ENC28J60::init DEBUG:csPin = ")); + LogObject.uart_send_decln(ENC28J60ControlCS); + LogObject.uart_send_str(F("ENC28J60::init DEBUG:miso = ")); + LogObject.uart_send_decln(SPI_MISO); + LogObject.uart_send_str(F("ENC28J60::init DEBUG:mosi = ")); + LogObject.uart_send_decln(SPI_MOSI); + LogObject.uart_send_str(F("ENC28J60::init DEBUG:sck = ")); + LogObject.uart_send_decln(SPI_SCK); + #endif +#if ENC28J60_USE_SPILIB + #if ACTLOGLEVEL>=LOG_DEBUG + LogObject.uart_send_strln(F("ENC28J60::init DEBUG:Use SPI lib SPI.begin()")); + #endif + #if defined(ARDUINO) + #if defined(__STM32F3__) || (!defined(ARDUINO_ARCH_STM32) && defined(STM32F3)) || defined(__STM32F4__) + SPI.begin(SPI_9MHZ, MSBFIRST, 0); + #else + SPI.begin(); + #endif + #endif + #if defined(ARDUINO_ARCH_AVR) + // AVR-specific code + SPI.setClockDivider(SPI_CLOCK_DIV2); //results in 8MHZ at 16MHZ system clock. + #elif defined(ARDUINO_ARCH_SAM) + // SAM-specific code + SPI.setClockDivider(10); //defaults to 21 which results in aprox. 4MHZ. A 10 should result in a little more than 8MHZ. + #elif defined(ARDUINO_ARCH_SAMD) + // SAMD-specific code + // Should we set clock divider? + SPI.setClockDivider(10); + #elif defined(__STM32F1__) || defined(__STM32F3__) + // generic, non-platform specific code + #define USE_STM32F1_DMAC 1 //on STM32 + // BOARD_NR_SPI >= 1 BOARD_SPI1_NSS_PIN, BOARD_SPI1_SCK_PIN, BOARD_SPI1_MISO_PIN, BOARD_SPI1_MOSI_PIN + SPI.setBitOrder(MSBFIRST); + SPI.setDataMode(SPI_MODE0); + SPI.setClockDivider(SPI_CLOCK_DIV8); //value 8 the result is 9MHz at 72MHz clock. + #else + #if defined(ARDUINO) + #if !defined(__STM32F3__) && !defined(STM32F3) && !defined(__STM32F4__) + SPI.setBitOrder(MSBFIRST); + #endif + //Settings for ESP8266 + //SPI.setDataMode(SPI_MODE0); + //SPI.setClockDivider(SPI_CLOCK_DIV16); + #endif + #if defined(__MBED__) + _spi.format(8, 0); // 8bit, mode 0 + _spi.frequency(7000000); // 7MHz + #endif + #endif +#else + #if ACTLOGLEVEL>=LOG_DEBUG + LogObject.uart_send_strln(F("ENC28J60::init DEBUG:Use Native hardware SPI")); + #endif + pinMode(SPI_MOSI, OUTPUT); + pinMode(SPI_SCK, OUTPUT); + pinMode(SPI_MISO, INPUT); + //Hardware SS must be configured as OUTPUT to enable SPI-master (regardless of which pin is configured as ENC28J60ControlCS) + pinMode(SS, OUTPUT); + digitalWrite(SS,HIGH); + + digitalWrite(SPI_MOSI, LOW); + digitalWrite(SPI_SCK, LOW); + + // initialize SPI interface + // master mode and Fosc/2 clock: + SPCR = (1<=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("ENC28J60::init DEBUG_V3:Before readOp(ENC28J60_READ_CTRL_REG, ESTAT)")); + #endif + nextPacketPtr = RXSTART_INIT; + while ((!readOp(ENC28J60_READ_CTRL_REG, ESTAT) & ESTAT_CLKRDY) && (timeout>0)) + { + timeout=timeout-1; + delay(10); + #if defined(ESP8266) + wdt_reset(); + #endif + } + #if ACTLOGLEVEL>=LOG_ERR + if (timeout==0) {LogObject.uart_send_strln(F("ENC28J60::init ERROR:TIMEOUT !!"));} + #endif + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("ENC28J60::init DEBUG_V3:After readOp(ENC28J60_READ_CTRL_REG, ESTAT)")); + #endif + // Rx start + writeRegPair(ERXSTL, RXSTART_INIT); + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("ENC28J60::init DEBUG_V3:After writeRegPair(ERXSTL, RXSTART_INIT)")); + #endif + // set receive pointer address + writeRegPair(ERXRDPTL, RXSTART_INIT); + // RX end + writeRegPair(ERXNDL, RXSTOP_INIT); + // TX start + //writeRegPair(ETXSTL, TXSTART_INIT); + // TX end + //writeRegPair(ETXNDL, TXSTOP_INIT); + // do bank 1 stuff, packet filter: + // For broadcast packets we allow only ARP packtets + // All other packets should be unicast only for our mac (MAADR) + // + // The pattern to match on is therefore + // Type ETH.DST + // ARP BROADCAST + // 06 08 -- ff ff ff ff ff ff -> ip checksum for theses bytes=f7f9 + // in binary these poitions are:11 0000 0011 1111 + // This is hex 303F->EPMM0=0x3f,EPMM1=0x30 + //TODO define specific pattern to receive dhcp-broadcast packages instead of setting ERFCON_BCEN! +// enableBroadcast(); // change to add ERXFCON_BCEN recommended by epam + writeReg(ERXFCON, ERXFCON_UCEN|ERXFCON_CRCEN|ERXFCON_PMEN|ERXFCON_BCEN); + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("ENC28J60::init DEBUG_V3:After writeReg(ERXFCON, ERXFCON_UCEN|ERXFCON_CRCEN|ERXFCON_PMEN|ERXFCON_BCEN)")); + #endif + #if defined(ESP8266) + wdt_reset(); + #endif + writeRegPair(EPMM0, 0x303f); + writeRegPair(EPMCSL, 0xf7f9); + // + // + // do bank 2 stuff + // enable MAC receive + // and bring MAC out of reset (writes 0x00 to MACON2) + writeRegPair(MACON1, MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS); + // enable automatic padding to 60bytes and CRC operations + writeOp(ENC28J60_BIT_FIELD_SET, MACON3, MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN); + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("ENC28J60::init DEBUG_V3:After writeOp(ENC28J60_BIT_FIELD_SET, MACON3, MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN)")); + #endif + // set inter-frame gap (non-back-to-back) + writeRegPair(MAIPGL, 0x0C12); + // set inter-frame gap (back-to-back) + writeReg(MABBIPG, 0x12); + // Set the maximum packet size which the controller will accept + // Do not send packets longer than MAX_FRAMELEN: + writeRegPair(MAMXFLL, MAX_FRAMELEN); + // do bank 3 stuff + // write MAC address + // NOTE: MAC address in ENC28J60 is byte-backward + writeReg(MAADR5, macaddr[0]); + writeReg(MAADR4, macaddr[1]); + writeReg(MAADR3, macaddr[2]); + writeReg(MAADR2, macaddr[3]); + writeReg(MAADR1, macaddr[4]); + writeReg(MAADR0, macaddr[5]); + // no loopback of transmitted frames + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("ENC28J60::init DEBUG_V3:Before phyWrite(PHCON2, PHCON2_HDLDIS)")); + #endif + phyWrite(PHCON2, PHCON2_HDLDIS); + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("ENC28J60::init DEBUG_V3:After phyWrite(PHCON2, PHCON2_HDLDIS)")); + #endif + // switch to bank 0 + setBank(ECON1); + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("ENC28J60::init DEBUG_V3:After setBank(ECON1)")); + #endif + // enable interrutps + writeOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE|EIE_PKTIE); + // enable packet reception + writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN); + //Configure leds + phyWrite(PHLCON,0x476); + + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("ENC28J60::init DEBUG_V3:Before readReg(EREVID);")); + #endif + erevid=readReg(EREVID); + if (erevid==0xFF) {erevid=0;} + // microchip forgot to step the number on the silcon when they + // released the revision B7. 6 is now rev B7. We still have + // to see what they do when they release B8. At the moment + // there is no B8 out yet + //if (erevid > 5) ++erevid; + #if ACTLOGLEVEL>=LOG_INFO + LogObject.uart_send_str(F("ENC28J60::init INFO: Chip erevid=")); + LogObject.uart_send_dec(erevid); + LogObject.uart_send_strln(F(" initialization completed.")); + #endif + +// return Enc28J60Network::erevid; +} + +memhandle +Enc28J60Network::receivePacket(void) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::receivePacket(void) DEBUG_V3:Function started")); + #endif + #if defined(ESP8266) + wdt_reset(); + #endif + uint8_t rxstat; + uint16_t len; + // check if a packet has been received and buffered + //if( !(readReg(EIR) & EIR_PKTIF) ){ + // The above does not work. See Rev. B4 Silicon Errata point 6. + #if ACTLOGLEVEL>=LOG_ERR + if (erevid==0) + { + LogObject.uart_send_strln(F("Enc28J60Network::receivePacket(void) ERROR:ENC28j50 Device not found !! Bypass receivePacket function !!")); + } + #endif + uint8_t epktcnt=readReg(EPKTCNT); + if ((erevid!=0) && (epktcnt!=0)) + { + uint16_t readPtr = nextPacketPtr+6 > RXSTOP_INIT ? nextPacketPtr+6-((RXSTOP_INIT + 1)-RXSTART_INIT) : nextPacketPtr+6; + // Set the read pointer to the start of the received packet + writeRegPair(ERDPTL, nextPacketPtr); + // read the next packet pointer + nextPacketPtr = readOp(ENC28J60_READ_BUF_MEM, 0); + nextPacketPtr |= readOp(ENC28J60_READ_BUF_MEM, 0) << 8; + // read the packet length (see datasheet page 43) + len = readOp(ENC28J60_READ_BUF_MEM, 0); + len |= readOp(ENC28J60_READ_BUF_MEM, 0) << 8; + len -= 4; //remove the CRC count + // read the receive status (see datasheet page 43) + rxstat = readOp(ENC28J60_READ_BUF_MEM, 0); + //rxstat |= readOp(ENC28J60_READ_BUF_MEM, 0) << 8; + #if ACTLOGLEVEL>=LOG_DEBUG + LogObject.uart_send_str(F("Enc28J60Network::receivePacket(void) DEBUG:receivePacket [")); + LogObject.uart_send_hex(readPtr); + LogObject.uart_send_str(F("-")); + LogObject.uart_send_hex((readPtr+len) % (RXSTOP_INIT+1)); + LogObject.uart_send_str(F("], next: ")); + LogObject.uart_send_hex(nextPacketPtr); + LogObject.uart_send_str(F(", stat: ")); + LogObject.uart_send_hex(rxstat); + LogObject.uart_send_str(F(", Packet count: ")); + LogObject.uart_send_dec(epktcnt); + LogObject.uart_send_str(F(" -> ")); + LogObject.uart_send_strln((rxstat & 0x80)!=0 ? "OK" : "failed"); + #endif + // decrement the packet counter indicate we are done with this packet + writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC); + // check CRC and symbol errors (see datasheet page 44, table 7-3): + // The ERXFCON.CRCEN is set by default. Normally we should not + // need to check this. + if (((rxstat & 0x80) != 0) && (nextPacketPtr<=RXSTOP_INIT)) + { + receivePkt.begin = readPtr; + receivePkt.size = len; + #if ACTLOGLEVEL>=LOG_DEBUG + LogObject.uart_send_str(F("Enc28J60Network::receivePacket(void) DEBUG: rxstat OK. receivePkt.size=")); + LogObject.uart_send_decln(len); + #endif + return UIP_RECEIVEBUFFERHANDLE; + } + // Move the RX read pointer to the start of the next received packet + // This frees the memory we just read out + setERXRDPT(); + } + return (NOBLOCK); +} + +void +Enc28J60Network::setERXRDPT(void) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::setERXRDPT(void) DEBUG_V3:Function started")); + #endif + // Make sure the value is odd. See Rev. B1,B4,B5,B7 Silicon Errata issues 14 + uint16_t actnextPacketPtr = nextPacketPtr == RXSTART_INIT ? RXSTOP_INIT : nextPacketPtr-1; + #if ACTLOGLEVEL>=LOG_DEBUG + LogObject.uart_send_str(F("Enc28J60Network::setERXRDPT(void) DEBUG:Set actnextPacketPtr:")); + LogObject.uart_send_hexln(actnextPacketPtr); + #endif + // datasheet: The ENC28J60 will always write up to, but not including + writeRegPair(ERXRDPTL, actnextPacketPtr); +} + +memaddress +Enc28J60Network::blockSize(memhandle handle) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::blockSize(memhandle handle) DEBUG_V3:Function started")); + #endif + return ((handle == NOBLOCK) || (erevid==0)) ? 0 : handle == UIP_RECEIVEBUFFERHANDLE ? receivePkt.size : blocks[handle].size; +} + +bool +Enc28J60Network::sendPacket(memhandle handle) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::sendPacket(memhandle handle) INFO:Function started")); + #endif + #if defined(ESP8266) + wdt_reset(); + #endif + if (erevid==0) + { + #if ACTLOGLEVEL>=LOG_ERR + LogObject.uart_send_strln(F("Enc28J60Network::sendPacket(memhandle handle) ERROR:ENC28j50 Device not found !! Bypass sendPacket function !!")); + #endif + return false; + } + + memblock *packet = &blocks[handle]; + uint16_t start = packet->begin; // includes the UIP_SENDBUFFER_OFFSET for control byte + uint16_t end = start + packet->size - 1 - UIP_SENDBUFFER_PADDING; // end = start + size - 1 and padding for TSV is no included + + // write control-byte (if not 0 anyway) + writeByte(start, 0); + + #if ACTLOGLEVEL>=LOG_DEBUG + LogObject.uart_send_str(F("Enc28J60Network::sendPacket(memhandle handle) DEBUG:sendPacket(")); + LogObject.uart_send_dec(handle); + LogObject.uart_send_str(F(") [")); + LogObject.uart_send_hex(start); + LogObject.uart_send_str(F("-")); + LogObject.uart_send_hex(end); + LogObject.uart_send_str(F("]: ")); + for (uint16_t i=start; i<=end; i++) + { + LogObject.uart_send_hex(readByte(i)); + LogObject.uart_send_str(F(" ")); + } + LogObject.uart_send_strln(F("")); + #endif + + // TX start + writeRegPair(ETXSTL, start); + // Set the TXND pointer to correspond to the packet size given + writeRegPair(ETXNDL, end); + + bool success = false; + // See Rev. B7 Silicon Errata issues 12 and 13 + for (uint8_t retry = 0; retry < TX_COLLISION_RETRY_COUNT; retry++) + { + // Reset the transmit logic problem. Errata 12 + writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRST); + writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRST); + writeOp(ENC28J60_BIT_FIELD_CLR, EIR, EIR_TXERIF | EIR_TXIF); + + // send the contents of the transmit buffer onto the network + writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS); + + uint8_t eir; + // wait for transmission to complete or fail + while (((eir = readReg(EIR)) & (EIR_TXIF | EIR_TXERIF)) == 0); + writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRTS); + success = ((eir & EIR_TXERIF) == 0); + if (success) + break; // usual exit of the for(retry) loop + + // Errata 13 detection + uint8_t tsv4 = readByte(end + 4); + if (!(tsv4 & 0b00100000)) // is it "late collision" indicated in bit 29 of TSV? + break; // other fail, not the Errata 13 situation + #if ACTLOGLEVEL>=LOG_ERROR + LogObject.uart_send_strln(F("Enc28J60Network::sendPacket(memhandle handle) Errata 13 LATE COLLISION !!")); + #endif + } + + return success; +} + +uint16_t +Enc28J60Network::setReadPtr(memhandle handle, memaddress position, uint16_t len) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::setReadPtr(memhandle handle, memaddress position, uint16_t len) DEBUG_V3:Function started")); + #endif + memblock *packet = handle == UIP_RECEIVEBUFFERHANDLE ? &receivePkt : &blocks[handle]; + memaddress start = handle == UIP_RECEIVEBUFFERHANDLE && packet->begin + position > RXSTOP_INIT ? packet->begin + position-((RXSTOP_INIT + 1)-RXSTART_INIT) : packet->begin + position; + + writeRegPair(ERDPTL, start); + + if (len > packet->size - position) + len = packet->size - position; + return len; +} + +uint16_t +Enc28J60Network::readPacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::readPacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len) DEBUG_V3:Function started")); + #endif + #if defined(ESP8266) + wdt_reset(); + #endif + len = setReadPtr(handle, position, len); + readBuffer(len, buffer); + #if ACTLOGLEVEL>=LOG_DEBUG_V2 + LogObject.uart_send_str(F("Enc28J60Network::readPacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len) DEBUG_V2: Read bytes:")); + LogObject.uart_send_dec(len); + LogObject.uart_send_str(F(" save to block(")); + LogObject.uart_send_dec(handle); + LogObject.uart_send_str(F(") [")); + LogObject.uart_send_hex(position); + LogObject.uart_send_str(F("]: ")); + for (uint16_t i=0; i=LOG_DEBUG_V3 + LogObject.uart_send_str(F("Enc28J60Network::writePacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len) DEBUG_V3:Function started with len:")); + LogObject.uart_send_decln(len); + #endif + #if defined(ESP8266) + wdt_reset(); + #endif + memblock *packet = &blocks[handle]; + uint16_t start = packet->begin + position; + + writeRegPair(EWRPTL, start); + + if (len > packet->size - position) + len = packet->size - position; + writeBuffer(len, buffer); + #if ACTLOGLEVEL>=LOG_DEBUG_V2 + LogObject.uart_send_str(F("Enc28J60Network::writePacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len) DEBUG_V2: Write bytes:")); + LogObject.uart_send_dec(len); + LogObject.uart_send_str(F(" save to block(")); + LogObject.uart_send_dec(handle); + LogObject.uart_send_str(F(") [")); + LogObject.uart_send_hex(start); + LogObject.uart_send_str(F("]: ")); + for (uint16_t i=0; i=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::enableBroadcast (bool temporary) DEBUG_V3:Function started")); + #endif + writeRegByte(ERXFCON, readRegByte(ERXFCON) | ERXFCON_BCEN); + if(!temporary) + broadcast_enabled = true; +} + +void Enc28J60Network::disableBroadcast (bool temporary) { + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::disableBroadcast (bool temporary) DEBUG_V3:Function started")); + #endif + if(!temporary) + broadcast_enabled = false; + if(!broadcast_enabled) + writeRegByte(ERXFCON, readRegByte(ERXFCON) & ~ERXFCON_BCEN); +} + +void Enc28J60Network::enableMulticast (void) { + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::enableMulticast (void) DEBUG_V3:Function started")); + #endif + writeRegByte(ERXFCON, readRegByte(ERXFCON) | ERXFCON_MCEN); +} + +void Enc28J60Network::disableMulticast (void) { + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::disableMulticast (void) DEBUG_V3:Function started")); + #endif + writeRegByte(ERXFCON, readRegByte(ERXFCON) & ~ERXFCON_MCEN); +} + +uint8_t Enc28J60Network::readRegByte (uint8_t address) { + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::readRegByte (uint8_t address) DEBUG_V3:Function started")); + #endif + setBank(address); + return readOp(ENC28J60_READ_CTRL_REG, address); +} + +void Enc28J60Network::writeRegByte (uint8_t address, uint8_t data) { + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::writeRegByte (uint8_t address, uint8_t data) DEBUG_V3:Function started")); + #endif + setBank(address); + writeOp(ENC28J60_WRITE_CTRL_REG, address, data); +} + + +uint8_t Enc28J60Network::readByte(uint16_t addr) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::readByte(uint16_t addr) DEBUG_V3:Function started")); + #endif + #if defined(ESP8266) + wdt_reset(); + #endif + writeRegPair(ERDPTL, addr); + + CSACTIVE; + #if ENC28J60_USE_SPILIB + // issue read command + #if defined(ARDUINO) + SPI.transfer(ENC28J60_READ_BUF_MEM); + // read data + uint8_t c = SPI.transfer(0x00); + #endif + #if defined(__MBED__) + _spi.write(ENC28J60_READ_BUF_MEM); + // read data + uint8_t c = _spi.write(0x00); + #endif + CSPASSIVE; + return (c); + #else + // issue read command + SPDR = ENC28J60_READ_BUF_MEM; + waitspi(); + // read data + SPDR = 0x00; + waitspi(); + CSPASSIVE; + return (SPDR); + #endif +} + +void Enc28J60Network::writeByte(uint16_t addr, uint8_t data) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::writeByte(uint16_t addr, uint8_t data) DEBUG_V3:Function started")); + #endif + #if defined(ESP8266) + wdt_reset(); + #endif + writeRegPair(EWRPTL, addr); + + CSACTIVE; + #if ENC28J60_USE_SPILIB + // issue write command + #if defined(ARDUINO) + SPI.transfer(ENC28J60_WRITE_BUF_MEM); + // write data + SPI.transfer(data); + #endif + #if defined(__MBED__) + _spi.write(ENC28J60_WRITE_BUF_MEM); + // write data + _spi.write(data); + #endif + #else + // issue write command + SPDR = ENC28J60_WRITE_BUF_MEM; + waitspi(); + // write data + SPDR = data; + waitspi(); + #endif + CSPASSIVE; +} + +void +Enc28J60Network::copyPacket(memhandle dest_pkt, memaddress dest_pos, memhandle src_pkt, memaddress src_pos, uint16_t len) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::copyPacket(memhandle dest_pkt, memaddress dest_pos, memhandle src_pkt, memaddress src_pos, uint16_t len) DEBUG_V3:Function started")); + #endif + memblock *dest = &blocks[dest_pkt]; + memblock *src = src_pkt == UIP_RECEIVEBUFFERHANDLE ? &receivePkt : &blocks[src_pkt]; + memaddress start = src_pkt == UIP_RECEIVEBUFFERHANDLE && src->begin + src_pos > RXSTOP_INIT ? src->begin + src_pos-((RXSTOP_INIT + 1)-RXSTART_INIT) : src->begin + src_pos; + enc28J60_mempool_block_move_callback(dest->begin+dest_pos,start,len); + // setERXRDPT(); let it to freePacket after all packets are saved +} + +void +enc28J60_mempool_block_move_callback(memaddress dest, memaddress src, memaddress len) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("enc28J60_mempool_block_move_callback(memaddress dest, memaddress src, memaddress len) DEBUG_V3:Function started")); + #endif +//void +//Enc28J60Network::memblock_mv_cb(uint16_t dest, uint16_t src, uint16_t len) +//{ + //as ENC28J60 DMA is unable to copy single bytes: + if (len == 1) + { + Enc28J60Network::writeByte(dest,Enc28J60Network::readByte(src)); + } + else + { + // calculate address of last byte + len += src - 1; + + /* 1. Appropriately program the EDMAST, EDMAND + and EDMADST register pairs. The EDMAST + registers should point to the first byte to copy + from, the EDMAND registers should point to the + last byte to copy and the EDMADST registers + should point to the first byte in the destination + range. The destination range will always be + linear, never wrapping at any values except from + 8191 to 0 (the 8-Kbyte memory boundary). + Extreme care should be taken when + programming the start and end pointers to + prevent a never ending DMA operation which + would overwrite the entire 8-Kbyte buffer. + */ + Enc28J60Network::writeRegPair(EDMASTL, src); + Enc28J60Network::writeRegPair(EDMADSTL, dest); + + if ((src <= RXSTOP_INIT)&& (len > RXSTOP_INIT))len -= ((RXSTOP_INIT + 1)-RXSTART_INIT); + Enc28J60Network::writeRegPair(EDMANDL, len); + + /* + 2. If an interrupt at the end of the copy process is + desired, set EIE.DMAIE and EIE.INTIE and + clear EIR.DMAIF. + + 3. Verify that ECON1.CSUMEN is clear. */ + Enc28J60Network::writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_CSUMEN); + + /* 4. Start the DMA copy by setting ECON1.DMAST. */ + Enc28J60Network::writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_DMAST); + + // wait until runnig DMA is completed + while (Enc28J60Network::readOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_DMAST) + { + delay(1); + } + } +} + +void +Enc28J60Network::freePacket(void) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::freePacket(void) DEBUG_V3:Function started")); + #endif + setERXRDPT(); +} + +uint8_t +Enc28J60Network::readOp(uint8_t op, uint8_t address) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::readOp(uint8_t op, uint8_t address) DEBUG_V3:Function started")); + #endif + CSACTIVE; + // issue read command + #if ENC28J60_USE_SPILIB + #if defined(ARDUINO) + SPI.transfer(op | (address & ADDR_MASK)); + // read data + if(address & 0x80) + { + // do dummy read if needed (for mac and mii, see datasheet page 29) + SPI.transfer(0x00); + } + uint8_t c = SPI.transfer(0x00); + #endif + #if defined(__MBED__) + _spi.write(op | (address & ADDR_MASK)); + // read data + if(address & 0x80) + { + // do dummy read if needed (for mac and mii, see datasheet page 29) + _spi.write(0x00); + } + uint8_t c = _spi.write(0x00); + #endif + // release CS + CSPASSIVE; + return(c); + #else + // issue read command + SPDR = op | (address & ADDR_MASK); + waitspi(); + // read data + SPDR = 0x00; + waitspi(); + // do dummy read if needed (for mac and mii, see datasheet page 29) + if(address & 0x80) + { + SPDR = 0x00; + waitspi(); + } + // release CS + CSPASSIVE; + return(SPDR); + #endif + #if defined(ESP8266) + yield(); + #endif +} + +void +Enc28J60Network::writeOp(uint8_t op, uint8_t address, uint8_t data) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::writeOp(uint8_t op, uint8_t address, uint8_t data) DEBUG_V3:Function started")); + #endif + CSACTIVE; + // issue write command + #if ENC28J60_USE_SPILIB + #if defined(ARDUINO) + SPI.transfer(op | (address & ADDR_MASK)); + // write data + SPI.transfer(data); + #endif + #if defined(__MBED__) + _spi.write(op | (address & ADDR_MASK)); + // write data + _spi.write(data); + #endif + #else + // issue write command + SPDR = op | (address & ADDR_MASK); + waitspi(); + // write data + SPDR = data; + waitspi(); + #endif + CSPASSIVE; + #if defined(ESP8266) + yield(); + #endif +} + +void +Enc28J60Network::readBuffer(uint16_t len, uint8_t* data) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::readBuffer(uint16_t len, uint8_t* data) DEBUG_V3:Function started")); + #endif + CSACTIVE; + // issue read command + #if ENC28J60_USE_SPILIB + #if defined(ARDUINO) + SPI.transfer(ENC28J60_READ_BUF_MEM); + #endif + #if defined(__MBED__) + _spi.write(ENC28J60_READ_BUF_MEM); + #endif + #else + SPDR = ENC28J60_READ_BUF_MEM; + waitspi(); + #endif + while(len) + { + len--; + // read data + #if ENC28J60_USE_SPILIB + #if defined(ARDUINO) + *data = SPI.transfer(0x00); + #endif + #if defined(__MBED__) + *data = _spi.write(0x00); + #endif + #else + SPDR = 0x00; + waitspi(); + *data = SPDR; + #endif + data++; + } + //*data='\0'; + CSPASSIVE; +} + +void +Enc28J60Network::writeBuffer(uint16_t len, uint8_t* data) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::writeBuffer(uint16_t len, uint8_t* data) DEBUG_V3:Function started")); + #endif + CSACTIVE; + // issue write command + #if ENC28J60_USE_SPILIB + #if defined(ARDUINO) + SPI.transfer(ENC28J60_WRITE_BUF_MEM); + #endif + #if defined(__MBED__) + _spi.write(ENC28J60_WRITE_BUF_MEM); + #endif + #else + SPDR = ENC28J60_WRITE_BUF_MEM; + waitspi(); + #endif + while(len) + { + len--; + // write data + #if ENC28J60_USE_SPILIB + #if defined(ARDUINO) + SPI.transfer(*data); + #endif + #if defined(__MBED__) + _spi.write(*data); + #endif + data++; + #else + SPDR = *data; + data++; + waitspi(); + #endif + } + CSPASSIVE; +} + +void +Enc28J60Network::setBank(uint8_t address) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::setBank(uint8_t address) DEBUG_V3:Function started")); + #endif + // set the bank (if needed) + if((address & BANK_MASK) != bank) + { + // set the bank + writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, (ECON1_BSEL1|ECON1_BSEL0)); + writeOp(ENC28J60_BIT_FIELD_SET, ECON1, (address & BANK_MASK)>>5); + bank = (address & BANK_MASK); + } +} + +uint8_t +Enc28J60Network::readReg(uint8_t address) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::readReg(uint8_t address) DEBUG_V3:Function started")); + #endif + // set the bank + setBank(address); + // do the read + return readOp(ENC28J60_READ_CTRL_REG, address); +} + +void +Enc28J60Network::writeReg(uint8_t address, uint8_t data) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::writeReg(uint8_t address, uint8_t data) DEBUG_V3:Function started")); + #endif + // set the bank + setBank(address); + // do the write + writeOp(ENC28J60_WRITE_CTRL_REG, address, data); +} + +void +Enc28J60Network::writeRegPair(uint8_t address, uint16_t data) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::writeRegPair(uint8_t address, uint16_t data) DEBUG_V3:Function started")); + #endif + // set the bank + setBank(address); + // do the write + writeOp(ENC28J60_WRITE_CTRL_REG, address, (data&0xFF)); + writeOp(ENC28J60_WRITE_CTRL_REG, address+1, (data) >> 8); +} + +void +Enc28J60Network::phyWrite(uint8_t address, uint16_t data) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::phyWrite(uint8_t address, uint16_t data) DEBUG_V3:Function started")); + #endif + unsigned int timeout = 15; + // set the PHY register address + writeReg(MIREGADR, address); + // write the PHY data + writeRegPair(MIWRL, data); + // wait until the PHY write completes + while (readReg(MISTAT) & MISTAT_BUSY) + { + delay(10); + #if defined(ESP8266) + wdt_reset(); + #endif + if (--timeout == 0) + { + #if ACTLOGLEVEL>=LOG_ERR + LogObject.uart_send_strln(F("Enc28J60Network::phyWrite ERROR:TIMEOUT !!")); + #endif + return; + } + } +} + +uint16_t +Enc28J60Network::phyRead(uint8_t address) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::phyRead(uint8_t address) DEBUG_V3:Function started")); + #endif + unsigned int timeout = 15; + writeReg(MIREGADR,address); + writeReg(MICMD, MICMD_MIIRD); + // wait until the PHY read completes + while(readReg(MISTAT) & MISTAT_BUSY) + { + delay(10); + #if defined(ESP8266) + wdt_reset(); + #endif + if (--timeout == 0) + { + #if ACTLOGLEVEL>=LOG_ERR + LogObject.uart_send_strln(F("Enc28J60Network::phyRead ERROR:TIMEOUT !!")); + #endif + return 0; + } + } + writeReg(MICMD, 0); + return (readReg(MIRDL) | readReg(MIRDH) << 8); +} + +void +Enc28J60Network::clkout(uint8_t clk) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::clkout(uint8_t clk) DEBUG_V3:Function started")); + #endif + //setup clkout: 2 is 12.5MHz: + writeReg(ECOCON, clk & 0x7); +} + +uint16_t +Enc28J60Network::chksum(uint16_t sum, memhandle handle, memaddress pos, uint16_t len) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::chksum(uint16_t sum, memhandle handle, memaddress pos, uint16_t len) DEBUG_V3:Function started")); + #endif + uint16_t t; + len = setReadPtr(handle, pos, len)-1; + CSACTIVE; + // issue read command + #if ENC28J60_USE_SPILIB + #if defined(ARDUINO) + SPI.transfer(ENC28J60_READ_BUF_MEM); + #endif + #if defined(__MBED__) + _spi.write(ENC28J60_READ_BUF_MEM); + #endif + #else + SPDR = ENC28J60_READ_BUF_MEM; + waitspi(); + #endif + uint16_t i; + for (i = 0; i < len; i+=2) + { + // read data + #if ENC28J60_USE_SPILIB + #if defined(ARDUINO) + t = SPI.transfer(0x00) << 8; + t += SPI.transfer(0x00); + #endif + #if defined(__MBED__) + t = _spi.write(0x00) << 8; + t += _spi.write(0x00); + #endif + #else + SPDR = 0x00; + waitspi(); + t = SPDR << 8; + SPDR = 0x00; + waitspi(); + t += SPDR; + #endif + sum += t; + if(sum < t) + { + sum++; /* carry */ + } + } + if(i == len) + { + #if ENC28J60_USE_SPILIB + #if defined(ARDUINO) + t = (SPI.transfer(0x00) << 8) + 0; + #endif + #if defined(__MBED__) + t = (_spi.write(0x00) << 8) + 0; + #endif + #else + SPDR = 0x00; + waitspi(); + t = (SPDR << 8) + 0; + #endif + sum += t; + if(sum < t) + { + sum++; /* carry */ + } + } + CSPASSIVE; + + /* Return sum in host byte order. */ + return sum; +} + +void +Enc28J60Network::powerOff(void) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::powerOff(void) DEBUG_V3:Function started")); + #endif + writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_RXEN); + delay(50); + writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_VRPS); + delay(50); + writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PWRSV); +} + +void +Enc28J60Network::powerOn(void) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::powerOn(void) DEBUG_V3:Function started")); + #endif + writeOp(ENC28J60_BIT_FIELD_CLR, ECON2, ECON2_PWRSV); + delay(50); + writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN); + delay(50); +} + +// read erevid from object: +uint8_t +Enc28J60Network::geterevid(void) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_str(F("Enc28J60Network::geterevid(void) DEBUG_V3:Function started and return:")); + LogObject.uart_send_decln(erevid); + #endif + return(erevid); +} + +// read the phstat2 of the chip: +uint16_t +Enc28J60Network::PhyStatus(void) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_str(F("Enc28J60Network::PhyStatus(void) DEBUG_V3:Function started")); + LogObject.uart_send_decln(erevid); + #endif + uint16_t phstat2; + phstat2=phyRead(PHSTAT2); + if ((phstat2 & 0x20) > 0) {phstat2=phstat2 &0x100;} + phstat2=(phstat2 & 0xFF00) | erevid; + if ((phstat2 & 0x8000) > 0) {phstat2=0;} + return phstat2; +} + +bool +Enc28J60Network::linkStatus(void) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::linkStatus(void) DEBUG_V3:Function started")); + #endif + return (phyRead(PHSTAT2) & 0x0400) > 0; +} diff --git a/LibraryPatches/UIPEthernet-2.0.9/utility/Enc28J60Network.h b/LibraryPatches/UIPEthernet-2.0.9/utility/Enc28J60Network.h new file mode 100644 index 0000000..5174462 --- /dev/null +++ b/LibraryPatches/UIPEthernet-2.0.9/utility/Enc28J60Network.h @@ -0,0 +1,337 @@ +/* + Enc28J60NetworkClass.h + UIPEthernet network driver for Microchip ENC28J60 Ethernet Interface. + + Copyright (c) 2013 Norbert Truchsess + All rights reserved. + + inspired by enc28j60.c file from the AVRlib library by Pascal Stang. + For AVRlib See http://www.procyonengineering.com/ + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#ifndef Enc28J60Network_H_ +#define Enc28J60Network_H_ + +// KH mod +#if defined(ESP32) + //pin SS already defined in ESP32 as pin 5, don't use this as conflict with SPIFFS, EEPROM, etc. + // Use in GPIO13 + #warning ENC28J60Network.h => use ESP32, change ENC28J60_CONTROL_CS/SS_PIN_DEFAULT to GPIO13, MOSI(23), MISO(19), SCK(18) + #define ENC28J60_CONTROL_CS 13 +#endif + +// KH, For nRF52 +#if ( defined(NRF52840_FEATHER) || defined(NRF52832_FEATHER) || defined(NRF52_SERIES) || defined(ARDUINO_NRF52_ADAFRUIT) || \ + defined(NRF52840_FEATHER_SENSE) || defined(NRF52840_ITSYBITSY) || defined(NRF52840_CIRCUITPLAY) || defined(NRF52840_CLUE) || \ + defined(NRF52840_METRO) || defined(NRF52840_PCA10056) || defined(PARTICLE_XENON) || defined(NINA_B302_ublox) || defined(NINA_B112_ublox) ) + #include + #define ENC28J60_USE_SPILIB 1 + + #ifndef USE_THIS_SS_PIN + // default to pin 10 + #define ENC28J60_CONTROL_CS 10 + #else + #warning Using USE_THIS_SS_PIN in Enc28J60Network.h for nRF52 + #define ENC28J60_CONTROL_CS USE_THIS_SS_PIN + #endif +#endif + +// KH, For SAMD21/SAMD51 +#if ( defined(ARDUINO_SAMD_ZERO) || defined(ARDUINO_SAMD_MKR1000) || defined(ARDUINO_SAMD_MKRWIFI1010) \ + || defined(ARDUINO_SAMD_NANO_33_IOT) || defined(ARDUINO_SAMD_MKRFox1200) || defined(ARDUINO_SAMD_MKRWAN1300) || defined(ARDUINO_SAMD_MKRWAN1310) \ + || defined(ARDUINO_SAMD_MKRGSM1400) || defined(ARDUINO_SAMD_MKRNB1500) || defined(ARDUINO_SAMD_MKRVIDOR4000) || defined(__SAMD21G18A__) \ + || defined(ARDUINO_SAMD_CIRCUITPLAYGROUND_EXPRESS) || defined(__SAMD21E18A__) || defined(__SAMD51__) || defined(__SAMD51J20A__) || defined(__SAMD51J19A__) \ + || defined(__SAMD51G19A__) || defined(__SAMD51P19A__) || defined(__SAMD21G18A__) ) + #include + #define ENC28J60_USE_SPILIB 1 + + #ifndef USE_THIS_SS_PIN + // default to pin 10 + #define ENC28J60_CONTROL_CS 10 + #else + #warning Using USE_THIS_SS_PIN in Enc28J60Network.h for SAMD + #define ENC28J60_CONTROL_CS USE_THIS_SS_PIN + #endif +#endif +////// + +#include "mempool.h" +#if defined(__MBED__) + #include + //UIPEthernet(SPI_MOSI, SPI_MISO, SPI_SCK, SPI_CS); + #if defined(TARGET_LPC1768) + #define SPI_MOSI p11 + #define SPI_MISO p12 + #define SPI_SCK p13 + #define SPI_CS p8 + #elif defined(TARGET_LPC1114) + #define SPI_MOSI dp2 + #define SPI_MISO dp1 + #define SPI_SCK dp6 + #define SPI_CS dp25 + #elif defined(TARGET_LPC11U68) + #define SPI_MOSI P0_9 + #define SPI_MISO P0_8 + #define SPI_SCK P1_29 + #define SPI_CS P0_2 + #elif defined(TARGET_NUCLEO_F103RB) || defined(TARGET_NUCLEO_L152RE) || defined(TARGET_NUCLEO_F030R8) \ + || defined(TARGET_NUCLEO_F401RE) || defined(TARGET_NUCLEO_F302R8) || defined(TARGET_NUCLEO_L053R8) \ + || defined(TARGET_NUCLEO_F411RE) || defined(TARGET_NUCLEO_F334R8) || defined(TARGET_NUCLEO_F072RB) \ + || defined(TARGET_NUCLEO_F091RC) || defined(TARGET_NUCLEO_F303RE) || defined(TARGET_NUCLEO_F070RB) + #define SPI_MOSI D4 + #define SPI_MISO D5 + #define SPI_SCK D3 + #define SPI_CS D2 + #endif + #define ENC28J60_CONTROL_CS SPI_CS +#endif + +#if defined(STM32F3) || defined(STM32F2) //This is workaround for stm32duino STM32F2, and adafruit wiced feather STM32F2 + #define BOARD_SPI1_NSS_PIN PA4 + #define BOARD_SPI1_SCK_PIN PA5 + #define BOARD_SPI1_MISO_PIN PA6 + #define BOARD_SPI1_MOSI_PIN PA7 +#endif //This is workaround for stm32duino STM32F3, and adafruit wiced feather STM32F2 + +#if defined(BOARD_discovery_f4) + #define __STM32F4__ +#endif +#if defined(__MK20DX128__) || defined(__MKL26Z64__) + #include +#endif + +#if !defined(ENC28J60_CONTROL_CS) + #if defined(__AVR__) || defined(ESP8266) || defined(__RFduino__) + // Arduino Uno (__AVR__) SS defined to pin 10 + // Arduino Leonardo (ARDUINO_AVR_LEONARDO) SS defined to LED_BUILTIN_RX (17) + // Arduino Mega(__AVR_ATmega2560__) SS defined to pin 53 + // ESP8266 (ESP8266) SS defined to pin 15 + #if defined(ARDUINO_AVR_LEONARDO) || defined(ARDUINO_AVR_MICRO) + #define ENC28J60_CONTROL_CS PIN_A10 + #warning "Using LEONARDO borad PIN_A10 for ENC28J60_CONTROL_CS. Use UIPEthernet::init(uint8_t) to change it." + #else + #define ENC28J60_CONTROL_CS SS + #endif + #elif defined(ARDUINO_ARCH_AMEBA) //Defined SS to pin 10 + #define ENC28J60_CONTROL_CS SS //PC_0 A5 10 + #elif defined(ARDUINO_ARCH_SAM) + // Arduino Due (ARDUINO_ARCH_SAM) BOARD_SPI_DEFAULT_SS (SS3) defined to pin 78 + //#define ENC28J60_CONTROL_CS BOARD_SPI_DEFAULT_SS + #define ENC28J60_CONTROL_CS BOARD_SPI_SS0 + #elif defined(ARDUINO_ARCH_SAMD) + #define ENC28J60_CONTROL_CS SS + #elif defined(__ARDUINO_ARC__) //Intel ARC32 Genuino 101 + #define ENC28J60_CONTROL_CS SS + #elif defined(__RFduino__) //RFduino + #define ENC28J60_CONTROL_CS SS + #elif defined(ARDUINO_ARCH_STM32) // STM32duino core + #define ENC28J60_CONTROL_CS SS + #elif defined(ARDUINO_ARCH_ESP32) // arduino-esp32 + #define ENC28J60_CONTROL_CS SS + #elif defined(STM32_MCU_SERIES) || defined(__STM32F1__) || defined(__STM32F3__) || defined(STM32F3) || defined(__STM32F4__) || defined(STM32F2) + #if defined(BOARD_SPI1_NSS_PIN) + #define ENC28J60_CONTROL_CS BOARD_SPI1_NSS_PIN + #elif defined(ARDUINO_STM32F4_NETDUINO2PLUS) + #define ENC28J60_CONTROL_CS PC8 + #else + #define ENC28J60_CONTROL_CS SPI.nssPin() + //#define ENC28J60_CONTROL_CS PA4 + #endif + #elif defined(__MK20DX128__) || defined(__MKL26Z64__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) || defined(__IMXRT1062__) + #define ENC28J60_CONTROL_CS PIN_SPI_SS + #endif +#endif +#if !defined(ENC28J60_CONTROL_CS) + #warning "Default ENC28J60_CONTROL_CS could not be defined! Use UIPEthernet::init(uint8_t) to set it." + #define ENC28J60_CONTROL_CS 0 +#endif + +extern uint8_t ENC28J60ControlCS; + +#if !defined(SPI_MOSI) + #if defined(__AVR__) || defined(ESP8266) || defined(__RFduino__) + #define SPI_MOSI MOSI + #elif defined(ARDUINO_ARCH_AMEBA) + #define SPI_MOSI 11 //PC_2 + #elif defined(ARDUINO_ARCH_SAM) || defined(ARDUINO_ARCH_SAMD) + #define SPI_MOSI PIN_SPI_MOSI + #elif defined(__ARDUINO_ARC__) //Intel ARC32 Genuino 101 + #define SPI_MOSI MOSI + #elif defined(__RFduino__) //RFduino + #define SPI_MOSI MOSI + #elif defined(ARDUINO_ARCH_STM32) // STM32duino core + #define SPI_MOSI MOSI + #elif defined(ARDUINO_ARCH_ESP32) // arduino-esp32 + #define SPI_MOSI MOSI + #elif defined(STM32_MCU_SERIES) || defined(__STM32F1__) || defined(__STM32F3__) || defined(STM32F3) || defined(__STM32F4__) || defined(STM32F2) + #if defined(BOARD_SPI1_MOSI_PIN) + #define SPI_MOSI BOARD_SPI1_MOSI_PIN + #else + #define SPI_MOSI SPI.mosiPin() + #endif + #elif defined(__MK20DX128__) || defined(__MKL26Z64__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) || defined(__IMXRT1062__) + #define SPI_MOSI PIN_SPI_MOSI + #endif +#endif +#if !defined(SPI_MOSI) + // KH mod + //#error "Not defined SPI_MOSI!" + #define SPI_MOSI MOSI + ////// +#endif + +#if !defined(SPI_MISO) + #if defined(__AVR__) || defined(ESP8266) || defined(__RFduino__) + #define SPI_MISO MISO + #elif defined(ARDUINO_ARCH_AMEBA) + #define SPI_MISO 12 //PC_3 + #elif defined(ARDUINO_ARCH_SAM) || defined(ARDUINO_ARCH_SAMD) + #define SPI_MISO PIN_SPI_MISO + #elif defined(__ARDUINO_ARC__) //Intel ARC32 Genuino 101 + #define SPI_MISO MISO + #elif defined(__RFduino__) //RFduino + #define SPI_MISO MISO + #elif defined(ARDUINO_ARCH_STM32) // STM32duino core + #define SPI_MISO MISO + #elif defined(ARDUINO_ARCH_ESP32) // arduino-esp32 + #define SPI_MISO MISO + #elif defined(STM32_MCU_SERIES) || defined(__STM32F1__) || defined(__STM32F3__) || defined(STM32F3) || defined(__STM32F4__) || defined(STM32F2) + #if defined(BOARD_SPI1_MISO_PIN) + #define SPI_MISO BOARD_SPI1_MISO_PIN + #else + #define SPI_MISO SPI.misoPin() + #endif + #elif defined(__MK20DX128__) || defined(__MKL26Z64__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) || defined(__IMXRT1062__) + #define SPI_MISO PIN_SPI_MISO + #endif +#endif +#if !defined(SPI_MISO) + // KH mod + //#error "Not defined SPI_MISO!" + #define SPI_MISO MISO + ////// +#endif +#if !defined(SPI_SCK) + #if defined(__AVR__) || defined(ESP8266) || defined(__RFduino__) + #define SPI_SCK SCK + #elif defined(ARDUINO_ARCH_AMEBA) + #define SPI_SCK 13 //PC_1 A4 + #elif defined(ARDUINO_ARCH_SAM) || defined(ARDUINO_ARCH_SAMD) + #define SPI_SCK PIN_SPI_SCK + #elif defined(__ARDUINO_ARC__) //Intel ARC32 Genuino 101 + #define SPI_SCK SCK + #elif defined(__RFduino__) //RFduino + #define SPI_SCK SCK + #elif defined(ARDUINO_ARCH_STM32) // STM32duino core + #define SPI_SCK SCK + #elif defined(ARDUINO_ARCH_ESP32) // arduino-esp32 + #define SPI_SCK SCK + #elif defined(STM32_MCU_SERIES) || defined(__STM32F1__) || defined(__STM32F3__) || defined(STM32F3) || defined(__STM32F4__) || defined(STM32F2) + #if defined(BOARD_SPI1_SCK_PIN) + #define SPI_SCK BOARD_SPI1_SCK_PIN + #else + #define SPI_SCK SPI.sckPin() + #endif + #elif defined(__MK20DX128__) || defined(__MKL26Z64__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) || defined(__IMXRT1062__) + #define SPI_SCK PIN_SPI_SCK + #endif +#endif +#if !defined(SPI_SCK) + // KH, For nRF52 + //#error "Not defined SPI_SCK!" + #define SPI_SCK SCK + ////// +#endif + +#if defined(__MBED__) || defined(ARDUINO_ARCH_SAM) || defined(ARDUINO_ARCH_SAMD) || defined(__ARDUINO_ARC__) || defined(__STM32F1__) || defined(__STM32F3__) || defined(STM32F3) || defined(__STM32F4__) || defined(STM32F2) || defined(ESP8266) || defined(ARDUINO_ARCH_AMEBA) || defined(__MK20DX128__) || defined(__MKL26Z64__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) || defined(__IMXRT1062__) || defined(__RFduino__) || defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_MEGAAVR) + #if defined(ARDUINO) && (!defined(ARDUINO_ARCH_STM32) && defined(STM32F3)) + #include "HardwareSPI.h" + #else + #include + #endif + #define ENC28J60_USE_SPILIB 1 +#endif + +#define UIP_RECEIVEBUFFERHANDLE 0xff + +#define UIP_SENDBUFFER_PADDING 7 +#define UIP_SENDBUFFER_OFFSET 1 + +/* + * Empfangen von ip-header, arp etc... + * wenn tcp/udp -> tcp/udp-callback -> assign new packet to connection + */ + +#define TX_COLLISION_RETRY_COUNT 10 + +class Enc28J60Network : public MemoryPool +{ + +private: + static uint16_t nextPacketPtr; + static uint8_t bank; + static uint8_t erevid; + + static struct memblock receivePkt; + + static bool broadcast_enabled; //!< True if broadcasts enabled (used to allow temporary disable of broadcast for DHCP or other internal functions) + + static uint8_t readOp(uint8_t op, uint8_t address); + static void writeOp(uint8_t op, uint8_t address, uint8_t data); + static uint16_t setReadPtr(memhandle handle, memaddress position, uint16_t len); + static void setERXRDPT(void); + static void readBuffer(uint16_t len, uint8_t* data); + static void writeBuffer(uint16_t len, uint8_t* data); + static uint8_t readByte(uint16_t addr); + static void writeByte(uint16_t addr, uint8_t data); + static void setBank(uint8_t address); + static uint8_t readReg(uint8_t address); + static void writeReg(uint8_t address, uint8_t data); + static void writeRegPair(uint8_t address, uint16_t data); + static void phyWrite(uint8_t address, uint16_t data); + static uint16_t phyRead(uint8_t address); + static void clkout(uint8_t clk); + + static void enableBroadcast (bool temporary); + static void disableBroadcast (bool temporary); + static void enableMulticast (void); + static void disableMulticast (void); + + static uint8_t readRegByte (uint8_t address); + static void writeRegByte (uint8_t address, uint8_t data); + + friend void enc28J60_mempool_block_move_callback(memaddress,memaddress,memaddress); + +public: + + void powerOn(void); + void powerOff(void); + static uint8_t geterevid(void); + uint16_t PhyStatus(void); + static bool linkStatus(void); + + static void init(uint8_t* macaddr); + static memhandle receivePacket(void); + static void freePacket(void); + static memaddress blockSize(memhandle handle); + static bool sendPacket(memhandle handle); + static uint16_t readPacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len); + static uint16_t writePacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len); + static void copyPacket(memhandle dest, memaddress dest_pos, memhandle src, memaddress src_pos, uint16_t len); + static uint16_t chksum(uint16_t sum, memhandle handle, memaddress pos, uint16_t len); +}; + +#endif /* Enc28J60NetworkClass_H_ */ diff --git a/LibraryPatches/UIPEthernet/UIPEthernet.cpp b/LibraryPatches/UIPEthernet/UIPEthernet.cpp new file mode 100644 index 0000000..df35732 --- /dev/null +++ b/LibraryPatches/UIPEthernet/UIPEthernet.cpp @@ -0,0 +1,641 @@ +/* + UIPEthernet.cpp - Arduino implementation of a uIP wrapper class. + Copyright (c) 2013 Norbert Truchsess + All rights reserved. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#if defined(ARDUINO) + #include +#endif +#if defined(__MBED__) + #include + #include "mbed/millis.h" +#endif +#include "UIPEthernet.h" +#include "utility/logging.h" +#include "utility/Enc28J60Network.h" + +#include "UIPUdp.h" + +extern "C" +{ +#include "utility/uipopt.h" +#include "utility/uip.h" +#include "utility/uip_arp.h" +#include "utility/uip_timer.h" +} + +#define ETH_HDR ((struct uip_eth_hdr *)&uip_buf[0]) + +memhandle UIPEthernetClass::in_packet(NOBLOCK); +memhandle UIPEthernetClass::uip_packet(NOBLOCK); +uint8_t UIPEthernetClass::uip_hdrlen(0); +uint8_t UIPEthernetClass::packetstate(0); + +unsigned long UIPEthernetClass::periodic_timer; + +IPAddress UIPEthernetClass::_dnsServerAddress; +#if UIP_UDP + DhcpClass* UIPEthernetClass::_dhcp(NULL); + static DhcpClass s_dhcp; // Placing this instance here is saving 40K to final *.bin (see bug below) +#endif + +// Because uIP isn't encapsulated within a class we have to use global +// variables, so we can only have one TCP/IP stack per program. + +UIPEthernetClass::UIPEthernetClass() +{ +} + +void UIPEthernetClass::init(const uint8_t pin) +{ + ENC28J60ControlCS = pin; +} + +#if UIP_UDP +int +UIPEthernetClass::begin(const uint8_t* mac) +{ + // KH mod to work with new func void MACAddress(uint8_t *mac_address); and SinricPro v2.5.1+ + // Now store to private var _mac_address + //uint8_t _mac_address[6] ={0,}; + memcpy(_mac_address, mac, sizeof(_mac_address)); + ////// + + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("UIPEthernetClass::begin(const uint8_t* mac) DEBUG_V3:Function started")); + #endif + //static DhcpClass s_dhcp; // <-- this is a bug ! + // I leave it there commented for history. It is bring all GCC "new" memory allocation code, making the *.bin almost 40K bigger. I've move it globally. + _dhcp = &s_dhcp; + // Initialise the basic info + netInit(mac); + + // Now try to get our config info from a DHCP server + int ret = _dhcp->beginWithDHCP((uint8_t*)mac); + if(ret == 1) + { + // We've successfully found a DHCP server and got our configuration info, so set things + // accordingly + configure(_dhcp->getLocalIp(),_dhcp->getDnsServerIp(),_dhcp->getGatewayIp(),_dhcp->getSubnetMask()); + } + return ret; +} +#endif + +void +UIPEthernetClass::begin(const uint8_t* mac, IPAddress ip) +{ + // KH mod to work with new func void MACAddress(uint8_t *mac_address); and SinricPro v2.5.1+ + // Now store to private var _mac_address + //uint8_t _mac_address[6] ={0,}; + memcpy(_mac_address, mac, sizeof(_mac_address)); + ////// + + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("UIPEthernetClass::begin(const uint8_t* mac, IPAddress ip) DEBUG_V3:Function started")); + #endif + IPAddress dns = ip; + dns[3] = 1; + begin(mac, ip, dns); +} + +void +UIPEthernetClass::begin(const uint8_t* mac, IPAddress ip, IPAddress dns) +{ + // KH mod to work with new func void MACAddress(uint8_t *mac_address); and SinricPro v2.5.1+ + // Now store to private var _mac_address + //uint8_t _mac_address[6] ={0,}; + memcpy(_mac_address, mac, sizeof(_mac_address)); + ////// + + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("UIPEthernetClass::begin(const uint8_t* mac, IPAddress ip, IPAddress dns) DEBUG_V3:Function started")); + #endif + IPAddress gateway = ip; + gateway[3] = 1; + begin(mac, ip, dns, gateway); +} + +void +UIPEthernetClass::begin(const uint8_t* mac, IPAddress ip, IPAddress dns, IPAddress gateway) +{ + // KH mod to work with new func void MACAddress(uint8_t *mac_address); and SinricPro v2.5.1+ + // Now store to private var _mac_address + //uint8_t _mac_address[6] ={0,}; + memcpy(_mac_address, mac, sizeof(_mac_address)); + ////// + + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("UIPEthernetClass::begin(const uint8_t* mac, IPAddress ip, IPAddress dns, IPAddress gateway) DEBUG_V3:Function started")); + #endif + IPAddress subnet(255, 255, 255, 0); + begin(mac, ip, dns, gateway, subnet); +} + +void +UIPEthernetClass::begin(const uint8_t* mac, IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet) +{ + // KH mod to work with new func void MACAddress(uint8_t *mac_address); and SinricPro v2.5.1+ + // Now store to private var _mac_address + //uint8_t _mac_address[6] ={0,}; + memcpy(_mac_address, mac, sizeof(_mac_address)); + ////// + + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("UIPEthernetClass::begin(const uint8_t* mac, IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet) DEBUG_V3:Function started")); + #endif + netInit(mac); + configure(ip,dns,gateway,subnet); +} + +int UIPEthernetClass::maintain(){ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("UIPEthernetClass::maintain() DEBUG_V3:Function started")); + #endif + tick(); + int rc = DHCP_CHECK_NONE; +#if UIP_UDP + if(_dhcp != NULL){ + //we have a pointer to dhcp, use it + rc = _dhcp->checkLease(); + switch ( rc ){ + case DHCP_CHECK_NONE: + //nothing done + break; + case DHCP_CHECK_RENEW_OK: + case DHCP_CHECK_REBIND_OK: + //we might have got a new IP. + configure(_dhcp->getLocalIp(),_dhcp->getDnsServerIp(),_dhcp->getGatewayIp(),_dhcp->getSubnetMask()); + break; + default: + //this is actually a error, it will retry though + break; + } + } + return rc; +#endif +} + +EthernetLinkStatus UIPEthernetClass::linkStatus() +{ + if (!Enc28J60.geterevid()) + return Unknown; + return Enc28J60.linkStatus() ? LinkON : LinkOFF; +} + +IPAddress UIPEthernetClass::localIP() +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("UIPEthernetClass::localIP() DEBUG_V3:Function started")); + #endif + IPAddress ret; + uip_ipaddr_t a; + uip_gethostaddr(a); + return ip_addr_uip(a); +} + +IPAddress UIPEthernetClass::subnetMask() +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("UIPEthernetClass::subnetMask() DEBUG_V3:Function started")); + #endif + IPAddress ret; + uip_ipaddr_t a; + uip_getnetmask(a); + return ip_addr_uip(a); +} + +IPAddress UIPEthernetClass::gatewayIP() +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("UIPEthernetClass::gatewayIP() DEBUG_V3:Function started")); + #endif + IPAddress ret; + uip_ipaddr_t a; + uip_getdraddr(a); + return ip_addr_uip(a); +} + +IPAddress UIPEthernetClass::dnsServerIP() +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("UIPEthernetClass::dnsServerIP() DEBUG_V3:Function started")); + #endif + return _dnsServerAddress; +} + +void +UIPEthernetClass::tick() +{ +#if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("UIPEthernetClass::tick() DEBUG_V3:Function started")); +#endif +if (Enc28J60Network::geterevid()==0) + { + #if ACTLOGLEVEL>=LOG_ERR + LogObject.uart_send_strln(F("UIPEthernetClass::tick() ERROR:EREVID=0 -> Not found ENC28j60 device !! Function ended !!")); + #endif + return; + } +#if defined(ESP8266) + wdt_reset(); +#endif + if (in_packet == NOBLOCK) + { + in_packet = Enc28J60Network::receivePacket(); + #if ACTLOGLEVEL>=LOG_DEBUG + if (in_packet != NOBLOCK) + { + LogObject.uart_send_str(F("UIPEthernetClass::tick() DEBUG:receivePacket: ")); + LogObject.uart_send_decln(in_packet); + } + #endif + } + if (in_packet != NOBLOCK) + { + packetstate = UIPETHERNET_FREEPACKET; + uip_len = Enc28J60Network::blockSize(in_packet); + if (uip_len > 0) + { + Enc28J60Network::readPacket(in_packet,0,(uint8_t*)uip_buf,UIP_BUFSIZE); + if (ETH_HDR ->type == HTONS(UIP_ETHTYPE_IP)) + { + uip_packet = in_packet; //required for upper_layer_checksum of in_packet! + #if ACTLOGLEVEL>=LOG_DEBUG + LogObject.uart_send_str(F("UIPEthernetClass::tick() DEBUG:readPacket type IP, uip_len: ")); + LogObject.uart_send_decln(uip_len); + #endif + uip_arp_ipin(); + uip_input(); + if (uip_len > 0) + { + uip_arp_out(); + network_send(); + } + } + else if (ETH_HDR ->type == HTONS(UIP_ETHTYPE_ARP)) + { + #if ACTLOGLEVEL>=LOG_DEBUG + LogObject.uart_send_str(F("UIPEthernetClass::tick() DEBUG:readPacket type ARP, uip_len: ")); + LogObject.uart_send_decln(uip_len); + #endif + uip_arp_arpin(); + if (uip_len > 0) + { + network_send(); + } + } + } + if (in_packet != NOBLOCK && (packetstate & UIPETHERNET_FREEPACKET)) + { + #if ACTLOGLEVEL>=LOG_DEBUG + LogObject.uart_send_str(F("UIPEthernetClass::tick() DEBUG:freeing packet: ")); + LogObject.uart_send_decln(in_packet); + #endif + Enc28J60Network::freePacket(); + in_packet = NOBLOCK; + } + } + + unsigned long now = millis(); + +#if UIP_CLIENT_TIMER >= 0 + bool periodic = (long)( now - periodic_timer ) >= 0; + for (int i = 0; i < UIP_CONNS; i++) + { +#else + if ((long)( now - periodic_timer ) >= 0) + { + periodic_timer = now + UIP_PERIODIC_TIMER; + + for (int i = 0; i < UIP_CONNS; i++) + { +#endif + + uip_conn = &uip_conns[i]; + +#if UIP_CLIENT_TIMER >= 0 + if (periodic) + { +#endif + + uip_process(UIP_TIMER); + +#if UIP_CLIENT_TIMER >= 0 + } + else + { + if (uip_conn!=NULL) + { + if (((uip_userdata_t*)uip_conn->appstate)!=NULL) + { + if ((long)( now - ((uip_userdata_t*)uip_conn->appstate)->timer) >= 0) + { + uip_process(UIP_POLL_REQUEST); + } + else + { + continue; + } + } + else + { + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("UIPEthernetClass::tick() DEBUG_V3:((uip_userdata_t*)uip_conn->appstate) is NULL")); + #endif + if ((long)( now - ((uip_userdata_t*)uip_conn)->timer) >= 0) + { + uip_process(UIP_POLL_REQUEST); + } + else + { + continue; + } + } + } + else + { + #if ACTLOGLEVEL>=LOG_ERR + LogObject.uart_send_strln(F("UIPEthernetClass::tick() ERROR:uip_conn is NULL")); + #endif + continue; + } + } +#endif + // If the above function invocation resulted in data that + // should be sent out on the Enc28J60Network, the global variable + // uip_len is set to a value > 0. + if (uip_len > 0) + { + uip_arp_out(); + network_send(); + } + } +#if UIP_CLIENT_TIMER >= 0 + if (periodic) + { + periodic_timer = now + UIP_PERIODIC_TIMER; +#endif +#if UIP_UDP + for (int i = 0; i < UIP_UDP_CONNS; i++) + { + uip_udp_periodic(i); + // If the above function invocation resulted in data that + // should be sent out on the Enc28J60Network, the global variable + // uip_len is set to a value > 0. */ + if (uip_len > 0) + { + UIPUDP::_send((uip_udp_userdata_t *)(uip_udp_conns[i].appstate)); + } + } +#endif /* UIP_UDP */ + } +} + +bool UIPEthernetClass::network_send() +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("UIPEthernetClass::network_send() DEBUG_V3:Function started")); + #endif + if (packetstate & UIPETHERNET_SENDPACKET) + { +#if ACTLOGLEVEL>=LOG_DEBUG + LogObject.uart_send_str(F("UIPEthernetClass::network_send() DEBUG:uip_packet: ")); + LogObject.uart_send_dec(uip_packet); + LogObject.uart_send_str(F(", hdrlen: ")); + LogObject.uart_send_decln(uip_hdrlen); +#endif + Enc28J60Network::writePacket(uip_packet,0,uip_buf,uip_hdrlen); + packetstate &= ~ UIPETHERNET_SENDPACKET; + goto sendandfree; + } + uip_packet = Enc28J60Network::allocBlock(uip_len); + if (uip_packet != NOBLOCK) + { +#if ACTLOGLEVEL>=LOG_DEBUG + LogObject.uart_send_str(F("UIPEthernetClass::network_send() DEBUG:uip_buf (uip_len): ")); + LogObject.uart_send_dec(uip_len); + LogObject.uart_send_str(F(", packet: ")); + LogObject.uart_send_decln(uip_packet); +#endif + Enc28J60Network::writePacket(uip_packet,0,uip_buf,uip_len); + goto sendandfree; + } + return false; +sendandfree: + Enc28J60Network::sendPacket(uip_packet); + Enc28J60Network::freeBlock(uip_packet); + uip_packet = NOBLOCK; + return true; +} + +void UIPEthernetClass::netInit(const uint8_t* mac) { + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("UIPEthernetClass::netInit(const uint8_t* mac) DEBUG_V3:Function started")); + #endif + periodic_timer = millis() + UIP_PERIODIC_TIMER; + + Enc28J60Network::init((uint8_t*)mac); + uip_seteth_addr(mac); + + uip_init(); + uip_arp_init(); +} + +void UIPEthernetClass::configure(IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet) { + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("UIPEthernetClass::configure(IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet) DEBUG_V3:Function started")); + #endif + uip_ipaddr_t ipaddr; + + uip_ip_addr(ipaddr, ip); + uip_sethostaddr(ipaddr); + + uip_ip_addr(ipaddr, gateway); + uip_setdraddr(ipaddr); + + uip_ip_addr(ipaddr, subnet); + uip_setnetmask(ipaddr); + + _dnsServerAddress = dns; +} + +UIPEthernetClass UIPEthernet; + +/*---------------------------------------------------------------------------*/ +uint16_t +UIPEthernetClass::chksum(uint16_t sum, const uint8_t *data, uint16_t len) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("UIPEthernetClass::chksum(uint16_t sum, const uint8_t *data, uint16_t len) DEBUG_V3:Function started")); + #endif + uint16_t t; + const uint8_t *dataptr; + const uint8_t *last_byte; + + dataptr = data; + last_byte = data + len - 1; + + while(dataptr < last_byte) { /* At least two more bytes */ + t = (dataptr[0] << 8) + dataptr[1]; + sum += t; + if(sum < t) { + sum++; /* carry */ + } + dataptr += 2; + } + + if(dataptr == last_byte) { + t = (dataptr[0] << 8) + 0; + sum += t; + if(sum < t) { + sum++; /* carry */ + } + } + + /* Return sum in host byte order. */ + return sum; +} + +/*---------------------------------------------------------------------------*/ + +uint16_t +UIPEthernetClass::ipchksum(void) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("UIPEthernetClass::ipchksum(void) DEBUG_V3:Function started")); + #endif + uint16_t sum; + + sum = chksum(0, &uip_buf[UIP_LLH_LEN], UIP_IPH_LEN); + return (sum == 0) ? 0xffff : htons(sum); +} + +/*---------------------------------------------------------------------------*/ +uint16_t +#if UIP_UDP +UIPEthernetClass::upper_layer_chksum(uint8_t proto) +#else +uip_tcpchksum(void) +#endif +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + #if UIP_UDP + LogObject.uart_send_strln(F("UIPEthernetClass::upper_layer_chksum(uint8_t proto) DEBUG_V3:Function started")); + #else + LogObject.uart_send_strln(F("uip_tcpchksum(void) INFO:Function started")); + #endif + #endif + uint16_t upper_layer_len; + uint16_t sum; + +#if UIP_CONF_IPV6 + upper_layer_len = (((u16_t)(BUF->len[0]) << 8) + BUF->len[1]); +#else /* UIP_CONF_IPV6 */ + upper_layer_len = (((u16_t)(BUF->len[0]) << 8) + BUF->len[1]) - UIP_IPH_LEN; +#endif /* UIP_CONF_IPV6 */ + + /* First sum pseudoheader. */ + + /* IP protocol and length fields. This addition cannot carry. */ +#if UIP_UDP + sum = upper_layer_len + proto; +#else + sum = upper_layer_len + UIP_PROTO_TCP; +#endif + /* Sum IP source and destination addresses. */ + sum = UIPEthernetClass::chksum(sum, (u8_t *)&BUF->srcipaddr[0], 2 * sizeof(uip_ipaddr_t)); + + uint8_t upper_layer_memlen; +#if UIP_UDP + switch(proto) + { +// case UIP_PROTO_ICMP: +// case UIP_PROTO_ICMP6: +// upper_layer_memlen = upper_layer_len; +// break; + case UIP_PROTO_UDP: + upper_layer_memlen = UIP_UDPH_LEN; + break; + default: +// case UIP_PROTO_TCP: +#endif + upper_layer_memlen = (BUF->tcpoffset >> 4) << 2; +#if UIP_UDP + break; + } +#endif + sum = UIPEthernetClass::chksum(sum, &uip_buf[UIP_IPH_LEN + UIP_LLH_LEN], upper_layer_memlen); +#if ACTLOGLEVEL>=LOG_DEBUG + #if UIP_UDP + LogObject.uart_send_str(F("UIPEthernetClass::upper_layer_chksum(uint8_t proto) DEBUG:uip_buf[")); + #else + LogObject.uart_send_str(F("uip_tcpchksum(void) DEBUG:uip_buf[")); + #endif + LogObject.uart_send_dec(UIP_IPH_LEN + UIP_LLH_LEN); + LogObject.uart_send_str(F("-")); + LogObject.uart_send_dec(UIP_IPH_LEN + UIP_LLH_LEN + upper_layer_memlen); + LogObject.uart_send_str(F("]: ")); + LogObject.uart_send_hexln(htons(sum)); +#endif + if (upper_layer_memlen < upper_layer_len) + { + sum = Enc28J60Network::chksum( + sum, + UIPEthernetClass::uip_packet, + UIP_IPH_LEN + UIP_LLH_LEN + upper_layer_memlen, + upper_layer_len - upper_layer_memlen + ); +#if ACTLOGLEVEL>=LOG_DEBUG + #if UIP_UDP + LogObject.uart_send_str(F("UIPEthernetClass::upper_layer_chksum(uint8_t proto) DEBUG:uip_packet(")); + #else + LogObject.uart_send_str(F("uip_tcpchksum(void) DEBUG:uip_packet(")); + #endif + LogObject.uart_send_dec(UIPEthernetClass::uip_packet); + LogObject.uart_send_str(F(")[")); + LogObject.uart_send_dec(UIP_IPH_LEN + UIP_LLH_LEN + upper_layer_memlen); + LogObject.uart_send_str(F("-")); + LogObject.uart_send_dec(UIP_IPH_LEN + UIP_LLH_LEN + upper_layer_len); + LogObject.uart_send_str(F("]: ")); + LogObject.uart_send_hexln(htons(sum)); +#endif + } + return (sum == 0) ? 0xffff : htons(sum); +} + +uint16_t +uip_ipchksum(void) +{ + return UIPEthernet.ipchksum(); +} + +#if UIP_UDP +uint16_t +uip_tcpchksum(void) +{ + uint16_t sum = UIPEthernet.upper_layer_chksum(UIP_PROTO_TCP); + return sum; +} + +uint16_t +uip_udpchksum(void) +{ + uint16_t sum = UIPEthernet.upper_layer_chksum(UIP_PROTO_UDP); + return sum; +} +#endif diff --git a/LibraryPatches/UIPEthernet/UIPEthernet.h b/LibraryPatches/UIPEthernet/UIPEthernet.h new file mode 100644 index 0000000..d02e538 --- /dev/null +++ b/LibraryPatches/UIPEthernet/UIPEthernet.h @@ -0,0 +1,158 @@ +/* + UIPEthernet.h - Arduino implementation of a uIP wrapper class. + Copyright (c) 2013 Norbert Truchsess + All rights reserved. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#ifndef UIPETHERNET_H +#define UIPETHERNET_H + +#include "ethernet_comp.h" +#if defined(__MBED__) + #include +#endif +#if defined(ARDUINO) + #include + #if defined(__STM32F3__) || defined(STM32F3) || defined(__RFduino__) + #include "mbed/IPAddress.h" + #else + #include "IPAddress.h" + #endif +#endif +#include "utility/Enc28J60Network.h" +#include "utility/uipopt.h" +#include "Dhcp.h" +#if UIP_UDP + #include "UIPUdp.h" +#endif +#include "UIPClient.h" +#include "UIPServer.h" + +extern "C" +{ +#include "utility/uip_timer.h" +#include "utility/uip.h" +} + +#define UIPETHERNET_FREEPACKET 1 +#define UIPETHERNET_SENDPACKET 2 +#define UIPETHERNET_BUFFERREAD 4 + +#define uip_ip_addr(addr, ip) do { \ + ((u16_t *)(addr))[0] = HTONS(((ip[0]) << 8) | (ip[1])); \ + ((u16_t *)(addr))[1] = HTONS(((ip[2]) << 8) | (ip[3])); \ + } while(0) + +#define ip_addr_uip(a) IPAddress(a[0] & 0xFF, a[0] >> 8 , a[1] & 0xFF, a[1] >> 8) //TODO this is not IPV6 capable + +#define uip_seteth_addr(eaddr) do {uip_ethaddr.addr[0] = eaddr[0]; \ + uip_ethaddr.addr[1] = eaddr[1];\ + uip_ethaddr.addr[2] = eaddr[2];\ + uip_ethaddr.addr[3] = eaddr[3];\ + uip_ethaddr.addr[4] = eaddr[4];\ + uip_ethaddr.addr[5] = eaddr[5];} while(0) + +#define BUF ((struct uip_tcpip_hdr *)&uip_buf[UIP_LLH_LEN]) + +enum EthernetLinkStatus { + Unknown, + LinkON, + LinkOFF +}; + +class UIPEthernetClass +{ +public: + UIPEthernetClass(); + + void init(const uint8_t pin); + + int begin(const uint8_t* mac); + void begin(const uint8_t* mac, IPAddress ip); + void begin(const uint8_t* mac, IPAddress ip, IPAddress dns); + void begin(const uint8_t* mac, IPAddress ip, IPAddress dns, IPAddress gateway); + void begin(const uint8_t* mac, IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet); + + // maintain() must be called at regular intervals to process the incoming serial + // data and issue IP events to the sketch. It does not return until all IP + // events have been processed. Renews dhcp-lease if required. + int maintain(); + + EthernetLinkStatus linkStatus(); + + // KH add to have similar function to Ethernet lib + // Certainly we can use void macAddress(uint8_t mac[]) to read from W5x00. + void MACAddress(uint8_t *mac_address) + { + memcpy(mac_address, _mac_address, sizeof(_mac_address)); + } + ////// + + IPAddress localIP(); + IPAddress subnetMask(); + IPAddress gatewayIP(); + IPAddress dnsServerIP(); + +private: + + // KH add to work with new func void MACAddress(uint8_t *mac_address); and SinricPro v2.5.1+ + uint8_t _mac_address[6] ={0,}; + ////// + + static memhandle in_packet; + static memhandle uip_packet; + static uint8_t uip_hdrlen; + static uint8_t packetstate; + + static IPAddress _dnsServerAddress; + #if UIP_UDP + static DhcpClass* _dhcp; + #endif + static unsigned long periodic_timer; + + static void netInit(const uint8_t* mac); + static void configure(IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet); + + static void tick(); + + static bool network_send(); + + friend class UIPServer; + + friend class UIPClient; + + friend class UIPUDP; + + static uint16_t chksum(uint16_t sum, const uint8_t* data, uint16_t len); + static uint16_t ipchksum(void); +#if UIP_UDP + static uint16_t upper_layer_chksum(uint8_t proto); +#endif + friend uint16_t uip_ipchksum(void); + friend uint16_t uip_tcpchksum(void); + friend uint16_t uip_udpchksum(void); + + friend void uipclient_appcall(void); + friend void uipudp_appcall(void); + +#if UIP_CONF_IPV6 + uint16_t uip_icmp6chksum(void); +#endif /* UIP_CONF_IPV6 */ +}; + +extern UIPEthernetClass UIPEthernet; + +#endif diff --git a/LibraryPatches/UIPEthernet/utility/Enc28J60Network.cpp b/LibraryPatches/UIPEthernet/utility/Enc28J60Network.cpp new file mode 100644 index 0000000..a280225 --- /dev/null +++ b/LibraryPatches/UIPEthernet/utility/Enc28J60Network.cpp @@ -0,0 +1,1244 @@ +/* + Enc28J60NetworkClass.h + UIPEthernet network driver for Microchip ENC28J60 Ethernet Interface. + + Copyright (c) 2013 Norbert Truchsess + All rights reserved. + + based on enc28j60.c file from the AVRlib library by Pascal Stang. + For AVRlib See http://www.procyonengineering.com/ + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + + +#include "Enc28J60Network.h" +#if defined(ARDUINO) + #include "Arduino.h" +#endif +#if defined(__MBED__) + #include + #include "mbed/millis.h" + #define delay(x) wait_ms(x) +#endif +#include "logging.h" + +// KH, For nRF52 +//#define ENC28J60_USE_SPILIB true + +uint8_t ENC28J60ControlCS = ENC28J60_CONTROL_CS; + +#if ENC28J60_USE_SPILIB + #if defined(ARDUINO) + #if defined(STM32F2) + #include + #elif !defined(STM32F3) && !defined(__STM32F4__) + #include + extern SPIClass SPI; + //#elif defined(ARDUINO_ARCH_AMEBA) + //SPIClass SPI((void *)(&spi_obj), 11, 12, 13, 10); + //SPI _spi(SPI_MOSI,SPI_MISO,SPI_SCK,ENC28J60ControlCS); + #else + #include "HardwareSPI.h" + extern HardwareSPI SPI(1); + #endif + #endif + #if defined(__MBED__) + SPI _spi(SPI_MOSI,SPI_MISO,SPI_SCK); + DigitalOut _cs(ENC28J60ControlCS); + Serial LogObject(SERIAL_TX,SERIAL_RX); + #endif +#endif + +extern "C" { + #if defined(ARDUINO_ARCH_AVR) + // AVR-specific code + #include + #elif defined(ARDUINO_ARCH_SAM) + // SAM-specific code + #elif defined(ARDUINO_ARCH_SAMD) + // SAMD-specific code + #else + // generic, non-platform specific code + #endif +#include "enc28j60.h" +#include "uip.h" +} + +#if defined(ARDUINO) + // set CS to 0 = active + #define CSACTIVE digitalWrite(ENC28J60ControlCS, LOW) + // set CS to 1 = passive + #define CSPASSIVE digitalWrite(ENC28J60ControlCS, HIGH) +#endif +#if defined(__MBED__) + // set CS to 0 = active + #define CSACTIVE _cs=0 + // set CS to 1 = passive + #define CSPASSIVE _cs=1 +#endif + +// +#if defined(ARDUINO_ARCH_AVR) +#define waitspi() while(!(SPSR&(1<=LOG_DEBUG + LogObject.uart_send_str(F("ENC28J60::init DEBUG:csPin = ")); + LogObject.uart_send_decln(ENC28J60ControlCS); + LogObject.uart_send_str(F("ENC28J60::init DEBUG:miso = ")); + LogObject.uart_send_decln(SPI_MISO); + LogObject.uart_send_str(F("ENC28J60::init DEBUG:mosi = ")); + LogObject.uart_send_decln(SPI_MOSI); + LogObject.uart_send_str(F("ENC28J60::init DEBUG:sck = ")); + LogObject.uart_send_decln(SPI_SCK); + #endif +#if ENC28J60_USE_SPILIB + #if ACTLOGLEVEL>=LOG_DEBUG + LogObject.uart_send_strln(F("ENC28J60::init DEBUG:Use SPI lib SPI.begin()")); + #endif + #if defined(ARDUINO) + #if defined(__STM32F3__) || defined(STM32F3) || defined(__STM32F4__) + SPI.begin(SPI_9MHZ, MSBFIRST, 0); + #else + SPI.begin(); + #endif + #endif + #if defined(ARDUINO_ARCH_AVR) + // AVR-specific code + SPI.setClockDivider(SPI_CLOCK_DIV2); //results in 8MHZ at 16MHZ system clock. + #elif defined(ARDUINO_ARCH_SAM) + // SAM-specific code + SPI.setClockDivider(10); //defaults to 21 which results in aprox. 4MHZ. A 10 should result in a little more than 8MHZ. + #elif defined(ARDUINO_ARCH_SAMD) + // SAMD-specific code + // Should we set clock divider? + SPI.setClockDivider(10); + #elif defined(__STM32F1__) || defined(__STM32F3__) + // generic, non-platform specific code + #define USE_STM32F1_DMAC 1 //on STM32 + // BOARD_NR_SPI >= 1 BOARD_SPI1_NSS_PIN, BOARD_SPI1_SCK_PIN, BOARD_SPI1_MISO_PIN, BOARD_SPI1_MOSI_PIN + SPI.setBitOrder(MSBFIRST); + SPI.setDataMode(SPI_MODE0); + SPI.setClockDivider(SPI_CLOCK_DIV8); //value 8 the result is 9MHz at 72MHz clock. + #else + #if defined(ARDUINO) + #if !defined(__STM32F3__) && !defined(STM32F3) && !defined(__STM32F4__) + SPI.setBitOrder(MSBFIRST); + #endif + //Settings for ESP8266 + //SPI.setDataMode(SPI_MODE0); + //SPI.setClockDivider(SPI_CLOCK_DIV16); + #endif + #if defined(__MBED__) + _spi.format(8, 0); // 8bit, mode 0 + _spi.frequency(7000000); // 7MHz + #endif + #endif +#else + #if ACTLOGLEVEL>=LOG_DEBUG + LogObject.uart_send_strln(F("ENC28J60::init DEBUG:Use Native hardware SPI")); + #endif + pinMode(SPI_MOSI, OUTPUT); + pinMode(SPI_SCK, OUTPUT); + pinMode(SPI_MISO, INPUT); + //Hardware SS must be configured as OUTPUT to enable SPI-master (regardless of which pin is configured as ENC28J60ControlCS) + pinMode(SS, OUTPUT); + digitalWrite(SS,HIGH); + + digitalWrite(SPI_MOSI, LOW); + digitalWrite(SPI_SCK, LOW); + + // initialize SPI interface + // master mode and Fosc/2 clock: + SPCR = (1<=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("ENC28J60::init DEBUG_V3:Before readOp(ENC28J60_READ_CTRL_REG, ESTAT)")); + #endif + nextPacketPtr = RXSTART_INIT; + while ((!readOp(ENC28J60_READ_CTRL_REG, ESTAT) & ESTAT_CLKRDY) && (timeout>0)) + { + timeout=timeout-1; + delay(10); + #if defined(ESP8266) + wdt_reset(); + #endif + } + #if ACTLOGLEVEL>=LOG_ERR + if (timeout==0) {LogObject.uart_send_strln(F("ENC28J60::init ERROR:TIMEOUT !!"));} + #endif + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("ENC28J60::init DEBUG_V3:After readOp(ENC28J60_READ_CTRL_REG, ESTAT)")); + #endif + // Rx start + writeRegPair(ERXSTL, RXSTART_INIT); + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("ENC28J60::init DEBUG_V3:After writeRegPair(ERXSTL, RXSTART_INIT)")); + #endif + // set receive pointer address + writeRegPair(ERXRDPTL, RXSTART_INIT); + // RX end + writeRegPair(ERXNDL, RXSTOP_INIT); + // TX start + //writeRegPair(ETXSTL, TXSTART_INIT); + // TX end + //writeRegPair(ETXNDL, TXSTOP_INIT); + // do bank 1 stuff, packet filter: + // For broadcast packets we allow only ARP packtets + // All other packets should be unicast only for our mac (MAADR) + // + // The pattern to match on is therefore + // Type ETH.DST + // ARP BROADCAST + // 06 08 -- ff ff ff ff ff ff -> ip checksum for theses bytes=f7f9 + // in binary these poitions are:11 0000 0011 1111 + // This is hex 303F->EPMM0=0x3f,EPMM1=0x30 + //TODO define specific pattern to receive dhcp-broadcast packages instead of setting ERFCON_BCEN! +// enableBroadcast(); // change to add ERXFCON_BCEN recommended by epam + writeReg(ERXFCON, ERXFCON_UCEN|ERXFCON_CRCEN|ERXFCON_PMEN|ERXFCON_BCEN); + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("ENC28J60::init DEBUG_V3:After writeReg(ERXFCON, ERXFCON_UCEN|ERXFCON_CRCEN|ERXFCON_PMEN|ERXFCON_BCEN)")); + #endif + #if defined(ESP8266) + wdt_reset(); + #endif + writeRegPair(EPMM0, 0x303f); + writeRegPair(EPMCSL, 0xf7f9); + // + // + // do bank 2 stuff + // enable MAC receive + // and bring MAC out of reset (writes 0x00 to MACON2) + writeRegPair(MACON1, MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS); + // enable automatic padding to 60bytes and CRC operations + writeOp(ENC28J60_BIT_FIELD_SET, MACON3, MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN); + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("ENC28J60::init DEBUG_V3:After writeOp(ENC28J60_BIT_FIELD_SET, MACON3, MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN)")); + #endif + // set inter-frame gap (non-back-to-back) + writeRegPair(MAIPGL, 0x0C12); + // set inter-frame gap (back-to-back) + writeReg(MABBIPG, 0x12); + // Set the maximum packet size which the controller will accept + // Do not send packets longer than MAX_FRAMELEN: + writeRegPair(MAMXFLL, MAX_FRAMELEN); + // do bank 3 stuff + // write MAC address + // NOTE: MAC address in ENC28J60 is byte-backward + writeReg(MAADR5, macaddr[0]); + writeReg(MAADR4, macaddr[1]); + writeReg(MAADR3, macaddr[2]); + writeReg(MAADR2, macaddr[3]); + writeReg(MAADR1, macaddr[4]); + writeReg(MAADR0, macaddr[5]); + // no loopback of transmitted frames + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("ENC28J60::init DEBUG_V3:Before phyWrite(PHCON2, PHCON2_HDLDIS)")); + #endif + phyWrite(PHCON2, PHCON2_HDLDIS); + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("ENC28J60::init DEBUG_V3:After phyWrite(PHCON2, PHCON2_HDLDIS)")); + #endif + // switch to bank 0 + setBank(ECON1); + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("ENC28J60::init DEBUG_V3:After setBank(ECON1)")); + #endif + // enable interrutps + writeOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE|EIE_PKTIE); + // enable packet reception + writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN); + //Configure leds + phyWrite(PHLCON,0x476); + + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("ENC28J60::init DEBUG_V3:Before readReg(EREVID);")); + #endif + erevid=readReg(EREVID); + if (erevid==0xFF) {erevid=0;} + // microchip forgot to step the number on the silcon when they + // released the revision B7. 6 is now rev B7. We still have + // to see what they do when they release B8. At the moment + // there is no B8 out yet + //if (erevid > 5) ++erevid; + #if ACTLOGLEVEL>=LOG_INFO + LogObject.uart_send_str(F("ENC28J60::init INFO: Chip erevid=")); + LogObject.uart_send_dec(erevid); + LogObject.uart_send_strln(F(" initialization completed.")); + #endif + +// return Enc28J60Network::erevid; +} + +memhandle +Enc28J60Network::receivePacket(void) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::receivePacket(void) DEBUG_V3:Function started")); + #endif + #if defined(ESP8266) + wdt_reset(); + #endif + uint8_t rxstat; + uint16_t len; + // check if a packet has been received and buffered + //if( !(readReg(EIR) & EIR_PKTIF) ){ + // The above does not work. See Rev. B4 Silicon Errata point 6. + #if ACTLOGLEVEL>=LOG_ERR + if (erevid==0) + { + LogObject.uart_send_strln(F("Enc28J60Network::receivePacket(void) ERROR:ENC28j50 Device not found !! Bypass receivePacket function !!")); + } + #endif + uint8_t epktcnt=readReg(EPKTCNT); + if ((erevid!=0) && (epktcnt!=0)) + { + uint16_t readPtr = nextPacketPtr+6 > RXSTOP_INIT ? nextPacketPtr+6-RXSTOP_INIT+RXSTART_INIT : nextPacketPtr+6; + // Set the read pointer to the start of the received packet + writeRegPair(ERDPTL, nextPacketPtr); + // read the next packet pointer + nextPacketPtr = readOp(ENC28J60_READ_BUF_MEM, 0); + nextPacketPtr |= readOp(ENC28J60_READ_BUF_MEM, 0) << 8; + // read the packet length (see datasheet page 43) + len = readOp(ENC28J60_READ_BUF_MEM, 0); + len |= readOp(ENC28J60_READ_BUF_MEM, 0) << 8; + len -= 4; //remove the CRC count + // read the receive status (see datasheet page 43) + rxstat = readOp(ENC28J60_READ_BUF_MEM, 0); + //rxstat |= readOp(ENC28J60_READ_BUF_MEM, 0) << 8; + #if ACTLOGLEVEL>=LOG_DEBUG + LogObject.uart_send_str(F("Enc28J60Network::receivePacket(void) DEBUG:receivePacket [")); + LogObject.uart_send_hex(readPtr); + LogObject.uart_send_str(F("-")); + LogObject.uart_send_hex((readPtr+len) % (RXSTOP_INIT+1)); + LogObject.uart_send_str(F("], next: ")); + LogObject.uart_send_hex(nextPacketPtr); + LogObject.uart_send_str(F(", stat: ")); + LogObject.uart_send_hex(rxstat); + LogObject.uart_send_str(F(", Packet count: ")); + LogObject.uart_send_dec(epktcnt); + LogObject.uart_send_str(F(" -> ")); + LogObject.uart_send_strln((rxstat & 0x80)!=0 ? "OK" : "failed"); + #endif + // decrement the packet counter indicate we are done with this packet + writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC); + // check CRC and symbol errors (see datasheet page 44, table 7-3): + // The ERXFCON.CRCEN is set by default. Normally we should not + // need to check this. + if (((rxstat & 0x80) != 0) && (nextPacketPtr<=RXSTOP_INIT)) + { + receivePkt.begin = readPtr; + receivePkt.size = len; + #if ACTLOGLEVEL>=LOG_DEBUG + LogObject.uart_send_str(F("Enc28J60Network::receivePacket(void) DEBUG: rxstat OK. receivePkt.size=")); + LogObject.uart_send_decln(len); + #endif + return UIP_RECEIVEBUFFERHANDLE; + } + // Move the RX read pointer to the start of the next received packet + // This frees the memory we just read out + setERXRDPT(); + } + return (NOBLOCK); +} + +void +Enc28J60Network::setERXRDPT(void) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::setERXRDPT(void) DEBUG_V3:Function started")); + #endif + // Make sure the value is odd. See Rev. B1,B4,B5,B7 Silicon Errata issues 14 + uint16_t actnextPacketPtr = nextPacketPtr == RXSTART_INIT ? RXSTOP_INIT : nextPacketPtr-1; + #if ACTLOGLEVEL>=LOG_DEBUG + LogObject.uart_send_str(F("Enc28J60Network::setERXRDPT(void) DEBUG:Set actnextPacketPtr:")); + LogObject.uart_send_hexln(actnextPacketPtr); + #endif + // datasheet: The ENC28J60 will always write up to, but not including + writeRegPair(ERXRDPTL, actnextPacketPtr); +} + +memaddress +Enc28J60Network::blockSize(memhandle handle) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::blockSize(memhandle handle) DEBUG_V3:Function started")); + #endif + return ((handle == NOBLOCK) || (erevid==0)) ? 0 : handle == UIP_RECEIVEBUFFERHANDLE ? receivePkt.size : blocks[handle].size; +} + +void +Enc28J60Network::sendPacket(memhandle handle) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::sendPacket(memhandle handle) INFO:Function started")); + #endif + #if defined(ESP8266) + wdt_reset(); + #endif + if (erevid==0) + { + #if ACTLOGLEVEL>=LOG_ERR + LogObject.uart_send_strln(F("Enc28J60Network::sendPacket(memhandle handle) ERROR:ENC28j50 Device not found !! Bypass sendPacket function !!")); + #endif + return; + } + + memblock *packet = &blocks[handle]; + uint16_t start = packet->begin-1; + uint16_t end = start + packet->size; + + // backup data at control-byte position + uint8_t data = readByte(start); + // write control-byte (if not 0 anyway) + if (data) + writeByte(start, 0); + + #if ACTLOGLEVEL>=LOG_DEBUG + LogObject.uart_send_str(F("Enc28J60Network::sendPacket(memhandle handle) DEBUG:sendPacket(")); + LogObject.uart_send_dec(handle); + LogObject.uart_send_str(F(") [")); + LogObject.uart_send_hex(start); + LogObject.uart_send_str(F("-")); + LogObject.uart_send_hex(end); + LogObject.uart_send_str(F("]: ")); + for (uint16_t i=start; i<=end; i++) + { + LogObject.uart_send_hex(readByte(i)); + LogObject.uart_send_str(F(" ")); + } + LogObject.uart_send_strln(F("")); + #endif + + // TX start + writeRegPair(ETXSTL, start); + // Set the TXND pointer to correspond to the packet size given + writeRegPair(ETXNDL, end); + // send the contents of the transmit buffer onto the network + + unsigned int retry = TX_COLLISION_RETRY_COUNT; + unsigned int timeout = 100; + do + { + // seydamir added + // Reset the transmit logic problem. See Rev. B7 Silicon Errata issues 12 and 13 + writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRST); + writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRST); + writeOp(ENC28J60_BIT_FIELD_CLR, EIR, EIR_TXERIF | EIR_TXIF); + // end + + writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS); + // Reset the transmit logic problem. See Rev. B4 Silicon Errata point 12. + //if( (readReg(EIR) & EIR_TXERIF) ) + // { + // writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRTS); + // } + + timeout = 100; + while (((readReg(EIR) & (EIR_TXIF | EIR_TXERIF)) == 0) && (timeout>0)) + { + timeout=timeout-1; + delay(10); + #if defined(ESP8266) + wdt_reset(); + #endif + } + if (timeout==0) + { + /* Transmit hardware probably hung, try again later. */ + /* Shouldn't happen according to errata 12 and 13. */ + writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRTS); + #if ACTLOGLEVEL>=LOG_WARN + LogObject.uart_send_strln(F("Enc28J60Network::sendPacket(memhandle handle) WARNING:Collision")); + #endif + retry=retry-1; + } + } while ((timeout == 0) && (retry != 0)); + + //restore data on control-byte position + if (data) + writeByte(start, data); + + if (retry == 0) + { + #if ACTLOGLEVEL>=LOG_ERROR + LogObject.uart_send_strln(F("Enc28J60Network::sendPacket(memhandle handle) ERROR:COLLISION !!")); + #endif + return; + } +} + +uint16_t +Enc28J60Network::setReadPtr(memhandle handle, memaddress position, uint16_t len) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::setReadPtr(memhandle handle, memaddress position, uint16_t len) DEBUG_V3:Function started")); + #endif + memblock *packet = handle == UIP_RECEIVEBUFFERHANDLE ? &receivePkt : &blocks[handle]; + memaddress start = handle == UIP_RECEIVEBUFFERHANDLE && packet->begin + position > RXSTOP_INIT ? packet->begin + position-RXSTOP_INIT+RXSTART_INIT : packet->begin + position; + + writeRegPair(ERDPTL, start); + + if (len > packet->size - position) + len = packet->size - position; + return len; +} + +uint16_t +Enc28J60Network::readPacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::readPacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len) DEBUG_V3:Function started")); + #endif + #if defined(ESP8266) + wdt_reset(); + #endif + len = setReadPtr(handle, position, len); + readBuffer(len, buffer); + #if ACTLOGLEVEL>=LOG_DEBUG_V2 + LogObject.uart_send_str(F("Enc28J60Network::readPacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len) DEBUG_V2: Read bytes:")); + LogObject.uart_send_dec(len); + LogObject.uart_send_str(F(" save to block(")); + LogObject.uart_send_dec(handle); + LogObject.uart_send_str(F(") [")); + LogObject.uart_send_hex(position); + LogObject.uart_send_str(F("]: ")); + for (uint16_t i=0; i=LOG_DEBUG_V3 + LogObject.uart_send_str(F("Enc28J60Network::writePacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len) DEBUG_V3:Function started with len:")); + LogObject.uart_send_decln(len); + #endif + #if defined(ESP8266) + wdt_reset(); + #endif + memblock *packet = &blocks[handle]; + uint16_t start = packet->begin + position; + + writeRegPair(EWRPTL, start); + + if (len > packet->size - position) + len = packet->size - position; + writeBuffer(len, buffer); + #if ACTLOGLEVEL>=LOG_DEBUG_V2 + LogObject.uart_send_str(F("Enc28J60Network::writePacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len) DEBUG_V2: Write bytes:")); + LogObject.uart_send_dec(len); + LogObject.uart_send_str(F(" save to block(")); + LogObject.uart_send_dec(handle); + LogObject.uart_send_str(F(") [")); + LogObject.uart_send_hex(start); + LogObject.uart_send_str(F("]: ")); + for (uint16_t i=0; i=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::enableBroadcast (bool temporary) DEBUG_V3:Function started")); + #endif + writeRegByte(ERXFCON, readRegByte(ERXFCON) | ERXFCON_BCEN); + if(!temporary) + broadcast_enabled = true; +} + +void Enc28J60Network::disableBroadcast (bool temporary) { + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::disableBroadcast (bool temporary) DEBUG_V3:Function started")); + #endif + if(!temporary) + broadcast_enabled = false; + if(!broadcast_enabled) + writeRegByte(ERXFCON, readRegByte(ERXFCON) & ~ERXFCON_BCEN); +} + +void Enc28J60Network::enableMulticast (void) { + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::enableMulticast (void) DEBUG_V3:Function started")); + #endif + writeRegByte(ERXFCON, readRegByte(ERXFCON) | ERXFCON_MCEN); +} + +void Enc28J60Network::disableMulticast (void) { + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::disableMulticast (void) DEBUG_V3:Function started")); + #endif + writeRegByte(ERXFCON, readRegByte(ERXFCON) & ~ERXFCON_MCEN); +} + +uint8_t Enc28J60Network::readRegByte (uint8_t address) { + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::readRegByte (uint8_t address) DEBUG_V3:Function started")); + #endif + setBank(address); + return readOp(ENC28J60_READ_CTRL_REG, address); +} + +void Enc28J60Network::writeRegByte (uint8_t address, uint8_t data) { + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::writeRegByte (uint8_t address, uint8_t data) DEBUG_V3:Function started")); + #endif + setBank(address); + writeOp(ENC28J60_WRITE_CTRL_REG, address, data); +} + + +uint8_t Enc28J60Network::readByte(uint16_t addr) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::readByte(uint16_t addr) DEBUG_V3:Function started")); + #endif + #if defined(ESP8266) + wdt_reset(); + #endif + writeRegPair(ERDPTL, addr); + + CSACTIVE; + #if ENC28J60_USE_SPILIB + // issue read command + #if defined(ARDUINO) + SPI.transfer(ENC28J60_READ_BUF_MEM); + // read data + uint8_t c = SPI.transfer(0x00); + #endif + #if defined(__MBED__) + _spi.write(ENC28J60_READ_BUF_MEM); + // read data + uint8_t c = _spi.write(0x00); + #endif + CSPASSIVE; + return (c); + #else + // issue read command + SPDR = ENC28J60_READ_BUF_MEM; + waitspi(); + // read data + SPDR = 0x00; + waitspi(); + CSPASSIVE; + return (SPDR); + #endif +} + +void Enc28J60Network::writeByte(uint16_t addr, uint8_t data) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::writeByte(uint16_t addr, uint8_t data) DEBUG_V3:Function started")); + #endif + #if defined(ESP8266) + wdt_reset(); + #endif + writeRegPair(EWRPTL, addr); + + CSACTIVE; + #if ENC28J60_USE_SPILIB + // issue write command + #if defined(ARDUINO) + SPI.transfer(ENC28J60_WRITE_BUF_MEM); + // write data + SPI.transfer(data); + #endif + #if defined(__MBED__) + _spi.write(ENC28J60_WRITE_BUF_MEM); + // write data + _spi.write(data); + #endif + #else + // issue write command + SPDR = ENC28J60_WRITE_BUF_MEM; + waitspi(); + // write data + SPDR = data; + waitspi(); + #endif + CSPASSIVE; +} + +void +Enc28J60Network::copyPacket(memhandle dest_pkt, memaddress dest_pos, memhandle src_pkt, memaddress src_pos, uint16_t len) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::copyPacket(memhandle dest_pkt, memaddress dest_pos, memhandle src_pkt, memaddress src_pos, uint16_t len) DEBUG_V3:Function started")); + #endif + memblock *dest = &blocks[dest_pkt]; + memblock *src = src_pkt == UIP_RECEIVEBUFFERHANDLE ? &receivePkt : &blocks[src_pkt]; + memaddress start = src_pkt == UIP_RECEIVEBUFFERHANDLE && src->begin + src_pos > RXSTOP_INIT ? src->begin + src_pos-RXSTOP_INIT+RXSTART_INIT : src->begin + src_pos; + enc28J60_mempool_block_move_callback(dest->begin+dest_pos,start,len); + // setERXRDPT(); let it to freePacket after all packets are saved +} + +void +enc28J60_mempool_block_move_callback(memaddress dest, memaddress src, memaddress len) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("enc28J60_mempool_block_move_callback(memaddress dest, memaddress src, memaddress len) DEBUG_V3:Function started")); + #endif +//void +//Enc28J60Network::memblock_mv_cb(uint16_t dest, uint16_t src, uint16_t len) +//{ + //as ENC28J60 DMA is unable to copy single bytes: + if (len == 1) + { + Enc28J60Network::writeByte(dest,Enc28J60Network::readByte(src)); + } + else + { + // calculate address of last byte + len += src - 1; + + /* 1. Appropriately program the EDMAST, EDMAND + and EDMADST register pairs. The EDMAST + registers should point to the first byte to copy + from, the EDMAND registers should point to the + last byte to copy and the EDMADST registers + should point to the first byte in the destination + range. The destination range will always be + linear, never wrapping at any values except from + 8191 to 0 (the 8-Kbyte memory boundary). + Extreme care should be taken when + programming the start and end pointers to + prevent a never ending DMA operation which + would overwrite the entire 8-Kbyte buffer. + */ + Enc28J60Network::writeRegPair(EDMASTL, src); + Enc28J60Network::writeRegPair(EDMADSTL, dest); + + if ((src <= RXSTOP_INIT)&& (len > RXSTOP_INIT))len -= ((RXSTOP_INIT + 1)-RXSTART_INIT); + Enc28J60Network::writeRegPair(EDMANDL, len); + + /* + 2. If an interrupt at the end of the copy process is + desired, set EIE.DMAIE and EIE.INTIE and + clear EIR.DMAIF. + + 3. Verify that ECON1.CSUMEN is clear. */ + Enc28J60Network::writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_CSUMEN); + + /* 4. Start the DMA copy by setting ECON1.DMAST. */ + Enc28J60Network::writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_DMAST); + + // wait until runnig DMA is completed + while (Enc28J60Network::readOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_DMAST) + { + delay(1); + } + } +} + +void +Enc28J60Network::freePacket(void) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::freePacket(void) DEBUG_V3:Function started")); + #endif + setERXRDPT(); +} + +uint8_t +Enc28J60Network::readOp(uint8_t op, uint8_t address) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::readOp(uint8_t op, uint8_t address) DEBUG_V3:Function started")); + #endif + CSACTIVE; + // issue read command + #if ENC28J60_USE_SPILIB + #if defined(ARDUINO) + SPI.transfer(op | (address & ADDR_MASK)); + // read data + if(address & 0x80) + { + // do dummy read if needed (for mac and mii, see datasheet page 29) + SPI.transfer(0x00); + } + uint8_t c = SPI.transfer(0x00); + #endif + #if defined(__MBED__) + _spi.write(op | (address & ADDR_MASK)); + // read data + if(address & 0x80) + { + // do dummy read if needed (for mac and mii, see datasheet page 29) + _spi.write(0x00); + } + uint8_t c = _spi.write(0x00); + #endif + // release CS + CSPASSIVE; + return(c); + #else + // issue read command + SPDR = op | (address & ADDR_MASK); + waitspi(); + // read data + SPDR = 0x00; + waitspi(); + // do dummy read if needed (for mac and mii, see datasheet page 29) + if(address & 0x80) + { + SPDR = 0x00; + waitspi(); + } + // release CS + CSPASSIVE; + return(SPDR); + #endif + #if defined(ESP8266) + yield(); + #endif +} + +void +Enc28J60Network::writeOp(uint8_t op, uint8_t address, uint8_t data) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::writeOp(uint8_t op, uint8_t address, uint8_t data) DEBUG_V3:Function started")); + #endif + CSACTIVE; + // issue write command + #if ENC28J60_USE_SPILIB + #if defined(ARDUINO) + SPI.transfer(op | (address & ADDR_MASK)); + // write data + SPI.transfer(data); + #endif + #if defined(__MBED__) + _spi.write(op | (address & ADDR_MASK)); + // write data + _spi.write(data); + #endif + #else + // issue write command + SPDR = op | (address & ADDR_MASK); + waitspi(); + // write data + SPDR = data; + waitspi(); + #endif + CSPASSIVE; + #if defined(ESP8266) + yield(); + #endif +} + +void +Enc28J60Network::readBuffer(uint16_t len, uint8_t* data) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::readBuffer(uint16_t len, uint8_t* data) DEBUG_V3:Function started")); + #endif + CSACTIVE; + // issue read command + #if ENC28J60_USE_SPILIB + #if defined(ARDUINO) + SPI.transfer(ENC28J60_READ_BUF_MEM); + #endif + #if defined(__MBED__) + _spi.write(ENC28J60_READ_BUF_MEM); + #endif + #else + SPDR = ENC28J60_READ_BUF_MEM; + waitspi(); + #endif + while(len) + { + len--; + // read data + #if ENC28J60_USE_SPILIB + #if defined(ARDUINO) + *data = SPI.transfer(0x00); + #endif + #if defined(__MBED__) + *data = _spi.write(0x00); + #endif + #else + SPDR = 0x00; + waitspi(); + *data = SPDR; + #endif + data++; + } + //*data='\0'; + CSPASSIVE; +} + +void +Enc28J60Network::writeBuffer(uint16_t len, uint8_t* data) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::writeBuffer(uint16_t len, uint8_t* data) DEBUG_V3:Function started")); + #endif + CSACTIVE; + // issue write command + #if ENC28J60_USE_SPILIB + #if defined(ARDUINO) + SPI.transfer(ENC28J60_WRITE_BUF_MEM); + #endif + #if defined(__MBED__) + _spi.write(ENC28J60_WRITE_BUF_MEM); + #endif + #else + SPDR = ENC28J60_WRITE_BUF_MEM; + waitspi(); + #endif + while(len) + { + len--; + // write data + #if ENC28J60_USE_SPILIB + #if defined(ARDUINO) + SPI.transfer(*data); + #endif + #if defined(__MBED__) + _spi.write(*data); + #endif + data++; + #else + SPDR = *data; + data++; + waitspi(); + #endif + } + CSPASSIVE; +} + +void +Enc28J60Network::setBank(uint8_t address) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::setBank(uint8_t address) DEBUG_V3:Function started")); + #endif + // set the bank (if needed) + if((address & BANK_MASK) != bank) + { + // set the bank + writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, (ECON1_BSEL1|ECON1_BSEL0)); + writeOp(ENC28J60_BIT_FIELD_SET, ECON1, (address & BANK_MASK)>>5); + bank = (address & BANK_MASK); + } +} + +uint8_t +Enc28J60Network::readReg(uint8_t address) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::readReg(uint8_t address) DEBUG_V3:Function started")); + #endif + // set the bank + setBank(address); + // do the read + return readOp(ENC28J60_READ_CTRL_REG, address); +} + +void +Enc28J60Network::writeReg(uint8_t address, uint8_t data) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::writeReg(uint8_t address, uint8_t data) DEBUG_V3:Function started")); + #endif + // set the bank + setBank(address); + // do the write + writeOp(ENC28J60_WRITE_CTRL_REG, address, data); +} + +void +Enc28J60Network::writeRegPair(uint8_t address, uint16_t data) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::writeRegPair(uint8_t address, uint16_t data) DEBUG_V3:Function started")); + #endif + // set the bank + setBank(address); + // do the write + writeOp(ENC28J60_WRITE_CTRL_REG, address, (data&0xFF)); + writeOp(ENC28J60_WRITE_CTRL_REG, address+1, (data) >> 8); +} + +void +Enc28J60Network::phyWrite(uint8_t address, uint16_t data) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::phyWrite(uint8_t address, uint16_t data) DEBUG_V3:Function started")); + #endif + unsigned int timeout = 15; + // set the PHY register address + writeReg(MIREGADR, address); + // write the PHY data + writeRegPair(MIWRL, data); + // wait until the PHY write completes + while (readReg(MISTAT) & MISTAT_BUSY) + { + delay(10); + #if defined(ESP8266) + wdt_reset(); + #endif + if (--timeout == 0) + { + #if ACTLOGLEVEL>=LOG_ERR + LogObject.uart_send_strln(F("Enc28J60Network::phyWrite ERROR:TIMEOUT !!")); + #endif + return; + } + } +} + +uint16_t +Enc28J60Network::phyRead(uint8_t address) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::phyRead(uint8_t address) DEBUG_V3:Function started")); + #endif + unsigned int timeout = 15; + writeReg(MIREGADR,address); + writeReg(MICMD, MICMD_MIIRD); + // wait until the PHY read completes + while(readReg(MISTAT) & MISTAT_BUSY) + { + delay(10); + #if defined(ESP8266) + wdt_reset(); + #endif + if (--timeout == 0) + { + #if ACTLOGLEVEL>=LOG_ERR + LogObject.uart_send_strln(F("Enc28J60Network::phyRead ERROR:TIMEOUT !!")); + #endif + return 0; + } + } + writeReg(MICMD, 0); + return (readReg(MIRDL) | readReg(MIRDH) << 8); +} + +void +Enc28J60Network::clkout(uint8_t clk) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::clkout(uint8_t clk) DEBUG_V3:Function started")); + #endif + //setup clkout: 2 is 12.5MHz: + writeReg(ECOCON, clk & 0x7); +} + +uint16_t +Enc28J60Network::chksum(uint16_t sum, memhandle handle, memaddress pos, uint16_t len) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::chksum(uint16_t sum, memhandle handle, memaddress pos, uint16_t len) DEBUG_V3:Function started")); + #endif + uint16_t t; + len = setReadPtr(handle, pos, len)-1; + CSACTIVE; + // issue read command + #if ENC28J60_USE_SPILIB + #if defined(ARDUINO) + SPI.transfer(ENC28J60_READ_BUF_MEM); + #endif + #if defined(__MBED__) + _spi.write(ENC28J60_READ_BUF_MEM); + #endif + #else + SPDR = ENC28J60_READ_BUF_MEM; + waitspi(); + #endif + uint16_t i; + for (i = 0; i < len; i+=2) + { + // read data + #if ENC28J60_USE_SPILIB + #if defined(ARDUINO) + t = SPI.transfer(0x00) << 8; + t += SPI.transfer(0x00); + #endif + #if defined(__MBED__) + t = _spi.write(0x00) << 8; + t += _spi.write(0x00); + #endif + #else + SPDR = 0x00; + waitspi(); + t = SPDR << 8; + SPDR = 0x00; + waitspi(); + t += SPDR; + #endif + sum += t; + if(sum < t) + { + sum++; /* carry */ + } + } + if(i == len) + { + #if ENC28J60_USE_SPILIB + #if defined(ARDUINO) + t = (SPI.transfer(0x00) << 8) + 0; + #endif + #if defined(__MBED__) + t = (_spi.write(0x00) << 8) + 0; + #endif + #else + SPDR = 0x00; + waitspi(); + t = (SPDR << 8) + 0; + #endif + sum += t; + if(sum < t) + { + sum++; /* carry */ + } + } + CSPASSIVE; + + /* Return sum in host byte order. */ + return sum; +} + +void +Enc28J60Network::powerOff(void) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::powerOff(void) DEBUG_V3:Function started")); + #endif + writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_RXEN); + delay(50); + writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_VRPS); + delay(50); + writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PWRSV); +} + +void +Enc28J60Network::powerOn(void) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::powerOn(void) DEBUG_V3:Function started")); + #endif + writeOp(ENC28J60_BIT_FIELD_CLR, ECON2, ECON2_PWRSV); + delay(50); + writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN); + delay(50); +} + +// read erevid from object: +uint8_t +Enc28J60Network::geterevid(void) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_str(F("Enc28J60Network::geterevid(void) DEBUG_V3:Function started and return:")); + LogObject.uart_send_decln(erevid); + #endif + return(erevid); +} + +// read the phstat2 of the chip: +uint16_t +Enc28J60Network::PhyStatus(void) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_str(F("Enc28J60Network::PhyStatus(void) DEBUG_V3:Function started")); + LogObject.uart_send_decln(erevid); + #endif + uint16_t phstat2; + phstat2=phyRead(PHSTAT2); + if ((phstat2 & 0x20) > 0) {phstat2=phstat2 &0x100;} + phstat2=(phstat2 & 0xFF00) | erevid; + if ((phstat2 & 0x8000) > 0) {phstat2=0;} + return phstat2; +} + +bool +Enc28J60Network::linkStatus(void) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::linkStatus(void) DEBUG_V3:Function started")); + #endif + return (phyRead(PHSTAT2) & 0x0400) > 0; +} + +Enc28J60Network Enc28J60; diff --git a/LibraryPatches/UIPEthernet/utility/Enc28J60Network.h b/LibraryPatches/UIPEthernet/utility/Enc28J60Network.h new file mode 100644 index 0000000..8f5e446 --- /dev/null +++ b/LibraryPatches/UIPEthernet/utility/Enc28J60Network.h @@ -0,0 +1,331 @@ +/* + Enc28J60NetworkClass.h + UIPEthernet network driver for Microchip ENC28J60 Ethernet Interface. + + Copyright (c) 2013 Norbert Truchsess + All rights reserved. + + inspired by enc28j60.c file from the AVRlib library by Pascal Stang. + For AVRlib See http://www.procyonengineering.com/ + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#ifndef Enc28J60Network_H_ +#define Enc28J60Network_H_ + +#if defined(ESP32) + //pin SS already defined in ESP32 as pin 5, don't use this as conflict with SPIFFS, EEPROM, etc. + // Use in GPIO13 + #warning ENC28J60Network.h => use ESP32, change ENC28J60_CONTROL_CS/SS_PIN_DEFAULT to GPIO13, MOSI(23), MISO(19), SCK(18) + #define ENC28J60_CONTROL_CS 13 +#endif + +// KH, For nRF52 +#if ( defined(NRF52840_FEATHER) || defined(NRF52832_FEATHER) || defined(NRF52_SERIES) || defined(ARDUINO_NRF52_ADAFRUIT) || \ + defined(NRF52840_FEATHER_SENSE) || defined(NRF52840_ITSYBITSY) || defined(NRF52840_CIRCUITPLAY) || defined(NRF52840_CLUE) || \ + defined(NRF52840_METRO) || defined(NRF52840_PCA10056) || defined(PARTICLE_XENON) || defined(NINA_B302_ublox) || defined(NINA_B112_ublox) ) + #include + #define ENC28J60_USE_SPILIB 1 + + #ifndef USE_THIS_SS_PIN + // default to pin 10 + #define ENC28J60_CONTROL_CS 10 + #else + #warning Using USE_THIS_SS_PIN in Enc28J60Network.h for nRF52 + #define ENC28J60_CONTROL_CS USE_THIS_SS_PIN + #endif +#endif + +// KH, For SAMD21/SAMD51 +#if ( defined(ARDUINO_SAMD_ZERO) || defined(ARDUINO_SAMD_MKR1000) || defined(ARDUINO_SAMD_MKRWIFI1010) \ + || defined(ARDUINO_SAMD_NANO_33_IOT) || defined(ARDUINO_SAMD_MKRFox1200) || defined(ARDUINO_SAMD_MKRWAN1300) || defined(ARDUINO_SAMD_MKRWAN1310) \ + || defined(ARDUINO_SAMD_MKRGSM1400) || defined(ARDUINO_SAMD_MKRNB1500) || defined(ARDUINO_SAMD_MKRVIDOR4000) || defined(__SAMD21G18A__) \ + || defined(ARDUINO_SAMD_CIRCUITPLAYGROUND_EXPRESS) || defined(__SAMD21E18A__) || defined(__SAMD51__) || defined(__SAMD51J20A__) || defined(__SAMD51J19A__) \ + || defined(__SAMD51G19A__) || defined(__SAMD51P19A__) || defined(__SAMD21G18A__) ) + #include + #define ENC28J60_USE_SPILIB 1 + + #ifndef USE_THIS_SS_PIN + // default to pin 10 + #define ENC28J60_CONTROL_CS 10 + #else + #warning Using USE_THIS_SS_PIN in Enc28J60Network.h for SAMD + #define ENC28J60_CONTROL_CS USE_THIS_SS_PIN + #endif +#endif + +#include "mempool.h" +#if defined(__MBED__) + #include + //UIPEthernet(SPI_MOSI, SPI_MISO, SPI_SCK, SPI_CS); + #if defined(TARGET_LPC1768) + #define SPI_MOSI p11 + #define SPI_MISO p12 + #define SPI_SCK p13 + #define SPI_CS p8 + #elif defined(TARGET_LPC1114) + #define SPI_MOSI dp2 + #define SPI_MISO dp1 + #define SPI_SCK dp6 + #define SPI_CS dp25 + #elif defined(TARGET_LPC11U68) + #define SPI_MOSI P0_9 + #define SPI_MISO P0_8 + #define SPI_SCK P1_29 + #define SPI_CS P0_2 + #elif defined(TARGET_NUCLEO_F103RB) || defined(TARGET_NUCLEO_L152RE) || defined(TARGET_NUCLEO_F030R8) \ + || defined(TARGET_NUCLEO_F401RE) || defined(TARGET_NUCLEO_F302R8) || defined(TARGET_NUCLEO_L053R8) \ + || defined(TARGET_NUCLEO_F411RE) || defined(TARGET_NUCLEO_F334R8) || defined(TARGET_NUCLEO_F072RB) \ + || defined(TARGET_NUCLEO_F091RC) || defined(TARGET_NUCLEO_F303RE) || defined(TARGET_NUCLEO_F070RB) + #define SPI_MOSI D4 + #define SPI_MISO D5 + #define SPI_SCK D3 + #define SPI_CS D2 + #endif + #define ENC28J60_CONTROL_CS SPI_CS +#endif + +#if defined(STM32F3) || defined(STM32F2) //This is workaround for stm32duino STM32F2, and adafruit wiced feather STM32F2 + #define BOARD_SPI1_NSS_PIN PA4 + #define BOARD_SPI1_SCK_PIN PA5 + #define BOARD_SPI1_MISO_PIN PA6 + #define BOARD_SPI1_MOSI_PIN PA7 +#endif //This is workaround for stm32duino STM32F3, and adafruit wiced feather STM32F2 + +#if defined(BOARD_discovery_f4) + #define __STM32F4__ +#endif +#if defined(__MK20DX128__) || defined(__MKL26Z64__) + #include +#endif + +#if !defined(ENC28J60_CONTROL_CS) + #if defined(__AVR__) || defined(ESP8266) || defined(__RFduino__) + // Arduino Uno (__AVR__) SS defined to pin 10 + // Arduino Leonardo (ARDUINO_AVR_LEONARDO) SS defined to LED_BUILTIN_RX (17) + // Arduino Mega(__AVR_ATmega2560__) SS defined to pin 53 + // ESP8266 (ESP8266) SS defined to pin 15 + #if defined(ARDUINO_AVR_LEONARDO) || defined(ARDUINO_AVR_MICRO) + #define ENC28J60_CONTROL_CS PIN_A10 + #warning "Using LEONARDO borad PIN_A10 for ENC28J60_CONTROL_CS. Use UIPEthernet::init(uint8_t) to change it." + #else + #define ENC28J60_CONTROL_CS SS + #endif + #elif defined(ARDUINO_ARCH_AMEBA) //Defined SS to pin 10 + #define ENC28J60_CONTROL_CS SS //PC_0 A5 10 + #elif defined(ARDUINO_ARCH_SAM) + // Arduino Due (ARDUINO_ARCH_SAM) BOARD_SPI_DEFAULT_SS (SS3) defined to pin 78 + //#define ENC28J60_CONTROL_CS BOARD_SPI_DEFAULT_SS + #define ENC28J60_CONTROL_CS BOARD_SPI_SS0 + #elif defined(ARDUINO_ARCH_SAMD) + #define ENC28J60_CONTROL_CS SS + #elif defined(__ARDUINO_ARC__) //Intel ARC32 Genuino 101 + #define ENC28J60_CONTROL_CS SS + #elif defined(__RFduino__) //RFduino + #define ENC28J60_CONTROL_CS SS + #elif defined(ARDUINO_ARCH_STM32) // STM32duino core + #define ENC28J60_CONTROL_CS SS + #elif defined(ARDUINO_ARCH_ESP32) // arduino-esp32 + #define ENC28J60_CONTROL_CS SS + #elif defined(STM32_MCU_SERIES) || defined(__STM32F1__) || defined(__STM32F3__) || defined(STM32F3) || defined(__STM32F4__) || defined(STM32F2) + #if defined(BOARD_SPI1_NSS_PIN) + #define ENC28J60_CONTROL_CS BOARD_SPI1_NSS_PIN + #elif defined(ARDUINO_STM32F4_NETDUINO2PLUS) + #define ENC28J60_CONTROL_CS PC8 + #else + #define ENC28J60_CONTROL_CS SPI.nssPin() + //#define ENC28J60_CONTROL_CS PA4 + #endif + #elif defined(__MK20DX128__) || defined(__MKL26Z64__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) || defined(__IMXRT1062__) + #define ENC28J60_CONTROL_CS PIN_SPI_SS + #endif +#endif +#if !defined(ENC28J60_CONTROL_CS) + #warning "Default ENC28J60_CONTROL_CS could not be defined! Use UIPEthernet::init(uint8_t) to set it." + #define ENC28J60_CONTROL_CS 0 +#endif + +extern uint8_t ENC28J60ControlCS; + +#if !defined(SPI_MOSI) + #if defined(__AVR__) || defined(ESP8266) || defined(__RFduino__) + #define SPI_MOSI MOSI + #elif defined(ARDUINO_ARCH_AMEBA) + #define SPI_MOSI 11 //PC_2 + #elif defined(ARDUINO_ARCH_SAM) || defined(ARDUINO_ARCH_SAMD) + #define SPI_MOSI PIN_SPI_MOSI + #elif defined(__ARDUINO_ARC__) //Intel ARC32 Genuino 101 + #define SPI_MOSI MOSI + #elif defined(__RFduino__) //RFduino + #define SPI_MOSI MOSI + #elif defined(ARDUINO_ARCH_STM32) // STM32duino core + #define SPI_MOSI MOSI + #elif defined(ARDUINO_ARCH_ESP32) // arduino-esp32 + #define SPI_MOSI MOSI + #elif defined(STM32_MCU_SERIES) || defined(__STM32F1__) || defined(__STM32F3__) || defined(STM32F3) || defined(__STM32F4__) || defined(STM32F2) + #if defined(BOARD_SPI1_MOSI_PIN) + #define SPI_MOSI BOARD_SPI1_MOSI_PIN + #else + #define SPI_MOSI SPI.mosiPin() + #endif + #elif defined(__MK20DX128__) || defined(__MKL26Z64__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) || defined(__IMXRT1062__) + #define SPI_MOSI PIN_SPI_MOSI + #endif +#endif +#if !defined(SPI_MOSI) + //#error "Not defined SPI_MOSI!" + #define SPI_MOSI MOSI +#endif + +#if !defined(SPI_MISO) + #if defined(__AVR__) || defined(ESP8266) || defined(__RFduino__) + #define SPI_MISO MISO + #elif defined(ARDUINO_ARCH_AMEBA) + #define SPI_MISO 12 //PC_3 + #elif defined(ARDUINO_ARCH_SAM) || defined(ARDUINO_ARCH_SAMD) + #define SPI_MISO PIN_SPI_MISO + #elif defined(__ARDUINO_ARC__) //Intel ARC32 Genuino 101 + #define SPI_MISO MISO + #elif defined(__RFduino__) //RFduino + #define SPI_MISO MISO + #elif defined(ARDUINO_ARCH_STM32) // STM32duino core + #define SPI_MISO MISO + #elif defined(ARDUINO_ARCH_ESP32) // arduino-esp32 + #define SPI_MISO MISO + #elif defined(STM32_MCU_SERIES) || defined(__STM32F1__) || defined(__STM32F3__) || defined(STM32F3) || defined(__STM32F4__) || defined(STM32F2) + #if defined(BOARD_SPI1_MISO_PIN) + #define SPI_MISO BOARD_SPI1_MISO_PIN + #else + #define SPI_MISO SPI.misoPin() + #endif + #elif defined(__MK20DX128__) || defined(__MKL26Z64__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) || defined(__IMXRT1062__) + #define SPI_MISO PIN_SPI_MISO + #endif +#endif +#if !defined(SPI_MISO) + //#error "Not defined SPI_MISO!" + #define SPI_MISO MISO +#endif +#if !defined(SPI_SCK) + #if defined(__AVR__) || defined(ESP8266) || defined(__RFduino__) + #define SPI_SCK SCK + #elif defined(ARDUINO_ARCH_AMEBA) + #define SPI_SCK 13 //PC_1 A4 + #elif defined(ARDUINO_ARCH_SAM) || defined(ARDUINO_ARCH_SAMD) + #define SPI_SCK PIN_SPI_SCK + #elif defined(__ARDUINO_ARC__) //Intel ARC32 Genuino 101 + #define SPI_SCK SCK + #elif defined(__RFduino__) //RFduino + #define SPI_SCK SCK + #elif defined(ARDUINO_ARCH_STM32) // STM32duino core + #define SPI_SCK SCK + #elif defined(ARDUINO_ARCH_ESP32) // arduino-esp32 + #define SPI_SCK SCK + #elif defined(STM32_MCU_SERIES) || defined(__STM32F1__) || defined(__STM32F3__) || defined(STM32F3) || defined(__STM32F4__) || defined(STM32F2) + #if defined(BOARD_SPI1_SCK_PIN) + #define SPI_SCK BOARD_SPI1_SCK_PIN + #else + #define SPI_SCK SPI.sckPin() + #endif + #elif defined(__MK20DX128__) || defined(__MKL26Z64__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) || defined(__IMXRT1062__) + #define SPI_SCK PIN_SPI_SCK + #endif +#endif + +#if !defined(SPI_SCK) + // For nRF52 + //#error "Not defined SPI_SCK!" + #define SPI_SCK SCK +#endif + +#if defined(__MBED__) || defined(ARDUINO_ARCH_SAM) || defined(ARDUINO_ARCH_SAMD) || defined(__ARDUINO_ARC__) || defined(__STM32F1__) || defined(__STM32F3__) || defined(STM32F3) || defined(__STM32F4__) || defined(STM32F2) || defined(ESP8266) || defined(ARDUINO_ARCH_AMEBA) || defined(__MK20DX128__) || defined(__MKL26Z64__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) || defined(__IMXRT1062__) || defined(__RFduino__) || defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_MEGAAVR) + #if defined(ARDUINO) && defined(STM32F3) + #include "HardwareSPI.h" + #else + #include + #endif + #define ENC28J60_USE_SPILIB 1 +#endif + + + +#define UIP_RECEIVEBUFFERHANDLE 0xff + +/* + * Empfangen von ip-header, arp etc... + * wenn tcp/udp -> tcp/udp-callback -> assign new packet to connection + */ + +#define TX_COLLISION_RETRY_COUNT 10 + +class Enc28J60Network : public MemoryPool +{ + +private: + static uint16_t nextPacketPtr; + static uint8_t bank; + static uint8_t erevid; + + static struct memblock receivePkt; + + static bool broadcast_enabled; //!< True if broadcasts enabled (used to allow temporary disable of broadcast for DHCP or other internal functions) + + static uint8_t readOp(uint8_t op, uint8_t address); + static void writeOp(uint8_t op, uint8_t address, uint8_t data); + static uint16_t setReadPtr(memhandle handle, memaddress position, uint16_t len); + static void setERXRDPT(void); + static void readBuffer(uint16_t len, uint8_t* data); + static void writeBuffer(uint16_t len, uint8_t* data); + static uint8_t readByte(uint16_t addr); + static void writeByte(uint16_t addr, uint8_t data); + static void setBank(uint8_t address); + static uint8_t readReg(uint8_t address); + static void writeReg(uint8_t address, uint8_t data); + static void writeRegPair(uint8_t address, uint16_t data); + static void phyWrite(uint8_t address, uint16_t data); + static uint16_t phyRead(uint8_t address); + static void clkout(uint8_t clk); + + static void enableBroadcast (bool temporary); + static void disableBroadcast (bool temporary); + static void enableMulticast (void); + static void disableMulticast (void); + + static uint8_t readRegByte (uint8_t address); + static void writeRegByte (uint8_t address, uint8_t data); + + friend void enc28J60_mempool_block_move_callback(memaddress,memaddress,memaddress); + +public: + + void powerOn(void); + void powerOff(void); + static uint8_t geterevid(void); + uint16_t PhyStatus(void); + static bool linkStatus(void); + + static void init(uint8_t* macaddr); + static memhandle receivePacket(void); + static void freePacket(void); + static memaddress blockSize(memhandle handle); + static void sendPacket(memhandle handle); + static uint16_t readPacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len); + static uint16_t writePacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len); + static void copyPacket(memhandle dest, memaddress dest_pos, memhandle src, memaddress src_pos, uint16_t len); + static uint16_t chksum(uint16_t sum, memhandle handle, memaddress pos, uint16_t len); +}; + +extern Enc28J60Network Enc28J60; +#endif /* Enc28J60NetworkClass_H_ */ diff --git a/Packages_Patches/arduino/hardware/sam/1.6.12/platform.txt b/Packages_Patches/arduino/hardware/sam/1.6.12/platform.txt new file mode 100644 index 0000000..52a7aa1 --- /dev/null +++ b/Packages_Patches/arduino/hardware/sam/1.6.12/platform.txt @@ -0,0 +1,110 @@ + +# Arduino SAM Core and platform. +# ------------------------------ +# +# For more info: +# https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5-3rd-party-Hardware-specification + +name=Arduino ARM (32-bits) Boards +version=1.6.12 + +# SAM3 compile variables +# ---------------------- + +compiler.warning_flags=-w +compiler.warning_flags.none=-w +compiler.warning_flags.default= +compiler.warning_flags.more=-Wall +compiler.warning_flags.all=-Wall -Wextra + +compiler.path={runtime.tools.arm-none-eabi-gcc-4.8.3-2014q1.path}/bin/ +compiler.c.cmd=arm-none-eabi-gcc +compiler.c.flags=-c -g -Os {compiler.warning_flags} -std=gnu11 -ffunction-sections -fdata-sections -nostdlib --param max-inline-insns-single=500 -Dprintf=iprintf -MMD +# KH, Error here to use gcc, mjust use g++ +#compiler.c.elf.cmd=arm-none-eabi-gcc +compiler.c.elf.cmd=arm-none-eabi-g++ +compiler.c.elf.flags=-Os -Wl,--gc-sections +compiler.S.cmd=arm-none-eabi-gcc +compiler.S.flags=-c -g -x assembler-with-cpp -MMD +compiler.cpp.cmd=arm-none-eabi-g++ +compiler.cpp.flags=-c -g -Os {compiler.warning_flags} -std=gnu++11 -ffunction-sections -fdata-sections -nostdlib -fno-threadsafe-statics --param max-inline-insns-single=500 -fno-rtti -fno-exceptions -MMD +compiler.ar.cmd=arm-none-eabi-ar +compiler.ar.flags=rcs +compiler.objcopy.cmd=arm-none-eabi-objcopy +compiler.objcopy.eep.flags=-O ihex -j .eeprom --set-section-flags=.eeprom=alloc,load --no-change-warnings --change-section-lma .eeprom=0 +compiler.elf2hex.flags=-O binary +compiler.elf2hex.cmd=arm-none-eabi-objcopy +compiler.ldflags= +compiler.size.cmd=arm-none-eabi-size +compiler.define=-DARDUINO= +compiler.combine.flags=-u _sbrk -u link -u _close -u _fstat -u _isatty -u _lseek -u _read -u _write -u _exit -u kill -u _getpid + +# This can be overridden in boards.txt +build.extra_flags= + +# These can be overridden in platform.local.txt +compiler.c.extra_flags= +compiler.c.elf.extra_flags= +compiler.cpp.extra_flags= +compiler.S.extra_flags= +compiler.ar.extra_flags= +compiler.elf2hex.extra_flags= + + +compiler.libsam.c.flags="-I{build.system.path}/libsam" "-I{build.system.path}/CMSIS/CMSIS/Include/" "-I{build.system.path}/CMSIS/Device/ATMEL/" + +# USB Flags +# --------- +build.usb_flags=-DUSB_VID={build.vid} -DUSB_PID={build.pid} -DUSBCON '-DUSB_MANUFACTURER={build.usb_manufacturer}' '-DUSB_PRODUCT={build.usb_product}' + +# Default usb manufacturer will be replaced at compile time using +# numeric vendor ID if available or by board's specific value. +build.usb_manufacturer="Unknown" + + +# SAM3 compile patterns +# --------------------- + +## Compile c files +recipe.c.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.c.flags} -mcpu={build.mcu} -mthumb -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.c.extra_flags} {build.extra_flags} {compiler.libsam.c.flags} {includes} "{source_file}" -o "{object_file}" + +## Compile c++ files +recipe.cpp.o.pattern="{compiler.path}{compiler.cpp.cmd}" {compiler.cpp.flags} -mcpu={build.mcu} -mthumb -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.cpp.extra_flags} {build.extra_flags} {compiler.libsam.c.flags} {includes} "{source_file}" -o "{object_file}" + +## Compile S files +recipe.S.o.pattern="{compiler.path}{compiler.S.cmd}" {compiler.S.flags} -mcpu={build.mcu} -mthumb -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.S.extra_flags} {build.extra_flags} {compiler.libsam.c.flags} {includes} "{source_file}" -o "{object_file}" + +## Create archives +# archive_file_path is needed for backwards compatibility with IDE 1.6.5 or older, IDE 1.6.6 or newer overrides this value +archive_file_path={build.path}/{archive_file} +recipe.ar.pattern="{compiler.path}{compiler.ar.cmd}" {compiler.ar.flags} {compiler.ar.extra_flags} "{archive_file_path}" "{object_file}" + +## Combine gc-sections, archives, and objects +recipe.c.combine.pattern="{compiler.path}{compiler.c.elf.cmd}" -mcpu={build.mcu} -mthumb {compiler.c.elf.flags} "-T{build.variant.path}/{build.ldscript}" "-Wl,-Map,{build.path}/{build.project_name}.map" {compiler.c.elf.extra_flags} -o "{build.path}/{build.project_name}.elf" "-L{build.path}" -Wl,--cref -Wl,--check-sections -Wl,--gc-sections -Wl,--entry=Reset_Handler -Wl,--unresolved-symbols=report-all -Wl,--warn-common -Wl,--warn-section-align -Wl,--start-group {compiler.combine.flags} {object_files} "{build.variant.path}/{build.variant_system_lib}" "{build.path}/{archive_file}" -Wl,--end-group -lm -lgcc + +## Create output (.bin file) +recipe.objcopy.bin.pattern="{compiler.path}{compiler.elf2hex.cmd}" {compiler.elf2hex.flags} {compiler.elf2hex.extra_flags} "{build.path}/{build.project_name}.elf" "{build.path}/{build.project_name}.bin" + +## Save hex +recipe.output.tmp_file={build.project_name}.bin +recipe.output.save_file={build.project_name}.{build.variant}.bin + +## Compute size +recipe.size.pattern="{compiler.path}{compiler.size.cmd}" -A "{build.path}/{build.project_name}.elf" +recipe.size.regex=\.text\s+([0-9]+).* + + +# SAM3 Uploader tools +# ------------------- + +# BOSSA +tools.bossac.path={runtime.tools.bossac.path} +tools.bossac.cmd=bossac +tools.bossac.cmd.windows=bossac.exe + +tools.bossac.upload.params.verbose=-i -d +tools.bossac.upload.params.quiet= +tools.bossac.upload.params.verify=-v +tools.bossac.upload.pattern="{path}/{cmd}" {upload.verbose} --port={serial.port.file} -U {upload.native_usb} -e -w {upload.verify} -b "{build.path}/{build.project_name}.bin" -R + +tools.bossac_remote.upload.pattern=/usr/bin/run-bossac {upload.verbose} --port=ttyATH0 -U {upload.native_usb} -e -w -v -b /tmp/sketch.bin -R diff --git a/README.md b/README.md new file mode 100644 index 0000000..bbc8039 --- /dev/null +++ b/README.md @@ -0,0 +1,1043 @@ +# SAMDUE_TimerInterrupt Library + +[![arduino-library-badge](https://www.ardu-badge.com/badge/SAMDUE_TimerInterrupt.svg?)](https://www.ardu-badge.com/SAMDUE_TimerInterrupt) +[![GitHub release](https://img.shields.io/github/release/khoih-prog/SAMDUE_TimerInterrupt.svg)](https://github.com/khoih-prog/SAMDUE_TimerInterrupt/releases) +[![GitHub](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/khoih-prog/SAMDUE_TimerInterrupt/blob/master/LICENSE) +[![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](#Contributing) +[![GitHub issues](https://img.shields.io/github/issues/khoih-prog/SAMDUE_TimerInterrupt.svg)](http://github.com/khoih-prog/SAMDUE_TimerInterrupt/issues) + +--- +--- + +## Features + +This library enables you to use Interrupt from Hardware Timers on an SAM-DUE-based board. + +As **Hardware Timers are rare, and very precious assets** of any board, this library now enables you to use up to **16 ISR-based Timers, while consuming only 1 Hardware Timer**. Timers' interval is very long (**ulong millisecs**). + +### Why do we need this Hardware Timer Interrupt? + +Imagine you have a system with a **mission-critical** function, measuring water level and control the sump pump or doing something much more important. You normally use a software timer to poll, or even place the function in loop(). But what if another function is **blocking** the loop() or setup(). + +So your function **might not be executed on-time or not at all, and the result would be disastrous.** + +You'd prefer to have your function called, no matter what happening with other functions (busy loop, bug, etc.). + +The correct choice is to use a Hardware Timer with **Interrupt** to call your function. + +These hardware timers, using interrupt, still work even if other functions are blocking. Moreover, they are much more **precise** (certainly depending on clock frequency accuracy) than other software timers using millis() or micros(). That's necessary if you need to measure some data requiring better accuracy. + +Functions using normal software timers, relying on loop() and calling millis(), won't work if the loop() or setup() is blocked by certain operation. For example, certain function is blocking while it's connecting to WiFi or some services. + +The catch is **your function is now part of an ISR (Interrupt Service Routine), and must be lean / mean, and follow certain rules.** More to read on: + +[**HOWTO Attach Interrupt**](https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/) + +--- + +#### Important Notes: + +1. Inside the attached function, **delay() won’t work and the value returned by millis() will not increment.** Serial data received while in the function may be lost. You should declare as **volatile any variables that you modify within the attached function.** + +2. Typically global variables are used to pass data between an ISR and the main program. To make sure variables shared between an ISR and the main program are updated correctly, declare them as volatile. + +--- +--- + +### Releases v1.0.1 + +1. Permit up to 16 super-long-time, super-accurate ISR-based timers to avoid being blocked +2. Using cpp code besides Impl.h code to use if Multiple-Definition linker error3. +3. Add complicated example [ISR_16_Timers_Array](examples/ISR_16_Timers_Array) utilizing and demonstrating the full usage of 16 independent ISR Timers based on just 1 Hardware Timer. + +#### Supported Boards + + - **Arduino SAM DUE**. + +--- +--- + +## Prerequisite + + 1. [`Arduino IDE 1.8.13+` for Arduino](https://www.arduino.cc/en/Main/Software) + 2. [`Arduino SAM DUE core v1.6.12+`](https://www.arduino.cc/en/Guide/ArduinoDue) for SAM DUE ARM Cortex-M3 boards + 3. [`Blynk library 0.6.1+`](https://github.com/blynkkk/blynk-library) to use with certain example. + 4. To use with certain example, depending on which Ethernet card you're using: + - [`Ethernet library v2.0.0+`](https://www.arduino.cc/en/Reference/Ethernet) for W5100, W5200 and W5500. + - [`Ethernet2 library v1.0.4+`](https://github.com/khoih-prog/Ethernet2) for W5500 (Deprecated, use Arduino Ethernet library). + - [`Ethernet3 library v1.5.3+`](https://github.com/sstaub/Ethernet3) for W5500/WIZ550io/WIZ850io/USR-ES1 with Wiznet W5500 chip. + - [`EthernetLarge library v2.0.0+`](https://github.com/OPEnSLab-OSU/EthernetLarge) for W5100, W5200 and W5500. ***Ready*** from v1.0.1. + - [`UIPEthernet library v2.0.9+`](https://github.com/UIPEthernet/UIPEthernet) for ENC28J60. + 5. To use with certain example + - [`SimpleTimer library`](https://github.com/schinken/SimpleTimer) for [ISR_16_Timers_Array example](examples/ISR_16_Timers_Array). + +--- +--- + +## Installation + +### Use Arduino Library Manager + +The best and easiest way is to use `Arduino Library Manager`. Search for [**SAMDUE_TimerInterrupt**](https://github.com/khoih-prog/SAMDUE_TimerInterrupt), then select / install the latest version. +You can also use this link [![arduino-library-badge](https://www.ardu-badge.com/badge/SAMDUE_TimerInterrupt.svg?)](https://www.ardu-badge.com/SAMDUE_TimerInterrupt) for more detailed instructions. + +### Manual Install + +Another way to install is to: + +1. Navigate to [**SAMDUE_TimerInterrupt**](https://github.com/khoih-prog/SAMDUE_TimerInterrupt) page. +2. Download the latest release `SAMDUE_TimerInterrupt-master.zip`. +3. Extract the zip file to `SAMDUE_TimerInterrupt-master` directory +4. Copy whole `SAMDUE_TimerInterrupt-master` folder to Arduino libraries' directory such as `~/Arduino/libraries/`. + +### VS Code & PlatformIO + +1. Install [VS Code](https://code.visualstudio.com/) +2. Install [PlatformIO](https://platformio.org/platformio-ide) +3. Install [**SAMDUE_TimerInterrupt** library](https://platformio.org/lib/show/11428/SAMDUE_TimerInterrupt) by using [Library Manager](https://platformio.org/lib/show/11428/SAMDUE_TimerInterrupt/installation). Search for **SAMDUE_TimerInterrupt** in [Platform.io Author's Libraries](https://platformio.org/lib/search?query=author:%22Khoi%20Hoang%22) +4. Use included [platformio.ini](platformio/platformio.ini) file from examples to ensure that all dependent libraries will installed automatically. Please visit documentation for the other options and examples at [Project Configuration File](https://docs.platformio.org/page/projectconf.html) + +--- +--- + +### Packages' Patches + + 1. **To be able to compile and run on SAM DUE boards**, you have to copy the whole [SAM DUE](Packages_Patches/arduino/hardware/sam/1.6.12) directory into Arduino sam directory (~/.arduino15/packages/arduino/hardware/sam/1.6.12). + +Supposing the Arduino SAM core version is 1.6.12. This file must be copied into the directory: + +- `~/.arduino15/packages/arduino/hardware/sam/1.6.12/platform.txt` + +Whenever a new version is installed, remember to copy this file into the new version directory. For example, new version is x.yy.zz +This file must be copied into the directory: + +- `~/.arduino15/packages/arduino/hardware/sam/x.yy.zz/platform.txt` + +--- +--- + +### Libraries' Patches + +1. If your application requires 2K+ HTML page, the current [`Ethernet library`](https://www.arduino.cc/en/Reference/Ethernet) must be modified if you are using W5200/W5500 Ethernet shields. W5100 is not supported for 2K+ buffer. If you use boards requiring different CS/SS pin for W5x00 Ethernet shield, for example ESP32, ESP8266, nRF52, etc., you also have to modify the following libraries to be able to specify the CS/SS pin correctly. + +2. To fix [`Ethernet library`](https://www.arduino.cc/en/Reference/Ethernet), just copy these following files into the [`Ethernet library`](https://www.arduino.cc/en/Reference/Ethernet) directory to overwrite the old files: +- [Ethernet.h](LibraryPatches/Ethernet/src/Ethernet.h) +- [Ethernet.cpp](LibraryPatches/Ethernet/src/Ethernet.cpp) +- [EthernetServer.cpp](LibraryPatches/Ethernet/src/EthernetServer.cpp) +- [w5100.h](LibraryPatches/Ethernet/src/utility/w5100.h) +- [w5100.cpp](LibraryPatches/Ethernet/src/utility/w5100.cpp) + +3. To fix [`EthernetLarge library`](https://github.com/OPEnSLab-OSU/EthernetLarge), just copy these following files into the [`EthernetLarge library`](https://github.com/OPEnSLab-OSU/EthernetLarge) directory to overwrite the old files: +- [EthernetLarge.h](LibraryPatches/EthernetLarge/src/EthernetLarge.h) +- [EthernetLarge.cpp](LibraryPatches/EthernetLarge/src/EthernetLarge.cpp) +- [EthernetServer.cpp](LibraryPatches/EthernetLarge/src/EthernetServer.cpp) +- [w5100.h](LibraryPatches/EthernetLarge/src/utility/w5100.h) +- [w5100.cpp](LibraryPatches/EthernetLarge/src/utility/w5100.cpp) + +4. To fix [`Ethernet2 library`](https://github.com/khoih-prog/Ethernet2), just copy these following files into the [`Ethernet2 library`](https://github.com/khoih-prog/Ethernet2) directory to overwrite the old files: + +- [Ethernet2.h](LibraryPatches/Ethernet2/src/Ethernet2.h) +- [Ethernet2.cpp](LibraryPatches/Ethernet2/src/Ethernet2.cpp) + +To add UDP Multicast support, necessary for this [**UPnP_Generic library**](https://github.com/khoih-prog/UPnP_Generic): + +- [EthernetUdp2.h](LibraryPatches/Ethernet2/src/EthernetUdp2.h) +- [EthernetUdp2.cpp](LibraryPatches/Ethernet2/src/EthernetUdp2.cpp) + +5. To fix [`Ethernet3 library`](https://github.com/sstaub/Ethernet3), just copy these following files into the [`Ethernet3 library`](https://github.com/sstaub/Ethernet3) directory to overwrite the old files: +- [Ethernet3.h](LibraryPatches/Ethernet3/src/Ethernet3.h) +- [Ethernet3.cpp](LibraryPatches/Ethernet3/src/Ethernet3.cpp) + +6. **To be able to compile and run on nRF52 boards with ENC28J60 using UIPEthernet library**, you have to copy these following files into the UIPEthernet `utility` directory to overwrite the old files: + +- For [UIPEthernet v2.0.8](https://github.com/UIPEthernet/UIPEthernet) + + - [UIPEthernet.h](LibraryPatches/UIPEthernet/UIPEthernet.h) + - [UIPEthernet.cpp](LibraryPatches/UIPEthernet/UIPEthernet.cpp) + - [Enc28J60Network.h](LibraryPatches/UIPEthernet/utility/Enc28J60Network.h) + - [Enc28J60Network.cpp](LibraryPatches/UIPEthernet/utility/Enc28J60Network.cpp) + +- For [UIPEthernet v2.0.9](https://github.com/UIPEthernet/UIPEthernet) + + - [UIPEthernet.h](LibraryPatches/UIPEthernet-2.0.9/UIPEthernet.h) + - [UIPEthernet.cpp](LibraryPatches/UIPEthernet-2.0.9/UIPEthernet.cpp) + - [Enc28J60Network.h](LibraryPatches/UIPEthernet-2.0.9/utility/Enc28J60Network.h) + - [Enc28J60Network.cpp](LibraryPatches/UIPEthernet-2.0.9/utility/Enc28J60Network.cpp) + +7. Check if you need to install the UIPthernet patch [new SAMD core F3/F4 compatibility](https://github.com/UIPEthernet/UIPEthernet/commit/c6d6519a260166b722b9cee5dd1f0fb2682e6782) to avoid errors `#include HardwareSPI.h` on some SAMD boards (Nucleo-32 F303K8, etc.) + +--- +--- + +### HOWTO Fix `Multiple Definitions` Linker Error + +The current library implementation, using **xyz-Impl.h instead of standard xyz.cpp**, possibly creates certain `Multiple Definitions` Linker error in certain use cases. Although it's simple to just modify several lines of code, either in the library or in the application, the library is adding 2 more source directories + +1. **scr_h** for new h-only files +2. **src_cpp** for standard h/cpp files + +besides the standard **src** directory. + +To use the **old standard cpp** way, locate this library' directory, then just + +1. **Delete the all the files in src directory.** +2. **Copy all the files in src_cpp directory into src.** +3. Close then reopen the application code in Arduino IDE, etc. to recompile from scratch. + +To re-use the **new h-only** way, just + +1. **Delete the all the files in src directory.** +2. **Copy the files in src_h directory into src.** +3. Close then reopen the application code in Arduino IDE, etc. to recompile from scratch. + +--- +--- + + +## New from v1.0.1 + +Now with these new `16 ISR-based timers` (while consuming only **1 hardware timer**), the maximum interval is practically unlimited (limited only by unsigned long miliseconds). The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers Therefore, their executions are not blocked by bad-behaving functions / tasks. +This important feature is absolutely necessary for mission-critical tasks. + +The [**ISR_16_Timers_Array**](examples/ISR_16_Timers_Array) and [**ISR_Timer_Complex_Ethernet**](examples/ISR_Timer_Complex_Ethernet) examples will demonstrate the nearly perfect accuracy compared to software timers by printing the actual elapsed millisecs of each type of timers. +Being ISR-based timers, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet and Blynk services. You can also have many `(up to 16)` timers to use. +This non-being-blocked important feature is absolutely necessary for mission-critical tasks. +You'll see blynkTimer Software is blocked while system is connecting to WiFi / Internet / Blynk, as well as by blocking task +in loop(), using delay() function as an example. The elapsed time then is very unaccurate + +--- + +## Supported Boards + +- **SAM DUE** + +--- + +## Usage + +Before using any Timer, you have to make sure the Timer has not been used by any other purpose. + +### 1. Using only Hardware Timer directly + +#### 1.1 Init Hardware Timer + +``` +// Interval in microsecs +attachDueInterrupt(TIMER1_INTERVAL_MS * 1000, TimerHandler1, "ITimer1"); +``` + +#### 1.2 Set Hardware Timer Interval and attach Timer Interrupt Handler function + +``` +void TimerHandler(void) +{ + // Doing something here inside ISR +} + +#define TIMER_INTERVAL_MS 1000 // 1s = 1000ms + +uint16_t attachDueInterrupt(double microseconds, timerCallback callback, const char* TimerName) +{ + DueTimerInterrupt dueTimerInterrupt = DueTimer.getAvailable(); + + dueTimerInterrupt.attachInterruptInterval(microseconds, callback); + + uint16_t timerNumber = dueTimerInterrupt.getTimerNumber(); + + Serial.print(TimerName); + Serial.print(" attached to Timer("); + Serial.print(timerNumber); + Serial.println(")"); + + return timerNumber; +} + +void setup() +{ + .... + + // Interval in microsecs + attachDueInterrupt(TIMER_INTERVAL_MS * 1000, TimerHandler, "ITimer"); +} +``` + +### 2. Using 16 ISR_based Timers from 1 Hardware Timers + + +#### 2.1 Init Hardware Timer and ISR-based Timer + +``` +// Interval in microsecs +attachDueInterrupt(TIMER1_INTERVAL_MS * 1000, TimerHandler1, "ITimer1"); +``` + +#### 2.2 Set Hardware Timer Interval and attach Timer Interrupt Handler functions + +``` +void TimerHandler(void) +{ + ISR_Timer.run(); +} + +#define HW_TIMER_INTERVAL_MS 1L + +#define TIMER_INTERVAL_2S 2000L +#define TIMER_INTERVAL_5S 5000L +#define TIMER_INTERVAL_11S 11000L +#define TIMER_INTERVAL_101S 101000L + +// In SAM DUE, avoid doing something fancy in ISR, for example complex Serial.print with String() argument +// The pure simple Serial.prints here are just for demonstration and testing. Must be eliminate in working environment +// Or you can get this run-time error / crash +void doingSomething2s() +{ + // Doing something here inside ISR +} + +void doingSomething5s() +{ + // Doing something here inside ISR +} + +void doingSomething11s() +{ + // Doing something here inside ISR +} + +void doingSomething101s() +{ + // Doing something here inside ISR +} + +uint16_t attachDueInterrupt(double microseconds, timerCallback callback, const char* TimerName) +{ + DueTimerInterrupt dueTimerInterrupt = DueTimer.getAvailable(); + + dueTimerInterrupt.attachInterruptInterval(microseconds, callback); + + uint16_t timerNumber = dueTimerInterrupt.getTimerNumber(); + + Serial.print(TimerName); + Serial.print(" attached to Timer("); + Serial.print(timerNumber); + Serial.println(")"); + + return timerNumber; +} + +void setup() +{ + .... + + // Interval in microsecs + attachDueInterrupt(HW_TIMER_INTERVAL_MS * 1000, TimerHandler, "ITimer"); + + // Just to demonstrate, don't use too many ISR Timers if not absolutely necessary + // You can use up to 16 timer for each ISR_Timer + ISR_Timer.setInterval(TIMER_INTERVAL_2S, doingSomething2s); + ISR_Timer.setInterval(TIMER_INTERVAL_5S, doingSomething5s); + ISR_Timer.setInterval(TIMER_INTERVAL_11S, doingSomething11s); + ISR_Timer.setInterval(TIMER_INTERVAL_101S, doingSomething101s); +} +``` + +--- +--- + +### Examples: + + 1. [Argument_None](examples/Argument_None) + 2. [ISR_16_Timers_Array](examples/ISR_16_Timers_Array) + 3. [ISR_RPM_Measure](examples/ISR_RPM_Measure) + 4. [ISR_Timer_Complex_Ethernet](examples/ISR_Timer_Complex_Ethernet) + 5. [RPM_Measure](examples/RPM_Measure) + 6. [SwitchDebounce](examples/SwitchDebounce) + 7. [TimerInterruptTest](examples/TimerInterruptTest) + 8. [TimerInterruptLEDDemo](examples/TimerInterruptLEDDemo) + + +--- +--- + +### Example [ISR_16_Timers_Array](examples/ISR_16_Timers_Array) + +``` +#if !( defined(ARDUINO_SAM_DUE) || defined(__SAM3X8E__) ) + #error This code is designed to run on SAM DUE board / platform! Please check your Tools->Board setting. +#endif + +// These define's must be placed at the beginning before #include "SAMDTimerInterrupt.h" +// Don't define SAMDUE_TIMER_INTERRUPT_DEBUG > 0. Only for special ISR debugging only. Can hang the system. +#define SAMDUE_TIMER_INTERRUPT_DEBUG 1 + +#include "SAMDUETimerInterrupt.h" +#include "SAMDUE_ISR_Timer.h" + +#include // https://github.com/schinken/SimpleTimer + +#ifndef LED_BUILTIN + #define LED_BUILTIN 13 +#endif + +#ifndef LED_BLUE + #define LED_BLUE 2 +#endif + +#ifndef LED_RED + #define LED_RED 3 +#endif + +// Resolution for ISR_Timer. Smaller => more precise. +#define HW_TIMER_INTERVAL_US 100L + +volatile uint32_t startMillis = 0; + +// Init SAMDUE_ISR_Timer +// Each SAMDUE_ISR_Timer can service 16 different ISR-based timers +SAMDUE_ISR_Timer ISR_Timer; + +#define LED_TOGGLE_INTERVAL_MS 500L + +void TimerHandler(void) +{ + static bool toggle = false; + static bool started = false; + static int timeRun = 0; + + ISR_Timer.run(); + + // Toggle LED every LED_TOGGLE_INTERVAL_MS = 500ms = 0.5s + if (++timeRun == ( (LED_TOGGLE_INTERVAL_MS * 1000) / HW_TIMER_INTERVAL_US) ) + { + timeRun = 0; + + if (!started) + { + started = true; + pinMode(LED_BUILTIN, OUTPUT); + } + + //timer interrupt toggles pin LED_BUILTIN + digitalWrite(LED_BUILTIN, toggle); + toggle = !toggle; + } +} + +#define NUMBER_ISR_TIMERS 16 + +// You can assign any interval for any timer here, in milliseconds +uint32_t TimerInterval[NUMBER_ISR_TIMERS] = +{ + 1000L, 2000L, 3000L, 4000L, 5000L, 6000L, 7000L, 8000L, + 9000L, 10000L, 11000L, 12000L, 13000L, 14000L, 15000L, 16000L +}; + +typedef void (*irqCallback) (void); + +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 0) +void printStatus(uint16_t index, unsigned long deltaMillis, unsigned long currentMillis) +{ + Serial.print(TimerInterval[index]/1000); + Serial.print("s: Delta ms = "); + Serial.print(deltaMillis); + Serial.print(", ms = "); + Serial.println(currentMillis); +} +#endif + +// In SAMDUE, avoid doing something fancy in ISR, for example complex Serial.print with String() argument +// The pure simple Serial.prints here are just for demonstration and testing. Must be eliminate in working environment +// Or you can get this run-time error / crash +void doingSomething0() +{ +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 0) + static unsigned long previousMillis = startMillis; + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(0, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething1() +{ +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(1, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething2() +{ +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(2, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething3() +{ +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(3, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething4() +{ +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(4, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething5() +{ +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(5, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething6() +{ +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(6, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething7() +{ +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(7, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething8() +{ +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(8, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething9() +{ +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(9, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething10() +{ +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(10, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +// In SAMDUE, avoid doing something fancy in ISR, for example complex Serial.print with String() argument +// The pure simple Serial.prints here are just for demonstration and testing. Must be eliminate in working environment +// Or you can get this run-time error / crash +void doingSomething11() +{ +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(11, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +// In SAMDUE, avoid doing something fancy in ISR, for example complex Serial.print with String() argument +// The pure simple Serial.prints here are just for demonstration and testing. Must be eliminate in working environment +// Or you can get this run-time error / crash +void doingSomething12() +{ +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(12, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething13() +{ +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(13, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething14() +{ +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(14, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething15() +{ +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(15, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +irqCallback irqCallbackFunc[NUMBER_ISR_TIMERS] = +{ + doingSomething0, doingSomething1, doingSomething2, doingSomething3, + doingSomething4, doingSomething5, doingSomething6, doingSomething7, + doingSomething8, doingSomething9, doingSomething10, doingSomething11, + doingSomething12, doingSomething13, doingSomething14, doingSomething15 +}; + +//////////////////////////////////////////////// + + +#define SIMPLE_TIMER_MS 2000L + +// Init SimpleTimer +SimpleTimer simpleTimer; + +// Here is software Timer, you can do somewhat fancy stuffs without many issues. +// But always avoid +// 1. Long delay() it just doing nothing and pain-without-gain wasting CPU power.Plan and design your code / strategy ahead +// 2. Very long "do", "while", "for" loops without predetermined exit time. +void simpleTimerDoingSomething2s() +{ + static unsigned long previousMillis = startMillis; + unsigned long currMillis = millis(); + + Serial.print("simpleTimer2s: Dms="); + Serial.print(SIMPLE_TIMER_MS); + Serial.print(", actual="); + Serial.println(currMillis - previousMillis); + + previousMillis = currMillis; +} + +uint16_t attachDueInterrupt(double microseconds, timerCallback callback, const char* TimerName) +{ + DueTimerInterrupt dueTimerInterrupt = DueTimer.getAvailable(); + + dueTimerInterrupt.attachInterruptInterval(microseconds, callback); + + uint16_t timerNumber = dueTimerInterrupt.getTimerNumber(); + + Serial.print(TimerName); + Serial.print(" attached to Timer("); + Serial.print(timerNumber); + Serial.println(")"); + + return timerNumber; +} + +void setup() +{ + Serial.begin(115200); + while (!Serial); + + Serial.println("\nStarting ISR_16_Timers_Array on " + String(BOARD_NAME)); + Serial.println("Version : " + String(SAMDUE_TIMER_INTERRUPT_VERSION)); + Serial.println("CPU Frequency = " + String(F_CPU / 1000000) + " MHz"); + Serial.println("Timer Frequency = " + String(SystemCoreClock / 1000000) + " MHz"); + + // Interval in microsecs + attachDueInterrupt(HW_TIMER_INTERVAL_US, TimerHandler, "ITimer"); + + // Just to demonstrate, don't use too many ISR Timers if not absolutely necessary + // You can use up to 16 timer for each ISR_Timer + for (int i = 0; i < NUMBER_ISR_TIMERS; i++) + { + ISR_Timer.setInterval(TimerInterval[i], irqCallbackFunc[i]); + } + + // You need this timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary. + simpleTimer.setInterval(SIMPLE_TIMER_MS, simpleTimerDoingSomething2s); +} + +#define BLOCKING_TIME_MS 11111L + +void loop() +{ + // This unadvised blocking task is used to demonstrate the blocking effects onto the execution and accuracy to Software timer + // You see the time elapse of ISR_Timer still accurate, whereas very unaccurate for Software Timer + // The time elapse for 2000ms software timer now becomes 11111ms (BLOCKING_TIME_MS) + // While that of ISR_Timer is still prefect. + delay(BLOCKING_TIME_MS); + + // You need this Software timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary + // You don't need to and never call ISR_Timer.run() here in the loop(). It's already handled by ISR timer. + simpleTimer.run(); +} +``` +--- +--- + +### Debug Terminal Output Samples + +1. The following is the sample terminal output when running example [ISR_Timer_Complex_Ethernet](examples/ISR_Timer_Complex_Ethernet) on **Arduino SAM DUE** to demonstrate the accuracy of ISR Hardware Timer, **especially when system is very busy**. The ISR timer is **programmed for 2s, is activated exactly after 2.000s !!!** + +While software timer, **programmed for 2s, is activated after 10.917s !!!**. Then in loop(), it's also activated **every 3s**. + +``` +Starting ISR_Timer_Complex_Ethernet on SAM DUE +Version : 1.0.1 +Using Timer(0) = TC0, channel = 0, IRQ = TC0_IRQn +Timer(0), us = 50000.00 +ITimer attached to Timer(0) +[5] Getting IP... +[7] MAC: FE-8A-F1-EA-DE-82 +_pinCS = 0 +W5100 init, using SS_PIN_DEFAULT = 10, new ss_pin = 10, W5100Class::ss_pin = 10 +W5100::init: W5100, SSIZE =4096 +2s: Delta ms = 2000 +2s: Delta ms = 2000 +[7728] IP:192.168.2.134 +[7728] + ___ __ __ + / _ )/ /_ _____ / /__ + / _ / / // / _ \/ '_/ + /____/_/\_, /_//_/_/\_\ + /___/ v0.6.1 on Arduino Due + +[7732] BlynkArduinoClient.connect: Connecting to account.duckdns.org:8080 +[7849] Ready (ping: 6ms). +IP = 192.168.2.134 +2s: Delta ms = 2000 +2s: Delta ms = 2000 +5s: Delta ms = 5000 +blynkDoingSomething2s: Delta programmed ms = 2000, actual = 10917 +2s: Delta ms = 2000 +blynkDoingSomething2s: Delta programmed ms = 2000, actual = 3000 +2s: Delta ms = 2000 +5s: Delta ms = 5000 +2s: Delta ms = 2000 +blynkDoingSomething2s: Delta programmed ms = 2000, actual = 3000 +2s: Delta ms = 2000 +blynkDoingSomething2s: Delta programmed ms = 2000, actual = 3000 +2s: Delta ms = 2000 +5s: Delta ms = 5000 +2s: Delta ms = 2000 +11s: Delta ms = 11000 +blynkDoingSomething2s: Delta programmed ms = 2000, actual = 3000 +2s: Delta ms = 2000 +5s: Delta ms = 5000 +blynkDoingSomething2s: Delta programmed ms = 2000, actual = 3000 +2s: Delta ms = 2000 +2s: Delta ms = 2000 +blynkDoingSomething2s: Delta programmed ms = 2000, actual = 3000 +2s: Delta ms = 2000 +5s: Delta ms = 5000 +blynkDoingSomething2s: Delta programmed ms = 2000, actual = 3000 +2s: Delta ms = 2000 +11s: Delta ms = 11000 +2s: Delta ms = 2000 +blynkDoingSomething2s: Delta programmed ms = 2000, actual = 3000 +5s: Delta ms = 5000 +2s: Delta ms = 2000 +blynkDoingSomething2s: Delta programmed ms = 2000, actual = 3000 +2s: Delta ms = 2000 +2s: Delta ms = 2000 +5s: Delta ms = 5000 +blynkDoingSomething2s: Delta programmed ms = 2000, actual = 3000 +2s: Delta ms = 2000 +blynkDoingSomething2s: Delta programmed ms = 2000, actual = 3000 +2s: Delta ms = 2000 +11s: Delta ms = 11000 +5s: Delta ms = 5000 +2s: Delta ms = 2000 +blynkDoingSomething2s: Delta programmed ms = 2000, actual = 3000 +2s: Delta ms = 2000 +blynkDoingSomething2s: Delta programmed ms = 2000, actual = 3000 +2s: Delta ms = 2000 +5s: Delta ms = 5000 +2s: Delta ms = 2000 +blynkDoingSomething2s: Delta programmed ms = 2000, actual = 3000 +2s: Delta ms = 2000 +5s: Delta ms = 5000 +11s: Delta ms = 11000 +blynkDoingSomething2s: Delta programmed ms = 2000, actual = 3000 +2s: Delta ms = 2000 +2s: Delta ms = 2000 +blynkDoingSomething2s: Delta programmed ms = 2000, actual = 3000 + +``` + +--- + +2. The following is the sample terminal output when running example [**TimerInterruptTest**](examples/TimerInterruptTest) on **Arduino SAM DUE** to demonstrate how to start/stop Hardware Timers. + +``` +Starting TimerInterruptTest on SAM DUE +Version : 1.0.1 +CPU Frequency = 84 MHz +Timer Frequency = 84 MHz +Using Timer(0) = TC0, channel = 0, IRQ = TC0_IRQn +ITimer0 attached to Timer(0) +Using Timer(1) = TC0, channel = 1, IRQ = TC1_IRQn +ITimer1 attached to Timer(1) +ITimer0: millis() = 1104, delta = 1104 +ITimer0: millis() = 2104, delta = 1000 +ITimer0: millis() = 3104, delta = 1000 +ITimer1: millis() = 3111, delta = 3111 +ITimer0: millis() = 4104, delta = 1000 +Stop ITimer0, millis() = 5001 +ITimer1: millis() = 6111, delta = 3000 +ITimer1: millis() = 9111, delta = 3000 +Start ITimer0, millis() = 10002 +ITimer0: millis() = 11002, delta = 1000 +ITimer0: millis() = 12002, delta = 1000 +ITimer1: millis() = 12111, delta = 3000 +ITimer0: millis() = 13002, delta = 1000 +ITimer0: millis() = 14002, delta = 1000 +Stop ITimer1, millis() = 15001 +ITimer0: millis() = 15002, delta = 1000 +Stop ITimer0, millis() = 15003 +Start ITimer0, millis() = 20004 +ITimer0: millis() = 21004, delta = 1000 +ITimer0: millis() = 22004, delta = 1000 +ITimer0: millis() = 23004, delta = 1000 +ITimer0: millis() = 24004, delta = 1000 +ITimer0: millis() = 25004, delta = 1000 +Stop ITimer0, millis() = 25005 +Start ITimer1, millis() = 30002 +Start ITimer0, millis() = 30006 +ITimer0: millis() = 31006, delta = 1000 +ITimer0: millis() = 32006, delta = 1000 +ITimer1: millis() = 33002, delta = 3000 +ITimer0: millis() = 33006, delta = 1000 +ITimer0: millis() = 34006, delta = 1000 +ITimer0: millis() = 35006, delta = 1000 +Stop ITimer0, millis() = 35007 +ITimer1: millis() = 36002, delta = 3000 +ITimer1: millis() = 39002, delta = 3000 +Start ITimer0, millis() = 40008 +ITimer0: millis() = 41008, delta = 1000 +ITimer1: millis() = 42002, delta = 3000 +ITimer0: millis() = 42008, delta = 1000 +ITimer0: millis() = 43008, delta = 1000 +ITimer0: millis() = 44008, delta = 1000 +ITimer1: millis() = 45002, delta = 3000 +Stop ITimer1, millis() = 45003 +ITimer0: millis() = 45008, delta = 1000 +Stop ITimer0, millis() = 45009 + +``` + +--- + +3. The following is the sample terminal output when running example [**ISR_16_Timers_Array**](examples/ISR_16_Timers_Array) on **Arduino SAM DUE** to demonstrate the accuracy and how to use 16 ISR Timers from just 1 Hardware Timer. + +``` +Starting ISR_16_Timers_Array on SAM DUE +Version : 1.0.1 +CPU Frequency = 84 MHz +Timer Frequency = 84 MHz +Using Timer(0) = TC0, channel = 0, IRQ = TC0_IRQn +Timer(0), us = 100.00 +ITimer attached to Timer(0) +1s: Delta ms = 1006, ms = 1006 +1s: Delta ms = 1000, ms = 2006 +1s: Delta ms = 1000, ms = 3006 +1s: Delta ms = 1000, ms = 4006 +1s: Delta ms = 1000, ms = 5006 +1s: Delta ms = 1000, ms = 6006 +1s: Delta ms = 1000, ms = 7006 +1s: Delta ms = 1000, ms = 8006 +1s: Delta ms = 1000, ms = 9006 +1s: Delta ms = 1000, ms = 10006 +1s: Delta ms = 1000, ms = 11006 +simpleTimer2s: Dms=2000, actual=11118 +1s: Delta ms = 1000, ms = 12006 +1s: Delta ms = 1000, ms = 13006 +1s: Delta ms = 1000, ms = 14006 +1s: Delta ms = 1000, ms = 15006 +1s: Delta ms = 1000, ms = 16006 +1s: Delta ms = 1000, ms = 17006 +1s: Delta ms = 1000, ms = 18006 +1s: Delta ms = 1000, ms = 19006 +1s: Delta ms = 1000, ms = 20006 +1s: Delta ms = 1000, ms = 21006 +1s: Delta ms = 1000, ms = 22006 +simpleTimer2s: Dms=2000, actual=11111 +1s: Delta ms = 1000, ms = 23006 +1s: Delta ms = 1000, ms = 24006 +1s: Delta ms = 1000, ms = 25006 +1s: Delta ms = 1000, ms = 26006 +1s: Delta ms = 1000, ms = 27006 +1s: Delta ms = 1000, ms = 28006 +1s: Delta ms = 1000, ms = 29006 +1s: Delta ms = 1000, ms = 30006 +1s: Delta ms = 1000, ms = 31006 +1s: Delta ms = 1000, ms = 32006 +1s: Delta ms = 1000, ms = 33006 +simpleTimer2s: Dms=2000, actual=11111 +1s: Delta ms = 1000, ms = 34006 +1s: Delta ms = 1000, ms = 35006 +1s: Delta ms = 1000, ms = 36006 +1s: Delta ms = 1000, ms = 37006 +1s: Delta ms = 1000, ms = 38006 +1s: Delta ms = 1000, ms = 39006 +1s: Delta ms = 1000, ms = 40006 +1s: Delta ms = 1000, ms = 41006 +1s: Delta ms = 1000, ms = 42006 +1s: Delta ms = 1000, ms = 43006 +1s: Delta ms = 1000, ms = 44006 +simpleTimer2s: Dms=2000, actual=11111 +1s: Delta ms = 1000, ms = 45006 +1s: Delta ms = 1000, ms = 46006 +1s: Delta ms = 1000, ms = 47006 + +``` +--- +--- + +### Releases v1.0.1 + +1. Permit up to 16 super-long-time, super-accurate ISR-based timers to avoid being blocked +2. Using cpp code besides Impl.h code to use if Multiple-Definition linker error3. +3. Add complicated example [ISR_16_Timers_Array](examples/ISR_16_Timers_Array) utilizing and demonstrating the full usage of 16 independent ISR Timers based on just 1 Hardware Timer. + +#### Supported Boards + + - **Arduino SAM DUE**. + +--- +--- + +### Issues ### + +Submit issues to: [SAMDUE_TimerInterrupt issues](https://github.com/khoih-prog/SAMDUE_TimerInterrupt/issues) + +--- + +## TO DO + +1. Search for bug and improvement. + + +## DONE + + +1. Basic hardware timers for SAM DUE. +2. More hardware-initiated software-enabled timers +3. Longer time interval + +--- +--- + +### Contributions and Thanks + +Many thanks for everyone for bug reporting, new feature suggesting, testing and contributing to the development of this library. + +1. Use some code from the [**Ivan Seidel's DueTimer Library**](https://github.com/ivanseidel/DueTimer). + + + + + +
ivanseidel
⭐️ Ivan Seidel

+ +--- + +## Contributing + +If you want to contribute to this project: +- Report bugs and errors +- Ask for enhancements +- Create issues and pull requests +- Tell other people about this library + +--- + +### License + +- The library is licensed under [MIT](https://github.com/khoih-prog/SAMDUE_TimerInterrupt/blob/master/LICENSE) + +--- + +## Copyright + +Copyright 2020- Khoi Hoang + + diff --git a/examples/Argument_None/Argument_None.ino b/examples/Argument_None/Argument_None.ino new file mode 100644 index 0000000..b1d17e5 --- /dev/null +++ b/examples/Argument_None/Argument_None.ino @@ -0,0 +1,214 @@ +/**************************************************************************************************************************** + Argument_None.ino + For SAM DUE boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/SAMDUE_TimerInterrupt + Licensed under MIT license + + Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by + unsigned long miliseconds), you just consume only one SAM DUE timer and avoid conflicting with other cores' tasks. + The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers + Therefore, their executions are not blocked by bad-behaving functions / tasks. + This important feature is absolutely necessary for mission-critical tasks. + + Based on SimpleTimer - A timer library for Arduino. + Author: mromani@ottotecnica.com + Copyright (c) 2010 OTTOTECNICA Italy + + Based on BlynkTimer.h + Author: Volodymyr Shymanskyy + + Version: 1.0.1 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.1 K Hoang 07/11/2020 Initial coding. +*****************************************************************************************************************************/ + +/* + Notes: + Special design is necessary to share data between interrupt code and the rest of your program. + Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume + variable can not spontaneously change. Because your function may change variables while your program is using them, + the compiler needs this hint. But volatile alone is often not enough. + When accessing shared variables, usually interrupts must be disabled. Even with volatile, + if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. + If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled + or the entire sequence of your code which accesses the data. +*/ + +#if !( defined(ARDUINO_SAM_DUE) || defined(__SAM3X8E__) ) + #error This code is designed to run on SAM DUE board / platform! Please check your Tools->Board setting. +#endif + +// These define's must be placed at the beginning before #include "SAMDTimerInterrupt.h" +// Don't define SAMDUE_TIMER_INTERRUPT_DEBUG > 0. Only for special ISR debugging only. Can hang the system. +#define SAMDUE_TIMER_INTERRUPT_DEBUG 1 + +#include "SAMDUETimerInterrupt.h" + +//#ifndef LED_BUILTIN +// #define LED_BUILTIN 13 +//#endif + +#ifndef LED_BLUE + #define LED_BLUE 2 +#endif + +#ifndef LED_RED + #define LED_RED 8 +#endif + +#define TIMER0_INTERVAL_MS 1000L +#define TIMER1_INTERVAL_MS 2000L +#define TIMER2_INTERVAL_MS 5000L +#define TIMER3_INTERVAL_MS 8000L + +volatile uint32_t preMillisTimer0 = 0; +volatile uint32_t preMillisTimer1 = 0; +volatile uint32_t preMillisTimer2 = 0; +volatile uint32_t preMillisTimer3 = 0; + +void TimerHandler0(void) +{ + static bool toggle = false; + static bool started = false; + static uint32_t curMillis = 0; + + if (!started) + { + started = true; + pinMode(LED_BUILTIN, OUTPUT); + } + +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 0) + curMillis = millis(); + + if (curMillis > TIMER0_INTERVAL_MS) + { + Serial.println("ITimer0: millis() = " + String(curMillis) + ", delta = " + String(curMillis - preMillisTimer0)); + } + + preMillisTimer0 = curMillis; +#endif + + //timer interrupt toggles pin LED_BUILTIN + digitalWrite(LED_BUILTIN, toggle); + toggle = !toggle; +} + +void TimerHandler1(void) +{ + static bool toggle = false; + static bool started = false; + static uint32_t curMillis = 0; + + if (!started) + { + started = true; + pinMode(LED_BLUE, OUTPUT); + } + +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 0) + curMillis = millis(); + + if (curMillis > TIMER1_INTERVAL_MS) + { + Serial.println("ITimer1: millis() = " + String(curMillis) + ", delta = " + String(curMillis - preMillisTimer1)); + } + + preMillisTimer1 = curMillis; +#endif + + //timer interrupt toggles outputPin + digitalWrite(LED_BLUE, toggle); + toggle = !toggle; +} + +void TimerHandler2(void) +{ +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 0) + static uint32_t curMillis = 0; + + curMillis = millis(); + + if (curMillis > TIMER2_INTERVAL_MS) + { + Serial.println("ITimer2: millis() = " + String(curMillis) + ", delta = " + String(curMillis - preMillisTimer2)); + } + + preMillisTimer2 = curMillis; +#endif +} + +void TimerHandler3(void) +{ +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 0) + static uint32_t curMillis = 0; + + curMillis = millis(); + + if (curMillis > TIMER3_INTERVAL_MS) + { + Serial.println("ITimer3: millis() = " + String(curMillis) + ", delta = " + String(curMillis - preMillisTimer3)); + } + + preMillisTimer3 = curMillis; +#endif +} + +uint16_t attachDueInterrupt(double microseconds, timerCallback callback, const char* TimerName) +{ + DueTimerInterrupt dueTimerInterrupt = DueTimer.getAvailable(); + + dueTimerInterrupt.attachInterruptInterval(microseconds, callback); + + uint16_t timerNumber = dueTimerInterrupt.getTimerNumber(); + + Serial.print(TimerName); + Serial.print(" attached to Timer("); + Serial.print(timerNumber); + Serial.println(")"); + + return timerNumber; +} + +void setup() +{ + Serial.begin(115200); + while (!Serial); + + delay(100); + + Serial.println("\nStarting Argument_None on " + String(BOARD_NAME)); + Serial.println("Version : " + String(SAMDUE_TIMER_INTERRUPT_VERSION)); + Serial.println("CPU Frequency = " + String(F_CPU / 1000000) + " MHz"); + Serial.println("Timer Frequency = " + String(SystemCoreClock / 1000000) + " MHz"); + + // Interval in microsecs + uint32_t curMillis = millis(); + attachDueInterrupt(TIMER0_INTERVAL_MS * 1000, TimerHandler0, "ITimer0"); + // Just to have precise start time for the first time + preMillisTimer0 = (curMillis + millis()) / 2; + + curMillis = millis(); + attachDueInterrupt(TIMER1_INTERVAL_MS * 1000, TimerHandler1, "ITimer1"); + // Just to have precise start time for the first time + preMillisTimer1 = (curMillis + millis()) / 2; + + curMillis = millis(); + attachDueInterrupt(TIMER2_INTERVAL_MS * 1000, TimerHandler2, "ITimer2"); + // Just to have precise start time for the first time + preMillisTimer2 = (curMillis + millis()) / 2; + + curMillis = millis(); + attachDueInterrupt(TIMER3_INTERVAL_MS * 1000, TimerHandler3, "ITimer3"); + // Just to have precise start time for the first time + preMillisTimer3 = (curMillis + millis()) / 2; +} + +void loop() +{ + +} diff --git a/examples/ISR_16_Timers_Array/ISR_16_Timers_Array.ino b/examples/ISR_16_Timers_Array/ISR_16_Timers_Array.ino new file mode 100644 index 0000000..3c45e01 --- /dev/null +++ b/examples/ISR_16_Timers_Array/ISR_16_Timers_Array.ino @@ -0,0 +1,443 @@ +/**************************************************************************************************************************** + ISR_16_Timers_Array.ino + For SAM DUE boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/SAMDUE_TimerInterrupt + Licensed under MIT license + + Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by + unsigned long miliseconds), you just consume only one SAM DUE timer and avoid conflicting with other cores' tasks. + The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers + Therefore, their executions are not blocked by bad-behaving functions / tasks. + This important feature is absolutely necessary for mission-critical tasks. + + Based on SimpleTimer - A timer library for Arduino. + Author: mromani@ottotecnica.com + Copyright (c) 2010 OTTOTECNICA Italy + + Based on BlynkTimer.h + Author: Volodymyr Shymanskyy + + Version: 1.0.1 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.1 K Hoang 07/11/2020 Initial coding. +*****************************************************************************************************************************/ +/* + Notes: + Special design is necessary to share data between interrupt code and the rest of your program. + Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume + variable can not spontaneously change. Because your function may change variables while your program is using them, + the compiler needs this hint. But volatile alone is often not enough. + When accessing shared variables, usually interrupts must be disabled. Even with volatile, + if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. + If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled + or the entire sequence of your code which accesses the data. + + RPM Measuring uses high frequency hardware timer 1Hz == 1ms) to measure the time from of one rotation, in ms + then convert to RPM. One rotation is detected by reading the state of a magnetic REED SW or IR LED Sensor + Asssuming LOW is active. + For example: Max speed is 600RPM => 10 RPS => minimum 100ms a rotation. We'll use 80ms for debouncing + If the time between active state is less than 8ms => consider noise. + RPM = 60000 / (rotation time in ms) + + We use interrupt to detect whenever the SW is active, set a flag then use timer to count the time between active state + + This example will demonstrate the nearly perfect accuracy compared to software timers by printing the actual elapsed millisecs. + Being ISR-based timers, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet + and Blynk services. You can also have many (up to 16) timers to use. + This non-being-blocked important feature is absolutely necessary for mission-critical tasks. + You'll see blynkTimer is blocked while connecting to WiFi / Internet / Blynk, and elapsed time is very unaccurate + In this super simple example, you don't see much different after Blynk is connected, because of no competing task is + written +*/ + +#if !( defined(ARDUINO_SAM_DUE) || defined(__SAM3X8E__) ) + #error This code is designed to run on SAM DUE board / platform! Please check your Tools->Board setting. +#endif + +// These define's must be placed at the beginning before #include "SAMDTimerInterrupt.h" +// Don't define SAMDUE_TIMER_INTERRUPT_DEBUG > 0. Only for special ISR debugging only. Can hang the system. +#define SAMDUE_TIMER_INTERRUPT_DEBUG 1 + +#include "SAMDUETimerInterrupt.h" +#include "SAMDUE_ISR_Timer.h" + +#include // https://github.com/schinken/SimpleTimer + +#ifndef LED_BUILTIN + #define LED_BUILTIN 13 +#endif + +#ifndef LED_BLUE + #define LED_BLUE 2 +#endif + +#ifndef LED_RED + #define LED_RED 3 +#endif + +// Resolution for ISR_Timer. Smaller => more precise. +#define HW_TIMER_INTERVAL_US 100L + +volatile uint32_t startMillis = 0; + +// Init SAMDUE_ISR_Timer +// Each SAMDUE_ISR_Timer can service 16 different ISR-based timers +SAMDUE_ISR_Timer ISR_Timer; + +#define LED_TOGGLE_INTERVAL_MS 500L + +void TimerHandler(void) +{ + static bool toggle = false; + static bool started = false; + static int timeRun = 0; + + ISR_Timer.run(); + + // Toggle LED every LED_TOGGLE_INTERVAL_MS = 500ms = 0.5s + if (++timeRun == ( (LED_TOGGLE_INTERVAL_MS * 1000) / HW_TIMER_INTERVAL_US) ) + { + timeRun = 0; + + if (!started) + { + started = true; + pinMode(LED_BUILTIN, OUTPUT); + } + + //timer interrupt toggles pin LED_BUILTIN + digitalWrite(LED_BUILTIN, toggle); + toggle = !toggle; + } +} + +#define NUMBER_ISR_TIMERS 16 + +// You can assign any interval for any timer here, in milliseconds +uint32_t TimerInterval[NUMBER_ISR_TIMERS] = +{ + 1000L, 2000L, 3000L, 4000L, 5000L, 6000L, 7000L, 8000L, + 9000L, 10000L, 11000L, 12000L, 13000L, 14000L, 15000L, 16000L +}; + +typedef void (*irqCallback) (void); + +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 0) +void printStatus(uint16_t index, unsigned long deltaMillis, unsigned long currentMillis) +{ + Serial.print(TimerInterval[index]/1000); + Serial.print("s: Delta ms = "); + Serial.print(deltaMillis); + Serial.print(", ms = "); + Serial.println(currentMillis); +} +#endif + +// In SAMDUE, avoid doing something fancy in ISR, for example complex Serial.print with String() argument +// The pure simple Serial.prints here are just for demonstration and testing. Must be eliminate in working environment +// Or you can get this run-time error / crash +void doingSomething0() +{ +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 0) + static unsigned long previousMillis = startMillis; + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(0, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething1() +{ +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(1, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething2() +{ +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(2, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething3() +{ +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(3, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething4() +{ +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(4, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething5() +{ +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(5, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething6() +{ +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(6, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething7() +{ +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(7, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething8() +{ +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(8, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething9() +{ +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(9, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething10() +{ +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(10, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +// In SAMDUE, avoid doing something fancy in ISR, for example complex Serial.print with String() argument +// The pure simple Serial.prints here are just for demonstration and testing. Must be eliminate in working environment +// Or you can get this run-time error / crash +void doingSomething11() +{ +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(11, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +// In SAMDUE, avoid doing something fancy in ISR, for example complex Serial.print with String() argument +// The pure simple Serial.prints here are just for demonstration and testing. Must be eliminate in working environment +// Or you can get this run-time error / crash +void doingSomething12() +{ +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(12, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething13() +{ +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(13, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething14() +{ +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(14, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething15() +{ +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(15, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +irqCallback irqCallbackFunc[NUMBER_ISR_TIMERS] = +{ + doingSomething0, doingSomething1, doingSomething2, doingSomething3, + doingSomething4, doingSomething5, doingSomething6, doingSomething7, + doingSomething8, doingSomething9, doingSomething10, doingSomething11, + doingSomething12, doingSomething13, doingSomething14, doingSomething15 +}; + +//////////////////////////////////////////////// + + +#define SIMPLE_TIMER_MS 2000L + +// Init SimpleTimer +SimpleTimer simpleTimer; + +// Here is software Timer, you can do somewhat fancy stuffs without many issues. +// But always avoid +// 1. Long delay() it just doing nothing and pain-without-gain wasting CPU power.Plan and design your code / strategy ahead +// 2. Very long "do", "while", "for" loops without predetermined exit time. +void simpleTimerDoingSomething2s() +{ + static unsigned long previousMillis = startMillis; + unsigned long currMillis = millis(); + + Serial.print("simpleTimer2s: Dms="); + Serial.print(SIMPLE_TIMER_MS); + Serial.print(", actual="); + Serial.println(currMillis - previousMillis); + + previousMillis = currMillis; +} + +uint16_t attachDueInterrupt(double microseconds, timerCallback callback, const char* TimerName) +{ + DueTimerInterrupt dueTimerInterrupt = DueTimer.getAvailable(); + + dueTimerInterrupt.attachInterruptInterval(microseconds, callback); + + uint16_t timerNumber = dueTimerInterrupt.getTimerNumber(); + + Serial.print(TimerName); + Serial.print(" attached to Timer("); + Serial.print(timerNumber); + Serial.println(")"); + + return timerNumber; +} + +void setup() +{ + Serial.begin(115200); + while (!Serial); + + Serial.println("\nStarting ISR_16_Timers_Array on " + String(BOARD_NAME)); + Serial.println("Version : " + String(SAMDUE_TIMER_INTERRUPT_VERSION)); + Serial.println("CPU Frequency = " + String(F_CPU / 1000000) + " MHz"); + Serial.println("Timer Frequency = " + String(SystemCoreClock / 1000000) + " MHz"); + + // Interval in microsecs + attachDueInterrupt(HW_TIMER_INTERVAL_US, TimerHandler, "ITimer"); + + // Just to demonstrate, don't use too many ISR Timers if not absolutely necessary + // You can use up to 16 timer for each ISR_Timer + for (int i = 0; i < NUMBER_ISR_TIMERS; i++) + { + ISR_Timer.setInterval(TimerInterval[i], irqCallbackFunc[i]); + } + + // You need this timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary. + simpleTimer.setInterval(SIMPLE_TIMER_MS, simpleTimerDoingSomething2s); +} + +#define BLOCKING_TIME_MS 11111L + +void loop() +{ + // This unadvised blocking task is used to demonstrate the blocking effects onto the execution and accuracy to Software timer + // You see the time elapse of ISR_Timer still accurate, whereas very unaccurate for Software Timer + // The time elapse for 2000ms software timer now becomes 11111ms (BLOCKING_TIME_MS) + // While that of ISR_Timer is still prefect. + delay(BLOCKING_TIME_MS); + + // You need this Software timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary + // You don't need to and never call ISR_Timer.run() here in the loop(). It's already handled by ISR timer. + simpleTimer.run(); +} diff --git a/examples/ISR_RPM_Measure/ISR_RPM_Measure.ino b/examples/ISR_RPM_Measure/ISR_RPM_Measure.ino new file mode 100644 index 0000000..2e16521 --- /dev/null +++ b/examples/ISR_RPM_Measure/ISR_RPM_Measure.ino @@ -0,0 +1,173 @@ +/**************************************************************************************************************************** + ISR_RPM_Measure.ino + For SAM DUE boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/SAMDUE_TimerInterrupt + Licensed under MIT license + + Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by + unsigned long miliseconds), you just consume only one SAM DUE timer and avoid conflicting with other cores' tasks. + The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers + Therefore, their executions are not blocked by bad-behaving functions / tasks. + This important feature is absolutely necessary for mission-critical tasks. + + Based on SimpleTimer - A timer library for Arduino. + Author: mromani@ottotecnica.com + Copyright (c) 2010 OTTOTECNICA Italy + + Based on BlynkTimer.h + Author: Volodymyr Shymanskyy + + Version: 1.0.1 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.1 K Hoang 07/11/2020 Initial coding. +*****************************************************************************************************************************/ +/* + Notes: + Special design is necessary to share data between interrupt code and the rest of your program. + Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume + variable can not spontaneously change. Because your function may change variables while your program is using them, + the compiler needs this hint. But volatile alone is often not enough. + When accessing shared variables, usually interrupts must be disabled. Even with volatile, + if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. + If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled + or the entire sequence of your code which accesses the data. + + RPM Measuring uses high frequency hardware timer 1Hz == 1ms) to measure the time from of one rotation, in ms + then convert to RPM. One rotation is detected by reading the state of a magnetic REED SW or IR LED Sensor + Asssuming LOW is active. + For example: Max speed is 600RPM => 10 RPS => minimum 100ms a rotation. We'll use 80ms for debouncing + If the time between active state is less than 8ms => consider noise. + RPM = 60000 / (rotation time in ms) + + We use interrupt to detect whenever the SW is active, set a flag + then use timer to count the time between active state +*/ + +#if !( defined(ARDUINO_SAM_DUE) || defined(__SAM3X8E__) ) + #error This code is designed to run on SAM DUE board / platform! Please check your Tools->Board setting. +#endif + +// These define's must be placed at the beginning before #include "SAMDTimerInterrupt.h" +// Don't define SAMDUE_TIMER_INTERRUPT_DEBUG > 0. Only for special ISR debugging only. Can hang the system. +#define SAMDUE_TIMER_INTERRUPT_DEBUG 1 + +#include "SAMDUETimerInterrupt.h" + +//#ifndef LED_BUILTIN +// #define LED_BUILTIN 13 +//#endif + +#ifndef LED_BLUE + #define LED_BLUE 2 +#endif + +#ifndef LED_RED + #define LED_RED 8 +#endif + +unsigned int interruptPin = 7; + +#define TIMER1_INTERVAL_MS 1L +#define DEBOUNCING_INTERVAL_MS 80 + +#define LOCAL_DEBUG 1 + +volatile unsigned long rotationTime = 0; +float RPM = 0.00; +float avgRPM = 0.00; + +volatile int debounceCounter; + +volatile bool activeState = false; + +void detectRotation(void) +{ + activeState = true; +} + +void TimerHandler1() +{ + static bool started = false; + + if ( activeState ) + { + // Reset to prepare for next round of interrupt + activeState = false; + + if (debounceCounter >= DEBOUNCING_INTERVAL_MS / TIMER1_INTERVAL_MS ) + { + + //min time between pulses has passed + RPM = (float) ( 60000.0f / ( rotationTime * TIMER1_INTERVAL_MS ) ); + + avgRPM = ( 2 * avgRPM + RPM) / 3, + + Serial.println("RPM = " + String(avgRPM) + ", rotationTime ms = " + String(rotationTime * TIMER1_INTERVAL_MS) ); + + rotationTime = 0; + debounceCounter = 0; + } + else + debounceCounter++; + } + else + { + debounceCounter++; + } + + if (rotationTime >= 5000) + { + // If idle, set RPM to 0, don't increase rotationTime + RPM = 0; + Serial.println("RPM = " + String(RPM) + ", rotationTime = " + String(rotationTime) ); + rotationTime = 0; + } + else + { + rotationTime++; + } +} + +uint16_t attachDueInterrupt(double microseconds, timerCallback callback, const char* TimerName) +{ + DueTimerInterrupt dueTimerInterrupt = DueTimer.getAvailable(); + + dueTimerInterrupt.attachInterruptInterval(microseconds, callback); + + uint16_t timerNumber = dueTimerInterrupt.getTimerNumber(); + + Serial.print(TimerName); + Serial.print(" attached to Timer("); + Serial.print(timerNumber); + Serial.println(")"); + + return timerNumber; +} + +void setup() +{ + pinMode(interruptPin, INPUT_PULLUP); + + Serial.begin(115200); + while (!Serial); + + Serial.println("\nStarting ISR_RPM_Measure on " + String(BOARD_NAME)); + Serial.println("Version : " + String(SAMDUE_TIMER_INTERRUPT_VERSION)); + Serial.println("CPU Frequency = " + String(F_CPU / 1000000) + " MHz"); + Serial.println("Timer Frequency = " + String(SystemCoreClock / 1000000) + " MHz"); + + // Interval in microsecs + attachDueInterrupt(TIMER1_INTERVAL_MS * 1000, TimerHandler1, "ITimer1"); + + // Assumming the interruptPin will go LOW + attachInterrupt(digitalPinToInterrupt(interruptPin), detectRotation, FALLING); +} + +void loop() +{ + +} diff --git a/examples/ISR_Timer_Complex_Ethernet/ISR_Timer_Complex_Ethernet.ino b/examples/ISR_Timer_Complex_Ethernet/ISR_Timer_Complex_Ethernet.ino new file mode 100644 index 0000000..5eadf25 --- /dev/null +++ b/examples/ISR_Timer_Complex_Ethernet/ISR_Timer_Complex_Ethernet.ino @@ -0,0 +1,322 @@ +/**************************************************************************************************************************** + ISR_Timer_Complex_Ethernet.ino + For SAM DUE boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/SAMDUE_TimerInterrupt + Licensed under MIT license + + Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by + unsigned long miliseconds), you just consume only one SAM DUE timer and avoid conflicting with other cores' tasks. + The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers + Therefore, their executions are not blocked by bad-behaving functions / tasks. + This important feature is absolutely necessary for mission-critical tasks. + + Based on SimpleTimer - A timer library for Arduino. + Author: mromani@ottotecnica.com + Copyright (c) 2010 OTTOTECNICA Italy + + Based on BlynkTimer.h + Author: Volodymyr Shymanskyy + + Version: 1.0.1 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.1 K Hoang 07/11/2020 Initial coding. +*****************************************************************************************************************************/ +/* + Notes: + Special design is necessary to share data between interrupt code and the rest of your program. + Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume + variable can not spontaneously change. Because your function may change variables while your program is using them, + the compiler needs this hint. But volatile alone is often not enough. + When accessing shared variables, usually interrupts must be disabled. Even with volatile, + if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. + If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled + or the entire sequence of your code which accesses the data. + + RPM Measuring uses high frequency hardware timer 1Hz == 1ms) to measure the time from of one rotation, in ms + then convert to RPM. One rotation is detected by reading the state of a magnetic REED SW or IR LED Sensor + Asssuming LOW is active. + For example: Max speed is 600RPM => 10 RPS => minimum 100ms a rotation. We'll use 80ms for debouncing + If the time between active state is less than 8ms => consider noise. + RPM = 60000 / (rotation time in ms) + + We use interrupt to detect whenever the SW is active, set a flag then use timer to count the time between active state + + This example will demonstrate the nearly perfect accuracy compared to software timers by printing the actual elapsed millisecs. + Being ISR-based timers, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet + and Blynk services. You can also have many (up to 16) timers to use. + This non-being-blocked important feature is absolutely necessary for mission-critical tasks. + You'll see blynkTimer is blocked while connecting to WiFi / Internet / Blynk, and elapsed time is very unaccurate + In this super simple example, you don't see much different after Blynk is connected, because of no competing task is + written +*/ + +#if !( defined(ARDUINO_SAM_DUE) || defined(__SAM3X8E__) ) + #error This code is designed to run on SAM DUE board / platform! Please check your Tools->Board setting. +#endif + +// These define's must be placed at the beginning before #include "SAMDTimerInterrupt.h" +// Don't define > 0. Only for special ISR debugging only. Can hang the system. +#define SAMDUE_TIMER_INTERRUPT_DEBUG 1 + +#define BLYNK_PRINT Serial + +//#define BLYNK_DEBUG +#ifdef BLYNK_DEBUG + #undef BLYNK_DEBUG +#endif + +/* Comment this out to disable prints and save space */ +#define BLYNK_PRINT Serial + +// If don't use USE_UIP_ETHERNET => use W5x00 with Ethernet library +#define USE_UIP_ETHERNET false + +#if (USE_UIP_ETHERNET) + #define ETHERNET_NAME "ENC28J60 Ethernet Shield" +#else + #define ETHERNET_NAME "W5x00 Ethernet Shield" +#endif + +#define BLYNK_NO_YIELD + +#if USE_UIP_ETHERNET + #include +#else + #include +#endif + +#define USE_LOCAL_SERVER true + +#if USE_LOCAL_SERVER + char auth[] = "******"; + char server[] = "account.duckdns.org"; + //char server[] = "192.168.2.112"; + +#else + char auth[] = "******"; + char server[] = "blynk-cloud.com"; +#endif + + #define BLYNK_HARDWARE_PORT 8080 + +#if !(USE_UIP_ETHERNET) + #define W5100_CS 10 + #define SDCARD_CS 4 +#endif + +#include "SAMDUETimerInterrupt.h" +#include "SAMDUE_ISR_Timer.h" + +#define TIMER_INTERVAL_MS 100L +#define HW_TIMER_INTERVAL_MS 50L + +volatile uint32_t lastMillis = 0; + +// Init SAMDUE_ISR_Timer +// Each SAMDUE_ISR_Timer can service 16 different ISR-based timers +SAMDUE_ISR_Timer ISR_Timer; + +// Ibit Blynk Timer +BlynkTimer blynkTimer; + +#ifndef LED_BUILTIN + #define LED_BUILTIN 13 +#endif + +#define LED_TOGGLE_INTERVAL_MS 5000L + +#define TIMER_INTERVAL_2S 2000L +#define TIMER_INTERVAL_5S 5000L +#define TIMER_INTERVAL_11S 11000L +#define TIMER_INTERVAL_101S 101000L + +void TimerHandler(void) +{ + static bool toggle = false; + static bool started = false; + static int timeRun = 0; + + ISR_Timer.run(); + + // Toggle LED every LED_TOGGLE_INTERVAL_MS = 5000ms = 5s + if (++timeRun == (LED_TOGGLE_INTERVAL_MS / HW_TIMER_INTERVAL_MS) ) + { + timeRun = 0; + + if (!started) + { + started = true; + pinMode(LED_BUILTIN, OUTPUT); + } + + //timer interrupt toggles pin LED_BUILTIN + digitalWrite(LED_BUILTIN, toggle); + toggle = !toggle; + } +} + +// In SAMD, avoid doing something fancy in ISR, for example complex Serial.print with String() argument +// The pure simple Serial.prints here are just for demonstration and testing. Must be eliminate in working environment +// Or you can get this run-time error / crash +void doingSomething2s() +{ + static unsigned long previousMillis = lastMillis; + unsigned long deltaMillis = millis() - previousMillis; + +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 0) + if (previousMillis > TIMER_INTERVAL_2S) + { + Serial.print("2s: Delta ms = "); + Serial.println(deltaMillis); + } +#endif + + previousMillis = millis(); +} + +// In SAMD, avoid doing something fancy in ISR, for example complex Serial.print with String() argument +// The pure simple Serial.prints here are just for demonstration and testing. Must be eliminate in working environment +// Or you can get this run-time error / crash +void doingSomething5s() +{ + static unsigned long previousMillis = lastMillis; + unsigned long deltaMillis = millis() - previousMillis; + +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 0) + if (previousMillis > TIMER_INTERVAL_5S) + { + Serial.print("5s: Delta ms = "); + Serial.println(deltaMillis); + } +#endif + + previousMillis = millis(); +} + +// In SAMD, avoid doing something fancy in ISR, for example complex Serial.print with String() argument +// The pure simple Serial.prints here are just for demonstration and testing. Must be eliminate in working environment +// Or you can get this run-time error / crash +void doingSomething11s() +{ + static unsigned long previousMillis = lastMillis; + unsigned long deltaMillis = millis() - previousMillis; + +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 0) + if (previousMillis > TIMER_INTERVAL_11S) + { + Serial.print("11s: Delta ms = "); + Serial.println(deltaMillis); + } +#endif + + previousMillis = millis(); +} + +// In SAMD, avoid doing something fancy in ISR, for example complex Serial.print with String() argument +// The pure simple Serial.prints here are just for demonstration and testing. Must be eliminate in working environment +// Or you can get this run-time error / crash +void doingSomething101s() +{ + static unsigned long previousMillis = lastMillis; + unsigned long deltaMillis = millis() - previousMillis; + +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 0) + if (previousMillis > TIMER_INTERVAL_101S) + { + Serial.print("101s: Delta ms = "); + Serial.println(deltaMillis); + } +#endif + + previousMillis = millis(); +} + +#define BLYNK_TIMER_MS 2000L + +// Here is software Timer, you can do somewhat fancy stuffs without many issues. +// But always avoid +// 1. Long delay() it just doing nothing and pain-without-gain wasting CPU power.Plan and design your code / strategy ahead +// 2. Very long "do", "while", "for" loops without predetermined exit time. +void blynkDoingSomething2s() +{ + static unsigned long previousMillis = lastMillis; + Serial.println("blynkDoingSomething2s: Delta programmed ms = " + String(BLYNK_TIMER_MS) + ", actual = " + String(millis() - previousMillis)); + previousMillis = millis(); +} + +uint16_t attachDueInterrupt(double microseconds, timerCallback callback, const char* TimerName) +{ + DueTimerInterrupt dueTimerInterrupt = DueTimer.getAvailable(); + + dueTimerInterrupt.attachInterruptInterval(microseconds, callback); + + uint16_t timerNumber = dueTimerInterrupt.getTimerNumber(); + + Serial.print(TimerName); + Serial.print(" attached to Timer("); + Serial.print(timerNumber); + Serial.println(")"); + + return timerNumber; +} + +void setup() +{ + Serial.begin(115200); + while (!Serial); + + Serial.println("\nStarting ISR_Timer_Complex_Ethernet on " + String(BOARD_NAME)); + Serial.println("Version : " + String(SAMDUE_TIMER_INTERRUPT_VERSION)); + + // You need this timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary. + blynkTimer.setInterval(BLYNK_TIMER_MS, blynkDoingSomething2s); + + attachDueInterrupt(HW_TIMER_INTERVAL_MS * 1000, TimerHandler, "ITimer"); + + // Just to demonstrate, don't use too many ISR Timers if not absolutely necessary + // You can use up to 16 timer for each ISR_Timer + ISR_Timer.setInterval(TIMER_INTERVAL_2S, doingSomething2s); + ISR_Timer.setInterval(TIMER_INTERVAL_5S, doingSomething5s); + ISR_Timer.setInterval(TIMER_INTERVAL_11S, doingSomething11s); + ISR_Timer.setInterval(TIMER_INTERVAL_101S, doingSomething101s); + +#if !(USE_BUILTIN_ETHERNET || USE_UIP_ETHERNET) + pinMode(SDCARD_CS, OUTPUT); + digitalWrite(SDCARD_CS, HIGH); // Deselect the SD card +#endif + +#if USE_LOCAL_SERVER + Blynk.begin(auth, server, BLYNK_HARDWARE_PORT); +#else + Blynk.begin(auth); + // You can also specify server: + //Blynk.begin(auth, server, BLYNK_HARDWARE_PORT); +#endif + + if (Blynk.connected()) + { + Serial.print(F("IP = ")); + Serial.println(Ethernet.localIP()); + } +} + +#define BLOCKING_TIME_MS 3000L + +void loop() +{ + Blynk.run(); + + // This unadvised blocking task is used to demonstrate the blocking effects onto the execution and accuracy to Software timer + // You see the time elapse of ISR_Timer still accurate, whereas very unaccurate for Software Timer + // The time elapse for 2000ms software timer now becomes 3000ms (BLOCKING_TIME_MS) + // While that of ISR_Timer is still prefect. + delay(BLOCKING_TIME_MS); + + // You need this Software timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary + // You don't need to and never call ISR_Timer.run() here in the loop(). It's already handled by ISR timer. + blynkTimer.run(); +} diff --git a/examples/RPM_Measure/RPM_Measure.ino b/examples/RPM_Measure/RPM_Measure.ino new file mode 100644 index 0000000..0d29c79 --- /dev/null +++ b/examples/RPM_Measure/RPM_Measure.ino @@ -0,0 +1,168 @@ +/**************************************************************************************************************************** + RPM_Measure.ino + For SAM DUE boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/SAMDUE_TimerInterrupt + Licensed under MIT license + + Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by + unsigned long miliseconds), you just consume only one SAM DUE timer and avoid conflicting with other cores' tasks. + The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers + Therefore, their executions are not blocked by bad-behaving functions / tasks. + This important feature is absolutely necessary for mission-critical tasks. + + Based on SimpleTimer - A timer library for Arduino. + Author: mromani@ottotecnica.com + Copyright (c) 2010 OTTOTECNICA Italy + + Based on BlynkTimer.h + Author: Volodymyr Shymanskyy + + Version: 1.0.1 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.1 K Hoang 07/11/2020 Initial coding. +*****************************************************************************************************************************/ +/* + Notes: + Special design is necessary to share data between interrupt code and the rest of your program. + Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume + variable can not spontaneously change. Because your function may change variables while your program is using them, + the compiler needs this hint. But volatile alone is often not enough. + When accessing shared variables, usually interrupts must be disabled. Even with volatile, + if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. + If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled + or the entire sequence of your code which accesses the data. + + RPM Measuring uses high frequency hardware timer 1Hz == 1ms) to measure the time from of one rotation, in ms + then convert to RPM. One rotation is detected by reading the state of a magnetic REED SW or IR LED Sensor + Asssuming LOW is active. + For example: Max speed is 600RPM => 10 RPS => minimum 100ms a rotation. We'll use 80ms for debouncing + If the time between active state is less than 8ms => consider noise. + RPM = 60000 / (rotation time in ms) + + We use interrupt to detect whenever the SW is active, set a flag then use timer to count the time between active state + + RPM Measuring uses high frequency hardware timer 1Hz == 1ms) to measure the time from of one rotation, in ms + then convert to RPM. One rotation is detected by reading the state of a magnetic REED SW or IR LED Sensor + Asssuming LOW is active. + For example: Max speed is 600RPM => 10 RPS => minimum 100ms a rotation. We'll use 80ms for debouncing + If the time between active state is less than 8ms => consider noise. + RPM = 60000 / (rotation time in ms) + + You can also use interrupt to detect whenever the SW is active, set a flag then use timer to count the time between active state +*/ + +#if !( defined(ARDUINO_SAM_DUE) || defined(__SAM3X8E__) ) + #error This code is designed to run on SAM DUE board / platform! Please check your Tools->Board setting. +#endif + +// These define's must be placed at the beginning before #include "SAMDTimerInterrupt.h" +// Don't define SAMDUE_TIMER_INTERRUPT_DEBUG > 0. Only for special ISR debugging only. Can hang the system. +#define SAMDUE_TIMER_INTERRUPT_DEBUG 1 + +#include "SAMDUETimerInterrupt.h" + +//#ifndef LED_BUILTIN +// #define LED_BUILTIN 13 +//#endif + +#ifndef LED_BLUE + #define LED_BLUE 2 +#endif + +#ifndef LED_RED + #define LED_RED 8 +#endif + +unsigned int SWPin = 7; + +#define TIMER0_INTERVAL_MS 1 +#define DEBOUNCING_INTERVAL_MS 80 + +#define LOCAL_DEBUG 1 + +volatile unsigned long rotationTime = 0; +float RPM = 0.00; +float avgRPM = 0.00; + +volatile int debounceCounter; + +void TimerHandler() +{ + static bool started = false; + + if (!started) + { + started = true; + pinMode(SWPin, INPUT_PULLUP); + } + + if ( !digitalRead(SWPin) && (debounceCounter >= DEBOUNCING_INTERVAL_MS / TIMER0_INTERVAL_MS ) ) + { + //min time between pulses has passed + RPM = (float) ( 60000.0f / ( rotationTime * TIMER0_INTERVAL_MS ) ); + + avgRPM = ( 2 * avgRPM + RPM) / 3, + + Serial.println("RPM = " + String(avgRPM) + ", rotationTime ms = " + String(rotationTime * TIMER0_INTERVAL_MS) ); + + rotationTime = 0; + debounceCounter = 0; + } + else + { + debounceCounter++; + } + + if (rotationTime >= 5000) + { + // If idle, set RPM to 0, don't increase rotationTime + RPM = 0; + Serial.println("RPM = " + String(RPM) + ", rotationTime = " + String(rotationTime) ); + rotationTime = 0; + } + else + { + rotationTime++; + } +} + +uint16_t attachDueInterrupt(double microseconds, timerCallback callback, const char* TimerName) +{ + DueTimerInterrupt dueTimerInterrupt = DueTimer.getAvailable(); + + dueTimerInterrupt.attachInterruptInterval(microseconds, callback); + + uint16_t timerNumber = dueTimerInterrupt.getTimerNumber(); + + Serial.print(TimerName); + Serial.print(" attached to Timer("); + Serial.print(timerNumber); + Serial.println(")"); + + return timerNumber; +} + +void setup() +{ + Serial.begin(115200); + while (!Serial); + + Serial.println("\nStarting RPM_Measure on " + String(BOARD_NAME)); + Serial.println("Version : " + String(SAMDUE_TIMER_INTERRUPT_VERSION)); + Serial.println("CPU Frequency = " + String(F_CPU / 1000000) + " MHz"); + Serial.println("Timer Frequency = " + String(SystemCoreClock / 1000000) + " MHz"); + + // Interval in microsecs + attachDueInterrupt(TIMER0_INTERVAL_MS * 1000, TimerHandler, "ITimer"); + + Serial.flush(); +} + +void loop() +{ + +} diff --git a/examples/SwitchDebounce/SwitchDebounce.ino b/examples/SwitchDebounce/SwitchDebounce.ino new file mode 100644 index 0000000..3b412d1 --- /dev/null +++ b/examples/SwitchDebounce/SwitchDebounce.ino @@ -0,0 +1,199 @@ +/**************************************************************************************************************************** + SwitchDebounce.ino + For SAM DUE boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/SAMDUE_TimerInterrupt + Licensed under MIT license + + Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by + unsigned long miliseconds), you just consume only one SAM DUE timer and avoid conflicting with other cores' tasks. + The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers + Therefore, their executions are not blocked by bad-behaving functions / tasks. + This important feature is absolutely necessary for mission-critical tasks. + + Based on SimpleTimer - A timer library for Arduino. + Author: mromani@ottotecnica.com + Copyright (c) 2010 OTTOTECNICA Italy + + Based on BlynkTimer.h + Author: Volodymyr Shymanskyy + + Version: 1.0.1 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.1 K Hoang 07/11/2020 Initial coding. +*****************************************************************************************************************************/ +/* + Notes: + Special design is necessary to share data between interrupt code and the rest of your program. + Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume + variable can not spontaneously change. Because your function may change variables while your program is using them, + the compiler needs this hint. But volatile alone is often not enough. + When accessing shared variables, usually interrupts must be disabled. Even with volatile, + if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. + If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled + or the entire sequence of your code which accesses the data. + + Switch Debouncing uses high frequency hardware timer 50Hz == 20ms) to measure the time from the SW is pressed, + debouncing time is 100ms => SW is considered pressed if timer count is > 5, then call / flag SW is pressed + When the SW is released, timer will count (debounce) until more than 50ms until consider SW is released. + We can set to flag or call a function whenever SW is pressed more than certain predetermined time, even before + SW is released. +*/ + +#if !( defined(ARDUINO_SAM_DUE) || defined(__SAM3X8E__) ) + #error This code is designed to run on SAM DUE board / platform! Please check your Tools->Board setting. +#endif + +// These define's must be placed at the beginning before #include "SAMDTimerInterrupt.h" +// Don't define SAMDUE_TIMER_INTERRUPT_DEBUG > 0. Only for special ISR debugging only. Can hang the system. +#define SAMDUE_TIMER_INTERRUPT_DEBUG 1 + +#include "SAMDUETimerInterrupt.h" + +//#ifndef LED_BUILTIN +// #define LED_BUILTIN 13 +//#endif + +#ifndef LED_BLUE + #define LED_BLUE 2 +#endif + +#ifndef LED_RED + #define LED_RED 8 +#endif + +unsigned int SWPin = 7; + +#define TIMER1_INTERVAL_MS 20 +#define DEBOUNCING_INTERVAL_MS 100 +#define LONG_PRESS_INTERVAL_MS 5000 + +volatile bool SWPressed = false; +volatile bool SWLongPressed = false; + +void TimerHandler(void) +{ + static unsigned int debounceCountSWPressed = 0; + static unsigned int debounceCountSWReleased = 0; + + static unsigned long SWPressedTime; + static unsigned long SWReleasedTime; + + static bool started = false; + + if (!started) + { + started = true; + pinMode(SWPin, INPUT_PULLUP); + } + + if ( (!digitalRead(SWPin)) ) + { + // Start debouncing counting debounceCountSWPressed and clear debounceCountSWReleased + debounceCountSWReleased = 0; + + if (++debounceCountSWPressed >= DEBOUNCING_INTERVAL_MS / TIMER1_INTERVAL_MS) + { + // Call and flag SWPressed + if (!SWPressed) + { + SWPressedTime = millis(); + +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 0) + Serial.println("SW Press, from millis() = " + String(SWPressedTime - DEBOUNCING_INTERVAL_MS)); +#endif + + SWPressed = true; + // Do something for SWPressed here in ISR + // But it's better to use outside software timer to do your job instead of inside ISR + //Your_Response_To_Press(); + } + + if (debounceCountSWPressed >= LONG_PRESS_INTERVAL_MS / TIMER1_INTERVAL_MS) + { + // Call and flag SWLongPressed + if (!SWLongPressed) + { +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 0) + Serial.println("SW Long Pressed, total time ms = " + String(millis()) + " - " + String(SWPressedTime - DEBOUNCING_INTERVAL_MS) + + " = " + String(millis() - SWPressedTime + DEBOUNCING_INTERVAL_MS) ); +#endif + + SWLongPressed = true; + // Do something for SWLongPressed here in ISR + // But it's better to use outside software timer to do your job instead of inside ISR + //Your_Response_To_Long_Press(); + } + } + } + } + else + { + // Start debouncing counting debounceCountSWReleased and clear debounceCountSWPressed + if ( SWPressed && (++debounceCountSWReleased >= DEBOUNCING_INTERVAL_MS / TIMER1_INTERVAL_MS)) + { + SWReleasedTime = millis(); + + // Call and flag SWPressed +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 0) + Serial.println("SW Released, from millis() = " + String(SWReleasedTime)); +#endif + + SWPressed = false; + SWLongPressed = false; + + // Do something for !SWPressed here in ISR + // But it's better to use outside software timer to do your job instead of inside ISR + //Your_Response_To_Release(); + + // Call and flag SWPressed +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 0) + Serial.println("SW Pressed total time ms = " + String(SWReleasedTime - SWPressedTime)); +#endif + + debounceCountSWPressed = 0; + } + } +} + +uint16_t attachDueInterrupt(double microseconds, timerCallback callback, const char* TimerName) +{ + DueTimerInterrupt dueTimerInterrupt = DueTimer.getAvailable(); + + dueTimerInterrupt.attachInterruptInterval(microseconds, callback); + + uint16_t timerNumber = dueTimerInterrupt.getTimerNumber(); + + Serial.print(TimerName); + Serial.print(" attached to Timer("); + Serial.print(timerNumber); + Serial.println(")"); + + return timerNumber; +} + +void setup() +{ + Serial.begin(115200); + while (!Serial); + + delay(100); + + Serial.println("\nStarting SwitchDebounce on " + String(BOARD_NAME)); + Serial.println("Version : " + String(SAMDUE_TIMER_INTERRUPT_VERSION)); + Serial.println("CPU Frequency = " + String(F_CPU / 1000000) + " MHz"); + Serial.println("Timer Frequency = " + String(SystemCoreClock / 1000000) + " MHz"); + + // Interval in microsecs + attachDueInterrupt(TIMER1_INTERVAL_MS * 1000, TimerHandler, "ITimer"); + + Serial.flush(); +} + +void loop() +{ + +} diff --git a/examples/TimerInterruptLEDDemo/TimerInterruptLEDDemo.ino b/examples/TimerInterruptLEDDemo/TimerInterruptLEDDemo.ino new file mode 100644 index 0000000..d640784 --- /dev/null +++ b/examples/TimerInterruptLEDDemo/TimerInterruptLEDDemo.ino @@ -0,0 +1,145 @@ +/**************************************************************************************************************************** + TimerInterruptLEDDemo.ino + For SAM DUE boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/SAMDUE_TimerInterrupt + Licensed under MIT license + + Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by + unsigned long miliseconds), you just consume only one SAM DUE timer and avoid conflicting with other cores' tasks. + The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers + Therefore, their executions are not blocked by bad-behaving functions / tasks. + This important feature is absolutely necessary for mission-critical tasks. + + Based on SimpleTimer - A timer library for Arduino. + Author: mromani@ottotecnica.com + Copyright (c) 2010 OTTOTECNICA Italy + + Based on BlynkTimer.h + Author: Volodymyr Shymanskyy + + Version: 1.0.1 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.1 K Hoang 07/11/2020 Initial coding. +*****************************************************************************************************************************/ + +/* + Notes: + Special design is necessary to share data between interrupt code and the rest of your program. + Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume + variable can not spontaneously change. Because your function may change variables while your program is using them, + the compiler needs this hint. But volatile alone is often not enough. + When accessing shared variables, usually interrupts must be disabled. Even with volatile, + if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. + If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled + or the entire sequence of your code which accesses the data. +*/ + +#if !( defined(ARDUINO_SAM_DUE) || defined(__SAM3X8E__) ) + #error This code is designed to run on SAM DUE board / platform! Please check your Tools->Board setting. +#endif + +// These define's must be placed at the beginning before #include "SAMDTimerInterrupt.h" +// Don't define SAMDUE_TIMER_INTERRUPT_DEBUG > 0. Only for special ISR debugging only. Can hang the system. +#define SAMDUE_TIMER_INTERRUPT_DEBUG 1 + +#include "SAMDUETimerInterrupt.h" +#include "SAMDUE_ISR_Timer.h" + +//#ifndef LED_BUILTIN +// #define LED_BUILTIN 13 +//#endif + +#ifndef LED_BLUE + #define LED_BLUE 2 +#endif + +#ifndef LED_RED + #define LED_RED 8 +#endif + +#define HW_TIMER_INTERVAL_MS 10 + +// Init SAMDUE_ISR_Timer +// Each SAMDUE_ISR_Timer can service 16 different ISR-based timers +SAMDUE_ISR_Timer ISR_Timer; + +#define TIMER_INTERVAL_1S 1000L +#define TIMER_INTERVAL_2S 2000L +#define TIMER_INTERVAL_5S 5000L + +void TimerHandler(void) +{ + ISR_Timer.run(); +} + +// In SAMD, avoid doing something fancy in ISR, for example complex Serial.print with String() argument +// The pure simple Serial.prints here are just for demonstration and testing. Must be eliminate in working environment +// Or you can get this run-time error / crash +void doingSomething1() +{ + digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); + Serial.println("G"); +} + +void doingSomething2() +{ + digitalWrite(LED_BLUE, !digitalRead(LED_BLUE)); + Serial.println("B"); +} +void doingSomething3() +{ + digitalWrite(LED_RED, !digitalRead(LED_RED)); + Serial.println("R"); +} + +uint16_t attachDueInterrupt(double microseconds, timerCallback callback, const char* TimerName) +{ + DueTimerInterrupt dueTimerInterrupt = DueTimer.getAvailable(); + + dueTimerInterrupt.attachInterruptInterval(microseconds, callback); + + uint16_t timerNumber = dueTimerInterrupt.getTimerNumber(); + + Serial.print(TimerName); + Serial.print(" attached to Timer("); + Serial.print(timerNumber); + Serial.println(")"); + + return timerNumber; +} + +void setup() +{ + Serial.begin(115200); + while (!Serial); + + Serial.println("\nTimerInterruptLEDDemo on " + String(BOARD_NAME)); + Serial.println("CPU Frequency = " + String(F_CPU / 1000000) + " MHz"); + + // Instantiate HardwareTimer object. Thanks to 'new' instanciation, HardwareTimer is not destructed when setup() function is finished. + //HardwareTimer *MyTim = new HardwareTimer(Instance); + + // configure pin in output mode + pinMode(LED_BUILTIN, OUTPUT); + pinMode(LED_BLUE, OUTPUT); + pinMode(LED_RED, OUTPUT); + + // Interval in microsecs + attachDueInterrupt(HW_TIMER_INTERVAL_MS * 1000, TimerHandler, "ITimer"); + + // Just to demonstrate, don't use too many ISR Timers if not absolutely necessary + // You can use up to 16 timer for each ISR_Timer + ISR_Timer.setInterval(TIMER_INTERVAL_1S, doingSomething1); + ISR_Timer.setInterval(TIMER_INTERVAL_2S, doingSomething2); + ISR_Timer.setInterval(TIMER_INTERVAL_5S, doingSomething3); +} + + +void loop() +{ + /* Nothing to do all is done by hardware. Even no interrupt required. */ +} diff --git a/examples/TimerInterruptTest/TimerInterruptTest.ino b/examples/TimerInterruptTest/TimerInterruptTest.ino new file mode 100644 index 0000000..bf61fc3 --- /dev/null +++ b/examples/TimerInterruptTest/TimerInterruptTest.ino @@ -0,0 +1,216 @@ +/**************************************************************************************************************************** + TimerInterruptTest.ino + For SAM DUE boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/SAMDUE_TimerInterrupt + Licensed under MIT license + + Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by + unsigned long miliseconds), you just consume only one SAM DUE timer and avoid conflicting with other cores' tasks. + The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers + Therefore, their executions are not blocked by bad-behaving functions / tasks. + This important feature is absolutely necessary for mission-critical tasks. + + Based on SimpleTimer - A timer library for Arduino. + Author: mromani@ottotecnica.com + Copyright (c) 2010 OTTOTECNICA Italy + + Based on BlynkTimer.h + Author: Volodymyr Shymanskyy + + Version: 1.0.1 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.1 K Hoang 07/11/2020 Initial coding. +*****************************************************************************************************************************/ +/* + Notes: + Special design is necessary to share data between interrupt code and the rest of your program. + Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume + variable can not spontaneously change. Because your function may change variables while your program is using them, + the compiler needs this hint. But volatile alone is often not enough. + When accessing shared variables, usually interrupts must be disabled. Even with volatile, + if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. + If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled + or the entire sequence of your code which accesses the data. +*/ + +#if !( defined(ARDUINO_SAM_DUE) || defined(__SAM3X8E__) ) + #error This code is designed to run on SAM DUE board / platform! Please check your Tools->Board setting. +#endif + +// These define's must be placed at the beginning before #include "SAMDTimerInterrupt.h" +// Don't define SAMDUE_TIMER_INTERRUPT_DEBUG > 0. Only for special ISR debugging only. Can hang the system. +#define SAMDUE_TIMER_INTERRUPT_DEBUG 1 + +#include "SAMDUETimerInterrupt.h" + +//#ifndef LED_BUILTIN +// #define LED_BUILTIN 13 +//#endif + +#ifndef LED_BLUE + #define LED_BLUE 2 +#endif + +#ifndef LED_RED + #define LED_RED 8 +#endif + +unsigned int SWPin = 7; + +// Index in SAMDUETimerInterrupt. To use to retrieve SAMDUETimerInterrupt Info if necessary +uint16_t Timer0_Index = 0; +uint16_t Timer1_Index = 0; + +#define TIMER0_INTERVAL_MS 1000 +#define TIMER0_DURATION_MS 5000 + +#define TIMER1_INTERVAL_MS 3000 +#define TIMER1_DURATION_MS 15000 + +volatile uint32_t preMillisTimer0 = 0; +volatile uint32_t preMillisTimer1 = 0; + +void TimerHandler0(void) +{ + static bool toggle0 = false; + static bool started = false; + + + if (!started) + { + started = true; + pinMode(LED_BUILTIN, OUTPUT); + } + +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 0) + static uint32_t curMillis = 0; + + curMillis = millis(); + + if (curMillis > TIMER0_INTERVAL_MS) + { + Serial.println("ITimer0: millis() = " + String(curMillis) + ", delta = " + String(curMillis - preMillisTimer0)); + } + + preMillisTimer0 = curMillis; +#endif + + //timer interrupt toggles pin LED_BUILTIN + digitalWrite(LED_BUILTIN, toggle0); + toggle0 = !toggle0; +} + +void TimerHandler1(void) +{ + static bool toggle1 = false; + static bool started = false; + + if (!started) + { + started = true; + pinMode(LED_BLUE, OUTPUT); + } + +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 0) + static uint32_t curMillis = 0; + + curMillis = millis(); + + if (curMillis > TIMER1_INTERVAL_MS) + { + Serial.println("ITimer1: millis() = " + String(curMillis) + ", delta = " + String(curMillis - preMillisTimer1)); + } + + preMillisTimer1 = curMillis; +#endif + + //timer interrupt toggles outputPin + digitalWrite(LED_BLUE, toggle1); + toggle1 = !toggle1; +} + +uint16_t attachDueInterrupt(double microseconds, timerCallback callback, const char* TimerName) +{ + DueTimerInterrupt dueTimerInterrupt = DueTimer.getAvailable(); + + dueTimerInterrupt.attachInterruptInterval(microseconds, callback); + + uint16_t timerNumber = dueTimerInterrupt.getTimerNumber(); + + Serial.print(TimerName); + Serial.print(" attached to Timer("); + Serial.print(timerNumber); + Serial.println(")"); + + return timerNumber; +} + +void setup() +{ + Serial.begin(115200); + while (!Serial); + + delay(100); + + Serial.println("\nStarting TimerInterruptTest on " + String(BOARD_NAME)); + Serial.println("Version : " + String(SAMDUE_TIMER_INTERRUPT_VERSION)); + Serial.println("CPU Frequency = " + String(F_CPU / 1000000) + " MHz"); + Serial.println("Timer Frequency = " + String(SystemCoreClock / 1000000) + " MHz"); + + // Interval in microsecs + Timer0_Index = attachDueInterrupt(TIMER0_INTERVAL_MS * 1000, TimerHandler0, "ITimer0"); + + // Interval in microsecs + Timer1_Index = attachDueInterrupt(TIMER1_INTERVAL_MS * 1000, TimerHandler1, "ITimer1"); +} + +void loop() +{ + static unsigned long lastTimer0 = 0; + static bool timer0Stopped = false; + + + if (millis() - lastTimer0 > TIMER0_DURATION_MS) + { + lastTimer0 = millis(); + + if (timer0Stopped) + { + preMillisTimer0 = millis(); + Serial.println("Start ITimer0, millis() = " + String(preMillisTimer0)); + DueTimerPtr[Timer0_Index].restartTimer(); + } + else + { + Serial.println("Stop ITimer0, millis() = " + String(millis())); + DueTimerPtr[Timer0_Index].stopTimer(); + } + timer0Stopped = !timer0Stopped; + } + + static unsigned long lastTimer1 = 0; + static bool timer1Stopped = false; + + if (millis() - lastTimer1 > TIMER1_DURATION_MS) + { + lastTimer1 = millis(); + + if (timer1Stopped) + { + preMillisTimer1 = millis(); + Serial.println("Start ITimer1, millis() = " + String(preMillisTimer1)); + DueTimerPtr[Timer1_Index].restartTimer(); + } + else + { + Serial.println("Stop ITimer1, millis() = " + String(millis())); + DueTimerPtr[Timer1_Index].stopTimer(); + } + + timer1Stopped = !timer1Stopped; + } +} diff --git a/keywords.txt b/keywords.txt new file mode 100644 index 0000000..585278e --- /dev/null +++ b/keywords.txt @@ -0,0 +1,74 @@ +####################################### +# Datatypes (KEYWORD1) +####################################### + +DueTimerInterrupt KEYWORD1 +DueTimerIRQInfo KEYWORD1 +DueTimerIRQInfoStr KEYWORD1 +timerCallback KEYWORD1 + +SAMDUE_ISRTimer KEYWORD1 +SAMDUE_ISR_Timer KEYWORD1 +SAMDUETimerNumber KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +############################## +# Class DueTimerInterrupt +############################## + +getAvailable + +attachInterruptInterval KEYWORD2 +attachInterrupt KEYWORD2 +detachInterrupt KEYWORD2 +startTimer KEYWORD2 +restartTimer KEYWORD2 +stopTimer KEYWORD2 +disableTimer KEYWORD2 +bestClock KEYWORD2 +setFrequency KEYWORD2 +setPeriod KEYWORD2 +setInterval KEYWORD2 +getPeriod KEYWORD2 +getTimerNumber + +############################## +# Class SAMDUE_ISR_Timer +############################## + +run KEYWORD2 +setTimeout KEYWORD2 +setTimer KEYWORD2 +changeInterval KEYWORD2 +deleteTimer KEYWORD2 +restartTimer KEYWORD2 +isEnabled KEYWORD2 +enable KEYWORD2 +disable KEYWORD2 +enableAll KEYWORD2 +disableAll KEYWORD2 +toggle KEYWORD2 +getNumTimers KEYWORD2 +getNumAvailableTimers KEYWORD2 + +############################## +# IRQ Handlers +############################## + +TC0_Handler KEYWORD2 +TC1_Handler KEYWORD2 +TC2_Handler KEYWORD2 +TC3_Handler KEYWORD2 +TC4_Handler KEYWORD2 +TC5_Handler KEYWORD2 +TC6_Handler KEYWORD2 +TC7_Handler KEYWORD2 +TC8_Handler KEYWORD2 + + +####################################### +# Constants (LITERAL1) +####################################### diff --git a/library.json b/library.json new file mode 100644 index 0000000..3da0f56 --- /dev/null +++ b/library.json @@ -0,0 +1,29 @@ +{ + "name": "SAMDUE_TimerInterrupt", + "version": "1.0.1", + "keywords": "timing,device,control,timer,interrupt,hardware,due,sam-due,mission-critical,precise,non-blocking,isr", + "description": "This library enables you to use Interrupt from Hardware Timers on an SAM DUE-based board. These hardware timers, using interrupt, still work even if other functions are blocking. Moreover, they are much more precise (certainly depending on clock frequency accuracy) than other software timers using millis() or micros(). That's necessary if you need to measure some data requiring better accuracy.", + "authors": + { + "name": "Khoi Hoang", + "url": "https://github.com/khoih-prog", + "maintainer": true + }, + "repository": + { + "type": "git", + "url": "//https://github.com/khoih-prog/SAMDUE_TimerInterrupt" + }, + "homepage": "https://github.com/khoih-prog/SAMDUE_TimerInterrupt", + "export": { + "exclude": [ + "linux", + "extras", + "tests" + ] + }, + "frameworks": "*", + "platforms": "atmelsam", + "examples": "examples/*/*/*.ino", + "license": "MIT" +} diff --git a/library.properties b/library.properties new file mode 100644 index 0000000..dd35c07 --- /dev/null +++ b/library.properties @@ -0,0 +1,12 @@ +name=SAMDUE_TimerInterrupt +version=1.0.1 +author=Khoi Hoang +maintainer=Khoi Hoang +sentence=This library enables you to use Interrupt from Hardware Timers on an SAM-DUE-based board +paragraph=These SAM-DUE Hardware Timers, using Interrupt, still work even if other functions are blocking. Moreover, they are much more precise (certainly depending on clock frequency accuracy) than other software timers using millis() or micros(). That's mandatory if you need to measure some data requiring better accuracy. It now supports 16 ISR-based Timers, while consuming only 1 Hardware Timer. Timers' interval is very long (ulong millisecs). The most important feature is they're ISR-based Timers. Therefore, their executions are not blocked by bad-behaving functions or tasks. This important feature is absolutely necessary for mission-critical tasks. +category=Timing,Control,Device,Time,Timer,SAM-DUE,interrupt +url=https://github.com/khoih-prog/SAMDUE_TimerInterrupt +architectures=sam,atmelsam +repository=https://github.com/khoih-prog/SAMDUE_TimerInterrupt +license=MIT +includes=SAMDUETimerInterrupt.h,SAMDUE_ISR_Timer.h diff --git a/platformio/platformio.ini b/platformio/platformio.ini new file mode 100644 index 0000000..aa85344 --- /dev/null +++ b/platformio/platformio.ini @@ -0,0 +1,349 @@ +;PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + + +[platformio] +; ============================================================ +; chose environment: +; ESP8266 +; ESP32 +; SAMD +; DUE +; NRF52 +; STM32 +; ============================================================ +;default_envs = ESP8266 +;default_envs = ESP32 +;default_envs = SAMD +default_envs = DUE +;default_envs = NRF52 +;default_envs = STM32 + +[env] +; ============================================================ +; Serial configuration +; choose upload speed, serial-monitor speed +; ============================================================ +upload_speed = 921600 +;upload_port = COM11 +;monitor_speed = 9600 +;monitor_port = COM11 + +lib_deps = + +build_flags = +; set your debug output (default=Serial) +; -D DEBUG_ESP_PORT=Serial +; comment the folowing line to enable WiFi debugging +; -D NDEBUG + +[env:DUE] +platform = atmelsam +board = due + +; change microcontroller +board_build.mcu = at91sam3x8e + +; change MCU frequency +board_build.f_cpu = 84000000L + + +[env:ESP8266] +platform = espressif8266 +framework = arduino +; ============================================================ +; Board configuration +; choose your board by uncommenting one of the following lines +; ============================================================ +;board = gen4iod +;board = huzzah +;board = oak +;board = esp_wroom_02 +;board = espduino +;board = espectro +;board = espino +;board = espresso_lite_v1 +;board = espresso_lite_v2 +;board = esp12e +;board = esp01_1m +;board = esp01 +;board = esp07 +;board = esp8285 +;board = heltec_wifi_kit_8 +;board = inventone +;board = nodemcu +board = nodemcuv2 +;board = modwifi +;board = phoenix_v1 +;board = phoenix_v2 +;board = sparkfunBlynk +;board = thing +;board = thingdev +;board = esp210 +;board = espinotee +;board = d1 +;board = d1_mini +;board = d1_mini_lite +;board = d1_mini_pro +;board = wifi_slot +;board = wifiduino +;board = wifinfo +;board = wio_link +;board = wio_node +;board = xinabox_cw01 +;board = esp32doit-devkit-v1 + +[env:ESP32] +platform = espressif32 +framework = arduino, espidf +; ============================================================ +; Board configuration +; choose your board by uncommenting one of the following lines +; ============================================================ +;board = esp32cam +;board = alksesp32 +;board = featheresp32 +;board = espea32 +;board = bpi-bit +;board = d-duino-32 +board = esp32doit-devkit-v1 +;board = pocket_32 +;board = fm-devkit +;board = pico32 +;board = esp32-evb +;board = esp32-gateway +;board = esp32-pro +;board = esp32-poe +;board = oroca_edubot +;board = onehorse32dev +;board = lopy +;board = lopy4 +;board = wesp32 +;board = esp32thing +;board = sparkfun_lora_gateway_1-channel +;board = ttgo-lora32-v1 +;board = ttgo-t-beam +;board = turta_iot_node +;board = lolin_d32 +;board = lolin_d32_pro +;board = lolin32 +;board = wemosbat +;board = widora-air +;board = xinabox_cw02 +;board = iotbusio +;board = iotbusproteus +;board = nina_w10 + +[env:SAMD] +platform = atmelsam +framework = arduino +; ============================================================ +; Choose your board by uncommenting one of the following lines +; ============================================================ +; ============================================================ +; Board configuration Adafruit SAMD +; ============================================================ + +;board = adafruit_feather_m0 +;board = adafruit_feather_m0_express +;board = adafruit_metro_m0 +;board = adafruit_circuitplayground_m0 +;board = adafruit_gemma_m0 +;board = adafruit_trinket_m0 +;board = adafruit_itsybitsy_m0 +;board = adafruit_pirkey +;board = adafruit_hallowing +;board = adafruit_crickit_m0 +;board = adafruit_metro_m4 +;board = adafruit_grandcentral_m4 +board = adafruit_itsybitsy_m4 +;board = adafruit_feather_m4 +;board = adafruit_trellis_m4 +;board = adafruit_pyportal_m4 +;board = adafruit_pyportal_m4_titano +;board = adafruit_pybadge_m4 +;board = adafruit_metro_m4_airliftlite +;board = adafruit_pygamer_m4 +;board = adafruit_pygamer_advance_m4 +;board = adafruit_pybadge_airlift_m4 +;board = adafruit_monster_m4sk +;board = adafruit_hallowing_m4 + +; ============================================================ +; Board configuration Arduino SAMD and SAM +; ============================================================ + +;board = arduino_zero_edbg +;board = arduino_zero_native +;board = mkr1000 +;board = mkrzero +;board = mkrwifi1010 +;board = nano_33_iot +;board = mkrfox1200 +;board = mkrwan1300 +;board = mkrwan1310 +;board = mkrgsm1400 +;board = mkrnb1500 +;board = mkrvidor4000 +;board = adafruit_circuitplayground_m0 +;board = mzero_pro_bl_dbg +;board = mzero_pro_bl +;board = mzero_bl +;board = tian +;board = tian_cons +;board = arduino_due_x_dbg +;board = arduino_due_x + +; ============================================================ +; Board configuration Seeeduino SAMD +; ============================================================ + +;board = seeed_wio_terminal +;board = Seeed_femto_m0 +;board = seeed_XIAO_m0 +;board = Wio_Lite_MG126 +;board = WioGPS +;board = zero +;board = rolawan +;board = seeed_grove_ui_wireless + + +[env:NRF52] +platform = nordicnrf52 +framework = arduino +; ============================================================ +; Board configuration Adafruit nRF52 +; choose your board by uncommenting one of the following lines +; ============================================================ +;board = feather52832 +board = feather52840 +;board = feather52840sense +;board = itsybitsy52840 +;board = cplaynrf52840 +;board = cluenrf52840 +;board = metro52840 +;board = pca10056 +;board = particle_xenon +;board = mdbt50qrx +;board = ninab302 +;board = ninab112 + +[env:STM32] +platform = ststm32 +framework = arduino + +; ============================================================ +; Choose your board by uncommenting one of the following lines +; ============================================================ + +; ============================================================ +; Board configuration Nucleo-144 +; ============================================================ + +;board = nucleo_f207zg +;board = nucleo_f429zi +;board = nucleo_f746zg +;board = nucleo_f756zg +;board = nucleo_f767zi +;board = nucleo_h743zi +;board = nucleo_l496zg +;board = nucleo_l496zg-p +;board = nucleo_l4r5zi +;board = nucleo_l4r5zi-p + +; ============================================================ +; Board configuration Nucleo-64 +; ============================================================ + +;board = nucleo_f030r8 +;board = nucleo_f072rb + +;board = nucleo_f091rc +;board = nucleo_f103rb +;board = nucleo_f302r8 +;board = nucleo_f303re +;board = nucleo_f401re +;board = nucleo_f411re +;board = nucleo_f446re +;board = nucleo_g071rb +;board = nucleo_g431rb +;board = nucleo_g474re +;board = nucleo_l053r8 +;board = nucleo_l073rz +;board = nucleo_l152re +;board = nucleo_l433rc_p +;board = nucleo_l452re +;board = nucleo_l452re-p +;board = nucleo_l476rg +;board = pnucleo_wb55rg + +; ============================================================ +; Board configuration Nucleo-32 +; ============================================================ + +;board = nucleo_f031k6 +;board = nucleo_l031k6 +;board = nucleo_l412kb +;board = nucleo_l432lc +;board = nucleo_f303k8 +;board = nucleo_g431kb + +; ============================================================ +; Board configuration Discovery Boards +; ============================================================ + +;board = disco_f030r8 +;board = disco_f072rb +;board = disco_f030r8 +;board = disco_f100rb +;board = disco_f407vg +;board = disco_f413zh +;board = disco_f746ng +;board = disco_g0316 +;board = disco_l475vg_iot +;board = disco_f072cz-lrwan1 + +; ============================================================ +; Board configuration STM32MP1 Boards +; ============================================================ + +;board = stm32mp157a-dk1 +;board = stm32mp157c-dk2 + +; ============================================================ +; Board configuration Generic Boards +; ============================================================ + +;board = bluepill_f103c6 +;board = bluepill_f103c8 +;board = blackpill_f103c8 +;board = stm32f103cx +;board = stm32f103rx +;board = stm32f103tx +;board = stm32f103vx +;board = stm32f103zx +;board = stm32f103zet6 +;board = maplemini_f103cb +;board = blackpill_f303cc +;board = black_f407ve +;board = black_f407vg +;board = black_f407ze +;board = black_f407zg +;board = blue_f407ve_mini +;board = blackpill_f401cc +;board = blackpill_f411ce +;board = coreboard_f401rc +;board = feather_f405 + +; ============================================================ +; Board configuration Many more Boards to be filled +; ============================================================ + + diff --git a/src/SAMDUETimerInterrupt.h b/src/SAMDUETimerInterrupt.h new file mode 100644 index 0000000..3514179 --- /dev/null +++ b/src/SAMDUETimerInterrupt.h @@ -0,0 +1,607 @@ +/**************************************************************************************************************************** + SAMDUETimerInterrupt.h + For SAM DUE boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/SAMDUE_TimerInterrupt + Licensed under MIT license + + Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by + unsigned long miliseconds), you just consume only one SAM DUE timer and avoid conflicting with other cores' tasks. + The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers + Therefore, their executions are not blocked by bad-behaving functions / tasks. + This important feature is absolutely necessary for mission-critical tasks. + + Based on SimpleTimer - A timer library for Arduino. + Author: mromani@ottotecnica.com + Copyright (c) 2010 OTTOTECNICA Italy + + Based on BlynkTimer.h + Author: Volodymyr Shymanskyy + + Version: 1.0.1 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.1 K Hoang 06/11/2020 Initial coding +*****************************************************************************************************************************/ + +#pragma once + +#if !( defined(ARDUINO_SAM_DUE) || defined(__SAM3X8E__) ) + #error This code is designed to run on SAM DUE board / platform! Please check your Tools->Board setting. +#endif + +#include "Arduino.h" +#include + +#define SAMDUE_TIMER_INTERRUPT_VERSION "1.0.1" + +#ifndef SAMDUE_TIMER_INTERRUPT_DEBUG + #define SAMDUE_TIMER_INTERRUPT_DEBUG 0 +#endif + +#ifdef BOARD_NAME + #undef BOARD_NAME +#endif + +#define BOARD_NAME "SAM DUE" + +/* + This fixes compatibility for Arduino Servo Library. + Uncomment to make it compatible. + + Note that: + + Timers: 0,2,3,4,5 WILL NOT WORK, and will + neither be accessible by Timer0,... +*/ +// #define USING_SERVO_LIB true + +#if USING_SERVO_LIB + // Arduino Servo library uses timers 0,2,3,4,5. + // You must have `#define USING_SERVO_LIB true` in your sketch. + #warning Using Servo Library, Timer0, 2, 3, 4 and 5 not available +#endif + +#if defined TC2 + #define NUM_TIMERS 9 +#else + #define NUM_TIMERS 6 +#endif + +typedef void (*timerCallback) (); + +typedef struct +{ + Tc *tc; + uint32_t channel; + IRQn_Type irq; +} DueTimerIRQInfo; + +typedef struct +{ + const char* tc; + uint32_t channel; + const char* irq; +} DueTimerIRQInfoStr; + +// For printing info of selected Timer +const DueTimerIRQInfoStr TimersInfo[NUM_TIMERS] = +{ + { "TC0", 0, "TC0_IRQn" }, + { "TC0", 1, "TC1_IRQn" }, + { "TC0", 2, "TC2_IRQn" }, + { "TC1", 0, "TC3_IRQn" }, + { "TC1", 1, "TC4_IRQn" }, + { "TC1", 2, "TC5_IRQn" }, + +#if defined(TC2) + { "TC2", 0, "TC6_IRQn" }, + { "TC2", 1, "TC7_IRQn" }, + { "TC2", 2, "TC8_IRQn" }, +#endif +}; + +class DueTimerInterrupt +{ + protected: + + // Represents the timer id (index for the array of DueTimerIRQInfo structs) + const unsigned short _timerNumber; + + // Stores the object timer frequency + // (allows to access current timer period and frequency): + static double _frequency[NUM_TIMERS]; + + // Make Interrupt handlers friends, so they can use _callbacks + friend void TC0_Handler(); + friend void TC1_Handler(); + friend void TC2_Handler(); + friend void TC3_Handler(); + friend void TC4_Handler(); + friend void TC5_Handler(); + +#if defined(TC2) + friend void TC6_Handler(); + friend void TC7_Handler(); + friend void TC8_Handler(); +#endif + + static timerCallback _callbacks[NUM_TIMERS]; + + // Store timer configuration (static, as it's fixed for every object) + static const DueTimerIRQInfo Timers[NUM_TIMERS]; + + public: + + DueTimerInterrupt(unsigned short timer) : _timerNumber(timer) + { + /* + The constructor of the class DueTimerInterrupt + */ + } + + static DueTimerInterrupt getAvailable() __attribute__((always_inline)) + { + /* + Return the first timer with no callback set + */ + + for (int i = 0; i < NUM_TIMERS; i++) + { + if (!_callbacks[i]) + { +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 0) + // Get data from TimersInfo[NUM_TIMERS] + Serial.print("Using Timer("); + Serial.print(i); + Serial.print(") = "); + Serial.print(TimersInfo[i].tc); + Serial.print(", channel = "); + Serial.print(TimersInfo[i].channel); + Serial.print(", IRQ = "); + Serial.println(TimersInfo[i].irq); +#endif + return DueTimerInterrupt(i); + } + } + + // Default, return Timer0; + return DueTimerInterrupt(0); + } + + DueTimerInterrupt& attachInterruptInterval(double microseconds, timerCallback callback) __attribute__((always_inline)) + { + _callbacks[_timerNumber] = callback; + + return startTimer(microseconds); + } + + DueTimerInterrupt& attachInterrupt(float frequency, timerCallback callback) __attribute__((always_inline)) + { + return attachInterruptInterval((double) (1000000.0f / frequency), callback); + } + + DueTimerInterrupt& attachInterrupt(timerCallback callback) __attribute__((always_inline)) + { + /* + Links the function passed as argument to the timer of the object + */ + + _callbacks[_timerNumber] = callback; + + return *this; + } + + DueTimerInterrupt& detachInterrupt() __attribute__((always_inline)) + { + /* + Links the function passed as argument to the timer of the object + */ + + stopTimer(); // Stop the currently running timer + + _callbacks[_timerNumber] = NULL; + + return *this; + } + + DueTimerInterrupt& startTimer(double microseconds= -1) __attribute__((always_inline)) + { + /* + Start the timer + If a period is set, then sets the period and start the timer + If not period => default to 1Hz + */ + + if (microseconds > 0) + setPeriod(microseconds); + + if (_frequency[_timerNumber] <= 0) + setFrequency(1); + + NVIC_ClearPendingIRQ(Timers[_timerNumber].irq); + NVIC_EnableIRQ(Timers[_timerNumber].irq); + + TC_Start(Timers[_timerNumber].tc, Timers[_timerNumber].channel); + + return *this; + } + + DueTimerInterrupt& restartTimer(double microseconds= -1) __attribute__((always_inline)) + { + /* + Restart the timer + If a period is set, then sets the period and start the timer + If not period => default to 1Hz + */ + // If not yet initialized, set 1Hz + if (_frequency[_timerNumber] <= 0) + { + setFrequency(1); + } + else if (microseconds < 0) + { + // Using previous settings if no argument (microseconds = -1) + setFrequency(_frequency[_timerNumber]); + } + else + { + setPeriod(microseconds); + } + + NVIC_ClearPendingIRQ(Timers[_timerNumber].irq); + NVIC_EnableIRQ(Timers[_timerNumber].irq); + + TC_Start(Timers[_timerNumber].tc, Timers[_timerNumber].channel); + + return *this; + } + + DueTimerInterrupt& stopTimer() __attribute__((always_inline)) + { + /* + Stop the timer + */ + + NVIC_DisableIRQ(Timers[_timerNumber].irq); + + TC_Stop(Timers[_timerNumber].tc, Timers[_timerNumber].channel); + + return *this; + } + + DueTimerInterrupt& disableTimer() + { + return stopTimer(); + } + + // Picks the best clock to lower the error + static uint8_t bestClock(double frequency, uint32_t& retRC) + { + /* + Pick the best Clock, thanks to Ogle Basil Hall! + + Timer Definition + TIMER_CLOCK1 MCK / 2 + TIMER_CLOCK2 MCK / 8 + TIMER_CLOCK3 MCK / 32 + TIMER_CLOCK4 MCK /128 + */ + const struct + { + uint8_t flag; + uint8_t divisor; + } clockConfig[] = + { + { TC_CMR_TCCLKS_TIMER_CLOCK1, 2 }, + { TC_CMR_TCCLKS_TIMER_CLOCK2, 8 }, + { TC_CMR_TCCLKS_TIMER_CLOCK3, 32 }, + { TC_CMR_TCCLKS_TIMER_CLOCK4, 128 } + }; + + float ticks; + float error; + int clkId = 3; + int bestClock = 3; + float bestError = 9.999e99; + + do + { + ticks = (float) SystemCoreClock / frequency / (float) clockConfig[clkId].divisor; + // error = abs(ticks - round(ticks)); + error = clockConfig[clkId].divisor * abs(ticks - round(ticks)); // Error comparison needs scaling + + if (error < bestError) + { + bestClock = clkId; + bestError = error; + } + } while (clkId-- > 0); + + ticks = (float) SystemCoreClock / frequency / (float) clockConfig[bestClock].divisor; + retRC = (uint32_t) round(ticks); + + return clockConfig[bestClock].flag; + } + + + DueTimerInterrupt& setFrequency(double frequency) + { + /* + Set the timer frequency (in Hz) + */ + + // Prevent negative frequencies + if (frequency <= 0) + { + frequency = 1; + } + + // Remember the frequency — see below how the exact frequency is reported instead + //_frequency[_timerNumber] = frequency; + + // Get current timer configuration + DueTimerIRQInfo timerIRQInfo = Timers[_timerNumber]; + + uint32_t rc = 0; + uint8_t clock; + + // Tell the Power Management Controller to disable + // the write protection of the (Timer/Counter) registers: + pmc_set_writeprotect(false); + + // Enable clock for the timer + pmc_enable_periph_clk((uint32_t)timerIRQInfo.irq); + + // Find the best clock for the wanted frequency + clock = bestClock(frequency, rc); + + switch (clock) + { + case TC_CMR_TCCLKS_TIMER_CLOCK1: + _frequency[_timerNumber] = (double)SystemCoreClock / 2.0 / (double)rc; + break; + case TC_CMR_TCCLKS_TIMER_CLOCK2: + _frequency[_timerNumber] = (double)SystemCoreClock / 8.0 / (double)rc; + break; + case TC_CMR_TCCLKS_TIMER_CLOCK3: + _frequency[_timerNumber] = (double)SystemCoreClock / 32.0 / (double)rc; + break; + default: // TC_CMR_TCCLKS_TIMER_CLOCK4 + _frequency[_timerNumber] = (double)SystemCoreClock / 128.0 / (double)rc; + break; + } + + // Set up the Timer in waveform mode which creates a PWM + // in UP mode with automatic trigger on RC Compare + // and sets it up with the determined internal clock as clock input. + TC_Configure(timerIRQInfo.tc, timerIRQInfo.channel, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | clock); + + // Reset counter and fire interrupt when RC value is matched: + TC_SetRC(timerIRQInfo.tc, timerIRQInfo.channel, rc); + + // Enable the RC Compare Interrupt. + timerIRQInfo.tc->TC_CHANNEL[timerIRQInfo.channel].TC_IER = TC_IER_CPCS; + + // ... and disable all others. + timerIRQInfo.tc->TC_CHANNEL[timerIRQInfo.channel].TC_IDR = ~TC_IER_CPCS; + + return *this; + } + + DueTimerInterrupt& setPeriod(double microseconds) __attribute__((always_inline)) + { + /* + Set the period of the timer (in microseconds) + */ + + // Convert period in microseconds to frequency in Hz + double frequency = 1000000.0 / microseconds; + + setFrequency(frequency); + + return *this; + } + + DueTimerInterrupt& setInterval(double microseconds) __attribute__((always_inline)) + { + return setPeriod(microseconds); + } + + double getFrequency() const __attribute__((always_inline)) + { + /* + Get current time frequency + */ + return _frequency[_timerNumber]; + } + + double getPeriod() const __attribute__((always_inline)) + { + /* + Get current time period + */ + return 1.0 / getFrequency() * 1000000; + } + + uint16_t getTimerNumber() + { + return _timerNumber; + } + + bool operator== (const DueTimerInterrupt& rhs) const __attribute__((always_inline)) + { + return _timerNumber == rhs._timerNumber; + }; + + bool operator!= (const DueTimerInterrupt& rhs) const __attribute__((always_inline)) + { + return _timerNumber != rhs._timerNumber; + }; +}; + + + +//////////////////////////////////////////////////////////////////// + +const DueTimerIRQInfo DueTimerInterrupt::Timers[NUM_TIMERS] = +{ + { TC0, 0, TC0_IRQn }, + { TC0, 1, TC1_IRQn }, + { TC0, 2, TC2_IRQn }, + { TC1, 0, TC3_IRQn }, + { TC1, 1, TC4_IRQn }, + { TC1, 2, TC5_IRQn }, + +#if defined(TC2) + { TC2, 0, TC6_IRQn }, + { TC2, 1, TC7_IRQn }, + { TC2, 2, TC8_IRQn }, +#endif +}; + +// Fix for compatibility with Servo library +#if USING_SERVO_LIB + // Set _callbacks as used, allowing DueTimerInterrupt::getAvailable() to work + void (*DueTimerInterrupt::_callbacks[NUM_TIMERS])() = + { + (void (*)()) 1, // Timer 0 - Occupied + (void (*)()) 0, // Timer 1 + (void (*)()) 1, // Timer 2 - Occupied + (void (*)()) 1, // Timer 3 - Occupied + (void (*)()) 1, // Timer 4 - Occupied + (void (*)()) 1, // Timer 5 - Occupied + + #if defined(TC2) + (void (*)()) 0, // Timer 6 + (void (*)()) 0, // Timer 7 + (void (*)()) 0 // Timer 8 + #endif + }; + +#else + void (*DueTimerInterrupt::_callbacks[NUM_TIMERS])() = {}; +#endif + +#if defined(TC2) + double DueTimerInterrupt::_frequency[NUM_TIMERS] = { -1, -1, -1, -1, -1, -1, -1, -1, -1}; +#else + double DueTimerInterrupt::_frequency[NUM_TIMERS] = { -1, -1, -1, -1, -1, -1}; +#endif + +/* + Initializing all timers, so you can use them like this: Timer0.startTimer(); +*/ +DueTimerInterrupt DueTimer(0); + +DueTimerInterrupt Timer1(1); + +// Fix for compatibility with Servo library +#if ( !USING_SERVO_LIB || !defined(USING_SERVO_LIB) ) + DueTimerInterrupt Timer0(0); + DueTimerInterrupt Timer2(2); + DueTimerInterrupt Timer3(3); + DueTimerInterrupt Timer4(4); + DueTimerInterrupt Timer5(5); +#endif + +#if defined(TC2) + DueTimerInterrupt Timer6(6); + DueTimerInterrupt Timer7(7); + DueTimerInterrupt Timer8(8); +#endif + +DueTimerInterrupt DueTimerPtr[NUM_TIMERS] = +{ +#if ( !USING_SERVO_LIB || !defined(USING_SERVO_LIB) ) + Timer0, +#endif + + Timer1, + +#if ( !USING_SERVO_LIB || !defined(USING_SERVO_LIB) ) + Timer2, + Timer3, + Timer4, + Timer5, +#endif + +#if defined(TC2) + Timer6, + Timer7, + Timer8 +#endif +}; + +/////////////////////////////////////////////////////////////////////// + +/* + Implementation of the timer _callbacks defined in + arduino-1.5.2/hardware/arduino/sam/system/CMSIS/Device/ATMEL/sam3xa/include/sam3x8e.h +*/ +// Fix for compatibility with Servo library +#if ( !USING_SERVO_LIB || !defined(USING_SERVO_LIB) ) + +void TC0_Handler() +{ + TC_GetStatus(TC0, 0); + DueTimerInterrupt::_callbacks[0](); +} + +#endif + +void TC1_Handler() +{ + TC_GetStatus(TC0, 1); + DueTimerInterrupt::_callbacks[1](); +} + +// Fix for compatibility with Servo library +#if ( !USING_SERVO_LIB || !defined(USING_SERVO_LIB) ) + +void TC2_Handler() +{ + TC_GetStatus(TC0, 2); + DueTimerInterrupt::_callbacks[2](); +} + +void TC3_Handler() +{ + TC_GetStatus(TC1, 0); + DueTimerInterrupt::_callbacks[3](); +} + +void TC4_Handler() +{ + TC_GetStatus(TC1, 1); + DueTimerInterrupt::_callbacks[4](); +} + +void TC5_Handler() +{ + TC_GetStatus(TC1, 2); + DueTimerInterrupt::_callbacks[5](); +} +#endif + +#if defined(TC2) + +void TC6_Handler() +{ + TC_GetStatus(TC2, 0); + DueTimerInterrupt::_callbacks[6](); +} + +void TC7_Handler() +{ + TC_GetStatus(TC2, 1); + DueTimerInterrupt::_callbacks[7](); +} + +void TC8_Handler() +{ + TC_GetStatus(TC2, 2); + DueTimerInterrupt::_callbacks[8](); +} +#endif diff --git a/src/SAMDUE_ISR_Timer-Impl.h b/src/SAMDUE_ISR_Timer-Impl.h new file mode 100644 index 0000000..997d0c3 --- /dev/null +++ b/src/SAMDUE_ISR_Timer-Impl.h @@ -0,0 +1,337 @@ +/**************************************************************************************************************************** + SAMDUE_ISR_Timer-Impl.h + For SAM DUE boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/SAMDUE_TimerInterrupt + Licensed under MIT license + + Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by + unsigned long miliseconds), you just consume only one SAM DUE timer and avoid conflicting with other cores' tasks. + The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers + Therefore, their executions are not blocked by bad-behaving functions / tasks. + This important feature is absolutely necessary for mission-critical tasks. + + Based on SimpleTimer - A timer library for Arduino. + Author: mromani@ottotecnica.com + Copyright (c) 2010 OTTOTECNICA Italy + + Based on BlynkTimer.h + Author: Volodymyr Shymanskyy + + Version: 1.0.1 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.1 K Hoang 06/11/2020 Initial coding +*****************************************************************************************************************************/ + +#pragma once + +//#include "SAMDUE_ISR_Timer.h" +#include + +SAMDUE_ISR_Timer::SAMDUE_ISR_Timer() + : numTimers (-1) +{ +} + +void SAMDUE_ISR_Timer::init() +{ + unsigned long current_millis = millis(); //elapsed(); + + for (int i = 0; i < MAX_NUMBER_TIMERS; i++) + { + memset((void*) &timer[i], 0, sizeof (timer_t)); + timer[i].prev_millis = current_millis; + } + + numTimers = 0; +} + +void SAMDUE_ISR_Timer::run() +{ + int i; + unsigned long current_millis; + + // get current time + current_millis = millis(); //elapsed(); + + for (i = 0; i < MAX_NUMBER_TIMERS; i++) + { + + timer[i].toBeCalled = TIMER_DEFCALL_DONTRUN; + + // no callback == no timer, i.e. jump over empty slots + if (timer[i].callback != NULL) + { + + // is it time to process this timer ? + // see http://arduino.cc/forum/index.php/topic,124048.msg932592.html#msg932592 + + if ((current_millis - timer[i].prev_millis) >= timer[i].delay) + { + unsigned long skipTimes = (current_millis - timer[i].prev_millis) / timer[i].delay; + + // update time + timer[i].prev_millis += timer[i].delay * skipTimes; + + // check if the timer callback has to be executed + if (timer[i].enabled) + { + + // "run forever" timers must always be executed + if (timer[i].maxNumRuns == TIMER_RUN_FOREVER) + { + timer[i].toBeCalled = TIMER_DEFCALL_RUNONLY; + } + // other timers get executed the specified number of times + else if (timer[i].numRuns < timer[i].maxNumRuns) + { + timer[i].toBeCalled = TIMER_DEFCALL_RUNONLY; + timer[i].numRuns++; + + // after the last run, delete the timer + if (timer[i].numRuns >= timer[i].maxNumRuns) + { + timer[i].toBeCalled = TIMER_DEFCALL_RUNANDDEL; + } + } + } + } + } + } + + for (i = 0; i < MAX_NUMBER_TIMERS; i++) + { + if (timer[i].toBeCalled == TIMER_DEFCALL_DONTRUN) + continue; + + if (timer[i].hasParam) + (*(timerCallback_p)timer[i].callback)(timer[i].param); + else + (*(timerCallback)timer[i].callback)(); + + if (timer[i].toBeCalled == TIMER_DEFCALL_RUNANDDEL) + deleteTimer(i); + } +} + + +// find the first available slot +// return -1 if none found +int SAMDUE_ISR_Timer::findFirstFreeSlot() +{ + // all slots are used + if (numTimers >= MAX_NUMBER_TIMERS) + { + return -1; + } + + // return the first slot with no callback (i.e. free) + for (int i = 0; i < MAX_NUMBER_TIMERS; i++) + { + if (timer[i].callback == NULL) + { + return i; + } + } + + // no free slots found + return -1; +} + + +int SAMDUE_ISR_Timer::setupTimer(unsigned long d, void* f, void* p, bool h, unsigned n) +{ + int freeTimer; + + if (numTimers < 0) + { + init(); + } + + freeTimer = findFirstFreeSlot(); + if (freeTimer < 0) + { + return -1; + } + + if (f == NULL) + { + return -1; + } + + timer[freeTimer].delay = d; + timer[freeTimer].callback = f; + timer[freeTimer].param = p; + timer[freeTimer].hasParam = h; + timer[freeTimer].maxNumRuns = n; + timer[freeTimer].enabled = true; + timer[freeTimer].prev_millis = millis(); + + numTimers++; + + return freeTimer; +} + + +int SAMDUE_ISR_Timer::setTimer(unsigned long d, timerCallback f, unsigned n) +{ + return setupTimer(d, (void *)f, NULL, false, n); +} + +int SAMDUE_ISR_Timer::setTimer(unsigned long d, timerCallback_p f, void* p, unsigned n) +{ + return setupTimer(d, (void *)f, p, true, n); +} + +int SAMDUE_ISR_Timer::setInterval(unsigned long d, timerCallback f) +{ + return setupTimer(d, (void *)f, NULL, false, TIMER_RUN_FOREVER); +} + +int SAMDUE_ISR_Timer::setInterval(unsigned long d, timerCallback_p f, void* p) +{ + return setupTimer(d, (void *)f, p, true, TIMER_RUN_FOREVER); +} + +int SAMDUE_ISR_Timer::setTimeout(unsigned long d, timerCallback f) +{ + return setupTimer(d, (void *)f, NULL, false, TIMER_RUN_ONCE); +} + +int SAMDUE_ISR_Timer::setTimeout(unsigned long d, timerCallback_p f, void* p) +{ + return setupTimer(d, (void *)f, p, true, TIMER_RUN_ONCE); +} + +bool SAMDUE_ISR_Timer::changeInterval(unsigned numTimer, unsigned long d) +{ + if (numTimer >= MAX_NUMBER_TIMERS) + { + return false; + } + + // Updates interval of existing specified timer + if (timer[numTimer].callback != NULL) + { + timer[numTimer].delay = d; + timer[numTimer].prev_millis = millis(); + + return true; + } + + // false return for non-used numTimer, no callback + return false; +} + +void SAMDUE_ISR_Timer::deleteTimer(unsigned timerId) +{ + if (timerId >= MAX_NUMBER_TIMERS) + { + return; + } + + // nothing to delete if no timers are in use + if (numTimers == 0) + { + return; + } + + // don't decrease the number of timers if the specified slot is already empty + if (timer[timerId].callback != NULL) + { + memset((void*) &timer[timerId], 0, sizeof (timer_t)); + timer[timerId].prev_millis = millis(); + + // update number of timers + numTimers--; + } +} + +// function contributed by code@rowansimms.com +void SAMDUE_ISR_Timer::restartTimer(unsigned numTimer) +{ + if (numTimer >= MAX_NUMBER_TIMERS) + { + return; + } + + timer[numTimer].prev_millis = millis(); +} + + +bool SAMDUE_ISR_Timer::isEnabled(unsigned numTimer) +{ + if (numTimer >= MAX_NUMBER_TIMERS) + { + return false; + } + + return timer[numTimer].enabled; +} + + +void SAMDUE_ISR_Timer::enable(unsigned numTimer) +{ + if (numTimer >= MAX_NUMBER_TIMERS) + { + return; + } + + timer[numTimer].enabled = true; +} + + +void SAMDUE_ISR_Timer::disable(unsigned numTimer) +{ + if (numTimer >= MAX_NUMBER_TIMERS) + { + return; + } + + timer[numTimer].enabled = false; +} + +void SAMDUE_ISR_Timer::enableAll() +{ + // Enable all timers with a callback assigned (used) + + for (int i = 0; i < MAX_NUMBER_TIMERS; i++) + { + if (timer[i].callback != NULL && timer[i].numRuns == TIMER_RUN_FOREVER) + { + timer[i].enabled = true; + } + } +} + +void SAMDUE_ISR_Timer::disableAll() +{ + // Disable all timers with a callback assigned (used) + + for (int i = 0; i < MAX_NUMBER_TIMERS; i++) + { + if (timer[i].callback != NULL && timer[i].numRuns == TIMER_RUN_FOREVER) + { + timer[i].enabled = false; + } + } +} + +void SAMDUE_ISR_Timer::toggle(unsigned numTimer) +{ + if (numTimer >= MAX_NUMBER_TIMERS) + { + return; + } + + timer[numTimer].enabled = !timer[numTimer].enabled; +} + + +unsigned SAMDUE_ISR_Timer::getNumTimers() +{ + return numTimers; +} diff --git a/src/SAMDUE_ISR_Timer.h b/src/SAMDUE_ISR_Timer.h new file mode 100644 index 0000000..c98277a --- /dev/null +++ b/src/SAMDUE_ISR_Timer.h @@ -0,0 +1,170 @@ +/**************************************************************************************************************************** + SAMDUE_ISR_Timer.h + For SAM DUE boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/SAMDUE_TimerInterrupt + Licensed under MIT license + + Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by + unsigned long miliseconds), you just consume only one SAM DUE timer and avoid conflicting with other cores' tasks. + The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers + Therefore, their executions are not blocked by bad-behaving functions / tasks. + This important feature is absolutely necessary for mission-critical tasks. + + Based on SimpleTimer - A timer library for Arduino. + Author: mromani@ottotecnica.com + Copyright (c) 2010 OTTOTECNICA Italy + + Based on BlynkTimer.h + Author: Volodymyr Shymanskyy + + Version: 1.0.1 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.1 K Hoang 06/11/2020 Initial coding +*****************************************************************************************************************************/ + +#pragma once + +#if !( defined(ARDUINO_SAM_DUE) || defined(__SAM3X8E__) ) + #error This code is designed to run on SAM DUE board / platform! Please check your Tools->Board setting. +#endif + +#include + +#include + +#if defined(ARDUINO) + #if ARDUINO >= 100 + #include + #else + #include + #endif +#endif + +#define SAMDUE_ISR_Timer SAMDUE_ISRTimer + +typedef void (*timerCallback)(); +typedef void (*timerCallback_p)(void *); + +class SAMDUE_ISR_Timer +{ + + public: + // maximum number of timers +#define MAX_NUMBER_TIMERS 16 +#define TIMER_RUN_FOREVER 0 +#define TIMER_RUN_ONCE 1 + + // constructor + SAMDUE_ISR_Timer(); + + void init(); + + // this function must be called inside loop() + void run(); + + // Timer will call function 'f' every 'd' milliseconds forever + // returns the timer number (numTimer) on success or + // -1 on failure (f == NULL) or no free timers + int setInterval(unsigned long d, timerCallback f); + + // Timer will call function 'f' with parameter 'p' every 'd' milliseconds forever + // returns the timer number (numTimer) on success or + // -1 on failure (f == NULL) or no free timers + int setInterval(unsigned long d, timerCallback_p f, void* p); + + // Timer will call function 'f' after 'd' milliseconds one time + // returns the timer number (numTimer) on success or + // -1 on failure (f == NULL) or no free timers + int setTimeout(unsigned long d, timerCallback f); + + // Timer will call function 'f' with parameter 'p' after 'd' milliseconds one time + // returns the timer number (numTimer) on success or + // -1 on failure (f == NULL) or no free timers + int setTimeout(unsigned long d, timerCallback_p f, void* p); + + // Timer will call function 'f' every 'd' milliseconds 'n' times + // returns the timer number (numTimer) on success or + // -1 on failure (f == NULL) or no free timers + int setTimer(unsigned long d, timerCallback f, unsigned n); + + // Timer will call function 'f' with parameter 'p' every 'd' milliseconds 'n' times + // returns the timer number (numTimer) on success or + // -1 on failure (f == NULL) or no free timers + int setTimer(unsigned long d, timerCallback_p f, void* p, unsigned n); + + // updates interval of the specified timer + bool changeInterval(unsigned numTimer, unsigned long d); + + // destroy the specified timer + void deleteTimer(unsigned numTimer); + + // restart the specified timer + void restartTimer(unsigned numTimer); + + // returns true if the specified timer is enabled + bool isEnabled(unsigned numTimer); + + // enables the specified timer + void enable(unsigned numTimer); + + // disables the specified timer + void disable(unsigned numTimer); + + // enables all timers + void enableAll(); + + // disables all timers + void disableAll(); + + // enables the specified timer if it's currently disabled, and vice-versa + void toggle(unsigned numTimer); + + // returns the number of used timers + unsigned getNumTimers(); + + // returns the number of available timers + unsigned getNumAvailableTimers() + { + return MAX_NUMBER_TIMERS - numTimers; + }; + + private: + // deferred call constants +#define TIMER_DEFCALL_DONTRUN 0 // don't call the callback function +#define TIMER_DEFCALL_RUNONLY 1 // call the callback function but don't delete the timer +#define TIMER_DEFCALL_RUNANDDEL 2 // call the callback function and delete the timer + + // low level function to initialize and enable a new timer + // returns the timer number (numTimer) on success or + // -1 on failure (f == NULL) or no free timers + int setupTimer(unsigned long d, void* f, void* p, bool h, unsigned n); + + // find the first available slot + int findFirstFreeSlot(); + + typedef struct + { + unsigned long prev_millis; // value returned by the millis() function in the previous run() call + void* callback; // pointer to the callback function + void* param; // function parameter + bool hasParam; // true if callback takes a parameter + unsigned long delay; // delay value + unsigned maxNumRuns; // number of runs to be executed + unsigned numRuns; // number of executed runs + bool enabled; // true if enabled + unsigned toBeCalled; // deferred function call (sort of) - N.B.: only used in run() + } timer_t; + + volatile timer_t timer[MAX_NUMBER_TIMERS]; + + // actual number of timers in use (-1 means uninitialized) + volatile int numTimers; +}; + + +#include "SAMDUE_ISR_Timer-Impl.h" + diff --git a/src_cpp/SAMDUETimerInterrupt.h b/src_cpp/SAMDUETimerInterrupt.h new file mode 100644 index 0000000..3514179 --- /dev/null +++ b/src_cpp/SAMDUETimerInterrupt.h @@ -0,0 +1,607 @@ +/**************************************************************************************************************************** + SAMDUETimerInterrupt.h + For SAM DUE boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/SAMDUE_TimerInterrupt + Licensed under MIT license + + Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by + unsigned long miliseconds), you just consume only one SAM DUE timer and avoid conflicting with other cores' tasks. + The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers + Therefore, their executions are not blocked by bad-behaving functions / tasks. + This important feature is absolutely necessary for mission-critical tasks. + + Based on SimpleTimer - A timer library for Arduino. + Author: mromani@ottotecnica.com + Copyright (c) 2010 OTTOTECNICA Italy + + Based on BlynkTimer.h + Author: Volodymyr Shymanskyy + + Version: 1.0.1 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.1 K Hoang 06/11/2020 Initial coding +*****************************************************************************************************************************/ + +#pragma once + +#if !( defined(ARDUINO_SAM_DUE) || defined(__SAM3X8E__) ) + #error This code is designed to run on SAM DUE board / platform! Please check your Tools->Board setting. +#endif + +#include "Arduino.h" +#include + +#define SAMDUE_TIMER_INTERRUPT_VERSION "1.0.1" + +#ifndef SAMDUE_TIMER_INTERRUPT_DEBUG + #define SAMDUE_TIMER_INTERRUPT_DEBUG 0 +#endif + +#ifdef BOARD_NAME + #undef BOARD_NAME +#endif + +#define BOARD_NAME "SAM DUE" + +/* + This fixes compatibility for Arduino Servo Library. + Uncomment to make it compatible. + + Note that: + + Timers: 0,2,3,4,5 WILL NOT WORK, and will + neither be accessible by Timer0,... +*/ +// #define USING_SERVO_LIB true + +#if USING_SERVO_LIB + // Arduino Servo library uses timers 0,2,3,4,5. + // You must have `#define USING_SERVO_LIB true` in your sketch. + #warning Using Servo Library, Timer0, 2, 3, 4 and 5 not available +#endif + +#if defined TC2 + #define NUM_TIMERS 9 +#else + #define NUM_TIMERS 6 +#endif + +typedef void (*timerCallback) (); + +typedef struct +{ + Tc *tc; + uint32_t channel; + IRQn_Type irq; +} DueTimerIRQInfo; + +typedef struct +{ + const char* tc; + uint32_t channel; + const char* irq; +} DueTimerIRQInfoStr; + +// For printing info of selected Timer +const DueTimerIRQInfoStr TimersInfo[NUM_TIMERS] = +{ + { "TC0", 0, "TC0_IRQn" }, + { "TC0", 1, "TC1_IRQn" }, + { "TC0", 2, "TC2_IRQn" }, + { "TC1", 0, "TC3_IRQn" }, + { "TC1", 1, "TC4_IRQn" }, + { "TC1", 2, "TC5_IRQn" }, + +#if defined(TC2) + { "TC2", 0, "TC6_IRQn" }, + { "TC2", 1, "TC7_IRQn" }, + { "TC2", 2, "TC8_IRQn" }, +#endif +}; + +class DueTimerInterrupt +{ + protected: + + // Represents the timer id (index for the array of DueTimerIRQInfo structs) + const unsigned short _timerNumber; + + // Stores the object timer frequency + // (allows to access current timer period and frequency): + static double _frequency[NUM_TIMERS]; + + // Make Interrupt handlers friends, so they can use _callbacks + friend void TC0_Handler(); + friend void TC1_Handler(); + friend void TC2_Handler(); + friend void TC3_Handler(); + friend void TC4_Handler(); + friend void TC5_Handler(); + +#if defined(TC2) + friend void TC6_Handler(); + friend void TC7_Handler(); + friend void TC8_Handler(); +#endif + + static timerCallback _callbacks[NUM_TIMERS]; + + // Store timer configuration (static, as it's fixed for every object) + static const DueTimerIRQInfo Timers[NUM_TIMERS]; + + public: + + DueTimerInterrupt(unsigned short timer) : _timerNumber(timer) + { + /* + The constructor of the class DueTimerInterrupt + */ + } + + static DueTimerInterrupt getAvailable() __attribute__((always_inline)) + { + /* + Return the first timer with no callback set + */ + + for (int i = 0; i < NUM_TIMERS; i++) + { + if (!_callbacks[i]) + { +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 0) + // Get data from TimersInfo[NUM_TIMERS] + Serial.print("Using Timer("); + Serial.print(i); + Serial.print(") = "); + Serial.print(TimersInfo[i].tc); + Serial.print(", channel = "); + Serial.print(TimersInfo[i].channel); + Serial.print(", IRQ = "); + Serial.println(TimersInfo[i].irq); +#endif + return DueTimerInterrupt(i); + } + } + + // Default, return Timer0; + return DueTimerInterrupt(0); + } + + DueTimerInterrupt& attachInterruptInterval(double microseconds, timerCallback callback) __attribute__((always_inline)) + { + _callbacks[_timerNumber] = callback; + + return startTimer(microseconds); + } + + DueTimerInterrupt& attachInterrupt(float frequency, timerCallback callback) __attribute__((always_inline)) + { + return attachInterruptInterval((double) (1000000.0f / frequency), callback); + } + + DueTimerInterrupt& attachInterrupt(timerCallback callback) __attribute__((always_inline)) + { + /* + Links the function passed as argument to the timer of the object + */ + + _callbacks[_timerNumber] = callback; + + return *this; + } + + DueTimerInterrupt& detachInterrupt() __attribute__((always_inline)) + { + /* + Links the function passed as argument to the timer of the object + */ + + stopTimer(); // Stop the currently running timer + + _callbacks[_timerNumber] = NULL; + + return *this; + } + + DueTimerInterrupt& startTimer(double microseconds= -1) __attribute__((always_inline)) + { + /* + Start the timer + If a period is set, then sets the period and start the timer + If not period => default to 1Hz + */ + + if (microseconds > 0) + setPeriod(microseconds); + + if (_frequency[_timerNumber] <= 0) + setFrequency(1); + + NVIC_ClearPendingIRQ(Timers[_timerNumber].irq); + NVIC_EnableIRQ(Timers[_timerNumber].irq); + + TC_Start(Timers[_timerNumber].tc, Timers[_timerNumber].channel); + + return *this; + } + + DueTimerInterrupt& restartTimer(double microseconds= -1) __attribute__((always_inline)) + { + /* + Restart the timer + If a period is set, then sets the period and start the timer + If not period => default to 1Hz + */ + // If not yet initialized, set 1Hz + if (_frequency[_timerNumber] <= 0) + { + setFrequency(1); + } + else if (microseconds < 0) + { + // Using previous settings if no argument (microseconds = -1) + setFrequency(_frequency[_timerNumber]); + } + else + { + setPeriod(microseconds); + } + + NVIC_ClearPendingIRQ(Timers[_timerNumber].irq); + NVIC_EnableIRQ(Timers[_timerNumber].irq); + + TC_Start(Timers[_timerNumber].tc, Timers[_timerNumber].channel); + + return *this; + } + + DueTimerInterrupt& stopTimer() __attribute__((always_inline)) + { + /* + Stop the timer + */ + + NVIC_DisableIRQ(Timers[_timerNumber].irq); + + TC_Stop(Timers[_timerNumber].tc, Timers[_timerNumber].channel); + + return *this; + } + + DueTimerInterrupt& disableTimer() + { + return stopTimer(); + } + + // Picks the best clock to lower the error + static uint8_t bestClock(double frequency, uint32_t& retRC) + { + /* + Pick the best Clock, thanks to Ogle Basil Hall! + + Timer Definition + TIMER_CLOCK1 MCK / 2 + TIMER_CLOCK2 MCK / 8 + TIMER_CLOCK3 MCK / 32 + TIMER_CLOCK4 MCK /128 + */ + const struct + { + uint8_t flag; + uint8_t divisor; + } clockConfig[] = + { + { TC_CMR_TCCLKS_TIMER_CLOCK1, 2 }, + { TC_CMR_TCCLKS_TIMER_CLOCK2, 8 }, + { TC_CMR_TCCLKS_TIMER_CLOCK3, 32 }, + { TC_CMR_TCCLKS_TIMER_CLOCK4, 128 } + }; + + float ticks; + float error; + int clkId = 3; + int bestClock = 3; + float bestError = 9.999e99; + + do + { + ticks = (float) SystemCoreClock / frequency / (float) clockConfig[clkId].divisor; + // error = abs(ticks - round(ticks)); + error = clockConfig[clkId].divisor * abs(ticks - round(ticks)); // Error comparison needs scaling + + if (error < bestError) + { + bestClock = clkId; + bestError = error; + } + } while (clkId-- > 0); + + ticks = (float) SystemCoreClock / frequency / (float) clockConfig[bestClock].divisor; + retRC = (uint32_t) round(ticks); + + return clockConfig[bestClock].flag; + } + + + DueTimerInterrupt& setFrequency(double frequency) + { + /* + Set the timer frequency (in Hz) + */ + + // Prevent negative frequencies + if (frequency <= 0) + { + frequency = 1; + } + + // Remember the frequency — see below how the exact frequency is reported instead + //_frequency[_timerNumber] = frequency; + + // Get current timer configuration + DueTimerIRQInfo timerIRQInfo = Timers[_timerNumber]; + + uint32_t rc = 0; + uint8_t clock; + + // Tell the Power Management Controller to disable + // the write protection of the (Timer/Counter) registers: + pmc_set_writeprotect(false); + + // Enable clock for the timer + pmc_enable_periph_clk((uint32_t)timerIRQInfo.irq); + + // Find the best clock for the wanted frequency + clock = bestClock(frequency, rc); + + switch (clock) + { + case TC_CMR_TCCLKS_TIMER_CLOCK1: + _frequency[_timerNumber] = (double)SystemCoreClock / 2.0 / (double)rc; + break; + case TC_CMR_TCCLKS_TIMER_CLOCK2: + _frequency[_timerNumber] = (double)SystemCoreClock / 8.0 / (double)rc; + break; + case TC_CMR_TCCLKS_TIMER_CLOCK3: + _frequency[_timerNumber] = (double)SystemCoreClock / 32.0 / (double)rc; + break; + default: // TC_CMR_TCCLKS_TIMER_CLOCK4 + _frequency[_timerNumber] = (double)SystemCoreClock / 128.0 / (double)rc; + break; + } + + // Set up the Timer in waveform mode which creates a PWM + // in UP mode with automatic trigger on RC Compare + // and sets it up with the determined internal clock as clock input. + TC_Configure(timerIRQInfo.tc, timerIRQInfo.channel, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | clock); + + // Reset counter and fire interrupt when RC value is matched: + TC_SetRC(timerIRQInfo.tc, timerIRQInfo.channel, rc); + + // Enable the RC Compare Interrupt. + timerIRQInfo.tc->TC_CHANNEL[timerIRQInfo.channel].TC_IER = TC_IER_CPCS; + + // ... and disable all others. + timerIRQInfo.tc->TC_CHANNEL[timerIRQInfo.channel].TC_IDR = ~TC_IER_CPCS; + + return *this; + } + + DueTimerInterrupt& setPeriod(double microseconds) __attribute__((always_inline)) + { + /* + Set the period of the timer (in microseconds) + */ + + // Convert period in microseconds to frequency in Hz + double frequency = 1000000.0 / microseconds; + + setFrequency(frequency); + + return *this; + } + + DueTimerInterrupt& setInterval(double microseconds) __attribute__((always_inline)) + { + return setPeriod(microseconds); + } + + double getFrequency() const __attribute__((always_inline)) + { + /* + Get current time frequency + */ + return _frequency[_timerNumber]; + } + + double getPeriod() const __attribute__((always_inline)) + { + /* + Get current time period + */ + return 1.0 / getFrequency() * 1000000; + } + + uint16_t getTimerNumber() + { + return _timerNumber; + } + + bool operator== (const DueTimerInterrupt& rhs) const __attribute__((always_inline)) + { + return _timerNumber == rhs._timerNumber; + }; + + bool operator!= (const DueTimerInterrupt& rhs) const __attribute__((always_inline)) + { + return _timerNumber != rhs._timerNumber; + }; +}; + + + +//////////////////////////////////////////////////////////////////// + +const DueTimerIRQInfo DueTimerInterrupt::Timers[NUM_TIMERS] = +{ + { TC0, 0, TC0_IRQn }, + { TC0, 1, TC1_IRQn }, + { TC0, 2, TC2_IRQn }, + { TC1, 0, TC3_IRQn }, + { TC1, 1, TC4_IRQn }, + { TC1, 2, TC5_IRQn }, + +#if defined(TC2) + { TC2, 0, TC6_IRQn }, + { TC2, 1, TC7_IRQn }, + { TC2, 2, TC8_IRQn }, +#endif +}; + +// Fix for compatibility with Servo library +#if USING_SERVO_LIB + // Set _callbacks as used, allowing DueTimerInterrupt::getAvailable() to work + void (*DueTimerInterrupt::_callbacks[NUM_TIMERS])() = + { + (void (*)()) 1, // Timer 0 - Occupied + (void (*)()) 0, // Timer 1 + (void (*)()) 1, // Timer 2 - Occupied + (void (*)()) 1, // Timer 3 - Occupied + (void (*)()) 1, // Timer 4 - Occupied + (void (*)()) 1, // Timer 5 - Occupied + + #if defined(TC2) + (void (*)()) 0, // Timer 6 + (void (*)()) 0, // Timer 7 + (void (*)()) 0 // Timer 8 + #endif + }; + +#else + void (*DueTimerInterrupt::_callbacks[NUM_TIMERS])() = {}; +#endif + +#if defined(TC2) + double DueTimerInterrupt::_frequency[NUM_TIMERS] = { -1, -1, -1, -1, -1, -1, -1, -1, -1}; +#else + double DueTimerInterrupt::_frequency[NUM_TIMERS] = { -1, -1, -1, -1, -1, -1}; +#endif + +/* + Initializing all timers, so you can use them like this: Timer0.startTimer(); +*/ +DueTimerInterrupt DueTimer(0); + +DueTimerInterrupt Timer1(1); + +// Fix for compatibility with Servo library +#if ( !USING_SERVO_LIB || !defined(USING_SERVO_LIB) ) + DueTimerInterrupt Timer0(0); + DueTimerInterrupt Timer2(2); + DueTimerInterrupt Timer3(3); + DueTimerInterrupt Timer4(4); + DueTimerInterrupt Timer5(5); +#endif + +#if defined(TC2) + DueTimerInterrupt Timer6(6); + DueTimerInterrupt Timer7(7); + DueTimerInterrupt Timer8(8); +#endif + +DueTimerInterrupt DueTimerPtr[NUM_TIMERS] = +{ +#if ( !USING_SERVO_LIB || !defined(USING_SERVO_LIB) ) + Timer0, +#endif + + Timer1, + +#if ( !USING_SERVO_LIB || !defined(USING_SERVO_LIB) ) + Timer2, + Timer3, + Timer4, + Timer5, +#endif + +#if defined(TC2) + Timer6, + Timer7, + Timer8 +#endif +}; + +/////////////////////////////////////////////////////////////////////// + +/* + Implementation of the timer _callbacks defined in + arduino-1.5.2/hardware/arduino/sam/system/CMSIS/Device/ATMEL/sam3xa/include/sam3x8e.h +*/ +// Fix for compatibility with Servo library +#if ( !USING_SERVO_LIB || !defined(USING_SERVO_LIB) ) + +void TC0_Handler() +{ + TC_GetStatus(TC0, 0); + DueTimerInterrupt::_callbacks[0](); +} + +#endif + +void TC1_Handler() +{ + TC_GetStatus(TC0, 1); + DueTimerInterrupt::_callbacks[1](); +} + +// Fix for compatibility with Servo library +#if ( !USING_SERVO_LIB || !defined(USING_SERVO_LIB) ) + +void TC2_Handler() +{ + TC_GetStatus(TC0, 2); + DueTimerInterrupt::_callbacks[2](); +} + +void TC3_Handler() +{ + TC_GetStatus(TC1, 0); + DueTimerInterrupt::_callbacks[3](); +} + +void TC4_Handler() +{ + TC_GetStatus(TC1, 1); + DueTimerInterrupt::_callbacks[4](); +} + +void TC5_Handler() +{ + TC_GetStatus(TC1, 2); + DueTimerInterrupt::_callbacks[5](); +} +#endif + +#if defined(TC2) + +void TC6_Handler() +{ + TC_GetStatus(TC2, 0); + DueTimerInterrupt::_callbacks[6](); +} + +void TC7_Handler() +{ + TC_GetStatus(TC2, 1); + DueTimerInterrupt::_callbacks[7](); +} + +void TC8_Handler() +{ + TC_GetStatus(TC2, 2); + DueTimerInterrupt::_callbacks[8](); +} +#endif diff --git a/src_cpp/SAMDUE_ISR_Timer.cpp b/src_cpp/SAMDUE_ISR_Timer.cpp new file mode 100644 index 0000000..08e8d75 --- /dev/null +++ b/src_cpp/SAMDUE_ISR_Timer.cpp @@ -0,0 +1,335 @@ +/**************************************************************************************************************************** + SAMDUE_ISR_Timer.cpp + For SAM DUE boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/SAMDUE_TimerInterrupt + Licensed under MIT license + + Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by + unsigned long miliseconds), you just consume only one SAM DUE timer and avoid conflicting with other cores' tasks. + The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers + Therefore, their executions are not blocked by bad-behaving functions / tasks. + This important feature is absolutely necessary for mission-critical tasks. + + Based on SimpleTimer - A timer library for Arduino. + Author: mromani@ottotecnica.com + Copyright (c) 2010 OTTOTECNICA Italy + + Based on BlynkTimer.h + Author: Volodymyr Shymanskyy + + Version: 1.0.1 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.1 K Hoang 06/11/2020 Initial coding +*****************************************************************************************************************************/ + +#include "SAMDUE_ISR_Timer.h" +#include + +SAMDUE_ISR_Timer::SAMDUE_ISR_Timer() + : numTimers (-1) +{ +} + +void SAMDUE_ISR_Timer::init() +{ + unsigned long current_millis = millis(); //elapsed(); + + for (int i = 0; i < MAX_NUMBER_TIMERS; i++) + { + memset((void*) &timer[i], 0, sizeof (timer_t)); + timer[i].prev_millis = current_millis; + } + + numTimers = 0; +} + +void SAMDUE_ISR_Timer::run() +{ + int i; + unsigned long current_millis; + + // get current time + current_millis = millis(); //elapsed(); + + for (i = 0; i < MAX_NUMBER_TIMERS; i++) + { + + timer[i].toBeCalled = TIMER_DEFCALL_DONTRUN; + + // no callback == no timer, i.e. jump over empty slots + if (timer[i].callback != NULL) + { + + // is it time to process this timer ? + // see http://arduino.cc/forum/index.php/topic,124048.msg932592.html#msg932592 + + if ((current_millis - timer[i].prev_millis) >= timer[i].delay) + { + unsigned long skipTimes = (current_millis - timer[i].prev_millis) / timer[i].delay; + + // update time + timer[i].prev_millis += timer[i].delay * skipTimes; + + // check if the timer callback has to be executed + if (timer[i].enabled) + { + + // "run forever" timers must always be executed + if (timer[i].maxNumRuns == TIMER_RUN_FOREVER) + { + timer[i].toBeCalled = TIMER_DEFCALL_RUNONLY; + } + // other timers get executed the specified number of times + else if (timer[i].numRuns < timer[i].maxNumRuns) + { + timer[i].toBeCalled = TIMER_DEFCALL_RUNONLY; + timer[i].numRuns++; + + // after the last run, delete the timer + if (timer[i].numRuns >= timer[i].maxNumRuns) + { + timer[i].toBeCalled = TIMER_DEFCALL_RUNANDDEL; + } + } + } + } + } + } + + for (i = 0; i < MAX_NUMBER_TIMERS; i++) + { + if (timer[i].toBeCalled == TIMER_DEFCALL_DONTRUN) + continue; + + if (timer[i].hasParam) + (*(timerCallback_p)timer[i].callback)(timer[i].param); + else + (*(timerCallback)timer[i].callback)(); + + if (timer[i].toBeCalled == TIMER_DEFCALL_RUNANDDEL) + deleteTimer(i); + } +} + + +// find the first available slot +// return -1 if none found +int SAMDUE_ISR_Timer::findFirstFreeSlot() +{ + // all slots are used + if (numTimers >= MAX_NUMBER_TIMERS) + { + return -1; + } + + // return the first slot with no callback (i.e. free) + for (int i = 0; i < MAX_NUMBER_TIMERS; i++) + { + if (timer[i].callback == NULL) + { + return i; + } + } + + // no free slots found + return -1; +} + + +int SAMDUE_ISR_Timer::setupTimer(unsigned long d, void* f, void* p, bool h, unsigned n) +{ + int freeTimer; + + if (numTimers < 0) + { + init(); + } + + freeTimer = findFirstFreeSlot(); + if (freeTimer < 0) + { + return -1; + } + + if (f == NULL) + { + return -1; + } + + timer[freeTimer].delay = d; + timer[freeTimer].callback = f; + timer[freeTimer].param = p; + timer[freeTimer].hasParam = h; + timer[freeTimer].maxNumRuns = n; + timer[freeTimer].enabled = true; + timer[freeTimer].prev_millis = millis(); + + numTimers++; + + return freeTimer; +} + + +int SAMDUE_ISR_Timer::setTimer(unsigned long d, timerCallback f, unsigned n) +{ + return setupTimer(d, (void *)f, NULL, false, n); +} + +int SAMDUE_ISR_Timer::setTimer(unsigned long d, timerCallback_p f, void* p, unsigned n) +{ + return setupTimer(d, (void *)f, p, true, n); +} + +int SAMDUE_ISR_Timer::setInterval(unsigned long d, timerCallback f) +{ + return setupTimer(d, (void *)f, NULL, false, TIMER_RUN_FOREVER); +} + +int SAMDUE_ISR_Timer::setInterval(unsigned long d, timerCallback_p f, void* p) +{ + return setupTimer(d, (void *)f, p, true, TIMER_RUN_FOREVER); +} + +int SAMDUE_ISR_Timer::setTimeout(unsigned long d, timerCallback f) +{ + return setupTimer(d, (void *)f, NULL, false, TIMER_RUN_ONCE); +} + +int SAMDUE_ISR_Timer::setTimeout(unsigned long d, timerCallback_p f, void* p) +{ + return setupTimer(d, (void *)f, p, true, TIMER_RUN_ONCE); +} + +bool SAMDUE_ISR_Timer::changeInterval(unsigned numTimer, unsigned long d) +{ + if (numTimer >= MAX_NUMBER_TIMERS) + { + return false; + } + + // Updates interval of existing specified timer + if (timer[numTimer].callback != NULL) + { + timer[numTimer].delay = d; + timer[numTimer].prev_millis = millis(); + + return true; + } + + // false return for non-used numTimer, no callback + return false; +} + +void SAMDUE_ISR_Timer::deleteTimer(unsigned timerId) +{ + if (timerId >= MAX_NUMBER_TIMERS) + { + return; + } + + // nothing to delete if no timers are in use + if (numTimers == 0) + { + return; + } + + // don't decrease the number of timers if the specified slot is already empty + if (timer[timerId].callback != NULL) + { + memset((void*) &timer[timerId], 0, sizeof (timer_t)); + timer[timerId].prev_millis = millis(); + + // update number of timers + numTimers--; + } +} + +// function contributed by code@rowansimms.com +void SAMDUE_ISR_Timer::restartTimer(unsigned numTimer) +{ + if (numTimer >= MAX_NUMBER_TIMERS) + { + return; + } + + timer[numTimer].prev_millis = millis(); +} + + +bool SAMDUE_ISR_Timer::isEnabled(unsigned numTimer) +{ + if (numTimer >= MAX_NUMBER_TIMERS) + { + return false; + } + + return timer[numTimer].enabled; +} + + +void SAMDUE_ISR_Timer::enable(unsigned numTimer) +{ + if (numTimer >= MAX_NUMBER_TIMERS) + { + return; + } + + timer[numTimer].enabled = true; +} + + +void SAMDUE_ISR_Timer::disable(unsigned numTimer) +{ + if (numTimer >= MAX_NUMBER_TIMERS) + { + return; + } + + timer[numTimer].enabled = false; +} + +void SAMDUE_ISR_Timer::enableAll() +{ + // Enable all timers with a callback assigned (used) + + for (int i = 0; i < MAX_NUMBER_TIMERS; i++) + { + if (timer[i].callback != NULL && timer[i].numRuns == TIMER_RUN_FOREVER) + { + timer[i].enabled = true; + } + } +} + +void SAMDUE_ISR_Timer::disableAll() +{ + // Disable all timers with a callback assigned (used) + + for (int i = 0; i < MAX_NUMBER_TIMERS; i++) + { + if (timer[i].callback != NULL && timer[i].numRuns == TIMER_RUN_FOREVER) + { + timer[i].enabled = false; + } + } +} + +void SAMDUE_ISR_Timer::toggle(unsigned numTimer) +{ + if (numTimer >= MAX_NUMBER_TIMERS) + { + return; + } + + timer[numTimer].enabled = !timer[numTimer].enabled; +} + + +unsigned SAMDUE_ISR_Timer::getNumTimers() +{ + return numTimers; +} diff --git a/src_cpp/SAMDUE_ISR_Timer.h b/src_cpp/SAMDUE_ISR_Timer.h new file mode 100644 index 0000000..b6971de --- /dev/null +++ b/src_cpp/SAMDUE_ISR_Timer.h @@ -0,0 +1,168 @@ +/**************************************************************************************************************************** + SAMDUE_ISR_Timer.h + For SAM DUE boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/SAMDUE_TimerInterrupt + Licensed under MIT license + + Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by + unsigned long miliseconds), you just consume only one SAM DUE timer and avoid conflicting with other cores' tasks. + The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers + Therefore, their executions are not blocked by bad-behaving functions / tasks. + This important feature is absolutely necessary for mission-critical tasks. + + Based on SimpleTimer - A timer library for Arduino. + Author: mromani@ottotecnica.com + Copyright (c) 2010 OTTOTECNICA Italy + + Based on BlynkTimer.h + Author: Volodymyr Shymanskyy + + Version: 1.0.1 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.1 K Hoang 06/11/2020 Initial coding +*****************************************************************************************************************************/ + +#pragma once + +#if !( defined(ARDUINO_SAM_DUE) || defined(__SAM3X8E__) ) + #error This code is designed to run on SAM DUE board / platform! Please check your Tools->Board setting. +#endif + +#include + +#include + +#if defined(ARDUINO) + #if ARDUINO >= 100 + #include + #else + #include + #endif +#endif + +#define SAMDUE_ISR_Timer SAMDUE_ISRTimer + +typedef void (*timerCallback)(); +typedef void (*timerCallback_p)(void *); + +class SAMDUE_ISR_Timer +{ + + public: + // maximum number of timers +#define MAX_NUMBER_TIMERS 16 +#define TIMER_RUN_FOREVER 0 +#define TIMER_RUN_ONCE 1 + + // constructor + SAMDUE_ISR_Timer(); + + void init(); + + // this function must be called inside loop() + void run(); + + // Timer will call function 'f' every 'd' milliseconds forever + // returns the timer number (numTimer) on success or + // -1 on failure (f == NULL) or no free timers + int setInterval(unsigned long d, timerCallback f); + + // Timer will call function 'f' with parameter 'p' every 'd' milliseconds forever + // returns the timer number (numTimer) on success or + // -1 on failure (f == NULL) or no free timers + int setInterval(unsigned long d, timerCallback_p f, void* p); + + // Timer will call function 'f' after 'd' milliseconds one time + // returns the timer number (numTimer) on success or + // -1 on failure (f == NULL) or no free timers + int setTimeout(unsigned long d, timerCallback f); + + // Timer will call function 'f' with parameter 'p' after 'd' milliseconds one time + // returns the timer number (numTimer) on success or + // -1 on failure (f == NULL) or no free timers + int setTimeout(unsigned long d, timerCallback_p f, void* p); + + // Timer will call function 'f' every 'd' milliseconds 'n' times + // returns the timer number (numTimer) on success or + // -1 on failure (f == NULL) or no free timers + int setTimer(unsigned long d, timerCallback f, unsigned n); + + // Timer will call function 'f' with parameter 'p' every 'd' milliseconds 'n' times + // returns the timer number (numTimer) on success or + // -1 on failure (f == NULL) or no free timers + int setTimer(unsigned long d, timerCallback_p f, void* p, unsigned n); + + // updates interval of the specified timer + bool changeInterval(unsigned numTimer, unsigned long d); + + // destroy the specified timer + void deleteTimer(unsigned numTimer); + + // restart the specified timer + void restartTimer(unsigned numTimer); + + // returns true if the specified timer is enabled + bool isEnabled(unsigned numTimer); + + // enables the specified timer + void enable(unsigned numTimer); + + // disables the specified timer + void disable(unsigned numTimer); + + // enables all timers + void enableAll(); + + // disables all timers + void disableAll(); + + // enables the specified timer if it's currently disabled, and vice-versa + void toggle(unsigned numTimer); + + // returns the number of used timers + unsigned getNumTimers(); + + // returns the number of available timers + unsigned getNumAvailableTimers() + { + return MAX_NUMBER_TIMERS - numTimers; + }; + + private: + // deferred call constants +#define TIMER_DEFCALL_DONTRUN 0 // don't call the callback function +#define TIMER_DEFCALL_RUNONLY 1 // call the callback function but don't delete the timer +#define TIMER_DEFCALL_RUNANDDEL 2 // call the callback function and delete the timer + + // low level function to initialize and enable a new timer + // returns the timer number (numTimer) on success or + // -1 on failure (f == NULL) or no free timers + int setupTimer(unsigned long d, void* f, void* p, bool h, unsigned n); + + // find the first available slot + int findFirstFreeSlot(); + + typedef struct + { + unsigned long prev_millis; // value returned by the millis() function in the previous run() call + void* callback; // pointer to the callback function + void* param; // function parameter + bool hasParam; // true if callback takes a parameter + unsigned long delay; // delay value + unsigned maxNumRuns; // number of runs to be executed + unsigned numRuns; // number of executed runs + bool enabled; // true if enabled + unsigned toBeCalled; // deferred function call (sort of) - N.B.: only used in run() + } timer_t; + + volatile timer_t timer[MAX_NUMBER_TIMERS]; + + // actual number of timers in use (-1 means uninitialized) + volatile int numTimers; +}; + + diff --git a/src_h/SAMDUETimerInterrupt.h b/src_h/SAMDUETimerInterrupt.h new file mode 100644 index 0000000..3514179 --- /dev/null +++ b/src_h/SAMDUETimerInterrupt.h @@ -0,0 +1,607 @@ +/**************************************************************************************************************************** + SAMDUETimerInterrupt.h + For SAM DUE boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/SAMDUE_TimerInterrupt + Licensed under MIT license + + Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by + unsigned long miliseconds), you just consume only one SAM DUE timer and avoid conflicting with other cores' tasks. + The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers + Therefore, their executions are not blocked by bad-behaving functions / tasks. + This important feature is absolutely necessary for mission-critical tasks. + + Based on SimpleTimer - A timer library for Arduino. + Author: mromani@ottotecnica.com + Copyright (c) 2010 OTTOTECNICA Italy + + Based on BlynkTimer.h + Author: Volodymyr Shymanskyy + + Version: 1.0.1 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.1 K Hoang 06/11/2020 Initial coding +*****************************************************************************************************************************/ + +#pragma once + +#if !( defined(ARDUINO_SAM_DUE) || defined(__SAM3X8E__) ) + #error This code is designed to run on SAM DUE board / platform! Please check your Tools->Board setting. +#endif + +#include "Arduino.h" +#include + +#define SAMDUE_TIMER_INTERRUPT_VERSION "1.0.1" + +#ifndef SAMDUE_TIMER_INTERRUPT_DEBUG + #define SAMDUE_TIMER_INTERRUPT_DEBUG 0 +#endif + +#ifdef BOARD_NAME + #undef BOARD_NAME +#endif + +#define BOARD_NAME "SAM DUE" + +/* + This fixes compatibility for Arduino Servo Library. + Uncomment to make it compatible. + + Note that: + + Timers: 0,2,3,4,5 WILL NOT WORK, and will + neither be accessible by Timer0,... +*/ +// #define USING_SERVO_LIB true + +#if USING_SERVO_LIB + // Arduino Servo library uses timers 0,2,3,4,5. + // You must have `#define USING_SERVO_LIB true` in your sketch. + #warning Using Servo Library, Timer0, 2, 3, 4 and 5 not available +#endif + +#if defined TC2 + #define NUM_TIMERS 9 +#else + #define NUM_TIMERS 6 +#endif + +typedef void (*timerCallback) (); + +typedef struct +{ + Tc *tc; + uint32_t channel; + IRQn_Type irq; +} DueTimerIRQInfo; + +typedef struct +{ + const char* tc; + uint32_t channel; + const char* irq; +} DueTimerIRQInfoStr; + +// For printing info of selected Timer +const DueTimerIRQInfoStr TimersInfo[NUM_TIMERS] = +{ + { "TC0", 0, "TC0_IRQn" }, + { "TC0", 1, "TC1_IRQn" }, + { "TC0", 2, "TC2_IRQn" }, + { "TC1", 0, "TC3_IRQn" }, + { "TC1", 1, "TC4_IRQn" }, + { "TC1", 2, "TC5_IRQn" }, + +#if defined(TC2) + { "TC2", 0, "TC6_IRQn" }, + { "TC2", 1, "TC7_IRQn" }, + { "TC2", 2, "TC8_IRQn" }, +#endif +}; + +class DueTimerInterrupt +{ + protected: + + // Represents the timer id (index for the array of DueTimerIRQInfo structs) + const unsigned short _timerNumber; + + // Stores the object timer frequency + // (allows to access current timer period and frequency): + static double _frequency[NUM_TIMERS]; + + // Make Interrupt handlers friends, so they can use _callbacks + friend void TC0_Handler(); + friend void TC1_Handler(); + friend void TC2_Handler(); + friend void TC3_Handler(); + friend void TC4_Handler(); + friend void TC5_Handler(); + +#if defined(TC2) + friend void TC6_Handler(); + friend void TC7_Handler(); + friend void TC8_Handler(); +#endif + + static timerCallback _callbacks[NUM_TIMERS]; + + // Store timer configuration (static, as it's fixed for every object) + static const DueTimerIRQInfo Timers[NUM_TIMERS]; + + public: + + DueTimerInterrupt(unsigned short timer) : _timerNumber(timer) + { + /* + The constructor of the class DueTimerInterrupt + */ + } + + static DueTimerInterrupt getAvailable() __attribute__((always_inline)) + { + /* + Return the first timer with no callback set + */ + + for (int i = 0; i < NUM_TIMERS; i++) + { + if (!_callbacks[i]) + { +#if (SAMDUE_TIMER_INTERRUPT_DEBUG > 0) + // Get data from TimersInfo[NUM_TIMERS] + Serial.print("Using Timer("); + Serial.print(i); + Serial.print(") = "); + Serial.print(TimersInfo[i].tc); + Serial.print(", channel = "); + Serial.print(TimersInfo[i].channel); + Serial.print(", IRQ = "); + Serial.println(TimersInfo[i].irq); +#endif + return DueTimerInterrupt(i); + } + } + + // Default, return Timer0; + return DueTimerInterrupt(0); + } + + DueTimerInterrupt& attachInterruptInterval(double microseconds, timerCallback callback) __attribute__((always_inline)) + { + _callbacks[_timerNumber] = callback; + + return startTimer(microseconds); + } + + DueTimerInterrupt& attachInterrupt(float frequency, timerCallback callback) __attribute__((always_inline)) + { + return attachInterruptInterval((double) (1000000.0f / frequency), callback); + } + + DueTimerInterrupt& attachInterrupt(timerCallback callback) __attribute__((always_inline)) + { + /* + Links the function passed as argument to the timer of the object + */ + + _callbacks[_timerNumber] = callback; + + return *this; + } + + DueTimerInterrupt& detachInterrupt() __attribute__((always_inline)) + { + /* + Links the function passed as argument to the timer of the object + */ + + stopTimer(); // Stop the currently running timer + + _callbacks[_timerNumber] = NULL; + + return *this; + } + + DueTimerInterrupt& startTimer(double microseconds= -1) __attribute__((always_inline)) + { + /* + Start the timer + If a period is set, then sets the period and start the timer + If not period => default to 1Hz + */ + + if (microseconds > 0) + setPeriod(microseconds); + + if (_frequency[_timerNumber] <= 0) + setFrequency(1); + + NVIC_ClearPendingIRQ(Timers[_timerNumber].irq); + NVIC_EnableIRQ(Timers[_timerNumber].irq); + + TC_Start(Timers[_timerNumber].tc, Timers[_timerNumber].channel); + + return *this; + } + + DueTimerInterrupt& restartTimer(double microseconds= -1) __attribute__((always_inline)) + { + /* + Restart the timer + If a period is set, then sets the period and start the timer + If not period => default to 1Hz + */ + // If not yet initialized, set 1Hz + if (_frequency[_timerNumber] <= 0) + { + setFrequency(1); + } + else if (microseconds < 0) + { + // Using previous settings if no argument (microseconds = -1) + setFrequency(_frequency[_timerNumber]); + } + else + { + setPeriod(microseconds); + } + + NVIC_ClearPendingIRQ(Timers[_timerNumber].irq); + NVIC_EnableIRQ(Timers[_timerNumber].irq); + + TC_Start(Timers[_timerNumber].tc, Timers[_timerNumber].channel); + + return *this; + } + + DueTimerInterrupt& stopTimer() __attribute__((always_inline)) + { + /* + Stop the timer + */ + + NVIC_DisableIRQ(Timers[_timerNumber].irq); + + TC_Stop(Timers[_timerNumber].tc, Timers[_timerNumber].channel); + + return *this; + } + + DueTimerInterrupt& disableTimer() + { + return stopTimer(); + } + + // Picks the best clock to lower the error + static uint8_t bestClock(double frequency, uint32_t& retRC) + { + /* + Pick the best Clock, thanks to Ogle Basil Hall! + + Timer Definition + TIMER_CLOCK1 MCK / 2 + TIMER_CLOCK2 MCK / 8 + TIMER_CLOCK3 MCK / 32 + TIMER_CLOCK4 MCK /128 + */ + const struct + { + uint8_t flag; + uint8_t divisor; + } clockConfig[] = + { + { TC_CMR_TCCLKS_TIMER_CLOCK1, 2 }, + { TC_CMR_TCCLKS_TIMER_CLOCK2, 8 }, + { TC_CMR_TCCLKS_TIMER_CLOCK3, 32 }, + { TC_CMR_TCCLKS_TIMER_CLOCK4, 128 } + }; + + float ticks; + float error; + int clkId = 3; + int bestClock = 3; + float bestError = 9.999e99; + + do + { + ticks = (float) SystemCoreClock / frequency / (float) clockConfig[clkId].divisor; + // error = abs(ticks - round(ticks)); + error = clockConfig[clkId].divisor * abs(ticks - round(ticks)); // Error comparison needs scaling + + if (error < bestError) + { + bestClock = clkId; + bestError = error; + } + } while (clkId-- > 0); + + ticks = (float) SystemCoreClock / frequency / (float) clockConfig[bestClock].divisor; + retRC = (uint32_t) round(ticks); + + return clockConfig[bestClock].flag; + } + + + DueTimerInterrupt& setFrequency(double frequency) + { + /* + Set the timer frequency (in Hz) + */ + + // Prevent negative frequencies + if (frequency <= 0) + { + frequency = 1; + } + + // Remember the frequency — see below how the exact frequency is reported instead + //_frequency[_timerNumber] = frequency; + + // Get current timer configuration + DueTimerIRQInfo timerIRQInfo = Timers[_timerNumber]; + + uint32_t rc = 0; + uint8_t clock; + + // Tell the Power Management Controller to disable + // the write protection of the (Timer/Counter) registers: + pmc_set_writeprotect(false); + + // Enable clock for the timer + pmc_enable_periph_clk((uint32_t)timerIRQInfo.irq); + + // Find the best clock for the wanted frequency + clock = bestClock(frequency, rc); + + switch (clock) + { + case TC_CMR_TCCLKS_TIMER_CLOCK1: + _frequency[_timerNumber] = (double)SystemCoreClock / 2.0 / (double)rc; + break; + case TC_CMR_TCCLKS_TIMER_CLOCK2: + _frequency[_timerNumber] = (double)SystemCoreClock / 8.0 / (double)rc; + break; + case TC_CMR_TCCLKS_TIMER_CLOCK3: + _frequency[_timerNumber] = (double)SystemCoreClock / 32.0 / (double)rc; + break; + default: // TC_CMR_TCCLKS_TIMER_CLOCK4 + _frequency[_timerNumber] = (double)SystemCoreClock / 128.0 / (double)rc; + break; + } + + // Set up the Timer in waveform mode which creates a PWM + // in UP mode with automatic trigger on RC Compare + // and sets it up with the determined internal clock as clock input. + TC_Configure(timerIRQInfo.tc, timerIRQInfo.channel, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | clock); + + // Reset counter and fire interrupt when RC value is matched: + TC_SetRC(timerIRQInfo.tc, timerIRQInfo.channel, rc); + + // Enable the RC Compare Interrupt. + timerIRQInfo.tc->TC_CHANNEL[timerIRQInfo.channel].TC_IER = TC_IER_CPCS; + + // ... and disable all others. + timerIRQInfo.tc->TC_CHANNEL[timerIRQInfo.channel].TC_IDR = ~TC_IER_CPCS; + + return *this; + } + + DueTimerInterrupt& setPeriod(double microseconds) __attribute__((always_inline)) + { + /* + Set the period of the timer (in microseconds) + */ + + // Convert period in microseconds to frequency in Hz + double frequency = 1000000.0 / microseconds; + + setFrequency(frequency); + + return *this; + } + + DueTimerInterrupt& setInterval(double microseconds) __attribute__((always_inline)) + { + return setPeriod(microseconds); + } + + double getFrequency() const __attribute__((always_inline)) + { + /* + Get current time frequency + */ + return _frequency[_timerNumber]; + } + + double getPeriod() const __attribute__((always_inline)) + { + /* + Get current time period + */ + return 1.0 / getFrequency() * 1000000; + } + + uint16_t getTimerNumber() + { + return _timerNumber; + } + + bool operator== (const DueTimerInterrupt& rhs) const __attribute__((always_inline)) + { + return _timerNumber == rhs._timerNumber; + }; + + bool operator!= (const DueTimerInterrupt& rhs) const __attribute__((always_inline)) + { + return _timerNumber != rhs._timerNumber; + }; +}; + + + +//////////////////////////////////////////////////////////////////// + +const DueTimerIRQInfo DueTimerInterrupt::Timers[NUM_TIMERS] = +{ + { TC0, 0, TC0_IRQn }, + { TC0, 1, TC1_IRQn }, + { TC0, 2, TC2_IRQn }, + { TC1, 0, TC3_IRQn }, + { TC1, 1, TC4_IRQn }, + { TC1, 2, TC5_IRQn }, + +#if defined(TC2) + { TC2, 0, TC6_IRQn }, + { TC2, 1, TC7_IRQn }, + { TC2, 2, TC8_IRQn }, +#endif +}; + +// Fix for compatibility with Servo library +#if USING_SERVO_LIB + // Set _callbacks as used, allowing DueTimerInterrupt::getAvailable() to work + void (*DueTimerInterrupt::_callbacks[NUM_TIMERS])() = + { + (void (*)()) 1, // Timer 0 - Occupied + (void (*)()) 0, // Timer 1 + (void (*)()) 1, // Timer 2 - Occupied + (void (*)()) 1, // Timer 3 - Occupied + (void (*)()) 1, // Timer 4 - Occupied + (void (*)()) 1, // Timer 5 - Occupied + + #if defined(TC2) + (void (*)()) 0, // Timer 6 + (void (*)()) 0, // Timer 7 + (void (*)()) 0 // Timer 8 + #endif + }; + +#else + void (*DueTimerInterrupt::_callbacks[NUM_TIMERS])() = {}; +#endif + +#if defined(TC2) + double DueTimerInterrupt::_frequency[NUM_TIMERS] = { -1, -1, -1, -1, -1, -1, -1, -1, -1}; +#else + double DueTimerInterrupt::_frequency[NUM_TIMERS] = { -1, -1, -1, -1, -1, -1}; +#endif + +/* + Initializing all timers, so you can use them like this: Timer0.startTimer(); +*/ +DueTimerInterrupt DueTimer(0); + +DueTimerInterrupt Timer1(1); + +// Fix for compatibility with Servo library +#if ( !USING_SERVO_LIB || !defined(USING_SERVO_LIB) ) + DueTimerInterrupt Timer0(0); + DueTimerInterrupt Timer2(2); + DueTimerInterrupt Timer3(3); + DueTimerInterrupt Timer4(4); + DueTimerInterrupt Timer5(5); +#endif + +#if defined(TC2) + DueTimerInterrupt Timer6(6); + DueTimerInterrupt Timer7(7); + DueTimerInterrupt Timer8(8); +#endif + +DueTimerInterrupt DueTimerPtr[NUM_TIMERS] = +{ +#if ( !USING_SERVO_LIB || !defined(USING_SERVO_LIB) ) + Timer0, +#endif + + Timer1, + +#if ( !USING_SERVO_LIB || !defined(USING_SERVO_LIB) ) + Timer2, + Timer3, + Timer4, + Timer5, +#endif + +#if defined(TC2) + Timer6, + Timer7, + Timer8 +#endif +}; + +/////////////////////////////////////////////////////////////////////// + +/* + Implementation of the timer _callbacks defined in + arduino-1.5.2/hardware/arduino/sam/system/CMSIS/Device/ATMEL/sam3xa/include/sam3x8e.h +*/ +// Fix for compatibility with Servo library +#if ( !USING_SERVO_LIB || !defined(USING_SERVO_LIB) ) + +void TC0_Handler() +{ + TC_GetStatus(TC0, 0); + DueTimerInterrupt::_callbacks[0](); +} + +#endif + +void TC1_Handler() +{ + TC_GetStatus(TC0, 1); + DueTimerInterrupt::_callbacks[1](); +} + +// Fix for compatibility with Servo library +#if ( !USING_SERVO_LIB || !defined(USING_SERVO_LIB) ) + +void TC2_Handler() +{ + TC_GetStatus(TC0, 2); + DueTimerInterrupt::_callbacks[2](); +} + +void TC3_Handler() +{ + TC_GetStatus(TC1, 0); + DueTimerInterrupt::_callbacks[3](); +} + +void TC4_Handler() +{ + TC_GetStatus(TC1, 1); + DueTimerInterrupt::_callbacks[4](); +} + +void TC5_Handler() +{ + TC_GetStatus(TC1, 2); + DueTimerInterrupt::_callbacks[5](); +} +#endif + +#if defined(TC2) + +void TC6_Handler() +{ + TC_GetStatus(TC2, 0); + DueTimerInterrupt::_callbacks[6](); +} + +void TC7_Handler() +{ + TC_GetStatus(TC2, 1); + DueTimerInterrupt::_callbacks[7](); +} + +void TC8_Handler() +{ + TC_GetStatus(TC2, 2); + DueTimerInterrupt::_callbacks[8](); +} +#endif diff --git a/src_h/SAMDUE_ISR_Timer-Impl.h b/src_h/SAMDUE_ISR_Timer-Impl.h new file mode 100644 index 0000000..997d0c3 --- /dev/null +++ b/src_h/SAMDUE_ISR_Timer-Impl.h @@ -0,0 +1,337 @@ +/**************************************************************************************************************************** + SAMDUE_ISR_Timer-Impl.h + For SAM DUE boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/SAMDUE_TimerInterrupt + Licensed under MIT license + + Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by + unsigned long miliseconds), you just consume only one SAM DUE timer and avoid conflicting with other cores' tasks. + The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers + Therefore, their executions are not blocked by bad-behaving functions / tasks. + This important feature is absolutely necessary for mission-critical tasks. + + Based on SimpleTimer - A timer library for Arduino. + Author: mromani@ottotecnica.com + Copyright (c) 2010 OTTOTECNICA Italy + + Based on BlynkTimer.h + Author: Volodymyr Shymanskyy + + Version: 1.0.1 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.1 K Hoang 06/11/2020 Initial coding +*****************************************************************************************************************************/ + +#pragma once + +//#include "SAMDUE_ISR_Timer.h" +#include + +SAMDUE_ISR_Timer::SAMDUE_ISR_Timer() + : numTimers (-1) +{ +} + +void SAMDUE_ISR_Timer::init() +{ + unsigned long current_millis = millis(); //elapsed(); + + for (int i = 0; i < MAX_NUMBER_TIMERS; i++) + { + memset((void*) &timer[i], 0, sizeof (timer_t)); + timer[i].prev_millis = current_millis; + } + + numTimers = 0; +} + +void SAMDUE_ISR_Timer::run() +{ + int i; + unsigned long current_millis; + + // get current time + current_millis = millis(); //elapsed(); + + for (i = 0; i < MAX_NUMBER_TIMERS; i++) + { + + timer[i].toBeCalled = TIMER_DEFCALL_DONTRUN; + + // no callback == no timer, i.e. jump over empty slots + if (timer[i].callback != NULL) + { + + // is it time to process this timer ? + // see http://arduino.cc/forum/index.php/topic,124048.msg932592.html#msg932592 + + if ((current_millis - timer[i].prev_millis) >= timer[i].delay) + { + unsigned long skipTimes = (current_millis - timer[i].prev_millis) / timer[i].delay; + + // update time + timer[i].prev_millis += timer[i].delay * skipTimes; + + // check if the timer callback has to be executed + if (timer[i].enabled) + { + + // "run forever" timers must always be executed + if (timer[i].maxNumRuns == TIMER_RUN_FOREVER) + { + timer[i].toBeCalled = TIMER_DEFCALL_RUNONLY; + } + // other timers get executed the specified number of times + else if (timer[i].numRuns < timer[i].maxNumRuns) + { + timer[i].toBeCalled = TIMER_DEFCALL_RUNONLY; + timer[i].numRuns++; + + // after the last run, delete the timer + if (timer[i].numRuns >= timer[i].maxNumRuns) + { + timer[i].toBeCalled = TIMER_DEFCALL_RUNANDDEL; + } + } + } + } + } + } + + for (i = 0; i < MAX_NUMBER_TIMERS; i++) + { + if (timer[i].toBeCalled == TIMER_DEFCALL_DONTRUN) + continue; + + if (timer[i].hasParam) + (*(timerCallback_p)timer[i].callback)(timer[i].param); + else + (*(timerCallback)timer[i].callback)(); + + if (timer[i].toBeCalled == TIMER_DEFCALL_RUNANDDEL) + deleteTimer(i); + } +} + + +// find the first available slot +// return -1 if none found +int SAMDUE_ISR_Timer::findFirstFreeSlot() +{ + // all slots are used + if (numTimers >= MAX_NUMBER_TIMERS) + { + return -1; + } + + // return the first slot with no callback (i.e. free) + for (int i = 0; i < MAX_NUMBER_TIMERS; i++) + { + if (timer[i].callback == NULL) + { + return i; + } + } + + // no free slots found + return -1; +} + + +int SAMDUE_ISR_Timer::setupTimer(unsigned long d, void* f, void* p, bool h, unsigned n) +{ + int freeTimer; + + if (numTimers < 0) + { + init(); + } + + freeTimer = findFirstFreeSlot(); + if (freeTimer < 0) + { + return -1; + } + + if (f == NULL) + { + return -1; + } + + timer[freeTimer].delay = d; + timer[freeTimer].callback = f; + timer[freeTimer].param = p; + timer[freeTimer].hasParam = h; + timer[freeTimer].maxNumRuns = n; + timer[freeTimer].enabled = true; + timer[freeTimer].prev_millis = millis(); + + numTimers++; + + return freeTimer; +} + + +int SAMDUE_ISR_Timer::setTimer(unsigned long d, timerCallback f, unsigned n) +{ + return setupTimer(d, (void *)f, NULL, false, n); +} + +int SAMDUE_ISR_Timer::setTimer(unsigned long d, timerCallback_p f, void* p, unsigned n) +{ + return setupTimer(d, (void *)f, p, true, n); +} + +int SAMDUE_ISR_Timer::setInterval(unsigned long d, timerCallback f) +{ + return setupTimer(d, (void *)f, NULL, false, TIMER_RUN_FOREVER); +} + +int SAMDUE_ISR_Timer::setInterval(unsigned long d, timerCallback_p f, void* p) +{ + return setupTimer(d, (void *)f, p, true, TIMER_RUN_FOREVER); +} + +int SAMDUE_ISR_Timer::setTimeout(unsigned long d, timerCallback f) +{ + return setupTimer(d, (void *)f, NULL, false, TIMER_RUN_ONCE); +} + +int SAMDUE_ISR_Timer::setTimeout(unsigned long d, timerCallback_p f, void* p) +{ + return setupTimer(d, (void *)f, p, true, TIMER_RUN_ONCE); +} + +bool SAMDUE_ISR_Timer::changeInterval(unsigned numTimer, unsigned long d) +{ + if (numTimer >= MAX_NUMBER_TIMERS) + { + return false; + } + + // Updates interval of existing specified timer + if (timer[numTimer].callback != NULL) + { + timer[numTimer].delay = d; + timer[numTimer].prev_millis = millis(); + + return true; + } + + // false return for non-used numTimer, no callback + return false; +} + +void SAMDUE_ISR_Timer::deleteTimer(unsigned timerId) +{ + if (timerId >= MAX_NUMBER_TIMERS) + { + return; + } + + // nothing to delete if no timers are in use + if (numTimers == 0) + { + return; + } + + // don't decrease the number of timers if the specified slot is already empty + if (timer[timerId].callback != NULL) + { + memset((void*) &timer[timerId], 0, sizeof (timer_t)); + timer[timerId].prev_millis = millis(); + + // update number of timers + numTimers--; + } +} + +// function contributed by code@rowansimms.com +void SAMDUE_ISR_Timer::restartTimer(unsigned numTimer) +{ + if (numTimer >= MAX_NUMBER_TIMERS) + { + return; + } + + timer[numTimer].prev_millis = millis(); +} + + +bool SAMDUE_ISR_Timer::isEnabled(unsigned numTimer) +{ + if (numTimer >= MAX_NUMBER_TIMERS) + { + return false; + } + + return timer[numTimer].enabled; +} + + +void SAMDUE_ISR_Timer::enable(unsigned numTimer) +{ + if (numTimer >= MAX_NUMBER_TIMERS) + { + return; + } + + timer[numTimer].enabled = true; +} + + +void SAMDUE_ISR_Timer::disable(unsigned numTimer) +{ + if (numTimer >= MAX_NUMBER_TIMERS) + { + return; + } + + timer[numTimer].enabled = false; +} + +void SAMDUE_ISR_Timer::enableAll() +{ + // Enable all timers with a callback assigned (used) + + for (int i = 0; i < MAX_NUMBER_TIMERS; i++) + { + if (timer[i].callback != NULL && timer[i].numRuns == TIMER_RUN_FOREVER) + { + timer[i].enabled = true; + } + } +} + +void SAMDUE_ISR_Timer::disableAll() +{ + // Disable all timers with a callback assigned (used) + + for (int i = 0; i < MAX_NUMBER_TIMERS; i++) + { + if (timer[i].callback != NULL && timer[i].numRuns == TIMER_RUN_FOREVER) + { + timer[i].enabled = false; + } + } +} + +void SAMDUE_ISR_Timer::toggle(unsigned numTimer) +{ + if (numTimer >= MAX_NUMBER_TIMERS) + { + return; + } + + timer[numTimer].enabled = !timer[numTimer].enabled; +} + + +unsigned SAMDUE_ISR_Timer::getNumTimers() +{ + return numTimers; +} diff --git a/src_h/SAMDUE_ISR_Timer.h b/src_h/SAMDUE_ISR_Timer.h new file mode 100644 index 0000000..c98277a --- /dev/null +++ b/src_h/SAMDUE_ISR_Timer.h @@ -0,0 +1,170 @@ +/**************************************************************************************************************************** + SAMDUE_ISR_Timer.h + For SAM DUE boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/SAMDUE_TimerInterrupt + Licensed under MIT license + + Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by + unsigned long miliseconds), you just consume only one SAM DUE timer and avoid conflicting with other cores' tasks. + The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers + Therefore, their executions are not blocked by bad-behaving functions / tasks. + This important feature is absolutely necessary for mission-critical tasks. + + Based on SimpleTimer - A timer library for Arduino. + Author: mromani@ottotecnica.com + Copyright (c) 2010 OTTOTECNICA Italy + + Based on BlynkTimer.h + Author: Volodymyr Shymanskyy + + Version: 1.0.1 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.1 K Hoang 06/11/2020 Initial coding +*****************************************************************************************************************************/ + +#pragma once + +#if !( defined(ARDUINO_SAM_DUE) || defined(__SAM3X8E__) ) + #error This code is designed to run on SAM DUE board / platform! Please check your Tools->Board setting. +#endif + +#include + +#include + +#if defined(ARDUINO) + #if ARDUINO >= 100 + #include + #else + #include + #endif +#endif + +#define SAMDUE_ISR_Timer SAMDUE_ISRTimer + +typedef void (*timerCallback)(); +typedef void (*timerCallback_p)(void *); + +class SAMDUE_ISR_Timer +{ + + public: + // maximum number of timers +#define MAX_NUMBER_TIMERS 16 +#define TIMER_RUN_FOREVER 0 +#define TIMER_RUN_ONCE 1 + + // constructor + SAMDUE_ISR_Timer(); + + void init(); + + // this function must be called inside loop() + void run(); + + // Timer will call function 'f' every 'd' milliseconds forever + // returns the timer number (numTimer) on success or + // -1 on failure (f == NULL) or no free timers + int setInterval(unsigned long d, timerCallback f); + + // Timer will call function 'f' with parameter 'p' every 'd' milliseconds forever + // returns the timer number (numTimer) on success or + // -1 on failure (f == NULL) or no free timers + int setInterval(unsigned long d, timerCallback_p f, void* p); + + // Timer will call function 'f' after 'd' milliseconds one time + // returns the timer number (numTimer) on success or + // -1 on failure (f == NULL) or no free timers + int setTimeout(unsigned long d, timerCallback f); + + // Timer will call function 'f' with parameter 'p' after 'd' milliseconds one time + // returns the timer number (numTimer) on success or + // -1 on failure (f == NULL) or no free timers + int setTimeout(unsigned long d, timerCallback_p f, void* p); + + // Timer will call function 'f' every 'd' milliseconds 'n' times + // returns the timer number (numTimer) on success or + // -1 on failure (f == NULL) or no free timers + int setTimer(unsigned long d, timerCallback f, unsigned n); + + // Timer will call function 'f' with parameter 'p' every 'd' milliseconds 'n' times + // returns the timer number (numTimer) on success or + // -1 on failure (f == NULL) or no free timers + int setTimer(unsigned long d, timerCallback_p f, void* p, unsigned n); + + // updates interval of the specified timer + bool changeInterval(unsigned numTimer, unsigned long d); + + // destroy the specified timer + void deleteTimer(unsigned numTimer); + + // restart the specified timer + void restartTimer(unsigned numTimer); + + // returns true if the specified timer is enabled + bool isEnabled(unsigned numTimer); + + // enables the specified timer + void enable(unsigned numTimer); + + // disables the specified timer + void disable(unsigned numTimer); + + // enables all timers + void enableAll(); + + // disables all timers + void disableAll(); + + // enables the specified timer if it's currently disabled, and vice-versa + void toggle(unsigned numTimer); + + // returns the number of used timers + unsigned getNumTimers(); + + // returns the number of available timers + unsigned getNumAvailableTimers() + { + return MAX_NUMBER_TIMERS - numTimers; + }; + + private: + // deferred call constants +#define TIMER_DEFCALL_DONTRUN 0 // don't call the callback function +#define TIMER_DEFCALL_RUNONLY 1 // call the callback function but don't delete the timer +#define TIMER_DEFCALL_RUNANDDEL 2 // call the callback function and delete the timer + + // low level function to initialize and enable a new timer + // returns the timer number (numTimer) on success or + // -1 on failure (f == NULL) or no free timers + int setupTimer(unsigned long d, void* f, void* p, bool h, unsigned n); + + // find the first available slot + int findFirstFreeSlot(); + + typedef struct + { + unsigned long prev_millis; // value returned by the millis() function in the previous run() call + void* callback; // pointer to the callback function + void* param; // function parameter + bool hasParam; // true if callback takes a parameter + unsigned long delay; // delay value + unsigned maxNumRuns; // number of runs to be executed + unsigned numRuns; // number of executed runs + bool enabled; // true if enabled + unsigned toBeCalled; // deferred function call (sort of) - N.B.: only used in run() + } timer_t; + + volatile timer_t timer[MAX_NUMBER_TIMERS]; + + // actual number of timers in use (-1 means uninitialized) + volatile int numTimers; +}; + + +#include "SAMDUE_ISR_Timer-Impl.h" +