AVR- Kızılötesi Haberleşme (IR LED-1838)




 

 

Bir önceki yazımda boşta bekleyen ve birçok amatörün elinde olan malzemelerle bir şeyler yapmaya çalışacağımı söyledim. Eski telefonlarda veri aktarımı için kullanılan telefonlar için eskimiş bir teknolojiyi kullanmak ve yine öğrenmek amacıyla basit bir çalışma yaptım. Tek yönlü bir iletişim için kullanacağım bu çalışmada verici olarak kızılötesi (IR) Ledi kullanacağım teknolojisine kaplamasına girmeyeceğim. Alıcı tarafta ise elimde olan KY-022 kullanacağım. Benim kullandığım modül bu üstünde 1838 yazan sensörün tam olarak VS1838,  TL1838  veya OS1838 olması çok  önemli değil . Sanırım bütün üreticiler aynı özelliklerde üretim yapıyor. Bu kızılötesi alıcı sensörler 940nm dalga boyundaki ışığı algılayacak şekilde yapılmış.

1838

Hiç datasheet okumadan hemen bağladım. Yazılımı yükledim ama çalışmadı. Verici olarak kullandığım ledi kontrol ettim gayet güzel yanıp sönüyor.  Mecburen en başta yapmam gerekeni yaptım okumaya başladım. Bu sensörler kızıl ötesi ışığı algılıyor ama bunun için 38-40 kHz arası kare dalga ile göndermek gerekiyor. Burada verici IR ledi sürekli yanık tutsak da bu alıcı sensör için bir anlam ifade etmiyor ve çıkış vermiyor. Klasik olarak kablo ile UART veri gönderirken TX normalde "1, high" durumdadır. İletişim başlarken "0, low" olur ve veriye göre "1" veya "0" olarak devam eder. "1" durumda IR led sürekli yanarken nasıl yapacağım derken bu şema sorumu cevapladı.


Şemaya göre verici taraf yanıyorken daha doğrusu 38kHz ile yanıp sönüyorken, sensör çıkışı "0" durumuna geçiyor. Benim ilk olarak düşündüğüm verici led yandığında alıcı "0" olur. Bunun için verici ledi bir  transistör ile anahtarlarım ve TX "0" olunca ledi yakarım şeklindeydi. Bu 38kHz ve şemayı öğrenmem işimi kolaylaştırdı. Bu arada sensörün öneri bağlantı şemasını uygulamadım. Ben çıkış ucunu direk RX e bağladım. Belki daha kötü bir iletişim olacaktır ama sonuçta bu amaçsız bir proje olduğundan bu detaylara girmedim. Önemli bir detayı atlamamak gerek. Datasheette belirtilen "1" ve "0" olma zamanları için sınırlama mevcut.


Low "0" 450-500µs den daha az olamıyor. High "1" için 700-750µs süresi verilmiş ama bu sınırın üstüne çıktığım halde sorun olmadı. Low "0" TL veya Tpwl olarak belirtilen süre önemli. Bu kısıtlama bizim en büyük yani en hızlı iletişim sınırımızı belirliyor. Burada verinin gönderimindeki Baud değeri bir bit için geçen süre 450µs den az olamıyor. 4800 Baud değerinde bir bit için 208µs gerekiyor ve bu hızda çalışmıyor. 2400 için 416µs gerekiyor ve bu sürede 450 den küçük olmasına rağmen çalışıyor.

Düşük hızlarda neredeyse hatasız şekilde iletişim kuruluyor. Önüne bir engel gelmez veya başka bir kaynaktan gelen ışığın etkisi olmazsa hatasız. Sensörün çalışması için gereken sınırlar ve çalışma mantığı bu şekilde. Verici led yanarken "0" olduğundan ve çıkış voltajı 0,5-4,5V arası olduğundan çıkış pinini Rx pinine bağlayıp bu sensörle işimizi bitiyoruz.

IR led

Verici tarafta KY-005 isimli bir modül kullandım. Üstünde 940nm dalga boyunda ışık yayan bir led bulunmakta. UART Tx ucuna bağlı çalışacak olan bu ledin bağlantısı basit. Ledin negatif pini Tx'e, pozitif pini PWM çıkış veren PORTD5'e bağlı olacak. Artı-eksi ayrımı olmayan bir ampül bağlı olsaydı. Tx iletişim yokken "1" durumda olduğundan  PWM sayesinde bu ampül %50 oranında sürekli yanacaktı. IR ledin sadece iletişim sırasında yanması gerekiyor.  Tx iletişim başladığında ve verinin durumuna göre "0" olduğundan ledin eksi ucuna bağlıyoruz.

PWM Ayarla

IR ledi alıcı tarafın algılaması için 38kHz frekansında yakıp söndürmem gerekiyor. Bunun için PWM kullanacağım. Önceki yazılarda bir örneğini yapmış ve detaylı anlatmıştım. Burada hızlıca değineceğim. PWM için timer0 birimine bağlı PORTD5 pinini kullandım. Gerekli ayarlara geçmeden 38kHz frekansının periyodunu bulalım. T=1/F, T=1/38000 ve çıkan sonucu µs cinsinde yazmak için 1.000.000 ile çarparsak  T=26,3157µs buluruz. Bu çıkışın "1" ve "0" olma süresidir.


Şemada görüldüğü gibi Faz doğrulamalı PWM modunda TCNT0 üst (TOP,0xFF) değere çıkıyor ve iniyor. Bu sırada OCR0A-OCR0B ile eşleşince bizim ayarlarımıza bağlı olarak COM0A-COM0B  "1" veya "0" oluyor. Bu şekilde yaptığımızda frekans- periyot ayarlama şansımız yok. Prescaler değerine göre frekans belirleniyor. Bu nedenle aşağıda gördüğünüz "Mode 5" seçeneğini kullanacağız.


Bu seçenekte diğerinden farklı olarak TOP değerini 0xFF yerine OCR0A ya eşitleyebiliriz. Böylece bizim istediğimiz üst değer çıkar ve yine azalarak sıfırlanır.  TCNT0 değeri kristalden alınan saat darbelerine göre artıyordu. Bir prescaler değeri belirlemezsek 26,3157µs sürede ulaşılan değer 8 bit üstüne çıkar. (timer0 8 bit) Bu nedenle prescaler değerini 8 olarak seçeceğiz. 16.000.000/8=2.000.000 saniyedeki darbe değeridir. 1µs de ise 2 saat darbesi ile TCNT0 artar. Periyot 26µs olduğundan TCNT0 değerinin TOP değere ulaşıp tekrar sıfır olması gerekir (26 darbe TOP-26 darbe BOTTOM) Bunun için OCR0A değerini 25 seçeriz. (0 dan 25'e 26 darbe) Bu değerin yarısını OCR0B(12 "0" dahil) ye eşitlersek kare dalga elde ederiz. NOT: OCR0A PORTD6 ya bağlı olan register olduğundan bu pini kullanamayız.

İki tarafta gerekli bağlantıları yaptıktan sonra daha önce kullandığım kodlarda ufak değişiklikler yaptım. Bu kodlar ile iletişime gerçekleşiyor.

Gönderici taraf:

/*
 * IR_trans.c
 *
 * Created: 12/16/2020 11:05:43 AM
 *  Author: haluk
 */ 


#define F_CPU 16000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

//uart
#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)// uart UDR register boş kesmesi
#define UART_Bos_Off UCSR0B&=~(1<<UDRIE0)// uart UDR register boş kesmesi
////////////////////////////////////////////////////
#define  olcme_aralik 249// ölçüm arası bekleme  1ms 249
#define  oranH 0.9// Şenol beyin Devreforum'daki yazısından aldım.
#define  oranL 0.1
////////////////////////////////////////////////////
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];
volatile uint8_t rx_durum=0, veri_toplam=0;
volatile uint16_t adc_okunan=0,adc_giden=0;
volatile float sonuc=0.0;
//////fonksiyonlar
void uart_basla(uint32_t baud);
uint8_t uart_oku();
void uart_gonder(uint8_t uData);
void uart_dizi(const char *str);
uint8_t uart_gelen();
void adc_basla();
void adc_oku(uint8_t kanal);
void zaman1_ayar();
void pwm_IR();

ISR (TIMER1_COMPB_vect){//adc ölçümü timer1 OCR1B kesmesiyle başlar
	//ADMUX=(ADMUX & 0xF8) | pin;

}

ISR (USART_UDRE_vect){	//uart veri gönderim kesmesi
	tx_son=(tx_son+1)&UART_Tx_Mask;
	UDR0=tx_ring[tx_son];
	if (tx_son==tx_bas)
	UART_Bos_Off;
}

ISR(USART_TX_vect){
	TCCR0B&=~(1<<CS01);//pwm durdu sürekli iletişim halinde gereksiz
}
ISR (ADC_vect){
	adc_okunan=ADCW;// adc ölçüm bitti
	sonuc=(float)((sonuc)*oranH)+(float)(adc_okunan*oranL);	// Şenol beyin Devreforum'daki yazısından aldım.
	adc_giden=(int)sonuc;//gidecek olan veri
	veri_toplam=((adc_giden>>8)&0xFF)+(adc_giden&0xFF);//gidecek verinin 8 bit haldeki toplamı checksum

}
int main(void){
	adc_basla();// adc başladı
	zaman1_ayar();// timer1 başladı, adc okuma için ayarlı
	uart_basla(2400);
	pwm_IR();// PORTD5 için ayarlı 
	while(1){		
			uart_gonder(0x55);
			uart_gonder(0xAA);
			uart_gonder((adc_giden>>8)&0xFF);//16bit sayıyı 8 bit olarak iki parçada gönderiyoruz.
			uart_gonder(adc_giden&0xFF);//16bit sayıyı 8 bit olarak iki parçada gönderiyoruz.
			uart_gonder(veri_toplam);		
			_delay_ms(50);// tek taraflı iletişim, çok sık olduğunda sorun çıkıyor.
	}
}
////// uart
void uart_basla(uint32_t baud){
	cli();
	uint16_t baudRate=0;
	baudRate=(F_CPU/baud/16)-1;
	if (baud>=115200){//115200 ve üstünde U2X 1 yapılıyor.
		baudRate=(F_CPU/baud/8)-1;
		UCSR0A|=(1<<U2X0);
	}
	UBRR0H=(baudRate>>8);
	UBRR0L=baudRate;
	UCSR0B|=(1<<RXEN0)|(1<<TXEN0)|(1<<TXCIE0);
	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){
	TCCR0B|=(1<<CS01);//pwm basladı sürekli iletişimde gereksiz.
	tx_bas=(tx_bas+1)&UART_Tx_Mask;
	tx_ring[tx_bas]=uData;
	UART_Bos_On;
}
void uart_dizi(const char *str){
	while(*str){
		uart_gonder (*str++);
	}
}
uint8_t uart_gelen(){
	if (rx_son==rx_bas){
		return 0;
	}
	return 1;
}

void pwm_IR(){		
			DDRD|=(1<<PORTD5);
			TCCR0A|=(1<<COM0B1)|(1<<WGM00); //d5 eşleşme anında 0, mode5
			TCCR0B|=(1<<CS01)|(1<<WGM02);//prescaler 8, mode5 
			OCR0A=25;//tcnt top değer
			OCR0B=12;//d5 eşleşme değeri
	
}
//////adc
void adc_basla(){
	cli();
	ADMUX=(1<<REFS0);
	ADCSRA=(1<<ADEN)|(1<<ADPS0)|(1<<ADPS1)|(1<<ADPS2)|(1<<ADIE)|(1<<ADATE);
	ADCSRB |=(1<<ADTS2)|(1<<ADTS0);
	ADCSRA |=(1<<ADSC);
	sei();
}

//////timer1
void zaman1_ayar(){
	cli();
	TCCR1B|=(1<<WGM12)|(1<<CS11)|(1<<CS10);// CTC  ve prescaler 64
	TIMSK1|=(1<<OCIE1B);
	OCR1A=olcme_aralik;
	sei();
}

Alıcı taraf:

/*
 * IR_rec.c
 *
 * Created: 12/16/2020 12:18:58 PM
 *  Author: haluk
 */ 

#define F_CPU 16000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdio.h>

//uart
#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)// uart UDR register boş kesmesi
#define UART_Bos_Off UCSR0B&=~(1<<UDRIE0)// uart UDR register boş kesmesi

//lcd
#define DATA_PORT PORTD
#define CMD_PORT PORTB
#define EN_ PORTB1
#define RS_ PORTB0
#define DATA_OUT DDRD|=(1<<PORTD4)|(1<<PORTD5)|(1<<PORTD6)|(1<<PORTD7)
#define CMD_OUT DDRB|=(1<<RS_)|(1<<EN_)
#define EN_HIGH CMD_PORT|=(1<<EN_)
#define EN_LOW CMD_PORT&=~(1<<EN_)
#define RS_HIGH CMD_PORT|=(1<<RS_)
#define RS_LOW CMD_PORT&=~(1<<RS_)
//lcd komutlar
#define LCD_CL lcd_kmt(0x01)
#define LCD_HOME lcd_kmt(0x02)
#define LCD_NSCR_RL lcd_kmt(0x04)
#define LCD_SCR_RL lcd_kmt(0x05)
#define LCD_NSCR_LR lcd_kmt(0x06)
#define LCD_SCR_LR lcd_kmt(0x07)
#define LCD_DOFF lcd_kmt(0x08)
#define LCD_DON lcd_kmt(0x0C)
#define LCD_DBON lcd_kmt(0x0D)
#define LCD_DCON lcd_kmt(0x0E)
#define LCD_DCBON lcd_kmt(0x0F)
#define LCD_CR_L lcd_kmt(0x10)
#define LCD_CR_R lcd_kmt(0x14)
#define LCD_SC_L lcd_kmt(0x18)
#define LCD_SC_R lcd_kmt(0x1C)
#define LCD_4L1 lcd_kmt(0x20)
#define LCD_4L2 lcd_kmt(0x28)
////////////////////////////////////////////////////
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];
volatile uint8_t rx_durum=0, veri_toplam=0;
volatile uint16_t  gelen_adc, sen_adc=0,sayac=0;
char str_sen_adc[15],str_sayac[15];//sayac hatalı veri sayısını tutar
volatile uint8_t veri_geldi=0;
//////fonksiyonlar
void uart_basla(uint32_t baud);
uint8_t uart_oku();
void uart_gonder(uint8_t uData);
void uart_dizi(const char*str);
uint8_t uart_gelen();
void lcd_basla();
void lcd_data(uint8_t  gelen);
void lcd_kmt(uint8_t  cmd);
void lcd_yaz(uint8_t  data);
void lcd_dizi (const char *str);
void lcd_git(uint8_t  x, uint8_t y);

ISR (USART_RX_vect){//uart veri alma kesmesi
	uint8_t veri=UDR0;
	
	switch (rx_durum){
		case 0:
		if (veri==0x55){rx_durum=1;}else{rx_durum=0;}// veri 0x55 ise bir sonraki durum, değilse başa döner
		break;
		case 1:
		if (veri==0xAA){rx_durum=2;}else{rx_durum=0;}// veri 0xAA ise bir sonraki durum, değilse başa döner
		break;
		case 2:		
		gelen_adc=(veri<<8);// gelen veriyi 8 bit kaydırıp geçici değişkene yazdık
		veri_toplam=veri;//veri toplama ekledik
		rx_durum=3;
		break;
		case 3:
		gelen_adc+=veri;// kalan 8 biti geçici değişkene yazdık
		veri_toplam+=veri;//veri toplama ekledik
		rx_durum=4;
		break;
		case 4:
		if (veri_toplam==veri){// sensörden gelen toplam ile burada yapılan toplama eşitse
			sen_adc=gelen_adc;// gelen adc veri kaydedildi
			veri_geldi=1;//veri geldi bayrağı 1 yapıldı
		}
		else{sayac++;}//veri hatalı geldiğinde sayac artar.
		rx_durum=0;
		break;
		default:
		rx_durum=0;
		break;
	}
}
ISR (USART_UDRE_vect){	//uart veri gönderim kesmesi
	tx_son=(tx_son+1)&UART_Tx_Mask;
	UDR0=tx_ring[tx_son];
	if (tx_son==tx_bas)
	UART_Bos_Off;
}
int main(void){
	lcd_basla();
	uart_basla(2400);
	while(1){
		
		sprintf(str_sen_adc,"%d",sen_adc);//dizi içine alıyorum
		sprintf(str_sayac,"%d",sayac);
		lcd_git(0,0);
		lcd_dizi(str_sen_adc);
		lcd_git(5,0);
		lcd_dizi("ADC");
		lcd_git(0,1);
		lcd_dizi(str_sayac);
		lcd_git(4,1);
		lcd_dizi("HATA");
		if (veri_geldi==1){			
			veri_geldi=0;
			lcd_git(0,0);
			lcd_dizi("    ");
			lcd_git(0,1);
			lcd_dizi("    ");
		}
		
	}
}
////// uart
void uart_basla(uint32_t baud){
	cli();
	uint16_t baudRate=0;
	baudRate=(F_CPU/baud/16)-1;
	if (baud>=115200){//115200 ve üstünde U2X 1 yapılıyor.
		baudRate=(F_CPU/baud/8)-1;
		UCSR0A|=(1<<U2X0);
	}
	UBRR0H=(baudRate>>8);
	UBRR0L=baudRate;
	UCSR0B|=(1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0)|(1<<TXCIE0);
	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(const char *str){
	while(*str){
		uart_gonder (*str++);
	}
}
uint8_t uart_gelen(){
	if (rx_son==rx_bas){
		return 0;
	}
	return 1;
}
//lcd
void lcd_basla(){
	CMD_OUT;
	DATA_OUT;
	_delay_ms(150);
	lcd_data(0x30);
	_delay_us(4100);
	lcd_data(0x30);
	_delay_us(100);
	lcd_data(0x30);
	_delay_us(100);
	lcd_data(0x20);
	_delay_us(100);
	LCD_4L2;
	_delay_us(50);
	LCD_DON;
	_delay_us(50);
	LCD_NSCR_LR;
	_delay_us(50);
	LCD_HOME;
	_delay_ms(2);
	LCD_CL;
	_delay_ms(2);
}
void lcd_data(uint8_t  gelen){
	DATA_PORT=(DATA_PORT&0x0f)|(gelen & 0xF0);
	EN_HIGH;
	_delay_us(1);
	EN_LOW;
	_delay_us(100);
	
}
void lcd_kmt(uint8_t  cmd){
	RS_LOW;
	lcd_data(cmd);
	lcd_data(cmd<<4);
}
void lcd_yaz(uint8_t  data){
	RS_HIGH;
	lcd_data(data);
	lcd_data(data<<4);
	
}
void lcd_dizi (const char *str){
	while(*str){
		lcd_yaz (*str++);
	}
}
void lcd_git(uint8_t  x, uint8_t y){
	if (y==0)
	lcd_kmt(0x80+x);
	if (y==1)
	lcd_kmt(0xC0+x);
	if (y==2)
	lcd_kmt(0x94+x);
	if (y>=3)
	lcd_kmt(0xD4+x);
}

 

Hiç yorum yok:

Yorum Gönder