IAR ile STM8S – UART

UART/USART asla elimizin altından düşürmediğimiz bir protokol. Gerek özel haberleşmelerimiz gerek sensör okuma olsun bir çok yerde kullanılmasıyla birlikte uzun zamandır RS-232 olarak da çevremizde görüyoruz. Burada RS232 sadece daha farklı gerilim sevilerinde olmasının yanı sıra büyük port olması ve artık bilgisayarlarımızda bulunmaması nedeniyle sanal Com Port’lar bu eski dinazorların yerini çoktan aldı. FT232, CPxxx serisi gibi USB/Seri Çeviriciler hem az yer kaplaması hem USB den çalışması hemde düşük gerilim seviyeleri ile deneleyicilere uygun seviyede çıkış vermesinden ötürü bir hayli yaygınlaştı. Lafı daha fazla uzatmadan STM8S serisinde bu işleri nasıl yaptığımızı inceleyelim.

Yukarıda bahsettiğim üzere bende sanal Port oluşturan bir çevirici kullandım. Kullandığım çeviri FT232.

Öncelikle UART/USART arasındaki farka bakacak olursak burada sadece senktonizasyon olduğunu görürüz. Bunun için normalde bulunan RX-TX pinleri dışında ekstra pin kullanmamız gerekir ki biz şuan bunu istemiyoruz. Zaten bu işleri genelde yazılımsal yapmayı tercih ederiz.

Şimdi bir UART Paketini inceleyelim.


Resimde görüldüğü üzere değişken bölümlerin olmasından ziyade değiştirilemeyecek olan tek alan start bitidir.

UART haberleşmesinde önemli olan bir takım özellikler şunlardır.

  • Baud Rate
  • Data Bits
  • Parity Bits
  • Stop Bits

Buradan en çok dikkat edilmesi gereken nokta 2 tarafında haberleşme ayarlarını aynı olmasıdır. Aksi takdirde bu cihazlar farklı dilde konuşuyormuş gibi olur ve birbirlerini anlayamazlar

1) Baud Rate

Haberleşme hızını temsil eder. Saniyede gönderilecek olan bit sayısını temsil eder. Yüksek hızlarda hata oranı artacağı için dikkatli seçilmesi gerekir. Genelde 2400, 4800, 9600, 115200 gibi değerler kullanılır.

2) Data Bits

Tek pakette kaç bit gideceğini temsil eder. Biz genelde 8 bit yani 1 byte’ı tercih ederiz. Kullandığımız denetleyicide 8 ve 9 bit seçenekleri mevcuttur.

3) Parity Bits

Özel tanımlı bir hata kontrol bitidir. Bit toplamım 2 ye göre modu ile hesaplanılır. Kullandığımız denetleyicide NO, EVEN, ODD olmak üzere 3 seçenek vardır. Genelde NO olarak ayarlarız, yani Parity biti koymayacağız anlamına gelir. Daha detaylı bir açıklama için şurada güzel örnekler mevcut Parity Bit

4) Stop Bits

Paket sonu bitiş bitleridir. 0.5, 1, 1.5, 2 olmak üzere 3 seçeneğimiz vardır. Bu biti ModBus haberleşmesinde 2 olarak görebilirsiniz. Biz genelde 1 olarak kullanırız.

Burada RX pini alıcı TX pini ise göndericidir. Bu  yüzden çapraz bağlantı yapılır.


İşin fiziksel kısmını tamamladığımıza göre yazılımsal bölüme geçelim. Ayrıca UART/USART da kesme özelliklerinin olduğunu da unutmayalım. Veri alma, gönderme kesmelerinin yanı sıra bir çok kesmede mevcut. Bunlara birazdan değineceğiz.

UART1_Init((uint32_t)9600, UART1_WORDLENGTH_8D, UART1_STOPBITS_1, UART1_PARITY_NO, UART1_SYNCMODE_CLOCK_DISABLE, UART1_MODE_TXRX_ENABLE);

Görüldüğü üzere yine tek satırda konfigürasyon işlemini yapmak oldukça basit ve sade.

Burada 9600 Baud Rate’de 8 Data biti, 1 Stop biti ve No parity olarak ayarları yaptığımız gözükmekte. Bunun yanı sıra senkronizasyonu kapatıp TXRX modunu aktif ediyoruz. Bu hem TX hemde RX aktif olacağı anlamına geliyor. Eğer sadece alma yada gönderme yapacaksanız. TX yada sadec RX özelliğini aktif edebilirsiniz. Son olarak veri alma kesmesini aktif edip UART1’i çalıştırabiliriz.

UART1_ITConfig(UART1_IT_RXNE_OR, ENABLE);
UART1_Cmd(ENABLE);
  
enableInterrupts();

Veri gönderme ve alma işlerini ise 2 adet fonksiyon ile yapıyoruz.

data = UART1_ReceiveData8();
UART1_SendData8(data);

Tabi veri gönderme işlemini yaparken hattın durumunu kontrol etmekte fayda var.

while (UART1_GetFlagStatus(UART1_FLAG_TXE) == RESET);

Merak etmeyin bu while yerine ilerde TX kesmesini kullanacaksınız.

Son olarak kesme fonksiyonunu yazalım. Burada bir çok kesme olacağı için kesme içinde hangi kesme bayrağının geldiğini kontrol etmeyi unutmayın.

INTERRUPT_HANDLER(UART1_RX_IRQHandler, 18)
{
   if (UART1_GetITStatus(UART1_IT_RXNE) != RESET)
   {
      UART1_ClearITPendingBit(UART1_IT_RXNE);

      uartRxData = UART1_ReceiveData8();
      
      uartRxFlag = 1;
   }
}

Ben burada 2 tane değişken kullandım. Bu sayede hem veriyi alıyorum hemde ana döngüde veri alındığından haberdar oluyorum. Bu aslında sanal bir bayrak oluyor da denebilir.

Tabi daha sonra kullanacağımız veri gönderme, metin gönderme gibi işler için fonksiyon hazırlayalım. Hadi yine iyisiniz. Ben sizin için basit bir şeyler karaladım.

static void UART1_SendByte(uint8_t data)
{
   UART1_SendData8(data);

   while (UART1_GetFlagStatus(UART1_FLAG_TXE) == RESET);
}

static void UART1_Println(uint8_t* s)
{
   while(*s)
   {
      UART1_SendByte(*s++);
   }
   UART1_SendByte('\n');
   UART1_SendByte('\r');
}

Basit bir kontrol işlemini şu şekilde yapmak ve cevap almak mümkün.

void main(void)
{
   SYSTEM_Config();
   
   for (;;)
   {
      if (uartRxFlag == 1)
      {
         uartRxFlag = 0;
         
         if (uartRxData == '1')
         {
            GPIO_WriteHigh(LED_GPIO, LED_PIN);
            UART1_Println("LED Off.");
         }
         if (uartRxData == '0')
         {
            GPIO_WriteLow(LED_GPIO, LED_PIN);
            UART1_Println("LED On.");
         }
      }
   }
}

Artık herhangi bir terminal ile haberleşmeyi test edebilirsiniz.


Son olarak kodlarımızı hali şu şekilde.

main.c Dosyası

#include "main.h"

uint8_t uartRxData = 0;
uint8_t uartRxFlag = 0;

void main(void)
{
   SYSTEM_Config();
   
   for (;;)
   {
      if (uartRxFlag == 1)
      {
         uartRxFlag = 0;
         
         if (uartRxData == '1')
         {
            GPIO_WriteHigh(LED_GPIO, LED_PIN);
            UART1_Println("LED Off.");
         }
         if (uartRxData == '0')
         {
            GPIO_WriteLow(LED_GPIO, LED_PIN);
            UART1_Println("LED On.");
         }
      }
   }
}

static void SYSTEM_Config(void)
{
   CLK_Config();
   GPIO_Config();
   UART_Config();
}

static void CLK_Config(void)
{
   CLK_DeInit();
   CLK_HSECmd(DISABLE);
   CLK_HSICmd(ENABLE);
   CLK_ClockSwitchConfig(CLK_SWITCHMODE_AUTO, CLK_SOURCE_HSI, DISABLE, CLK_CURRENTCLOCKSTATE_DISABLE);
   CLK_SYSCLKConfig(CLK_PRESCALER_HSIDIV1);
   CLK_SYSCLKConfig(CLK_PRESCALER_CPUDIV1);
}

static void GPIO_Config(void)
{
   GPIO_Init(GPIOB, GPIO_PIN_5, GPIO_MODE_OUT_PP_LOW_FAST);
}

static void UART_Config(void)
{
   UART1_DeInit();
   UART1_Init((uint32_t)9600, UART1_WORDLENGTH_8D, UART1_STOPBITS_1, UART1_PARITY_NO, UART1_SYNCMODE_CLOCK_DISABLE, UART1_MODE_TXRX_ENABLE);
   UART1_ITConfig(UART1_IT_RXNE_OR, ENABLE);
   UART1_Cmd(ENABLE);
  
   enableInterrupts();   
}

static void UART1_SendByte(uint8_t data)
{
   UART1_SendData8(data);

   while (UART1_GetFlagStatus(UART1_FLAG_TXE) == RESET);
}

static void UART1_Println(uint8_t* s)
{
   while(*s)
   {
      UART1_SendByte(*s++);
   }
   UART1_SendByte('\n');
   UART1_SendByte('\r');
}

#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t* file, uint32_t line)
{ 
   while (1);
}
#endif


main.h Dosyası

#ifndef MAIN_H
#define MAIN_H

#include "stm8s.h"

#define LED_GPIO        (GPIOB)
#define LED_PIN         ((GPIO_Pin_TypeDef)GPIO_PIN_5)

extern uint8_t uartRxData;
extern uint8_t uartRxFlag;

static void SYSTEM_Config(void);
static void CLK_Config(void);
static void GPIO_Config(void);
static void UART_Config(void);
static void UART1_SendByte(uint8_t data);
static void UART1_Println(uint8_t* data);

#endif

Interrupt dosyası çok büyük olduğu için ilgili kısmı veriyorum gerekli eklemeyi yapabilirsiniz.

INTERRUPT_HANDLER(UART1_RX_IRQHandler, 18)
{
   if (UART1_GetITStatus(UART1_IT_RXNE) != RESET)
   {
      UART1_ClearITPendingBit(UART1_IT_RXNE);

      uartRxData = UART1_ReceiveData8();
      
      uartRxFlag = 1;
   }
}

 

You may also like...

3 Responses

  1. muharrem dedi ki:

    Üstadım eline sağlık güzel bilgiler vermişsin.
    Bu kısmını açıklayabilir misin? Ne amaçla kullanılıyor.
    ///********************/
    #ifdef USE_FULL_ASSERT
    void assert_failed(uint8_t* file, uint32_t line)
    {
    while (1);
    }
    #endif
    ///********************/

    Interrupt kısmını main.c dosyasında mı yoksa main.h dosyasında mı kullanıyoruz ?
    sorularım için kusura bakma.

    • Baran EKREM dedi ki:

      Merhaba,
      Interrupt fonksiyonunu ilgili dosyada yani interrupt dosyasında kullanıyorum.
      İsteğe bağlı olarak maine alabilirsin ama önerilen bir yöntem değil.

      ASSERT kısmı aslında bir hata kontrolü.
      Parametreleri kontrol etmeye yarıyor.
      Bazı durumlarda kullanılmasa da olur fakat genelde kütüphaneler bunu istiyor.

  2. Şafak dedi ki:

    merhaba stm8.h kütüphanesiyle ilgili bir hata veriyor. bvaşka bir yerden aldığım stm8.h dosyası kopyaladığımda değişkenler hata veriyor. UART1_PARITY gibi. sanırım farklı bir stm8.h dosyası var. bu dosyayı nereden alabilirim.
    Kolay gelsin.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir