8x8x8-ledikuutio

Moi

Tuli tuossa keväällä rakennettua tuommoinen 512 ledin kuutio http://youtu.be/R_aXyEJWcZA. Ideahan on vanha ja itsekin rakensin kuution toisen tekemän projektin perusteella http://www.instructables.com/id/Led-Cube-8x8x8/ mutta ei ole suomalaisten tekemää projektia sattunut eteen niin ajattelin kirjoittaa tänne omasta.

Toiminta periaate ja käytetyt piirit:
Eli kyseessä on 8 kerroksinen ledi kuutio, jokaisessa kerroksessa 64 lediä. Ohjaus tapahtuu niin, että yhden kerroksen ledejä poltetaan vain kerrallaan hetken, jonka jälkeen kerros sammutetaan ja seuraavan kerroksen ledit valitaan ja valittu kerros sytytetään. Tämä sytytys ja sammutus tapahtuu niin nopeasti, ettei ledien sammumista ehdi huomata.

Jokaisen kerroksen 64:ää lediä ohjataan 8:lla 74HC164N shift registerillä http://www.alldatasheet.com/datasheet-pdf/pdf/101613/PHILIPS/74HC164N.html ja kerroksia sytytetään darlington transistori paketilla ULN2803A http://www.alldatasheet.com/datasheet-pdf/pdf/25571/STMICROELECTRONICS/ULN2803A.html, 74HC238N 3-to-8 line decoder/demultiplexerillä http://www.alldatasheet.com/datasheet-pdf/pdf/15570/PHILIPS/74HC238N.html. Ohjaukseen käytän Arduino Duemilanovea, josta projekti vaatii 8+1 pinniä ledien valintaan ja 3+1 kerrosten sytytyksiin, eli 13 lähtöä yhteensä.

Palavien ledien valinta:
Jokaisen (64:n) pystyrivin ledien anodit ovat juotettu yhteen, mutta kuten seuraavasta kappaleesta käy ilmi, vain valitun kerroksen ledien katodit kulkevat maihin, ja näin vain yksi koko pystyrivin ledeistä palaa. Ledien sytytys tapahtuu shift registereillä, joihin on kytketty kuhunkin 8 vierekkäistä pystyriviä. Tällöin yhdellä mikronkontrollerin pinnillä pystytään syöttämään 8:lle eri pystyriville on/off tila yhden shift regin kautta; kaikille shift regeille on myös yhteinen clock-pinni, joka kertoo milloin uusi tilatieto (0/1) on luettavissa. Käytännössä siis mikrokontrolleri laittaa 8 eri lähtöä(shift regien data-in pinneihin kytketyt) 0/1 tilaan, jonka jälkeen yhteinen clock-pinni käy 0->1, jolloin ledien tilatiedot siirtyvät kaikille 8:lle shift regille. Kun tämä sekvenssi on toteutettu 8:n kertaa, on yhden kerroksen 64:n ledin olotilat kirjoitettu. Jokaisen shift regin lähdön eteen on laitettu etuvastus ledeille (120ohm, olisi saanut olla 100!) joka rajoittaa virtaa.

Kerroksen sytytys:
Jokaisen kerroksen ledien katodit ovat yhteydessä toisiinsa, ja kun jokin kerros halutaan sytyttää, viedään yhteinen katodi maihin transistorin kautta. Koska kerroksia on 8:n, niin niitä pystyi ohjaamaan 3:lla mikrokontrollerin pinnillä 3-to-8 piirin avulla, ja koska piirissä oli vielä enable mahdollisuus, sai kerrosten sammutuksen totetutettua helposti.

Projektin eteneminen:

Olen jo jonkin aikaa pällistellyt ja ihaillut noita ledi kuutioita. Ensimmäinen kuutio jonka rakensin, oli todella surkean näköinen 3x3x3 RGB ledi kuutio. Sain sen kuitenkin toimimaan joten kuten ja kun lopulta törmäsin tuohon projektin esikuvaan, päätin kokeilla rakentaa isomman yksivärisen.

Ensimmäisenä tilasin ledit e-baystä… Halusin 3mm sinisiä haalealla kuvulla olevia ledejä 26mm katodilla… päädyin tilaamaan joltain kiinalaiselta 1000kpl 16dollarin hintaan ja sainkin lähes mitä halusin… muuten hienoja ledejä, mutta vain 16mm jalalla! tämä tarkoitti siis sitä että kuutiostani tulisi n. 130mm korkea/leveä eikä 200mm kuten olin alunperin halunnut.

Ledien tilauksen jälkeen kävin sähköisen ohjauksen läpi ja tilasin osat http://www.vekoy.com/.

Kun ledit saapuivat n. kuukauden jälkee tilauksesta, alkoi projekti edetä mukavalla vauhdilla. Ensimmäisenä rupesin testaamaan ledejä, koska en halunnut ottaa sitä riskiä, että keskelle kuutiota päätyisi rikkinäisiä ledejä. Testasin ledit 8:n ledin ryhmissä ihan vaan ison vastuksen läpi patterista jännitettä syöttämällä. Taisi yksi rikkinäinen ledi löytyä tässä testissä.

Kun ledit oli testattu alkoi toinen hieman puuduttava työvaihe: jokaisen ledin katodi jalan kääntö 90 asteen kulmaan. Tein myös jokaisen ledin anodiin pienen taivutuksen helpoittaakseni pystyrivien yhdistystä, mutta tämä koitui huonoksi ideaksi loppupeleissä.

512 lediä myöhemmin, sormet kipeinä, aloin mittaamaan ja merkkaamaan puulevylle 15mm välein ruudukkoa johon porasin 3,2mm reijät. Tästä tulisi siis sapluuna johon asettelin yhden kerroksen 64 lediä, ja kolvasin katodit yhteen. Tämä operaatio tapahtui 8 kertaa, ja aina kun sain yhden kerroksen valmiiksi, testasin yleis mittarin diodi testerillä että jokainen ledi vielä syttyisi. Kuten kuvista huomaa, kaikki ledit, paitsi ns ylärivin ledit ovat saman suuntaisesti, jolloin katodit jäivät mukavasti makaamaan toistensa päälle (16mm katodi, 15mm välit). Väliin piti lisätä muutama jäykkä langan pätkä pitämään taso vakaana.

Kun kaikki 8:n 64:n ledin tasoa oli valmiina, alkoi haastava osuus, kerrosten yhdistäminen. Valitsin mielestäni parhaimman tason ja asettelin sen sapluunaan, tästä tulisi siis ylin kerros. Kerrosten väliin piti löytää jotain 15mm levyistä ja käsiini sattui taiteltava mitta.


Se oli juuri 15mm leveä ja sopivasti taiteltuna, se meni kolmeen eri väliin ja antoi tarpeeksi tukea jottei lisättävä kerros liikkunut. Tinaus alkoi aina kulmasta ja eteni siitä vastakkaiseen kulmaan sitä mukaan mitä ledit taipuivat. Hieman väkivaltaa piti näyttää ajoittain jotta kaikki ledit tulivat täydellisesti päällekkäin ja riviin + sopivalle korkeudelle. Aina kun uusi kerros loputla tuli valmiiksi, tapahtui taas jokaisen ledin testaus. Yhdistin tinalla jokaisen kerroksen yhteen ja yleismittarilla testasin että jokainen ledi toimii. Lopulta kaikki 8 kerrosta oli yhdessä ja kaikki 512 lediä kytkettynä toisiinsa, ja kaikki vielä toimi ja näytti hyvältä.




Kun kuutio lopulta oli kasassa, rakensin pleksistä sille alustan johon porasin 15mm välein 64kpl 1mm(vai 1,5mm) reikiä yhteisille anodeille ja vielä 4,5mm reijät kulmiin, jotta saisin “jalat” telineelle.

Kun teline oli valmis, lisäsin 8:n erillistä pystytankoa kuution sisään, yksi kullekin kerrokselle. Näin sain jokaisen kerroksen yhteiset katodit alas anodi pinnien viereen, josta tulisin viemään ne ohjauslevylle.

Siinä kuutiota kasaillessa välillä lepuutin sormiani ja hermojani rakentamalla ohjaus piirilevyä. En jaksanut käydä piirtämään ja tekemään koko piirilevyä alusta loppuun, vaan käytin tuollaista täplälevyä ja kasasin siihen järjestelmän. Jostain helvetin kumman syystä päädyin käyttämään noita valkoisia liittimiä jotta saisin kuution ledit irti ohjauslevystä… en nyt tiedä mikä se hieno idea siinä oli takana mutta lopulta liittimet osoittautuivat huonoksi ideaksi ja joskus varmasti tulen ottamaan ne irti. Mutta niin, homma lähti layoutin kehittelyllä, eli mihin tulee piirikannat ja vastukset ja ne hemmetin liittimet. Kun tämä oli selvä, tinasin piirikannat, jonka jälkeen vastukset ja tämän jälkeen liittimet. Lopuksi yhdistin plussat, miinukset ja clock-pinnit.


Kun ohjauslevyn shift reggien komponentit olivat paikoillaan, aloin kehittelemään linkkiä ledien ja ohjauksen välille. Päädyin käyttämään töissä käyttämäämme verkkokaapelia, koska siinä on 8 eriväristä ohutta johdinta, ja koska kaapeli on kiinteäksi tarkoitettu, olivat johtimet yksisäikeisiä eli kohtuu jäykkiä. Johdotin liittimet, jonka jälkeen päätin johtojen pituuden ja liitin johdot kuution yhteisiin anodeihin. Tämän jälkeen kytkin myös 8 johtoa yhteisiin kerrosten katodeihin.

Tässä vaiheessa rupesi polttelemaan jo niin paljon, että oli pakko testata. En myöskään ollut (tai ole vieläkään!) tilannut erillistä arduinoa kuutiota varten, niin en viitsinyt kerrosten ohjausta lisätä ohjaus levyyn tässä vaiheessa, vaan käytin koekytkentä levyä kerrosten ohjauseen.

Kytkin siis ohjaus levystä 8:n shiftregin data-in pinnit ja yhteisen clock pinnin, koekytkentälevystä 3 kerroksen valintaan liittyvää pinniä ja enablen arduinoon, ja +5V ja GND:n arduinosta koekytkentä levyyt ja ohjaus levyyn. Näin koko järjestelmä oli kasassa ja testikoodin pystyi syöttämään sisään.



Koodin pyörittelyn jälkeen sain lopulta kuutioon eloa ja liittimien kosketusongelmien poistamisen jälkeen, kaikki 512 lediä loistivat kirkkaina.

Sitten homma olikin pitkälti tehosteiden ohjelmointia ja kehittelyä… Koodi toimii käytännössä niin, että kontrolleri interrupin avulla hoitaa ledien sytytykset ja sammutukset, kun taas pääohjelma valitsee mitkä ledit tulee olla seuraavaksi päällä ja mitkä ei. Jos joku innostuu rakentamaan vastaavan härvelin, voin pistää tarkemaa tietoa ohjelmasta.

Lopuksi kun maltoin testailuilta, yhdistin kerrosten ohjausen ohjauslevyyn ja yhdistin ohjauslevyn ja kuution yhdeksi nätiksi paketiksi toisen pleksilevyn avulla.


Nyt kuutio on muuten lähes valmis, ainoastaan pitäisi vielä se mikrokontrolleri hommata ja kiinnittää kiinteäksi osaksi kuutiota jolloin sen voi laittaa vaikka hyllylle patterin voimin välkkymään.

Kuutiosta tuli hyvän näköinen vaikka se onkin suht pieni lyhyiden ledien jalkojen takia ja efektit ovat todella näyttäviä kun on hieman hämärä valaistus.

Tässä on nyt mitä tällä istumalla jaksoin kirjoittaa… jos tulee jotain mieleen, kysykää vaan niin vastaan… ja pyrin myös lisäilemään tänne sitä mukaan mitä itselläni puskee mieleen.

Tässä vielä koodi.

[code]#include <avr/interrupt.h>

byte aakkoset[29][8] = {{0,0,0,0,0,0,0,0},
{0,24,36,66,126,66,66,66},
{0,56,36,36,56,36,36,56},
{0,28,34,64,64,64,34,28},
{0,120,68,68,68,68,68,120},
{0,124,64,64,120,64,64,124},
{0,124,64,64,120,64,64,64},
{0,60,66,64,78,66,66,60},
{0,68,68,68,124,68,68,68},
{0,16,16,16,16,16,16,16},
{0,8,8,8,8,72,72,48},
{0,68,72,80,96,80,72,68},
{0,64,64,64,64,64,64,124},
{0,65,99,85,73,65,65,65},
{0,66,98,114,90,78,70,66},
{60,66,66,66,66,66,66,60},
{0,56,36,36,56,32,32,32},
{56,68,130,130,130,154,68,58},
{0,56,36,36,56,36,36,36},
{56,68,64,56,4,4,68,56},
{0,127,8,8,8,8,8,8},
{0,66,66,66,66,66,66,60},
{0,65,65,34,34,20,20,8},
{0,130,130,146,146,170,68,0},
{129,66,36,24,24,36,66,129},
{130,130,68,40,16,16,16,16},
{0,126,4,8,16,32,64,126},
{102,24,36,66,126,66,66,66},
{102,24,36,66,66,66,36,24}};

int rivi[8] = {0,1,2,3,4,5,6,7}; //Portti D

int clock=13;

int enable = 17;

int k1=14;
int k2=15;
int k3=16;

volatile byte kuva[8][8]; // Z-kerros , Y-sivu
volatile int kerros=0;
volatile boolean tyhjennetaan=false;

void setup(){

TCCR2A = 0x00;
TCCR2B = 0x00;

//Interruppi asetukset
TCCR2A |= (0x01 << WGM21);
OCR2A = 40;
TCNT2 = 0x00;
TCCR2B |= (0x01 << CS22) | (0x01 << CS21);

TIMSK2 |= (0x01 << OCIE2A);

for(int i=0; i<8; i++){
pinMode(rivi[i],OUTPUT);
}
pinMode(clock,OUTPUT);
pinMode(enable,OUTPUT);
pinMode(k1,OUTPUT);
pinMode(k2,OUTPUT);
pinMode(k3,OUTPUT);
}

//Interruppi, Sytyttää aina yhden kerroksen kerrallaan ja palaa pääohjelmaan
ISR (TIMER2_COMPA_vect){
if(tyhjennetaan){
for(int a=0; a<8; a++){
for(int b=0; b<8; b++){
kuva[a][b]=0;
}}
tyhjennetaan=false;

}
else{
digitalWrite(enable,LOW); //Ledit offfffff

//Kerroksen lisäys ja asetus
kerros++;
if(kerros>=8){kerros=0;}
SetKerros(kerros);

// Kerroksen ledien asetus
for(int i=0; i<8; i++){
digitalWrite(clock,LOW);
PORTD = kuva[kerros][i];
digitalWrite(clock,HIGH);
}

//Ledien sytytys
digitalWrite(enable,HIGH);
}
}

//----------------------------LOOOOOOOOP------------------------------

void loop(){
splash(3);

kirjain_lento(‘R’,50);
kirjain_lento(‘U’,50);
kirjain_lento(‘U’,50);
kirjain_lento(‘V’,50);
kirjain_lento(‘I’,50);
kirjain_lento(‘P’,50);
kirjain_lento(‘E’,50);
kirjain_lento(‘N’,50);
kirjain_lento(‘K’,50);
kirjain_lento(‘K’,50);
kirjain_lento(‘I’,50);

aalto(15);
merenaalto(15);
antiaalto(15);

sineCubeP(15,50);
sineCubeV(15,50);

effect_random_filler(10,1);
delay(500);
effect_random_filler(10,0);

effect_rain(200);
tyhjenna();

for(int j=0; j<5;j++){
for(int i=0; i<4; i++){
tyhjenna();
box_wireframe(i,i,i,7-i,7-i,7-i);
delay(j50);
}
for(int i=3; i>0; i–){
tyhjenna();
box_wireframe(i,i,i,7-i,7-i,7-i);
delay(j
50);
}
}
tyhjenna();
box_wireframe(0,0,0,7,7,7);
delay(2000);
tyhjenna();
}
//-------------------------------------------------------------------

void sineCube(){
float phase=0;
int size=8;
float Z=0;
unsigned long tikit;
for(int g=0; g<14; g++){
for(phase=0; phase<2*PI; phase=phase+0.09){
tyhjenna();
for(int x = 0; x < size; x++){
for(int y = 0; y < size; y++){
Z = sin(phase + sqrt(pow(skaalaus(x,0,7,-PI,PI),2) + pow(skaalaus(y,0,7,-PI,PI),2)));
Z = round(skaalaus(Z,-1,1,0,size-1));
int z = int(Z);
setLed(x,y,z);
}
}
tikit=millis();
while(tikit+5>millis()){
;
}
}
}
}

//-----------------------------------------------------------------
//Pysty liike
void sineCubeP(int kierrokset, int tauko){
float phase=0;
float Z=0;

for(int g=0; g<kierrokset; g++){
for(phase=0; phase<2*PI; phase=phase+0.39){
tyhjenna();

for(int x = 0; x < 4; x++){
for(int y = 0; y < 4; y++){
Z = sin(phase + sqrt(pow(skaalaus(x,0,7,-PI,PI),2) + pow(skaalaus(y,0,7,-PI,PI),2)));
Z = round(skaalaus(Z,-1,1,0,7));
int z = int(Z);
setLed(x,y,z);
setLed(x,7-y,z);
setLed(7-x,y,z);
setLed(7-x,7-y,z);
}
}
delay(tauko);
}
}
}

//-----------------------------------------------------------------

//vaaka liike
void sineCubeV(int kierrokset, int tauko){
float phase=0;
float Y=0;

for(int g=0; g<kierrokset; g++){
for(phase=0; phase<2*PI; phase=phase+0.39){
tyhjenna();

for(int x = 0; x < 4; x++){
for(int z = 0; z < 4; z++){
Y = sin(phase + sqrt(pow(skaalaus(x,0,7,-PI,PI),2) + pow(skaalaus(z,0,7,-PI,PI),2)));
Y = round(skaalaus(Y,-1,1,0,7));
int y = int(Y);
setLed(x,y,z);
setLed(x,y,7-z);
setLed(7-x,y,z);
setLed(7-x,y, 7-z);
}
}
delay(tauko);
}
}
}

//-----------------------------------------------------------------

//aaltoilu
void aalto(int kierrokset){
for(int g=0; g<kierrokset; g++){

for(float kierros=0; kierros< 2*PI; kierros=kierros+0.39){
for(int x=0; x<8; x++){
int z = int(round(skaalaus(sin(kierros + skaalaus(x,0,7,0,PI)),-1,1,0,7) ));
setLed(x,0,z);
setLed(x,1,z);
setLed(x,2,z);
setLed(x,3,z);
setLed(x,4,z);
setLed(x,5,z);
setLed(x,6,z);
setLed(x,7,z);
}
delay(50);
tyhjenna();
}
}
}

//-----------------------------------------------------------------

//aaltoilu
void merenaalto(int kierrokset){
for(int g=0; g<kierrokset; g++){

for(float kierros=0; kierros< 2*PI; kierros=kierros+0.39){
for(int x=0; x<8; x++){
int z = int(round(skaalaus(sin(kierros + skaalaus(x,0,7,0,PI)),-1,1,0,7) ));
for(int i=0; i<=z; i++){
setLed(x,0,i);
setLed(x,1,i);
setLed(x,2,i);
setLed(x,3,i);
setLed(x,4,i);
setLed(x,5,i);
setLed(x,6,i);
setLed(x,7,i);
}
}
delay(50);
tyhjenna();
}
}
}
//-----------------------------------------------------------------

//käänteinen aaltoilu
void antiaalto(int kierrokset){
for(int g=0; g<kierrokset; g++){

for(float kierros=0; kierros< 2*PI; kierros=kierros+0.39){
tayta();
for(int x=0; x<8; x++){
int z = int(round(skaalaus(sin(kierros + skaalaus(x,0,7,0,PI)),-1,1,0,7) ));
resLed(x,0,z);
resLed(x,1,z);
resLed(x,2,z);
resLed(x,3,z);
resLed(x,4,z);
resLed(x,5,z);
resLed(x,6,z);
resLed(x,7,z);
if(z<7){
resLed(x,0,z+1);
resLed(x,1,z+1);
resLed(x,2,z+1);
resLed(x,3,z+1);
resLed(x,4,z+1);
resLed(x,5,z+1);
resLed(x,6,z+1);
resLed(x,7,z+1);
}
if(z>0){
resLed(x,0,z-1);
resLed(x,1,z-1);
resLed(x,2,z-1);
resLed(x,3,z-1);
resLed(x,4,z-1);
resLed(x,5,z-1);
resLed(x,6,z-1);
resLed(x,7,z-1);
}
}
delay(50);

}
}
tyhjenna();
}

//-----------------------------------------------------------------

float skaalaus(float in, float inMin, float inMax, float outMin, float outMax){
float out;
out = (in-inMin)/(inMax-inMin)*(outMax-outMin) + outMin;
return out;
}

//----------------------------------------------------------

void splash(int kiertomaara){
for(int i=0; i<kiertomaara; i++){

//täytto
for(int j=0; j<8; j++){
  kuva[j][0]=255;
  kuva[j][1]=255;
  kuva[j][2]=255;
  kuva[j][3]=255;
  kuva[j][4]=255;
  kuva[j][5]=255;
  kuva[j][6]=255;
  kuva[j][7]=255;
  delay(15);
}
delay(100);

//tyhjennys
for(int j=0; j<8; j++){
  kuva[j][0]=0;
  kuva[j][1]=0;
  kuva[j][2]=0;
  kuva[j][3]=0;
  kuva[j][4]=0;
  kuva[j][5]=0;
  kuva[j][6]=0;
  kuva[j][7]=0;
  delay(15+j*10);
}

}
}
//----------------------------------------------------------
// Set or clear exactly 512 voxels in a random order.
void effect_random_filler (int viive, int state){
int x,y,z;
int loop = 0;

if (state == 1){tyhjenna();} 
    else{tayta();}

while (loop<511){
	x = rand()%8;
	y = rand()%8;
	z = rand()%8;

	if ((state == 0 && getLed(x,y,z) == 0x01) || (state == 1 && getLed(x,y,z) == 0x00))
	{
		altervoxel(x,y,z,state);
		delay(viive);
		loop++;
	}	
}

}

//---------------------------------------------------------------------
void box_walls(int x1, int y1, int z1, int x2, int y2, int z2)
{
int iy;
int iz;

argorder(x1, x2, &x1, &x2);
argorder(y1, y2, &y1, &y2);
argorder(z1, z2, &z1, &z2);

for (iz=z1;iz<=z2;iz++)
{
	for (iy=y1;iy<=y2;iy++)
	{	
		if (iy == y1 || iy == y2 || iz == z1 || iz == z2)
		{
			kuva[iz][iy] = byteline(x1,x2);
		} else
		{
			kuva[iz][iy] |= ((0x01 << x1) | (0x01 << x2));
		}
	}
}

}
//-----------------------------------------------------------------
void effect_rain (int iterations)
{
int i, ii;
int rnd_x;
int rnd_y;
int rnd_num;

for (ii=0;ii<iterations;ii++)
{
	rnd_num = rand()%4;
	
	for (i=0; i < rnd_num;i++)
	{
		rnd_x = rand()%8;
		rnd_y = rand()%8;
		setLed(rnd_x,rnd_y,7);
	}
	
	delay(50);
	shift(1,-1);
}

}

//--------------------------------------------------------------------

void draw_positions_axis (char axis, unsigned char positions[64], int invert)
{
int x, y, p;
tyhjenna();
for (x=0; x<8; x++)
{
for (y=0; y<8; y++)
{
if (invert)
{
p = (7-positions[(x8)+y]);
} else
{
p = positions[(x
8)+y];
}

		if (axis == 1)
			setLed(x,y,p);
			
		if (axis == 2)
			setLed(x,p,y);
			
		if (axis == 3)
			setLed(p,y,x);
	}
}

}
//-------------------------------------------------------------------

void kirjain_lento(char kirjain, int nopeus){
tyhjenna();
if(kirjain==32){kirjain=0;};
if(kirjain>64 && kirjain<91){kirjain = kirjain - 64;}; //A-Z (isot)
if(kirjain>96 && kirjain<123){kirjain = kirjain - 96;}; //A-Z (pienet)
if(kirjain ==63 ){kirjain=27;};// Ä
if(kirjain ==33 ){kirjain=28;};// Ö

for(int i=0; i<8; i++){
//piirretään kirjain
kuva[7][i]=aakkoset[kirjain][0];
kuva[6][i]=aakkoset[kirjain][1];
kuva[5][i]=aakkoset[kirjain][2];
kuva[4][i]=aakkoset[kirjain][3];
kuva[3][i]=aakkoset[kirjain][4];
kuva[2][i]=aakkoset[kirjain][5];
kuva[1][i]=aakkoset[kirjain][6];
kuva[0][i]=aakkoset[kirjain][7];
delay(nopeus);//Tauko

if(i==7){
  delay(3*nopeus);
}
//Tyhjennetään uutta sivua varten
kuva[0][i]=0;
kuva[1][i]=0;
kuva[2][i]=0;
kuva[3][i]=0;
kuva[4][i]=0;
kuva[5][i]=0;
kuva[6][i]=0;
kuva[7][i]=0;

}
}
//---------------------------------------------------------------------
void effect_axis_updown_randsuspend (char axis, int tauko, int sleep, int invert){
unsigned char positions[64];
unsigned char destinations[64];

int i,px;

//set 64 rand pos
for(i=0; i<64; i++){
positions[i] = 0;
destinations[i] = rand()%8;
}

//loop 8 times to allow dest. 7 to reach all the way
for(i=0; i<8; i++){
//For every iteration, move all position one step closer to dest.
for(px=0; px<64; px++){
if(positions[px]<destinations[px]){positions[px]++;}
//piirto ja tauko
draw_positions_axis(axis, positions, invert);
delayMicroseconds(tauko);
}
}

//set all destin. to 7 opposite<>start
for(i=0; i<64; i++){destinations[i]=7;}

//suspend the positions in mid air for while
delay(sleep);

for(i=0; i<8; i++){
for(px=0; px<64; px++){
if(positions[px]<destinations[px]){positions[px]++;}
if(positions[px]>destinations[px]){positions[px]–;}
}
draw_positions_axis(axis, positions, invert);
delayMicroseconds(tauko);
}

}
//----------------------------------------------------------------------

void setLed( int x, int y, int z){
if(inrange(x,y,z)){kuva[z][y] |= (1<< x);}
}

void resLed( int x, int y, int z){
if(inrange(x,y,z)){kuva[z][y] &= ~(1<<x);}
}

boolean getLed( int x, int y, int z){
if(inrange(x,y,z)){
if(kuva[z][y] & (1<<x)){ return true;}
else{ return false;}
}
else{ return false;}
}

void altervoxel(int x, int y, int z, int state)
{
if (state == 1){
setLed(x,y,z);}
else{
resLed(x,y,z);}
}

void tayta(){
for(int a=0; a<8; a++){
for(int b=0; b<8; b++){
kuva[a][b]=255;
}}
}

void tyhjenna(){
tyhjennetaan=true;
while(tyhjennetaan==true){
;
}
}

boolean inrange(int x, int y, int z){
if(x >= 0 && x < 8 && y >= 0 && y < 8 && z >= 0 && z < 8){return true;}
else{return false;}
}

// Draw a wireframe box. This only draws the corners and edges,
// no walls.
void box_wireframe(int x1, int y1, int z1, int x2, int y2, int z2)
{
int iy;
int iz;

argorder(x1, x2, &x1, &x2);
argorder(y1, y2, &y1, &y2);
argorder(z1, z2, &z1, &z2);

// Lines along X axis
kuva[z1][y1] = byteline(x1,x2);
kuva[z1][y2] = byteline(x1,x2);
kuva[z2][y1] = byteline(x1,x2);
kuva[z2][y2] = byteline(x1,x2);

// Lines along Y axis
for (iy=y1;iy<=y2;iy++)
{
	setLed(x1,iy,z1);
	setLed(x1,iy,z2);
	setLed(x2,iy,z1);
	setLed(x2,iy,z2);
}

// Lines along Z axis
for (iz=z1;iz<=z2;iz++)
{
	setLed(x1,y1,iz);
	setLed(x1,y2,iz);
	setLed(x2,y1,iz);
	setLed(x2,y2,iz);
}

}

char byteline (int start, int end){
return ((0xff<<start) & ~(0xff<<(end+1)));
}

void argorder(int ix1, int ix2, int *ox1, int *ox2){
if (ix1>ix2){
int tmp;
tmp = ix1;
ix1= ix2;
ix2 = tmp;
}
*ox1 = ix1;
*ox2 = ix2;
}

// Draw a line between any coordinates in 3d space.
// Uses integer values for input, so dont expect smooth animations.
void line(int x1, int y1, int z1, int x2, int y2, int z2){
float xy; // how many voxels do we move on the y axis for each step on the x axis
float xz; // how many voxels do we move on the y axis for each step on the x axis
unsigned char x,y,z;
unsigned char lasty,lastz;
int X,Y,Z;

// We always want to draw the line from x=0 to x=7.
// If x1 is bigget than x2, we need to flip all the values.
if (x1>x2){
	int tmp;
	tmp = x2; x2 = x1; x1 = tmp;
	tmp = y2; y2 = y1; y1 = tmp;
	tmp = z2; z2 = z1; z1 = tmp;
}

if (y1>y2){
	xy = (float)(y1-y2)/(float)(x2-x1);
	lasty = y2;
} else
{
	xy = (float)(y2-y1)/(float)(x2-x1);
	lasty = y1;
}

if (z1>z2)
{
	xz = (float)(z1-z2)/(float)(x2-x1);
	lastz = z2;
} else
{
	xz = (float)(z2-z1)/(float)(x2-x1);
	lastz = z1;
}

// For each step of x, y increments by:
for (x = x1; x<=x2;x++)
{
	y = (xy*(x-x1))+y1;
	z = (xz*(x-x1))+z1;

            //X=int(x);
            //Y=int(y);
            //Z=int(z);
    	setLed(X,Y,Z);
}	

}

// Shift the entire contents of the cube along an axis
// This is great for effects where you want to draw something
// on one side of the cube and have it flow towards the other
// side. Like rain flowing down the Z axiz.
void shift (char axis, int direction){
int i, x ,y;
int ii, iii;
int state;

for (i = 0; i < 8; i++){
	if (direction == -1)
	{
		ii = i;
	} else
	{
		ii = (7-i);
	}	


	for (x = 0; x < 8; x++){
		for (y = 0; y < 8; y++){
			if (direction == -1)
			{
				iii = ii+1;
			} else
			{
				iii = ii-1;
			}
			
			if (axis == 1)
			{
				state = getLed(x,y,iii);
				altervoxel(x,y,ii,state);
			}
			
			if (axis == 2)
			{
				state = getLed(x,iii,y);
				altervoxel(x,ii,y,state);
			}
			
			if (axis == 3)
			{
				state = getLed(iii,y,x);
				altervoxel(ii,y,x,state);
			}
		}
	}
}

if (direction == -1)
{
	i = 7;
} else
{
	i = 0;
}	

for (x = 0; x < 8; x++)
{
	for (y = 0; y < 8; y++)
	{
		if (axis == 1)
			resLed(x,y,i);
			
		if (axis == 2)
			resLed(x,i,y);
		
		if (axis == 3)
			resLed(i,y,x);
	}
}

}

//---------------------------------------------------------------------------
void SetKerros(int x){
switch(x){

case 0:
//1 kerros
digitalWrite(k1,LOW);
digitalWrite(k2,LOW);
digitalWrite(k3,LOW);
break;

case 1:
//2 kerros
digitalWrite(k1,HIGH);
digitalWrite(k2,LOW);
digitalWrite(k3,LOW);
break;

case 2:
//3 kerros
digitalWrite(k1,LOW);
digitalWrite(k2,HIGH);
digitalWrite(k3,LOW);
break;

case 3:
//4 kerros
digitalWrite(k1,HIGH);
digitalWrite(k2,HIGH);
digitalWrite(k3,LOW);
break;

case 4:
//5 kerros
digitalWrite(k1,LOW);
digitalWrite(k2,LOW);
digitalWrite(k3,HIGH);
break;

case 5:
//6 kerros
digitalWrite(k1,HIGH);
digitalWrite(k2,LOW);
digitalWrite(k3,HIGH);
break;

case 6:
//7 kerros
digitalWrite(k1,LOW);
digitalWrite(k2,HIGH);
digitalWrite(k3,HIGH);
break;

case 7:
//8 kerros
digitalWrite(k1,HIGH);
digitalWrite(k2,HIGH);
digitalWrite(k3,HIGH);
break;
}
}
[/code]





5.jpg



1.JPG

Ei voi muuta sanoa, kuin että sekasin olet :smiley:

Itselläni ei olisi hermot riittäneet tuollaisen rakenteluun millään.

Onnittelut hienosta projektista! Voin vain kuvitella kun tuolta jäisi keskeltä pari lediä juottamatta tai iskisi ne vanhingossa väärin päin :wink: Seuraava rakentamaankin sitten RGB Ledeistä! Kyllähän tuo sitä luovaa hulluutta vaatii! :laughing:

Alkoi itselläkin viiraan. Parasta hakea kahvia, että tulee kierroksia lisää ja moinen hullu ajatus tippuu kyydistä.

Jos ottaisi peilin ja saisi siitä sen taustamaalin pois. Alumiininhan saa varmaan hapotettua, kuin kuparifolionkin. TAI parempi oisi jos pääsisi maskaamaan sen peilin takaosan itse ja jätettäisiin prosessissa sen maalaus kokonaan pois. Sitten tähän alumiini kuvioon juottaisi SMD-RGB ledit. Veikeä tulisi jos olisi aina pleksi välissä, jossa on reiät ledeille ja sitten taas lasi.

Käyttävät varmaan alumiinia tuon lasin hopeoimiseen. Ja tekevät sen tyhjiö aluminoinnilla. Kukas Suomessa tekee peilejä?

Nätti!
Onko tuon puhtaana pitäminen pölystä kuinka vaivaloista? :slight_smile:

Paljonko sitä kahvia oikein joit… :smiley:

Eipä ole pölyä kertynyt vielä kun ei ole hyllylle joutunut… varmasti voi tulla joskus hankaluuksia pölyn kanssa kun ei millään rätilläkään pysty puunaa, mutta paineilmalla/ imurilla varmasti lähtee.

Paketti on aika kestävä eli voi imuroida / yrittää pyyhkiä aika huoletta.

Komea on kuutio, ja varmasti työtä juottamisessa. Sen verran hyvän näköinen , että voisin itse testata jollain 4x4x4 versiolla sitten joskus kun pääsee vähän kelkkaan mukaan näissä sähkö puuhissa… Arduino uunon hommaaminen kiinnostais…

Minkä laisilla ledeillä ruo värillen homma toteutetaan? saati miten sitä sitten ohjataan? hieman monimutkasemmaksi liene menee…

En ole juuri rgb ledeihin törmännyt… Mutta tässäkin tuntuis olevan enemmän kuin kolme väriä:
http://www.youtube.com/watch?v=_cLLyfop3QI&feature=related

Järjettömän upea! Meni ihan hiljaiksksi, sillä mahdollisuudet ovat massiiviset. Tosin tuo kannattaisi laittaa plexi-kuution sisälle sormilta ja pölyltä suojaan… ehkä vähän matta/maito-pintainenkin voisi lasi olla, voisi olla siisti… :slight_smile:

Mikäli kysyt siis, että miten RGB-ledeillä tuollainen tapahtuisi, on vastaus se, että sinulla on 3 kertaa enemmän jalkoja juotettavana ja ohjattavana. RGB-ledi on siis nelijalkainen, yksi jalka (anodi tai katodi riippuen ledistä) jokaista väriä kohden ja sitten yhteinen jalka, yhteisanodi tai -katodi, edelleen riippuen ledistä. Linkittämäsi video on tehty RGB-ledeillä, joiden ominaisuus on, että muuttamalla punaisen (R), vihreän (G) ja sinisen (B) suhdetta keskenään saadaan eri värit. Voisin kuvitella, että koska ohjattavana on kolme väriä, ohjaat periaatteessa kolmea kuviota kerralla koodin puolella, jotta saat oikean värin ja oikean kuvion syntymään.

Ja jotta ei mennä ihan off-topiciksi, todella hieno kuutio on! Itsekin suojaisin sen jollain pleksillä niin siivoaminen helpottuu. Tiedä tosin miten tuo lämpenee suljetussa tilassa, hirveästi ledejä ja johtimia.

Pitänee kaivaa kuvetta ja tilata ledit tuota varten :sunglasses: .
Mika, voisitko pistää tarkempaa tietoa ledien ohjauskytkennöistä (lähinnä kiinnostais kytkis noiden shift registerien ja transistoripaketin liittäminen kuutioon ja arduinoon.
Datatiedot saan kyllä netistä, mutta miten olet itse kytkenyt osat?

Sitten kysymys kaksi; tuo ohjelma on näppärä, (pääohjelma valitsee palavat ledit ja aliohjelma hoitaa sytytyksen tai päinvastoin) joten voisiko sen toteuttaa toisella ohjelmointikielellä ja täysin toisenlaisell mikrokontrollerilla?

Näin yhden kerroksen ledien ohjaus tapahtuu, eli interrup koodin purku suomeksi.

Aluksi ledit sammutetaan ettei tulisi turhia “välähdyksiä” kun suoritetaan bittien kirjoitus shift regeihin. Sammutus tapahtuu enable lähdöllä, joka on johdotettu 3-to-8 piirin E3 inputtiin. Arduino laittaa enable lähdön pois päältä, 0V, jolloin 3-to-8 piiri asettaa lähtönsä 0 tilaan, joka estää kerroksia sytyttävien transistorien vedon. Toiset enablepinnit, E1 ja E2, ovat 3-to-8 piirissä tinattu suoraan 0V potentiaaliin.

Kun ledit on sammutettu, koodissa lisätään kerros muuttujaan yksi, kerros = kerros +1, jonka jälkeen vielä tarkastetaan ettei kerros muuttuja ole 8 tai yli. Jos on kerros muuttuja saa arvon 0, koska kerrokset ovat taulukossa 0-7 numeroissa. Nyt kun tiedetään mikä kerros on vuorossa, asetetaan oikeat lähdöt päälle. Kerrosten sytyttäminen taas tapahtui tuolla 3-to-8 paketilla, eli kolmella arduino lähdöllä kerrotaan binääri luku piirille, joka pistää outputin sen mukaisesti päälle, esim 101= 5 → lähtö 5 päälle. Tämä 3-to-8 piiri taas liipaisee transistori paketin transistorin päästävään tilaan. Lähdöt eivät kuitenkaan vielä mene päälle 3-to-8 piirissä, sillä enable on edelleen vedetty alas.

Tämän jälkeen suoritetaan rivien ledien kirjoitus eli bittien siirto shift registerissä. Bittien tilat luetaan kuva bufferista, jota muokataan pääohjelman edetessä. Kaikilta shift registereiltä tulee erikseen data in pinni arduinon lähtöön, ja yhteinen clock pinni yhteen arduinon lähtöön, yhteensä 9 lähtöä. Ideana on siis se, että aluksi clock bitti vedetään alas, lähtö 0V, koska bittien siirto tapahtuu nousevasta reunasta 0V->5V. Sen jälkeen ohjelma katsoo pitääkö ledin olla päällä vai ei, laittaa lähtön joko 0 tai 1 tilaan, tämä kertaa kahdeksan. Tämän jälkeen clock bitti vedetään ylös eli arduinon kahdeksan eri lähdön tilat siirtyvät kahdeksaan shift registeriin. Tämä luuppi suoritetaan kahdeksan kertaa, jolloin kaikkiin shift regeihin saadaan siirrettyä kahdeksan 0/1 tietoa.

Nyt kun kerros on valittu ja shift regeille on kirjoitettu tieto niistä ledeistä joiden tulee olla päälle, laitettaan enable pinni päälle ja ledit syttyvät.

Tässä vielä koodi poimittuna

[code] digitalWrite(enable,LOW); //Ledit offfffff

//Kerroksen lisäys ja asetus
kerros++;
if(kerros>=8){kerros=0;}
SetKerros(kerros); //Tämä funktio määrittää mitkä 3 lähtöä laitetaan päälle

// Kerroksen ledien asetus
for(int i=0; i<8; i++){
digitalWrite(clock,LOW);
PORTD = kuva[kerros][i]; //koska shift regien in lähdöt ovat arduinon yhdessä portissa
//voidaan arduinolla suorittaa portin kirjoitus ydellä lauseella.
//Onnistuu varmasti myös muilla kielillä
digitalWrite(clock,HIGH);
}

//Ledien sytytys
digitalWrite(enable,HIGH);
}[/code]

Toivottavasti tässä oli tarpeeksi kattavasti nuo kytkennät. Itse pinnit pääset hakemaan datasheeteistä. Pää asia oli siis tämä, arduinosta on käytössä 13 lähtö pinniä ledien ohjaamiseen. Jos ei halua käyttää 3-to-8 piiriä, tarvitaan 17 pinniä ohjaamiseen. 8x shift reg data in, 1x shift reg clock (yhteinen), 8x kerros transistorien ohjaus ( tai 3+1 jos käyttää 3-to-8 piiriä).

Muihin kieliin en osaa heti tältä kädeltä heittää vastausta. Tuo interrup toiminto on oleellinen koska sen avulla saadaan tuo päivitys tehtyä aina tarkoin väliajoin eli saadaan näköharha siitä että ledit palaisivat samaan aikaan joka kerroksessa. Mikrokontrollerilla ei ole niinkään väliä, kunhan se on tarpeeksi nopea suorittamaan nuo toiminnot ja siitä löytyy vähintään 13 ulostuloa.

Kerro hieman mikä kieli sinulla oli mielessä ja millainen mikrokontrolleri. Jos itse en osaa vastata, täällä on varmasti muita jotka osaavat.

Kehtaakko piirtää scheman. Olin ajatellut tämän olevan jo hallussa, mutta en ainakaan itse osannut pallotella tuota oikein päässäni.

Pieni optimointi:
kerros = kerros +1 % 8;

Pitäiskö tuo olla että summasta otetaan jakojäännös :slight_smile:
kerros = (kerros +1) % 8;

Joo. Mulla oli ensin mielessä kerros = ++kerros %8; mutta tuo on jo niin lukematon rivi, ettei sitä uskalla julkisesti näyttää. kerros++ ei sitten tietenkään toimi.

Itseasiassa jos katsotte koodia, olen käyttänyt

kerros++;
if(kerros>=8){kerros=0;}

Kirjoitin sen vain “selvemmäksi”, koska itselleni aikoinaan tuo kerros++ ei tarkoittanut yhtikäs mitään.

Katsotaan jos iskee herkkä hetki niin voin piirtää jonkinlaisen kuvion… se on sitten kynä/paperi versio, en jaksa kikkailla eaglella :unamused:

Ensimmäisenä tuki mieleen picaxe ja basic, koska se on ennalta tuttu ja yksinkertainen. Arduinoa en omista ja sen hinta on moninketrainen picaxe-järjestelmään verrattuna. Mitenkäs, mitä ulkoisia komponentteja tuo duemilanoven MCU tarvitsee (tarvitseeko esim erillisen kiteen yms.). Jos lisäkomponenttien määrä ei ole hiveä, voisi ostaa duemilanoven ja toisen siihen sopivan mikrokontrollerin oheiskomponentteineen ja käyttää lautaa piirin ohjelmoimiseen, jonka jälkeen piirin voisi istuttaa omalle piirilevylleen ja käyttää duemilanovea muihin projekkteihin.

Itsekin aloitin aikoinaan picaxella ja siirryin arduinoon koska picaxen ominaisuudet olivat turhan suppeat, tai sitten vain taito loppui kesken. Mitä nyt vanhasta tulee mieleen niin nuo taulukot ja interrupit voivat aihetuttaa tuskaa…

Uudempi Uno maksaa vajaa 10€. Siihen 16MHz kide, yksi 10k vastus ja kaksi 22pF konkkaa (omasta kuutiosta jäi kyllä pois) lisäksi ja 5V regu + regulle pari konkkaa eli jotain 2-3€. Noilla saa toimivan arduino mikrokontrollerin omatekemään piiriin. Ohjelmointiin tarvitset vielä ohjelmointi piirin 16€ jos et osta valmista arduino settiä, jossa voit ohjelmoida mikron.
http://www.robomaa.com/index.php?main_page=product_info&products_id=641
Itselläni on edellä mainittu kokoonpano kuutiossani.

Toimiiko tuo artikkelissa julkaistu koodi suoraan unossa?

Joo, ehkä ostan sittenkin arduino unon ja yhden ylimääräilen piirin, sekä edellä mainitut komponentit ja aloitan arduino alustojen kanssa lekkimisen.

Jos tuota ohjainta alkaa kasaamaan piirilevylle, minkä kokoinen pitää varata? 14x14cm riittänee…

Toimii sillä omassani on nyt UNO ja koodi on sen mukainen, tuossa koodissa ei taida mikään muuttua vaikka olisikin duemilanove. Itsekin muistin että minulla oli duemilanove, mutta nyt kun muistelen niin tilasin Unon kun halusin istuttaa mikrokontrollerin tuohon ohjain piiriin. Oma piirilevyni on 10x14cm ja siitäkin saisi viilattua ylimääräistä pois pidemmästä sivusta.


Onko tuon atmegan päälle liimattu paperi?
Ilmeisesti jokaiselle pystyriville on oma vastus?
Toimivatko leditkin 5 voltilla? Jos toimivat, paljonko ne kuluttavat virtaa, eli riittääkö yksi 1,5A regu sekä kontrollerille että ledeille? Tietysti, ledeistä palaa kerrallaan korkeintaa 64 (jos ymmärsin oikein), jos jokaisen ledin läpi johdetaan esim 20mA saadaan 64x0,02mA = 1,28A, olloin MCU:lle jää 320mA.

Sitten jos vielä saisi lisää tietoa miten kytkit noi piirit (mikä piirin nasta mihinkin mikrokontrollerin koipeen/lediin/ledikerrokseen, koska koodin toimimisen annalta tieto on aika oleellinen).