tuenhi.n2012 gửi vào
- 10739 lượt xem
DẪN NHẬP
Về LED ma trận, mình thấy trên diễn đàn cũng đã có rất nhiều bài viết. Để tiếp tục chuỗi bài về B.A.M, mình sẽ chia sẻ các bạn cách để Bicolor Led Matrix có thể hiển thị 256 màu riêng biệt với phương pháp B.A.M – 4 bit, tất nhiên nếu mắt người có thể phân biệt được sự khác biệt này...



I. GIỚI THIỆU
II. B.O.M CHO BẢNG LED 32x32 - 2 MÀU
No.
Item
Spec
Q’ty
Remarks
1
74HC595
16
2
ULN2803
16
3
74HC238
2
4
TIP42C
16
5
2N2222
16
6
Arduino Uno/ Mega
1
7
Bi-color Led Matrix
16
8
R100
200
Hạn dòng LED
9
Tụ 0.1uF
16
10
R1K
20
11
R10K
40
12
Bus 8
20
13
Bus 4
4
14
Mica trong/đục
1
15
Jack 3.5mm & dây
1
Để làm VU meter
16
Jack nguồn, linh tinh khác
1
III. SƠ ĐỒ NGUYÊN LÝ
Các bạn có thể xem chi tiết theo hình vẽ trên cho bảng Led 32x32, lưu ý:
Như vậy để điều khiển tổng cộng 2x32x32=2048 LED chúng ta chỉ cần :
IV. CHƯƠNG TRÌNH
//************************************************************************************************************// // The 32x32 BiColor LED Matrix Table //************************************************************************************************************// #include <SPI.h> // SPI Library used to clock data out to the shift registers #define blank_pin 3 // Defines actual BIT of PortD for blank - is Arduino UNO pin 3, MEGA pin 5 #define latch_pin 2 // Defines actual BIT of PortD for latch - is Arduino UNO pin 2, MEGA pin 4 #define clock_pin 13 // used by SPI, must be 13 SCK 13 on Arduino UNO, 52 on MEGA #define data_pin 11 // used by SPI, must be pin MOSI 11 on Arduino UNO, 51 on MEGA #define RowA_Pin 4 #define RowB_Pin 5 #define RowC_Pin 6 #define RowD_Pin 7 //************************************************************************************************************// byte red[4][128]; byte green[4][128]; //*********** Defining the Matrix ************* #define BAM_RESOLUTION 4 // EG 4 bit colour = 15 variation of R, G (256 colours) const byte Size_X = 32; //Number of Layers Y axis (levels/Layers) const byte Size_Y = 32; //Number of LEDs X axis (Left to right across front) int level = 0; //keeps track of which level we are shifting data to int row = 0; int BAM_Bit, BAM_Counter = 0; // Bit Angle Modulation variables to keep track of things byte myRed, myGreen, myBlue; //****setup****setup****setup****setup****setup****setup****setup****setup****setup****setup****setup****setup****setup void setup() { SPI.setBitOrder(MSBFIRST); //Most Significant Bit First SPI.setDataMode(SPI_MODE0); // Mode 0 Rising edge of data, keep clock low SPI.setClockDivider(SPI_CLOCK_DIV2); //Run the data in at 16MHz/2 - 8MHz noInterrupts(); // kill interrupts until everybody is set up //We use Timer 1 to refresh the cube TCCR1A = B00000000; //Register A all 0's since we're not toggling any pins TCCR1B = B00001011; //bit 3 set to place in CTC mode, will call an interrupt on a counter match //bits 0 and 1 are set to divide the clock by 64, so 16MHz/64=250kHz TIMSK1 = B00000010; //bit 1 set to call the interrupt on an OCR1A match OCR1A = 20; // you can play with this, but I set it to 20, which means: //our clock runs at 250kHz, which is 1/250kHz = 4us //with OCR1A set to 20, this means the interrupt will be called every (20+1)x4us=84us, // which gives a multiplex frequency of about 12kHz //finally set up the Outputs pinMode(latch_pin, OUTPUT); //Latch pinMode(blank_pin, OUTPUT); //Blank pinMode(data_pin, OUTPUT); //MOSI DATA pinMode(clock_pin, OUTPUT); //SPI Clock pinMode(RowA_Pin, OUTPUT); pinMode(RowB_Pin, OUTPUT); pinMode(RowC_Pin, OUTPUT); pinMode(RowD_Pin, OUTPUT); SPI.begin(); //start up the SPI library interrupts(); //let the show begin, this lets the multiplexing start } //***end setup***end setup***end setup***end setup***end setup***end setup***end setup***end setup***end setup***end setup void loop() { //***start loop***start loop***start loop***start loop***start loop***start loop***start loop***start loop***start loop clearfast(); fillTable(4, 8); delay(10000); clearfast(); fillTable(8, 8); delay(10000); clearfast(); for (int x = 0; x < 32; x++) { for (int y = 0; y < 32; y++) { LED(x, y, int(x / 2), int(y / 2)); delay(50); } } delay(10000); } //***end loop***end loop***end loop***end loop***end loop***end loop***end loop***end loop***end loop***end loop***end loop***end loop void LED(int CX, int CY, int CR, int CG) { //****LED Routine****LED Routine****LED Routine****LED Routine // First, check and make sure nothing went beyond the limits, Limits are either 0 or Size of the Y,X,Z axis -1 for locations, and 0 or Bam_Resolution - 1 for brightness/colour CX = checkConstrains(CX, 0, Size_X - 1); //Matrix X axis CY = checkConstrains(CY, 0, Size_Y - 1); //Matrix Y axis CR = checkConstrains(CR, 0, (1 << BAM_RESOLUTION) - 1); //Cube Red CG = checkConstrains(CG, 0, (1 << BAM_RESOLUTION) - 1); //Cube Green int WhichByte = int(CY * 4 + CX / 8); int WhichBit = 7 - (CX % 8); for (byte I = 0; I < BAM_RESOLUTION; I++) { //*** RED *** bitWrite(red[I][WhichByte], WhichBit, bitRead(CR, I)); //*** GREEN *** bitWrite(green[I][WhichByte], WhichBit, bitRead(CG, I)); } } //****LED ROUTINE END**** void rowScan(byte row) { if (row & 0x01) PORTD |= 1 << RowA_Pin; //RowA pin HIGH else PORTD &= ~(1 << RowA_Pin); //RowA pin LOW if (row & 0x02) PORTD |= 1 << RowB_Pin; //RowA pin HIGH else PORTD &= ~(1 << RowB_Pin); //RowA pin LOW if (row & 0x04) PORTD |= 1 << RowC_Pin; //RowA pin HIGH else PORTD &= ~(1 << RowC_Pin); //RowA pin LOW if (row & 0x08) PORTD |= 1 << RowD_Pin; //RowA pin HIGH else PORTD &= ~(1 << RowD_Pin); //RowA pin LOW } ISR(TIMER1_COMPA_vect) { //***MultiPlex BAM***MultiPlex BAM***MultiPlex BAM***MultiPlex BAM***MultiPlex BAM***MultiPlex BAM***MultiPlex BAM //This routine is called in the background automatically at frequency set by OCR1A //In this code, I set OCR1A to 20, so this is called every 84us, giving each level in the matrix 84us of ON time //There are 8 levels, so we have a maximum brightness of 1/8, since the level must turn off before the next level is turned on //The frequency of the multiplexing is then 84us*8=672us, or 1/672us= about 1.5kHz PORTD |= ((1 << blank_pin)); //The first thing we do is turn all of the LEDs OFF, by writing a 1 to the blank pin //This is 4 bit 'Bit angle Modulation' or BAM, There are 8 levels, so when a '1' is written to the color brightness, //each level will have a chance to light up for 1 cycle, the BAM bit keeps track of which bit we are modulating out of the 4 bits //Bam counter is the cycle count, meaning as we light up each level, we increment the BAM_Counter if (BAM_Counter == 8) BAM_Bit++; else if (BAM_Counter == 24) BAM_Bit++; else if (BAM_Counter == 56) BAM_Bit++; BAM_Counter++; //Here is where we increment the BAM counter switch (BAM_Bit) { //The BAM bit will be a value from 0-3, and only shift out the arrays corresponding to that bit, 0-3 //Here's how this works, each case is the bit in the Bit angle modulation from 0-4, case 0: //Red myTransfer(red[0][level + 0]); myTransfer(red[0][level + 1]); myTransfer(red[0][level + 2]); myTransfer(red[0][level + 3]); myTransfer(red[0][level + 64]); myTransfer(red[0][level + 65]); myTransfer(red[0][level + 66]); myTransfer(red[0][level + 67]); //Green myTransfer(green[0][level + 0]); myTransfer(green[0][level + 1]); myTransfer(green[0][level + 2]); myTransfer(green[0][level + 3]); myTransfer(green[0][level + 64]); myTransfer(green[0][level + 65]); myTransfer(green[0][level + 66]); myTransfer(green[0][level + 67]); break; case 1: //Red myTransfer(red[1][level]); myTransfer(red[1][level + 1]); myTransfer(red[1][level + 2]); myTransfer(red[1][level + 3]); myTransfer(red[1][level + 64]); myTransfer(red[1][level + 65]); myTransfer(red[1][level + 66]); myTransfer(red[1][level + 67]); //Green myTransfer(green[1][level]); myTransfer(green[1][level + 1]); myTransfer(green[1][level + 2]); myTransfer(green[1][level + 3]); myTransfer(green[1][level + 64]); myTransfer(green[1][level + 65]); myTransfer(green[1][level + 66]); myTransfer(green[1][level + 67]); break; case 2: //Red myTransfer(red[2][level]); myTransfer(red[2][level + 1]); myTransfer(red[2][level + 2]); myTransfer(red[2][level + 3]); myTransfer(red[2][level + 64]); myTransfer(red[2][level + 65]); myTransfer(red[2][level + 66]); myTransfer(red[2][level + 67]); //Green myTransfer(green[2][level]); myTransfer(green[2][level + 1]); myTransfer(green[2][level + 2]); myTransfer(green[2][level + 3]); myTransfer(green[2][level + 64]); myTransfer(green[2][level + 65]); myTransfer(green[2][level + 66]); myTransfer(green[2][level + 67]); break; case 3: //Red myTransfer(red[3][level]); myTransfer(red[3][level + 1]); myTransfer(red[3][level + 2]); myTransfer(red[3][level + 3]); myTransfer(red[3][level + 64]); myTransfer(red[3][level + 65]); myTransfer(red[3][level + 66]); myTransfer(red[3][level + 67]); //Green myTransfer(green[3][level]); myTransfer(green[3][level + 1]); myTransfer(green[3][level + 2]); myTransfer(green[3][level + 3]); myTransfer(green[3][level + 64]); myTransfer(green[3][level + 65]); myTransfer(green[3][level + 66]); myTransfer(green[3][level + 67]); //Here is where the BAM_Counter is reset back to 0, it's only 4 bit, but since each cycle takes 8 counts, //, it goes 0 8 16 32, and when BAM_counter hits 64 we reset the BAM if (BAM_Counter == 120) { BAM_Counter = 0; BAM_Bit = 0; } break; } //switch_case rowScan(row); PORTD |= 1 << latch_pin; //Latch pin HIGH PORTD &= ~(1 << latch_pin); //Latch pin LOW delayMicroseconds(10); //???; PORTD &= ~(1 << blank_pin); //Blank pin LOW to turn on the LEDs with the new data // Blank is the same as the OE or ENABLE pin row++; //inrement the anode level level = row * 4; if (row == 16) //go back to 0 if max is reached row = 0; if (level == 64) //if you hit 64 on level, this means you just sent out all 128 bytes, so go back level = 0; pinMode(blank_pin, OUTPUT); //moved down here so outputs are all off until the first call of this function } //***MultiPlex BAM END***MultiPlex BAM END***MultiPlex BAM END***MultiPlex BAM END***MultiPlex BAM END***MultiPlex BAM END***MultiPlex BAM END int checkConstrains(int value, int min, int max) { if (value < min) { return min; } else if (value > max) { return max; } else { return value; } } inline static uint8_t myTransfer(uint8_t C_data) { SPDR = C_data; asm volatile("nop"); asm volatile("nop"); } void delay_ms(uint16_t x) { uint8_t y, z; for (; x > 0; x--) { for (y = 0; y < 90; y++) { for (z = 0; z < 6; z++) { asm volatile("nop"); } } } } void clearfast() { for (unsigned char j = 0; j < 128; j++) { red[0][j] = 0; red[1][j] = 0; red[2][j] = 0; red[3][j] = 0; green[0][j] = 0; green[1][j] = 0; green[2][j] = 0; green[3][j] = 0; } } void fillTable(byte R, byte G) { // This subroutine fills the cube with a colour for (byte x = 0; x < 32; x++) { // scan thru every column for (byte y = 0; y < 32; y++) { // scan thru every panel LED(x, y, R, G); } } }V. HÌNH ẢNH THỰC TẾ
1. Hình thực tế cho
2. Hình thực tế cho
3. Hình thực tế cho
VI. LỜI KẾT
Trong Video, các bạn thấy bảng LED chớp tắt liên tục (flicker) là bởi nhìn qua CAMERA, thực tế khi nhìn bằng mắt thường bảng LED rất đẹp và không bị nhấp nháy như vậy.
Các bạn có thể so sánh giữa việc dùng phương pháp B.A.M và điều khiển thông thường với "cùng cấu hình phần cứng" qua các Videos sau:
Nếu có sẵn Led ma trận RGB, các bạn có thể dùng phương pháp này để điều khiển chúng. Thực tế là mình cũng đã làm & chia sẻ trên RGB LED CUBE 8x8x8, cube này tương đương với 8 Led ma trận RGB.
.
Các bạn nhớ Rate Node để có thêm động lực nhé. THANKS !