Chapter 07 - Hongjayeong-02/Basic_SoC_implementation GitHub Wiki

07. GPIO

Goal

NUCLEO-F439ZI의 LD1(PB0) κ³Ό B1(PC13) 을 직접 μ œμ–΄ν•œλ‹€.

이번 μž₯μ—μ„œ 확인할 핡심:

PB0  β†’ Output β†’ LED
PC13 β†’ Input  β†’ Push Button

μ‚¬μš© register:

RCC->AHB1ENR
GPIOB->MODER
GPIOB->ODR
GPIOB->BSRR
GPIOC->MODER
GPIOC->PUPDR
GPIOC->IDR

1. GPIO 핡심

GPIO pin은 MODERμ—μ„œ λ™μž‘ λͺ¨λ“œλ₯Ό κ²°μ •ν•œλ‹€.

00 = Input
01 = Output
10 = Alternate Function
11 = Analog

Pin ν•˜λ‚˜λ‹Ή 2bitλ₯Ό μ‚¬μš©ν•œλ‹€.

Pin n
β†’ MODER[(2n+1):(2n)]

λ”°λΌμ„œ:

PB0  β†’ MODER[1:0]
PC13 β†’ MODER[27:26]

2. Board Mapping

NUCLEO-F439ZI:

LD1 Green LED
β†’ PB0

B1 User Button
β†’ PC13

이번 boardμ—μ„œ B1은:

Released β†’ 0
Pressed  β†’ 1

즉 active-HIGH둜 μ‚¬μš©ν•œλ‹€.


3. GPIO Data Registers

IDR

GPIOC->IDR

Input pin의 μ‹€μ œ logic level을 μ½λŠ”λ‹€.

PC13:

GPIOC->IDR & (1U << 13)

ODR

GPIOB->ODR

Output stateλ₯Ό μ €μž₯ν•œλ‹€.

PB0 toggle:

GPIOB->ODR ^= (1U << 0);

BSRR

νŠΉμ • output pin을 ν•œ 번의 write둜 set/resetν•  수 μžˆλ‹€.

BSRR[15:0]
β†’ SET

BSRR[31:16]
β†’ RESET

PB0:

GPIOB->BSRR = (1U << 0);   /* SET   */
GPIOB->BSRR = (1U << 16);  /* RESET */

ODR |= ...처럼 Read-Modify-Writeλ₯Ό ν•˜μ§€ μ•Šμ•„λ„ λ˜λ―€λ‘œ λ‹¨μˆœ set/resetμ—λŠ” BSRR이 μœ λ¦¬ν•˜λ‹€.


4. Practice Code

이번 μž₯λΆ€ν„° GPIO μ½”λ“œλ₯Ό driver ν˜•νƒœλ‘œ λΆ„λ¦¬ν•œλ‹€.

Inc/
└── gpio.h

Src/
β”œβ”€β”€ gpio.c
└── main.c

gpio.h

#ifndef GPIO_H__
#define GPIO_H__

#include <stdbool.h>
#include "stm32f4xx.h"

/*
 * LED control functions
 *
 * LD1 on NUCLEO-F439ZI is connected to PB0.
 */
void led_init(void);
void led_on(void);
void led_off(void);
void led_toggle(void);

/*
 * Push button functions
 *
 * B1 on NUCLEO-F439ZI is connected to PC13.
 * In this project:
 *
 * Released = LOW
 * Pressed  = HIGH
 */
void button_init(void);
bool get_btn_state(void);

#endif

5. gpio.c

#include "gpio.h"

/*
 * ============================================================
 * Pin Definitions
 * ============================================================
 */

/*
 * LD1 is connected to PB0.
 *
 * BSRR lower 16 bits are used to SET GPIO pins.
 *
 * bit0 = PB0 set
 */
#define LED_SET      (1U << 0)

/*
 * BSRR upper 16 bits are used to RESET GPIO pins.
 *
 * PB0 reset:
 *
 * pin number 0 + 16 = bit16
 */
#define LED_RESET    (1U << 16)

/*
 * ODR bit0 corresponds to PB0.
 *
 * Used when toggling the LED.
 */
#define LED_PIN      (1U << 0)

/*
 * B1 user button is connected to PC13.
 *
 * IDR bit13 represents PC13 input state.
 */
#define BTN_PIN      (1U << 13)


/*
 * ============================================================
 * LED Initialization
 * ============================================================
 */

void led_init(void)
{
    /*
     * Step 1
     * Enable GPIOB peripheral clock.
     *
     * GPIO peripheral registers cannot be used properly
     * before its clock is enabled.
     *
     * RCC_AHB1ENR_GPIOBEN = GPIOB clock enable bit.
     */
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;

    /*
     * Step 2
     * Configure PB0 as General Purpose Output.
     *
     * GPIO MODER uses 2 bits for each pin.
     *
     * PB0:
     * MODER[1:0]
     *
     * 00 = Input
     * 01 = Output
     * 10 = Alternate Function
     * 11 = Analog
     */

    /*
     * Clear PB0 mode field first.
     *
     * 0x3 = binary 11
     *
     * ~(0x3 << 0)
     * clears MODER[1:0].
     */
    GPIOB->MODER &= ~(0x3UL << (0 * 2));

    /*
     * Set MODER[1:0] = 01.
     *
     * PB0 becomes General Purpose Output.
     */
    GPIOB->MODER |= (0x1UL << (0 * 2));
}


/*
 * ============================================================
 * LED ON
 * ============================================================
 */

void led_on(void)
{
    /*
     * Set PB0 using BSRR.
     *
     * BSRR lower 16 bits:
     * writing 1 sets the corresponding GPIO output.
     *
     * PB0 β†’ bit0
     *
     * GPIOB->BSRR = 0x00000001
     *
     * Result:
     * PB0 = HIGH
     * LD1 = ON
     */
    GPIOB->BSRR = LED_SET;
}


/*
 * ============================================================
 * LED OFF
 * ============================================================
 */

void led_off(void)
{
    /*
     * Reset PB0 using BSRR.
     *
     * BSRR upper 16 bits:
     * writing 1 resets the corresponding GPIO output.
     *
     * PB0 reset β†’ bit16
     *
     * GPIOB->BSRR = 0x00010000
     *
     * Result:
     * PB0 = LOW
     * LD1 = OFF
     */
    GPIOB->BSRR = LED_RESET;
}


/*
 * ============================================================
 * LED Toggle
 * ============================================================
 */

void led_toggle(void)
{
    /*
     * XOR the current ODR bit0.
     *
     * 0 XOR 1 = 1
     * 1 XOR 1 = 0
     *
     * Therefore:
     *
     * LOW  β†’ HIGH
     * HIGH β†’ LOW
     *
     * Note:
     * ODR toggle requires Read-Modify-Write.
     * BSRR is preferred when only explicit SET/RESET is needed.
     */
    GPIOB->ODR ^= LED_PIN;
}


/*
 * ============================================================
 * Button Initialization
 * ============================================================
 */

void button_init(void)
{
    /*
     * Step 1
     * Enable GPIOC peripheral clock.
     *
     * B1 is connected to PC13.
     */
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN;

    /*
     * Step 2
     * Configure PC13 as input.
     *
     * Pin 13 uses:
     *
     * MODER[27:26]
     *
     * Input mode = 00
     *
     * Clear both bits.
     */
    GPIOC->MODER &= ~(0x3UL << (13 * 2));

    /*
     * Step 3
     * Disable internal pull-up / pull-down.
     *
     * PUPDR also uses 2 bits per pin.
     *
     * PC13:
     * PUPDR[27:26]
     *
     * 00 = No Pull
     */
    GPIOC->PUPDR &= ~(0x3UL << (13 * 2));
}


/*
 * ============================================================
 * Read Button State
 * ============================================================
 */

bool get_btn_state(void)
{
    /*
     * Read PC13 from GPIOC Input Data Register.
     *
     * BTN_PIN = (1U << 13)
     *
     * If bit13 is HIGH:
     *     GPIOC->IDR & BTN_PIN != 0
     *
     *     return true
     *
     * If bit13 is LOW:
     *     GPIOC->IDR & BTN_PIN == 0
     *
     *     return false
     *
     * NUCLEO-F439ZI B1:
     *
     * Released β†’ LOW
     * Pressed  β†’ HIGH
     */
    return ((GPIOC->IDR & BTN_PIN) != 0U);
}

6. main.c

#include <stdbool.h>
#include "gpio.h"

int main(void)
{
    /*
     * Variable used to store the current
     * PC13 button input state.
     */
    bool btn_state;

    /*
     * Initialize PB0 as output.
     *
     * PB0 β†’ LD1
     */
    led_init();

    /*
     * Initialize PC13 as input.
     *
     * PC13 β†’ B1 user button
     */
    button_init();

    while (1)
    {
        /*
         * Read current PC13 input level.
         *
         * false β†’ button released
         * true  β†’ button pressed
         */
        btn_state = get_btn_state();

        /*
         * B1 on NUCLEO-F439ZI is active-HIGH.
         *
         * Pressed:
         * PC13 = HIGH
         * β†’ turn LED on
         *
         * Released:
         * PC13 = LOW
         * β†’ turn LED off
         */
        if (btn_state)
        {
            led_on();
        }
        else
        {
            led_off();
        }
    }
}

7. Code Flow

전체 software flow:

main()
 β”‚
 β”œβ”€ led_init()
 β”‚    β”‚
 β”‚    β”œβ”€ GPIOB Clock ON
 β”‚    └─ PB0 Output
 β”‚
 β”œβ”€ button_init()
 β”‚    β”‚
 β”‚    β”œβ”€ GPIOC Clock ON
 β”‚    β”œβ”€ PC13 Input
 β”‚    └─ No Pull
 β”‚
 └─ while(1)
      β”‚
      β”œβ”€ Read GPIOC->IDR[13]
      β”‚
      β”œβ”€ Pressed?
      β”‚
      β”œβ”€ YES β†’ BSRR Set PB0
      β”‚
      └─ NO  β†’ BSRR Reset PB0

Hardware flow:

B1
 ↓
PC13
 ↓
GPIOC IDR[13]
 ↓
CPU
 ↓
if()
 ↓
GPIOB BSRR
 ↓
PB0
 ↓
LD1

8. Why Use BSRR?

λ‹€μŒ μ½”λ“œλŠ”:

GPIOB->ODR |= (1U << 0);

CPU μž…μž₯μ—μ„œλŠ” 보톡:

Read ODR
   ↓
Modify bit0
   ↓
Write ODR

κ°€ λœλ‹€.

이것을:

Read-Modify-Write

라고 ν•œλ‹€.

반면:

GPIOB->BSRR = (1U << 0);

은 μ›ν•˜λŠ” bitλ₯Ό 직접 setν•˜λŠ” write commandλ‹€.

Write BSRR
   ↓
PB0 SET

λ”°λΌμ„œ λ‹€λ₯Έ GPIO output bit에 영ν–₯을 μ£Όμ§€ μ•Šκ³  νŠΉμ • pin만 λ³€κ²½ν•˜κΈ°μ— μ ν•©ν•˜λ‹€.


9. Polling

ν˜„μž¬ main loop:

while (1)
{
    btn_state = get_btn_state();
}

λŠ” CPUκ°€ 계속 button을 ν™•μΈν•œλ‹€.

Read
 ↓
Read
 ↓
Read
 ↓
Read
 ↓
...

이λ₯Ό Polling이라고 ν•œλ‹€.

μž₯점:

Simple
Easy to debug

단점:

CPU continuously checks the peripheral

Ch14μ—μ„œλŠ” 같은 button을 EXTI Interrupt λ°©μ‹μœΌλ‘œ λ³€κ²½ν•œλ‹€.


10. Button Bounce

Mechanical switchλŠ” μ‹€μ œλ‘œ ν•œ 번 λˆŒλ €μ„ λ•Œ signal이 μ™„λ²½ν•˜κ²Œ ν•œ 번만 λ°”λ€Œμ§€ μ•Šμ„ 수 μžˆλ‹€.

Ideal

LOW ──────────┐
              └──── HIGH

μ‹€μ œ switchμ—μ„œλŠ”:

LOW ───┐ β”Œβ”€β” β”Œβ”€β”€β”€β”€ HIGH
       β””β”€β”˜ β””β”€β”˜

처럼 짧게 μ—¬λŸ¬ 번 λ³€ν•  수 μžˆλ‹€.

이λ₯Ό:

Switch Bounce

라고 ν•œλ‹€.

이번 μ‹€μŠ΅μ€ λ‹¨μˆœνžˆ:

ν˜„μž¬ button level
β†’ LED level

을 μ—°κ²°ν•˜κΈ° λ•Œλ¬Έμ— 큰 λ¬Έμ œκ°€ μ—†μ§€λ§Œ,

Button Counter
LED Toggle Once
Interrupt

같은 λ™μž‘μ—μ„œλŠ” debounce μ²˜λ¦¬κ°€ ν•„μš”ν•˜λ‹€.


11. Board Porting

μ›μ„œμ—μ„œ μ‚¬μš©ν•œ board와 ν˜„μž¬ board의 LED pin이 λ‹€λ₯΄λ‹€.

NUCLEO-F411
LD2 β†’ PA5

NUCLEO-F439ZI
LD1 β†’ PB0

λ”°λΌμ„œ λ³€κ²½:

GPIOA
β†’ GPIOB

bit5
β†’ bit0

Button은 PC13을 μ‚¬μš©ν•˜μ§€λ§Œ polarityλ₯Ό board νšŒλ‘œμ— 맞게 확인해야 ν•œλ‹€.

이번 projectμ—μ„œλŠ”:

B1 Press
β†’ PC13 HIGH

이닀.


12. Verification

Build

make clean
make

확인:

Build PASS

Board

Expected:

B1 Released
β†’ LD1 OFF

B1 Pressed
β†’ LD1 ON

Debugger

λ‹€μŒ registerλ₯Ό ν™•μΈν•œλ‹€.

RCC->AHB1ENR
GPIOB->MODER
GPIOB->ODR
GPIOC->MODER
GPIOC->IDR

Button released:

GPIOC->IDR bit13 = 0

Button pressed:

GPIOC->IDR bit13 = 1

13. Recommended Debug Test

Breakpointλ₯Ό:

btn_state = get_btn_state();

λ‹€μŒ 쀄에 κ±Έκ³  B1을 λˆŒλŸ¬λ³Έλ‹€.

Watch:

btn_state
GPIOC->IDR
GPIOB->ODR

확인:

Released

btn_state = false
IDR[13]  = 0
ODR[0]   = 0
Pressed

btn_state = true
IDR[13]  = 1
ODR[0]   = 1

μ΄λ ‡κ²Œ λ‚˜μ˜€λ©΄:

Physical Button
β†’ Input Register
β†’ Software
β†’ Output Register
β†’ Physical LED

전체 흐름을 ν™•μΈν•œ 것이닀.


14. Screenshots to Add

images/ch07_build.png
images/ch07_button_released.png
images/ch07_button_pressed.png
images/ch07_idr_debug.png

κ°€μž₯ 쒋은 ꡬ성:

Button Released / LED OFF
+
Button Pressed / LED ON
+
Debugger IDR[13]

15. Key Points

PC13
 ↓
IDR
 ↓
Software
 ↓
BSRR
 ↓
PB0
 ↓
LED
  • GPIOλ₯Ό μ‚¬μš©ν•˜κΈ° 전에 RCC clock을 enableν•œλ‹€.
  • MODERλŠ” GPIO pin ν•˜λ‚˜λ‹Ή 2bitλ₯Ό μ‚¬μš©ν•œλ‹€.
  • PB0의 mode fieldλŠ” MODER[1:0]이닀.
  • PC13의 mode fieldλŠ” MODER[27:26]이닀.
  • Input μƒνƒœλŠ” IDRμ—μ„œ μ½λŠ”λ‹€.
  • Output stateλŠ” ODR둜 확인할 수 μžˆλ‹€.
  • νŠΉμ • output의 set/resetμ—λŠ” BSRR을 μ‚¬μš©ν•  수 μžˆλ‹€.
  • NUCLEO-F439ZI의 B1은 이번 μ‹€μŠ΅μ—μ„œ active-HIGH둜 μ‚¬μš©ν•œλ‹€.
  • ν˜„μž¬ 방식은 polling이며 이후 EXTI interrupt둜 ν™•μž₯ν•œλ‹€.
⚠️ **GitHub.com Fallback** ⚠️