DMA не работает между Arduino DUE и ethernet shiled (W5500)

У меня есть небольшой проект, который состоит в том, чтобы принимать все импульсы от кодировщика и посылать их через UDP. Кодер подает 16384 сигнала (ACP) на 1 оборот и 1 сигнал (ARP) на каждый поворот (для обозначения севера). Кодер может идти от 4,5 об / мин (период ACP ~814 мкс) до 15 об / мин (период ACP ~244 мкс). Цель этого проекта состоит в том, чтобы измерить длину каждого ACP (the high time and period) и посмотреть, есть ли ARP, и отправить эти данные через UDP (я использую Ethernet Shield с W5500). Для этого проекта я использую Arduino DUE. Чтобы измерить длину ACP, я использую значение SysTick (для максимальной точности), и я делаю разницу, чтобы иметь количество тиков.

Для 1 информации мне нужно 4 байта (я храню непосредственно количество тиков, я преобразую количество тиков в программном обеспечении, когда я получаю свои пакеты). Эти 4 байта хранятся в буфере (обычно это буфер размером 400х4 байта), и это делается в режиме прерывания. Высокое время ACP длится каждый раз 80 мкс (независимо от того, какая скорость вращения). Таким образом, наихудший случай-это ACP при 15 об / мин (80 мкс на максимуме и 164 мкс на минимуме). Когда буфер заполнен, я устанавливаю флаг в своем прерывании, чтобы отправить буфер в мой основной цикл, и я переключаю буфер, чтобы заполнить другой и так далее.

Для этого проекта я использую библиотеку Ethernet для DMA (которая обычно является хорошим решением, чтобы не "мешать" основной программе). Я выбрал его из этой ниточки.

Вот моя проблема: когда я отправляю свои буферы, у меня есть, каждые 400 байт (действительно, общий размер буфера делится на количество байтов, которые я использую. Итак, если я возьму буфер размером 500х4 байта, что-то произойдет каждые 500 байт), что-то произойдет, что, по-видимому, вызывает падение моих тиков (вот большое время ACP для небольшой записи):Ticks drops Я потратил более 2 месяцев, пытаясь избавиться от этой проблемы, но мне не удается понять, что происходит. Вот мой код :

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

#include <Ethernet.h>

// Функции, которые вычисляют разницу между 2 значениями тиков и возвращают разницу
// SysTick считает до 84000 (что соответствует 1 мс), а затем возвращается к 0
uint32_t ticks_diff(uint32_t t0, uint32_t t1) {
  return ((t0 < t1) ? 84000 + t0 : t0) - t1;
}

//#define DEBUG

//Constants
#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;

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


////Переменные для прерываний процедур
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
  Serial.begin(BAUDSPEED);
#endif

  //Setup ACP and ARP inputs
  pinMode(ACP_PIN_HIGH, INPUT_PULLUP);
  pinMode(ACP_PIN_LOW, INPUT_PULLUP);
  pinMode(ARP_PIN, INPUT_PULLUP);

  //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");
#endif

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

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

  delay(1500);
}

/**************************************************************************************/
/* Main loop                                                                          */
/**************************************************************************************/


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

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

    if (change_buffer) {
      Udp.beginPacket(Udp.remoteIP(), Udp.remotePort()); //Инициализировать отправку пакета
      Udp.write((char*)buff1, sizeof(buff1) / sizeof(char)); // Приведение необходимо, потому что buff1 является изменчивым
      Udp.endPacket(); //Завершение пакета
    }
    
    else
    {
      Udp.beginPacket(Udp.remoteIP(), Udp.remotePort()); //Инициализировать отправку пакета
      Udp.write((char*)buff2, sizeof(buff2) / sizeof(char));
      Udp.endPacket(); //End the packet
    }
  }
}



/**************************************************************************************/
/* Этот метод вызывается в начале каждого 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;
}

/**************************************************************************************/
/* Этот метод вызывается в конце каждого 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) {
    // ... ретарт в самом начале ...
    increment = 0;
    // ... изменение буфера ...
    change_buffer = !change_buffer;
    // ... и позволяет флагу отправлять буфер по UDP
    writing = true;
  }
  
  // Storing values for high time and period
  acpFALL = acpFALL_now;

}

/**************************************************************************************/
/* Этот метод вызывается в начале каждого ARP.                                        */
/**************************************************************************************/
void arpRising()
{
  // If ARP, then flag is at 1
  arpNbr = 1;
}

Наконец, вот что я уже попробовал:

  • Я проверил все с помощью осциллографа, и нет таких различий каждые 400 ACP
  • Я проверил длительность прерываний, и это занимает менее 40 мкс для максимума и 100 мкс для минимума
  • Я попытался добавить фиктивный байт (поэтому для 1 информации нужно 5 байтов), чтобы буфер был 400x5 байт. Ошибка возникает также каждые 400 байт.
  • Я попытался смешать библиотеку с библиотекой TurboSPI (здесь)
  • Я попытался использовать memcpy 32 бит (здесь), который использует DMA и отправляет его слово в слово. Таким образом, моя информация все равно будет нуждаться в 4 байтах, но буфер будет хранить 400 слов.
  • Я использовал обычную библиотеку Ethernet (с обычным SPI), и у меня такое же поведение. Это показывает, что библиотека не помещает буфер в DMA, так что ...

Итак, я пытаюсь спросить, нужно ли иметь больше информации о том, что вызывает эти падения, или как использовать DMA для отправки буфера из MCU в Ethernet Shield ?

Если хотите, я открыл нить в Arduino : https://forum.arduino.cc/index.php?topic=735897.0

Спасибо всем! :)

, 👍0