Pages

STM32VLDISCOVERY KART ÜZERİNDE BULUNAN BUTON VE LEDLER İÇİN MODÜLER DRIVER GELİŞTİRME

 


STM32VLDiscovery geliştirme kitinin üzerinde bulunan kullanıcı buton ve ledleri için geliştirdiğim örnek driver kodunu sizlerle paylaşmak istiyorum. Register seviyesinde programlamaya  yeni başlamış arkadaşlar için öğretici, sade ve basit bir kod olacağını düşünüyorum. Adımları elimden geldiğince detaylı açıklamaya çalışacağım bu sayede kendi kartınızın dökümanlarını inceleyerek sizde kendi  kartınız için uyarlayabilirsiniz.



User Manual'de gördüğünüz gibi kart üzerinde kullanıcı için konumlandırılmış iki adet led ve bir adet buton olduğunu görebiliyoruz. 

Kart üzerindeki konumları ve ledlerin hangi pinlere bağlı olduğunu gösteren bir görsel de mevcuttur.
Burada ledlerin PC9 ve PC8 pinlerine bağlı olduğunu görüyoruz.





User Manuel'de bulunan Elektriksel Şematik (Electrical Schematics) bölümünden kart üzerindeki komponentlerin  bağlantılarını inceleyebiliriz.  Yukarıda gördüğümüz  gibi mavi ledin PC8, yeşil ledin PC9 ve kullanıcı butonunun ise bir pull-down direnci ile PA0 pinine bağlı olduğunu görüyoruz. 

Pull-up / Pull down dirençleri hakkında yeterli bilgiye sahip değilseniz önceden yazmış olduğum yazımı inceleyebilirsiniz.

Artık donanım kısmı ile yeterli bilgilere ulaştığımıza göre kod kısmına geçebiliriz.


Öncelikle ledler ve buton için kullanacağımız pinleri giriş ve çıkış olarak tanımlamamız gerekir. Ancak  bunu yapmadan önce pinlerin hangi porta bağlı olduğunu ve bu portun hangi bus hattına bağlı olduğunu öğrenmemiz gerekir. GPIO portlarının APB2 bus hattına bağlı olduğunu görebiliyoruz. Portları aktif etmek için ilgili register'ı inceleyelim.

RCC_APB2ENR register'ının bitlerine bakarsak 2. ve  4. bitleri ihtiyacımız olan led ve buton portlarını içermektedir.


İlgili bitlere 1 değerini yazarak portu aktifleştirip, 0 değerini yazarak portu pasif hale getirebiliriz.
İlgili işlemin kod karşılığı aşağıdaki gibidir.


APB2ENR register'ının 1. ve 4. bitlerine 1 değerini yazarak GPIOA ve GPIOC portlarını aktif etmiş olduk. Şimdi ise aktifleştirdiğimiz bu portlarda ilgili pinleri giriş olarak mı yoksa çıkış olarak mı kullanacağımızı seçmemiz gerekiyor. Bildiğiniz üzere butonun bağlı olduğu pini (PA0) giriş (input), ledlerin bağlı olduğu pinleri ise (PC8&PC9) çıkış (output) olarak tanımlamamız gerekir. Bu işlem için pin ayarlamalarının yapıldığı ilgili register'ı inceleyelim.



GPIOx_CRL ve GPIOx_CRH olmak üzere işlevleri aynı fakat iki farklı register bulunmaktadır.  Bunun  sebebi CRL  register'ı ilk  8 pin için (0-7) CRH register'ı ise sonraki 8 pin (8-15) kullanılmaktadır.
Bir pin üzerinden incelememiz gerekirse MODER ve CNF bitlerini aşağıdaki yönergelere uygun ayarlamamız gerekir. (CRL  ve CRH için işlemler aynıdır)




Önce bir led üzerinden örnek vermek gerekirse PC8 pinini çıkış olarak ayarlamak için bitleri aşağıdaki  gibi düzenlememiz gerekir.



Yukarıda gördüğümüz gibi mode bitleri ile pinin giriş mi çıkış mı olacağı ve çıkış olarak kullanılacak ise hızının ayarlanması gerçekleştirilir. Led yakmak için hızın herhangi bir önemi olmasada ben uygulamamda maksimum hız kullanmak istediğim için Output mode - max speed 50 MHz olarak  ayarladım. Ardından CNF bitlerini ayarlamak için yukarıda bulunan iki koşul için çıkış ayarlamasında mode biti sıfırdan büyük olduğu için altta gösterilen In output mode kısmından çıkışın türünü ayarlarız.
Led yakmak için çıkışımızı output push-pull olarak ayarlamamız gerekir. Şimdilik bu kadarını bilmek bu uygulama için yeterli olduğundan çıkış türlerini bu yazımda açıklamıyorum. Bu çıkış türleri arasındaki farklar da bir başka yazının konusu olabilir. Görseldeki gibi ilgili bitlere 1 ve 0 yazarak pinimi istediğim hızda istediğim türde çıkış olarak ayarlamış oldum. 




Her iki led için gerekli kod karşılığı yukarıdaki gibidir.


Buton için gerekli pini giriş olarak ayarlamak için ise aşağıdaki adımları izlememiz gerekir.
Butonumuz PA0 pinine bağlı olduğu için bu sefer CRL register'ını kullanacağız.
CRL register'ının 0-4 bitlerine gerekli ayarlamaları yaparsak,


Bu sefer MODE bitlerine 0 değerlerini yazdık çünkü yukarıdaki yönergede giriş seçimi için ilk iki bite 0 yazılması gerektiği söylenmektedir. MODE bitlerine 0 değeri yazdığımız için MODE değerimiz sıfıra eşit olmuş oldu ve bu sefer CNF bitleri için ilk kısım üzerinden ayarlamalarımıza devam edeceğiz. Butonumuz için Input with pull-up / pull-down seçeneğini seçmemiz gerekir. Bunun için CNF bitlerine 1 ve 0 değerlerini yazdık ve PA0 pinini butonumuz için giriş olarak ayarlamış olduk.


Buton için gerekli kod karşılığı yukarıdaki gibidir.

Init fonksiyonumuz için gerekli ayarlamalar yukarıdaki gibidir. Buton okuma, led yakma söndürme, led toggle gibi fonksiyonlar içerisinde kullandığım diğer resigterlar ile ilgili bilgi verdikten sonra yazının sonunda tüm kodu inceleyip ilgili registerlar ile kendi fonksiyonlarınızı algoritmanızı kurabilirsiniz.

GPIOx_IDR



Input Data Register'ından (IDR) kısaca bahsetmek gerekirse, register'ın  bitleri read only yani sadece okuma işlemi yapabildiğimiz bitlerdir. Giriş - çıkış portlarının değerini okumamıza yarar.  Buton basıldı basılmadı bilgisine ulaşmak için veya herhangi bir çıkış portunun durumunu anlık olarak okumak için kullanabiliriz. Birazdan fonksiyonların içerisinde de göreceğiniz gibi IDR register'ından bolca yararlandık.

GPIOx_BSRR

    

BSRR register'ı diğer registerlara göre biraz farklı çalışmakta. Açıklamak gerekirse, ilk 16 bit için farklı bir görevi varken diğer 16 bit için farklı görevi vardır. Şöyle ki ilk 0-15 bit için 1 yazıldığı durumda ilgili sayısal karşılığı olan pin set edilerek çıkış verir. Set konuma getirdiğimiz pini reset konumuna çekmek için ise MSB karşılığı olan 16 bit kaydırılmış bite 1 yazmamız gerekir. Örnek ile açıklamak  gerekirse PA0 pinine bağlı bir ledi yakmak için BSSR register'ının  0. bitini 1 konumuna çekmemiz gerekir. Söndürmek istediğimizde ise 16. bitini  1 konumuna çekerek söndürme işlemini gerçekleştiririz. Set ve toggle fonksiyonlarını incelediğinizde daha net anlaşılacağından eminim.

Aşağıya user_lib.h, user_lib.c uzantılı dosyaları ekledim ve yazdığımız fonksiyonların main içerisinde test edildiği main kodunu da en sona ekledim. Umarım başlangıç seviyesi açıklayıcı ve öğretici bir yazı olmuştur.

 /*
 * user_lib.h
 *
 *  Created on: Apr 3, 2022
 *      Author: Emre Aytur
 */

#ifndef INC_USER_LIB_H_
#define INC_USER_LIB_H_

#include "stm32f1xx_hal.h"
#include "stdint.h"

typedef enum
{
	OFF  = 0,
	ON

}LED_State;

typedef enum
{
	BLUE  = 8u,
	GREEN = 9u

}LED_Color;


typedef enum
{
	RELEASED = 0,
	PRESSED	 = 1

}BUTTON_State;

void user_lib_init(void);
void led_set_state(LED_Color led_color, LED_State led_state);
void led_toggle(LED_Color led_color);
uint8_t	get_button_state(void);


#endif /* INC_USER_LIB_H_ */

/*
 * user_lib.c
 *
 *  Created on: Apr 3, 2022
 *      Author: Emre Aytur
 */

#include "user_lib.h"

void user_lib_init(void)
{
	RCC   -> APB2ENR  |= (1<<2)  | (1<<4);

	GPIOC -> CRH = 0x33;	//GPIOC  PIN8 & PIN9 - OUTPUT PP 50 MHZ MAX SPEED

	/* 0000 0000 0000 0000 0000 0000 0011 0011
	   0    0    0     0   0    0    3    3      = 0x33 */

	GPIOA -> CRL = 0x08;	//GPIOA	PIN0 INPUT

	/* 0000 0000 0000 0000 0000 0000 0000 1000
	   0    0    0     0   0    0    0    8      = 0x08 */
}

void led_set_state(LED_Color led_color, LED_State led_state)
{

	if(led_state == ON)
	{
		GPIOC -> BSRR |= (1 << led_color);
	}
	else
	{
		GPIOC -> BSRR |= (uint32_t)(1 << led_color) << 16;
	}

}

void led_toggle(LED_Color led_color)
{

	if(GPIOC -> IDR & (1<<led_color)) // IF LED ON
	{
		GPIOC -> BSRR |= (uint32_t)(1 << led_color) << 16;
	}
	else // IF LED OFF
	{
		GPIOC -> BSRR |= (1 << led_color);
	}
}

uint8_t	get_button_state(void)
{
	uint8_t button_state;

	if(GPIOA -> IDR & (1<<0))
	{
		button_state = PRESSED;
	}
	else
	{
		button_state = RELEASED;
	}

	return button_state;
}


////////////////////////////////////////////////////////////
//     01_ONBOARD_PERIPH		USER DRIVER        //
//	03.04.2022				EA         //
////////////////////////////////////////////////////////////


//      INCLUDES                //
#include "main.h"
#include "user_lib.h"

//	FUNCTION PROTOTYPES	//
void SystemClock_Config(void);
static void MX_GPIO_Init(void);



int main(void)
{

  //INITIALIZATIONS
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  user_lib_init();

  while (1)
  {

	  if(get_button_state() == PRESSED)
	  {
		  led_set_state(GREEN,ON);
		  led_set_state(BLUE,ON);
	  }
	  else //BUTTON RELEASED
	  {
		  led_set_state(GREEN,OFF);
		  led_set_state(BLUE,OFF);
	  }

	  led_toggle(GREEN);
	  HAL_Delay(500);
	  led_toggle(GREEN);
	  HAL_Delay(500);
  }


}


//////////////////////////////////////////////////////SYSCFG////////////////////////////////////////////////////////////////////////////////////
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL3;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
  {
    Error_Handler();
  }
}
static void MX_GPIO_Init(void)
{
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();

}
void Error_Handler(void)
{
  __disable_irq();
  while (1)
  {
  }
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

Emre Aytur

Gömülü Sistemler, Elektronik, Teknoloji