Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 9 of 9

Thread: TeensyTimerTool & NativeEthernet on teensy 4.1

  1. #1
    Junior Member
    Join Date
    Apr 2021
    Posts
    12

    TeensyTimerTool & NativeEthernet on teensy 4.1

    I am successfully using the TeensyTimerTool for controlling stepper motors.
    In another Project I successfully use NativeEthernet for receiving Commands via UDP.

    Now I want to combine both projects to control the steppers via UDP.
    The steppers happily step in timer routines from TeensyTimerTool,
    while UDP communication shall be done in the loop() function.

    Unfortunately there seems to be some incompatibility between these 2 libraries:
    As soon a I call
    Code:
      int packetSize = Udp.parsePacket();
    the steppers just stop. (There is no further processing of the received data yet).
    Without this line the steppers work fine.

    I guess there is an interrupt conflict between the 2 libraries.
    But teensy 4.1 should have more than one hardware timer, so I think the conflict could be avoided.

    Thanks in advance for your help!

  2. #2
    First thing I found was this. Not sure if this is related but it's worth a shot either disabling the timer before calling parsePacket then re-enabling it. Alternatively call noInterrupts() and interrupts() around it just to see if it makes a difference.
    https://github.com/arduino/Arduino/issues/3087

    Also, TeensyTimerTool warns about timers causing problems with PWM pins. You may need to change the pin you are using or change which timer you are using. Here's the wiki.
    https://github.com/luni64/TeensyTime...-timer-clashes
    If you scroll down there is a table showing which timers are related to which pins on the 4.1 so you can avoid using them together.

    Hopefully those can help with your issue.

  3. #3
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    1,626
    Which timer are you using for your stepper control? It looks like NativeEthernet is using TMR4 (quad timer) per default: https://github.com/vjmuzik/FNET/blob...2_config.h#L59, so that may clash. The linked FNET config file suggests that you can switch that to PIT. @vjmuzik will be able to explain how.

    Anyway, it might be much easier to just use another timer from the TimerTool for your stepper controller.

  4. #4
    Junior Member
    Join Date
    Apr 2021
    Posts
    12
    All of you, thanks for your quick replies! I am impressed by the quality and responsiveness of this forum!
    Spencez: the quickfix with noInterrupts() and interrupts() changed nothing. Also I use no PWM. But the wiki is very interesting, I did not know there are so many timers!
    luni: I did not yet find out which timer I am actually using with the TimerTool, and I did not even know I have the possibility to choose! Up to now I just used

    Code:
    TeensyTimerTool::OneShotTimer timer1; // generate a timer from the pool (Pool: 2xGPT, 16xTMR(QUAD), 20xTCK)
    TeensyTimerTool::OneShotTimer timer2; // generate a timer from the pool (Pool: 2xGPT, 16xTMR(QUAD), 20xTCK)
    But I found that there are other constructors accepting an argument like
    Code:
    TeensyTimerTool::OneShotTimer timer1(TeensyTimerTool::GPT1); // generate a timer from the pool (Pool: 2xGPT, 16xTMR(QUAD), 20xTCK)
    TeensyTimerTool::OneShotTimer timer2(TeensyTimerTool::GPT2); // generate a timer from the pool (Pool: 2xGPT, 16xTMR(QUAD), 20xTCK)
    I have messed around trying out some values other than GPT1/GPT2 but to no avail. GPT1 and GPT2 work well as long as I do not call Udp.parsePacket().
    But when I uncomment the line with Udp.parsePacket(), I do not even get the Serial output of the previous line. Weird. Also the USB connection is somewhat messed up and I alway have to press the Teensy-button for uploading a new sketch.

    I post my complete sketch, which is rather long. But perhaps one of you will immediately know what to do.
    with the sketch as it is everything works as expected: all Serial output ok, even the Ethernet link status visualisation on LCD is ok.
    But no Udp parsing.
    And when I enable the line Udp.parsePacket(), then my output on the TeensyMonitor is just:
    Code:
    20:20:55.734 -> Ethernet HW found
    20:20:55.800 -> setting up stepper pins
    20:20:55.800 -> triggering timers
    20:20:55.800 -> setup finished
    20:20:55.800 -> checking Ethernet link status...
    But the LCD shows already "ON" for the link info.

    Thanks in advance!
    Code:
    #define ENA    3
    #define MS1A   4
    #define MS2A   5
    #define STEPA  6
    #define DIRA   7
    
    #define ENB    8
    #define MS1B   9
    #define MS2B  10
    #define STEPB 11
    #define DIRB  12
    
    #include <LiquidCrystal.h>
    #include <NativeEthernet.h>
    
    #include "Arduino.h"
    #include "TeensyTimerTool.h"
    
    #include <atomic>
    
    const int rs = 27, en = 28, d4 = 29, d5 = 30, d6 = 31, d7 = 32;
    LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
    
    byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
    IPAddress ip(192, 168, 1, 177);
    uint16_t localPort = 8888;      // local port to listen on
    EthernetLinkStatus link_status = Unknown;
    EthernetUDP Udp;
    char packetBuffer[UDP_TX_PACKET_MAX_SIZE];  // buffer to hold incoming packet,
    
    
    
    TeensyTimerTool::OneShotTimer timer1(TeensyTimerTool::GPT1); // generate a timer from the pool (Pool: 2xGPT, 16xTMR(QUAD), 20xTCK)
    TeensyTimerTool::OneShotTimer timer2(TeensyTimerTool::GPT2); // generate a timer from the pool (Pool: 2xGPT, 16xTMR(QUAD), 20xTCK)
    
    #define V_MAX ((float)(0.25*2048.0*64.0*1e-6))
    #define A_MAX ((float)(V_MAX*1e-6))
    
    
    float ComputeV(float v,float a) {
      if (a > 0.0f) {
        if (a > A_MAX) a = A_MAX;
        const float v_half = 0.5f * v;
        if (v > 0.0f) { // acceleration
          return std::min(V_MAX, v_half + sqrtf(v_half*v_half + a));
        }
        if (v < 0.0f) { // deceleration
          const float discr = v_half*v_half - a;
          if (discr >= 0.0f) {
            return v_half - sqrtf(discr);
          }
            // change of direction
          const float a_inv = 1.0f / a;   // > 0
          const float h = v_half * a_inv; // < 0
          return 1.0f / (sqrtf(h*h + a_inv) - h);
        }
        return sqrtf(a);
      }
      if (a < 0.0f) {
        if (a < -A_MAX) a = -A_MAX;
        const float v_half = 0.5f * v;
        if (v < 0.0f) { // acceleration
          return std::max(-V_MAX, v_half - sqrtf(v_half*v_half - a));
        }
        if (v > 0.0f) { // deceleration
          const float discr = v_half*v_half + a;
          if (discr >= 0.0f) {
            return v_half + sqrtf(discr);
          }
            // change of direction
          const float a_inv = 1.0f / a;   // < 0
          const float h = v_half * a_inv; // < 0
          return 1.0f / (h - sqrtf(h*h - a_inv));
        }
        return -sqrt(-a);
      }
        // constant speed
      return v;
    }
    
    
    
    unsigned int pos1 = 0;
    uint32_t last_micros;
    double v1 = 0; // steps/microsecond
    
    volatile std::atomic<float> a;
    float next_v = 0.f;
    
    void callback1() {
      next_v = ComputeV(next_v,a);
      if (next_v > 0.f) {
        digitalWriteFast(DIRA,1);
        digitalWriteFast(STEPA,1);
        pos1++;
        last_micros = last_micros + (0.5f + 1.f/next_v);
        const uint32_t curr_micros = micros();
        const int32_t trigger_delay = last_micros-curr_micros;
        if (trigger_delay <= 0) timer1.trigger(1);
        else timer1.trigger(trigger_delay);
        digitalWriteFast(STEPA,0);
      } else if (next_v < 0.f) {
        digitalWriteFast(DIRA,0);
        digitalWriteFast(STEPA,1);
        pos1--;
        last_micros = last_micros + (0.5f - 1.f/next_v);
        const uint32_t curr_micros = micros();
        const int32_t trigger_delay = last_micros-curr_micros;
        if (trigger_delay <= 0) timer1.trigger(1);
        else timer1.trigger(trigger_delay);
        digitalWriteFast(STEPA,0);
      } else {
        timer1.trigger(1);
      }
    }
    
    unsigned int pos2 = 0;
    bool step_pin_high2 = false;
    void callback2() {
      step_pin_high2 = !step_pin_high2;
      digitalWriteFast(STEPB,step_pin_high2);
      if (step_pin_high2) {
        pos2++;
        timer2.trigger(1);
      } else {
        timer2.trigger(529);
      }
    }
    
    
    void setup()
    {
      Ethernet.begin(mac, ip);
      if (Ethernet.hardwareStatus() == EthernetNoHardware) {
        Serial.println("Ethernet shield was not found.  Sorry, can't run without hardware. :(");
        while (true) {
          delay(1); // do nothing, no point running without Ethernet hardware
        }
      }
      Serial.println("Ethernet HW found");
    
      lcd.begin(16, 2);
      lcd.print("position");
    
      Serial.println("setting up stepper pins");
    
    //  pinMode(LED_BUILTIN, OUTPUT);
    //  for (int j=0;j<9;j++) pinMode(33+j, OUTPUT);
      pinMode(ENA, OUTPUT);
      pinMode(MS1A, OUTPUT);
      pinMode(MS2A, OUTPUT);
      pinMode(STEPA, OUTPUT);
      pinMode(DIRA, OUTPUT);
      pinMode(ENB, OUTPUT);
      pinMode(MS1B, OUTPUT);
      pinMode(MS2B, OUTPUT);
      pinMode(STEPB, OUTPUT);
      pinMode(DIRB, OUTPUT);
    
      digitalWrite(ENA,1);
      digitalWrite(MS1A,0);
      digitalWrite(MS2A,1); // damit steuere ich das Microstepping: 64
      digitalWrite(DIRA, 0);
      digitalWrite(STEPA, 1);
      digitalWrite(ENA,0);
      digitalWrite(ENB,1);
      digitalWrite(MS1B,0);
      digitalWrite(MS2B,1); // damit steuere ich das Microstepping: 64
      digitalWrite(DIRB, 0);
      digitalWrite(STEPB, 1);
      digitalWrite(ENB,0);
    
      Serial.println("triggering timers");
    
        pinMode(LED_BUILTIN,OUTPUT);
        timer1.begin(callback1);
        timer2.begin(callback2);
        timer1.trigger(1000);
        timer2.trigger(1000);
      a = 0.001*A_MAX;
      Serial.println("setup finished");
    }
    
    int count = 0;
    void loop()
    {
      if (1) {
        Serial.println("checking Ethernet link status...");
        EthernetLinkStatus s = Ethernet.linkStatus();
        if (s != link_status) {
          link_status = s;
          lcd.setCursor(13,0);
          switch (link_status) {
            case LinkON:
              lcd.print("ON ");
              Serial.println("Ethernet cable connected.");
              break;
            case LinkOFF:
              lcd.print("OFF");
              Serial.println("Ethernet cable disconnected.");
              break;
            default: 
              lcd.print("???");
              Serial.println("weird link status.");
          }
        }
      }
    
      Serial.println("calling Udp.parsePacket()");
    //  noInterrupts(); // does not help
    //  int packetSize = Udp.parsePacket();
    //  interrupts();
      Serial.println("Udp.parsePacket() ok");
    /*
      if (packetSize) {
        Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE);
        packetBuffer[packetSize] = '\0';
        int value = 0;
        int i = (packetBuffer[0] == '-' || packetBuffer[0] == '+') ? 1 : 0;
        if (i < packetSize) {
          do {
            char c = packetBuffer[i];
            if (c < '0' || c > '9') goto parse_error;
            value = 10*value + (c-'0');
          } while (++i < packetSize);
          if (packetBuffer[0] == '-') value = -value;
          Serial.print("Received ");
          Serial.println(value);
          value = std::max(std::max(value,-1000),1000);
          a = (A_MAX*1e-3)*value;
          lcd.setCursor(9,0);
          lcd.print(value);
        } else {
          parse_error:
          Serial.print("Received bad data: \"");
          Serial.print(packetBuffer);
          Serial.println('"');
        }
      }
    */
    
    
    //  if (++count >= 4) {
    //    count = 0;
    //    a = -a;
    //  }
    
    //    digitalWriteFast(LED_BUILTIN, HIGH);
    
          lcd.setCursor(0, 1);
          lcd.print(pos1>>6);
          lcd.print(" ");
          lcd.print(pos2>>6);
        delay(1000);
    }

  5. #5
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    1,626
    Ok, if you are using the GPT timers a clash is not very likely (at least I didn't see any use of the GPT timers in the Ethernet library). Anyway, just to make sure I'd try the TCK timers. They are purely software based and don't touch any hardware timer which would exclude any possible clashes with the ethernet library. You can also try the PIT timers.

    BTW: Do yourself a favor and place a 'using namespace TeensyTimerTool' on top of your sketch. This will save typing the fully qualified type names all the time.

    I.e.:
    Code:
    #include "TeensyTimerTool.h"
    
    using namespace TeensyTimerTool;
    
    //....
    OneShotTimer timer1(TCK);  // two software timers (out of 20)
    OneShotTimer timer2(TCK);
    //...
    // or:
    OneShotTimer timer1(PIT);  // two  PIT timers (out of 4)
    OneShotTimer timer2(PIT);

    If this doesn't help I can have a look at details but I would need a minimal version of your sketch which shows the problem so that I can reproduce it. Ideally without need for additional hard/software (I do have an ethernet kit though)

  6. #6
    Junior Member
    Join Date
    Apr 2021
    Posts
    12
    Thanks for helping me!

    I have simplified the sketch so that no external HW is needed. The builtin LED blinks and the blinking period should be set via Ethernet. The following sketch works, but no UDP packets will be received because the line
    Code:
    //  int packetSize = Udp.parsePacket();
    is commented out:

    Code:
    #include <NativeEthernet.h>
    
    #include "Arduino.h"
    #include "TeensyTimerTool.h"
    using TeensyTimerTool::OneShotTimer;
    
    
    
    byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
    IPAddress ip(192, 168, 1, 177);
    uint16_t localPort = 8888;      // local port to listen on
    EthernetLinkStatus link_status = Unknown;
    EthernetUDP Udp;
    char packetBuffer[UDP_TX_PACKET_MAX_SIZE];  // buffer to hold incoming packet,
    
    
    
    OneShotTimer timer1(TeensyTimerTool::TCK);
    
    volatile uint16_t a;
    
    
    bool on = false;
    void callback1() {
      digitalWrite(LED_BUILTIN,on);
      on = !on;
      timer1.trigger(1000*(uint32_t)a);
    }
    
    
    void setup()
    {
      Serial.begin(115200);
      Ethernet.begin(mac, ip);
      if (Ethernet.hardwareStatus() == EthernetNoHardware) {
        Serial.println("Ethernet shield was not found.  Sorry, can't run without hardware. :(");
        while (true) {
          delay(1); // do nothing, no point running without Ethernet hardware
        }
      }
      Serial.println("Ethernet HW found");
    
      pinMode(LED_BUILTIN, OUTPUT);
    
      Serial.println("triggering timers");
    
      timer1.begin(callback1);
      timer1.trigger(100);
      a = 500;
      Serial.println("setup finished");
    }
    
    int count = 0;
    void loop()
    {
      {
    //    Serial.println("checking Ethernet link status...");
        EthernetLinkStatus s = Ethernet.linkStatus();
        if (s != link_status) {
          link_status = s;
          switch (link_status) {
            case LinkON:
              Serial.println("Ethernet cable connected.");
              break;
            case LinkOFF:
              Serial.println("Ethernet cable disconnected.");
              break;
            default: 
              Serial.println("weird link status.");
          }
        }
      }
    
    //  Serial.println("calling Udp.parsePacket()");
    //  noInterrupts(); // does not help
    //  int packetSize = Udp.parsePacket();
    //  interrupts();
    //  Serial.println("Udp.parsePacket() ok");
    
    Serial.println(on);
    
    /*  
      if (packetSize) {
        Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE);
        packetBuffer[packetSize] = '\0';
        int value = 0;
        int i = (packetBuffer[0] == '-' || packetBuffer[0] == '+') ? 1 : 0;
        if (i < packetSize) {
          do {
            char c = packetBuffer[i];
            if (c < '0' || c > '9') goto parse_error;
            value = 10*value + (c-'0');
          } while (++i < packetSize);
          if (packetBuffer[0] == '-') value = -value;
          Serial.print("Received ");
          Serial.println(value);
          value = std::max(std::max(value,-1000),1000);
          a = value;
        } else {
          parse_error:
          Serial.print("Received bad data: \"");
          Serial.print(packetBuffer);
          Serial.println('"');
        }
      }
    */
    
    
        delay(250);
    }
    As soon as I uncomment the line:
    Code:
      int packetSize = Udp.parsePacket();
    there is no blinking, and I get no Serial output in the TeensyMonitor any more.
    Also loading of sketches will from now on requires pushing the button on the teensy.

    Something is very wrong, but probably not the interrupts: I use the TCK timer.

  7. #7
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    1,626
    Thanks that was helpful. I had a quick look at your code.
    Following results:

    1) This is not related to the TimerTool, I get the same problem if I completely remove the TimerTool from the test code.
    2) I traced the issue down to the following function in fnet_socket.c (https://github.com/vjmuzik/FNET/blob..._socket.c#L199)
    Code:
    /************************************************************************
    * DESCRIPTION: This function looking for socket structure
    *              associated with the socket descriptor.
    *************************************************************************/
    static fnet_socket_if_t* _fnet_socket_desc_find(fnet_socket_t desc)
    {
        fnet_socket_if_t* s = FNET_NULL;
    
        if (_fnet_is_enabled && desc)
        {
            s = *(fnet_socket_if_t**)desc;
        }
        return (s);
    }
    Adding a debug print before the assignment to "s" still works, adding it after the assignment doesn't print anymore. I assume that "desc" points to some wrong/protected memory (it obviously isn't a nullptr since the conditional checks for that). The dereferencing might then generate an exception. If, for testing you comment the assignment out, your program doesn't crash anymore (but of course this doesn't fix the underlying root cause).

    I suggest to file an issue on vjmuzik's NativeEthernet gitub repo.
    Last edited by luni; 05-10-2021 at 09:40 AM.

  8. #8
    Junior Member
    Join Date
    Apr 2021
    Posts
    12
    Thanks very much! Your detailed analyses pointed me to the error: I did not call
    Code:
      Udp.begin(localPort);
    in the setup() of my code! Therefore some internals of NativeEthernet were not initialized leading to the problem.
    This is clearly my own mistake, and it happened when I merged my Stepper sketch with my NativeEthernet sketch.
    It has nothing to do with interrupts.
    My sincerest apologies for taking up your time, and thanks again for helping me!
    Yours,
    Johannes

  9. #9
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    1,626
    Glad it works!
    ... and nothing to apologize. At least this made me to finally solder that ethernet adapter :-)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •