1ms Timer가 정상 작동 하는 것을 보았으니, Scheduler를 다시 심어보자. 이전에 다루었었던 내용이니 내용적인 부분은 넘어가도록 하겠다.
https://enginbear.tistory.com/29
[Whale Car] Scheduler 만들기
이번 시간에는 지난 시간 만든 1ms Timer Interrupt를 활용해서 Scheduler를 만들어 봅시다. 임베디드에서는 Scheduler가 심장이라고 해도 과언이 아닙니다. 임베디드 위에서 올라가있는 모든 로직들의 일
enginbear.tistory.com
근데 한 가지 문제점이 있다. Dual Core 프로젝트의 경우에 하나의 elf가 만들어질 것이라 생각했는데, 두 개의 elf를 올리는 방식이다. 즉 그 말은 하나의 프로젝트 안에 두 개의 빌드 타깃이 있다는 건데, 이 둘은 심지어 메모리 영역도 따로 쓰고 있다. 여기서 발생하는 문제는 바로 공용으로 쓰는 함수는 존재하는데, 두 빌드 타깃에 동일한 파일을 선언하는 공수가 든다. 근데 프로젝트를 잘 까보니 Common이라는 폴더가 맨 위에 존재하게 되는데 해당 폴더를 활용해서 Code 공용화를 수행하면 될 것 같다.
실제 폴더 구조가 위와 같은 Tree로 구성되어 있어 별 다른 작업이 필요하지는 않았다. 헤더 선언만 잘 잡아주면 문제없다. 자 이제 기 구현되어 있던 스케줄러 틀을 가지고 와서 집어넣어 보자. 사실 Service Module을 복사 붙여 넣기 하여 이식하면 끝날 문제지만 직접 이전에 생각했던 콘셉트를 되짚어보며 작성하면 눈에 거슬리는 부분들이 생긴다. 직접 타이핑을 해서 옮기다 보면 그런 부분을 개선할 수 있어 좋다. 사실 크게 달라진 부분은 없고 두 Core에서 Timer가 2개가 돌고 있으니 각 Core에서 Scheduler를 따로 돌리면 된다.
우선 여러 Task를 만들기 이전에 1sec짜리 Sample Task를 만들어 전역변수를 증가시켜 보자. 물론 눈으로 보았을 때, 1 sec인지 확인하기는 어려워 영상 편집 프로그램을 통해서 Time Code를 심어 확인해 보니 미세한 시간 오차가 존재했다. 동영상을 업로드하여 확인시켜주려 했으나 용량 문제로 업로드가 불가하다ㅠ 찜찜한 마음을 확실하게 해결하고 넘어가기 위해 Toggle 핀을 심어 오실로로 찍어보자.
오실로로 보았을 때 정확히 1 sec마다 토글 되는 걸 보았으니 안심해도 될 것 같다.
지금까지는 이전에 다루었던 스케줄러 내용을 포팅하는 과정이라 지루했을 것이다. 새로운 주제로 넘어가 보자. 회사에 입사한 지 얼마 안 되었을 때, Micom의 CPU Load를 측정하는 로직을 구현하라는 요청을 받았었다. 우리 코드와 같이 주기 Task들로 이루어진 스케줄러 위에서는 CPU Load라는 것을 측정할 수가 있다.
CPU Load 측정을 위해서는 각 함수 별로 수행 시간이 측정되어야 하는데, 그렇기 위해서는 1ms보다 작은 단위로 시간이 측정 가능하여야 한다.
"시간을 어떻게 측정할 것인가?"에 대한 해답은 가까이에 존재한다. 우리는 1ms Timer를 만들기 위해 Timer를 세팅하였었다. Timer Setting 값을 보면 아래와 같이 1 MHz로 동작한다. -> (200 / (1+199)) MHz
그렇단 이야기는 Timer는 1 Tick이 증가할 때마다 1us의 시간을 갖는다는 의미이다. 그럼 우린 이 Timer의 Tick값을 함수가 실행되기 전 후로 측정하여 빼면 몇 us동안 함수가 수행되었는지 알 수가 있다. 그리고 모든 함수가 수행되는 시간을 누적한 뒤 1ms 마다 이를 1000으로 나누게 되면 1ms당 함수가 수행된 시간의 비율을 구할 수 있고 여기에 100을 곱하면 CPU Load의 백분율이 나오는 것이다. 직접 Code를 보고 이해해 보자.
uint32_t SchMain_GetTime_us(uint8_t core_id){
TIM_HandleTypeDef htim;
uint32_t time_us;
if(core_id == SCHMAIN_CORE_M4){
htim.Instance = TIM2;
}
else if(core_id == SCHMAIN_CORE_M7){
htim.Instance = TIM5;
}
else{
htim.Instance = TIM2;
}
time_us = htim.Instance->CNT;
return time_us;
}
void PrjSch_Main(void){
uint8_t i;
uint32_t time_us;
for(i = 0U;i < PRJSCH_TASK_M7_NUM;i++){
if(vPrjSch_M7_Task[i].activation == 1U){
time_us = SchMain_GetTime_us(SCHMAIN_CORE_M7);
vPrjSch_M7_Task[i].func_ptr();
vPrjSch_M7_Task[i].activation = 0U;
vPrjSch_M7_Task[i].exe_time_us = (uint16_t)(SchMain_GetTime_us(SCHMAIN_CORE_M7) - time_us);
vPrjSch_M7_ExeTime_us = vPrjSch_M7_Task[i].exe_time_us;
break;
}
}
}
uint32_t PrjSch_Get_TotalExeTime_us(void){
uint32_t total_exe;
total_exe = vPrjSch_M7_ExeTime_us;
vPrjSch_M7_ExeTime_us = 0U;
return total_exe;
}
void AppMon_Cal_CpuLoad_M7(void){
vAppMon_CpuLoad_M7 = (uint16_t)(PrjSch_Get_TotalExeTime_us() / 100U); // Tenths Place Value [Unit: 0.1]
}
프로젝트 코드의 일부를 발췌한 것이니 각 라인 별 해석은 숙제로 맡기도록 하겠다. 이렇게 구현된 CPU Load 측정은 1ms마다 수행이 될 것이며, 0.1의 단위로 표기가 될 것이다. 예를 들어 실제 30.5% 일 경우 305로 표기가 된다. float 변수를 쓰기 싫어서 이렇게 작성했다. 추후 이것저것 로직이 추가될 때, CPU Load가 너무 넘쳐서 Task들이 밀리는 문제 등을 디버깅할 때 용이할 것이다.
'프로젝트 일지 > Whale Car' 카테고리의 다른 글
[Whale Car] Motor 제어 - PWM (0) | 2025.03.05 |
---|---|
[Whale Car] Core간 Communication (0) | 2024.11.27 |
[Whale Car] 1ms Timer의 늪 (5) | 2024.10.15 |
[Whale Car] STM32H745ZIQ 보드 연결 (0) | 2024.10.03 |
[Whale Car] 개발 보드 변경 (2) | 2024.09.19 |