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.
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; }
Hiç yorum yok:
Yorum Gönder