32
code mẫu cho 8051 - Đo nhiệt độ, hiển thị LCD, truyền dữ liệu qua RS232 Đƣợc đăng bởi NetVNThứ bảy, ngày 25 tháng hai năm 20120 nhận xét Chƣơng trình thực hiện kết hợp đo nhiệt độ, hiển thị lên LCD, và truyền dữ liệu lên máy tính qua giao tiếp RS232. Mô phỏng trên ISIS - Proteus: mô phỏng trên ISIS - Proteus Mã nguồn C: #include<at89x51.h> #include<conio.h> #include<stdio.h> #include<string.h> //========================= #DEFINE LCD #define RS P3_5 #define RW P3_6//RW=0 => ghi #define EN P3_7//RW=1 => doc //RS=0 => code //RS=1 => data #define LCD_PORT P1 //========================= #DEFINE ADC #define INTR P3_2 #define RD P3_3 #define WR P3_4 //======================================================== ====== DELAY void delay_ms(int n) { int k,j; for(k=0;k<n;k++) { for(j=0;j<500;j++);

Ứng Dụng 89C51 Và Pic 16F877 Đo nhiệt độ, hiển thị LCD, truyền dữ liệu qua RS232

  • Upload
    say-kim

  • View
    835

  • Download
    10

Embed Size (px)

Citation preview

Page 1: Ứng Dụng 89C51 Và Pic 16F877 Đo nhiệt độ, hiển thị LCD, truyền dữ liệu qua RS232

code mẫu cho 8051 - Đo nhiệt độ, hiển thị LCD, truyền dữ liệu qua RS232 Đƣợc đăng bởi NetVNThứ bảy, ngày 25 tháng hai năm 20120 nhận xét

Chƣơng trình thực hiện kết hợp đo nhiệt độ, hiển thị lên LCD, và truyền dữ liệu lên máy tính qua giao tiếp RS232.

Mô phỏng trên ISIS - Proteus:

mô phỏng trên ISIS - Proteus

Mã nguồn C:

#include<at89x51.h> #include<conio.h>

#include<stdio.h> #include<string.h>

//========================= #DEFINE LCD #define RS P3_5

#define RW P3_6//RW=0 => ghi #define EN P3_7//RW=1 => doc

//RS=0 => code

//RS=1 => data #define LCD_PORT P1

//========================= #DEFINE ADC #define INTR P3_2

#define RD P3_3 #define WR P3_4

//============================================================== DELAY

void delay_ms(int n) {

int k,j; for(k=0;k<n;k++)

{ for(j=0;j<500;j++);

Page 2: Ứng Dụng 89C51 Và Pic 16F877 Đo nhiệt độ, hiển thị LCD, truyền dữ liệu qua RS232

}

} //==========================

void delay_5ms() {

int i,j; for(i=0;i<250;i++)

for(j=0;j<4;j++){} }

//=========================== void delay_15ms()

{ int i,j;

for(i=0;i<250;i++) for(j=0;j<100;j++){}

}

//============================================================== LCD

//============================== GUI LENH CHO LCD void LCD_CODE(unsigned char c)

{ RS=0;//code

RW=0;//ghi LCD_PORT=c;

EN=1; EN=0;

delay_5ms(); }

//=============================== KHOI TAO LCD

void LCD_INIT() {

delay_15ms(); LCD_CODE(0x38); //che do 8bit,2 hang,kieu ky tu 5x8 diem anh.

LCD_CODE(0x0C); //hien thi man hinh,có con tro, con tro nhâp' nháy. LCD_CODE(0x01); // Xoa man hinh LCD

} //============================== IN KY TU

void LCD_DATA(unsigned char c) {

RS=1;//data RW=0;//ghi

LCD_PORT=c;

Page 3: Ứng Dụng 89C51 Và Pic 16F877 Đo nhiệt độ, hiển thị LCD, truyền dữ liệu qua RS232

EN=1; EN=0;

delay_5ms(); }

//=============================== IN CHUOI KY TU void LCD_STRING(unsigned char *s)

{ while(*s) //den NULL thi thoi

{ LCD_DATA(*s);

s++; }

} //========================================================

====== KHOI TAO RS232 void SetupSerial()

{

TMOD=0x20; /* timer 1 che do 2: 8-Bit tu dong nap lai. */ TH1=0xFD; /* toc do 9600 baud */

TL1=0xFD; SCON=0x52; /* Che do 1: 8-bit UART, cho phep truyen */

TR1=1; /* timer 1 run */ TI=0; /* co ngat nha^n.=0*/

RI=0; /* co ngat' truye^n =0*/ ES=1; /* cho phep ngat noi tiep */

} //========================================================

====== HIEN THI GIA TRI ADC LEN LCD void HIENTHI_ADC(unsigned char t)

{

unsigned char v; if(t<10)

LCD_DATA(t+48); else

if(t<100) {

LCD_DATA(t/10+48); LCD_DATA(t%10+48);

} else

{ v=t/10;

LCD_DATA(v/10+48);

Page 4: Ứng Dụng 89C51 Và Pic 16F877 Đo nhiệt độ, hiển thị LCD, truyền dữ liệu qua RS232

LCD_DATA(v%10+48); LCD_DATA(t%10+48);

} }

//============================================================== HAM GUI SO LIEU LEN VB

void send(unsigned char a) {

if(a<10) {

SBUF=a+48; while(TI==0){}

TI=0; }

if(a>9&&a<100) {

unsigned char c=a/10;

unsigned char d=a%10; SBUF=c+48;

while(TI==0){} TI=0;

SBUF=d+48; while(TI==0){}

TI=0; }

if(a>99) {

unsigned char t=a/100; unsigned char c=a/10-10*t;

unsigned char d=a%10;

SBUF=t+48; while(TI==0){}

TI=0; SBUF=c+48;

while(TI==0){} TI=0;

SBUF=d+48; while(TI==0){}

TI=0; }

} //========================================================

====== NGAT NOI TIEP

Page 5: Ứng Dụng 89C51 Và Pic 16F877 Đo nhiệt độ, hiển thị LCD, truyền dữ liệu qua RS232

void RS232(void) interrupt 4 {

unsigned char *c,count=0; while(RI==0){}

RI=0; *c=SBUF;

if(count==1&&*c=='1') *c='3'; if(count==1&&*c=='2') *c='4';

switch(*c) {

case '0': //xoa man hinh LCD {

LCD_CODE(0x01); }

break; //o day co the su dung ma thap phan! (ngoai tru cac ky tu dieu khien)

case '-': //lui con tro hien thi LCD 1 don vi. {

LCD_CODE(0x10); }

break;

case '1': //truyen len may tinh: gia tri do duoc. {

send(P2); }

break;

case '2': //truyen len may tinh: gia tri do duoc.

{ send(P2);

} break;

case '9': //thoat khoi ham ngat, cho ADC làm viec.

{ TI=0; //cho phep thoat khoi ham ngat.

count=0; //ket thuc viec truyen ky tu len LCD }

break;

default : //truyen ky tu xuong LCD ^ .̂

Page 6: Ứng Dụng 89C51 Và Pic 16F877 Đo nhiệt độ, hiển thị LCD, truyền dữ liệu qua RS232

{ if(TI==0)

LCD_CODE(0x01); LCD_DATA(*c);

TI=1; //de cho chuong trinh ko thoat khoi ham ngat,phuc vu viec truyen ky tu len LCD.

count=1; //danh dau bat dau viec truyen ky tu len LCD }

} }

//============================================================== MAIN

void main() {

SetupSerial(); //Khoi tao cac thong so cho truyen thong noi tiep LCD_INIT();

//LCD_CODE(0x80);//hien thi 2 hang : dia chi hien thi + 80

EA = 1; //Cho phep ngat nhung chi? có ngat noi tiep duoc dung trong code nay

while(1) {

//if(P0_3==1) {

P0_0=P0_1=P0_2=0; WR=0; //Bat dau chuyen doi gia tri tu ADC

delay_ms(10); //Tao tre de cap nhat du lieu tu ADC WR=1; //Ket thuc chuyen doi

RD=0; //Chot du lieu da duoc chuyen doi: P2 LCD_CODE(0x01);

LCD_putstr("Nhiet do:");

HIENTHI_ADC(P2); LCD_STRING(" oC");

delay_ms(300); }

} }

Page 7: Ứng Dụng 89C51 Và Pic 16F877 Đo nhiệt độ, hiển thị LCD, truyền dữ liệu qua RS232
Page 8: Ứng Dụng 89C51 Và Pic 16F877 Đo nhiệt độ, hiển thị LCD, truyền dữ liệu qua RS232

Code mẫu cho PIC - Kết hợp: ADC đo nhiệt độ, điện áp, hiển thị LCD và giao tiếp với máy tính

Đƣợc đăng bởi NetVNThứ sáu, ngày 24 tháng hai năm 20120 nhận xét Chƣơng trình thực hiện đo nhiệt độ, điện áp, hiển thị kết quả lên màn hình LCD

16x2, và truyền giá trị lên máy tính. Mô phỏng trên Proteus:

mô phỏng trên Proteus

Mã nguồn:

#include <16f877a.h>

#include <def_877a.h> #device *=16 ADC=10

#use delay(clock=20000000) #FUSES NOWDT, HS, NOPUT, NOPROTECT, NODEBUG,

NOBROWNOUT, NOLVP, NOCPD, NOWRT

#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8)

#include <lcd_lib_4bit.c> //========================================================

=============== void send(int8 a);

void HIENTHI_LCD(int8 t); void HIENTHI_LCD_float(int8 t);

int8 count=0; int8 i,value,vl[10];

//=======================================================================

#INT_RDA //ngat khi nhan du lieu. Receive_isr()

{ char c;

Page 9: Ứng Dụng 89C51 Và Pic 16F877 Đo nhiệt độ, hiển thị LCD, truyền dữ liệu qua RS232

c=getc(); switch(c)

{ case '0': //xoa man hinh LCD

{ LCD_putcmd(0x01);

} break;

//o day co the su dung ma thap phan! (ngoai tru cac ky tu dieu khien) case '-': //lui con tro hien thi LCD 1 don vi.

{ LCD_putcmd(0x10);

} break;

case '1': //truyen len may tinh: gia tri do duoc. {

Set_ADC_channel(0); //kenh 0 chan so2

delay_us(10); for(i=0;i<10;i++)

{ vl[i]=read_adc();

delay_us(10); }

value=(vl[0]+vl[1]+vl[2]+vl[3]+vl[4]+vl[5]+vl[6]+vl[7]+vl[8]+vl[9])/20.48;

send(value); }

break; case '2': //truyen len may tinh: gia tri do duoc.

{

Set_ADC_channel(1); //kenh 1 chan so3 delay_us(10);

if(read_adc()==0) {

for(i=0;i<10;i++) {

vl[i]=read_adc(); delay_us(10);

} value=(vl[0]+vl[1]+vl[2]+vl[3]+vl[4]+vl[5]+vl[6]+vl[7]+vl[8]+vl[9])/

10; }

else

Page 10: Ứng Dụng 89C51 Và Pic 16F877 Đo nhiệt độ, hiển thị LCD, truyền dữ liệu qua RS232

{ value=read_adc()+6;

for(i=0;i<10;i++) {

vl[i]=read_adc()+6; delay_us(10);

} value=(vl[0]+vl[1]+vl[2]+vl[3]+vl[4]+vl[5]+vl[6]+vl[7]+vl[8]+vl[9])/

10; }

send(value); }

break; case '9': //thoat khoi ham ngat, cho ADC làm viec.

{ count=0; //ket thuc viec truyen ky tu len LCD

LCD_putcmd(0x01);

} break;

default : //truyen ky tu xuong LCD ^ .̂ {

count++; if(count==1) LCD_putcmd(0x01);

if(count==16) LCD_putcmd(0xC0); if(count==32)

{ LCD_putcmd(0x01);

count=0; }

LCD_putchar(c);

} }

} //========================================================

=============== void main()

{ //trisD=0x0C; //D0,D1 LA CONG VAO, D2-D7 LA CONG RA.

enable_interrupts(int_rda); //cho phep ngat noi tiep nhan.

enable_interrupts(GLOBAL);

Page 11: Ứng Dụng 89C51 Và Pic 16F877 Đo nhiệt độ, hiển thị LCD, truyền dữ liệu qua RS232

LCD_init(); //Khoi tao LCD. LCD_putcmd(0xC0);

Printf(LCD_putchar,"Khoi tao..."); delay_ms(250);

setup_adc_ports(AN0_AN1_AN3); //Khoi tao che do cho bo ADC.

setup_adc(ADC_CLOCK_INTERNAL); delay_us(10);

while(1)

{ //do nhiet do

{ Set_ADC_channel(0); //kenh 0 chan so2

delay_us(10); value=read_adc(); value=value/2.048;

LCD_putcmd(0x80);

LCD_putchar("Nhiet do: "); LCD_putcmd(0x89);

HIENTHI_LCD(value); LCD_putcmd(0x8C);

LCD_putchar("oC"); delay_ms(0.1);

} //do diep ap

{ Set_ADC_channel(1); //kenh 1 chan so3

delay_us(10);

if(read_adc()==0) {

for(i=0;i<10;i++) {

vl[i]=read_adc(); delay_us(10);

} value=(vl[0]+vl[1]+vl[2]+vl[3]+vl[4]+vl[5]+vl[6]+vl[7]+vl[8]+vl[9])/

10; }

else {

value=read_adc()+6;

Page 12: Ứng Dụng 89C51 Và Pic 16F877 Đo nhiệt độ, hiển thị LCD, truyền dữ liệu qua RS232

for(i=0;i<10;i++) {

vl[i]=read_adc()+6; delay_us(10);

} value=(vl[0]+vl[1]+vl[2]+vl[3]+vl[4]+vl[5]+vl[6]+vl[7]+vl[8]+vl[9])/

10; }

LCD_PutCmd(0xC0);

LCD_putchar("DIEN AP : "); LCD_putcmd(0xC9);

//HIENTHI_LCD(value); HIENTHI_LCD_float(value);

LCD_putcmd(0xCD); LCD_putchar("V ");

}

} }

//=======================================================================

void HIENTHI_LCD(int8 t) {

unsigned char v; if(t<10)

LCD_putchar(t+48); else

if(t<100) {

LCD_putchar(t/10+48);

LCD_putchar(t%10+48); }

else {

v=t/10; LCD_putchar(v/10+48);

LCD_putchar(v%10+48); LCD_putchar(t%10+48);

} }

void send(int8 a) {

if(a<10)

Page 13: Ứng Dụng 89C51 Và Pic 16F877 Đo nhiệt độ, hiển thị LCD, truyền dữ liệu qua RS232

{ putc(a+48);

} if(a>9&&a<100)

{ unsigned char c=a/10;

unsigned char d=a%10; putc(c+48);

putc(d+48); }

if(a>99) {

unsigned char t=a/100; unsigned char c=a/10-10*t;

unsigned char d=a%10; putc(t+48); putc(c+48);

putc(d+48); }

} void HIENTHI_LCD_float(int8 t)

{ int8 v;

if(t<10) {

LCD_putchar(48); LCD_putchar(46);

LCD_putchar(t+48); LCD_putchar(32);

}

else if(t<100)

{ LCD_putchar(t/10+48);

LCD_putchar(46); LCD_putchar(t%10+48);

LCD_putchar(32); }

else {

v=t/10; LCD_putchar(v/10+48);

LCD_putchar(v%10+48);

Page 14: Ứng Dụng 89C51 Và Pic 16F877 Đo nhiệt độ, hiển thị LCD, truyền dữ liệu qua RS232

LCD_putchar(46); LCD_putchar(t%10+48);

} }

/* void send_float(float a)

{ if(a<10)

{ putc(a+48);

} if(a>9&&a<100)

{ unsigned char c=a/10;

unsigned char d=a%10; putc(c+48);

putc(d+48);

} if(a>99)

{ unsigned char t=a/100;

unsigned char c=a/10-10*t; unsigned char d=a%10;

putc(t+48); putc(c+48);

putc(d+48); }

}*/ //========================================================

===============

Page 15: Ứng Dụng 89C51 Và Pic 16F877 Đo nhiệt độ, hiển thị LCD, truyền dữ liệu qua RS232
Page 16: Ứng Dụng 89C51 Và Pic 16F877 Đo nhiệt độ, hiển thị LCD, truyền dữ liệu qua RS232

Lập Trình RoBot Tự Động Đơn Giản Với VĐK PIC16F877A Đƣợc đăng bởi NetVNThứ hai, ngày 27 tháng hai năm 20120 nhận xét

Tóm tắt

Tài liệu hƣớng dẫn lập trình cho robot tự động dò đƣờng theo vạch trắng và điều khiển các cơ cấu (nâng hạ, gắp nhả quà) một cách cơ bản nhất.Vi điều khiển

đƣợc sử dụng trong tài liệu là PIC16F877A của Microchip.Lập trình bằng ngôn ngữ C với trình biên dịch CCS.

1 TÓM TẮT VỀ THIẾT KẾ ROBOT TỰ ĐỘNG

Robot tự động trong các cuộc thi Robocon gồm 3 thành phần chính: Cơ khí, Mạch điện tử, Lập trình.

1.1 Cơ khí

Một robot đơn giản gồm 2 động cơ truyền động cho 2 bánh xe bên trái và bên phải giúp robot di chuyển. Phía trƣớc có thể là 1 hoặc 2 bánh tự do (bánh tự lựa, omni, mắt trâu,…). Để thực hiện đƣợc các công việc nhƣ nâng hạ

trục, gắp nhả đẩy quá, robot đƣợc trang bị thêm các động cơ khác để truyền động cho các cơ cấu này.

Tất cả các bộ phận trên đƣợc bố trí trên một khung bằng nhôm, sắt,… Phần hƣớng dẫn chi tiết về thiết kế cơ khí sẽ đƣợc trình bày trong một tài liệu

khác. Tài liệu này chỉ tập trung vào phần lập trình.

Mô hình robot dò dường đơn giản

1.2 Mạch điện tử

Page 17: Ứng Dụng 89C51 Và Pic 16F877 Đo nhiệt độ, hiển thị LCD, truyền dữ liệu qua RS232

Sơ đồ hoạt động của robot tự động

1.2.1 Mạch ngõ vào (cảm biến, nút ấn, công tắc hành trình) Với robot đơn giản, ngõ vào thƣờng là mức logic lấy từ cảm biến quang (quang

trở, quang diode), nút ấn hoặc công tắc hành trình. Từ đó mạch vi điều khiển xử lý các tín hiệu này để xuất ngõ ra (thƣờng là động cơ DC) chophù hợp.

Cảm biến quang phải đƣợc che chắn cẩn thận để hạn chế ảnh hƣởng từ cácnguồn ánh sáng bên ngoài.

Mạch cảm biến, nút ấn và công tắc hành trình

1.2.2 Mạch vi điều khiển Mạch sử dụng vi điều khiển PIC16F877A của Microchip. Mạch nhận tín hiệu từ ngõ vào, xử lý và xuất ngõ ra qua một mạch cách ly bằng opto ra mạch công

suất.

Page 20: Ứng Dụng 89C51 Và Pic 16F877 Đo nhiệt độ, hiển thị LCD, truyền dữ liệu qua RS232

Mạch cầu H điều khiển động cơ với Half Bridge Driver IR2184

1.3 Lập trình

Đây là phần chính của tài liệu này. Ngôn ngữ lập trình đƣợc sử dụng là C, với trình biên dịch CCS cho vi điều khiển PIC của Microchip.

Kiến thức ban đầu: Lập trình C căn bản Các tài liệu tham khảo:

Tài liệu CCS tiếng Việt.

Đây là các tài liệu cần đọc qua trƣớc khi vào lập trình cho PIC để có thể biết

các hàm nhận tín hiệu ngõ vào, xuất tín hiệu ngõ ra, ngắt, timer, counter, PWM,… Các hàm quan trọng sẽ đƣợc nhắc lại ở phần 2.

2 LẬP TRÌNH CHO ROBOT TỰ ĐỘNG DÒ ĐƢỜNG ĐƠN GIẢN

Chƣơng trình giúp robot chạy theo vạch trắng trên nền màu sậm.

2.1 Phần cứng

8 cảm biến quang dò đƣờng

Mạch công suất điều khiển 2 động cơ

Mạch vi điều khiển:

o PORTD: nối với tín hiệu ra của 8 cảm biến o Động cơ trái:

Chân C0: điều khiển chiều (DIR_LEFT)

Chân C1: điều khiển cho phép chạy (EN_LEFT)

o Động cơ phải:

Chân C3: điều khiển chiều (DIR_RIGHT)

Chân C2: điều khiển cho phép chạy (EN_RIGHT)

(Các ngõ vào và ngõ ra có thế nối với bất cứ PORT và chân nào của vi điều khiển)

2.2 Nguyên tắc điều khiển

2.2.1 Điều khiển động cơ

Tƣơng tự đối với các động cơ điều khiển các cơ cấu.

Page 21: Ứng Dụng 89C51 Và Pic 16F877 Đo nhiệt độ, hiển thị LCD, truyền dữ liệu qua RS232

2.2.2 Hƣớng di chuyển của robot

2.2.3 Xử lý tín hiệu cảm biến (xem hình vẽ)

Mục đích của việc dò đƣờng là hƣớng cho robot đi theo 1 vạch thẳng màu trắng trên một nền màu đậm (đen, xanh,…)

Cảm biến đƣợc đặt ở giữa robot. o Khi cảm biến số 3,4 nằm trên vạch trắng (mức 1): robot chạy thẳng

o Khi robot lệch sang trái: quay phải để điều chỉnh robot về đúng vạch o Khi robot lệch sang phải: quay trái để điều chỉnh robot về đúng vạch

Các mức độ lệch ra khỏi vạch trắng của robot

2.3 Chƣơng trình điều khiển

Page 22: Ứng Dụng 89C51 Và Pic 16F877 Đo nhiệt độ, hiển thị LCD, truyền dữ liệu qua RS232

#include <16f877A.h>

#include <def_877A.h> #fuses HS,NOWDT,NOPROTECT,NOLVP

#use delay(clock=20000000) /* ĐỊNH NGHĨA CÁC CHÂN VÀ PORT */

#define DIR_LEFT RC0 #define EN_LEFT RC1

#define DIR_RIGHT RC3 #define EN_RIGHT RC2

#define SENSOR PORTD

/* KHAI BÁO CÁC CHƢƠNG TRÌNH CON */ void motor_left_forward();

void motor_left_reverse(); void motor_left_stop(); void motor_right_forward();

void motor_right_reverse(); void motor_right_stop();

void forward(); void reverse();

void stop(); void turn_left();

void turn_right(); /* CÁC CHƢƠNG TRÌNH CON */

// Động cơ trái chạy thuận void motor_left_forward()

{ DIR_LEFT=1; // chiều thuận EN_LEFT=1; // cho phép chạy

} // Động cơ trái chạy ngƣợc

void motor_left_reverse() {

DIR_LEFT=0; // chiều ngƣợc EN_LEFT=1; // cho phép chạy

} // Động cơ trái dừng

void motor_left_stop() {

EN_LEFT=0; // không cho phép chạy }

// Động cơ phải chạy thuận

Page 23: Ứng Dụng 89C51 Và Pic 16F877 Đo nhiệt độ, hiển thị LCD, truyền dữ liệu qua RS232

void motor_right_forward() {

DIR_RIGHT=1;// chiều thuận EN_RIGHT=1; // cho phép chạy

} // Động cơ phải chạy ngƣợc

void motor_right_reverse() {

DIR_RIGHT=0;// chiều ngƣợc EN_RIGHT=1; // cho phép chạy

} // Động cơ phải dừng

void motor_right_stop() {

EN_RIGHT=0; // không cho phép chạy } // Chạy thẳng

void forward() {

motor_left_forward(); motor_right_forward();

} // Chạy lùi

void reverse() {

motor_left_reverse(); motor_right_reverse();

} // Dừng void stop()

{ motor_left_stop();

motor_right_stop(); }

// Quay trái void turn_left()

{ motor_left_forward();

motor_right_reverse(); // hoặc motor_right_stop(); }

// Quay phải void turn_right()

{

Page 24: Ứng Dụng 89C51 Và Pic 16F877 Đo nhiệt độ, hiển thị LCD, truyền dữ liệu qua RS232

motor_left_reverse(); // hoặc motor_left_stop(); motor_right_forward();

} /* CHƢƠNG TRÌNH CHÍNH */

void main () {

TRISC=0x00; // PORTC là ngõ ra ( động cơ) TRISD=0x00; // PORTD là ngõ vào (cảm biến quang)

PORTC=0x00; // Khởi tạo giá trị ban đầu 0x00 cho PORTC while(1)

{ switch (SENSOR)

{ case 0b00011000: forward(); break;

case 0b00001100: turn_left(); break; case 0b00000110: turn_left(); break; case 0b00000011: turn_left(); break;

case 0b00000001: turn_left(); break; case 0b00110000: turn_right(); break;

case 0b01100000: turn_right(); break; case 0b11000000: turn_right(); break;

case 0b10000000: turn_right(); break; }

} }

3 CẢI TIẾN CHƢƠNG TRÌNH DÒ ĐƢỜNG 3.1 Điều khiển tốc độ động cơ với các trạng thái lệch khỏi vạch trắng 3.1.1 Nguyên lý

Đối với chƣơng trình dò đƣờng đơn giản, khi robot lệch trái hoặc lệch phải, robot sẽ quay phải hoặc quay trái để điều chỉnh cho dù lệch ít hay lệch

nhiều. Nhƣ vậy, trong quá trình di chuyển, robot sẽ lắc liên tục vì phải quay trái, quay phải liên tục. Do đó, với các mức độ lệch ra khỏi vạch trắng khác nhau, ta

điều chỉnh tốc độ 2 bánh trái, phải cho phù hợp để quá trình di chuyển theo vạch của robot đƣợc “nhuyễn” và “mƣợt” hơn.

Bảng giá trị tham khảo tốc độ động cơ trái và động cơ phải tƣơng ứng với các trạng thái lệch khỏi vạch trẳng của robot:

Page 25: Ứng Dụng 89C51 Và Pic 16F877 Đo nhiệt độ, hiển thị LCD, truyền dữ liệu qua RS232

3.1.2 Điều khiển tốc độ động cơ DC bằng phƣơng pháp PWM Đối với điều khiển tốc độ động cơ DC trong robot, phƣơng pháp đƣợc sử dụng phổ biến nhất là điều chế độ rộng xung (Pulse Width Modulation) hay

đƣợc gọi tắt là điều xung, băm xung hoặc PWM. Nguyên lý của phƣơng pháp này là bật tắt nhanh nguồn điện cấp vào động

cơ tạo ra một tín hiệu xung. Khi việc bật tắt ở tần số đủ lớn (thƣờng sử dụng từ 1kHz đến 20kHz), động cơ sẽ chạy với 1 tốc độ ổn định nhờ moment quay.

Thời gian cấp nguồn cho động cơ là T-on, thời gian tắt nguồn động cơ là T-off. Việc thay đổi thời gian T-on và T-off làm thay đổi điện áp hiệu dụng cấp

cho động cơ. Đối với động cơ DC, tốc độ động cơ tƣơng đối tỉ lệ thuận với điện áp cấp cho động cơ. Vì vậy, bằng cách thay đổi độ rộng của xung, ta đã thay

đổi đƣợc tốc độ của động cơ DC. Đại lƣợng biểu diễn mối quan hệ giữa T-on và T-off đƣợc gọi là duty cycle:

duty_cycle = Ton / ( Ton + Toff ) Ví dụ: Ta cấp nguồn động cơ trong 0.8ms, sau đó tắt 0.2ms.

Nhƣ vậy: T-on = 0.8ms; T-off = 1ms. Tần số PWM là: f = 1 / ( Ton + Toff ) = 1 / ( 0.8ms + 0.2ms ) = 1/1ms = 1KHz

duty_cycle = Ton / ( Ton + Toff ) = 0.8 / ( 0.8 + 0.2 ) = 0.8 = 80%

Vì tốc độ động cơ DC tỉ lệ với duty cycle nên tốc độ động cơ đạt tƣơng đƣơng

80% tốc độ tối đa.

Tính toán duty cycle để điều khiển tốc độ động cơ DC

3.1.3 Điều xung PWM dùng vi điều khiển

Điều xung PWM bằng phần mềm:

Page 26: Ứng Dụng 89C51 Và Pic 16F877 Đo nhiệt độ, hiển thị LCD, truyền dữ liệu qua RS232

Điều xung PWM một cách đơn giản là đƣa 1 chân nào đó của vi điều khiển lên mức 1, sau đó đƣa xuống mức 0. Công việc này đƣợc lặp đi lặp lại liên tục sẽ

tạo ra xung, và tốc độ của động cơ sẽ tƣơng ứng với duty cycle. Ví dụ: Điều xung trên chân A0 :

Code RA0=1;

Delay_ms(Ton); RA0=0;

Delay_ms(Toff);

Tuy nhiên, nếu thực hiện bằng cách này thì vi điều khiển sẽ luôn dành thời gian cho việc điều xung PWM. Do đó, các công việc khác nhƣ nhận tín hiệu từ cảm

biến, điều khiển các cơ cấu sẽ bị ảnh hƣởng.

Điều xung PWM bằng phần cứng Để giải quyết vấn đề việc điều xung PWM bằng phần mềm chiếm phần lớn thời

gian hoạt động của vi điều khiển, PIC16F877A có hỗ trợ 2 kênh điều xung bằng phần cứng ở 2 chân C1 (CCP2) và C2(CCP1) sử dụng TIMER2. Nghĩa là, khi

ta khai báo điều xung PWM ở một tần số và duty cycle nào đó thì vi điều khiển sẽ thực hiện công việc xuất xung một cách liên tục và tự động cho đến khi ta

thay đổi các giá trị đã khai báo. Khi đó, ta có thể làm các công việc khác một cách dễ dàng mà không phải mất thời gian cho việc duy trì xung PWM.

Các hàm hỗ trợ việc điều xung bằng phần cứng của CCS:

Ghi chú: Chỉ đề cập đến các đối số của các hàm đƣợc phục vụ cho việc điều xung PWM. o setup_timer_2 (mode, period, postscale)

mode: T2_DIV_BY_1, T2_DIV_BY_4, T2_DIV_BY_16

period: 0-255 postscale: 1

Tần số điều xung PWM:

f = fosc / [ 4*mode*(period+1) ] o setup_ccp1(mode) và setup_ccp2(mode)

mode: CCP_PWM: chọn chế độ PWM.

CCP_OFF: tắt chế độ PWM. o set_pwm1_duty(value) và set_pwm2_duty(value)

Nếu value là giá trị kiểu int 8bit:

duty_cycle = value / ( period+1 )

Nếu value là giá trị long int 16bit:

duty_cycle = value&1023 / [4*( period+1 )]

Page 27: Ứng Dụng 89C51 Và Pic 16F877 Đo nhiệt độ, hiển thị LCD, truyền dữ liệu qua RS232

Nếu không cần điều xung quá “mịn” thì nên điều xung ở giá trị value 8bit cho

đơn giản. Ví dụ: Ta muốn điều xung PWM với tần số 10kHz với tần số thạch anh (fosc) sử dụng là 20MHz (value 8bit).

f=fosc/[4*mode*(period+1)] <=> 10000 =20000000/[ 4*mode*(period+1) ]

<=> mode(period+1) = 500 Với mode = [1,4,16] và period = 0-255 ta có thể chọn:

o mode = 4; period = 124 o mode = 16; period = 32

Để cho việc điều xung đƣợc “mịn” (chọn đƣợc nhiều giá trị duty cycle) ta chọn mode = 4 và period = 124.

Nhƣ vậy, để duty_cycle từ 0% đến 100% ta cho value từ 0 đến 125. o value = 30 => duty_cycle = 30 / ( 124+1 ) = 0.32 = 32% o value = 63 => duty_cycle = 63 / ( 124+1 ) = 0.504 = 50.4%

o value = 113 => duty_cycle = 113 / ( 124+1 ) = 0.904 = 90.4%

Code: setup_timer_2(T2_DIV_BY_4,124,1);

setup_ccp1(CCP_PWM); set_pwm1_duty(30);

Sử dụng CCP1 và CCP2 cho động cơ trái và động cơ phải, ta có thể điều khiển

đƣợc tốc độ của 2 động cơ phù hợp trạng thái lệch khỏi vạch trắng của robot.

Các chƣơng trình con tham khảo:

Để việc lập trình đƣợc dễ dàng, ta nên tạo các chƣơng trình con xử lý tốc độ. Sau đây là chƣơng trình tham khảo của hàm speed.

o Speed (tốc độ động cơ trái, tốc độ động cơ phải)

Tốc độ: -100 đến 100 (chạy ngƣợc 100% đến chạy thuận 100%)

Ví dụ: speed(80,60) => động cơ trái chạy 80%, phải 60%

// Các hàm hỗ trợ

void left_motor_forward(int value) { MOTOR_LEFT_DIR=0;

setup_timer_2(T2_DIV_BY_4,124,1); // Dieu xung 10kHz setup_ccp2(CCP_PWM);

set_pwm2_duty(value); }

void right_motor_forward(int value) {

MOTOR_RIGHT_DIR=0; setup_timer_2(T2_DIV_BY_4,124,1); // Dieu xung 10kHz

setup_ccp1(CCP_PWM); set_pwm1_duty(value);

Page 28: Ứng Dụng 89C51 Và Pic 16F877 Đo nhiệt độ, hiển thị LCD, truyền dữ liệu qua RS232

} void left_motor_reverse(int value)

{ MOTOR_LEFT_DIR=1;

setup_timer_2(T2_DIV_BY_4,124,1); // Dieu xung 10kHz setup_ccp2(CCP_PWM);

set_pwm2_duty(value); }

void right_motor_reverse(int value) {

MOTOR_RIGHT_DIR=1; setup_timer_2(T2_DIV_BY_4,124,1); // Dieu xung 10kHz

setup_ccp1(CCP_PWM); set_pwm1_duty(value);

} void left_motor_stop() {

setup_ccp1(CCP_OFF); }

void right_motor_stop() {

setup_ccp1(CCP_OFF); }

// Chƣơng trình xử lý tốc độ 2 động cơ // 0:Stop,100:FORWARD 100%,-100:Reverse 100%

void speed (signed int left_motor_speed, signed int right_motor_speed) {

int left_pwm_value=0,right_pwm_value=0; /* Left motor */ if( left_motor_speed >= 0 )

{ left_pwm_value = 1.25*left_motor_speed; // (125*left_motor_speed/100)

left_motor_forward(left_pwm_value); }

else {

left_motor_speed = -left_motor_speed; left_pwm_value = 1.25*left_motor_speed; // (125*left_motor_speed/100)

left_motor_reverse(left_pwm_value); }

/* Right motor */ if( right_motor_speed >= 0 )

{

Page 29: Ứng Dụng 89C51 Và Pic 16F877 Đo nhiệt độ, hiển thị LCD, truyền dữ liệu qua RS232

right_pwm_value = 1.25*right_motor_speed; // (125*left_motor_speed/100)

right_motor_forward(right_pwm_value); }

else {

right_motor_speed = -right_motor_speed; right_pwm_value = 1.25*right_motor_speed; //

(125*left_motor_speed/100) right_motor_reverse(right_pwm_value);

} }

3.2 Nhận biết vạch ngang Trong quá trình di chuyển của robot, sẽ có các vạch ngang màu trắng. Nhờ các vạch ngang này, robot biết đƣợc mình đang đi đến đâu và sẽ thực hiện

công việc gì tiếp theo (quay trái, quay phải, nâng hạ trục, gắp nhả quà,…)

Một cách đơn giản, khi robot đến vạch trắng, tất cả 8 cảm biến sẽ lên mức

1 ứng với giá trị đọc đƣợc của cảm biến là 0b11111111 hay 0xFF. Ta dùng một

biến đếm để biết thứ tự của vạch ngang đang gặp và thực hiện công việc mong muốn.

Code: int n // đặt n là số vạch ngang đã nhận

if (SENSOR==0xFF) { n++; // tăng giá trị n lên 1 khi gặp vạch ngang

switch (n) {

case 1: (công việc 1) break; case 2: (công việc 2) break;

…. case n: (công việc n) break;

} }

else (chƣơng trình dò đƣờng)

Tuy nhiên, vì nhiều lý do khác nhau (nhiễu do ánh sáng bên ngoài, các cảm biến có độ nhạy không đều nhau hoặc robot tiếp cận với vạch ngang

không theo phƣơng vuông góc), một vài cảm biến khi tiếp xúc với màu trắng nhƣng không nhận biết (vẫn giữ trạng thái mức 0 – màu sậm). Điều này

gây khó khăn cho việc nhận biết vạch ngang. Vì vậy, khi 4/8 cảm biến ở mức 1, ta nhận đó là một vạch ngang để khắc phục các ảnh hƣởng này.

Page 30: Ứng Dụng 89C51 Và Pic 16F877 Đo nhiệt độ, hiển thị LCD, truyền dữ liệu qua RS232

Code: //KIEM TRA VACH NGANG: 1: co vach, 0:khong co vach

int check_cross_line() {

int temp_sensor=0,led_in_line=0,i=0; temp_sensor=SENSOR;

for (i=0;i<8;i++) {

if ((temp_sensor&0x01)==0x01) led_in_line++; if (led_in_line==4) break;

temp_sensor=temp_sensor>>1; }

if (led_in_line==4) return 1;

else return 0; }

Do chƣơng trình nhận vạch ngang đƣợc gọi liên tục để kiểm tra có vạch ngang

xuất hiện hay không nên sẽ dẫn đến tình trạng khi robot đến vạch ngang, biến

đếm số vạch tăng thêm 1. Sau đó, khi robot chƣa kịp chạy qua vạch ngang mà hàm kiểm tra đƣợc gọi dẫn đến việc biến đếm tăng liên tục.

Việc này khiến cho robot thực hiện sai công việc. Hƣớng giải quyết tình huống này nhƣ sau:

o Khi robot gặp vạch ngang: chạy thẳng o Khi hết vạch ngang: biến đếm số vạch tăng thêm 1 o Thực hiện công việc tƣơng ứng

Code: while (check_cross_line() == 1) // gặp vạch ngang

{ speed(100,100); // chạy thẳng

} number_cross_line++; // tăng biến đếm số vạch ngang thêm 1

3.3 Xử lý khi robot lệch hoàn toàn khỏi vạch

Trƣờng hợp robot lệch ra khỏi vạch (quay quá mạnh hoặc bị va chạm), giá trị cảm biến đọc đƣợc là 0x00, nhƣ vậy robot sẽ bị mất phƣơng hƣớng.

Để giải quyết trƣờng hợp này, ta đặt một biến trạng thái là biến toàn cục. Gọi biến này là line_status:

o Line_status=0: giữa vạch; o Line_status=1: lệch trái; o Line_status=2: lệch phải;

Page 31: Ứng Dụng 89C51 Và Pic 16F877 Đo nhiệt độ, hiển thị LCD, truyền dữ liệu qua RS232

Khi đọc giá trị cảm biến để dò đƣờng , ta gán luôn giá trị cho biến này. Nhƣ vậy khi robot lệch hẳn ra khỏi vạch, ta vẫn biết đƣợc trạng thái trƣớc đó để biết quay

trái hoặc quay phải để hƣớng robot di chuyển về phía line.

Code switch (SENSOR)

{ case 0b00000000: // lệch ra hẳn khỏi vạch

{ if (line_status==1) // trạng thái cũ là lệch trái

turn_right(); break; // quay phải để di chuyển về phía vạch if (line_status==2) // trạng thái cũ là lệch phải

turn_left(); break; // quay trái để di chuyển về phía vạch }

// Trạng thái lệch thông thƣờng case 0b00011000: forward(); line_status=0; break; case 0b00001100: turn_left(); line_status=1; break;

case 0b00000110: turn_left(); line_status=1; break; case 0b00000011: turn_left(); line_status=1; break;

case 0b00000001: turn_left(); line_status=1; break; case 0b00110000: turn_right(); line_status=2 ;break;

case 0b01100000: turn_right(); line_status=2 ;break; case 0b11000000: turn_right(); line_status=2 ;break;

case 0b10000000: turn_right(); line_status=2 ;break; }

3.4 Ứng dụng encoder

3.4.1 Kiến thức cơ bản về encoder Trong Robot, ta thƣờng sử dụng incremental encoder (encoder tƣơng đối) hay còn gọi là rotary encoder. Mục đích của việc sử dụng encoder trong robot là

đếm số vòng quay để tính số vòng quay của động cơ (bánh xe), từ đó suy ra quãng đƣờng di chuyển và tốc độ của robot.

Encoder thường được sử dụng trong Robot

3.4.2 Sử dụng PIC để nhận và đếm xung từ encoder

Page 32: Ứng Dụng 89C51 Và Pic 16F877 Đo nhiệt độ, hiển thị LCD, truyền dữ liệu qua RS232

Để nhận xung từ encoder, ta có thể sử dụng ngắt ngoài, ngắt timer hoặc đơn giản là tham dò mức logic của các chân vi điều khiển một cách liên

tục. Phần sau đây giới thiệu cách nhận và đếm xung của PIC16F877A dùng ngắt ngoài B0 (nối với kênh A của encoder) và chân B1 (nối với kênh B

của encoder). Ta có thể làm tƣơng tự đối với các cách nhận xung khác.

Khởi tạo ngắt ngoài theo cạnh lên tại chân B0:

Code:

ext_int_edge(0,L_TO_H); // Ngắt cạnh lên tại RB0 enable_interrupts(INT_EXT); // Cho phép ngắt ngoài

enable_interrupts(GLOBAL); // Cho phép ngắt toàn cục

Chƣơng trình con phục vụ ngắt:

Code: #int_EXT

void EXT_isr(void) //Chƣơng trình đƣợc gọi khi có tác động cạnh lên tại chân B0

{ if (RB1==1) pulse++; // Nếu kênh B mức cao thì tăng giá trị xung thêm 1

else pulse--; // Nếu kênh B mức cao thì giảm giá trị xung xuống 1 }

Từ giá trị xung tính đƣợc tại các thời điểm ta có thể tính ra các thông

số mong muốn.