MSP430 Launchpad – RS232 ile Seri Haberleşme

RS232, genelde farklı cihazlar arasında haberleşmeyi sağlayan bir seri haberleşme protokolüdür. Bu protokol sayesinde denetleyicimiz ile bilgisayarımızı haberleştirebilir, bilgisayar ile kontrol edilen devreler yapabiliriz.

RS232 asenkron bir haberleşme türüdür. Yani haberleşme esnasında bir clock sinyali gönderilmez. Bu yüzden cihazların sağlıklı haberleşebilmeleri için ikisinin de aynı hızda alım / gönderim yapacak şekilde ayarlanması gerekir.

Bazı gelişmiş denetleyicilerde kolayca RS232 ile haberleşme yapabilmeyi sağlayan dahili modüller bulunabilmektedir. Ancak benim kullandığım MSP430G2231’de böyle bir modül yok. Bu yüzden “bit banging” denilen bir yöntem kullanacağım. Bu yöntemde yapmam gereken şey, RS232 protokolünün standartlarına uyarak gönderme ve alma işlemlerini bit bit yazılımla yapmak.

Aşağıda RS232 protokolünün zamanlama diyagramını görebilirsiniz:

RS232 zamanlama diyagramı

RS232 zamanlama diyagramı

RS232 -15 – +15V aralığında çalışıyor. Lojik “1” -15, lojik “0” +15V. Şekilden de görebileceğimiz üzere hat normalde “1” seviyesinde. “0”a düştüğü anda veri iletimi başlıyor. İlk gelen bit start biti. Daha sonra en değerliksiz bitten en değerlikliye doğru 8 bitlik esas verimiz geliyor. En son ise hat “1”e çekiliyor, bu da stop biti. Parite gelen verinin doğru olup olmadığını hata payıyla test etmeye yarıyor. Bu uygulamada ben parite kullanmayacağım. Parite hakkında şuradan bilgi alabilirsiniz.

RS232’nin +15 – -15V gerilimleriyle çalıştığını biliyoruz. Normalde MSP430 ile bu gerilim değerlerini elde edemiyoruz. Bu gerilim düzeyini değiştirmek için piyasada satılan entegreler bulunmakta. (MAX232 gibi.) Ancak launchpad bize çok güzel bir özellik sağlıyor. Herhangi bir entegre kullanmadan, bilgisayar tarafında oluşturduğu sanal seri port aracılığıyla, denetleyicinin P1.1 (TXD) ve P1.2 (RXD) pinleri ile RS232 haberleşmesi yapabiliyoruz.

Yazdığım kodu aşağıda paylaşıyorum. Herhangi bir RS232 uygulamasında kolayca kullanılabileceğini düşündüğüm bir kod. Bu kod, bizim bilgisayardan gönderdiğimiz veriyi aynen  geri gönderecektir. Veri göndermek ve almak için bilgisayar tarafında herhangi bir terminal programını kullanabiliyoruz. Yazılım 9600 baudrate’e göre yazılmıştır. Bu yüzden terminal programında da 9600 baud seçilmelidir.

#include "io430.h"
#include "in430.h"
#include "stdbool.h"

bool rs232Alinacak = 0;      // Veri alınacak mı?
bool rs232Alindi = 0;        // Veri alındı mı?
bool rs232Gonderilecek = 0;  // Veri gönderilecek mi?
int rs232Bit = 0;            // O an kaçıncı bit işlem görüyor?
int rs232Gelen = 0;          // Gelen veri.
int rs232Giden = 0;          // Giden veri.

void rs232Gonder(int);       // Veriyi gönderime hazırlayan fonksiyon.

void main( void )
{
  // Stop watchdog timer to prevent time out reset
  WDTCTL = WDTPW + WDTHOLD;

  P1DIR = 0x02;           // P1.1 (TXD) çıkış.
  P1OUT = 0x02;           // P1.1 idle durumda "1" olmalı.
  P1IE = 0x04;            // P1.2 (RXD)'den kesme gelecek.
  P1IES = 0x04;           // Kesme düşen kenarda gelecek. (Start biti)
  TACCR0 = 108;           // 1000000/9600 = 104. Kritik zamanlama olmaması için biraz arttırdım.
  TACCTL0 = CCIE;         // Timer kesmesine izin ver.
  __enable_interrupt();   // Genel kesmelere izin ver.

  while(1)                      // Sonsuz döngü içinde,
  {
    if(rs232Alindi)             // Veri alındıysa,
    {
      rs232Alindi = 0;          // Alındı bayrağını temizle.

      rs232Gonder(rs232Gelen);  // Gelen veriyi aynen gönder.
    }
  }
}

void rs232Gonder(int rs232GidenFonk)
{
  if(rs232Gonderilecek == 0)      // Eğer o anda veri gönderilmiyorsa,
  {
    rs232Giden = rs232GidenFonk;  // Fonksiyona gönderilen değeri global giden değişkenine al.
    rs232Giden = rs232Giden << 1; // 1 bit sola kaydırarak start biti eklendi.
    rs232Giden += 512;            // En sola stop biti eklendi.
    rs232Gonderilecek = 1;        // Gönderilecek bayrağı set edildi.
    rs232Bit = 0;                 // 0. bitten başlanacak.
    TACTL = TASSEL_2 + ID_0 + MC_1 + TACLR;   // SMCLK + Div = 1 + Up mod + Sıfırla.
  }
}

#pragma vector = PORT1_VECTOR
__interrupt void portKesmesi (void)
{
  if(P1IFG_bit.P1IFG_2)       // Kesme P1.2'den geldiyse,
  {
    if(rs232Alinacak == 0)    // Eğer o anda veri alınmıyorsa,
    {
      rs232Gelen = 256;       // Gelen veriyi 256 yap. Bu şekilde bit bit kaydırırken olası sıkıntı engellenecek.
      rs232Alinacak = 1;      // Alınacak bayrağını 1 yap.
      rs232Bit = 0;           // 0. bitten başlanacak.
      TACTL = TASSEL_2 + ID_0 + MC_1 + TACLR;   // SMCLK + Div = 1 + Up mod + Sıfırla
    }
    P1IFG_bit.P1IFG_2 = 0;    // P1.2 kesme bayrağını temizle.
  }
}

#pragma vector = TIMERA0_VECTOR
__interrupt void timerKesmesi (void)
{
  if(rs232Alinacak == 1)              // Eğer veri alınacaksa,
  {
    if(rs232Bit < 9)                  // Toplam 9 bit alınacak. 8 bit veri + 1 stop biti.
    {       
      rs232Gelen = rs232Gelen >> 1;   // Zaman geldiğinde gelen veriyi 1 bit sağa kaydır,
      if(P1IN_bit.P1IN_2 == 1)        // Eğer bit "1" ise,
      rs232Gelen += 256;              // Gelen verinin en soldaki bitine 1 yaz. (2^8, 8. bit)
    }
    else                              // 9 bit de alındıysa,
    {
      TACTL = MC_0;                   // Timerı durdur.
      rs232Gelen -= 256;              // Veriden stop bitini ayıkla.
      rs232Alinacak = 0;              // Alınacak bayrağını "0" yap.
      rs232Alindi = 1;                // Alındı bayrağını "1" yap.
    }
  }

  if(rs232Gonderilecek == 1)
  {
    if(rs232Bit < 10)   // Toplam 10 bit gidecek. 8 bit veri + 1 start biti + 1 stop biti.     
    {       
      P1OUT_bit.P1OUT_1 = rs232Giden & BIT0;  // Giden verinin 0. bitini P1.1'e bas.       
      rs232Giden = rs232Giden >> 1;           // Giden veriyi 1 bit sağa kaydır.
    }
    else                      // Eğer 10 bit de gönderilmişse,
    {
      TACTL = MC_0;           // Timerı durdur.
      rs232Gonderilecek = 0;  // Gönderilecek bayrağını sıfırla.
    }
  }
  rs232Bit++;       // Zaman geldikçe kaçıncı bitte olduğumuz artacak.
}

Kısaca açıklayalım. Gönderme işlemi için fonksiyona gönderilen veriye start ve stop bitleri ekleniyor. Oluşan son 10 bitlik verinin 0. biti P1.1 pinine basılıyor. Daha sonra veri bir bit sağa kaydırılarak, zaman geldiğinde bir sonraki bitin pine basılması sağlanıyor. Bu işlem 9600 baud (saniyede 9600 bit gidecek) hızda yapılıyor. Göndermek istediğimiz veriyi rs232Gonder fonksiyonuna parametre olarak vererek gönderim için hazırlanmasını ve gönderilmesini sağlıyoruz.

Alım işleminde ise P1.2’den düşen kenarda kesme bekleniyor. Kesme geldiğinde bu start biti olacaktır. Bundan sonra P1.2, uygun süre aralıklarla kontrol edilerek toplam 9 bit alınıyor. Son olarak da stop biti bu 9 bitlik veriden ayıklanarak gerçek verimiz elde edilmiş oluyor. Veri alımı tamamlandığında rs232Alindi bayrağı set ediliyor. Bu durumda main fonksiyonu içinde kısa bir kontrol ile veri gelip gelmediğini anlayabiliyoruz. İşimizi tamamladıktan sonra bu bayrağı temizlememiz gerekiyor. Aksi durumda sürekli yeni veri gelmiş gibi davranacaktır. Gelen veri rs232Gelen değişkeninde tutuluyor. Buradan veriyi alarak istediğimiz işlemi yapabiliriz.

Gönderdiğimiz veriyi 8 bitlik paketler halinde göndermemiz gerekiyor. Yani karakter karakter. Bir string gönderildiğinde bu stringi yakalayacak bir fonksiyon yazılabilir. Yine aynı şekilde string gönderecek bir fonksiyon da yazılabilir. Aşağıda devrenin çalışmasından bir görüntüyü görebilirsiniz.

Terminal ekranı

Terminal ekranı

Proje dosyalarını buradan indirebilirsiniz.

Kolay gelsin…

Share
  1. A. Tahir İnce

    Bana da bu yazılım lazımdı , çok teşekkürler. 😀

    Bu arada şöyle bir donanım ‘UART to USB Breakout ‘ ile COM portu üzerinde güzel VB uygulamaları yapılabilir msp430 ile.

  2. A. Tahir İnce

    Bu kodu deneme amacıyla 2231’e yükledim.Sanal bağlantıyı fxdev ile kullanayım dedim ama ara sıra sapıtıyor.Nedense daima doğru veri yollamıyor.

    Launchpad’e 32Khz kristali daha lehimlememiş olmamdan kaynaklanabilir mi?
    Gördüğüm kadarı ile sen de iç osilatorlerinin frekansını kodda belirtmemişsin.

    • Uygulamada dahili RC osilatörü kullandım. Timer’da işaret kaynağı olarak SMCLK kullanıldı, yani 1MHz frekansında. Aslında şu satırda frekansa dair bir bilgi var :

      TACCR0 = 108; // 1000000/9600 = 104. Kritik zamanlama olmaması için biraz arttırdım.

      Bir de şöyle bir durum var, elimdeki 2 tane G2231’den birinde sorunsuz çalışıyor, diğerinde sizin dediğiniz gibi her zaman doğru veriyi yollamıyor. Nedenini bilmiyorum. Aslında 32 KHz’lik kristal takılıp zamanlamalar ona göre ayarlansa daha sağlıklı olabilir. Elinizde başka denetleyici varsa onunla denemenizi önereceğim. Kolay gelsin…

  3. A. Tahir İnce

    :mrgreen:
    Kısa bir soru daha soracaktım.

    g2211(herhalde 2231 den ADC harici bir farkı yoktu 😕 )’in 8 bacağını seçip bunları giriş olarak tanımlayacağım uğraştığım kodda.
    Bu gelen 8 pini okutup(girişe & maskeleme yaparım herhalde) seri iletişim için 1.1 ve 1.2 ile beraber kullanarak paralel to seri gibi basit bir uygulama yapmaya çalışıyorum.
    Ama nerede ise 1Mhz hızla gelen sinyal nasıl 9600 baud’a çevrilir vs emin değilim.

    • Seri iletişimdeki zamanlama kısmını timer ile yapacağınız için CCR0 registerını, timer saniyede 9600 kere kesme üretecek şekilde ayarlamalısınız. Bu durumda 1000000/9600 =~104 olarak buluruz CCR0 değerini. Ancak dahili osilatör mükemmel çalışmayacaktır. Bu yüzden ben bu 104 değerini biraz arttırarak kullanmıştım. Böylece kontrolün kritik zamanda olmasını önlemiştim. Örneğin yazıda 108 idi. Bu şekilde ayarlayabilirsiniz. Kolay gelsin…

  4. Mrb,

    bi sorum olucaktı.elimdeki msp430fg4618 den veri göndermek istiyorum.veri gidip gitmediğini kontrol etmek için msp nin TX ucuna scope ile bakıp sinyal var mı diye kontrol etmek amacım.bunun için

    main fonksiyonunda

    for(;;){
    UCA0TXBUF =55;
    }

    yapınca TX ucunda sinyal göremiyorum bu şekilde yapmakla sadece sembolik sabitin değirini mi değiştirmiş oluyorum acaba.TX ucuna nasıl veri gönderebilirim ?

    • Merhaba,
      Bahsettiğiniz modelde UART modülü var ve siz bunu kullanmak istiyorsunuz anladığım kadarıyla. Daha önce hiç UART modülü ile çalışmadığım için bu konuda bir bilgim yok. Ancak bir ADC ya da timer gibi önce modülün ayarlarının yapılması gerekecektir diye düşünüyorum. Ayrıca burada UCA0TXBUF’a 55 değerini atadınız ama bence göndermek için de bir şeyler yapmanız gerekiyor. Yine de bilgi sahibi olmadığımdan yardımcı olamayacağım. İnternetten bununla ilgili örnek kodlar bulacaksınızdır. Onları incelemenizi öneriyorum. Kolay gelsin…

  5. ztn TI ın 4618 için örnek kodları üzerinde oynama yaparak çalışıyorum ama olmuyor.

    biraz dAha araştırıyım.Teşekkürler Berk..

  6. merhaba berk;
    stdbool kutuphanesi ne işe yarıyor ve degişkenler neden bool tipinde yardımcı olursan sevinirim
    iyi çalısmalar;

    • Merhaba,
      stdbool kütüphanesi boolean değişken tanımlayabilmemizi sağlıyor. Boolean tipinde veri ya doğrudur (“1”) ya da yanlıştır (“0”). Bool tanımladığım değişkenlerde bana gerekli olan da sadece bu olduğu için RAM’den tasarruf edeceğimi düşünerek boolean tanımladım. Integer da tanımlasam işleyişte değişen bir şey olmazdı. Sadece aynı değişken gereksiz yere RAM’de daha fazla yer tutardı. Kolay gelsin…

  7. Merhabalar
    Ben bu kodu MSP430g2553’e ayarlamaya calistim. Yaptigim degisiklikler io430.h kutuphanesini msp430g2553.h ile degistirmek oldu, ve 2553 de 2 tane timer oldugu icin TIMER_A0’u TIMER0_A0 ile degistirdim ve “P1IFG_bit.P1IFG_2” gibi komutlari “P1IFG & BIT2” ile degistirdim. Bu degisiklikleri yaptiktan sonra programi denedigimde Serial Port Terminal’dan yolladigim ilk datayi, sorunsuz bir sekilde geri alirken, 2. ve sonrasinda yolladigim datayi saglikli sekilde alamiyorum. 2553’e data yollamadan once mikrokontrollore reset atmam gerekiyor. Bunun nedeni ne olabilir yardimci olabilirmisiniz? Degistirdigim koda burdan ulasabilirsiniz: http://dl.dropbox.com/u/23063108/deneme.txt

    tesekkurler

    • Belki sorunuza tam cevap olmayacak ama, MSP430G2553’te uart modülü var ve yazıda anlattığım yönteme göre çok daha iyi bir çözüm. UART modülünü kullanmanızı önereceğim, zira ben de G2553 kullandığımda uart modülünü kullanarak haberleşiyorum. TI’ın örnek kodları gayet güzel, üzerinde oynamalar yaparak basit bir şekilde haberleşebilirsiniz. Geç cevap verdiğim için kusura bakmayın. 🙂
      Kolay gelsin…

  8. aynı kodu bende çalıştırdım fakat gönderidiğim karakterin aynısı geri gelmiyor… Acaba usb-serial converter kullandığım için midir yoksa farklı bir şeyi mi yanlış yapıyorum?

    • Merhaba,

      Kodda zamanlamayı timer kullanarak yaptım. Timer için kullandığım frekans kaynağı RC osilatör ve çok stabil çalışmıyor. Böyle olunca denetleyiciden denetleyiciye farklı sonuçlar ortaya çıkabiliyor. Örneğin bendeki msp430g2231’lerden birinde sorunsuz çalışırken, diğerinde sizdeki durum oluşuyor. Bu yazıdaki kod ve konfigürasyon bu yüzden çok sağlıklı değil. Daha iyi bir sonuç için frekans kaynağı olarak launchpad ile gelen kristal osilatör kullanılabilir (kodun biraz değişmesi gerekir), ya da uart modülü içeren bir msp430 tercih edilebilir (kodun tamamen değişmesi gerekir :)).

  9. Merhaba Berk,
    IAR 6.0 ‘da kodunu derlediğimde aşağıdaki hataları veriyor
    Struct”” has no field “P1IFG_2″
    Struct”” has no field “P1OUT_1”

    Bu hatalarla ilgili bir bilgin varmı nerde yanlıs yapıyor olabilirim?
    Teşekkürler.

    • Bu tarz bir sorunu sıkça duyuyorum. Sanırım IAR’ın farklı versiyonlarında benim kullandığım structlar yer almıyor. Bende sorun olmadan derleniyor kodlar. Çözüm için sizdeki versiyonda da benzer isimde structlar olduğunu düşünüyorum, bunları kullanabilirsiniz. Ya da bitsel and ve or işlemleriyle port ve interrupt vektörlerini direkt olarak kontrol edebilirsiniz.
      Kolay gelsin…

  10. Berk arkadaşım yaptığın projeyi denemek için indirdim. Ama bir sorunla karşılaşıyorum. Derleyici ile msp ye atarken şu satırlarda hata veriyor.

    if(P1IFG_bit.P1IFG_2) // Kesme P1.2’den geldiyse,
    P1IFG_bit.P1IFG_2 = 0;// P1.2 kesmebayrağını temizle
    if(P1IN_bit.P1IN_2 == 1) // Eğer bit “1” ise
    P1OUT_bit.P1OUT_1 = rs232Giden & BIT0;// Giden verinin 0. bitini P1.1’e bas
    bu hataları nasıl düzeltebilirim

    • Üstteki bir yorum için yazdığım cevabı tekrarlayacağım; Sanırım IAR’ın farklı versiyonlarında benim kullandığım structlar yer almıyor. Bende sorun olmadan derleniyor kodlar. Çözüm için sizdeki versiyonda da benzer isimde structlar olduğunu düşünüyorum, bunları kullanabilirsiniz. Ya da bitsel and ve or işlemleriyle port ve interrupt vektörlerini direkt olarak kontrol edebilirsiniz.
      Kolay gelsin…

Yorum Yap


Not - Bunları KullanabilirsinizHTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>