Skocz do zawartości

Pomocna odpowiedź

Napisano (edytowany)

Witam aktualnie razem z synem pracujemy nad lampką DIV (Led Cubes 4x4x4)
Docelowo lampka ma mieć możliwość ustalenia do 10 różnych trybów wyświetlania, dodatkowo planujemy aby tryb wyświetlania był możliwy do zaprogramowania przez terminal Bluetooth z odpowiednią komendą (np. "set 1 0x0000x0660x0660x0000\n0x9009x0000x0000x9009\n")
W tym celu przygotowałem stronę z wykorzystaniem obiektów Canvas JS, co pozwala na przygotowanie programu dla animacji (po klatkowej Cubs) i zabawę z lampką uprzyjemni.

image.thumb.png.471f6cfd6982f8b0379d43d867d01b34.png

W załączniku ‘led_prog.zip’ kod strony do pobrania dla chętnych.

Podczas projektu pojawił się problem z wyświetlaniem warstw diodów led, ze względu że diody w warstwie są podłączone do jednej masy, oczywiste jest że będzie problem z wyświetlaniem na różnych warstwach z różnym stanie Led np. W1 pali się W2 nie pali się.

Problem rozwiązany został programowo , kolejne warstwy Led są wyświetlane w odstępie 2 milisekund dzięki czemu otrzymujemy złudzenie że cała kostka Led (klatka Led) jest aktywna w tym samym czasie.

image.thumb.png.7c563217ee630a750a198c1a38eaf297.png

Dla rozjaśnienia poniżej kod programu.

void loop() {
  aktualnyCzas = millis();
  roznicaCzasu = aktualnyCzas - zapamietanyCzas;

   for (int i = 0; i < 4; i++)
   {
    DrawLayer(i);
   }

   if (roznicaCzasu >= AnimationRefresh)
   {
    zapamietanyCzas = aktualnyCzas;
     if (AnimationIndex<IndexK[AnimationSelect]) AnimationIndex++;
     else AnimationIndex=0;
   }
}
//rysujemy animacje dla odpowiedniej warstwy (i numer warstwy) 
void DrawLayer(int i)
{
unsigned int animation=ReadData(AnimationSelect,i);

digitalWrite( LAYER_1, 0 );
digitalWrite( LAYER_2, 0 );
digitalWrite( LAYER_3, 0 );
digitalWrite( LAYER_4, 0 );

byte rows =ReadRows(animation,1);

digitalWrite( LED_1, ReadLed(rows,1));
digitalWrite( LED_2, ReadLed(rows,2));
digitalWrite( LED_3, ReadLed(rows,3));
digitalWrite( LED_4, ReadLed(rows,4));

 rows =ReadRows(animation,2);

digitalWrite( LED_5, ReadLed(rows,1));
digitalWrite( LED_6, ReadLed(rows,2));
digitalWrite( LED_7, ReadLed(rows,3));
digitalWrite( LED_8, ReadLed(rows,4));

 rows =ReadRows(animation,3);

digitalWrite( LED_9, ReadLed(rows,1));
digitalWrite( LED_10, ReadLed(rows,2));
digitalWrite( LED_11, ReadLed(rows,3));
digitalWrite( LED_12, ReadLed(rows,4));

 rows =ReadRows(animation,4);

digitalWrite( LED_13, ReadLed(rows,1));
digitalWrite( LED_14, ReadLed(rows,2));
digitalWrite( LED_15, ReadLed(rows,3));
digitalWrite( LED_16, ReadLed(rows,4));

if (i==0)
  digitalWrite( LAYER_1, 255);
 else  if (i==1)
  digitalWrite( LAYER_2, 255);
 else if (i==2)
  digitalWrite( LAYER_3, 255);
  else if (i==3)
  digitalWrite( LAYER_4, 255);

  delay(2); //wymagany w celu przedłurzenia czasu wyswietlania jednej warstwy 2 milisekundy
}
//PINY na ARDUINO
#define LED_1 0
#define LED_2 1
#define LED_3 2
#define LED_4 3
#define LED_5 4
#define LED_6 5
#define LED_7 6
#define LED_8 7
#define LED_9 8
#define LED_10 9
#define LED_11 10
#define LED_12 11
#define LED_13 12
#define LED_14 13
#define LED_15 A4
#define LED_16 A5 
//piny odwierzające kolejne warstwy animacji
#define LAYER_1 A0
#define LAYER_2 A1
#define LAYER_3 A2
#define LAYER_4 A3

#include <stdlib.h>

int PROGRAMSIZE = 10;   //masymalny rozmiar animacji 
int PROGRAMMAX = 10;     //masymalna liczba możliwych programuw do zaprogramowania
//Bufor dla pamieci danych
  //jednka klataka zajmujew 2 bajty x 4 warstwy = 8 bajtów + 1 bajt index klatki = 9 bajtów
unsigned int PROGRAM[10][4][10];  //900 bajtów 
unsigned IndexK[10];

int AnimationRefresh=1000;
int AnimationSelect=0;    //index wybranego programu
int AnimationIndex=0;     //index wybranej klatki w programie

unsigned long aktualnyCzas = 0;
unsigned long zapamietanyCzas = 0;
unsigned long roznicaCzasu = 0;
 
void ClearLed()
{
  digitalWrite( LED_1, LOW );
  digitalWrite( LED_2, LOW );
  digitalWrite( LED_3, LOW );
  digitalWrite( LED_4, LOW );
  digitalWrite( LED_5, LOW );
  digitalWrite( LED_6, LOW );
  digitalWrite( LED_7, LOW );
  digitalWrite( LED_8, LOW );
  digitalWrite( LED_9, LOW );
  digitalWrite( LED_10, LOW );
  digitalWrite( LED_11, LOW );
  digitalWrite( LED_12, LOW );
  digitalWrite( LED_13, LOW );
  digitalWrite( LED_14, LOW );
  digitalWrite( LED_15, LOW );
  digitalWrite( LED_16, LOW );
  }

void setup() {
  pinMode( LED_1, OUTPUT );
  digitalWrite( LED_1, HIGH );
  pinMode( LED_2, OUTPUT );
  digitalWrite( LED_2, HIGH );
  pinMode( LED_3, OUTPUT );
  digitalWrite( LED_3, HIGH );
  pinMode( LED_4, OUTPUT );
  digitalWrite( LED_4, HIGH );
  pinMode( LED_5, OUTPUT );
  digitalWrite( LED_5, HIGH );
  pinMode( LED_6, OUTPUT );
  digitalWrite( LED_6, HIGH );
  pinMode( LED_7, OUTPUT );
  digitalWrite( LED_7, HIGH );
  pinMode( LED_8, OUTPUT );
  digitalWrite( LED_8, HIGH );
  pinMode( LED_9, OUTPUT );
  digitalWrite( LED_9, HIGH );
  pinMode( LED_10, OUTPUT );
  digitalWrite( LED_10, HIGH );
  pinMode( LED_11, OUTPUT );
  digitalWrite( LED_11, HIGH );
  pinMode( LED_12, OUTPUT );
  digitalWrite( LED_12, HIGH );
  pinMode( LED_13, OUTPUT );
  digitalWrite( LED_13, HIGH );
  pinMode( LED_14, OUTPUT );
  digitalWrite( LED_14, HIGH );
  pinMode( LED_15, OUTPUT );
  digitalWrite( LED_15, HIGH );
  pinMode( LED_16, OUTPUT );
  digitalWrite( LED_16, HIGH );

  pinMode( LAYER_1, OUTPUT );
  digitalWrite( LAYER_1, LOW );
  pinMode( LAYER_2, OUTPUT );
  digitalWrite( LAYER_2, LOW );
  pinMode( LAYER_3, OUTPUT );
  digitalWrite( LAYER_3, LOW );
  pinMode( LAYER_4, OUTPUT );
  digitalWrite( LAYER_4, LOW );

  ClearLed();

  AnimationRefresh=1000;
  AnimationIndex=0;
  AnimationSelect=0;

  for (int io=0;io<PROGRAMMAX;io++) ClearProgram(io);

  InsertAnimation(AnimationSelect,"0xFFFFx0000xFFFFx0000\n0x0000xFFFFx0000xFFFF\n");

  InsertAnimation(1,"0x0000x0660x0660x0000\n0x9009x0000x0000x9009\n");

  InsertAnimation(2,"0x8421x8421x8421x8421\n0x0C30x0C30x0C30x0C30\n0x03C0x03C0x03C0x03C0\n0x1248x1248x1248x1248\n0x2244x2244x2244x2244\n0x4422x4422x4422x4422\n");

  InsertAnimation(3,"0xFFFFxFFFFx0660x0660\n");
  AnimationSelect=1;
}

void ClearProgram(int nr)
{
  for (int i=0;i<PROGRAMSIZE;i++)
  {
    PROGRAM[nr][0][i]=0x0000;
    PROGRAM[nr][1][i]=0x0000;
    PROGRAM[nr][2][i]=0x0000;
    PROGRAM[nr][3][i]=0x0000;
  }
}

String getValue(String data, char separator, int index)
{
    int found = 0;
    int strIndex[] = { 0, -1 };
    int maxIndex = data.length() - 1;

    for (int i = 0; i <= maxIndex && found <= index; i++) {
        if (data.charAt(i) == separator || i == maxIndex) {
            found++;
            strIndex[0] = strIndex[1] + 1;
            strIndex[1] = (i == maxIndex) ? i+1 : i;
        }
    }
    return found > index ? data.substring(strIndex[0], strIndex[1]) : "";
}

unsigned int toLayer(String value)
{
  char ssid_array[5];
  value.toCharArray(ssid_array, 5);
  long decimal_answer = strtol(ssid_array, NULL, 16);
  return  decimal_answer;
}

void InsertAnimation(int nr,String value)
{
  unsigned long vauleWord=0;
  ClearProgram(nr);
  int indexFream=0;
  PROGRAM[AnimationSelect];
  for (int index=0;index<PROGRAMSIZE;index++)
  {
    int indexLayr=1;
    String objFream = getValue(value,'\n',index);
    if (objFream=="") break;
      String objLayr  = getValue(objFream,'0x',indexLayr);  //1
      vauleWord =  toLayer(objLayr);
      PROGRAM[nr][0][index] = vauleWord;
      indexLayr++;
      objLayr  = getValue(objFream,'0x',indexLayr);         //2
      vauleWord =  toLayer(objLayr);
      PROGRAM[nr][1][index] = vauleWord;
      indexLayr++;
      objLayr  = getValue(objFream,'0x',indexLayr);         //3
      vauleWord =  toLayer(objLayr);
      PROGRAM[nr][2][index] = vauleWord;
      indexLayr++;
      objLayr  = getValue(objFream,'0x',indexLayr);       //4
      vauleWord =  toLayer(objLayr);
      PROGRAM[nr][3][index] = vauleWord;
      indexFream++;
  } 

  IndexK[nr]=indexFream-1;
}

//pobieramy odpowiednia klatkę animacji
//NR    - NMERA AKTUANIE WYBRANEJ ANIMACJI
//index - INDEKS OKRESLAJACY AKTUALNY NUMER WYSWIETLANEJ KLATKI
unsigned int ReadData(int nr,int i)
{
  unsigned int aut=0x0000;
  aut = PROGRAM[nr][i][AnimationIndex];
  return aut;
}

//pobieramy dane dla odpowiedniej warstwy - wiersz led
byte ReadRows(unsigned int dane,int nrL)
{
  switch (nrL) {
  case 1:
    return (0xF000 & dane) >> 12;
  case 2:
    return (0x0F00 & dane) >> 8;
  case 3:
    return (0x00F0 & dane) >> 4;
  case 4:
    return (0x000F & dane) ;
  default:
    return 0x0;
  }
}

//pobermy dane dla odpowiedniego led w wierszu
bool ReadLed(byte dane,int nrLed)
{
  switch (nrLed) {
          case 4:
    return (0b0001 & dane)>0;
          case 3:
    return (0b0010 & dane)>0;
          case 2:
    return (0b0100 & dane)>0;
          case 1:
    return (0b1000 & dane)>0;
      default: 
    return false;
  }
}

Aktualnie zastanawiam się co wykorzystać w celu zwiększenia ilości pionów dla Arduino , Chodzi o fakt że Ledy są zasilane bezpośrednio z pionów cyfrowych Arduino (+) i zależy mi aby moc Ledów nie uległy zmianie (aby świeciły równomiernie). Proszę o sugestię…

led_prog.zip filmy.zip

Edytowano przez gohunoff
  • Lubię! 1

Mylales o rejestrze przesuwnym? 3 piny arduino obsługują wtedy 8 (i wielokrotność 8 przy zastosowaniu szeregowym większej ilości rejestrów) wyjsc

Poza tym nie wiemy jak obecnie wyglądają połączenia Twojej megalampki z samym procesorem. Jego piny są dość słabe i przy multipleksowaniu 64 LEDów jasność może być marna, a co gorsza zmieniać się wraz liczbą jednocześnie świecących punktów. No a przecież planujesz zwiększyć jeszcze liczbę diodek (rodzaj żeński: dioda, diod, diody..) co może wymagać dodania driverów (np. tranzystorowych). Ograniczeniem jest nie tylko prąd pojedynczego pinu, ale także sumaryczny prąd przepływający przez wyprowadzenia VCC/GND kostki procesora.

BTW: Łańcuchy typu:

"0x8421x8421x8421x8421\n0x0C30x0C30x0C30x0C30\n0x03C0x03C0x03C0x03C0\n0x1248x1248x1248x1248\n0x2244x2244x2244x2244\n0x4422x4422x4422x4422\n"

są przechowywane w RAMie procesorka. Nie programujesz PCta z praktycznie nieskończoną pamięcią tylko małe Arduino, które wszystkiego ma 2K a tą metodą zaraz cały dostępny RAM zapełnisz. Pomyśl o umieszczeniu tego rodzaju danych w pamięci programu.

  • Lubię! 1

Dzięki za odpowiedz ogólnie wykorzystam rejestr przesuwny wydaje się najlepszym rozwiązaniem.

marek1707 ja w programie ma zarezerwowaną tablicę unsigned int PROGRAM[10][4][10];   dzięki czemu mam możliwość do zaprogramowania 10 programów po 10 klatek animacji. Ja nie zapamiętuję stringów tylko będę konwertował obiekty  przesyłane przez terminal Bluetooth do tej tabeli - metoda InsertAnimation

Zależy mi na tym aby ta tablica była zarezerwowana zajmie ona sporo prawie 1k Ram ale będzie możliwe przeprogramowanie urządzenia z poziomu komórki i dodatkowo nie będzie wymaganie stałe połączenie z  komórką w celu wyświetlania nowej animacji.

Co do do portów wychodzących to mam dodane tranzystory na każdej z warstw (4 tranzystory BC547 - może za słabe), właściwe w celu żeby zabezpieczyć porty analogowe.

Nie wiem jednak czy muszę koniecznie dodawać tranzystory na każde wiście cyfrowe.

Dopiero zaczynam przygody e elektroniką  wiec proszę wyrozumiałość. 

53 minuty temu, gohunoff napisał:

Ja nie zapamiętuję stringów

A myślisz, że skąd one się biorą jako argumenty dla InsertAnimation()? Obecnie siedzą w RAMie a funkcja dostaje wskaźniki do nich. Chyba, że w docelowej wersji nie będziesz nigdzie przechowywał stringów "startowych" a zawsze dosyłał je przez BT. Bo obecnie, w kodzie jaki zaprezentowałeś, mimo Twoich szczerych chęci trzymasz je w RAMie. Wyjściem jest inicjalizacja tablicy PROGRAM w miejscu jej definicji. Wtedy rzeczywiście, rozbiegówka C przepisuje zawartość początkową z FLASHa do RAMu jeszcze przed uruchomieniem main() czy tam startup().

1 godzinę temu, gohunoff napisał:

Co do do portów wychodzących to mam dodane tranzystory na każdej z warstw (4 tranzystory BC547 - może za słabe), właściwe w celu żeby zabezpieczyć porty analogowe.

Tranzystory przynajmniej od strony elektrod wspólnych (czyli tam gdzie spływa sumaryczny prąd z każdej warstwy) to bardzo dobry pomysł. BC547 nie są za słabe - przynajmniej jeszcze nie w tej konstrukcji. Mogą być tylko za słabo sterowane, ale jeśli nie zauważasz przygaszania warstw w zależności od liczby jednocześnie załączonych na nich LEDów, to jest OK. "Porty analogowe" są w trybie wyjścia cyfrowego tak samo silne jak te normalnie cyfrowe, więc to nie jest kwestia "zabezpieczenia" a zwiększenia wydajności prądowej. Linie "Ax" mają dodatkowo funkcje wejść analogowych, ale to dodatek do ich normalnej działalności cyfrowej.

1 godzinę temu, gohunoff napisał:

Nie wiem jednak czy muszę koniecznie dodawać tranzystory

My też nie wiemy, bo nie wiemy co chcesz zrobić. Dopóki współczynnik multipleksowania (w tym przypadku liczba warstw) nie przekracza kilku, to wydajność prądowa pojedynczej linii portu cyfrowego wystarczy. Ale jeśli planujesz zrobienie np. 6  lub więcej warstw, to już bym się zastanowił nad rekonfiguracją całości, bo w ciągu dnia kostka może już wyglądać słabo (pomijając fakt, że zabraknie pinów procesora i i tak będziesz musiał dodać jakieś rozszerzenia). No kwestia jakości używanych LEDów. Jedne świecą dobrze przy 1mA prądu stałego a innym 20mA za mało by w ogóle je zauważyć.

  • Pomogłeś! 1

Jeśli chodzi o tranzystory to nigdy nie widziałem nigdy projektu tego typu bez nich. Uznałem, że są niezbędne lub z nimi jest dużo łatwiej. 

Rejest przesówny z którym się bawiłem to 74hc595. Jeśli potrzebujesz zrozumieć zasadę działania to służę pomocą 

Fajnie, że po polsku. Z tym kodem się spotkałem chyba na oficjalnej stronie arduino. Średnio był pomocny w nauce 😉.

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...