Biên soạn:Phạm Văn Cường Sv: Đại Học KTCN Thái Nguyên. TÀI LIỆU HỌC VI ĐIỀU KHIỂN PIC -Ngôn ngữ lập trình C++ -Mô phỏng mạch điện trên Proteus 7.8 -Lập trình PIC cơ bản trên CCS -Các Ứng dụng PIC -Thiết kế mạch in trên Altium Designer -Làm mạch in thủ công <Đam mê-Làm chủ công nghệ> Chương I: ÔN TẬP LẠI NGÔN NGỮ LẬP TRÌNH C++ I:Tón tắt về ngôn ngữ c. Ngôn ngữ lập trình C là một ngôn ngữ mệnh lệnh được phát triển từ đầu thập niên 1970 bởi Ken Thompson và Dennis Ritchie để dùng trong hệ điều hành UNIX. Từ dó, ngôn ngữ này đã lan rộng ra nhiều hệ điều hành khác và trở thành một những ngôn ngữ phổ dụng nhất. C là ngôn ngữ rất có hiệu quả và được ưa chuộng nhất để viết các phần mềm hệ thống, mặc dù nó cũng được dùng cho việc viết các ứng dụng. Ngoài ra, C cũng thường được dùng làm phương tiện giảng dạy trong khoa học máy tính mặc dù ngôn ngữ này không được thiết kế dành cho người nhập môn. II:Cá cấu trúc lệnh biến,hằng, hàm, mảng, chương trình con trong c++(cụ thể là phần mềm CCS). *Các hệ số thường dùng trong CCS -Hệ cơ số 10 0,1,2,3,4......9. VD: 14,25,153. -Hệ nhị phân 0,1 VD: 0101. -Hệ cơ số 16 VD: 0x25,0xFA. Cách đổi qua lại các hệ cơ số trình bày trên lớp. 1:Biến sử dụng trong CCS ĐN: Biến là đại lượng số học hay ký tự chữ viết mà có thể thay đổi được giá trị. 2:Các kiểu biến sử dụng trong CCS -Int1 kiểu logic 1 or 0 - Int8,Int,short,byte số nguyên 8 bit (-127 đến 127) -Int16 số nguyên 16bit -Int32 số nguyên 32 bit -char ký tự 8 bit -float số thực 32 bit -long số nguyên 16 bit -double số thực có độ chính xác cao 3:Định nghĩa biến và sử dụng -Ví dụ sau sẽ giúp bạn hiểu về biến Int8 a; Int b=5; Int c; a=10; a=a+2; c=a+b; //khai báo biến a 8 bit //Khai báo biến b 8 bit và gán giá trị bằng 5 //khai báo biến a 8 bit //Gán giá trị cho a=10 // a=10+2=12 biến a mang giá trị mới bằng 12 // c=12+5=17 biến c mang giá trị mới =17 Tóm lại là biến sau khi đưa vào tính toán thì nó bị thay đổi giá trị đi. -Ví dụ khai báo mảng-Mảng là một tập hợp các biến cùng tên cùng kiểu dữ liệu nhưng khác chỉ số Int8 abc[10]; //mảng abc có 10 phần tử từ abc[0] đến abc[9] abc[0]=5; //Gán giá trị cho phần tử thứ 1 trong mảng abc =5 abc[1]=7; //Gán giá trị cho phần tử thứ 2 trong mảng abc =7 c=abc[0]+abc[1]; //Biến c có giá trị =5+7=12. 4:Hằng ,và khai báo hằng số. Hằng số giống như biến nhưng không thay đổi được giá trị mà chỉ mang một giá trị ngay từ lúc đầu khi khai báo nó. Ví dụ sau sẽ rõ. Const int8 a=2; //Khai báo hằng số a có giá trị =2 Ta có một biến Int8 b=2; Int8 c ; c=a+b; // Biến c có giá trị là 2+2=4. Nếu dùng như sau là không được vì hằng số không thể thay đổi giá trị. a=b+3; 5:Các hàm hay dùng trong CCS(tôi sẽ trình bày theo mức độ quan trọng giảm dần) -Hàm if-else (Nếu thì) if(điều kiện){ chương trình A } else {chương trình B} Giải thích nếu điều kiện đúng thì thực hiện chương trình A còn nếu sai thì thực hiện chương trình B VD: If (a>=5) { b=10;} else{ b=0} Nếu a có giá trị =1 thì chương trình trên b=0; Nếu a có giá trị =7 thì chương trình trên b=10; -Hàm for(vòng lặp) for(biến chạy;điều kiện;thay đổi biến){chương trình A } Giải thích, hàm này sẽ tạo ra một vòng lặp với một số lần quy ước của người sử dụng. VD: Int i,b=0; for(i=0;i<=9;i++){ b=b+2;} Sau đoạn chương trình này b=20. -Hàm While(điều kiện) Giải thích: hàm này cũng tạo ra một vòng lặp , điều kiện còn đúng thì còn thực hiện,thường thì chương trình của chúng ta đặt trong một vòng lặp While vô tận. VD: Int i,b=5; While(i>=5){ i++; b--; } Sau chương trình này biến b sẽ mang giá trị bằng 0. 6:Các hàm xử lý toán số , toán học trong CCS. Toán tử gán = Toán tử gán dùng để gán một giá trị nào đó cho một biến a = 5; gán giá trị nguyên 5 cho biến a. Vế trái bắt buộc phải là một biến còn vế phải có thể là bất kỳ hằng, biến hay kết quả của một biểu thức. Cần phải nhấn mạnh rằng toán tử gán luôn được thực hiện từ trái sang phải và không bao giờ đảo ngược a = b; gán giá trị của biến a bằng giá trị đang chứa trong biến b. Chú ý rằng chúng ta chỉ gán giá trị của b cho a và sự thay đổi của b sau đó sẽ không ảnh hưởng đến giá trị của a. Một thuộc tính của toán tử gán trong C++ góp phần giúp nó vượt lên các ngôn ngữ lập trình khác là việc cho phép vế phải có thể chứa các phép gán khác. Ví dụ: a = 2 + (b = 5); tương đương với b = 5;a = 2 + b; Vì vậy biểu thức sau cũng hợp lệ trong C++ a = b = c = 5; gán giá trị 5 cho cả ba biến a, b và c Các toán tử số học (+, -, *, /, %) Năm toán tử số học được hỗ trợ bởi ngôn ngữ là: + cộng - trừ * nhân / chia % lấy phần dư (trong phép chia) Thứ tự thực hiện các toán tử này cũng giống như chúng được thực hiện trong toán học. Điều duy nhất có vẻ hơi lạ đối với bạn là phép lấy phần dư, ký hiệu bằng dấu phần trăm (%). Đây chính là phép toán lấy phần dư trong phép chia hai số nguyên với nhau. Ví dụ, nếu a = 11 % 3, biến a sẽ mang giá trị 2 vì 11 = 3*3 +2. Các toán tử gán phức hợp (+=, -=, *=, /=, %=, >>=, <<=, &=, ^=, |=) Một đặc tính của ngôn ngữ C++ làm cho nó nổi tiếng là một ngôn ngữ súc tích chính là các toán tử gán phức hợp cho phép chỉnh sửa giá trị của một biến với một trong những toán tử cơ bản sau: value += increase; tương đương với value = value + increase; a -= 5; tương đương với a = a - 5; a /= b; tương đương với a = a / b; price *= units + 1; tương đương với price = price * (units + 1); và tương tự cho tất cả các toán tử khác. Tăng và giảm Một ví dụ khác của việc tiết kiệm khi viết mã lệnh là toán tử tăng (++) và giảm (-). Chúng tăng hoặc giảm giá trị chứa trong một biến đi 1. Chúng tương đương với +=1 hoặc -=1. Vì vậy, các dòng sau là tương đương: a++; a+=1; a=a+1; Một tính chất của toán tử này là nó có thể là tiền tố hoặc hậu tố, có nghĩa là có thể viết trước tên biến (++a) hoặc sau (a++) và mặc dù trong hai biểu thức rất đơn giản đó nó có cùng ý nghĩa nhưng trong các thao tác khác khi mà kết quả của việc tăng hay giảm được sử dụng trong một biểu thức thì chúng có thể có một khác biệt quan trọng về ý nghĩa: Trong trường hợp toán tử được sử dụng như là một tiền tố (++a) giá trị được tăng trước khi biểu thức được tính và giá trị đã tăng được sử dụng trong biểu thức; trong trường hợp ngược lại (a++) giá trị trong biến a được tăng sau khi đã tính toán. Hãy chú ý sự khác biệt: Ví dụ 1 Ví dụ 2 B=3; B=3; A=++B; A=B++; // A is 4, B is 4 // A is 3, B is 4 Các toán tử quan hệ ( ==, !=, >, <, >=, <= ) Để có thể so sánh hai biểu thức với nhau chúng ta có thể sử dụng các toán tử quan hệ. Theo chuẩn ANSI-C++ thì giá trị của thao tác quan hệ chỉ có thể là giá trị logic - chúng chỉ có thể có giá trị true hoặc false, tuỳ theo biểu thức kết quả là đúng hay sai. Sau đây là các toán tử quan hệ bạn có thể sử dụng trong C++ == Bằng != Khác > Lớn hơn <</td> Nhỏ hơn >= Lớn hơn hoặc bằng <= Nhỏ hơn hoặc bằng Ví dụ: (7 == 5) sẽ trả giá trị false (6 >= 6) sẽ trả giá trị true tất nhiên thay vì sử dụng các số, chúng ta có thể sử dụng bất cứ biểu thức nào. Cho a=2, b=3 và c=6 (a*b >= c) sẽ trả giá trị true. (b+4 < a*c) sẽ trả giá trị false Cần chú ý rằng = (một dấu bằng) lf hoàn toàn khác với == (hai dấu bằng). Dấu đầu tiên là một toán tử gán (gán giá trị của biểu thức bên phải cho biến ở bên trái) và dấu còn lại (==) là một toán tử quan hệ nhằm so sánh xem hai biểu thức có bằng nhau hay không. Trong nhiều trình dịch có trước chuẩn ANSI-C++ cũng như trong ngôn ngữ C, các toán tử quan hệ không trả về giá trị logic true hoặc false mà trả về giá trị int với 0tương ứng với false còn giá trị khác 0 (thường là 1) thì tương ứng với true. Các toán tử logic (!, &&, ||) Toán tử ! tương đương với toán tử logic NOT, nó chỉ có một đối số ở phía bên phải và việc duy nhất mà nó làm là đổi ngược giá trị của đối số từ true sang false hoặc ngược lại. Ví dụ: !(5 == 5) trả về false vì biểu thức bên phải (5 == 5) có giá trịtrue !(6 <= 4) trả về true vì (6 <= 4)có giá trị false !true trả về false !false trả về true Toán tử logic && và || được sử dụng khi tính toán hai biểu thức để lấy ra một kết quả duy nhất. Chúng tương ứng với các toán tử logic AND và OR. Kết quả của chúng phụ thuộc vào mối quan hệ của hai đối số: Đối số thứ nhất Đối số thứ hai Kết quả Kết quả a b a && b a || b true True true true true False false true false True false true false False false false Ví dụ: ( (5 == 5) && (3 > 6) ) trả về false ( true && false ). ( (5 == 5) || (3 > 6)) trả về true ( true || false ). Toán tử điều kiện ( ? ). Toán tử điều kiện tính toán một biểu thức và trả về một giá trị khác tuỳ thuộc vào biểu thức đó là đúng hay sai. Cấu trúc của nó như sau: condition ? result1 : result2 Nếu condition là true thì giá trị trả về sẽ là result1, nếu không giá trị trả về là result2. 7==5 ? 4 : 3 trả về 3 vì 7 không bằng 5 7==5+2 ? 4 : 3 trả về 4 vì 7 bằng 5+2 5>3 ? a : b trả về a, vì 5 lớn hơn 3 a>b ? a : b trả về giá trị lớn hơn, a hoặc b Các toán tử thao tác bit (&, |, ^, ~, <<, >>) Các toán tử thao tác bit thay đổi các bit biểu diễn một biến, có nghĩa là thay đổi biểu diễn nhị phân của chúng: Toán tử asm Mô tả & AND Logical AND | OR Logical OR ^ XOR Logical exclusive OR ~ NOT Đảo ngược bit << SHL Dịch bit sang trái >> SHR Dịch bit sang phải Các toán tử chuyển đổi kiểu Các toán tử chuyển đổi kiểu cho phép bạn chuyển đổi dữ liệu từ kiểu này sang kiểu khác. Có vài cách để làm việc này trong C++, cách cơ bản nhất được thừa kế từ ngôn ngữ C là đặt trước biểu thức cần chuyển đổi tên kiểu dữ liệu được bọc trong cặp ngoặc đơn (), ví dụ: int i; float f = 3.14; i = (int) f; Đoạn mã trên chuyển số thập phân 3.14 sang một số nguyên (3). Ở đây, toán tử chuyển đổi kiểu là (int). Một cách khác để làm điều này trong C++ là sử dụng các constructors (ở một số sách thuật ngữ này được dịch là cấu tử nhưng tôi thấy nó có vẻ không xuôi tai lắm) thay vì dùng các toán tử: Đặt trước biểu thức cần chuyển đổi kiểu tên kiểu mới và bao bọc biểu thức giữa một cặp ngoặc đơn. i = int ( f ); Cả hai cách chuyển đổi kiểu đều hợp lệ trong C++. Thêm vào đó ANSI-C++ còn có những toán tử chuyển đổi kiểu mới đặc trưng cho lập trình hướng đối tượng. sizeof() Toán tử này có một tham số, đó có thể là một kiểu dữ liệu hay là một biến và trả về kích cỡ bằng byte của kiểu hay đối tượng đó. a = sizeof (char); a sẽ mang giá trị 1 vì kiểu char luôn có kích cỡ 1 byte trên mọi hệ thống. Giá trị trả về của sizeof là một hằng số vì vậy nó luôn luôn được tính trước khi chương trình thực hiện. 6:Viết một chương trình con (Cái này sử dụng nhiều và quan trong). Cú pháp 1 chương trình con. -Kiểu trả về kết quả. Kieudulieu tenchuongtrinhcon(cac bien truyen vao) { Thân chương trình Return (ketquatrave ); } VD: int8 tinhtoan(int a){ b=a.2; return(b); } Nếu ta gọi hàm con c=tinhtoan(5); thì c se mang giá trị là 10. -Kiểu không trả về kết quả. Void tenchuongtrinhcon(cac bien truyen vao) { Thân chương trình } VD: Void hienthi(int a){ Output_D(a); } Chương trình này xuất ra cổng D dữ liệu biến a. 7:Bài tập Bài 1:Viết một chương trình vòng lặp for biến i chạy từ 20 về 0. Bài 2:Viết một chương trình sử dụng if-else kiểm tra biến a nếu a>0 thì thực hiện vòng lặp for biến i chạy từ 0 đến 10 ,nếu a<0 thì biến i chạy từ 10 về 0 Bài 3: Viết một chương trình sử dụng If-else Nếu 5<=a<=10 thì b=10. Nếu 11<=a<=15 thì b=15. Nếu a<5 hoặc a>15 thì b=0. III:Làm quen với CCS -CCs là một môi trường lập trình phổ biến cho dòng PIC sử dụng cả ngôn ngữ c và asm -Ở tài liệu này,ta lập trình trên ngôn ngữ C -Ngôn ngữ C giúp người dùng tiếp cận và học PIC nhanh hơn, ta không cần quan tâm tới cấu trúc của Pc như thế nào, hay các thanh gi và địa chỉ các thanh gi như nào.với C ta chỉ cần quan tâm duy nhất tới một thứ đó là Cú pháp câu lệnh, để ta sử dụng nó lập trình cho PIC. -Một điều nữa mà C mang lại là ta có thế dễ dàng viết ra ý tưởng hay thuật toán trong đầu trên ngôn ngữ C một cách đơn giản.gần như là nghĩ được gì viết ra đấy. Cấu trúc một chương trình #include<tên chương tring.h > //Gọi đến hàm định nghĩa ban đầu cho chíp. #int_ngắt void ngat() { Chương trình ngắt. } Void chuongtrinhcon() { Chương trinh con } Void main() { Thân chương trình. } IV:Ôn lại một số linh kiện điện tử cơ bản, -Tụ điệu, điện trở, tranzito -Xem lại trong quấn điện tử cơ bản. đính kèm file. Chương II: HỌC PIC CƠ BẢN CÁC DỰ ÁN CƠ ĐƠN GIẢN CHO NGƯỜI MỚI BẮT ĐẦU Bài 1:CƠ BẢN VỀ VDK PIC 1)Giới thiệu về VDK PIC. - VDK PIC là một họ vi điều khiển có khả năng lập trình để xử lý tính toán số liệu hay điều khiển các modul thết bị ngoại vi. -Là một dòng VDK mạnh giải quyết được nhiều bài toán ,ứng dụng trong thực tế. - PIC hỗ trợ lập trình trên ngôn ngữ C -PIC hỗ trợ nhiều modul phần cứng nhưng ADC, PWM, RS232, USB, SPI, I2C .... -PIC mình nghiên cứu ở trong tài liệu này là PIC 16F887.Gồm có 34 chân in out dữ liệu, 14 chân ADC, 3 timer, 8k byte rom, 256 byte EEPROM , 368 byte RAM. - Phần mềm lập trình CCS bản 4.104 -Phần mềm mô phỏng Proteus 7.8 -Phần mềm và mạch nạp PIC-kit 2. 2)Mạch đơn giản để PIC có thể chạy được. Sơ đồ nguyên lý. 3)Một số lệnh cơ bản trong lập trình. Tạo trể. +delay_ms(value); tạo trễ một khoảng thời gian ms. +delay_us(value); tạo trễ một khoảng thời gian us. +delay_cycles (count): tạo trễ theo chu kỳ lệnh. Tất cả các lệnh của CCS các bạn có thể xem trong phần trợ giúp của phần mềm . Bài 2:XUẤT DỮ LIỆU 1)Các lệnh cơ bản xuất dữ liệu -Lệnh output_high(PIN_X?); Lệnh này xuất dữ liệu mức 1 ra một chân của vi điều khiển. VD: Ta xuất ra chân A1 của vi điều khiển mức 1bằng lệnh này. Output_high(PIN_A1); -Lệnh output_low(PIN_X?); Lệnh này xuất dữ liệu mức 0 ra một chân của vi điều khiển. VD: Ta xuất ra chân B2 của vi điều khiển mức 1bằng lệnh này. Output_low(PIN_B2); -Lệnh output_bit (pin, value); Lệnh này xuất dữ liệu value ra một chân của vi điều khiển.dữ liệu ở đây có giá trị là 0 hoặc 1, các số khác số 0 đều có giá trị bằng 1. VD:Ta xuất ra chân C1 của vi điều khiển một mức nào đấy + Mức cao: output_bit(PIN_B0,1); output_bit(PIN_B0,3); output_bit(PIN_B0,0x05); Những câu lệnh trên đều có tác dụng làm cho chân B0 lên mức cao. +Mức thấp: output_bit(PIN_B0,0); output_bit(PIN_B0,0x00); Những câu lệnh trên đều có tác dụng làm cho chân B0 xuống mức thấp. -Lệnh output_X ( value); Lệnh này xuất dữ liệu ra một cổng của VDK với giá trị là value. VD: output_B(0x00) xuất ra cổng b tất cả các chân đều ở mức thấp(mức 0). output_B(0xff) xuất ra cổng b tất cả các chân đều ở mức cao(mức 1). output_B(0x0f) các chân B0->B3 mức cao.Các chân B4-B7 mức thấp. 2)Thực hành xuất dữ liệu trên PIC. + Lập trình nhấp nháy 1 bóng led -8 bóng led,hoặc 8 chữ cái gồm nhiều led,relay on off thiết bị. +Chương trình on off một re lay nối vào chân B1, thời gian đóng cắt 2 giây. #include <On off.h> void main(){ while(TRUE) { output_high(PIN_B1); delay_ms(2000); output_low(PIN_B1); delay_ms(2000); } } 3)Bài tập ôn tập. Bài 1: Viết một chương trình nhấp nháy 8 led đơn với ít nhất 10 kiểu nháy khác nhau. Bài 2: Viết một chương trình nháy 16 led đơn với 5 hiệu ứng nháy, trong đó có 1 kiểu chạy từ phải sang trái. Bài 3 : Viết một chương trình nháy 8 led đơn từ phải sang trái (trên xuống dưới) sử dụng vòng lặp for và phép dịch bit. Bài 4 : Thiết kế một biển led quảng cáo nháy chữ “DAM ME CONG NGHE”, Mỗi chữ cái gồm 50 led đơn nói song song với nhau.Lập trình với trên 10 kiểu nháy đẹp mắt. Thiết kế mạch nguyên lý chi tiết trên giấy khổ A4, mô phỏng trên phẩn mềm Proteus và code lập trình.(Buổi tới đi học mang đi để kiểm tra và sửa lỗi). Bài 3:LED 7 THANH 1)Tìm hiểu về led 7 thanh Khái niệm cơ bản Led 7 thanh có cấu tạo bao gồm 7 led đơn có dạng thanh xếp theo Hình 1.1 dưới và có thêm một led đơn hình tròn nhỏ thể hiện dấu chấm tròn ở góc dưới, bên phải của led 7 thanh. 8 led đơn trên led 7 đoạn có Aode(cực +) hoặc Cathode(cực -) được nối chung với nhau vào một điểm, được đưa chân ra ngoài để kết nối với mạch điện. 8 cực còn lại trên mỗi led đơn được đưa thành 8 chân riêng: Nếu led 7 đoạn có Anode(cực +) chung, đầu chung này được nối với +Vcc, các chân còn lại dùng để điều khiển trạng thái sáng tắt của các led đơn, led chỉ sáng khi tín hiệu đặt vào các chân này ở mức 0. Nếu led 7 đoạn có Cathode (cực -) chung, đầu chung này được nối xuống Ground (hay Mass), các chân còn lại dùng để điều khiển trạng thái sáng tắt của các led đơn, led chỉ sáng khi tín hiệu đặt vào các chân này ở mức 1. Hình 1.1 Bảng giá trị tín hiệu. -Chung A => -Chung K => 2)Thực hành hiển thị led 7 thanh. a. Led 7 thanh đơn. Ví dụ:sơ đồ mô phỏng như sau Chương trình hiển thị số 1 lên led 7 thanh. While (true){ Output_D(0xF9); } -Ta chỉ cần đưa dữ liệu đã tính ở trên của số muốn hiển thị ra cổng dữ liệu nối với các chân dữ liệu của led 7. b.Led 7 thanh kép và phương pháp quét led. -Hiệu ứng lưu ảnh của mắt: hiểu đơn giản như thế này, có 1 bóng led tắt rồi bật, lúc đầu cứ 1s sáng thì lại tắt 1s nghĩa là chu kỳ 2 s, rồi giảm chu kỳ ấy xuống 1s ta vẫn thấy bóng led đấy nháy, khi ta giảm chu kỳ xuống 1ms thì lúc đó mắt ta nhìn thấy gì? Trả lời được câu hỏi thì bạn cũng đã hiểu về hiệu ứng lưu ảnh của mắt. - Phương pháp quét led: Ta có mạch nguyên lý như dưới đây. Gồm 2 led loại chung A được mắc chung chân dữ liệu. bây giờ bạn hãy thử đoạn chương trình sau,và thay đổi tham số chuky nhỏ dần từ 1s đến nhỏ hơn 1/24/a s xem nhé.(a là số led 7 thanh) While(TRUE) { #define chuky 2000 output_E(0x00); output_D(0x99); output_E(0x01); delay_ms(chuky); output_E(0x00); output_D(0x99); output_E(0x02); delay_ms(chuky); } Hãy nêu hiện tượng và kết luận ,phương pháp quét led là như nào. c.Hiển thị một số có 2 chữ số trên led VD: Hiển thị biến a có 2 chữ số , Int8 led7[10]= {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90} While(TRUE) { #define nhietdo 29 output_E(0x00); output_D(led7[nhietdo/10]); output_E(0x01); delay_ms(1); output_E(0x00); output_D(led7[nhietdo%10]); output_E(0x02); delay_ms(1); } Bài tập: Bài 1) Lập trình hiển thị led 7 thanh chạy từ 0->9 chu kỳ 500ms. Bài 2) Lập trình hiển thị led 7 thanh chạy từ 000->150 chu ky 200ms. Bài 3) Lập trình hiển thị led 7 thanh chạy từ 0->50 rồi nhảy lên 60->100. Bài 4) Có 7 biến giờ phút giây thư ngày tháng năm được lưu trong các biến hour,min,sec,day,date,month,year. Hãy mô phỏng trên proteus và viết chương trình hiển thị các biến trên bằng phương pháp quét led, triển khai sơ đồ nguyên lý chi tiết ,cụ thể trên giấy A4.Buổi sau đi học mang bài đến nộp. Bài 4:ĐỌC DỮ LIỆU-INPUT 1)Các lệnh cơ bản đọc vào dữ liệu. a=input(PIN_X); //đọc giá trị logic ở một chân của vdk VD: a=input(PIN_A1) ; Nếu chân A1 ở mức cao thì biến a mang giá trị là 1 còn chân A1 ở mức thấp thì biến a mang giá trị là 0. b=input_X(); VD: b=input_B(); Trạng thái của cổng B sẽ được lưu vào biến b, nếu cổng B đang ở trạng thái 0x2f thì sau câu lệnh trên biến b cũng mang giá trị là 0x2f. 2)Thực hành đọc dữ liệu vào vdk và xử lý. a) Nút nhấn. Sơ đồ mô phỏng dưới đây. -Viết chương trình khi nhấn nút trên thì động cơ quay và nhấn nút dưới thì động cơ ngừng quay, tức là đưa ra chân D0 mức cao sẽ làm mở Tip41 đóng re lay relay tác động thì tiếp điểm thường mở sẽ thành thường đóng, mạch kín động cơ quay, và ngược lại -Code #include <nut nhan.h> #define Relay_on output_high(PIN_D0) #define Relay_off output_low(PIN_D0) void main() { while(TRUE) { if(input(PIN_C0)) Relay_on; if(input(PIN_C1)) Relay_off; } } b. đọc dữ liệu từ 1 cổng vào. -Sơ đồ nguyên lý. -Lập trình đọc dữ liệu vào từ cổng C và xuất ra led cổng D. #include <nut nhan.h> int a; void main() { while(TRUE) { a=input_C(); output_D(a); } } c.Đọc molul thu phát RF. -Giới thiệu về modul thu phát RF. -Modul thu và sơ đồ chân. Có 2 loại modul thu RF là loại giữ mức và loại không giữ mức. -Modul phát 4 kênh và hình ảnh thực tế -Kết nối bộ RF 4 kênh vào 4 chân input của vi điều khiển ta lập trình đọc dữ liệu tương tự như nút nhấn. -Bộ RF ở trong bài học này là loại giữ mức nên ta sẽ dùng để điều khiển 4 relay đóng cắt cho 4 thiết bị điện trong nhà. d.Cảm biến chuyển động. -Hình ảnh cảm biến. Sơ đồ chân. Cách sử dụng rất đơn giản: Khi có người trong vùng cảm biến có thể phát hiện ra được thì chân Out của cảm biến sẽ out ra mức cao nhưng ở dạng xung , ta đưa vào một chân VDk để đọc tín hiệu đó vào và xử lý bật và tắt một relay để đóng mở một thiết bị nào đấy. Code ví dụ. While(true){ Int a; If(input(PIN_B0)) a=100; a-- ; if(a<9) a=9; if(a>=10) output_high(PIN_D0); else output_low(PIN_D0); delay_ms(100); } 3)Bài tập: 1) Lập trình và mô phỏng 8 nút nhấn nối với cổng B khi nhấn nút nào thì led 7 thanh đơn nối với cổng D sáng số đấy. VD nhấn nút nhấn nối vào cổng B0 thì led 7 thanh hiển thị số 0.(Mức tích cực mức thấp) 2) Lập trình và mô phỏng chương trình một nút nhấn nối vào cổng B0 nếu nút nhấn thì giá trị trên led 7 thanh tăng lên 1 sử dụng 2 led 7 thanh hiển thị bằng phương pháp quét led. 3) Đọc dữ liệu 1 cổng từ Dipswhich và hiển thị giá trị 8 bit của cổng đó lên led 7 thanh. VD: giá trị cổng B=0x12=18 thì led 7 thanh phải hiển thị số 018. 4) Lập trình và mô phỏng nhận dữ liệu từ 4 đầu vào nếu ở mức cao thì bật một relay nối ở bất kỳ 1 cổng nào đấy.còn mức thấp thì tắt relay tương ứng. 5) Lập trình đầu vào dữ liệu cho 4 nút nhấn, cứ mỗi lần nhấn thì thay đổi mức logic cho một chân ra của vdk để đóng cắt một relay. 6) BÀI TẬP TỔNG HỢP. -Thiết kế một dự án điều khiển 4 thiết bị điện từ xa bằng modul RF 4 kênh (modul không giữ mức) .3 kênh A B C điều khiển 3 thiết bị điện,nhấn một lần mở, nhấn thêm lần nữa tắt. Còn kênh D để mở một bóng điện kết hợp với cảm biến chuyển động, khi mở bóng mà người còn trong phòng thì bóng sáng, người ra khỏi phỏng một thời gian thì bóng tắt. (Bao gồm code lập trình, mô phỏng và mạch nguyên lý chi tiết) Bài 5:CHUYỂN ĐỔI ADC 1)ADC là gì. -Chuyển đổi ADC chính là chuyển đồi từ một tín hiệu dạng tương tự sang một tín hiệu số . -Trong VDK có các bộ chuyển đổi ADC với độ phân giải khác nhau 8bit (256 bước lượng tử) 10bit(2^10 bước lượng tử) và 12bit(2^12 bước lượng tử), độ phân giải càng lớn thì độ chính xác càng cao. -VD ta có một tín hiệu điện áp thay đổi trong dải từ 0V-5V ta chọn điện áp so sánh là 0-5V ,và độ phân giải ADC là 8 bit , tại một thời điểm điện áp là 2,5V thì tín hiệu tương tự này sau khi chuyển đổi có giá trị là 127. 2)Cách cấu hình và sử dụng ADC trong PIC. -PIC 16f887 hỗ trợ 2 độ phân giải chuyển đổi ADC là 8 bit và 10 bit. Sau đây là một chương trình đơn giản về chuyển đổi ADC và hiển thị giá trị ADC chuyển đổi được lên LED 7 thanh. -Sơ đồ nguyên lý -Code lập trình #include <16F887.h> #device ADC=8 // Thiết lập độ phân giải chuyển đổi ADC #FUSES NOWDT,NOBROWNOUT,NOLVP #use delay(crystal=20000000) int8 value; int8 const Led7[12]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90}; int8 const Led[6]={0x00,0x01,0x02,0x04,0x08}; //Chương trình quét led Void Quetled(int i,int j){ output_B(Led[0]); output_D(Led7[i]); output_B(Led[j]); delay_ms(3); } //Chương trình chính void main() { setup_adc_ports(sAN0); //Thiết lập chân chuyển đổi ADC setup_adc(ADC_CLOCK_DIV_2); //Thiết lập tần số lấy mẫu while(TRUE) { set_adc_channel(0); delay_us(10); value = read_adc(); // Chọn kênh cần lấy mẫu // Trễ chờ chuyển đổi // Hàm đọc về giá trị ADC Quetled(value/1000,1); //Đoạn chương trình hiển thị led 7 Quetled(value%1000/100,2); Quetled(value%1000%100/10,3); Quetled(value%10,4); } } Cụ thể cách cài đặt và ý nghĩa sẽ được hướng dẫn chi tiết trên lớp. 3)Cảm biến tương tự và cách đo, đọc kết quả để xử lý. -Cảm biến tương tự là các loại cảm biến tín hiệu đo được , được trả về bằng tín hiệu tương tự. -Khi có trong tay một cảm biến ta phải xác định xem nó trả về kết quả dưới dạng gì,số hay tương tự.Cách nhanh nhất là tra Datasheet của nhà sản xuất để biết được thông số và dạng tín hiệu trả về. VD: ta có một cảm biến đo áp suất tương tự tín hiệu là tuyến tính,theo thông số của nhà sản xuất thì dải tín hiệu từ 0-5v tương ứng với áp suất là 0 atm tới 250 atm. thì ta làm sao để có thể đọc được kết quả giá trị áp suất tại một thời điểm.Giả sử ta chuyển đổi ADC 10 bit của VDK để đo áp suất, tại thời điểm t ta đọc được giá trị ADC=756,bằng công thức sau ta suy ra được kết quả áp suất tại thời điểm đó tính bằng atm. Giá trị vật lý=( kết quả ADC/độ phân giải)*Thang đo. (Công thức này chỉ dùng với cảm biến trên này thôi, mỗi cảm biến một công thức khác nhau) Giá trị áp suất =(756/1023)*250=184.75 atm. 4)Thực hành và làm ứng dụng với cảm biến nhiệt độ LM35. -LM35 là một cảm biến nhiệt độ tín hiệu trả về là tương tự, -Các thông số cơ bản. +Giá trị quy đổi tuyến tính 10mV/°C +Dải đo từ -50°C đến 150°C +Dòng tiêu thụ < 60-μA +Sai số ±¼°C +Thông số ví dụ VOUT = 1500 mV at 150°C VOUT = 250 mV at 25°C VOUT = –550 mV at –55°C Do cảm biến là tuyến tính nên => VOUT =0 V at 0°C - Sơ đồ nguyên lý. - Sơ đồ chân. -Như ta thấy ở trên thì cảm biến LM35 có giá trị quy đổi tương đương 10mv//°C ,và tương ứng 0v với 0/°C vậy tín hiệu là 300mV thì nhiệt độ tương ứng sẽ là 30/°C. Như vậy nếu dùng ADC 10 bít ta có các giá trị tương ứng là 0°C -> 0mV -> ADC=0 150°C -> 1500mV -> ADC=307 Vậy để tính toán ra giá trị nhiệt độ thực tế ta phải thực hiện một phép chia cho một hằng số tuyến tính K ở đấy ta tìm K bằng cách Lấy giá trị ADC của giá trị nhiệt độ lơn nhất chia cho ADC của giá trị nhiệt độ lớn nhất đó. K=307/150°C = 2.046 Bây giờ ta dùng PIC để đọc giá trị ADC từ cảm biến về được kế quả ta sẽ tính toán bằng phép tính sau sẽ ra được kết quả nhiệt độ hiện tại. Nhiệt độ = ADC/2.046 Ví dụ về đọc nhiệt độ và hiển thị lên led 7 thanh. -Sơ đồ nguyên lý. -Code . #include <16F887.h> #device ADC=10 #FUSES NOWDT,NOBROWNOUT,NOLVP #use delay(crystal=20000000) float value; int8 nhietdo; int8 const Led7[12]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x9c,0xc6}; int8 const Led[6]={0x00,0x01,0x02,0x04,0x08}; Void Quetled(int i,int j){ output_B(Led[0]); output_D(Led7[i]); output_B(Led[j]); delay_ms(3); } void main() { setup_adc_ports(sAN0); setup_adc(ADC_CLOCK_DIV_2); while(TRUE) { set_adc_channel(0); delay_us(10); value = read_adc(); nhietdo=value/2.046; Quetled(nhietdo/1000,1); Quetled(nhietdo%1000/100,2); Quetled(nhietdo%1000%100/10,3); Quetled(nhietdo%10,4); } } 4)Quang trở và hồng ngoại. -Quang trở là: Là một loại điện trở, mà điện trở suất của nó giảm xuống rất nhanh khi có ánh sáng chiếu vào, làm bằng CdS hoạt dộng trên hiện tượng quang dẫn -LED thu hồng ngoại: Cũng tương tự quang trở là điện trở suất của nó giảm xuống rất nhanh khi có tia hồng ngoại chiếu vào. -Cả hai đều có thể coi là những biến trở, giá trị điện trở sẽ thay đổi khi có những điều kiện ảnh hướng tới điện trở suất thay đổi,Vì thế phương pháp sử dụng 2 loại cảm biến này trong thực tế là mắc nối tiếp nó với một điện trở khác để tạo thành mạch phân áp. -Mắc nguyên lý. Từ mạch trên ta thấy, điện áp tại điểm Out sẽ bằng Vout=Vcc/(Rp+R)*R -Rp là điện trở của Phôt Diode , là quang trở hay là led hồng ngoại. -Trong biểu thức ta thấy Vcc và R không đổi còn Rp sẽ thay đổi theo cường độ ánh sáng hay cường độ tia hồng ngoại chiếu vào nó nên => Vcc sẽ thay đổi theo, từ đấy ta đọc ADC và được kết quả để xử lý. Bài tập: Bài 1) Đọc giá trị nhiệt độ cảm biến LM35 hiển thì nhiệt độ lên LED 7 thanh và điều khiển tốc độ quạt.Quạt có 3 số khoảng nhiệt độ từ 27-30 số 1 từ 30-33 số 2, từ 33 trở lên là số 3, nhỏ hơn 27 độ là số 0. HD:dùng re lay để đóng cắt tiếp điểm của quạt.(xem lại phần relay). Bài 2) Thiết kế một đồng hồ vạn năng, đo điện áp một chiều DC đo được điện áp <= 50v Hiển thị kết quả đo trên led 7 thanh. Bài 3) Lập trình hệ thống phát hiện trời tối để bật tắt bong điện chiếu sáng đường. Bài 4) Lập trình hệ thống chống trộm bằng hồng ngoại,hoặc laze khi có trộm thì đóng chuống kêu.(Chuông 12V-DC) Bài 5) Thiết kế bao gồm( Sơ đồ nguyên lý chi tiết+mô phỏng+lập trình) Hệ thống gia nhiệt lò ấp trứng. Lò ấp bao gồm điều khiển + 1 quạt 220V AC trao đổi khí lò ấp với môi trường ngoài,hoạt động theo chu kỳ cứ 1 phút mở một lần 5s. + Nhiệt độ trong lò đặt được bằng nút nhấn. (Trình bày bài 3 sơ đồ nguyên lý trên giấy A4 vẽ sạch sẽ cẩn thận, buổi sau đi học mang theo để kiểm tra và sửa lỗi) Bài 6:HIỂN THỊ LCD 1)Giới thiệu về LCD. - Có rất nhiều loại LCD với nhiều hình dáng và kích thước khác nhau, LCD 16x2 là một loại LCD thông dụng. Hình dáng của loại LCD thông dụng - Khi sản xuất LCD, nhà sản xuất đã tích hợp chíp điều khiển (HD44780) bên trong lớp vỏ và chỉ đưa các chân giao tiếp cần thiết. Các chân này được đánh số thứ tự và đặt tên như hình 2 : Sơ đồ chân của LCD - Chức năng các chân : Mô tả Chân Ký hiệu 1 Vss 2 VDD Chân cấp nguồn cho LCD, khi thiết kế mạch ta nối chân này với VCC=5V của mạch điều khiển 3 VEE Điều chỉnh độ tương phản của LCD. 4 RS Chân nối đất cho LCD, khi thiết kế mạch ta nối chân này với GND của mạch điều khiển Chân chọn thanh ghi (Register select). Nối chân RS với logic “0” (GND) hoặc logic “1” (VCC) để chọn thanh ghi. + Logic “0”: Bus DB0-DB7 sẽ nối với thanh ghi lệnh IR của LCD (ở chế độ “ghi” - write) hoặc nối với bộ đếm địa chỉ của LCD (ở chế độ “đọc” - read) + Logic “1”: Bus DB0-DB7 sẽ nối với thanh ghi dữ liệu DR bên trong LCD. 5 6 7 - 14 R/W Chân chọn chế độ đọc/ghi (Read/Write). Nối chân R/W với logic “0” để LCD hoạt động ở chế độ ghi, hoặc nối với logic “1” để LCD ở chế độ đọc. E Chân cho phép (Enable). Sau khi các tín hiệu được đặt lên bus DB0DB7, các lệnh chỉ được chấp nhận khi có 1 xung cho phép của chân E. + Ở chế độ ghi: Dữ liệu ở bus sẽ được LCD chuyển vào(chấp nhận) thanh ghi bên trong nó khi phát hiện một xung (high-to-low transition) của tín hiệu chân E. + Ở chế độ đọc: Dữ liệu sẽ được LCD xuất ra DB0-DB7 khi phát hiện cạnh lên (low-to-high transition) ở chân E và được LCD giữ ở bus đến khi nào chân E xuống mức thấp. Tám đường của bus dữ liệu dùng để trao đổi thông tin với MPU. Có 2 chế độ sử dụng 8 đường bus này : DB0 - + Chế độ 8 bit : Dữ liệu được truyền trên cả 8 đường, với bit MSB là DB7 bit DB7. + Chế độ 4 bit : Dữ liệu được truyền trên 4 đường từ DB4 tới DB7, bit MSB là DB7 15 - 16 - Nguồn dương cho đèn nền GND cho đèn nền * Ghi chú : Ở chế độ “đọc”, nghĩa là MPU sẽ đọc thông tin từ LCD thông qua các chân DBx. Còn khi ở chế độ “ghi”, nghĩa là MPU xuất thông tin điều khiển cho LCD thông qua các chân DBx. 2)Ghép nối LCD với VDK PIC. -Giao tiếp LCD 16x2 với vi điều khiển PIC16F887 ở chế độ 4 bit. -Sơ đồ ghép nối chân. -Biến trở RV1 để điều chỉnh độ tương phản cho LCD. -CHương trình ví dụ hiển thị LCD, chương trình hiển thị chữ “LOP HOC PIC” ở dòng 1 và dòng 2 sẽ hiển thị một biến tăng từ 0 đến 100 tăng sau mỗi 0.5s. #include <16F887.h> #FUSES NOWDT,NOBROWNOUT,NOLVP #use delay(crystal=20000000) int8 a=0; //Dinh nghia chan cho LCD #define LCD_ENABLE_PIN PIN_D2 #define LCD_RS_PIN PIN_D0 #define LCD_RW_PIN PIN_D1 #define LCD_DATA4 PIN_D3 #define LCD_DATA5 PIN_D4 #define LCD_DATA6 PIN_D5 #define LCD_DATA7 PIN_D6 #define LCD_TYPE 2 #include <LCD.C> void main() { lcd_init(); while(TRUE) { LCD_gotoxy(1,1); printf(lcd_putc,"LOP HOC PIC "); LCD_gotoxy(1,2); printf(lcd_putc,"%d",a); a++; delay_ms(500); } } Các lựa chọn hiển thị biến +%d số nguyên 8 bit có dấu +%u số nguyên 8 bit không dấu +%lu,ld số nguyên lớn int16 hay long có dấu và không dấu +%f số thực +%c ký tự Thêm tiền tố %04d thì giành ra 4 vị trí để hiện thị biến và thêm số 0 đằng trước nếu bằng 0. Bài tập: Bài 1) Hiển thị giá trị nhiệt độ đọc được từ LM35 lên LDC. Bài 2) Thiết kế ( nguyên lý chi tiết, mô phỏng,code) hệ thông điều khiển 8 thiết bị qua 4 nút nhấn và hiển thị trạng thái 8 thiết bị đó trên màn hình LCD. (Yêu cầu nguyên lý như bài trước) Bài 7:BĂM XUNG PWM 1)Pwm -PWM là cái gì mà sao nó được ứng dụng nhiều trong điều khiển. Lấy điển hình nhất mà chúng ta thường hay gặp là điều khiển động cơ và các bộ băm xung áp, điều áp… Sử dụng PWM điều khiển nhanh chậm của động cơ hay cao hơn nữa nó còn được dùng để điều khiển ổn định tốc độ động cơ. Ngoài lĩnh vực điều khiển hay ổn định tải thì PWM nó còn tham gia và điều chế các mạch nguồn như là : boot, buck, nghịch lưu 1 pha và 3 pha…PWM chúng ta còn gặp nhiều trong thực tế và các mạch điện điều khiển. Điều đặc biệt là PWM chuyên dùng để điều khiển các phần tử điện tử công suất có đường đặc tính là tuyến tính khi có sẵn 1 nguồn 1 chiều cố định . Như vậy PWM nó được ứng dụng rất nhiều trong các thiết bị điện điện tử. Điều mà dân điện điện tử dễ dàng nhận ra là PWM chính nhân tố mà các đội Robocon sử dụng để điều khiển động cơ hay ổn định tốc độ động cơ. Trong bài này chúng ta sẽ tìm hiểu phương pháp dùng PIC để tạo xung có độ rộng mong muốn trong điều khiển động cơ điện. Giải thuật như sau: - Tạo một tần số chuẩn từ 0,1 ms đến 10ms dùng các bộ timer của vi điều khiển. - Thay đổi độ rộng xung bằng cách thay đổi tỷ số xung mức 1 và mức 0 của tần số chuẩn đó kết quả cho xung như yêu cầu . Các bạn xem hình vẽ sau : -Với độ rộng xung thay đổi chúng ta có thể thay đổi độ sáng tối của đèn led , tốc độ động cơ .. -Dưới đây là một ứng dụng của PWM trong việc điều khiển động cơ điện 1 chiều -Chương trình với PIC. #include <16F887.h> #FUSES NOWDT,NOBROWNOUT,NOLVP #use delay(crystal=20000000) void main() { setup_timer_2(T2_DIV_BY_16,249,1); setup_ccp1(CCP_PWM|CCP_SHUTDOWN_AC_L|CCP_SHUTDOWN_BD_L); setup_ccp2(CCP_PWM); set_pwm1_duty(0); set_pwm2_duty(0); while(TRUE) { set_pwm1_duty(100); }} 2)Ứng dụng điều khiển động cơ đảo chiều, tăng tốc giảm tốc. -Trước tiên ta tìm hiểu mạch cầu H là gì.? Giả sử bạn có một động cơ DC có 2 đầu A và B, nối 2 đầu dây này với một nguồn điện DC (ắc qui điện – battery). Ai cũng biết rằng nếu nối A với cực (+), B với cực (-) mà động cơ chạy theo chiều thuận (kim đồng hồ) thì khi đảo cực đấu dây (A với (-), B với (+)) thì động cơ sẽ đảo chiều quay. Tất nhiên khi bạn là một “control guy” thì bạn không hề muốn làm công việc “động tay động chân” này (đảo chiều đấu dây), bạn ắt sẽ nghĩ đến một mạch điện có khả năng tự động thực hiện việc đảo chiều này, mạch cầu H (H-Bridge Circuit) sẽ giúp bạn. Như thế, mạch cầu H chỉ là một mạch điện giúp đảo chiều dòng điện qua một đối tượng. Tuy nhiên, rồi bạn sẽ thấy, mạch cầu H không chỉ có một tác dụng “tầm thường” như thế. Nhưng tại sao lại gọi là mạch cầu H, đơn giản là vì mạch này có hình chữ cái H. Xem minh họa trong hình 1. Hình 1. Mạch cầu H. Trong hình 1, hãy xem 2 đầu V và GND là 2 đầu (+) và (-) của ắc qui, “đối tượng” là động cơ DC mà chúng ta cần điều khiển, “đối tượng” này có 2 đầu A và B, mục đích điều khiển là cho phép dòng điện qua “đối tượng” theo chiều A đến B hoặc B đến A. Thành phần chính tạo nên mạch cầu H của chúng ta chính là 4 “khóa” L1, L2, R1 và R2 (L: Left, R:Right). Ở điều kiện bình thường 4 khóa này “mở”, mạch cầu H không hoạt động. Tiếp theo chúng ta sẽ khảo sát hoạt động của mạch cầu H thông qua các hình minh họa 2a và 2b. Hình 2. Nguyên lý hoạt động mạch cầu H. Giả sử bằng cách nào đó (cái cách nào đó chính là nhiệm vụ của người thiết kế mạch) mà 2 khóa L1 và R2 được “đóng lại” (L2 và R1 vẫn mở), bạn dễ dàng hình dung có một dòng điện chạy từ V qua khóa L1 đến đầu A và xuyên qua đối tượng đến đầu B của nó trước khi qua khóa R2 và về GND (như hình 2a). Như thế, với giả sử này sẽ có dòng điện chạy qua đối tượng theo chiều từ A đến B. Bây giờ hãy giả sử khác đi rằng R1 và L2 đóng trong khi L1 và R2 mở, dòng điện lại xuất hiện và lần này nó sẽ chạy qua đối tượng theo chiều từ B đến A như trong hình 2b (V>R1->B->A->L2->GND). Vậy là đã rõ, chúng ta có thể dùng mạch cầu H để đảo chiều dòng điện qua một “đối tượng” (hay cụ thể, đảo chiều quay động cơ) bằng “một cách nào đó”. Chuyện gì sẽ xảy ra nếu ai đó đóng đồng thời 2 khóa ở cùng một bên (L1 và L2 hoặc R1 và R2) hoặc thậm chí đóng cả 4 khóa? Rất dễ tìm câu trả lời, đó là hiện tượng “ngắn mạch” (short circuit), V và GND gần như nối trực tiếp với nhau và hiển nhiên ắc qui sẽ bị hỏng hoặc nguy hiểm hơn là cháy nổ mạch xảy ra. Cách đóng các khóa như thế này là điều “đại kị” đối với mạch cầu H. Để tránh việc này xảy ra, người ta thường dùng thêm các mạch logic để kích cầu H, chúng ta sẽ biết rõ hơn về mạch logic này trong các phần sau. Giả thuyết cuối cùng là 2 trường hợp các khóa ở phần dưới hoặc phần trên cùng đóng (ví dụ L1 và R1 cùng đóng, L2 và R2 cùng mở). Với trường hợp này, cả 2 đầu A, B của “đối tượng” cùng nối với một mức điện áp và sẽ không có dòng điện nào chạy qua, mạch cầu H không hoạt động. Đây có thể coi là một cách “thắng” động cơ (nhưng không phải lúc nào cũng có tác dụng). Nói chung, chúng ta nên tránh trường hợp này xảy ra, nếu muốn mạch cầu không hoạt động thì nên mở tất cả các khóa thay vì dùng trường hợp này. -Giới thiệu ic tích hợp cầu H. IC L298 -Nguyên lý của IC. -Trong L298 tích hợp 2 cầu H như trong hình. Các chân : +IN1(5), IN2(7) là đầu vào cầu A (Chân PWM) +IN3(10), IN4 (12)là đầu vào cầu B (Chân PWM) +ENA(6),ENB(11) là 2 chân cho phép cầu A và cầu B.(mức cao cho phép) +Vss(9) điện áp 5V. +VDD(4) điện áp 12V. +Out1(2),Out2(3) là đầu ra của cầu A. (Nối ra động cơ.) +Out3(13),Out4(14) là đầu ra của cầu B. (Nối ra động cơ.) +GND(8),ISENA(1), ISENA(15) nối đất (Chân 1 và 15 nối mạch to,vì chịu dòng lớn) -Dưới đây là mạch nguyên lý khi vẽ mạch của L298 -Vậy việc ghép nối với VDK bây giờ là đơn giản. -Ta có mạch VDK và nút nhấn để điều khiển . Sau đây ta sẽ lập trình cho 4 nút nhấn là 4 tốc độ.khác nhau 25%,50%,75%,100%. #include <16F887.h> #FUSES NOWDT,NOBROWNOUT,NOLVP #use delay(crystal=20000000) #define button_1 input(PIN_B0)==0 #define button_2 input(PIN_B1)==0 #define button_3 input(PIN_B2)==0 #define button_4 input(PIN_B3)==0 void main() { setup_timer_2(T2_DIV_BY_16,249,1); //800 us overflow, 800 us interrupt setup_ccp1(CCP_PWM|CCP_SHUTDOWN_AC_L|CCP_SHUTDOWN_BD_L); setup_ccp2(CCP_PWM); set_pwm1_duty(0); set_pwm2_duty(0); while(TRUE) { if(button_1) set_pwm1_duty(64); if(button_2) set_pwm1_duty(127); if(button_3) set_pwm1_duty(191); if(button_4) set_pwm1_duty(255); } } Sau khi học PWM muốn ổn định được tốc độ động cơ ta sẽ phải đo được tốc độ để điều khiển.Cái này được học trong các bài sau. Bài tập Bài 1: Thiết kế một ứng dụng điều khiển động cơ DC-12V,gồm có các yêu cầu sau. +Đảo chiều thuận ngươc, tăng tốc giảm tốc trơn. +Hiển thị chế độ quay thuận hay ngược trên LCD. +Hiển thị độ rộng xung băm trên LCD độ rộng theo %. Bài 8:NGẮT NGOÀI. 1. Ngắt ngoài - Trong PIC 16F887 có 1 ngõ vào ngắt ngoài tại chân INT (RB0). Khi có một sự kiện trên chân INT (cạnh xuống hoặc cạnh lên) sẽ sinh ra một ngắt (nếu được cho phép). - Để sử dụng chức năng ngắt ngoài ta phải làm như sau: · Chọn kiểu ngắt: (cạnh lên hoặc cạnh xuống). · Kích hoạt ngắt ngoài. · Kích hoạt ngắt toàn cục. 2. Ví dụ ngắt ngoài -Ở ví dụ này cứ mỗi lần xảy ra ngắt thì sẽ thay đổi trạng thái của PIN_A0. -Tạo ngắt ngoài bằng nút nhấn. -Nguyên lý: -Code . #include <16F887.h> #FUSES NOWDT,NOBROWNOUT,NOLVP #use delay(crystal=20000000) int1 a=0; #INT_EXT //Bắt đầu chương trình ngắt ngoài void EXT_isr(void) //Chương trình ngắt ngoài. { a++; output_bit(PIN_A0,a); } void main() { enable_interrupts(INT_EXT); //Cho phép ngắt ngoài ext_int_edge(H_TO_L);// chon ngat canh xuong enable_interrupts(GLOBAL); //Cho phép ngắt toàn cục. while(TRUE) { } } 3)Ứng dụng đo tốc độ động cơ. -Tìm hiểu về encoder. -Nguyên lý cơ bản của encoder, đó là một đĩa tròn xoay, quay quanh trục. Trên đĩa có các lỗ (rãnh). Người ta dùng một đèn led để chiếu lên mặt đĩa. Khi đĩa quay, chỗ không có lỗ (rãnh), đèn led không chiếu xuyên qua được, chỗ có lỗ (rãnh), đèn led sẽ chiếu xuyên qua. Khi đó, phía mặt bên kia của đĩa, người ta đặt một con mắt thu. Với các tín hiệu có, hoặc không có ánh sáng chiếu qua, người ta ghi nhận được đèn led có chiếu qua lỗ hay không. -Hình ảnh xung encoder - Các bạn thấy rằng, cứ mỗi lần quay qua một lỗ, thì encoder sẽ tăng một đơn vị trong biến đếm.Vậy nếu encoder có 100 lỗ tức 100 xung,ta cứ đếm được 100 xung thì động cơ quay được 1 vòng, bằng cách đo số xung trong 1s thì ta sẽ biết được tốc độ động cơ. Là vòng trên giây. VD: Trong mootj giây ta đo được 500 xung thì tức là động cơ đang hoạt động với tốc độ 5 Vong/s. -VD : đo tốc độ động cơ có gắn encoder. -Nguyên lý: -Code. #include <16F887.h> #FUSES NOWDT,NOBROWNOUT,NOLVP #use delay(crystal=20000000) int16 spirt=0; int8 tocdo; //Dinh nghia chan cho LCD #define LCD_ENABLE_PIN PIN_D2 #define LCD_RS_PIN PIN_D0 #define LCD_RW_PIN PIN_D1 #define LCD_DATA4 PIN_D3 #define LCD_DATA5 PIN_D4 #define LCD_DATA6 PIN_D5 #define LCD_DATA7 PIN_D6 #define LCD_TYPE 2 #include <LCD.C> #INT_EXT void EXT_isr(void) { ++spirt; } void main() { setup_timer_2(T2_DIV_BY_16,249,1); //800 us overflow, 800 us interrupt setup_ccp1(CCP_PWM|CCP_SHUTDOWN_AC_L|CCP_SHUTDOWN_BD_L); set_pwm1_duty(0); enable_interrupts(INT_EXT); ext_int_edge(H_TO_L);// chon ngat canh xuong enable_interrupts(GLOBAL); LCD_init(); set_pwm1_duty(100); while(TRUE) { delay_ms(1000); tocdo=spirt/100; lcd_gotoxy(1,1); printf(lcd_putc,"TOC DO %d ",tocdo); } } Bài tập: Bài 1) Lập trình đo tần số của một nguồn tín hiệu xung vuông điện áp 50V.Hiển thị lên LCD. Bài 2) Thiết kế bài toán điều khiển động cơ DC-12V phương pháp PWM có đảo chiều tăng giảm trơn tốc độ bằng nút nhấn,hiển thị tốc độ động cơ trên LCD. Bài 9:TIMER-COUNTER 1. Timer/Counter trong PIC 16F887: Trong Pic16f887 có 3 timer : + Timer0 : 8 bit + Timer1 : 16 bit + Timer2 : 8 bit Timer dùng cho nhiều ứng dụng : định thời, capture, pwm, ... 1.Timer0 Thanh ghi tác động: Code: setup_TIMER_0(mode); setup_COUNTERS (rtcc_state, ps_state); // hay setup_WDT() set_TIMER0(value); // hay set_RTCC(value) : xác định giá trị ban đầu (8bit) cho Timer0 get_TIMER0(); // hay get_RTCC() :trả về số nguyên (8bit) của Timer0 ////////////////////////////////////////////////////////////////////////////// Trong đó mode là một hoặc hai constant (nếu dùng hai thì chèn dấu "|"ở giữa) được định nghĩa trong file 16F877A.h gồm : RTCC_INTERNAL : chọn xung clock nội RTCC_EXT_L_TO_H : chọn bit cạnh lên trên chân RA4 RTCC_EXT_H_TO_L : chọn bit cạnh xuống trên chân RA4 RTCC_DIV_2 :chia prescaler 1:2 RTCC_DIV_4 1:4 RTCC_DIV_8 1:8 RTCC_DIV_16 1:16 RTCC_DIV_32 1:32 RTCC_DIV_64 1:64 RTCC_DIV_128 1:128 RTCC_DIV_256 1:256 rtcc_state là một trong những constant sau: RTCC_INTERNAL RTCC_EXT_L_TO_H RTCC_EXT_H_TO_L ps_state là một trong những constant sau: RTCC_DIV_2 RTCC_DIV_4 RTCC_DIV_8 RTCC_DIV_16 RTCC_DIV_32 RTCC_DIV_64 RTCC_DIV_128 RTCC_DIV_256 WDT_18MS WDT_36MS WDT_72MS WDT_144MS WDT_288MS WDT_576MS WDT_1152MS WDT_2304MS 2.Timer1 Thanh ghi tác động: Code: setup_TIMER_1(mode); set_TIMER1(value); // xác định giá trị ban đầu (16bit) cho Timer1 get_TIMER1(); // trả về số nguyên (16bit) của Timer1 /////////////////////////////////////////////////////////////////////// mode gồm (có thể kết hợp bằng dấu "|"): T1_DISABLED : tắt Timer1 T1_INTERNAL : xung clock nội (Fosc/4) T1_EXTERNAL : xung clock ngoài trên chân RC0 T1_EXTERNAL_SYNC : xung clock ngoài đồng bộ T1_CLK_OUT T1_DIV_BY_1 T1_DIV_BY_2 T1_DIV_BY_4 T1_DIV_BY_8 3.Timer2 Thanh ghi tác động Code: setup_TIMER_2(mode, period, postscale); set_TIMER2(value); // xác định giá trị ban đầu (8bit) cho Timer2 get_TIMER2(); // trả về số nguyên 8bit /////////////////////////////////////////////////////////////////////// Với mode gồm (co the ket hop bang dau "|"): T2_DISABLED T2_DIV_BY_1 T2_DIV_BY_4 T2_DIV_BY_16 period là số nguyên từ 0-255, xác định giá trị xung reset postscale là số nguyên 1-16, xác định reset bao nhiêu lần trước khi ngắt. 2. Ví dụ về sử dụng timer: - Sau đây sẽ làm một ví dụ về cách sử dụng timer 0. Cứ sau 0.5s thì sẽ tăng một biến a lên 1 đơn vị ,a chạy từ 0 đến 99.Hiển thị giá trị biến lên LCD. - Sơ đồ nguyên lý Tính toán thời gian tràn (thời gian xảy ra ngắt cho timer). -Do thời gian tràn lớn nên ta dùng giao động của CPU/256 để làm tần số cho bộ timer 0. Ftimer0= 20M/(256*4) = 19531 Hz -Tức là trong một giây timer0 đếm được 19531 đơn vị,=>0.5s đếm được 9766 đơn vị. Mà timer0 là một timer 8 bit sẽ tràn sau 256 đơn vị nên để đếm được 9766 đơn vị timer0 sẽ phải tràn 9766/256=38 lần. -Chương trình (code) #include <16F887.h> #FUSES NOWDT,NOBROWNOUT,NOLVP #use delay(crystal=20000000) #define LCD_ENABLE_PIN PIN_D2 #define LCD_RS_PIN PIN_D0 #define LCD_RW_PIN PIN_D1 #define LCD_DATA4 PIN_D3 #define LCD_DATA5 PIN_D4 #define LCD_DATA6 PIN_D5 #define LCD_DATA7 PIN_D6 #define LCD_TYPE 2 #include <LCD.C> int8 a=0,i=0; #INT_TIMER0 //Chuong trinh ngat timer0 void TIMER0_isr(void) { set_timer0(0); i++; if(i==46){ i=0;a++; if (a==99) a=0;} } void main() { //Chọn giao động CPU/256 setup_timer_0(RTCC_INTERNAL|RTCC_DIV_256|RTCC_8_bit); LCD_init(); enable_interrupts(INT_TIMER0); //Cho phép ngắt Timer0 enable_interrupts(GLOBAL); while(TRUE) { lcd_gotoxy(1,1); printf(lcd_putc,"LOP HOC PIC "); lcd_gotoxy(1,2); printf(lcd_putc,"%d",a); } } -Timer là một bộ định thời, ở ví dụ sau ta sẽ dùng nó để quét LED 7 thanh.Và sau này trở đi thì cứ quét led 7 thanh ta sẽ dùng timer. -Sơ đồ mô phỏng. -Code: #include <16F887.h> #FUSES NOWDT,NOBROWNOUT,NOLVP #use delay(crystal=20000000) int8 const LED[10]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90}; int8 const CA[5]={0x00,0x01,0x02,0x04,0x08}; int i=0; int16 bienchay=0; void Quet_led(int8 so,int8 ledhienthi){ output_D(CA[0]); output_C(LED[so]); output_D(CA[ledhienthi]); } #INT_TIMER0 void TIMER0_isr(void) { i++; if(i==5) i=1; if(i==1) Quet_led(bienchay/1000,i); if(i==2) Quet_led((bienchay%1000)/100,i); if(i==3) Quet_led((bienchay%100)/10,i); if(i==4) Quet_led(bienchay%10,i); } void main() { setup_timer_0(RTCC_INTERNAL|RTCC_DIV_256|RTCC_8_bit); enable_interrupts(INT_TIMER0); enable_interrupts(GLOBAL); while(TRUE) { bienchay++; delay_ms(300); } } //13.1 ms overflow -Ta thấy trong chương trình chính không còn phải quét led nữa, ta giành thời gian của CPU để làm các việc khác, còn quét led thì timer đã giải quyết. -Bộ timer này ta có thể dùng nhiều việc ví dụ như định thời để đọc cảm biến, quét led matrix hay tính thời gian trong đồng hồ đếm ngược, nói chung là các ứng dụng liên quan tới thời gian, nhưng nó không dùng để cho thời gian thực. 3.Ví dụ về sử dụng counter: -Tương tự như timer nhưng counter lại lấy dao động ngoài để đếm chứ không phải dao động nội,các bạn xem ví dụ sau sẽ hiểu ngay. -Hình ảnh mô phỏng. -Chương trình. #include <16F887.h> #FUSES NOWDT,NOBROWNOUT,NOLVP #use delay(crystal=20000000) #define LCD_ENABLE_PIN PIN_C2 #define LCD_RS_PIN PIN_C0 #define LCD_RW_PIN PIN_C1 #define LCD_DATA4 PIN_C3 #define LCD_DATA5 PIN_C4 #define LCD_DATA6 PIN_C5 #define LCD_DATA7 PIN_C6 #define LCD_TYPE 2 #include <LCD.C> int a; void main() { setup_timer_0(RTCC_EXT_H_TO_L|RTCC_DIV_1|RTCC_8_bit); enable_interrupts(INT_TIMER0); enable_interrupts(GLOBAL); lcd_init(); set_timer0(0); while(TRUE) { a=get_timer0(); lcd_gotoxy(1,1); printf(lcd_putc,"Helo..!"); lcd_gotoxy(1,2); printf(lcd_putc,"DA NHAN %02d LAN",a); } } -Mỗi khi nút nhấn,thì tạo ta một xung clock , làm thanh gi của timer0 tăng lên một đơn vị,Hàm get_timer0(); là hàm trả về giá trị của thanh gi timer0 thời điểm đó. -Nhưng đây là ví dụ để cho các bạn hiểu về counter thôi, chứ không ai dùng để đếm lần nhấn nút cả, đếm nút nhấn ta chỉ cần dùng hàm input đơn giản là được rồi. -Counter người ta dùng để đếm xung, đếm sự kiện. Ví dụ cho một song vuông có tần số f vào chân A4 thì khi đó bộ counter sẽ đếm, sau một thời gian nhất định nào đấy ta sẽ đọc giá trị trong thanh gi timer ra và biết được số xung đếm được, từ đó có số xung có khoảng thời gian ta suy ra được tần số. -Sau đây chúng ta sẽ đi làm một ví dụ đo tần số của một tín hiệu xung vuông. -Sơ đồ nguyên lý : -Code: #include <16F887.h> #FUSES NOWDT,NOBROWNOUT,NOLVP #use delay(crystal=20000000) #define LCD_ENABLE_PIN PIN_C2 #define LCD_RS_PIN PIN_C0 #define LCD_RW_PIN PIN_C1 #define LCD_DATA4 PIN_C3 #define LCD_DATA5 PIN_C4 #define LCD_DATA6 PIN_C5 #define LCD_DATA7 PIN_C6 #define LCD_TYPE 2 #include <LCD.C> int clock; void main() { setup_timer_0(RTCC_EXT_L_TO_H|RTCC_DIV_1|RTCC_8_bit); enable_interrupts(INT_TIMER0); enable_interrupts(GLOBAL); lcd_init(); set_timer0(0); while(TRUE) { clock=get_timer0(); set_timer0(0); lcd_gotoxy(1,1); printf(lcd_putc,"XUNG CO TAN SO"); lcd_gotoxy(1,2); printf(lcd_putc,"F=%03d Hz",clock); delay_ms(1000); } } -Trong chương trình trên cứ sau 1s ta sẽ đọc giá trị timer0 một lần và số xung đếm được chính là tần số của nguồn tín hiệu, chú ý sau khi đọc được giá trị timer0 ta phải set timer0 về giá trị 0 để bắt đầu đếm lại. Bài tập: Bài 1) Thiết kế hệ thống điều khiển độ cơ DC-12V có đảo chiều tăng giảm tốc độ dùng mạch cầu H,hoặc relay,có đo tốc độ và ổn định tốc độ đặt khi có thay đổi tải dùng thuật toán P hoặc PI hoặc PID. Bài 10:GIAO TIẾP I2C LẬP TRÌNH THỜI GIAN THỰC_DS1307 1. Sơ qua về giao tiếp I2C. Giao thức ưu tiên truyền thông nối tiếp được phát triển bởi Philips Semiconductor và được gọi là bus I2C. Vì nguồn gốc nó được thiết kế là để điều khiển liên thông IC (Inter-Intergrated Circuit) nên nó được đặt tên là I2C. Tất cả các chip có tích hợp và tương thích với I2C đều có thêm một giao diện tích hợp trên Chip để truyền thông trực tiếp với các thiết bị tương thích I2C khác. Việc truyền dữ liệu nối tiếp theo hai hướng 8 bit được thực thi theo 3 chế độ sau: Chuẩn (Standard)— 100 Kbits/sec Nhanh (Fast)—400 Kbits/sec Tốc độ cao (High speed)—3.4 Mbits/sec Đường bus thực hiện truyền thông nối tiếp I2C gồm hai đường là đường truyền dữ liệu nối tiếp SDA và đường truyền nhịp xung đồng hồ nối tiếp SCL. Vì cơ chế hoạt động là đồng bộ nên nó cần có một nhịp xung tín hiệu đồng bộ. Các thiết bị hỗ trợ I2C đều có một địa chỉ định nghĩa trước, trong đó một số bit địa chỉ là thấp có thể cấu hình. Đơn vị hoặc thiết bị khởi tạo quá trình truyền thông là đơn vị Chủ và cũng là đơn vị tạo xung nhịp đồng bộ, điều khiển cho phép kết thúc quá trình truyền. Nếu đơn vị Chủ muốn truyền thông với đơn vị khác nó sẽ gửi kèm thông tin địa chỉ của đơn vị mà nó muốn truyền trong dữ liệu truyền. Đơn vị Tớ đều được gán và đánh địa chỉ thông qua đó đơn vị Chủ có thể thiết lập truyền thông và trao đổi dữ liệu. Bus dữ liệu được thiết kế để cho phép thực hiện nhiều đơn vị Chủ và Tớ ở trên cùng Bus. Quá trình truyền thông I2C được bắt đầu bằng tín hiệu start tạo ra bởi đơn vị Chủ. Sau đó đơn vị Chủ sẽ truyền đi dữ liệu 7 bit chứa địa chỉ của đơn vị Tớ mà nó muốn truyền thông, theo thứ tự là các bit có trọng số lớn nhất MSB sẽ được truyền trước. Bit thứ tám tiếp theo sẽ chứa thông tin để xác định đơn vị Tớ sẽ thực hiện vai trò nhận (0) hay gửi (1) dữ liệu. Tiếp theo sẽ là một bit ACK xác nhận bởi đơn vị nhận đã nhận được 1 byte trước đó hay không. Đơn vị truyền (gửi) sẽ truyền đi 1 byte dữ liệu bắt đầu bởi MSB. Tại điểm cuối của byte truyền, đơn vị nhận sẽ tạo ra một bit xác nhận ACK mới. Khuôn mẫu 9 bit này (gồm 8 bit dữ liệu và 1 bit xác nhận) sẽ được lặp lại nếu cần truyền tiếp byte nữa. Khi đơn vị Chủ đã trao đổi xong dữ liệu cần nó sẽ quan sát bit xác nhận ACK cuối cùng rồi sau đó sẽ tạo ra một tín hiệu dừng STOP để kết thúc quá trình truyền thông. I2C là một giao diện truyền thông đặc biệt thích hợp cho các ứng dụng truyền thông giữa các đơn vị trên cùng một bo mạch với khoảng cách ngắn và tốc độ thấp. Ví dụ như truyền thông giữa CPU với các khối chức năng trên cùng một bo mạch như EEPROM, cảm biến, đồng hồ tạo thời gian thực… Hầu hết các thiết bị hỗ trợ I2C hoạt động ở tốc độ 400Kbps, một số cho phép hoạt động ở tốc độ cao vài Mbps. I2C khá đơn giản để thực thi kết nối nhiều đơn vị vì nó hỗ trợ cơ chế xác định địa chỉ. 2. Giới thiệu về thời gian thực DS13307 -DS1307 là chip thời gian thực hay RTC (Read time clock). Đây là một IC tích hợp cho thời gian bởi vì tính chính xác về thời gian tuyệt đối cho thời gian : Thứ, ngày,tháng, năm, giờ, phút, giây. DS1307 là chế tạo bởi Dallas. Chip này có 7 thanh ghi 8 bit mỗi thanh ghi này chứa : Thứ , ngày, tháng, năm, giờ , phút, giây. Ngoài ra DS1307 còn chứa 1 thanh ghi điều khiển ngõ ra phụ và 56 thanh ghi trống các thanh ghi này có thể dùng như là RAM. DS1307 được đọc thông qua chuẩn truyền thông I2C nên do đó để đọc được và ghi từ DS1307 thông qua chuẩn truyền thông này. Do nó được giao tiếp chuẩn I2C nên cấu tạo bên ngoài nó rất đơn giản. Ví dụ 1 dạng đóng vỏ của DS1307 như sau : Trên là hai dạng cấu tạo của DS1307. Chip này có 8 chân và chúng ta hay dùng là dạng Dip và các chân nó được mô tả như sau : + X1 và X2 là đầu vào dao động cho DS1307. Cần dao động thạch anh 32.768Khz. + Vbat là nguồn nuôi cho chip. Nguồn này từ ( 2V- 3.5V) ta lấy pin có nguồn 3V. Đây là nguồn cho chip hoạt động liên tục khi không có nguồn Vcc mà DS1307 vẫn hoạt động theo thời gian + Vcc là nguồn cho giao tiếp I2C. Điện áp cung cấp là 5V chuẩn và được dùng chung với vi xử lý. Nếu mà Vcc không có mà Vbat có thì DS1307 vẫn hoạt động bình thường nhưng mà không ghi và đọc được dữ liệu. + GND là nguồn Mass chung cho cả Vcc và Vbat + SQW/OUT là một ngõ ra phụ tạo xung dao động (xung vuông). Chân này tôi nghĩ không ảnh hưởng đến thời gian thực nên chúng ta không sử dụng chân này trong thời gian thực và bỏ trống chân này! + SCL và SDA là hai bus dữ liệu của DS1307. Thông tin truyền và ghi đều được truyền qua 2 đường truyền này theo chuẩn I2C II) Ghép nối DS1307 với vi điều khiển Do DS1307 giao tiếp chuẩn I2C nên việc ghép nối nó với vi điều khiển khá là đơn giản và theo datasheet thì tôi đưa ra sơ đồ sau : Ds1307 nó chỉ giao tiếp với vi điều khiển với 2 đường truyền SCL và SDA nên do đó trên vi xử lý cần phải xác định chân nào trên vi xử lý nó có SCL và SDA để nối với DS1307 cái này đối với dòng PIC, AVR còn với dòng Psoc nó có sự khác tùy theo kiều Fimware hay harware mà các chân SDA và SCL nó sẽ nằm ở chân nào cái được thiết lập trong phần mền. III : Tổ chức thanh ghi trong DS1307 Cấu tạo bên trong của DS1307 bao gồm mạch nguồn, dao động, logic và con trỏ ,thanh ghi thực hiện việc ghi đọc. Do trong các bài toán chúng ta thường sử dụng DS1307 cho đồng hồ thời gian thực nên do đó chúng ta chỉ quan tâm đến việc ghi đọc các thanh ghi cần thiết (sec, min, hour…) thông qua chuẩn truyền thông I2C còn các thanh ghi khác thì chúng ta có thể tìm hiểu kỹ trong datasheet! Vì các thanh ghi đó được coi như là RAM lưu trữ. Nên do đó tôi chỉ giới thiệu các thanh ghi có chức năng thời gian thực phục vụ cho bài toán thời gian. Trong bộ nhớ của DS1307 có tất cả 64 thanh ghi địa chỉ từ 0 đến 63 và được bắt đầu từ 0x00 đến 0x3F nhưng trong đó chỉ có 8 thanh ghi đầu là thanh ghi thời gian thực nên chúng ta sẽ đi sâu vào 8 thanh ghi ( chức năng và địa chỉ thanh ghi thời gian thực này). Nhìn vào bảng thanh ghi trong datasheet ta sẽ thấy như sau : Nhìn vào bảng trên chúng ta thấy các thanh ghi thời gian thực nó được sắp sếp theo thứ tự : giây, phút, giờ, thứ, ngày , tháng, năm và bắt đầu từ thanh ghi Giây (0x00) và kết thúc bằng thanh ghi năm (0x06). Riêng thanh ghi Control dùng để điều khiển ngõ ra của chân SQW/OUT nên trong thực tế nên không mấy ai sử dụng thanh ghi này trong thời gian thực nên chúng ta bỏ qua thanh ghi này! Do 7 thanh ghi đầu tiên là khá quan trọng cho thời gian thực và là thanh ghi quan trọng nhất trong con DS1307 nên chúng ta phải hiểu được cách tổ chức thanh ghi này trong DS1307. Tôi tham khảo datasheet và đưa ra tổ chức thanh ghi trong datasheet thời gian thực như sau : Nhìn bảng trên chúng ta thấy các thanh ghi được mã hóa theo bit. Mỗi bit trong thanh ghi đều có chức năng riêng và tôi sẽ trình bày chi tiết như sau : + Thanh ghi giây (0x00) : Đây là thanh ghi giây của DS1307. Nhìn trên bảng trên ta thấy được từ bit 0 đến bit 3 là dùng để mã hóa số BCD hàng đơn vị của giây. Tiếp theo từ bit 4 đến bit 6 dùng để mã hóa BCD hàng chục của giây. Tại sao nó chỉ sử dụng có 3 bit này là do giây của chúng ta lớn nhất chỉ đến 59 nên hàng chục lớn nhất là 5 nên chỉ cần 3 thanh ghi này là cũng đủ mã hóa rồi! Còn bit thứ 7 có tên là “CH” theo tôi nó có nghĩa là “ Clock Halt – Treo đồng hồ” Do đó nếu mà bit 7 này mà được đưa lên 1 tức là khóa đồng hồ nên do đó nó vô hiệu hóa chip và chip không hoạt động. Nên do vậy lúc nào cũng phải cho bit 7 này luôn xuống 0 từ lúc đầu( cái này sử dụng lệnh end với 0x7F) + Thanh ghi phút (0x01) : Đây là thanh ghi phút của DS1307. Cũng nhìn trên bảng thanh ghi này được tổ chức như thanh ghi giây. Cũng là 3 bit thấp dùng để mã hóa BCD chữ số hàng đơn vị và số hàng trục chỉ lớn nhất là 5 nên do đó chỉ cần dùng từ bit 4 đến bit 6 để mã hóa BCD tiếp chữ số hàng chục. Nhưng thanh ghi này có sự khác biệt với thanh ghi giây là bit 7 nó đã mặc định bằng 0 rồi nên do đó chúng ta không phải làm gì với bit 7 mà kệ nó! + Thanh ghi giờ (0x02) : Đây là thanh ghi giờ của DS1307 và tôi thấy thanh ghi này được coi là phức tạp nhất vì nó lằng nhà lằng nhằng nhưng mà nhìn bảng thì thấy các tổ chức của nó cũng hợp lý. Trước tiên chúng ta thấy được rằng từ bit 0 đến bit 3 nó dùng để mã hóa BCD của chữ số hàng đơn vị của giờ. Nhưng mà giờ nó còn có chế độ 24h và 12h nên do đó nó phức tạp ở các bit cao (bit 4 đến bit 7) và sự chọn chế độ 12h và 24h nó lại nằm ở bit 6. Nếu bit 6=0 thì ở chế độ 24h thì do chữ số hàng trục lớn nhất là 2 nên do đó nó chỉ dùng 2 bit ( bit 4 và bit 5 ) để mã hóa BCD chữ số hàng trục của giờ. Nếu bit 6 =1 thì chế độ 12h được chọn nhưng do chữ số của hàng trục của giờ trong chế độ này chỉ lớn nhất là 1 nên do đó bit thứ 4 là đủ để mã hóa BCD chữ số hàng trục của giờ rồi nhưng mà bit thứ 5 nó lại dùng để chỉ buổi sáng hay chiều, nếu mà bit 5 = 0 là AM và bit 5 =1 là PM. Trong cả 2 chế độ 12h và 24h thì bit 7 =0 nên ta ko cần chú ý đến thanh ghi này. + Thanh ghi thứ (0x03): Đây là thanh ghi thứ trong tuần của DS1307 và thanh ghi này khá là đơn giản trong DS1307. Nó dùng số để chỉ thứ trong tuần nên do đó nó chỉ lấy từ 1 đến 7 tương đương từ thứ hai đến chủ nhật. Nên do đó nó dùng 3 bit thấp (bit 0 đến bit 2) để mã hóa BCD ra thứ trong ngày. Còn các bit từ 3 đến 7 thì nó mặc định bằng 0 và ta không làm gì với các bit này! + Thanh ghi ngày (0x04) : Đây là thanh ghi ngày trong tháng của DS1307. Do trong các tháng có số ngày khác nhau nhưng mà nằm trong khoảng từ 1đến 31 ngày. Do đó thanh ghi này các bit được tổ chức khá là đơn giản. Nó dùng 4 bit thấp (bit0 đến bit 3) dùng để mã hóa BCD ra chữ số hàng đơn vị của ngày trong tháng. Nhưng do chữ số hàng trục của ngày trong tháng chỉ lớn nhất là 3 nên chỉ dùng bit 4 và bit 5 là đủ mã hóa BCD rồi. Còn bit 6 và bit 7 chúng ta không làm gì và nó mặc định bằng 0. + Thanh ghi tháng (0x05) : Đây là thanh ghi tháng trong năm của DS1307. Tháng trong năm chỉ có từ 1 đến 12 tháng nên việc tổ chức trong bit cũng tương tự như ngày trong tháng nên do cũng 4 bit thấp (từ bit 0 đến bit 3) mã hóa BCD hàng đơn vị của tháng. Nhưng do hàng chục chỉ lớn nhất là 1 nên chỉ dùng 1 bit thứ 4 để mã hóa BCD ra chữ số hàng trục và các bit còn lại từ bit 5 đến bit 7 thì bỏ trống và nó mặc định cho xuống mức 0. + Thanh ghi năm (0x06): Đây là thanh ghi năm trong DS1307. DS1307 chỉ có 100 năm thôi tương đương với 00 đến 99 nên nó dùng tất cả các bit thấp và bit cao để mã hóa BCD ra năm! + Thanh ghi điều khiển (0x07): Đây là thanh ghi điều khiển quá trình ghi của DS1307 và Quá trình ghi phải được kết thúc bằng địa chỉ 0x93. IV: Tổng kết Như chúng ta đã biết thì DS1307 nó mã hóa ra số BCD như tôi đã nói ở trên do đó khi ghi vào các thanh ghi này cũng phải là số BCD. Vì vậy việc đọc và ghi thì đều là giá trị BCD trong lập trình thì việc đưa các giá trị BCD này vào khó khăn nên chúng ta thường dùng biến đổi qua lại giữa BCD và thập lục phân để dễ dàng kiểm soát của các giá trị của thanh ghi. Tôi lấy ví dụ như thế này : Thanh ghi giờ cho giá trị là 0x10 đây là mã BCD nhưng mà khi chuyển sang mã thập lục phân thì giá trị nó là 16 Cái này các bạn tìm hiểu các chuyển đổi và cấu tạo của hai mã này. Ở đây tôi không nói về nó! Nói chung trong LED 7 vạch thì các mã BCD này rất tiện dùng vì LED 7 được mã hóa theo BCD khi dùng thêm con mã hóa 7447 chả hạn! Trong quá trình ghi dữ liệu cho các thanh ghi thời gian thực chúng ta cũng phải chuyển đổi thành mã BCD tương ứng sau đó mới ghi vào cho DS1307. 3.Giao tiếp PIC với DS1307. -Chuẩn I2C với PIC,trong PIC đã hỗ trợ sẵn phần cứng giao tiếp I2C nên chúng ta chỉ cần sử dụng thôi.Ta viết một thư viện cho DS1307 để tiện cho việc sử dụng. -Thư viện DS1307.(Địa chỉ DS1307 là 0x68 ) #use i2c(master, sda=DS1307_SDA, scl=DS1307_SCL) //Cấu hình I2C void init_DS1307() //Hàm khởi tạo DS1307 { output_float(DS1307_SCL); // Xuất ra tín hiệu trống output_float(DS1307_SDA); // Xuất ra tín hiệu trống } void write_DS1307(byte address, BYTE data) Hàm viết vào thanh ghi DS1307 { short int status; i2c_start(); // Tạo xung start i2c_write(0xd0); // Gửi đi địa chỉ của DS1307 i2c_write(address); i2c_write(data); i2c_stop(); i2c_start(); status=i2c_write(0xd0); while(status==1) { i2c_start(); status=i2c_write(0xd0); } // Gửi đi địa chỉ của thanh gi cần giao tiếp // Gửi đi dữ liệu cần viết // Tạo xung stop // Chỗ này không cần quan tâm lắm } BYTE read_DS1307(byte address) { BYTE data; i2c_start(); i2c_write(0xd0); i2c_write(address); i2c_start(); i2c_write(0xd1); data=i2c_read(0); i2c_stop(); return(data); } //Hàm đọc về dữ liệu từ DS1307, tương tự nha -Bây giờ các bạn đã hiểu về I2C và DS1307 vậy việc còn lại để giao tiếp với nó là đọc và gi dữ liệu trên các thanh gi của nó mà thôi. -Giả sử ta muốn cài đặt giờ mới cho DS1307 ta sẽ làm như sau. +Trước tiên có một biến là hour ta chỉnh biến đấy theo đúng giờ mà ta muốn. +Sau đó là gi vào thanh gi hour của Ds1307,nhưng trước đó phải đổi sang số BCD. VD: muốn gi giờ vào DS1307 dùng câu lệnh write_DS1307(2,(hour/10)*16+hour%10); -Nếu muốn đọc thời gian từ DS1307 ra ta làm như sau. +Tạo các biến tương ứng rồi đọc giá trị DS1307 ra và gán cho nó,khi hiển thị nếu hiển thị dưới dạng số BCD thì hiển thị luôn, nếu hiển thị cơ số 10 thì phải chuyển đổi về dạng cơ số 10. VD: đọc giờ từ DS1307 ra. Hour=read_DS1307(2); nếu chuyển đổi sang cơ số 10 thêm câu lệnh hour=(hour/16)*10+hour%16; Sau đó các bạn sẽ hoàn thành một ứng dụng đọc DS1307 và hiển thị thời gian. Bài tập: Bài 1) Thiết kế đồng hồ vạn niên với hiển thị giờ phút giây, thứ ngày tháng năm, nhiệt độ. Bài 11:MỞ RỘNG-KHUẾCH ĐẠI 1.Mở rộng cổng 573.(song song) -Sơ đồ chân. -Chức năng các chân. +D0->D7 chân dữ lệu vào. +Q0->Q7 chân dữ lệu ra. +OE(1) chân cho phép xuất dữ liệu (tích cực mức 0) +LE(11) chân cho phép vào dữ liệu (tích cực mức 1) + Chân 10,20 là nguồn và mát. -Ta sẽ làm một ví dụ mô phỏng với 573. - sơ đồ mô phỏng. -Trên sơ đồ mô phỏng gồm 4 con 573 được nối với cổng D của PIC,các chân cho phép xuất dữ liệu của 573 được nối mát, tức là luôn cho phép 573 xuất dữ liệu, Các chân cho phép vào dữ liệu được nối lần lượt với các chân từ C0->C3. -Sau đây ta sẽ viết một chương trình xuất dữ liệu ra 573. #include <16F887.h> #FUSES NOWDT,NOBROWNOUT,NOLVP #use delay(crystal=20000000) #define in573_1 output_high(PIN_C0);output_low(PIN_C0); #define in573_2 output_high(PIN_C1);output_low(PIN_C1); #define in573_3 output_high(PIN_C2);output_low(PIN_C2); #define in573_4 output_high(PIN_C3);output_low(PIN_C3); void Out_573(int32 data){ output_D(data); in573_1; output_D(data>>8); in573_2; output_D(data>>16); in573_3; output_D(data>>24); in573_4; } void main() { output_C(0x00); while(TRUE) { Out_573(0xff000000); } } 2.Mở rộng cổng 595(nối tiếp) -Sơ đồ chân. -Là ic ghi dịch 8bit kết hợp chốt dữ liệu , đầu vào nối tiếp đầu ra song song . Chức năng: Thường dùng trong các mạch quét led 7 , led matrix …để tiết kiệm số chân VDK tối đa (3 chân) . Có thể mở rộng số chân vi điều khiển bao nhiêu tùy thích mà k ic nào có thể làm dc bằng việc mắc nối tiếp đầu vào dữ liệu các ic với nhau . Giải thích ý nghĩa hoạt động của một số chân quan trọng: (input) Chân 14 : đầu vào dữ liệu nối tiếp . Tại 1 thời điểm xung clock chỉ đưa vào được 1 bit (output) QA=>QH : trên các chân (15,1,2,3,4,5,6,7) Xuất dữ liệu khi chân chân 13 tích cực ở mức thấp và có một xung tích cực ở sườn âm tại chân chốt 12 (output-enable) Chân 13 : Chân cho phép tích cực ở mức thấp (0) .Khi ở mức cao, tất cả các đầu ra của 74595 trở về trạng thái cao trở, không có đầu ra nào được cho phép. (SQH) Chân 9: Chân dữ liệu nối tiếp . Nếu dùng nhiều 74595 mắc nối tiếp nhau thì chân này đưa vào đầu vào của con tiếp theo khi đã dịch đủ 8bit. (Shift clock) Chân 11: Chân vào xung clock . Khi có 1 xung clock tích cực ở sườn dương(từ 0 lên 1) thì 1bit được dịch vào ic. (Latch clock) Chân 12 : xung clock chốt dữ liệu . Khi có 1 xung clock tích cực ở sườn dương thì cho phép xuất dữ liệu trên các chân output . lưu ý có thể xuất dữ liệu bất kỳ lúc nào bạn muốn ,ví dụ đầu vào chân 14 dc 2 bit khi có xung clock ở chân 12 thì dữ liệu sẽ ra ở chân Qa và Qb (chú ý chiều dịch dữ liệu từ Qa=>Qh) (Reset) Chân 10: khi chân này ở mức thấp(mức 0) thì dữ liệu sẽ bị xóa trên chip) -Sau đây để hiểu rõ hơn về IC ta làm một ví dụ. -Sơ đồ nguyên lý: -Code lập trình. #include <16F887.h> #FUSES NOWDT,NOBROWNOUT,NOLVP #use delay(crystal=20000000) #define DS(x) output_bit(pin_D0,x); #define CKSH output_bit(pin_D1,1);output_bit(pin_D1,0); #define CKCT output_bit(pin_D2,1);output_bit(pin_D2,0); int8 i; void Out_595(int32 RXout){ for(i=1;i<=32;i++){ DS(shift_left(&RXout,4,1)); CKSH;} CKCT; } void main() { while(TRUE) { Out_595(0xff00ff00); delay_ms(500); Out_595(0x00ff00ff); delay_ms(500); } } Bài tập: -Phần này các bạn tự làm ứng dụng nha, với led đơn hoặc led 7 thanh.Phần quét led matrix nếu có thời gian mình sẽ hướng dẫn sau, vì phần này không ứng dụng nhiều trong học tập, chỉ để giải trí. Chương III: VẼ MẠCH TRÊN PHẦN MỀM altium_designer -Tất cả được đính kèm theo tài liệu trong đường link sau. http://www.mediafire.com/download/6l66z80w7th8s6z/altium_designer.pdf -Và được hướng dẫn trên lớp. -Các bạn vẽ một số mạch cơ bản cho thành thạo rồi chọn một trong các dự án đã học của những bài trước để vẽ mạch in và thực hiện mạch in.