Chapter 10 - Hongjayeong-02/Basic_SoC_implementation GitHub Wiki
NUCLEO-F439ZIμ USART3λ₯Ό register levelμμ μ€μ νκ³ , ST-LINK Virtual COM Portλ₯Ό ν΅ν΄ PC terminalλ‘ λ¬Έμμ΄μ μ μ‘νλ€.
μ΄λ² μ€μ΅:
STM32F439ZI
β
β USART3
β
βββ PD8 (TX)
β
ST-LINK
β
USB VCP
β
PC
β
"Hello from NUCLEO-F439ZI!"μ€μ :
USART : USART3
TX : PD8
RX : PD9
AF : AF7
Baud : 115200 bps
Clock : 16 MHz
Format : 115200-8-N-1μμμ NUCLEO-F411κ³Ό νμ¬ μ¬μ©νλ NUCLEO-F439ZIλ Virtual COM Port μ°κ²°μ΄ λ€λ₯΄λ€.
| NUCLEO-F411 | NUCLEO-F439ZI -- | -- | -- USART | USART2 | USART3 TX | PA2 | PD8 RX | PA3 | PD9 AF | AF7 | AF7 APB1 Enable | USART2EN | USART3EN AFR | AFR[0] | AFR[1]λ°λΌμ μμ μ½λλ₯Ό κ·Έλλ‘ μ¬μ©νλ©΄ compileμ λλλΌλ VCPμ μΆλ ₯μ΄ λνλμ§ μμ μ μλ€.
μ΄λ² boardμμλ:
USART3 TX β PD8
USART3 RX β PD9λ₯Ό μ¬μ©νλ€.
μ΄λ² μ½λμμ μ§μ μ¬μ©νλ register:
RCC->AHB1ENR
RCC->APB1ENR
GPIOD->MODER
GPIOD->AFR[1]
USART3->BRR
USART3->CR1
USART3->SR
USART3->DR
USART μ£Όμ bit:
CR1 bit3 TE
β Transmitter Enable
CR1 bit13 UE
β USART Enable
SR bit7 TXE
β Transmit Data Register Empty
Peripheral clock:
APB1 Clock = 16 MHzTarget:
Baud Rate = 115200κΈ°μ‘΄ μ€μ΅ μ½λ:
BRR = (periph_clk + baudrate / 2) / baudrate;λ°λΌμ:
16,000,000 + 57,600
βββββββββββββββββββ
115,200
β 139
μ¦:
USART3->BRR β 139λ₯Ό μ¬μ©νλ€.
baudrate / 2λ₯Ό λ¨Όμ λνλ κ²μ μ μ λλμ
μμ λ°μ¬λ¦Ό ν¨κ³Όλ₯Ό μ£ΌκΈ° μν κ²μ΄λ€.
Project:
Inc/
βββ gpio.h
βββ systick.h
βββ uart.h
Src/
βββ gpio.c
βββ systick.c
βββ uart.c
βββ main.c
gpio.cμ systick.cλ μ chapterμμ λ§λ driverλ₯Ό μ¬μ¬μ©νλ€.
#ifndef UART_H
#define UART_H
#include "stm32f4xx.h"
/*
- Initialize USART3.
-
- Board:
-
NUCLEO-F439ZI
-
- Pin:
-
PD8 β USART3_TX
-
PD9 β USART3_RX
-
- Alternate Function:
-
AF7
-
- Baud:
-
115200 bps
*/
void uart_init(void);
/*
- Send a null-terminated string through USART3.
-
- Example:
-
- uart_write_string("Hello\r\n");
*/
void uart_write_string(const char *s);
/*
- printf redirection hook.
-
- This function is useful when the project is built
- in an environment where newlib/newlib-nano is linked.
-
- The standalone bare-metal Make build does not depend
- on printf, so uart_write_string() is used directly.
*/
int __io_putchar(int ch);
#endif
#include <stdint.h>
#include "uart.h"
/*
- ============================================================
- RCC Clock Enable Bits
- ============================================================
*/
/*
- RCC AHB1ENR bit3:
-
- Enable GPIOD peripheral clock.
-
- PD8 / PD9 cannot be configured until
- the GPIOD clock is enabled.
*/
#define GPIODEN (1U << 3)
/*
- RCC APB1ENR bit18:
-
- Enable USART3 peripheral clock.
*/
#define USART3EN (1U << 18)
/*
- ============================================================
- UART Clock / Baud Configuration
- ============================================================
*/
/*
- Target communication speed.
*/
#define DBG_UART_BAUDRATE 115200U
/*
- This Bare-Metal project does not configure the PLL.
-
- System clock:
-
-
HSI = 16 MHz
*/
#define SYS_FREQ 16000000U
/*
- USART3 is connected to APB1.
-
- In this project APB1 clock is also 16 MHz.
*/
#define APB1_CLK SYS_FREQ
/*
- ============================================================
- USART Register Bits
- ============================================================
*/
/*
- USART CR1 bit3:
-
- TE = Transmitter Enable
-
- 0 β transmitter disabled
- 1 β transmitter enabled
*/
#define CR1_TE (1U << 3)
/*
- USART CR1 bit13:
-
- UE = USART Enable
-
- 0 β USART disabled
- 1 β USART enabled
*/
#define CR1_UE (1U << 13)
/*
- USART SR bit7:
-
- TXE = Transmit Data Register Empty
-
- 0 β DR is not ready for new data
- 1 β next byte may be written to DR
*/
#define SR_TXE (1U << 7)
/*
- ============================================================
- Private Function Prototypes
- ============================================================
*/
/*
- Send one byte.
-
- This is kept static because it is used only
- inside uart.c.
*/
static void uart_write(int ch);
/*
- Calculate BRR value from peripheral clock
- and target baud rate.
*/
static uint16_t compute_uart_bd(uint32_t periph_clk,
uint32_t baudrate);
/*
- Program the calculated baud rate into USART3 BRR.
*/
static void uart_set_baudrate(uint32_t periph_clk,
uint32_t baudrate);
/*
- ============================================================
- printf Redirection
- ============================================================
*/
int __io_putchar(int ch)
{
/*
* Redirect one character to USART3.
*/
uart_write(ch);
/*
* Standard putchar-style function returns
* the transmitted character.
*/
return ch;
}
/*
- ============================================================
- USART3 Initialization
- ============================================================
*/
void uart_init(void)
{
/*
* --------------------------------------------------------
* Step 1. Enable GPIOD Clock
* --------------------------------------------------------
*
* USART3 VCP pins:
*
* PD8 β TX
* PD9 β RX
*
* Before configuring these pins,
* GPIOD must receive an AHB1 clock.
*/
RCC->AHB1ENR |= GPIODEN;
/*
* --------------------------------------------------------
* Step 2. Configure PD8 as Alternate Function
* --------------------------------------------------------
*
* GPIO MODER uses 2 bits per pin.
*
* PD8:
*
* MODER<span>[17:16]</span>
*
* Mode:
*
* 00 β Input
* 01 β Output
* 10 β Alternate Function
* 11 β Analog
*
* First clear the field.
*/
GPIOD->MODER &= ~(0x3UL << (8 * 2));
/*
* Then write 10b.
*
* PD8 becomes Alternate Function mode.
*/
GPIOD->MODER |= (0x2UL << (8 * 2));
/*
* --------------------------------------------------------
* Step 3. Configure PD9 as Alternate Function
* --------------------------------------------------------
*
* PD9:
*
* MODER<span>[19:18]</span>
*/
GPIOD->MODER &= ~(0x3UL << (9 * 2));
GPIOD->MODER |= (0x2UL << (9 * 2));
/*
* --------------------------------------------------------
* Step 4. Select AF7 for PD8
* --------------------------------------------------------
*
* AFR register:
*
* AFR<span>[0]</span> β Pin 0 ~ 7
* AFR<span>[1]</span> β Pin 8 ~ 15
*
* PD8 is therefore controlled by AFR<span>[1]</span>.
*
* Each pin uses 4 bits.
*
* PD8 position:
*
* (8 - 8) Γ 4
* = bit <span>[3:0]</span>
*
* Clear existing AF field first.
*/
GPIOD->AFR<span>[1]</span> &= ~(0xFUL << ((8 - 8) * 4));
/*
* AF7 = 0111b
*
* PD8 β USART3_TX
*/
GPIOD->AFR<span>[1]</span> |= (0x7UL << ((8 - 8) * 4));
/*
* --------------------------------------------------------
* Step 5. Select AF7 for PD9
* --------------------------------------------------------
*
* PD9 position:
*
* (9 - 8) Γ 4
* = bit <span>[7:4]</span>
*/
GPIOD->AFR<span>[1]</span> &= ~(0xFUL << ((9 - 8) * 4));
/*
* PD9 β AF7 β USART3_RX
*/
GPIOD->AFR<span>[1]</span> |= (0x7UL << ((9 - 8) * 4));
/*
* --------------------------------------------------------
* Step 6. Enable USART3 Clock
* --------------------------------------------------------
*
* USART3 belongs to APB1.
*
* RCC APB1ENR bit18:
*
* USART3EN = 1
*/
RCC->APB1ENR |= USART3EN;
/*
* --------------------------------------------------------
* Step 7. Configure Baud Rate
* --------------------------------------------------------
*
* Peripheral clock:
*
* 16 MHz
*
* Baud:
*
* 115200 bps
*
* Calculated BRR:
*
* approximately 139
*/
uart_set_baudrate(APB1_CLK,
DBG_UART_BAUDRATE);
/*
* --------------------------------------------------------
* Step 8. Enable Transmitter
* --------------------------------------------------------
*
* This chapter transmits only.
*
* TE = 1
*
* Note:
* PD9 is configured for USART3_RX,
* but RE(receiver enable) is not set here.
*
* Therefore actual communication in this exercise
* is board β PC TX only.
*/
USART3->CR1 = CR1_TE;
/*
* --------------------------------------------------------
* Step 9. Enable USART3
* --------------------------------------------------------
*
* UE = 1
*
* USART3 is now operational.
*/
USART3->CR1 |= CR1_UE;
}
/*
- ============================================================
- Send One Character
- ============================================================
*/
static void uart_write(int ch)
{
/*
* --------------------------------------------------------
* Step 1. Wait Until DR Can Accept New Data
* --------------------------------------------------------
*
* TXE:
*
* 0 β transmit data register is not ready
* 1 β transmit data register is empty
*
* CPU continuously checks TXE.
*
* This is polling / busy waiting.
/
while (!(USART3->SR & SR_TXE))
{
/
* Wait here until TXE becomes 1.
*/
}
/*
* --------------------------------------------------------
* Step 2. Write Data
* --------------------------------------------------------
*
* Only the lower 8 bits are transmitted.
*/
USART3->DR = (ch & 0xFF);
}
/*
- ============================================================
- Send String
- ============================================================
*/
void uart_write_string(const char s)
{
/
* C string:
*
* H e l l o \0
*
* Continue until null terminator '\0'.
*/
while (s)
{
/
* Send current character.
*/
uart_write((int)*s);
/*
* Move pointer to next character.
*/
s++;
}
}
/*
- ============================================================
- Baud Rate Calculation
- ============================================================
*/
static uint16_t compute_uart_bd(uint32_t periph_clk,
uint32_t baudrate)
{
/*
* Integer arithmetic would normally truncate
* the fractional part.
*
* Adding baudrate / 2 before division gives
* a rounding effect.
*
* Example:
*
* periph_clk = 16,000,000
* baudrate = 115,200
*
* BRR =
*
* (16,000,000 + 57,600)
* --------------------
* 115,200
*
* β 139
*/
return (uint16_t)
((periph_clk + (baudrate / 2U))
/ baudrate);
}
/*
- ============================================================
- Program Baud Rate Register
- ============================================================
*/
static void uart_set_baudrate(uint32_t periph_clk,
uint32_t baudrate)
{
/*
* Store the calculated divider into BRR.
*/
USART3->BRR =
compute_uart_bd(periph_clk, baudrate);
}
#include "gpio.h"
#include "systick.h"
#include "uart.h"
int main(void)
{
/*
* --------------------------------------------------------
* Step 1. Initialize LD1
* --------------------------------------------------------
*
* NUCLEO-F439ZI:
*
* LD1 β PB0
*/
led_init();
/*
* --------------------------------------------------------
* Step 2. Initialize USART3
* --------------------------------------------------------
*
* Configuration:
*
* USART3
* PD8 β TX
* PD9 β RX pin mapping
* AF7
* 115200 bps
*/
uart_init();
while (1)
{
/*
* ----------------------------------------------------
* Step 3. Send String to PC
* ----------------------------------------------------
*
* uart_write_string() sends the string
* one character at a time.
*
* '\r\n':
*
* \r β Carriage Return
* \n β Line Feed
*
* Terminal output moves to the next line.
*/
uart_write_string(
"Hello from NUCLEO-F439ZI! "
"(USART3, 115200bps)\r\n"
);
/*
* ----------------------------------------------------
* Step 4. Toggle LD1
* ----------------------------------------------------
*
* Provides a visual indication that
* the main loop is running.
*/
led_toggle();
/*
* ----------------------------------------------------
* Step 5. Wait 1 Second
* ----------------------------------------------------
*
* Ch08 SysTick driver is reused.
*/
systick_msec_delay(1000);
}
}
μ 체 μ΄κΈ°ν μμ:
uart_init()
β
βββ GPIOD Clock Enable
β
βββ PD8 β Alternate Function
β
βββ PD9 β Alternate Function
β
βββ PD8 β AF7
β
βββ PD9 β AF7
β
βββ USART3 Clock Enable
β
βββ BRR = 139
β
βββ TE = 1
β
βββ UE = 1μ΄ν:
USART3 Readyμνκ° λλ€.
GPIO pin νλλ GPIO κΈ°λ₯λ§ μννλ κ²μ΄ μλλ€.
μλ₯Ό λ€μ΄ PD8μ μ€μ μ λ°λΌ μ¬λ¬ peripheral functionμΌλ‘ μ°κ²°λ μ μλ€.
PD8
β
βββ GPIO
βββ USART
βββ Timer
βββ Other PeripheralMODER=10μ:
μ΄ pinμ GPIO outputμΌλ‘ μ§μ μ°μ§ μκ³
Peripheralμκ² λκΈ°κ² λ€λ μλ―Έλ€.
κ·Έ ν:
GPIOD->AFR[1] = ...μμ μ΄λ€ peripheral functionμ μ°κ²°ν μ§ μ ννλ€.
μ΄λ² μ€μ :
PD8
β
Alternate Function Mode
β
AF7
β
USART3_TXAlternate Function registerλ λ κ°λ€.
AFR[0]
β Pin 0 ~ 7
AFR[1]
β Pin 8 ~ 15
PD8κ³Ό PD9λ λͺ¨λ pin numberκ° 8 μ΄μμ΄λ€.
λ°λΌμ:
GPIOD->AFR[1]μ μ¬μ©νλ€.
PD8 field:
AFR[1][3:0]PD9 field:
AFR[1][7:4]κ·Έλμ μ½λκ°:
((8 - 8) * 4)
((9 - 8) * 4)ννκ° λλ€.
λ¬Έμ 'A'λ₯Ό 보λΈλ€κ³ κ°μ νλ€.
ASCII:
'A'
0x41
νΈμΆ:
uart_write('A');νλ¦:
CPU
β
TXE νμΈ
β
TXE = 1
β
USART3->DR = 0x41
β
USART Hardware
β
Serial Frame μμ±
β
PD8 TX
β
ST-LINK VCP
β
PC Terminal
β
Aν΅μ¬:
while (!(USART3->SR & SR_TXE))
{
}CPUκ° κ³μ:
TXE?
β
0 β κΈ°λ€λ¦Ό
β
0 β κΈ°λ€λ¦Ό
β
1 β λ€μ data writeλ₯Ό μννλ€.
μ¬κΈ°μ TXEμ μ νν μλ―Έλ:
Transmit Data Register Emptyμ΄λ€.
μ¦:
λ€μ byteλ₯Ό DRμ μΈ μ μλ€λ λ»μ΄λ€.
TXEμ **μ 체 serial frame μ μ‘ μλ£λ₯Ό μλ―Ένλ TC**λ ꡬλΆν΄μΌ νλ€.
μ΄λ² λ¬Έμμ΄ μ°μ μ‘μ μμλ:
TXEλ₯Ό μ΄μ©νλ©΄ λ€μ characterλ₯Ό μ°μν΄μ 곡κΈν μ μλ€.
uart_write_string("ABC");memory:
'A' 'B' 'C' '\0'
β
s첫 λ²μ§Έ loop:
*s = 'A'
β uart_write('A')
β s++λ λ²μ§Έ:
*s = 'B'
β uart_write('B')
β s++μΈ λ²μ§Έ:
*s = 'C'
β uart_write('C')
β s++λ§μ§λ§:
*s = '\0'μ΄λ©΄:
while (*s)μ‘°κ±΄μ΄ falseκ° λμ΄ μ’ λ£λλ€.
νμ¬ bare-metal Make buildλ νμ€ library μμ‘΄μ±μ μ΅μννλ€.
λ°λΌμ λ¨μ μΆλ ₯μλ:
uart_write_string()μ μ§μ μ¬μ©νλ€.
μ₯μ :
μμ μ½λ
λͺ
νν μ€ν νλ¦
Library dependency μ΅μν
UART hardware λμ μ§μ νμΈ__io_putchar()λ μ΄ν newlib/newlib-nanoκ° μ°κ²°λ νκ²½μμ printf() redirectionμ νμ©ν μ μλλ‘ λ¨κ²¨λ ν¨μλ€.
PD9κΉμ§ AF7λ‘ μ€μ νμ§λ§ νμ¬ CR1μλ:
TEλ§ enableνλ€.
μ¦:
Transmitter Enable
= YES
Receiver Enable
= NO
μ΄λ€.
λ°λΌμ νμ¬ κΈ°λ₯:
Board
β
PCλ¨λ°©ν₯μ΄λ€.
RXκΉμ§ μ¬μ©νλ €λ©΄ μ΄ν:
CR1.RE
SR.RXNE
DR Readλ±μ΄ μΆκ°λ‘ νμνλ€.
main()
β
βββ led_init()
β
βββ uart_init()
β
βββ while(1)
β
βββ uart_write_string()
β β
β βββ TXE wait
β βββ DR write
β βββ TXE wait
β βββ DR write
β βββ ...
β
βββ LED toggle
β
βββ SysTick 1000 ms
β
βββ RepeatCPU
β
β write
βΌ
USART3->DR
β
βΌ
USART3 Transmitter
β
βΌ
PD8 / AF7
β
βΌ
ST-LINK VCP
β
βΌ
USB
β
βΌ
PC TerminalκΈ°μ‘΄ μ€μ΅ κ²°κ³Ό:
text = 1168 bytes
data = 0 bytes
bss = 2560 bytesκ²½κ³ μμ΄ buildλμκ³ , μ€μ NUCLEO-F439ZIμ download/runνμ¬ VCP 115200-8-N-1μμ 1μ΄λ§λ€ λ¬Έμμ΄ μΆλ ₯κ³Ό LD1 toggleμ νμΈνλ€.
make clean
makeExpected:
Build PASS
text = 1168
data = 0
bss = 2560
νμΈ:
GPIOD->MODER
GPIOD->AFR[1]Expected:
PD8 MODER = 10
PD9 MODER = 10
PD8 AF = 7
PD9 AF = 7
νμΈ:
USART3->BRR
USART3->CR1
USART3->SRExpected:
BRR β 139
CR1.TE = 1
CR1.UE = 1
TX λμ μ€:
SR.TXEλ³νλ₯Ό νμΈν μ μλ€.
Terminal configuration:
Baud Rate : 115200
Data Bits : 8
Parity : None
Stop Bits : 1
Flow Ctrl : Noneμ¦:
115200-8-N-1Expected output:
Hello from NUCLEO-F439ZI! (USART3, 115200bps)
Hello from NUCLEO-F439ZI! (USART3, 115200bps)
Hello from NUCLEO-F439ZI! (USART3, 115200bps)
...μ½ 1μ΄ κ°κ²©μΌλ‘ μΆλ ₯λλ€.
κ°μ₯ λ¨Όμ νμΈν λΆλΆ:
Board
β F439ZI λ§λκ°?
USART
β USART3μΈκ°?
TX
β PD8μΈκ°?
AF
β AF7μΈκ°?
AFR
β AFR[1]μΈκ°?
Clock
β APB1 USART3EN bit18μΈκ°?
Baud
β 115200μΈκ°?
Terminal
β μ¬λ°λ₯Έ ST-LINK COM PortμΈκ°?
νΉν μμμ:
USART2 / PA2λ₯Ό κ·Έλλ‘ μ¬μ©νλ©΄ F439ZI VCPμμλ μΆλ ₯μ΄ λ³΄μ΄μ§ μλ κ²μ΄ ν΅μ¬ porting issueλ€.
images/ch10_build.png
images/ch10_gpio_af.png
images/ch10_usart_registers.png
images/ch10_terminal.png
images/ch10_board_led.gifκ°μ₯ μ€μν μ¦κ±°:
USART3 Register
+
115200 Terminal Output
+
LD1 ToggleGPIOD Clock
β
PD8 / PD9 AF Mode
β
AF7
β
USART3 Clock
β
BRR = 139
β
TE + UE
β
TXE Polling
β
DR Write
β
PD8
β
PCNUCLEO-F439ZIμ ST-LINK VCPλ USART3λ₯Ό μ¬μ©νλ€.
USART3 TXλ PD8, RXλ PD9λ€.
PD8/PD9λ AF7λ‘ μ€μ νλ€.
Pin 8~15λ
AFR[1]μ μ¬μ©νλ€.USART3λ APB1 peripheralμ΄λ€.
μ΄λ² project clockμ 16MHzλ€.
115200bpsμμ BRRμ μ½ 139λ€.
CR1.TEλ transmitterλ₯Ό enableνλ€.CR1.UEλ USART peripheral μ체λ₯Ό enableνλ€.SR.TXEλ λ€μ dataλ₯ΌDRμ μΈ μ μμμ λνλΈλ€.λ¬Έμμ΄μ ν characterμ© TXEλ₯Ό pollingνλ©° μ μ‘νλ€.
μ΄λ² μ€μ΅μ TX-only communicationμ΄λ€.
μ€μ boardμμ VCP 115200-8-N-1 μΆλ ₯κΉμ§ κ²μ¦νλ€.