Серийные проблемы с Neopixel

Я искал помощи; понимание DMA, последовательного порта, управления прерываниями и кода блокировки.

У меня есть ESP32 DEVKITC v4, который я использую для обработки пользовательского ввода как с оборудования, такого как поворотные энкодеры, так и с Wi-Fi. ESP32 также отвечает за управление ЖК-дисплеем 20x4 для отображения меню, с которым пользователь может взаимодействовать.

Затем у меня есть Teensy 3.5, обрабатывающий пиксельное вождение. Он обрабатывает создание кадра пикселей, загрузку его в буфер и вывод сигнала. Я использую модифицированную версию Adafruit Neopixel Library для управления своими светодиодами TM1814.

Проблема, с которой я столкнулся на данный момент, - это связь между ESP32 и Teensy. Код neopixel требует блокировки, чтобы получить правильные тайминги для ИС драйвера светодиода. в то время как ESP имеет прерывания, используемые поворотным энкодером для точного подсчета, оба из них мешают последовательной связи. Вот мой тестовый код, это урезанная версия окончательного кода проекта, чтобы упростить выявление проблем и помочь медленно наращивать сложность.

ESP_Transmitter

    #include <Rotary.h>

#define RTS_PIN 5

int previousArray;
int previousRGBW;

#define inPinA  35

//rotary acceleration variables
int rotaryTime;
volatile int counterA;
volatile int counterB;
byte enableAcceleration;
bool lockFlag = false;

Rotary rotaryA = Rotary(32, 33);

//teensy is expecting data <rgbwArrayToTeensy,rgbwToTeensy>
typedef struct ESPtransmit_t {
  char startMarker;
  int rgbwArrayToTeensy;
  char comma;
  int rgbwToTeensy;
  char endMarker;
};

typedef union Channel_Packet_t {
  ESPtransmit_t rgbwLED;
  byte ChannelPacket[sizeof(ESPtransmit_t)];
};

Channel_Packet_t blueOn;

void setup() {
  Serial.begin(9600);
  Serial2.begin(115200, SERIAL_8N1, 16, 17);

  while (!Serial);
  while (!Serial2);

  pinMode(RTS_PIN, INPUT);
  attachInterrupt(digitalPinToInterrupt(RTS_PIN), Transmit_Data, RISING);
  attachInterrupt(digitalPinToInterrupt(32), rotateA, CHANGE);
  attachInterrupt(digitalPinToInterrupt(33), rotateA, CHANGE);

}

void loop() {
  blueOn.rgbwLED = {'<', 2, ',', counterA, '>'};
}


void Transmit_Data() {
  noInterrupts();
  if (previousRGBW != blueOn.rgbwLED.rgbwToTeensy) {
    Serial2.write(blueOn.ChannelPacket, sizeof(ESPtransmit_t));
    Serial.println("send");
    previousRGBW = blueOn.rgbwLED.rgbwToTeensy;
  }
  interrupts();
}

void rotateA() {
  int speedMultiplier = 1;
  unsigned char result = rotaryA.process();
  if (lockFlag == false) {
    if (result == DIR_CW) {
      if (millis() - rotaryTime <= 10 && enableAcceleration == 0x01) {
        speedMultiplier = 7;

      }
      else if (digitalRead(inPinA) == HIGH) {
        speedMultiplier = 700;
      }
      counterA += speedMultiplier;
      rotaryTime = millis();
    }
    else if (result == DIR_CCW) {
      if (millis() - rotaryTime <= 10 && enableAcceleration == 0x01) {
        speedMultiplier = 7;

      }
      else if (digitalRead(inPinA) == HIGH) {
        speedMultiplier = 700;
      }
      counterA -= speedMultiplier;
      rotaryTime = millis();
    }
  }
}

ПОДРОСТКА3.5_RECEIVER

// include the library code:
#include <Adafruit_NeoPixel.h>

//number of LEDs in Strip
int NUM_LEDS = 52;

//data and clock pin RGB
#define DATA_PINA 11

#define RTR_PIN 28

uint32_t amp = ((uint32_t)63 << 24) | ((uint32_t)63 << 16) | ((uint32_t)63 <<  8) | 63;

Adafruit_NeoPixel pixelsA(NUM_LEDS, DATA_PINA, NEO_WRGB + NEO_KHZ800);

struct Received_Data_t {
  char startMarker;
  int rgbwArrayFromESP;
  char comma;
  int rgbwFromESP;
  char endMarker;
};

union Channel_Packet_t {
  Received_Data_t rgbwLED;
  byte ChannelPacket[sizeof(Received_Data_t)];
};

Channel_Packet_t LEDon;

//apeture controls
int apeture = NUM_LEDS;
int apeturePosition = NUM_LEDS / 2;

//RGB Sub Menu Variables
int rgbArraySelector;
uint8_t subRed;
uint8_t subGreen;
uint8_t subBlue;
uint8_t subWhite;

uint8_t rgbwArray [] = {subRed, subGreen, subBlue, subWhite};

const byte numChars = sizeof(Received_Data_t);
char receivedChars[numChars];

int rgbwFromESP = 0;

boolean newData = false;

void setup() {
  Serial1.setTX(26);
  Serial1.setRX(27);
  Serial1.begin(115200);
  Serial.begin(9600);

  while (!Serial);
  while (!Serial1);

  pixelsA.begin(); // INITIALIZE NeoPixel strip object

  //clear the LEDS
  pixelsA.fill(amp, ~amp, pixelsA.Color(0, 0, 0, 0), 0, NUM_LEDS);
  pixelsA.show();

  pinMode(RTR_PIN, OUTPUT);
  digitalWrite(RTR_PIN, LOW);

}

void loop() {
  Read_to_Receive(); //activate transmission 
  recvWithStartEndMarkers(); //read buffer
  if (newData == true) {
    parseData();
    showParsedData();
    newData = false;
  }
}

void LED_clear() {

  pixelsA.fill(amp, ~amp, pixelsA.Color(0, 0, 0, 0), 0, NUM_LEDS);
  pixelsA.show();

}

void LED_RGBW() {
  pixelsA.fill(amp, ~amp, pixelsA.Color(0, 0, 0, 0), 0, NUM_LEDS);
  pixelsA.fill(amp, ~amp, pixelsA.Color(rgbwArray[0], rgbwArray[1], rgbwArray[2], rgbwArray[3]), apeturePosition, apeture / 2);
  pixelsA.fill(amp, ~amp, pixelsA.Color(rgbwArray[0], rgbwArray[1], rgbwArray[2], rgbwArray[3]), apeturePosition - (apeture / 2), apeture / 2);
  pixelsA.show();
}

void recvWithStartEndMarkers() {
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char rc;
  while (Serial1.available() > 0 && newData == false) {
    rc = Serial1.read();

    if (recvInProgress == true) {
      if (rc != endMarker) {
        receivedChars[ndx] = rc;
        ndx++;
        if (ndx >= numChars) {
          ndx = numChars - 1;
        }
      }
      else {
        receivedChars[ndx] = rc; // terminate the string
        recvInProgress = false;
        ndx = 0;
        newData = true;
      }
    }

    else if (rc == startMarker) {
      receivedChars[ndx] = rc;
      ndx++;
      recvInProgress = true;
    }
  }
}
//============

void Read_to_Receive() {

  pixelsA.fill(amp, ~amp, pixelsA.Color(0, 0, 0, 0), 0, NUM_LEDS);
  pixelsA.fill(amp, ~amp, pixelsA.Color(rgbwArray[0], rgbwArray[1], rgbwArray[2], rgbwArray[3]), apeturePosition, apeture / 2);
  pixelsA.fill(amp, ~amp, pixelsA.Color(rgbwArray[0], rgbwArray[1], rgbwArray[2], rgbwArray[3]), apeturePosition - (apeture / 2), apeture / 2);
  digitalWrite(RTR_PIN, LOW);
  pixelsA.show();
  digitalWrite(RTR_PIN, HIGH);
  //wait for ESP to transmit
  delay(1);
}

//============

void parseData() {      // split the data into its parts
  for (uint8_t k = 0; k < sizeof(Received_Data_t); k++) {
    LEDon.ChannelPacket[k] = receivedChars[k];
  }
  rgbArraySelector = LEDon.rgbwLED.rgbwArrayFromESP;
  rgbwFromESP = LEDon.rgbwLED.rgbwFromESP;

  rgbwArray[rgbArraySelector] = rgbwFromESP;
}

//============
void showParsedData() {
  Serial.print("Array ");
  Serial.println(rgbArraySelector);
  Serial.print("Intensity ");
  Serial.println(rgbwFromESP);

}

Хотя этот код в основном работает, я все равно получаю ошибки при передаче при быстром повороте кодировщика. Вот где я надеюсь, что DMA может быть решением. Если я правильно понимаю DMA, я могу использовать его и uart, Serial, для передачи данных между двумя MCU и игнорировать код блокировки и прерывания. Затем в основном цикле опрашиваем буфер DMA и анализируем полученные данные, но я не могу найти убедительного примера использования DMA и Uart. Кто-нибудь знает, будет ли это работать, и если да, то есть ли примеры, на которые вы можете ссылаться, чтобы я мог их проверить?

Я бы предпочел найти программное решение, но в качестве аппаратного решения я также искал использование этого или внешняя SRAM, к которой имеют доступ оба MCU. Действовать как буфер для хранения сгенерированных пользователем переменных для опроса при необходимости.

Я все еще новичок во всем этом, поэтому любые дополнительные вопросы приветствуются, и я хочу знать, что думают люди по этому поводу.


person Connor N    schedule 28.12.2019    source источник
comment
Код неопикселя требует блокировки ... - Не могли бы вы объяснить, что это значит? Если прикладная программа выдает системный запрос блокировки, это просто означает, что программа приостанавливается до тех пор, пока запрос не будет завершен; в это время система может выполнять другие задачи. OTOH, если к устройству предъявляются определенные требования к времени, возможно, вам придется ввести задержки между операциями. Если вы используете занятое ожидание, значит, вы заблокировали ЦП и больше ничего не можете сделать. Такая задержка не называется блокировкой (по крайней мере, в той информатике, которую я изучил).   -  person sawdust    schedule 29.12.2019
comment
Извините за путаницу, я использую ту же терминологию, что и автор кода Neopixel. Когда они говорят о блокировке, как вы сказали, система имеет встроенные задержки для синхронизации высокого и низкого состояния вывода для отправки данных. прерывания также полностью отключаются в течение всего времени отправки, чтобы избежать проблем с синхронизацией.   -  person Connor N    schedule 29.12.2019


Ответы (1)


Проблема, с которой я столкнулся на данный момент, - это связь между ESP32 и Teensy.

Если у вас есть проблемы на обоих концах последовательного канала, вам следует попытаться упростить настройку теста, чтобы уменьшить количество переменных / неизвестных.
Либо используйте ESP32 для передачи данных, а затем попробуйте проверить эти данные с помощью терминал или программу захвата (на ПК?).
Или передайте стандартные данные с ПК на Teensy 3.5, а затем проанализируйте ответ.


Хотя этот код в основном работает, я все равно получаю ошибки при передаче при быстром повороте кодировщика.

Я вижу проблему в вашем коде ESP_Transmitter, процедуре Transmit_Data ():

void Transmit_Data() {
  noInterrupts();
  if (previousRGBW != blueOn.rgbwLED.rgbwToTeensy) {
    Serial2.write(blueOn.ChannelPacket, sizeof(ESPtransmit_t));
    Serial.println("send");
    previousRGBW = blueOn.rgbwLED.rgbwToTeensy;
  }
  interrupts();
}

Выполнение запросов ввода-вывода с отключенными прерываниями проблематично.
В общем, вы всегда хотите минимизировать время, затрачиваемое с отключенными прерываниями.

На самом деле я ничего не знаю об Arduino, но ...
Похоже, что previousRGBW и / или blueOn являются критическими областями, которые необходимо защитить, и последовательные операции могут выполняться вне этого раздела кода.
Чтобы защитить blueOn просто сделайте копию blueOn.ChannelPacket только для последовательной передачи.
Что-то вроде следующего должно стать улучшением:

byte xmitPacket[sizeof(ESPtransmit_t)];

void Transmit_Data() {
  noInterrupts();
  if (previousRGBW != blueOn.rgbwLED.rgbwToTeensy) {
    memcpy(xmitPacket, blueOn.ChannelPacket, sizeof(ESPtransmit_t));
    previousRGBW = blueOn.rgbwLED.rgbwToTeensy;
    interrupts();

    Serial2.write(xmitPacket, sizeof(ESPtransmit_t));
    Serial.println("send");
  } else {
    interrupts();
  }
}
person sawdust    schedule 30.12.2019
comment
Привет, Sawdust, Спасибо за предложения, я полагаю, мне следовало быть яснее. Я могу наладить связь между ними для работы на базовом уровне. Я могу отправить blueOn с esp32 и правильно получить и проанализировать его на teesny 3.5. когда я представляю pixelsA.show(); и прерывания энкодера, у меня возникают проблемы. Я обязательно учту ваше предложение по защите blueOn. - person Connor N; 31.12.2019