Dương Huynh gửi vào
- 24084 lượt xem
1. Giới thiệu
Tiếp nối với bài viết Cube LED 8x8x8 Arduino Promini, hôm nay mình xin chia sẽ tiếp phần code lập trình cho khối Cube. Thật sự thì mình vẫn chưa viết được một vài hiệu ứng, nhưng mình mong rằng bài viết này sẽ giúp ích cho mọi người trong việc thao tác với mảng 3 chiều. Trong bài viết này mình sẽ chia sẽ từ thuật toán tới code để mọi người có thể hình dung các tạo một hiệu ứng cho Cube LED, và mình rất mong sẽ nhận được thêm các hiệu ứng mới, hiệu ứng đẹp từ các bạn. Trong quá trình viết bài viết này, mình đã có những sự rút gọn để thuật toán nhanh hơn trước.
2. Khai báo và các phần mềm hỗ trợ
2.1 Khai báo
Ở phần 1 mình đã đề cập đến việc dùng thêm thẻ nhớ SD để nhớ dữ liệu. Nên mình đã có khai báo thẻ nhớ SD luôn, cũng như đã vẽ các chân kết nối trên mạch in luôn rồi. Nếu như mọi người dùng đến thẻ nhớ SD thì có thể kết nối theo chuẩn SPI có ghi ở bên dưới (nhìn theo cái file mạch in nhé!).
Chân A1, A2, A3 lần lượt là các chân nối với 74hc595.
Mảng Tangz[8] là chân ra của các tầng z. Chúng ta sẽ quét các tầng z để mở các đèn.
//byte DS=A1; //byte STCP=A2; //byte SHCP=A3; int Tangz[8] = {2, 3, 4, 5, 6, 7, 8, 9}; /* SD card read/write This example shows how to read and write data to and from an SD card file The circuit: * SD card attached to SPI bus as follows: ** MOSI - pin 11 ** MISO - pin 12 ** CLK - pin 13 ** CS - pin 10 created Nov 2010 by David A. Mellis modified 9 Apr 2012 by Tom Igoe This example code is in the public domain. */ #include <SPI.h> #include <SD.h> File myFile;
2.2 Setup
Khai báo thẻ nhớ SD nó sẽ có 2 dòng lệnh để xuất ra Serial cho ta biết là thẻ nhớ có hoạt động hay không. Để tiết kiệm tối đa dung lượng sram mình đã không cho nó in ra luôn, nhưng nếu mọi người muốn xem thẻ nhớ có hoạt động không thì có thể bỏ dấu //
Nhưng nếu các bạn vẫn thích in ra mà lại không muốn tốn RAM thì xem bài viết này của bạn ksp nhé.
void setup() { Serial.begin(9600); pinMode(A1, OUTPUT); pinMode(A2, OUTPUT); pinMode(A3, OUTPUT); for (int x = 0; x < 8; x++) { pinMode(Tangz[x], OUTPUT); } while (!Serial) { ; } if (!SD.begin(10)) { //Serial.println("initialization failed!"); return; } //Serial.println("initialization done."); }
2.3 Các phần mềm hỗ trợ
dich74hc595 sẽ nhận vào một biến a (với a có giá trị là 0 hoặc 1; quy ước luôn là 1 là đèn sáng, 0 là đèn tắt) và sẽ trả ra (shiftout) một tín hiệu đến 74hc595. Khi A1 LOW hoặc HIGH, thể hiện trạng thái tắt hoặc mở của đèn. Còn A3 là một xung cho chân SHCP, nó phân biệt giữa 2 đèn đứng liền nhau. Mọi người có thể xem lại phần 74hc595 nhé!
void dich74hc595(byte a){ if (a==0) { digitalWrite(A1, LOW); digitalWrite(A3, HIGH); digitalWrite(A3, LOW); } else if(a==1) { digitalWrite(A1, HIGH); digitalWrite(A3, HIGH); digitalWrite(A3, LOW); } }
mo74hc595 chỉ đơn giản là tạo ra một xung vào chân STCP thôi. Khi có xung ở đây 74hc595 sẽ xuất các tín hiệu trước đó đã nhận được từ dich74hc595 ra ngoài.
void mo74hc595() { digitalWrite(A2, HIGH); digitalWrite(A2, LOW); }
reset sẽ đẩy 64 con 0 vào 74hc595. Làm như thế để tránh trường hợp khi chuyển từ tầng này sang tầng khác của Cube LED thì các đèn nó không bị sáng lung tung lên.
void reset() { for (byte t=0; t<64; t++) { dich74hc595(0); } mo74hc595(); }
hienthi là một hàm cho giúp mở hoặc tắt các đèn theo một dữ liệu cho trước. Do sram ít nên mình đã cho mangba trở thành biến cục bộ của các hiệu ứng, vì thế nên mình phải khai báo mangba là một mảng được nhập vào hàm hiển thị (tự động nó sẽ nhập vào thôi). Làm như thế mình sẽ dùng được hàm hienthi trên mọi hàm hiệu ứng. Còn biến Solan sẽ là số lần lặp lại của một hình ảnh nào đó, khoảng 60 lần thì được 1 giây.
Trong một lần hiển thị một hình ảnh, mình sẽ quét lần lượt 8 tầng z, ở mỗi tầng x mình sẽ quét x, quét y để tìm ra toạ độ của đèn nào sáng hay tắt. Sau đó mình sẽ cho mở tầng đó lên rồi tắt nó đi để chuẩn bị cho tầng tiếp theo.
void hienthi(byte mangba[8][8][8], byte Solan){ for (byte t=0; t<Solan; t++) { // so 7 la thoi gian qua 1 tang //to chinh la toc do roi cua mua for (byte z=0; z<8; z++) { for (byte x=0; x<8; x++) { for (byte y=0; y<8; y++) { dich74hc595(mangba[x][y][z]); } } mo74hc595(); digitalWrite(Tangz[z], HIGH); reset(); digitalWrite(Tangz[z], LOW); } } }
Như vậy là đã tạm đủ rồi, trong một số hiệu ứng chúng ta sẽ cần thêm vài chương trình con nữa để hỗ trợ, nhưng tới đó rồi tính sau nhé!
3. Hiệu ứng mưa rơi
3.1 Ý tưởng
Hiệu ứng này cần 2 thông số đầu vào đó là số lần mưa (Solanmua) và số lần hiển thị một ảnh (Solan).
Trong một lần mưa, đầu tiên ta phải tìm ra số hạt mưa rơi trong cùng một lúc (tìm ngẫu nhiên). Trong code mình đã cài cho random từ 1 đến 4. Mình cài là 1 vì không muốn có lúc lại ko có hạt nào rơi. Còn 4 vì không muốn rơi quá nhiều. Sau đó mình sẽ chọn ngẫu nhiên ra x, y để tìm toạ độ tại tầng z=7 (mưa rơi từ trên xuống) để tìm vị trí bắt đầu rơi của hạt mưa. Sau đó là hiển thị LED lên. Kế tiếp ta phải dời 7 tầng còn lại xuống 1 tầng. Sau đó trả các giá trị của tầng z=7 trở về 0 để chuẩn bị cho việc chọn ngẫu nhiên ở vòng lặp tiếp theo.
3.2 Sơ đồ thuật toán
3.3 Code
void mua(int solanmua, byte Solan) { byte mangba[8][8][8] = { 0 }; for (byte s = 0; s < solanmua; s++) { byte n = random(1, 5); // chọn ngẫu nhiên số hạt mưa rơi for (byte h = 0; h < n; h++) { byte x = random(0, 8); // chọn ngẫu nhiên một toạ độ hạt mưa byte y = random(0, 8); mangba[x][y][7] = 1; // Cho cái hạt mưa đó =1 để cái đèn đó sáng lên } hienthi(mangba, Solan); // hiển thị for (byte z = 0; z < 7; z++) { // dời các tầng z xuống một tầng for (byte x = 0; x < 8; x++) { // nhưng phải giữ nguyên toạ độ theo x, y để mưa rơi thẳng for (byte y = 0; y < 8; y++) { mangba[x][y][z] = mangba[x][y][z + 1]; } } } for (byte x = 0; x < 8; x++) { for (byte y = 0; y < 8; y++) { mangba[x][y][7] = 0; // trả tầng z=7 về 0 } } } }
4. Hiệu ứng plane
4.1 Ý tưởng
Hiệu ứng này sẽ tạo ra một mảng 2 chiều gồm 64 LED. Và mảng sáng này sẽ được dời đi theo trục tuỳ thích. Chắc hẳn mọi người sẽ rất muốn cube LED của mình có thể xuất ra chữ, chúng ta có thể thay đổi mảng sáng của hiệu ứng này thành một chữ cái, một ký tự hay hình ảnh này đó và cho nó dời đi để tạo thêm một hiệu ứng mới. Hoặc cho nó dời đi và trên đường đi sẽ bỏ lại vài đèn để tạo nên một sự ngẫu nhiên. Hiệu ứng này sẽ tạo ra cảm giác sâu rộng cho khối cube. Dời đi rồi thì phải trả trở về luôn nha.
Hiệu ứng plane cần 2 biến là trục (truc) và Solan (giống hiệu ứng trên). Trục là một ký tự (X, Y hoặc Z) để thể hiện trục chuyển động của mảng, với X nó sẽ chạy theo trục X.
Lần này tôi sẽ không sử dụng hàm resetmangba như trên nữa, vì tôi sẽ reset luôn trong đoạn code tính toán sáng hay tắt luôn. Đầu tiên ta sẽ dời theo trục một trục theo 8 lần. Với mỗi lần ta sẽ quét hết khối cube, với lần dời thứ nhất i=0 ta thay toạ độ của truc x bằng i (nếu dời theo trục x) nhé. Tương tự thế cho trục Y ,Z. Sau đó là hiển thị ra.
Sau khi dời đi, ta sẽ bắt đầu quá trình quay ngược lại. Cũng giống với chuyến đi, nhưng chuyến về chỉ có 7 lần thôi, vì cuối chuyến đi thì nó đã ở tầng (cột hoặc hàng) thứ 8 rồi. Nó hoàn toàn giống với chuyến đi, chỉ khác ở chỗ i lần này sẽ chạy ngược từ 6 đến 0 nên toạ độ cũng được ngược lại luôn, toạ độ một điểm sẽ trở thành mangba[7-x][7-y][i].
4.2 Sơ đồ thuật toán
4.3 Code
void plane(char truc, byte Solan) { byte mangba[8][8][8] = { 0 }; for (byte i = 0; i < 8; i++) { for (byte z = 0; z < 8; z++) { for (byte x = 0; x < 8; x++) { for (byte y = 0; y < 8; y++) { mangba[x][y][z] = 0; if (truc == 'X') { mangba[i][y][z] = 1; } if (truc == 'Y') { mangba[x][i][z] = 1; } if (truc == 'Z') { mangba[x][y][i] = 1; } } } } hienthi(mangba, Solan); } for (int i = 7; i >= 0; i--) { for (byte z = 0; z < 8; z++) { for (byte x = 0; x < 8; x++) { for (byte y = 0; y < 8; y++) { mangba[7 - x][7 - y][7 - z] = 0; if (truc == 'X') { mangba[i][7 - y][7 - z] = 1; } if (truc == 'Y') { mangba[7 - x][i][7 - z] = 1; } if (truc == 'Z') { mangba[7 - x][7 - y][i] = 1; } } } } hienthi(mangba, Solan); } }
5. Hiệu ứng Plane2
5.1 Ý tưởng
Hiệu ứng này là nâng cấp từ hiệu ứng Plane. Nó sẽ tạo ra một mảng sáng và dời mảng này đi theo trục mà ta chọn, trên đường đi ta sẽ chọn ngẫu nhiên 8 đèn nằm trên mỗi tầng để bỏ lại. Khi đi hết thì ta sẽ thu các đèn bị bỏ lại đó trở về để tạo thành 1 mảng ở mua bên kia của trục (cách làm giống hiệu ứng mưa rơi). Do hiệu ứng này giống với 2 hiệu ứng trên nên mình sẽ bỏ qua phần sơ đồ thuật toán!
5.2 Code
void plane2(char truc, byte Solan) { byte mangba[8][8][8] = { 0 }; byte manghai[8][8] = { 0 }; //Đây là mảng dùng để đánh dấu là đèn nào đã bị bỏ lại for (byte x = 0; x < 8; x++) { // Tạo ra một mảng sáng trước khi dời đi for (byte y = 0; y < 8; y++) { if (truc == 'X') { mangba[0][x][y] = 1; } else if (truc == 'Y') { mangba[x][0][y] = 1; } else { mangba[x][y][0] = 1; } } } hienthi(mangba, Solan * 2); for (byte i = 1; i < 8; i++) { // Do đã tạo 1 mảng sáng trước rồi nên chỉ cần dời mảng này đi trong 7 lần byte u = 0; while (u < 8) { // tại mỗi lần rời đi, ta bỏ lại 8 điểm ngẫu nhiên byte x = random(8); byte y = random(8); if (manghai[x][y] == 0) { manghai[x][y] = 1; u = u + 1; hienthi(mangba, 1); // do sư ngẫu nhiên có thể bị trùng, nên ta thêm lệnh này để // hình ảnh được liên tục } } for (byte x = 0; x < 8; x++) { // Bắt đầu việc dời trục for (byte y = 0; y < 8; y++) { if (manghai[x][y] == 0) { if (truc == 'X') { mangba[i][x][y] = 1; mangba[i - 1][x][y] = 0; } else if (truc == 'Y') { mangba[x][i][y] = 1; mangba[x][i - 1][y] = 0; } else if (truc == 'Z') { mangba[x][y][i] = 1; mangba[x][y][i - 1] = 0; } } } } } hienthi(mangba, Solan); // ngược lại for (byte z = 0; z < 8; z++) { // Z là số lần dời for (byte i = 1; i < 8 - z; i++) { // i là số công việc phải làm trong 1 lần dời for (byte x = 0; x < 8; x++) { for (byte y = 0; y < 8; y++) { if (truc == 'X') { if (mangba[7 - i][x][y] == 1) { mangba[7 - i + 1][x][y] = 1; mangba[7 - i][x][y] = 0; } } else if (truc == 'Y') { if (mangba[x][7 - i][y] == 1) { mangba[x][7 - i + 1][y] = 1; mangba[x][7 - i][y] = 0; } } else { if (mangba[x][y][7 - i] == 1) { mangba[x][y][7 - i + 1] = 1; mangba[x][y][7 - i] = 0; } } } } } hienthi(mangba, Solan + 4); // lấy Solan=4 rồi cộng thêm 4 nữa vì lúc chọn 8 vị trí ngẫu // nhiên mình đã cho hiển thi hết 8 lần } }
6. Kết quả
Còn chương trình chính chỉ việc gọi hiệu ứng ra mà thôi!
void loop() { mua(100, 7); plane('X', 4); plane('Y', 4); plane('Z', 4); plane2('X', 4); plane2('Y', 4); plane2('Z', 4); }
Có cái link để coi cái hiệu ứng nó ra thế lào chứ nhỉ!
Chắc là phải có thêm phần 3 thôi, bài viết này cũng đã dài lắm rồi! Có lẽ sau một thời gian nữa mình sẽ phải viết tiếp để bổ sung thêm các hiệu ứng mới, vì các hiệu ứng sau mình cũng chưa có thời gian để hiệu chỉnh cho nó nhanh nhất có thể, cũng như là đẹp nhất có thể!