Neopixel的系列问题

时间:2019-12-28 10:24:10

标签: arduino uart dma neopixel

我正在寻找一些帮助;了解DMA,串行,管理中断和阻塞代码。

我有一个ESP32 DEVKITC v4,用于处理来自旋转编码器等硬件和wifi的用户输入。 ESP32还负责驱动20x4 LCD来显示菜单,供用户进行交互。

然后我有一个Teensy 3.5处理像素驱动。它负责生成像素帧,将其加载到缓冲区并输出信号。我正在运行Adafruit Neopixel Library的修改版本来控制我的TM1814 LED

此刻我遇到的麻烦是ESP32与Teensy之间的通信。新像素的代码需要阻塞,以便正确确定LED驱动器IC的时序。当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();
    }
  }
}

TEENSY3.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(串行)在两个MCU之间发送数据,而忽略阻塞代码和中断。然后在主循环中轮询DMA缓冲区并解析接收到的数据,但是我找不到使用DMA和Uart的可靠示例。有谁知道这是否行得通,如果有的话,您可以链接一些示例让我签出吗?

我希望找到一个软件解决方案,但作为硬件解决方案,我还希望使用this或两个MCU都可以访问的外部SRAM。在适当时充当缓冲区存储用户生成的要轮询的变量。

我对这一切仍然很陌生,因此欢迎进一步提出任何问题,我想知道人们对此的想法。

1 个答案:

答案 0 :(得分:0)

  

此刻我遇到的麻烦是ESP32与Teensy之间的通信。

如果串行链接的两端都有问题,则应尝试简化测试设置以减少变量/未知数。
请使用ESP32传输数据,然后尝试使用终端或捕获程序(在PC上)验证该数据。
或将罐装数据从PC传输到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();
}

在禁用中断的情况下执行I / O请求是有问题的。
通常,您总是希望尽量减少禁用中断所花费的时间。

实际上我对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();
  }
}