Тема избитая и повторять то, что было сказано уже 100500 раз до меня я не буду. Поэтому сразу "КОД в студию!":
Ссылка для обязательного ознакомления https://alexgyver.ru/lessons/led-crt/
Этот простой код будет ПЛАВНО зажигать 2 светодиода из выключеннго состояния до максимума не задерживая выполнения основного кода. Из хитрожопости тут использование такой особенности языка Си, как переполнение переменной. После того, как переменная шаг дойдет до своего максимума - 255, следующим числом будет 0 и все повториться заново. Надеюсь тут всем понятно, что для изменения яркости используются порты ардуины с ШИМ, задержка между шагами описана 3-ей строкой. Ну и диоди как бы зажигаются поочередно.
Хорошо, но мало, хотя для какой то визуализации деятельности пойдет.
Хотелось бы что бы диод не только загорался , то и гас плавно. Можно пойти разными путями, я решил изменить тип данных для шага и ввести второй шаг для 2-го светодиод.
Теперь и гаснет и тухнет.. тьфу, зажигается плавно.
Далее можно разделить функции , ввести для каждого свою задержку и свой таймер. Вот так:
Результат намного интереснее. Прям машина времени из малобюджетного фильма.
А как бы сделать так, что бы режим был такой: не горим, не горим, не горим, плавно, плавно, очень плавно зажигаемся, потом сколько то горим , и обратно гаснем, но уже быстрее , чем зажигаемся. И так по кругу..
Для того , что бы описать режим работы воспользуемся такой удобной штукой как массив
В данном массиве будет храниться время каждого этапа в целых секундах. Перевод в миллисекунды, в не целые секунды можете делать сами.
Что представляет собой каждый элемент массива ? 0- общее время цикла. Будет рассчитываться один раз в setup . 2 - время выключенного состояния, 4 время плавного включения, 1 время максимального горения, 2 время выключения.
Массив потому что он очень удобен в использовании . Для упрощения понимания кода я пока оставил только один светодиод.
Что мы добились таким кодом ? Легкого и понятного редактирования в самом начале , в массиве. Выполнение без задержек.
Что тут плохо : очень частое выполнение этого самого кода. Я бы сказал избыточно частое. Если в предыдущих примерах цикл loop крутился более 100 000 раз в секунду, то в данном примере он максимум крутиться 12 000 раз, а минимум ближе к 7 000. Есть несколько вариантов оптимизации, 1) сделать изменение освещенности не более 30 раз в секунду. 2) не вызывать запись в порты, если значение не менялось.
Код с ограничением в 30 обновлений в секунду, по 1-ому варианту, позволяет "крутить " loop более 150 000 раз в секунду.
Добавление второго светодиода сначала хотел оформлять отдельно, но подумав решил, что просто покажу как это сделать сразу.
Код практически полностью повторяет предыдущий, но добавлен еще один внешний цикл.
Часть очередная. Реализация светодиода в виде класса.
Предварительно советую как следует ознакомиться с соответствующим уроком https://alexgyver.ru/lessons/class/
Начнем как всегда с самого простого.
Будем подключать 1 светодиод к ШИМ выходу радуины. А так же подключим одну кнопку на 3-ий пин (у меня, можно любой другой)
В двух словах класс это переменная определенного, хитрого!, типа, которая содержит в себе не только значениЯ (да, их может быть много) но и функции для работы с этими значениями.
Что мы хотим от светодиода ? Для начала, что бы он был куда то подключен, а так же что бы он мог включаться и выключаться.
Для этого создадим свой класс , а в нем несколько полей и методов :
И это уже работает. Казалось бы зачем городить такие сложности ? Просто мы жертвуем объемами и временем программирования для дальнейшего удобства работы, уменьшения числа ошибок.
Данный фрагмент будет включать и выключать светодиод по нажатию кнопки. При этом цикл loop будет крутиться в районе 70 000 раз в секунду. А все из за чрезмерных обращений к портам. Что бы уменьшить кол-во обращений добавим в класс еще одно поле , помимо поля пина, Это будет логическое поле, в которому сохраняется текущее состояние светодиода и посмотрим что будет.
Заметьте, что тут мы изменили только файл класса, а сам проект не трогали и он все равно работает. Это УДОБНО! В результате мы потратили еще немного памяти, но получили ощутимую прибавку в скорости! Теперь цикл loop крутиться более 107 000 раз в секунду. А было 70. В полтора раза быстрее. При этом мы опрашиваем кнопку с максимально возможной скорость.
Ремарка: можно еще ускорить работу программы прямыми чтениями регистров, но тогда потеряется совместимость между разными видами микропроцессоров, поэтому оставим это дело так. Хотите оптимизации ? Делайте непосредственно у себя.
Пример достаточно синтетический. Теперь давайте добавим постепенное зажигание и включение. Сделаем так, что бы пока кнопка была нажата светодиод плавно увеличивал свою яркость. И при отпускании плавно гас. Код я приведу чуть позже, однако замечу, что при выполнении проверок на допустимость и необходимость изменений он зажигает светодиод чрезвычайно быстро и эффект теряется.
Добавим еще одно поле, которое будет "помнить" интервал изменений светодиода, а так же поле последнего изменения яркости. Попутно я добавлю второй светодиод, работающий в противофазе.
В результате работа выполняется, основная программа выглядит чрезвычайно простой, все "сложности" зашиты в класс. При изменении яркости скорость работы программы снижается практически в 2 раза, но как только яркость меняется на конечную цикл loop набирает 100 000 раз в секунду.
Оба файла (примера и класса) я прикрепляю к посту для дальнейшего изучения.
Ссылка для обязательного ознакомления https://alexgyver.ru/lessons/led-crt/
C++:
#define LED_1 9
#define LED_2 10
#define TMD 5 // задержка между шагами
void setup() {
pinMode(LED_1, OUTPUT);
pinMode(LED_2, OUTPUT);
}
uint8_t step = 0;
uint32_t tmr = 0;
void loop() {
if (millis() - tmr > TMD)
{
tmr = millis();
analogWrite(LED_1, step);
analogWrite(LED_2, step + 128);
step++;
}
}
Хорошо, но мало, хотя для какой то визуализации деятельности пойдет.
Хотелось бы что бы диод не только загорался , то и гас плавно. Можно пойти разными путями, я решил изменить тип данных для шага и ввести второй шаг для 2-го светодиод.
Теперь и гаснет и тухнет.. тьфу, зажигается плавно.
C++:
#define LED_1 9
#define LED_2 10
#define TMD 5 // задержка между шанами
void setup() {
pinMode(LED_1, OUTPUT);
pinMode(LED_2, OUTPUT);
}
uint16_t step = 0;
uint16_t step2 = 256;
uint32_t tmr = 0;
void loop() {
if (millis() - tmr > TMD)
{
tmr = millis();
if (step<256) analogWrite(LED_1, step);
else analogWrite(LED_1, 511-step);
if (step2<256) analogWrite(LED_2, step2);
else analogWrite(LED_2, 511-step2);
step++;
if (step==512) step=0;
step2++;
if (step2==512) step2=0;
}
}
C++:
#define LED_1 9
#define LED_2 10
#define TMD 5 // задержка между шанами
#define TMD2 10 // задержка между шанами
void setup() {
pinMode(LED_1, OUTPUT);
pinMode(LED_2, OUTPUT);
}
uint16_t step = 0;
uint16_t step2 = 256;
uint32_t tmr = 0;
uint32_t tmr2 = 0;
void loop() {
if (millis() - tmr > TMD)
{
tmr = millis();
if (step<256) analogWrite(LED_1, step);
else analogWrite(LED_1, 511-step);
step++;
if (step==512) step=0;
}
if (millis() - tmr2 > TMD2)
{
tmr2= millis();
if (step2<256) analogWrite(LED_2, step2);
else analogWrite(LED_2, 511-step2);
step2++;
if (step2==512) step2=0;
}
}
А как бы сделать так, что бы режим был такой: не горим, не горим, не горим, плавно, плавно, очень плавно зажигаемся, потом сколько то горим , и обратно гаснем, но уже быстрее , чем зажигаемся. И так по кругу..
Для того , что бы описать режим работы воспользуемся такой удобной штукой как массив
uint8_t time_delay[] = {0, 2, 4, 1, 2};
В данном массиве будет храниться время каждого этапа в целых секундах. Перевод в миллисекунды, в не целые секунды можете делать сами.
Что представляет собой каждый элемент массива ? 0- общее время цикла. Будет рассчитываться один раз в setup . 2 - время выключенного состояния, 4 время плавного включения, 1 время максимального горения, 2 время выключения.
Массив потому что он очень удобен в использовании . Для упрощения понимания кода я пока оставил только один светодиод.
C++:
#define LED_1 9
// временные интревалы на каждый этам. 0 - общее время, будет считаться отдельно. 2 - время выключенного состояния. 5 время включения. 3 время максимального горения, 1 вреям затухания.
uint8_t time_delay[] = {0, 2, 4, 1, 2};
void setup() {
pinMode(LED_1, OUTPUT);
Serial.begin(115200);
for (uint8_t i = 1; i < 5; i++) time_delay[0] = time_delay[0] + time_delay[i];
}
void loop() {
uint32_t vrem = millis() % (1000 * time_delay[0]); // вычисление времени, по которому будет рассчитан этап
uint8_t n = 0;
uint8_t s = 0;
for (int i = 1; i < 5; i++)
{
n = i;
s = s + time_delay[i];
if (vrem / 1000 < s)break;
} // после выхода мы будем иметь в n номер этапа (1-4) в s будет сумма времен от начала до конца этапа.
//Serial.print(1000 * time_delay[0]); Serial.print(" "); Serial.print(vrem); Serial.print(" "); Serial.print(s); Serial.print(" "); Serial.println(n); // этот вывод что бы посмотреть что получается
switch (n)
{
case 1:
{
digitalWrite(LED_1, LOW);
}
break;
case 2:
{
uint32_t l = 1000 * (s - time_delay[n]);
analogWrite(LED_1, map(vrem, l , s * 1000, 0, 255));
}
break;
case 3:
{
digitalWrite(LED_1, HIGH);
}
break;
case 4:
{
uint16_t l = 1000 * (s - time_delay[n]);
analogWrite(LED_1, map(vrem, l , s * 1000, 255, 0));
}
break;
}
}
Что мы добились таким кодом ? Легкого и понятного редактирования в самом начале , в массиве. Выполнение без задержек.
Что тут плохо : очень частое выполнение этого самого кода. Я бы сказал избыточно частое. Если в предыдущих примерах цикл loop крутился более 100 000 раз в секунду, то в данном примере он максимум крутиться 12 000 раз, а минимум ближе к 7 000. Есть несколько вариантов оптимизации, 1) сделать изменение освещенности не более 30 раз в секунду. 2) не вызывать запись в порты, если значение не менялось.
Код с ограничением в 30 обновлений в секунду, по 1-ому варианту, позволяет "крутить " loop более 150 000 раз в секунду.
C++:
#define LED_1 9
// временные интревалы на каждый этам. 0 - общее время, будет считаться отдельно. 2 - время выключенного состояния. 5 время включения. 3 время максимального горения, 1 вреям затухания.
uint8_t time_delay[] = {0, 2, 4, 1, 2};
void setup() {
pinMode(LED_1, OUTPUT);
Serial.begin(115200);
for (uint8_t i = 1; i < 5; i++) time_delay[0] = time_delay[0] + time_delay[i];
}
uint32_t tmr = millis();
void loop() {
ShowFPS();
if (millis() - tmr < 33) return;
tmr = millis();
uint32_t vrem = millis() % (1000 * time_delay[0]); // вычисление времени, по которому будет рассчитан этап
uint8_t n = 0;
uint8_t s = 0;
for (int i = 1; i < 5; i++)
{
n = i;
s = s + time_delay[i];
if (vrem / 1000 < s)break;
} // после выхода мы будем иметь в n номер этапа (1-4) в s будет сумма времен от начала до конца этапа.
//Serial.print(1000 * time_delay[0]); Serial.print(" "); Serial.print(vrem); Serial.print(" "); Serial.print(s); Serial.print(" "); Serial.println(n); // этот вывод что бы посмотреть что получается
switch (n)
{
case 1:
{
digitalWrite(LED_1, LOW);
}
break;
case 2:
{
uint32_t l = 1000 * (s - time_delay[n]);
analogWrite(LED_1, map(vrem, l , s * 1000, 0, 255));
}
break;
case 3:
{
digitalWrite(LED_1, HIGH);
}
break;
case 4:
{
uint16_t l = 1000 * (s - time_delay[n]);
analogWrite(LED_1, map(vrem, l , s * 1000, 255, 0));
}
break;
}
}
void ShowFPS()
{
static uint32_t tm_m = 0;
static uint32_t cnt_m = 0;
cnt_m++;
if ((millis() - tm_m) > 1000)
{
Serial.print("loop per sec: "); Serial.println(cnt_m);
cnt_m = 0;
tm_m = millis();
}
}
Добавление второго светодиода сначала хотел оформлять отдельно, но подумав решил, что просто покажу как это сделать сразу.
Код практически полностью повторяет предыдущий, но добавлен еще один внешний цикл.
C++:
// временные интревалы на каждый этам. 0 - общее время, будет считаться отдельно. 2 - время выключенного состояния. 5 время включения. 3 время максимального горения, 1 вреям затухания.
uint8_t time_delay[2][5] = {
{0, 2, 4, 1, 2},
{0, 1, 1, 1, 1}
};
uint8_t pins[2]={9,10}; // теперь номера пинов храним в массиве!
void setup() {
Serial.begin(115200);
for (uint8_t j = 0; j < 2; j++)
{
for (uint8_t i = 1; i < 5; i++) time_delay[j][0] = time_delay[j][0] + time_delay[j][i];
pinMode(pins[j], OUTPUT);
}
}
uint32_t tmr = millis();
void loop() {
ShowFPS();
if (millis() - tmr < 33) return;
tmr = millis();
for (uint8_t j = 0; j < 2; j++)
{
uint32_t vrem = millis() % (1000 * time_delay[j][0]); // вычисление времени, по которому будет рассчитан этап
uint8_t n = 0;
uint8_t s = 0;
for (int i = 1; i < 5; i++)
{
n = i;
s = s + time_delay[j][ i];
if (vrem / 1000 < s)break;
} // после выхода мы будем иметь в n номер этапа (1-4) в s будет сумма времен от начала до конца этапа.
//Serial.print(1000 * time_delay[0]); Serial.print(" "); Serial.print(vrem); Serial.print(" "); Serial.print(s); Serial.print(" "); Serial.println(n); // этот вывод что бы посмотреть что получается
switch (n)
{
case 1:
{
digitalWrite(pins[j], LOW);
}
break;
case 2:
{
uint32_t l = 1000 * (s - time_delay[j][n]);
analogWrite(pins[j], map(vrem, l , s * 1000, 0, 255));
}
break;
case 3:
{
digitalWrite(pins[j], HIGH);
}
break;
case 4:
{
uint16_t l = 1000 * (s - time_delay[j][n]);
analogWrite(pins[j], map(vrem, l , s * 1000, 255, 0));
}
break;
}
}
}
void ShowFPS()
{
static uint32_t tm_m = 0;
static uint32_t cnt_m = 0;
cnt_m++;
if ((millis() - tm_m) > 1000)
{
Serial.print("loop per sec: "); Serial.println(cnt_m);
cnt_m = 0;
tm_m = millis();
}
}
AelexGyver, один диод плавно зажигается и гаснет:
void setup() {
pinMode(3, OUTPUT);
}
uint32_t tmr;
int val = 0;
bool dir = true;
void loop() {
if (millis() - tmr >= 20) {
tmr = millis();
val += dir ? (1) : (-1); // увеличиваем или уменьшаем на шаг
if (val >= 255) dir = false; // разворачиваем
if (val <= 0) dir = true; // разворачиваем
analogWrite(3, val);
}
}
bort707, два светодиода в разнобой.:
#define LED_1 9
#define LED_2 10
#define TMD 5 // задержка между шанами
void setup() {
pinMode(LED_1, OUTPUT);
pinMode(LED_2, OUTPUT);
}
int8_t step = 0;
uint32_t tmr = 0;
void loop() {
if (millis() - tmr > TMD)
{
tmr = millis();
analogWrite(LED_1, (abs(abs(2*step) -1)) );
analogWrite(LED_2, (abs(abs(2*(step+128)) -1)));
step++;
}
}
Часть очередная. Реализация светодиода в виде класса.
Предварительно советую как следует ознакомиться с соответствующим уроком https://alexgyver.ru/lessons/class/
Начнем как всегда с самого простого.
Будем подключать 1 светодиод к ШИМ выходу радуины. А так же подключим одну кнопку на 3-ий пин (у меня, можно любой другой)
В двух словах класс это переменная определенного, хитрого!, типа, которая содержит в себе не только значениЯ (да, их может быть много) но и функции для работы с этими значениями.
Что мы хотим от светодиода ? Для начала, что бы он был куда то подключен, а так же что бы он мог включаться и выключаться.
Для этого создадим свой класс , а в нем несколько полей и методов :
У меня этот фрагмент назван "c_led.h":
А это основная программа, которая уже работает с классом:
C++:
class class_LED
{
private:
uint8_t _l_pin;
public:
class_LED(uint8_t l_pin);
void setOn();
void setOff();
};
class_LED::class_LED(uint8_t l_pin)
{
_l_pin = l_pin;
pinMode(_l_pin, OUTPUT);
}
void class_LED::setOn()
{
digitalWrite(_l_pin, HIGH);
}
void class_LED::setOff()
{
digitalWrite(_l_pin, LOW);
}
C++:
#define BTN_PIN 3
#include "c_led.h"
class_LED LED1(9);
void setup() {
Serial.begin(115200);
pinMode(BTN_PIN,INPUT_PULLUP);
}
void loop() {
if (!digitalRead(3)) LED1.setOn(); else LED1.setOff();
ShowFPS();
}
void ShowFPS()
{
static uint32_t tm_m = 0;
static uint32_t cnt_m = 0;
cnt_m++;
if ((millis() - tm_m) > 1000)
{
Serial.print("loop per sec: "); Serial.println(cnt_m);
cnt_m = 0;
tm_m = millis();
}
}
Данный фрагмент будет включать и выключать светодиод по нажатию кнопки. При этом цикл loop будет крутиться в районе 70 000 раз в секунду. А все из за чрезмерных обращений к портам. Что бы уменьшить кол-во обращений добавим в класс еще одно поле , помимо поля пина, Это будет логическое поле, в которому сохраняется текущее состояние светодиода и посмотрим что будет.
C++:
class class_LED
{
private:
uint8_t _l_pin;
bool _state = false;
public:
class_LED(uint8_t l_pin);
void setOn();
void setOff();
};
class_LED::class_LED(uint8_t l_pin)
{
_l_pin = l_pin;
pinMode(_l_pin, OUTPUT);
_state = digitalRead(_l_pin);
}
void class_LED::setOn()
{
if (!_state)
{
digitalWrite(_l_pin, HIGH);
_state = true;
}
}
void class_LED::setOff()
{
if (_state)
{
digitalWrite(_l_pin, LOW);
_state = false;
}
}
Ремарка: можно еще ускорить работу программы прямыми чтениями регистров, но тогда потеряется совместимость между разными видами микропроцессоров, поэтому оставим это дело так. Хотите оптимизации ? Делайте непосредственно у себя.
Пример достаточно синтетический. Теперь давайте добавим постепенное зажигание и включение. Сделаем так, что бы пока кнопка была нажата светодиод плавно увеличивал свою яркость. И при отпускании плавно гас. Код я приведу чуть позже, однако замечу, что при выполнении проверок на допустимость и необходимость изменений он зажигает светодиод чрезвычайно быстро и эффект теряется.
Добавим еще одно поле, которое будет "помнить" интервал изменений светодиода, а так же поле последнего изменения яркости. Попутно я добавлю второй светодиод, работающий в противофазе.
В результате работа выполняется, основная программа выглядит чрезвычайно простой, все "сложности" зашиты в класс. При изменении яркости скорость работы программы снижается практически в 2 раза, но как только яркость меняется на конечную цикл loop набирает 100 000 раз в секунду.
Оба файла (примера и класса) я прикрепляю к посту для дальнейшего изучения.
Вложения
-
1 KB Просмотры: 10
-
585 байт Просмотры: 8
Изменено: