Thứ Tư, 29 tháng 6, 2016

Bài 5.1: Giao tiếp với LEDs đơn nâng cao

Lần này mình sẽ hướng dẫn các bạn tương tác với LEDs đơn nhưng với quy mô là cả một PORT, đây là một bài nâng cao hơn so với bài 5 vừa rồi.

Trước hết, ta quay lại tìm hiểu cấu trúc của một PORT của 8051 gồm những gì ?

Các PORT xuất/nhập của 8051 bao gồm port 0 tại địa chỉ 80H, port 1 ở địa chỉ 90H, port 2 ở địa chỉ A0H và port 3 ở địa chỉ B0H. Các port 0, 2, 3 không được dùng để xuất/nhập nếu ta sử dụng thêm bộ nhớ ngoài hoặc có một số đặc tính đặc biệt của 8051 được sử dụng( như ngắt, port nối tiếp...). P1.2 đến P1.7, ngược lại luôn luôn là các đường xuất/nhập đa mục đích hợp lệ.

Tất cả các port đều được định địa chỉ từng bit nhằm cung cấp khả năng giao tiếp mạnh. Tức mỗi port là một byte, gồm 8 bits, ở đây tương đương với 8 chân xuất/nhập của từng port. Để làm việc với cả port, ta chỉ cần nhớ tên của port và giá trị mong muốn của port( dạng hexa hoặc nhị phân đều được).
Ví dụ ở đây là cho chân P1.0 lên mức cao và các chân còn lại của port 1 ở mức thấp, ta có thể khai báo trong C như sau:

P1=00000001 // ở dạng nhị phân
Hoặc P1=0x01 // ở dạng hexa

Như vậy chúng ta đã có thể tương tác trực tiếp được với từng bit của một port cũng như cả port của 8051.

Ví dụ về tương tác với port 2 của 8051:

Sơ đồ nguyên lý như sau:

Ví dụ đầu tiên mình sẽ cho cả cổng P2 cùng tắt cùng sáng, code như sau:

#include <REGX52.H>

void delay( unsigned int );

void main()
{
while(1)
{
P2 = 00000000; // gia tri hexa là 0x00
delay(100); trễ 0.1s
P2 = ~P2;
delay(100);
}
}

//ham delay( mS )
void delay( unsigned int time )
{
    unsigned int i;
 unsigned int j;

for( i=0; i<time; i++ )
for( j=0; j<1275; j++ );
}

Ở ví dụ này, P2 được thiết lập ở giá trị 0x00, tức tất cả các bits được để ở mức 0 hết, với các mắc anot chung thì LED sẽ sáng khi các bit ở mức 0. Hàm P2=~P2 là hảm lật trạng thái của cả port P2, vì vậy ta thu được cả port 2 nhấp nháy.

Ở ví dụ tiếp theo, mình sẽ làm cho từng led của port sáng lên và khi sáng tất cả các đèn thì cả port sẽ cùng sáng và tắt:

#include <REGX52.H>

void delay( unsigned int );

void main()
{
while(1)
{
P2 = 11111110;
delay(100);
P2= 11111101;
delay(100);
P2 = 11111011;
delay(100);
P2 = 11110111;
delay(100);
P2 = 11101111;
delay(100);
P2 = 11011111;
delay(100);
P2 = 10111111;
delay(100);
P2 = 01111111;
delay(100);
P2 = 11111111;
delay(100);
                P2 = ~P2;
                delay(100);
}
}

//ham delay( mS )
void delay( unsigned int time )
{
    unsigned int i;
 unsigned int j;

for( i=0; i<time; i++ )
for( j=0; j<1275; j++ );
}

Các port khác chúng ta có thể làm tương tự, các bạn có thể tùy biến các bit để tạo thành hiệu ứng mà các bạn mong muốn.
Chúc các bạn thành công!

Thứ Ba, 28 tháng 6, 2016

Bài 5: Giao tiếp với LED đơn

Tiếp nối bài 4 lần trước, lần này mình xin hướng dẫn các bạn giao tiếp với LED đơn như sau:
Trước hết, ta cần hiểu về một cấu trúc của port Xuất/Nhập (Output/Input)

1. Cấu trúc của một port xuất/nhập:

Dưới đây là cấu trúc của một port xuất nhập. Việc ghi đến 1 chân của port sẽ nạp dữ liệu vào bộ chốt của port( Port Latch),ngõ ra Q của bộ chốt điều khiển một transistor trường và transistor này nối với các chân của port. Khả năng fanout( số lượng đầu vào logic của một cổng nhiều hay ít) của các port 1, 2 và 3 là 4 tải vi mạch TTL loại Schottky công suất thấp( LS  ) còn của port 0 là 8 tải loại LS.

Lưu ý là điện trở kéo lên( pull up ) sẽ không có ở port 0( trừ khi port này làm nhiệm vụ của bus địa chỉ/ dữ liệu đa hợp ) do vậy chúng ta cần lắp điện trở kéo bên ngoài cho port 0.
Giá trị của điện trở này phụ thuộc vào đặc tính ngõ vào của thành phần nối với chân của port( thông thường sẽ là 4k7 đến 10k).



2. Chu kì máy của 8051

Đối với CPU để thực hiện một lệnh bất kì thì mất một chu kì đồng hồ này được coi như các chu kì máy. Đối với họ 8051 thì độ dài chu kì máy phụ thuộc vào tần số bộ dao động thạch anh được nối vào hệ thống 8051. Bộ dao động thạch anh cùng với mạch điện trên chip cung cấp xung đồng hồ cho CPU của 8051. Tần số của thạch anh nối với 8051 dao động trong khoảng 4MHz đến 30MHz phụ thuộc vào tốc độ chíp và nhà sản xuất. Thường xuyên nhất là bộ dao động thạch anh với tần số 10.0592MHz được sử dụng làm cho hệ thống 8051 tương thích với cổng nối tiếp của PC IBM. Trong 8051, một chu kì máy kéo dài 12 chu kì dao động. Do vậy, để tính toán chu kì máy ta lấy 1/12 tần số của thạch anh, sau đó ta lấy giá trị ngịch đảo để tìm đc thời gian của một chu kì máy.
Ví dụ chu kì máy với trường hợp thạch anh là 11.0592MHz được tính như sau:
11.0592/12=921.6KHz, Chu kì máy là 1/921.6kHz=1.085uS.

3, Giao tiếp với LED đơn.

Mạch nguyên lý được vẽ trên Proteus như sau:


Code như sau:
#include<reg52.h>           // file header cua 89c52

sbit LED = P2^0;            // dinh ngia chan p2.0 co ten la LED

void Delay(void);           // ham Delay

void main (void)
{
    while(1)                // vong lap vo han
    {
        LED = 0;            // LED ON
        Delay();            // ham delay 1s
        LED = 1;            // LED OFF
        Delay();
    }
}

void Delay(void)      
{
    int j;
    int i;
    for(i=0;i<10;i++)
    {
        for(j=0;j<10000;j++)  // 1s delay
        {
        }
    }
}
Như vậy là chúng ta đã tương tác xong với LED đơn sử dụng 8051, bài tiếp theo sẽ là bài nâng cao hơn từ bài này, đó là mạch trái tim sử dụng 8051. Mong các bạn theo dõi và ủng hộ.

Thứ Tư, 22 tháng 6, 2016

Bài 4. Hướng dẫn mô phỏng trên Proteus và cách nạp chương trình cho họ 8051

Bài này mình sẽ hướng dẫn các bạn dùng Keil C để dịch sang file .hex dùng cho mô phỏng mạch trên Proteus. Công cụ chúng ta cần là Keil C và Proteus 8.4.

File .hex là gì ?

Hex ở đây được viết tắt bởi từ hexadecimal có nghĩa là hệ cơ số 16 hay hệ thập lục phân. Khi bạn viết code và nạp chương trình cho vi điều khiển thì không đơn thuần là nạp những "câu lệnh" bạn viết ra đưa vào vi điều khiển, mà phải thông qua trình biên dịch (Code Vision, Keil C...đều là trình biên dịch) "dịch" lại những gì mình viết và tạo ra một chuỗi kí tự gồm những chữ (từ A đến F) và số (từ 0 đến 9) thuộc hệ thập lục phân. Những chữ số này nạp vào vi điều khiển thì vi điều khiển mới "hiểu" được. Những chữ số này được đóng gói và lưu lại thành một file có đuôi mở rộng là ".hex".

Tại sao lại dịch sang file .hex là không phải là file với định dạng khác? 

Vi điều khiển chỉ có khả năng làm việc với các tập lệnh dưới định dạng là ở cơ số 16, vì vậy ta cần một file .hex để nạp vào vi điều khiển, qua đó thì vi điều khiển mới hoạt động được.

1. Hướng dẫn dùng Keil C để dịch ra file .hex
Để bắt đầu viết code cho một dự án nào đó, các bạn mở Keil C lên  và chọn Project/ New uVision Project. Màn hình tiếp theo các bạn điền tên project rồi chọn OK.
Ở màn hình tiếp theo, các bạn chọn vi điều khiển mà các bạn đang dùng, ở đây mình chọn là AT89S52.
Ở màn hình tiếp theo, đây là lựa chọn không bắt buộc, các bạn có thể đồng ý thêm file STARTUP.A51( Đây là một file trích xuất từ hệ thống, được viết bằng hợp ngữ). File này không ảnh hưởng gì đến chương trình của bạn nên các bạn có thể bỏ chọn.

Ở màn hình tiếp theo, ta bắt đầu thêm file C cho project bằng cách chọn chuột phải vào ô Source Group 1 và chọn Add new item to.... Nếu các bạn đã soạn thảo file .c hay .h từ trước đó mà muốn nhập vào project thì các bạn có thể chọn Add existing items to... và chọn các file đã soạn trước đó.

Các bạn gõ tên cho file c và ấn Add.
Màn hình tiếp theo là phần soạn thảo code, ngôn ngữ dùng ở đây là C. Mình có viết qua chương trình nhấp nháy LEDs như sau:

#include<reg52.h>           // thu vien cua 89s52
sbit LED = P2^0;            // dinh nghia chan P2.0 co ten la LED
void Delay(void);           // ham delay
void main (void)
{
    while(1)                // vong lap vo han
    {
        LED = 0;            // LED ON
        Delay();
        LED = 1;            // LED OFF
        Delay();
    }
}

void Delay(void)
{
    int j;
    int i;
    for(i=0;i<10;i++)
    {
        for(j=0;j<10000;j++) // delay 1ms
        {
        }
    }

Các bạn nên ấn nút lưu lại thường xuyên đề phỏng những trường hợp không may.
Khi các bạn soạn thảo xong thì ta tiến hành dịch sang file .hex như sau:
Chọn Flash/ Configure Flash tools
Ở ô Target các bạn chọn giá trị Xtal cho phù hợp, ở đây mình chọn là 12MHz.
Tiếp theo bên tab Output các bạn tích chọn ô Create HEX file, format các bạn để ở mặc định. Một tùy biến ở đây các bạn có thể chọn thư mục sẽ chứa file .hex để sau này dễ tìm kiếm.

Tiếp theo, để tiến hành dịch chương trình sang file hex, các bạn ấn Project/ Build target. Nếu không có lỗi gì thì sẽ có thông báo là 0 error 0 warning và đã tạo xong file hex ở trong thư mục mà ở trên các bạn đã chọn.
 Như vậy ta đã tiến hành xong việc dùng Keil C để dịch sang file .hex dùng cho mô phỏng trên proteus cũng như dùng để nạp chương trình vào IC sau này.

2. Mô phỏng trên Proteus

Các bạn mở Proteus lên, tiến hành vẽ mạch như bình thường.

Để nạp file hex cho vi điều khiển, ta chuột phải vào IC rồi chọn Edit Properties. Ở ô program file, ta ấn vào icon Open rồi dẫn đến file hex ta vừa tạo ở trên rồi chọn OK. Để tiến hành mô phỏng, ta ấn vào Debug/ Run Simulation hoặc ấn vào icon run ở góc dưới bên trái màn hình.

3. Nạp chương trình cho 8051 bằng mạch nạp ISP

Công cụ cần thiết là mạch nạp ISP( http://banlinhkien.vn/goods-493-mach-nap-89-avr-usbasp-usbisp.html ) và chương trình nạp progisp1.72 ( https://www.fshare.vn/file/ZP4AFQ9O5H/ )

Dao diện của progisp như sau:

Các bạn tiến hành chọn đúng chip mình đang dùng, ở đây là AT89S52. 
Chữ PRG ISP sẽ sáng lên khi phần mềm đã nhận đc mạch nạp:
Các bạn chọn Load Flash rồi dẫn đến file hex vừa tạo ở trên. Rồi ấn Auto, nếu thành công sẽ có thông báo sau: 
Erase, Write Flash, Verify Flash, Successful Done.
Lưu ý là các bạn phải nối đúng các chân của mạch nạp với vi điều khiển nhé:


Chúc các bạn thành công! Bài tiếp theo mình sẽ hướng dẫn các bạn về lập trình 8051 từ cơ bản đến nâng cao. Mong các bạn theo dõi và ủng hộ!









Thứ Hai, 20 tháng 6, 2016

Bài 3: Hướng dẫn cài đặt Keil C

1. Cài đặt:

Các bạn vào trang này rồi điền các thông tin cần thiết để tiến hành tải về Keil C for 8051 familes:
https://www.keil.com/demo/eval/c51.htm.

Link sau là file Keygen.rar

https://www.mediafire.com/?pj1iof9f98ddahd

Sau khi tải về đươc file .exe thì ta tiến hành cài đặt:

Các bạn ấn Next để tiếp tục:


Các bạn ấn Next để máy tính bắt đầu cài đặt


Kết thúc quá trình cài đặt như sau. Các  bạn nhấn finish để kết thúc quá trình cài đặt:


2. Kích hoạt bản quyền:

Tại sao lại phải kích hoạt bản quyền??

--> Vì bản dành cho dùng thử( Evalution Version) chỉ hỗ trợ dịch sang file .hex với kích thước tối đa là 2000h. Vì vậy, nếu chương trình của chúng ta mà lớn hơn thì phiên bản này sẽ không dịch sang file .hex được.

Các bạn mở Keil C lên, chọn File/License Managerment.
Các bạn Copy dòng code CID( Computer ID), Bạn mở file keygen tải được ở trên :
Bạn paste dòng CID vào ô CID, target là C51(họ 8051). Rồi ấn generate, ta thu được dòng License ID, các bạn copy dòng code này rồi paste vào ô New License ID code trên màn hình License Managerment trên Keil C. Rồi ấn Add LIC. Nếu thành công sẽ có màn hình như sau:
Chúc các bạn thành công!
Bài sắp tới mình sẽ hướng dẫn các bạn cách sử dụng Keil C để tiến hành dịch file .hex để dùng trong mô phỏng Proteus.






Bài 2: Ngôn ngữ lập trình C

1.  Giới thiệu ngôn ngữ C

Trong kỹ thuật lập trình vi điều khiển nói chung, ngôn ngữ lập trình được sử dụng thường chia làm 2 loại: Ngôn ngữ bậc thấp và Ngôn ngữ bậc cao.
Ngôn ngữ bậc cao là các ngôn ngữ gần vơi ngôn ngữ con người hơn, do đó việc lập trình bằng các ngôn ngữ này trở nên dễ dàng và đơn giản hơn. Có thể kể đến một số ngôn ngữ lập trình bậc cao như C, Basic, Pascal… trong dó C là ngôn ngữ thông dụng hơn cả trong kỹ thuật vi điều khiển. Về bản chất, sử dụng các ngôn ngữ này thay cho ngôn ngữ bậc thấp là giảm tải cho lập trình viên trong việc nghiên cứu các tập lệnh và xây dựng các cấu trúc giải thuật. Chương trình viết bằng ngôn ngữ bậc cao cũng sẽ được một phần mềm trên máy tính gọi là trình biên dịch (Compiler) chuyển sang dạng hợp ngữ trước khi chuyển sang mã máy.
Khi sử dụng ngôn ngữ C người lập trình không cần hiểu sâu sắc về cấu trúc của bộ vi điều khiển. Có nghĩa là với một người chưa quen với một vi điểu khiển cho trước sẽ xây dựng được chương trình một cách nhanh chóng hơn, do không phải mất thời gian tìm hiểu kiến trúc của vi điều khiển đó. Và việc sử dụng lại các chương trình đã xây dựng trước đó cũng dễ dàng hơn, có thể sử dụng toàn bộ hoặc sửa chữa một phần.

2. Ngôn ngữ C

2.1 Kiểu dữ liệu

2.1.1 Kiểu dữ liệu trong C

Kiểu
Số Byte
Khoảng giá trị
Char
1
-128 – +127
Unsigned char
1
0 – 255
Int
2
-32768 - +32767
Unsigned int
2
0 - 65535
Long
4
-2147483648 - +2147483647
Unsigned long
4
0 – 4294967295
Float
4

* Khai báo biến:

 - Cú pháp: Kiểu_dữ_liệu Vùng_nhớ   Tên_biến _at_ Đia_chỉ;
     Ví dụ:        Unsigned char data x;
 - Khi khai báo biến có thể gán luôn cho biến giá trị ban đầu.

      Ví dụ:       Thay vì:         unsigned char x;
                                                 x = 0;
                         Ta chỉ cần:    unsigned char x = 0;
 - Có thể khai báo nhiều biến cùng một kiểu một lúc.

       Ví dụ:   Unsigned int x,y,z,t;

 - Chỉ định vùng nhớ: từ khoá “Vùng_nhớ” cho phép người dùng có thể chỉ ra vùng nhớ sử dụng để lưu trữ các biến sử dụng trong chương trình. Các vùng nhớ có thể sử dụng là:
CODE, DATA, DATAB, IDATA, PDATA, XDTA. Khi không khai báo vùng nhớ trình dịch Keil C sẽ mặc định đó là vùng nhớ DATA.

Vùng nhớ
Ý nghĩa
CODE
Bộ nhớ mã nguồn chương trình
DATA
Bộ nhớ dữ liệu gồm 128 Byte thấp của RAM trong vi điều khiển
BDATA
Bộ nhớ dữ liệu có thê định địa chỉ bit, nằm trong vùng nhớ DATA
IDATA
Bộ nhớ dữ liệu gồm 128 Byte cao của RAM trong vi điều khiển chỉ có ở một số dòng vi điều khiển sau này
PDATA
Bố nhớ dữ liệu ngoài gồm 256 Byte, được truy cập bởi địa chỉ đặt trên P0
XDATA
Bộ nhớ dữ liệu ngoài có dung lượng có thể lên đến 64 KB, được truy cập bởi địa chỉ đặt trên P0 và P2

  * Định nghĩa lại kiểu

 - Cú pháp: Typedef Kiễu_dữ_liệu Tên_biến;
 - Tên_biến sau này sẽ được sử dụng như một kiểu dữ liệu mới và có thể dùng để khai báo các biến khác.

Ví dụ: Typedef int m5[5];
            Dùng tên m5 khai báo hai biến tên a và b có kiểu dữ liệu là mảng 1 chiểu 5 phần tử:         m5 a,b;

2.1.2 Kiểu dữ liệu trong Keil C

Kiểu
Số bit
Bit
1
Sbit
1
Sfr
8
Sfr16
16

  - bit : dùng để khai báo các biến có giá trị 0 hoặc một hay các biến logic trên vùng RAM của vi điều khiển. Khi khai báo biến kiểu bit trình dịc Keil C sẽ mặc định vùng nhớ sử dụng là BDATA.
  - sbit, sfr, sfr16: dùng để định nghĩa các cho các thanh ghi chức năng hoặc các cổng trên vi điều khiển dùng để truy nhập các đoạn dữ liệu 1 bit, 8 bit, 16 bit.

2.1.3 Mảng

Mảng là một tập hợp nhiều phần tử cùng một kiểu giá trị và chung một tên. Các phần tử của mảng phân biệt với nhau bởi chỉ số hay số thứ tự của phần tử trong dãy phẩn tử. Mỗi phần tử có vai trò như một biến và lưu trữ được một giá trị độc lập với các phần tử khác của mảng.
Mảng có thể là mảng một chiều hoặc mảng nhiều chiều.

  Khai báo:

 - Cú pháp: Tên_kiểu Vùng_nhớ Tên_mảng[số_phần_tử_mảng];
  Khi bỏ trống số phần tử mảng ta sẽ có mảng có số phần tử bất kì.

  Ví dụ:           Unsigned int data a[5],b[2] [3];
Với khai báo trên ta sẽ có: mảng a là mảng một chiều 5 phần tử. Mảng b là mảng hai chiều, tổng số phần tử là 6.
Chỉ số của mảng bắt đầu từ số 0. Mảng có bao nhiêu chiều phải cung cấp đầy đủ bấy nhiêu chỉ số.

  Ví du:           Phần tử mảng 2 chiều:
b[0] [1] là đúng
                        Khi viết:         b[0] là sai

2.1.4. Con trỏ

Khi ta khai báo một biến, biến đó sẽ được cấp phát một khoảng nhớ bao gồm một số byte nhất định dùng để lưu trữ giá trị. Địa chỉ đầu tiên của khoảng nhớ đó chính là địa chỉ của biến được khai báo.
Con trỏ là một biến dùng để chứa địa chỉ mà không chứa giá trị, hay giá trị của con trỏ chính là địa chỉ khoảng nhớ mà nó trỏ tới.
Với các vùng nhớ cụ thể con trỏ tới vùng nhớ đó chiếm dung lượng phụ thuộc vào độ lớn của vùng nhớ đó. Con trỏ tổng quát khi không xác định trước vùng nhớ sẽ có dung lượng lớn nhất vì vậy tốt nhất nên sử dụng con trỏ cụ thể.

Loại con trỏ
Kích thước
Con trỏ tổng quát
3 byte
Con trỏ XDATA
2 byte
Con trỏ CODE
2 byte
Con trỏ DATA
1 byte
Con trỏ IDATA
1 byte
Con trỏ PDATA
1 byte

  Khai báo biến con trỏ:

 - Cú pháp: Kiểu_Dữ_liệu Vùng_nhớ *Tên_biến;

 - Ví dụ:         int *int_ptr;
                        long data *long_ptr;

 - khi không chỉ rõ vùng nhớ con trỏ sẽ được coi là con trỏ tổng quát.

2.1.5 Kiểu dữ liệu cấu trúc

Kiểu dữ liệu cấu trúc là một tập hợp các biến, các mảng và cả các kiểu cấu trúc khác được biểu thị bởi một tên duy nhất. kiểu dữ liệu cấu trúc dùng để lưu trữ các giá trị, thông tin có liên quan đến nhau.

  Định nghĩa và khai báo biến cấu trúc:

 - Định nghĩa:           Typedef struct {
                                                                        Khai báo các biến thành phần;
                                                               } Tên_kiểu_cấu_trúc;
 - Khai báo: Tên_kiểu_cấu_trúc Vùng_nhớ Tên_biến;

  Ví dụ:           Typedef struct {
                                                            char day;
                                                char month;
                                                            int year;
                                                  } Date_type;
                        Date_type date,date_arr[5];

2.2 Phép toán

  Phép gán kí hiệu: “=”.
 - Cú pháp: Biến_1 = Biến_2;
            Trong đó Biến_2 có thể là giá trị xác định cũng có thể là biến.

2.2.1 Phép toán số học

Phép toán
Ý nghĩa
Ví dụ
+
Phép cộng 
X = a+b;
-
Phép trừ 
X = a-b;
*
Phép nhân 
X = a*b;
/
Phép chia lấy phần nguyên
X = a/b;
(a=9, b=2 → X=4)
%
Phép chia lấy phần dư 
X = a%b;
(a=9, b=2 → X=1)

2.2.2 Phép toán Logic

Chức năng
Phép toán
AND
&&
OR
||
NOT
!


2.2.3 Các phép toán so sánh:

Phép toán
ý nghĩa
Ví dụ
So sánh lớn hơn
a>b
4>5 sẽ trả ra giá trị 0
>=
So sánh lớn hơn hoặc bằng
a>=b
6>=2 sẽ trả ra giá trị 1
So sánh nhỏ hơn
a<b 
6<7 sẽ trả ra giá trị 1
<=
So sánh nhỏ hơn hoặc bằng
a<=b
8<=5 sẽ trả ra giá trị 0
==
So sánh bằng nhau
a==b
6==6 sẽ trả ra giá trị 1
!=
So sánh khác nhau
a!=b
9!=9 sẽ trả ra giá trị 0

2.2.4 Phép toán thao tác Bit

Phép toán
Ý nghĩa
Ví dụ
&
Phép và (AND)
Bit_1 & Bit_2
|
Phép hoặc (OR) 
Bit_1 | Bit_2
!
Phép đảo (NOT) 
!Bit_1
^
Phép hoặc loại trừ (XOR)
Bit_1 ^ Bit_2
<< 
Dịch trái 
a<<3
>> 
Dịch phải 
a>>4
~
Lấy bù theo bit
~a

2.2.5 Phép toán kết hợp

Phép toán
Ví dụ
+=
a+=5 <=> a=a+5
-=
a-=5 <=> a=a-5
*=
a*=5 <=> a=a*5
/=
a/=5 <=> a=a/5
%=
a%=5 <=> a=a%5

2.3 Cấu trúc chương trình C

2.3.1 Cấu trúc chương trình

  * Cấu trúc:
            1. Khai báo chỉ thị tiền xử lý
            2. Khai báo các biến toàn cục
3. Khai báo nguyên mẫu các hàm
4. Xây dựng các hàm và chương trình chính

* Ví dụ:
// Khai báo chỉ thị tiền xử lý:
#include<regx51.h>
            #include<string.h>
            #define Led1 P1_0
//*********************************
// Khai báo biến toàn cục:
            Unsigned char code Led_arr[3];
            Unsigned char data dem;
            Unsigned int xdata X;
//*********************************
// Khai báo nguyên mẫu hàm
            Void delay(unsigned int n);
            bit kiemtra(unsigned int a);
//*********************************
// Xây dựng các hàm và chương trình chính:
             void delay(unsigned int n)
            {
                        Khai báo biến cục bộ;
Mã chương trình trễ;
            }
Void main()  // Chương trình chính
            {
                        Khai báo biến cụ bộ;
Mã chương trình chính;
            }
            Bit kiemtra(unsigned int a)
            {
                        Khai báo biến cục bô;
Mã chương trình kiểm tra biến a;
            }

Chú ý: Hàm không khai báo nguyên mẫu phải được xây dựng trước hàm có lời gọi hàm đó. Ở ví dụ trên do hàm “bit kiemtra(unsigned int a)” đã được khai báo nguyên mẫu hàm ở trên nên có thể xây dựng hàm ở bất kì vị trí nào trong chương trình.
Tuy nhiên chúng ta nên khai báo nguyên mẫu hàm trước hàm main, và xây dựng các hàm phụ ở sau hàm main. Như thế sẽ tạo thói quen lập trình gọn gàng hơn, và cũng tạo thuận lợi hơn cho việc xem lại code, tìm kiếm và sửa lỗi sau này.

2.3.2 Chỉ thị tiền xử lý

            Các chỉ thị tiền sử lý không phải là các lệnh của ngôn ngữ C mà là các lệnh giúp cho việc soạn thảo chương trình nguồn C trước khi biên dịch. Khi dịch một chương trình C thì không phải chính bản chương trình nguồn mà ta soạn thảo được dịch. Trước khi dịch, các lệnh tiền xử lý sẽ chỉnh lý bản gốc, sau đó bản chỉnh lý này sẽ được dịch. Có ba cách chỉnh lý được dùng là:

  + Phép thay thế #Define
  + Phép chèn tệp #Include
  + Phép lựa chọn biên dịch #Ifdef

            Các chỉ thị tiền xử lý giúp ta viết chương trình ngắn gọn hơn và tổ chức biên dịch, gỡ rối chương trình linh hoạt, hiệu quả hơn.

* Chỉ thị #Define: Chỉ thị #define cho phép tạo các macro thay thế đơn giản.

 - Cú pháp: #Define Tên_thay_thế dãy_kí_tự

Một Tên_thay_thế có thể được định nghĩa lại nhiều lần, nhưng trước khi định nghĩa lại phải giải phóng định nghĩa bằng chỉ thị:
            #Undef Tên_thay_thế

 - Ví dụ:         #define N 100

* Chỉ thị #Include: Chỉ thị #include báo cho trình biên dịch nhận nội dung của tệp khác và chèn vào tệp chương trình nguồn mà ta soạn thảo.

 - Cú pháp:
Cách 1:          #include<tên_tệp>
Cách 2:          #include“tên_tệp”

 - Ví dụ:
            Cách 1: #include<regx51.h>
Ở cách này tệp regx51.h sẽ được tìm trong thư mục INC để chèn vào chương trình nguồn.

            Cách 2: #include“regx51.h”
Ở cách này tệp regx51.h sẽ được tìm trong thư mục chứa chương trình nguồn nếu không có mới tìm trong thư mục INC.

Khi muốn chèn tệp ngoài thư viện hoặc ngoài thư mục chứa chương trình nguồn thìtên_tệp sẽ bao gồm cả đường dẫn thư mục chứa tệp.

* Chỉ thị #Ifdef: Chỉ thị #ifdef này thường dùng để biên dịch các tệp thư viện.

 - Cú pháp:
Cách 1:          #Ifdef ten_macro
                                                //Đoạn chương trình
                                    #endif
Cách 2:          #ifdef ten_macro
                                                //Đoạn chương trình 1
                                    #else
                                                //Đoạn chương trình 2
                                    #endif

Ở cách 1: nếu tên_macro đã  được định nghĩa thì “Đoạn chương trình” sẽ được dịch, ngược lại thì “Đoạn chương trình” sẽ bị bỏ qua.

* Chỉ thị #Ifndef: Chỉ thị #ifndef này thường dùng để biên dịch các tệp thư viện.

 - Cú pháp:
Cách 1:          #ifndef ten_macro
//Đoạn chương trình
                                    #endif
Cách 2:          #ifndef ten_macro
                                                //Đoạn chương trình 1
                                    #else
                                                //Đoạn chương trình 2
                                    #endif

Ở cách 1: nếu  tên_macro chưa được định nghĩa thì “Đoạn chương trình” sẽ được dịch, ngược lại thì “Đoạn chương trình” sẽ bị bỏ qua.

2.3.3 Chú thích trong chương trình

Việc viết chú thích trong trình nhằm mục đích giải thích ý nghĩa của câu lệnh, đoạn chương trình hoặc hàm hoạt động như thế nào và làm gì. Viết chú thích sẽ giúp cho người đọc có thể hiểu được chương trình dễ dàng và nhanh chóng hơn, sửa lỗi đơn giản hơn hoặc giúp cho ta xem lại chương trình cũ mà ta đã làm trở lên nhanh hơn.
Chú thích trong chương trình sẽ không ảnh hưởng đến chương trình mà ta soạn thảo vì trình dịch sẽ bỏ qua tất cả lời chú thích khi biên dịch chương trình sang mã máy.
            Lời giải thích được đặt sau dấu “//” nếu chú thích chỉ viết trên một dòng hoặc trong  cặp dấu “\*” và “*\”.

3. Các lệnh cơ bản trong C

   + Câu lệnh rẽ nhánh - if:

        - Cấu trúc 1:     if(dieu_kien)
                                    {
                                                // Đoạn chương trình
                                    }
      
        Giải thích: nếu dieu_kien đúng thì xử lí các câu lệnh bên trong còn sai thì nhảy qua.

        - Cấu trúc 2:           if(dieu_kien)
                                    {
                                    // Đoạn chương trình 1
                                    }
                                    else
                                    {
// Đoạn chương trình 2
                                    }

        Giải thích: nếu dieu_kien đúng thì xử lí “Đoạn chương trình 1” bên trong còn sai thì xử lý “Đoạn chương trình 2”.

   + Câu lệnh lựa chọn - Switch:

      Cấu trúc:            switch(biến)
                                    {
                                                case gia_tri_1:
//các câu lệnh 
break;
                                    case gia_tri_2:
//các câu lệnh 
break;
                                    ……………………………………...
                                    case gia_tri_n:
//các câu lệnh 
break;
Default:
            //các câu lệnh
                                    }

      Giải thích: Tuỳ vào biến có giá trị bằng giá trị của Case nào thì thực hiện các câu lệnh tương ứng trong Case đó, sau đó thoát khỏi cấu trúc nhờ câu lệnh “break;”. Nếu không có Case nào phù hợp thì thực hiện các câu lệnh trong default.

   + Vòng lặp xác định - For:

       Cấu trúc:           for( x=n ; điều_kiện ; phép_toán )
                                    {
                                                // các câu lệnh xử lí
                                    }

       Giải thích:  x là biến, n là giá trị xác định. Trước tiên vòng lặp sẽ gán giá trị ban đầu cho biến: x=n, rồi kiểm tra nếu điều_kiện đúng thì thực hiện các câu lệnh xử lý, sau đó thực hiện Phép_toán nhằm tác động đến điều kiện. Sau đó lại kiểm tra lại điều_kiện, nếu còn đúng thì thực hiện tiếp, nếu sai sẽ thoát khỏi vòng lặp.
            Các thành phần trong vòng for có thể không cần khai báo,for sẽ bỏ qua phần đó, nhưng vẫn phải có đủ 2 dấu “;”.

   + Vòng lặp không xác định - while:

        Cấu trúc:           while(dieu_kien)
                                    {
                                                // các câu lệnh
                                    }

Giả thích: Trước tiên chương trình sẽ kiểm tra điều_kiện, nếu đúng thì thực hiện các câu lệnh, sau đó quay lại kiểm tra điều_kiện. Còn nếu điều_kiện sai thì thoát khỏi vòng lặp ngay.

   + Vòng lặp không xác định - do while:

        Cấu trúc:          do
                                    {
                                                // các câu lệnh
                                    } while(dieu_kien);

          Giả thích: Trước tiên đoạn chương trình thực hiện các câu lệnh sau đó kiểm trađiều_kiện nếu đúng thì lặp lại thực hiện các câu lệnh tiếp, nếu sai thì thoát khỏi vòng lặp.