I. Vị trí, tính chất mô đun
- Vị trí: Đây là mô đun chuyên ngành cho học sinh ngành điện tử, viễn thông. Mô đun này phải học sau khi đã học xong các mô đun: Kỹ thuật vi xử lý.
- Tính chất: Bắt buộc
II. Mục tiêu mô đun
- Kiến thức:
+ Mô tả được cấu trúc bên trong, chức năng các chân của họ vi điều khiển PIC 18F4520.
+ Trình bày được các lệnh lập trình C của họ vi điều khiển PIC 18F4520
+ Trình bày được một số ứng dụng của họ vi điều khiển
- Kỹ năng:
+ Lập được lưu đồ thuật toán của một số ứng dụng cơ bản
+ Lập trình trên ngôn ngữ lập trình C và nạp được vào trong chip một số bài tập ứng dụng của vi điều khiển PIC 18F4520
- Về năng lực tự chủ và trách nhiệm:
+ Dự lớp đầy đủ theo quy định, rèn luyện tác phong công nghiệp, biết cách làm việc nhóm.
III. Nội dung mô đun
102 trang |
Chia sẻ: Tiểu Khải Minh | Ngày: 19/02/2024 | Lượt xem: 150 | Lượt tải: 0
Bạn đang xem trước 20 trang tài liệu Giáo trình Kỹ thuật Vi điều khiển (Trình độ: Trung cấp/Cao đẳng nghề), để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
ư viện của trình dịch.
MCC18 cung cấp một số lượng lớn các hàm giúp người lập trình có thể phát triển
các ứng dụng một các nhanh nhất. Các hàm của MCC18 chia thành ba nhóm
- Nhóm các hàm sử dụng cho các tài nguyên ngoại vi của PIC18Fxxx (ví dụ: các
hàm phục vụ giao tiếp ADC).
- Nhóm các hàm sử dụng cho các các phần cứng thông dụng có thể kết nối với
PIC18Fxxx (ví dụ LCD).
- Nhóm các hàm công dụng chung (ví dụ: các hàm tạo trễ)
- Nhóm các hàm toán học.
Các hàm được viết trong các tệp tin *.h tương ứng, người lập trình có thể xem trực
tiếp trên các tập tin này để sử dụng các hàm.
Ví dụ: Trong tập tin delay.h chứa 05 hàm tạo trễ, bao gồm:
TT Tên hàm Mô tả
1 Delay1TCY Trễ 1 chu kỳ lệnh
2 Delay10TCYx Trễ 10 chu kỳ lệnh
3 Delay100TCYx Trễ 100 chu kỳ lệnh
4 Delay1KTCYx Trễ 1000 chu kỳ lệnh
5 Delay10KTCYx Trễ 10.000 chu kỳ lệnh
Bảng3.1. Các hàm trong thư viện delay.h
Để sử dụng các hàm trong trình dịch, người lập trình cần khai báo thư viện chứa
hàm. Ví dụ: Để tạo trễ 1 giây (giả thiết bộ tạo dao động hoạt động ở chế độ HS,
thạch anh sử dụng có tần số 8Mhz) cần khai báo và viết các lệnh sau:
#include
... Delay10KTCYx(200);
//thời gian trễ: 200 * 10.000 chu kỳ lệnh * 0.5 uS = 2 s
Các lệnh xử lý bit.
58
Lệnh “And” : Cú pháp: & Ví dụ:
unsigned char x,y,z; x=0b11110000; y=0b11101111;
y=y&x; //y=0b11100000
z&=0b1111110; //z=*******0 (xóa bit có giá trị thấp nhất của biến z)
Lệnh “Or” : Cú pháp: | Ví dụ:
unsigned char x,y,z; x=0b11110000; y=0b11101111;
y=y|x; //y=0b11111111
z|=0b1000000; //z=1******* (thiết lập bit có giá trị cao nhất của biến z)
Lệnh “Xor” : Cú pháp: ^ Ví dụ:
unsigned char x,y;
x=0b11110000; y=0b11101111;
y=y^x; //y=0b00011111
Lệnh “Not” :
Cú pháp: ~
Ví dụ:
#define xung PORTBbits.RB0 xung=1;
~xung; //xung=0
Lệnh dịch trái : Cú pháp: << Ví dụ:
unsigned char x; x=0b11110000;
x=x<<2; //x=0b11000000
Lệnh dịch phải : Cú pháp: >> Ví dụ:
unsigned char x; x=0b11110000;
x=x>>2; //x=0b00110000
Các cấu trúc điều khiển, rẽ nhánh.
Trong khuôn khổ giáo trình này, chúng tôi cũng chỉ đề cập tới một số các cấu trúc
cơ bản, các cấu trúc khác trong ngôn ngữ lập trình C cũng hoàn toàn có giá trị
trong MCC18.
Cấu trúc While :
while(biểu thức điều kiện)
{
các câu lệnh;
}
Nếu biểu thức điều kiện đúng, chương trình sẽ thực hiện các câu lệnh bên trong
thân while
Ví dụ:
#include void main()
{
while(1)
{
while(PORTD==0xFE)
{ PORTC=0x02;
}
59
}
}
Cấu trúc IF, IF...ELSE:
if(Biểu thức điều kiện)
{
các câu lệnh;
}
else
{
các câu lệnh;
}
Ví dụ:
#include void main()
{
while(1)
{ if(PORTD==1)
{ PORTC=0x02;
}
}
}
Cấu trúc SWITCH:
switch(biểu thức nguyên):
{
case n1:
các câu lệnh; case n2:
các câu lệnh; case nk:
các câu lệnh; default:
các câu lệnh;
}
Cấu trúc switch cho phép ta căn cứ vào giá trị của biểu thức nguyên để chọn một
trong những cách thực hiện.
Ví dụ:
#include
unsigned char i; //khai báo biến kiểu byte void main()
{
while(1)
{
i=PORTD; //đọc PORTD switch(i):
{
case 0: PORTC=1;
case 4:
PORTC =7;
60
case 6:
PORTC =3;
}
}
}
Cấu trúc For
for(biểu thức1;biểu thức2;biểu thức3)
{
các câu lệnh;
}
Ví dụ:
#include
#include void main()
{
int i;
for (i=1;i<=1000;++i)
{
PORTD=0x00; Delay10KCTYx(200); PORTD=0xFF; Delay10KCTYx(200);
}
}
Cấu trúc DoWhile :
do
{
các câu lệnh;
}
while(biểu thức điều kiện); Ví dụ:
#include
#include
#define led PORTAbits.RA5
#define contact PORTAbits.RA6 void main ()
{
do
{
led=~led; Delay10KTCYx(100);
} //led nháy liên tuc while(contact!=1); //đến khi RA6=1
}
3.3. Phần mềm MPLAB_IDE_v8_70
Sử dụng MPLAB
Việc thực hiện các bài thực hành về vi điều khiển PIC được thực hiện trên phần
mềm MPLAB. Các chức năng chính của MPLAB bao gồm:
61
- Biên dịch từ mã nguồn C hoặc ASM
- Mô phỏng, gỡ rối.
- Nạp chương trình.
Các ví dụ dưới đây sẽ đề cập chi tiết tới cách sử dụng, khai thác các tính năng của
MPLAB.
Ví dụ 1: Viết chương trình xuất 0x00 ra PORTB sau đó xuất 0x55 và lặp lại. Sử
dụng ngôn ngữ ASM để viết chương trình, biên dịch ra file HEX, mô phỏng hoạt
động của vi điều khiển (Debugger) sử dụng MPLAB SIM.
Tạo Project ASM trên MPLAB.
Bước 1: Tạo Project và lựa chọn chip.
- Mở MPLAB IDE bằng cách click vào biểu tượng
- Cửa sổ khi khởi động của MPLAB IDE như hình 1.18
Hình 3.4. Cửa sổ khởi động của MPLAB
- Vào menu [Project] >> [Project Winzard] để tạo một Project mới.
Hình 3.5. Tạo Project (1)
- Chọn [Next] để chuyển sang cửa sổ chọn chip.
62
Hình 3.6. Tạo Project (2)
- Tại cửa sổ chọn chip (Step One: Slect a device) lựa chọn chip 18F4520 trong
ô lựa chọn Device.
Hình 3.7. Tạo Project (3): Chọn vi điều khiển
Bước 2: Lựa trình biên dịch (Chọn trình biên dịch ASM hoặc MCC18 hoặc CCS).
Nếu chọn trình dịch hợp ngữ (ASM): Tại cửa sổ Step Two, cửa sổ lựa chọn công
cụ biên dịch [Active Toolsuite] lựa chọn công cụ biên dịch là [Microchip MPASM
Toolsuite] và trong ô [Toolsuite Contents] chọn [MPASM Assembler
(mpasmwin.exe) v5.41], tiếp tục nhấn [Next] để sang bước 3
63
Hình 3.8. Tạo Project (4): Chọn trình dịch hợp ngữ
- Nếu chọn trình dịch C (MCC18): Tại cửa sổ Step Two, cửa sổ lựa chọn công
cụ biên dịch [Active Toolsuite] lựa chọn công cụ biên dịch là [Microchip C18
Toolsuite] và trong ô [Toolsuite Contents] chọn [MPASM C18 C Compiler
(mcc18.exe) v3.43], tiếp tục nhấn [Next] để sang bước 3.
Hình 3.9. Tạo Project (4): Chọn trình dịch C
Bước 3: Đặt tên cho Project và chọn đường dẫn cho Project.
64
Chú ý: Chọn tên Project và đường dẫn nên gợi nhớ theo nội dung của bài lập trình.
- Đặt tên đường dẫn như hình 1.24.
Hình 3.10. Tạo Project (5): Đặt tên, đường dẫn Project
- Chọn [Next] sẽ xuất hiện cửa sổ hỏi lại bạn có muốn tạo thư mục theo đường
dẫn này không. Chọn [OK] để tạo thư mục và chuyển sang bước 4.
Hình 3.11. Tạo Project (5):Tạo tên, đường dẫn Project
Bước 4: Thêm file vào Project và kiểm tra lại các thông tin của Project.
- Cửa sổ bước thứ 4 (Step Four) sử dụng để thêm các file vào Project. Ở bước
này chúng ta nhấn [Next] sẽ sang cửa sổ Summary (hình 1.26).
- Ở cửa sổ Summary hiển thị tóm tắt các thông tin của Project chúng ta vừa tạo,
kiểm tra lại các thông tin và nhấn [Finish] để kết thúc.
65
Hình 3.12. Các thông tin của Project vừa tạo
Bước 5: Tạo file mã nguồn.
- Vào menu [File] >> [New] để tạo file mã nguồn
Hình 3.13. Tạo file mã nguồn từ menu File
- Vào menu [File] >> [Save As] để lưu file mã nguồn.
Hình 3.14. Đặt tên, lưu file mã nguồn ASM
66
Chú ý: Chọn đường dẫn cho file ASM là được dẫn của Project vừa tạo bằng cách
trong mục Jump to chọn Project Directory, đặt tên của file ASM với phần mở
rộng .asm, tích chọn Add File To Project để thêm file ASM vào Project vừa tạo.
Trong trường hợp file mã nguồn là file C, tên file cần có phần mở rộng .C (hình
1.29)
Hình 3.15. Đặt tên, lưu file mã nguồn C
- Viết chương trình vào file mã nguồn vừa tạo.
Hình 3.16. Viết chương trình
Biên dịch và sửa lỗi chương trình.
- Chọn menu [Project] >> [Buil All] hoạc nhấn [Ctrl + F10] hoạc click vào biểu
tượng Buil All trên thanh công cụ biên dịch.
Hình 3.17. Chọn BUILD ALL
67
- Sau khi biên dịch sẽ xuất hiện thông báo BUILD SUCCEEDED nếu như biên
dịch ra file Hex thành công, tức là chương trình không có lỗi.
Hình 3.18. Chương trình được biên dịch thành công
- Sau khi biên dịch sẽ xuất hiện thông báo BUILD FAILED nếu như biên dịch
không thành công, có lỗi cú pháp. Click vào lỗi [Error[113] D:\MAY1.] trình
biên dịch IDE MPLAB sẽ trỏ vào lỗi trên cửa sổ viết mã chương trình, tiến hành
sửa lỗi và biên dịch lại cho tới khi tao được file Hex (hình 1.31).
Hình 3.19. Sửa lỗi chương trình
68
Bài 4: PORT XUẤT NHẬP, TIMER – COUNTER
* Mục tiêu của bài:
- Trình bày được về cấu hình, thông số điện áp, dòng của các port vi điều khiển
PIC 18f4520
- Trình bày được cách truy xuất port điều khiển PIC 18f4520
- Lập trình được các ứng dụng truy xuất port bằng ngôn ngữ C với LED đơn, 7
thanh, LCD
- Lập trình được các ứng dụng Timer/counter của vi điều khiển PIC 18f4520
- Cẩn thận, sáng tạo đảm bảo an toàn thiết bị và dụng cụ.
- Nghiêm túc, khoa học, tỉ mỷ.
* Nội dung của bài:
4.1. Cấu hình, thông số điện áp, dòng của các port
Hoạt động vào/ra
Vi điều khiển PIC 18F4520 có 36 chân vào/ra được chia thành 5 cổng là PORTA,
PORTB, PORTC, PORTD có 8 chân và PORTE có 4 chân. Các chân vào/ra của vi
điều khiển PIC 18F4520 mang nhiều chức năng, nó có thể được thiết lập là chân
vào/ra dữ liệu hay là các chân chức năng đặc biệt của các bộ ngoại vi, sử dụng các
thanh ghi điều khiển của ngoại vi để lựa chọn chức năng cho các chân.
Mỗi cổng vào/ra của vi điều khiển PIC 18F4520 có 3 thanh ghi để điểu khiển hoạt
động:
a. Thanh ghi PORT là thanh ghi dữ liệu, được định địa chỉ theo byte và theo bit, sử
dụng để đệm đọc/ghi dữ liệu trên các cổng.
b. Thanh ghi LAT là thanh ghi chốt dữ liệu đầu ra (bằng 1 đầu ra chốt mức 1, bằng
0 đầu ra chốt mức 0).
c. Thanh ghi TRIS là thanh ghi lựa chọn hướng dữ liệu (bằng 0 chiều ra, bằng 1
chiều vào).
Cấu trúc chung của một chân vào/ra
69
Hình 4.1. Thể hiện cấu trúc chung của một chân vào/ra.
a. PORTA
PORTA gồm 8 bit tương ứng với 8 chân được ký hiệu từ RA0 đến RA7. Các chân
của PORTA có thể đọc/ghi theo từng bit hoặc cả byte.
Chức năng các chân của PORTA
Chân Chức
năng
Thanh
ghi I/O
Kiểu
I/O Mô tả
RA0/AN0 RA0 0 O DIG Chiều ra dữ liệu sử dụng bit LATA; không bị ảnh
hưởng bởi đầu vào tương tự.
1 I TTL Chiều vào dữ liệu sử dụng bit PORTA.
AN0 1 I ANA Đầu vào kênh 0 của bộ biến đổi A/D hoặc chân đầu vào
C1- của bộ so sánh. Mặc đinh khi Reset POR.
RA1/AN1 RA1 0 O DIG Chiều ra dữ liệu sử dụng bit LATA; không bị ảnh
hưởng bởi đầu vào tương tự.
1 I TTL Chiều vào dữ liệu sử dụng bit PORTA.
AN1 1 I ANA Đầu vào kênh 1 của bộ biến đổi A/D hoặc chân đầu vào
C2- của bộ so sánh. Mặc đinh khi Reset POR.
RA2/AN2/ VREF-
/CVREF
RA2 0 O DIG Chiều ra dữ liệu sử dụng bit LATA; Cấm chức năng
này khi đầu ra CVREF được cho phép.
1 I TTL Chiều vào dữ liệu sử dụng bit PORTA. Chức năng
này sẽ bị cấm khi chức năng tương tự hoặc đầu ra CVREF
được cho phép.
AN2 1 I ANA Đầu vào kênh 2 của bộ biến đổi A/D hoặc chân đầu vào
C2+ của bộ so sánh. Mặc đinh khi Reset POR.
VREF- 1 I ANA A/D và đầu vào điện áp tham chiếu mức thấp bộ so sánh.
CVREF x O ANA Đầu ra điện áp tham chiếu bộ so sánh.
RA3/AN3/VREF+ RA3 0 O DIG Chiều ra dữ liệu sử dụng bit LATA.
1 I TTL Chiều vào dữ liệu sử dụng bit PORTA; Chức năng
này bị cấm khi đầu vào tương tự được cho phép.
AN3 1 I ANA Đầu vào kênh 3 của bộ biến đổi A/D hoặc hoặc chân đầu
vào C1+ của bộ so sánh. Mặc đinh khi Reset POR.
VREF+ 1 I ANA A/D và đầu vào điện áp tham chiếu mức cao bộ so sánh.
RA4/T0CKI/C1OUT RA4 0 O DIG Chiều ra dữ liệu sử dụng bit LATA.
1 I ST Chiều vào dữ liệu sử dụng bit PORTA; mặc định khi
reset POR.
T0CKI 1 I ST Cấp xung cho Timer0.
70
C1OUT 0 O DIG Đầu ra 1 bộ so sánh; ưu tiên hơn vào/ra dữ liệu.
RA5/AN4/SS/
HLVDIN/C2OUT
RA5 0 O DIG Chiều ra dữ liệu sử dụng bit LATA; không ảnh
hưởng bởi đầu vào tương tự.
1 I TTL Chiều vào dữ liệu sử dụng bit PORTA; Chức năng
này bị cấm khi cho phép đầu vào tương tự.
AN4 1 I ANA Đầu vào kênh 4 của bộ biến đổi A/D. mặc định khi reset
BOR.
SS 1 I TTL Đầu vào SS(lựa chọn t/b tớ) của Module MSSP.
HLVDIN 1 I ANA Đầu vào của Module phát hiện điện áp cao/thấp
(High/Low-Voltage Detect).
C2OUT 0 O DIG Đầu ra 2 bộ so sánh;được ưu tiên hơn chức năng vào/ra dữ
liệu.
OSC2/CLKO/RA6 RA6
0 O DIG
Chiều ra dữ liệu sử dụng bit LATA. Chức năng này
chỉ được cho phép ở các chế độ RCIO, INTIO2 and
ECIO.
1 I TTL Chiều vào dữ liệu sử dụng bit PORTA. Chế độ này
chỉ được cho phép ở các chế độ RCIO, INTIO2 và ECIO.
OSC2 x O ANA Kết nối với bộ phát xung chính ( ở các chế độ XT, HS và
LP).
CLKO x O DIG Chân phát xung hệ thống(FOSC/4) ở RC, INTIO1 và EC.
OSC1/CLKI/RA7 RA7 0 O DIG Chiều ra dữ liệu sử dụng bit LATA. Chức năng này
bị cấm ở chế độ dao động ngoài.
1 I TTL Chiều vào dữ liệu sử dụng bit PORTA. Chức năng
này bị cấm ở chế độ dao động ngoài.
OSC1 x I ANA Kêt nối với bộ dao động ngoài.
CLKI x I ANA Kết nối với nguồn xung bên ngoài.
Bảng 4.1. Chức năng các chân trên PORTA
Chú thích:
DIG = Digital level output(đầu ra số); TTL = đệm đầu vào chuẩn TTL(Transistor -
Transistor Logic ); ST = đệm đầu vào sử dụng Schmitt Trigger; ANA = vào/ra tương
tự; x= không xác định; I=Input (vào); O=Output (ra).
Chú ý: Hai chân RA6 và RA7 còn phụ thuộc vào cấu hình bộ phát xung hệ thống
· Các thanh ghi liên quan đến PORTA
Các thanh ghi liên quan đến PORTA gồm 6 thanh ghi sau:
- PORTA: Thanh ghi dữ liệu PORTA.
- LATA: Thanh ghi chốt dữ liệu đầu ra của PORTA.
- TRISA: Thanh ghi lựa chọn hướng dữ liệu của PORTA(bit tương ứng trên
thanh ghi đặt bằng 0 thì chân tương ứng có chiều ra, bằng 1 là chiều vào).
4.2. Các ứng dụng dùng port điều khiển của vi điều khiển 8 bit.
4.2.1. Các ứng dụng điều khiển led đơn.
a. Điều khiển 8 LED đơn nhấp nháy tại PORTA và PORTB
/* Khai bao thu vien */
#include
#include /* Tao tre */
/* Cau hinh phan cung chi PIC */
71
#pragma config OSC = HS /* Cau hinh bo tao dao dong Datasheet */
#pragma config LVP = OFF /* Cap nguon cho mach nap va dieu khien */
#pragma config WDT = OFF /* Tu dong RS chip theo thoi gian */
#pragma config MCLRE = OFF //Rs liên tuc chân MCLRE
/* Dinh nghia chi PIN va PORT */
#define H1 PORTAbits.RA0
#define H2 PORTAbits.RA1
#define H3 PORTAbits.RA2
#define H4 PORTAbits.RA3
#define H5 PORTAbits.RA4
#define H6 PORTAbits.RA5
#define H7 PORTAbits.RA6
#define H8 PORTAbits.RA7
#define C1 PORTBbits.RB0
#define C2 PORTBbits.RB1
#define C3 PORTBbits.RB2
#define C4 PORTBbits.RB3
#define C5 PORTBbits.RB4
#define C6 PORTBbits.RB5
#define C7 PORTBbits.RB6
#define C8 PORTBbits.RB7
unsigned char cot[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
unsigned char hang[]={0xff,0x03,0xc9,0xcc,0xcc,0xc9,0x03,0xff};
unsigned int i,j;
void main (void)
{
/* Cau hinh ban dau (vao/ra, dinh thoi ...*/
TRISC = 0b00000000;
TRISB = 0b00000000; //RE0:RE2 chieu ra
//ADCON1=0x0f; //0b000001111
while(1) //Lap vo han
{
PORTC=0x02;
PORTB=0x03;
Delay10KTCYx(1);
PORTC=0x04;
72
PORTB=0x01;
Delay10KTCYx(1);
PORTC=0x08;
PORTB=0xcc;
Delay10KTCYx(1);
PORTC=0x10;
PORTB=0xcc;
Delay10KTCYx(1);
PORTC=0x20;
PORTB=0x01;
Delay10KTCYx(1);
PORTC=0x40;
PORTB=0x03;
Delay10KTCYx(70);
}
}
b. Điều khiển 4 công tác chuyển mạch tại PORTB cho 3 LED đơn tại PORTE
/* Khai bao thu vien */
#include
#include /* Tao tre */
/* Cau hinh phan cung chi PIC */
#pragma config OSC = HS /* Cau hinh bo tao dao dong Datasheet */
#pragma config LVP = OFF /* Cap nguon cho mach nap va dieu khien */
#pragma config WDT = OFF /* Tu dong RS chip theo thoi gian */
#pragma config MCLRE = ON
/* Dinh nghia chi PIN va PORT */
#define SW1 PORTBbits.RB0
#define SW2 PORTBbits.RB1
#define SW3 PORTBbits.RB2
#define SW4 PORTBbits.RB3
#define LED0 PORTEbits.RE0
73
#define LED1 PORTEbits.RE1
#define LED2 PORTEbits.RE2
/* Khai bao bien va mang du lieu */
int x;
char mang [] = {1, 2, 3, 4};
/* Chuong trinh chinh*/
void main (void)
{
/* Cau hinh ban dau (vao/ra, dinh thoi ...*/
TRISB = 0b00001111; //RB0:RB3 chieu vao
TRISE = 0b00000000; //RE0:RE2 chieu ra
ADCON1=0x0f; //0b000001111
while(1) //Lap vo han
{
LED0=~SW1;
LED1=~SW2;
LED2=~SW3;
if (SW4==0)
{
LED0=1;
Delay10KTCYx(200);
LED1=1;
Delay10KTCYx(200);
LED2=1;
Delay10KTCYx(200);
else
{
LED0=LED1=LED2=0;
}
}
}
4.2.2. Các ứng dụng điều khiển led 7 đoạn trực tiếp.
#include
#include
#pragma config OSC=HS
74
#pragma config WDT = OFF
#pragma config MCLRE = OFF
unsigned char Code7Seg[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D,
0x07, 0x7F, 0x6F};
void main()
{
unsigned dem,chuc,dvi;
unsigned int i;
TRISC=0x00;
TRISD=0x00;
ADCON1=0x0f;
while(1)
{
for(dem=0;dem<99;dem++)
{
chuc=dem/10;
dvi=dem%10;
for(i=0;i<50;i++)
{
PORTC=Code7Seg[chuc];
PORTDbits.RD0=0;
Delay1KTCYx(10);
PORTDbits.RD0=1;
PORTC=Code7Seg[dvi];
PORTDbits.RD1=0;
Delay1KTCYx(10);
PORTDbits.RD1=1;
}
}
}
}
4.2.3. Các ứng dụng điều khiển led 7 đoạn quét.
/*
Ten Project: Quet 2 led 7 doan Anode chung
Ket noi: Ta su dung PORTA de dieu khien 2 led, PORTD (RA0...RA7) ket noi
den 7 segment (a....dp)
*/
/*Tao ma hex cho led 7 doan*/
unsigned short mask(unsigned short num)
{
75
switch (num)
{
case 0 : return 0xc0;
case 1 : return 0xf9;
case 2 : return 0xa4;
case 3 : return 0xb0;
case 4 : return 0x99;
case 5 : return 0x92;
case 6 : return 0x82;
case 7 : return 0xf8;
case 8 : return 0x80;
case 9 : return 0x90;
}
}
unsigned short mask(unsigned short num);
unsigned short digit_no, digit10, digit1, digit, i;
void interrupt()
{
if (digit_no==0)
{
PORTA = 0; // Turn off both displays
PORTD = digit1; // Set mask for displaying ones on PORTD
PORTA = 1; // Turn on display for ones (LSD)
digit_no = 1;
}
else
{
PORTA = 0; // Turn off both displays
PORTD = digit10; // Set mask for displaying tens on PORTD
PORTA = 2; // Turn on display for tens (MSD)
digit_no = 0;
}
TMR0 = 0; // Reset counter TMRO
INTCON = 0x20; // Bit T0IF=0, T0IE=1
}
void main()
{
OPTION_REG = 0x80; // Set timer TMR0
TMR0 = 0;
INTCON = 0xA0; // Disable interrupt PEIE,INTE,RBIE,T0IE
PORTA = 0; // Turn off both displays
76
TRISA = 0; // All port A pins are configured as outputs
PORTD = 0; // Turn off all display segments
TRISD = 0; // All port D pins are configured as outputs
do
{
for (i = 0; i<=99; i++) { // Count from 0 to 99
digit = i % 10u;
digit1 = mask(digit); // Prepare mask for displaying ones
digit = (char)(i / 10u) % 10u;
digit10 = mask(digit); // Prepare mask for displaying tens
Delay_ms(200);
}
} while (1); // Endless loop
}
4.2.4. Giao tiếp vi điều khiển 8 bit với LCD
a. Hiển thị giờ, phút giây trên LCD 16x2
/* Khai bao thu vien */
#include
#include /* Tao tre */
#include
/* Cau hinh phan cung chi PIC */
#pragma config OSC = HS /* Cau hinh bo tao dao dong Datasheet */
#pragma config LVP = OFF /* Cap nguon cho mach nap va dieu khien */
#pragma config WDT = OFF /* Tu dong RS chip theo thoi gian */
#pragma config MCLRE = OFF
/* Dinh nghia chi PIN va PORT */
#define RS PORTAbits.RA0
#define RW PORTAbits.RA1
#define EN PORTAbits.RA2
#define DATA PORTD
/* Khai bao bien va mang du lieu */
int x;
char mang [32];
char hh;
77
char mm;
char ss;
void ghilenh(char lenh)
{
RS=0; //ghi lenh
RW=0; //dia chi
EN=1;
DATA=lenh;
EN=0;
Delay10KTCYx(1);
}
void ghidulieu(char dulieu)
{
RS=1; //ghi du lieu
RW=0; //dia chi
EN=1;
DATA=dulieu;
EN=0;
Delay10KTCYx(1);
}
void cauhinhLCD(void)
{
ghilenh(0x03);// che do ban dau
ghilenh(0x38);// cau hinh cho LCD
ghilenh(0x06); // thiet lap che do hien thi
ghilenh(0x0f);// bat/tat hien thi
ghilenh(0x14);
ghilenh(0x01);//xoa man hinh
}
//ham ghi chuoi
void ghichuoi(char *str)
{
while(*str)
{
ghidulieu(*str);
str++;
}
}
/* Chuong trinh chinh*/
78
void main (void)
{
/* Cau hinh ban dau (vao/ra, dinh thoi ...*/
TRISA = 0b00000000; //RB0:RB3 chieu vao 0x00
TRISD = 0b00000000; //RE0:RE2 chieu ra
ADCON1=0x0f; //0b000001111
cauhinhLCD();
hh = 16;
mm = 35;
ss = 16;
while(1) //Lap vo han
{
ghilenh(0x80);
sprintf(&mang[0], "GIO :PHUT :GIAY");
ghichuoi(&mang[0]);
ghilenh(0xC0);
sprintf(&mang[0], "%2d : %2d : %2d ", hh, mm, ss);
ghichuoi(&mang[0]);//
Delay10KTCYx(200);
ss++;
if(ss>=60)
{
ss = 0;
mm++;
}
if(mm>=60)
{
mm = 0;
hh++;
}
if(hh==24)
{
hh=0;
mm=0;
ss=0;
}
}
}
79
4.3. Timer/counter của vi điều khiển PIC 18f4520
4.3.1. Khảo sát các timer của vi điều khiển PIC 18f4520.
a. Giới thiệu
Các bộ định thời (timer) được sử dụng rất rộng rãi trong các ứng dụng đo lường và
điều khiển. Có thể coi một bộ định thời n bit là một bộ đếm n bit được tạo ra bởi n
flip-flop mắc nối tiếp với nhau. Đầu vào của bộ định thời chính là đầu vào của flip-
lop đầu tiên, đầu ra báo tràn (over flow) của bộ định thời phản ánh trạng thái tràn
của nó. Đầu ra của các flip-flop phản ánh giá trị hiện thời của bộ đếm. Tuỳ thuộc
vào ứng dụng, đầu vào bộ định thời có thể là nguồn xung lấy từ xung nhịp của vi
điều khiển hoặc nguồn xung từ bên ngoài đưa đến. Vi điều khiển PIC18F4520 có
bốn bộ định thời. Các bộ định thời được dùng để định khoảng thời gian hoặc đếm
các sự kiện.
Trong các ứng dụng định khoảng thời gian, timer được lập trình sao cho sau một
khoảng thời gian timer sẽ tràn. Khi tràn, cờ tràn của timer được đặt bằng 1. Căn cứ
vào trạng thái của cờ tràn, người lập trình có thực hiện một thao tác điều khiển hoặc
đo lường nào đó để tạo ra các ứng dụng đo lường/điều khiển theo thời gian.
Đếm sự kiện dùng để xác định số lần xảy ra của một sự kiện. Trong ứng dụng này
người ta tìm cách quy các sự kiện thành sự chuyển mức từ 1 xuống 0 hoặc ngược lại
trên các lối vào của các bộ timer. Sau mỗi sự chuyển mức, giá trị của timer sẽ thay
đổi (tăng hoặc giảm đi 1 tùy theo chế độ đếm tiến hoặc lùi). Giá trị hiện thời của
timer sẽ chính là số sự kiện đã xảy ra.
Dựa trên hai chức năng chính này, trong thực tế, các bộ timer có thể được dùng để
tạo xung, hẹn thời gian bật/tắt của một thiết bị, điều chế độ rộng xung (PWM –
Pulse Width Modulation), đo tần số, đếm sản phẩm, tạo tốc độ baud cho PORT nối
tiếp của vi điều khiển.
b. Timer0
Timer0 là bộ timer 16 bit tuy nhiên bộ timer có thể hoạt động được cả ở chế độ 8
bit. Timer0 có thể dùng làm bộ đếm hoặc định thời với nguồn xung từ bộ dao động
trên chíp hoặc từ ngoài đưa vào qua chân T0CLKI.
Các thanh ghi của Timer0
- Thanh ghi điều khiển Timer0: T0CON
R/W-1 R/W-1 R/W-1 R/W-1 R/W-1 R/W-1 R/W- R/W-1
TMR0O T08BIT T0CS T0SE PSA T0PS2 T0PS T0PS0
bit 7
Ghi chú:
R = Cho phép đọc W = Cho phép ghi U = Không sử dụng, đọc bằng ‗0‘
-n = Reset - POR ‗1‘ = Được thiết lập ‗0‘ =Được xóa -x = Reset không xác định
Bit 7 TMR0ON: Bit điều khiển Bật/Tắt Timer.
1 = Cho phép Timer0
80
0 = Dừng Timer0
Bit 6 T08BIT: Bit lựa chọn 8-bit /16-bit của Timer0
1 = Timer0 được cấu hình là bộ đếm 8-bit
0 = Timer0 được cấu hình là bộ đếm 16-bit
Bit 5 T0CS: Bit lựa chọn nguồn xung cấp cho Timer0
1 = Nguồn xung từ chân T0CKI
0 = Nguồn xung hệ thống (CLKO)
Bit 4 T0SE: Bit lựa chọn sườn xung đếm cho Timer0
1 = Lựa chọn sườn âm trên chân T0CKI
0 = Lựa chọn sườn dương trên chân T0CKI
Bit 3 PSA: Bit thiết lập bộ chia tần đầu vào
1 = Xung cấp vào Timer0 không qua bộ chia tần.
0 = Xung cấp vào Timer0 qua bộ chia tần(Prescaler).
Bit 2 T0PS: Bit lựa chọn hệ số chia tần 111 = 1:256
110 = 1:128
101 = 1:64
100 = 1:32
011 = 1:16
010 = 1:8
001 = 1:4
000 = 1:2
- Thanh ghi chứa byte thấp của Timer0: TMR0L (8 bit, không định địa chỉ bit)
- Thanh ghi chứa byte cao của Timer0: TMR0H (8 bit, không định địa chỉ bit)
- Thanh ghi điều khiển ngắt: INTCON (xem phần ngắt và xử lý ngắt) Các thanh
ghi liên quan tới Timer0 :
Tên Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
TMR0L Thanh ghi chứa giá trị đếm byte thấp của Timer0
TMR0H Thanh ghi chứa giá trị đếm byte cao của Timer0
INTCON GIE/GIEH PEIE/GIEL TMR0IE INT0IE RBIE TMR0IF INT0IF RBIF
T0CON TMR0ON T08BIT T0CS T0SE PSA T0PS2 T0PS1 T0PS0
TRISA RA7(1) RA6(1) RA5 RA4 RA3 RA2 RA1 RA0
Chế độ hoạt động của Timer0
· Chế độ 8 bit
Ở chế độ 8 bit, giá trị của Timer0 chỉ chứa trong thanh ghi TMR0L, khi tràn (khi có
sự chuyển giá trị đếm từ 255 sang 0), cờ ngắt tràn TMR0IF được đặt bằng 1.
Nguồn xung cấp cho Timer0 có thể lấy từ 2 nguồn:
- Khi bit T0CS=0, xung được lấy từ bộ dao động trên chíp, tần số của xung bằng ¼
tần số của bộ dao động thạch anh được sử dụng (Fosc/4).
- Khi bit T0CS=1, xung được lấy từ ngoài đưa vào qua chân T0CLKI (T0CLKI
81
pin). Trong trường hợp này Timer0 thường được sử dụng để đếm xung đưa vào từ
chân T0CLKI.
Bit PSA trong thanh ghi T0CON được dùng để lựa chọn việc sử dụng/không sử
dụng bộ chia tần số:
Khi PSA=1, giá trị của Timer0 sẽ tăng lên 1 đơn vị sau mỗi chu kỳ lệnh (tương
đương 4 chu kỳ thạch anh) hoặc sau 1 chu kỳ xung ngoài đưa đến chân T0CLKI.
Hình 4.2. Mô tả hoạt động Timer0 ở chế độ 8 bit
- Khi PSA=0, xung từ 2 nguồn trên sẽ qua bộ chia tần số với 8 hệ số chia từ 2 đến
256 tùy thuộc vào giá trị của các bit T0PS2, T0PS1, T0PS0 trong thanh ghi
T0CON. Chẳng hạn khi T0PS2 T0PS1 T0PS0 = 011, hệ số chia sẽ là 1/16, khi đó
sau 16 chu kỳ xung ngoài đưa đến chân T0CLKI hoặc sau 16 chu kỳ lệnh giá trị
trong TMR0L mới tăng lên 1 đơn vị.
Bit T0SE dùng để chọn kiểu tác động của xung ngoài:
- Khi T0SE=0, giá trị của TMR0L sẽ tăng 1 đơn vị sau mỗi sườn xung dương đưa
tới chân T0CLKI.
- Khi T0SE=1, giá trị của TMR0L sẽ tăng 1 đơn vị sau mỗi sườn xung âm đưa tới
chân T0CLKI.
Khi sử dụng nguồn xung ngoài (sử dụng Timer0 như bộ đếm), nguồn xung này cần
có một quá trình đồng bộ với xung nội (internal clock), quá trình này thường mất 2
chu kỳ lệnh (tương đương 8 chu kỳ thạch anh).
Chế độ 16 bit
Ở chế độ 16 bit, giá trị của Timer0 được chứa trong thanh ghi TMR0L (byte thấp) và
TMR0 High Byte (byte cao), TMR0H đóng vai trò là bộ đệm của byte cao trong quá
trình ghi đọc. Khi tràn (khi có sự chuyển giá trị đếm từ 65535 sang 0), cờ ngắt tràn
TMR0IF được đặt bằng 1.
Việc lựa chọn nguồn xung, kiểu tác động của xung và bộ chia tần hoàn toàn giống
chế độ 8 bit (hình 2.4).
Khi ghi giá trị cho Timer0, byte thấp được ghi vào TMR0L còn byte cao sẽ được
được ghi từ TMR0H ngay khi byte thấp được ghi. Khi đọc giá trị của Timer0, byte
thấp được đọc từ TMR0L, byte cao sẽ xuất hiện trong TMR0H ngay khi đọc byte
thấp. Với thiết kế này byte thấp và byte cao của Timer được ghi/đọc đồng thời.
82
Hình 4.3. Mô tả hoạt động Timer0 ở chế độ 16 bit
Ngắt Timer0.
Ngắt Timer0 xảy ra khi Timer0 tràn. Ở chế độ đếm 8 bit sự kiện tràn xảy ra khi có
sự chuyển số đếm từ FFH sang 00H và FFFFH sang 0000H ở chế độ 16 bit. Khi tràn
cờ ngắt tương ứng của Timer0 (TMR0IF- bit 2 thanh ghi INTCON) được thiết lập.
Ngắt Timer0 được cho phép ngắt bởi bit TMR0IE (bit 5 thanh ghi INTCON). Mức
ưu tiên ngắt Timer0 được đặt bởi bit TMR0IP (bit 2, thanh ghi INTCON2).
Ví dụ ứng dụng Timer0.
Ví dụ 1: Sử dụng Timer0 ở chế độ timer 8 bit, tạo xung 1KHz, nguồn xung lấy từ
bộ dao động trên chíp, xung tạo trên chân RD0, tần số thạch anh sử dụng là 8Mhz.
Cách 1: Sử dụng các thanh ghi.
#include
#include
#pragma config OSC = HS // xung he thong o che do HS
#pragma config WDT = OFF // tat che do watchdog
#pragma config MCLRE = ON // cho phep Reset tren chan MCLR
#define xung PORTDbits.RD0 void main()
{
//Khoi tao timer0
T0CONbits.T08BIT=1; //Timer 0 che do 8 bit T0CONbits.
PSA=0; //su dung bo chia tan (Prescaler) T0CONbits.
T0PS2=0; //He so chia bang 4 T0CONbits.T0PS1=0;
T0CONbits.T0PS0=1;
T0CONbits.T0CS=0; //Su dung xung tu Fosc/4 ADCON1=0x0f;
TRISD = 0x00; // thiet lap dau ra cho portD xung=1;
while(1)
{
xung=~xung;
TMR0L =(256 - 250); //sau 500uS, timer tran (*)
T0CONbits.TMR0ON = 1; // cho phep Timer hoat dong
while(!INTCONbits.TMR0IF); // cho co tran T0CONbits.
83
TMR0ON = 0; // dung Timer INTCONbits.TMR0IF=0;
// xoa co tran
}
}
(*): Tần số xung cấp cho timer: Fosc/4= 2Mhz => chu kỳ = 0,5uS. Chu kỳ xung sau
bộ chia tần trước (Prescaler) = 0,5uS * 4 = 2uS. Giá trị khởi tạo cho timer là 256-
250 => sau 250 xung, timer tràn, tương đương khoảng thời gian: 250*2 uS= 500 uS.
Cách 2: Sử dụng các các hàm hỗ trợ có sẵn trong trình dịch.
#include
#include
#include
void main()
{
//Khoi tao timer0
OpenTimer0( TIMER_INT_OFF & //khong su dung ngat T0_8BIT & //che do 8bit
T0_SOURCE_INT & //su dung xung tu Fosc/4
T0_PS_1_4 ); //he so chia truoc = 4 ADCON1=0x0f;
TRISD = 0x00; // thiet lap dau ra cho portB
xung=1; while(1)
{
xung=~xung;
WriteTimer0(256 - 250);
T0CONbits.TMR0ON = 1; // cho phep Timer hoat dong
while(!INTCONbits.TMR0IF); // cho co tran T0CONbits.
TMR0ON = 0; // dung Timer INTCONbits.TMR0IF=0;
// xoa co tran
}
}
Ví dụ 2: Sử dụng Timer0 ở chế độ timer 16 bit, tạo xung 1Hz, nguồn xung lấy từ bộ
dao động trên chíp, xung tạo trên chân RB1, tần số thạch anh sử dụng là 4 Mhz.
#include
#define xung PORTBbits.RB1 void main()
{
T0CON = 0b00000100; //16bit,he so chia = 32
TRISB = 0x00; //thiet lap chieu ra i/o PortB
ADCON1= 0x0f; //vao ra so
xung=1;
TMR0H=(65536-15625)/256;
while(1)
{
84
xung=~xung; TMR0L=(65536-15625)%256;
T0CONbits.TMR0ON=1; // cho phep Timer0 hoat dong
while(!INTCONbits.TMR0IF); // cho co tran 500 uS
T0CONbits.TMR0ON=0; // dung Timer0
INTCONbits.TMR0IF=0; // xoa co tran
}
}
Ví dụ 3: Sử dụng Timer0 ở chế độ đếm, đếm số lần nhấn Button (hình 4.4) và hiển
thị các giá trị đếm được dưới dạng số nhị phân trên 8 led đơn
Hình 4.4. Sơ đồ nguyên lý ví dụ 3
#include
void main()
{ TRISD=0x00; LATD=0;
ADCON1 = 0x0f; //vao ra so
//Khoi che do cho Timer0
T0CON = 0b01111000; //nguon xung ngoai T0CKI
//Che do timer0 8bit
TMR0H = 0;
TMR0L = 0;
T0CONbits.TMR0ON = 1;
while(1)
{ PORTD=TMR0L;
}
}
Ví dụ 4: Viết chương trình dùng ngắt Timer0 tạo xung 1KHz trên chân RD0.
85
#include
#define xung PORTDbits.RD0
void ngat_uu_tien_cao(void);
#pragma code uu_tien_cao=0x08
void ngat_cao(void)
{
ngat_uu_tien_cao();
}
#pragma code
#pragma interrupt ngat_uu_tien_cao
void ngat_uu_tien_cao(void)
{
T0CONbits.TMR0ON = 0; //dung timer
TMR0L = -125; //nap lai gia tri
T0CONbits.TMR0ON = 1; // cho phep Timer hoat dong
INTCONbits.TMR0IF=0; // xoa co tran
xung = ~xung;
}
void main()
{
T0CON = 0b01000001; // Timer0 8 bit, chia truoc 4
ADCON1=0x0f;
TRISD = 0x00; // thiet lap dau ra cho portB
INTCON=0b11100100;
xung=1; while(1);
}
c. Timer1
Timer1 là bộ Timer 16 bit. Ngoài chức năng đếm và định thời thông thường của
một bộ Timer, Timer1 còn có thể được dùng như một bộ phát xung thứ hai, cấp
nguồn xung đồng hồ cho toàn bộ hệ thống.
· Các thanh ghi của Timer1
- Thanh ghi điều khiển Timer1: T1CON
R/W-0 R-0 R/W-0 R/W-0 R/W-0 R/W-0 R/W-0 R/W-0
RD16 T1RUN T1CKPS1 T1CKPS0 T1OSCEN T1SYNC TMR1CS TMR1ON
bit 7 bit 0
bit 7 RD16: Bit lựa chọn chế độ ghi/đọc Timer1 1 = Ghi/đọc 1 lần 16 bit.
0 = Ghi/đọc 2 lần mỗi lần 8 bit.
bit 6 T1RUN: Bit cho phép hệ thống lấy xung từ Timer1
1 = Hệ thống hoạt động bằng nguồn xung cấp từ Timer1
0 = Hệ thống hoạt động bằng nguồn xung khác
86
27pF
T1OSO
Pin
SYSTEM
CLOCK
32Khz
T1OSCEN
27pF
T1OSI
Pin PIC18F452
0
bit 5-4 T1CKPS1:T1CKPS0: Các bit đặt hệ số chia tần số của xung cấp cho
Timer1 11 = Hệ số chia là 1:8
10 = Hệ số chia là 1:4 01 = Hệ số chia là 1:2 00 = Hệ số chia là 1:1
bit 3 T1OSCEN: Bit cho phép/cấm chức năng phát xung cho hệ thống
1 = Cho phép
0 = Cấm
bit 2 T1SYNC: Bit lựa chọn sự đồng bộ giữa xung ngoài cấp cho Timer1 và xung
trên chip. Khi bit TMR1CS = 1:
1 = Không đồng bộ
0 = Đồng bộ xung ngoài với xung trên chip Khi bit TMR1CS = 0:
Bit T1SYNC không có giá trị. (khi đó Timer1 sử dụng nguồn xung nội).
bit 1 TMR1CS: Bit lựa chọn nguồn xung cấp cho Timer1
1 = Timer1 được cấp xung từ ngoài qua chân RC0/T1OSO/T13CKI
0 = Timer1 được cấp xung nội (tần số bằng FOSC/4)
bit 0 TMR1ON: Bit điều khiển hoạt động của Timer1
1 = Timer1 hoạt động
0 = Dừng Timer1
- Thanh ghi chứa giá tri đếm byte thấp của Timer1: TMR1L
- Thanh ghi chứa giá tri đếm byte cao của Timer1: TMR1H Các thanh ghi liên
quan đến Timer1:
Tên Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
INTCON GIE/GIEH PEIE/GIEL TMR0IE INT0IE RBIE TMR0IF INT0IF RBIF
PIR1 PSPIF(1) ADIF RCIF TXIF SSPIF CCP1IF TMR2IF TMR1IF
PIE1 PSPIE(1) ADIE RCIE TXIE SSPIE CCP1IE TMR2IE TMR1IE
IPR1 PSPIP(1) ADIP RCIP TXIP SSPIP CCP1IP TMR2IP TMR1IP
TMR1L Thanh ghi chứa giá tri đếm byte thấp của Timer1
TMR1H Thanh ghi chứa giá tri đếm byte cao của Timer1
T1CON RD16 T1RUN T1CKPS1 T1CKPS0 T1OSCEN T1SYNC TMR1CS TMR1ON
· Chế độ hoạt động của Timer1
Ø Chế độ phát xung cho toàn hệ thống.
Hình 4.5. Timer1 hoạt động như một bộ phát xung cho hệ thống
87
Thông thường PIC18F4520 dùng nguồn dao động từ bộ dao động thạch anh mắc
trên 2 chân OSC1 và OSC2. Tuy nhiên khi nguồn dao động này bị hỏng, bộ vi điều
khiển vẫn có thể hoạt động nhờ việc chuyển sang dùng nguồn dao động thứ hai, khi
đó bit T1RUN và bit T1OSCEN (thanh ghi T1CON )phải được đặt bằng 1 để cho
phép hệ thống dùng nguồn xung từ Timer1 và cho phép mạch phát xung trên Timer1
hoạt động (hình 2.6)
Ø Chế độ ghi/đọc 2 lần 8bit
Hình 4.6. Timer1 hoạt động ở chế độ ghi/đọc 2 lần 8bit
Trong chế độ ghi/đọc 2 lần 8bit, giá trị của Timer1 được chứa trong 2 thanh ghi
TMR1H (byte cao) và TMR1L (byte thấp). Quá trình ghi đọc được chia làm 2 lần:
Ghi/đọc thanh ghi TMR1H và ghi/đọc thanh ghi TMR1L. Khi có sự chuyển số đếm
từ FFFFH sang 0 (tràn), cờ tràn của Timer1 (bit TMR1IF) được đặt bằng 1 và xảy ra
ngắt Timer1 nếu nguồn ngắt này được cho phép.
Timer1 được điều khiển hoạt động/dừng bởi bit TMR1ON (hình 2.7). Nguồn xung
cấp cho Timer1 có thể lấy từ 1 trong 3 cách sau:
- Lấy xung từ bộ dao động trên chíp, xung này có tần số bằng Fosc/4. Khi đó bit
TMR1CS phải được đặt bằng 0.
- Lấy xung từ một nguồn xung bên ngoài đưa vào qua chân T13CKI. Khi đó bit
T1OSCEN phải được đặt bằng 0 và bit TMR1CS phải được đặt bằng 1. Trong
trường hợp này Timer1 thường được dùng như một bộ đếm (đếm số xung đưa vào
qua chân T13CKI).
- Lấy xung từ mạch tạo dao động bao gồm bộ tạo dao động của Timer1 và các
linh kiện như hình 2.7. Khi đó bit T1OSCEN và bit TMR1CS phải được đặt bằng 1.
Cả 3 nguồn xung này đếu được đưa qua bộ chia tần số với các hệ số chia là 1,2,4,8
(tùy thuộc vào giá trị của các bit T1CKPS1, T1CKPS0).
Khi sử dụng xung từ ngoài cấp cho Timer1 thì có thể lựa chọn việc sử dụng bộ
đồng bộ để đồng bộ hóa nguồn xung này với xung của hệ thống bằng cách đặt bit
T1SYNC bằng 0 (=1 nếu không sử dụng bộ đồng bộ).
88
Ø Chế độ ghi/đọc 1 lần 16bit
Hình 4.7. Timer1 hoạt động ở chế độ ghi/đọc 1 lần 16bit
Trong chế độ ghi/đọc 16 bit 1 lần, byte thấp của Timer1 vẫn là thanh ghi TMR1L,
thanh ghi TMR1H trở thành bộ đệm của byte cao. Khi đọc TMR1L, giá trị của byte
cao sẽ tự động chuyển vào TMR1H, khi ghi TMR1L, giá trị của TMR1H sẽ tự động
chuyển vào byte cao. Hai hoạt động ghi/đọc TMR1L và chuyển giá trị giữa byte
cao của Timer1 với TMR1H diễn ra ở cùng thời điểm.
Điều khiển sự hoạt động của Timer1 và các nguồn xung cấp cho Timer1 trong chế
độ này hoàn toàn giống chế độ ghi/đọc 2 lần 8 bit.
· Ngắt Timer1.
Ngắt Timer1 được cho phép bởi bit TMR1IE (thanh ghi PIE1), được đặt mức ưu
tiên cao/thấp bởi bit TMR1IP (thanh ghi IPR1). Ngắt Timer1 xảy ra khi Timer1
tràn, khi đó cờ ngắt tràn TMR1IF (thanh ghi PIR1) được đặt bằng 1.
89
Ví dụ ứng dụng Timer1.
Sử dụng Timer1 chế độ ghi/đọc 2 lần 8bit tạo tần số 50Hz trên chân RE0
#include
#include
#include
#pragma config OSC = HS
#pragma config WDT = OFF
#pragma config MCLRE = ON
#define xung PORTEbits.RE0 void main()
{ TRISE=0x00;
ADCON1 = 0x0f; //vao ra so
//Khoi che do cho Timer1
T1CON = 0b00110000; /*nguon xung noi,doc ghi 2 lan 8 bit, he so chia truoc
bang 8*/
xung = 0; while(1)
{
xung=~xung;
TMR1H = (65536-1250)/256; TMR1L = (65536-1250)%256;
T1CONbits.TMR1ON = 1;
while(!PIR1bits.TMR1IF); //50uS PIR1bits.TMR1IF = 0;
T1CONbits.TMR1ON = 0;
}
}
· Lập trình với timer
Ví dụ 1: Sử dụng Timer0 chế độ 8bit tạo xung có tần số 1KHz, Ton=Toff trên
chân RA5, sử dụng bộ dao động ở chế độ HS.
Các bước thực hiện :
Bước 1: Viết chương trình:
- Lựa chọn nguồn xung hệ thống từ thạch anh ngoài chế độ HS.
- Đặt Timer0 ở chế độ 8bit (T08BIT = 1), hệ số chia tần là 4 và nguồn xung
từ thạch anh.
- Căn cứ vào thạch anh 8MHhz, hệ số chia tần 4 và thời gian cần tạo trễ 500
uS để tính giá trị khởi tạo cho bộ đếm là -250 hoặc (256-250). Ở chế độ 8 bit
thanh ghi chứa giá trị đếm là TMR0L.
- Sau khi bộ đếm tràn cần xóa cờ tràn để có thể phát hiện được thời điểm tràn
ở các lần tràn sau.
- Đảo mức tín hiệu trên chân RA5 sau 500 uS. Dưới đây là chương trình tham
khảo:
#include
#include
90
#include
#pragma config OSC = HS // xung he thong o che do HS
#pragma config WDT = OFF // tat watchdog timer
#pragma config MCLRE = ON // cho phep Reset tren chan MCLR
#define xung PORTAbits.RA5 void main()
{
T0CONbits.T08BIT=1; //Timer 0 che do 8 bit T0CONbits.PSA=0;
//su dung bo chia tan (Prescaler)
T0CONbits.T0PS2=0; //He so chia bang 4 T0CONbits.T0PS1=0;
T0CONbits.T0PS0=1;
T0CONbits.T0CS=0; //Su dung xung tu Fosc/4 ADCON1=0x0f;
TRISA = 0x00;
xung=1; while(1)
{
TMR0L = -250; // khoi tao timer (sau 250 xung= 250*2uS timer tran)
T0CONbits.TMR0ON=1;// cho phep Timer hoat động
while(!INTCONbits.TMR0IF); // cho co tran T0CONbits.TMR0ON = 0;
// dung Timer INTCONbits.TMR0IF=0;
// xoa co tran xung=~xung
}
}
Bước 2: Kiểm tra.
Sử dụng máy hiện sóng đo kiểm tra tín hiệu phát ra từ chân RA5 trên Testpoint 2
Ví dụ 2: Sử dụng Timer0 chế độ 16 bit, tạo xung có tần số 1Hz (Ton=Toff) trên
chân RC2.
Gợi ý thực hiện:
- Các bước thực hiện như ví dụ 1.
- Một số điểm cần lưu ý:
+ Đặt Timer0 ở chế độ 16bit (T08BIT = 0), chọn hệ số chia tần là 32.
+ Ở chế độ 16 bit byte cao sẽ được nạp sẵn, khi khởi tạo lại không cần nạp giá trị
cho byte cao mà chỉ cần nạp cho byte thấp, byte cao sẽ tự động được nạp lại cùng
byte thấp.
+ Sử dụng LED để quan sát và máy hiện sóng để kiểm tra.
Dưới đây là chương trình tham khảo:
#include
#include
#include
#pragma config OSC = HS
#pragma config WDT = OFF
#pragma config MCLRE = ON
91
#define xung PORTCbits.RC2 void main(void)
{
T0CON = 0b00000100; // Time0 16bit,he so chia tan:32 PORTC = 0x00;
TRISC = 0x00; TMR0H=(65536-31250)/256;
while(1)
{
TMR0L=(65536-31250)%256;
T0CONbits.TMR0ON=1; // cho phep Timer0 hoat dong
while(!INTCONbits.TMR0IF); // cho co tran
T0CONbits.TMR0ON=0; // dung Timer0
INTCONbits.TMR0IF=0; // xoa co tran xung=~xung;
};
· Bài tập timer
Bài tập 1: Viết chương trình tạo xung 50 Hz sử dụng Timer0. Bài tập 2: Viết
chương trình tạo xung 5Hz sử dụng Timer0.
Bài tập 3: Viết chương trình tạo xung 2 KHz sử dụng ngắt Timer1.
Bài tập 4: Viết chương trình tạo xung 1KHz sử dụng Timer0, tạo xung 2KHz sử
dụng ngắt từ Timer1.
4.3.2. Cách truy xuất các thành phần của timer trong ngôn ngữ lập trình
C.
Ứng dụng 1: Sử dụng ngắt Timer0 tạo xung theo yêu cầu như sau: Sử dụng
Timer0 chế độ 16 bit, tạo xung có tần số 1Hz (Ton=Toff) trên chân RC2
Gợi ý thực hiện:
- Các bước thực hiện như ví dụ 1.
- Một số điểm cần lưu ý:
+ Khởi tạo ngắt Timer0.
+ Mỗi lần xảy ra ngắt cần: Xóa cờ ngắt; Dừng Timer; Khởi tạo lại TMR0L; Đảo
mức logic trên RC2.
Dưới đây là chương trình tham khảo:
#include
#include
#include
#pragma config OSC = HS
#pragma config WDT = OFF
#pragma config MCLRE = ON
#define xung PORTCbits.RC2 void ngat_timer0(void); void ngat_cao(void);
#pragma code abc =0x08 void ngat_cao(void)
{
_asm goto ngat_timer0 _endasm
}
92
#pragma code
#pragma interrupt ngat_timer0 void ngat_timer0(void)
{
T0CONbits.TMR0ON=0; // dung Timer0
INTCONbits.TMR0IF=0; // xoa co tran TMR0L=(65536-
31250)%256;
T0CONbits.TMR0ON=1; // cho phep Timer0 hoat dong
xung=~xung;
}
void main(void)
{
T0CON = 0b00000100; PORTC = 0x00;
TRISC = 0x00; INTCONbits.GIE=1;
INTCONbits.TMR0IE=1; //cho phep ngat timer0
INTCON2bits.TMR0IP=1; //muc uu tien cao TMR0H=(65536-31250)/256;
INTCONbits.TMR0IF=1; //buoc ngat timer0 while(1);
}
Ứng dụng 2: Sử dụng dây nối hai chân RC0 với RA4 (hình 4.8). Liên tục đọc
trạng thái của chân RB0 và ghi giá trị đọc được ra RA4. Sử dụng Timer1 chế độ
đếm sự kiện, đếm số lần nhấn nút nhấn Key1 và hiển thị giá trị đếm được dưới
dạng số nhị phân ra LED1÷LED4.
Hình 4.8. Sơ đồ mạch điện thực hiện yêu cầu ứng dụng 2
93
Gợi ý thực hiện:
- Chọn chiều ra cho các chân RA4, và RB4 đến RB7.
- Chọn chiều vào cho các chân RB0 và RC0.
- Chọn đầu vào/ra số cho tất cả các chân.
- Timer1 khởi tạo ở chế độ đọc/ghi 1 lần 16 bit, nguồn xung ngoài từ chân
RC0/T1OSO.
- Lặp lại liên tục các thao tác:
+ Đọc từ RB0 xuất ra RA4
+ Đọc giá trị từ bộ đếm, dịch trái 4 bit xuất ra PORB để hiện thi trên 4 LED.
Dưới đây là chương trình tham khảo:
#include
#include
#include
#pragma config OSC = HS
#pragma config WDT = OFF
#pragma config MCLRE = ON void main()
{ TRISA=0x00; PORTA=0x00; TRISB=0x0f; TRISC=0x01;
ADCON1 = 0x0f; //vao ra so
//Khoi che do cho Timer1:
//TMR1CS=1: chon nguon xung ngoai tu T1OSO/T13CKI
//T1CKPS1:T1CKPS0=00: He so chia bang 0
//T1SYNC=1: Khong dong bo
//RD16=0: ghi doc 2 lan T1CON = 0b00000110; TMR1H = 0;
TMR1L = 0;
T1CONbits.TMR1ON = 1;
while(1)
{ PORTAbits.RA4=PORTBbits.RB0; PORTB=TMR1L<<4;
}
}
4.3.3. Ứng dụng timer/counter của vi điều khiển PIC 18f4520
a. Giao tiếp DS1307
DS1307 là một bộ đồng hồ thời gian thực của hãng Maxim có tích hợp giao thức
I2C. Vi mạch này cung cấp thông tin về năm, tháng, ngày, giờ, phút, giây dưới
dạng mã BCD . Thông tin về lịch sẽ được vi mạch tự cập nhật sau khi được cài
đặt. Thời gian cập nhật lên tới năm 2100. Hình 1.13 mô tả cách kết nối vi mạch
này với bộ vi điều khiển. Để có thể hoạt động, vi mạch này cần một nguồn PIN
(3V) và một bộ dao động thạch anh (32.768 Khz).
- 1 -
Hình 4.9. Mạch điện giao tiếp DS1307 với vi điều khiển
Người dùng có thể cài đặt thời gian thực cho vi mạch bằng cách ghi thông tin
thời gian vào các thanh ghi tương ứng của DS1307 (hình 1.14). Sau khi được
cài đặt, DS1307 sẽ trở thành một đồng hồ thời gian thực (real time clock).
Thông tin về thời gian được đọc bằng cách truy xuất các thanh ghi tương ứng.
Hình 4.10. Các thanh ghi của DS1307.
Bit 7 của thanh ghi seconds (địa chỉ 00h) là bit clock halt (CH). CH=1, mạch
tạo dao động (oscillator) trên DS1307 sẽ bị cấm, thông tin về thời gian trên vi
mạch sẽ không được cập nhật. Chức năng này được sử dụng cho mục đích tiết
kiệm năng lượng trên PIN khi cần thiết (DS1307 chỉ sử dụng dòng điện 10nA ở
chế độ oscillator off thay vì 480 nA ở chế độ oscillator on). Khi xuất xưởng,
nhà sản xuất mặc định CH=1, ngày/tháng/năm-giờ:phút:giây = 01/01/00-
00:00:00.
Bit 6 trong thanh ghi hours (địa chỉ 02h) dùng để cài đặt chế độ 12h (=1) hoặc
chế độ 24h (=0). Ở chế độ 12h, đọc bit 5 trên thanh ghi này sẽ cho thông tin về
- 2 -
thời gian: buổi sáng (=0), buổi chiều (=1). Ở chế độ 24h, bit 5 bằng 1 khi thời
gian hiện tại là 20h đến trước 24h.
Thanh ghi control (địa chỉ 07h) sử dụng điều khiển xung vuông phát ra từ chân
SQW của DS1307.
Trình tự ghi trên DS1307:
Hình 4.11. Ghi các thanh ghi/ô nhớ trên DS1307
Từ vi điều khiển, tạo “start”:
StartI2C();
IdleI2C(); //chờ bus rảnh (idle)
Ghi byte chứa địa chỉ thiết bị (slave address=1101000) và bit R/W(=0):
WriteI2C(0xd0); IdleI2C();
Ghi byte chứa địa chỉ của thanh ghi đầu tiên cần ghi (word address (n)). Đây
thực chất là một con trỏ (pointer) cho phép từ thiết bị chủ có thể lựa chọn bắt
đầu quá trình ghi từ địa chỉ nào trong không gian từ 0x00 đến 0xff trên
DS1307. Ví dụ: câu lệnh dưới đây sẽ ghi vào word address giá trị 0x01:
WriteI2C(0x01); IdleI2C();
Điều này đồng nghĩa với các lệnh ghi tiếp theo sẽ ghi vào các thanh ghi/ô nhớ
có địa chỉ 0x01 (thanh ghi phút), 0x02....
Ghi vào các thanh ghi/ô nhớ bắt đầu từ địa chỉ word address:
WriteI2C(0x06); //ghi vào thanh ghi địa chỉ 0x01,
//giá trị 0x06
IdleI2C();
WriteI2C(0x07); //ghi vào thanh ghi địa chỉ 0x02,
//giá trị 0x07
IdleI2C();
...
Tạo tín hiệu stop kết thúc quá trình ghi:
StopI2C();
Đoạn chương trình dưới đây sẽ đặt cho DS1307 thời gian:
giây:phút:giờ=05:06:07.
StartI2C(); IdleI2C();
WriteI2C(0xD0); IdleI2C();
WriteI2C(0x00); //word address=0x00 IdleI2C();
- 3 -
WriteI2C(0x05); //ghi thanh ghi giây, địa chỉ 0x00 IdleI2C();
WriteI2C(0x06);
IdleI2C(); //ghi thanh ghi phút, địa chỉ 0x01 WriteI2C(0x07); //ghi
thanh ghi giờ, địa chỉ 0x02 IdleI2C();
StopI2C();
Trình tự đọc giá trị của các thanh ghi trên DS1307 được mô tả trên hình 1.12.
Từ vi điều khiển, tạo “start”:
StartI2C();
IdleI2C(); //chờ bus rảnh (idle)
Hình 4.12. Đọc các thanh ghi trên DS1307
Ghi byte chứa địa chỉ thiết bị (slave address=1101000) và bit R/W(=0):
WriteI2C(0xd0); IdleI2C();
Ghi word address (giả thiết word address=0x00):
WriteI2C(0x00); IdleI2C();
Khởi tạo lại bus:
RestartI2C(); IdleI2C();
Ghi byte chứa địa chỉ thiết bị (slave address=1101000) và bit R/W(=1) để đọc:
WriteI2C(0xd1); IdleI2C();
Đọc lần lượt các thanh ghi/ô nhớ bắt đầu từ địa chỉ 0x00:
s=ReadI2C(); //đọc thanh ghi có địa chỉ 0x00 IdleI2C();
AckI2C(); //tạo tín hiệu ACK IdleI2C();
Tạo tín hiệu NotACK, stop, kết thúc quá trình đọc:
NotAckI2C(); IdleI2C();
StopI2C();
Chú ý: Thao tác (1)÷(3) không được mô tả trên hình 1.13, dùng để đặt con trỏ
cho lệnh đọc (ghi word address).
Đoạn chương trình dưới đây sẽ đọc các thanh ghi giây, phút, giờ và lưu vào các
biến s,m,h:
//(1) StartI2C(); IdleI2C();
//(2) WriteI2C(0xd0); IdleI2C();
//(3) WriteI2C(0x00); IdleI2C();
//(4) RestartI2C(); IdleI2C();
//(5) WriteI2C(0xd1); IdleI2C();
- 4 -
//(6) s=ReadI2C(); IdleI2C();
AckI2C();
IdleI2C(); m=ReadI2C();
IdleI2C(); AckI2C(); IdleI2C();
h=ReadI2C(); IdleI2C();
//(7) NotAckI2C(); IdleI2C();
StopI2C();
Giá trị đọc/ghi từ/đến DS1307 là các số biểu diễn dưới dạng mã BCD. Để thuận
tiện cho quá trình hiển thị trên LCD cần chuyển đổi từ số BCD sang số nguyên
và ngược lại:
Chuyển từ BCD sang số nguyên:
char bcd_int(int x)
{
return (((x>>4)&0x0f)*10)+(x&0x0f);
}
Chuyển từ số nguyên sang BCD:
int int_bcd(int x)
{
char N[10]={0X00,0X01,0X02,0X3,0X4,0X05,0X06,0X07,0X08,0X09};
int a,b; a=x/10;
b=x%10;
return ((N[a]<<4)&0xf0)+N[b] ;
}
Ứng dụng 1:
Cho sơ mạch điện như hình 4.13. Viết chương trình đọc các thanh ghi giây (địa
chỉ 0x00), phút, giờ và hiển thị trên LCD.
Hình 4.13. Giao tiếp PIC18F4520 với DS1307
- 5 -
Dưới đây là chương trình tham khảo:
#include
#include
#include
#include
#define lcd_data PORTD
#define RS PORTAbits.RA0
#define RW PORTAbits.RA1
#define E PORTAbits.RA2
unsigned int s,m,h; char M[32];
void lcd_int(void);
void lcd_cmd(unsigned char cmd); void
lcd_write(unsigned char data); void
lcd_str(unsigned char *str);
char bcd_int(int x); int
int_bcd(int x);
//******cac ham cua LCD****** void
lcd_int(void)
{
lcd_cmd(0x01);
lcd_cmd(0x38);
lcd_cmd(0x0c);
lcd_cmd(0x06);
lcd_cmd(0x01);
}
void lcd_cmd (unsigned char cmd)
{
RW=0; RS=0;
E=1;
lcd_data=cmd; E=0;
Delay1KTCYx(10);
}
void lcd_write(unsigned char data)
{
if(data=='\n')
{
- 6 -
lcd_cmd(0xc0);
Delay1KTCYx(10); return;
} RW=0;
RS=1; E=1;
lcd_data=data; E=0;
Delay1KTCYx(10);
}
void lcd_str(unsigned char*str)
{
while(*str)
{
lcd_write(*str); str++;
}
Delay1KTCYx(10);
}
/******************************************************/
//Cac ham chuyen doi so nguyen, BCD char bcd_int(int
x)
{
return (((x>>4)&0x0f)*10)+(x&0x0f);
}
int int_bcd(int x)
{
char N[10]={0X00,0X01,0X02,0X3,0X4,0X05,0X06,0X07,0X08,0X09};
int a,b; a=x/10;
b=x%10;
return ((N[a]<<4)&0xf0)+N[b] ;
}
/******************************************************/ void
main(void)
{
unsigned int a; TRISA=0X00;
TRISB=0X0F; TRISC=0X00;
TRISD=0X00; TRISE=0X00;
ADCON1=0X0F;
OpenI2C(MASTER,SLEW_OFF); //master mode, clock=100Khz lcd_int();
while(1)
{
- 7 -
StartI2C(); IdleI2C();
WriteI2C(0xd0); IdleI2C();
WriteI2C(0x00); IdleI2C();
RestartI2C(); IdleI2C();
WriteI2C(0xd1); IdleI2C();
s=ReadI2C(); IdleI2C();
AckI2C(); IdleI2C();
m=ReadI2C(); IdleI2C();
AckI2C(); IdleI2C();
h=ReadI2C(); IdleI2C();
NotAckI2C(); IdleI2C();
StopI2C();
s=bcd_int(s); m=bcd_int(m);
h=bcd_int(h); lcd_cmd(0x80);
sprintf(&M[0],"TIME:
%d%d:%d%d:%d%d",h/10,h%10,m/10,m%10,s/10,s%10);
lcd_str(&M[0]);
}
}
Ứng dụng 2. Đặt thời gian cho DS1307
Để thuận tiện cho quá trình đặt thời gian, Xây dựng hàm set_time dung để đặt
giờ, phút, giây và ngày, tháng, năm. Lưu ý: Thanh ghi năm (year) chỉ gồm 8bit
nên chỉ có thể đặt 2 số hàng chục và hàng đơn vị của năm hiện tại. Ví dụ: năm
2015 có thể ghi số 15 vào thanh ghi này.
void set_time(unsigned int s,unsigned int m,unsigned int h,unsigned int
dd,unsigned int mm,unsigned int yy )
{
s=int_bcd(s); m=int_bcd(m);
h=int_bcd(h); dd=int_bcd(dd);
mm=int_bcd(mm); yy=int_bcd(yy);
StartI2C(); IdleI2C();
WriteI2C(0xD0); IdleI2C();
- 8 -
WriteI2C(0x00); IdleI2C();
WriteI2C(s); IdleI2C();
WriteI2C(m); IdleI2C();
WriteI2C(h); IdleI2C();
StopI2C();
//bat dau lai qua trinh ghi StartI2C();
IdleI2C();
WriteI2C(0xD0); IdleI2C();
WriteI2C(0x04);//ghi tu thanh ghi date(dia chi 0x04) IdleI2C();
WriteI2C(dd); IdleI2C();
WriteI2C(mm); IdleI2C();
WriteI2C(yy); IdleI2C();
StopI2C();
}
Tương tự, chúng ta có thể xây dựng hàm đọc thời gian:
void get_time(void)
{
StartI2C(); IdleI2C();
WriteI2C(0xd0); IdleI2C();
WriteI2C(0x00); IdleI2C();
RestartI2C(); IdleI2C();
WriteI2C(0xd1); IdleI2C();
s=ReadI2C(); IdleI2C();
AckI2C(); IdleI2C();
m=ReadI2C(); IdleI2C();
AckI2C(); IdleI2C();
h=ReadI2C(); IdleI2C();
NotAckI2C(); IdleI2C();
StopI2C();
//bat dau lai qua trinh doc StartI2C();
IdleI2C();
WriteI2C(0xd0); IdleI2C();
WriteI2C(0x04);//doc tu thanh ghi date (dia chi 0x04) IdleI2C();
RestartI2C(); IdleI2C();
WriteI2C(0xd1); IdleI2C();
dd=ReadI2C(); //doc ngay IdleI2C();
AckI2C(); IdleI2C();
mm=ReadI2C(); //doc thang IdleI2C();
AckI2C(); IdleI2C();
yy=ReadI2C(); //doc nam IdleI2C();
- 9 -
NotAckI2C(); IdleI2C();
StopI2C();
}
Với các hàm đã xây dựng, chúng ta có thể tùy ý đặt/đọc thời gian thực, dưới đây
là chương trình minh họa:
void main(void)
{
//cac lenh khoi tao
//dat thoi gian:
//gio:phut:giay=06:06:06
//ngay/thang/nam=23/12/17
set_time(6,6,6,23,12,15); while(1)
{
get_time(); s=bcd_int(s);
m=bcd_int(m); h=bcd_int(h);
dd=bcd_int(dd);
mm=bcd_int(mm);
yy=bcd_int(yy);
//hien thi gio, phut, giay tren dong thu nhat lcd_cmd(0x80);
sprintf(&M[0],"TIME:
%d%d:%d%d:%d%d",h/10,h%10,m/10,m%10,s/10,s%10);
lcd_str(&M[0]);
//hien thi ngay, thang, nam tren dong thu hai lcd_cmd(0xc0);
sprintf(&M[0],"DATE:
%d%d/%d%d/20%d%d",dd/10,dd%10,mm/10,mm%10,((yy%1000)%100)/
10,((yy%1000)%100)%10);
lcd_str(&M[0]);
}
}
Chương trình chạy cho ra kết quả trên LCD như sau:
TIME: 06:06:06
DATE: 23/12/2017
Các file đính kèm theo tài liệu này:
- giao_trinh_ky_thuat_vi_dieu_khien_trinh_do_trung_capcao_dang.pdf