I was looking for some help with; understanding DMA, Serial, managing interrupts and blocking code.
I have an ESP32 DEVKITC v4 that I'm using to handle user Input both from hardware such as rotary encoders and from wifi. The ESP32 is also responsible for driving an 20x4 LCD to display a menu for the user to interact with.
I then have a Teensy 3.5 handling the pixel driving. It's handles generating the pixels frame, loading it to the buffer and outputting the signal. I'm running a modified version of the Adafruit Neopixel Library to control my TM1814 LED's
The trouble I'm having at the moment is the communication between the ESP32 and Teensy. The neopixel's code requires blocking in order to get right the timings for the LED driver IC's. while the ESP has interrupts used for the rotary encoder to keep an accurate count, both of these mess with the Serial communication. Here is my test code so far, it is a stripped back version of the final project's code to make it simple to identify problems and help build complexity slowly.
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);
}
Although this code mostly works I still get errors in transmission while turning the encoder quickly. This is where I'm hoping DMA might be a solution. If I understand DMA correctly I can use it and uart, Serial, to send data between the two MCU's and ignore the blocking code and interrupts. Then in the main loop poll DMA buffer and parse the received data but I'm unable to find a solid example of using DMA and Uart. Does anyone know if this will work and if so are there some examples you can link for me to check out?
I would prefer to find a software solution but As a hardware solution I was also looking at using this or an external SRAM that both MCU's have access to. To act as a buffer to store the user generated variables to be polled when appropriate.
I'm still fairly new to all this so any further questions are welcome and I want to know people's thoughts on this.