최근 내연기관에서 전기차로 넘어가는 것이 핫 토픽입니다.(한 물 간 거 같기도 하구요..ㅎ) 테슬라의 모델 3의 보급을 기점으로 많은 자동차 제조사들이 너도나도 전기차를 내놓기 시작했는데요. 사실 내연기관 개발보다 진입 장벽이 낮은 것은 사실입니다.
엔진을 개발한다는 것보다, 모터 + 배터리 기술을 개발하는 것은 친숙하니까요. (쉽다는 것은 아닙니다) 왜냐하면 배터리와 모터는 이미 우리 삶에서 많은 부분에서 쓰이고 있습니다. 예전에 잠깐이나마 LG에 차를 만든다는 찌라시가 돌았던 것도 LG에서는 전기차에 필요한 기술력을 다 가지고 있었기에 나왔던 말인 것 같습니다. 계속 LG전자로 예를 들어보면 모터의 경우 수많은 전자제품, 특히 세탁기에 들어가기에 모터제어 기술력을 가지고 있고 배터리는 스마트폰부터 해서 다양한 포터블 전자 장비에서 배터리 관리 기술을 보유하고 있습니다. 결국 제가 말씀드리고 싶은 말은 전기차를 개발한다는 것이 내연기관차를 개발하는 것보다 친숙한 기술이라는 이야기죠. 그렇기에 기존 자동차 제조사뿐만 아니라 다양한 회사에서도 시도를 하는 것입니다.
그러니 저도 도전해볼법하지 않을까요? ㅎㅎ 물론 RC카로 만들고 있지만 이 프로젝트가 끝나고 기회가 될 땐, 카트의 형태로도 만들어보고 싶습니다.
서론이 길었는데 아무튼 우리의 드림카를 만들기 위해서는 가장 기본적으로 OS(Operating System)을 구현해야 하는데요. 대부분의 자동차에 들어가는 OS의 경우 RTOS형태의 주기 Task들로 이루어져 있습니다. 여기서 주기 테스크가 핵심인데, 스케줄러는 그러한 주기 Task들을 정해진 타이밍에 맞춰 수행시켜주어야 하거든요. 제 자동차는 기본적으로 1ms를 기본 주기로 가져가고 1ms / 10ms / 100ms / 1 sec 테스크들을 구동할 계획입니다. 그러기 위해서는 우선 1ms 마다 동작하는 Timer를 구현해 주어야 합니다. 앞서 프로젝트에 대한 이야기를 했던 글에서는 ATmega2560(Arduino Mega)을 사용하기로 했었는데요. 개발을 하다 보니 이것저것 욕심이 생겨 STM사의 Nucleo-F401RE 보드로 넘어갔습니다. 아두이노도 물론 좋은 보드지만 Nucleo보드는 고려해야 할 것이 훨씬 많지만 퍼포먼스 적으로 훨씬 뛰어납니다. 단적으로 스펙만 놓고 비교해 보아도 아두이노의 경우 대부분 8-bit Microcontroller인데 nucleo보드의 경우 32-bit Microcontroller입니다. 클럭 또한 아두이노 메가의 경우 16 MHz인데, Nucleo-F401RE보드의 경우 84 MHz이죠. 무슨 소리 하는지 모르시겠다구요?
어떤 호수의 물을 퍼내야 한다고 가정해 봅시다. 아두이노 우노로 우물을 퍼내려면 8L짜리 양동이를 가지고 초당 16번 퍼낼 수 있는 셈입니다. 반대로 Nucleo보드로 퍼낼 경우 32L짜리 양동이를 가지고 84번 퍼낼 수 있는 셈이죠. 이렇게 예시를 들면 확 체감이 될까요? 물론 100% 매칭되는 비유는 아닙니다만 어느 정도 이런 느낌이다 생각하시면 될 거 같아요.
이제 개발하러 가봅시다. STM보드의 경우 STM 홈페이지에서 개발 IDE를 다운로드할 수 있습니다. CubeMXIDE를 가지고 개발해볼 거예요. 아주 편리한 게 각종 Peripheral들을 GUI환경에서 세팅하고 자동으로 Code까지 생성해 줍니다. 우리는 임베디드 지식만 가지고 있으면 어느 정도 세팅은 해주는 셈이죠. 프로젝트를 생성하고 나면 가장 먼저 해주어야 할 것이 Clock 세팅입니다. 아두이노의 경우 기본 Clock설정이 되어있어 우리가 별도로 신경 써줄 일이 없었지만, Nucleo의 경우 Clock이 나눠져서 각 모듈에 들어가기까지 상당히 복잡합니다. 거기까지 파고들기엔 너무 깊은 내용이니까 조금 얕게 우린 그저 Clock을 사용할 수 있는 최댓값으로 설정해 두고 나머지는 자동분배 Tool을 돌릴 겁니다.
자동 설정된 Clock을 유심히 보면 우리가 만들어야 되는 Timer에도 84 MHz가 들어가는 것을 볼 수 있는데요.
우린 이 84 MHz를 가지고 1ms를 만들어야 합니다. 1ms라 하면 주파수로 변경했을 때, 1kHz입니다. 그럼 이제 딱 각 나왓죠. 84,000,000 /?? = 1,000를 만들면 된다는 이야기인데, 여기서?? 의 역할이 바로 Prescaler입니다. Prescaler란 번역 하면 분주기라고 표현합니다. 이 분주기는 입력받은 Clock을 특정 설정 값만큼 마다 걸러서 출력해 주는 역할을 합니다.
다시 말해 Prescaler가 3으로 설정되어 있을 경우 4번의 Clock마다 하나씩 통과시켜주는 것입니다. (0부터 세기 때문에 4번마다 한 번씩 통과시켜 주는 겁니다. 0, 1, 2, 3... 0, 1, 2, 3...) 그럼 Prescaler를 통과한 Clock은 Counter Resgister를 증가시키는 역할을 하는데 이 Counter Register가 Max값에 도달하면 우리가 원하는 Timer Alarm이 울리게 되는 거죠. 이걸 정리해 보면, 84,000,000 클럭이 들어오는데 이걸 Prescaler에 통과시킨 후, 통과된 Clock기준으로 목푯값에 달할 때마다 Alarm이 울리는데 이 Period가 1ms(= 1kHz) 면 되는 거죠? 이를 수식으로 바꿔보면,
가 나옵니다! 이제 여기서 자유롭게 Prescaler값과 Counter Register Max값을 설정해 주면 됩니다. 두 값을 설정할 수 있는 범위는 매우 넉넉하니까 정말 사용자가 편한 대로 설정하시면 됩니다. (Prescaler: 0 ~ 65,535 / Counter Register Max: 0 ~ 4,294,967,295)
저는 딱 떨어지는 숫자가 좋아서 Prescaler는 83, Counter Period는 999를 선택했습니다. 이렇게 설정하고 Interrupt설정까지 활성화시켜준 뒤 Code를 생성해 주면 끝입니다!
근데 우리가 1ms를 눈으로 쫒을 수 있는 방법이 없잖아요? 그래서 저는 1ms마다 하나씩 카운팅해 1000번에 한 번씩 LED를 토글 시켜보려고요. 그럼 결국에는 1 sec마다 LED가 깜빡이겠죠? 이 로직은 어디 구현해야 하느냐! 바로 weak로 간이 함수가 만들어져있는데 내가 필요한곳에 재선언 해줄 경우 해당 ISR이 수행된다고 합니다.
static uint16_t vSchTask_Cnt = 0;
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){
if(htim->Instance == TIM2){
vSchTask_Cnt++;
if(vSchTask_Cnt == 1000){
vSchTask_Cnt = 0;
HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
}
}
}
저 HAL_TIM_PeriodElapsedCallback함수가 바로 1ms 주기가 되면 수행되는 친구입니다. 저 함수가 수행되었을 때, 우리가 설정한 타이머에 의해서 울렸는지 확인하고 1sec마다 LED를 토글 하도록 설계를 하면 됩니다.
중요한걸 하나 빠트렸는데, main loop가 돌기 전에 HAL_TIM_Base_Start_IT(&htim2) 함수를 수행시켜 주어야 Timer와 Interrupt가 수행됩니다!
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();
MX_USART2_UART_Init();
MX_TIM2_Init();
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim2);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
오늘 개발일지는 여기까지이며 해당 동작에 대해서는 아래 영상을 통해 확인해 주세요~ 다음 개발일지 때 뵙겠습니다.
Project Github 주소: https://github.com/DongjinJ/Whale_Mk1
GitHub - DongjinJ/Whale_Mk1
Contribute to DongjinJ/Whale_Mk1 development by creating an account on GitHub.
github.com
유튜브 주소: https://youtu.be/LpYagMiTDps
'프로젝트 일지 > Whale Car' 카테고리의 다른 글
[Whale Car] STM32H745ZIQ 보드 연결 (0) | 2024.10.03 |
---|---|
[Whale Car] 개발 보드 변경 (2) | 2024.09.19 |
[Whale Car] Serial 통신 뚫기 (1) | 2024.04.08 |
[Whale Car] Scheduler 만들기 (0) | 2023.06.19 |
[Whale Car] RC카 만드는 이야기 (0) | 2023.05.29 |