Bare metal C Ch10 UART - Hongjayeong-02/Basic_SoC_implementation GitHub Wiki
ํ ์ค ์์ฝ: NUCLEO-F439ZI์ ST-LINK VCP์ ์ฐ๊ฒฐ๋ USART3๋ฅผ 115200 bps๋ก ์ค์ ํ๊ณ , 1์ด๋ง๋ค ๋ฌธ์์ด์ PC ํฐ๋ฏธ๋๋ก ์ถ๋ ฅํ๋ค.
UART๋ ๋ณ๋์ clock line ์์ด TX/RX ์ ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ๋ ๋น๋๊ธฐ ์ง๋ ฌ ํต์ ์ด๋ค. ์์ชฝ ์ฅ์น๊ฐ ๊ฐ์ baud rate๋ฅผ ์ฌ์ฉํด์ผ ํ๋ฏ๋ก baud-rate ์ค์ ์ด ์ค์ํ๋ค.
ํญ๋ชฉ | ์์ NUCLEO-F411 | NUCLEO-F439ZI ์ค์ต -- | -- | -- ST-LINK VCP | USART2 | USART3 TX | PA2 | PD8 RX | PA3 | PD9 Clock enable | USART2EN | USART3EN, APB1ENR bit18 Alternate Function | AF7 | AF7 AFR | PA2/3 โ AFR[0] | PD8/9 โ AFR[1]Notion๊ณผ ์ค์ ์ฝ๋์์๋:
(periph_clk + baudrate / 2U) / baudrate
ํํ๋ก ์ ์ ๋๋์ ์ ๋ฐ์ฌ๋ฆผ์ ๊ตฌํํ๋ค.
APB1 clock = 16 MHz
baud = 115200
BRR โ 139
while (!(USART3->SR & SR_TXE)) { }
USART3->DR = data;
CPU๊ฐ TXE๊ฐ set๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฐ ๋ค ๋ค์ byte๋ฅผ DR์ ์ด๋ค.
GPIOD clock enable
โ
โผ
PD8 / PD9 โ Alternate Function
โ
โผ
AF7 โ USART3
โ
โผ
USART3 clock enable
โ
โผ
BRR = 115200 bps ์ค์
โ
โผ
TE + UE enable
โ
โผ
TXE polling
โ
โผ
DR write
โ
โผ
ST-LINK VCP โ PC terminal
Ch10_uart/
โโโ Inc/gpio.h
โโโ Src/gpio.c
โโโ Inc/systick.h
โโโ Src/systick.c
โโโ Inc/uart.h
โโโ Src/uart.c
โโโ Src/main.c
syscalls.c,sysmem.c, startup file, linker script, CubeIDE metadata/build output์ Ch04์์ ๋ค๋ฃฌ ๊ณตํต ๊ธฐ๋ฐ์ด๋ฏ๋ก ์ด ํ์ด์ง์์๋ ๋ฐ๋ณตํ์ง ์๋๋ค. ์๋์๋ ์ด chapter ๊ธฐ๋ฅ ๊ตฌํ์ ์ง์ ์ฌ์ฉํ source/header๋ฅผ ์ ๋ถ ๊ธฐ๋กํ๋ค.
#ifndef GPIO_H
#define GPIO_H
#include <stdbool.h>
/* ---- LED (PB0, LD1 ๋
น์) ---- */
void led_init(void);
void led_on(void);
void led_off(void);
/* ---- Push Button (PC13, B1) ---- */
void button_init(void);
bool get_btn_state(void);
void led_toggle(void); /* Ch08์์ ์ถ๊ฐ */
#endif /* GPIO_H */
#include "stm32f4xx.h"
#include "gpio.h"
#define LED_PIN 0U /* PB0 = LD1 (๋
น์) /
#define BTN_PIN 13U / PC13 = B1 */
void led_init(void)
{
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;
GPIOB->MODER &= ~(0x3UL << (LED_PIN * 2));
GPIOB->MODER |= (0x1UL << (LED_PIN * 2));
}
void led_on(void)
{
GPIOB->BSRR = (1UL << LED_PIN); /* Set */
}
void led_off(void)
{
GPIOB->BSRR = (1UL << (LED_PIN + 16)); /* Reset */
}
void button_init(void)
{
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN;
GPIOC->MODER &= ~(0x3UL << (BTN_PIN * 2)); /* Input */
GPIOC->PUPDR &= ~(0x3UL << (BTN_PIN * 2)); /* No pull (๋ณด๋๊ฐ ์์ฒด ์ฒ๋ฆฌ) */
}
bool get_btn_state(void)
{
return (GPIOC->IDR & (1UL << BTN_PIN)) != 0U; /* active-HIGH */
}
void led_toggle(void)
{
GPIOB->ODR ^= (1UL << 0); /* PB0 = LD1 */
}
#ifndef SYSTICK_H_
#define SYSTICK_H_
#include <stdint.h>
#include "stm32f4xx.h"
void systick_msec_delay(uint32_t delay);
#endif /* SYSTICK_H_ */
#include "systick.h"
#define CTRL_ENABLE (1UL << 0)
#define CTRL_CLKSRC (1UL << 2)
#define CTRL_COUNTFLAG (1UL << 16)
/* ์ฝ์ดํด๋ญ 16MHz(๊ธฐ๋ณธ HSI) ๊ธฐ์ค 1ms๋น ํด๋ญ ์ฌ์ดํด ์ */
#define ONE_MSEC_LOAD 16000UL
void systick_msec_delay(uint32_t delay)
{
SysTick->LOAD = ONE_MSEC_LOAD - 1UL;
SysTick->VAL = 0UL;
SysTick->CTRL = CTRL_CLKSRC;
SysTick->CTRL |= CTRL_ENABLE;
for (uint32_t i = 0; i < delay; i++)
{
while ((SysTick->CTRL & CTRL_COUNTFLAG) == 0UL) { }
}
SysTick->CTRL = 0UL;
}
#ifndef UART_H
#define UART_H
#include "stm32f4xx.h"
void uart_init(void);
void uart_write_string(const char *s);
int __io_putchar(int ch);
#endif
#include <stdint.h>
#include "uart.h"
#define GPIODEN (1U << 3)
#define USART3EN (1U << 18)
#define DBG_UART_BAUDRATE 115200U
#define SYS_FREQ 16000000U
#define APB1_CLK SYS_FREQ
#define CR1_TE (1U << 3)
#define CR1_UE (1U << 13)
#define SR_TXE (1U << 7)
static void uart_write(int ch);
static uint16_t compute_uart_bd(uint32_t periph_clk, uint32_t baudrate);
static void uart_set_baudrate(uint32_t periph_clk, uint32_t baudrate);
int __io_putchar(int ch)
{
uart_write(ch);
return ch;
}
void uart_init(void)
{
RCC->AHB1ENR |= GPIODEN;
GPIOD->MODER &= ~(0x3UL << (8 * 2));
GPIOD->MODER |= (0x2UL << (8 * 2));
GPIOD->MODER &= ~(0x3UL << (9 * 2));
GPIOD->MODER |= (0x2UL << (9 * 2));
GPIOD->AFR[1] &= ~(0xFUL << 0);
GPIOD->AFR[1] |= (0x7UL << 0); /* PD8 -> AF7 (USART3_TX) */
GPIOD->AFR[1] &= ~(0xFUL << 4);
GPIOD->AFR[1] |= (0x7UL << 4); /* PD9 -> AF7 (USART3_RX) */
RCC->APB1ENR |= USART3EN;
uart_set_baudrate(APB1_CLK, DBG_UART_BAUDRATE);
USART3->CR1 = CR1_TE;
USART3->CR1 |= CR1_UE;
}
static void uart_write(int ch)
{
while (!(USART3->SR & SR_TXE)) { }
USART3->DR = (ch & 0xFF);
}
void uart_write_string(const char *s)
{
while (*s) uart_write((int)*s++);
}
static uint16_t compute_uart_bd(uint32_t periph_clk, uint32_t baudrate)
{
return (uint16_t)((periph_clk + (baudrate / 2U)) / baudrate);
}
static void uart_set_baudrate(uint32_t periph_clk, uint32_t baudrate)
{
USART3->BRR = compute_uart_bd(periph_clk, baudrate);
}
#include "gpio.h"
#include "systick.h"
#include "uart.h"
int main(void)
{
led_init();
uart_init();
while (1)
{
uart_write_string("Hello from NUCLEO-F439ZI! (USART3, 115200bps)\r\n");
led_toggle();
systick_msec_delay(1000);
}
}
Notion ๊ธฐ๋ก ๊ธฐ์ค ์ค์ ๋ณด๋ ๊ฒ์ฆ ์๋ฃ.
STM32CubeIDE build/download/run ์๋ฃ
VCP๋ฅผ
115200-8-N-1๋ก ์ด์ด ๋ฌธ์์ด ์ถ๋ ฅ ํ์ธ1์ด๋ง๋ค ๋ฌธ์์ด ์ถ๋ ฅ๊ณผ LD1 toggle ํ์ธ
text = 1168 bytes
data = 0 bytes
bss = 2560 bytes
warning ์์
F439ZI VCP๋ USART3 PD8/PD9๋ฅผ ์ฌ์ฉํ๋ค.
GPIO Alternate Function ์ค์ ํ USART clock/BRR/TE/UE๋ฅผ ์ค์ ํ๋ค.
TXE polling ํ DR์ byte๋ฅผ ๊ธฐ๋กํ๋ค.