diff --git a/README.md b/README.md index d4abcb66..9bb791ed 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,6 @@ files provided with the lwIP release. 6. [`MDNS`](#mdns) 7. [`DNSClient`](#dnsclient) 8. [Print utilities](#print-utilities) - 9. [`IPAddress` operators](#ipaddress-operators) 3. [How to run](#how-to-run) 4. [How to write data to connections](#how-to-write-data-to-connections) 1. [Write immediacy](#write-immediacy) @@ -394,15 +393,6 @@ interface so that it is easy to print `Printable` objects to `stdout` or `stderr` without having to worry about buffering and the need to flush any output before printing a `Printable` directly to, say, `Serial`. -### `IPAddress` operators - -The core library version of `IPAddress` is missing `==` and `!=` operators that -can compare `const IPAddress` values. Provided in this library are these two -operators. They are declared as follows in the usual namespace: - -1. `bool operator==(const IPAddress &a, const IPAddress &b);` -2. `bool operator!=(const IPAddress &a, const IPAddress &b);` - ## How to run This library works with both PlatformIO and Arduino. To use it with Arduino, diff --git a/examples/PPSIn/PPSIn.ino b/examples/PPSIn/PPSIn.ino new file mode 100644 index 00000000..fcab6576 --- /dev/null +++ b/examples/PPSIn/PPSIn.ino @@ -0,0 +1,64 @@ +// SPDX-FileCopyrightText: (c) 2023 Jens Schleusner +// SPDX-License-Identifier: MIT + +// PPSOut generates a Pulse per Second using the IEEE1588-Timer on Pin 24 +// We read the pulse back on channels 0 and 2 to measure the timer delay +// Connect Oscilloscope to Pin 24 +// Connect Pins 24(out), 15(in), 35(in) +// +// This file is part of the QNEthernet library. + +#include +using namespace qindesign::network; + +void setup() { + + Serial.begin(2000000); + Serial.println("Setup EthernetIEEE1588"); + + qindesign::network::Ethernet.begin(); + qindesign::network::EthernetIEEE1588.begin(); + + //PPS-Out on Channel 1 + IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B0_12 = 6; //ENET_1588_EVENT1_OUT, IOMUX: ALT6, Teensy Pin: 24 + EthernetIEEE1588.setChannelCompareValue(1, 0); //Compare at counter value 0 + EthernetIEEE1588.setChannelMode(1, qindesign::network::EthernetIEEE1588.TimerChannelModes::kPulseHighOnCompare); //enable Channel0 positive pulse + EthernetIEEE1588.setChannelOutputPulseWidth(1, 25); //Generate a Pulse width of 25 25MHz clock cycles (1us) + + //PPS-IN + attachInterruptVector(IRQ_ENET_TIMER, interrupt_1588_timer); //Configure Interrupt Handler + NVIC_ENABLE_IRQ(IRQ_ENET_TIMER); //Enable Interrupt Handling + + //PPS-In on Channel 2 + IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B1_03 = 4; //ENET_1588_EVENT2_IN, IOMUX: ALT4, Teensy Pin: 15 + EthernetIEEE1588.setChannelMode(2, qindesign::network::EthernetIEEE1588.TimerChannelModes::kCaptureOnRising); //enable Channel2 rising edge trigger + EthernetIEEE1588.setChannelInterruptEnable(2, true); //Configure Interrupt generation + + //PPS-In on Channel 0 + IOMUXC_SW_MUX_CTL_PAD_GPIO_B1_12 = 3; //ENET_1588_EVENT0_IN, IOMUX: ALT3, Teensy Pin: 35 + IOMUXC_ENET0_TIMER_SELECT_INPUT = 0b10; // route B1_12 to 1588 timer (pg 796, Rev. 3) + EthernetIEEE1588.setChannelMode(0, qindesign::network::EthernetIEEE1588.TimerChannelModes::kCaptureOnFalling); + EthernetIEEE1588.setChannelInterruptEnable(0, true); + + + Serial.printf("TCSR Register state: ENET_TCSR0 %08" PRIX32 "h ENET_TCSR1 %08" PRIX32 "h ENET_TCSR2 %08" PRIX32 "h\n", ENET_TCSR0, ENET_TCSR1, ENET_TCSR2); // (pg 2247, Rev. 3) +} + +void loop() { + // put your main code here, to run repeatedly: + +} + +static void interrupt_1588_timer() { + uint32_t t; + if(EthernetIEEE1588.getAndClearChannelStatus(0)){ + EthernetIEEE1588.getChannelCompareValue(0,t); + Serial.printf("Timer0 Falling Edge: %d\n\n", t); + } + if (EthernetIEEE1588.getAndClearChannelStatus(2)) { + EthernetIEEE1588.getChannelCompareValue(2,t); + Serial.printf("Timer2 Rising Edge: %d\n", t); + } + asm("dsb"); // allow write to complete so the interrupt doesn't fire twice +} + diff --git a/examples/PPSOut/PPSOut.ino b/examples/PPSOut/PPSOut.ino new file mode 100644 index 00000000..d114201a --- /dev/null +++ b/examples/PPSOut/PPSOut.ino @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: (c) 2023 Jens Schleusner +// SPDX-License-Identifier: MIT + +// PPSOut generates a Pulse per Second using the IEEE1588-Timer +// Connect Oscilloscope to Pins 14,24,34 +// Connect LED to Pin 14 +// +// This file is part of the QNEthernet library. + +#include +using namespace qindesign::network; + +void setup() { + + Serial.begin(2000000); + Serial.println("Setup EthernetIEEE1588"); + + qindesign::network::Ethernet.begin(); + qindesign::network::EthernetIEEE1588.begin(); + + IOMUXC_SW_MUX_CTL_PAD_GPIO_B1_13 = 3; //ENET_1588_EVENT0_OUT, IOMUX: ALT3, Teensy Pin: 34 + EthernetIEEE1588.setChannelCompareValue(0, 0); //Compare at counter value 0 + EthernetIEEE1588.setChannelMode(0, qindesign::network::EthernetIEEE1588.TimerChannelModes::kPulseHighOnCompare); //enable Channel0 positive pulse + EthernetIEEE1588.setChannelOutputPulseWidth(0, 25); //Generate a Pulse width of 25 25MHz clock cycles (1us) + + IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B0_12 = 6; //ENET_1588_EVENT1_OUT, IOMUX: ALT6, Teensy Pin: 24 + EthernetIEEE1588.setChannelCompareValue(1, 1000); //Compare at counter value 1000 + EthernetIEEE1588.setChannelOutputPulseWidth(1, 10); //Generate a Pulse width of 10 25MHz clock cycles (400ns) + EthernetIEEE1588.setChannelMode(1, qindesign::network::EthernetIEEE1588.TimerChannelModes::kPulseLowOnCompare); //enable Channel1 negative pulse + + IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B1_02 = 4; //ENET_1588_EVENT2_OUT, IOMUX: ALT4, Teensy Pin: 14 + EthernetIEEE1588.setChannelCompareValue(2, 500 * 1000 * 1000); //Compare at counter 500ms + EthernetIEEE1588.setChannelMode(2, qindesign::network::EthernetIEEE1588.TimerChannelModes::kClearOnCompareSetOnOverflow); //enable Channel2 for 50/50 On-Off Signal + + Serial.printf("TCSR Register state: ENET_TCSR0 %08" PRIX32 "h ENET_TCSR1 %08" PRIX32 "h ENET_TCSR2 %08" PRIX32 "h\n", ENET_TCSR0, ENET_TCSR1, ENET_TCSR2); // (pg 2247, Rev. 3) +} + +void loop() { + // put your main code here, to run repeatedly: + +} diff --git a/examples/TimerAdjustment/TimerAdjustment.ino b/examples/TimerAdjustment/TimerAdjustment.ino new file mode 100644 index 00000000..4a189f72 --- /dev/null +++ b/examples/TimerAdjustment/TimerAdjustment.ino @@ -0,0 +1,46 @@ +// SPDX-FileCopyrightText: (c) 2023 Jens Schleusner +// SPDX-License-Identifier: MIT +// +// This file is part of the QNEthernet library. + +#include +using namespace qindesign::network; + +IntervalTimer myTimer; + +int adjustment = 0; +timespec tmlast; + +void setup() { + + Serial.begin(2000000); + Serial.println("Setup EthernetIEEE1588"); + + qindesign::network::Ethernet.begin(); + qindesign::network::EthernetIEEE1588.begin(); + + myTimer.begin(timerInterrupt, 1000000); + EthernetIEEE1588.readTimer(tmlast); + +} + +void timerInterrupt() { + timespec tm; + EthernetIEEE1588.readTimer(tm);//read current timer value + int lastadjustment=adjustment; + adjustment += 100; //increase timer spped by 100ns per second + EthernetIEEE1588.adjustFreq(adjustment); + + int diff = (static_cast(tm.tv_sec) - static_cast(tmlast.tv_sec)) * (1000 * 1000 * 1000) + (static_cast(tm.tv_nsec) - static_cast(tmlast.tv_nsec)); + diff -= (1000 * 1000 * 1000); + + Serial.printf("Adjustment:%d nsps ATINC %08" PRIX32 "h ATCOR %d Diff %d\n",lastadjustment,ENET_ATINC,ENET_ATCOR,diff); + //Serial.printf("%d %d\n", lastadjustment, diff); + + tmlast = tm; + +} + +void loop() { + +} diff --git a/src/QNEthernet.h b/src/QNEthernet.h index d7d1bb67..bb9ddc03 100644 --- a/src/QNEthernet.h +++ b/src/QNEthernet.h @@ -28,7 +28,6 @@ #include "lwip/opt.h" #include "lwip_t41.h" #include "util/PrintUtils.h" -#include "util/ip_tools.h" namespace qindesign { namespace network { diff --git a/src/QNEthernetIEEE1588.cpp b/src/QNEthernetIEEE1588.cpp index 94af43c4..2d7fb708 100644 --- a/src/QNEthernetIEEE1588.cpp +++ b/src/QNEthernetIEEE1588.cpp @@ -33,6 +33,10 @@ bool EthernetIEEE1588Class::writeTimer(const timespec &t) const { return enet_ieee1588_write_timer(&t); } +bool EthernetIEEE1588Class::offsetTimer(int64_t ns) const{ + return enet_ieee1588_offset_timer(ns); +} + void EthernetIEEE1588Class::timestampNextFrame() const { enet_ieee1588_timestamp_next_frame(); } @@ -47,7 +51,7 @@ bool EthernetIEEE1588Class::adjustTimer(uint32_t corrInc, return enet_ieee1588_adjust_timer(corrInc, corrPeriod); } -bool EthernetIEEE1588Class::adjustFreq(int nsps) const { +bool EthernetIEEE1588Class::adjustFreq(double nsps) const { return enet_ieee1588_adjust_freq(nsps); } @@ -57,10 +61,8 @@ bool EthernetIEEE1588Class::setChannelMode(int channel, } bool EthernetIEEE1588Class::setChannelOutputPulseWidth(int channel, - TimerChannelModes mode, int pulseWidth) const { return enet_ieee1588_set_channel_output_pulse_width(channel, - static_cast(mode), pulseWidth); } @@ -69,10 +71,19 @@ bool EthernetIEEE1588Class::setChannelCompareValue(int channel, return enet_ieee1588_set_channel_compare_value(channel, value); } +bool EthernetIEEE1588Class::getChannelCompareValue(int channel, + uint32_t &value) const { + return enet_ieee1588_get_channel_compare_value(channel, &value); +} + bool EthernetIEEE1588Class::getAndClearChannelStatus(int channel) const { return enet_ieee1588_get_and_clear_channel_status(channel); } +bool EthernetIEEE1588Class::setChannelInterruptEnable(int channel, bool enable) const { + return enet_ieee1588_set_channel_interrupt_enable(channel, enable); +} + EthernetIEEE1588Class::operator bool() const { return enet_ieee1588_is_enabled(); } diff --git a/src/QNEthernetIEEE1588.h b/src/QNEthernetIEEE1588.h index a02b7d3b..f7bd66fe 100644 --- a/src/QNEthernetIEEE1588.h +++ b/src/QNEthernetIEEE1588.h @@ -53,6 +53,9 @@ class EthernetIEEE1588Class final { // Writes the current IEEE 1588 timer value. This returns whether successful. bool writeTimer(const timespec &t) const; + // Adds an offset to the current IEEE 1588 timer value. This returns whether successful. + bool offsetTimer(int64_t ns) const; + // Tells the driver to timestamp the next transmitted frame. This should be // called before functions like `EthernetUDP::endPacket()`, // `EthernetUDP::send()`, and any of the `EthernetFrame` send functions. @@ -75,7 +78,7 @@ class EthernetIEEE1588Class final { // Adjusts the correction frequency in nanoseconds per second. To slow down // the timer, specify a negative value. To speed it up, specify a positive // value. This returns whether successful. - bool adjustFreq(int nsps) const; + bool adjustFreq(double nsps) const; // Sets the channel mode for the given channel. This does not set the output // compare pulse modes. This returns whether successful. @@ -84,20 +87,25 @@ class EthernetIEEE1588Class final { // output compare pulse modes. bool setChannelMode(int channel, TimerChannelModes mode) const; - // Sets the output compare pulse mode and pulse width for the given channel. - // The pulse width must be in the range 1-32. This only sets the output - // compare pulse modes. This returns whether successful. + // Sets the output compare pulse width for the given channel. + // The pulse width must be in the range 1-32. This returns whether successful. bool setChannelOutputPulseWidth(int channel, - TimerChannelModes mode, int pulseWidth) const; // Sets the channel compare value. This returns whether successful. bool setChannelCompareValue(int channel, uint32_t value) const; + // Gets the channel compare value. This returns whether successful. + bool getChannelCompareValue(int channel, uint32_t &value) const; + // Retrieves and then clears the status for the given channel. This will // return false for an unknown channel. bool getAndClearChannelStatus(int channel) const; + // Enables or disables timer interrupt generation for a channel. This will + // return false for an unknown channel. + bool setChannelInterruptEnable(int channel, bool enable) const; + // Tests if the IEEE 1588 timer has been started. operator bool() const; diff --git a/src/lwip_t41.c b/src/lwip_t41.c index 76a6a252..026142f8 100644 --- a/src/lwip_t41.c +++ b/src/lwip_t41.c @@ -936,9 +936,15 @@ void enet_leave_group(const ip4_addr_t *group) { #define ENET_TCSR_TMODE_MASK (0x0000003cU) #define ENET_TCSR_TMODE(n) ((uint32_t)(((n) & 0x0f) << 2)) + +#define ENET_TCSR_TPWC_MASK ((uint32_t)(0x1f << 11)) #define ENET_TCSR_TPWC(n) ((uint32_t)(((n) & 0x1f) << 11)) + #define ENET_TCSR_TF ((uint32_t)(1U << 7)) +#define ENET_TCSR_TIE_MASK ((uint32_t)(1U << 6)) +#define ENET_TCSR_TIE(n) ((uint32_t)(((n) & 0x01) << 6)) + void enet_ieee1588_init() { ENET_ATCR = ENET_ATCR_RESTART | ENET_ATCR_Reserved; // Reset timer ENET_ATPER = NANOSECONDS_PER_SECOND; // Wrap at 10^9 @@ -1004,6 +1010,18 @@ bool enet_ieee1588_write_timer(const struct timespec *t) { return true; } +bool enet_ieee1588_offset_timer(int64_t ns){ + struct timespec tm; + if(!enet_ieee1588_read_timer(&tm)){ + return false; + } + int64_t t = (((int64_t)tm.tv_sec) * NANOSECONDS_PER_SECOND) + ((int64_t)tm.tv_nsec); + t += ns; + tm.tv_nsec = t % NANOSECONDS_PER_SECOND; + tm.tv_sec = t / NANOSECONDS_PER_SECOND; + return enet_ieee1588_write_timer(&tm); +} + void enet_ieee1588_timestamp_next_frame() { doTimestampNext = true; } @@ -1026,12 +1044,12 @@ bool enet_ieee1588_adjust_timer(uint32_t corrInc, uint32_t corrPeriod) { if (corrInc >= 128 || corrPeriod >= (1U << 31)) { return false; } - CLRSET(ENET_ATINC, ENET_ATINC_INC_MASK, ENET_ATINC_INC(corrInc)); - ENET_ATCOR = corrPeriod | ENET_ATCOR_COR_MASK; + CLRSET(ENET_ATINC, ENET_ATINC_INC_MASK, ENET_ATINC_INC_CORR(corrInc)); + ENET_ATCOR = corrPeriod & ENET_ATCOR_COR_MASK; return true; } -bool enet_ieee1588_adjust_freq(int nsps) { +bool enet_ieee1588_adjust_freq(double nsps) { if (nsps == 0) { ENET_ATCOR = 0; return true; @@ -1047,7 +1065,7 @@ bool enet_ieee1588_adjust_freq(int nsps) { // Speed up inc++; } - return enet_ieee1588_adjust_timer(inc, F_ENET_TS_CLK / nsps); + return enet_ieee1588_adjust_timer(inc, round(F_ENET_TS_CLK / nsps)); } // Channels @@ -1076,44 +1094,38 @@ static volatile uint32_t *tccrReg(int channel) { } bool enet_ieee1588_set_channel_mode(int channel, int mode) { - switch (mode) { - case 14: // kTimerChannelPulseLowOnCompare - case 15: // kTimerChannelPulseHighOnCompare - case 12: // Reserved - case 13: // Reserved - return false; - default: - if (mode < 0 || 0x0f < mode) { - return false; - } - break; + if (channel < 0 || channel > 3){ + return false; + } + + if (mode < 0 || mode > 15 || mode == 8 || mode == 12 || mode == 13) {//Check for reserverd modes + return false; } - volatile uint32_t *tcsr = tcsrReg(channel); if (tcsr == NULL) { return false; } + uint32_t state = *tcsr; // Backup current state *tcsr = 0; while ((*tcsr & ENET_TCSR_TMODE_MASK) != 0) { // Check until the channel is disabled } - *tcsr = ENET_TCSR_TMODE(mode); + CLRSET(state,ENET_TCSR_TMODE_MASK,ENET_TCSR_TMODE(mode)); + *tcsr = state; + while (*tcsr != state) { + // Check until the channel is enabled + } return true; } bool enet_ieee1588_set_channel_output_pulse_width(int channel, - int mode, - int pulseWidth) { - switch (mode) { - case 14: // kTimerChannelPulseLowOnCompare - case 15: // kTimerChannelPulseHighOnCompare - break; - default: - return true; + int pulseWidth) { + if (channel < 0 || channel > 3){ + return false; } - + if (pulseWidth < 1 || 32 < pulseWidth) { return false; } @@ -1122,17 +1134,23 @@ bool enet_ieee1588_set_channel_output_pulse_width(int channel, if (tcsr == NULL) { return false; } - + uint32_t state = *tcsr; // Backup current state *tcsr = 0; while ((*tcsr & ENET_TCSR_TMODE_MASK) != 0) { // Check until the channel is disabled } - *tcsr = ENET_TCSR_TMODE(mode) | ENET_TCSR_TPWC(pulseWidth - 1); - + CLRSET(state,ENET_TCSR_TPWC_MASK,ENET_TCSR_TPWC(pulseWidth - 1)); + *tcsr = state; + while (*tcsr != state) { + // Check until the channel is enabled + } return true; } bool enet_ieee1588_set_channel_compare_value(int channel, uint32_t value) { + if (channel < 0 || channel > 3) { + return false; + } volatile uint32_t *tccr = tccrReg(channel); if (tccr == NULL) { return false; @@ -1141,7 +1159,22 @@ bool enet_ieee1588_set_channel_compare_value(int channel, uint32_t value) { return true; } +bool enet_ieee1588_get_channel_compare_value(int channel, uint32_t *value) { + if (channel < 0 || channel > 3) { + return false; + } + volatile uint32_t *tccr = tccrReg(channel); + if (tccr == NULL) { + return false; + } + *value = *tccr; + return true; +} + bool enet_ieee1588_get_and_clear_channel_status(int channel) { + if (channel < 0 || channel > 3) { + return false; + } volatile uint32_t *tcsr = tcsrReg(channel); if (tcsr == NULL) { return false; @@ -1150,9 +1183,21 @@ bool enet_ieee1588_get_and_clear_channel_status(int channel) { *tcsr |= ENET_TCSR_TF; ENET_TGSR = (1 << channel); return true; - } else { + } + return false; +} + +bool enet_ieee1588_set_channel_interrupt_enable(int channel, bool enable){ + if (channel < 0 || channel > 3) { return false; } + + volatile uint32_t *tcsr = tcsrReg(channel); + if (tcsr == NULL) { + return false; + } + CLRSET(*tcsr,ENET_TCSR_TIE_MASK,ENET_TCSR_TIE(enable)); + return true; } #endif // ARDUINO_TEENSY41 diff --git a/src/lwip_t41.h b/src/lwip_t41.h index d2d132f7..bb9f503e 100644 --- a/src/lwip_t41.h +++ b/src/lwip_t41.h @@ -114,6 +114,10 @@ bool enet_ieee1588_read_timer(struct timespec *t); // This will return false if the argument is NULL. bool enet_ieee1588_write_timer(const struct timespec *t); +// Adds an offset to the current timer value. Uses the read and write +// functions under the hood. This returns whether successful. +bool enet_ieee1588_offset_timer(int64_t ns); + // Tells the driver to timestamp the next transmitted frame. void enet_ieee1588_timestamp_next_frame(); @@ -138,7 +142,7 @@ bool enet_ieee1588_adjust_timer(uint32_t corrInc, uint32_t corrPeriod); // Adjust the correction in nanoseconds per second. This uses // `enet_ieee1588_adjust_timer()` under the hood. -bool enet_ieee1588_adjust_freq(int nsps); +bool enet_ieee1588_adjust_freq(double nsps); // Sets the channel mode for the given channel. This does not set the output // compare pulse modes. This returns whether successful. @@ -154,10 +158,8 @@ bool enet_ieee1588_set_channel_mode(int channel, int mode); // // This will return false if: // 1. The channel is unknown, -// 2. The mode is not one of the output compare pulse modes, or -// 3. The pulse width is not in the range 1-32. +// 2. The pulse width is not in the range 1-32. bool enet_ieee1588_set_channel_output_pulse_width(int channel, - int mode, int pulseWidth); // Sets the channel compare value. This returns whether successful. @@ -165,10 +167,19 @@ bool enet_ieee1588_set_channel_output_pulse_width(int channel, // This will return false for an unknown channel. bool enet_ieee1588_set_channel_compare_value(int channel, uint32_t value); -// Retrieves and then clears the status for the given channel. This will return -// false for an unknown channel. +// Gets the channel compare value. This returns whether successful. +// +// This will return false for an unknown channel. +bool enet_ieee1588_get_channel_compare_value(int channel, uint32_t *value); + +// Retrieves and then clears the status for the given channel. This will +// return false for an unknown channel. bool enet_ieee1588_get_and_clear_channel_status(int channel); +// Enables or disables timer interrupt generation for a channel. This will +// return false for an unknown channel. +bool enet_ieee1588_set_channel_interrupt_enable(int channel, bool enable); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/util/ip_tools.cpp b/src/util/ip_tools.cpp index 921bb0d5..6fd9a2c1 100644 --- a/src/util/ip_tools.cpp +++ b/src/util/ip_tools.cpp @@ -24,14 +24,6 @@ uint32_t ip_addr_get_ip4_uint32(const ip_addr_t *ip) { return IPADDR_ANY; } -bool operator==(const IPAddress &a, const IPAddress &b) { - return (const_cast(a) == b); -} - -bool operator!=(const IPAddress &a, const IPAddress &b) { - return !(const_cast(a) == b); -} - uint32_t get_uint32(const IPAddress &ip) { // The uint32_t operator doesn't work with const IPAddress, hence // the const_cast diff --git a/src/util/ip_tools.h b/src/util/ip_tools.h index c54e4f2e..bcdb5bc4 100644 --- a/src/util/ip_tools.h +++ b/src/util/ip_tools.h @@ -22,10 +22,6 @@ namespace network { // non-IPv4-mapped addresses. uint32_t ip_addr_get_ip4_uint32(const ip_addr_t *ip); -// Missing IPAddress operators -bool operator==(const IPAddress &a, const IPAddress &b); -bool operator!=(const IPAddress &a, const IPAddress &b); - // Gets the 32-bit address from the given const IPAddress. uint32_t get_uint32(const IPAddress &ip);