Не работает ШИМ-затухание светодиода ESP32
Не уверен, что это больше подходит для электронного SE, но я попробую. Я пытаюсь использовать ESP32-C3 и NPN-транзистор (2N5551) для ШИМ-управления светодиодом (параметры указаны ниже). Пробую простое плавное свечение, но светодиод не плавно затухает, а лишь плавно меняет цвет.
Я использую analogWrite (приведён ниже), но почти уверен, что код правильный. Вполне возможно, что проблема в моей пайке, но решил спросить здесь, вдруг я упускаю что-то очевидное?
Я измерил ток светодиода 100 мА при падении напряжения 2,7 В, поэтому подключил его последовательно с резистором 22 Ом. Схема ниже:

#include <Arduino.h>
unsigned long fadeStartTime;
void setup() {
Serial.begin(115200);
while(!Serial); // Ждать, пока serial станет доступен
Serial.println("Starting");
setCpuFrequencyMhz(80);
LOG_INFO(MODULE_NAME, "Finished setup.");
}
void loop() {
unsigned long progress = millis() - fadeStartTime;
long brightness;
if (progress <= 3000) brightness = map(progress, 0, 3000, 0, 255);
else if (progress <= 6000) brightness = map(6000 - progress, 0, 3000, 0, 255);
else fadeStartTime = millis(); // перезапустить затухание снова
analogWrite(STEMS_DATA_PIN, brightness * brightness / 255);
}
@Lorenzo, 👍2
Обсуждение1 ответ
Лучший ответ:
Полная благодарность @6v6gt и другим комментаторам за освещение этих проблем:
1. Схема транзистора неверна для NPN-транзистора.
Из-за свойств n-p-n-транзисторов, при использовании в конфигурации «эмиттерный повторитель», они выдают напряжение только на базу. В этом случае напряжение на R1+D1 будет равно всего 3,3 В от вывода GPIO ESP. Это приводит к меньшему току через светодиод, чем ожидалось. Ниже представлена правильная схема. Подробнее см. в этом ответе.
2. Код затухания был на самом деле неверным
Функция analogWrite микроконтроллера ESP работает с использованием ШИМ. Это прямоугольный сигнал с чередующимися высокими и низкими импульсами, соотношение которых определяет скважность, определяющую средний ток, протекающий через светодиод. Если эта функция вызывается слишком часто, сигнал может не пройти полный цикл импульсов «высокий-низкий», что приведёт к сплошному высокому сигналу. Это исправляется добавлением небольшой задержки delay(50), которая длиннее нескольких циклов ШИМ (в ESP частота по умолчанию составляет 1 кГц).
В моём случае код не мог блокироваться даже на таком небольшом участке, поскольку ему требовалось объединить несколько серверов, а также записать данные на другие линии GPIO для некоторых светодиодов WS2812. Простейший вариант — проверить, изменилось ли записываемое значение. Оно меняется быстро, но достаточно медленно, чтобы импульсы не мешали друг другу.

#include <Arduino.h>
#define DATA_PIN 0
unsigned long fadeStartTime;
unsigned long lastValue;
void setup() {
Serial.begin(115200);
while(!Serial); // Ждать, пока serial станет доступен
Serial.println("Starting");
setCpuFrequencyMhz(80);
LOG_INFO(MODULE_NAME, "Finished setup.");
}
void loop() {
unsigned long progress = millis() - fadeStartTime;
long brightness;
if (progress <= 3000) brightness = map(progress, 0, 3000, 0, 255);
else if (progress <= 6000) brightness = map(progress, 3000, 6000, 255, 0);
else fadeStartTime = millis(); // перезапустить затухание снова
if(brightness != lastValue) {
analogWrite(DATA_PIN, brightness);
lastValue = brightness;
};
}

Интересное наблюдение за поведением analogWrite() esp32. В этой конфигурации вам понадобится последовательный резистор в цепи базы транзистора (например, 680 Ом на 1 кОм). В предыдущей конфигурации эмиттерного повторителя базовый резистор был не нужен. Кроме того, если сопротивление 22 Ом для токоограничивающего резистора светодиода было правильным в предыдущей конфигурации, то в текущей конфигурации, где доступно 5 В вместо примерно 2,7 В, оно, безусловно, будет слишком низким. Это зависит от прямого напряжения светодиода и максимального тока., @6v6gt
@6v6gt Честно говоря, это моё единственное разумное предположение, почему это должно работать с задержкой. Что касается резистора, значения для светодиода указаны на схеме, 22 Ом были рассчитаны для исходных 5 В (с падением напряжения на транзисторе 0,3 В). Я также добавил базовый резистор на 150 Ом, немного погорячился, так как не слишком разбираюсь в этом, и все онлайн-калькуляторы, похоже, давали разные результаты. Отредактировал схему, чтобы добавить его., @Lorenzo
Это действительно «интересное» поведение! В AVR можно вызывать analogWrite() сколько угодно раз, и это не нарушит работу ШИМ., @Edgar Bonet
Из любопытства я только что попробовал оригинальный код автора (с минимальными необходимыми изменениями) как на Uno, так и на Espressif ESP32_C3 devkitC-02 (Arduino ESP32 Core 3.0.4), и не смог воспроизвести проблему. Похоже, функция analogWrite() в ESP32 претерпела ряд изменений. Я бегло просмотрел историю проблем с ESP32 analogWrite на GitHub (сейчас там 56 записей), так что могу предположить, что автору неудачно совпали версии платы и ядра. Тем не менее, бить по analogWrite() на скорости цикла не кажется хорошей идеей., @6v6gt
@6v6gt Думаю, это могло быть ещё и какое-то странное сочетание других проблем. Я не упоминал об этом, но мой светодиод был сильноточным (больше, чем маленький светодиод TH) и был подключён тонким проводом AWG30 длиной 30 см. Какая-то ёмкость / индуктивность / какое там ещё слово из электроники могло сыграть свою роль, ахаха., @Lorenzo
- AnalogWrite никогда не выводит ненулевое напряжение
- «Вручную» генерация ШИМ-сигнала
- Светодиодная панель ESP32 с регулируемой яркостью и MOSFET
- Arduino включение/затухание 5 светодиодов, схема действия
- Чтение аналогового значения при генерации сигнала ШИМ
- ШИМ с ТРАНЗИСТОРОМ на гибкой светодиодной нити
- Помогите с простым постепенно более ярко светящим светодиодом
- Какова частота PWM-выхода на Arduino
Пожалуйста, приведите достаточно кода для компиляции. Похоже, затухание может происходить так быстро, что вы его не видите. Схема (эмиттерный повторитель) здесь необычна, поскольку ограничивает выходное напряжение 3,3 В. Лучше использовать резистор сопротивлением 680 Ом/1 кОм между выводом ESP32 и базой транзистора, подключить эмиттер к земле и подключить резистор/светодиод между 5 В и коллектором. В этом случае сопротивление 22 Ом будет слишком низким., @6v6gt
поместите транзистор между светодиодом и землей, @jsotola
Из-за остальной проводки, связанной с другими частями проекта, гораздо проще использовать общий заземляющий кабель с другими компонентами, поэтому транзистор не может быть идеально расположен там. Не могли бы вы вкратце объяснить, почему это лучше? Почему это ограничит выходное напряжение 3 В?, @Lorenzo
Пожалуйста, прочтите [этот ответ](https://electronics.stackexchange.com/a/303184) относительно того, почему следует использовать драйвер BJT верхнего плеча для управления светодиодом., @hcheung
Как вы показали, особенностью конфигурации «транзисторный эмиттерный повторитель» является то, что выходное напряжение ограничено напряжением базы (минус около 0,7 В). ESP32 может выдавать только 3,3 В. Вам нужно протестировать его, поискать в Google или задать вопрос на https://electronics.stackexchange.com/. Не думайте об использовании PNP-транзистора для достижения ваших целей, так как 3,3 В будет недостаточно для его выключения в 5-вольтовой схеме. Как насчёт демонстрации компилируемой версии вашего кода, которая наглядно демонстрирует вашу проблему?, @6v6gt
@6v6gt, хорошо, спасибо большое. После небольшого поиска в Google я убедился, что проблема, скорее всего, именно в этом (но я также обновил код выше)! Вы были первым, кто ответил, так что если вы напишите это как ответ, я буду рад принять его! Вот полезная ссылка, которую я нашёл, если хотите добавить: https://electronics.stackexchange.com/questions/57845/why-would-one-drive-leds-with-a-common-emitter, @Lorenzo
Хорошо, но, проверив ваш код (без тестирования), я всё ещё думаю, что исходная проблема осталась. Попробуйте добавить задержку (50) в цикл, чтобы замедлить эффект затухания., @6v6gt
ОТ: Ваше второе сопоставление можно упростить до
map(progress, 3000, 6000, 255, 0), что сделает ваши намерения гораздо более понятными., @the busybee@6v6gt Честно говоря, я очень сомневался в тебе — извини :) — но после того, как я попробовал другую схему транзисторов, у меня всё равно возникли проблемы! Добавил задержку, и всё заработало. Мне нужно, чтобы код был неблокирующим, поэтому я заменил её на
if(value != lastValue), и теперь всё работает отлично! Полагаю, он слишком часто пытался выполнитьanalogWriteи нарушал рабочий цикл?, @LorenzoХорошо, что теперь всё работает. Поскольку вы разработали рабочий код и смогли его протестировать, теперь вы можете написать свой ответ, включив этот код, и, возможно, получить несколько очков Brownie. Также воспользуйтесь возможностью объяснить это:
analogWrite(STEMS_DATA_PIN, bright * bright / 255);вместо простоanalogWrite(STEMS_DATA_PIN, bright );. Коэффициент заполнения будет увеличиваться нелинейно, а значения яркости <16 дадут 0 (целочисленное деление)., @6v6gt