Интерфейс DALI и Arduino. Реальный сюрреализм

Всем привет. Нашему отделу была поставлена задача провести презентацию цифрового интерфейса 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 и Arduino. Реальный сюрреализм

Это подключение кнопок для тестового скетча.

А здесь уже маленькая сеть DALI

 
Источник

Читайте также