Nạp chương trình

Một phần của tài liệu Lập trình 8051 siêu chi tiết (Trang 34)

Bước 1 : Kết nối mạch nạp với board thông qua jack 4, với chân số 1 là VCC, chân 2 là GND, chân 3 là TX và chân 4 là RX.

Bước 2 : Gắn nguồn cho kit thí nghiệm. Bước 3 : Khởi động Flash Magic.

Bước 4 : Chọn menu ISP và chọn Erase Flash Pages để xoá code cũ trong 89V51RB2. Bạn cần phải làm bước này trước khi nạp chương trình cho các dòng 8051 không tự xoá. Màn hình sau sẽ hiện lên :

Bạn dùng thêm tổ hợp phím Shift để có thể chọn được nhiều Pages, và nhấn Erase. Khi màn hình sau hiện lên, bạn nhấn nút Reset trên board để tiếp tục (nên nhấn và giữ trong khoảng 2 giây).

BKIT HARDWARE CLUB www.bkit4u.com 35 Bước 6 : Nhấn Browse để chọn đường dẫn tới file Hex cần nạp rồi nhấn Start.

Bước 7 : Khi thấy Status là Finished, bạn có thể nhấn nút Reset trên board để bắt đầu chạy chương trình của bạn.

2.2.4 Các lỗi xảy ra khi nạp chương trình

Dưới đây là 1 số lỗi phổ biến khi nạp chương trình. Kinh nghiệm để tìm lỗi là kiểm tra qua hết các lỗi thông dụng dưới đây hoặc dựa vào các thông báo lỗi để phán đoán.

Để khắc phục lỗi này, bạn chọn vào menu Opition/ Advance Option , chọn qua tab Hardware Config và check chọn Assert DTR and RTS while COM Port open. Nhấn OK để đóng cửa số này lại.

Chọn sai cổng COM

Chọn sai tốc độ Baud

Ở lỗi này, Flash Magic hiển thị thông báo yêu cầu bạn nhấn reset, nhưng khi nhấn reset thì thông báo trên xuất hiện, bạn chỉnh lại tốc độ Baud là 9600.

BKIT HARDWARE CLUB www.bkit4u.com 37

Chọn sai chip

Ở lỗi này, Flash Magic cũng chạy được tới phần chờ nhấn nút Reset, sau khiấn nút Reset thì thông báo này xuất hiện.

Xung đột ở Port 3

Lỗi này xuất hiện là do người lập trình can thiệp đến Port 3, nên Flash Magic không thể giao tiếp được với vi điều khiển để nạp chương trình. Khi bị lỗi này thông báo sau đây sẽ xuất hiện (tương tự như thông báo chọn sai cổng COM) :

Để khắc phục lỗi này, trước khi nạp bạn nhấn đè nút Reset, mọi thao tác vẫn diễn ra bình thường cho đến khi cửa sổ yêu cầu bạn nhấn nút Reset, bạn giữa thêm vài giây rồi thả ra. Cách thứ 2 là rút nguồn của board ra, cho đến khi Flash Magic yêu cầu bạn nhấn Reset thì cắm nguồn vào.

Nếu như đã kiểm tra qua hết các lỗi kia mà chương trình vẫn chưa nạp được, thì nguyên nhân có thể là bạn cắm ngược chiều mạch nạp, phần mềm Flash Magic bị lỗi, IC MAX 232 bị hư hoặc hư vi điều khiển.

Bài 3 : Điều khiển đơn giản Led 7 đoạn

Mục đích:

Nắm nguyên lý điều khiển led 7 đoạn.

Yêu cầu:

Viết chương trình nhấn nút 1 sẽ hiển thị số 0 ở led 7 đoạn ngoài cùng, nút 2 sẽ hiện thị số 1ở led 7 đoạn kế tiếp, v.v…

3.1 Kết nối phần cứng:

Gạt switch 2 lên ON để kích hoạt Port 3 là các nút nhấn. Gạt switch 4 lên ON để kích hoạt Led 7 đoạn.

Port 2 dùng để chọn led 7 đoạn nào sẽ được sáng và Port 0 là dữ liệu cho led 7 đoạn đó. P0 : Data P2 : Select P0.0 : a P2.0 : Led 1 P0.1 : b P2.0 : Led 2 P0.2 : c P2.0 : Led 3 P0.3 : d P2.0 : Led 4 P0.4 : e P2.0 : Led 5 P0.5 : f P2.0 : Led 6 P0.6 : g P2.0 : Led 7 P0.7 : dot P2.0 : Led 8

BKIT HARDWARE CLUB www.bkit4u.com 39 Với kết nối phần cứng như vậy, để hiện thị số 5 thì dữ liệu xuất ra sẽ là 0x6D

(0110 1101). Tương tự, giá trị cho các số từ 0 đến 9 sẽ là :

unsigned char led7_data[10] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F}; 3.2 Viết chương trình

3.2.1 Hàm init_main() (adsbygoogle = window.adsbygoogle || []).push({});

Do đăc điểm của phần cứng, trong hàm main, bạn phải dùng thêm hàm init_main, để gán các Port 0,1,2 với giá trị 0x00.

void init_main() { P0 = 0x00; P2 = 0x00; P1 = 0x00; }

Các chương trình sau này sẽ đều có hàm này.

3.2.2 Hàm main()

Hàm này sẽ gọi init_main(), sau đó xét các giá trị của Port 3 và hiển thị ra led 7 đoạn giá trị tương ứng.

void main() { //P0 : Data P2 : Select //P0.0 : a P2.0 : Led 1 //P0.1 : b P2.0 : Led 2 //P0.2 : c P2.0 : Led 3 //P0.3 : d P2.0 : Led 4

//P0.4 : e P2.0 : Led 5

//P0.5 : f P2.0 : Led 6

//P0.6 : g P2.0 : Led 7

//P0.7 : dot P2.0 : Led 8

unsigned char led7_data[10] =

{0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F}; init_main(); while(1) { if((P3 & 0x01) == 0) // P3 = 1111 1110 { P0 = led7_data[0]; P2 = 0x01; }

else if((P3 & 0x02) == 0)// P3 = 1111 1101 {

P0 = led7_data[1]; P2 = 0x02;

}

else if((P3 & 0x04) == 0)// P3 = 1111 1011 {

P0 = led7_data[2]; P2 = 0x04;

}

else if((P3 & 0x08) == 0) // P3 = 1111 0111 {

P0 = led7_data[3]; P2 = 0x08;

}

else if((P3 & 0x10) == 0) // P3 = 1110 1111 {

P0 = led7_data[4]; P2 = 0x10;

}

else if((P3 & 0x20) == 0) // P3 = 1101 1111 {

P0 = led7_data[5]; P2 = 0x20;

}

else if((P3 & 0x40) == 0) // P3 = 1011 1111 {

BKIT HARDWARE CLUB www.bkit4u.com 41

P2 = 0x40; } (adsbygoogle = window.adsbygoogle || []).push({});

else if((P3 & 0x80) == 0) // P3 = 0111 1111 { P0 = led7_data[7]; P2 = 0x80; } else { P0 = 0x00; P2 = 0x00; } } }

Các bước dịch và nạp chương trình bạn xem lại ở bài trước. Code mẫu của bài thí nghiệm này bạn có thể tham khảo ở thư mục Bài 3 đính kèm trong CD này.

3.3 Một số lỗi lập trình quan trọng

Lỗi gọi sai hàm : Chẳng hạn bạn định nghĩa hàm là init_main(), nhưng trong hàm main.c bạn lại gọi là initmain() (thiếu dấu _ ), thì khi nhấn F7 compile chương trình, file hex vẫn được tạo ra, nhưng khi nạp vào chương trình chạy sai. Bạn nên lưu ý các thông báo của compiler lúc dịch, sẽ có thông báo sau :

Khi gọi sai tên 1 hàm, compiler của KeilC sẽ vẫn tạo ra file hex, bạn nên lưu ý các warning để khắc phục lỗi.

Không gọi hàm đã được định nghĩa : Để 1 module có thể dùng lại, thông thường ta hiện thực nhiều hàm liên quan đến nó, nhưng trong 1 ứng dụng không gọi hết tất cả các hàm đó, sẽ có thông báo về trường hợp này. Tuy nhiên đây không phải là lỗi, file hex được tạo ra vẫn sẽ chạy đúng. Thông báo dạng này như sau :

Như thông báo ở trên, hàm start_timer0()start_timer1() không được gọi trong bất kì hàm nào.

Bài 4 : Ngắt Timer

Mục đích:

Tìm hiểu ngắt timer của 89V51

Xây dựng module dành cho timer của 89V51 có thể dùng lại được.

Yêu cầu:

Viết chương trình hiển thị từ 0, sau 1 giây tăng lên 1, khi tăng đến 9 thì chuyển sang hiển thị ở led kế tiếp, hiển thị bắt đầu lại từ 0.

4.1 Khai báo ngắt timer

Trình tự là cho phép ngắt toàn cục, cho phép ngắt timer và thiết lập chế độ hoạt động cho timer:

IE = 0x80; //enable global interrupt IE &= ~(0x02);// enable timer 0 IE |= 0x02;

TMOD &= ~(0x01);// setup mode for timer 0 TMOD |= 0x01;

TH0 = (-10000/256); //10 ms TL0 = (-10000%256);

TR0 = 1;//start timer0

Hàm phục vụ ngắt sẽ được khai báo như sau:

void timer0_isr() interrupt 1 {

TR0 = 0;//stop timer 0 // reinitialize

TH0 = (-10000/256); TL0 = (-10000%256); //Add your code here

TR0 = 1;//start timer 0 }

BKIT HARDWARE CLUB www.bkit4u.com 43

4.2 Viết chương trình

Vì mục đích là tạo ra module Timer có khả năng dùng lại, nên chương trình sẽ gồm group TIMER có 2 file timer.h và timer.c. File timer.h đặc tả các hàm của module này còn file timer.c sẽ hiện thực các hàm đó. Các hàm trong timer.h bao gồm:

void init_timer0(); //init timer 0, interrupt after 10ms void init_timer1(); //init timer 1, interrupt after 1ms void start_timer0();

void start_timer1(); void stop_timer0(); void stop_timer1();

void delay_ms(unsigned int duration);//using timer1 to delay in ms (adsbygoogle = window.adsbygoogle || []).push({});

4.2.1 Hàm init_timer1()

Hàm này làm nhiệm vụ khởi tạo ngắt timer1 sau mỗi 1ms, khi khởi tạo ngắt timer1 xong, ta phải hiện thực hàm phục vụ ngắt quãng cho nó, nếu không chương trình sẽ chạy sai.

void init_timer1() {

IE &= ~(0x08);// enable timer 1 IE |= 0x08;

TMOD &= ~(0x10);// setup mode for timer 1 TMOD |= 0x10; TH1 = (-1000/256); //1 ms TL1 = (-1000%256); TR1 = 1; //start timer1 } 4.2.2 Hàm start_timer1() void start_timer1() { TR1 = 1; } 4.2.3 Hàm timer1_isr()

void timer1_isr() interrupt 3 { TR1 = 0;//stop timer 1 // reinitialize TH1 = (-1000/256); TL1 = (-1000%256); //Add your code here if(counter1!= 0) {

counter1--; }

//End your code TR1 = 1;//start timer 1 }

4.2.4 Hàm delay_ms(unsigned int duration)

Hàm này chỉ cần cài đặt giá trị cho biến counter1, giá trị này sẽ tự động giảm nhờ đoạn code trong hàm phục vụ ngắt quãng của timer1. Vòng lặp while để chờ cho đến khi counter1 bằng 0.

void delay_ms(unsigned int duration) { counter1 = duration; while(counter1 !=0) { } } 4.2.5 Hàm stop_timer1() void stop_timer1() { TR1 = 0; }

BKIT HARDWARE CLUB www.bkit4u.com 45

Bài 5 : Dùng ngắt timer viết ứng dụng LED RIVER

Mục đích:

Nắm vững ngắt timer.

Sử dụng ngắt timer để viết ứng dụng LED RIVER.

Yêu cầu:

Viết chương trình led river đơn giản, 1 led chạy từ P0.0 sang P1.1, đến P0.7 chuyển sang P3.7, P3.6. Khi tới P3.0 thì trở về P0.0. Thời gian chuyển qua trạng thái mới là 1s

5.1 Kết nối phần cứng

Gạt switch 1 lên ON để kích hoạt Port 1 (các led đơn).

Gạt switch 2 lên ON để kích hoạt Port 3 (các led đơn và nút nhấn).

5.2 Viết chương trình

Đối với led chạy như yêu cầu, ta thấy có 16 trạng thái của P1 và P3 như sau P0.0 sáng : P1 = 0x01, P3 = 0x00 P0.1 sáng : P1 = 0x02, P3 = 0x00 …… P0.7 sáng : P1 = 0x80, P3 = 0x00 P3.7 sáng : P1 = 0x00, P3 = 0x80 P3.6 sáng : P1 = 0x00, P3 = 0x40 …… P3.0 sáng : P1 = 0x00, P3 = 0x01

Như vậy, ta sẽ khai báo 2 mảng, mỗi mảng 16 phần tử cho P1 và P3, rồi cho 1 biến index tự động tăng lên sau 1s, khi index bằng 16 thì gán lại index bằng 0, để quá trình được lặp lại.

Đối với led river có nhiều trạng thái hơn, bạn chỉ cần khai báo ra tất cả các trạng thái vào 1 mảng, rồi lần lượt định kì xuất nó ra.

Việc delay giữa các trạng thái, ta sẽ sử dụng lại hàm delay_ms(unsigned int duration) đã làm ở bài thí nghiệm trước.

Đối với yêu cầu của bài led river này, chúng tôi chỉ sử dụng 1 mảng 16 phần tử dùng cho cả 2 Port. Index của P1 sẽ tăng dần từ 0 đến 15, Index của P3 sẽ giảm dần từ 15 đến 0. //led_river : mảng 16 phần tử //INDEX_MAX = 16 //duration = 1000ms while(1) { P3 = led_river[INDEX_MAX - index-1]; P1 = led_river[index++]; if(index == INDEX_MAX) index = 0;

delay_ms(duration); }

Chi tiết code của bài thí nghiệm này, bạn xem thêm trong thư mục Bài 5 trong CD đi kèm.

BKIT HARDWARE CLUB www.bkit4u.com 47 (adsbygoogle = window.adsbygoogle || []).push({});

Bài 6 : Chống rung cho phím

Mục đích:

Nắm vững kĩ thuật chống rung cho phím.

Yêu cầu:

Viết chương trình xuất số đếm ra led 7 đoạn với giá trị khởi tạo là 0. Khi nhấn nút 1 (được nối với P3.0) thì giá trị này tăng lên 1, tăng đến 9 thì quay trở lại 0.

6.1 Nguyên lý chống rung

Hình trên minh hoạ mức điện áp của 1 phím nhấn tích cực mức 0, ở trạng thái bình thường, điện áp vi điều khiển nhận vào là 5V còn khi nhấn là 0V. Tuy nhiên, do độ rung cơ học của phím, tại thời điểm vừa nhấn xuống, điện áp sẽ không ổn định trong 1 khoảng thời gian, trước khi ổn định ở mức 0V. Hiệu tượng này gọi là rung phím.

Mặc dù khoảng thời gian điện áp ở mức 0 trong giai đoạn rung phím là nhỏ nhưng cũng đủ để vi điều khiển nhận được. Vì vậy khi ta xét nếu điện áp là 0 thì gọi hàm func() thì hàm này sẽ được gọi rất nhiều lần, là điều mà ta không mong muốn. Để khác phục hiện tượng rung phím, có 2 hướng giải quyết : dùng phần cứng và phần mềm.

Về giải pháp phần cứng : thay vì mắc đơn giản như kit thí nghiệm này (xem lại sơ đồ ở Bài 2), ta có thể dùng thêm tụ điện để hạn chế việc thay đổi điện áp đột ngột, sơ đồ nguyên lý như sau:

Ở sơ đồ trên, khi không nhấn là mức 1, khi nhấn là mức 0. Phím nhấn trên tích cực mức 0. Mạch ở trên còn gọi là mạch RC.

Nếu nút nhấn có 2 cực (3 chân), ta có thể chọn giải pháp dùng mạch RS flip flop, đây là mạch phần cứng chống rung tốt nhất, sơ đồ nguyên lý như sau:

Về giải pháp phần mềm : Ta sẽ định kì đọc tín hiệu từ nút nhấn, cho đến khi nào chúng trùng nhau n lần thì mới xử lý. Hình dưới đây minh hoạ trong trường hợp 2 lần là 0 thì mới xác nhận là phím được nhấn và mới xử lý tác vụ mà ta mong muốn.

Khoảng thời gian giữa 2 lần đọc là khoảng 10ms, ta sẽ hiện thực hàm đọc này và gọi nó trong timer. Giải thuật đơn giản để xử lý chống rung có thể hiện thực như sau:

previous_key = current_key; current_key = Port_key;

If(previous_key == current_key) effective_key = current_key;

Trong đó :

previous_key : biến lưu giá trị phím trước đó.

current_key : biến lưu giá trị phím hiện tại.

Port_key : Port của vi điều khiển kết nối với phím.

effective_key : giá trị phím hợp lệ (giá trị trong giai đoạn ổn định)

Để tăng tính chính xác, ta có thể dùng nhiều biến previous_key để lưu lại các giá trị và so sánh nhiều lần. Đoạn code trên chỉ so sánh trùng nhau 2 lần.

6.2 Kết nối phần cứng

BKIT HARDWARE CLUB www.bkit4u.com 49 Phím nhấn này tính cực mức 0, được kết nối khá đơn giản, nên ta sẽ dùng phần mềm để chống rung. Ta sẽ dùng 3 biến để so sánh 2 lần trùng nhau, 2 lần liên tiếp cách nhau 10ms.

Trong trường hợp nhấn đè 1 phím, ta sẽ dùng biến TimeOutForKeyPress để xác định thời gian tích cực tiếp theo. Biến này sẽ quan trọng trong trường hợp ta viết 1 ứng dụng chẳng hạn như soạn thảo văn bản. Nếu không có biến này để quản lý, nếu ta đè thì trong 1s có tới 100 lần tích cực. Trong ví dụ của Bài 6, thời gian TimeOutForKeyPress = 100 tương ứng với 1s (100*10 = 1000ms = 1s).

6.3 Viết chương trình

Module này có 2 hàm như sau :

void initKey() : Khởi động các thông số ban đầu

void getKey() : Hàm này được gọi trong timer0, dùng để quét phím.

void SubKeyProcess() : Hàm này hiện thực tác vụ bạn cần thực hiện khi nhấn phím.

Ở kit thí nghiệm này, các nút nhấn được nối với port 3, nên ta có định nghĩa sau ở đầu file key.c : (adsbygoogle = window.adsbygoogle || []).push({});

#define KEY_PORT P3 6.3.1 Hàm initKey() void initKey() { KeyReg0 = 0x00; KeyReg1 = 0x00; KeyReg2 = 0x00; KeyReg3 = 0x00; }

Trong đó KeyReg0, KeyReg1, KeyReg2 dùng để lưu 3 lần liên tiếp. Khi 3 biến này bằng nhau, biến KeyReg3 mới được cập nhật. Biến KeyReg3 là giá trị hợp lệ của phím nhấn.

6.3.2 Hàm getKey()

Hàm này được chia làm 2 phần, phần đầu là để chống rung phím dùng 2 lần so sánh trùng nhau. Phần thứ 2 xử lý khi 1 phím được đè, phải sau 1 khoảng thời gian TimeOutForKeyPress mới được tính cực.

void getKey() {

KeyReg2 = KeyReg1; KeyReg1 = KeyReg0;

KeyReg0 = KEY_PORT;// Cho phep nut nhan nao duoc tich cuc. if ((KeyReg1 == KeyReg0) && (KeyReg1 == KeyReg2))

{ TimeOutForKeyPress --; if (TimeOutForKeyPress == 0) { KeyReg3 = 0x00; } if (KeyReg2 != KeyReg3) { KeyReg3 = KeyReg2;

if (FlagFirstTimeKeyPress == 1)// Day la lan dau phim duoc nhan. { TimeOutForKeyPress = 100; SubKeyProcess(); FlagFirstTimeKeyPress = 0; } else { if (KeyReg2 == 0x00) FlagFirstTimeKeyPress = 1; else { TimeOutForKeyPress = 100; SubKeyProcess(); } } } } }

BKIT HARDWARE CLUB www.bkit4u.com 51

Bài 7 : Quét led điều khiển Led 7 đoạn

Mục đích:

Nắm vững kĩ thuật quét Led.

Ứng dụng quét Led vào việc điều khiển 8 led 7 đoạn.

Một phần của tài liệu Lập trình 8051 siêu chi tiết (Trang 34)