테스트
테스트
STM32는 주소 공간을 512MB 단위로 8개의 블록으로 나눕니다:
주소 범위 | 블록 | 설명 |
0x0000_0000 ~ 0x1FFF_FFFF | Block 0 | Code 영역 (Flash, Boot Memory) |
0x2000_0000 ~ 0x3FFF_FFFF | Block 1 | SRAM 영역 |
0x4000_0000 ~ 0x5FFF_FFFF | Block 2 | Peripherals (레지스터) |
0x6000_0000 ~ 0x7FFF_FFFF | Block 3 | FSMC bank1 & bank2 (외부 메모리 인터페이스) |
0x8000_0000 ~ 0x9FFF_FFFF | Block 4 | FSMC bank3 & bank4 |
0xA000_0000 ~ 0xBFFF_FFFF | Block 5 | FSMC 레지스터 |
0xC000_0000 ~ 0xDFFF_FFFF | Block 6 | 사용되지 않음 |
0xE000_0000 ~ 0xFFFF_FFFF | Block 7 | Cortex-M3 내부 시스템 및 디버그용 레지스터 |
주소 | 설명 |
0x0000_0000 | Flash, System Memory, or SRAM으로 alias 됨 (BOOT 핀에 따라 달라짐) |
0x0800_0000 ~ 0x080F_FFFF | Flash 메모리 |
0x1FFF_0000 ~ 0x1FFF_7A0F | System Memory, OTP, Option Bytes 등 |
0x1FFF_C000 ~ 0x1FFF_FFFF | Option Bytes, Reserved 등 |
주소 | 설명 |
0x2000_0000 ~ 0x2001_BFFF | SRAM (112KB) |
0x2001_C000 ~ 0x2001_FFFF | SRAM (16KB) |
0x2002_0000 ~ 0x3FFF_FFFF | Reserved (bit-band alias 영역 포함) |
💡 Bit-Banding: 비트를 개별적으로 다루기 위해 특별히 매핑된 주소 영역입니다.
주소 | 주변장치 |
0x4000_0000 ~ 0x4000_03FF | TIM2, TIM3, TIM4 등 |
… | … |
0x4001_0000 ~ 0x4001_FFFF | USART1, SPI1, ADC1~3 등 |
총 0x4000_0000 ~ 0x5006_03FF | 모든 AHB/APB 장치들이 여기에 위치 |
이 영역은 **외부 메모리 (PSRAM, NOR, NAND 등)**를 연결하는 FSMC 컨트롤러의 bank별 주소입니다:
주소 범위 | 연결 장치 |
0x6000_0000 ~ 0x63FF_FFFF | FSMC bank1 NOR/PSRAM 1 |
0x6400_0000 ~ 0x67FF_FFFF | FSMC bank1 NOR/PSRAM 2 |
0x6800_0000 ~ 0x6BFF_FFFF | FSMC bank1 NOR/PSRAM 3 |
0x6C00_0000 ~ 0x6FFF_FFFF | FSMC bank1 NOR/PSRAM 4 |
0x7000_0000 ~ 0x7FFF_FFFF | FSMC bank2 NAND |
0x8000_0000 ~ 0x8FFF_FFFF | FSMC bank3 NAND |
주소 범위 | 설명 |
0xE000_0000 ~ 0xE00F_FFFF | NVIC, SCB, SysTick 등 |
0xE004_2000 등 | 디버깅, ITM, DWT 등 디버그 모듈 |
이 메모리 맵은 레지스터 접근, DMA 설정, Flash 접근, SRAM 제어, 부트 동작 등 STM32를 정확히 다루기 위한 핵심 정보입니다.
필요하면 각 블록별로 추가 설명이나 예제 코드도 제공 가능합니다.
특정 영역에서 자세히 알고 싶은 부분이 있나요?
memory mapped i/o 잖아
맞습니다, 이 STM32 메모리 구조는 Memory-Mapped I/O (MMIO) 구조를 따릅니다. 아래에서 개념부터 STM32에서의 적용까지 핵심을 정리해 드리겠습니다:
Memory-Mapped I/O는 주변 장치 (GPIO, USART, TIM 등)의 레지스터를 메모리 주소 공간에 직접 매핑해서 일반 메모리처럼 접근하는 방식입니다.
즉,
c
복사편집
#define GPIOA_MODER (*(volatile uint32_t*)0x40020000)
GPIOA_MODER = 0x00000001;
처럼 메모리에 값을 쓰는 것처럼 레지스터를 제어하는 방식입니다.
STM32에서는 Block 2 (0x4000_0000 ~ 0x5FFF_FFFF)가 바로 이 MMIO를 위한 주변장치 레지스터 영역입니다.
장치 | 시작 주소 | 예시 설명 |
GPIOA | 0x4002_0000 | 포트 A 제어 |
USART2 | 0x4000_4400 | 시리얼 통신 제어 |
TIM2 | 0x4000_0000 | 타이머 제어 |
RCC | 0x4002_3800 | 클럭 설정 |
방식 | 특징 |
MMIO | CPU가 메모리처럼 장치 레지스터에 접근함. Load/Store 명령 사용. |
Port-Mapped I/O (x86에서 사용) | 특별한 I/O 명령 사용 (in , out ) |
STM32는 MMIO 방식만 지원합니다. 장점은:
STM32 내부 구조는 다음과 같은 흐름입니다:
복사편집
[ CPU ]
↓
[ BUS (AHB/APB) ]
↓
[ 주소 디코더 ] → 0x4002_0000 = GPIOA
↓
[ 장치 레지스터 ]
c
복사편집
#define RCC_AHB1ENR (*(volatile uint32_t*)0x40023830)
#define GPIOA_MODER (*(volatile uint32_t*)0x40020000)
#define GPIOA_ODR (*(volatile uint32_t*)0x40020014)
void gpio_init(void) {
RCC_AHB1ENR |= (1 << 0); // GPIOA 클럭 활성화
GPIOA_MODER |= (1 << 10); // PA5를 출력 모드로
}
void gpio_write_high(void) {
GPIOA_ODR |= (1 << 5); // PA5 출력 High
}
항목 | 설명 |
MMIO란? | 주변장치 레지스터를 메모리 주소처럼 직접 접근 |
STM32 적용 주소 | 0x4000_0000 ~ 0x5FFF_FFFF (Block 2) |
접근 방법 | C 포인터를 이용해 직접 레지스터 주소에 접근 |
장점 | 빠르고 일관된 접근 방식, 간결한 하드웨어 제어 |
필요하면 주변장치별 MMIO 주소 정리표나, 직접 접근 대신 CMSIS를 사용하는 구조도 정리해 드릴 수 있습니다. 특정 장치를 예로 들어볼까요?
block2 만 memory mapped i/o야?
아주 좋은 질문입니다. 결론부터 말하면:
🟨 Block 2(0x4000_0000 ~ 0x5FFF_FFFF) 영역이 대표적인 Memory-Mapped I/O 영역이지만,
**🟥 Block 7(0xE000_0000 ~ 0xFFFF_FFFF)**도 Memory-Mapped I/O 방식으로 동작합니다.
블록 | 주소 범위 | 설명 | MMIO 여부 |
Block 2 | 0x4000_0000 ~ 0x5FFF_FFFF | APB/AHB 버스를 통한 외부 주변장치 (GPIO, USART 등) | ✅ O |
Block 7 | 0xE000_0000 ~ 0xE00F_FFFF | System Control Block (SCB), NVIC, SysTick, Debug 등 | ✅ O |
기타 | 일부 Flash 컨트롤러, Option Bytes | Flash interface (write/erase 제어용 레지스터) | ✅ O (간접적으로) |
블록 | 주소 범위 | 설명 | MMIO 여부 |
Block 0 | 0x0000_0000 ~ 0x1FFF_FFFF | Flash, Boot ROM, alias 영역 | ❌ (일반 메모리) |
Block 1 | 0x2000_0000 ~ 0x3FFF_FFFF | SRAM | ❌ (일반 메모리) |
Block 3~5 | FSMC 영역 (0x6000_0000 이상) | 외부 메모리 인터페이스 (SRAM, NAND 등) | ❌ (외부 memory-mapped, 장치제어는 아님) |
0xE000_0000 ~
주소는 Cortex-M3 코어 내부 시스템 장치가 위치하는 영역입니다. 예를 들어:
장치 | 주소 | 설명 |
SCB (System Control Block) | 0xE000_ED00 | 인터럽트 우선순위, Sleep 제어 |
NVIC (Interrupt Controller) | 0xE000_E100 | 인터럽트 활성화/비활성화 |
SysTick Timer | 0xE000_E010 | 시스템 타이머 |
이들은 레지스터 방식으로 메모리처럼 접근하므로 Memory-Mapped I/O입니다.
필요하다면 STM32 MMIO 전체 주소 정리표도 제공해 드릴 수 있어요. 특정 영역이나 레지스터가 궁금하신가요?
응
좋습니다! STM32에서 사용되는 주요 Memory-Mapped I/O (MMIO) 주소 정리표를 아래에 핵심만 요약해드립니다. 이 표는 실무에서 레지스터 직접 접근하거나 디버깅할 때 매우 유용합니다.
장치 | 주소 범위 | 설명 |
NVIC | 0xE000_E100 ~ | 인터럽트 활성화/비활성화 |
SCB | 0xE000_ED00 ~ | 우선순위, 벡터 테이블, SYSRESET 등 |
SysTick | 0xE000_E010 ~ | 시스템 타이머 |
ITM/DWT/TPIU | 0xE000_0000 ~ | 디버그 추적 모듈 |
장치 | 주소 |
TIM2 ~ TIM5 | 0x4000_0000 ~ 0x4000_3FFF |
TIM6 ~ TIM14 | 0x4000_1000 ~ 0x4000_4FFF |
장치 | 주소 |
USART1 | 0x4001_3800 |
USART2 | 0x4000_4400 |
USART3 | 0x4000_4800 |
UART4 | 0x4000_4C00 |
UART5 | 0x4000_5000 |
장치 | 주소 |
SPI1 | 0x4001_3000 |
SPI2 | 0x4000_3800 |
I2C1 | 0x4000_5400 |
I2C2 | 0x4000_5800 |
장치 | 주소 |
RCC | 0x4002_3800 |
PWR | 0x4000_7000 |
장치 | 주소 |
GPIOA | 0x4002_0000 |
GPIOB | 0x4002_0400 |
GPIOC | 0x4002_0800 |
GPIOD | 0x4002_0C00 |
장치 | 주소 |
Flash memory | 0x0800_0000 ~ |
System memory (bootloader) | 0x1FFF_0000 ~ 0x1FFF_7A0F |
Option Bytes | 0x1FFF_C000 ~ |
c
복사편집
#define RCC_AHB1ENR (*(volatile uint32_t*)0x40023830)
#define GPIOA_MODER (*(volatile uint32_t*)0x40020000)
#define GPIOA_ODR (*(volatile uint32_t*)0x40020014)
void init_gpioa_pa5() {
RCC_AHB1ENR |= (1 << 0); // GPIOA 클럭 인가
GPIOA_MODER |= (1 << 10); // PA5 = Output
GPIOA_ODR |= (1 << 5); // PA5 High
}
원하시면 전체 MMIO 주소 맵을 PDF 혹은 엑셀로도 정리해드릴 수 있어요.
어떤 장치를 위주로 더 보고 싶으신가요? (예: DMA, ADC, SPI 등)
PDF 로 줘
완료되었습니다. 아래 링크에서 STM32의 MMIO 주소 맵 PDF를 다운로드할 수 있습니다:
👉 stm32_mmio_map.pdf 다운로드
필요하시면 ADC, DMA, CAN 등 추가 주변장치도 포함해서 확장할 수 있습니다. 추가로 포함할까요?
i2c도 memory mapped io잖아 그러면 i2c를 함수말고 register 설정만으로 통신가능하도록
맞습니다. STM32의 I2C1 (0x40005400), I2C2 (0x40005800) 는 모두 Memory-Mapped I/O 방식으로 접근 가능한 레지스터 기반 주변장치입니다.
따라서 HAL 라이브러리를 사용하지 않고도 레지스터만 직접 제어하여 I2C 통신을 구현할 수 있습니다.
여기서는 마스터 송신 (Master Transmit) 기준으로 I2C1을 사용한 예제를 설명합니다.
오프셋 | 이름 | 설명 |
0x00 | I2C_CR1 | Control register 1 |
0x04 | I2C_CR2 | Control register 2 |
0x08 | I2C_OAR1 | Own address register 1 |
0x10 | I2C_DR | Data register |
0x14 | I2C_SR1 | Status register 1 |
0x18 | I2C_SR2 | Status register 2 |
0x1C | I2C_CCR | Clock control register |
0x20 | I2C_TRISE | Maximum rise time |
c
복사편집
#define I2C1_BASE 0x40005400
#define RCC_APB1ENR (*(volatile uint32_t*)0x40023840)
#define RCC_AHB1ENR (*(volatile uint32_t*)0x40023830)
#define I2C1_CR1 (*(volatile uint32_t*)(I2C1_BASE + 0x00))
#define I2C1_CR2 (*(volatile uint32_t*)(I2C1_BASE + 0x04))
#define I2C1_OAR1 (*(volatile uint32_t*)(I2C1_BASE + 0x08))
#define I2C1_DR (*(volatile uint32_t*)(I2C1_BASE + 0x10))
#define I2C1_SR1 (*(volatile uint32_t*)(I2C1_BASE + 0x14))
#define I2C1_SR2 (*(volatile uint32_t*)(I2C1_BASE + 0x18))
#define I2C1_CCR (*(volatile uint32_t*)(I2C1_BASE + 0x1C))
#define I2C1_TRISE (*(volatile uint32_t*)(I2C1_BASE + 0x20))
c
복사편집
void i2c1_init() {
RCC_APB1ENR |= (1 << 21); // I2C1 클럭 활성화
RCC_AHB1ENR |= (1 << 0); // GPIOA 클럭 활성화 등 (SCL/SDA 핀 설정 필요)
I2C1_CR2 = 16; // APB1 클럭 16MHz
I2C1_CCR = 80; // Standard mode 100kHz
I2C1_TRISE = 17; // 최대 상승 시간
I2C1_CR1 |= (1 << 0); // Peripheral Enable
}
void i2c1_start() {
I2C1_CR1 |= (1 << 8); // START bit
while (!(I2C1_SR1 & (1 << 0))); // SB 설정될 때까지 대기
}
void i2c1_send_addr(uint8_t addr) {
I2C1_DR = addr;
while (!(I2C1_SR1 & (1 << 1))); // ADDR 확인
(void)I2C1_SR2; // SR2 읽기로 클리어
}
void i2c1_send_data(uint8_t data) {
while (!(I2C1_SR1 & (1 << 7))); // TXE
I2C1_DR = data;
while (!(I2C1_SR1 & (1 << 2))); // BTF
}
void i2c1_stop() {
I2C1_CR1 |= (1 << 9); // STOP
}
void i2c1_write_byte(uint8_t dev_addr, uint8_t reg, uint8_t data) {
i2c1_start();
i2c1_send_addr(dev_addr << 1); // Write mode
i2c1_send_data(reg); // Register address
i2c1_send_data(data); // Data
i2c1_stop();
}
Alternate Function
설정 필요AF4
로 설정 (STM32F4 기준)CCR
, TRISE
값 변경 필요항목 | 내용 |
구조 | I2C는 MMIO 레지스터를 통해 직접 제어 가능 |
주소 | I2C1 = 0x40005400, I2C2 = 0x40005800 |
장점 | HAL 미사용 → 코드 경량화, 빠른 제어 |
위험 | 오류 처리, 타이밍, ACK 체크 등 수동 처리 필요 |
필요하면 “I2C 수신”이나 “EEPROM 읽기”, “ACK 검사” 등도 추가로 작성해드릴 수 있습니다. 어떤 작업도 해보시겠어요?
코드에 주석을 달아야지
물론입니다. 아래는 HAL 없이 레지스터만으로 I2C1 마스터 송신을 구현한 예제 코드에 자세한 주석을 추가한 버전입니다. 이 코드는 STM32에서 I2C를 직접 제어하여 0x50 주소의 슬레이브에 데이터를 쓰는 동작입니다.
c
복사편집
// RCC 관련 레지스터: 클럭 설정용
#define RCC_APB1ENR (*(volatile uint32_t*)0x40023840) // APB1 버스 (I2C 클럭)
#define RCC_AHB1ENR (*(volatile uint32_t*)0x40023830) // AHB1 버스 (GPIO 클럭)
// I2C1 베이스 주소 및 레지스터 정의
#define I2C1_BASE 0x40005400
#define I2C1_CR1 (*(volatile uint32_t*)(I2C1_BASE + 0x00)) // 제어 1
#define I2C1_CR2 (*(volatile uint32_t*)(I2C1_BASE + 0x04)) // 제어 2 (클럭 설정)
#define I2C1_OAR1 (*(volatile uint32_t*)(I2C1_BASE + 0x08)) // Own Address 1
#define I2C1_DR (*(volatile uint32_t*)(I2C1_BASE + 0x10)) // 데이터 레지스터
#define I2C1_SR1 (*(volatile uint32_t*)(I2C1_BASE + 0x14)) // 상태 1
#define I2C1_SR2 (*(volatile uint32_t*)(I2C1_BASE + 0x18)) // 상태 2
#define I2C1_CCR (*(volatile uint32_t*)(I2C1_BASE + 0x1C)) // 클럭 제어
#define I2C1_TRISE (*(volatile uint32_t*)(I2C1_BASE + 0x20)) // 최대 상승 시간
c
복사편집
void i2c1_init() {
RCC_APB1ENR |= (1 << 21); // I2C1 클럭 활성화
RCC_AHB1ENR |= (1 << 1); // GPIOB 클럭 활성화 (PB6=SCL, PB7=SDA)
I2C1_CR2 = 16; // APB1 클럭 주파수 설정 (16MHz)
I2C1_CCR = 80; // 100kHz 설정 (Standard Mode = 16MHz / (2*CCR))
I2C1_TRISE = 17; // 최대 상승 시간 = 1000ns 기준 → TRISE = Freq(MHz) + 1
I2C1_CR1 |= (1 << 0); // I2C1 Peripheral Enable
}
c
복사편집
void i2c1_start() {
I2C1_CR1 |= (1 << 8); // START 비트 설정
while (!(I2C1_SR1 & (1 << 0))); // SB (Start Bit 설정됨) 플래그 대기
}
void i2c1_send_addr(uint8_t addr) {
I2C1_DR = addr; // 슬레이브 주소 (LSB = 0 for Write)
while (!(I2C1_SR1 & (1 << 1))); // ADDR 플래그 대기 (주소 전송 완료)
(void)I2C1_SR2; // SR2 읽기로 ADDR 클리어
}
void i2c1_send_data(uint8_t data) {
while (!(I2C1_SR1 & (1 << 7))); // TXE (전송 버퍼 Empty) 대기
I2C1_DR = data; // 데이터 전송
while (!(I2C1_SR1 & (1 << 2))); // BTF (Byte Transfer Finished) 대기
}
void i2c1_stop() {
I2C1_CR1 |= (1 << 9); // STOP 비트 설정 → 통신 종료
}
c
복사편집
void i2c1_write_byte(uint8_t dev_addr, uint8_t reg, uint8_t data) {
i2c1_start(); // START 신호 발생
i2c1_send_addr(dev_addr << 1); // 슬레이브 주소 전송 (Write 모드)
i2c1_send_data(reg); // 레지스터 주소 전송 (보통 EEPROM은 주소 지정 필요)
i2c1_send_data(data); // 실제 전송할 데이터 바이트
i2c1_stop(); // STOP으로 종료
}
c
복사편집
i2c1_init(); // I2C1 초기화
i2c1_write_byte(0x50, 0x10, 0xAB); // EEPROM 0x50, 주소 0x10에 0xAB 쓰기
필요하다면 읽기(Receive) 처리, EEPROM 연속 쓰기, ACK 오류 처리, DMA 연동 버전도 도와드릴 수 있습니다. 어떤 작업을 더 해볼까요?
i2c 프로토콜 자세히 설명
좋습니다. 아래는 I2C(Inter-Integrated Circuit) 프로토콜에 대한 핵심 개념, 동작 순서, 신호 구조, 상태 플래그 등을 포함한 완전한 설명입니다.
I2C (Inter-Integrated Circuit) 는 NXP(舊 Philips) 가 만든 양방향 2선식 직렬 통신 프로토콜입니다.
하나의 마스터가 시계를 제어하고, 슬레이브가 응답합니다.
슬레이브는 7비트 또는 10비트 주소를 가짐 (일반적으로 7비트)
sql
복사편집
START → 주소(7bit) + R/W → ACK → 데이터 전송 → ACK → STOP
구분 | 비트수 | 설명 |
Start | 1 | 시작 조건 |
Address | 7 | 슬레이브 주소 |
R/W | 1 | 0: Write, 1: Read |
ACK | 1 | 수신자의 응답 |
Data | 8 | 보낼 데이터 |
ACK | 1 | 수신자의 응답 |
… | … | 반복 가능 |
Stop | 1 | 종료 조건 |
css
복사편집
[S] → [ADDR + 0] → [ACK] → [DATA1] → [ACK] → [DATA2] → [ACK] → [P]
예: EEPROM에 0x50 주소로 0xAB 전송
css
복사편집
[S] → [ADDR + 0] → [ACK] → [MEM_ADDR] → [ACK] → [S repeated] → [ADDR + 1] → [ACK] → [DATA] → [NACK] → [P]
플래그 | 설명 | 위치 |
SB (Start Bit) | START 신호 발생됨 | SR1[0] |
ADDR | 주소 전송 완료 | SR1[1] |
BTF | 바이트 전송 완료 | SR1[2] |
TXE | 데이터 레지스터 비어 있음 | SR1[7] |
RXNE | 수신 데이터 준비 완료 | SR1[6] |
STOPF | 슬레이브가 STOP 감지 | SR1[4] |
항목 | 설명 |
🧷 전선 | 2개 (SDA, SCL) |
🎛 구조 | 마스터-슬레이브 |
⏱ 속도 | 표준(100kHz), 패스트(400kHz), 패스트플러스(1MHz), 하이스피드(3.4MHz) |
🧪 에러 처리 | 슬레이브가 응답하지 않으면 ACK 없음, 타임아웃 필요 |
🔧 장점 | 회로 단순, 주소 기반, 멀티슬레이브 지원 |
🔩 단점 | SPI보다 느림, 프로토콜 복잡도 ↑ |
text
복사편집
SCL: __|‾|__|‾|__|‾|__|‾|__|‾|__|‾|__|‾|__|‾|__
SDA: --\___0_1_0_1_0_0_0/---ACK---data_byte---/
START 7bit ADDR + W