Tải bản đầy đủ (.pdf) (29 trang)

lập trình c căn bản (hướng dẫn làm đồ án)

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (1.2 MB, 29 trang )

[HƢỚNG DẪN LẬP TRÌNH PIC CĂN BẢN]
BÀI 1: DIGITAL OUTPUT
GIAO TIẾP MAX7219-LED MA TRẬN ĐƠN SẮC 8x8
1. Phần cứng:
-Max7219 giao tiếp MCU bằng 3 IO:
DIN(1): Dữ liệu vào nối tiếp.
LOAD(12): Chốt dữ liệu, cho phép hiển thị.
CLK(13): Xung nhịp clock.
-Đặc tính:
Quét led độc lập - không phụ thuộc vào vi điều khiển.
Vi điều khiển chỉ việc đƣa dữ liệu vào các thanh ghi nhớ ứng với từng digit (1 led 7 đoạn/ 1 hàng trong led ma
trận).
Dữ liệu không bị mất khi chƣa có dữ liệu mới đƣợc đƣa vào.
Tƣơng tự nhƣ 74HC595, các IC Max7219 nối dữ liệu với nhau bằng cách nối Dout của IC trƣớc với Din của IC
sau.
2. Phần mềm:
Khai báo tiền xử lý:
#define LOAD PIN_A1
#define CLK PIN_A2
#define DIN PIN_A0
#define NUM 3 //Số IC đƣợc sử dụng, cũng ứng với số led ma trận.
#include <MAX7219_MULTI.c>
unsigned char alphabet[NUM][8]={}; //Mảng lƣu giá trị hiển thị của từng led ma trận, mỗi led 8 byte dữ liệu.
Khai báo config:
InitDisplay();
delay_ms(100);
Hàm hiển thị:
Display(i+1,j+1,alphabet[i][j]);
/*
i là thứ tự IC, con nối chân Din với vi điều khiển là con thứ i=0, j là thứ tự hàng, alphabet[i],[j] là truy xuất dữ
liệu cần


hiển thị tƣơng ứng.
*/
3. VD mẫu:
Hiển thị chữ TEC trên 3 led ma trận dùng Max7219.
#include <12F629.h>
#fuses XT, INTRC_IO, NOWDT, PROTECT, NOMCLR, NOBROWNOUT
#use delay(clock=4M)
#define LOAD PIN_A1
#define CLK PIN_A2
#define DIN PIN_A0
#define NUM 3
#include <MAX7219_MULTI.c>
unsigned char alphabet[NUM][8]=
{
0xFE,0xFE,0x38,0x38,0x38,0x38,0x38,0x38, //T
0xFE,0xFE,0xC0,0xF8,0xF8,0xC0,0xFE,0xFE, //E
0x7C,0xFE,0xE6,0xC0,0xC0,0xE6,0xFE,0x7C //C
};
void TEC_display()
{
for(int i=0;i<3;i++)


{
for(int j=0;j<8;j++) Display(i+1,j+1,alphabet[i][j]);
}
}
void main()
{
InitDisplay();

delay_ms(100);
TEC_display();
while(true);


II. CẤU TRÚC CƠ BẢN CỦA MỘT CHƢƠNG TRÌNH VIẾT TRÊN CCS:
1. Cấu trúc chung:
- Khai báo tiền xử lý:
- Các chƣơng trình con.
- Chƣơng trình chính.
--> Ta đặt chƣơng trình chính cuối cùng để tránh phải khai báo nhiều trong khai báo tiền xử lý.
--> Chƣơng trình đứng phía dƣới đƣợc phép gọi chƣơng trình phía trên mà không cần khai báo chƣơng trình cần
gọi ở tiền xử lý.
2. Khai báo tiền xử lý:
#include<device.h> //Khai báo thƣ viện biên dịch của PIC đang sử dụng ví dụ: #include<16F877A.h>
//Thƣ viện các dòng PIC chứa trong C:\Program Files\PICC\Devices
#device ADC=10 //Chỉ khai báo khi Chip có bộ chuyển đổi ADC 10bit
#fuses HS, NOWDT //Khai báo "cầu chì", cần chú ý khai báo HS là dùng dao động cao tần bên ngoài nhƣ thạch
anh.
//Nếu dùng dao động tần số thấp (<=4MHz), ta khai báo nhƣ sau: #fuses XT
//Nếu sử dụng dao động nội (chỉ Chip nào có hỗ trợ): #fuses XT, INTRC_IO
//Khai báo NOWDT không sử dụng "đồng hồ chó". WDT - chống treo Chip khi rơi vào vòng lặp chết.
#use delay(clock=xM) //Khai báo tần số xung nhịp cấp cho core xử lý, x là tần số dao động (MHz). Ví dụ: #use
delay(clock=20M).
//Nếu dùng dao động nội thì clock=4M.
#define A B //Thay thế B bằng A, A sẽ mang mọi đặc tính của B. Ví dụ #define LED PIN_A0 thay thế PIN_A0
bằng LED.
3. Cấu trúc chƣơng trình chính:
void main()
{

Các config;
while(true)
{
Các chƣơng trình con;
}
}
-1-

[HƢỚNG DẪN LẬP TRÌNH PIC CĂN BẢN]
BÀI 1: DIGITAL OUTPUT [TT]
I. GIỚI THIỆU CÔNG CỤ LẬP TRÌNH, MÔ PHỎNG:
III. CÁC LỆNH XỬ LÝ:
set_tris_x(8 bit value); //Nếu I.O cố định thì gọi hàm này 1 lần trong config.
//Cài đặt các port x là ngõ vào/ra. value ở binary, bit nào giá trị 1 là ngỏ vào, 0 là ngỏ ra.
//VD: set_tris_a(0b00111001); port A có bit 0,3,4,5 là ngỏ vào, 1,2,6,7 là ngỏ ra.
output_x(8 bit value); //Sử dụng khi có khai báo set_tris_x(); Có thể gọi nhiều lần.
//Xuất ngỏ ra cho port x. VD: ouput_a(0b01010101);
//bit mang giá trị 1 là mức cao (5V), mức thấp là 0V.
output_high(PIN_name); //Xuất mức cao chân PIN_name. VD: output_high(PIN_A0);
output_low(PIN_name); //Xuất mức thấp chân PIN_name. VD: output_low(PIN_A0);
output_bit(PIN_name, x); //Xuất mức x ở chân PIN_name. Mức thấp x=0, cao x=1.
output_float(PIN_name); //Trạng thái ngỏ ra cao trở.
--> Khi ta set trạng thái thì trạng thái đƣợc giữ nguyên cho đến khi ta set trạng thái khác hoặc mất điện.
delay_us(16 bit value); //Delay micro giây.
delay_ms(16 bit value); //Delay mili giây.
IV. VÍ DỤ:


#include<16F877A.h>
#fuses HS, NOWDT

#use delay(clock=20M)
void main()
{
set_tris_b(0x00); //0x là số hexa, 0b là số binary, số thƣờng là decimal. 0x00=0b00000000
while(true)
{
output_b(0x55); //0x55=0b01010101
output_high(PIN_A0);
delay_ms(250);
output_b(0xaa); //0xaa=0b10101010
output_low(PIN_A0);
delay_ms(250);
}
}


BÀI 1: DIGITAL OUTPUT [TT]
I. LẬP TRÌNH ĐIỀU KHIỂN LED 7 ĐOẠN 1 SỐ:
1. Xác định phần cứng:
- Xác định loại led 7 đoạn: âm cực chung (CC), dƣơng cực chung (CA).
- Kết nối với IO vi điều khiển tuỳ ý sao cho dễ thi công mạch. Chú ý sử dụng 1 port 8 IO để dễ điều khiển.
2. VD mẫu:
Điều khiển 1 led 7 đoạn CA kết nối port B của PIC16F877A, hiển thị từ 0 đến 9. Các bƣớc thực hiện nhƣ sau:
Qui định IO:
Bit 0: A Bit 1: B Bit 2: C Bit 3: D
Bit 4: A Bit 5: E Bit 6: F Bit 7: DP
Bảng mã 7 đoạn:
Vì led 7 đoạn dƣơng chung nên led sẽ sáng ở mức 0.
0: 0xc0 1: 0xf9 2: 0xa4 3: 0xb0 4: 0x99
5: 0x92 6: 0x82 7: 0xd8 8: 0x80 9: 0x90

Chƣơng trình điều khiển:
#include<16F877A.h>
#fuses HS, NOWDT
#use delay(clock=20M)
unsigned char ma_7doan[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xd8,0x80,0x90};
void main()
{
set_tris_b(0x00);
while(true)
{
for(int i=0;i<10;i++)
{
output_b(ma_7doan[i]);//mã xuất ra port B đƣợc truy xuất từ vị trí thứ i của mảng ma_7doan[].
delay_ms(500);
}
}
}
II. QUÉT LED 7 ĐOẠN:
1.
2.

Xác định phần cứng:
Tƣơng tự phần I. Chú ý ta dùng 1 port 8 IO xuất mã 7 đoạn và dùng thêm IO khác để quét led.
VD mẫu:
Thiết kế mạch đếm tự động từ 0 đến 99 dùng led 7 đoạn CA và kỹ thuật quét led.
Qui định IO:
Bit 0: A Bit 1: B Bit 2: C Bit 3: D
Bit 4: A Bit 5: E Bit 6: F Bit 7: DP
PIN_C0: CA0 PIN_C1: CA1
Quét led thực tế là cho lần lƣợt từng led 7 đoạn sáng - tại 1 thời điểm chỉ có 1 led 7 đoạn sáng, thời

gian thay đổi giữa các
led rất nhỏ (vài us/ms) nên mắt không phân biệt đƣợc và thấy chúng sáng đồng thời.
Mắt chỉ phân biệt đƣợc dao động <22Hz, trên 22Hz ta thấy vật dao động đứng im.
Chƣơng trình mẫu:
#include<16F877A.h>
#fuses HS, NOWDT
#use delay(clock=20M)
unsigned char ma_7doan[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xd8,0x80,0x90};
void main()
{


set_tris_b(0x00);
unsigned char i=0, count=0, chuc, dv; //Các biến
while(true)
{
chuc=i/10; // i là số nguyên, phép "/" là chia lấy phần nguyên.
dv=i%10; // i là số nguyên, phép "%" là chia lấy phần dƣ.
output_low(PIN_C1); //Tắt led hàng đơn vị.
output_b(ma_7doan[chuc]); //Xuất mã hàng chục.
output_high(PIN_C0); //Cho phép led hàng chục sáng.
delay_ms(1); //Tạo trễ để nhìn thấy led sáng.
output_low(PIN_C0); //Tắt led hàng chục.
output_b(ma_7doan[dv]); //Xuất mã hàng đơn vị.
output_high(PIN_C1); //Cho phép hàng đv sáng.
delay_ms(1); //Tạo trễ để hiển thị.
count++; //Cho biến đếm tự tăng 1 đơn vị.
if(count==250) //Khi biến đếm =250 ứng với 500ms do 2 lệnh delay_ms(1) phía trên hết 2 ms.
{
i++; //Tăng i lên 1 đơn vị

if(i>=100) i=0; //Khi i>=100 thì reset về 0. Tức mạch đếm từ 00-99
count=0; //Reset biến đếm sau 250 lần đếm.
}
}
}

BÀI 1: DIGITAL OUTPUT [TT2]
I. Giao tiếp LCD 16x2 chế độ 4 bit dữ liệu:
1. Kết nối phần cứng:


Các IO trên LCD đƣợc sử dụng: RS, RW, EN, D4, D5, D6, D7.
Chân RW nối GND, các chân còn lại nối với vi điều khiển.
2. Phần mềm:
Khai báo tiền xử lý cần thêm vào:(Các IO kết nối tuỳ ý, không bắt buộc theo port)
#define LCD_RS PIN_B0
#define LCD_EN PIN_B2
#define LCD_D4 PIN_B3
#define LCD_D5 PIN_B4
#define LCD_D6 PIN_B5
#define LCD_D7 PIN_B6
#include<LCD1602.c>
unsigned char string[17];
Các config trong void main():
LCD_Init();
delay_ms(100);
Các lệnh hiển thị:
sprintf(string,"chuỗi cần hiển thị");// VD sprintf(string,"PIC16F877A"); Lƣu mã ASCII của chuỗi
PIC16F877A vào mảng string[].
sprintf(string,"chuỗi cần hiển thị %kiểu dữ liệu",biến dữ liệu);

LCD_Gotoxy(vị trí, hàng);//Chữ đƣợc in ra từ vị trí 0-15, hàng(trên là 0, dƣới là 1).
LCD_Puts(string);//Gửi dữ liệu trong mảng string lên LCD để hiển thị ra.
LCD_Clear_All();//Xoá hết màn hình
LCD_Clear_Row(hàng);//Xoá theo hàng 0 hoặc 1
3. VD mẫu:
Hiển thị chữ "DEE-TDTu" ở chính giữ, hàng trên và chữ "CLB Dien Tu TEC" ở hàng dƣới trong vòng
2s rồi đếm từ 0-99 ở hàng
dƣới, hàng trên giữ nguyên.
#include<16F1823.h>
#fuses XT, INTRC_IO, NOWDT, NOMCLR
#use delay(clock=4M)
/*Định nghĩa các IO giao tiếp với LCD*/
#define LCD_RS PIN_A0
#define LCD_EN PIN_A1
#define LCD_D4 PIN_C0
#define LCD_D5 PIN_C1
#define LCD_D6 PIN_C2
#define LCD_D7 PIN_C3
#include<LCD1602.c>
unsigned char string[17];
/*
Hiển thị chữ "DEE-TDTu" ở chính giữ, hàng trên.
Hiển thị chữ "CLB Dien Tu TEC" ở hàng dƣới trong vòng 2s.
Đếm từ 0-99 ở hàng dƣới, hàng trên giữ nguyên.
*/
void hien_thi1()
{
sprintf(string,"DEE-TDTu");
LCD_Gotoxy(4,0);
LCD_Puts(string);

sprintf(string,"CLB Dien Tu TEC");
LCD_Gotoxy(0,1);
LCD_Puts(string);


delay_ms(2000);
LCD_Clear_Row(1);
sprintf(string,"Gia tri dem=");
LCD_Gotoxy(0,1);
LCD_Puts(string);
}
/*
Hiển thị giá trị đếm.
*/
void hien_thi2(int count)
{
sprintf(string,"%02d",count);
LCD_Gotoxy(12,1);
LCD_Puts(string);
}
void main()
{
LCD_Init();
delay_ms(100);
hien_thi1();
while(true)
{
for(int i=0;i<100;i++)
{
hien_thi2(i);

delay_ms(250);
}
}
}
II. Giao tiếp IC 74HC595:
1. Kết nối phần cứng:
74HC595 kết nối với vi điều khiển qua 3 chân:
SH_CP(11): Chân xung nhịp clock.
ST_CP(12): Chân chốt dữ liệu - cho phép xuất dữ liệu trong thanh ghi tạm ra các chân.
DS(13): Chân nhận dữ liệu nối tiếp.
--> Giúp mở rộng ngỏ ra số cho vi điều khiển.
--> Đồng bộ hoá dữ liệu đầu ra. Vào nối tiếp, ra song song.
2. Phần mềm:
Khai báo tiền xử lý:
#define EXP_OUT_ENABLE PIN_A2 //Chân ST_CP
#define EXP_OUT_CLOCK PIN_A0 //Chân SH_CP
#define EXP_OUT_DO PIN_A1 //Chân DS
#define NUMBER_OF_74595 2 //Số IC sử dụng. IC nào có chân DS nối với MCU là IC thứ 0, kế tiếp
là 1,2,3,...
#include <74595.c>
byte data[2]; //mảng lƣu giá trị cần hiển thị ứng với mỗi IC.
Lệnh hiển thị:
write_expanded_outputs(mảng dữ liệu);
3. VD mẫu:
Viết chƣơng trình điều khiển 2 IC 74HC595 dùng PIC12F629. Yêu cầu: IC thứ nhất dịch bit từ phải
qua trái, IC thứ 2 dịch bit
từ trái qua phải.


#include <12F629.h>

#fuses XT, INTRC_IO, NOMCLR, NOWDT
#use delay(clock=4M)
#define EXP_OUT_ENABLE PIN_A2
#define EXP_OUT_CLOCK PIN_A0
#define EXP_OUT_DO PIN_A1
#define NUMBER_OF_74595 2
#include <74595.c>
byte data[2];
void main()
{
while(true)
{
for(int i=0;i<8;i++)
{
data[0]=0x01<data[1]=0x80>>i;
write_expanded_outputs(data);
delay_ms(250);
}
}
}

BÀI 1: DIGITAL OUTPUT [TT4]
FLOATING STATE
Floating là trạng thái cao trở, có thể xem là trạng thái logic thứ 3.


Trạng thái này hỗ trợ cho việc quét led, các giao tiếp cần trạng thái thứ 3, chuyển mạch.
Sử dụng tối ƣu các IO hơn.
Khi sử dụng, không cần khai báo gì khác, chỉ việc gọi hàm output_float(IO_name);

VD: output_float(PIN_A0);// Cho ngỏ ra chân A0 ở trạng thái cao trở.
VD: Điều khiển 6 led dùng 3 IO.
#include <12F629.h>
#fuses XT, INTRC_IO, NOMCLR, NOWDT
#use delay(clock=4M)
void main()
{
while(true)
{
output_high(PIN_A0); output_low(PIN_A1); output_float(PIN_A2);
delay_ms(1000);
output_low(PIN_A0); output_high(PIN_A1); output_float(PIN_A2);
delay_ms(1000);
output_high(PIN_A0); output_float(PIN_A1); output_low(PIN_A2);
delay_ms(1000);
output_low(PIN_A0); output_float(PIN_A1); output_high(PIN_A2);
delay_ms(1000);
output_float(PIN_A0); output_high(PIN_A1); output_low(PIN_A2);
delay_ms(1000);
output_float(PIN_A0); output_low(PIN_A1); output_high(PIN_A2);
delay_ms(1000);
}
}

BÀI 2: DIGITAL INPUT
Khai báo tiền xử lý:
/*Sử dụng cho ngỏ vào là 1 port*/


#use fast_io(port) //Khai báo sử dụng port vừa vào vừa ra.

#bit name=pin_address // Đặt tên 1 bit cụ thể của port.
--> port A,B,C,D,E có địa chỉ lần lƣợt là: 0x05,0x06,0x07,0x08,0x09.
--> pin của port có địa chỉ từ 0-7.
VD: Ta muốn đặt tên in5 cho chân số 5 của port C ta khai báo nhƣ sau:
#bit in5=0x07.5
/*Sử dụng ngỏ vào là IO bất kì*/
Không khai báo gì.
Khai báo config:
set_tris_port(8 bit value);//Sử dụng cho ngỏ vào port hoặc bit của port.
--> Thanh ghi tris sẽ qui định IO tƣơng ứng là vào khi nhận bit giá trị 1 và ra khi nhận bit giá trị 0.
VD: Qui định C là port vào 8 bit: set_tris_c(0xff);
Qui định C là port vào 3 bit đầu: set_tris_c(0x07);
--> Sử dụng ngỏ vào là IO bất kỳ ta không cần khai báo config.
Các lệnh xử lý:
/*Sử dụng cho ngỏ vào là 1 port*/
byte value=ingput_port(); //Lƣu giá trị ngỏ vào port vào biến value có kiểu dữ liệu là byte.
int1 value=pin_address; //Lƣu giá trị của pin cụ thể của port vào viến value có kiểu dữ liệu 1 bit.
/*Sử dụng ngỏ vào là IO bất kì*/
int1 value=input(PIN_name); //Lƣu giá trị của IO vào biến value kiểu 1 bit.
VD mẫu: Sử dụng PIC16F877A
Dùng 1 IO E1 để bật tắt 1 đèn nối với PIN_B0 của port B.
Dùng port C để bật tắt các đèn tƣơng ứng của port D.
Dùng pin 3,4,5 để bật tắt đèn nối với pin 0,1,2 của port A.
#include <16F877A.h>
#fuses HS, NOWDT
#use delay(clock=20M)
#use fast_io(a)
#bit I0=0x05.3
#bit I1=0x05.4
#bit I2=0x05.5

#bit Q0=0x05.0
#bit Q1=0x05.1
#bit Q2=0x05.2
void main()
{
set_tris_a(0x38);
set_tris_c(0xff);
set_tris_d(0x00);
while(true)
{
output_bit(PIN_B0,input(PIN_E1));
output_d(input_c());
Q0=I0;
Q1=I1;
Q2=I2;
}}



BÀI 2: DIGITAL INPUT [TT1]
BÀN PHÍM MA TRẬN 4x4
1. Phần cứng:
- Bàn phím ma trận là 1 giải pháp giúp tiết kiệm IO vi điều khiển.
- Cấu tạo phím ma trận 4x4 gồm: 4 IO hàng và 4 IO cột và kết nối nhƣ hình vẽ.
- Với 8 IO ta có thể tạo ra 16 phím bấm, nếu sử dụng IO riêng lẻ ta phải dùng 16 IO.
2. Phần mềm:
Chép và ghi đè file key_4x4.c đính kèm file PDF này vào "C:\Program Files\PICC\Drivers".
- Phƣơng pháp điều khiển:
Ở ví dụ này, ta dùng 4 hàng phát xung và 4 cột nhận xung.
Các IO hàng đặt ở chế độ output, 4 IO cột cở chế độ input.

Ta lần lƣợt cho các IO hàng lên mức cao. Cứ mỗi lần nhƣ thế ta lại kiểm tra 4 IO cột. Nếu cột nào có mức logic
cao, tức nút
đƣợc nhấn thì trả về giá trị cột và giá trị hàng tại thời điểm đó.
Dựa vào thông số trả về, ta quy định giá trị phím nhấn.
- Khai báo tiền xử lý:
#define ROW1 PIN_C0
#define ROW2 PIN_C1
#define ROW3 PIN_C2
#define ROW4 PIN_C3
#define COL1 PIN_C4
#define COL2 PIN_C5
#define COL3 PIN_C6
#define COL4 PIN_C7
#include <key_4x4.c>
- Không sử dụng config.
- Lệnh lấy giá trị phím:
byte code=get_key_4x4();
Giá trị trả về kiểu 8bit, mã bàn phím gồm:
'7','8','9','/',
'4','5','6','x',
'1','2','3','-',
'C','0','=','+'
Bạn có thể sửa chúng trong file key_4x4.c chứa tại "C:\Program Files\PICC\Drivers".


3.

4.

VD mẫu:

Viết chƣơng trình giao tiếp bàn phím ma trận, hiển thị phím vừa đƣợc nhấn ra LCD 16x2, nếu phím
nhấn là C thì reset vi điều khiển.
#include <16F877A.h>
#fuses HS, NOWDT
#use delay(clock=20M)
#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
#include<LCD1602.c>
/*Giao tiep phim ma tran*/
#define ROW1 PIN_C0
#define ROW2 PIN_C1
#define ROW3 PIN_C2
#define ROW4 PIN_C3
#define COL1 PIN_C4
#define COL2 PIN_C5
#define COL3 PIN_C6
#define COL4 PIN_C7
#include <key_4x4.c>
/*IO Reset*/
#define RST PIN_E2
unsigned string[17];
char code=' ', temp=' ';
void main()
{
output_high(RST); //Không cho chân RST tích cực.
LCD_init();

delay_ms(1000);
sprintf(string,"MATRIX KEY 4x4");
LCD_Gotoxy(1,0);
LCD_Puts(string);
sprintf(string,"Key: ");
LCD_Gotoxy(0,1);
LCD_Puts(string);
while(true)
{
code=get_key_4x4(); //Lấy mã bàn phím
if(code=='C') output_low(RST); //Nếu mã phím là C thì cho chân RST tích cực.
else if((code!=temp)&&(code!=' ')) //Ngƣợc lại, nếu mã phím khác mã trƣớc đó và khác mã khoảng
trống thì in mã mới ra LCD.
{
sprintf(string,"%c",code);
LCD_Gotoxy(5,1);
LCD_Puts(string);
temp=code; //Cập nhật mã mới để so sánh lần sau.
}
}
}


BÀI 3: ANALOG INPUT
I. Giới thiệu:
- Một số dòng PIC có hỗ trợ bộ chuyển đổi tín hiệu tƣơng tự sang số với độ phân giải 8/10 bit.
- Bộ chuyển đổi tƣơng tự sang số gọi là ADC (Analog to Digital Converter).
- Ý nghĩa:
--> Nhiều bạn chƣa học xử lý số tín hiệu nên AD tạm giải thích nhƣ sau:
+ Độ phân giải (số bit phân giải) là 8 hoặc 10bit, tức có 2^8=256 hoặc 2^10=1024 giá trị số đƣợc

chuyển đổi (số bậc phân giải).
+ Áp tham chiếu: Là điện áp chuẩn, lớn nhất, điện áp này đƣợc chia đều cho số bậc phân giải.
VD minh hoạ với độ phân giải là 2 bit (4 bậc phân giải). Áp tham chiếu Vref=9V.
Giá trị analog Giá trị số
9V..................3
6V..................2
3V..................1
0V..................0
Ta thấy, nếu ngỏ vào [0V;3V) ADC=0, [3V;6V) ADC=1, [6V;9V) ADC=2, [9V;...) ADC=3.
--> Với PIC, giá trị ADC min=Vfref-=0, ADC max=Vref+.
Công thức chung để tính giá trị mỗi bậc ADC của PIC là:
Vref/((2^bit)-1).
II. Phần cứng:
- Cần kiểm tra datasheet xem chip sử dụng có bộ ADC hay không.
- Một số dòng PIC, ADC chỉ có 8 bit, hầu hết là có 2 độ phân giải 8/10 bit.
- Trong Proteus, chân ANx là chân vào của tín hiệu analog. Một chip có thể có nhiều ngỏ vào analog.
- Với PIC, áp tham chiếu tối đa là 5V, nếu sử dụng áp khác thì cần cấp áp chuẩn vào chân có kí hiệu
Vref.
III. Phần mềm:
1. Khai báo tiền xử lý:
#device ADC=8 //Chế độ 8 bit
#device ADC=10 //Chế độ 10 bit
--> Khai báo ngay sau khai báo #include<device.h> và trƣớc khai báo #fuses.
2. Khai báo config:
setup_adc(ADC_CLOCK_INTERNAL); //Chu kỳ lấy mẫu 2-6us.
setup_adc_ports(pin name); //Chọn port vào analog. Tối đa 2 port, phân cách bằng dấu phẩy.
set_adc_channel(value); //Đi chung với setup_adc_port() tƣơng ứng. Chỉ khai báo lệnh này khi dùng 1
port analog.
--> Xem trong file device.h.
3. Hàm lấy giá trị ADC:

/*Dùng 1 port analog*/
delay_us(20); //Delay tối thiểu để lấy mẫu.
float value=read_adc();
/*Dùng 2 port analog*/
set_adc_channel(x);
delay_us(20);
float value1=read_adc();
set_adc_channel(y);
delay_us(20);
float value2=read_adc();
/*Dùng trên 2 port analog*/
setup_adc_ports(pin name);
set_adc_channel(x);
delay_us(20);
float value2=read_adc();


4. VD mẫu:
Đo điện áp 0-5000mV dùng bộ ADC, hiển thị LCD.
#include <16F886.h>
#device ADC=10
#fuses XT, INTRC_IO, NOWDT, NOMCLR
#use delay(clock=4M)
#define LCD_RS PIN_C2
#define LCD_EN PIN_C3
#define LCD_D4 PIN_C4
#define LCD_D5 PIN_C5
#define LCD_D6 PIN_C6
#define LCD_D7 PIN_C7
#include<LCD1602.c>

unsigned char string[17];
void main()
{
float temp, adc;
unsigned int16 vol;
setup_adc(ADC_CLOCK_INTERNAL);
setup_adc_ports(sAN0);
set_adc_channel(0);
LCD_Init();
delay_ms(100);
sprintf(string,"ADC VOL");
LCD_Gotoxy(0,0);
LCD_Puts(string);
while(true)
{
delay_us(20);
adc=read_adc(); //Đọc giá trị ADC.
temp=(5000*adc)/1023; //Chuyển về giá trị điện áp (mV).
vol=(unsigned int16)temp; //Lấy phần nguyên của số thực temp.
sprintf(string,"%04ld %04ldmV",(unsigned int16)adc, vol);
LCD_Gotoxy(0,1);
LCD_Puts(string);
}
}

BÀI 3: ANALOG INPUT
NHIỆT KẾ ĐIỆN TỬ
I. Giới thiệu:



- Để đo đƣợc nhiệt độ bằng phƣơng pháp điện tử, ta phải chuyển năng đại lƣợng nhiệt (dạng phi điện)
về dạng tín hiệu điện với một
tỉ lệ chuyển đổi tƣơng đối tuyến tính. Phƣơng pháp chuyển đổi này là cơ sở hình thành cảm biến mà cụ
thể ở đây là cảm biến nhiệt.
- Trên thị trƣờng có nhiều loại cảm biến nhiệt với cấu tạo và phƣơng thức chuyển đổi sang tín hiệu điện
khác nhau. Khá phổ biến
hiện nay là cảm biến nhiệt LM35.
- Đặc điểm LM35: />+ Điện áp hoạt động: 4-30VDC, khuyên dùng 5VDC.
+ Dạng tín hiệu ngỏ: Analog.
+ Độ phân giải: 10mV/ độ C.
+ Tầm đo: -55 đến 150 độ C.
II. Phần cứng:
Nhƣ hình bên.
III. Phần mềm:
- Ngỏ ra của cảm biến là analog nên ta cài đặt PIC ở chế độ analog.
- Ta chỉ dùng 1 kênh analog.
- VD mẫu:
Đo nhiệt độ dùng LM35 với các yêu cầu sau:
+ Hiển thị led 7 đoạn 4 số.
+ Tầm đo 0-99 độ C. Vƣợt quá sẽ báo lỗi.
+ Nhiệt độ đƣợc cập nhật 1 lần/s.
#include <16F886.h>
#device ADC=10
#fuses XT, INTRC_IO, NOMCLR, NOWDT
#use delay(clock=4M)
#define CA1 PIN_B4 //Pin lái cực dƣơng led hàng nghìn
#define CA2 PIN_B5 //Pin lái cực dƣơng led hàng trăm
#define CA3 PIN_B6 //Pin lái cực dƣơng led hàng chục
#define CA4 PIN_B7 //Pin lái cực dƣơng led hàng đv.
#define LOW output_low

#define HIG output_high
unsigned char ma_7doan[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xd8,0x80,0x90}; //Mã led 7
đoạn
unsigned char digit[4]; //Mảng nhớ mã hiển thị cho từng led 7 đoạn.
float temp;
unsigned int8 doC, count=250;
void hien_thi()
{
HIG(CA1);
output_c(digit[0]);
delay_ms(1);
LOW(CA1);
HIG(CA2);
output_c(digit[1]);
delay_ms(1);
LOW(CA2);
HIG(CA3);
output_c(digit[2]);
delay_ms(1);
LOW(CA3);
HIG(CA4);


output_c(digit[3]);
delay_ms(1);
LOW(CA4);
}
void main()
{
set_tris_c(0x00);

setup_adc(adc_clock_internal);
setup_adc_ports(sAN0);
set_adc_channel(0);
delay_us(20);
while(true)
{
/*
Tạo bộ định thời 1s
Ta bỏ qua thời gian xử lý từng dòng lệnh thì tổng thời gian khi thực hiện hết
đoạn chƣơng trình là 4ms do hàm delay_ms(1) ở hàm hien_thi().
Vậy để định thời 1s ta cần đếm 250 lần.
*/
if(count<250) count++;
else
{
/*
Đọc adc và chuyển giá trị adc sang nhiệt độ.
Cứ 10mV thì đƣợc 1 độ C. Tầm toàn thang Vref= 5V= 5000mV thì đo đƣợc 500 độ.
Vậy ta xem nhƣ tầm toàn thang Vref= 500 thì công thức tính nhiệt độ nhƣ sau:
to=(Vref*adc)/1023=(500*read_adc())/1023;
*/
temp=0.5+((500.0*read_adc())/1023.0); //Cộng 0.5 là làm tròn đến hàng đơn vị.
doC=(unsigned int8) temp; //Lấy phần nguyên của nhiệt độ.
count=0; //Reset biến đếm.
}
if(doC<100)//Hiển thị nhiệt độ khi đo đƣợc dƣới 100 độ.
{
digit[0]=ma_7doan[doC/10];
digit[1]=ma_7doan[doC%10];
digit[2]=0x9c; //Kí hiệu độ.

digit[3]=0xc6; //Chữ "C"
}
else //Hiển thị lỗi khi đo đƣợc từ 100 độ trở lên.
{
digit[0]=0x86; //Chữ "E".
digit[1]=0xaf; //Chữ "r".
digit[2]=0x2f; //Chữ "r.".
digit[3]=0xff; //Không hiển thị.
}
hien_thi();
}


}

BÀI 3: ANALOG INPUT
ADC 12 BIT MCP3204
I. Giới thiệu:
- Vi điều khiển dòng PIC16, thƣờng trang bị module ADC tối đa 10 bit, với những ứng dụng đòi hỏi độ phân
giải cao hơn, ta có thể
sử dụng IC ADC chuyên dụng để kết nối với PIC. Bài viết hôm nay, mình xin giới thiệu IC MCP3204 là IC
ADC 12 bit do Microchip sản
xuất.
- Thông số kỹ thuật:
+ 12 bit ADC.
+ 4 kênh ngỏ vào analog.
+ Cho phép sử dụng áp tham chiếu khác áp nguồn.
+ Giao tiếp 4 dây theo kiểu giao tiếp SPI - sẽ tìm hiểu kỹ các phần sau, nhƣng về cơ bản ta cần kết nối IC với
PIC bằng 4 IO
bất kỳ với các chức năng: IO tạo xung clock, IO cho phép, IO dữ liệu vào, IO dữ liệu ra. Về bản chất, giữa PIC

và IC ADC chỉ
truyền nhận tín hiệu số.
+ Nguồn nuôi 5VDC.
II. Lập trình:
CCS đã hỗ trợ sẵn thƣ viện giao tiếp nên ta không cần phải "sáng tạo" làm gì.
Thƣ viện chứa tại: "C:\Program Files\PICC\Drivers\mcp3204.c"
1. Khai báo tiền xử lý:
#define MCP3204_CLK PIN_B0 //IO xung nhịp
#define MCP3204_DIN PIN_B1 //IO ghi dữ liệu


#define MCP3204_DOUT PIN_B2 //IO nhận dữ liệu
#define MCP3204_CS PIN_B3 //IO chọn chip
#include<MCP3204.c>
2. Khai báo config:
adc_init();
3. Đọc dữ liệu:
float value=read_analog_mcp(i,1);// i là channel từ 0-3.
4. VD mẫu:
Viết chƣơng trình đọc điện áp 0-5000mV dùng MCP3204 hiển thị LCD 16x4. Thời gian cập nhật giá trị là 1s.
#include <16F723A.h>
#fuses HS, NOWDT, PROTECT, NOMCLR
#use delay(clock=20M)
/*Giao tiếp LCD*/
#define LCD_RS PIN_C0
#define LCD_EN PIN_C1
#define LCD_D4 PIN_C2
#define LCD_D5 PIN_C3
#define LCD_D6 PIN_C4
#define LCD_D7 PIN_C5

/*Giao tiếp MCP3204*/
#include<LCD1604.c>
#define MCP3204_CLK PIN_B0
#define MCP3204_DIN PIN_B1
#define MCP3204_DOUT PIN_B2
#define MCP3204_CS PIN_B3
#include<MCP3204.c>
unsigned char string[17];
unsigned int32 vol;
float temp;
void main()
{
LCD_Init();
adc_init();
delay_ms(100);
while(true)
{
for(int i=0;i<4;i++)
{
temp=read_analog_mcp(i,1); //Đọc giá trị ADC ở channel thứ i.
temp=0.5+((temp*5000)/4095); //Tính giá trị điện áp (mV) và làm tròn đến hàng đv.
vol=(unsigned int32)temp; //Lấy phần nguyên của giá trị.
sprintf(string,"CH%d=%04ld mV",i,vol);
LCD_Gotoxy(0,i);
LCD_Puts(string);
}
delay_ms(1000);
}
}



[HƯỚNG DẪN LẬP TRÌNH PIC CĂN BẢN]
BÀI 4: TIMER - BỘ ĐỊNH THỜI
I. Giới thiệu:
- Tốc độ xử lý của PIC16 tối đa là 5 MIPS (Million Instructions Per Second) tức 5 triệu
lệnh mỗi giây ứng với nguồn cấp xung 20MHz.
- PIC16 có tốc độ core xử lý (fcore) bằng ¼ tốc độ nguồn cấp xung nhịp (fosc). Tức xung
nhịp cấp cho PIC hoạt động đƣợc đƣa qua bộ chia 4 trƣớc khi đƣa vào core.

Bộ định thời của PIC16 hoạt động dựa trên xung nhịp core xử lý. Tuy nhiên, PIC
không dùng trực tiếp xung nhịp của core để định thời mà đƣa nó qua một bộ chia
trƣớc gọi là prescaler. Sau khi qua bộ chia trƣớc, xung nhịp mới đƣợc cấp vào thanh
ghi định thời. Vậy xung nhịp từ nguồn xung đến đƣợc thanh ghi định thời có tốc độ
là:

Cứ 1 xung nhịp (ftimer) thì giá trị thanh ghi tăng lên 1. Khi thanh ghi đạt giá trị max mà
tiếp tục có xung nhịp thì cờ tràn định thời sẽ đƣợc set.
II. Phần cứng:
- PIC16 thƣờng có 3 timer: Timer 0, 1, 2. Định thời ta thƣờng dùng timer0 và 1, timer 2
dùng cho PWM.
- Thông số các timer:

III. Phần mềm:
1. Khai báo tiền xử lý:
Không có.
2. Khai báo ngắt:
#INT_TIMERx


void chuong_trinh_ngat()

 Chƣơng trình ngắt là đoạn chƣơng trình sẽ thực thi khi cờ ngắt đƣợc set. Lúc này,
timer dừng đếm, các chƣơng trình trong void main sẽ đƣợc sao lƣu và core xử lý
sẽ xử lý nội dung trong chƣơng trình ngắt, khi thực hiện xong thì xoá cờ ngắt, set
lại thanh ghi định thời, core xử lý quay về tiếp tục xử lý chƣơng trình đƣợc
backup lúc đầu.
 Chƣơng trình ngắt chạy độc lập và ƣu tiên hơn chƣơng trình chính.
[HƯỚNG DẪN LẬP TRÌNH PIC CĂN BẢN]
3. Khai báo config:
setup_timer_x(Tx_INTERNAL|Tx_DIV_prescaler);// Cài đặt bộ chia trƣớc.
enable_interrupts(INT_TIMERx);//Cho phép ngắt timer.
enable_interrupts(GLOBAL);//Cho phép ngắt toàn cục.
4. Các lệnh khác:
disable_interrupts(INT_TIMERx);//Cấm ngắt timer.
disable_interrupts(GLOBAL);//Cấm ngắt toàn cục.
set_timerx(int8 value);//Cài đặt giá trị bắt đầu đếm của thanh ghi định thời.
//Không có hàm này thì mặc định đếm từ 0 đếm max.
5. VD mẫu:
Viết chƣơng trình tạo xung clock ở chân A0 với T=1s, sử dụng timer0, thạch anh
20MHz, bộ chia trƣớc tuỳ chọn sao cho thời gian tràn timer không lẻ.
#include <16F877.h>
#fuses HS, NOWDT
#use delay(clock=20M)
unsigned int16 count=0;
int1 i=0;
#INT_TIMER0
void ngat_tmr0()
{
set_timer0(5);
if(count<625) count++;
else

{
i=~i;
output_bit(PIN_A0,~i);
count=0;
}
}
void main()
{
setup_timer_0(T0_INTERNAL|T0_DIV_16);
enable_interrupts(INT_TIMER0);
enable_interrupts(GLOBAL);
while(true);
}
Chú ý: Các bạn chỉ cần nhớ các tính số lần tràn để định thời 1s:


[HƯỚNG DẪN LẬP TRÌNH PIC CĂN BẢN]
Với:
Count: số lần tràn timer để định thời 1s.
b: là số bit thanh ghi định thời.
n: là giá trị set thanh ghi timer bắt đầu đếm. n=[0;2b-1]. Ta chọn số này và
prescaler sao cho Count là 1 số nguyên.
Ở đây ta có số lần tràn định thời 1s sẽ là:
= ‫ܥ݋ݑ ݐ‬
20. 10‫݋‬
4.16. (2 − 5 − 1) = 1250
Mà đề bài yêu cầu T là 1s tức 500ms mức cao và 500ms mức thấp. Vậy mỗi mức
logic sẽ chiếm 1250/2 số count tức 625.



[HƯỚNG DẪN LẬP TRÌNH PIC CĂN BẢN]
BÀI 4: TIMER - BỘ ĐỊNH THỜI [TT1]
ĐỒNG HỒ ĐẾM NGƯỢC
I. Giới thiệu:
- Ứng dụng chức năng định thời của PIC, ta tạo ra bộ định thời 1 giây. Sau mỗi giây,
giá trị định thời sẽ giảm 1 đơn vị cho đến khi bằng 0 thì dừng đếm.
- Vì phụ thuộc vào tần số nguồn dao động (luôn có sai số), nên định thời chỉ gần đúng
chứ không mang tính thời gian thực.
II. Phần cứng:
Nhƣ hình bên:
III. Phần mềm:
#include <16F877.h>
#fuses HS, NOWDT
#use delay(clock=20M)
#define STA PIN_A0
#define DG1 PIN_C0
#define DG2 PIN_C1
#define HIG output_high
#define LOW output_low
#define OUT output_bit
unsigned char ma_7doan[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xd8,0x80,0x90};
unsigned int16 count=0;
unsigned int8 sec_count=0;
#INT_TIMER0
void ngat_tmr0()
{
set_timer0(5);
if(count<1250) count++;
else
{

if(sec_count>0) sec_count--;
else
{
disable_interrupts(INT_TIMER0);
disable_interrupts(GLOBAL);
}
count=0;
}
}
void hien_thi()
{
HIG(DG1);
output_b(ma_7doan[sec_count/10]);
delay_ms(1);


[HƯỚNG DẪN LẬP TRÌNH PIC CĂN BẢN]
LOW(DG1);
HIG(DG2);
output_b(ma_7doan[sec_count%10]);
delay_ms(1);
LOW(DG2);
}
void nut_nhan()
{
if(input(STA)==0)
{
sec_count=10;
enable_interrupts(INT_TIMER0);
enable_interrupts(GLOBAL);

while(input(STA)==0);
}
}
void main()
{
set_tris_a(0x01);
set_tris_b(0x00);
setup_timer_0(T0_INTERNAL|T0_DIV_16);
disable_interrupts(INT_TIMER0);
disable_interrupts(GLOBAL);
while(true)
{
nut_nhan();
hien_thi();
}
}


×