MODUL 3 PERCOBAAN 2
1. Buat dua project baru pada STM32CubeIDE menggunakan mikrokontroler STM32 NUCLEO G474RE.
- Project_Master
- Project_Slave
2. Konfigurasi Project Master
a. Aktifkan I2C1 dengan pengaturan:
Mode: I2C
- Clock Speed: 100 kHz (Standard Mode)
- Addressing Mode: 7-bit
- Pin yang digunakan:
- PB6 → SCL
- PB7 → SDA
b. Aktifkan SPI1 dengan pengaturan:
- Mode: Full Duplex Master
- Direction: 2 Lines
- Data Size: 8-bit
- Clock Polarity: Low
- Clock Phase: 1 Edge
- NSS: Software
- Baudrate Prescaler: 16
- First Bit: MSB First
Pin yang digunakan:
- PA5 → SCK
- PA6 → MISO
- PA7 → MOSI
3. Konfigurasi Project Slave
a. Aktifkan SPI1 dengan pengaturan:
- Mode: Full Duplex Slave
- Direction: 2 Lines
- Data Size: 8-bit
- Clock Polarity: Low
- Clock Phase: 1 Edge
- NSS: Hardware Input
- First Bit: MSB First
Pin yang digunakan:
- PA5 → SCK
- PA6 → MISO
- PA7 → MOSI
- PA4 → NSS
b. I2C tidak digunakan pada Slave
4. Setelah seluruh konfigurasi selesai, lakukan Generate Code untuk masing-masing project.
2. Hardware dan Diagram Blok [Kembali]
3. Rangkaian Simulasi dan Prinsip Kerja [Kembali]
Prinsip kerja sistem dimulai dari STM32 Master yang berfungsi sebagai pengendali utama permainan Dino pada layar OLED menggunakan komunikasi I2C. Master membaca input tombol melalui GPIO untuk mengontrol karakter Dino agar dapat melompat, kemudian memproses perhitungan gerakan menggunakan nilai kecepatan (velocity) dan gravitasi. Selanjutnya Master mengatur pergerakan objek kaktus, memperbarui posisi karakter, serta menampilkan elemen permainan seperti Dino, rintangan, garis tanah, dan skor pada layar OLED secara terus menerus.
Selama permainan berjalan, Master juga melakukan proses deteksi tabrakan (collision detection) antara karakter Dino dan kaktus. Jika Dino berhasil melewati rintangan, nilai skor akan bertambah. Namun apabila terjadi tabrakan, sistem akan mengubah status menjadi game over dan menampilkan pesan Game Over beserta nilai high score pada OLED. Seluruh proses logika permainan diproses oleh board Master sehingga board ini berperan sebagai pusat kendali sistem.
Selain memproses permainan, Master juga mengirimkan data perintah ke STM32 Slave menggunakan komunikasi SPI melalui jalur MOSI, MISO, SCK, dan NSS. Perintah yang dikirim berupa kondisi permainan seperti game berjalan, game over, suara lompatan, dan suara tabrakan. Data tersebut diterima oleh Slave menggunakan fungsi HAL_SPI_Receive().
Board STM32 Slave berfungsi mengendalikan perangkat keluaran tambahan berupa LED hijau, LED merah, dan buzzer. Saat permainan berjalan normal LED hijau menyala, sedangkan ketika permainan berakhir LED merah akan aktif. Selain itu buzzer menghasilkan bunyi pendek saat Dino melompat dan pola bunyi tertentu saat terjadi tabrakan. Dengan pembagian tugas ini, sistem master–slave mampu membuat proses kerja lebih terstruktur karena pemrosesan permainan dan pengendalian output dilakukan secara terpisah.
Listing Program :
Progarm Master (main.c)
#include "main.h"
#include "stm32g4xx_hal_i2c.h"
/* Private variables ---------------------------------------------------------*/
I2C_HandleTypeDef hi2c1;
#define SSD1306_I2C_PORT hi2c1
/* Variabel Game */
int dinoY = GROUND_Y;
int velocityY = 0;
const int gravity = 2;
uint8_t isJumping = 0;
int cactusX = 128;
uint32_t score = 0;
char scoreBuf[10];
uint8_t gameOver = 0;
uint32_t highScore = 0;
/* Private function prototypes -----------------------------------------------*/
void ResetGame(void);
int main(void)
{
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
SystemClock_Config();
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_I2C1_Init();
/* USER CODE BEGIN 2 */
ssd1306_Init();
ssd1306_Fill(Black);
ssd1306_UpdateScreen();
/* USER CODE END 2 */
while (1)
{
if (!gameOver)
{
/* 1. Input Handling (Pull-up: LOW = Pressed) */
if (HAL_GPIO_ReadPin(JUMP_BTN_GPIO_Port, JUMP_BTN_Pin) == GPIO_PIN_RESET && !isJumping)
{
velocityY = -12;
isJumping = 1;
}
/* 2. Physics Update */
dinoY += velocityY;
velocityY += gravity;
if (dinoY >= GROUND_Y)
{
dinoY = GROUND_Y;
isJumping = 0;
velocityY = 0;
}
/* 3. Obstacle Update */
cactusX -= 6;
if (cactusX < -10)
{
cactusX = 128;
score++;
}
/* 4. Collision Detection */
// Hitbox sederhana: Jika kaktus berada di area Dino dan Dino tidak cukup tinggi
if (cactusX < 25 && cactusX > 5 && (dinoY + DINO_HEIGHT) > 48)
{
gameOver = 1;
}
/* 5. Rendering */
ssd1306_Fill(Black);
// Menggambar Dino (Kotak)
ssd1306_DrawRectangle(10, dinoY, 10 + DINO_WIDTH, dinoY + DINO_HEIGHT, White);
// Menggambar Kaktus (Isi penuh)
ssd1306_FillRectangle(cactusX, 48, cactusX + 8, 60, White);
// Garis Tanah
ssd1306_Line(0, 61, 127, 61, White);
// Menampilkan Score
sprintf(scoreBuf, "Score: %lu", score);
ssd1306_SetCursor(0, 0);
ssd1306_WriteString(scoreBuf, Font_7x10, White);
ssd1306_UpdateScreen();
}
else
{
char hsBuf[20];
ssd1306_Fill(Black);
ssd1306_SetCursor(35, 25);
ssd1306_WriteString("GAME OVER", Font_7x10, White);
ssd1306_SetCursor(20, 10);
sprintf(hsBuf, "HighScore: %lu", highScore);
ssd1306_WriteString(hsBuf, Font_7x10, White);
ssd1306_UpdateScreen();
// Update high score
if (score > highScore)
{
highScore = score;
}
// restart
if (HAL_GPIO_ReadPin(JUMP_BTN_GPIO_Port, JUMP_BTN_Pin) == GPIO_PIN_RESET)
{
ResetGame();
HAL_Delay(300);
}
}
}
}
void ResetGame(void)
{
dinoY = GROUND_Y;
velocityY = 0;
isJumping = 0;
cactusX = 128;
score = 0;
gameOver = 0;
}
void MX_I2C1_Init(void)
{
hi2c1.Instance = I2C1;
hi2c1.Init.Timing = 0x00303D5D;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLED;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLED;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLED;
if (HAL_I2C_Init(&hi2c1) != HAL_OK)
{
Error_Handler();
}
}
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
/* Konfigurasi PA0 sebagai Input Pull-up */
GPIO_InitStruct.Pin = JUMP_BTN_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(JUMP_BTN_GPIO_Port, &GPIO_InitStruct);
}
void Error_Handler(void)
{
__disable_irq();
while (1) {}
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV4;
RCC_OscInitStruct.PLL.PLLN = 85;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
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_4) != HAL_OK)
{
Error_Handler();
}
}
Program Slave Main.c
#include "main.h"
#include "stm32g4xx_hal_spi.h"
/* =========================================================
GLOBAL HANDLE
========================================================= */
SPI_HandleTypeDef hspi1;
/* =========================================================
GLOBAL VARIABLE
========================================================= */
static uint8_t rxData;
/* =========================================================
MAIN
========================================================= */
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_SPI1_Init();
/* LED awal */
HAL_GPIO_WritePin(LED_GREEN_GPIO_Port,
LED_GREEN_Pin,
GPIO_PIN_RESET);
HAL_GPIO_WritePin(LED_RED_GPIO_Port,
LED_RED_Pin,
GPIO_PIN_RESET);
while (1)
{
/* =====================================
TERIMA DATA SPI
===================================== */
HAL_SPI_Receive(&hspi1,
&rxData,
1,
HAL_MAX_DELAY);
/* =====================================
PROCESS COMMAND
===================================== */
switch (rxData)
{
/* ================================
GAME RUN
================================ */
case CMD_GAME_RUN:
HAL_GPIO_WritePin(LED_GREEN_GPIO_Port,
LED_GREEN_Pin,
GPIO_PIN_SET);
HAL_GPIO_WritePin(LED_RED_GPIO_Port,
LED_RED_Pin,
GPIO_PIN_RESET);
break;
/* ================================
GAME OVER
================================ */
case CMD_GAME_OVER:
HAL_GPIO_WritePin(LED_GREEN_GPIO_Port,
LED_GREEN_Pin,
GPIO_PIN_RESET);
HAL_GPIO_WritePin(LED_RED_GPIO_Port,
LED_RED_Pin,
GPIO_PIN_SET);
break;
/* ================================
JUMP SOUND
================================ */
case CMD_JUMP_SOUND:
Beep_Jump();
break;
/* ================================
HIT SOUND
================================ */
case CMD_HIT_SOUND:
Beep_Hit();
break;
default:
break;
}
}
}
/* =========================================================
BEEP JUMP
bib
========================================================= */
void Beep_Jump(void)
{
HAL_GPIO_WritePin(BUZZER_GPIO_Port,
BUZZER_Pin,
GPIO_PIN_SET);
HAL_Delay(80);
HAL_GPIO_WritePin(BUZZER_GPIO_Port,
BUZZER_Pin,
GPIO_PIN_RESET);
}
/* =========================================================
BEEP HIT
bib bib biiiiib
========================================================= */
void Beep_Hit(void)
{
/* bib */
HAL_GPIO_WritePin(BUZZER_GPIO_Port,
BUZZER_Pin,
GPIO_PIN_SET);
HAL_Delay(100);
HAL_GPIO_WritePin(BUZZER_GPIO_Port,
BUZZER_Pin,
GPIO_PIN_RESET);
HAL_Delay(100);
/* bib */
HAL_GPIO_WritePin(BUZZER_GPIO_Port,
BUZZER_Pin,
GPIO_PIN_SET);
HAL_Delay(100);
HAL_GPIO_WritePin(BUZZER_GPIO_Port,
BUZZER_Pin,
GPIO_PIN_RESET);
HAL_Delay(100);
/* biiiiib */
HAL_GPIO_WritePin(BUZZER_GPIO_Port,
BUZZER_Pin,
GPIO_PIN_SET);
HAL_Delay(500);
HAL_GPIO_WritePin(BUZZER_GPIO_Port,
BUZZER_Pin,
GPIO_PIN_RESET);
}
/* =========================================================
GPIO INIT
========================================================= */
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* =====================================
CLOCK ENABLE
===================================== */
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/* =====================================
LED + BUZZER OUTPUT
===================================== */
GPIO_InitStruct.Pin =
LED_GREEN_Pin |
LED_RED_Pin |
BUZZER_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB,
&GPIO_InitStruct);
/* =====================================
SPI1 GPIO
PA4 = NSS
PA5 = SCK
PA6 = MISO
PA7 = MOSI
===================================== */
GPIO_InitStruct.Pin =
GPIO_PIN_4 |
GPIO_PIN_5 |
GPIO_PIN_6 |
GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
HAL_GPIO_Init(GPIOA,
&GPIO_InitStruct);
}
/* =========================================================
SPI1 INIT
========================================================= */
void MX_SPI1_Init(void)
{
hspi1.Instance = SPI1;
hspi1.Init.Mode =
SPI_MODE_SLAVE;
hspi1.Init.Direction =
SPI_DIRECTION_2LINES;
hspi1.Init.DataSize =
SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity =
SPI_POLARITY_LOW;
hspi1.Init.CLKPhase =
SPI_PHASE_1EDGE;
hspi1.Init.NSS =
SPI_NSS_HARD_INPUT;
hspi1.Init.FirstBit =
SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode =
SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation =
SPI_CRCCALCULATION_DISABLE;
if (HAL_SPI_Init(&hspi1) != HAL_OK)
{
Error_Handler();
}
}
/* =========================================================
CLOCK CONFIG
========================================================= */
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
HAL_PWREx_ControlVoltageScaling(
PWR_REGULATOR_VOLTAGE_SCALE1);
RCC_OscInitStruct.OscillatorType =
RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState =
RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState =
RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource =
RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM =
RCC_PLLM_DIV4;
RCC_OscInitStruct.PLL.PLLN =
85;
RCC_OscInitStruct.PLL.PLLR =
RCC_PLLR_DIV2;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
RCC_ClkInitStruct.ClockType =
RCC_CLOCKTYPE_SYSCLK |
RCC_CLOCKTYPE_HCLK |
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;
HAL_RCC_ClockConfig(
&RCC_ClkInitStruct,
FLASH_LATENCY_4
);
}
/* =========================================================
ERROR HANDLER
========================================================= */
void Error_Handler(void)
{
__disable_irq();
while (1)
{
}
}
- Download Analisa disini
- Download Vidio disini
- Download Datasheet STM32 Nucleo G474RE [Klik Disini]
- Download Datasheet Resistor (klik disini)
- Download Datasheet LED (klik disini)
- Download Datasheet Push Button [Klik Disini]
No comments:
Post a Comment