Laporan Akhir M3




1. Prosedur [Kembali]

  1. Siapkan alat dan bahan yang diperlukan, yaitu dua board STM32 NUCLEO-G474RE, OLED SSD1306, push button, LED hijau, LED merah, buzzer, jumper, dan breadboard.
  2. Rangkai komponen sesuai konfigurasi pin, hubungkan OLED ke board master pada pin PA13 (SCL) dan PA14 (SDA), hubungkan push button ke pin PA0 master, hubungkan jalur SPI antara master dan slave pada pin PA4 (NSS), PA5 (SCK), PA6 (MISO), dan PA7 (MOSI), serta hubungkan LED hijau ke PB0, LED merah ke PB1, dan buzzer ke PB2 pada board slave.
  3. Buka software STM32CubeIDE dan buat dua project baru, satu untuk master (nucleoi2c) dan satu untuk slave (nucleoi2c_slave), menggunakan mikrokontroler STM32G474RETx.
  4. Lakukan konfigurasi pada project master dengan mengaktifkan I2C1 pada pin PA13 dan PA14, mengaktifkan SPI1 sebagai Full Duplex Master pada pin PA5-PA7, serta mengatur PA0 sebagai GPIO Input Pull-up dan PA4 sebagai GPIO Output untuk NSS.
  5. Lakukan konfigurasi pada project slave dengan mengaktifkan SPI1 sebagai Full Duplex Slave pada pin PA4-PA7 dengan NSS Hard Input, serta mengatur PB0, PB1, dan PB2 sebagai GPIO Output.
  6. Lakukan Generate Code pada masing-masing project, kemudian masukkan program master dan program slave sesuai listing program yang telah disiapkan.
  7. Hubungkan masing-masing board ke komputer menggunakan kabel USB, lalu compile dan upload program ke masing-masing board menggunakan fitur Run pada STM32CubeIDE.
  8. Jalankan program dan amati hasilnya, tekan push button untuk mengendalikan permainan dan perhatikan respon LED serta buzzer pada board slave sesuai kondisi permainan yang berjalan.

2. Hardware dan diagram blok [Kembali]

1. Push Button


2. Breadboard

3. STM 32 NUCLEO G474RE

 


4. Adaptor
5. LED 

                                                                


 6. Jumper


3. Rangkaian Simulasi dan Prinsip Kerja [Kembali]



Sistem ini terdiri dari dua board STM32 NUCLEO-G474RE yang saling terhubung melalui protokol SPI, dengan board master juga terhubung ke layar OLED melalui protokol I2C. Board master menjalankan logika permainan Dino Jump, di mana input berupa tombol push button pada pin PA0 dibaca setiap siklus loop. Ketika tombol ditekan, karakter dino pada layar OLED akan bergerak ke atas mengikuti simulasi fisika gravitasi, sementara kaktus bergerak dari kanan ke kiri secara otomatis dengan kecepatan yang meningkat seiring bertambahnya skor.

Setiap perubahan kondisi permainan pada master langsung dikomunikasikan ke board slave melalui jalur SPI. Master menarik pin NSS ke kondisi LOW, mengirimkan satu byte command via MOSI, lalu melepas NSS ke HIGH sebagai tanda akhir pengiriman. Board slave menunggu data secara blocking menggunakan HAL_SPI_Receive, kemudian memproses command yang diterima melalui mekanisme switch-case untuk menghasilkan respons output yang sesuai.

Terdapat empat command yang digunakan sebagai penghubung antara logika game di master dengan output fisik di slave. Selama game berjalan, master mengirim command 0x01 sehingga LED hijau slave menyala sebagai indikator permainan aktif. Saat tombol ditekan dan dino melompat, master mengirim command 0x03 sehingga buzzer slave berbunyi selama 80ms sebagai efek suara lompat. Ketika posisi kaktus bertabrakan dengan posisi dino, master menetapkan status game over, menampilkan layar "GAME OVER" beserta high score pada OLED, lalu mengirim command 0x02 untuk menyalakan LED merah dan command 0x04 untuk membunyikan buzzer dengan pola bib-bib-biiib. Menekan tombol kembali saat game over akan mereset semua variabel dan permainan dimulai dari awal.








Listing Program

PROGRAM MASTER
main.c

#include "main.h"

#include "ssd1306.h"

#include <stdio.h>

/* ================= SPI ================= */
SPI_HandleTypeDef hspi1;

/* ================= OLED I2C ================= */
I2C_HandleTypeDef hi2c1;

/* ================= COMMAND ================= */
#define CMD_GAME_RUN     0x01
#define CMD_GAME_OVER    0x02
#define CMD_JUMP_SOUND   0x03
#define CMD_HIT_SOUND    0x04

/* ================= CS PIN ================= */
#define CS_PORT GPIOA
#define CS_PIN  GPIO_PIN_4

/* ================= GAME ================= */
int dinoY, velocityY, cactusX;

uint32_t score, highScore;
uint8_t isJumping, gameOver;

#define GRAVITY 2
#define FRAME_DELAY 30
#define GROUND_Y 48

#define DINO_HEIGHT 10

char buf[20];

/* ================= SEND SPI ================= */
void Send_To_Slave(uint8_t cmd)
{
    HAL_GPIO_WritePin(CS_PORT, CS_PIN, GPIO_PIN_RESET);

    HAL_SPI_Transmit(&hspi1, &cmd, 1, 100);

    HAL_GPIO_WritePin(CS_PORT, CS_PIN, GPIO_PIN_SET);

    HAL_Delay(1); // penting untuk sync slave
}

/* ================= MAIN ================= */
int main(void)
{
    HAL_Init();

    SystemClock_Config();

    MX_GPIO_Init();
    MX_SPI1_Init();
    MX_I2C1_Init();

    ssd1306_Init();

    HAL_GPIO_WritePin(GPIOA, CS_PIN, GPIO_PIN_SET);

    ResetGame();

    while (1)
    {
        if (!gameOver)
        {
            UpdateGame();

            DrawGame();

            Send_To_Slave(CMD_GAME_RUN);
        }
        else
        {
            DrawGameOver();

            if (score > highScore)
                highScore = score;

            Send_To_Slave(CMD_GAME_OVER);

            if (HAL_GPIO_ReadPin(JUMP_BTN_GPIO_Port, JUMP_BTN_Pin) ==
                GPIO_PIN_RESET)
            {
                ResetGame();

                HAL_Delay(300);
            }
        }

        HAL_Delay(FRAME_DELAY);
    }
}

/* ================= GAME LOGIC ================= */

void UpdateGame(void)
{
    if (HAL_GPIO_ReadPin(JUMP_BTN_GPIO_Port, JUMP_BTN_Pin) ==
        GPIO_PIN_RESET && !isJumping)
    {
        velocityY = -12;

        isJumping = 1;

        Send_To_Slave(CMD_JUMP_SOUND);
    }

    dinoY += velocityY;

    velocityY += GRAVITY;

    if (dinoY >= GROUND_Y)
    {
        dinoY = GROUND_Y;

        velocityY = 0;

        isJumping = 0;
    }

    cactusX -= (6 + score / 15);

    if (cactusX < -10)
    {
        cactusX = 128;

        score++;
    }

    if (cactusX < 25 && cactusX > 5 &&
        (dinoY + DINO_HEIGHT) > 48)
    {
        gameOver = 1;

        Send_To_Slave(CMD_HIT_SOUND);
    }
}

/* ================= DRAW ================= */

void DrawGame(void)
{
    ssd1306_Fill(Black);

    ssd1306_DrawRectangle(10, dinoY,
                          20, dinoY + DINO_HEIGHT,
                          White);

    ssd1306_FillRectangle(cactusX, 48,
                          cactusX + 8, 60,
                          White);

    ssd1306_Line(0, 61, 127, 61, White);

    sprintf(buf, "Sc:%lu", score);

    ssd1306_SetCursor(0, 0);

    ssd1306_WriteString(buf, Font_7x10, White);

    sprintf(buf, "Hsc:%lu", highScore);

    ssd1306_SetCursor(80, 0);

    ssd1306_WriteString(buf, Font_7x10, White);

    ssd1306_UpdateScreen();
}

void DrawGameOver(void)
{
    ssd1306_Fill(Black);
}
ssd1306_SetCursor(30, 15);

ssd1306_WriteString("GAME OVER", Font_7x10, White);

sprintf(buf, "HighScore:%lu", highScore);

ssd1306_SetCursor(25, 35);

ssd1306_WriteString(buf, Font_7x10, White);

ssd1306_UpdateScreen();

}

/* ================= RESET ================= */

void ResetGame(void)
{
    dinoY = GROUND_Y;

    velocityY = 0;

    cactusX = 128;

    score = 0;

    isJumping = 0;

    gameOver = 0;
}

/* ================= SPI INIT ================= */

void MX_SPI1_Init(void)
{
    hspi1.Instance = SPI1;

    hspi1.Init.Mode = SPI_MODE_MASTER;

    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_SOFT;

    hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;

    hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;

    HAL_SPI_Init(&hspi1);
}

/* ================= GPIO ================= */

void MX_GPIO_Init(void)
{
    __HAL_RCC_GPIOA_CLK_ENABLE();

    GPIO_InitTypeDef GPIO_InitStruct = {0};

    /* CS */
    GPIO_InitStruct.Pin = GPIO_PIN_4;

    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;

    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;

    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* BUTTON */
    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);
}

main.h

#ifndef __MAIN_H
#define __MAIN_H

#ifdef __cplusplus
extern "C" {
#endif

#include "stm32g4xx_hal.h"

#include "ssd1306.h"

#include "ssd1306_fonts.h"

#include <stdio.h>

/* Definisi Pin Hardware */

#define JUMP_BTN_Pin GPIO_PIN_0

#define JUMP_BTN_GPIO_Port GPIOA

/* Konstanta Permainan */

#define GROUND_Y 44

#define DINO_WIDTH 15

#define DINO_HEIGHT 15

/* Prototipe Fungsi */
void SystemClock_Config(void);
 void MX_GPIO_Init(void); 
 void MX_I2C1_Init(void);
 void Error_Handler(void);
 #ifdef __cplusplus
 }
 #endif 
#endif /* __MAIN_H */  

PROGRAM SLAVE

 main.c

#include "main.h"

/* ================= SPI ================= */
SPI_HandleTypeDef hspi1;

/* ================= COMMAND ================= */
#define CMD_GAME_RUN      0x01
#define CMD_GAME_OVER     0x02
#define CMD_JUMP_SOUND    0x03
#define CMD_HIT_SOUND     0x04

/* ================= PROTOTYPE ================= */
void SystemClock_Config(void);
void MX_GPIO_Init(void);
void MX_SPI1_Init(void);
void Send(uint8_t data);

/* ================= MAIN ================= */
int main(void)
{
    HAL_Init();
    SystemClock_Config();

    MX_GPIO_Init();
    MX_SPI1_Init();

    while (1)
    {
        Send(CMD_GAME_RUN);
        HAL_Delay(500);

        Send(CMD_JUMP_SOUND);
        HAL_Delay(500);

        Send(CMD_GAME_OVER);
        HAL_Delay(1000);
    }
}

/* ================= SEND SPI ================= */
void Send(uint8_t data)
{
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); // CS LOW
    HAL_SPI_Transmit(&hspi1, &data, 1, 100);
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);   // CS HIGH
}

/* ================= SPI INIT ================= */
void MX_SPI1_Init(void)
{
    hspi1.Instance = SPI1;

    hspi1.Init.Mode = SPI_MODE_MASTER;
    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_SOFT;
    hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
    hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;

    HAL_SPI_Init(&hspi1);
}

/* GPIO CS */
void MX_GPIO_Init(void)
{
    __HAL_RCC_GPIOA_CLK_ENABLE();

    GPIO_InitTypeDef GPIO_InitStruct = {0};

    GPIO_InitStruct.Pin = GPIO_PIN_4;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;

    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
}

/* CLOCK (simple safe) */
void SystemClock_Config(void)
{
}

5. Video Demo [Kembali]


6. Analisa [Kembali]










7. Video Simulasi [Kembali]


                                             

8. Download File [Kembali]














 

Komentar

Postingan populer dari blog ini

TUGAS BESAR

MODUL 1

MODUL 1