PIC16F84 İle ADC Kullanarak Servo Motor Kontrol Uygulaması
Bu uygulamada PIC16F84 ve ADC0831 kullanarak hem bir ADC uygulaması
hem de servo motor kontrol uygulamasını beraber yapıyoruz. Bildiğiniz gibi 16F84'ün kendi ADC'si yok,
ee bize de 16F877'deki gibi 40 pin gerekmiyor ve içinde adc olan PIC12C671-72 Türkiye'de zor bulunabildiğine
göre (hatta yok!!) geriye ADC0831 gibi bi entegre kullanarak kendi ADC'mizi yapmak kalıyor. Siz zahmet
etmeyin diye ben uğraştım, yaptım ve oldu... Projede yaptığım uyguşama ise şöyle; bildiğiniz gibi
(ya da bilmiyosanız aşağıdaki linkleri ziyaret edin!!) servo motorlar konum kontrolü yapılabilen motorlar,
yani gönderdiğiniz sinyalin süresine göre döndürülebilen motorlar. Yaklaşık olarak 1ms süreli bir sinyal
gittiğinde tam sağ, 2ms sinyal gittiğinde tam sol (tersi de olabilir :)) tarafa dönen, arada bi değer gittiğinde
ise bu süreyle orantılı değerde dönen motorlardır. Peki ben ne yaptım? ADC'den standart bir potun değerini
okuyırak elde ettiğim değer kadar motoru döndürdüm. Bu tip bir uygulamayla elimizin altındaki potu döndürerek
motoru da aynı oaranda döndürmüş olduk. Mesela siz motora bi düzenekle Web-Cam bağlayıp istediğiniz gibi
döndürebilirsiniz...Uygulamanın yazılımını CCS PIC C dili ile oluşturdum. Kod aşagıdaki gibi..
#if defined(__PCM__)
#include <16F84A.h>
#fuses XT, NOPROTECT, NOWDT,PUT
#use delay(clock=4000000)
#define csenable PIN_A0 //CSENABLE PİNİ A0
#define clock PIN_A1 //CLOCK PİNİ A1
#define veri PIN_A2 //DO PİNİ A2
#define servo PIN_A3 //SERVO PİNİ A3
SET_TRIS_A(0b00000100); //DO PİNİ GİRİŞ
SET_TRIS_B(0b00000000); //LCD İÇİN HEPSİ ÇIKIŞ
//***************************************************
//***************************************************
//********* LCD AYARLARI ****************************
// B0 enable
// B1 rs
// B2 rw
// B4 D4
// B5 D5
// B6 D6
// B7 D7
#define use_portb_lcd TRUE
struct lcd_pin_map { // This structure is overlayed
BOOLEAN enable; // on to an I/O port to gain
BOOLEAN rs; // access to the LCD pins.
BOOLEAN rw; // The bits are allocated from
BOOLEAN unused; // low order up. ENABLE will
int data : 4; // be pin B0.
} lcd;
#if defined use_portb_lcd
#byte lcd = 6 // on to port B (at address 6)
#define set_tris_lcd(x) set_tris_b(x)
#endif
#define lcd_type 2 // 0=5x7, 1=5x10, 2=2 lines
#define lcd_line_two 0x40 // LCD RAM address for the second line
BYTE const LCD_INIT_STRING[4] = {0x20 | (lcd_type << 2), 0xc, 1, 6};
// These bytes need to be sent to the LCD
// to start it up.
// The following are used for setting
// the I/O port direction register.
struct lcd_pin_map const LCD_WRITE = {0,0,0,0,0}; // For write mode all pins are out
struct lcd_pin_map const LCD_READ = {0,0,0,0,15}; // For read mode data pins are in
BYTE lcd_read_byte() {
BYTE low,high;
set_tris_lcd(LCD_READ);
lcd.rw = 1;
delay_cycles(1);
lcd.enable = 1;
delay_cycles(1);
high = lcd.data;
lcd.enable = 0;
delay_cycles(1);
lcd.enable = 1;
delay_us(1);
low = lcd.data;
lcd.enable = 0;
set_tris_lcd(LCD_WRITE);
return( (high<<4) | low);
}
void lcd_send_nibble( BYTE n ) {
lcd.data = n;
delay_cycles(1);
lcd.enable = 1;
delay_us(2);
lcd.enable = 0;
}
void lcd_send_byte( BYTE address, BYTE n ) {
lcd.rs = 0;
while ( bit_test(lcd_read_byte(),7) ) ;
lcd.rs = address;
delay_cycles(1);
lcd.rw = 0;
delay_cycles(1);
lcd.enable = 0;
lcd_send_nibble(n >> 4);
lcd_send_nibble(n & 0xf);
}
void lcd_init() {
BYTE i;
set_tris_lcd(LCD_WRITE);
lcd.rs = 0;
lcd.rw = 0;
lcd.enable = 0;
delay_ms(15);
for(i=1;i<=3;++i) {
lcd_send_nibble(3);
delay_ms(5);
}
lcd_send_nibble(2);
for(i=0;i<=3;++i)
lcd_send_byte(0,LCD_INIT_STRING[i]);
}
void lcd_gotoxy( BYTE x, BYTE y) {
BYTE address;
if(y!=1)
address=lcd_line_two;
else
address=0;
address+=x-1;
lcd_send_byte(0,0x80|address);
}
void lcd_putc( char c) {
switch (c) {
case '\f' : lcd_send_byte(0,1);
delay_ms(2);
break;
case '\n' : lcd_gotoxy(1,2); break;
case '\b' : lcd_send_byte(0,0x10); break;
default : lcd_send_byte(1,c); break;
}
}
//********* LCD AYARLARI ****************************
//***************************************************
//***************************************************
int adc() //F84 İLE ADC OKUMA RUTİNİ
{
int i,digital; //tanımlama
//BU KISIM ADC0831 OKUMAK İÇİN
output_low(csenable);
delay_us(10);
for(i=0;i<=1;i++)
{
output_high(clock);
delay_us(1);
output_low(clock);
delay_us(1);
}
for(i=8;i>=1;i--)
{
shift_left(&digital,1,input(veri));
output_high(clock);
delay_us(1);
output_low(clock);
delay_us(1);
}
for(i=0;i<=1;i++)
{
output_high(clock);
delay_us(1);
output_low(clock);
delay_us(1);
}
delay_us(10);
output_high(csenable);
//OKUMA BİTTİ..........................
return (digital); //ADC DEĞERİ OLAN digital İ DÖNDÜR
}
void pulsout(long x) //SERVOYA PULS GÖNDERME
{
int y,z,i; //TANIMLAMA
x=x*10; //X İN 10 KATINI AL. NEDENİ 1000-2000 ARASI SAYI ÜRETMEK
y=x%250; //X İN 250 YE GÖRE MODUNU AL. KALAN Y YE ATANIYOR
z=(x-100)/250; //Z SAYISI X İN İÇİNDE KAÇ TANE 250 OLDUĞUNU TUTAR
output_high(servo); //SERVO PİNİNİ 1 YAP
for(i=1;i<=z;i++) //Z KADAR 250 us DÖN
{
delay_us(250);
}
delay_us(y); //BİR DE KALAN SAYISI Y us KADAR DÖN Kİ TAM DEĞERE ULAŞ
output_low(servo); //SERVO PİNİNİ 0 YAP
}
void main() //ANA PROGRAM
{
int digital_v,i; //TANIMLAMA
lcd_init(); //LCD Yİ HAZIRLA
OUTPUT_A(0b00000000); //A PROTUNU TEMİZLE
output_low(servo); //SERVO PİNİ 0
printf(lcd_putc,"program ok"); //LCD YE YAZDIR
delay_ms(250); //BEKLE
delay_ms(250); //BEKLE
for(;;) //SONSUZ DÖNGÜ
{
digital_v=adc(); //ADC FONKSİYONUNA GİT, DEĞERİ OKU,DÖN
lcd_gotoxy(1,1); //LCD DE 1. SATIR 1. SÜTÜN
lcd_putc("\f"); //LCD SİL
printf(lcd_putc,"ADC: %U",digital_v); //DEĞERİ YAZ
delay_ms(250); //BEKLE
pulsout(digital_v); //SERVOYA SİNYAL GÖNDER
}
}
#include <16F84A.h>
#fuses XT, NOPROTECT, NOWDT,PUT
#use delay(clock=4000000)
#define csenable PIN_A0 //CSENABLE PİNİ A0
#define clock PIN_A1 //CLOCK PİNİ A1
#define veri PIN_A2 //DO PİNİ A2
#define servo PIN_A3 //SERVO PİNİ A3
SET_TRIS_A(0b00000100); //DO PİNİ GİRİŞ
SET_TRIS_B(0b00000000); //LCD İÇİN HEPSİ ÇIKIŞ
//***************************************************
//***************************************************
//********* LCD AYARLARI ****************************
// B0 enable
// B1 rs
// B2 rw
// B4 D4
// B5 D5
// B6 D6
// B7 D7
#define use_portb_lcd TRUE
struct lcd_pin_map { // This structure is overlayed
BOOLEAN enable; // on to an I/O port to gain
BOOLEAN rs; // access to the LCD pins.
BOOLEAN rw; // The bits are allocated from
BOOLEAN unused; // low order up. ENABLE will
int data : 4; // be pin B0.
} lcd;
#if defined use_portb_lcd
#byte lcd = 6 // on to port B (at address 6)
#define set_tris_lcd(x) set_tris_b(x)
#endif
#define lcd_type 2 // 0=5x7, 1=5x10, 2=2 lines
#define lcd_line_two 0x40 // LCD RAM address for the second line
BYTE const LCD_INIT_STRING[4] = {0x20 | (lcd_type << 2), 0xc, 1, 6};
// These bytes need to be sent to the LCD
// to start it up.
// The following are used for setting
// the I/O port direction register.
struct lcd_pin_map const LCD_WRITE = {0,0,0,0,0}; // For write mode all pins are out
struct lcd_pin_map const LCD_READ = {0,0,0,0,15}; // For read mode data pins are in
BYTE lcd_read_byte() {
BYTE low,high;
set_tris_lcd(LCD_READ);
lcd.rw = 1;
delay_cycles(1);
lcd.enable = 1;
delay_cycles(1);
high = lcd.data;
lcd.enable = 0;
delay_cycles(1);
lcd.enable = 1;
delay_us(1);
low = lcd.data;
lcd.enable = 0;
set_tris_lcd(LCD_WRITE);
return( (high<<4) | low);
}
void lcd_send_nibble( BYTE n ) {
lcd.data = n;
delay_cycles(1);
lcd.enable = 1;
delay_us(2);
lcd.enable = 0;
}
void lcd_send_byte( BYTE address, BYTE n ) {
lcd.rs = 0;
while ( bit_test(lcd_read_byte(),7) ) ;
lcd.rs = address;
delay_cycles(1);
lcd.rw = 0;
delay_cycles(1);
lcd.enable = 0;
lcd_send_nibble(n >> 4);
lcd_send_nibble(n & 0xf);
}
void lcd_init() {
BYTE i;
set_tris_lcd(LCD_WRITE);
lcd.rs = 0;
lcd.rw = 0;
lcd.enable = 0;
delay_ms(15);
for(i=1;i<=3;++i) {
lcd_send_nibble(3);
delay_ms(5);
}
lcd_send_nibble(2);
for(i=0;i<=3;++i)
lcd_send_byte(0,LCD_INIT_STRING[i]);
}
void lcd_gotoxy( BYTE x, BYTE y) {
BYTE address;
if(y!=1)
address=lcd_line_two;
else
address=0;
address+=x-1;
lcd_send_byte(0,0x80|address);
}
void lcd_putc( char c) {
switch (c) {
case '\f' : lcd_send_byte(0,1);
delay_ms(2);
break;
case '\n' : lcd_gotoxy(1,2); break;
case '\b' : lcd_send_byte(0,0x10); break;
default : lcd_send_byte(1,c); break;
}
}
//********* LCD AYARLARI ****************************
//***************************************************
//***************************************************
int adc() //F84 İLE ADC OKUMA RUTİNİ
{
int i,digital; //tanımlama
//BU KISIM ADC0831 OKUMAK İÇİN
output_low(csenable);
delay_us(10);
for(i=0;i<=1;i++)
{
output_high(clock);
delay_us(1);
output_low(clock);
delay_us(1);
}
for(i=8;i>=1;i--)
{
shift_left(&digital,1,input(veri));
output_high(clock);
delay_us(1);
output_low(clock);
delay_us(1);
}
for(i=0;i<=1;i++)
{
output_high(clock);
delay_us(1);
output_low(clock);
delay_us(1);
}
delay_us(10);
output_high(csenable);
//OKUMA BİTTİ..........................
return (digital); //ADC DEĞERİ OLAN digital İ DÖNDÜR
}
void pulsout(long x) //SERVOYA PULS GÖNDERME
{
int y,z,i; //TANIMLAMA
x=x*10; //X İN 10 KATINI AL. NEDENİ 1000-2000 ARASI SAYI ÜRETMEK
y=x%250; //X İN 250 YE GÖRE MODUNU AL. KALAN Y YE ATANIYOR
z=(x-100)/250; //Z SAYISI X İN İÇİNDE KAÇ TANE 250 OLDUĞUNU TUTAR
output_high(servo); //SERVO PİNİNİ 1 YAP
for(i=1;i<=z;i++) //Z KADAR 250 us DÖN
{
delay_us(250);
}
delay_us(y); //BİR DE KALAN SAYISI Y us KADAR DÖN Kİ TAM DEĞERE ULAŞ
output_low(servo); //SERVO PİNİNİ 0 YAP
}
void main() //ANA PROGRAM
{
int digital_v,i; //TANIMLAMA
lcd_init(); //LCD Yİ HAZIRLA
OUTPUT_A(0b00000000); //A PROTUNU TEMİZLE
output_low(servo); //SERVO PİNİ 0
printf(lcd_putc,"program ok"); //LCD YE YAZDIR
delay_ms(250); //BEKLE
delay_ms(250); //BEKLE
for(;;) //SONSUZ DÖNGÜ
{
digital_v=adc(); //ADC FONKSİYONUNA GİT, DEĞERİ OKU,DÖN
lcd_gotoxy(1,1); //LCD DE 1. SATIR 1. SÜTÜN
lcd_putc("\f"); //LCD SİL
printf(lcd_putc,"ADC: %U",digital_v); //DEĞERİ YAZ
delay_ms(250); //BEKLE
pulsout(digital_v); //SERVOYA SİNYAL GÖNDER
}
}
Üstte görülen devrede RV1 potu ile motorun dönüşünü ayarlıyoruz...
Not: Proteus simulasyonları Proteus 6.5 ile hazırlanmıştır. Eski versiyonlarda açmak konusunda sorun çıkabilir.
Servo motorlar hakkında bilgi için...
