Giáo trình Kỹ thuật Vi điều khiển (Trình độ: Trung cấp/Cao đẳng nghề)

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

pdf102 trang | Chia sẻ: Tiểu Khải Minh | Ngày: 19/02/2024 | Lượt xem: 150 | Lượt tải: 0download
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:

  • pdfgiao_trinh_ky_thuat_vi_dieu_khien_trinh_do_trung_capcao_dang.pdf
Tài liệu liên quan