LinkedIn YouTube Facebook
Szukaj

Wstecz
Artykuły

[CZĘŚĆ 1] STM32Butterfly2: Tetris na STM32 – wprowadzenie do mechanizmu gry

 

 

 

 

W artykule przedstawiamy sposób prostej implementacji na mikrokontrolerze STM32F107 znanej i popularnej od lat gry Tetris. Autor projektu wykorzystał peryferia zestawu STM32Butterfly2 i moduł z tanim monochromatycznym wyświetlaczem LCD z telefonu Nokia 3310.

 

 

Sposób połączenia modułu KAmodLCD z zestawem STM32Butterfly2, który zamienia ten zestaw w konsolę do grania, pokazano na rysunku 1. W aplikacji wykorzystano joystick znajdujący się na płytce „motyla”, który dołączono do wyprowadzeń mikrokontrolera w zestawie „na sztywno”. Do zaprogramowania mikrokontrolera potrzebny będzie programator JTAG, można użyć zgodnego z ST-Link/v2 programatora ZL30PRGv2 (fotografia 2). Projekt programistyczny utworzono za pomocą pakietu Atollic TrueSTUDIO Lite.

 

Rys. 1. Schemat ilustrujący połączenia niezbędne do prawidłowego działania Tetrisa na STM32Butterfly2

Rys. 1. Schemat ilustrujący połączenia niezbędne do prawidłowego działania Tetrisa na STM32Butterfly2

 

 

Z istniejących plików projektu interesują nas znajdujące się w katalogu src (pliki źródłowe) plik main.c i stm32f10x_it.c. W pierwszym znajduje się główna część programu, w drugim procedury obsługi przerwań.

 

Fot. 2. Wygląd programatora JTAG/SWD o nazwie ZL30PRGv2

Fot. 2. Wygląd programatora JTAG/SWD o nazwie ZL30PRGv2

 

 

Funkcja main to główna funkcja każdego programu, od niej rozpoczyna się jego wykonywanie. Na jego początku znajdują się procedury konfigurujące peryferia mikrokontrolera. W przykładowym projekcie procedury konfiguracyjne umieszczono w osobnych funkcjach, które kolejno wywołuje na początku funkcji main(), dlatego przed funkcją main należy umieścić deklaracje funkcji:

void RCC_Configuration(void);
void GPIO_Configuration(void);
void EXTI_Configuration(void);
void NVIC_Configuration(void);

Ciała tych funkcji znajdują się na listingu 1.

 

List. 1. Ciała funkcji inicjujących mikrokontroler

//Funkcje konfiguracyjne
//Funkcja konfigurująca porty GPIO

void GPIO_Configuration(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;						//inicjalizacja struktury konfiguracji GPIO

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE | RCC_APB2Periph_AFIO, ENABLE);	//uruchomienie taktowania dla portu GPIOE

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14 | GPIO_Pin_15;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(GPIOE, &GPIO_InitStructure);				//ustawienie pinów 14 i 15 portu GPIOE jako wyjść

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING  ;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
  GPIO_Init(GPIOE, &GPIO_InitStructure);				//ustawienie pinów 8, 9, 10, 11 i 12 portu GPIOE jako wejść
}

//Funkcja konfigurująca system sygnałów taktowania

void RCC_Configuration(void)
{
  ErrorStatus HSEStartUpStatus;

  RCC_DeInit();								//reset ustawień RCC
  RCC_HSEConfig(RCC_HSE_ON);							//włącz HSE

  HSEStartUpStatus = RCC_WaitForHSEStartUp();				//oczekiwanie na gotowość HSE

  if (HSEStartUpStatus == SUCCESS)
  {
	FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);		//włączenie buforu dla pamięci Flash
	FLASH_SetLatency(FLASH_Latency_2);					//ustawienie opóźnień dla pamięci Flash

	RCC_HCLKConfig(RCC_SYSCLK_Div1);					//taktowanie rdzenia sygnałem 1:1 (72MHz)
	RCC_PCLK2Config(RCC_HCLK_Div1);					//taktowanie magistrali APB2 sygnałem 1:1 (72MHz)
	RCC_PCLK1Config(RCC_HCLK_Div2);					//taktowanie magistrali APB1 sygnałem 1:2 (36MHz)

	RCC_PREDIV2Config(RCC_PREDIV2_Div5 );				//ustawienie PLL2 1:5*8  (25MHz/5*8=20Mhz)
	RCC_PLL2Config(RCC_PLL2Mul_8);
	RCC_PLL2Cmd(ENABLE);
	while(RCC_GetFlagStatus(RCC_FLAG_PLL2RDY) == RESET) {} 		//oczekiwanie na uruchomienie PLL2

  	RCC_PREDIV1Config(RCC_PREDIV1_Source_PLL2,RCC_PREDIV1_Div5);	//ustawienie PLL1 PLL2CLK/5*9 (20MHz/5*9=72MHz)
	RCC_PLLConfig(RCC_PLLSource_PREDIV1, RCC_PLLMul_9);
 	 RCC_PLLCmd (ENABLE);
  	while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) {} 		//oczekiwanie na uruchomienie PLL1

	RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);		//ustawienie pętli PLL1 jako źródła sygnału zegarowego dla systemu
  while (RCC_GetSYSCLKSource() != 0x08) {}			//oczekiwanie aż pętla PLL1 stanie się źródłem sygnały zegarowego dla sytemu
  }
}

//Funkcja konfigurująca GPIO jako źródła przerwań (kontroler EXTI)

void EXTI_Configuration(void)
{
  EXTI_InitTypeDef EXTI_InitStructure;  					//inicjalizacja struktury konfiguracji przerwań EXTI

  GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource8);		//aktywowanie funkcji EXTI pinów joystick'a
  GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource9);
  GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource10);
  GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource11);
  GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource12);

  EXTI_InitStructure.EXTI_Line = EXTI_Line8 | EXTI_Line9 | EXTI_Line10 | EXTI_Line11 | EXTI_Line12;
  EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
  EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; 			//przerwanie przy zboczu opadającym

  EXTI_InitStructure.EXTI_LineCmd = ENABLE;					//uaktywnienie kontrolera EXTI
  EXTI_Init(&EXTI_InitStructure);
}

//Funkcja konfigurująca kontroler przerwań NVIC

void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure; 				//inicjalizacja struktury konfiguracji kontrolera przerwań

  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); 			//wybór grupy priorytetów

  NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;		//konfiguracja przerwania od GPIO piny od 5 do 9
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);

  NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn; 		//konfiguracja przerwania od GPIO piny od 10 do 15
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}