Bài này chúng ta sẽ lập trình cho LED đếm từ 0 đến 9
Trước tiên chúng ta sẽ lấy các font chữ cho các số từ 0 đến 9 nhé.
Giới thiệu phần mềm LCD font maker
Để lấy font dễ dàng thì sự giúp đỡ của phần mềm là cần thiết, các bạn hãy tải phần mềm này về tại đây
Mình đã tạo sẵn mã từ 0 đến 9. Chúng ta sẽ đưa vào mảng 2 chiều để lưu mã này !
1 2 3 4 5 6 7 8 9 10 11 12 13 |
unsigned char code maled[][8]= { 0x1C,0x22,0x32,0x2A,0x26,0x22,0x1C,0x00, //0 0x08,0x0C,0x08,0x08,0x08,0x08,0x1C,0x00, //1 0x1C,0x22,0x20,0x18,0x04,0x02,0x3E,0x00, //2 0x1C,0x22,0x20,0x1C,0x20,0x22,0x1C,0x00, //3 0x10,0x18,0x14,0x12,0x3E,0x10,0x10,0x00, //4 0x3E,0x02,0x02,0x1E,0x20,0x22,0x1C,0x00, //5 0x18,0x04,0x02,0x1E,0x22,0x22,0x1C,0x00, //6 0x3E,0x20,0x10,0x08,0x04,0x04,0x04,0x00, //7 0x1C,0x22,0x22,0x1C,0x22,0x22,0x1C,0x00, //8 0x1C,0x22,0x22,0x3C,0x20,0x10,0x0C,0x00, //9 }; |
Điều khiển LED ma trận bằng phương pháp ngắt TIMER
Trong hầu hết các lĩnh vực điều khiển có liên quan đến hiển thị, quét led. Người ta thường sử dụng ngắt để quét. Nó giống như 1 module tự động quét led và làm công việc hiển thị ra màn hình cho chúng ta. Trong hàm main ta có thể thoải mái làm những việc khác.
Tuy nhiên giữa hàm main và hàm ngắt phải có 1 mối liên hệ nhất định để hàm main muốn hiển thị gì thì ngắt phải nghe theo. Do vậy phương pháp này cần tới 1 Buffer ( bộ đệm hiển thị) – nó là 1 mảng dữ liệu chứa dữ liệu cần hiển thị. Khi hàm main muốn hiển thị gì thì chỉ cần ghi dữ liệu vào bộ đệm. Còn hàm ngắt sẽ tự động lấy dữ liệu trong bộ đệm ra để hiển thị 1 cách định kì và tự động.
Khởi tạo bộ đệm
Tùy vào số lượng led và cách điều khiển ta sẽ cần các bộ đệm có kích cỡ khác nhau. Ở ví dụ này, mình có con led ma trận 8×8, tức là 64 con led. Mỗi con led có 2 trạng thái ON và OF tương ứng 1 và 0. Như vậy mình sẽ cần 64 bit dữ liệu cho bộ đệm. 64 bit = 8 byte. Do vậy mình sẽ tạo 1 bộ đệm có 8 byte data :
1 |
unsigned char Buffer_display[8]; |
Thuật toán điều khiển của mình như sau:
1 bộ đệm được tạo ra để lưu trữ dữ liệu sáng hay tắt của led. Mỗi bit data tương ứng 1 led. 1 chương trình ngắt timer được gọi tự động sau 1 khoảng thời gian nào đó để lấy dữ liệu trong bộ đệm ra để xuất tín hiệu điều khiển ra LED. Đây là công việc tự động, do đó chúng ta chỉ cần xây dựng xong chương trình ngắt này thì có thế “bơ” nó luôn ! Công việc của ta là “vẽ” nhưng gì cần hiển thị vào bộ đệm hoy !
Xây dựng module ngắt
Module ngắt sẽ tự chạy vào bộ đệm, lấy dữ liệu và xuất ra 1 màn hình LED. Để làm được việc này, mình cần 1 cái timer, khi timer đếm đến 1 khoảng thời gian nào đó. Nó sẽ tự động nhảy vào hàm ngắt. Khoảng thời gian này quyết định tần số quét led của chúng ta. Như đã nói ở bài trước, tần số càng cao càng tốt. Nhưng cao quá thì chương trình chỉ lo nhảy vào ngắt mà bỏ bê hàm main, vì thể tùy theo tài nguyên, tốc độ MCU mà nên chọn tần số ngắt cho hợp lí nhé !
Mỗi lần ngắt tương ứng 1 lần quét hàng hoặc quét cột. Module matrix 8×8 của chúng ta quét 8 lần sẽ xong 1 bảng. Do vậy cần 8 lần ngắt cho khung hình. Mình muốn quét ở ~120 khung hình 1 giây. Nên tần số ngắt sẽ xấp xỉ 1 giây / (120*8) = 0.001s = 1ms
Như vậy mình sẽ nạp giá trị vào timer sao cho nó đếm đc 1ms thì tràn để nhảy vào hàm ngắt
Mình sẽ dùng timer1 của vi điều khiển 89s52, thạch anh 12Mhz qua bộ chia 12 còn 1Mhz => 1 xung sẽ đếm trong 1us => cần 1000 xung để đếm được 1ms. Do cần tới 1000 xung nên mình sẽ cài timer ở chế độ 16bit và nạp giá trị cho thanh ghi TH1 và TL1 là 64535 = 0xFC17
1 2 3 4 5 |
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 |
OK bây giờ xây dựng hàm ngắt
1 2 3 4 5 6 7 8 9 10 |
void T1_ISR() interrupt 3 // Dung timer 1 de quet led matrix { static unsigned char z ; // Bien dem P3=0xFF; //tat het LED P2=Buffer_display[z]; //lay du lieu trong bo dem hien thi ra man hinh P3=maquet[z]; //cho phep LED sang z++;if(z==8)z=0; TH1=0xFC; // Nap gia tri cho TH1 TL1=0x17; // Nap gia tri cho TL1 } |
Hãy nói qua 1 chút về hàm ngắt này nào !
Đầu tiến biến z là 1 biến con chạy từ 0 đến 7. Do chúng ta quét 8 lần cho 1 khung hình nên chỉ cần nó chạy từ 0 đến 7 thôi. Cách khai báo static giống như khai báo biến toàn cục nhưng chỉ hàm ngắt mới được dùng !
Tiếp đó, tắt hết led đi ( trước khi xuất dữ liệu luôn luôn phải tắt led đi nhé – nếu không sẽ xuất hiện hiện tượng bóng ma, thử đi là biêt :v)
Sau đó lấy dữ liệu trong bộ đệm để hiển thị ra ngoài qua cổng P2
Tiếp đó cho phép LED sáng bằng cách xuất dữ liệu ra công P3
Sau đó tăng biến con chạy z lên để lần ngắt tiếp nó sẽ quét vào dòng tiếp
- z=0 quét hàng 1
- z=1 quét hàng 2
- z=2 quét hàng 3
- z=3 quét hàng 4
- z=4 quét hàng 5
- z=5 quét hàng 6
- z=6 quét hàng 7
- z=7 quét hàng 8
- z=8 đã quét xong 1 khung hình, quay trở lại từ đầu
Do chúng ta quét theo hàng và mỗi lần ngắt quét 1 hàng, tổng số hàng cần quét là 8. Nên phương pháp quét này còn gọi là quét 1/8
Cuối hàm mình nạp lại giá trị vào thanh TH1 TL1 để đảm bảo mỗi lần ngắt cách nhau 1ms
OK ! Thế là chương trình ngắt (module hiển thị) đã xây dựng xong, chúng ta có thể bơ nó luôn không cần quan tâm gì nữa. Từ giờ là phần việc của hàm main, việc lập trình bây giờ gần như là lập trình C thuần túy (làm việc với dữ liệu), dó đó các bạn có thể tạo thành 1 thư viện riêng để sử dụng sau này mà không cần quan tâm tới việc chúng ta sẽ dùng vi điều khiển nào !
Bây giờ muốn hiện thị cái gì, chỉ cần bắn dữ liệu vào bộ đệm, trước đó chúng ta đã có bộ font từ 0 đến 9. Giờ hãy xây dựng hàm lấy font ra để bắn vào bộ đệm nhé
1 2 3 4 5 6 7 8 |
void print_number(char number) { int i; for(i=0;i<8;i++) { Buffer_display[i]=maled[number][i]; } } |
Hàm trên chỉ đơn giản là nhảy vào mảng dữ liệu font từ 0 đến 9 để bắn 8 byte data vào bộ đệm
Giờ muốn hiển thị số mấy thì chỉ cần gọi hàm kèm theo số cần hiện trong hàm main
Ví dụ, mình muốn matrix hiện số 2
print_number(2);
Còn muốn đếm từ 0 đến 9 thì sao
1 2 3 4 5 |
for(i=0;i<10;i++) { print_number(i); delay(500); } |
Full code cho project đếm từ 0 đến 9 led matrix 8×8
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 |
#include <REGX52.H> unsigned char code maquet[8]={0xFE,0xFD,0xFB,0xF7,0xEF,0xDF,0xBF,0x7F}; unsigned char code maled[][8]= { 0x1C,0x22,0x32,0x2A,0x26,0x22,0x1C,0x00, //0 0x08,0x0C,0x08,0x08,0x08,0x08,0x1C,0x00, //1 0x1C,0x22,0x20,0x18,0x04,0x02,0x3E,0x00, //2 0x1C,0x22,0x20,0x1C,0x20,0x22,0x1C,0x00, //3 0x10,0x18,0x14,0x12,0x3E,0x10,0x10,0x00, //4 0x3E,0x02,0x02,0x1E,0x20,0x22,0x1C,0x00, //5 0x18,0x04,0x02,0x1E,0x22,0x22,0x1C,0x00, //6 0x3E,0x20,0x10,0x08,0x04,0x04,0x04,0x00, //7 0x1C,0x22,0x22,0x1C,0x22,0x22,0x1C,0x00, //8 0x1C,0x22,0x22,0x3C,0x20,0x10,0x0C,0x00, //9 }; unsigned char Buffer_display[8]; 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 T1_ISR() interrupt 3 // Dung timer 1 de quet led matrix { static unsigned char z ; // Bien dem P3=0xFF; //tat het LED P2=Buffer_display[z]; //lay du lieu trong bo dem hien thi ra man hinh P3=maquet[z]; //cho phep LED sang z++;if(z==8)z=0; TH1=0xFC; // Nap gia tri cho TH1 TL1=0x17; // Nap gia tri cho TL1 } void print_number(char number) { int i; for(i=0;i<8;i++) { Buffer_display[i]=maled[number][i]; } } void main() { int i; 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) { for(i=0;i<10;i++) { print_number(i); delay(500); } } } |
Tổng kết
Có thể bạn sẽ thấy có cái project đếm từ 0 đến 9 thôi mà code dài hơn so với code mẫu khác trên mạng rất nhiều. Nhưng sau này làm tới các project phức tạp sử dụng led ma trận như đồng hồ led có các hiệu ứng đẹp. Các bạn bắt buộc phải dùng tới kĩ thuật quét LED này. Do vậy mình giới thiệu sớm và từ nay đến hết tutorial về LED ma trận mình đều code sử dụng kĩ thuật này
Đây cũng chính là kĩ thuật dùng trong hầu hết các thiết bị hiển thị, card đồ họa
Tham khảo Frame Buffer tại Wikipedia
Các bạn có thể tải file mô phỏng và code cho project này Tại đây
Attractive section of content. I just stumbled upon your weblog and in accession capital to assert that I get in fact enjoyed account your blog posts. Anyway I’ll be subscribing to your feeds and even I achievement you access consistently rapidly.
Nếu để hiệu ứng trôi hoặc lật trang thì dùng lệnh nào vậy anh ơi
Dùng pic16F877 ĐC K BÁC