- 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
Источник