development_ kit 16f877a

32
NG DNG BKIT 16F877A 1. CHC NĂNG CƠ BN CA BKIT: 1.1.Khi xlý trung tâm : PIC 16F877A: - Giao tiếp vi 8255 và LCD thông qua PORT D - Cho phép LED7x4 qua ALED (3:0) ( RB4:RB1) - RA2:RA0 sdng làm chân ADC - Kết ni vi bđếm thi gian thc DS1307 và bnhngoài 24C64 - Ngoài ra còn có cng np, điu khin động cơ, giao tiếp RS232 và RS485 8255: - Điu khin giá trhin thlên LED7x4 qua PORT B - Kết ni vi keypad 4x4 qua PORT C

Upload: dao-nguyen-huu

Post on 03-Oct-2014

555 views

Category:

Documents


4 download

TRANSCRIPT

Page 1: Development_ KIT 16F877A

ỨNG DỤNG BỘ KIT 16F877A

1. CHỨC NĂNG CƠ BẢN CỦA BỘ KIT:

1.1.Khối xử lý trung tâm :

PIC 16F877A:

- Giao tiếp với 8255 và LCD thông qua PORT D - Cho phép LED7x4 qua ALED (3:0) ( RB4:RB1) - RA2:RA0 sử dụng làm chân ADC - Kết nối với bộ đếm thời gian thực DS1307 và bộ nhớ ngoài 24C64 - Ngoài ra còn có cổng nạp, điều khiển động cơ, giao tiếp RS232 và RS485 8255: - Điều khiển giá trị hiển thị lên LED7x4 qua PORT B - Kết nối với keypad 4x4 qua PORT C

Page 2: Development_ KIT 16F877A

1.2. Khối giao tiếp USB : Sử dụng PIC 18F4550 có tích hợp sẵn giao tiếp USB Flash 2.0

2. Giao tiếp qua 8255:

2.1.Giới thi ệu về 8255 : - Có 3 cổng ( 8 bit ) truy cập riêng biệt : A, B và C đều có thể được lập trình là đầu ra hay đầu vào - Riêng Port C có khả năng lập trình theo nửa byte cao và nửa byte thấy hoặc lập trình trên từng bit - Port D đùng để chuyển dữ liệu giữa 8255 và PIC - RD và WR là 2 tín hiệu điều khiển tích cực thấp - RESET được tích cực mức cao, dùng để xóa thanh ghi điều khiển - A0, A1 lựa chọn cổng riêng biệt, CS tích cực mức thấp cho phép 8255 làm việc

CS A1 A0 Chọn cổng 0 0 0 Cổng A 0 0 1 Cổng B 0 1 0 Cổng C 0 1 1 Thanh ghi điều khiển ( W ) 1 x X 8255 ko được chọn

Page 3: Development_ KIT 16F877A

2.2.Chọn chế độ 8255 : - Mode 0 : đây là chế độ vào ra đơn giản, các cổng A,B,C có thể được lập trình như đầu vào hoặc

đầu ra, không điều khiển theo từng bit ( đây là chế độ phổ biến nhất và được đề cập đến trong phần này )

- Mode 1 : các cổng A,B có thể được lập trình như đầu vào hoặc đầu ra với khả năng bắt tay, tín hiệu bắt tay được cấp bởi các bit cổng C

- Mode 2 : cổng A được sử dụng như cổng vào ra 2 chiều với khả năng bắt tay và tín hiệu bắt tay cấp bởi các bit cổng C. Cổng B được dùng như chế độ vào ra đơn giản hoặc chế độ bắt tay mode 1

- BSR : các bit riêng rẽ cổng C có thể lập trình Lập trình chế độ vào ra đơn giản : - Chế độ này do thanh ghi điều khiển quyết định

D7 D6 – D5 D4 D3 D2 D1 D0 1 = I/O mode 0 = BSR

00 = mode 0 01 = mode 1 10 = mode 2

Port A 1 = input 0 = output

Port CH 1 = input 0 = output

0 = mode 0

Port B 1 = input 0 = output

Port CL 1 = input 0 = output

- Dựa vào bảng trên, ta có thể chọn chế độ vào ra mong muốn Chức năng và ghép nối 8255 với PIC 16F877A : - Chức năng : hiển thị LED thông qua port B, kết nối với keypad 4x4 thông qua port C - Ghép nối với PIC 16F877A : PORT D của PIC truyền nhận dữ liệu với 8255 qua PORT của 8255 RE2 nối với chân CS của 8255 có nhiệm vụ cho phép 8255 làm việc RE1 nối với WR và RE0 nối với RD RA4 nối với A0, RA5 nối với A1 có nhiệm vụ chọn port

//8255 Procedure void ppiInit(void); void switch_port(char ch); void ppiWrite(unsigned char data, unsigned char ch); unsigned char ppiRead(unsigned char ch); void ppiInit(void) // khoi tao che do cua 8255 { ppiWrite(0x89,'W'); // PA, PB are Outputs, PC is Inputs } void switch_port(char ch) // chon port { switch(ch) { case 'A': // PortA A0 = 0; A1 = 0; break; case 'B': //PortB A0 = 1; A1 = 0; break;

Page 4: Development_ KIT 16F877A

case 'C': // PortC A0 = 0; A1 = 1; break; case 'W': // control register A0 = 1; A1 = 1; break; } } void ppiWrite(unsigned char data, char ch) // ghi du lieu { TRISD = 0; // port D cua PIC lam output RD = 1; // khong cho phep read enLCD = 0; ppiEn = 0; // cho phep 8255 lam viec switch_port(ch); // chon port PORTD = data; // ghi data vao port D Wr = 0; // cho phep write Wr = 1; // khong cho phep write en8255 = 1; // khong cho phep 8255 lam viec TRISD = 0xff; // port D lam input } unsigned char ppiRead(char ch) // doc du lieu { unsigned char data;

TRISD = 0xff; // port D la input WR = 1; enLCD = 0; ppiEn = 0; switch_port(ch); RD = 0; RD = 1; data = PORTD; // luu gia tri doc duoc vao port D ppiEn = 1; return data; }

Page 5: Development_ KIT 16F877A

3. Một số chương trình ví dụ :

3.1.Điều khiển LED Các linh kiện sử dụng : PIC 16F877A, 8255, LED7x4( LS401 ) Giới thiệu về LED7x4

ALED 0…3 cho phép 4 LED hoạt động, tích cực mức thấp PB 0…7 điều khiển 7 thanh trong bộ LED

Yêu cầu : Hiển thị hàng nghìn, hàng trăm, hàng chục và hàng đơn vị : 1. Yêu cầu ghi dữ liệu vào 8255, từ PORT B của 8255, điểu khiển LED hiển thị hàng nghìn,

trăm, chục, đơn vị. 2. Các hàm con cần thiết :

unsigned char data, buffer; //Chuong trinh chuyen doi sang ma led 7 thanh unsigned char convert_led7s(unsigned char val) { unsigned char buffer; switch(val) { case 0: buffer = 0b11101011; break; //hien thi so 0 case 1: buffer = 0b00101000; break; //hien thi so 1 case 2: buffer = 0b10110011; break; //hien thi so 2 case 3: buffer = 0b10111010; break; //hie n thi so 3 case 4: buffer = 0b01111000; break; //hien thi so 4 case 5: buffer = 0b11011010; break; //hie n thi so 5 case 6: buffer = 0b11011011; break; //hien thi so 6 case 7: buffer = 0b10101000; break; //hie n thi so 7 case 8: buffer = 0b11111011; break; //hien thi so 8 case 9: buffer = 0b11111010; break; //hien thi so 9

Page 6: Development_ KIT 16F877A

case '#':buffer= 0b00000000; break; //khon g hien thi default: buffer = 0b00000001; } return buffer; } //chuong trinh hien thi gia tri hang nghin, hang tr am, hang chuc và don vi

len cac LED void dislay (unsigned int data) {

unsigned char nghin,tram,chuc,dv; unsigned char buffer;

//tach cac thanh phan cua data

nghin = (unsigned char)(data/1000); tram = (unsigned char)((data%1000)/100); chuc = (unsigned char)(((data%1000)%100)/10); dv = (unsigned char)(data%10); // quet led for (int index = 0; index < 4; index++ ) {

switch ( index ) {

case 0 : { ALED0 = ALED1= ALED2 = 1; ALED3 = 0; buffer = convert_led7s(dv); ppiWrite(buffer,’B’);

break; } case 1 : { ALED1 = ALED0= ALED3 = 1; ALED2 = 0; buffer = convert_led7s(chuc); ppiWrite(buffer,’B’); break; } case 2 : { ALED0 = ALED3= ALED2 = 1; ALED1 = 0; buffer = convert_led7s(tram); ppiWrite(buffer,’B’); break; } case 3 : { ALED3 = ALED1= ALED2 = 1; ALED0 = 0; buffer = convert_led7s(nghin); ppiWrite(buffer,’B’); break; }

} }

}

Page 7: Development_ KIT 16F877A

3. Ví dụ minh họa : #include <16F877a.h> #include <def_877a.h> #include <Kit16F877a.h> void main() {

unsigned int i=0; TRISD=0; PORTD=0; while(1)

{ display i; i++; if(i==100) i=0;

} }

Chương trình trên hiện thị lên LED từ 0 đến 100 rồi quay lại về 0

3.2.Keypad 4x4 Các linh kiện sử dụng : PIC 16F877A, 8255, keypad 4x4 Giới thiệu về Keypad 4x4 Keypad 4x4 gồm 16 nút ấn được bố trí dạng ma trận 4x4. Mô hình keypad được thể hiện như sau:

Hoạt động : giả sử nút ‘2’ được ấn, đường 2 và C được nối với nhau. Xác định nút được ấn bằng phương pháp quét keypad : Quét hàng 1 : A = 0, B = C = D = 1, kiểm tra chân 1, 2, 3, 4 thu được kết quả : 1 = 2 = 3 = 4 = 1 Quét hàng 2 : B = 0, C = D = A = 1, kiểm tra chân 1, 2, 3, 4 thu được kết quả : 1 = 2 = 3 = 4 = 1 Quét hàng 3 : C = 0, D = A = B = 1, kiểm tra chân 1, 2, 3, 4 thu được kết quả : 1 = 3 = 4 = 1, 2 = 0 Quét hàng 4 : D = 0, A = B = C = 1, kiểm tra chân 1, 2, 3, 4 thu được kết quả : 1 = 2 = 3 = 4 = 1 Các trường hợp nhiều nút được nhấn cùng lúc, ta cho ra cùng 1 kí tự (không chấp nhận kết quả phím bấm) Hoạt động : giá trị phím bấm sẽ được ghi vào PORT C của 8255, kết quả được đưa về PIC 16F877A để xử lý hoặc hiển thị lên LCD qua PORT D của 8255

unsigned char colScan(unsigned char row); unsigned char keyFilter(unsigned char num, unsigned char channel); unsigned char keyScan(void); unsigned char freeHand(void);

Page 8: Development_ KIT 16F877A

unsigned char colScan(unsigned char row) //quet co t cua 1 hang { unsigned char buffer,buff,i = 255; switch(row) //chon hang { case 0: ppiWrite(0xE0,'C'); break; case 1: ppiWrite(0xD0,'C'); break; case 2: ppiWrite(0xB0,'C'); break; case 3: ppiWrite(0x70,'C'); } buffer = ppiRead('C')&0x0F; //lay gia tri cot

//kiem tra hien tuong nhieu phim an lien tuc if(buffer!=0x0F) //neu co phim an { while(i--){} buff = ppiRead('C')&0x0F; if(buffer!=buff) buffer = 0xEE;//kiem tra n hieu phim an lien tuc } else buffer = 0xFF; // khong phim nao duoc a n return buffer; } unsigned char keyFilter(unsigned char num, unsigned char channel)// loc phim { unsigned char buffer; switch(num) { case 14: buffer = 1; break; case 13: buffer = 2; break; case 11: buffer = 3; break; case 7 : buffer = 4; break; default: buffer = 0xEE; } if(buffer!=0xEE) { buffer = buffer+channel*3; } return buffer; // gia tri that cua phim nhan } unsigned char keyScan(void //quet phim { unsigned char buffer, buff, i, n; i = 0; while(i<4) { buffer = colScan(i); if(buffer!=0xff) { n = i; i = 5; } else i++; }

Page 9: Development_ KIT 16F877A

if(i==5) // khi co phim an { // tiep tuc quet cac phim khac i = n+1; while(i<4) { buff = colScan(i); if(buff!=0xff) //neu co phim khac duoc an { i = 5; //thoat vong lap buffer = 0xee; } else i++; } } //luu lai gia tri phim an neu chi co 1 phim duoc a n

if((buffer!=0xff)&&(buffer!=0xee)) buffer = ke yFilter(buffer,n); return buffer; } unsigned char freeHand(void) { ppiWrite(0x00,'C'); buffer = ppiRead('C')&0x0F; if(buffer==0x0F) return 1; //tra gia tri 1 neu khong co phim an else return 0; //tra gia tri 0 neu co phim an }

Ví dụ minh họa :

#include <16F877a.h> #include <def_877a.h> #include <Kit16F877a.h> void main() { unsigned char keybuff, buffer; TRISD=0;

PORTD=0; while(1) { keybuff = keyScan(); while(keybuff==255) keybuff = keyScan(); // cho den khi co

phim an buffer = convert_led7s(keybuff); //chuyen sang dang BCD ppiWrite(buffer,’B’); //ghi ra PORT B cua 8255

while(freeHand()==0){} //cho den khi nha phi m }

}

Page 10: Development_ KIT 16F877A

3.3.Sử dụng ngắt thời gian :

3.3.1. Sử dụng Timer 0:

Các thanh ghi liên quan đến Timer0 bao gồm: OPTION_REG(81h, 181h) : điều khiển hoạt động của Timer0

- bit 5 TOCS lựa chọn nguồn clock

1=Clock ngoài từ chân T0CKI 0=Clock trong Focs/4

- bit 4 T0SE bit lựa chọn sườn xung clock 1=Timer 0 tăng khi chân T0CKI tử cao xuống thấp(s−ờn xuống) 0=Timer 0 tăng khi chân T0CKI tử thấp lên cao(s−ờn xuống)

- bit 3 PSA bit gán bộ chia xung đầu vào 1=gán bộ chia Prescaler cho WDT 0=gán bộ chia Prescaler cho Timer 0

- bit 2:0 PS2:PS1 lựa chọn hệ số chia hệ số xung theo bảng sau: PS2:PS0

Timer0

WDT

000 1:2 1:1 001 1:4 1:2 010 1:8 1:4 011 1:16 1:8 100 1:32 1:16 101 1:64 1:32 110 1:128 1:64 111 1:256 1:128

TMR0(01h, 101h) : chứa giá trị của bộ định thời Timer0 INTCON(0Bh, 8Bh, 10Bh, 18Bh): cho phép ngắt hoạt động Thanh ghi chứa các bit điều khiển và các bít cờ hiệu khi timer0 bị tràn, ngắt ngoại vi RB0/INT và

ngắt interrupt_on_change tại các chân của PORTB.

- Bit 7 GIE Global Interrupt Enable bit

GIE = 1 cho phép tất cả các ngắt. GIE = 0 không cho phép tất cả các ngắt.

- Bit 6 PEIE Pheripheral Interrupt Enable bit PEIE = 1 cho phép tất cả các ngắt ngoại vi. PEIE = 0 không cho phép tất cả các ngắt ngoại vi.

- Bit 5 TMR0IE Timer0 Overflow Interrupt Enable bit TMR0IE = 1 cho phép ngắt Timer0. TMR0IE = 0 không cho phép ngắt Timer0.

- Bit 4 RBIE RB0/INT External Interrupt Enable bit

Page 11: Development_ KIT 16F877A

RBIE = 1 cho phép tất cả các ngắt ngoại vi RB0/INT RBIE = 0 không cho phép tất cả các ngắt ngoại vi RB0/INT

- Bit 3 RBIE RB Port change Interrupt Enable bit RBIE = 1 cho phép ngắt RB Port change RBIE = 0 không cho phép ngắt RB Port change

- Bit 2 TMR0IF Timer0 Interrupt Flag bit TMR0IF = 1 thanh ghi TMR0 bị tràn (phải xóa cờ hiệu bằng chương trình). TMR0IF = 0 thanh ghi TMR0 chưa bị tràn.

- Bit 1 INTF BR0/INT External Interrupt Flag bit INTF = 1 ngắt RB0/INT xảy ra (phải xóa cờ hiệu bằng chương trình). INTF = 0 ngaột RB0/INT chửa xay ra.

- Bit 0 RBIF RB Port Change Interrupt Flag bit RBIF = 1 ít nhất có một chân RB7:RB4 có sự thay đổi trạng thái. Bít này phải được xóa bằng chương trình sau khi đã kiểm tra lại các giá trị chân tại PORTB. RBIF = 0 không có sự thay đổi trạng thái các chân RB7:RB4.

Thiết lập giá trị ban đầu cho Timer 0 : void Timer0_init() { TMR0=0; //dat gia tri bat dau dem cho timer 0 TMR0IE=1; //cho phep ngat timer 0 hoat dong TMR0IF=0; //xoa interrupt flag timer 0 OPTION=0x01; //chon che do cho timer 0 GIE=1; //cho phep tat ca cac ngat hoat dong PEIE=1; //cho phep ngat ngoai vi }

Thực hiện công việc khi xảy ra ngắt Timer 0: void interrupt isr() { if(TMR0IF && TMR0IE) { TMR0IF=0; TMR0=0; i++; if(i==500)

{ i=0; RD7=!RD7

} } } void main() { Timer0_init(); while(1); }

Page 12: Development_ KIT 16F877A

Sau mỗi nhịp của vi điều khiển, TMR0 sẽ tăng lên 1. Giá trị ban đầu của TMR0 = 0 nên sau 256 nhịp, TMR0IF sẽ được set ( xảy ra hiện tượng tràn), đồng thời i được +1. Khi i = 100, led tại chân RD7 sẽ thay đổi trạng thái 1 lần. Vậy led sẽ sáng, tối liên tục với chu kì : 256*0.2*4*500=0.1024(s)

( ta chọn giá trị PS2:PS0=001 >>> prescaler = 1:4 nên phải nhân 4 trong công thức trên, chọn thạch anh có tần số 20MHz nên thời gian cho 1 nhịp vi điều khiển = 4/20 = 0.2 us )

3.3.2. Sử dụng Timer 1:

Các thanh ghi liên quan đến Timer1 bao gồm: INTCON (địa chỉ 0Bh, 8Bh, 10Bh, 18Bh): cho phép ngắt hoạt động (2 bit GIE và PEIE). PIR1 (địa chỉ 0Ch): chứa cờ ngắt Timer1 (TMR1IF). PIE1 (địa chỉ 8Ch): cho phép ngắt Timer1 (TMR1IE). TMR1L (địa chỉ 0Eh): chứa giá trị 8 bít thấp của bộ đếm Timer1. TMR1H (địa chỉ 0Eh): chứa giá trị 8 bít cao của bộ đếm Timer1. Hai thanh ghi TMR1L và TMR1H là 2 thanh ghi chứa dữ liệu 16 bit (lần lượt chứa 4 bit thấp và 4

bit cao) của bộ đếm Timer1 T1CON (địa chỉ 10h): xác lập các thông số chi Timer

- bit 7,6 không sử dụng - bit 5,4 T1CKPS1: T1CKPS0 lựa chọn hệ số chia xung vào.

T1CKPS1 T1CKPS0 00 1:1 01 1:2 10 1:4 11 1:8

- bit 3 T1OSCEN bit điều khiển bộ dao động Timer1

1= Bộ dao động hoạt động 0= Bộ dao động không hoạt động

- bit 2 bit điều khiển xung clock ngoài đồng bộ khi TMR1CS=1 =0 đồng bộ clock ngoài =1 không đồng bộ clock ngoài khi TMR1CS=0 bit này không có tác dụng

- bit 1 TMR1CS bit lựa chọn nguồn xung clock vào TMR1CS=1 clock từ chân RC0/T1OSO/T1CKI (sườn lên) TMR1CS=0 clock trong Fosc/4

- bit 0 bit bật tắt Timer1 =1 Timer 1 enable =0 Timer 1 disable

Thiết lập giá trị ban đầu cho Timer 1

Hình 10: Cấu trúc thanh ghi T1CON điều khiển hoạt động của Timer1

Page 13: Development_ KIT 16F877A

void Timer1_init() { //nap gia tri 16 bit cho timer 1 TMR1L=0x00; TMR1H=0x80; T1CON=0x0F; //chon che do:cho phep dao dong, khon g dong bo clock ngoai, bat timer1 TMR1IE=1; TMR1IF=0; PEIE=1; GIE=1; }

Thực hiện công việc khi xảy ra ngắt Timer 1

void interrupt isr() { if(TMR1IF && TMR1IE)

{ TMR1IF=0; TMR1L=0; TMR1H=0x80;

} }

3.3.3. Sử dụng Timer 2: Các thanh ghi liên quan : TMR2(11h) : chứa giá trị của Timer 2 PR2(92h) : Timer 2 sẽ đếm từ 00h đến PR2 ( mặc định giá trị cho PR2 là FFh) Thanh ghi T2CON: điều khiển hoạt động của Timer2

- bit 7 không sử dụng - bit 6-3 TOUTPS3: TOUTPS0 bit lựa chọn hệ số đầu ra Timer 2

0000=1:1 0001=1:2 0010=1:3 … 1111=1:16

- bit 2 TMR2ON bit bật tắt hoạt động Timer 2 1= enable 0= disable

- bit 1- 0 T2CKPS1:T2CKPS0 chọn hệ chia đầu vào 00 = 1:1 01 = 1:4 1x=1:16 Cách sử dụng timer 2 tương tự 2 timer trên

Page 14: Development_ KIT 16F877A

3.4.PWM : Để thực hiện chức năng PWM, ta cần cài đặt như sau : - Thiết lập thời gian 1 chu kì của xung điều chế cho PWM ( period ) bằng cách đưa giá trị vào

thanh ghi PR2 - Thiết lập độ rộng xung cần điều chế ( duty cycle) bằng cách đưa giá trị vào thanh ghi CCPRxL

và các bit CCP1CON<5:4> - Điều khiển các chân của CCP là out put bằng cách clear các bit tương ứng trong thanh ghi

TRISC - Thiết lập giá trị bộ chia tần số prescaler của Timer 2 và cho phép Timer 2 hoạt động bằng cách đưa giá trị thích hợp vào thanh ghi T2CON

- Cho phép CCP hoạt động ở chế độ PWM Công thức tính period : PWM period = [(PR2)+1]*4*TOSC*(giá trị bộ chia tần số của TMR2). Công thức tính duty cycle PWM duty cycle = (CCPRxL:CCPxCON<5:4>)*TOSC*( giá trị bộ chia tần số của TMR2)

unsigned long adc_val=0; void main() { //cau hinh port TRISC1 = 0; // chan CCP2 la output TRISC2 = 0; // chan CCP1 la output PORTC = 0; TRISA=0x04; // chan AN2 la input PORTA=0; // cau hinh ADC

ADFM = 1; //10 bit PCFG3 = PCFG2 = PCFG1 = 1; PCFG0 = 0; //chon kenh ADC ADCS2 = ADCS1 = ADCS0 = 0 ; //chon xung cho b o ADC

GODONE = 0 ; ADON = 1;

// cau hinh timer 2 T2CON = 0; // prescaler = 1:1; postscaler = 1:1 TMR2ON = 1; PR2 = 199; // Thoi gian timer tran: T = Tos c * prescaler * (PR2+1) // = 1us * 1 * 200 = 200us // => tan so PWM = 1/T = 5KHz

while(1) {

delay_us(5); // cho ADC lay mau GODONE = 1; // bat dau chuyen doi ADC while(GODONE); // cho ADC chuyen doi xong adc_val = ADRESL; adc_val |= (unsigned int)ADRESH << 8; adc_val = adc_val * 400 / 1023;

Page 15: Development_ KIT 16F877A

if(adc_val<200)

{ CCP1CON = 0x0C; // cau hinh CCP1 la PWM

CCP2CON = 0; // tat CCP2, chan RC1 d o PORTC dieu khien CCPR1L = 200 - adc_val; // set duty cho CCP1 }

else {

CCP2CON = 0x0C; // cau hinh CCP2 la PWM CCP1CON = 0; // tat CCP1, chan RC 2 do PORTC dieu khien CCPR2L = adc_val-200; // set duty cho C CP2 } delay_ms(50); } } Chương trình trên thực hiện công việc lấy giá trị analog từ AN2 rồi chuyển sang digital, so sánh với giá trị 200 để xuất ra chân CCP1 và CCP2 giá trị PWM

3.5.ADC :

Quá trình chuyển đổi gồm các bước sau : - Thiết lập thông số cho bộ ADC

Chọn ngõ vào analog, chọn điện áp mẫu ( ADCON1 ) Chọn kênh chuyển đổi ( ADCON0 ) Chọn xung clock cho kênh chuyển đổi ( ADCON0 ) Cho phép bộ chuyển đổi hoạt động ( ADCON0 )

- Thiết lập cờ ngắt ADIF = 0 ADIE = 1 PEIE = 1 GIE = 1

- Đợi tới khi kết quả lấy mẫu hoàn tất - Bắt đầu quá trình chuyển đổi - Kiểm tra quá trình chuyển đồi hoàn tất qua

GO/DONE = 0 >>> quá trình chuyển đổi hoàn tất Kiểm tra cờ ngắt

- Đọc kết quả chuyển đổi và xóa cờ ngắt, set GO/DONE nếu cần tiếp tục chuyển đổi - Tiếp tục thực hiện các bước 1,2 cho các quá trình chuyển dổi tiếp theo

Có 2 cách lưu kết quả : - ADFM = 1 : right justified - ADFM = 0 : left justified Các thanh ghi liên quan : - INTCON ( 0Bh, 8Bh, 10Bh, 18Bh) : cho phép ngắt ( GIE, PEIE ) - PIR1 ( 0Ch ) : cờ ngắt ( ADIF ) - PIE1 ( 8Ch ) : bit điều khiển ( ADIE ) - ADRESH ( 1Eh ) và ADRESH ( 9Eh ) : chứa kết quả chuyển đổi - ADCON1 ( 9Fh )

ADFM ADCS2 - - PCFG3:PCFG0 =1: right justified Kết hợp với ADCS1, Điều khiển việc chọn cấu hình hoạt động các

Page 16: Development_ KIT 16F877A

=0: left justigied ADCS0 điều khiển xung clock cho ADC

cổng của ADC

Trong đó : A : ngõ vào Analog, D : ngõ vào Digital, C/R : số ngõ vào Analog/số điện áp mẫu

- ADCON0 ( 1Fh ) : ADCS1:ADCS0 CHS2:CHS0 GO/DONE - ADON Kết hợp với ADCS2 điều khiển xung clock cho ADC

Chọn kênh chuyển đổi ADC 000 : AN0 111 : AN7

=1:khởi động ADC và tự xóa khi quá trình chuyển đổi kết thúc =0:A/D không hoạt động

=1:turn on ADC =0:turn off ADC

void ADC_init() {

ADFM = 1; //10 bit PCFG3 = PCFG2 = PCFG1 = 1; PCFG0 = 0; //chon kenh ADC

Page 17: Development_ KIT 16F877A

ADCS2 = ADCS1 = ADCS0 = 0 ; //chon xung cho bo ADC GODONE = 0 ; ADON = 1;

ADIE = 1; //Cho phep ngat ADIF = 0; PEIE = 1; GIE = 1; } void interrupt adc(void) { if(ADIF) { value =(ADRESH<<8)|ADRESL; ADIF = 0; } } void main() { unsigned int i=0; ADC_init(); while(1)

{ if(i<10) i++ else { GODONE=1; i=0; } }

}

Chương trình trên thực hiện nhiệm vụ cứ sau khi nhận được tín hiệu analog từ bên ngoài 10 lần thì lấy mẫu 1 lần

3.6.Giao tiếp truy ền thông :

3.6.1. USART: Bất đồng bộ :

Các bước thực hiện truyền dữ liệu : - Tạo xung truyền baud (RSBRG) và mức tốc độ (BRGH) - Cho phép bất đồng bộ (SYNC = 0, PSEN = 1) - Nếu cần sự dụng ngắt truyền (TXIE = 1) - Nếu định dạng dữ liệu cần truyền 9 bit (TX = 1) - Cho phép truyền dữ liệu (TXEN = 1) >>> TXIF = 1 - Nếu định dạng dữ liệu 9 bit, đưa bit thứ 9 vào TX9D - Đưa 8 bit dữ liệu vào TXREG - Nếu sử dụng ngắt truyền, kiểm tra GIE, PEIE (INTCON)

Các bước thực hiện nhận dữ liệu : - Thiết lập tốc độ baud (SPBRG, BRGH) - Cho phép bất đồng bộ (SYNC = 0, SPEN = 1)

Page 18: Development_ KIT 16F877A

- Nếu cần sử dụng ngắt nhận (RCIE = 1) - Nếu dữ liệu định dạng 9 bit (RX9 = 1) - Cho phép nhận dữ liệu (CREN = 1) >>> RCIF = 1 >>> ngắt được kích hoạt - Nếu dữ liệu định dạng 9 bit, đọc bit 9 yaij RCSTA và kiểm tra quá trình nhận dữ liệu có bị lỗi

không - Đọc 8 bit từ RCREG - Nếu quá trình có lỗi, xóa lỗi (CREN = 0) - Nếu sử dụng ngắt nhận thì GIE = PEIE = 1

void USART_init(void) { SPEN = 1; //cho phep giao tiep USART SYNC = 0; //khong dong bo

BRGH = 1; //toc do cao SPBRG = 77; //Baud = 9600; Baud Rate = Fosc/( 16(X+1)) TXEN = 1; //cho phep truyen du lieu TX9 = 0 ; //truyen du lieu 8 bit TXIE = 0; //khong cho phep ngat truyen RCIE = 1; //cho phep ngat nhan RX9 = 0; //nhan du lieu 8 bit CREN = 1; //cho pheo nhan du lieu PEIE = 1; GIE = 1; } void uart_putc(char c) { while(!TXIF); TXREG = c; } void uart_puts(const char* s) { while(*s != '\0')

{ uart_putc(*s); s++;

} } char uart_getc() { while(!RCIF); return RCREG; } char uart_data_ready() { if(RCIF) return 1; else return 0; }

Page 19: Development_ KIT 16F877A

void uart_gets(char *s) { *s = uart_getc(); while(*s!='\0')

{ s++; *s = uart_getc(); } } void main(void) {

bit flag; unsigned char i;

USART_init(); while(1)

{ i++; if(i == 0xff) i = 0; delay(0xffff);delay(0xffff); if(usart_data_ready) { usart_putc(send); } else putchar(i); } }

3.6.2. I2C:

void i2c_start(){ SEN = 1; while(SEN); } void i2c_stop(){ PEN = 1; while(PEN); } unsigned char i2c_read(unsigned char ack){ RCEN = 1; while(RCEN); ACKDT = ack; ACKEN = 1; while(ACKEN); return SSPBUF; } unsigned char i2c_write(unsigned char d) { WCOL = 0; SSPIF = 0; SSPBUF = d; delay_us(3); if(WCOL) return 2; // write collision else

Page 20: Development_ KIT 16F877A

{ while(!SSPIF); if(ACKSTAT) return 1; // not acknowledge return 0; // everything is ok } } void i2c_init() { RC3 = 1; RC4 = 1; TRISC3 = 1; TRISC4 = 1; // control by mssp SSPADD = 9; // Baudrate = Fosc/4/(SSPADDR+1 ) SMP = 1; // slew rate control disabled for 100KHz baudrate SSPCON2 = 0; SSPCON = 0x28; //I2C Master Hardware mode }

3.6.3. RS232:

Để cài đặt thông số cho truyền thông RS232, ta làm như sau : #use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8) baud = 9600 parity = N : không dùng parity xmit = PIN_C6 : set transmit pin rcv = PIN_C7 : set receive pin bits = 8 : truyền dữ liệu 8 bit Một ví dụ về việc sử dụng truyền thông RS232 :

#include <16F877.H> #use delay(clock=11059200) #fuses HS,NOWDT,NOPROTECT,NOLVP #use rs232(baud=9600, xmit=PIN_C7, rcv=PIN_C6) void main() { int8 sec,min,hour,day,date,month,year; sec=1; // read second min=2; // read minute hour=3; // read hour day=4; // read day date=5; // read date month=6; // read month year=7; // read year while(true) { putc(0x0c); printf("Time : %02X:%02X:%02X\r\n",hour,min,sec ); printf("Day : %02X\r\n",day); printf("Date : %02X/%02X/20%02X\r\n",date,month ,year); delay_ms(100); }

Page 21: Development_ KIT 16F877A

}

3.7.Bộ đếm thời gian thực DS 1307 Là 1 IC thời gian thực với nguồn cung cấp nhỏ, dùng để cập nhật thời gian và ngày tháng với 56

bytes SRAM. Địa chỉ và dữ liệu được truyền nối tiếp qua 2 đường bus 2 chiều. Đồng hồ có thể hoạt động wor dạng 12h với chỉ thị AM/PM và 24h. DS1307 còn có một mạch cảm biến điện áp dùng để dò các điện áp lỗi và tự động đóng ngắt với nguồn pin cung cấp.

Hoạt động của các chân : - Vcc, GND : khi được cung cấp 5V : thiết bị được truy cập hoàn chỉnh, có thể đọc và viết

Khi được cung cấp 3 V và nhỏ hơn 1.25Vbat thì quá trình đọc và ghi ko được thực thi, nhưng chức năng timekeeper ko bị ảnh hưởng. Khi Vcc nhỏ hơn Vbat thì Ram và timekeeper sẽ được ngắt tới nguồn cấp trong.

- Vbat : được giữ từ 2.5 – 3 V để đảm bảo hoạt động bt cho thiết bị - SCL : dùng để đồng bộ sự truyền dữ liệu trên đường dây nối tiếp - SDA : là chân vào ra cho 2 đường dây nối tiếp, đòi hỏi có điện trở kéo trong khi hoạt động - SQW/OUT : khi được thiết lập ( = 1 ), chân này phát đi 1 trong 4 tần số : 1Hz, 4Hz, 8Hz, 32Hz,

hoạt động khi Vcc và Vbat cùng được cấp - X1, X2 : được nối với thạch anh tần số 32,768kHz Sơ đồ địa chỉ Ram và RTC :

Seconds 00h Minutes Hours Day Date Month Year Control 07h RAM 58x8 08h

3Fh Thông tin về thời gian và ngày tháng được lấy ra bằng cách đọc các byte thanh ghi thích hợp, và

cũng được thiết lập thông qua các byte thanh ghi đó ( viết vào bằng mã BCD ). Thanh ghi thời gian thực được mô tả như sau :

Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0 X 10 SECONDS SECONDS X 10 MINUTES MINUTES X 12 24 10 HR (A/P) 10 HR HOURS X X X X X DAY X X 10 DATE DATE X X X 10 MONTH MONTH 10 YEAR YEAR OUT X X SQWE X X RS1 RS0

CH ( clock halt ) : = 0 để cho phép DS1307 hoạt động

DS1307 hoạt động ở 2 chế độ sau : - Chế độ slave nhận ( DS1307 ghi) :

Page 22: Development_ KIT 16F877A

- Chế độ slave phát (DS1307 đọc) :

Về chi tiết thì có thể đọc thêm data sheet của DS1307

Chương trình minh học giao tiếp với DS1307

#use delay(clock=11059200) #use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C 7,bits=8) #define DS1307_SDA RC4 #define DS1307_SCL RC3 #use i2c(master, sda=DS1307_SDA, scl=DS1307_SCL) #define lcdRs RE0 #define lcdRw RE1 #define lcdEn RC0 #define BG_LCD RB5 #define data_lcd PORTD //LCD Procedure void lcdWrCmd(unsigned char ch); void lcdInit(void); void lcdWrDat(unsigned char ch); void lcdWrInt(unsigned int val); void lcdGoHome (void); void lcdGoLeft (void); void lcdGoRight (void); void lcdGoPos (unsigned char line, unsigned char po s_of_line); void lcdDownLine(void); void lcdClr(void);

Page 23: Development_ KIT 16F877A

void lcdWrStr(unsigned char s[]); void setup_sys(); void delay(unsigned int16 d); void hienthi_time(unsigned char hour, unsigned char min, unsigned char sec); void hienthi_date(unsigned char day, unsigned char month, unsigned char year); void init_DS1307(); void write_DS1307(byte address, BYTE data); BYTE read_DS1307(byte address); unsigned int8 sec,min,hour,day,date,month,year; void lcd_realtime(); void main() { int8 sec,min,hour,day,date,month,year; delay_ms(50); init_ds1307(); // initial DS1307 sec=read_ds1307(0); write_ds1307(0,sec & 0x7F); // enable oscilla tor(bit 7=0) // cai dat ngay thang nam write_ds1307(0,0); // sec delay_ms(10); write_ds1307(1,45); // min delay_ms(10); write_ds1307(2,12); // hour delay_ms(10); write_ds1307(3,21); // date delay_ms(10); write_ds1307(4,11); // month delay_ms(10); write_ds1307(5,10); // year delay_ms(10);

while(1) {

sec=read_ds1307(0); // read second min=read_ds1307(1); // read minute hour=read_ds1307(2); // read hour day=read_ds1307(3); // read day date=read_ds1307(4); // read date month=read_ds1307(5); // read month year=read_ds1307(6); // read year putc(0x0c); printf("Time : %02X:%02X:%02X\r\n",hour,min, sec); printf("Day : %02X\r\n",day); printf("Date : %02X/%02X/20%02X\r\n",date,mo nth,year); delay_ms(1000); } } void lcd_realtime() { lcdClr(); hienthi_time(hour,min,sec);

Page 24: Development_ KIT 16F877A

hienthi_date(date,month,year); } //HAM HIEN THI : GIO: PHUT: GIAY void hienthi_time(unsigned int8 gio,unsigned int8 p hut,unsigned int8 giay) { unsigned int8 chuc_phut,dv_phut,chuc_giay,dv_giay ,chuc_gio,dv_gio; unsigned char m[]="TIME:"; chuc_phut=phut/10; dv_phut=phut%10; chuc_giay=giay/10; dv_giay=giay%10; chuc_gio=gio/10; dv_gio=gio%10; lcdWrCmd(0x80); // hien thi tu dong 1 lcdWrStr(m); lcdWrDat(chuc_gio+0x30); lcdWrDat(dv_gio+0x30); lcdWrDat(':'); lcdWrDat(chuc_phut+0x30); lcdWrDat(dv_phut+0x30); lcdWrDat(':'); lcdWrDat(chuc_giay+0x30); lcdWrDat(dv_giay+0x30); } //HAM HIEN THI : NGAY:THANG:NAM void hienthi_date(unsigned int8 ngay,unsigned int8 thang,unsigned int8 nam) {

unsigned int8 chuc_ngay,dv_ngay,chuc_thang,dv_thang ,chuc_nam,dv_nam; unsigned char m[] = "DATE:"; chuc_ngay=ngay/10;

dv_ngay=ngay%10;

chuc_thang=thang/10; dv_thang=thang%10; chuc_nam=nam/10; dv_nam=nam%10;

lcdWrCmd(0xc0); // hien thi tu dong 2 lcdWrStr(m);

lcdWrDat(chuc_ngay+0x30); lcdWrDat(dv_ngay+0x30); lcdWrDat(':');

lcdWrDat(chuc_thang+0x30); lcdWrDat(dv_thang+0x30); lcdWrDat(':'); lcdWrDat('2'); lcdWrDat('0'); lcdWrDat(chuc_nam+0x30); lcdWrDat(dv_nam+0x30);

Page 25: Development_ KIT 16F877A

} //================================================= =================== void setup_sys() { ADCON1 = 0x07; TRISA = 0; //config ports are output TRISE = 0; TRISC = 0; TRISB = 0; TRISD = 0; } //================================DS1307=========== ======================== // initial DS1307 //========================== void init_DS1307() { output_float(DS1307_SCL); output_float(DS1307_SDA); } //========================== // write data one byte to // DS1307 //========================== void write_DS1307(byte address, BYTE data) { short int status; i2c_start(); i2c_write(0xd0); //dia chi mac dinh cua DS1307 i2c_write(address); //dia chi thanh ghi i2c_write(data); //du lieu can ghi

i2c_stop(); i2c_start(); status=i2c_write(0xd0); while(status==1) //chua nhan duoc du lieu

{ i2c_start(); //tiep tuc gui den khi nhan duoc du lieu status=i2c_write(0xd0); } } //========================== // read data one byte from DS1307 //========================== BYTE read_DS1307(byte address) { BYTE data; i2c_start(); i2c_write(0xd0); i2c_write(address); i2c_start(); i2c_write(0xd1); data=i2c_read(0); i2c_stop(); return(data); }

Page 26: Development_ KIT 16F877A

3.8. LCD :

Chức năng các chân :

- VSS : chân nối đất - VDD : chân nối nguồn 5V - VEE : điều chỉnh độ tương phản LCD - RS : = 0 : sử dụng thanh ghi lệnh IR ở chế độ ghi hoặc sử dụng bộ đếm địa chỉ ở chế độ đọc, = 1 :

sử dụng thanh ghi dữ liệu DR - R/W : = 0 : ghi, = 1 : đọc - E : chân cho phép LCD hoạt động, các lệnh chỉ được chấp nhận khi có xung cho phép của chân E

Ở chế độ ghi : dữ liệu ở bus sẽ được chuyển vào LCD ( chấp nhận ) thanh ghi bên trong nó khi phát hiện 1 xung L-H của tín hiện chân E Ở chế độ đọc : dữ liệu sẽ được LCD xuất ra DB0 – DB7 khi phát hiện cạnh lên ( L-H ) ở chân E

và được giữ ở bus đến khi nào chân E xuống mức thấp - DB0 – DB7 : bus dữ liệu, có 2 chế độ 8 bit và 4 bit ( DB4 – DB7 )

Các thanh ghi : có 2 thanh ghi 8 bit quan trọng : IR ( Instructor Register ) và thanh ghi dữ liệu DR ( Data Register )

- IR : để điều khiển LCD, chúng ta phải ra lệnh thông qua DB7 – DB0 Bảng lệnh như sau :

Page 27: Development_ KIT 16F877A

- DR : dùng để chứa dữ liệu ghi vào vùng RAM DDRAM hoặc CGRAM ở chế độ ghi hoặc chứa dữ

liệu từ 2 vùng RAM này gửi ra MCU ở chế độ đọc. Bằng cách điều khiển chân RS và R/W, ta có thể chuyển qua lại giữa 2 thanh ghi này trong khi giao tiếp với MCU

- Busy Flag : khi thực hiện các hoạt động bên trong chip, cần 1 khoảng thời gian để thực hoàn tất nên

khi đang thực hiện các hoạt động, LCD sẽ bỏ qua mọi giao tiếp bên ngoài và bật BF ( thông qua chân DB7 khi có RS = 0, R/W = 1 ). Khi xong việc, BF tự động bằng 0

//LCD Procedure void lcdWrCmd(unsigned char ch); void lcdInit(void); void lcdWrDat(unsigned char ch); void lcdWrInt(unsigned int val); void lcdGoHome (void); // Move cursor go to home; void lcdGoLeft (void); // Move cursor left void lcdGoRight (void); // Move cursor right void lcdGoPos (unsigned char line, unsigned char po s_of_line); void lcdDownLine(void); void lcdClr(void);

Page 28: Development_ KIT 16F877A

void lcdWrStr(unsigned char s[]); void delay(unsigned int d); void lcdWrCmd(unsigned char ch) // ghi lenh { TRISD = 0; ppiEn = 1; delay(180); Rs = 0; Rw = 0; PORTD = ch; lcdEn = 1; lcdEn=0; TRISD = 0xff; } void lcdWrDat(unsigned char ch) // ghi du lieu { TRISD = 0; ppiEn = 1; delay(180); Rs = 1; Rw = 0; PORTD = ch; lcdEn = 1; lcdEn=0; TRISD = 0xff; } void lcdWrStr(unsigned char s[]) // ghi chuoi { unsigned char i = 0; while(s[i]!='\0') lcdWrDat(s[i++]); } void lcdInit(void) // khoi tao LCD { TRISD = 0; delay(0xffff); RD = 0; // Instruction Register WR = 0; // Write to enLCD = 1; // Enable LCD PORTD = 0x38; // 2 lines, matrix 5x7 enLCD = 1; delay(100); enLCD = 0; delay(100); lcdWrCmd(0x0C); // Display ON, Cursor off, no blink delay(10); lcdWrCmd(0x01); // Clear Display delay(10); lcdWrCmd(0x80); // Set Cursor to top left delay(100); TRISD = 0xff; }

Page 29: Development_ KIT 16F877A

void lcdWrInt(unsigned int val) { unsigned char i, j, num[5]; i = 0; while(val > 9) { num[i] = val%10 + 0x30; val = val/10; i++; } num[i] = val + 0x30; for(j = 0; j <= i; j++)

{ lcdWrDat(num[i - j]);

} } void lcdGoHome (void) // Move cursor go to hom e; { lcdWrCmd (0x02); // Cursor go home } void lcdGoLeft (void) // Move cursor left { lcdWrCmd (0x10); // Cursor go left } void lcdGoRight (void) // Move cursor right { lcdWrCmd (0x14); // Cursor go right } void lcdGoPos (unsigned char line, unsigned char po s_of_line) // Move cursor to posistion of a line { unsigned char addr; if (line==0) addr = pos_of_line; else addr = 0x40+pos_of_line; lcdWrCmd (0x80+addr); } void lcdDownLine(void) { lcdWrCmd(0xC0); } void lcdClr(void) { lcdWrCmd (0x01); // Cursor go home lcdGoHome(); } void delay(unsigned int d) { while(d--){}; }

Page 30: Development_ KIT 16F877A

3.9.STEP MOTOR : - Động cơ bước gồm 4 cuộn dây A , B , C , D phân bố đều trên stato . Rôto làm bằng nam châm vĩnh

cửu . Để động cơ hoạt động , ta cấp điện cho từng cuộn dây một ( chế độ 1 pha ) , hoặc từng cặp cuộn dây một ( chế độ 2 pha ) .

- Khi cấp điện 1 pha , để động cơ quay thuận thì thứ tự cấp điện là Step1, Step2, Step3, Step4 . Để động cơ quay ngược thì thứ tự cấp điện là Step4, Step3, Step2, Step1 Chương trình sau minh hoạ việc điều khiển động cơ bước chế độ 1 pha.

#define Step1 RC1; #define Step2 RB0; #define Step3 RC2; #define Step4 RB5; #define CW1 1

Page 31: Development_ KIT 16F877A

#define CW2 2 void delay(unsigned int t); void Stepmotor(unsigned char step,unsigned char CW) ; void main() { unsigned int d; ADCON1 = 0x07; TRISA = 0; TRISB = 0; TRISC = 0; TRISD = 0; TRISE = 0; d = 10; while(1) { Stepmotor(100,CW1); delay(0xffff); //quay thuan 100 buoc Stepmotor(200,CW2); delay(0xffff); //quay nguo c 200 buoc } } void Stepmotor(unsigned char step,unsigned char CW) {

unsigned char i,st; if(CW==CW1) //quay thuan {

st=1; for(i=0;i<step;i++) { switch (st) { case 1:

{ Step1=0;Step2=1;Step3=1;Step4=1;delay(0x8ff);st=2;b reak;

} case 2: { Step1=1;Step2=0;Step3=1;Step4=1; delay(0x8ff);st=3; break; } case 3: { Step1=1;Step2=1;Step3=0;Step4=1;del ay(0x8ff);st=4; break; }

case 4: {

Step1=1;Step2=1;Step3=1;Step4=0;delay(0x8ff);s t=1;break; }

default:st=0; }; }

} else if(CW==CW2) //quay nguoc {

Page 32: Development_ KIT 16F877A

st=1; for(i=0;i<step;i++) { switch (st) { case 1: { Step1=1;Step2=1;Step3=1;Step4=0;delay(0x8ff); st=2;break; } case 2: { Step1=1;Step2=1;Step3=0;Step4=1; delay(0x8ff);st=3; break; } case 3: { Step1=1;Step2=0;Step3=1;Step4= 1;delay(0x8ff);st=4; break; }

case 4: {

Step1=0;Step2=1;Step3=1;Step4=1;delay(0x8ff);st=1;b reak; }

default:st=0; };

} }

} //--------------------------------------- void delay(unsigned int t) { while(t--){}; }