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

*/