Всем привет. Нашему отделу была поставлена задача провести презентацию цифрового интерфейса DALI. Причем презентацию с демонстрацией работы этого интерфейса. Если надо — значит, надо. Чего мы только не делали. Для этой цели были предоставлены два модуля управления светодиодными светильниками. Оба оказались ведомыми. А мастер? Начали выбирать контроллер для управления этим интерфейсом. В итоге или цена какая-то заоблачная или сроки поставки такие же. А приближается отпуск, и откладывать уже не хочется. Ещё раз просмотрели характеристики и обратили внимание на особенности данного цифрового протокола:
Всё это показалось очень привлекательным и задача показалась совершенно не сложной. На первый взгляд. Решили сделать мастера DALI на Arduino.
Большое спасибо Тимуру Набиеву за его публикацию на Хабре. Пожалуйста, почитайте. Я не буду повторяться, теорию он прописал неплохо. Схема интерфейса – проще не бывает. Но вот с опубликованной им библиотекой у нас что-то не очень всё получилось.
Поэтому решили сделать свои скетчи. Сделали два. Первый для назначения коротких адресов всем “участникам” сети.
Или скачать
А это тестовый. Управляем двумя модулями подключенными к DALI.
Или скачать
Была проведена очень большая работа и поэтому хочется поделится со всеми. Может это кому-то облегчит разработку.
В сети не найдено ни одной полноценной библиотеки. Пожалуйста, пользуйтесь, все реально работает. Задавайте вопросы, я со своими коллегами постараемся ответить на все. Может не сразу, мы ведь действительно уходим в отпуск на две недели.
В ролике отчет о проведённой работе.
Тестировали модули DAP-04 и LCM-60DA от Mean Well. Но работать будет с любыми другими.
А это схема обвески Arduino переводящая её в режим мастера DALI и блока питания одновременно.
Это подключение кнопок для тестового скетча.
А здесь уже маленькая сеть DALI
- DALI является открытым протоколом;
- DALI- децентрализованная шина, то есть не имеет центрального контроллера и допускает любую топологию.
Всё это показалось очень привлекательным и задача показалась совершенно не сложной. На первый взгляд. Решили сделать мастера DALI на Arduino.
Большое спасибо Тимуру Набиеву за его публикацию на Хабре. Пожалуйста, почитайте. Я не буду повторяться, теорию он прописал неплохо. Схема интерфейса – проще не бывает. Но вот с опубликованной им библиотекой у нас что-то не очень всё получилось.
Поэтому решили сделать свои скетчи. Сделали два. Первый для назначения коротких адресов всем “участникам” сети.
Посмотреть
#define DALI_TX_PIN 3
#define DALI_RX_PIN A0
#define LED_PIN 13
#define RESET 0b00100000
#define INITIALISE 0xA5
#define RANDOMISE 0xA7
#define SEARCHADDRH 0xB1
#define SEARCHADDRM 0xB3
#define SEARCHADDRL 0xB5
#define PRG_SHORT_ADDR 0xB7
#define COMPARE 0xA9
#define WITHDRAW 0xAB
#define TERMINATE 0xA1
#define START_SHORT_ADDR 2
#define DALI_ANALOG_LEVEL 650
#define DALI_HALF_BIT_TIME 416 //microseconds
#define DALI_TWO_PACKET_DELAY 10 //miliseconds
#define DALI_RESPONSE_DELAY_COUNT 15 //максимальное число полубитов
//до ответа
uint8_t ShortAddr = START_SHORT_ADDR;
void setup()
{
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
pinMode(DALI_TX_PIN, OUTPUT);
digitalWrite(DALI_TX_PIN, HIGH);
Serial.begin(115200);
DaliInit();
}
//-----------------------------------------------------
void loop()
{
}
//-----------------------------------------------------
void DaliInit()
{
Serial.println("Initialization...");
DaliTransmitCMD(RESET, 0x00);
delay(2*DALI_TWO_PACKET_DELAY);
DaliTransmitCMD(RESET, 0x00);
delay(2*DALI_TWO_PACKET_DELAY);
delay(100);
DaliTransmitCMD(INITIALISE, 0x00);
delay(DALI_TWO_PACKET_DELAY);
DaliTransmitCMD(INITIALISE, 0x00);
delay(DALI_TWO_PACKET_DELAY);
DaliTransmitCMD(INITIALISE, 0x00);
delay(DALI_TWO_PACKET_DELAY);
delay(100);
DaliTransmitCMD(RANDOMISE, 0x00);
delay(DALI_TWO_PACKET_DELAY);
DaliTransmitCMD(RANDOMISE, 0x00);
delay(DALI_TWO_PACKET_DELAY);
delay(100);
while(ShortAddr < 64)
{
long SearchAddr = 0xFFFFFF;
bool Response = 0;
long LowLimit = 0;
long HighLimit = 0x1000000;
Response = SearchAndCompare(SearchAddr);
delay(DALI_TWO_PACKET_DELAY);
if(Response)
{
digitalWrite(LED_PIN, LOW);
Serial.println("Device detected, address searching...");
if(!SearchAndCompare(SearchAddr - 1))
{
delay(DALI_TWO_PACKET_DELAY);
SearchAndCompare(SearchAddr);
delay(DALI_TWO_PACKET_DELAY);
DaliTransmitCMD(PRG_SHORT_ADDR, ((ShortAddr << 1) | 1));
delay(3*DALI_TWO_PACKET_DELAY);
DaliTransmitCMD(WITHDRAW, 0x00);
Serial.print("24-bit address found: 0x");
Serial.println(SearchAddr, HEX);
Serial.print("Assigning short address ");
Serial.println(ShortAddr);
break;
}
}
else
{
Serial.println("No devices detected");
break;
}
while(1)
{
SearchAddr = (long)((LowLimit + HighLimit) / 2);
Response = SearchAndCompare(SearchAddr);
delay(DALI_TWO_PACKET_DELAY);
if (Response)
{
digitalWrite(LED_PIN, LOW);
if ((SearchAddr == 0) || (!SearchAndCompare(SearchAddr - 1)))
break;
HighLimit = SearchAddr;
}
else
LowLimit = SearchAddr;
}
delay(DALI_TWO_PACKET_DELAY);
SearchAndCompare(SearchAddr);
delay(DALI_TWO_PACKET_DELAY);
DaliTransmitCMD(PRG_SHORT_ADDR, ((ShortAddr << 1) | 1));
delay(5*DALI_TWO_PACKET_DELAY);
DaliTransmitCMD(WITHDRAW, 0x00);
delay(DALI_TWO_PACKET_DELAY);
Serial.print("24-bit address found: 0x");
Serial.println(SearchAddr, HEX);
Serial.print("Assigning short address ");
Serial.println(ShortAddr);
ShortAddr++;
// break; //только для одного модуля
}
delay(DALI_TWO_PACKET_DELAY);
DaliTransmitCMD(TERMINATE, 0x00);
delay(DALI_TWO_PACKET_DELAY);
Serial.println("Init complete");
}
//-------------------------------------------------
bool SearchAndCompare(long SearchAddr)
{
bool Response = 0;
uint8_t HighByte = SearchAddr >> 16;
uint8_t MiddleByte = SearchAddr >> 8;
uint8_t LowByte = SearchAddr;
for(uint8_t i = 0; i < 3; i++)
{
DaliTransmitCMD(SEARCHADDRH, HighByte);
delay(DALI_TWO_PACKET_DELAY);
DaliTransmitCMD(SEARCHADDRM, MiddleByte);
delay(DALI_TWO_PACKET_DELAY);
DaliTransmitCMD(SEARCHADDRL, LowByte);
delay(DALI_TWO_PACKET_DELAY);
}
DaliTransmitCMD(COMPARE, 0x00);
delayMicroseconds(7 * DALI_HALF_BIT_TIME);
for(uint8_t i = 0; i < DALI_RESPONSE_DELAY_COUNT; i++)
{
if (analogRead(DALI_RX_PIN) < DALI_ANALOG_LEVEL)
{
Response = 1;
digitalWrite(LED_PIN, HIGH);
break;
}
delayMicroseconds(DALI_HALF_BIT_TIME);
}
return Response;
}
//-------------------------------------------------
void DaliTransmitCMD(uint8_t Part1, uint8_t Part2)
{
uint8_t DALI_CMD[] = { Part1, Part2 };
//Старт бит
digitalWrite(DALI_TX_PIN, LOW);
delayMicroseconds(DALI_HALF_BIT_TIME);
digitalWrite(DALI_TX_PIN, HIGH);
delayMicroseconds(DALI_HALF_BIT_TIME);
//команда
for (uint8_t CmdPart = 0; CmdPart < 2; CmdPart++)
{
for(int i = 7; i >= 0; i--)
{
bool BitToSend = false;
if ((DALI_CMD[CmdPart] >> i) & 1)
BitToSend = true;
if (BitToSend)
digitalWrite(DALI_TX_PIN, LOW);
else
digitalWrite(DALI_TX_PIN, HIGH);
delayMicroseconds(DALI_HALF_BIT_TIME);
if (BitToSend)
digitalWrite(DALI_TX_PIN, HIGH);
else
digitalWrite(DALI_TX_PIN, LOW);
delayMicroseconds(DALI_HALF_BIT_TIME);
}
}
digitalWrite(DALI_TX_PIN, HIGH);
}
Или скачать
А это тестовый. Управляем двумя модулями подключенными к DALI.
Посмотреть
#define DALI_TX_PIN 3
#define DALI_RX_PIN A0
#define BROADCAST_CMD 0b11111111
#define DOWN 0b00000010
#define UP 0b00000001
#define DALI_CHNL_COUNT 4
#define LAMP_OFF_VALUE 0
#define DALI_HALF_BIT_TIME 416 //microseconds
#define DALI_TWO_PACKET_DELAY 10 //miliseconds
//аналоговые входы
uint8_t AnalogPins[DALI_CHNL_COUNT] = {A1, A2, A3, A4, };
//кнопки
uint8_t KeyPins[DALI_CHNL_COUNT] = {4, 5, 6, 7, };
uint8_t DALIPrevVals[DALI_CHNL_COUNT] = {0, 0, 0, 0};
uint8_t LampState[DALI_CHNL_COUNT] = {0, 0, 0, 0};
void setup()
{
pinMode(DALI_TX_PIN, OUTPUT);
digitalWrite(DALI_TX_PIN, HIGH);
for(uint8_t i = 0; i < DALI_CHNL_COUNT; i++)
{
pinMode(KeyPins[i], INPUT);
digitalWrite(KeyPins[i], HIGH);
}
}
void loop()
{
for(uint8_t PWM = 2; PWM < DALI_CHNL_COUNT; PWM++)
{
if (LampState[PWM] == 1)
{
uint16_t ADCValue = analogRead(AnalogPins[PWM]);
if (ADCValue > 1016)
ADCValue = 1016;
ADCValue /= 4;
uint8_t PWMVal = ADCValue;
if (abs(DALIPrevVals[PWM] - PWMVal) >= 1)
{
DALIPrevVals[PWM] = PWMVal;
DaliTransmitCMD(PWM << 1, PWMVal);
if (LampState[PWM] == 0)
LampState[PWM] = 1;
delay(DALI_TWO_PACKET_DELAY);
}
}
}
for(uint8_t KEY = 0; KEY < DALI_CHNL_COUNT; KEY++)
{
if (digitalRead(KeyPins[KEY]) == LOW)
{
delay(70);
if (KEY == 0)
{
DaliTransmitCMD(BROADCAST_CMD, UP);
delay(DALI_TWO_PACKET_DELAY);
break;
}
else if (KEY == 1)
{
DaliTransmitCMD(BROADCAST_CMD, DOWN);
delay(DALI_TWO_PACKET_DELAY);
break;
}
if (digitalRead(KeyPins[KEY]) == LOW)
{
if (LampState[KEY] == 0)
{
LampState[KEY] = 1;
uint16_t ADCValue = analogRead(AnalogPins[KEY]);
if (ADCValue > 1016)
ADCValue = 1016;
ADCValue /= 4;
uint8_t PWMVal = ADCValue;
DaliTransmitCMD(KEY << 1, PWMVal);
}
else
{
LampState[KEY] = 0;
DaliTransmitCMD(KEY << 1, LAMP_OFF_VALUE);
}
delay(DALI_TWO_PACKET_DELAY);
}
delay(500);
}
}
}
//-------------------------------------------------
void DaliTransmitCMD(uint8_t Part1, uint8_t Part2)
{
uint8_t DALI_CMD[] = { Part1, Part2 };
//Старт бит
digitalWrite(DALI_TX_PIN, LOW);
delayMicroseconds(DALI_HALF_BIT_TIME);
digitalWrite(DALI_TX_PIN, HIGH);
delayMicroseconds(DALI_HALF_BIT_TIME);
//команда
for (uint8_t CmdPart = 0; CmdPart < 2; CmdPart++)
{
for(int i = 7; i >= 0; i--)
{
bool BitToSend = false;
if ((DALI_CMD[CmdPart] >> i) & 1)
BitToSend = true;
if (BitToSend)
digitalWrite(DALI_TX_PIN, LOW);
else
digitalWrite(DALI_TX_PIN, HIGH);
delayMicroseconds(DALI_HALF_BIT_TIME);
if (BitToSend)
digitalWrite(DALI_TX_PIN, HIGH);
else
digitalWrite(DALI_TX_PIN, LOW);
delayMicroseconds(DALI_HALF_BIT_TIME);
}
}
digitalWrite(DALI_TX_PIN, HIGH);
}
Или скачать
Была проведена очень большая работа и поэтому хочется поделится со всеми. Может это кому-то облегчит разработку.
В сети не найдено ни одной полноценной библиотеки. Пожалуйста, пользуйтесь, все реально работает. Задавайте вопросы, я со своими коллегами постараемся ответить на все. Может не сразу, мы ведь действительно уходим в отпуск на две недели.
В ролике отчет о проведённой работе.
Тестировали модули DAP-04 и LCM-60DA от Mean Well. Но работать будет с любыми другими.
А это схема обвески Arduino переводящая её в режим мастера DALI и блока питания одновременно.
Это подключение кнопок для тестового скетча.
А здесь уже маленькая сеть DALI
Комментарии (8)
kolodkin
05.02.2018 05:51У ШИМ шим контроллера случайным образом сгенерировался адрес 0xFFFFFF. Интересно насколько хорошо модули генерируют адреса.
pesp
05.02.2018 10:52Делал такой же мастер, как у Тимура Набиева (огромное ему спасибо за сэкономленное мне время). Все заработало. Причем отлично. Единственный косяк у меня был при компиляции. Пришлось два файла в один слить — и все скомпилировалось. Мой коллега, который на ардуине пол собаки съел, сказал что так бывает.
Делаю так же проект с DALI. Могу предложить схему приемопередатчика для DALI master который хорошо соответствует спецификации стандарта. И к тому же гальваноразвязанный.
rexen
05.02.2018 14:43Одному мне кажется странным на картинках -12VA вместе с GND? Вторая опечатка тут же — VA. Ну нельзя же так.
MEG123
Всё вроде здорово в стандарте, но 1200 бит/с… Пяток-другой RGBW источников с попыткой плавной регулировки поставят всю систему на колени и красивому слайдшоу, а если их всей группой регулировать, то весь смысл поштучной адресации пропадает.
pesp
Тут нужно заметить, что по моему мнению DALI отлично подходит для управления светом в жилых помещениях, а для цветомузыки лучше использовать DMX512.
MEG123
для цветомузыки — да. но мы говорим про подсветку. Сейчас освещение одной комнаты запросто может иметь десяток, а то и два источников. ну вот плавна регулировка такой систеы на DALI упрётся в пропускную способность.
pesp
Не упрется. Если память не изменяет, там есть команда установить яркость светильника с определенной скоростью изменения. Не надо все время долбить команды изменения яркости.