В проектах подсветки часто ПЛАВНО приходится менять яркость от текущего состояния к требуемому. В работе с трехцветной лентой вопрос умножается на три. Если изменения цвета вызываются из одной части программы - особо проблем нет. Но у меня несколько режимов работы: где-то цвет плавно зажигается, где-то гаснет, где-то постоянно меняются яркости в каналах. Написал функцию, но что-то она мне не нравится. Слишком много приходится передавать в нее аргументов. Ниже программка целиком. Два вопроса: 1. Можно ли что-то упростить? 2. В функции используются тренарные операторы, но есть и закомментированые обычные "if". Как по-вашему, что занимает меньше места в памяти МК? Код (C): #include <iostream> using namespace std; // Цвета текущие и целевые. Целевые определены стразу для примера. int redOld = 0, greenOld = 0, blueOld = 110, red = 105, green = 100, blue = 0; // Объявим функцию void CalculateNextColor (int* red, int* green, int* blue, int* redOld, int* greenOld, int* blueOld); int main() { do { cout << redOld << " " << blueOld << endl; // Чтобы было видно как работает выведем пару цветов CalculateNextColor (&red, &green, &blue, &redOld, &greenOld, &blueOld); } while(redOld != red); return 0; } // Функция постепенного подтягивания цвета к целевому void CalculateNextColor (int* red, int* green, int* blue, int* redOld, int* greenOld, int* blueOld) { // if (*red > *redOld) *redOld++; // if (*red < *redOld) *redOld--; // if (*green > *greenOld) *greenOld++; // if (*green < *greenOld) *greenOld--; // if (*blue > *blueOld) *blueOld++; // if (*blue < *blueOld) *blueOld--; if (*red != *redOld) { *redOld = (*red > *redOld) ? *redOld = *redOld +1 : *redOld = *redOld - 1; } if (*green != *greenOld) { *greenOld = (*green > *greenOld) ? *greenOld = *greenOld +1 : *greenOld = *greenOld - 1; } if (*blue != *blueOld) { *blueOld = (*blue > *blueOld) ? *blueOld = *blueOld + 1 : *blueOld = *blueOld - 1; } }
В данном случае мы уже видим что есть некая абстрактная сущность ЦВЕТ, которая уже имеет состояние и обладает неким поведением. Один из возможных способов, создание Абстрактного Типа Данных. проще говоря Отдельный объект который будет представлять ЦВЕТ включая сопутствующие абстракции методы. Таким образом вы сможете икапсулировать всю логику работы с цветом в этот объект оставив лишь понятных и читаемый интерфейс. Что как минимум тут же уберет необходимость передачи куда-либо старых значений, так как объект цвета будет хранить текущие состояние (значения каналов) и корректировать их в зависимости от вызываемых методов. Более того, все части ваших программ могут работать с данным интерфейсом а вы в свою очередь сможете изменить логику работы с цветом если потребуется не трогая других частей программы которые работают с цветом Например работа с таким объектом может выглядеть так: Код (Text): RGBColor color = RGBColor( 125, 234, 45 ); color.getRed(); color.getGreen(); color.getBlue(); color.mix( 100, 50, 23 ); Ну и тд и тп, в зависимости от тех действий который вы желаете выполнять с цветом А насчет того что больше места занимает, тут все зависит от компилятора, они порой даже могут некоторые переменные или даже куски кода выкидывать оптимизируя конечную программу Самый наверное простой вариант это сравнить исполняемый файл той и той версии, и тогда будет видно что занимает памяти меньше P.s. Я бы даже предложил указывать каналы не в конкретных значениях от 0 до 255 а в процентах от 0 до 100, а уже конкретные значения использовать лишь в том месте где уже используются именно цифры для записи в аппаратную часть, это позволило бы по всей программе абстрагироваться от конкретных значений, и елси например появится модуль который будет принимать значения от 0 до 1024 не изменится ничего кроме самого класса цвета и того места где эти данные будут переводится в данные для аппаратной части (если это не будет встроено в класс цвета) Но это уже как говорится зависит от того насколько вам это нужно В общем вариантов как сделать красиво и гибко хватает, все зависит от ситуации
Ну, что-то такое я и представлял по книжкам, только опыта маловато. Плюс вот что важно - приведенная здесь программка обладает "прозрачностью" в том плане, что за один вызов функции делается один шаг изменения цвета, то есть нет никаких тормозов. Заменим цикл do/while (redOld != red) на if (redOld != red) и мы можем крутить программу безостановочно, проверяя кнопки, датчики и т.п., при этом продолжая плавно менять яркость. В общем, спасибо, конечно... Но я полагаю пока сделать так: Код (C): int redOld = 0, greenOld = 125, blueOld = 255, red = 255, green = 15, blue = 0; int main() { do{ cout << redOld << " " << blueOld << " " << greenOld << endl; CalculateNextColor(); } while (redOld != red); } void CalculateNextColor (void) { if (*(&red) > *(&redOld)) *(&redOld) = *(&redOld)+1 ; if (*(&red) < *(&redOld)) *(&redOld) = *(&redOld)-1 ; if (*(&green) > *(&greenOld)) *(&greenOld) = *(&greenOld)+1; if (*(&green) < *(&greenOld)) *(&greenOld) = *(&greenOld)-1; if (*(&blue) > *(&blueOld)) *(&blueOld) = *(&blueOld)+1; if (*(&blue) < *(&blueOld)) *(&blueOld) = *(&blueOld)-1; }
Вот что здесь некрасиво - так это передача по указателю, когда можно спокойно передавать по ссылке. Остальная критика: а) использование using namespace std и неиспользование явного указания пространства имен std:: ; b) неиспользование struct и union с массивом, и тем более class для операций с цветом, как объектом, что было бы довольно уместно;
Я извиняюсь за оффтоп, но вот подскажите еще: 1. Зачем зачастую в начале программы делают определение прототипов функций? Что это дает? 2. Посоветуйте наиболее простую среду разработки под С++, что бы отлаживать код (алгоритм) не привязанный к Ардуино?
1. Компилятор, встретив вызов функции, уже знает "с чем ее есть". Для среды Ардуино это не критично, но большей частью это требуется обязательно. 2. Имхо, вот это. И в том числе потому, что есть вот это, сделанное из него.
Компоненты библиотеки Wiring прекрасно интегрируются в самый обычный C++ проект, единственное отличие от Arduino IDE - нужно самому найти и подключить все .c .cpp файлы и настроить пути поиска .h файлов. В той же Code::Blocks IDE довольно продвинутый редактор кода, что делает его написание крайне удобным занятием. Ну а заливку проекта в ардуину или голый МК через avrdude можно повесить через макросы или скрипты на кнопки в меню.
Добрались руки, наконец, и до этого вопроса. Как работа на ошибками, написал три одинаковые программки управления RGB лентой - лампа настроения на С и С++, то есть через структуру и через класс, а также через библиотеку. Напомню задачу: в большом проекте разные куски кода обращаются к ленте с требованием плавно зажечь, погасить, играть цветами. Для облегчения вопроса требуется стандартизация. Конкретно эти программки как лампа настроения избыточны, хотя полностью рабочие, но могут быть кому-то полезны, если встретятся задачи аналогичные моей. На С++. Код (C): #define PIN_RED 3 // Ноги для RGB #define PIN_GREEN 5 #define PIN_BLUE 6 unsigned long timeToChangeColor = 0; // Здесь будет время когда менять цвета #define DELAY_TO_CHANGE_COLOR 20 // Задержка между заменой цветов bool gotAllColor = false; // Информация о том, что все цвета достигли целевых class myRGB { // Класс, описывающий состояие RGB ленты byte red; byte redTarget; byte green; byte greenTarget; byte blue; byte blueTarget; public : myRGB (void) { // Конструктор, устанавливает начальные цвета red = 0; redTarget = 255; green = 0; greenTarget = 255; blue = 0; blueTarget = 255; } // Функция установки целевых цветов void SetTarget(byte newRed, byte newGreen, byte newBlue) { redTarget = newRed; greenTarget = newGreen; blueTarget = newBlue; } bool GotTargetColors (void) { // Функция пошагового приведения каждого цвета к целевому if (red > redTarget) red -= 1; if (red < redTarget) red += 1; if (green > greenTarget) green -= 1; if (green < greenTarget) green += 1; if (blue > blueTarget) blue -= 1; if (blue < blueTarget) blue += 1; analogWrite(PIN_RED, red); // Зажигаем новые цвета analogWrite(PIN_GREEN, green); analogWrite(PIN_BLUE, blue); bool gotAllColors = false; // Возвращаем информацию о приведении цветов // Если все цвета достигли целевых if((red == redTarget) && (green == greenTarget) && (blue == blueTarget)) { gotAllColors = true; } return gotAllColors; // Возвращаем true, иначе false; } }; myRGB rgb; // Создаем класс // Устанавливаем через конструктор целевой цвет белый void setup() { } void loop() { if(millis() > timeToChangeColor) { // Если время менять цвет - меняем // Определяем следующее время замены цвета timeToChangeColor = millis() + DELAY_TO_CHANGE_COLOR; gotAllColor = rgb.GotTargetColors(); // Собственно, меняем и зажигаем } if(gotAllColor) { // Если все цвета достигнуты - случайно выбираем следующий rgb.SetTarget((byte)random(), (byte)random(), (byte)random()); gotAllColor = 0; } } Результат компиляции: На С. Код (C): #define PIN_RED 3 // Ноги для RGB #define PIN_GREEN 5 #define PIN_BLUE 6 unsigned long timeToChangeColor = 0; // Здесь будет время когда менять цвета #define DELAY_TO_CHANGE_COLOR 20 // Задержка между заменой цветов bool gotAllColor = false; // Информация о том, что все цвета достигли целевых struct myRGB { // Структура, описывающая состояие RGB ленты byte red; byte redTarget; byte green; byte greenTarget; byte blue; byte blueTarget; } rgb, *pRGB; // Определяем объект и указатель // Функция установки целевых цветов void SetTarget(myRGB *point, byte newRed, byte newGreen, byte newBlue) { point->redTarget = newRed; point->greenTarget = newGreen; point->blueTarget = newBlue; } bool GotTargetColors (myRGB *point) { // Функция пошагового приведения каждого цвета к целевому if (point->red > point->redTarget) point->red -= 1; if (point->red < point->redTarget) point->red += 1; if (point->green > point->greenTarget) point->green -= 1; if (point->green < point->greenTarget) point->green += 1; if (point->blue > point->blueTarget) point->blue -= 1; if (point->blue < point->blueTarget) point->blue += 1; analogWrite(PIN_RED, point->red); analogWrite(PIN_GREEN, point->green); analogWrite(PIN_BLUE, point->blue); bool gotAllColors = false; // Возвращаем информацию о приведении цветов // Если все цвета достигли целевых if((point->red == point->redTarget) && (point->green == point->greenTarget) && (point->blue == point->blueTarget)) { gotAllColors = true; } return gotAllColors; // Возвращаем true, иначе false; } void setup() { pRGB = &rgb; // Ставим указатель на нашу структуру SetTarget(pRGB, 255, 255, 255); // Устанавливаем целевой цвет белый } void loop() { if(millis() > timeToChangeColor) { // Если время менять цвет - меняем // Определяем следующее время замены цвета timeToChangeColor = millis() + DELAY_TO_CHANGE_COLOR; gotAllColor = GotTargetColors(pRGB); // Собственно, меняем и зажигаем } if(gotAllColor) { // Если все цвета достигнуты - случайно выбираем следующий SetTarget(pRGB, (byte)random(), (byte)random(), (byte)random()); gotAllColor = 0; } } Результат компиляции: И на закуску сформировал простую библиотеку для работы с RGB лентой. Вот пример лампы настроения, включения и выключения цветов с ней: Код (C): #include <RGB.h> #define PIN_RED 3 // Ноги для RGB #define PIN_GREEN 5 #define PIN_BLUE 6 unsigned long timeToChangeColor = 0; // Здесь будет время когда менять цвета #define DELAY_TO_CHANGE_COLOR 20 // Задержка между заменой цветов bool gotAllColor = false; // Информация о том, что все цвета достигли целевых rgb rgb(PIN_RED, PIN_GREEN, PIN_BLUE); // Объект -Лента RGB void setup() { rgb.SwitchOnNow(); // Включим ленту полностью delay(1000); rgb.SwitchOffNow(); // Погасим ленту delay(1000); rgb.SwitchColorNow(125,125,125); // Включим ленту наполовину delay(1000); rgb.SwitchOffNow(); // Погасим ленту delay(1000); rgb.SetTarget(255,255,255); // Установим целевой цвет белый } void loop() { if(millis() > timeToChangeColor) { // Если время менять цвет - меняем // Определяем следующее время замены цвета timeToChangeColor = millis() + DELAY_TO_CHANGE_COLOR; gotAllColor = rgb.GotTargetColors(); // Собственно, меняем и зажигаем } if(gotAllColor) { // Если все цвета достигнуты - случайно выбираем следующий rgb.SetTarget((byte)random(), (byte)random(), (byte)random()); gotAllColor = 0; } } Спасибо всем, кто помогал в этой теме.
Потребовался этот код случаем. Доработал библиотеку - добавил в нее функцию псевдослучайной установки цвета, причем периодически принудительно выставляются чистые цвета. Также в функцию UpdateColors снабдил временем задержки, если нужно плавное изменение цвета. Код лампы настроения теперь выглядит так: Код (C): #include "RGB.h" bool gotAllColors = false; rgb rgb(3,5,6); // Создаем класс, указываем PWM ноги; void setup() { // Примеры использования функций: rgb.SetTimeIntervalToNextColor(100); // ВременнAя задержка при смене цвета, по умолчанию - 10 мс. Работает в функции UpdateColors(); rgb.SwitchOnNow(); // Все включить. Немедленно; delay(3000); rgb.SwitchOffNow(); // Все выключить. Немедленно; delay(3000); rgb.SwitchColorNow(127,0,0); // Включить красный цвет наполовину. Немедленно; delay(3000); rgb.SetTarget(255,255,255); // Установить белый цвет как целевой; } void loop() { // Лампа настроения; if(gotAllColors) { // Если все цвета достигнуты - случайно выбираем следующий; rgb.SetRandomColors(); gotAllColors = 0; } else { gotAllColors = rgb.UpdateColors(); // Собственно, меняем и зажигаем; // Функция UpdateColors() возвращает true, когда достигнут целевой цвет; } }
Библиотека есть, управление аналогичное, только еще 1 параметр добавляется - номер диода. Adafruit_NeoPixel.h
Загрузил второй (последний) вариант библиотеки с примером. При запуске скетча примера, диод горел очень странно. Стал ковырять и в RGB.cpp обнаружил ошибочку, вместо соответствующих цветов стоял red. Код (C++): void rgb::SwitchColorNow(uint8_t red, uint8_t green, uint8_t blue) { analogWrite(pinRed, red); analogWrite(pinGreen, green); // тут был red analogWrite(pinBlue, blue); // тут был red Пример не работал так, как ожидалось. Будьте внимательны и поправте код, если воспользуетесь данной библиотекой.