16F628A & DFPlayer

16F628A & DFPlayer & I2CLCD & IR SENSOR & kütüphanesiz DFPlayer. Hepsi bir arada.
Devremiz NEC protokolü kullanan kumandaların kodlarını çözüyor, kodları LCD ekranda gösteriyor, DFPlayer çalıştırıyor. Bu arada I2C ve  RS232 protokollerini kullanıyor. Yani çoklu bir çalışma:
1- IR kod çözme
2- IR kod gösterme
3- Checksum hesaplama, alçak ve yüksek baytları bulma
4- DFPlayer'e komut gönderme

Bu projeyi Arduino Uno ile yapmıştım. PIC'e de uygulamak istedim.Elimde iki adet atıl durumda bulunan 16F628A'yı kullanmak istedim.Ama uygulamaya geçince öncelikle PIC'e göndereceğim komut yapısını, checksum hesaplamayı öğrenmek durumunda kaldım. DFPLayer ile ilgili  mikrobotik.com adresindeki yazı zaten ana başvuru kaynağım.   Bunları yaparken C dilinin biraz daha ayrıntısına girdim. Bu arada değişkenler hakkında da eksiklerim tamamlamak durumunda kaldım. C konusunda en önemli kaynağım: Gökhan DÖKMETAŞ'ın Temel C Programlama notları oldu.


nec_remote_read() fonksiyonunu internet üzerinden ccspicc.blogspot.com adresinden buldum. Bu fonksiyon projemin kalbi oldu. İkinci önemli fonksiyon da hesap() fonksiyonu. Bu fonksiyon DFPlayer'e komut gönderiyor. Komutların alçak bayt'ını (high byte) ve yüksek bayt'ını (high byte) hesaplayıp putc komutu ile DFPayer gönderiyor.

I2C_LCD.c kütüphanesini de internetten buldum. İşin geri kalanı biraz montaj biraz kopyala yapıştır...:)

Malzeme listesini ayrıntılı olarak devre şemasında görebilirsiniz.






Denemelerde özellikle karşılaştığım sorunlar ve çözümleri:
1) Devrenizi baskı devre kartı kullanmadan sadece delikli pertinaksa kuracaksanız, bağlantıları kısa tutun.
2) Beleme devrenizi güzel bir 5v regüle devre kullanın. LM7805'le kurduğum besleme devresini kullandım. Devrede en fazla akım çeken parça LCD.
3) DFPlayer'in sd kart bölümünün üst  metali her ne kadar eksi pinlere bağlı olsa da ben  özellikle ayrı bir kablo ile şaseledim.
4) Devre çalışınca hoparörden parazit gürültüler gelmeye başladı. Bunu da pic'in RB2'den DFPlayer'in TX pinine giden bağlantı arasına 1k direnç koydum, parazit kesildi. Hem RX hem de TX'e giden bağlantılarda 1k dirençler var. DFPlayer'in datasheets dosyasında yalnızca RX'e  direnç bağlanmış.
5) Derleyici olarak PIC C Compiler (CCS C Compiler) kullanıyorum. Programı yazdıkça 16F628A'ın belleği yetersiz gelmeye başlıyor ve kodda ekonomiye gitmeye başlıyorum.
6) Uzaktan kumanda tuşlarına bastığınızda kodu alması-çözmesi derken bazen sapıtabiliyor. İkinci farklı tuş basışta bir önceki kodu sürdürebiliyor. Tuş basışları arası yavaş olmak lazım. Benzer protokol kullanan kumanda kodlarını da çalıştırıyor.
7) Gelen sinyal ve sinyalin çözülmesi sırasında kod kontrolleri iyileştirilebilir diye düşünüyorum.
8) Kumanda üzerinde info tuşuna bastığımızda ekranın ikinci satırında kumandadan gelen kodu gösteriyor, aynı tuşa tekrar basınca eski gösterimine dönüyor. Yani kod okuma da yapabiliyor.
9) Bence kodlar daha da geliştirilip kusursuz hale getirilebilir. Ben bu işlerle amatörce, boş vakitlerimi değerlendirmek amaçlı uğraşıyorum.
10) Mutlaka 8Mhz kristal kullanın. Çünkü nec_remote_read() fonksiyonundaki bütün değişkenler buna göre hesaplanmış. Farklı bir kristal kullanırsanız bu değerleri yeniden hesaplamanız gerekir.  bu kod programın ana iskeleti oldu.
11) LCD üşüyünce ekran parlaklığı değişiyor. Isınınca dengeli çalışıyor.
12) Dibinde masa lambası açılınca ateşleme paraziti şarkı atlatabiliyor.
13) Ama her şeye rağmen oldukça zevkli ve gayet güzel çalıyor.
14) NEXT ve PREV komutlarını gönderdiğinizde, bir sonraki veya bir önceki şarkı çalıyor ve bitince duruyor. Bu sorunu daha önce gerçekleştirdiğim Arduino'lu projemde BUSY portunu dinleyerek gidermiştim. Benzer çalışma burada da yapılabilir. O yazıma da bakabilirsiniz.
15) Projemizin geliştirilmesi artık sizin hayalinize kalmış

Çalışmalarınızda yararlı olması dileklerimle.











 main.c dosyası:

/* KODLA GİTSİN  KODLA
   -------Developed by Kodla Gitsin-----

   DFPlayer MP3 PLAYER UYGULAMASI
   Malzemeler:
   16F628A, DFPlayer Modülü, IR alıcı,
   I2C modüllü 16X2 LCD
   Herhangi bir NEC Protokou kullanan uzaktan kumanda
   Ben Next UK-660 uydu alıcısı (Next Minix Punto Plus) kumandasını kullandım
   (kodlarını öğrenebildiğiniz NEC Protokol kullanan her kumandayı kullanabilirsiniz)
 
   https://ccspicc.blogspot.com/2016/07/nec-ir-remote-control-decoder-pic16f84a-microcontroller-ccs-c-code.html?m=1
   */
#include <16f628A.H>    //kullanılacak pic
//sigorta ayarları ----------------------------------------------
#fuses XT,NOWDT,NOPROTECT,NOPUT,NOBROWNOUT //pic sigorta ayarları
#fuses nomclr    //mclr devre dışı
//---------------------------------------------------------------
#include <stdio.h>
#include <stdint.h>
//#include <stdbool.h>
//bool Playing=false;   //<stdbool.h> kütüphanesi ile.....  (kullanmadığım için kapattım)
#use delay (clock=8000000)   // kesinlikle 8 MHz kullanın. Çünkü nec_remote_read() fonksiyonu 8Mhz'e göre yazılmış
//#use I2C(master, I2C1, FAST = 100000, STREAM = I2C_LCD)
#use i2c(Master,Fast,sda=PIN_B4,scl=PIN_B5,STREAM = I2C_LCD)   //LCD ile haberleşmek için I2C protokolü

#include <I2C_LCD.c>  //I2C LCD sürücü kaynak kodu
//#bit RCIF=0xF9E.5
//#include <UART.h>
//#include <DFPlayer.h>
#use RS232 (baud = 9600, bits = 8, parity = N, xmit = pin_B2, rcv = pin_B1)   //DFPlayer ile haberleşmek için RS232 protokulü
//#use fast_io(B)
//-------------------------------------------------------------------------------------------------------------------------
#define led1 pin_A2 // 1 nolu pin
//buton kullanırsanız:
//#define buton1 pin_A3 // 2 nolu pin (1K...4K7 direnç ile gnd'ye) ---buton +5v dan tetiklemeli
//#define buton2 pin_A4 // 3 nolu pin (1K...4K7  direnç ile gnd'ye) ---buton +5v dan tetiklemeli
//#define  DFPlayer_BUSY PIN=A1;  // DFPlayer BUSY portu bağlantısı---

#define IR_sensor pin_B0 // IR receiver IIR alıcının bağlı olduğu pin)
//#define BUSY pin_B3      // DFPlayer BUSY port dinleme pini

//DFPlayer-----------------------------------------------------------------------------------------------------------------
# define Start_Byte 0x7E
# define Version_Byte 0xFF
# define Command_Length 0x06
# define End_Byte 0xEF
# define Acknowledge 0x00

//--------NEXT KUMANDA KODLARI----NEC protocol----Next UK-660 uydu alıcısı (Next Minix Punto Plus) kumandası-----NEC protocol ------
// kullanmadıklarımı pasivize ettim
# define t_play      0x80BF1BE4    // play
# define t_p_inc     0x80BF53AC    // p+
# define t_p_dec     0x80BF4BB4    // p-
# define t_vol_inc   0x80BF837C    // vol +
# define t_vol_dec   0x80BF9966    // vol -
# define t_equalizer 0x80BF6B94    // equaliser  //epg
# define t_info      0x80BF0BF4    // info
# define t_usb       0x80BF4AB5    // USB   
# define t_sd        0x80BF8B74    // SHIFT
# define t_pause     0x80BFE31C    // pause
//# define t_next      0x80BFAB54    // next
//# define t_prev      0x80BF639C    // prev
//# define t_mute      0x80BF39C6    // mute
//# define t_ok        0x80BF738C    // ok 
//# define t_stop      0x80BF2BD4    // stop
//# define t_apps      0x80BF817E    // APPs 
//# define t_pic       0x80BFC13E    // PICTURE
//# define t_menu      0x80BFA956    // MENU
//# define t_exit      0x80BFA3C5    // EXIT
//# define t_pr        0x80BF41BE    // PR
//# define t_tvrad     0x80BF9B64    // TV/RADYO
//# define rak0    0x80BFE11E   // 0
//# define rak1    0x80BF49B6   // 1
//# define rak2    0x80BFC936   // 2
//# define rak3    0x80BF33CC   // 3
//# define rak4    0x80BF718E   // 4
//# define rak5    0x80BFF10E   // 5
//# define rak6    0x80BF13EC   // 6
//# define rak7    0x80BF51AE   // 7
//# define rak8    0x80BFD12E   // 8
//# define rak9    0x80BF23DC   // 9
//--------------------------------------------------------------------------------------
int i=1;                  // equalizer seçimlerinde kullandığımız değişken
int volume = 10;          // başlangıç volüm seviyesi
int gosterge=1;           // ekran durum - basılı tuşa göre ilgili ekran görüntüsüne yollar - ekran() fonksiyonunca kullanılır
int kaynak=0;             //

unsigned int32 ir_code;
unsigned int16 address;
unsigned int8 command, inv_command;

//-------------------------------------------------------------------------------------
// IR sensörden aldığı kodu çözüyor bu bölüm 8 Mhz kristale göre düzenlenmiştir
short nec_remote_read(){
  unsigned int8 count = 0, i;
  // Check 9ms pulse (remote control sends logic high)
  while((input(IR_sensor) == 0) && (count < 200)){
    count++;
    delay_us(50);}
  if( (count > 199) || (count < 160))     // NEC protocol?
    return FALSE;                       
  count = 0;
  // Check 4.5ms space (remote control sends logic low)
  while((input(IR_sensor)) && (count < 100)){
    count++;
    delay_us(50);}
  if( (count > 99) || (count < 60))       // NEC protocol?
    return FALSE;                       
  for(i = 0; i < 32; i++){
    count = 0;
    while((input(IR_sensor) == 0) && (count < 14)){
      count++;
      delay_us(50);}
    if( (count > 13) || (count < 8))      // NEC protocol?
      return FALSE;                       
    count = 0;
    while((input(IR_sensor)) && (count < 40)){
      count++;
      delay_us(50);}
    if( (count > 39) || (count < 8))      // NEC protocol?
      return FALSE;                         
    if( count > 20)                       // If space width > 1ms
      bit_set(ir_code, (31 - i));         // Write 1 to bit (31 - i)
    else                                  // If space width < 1ms
      bit_clear(ir_code, (31 - i));       // Write 0 to bit (31 - i)
  }
  return TRUE;
}
//-------------------------------------------------------------------------------------
// DFPlayer'e komut gönderimi:
// komutların alçak bayt'ını (high byte) ve yüksek baytını(high byte) hesaplayıp
// putc komutu ile DFPayer' komut gönderiyor
 void hesap(int CMD,int Par1,int Par2) {
 delay_ms(100);
 uint16_t checksum=(65535-(Version_Byte + Command_Length + CMD + Acknowledge + Par1 + Par2))+1;
   unsigned char hi=(checksum >>8);
   unsigned char lo=checksum & 0x00FF;
 
   int high=hi;
   int low=lo;

// komutu gönderiyoruz.---------------------------------------------
  putc(Start_Byte);
   putc(Version_Byte);
     putc(Command_Length);
      putc(CMD);
       putc(Acknowledge);
        putc(Par1);
         putc(Par2);
          putc(high);
           putc(low);
            putc(End_Byte);               
 }

 //-------------------------------------------------------------------------------------

 void LCD_Yaz()
 {
  LCD_Begin(0x4E);                    // LCD modülünü I2C ile başlat address = 0x4E
  delay_ms(1000);
  }

void ekran()
{
switch (gosterge) {  //seçimlere göre ekrandaki mesajlar
case 1:
 //LCD_Goto(0, 2);             
  //     LCD_Out("Kodla Gitsin");
  LCD_Goto(1, 1);       
    LCD_Out("Play ");
    LCD_Goto(5, 1);
  printf(LCD_Out,"%c",0x7E);  //sağ ok
delay_ms(100);
break;

case 2:   
     address = ir_code >> 16;
      command = ir_code >> 8;
      inv_command = ir_code; 
       LCD_Goto(1, 2); 
       LCD_Out("irCod:");   
      printf(LCD_Out,"%4LX %2X %2X ",address,command,inv_command);     
      delay_ms(50);     
break;

case 3:
    LCD_Goto(1, 1);       
    LCD_Out("Next");
  LCD_Goto(5, 1);
  printf(LCD_Out,"%c%c",0x7E,0x7E);  //sağ ok   
delay_ms(1000);
LCD_Goto(1, 1);
LCD_Out("Play ");
    LCD_Goto(5, 1);
  printf(LCD_Out,"%c ",0x7E);  //sağ ok
  //LCD_Goto(6, 1);       
 //   LCD_Out(" ");
break;

case 4:
  LCD_Goto(7, 1);       
    LCD_Out("Volum: ");
   LCD_Goto(13, 1);
   if (volume<10){
   LCD_Out("0"); printf(LCD_Out,"%i",volume);}
  else {printf(LCD_Out,"%i",volume);}
   
break;
case 5: 
LCD_Goto(1, 1);
  printf(LCD_Out,"%c%c",0x7F,0x7F);  //sol ok
  LCD_Goto(3, 1);       
    LCD_Out("Prev");   
delay_ms(1000);
LCD_Goto(1, 1);
LCD_Out("Play ");
    LCD_Goto(5, 1);
  printf(LCD_Out,"%c",0x7E);  //sol ok
  LCD_Goto(6, 1);       
    LCD_Out(" ");
break;

case 6: 
  LCD_Goto(1, 2);         
    LCD_Out("***");
break;
case 7: 
  LCD_Goto(1, 2);         
    LCD_Out("SD ");
break;
case 8: 
  LCD_Goto(1, 2);         
    LCD_Out("USB");
break;

case 9:  // ilk açılışta ekran değerleri
LCD_Goto(1, 1);       
    LCD_Out("Play ");
    LCD_Goto(5, 1);
  printf(LCD_Out,"%c",0x7E);  //sol
  LCD_Goto(7, 1);       
    LCD_Out("Volum: ");
   LCD_Goto(13, 1);
   if (volume<10){
   //LCD_Out("0");
   printf(LCD_Out,"%i 0",volume);}
  else {printf(LCD_Out,"%i",volume);}
   LCD_Goto(1, 2);         
    LCD_Out("                ");
   LCD_Goto(1, 2);         
    if (kaynak==1) {LCD_Out("SD ");}
     else if (kaynak==2) {LCD_Out("USB");}
      else{  LCD_Out("***");}
    LCD_Goto(7, 2);       
    LCD_Out("Normal ");
break;
}
}

//-------------------------------------------------------------------------------------
void eqAyarla()                 
                // equalizer modları fonksiyonu  switch  case ile seçiyoruz.
{ gosterge=0;
switch (i) {

case 1:         // normal
hesap(0x07,0,0);
LCD_Goto(7, 2);
    LCD_Out("Normal ");
i=2;
break;

case 2:        // pop
hesap(0x07,0,1);
LCD_Goto(7, 2);   
    LCD_Out("Pop    ");
i=3;
break;

case 3:       // rock
hesap(0x07,0,2);
LCD_Goto(7, 2);
    LCD_Out("Rock   ");
i=4;
break;

case 4:      // jazz
hesap(0x07,0,3);
LCD_Goto(7, 2); 
    LCD_Out("Jazz   ");
i=5;
break;

case 5:      // classic
hesap(0x07,0,4);
LCD_Goto(7, 2); 
    LCD_Out("Classic");
i=6;
break;

case 6:      // bass
hesap(0x07,0,5);
LCD_Goto(7, 2);
LCD_Out("Bass   ");
i=1;

break;
}   
}
//-------------------------------------------------------------------------------------
void volumeINC()                 // volüm +   fonksiyonu
{
volume = volume+1;
if(volume==31)
{
volume=30;
}
hesap(0x06, 0, volume);
gosterge=4;
}
//-------------------------------------------------------------------------------------
void volumeDEC()                 // volüm -   fonksiyonu
{
volume = volume-1;
if(volume==-1)
{
volume=0;
}
hesap(0x06, 0, volume);
gosterge=4;
}
//-------------------------------------------------------------------------------------
void main(void)
   { 
   LCD_Yaz();
   set_tris_b(1);
   gosterge=9;
  ekran();
      delay_ms(1000);                //uzaktan kumanda komutlarının düzgün çalışması için ilk açılıta bekletiyor.
   
   hesap(0x11,0,1);  // FIRST --- ilk şarkıdan çalmaya başlıyor 
   hesap(0x06, 0, volume);   //ilk açılış volümü

    while(TRUE){

    while(input(IR_sensor));  // RB0 pinini (IR sensör girişi) dinliyor
          nec_remote_read();  // RB0 giriş alırsa kodu çözmeye gidiyor
 
    {       
             output_high(led1);  // görsel amaçlı LED'i yakıyoruz         
         
   if (ir_code==t_p_inc){
 hesap(0x01,0,1);        // next
gosterge=3;
     }

   if (ir_code==t_p_dec){ 
 hesap(0x02,0,1);        // prev 
 gosterge=5;       
    }
 
    if (ir_code==t_equalizer){ 
 eqAyarla();            //  equalizer         
    }
 
     if (ir_code==t_vol_inc){ 
 volumeINC();           // volum +++       
    }
 
     if (ir_code==t_vol_dec){ 
 volumeDEC();          //  volum ---       
    }
 
 
     if (ir_code==t_info){   // player modu- IR kod okuma modu arasında geçiş yapar
                             // ekran 2.satırda kumandadan gelen kodu yazar                           
      // aynı tuşu kullanarak iki durum arasıda geçiş yapıyoruz
     if (gosterge>2) gosterge=1;
    gosterge=gosterge+1;
    if (gosterge==3)
    gosterge=9; 
    }
 
    if (ir_code==t_pause) {
    hesap(0x0E,0,0);       // pause
    }
    /*
    if (ir_code==t_play){
 hesap(0x11,0,1); // FIRST
//Playing=true;
            }
            */
    if (ir_code==t_play){
    hesap(0x0D,0,0);      // play - pause'den devam -
    }
 
    if (ir_code==t_usb){   // usb modu
//hesap(0x3F, 0, 1);  //sıfırla
 hesap(0x09,0,1);        //usb
  hesap(0x11,0,1); // FIRST
   kaynak=2;
    gosterge=8; 
    }
 
    if (ir_code==t_sd){   // sd kart modu
   
   //  hesap(0x3F, 0, 0); //sıfırla
 hesap(0x09,0,0);        //sd
  hesap(0x11,0,1); // FIRST
  kaynak=1;
   gosterge=7; 
    }

  delay_ms(250);   // kumandadan gelen kodları kararlı yakalaması için alt bekleme sınırı
 ekran();
      output_low(led1);   //görsel amaçlı yakdığımız LED'i söndürüyoruz 
    }
   }
   }

//-------------------------------------------------------



I2C_LCD dosyası:


// CCS C driver code for I2C LCDs (HD44780 compliant controllers)
// https://simple-circuit.com/

#define LCD_BACKLIGHT          0x08
#define LCD_NOBACKLIGHT        0x00
#define LCD_FIRST_ROW          0x80
#define LCD_SECOND_ROW         0xC0
#define LCD_THIRD_ROW          0x94
#define LCD_FOURTH_ROW         0xD4
#define LCD_CLEAR              0x01
#define LCD_RETURN_HOME        0x02
#define LCD_ENTRY_MODE_SET     0x04
#define LCD_CURSOR_OFF         0x0C
#define LCD_UNDERLINE_ON       0x0E
#define LCD_BLINK_CURSOR_ON    0x0F
#define LCD_MOVE_CURSOR_LEFT   0x10
#define LCD_MOVE_CURSOR_RIGHT  0x14
#define LCD_TURN_ON            0x0C
#define LCD_TURN_OFF           0x08
#define LCD_SHIFT_LEFT         0x18
#define LCD_SHIFT_RIGHT        0x1E

#ifndef LCD_TYPE
   #define LCD_TYPE 2           // 0=5x7, 1=5x10, 2=2 lines
#endif

int1 RS;
unsigned int8 i2c_addr, backlight_val = LCD_BACKLIGHT;

void LCD_Write_Nibble(unsigned int8 n);
void LCD_Cmd(unsigned int8 Command);
void LCD_Goto(unsigned int8 col, unsigned int8 row);
void LCD_Out(unsigned int8 LCD_Char);
void LCD_Begin(unsigned int8 _i2c_addr);
void Backlight();
void noBacklight();
void Expander_Write(unsigned int8 value);

void LCD_Write_Nibble(unsigned int8 n) {
  n |= RS;
  Expander_Write(n);
  Expander_Write(n | 0x04);
  delay_us(1);
  Expander_Write(n & 0xFB);
 delay_us(50);
}

void LCD_Cmd(unsigned int8 Command) {
  RS = 0;
  LCD_Write_Nibble(Command & 0xF0);
  LCD_Write_Nibble((Command << 4) & 0xF0);
}

void LCD_Goto(unsigned int8 col, unsigned int8 row) {
  switch(row) {
    case 2:
      LCD_Cmd(0xC0 + col-1);
      break;
    case 3:
      LCD_Cmd(0x94 + col-1);
      break;
    case 4:
      LCD_Cmd(0xD4 + col-1);
    break;
    default:      // case 1:
      LCD_Cmd(0x80 + col-1);
  }
}

void LCD_Out(unsigned int8 LCD_Char){
  RS = 1;
  LCD_Write_Nibble(LCD_Char & 0xF0);
  LCD_Write_Nibble((LCD_Char << 4) & 0xF0);
}

void LCD_Begin(unsigned int8 _i2c_addr) {
  i2c_addr = _i2c_addr;
  Expander_Write(0);
  delay_ms(40);
  LCD_Cmd(3);
  delay_ms(5);
  LCD_Cmd(3);
 delay_ms(5);
  LCD_Cmd(3);
  delay_ms(5);
  LCD_Cmd(LCD_RETURN_HOME);
  delay_ms(5);
  LCD_Cmd(0x20 | (LCD_TYPE << 2));
 delay_ms(50);
  LCD_Cmd(LCD_TURN_ON);
  delay_ms(50);
  LCD_Cmd(LCD_CLEAR);
 delay_ms(50);
  LCD_Cmd(LCD_ENTRY_MODE_SET | LCD_RETURN_HOME);
delay_ms(50);
}

void Backlight() {
  backlight_val = LCD_BACKLIGHT;
  Expander_Write(0);
}

void noBacklight() {
  backlight_val = LCD_NOBACKLIGHT;
  Expander_Write(0);
}

void Expander_Write(unsigned int8 value) {
  I2C_Start(I2C_LCD);
  I2C_Write(I2C_LCD, i2c_addr);
  I2C_Write(I2C_LCD, value | backlight_val);
  I2C_Stop(I2C_LCD);

}

//----------------------------------------------------------




PROGRAM DOSYALARINI İNDİR

KULLANILAN PROGRAMLAR: PIC C Compiler (CCS C Compiler  5.015), microbrn, 
KULLANILAN PROGRAMLAYICI: k150_pic_programmer



Yorumlar