Мы будем отталкиваться от того, что любое событие будь оно внутренним (внутри модуля) или внешним (предназначенным для исполнения другим модулем) передается на шину CAN. Для чего это делается. В первую очередь для того чтобы иметь возможность вести логи. Инициатор события выставляет на шину CAN событие в определенном формате, далее оно доступно всем устройствам на шине.
Структура сообщения CAN
Понимание Титова
Цифровой вход, предусмотреть выбор генерации событий по переходу в 0, 1 и одновременно
Аналоговый вход, генерирует событие при превышении или уменьшении значения от заданного или периодически отправляет сообщения уровня
Кнопка со светодиодом, хитрая, но удобная и простая конструкция, опишу позже
Цифровой выход
Цифровой выход ОК, то же но с открытым коллектором
ШИМ
ШИМ простой, применяется, например, там где не нужно большой точности и скорости см. Управление светодиодами
Серво, для подключения аналоговых сервоприводов.
ПИД, для регулирования медленных процессов, например регулировки температуры
Допустим в модуле 2 нажали на кнопку 5. В сеть CAN пошло сообщение такого формата: 0х02050101,
где:
02 - номер модуля, который генерирует сообщение,
05 - номер кнопки,
01 - тип команды "событие",
01 - логическая единица, т.е. то что нажали кнопку.
Модуль, который запрограммирован на это событие на него прореагирует.
Событие (2.5) {
Порт1 = Событие.значение
}
Событие.значение равно в нашем случае = 01
При передаче 0х02050100, порт, соответственно, примет значение нуля
предварительно Порт1 надо установить:
Установка (Порт1, PA01, выход)
либо в одну строчку
Установка (Порт1, PA01, выход, 2.5)
Можно вообще упростить написание скриптов
Пишем:
2.PA05.Тип = ЦифровойВход
2.PA05 = 3.PA01
Эти две строчки скомпилируют программу сразу для двух модулей.
Попробуем написать сценарий для реальной системы освещения.
Вот наша комната.
Имеем диммируемую лампу 1 (светодиодное освещение по периметру), обычную пятирожковую люстру 2 и торшер 3.
Также два выключателя с несколькими кнопками на каждом a и b.
Начнем со сценариев выключателя a, расположенного у торшера рядом с креслом.
кнопка включения/выключения торшера
кнопка сценария "все включено"
кнопка сценария "минимум света", когда горит только 1 светильник на малой мощности
выключатель b:
кнопка "все включено"
кнопка "минимум"
кнопка "средний свет"
кнопка "все выключено", включая торшер, например когда вы уходите на работу или ложитесь спать
Понимание Чернова
1) Описываем железку
2) Описываем какой порт за что отвечает
3) Вешаем события для исполнения, если надо
4) Пишем код обработки событий
/*
Описываем железки
*/
##UNITS SETUP // Настройка модулей
#UNIT_ID 01 // Описываем модуль с ID 01
/*
Подключаем стандартный обработчик для высоковольтного модуля выходов на мосфетах, с автоматическим контролем прохода через 0, тоесть без диммирования.
*/
UNIT[1].mbHiVoltageInit(_TYPE_MOSFET_W0DETECT_);
/*
После такой инициализации, мы получаем
UNIT[1].HV_Out[0-5]
6 объектов типа HV_Out (HV это я так сократил Хай Волтейдж, может надо как-то по другому обозвать.
Каждый объект имеет несколько методов
. toggle() // Заменяет Вкл на Выкл и обратно.
.set(val); // Ждет значения HIGH или LOW (true, false, ON, OFF)
.get(); // вертает текущее значение вывода
Так-же у объекта есть свойства
.value которое напрямую вызывает методы set() и get().
.parent вертает указатель на родителя (в нашем случае это UNIT[1])
*/
/*
Необходимо сделать инициализацию методов общения с другими модулями.
В нашем случае это может быть I2C или CAN, иногда оба этих метода.
*/
UNIT[1].I2CInit(001); //Прописываем ID модуля для инициализации
UNIT[1].CANInit(001); //Прописываем ID модуля для инициализации
// Описываем следующий модуль
#UNIT_ID 02 // Это пусть будет у нас модуль для обработки кнопок
UNIT[2].mbDigitalInputInit(_TYPE_8_INPUTS_); //Модуль с 8 дискретными входами
/*
Получаем объекты
UNIT[2].dgInput[0-8]
По умолчанию, все входы имею только свойства:
.value которое вернет значения 1 или 0, в некоторых случаях этого будет достаточно, но зачастую на линию вешается сопротивление, для определения обрыва или замыкания на линии, тогда любой вход можно проинициализировать таким образом.
*/
UNIT[2].mbDigitalInputUpdate(_TYPE_UNIVERSAL_INPUTS_,[0,1,2]) // метод ждет тип изменяемых входов, и массив портов для изменения.
/*
После такой инициализации порты 0,1,2 стали универсальными портами входа и получили дополнительные свойства:
.status; // вертает статус порта {OK,LINE_BREAK,LINE_ SHORT}
.analogValue; //вертает аналоговое значение порта от 0 до max_12bit
И получили дополнительный метод инициализации порта, крайних значений, после которых считать обрыв или замыкание.
.initUniversal(min,middle,max) //где min, значение ниже которого считаем обрыв, middle – значение разделяющее 1 и 0, max-значение, выше которого считаем замыкание. Вызов этого метода не обязателен, по умолчанию какие-то значения уже стоят.
*/
UNIT[2].mbDigitalInputUpdate(_TYPE_COUNTER_,[2]) // устанавливаем тип счетчик
/*
После этой инициализации, порт 2 получит еще одно свойство (RW)
.count; //Значение от 0 до max_int
*/
UNIT[2].mbDigitalInputUpdate(_TYPE_BUTTON_,[0,1,3,4]) // устанавливаем тип кнопка
/*
Фактически кнопка ничем не отличается от просто типа digital_input, но при этом у кнопки появляются события:
.onKeyDown();
.onKeyUp();
.onKeyPressed(); // нужно для периодического вызова события пока кнопка нажата
.onKeyClick();
*/
// Закончили описание, всех блоков и их блоков расширения.
/*
Описываем блок Алиасов, для более удобной дальнейшей работы.
*/
##ALIASES
Свет_Люстра = UNIT[1].HV_Out[0];
Свет_Подсветка = UNIT[1].HV_Out[1];
Свет_Торшер = UNIT[1].HV_Out[2];
Свет_Бра = UNIT[1].HV_Out[3];
Свет_Кухня = UNIT[3].HV_Out[0];
Свет_Коридор = UNIT[3].HV_Out[1];
Свет_Туалет = UNIT[3].HV_Out[2];
Выключатель_Зал = UNIT[2].dgInput[0];
Выключатель_Кухня = UNIT[2].dgInput[1];
Выключатель_Корридор = UNIT[2].dgInput[3];
Выключатель_Туалет = UNIT[2].dgInput[4];
Котельная_НасосКотла = UNIT[4].HV_Out[0]
/* Блок алиасов нужен не только для удобства работы, но и для абстрагирования от железа. В случае если надо будет что-то изменить, то достаточно будет пере подвесить алиас на другой модуль или другой порт, и все будет дальше работать как работало.*/
/*
Блок описания глобальных переменных
*/
##GLOBALS
/* Все глобалсы имеют кроме собственно значения, еще и события, на которые можно подписаться. И глобалсы не зависят от физических модулей, доступ к ним есть из любого программного модуля */
GLOBAL Свет_Сцена_Гостинная = {День, Полный_свет, Интим, Просмотр_Телика, Гости};
GLOBAL Отпление_Дом_Т = 22; // Желаемое значение Т в доме
GLOBAL Отпление_Улица_Т = 22; // Т за бортом (обновляться должно не часто)
GLOBAL Время_Время = [23,59]; // Без секунд, кому надо, пусть подписывается, и сам считает секунды. Устанавливать эту переменную, и другие должен GPS модуль, если его нет, то должен быть в системе блок контроля времени…
GLOBAL Время_Суток = {День, Вечер, Ночь, Утро};
GLOBAL Время_Года = {Зима,Весна,Осень,Лето};
GLOBAL Время_Дата = [29,11,2999];
/*
Все глобалсы имеют событие onCange(), на которое может подписаться все кто угодно. Именно из-за этого в глобалы нет смысла пихать часто-изменяемые значения.
И метод
.set() – который позволяет собственно значение переменной установить.
В общем работа с глобалами и производится через вызов:
GLOBALS.Set(name,value);
GLOBALS.Get(name);
Просто в процессе прекомпиляции структуры типа:
Свет_Сцена_Гостинная = Полный_Свет;
Автоматически заменятся на:
GLOBALS.Set(Свет_Сцена_Гостинная, Полный_Свет);
Где Свет_Сцена_Гостинная == 0, а Полный_Свет == 1
*/
/*
Блок описания событий
*/
## EVENTS
Выключатель_Зал.onClick = Свет.ЗалOnOff(); // По кликам выключаем свет
Выключатель_Зал.onKeyPressed = Свет.ЗалПлавнаяСменаСцены(); // Нажать и держать, плавная смена сцены
/*Где «Свет». Это софт-модуль для управления светом. */
Выключатель_Кухня.onClick = Свет.КухняOnOff ();
Выключатель_Корридор.onClick = Свет.КорридорOnOff ();
Выключатель_Туалет.onClick = Свет.ТуалетOnOff ();
Итак определяемся с основными положениями:
1. Для всей структуры автоматизации существует единый скрипт автоматизации
2. При трансляции сначала создаются подскрипты для каждого процессорного модуля.
3. Идет сравнение с актуальными скриптами, при нахождении отличий компилируются бинарники
4. Идет запрос переменных состояния модулей, загрузка бинарников и установка переменных состояния в старое значение.