들어가며
472시간 / 1000시간
자주 적겠다고 선언한지 단 한 번만에 TIL을 미루고 또 미뤄버렸다. 배우게 너무 없는건 아니고, 배운게 너무 많아서 정리하느라 시간이 계속 부족했다 ;ㅅ;
ㅤㅤ
지난 주에 한 거라도 적어둬야겠다.
지난 주의 키워드
지난주에는 STM32F429ZI의 타이머와 CLK에 대해서 정리하고 있었다. 전체적으로 어떤 타이머들이 있는지, 그 다음 SysTick와 GP-Timer, Independent Watchdog 에 대해 정리하고, 앞으로 Advanced Timer와 Window Watchdog 에 대한 내용도 천천히 추가해볼 예정.
[MCU] General Purpose Timer
드디어 General Purpose 타이머까지 왔다. 사실 정리는 금방 해서 한 3-4일 전에 완료해뒀는데, 실습을 하면서 실제로 타이머를 사용해보느라 시간이 좀 걸린 것 같아. 실습도 내가 생각했던 시나리오
etst.tistory.com
ㅤㅤ
[MCU] SysTick 타이머
SysTick 타이머란?ARMv7-M Architecture Reference Manual 에서 SysTick의 정체에 대해서 찾을 수 있다. SysTick은메인 프로세서의 CLK을 사용하는 빠른 속도의 타이머SysTick Routine을 매 주기마다 실행하는 RTOS 타이
etst.tistory.com
ㅤㅤ
[MCU] Basic Timer
STM32F429ZIT6 / NUCLEO-F429ZI 보드 기준Basic Timer의 목적Basic Timer로 분류되는 TIM6과 TIM7은 다음의 특징을 가진다.시간 측정만 수행하는 아주 단순한 타이머이다. (Generic Timer!)내부적으로 DAC와 연결되어, D
etst.tistory.com
ㅤ
[MCU] STM32F429ZIT6의 타이머들
[MCU] STM32F429의 타이머STM32F429ZIT6 프로세서에는 다양한 타이머들이 탑재되어있다. 그런데 이 타이머들이 하나의 종류가 여러개가 있는게 아니라 다양한 종류의 타이머를 2-4개씩 탑재해둔 것이었
etst.tistory.com
General Purpose Timer는 수업에서는 타이머에 Pin으로 입력을 넣어주고 캡쳐하는 부분까지는 다루지 않았었는데, 이게 초음파나 여러 센서에서 유용하게 사용될 방법이라고 생각된다.
ㅤ
[MCU] Independent 워치독 타이머 - IWDG
펌웨어 수업을 하는 동안 계속 ‘워치독’이라는 키워드가 나왔는데, 내 머릿속에는 ‘아 그거 서비스 아닌가? 이전에 일 잠깐 할 때 슬랙으로 데이터나 에러 관련해서 알림 오는걸 본 것 같은
etst.tistory.com
특히 Independent Watchdog은 기존에 사용하던 타이머들과 설정(시작)방법이 약간 달라서 애를 먹었다. (그런데 그게 버그가 아니라 기능이라니!!)
ㅤ
오늘의 키워드
RTOS
- Real-Time : 정해진 시간 내에 처리하는 것.
- Context-Switch 의 Re-entrancy : Context Switch로 다른 프로세스로 넘어갔다가 다시 불러오면 실행이 가능함.
ㅤ
Task
- create() →
Ready→ osstart() →run
ㅤ
- 우선순위가 높다면
- 이벤트를 대기하다가 발생하면 처리해야함
- 이벤트를 계속 polling으로 기다린다면 낮은 우선순위의 프로세스들이 실행되지 않아 굶어죽을 수 있다.
- 우선순위가 높은데 다른 프로세스(낮은 우선순위)에게 양보하지 않는 Task도 있다.
- 초기화를 하는 Task
- 쭉 설정값들을 초기화한 뒤 프로세스 스스로 삭제
- 예외처리 Task
- 자판기에 종이컵이 없음
- → 다른 프로세스를 다 죽이고, 종이컵 없음 출력하고 프로세스 스스로 삭제
- 초기화를 하는 Task
ㅤ
Cortex-M의 장점
C언어만 알아도 개발할 수 있게 구조를 설계했다.
ㅤ
ARM에서는 페리퍼럴에 있는 장치들을 프로세서 내부로 가져왔음
- NVIC, MPU, SysTick Timer
- 이것도 C만 알아도 개발할 수 있도록 하기위해서 프로세서 내부로 가져옴
- 제조사가 변경되더라도(ST, 삼성, 도시바, … ) ARM에서 설계한 NVIC을 따르면 되기 때문에 여러 보드에서 통일된 인터럽트 제어 환경을 제공할 수 있다.
ㅤ
- Exception or Interrupt가 발생하면 HW가 자동으로 처리해주는 일이 많다.
- [HW] Context를 저장
- [HW] Handler로 분기
- [SW] Pending Flag를 클리어 (Mute)
- [SW] 필요한 동작을 수행
- [HW] Context 복원
- 재개
예전에는 일반함수에서 복귀하는 명령어와 인터럽트에서 복귀하는 명령어가 따로 있었음. 그런데 C언어로 모든게 가능하도록 하다보니 인터럽트 전용 복귀 명령어를 사용하기 어려워졌음.
ㅤ
ㅤ
개고생
깃-똥차게 깃 관리하자
STM32CubeIDE를 깃으로 관리한다면, ioc 파일에서 발생하는 충돌이 가장 파급력이 컸던 것 같다. 핀 설정이 그냥 충돌나는 부분들을 확인하고 클릭으로 병합을 할 수 있는게 아니라, Input Pin의 개수 / Output Pin의 개수 등의 값을 직접 변경해줘야하는 수고로움이 크다. 심지어 이게 일반적인 텍스트파일이 아니라 프로젝트 파일이다보니, 잘못 수정하면 프로젝트 설정이 열리지 않거나 핀 세팅이 날아가버리는 문제가 발생한다.

ㅤ
또, BEGIN와 END 사이에 코드를 잘 작성하지 않으면 Code Generation 시점에 그냥 이 코드들을 다 삭제해버리는 괴랄한 CubeIDE의 특성상, 코드 병합 과정도 굉장히 까다로웠다.

ㅤ
내가 생각할 때는
- I/O Pin Configuration, CLK Configuration은 무조건 모여서 처음에 한다.
- 만약 불가피하게 핀 설정을 해야한다면 main 에서 수정 후, 수정된 ioc 관련 코드를 모두에게 동일하게 배포 (rebase든 머지든) 한다.
- main.c는 최대한 건드리지 않는다. 모두 각자 모듈 분리해서 작성한다. main에서 건드릴 수 있는건 최대한 줄여서 헤더 추가, while 문 내에 내 모듈 속 로직을 호출하는 함수 작성 정도로만 하자.
사실 이렇게 하면 아무래도 함수 호출이 최소 한 번 발생하니깐 성능상의 이슈가 발생할 수 있을 것 같은데,,, 이건 인라인 함수로 어떻게 처리가 안되나? (→ 이건 컴파일러의 판단임)
개발하고나서 사후 최적화? 이건 사실 말도 안되는 것 같은데. 무튼 ioc 파일과 싸워본 결과, 협업을 할 때는 성능이 필요한 시점에만 최적화를 적용하고 그외에는 모듈화를 하는게 더 좋겠다고 생각이 든다. 코드 합치기가 iOS 스토리보드보다 더 어렵다!!
ㅤ
IOC 와의 한 판 승부
왜 되지? 왜 안되지?
이번에 너무나도 수상한 현상이 있었다.
DHT11 온습도 센서를 이용해서 데이터를 주고받는 코드 + I2C 통신을 통해서 LCD에 데이터를 보내는 코드를 합치면 DHT11 모듈이 동작하지 않는 문제가 있었다.
ㅤ
그런데 모든 설정은 동일함.
ioc 파일에서 설정된 것들도 모두 동일함.
DHT11 코드와 I2C/LCD 코드를 합쳐보면 동작하지 않는데, 그대로 두고 DHT11의 main.c 파일의 코드로 갈아끼워 실행해보면 DHT가 동작한다. 분명 main 쪽 문제인데,,,

ㅤ
자동으로 만들어지는 똥
그러다가 결국 차이점을 찾았고, 이게 문제의 원인이라는 것도 파악을 했다.
DHT11 모듈이 동작하는 SystemCLK 설정 파일
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = 8;
RCC_OscInitStruct.PLL.PLLN = 168;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 4;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
{
Error_Handler();
}
}
ㅤ
DHT11 모듈이 동작하지 않는, I2C가 포함되었을 때의 SystemCLK 설정 파일
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE3);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
{
Error_Handler();
}
}
ㅤ
근데 의아했던건 여러 코드들을 왔다갔다하면서 문제가 무엇이었는지 찾아보고 있었는데, 분명 ioc 파일의 CLK 설정이나 이런 저런게 변경되는게 없이 멀쩡했었다. 그래서 “아 분명 이건 ioc 파일이 아니라 프로젝트 빌드 설정 (실제로 printf에서 float를 쓰기 위한 설정을 변경한다던가 하는 옵션을 체크하는 CPU CLK 값이 .cproject 파일 내에서 변경되는 것을 확인했다) 의 문제구나 라고 생각하고 막 찾아보고 있었다.
ㅤ
근데 아무리 테스트를 해봐도 이게 프로젝트 설정을 바꾼다고 해서 SystemCLK 값을 바꾸지는 않았다. FPU 설정이나 빌드 옵션같은 것들도 모두 의심했었는데 + I2C 활성화나 USART3 모듈 활성화, GPIO의 Pin Frequency Speed 변경 까지 모두 체크해봤는데도 이게 SysCLK 값을 바꿔주지는 않았다.
ㅤ
그러다가 혹시나 해서 DHT11 모듈이 잘 작동하던 코드에 가서 프로젝트 Refresh를 해봤는데, 갑자기 ioc 파일에서 설정값들이 분명 8MHz 였던 값들이 168MHz로 바뀌어있었다…. 어흑마이갓…
ㅤ
checkout을 하고나면 꼮 IDE Refresh를 해주자.

ㅤ
나의 삽질들…

ㅤ
그래서 앞으로는 ioc 파일은 프로젝트에 포함시키지 말까 고민하고있다. 이 설정값 건드리면 generate code 해버리면서 main.c에 들어가있던 코드들이 막 없어지고 난리가 나는 걸 경험하고서는, 이거 프로젝트에 쓰면 난리나겠다는 생각이 났다. ;ㅅ;
'TIL' 카테고리의 다른 글
| [260202] Day 173 - 뚫어라 Ad-Hoc의 벽 (0) | 2026.02.03 |
|---|---|
| [260201] Day 172 - 미루고 미뤘던 TIL과 2차 프로젝트의 진행 중 회고 (0) | 2026.02.02 |
| [251102] Day 83 - 시간이 많이 흘렀구려 (0) | 2025.11.02 |
| [251021] Day 71 - 겨울이 온 것 같아요 (0) | 2025.10.22 |
| [251020] Day 70 - 나 어쩌면 열심히 살았나봐 (1) | 2025.10.20 |