728x90
1. 강의 목표
WritePin부분을 HAL Driver를 사용하지 않고 순수 코딩만으로 만들어보기
- HAL Driver의 코드를 분석하고 Datasheet를 보며 필요한 것들만 뽑아 코드를 만들 것
MX_GPIO_Init();
HAL_GPIO_WritePin(GPIO_LED_GPIO_Port, GPIO_LED_Pin, 0);
- 위 두개의 코드 분석
- Data Sheet를 보며 해당하는 주소값을 보며 코딩을 해야 함
- Embedded Programming은 Source Code는 몇 줄 안되나 이해하고 코드로 표현을 하기까지 수많은 공부가 필요
- 회로, 컴퓨터 등의 알아야 할 배경 지식이 너무 많음
- 코드를 분석하기 쉬운 방법
- 소스가 어떤 환경에서 작동하는지 이해 후 환경 구성
- Breakpoint를 찍어가며 Debugging 진행
2. HAL Driver 분석
* 아래 내용부터는 기본적인 C언어 지식이 필요함
- 위 HAL_Init -> HAL_FLASH_PREFETCH_BUFFER_ENABLE 의 (FLASH->ACR |= FLASH_ACR_PRFTBE) 분석
- 코드는 Core -> startup_stm32f103c8tx.s 에서부터 시작 (어셈블리)
- .s : 어셈블리어 코드의 확장자. 소문자 's' 확장자는 Compiler가 출력한 어셈블리어 코드로,
GCC 컴파일러가 컴파일 시 전처리가 필요없는 파일로 인식한다 - .S : 사용자가 직접 작성한 어셈블리어 코드로, GCC 컴파일러가 전처리가 필요한 파일로 인식한다
- .s : 어셈블리어 코드의 확장자. 소문자 's' 확장자는 Compiler가 출력한 어셈블리어 코드로,
- 여러 경로를 거쳐 main.c 의 int main에 옴
2.1. Debugging
1. HAL_Init()에서 Ctrl + 좌클릭 (함수 정의 부분으로 이동)
2. HAL_Init()과 __HAL_FLASH_PREFETCH_BUFFER_ENABLE() 에 breakpoint 찍고 debugging 시작
- 위의 #if문은 Linux Driver 구현시 많이 볼 문장들 : 다양한 칩들에 따라 속성을 변경하기 위해 define 사용
- 우리가 사용하는 칩은 defined(STM32F103xB)에 해당 : __HAL_FLASH_PREFETCH_BUFFER_ENABLE();로 진입
- Linux Driver를 vi Editor 하나로 할 땐 Grep을 통해 분석을 해나감. breakpoint도 못찍어서 불편함
- Grep : 윈도우에서(모든 파일 중에서) 내가 원하는 문자열 찾기
3. (FLASH->ACR |= FLASH_ACR_PRFTBE) 의 의미 해석
- __HAL_FLASH_PREFETCH_BUFFER_ENABLE( ) 은 define 문이므로 함수 단위로 접근 불가
- 위 statement 해석을 위해 Editor 우측 상단의 'Expression' 창에 각 변수 및 표현을 추가
- FLASH : 0x40022000에 접근하는 변수
- ACR은 변수 이름으로 접근 불가 : 주소 확인
- 위 사진은 (FLASH->ACR |= FLASH_ACR_PRFTBE) 의 각 변수들의 (주소)값을 나타낸 것
- 해당 statement는 아래와 같이
*(40022000) = *(40022000) | 16;
- 40022000 주소번지에 16을 쓴다는 뜻
2.1.1. FLASH_TypeDef의 의미
- 레지스터의 이름을 정해서 Structure 형태로 만듦
- 레지스터들을 FLASH의 맴버로 만들어 접근하기 편하게 함
- 즉 위 문장의 의미는 FLASH 주소번지의 ACR에 16을 OR 연산하는 코드
- ACR이 FLASH 번지의 첫번째 변수이므로 FLASH와 주소가 같았던 것
2.1.2. uint32_t의 의미
#define uint32_t unsinged_int // 4byte 크기
- ACR을 세팅하는 레지스터, KEYR을 세팅하는 레지스터 OPTKERYR을 세팅하는 레지스터등등이 0x40022000 부터 4byte씩 있다
- RESERVED는 사용하지 않는 공간
- FLASH -> OBR |= 4; : FLASH의 OBR에 4를 넣음
2.1.3. __IO의 의미
#define __IO volatile
- 최적화 방지를 위해 사용
- Compiler는 compile시 프로그램의 실행 속도를 최대화하거나 프로그램이 차지하는메모리의 양을최소화하기 위해
컴파일러 최적화(optimizing compiler)를 한다 - 참고 : https://youngseong.tistory.com/125
- volatile은 CV Qualifier로, 변수 선언문 앞에 붙이면 해당 변수에 대해서는 어떠한 최적화 처리도 하지 않음
- CV Qualifier : 변수의 성질을 바꾸는 역할을 하는 Keyword (Ex) Const, volatile)
access and modification semantics for variables and objects:
2.1.3.1. 최적화 방지를 하는 이유
int a = 3;
a = 7;
a = 8;
a = 12;
printf(%d\n", a);
- Compiler는 위의 코드 중 미사용되는 부분을 삭제하여
int a = 12;
printf(%d\n", a);
- 이처럼 최적화를 시킴. 위의 경우에선 효율적이지만 GPIO를 제어할 때,
*a = 0x03;
*a |= 1; // a를 킴
*a |=0; // a를 끔
*a |= 1; // while문 안에서 키고 끄고를 반복
*a |=0;
*a |= 1;
*a |=0;
- 위의 코드를 최적화하면
*a = 0x03;
*a |=0;
- 가 되는데, 이러면 의도한 대로 동작을 하지 못함
a = 3;
while( ){
if(a){
statement; // a가 특정 상황일 때 하는 동작
}
}
- 위와 같이 a의 환경에 따라 동작할 것을 지정했는데. a가 소스코드상 3에서 바뀌지 않을 때
- a를 메모리에 넣지 않고 CPU 레지스터에 넣고 씀 (자주 쓴다고 생각해서)
- 이렇게 되면 GPIO값에 따라 a가 변해야 되는데 변하지 않음
- 위와 같은 상황을 방지하기 위해 #define __IO volatile 로 최적화 방지. volatile이 최적화 하지 말라는 뜻
- 최적화를 위한 코드 삭제 (중간 과정 삭제) 방지
- 어떤 변수를 자주 사용한다고 CPU 레지스터에 넣는 것을 방지하고 원래대로 메모리에 넣어서 사용하게 함
2.2. 결론
(FLASH->ACR |= FLASH_ACR_PRFTBE) == *(40022000) |= 16;
- 위 코드의 왼쪽 부분은 오른쪽 동작을 하며
voltile int unsigned int* ACR = 40022000
*ACR |= 16;
- 위와 같이 대체할 수 있다
2.2.1. FLASH_ACR_PRFTBE의 의미
- 1을 FLASH_ACR_PRFTBE_Pos(4) 만큼 shift 이동 (bitwise shift) : 1 << 4;
- 1을 옆으로 4칸 밀면 16이 된다 (16진수)
2.2.1.1. 1을 옆으로 4칸 이동하는 것의 의미 : Datasheet 확인
- 강의 자료중 레퍼런스 메뉴얼을 킨 후 Ctrl + F로 ACR 검색 : 56페이지
- FLASH_ACR Register가 Prefetch와 Half Cycle Access를 활성화/비활성화 및
CPU 주파수에 따라 Flash Memory Access Time을 제어 함을 알 수 있음- 아래와 같이 define 문의 이름에 'PREFETCH'가 들어감으로 Prefetch 기능에 연관이 있음을 유추 가능
- 1을 옆으로 4칸 이동시키는 것의 의미는 60페이지에서 확인할 수 있다
- ACR : Access Control Register를 의미
voltile int unsigned int* ACR = 40022000 *ACR |= 16;
- 즉 위 코드는 Prefetch buffer enable 레지스터를 제어하는 것
- 1이 해당 레지스터에 입력됐으므로 Prefetch 활성화
2.2.1.2. Prefetch에 대한 이해
- OS(Window)는 보조기억장치 (HDD, SSD)에 저장. 주기억장치(RAM)에 저장 시 전원을 끄면 데이터가 사라지므로
- 부팅시 보조기억장치에 있는 메모리가 RAM으로 복사된 후 그 복사된 OS를 CPU가 실행시킴
- Prefetch : SSD 보급 전 HDD를 읽는 속도를 높이기 위해 HDD의 데이터를 미리 메모리(RAM)에 가져오는 기술
윈도우 부팅시 사용하는 시스템 파일과 자주 사용하는 응용 프로그램들의 정보 등을 먼저 읽어들여서 부팅이나 프로그램을 빠르게 실행되도록 도와주는 기능이다 - pf 파일에는 실제 메모리로 읽어오는 파일이 하드디스크 어디에 있는지 그 주소가 저장되어 있어 실제 사용시에는 memory mapping만을 수행하여 사용할 수 있도록 해 실행속도를 향상시킴
- 하지만 SSD의 보급 이후, SSD의 속도가 HDD가 Prefetch를 사용하는 것에 비해 훨씬 빨라서 SSD 사용시에는 Prefetch의 사용이 자동으로 비활성화된다
2.3. 결론
#define __HAL_FLASH_PREFETCH_BUFFER_ENABLE() (FLASH->ACR |= FLASH_ACR_PRFTBE)
- 위의 코드는 아래와 같은 의미이고
voltile int unsigned int* reg = 40022000
*reg |= 16;
- 이 코드는 Flash Access Control Register의 Prefetch buffer enable bit를 활성화 시킨다
2.4. 마무리
- HAL 드라이버 분석을 할 수 있으면 임베디드 고수가 될 수 있다
- 이번 강의 내용은 선생님 혼자서 터득한 것. 선생님이 공부하신데로 공부하면 그만큼은 할 수 있다
- 프로그래밍은 결국 혼자서 공부하는 시간이 중요. 강의를 그대로 따라하는게 아닌 앞으로의 공부 방향과 선생님의 공부 방식을 참고하여 내 공부 방식을 연구하는 것이 중요
- 노하우가 중요한 것이 아닌 궁금증을 가지고 무조건 한다는 의지와 함께 그 궁금증을 푸는 것이 중요. 그러면 노하우는 따라옴. 안될 때도 있으나 그럴 땐 좌절하지 말고 잠시 넘어가고 나중에 다시 해보기
- 이번 강의 내용들은 컴퓨터에 대한 지식들이 있어야 이해하기 쉽다
- 학부생일땐 컴퓨터에 대한 지식을 많이 쌓아야 함
- 웹상의 최적화 관련 문제는 결국 OS와 운영체제와 연결이 됨
- C언어를 하면 하드웨어와 밀접한 언어를 공부하는 것이라 하드웨어와 익숙해져 분석에 용이해 진다 : 학부과정에 C언어가 있는 이유
- 이번 강에서는 __HAL_FLASH_PREFETCH_BUFFER_ENABLE() 문장 하나를 해석
- 이런 과정은 시간과 연구가 필요함. 즉 혼자 씹어먹는 시간이 필요함
3. etc
3.1. Grep을 이용한 __HAL_FLASH_PREFETCH_BUFFER_ENABLE( ) 의 FLASH 추적
- PERIPH_BASE = 0x40000000UL
#define FLASH ((FLASH_TypeDef *)FLASH_R_BASE)
#define FLASH_R_BASE (AHBPERIPH_BASE + 0x00002000UL)
// UL = Unsigned Long
#define AHBPERIPH_BASE (PERIPH_BASE + 0x00020000UL)
#define PERIPH_BASE 0x40000000UL
// 결과는 FLASH는 0x40022000의 메모리 번지에 접근한다는 뜻
위 내용의 모든 출처는 유튜버 '[오제이 튜브]OJ Tube' 님께 있습니다.
https://www.youtube.com/watch?v=gf-7jy8l2mQ&list=PLz--ENLG_8TNjRg1OtyFBvUyV4PHaKwmu&index=10
참고 자료 :
https://jakupsil.tistory.com/33
'Study_Embedded > [오제이 튜브의 임베디드 실전 강의]' 카테고리의 다른 글
[오제이 튜브의 임베디드 실전 강의] 7강 GPIO제어 고아 먹기 (0) | 2022.02.11 |
---|---|
[오제이 튜브의 임베디드 실전 강의] 6강 GPIO제어 뿌셔 먹기 (0) | 2022.02.08 |
[오제이 튜브의 임베디드 실전 강의] 3-1강 개발 환경 구축하기, 4강 Hello!! GPIO!! (2) | 2022.01.08 |
[오제이 튜브의 임베디드 실전 강의] 3강 전기 기본 상식 - 모르면 보드 태워먹는다 (0) | 2021.12.02 |
[오제이 튜브의 임베디드 실전 강의] 2강 실무자는 어떻게 칩을 고르나요? (0) | 2021.11.28 |