AVR- Bluetooth Kontollü Araba

Gruplarda birkaç kişinin satışını gördüğüm aslında çok basit olan bluetooth kontrol konusuna değinmek istiyorum. Bir yıl önce çocuklar için yaptığım minik bir araba üstünden konuyu aktaracağım. Benim gibi amatörlerin bunları satın almasına karşıyım. Oturup yapmak yerine, bu tür şeyleri hazır almak öğrenme açısından size bir şey katmaz. Kullanılan modül ve motor sürücü için çok fazla Türkçe kaynak var.Bu nedenle çok detaya girmeyeceğim.


Bluetooth Modül (HC-06)

Kolay ve ucuz ulaşabileceğiniz bir modül (GSM-LORA vb. kıyasla) HC-06, eşleşme çağrısına yanıt verebilen, eşleştirme isteği gönderemeyen bir modüldür. Yalnızca köle modunda çalışır. GSM modülleri gibi AT komut seti kullanılarak gerekli ayarlamalar yapılır. Kullandığım modül HC-06 (ZS-040)(EGBT-046S)


Resim: www.martyncurrey.com/arduino-and-hc-06-zs-040/
Pinler sırasıyla  RXD, TXD, GND, VCC şeklindedir. (State ve En bağlı değil).
RXD: Şekilde görüldüğü gibi 5V ile çalışan bir denetleyicide voltaj bölücü ile bağlanmalıdır. Denetleyicinin Tx pinine bağlantı yapılacak.
TXD: Denetleyicinin Rx pinine bağlanacak. Bu iki pin UART iletişimin yapıldığı pinlerdir, UART konusunu burada anlatmaya çalıştım.
GND: Negatif besleme
VCC: +3,3 ve +6V arası pozitif besleme pinleridir.
State bağlantı durumunu gösteren pindir, "1" olduğunda eşleşme gerçekleşmiştir. EN pini komut veya veri modu seçimi için kullanılan pindir.

AT Komutları

AT komut setini kullanarak ayarlama yapmak isterseniz USB seri çevirici kullanmanız gerekir. Elinizde bu yoksa Arduino kartlarını da kullanabilirsiniz. Bunun için Arduino İde de örnekler, "Software Serial" kütüphanesi içinde bulunan "SoftwareSerialExample" ile Rx-Tx pinlerini bağlayarak yapabilirsiniz. (Voltaj bölücü bağlantıyı unutmayın) Baud rate 9600 olarak ayarlamanız ve seri port ekranı altında yer alan "Satır sonu yok" seçeneğini "NL ve CR ile birlikte" seçeneğini seçmek gerekir.
AT: UART bağlantısı kurulmuşsa "OK" cevabını döndürür.
AT+ADDR: Modül adresi cevap olarak gelir.
AT+NAME: Modül ismi cevap olarak gelir. Değiştirmek isterseniz, "AT+NAME=BLUECAR" şeklinde yazabilirsiniz.
AT+UART: Baud rate, Stop bit sayısı, Eşlik biti şeklinde cevap döndürür. Fabrika ayarları olarak 9600,0,0 cevabı gelir. Baud rate=9600,Stop bit sayısı 1 bit ve eşlik biti yok şeklindedir. Burada sadece baud rate değişikliği yapmanızı tavsiye edeceğim. Bunun için "AT+UART=38400,0,0" şeklinde komut göndermeniz gerekir. Diğer komutlar için Buraya bakabilirsiniz.

Motor Sürücü

L293D elimde olduğundan onu kullandım. 600mA çıkış vermekte, bu kullandığım motorlar için yeterli. Yüksek akım çeken motorlar için farklı sürücü kullanmanız gerekir. L293D birbirinden bağımsız iki motoru iki yönde çalıştırma imkânı verir.
Bilgi sayfasından alıntı bu resimde pinler açıklanmış. Resmin durumuna göre Sol-Sağ motor olarak isimlendireceğim.
1-9 (1,2EN-3,4EN): Bu pinler "1" olduğunda motorlar hareket edecektir. PWM ile motor dönüş hızı kontrol edilebilir.
2-7 (1A-2A) ve  10-15 (3A-4A: Sol-Sağ motor ileri-geri dönüş pinleridir. 2 numaralı pin (1A)  "1" ve 7 numaralı pin (2A) "0" olduğunda sol motor ileri tersi olduğunda (2 "0" ve 7 "1") sol motor geri dönecektir. Sağ motor içinde ilgili pinlerle aynı şey geçerlidir.
3-6(1Y-2Y) ve 11-14(3Y-4Y): Motor besleme pinleridir. 3-6 sol motor ve 11-14 sağ motor.
4-5,12-13 : GND negatif besleme ve soğutucu bağlantısı yapılacak pinlerdir.
VCC1: Entegrenin +5V besleme pinidir.
VCC2: Motorların pozitif besleme pinidir. +4,5 ve +36V arası besleme yapılabilir.

PWM

Kabaca değinmek gerekirse PWM bir çıkışı "1" ve "0" yaparak Analog bir sistem gibi voltaj ayarı yapmamıza olanak verir. "1" ve "0" arası  geçişlerin süresine göre bu ayarlamayı yapabiliyoruz. Atmega 328p de bu işlemler için 3 adet zamanlayıcı ve bu zamanlayıcılara bağlı 6 adet pin bulunur. Ben PD5 (OC0B) ve PD6 (OC0A)  pinlerini kullandım. (Uno D5-D6). Bu pinler Timer 0 zamanlayıcısına bağlıdır ve ilgili registerler ve daha fazlası için Atmega 328p
Timer0 da bir sayaç (TCNT0) üst sınıra kadar çıkar (0xFF)(255), tekrar sıfırlanır veya sıfıra doğru azalır. Bu işlem sürekli tekrar eder. Ayarlayacağımız değer ile eşleştiğinde (OCR0A veya OCR0B) OC0A (PD6) veya OC0B (PD5) pinleri "1" veya "0" olur. PWM için farklı modlar bulunmaktadır. Bunlar pinlerin frekansını değiştirmemize olanak sağlar. Prescaler ile birlikte istenilen frekans değerine ulaşılabilir.  Ben Faz Doğrulamalı PWM Modu kullanacağım. Benim için esas olan "1" ve "0" durumunun birbirine göre yüzdesi olduğundan frekans çok düşük olmadıkça önemi yok.

Timer 0 kontrol register A :

7 (COM0A1)  ve 6 (COM0A0)numaralı bitler OC0A (PD6)pininin OCR0A ile TCNT0 eşleştiğinde ne olacağını seçmemizi sağlayan bitlerdir. Seçilen moda göre ve WGM02 bit durumuna göre değişkenlik gösterir. Faz doğrulamalı mod için tablo:
Tabloda görüldüğü gibi COM0A1 (7) ve COM0A0 (6) bitlerine göre OC0A (PD6)pininin eşleşme olduğunda toggle (tersi), clear (0) veya set (1) seçimini yapmamıza olanak verir.  Örnek olarak OCR0A=64, COM0A1=1 ve COM0A0=0 şeklinde ayarlayıp PWM aktif ettiğimizde %25 ile  1,25V gibi bir çıkış etkisi verecektir. Sadece COM0A1=0 ve COM0A0=1 yaptığımızda  %75 ile 3,75V çıkış etkisi verecektir. Aradaki fark eşleşme oluşunca pinin "1" veya "0" olmasıdır.
5 (COM0B1)  ve 4 (COM0B0)numaralı bitler OC0B (PD5)pininin OCR0B ile TCNT0 eşleştiğinde ne olacağını seçmemizi sağlayan bitlerdir.
1 (WGM01) ve 0 (WGM00) numaralı bitler Timer0 zamanlayıcısının modlarını seçmemizi sağlayan bitlerdir. Tabloda 1 numaralı mod olan PWM Phase Correct için (WGM00) "1" yapılmalıdır.

 

Timer 0 kontrol register B :

7 ve 6 numaralı bitler PWM olmadığında etkindir yani bu konunun dışındadır.  3 numaralı (WGM02) bit TCCR0A register 1 ve 0 numaralı bitler ile birlikte zamanlayıcı mod seçiminde kullanılır. (tablo 19-9).
2,1 ve 0 numaralı (CS02,CS01 ve CS00) bitler Prescaler seçiminin yapılmasını sağlar. 16Mhz ile çalışan denetleyici kristalden gelen her saat darbesinde TCNT0 sayacının değerini bir artırır. Presacler değerini /64 yaptığımızda 64 darbede bir artırır. Böylece saniyede 16.000.000/256 defa üst değere ulaşan TCNT0 16.000.000/64/256=976,56 Hz  üst değere ulaşır. Arduino uno için en yüksek frekans değeri budur. C dili ile aynı denetleyici faz doğrulamalı mod ile 31.372 Hz değerine ulaşır. (Fast Pwm 62.500 Hz).
Kısaca dedim ama oldukça uzun oldu. Umarım sıkıcı veya haddimi aşarak yanlış bilgiler vermiyorum. PWM konusunda register ve bitler bu şekilde. Bu işlemleri yapmak için bu şekilde bir fonksiyon tanımlıyorum.
void pwm_pin(uint8_t _pin,uint8_t _pwm){
  if(_pin==5){
   DDRD|=(1<<PORTD5);
   TCCR0A|=(1<<COM0B1)|(1<<WGM00);
   TCCR0B|=(1<<CS00);
   OCR0B=_pwm;
  }
  else if(_pin==6){
   DDRD|=(1<<PORTD6);
   TCCR0A|=(1<<COM0A1)|(1<<WGM00);
   TCCR0B|=(1<<CS00);
   OCR0A=_pwm;
  } 
}

Android Uygulama

Telefon veya tablet ile yönlendirme yapmak için http://ai2.appinventor.mit.edu/ adresinde uygulama oluşturabilirsiniz.








    

Kodlar

/*
 * blue_araba.c
 *
 * Created: 20.10.2019 14:10:35
 * Author : haluk simsek
 */ 

#define F_CPU 16000000ul
#include <avr/io.h>
#include <avr/interrupt.h>
#define UART_Rx_Boyut 64
#define UART_Tx_Boyut 64
#define UART_Rx_Mask (UART_Rx_Boyut-1)
#define UART_Tx_Mask (UART_Tx_Boyut-1)
#define UART_Bos_On UCSR0B|=(1<<UDRIE0)
#define UART_Bos_Off UCSR0B&=~(1<<UDRIE0)

#define  h_sol PORTD5 //L293-1 sol PWM
#define  h_sag PORTD6 //L293-9 sag PWM
#define  sol_g PORTB3 //L293-7
#define  sag_g PORTB2 //L293-10
#define  sag_i PORTB1 //L293-15
#define  sol_i PORTB0 //L293-2

volatile uint8_t rx_bas=0,rx_son=0,tx_bas=0,tx_son=0;
volatile uint8_t rx_ring[UART_Rx_Boyut];
volatile uint8_t tx_ring[UART_Tx_Boyut];

int hiz=200, m_durum=0;
int *ptrhiz=&hiz;

void dur();
void ileri_git();
void geri_git();
void sag_git();
void sol_git();
void sag_ileri_git();
void sol_ileri_git();
void sag_geri_git();
void sol_geri_git();
void hiz_1();
void hiz_2();
void hiz_3();
void hiz_4();
void hiz_5();
void pwm_pin(uint8_t _pin,uint8_t _pwm);
void uart_basla(uint32_t baud);
uint8_t uart_oku();
void uart_gonder(uint8_t uData);
void uart_dizi(char*str);
uint8_t uart_gelen();
void (*motor[14])(void)={
 dur,
 ileri_git,
 geri_git,
 sag_git,
 sol_git,
 sag_ileri_git,
 sol_ileri_git,
 sag_geri_git,
 sol_geri_git,
 hiz_1,
 hiz_2,
 hiz_3,
 hiz_4,
 hiz_5,
};
ISR (USART_RX_vect){
 rx_bas=(rx_bas+1) & UART_Rx_Mask;
 rx_ring[rx_bas]= UDR0;
}
ISR (USART_UDRE_vect){
 tx_son=(tx_son+1)&UART_Tx_Mask;
 UDR0=tx_ring[tx_son];
 if (tx_son==tx_bas)
 UART_Bos_Off;
}
int main(void){
 DDRB|=(1<<PORTB0)|(1<<PORTB1)|(1<<PORTB2)|(1<<PORTB3);  
 uart_basla(38400);  
    while (1){  
  if (uart_gelen()){
   m_durum=uart_oku();   
   motor[m_durum]();
  }else {
   motor[0]();
  }  
    }
}
/////////////////
void dur(){
 pwm_pin(h_sag,0);
 pwm_pin(h_sol,0);
 PORTB&=~(0x0F); 
}
void ileri_git(){
 pwm_pin(h_sag,hiz);
 pwm_pin(h_sol,hiz);
 PORTB=(PORTB&(0xF0))|(0x03);
}
void geri_git(){
 pwm_pin(h_sag,hiz);
 pwm_pin(h_sol,hiz);
 PORTB=(PORTB&(0xF0))|(0x0C);
}
void sag_git(){
 pwm_pin(h_sag,hiz);
 pwm_pin(h_sol,hiz);
 PORTB=(PORTB&(0xF0))|(0x05);
}
void sol_git(){
 pwm_pin(h_sag,hiz);
 pwm_pin(h_sol,hiz);
 PORTB=(PORTB&(0xF0))|(0x0A);
}
void sag_ileri_git(){
 pwm_pin(h_sag,hiz-(hiz>>3));
 pwm_pin(h_sol,hiz);
 PORTB=(PORTB&(0xF0))|(0x03);
}
void sol_ileri_git(){
 pwm_pin(h_sag,hiz);
 pwm_pin(h_sol,hiz-(hiz>>3)); 
 PORTB=(PORTB&(0xF0))|(0x03);
}
void sag_geri_git(){
 pwm_pin(h_sag,hiz-(hiz>>3));
 pwm_pin(h_sol,hiz);
 PORTB=(PORTB&(0xF0))|(0x0C);
}
void sol_geri_git(){
 pwm_pin(h_sag,hiz);
 pwm_pin(h_sol,hiz-(hiz>>3));
 PORTB=(PORTB&(0xF0))|(0x0C);
}
void hiz_1(){*ptrhiz=180;}
void hiz_2(){*ptrhiz=190;}
void hiz_3(){*ptrhiz=210;}
void hiz_4(){*ptrhiz=220;}
void hiz_5(){*ptrhiz=230;}
//////////////// pwm
void pwm_pin(uint8_t _pin,uint8_t _pwm){
  if(_pin==5){
   DDRD|=(1<<PORTD5);
   TCCR0A|=(1<<COM0B1)|(1<<WGM00);
   TCCR0B|=(1<<CS00);
   OCR0B=_pwm;
  }
  else if(_pin==6){
   DDRD|=(1<<PORTD6);
   TCCR0A|=(1<<COM0A1)|(1<<WGM00);
   TCCR0B|=(1<<CS00);
   OCR0A=_pwm;
  } 
}
//////////////// uart
void uart_basla(uint32_t baud){
 uint16_t baudRate=F_CPU/baud/16-1;
 UBRR0H=(baudRate>>8);
 UBRR0L=baudRate;
 UCSR0B|=(1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0);
 UCSR0C|=(1<<UCSZ01)|(1<<UCSZ00);
 sei();
}
uint8_t uart_oku(){
 rx_son=(rx_son+1) & UART_Rx_Mask;
 return rx_ring[rx_son];
}
void uart_gonder(uint8_t uData){
 tx_bas=(tx_bas+1)&UART_Tx_Mask;
 tx_ring[tx_bas]=uData;
 UART_Bos_On;
}
void uart_dizi(char*str){
 while(*str){
  uart_gonder (*str++);  
 }
}
uint8_t uart_gelen(){
 if (rx_son==rx_bas){
  return 0;
 }
 return 1;
}