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