본문 바로가기
임베디드

[오제이 튜브의 임베디드 강의] 6강. 혼자서 임베디드 고수 되는 방법

by 덤더리덤떰 2025. 1. 6.

0. main 함수 

int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  //HAL_Init(); 기존 코드 

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  //MX_GPIO_Init(); 기존 코드 
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */
  volatile unsigned int * reg = 0x40021018;
  *reg |= 16;
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */


  volatile unsigned int * reg2 = 0x40011004;
  *reg2 = (*reg2 & ~(15UL << 20U)) | (3U << 20U);


  volatile unsigned int * reg3 = 0x40011010;
  while (1)
  {
	  *reg3 = 0x2000;

	  HAL_Delay(100);

	  *reg3 = (0x2000 << 16);
	  HAL_Delay(100);

	  /*HAL_GPIO_WritePin(GPIO_LED_GPIO_Port, GPIO_LED_Pin, 1);
	  HAL_Delay(100);
	  HAL_GPIO_WritePin(GPIO_LED_GPIO_Port, GPIO_LED_Pin, 0);
	  HAL_Delay(100);
	  */

	  /*
	  if(!HAL_GPIO_ReadPin(GPIO_SW_GPIO_Port, GPIO_SW_Pin)){
		  HAL_GPIO_WritePin(GPIO_LED_GPIO_Port, GPIO_LED_Pin, 0);
	  }else{
		  HAL_GPIO_WritePin(GPIO_LED_GPIO_Port, GPIO_LED_Pin, 1);
	  }

	  HAL_Delay(100);
	  */
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

 

 

1. HAL 드라이버 분석 방법

: HAL_GPIO_WritePin(); 함수를 이용하지 않고 나의 코드로 어떻게 구현?
: 데이터 시트를 보고 해당하는 번지수 찾아서 하나하나씩 제어해야함 (소스 코드는 정작 5~6줄 but 그 과정이 매우 어려움)
: 우리는 파일 드라이브에 쓰여진 코드를 분석하며 어떤 코드인지 분석하고 그 코드 토대로 데이터시트 보면서 필요한것만 뽑아서 코드 작성


1-0. 코드 분석할 때 빨리 하는 방법

  • 소스가 어떤 환경에서 돌아가는지 이해
  • 소스가 돌아갈 수 있는 환경 만들기
  • 디버깅 할 수 있는 단계 만들기 (디버깅을 하면서 코드 분석해야함) 

ex) __HAL_FLASH_PREFETCH_BUFFER_ENABLE(); 함수 분석해보기 (HAL_Init함수)

#if defined(STM32F101x6) || defined(STM32F101xB) || defined(STM32F101xE) || defined(STM32F101xG) || \
    defined(STM32F102x6) || defined(STM32F102xB) || \
    defined(STM32F103x6) || defined(STM32F103xB) || defined(STM32F103xE) || defined(STM32F103xG) || \
    defined(STM32F105xC) || defined(STM32F107xC)

  /* Prefetch buffer is not available on value line devices */
  __HAL_FLASH_PREFETCH_BUFFER_ENABLE();
#endif
  • 칩에 따라 속성을 바꾸기 위한 #define문 코드 (우리꺼는 STM32F103xB) 
  • 이 칩들 중에 하나 속하면 아래 __HAL_FLASH_PREFETCH_BUFFER_ENABLE(); 수행 
  • ctrl + 왼쪽 마우스로 함수 클릭하면 아래 사진과 같이 동작 수행 

  • FLASH 분석하기 위해 FLASH도 ctrl + 왼쪽 마우스 클릭 

  • FLASH는 ((FLASH_TypeDef *) FLASH_R_BASE)로 정의되어 있음을 알 수 있음 

  • 디버깅하면서 Expression창에서 FLASH를 검색하면 위 사진과 같이 0x40022000 번지를 접근하는 것을 알 수 있음
  • 이때, 맨 처음 코드는 (FLASH->ACR |= FLASH_ACR_PRFTBE)이었기에 FLASH->ACR을 검색하면 동일하게 0x40022000이고 FLASH_ACR_PRFTBE와 or연산하기에 얘도 검색해보면 아래 사진과 같이 16임을 알 수 있다

 

결론.

#define __HAL_FLASH_PREFETCH_BUFFER_ENABLE()        (FLASH->ACR |= FLASH_ACR_PRFTBE)은,

*(0x40022000) |= 16;과 같다. 

 


1-1. FLASH_TypeDef에 더 깊이 분석해보기 

: FLASH는 ((FLASH_TypeDef *) FLASH_R_BASE)로 정의되어있음
: FLASH->ACR = 40022000번지에 접근 
: FLASH->KEYR = 40022004번지에 접근 
...

  • 0번지부터 시작한다고 가정한다면,
  • 0 ACR
  • 4 KEYR
  • 8 OPTKEYR
  • 12 SR
  • 16 CR
  • 20 AR
  • 24 RESERVED
  • 28 OBR
  • 32 WRPR
=> FLASH라는 공간이 큰 덩어리로 있고 그 안에 각각의 레지스터 이름을 설정하여 구조체화한 것 

 
(1) __IO는 무엇을 의미?
: #define __IO volatile로 정의되어있음
: 'volatile'은 휘발성을 뜻하여, 최적화 방지 위해 씀
 
cf. 컴파일러는 최적화라는 것을 한다.

ex)
int a = 3;
a = 7;
a = 8;
a = 12;
printf("%d\n", a); 라는 코드를 보면, 
     ↓
int a = 12;
printf("%d\n", a); 로 최적화

 
BUT, GPIO 제어과정에서 스위치를 껐다키는 과정을 반복하기위해

*a = 0x03;
*a |= 1;
*a |= 0;
*a |= 1;
*a |= 0;
    ↓
*a = 0x03;
*a |= 0;로 최적화한다면 내가 원했던 코드가 되지않음

+ 이외에도, 자주 사용하는 변수의 값이 있다면 메모리가 아닌 cpu 레지스터 내에 저장하는 경우 존재

결론.

__IO는 volatile로 정의되어있고 이 의미는 "최적화 하지마 + 자주 사용하는 값이라고 cpu 레지스터에 넣지마"
= " 최적화하지말고 무조건 메모리에 올려서 사용해" 

 
 


(FLASH->ACR |= FLASH_ACR_PRFTBE) 은,
volatile unsigned int * reg = 0x40022000;
* reg |= 16;과 같다 

 

(2) 16의 의미에 대해 알아보자.

=> 결론적으로, (0x1UL << FLASH_ACR_PRFTBE_Pos)인데 이때 FLASH_ACR_PRFTBE_Pos는 4U로 정의되어있다.
=> 1 << 4; 
=> 16
 

2. 데이터시트를 통해 코드 확인

 

  • FLASH_ACR을 다루려면 0x4002 2000번지로 접근
  • 이때, 아까 정의되어있던 구조체 구조(순서) 동일 
  • 좀 더 살펴보면, 아래 사진과 같이 정의되어있음
  • 프리패치 & 하프 사이클 액세스를 활성/비활성화하는 레지스터
  • 떠올려보니 함수명이 __HAL_FLASH_PREFETCH_BUFFER_ENABLE()

 

  • 우리는 FLASH_ACR |= 16;을 했는데 이 말은 즉슨, 4bit를 왼쪽으로 옮긴것
  • 이때, Bit 4 PRFTBE: Prefetch buffer enable로 정의되어있고 1은 Prefetch is enabled임

 
 
 
 
 

결론.

__HAL_FLASH_PREFETCH_BUFFER_ENABLE();
                  ↓

#define __HAL_FLASH_PREFETCH_BUFFER_ENABLE()        (FLASH->ACR |= FLASH_ACR_PRFTBE)
                  ↓
*(0x40022000) |= 16;
                  ↓
플래시 메모리의 프리패치 기능을 활성화한 것 

 

2-1. 프리패치란?

: SSD 보급 전 HDD를 읽는 속도를 높이기 위해 HDD의 데이터를 미리 메모리(RAM)에 가져오는 기술
윈도우 부팅시 사용하는 시스템 파일과 자주 사용하는 응용 프로그램들의 정보 등을 먼저 읽어들여서 부팅이나 프로그램을 빠르게 실행되도록 도와주는 기능
: CPU가 명령어를 실행하기 전에 Flash 메모리에서 데이터를 미리 읽어와 속도를 향상시키는 기능
 
cf. OS는 주기억장치(RAM)에 저장하고 전원 끄면 데이터 사라지기에, 보조기억장치(HDD/SDD)에 저장
=> 부팅시 보조기억장치에 있는 메모리가 RAM으로 복사되어 그 복사된 OS를 CPU가 실행시키는 것임