TeensyTimerTool & NativeEthernet on teensy 4.1

Status
Not open for further replies.

Johannes

Active member
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!
 
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/TeensyTimerTool/wiki/Avoid-PWM-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.
 
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);
}
 
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)
 
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.
 
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/blo...ba2421f90b58cb20/src/stack/fnet_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:
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
 
Glad it works!
... and nothing to apologize. At least this made me to finally solder that ethernet adapter :)
 
Status
Not open for further replies.
Back
Top