我正在寻找一些帮助,了解DMA,串行,管理中断和阻塞代码。
我有一个ESP32 DEVKITC v4,我正在使用它来处理来自硬件和wifi的用户输入。ESP32还负责驱动20x4LCD来显示菜单,以便用户与之交互。
然后我有一个很小的3.5来处理像素驱动。它负责生成像素帧,将其加载到缓冲区并输出信号。我正在运行Adafruit Neopixel Library的修改版本来控制我的TM1814发光二极管
我现在遇到的麻烦是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或外部静态随机存取存储器,这两个单片机的访问。作为缓冲区来存储用户生成的变量,以便在适当时进行轮询。
我对这一切还是个新手,所以欢迎任何进一步的问题,我想知道人们对此的想法。
发布于 2019-12-30 10:56:23
我现在遇到的问题是
和ESP32之间的通信问题。
如果串行链路的两端都有问题,那么应该尝试简化测试设置,以减少变量/未知数的数量。
使用ESP32传输数据,然后尝试使用终端或捕获程序(在PC上?)验证该数据。
或者将录制好的数据从PC传输到Teensy 3.5,然后分析响应。
虽然这段代码大部分都可以工作,但在快速打开编码器时,我仍然在传输中遇到错误。
我在Transmit_Data()过程的ESP_Transmitter代码中发现了一个问题:
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();
}
}https://stackoverflow.com/questions/59510009
复制相似问题