- 개발 환경
개발 보드 : NUCLEO-F103RB
개발 프로그램
- STM32 CubeMX : 6.12.0
- STM32 CubeIDE : 1.16.0
- 관련 자료
1) RM0008 메뉴얼
1. 서론
STM32를 개발할 때 STM32에서 제공하는 라이브러리를 사용하여 주변장치를 초기화 및 제어합니다. 그러므로 직접적으로 레지스터를 제어할 이유가 거의 없습니다. 그리고 직접적으로 레지스터를 제어하여 개발하기에는 기능이 너무 방대하여 비효율적입니다. 하지만 개발하는데 있어서 단순히 제공하는 라이브러리만을 가지고 개발하는 것보다 MCU의 내부 메모리 구조를 확인하면서 직접 제어를 해보는 것이 해당 기능을 이해하는데 큰 도움이 된다고 생각합니다. 이번 포스트에서는 CMSIS Library를 사용하지 않고 메모리 주소를 직접 접근하여 GPIO를 제어해보도록 하겠습니다.
2. GPIO 출력 관련 레지스터 확인
Nucleo-F103RB 보드는 기본적으로 PA5핀에 LED가 연결되어 있습니다.
그러므로 PA5을 출력을 담당하는 레지스터의 주소를 확인 해보도록 하겠습니다.
void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
{
/* Check the parameters */
assert_param(IS_GPIO_PIN(GPIO_Pin));
assert_param(IS_GPIO_PIN_ACTION(PinState));
if (PinState != GPIO_PIN_RESET)
{
GPIOx->BSRR = GPIO_Pin;
}
else
{
GPIOx->BSRR = (uint32_t)GPIO_Pin << 16u;
}
}
Driver/STM321xx_HAL_Driver/Src/stm32f1xx_hal_gpio.c
위 함수는 우리가 자주 사용하는 HAL_GPIO_WritePin함수입니다. 함수를 확인해보니 GPIOx의 맴버 변수인 BSRR을 통해 출력을 제어하고 있음을 확인 할 수 있습니다. 사실 BSRR이 우리가 제어해야 할 레지스터 이름입니다.
위 Datasheet의 내용을 정리하면 다음과 같습니다.
여기서 먼저 알아두어야 하는 것이 있습니다.
Set의 의미는 1로 설정하는 것이고, Reset의 의미는 0으로 설정하는 것입니다.
- Address offset의 주소가 0x10라는 것 ( 어떠한 주소로 부터 0x10만큼 떨어져 있다는 뜻입니다. 어떠한 주소의 값은 아래 내용에서 확인할 예정입니다.)
- Reset value: 0x0000 0000 라는 것 (해당 레지스터의 초기 값이 0x0000 0000 라는 뜻입니다.)
- GPIOx_BSRR의 크기가 32bit라는 것입니다. (해당 의미는 우리가 개발하고 있는 MCU의 아키텍쳐가 32bit 라는 것입니다.)
- 모든 bit들이 Read Only라는 것입니다. (만약 Read 도 가능하다면 , "rw"라고 적혀 있을 것입니다.)
- BRy를 1로 설정하면 해당하는 ODRx의 비트가 0이 설정된다는 것입니다.
예를 들어, 지금 우리는 PA5에 연결된 LED를 끄고 싶습니다.
그렇다면, GPIOA_BSRR 레지스터의 BR5 비트를 1로 설정하면, GPIOA_ODR레지스터의 ODR5 비트가 자동적으로 0으로 설정되어 LED의 불이 꺼지는 것입니다. - BSy를 1로 설정하면 해당하는 ODRx의 비트가 1로 설정된다는 것입니다.
예를 들어, 지금 우리는 PA5에 연결된 LED를 켜고 싶습니다.
그렇다면, GPIOA_BSRR 레지스터의 BS5 비트를 1로 설정하면, GPIOA_ODR레지스터의 ODR5 비트가 자동적으로 1으로 설정되어 LED의 불이 켜지게 되는 것입니다.
이제 우리가 어떠한 레지스터를 제어해야할지 확인하였으니 해당 레지스터의 주소를 확인하도록 하겠습니다.
3. GPIOA_BSSR 레지스터 주소 확인
먼저, BSRR이 속한 GPIO_TypeDef 구조체를 확인해보도록 하겠습니다.
/**
* @brief General Purpose I/O
*/
typedef struct
{
__IO uint32_t CRL;
__IO uint32_t CRH;
__IO uint32_t IDR;
__IO uint32_t ODR;
__IO uint32_t BSRR;
__IO uint32_t BRR;
__IO uint32_t LCKR;
} GPIO_TypeDef;
Driver/CMSIS/Device/ST/STM32F1xx/include/stm32f103xb.h
BSRR은 5번째 맴버변수라는 것을 확인 할 수 있습니다. 그렇다는 것은 32bit 크기를 가지는 맴버변수가 4개가 있다는 의미입니다. 그 의미는 32bit는 4byte이며 4byte가 4개 이므로 16byte 즉, 16진수로 0x10입니다. 0x10은 위 데이터 시트에서 Address offset으로 0x10이라는 것과 일치합니다.
그렇다면 어떠한 주소로부터 0x10만큼 떨어져 있다는 것일까요?
아래 코드를 확인해보면 GPIOA는 GPIOA_BASE라는 주소를 시작으로 하는 GPIO_TypeDef 구조체라는 것을 알 수 있습니다.
#define GPIOA ((GPIO_TypeDef *)GPIOA_BASE)
#define GPIOB ((GPIO_TypeDef *)GPIOB_BASE)
#define GPIOC ((GPIO_TypeDef *)GPIOC_BASE)
#define GPIOD ((GPIO_TypeDef *)GPIOD_BASE)
#define GPIOE ((GPIO_TypeDef *)GPIOE_BASE)
Driver/CMSIS/Device/ST/STM32F1xx/include/stm32f103xb.h
GPIOA는 APB2PERIPH_BASE으로부터 0x0000 0800만큼 떨어져 있으며
#define GPIOA_BASE (APB2PERIPH_BASE + 0x00000800UL)
#define GPIOB_BASE (APB2PERIPH_BASE + 0x00000C00UL)
#define GPIOC_BASE (APB2PERIPH_BASE + 0x00001000UL)
#define GPIOD_BASE (APB2PERIPH_BASE + 0x00001400UL)
#define GPIOE_BASE (APB2PERIPH_BASE + 0x00001800UL)
Driver/CMSIS/Device/ST/STM32F1xx/include/stm32f103xb.h
APB2PERIPH_BASE은 PERIPH_BASE 으로부터 0x0001 0000 만큼 떨어져 있습니다.
#define APB2PERIPH_BASE (PERIPH_BASE + 0x00010000UL)
Driver/CMSIS/Device/ST/STM32F1xx/include/stm32f103xb.h
마지막으로 PERIPH_BASE의 주소는 0x4000 0000입니다.
#define PERIPH_BASE 0x40000000UL /*!< Peripheral base address in the alias region */
Driver/CMSIS/Device/ST/STM32F1xx/include/stm32f103xb.h
이제 정리해보도록 하겠습니다.
최종적으로 GPIOA_BSSR의 주소는 다음과 같습니다.
0x4000 0000 + 0x0001 0000 + 0x0000 0800 + 0x0000 0010 = 0x4001 0810
코드가 아닌 데이터 시트를 통해 이 사실을 확인 해보도록 하겠습니다.
GPIO PortA의 시작 주소가 0x4001 0800라는 것을 확인 할 수 있습니다.
GPIO PortA의 시작주소에 Addres offset인 0x10를 더하면 0x4001 0810으로 확인할 수 있습니다.
이로써 GPIOA_BSSR의 주소가 0x4001 0810 라는 것을 확인하였습니다.
4. 실습 코드
while (1)
{
// GPIOA_BSSR = PERIPH_BASE + APB2PERIPH_BASE + GPIOA_BASE + 0x10
// BS5 비트 1로 설정 -> LED ON
*(volatile uint32_t*)(0x40010810UL) = (0x1 << 5);
HAL_Delay(1000);
// BR5 비트 1로 설정 -> LED OFF
*(volatile uint32_t*)(0x40010810UL) = (0x1 << 5) << 16;
HAL_Delay(1000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
'Embedded > STM32' 카테고리의 다른 글
[STM32] 외부 인터럽트(EXTI : EXTernal Interrupt)를 통해 버튼의 입력 감지 (0) | 2025.04.06 |
---|---|
[STM32] USART / UART를 통한 데이터 송수신 (0) | 2025.04.06 |
[STM32] GPIO로 버튼 및 LED 입출력 실습 (0) | 2025.04.05 |
[PlatformIO] STM32를 아두이노처럼 개발 (0) | 2025.03.26 |
[STM32] I2C 통신으로 1602 Character LCD 출력 (0) | 2025.02.22 |