ĐIỀU KHIỂN BẢNG LED BICOLOR 32x32 BẰNG B.A.M

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

  • 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.
  • Về B.A.M (Bit Angle Modulation) và mạch, các bạn có thể tham khảo tại: http://arduino.vn/result/1697-dieu-khien-rgb-led-cube-8x8x8-bang-phuong-phap-bam-4-bit-voi-74hc595

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 ý:

  1.  Linh kiện: so với sơ đồ nguyên lý, mạch thực tế cần có thêm các linh kiện sau
  2. CỘT:  màu RED và GREEN được nối tương tự nhau, dễ hiểu hơn các bạn nên xem hình bên dưới, cụ thể như sau
    • Màu RED:
      • Led Matrix 0 & 4 nối chung với 74HC595_RED_0.
      • …….
      • Led Matrix 11 & 15 nối chung với 74HC595_RED _7.
    • Màu GREEN:
      • Led Matrix 0 & 4 nối chung với 74HC595_GREEN_0.
      • …….
      • Led Matrix 11 & 15 nối chung với 74HC595_GREEN _7.
  3. HÀNG: do chỉ có 16 con TIP42C nên hàng được đấu nối chung như sau
    • Hàng 0 & 16 nối chung với TIP42C_0.
    • …….
    • Hàng 15 & 31 nối chung với TIP42C_15.

Như vậy để điều khiển tổng cộng 2x32x32=2048 LED chúng ta chỉ cần :

  • 2 x 74HC238 + 16 x 2N2222 + 16 x TIP42C để điều khiển 32 hàng.
  • 16 x 74HC595 + 16 x ULN2803 (8 cho RED & 8 cho GREEN) để điều khiển 32 cột.

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

  • fillTable(4,8)

2. Hình thực tế cho 

  • fillTable(8,8)

3. Hình thực tế cho

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);
    }
}

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. wink.

Các bạn nhớ Rate Node để có thêm động lực nhé. THANKS !

Youtube: 
B.A.M BICOLOR MATRIX 32x32 - FIRST TEST
Bài viết truyền cảm hứng: 
lên
2 thành viên đã đánh giá bài viết này hữu ích.
Các dự án được truyền cảm hứng

Select any filter and click on Apply to see results

Các bài viết cùng tác giả

ARDUINO™ "HACK" PLC SIEMENS™ QUA MẠNG ETHERNET

Hôm nay cuối tuần, mình sẽ dành thời gian làm một PLC & ARDUINO DEMO KIT và chia sẻ với các bạn cách kết nối Arduino với PLC S7-300 thông qua Ethernet, và thậm chí Read/Write vào PLC S7-300™ ở chế độ bảo mật cao nhất của CPU. Sau khi đọc bài viết này, các kỹ sư tự động có lý do để phải dè chừng khi tự dưng thấy một anh chàng điện tử cầm một Arduino có gắn Ethernet Shield và đang lúi húi kết nối vào hệ thống mạng Ethernet của nhà máy.....winkwinkwink....

lên
17 thành viên đã đánh giá bài viết này hữu ích.

BÀN LED TƯƠNG TÁC - INTERACTIVE RGB LED TABLE

Ngoài chức năng hiển thị như LED thông thường, “BÀN LED TƯƠNG TÁC” còn giống như một "màn hình cảm ứng" được làm từ LED với các hiệu ứng rất đẹp mắt. Nó được dùng làm bàn ăn tối, bàn coffee, hay được trang trí ở quán bar,  làm bàn phòng khách …. Với dự án mình chia sẻ dưới đây, bàn LED tương tác này có kích thước khoảng khổ A2 với 256 RGB LED và dùng Led phát hồng ngoại - phototransistor để xác định vị trí của vật thể trên bàn và tạo ra hiệu ứng đẹp mắt tại các vị trí đó....

lên
6 thành viên đã đánh giá bài viết này hữu ích.