I have a little project which consists of taking every impulsions from an encoder and sending them through UDP. The encoder delivers 16384 signals (ACP) for 1 turn and 1 signal (ARP) every turn (to mark the north). The encoder can go from 4.5 RPM (ACP period is ~814 µs) to 15 RPM (ACP period is ~244 µs). The goal of this project is to measure the length of each ACP (the high time and period) and see if there is an ARP and send these informations through UDP (I am using an ethernet shield with W5500). For this project, I use an Arduino DUE. To measure the length of the ACP, I use the SysTick value (for maximum accuracy) and I make the difference to have the number of ticks.

For 1 information, I need 4 bytes (I store directly the number of ticks, I transform the number of ticks in the software when I receive my packets). These 4 bytes are stored in a buffer (usually, a buffer of 400x4 bytes) and it is done in an interruption. The ACP high time lasts everytime 80 µs (no matter which the speed rotation). So, the worst case is to have an ACP at 15 RPM (80 µs at high and 164 µs at low). When a buffer is full, I set a flag in my interruption to send the buffer in my main loop and I switch of buffer to fill the other one and so on.

For this project, I use the Ethernet library for DMA purpose (which normally is a good solution to not "disturb" the main program). I picked it from this thread.

Here is my issue: When I send my buffers, I have, every 400 bytes (indeed, total size of the buffer divided by the number of bytes I use. So, if I take a buffer of 500x4 bytes, something will happen every 500 bytes), something happens which, apparently, causes a drop in my ticks (here is the high time of the ACP for a little recording):Ticks drops I spent over 2 months trying to get rid of this issue but I don't manage to understand what happens. Here is my code :

/* ACP_ARP_recorder.                                                                  */
/*                                                                                    */
/* Verson      : 8.0                                                                  */
/* Date        : 26/01/2021                                                           */

#include <Ethernet.h>

// Functions which calculates the difference between 2 values of ticks and return the difference
// The SysTick counts until 84000 (which corresponds to 1 ms) and then is comes back to 0
uint32_t ticks_diff(uint32_t t0, uint32_t t1) {
  return ((t0 < t1) ? 84000 + t0 : t0) - t1;

//#define DEBUG

#define ACP_PIN_HIGH            19
#define ACP_PIN_LOW             20
#define ARP_PIN                 21

#define NB_of_BYTES             4
#define SIZE                    400
#define TOTAL_SIZE              NB_of_BYTES*SIZE
#define BAUDSPEED               57600

byte mac[] = {
  0xA8, 0x61, 0x0A, 0xAE, 0x81, 0xFA

IPAddress ip(192, 168, 1, 20);
unsigned int localPort = 60006;

EthernetUDP Udp;

// Both buffers that store values
volatile char buff1[TOTAL_SIZE];
volatile char buff2[TOTAL_SIZE];

//Variables for interruption routines
volatile uint32_t acpRISE     = 0;
volatile uint32_t acpRISE_pre = 0;
volatile uint32_t acpFALL     = 0;
volatile uint32_t acpHIGH     = 0;
volatile uint32_t acpPERIOD   = 0;

volatile boolean writing          = 0;
volatile boolean arpNbr           = 0;
volatile boolean change_buffer    = false;
volatile unsigned int increment   = 0;

/* Initialization                                                                     */
void setup()
#ifdef DEBUG
  //Setup serial output

  //Setup ACP and ARP inputs

  //Attach interruption at the end of the initialisation (recording starts...)
  attachInterrupt(digitalPinToInterrupt(ACP_PIN_HIGH), acpRising, RISING);
  attachInterrupt(digitalPinToInterrupt(ACP_PIN_LOW), acpFalling, FALLING);
  attachInterrupt(digitalPinToInterrupt(ARP_PIN), arpRising, RISING);

#ifdef DEBUG
  Serial.println("Initialisation ISRs done");

  //Ethernet setup
  Ethernet.begin( mac, ip); //Inialize the Ethernet
#ifdef DEBUG
  Serial.println("Initialisation Ethernet begin done");
  Udp.begin(localPort); //Initialize UDP
#ifdef DEBUG
  Serial.println("Initialisation UDP done");

  while (!Udp.parsePacket()); // Wait for host to send a packet


/* Main loop                                                                          */

void loop()
#ifdef DEBUG
  Serial.print(Udp.remoteIP(), Udp.remotePort());

  //Check if the writing should be done
  if (writing)
    writing = false;

    if (change_buffer) {
      Udp.beginPacket(Udp.remoteIP(), Udp.remotePort()); //Initialize packet send
      Udp.write((char*)buff1, sizeof(buff1) / sizeof(char)); // The cast is needed because buff1 is volatile
      Udp.endPacket(); //End the packet
      Udp.beginPacket(Udp.remoteIP(), Udp.remotePort()); //Initialize packet send
      Udp.write((char*)buff2, sizeof(buff2) / sizeof(char));
      Udp.endPacket(); //End the packet

/* This method is called at the beginning of each ACP.                                */
void acpRising()
  // Storing values for high time and period
  volatile uint32_t acpRISE_now = SysTick->VAL;
  acpPERIOD = ticks_diff(acpRISE, acpRISE_now);
  acpRISE_pre = acpRISE;
  acpRISE = acpRISE_now;

/* This method is called at the end of each ACP.                                      */
void acpFalling()
  // Storing values for high time and period
  volatile uint32_t acpFALL_now = SysTick->VAL;
  acpHIGH = ticks_diff(acpRISE_pre, acpFALL);

  // Checking which buffer needs to be filled
  if (!change_buffer)
    // Filling the buffer
    buff1[increment++] = ((acpHIGH & 0x3F00) >> 8) | ((acpPERIOD & 0x10000) >> 10) | ((arpNbr << 7) & 0x80);
    buff1[increment++] = (acpHIGH & 0xFF);
    buff1[increment++] = ((acpPERIOD & 0xFF00) >> 8);
    buff1[increment++] = (acpPERIOD & 0xFF);

  else {
    buff2[increment++] = ((acpHIGH & 0x3F00) >> 8) | ((acpPERIOD & 0x10000) >> 10) | ((arpNbr << 7) & 0x80);
    buff2[increment++] = (acpHIGH & 0xFF);
    buff2[increment++] = ((acpPERIOD & 0xFF00) >> 8);
    buff2[increment++] = (acpPERIOD & 0xFF);
  arpNbr = 0;

  // If buffer is full ...
  if (increment == NB_of_BYTES * SIZE) {
    // ... retart at the beginning ...
    increment = 0;
    // ... changing buffer ...
    change_buffer = !change_buffer;
    // ... and enables the flag to send the buffer over UDP
    writing = true;
  // Storing values for high time and period
  acpFALL = acpFALL_now;


/* This method is called at the beginning of each ARP.                                */
void arpRising()
  // If ARP, then flag is at 1
  arpNbr = 1;

Finally, here is what I already tried:

  • I checked everything with an oscilloscope and there are not such differences every 400 ACP
  • I checked the duration in interrupts and it takes less than 40 µs for the high and 100 µs for the low
  • I tried to add a dummy byte (so 1 information needs 5 bytes) so the buffer would be 400x5 bytes. The error occurs also every 400 bytes.
  • I tried to mix the library with the TurboSPI library (here)
  • I tried to use the memcpy 32 bits (here) which uses DMA and sending it word per word. So my information would still need 4 bytes but the buffer would store 400 words.
  • I used the regular ethernet library (with regular SPI) and I have the same behaviour. It shows that the library do not put the buffer in DMA so ...

So, what I am trying to ask is whether to have more information of what causes those drops or how to use the DMA to send the buffer from the MCU to the Ethernet shield ?

If you want, I opened a thread in Arduino : https://forum.arduino.cc/index.php?topic=735897.0

Thank you all! :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.