Chỉ cần thêm vài dòng code từ bài 3 là có ngay hiệu ứng chạy chữ từ phải sang trái trên led ma trận 8×32. Gần như mọi thứ cần thiết ta đã học ở bài 3 rồi
1 2 3 4 5 6 |
for(x=31;x>-250;x--) { Matrix_chonvitri(x,1); Matrix_guichuoi("IoT47.com - Tu hoc lap trinh, dien tu !!!"); delay(20); } |
FULL CODE
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
#include <REGX52.H> #define DAT P2_0 #define LAT P2_1 #define SCK P2_2 int MatrixX,MatrixY; unsigned char code maquet[8]={0xFE,0xFD,0xFB,0xF7,0xEF,0xDF,0xBF,0x7F}; unsigned char code font[][7]= { 0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 32 0x10,0x38,0x38,0x10,0x10,0x00,0x10, //! 33 0x6C,0x6C,0x48,0x00,0x00,0x00,0x00, //" 34 0x00,0x28,0x7C,0x28,0x28,0x7C,0x28, //# 35 0x20,0x38,0x40,0x30,0x08,0x70,0x10, //$ 36 0x64,0x64,0x08,0x10,0x20,0x4C,0x4C, //% 37 0x20,0x50,0x50,0x20,0x54,0x48,0x34, //& 38 0x30,0x30,0x20,0x00,0x00,0x00,0x00, //' 39 0x10,0x20,0x20,0x20,0x20,0x20,0x10, //( 40 0x20,0x10,0x10,0x10,0x10,0x10,0x20, //) 41 0x00,0x28,0x38,0x7C,0x38,0x28,0x00, //* 42 0x00,0x10,0x10,0x7C,0x10,0x10,0x00, //+ 43 0x00,0x00,0x00,0x00,0x00,0x30,0x30, //, 44 0x00,0x00,0x00,0x7C,0x00,0x00,0x00, //- 45 0x00,0x00,0x00,0x00,0x00,0x30,0x30, //. 46 0x00,0x04,0x08,0x10,0x20,0x40,0x00, /// 47 0x38,0x44,0x4C,0x54,0x64,0x44,0x38, //0 48 0x00,0x04,0x08,0x10,0x20,0x40,0x00, //1 49 0x38,0x44,0x04,0x18,0x20,0x40,0x7C, //2 50 0x38,0x44,0x04,0x38,0x04,0x44,0x38, //3 51 0x08,0x18,0x28,0x48,0x7C,0x08,0x08, //4 52 0x7C,0x40,0x40,0x78,0x04,0x44,0x38, //5 53 0x18,0x20,0x40,0x78,0x44,0x44,0x38, //6 54 0x7C,0x04,0x08,0x10,0x20,0x20,0x20, //7 55 0x38,0x44,0x44,0x38,0x44,0x44,0x38, //8 56 0x38,0x44,0x44,0x3C,0x04,0x08,0x30, //9 57 0x00,0x00,0x30,0x30,0x00,0x30,0x30, //: 58 0x00,0x00,0x30,0x30,0x00,0x30,0x30, //; 59 0x08,0x10,0x20,0x40,0x20,0x10,0x08, //< 60 0x00,0x00,0x7C,0x00,0x00,0x7C,0x00, //= 61 0x20,0x10,0x08,0x04,0x08,0x10,0x20, //> 62 0x38,0x44,0x04,0x18,0x10,0x00,0x10, //? 63 0x38,0x44,0x5C,0x54,0x5C,0x40,0x38, //@ 64 0x38,0x44,0x44,0x44,0x7C,0x44,0x44, //A 65 0x78,0x44,0x44,0x78,0x44,0x44,0x78, //B 66 0x38,0x44,0x40,0x40,0x40,0x44,0x38, //C 67 0x78,0x44,0x44,0x44,0x44,0x44,0x78, //D 68 0x7C,0x40,0x40,0x78,0x40,0x40,0x7C, //E 69 0x7C,0x40,0x40,0x78,0x40,0x40,0x40, //F 70 0x38,0x44,0x40,0x5C,0x44,0x44,0x3C, //G 71 0x44,0x44,0x44,0x7C,0x44,0x44,0x44, //H 72 0x38,0x10,0x10,0x10,0x10,0x10,0x38, //I 73 0x04,0x04,0x04,0x04,0x44,0x44,0x38, //J 74 0x44,0x48,0x50,0x60,0x50,0x48,0x44, //K 75 0x40,0x40,0x40,0x40,0x40,0x40,0x7C, //L 76 0x44,0x6C,0x54,0x44,0x44,0x44,0x44, //M 77 0x44,0x64,0x54,0x4C,0x44,0x44,0x44, //N 78 0x38,0x44,0x44,0x44,0x44,0x44,0x38, //O 79 0x78,0x44,0x44,0x78,0x40,0x40,0x40, //P 80 0x38,0x44,0x44,0x44,0x54,0x48,0x34, //Q 81 0x78,0x44,0x44,0x78,0x48,0x44,0x44, //R 82 0x38,0x44,0x40,0x38,0x04,0x44,0x38, //S 83 0x7C,0x10,0x10,0x10,0x10,0x10,0x10, //T 84 0x44,0x44,0x44,0x44,0x44,0x44,0x38, //U 85 0x44,0x44,0x44,0x44,0x44,0x28,0x10, //V 86 0x44,0x44,0x54,0x54,0x54,0x54,0x28, //W 87 0x44,0x44,0x28,0x10,0x28,0x44,0x44, //X 88 0x44,0x44,0x44,0x28,0x10,0x10,0x10, //Y 89 0x78,0x08,0x10,0x20,0x40,0x40,0x78, //Z 90 0x38,0x20,0x20,0x20,0x20,0x20,0x38, //[ 91 0x00,0x40,0x20,0x10,0x08,0x04,0x00, //\ 92 0x38,0x08,0x08,0x08,0x08,0x08,0x38, //] 93 0x10,0x28,0x44,0x00,0x00,0x00,0x00, //^ 94 0x00,0x00,0x00,0x00,0x00,0x00,0x7C, //_ 95 0x30,0x30,0x10,0x00,0x00,0x00,0x00, //` 96 0x00,0x00,0x38,0x04,0x3C,0x44,0x3C, //a 97 0x40,0x40,0x78,0x44,0x44,0x44,0x78, //b 98 0x00,0x00,0x38,0x44,0x40,0x44,0x38, //c 99 0x04,0x04,0x3C,0x44,0x44,0x44,0x3C, //d 100 0x00,0x00,0x38,0x44,0x78,0x40,0x38, //e 101 0x18,0x20,0x20,0x78,0x20,0x20,0x20, //f 102 0x00,0x3C,0x44,0x44,0x3C,0x04,0x38, //g 103 0x40,0x40,0x70,0x48,0x48,0x48,0x48, //h 104 0x10,0x00,0x10,0x10,0x10,0x10,0x18, //i 105 0x08,0x00,0x18,0x08,0x08,0x48,0x30, //j 106 0x40,0x40,0x48,0x50,0x60,0x50,0x48, //k 107 0x20,0x20,0x20,0x20,0x20,0x20,0x38, //l 108 0x00,0x00,0x68,0x54,0x54,0x44,0x44, //m 109 0x00,0x00,0x70,0x48,0x48,0x48,0x48, //n 110 0x00,0x00,0x38,0x44,0x44,0x44,0x38, //o 111 0x00,0x78,0x44,0x44,0x44,0x78,0x40, //p 112 0x00,0x3C,0x44,0x44,0x44,0x3C,0x04, //q 113 0x00,0x00,0x58,0x24,0x20,0x20,0x70, //r 114 0x00,0x00,0x38,0x40,0x38,0x04,0x38, //s 115 0x00,0x20,0x78,0x20,0x20,0x28,0x10, //t 116 0x00,0x00,0x48,0x48,0x48,0x58,0x28, //u 117 0x00,0x00,0x44,0x44,0x44,0x28,0x10, //v 118 0x00,0x00,0x44,0x44,0x54,0x7C,0x28, //w 119 0x00,0x00,0x48,0x48,0x30,0x48,0x48, //x 120 0x00,0x48,0x48,0x48,0x38,0x10,0x60, //y 121 0x00,0x00,0x78,0x08,0x30,0x40,0x78, //z 122 0x18,0x20,0x20,0x60,0x20,0x20,0x18, //{ 123 0x10,0x10,0x10,0x10,0x10,0x10,0x10, //| 124 0x30,0x08,0x08,0x0C,0x08,0x08,0x30, //} 125 0x00,0x00,0x28,0x50,0x00,0x00,0x00, //~ 126 }; unsigned char Buffer_display[8][4]; void delay(unsigned int t) //hàm delay { unsigned int x,y; for(x=0;x<t;x++) { for(y=0;y<123;y++); } } void hc595_out(unsigned char byte) { int i; for(i=0;i<8;i++) { DAT = byte & (0x80>>i); SCK=0;SCK=1; } } void T1_ISR() interrupt 3 // Dung timer 1 de quet led matrix { static unsigned char z ; // Bien dem P3=0xFF; //tat het LED hc595_out(Buffer_display[z][0]); //lay du lieu trong bo dem hien thi ra man hinh matrix 1 hc595_out(Buffer_display[z][1]); //lay du lieu trong bo dem hien thi ra man hinh matrix 2 hc595_out(Buffer_display[z][2]); //lay du lieu trong bo dem hien thi ra man hinh matrix 3 hc595_out(Buffer_display[z][3]); //lay du lieu trong bo dem hien thi ra man hinh matrix 4 LAT=0;LAT=1; //chot du lieu P3=maquet[z]; //cho phep LED sang z++;if(z==8)z=0; TH1=0xF8; // Nap gia tri cho TH1 TL1=0x2F; // Nap gia tri cho TL1 } void Matrix_chonvitri(int x,int y) { MatrixX=x; MatrixY=y; } void Matrix_setpx(int x,int y,char color) { if(x>31 || y>7 || x<0 || y < 0)return; if(color)Buffer_display[y][x/8] |= (0x80 >> (x%8)); else Buffer_display[y][x/8] &= ~(0x80 >> (x%8)); } void Matrix_guikitu(unsigned char txt) { int x,y; if(6+MatrixX < 0){MatrixX+=6;return;} for(y=MatrixY;y<7+MatrixY;y++) { for(x=MatrixX;x<6+MatrixX;x++) { if ( (font[txt-32][y-MatrixY] & (0x80>>(x-MatrixX))) != 0)Matrix_setpx(x,y,1); else Matrix_setpx(x,y,0); } } MatrixX+=6; //sau khi in xong thi tang con tro vi tri len } void Matrix_guichuoi(unsigned char *s) { while(*s) { if(MatrixX>31)return; Matrix_guikitu(*s); s++; } } void Matrix_Clear() { int i; for(i=0;i<8;i++) { Buffer_display[i][0]=0; Buffer_display[i][1]=0; Buffer_display[i][2]=0; Buffer_display[i][3]=0; } } void main() { int x; TMOD=0x10; // khoi tao ngat T1, 16bit ET1=1; // cho phep ngat T1 TF1=0; // xoa co ngat T1 TR1=1; // khoi dong T1 EA = 1; // cho phep ngat toan cuc while(1) { Matrix_Clear(); for(x=31;x>-250;x--) { Matrix_chonvitri(x,1); Matrix_guichuoi("IoT47.com - Tu hoc lap trinh, dien tu !!!"); delay(20); } } } |
Hoặc tải file tại đây
Làm tương tự với hiệu ứng chạy từ trên xuống, dưới lên, trái sang phải, xiên góc nhé !
Xé màn hình
Nếu để ý kĩ clip trên, bạn sẽ có cảm giác hình ảnh hiển thị hơi “nát nát”. Đó chính là do màn hình đã dính phải hiện tượng xé màn hình.
Screen tearing là hiện tượng mà màn hình trông như bị vỡ, nát, xé toạc ra do nó phải hiện thị lẫn lộn thông tin giữa khung hình hiện tại và khung hình trước đó. Điều này xảy ra do tốc độ render dữ liệu để ghi vào Buffer display ( Buffer frame) xảy ra chậm, dẫn tới bất đồng bộ giữa hàm ngắt hiển thị ( đảm nhiệm công việc đọc dữ liệu để xuất ra màn hình) và hàm main ( đảm nhiệm cộng việc tính toán, xử lí dữ liệu).
Khắc phục: Cách khắc phục phổ biến là sử dụng nhiều bộ đệm hiển thị Multiple buffering (hãy click vào các liên kết wikipedia mình trích dẫn mà tham khảo nhé, rất hữu ích đó)
Việc sử dụng thêm bộ đệm đương nhiên sẽ làm hao phí thêm rất nhiều tài nguyên RAM của vi điều khiển, tùy vào lượng ram cần thiết hãy tính toán chọn vi điều khiển cho hợp lí nhé. Bây giờ mình sẽ tạo thêm 1 Buffer nữa
1 |
unsigned char Buffer_display2[8][4]; |
Bây giờ, hàm main sẽ thao tác lên Buffer_display còn hàm ngắt timer để hiển thị sẽ thao tác lên Buffer_display2
Do vậy hàm ngắt sẽ được viết lại như này
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
void T1_ISR() interrupt 3 // Dung timer 1 de quet led matrix { static unsigned char z ; // Bien dem P3=0xFF; //tat het LED hc595_out(Buffer_display2[z][0]); //lay du lieu trong bo dem hien thi ra man hinh matrix 1 hc595_out(Buffer_display2[z][1]); //lay du lieu trong bo dem hien thi ra man hinh matrix 2 hc595_out(Buffer_display2[z][2]); //lay du lieu trong bo dem hien thi ra man hinh matrix 3 hc595_out(Buffer_display2[z][3]); //lay du lieu trong bo dem hien thi ra man hinh matrix 4 LAT=0;LAT=1; //chot du lieu P3=maquet[z]; //cho phep LED sang z++;if(z==8)z=0; TH1=0xF8; // Nap gia tri cho TH1 TL1=0x2F; // Nap gia tri cho TL1 } |
Sau khi các hàm render dữ liệu trong hàm main làm xong nhiệm vụ. Mình sẽ COPY toàn bộ dữ liệu trong Buffer_display sang Buffer_display2
1 2 3 4 5 6 7 8 9 10 11 |
void Matrix_update() { int i; for(i=0;i<8;i++) { Buffer_display2[i][0]=Buffer_display[i][0]; Buffer_display2[i][1]=Buffer_display[i][1]; Buffer_display2[i][2]=Buffer_display[i][2]; Buffer_display2[i][3]=Buffer_display[i][3]; } } |
Và giờ mỗi khi render dữ liệu xong mình sẽ cho gọi hàm update để copy data. Ví dụ hàm chạy chữ ở bên trên sẽ sửa thành thế này !
1 2 3 4 5 6 7 |
for(x=31;x>-250;x--) { Matrix_chonvitri(x,1); Matrix_guichuoi("IoT47.com - Tu hoc lap trinh, dien tu !!!"); Matrix_update(); //COPY data to Buffer 2 delay(20); } |
Do việc copy dữ liệu diễn ra rất nhanh, nhanh hơn các hàm render dữ liệu nhiều nên hiệu ứng xé màn hình gần như biến mất. Tuy nhiên có thể khắc phục triệt để hơn bằng kĩ thuật lật trang (trong link wiki mình trích bên trên có nói đó nhé)
Thay vì 1 bộ đệm dành cho hàm main, 1 bộ đệm dành cho hàm ngắt hiển thị, sau khi render xong thì copy sang. Thì ở phương pháp lật trang, cả 2 bộ đệm sẽ được sử dụng luôn phiên thay thế nhau. Đầu tiên bộ đệm 1 dùng để hiển thị, bộ đệm 2 được “vẽ”. Sau đó lúc cần update thì chỉ việc “đóng cầu dao” để đảo ngược nhiệm vụ của 2 bộ đệm.
Rõ ràng việc “đóng cầu dao” nhanh hơn rất nhiều việc COPY dữ liệu từ bộ đệm này sang bộ đệm kia. Thậm chí có thể loại bỏ hoàn toàn hiện tượng xé màn hình nếu lật trang tại thời điểm”vàng” Vertical blanking interval ( thời điểm mà hàm ngắt hiển thị “nghỉ ngơi” )
[Matrix LED] Bài 6: Đồng hồ matrix 8×40 hiệu ứng lật trang đơn giản
hello!,I love your writing very so much! share we communicate extra about your post on AOL? I require an expert on this area to resolve my problem. May be that’s you! Having a look forward to look you.
Làm với Pic16f877a thì sao ạ?