18 Mayıs 2020 Pazartesi

16 - HAL Kütüphanesi ve 16x2 LCD Kullanımı

Merhaba,

Daha önceki yazılarımızda Stm için Standart Peripheral Library kullanmıştık. Stm'in hal kütüphanesini desteklemesi ve bundan sonra da bu kütüphaneyi kullanacak olması ister istemez bizleri de hal kütüphanesini kullanmaya mecbur kılmıştır. Bu yazıda basit bir şekilde STM32CubeMx derleyici kullanarak hal kütüphanesi ile nasıl proje oluşturulur ve 16x2 LCD nasıl kullanılır hakkında bahsedeceğiz.

Öncelikle STM'in kendi sitesinden STM32CubeMX programını indirin. Programın kurulu olduğunu var sayarsak, başlangıçta yukarıda bulunan:

File -> New Project yaparak yeni bir dosya oluşturmamız gerekiyor.

Akabinde bizi aşağıdaki gibi bir pencere karşılayacak:



Kırmızı çizgi içinde kalan yerden direkt olarak kullanacağımız Stm işlemcisini seçebiliriz, ya da onun altında bulunan kısımdan da hangi işlemciyi kullanacağımızı belirleyebiliriz. Biz STM32F407VG'yi seçiyoruz, çünkü discovery boardımızda bu işlemci bulunmaktadır. Pencerinin sağ alt tarafında bulunan MCUs/MPUs List kısmında STM32F407VG yazan satıra çift tıklıyoruz ve karşımıza aşağıdaki gibi bir ekran gelecek:


Burada öncelikle kristal ayarımızı yapalım. Bunun için sol tarafta bulunan System Core kısmını seçeceğiz. Hemen altında bir pencere açılacak oradan da RCC sekmesine tıklayalım, orda da hemen yana bir pencere açılacak ve karşımıza aşağıdaki gibi bir ekran görüntüsü oluşacaktır.


Sağ taraftaki kırmızı ile çizilen yerde Crystal/Ceramic Resonator seçeneğine tıklayacağız. Böyleye kristal seçimini tamamlamış olduk. 

Daha sonra yine sol yanda bulunan System Core kısmından SYS sekmesine tıklayalım. Burdan da yine hemen yana bir pencere açılacak ve karşımıza aşağıdaki gibi bir ekran görüntüsü oluşacaktır:


Sağ taraftaki kırmızı ile çizilen yerde Debug kısmında Serial Wire seçeneğine tıklayacağız. Bu program atarken kullanacağımız yöntemle alakalı. Biz Program atarken SWD pinlerini kullandığımız için bu seçeneği seçmemiz gerekiyor. Belki STM32F4 discovery board da sıkıntı çıkarmaz ama daha farklı işlemciler kullandığımızda bu seçeneği yapmazsak program atarken işlemcinin NRST pinini kendimiz manuel olarak gnd'ye çekip bırakmamız gerekecek ve sıkıntı oluşacaktır. Zaten bu kısmı yaptığımızda sağ tarafta aşağıdaki gibi bir görüntü oluşacaktır.


Kırmızı ile işaretlenen yerlerden 1 yazan yer kristal seçtimiz için oluştu. 2 ile işaretlenen yer ise Serial Wire seçtiğimiz için oluştu. Zaten SWCLK ve SWDIO pinleri de program atma pinleridir. 

Bu kısımlar her projede yapmamız gereken ayarlardır. Şimdi sırada LCD için ayar yapmaya geldi. Öncelikle LCD için kurulum yapmamız gerekiyor. Kendi yazmış olduğum kütüphane de var ancak aşağıdaki vereceğim linkteki LCD kütüphanesi çok iyi olduğu için onu kullanacağız. İnternetten bulduğum kütüphaneyi nereden indirdiğimi hatırlamadığım için orjinal linki veremiyorum. Ama dediğim gibi kütüphane bana ait değildir, indirdiğim sayfayı hatırlasam kaynak olarak gösterecektim, o yüzden bu yazıyı okuyan birileri o sayfayı bulursa ve buraya yorum olarak yazarsa daha sonra kaynak olarak o sayfayı göstereceğim. 

Kütüphane Linki: https://yadi.sk/d/j7lnlMGXzUgCXA

Yukarıdaki linkten kütüphane dosyalarını indirin. Şimdilik kenarda dursun daha sonra bu kütüphaneyi proje dosyamıza ekleyeceğiz.

Şimdi LCD için kullanacağımız pinleri seçelim. Seçeceğimiz pinlerin aynı portta olmasına dikkat edelim. Çünkü kütüphane pinleri aynı portta olduğu kabul edilmiştir. Yani A portu ise bütün pinleri A portunda seçelim ya da B ya da, ya da C.. Kütüphanede LCD kurulumu için hem 4 pin hem de 8 pin kullanarak yazılmasına izin verilmiştir. Ben burada data pinlerini 4 pin olarak kuracağım. İsteyen data pinlerini 8 pin olarak da kurabilir. İki şekilde de sıkıntı olmayacaktır. O halde pinleri seçmeye başlayalım. Pencerenin sağ tarafında bulunan işlemci görselinden PA4 pinine tıkladığımızda yine hemen üstünde bir pencere açılacaktır. Bu ufak pencere bu pinin işlevinin ne olarak kullanmak istediğimizi belirttiğimiz kısım. Mesela input, output, ADC girişi veya harici kesme vb birçok özellik bulunmaktadır. Biz output olarak kullanacağımız için GPIO_Output seçeneğine tıklayacağız. Görsel aşağıdaki gibi olacaktır:


Ayarı yaptığımızda pin rengi yeşile dönecektir. Sırasıyla PA4, PA5, PA6 ve PA7 pinlerini de çıkış yapalım. Son görsel aşağıdaki gibi olacaktır:


Şimdi de LCD nin RS ve Enable pinlerini ayarlayalım. Sırasıyla PA1 ve PA2 pinlerini de çıkış yapalım. Son görsel aşağıdaki gibi olacaktır:




Ayarları yaptık şimdi ise Clock Configuration kısmını yapalım. Programın üst kısmında bulunan Clock Configuration sekmesine tıklayalım, karşımıza aşağıdaki gibi bir pencere çıkacak:



Burada da işlemcimizi çalıştırmak istediğimiz hızı ayarlayacağız. İşlemci 168Mhz hız desteklediği için 168MHz yapacağız. Siz isterseniz daha düşük hızlarda da ayarlayabilirsiniz. Aşağıdaki resimdeki gibi sol tarafta kırmızı işaretli yere 8, sağdaki kırmızı işaretli yere 168 yazın ve entere basın. 



Bu kısmı ayarladıktan sonra yine üst tarafta bulunan Project Manager sekmesine tıklayın. Karşınıza aşağıdaki gibi bir pencere çıkacaktır:


Karşınıza çıkan pencerede kırmızı ile işaretli 1 yazan yere proje ismini yazın. 2 yazan yere projenizi kaydedeceğiniz klasörü seçin ve 3 yazan yerde de Keil kullanıyor iseniz MDK-ARM seçeneğini seçin. Diğer hiçbir yeri değiştirmenize gerek yoktur. Daha sonra sağ üst kısımda bulunan GENERATE CODE yerine tıklayın. Eğer daha önceden CubeMX i kurmuşsanız dosyalar zaten yüklü olduğu için direkt olarak size aşağıdaki gibi bir pencere gelecektir.


Buradan Open Project derseniz keil programı açılır ve konfigürasyon dosyaları otomatik olarak oluşturulmuş halde gelir. Eğer CubeMX için gerekli olan dosyalar daha önce yüklü değilse size bir uyarı verir ve yukarıdaki resim gelmeden önce o programı yüklemeyi gösteren pencere açılır oradan indir derseniz dosyaları otomatik olarak kurar. Bilgisayarınız internete bağlı olması gerekmektedir dosyaları yüklemek için. Yüklü ise internete ihtiyacınız yoktur. 

Proje açıldığında aşağıdaki gibi gelmektedir. Sol tarafta kırmızı ile işaretli olan yerden LCD_Ornek yazan yerin solundaki + işaretine tıklayın daha sonra Application/User yanındaki + işaretine tıklayın ve en son main üzerine çift tıkladığınız zaman main dosyası açılmış olacaktır.



Şimdi sıra daha önce verdiğim linkten indirmiş olduğunuz kütüphaneyi projemize dahil etmeye geldi. Bunun için indirdiğimiz kütüphane klasöründeki STM_MY_LCD16X2.h dosyasını kopyalayın ve projeyi oluşturduğumuz klasörün içindeki -- Inc -- klasörünün içine yapıştırın. Daha sonra yine indirdiğimiz kütüphane klasörü içindeki STM_MY_LCD16X2.c dosyasını kopyalayın ve projeyi oluşturduğumuz klasörün içindeki -- Src -- klasörünün içine yapıştırın.

Daha sonra keil programına gelin ve sol tarafta bulunan Application/User yazan yere çift tıklayın ve açılan pencerede proje klasörünün içine kopyaladığımız Src klasöründeki STM_MY_LCD16X2.c dosyasını seçin ve tamam deyin. Aşağıdaki gibi görsel gelecektir :

Daha sonra main.c dosyasındaki 

/* USER CODE BEGIN Includes */
#include "STM_MY_LCD16X2.h" // bu yazılacak araya
/* USER CODE END Includes */

görsel aşağıdaki gibidir:


Artık kodumuz hazır. LCD kurulumu yapıp lcd ye yazı yazabiliriz. LCD kurulumu için main fonksiyınunun içinde aşağıdaki yere lcd konfigürasyon fonksiyonunu yazacağız.

/* USER CODE BEGIN 2 */
LCD1602_Begin4BIT(GPIOA,GPIO_PIN_1,GPIO_PIN_2,GPIOA,GPIO_PIN_4,GPIO_PIN_5,GPIO_PIN_6,GPIO_PIN_7); araya bu fonksiyon yazılacak
  /* USER CODE END 2 */

Burada LCD1602_Begin4BIT fonksiyonunu kullandık çünkü başta da dediğimiz gibi data pinlerini 4 pin olarak kullanacağız. 

Artık LCD ye yazı yazdırabiliriz. 

Örnek olarak:

LCD nin birinci satırında NE MUTLU yazacak,
ikinci satırında ise TURKUM DIYENE yazacak.

LCD1602_1stLine();
LCD1602_print("NE MUTLU");
LCD1602_2ndLine();
LCD1602_print("TURKUM DIYENE");


Programı derleyin ve kartınıza yükleyin. 

Kütüphanede işinize yarayacak bir çok fonksiyon bulunmaktadır. İncelemeleriniz yaparsınız.

Takıldığınız yerde aşağıya yorum yaparak soru sorabilirsiniz.

İYİ Çalışmalar.






  

19 Nisan 2019 Cuma

15 - İki STM32F4 ile Haberleşme

Merhaba,

Elektronik yazılım ile ilgilenen bir çok kişi en azından bir kaç kere iki işlemciyi veya işlemci ile bilgisayarı haberleştirmek ihtiyacı hissetmiştir. Bu yazımızda iki adet Stm32f4 kartını birbiri ile USART üzerinden haberleştireceğiz. 

Normalde bu örneği yapmayacaktım ancak bir arkadaşım rica ettiği için paylaşıyorum. Örnek kolay bir örnek olabilir, ama bir çok kişinin işine yarayacaktır. 

Öncelikle haberleşme için bir çok yöntem kullanabilirdik, örneğin; Bluetooth, SPI, I2C, USART, RF, Ethernet vb örnekleri çoğaltabiliriz. Ancak bizim kitimizde USART dahili olarak bulunduğu için bu haberleşmeyi tercih ettik. 

O halde kod yazmaya geçebiliriz.

Bu yazıda her iki kart da birbirine veri gönderebilir.

#include "stm32f4xx.h"                             //stm32f4 kütüphanesi
#include "stm32f4xx_gpio.h"                    //GPIO kütüphanesi
#include "stm32f4xx_rcc.h"                       // RCC clock kütüphanesi
#include "stm32f4xx_usart.h"                  // usart kütüphanesi

/********************************************************************************/
void GPIO_Init(void);
void USART_Init(void);
/********************************************************************************/


/********************************************************************************/
/* Gerekli değişken tanımlamaları */

unsigned char Device_ID = 1;
unsigned char Rx_Data[10];
unsigned char Tx_Data[10];
unsigned char gelen_data_sayaci= 0;
unsigned char Gelen_veri=0;
/********************************************************************************/


/* Led için konfigürasyon ayarlamaları */
void GPIO_Init(void)
{
   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
   
    GPIO_InitTypeDef  GPIO_InitStructure; // Port yönlendirmesi

/* Kart üzerindeki butonu giriş olarak tanımladık */
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;  //Pinler cikis olarak belirlendi
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

   /* PD12, 13, 14 ve PD15 pinleri kullan1lacak */
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;  //Pinler cikis olarak belirlendi
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_Init(GPIOD, &GPIO_InitStructure);

}


/* USART1 ayarlamaları */
/* usart kesmesi de ayarlandı. gelen data direkt olarak kesme fonksiyonu ile alınacaktır */
void USART_Init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);

     GPIO_InitTypeDef GPIO_InitStructure;
     USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef  NVIC_InitStructure;

     GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;
     GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
     GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
     GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_6 | GPIO_Pin_7;
     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
     GPIO_Init(GPIOB, &GPIO_InitStructure);

// GPIO ALTERNATE FUNCTION olarak tanimlanan pinin konfigürasyonlari
// pin ile baglantı kuracagi modül belirlenir.
     GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_USART1);
     GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_USART1);


     // USART1 Reset
     USART_DeInit(USART1);
     // USART konfigürasyonlari:
     USART_InitStructure.USART_BaudRate = 9600;
     USART_InitStructure.USART_HardwareFlowControl= USART_HardwareFlowControl_None;
     USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
     USART_InitStructure.USART_Parity = USART_Parity_No;
     USART_InitStructure.USART_StopBits = USART_StopBits_1;
     USART_InitStructure.USART_WordLength = USART_WordLength_8b;
     USART_Init(USART1, &USART_InitStructure);

     USART_ITConfig(USART1, USART_IT_RXNE,ENABLE);

     // NVIC Init
     NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
     NVIC_Init(&NVIC_InitStructure);

     USART_Cmd(USART1, ENABLE);
}


int main(void)
{
     GPIO_Init();
     USART_Init();

/* Bütün ayarlamaları yaptık. Butona bastığımızda diğer karta veri gönderecek. Hatta bunu bir dizi paketi halinde gönderecek. Bu kartımızın kimlik bilgisi 1 olsun. diğer kartın 2. 
*/
 while(1)
 {

if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)) { while(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)); //Butondan çekilene kadar bekle
Tx_Data[0] = 1;   //kimlik bilgimiz 1
Tx_Data[1] = 41;// Gidecek veri, Bunu A harfi olarak da yazabilirsiniz ancak tek karakter olması gerek
// Tx_Data[1] = 'A';   // String bir karakter için böyle de gönderebilirsiniz
USART_SendData(USART1, Tx_Data[0]);
USART_SendData(USART1, Tx_Data[1]);
// İki Datamızı da gönderdik 
}

if(Gelen_veri == 41) {
 GPIO_SetBits(GPIOD,GPIO_Pin_12);
{
else
{
 GPIO_ResetBits(GPIOD,GPIO_Pin_12);
}

     }

}


/********************************************************************************/
/* USART1 KESME FONKSİYONU */

void USART1_IRQHandler(void)
{
     if( USART_GetITStatus(USART1, USART_IT_RXNE) )
     {
          USART_ClearITPendingBit(USART1, USART_IT_RXNE);//kesme bayrağı temizlenir

/* eğer USART biriminden herhangi bir veri gelirse bu fonksiyona girecektir*/

          // Gelen veriyi okuma işlemi
          Rx_Data[gelen_data_sayaci] = USART_ReceiveData(USART1);

          // Gelen paketteki data sayacini arttir
          gelen_data_sayaci++;

/* Bize gelen verinin 2 adet olduğunu varsayıyoruz, bu yüzden aşağıdaki gibi bir işlem yapıyoruz */

          if(gelen_data_sayaci == 2)
          {
                    if(Rx_Data[0]== 1) // Yani bizim kimilik bilgimiz ile eşleşiyor ise
                    {
                              Gelen_veri = Rx_Data[1];
                              gelen_data_sayaci = 0; //Yeni gelecek veri paketi için sayacı sıfırla
                    }
                    
          }

     }
     else if( USART_GetITStatus(USART1, USART_IT_TXE) )
     {
          USART_ClearITPendingBit(USART1, USART_IT_TXE);
     }


}


Bu kodu her iki karta da yüklediğinizde, birinin butonuna basarsanız diğerinin PD12 ledi yanacaktır. aynı şekilde diğerinin butonuna bastığınızda ilk kartın ledi yanacaktır. Bu örnek üzerinde ufak oynamalar yaparak değişik işlemlerde kullanabilirsiniz.

NOT: Birinci kartın TX pinini diğer kartın Rx pinine, Birinci kartın RX pinini diğer kartın Tx pinine bağlamalısınız. Ayrıca iki kartın GND pinleri de birbirine bağlanmalıdır. Yani  birinci kartın PB6 pinini diğer kartın PB7 pinine, yine birinci kartın PB7 pinini diğer kartın PB6 pinine bağlamalısınız.

İyi Çalışmalar




8 Nisan 2019 Pazartesi

14 - STM32F4 ile Enkoder Okuma

Merhaba,

Enkoder çoğu dc motor uygulamalarında konum belirleme, hız ölçme vb sebeplerden ötürü kullanılmaktadır. Ayrıca radyo ses artırma/azaltma gibi çeşitleri de bulunmaktadır. Ya da encoderli bir çevirmeli buton ile çeşitli projeler yapabilirsiniz. Örneğin aşağıda "rotary encoder" ve üzerinde "Ok" tuşu bulunan bir ürünle projelerinizi farklı bir boyuta taşıyabilirsiniz.


Bildiğiniz üzere "rotary encoder" 'lerde A, B ve bazen de Z çıkışı bulunmaktadır. Biz bu uygulamada A ve B uçlarını kullanarak enkoder okuyacağız. Eğer bu yazıyı okuyorsanız enkoderin genel yapısını bildiğinizi düşünüyorum. Bu yüzden enkoderin A ve B çıkış sinyallerine kısaca değinip koda geçeceğiz. 


Yukarıda bir enkodere ait A ve B çıkış sinyalleri bulunmaktadır. Bu uçlar bir kare dalga üretirler. Başlangıçta A çıkışı lojik 1 olduğunda B ucu lojik 0'dadır. A ucunun lojik 1 olduğu bölgenin yarısında B ucu lojik 1 olur. A ucu lojik 0'a geçtiğinde B ucu halen lojik 1'dedir ta ki A ucunun lojik 0 olduğu bölgenin yarısına kadar, ve bu böylece devam eder. 

Eğer bir enkoder bir turda 250 darbe üretiyor diye yazıyorsa teknik dokümanında, bu sadece bir tetik için geçerlidir. Yani sadece bir kanalın ürettiği kare dalganın yükselen kenarında ölçüm yapıyor isek bir turda 250 darbe okuruz. Hem A hem de B kanalından okuma yapıyor isek bu değer iki kanal olduğu için 500'e çıkar. Hem yükselen hem de düşen kenar tetiklemesinde ölçüm yapıyor isek bir kanalda 250*2=500 darbe okuruz, iki kanal okuyorsak 250*4=1000 darbe okuruz. 

Artık koda geçebiliriz. "Stm32f4 discovery board" stm32f407vgt6 işemcisine sahiptir ve bu işlemcinin kendi enkoder modulu bulunmaktadır. Yani adamlar demişler ki eğer enkoder okuyacaksanız biz sizin için bu işi basit hale getirdik, rahatça işleminizi yapabilirsiniz, ister yükselen kenarda, ister düşen kenarda, isterseniz hem düşen hem yükselen kenardan okuyabilirsiniz diyor. Ancak bunu yaparken bir "timer" birimini enkoder için kullancaksın diyor, çünkü bu "timer" birimi sayesinde rahatça okuyorsun diyor. Şimdiye kadar yazdıklarımız tamamsa, başlayalım.

Enkoder için bir fonskiyon yazalım, bu fonksiyonda enkoderin ayarlarını yapalım.
Enkoder için "timer4" birimini kulanacağız. Siz de herhangi bi CH1 ve Ch2 çıkışı olan bir "timer" birimi kullanabilirsiniz. burada "timer4" kullanacağız. CH1 ve Ch2 için de teknik dokümanından baktığımızda GPIO_Pin_6 ve GPIO_Pin_7 kullanabiliyoruz. 

void Encoder_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
   RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; 
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; 
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_Init (GPIOB, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_TIM4);
  GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_TIM4);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
TIM_EncoderInterfaceConfig(TIM4,TIM_EncoderMode_TI1,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising);  

TIM_SetAutoreload (TIM4, 0xffff);
TIM_Cmd (TIM4, ENABLE);
}

// burada enkodere herhangi bir değer verilebilir. örneğin 50, yada 100 artık ne verirseniz
//bu fonksiyondan sonra encoderiniz bu değerden saymaya başlar
void Encoder_Set(int deger)
{
TIM_SetCounter (TIM4, deger);
}

// buradan da enkoderin hangi değerde olduğu okunabilir

int  Read_Encoder_Value(void)
{
return TIM_GetCounter (TIM4);
}


yazdığınız programda ne zaman nerde enkoderden değer okumak istiyorsanız aşağıdaki gibi yapabilirsiniz.

int encoder_value=0;

encoder_value = Read_Encoder_Value();

bu kod parçasını istediğiniz gibi kendi projenize dahil edebilirsiniz.

enkoder ileri giderken artı yönde geri giderken eksi yönde saymaktadır.

İyi Çalışmalar

27 Mart 2019 Çarşamba

13 - STM32F4 Flash Yazma-Okuma

Merhaba arkadaşlar,

Stm32f4 discovery'de flasha veri yazma okuma işlemi yapacağız. Burada uzunca özelliklerden bahsedebilirdik ancak zaman kısıtlığından dolayı direkt kodlar üzerinden gideceğiz.


Yukarıdaki resimde Stm32f40X ve Stm32f41X serilerinin flash tablosu görülmektedir. Siz de Stm32f4 Reference Manual olarak google amcadan aratarak kolayca ulaşabilirsiniz. Sayfası 75. Burada görüldüğü üzere veriler sektör sektör kaydediliyor. Siz de bu tabloya göre istediğiniz adrese verileriniz yazıp okuyabilirsiniz. Ancak dikkat edilmesi gereken bir husus daha var, verileri flasha yazmadan önce yazacağınız adresin kullanılıp kullanılmadığına bakın. Bunu basitçe St-Link Utility programı ile yapabilirsiniz. Eğer adreste herhangi bir veri kayıtlı değilse adresin değeri 0xFFFF olarak görünecektir. 

Artık kod yazmaya başlayabiliriz. 
Burada direkt olarak flasha yazma ve okuma fonksiyonları verilecektir. Siz ihtiyacınıza göre farklı şekillerde kullanabilirsiniz. 

Flash'a yazma fonksiyonu:

void Data_Write_Flash()
{
uint32_t  Address = 0x08040000;
uint16_t data = 38;

FLASH_Unlock();  // öncelikle Flash kilidi kaldırılır
// Hata Bayraklari Temizlenir
FLASH_ClearFlag(FLASH_FLAG_EOP|FLASH_FLAG_OPERR|FLASH_FLAG_WRPERR|
FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR|FLASH_FLAG_PGSERR);
// Flash Sektörü Sil
/* Biz burada 6. söktörü kullanacağız. 6.sektör için flash adresimiz 0x0804 0000 - 0x0805 FFFF aralığında olabilir. Ancak yazmadan önce tüm sektörü silmek zorundayız. o yüzden eğer aynı sektöre birden çok ve farklı zamanlarda veri yazılacaksa silme işleminden önce sektördeki değerler bir değişkene aktarılır ve daha sonra yazılacak olan yeni veriyle birlikte hepsi yazılır. */
FLASH_EraseSector(FLASH_Sector_6, VoltageRange_3);
/* yukarıda yine VoltageRange_3 diye yazdık, bu da kullandığımız işlemciyi besleme voltajımızdır. Flash kütüphanesinin içinde bunu aşağıdaki gibi göstermiştir.*/
#define VoltageRange_1        ((uint8_t)0x00)  /*!< Device operating range: 1.8V to 2.1V */
#define VoltageRange_2        ((uint8_t)0x01)  /*!<Device operating range: 2.1V to 2.7V */
#define VoltageRange_3        ((uint8_t)0x02)  /*!<Device operating range: 2.7V to 3.6V */
#define VoltageRange_4        ((uint8_t)0x03)  /*!<Device operating range: 2.7V to 3.6V + External Vpp */
/* Biz işlemcimizi 3.3V ile beslediğimiz için VoltageRange_3 kullandık, bu da besleme voltajımız 2.7V to 3.6V arasında olduğu içindir. */

/*şimdi de kaydedilecek verimizi adrese yazabiliriz. 
FLASH_ProgramHalfWord(Address, data);
/*yukarıda sektör6 daki 0x08040000 adresine, 38 değerini yazdık. Artık bu adreste 38 verisi saklanmaktadır. Daha sonra bu adresi okuduğumuzda 38 verisini bize verecektir. */

FLASH_Lock();
/* Son olarak Flash kilitleme işlemi yapılır ve fonksiyondan çıkılır. */

}


Flash'dan okuma fonksiyonu:

void Data_Read_Flash()
{
uint32_t  Address = 0x08040000;
uint16_t data;

/* okuma işlemi çok basittir sadece daha önceden veri yazılan bir adresten veri okuma olayıdır. Yani direkt olarak adresten veriyi çekeceğiz */

data = *(uint32_t *)Address; 
/* Yani 0x08040000 adresindeki veriyi okuduk. Yukarıdaki fonksiyonda 38 olarak kayıt yapmıştık oku dediğimizde 38 değerini verecektir */

}

İyi Çalışmalar


18 Mart 2019 Pazartesi

12 - RS485 ŞEMATİK GÖSTERİMİ

Merhaba arkadaşlar,

Daha önceki yazılarımızda modbus haberleşmenin teorik anlatımı ve uygulaması paylaşılmıştı. Bu yazımızda da donanım altyapısı anlatılacaktır. 


Yukarıdaki resimde RS485 bağlantısının şematik gösterimi paylaşılmıştır. Entegre olarak DS485 kullanılmıştır. Ayrıca SP485 entegresi de aynı işlevi yapmaktadır ve hatta paket olarak birebir aynıdır. 

İster STM32F4 olsun isterse de herhangi bir işlemci olsun USART özelliği bulunan her işlemci için bu yapı kullanılabilir. İşlemcimizin Tx pini RS485 entegremizin DI pinine, Rx pini de RO pinine bağlıdır. Burada dikkat edilmesi gereken nolta DE ve RE pinleridir. Modbus haberleşmemizde DE ve RE pini birleştirilerek kullanılmıştır. Bu DERE pinini de işlemcimizin herhangi bir output pinine bağlayabiliriz. En önemli husus ise bu DERE pini veri alacağımız zaman logic 0'da, veri göndereceğimiz zaman da logic 1'de olması gerekmektedir. 

Konuyu daha açacak olursak, eğer Modbus haberleşmemizde Slave isek, başlangıçta DERE pinini logic 0'a çekeriz, yani bunu yaparak bir sorgu beklediğimizi belirtiriz. Sorguyu aldıktan sonra, cevabı göndermeden önce logic 1'e çekeriz ve cevabımızı göndeririz. Cevabı gönderdikten sonra ise tekrar logic 0'a çekerek yeni sorguyu bekleriz ve bu döngü böyle devam eder. 

Eğer Modbus haberleşmesinde biz Master isek, başlangıçta sorgu çekmemiz gerekiyor. O halde sorguyu çekmeden önce DERE pinini logic 1'e çekeriz, sorgumuzu göndeririz ve ardından DERE pinini logic 0'a çekeriz ve sorgumuzun cevabını bekleriz. Bu da böyle bir döngü içinde devam eder.

Bir diğer husus ise haberleşmede veri kayıplarını önlemek amacı ile, entegrenin A ve B pinlerinin olduğu tarafta, B pinin bir direnç ile GND'ye bağlarız. Buradaki direnç 1K ile 10K arasında olabilir. Bu aralığın dışında farklı bir direnç koymamanızı tavsiye ederim. Yine aynı şekilde A pinini de bir direnç yardımı ile Vcc besleme ucuna bağlarız. Bu direnç aralığı da aynı şekildedir. Ve son olarak, haberleşmenin sonundaki Slave'e A ve B pinleri arasına 120R'lik bir direnç koyulur. Bu da sonlandırma direnci diye geçer. Bu dirençleri koymadan da muhtemelen haberleşmemiz çalışacaktır. Ancak bazı haberleşmelerde bu dirençler olmadığı için sıkıntı oluşmaktadır. Örneğin ben kendim bizzat bu problemleri yaşadım. O yüzden eşeğinizi sağlam kazığa bağlayın ki sonradan uğraşmayın.

Daha önceki yazılarımızda yine RS485 haberleşmesi için diferansiyel bir haberleşmedir demiştik. Bu yüzden A ve B pinlerine bağlı olan telimiz ya da kablolarımız birbirine sarılarak uzatılır ya da birbirine çok yakın halde uzatılır. Yani eğer bir parazit varsa her iki pine de etki eder ve böylece haberleşme diferansiyel olduğu için haberleşmemiz etkilenmemiş olur. 

İyi Çalışmalar...

12 Mart 2019 Salı

11 - MODBUS RTU Master Örneği

Merhaba,

Bir önceki yazıda modbus rtu slave örneğini paylaşmıştık. Aslında bu örnek de ondan çok bir farkı bulunmamakta. Donanımsal altyapı olarak tamamen aynıdır. RS485 entegresinin çıkışındaki A ve B pini hat üzerindeki her slave cihazın A ve B pinlerine bağlıdır. Yani haberleşme iki tel üzerinden bütün cihazlara bağlıdır. RS232 gibi TX Rx'e, RX TX'e bağlı değildir. Yani Master'daki A pini Slave'deki A pinine, B pini de B pinine bağlıdır ve tek bir tel bütün cihazlara bağlıdır. 

Yukarıdaki resim RS485 altyapısı kullanıldığında geçerlidir. Eğer RS232 kullanılıyorsa Tx Rx'e, Rx Tx'e bağlanmalıdır.

Önceki yazımızda Slave örneği için bize sorgu gelmesini bekliyorduk. Şimdi ise Master olduğumuzdan mütevellit sorguyu bizim çekmemiz gerekiyor. 

O halde kodu yazmaya başlayabiliriz.

KOD KISMI:

Başlnagıç olarak kütüphaneler eklenir.

#include "stm32f4xx.h"
#include "stm32f4xx_gpio.h" // GPIO kütüphanesi
#include "stm32f4xx_rcc.h" // RCC clock kütüphanesi
#include "stm32f4xx_usart.h" // Keil::Device:StdPeriph Drivers:USART
#include "stm32f4xx_it.h"
#include "stm32f4xx_tim.h"

/********************************************************************************/
// Altta kullanılacak olan fonksiyonlar burada tanıtılır //

void GPIO_Init(void);
void USART_Init(void);
void TIM6_Init(void);
void USART1_IRQHandler(void);
void TIM6_DAC_IRQHandler(void);
void ReadHoldingRegister();
char CRC_check(char package[],unsigned int package_length);
void Get_CRC(unsigned char package[],unsigned int package_length);
/********************************************************************************/

/********************************************************************************/
/* Gerekli değişken tanımlamaları */

unsigned short Readed_Array[100];
unsigned char Modbus_Receive_Array[100];
unsigned char Modbus_Trans_Array[100];
unsigned short modbus_receive_counter = 0;
unsigned int modbus_data_counter = 0;
unsigned char modbus_slave_addr = 1; //Slave adresimiz 1 olsun
unsigned short Holding_Register_Adress_Value = 255;
unsigned char frame_length;
unsigned short Tx_count= 0;
unsigned short i=0;
unsigned short data_count = 0;
/********************************************************************************/

/* Led için konfigürasyon ayarlamaları */
// ledler aslında kullanılmamıştır ancak haberleşmeyi kontrol amaçlı kullanılabilir //

void GPIO_Init(void)
{
   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

    GPIO_InitTypeDef  GPIO_InitStructure; // Port yönlendirmesi

   /* PD12, 13, 14 ve PD15 pinleri kullan1lacak */
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;  //Pinler cikis olarak belirlendi
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_Init(GPIOD, &GPIO_InitStructure);

    /* PA0 pini buton için kullanılacak */
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;  //Pin giriş olarak belirlendi
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

}


/* USART1 ayarlamaları */
/* usart kesmesi de ayarlandı. sorgu yapıldıktan sonra gelen data direkt olarak kesme fonksiyonu ile alınacaktır */
void USART_Init(void)
{
     RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);

     GPIO_InitTypeDef GPIO_InitStructure;
     USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef  NVIC_InitStructure;

     GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;
     GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
     GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
     GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_6 | GPIO_Pin_7;
     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
     GPIO_Init(GPIOB, &GPIO_InitStructure);

// GPIO ALTERNATE FUNCTION olarak tanimlanan pinin konfigürasyonlari
// pin ile baglant1 kuracagi modül belirlenir.
     GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_USART1);
     GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_USART1);


     // USART1 Reset
     USART_DeInit(USART1);
     // USART konfigürasyonlari:
     USART_InitStructure.USART_BaudRate = 9600;
     USART_InitStructure.USART_HardwareFlowControl= USART_HardwareFlowControl_None;
     USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
     USART_InitStructure.USART_Parity = USART_Parity_No;
     USART_InitStructure.USART_StopBits = USART_StopBits_1;
     USART_InitStructure.USART_WordLength = USART_WordLength_8b;
     USART_Init(USART1, &USART_InitStructure);

     USART_ITConfig(USART1, USART_IT_RXNE,ENABLE);

     // NVIC Init
     NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
     NVIC_Init(&NVIC_InitStructure);

     USART_Cmd(USART1, ENABLE);
}


void TIM6_Init(void)
{
/*
1sn timeout zamanı için:

TIM6 APB1’e bağlı gözüküyor. APB1’in çalışma hızı 42 MHz. Ancak Datasheet’e bakıldığında önemli bir uyarı ile karşılaşıyoruz:

       APB2’ye bağlı olan zamanlayıcılar için clock hattı 168Mhz’e kadar olabilir, APB1 içinse 84Mhz olabilir. Burada bir ikilem var gibi duruyor ancak datasheet’te bunu şöyle açıklıyor:  APB ön bölücü değeri 1 ise timer clock hattı da aynı frekansta çalışır, ancak ön bölücü değeri 1 değilse veya 1’den farklı bir değerde ise timer clock hattı APB hattının 2 katı hızda çalışır.

Timer6’ye baktığımızda APB1 bus hattına bağlıdır. Bu hattın clock frekansı 42 Mhz. Daha önce CMSIS klasöründe oluşturduğumuz system_stm32f4xx.c dosyasında ön bölücü değeri 4 olarak kullanıldığı için bu hat 42*2 yani 84 Mhz clock frekansında çalışmaktadır. Daha detaylı bilgiye datasheet’ten bakılabilir.

Prescaler değeri ise:      Timer hızı  = Bus hızı / (prescaler + 1)  formülünden bulunur.

Sorgu yapıldıktan sonra cevap beklenir burada timer ile bekleme süresini ayarlayabiliriz

Örneğin bekleme süremiz yani haberleşme timeout süremiz 1 saniye olsun:

1sn = 1/1 = 1Hz frekansa eşittir. 

Öncelikle 2Khz (2000Hz) için:            2000 = 84Mhz(buz hızı) /(prescaler+1)
                                                            2000 = 84000000 / (PRSC + 1) = > PRSC = 41999 

Periyodu da biz 2000’e kadar saydırırsak:           2000 Hz/ 2000 = 1Hz
                                                                           1Hz = 1/= 1 sn demektir.

Prescaler değeri: 42000 - 1
Periyod: 2000 -1 = 1999 (sayıma 0’dan başladığı için 1 eksiği olur)
1 sn için değerlerimizi yukarıdaki gibi hesaplarız.
*/
      RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
     TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure; // Struct
     NVIC_InitTypeDef NVIC_InitStructure;

     TIM_DeInit(TIM6);

     TIM_TimeBaseStructure.TIM_Period = 2000 -1;
     TIM_TimeBaseStructure.TIM_Prescaler = 42000 -1; /
     TIM_TimeBaseStructure.TIM_ClockDivision =0;  // TIM_CKD_DIV1
     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
     TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
     TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure);

     TIM_ClearITPendingBit(TIM6,TIM_IT_Update);  // Clear Update interrupt flag

     TIM_ITConfig(TIM6, TIM_IT_Update, ENABLE); // Interrupt

     NVIC_InitStructure.NVIC_IRQChannel = TIM6_DAC_IRQn; // NVIC Ayarlari
     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
     NVIC_Init(&NVIC_InitStructure);

     TIM6->CR1 &=~ 0x0001;             // Counter Disable
      // TIM6'yı daha aktif etmedik dikkat edin. !!
}
/********************************************************************************/


int main(void)
{
unsigned short adres_baslangic = 0; //ahngi adresten veri istenilecek
     GPIO_Init();
     USART_Init();

/* modbus sorgusunu çektikten sonra gelen paketi değerlendirmek için 1sn timeout zamanı olsun dedik. bu süreyi siz istediğiniz gibi değiştirebilirsiniz.
    Bu zamanı timer6 birimi ile yapmak istiyoruz. o yüzden timer6 yı yaklaşık olarak 1sn timeout süresine ayarlamamız gerekli.
    yukarıdaki hesabımıza göre bize yaklaşık olarak 9600 baudrate hızında,
    1sn için hesaplamaları alttaki fonksiyon içerisinde yapalım
*/
     TIM6_Init();

/* Bütün ayarlamaları yaptık. Artık iş sorguyu çekmek ve gelen veriyi analiz etmede. Cevap için usart kesmesinden gelen veri takip edilecek. verinin bittiği timer ile anlaşılacak.
*/

/* Burada öncelikle sorgu yapacağımız paketi oluşturalım */
Modbus_Trans_Array[0] = 1; // sorgu çekilecek slave adresi, biz 1.slave sorgu yapacağız Modbus_Trans_Array[1] = 03; // fonksiyon kodu, 03 Read Holding Register Modbus_Trans_Array[2] = adres_baslangic>>8; // hangi adresten bilgi isteyeceğiz Modbus_Trans_Array[3] = adres_baslangic; // hangi adresten bilgi isteyeceğiz Modbus_Trans_Array[4] = 2>>8; // yukarıdaki adresten kaç adet bilgi isteyeceğiz, 2 adet dedik Modbus_Trans_Array[5] = 2; // yukarıdaki adresten kaç adet bilgi isteyeceğiz, 2 adet dedik Get_CRC(Modbus_Trans_Array,8); // Gidecek paketin son 2 byte'ına CRC kodunu ekledik 

     while(1)
     {
/* butona her basıldığında sorgu çekilsin.
          öncelikle gidecek paketi hazırlayalım
       */

// RTU istegi Yapilacak
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0))
{
while(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)); // tuşa basılıp çekildikten sonra

for(Tx_count=0;Tx_count<8;Tx_count++)
{
   while(!(USART_GetFlagStatus(USART1, USART_FLAG_TXE)));
   USART_SendData(USART1, Modbus_Trans_Array[Tx_count]);
// Clear & Start Timer
   TIM6->CNT = 0; // TIM6 Clear
   TIM_Cmd(TIM6, ENABLE); //  Enable TIM6
// timeri çalıştırdık ve artık timeout sonunda veriyi inceleyeceğiz
// veriyi gönderdik ve cevabı bekliyoruz
// cevap usart kesmesine gelecek
  }

}


     }

}


/********************************************************************************/
/* USART1 KESME FONKSİYONU */

void USART1_IRQHandler(void)
{
     if( USART_GetITStatus(USART1, USART_IT_RXNE) )
     {
          USART_ClearITPendingBit(USART1, USART_IT_RXNE);//kesme bayrağı temizlenir

/* Burada slave örneğinde olduğu gibi bir timeri veya farklı bir gecikme fonksiyonu uygulayarak gelen paket takibi yapılabilirdi. ancak buraya yazılması uzun olacağı için gerek duyulmadı. Bu şekilde olan haberleşme de gayet sağlıklı bir şekilde çalışmaktadır. Program timer6 kesmesine girdiğinde eğer veri gelmiş ise gelen veri değerlendirilir, eğer veri yoksa timeout'a düşmüştür yani ya slave cihazından veri gelmemiştir ya da slave cihazı bizim belirlediğimiz timeout süresi içerisinde bize cevap göndermemiştir*/

          // Gelen veriyi oku ve diziye atamasını yap
          Modbus_Receive_Array[modbus_receive_counter ] = USART_ReceiveData(USART1);

          // Gelen paketteki veri sayacini arttir
          modbus_receive_counter ++;

  
     }
     else if( USART_GetITStatus(USART1, USART_IT_TXE) )
     {
          USART_ClearITPendingBit(USART1, USART_IT_TXE);
     }


}



/********************************************************************************/
/* TIMER6 KESME FONKSİYONU */
void TIM6_DAC_IRQHandler(void)
{

/* Yukarıda da anlatıldığı üzere, eğer program bu kesme fonksiyonuna girmiş ise sorgu gönderilmiş ve Slave cihazdan cevap gelmiş demektir ( eğer haberleşmede bir yanlışlık yok ise). Gelen veri paketini usart kesmesinde almıştık. şimdi paketi inceleyelim
*//

TIM_ClearITPendingBit(TIM6,TIM_IT_Update);  // Clear Update interrupt flag

// Counter Disable
TIM6->CR1 &=~ 0x0001;
TIM_Cmd(TIM6, DISABLE); //  Disable TIM6


        //eğer veri gelmiş ise, sorgu yaptığımız slave adresi 1
if (Modbus_Receive_Array[0]  == 1)
        {
                //CRC dogru ise, yani paketimiz güvenli bir şekilde bize ulaştı mı
if(CRC_check((char*)Modbus_Receive_Array,modbus_receive_counter )==1)
{
       // Modbus haberleşmesinde 3 numaralı fonksiyon Read Holding Register fonksiyonu
       if ( (Modbus_Receive_Array[1] == 3)  ) // fonksiyon kodu 3 ise
        {

             data_count=0;
             for(i=0;i<Modbus_Receive_Array[2]/2;i++)
             {                  Readed_Array[i] = panelcontrol.RxBuf[data_count+3]*256 + panelcontrol.RxBuf[data_count+4];                  data_count = data_count+2;               }

                    /* Readed_Array dizimize gelen verileri yükledik.
                   burada artık gelen veri ile ne yapmak istiyorsak yapabiliriz
                    Led kontrolu
                     slave'den alınan sensör bilgileri
                    istenildi ise parametre değerleri
                    .... bu liste uzatılabilir artık ne yapılmak isteniliyorsa..
                    */
              }

}

}

modbus_receive_counter = 0;




}



char CRC_check(char package[],unsigned int package_length)
{
volatile unsigned int crc[2];
volatile unsigned int CRCFull = 0xFFFF;
volatile unsigned int CRCHigh = 0xFF, CRCLow = 0xFF;
volatile unsigned int CRCLSB;
volatile unsigned int i=0;
volatile unsigned int j=0;
char CRC_OK=0;

    for (i = 0; i < package_length-2; i++)
    {
        CRCFull = (unsigned int)(CRCFull ^ package[i]);

        for (j = 0; j < 8; j++)
        {
            CRCLSB =  (unsigned int)( CRCFull & 0x0001);
            CRCFull = (unsigned int)((CRCFull >> 1) & 0x7FFF);

            if (CRCLSB == 1)
                CRCFull = (unsigned int)(CRCFull ^ 0xA001);
        }
    }
    crc[1] = CRCHigh = (unsigned int)((CRCFull >> 8) & 0xFF);
    crc[0] = CRCLow  = (unsigned int)( CRCFull & 0xFF);

    if((crc[0] == package[package_length-2]) && (crc[1] == package[package_length-1]))
    CRC_OK = 1;
    else
    CRC_OK = 0;

    return CRC_OK;
}


void Get_CRC(unsigned char package[],unsigned int package_length)
{
    volatile unsigned int crc[2];
    volatile unsigned int CRCFull = 0xFFFF;
    volatile unsigned int CRCHigh = 0xFF, CRCLow = 0xFF;
    volatile unsigned int CRCLSB;
    volatile unsigned int i=0;
    volatile unsigned int j=0;
    volatile char CRC_OK=0;

    for (i = 0; i < package_length-2; i++)
    {
        CRCFull = (unsigned int)(CRCFull ^ package[i]);

        for (j = 0; j < 8; j++)
        {
            CRCLSB = (unsigned int)(CRCFull & 0x0001);
            CRCFull = (unsigned int)((CRCFull >> 1) & 0x7FFF);

            if (CRCLSB == 1)
                CRCFull = (unsigned int)(CRCFull ^ 0xA001);
        }
    }
    crc[1] = CRCHigh = (unsigned int)((CRCFull >> 8) & 0xFF);
    crc[0] = CRCLow  = (unsigned int)( CRCFull & 0xFF);
    package[package_length-2] = crc[0];
    package[package_length-1] = crc[1];
}


/* 
Anlamadığınız yerleri sorabilirsiniz.
İyi Çalışmalar

*/