Hi, I am working on a battery powered wearables project where I need to drive 4 LED strips and a 4 5V fairy lights (via darlington transistors plus PWM) using a motherboard PCB I've designed for the Teensy 4.1. I have my system working well, except that when I try to use it with a USB battery back the Teensy 4.1 browns out at a certain point, and gets stuck at a point in the program loop that's extremely hard to track down since it also shuts down serial communication. When I have the system running off a brick or wall wart power supply the loop runs cleanly, and has made it through a 12 hour overnight test.
All the elements of the project are a bit complex to post here all at once, but I wanted to see if y'all could answer some questions that might help me in troubleshooting.
1. What is minimum voltage the Teensy 4.1 can accept at its input before browning out? The product page marks the VIN pin as taking 3.6V to 6V, which seemed like it would be a safe margin for a 5V nominal output. My understanding is that this VIN pin connects to a 3.3V LDO regulator which powers the onboard electronics. When looking at the voltage out of my USB battery with a volt meter I see it fluctuating between 5.5 and 6V with brightness, which doesn't seem like it would cause any issues. I have never seen the voltage duck to anywhere close to 3.6V. Wondering if something else is going on here.
2. Does using the 4.1 SD card reader change voltage requirements at all? Does using a Timer Interrupt change voltage requirements? My assumption is that they wouldn't but I figured I'd check.\
3. Would putting another regulator between the battery voltage and the Teensy input help anything? I have been looking at 4V LDOs which could potentially provide a steady 4V from the 5V nominal input, but my understanding is that the onboard regulator should already be doing this, so I don't know if this would make any difference.
4. Is there anything else y'all can think of that I should check in this type of situation?
Here is the usb battery I am working with: https://www.amazon.com/gp/product/B01LYVF137/ref=ppx_yo_dt_b_asin_title_o03_s00?ie=UTF8&psc=1
Here is the schematic for my driver board:
Here is the code I am running to test with:
All the elements of the project are a bit complex to post here all at once, but I wanted to see if y'all could answer some questions that might help me in troubleshooting.
1. What is minimum voltage the Teensy 4.1 can accept at its input before browning out? The product page marks the VIN pin as taking 3.6V to 6V, which seemed like it would be a safe margin for a 5V nominal output. My understanding is that this VIN pin connects to a 3.3V LDO regulator which powers the onboard electronics. When looking at the voltage out of my USB battery with a volt meter I see it fluctuating between 5.5 and 6V with brightness, which doesn't seem like it would cause any issues. I have never seen the voltage duck to anywhere close to 3.6V. Wondering if something else is going on here.
2. Does using the 4.1 SD card reader change voltage requirements at all? Does using a Timer Interrupt change voltage requirements? My assumption is that they wouldn't but I figured I'd check.\
3. Would putting another regulator between the battery voltage and the Teensy input help anything? I have been looking at 4V LDOs which could potentially provide a steady 4V from the 5V nominal input, but my understanding is that the onboard regulator should already be doing this, so I don't know if this would make any difference.
4. Is there anything else y'all can think of that I should check in this type of situation?
Here is the usb battery I am working with: https://www.amazon.com/gp/product/B01LYVF137/ref=ppx_yo_dt_b_asin_title_o03_s00?ie=UTF8&psc=1
Here is the schematic for my driver board:
Here is the code I am running to test with:
Code:
#include <SD.h>
#include <Adafruit_NeoPixel.h>
#include "CopyRgbSD.h"
#define LED_PIN 12
#define LED_COUNT 96
#define BRIGHTNESS 127
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel strip2(LED_COUNT, 11, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel strip3(LED_COUNT, 10, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel strip4(LED_COUNT, 9, NEO_GRB + NEO_KHZ800);
#define PWM1 2
#define PWM2 3
#define PWM3 4
#define PWM4 5
IntervalTimer myTimer;
uint32_t color;
int pwmVal = 0;
int dir = 8;
uint32_t last2 = 0;
CopyRgbSD copySD;
void doOutputSD(){
__disable_irq();
if(millis() - last2 > 10000){
Serial.println("Running Interrupt");
last2 = millis();
}
for(int i=0; i<strip.numPixels(); i++) {
copySD.readFromBuffers(&color);
strip.setPixelColor(i, color);
strip2.setPixelColor(i, color);
strip3.setPixelColor(i, color);
strip4.setPixelColor(i, color);
}
strip.show();
strip2.show();
strip3.show();
strip4.show();
pwmVal += dir;
if(pwmVal == 256){
dir = -8;
}else if(pwmVal == 0){
dir = 8;
}
analogWrite(PWM1, pwmVal);
analogWrite(PWM2, pwmVal);
analogWrite(PWM3, pwmVal);
analogWrite(PWM4, pwmVal);
__enable_irq();
}
void setup() {
Serial.begin(9600);
delay(3000);
strip.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
strip.show(); // Turn OFF all pixels ASAP
strip.setBrightness(255);
strip2.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
strip2.show(); // Turn OFF all pixels ASAP
strip2.setBrightness(255);
strip3.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
strip3.show(); // Turn OFF all pixels ASAP
strip3.setBrightness(255);
strip4.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
strip4.show(); // Turn OFF all pixels ASAP
strip4.setBrightness(255);
pinMode(PWM1, OUTPUT);
pinMode(PWM2, OUTPUT);
pinMode(PWM3, OUTPUT);
pinMode(PWM4, OUTPUT);
analogWrite(PWM1, 0);
analogWrite(PWM2, 0);
analogWrite(PWM3, 0);
analogWrite(PWM4, 0);
Serial.println("starting");
copySD.begin("RGB11.TXT");
// put your setup code here, to run once:
myTimer.priority(0);
float outputRate = 1000000.0f / 30.0f;
Serial.println(outputRate);
myTimer.begin(doOutputSD, outputRate);
}
uint32_t last = 0;
void loop() {
if(copySD.checkOverrunAndClear()){
Serial.println("Buffer Overrun!");
}
if(millis() - last > 10000){
Serial.print("Time Running:");
Serial.println(millis() / 1000);
last = millis();
}
copySD.copyToBuffers();
// strip.setBrightness(analogRead(A14));
// Serial.println("Main loop");
// put your main code here, to run repeatedly:
}
Code:
//modified from play_sd_wav.cpp by Emmett Palaima
#include <Arduino.h>
#include "CopyRgbSD.h"
#include "spi_interrupt.h"
bool CopyRgbSD::begin(const char *filename)
{
write = read = copy = 0;
isRepeating = true;
if (!(SD.begin(BUILTIN_SDCARD))) {
// stop here, but print a message repetitively
while (1) {
Serial.println("Unable to access the SD card");
delay(500);
}
}
if(!open(filename)){
return false;
}
for(int i = 0; i < COPY_BUFFER_COUNT; ++i){
ready[i] = false;
}
copyToBuffers();
return true;
}
void CopyRgbSD::readFromBuffers(uint32_t *color)
{
if(ready[read] && rgbFile){
*color = copyBuffer[read][index];
index++;
if(index >= FRAME_SIZE){
index = 0;
ready[read++] = false;
if(read >= COPY_BUFFER_COUNT){ read = 0; }
}
}else{
if(!underrun && !underrunInternal && rgbFile){ overrun = true; }
}
}
void CopyRgbSD::copyToBuffers(void){
while(rgbFile && !ready[copy]){
unsigned int avail = rgbFile.available();
if(avail >= MULTI_BUFFER_SIZE){
rgbFile.read(buffer, MULTI_BUFFER_SIZE);
}else{
Serial.println("Rollover");
unsigned int rollover = MULTI_BUFFER_SIZE - avail;
rgbFile.read(buffer, avail);
rgbFile.seek(0);
rgbFile.read(&buffer[avail], rollover);
}
unsigned int count = 0;
uint8_t r, g, b, a;
write = 0;
while(count < MULTI_BUFFER_SIZE){
r = buffer[count++];
g = buffer[count++];
b = buffer[count++];
a = buffer[count++];
copyBuffer[copy][write++] = (a << 24) | (r << 16) | (g << 8) | b;
}
ready[copy++] = true;
if(copy >= COPY_BUFFER_COUNT){ copy = 0; }
}
}
bool CopyRgbSD::open(const char *filename)
{
__disable_irq();
rgbFile = SD.open(filename);
Serial.println(filename);
Serial.println(rgbFile.available());
if(!rgbFile) Serial.println("File not found!");
__enable_irq();
if (!rgbFile) {
return false;
}
rgbFile.seek(0);
return true;
}
Code:
//modified from play_sd_wav.h by Emmett Palaima
#ifndef copy_rgb_sd
#define copy_rgb_sd
#define COPY_BUFFER_COUNT 16
#define FRAME_SIZE 96
#define MULTI_BUFFER_SIZE (FRAME_SIZE * 4)
#include "Arduino.h"
#include "AudioStream.h"
#include "SD.h"
class CopyRgbSD
{
public:
CopyRgbSD(void){};
bool begin(const char *filename);
bool open(const char *filename);
bool setRepeating(bool onOff){ isRepeating = onOff; }
void readFromBuffers(uint32_t *color);
void copyToBuffers(void);
bool checkOverrunAndClear(){
bool check = overrun;
overrun = false;
return check;
};
bool checkUnderrunAndClear(){
bool check = underrun;
underrun = false;
return check;
};
unsigned long getOverrunInterval(){
if(!overrun){ return 0; }
else{
unsigned long count = overrunTime;
overrunTime = 0;
overrun = false;
return count;
}
}
private:
uint32_t copyBuffer[COPY_BUFFER_COUNT][FRAME_SIZE];
bool ready[COPY_BUFFER_COUNT];
int16_t write, read, copy;
int16_t index = 0;
bool error = false;
bool underrun = false;
bool underrunInternal = false;
bool overrun = false;
unsigned long overrunCounter = 0;
unsigned long overrunTime = 0;
File rgbFile;
uint8_t numChannels;
uint8_t buffer[MULTI_BUFFER_SIZE]; // buffer one block of data
bool isRepeating;
};
#endif