Giáo trình CCS C for PIC16F877A

pdf 251 trang phuongnguyen 6590
Bạn đang xem 20 trang mẫu của tài liệu "Giáo trình CCS C for PIC16F877A", để tải tài liệu gốc về máy bạn click vào nút DOWNLOAD ở trên

Tài liệu đính kèm:

  • pdfgiao_trinh_ccs_c_for_pic16f877a.pdf

Nội dung text: Giáo trình CCS C for PIC16F877A

  1. CCS C for PIC16F877A 24/06/2008 CCS C for PIC16F877A Thang8831 - 1 -
  2. CCS C for PIC16F877A 24/06/2008 Mục lục I. Tổng quan về CCS 10 1.1. Vì sao ta sử dung CCS ? 1.2. Giới thiệu về CCS ? 1.3. Một số ví dụ cho lập trình CCS .10 II.Chúng ta cùng nhau tìm hiểu lần lượt các phần sau .11 1. I/O_Delay 1.2. Input_output 11 1.3. Nháy LED PortB7 14 1.4. Nháy Led nhiều chế độ 15 1.5. Điều khiển led sáng dồn .18 1.6. I/O + Delay _ Delay 1s RB0 18 1.7. Nháy Led RB0 19 1.8. Delay 1s portB .21 1.9. Delay_Timer0 22 2. ADC 25 A. Sơ đồ: B.Code B.1. ADC reading voltage 25 B.2. LM335_LCD 26 B.3. LM335_F877A_LCD1602 29 B.4. ADC_186 33 3. DAC 36 3.1. DAC_1446 36 4. Timer .37 4.1. Timer0 38 4.2. Timer1 39 4.3. Timer2 39 4.4. frequencymeter 40 5. INTERRUPT 43 5.1. Ngắt Timer0 44 5.2. Ngắt ngoài .48 5.3. Ngắt ngoài trên RB4-RB7 51 5.4. Giải mã bàn phím 56 5.5. Chương trình gửi ký tự ra 2x16 LCD dùng CCS C 59 5.7. Ví dụ nhỏ về ngắt ngoài 61 5.8. Ngắt ngoài và đèn 7 đoạn .62 5.9. Chương trình hiển thị phím số ra đèn 7 đoạn (không dùng interrupt) 63 5.10. Chương trình hiển thị phím số ra đèn 7 đoạn (DÙNG INTERRUPT) .64 5.11. Thay đổi tốc độ đèn led dung ngắt .65 6. Chương trình ví dụ sau mô tả cách dùng PWM do CCS cung cấp .72 7. Tìm hiểu về LCD 76 7.1. 8bit interface 77 7.2. 4bit interface 78 7.3. LCD_lib_4bit Thang8831 - 2 -
  3. CCS C for PIC16F877A 24/06/2008 7.4. LCD lib 8bits 80 7.5. Hiển thị LCD 8bit interface 81 7.6. Hiển thị LCD 4bit interface .86 7.7. LCD_8bit interface, có kiểm tra cờ bận. 86 7.8. LCD and Keypad drive .89 7.9.LM335_F877A_LCD1602 106 7.10. LM35_F877A_LCD1602 107 7.11. LM335_F877A_LCD1602 .110 7.12. lcd_bargraph 113 7.13. Chương trình gửi ký tự ra 2x16 LCD dùng CCS C 113 8. LED ma trận 118 8.1. font_ascii 8.2. font_ascii2 120 8.3. led matrix_Ngat ngoai_COM 122 8.4. led matrix ket noi RS232 128 8.5. led matrix (595 va 154) ket noi rs232 132 8.6. led matrix ver 1.2 136 8.7. 16f877a_8x16_2mau 141 9. Động cơ 148 9.1. DC Motor 9.1.1. code 9.1.2. Position_Control .151 9.1.3. check_encoder .175 9.2. DK Step Motor 177 9.2.1. Code 9.2.2. Step_motor_F877A 9.2.3. Chương trình điều khiển động cơ bước 181 9.2.4. Điều khiển động cơ bước .183 10. Capture .187 10.1. Code cho CCS 10.2. Sử dụng capture newcode 188 10.3.Capture_LCD_5MH .190 10.4. Sử dụng capture_LCD .193 10.5. Sử dụng capture 195 11. SPI 196 12. Các chuẩn giao tiếp . .197 12.1. Chuẩn giao tiếp I2C 12.1.1. Master_Slave .204 12.1.1.1. I2Cmaster 204 12.1.1.2. I2Cslave 205 12.1.2. lcd1_lib 12.1.3. lcd2_lib 208 12.2. Giao tiếp RS232 210 Serial Port - lập trình giao tiếp nối tiếp 210 12.2.1. Giao tiep COM_LCD 222 12.2.2. USART-RS232 224 Thang8831 - 3 -
  4. CCS C for PIC16F877A 24/06/2008 12.2.3. RS232TUT.H 225 12.2.4. RS232TUT 225 12.2.5. RS232TUTDlg 227 12.2.6. RS232TUTDlg.CPP 228 12.2.7. StdAfx.H .235 12.2.8. mscomm.H . 12.2.9. mscomm.CPP .237 12.2.10. Giao tiep pc va pic6f877 qua cong rs232 244 13. Ghi đọc RAM ngoài . 246 13.1. Sơ đồ . 13.2. Code 246 Project 1: Kết nối PIC 16F877A với EEPROM 25AA640 .248 Thang8831 - 4 -
  5. CCS C for PIC16F877A 24/06/2008 Mở Đầu Để lập trình cho PIC, mọi người có thể chọn cho mình những ngôn ngữ lập trình khác nhau như ASM, CCS C, HT-PIC, pascal, basic, Với nhh, đầu tiên tìm hiểu và viết chương trình cơ bản bằng ASM để hiểu rõ cấu trúc sau đó thì viết bằng CCS C cũng viết lại những cái cơ bản và đi dần lên, tốc độ lúc này nhanh hơn khi viết bằng ASM rất nhiều. Khi viết bằng CCS C thông thường thì dịch ra file.hex có dài hơn so với khi viết bằng ASM. Hai ngôn ngữ CCS C và HT-PIC được ưa chuộng hơn cả, CCS C dễ học,gần gũi với ASM còn HT-PIC là dạng ANSI C. Để lập trình và biên dịch CCS C, dùng chương trình PIC C Complier,sau khi soạn thảo các bạn ấn F9, để dịch,nếu thành công sẽ có thông báo như sau: Ngoài ra, để xem code ASM như thế nào,sau khi dịch bạn chọn mục C/ASM List như hình dưới đây: Thang8831 - 5 -
  6. CCS C for PIC16F877A 24/06/2008 Link download trình biên dịch CCS C ở đây: CCSC_3.249.rar Phiên bản mới hơn tải tại: www.kho.tailieuvietnam.net, vào Download Home > Điện tử tàn thư > Vi điều khiển - Vi xử lý – PLC Sử dụng CCS cho việc lập trình PIC là rất hay và tiện lợi.Truoc day ban noisepic có đề cập cách lập trình cho ccs khá hay. Ta sẽ khai báo thêm một file định nghĩa các thanh ghi của pic kiểu như : // register definitions #define W 0 #define F 1 // register files #byte INDF =0x00 #byte TMR0 =0x01 #byte PCL =0x02 #byte STATUS =0x03 #byte FSR =0x04 #byte PORTA =0x05 #byte PORTB =0x06 #byte PORTC =0x07 #byte PORTD =0x08 #byte PORTE =0x09 #byte EEDATA =0x10C #byte EEADR =0x10D #byte EEDATH =0x10E #byte EEADRH =0x10F #byte ADCON0 =0x1F Thang8831 - 6 -
  7. CCS C for PIC16F877A 24/06/2008 #byte ADCON1 =0x9F #byte ADRESH =0x9F #byte ADSESL =0x9F #byte PCLATH =0x0a #byte INTCON =0x0b #byte PIR1 =0x0c #byte PIR2 =0x0d #byte PIE1 =0x8c #byte PIE2 =0x8d #byte OPTION_REG =0x81 #byte TRISA =0x85 #byte TRISB =0x86 #byte TRISC =0x87 #byte TRISD =0x88 #byte TRISE =0x89 #byte EECON1 =0x18C #byte EECON2 =0x18D //DINH NGHIA BIT #bit RA5 =0x05.5 #bit RA4 =0x05.4 #bit RA3 =0x05.3 #bit RA2 =0x05.2 #bit RA1 =0x05.1 #bit RA0 =0x05.0 #bit RB7 =0x06.7 #bit RB6 =0x06.6 #bit RB5 =0x06.5 #bit RB4 =0x06.4 #bit RB3 =0x06.3 #bit RB2 =0x06.2 #bit RB1 =0x06.1 #bit RB0 =0x06.0 #bit RC7 =0x07.7 #bit RC6 =0x07.6 #bit RC5 =0x07.5 #bit RC4 =0x07.4 #bit RC3 =0x07.3 #bit RC2 =0x07.2 #bit RC1 =0x07.1 #bit RC0 =0x07.0 #bit RD7 =0x08.7 #bit RD6 =0x08.6 #bit RD5 =0x08.5 Thang8831 - 7 -
  8. CCS C for PIC16F877A 24/06/2008 #bit RD4 =0x08.4 #bit RD3 =0x08.3 #bit RD2 =0x08.2 #bit RD1 =0x08.1 #bit RD0 =0x08.0 #bit RE2 =0x09.2 #bit RE1 =0x09.1 #bit RE0 =0x09.0 #bit TRISA5 =0x85.5 #bit TRISA4 =0x85.4 #bit TRISA3 =0x85.3 #bit TRISA2 =0x85.2 #bit TRISA1 =0x85.1 #bit TRISA0 =0x85.0 #bit TRISB7 =0x86.7 #bit TRISB6 =0x86.6 #bit TRISB5 =0x86.5 #bit TRISB4 =0x86.4 #bit TRISB3 =0x86.3 #bit TRISB2 =0x86.2 #bit TRISB1 =0x86.1 #bit TRISB0 =0x86.0 #bit TRISC7 =0x87.7 #bit TRISC6 =0x87.6 #bit TRISC5 =0x87.5 #bit TRISC4 =0x87.4 #bit TRISC3 =0x87.3 #bit TRISC2 =0x87.2 #bit TRISC1 =0x87.1 #bit TRISC0 =0x87.0 #bit TRISD7 =0x88.7 #bit TRISD6 =0x88.6 #bit TRISD5 =0x88.5 #bit TRISD4 =0x88.4 #bit TRISD3 =0x88.3 #bit TRISD2 =0x88.2 #bit TRISD1 =0x88.1 #bit TRISD0 =0x88.0 #bit TRISE2 =0x89.2 #bit TRISE1 =0x89.1 #bit TRISE0 =0x89.0 // INTCON Bits for C #bit gie = 0x0b.7 Thang8831 - 8 -
  9. CCS C for PIC16F877A 24/06/2008 #bit peie = 0x0b.6 #bit tmr0ie = 0x0b.5 #bit int0ie = 0x0b.4 #bit rbie = 0x0b.3 #bit tmr0if = 0x0b.2 #bit int0if = 0x0b.1 #bit rbif = 0x0b.0 // PIR1 for C #bit pspif = 0x0c.7 #bit adif = 0x0c.6 #bit rcif = 0x0c.5 #bit txif = 0x0c.4 #bit sspif = 0x0c.3 #bit ccp1if = 0x0c.2 #bit tmr2if = 0x0c.1 #bit tmr1if = 0x0c.0 //PIR2 for C #bit cmif = 0x0d.6 #bit eeif = 0x0d.4 #bit bclif = 0x0d.3 #bit ccp2if = 0x0d.0 // PIE1 for C #bit adie = 0x8c.6 #bit rcie = 0x8c.5 #bit txie = 0x8c.4 #bit sspie = 0x8c.3 #bit ccp1ie = 0x8c.2 #bit tmr2ie = 0x8c.1 #bit tmr1ie = 0x8c.0 //PIE2 for C #bit osfie = 0x8d.7 #bit cmie = 0x8d.6 #bit eeie = 0x8d.4 // OPTION Bits #bit not_rbpu = 0x81.7 #bit intedg = 0x81.6 #bit t0cs = 0x81.5 #bit t0se = 0x81.4 #bit psa = 0x81.3 #bit ps2 = 0x81.2 #bit ps1 = 0x81.1 #bit ps0 = 0x81.0 // EECON1 Bits #bit eepgd = 0x18c.7 Thang8831 - 9 -
  10. CCS C for PIC16F877A 24/06/2008 #bit free = 0x18C.4 #bit wrerr = 0x18C.3 #bit wren = 0x18C.2 #bit wr = 0x18C.1 #bit rd = 0x18C.0 Sau đó ta có thể sử dụng lệnh gán PortB = 0x00 để xuất sẽ tiện hơn nhiều. Mình lập trình cho CCS đều dùng kiểu này. Khi đó ta sẽ vừa tận dụng được các hàm có sẵn của CCS vừa thao tác trực tiếp các thanh ghi như bên ASM. I. Tổng quan về CCS 1.1. Vì sao ta sử dung CCS ? Sự ra đời của một loại vi điều khiển đi kèm với việc phát triển phần mềm ứng dụng cho việc lập trình cho con vi điều khiển đó. Vi điều khiển chỉ hiểu và làm việc với hai con số 0 và 1. Ban đầu để việc lập trình cho VĐK là làm việc với dãy các con số 0 và 1. Sau này khi kiến trúc của Vi điều khiển ngày càng phức tạp, số luợng thanh ghi lệnh nhiều lên, việc lập trình với dãy các số 0 và 1 không còn phù hợp nữa, đòi hỏi ra đời một ngôn ngữ mới thay thế. Và ngôn ngữ lập trình Assembly. Ở đây ta không nói nhiều đến Assmebly. Sau này khi ngôn ngữ C ra đời, nhu cầu dùng ngôn ngữ C đề thay cho ASM trong việc mô tả các lệnh lập trình cho Vi điều khiển một cách ngắn gọn và dễ hiểu hơn đã dẫn đến sự ra đời của nhiều chương trình soạn thảo và biên dịch C cho Vi điều khiển : Keil C, HT-PIC, MikroC, CCS Tôi chọn CCS cho bài giới thiệu này vì CCS là một công cụ lập trình C mạnh cho Vi điều khiển PIC. Những ưu và nhược điểm của CCS sẽ được đề cập đến trong các phần dưới đây. 1.2. Giới thiệu về CCS ? CCS là trình biên dịch lập trình ngôn ngữ C cho Vi điều khiển PIC của hãng Microchip. Chương trình là sự tích hợp của 3 trình biên dich riêng biết cho 3 dòng PIC khác nhau đó là: - PCB cho dòng PIC 12-bit opcodes - PCM cho dòng PIC 14-bit opcodes - PCH cho dòng PIC 16 và 18-bit Tất cả 3 trình biên dich này đuợc tích hợp lại vào trong một chương trình bao gồm cả trình soạn thảo và biên dịch là CCS, phiên bản mới nhất là PCWH Compiler Ver 3.227 Giống như nhiều trình biên dich C khác cho PIC, CCS giúp cho người sử dụng nắm bắt nhanh được vi điều khiển PIC và sử dụng PIC trong các dự án. Các chương trình diều khiển sẽ được thực hiện nhanh chóng và đạt hiệu quả cao thông qua việc sử dụng ngôn ngữ lạp trình cấp cao – Ngôn ngữ C Tài liệu hướng dẫn sử dụng có rất nhiều, nhưng chi tiết nhất chính là bản Help đi kèm theo phần mềm (tài liệu Tiếng Anh). Trong bản trợ giúp nhà sản xuất đã mô tả rất nhiều về hằng, biến, chỉ thị tiền xủa lý, cấu trúc các câu lệnh trong chương trình, các hàm tạo sẵn cho người sử dụng Ngoài ra về Tiếng Việt cũng có bản dịch của tác giả Trần Xuân Trường, SV K2001 DH BK HCM. Tài liệu này dịch trên cơ sở bản Help của CCS, tuy rằng chưa đầy đủ nhưng đây là một tài liệu hay, nếu bạn tìm hiểu về PIC và CCS thì nên tìm tài liệu này về đọc. Địa chỉ Download tài liệu: www.picvietnam.com -> Mục nói về CCS. 1.3. Một số ví dụ cho lập trình CCS Với mục tiêu giúp người đọc nhanh chóng lắm bắt được cách lập trình C cho PIC thông qua chương trình dịch CCS. Dưới đây tôi giới thiệu một vài bài lập trình đơn giản cho PIC, các bài mẫu này dựa theo tài liệu tutorial của Nigel như quét LED, LED 7 thanh, LCD, bàn phím , cách dùng các giao tiếp của PIC để giao tiếp với thiết bị ngoại vi như Real Time IC, ADC, EEPROM · Yêu cầu về phần cứng tối thiểu cần có để thực hành: - PIC16F877A ( hoặc 16F876A hay 16F88) = 50K (Tốt nhất là PIC16F877A) - 1 Board cắm linh kiện (tối thiểu ) = 40K Thang8831 - 10 -
  11. CCS C for PIC16F877A 24/06/2008 - Thạch anh 20MHz, tụ 22pF, 10uF, trở 10K, 4K7, 330Ω, nút bấm = 10K - 10 LED đơn xanh hay đỏ, 4 LED 7 thanh (loại 4 LED liền một đế ) = 15K - MAX232 để giao tiếp máy tính () = 10K Tổng cộng là: 125K · Phần cứng mở rộng - LCD 1602A loại 2 dòng 16 ký tự (Nếu có LCD 2002 càng tốt) = 65K (Minh Hà có bán) - Real Time IC DS1307 hay DS1337 = 25K (có thể xin sample của Maxim-IC) - EEPROM AT24Cxx - ADC/DAC IC loại 12-bit trở nên (ADC 10-bit thì PIC cũng có) - Sensor nhiêt LM335 hay LM35 = 13K - Động cơ bước, động cơ một chiều Mục đích chính của tôi trong việc giới thiệu các ví dụ dưới đây là nhằm giúp mọi người nhanh chóng nắm được kỹ thuật lập trình bằng CCS, thông qua các ví dụ mọi người sẽ hiểu các hàm của CCS, cách sử dụng trong từng ứng dụng cụ thể. Về chi tiết của mỗi hàm tôi sẽ không trình bày kỹ tại đây, để biết rõ ta có thể xem trong phần Trợ giúp của CCS hay tài liệu của tác giả Trần Xuân Trường, trong đó đã nói khá đầy đủ. Tôi nhấn mạnh một điều khi mọi người tìm hiểu về PIC và CCS đó là hãy tự mình tìm hiểu là chính, từ việc nghiên cứu tài liệu, tìm tài liệu cho đền thiết kế mạch và viết chương trình. Những gì tại đây chỉ là cơ bản, còn việc phát triển, sử dụng hết điểm mạnh của PIC và CCS là ở phía mọi người. Chúc thành công! Một điều chú ý là tất cả các mạch điện và code tôi trình bày dưới đây tôi đều đã lắp mạch thật trên bo cắm và chạy tốt. Các bác ơi cho em hỏi, vậy em muốn nhúng một đoạn ASM vào trong 1 function của CCS thì em phải nhúng như thế nào ạ? Dùng các directive #ASM và #ENDASM để bọc đoạn code đó. Đọc thêm hướng dẫn về hai directive này trong tài liệu hướng dẫn của CCS, ở đó đã có ví dụ. Em thật sự không hiểu câu này:" (nếu dùng hai thì chèn dấu "|"ở giữa) " anh NHH có thể minh họa cho em được không? em mới tìm hiểu về Pic được một tuần, nhưng chắc chắn là anh chỉ rõ hơn thì em sẽ hiểu! cảm ơn anh và chúc anh vui! Chọn ví dụ như vầy nè : Ví dụ chọn Timer0, chia prescaler 1:2 Code: setup_timer_0(CC_INTERNAL|RTCC_DIV_2); II.Chúng ta cùng nhau tìm hiểu lần lượt các phần sau: 1.I/O + Delay 2.Timer và ngắt Timer 3.Ngắt ngoài 4.ADC, PWM, (tập trung mổ xẻ nhiều) 5 Tạm thời cứ như vậy đã, sau này sẽ tính tiếp! 1. I/O_Delay 1.3. Input_output //=== === // Ten chuong trinh : Thuc hien vao ra // Nguoi thuc hien : linhnc308 // Ngay thuc hien : 1/09/2006 // Phien ban : 1.0 Thang8831 - 11 -
  12. CCS C for PIC16F877A 24/06/2008 // Mo ta phan cung : Dung PIC16F877A - thach anh 20MHz //=== === #include #include #device *=16 ADC=10 #FUSES NOWDT, HS, NOPUT, NOPROTECT, NODEBUG, NOBROWNOUT, NOLVP, NOCPD, NOWRT #use delay(clock=20000000) #CASE // Dinh nghia ten cac cong ra #define Relay1 RD0 #define Relay2 RD1 #define Relay3 RD2 #define Relay4 RD3 #define Relay5 RD4 #define Relay6 RD5 #define Relay7 RD6 #define Relay8 RD7 #define Relay9 RC4 #define Relay10 RC5 #define Relay11 RC6 #define Relay12 RC7 #define In1 RA0 #define In2 RA1 #define In3 RA2 #define In4 RA3 #define AllRelay1 PORTD // PIN D0 : D7 #define AllRelay2 PORTC // PIN C4 : C7 #define Step PORTB #Define AllInput PORTA #define OFF 0 #define ON 1 #define OutEnable1 TRISD // Relay Output #define OutEnable2 TRISC // Relay Output #define InEnable TRISA // Input #define StepEnable TRISB // Step Motor #define PWM_Enable TRISC2 // PWM, PIN_C2 void main() { int16 DutyCycle; delay_ms(250); // Khoi tao che do vao ra OutEnable1 = 0x00; OutEnable2 = 0x0F; InEnable = 0x0F; Thang8831 - 12 -
  13. CCS C for PIC16F877A 24/06/2008 StepEnable = 0; PWM_Enable = 1; // Khong cho phep xuat PWM //=== // Khoi tao cho bo PWM setup_ccp1(CCP_PWM); // CCP1_PINC2 as a PWM // CycleTime = (1/clock)*4*t2div*(period+1) // Clock=20000000 and period=127 (below) // Tinh toan tan so PWM phat ra: // (1/10000000)*4*1*128 = 51.2 us or 19.5 khz // (1/20000000)*4*2*128 = 51.2 us or 19.5 khz // (1/10000000)*4*4*128 = 204.8 us or 4.9 khz // (1/10000000)*4*16*128= 819.2 us or 1.2 khz //setup_timer_2(T2_DIV_BY_1, 31, 1); // 78.12KHz //setup_timer_2(T2_DIV_BY_1, 255, 1); // 19.53KHz duty = 0 1023 setup_timer_2(T2_DIV_BY_4, 255, 1); // 4.5KHz //setup_timer_2(T2_DIV_BY_16, 127, 1); // 1.2KHz //set_pwm1_duty(value); // This sets the time the pulse is //=== // Test Mode OutEnable2 = 0x0F; delay_ms(10); Step = 0x00; // Motor Stop AllRelay1 = 0x00; AllRelay2 = 0x00; // Mo toan bo cac Role DutyCycle = 1023; set_pwm1_duty(DutyCycle); // Chay bo PWM PWM_Enable = 1; while (TRUE) { AllRelay1 = 0x00; Relay12 = ON; // Mo toan bo cac Role delay_ms(1000); AllRelay1 = 0xFF; Relay12 = OFF; // Dong toan bo cac Role delay_ms(750); if (DutyCycle == 1024) DutyCycle = 1024; if (In1 == 0) { DutyCycle += 64; set_pwm1_duty(DutyCycle); // Chay bo PWM } if (DutyCycle == 0) DutyCycle = 0; if (In2 == 0) { DutyCycle -= 64; set_pwm1_duty(DutyCycle); // Chay bo PWM } delay_ms(250); Thang8831 - 13 -
  14. CCS C for PIC16F877A 24/06/2008 } } /* #use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=9) #use i2c(Master,Fast,sda=PIN_C4,scl=PIN_C3) #int_xxx // Khai bao chuong trinh ngat xxx_isr() { // Code here } void Ten_chuong_trinh_con(Bien) { // Code here } */ 1.3. Nháy LED PortB7 CODE: #include #include #device *=16 ADC=8 #FUSES NOWDT, HS, NOPUT, NOPROTECT, NODEBUG, NOBROWNOUT, NOLVP, NOCPD, NOWRT #use delay(clock=20000000) //#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=9) //#use i2c(Master,Fast,sda=PIN_C4,scl=PIN_C3) //#int_xxx // Khai bao chuong trinh ngat //xxx_isr() //{ // Code here Thang8831 - 14 -
  15. CCS C for PIC16F877A 24/06/2008 //} main() { //thiet lap che do cho portb trisb=0x00; portb=0xff; while (true) { portb=0; delay_ms(500); portb=0x80; delay_ms(500); } } 1.4. Nháy Led nhiều chế độ #include #include #FUSES NOWDT, HS, NOPUT, NOPROTECT, NODEBUG, NOBROWNOUT, NOLVP, NOCPD, NOWRT #use delay(clock=20000000) int8 mode,i; byte temp; #INT_EXT EXT_ISR() { mode++; if (mode==9) mode = 0; } // End of INT void program1(); void program2(); void program3(); void program4(); void program5(); void program6(); void program7(); void program8(); void main() { trisd = 0x00; trisb = 0xFF; portd=0xff; enable_interrupts(int_EXT); ext_int_edge(L_TO_H); enable_interrupts(GLOBAL); mode = 0; Thang8831 - 15 -
  16. CCS C for PIC16F877A 24/06/2008 while (1) { switch(mode) { case 1: program1(); break; case 2: program2(); break; case 3: program3(); break; case 4: program4(); break; case 5: program5(); break; case 6: program6(); break; case 7: program7(); break; case 8: program8(); break; } } } void program1() { PortD = 0x00; delay_ms(250); Portd = 0xFF; delay_ms(250); } void program2() { temp = 0xFF; for (i=0;i >= 1; } } void program3() { temp = 0xFF; for (i=0;i<=8;i++) { portd = temp; delay_ms(250); temp <<= 1; } } void program4() { portd = 0xAA; delay_ms(500); portd = 0x55; delay_ms(500); } void program5() { Portd = 0x7E; delay_ms(150); Portd = 0xBD; delay_ms(250); Portd = 0xDB; delay_ms(150); Portd = 0xE7; Thang8831 - 16 -
  17. CCS C for PIC16F877A 24/06/2008 delay_ms(150); Portd = 0xDB; delay_ms(150); Portd = 0xBD; delay_ms(150); Portd = 0x7E; delay_ms(150); } void program6() { temp = 0xFF; for (i=0;i > 1; } } void program7() { Portd = 0xFE; delay_ms(150); Portd = 0xFD; delay_ms(150); Portd = 0xFB; delay_ms(150); Portd = 0xF7; delay_ms(150); Portd = 0xEF; delay_ms(150); PortD = 0xDF; delay_ms(150); Portd = 0xBF; delay_ms(150); Portd = 0x7F; delay_ms(150); } void program8() { Portd = 0x7F; delay_ms(150); Portd = 0xBF; delay_ms(150); PortD = 0xDF; delay_ms(150); Portd = 0xEF; delay_ms(150); Portd = 0xF7; delay_ms(150); Portd = 0xFB; delay_ms(150); Portd = 0xFD; delay_ms(150); Thang8831 - 17 -
  18. CCS C for PIC16F877A 24/06/2008 Portd = 0xFE; delay_ms(150); } 1.5. Điều khiển led sáng dồn //Chuong trinh led sang don o PORTB /*ket qua o PORTB là:00000001,00000010,00000100,00001000,00010000,00100000,01000000,10000000, 10000001,10000010,10000100,10001000, cuoi cung thi PORTB=0xFF */ #include #include #fuses NOWDT,PUT,HS,NOPROTECT,NOLVP #use delay(clock=20000000) #use fast_io(b) int8 sck,bienxoay; main() { trisb=0; while(true){ sck=8; portb=0; delay_ms(100); bienxoay=1; while(sck>0) { portb=bienxoay; bienxoay=bienxoay #fuses NOWDT,PUT,XT,NOPROTECT #use delay(clock=4000000) #byte PORTB = 0x06 int16 count; int8 a; //Chuong trinh ngat TMR0 #int_timer0 void interrupt_timer0() { set_timer0(6); ++count; if(count == 2000) // 2000*500us = 500000us = 1s { count=0; rotate_left(&a,1); } } //Chuong trinh chinh void main(void) Thang8831 - 18 -
  19. CCS C for PIC16F877A 24/06/2008 { set_tris_b(0); enable_interrupts(int_timer0); setup_timer_0(RTCC_INTERNAL|RTCC_DIV_2); enable_interrupts(global); set_timer0(6);// T_dinhthi = 2*(256 - 6)*1us = 500us a = 0x01; while(true) { PORTB = a; } } 1.7. Nháy Led RB0 Chương trình này làm nhấp nháy con led ở chân RB0 1s sáng, 1s tắt. Code: #include #fuses NOWDT,PUT,HS,NOPROTECT #use delay(clock=10000000) main() { while(true) { output_high(PIN_B0); delay_ms(1000); output_low(PIN_B0); delay_ms(1000); } } Trên đây: Code: #include Thang8831 - 19 -
  20. CCS C for PIC16F877A 24/06/2008 Khai báo con PIC bạn sử dụng, file này chương trình viết sẵn nhằm khai báo các bit, thanh ghi quan trọng trong con pic này.Các bạn có thể vào thư mục cài đặt C:\Program Files\PICC\Devices\16F877A.h để xem nó khai báo được những gì trong đó! Code: #fuses NOWDT,PUT,HS,NOPROTECT Thiết lập các config Code: #use delay(clock=10000000) Khai báo tần số dao động cấp cho PIC Code: output_high(PIN_B0) Xuất ra chân RB0 mức logic 1 (tức 5V), do khi thực hiện hàm này đã bao hàm luôn việc tác động lên thanh ghi TRISB (dùng chọn chân I/O) do vậy ta không cần viết lệnh chọn chân I/O nữa. Code: output_low(PIN_B0) Ngược lại Code: delay_ms(1000) Tạo trễ khoảng thời gian theo mili giây là 1000 (tức 1s) *Chú ý hàm này chỉ có tác dụng khi có khai báo tần số dao động cấp cho PIC Và bây giờ thử làm cho tất cả 8 led nối với portB chớp tắt 1s xem nào!Phải chăng ta sẽ làm như sau (Viết trong vòng lặp while): Code: { output_high(PIN_B0); output_high(PIN_B1); output_high(PIN_B2); output_high(PIN_B3); output_high(PIN_B4); output_high(PIN_B5); output_high(PIN_B6); output_high(PIN_B7); delay_ms(1000); output_low(PIN_B0); output_low(PIN_B1); output_low(PIN_B2); output_low(PIN_B3); output_low(PIN_B4); output_low(PIN_B5); output_low(PIN_B6); output_low(PIN_B7); delay_ms(1000); } Viết như thế này thì quá dài và thiếu chính xác nữa, có cách nào khác hay hơn không ? Sao ta không xuất đẩy hẳn portB lên mức cao,tạo trễ 1s rồi ép cho nó xuống mức thấp,tạo trễ 1s cùng một lúc nhỉ ! Bài tiếp theo sẽ đưa ra câu trả lời Thang8831 - 20 -
  21. CCS C for PIC16F877A 24/06/2008 output_high(pin_xx); output_low (pin_xx); Hai câu lệnh trên chỉ làm cho chân ra xx là cao hay thấp, ứng với mức logic 1 hoặc 0.trong bài trên ta muốn cho sáng tất một port thì chỉ càn câu lệnh : Code: void main (void ) { set_tris_b(0); // cả port B là port ra set_tris_c(0); // cả port C là port ra port_b(0x00); // khởi tạo giá trị đầu port B là 0 ( Tất cả led đều tắt ) port_c(0x00); // khởi tạo giá trị đầu port B là 0 ( Tất cả led đều tắt ) delay_ms(100); while(1) { port_b(0xff); delay_ms(1000); port_c(0xff); delay_ms(1000); } } 1.8. Delay 1s portB Ăc ặc đang post thì bị cúp điện,bực cả mình Và đây là câu trả lời cho việc delay led ở portB 1s Code: #include #fuses NOWDT,PUT,HS,NOPROTECT #use delay(clock=10000000) #use fast_io(b) #byte portb=0x6 main() Thang8831 - 21 -
  22. CCS C for PIC16F877A 24/06/2008 { set_tris_b(0); while(true) { portb=255; //all led on delay_ms(1000); portb=0; //all led off delay_ms(1000); } } Code: #byte portb=0x6 Khai báo địa chỉ portB, không như trong MPLAB đã định nghĩa luôn cái này cho ta, nếu không có dòng này chương trình sẽ báo lỗi chưa định nghĩa portB Code: set_tris_b(0) Tất cả các chân của portB là output, muốn set tất cả các chân là input thì trong ngoặc là 255, Trong HELP hướng dẫn lệnh này như sau: "These functions allow the I/O port direction (TRI-State) registers to be set. This must be used with FAST_IO and when I/O ports are accessed as memory such as when a #BYTE directive is used to access an I/O port. Using the default standard I/O the built in functions set the I/O direction automatically." Rõ ràng khi set byte làm I/O nếu ta thêm khai báo: Code: #use fast_io(b) Dùng khai báo này thì CCS sẽ chỉ thực hiện đúng một lệnh cho việc I/O các chân trên port tương ứng, nếu ko nó phải mất khoảng 3 hay 4 lệnh gì đó. Phần I/O + Delay tạm thời xong như vậy, bác nào có phản hồi thì tiếp tục thảo luận.Xong phần này có thể viết về xuất led 7 đoạn, quét phím, LCD, Bác nào viết thì cứ post lên cho bà con thảo luận nhé ! +Một phương án khác: #include #fuses HS, NOWDT, NOLVP, XT #use delay(clock=4000000) void main() { byte leds = 0xff; set_tris_b(0x00); // configure pins of PORTB as output while (true) { output_b(leds); leds = ~leds; // toggle leds delay_ms(1000); } } 1.9. Delay_Timer0 // // CHUONG TRINH SU DUNG TMR0 //date:23/08/2005 //author:noisepic@gmail.com Thang8831 - 22 -
  23. CCS C for PIC16F877A 24/06/2008 //status: OK! // #include #include #fuses NOWDT,PUT,HS,NOPROTECT,NOLVP #use delay(clock=20000000) void tre_ms(unsigned int time); void main() { TRISB=0; setup_timer_0(); while(1) { PORTB = 0; tre_ms(250); PORTB =0xff; tre_ms(250); } } // void tre_ms(unsigned int time) { int8 i,j; GIE=0; T0CS=0;// Chon internal T0SE=0;// rising edge PSA =0;// Timer mode PS2=0;PS1=1;PS0=1; // 1:8 for(i=0;i<time;i++) { for(j=0;j<5;j++) { TMR0=132; while(tmr0if==0); tmr0if = 0; } } } // cho em hỏi, em đang làm thí nghiệm pic16f84a, làm led chớp tắt theo ý muốn nhưng em muốn dùng 1 biến trở để chỉnh tốc độ delay thì phải làm sao? nhờ các anh chỉ giáo! và em muốn dùng time 0 được không? (xin lỗi mấy anh, mục này mà em hỏi F84a) Người ta thường dùng bộ ADC trong PIC (tất nhiên đang nói đến những chip có module ADC) để đọc giá trị của biến trở rồi điều chỉnh thời gian/tốc độ chớp tắt. Với PIC16F84A (không có module ADC), bạn vẫn có thể làm được điều đó, nhưng dùng một cách khác, được Thang8831 - 23 -
  24. CCS C for PIC16F877A 24/06/2008 đề cập đến trong app. note 863 của Microchip. Bạn tìm đọc app. note đó trên web site của Microchip. +cho em hoi trong ccs co ho tro vong lap " for" ko vay cac bac sao em lam hoai ma ko duoc vay bien dich van dung ma chay thi ko duoc Code: #include #device *=16 ADC=8 #fuses NOWDT,PUT,HS,NOPROTECT #use delay(clock=10000000) #use fast_io(b) #byte p3=0x06 #byte porta=0x05 void main() { int8 const led[]={1,2,4,8,16,32,64,128},a; set_tris_b(0); set_tris_a(0); set_tris_c(0); set_tris_d(0); while(true) { for( a=0;a #device *=16 ADC=8 #fuses NOWDT,PUT,HS,NOPROTECT #use delay(clock=10000000) #use fast_io(b) #byte p3=0x06 void main() { int8 const led[]={1,2,4,8,16,32,64,128},a; set_tris_b(0); while(true) { for(a=0;a<8;a++) { p3=led[a]; delay_ms(20); Thang8831 - 24 -
  25. CCS C for PIC16F877A 24/06/2008 } } } namqn: bạn nên đặt phần code của bạn giữa hai tag [ code] và [ /code] để định dạng cho phần code (hai tag không có khoảng trắng nào hết chứ không phải như được hiển thị ở đây). 2. ADC A. Sơ đồ: B.Code B.1. ADC reading voltage reading Voltage #include #include #device *=16 ADC=10 Thang8831 - 25 -
  26. CCS C for PIC16F877A 24/06/2008 #FUSES NOWDT, HS, NOPUT, NOPROTECT, NODEBUG, BROWNOUT, LVP, NOCPD, NOWRT #use delay(clock=20000000) #use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=9) #include int16 temp,high,low; int8 nghin,tram,chuc,donvi; int1 mili_volt; float volt; int8 const a1[10] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90}; // Khong co cham int8 const a2[10] = {0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10}; // Co cham #INT_EXT void ngatngoai() { } //=== void convert_bcd(int16 x) { nghin = x / 1000 + 0x30; temp = x % 1000; tram = temp / 100 + 0x30; temp = temp % 100; chuc = temp / 10 + 0x30; donvi = temp % 10 + 0x30; } //=== void main() { lcd_init(); printf(lcd_putchar,"CT Do dien ap"); setup_adc_ports(AN0); chs0=0;chs1=0;chs2=0; setup_adc(ADC_CLOCK_INTERNAL); delay_us(10); do { temp = read_adc(); // Dang so thap phan volt = (float)(temp*5)/1023; if (volt < 1) mili_volt=1; else mili_volt = 0; // Dang so nguyen high = (temp*5)/1023; low = (temp*5)%1023; // ===Truyen len may tinh Thang8831 - 26 -
  27. CCS C for PIC16F877A 24/06/2008 printf("\r\nGia tri ADC = %lu",read_adc()); Printf("\r\nGia tri dien ap = %f",volt); //=== volt = volt * 1000; convert_bcd((int16)volt); lcd_putcmd(0xC0); printf(lcd_putchar, "V = "); if (!mili_volt) {lcd_putchar(nghin);lcd_putchar(".");} lcd_putchar(tram); lcd_putchar(chuc); lcd_putchar(donvi); if (mili_volt) printf(lcd_putchar," mV"); else printf(lcd_putchar," V"); printf("\r\n V = %lu",high); if(low #define LCD_RS PIN_D2 //#define LCD_RW PIN_A1 #define LCD_EN PIN_D3 Thang8831 - 27 -
  28. CCS C for PIC16F877A 24/06/2008 #define LCD_D4 PIN_D4 #define LCD_D5 PIN_D5 #define LCD_D6 PIN_D6 #define LCD_D7 PIN_D7 // misc display defines- #define Line_1 0x80 #define Line_2 0xC0 #define Clear_Scr 0x01 // prototype statements #separate void LCD_Init ( void );// ham khoi tao LCD #separate void LCD_SetPosition ( unsigned int cX );//Thiet lap vi tri con tro #separate void LCD_PutChar ( unsigned int cX );// Ham viet1kitu/1chuoi len LCD #separate void LCD_PutCmd ( unsigned int cX) ;// Ham gui lenh len LCD #separate void LCD_PulseEnable ( void );// Xung kich hoat #separate void LCD_SetData ( unsigned int cX );// Dat du lieu len chan Data // D/n Cong #use standard_io ( B ) #use standard_io (A) //khoi tao LCD #separate void LCD_Init ( void ) { LCD_SetData ( 0x00 ); delay_ms(200); /* wait enough time after Vdd rise >> 15ms */ output_low ( LCD_RS );// che do gui lenh LCD_SetData ( 0x03 ); /* init with specific nibbles to start 4-bit mode */ LCD_PulseEnable(); LCD_PulseEnable(); LCD_PulseEnable(); LCD_SetData ( 0x02 ); /* set 4-bit interface */ LCD_PulseEnable(); /* send dual nibbles hereafter, MSN first */ LCD_PutCmd ( 0x2C ); /* function set (all lines, 5x7 characters) */ LCD_PutCmd ( 0b00001100); /* display ON, cursor off, no blink */ LCD_PutCmd ( 0x06 ); /* entry mode set, increment & scroll left */ LCD_PutCmd ( 0x01 ); /* clear display */ } #separate void LCD_SetPosition ( unsigned int cX ) { /* this subroutine works specifically for 4-bit Port A */ LCD_SetData ( swap ( cX ) | 0x08 ); LCD_PulseEnable(); LCD_SetData ( swap ( cX ) ); LCD_PulseEnable(); } #separate void LCD_PutChar ( unsigned int cX ) { /* this subroutine works specifically for 4-bit Port A */ Thang8831 - 28 -
  29. CCS C for PIC16F877A 24/06/2008 output_high ( LCD_RS ); LCD_PutCmd( cX ); output_low ( LCD_RS ); } #separate void LCD_PutCmd ( unsigned int cX ) { /* this subroutine works specifically for 4-bit Port A */ LCD_SetData ( swap ( cX ) ); /* send high nibble */ LCD_PulseEnable(); LCD_SetData ( swap ( cX ) ); /* send low nibble */ LCD_PulseEnable(); } #separate void LCD_PulseEnable ( void ) { output_high ( LCD_EN ); delay_us ( 3 ); // was 10 output_low ( LCD_EN ); delay_ms ( 3 ); // was 5 } #separate void LCD_SetData ( unsigned int cX ) { output_bit ( LCD_D4, cX & 0x01 ); output_bit ( LCD_D5, cX & 0x02 ); output_bit ( LCD_D6, cX & 0x04 ); output_bit ( LCD_D7, cX & 0x08 ); } B.3. LM335_F877A_LCD1602 /* Mach do nhiet do - MCU = PIC16F877A - Sensor = LM335 (co the thay the bang LM35D) - MAX232 giao tiep may tinh - LCD1602A de hien thi gia tri nhiet do Mo ta phan cung: - Mach cho sensor mac nhu trong Datasheet cua LM335 Chan V_out noi qua dien tro 1K voi +5V. Chan nay cung duoc noi voi kenh AN0 cua PIC Chan Adj noi voi dien tro 10K de tinh chinh Chan GND noi dat - Mach VDK gom co LCD va max232 LCD noi voi PORTD cua PIC RS -> RD2, RW -> GND, E -> RD3 D4-D7 -> RD4-RD7 Max232: chan10 -> RC6, chan9 -> RC7 chan8 -> chan3 DB9, chan7 -> chan2 DB9, chan5 DB9 -> GND Kenh AN0 cua PIC noi den chan V_out LM335 Nut bam noi tai chan RB0 -> nhan ngat ngoai Thach anh loai 20MHz, tu 22pF === - Designer: linhnc308@yahoo.com - Chuc thanh cong cung VDK PIC Thang8831 - 29 -
  30. CCS C for PIC16F877A 24/06/2008 */ #include #include #device *=16 adc=10 #FUSES NOWDT, HS, NOPUT, NOPROTECT, NODEBUG, NOBROWNOUT, NOLVP, NOCPD, NOWRT #use delay(clock=20000000) #use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=9) #include int8 low,high,key,mode,min,max,mode1,i; int1 blink,on_off,blink_min,blink_max; int1 do_F; void convert_bcd(int8 x); void set_blink(); void bao_dong(); void test(); // void main() { float value; on_off =1; min =15; //nhiet do min default max =35; //nhiet do max default do_F =0 ; i = 0 ; mode =0 ; mode1 = 0 ; blink=0 ; trisa = 0xFF; trisb = 0x01; trisd = 0x00; printf("Chuong trinh do nhiet do\n"); LCD_init(); Printf(LCD_putchar,"Lop DT8 - BKHN"); LCD_putcmd(0xC0); Printf(LCD_putchar,"Khoi tao "); // Khoi tao cho ngat ngoai enable_interrupts (INT_EXT); ext_int_edge(H_TO_L); enable_interrupts (GLOBAL); // Khoi tao che do cho bo ADC setup_adc_ports(AN0); setup_adc(ADC_CLOCK_INTERNAL); delay_us(10); // Lay mau nhiet do lan dau tien value=(float)read_adc(); value = (value - 558.5)/2.048; // For 5V supply // value = (value - 754.8)/2.048; // For 3.7V Supply Thang8831 - 30 -
  31. CCS C for PIC16F877A 24/06/2008 // value = (value - 698.2)/2.048; // For 4V supply convert_bcd((int8)value); // Chuyen doi tach so tram, chuc, donvi de hien thi len LED 7 delay_ms(1000); LCD_putcmd(0xC0); Printf(LCD_putchar," Init OK"); while(1) { if (i==15) { value = read_adc(); value=(value-558.5)/2.048; if (do_F==1) value=1.8*value+32; convert_bcd((int8)value); printf("\n\rNhiet do phong: %u",value); LCD_putcmd(0xC0); printf(LCD_putchar," T = "); LCD_putchar(high); LCD_putchar(low); if (do_F==0) printf(LCD_putchar," C"); else printf(LCD_putchar," F"); i=0; } i++; if(((int8)value > 40) || ((int8)value < 15)) on_off=1; else { on_off = 0; LCD_Putcmd(0xCF); LCD_putchar(" "); blink=0; } if (on_off==1) { if (blink==0) { LCD_Putcmd(0xCF);LCD_putchar("!");blink=1;delay_ms(250);} else {LCD_Putcmd(0xCF);LCD_putchar(" ");blink=0;delay_ms(250);} } } } //end main #INT_EXT void test() { if (do_F == 1) do_F=0; else do_F=1; } void set_blink() { switch(mode) { Thang8831 - 31 -
  32. CCS C for PIC16F877A 24/06/2008 case 1: blink_min=1; break; case 2: {blink_max=1; blink_min=0;} break; case 3: {mode=0; blink=0; blink_min=0; blink_max=0;} break; } } void convert_bcd(int8 x) { low=x%10; //chia lay phan du, so hang don vi high=x/10; //tach hang tram va hang chuc low = low + 0x30; high = high + 0x30; } void bao_dong(){ int8 i; if (blink == 0) blink = 1; else blink=0; for(i=0;i #fuses HS,NOWDT,NOPROTECT,NOLVP #device 16F877*=16 ADC=8 #use delay(clock=10000000) Int8 adc; main() { setup_adc(adc_clock_internal); setup_adc_ports(AN0); set_adc_channel(0); delay_ms(10); while(true) { adc=read_adc(); output_B(adc); } } Hình Kèm Theo Thang8831 - 32 -
  33. CCS C for PIC16F877A 24/06/2008 Khai báo: Code: "#fuses HS,NOWDT,NOPROTECT,NOLVP,NOBROWNOUT -HS :sử dụng thạch anh tần số cao -NOWDT:tắt WDT -NOPROTECT:tắt PROTECT -NOLVP:không dùng LVP -NOBROWNOUT:ko BROWNOUT Còn cụ thể ý nghĩa thế nào,bạn vào help của CCS C gõ :"#fuses" Cái luồng bên này nhh vẽ hình cẩu thả quá! Cái chân MCLR*, bạn phải nối thên cái công tắc ấn vào. nhh nối như vậy thì không reset được con PIC đâu Hai cái chân Vss phải được nối mass. Chân nào không xài, nhh nên đánh dấu bỏ đi (trong thanh công cụ của ORCAD có cái dấu này đó). Một cách viết khác để tham khảo: #include #fuses HS, NOWDT, NOPROTECT, NOLVP #device 16F877*=16, ADC=8 #use delay(clock=4000000) void main() { setup_adc(adc_clock_internal); setup_adc_ports(ALL_ANALOG); set_adc_channel(0); // TM Board: VR3=0, VR2=1, VR1=2 delay_ms(10); while (true) output_b(read_adc()); } B.4. ADC_186 // ANALOGUE TO DIGITAL CONVERTER HARDWARE INTERFACE PROGRAM // COPYRIGHT PROPERTY OF ALPHADATA DESIGNS LIMITED (c) 1999 // WRITTEN FOR THE MAX186 12 BIT 8-CHANNEL MICRO-WIRE A TO D Thang8831 - 33 -
  34. CCS C for PIC16F877A 24/06/2008 // published by permission of Alphadata designs on // Hi-Tech C website, Thanks! // // Version History // // Issue 1.0 : 21/12/1999 : First Officially Released // #include "ioh8314.h" #include "h8genlib.h" #include "spi.h" extern byte p4dr; /* call this to set up the hardware ports */ void adc_initialise(void) { } /* this table makes up the muddled way the device is addressed */ /* see the data sheet for the MAX186 to see why ! */ const byte command_table[]={ 0b10001111, 0b11001111, 0b10011111, 0b11011111, 0b10101111, 0b11101111, 0b10111111, 0b11111111}; /* call this to read one channel of data from the a to d */ word adc_read_channel(byte channel_number) { byte bits; byte command; int data; spi_adc_select(); /* Start by sending command byte */ command = command_table[channel_number]; for (bits=0;bits<8;bits++) { if ((command & 0x80) == 0) spi_lodata(); else spi_hidata(); command = command << 1; spi_hiclock(); spi_loclock(); } spi_lodata(); spi_hiclock(); spi_loclock(); Thang8831 - 34 -
  35. CCS C for PIC16F877A 24/06/2008 data = 0; for (bits=0;bits #fuses HS,NOWDT,NOPROTECT,NOLVP #device 16F877*=16 ADC=8 #use delay(clock=10000000) Int8 adc; main() { setup_adc(adc_clock_internal); setup_adc_ports(AN0); set_adc_channel(0); delay_ms(10); while(true) { adc=read_adc(); output_B(adc); } } TL: ADC là chức năng chuyển đổi tín hiệu analog -> digital (Analog Digital Converter). Nó dùng 1 điện áp để so sánh (Vref) tùy độ phân giải mà điện áp này được chia là nhiểu mức khác nhau (2^n) với 16F877A ADC 10 bit tức Vref dc chia thành 2^10 = 1024 mức. Mỗi mức ADC cách nhau tương ứng với Vref/số mức. Vref = 5V thì mỗi mức ADC của 16F877 ứng với 5V/1024 = 4.88mV. Điện áp trên cổng AN sẽ được so sánh để ra được mức ADC tương ứng. 0V ứng với ADC =0, 4.88mV ứng với ADC =1 Kết quả được chứa trong 2 thanh ghi ADRESH:ADRESL. Còn mạch cho code trên thì bạn mắc 1 cầu chia áp để đưa điện áp vào chân AN0 là được. Thang8831 - 35 -
  36. CCS C for PIC16F877A 24/06/2008 Dùng biến trở cho gọn. Còn PWM thì bạn đọc trong datasheet đó chương 8 phần CAPTURE/COMPARE/PWM MODULES và xem thêm bên ứng dụng điều khiển PID cho động cơ DC. +Em có đoạn code sau nhưng em không hiểu là lệnh adcHI và adcLO làm gì (với khai báo char adcHI,adcLO).Anh nào giải thích hộ em Code: while(TRUE) { adcValue = read_adc(); // Get ADC reading adcHI = (char)((adcValue >> 5)& 0x1f); adcLO = (char)((adcValue & 0x1f)|0x80); putc(adcHI); putc(adcLO); delay_ms(10); // Preset delay, repeat every 10ms } } TL: adcHI và adcLO là các biến kiểu char (8-bit), không phải là lệnh. Trong đoạn lệnh mà bạn đã đưa ra, kết quả của việc biến đổi A/D được tách thành 2 phần, phần cao chứa trong adcHI, còn 5 bit thấp hơn của kết quả được chứa trong adcLO. Bit 7 của adcLO được bật. Sau đó adcHI và adcLO được xuất ra thông qua hàm putc(). 3. DAC 3.1. DAC_1446 // DIGITAL TO ANALOGUE CONVERTER HARDWARE INTERFACE PROGRAM // COPYRIGHT PROPERTY OF ALPHADATA DESIGNS LIMITED (c) 1999 // WRITTEN FOR THE LTC1446 12 BIT 2-CHANNEL MICRO-WIRE D TO A // published by permission of Alphadata designs on // Hi-Tech C website, Thanks! // // Version History // // Issue 1.0 : 21/12/1999 : First Officially Released // #include #include "h8genlib.h" #include "ioh8314.h" #include "sci.h" #include "spi.h" #include "dac_1446.h" extern byte p4dr; /* call this to write one channel of data to the d to a */ void dac_write(word output) { Thang8831 - 36 -
  37. CCS C for PIC16F877A 24/06/2008 byte count; byte block; byte ops[3]; //char s[50]; /* Select ADC device */ spi_dac_select(); //Format output data if (output > 8; ops[2]=output; } else { ops[0] = output >> 4; ops[1] = (output dùng dao động nội, chế độ prescal 1:4 => clock cho Timer0 la Fosc/4/4 = Fosc/16. Thang8831 - 37 -
  38. CCS C for PIC16F877A 24/06/2008 Khi này ta có Fosc/4 => Tcy = 1us. Ttimer0 = Fosc/16 => 4us Timer0 tràn 8 bit => 4x8bit = 1024us. 8 bit có 256 trạng thái chứ không phải 255 trạng thái, do đó x256, Còn cách thứ 2 Postscale (Only Timer2) thì nó sẽ đếm số lần tràn của Timer2, Nghĩa là: 1:2 - 2 làn tràn cho ra 1 lần xung 1:16 - 16 làn Timer2 tràn Giả sử Áp dụng với Timer2 thay Timer0 ở trên và với postscale 1:8 ta sẽ thu được đầu ra là : 4x256x8 = 8192 us 4.1. Timer0 Thanh ghi tác động: Các lệnh: 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 Thang8831 - 38 -
  39. CCS C for PIC16F877A 24/06/2008 WDT_2304MS Mình cũng chưa hiểu ý nghĩa của hàm WDT_ , ko biết có phải khai báo như trên thì sau khoảng thời gian ms bao nhiêu đó đặt sau WDT_ thì sẽ reset lại Pic ????? 4.2. Timer1 Thanh ghi tác động: Các lệnh: 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 4.3. Timer2 Thanh ghi tác động: Các lệnh: 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. Code: /////////////////////////////////////////////////////////////////////// //// EX_STWT.C //// //// //// //// This program uses the RTCC (timer0) and interrupts to keep a //// //// real time seconds counter. A simple stop watch function is //// //// then implemented. //// //// //// //// Configure the CCS prototype card as follows: //// //// Insert jumpers from: 11 to 17 and 12 to 18. //// /////////////////////////////////////////////////////////////////////// #include #fuses HS,NOLVP,NOWDT,PUT Thang8831 - 39 -
  40. CCS C for PIC16F877A 24/06/2008 #use delay(clock=20000000) #use rs232(baud=9600,xmit=PIN_C6,rcv=PIN_C7) #define high_start 76 byte seconds, high_count; #INT_RTCC //Interrupt procedure clock_isr() { //called every time RTCC high_count -= 1; //flips from 255 to 0 if(high_count==0) { ++seconds; high_count=high_start; //Inc SECONDS counter every } //76 times to keep time } void main() { //A simple stopwatch program byte start, time; high_count = high_start; setup_timer_0( RTCC_INTERNAL | RTCC_DIV_256 ); set_timer0(0); enable_interrupts(INT_RTCC); enable_interrupts(GLOBAL); do { printf("Press any key to begin.\n\r"); getc(); start = seconds; printf("Press any key to stop.\r\n"); getc(); time = seconds - start; printf("%U seconds.\n\r", time); } while (TRUE); } 4.4. frequencymeter Code: #include #use delay(clock=4000000)// #fuses NOPROTECT,NOWDT,PUT,noBROWNOUT,noLVP,NOMCLR,xt #BYTE PORT_A=0X05 #BYTE PORT_B=0X06 / entegreterbiyecisi@yahoo.com // entegreterbiyecisi@yahoo.com / // LCD STUFF #define LCD_RS PIN_b0 #define LCD_EN PIN_b1 #define LCD_D4 PIN_b2 #define LCD_D5 PIN_b3 #define LCD_D6 PIN_b4 #define LCD_D7 PIN_b5 #define FIRST_LINE 0x00 #define SECOND_LINE 0x40 #define CLEAR_DISP 0x01 #define CURS_ON 0x0e #define CURS_OFF 0x0c Thang8831 - 40 -
  41. CCS C for PIC16F877A 24/06/2008 / entegreterbiyecisi@yahoo.com / #use standard_io ( a ) #use standard_io ( b ) / entegreterbiyecisi@yahoo.com / // proto statements void LCD_Init ( void ); void LCD_SetPosition ( unsigned int cX ); void LCD_PutChar ( unsigned int cX ); void LCD_PutCmd ( unsigned int cX ); void LCD_PulseEnable ( void ); void LCD_SetData ( unsigned int cX ); / entegreterbiyecisi@yahoo.com // entegreterbiyecisi@yahoo.com / / entegreterbiyecisi@yahoo.com / int32 ab=0,hz=0; int1 stept_say=0,data_bitti=0,step,aa=0; int16 sayi=0,tr=20; / entegreterbiyecisi@yahoo.com / #int_timer1 tas(){ ab++; } #int_timer0 sn(){sayi=0; set_timer0(61);//(255-60)*195*20=1000000us=dahili 1sn icin if(tr){ tr ;} else{delay_us ( 698 ); output_low(pin_a0); disable_interrupts (global); disable_interrupts(int_timer0); disable_interrupts(int_timer1); sayi=get_timer1(); aa=1; hz=sayi+(65536*ab); tr=20; }} / entegreterbiyecisi@yahoo.com / void main() { setup_timer_1(t1_external|t1_div_by_1); setup_timer_0 (RTCC_INTERNAL|RTCC_DIV_256); enable_interrupts(int_timer0); // timer0 enable_interrupts(int_timer1); enable_interrupts(global); lcd_init(); SET_TRIS_A(0b00100000); SET_TRIS_B(0b11000000); set_timer0(61); set_timer1(0); LCD_SetPosition(first_LINE+0); printf(lcd_putchar,"\NECATi KIYLIOGLU "); LCD_SetPosition(second_LINE+1); printf(lcd_putchar,"\ 0532 613 65 87"); delay_ms (500); LCD_PutCmd ( CLEAR_DISP ); sayi=0; hz=0; / entegreterbiyecisi@yahoo.com / while(true){ if(aa==1){ //LCD_PutCmd ( CLEAR_DISP ); Thang8831 - 41 -
  42. CCS C for PIC16F877A 24/06/2008 LCD_SetPosition(first_LINE+0); printf(lcd_putchar,"\FREQUENCYMETER "); if(999>=hz){ LCD_SetPosition(second_LINE+0); printf(lcd_putchar,"\FRQ=%ldHz ",hz);} ////////////////////////////////////////// if(hz>=1000){ if(999999>=hz){ LCD_SetPosition(second_LINE+0); printf(lcd_putchar,"\FRQ=%3.3wKhz ",hz);}} ///////////////////////////////////////////// if(hz>=1000000){ LCD_SetPosition(second_LINE+0); printf(lcd_putchar,"\FRQ=%2.6wMhz ",hz);} //////////////////////////////////////////// delay_ms (1); set_timer1(0); enable_interrupts(int_timer0); enable_interrupts(int_timer1); enable_interrupts (global); aa=0; ab=0; } }} / entegreterbiyecisi@yahoo.com / / entegreterbiyecisi@yahoo.com / //lcd basla void LCD_Init ( void ){ LCD_SetData ( 0x00 ); output_low ( LCD_RS ); LCD_SetData ( 0x03 ); // init with specific nibbles to start 4-bit mode LCD_PulseEnable(); LCD_PulseEnable(); LCD_PulseEnable(); LCD_SetData ( 0x02 ); // set 4-bit interface LCD_PulseEnable(); // send dual nibbles hereafter, MSN first LCD_PutCmd ( 0x2C ); // function set (all lines, 5x7 characters) LCD_PutCmd ( 0x0C ); // display ON, cursor off, no blink LCD_PutCmd ( 0x01 ); // clear display LCD_PutCmd ( 0x06 ); // entry mode set, increment } / entegreterbiyecisi@yahoo.com / void LCD_SetPosition ( unsigned int cX ) { // this subroutine works specifically for 4-bit Port A LCD_SetData ( swap ( cX ) | 0x08 ); LCD_PulseEnable(); LCD_SetData ( swap ( cX ) ); LCD_PulseEnable(); } / entegreterbiyecisi@yahoo.com / void LCD_PutChar ( unsigned int cX ) { // this subroutine works specifically for 4-bit Port A output_high ( LCD_RS ); LCD_SetData ( swap ( cX ) ); // send high nibble LCD_PulseEnable(); LCD_SetData ( swap ( cX ) ); // send low nibble LCD_PulseEnable(); Thang8831 - 42 -
  43. CCS C for PIC16F877A 24/06/2008 output_low ( LCD_RS ); } / entegreterbiyecisi@yahoo.com / void LCD_PutCmd ( unsigned int cX ) { // this subroutine works specifically for 4-bit Port A LCD_SetData ( swap ( cX ) ); // send high nibble LCD_PulseEnable(); LCD_SetData ( swap ( cX ) ); // send low nibble LCD_PulseEnable(); } / entegreterbiyecisi@yahoo.com / void LCD_PulseEnable ( void ) { output_high ( LCD_EN ); delay_us ( 100 ); output_low ( LCD_EN ); delay_ms ( 5 ); } / entegreterbiyecisi@yahoo.com / void LCD_SetData ( unsigned int cX ) { output_bit ( LCD_D4, cX & 0x01 ); output_bit ( LCD_D5, cX & 0x02 ); output_bit ( LCD_D6, cX & 0x04 ); output_bit ( LCD_D7, cX & 0x08 ); } / entegreterbiyecisi@yahoo.com / //lcd son /////////////////////////////////////////////////////////////////////////// Cảm ơn bạn Necati đã Post lên chương trình mà tôi và nhiều anh em đang quan tâm. Tôi muốn hỏi thêm là nếu muốn đo 1 tần số sóng mang nằm trong tín hiệu điều chế thì giải quyết như thế nào. Giả sử có 1 tín hiệu cần điều chế có f = 2KHz độ rộng xung là 2us, tín hiệu sóng mang có f0 = 20MHz, nghĩa là trong 2us của tín hiệu điều chế sẽ có 20*2 = 40 chu kỳ xung của sóng mang trong đó. Bài toán ở đây là đo được tần số 20MHz từ tín hiệu đã điều chế đó. Tôi đọc kỹ thì thấy chương trình của anh Necati cũng đo tần số liên tục, nhưng sử dụng thêm Timer0 để định thời 1 giây, và Timer1 cũng để lấy giá trị đếm số lần xuất hiện xung vào. Tất nhiên, chương trình viết rất chuyên nghiệp, đó cũng là điều mà tôi và nhiều anh em cần học hỏi thêm rất nhiều. 5. INTERRUPT Các lệnh dùng cho ngắt: Code: enable_interrupts(level); //cho phép ngắt kiểu level disable_interrupts(level); //cấm ngắt kiểu level ext_int_edge(edge); // chọn cách lấy xung loại edge level bao gồm: GLOBAL : ngắt toàn cục INT_RTCC : tràn TMR0 INT_RB : có thay đổi trạng thái một trong các chân RB4 đến RB7 INT_EXT : ngắt ngoài INT_AD : chuyển đổi AD đã hoàn tất INT_TBE : bộ đệm chuyển RS232 trống INT_RDA : data nhận từ RS232 sẵn sàng Thang8831 - 43 -
  44. CCS C for PIC16F877A 24/06/2008 INT_TIMER1 : tràn TMR1 INT_TIMER2 : tràn TMR2 INT_CCP1 : có capture hay compare trên CCP1 INT_CCP2 : có capture hay compare trên CCP2 INT_SSP : có hoạt động SPI hay I2C INT_PSP : có data vào cổng parallel slave INT_BUSCOL : xung đột bus INT_EEPROM : ghi vào eeprom hoàn tất INT_TIMER0 : tràn TMR0 INT_COMP : kiểm tra bằng nhau comparator edge bao gồm: L_TO_H : cạnh lên H_TO_L : cạnh xuống Sau khai báo trên để vào đoạn chương trình ngắt, khai báo: #INT_ Ví dụ vào thực thi ngắt ngoài, ta có đoạn code: Code: #INT_EXT void ngat_ngoai() { //Chương trình ngắt tại đây } 5.1. Ngắt Timer0 Đây là chương trình dùng ngắt Timer0 định thì 1s. Đầu tiên led ở chân RB0 sáng, sau 1s sẽ dịch sang trái, nghĩa là led 1 trên chân RB1 sáng , lần lượt như vậy cho các led trên portB và lặp lại mãi mãi. Code: #include #fuses NOWDT,PUT,XT,NOPROTECT #use delay(clock=4000000) #byte PORTB = 0x06 int16 count; int8 a; //Chuong trinh ngat TMR0 #int_timer0 void interrupt_timer0() { set_timer0(6); ++count; if(count == 2000) // 2000*500us = 500000us = 1s { count=0; rotate_left(&a,1); } } //Chuong trinh chinh void main(void) { set_tris_b(0); enable_interrupts(int_timer0); setup_timer_0(RTCC_INTERNAL|RTCC_DIV_2); enable_interrupts(global); set_timer0(6);// T_dinhthi = 2*(256 - 6)*1us = 500us Thang8831 - 44 -
  45. CCS C for PIC16F877A 24/06/2008 a = 0x01; while(true) { PORTB = a; } } Trích: Mình cũng chưa hiểu ý nghĩa của hàm WDT_ , ko biết có phải khai báo như trên thì sau khoảng thời gian ms bao nhiêu đó đặt sau WDT_ thì sẽ reset lại Pic ????? WDT là "chó giữ nhà" (Watchdog Timer). Bộ phận này có nhiệm vụ reset lại PIC sau một khoảng thời gian định trước. WDT sẽ reset vi điều khiển khi bộ đếm của WDT bị tràn. Mục đích của nó là tránh trường hợp vi điều khiển bị "treo" khi phải hoạt động liên tục trong một khoảng thời gian lâu dài. Thời gian định trước này phụ thuộc vào tần số loại thạch anh sử dụng và bộ chia tần số trước (prescaler) của WDT. Ta thấy WDT chỉ liên quan đến Timer 0, còn các Timer khác không có liên quan. Đó là tại vì WDT có bộ chia tần số (prescaler) dùng chung với Timer 0. Lưu ý là muốn sử dụng WDT cần chú ý đến phần khai báo các "fuse" ở đầu chương trình. Trích: rtcc_state là một trong những constant sau: RTCC_INTERNAL RTCC_EXT_L_TO_H RTCC_EXT_H_TO_L Mỗi Timer đều có 2 tác dụng: Tác dụng định thời: Timer sẽ dựa vào các xung tạo ra bởi bộ dao động (thạch anh, dao động RC, ) cung cấp cho vi điều khiển để đếm. Và dựa vào tần số bộ dao động, giá trị các bộ chia tần số và giá trị của Timer, ta có thể xác định được thời gian thực. Như vậy trong Thang8831 - 45 -
  46. CCS C for PIC16F877A 24/06/2008 trường hợp muốn Timer hoạt động ở chế độ định thời, ta phải khai báo rtcc_state là "RTCC_INTERNAL" (xử dụng tần số dao động nội). Tác dụng đếm: Timer sẽ dựa vào các xung lấy từ môi trường bên ngoài để đếm. Tùy theo Timer mà ta sử dụng chân lấy xung tương ứng (Timer 0 là chân RA4, Timer1 là chân RC0). Các xung này có tác dụng phản ánh các hiện tượng trong thực tế, và việc đếm các xung cũng đồng nghĩa với việc đếm các hiện tượng đó. Và để linh động hơn trong quá trình xử lí, Timer còn cho phép chọn cạnh tác động lên bộ đếm (chế độ này chỉ có ở Timer 0). Như vậy muốn Timer hoạt động ở chế độ đếm, ta phải khai báo rtcc_state là một trong 2 trường hợp còn lại (sử dụng dao động ngoài). Trích: 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 Ở đây có đến 2 hàm dùng để ấn định tỉ số chia của prescaler, một hàm là "RTCC_DIV_ ", một hàm là "WDT_ ". Đó là bởi vì Timer 0 và WDT dùng chung bộ chia tần số. Khi bộ chia được Timer 0 sử dụng thì WDT không đựoc hỗ trợ với bộ chia này nữa. Như vậy sự khác biệt về thao tác giữa 2 hàm này có thể là như sau: Hàm "RTCC_DIV_ " : cho phép Timer 0 sử dụng bộ chia tần số, không cho phép WDT sử dụng và ấn định tỉ số chia của nó. Hàm "WDT_ " : cho phép WDT 0 sử dụng bộ chia tần số, không cho phép Timer 0 sử dụng và ấn định tỉ số chia của nó. Trích: 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. hôm nay 09:30 AM Ta có thể nhận thấy là Timer 2 có đến 2 bộ chia tần số trước và sau, một bộ prescaler được đính kèm vào các chế độ hoạt động của Timer 2 (T2_DIV_BY_1, T2_DIV_BY_4, Thang8831 - 46 -
  47. CCS C for PIC16F877A 24/06/2008 T2_DIV_BY_16), một bộ là postscaler cis tỉ số chia từ 1:16. Như vậy nó cho phép việc lựa chọn tỉ số chia linh động hơn. Timer 2 không hoạt động ở chế độ đếm. Chức năng của nó chủ yếu là tác động lên tốc độ baud cho MSSP thì phải. Không nhớ rõ lắm. Trích: postscale là số nguyên 1-16, xác định reset bao nhiêu lần trước khi ngắt. Cái này để mình coi lại đã, tại sao nó lại xác định reset bao nhiêu lần trước khi ngắt ??. Phải coi lại cái sơ đồ khối của Timer 2 mới biết được. Một cách viết khác để tham khảo với hy vọng viết C sao cho dễ hiểu :-) #include #fuses NOWDT, PUT, XT, NOPROTECT #use delay(clock=4000000) #define INITIAL_VALUE 6 byte count; byte led; void change_led(void); #int_timer0 void interrupt_timer0() { set_timer0(INITIAL_VALUE); count++; if (count == 2000) { count = 0; change_led(); } } void main() { set_tris_b(0); enable_interrupts(GLOBAL); enable_interrupts(INT_TIMER0); setup_timer_0(RTCC_INTERNAL | RTCC_DIV_2); // set mod set_timer0(INITIAL_VALUE); // set initial value count = 0; led = 1; while (true) output_b(led); } void change_led() { led = led << 1; if (led == 0) led = 1; } Thang8831 - 47 -
  48. CCS C for PIC16F877A 24/06/2008 Thứ nhất khi nào hàm con interrupt_timer0() được gọi Thứ hai, việc tính toán định thì 1s được tính như thế nào. Thứ ba, biến a được khai báo là số nguyên 8bit. Phạm vi từ 0->255,làm sao bằng 256 đc. 1. Ngắt Timer0 được gọi khi Timer 0 bị tràn từ 0xff sang 0x00 với điều kiện phải có 2 khai cho phép ngắt timer 0 và ngắt toàn cục: Code: enable_interrupts(int_timer0); enable_interrupts(global); 2. Việc tính toán thời gian tràn của Timer rất dễ, xem luồng "PIC6f877A từ dễ tới khó", hoặc tìm đâu đó trong diễn đàn này phần mềm "PIC Timer Calculator". 3. Biến a 8bit int, ko thể có giá trị 256 -> đúng vậy. 5.2. Ngắt ngoài Chương trình ngắt đây +Các bạn coi gium` mình có sai chỗ nào không mà mình delay không được: Mình muốn khi đóng RB4 thì LED sẽ nháp nháy với delay 50ms chẳng hạn. Phần ngắt chuyển chế độ thì mình làm được nhưng delay trong mỗi chế độ thì potay. Code: #include #fuses NOWDT,PUT,XT,NOPROTECT #use delay(clock=1000000) #use fast_io(b) #byte portb=0x06 #byte intcon=0x000B #bit RB4=portb.4 #bit RB5=portb.5 #bit RBIF=intcon.0 //dinh nghia co ngat RB #bit RBIE=intcon.3 //dinh nghia bit cho phep ngat RB // Chuong trinh ngat #int_RB void ngat_RB() { if((RBIF)&&(RBIE)) { //Kiem tra sw1 { if(RB4==0) { portb=0b00000001; delay_ms(200); portb=0b00001111; delay_ms(200); } } //Kiem tra sw2 { if(RB5==0) { portb=0b00001000; } } Thang8831 - 48 -
  49. CCS C for PIC16F877A 24/06/2008 RBIF=0; //Xoa co ngat RB } } // Chuong trinh chinh main() { set_tris_b(0b11110000); portb=0b11110000; enable_interrupts(global); enable_interrupts(int_RB); ext_int_edge(H_to_L); while(true) { } } TL: Code: #include #fuses NOWDT,PUT,XT,NOPROTECT #use delay(clock=4000000) #use fast_io(b) #byte portb=0x06 #byte intcon=0x000B #bit RB4=portb.4 #bit RB5=portb.5 #bit RBIF=intcon.0 //dinh nghia co ngat RB #bit RBIE=intcon.3 //dinh nghia bit cho phep ngat RB // Chuong trinh ngat #int_RB void ngat_RB() { if((RBIF)&&(RBIE)) { //Kiem tra sw1 { if(RB4==0) { portb=0b00000001; delay_ms(50); portb=0b00001111; delay_ms(50); } } //Kiem tra sw2 { if(RB5==0) { portb=0b00001000; } } RBIF=0; //Xoa co ngat RB Thang8831 - 49 -
  50. CCS C for PIC16F877A 24/06/2008 } } // Chuong trinh chinh main() { set_tris_b(0b11110000); portb=0b11110000; enable_interrupts(global); enable_interrupts(int_RB); ext_int_edge(H_to_L); while(true) { } } Đã sửa lại cho phù hợp file mô phỏng của bạn. Với code trên, kết thúc ngắt tất nhiên ko còn delay nữa. 50ms hơi ít, tăng lên 1000ms, thấy kết quả. +Chào cả nhà ! Sao không thấy bác nào post bài vào luồng này vậy kà !Trầm quá !Trầm quá !Hay cái CCS C này không hấp dẫn mọi người chăng! Không ai viết gì, tớ vẫn post cho nó đỡ trầm ! Đã ví dụ về ngắt Timer, sau đây là 2 ví dụ về ngắt ngoài trên chân RB0 và trên các chân RB4 đến RB7: Chương trình sau dùng ngắt ngoài trên RB0 đếm số lần cái button được nhấn xuống, hiển thị lên led 7 đoạn (common cathode).Nếu số lần nhấn vượt quá 9, chương trình sẽ quay về hiển thị lên led từ số 1. Code: // // Author : nhh // Date : 03/04/06 // Hardware: PIC16F877A // #include #fuses NOWDT,PUT,XT,NOPROTECT #use delay(clock=4000000) #use fast_io(b) #use fast_io(d) #byte portb=0x06 #byte portd=0x08 const unsigned char digital[]={0b00000110, 0b01011011, 0b01001111, 0b01100110,\ 0b01101101, 0b01111101, 0b00000111, 0b01111111, 0b01101111}; // ma hoa digital duoi dang mang // Chuong trinh ngat #int_ext void ngat_RB0() { int i; if(i<9) { portd=digital[i]; ++i; } if(i==9) { i=0; Thang8831 - 50 -
  51. CCS C for PIC16F877A 24/06/2008 } } // Chuong trinh chinh main() { set_tris_b(0b00000001); set_tris_d(0); enable_interrupts(global); enable_interrupts(int_ext); ext_int_edge(H_to_L); portd=0b00111111; while(true) { // chi doi ngat nen vong lap nay ko co gi ca ! } } 5.3. Ngắt ngoài trên RB4-RB7 Còn đây là ứng dụng ngắt ngoài trên RB4 đến RB7 để thay đổi kiểu cũng như tốc độ chớp nháy mấy con led chỉ để ngắm cho vui mắt ! Ấn sw1, led1 nhấp nháy với delay 250ms Ấn sw2, led1,2 nhấp nháy với delay 200ms Ấn sw3, led1,2,3 nhấp nháy với delay 150ms Ấn sw4, led1,2,3,4 nhấp nháy với delay 100ms Thang8831 - 51 -
  52. CCS C for PIC16F877A 24/06/2008 Code: // // Author : nhh // Date : 03/04/06 // Hardware: PIC16F877A // #include #fuses NOWDT,PUT,XT,NOPROTECT #use delay(clock=4000000) #use fast_io(b) #byte portb=0x06 #byte intcon=0x000B #bit RB4=portb.4 #bit RB5=portb.5 #bit RB6=portb.6 #bit RB7=portb.7 #bit RBIF=intcon.0 //dinh nghia co ngat RB #bit RBIE=intcon.3 //dinh nghia bit cho phep ngat RB int led=0,speed; // Chuong trinh ngat #int_RB void ngat_RB() { if((RBIF)&&(RBIE)) { //Kiem tra sw1 { if(RB4==0) { led=0b00000001; //led1 sang speed=250; } } //Kiem tra sw2 { if(RB5==0) { led=0b00000011; //led1,2 sang speed=200; } } //Kiem tra sw3 { if(RB6==0) { led=0b00000111; //led1,2,3 sang speed=150; } } //Kiem tra sw4 { if(RB7==0) { led=0b00001111; //led1,2,3,4 sang speed=100; } } RBIF=0; //Xoa co ngat RB } } Thang8831 - 52 -
  53. CCS C for PIC16F877A 24/06/2008 // Chuong trinh chinh main() { set_tris_b(0b11110000); portb=0b00001111; enable_interrupts(global); enable_interrupts(int_RB); ext_int_edge(H_to_L); while(true) { portb=led; delay_ms(speed); portb=0; delay_ms(speed); } } [quote=nhh;2261]Còn đây là ứng dụng ngắt ngoài trên RB4 đến RB7 để thay đổi kiểu cũng như tốc độ chớp nháy mấy con led chỉ để ngắm cho vui mắt ! Ấn sw1, led1 nhấp nháy với delay 250ms Ấn sw2, led1,2 nhấp nháy với delay 200ms Ấn sw3, led1,2,3 nhấp nháy với delay 150ms Ấn sw4, led1,2,3,4 nhấp nháy với delay 100ms [code]// // Author : nhh // Date : 03/04/06 // Hardware: PIC16F877A // Không như trong MPLAB, đã định nghĩa sẵng các thanh ghi và bit tương ứng, còn CCS C chỉ định nghĩa chân PIC, những thanh ghi, những bit mà CCS C cho là cần thiết, ta xem trong file PIC16F887A.h, thanh ghi, bit nào chưa định nghĩa mà muốn sử dụng thì phải định nghĩa nó. Ta có thể viết riêng 1 file.h loại này sao cho mình dễ nhớ nhất, đến khi muốn sử dụng chỉ cần khai báo #include vào là xài thôi! Em mới vô thôi, huynh giải thích mấy dòng code sau cho em được ko ? " set_timer0(6) " ; " ++count; " ; "if (count==2000) " Nó nằm trong ct nháy led dùng interrup và timer0. biến count và a có tác dụng gì ? tại sao phải lùi a "a=a<<1" ? • set_timer0(6); đây là phương thức gọi hàm. set_timer0() là một hàm, 6 là tham số. Còn hàm này làm gì thì bạn phải tự tìm hiểu lấy. * ++count; là thực hiện tăng biến count lên 1 đơn vị. Nó giống lệnh count=count+1. Còn có một cú pháp nhữa là count++. Hai lệnh lệnh này có sự khác nhau về thứ tự thực hiện khi nằm trong một biểu thức so sánh. * if(count==2000) là phép so sánh. Nếu giá trị của biến count bằng 2000 thì kết quả của phép so sánh là True ngược lại là False. * a là mình chịu thua!?!? Còn dịch trái là để nhân đôi giá trị biết. Bạn hãy xem lại đại số bool có rất nhiều điều hay ở đó. +Nhân tiện anh cho em hỏi thêm : giả sử em viết một ct nháy led (cho nó chạy như vòng lặp ), đầu tiên kt các khóa để biết ct sẽ nháy led kiểu nào. sau đó thì ct sẽ thực hiện lệnh nháy đèn led, nếu như sau khi ta bấm nút chọn kiểu nháy, ct sẽ tiếp tục thực hiện lệnh tiếp Thang8831 - 53 -
  54. CCS C for PIC16F877A 24/06/2008 theo mà ta đổi ý bấm nút khác để đổi kiểu thì ct sẽ không nhận vì đang thực hiện các lệnh bật-tắt led. Bài tập này hình như có ở trang 2, 3 dùng interrup và timer0, Tại sao nhỉ ? Nếu bạn sử dụng vòng lặp thì nút bấm sẽ ko được nhận trong khi vòng lặp đang thực hiện. Cách giải quyết bài toán như sau: _ Bạn hãy khai báo 1 biến toàn cục. Biến này lưu kiểu chớp. Hàm chớp sẽ dựa vào giá trị biến này mà đổi kiểu chớp. _ Sử dụng timer0 làm thời gian chuẩn để trì hoãn chớp tắt (vì nếu dùng làm thời gian trì hoãn chớp tắt thì nó quá ngắn ta không thể thấy chớp được). _ Khai báo một biến toàn cục làm hệ số cho thời gian trì hoãn. Trong ngắt Timer0 bạn sẽ đếm biến này đến một giá trị nào đó thì gọi hàm chớp tắt. _ Bạn cũng cần khai báo một biến toàn cục để cho hàm chớp tắt biết mình phải làm gì. Hoặc nếu ko bạn có thể truyền tham số cho hàm. _ Trong chương trình main, bạn dùng vòng lặp để bắt phím nhấn (hoặc dùng ngắt) và thực hiện thay đổi giá trị của biến kiểu chớp. Trên đây là giải thuật, đó là phương pháp để giải quyết bài toán. Tất nhiên trong quá trình viết bạn có thể khai báo thêm các biến toàn cục hay cục bộ để thực hiện thuật toán. Tốt nhất là bạn nên viết trước và gặp khó khăn thì đưa cả code lên để mọi người giúp bạn giải quyết. Em mượn tạm đoạn code của nhh anh mổ xẻ nó giùm em nhé // // Author : nhh // Date : 02/04/06 // Hardware: PIC16F877A // #include #fuses NOWDT,PUT,XT,NOPROTECT #use delay(clock=4000000) #use fast_io(b) #byte portb=0x06 #define led pin_B0 int16 count; int8 a; //Chuong trinh ngat TMR0 #int_timer0 void interrupt_timer0() { set_timer0(6); ++count; if(count==2000) { count=0; a=a<<1; // dich trai a 1bit } if(a==256) { a=1; count=0; } } Thang8831 - 54 -
  55. CCS C for PIC16F877A 24/06/2008 //Chuong trinh chinh main() { set_tris_b(0); enable_interrupts(global); enable_interrupts(int_timer0); setup_timer_0(RTCC_INTERNAL|RTCC_DIV_2); set_timer0(6); count=0; a=1; while(true) { portb=a; } } Anh giải thích mấy cỗ có giùm em. Tại sao phải setup_timer_0(RTCC_INTERNAL| RTCC_DIV_2); mà ko Div 3, 4, hở anh ? *Dấu thứ nhất: [CODE:] a=a<<1; // dich trai a 1bit [/code] Như tác giả đã chú thích đó là lệnh dịch trái 1 bit. VD: trước khi dịch, a có giá trị 0 0 0 0 0 0 0 1 (0x01) thì sau lệnh dịch này giá trị biến a sẽ là 0 0 0 0 0 0 1 0 (0x02). Vậy lệnh dịch trái sẽ làm tăng giá trị biến bị dịch lên 2 lần: 2 thành 4. Giống như bạn dịch trong hệ thập phân số 0500 thì được 5000 tức tăng 10 lần. Hệ nhị phân (2 số) dịch trái 1 bit sẽ tăng giá trị 2 lần, hệ thập phân (10 số) dịch trái một bit, số sẽ tăng giá trị 10 lần. Vậy nhiều lệnh dịch sẽ làm cho a thay đổi như sau 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 * Dấu thứ 2: [CODE:] setup_timer_0(RTCC_INTERNAL|RTCC_DIV_2); [/code] mode may be one or two of the constants defined in the devices .h file. RTCC_INTERNAL, RTCC_EXT_L_TO_H or RTCC_EXT_H_TO_L RTCC_DIV_2, RTCC_DIV_4, RTCC_DIV_8, RTCC_DIV_16, RTCC_DIV_32, RTCC_DIV_64, RTCC_DIV_128, RTCC_DIV_256 Bạn hãy đọc Help và các ví dụ của nó thì bạn sẽ hiểu được cách tính. Bạn hãy tìm hiều kỹ về bộ chia tần trong DataSheet của chip sẽ hiểu tại sao chỉ làm 2 mũ n. * Dấu thứ 3: Thang8831 - 55 -
  56. CCS C for PIC16F877A 24/06/2008 [CODE:] portb=a; [/code] Đây là lệnh xuất giá trị biến a ra PortB. a có 8 bit, PortB có 8 chân B7 đến B0. Lệnh trên sẽ áp các bit của a vào PortB theo đúng trọng số. Tôi copy nguyen bai delay1s_RB0 của bác về nạp thử, nhưng không hiểu sao nó không chạy, mà phải cắm chân RB3 (chân PGM) xuống đất thì nó mới chạy, bác có thể cho tôi biết tại sao không? Cám ơn bác! Ở một số chip, chân RB3 còn là chân PGM dùng để cấp nguồn cho chế độ nạp chip điện áp thấp (Low-Voltage Programming). Nếu chip của bạn còn mới, có thể nó đang được bật chế độ LVP (mặc định khi xuất xưởng), do đó bạn để hở RB3 sẽ không cho phép chip chạy chương trình, và bạn phải nối RB3 xuống 0V (nên nối qua một điện trở khoảng 1k) thì chip mới không đi vào chế độ LVP và mới chạy được chương trình đã có trong chip. Bây giờ thì tôi đã hiểu, nhưng khi phải nối mass chân RB3 rồi thì coi như chân đó không thể dùng được nữa hả? Bạn vẫn có thể dùng chân RB3 như bình thường khi chip đã ở chế độ chạy chương trình bình thường. Nhưng nếu bạn nối thẳng RB3 vào mức 0V và sau đó dùng RB3 như ngõ ra, thì khi bạn xuất mức cao ra RB3 sẽ gây ngắn mạch giữa các chân Vdd và Vss (vì bạn đã nối thẳng RB3 vào Vss, và sau đó lại yêu cầu ngõ ra RB3 được nối lên Vdd trong chương trình). Đó chính là lý do của lời khuyên nên nối RB3 xuống mức 0V thông qua một điện trở khoảng 1k, nếu bạn muốn RB3 khi dùng như ngõ ra được nhẹ tải hơn thì có thể dùng giá trị 4.7k hay 10k cho điện trở kéo xuống 0V đó. 5.4. Giải mã bàn phím Mạch quét 16 phím, hiện kết quả lên led 7 đoạn. Thang8831 - 56 -
  57. CCS C for PIC16F877A 24/06/2008 Ấn sw1, led1 nhấp nháy với delay 250ms Ấn sw2, led1,2 nhấp nháy với delay 200ms Ấn sw3, led1,2,3 nhấp nháy với delay 150ms Ấn sw4, led1,2,3,4 nhấp nháy với delay 100ms Code: // * // Author : nhh // Date : 03/04/06 // Hardware: PIC16F877A // #include #fuses NOWDT,PUT,XT,NOPROTECT #use delay(clock=4000000) #use fast_io(b) #use fast_io(c) #byte portb = 0x06 #byte portc = 0x07 #bit RB0 = 0x06.0 #bit RB1 = 0x06.1 #bit RB2 = 0x06.2 #bit RB3 = 0x06.3 #bit RB4 = 0x06.4 #bit RB5 = 0x06.5 #bit RB6 = 0x06.6 #bit RB7 = 0x06.7 #bit RBIF=intcon.0 //dinh nghia co ngat RB #bit RBIE=intcon.3 //dinh nghia bit cho phep ngat RB int a; const unsigned char dig[]={0b00111111,0b00000110, 0b01011011,0b01001111,\ Thang8831 - 57 -
  58. CCS C for PIC16F877A 24/06/2008 0b01100110,0b01101101,0b01111101,0b00000111,0b01111111,0b01101111,0b0111011 1,\ 0b01111100,0b00111001,0b01011110,0b11111001,0b11110001}; // ma hoa digital duoi dang mang // Chuong trinh ngat #int_RB void ngat_RB() { if((RBIF)&&(RBIE)) { { if(RB4&&RB0) a=dig[0]; } { if(RB4&&RB1) a=dig[4]; } { if(RB4&&RB2) a=dig[8]; } { if(RB4&&RB3) a=dig[12]; } // { if(RB5&&RB0) a=dig[1]; } { if(RB5&&RB1) a=dig[5]; } { if(RB5&&RB2) a=dig[9]; } { if(RB5&&RB3) a=dig[13]; } // { if(RB6&&RB0) a=dig[2]; } { if(RB6&&RB1) a=dig[6]; } { if(RB6&&RB2) a=dig[10]; } { if(RB6&&RB3) a=dig[14]; } Thang8831 - 58 -
  59. CCS C for PIC16F877A 24/06/2008 // { if(RB7&&RB0) a=dig[3]; } { if(RB7&&RB1) a=dig[7]; } { if(RB7&&RB2) a=dig[11]; } { if(RB7&&RB3) a=dig[15]; } RBIF=0; //Xoa co ngat RB } } // Chuong trinh chinh main() { set_tris_b(0b11110000); set_tris_c(0); enable_interrupts(global); enable_interrupts(int_RB); ext_int_edge(H_to_L); portb=0; portc=0; while(true) { portb=1; portb=2; portb=4; portb=8; portc=a; } } anh có thể nói rõ hơn được không a? số: 1,2,4,6,8 nó mặc định cho cổng đấy a? - Xuất 1 ra PORTA thì chỉ có bit 0 (tức là chân RA0) ở mức 1, các bit (chân) khác là 0. - Xuất 2 ra PORTA thì chỉ có bit 1 (tức là chân RA1) ở mức 1, các bit (chân) khác là 0. - Xuất 4 ra PORTA thì chỉ có bit 2 (tức là chân RA2) ở mức 1, các bit (chân) khác là 0. - Xuất 8 ra PORTA thì chỉ có bit 3 (tức là chân RA3) ở mức 1, các bit (chân) khác là 0. Bạn nhìn vào sơ đồ mạch của nhh sẽ thấy các chân RA0 RA3 nối với các cột của bàn phím. TL: Không như trong MPLAB, đã định nghĩa sẵng các thanh ghi và bit tương ứng, còn CCS C chỉ định nghĩa chân PIC, những thanh ghi, những bit mà CCS C cho là cần thiết, ta xem trong file PIC16F887A.h, thanh ghi, bit nào chưa định nghĩa mà muốn sử dụng thì phải định nghĩa nó.Ta có thể viết riêng 1 file.h loại này sao cho mình dễ nhớ nhất, đến khi muốn sử dụng chỉ cần khai báo #include vào là xài thôi! #fuses NOWDT,PUT,HS,NOPROTECT #use delay(clock=4000000) Bạn chú ý, clock = 4MHz là chế đọ dao động XT chứ không phải HS. Thang8831 - 59 -
  60. CCS C for PIC16F877A 24/06/2008 Bài : Giải mã bàn phím Mạch quét 16 phím, hiện kết quả lên led 7 đoạn. Trong chuong trinh thieu dinh nghia #byte intcon=0x000B và port xuất ra led 7 đoạn là PORTC không phải portd như hình vẽ. +Chào bạn mình thay led 7 đoạn bằng LCD thì chương trình chạy không đúng, bạn có thể hướng dẫn cho mình dùng ngắt để giải mã phím xuất ra lcd được không, cám ơn bạn nhiều. LCD và led 7 đoạn tất nhiên là khác nhau rồi. Để thực hiện tốt giải mã matrix phím, bác phải có giải pháp chống nhiễu (run phím) bằng phần cứng, hoặc phần mềm, thông thường là dùng phần mềm. Code bên trên chưa có chống nhiễu 5.5. Chương trình gửi ký tự ra 2x16 LCD dùng CCS C Chương trình gửi ký tự ra 2x16 LCD dùng CCS C #include "16F877A.h" // PIC16F877A header file #use delay(clock=4000000) // for 4Mhz crystal #fuses XT, NOWDT, NOPROTECT, NOLVP // for debug mode #define WRITE_DATA 0 #define WRITE_COMMAND 1 #define NCHAR_PER_LINE 16 // max char numbers per line #define MS10 10 // 10 milliseconds #define US400 400 // 400 microseconds #define LCD_RS PIN_A1 #define LCD_RW PIN_A2 #define LCD_E PIN_A3 /////////////////////////////////////////////////////////// // /* private */ void lcd_write(byte dat, int1 option) { delay_us(US400); if (option == WRITE_DATA) output_high(LCD_RS); else // option == WRITE_COMMAND output_low(LCD_RS); output_low(LCD_RW); output_b(dat); output_high(LCD_E); delay_us(US400); output_low(LCD_E); } /////////////////////////////////////////////////////////// // void lcd_init(void) { output_low(LCD_E); // Let LCD E line low lcd_write(0x38, WRITE_COMMAND); // Set LCD 16x2, 5x7, 8bits data delay_ms(15); Thang8831 - 60 -
  61. CCS C for PIC16F877A 24/06/2008 lcd_write(0x01, WRITE_COMMAND); // Clear LCD display delay_ms(MS10); lcd_write(0x0f, WRITE_COMMAND); // Open display & current delay_ms(MS10); lcd_write(0x06, WRITE_COMMAND); // Window fixed (Character Entry Mode?) delay_ms(MS10); } /////////////////////////////////////////////////////////// // void lcd_display_char(int8 line, int8 pos, char ch) { line = (line == 0) ? 0 : 1; pos = (pos > NCHAR_PER_LINE) ? NCHAR_PER_LINE : pos; lcd_write(0x80 + 0x40 * line + pos, WRITE_COMMAND); lcd_write(ch, WRITE_DATA); } /////////////////////////////////////////////////////////// void lcd_display_str(int8 line, char str[], int8 nchars) { int8 i; for (i = 0; i < nchars; i++) lcd_display_char(line, i, str[i]); } /////////////////////////////////////////////////////////// / * Display characters to a 2x16 LCD * * (1) LCD1 to GND * (2) LCD2 to VDD 5 volts * (3) LCD4 (RS) - LCD5 (RW) - LCD6 (E) to A1, A2, A3 * (4) LCD7-LCD14 to B0-B7 (bus data) * * Ref: ID=5879&page=1 */ void main(void) { int8 i; char LINE1[] = { "SGN Tech" }; char LINE2[] = { "Xin chao" }; lcd_init(); // use of lcd_display_char() for (i = 0; i < 8; i++) lcd_display_char(0, i, LINE1[i]); // use of lcd_display_str lcd_display_str(1, LINE2, 8); } +CCS C có một ví dụ hay hơn: Chỉ cần dùng 4 bits D4-D7 của LCD: Examples\EX_LCDKB.C 5.7. Ví dụ nhỏ về ngắt ngoài Thang8831 - 61 -
  62. CCS C for PIC16F877A 24/06/2008 Bình thường thì LED6 sáng, LED7 tối. Khi nhấn phím, LED6 tối, LED7 sáng trong vòng 0,5 giây, rồi trở về trạng thái ban đầu (LED6 sáng, LED7 tối) #include #fuses NOWDT, XT #use delay(clock=4000000) void high_b6_low_b7() { output_high(PIN_B6); output_low(PIN_B7); } void low_b6_high_b7() { output_low(PIN_B6); output_high(PIN_B7); } /////////////////////////////////////////////////////////// #INT_EXT void RB0_handler() { low_b6_high_b7(); delay_ms(500); high_b6_low_b7(); } /////////////////////////////////////////////////////////// / * Keep B6 on and B7 off. Pressing the button causes interrupt: * B6 off and B7 on, delay half second, then B6 on and B7 off * * Wiring (TM Board) * (1) PIC's B0 to Matrix Key R0 * Matrix Key C0 to GND * (2) PIC's B6-B7 to LED6-LED7 * * Ref: Interrupt Mechanism * sicbook/06.htm */ void main() { enable_interrupts(GLOBAL); // enable all interrupts enable_interrupts(INT_EXT); // enable external interrupt from pin RB0/INT high_b6_low_b7(); while (true) { // do nothing } } 5.8. Ngắt ngoài và đèn 7 đoạn Một phương án khác: #include #fuses NOWDT, XT Thang8831 - 62 -
  63. CCS C for PIC16F877A 24/06/2008 #fuses NOLVP // important #use delay(clock=4000000) // 0 1 2 3 4 5 6 7 8 9 byte const DIGITS[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f }; int8 i = 0; /////////////////////////////////////////////////////////// /* private */void off_on_led_transistor() { output_low(PIN_D1); delay_ms(1); output_high(PIN_D1); } /////////////////////////////////////////////////////////// /* private */void display(int8 digit) { output_c(DIGITS[digit] ^ 0xff); off_on_led_transistor(); } /////////////////////////////////////////////////////////// #INT_EXT void ngat_RB0() { i = (i < 9) ? i+1 : 1; delay_ms(200); // switch debounce period } /////////////////////////////////////////////////////////// / * Count number of key presses and display it on a 7-segment LED. * If the number is 9, the next count will be 1 * * Wiring (TM Board) * (1) PIC's B0 to R0 * Matrix Key C0 to GND * (2) PIC's C0-C6 to 7-segment LED's A-G * PIC's D1 to 7-segment LED's C2 */ void main() { enable_interrupts(GLOBAL); enable_interrupts(INT_EXT); while (true) display(i); } 5.9. Chương trình hiển thị phím số ra đèn 7 đoạn (không dùng interrupt) Code: /////////////////////////////////////////////////////////// /* private */void off_on_led_transistor() { output_low(PIN_D1); delay_ms(1); output_high(PIN_D1); } Thang8831 - 63 -
  64. CCS C for PIC16F877A 24/06/2008 /////////////////////////////////////////////////////////// void display(int8 digit) { output_c(DIGITS[digit] ^ 0xff); off_on_led_transistor(); } /////////////////////////////////////////////////////////// int8 char_to_digit(char c) { return c & 0b00001111; // first 4 bits only } /////////////////////////////////////////////////////////// int1 digit_key_pressed(char key) { byte pattern; pattern = 0b00110000; return (key & pattern) == pattern; } /////////////////////////////////////////////////////////// / * Echo digit-key presses (0 to 9) of a 4x3 keypad to the 7-segment LED * * Configuration: * Use PORTB for keypad by uncommenting the following line in PICC\Drivers\KBDD.c * #define use_portb_kbd TRUE * * Wiring: (TM Board) * (1) PIC's B1-B7 to Matrix Keypad's R3-R0&C2-C0 (notice the reverse order) * (2) PIC's C0-C6 to 7-segment LED's A-G * PIC's D1 to 7-segment LED's C2 */ void main() { int8 i, digit; char key; kbd_init(); while (true) { key = kbd_getc(); if (digit_key_pressed(key)) { digit = char_to_digit(key); for (i = 0; i #fuses NOWDT, XT #fuses NOLVP // important #use delay(clock=4000000) #include // in PICC\Drivers // 0 1 2 3 4 5 6 7 8 9 byte const DIGITS[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f }; Thang8831 - 64 -
  65. CCS C for PIC16F877A 24/06/2008 /////////////////////////////////////////////////////////// /* private */void off_on_led_transistor() { output_low(PIN_D1); delay_ms(1); output_high(PIN_D1); } /////////////////////////////////////////////////////////// void display(int8 digit) { output_c(DIGITS[digit] ^ 0xff); off_on_led_transistor(); } /////////////////////////////////////////////////////////// int8 char_to_digit(char c) { return c & 0b00001111; // first 4 bits only } /////////////////////////////////////////////////////////// int1 digit_key_pressed(char key) { byte pattern; pattern = 0b00110000; return (key & pattern) == pattern; } /////////////////////////////////////////////////////////// #INT_RB void RB_handler() { int8 i, digit; char key; key = kbd_getc(); if (digit_key_pressed(key)) { digit = char_to_digit(key); for (i = 0; i < 200; i++) // repeat the display for human eyes display(digit); } } /////////////////////////////////////////////////////////// / * Echo digit-key presses (0 to 9) of a 4x3 keypad to the 7-segment LED * * Configuration: * Use PORTB for keypad by uncommenting the following line in PICC\Drivers\KBDD.c * #define use_portb_kbd TRUE * * Wiring: (TM Board) * (1) PIC's B1-B7 to Matrix Keypad's R3-R0&C2-C0 (notice the reverse order) * (2) PIC's C0-C6 to 7-segment LED's A-G * PIC's D1 to 7-segment LED's C2 */ void main() { enable_interrupts(GLOBAL); enable_interrupts(INT_RB); Thang8831 - 65 -
  66. CCS C for PIC16F877A 24/06/2008 kbd_init(); while (true) { // do nothing } } Đoạn mã hiển thị led 7 thanh này, dấu ^ có ý nghĩa là vì hả các bác void display(int8 digit) { output_c(DIGITS[digit] ^ 0xff); off_on_led_transistor(); } Thân. Dấu "^" là ký hiệu phép toán XOR (exclusive OR) trong C. Phép toán && là phép and logic.Chắc là không phải giải thích về phép toán này. 5.11. Thay đổi tốc độ đèn led dung ngắt Code: // // Author : nhh // Date : 03/04/06 // Hardware: PIC16F877A // #include #fuses NOWDT,PUT,XT,NOPROTECT #use delay(clock=1000000) #use fast_io(b) #byte portb=0x06 #byte intcon=0x000B #bit RB4=portb.4 #bit RB5=portb.5 #bit RBIF=intcon.0 //dinh nghia co ngat RB #bit RBIE=intcon.3 //dinh nghia bit cho phep ngat RB // Chuong trinh ngat #int_RB void ngat_RB() { if((RBIF)&&(RBIE)) { //Kiem tra sw1 { if(RB4==0) { portb=0b00000001; delay_ms(200); portb=0b00001111; delay_ms(200); } Thang8831 - 66 -
  67. CCS C for PIC16F877A 24/06/2008 } //Kiem tra sw2 { if(RB5==0) { portb=0b00001000; //led1,2 sang } } RBIF=0; //Xoa co ngat RB } } // Chuong trinh chinh main() { set_tris_b(0b11110000); portb=0b11110000; enable_interrupts(global); enable_interrupts(int_RB); ext_int_edge(H_to_L); while(true) { } } Em ko dùng ngắt nhiều nên hỏi có vẽ ngu ngơ, xin thông cảm hai đoạn khai báo biến dưới đây để làm gì em không hiểu???? #byte portb=0x06 #byte intcon=0x000B Vì trong chương trình chính đã định ngõ vào ra của port B rồi, với lại cũng đã cho ngắt toàn cục rồi mà.Tại sao phải nhất thiêt là 0x06 và 0x000B mà ko là giá trị khác////Em thử gán giá trị khác thì ct chạy sai Giúp với Thang8831 - 67 -
  68. CCS C for PIC16F877A 24/06/2008 TL: Đây là khai báo địa chỉ của thanh ghi portB và thanh ghi intcon. Thường thì mỗi thanh ghi có một địa chỉ, giống như số nhà ấy, bạn khai báo nhầm địa chỉ của nó, nghĩa là bạn vào nhầm nhà rồi còn gì. Với 16F877A PORTB : địa chỉ 06h INTCON : địa chỉ 0Bh Vì trong file 16F877A.h của CCS C không có khai báo tường tận như trong các file .inc của Microchip, muốn sử dụng cho tiện thì khai báo thêm vào. Bạn mở file .h của con pic đang làm việc ra xem người ta đã khai báo những gì rồi. +Sau khi vào trang Web của CCSC để tìm hiểu về vấn đề này mình lĩnh hội được một vài điều như sau: - Thứ nhất, một thiết kế sẽ là không tối ưu nếu trong CTC ngắt lại gọi đến một hàm khác đã được sử dụng trong Main hay hàm khác, đặc biệt là khi bản thân hàm này lại gọi đến hàm khác nữa (những hàm chứa trong các header thường gọi lẫn nhau như vậy). - Thứ hai nếu sử dụng hàm như nêu trên thì chương trình chạy sẽ không theo đúng ý đồ lập trình (chẳng hạn như trong chương trình mà mình vừa Pót). Điều này xảy ra là do bộ nhớ Stack bị tràn (stack của PIC chỉ có 8 mức) khi gọi hàm chồng chéo (cả hàm Main và CTC ngắt đều gọi ). - Thứ ba bên CCSC khuyên nếu buộc phải gọi hàm như vậy (chẳng hạn như hàm delay_ms như trên) thì hãy khai báo ở cà hàm Main và hàm ngắt ???. TL Điều này thì mình không rõ vì đã thử nhưng không có hiệu quả. Vấn đề của bạn như có thể giải thích một cách dễ hiểu như sau: Nếu cả 2 hàm đều được gọi trong cả hàm ngắt và hàm main thì sẽ phát sinh ra lỗi. Tại sao lại thế. Các bạn thử suy nghĩ mà xem. khi hàm main đang chạy đến hàm mà nó và hàm ngắt cùng gọi. Nếu không có ngắt xẩy ra cùng thời điểm đó thì không có vấn đề gì cả. nhưng nếu có ngắt thì nó phải lưu các thông số hiện tại và nhảy vào ngắt, và khi nó nhảy ra khỏi ngắt thì các dữ liệu đã lưu sẽ bị chồng lên trong khi thực hiện hàm đó trong ngắt. vậy chương trình sẽ không đúng nữa. Trong Keil C của 89 thì nó chỉ là warning nhưng trong css c thì nó là error. và theo quan điểm của tôi nó phải là một error. Nếu bạn muốn dùng 2 cái delay_ms() trong cả ngắt và main thì mình nghĩ không có cách nào đâu. nếu bạn cứ muốn dùng nó thì hãy tạo ra 2 hàm delay_ms1() và delay_ms2() và trong ngắt gọi một hàm và trong main gọi hàm còn lại. Còn có một cách nữa mình nghĩ nó sẽ pro hơn đấy là. Mình xin viết lại một đoạn chương trình của bạn trong: bạn khai báo một biến toàn cục như thế này nhé. //=== int1 bit_timer0_status; #INT_TIMER1 void lapngat() { bit_timer0_status =1; } //=== // và đưa công việc của bạn muốn thực hiện khi có ngắt vào đây. //=== void isr_timer0(void) { count++; if (count==200) Thang8831 - 68 -
  69. CCS C for PIC16F877A 24/06/2008 while(true) { output_high(PIN_C1); delay_ms(1000); if(!input(PIN_B3)) break; output_low(PIN_C1); delay_ms(1000); } //=== //và trong hàm main bạn phải làm thế này //=== void main(void) { if(bit_timer0_status) { //có thể cấm ngắt timer0. //gọi hàm thực hiện công việc. isr_timer0(); bit_timer0_status=0; //bật ngắt trở lại } } Giải pháp của bạn rất hay, và thực ra sau khi mình tìm hiểu điều này trên chuyên trang của CCSC mình cũng có ý tưởng na ná như vậy. Nhưng nó vẫn có những hạn chế nhất định. Mình sẽ phân tích cho bạn như sau: Giả sử chương trình viết theo cách của bạn, khi nó kiểm tra đến lệnh 'if(bit_timer0_status)' mà chưa xảy ra ngắt thì nó sẽ không thực hiện hàm 'isr_timer0()', qua đó rồi ngắt mới xảy ra thì chương trình lại không test 'if(bit_timer0_status)' và không thực hiện hàm 'isr_timer0()', tức là không có ngắt. Còn nếu trong chương trình mà luôn kiểm tra 'if(bit_timer0_status)' thì đang từ việc sử dụng phương pháp ngắt lại thành ra phương pháp thăm dò ??? Bạn chưa đọc kỹ chương trình của mình thì phải. Sẽ không có trường hợp ngắt xẩy ra mà không thực hiện công việc mà bạn mong muốn. Có chăng thì nó chậm hơn so với khi để công việc trong ngắt một chút. thời gian này không đáng kể chỉ khoảng vài 2ms. cái này không ảnh hưởng gì phải không? Tại sao mình lại bảo không có chuyện như bạn nói: Trong chương trình phục vụ ngắt mình đã sử dụng một bit 'bit_Timer0_Status', bit này có mục đích khi có ngắt thì bật nó lên để báo cho hàm main biết đã có ngắt xẩy ra. và kể cả khi hàm main đang làm những gì, ở đâu thì khi gặp lệnh if(bit_Timer0_Status) thì nó thực hiện công việc mong muốn. và khi thực hiện công việc này xong phải xóa bit này để dùng cho lần sau. Như mình đã nói nếu viết theo cách của bạn thì gần như chắc chắn sẽ không thực hiện được hàm ngắt theo mong muốn. Đã đành khi timer1 báo cho(bit_Timer0_Status) =1 (timer đã ngắt), nhưng có thể nói chắc chắn rằng khi chương trình đang kiểm tra 'if(bit_timer0_status)' thì timer chưa ngắt vì nó kiểm tra ngay bắt đầu hàm main. Sau khi đã kiểm tra như vậy rồi thì chương trình có quay lại để tiếp tục kiểm tra đâu mà gọi hàm 'ngắt phụ' như bạn viêt. Nếu bạn không tin thì hãy lập thử một chương trình đơn giản sử dụng ngắt theo cách bạn viết và mô phỏng thử trên Proteus. (việc này đơn giản và không tốn nhiều thời gian,chắc không cần nói thêm!) Tiện đây mình nói với bạn về các phương pháp trao đổi dữ liệu nói chung. Có 3 phương Thang8831 - 69 -
  70. CCS C for PIC16F877A 24/06/2008 pháp chủ yếu là pp thăm dò (polling), pp ngắt (interrupt) và pp trao đổi trực tiếp (pp thú ba mình không nhớ rõ lắm). Làm như cách của bạn chính là chuyển từ pp ngắt sang pp thăm dò. Hóa ra bạn bảo mình là không cho cái if của mình vào trong cái while(true) của bạn. Thật ra cái đấy mình chỉ viết thí dụ thôi. Còn nếu bạn muốn sử dụng thì bạn phải tự làm thêm. Vì mình nghĩ trong một firmware cho vi điều khiển không có cái nào không có lệnh while(true) {} cả. Còn nếu đọc đến đây mà bạn vẫn khăng khăng nó không thực hiện được ngắt thì mình cũng bó tay rồi. Nếu cần mình sẽ cho bạn xem một chương trình của mình, mình đã viết và thành phẩm. Phải nói nó chạy phe phé. Và cái thứ 2 mình muốn nói thêm. Mình chỉ đưa ra những phương án như trên cho bạn, để bạn tham khảo thôi chứ không phải hay ho gì. nên bạn không cần thiết phải so sánh nó với 3 cái mà bạn nói. Nói thật mình chả hiểu đếch gì về mấy cái đấy cả hê hê. +Xin hỏi tại sao chương trình dùng ngắt timer0 em làm giống nhu hướng dẫn mà nó không chịu chạy? #include #fuses NOWDT,PUT,HS,NOPROTECT #use delay(clock=20000000) #use fast_io(b) #byte portb=0x6 #define led pin_B0 int8 a; void ngat() { delay_ms(1000); a=a #fuses NOWDT,PUT,XT,NOPROTECT #use delay(clock=20000000) . Mình đã sửa lại code của nhh như vậy để phù hợp với PIC18F4550, nhưng không hiểu sao vẫn không chạy đơợc. Bạn có thể hướng dẫn kỹ hơn về Interrup và Timer không, cảm on bạn nhiều. TL: Úi code của bạn sai rồi nếu muốn sử dụng được ngắt thì trong hàm main bạn phải thực hiện cho phép ngắt timer_0 và ngắt toàn cục (global) hoạt động, định xung nhịp cho timer, và hàm ngắt() phải được đặt ngay bên dưới chỉ thị #int_timer0 để trình dịch có thể hiểu được đây là hàm phục vụ ngắt, hàm ngắt() này bạn không thể gọi đến giống như một hàm thông thường đuợc mà nó chỉ được máy gọi đến khi có xuất hiện cờ tràn timer_0, mà khi đã sử dụng ngắt Thang8831 - 70 -
  71. CCS C for PIC16F877A 24/06/2008 timer (tức ngắt định thời) thì bạn không nên dùng delay trong nó (rất dở),vì ngắt timer được tạo ra để thay thế hoàn hảo cho delay, code của bạn mình sửa như sau : #include #fuses NOWDT,PUT,HS,NOPROTECT #use delay(clock=4000000) int16 count=0; int8 a=1; #int_timer0 void ngat(){ ++count; if(count==2000) {count=0;a=a #fuses HS,NOWDT,NOPROTECT,NOLVP #use delay(clock=4000000) int i=0; void main() { setup_timer_0(rtcc_div_32); set_timer0(10); ????nếu đặt 1 thì bao lâu mới ngắt enable_interrupts(int_rtcc); enable_interrupts(global); while(true) { } } #int_rtcc void ngat_timer() { i++; output_D(i); } Thang8831 - 71 -
  72. CCS C for PIC16F877A 24/06/2008 Cấu hỏi thứ 2 là khi giao tiếp máy tính mình truyền từ PC đến pic: dùng lệnh getc(); thì chỉ thu được 1 kí tự ví dụ truyền số 12 thì thu được 2 số 1 và 2 có bạn nào biết lệnh nào để lấy 1 chuỗi ko? TL: Về timer 0 và hàm set_timer0(), bạn đọc thêm tài liệu hướng dẫn của CCS C (trang 206, ver. 4, 01/2007) và datasheet của chip. Về getc(), nếu bạn dùng nó thì tất nhiên chỉ lấy được 1 ký tự là nó đã trở về. Bạn đọc thêm về hàm gets() trong tài liệu hướng dẫn của CCS C (đã nêu trên, trang 148) để đọc 1 chuỗi ký tự. +Thắc mắc ngắt timer Em viết chương trình ngắt timer nhấp nháy led, nhưng nạp vào pic phải đợi 1 lúc sau nó mới bắt đầu nháy led, bác nào khắc phục jùm em với. Đây là code: #include #include #device 16F877*=16 ADC=10 #FUSES NOWDT, HS, NOPUT, NOPROTECT, NODEBUG, NOBROWNOUT, NOLVP, NOCPD, NOWRT #use delay(clock=4000000) #use fast_io(b) #use fast_io(c) #use fast_io(d) #int_timer0 void interrupt_timer0(){ int1 a; int16 count; set_timer0(56); ++count; if(count == 500) { count=0; a=~a; RB0=a; } } void main() { set_tris_b(0); setup_timer_0(RTCC_INTERNAL|RTCC_DIV_2); enable_interrupts(int_timer0); enable_interrupts(global); set_timer0(56); while(true) { } } TL: Trước khi vào vòng while(true) trong main(), bạn nên đặt trước giá trị của count, nếu không thì count có thể mang giá trị bất kỳ, và điều kiện (count == 500) của bạn có thể phải sau khi count được tăng vài chục ngàn giá trị mới thỏa mãn (nếu tình cờ sau khi PIC reset biến count mang giá trị ngẫu nhiên là 501 chẳng hạn). Tôi thường khởi tạo biến count ngay trước khi vào vòng while(true) trong main(), và dùng điều kiện (count >= 500) thay cho (count == 500) trong phần xử lý ngắt. Thang8831 - 72 -
  73. CCS C for PIC16F877A 24/06/2008 6. Chương trình ví dụ sau mô tả cách dùng PWM do CCS cung cấp. PWM là gì? sử dụng nó vào mục đích gì? 1) PWM là gì? PWM là một bộ điều chế độ rộng xung. Có hai thông số (tạm gọi đơn giản như vậy, và có lẽ cũng chỉ quan tâm đến hai thông số này với PWM) quan trọng của PWM là chu kỳ xung T và thời gian t1 của mức logic 0, trong ví dụ này thì t1 tương ứng với value. Để "điều chế độ rộng xung" thì chúng ta sẽ giữ nguyên T và thay đổi t1, theo yêu cầu của bài toán cụ thể. Value trong ví dụ sau lấy được từ đầu vào anlaog, chu kỳ (hay tần số) của xung được chọn lựa từ PC thông qua cổng truyền thông nối tiếp RS232. 2) PWM dùng vào mục đích gì? Có nhiều ứng dụng cho nó, ví dụ truyền thông, điều khiển các van bán dẫn trong các biến tần, làm bộ nguồn chuyển mạch, ôi nhiều lắm! Bắt đầu viết nhé: #if defined(__PCM__) #include #fuses HS,NOWDT,NOPROTECT,NOLVP #use delay(clock=4000000) #use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, BRGH1OK) #elif defined(__PCH__) #include #fuses HS,NOWDT,NOPROTECT,NOLVP #use delay(clock=10000000) #use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, BRGH1OK) #endif void main() { char selection=1; int8 value; printf("\r\nFrequency:\r\n"); printf(" 1) 7.8 khz\r\n"); printf(" 2) 19.5 khz\r\n"); printf(" 3) 0.48 khz\r\n"); do { selection=getc(); }while((selection '3')); setup_ccp1(CCP_PWM); // Configure CCP1 as a PWM // The cycle time will be (1/clock)*4*t2div*(period+1) // In this program clock=4000000 and period=127 (below) // For the three possible selections the cycle time is: // (1/4000000)*4*1*128 = 12.8 us or 7.8 khz // (1/4000000)*4*4*128 = 51.2 us or 19.5 khz // (1/4000000)*4*16*128= 204.8 us or 0.48 khz switch(selection) { Thang8831 - 73 -
  74. CCS C for PIC16F877A 24/06/2008 case '1' : setup_timer_2(T2_DIV_BY_1, 127, 1); break; case '2' : setup_timer_2(T2_DIV_BY_4, 127, 1); break; case '3' : setup_timer_2(T2_DIV_BY_16, 127, 1); break; } setup_port_a(ALL_ANALOG); setup_adc(adc_clock_internal); set_adc_channel( 0 ); printf("%c\r\n",selection); while( TRUE ) { value=read_adc(); //value++; printf("%2X\r",value); set_pwm1_duty(value); //value may be an 8 or 16 bit constant or variable // This sets the time the pulse is // high each cycle. We use the A/D // input to make a easy demo. // the high time will be: // if value is LONG INT: // value*(1/clock)*t2div // if value is INT: // value*4*(1/clock)*t2div // for example a value of 30 and t2div // of 1 the high time is 30us // WARNING: A value too high or low will // prevent the output from // changing. } } + Yêu cầu của e là điều chỉnh độ rộng xung để thay đổi vận tốc của động cơ (trong để tài của e là động cơ của bơm cánh dẫn) Cảm ơn các trưởng lão namqn! Rất mong nhận được sự giúp đỡ của bạn! Với động cơ thì tần số điều chế thường quanh quẩn 20 kHz. Bây giờ bạn cho biết bạn dùng loại bộ điều khiển nào để điều chỉnh vận tốc của động cơ, bạn có đo vận tốc chứ? Từ ngõ ra của bộ điều chế độ rộng xung bạn làm thế nào để điều chỉnh vận tốc của động cơ? Động cơ đó có công suất khoảng bao nhiêu? Thang8831 - 74 -
  75. CCS C for PIC16F877A 24/06/2008 Thang8831 - 75 -
  76. CCS C for PIC16F877A 24/06/2008 7. Tìm hiểu về LCD LCD được tìm hiểu ở đây là HD44780 của hãng Hitachi, gồm 2 dòng, mỗi dòng 16 kí tự. Thang8831 - 76 -
  77. CCS C for PIC16F877A 24/06/2008 HD44780 có 14 chân, chức năng của các chân: 1.Các chân VCC, VSS và VEE: Chân VCC_Cấp dương nguồn 5V, chân VCC_Nối đất, chân VEE được dùng để điều khiển độ tương phản của màn hình LCD. 2.Chân chọn thanh ghi RS (Register Select): Có hai thanh ghi rất quan trọng bên trong LCD, chân RS được dùng để chọn các thanh ghi này như sau: Nếu RS = 0 thì thanh ghi mà lệnh được chọn để cho phép người dùng gửi một lệnh chẳng hạn như xoá màn hình, đưa con trỏ về đầu dòng, Nếu RS = 1 thì thanh ghi dữ liệu được chọn cho phép người dùng gửi dữ liệu cần hiển thị trên LCD. 3.Chân đọc/ghi R/W: Đầu vào đọc/ghi cho phép người dùng ghi thông tin lên LCD khi R/W = 0 hoặc đọc thông tin từ nó khi R/W = 1. 4.Chân cho phép E (Enable): Chân cho phép E được sử dụng bởi LCD để chốt thông tin hiện hữu trên chân dữ liệu của nó. Khi dữ liệu được cấp đến chân dữ liệu thì một xung mức cao xuống thấp phải được áp đến chân này để LCD chốt dữ liệu trên các chân dữ liêu. Xung này phải rộng tối thiểu là 450ns. 5.Các chân D0 - D7: Đây là 8 chân dữ liệu 8 bít, được dùng để gửi thông tin lên LCD hoặc đọc nội dung của các thanh ghi trong LCD. Để hiển thị các chữ cái và các con số, chúng ta gửi các mã ASCII của các chữ cái từ A đến Z, a đến f và các con số từ 0 - 9, đến các chân này khi bật RS = 1. Cũng có các mã lệnh mà có thể được gửi đến LCD để xoá màn hình hoặc đưa con trỏ về đầu dòng hoặc nhấp nháy con trỏ.Dưới đây là bảng liệt kê các mã lệnh: (Phải qua lần post khác vì số ảnh vượt quá 4 ) Tìm hiểu về LCD (ct) Chúng ta cũng sử dụng RS = 0 để kiểm tra bít cờ bận để xem LCD có sẵn sàng nhận thông tin chưa. Cờ bận là D7 và có thể được đọc khi R/W = 1 và RS = 0 như sau: Nếu R/W = 1, RS = 0 khi D7 = 1 (cờ bận 1) thì LCD bận bởi các công việc bên trong và sẽ không nhận bất kỳ thông tin mới nào. Khi D7 = 0 thì LCD sẵn sàng nhận thông tin mới. Lưu ý chúng ta nên kiểm tra cờ bận trước khi ghi bất kỳ dữ liệu nào lên LCD. Có thể di chuyển con trỏ đến vị trí bất kì trên màn hình LCD bằng cách nạp vào các giá trị tương ứng như bảng sau và gởi yêu cầu đến LCD: Thang8831 - 77 -