Плавное мигание светодиодов

led

Я пытаюсь сделать простую дыхательную светодиодную схему, в которой интенсивность светодиода варьируется от 0 до 255. Я следовал учебнику Fade на сайте Arduino, который дает мне то, что я хочу, но я замечаю, что изменение яркости гораздо более заметно, когда интенсивность равна 0.

Другими словами, яркость, кажется, отскакивает от 0, а не медленно скользит вверх.

Я попытался сопоставить значение задержки с яркостью: del = map(яркость, 0, 255, 10, 1); но это не помогло.

Отображение, кажется, работает лучше, когда я использую 30 вместо 10 в качестве самого длительного времени задержки: del = map(яркость, 0, 255, 30, 1); но я думаю, что мне действительно нужно реализовать какую-то синусоидальную функцию, чтобы сделать переходы более плавными, но я действительно борюсь с математика... Есть какие-нибудь предложения?

Вот код:

int led = 9;           // the PWM pin the LED is attached to
int brightness = 0;    // how bright the LED is
int fadeAmount = 1;    // how many points to fade the LED by
int del;

// the setup routine runs once when you press reset:
void setup() {
  // declare pin 9 to be an output:
  pinMode(led, OUTPUT);
}

// the loop routine runs over and over again forever:
void loop() {
  // set the brightness of pin 9:
  analogWrite(led, brightness);

  // change the brightness for next time through the loop:
  brightness = brightness + fadeAmount;

  // reverse the direction of the fading at the ends of the fade:
  if (brightness <= 0 || brightness >= 255) {
    fadeAmount = -fadeAmount;
  }

  // here I map the delay to brightness
  del = map(brightness, 0, 255, 10, 1);


  delay(del);
}

, 👍3

Обсуждение

Возможно, используйте синусоидальную волну, чтобы установить яркость. Или, может быть, вам нужна логарифмическая шкала?, @Majenko

Синусоидальная волна была бы хороша, и логарифмическая шкала тоже.. просто я действительно не уверен в том, как их реализовать..., @LaVielle

Я думаю, вам следует заняться гамма-коррекцией. Наше зрение логарифмическое, поэтому для того, чтобы наш глаз увидел что-то в два раза ярче, оно должно быть примерно в 4 раза ярче. Например, используя стандартный скетч затухания, светодиоды начинают довольно быстро увеличивать яркость, но затем, кажется, замедляются., @Gerben

В наши дни существуют так называемые "Цифровые потенциометры", они могут работать с платой, фактически изменяя напряжение на светодиоде, вместо того чтобы имитировать его с помощью ШИМ, @Coder9390


6 ответов


1

Я не уверен, что он будет работать с обычным светодиодом. Некоторые светодиоды 20 мА все еще ярки только при 1 мА. Программируемые светодиоды RGB 5050 (например, NeoPixels) легче обеспечивают плавное визуальное изменение яркости.

Человеческий глаз логарифмичен по яркости света. Для этого можно использовать экспоненту на основе 10 или менее крутую экспоненту на натуральной основе.

Это даст плавное изменение яркости:

y = a * exp (-b * x * x);
// x увеличивается во времени.

Если вам действительно нужно очень плавное изменение яркости для обычного светодиода, вам может понадобиться дополнительный выход с более высоким значением резистора для того же светодиода, для более высокого разрешения при низкой яркости.

,

4

Есть два способа алгоритмически сделать то, что вы хотите, и оба с разными результатами.

Первый - с синусоидальной волной:

for (int i = 0; i < 360; i++) {
    analogWrite(5, (sin(i * 0.0174533) + 1) * 127));
    delay(3);
}

В основном вы двигаетесь на 360 градусов по кругу и берете одну ось (например, вертикальную), смещаете ее так, чтобы все значения были положительными (sin дает значение от -1 до +1), а затем умножаете его, чтобы соответствовать полной яркости.

Это самый плавный способ затухания светодиода, но он не учитывает разную чувствительность вашего глаза к разной яркости (вы видите больше вариаций яркости, когда он тусклый, по сравнению с ярким).

Другой метод гораздо грубее, но учитывает эти различия в чувствительности. Этот конкретный метод дает вам только 8 яркостей, а не 256, но они находятся на логарифмической кривой 2:

for (int i = 0; i < 9; i++) {
    int v = (1 << i) - 1;
    analogWrite(5, v);
    delay(100);
}
for (int i = 7; i > 0; i--) {
    int v = (1 << i) - 1;
    analogWrite(5, v);
    delay(100);
}

Теория этого метода заключается в том, что он использует сдвиг битов, чтобы постепенно заполнить байт 1 от наименее значащего бита вверх.

Когда i равно 0, эта формула:

int v = (i << i) - 1;

дает:

1 << 0 == 0b00000001
0b00000001 - 1 = 0b00000000

Если i равно 4, то вы получите:

1 << 4 == 0b00010000
0b00010000 - 1 = 0b00001111

Таким образом, вы фактически получаете набор значений:

0: 0
1: 1
2: 3
3: 7
4: 15
5: 31
6: 63
7: 127
8: 255
,

Этот код дает-1 3 15 47 127 319 767 1791 4095 1791 767 319 127 47 15 3., @Mark Smith

Кажется, там опечатка - i вместо 1 - легко сделать. Я также исправил еще одну ошибку., @Majenko


1
  1. Используйте прерывание таймера и в Isr измените рабочий цикл генератора шим. Или

  2. Используйте таймер и настройте два канала сравнения с фиксированным смещением. В режиме сравнения Isr переместите математические точки вперед, а затем переверните выходной вывод. Вы увидите, что яркость идет вверх и вниз, скорость которых зависит от смещения.

Нет необходимости в гамма-коррекции.

edit: чтобы продемонстрировать, как может работать 2-й подход, вот краткий пример.

Он работает от TIMER0, выводит сравнение Ch A и Ch B.

TIMER0 устанавливается с помощью прескалера 256x, работающего свободно. Ch A настроен на прерывание каждого тика LED_PR, а Ch B настроен на прерывание каждого тика LED_PR + LED_OFFSET. они запускают один и тот же обработчик прерываний пользователя ->, чтобы перевернуть контакт, прикрепленный к LED_PORT / LED pin.

tmr0_init(TMR0_PS256x); //инициализировать tmr0 с помощью прескалера
tmr0a_setpr(LED_PR); //настроить сравнение вывода ch a period
tmr0a_act(led_flp); / / установить обработчик пользователя для ch a
tmr0b_setpr(LED_PR + LED_OFFSET); //настроить сравнение вывода ch b period со смещением
tmr0b_act(led_flp); //установить обработчик пользователя для ch b
ei();

а вот как это работает в симуляции:

DC будет идти вверх, затем вниз, затем вверх, а затем вниз ...

,

2

Три значения 255 для цветового пространства RGB (255^3) составляют 16 581 375 цветов. К ним относится и уровень яркости. Причина, по которой вы испытываете проблемы с яркостью, связана не с вашей платой arduino, а с цветовым пространством RGB. И, дыша, и я предполагаю здесь, что вы пытаетесь перейти через RGB:

void colorLoop() {  

    static int Col1;
    static int Col2;
    static int Col3;

    static int C1;
    static int C2;
    static int C3;

  if ((((Col1 == 0) || (Col1 == 250)) && (((Col2 == 0) || (Col2 == 250)) && ((Col3 == 0) || (Col3 == 250))))) {

  C1 = random(1, 3);
  C2 = random(1, 3);
  C3 = random(1, 3);
  }

  if (((C1 == 1) && (Col1 != 0))) {
      Col1 = (Col1 - 10);
  }
  if (((C2 == 1) && (Col2 != 0))) {
      Col2 = (Col2 - 10);
  }
  if (((C3 == 1) && (Col3 != 0))) {
      Col3 = (Col3 - 10);
  }

  if (((C1 == 2) && (Col1 != 250))) {
      Col1 = (Col1 + 10);
  }
  if (((C2 == 2) && (Col2 != 250))) {
      Col2 = (Col2 + 10);
  }
  if (((C3 == 2) && (Col3 != 250))) {
      Col3 = (Col3 + 10);
  }

if (((Col1 <= 90) && ((Col2 <= 90) && (Col3 <= 90)))) {  //avoid black
   C1 = 2;
   C2 = 2;
   C3 = 2;
 }

    for (uint8_t i = 0; i < LED_COUNT; i++) {
     leds[i] = CRGB(Col1, Col2, Col3);
    }
    delay(100);
    LEDS.show();
  }

Возможно, вы добьетесь большего успеха или вам будет проще использовать цветовое пространство HSL или HSV. HSV использует оттенок, насыщенность и значение вместо RGB. FastLED оптимизировал свою реализацию HSV, чтобы использовать адресацию 0-255 для Hue (вместо 360), сохраняя ее быстрой для анимации, выполняемой на микроконтроллерах:

https://github.com/FastLED/FastLED/wiki/FastLED-HSV-Colors

вот аналогичный эффект при использовании цветового спектра ВПГ:

void rainbow_fade() {                        

 int ihue = 0; //некоторые микроконтроллеры используют HSV от 0-255 против обычного 0-360
    ihue++;
    if (ihue > 255) {ihue = 0;}
    for(int i = 0 ; i < LED_COUNT; i++ ) {
     leds[i] = CHSV(ihue, 255, 255);
    }
      LEDS.show();    
      delay(100);
  }
,

2

Я подумал о том, чтобы сделать регулярное затухание с логарифмической задержкой: для примера:

val=30*(1-((sqrt(brightness))/(sqrt(255))));
delay(val);
,

0

Совсем недавно я опубликовал сообщение в блоге на эту тему по адресу https://thingpulse.com/breathing-leds-cracking-the-algorithm-behind-our-breathing-pattern/

Оказывается, вы получаете очень приятные результаты, если используете f(x) = e^sin(x). Если вы уменьшите это до 0 и получите амплитуду 255, то формула станет f(x) = (e^sin(x) - 1/e) * (255/(e - 1/e)). Наконец, вы растягиваете x так, чтобы получить частоту 12-20 вдохов в минуту: x * PI/2.

#include <math.h>

void setup() {
  pinMode(9, OUTPUT);
}

void loop() {
  float val = (exp(sin(millis()/2000.0 * PI)) - 0.368) * 108.0;
  analogWrite(9, val);
}
,