RGB-подсветка для макета участка города

whoim
Offline
Зарегистрирован: 03.11.2011

 Решил создать итоговую тему, в основном - чтобы складывать сюда возможную быть полезной информацию для проекта. Итак:

Обратилась одна фирма  задачей. Задача - подсвечивать участки макета длинной 10 метров, шириной 2 метра. Макет представляет некую часть славного города N. Там нарисованы кварталы, фонтаны и прочая существующая и будущая хрень. Ненавижу связываться с администрациями города и прочими госструктурами без стопроцентной предоплаты, ибо всегда начинаются траблы на моментах оплаты - бюджет успевают растащить и начинаю проситься как нищие.. Но тут прослойка в виде фирмы (серьезной и со знакомыми в ней), да и бюджет уже разворован - опасаться нечего ))))

Детали проекта: шесть "терминалов", компьютеры, мониторы с тачскрином, на которых также нарисован стенд. Терминалам присвоены цвета для упрощения процесса. Тычок пальцем в нужный проект и затем в нужную часть стенда должен подсвечивать нужную часть физического стенда в цвет терминала..

да, я тоже пока вник в суть, мозг напрягся. 

На стенд уже наклеены ленты из RGB-светодиодов типа SMD5050, порезаны минимальными кусочками. Даже там понимают что оптимальный вариант - матрица, с некой частотой развертки.

Прообраз проекта: http://arduino.ru/projects/arduino_led_matrix

В проекте нужно увеличить матрицу вдвое, получив 16 столбцов и 48 строк, и оснастить мосфетами.  Ну и написать программу: используя прерывание, высвечивать содержимое массива. Получать данные из Serial для обновления массива.

Пока рисую печатку, завтра буду травить.

whoim
Offline
Зарегистрирован: 03.11.2011

 

whoim
Offline
Зарегистрирован: 03.11.2011

 Мосфеты, варианты включения: 

whoim
Offline
Зарегистрирован: 03.11.2011

 Используя эту информацию, а также тот нюанс что у этой сборки диодов три катода и один анод, выводим что для столбцов используем P-мосфеты, а для строк - N-мосфеты.

Lavochkin
Offline
Зарегистрирован: 19.12.2011

Спасибо что выкладываете подробно. Очень интересная для меня тема.  А фотки будут появляться по мере реализации?

whoim
Offline
Зарегистрирован: 03.11.2011

 Lavochkin если новый телефон успеет прийти - то конечно)) сейчас камер в доме нет.

whoim
Offline
Зарегистрирован: 03.11.2011

 Похоже, будет двухсторонняя и прийдется осваивать фоторезист. В ламинатор не лезет, а утюгом я сомневаюсь, хотя и попробую.

whoim
Offline
Зарегистрирован: 03.11.2011

 Путем жуткого колдунства печатка удалась. Если мы со всей этой херней на борту еще и взлетим - выложу разводку для ЛЛТ.

carduino.ru
Offline
Зарегистрирован: 06.12.2011

Какой мосфет использовать будешь?  

whoim
Offline
Зарегистрирован: 03.11.2011

 да я маркировку не помню, в магазе по характеристикам подобрали для катода 12в 5А. Для анодов на тесты пару взял каких то 220в, ибо ничего низковольтного не было.

whoim
Offline
Зарегистрирован: 03.11.2011

 Будем докупать оставшиеся, если все получится - точно выясню.

whoim
Offline
Зарегистрирован: 03.11.2011

  

 

Недоработки на плате были, завтра исправлю. МК запустился, скетчи льются через внешний брелок.

whoim
Offline
Зарегистрирован: 03.11.2011

 Регистры завелись, N-мосфеты IRF430A тоже работают. P-мосфеты несколько раз снимал - ставил, видимо перегрел. Был косяк с порванной дорогой на регистр, сразу не заметил..

Косяков по плате много. На два нижних регистра не разведены ST_CP ST_CH или как там их... Также дырка под питание просверленная рубит дорожку на другой стороне платы ))

Сегодня не успеваю исправить.....

whoim
Offline
Зарегистрирован: 03.11.2011

 http://www.youtube.com/watch?v=Fkw-lwlWCx8&feature=youtu.be

P-мосфеты пришлось дополнить транзисторами со встроенными резисторами DTC144 (так называемые логические) и подтянуть затвор к стоку резистором 1кОм

Lavochkin
Offline
Зарегистрирован: 19.12.2011

Здорово. А каким колдовством делали плату? фоторезист, как планировалось?

whoim
Offline
Зарегистрирован: 03.11.2011

 Нет, ламинатор + лазерный принтер + журнальная бумага) Совпадение верхнего и нижнего слоя - с помощью такой-то матери)))
Многие дырки сверлил наискось )))

whoim
Offline
Зарегистрирован: 03.11.2011

Вот принцип доработки управления p-мосфетами

Резистор на базу транзистора не нужен если использовать "логический", сопротивление по которому разряжается мосфет нужно подбирать от 100ом до 1ком (чем ниже тем быстрее закрывается и можно повышать частоту)

с 1КоМ работает на 5кгц, в принципе разницы на глаз с 500гц нету) Мосфеты не греются

whoim
Offline
Зарегистрирован: 03.11.2011

 Ну что же, серверную часть тоже писать мне.. на делфи.  Основное уже работает.

 

whoim
Offline
Зарегистрирован: 03.11.2011

В общем, выяснил я одно правило из этого проекта. Если нужна яркость - не более 8 линий (анодов). А лучше 4.

Текущая схема при номинальном питании светодиодов подходит к сверхярким если они на информайионном табло (генерировать картинки) и если нет нужды в яркости.  Дело в отношении времени горения одного диода к их количеству (по линии анодов).  Когда оно одна шестнадцатая - диод горит мало. Конденсаторы в цепи если ставить питают другие каналы, которые не должны были гореть.

Выход (в моем случае) - двадцатьчетыре вольта на диоды. Год отработают если круглосуточно. Потом сядут. Устраивает всех.

Основные косяки:

1) на верхние ключи (аноды) нужны предварительные ключи. Транзисторы C144ESY в корпусе TO92 вписываются в перерезанные дорожки, также вписываются резисторы между gate и source мосфета. Базу транзистора на регистр, коллектор на gate мосфета, эммитеры в кучу и на минус.

При питании напряжением свыще 18в нужно между кучей эмиттеров и минусом поставить стабилизатор напряжения по минусу - 79L12, завтра я проверю эту теорему.

Когда проект уедет в марсель - нажрусь) Следом переделаю нормально печатку и выложу. Исходники управляющей программы на Delphi урежу (уберу специализации заказчика) и тоже выкину. Могу дать кусками кода сейчас если кому очень надо. В программе тычок на ячейку инвертирует ее а кнопка посылает данные в порт побитно, что экономит размер "кадра" в 8 раз.

Lozhkin
Offline
Зарегистрирован: 10.04.2011

 Здорово. Хочу полюбопытствовать, а как можно прикинуть сколько диоды отработают?
По остальным частям вопросов нет, ибо еще не все понял, попробую еще раз ветку перечитать, ибо учусь. 
Спасибо. Очень интересно и хорошо расписано.

whoim
Offline
Зарегистрирован: 03.11.2011

 По одному из отзывов при 1/8 скважности и вольтаже 24 вместо 12 хорошие диоды отработали 2 года в режиме 16 часов/сутки. При скважности 1/16 время службы должно быть большим, при условии той же частоты вызова. В общем, тут важно количество импульсов на один диод (завышенного вольтажа следовательно тока) и их длительность. В даташитах на светодиоды есть максимально допустимый кратковременный ток. Желательно за него не выходить даже при меньших чем в даташите длительностях этого импульса.

Как правило в нормальном режиме ток 20ма. В коротком импульсе позволительно 40ма, вот откуда 24в. Там конечно нужно пересчитывать по схеме ленты - там резисторы и диоды, и по три последовательно, но все же думаю нормально будет.

По поводу не все понял - главное сам принцип динамической индикации. У меня логика такая:

1) есть 16 "плюсов"  - аноды, это строки
2) есть 48 "минусов" - это катоды, столбцы
3) есть две переменные, x=строки, y=столбцы.
4) по прерыванию вызывается функция:

  тушит все
  зажигает строку X
  определяет по условиям, какие по Y надо зажечь столбцы и зажигает их
  увеличим на 1 Y
  если Y максимален, сбросим его в 0 и увеличим на 1 Х
  если Х максимален, его в начальное значение

В реале это немного сложнее, ну собсно вот

/***************************
* LED RGB controller       *
* atmega328 & 8pcs 74hc595 *
* by whoim@mail.ru         *
****************************/
#include <FlexiTimer2.h>

//74hc595 wiring
//ST_CP на 74HC595
#define _latch 8
#define _latchPORTB _latch - 8
//SH_CP на 74HC595
#define _clock 12
#define _clockPORTB _clock - 8
//DS на 74HC595
#define _data 11
#define _dataPORTB _data - 8
//информационный светодиод
#define _boot_led 13
//настройки матрицы
#define _FQ 1.8 //частота pwm_led 1, 1.0/1000000
#define _ROWS 16 //число строк в матрице (аноды)
#define _COLUMNS 48 //число столбцов в матрице (катоды)
boolean data[_ROWS][_COLUMNS]; //массив состояния светодиодов для вывода
int j=0; //храним строки между циклами
//настройки связи
#define _serial_speed 115200 //скорость компорта

//тестовый режим
#define _test_button A2 //кнопка test замыкается на LOW
#define testmode_speed 1000 //скорость переключения элементов в тестовом режиме (мс)
long testmode_prev_time; //переменная для хранения времени предыдущего цикла тестового режима
boolean testmode = false; //признак тестового режима
int test_x = 0; //переменные для связи
int test_y = 0; //массива данных и тестового режима
int test_x_prev = 0; //переменные для связи
int test_y_prev = 0; //массива данных и тестового режима

//настройки при запуске
void setup() { 
  //настройка таймера
  FlexiTimer2::set(_FQ, print); // 500ms period
  FlexiTimer2::start();
  //установка портов 74hc595
  pinMode(_latch, OUTPUT);
  digitalWrite(_latch, LOW);
  pinMode(_clock, OUTPUT);
  digitalWrite(_clock, LOW);
  pinMode(_data, OUTPUT);
  digitalWrite(_data, LOW);
  //установка портов кнопки TEST
  pinMode(_test_button, INPUT);
  digitalWrite(_test_button, HIGH);
  //установка портов светодиода
  pinMode(_boot_led, OUTPUT);
  digitalWrite(_boot_led, LOW);
  //инициализация Serial
  Serial.begin(_serial_speed);
  //сигнал к старту
  for (int j=1; j <=5; j++) {
     digitalWrite(_boot_led, HIGH);
     delay(80);
     digitalWrite(_boot_led, LOW); 
     delay(80);
  }
  digitalWrite(_boot_led, HIGH);
}

//цикл главной программы
void loop() {
  //по кнопке test
  /*
  if (digitalRead(_test_button) == LOW) { //если нажата кнопка
    digitalWrite(_boot_led, LOW);
    while (digitalRead(_test_button == LOW)) {//пока она нажата
      delay(10); //ничего не делаем
    }

  //как кнопка отпущена
  testmode = !testmode; //переключаем тестовый режим
  digitalWrite(_boot_led, testmode); //переключим светодиод на плате
  }

  
  //если тестовый режим и пришло время
  if (testmode) {
   if (millis()-testmode_prev_time >= testmode_speed) {
     data[test_x_prev][test_y_prev] = false; //выключаем предыдущий элемент
     test_x_prev = test_x; //запоминаем предыдущий x
     test_y_prev = test_y; //запоминаем предыдущий y
     data[test_x][test_y] = true; //включает текущий
     test_y++;
     if (test_y == _COLUMNS) {
       test_y = 0;
       test_x++;
       if(test_x == _ROWS) test_x = 0;   
     }
   }
  }
  */
} //loop

//функция, вызываемая по таймеру
void print() {
    //отрисовка матрицы
  //цикл по строкам (аноды)
    byte registers[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; //обнуляем регистры
    //задаем нужный анод
    if(j < 8) bitSet(registers[0], j);
    else bitSet(registers[1], j-8);
    for (int k=0; k < _COLUMNS; k++)
    //цикл по столбцам (катоды)
        if (data[j][k]) {
          //задаем катоды  
          if (k < 8) bitSet(registers[2], k);
          else if (k>=8 &&  k<16) bitSet(registers[3], k-8);
          else if (k>=16 && k<24) bitSet(registers[4], k-16);
          else if (k>=24 && k<32) bitSet(registers[5], k-24);
          else if (k>=32 && k<40) bitSet(registers[6], k-32);
          else if (k>=40 && k<48) bitSet(registers[7], k-40);
        }
        //shiftOut
        bitClear(PORTB,_latchPORTB);
        shiftOut2(_data,_clock,registers[7]);
        shiftOut2(_data,_clock,registers[6]);
        shiftOut2(_data,_clock,registers[5]);
        shiftOut2(_data,_clock,registers[4]);
        shiftOut2(_data,_clock,registers[3]);
        shiftOut2(_data,_clock,registers[2]);
        shiftOut2(_data,_clock,registers[1]);
        shiftOut2(_data,_clock,registers[0]);
        bitSet(PORTB,_latchPORTB);
        //корректировка x,y
        j++;
        if (j == _ROWS){
           j= 0; 
        }
}

void shiftOut2(int my_data, int my_clock, byte myDataOut) {
  bitClear(PORTB,_dataPORTB);
  for (int i=7; i>=0; i--)  {
    bitClear(PORTB,_clockPORTB);
    if ( bitRead(myDataOut,i) == 1)
      bitSet(PORTB,_dataPORTB);
    else
      bitClear(PORTB,_dataPORTB);
    bitSet(PORTB,_clockPORTB);
    bitClear(PORTB,_clockPORTB);
  }
  bitClear(PORTB,_clockPORTB);
}

//событие: данные в порт Serial
void serialEvent() {
  //if(testmode) testmode = false; //сбросим тестовый режим если есть данные
  int x = 0; //переменные для
  int y = 0; //сопоставления с массивом данных
  while (Serial.available()) {
    byte _in = (byte)Serial.read();
    for (int j=0; j<8; j++) {
      data[x][y]= bitRead(_in, j); //формируем массив данных
      y++;
      if(y == _COLUMNS) {
        y=0;
        x++;
        if (x == _ROWS) {
          Serial.flush();
          Serial.println("overflow on ROWS (many bytes)");
        }
      } //if max columns
    } //bits cycle
  delay(1);
  } //while serial active
  Serial.println("done");
}

Ну и попутно вопросы

1) Как правильно рассчитать частоту вызова таймена? Мне надо чтобы я мог получить значения для X(герц)*16 (в 16 раз быстрее, ибо мне надо за Х вызвать процедуру 16 раз). Библиотека FlexiRimer2, нихрена не понял из описания
2) Вы видите закоменченные строчки для нажатия кнопки. Если их расскоментить (тупо отследить событие и все) и нажать кнопку - контроллер умирает на 10 секунд. Почему?
3) Постоянно срабатывает условие в обработке принятого по serial которое если x = _ROWS, хотя шлю точно то количество байт, которое ожидает контроллер. Все работает но непонятно, может компонент из дельфи отсылает чонить вконец (типа \n). Попозже перепишу его для команд других, там и посмотрим. Смаллуарт ниасилил, ибо не нашел в нем события ОнСериал. А если использовать то что используется счас а в нем юзать смаллуарт - получится масло маслянное, две библиотеки используются, смысла нет.. 
 

whoim
Offline
Зарегистрирован: 03.11.2011

 Вопрос: можно как то просто узнать число пересланных в порт байт у события? без своего счетчика?

whoim
Offline
Зарегистрирован: 03.11.2011
Adessit
Adessit аватар
Offline
Зарегистрирован: 12.04.2011

Может лучше было бы сделать модульно всё? Так проще было бы обслуживать, увеличить - уменьшить количество портов? Да и отвод тепла можно сделать лучше.

Да и печатки намного легче делать и клонировать...

Еще один момент по Н-мосфетам, везде советуют ставить 2 резистора - 1 на затвор к выводу МК, 2й от затвора к земле, чтоб его подтягивать к земле... 

step962
Offline
Зарегистрирован: 23.05.2011

Adessit пишет:

Еще один момент по Н-мосфетам, везде советуют ставить 2 резистора - 1 на затвор к выводу МК, 2й от затвора к земле, чтоб его подтягивать к земле... 

Другими словами - два резистора, последовательно включенные между плюсом (сигнальной линией) и массой. А затвор транзистора подключен в точку между двумя транзисторами. Классический делитель напряжения. Тут, наверное, в даташит конкретного транзистора смотреть надо - требуется ли для него напряжение на затворе, меньшее 5 В? 

whoim
Offline
Зарегистрирован: 03.11.2011

 Ну.. работает - не трожь ))) Это я насчет N-мосфетов. А вообще мысль здравая, но если нужно контролировать напряжение. Опять же делитель уменьшит его (напряжение), что приведет к неполному открытию мосфета. 

 

На плате предусмотрен выход на регистры "дальше", если что вдруг. Для себя на будущее разработаю именно модульную систему. 

 

Adessit
Adessit аватар
Offline
Зарегистрирован: 12.04.2011

 Да но если подключать затвор мосфета напрямую к ноге МК которая в Z-состоянии то как ему(мосфету) себя вести? Можно обойтись и простой подтяжкой к земле.

Вообще молодцом)

Как подбирали мосфеты? По каким параметрам? А то сейчас сам задаюсь этим вопросом, и не могу дельного материала найти в интернете

whoim
Offline
Зарегистрирован: 03.11.2011

 Да, но ноги МК в правильном состоянии )) Ну и через резисторы 68ом ноги подключены. Думаю, 10ком на землю не лишним было бы но и не особо нужно как практика показала.

Да как продавцу сказал) 5в открытие, от 30в протекающий ток, от 4А. Вот он и дал)

N-FR430A, P-FR9120

whoim
Offline
Зарегистрирован: 03.11.2011

 Выкладываю доработанную печатку. В верхнем ряду мосфетов в правой части нужно или устанавливать с144 и 1ком или рядом бросать 33ом и ставить N-мосфет (если нужно 8 строк а не 16)

Гасящие резисторы на 74HC595 - 33 ома

http://narod.ru/disk/40023939001/pcb.zip.html

whoim
Offline
Зарегистрирован: 03.11.2011

 

Zaliv
Offline
Зарегистрирован: 05.03.2011

 Это мега-круто!!! Мои поздравления с успешным окончанием проекта. Спасибо, что так подробно, это очень интересно

whoim
Offline
Зарегистрирован: 03.11.2011

 пока не конец, есть проблема

mitos
Offline
Зарегистрирован: 11.12.2011

За подробный рассказ да еще и с кодом большое спасибо .

 

whoim
Offline
Зарегистрирован: 03.11.2011

 в коде будут дополнения. Вкратце - перед серией шифтаутов делаем их же, но последние два - аноды - отсылаем B00000000, суть - включить катоды без анодов и тут же включить все. От засветки предыдущих разрядов.

whoim
Offline
Зарегистрирован: 03.11.2011

 

/***************************
* LED RGB controller       *
* atmega328 & 8pcs 74hc595 *
* by whoim@mail.ru         *
****************************/
#include <FlexiTimer2.h>

//74hc595 wiring
//ST_CP на 74HC595
#define _latch 8
#define _latchPORTB _latch - 8
//SH_CP на 74HC595
#define _clock 12
#define _clockPORTB _clock - 8
//DS на 74HC595
#define _data 11
#define _dataPORTB _data - 8
//информационный светодиод
#define _boot_led 13
//настройки матрицы
#define _FQ 1 //частота pwm_led 1, 1.0/1000000
#define _ROWS 16 //число строк в матрице (аноды)
#define _COLUMNS 48 //число столбцов в матрице (катоды)
boolean data[_ROWS][_COLUMNS]; //массив состояния светодиодов для вывода
int j=0; //храним строки между циклами
//настройки связи
#define _serial_speed 115200 //скорость компорта

//настройки при запуске
void setup() { 
  //настройка таймера
  FlexiTimer2::set(_FQ, print); // 500ms period
  FlexiTimer2::start();
  //установка портов 74hc595
  pinMode(_latch, OUTPUT);
  digitalWrite(_latch, LOW);
  pinMode(_clock, OUTPUT);
  digitalWrite(_clock, LOW);
  pinMode(_data, OUTPUT);
  digitalWrite(_data, LOW);
  //установка портов светодиода
  pinMode(_boot_led, OUTPUT);
  digitalWrite(_boot_led, LOW);
  //инициализация Serial
  Serial.begin(_serial_speed);
  //сигнал к старту
  for (int j=1; j <=5; j++) {
     digitalWrite(_boot_led, HIGH);
     delay(80);
     digitalWrite(_boot_led, LOW); 
     delay(80);
  }
  digitalWrite(_boot_led, HIGH);
}

//цикл главной программы
void loop() {

} //loop

//функция, вызываемая по таймеру
void print() {
    //отрисовка матрицы
  //цикл по строкам (аноды)
    byte registers[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; //обнуляем регистры
    //задаем нужный анод
    if(j < 8) bitSet(registers[0], j);
    else bitSet(registers[1], j-8);
    for (int k=0; k < _COLUMNS; k++)
    //цикл по столбцам (катоды)
        if (data[j][k]) {
          //задаем катоды  
          if (k < 8) bitSet(registers[2], k);
          else if (k>=8 &&  k<16) bitSet(registers[3], k-8);
          else if (k>=16 && k<24) bitSet(registers[4], k-16);
          else if (k>=24 && k<32) bitSet(registers[5], k-24);
          else if (k>=32 && k<40) bitSet(registers[6], k-32);
          else if (k>=40 && k<48) bitSet(registers[7], k-40);
        }
        //shiftOut
        bitClear(PORTB,_latchPORTB);
        shiftOut2(_data,_clock,registers[7]);
        shiftOut2(_data,_clock,registers[6]);
        shiftOut2(_data,_clock,registers[5]);
        shiftOut2(_data,_clock,registers[4]);
        shiftOut2(_data,_clock,registers[3]);
        shiftOut2(_data,_clock,registers[2]);
        shiftOut2(_data,_clock,B00000000);
        shiftOut2(_data,_clock,B00000000);
        bitSet(PORTB,_latchPORTB);
        //shiftOut
        bitClear(PORTB,_latchPORTB);
        shiftOut2(_data,_clock,registers[7]);
        shiftOut2(_data,_clock,registers[6]);
        shiftOut2(_data,_clock,registers[5]);
        shiftOut2(_data,_clock,registers[4]);
        shiftOut2(_data,_clock,registers[3]);
        shiftOut2(_data,_clock,registers[2]);
        shiftOut2(_data,_clock,registers[0]);
        shiftOut2(_data,_clock,registers[1]);
        bitSet(PORTB,_latchPORTB);
        //корректировка x,y
        j++;
        if (j == _ROWS){
           j= 0; 
        }
}

void shiftOut2(int my_data, int my_clock, byte myDataOut) {
  bitClear(PORTB,_dataPORTB);
  for (int i=7; i>=0; i--)  {
    bitClear(PORTB,_clockPORTB);
    if ( bitRead(myDataOut,i) == 1)
      bitSet(PORTB,_dataPORTB);
    else
      bitClear(PORTB,_dataPORTB);
    bitSet(PORTB,_clockPORTB);
    bitClear(PORTB,_clockPORTB);
  }
  bitClear(PORTB,_clockPORTB);
}

//событие: данные в порт Serial
void serialEvent() {
  //if(testmode) testmode = false; //сбросим тестовый режим если есть данные
  int x = 0; //переменные для
  int y = 0; //сопоставления с массивом данных
  while (Serial.available()) {
    byte _in = (byte)Serial.read();
    for (int j=0; j<8; j++) {
      data[x][y]= bitRead(_in, j); //формируем массив данных
      y++;
      if(y == _COLUMNS) {
        y=0;
        x++;
        if (x == _ROWS) {
          Serial.flush();
        }
      } //if max columns
    } //bits cycle
  delay(1);
  } //while serial active
  Serial.println("done");
}

 

whoim
Offline
Зарегистрирован: 03.11.2011

 delphi-7 проект

http://narod.ru/disk/41560386001.fb08d21ae89630e11353906d0cb384fc/led_co...

допкомпоненты - BComPort и из папки дельфей Server/Client Socket, гугл знает детали