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);
}

 

AVR- 4 digit 7 Segment Sayıcı (MCU-74HC595-SPI)

Uzun zamandır bir şey yapmadım ve yazmadım. Bir tekrar çalışması olsun ve boş boş duran bazı parçaları değerlendirerek uygulamalar yapmak istedim. Facebook gruplarında sıkça sorulduğunu gördüğüm ve hiç kullanmadığım Led display kullanmaya karar verdim. Basit bir sayaç yapacağım. Zamana göre değişen bu sayaç ve displayi direk MCU, 74hc595 ve SPI-74hc595 ile kullanacağım. Bir amatör olarak öğrenmeye ve bunları paylaşmaya devam ediyorum.  Benim gibi amatörlere faydalı olacağını umarım.

7 Segment Display

8 Rakamını çizecek şekilde yerleştirilmiş ledlerden oluşur. İlgili bacaklara bağlantı yapar ve ledi yakarak istediğimiz rakamı oluşturabiliriz. Saat, gösterge ve sayıcı gibi birçok alanda kullanılır. Bu yazıda 4 haneli (digit) bir ürün kullanacağım. Ürün üstünde “5641BS” yazılı aşağıda bilgi sayfasından alınmış şema bulunmaktadır.


Bir digit içindeki ledler A-F olarak isimlendirilmiştir. Her hane için bu ledlerin eksi (katot) uçları birleştirilmiştir. 6,8,9 ve 12 numaralı bacaklara artı “1, high” ve diğer bacaklara eksi “0, low” verdiğimizde tüm ledler yanacaktır. Her digitin farklı yanmasını istersek artı uçları (6,8,9,12) ayrı ayrı beslememiz gerekir.

Bu displayleri kullanmanın püf noktası tam olarak artı uçların sırasıyla beslenmesidir. Digit 1 den digit 4 e kadar sırasıyla beslemenin verilmesi insanın gözünün algılamasından daha hızlı olarak yapılacaktır. Böylece yanıp sönme titreme fark edilmeyecektir.

Örnek olarak 0123 yazmak istediğimizde yapmamız gerekeni teorik olarak belirteyim. Digit1 pin 12 artı “1,high” besleme verilecek buna karşılık pin 6,8 ve 9 eksi “0, low” olarak kalacak.  0 rakamı için şemaya göre “A,B,C,D,E,F” pinleri eksi “0”, low”olacak diğer pinler ledin yanmaması için artı “1, high” olacak. Digit2 için 9 “high” olacak diğer digit pinleri “low” olacak. 1 rakamı için “B,C” ledlerinin yanması gerekir. Bu ledlerin pinleri “low” diğerleri “high” olacaktır. Digit3 ve digit 4 için de benzer şekilde devam edecektir.

Her rakam için yanması gereken ledlerin bağlı olduğu pinler “0, low”  yanmaması için “1, high” olması gerekiyor. Bunu her seferinde tek tek yazmakla uğraşmak saçma olur. Bir tablo oluşturarak sorunu çözebiliriz. Farklı kaynaklarda farklı tablolar görebilirsiniz. Bu tamamen ledlerin sırası ve kullanılan ürünü ortak anot veya katot olmasına göre değişir. "2" için aşağıdaki tabloyu inceleyelim.



Avr’ nin bir portunu ledlerin bağlantısı için ayırdım. A pini Portun 0 numaralı pinine bağlı olacak şekilde sıraladım. Bu bağlantıya göre “0, low “ olan led yanacak. Buna göre 2 için port çıkış registerini “0B 1010 0100” yapmam gerekiyor. Aşağıda kod içinde tüm rakamların tablosu var. Bu çıkış durumlarını bir dizi içine rakam sırasına göre alacağız. Dizinin ilgili elemanını çağırdığımızda port çıkışı dizinin elemanına eşitlenecek ve istediğimiz rakamı yazacağız.

Bağlantı şeması:





Timer kullanımı

Bu konuya daha önce detaylıca değindim, burada sadece haneler arası geçişlerde ne yaptığımı anlatacağım. Daha önce ki yazılarda gerekli ayarları ve modları aktarmıştım. Timer0 birimini CTC modunda ve OCR0A karşılaştırma kesmesi kullanacağım. İlgili ayarlamalar aşağıda yazılı. 1 ms de bir kesme oluşturacağım. Her kesmede zaman değişkeni bir artacak. Zaman değişkeninin değerine bağlı olarak digit arası geçiş yapılacak. Bunun için bir digit değişkeni tanımladım. Ayarladığım değere bağlı olarak digit değişkeni artacak. Bu değer ister 5 ms de bir ister 100 ms de bir olabilir. Titreme ve geçişi göstermek için değiştirebilirsiniz. Digit1-4 pinleri PORTB 0-4 pinlerine bağlı durumda. Displayin beslemesini MCU üstünden yapmak doğru olmaz ayrıca daha büyük paneller şeklinde bir çalışma da olabilir. Bu nedenle bir transistör ile anahtarlama yaptım. Elimde PNP olarak BC327 var ve akım değerleri bu iş için uygun. Direk MCU ile bağlantı yaptığımda çıkış “1, high” olduğunda digit çalışacaktı fakat transistör kullandığım için “0, low” olunca çalışacak.

Digit1 için PORTB|=0B 1111 1110, bir başka deyişle PORTB&=~(1<<0) olmalıdır. Portb nin 0. Pini “0, low” olduğunda digit1 yanacaktır.

Digit 4 için PORTB|=0B 1111 0111 veya PORTB&=~(1<<3) olmalıdır. Digit arası geçiş için belirlediğimiz süre bitiminde Portb nin çıkışı bir bit sola kaydırılıyor. PORTB diğer pinleri başka işler için kullanılıyor olabilir. Bunun  için ilk dört bit dışında diğer pinlere maskeleme yaparak sadece istediğimiz pini “0” yapacağız.

Kesme içinde haneler arası geçişi bu şekilde yaparken ilgili digitte hangi rakamın yanacağını dizi içindeki tablodan alıyoruz. Her digitin doğru rakamı yakması için switch case ile işlem yapıyoruz. Digit değişkeni değerine göre yazılması gereken değerleri yazdırıyoruz. Bu değerleri basamaklarına ayırarak değişkenlere atıyoruz ve ekrana yazıyoruz.

Haneler arası geçişler sırasıyla 500, 50, 10 ve 5ms dir. 5 ms de ki hareketi gözümüzün yakalama şansı kalmıyor.

Kalan ayrıntıları açıklama olarak yazdım. İlk olarak MCU ya direk bağlı hali bu şekilde olacak.

/*
 * 4digit_sayici_MCU.c
 *
 * Created: 10.12.2020 22:48:27
 * Author : haluk
 */ 
#define  F_CPU 16000000ul
#include <avr/io.h>
#include <avr/interrupt.h>

#define SEG_PORT		PORTD
#define SEG_DIR			DDRD
#define SEG_A			PORTD0
#define SEG_B			PORTD1
#define SEG_C			PORTD2
#define SEG_D			PORTD3
#define SEG_E			PORTD4
#define SEG_F			PORTD5
#define SEG_G			PORTD6
#define SEG_DP			PORTD7
#define SEG_PORT_OUT	SEG_DIR|=0xFF

#define DIG_PORT		PORTB
#define DIG_DIR			DDRB
#define DIG_1			PORTB0
#define DIG_2			PORTB1
#define DIG_3			PORTB2
#define DIG_4			PORTB3
#define DIG_PORT_OUT	DIG_DIR|=(1<<DIG_1)|(1<<DIG_2)|(1<<DIG_3)|(1<<DIG_4)

volatile uint16_t zaman=0, msaniye=1000, sayac=0;
volatile uint8_t digit=0, birler=0, onlar=0, yuzler=0, binler=0;
 uint8_t karekter_dizi[10]={
	 0b11000000//0
	,0b11111001//1
	,0b10100100//2
	,0b10110000//3
	,0b10011001//4
	,0b10010010//5
	,0b10000010//6
	,0b11111000//7
	,0b10000000//8
	,0b10010000//9
	};

void zaman0_ayar();

ISR(TIMER0_COMPA_vect){
	zaman++;	
	
	//geçişi görmek için değeri değiştir. zaman%100
	if ((zaman%5)==0){//5ms de bir digit artar		
		digit++;
		digit&=0x03;//digit 3 ten büyükse 0 olur		
	}
	if (zaman>=msaniye){	//istenen sayma aralığına ulaşınca sayaç artar
		sayac++;
		zaman=0;
		binler=(sayac/1000)%10;// sayac değerini basamaklarına ayırıp değişkene atadık
		yuzler=(sayac/100)%10;
		onlar=(sayac/10)%10;
		birler=sayac%10;
		
	}	
}

int main(void)
{
	SEG_PORT_OUT;//segment pinler çıkış
	DIG_PORT_OUT;//digit pinler çıkış
	zaman0_ayar();//zamanlayıcı ayarlama
    while (1){	
		DIG_PORT=(DIG_PORT|0x0F)^(1<<digit);
// DIG_PORT kullandığımız pinler DIG_PORT|0x0F işlemiyle high yapıldı.
		//digit değişken değerine göre ilgili pin low yapıldı.
		// low olan pine bağlı digit yanacak
		switch (digit)//hangi digit yanıyorsa ona gönderilen veri seçimi yapıldı.
		{
			case 0:
			SEG_PORT=karekter_dizi[binler];
			break;
			case 1:
			SEG_PORT=karekter_dizi[yuzler];
			break;
			case 2:
			SEG_PORT=karekter_dizi[onlar];
			break;
			case 3:
			SEG_PORT=karekter_dizi[birler];
			break;
			default:
			SEG_PORT=0xFF;
			break;
		}	
    }
}

void zaman0_ayar(){
	TCCR0A|=(1<<WGM01);//ctc mode
	TCCR0B|=(1<<CS00)|(1<<CS01);// prescaler 64 1sn 250000
	TIMSK0|=(1<<OCIE0A);//ocra eşleşme kesmesi açık
	OCR0A=249;//1ms de kesme
	sei();// tüm kesmeler açık
}

SPI-74HC595

Bu kadar çok pini kullanma yerine seri giriş paralel çıkış sağlayan 74HC595 ile yapmak mümkün. 74HC595 burada değindim aynı fonksiyonlar ve bu fonksiyonlar olmadan SPI ile bağlantılı olarak kullanımı için kodlar aşağıdadır. Burada SPI konusuna girmeyeceğim çok fazla kaynak var ve çok kolay. Dilerseniz SPI için buraya bakabilirsiniz. Kodlara geçmeden önce display bağlantısında değişiklik yok MCU ile arada 74HC595 olacak. Not: Şemada göreceğiniz gibi digit pinleri PORTD ye bağlandı.

Bağlantı şeması:



Avr ile 74HC595:

/*
 * 4digit_sayici_74HC595.c
 *
 * Created: 10.12.2020 23:03:08
 * Author : haluk
 */ 

#define F_CPU 16000000ul
#include <avr/io.h>
#include <avr/interrupt.h>

#define HC595			PORTB
#define HC595_DIR		DDRB
#define RCLK			PORTB1
#define SER				PORTB3//mosi
#define SRCLK			PORTB5//sck
#define HC595_OUT		HC595_DIR|=(1<<SER)|(1<<SRCLK)|(1<<RCLK)
// veri çıkış
#define SER_HIGH		HC595|=(1<<SER)
#define SER_LOW			HC595&=~(1<<SER)
//veri saat
#define SRCLK_HIGH		HC595|=(1<<SRCLK)
#define SRCLK_LOW		HC595&=~(1<<SRCLK)
//çıkış saat
#define RCLK_HIGH		HC595|=(1<<RCLK)
#define RCLK_LOW		HC595&=~(1<<RCLK)

//display digit
#define DIG_PORT		PORTD
#define DIG_DIR			DDRD
#define DIG_1			PORTD0
#define DIG_2			PORTD1
#define DIG_3			PORTD2
#define DIG_4			PORTD3
#define DIG_PORT_OUT	DIG_DIR|=(1<<DIG_1)|(1<<DIG_2)|(1<<DIG_3)|(1<<DIG_4)

volatile uint16_t zaman=0, msaniye=1000, sayac=0;
volatile uint8_t digit=0, birler=0, onlar=0, yuzler=0, binler=0;
uint8_t karekter_dizi[10]={
	0b11000000//0
	,0b11111001//1
	,0b10100100//2
	,0b10110000//3
	,0b10011001//4
	,0b10010010//5
	,0b10000010//6
	,0b11111000//7
	,0b10000000//8
	,0b10010000//9
};

void zaman0_ayar();
void saat_darbe();
void cikis_darbe();
void veri_yaz(uint8_t data);

ISR(TIMER0_COMPA_vect){
	zaman++;
		if ((zaman%5)==0){//5ms de bir digit artar
			digit++;
			digit&=0x03;//digit 3 ten büyükse 0 olur
		}
	if (zaman>=msaniye){	//istenen sayma aralığına ulaşınca sayaç artar
	    sayac++;
	    zaman=0;
	    binler=(sayac/1000)%10;// sayac değerini basamaklarına ayırıp değişkene atadık
	    yuzler=(sayac/100)%10;
	    onlar=(sayac/10)%10;
	    birler=sayac%10;
			
		}
	
}
int main(void)
{
	zaman0_ayar();//zamanlayıcı ayarlama
	DIG_PORT_OUT;//digit pinler çıkış
	HC595_OUT;
    while (1) 
    {
		
		DIG_PORT=(DIG_PORT|0x0F)^(1<<digit);
// DIG_PORT kullandığımız pinler DIG_PORT|0x0F işlemiyle high yapıldı.
		//digit değişken değerine göre ilgili pin low yapıldı.
		// low olan pine bağlı digit yanacak
		switch (digit)//hangi digit yanıyorsa ona gönderilen veri seçimi yapıldı.
		{
			case 0:
			veri_yaz(karekter_dizi[binler]);
			break;
			case 1:
			veri_yaz(karekter_dizi[yuzler]);
			break;
			case 2:
			veri_yaz(karekter_dizi[onlar]);
			break;
			case 3:
			veri_yaz(karekter_dizi[birler]);
			break;
			default:
			veri_yaz(0xFF);
			break;
		}
		
    }
}
void zaman0_ayar(){
	TCCR0A|=(1<<WGM01);//ctc mode
	TCCR0B|=(1<<CS00)|(1<<CS01);// prescaler 64 1sn 250000
	TIMSK0|=(1<<OCIE0A);//ocra eşleşme kesmesi açık
	OCR0A=249;//1ms de kesme
	sei();// tüm kesmeler açık
}
void saat_darbe(){
	SRCLK_HIGH;
	SRCLK_LOW;
}
void cikis_darbe(){
	RCLK_HIGH;
	RCLK_LOW;
}

void veri_yaz(uint8_t data){
	for (uint8_t i=0;i<=7;i++){
		if (data&0x80){
			SER_HIGH;
		}else{
			SER_LOW;}			
			saat_darbe();
			data<<=1;
	}
	cikis_darbe();
}

AVR SPI-74HC595

/*
 * 4digit_sayici_spi_74HC595.c
 *
 * Created: 11.12.2020 14:11:09
 * Author : haluk
 */ 

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

#define SPI_PORT		PORTB
#define SPI_DDR			DDRB
#define RCLK			PORTB1
#define SS				PORTB2
#define MOSI			PORTB3//ser
#define SCK				PORTB5//srclk
#define HC595			PORTB
#define HC595_DIR		DDRB
#define HC595_OUT		HC595_DIR|=(1<<RCLK)
#define RCLK_HIGH		HC595|=(1<<RCLK)
#define RCLK_LOW		HC595&=~(1<<RCLK)

//display digit
#define DIG_PORT		PORTD
#define DIG_DIR			DDRD
#define DIG_1			PORTD0
#define DIG_2			PORTD1
#define DIG_3			PORTD2
#define DIG_4			PORTD3
#define DIG_PORT_OUT	DIG_DIR|=(1<<DIG_1)|(1<<DIG_2)|(1<<DIG_3)|(1<<DIG_4)

volatile uint16_t zaman=0, msaniye=1000, sayac=0;
volatile uint8_t digit=0, birler=0, onlar=0, yuzler=0, binler=0;
uint8_t karekter_dizi[10]={
	0b11000000//0
	,0b11111001//1
	,0b10100100//2
	,0b10110000//3
	,0b10011001//4
	,0b10010010//5
	,0b10000010//6
	,0b11111000//7
	,0b10000000//8
	,0b10010000//9
};

void zaman0_ayar();
void spi_basla();
void spi_595(uint8_t sData);

ISR(TIMER0_COMPA_vect){
	zaman++;
	if ((zaman%5)==0){//5ms de bir digit artar
		digit++;
		digit&=0x03;//digit 3 ten büyükse 0 olur
	}
	if (zaman>=msaniye){	//istenen sayma aralığına ulaşınca sayaç artar
		sayac++;
		zaman=0;
		binler=(sayac/1000)%10;//sayac değerini basamaklarına ayırıp değişkene atadık
		yuzler=(sayac/100)%10;
		onlar=(sayac/10)%10;
		birler=sayac%10;		
	}	
}
int main(void)
{
	zaman0_ayar();//zamanlayıcı ayarlama
	DIG_PORT_OUT;//digit pinler çıkış
	HC595_OUT;
	spi_basla();
    /* Replace with your application code */
    while (1){
		DIG_PORT=(DIG_PORT|0x0F)^(1<<digit);
// DIG_PORT kullandığımız pinler DIG_PORT|0x0F işlemiyle high yapıldı.
		//digit değişken değerine göre ilgili pin low yapıldı.
		// low olan pine bağlı digit yanacak
		switch (digit)//hangi digit yanıyorsa ona gönderilen veri seçimi yapıldı.
		{
			case 0:
			spi_595(karekter_dizi[binler]);
			break;
			case 1:
			spi_595(karekter_dizi[yuzler]);
			break;
			case 2:
			spi_595(karekter_dizi[onlar]);
			break;
			case 3:
			spi_595(karekter_dizi[birler]);
			break;
			default:
			spi_595(0xFF);
			break;
		}
    }
}
void zaman0_ayar(){
	TCCR0A|=(1<<WGM01);//ctc mode
	TCCR0B|=(1<<CS00)|(1<<CS01);// prescaler 64 1sn 250000
	TIMSK0|=(1<<OCIE0A);//ocra eşleşme kesmesi açık
	OCR0A=249;//1ms de kesme
	sei();// tüm kesmeler açık
}
void spi_basla(){
	SPI_DDR|=(1<<MOSI)|(1<<SCK)|(1<<SS);//master
	SPI_PORT|=(1<<SS);// master ss pull-up
	SPCR|=(1<<SPE)|(1<<MSTR);//master
	sei();
}
void spi_595(uint8_t sData){
	SPDR=sData;
	while(!(SPSR&(1<<SPIF)));
	RCLK_HIGH;
	RCLK_LOW;
}


AVR- ADC Okuma ve RS485 ile Aktarma



Uzun zaman önce başladığım projemin yazılım kısmını nihayet sonlandırdım. Biçerdöverlerin hasat ettiği ürünü kesen tablanın yerden yüksekliğini kontrol edip buna göre ayarlama yapıyorum. Bunun için 15m  bir kabloyla tabla altında bulunan sensörlerden veri almam gerekiyor. Bu işi yaparken profesyonellerin yönlendirmesi ve araştırmalarım sonucunda öğrendiklerimi ve daha önceki yazılarımın güncellenmiş halini paylaşmak istedim. Bir hatırlatma ben bir amatörüm ve yazdıklarım amatörler içindir. Profesyonellere anlatım basit gelebilir. Hatalarım konusunda uyarıları her zaman bekliyorum. Öncelikle ADC konusuyla başlayalım.

ADC

ADC (Analog to Digital Converter) Analog dijital çevirici demektir. MCU (Mikrodenetleyici) "1" ve "0" lar ile işlem yapar. MCU gibi bakarsak olaya bir musluktan su ya akıyor ya da akmıyordur. Musluğu az veya çok açmanız önemsiz. Bir alt ve üst limit geçerli buna göre akıyor veya akmıyor olarak algılar. Ama gerçek hayatta bu şekilde değil suyun akış miktarı ölçülebilir ve "1-0" kadar keskin değildir. Su örneği verdim ama elektrik  için de aynı şey geçerlidir. Kullandığım MCU 10bit (0B11 1111 1111=1023) çözünürlükte ADC birimine sahiptir. Bu ölçüm için referans alınan gerilim değerinin en alt değerinin "0" üst değerinin "1023" olması demektir. ADC ile okunmak istenen gerilim, bu değere (1024) bölünen referans gerilim ile karşılaştırılır. Bu şekilde referansa göre analog değerin dijitale çevrilmiş halini öğrenebiliriz.

Giriş değerinin dijitale çevrilmiş haliyle girişteki değeri bulmak istersek  (ADC*VRef=Vin*1024) formülünü kullanmamız gerekir. Farklı MCU'ların çözünürlük değerine göre 1024 rakamı değişir. Buna göre örnek olarak referans voltajı 4 volt ve okunan ADC değeri 512 olduğunda formül 512*4=1024*Vin olur, sonuç Vin=2 volt bulunur.
Atmega328p'nin ADC birimini uzun uzun anlatmayacağım. ADC birimi blok şeması bu şekilde ve ilgili registerler görünmektedir.



ADMUX

ADC okuma için gerekli ayarlamaları yapmamız gerekiyor, öncelikle ölçüm için referans kaynağını seçiyoruz. Bunun için ADMUX registerinin ilgili pinlerini ayarlıyoruz. REFS1,0 Bitleri ile bu seçimi alttaki tabloya göre yapılır. Bu arada AVcc pini ADc biriminin besleme ayağıdır. Buraya enerji verilmezse birim çalışmaz. Datashette bazı bağlantı şemaları var göz atmak isteyebilirsiniz. Tabloda görüldüğü gibi ilk seçenek AREF pinine verilecek olan voltaja göre ölçüm yapılmasıdır. İkinci AVcc pini ve son olarak iç 1,1 volt referansıdır. AVcc pini yani ADC beslemesinin referans olmasını istersek ADMUX =(1<<REFS0) yapmamız gerekir.

MUX 3,0 bitleri ADC biriminin bağlı olduğu pinleri seçmemizi sağlar. Alttaki tabloya göre bu bitleri 1-0 yaparak seçim yapılır. Örnek olarak ADC2 (Arduino A2) seçmek istersek ADMUX |=(1<<MUX1) yapmamız gerekir. Bunu bir fonksiyonda tanımladığımızda her seçim için ayrı girmek veya maskelemek gerekir. Analog okuma için 8 adet pin olduğundan seçmek istediğimiz pin ikilik sayı sistemine göre  0b000 (0) ile 0b111 (7) arasında olabilir. Bu durumda 0b111 (0x07) ile "AND" işlemi uygular ve sonucu MUX bitlerine yazarsak kullanışlı bir fonksiyon oluşturabiliriz. Fonksiyon için altta kodlara bakabilirsiniz.




ADCSRA

ADCSRA diğer ayarlamaları yaptığımız ve ADC işlemini başlattığımız kontrol registeridir.  ADEN "1" olursa ADC birimi çalışır. ADCSRA|=(1<<ADEN) şeklinde yapılır. ADATE okuma çevrimini sürekli veya başka bir kesme vb. kaynakla başlaması için "1" yapmamız gerekir ADCSRA|=(1<<ADATE). Başlatma seçenekleri ADCSRB registerinde ayarlanır. ADSC biti "1" yapıldığında okuma başlar. ADCSRA|=(1<<ADSC) şeklinde "1" yapılır okuma başlar ve bitene kadar "1" olarak kalır. Bu sayede (ADCSRA&(1<<ADSC)) şeklinde çevrimin tamamlandığını öğrenebiliriz. ADIE  Kesmeyi aktifleştiren bit, çevrim sonunda kesme oluşması için "1" yapılır. ADCSRA|=(1<<ADIE)


ADPS2,0 bitleri ADC ölçümü için kullanılan saat sinyalini ayarlayan bitlerdir. Datasheet'e göre 50kHz ile 200khz arası olması uygundur. 16MHz  saat frekansını bu aralığa getirmek için 16.000.000/128=125kHz yapmamız gerekir. Böylede ADC saat çevrimi MCU saat sinyalinin 128 de biri olarak çalışır.

Bir ADC çevrimi ilk okumada 25 sonraki tek okumalarda 13 ADC saat döngüsü zaman alıyor. Bu ayarlamayla 104us bir okuma süresidir. Bu ayarla bundan daha hızlı okuma yapamayacaktır.  Yukarıdaki tablo frekans bölücü değerlerini göstermektedir. 16MHz den farklı frekans ile çalışıyorsanız farklı bölme değeri seçebilirsiniz. Benim seçimime göre ADCSRA|=(1<<ADPS0)|(1<<ADPS1)|(1<<ADPS2) yapmamız gerekir.


ADCSRB

ADCSRB bir diğer kontrol registeridir. Bu register içinde otomatik veya sürekli çevrim ayarları yapılır. ADTS2,0 bitleri aşağıdaki tabloya göre "1"- "0" yapılarak ayarlanır. Bir zamanlayıcı veya dış kesme ile okuma işlemini başlatabiliriz. Benim seçimime göre 6. seçenek olan ADCSRB|=(1<<ADPS0)|(1<<ADPS2) yapmamız gerekir.


Bütün bu ayarlamaları yapmak için bir ADC başlatma fonksiyonu ve program içinde çağırıp istenen pinden okuma yapmak için adc okuma fonksiyonu tanımlıyoruz.

void adc_basla(){
                    cli();// kesmeler kaptıldı
                    ADMUX=(1<<REFS0); //referans AVcc seçildi
        //adc birimi başladı,bölme faktörü 128,adc kesme aktif,otomatik başlatma aktif
                    ADCSRA=(1<<ADEN)|(1<<ADPS0)|(1<<ADPS1)|(1<<ADPS2)|(1<<ADIE)|(1<<ADATE);
                    ADCSRB |=(1<<ADTS2)|(1<<ADTS0);//otomatik başlatma timer1 ocr1b seçili
                    ADCSRA |=(1<<ADSC);//ilk okuma yapıldı 25 çevrim
                    sei();
}
//kesme veya otomatik dışında ADC okuma için fonksiyon
/*uint16_t adc_oku(uint8_t _pin){
                    _pin&=0x07;//7 den büyük değer girilmesi durumunda sınırlama
                    ADMUX=(ADMUX & 0xF8) | _pin;// pin seçimi
                    ADCSRA |=(1<<ADSC);//okuma başlatma
                    while(ADCSRA & (1<<ADSC));//çevrim bitene kadar bekleme
                    return (ADCW);// sonuç registeri dönüş alındı
}*/

Ben okumayı fonksiyon çağırma şeklinde yapmayacağım. Bir while ile beklemek yerine çevrim bitince kesme oluşsun istiyorum. Bunun için Timer1 zamanlayıcısını CTC modunda ayarladım ve OCR1B kesmesini açtım. "TIMER1_COMPB_vect" kesme rutini içine pin seçimi yapılabilir ama ADC0 ile okuma yapacağımdan bunu da yapmama gerek yok. Okuma ve sonrası yapılan işlemleri "ADC_vect" kesme rutini içinde yapıyorum. Zamanlayıcı konularını daha önce yazdığım için burada tekrar etmeyeceğim.

ISR (TIMER1_COMPB_vect){
//ADMUX=(ADMUX & 0xF8) | pin; //şeklinde farklı pin seçilebilir.
}
ISR (ADC_vect){           
                    adc_okunan=ADCW;// bu satırdan sonrası ana döngü içinde olabilir.
                    sonuc=(float)((sonuc)*oranH)+(float)(adc_okunan*oranL);                 // Şenol EKER Devreforum.
                    adc_giden=(int)sonuc;//gidecek olan veri
                    veri_toplam=((adc_giden>>8)&0xFF)+(adc_giden&0xFF);//gidecek verinin 8 bit haldeki toplamı checksum
}

ADC Veri Ortalaması


Ben ve eminim birçok amatör peş peşe ölçüm yapıp aldığı sonuçları bölerek ortalama alma yöntemini uyguluyor. Bunun yerine burada Şenol Beyin bir paylaşımını denedim. Mantık yapılan ölçümü belirlediğimiz oranla çarparak toplama eklemek. Böylece sanki oran kadar sayıyı toplayıp bölmüşüz gibi oluyor. Örneğin 0,1 ile çarptığımızda değerin toplama etkisi onda biri kadar olduğundan on adet veriyi toplayıp bölmek veya hareketli bir ortalama alarak ilk veriyi atıp son veriyi okumakla aynı işi daha hızlı yapıyoruz.

ADC konusu bu kadar artık bu aldığımız veriyi nasıl aktaracağız şimdi buna bakalım.

RS485 ile Haberleşme

TX Kesmesi

RS485 daha önce bahsettiğim bir konu fakat orada UART kullanırken kesmeleri kullanmamıştım. Şimdi kesmelerle daha sağlıklı bir iletişim için gerekli güncellemeleri aktaracağım.  UART gönderim sırasında 485 sürücüsünün tekrar veri alma pozisyonuna geçmemesi için bekleme koymuş ve Baudrate düştükçe bekleme süresi uzar demiştim. İletim hızımıza bağlı olarak giden her bir bit için gereken süre değişir. Biz her Baud değeri için bir hesaplama yapmak zorunda kalmamalıyız.

Örneğin baud 9600 olduğunda yaklaşık bir bit 104us de giderken 115200 olduğunda 9us de gider. Start,8 bit veri ve stop biti eklersek bir veri için birinde 1ms gerekirken diğerinde 87us gerekir. Bu süreleri hesaplayıp bekleme koyabiliriz. Güvenli taraf için daha fazla koyup boş yere de bekleyebiliriz. MCU içinde bu iş için yapılmış kesmeler varken bunlara hiç gerek yok. Daha önce UART gelen ve gönderim kesmelerini kullanmıştım ama giden kesmesini kullanmamıştım. Bu aralar takıntı haline gelen Delay kullanmama hastalığım nedeniyle ve değiştirilen baud değerlerinde uğraşmamak için "USART_TX_vect" kesmesini kullanarak RS485 konusu ve Uart fonksiyonlarımı güncellemiş olayım.  İşte bu kadar basit bir ekleme ile işlem tamam.

#define MAX_OUT DDRD|=(1<<PORTD2)//RS485 sürücü pinleri 1-0 yapacak makrolar
#define MAX_AL PORTD&=~(1<<PORTD2)
#define MAX_VER PORTD|=(1<<PORTD2)

ISR(USART_TX_vect){
                    MAX_AL;//tx registeri boş kesmesi max485 veri gitmeden veri okuma pozisyonuna geçmemesi için açıldı.
}

Veri Paketi

ADC ile okunan veriyi göndermek gürültüden korunmak için yapılan tüm emeklerden daha önemli. Siz ne kadar ortalama alsanız filtre uygulasanız da veri hatalı giderse bir anlamı kalmaz. Bunun için öncelikle paket oluşturuyoruz. Bu konuda Şenol Eker'e teşekkür ederim. Bu paketin başında iletişimin başladığını belirten bir önsöz (preamble) seçiyoruz. Bu 0x55,0xAA,0xCC ve 0x33 gibi birçok şey olabilir. Ben 0x55 ve 0XAA seçip ilk olarak gönderiyorum. Sonra hangi sensörden geldiğini bilmem gerek bunun için sensörlere özel bir adres tanımlıyorum. Mesela 0x01 gibi bir sayı seçiyorum. Her sensör farklı olsun yeterlidir. Adreste gittikten sonra ADC ile okuduğum değeri 8 bit iki sayıya ayırmam gerekiyor. Çünkü UART ile 8 bitlik olarak yollayabiliyoruz. Bunun için gidecek veriyi "(adc_giden>>8)&0xFF" şeklinde kaydırma yapıyoruz. Yön önemli değil yeter ki diğer tarafta tersi yönde yapılsın. "adc_giden&0xFF" bu şekilde 10 bit ADC verisini ikiye böldük. Karşı tarafta hata denetimi için bu iki veriyi topluyoruz. "veri_toplam=((adc_giden>>8)&0xFF)+(adc_giden&0xFF)" Bu şekilde karşı tarafta aynı işlemi yaparak karşılaştırma yapacağız. sonucun eşit çıkması net doğru demek değil ama hatayı tespit için hızlı bir yöntemdir. CRC16-8 gibi başka hesaplamalara hiç girmek istemedim. Tüm bunları bir araya getirince bir paket oluşturmuş olduk. Bu paketi ne zaman göndereceğiz?

uart_gonder(0x55);
uart_gonder(0xAA);
uart_gonder(SEN);                                                                  
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);//veri toplamı gönderiyoruz.



Aynı hatta birden fazla sensör varken sensörler sürekli veri yollarsa herkesin kafasına göre konuştuğu bir sınıf gibi gürültü olacaktır. Bu paketi sadece istendiği zaman göndereceğiz. Bu istek içinde yine önsöz ve sensör adresi olmalı ki iletişimin başladığı ve muhatabın kim olduğu belli olsun. Gelen veriyi kesme içinde kontrol edeceğiz. Paketin sırası ve verinin doğruluğunu denetleyeceğiz. Bir hata durumunda iletişimi devam ettirmeyeceğiz.

RX Kesmesi

UART ile veri geldiğinde kesme oluşacaktır. Bu her bir veri gelişinde tekrarlanır. Her kesme oluşumunda veriyi kontrol eden bir switch case yapısı kullanacağım. İlk durumda (case 0:) önsöz kontrol edilecek 0x55 ise durum değeri  bir artırıp sıradaki kesme oluşumunda 0xAA kontrol edilecek. Bu işlem pakette yer alan veriler kadar tekrar edilecek. Eğer bir hata varsa durum tekrar en başa alınarak hatalı iletişimin önüne geçilmiş olacak. Gelen veride bir hata olmadığında göndereceğimiz veri paketini göndereceğiz. Tüm bu işlemler aşağıdaki gibi olacaktır.

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:
                                                             if (veri==SEN){// veri SEN adresi ise bir cevap verilir, değilse başa döner
                                                                                 uart_gonder(0x55);
                                                                                 uart_gonder(0xAA);
                                                                                 uart_gonder(SEN);                                                                   
                                                                                 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);//veri toplamı gönderiyoruz.
                                                                                 rx_durum=0;                                                         
                                                             }
                                                             else{rx_durum=0;}
                                                             break;
                                         default:
                                                             rx_durum=0;
                                                             break;
                    }
}

Sensöre istek yapıldı ve sensör istenen veriyi bu şekilde gönderdi. Sensör tarafının yapacağı tüm iş bu kadar. Veriyi alan işleyen veya sadece bir ekrana yazan tarafta benzer işlemler var. Rx kesmesinde fazladan checksum ve ADC verisini tekrar 16 bit olarak birleştirme işlemi var. Bu kısmı en altta görebilirsiniz.

Timer Kesmesi

Veri için istek yapıldıkça sensörler veriyi gönderecektir. Bu isteğin zamanlanması için de kesme kullanıyorum. Sensörler 2ms de bir ölçüm yaparken 10ms bir veri istenmesi burada yeterlidir. Benim projemde farklı hesaplar bulunmakta bunları da eklediğimde 60ms de bir sorgulama yapıyorum. Bu da ortalama hızı 5km/s olan bir biçerdöverin 3mm de bir okuma yapması ve 8 cm de bir bu veriyi analiz etmem demek oluyor. Bu arazinin yüzeyini düşündüğümde oldukça yeterli bir ölçüdür. Kesme oluşunca önsöz ve kesme adresini gönderiyoruz.

ISR (TIMER1_COMPA_vect){//her kesme oluşumunda sensöre veri gönderiyoruz.                
                               uart_gonder(0x55);
                               uart_gonder(0xAA);
                                uart_gonder(SEN);     
}

Sensöre istek yaptık veriyi aldık bir değişkene yazdık. Bunu istediğimiz şekilde kullanabiliriz. Ben burada hem LCD ekrana hem UART ile seri ekrana yazacak şekilde paylaşıyorum. Artık bu paylaşım için yazacağım şeyler öncekilerin tekrarı olacağından burada bitiriyorum. Ana modül ve sensör tarafını ayrı ayrı altta paylaşıyorum. Umarım faydalı olacaktır.

Bu yazı Haluk ŞİMŞEK tarafından yazılmıştır.

/*
 * adc_sensor.c
 *
 * Created: 17.05.2020 13:31:13
 * Author : haluk
 */

#define F_CPU 16000000ul
#include <avr/io.h>
#include <avr/interrupt.h>
//////RS PORT
#define MAX_OUT DDRD|=(1<<PORTD2)
#define MAX_AL PORTD&=~(1<<PORTD2)
#define MAX_VER PORTD|=(1<<PORTD2)
//////uart makro
#define UART_Rx_Boyut 2
#define UART_Tx_Boyut 32
#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 SEN 0x01 // sensörler için farklı olacak
#define  olcme_aralik 499// ölçüm arası bekleme  2ms 499
#define  oranH 0.9// Şenol beyin Devreforum'daki yazısından aldım.
#define  oranL 0.1
//////değişkenler
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, t_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(char*str);
uint8_t uart_gelen();
void adc_basla();
void zaman1_ayar();
////// kesmeler

ISR (TIMER1_COMPB_vect){
                    //ADMUX=(ADMUX & 0xF8) | pin;
}

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:
                                                             if (veri==SEN){// veri SEN adresi ise bir cevap verilir, değilse başa döner
                                                                                 uart_gonder(0x55);
                                                                                 uart_gonder(0xAA);
                                                                                 uart_gonder(SEN);                                                                   
                                                                                 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);//veri toplamı gönderiyoruz.
                                                                                 rx_durum=0;                                                         
                                                             }
                                                             else{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;         
}
ISR(USART_TX_vect){
                    MAX_AL;//tx registeri boş kesmesi max485 veri gitmeden veri okuma pozisyonuna geçmemesi için açıldı.
}
ISR (ADC_vect){        
                    adc_okunan=ADCW;// bu satırdan sonrası ana döngü içinde olabilir.
                    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){
                    MAX_OUT;//rs 485 port çıkış yapıldı
                    MAX_AL;//rs 485 veri alma durumunda
                    adc_basla();// adc başladı
                    uart_basla(115200);   //uart başladı               
                    zaman1_ayar();// timer1 başladı
    while (1){                                                           
    }
}
////// 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){
                    MAX_VER;
                    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;
}
//////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();
}



/*
 * adc_ana_modul.c
 *
 * Created: 17.05.2020 14:16:02
 * Author : haluk
 */

#define F_CPU 16000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdio.h>
//RS485
#define MAX_OUT DDRD|=(1<<PORTD2)
#define MAX_AL PORTD&=~(1<<PORTD2)
#define MAX_VER PORTD|=(1<<PORTD2)
//uart
#define UART_Rx_Boyut 2
#define UART_Tx_Boyut 32
#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)
//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)
//////sensör makro
#define SEN 0x01 // sensör adres
#define  olcme_aralik 2499 // okuma arası bekleme 10ms 2499
////////////////////////////////////////////////////
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[10],str_sayac[10];//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(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 (char *str);
void lcd_git(uint8_t  x, uint8_t y);
void zaman1_ayar();
ISR (TIMER1_COMPA_vect){//her kesme oluşumunda sensöre veri gönderiyoruz.             
                                                             uart_gonder(0x55);
                                                             uart_gonder(0xAA);
                                                             uart_gonder(SEN);      
}
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:
                                                             if (veri==SEN){rx_durum=3;}else{rx_durum=0;}// veri SEN  ise bir sonraki durum, değilse başa döner
                                                             break;
                                         case 3:                                               
                                                             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=4;
                                                             break;
                                         case 4:
                                                             gelen_adc+=veri;// kalan 8 biti geçici değişkene yazdık
                                                             veri_toplam+=veri;//veri toplama ekledik
                                                             rx_durum=5;
                                                             break;
                                         case 5:
                                                             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;
}
ISR(USART_TX_vect){
                    MAX_AL;//tx registeri boş kesmesi max485 veri gitmeden veri okuma pozisyonuna geçmemesi için açıldı.
}
int main(void){   
                    MAX_OUT;
                    MAX_AL;
    lcd_basla();
                    uart_basla(115200);
                    zaman1_ayar();
    while (1) {                 
                                         sprintf(str_sen_adc,"%d",sen_adc);//dizi içine alıyorum
                                         sprintf(str_sayac,"%d",sayac);
                                         /*uart_dizi(str_sen_adc);
                                         uart_gonder('\n');
                                         uart_dizi(str_sayac);  
                                         uart_gonder('\n');*/
                                         lcd_git(0,0);
                                         lcd_dizi(str_sen_adc);
                                         lcd_git(0,1);
                                         lcd_dizi(str_sayac);
                                         if (veri_geldi==1){                                                
                                                             veri_geldi=0;
                                                             LCD_CL; 
                                         }                 
                    }
}
////// 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){
                    MAX_VER;
                    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;
}
//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 (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);
}
//////timer1
void zaman1_ayar(){
                    cli();
                    TCCR1B|=(1<<WGM12)|(1<<CS11)|(1<<CS10);// CTC  ve prescaler 64, .
                    TIMSK1|=(1<<OCIE1A);
                    OCR1A=olcme_aralik;
                    sei();
}