Chapter 14 - Hongjayeong-02/Basic_SoC_implementation GitHub Wiki
Ch07μμλ while(1) μμμ PC13 buttonμ κ³μ μ½λ Polling λ°©μμ μ¬μ©νλ€.
μ΄λ²μλ B1μ λλ₯΄λ μκ° hardware interruptκ° λ°μνλλ‘ κ΅¬μ±νλ€.
B1 Press
β
PC13 LOW β HIGH
β
EXTI13
β
Pending Request
β
NVIC
β
Cortex-M4
β
EXTI15_10_IRQHandler()
β
UART Message
+
LD1 Toggle
Board:
B1 β PC13
LD1 β PB0
μ΄λ² NUCLEO-F439ZIμ B1μ:
Released β LOW
Pressed β HIGH
μ΄λ―λ‘ Rising Edgeλ₯Ό μ¬μ©νλ€.
Ch07:
while (1)
{
if (get_btn_state())
{
led_on();
}
}CPUκ° κ³μ:
Button?
Button?
Button?
Button?
...
μ νμΈνλ€.
μ΄λ² Ch14:
CPU doing other work
β
Button Event
β
Interrupt
β
ISR
CPUκ° buttonμ κ³μ μ½μ νμκ° μλ€.
μ΄λ² μ€μ΅μμ νμν ꡬμ±:
GPIO
β
SYSCFG
β
EXTI
β
NVIC
β
Vector Table
β
ISR
κ° μν :
| Block | μν |
|---|---|
| GPIO | PC13 pin input |
| SYSCFG | PC13μ EXTI13μ μ°κ²° |
| EXTI | edge κ²μΆ λ° pending μμ± |
| NVIC | CPU interrupt enable / priority κ΄λ¦¬ |
| Vector Table | IRQμ ISR μ£Όμ μ°κ²° |
| ISR | μ€μ interrupt μ²λ¦¬ μ½λ |
μ΄λ² μ€μ :
IMR13 = 1
β EXTI13 interrupt unmask
RTSR13 = 1
β Rising edge detect
FTSR13 = 0
β Falling edge unused
Project:
Inc/
βββ gpio.h
βββ gpio_exti.h
βββ uart.h
Src/
βββ gpio.c
βββ gpio_exti.c
βββ uart.c
βββ main.c
Ch07 gpio.cμ Ch10 uart.cλ₯Ό μ¬μ¬μ©νλ€.
#ifndef GPIO_EXTI_H__
#define GPIO_EXTI_H__
#include <stdint.h>
#include "stm32f4xx.h"
/*
* EXTI line 13 mask.
*
* PC13 is connected to:
*
* EXTI13
*/
#define LINE13 (1U << 13)
/*
* Configure PC13 as an external interrupt source.
*
* Board:
*
* B1 β PC13
*
* Trigger:
*
* Rising Edge
*/
void pc13_exti_init(void);
#endif#include "gpio_exti.h"
/*
* ============================================================
* RCC Clock Enable Bits
* ============================================================
*/
/*
* RCC AHB1ENR bit2:
*
* GPIOC clock enable.
*
* B1 is connected to PC13.
*/
#define GPIOCEN (1U << 2)
/*
* RCC APB2ENR bit14:
*
* SYSCFG peripheral clock enable.
*
* SYSCFG is required to connect a GPIO port
* to an EXTI line.
*/
#define SYSCFGEN (1U << 14)
/*
* ============================================================
* PC13 EXTI Initialization
* ============================================================
*/
void pc13_exti_init(void)
{
/*
* --------------------------------------------------------
* Step 1. Disable Global Interrupts Temporarily
* --------------------------------------------------------
*
* Several registers must be configured in sequence.
*
* We do not want an interrupt to occur while
* configuration is only partially complete.
*/
__disable_irq();
/*
* --------------------------------------------------------
* Step 2. Enable GPIOC Clock
* --------------------------------------------------------
*
* PC13 belongs to GPIOC.
*/
RCC->AHB1ENR |= GPIOCEN;
/*
* --------------------------------------------------------
* Step 3. Configure PC13 as Input
* --------------------------------------------------------
*
* MODER uses 2 bits per pin.
*
* PC13:
*
* MODER[27:26]
*
* Input mode:
*
* 00
*/
GPIOC->MODER &= ~(1U << 26);
GPIOC->MODER &= ~(1U << 27);
/*
* --------------------------------------------------------
* Step 4. Enable SYSCFG Clock
* --------------------------------------------------------
*
* SYSCFG is on APB2.
*/
RCC->APB2ENR |= SYSCFGEN;
/*
* --------------------------------------------------------
* Step 5. Connect PC13 β EXTI13
* --------------------------------------------------------
*
* EXTI12 ~ EXTI15 are controlled by:
*
* EXTICR[3]
*
* EXTI13 field selects which GPIO port
* supplies line13.
*
* Port encoding:
*
* A = 0000
* B = 0001
* C = 0010
*
* For EXTI13:
*
* EXTICR3[7:4]
*
* Port C = 0010
*
* Original practice code:
*
* SYSCFG->EXTICR[3] |= (1U << 5);
*
* Reset value is 0000, so setting bit5 produces:
*
* 0010 = Port C
*/
SYSCFG->EXTICR[3] |= (1U << 5);
/*
* --------------------------------------------------------
* Step 6. Unmask EXTI13
* --------------------------------------------------------
*
* IMR:
*
* 0 β interrupt masked
* 1 β interrupt allowed
*/
EXTI->IMR |= LINE13;
/*
* --------------------------------------------------------
* Step 7. Select Rising Edge
* --------------------------------------------------------
*
* NUCLEO-F439ZI B1:
*
* Released = LOW
* Pressed = HIGH
*
* Button press therefore produces:
*
* LOW β HIGH
*
* so Rising Trigger Selection Register
* is used.
*/
EXTI->RTSR |= LINE13;
/*
* Optional defensive configuration:
*
* Make sure falling-edge trigger is disabled.
*/
EXTI->FTSR &= ~LINE13;
/*
* --------------------------------------------------------
* Step 8. Enable IRQ in NVIC
* --------------------------------------------------------
*
* EXTI lines 10 ~ 15 share:
*
* EXTI15_10_IRQn
*/
NVIC_EnableIRQ(EXTI15_10_IRQn);
/*
* --------------------------------------------------------
* Step 9. Enable Global Interrupts
* --------------------------------------------------------
*
* Initialization is complete.
*/
__enable_irq();
}#include "gpio.h"
#include "gpio_exti.h"
#include "uart.h"
/*
* ============================================================
* EXTI Callback
* ============================================================
*
* This function contains the application action
* performed when B1 is pressed.
*/
static void exti_callback(void)
{
/*
* Send a message through USART3.
*
* Expected terminal output:
*
* BTN Pressed...
*/
uart_write_string("BTN Pressed...\r\n");
/*
* Toggle LD1 connected to PB0.
*
* Every valid button interrupt changes
* the LED state.
*/
led_toggle();
}
int main(void)
{
/*
* --------------------------------------------------------
* Step 1. Initialize USART3
* --------------------------------------------------------
*
* Used to print the button event.
*/
uart_init();
/*
* --------------------------------------------------------
* Step 2. Initialize LD1
* --------------------------------------------------------
*
* PB0 β Output
*/
led_init();
/*
* --------------------------------------------------------
* Step 3. Configure PC13 EXTI
* --------------------------------------------------------
*
* B1 press:
*
* PC13 Rising Edge
* β
* EXTI13
* β
* NVIC
* β
* ISR
*/
pc13_exti_init();
while (1)
{
/*
* No button polling.
*
* The CPU does NOT repeatedly execute:
*
* get_btn_state()
*
* Button processing happens only when
* an interrupt occurs.
*
* A later low-power version could use:
*
* __WFI();
*
* here to sleep until an interrupt arrives.
*/
}
}
/*
* ============================================================
* EXTI10 ~ EXTI15 Shared Interrupt Handler
* ============================================================
*/
void EXTI15_10_IRQHandler(void)
{
/*
* --------------------------------------------------------
* Step 1. Identify the Interrupt Source
* --------------------------------------------------------
*
* EXTI10 ~ EXTI15 share one ISR.
*
* Therefore check whether EXTI13 is actually pending.
*/
if ((EXTI->PR & LINE13) != 0U)
{
/*
* ----------------------------------------------------
* Step 2. Clear Pending Flag
* ----------------------------------------------------
*
* EXTI PR is W1C:
*
* Write 1 β clear pending bit
* Write 0 β no effect
*
* IMPORTANT:
*
* Use direct assignment:
*
* EXTI->PR = LINE13;
*
* instead of:
*
* EXTI->PR |= LINE13;
*
* because '|=' performs a Read-Modify-Write.
*
* Since PR is W1C, reading multiple pending bits
* and writing them back as 1 could accidentally
* clear other pending EXTI lines.
*/
EXTI->PR = LINE13;
/*
* ----------------------------------------------------
* Step 3. Execute Application Callback
* ----------------------------------------------------
*
* Pending is cleared BEFORE the callback.
*
* If another edge occurs later,
* hardware can set PR again.
*/
exti_callback();
}
}μλ³Έ μ€μ΅ μ½λλ:
EXTI->PR |= LINE13;μ΄μλ€.
κΈ°λ₯μ μΌλ‘ line13 νλλ§ pendingμΈ λ¨μ μ€μ΅μμλ μ μ λμνκ³ μ€μ board testλ PASSνλ€.
νμ§λ§ register νΉμ±μ κ³ λ €νλ©΄ μ΅μ’ μ½λλ:
EXTI->PR = LINE13;μ΄ λ μ μ νλ€.
μλνλ©΄ PRμ:
W1C
=
Write 1 to Clear
registerμ΄κΈ° λλ¬Έμ΄λ€.
|=λ μ€μ λ‘:
Read PR
β
OR LINE13
β
Write PR
λ₯Ό μννλ€.
λ§μ½ μ½μ μκ°:
PR[13] = 1
PR[12] = 1
μ΄μλ€λ©΄:
Read
β bit13 = 1
β bit12 = 1
Write back
β bit13 = 1 β clear
β bit12 = 1 β clear
κ° λμ΄ μ²λ¦¬νμ§ μμ EXTI12κΉμ§ clearλ μνμ΄ μλ€.
κ·Έλμ W1C registerμμλ 보ν΅:
EXTI->PR = LINE13;μ²λΌ clearνλ €λ bitμλ§ 1μ μ§μ writeνλ€.
B1 Released
PC13 = 0
β User presses B1
PC13 = 1
β
LOW β HIGH
β
EXTI13 Rising Edge Detect
β
PR[13] = 1
β
EXTI15_10 IRQ Request
β
NVIC
β
Cortex-M4 pauses normal execution
β
Vector Table Lookup
β
EXTI15_10_IRQHandler()
β
Check PR[13]
β
Clear PR[13]
β
exti_callback()
βββ UART Print
βββ LED Toggle
β
Return From Interrupt
β
main() resumes
NVIC:
Nested
Vectored
Interrupt
Controller
μν :
Interrupt Enable
Interrupt Disable
Priority
Pending
Nested Interrupt
μ΄λ² μ½λλ:
NVIC_EnableIRQ(EXTI15_10_IRQn);
μ μ¬μ©νλ€.
μ¦:
EXTI peripheral
μμ interrupt requestκ° λ§λ€μ΄μ Έλ NVICμμ ν΄λΉ IRQκ° enableλμ΄ μμ΄μΌ CPUκΉμ§ μ λ¬λλ€.
IRQκ° λ€μ΄μ€λ©΄ Cortex-M4λ ν¨μ μ΄λ¦μ κ²μνλ κ²μ΄ μλλ€.
Startup fileμ μλ Vector Tableμ ISR addressκ° μ μ₯λμ΄ μλ€.
κ°λ μ μΌλ‘:
Vector Table
...
EXTI15_10_IRQn
β
EXTI15_10_IRQHandler address
...
λ°λΌμ μ°λ¦¬κ°:
void EXTI15_10_IRQHandler(void)λ₯Ό ꡬννλ©΄ startupμ weak handler λμ μ΄ ν¨μκ° μ¬μ©λλ€.
EXTI10~15λ νλμ IRQλ₯Ό 곡μ νλ€.
EXTI10 ββ
EXTI11 ββ€
EXTI12 ββ€
EXTI13 ββΌββ EXTI15_10_IRQHandler
EXTI14 ββ€
EXTI15 ββ
λ°λΌμ ISR λ΄λΆμμ:
if ((EXTI->PR & LINE13) != 0U)μ²λΌ μ€μ pending sourceλ₯Ό νμΈν΄μΌ νλ€.
μ¬λ¬ lineμ μ¬μ©ν κ²½μ°:
if (EXTI->PR & LINE10)
{
...
}
if (EXTI->PR & LINE13)
{
...
}
if (EXTI->PR & LINE15)
{
...
}μ²λΌ ꡬλΆν μ μλ€.
μ΄λ² board:
Not Pressed
PC13 = LOW
Pressed
PC13 = HIGH
λ°λΌμ button press:
0 β 1
μ΄λ―λ‘:
EXTI->RTSR |= LINE13;μ μ¬μ©νλ€.
μμμ²λΌ active-LOW buttonμ΄λ©΄:
1 β 0
μ΄λ―λ‘ FTSRμ μ¬μ©νμ μ μλ€.
μ¦ interrupt edgeλ board νλ‘ polarityμ λ°λΌ κ²°μ ν΄μΌ νλ€.
ISR:
if (EXTI->PR & LINE13)
{
EXTI->PR = LINE13;
exti_callback();
}μ²λΌ pending flagλ₯Ό callback μ μ μ§μ΄λ€.
μ΄μ :
IRQ λ°μ
β
PR = 1
β
ISR μ§μ
β
PR clear
β
Application processing
PRμ μ μ§μ°λ©΄ ISR μ’ λ£ νμλ:
Pending = 1
μ΄ λ¨μ μ μμ΄ λ€μ interruptκ° λ°μν μ μλ€.
μ΄λ² μ€μ΅μμλ μ΄ν΄λ₯Ό μν΄ ISR μμμ:
UART Output
LED Toggle
κΉμ§ μννλ€.
νμ§λ§ μΌλ°μ μΈ embedded designμμλ ISRμ μ§§κ² μ μ§νλ κ²μ΄ μ’λ€.
μ:
volatile uint8_t g_btn_pressed;
void EXTI15_10_IRQHandler(void)
{
if (EXTI->PR & LINE13)
{
EXTI->PR = LINE13;
g_btn_pressed = 1U;
}
}main:
while (1)
{
if (g_btn_pressed)
{
g_btn_pressed = 0U;
uart_write_string("BTN Pressed...\r\n");
led_toggle();
}
}μ΄ λ°©μμ:
ISR
β Event capture only
main
β Heavy processing
μΌλ‘ μν μ λΆλ¦¬νλ€.
μ΄λ² Ch14 μλ³Έ μ€μ΅μ interrupt λμμ μ§μ νμΈνκΈ° μν λ¨μ ꡬ쑰μ΄λ―λ‘ callbackμ ISRμμ λ°λ‘ μννλ€.
EXTIμμλ mechanical switch bounceκ° λ λμ λ μ μλ€.
ν λ² λλ₯Έλ€κ³ ν΄λ μ€μ waveform:
LOW
β
HIGH
β
LOW
β
HIGH
β
LOW
β
HIGH
μ²λΌ μ¬λ¬ edgeκ° μκΈΈ μ μλ€.
λ°λΌμ μ€μ λ‘λ ν λ² λλ λλ°:
BTN Pressed...
BTN Pressed...
BTN Pressed...
κ° μ¬λ¬ λ² λ°μν κ°λ₯μ±μ΄ μλ€.
μ΄λ₯Ό λ§μΌλ €λ©΄:
Software Debounce
Timer Debounce
State Machine
λ±μ μΆκ°ν μ μλ€.
μ΄λ² chapterμ ν΅μ¬μ EXTI + NVIC κ΅¬μ‘°λΌ debounceλ μΆκ°νμ§ μλλ€.
κΈ°μ‘΄ μ€μ΅:
Build : PASS
Download : PASS
Run : PASS
μ€μ board:
B1 Press
β
LD1 Toggle
β
UART:
BTN Pressed...
νμΈ μλ£.
Program size:
text = 1272 bytes
data = 0 bytes
bss = 2560 bytes
κ²½κ³ μμ΄ buildλλ€.
make clean
makeExpected:
Build PASS
text = 1272
data = 0
bss = 2560
Debugger:
GPIOC->MODER
Expected:
PC13 MODER[27:26]
=
00
νμΈ:
SYSCFG->EXTICR[3]
EXTI13 field:
0010
=
Port C
μ¦:
EXTI13 β PC13
νμΈ:
EXTI->IMR
EXTI->RTSR
EXTI->PR
Expected:
IMR[13] = 1
RTSR[13] = 1
button press μ§ν:
PR[13] = 1
ISR μ²λ¦¬ ν:
PR[13] = 0
Breakpoint:
void EXTI15_10_IRQHandler(void)B1μ λλ₯΄λ©΄ CPUκ° ν΄λΉ ISRλ‘ μ§μ νλμ§ νμΈνλ€.
Watch:
EXTI->PR
GPIOC->IDR
GPIOB->ODR
Expected:
BTN Pressed...
buttonμ λλ₯Ό λλ§λ€ μΆλ ₯λλ€.
Expected:
Press #1
β LD1 ON
Press #2
β LD1 OFF
Press #3
β LD1 ON
images/ch14_build.png
images/ch14_exticr.png
images/ch14_exti_registers.png
images/ch14_isr_breakpoint.png
images/ch14_uart.png
images/ch14_button_led.gif
κ°μ₯ μ’μ μ¦κ±° ꡬμ±:
B1 Press
+
ISR Breakpoint
+
PR[13]
+
UART "BTN Pressed..."
+
LD1 Toggle
PC13
β
SYSCFG
β
EXTI13
β
Rising Edge
β
PR[13]
β
NVIC
β
Vector Table
β
EXTI15_10_IRQHandler
β
LED + UART
- Ch07 pollingμ EXTI interrupt κ΅¬μ‘°λ‘ λ³κ²½νλ€.
- PC13μ EXTI13μ μ¬μ©νλ€.
-
SYSCFG->EXTICR[3]μμ EXTI13μ Port Cμ μ°κ²°νλ€. - F439ZI B1μ active-HIGHμ΄λ―λ‘ Rising Edgeλ₯Ό μ¬μ©νλ€.
-
IMRμμ EXTI13 interruptλ₯Ό unmaskνλ€. -
NVIC_EnableIRQ()λ‘ CPU interrupt inputμ enableνλ€. - EXTI10~15λ
EXTI15_10_IRQHandler()λ₯Ό 곡μ νλ€. - ISRμμ
PRμ νμΈν΄ μ€μ interrupt sourceλ₯Ό ꡬλΆνλ€. -
PRμ W1C registerμ΄λ―λ‘EXTI->PR = LINE13ννλ‘ clearνλ κ²μ΄ μμ νλ€. - μ€μ boardμμ B1 β LD1 toggle + UART outputκΉμ§ κ²μ¦ μλ£νλ€.