You are on page 1of 20

Lập trình C cho vi điều khiển bằng Keil C caonam@gmail.

com

LẬP TRÌNH C CHO VI ĐIỀU KHIỂN


VỚI KEIL C
Lập trình C cho VĐK - Cơ bản
I.Giới thiệu
C là một ngôn ngữ khá mạnh và rất nhiều ngưòi dùng .Nếu nói số lệnh cơ
bản của C thì không nhiều và khi lập trình cho VDK, chúng ta chi cần biết số
lượng lệnh không nhiều. Trong tài liệu này sử dụng phần mềm biên dịch
Keil C 8.05.
Đầu tiên bạn phải làm quen với
- Các kiểu toán tử ở C
- Các kiểu dữ liệu
- Cấu trúc cơ bản của một chương trình
- Các cấu trúc điều khiển (chính các tập lệnh)
- Cấu trúc điều kiện : if và else
- Các cấu trúc lặp
- Vòng lặp while
- Vòng lặp do while
- Vòng lặp for
- Lệnh break.
- Cấu trúc lựa chọn: switch. Case
- Hàm và chương trình con .

II.Lập trình C cơ bản


1. Các chỉ thị trước xử lý của Keil C
a) Chú thích:

// Chú thích

Hoặc

/********** Chú thích ******************


****************************************
****************************************/

Đây là dòng chú thích. Tất cả các dòng bắt đầu bằng hai dấu sổ (//) được coi
là chú thích mà chúng không có bất kì một ảnh hưởng nào đến hoạt động của
chương trình. Các lập trình viên dùng chúng để chú thích cho chương trình.
Trong trường hợp này, dòng chú thích là một giải thích ngắn gọn những gì
mà chương trình chúng ta làm. Còn trong dấu (/* */) bạn có thể chú thích
bao nhiêu dòng tuỳ thích.
b) Chỉ thị #include và #define
Đầu file .c ta phải khai báo

#include <AT89X52.H>

Biên soạn: Cao Nguyễn Khoa Nam – Khoa Điện – Trường Cao đẳng công nghệ 1
Lập trình C cho vi điều khiển bằng Keil C caonam@gmail.com

hoặc
#include "AT89X52.H"

Để báo cho trình biên dịch biết ta đang sử dụng loại chip nào, ở đây là
AT89C/S52

Chỉ thị #define cho phép ta đặt tên các hằng số, các vùng nhớ RAM, các bit
đặc biệt.

#define True 1 // Định nghĩa giá trị True = 1


// Khi sử dụng ta có thể sử dụng True thay vì 1
// Ví dụ: a = True cũng tương đương với a = 1.

#define Output P0_6 // Định nghĩa bit P0.6 có tên là Output_1

Lưu ý: Keil C quy định cách sử dụng các bit tương ứng với các chân của chip
như sau:
P1.0 ---- P1_0
P1.1 ---- P1_1
Tức khi sử dụng P1.1 trong chương trình ta phải dùng P1_1

Một ưu điểm nổi bật của C là các bạn có thể tạo ra các bộ thư viện .
Ví dụ sau là tạo thư viện thuvien.h (đuôi .h bạn có thể tạo bằng cách save as
.. *.h ở Keil C ).

#ifndef _thuvien_H
#define _thuvien_H
....//mã chương trình
#endif

2. Các toán tử:


- Toán tử gán (=).
Ví dụ:
b = 5;
a = 2 + b;
a = 2 + (b = 5);
a = b = c = 5;

- Các toán tử số học ( +, -, *, /, % )

+ cộng
- trừ
* nhân
/ chia
% lấy phần dư (trong phép chia)
VD: i = 5%2; // i = 1

Biên soạn: Cao Nguyễn Khoa Nam – Khoa Điện – Trường Cao đẳng công nghệ 2
Lập trình C cho vi điều khiển bằng Keil C caonam@gmail.com

- Các toán tử gán phức hợp (+=, -=, *=, /=, %=, >>=, <<=, &=, ^=, |=)
a += 5; a = a + 5;
a -= 5; a = a – 5;
a /= b; a = a / b;
a *= b; a = a * b;
a *= (b + c); a = a * (b+c);

Tăng và giảm ++ - -
a++; a = a + 1;
a+=1; a = a + 1;

Tính chất tiền tố hoặc hậu tố (++a) khác với (a++)

Ví dụ:

B=3;
A=++B; // Lúc này A = 4, B = 4
// Trình dịch xử lý tăng B thêm 1 xong mới gán A = B.

Khác với:

B=3;
A=B++; // Lúc này A = 3, B = 4
// Trình dịch xử lý gán A = B xong mới tăng B thêm 1.

- Các toán tử quan hệ ( ==, !=, >, <, >=, <= )

== Bằng
!= Khác
> Lớn hơn
< Nhỏ hơn
>= Lớn hơn hoặc bằng
<= Nhỏ hơn hoặc bằng

Ví dụ:

(7 == 5) sẽ cho giá trị false


(6 >= 6) sẽ cho giá trị true

Ta cũng có thể sử dụng bất cứ biểu thức nào.

Cho a=2, b=3 và c=6


(a*b >= c) sẽ cho giá trị true.
(b+4 < a*c) sẽ cho giá trị false

Biên soạn: Cao Nguyễn Khoa Nam – Khoa Điện – Trường Cao đẳng công nghệ 3
Lập trình C cho vi điều khiển bằng Keil C caonam@gmail.com

Chú ý: = (một dấu bằng) khác hoàn toàn khác với == (hai dấu bằng). (==)
nhằm so sánh còn (=) gán giá trị của biểu thức bên phải cho biến ở bên trái.

- Các toán tử logic ( !, &&, || ).

! NOT
&& AND
|| OR
Ví dụ:
!(5 == 5) trả về false vì biểu thức bên phải (5 == 5) có giá trị true.
!(6 <= 4) trả về true vì (6 <= 4)có giá trị false.
!true trả về false.
!false trả về true.
((5 == 5) && (3 > 6)) trả về false (true && false).
((5 == 5)

- Các toán tử thao tác bit ( &, |, ^, ~, <<, >> ).

& AND
| OR
^ XOR
~ NOT
<< SHL: dịch sang trái
>> SHR: dịch sang phải
- Thứ tự ưu tiên của các toán tử

Thứ tự Toán tử Mô tả Associativity


1 :: Scope Trái
2 () [] . -> sizeof Trái
3 ++ -- tăng/giảm Phải
~
!
& *
(type)
+ -
4 * / % Toán tử số học Trái
5 + - Toán tử số học Trái
6 << >> Dịch bit Trái
7 < <= > >= Toán tử quan hệ Trái
8 == != Toán tử quan hệ Trái
9 & ^ | Toán tử thao tác Trái
bit
10 && || Toán tử logic Trái
11 ?: Toán tử điều kiện Phải
12 = += -= *= /= %= Toán tử gán Phải
>>= <<= &= ^= |=
13 , Dấu phẩy Trái

Biên soạn: Cao Nguyễn Khoa Nam – Khoa Điện – Trường Cao đẳng công nghệ 4
Lập trình C cho vi điều khiển bằng Keil C caonam@gmail.com

3. Các kiểu dữ liệu


Các kiểu biến chuẩn:

Kiểu Bits Bytes Range


char 8 1 -128 to 127
unsigned char 8 1 0 to 255
enum 16 2 -32,768 to 32,767
short 16 2 -32,768 to 32,767
unsigned short 16 2 0 to 65535
int 16 2 -32,768 to 32,767
unsigned int 16 2 0 to 65,535
long 32 4 -2,147,483,648 to 2,147,483,647
unsigned long 32 4 0 to 4,294,697,295

Một số kiểu dữ liệu đặc trưng trong Keil C:


Để phù hợp với vi điều khiển, trong Keil C còn định nghĩa thêm các kiểu dữ
liệu như trong bảng sau:

Kiểu Bits Bytes Range


bit 1 0 0 to 1
sbit 1 0 0 to 1
sfr 8 1 0 to 255
sf16 16 2 0 to 65,535

- Kiểu bit: người dùng có thể truy nhập trực tiếp đến 1 bit trong vùng
RAM có thể đánh địa chỉ bit.
- Kiểu sbit: dùng truy nhập vùng nhớ chứa các thanh ghi đặc biệt (SFR),
ví dụ bit 7(LSB) của thanh ghi 0x90 được truy nhập với tên P1_7, tức
chân P1.7 của chip.
- Kiểu sfr để định nghĩa các thanh ghi trong vùng nhớ đặc biệt, vùng
SFR. Các định nghĩa các biến có kiểu sfr sẽ được tìm thấy trong file
header khai báo chip đang được sử dụng trong chương trình, file
AT89X51.H

Biên soạn: Cao Nguyễn Khoa Nam – Khoa Điện – Trường Cao đẳng công nghệ 5
Lập trình C cho vi điều khiển bằng Keil C caonam@gmail.com

4. Cấu trúc cơ bản của một chương trình C

//Các chỉ thị tiền định


#include <AT89X51.H> // Gọi thư viện có sẵn cách viết khác "*.h"
#define led1 P1_0 // Dùng định nghĩa các biến

char bien1,bien2; // Các biến cần dùng


int c,d; // biến c, d kiểu integer

/***********************************************************/
// Các chương trình con
/***********************************************************/

void ChuongTrinhCon(unsigned int b; char a) // Chương trình con


{
// Mã lệnh xử lý trong chương trình con
...
}

/***********************************************************/
// Các hàm
/***********************************************************/
int QuetPhim(void) // chuong trinh con dang ham
{
...
return(MaPhim);
}

/***********************************************************/
// Chương trình chính
/***********************************************************/

void main(void) // Chương trình chính


{
int a; // Khai báo các biến cục bộ
ChuongTrinhCon(4,6); // Gọi các chương trình con
a = Ham(); // Gọi các hàm
}

Chương trình con là nơi các bạn viết các chương trình nhỏ, rất tiện cho các đoạn
lệnh gặp lại nhiều lần. Chương trình con có thể được gọi ở bất kì đâu kể cả
chương trình chính, các chương trình con khác và các chương trình phục vụ ngắt
(ISR).
Hàm là chương trình con trả về cho mình một giá trị. Cuối hàm bắt buộc phải có
lệnh return(x);

Biên soạn: Cao Nguyễn Khoa Nam – Khoa Điện – Trường Cao đẳng công nghệ 6
Lập trình C cho vi điều khiển bằng Keil C caonam@gmail.com

5. Các lệnh cơ bản của C


Cấu trúc điều kiện: if và else
- Cú pháp:

if (condition) statement;

Ví dụ:
if (x==100) x++;

- Cú pháp:

if (condition) {
statement1;
statement2;
...
// Nếu xử lý có nhiều hơn 1 lệnh
}
Ví dụ:
if (x==100) {
i++;
j++;
}

- Cú pháp:

if (condition) statement1; else statement2;

Ví dụ:
if (x == 100) x++;
else x--; // Nếu x=100 thì tăng x thêm 1 nếu không thì giảm x đi 1.

- Cú pháp:

if (condition) {
statement1;
statement2;
...
// Nếu xử lý có nhiều hơn 1 lệnh
}
else {
statement3;
statement4;
...
// Nếu xử lý có nhiều hơn 1 lệnh
{

Ví dụ:
if (x==100) {
x++;
x-=2;
}
else
{
x--;
x-=2;

Biên soạn: Cao Nguyễn Khoa Nam – Khoa Điện – Trường Cao đẳng công nghệ 7
Lập trình C cho vi điều khiển bằng Keil C caonam@gmail.com

Các cấu trúc lặp


- Vòng lặp while .
Cú pháp:

while (expression) statement;

Ví dụ:
while(1) {

};

Tạo vòng lặp mãi mãi, rất hay sử dụng trong lập trình VĐK. Chương trình chính sẽ được
viết trong dấu ngoặc.

- Vòng lặp do-while


Cú pháp:
do statement while (condition);

hoặc

do {
statement1;
statement2;
...
} while (condition);

Ví dụ:
do x++ while (x<10);

hoặc
do {
x++;
i++;
j++;
} while (x<10);

Tăng giá trị của x cho đến khi x >= 10


Chức năng của nó là hoàn toàn giống vòng lặp while chỉ trừ có một điều là điều kiện điều
khiển vòng lặp được tính toán sau khi statement được thực hiện, vì vậy statement sẽ được
thực hiện ít nhất một lần ngay cả khi condition không bao giờ được thoả mãn .Như vd
trên kể cả x >10 thì nó vẫn tăng giá trị 1 lần trước khi thoát. Nếu x=10 thì tăng x thêm 1
còn không thì giảm x.

Biên soạn: Cao Nguyễn Khoa Nam – Khoa Điện – Trường Cao đẳng công nghệ 8
Lập trình C cho vi điều khiển bằng Keil C caonam@gmail.com

Vòng lặp for .


Cú pháp:

for (initialization; condition; increase) statement;

Chức năng chính vòng lặp for là lặp lại statement chừng nào condition còn mang giá trị
đúng, như trong vòng lặp while. Nhưng thêm vào đó, vòng lặp for cung cấp chỗ dành cho
lệnh khởi tạo và lệnh tăng. Vì vậy vòng lặp này được thiết kế đặc biệt lặp lại một hành
động với một số lần xác định.

Cách thức hoạt động của nó như sau:


1. initialization được thực hiện. Nói chung nó đặt một giá trị ban đầu cho biến điều khiển.
Lệnh này được thực hiện chỉ một lần.
2. condition được kiểm tra, nếu nó là đúng vòng lặp tiếp tục còn nếu không vòng lặp kết
thúc và statement được bỏ qua.
3. statement được thực hiện. Nó có thể là một lệnh đơn hoặc là một khối lệnh được bao
trong một cặp ngoặc nhọn. { }
4. Cuối cùng, increase được thực hiện để tăng biến điều khiển và vòng lặp quay trở lại
bước 2.
Phần khởi tạo và lệnh tăng không bắt buộc phải có. Chúng có thể được bỏ qua nhưng vẫn
phải có dấu chấm phẩy ngăn cách giữa các phần. Vì vậy, chúng ta có thể viết

Đầy đủ:
for (n=0;n<10;n++) {

}
hoặc
for (;n<10;) {

}
hoặc

for (;n<10;n++){
}

Bằng cách sử dụng dấu phẩy, chúng ta có thể dùng nhiều lệnh trong bất kì trường nào
trong vòng for, như là trong phần khởi tạo. Ví dụ chúng ta có thể khởi tạo một lúc nhiều
biến trong vòng lặp:

for (i=2, n=0; n!=100 ; n++)


{
// đặt các lệnh ở đây.
}

Biên soạn: Cao Nguyễn Khoa Nam – Khoa Điện – Trường Cao đẳng công nghệ 9
Lập trình C cho vi điều khiển bằng Keil C caonam@gmail.com

Các lệnh rẽ nhánh và lệnh nhảy


Lệnh break.
Sử dụng break chúng ta có thể thoát khỏi vòng lặp ngay cả khi điều kiện để nó kết thúc
chưa được thoả mãn. Lệnh này có thể được dùng để kết thúc một vòng lặp không xác
định hay buộc nó phải kết thúc giữa chừng thay vì kết thúc một cách bình thường. Ví dụ,
chúng ta sẽ dừng việc đếm ngược trước khi nó kết thúc:

Ví dụ:

unsigned char i, j;
...
j= 0;
for (i=1; i<10; i++) {
if (i==6) break;
j++;
}

Sau đoạn mã trên i=6; j=5;

Kết thúc vòng lặp i sẽ có giá trị là 6 vì khi i=6 thì điều kiện if xảy ra và vòng lặp for sẽ bị
dừng tại điểm đó.

Lệnh continue
Lệnh continue làm cho chương trình bỏ qua phần còn lại của vòng lặp và nhảy sang lần
lặp tiếp theo.

Ví dụ:

unsigned char i, j;
...
j= 0;
for (i=1; i<10; i++) {
if (i==6) continue;
j++;
}

Sau đoạn mã trên i=9; j=8 do bỏ qua lần i=6 (không tăng j khi i=6);

Lệnh goto
Lệnh này cho phép nhảy vô điều kiện tới bất kì điểm nào trong chương trình. Nói chung
bạn nên tránh dùng nó trong chương trình C vì đây là một lệnh có thể phá vỡ cấu trúc của
chương trình gây ra những hiện tượng khó hiểu nếu địa chỉ nhảy đến nằm ngoài chương
trình con chứa lệnh goto này.

Biên soạn: Cao Nguyễn Khoa Nam – Khoa Điện – Trường Cao đẳng công nghệ 10
Lập trình C cho vi điều khiển bằng Keil C caonam@gmail.com

Ví dụ:

unsigned char i, j;
{
...
j= 0;
for (i=1; i<10; i++) {
if (i==6) goto There;
j++;
}
...

There:
...
// Ít nhất phải có 1 lệnh sau nhãn trước khi kết thúc chương trình
}

Sau đoạn mã trên i=6; j=5;

Trong các lệnh trên, lệnh break hay được dùng để thoát khỏi vòng lặp. Các lệnh khác
thường rất ít được sử dụng.

Ví dụ:
while(1){
LCD_GotoXY(1,1);
StrToLCD("Quay thuan");
QuayThuan();
if(!Stop) {
LCD_Clear();
StrToLCD("Dung");
Stop();
break;
}
}

Cấu trúc lựa chọn: switch.


Cú pháp của lệnh switch hơi đặc biệt một chút. Mục đích của nó là kiểm tra một vài giá
trị hằng cho một biểu thức, tương tự với những gì chúng ta làm ở đầu bài này khi liên kết
một vài lệnh if và else if với nhau.

Cú pháp:

switch (expression)
{
case constant1:
{
block of instructions 1 // Khối lệnh 1
break;
}
case constant2:

Biên soạn: Cao Nguyễn Khoa Nam – Khoa Điện – Trường Cao đẳng công nghệ 11
Lập trình C cho vi điều khiển bằng Keil C caonam@gmail.com

block of instructions 2 // Khối lệnh 2


break;
.
.
.
default:
default block of instructions
}

Nó hoạt động theo cách sau: switch tính biểu thức và kiểm tra xem nó có bằng constant1
hay không, nếu đúng thì nó thực hiện “block of instructions 1” cho đến khi tìm thấy từ
khoá break, sau đó nhảy đến phần cuối của cấu trúc lựa chọn switch.
Còn nếu không, switch sẽ kiểm tra xem biểu thức có bằng constant2 hay không. Nếu
đúng nó sẽ thực hiện block of instructions 2 cho đến khi tìm thấy từ khoá break.
Cuối cùng, nếu giá trị biểu thức không bằng bất kì hằng nào được chỉ định ở trên (bạn có
thể chỉ định bao nhiêu câu lệnh case tuỳ thích), chương trình sẽ thực hiện các lệnh trong
phần default: nếu nó tồn tại vì phần này không bắt buộc phải có.

Biên soạn: Cao Nguyễn Khoa Nam – Khoa Điện – Trường Cao đẳng công nghệ 12
Lập trình C cho vi điều khiển bằng Keil C caonam@gmail.com

III. Mảng
Mảng là một chuỗi các dữ liệu nằm nối tiếp nhau trong bộ nhớ mà ta có thể truy cập đến
từng phần tử riêng rẽ của nó theo số thứ tự của nó trong mảng.

Ví dụ: Một mảng gồm 5 số nguyên có tên là A có thể biểu diễn như sau:

Mỗi ô chứa 1 phần tử của mảng và được đánh số thứ tự bắt đầu từ 0.

Cách khai báo một mảng như sau:

KieuDuLieu Ten [So_Phan_Tu_Cua_Mang];


KieuDuLieu là kiểu dữ liệu của các phần tử trong mảng ví dụ như int, float,
char ...
Ten là tên mảng
So_Phan_Tu_Cua_Mang: số phần tử trong mảng

Ví dụ:
int A[5]; // Khai báo mảng A gồm 5 phần tử kiêu số nguyên

Lưu ý: So_Phan_Tu_Cua_Mang phải là một hằng số vì đây không phải là một mảng
động nên số phần tử cần phải được xác định.

Khởi tạo mảng.

Khi khai báo một mảng, nếu ta không khới tạo các giá trị ban đầu cho nó thì
các phần tử trong mảng sẽ tự động mặc định giá trị là 0. Ta có thể
khởi tạo các giá trị ban đầu của mảng bằng cách như sau:

int A[5] = { 16, 2, 77, 40, 12071 };

Khai báo này sẽ tạo ra một mảng trong bộ nhớ RAM như sau:

Biên soạn: Cao Nguyễn Khoa Nam – Khoa Điện – Trường Cao đẳng công nghệ 13
Lập trình C cho vi điều khiển bằng Keil C caonam@gmail.com

Truy cập mảng.

Tại bất cứ đâu trong chương trình ta có thể truy cập (đọc- ghi) các phần tử
của mảng bằng dạng sau.
Ten[index]

Ví dụ mảng billy có 5 phần tử

Ví dụ muốn lưu giá trị 75 vào phần tử thứ 3 của mảng billy, ta làm như sau:

billy[2] = 75;

và, ví dụ muốn gán giá trị thứ 3 của mảng cho một biến a khác ta làm như sau:

a= billy[2];

Lưu ý ở đây, phần tử thứ 3 của mảng billy có số thứ tự là 2 vì các phần tử
trong mảng được đánh số từ 0.

Một chú ý nữa là ta nên lưu ý khi truy cập các phần tử của mảng, không được
truy cập các phần tử có chỉ số lớn hơn hoặc bằng tổng số phần tử trong
mảng. Ví dụ mảng A chỉ có 5 phần tử được đánh số từ 0 đến 4, ta truy cập
phần tử thứ 5, tức A[5] của mảng thì sẽ gây ra nhiều lỗi rất khó hiểu vì khi
khai báo thì mảng chỉ có 5 phần tử, trình biên dịch sẽ chỉ cấp phát 5 vùng
nhớ cho các phần tử trong mảng, vùng nhớ kế tiếp với mảng có thể chứa 1
biến nào đó, việc đọc ghi phần tử thứ 5 trong mảng tình cờ sẽ đọc ghi giá trị
biến nằm kế mảng đó, gây sai trong chương trình. Tuy nhiên với lỗi này thì
trình biên dịch không báo vì nó không gây lỗi khi biên dịch. Do đó ta cần phải
cẩn thận khi truy cập các phần tử của mảng.

Một số phép toán thao tác với mảng:

billy[0] = a;
billy[a] = 75;
b = billy [a+2];
billy[billy[a]] = billy[2] + 5;

Biên soạn: Cao Nguyễn Khoa Nam – Khoa Điện – Trường Cao đẳng công nghệ 14
Lập trình C cho vi điều khiển bằng Keil C caonam@gmail.com

// arrays example

int A [] = {16, 2, 77, 40, 12071};


int n, KetQua=0;

void main () {
for (n=0; n<5; n++) KetQua += A[n];
} // KetQua = 12206

Mảng nhiều chiều


Mảng nhiều chiều được mô tả như mảng của các mảng, ví dụ một mảng 2 chiều được
khai báo như sau.

jimmy là một mảng 2 chiều với 3 mảng 1 chiều, mỗi mảng chứa 5 phần tử.

Cách khai báo mảng 2 chiều như sau

int jimmy [3][5];

Truy cập mảng 2 chiều

jimmy[1][3]=10;

a = jimmy[1][3];

Biên soạn: Cao Nguyễn Khoa Nam – Khoa Điện – Trường Cao đẳng công nghệ 15
Lập trình C cho vi điều khiển bằng Keil C caonam@gmail.com

III. C cho 8051


1. Các kiểu biến, các loại vùng nhớ và các chế độ đánh địa chỉ:
data địa chỉ trực tiếp MOV A, 07Fh
idata địa chỉ gián tiếp MOV R0, #080h
MOV A, @R0
xdata bộ nhớ ngoài MOVX @DPTR, #10
code bộ nhớ chương trình MOVC A, @A+DPTR

Ví dụ 1:
unsigned char data X = 0; // Khai báo biến X kiểu số nguyên (integer) nằm trong RAM
từ 00 đến 7FH.

unsigned int idata Y = 0; // Khai báo biến Y kiểu số nguyên (integer) nằm trong RAM
từ 80H đến FFH, vùng nhớ này chỉ có trong các chip có 256 byte RAM nội trở lên như
AT89C52, AT89S52, AT89S8252, AT89C55WD ...
Chỗ khác nhau khi khai báo 1 biến nằm trong vùng data hay idata là ở kiểu đánh địa chỉ.
Một biến nằm trong vùng nhớ data là biến được truy cập trực tiếp còn biến nằm trong
vùng idata là biến được truy cập gián tiếp. Khi lập trình bằng C chúng ta không cần quan
tâm đến điều này, tuy nhiên khi cần thiết mới khai báo 1 biến nằm trong vùng idata vì nó
sẽ làm chậm chương trình. Ta xem ví dụ sau:
unsigned int data i = 0;
C:0x000F E4 CLR A
C:0x0010 F508 MOV 0x08,A
C:0x0012 F509 MOV 0x09,A

unsigned int idata j = 0;

C:0x0014 780A MOV R0,#0x0A


C:0x0016 F6 MOV @R0,A
C:0x0017 08 INC R0
C:0x0018 F6 MOV @R0,A

Khi thao tác xử lý các biến nằm trong vùng idata sẽ phải tốn nhiều lệnh hơn, do đó
chương trình sẽ bị chậm hơn. Một lời khuyên là chỉ đặt các biến cần tốc độ xử lý không
cao nằm trong vùng nhớ này.

Ví dụ 2:
unsigned char code KetQua = 100; // Đây là một cách khai báo hằng số, vì từ khoá
code xác định KetQua nằm trong vùng nhớ chương trình (ROM). KetQua sẽ là một hằng
số nên ta chỉ có thể thao tác đọc nó chứ không thay đổi được nó (không cho phép ghi).

Ví dụ 3:
unsigned char xdata KetQua = 0;

Chú ý rằng, bạn có thể không cần khai báo cụ thể vị trí của một biến nằm trong vùng
nhớ nào, chỉ cần khai báo như sau, trình biên dịch của Keil C mặc định biến này sẽ nằm
trong vùng nhớ data.
unsigned int Y = 0;

Biên soạn: Cao Nguyễn Khoa Nam – Khoa Điện – Trường Cao đẳng công nghệ 16
Lập trình C cho vi điều khiển bằng Keil C caonam@gmail.com

Trong trường hợp này trình biên dịch tự động phân bổ vùng nhớ RAM cho biến Y cho
đến lúc nào số biến mà bạn sử dụng nhiều vượt quá dung lượng vùng nhớ data, khi đó
trình biên dịch sẽ báo cho ta biết không đủ bộ nhớ, ta phải chuyển bớt các biến ra vùng
nhớ idata bằng cách khai báo như ví dụ 1.

2. Bảng vector ngắt và cách khai báo một chương trình con phục vụ ngắt - ISR

Interrupt Vector Interrupt


address number
Ngắt ngoài 0 0003h 0
Ngắt timer 0 000Bh 1
Ngắt ngoài 1 0013h 2
Ngắt timer 1 001Bh 3
Ngắt do cổng nối tiếp 0023h 4

Trong 8051 có 5 nguyên nhân sinh ra ngắt: ngắt ngoài 0, timer0, ngắt ngoài 1, timer1,
ngắt nối tiếp.

Các bạn lại mở file regx51.h ra ở phần cuối của file như sau:

/*------------------------------------------------
Interrupt Vectors:
Interrupt Address = (Number * 8) + 3
------------------------------------------------*/
#define IE0_VECTOR 0 /* 0x03 External Interrupt 0 */
#define TF0_VECTOR 1 /* 0x0B Timer 0 */
#define IE1_VECTOR 2 /* 0x13 External Interrupt 1 */
#define TF1_VECTOR 3 /* 0x1B Timer 1 */
#define SIO_VECTOR 4 /* 0x23 Serial port */

Địa chỉ của ngắt trong bảng vector ngắt = 8 * số thứ tự ngắt + 3, số thứ tự ngắt = 0,1,2,3,4
như kí hiệu trong file đó. Như vậy địa chỉ trong RAM từ 0x03 đến 0x30 là dành cho bảng
vector ngắt.

Chương trình con phục vụ ngắt là một hàm không có tham số, không có kiểu trả về. Vì
thực chất các biến mà hàm này thao tác chính là các biến toàn cục (các thanh ghi, các
port). Các chương trình con phục vụ ngắt này sẽ tự động được CPU gọi khi điều kiện
ngắt xảy ra, thực chất khi điều kiện ngắt xảy ra thì CPU sẽ tạm dừng chương trình đang
thực hiện, nhảy đến chương trình con phục vụ ngắt tương ứng, thực hiện hết chương trình
ngắt đó và trở về lại tại điểm mà lúc nãy nó bị dừng bởi ngắt.

Cấu trúc một chương trình ngắt như sau:


Code:
void TenChuongTrinhPhucVuNgat(void) interrupt a using b{
// code

Biên soạn: Cao Nguyễn Khoa Nam – Khoa Điện – Trường Cao đẳng công nghệ 17
Lập trình C cho vi điều khiển bằng Keil C caonam@gmail.com

Trong đó: những chữ in nghiêng là bắt buộc phải có và using b có thể có hoặc không có.

TenChuongTrinhPhucVuNgat: tùy ý
a : là thứ tự của ngắt trong bảng vector ngắt
a = 0 : ngắt ngoài 0
a = 1 : ngắt timer 0
a = 2 : ngắt ngoài 1
a = 3 : ngắt timer 1
a = 4 : ngắt nối tiếp

b: là bank được chọn dùng để thực hiện hàm ngắt. Do 8051 có 4 bank là bank 0, 1, 2, 3.
Do đó : b có thể là một trong các giá trị 0,1,2,3.

Lưu ý: nếu không viết thêm using b thì mặc định là hàm ngắt thực hiện tại bank0.

Ví dụ về hàm ngắt nối tiếp sẽ có dạng như sau:

void Serial_ISR(void) interrupt 4 using 2{


// mã thực hiện hàm
}

Ví dụ: Tạo một xung ở chân P1.0 và khi nhận được dữ liệu nối tiếp thì chuyển sang port
P2. Tần số thạch anh là 11.0592 MHz. Dùng AT89C51

Code:

# include "regx51.h"

// Hàm tạo trễ


void delay(int time){
while(time--){
unsigned char j = 122;
while(j--);
}
}

// Hàm thiết lập ban đầu


void init(){
EA = 1; // Cho phép ngắt toàn cục
ES = 1; // Cho phép ngắt nối tiếp
// Thiet lap tan so bus
TMOD = 0x02; // dung timer1, che do 8 bit tu nap lai
TH1 = 253; // chon tan so bus = 9600
TR1 = 1 ; // khoi dong timer1
}

Biên soạn: Cao Nguyễn Khoa Nam – Khoa Điện – Trường Cao đẳng công nghệ 18
Lập trình C cho vi điều khiển bằng Keil C caonam@gmail.com

// Chương trình con phục vụ ngắt nối tiếp


void inter4(void) interrupt 4 using 2{
if (RI){ // Kiểm tra cờ nhận RI.
P2 = SBUF; // Lấy dữ liệu từ SBUF
RI = 0; // Xóa cờ ngắt
}
}

// Chương trình chính


void main(){
// tao xung tren chan P1.0
init();
while(1){
P1_0 = 1;
delay(500);
P1_0 = 0;
delay(500);
}
}

Lưu ý: là để VĐK nhảy đến bảng vector ngắt thì ta phải cho phép ngắt đó.

Ví dụ: Muốn tạo ngắt nối tiếp:


Code:

EA = 1;// cho phép ngắt toàn cục


ES = 1;// chp phép ngắt do cổng nối tiếp (ngắt truyền thông)

Các bit trong thanh ghi IE đã được Keil C định nghĩa trong thư viện regx51.h, các bit này
có tên giống hệt tên các bit được ghi trong datasheet.

3. Cấu trúc cơ bản của một chương trình C

//Các chỉ thị tiền định


#include <AT89X51.H> // Gọi thư viện có sẵn cách viết khác "*.h"
#define led1 P1_0 // Dùng định nghĩa các biến

char bien1,bien2; // Các biến cần dùng


int c,d; // biến c, d kiểu integer

/***********************************************************/
// Các chương trình con
/***********************************************************/

void ChuongTrinhCon(unsigned int b; char a) // Chương trình con


{
// Mã lệnh xử lý trong chương trình con
...
}

Biên soạn: Cao Nguyễn Khoa Nam – Khoa Điện – Trường Cao đẳng công nghệ 19
Lập trình C cho vi điều khiển bằng Keil C caonam@gmail.com

/***********************************************************/
// Các hàm
/***********************************************************/
int QuetPhim(void) // chuong trinh con dang ham
{
...
return(MaPhim);
}

/***********************************************************/
// Chương trình con phục vụ ngắt
/***********************************************************/
void ISR() interrupt 1 // chuong trinh con dang ham
{
...
}

/***********************************************************/
// Chương trình chính
/***********************************************************/

void main(void) // Chương trình chính


{
int a; // Khai báo các biến cục bộ
ChuongTrinhCon(4,6); // Gọi các chương trình con
a = Ham(); // Gọi các hàm
}

Biên soạn: Cao Nguyễn Khoa Nam – Khoa Điện – Trường Cao đẳng công nghệ 20

You might also like