프로그래밍/STM32

[STM32] Timer 설정하기(feat. 이론)

Beginner:) 2023. 10. 7.
320x100

시작하기 전에 전자전공도 아니고 관련 지식이 없는 상태에서 공부를 하기 위해 구글링하며 이글 저글 짜집기하다 도출된 주관적인 결론으로 틀릴 확률이 높으니 참고만 하시길...

+) 피드백 주시면 정말 정말 감사합니다.

 

먼저 나의 환경은 Ubuntu 20.04, NUCLEO-F411RE

1. 이론

Timer는 어떻게 작동하느냐? Clock, Prescaler, Count Period의 개념을 먼저 알아야한다.

 

Clock(클럭)

만약에 1초에 84MHz의 클럭이 발생한다면 1초에 84,000,000의 신호(High/Low)가 발생한다는 뜻인데

그렇다면 84,000,000번째의 신호를 받았다면 그건 1초가 지났다는 뜻이다.

 

그런데 84,000,000의 신호를 모두 인터럽트 등으로 처리를 한다면 그건 불필요한 정보이지 않을까?

아마 CPU에 불필요한 처리가 발생할 것이고 성능의 비효율로 이어질 것이다.

(또한 채널이 여러 개일 때 딱 1초를 만들기 힘든 경우도 있다 -> 프로그래밍 관점으로 무한소수를 표현을 못하는 경우와 비슷)

 

그것을 완화시키기 위한 것이 Prescaler이다.

 

Prescaler(프리스케일러)

Prescaler란 몇 번의 신호를 묶어서 하나의 인터럽트신호로 만드는 것이다, (Prescaler Timer Register를 1 증가시킨다)

예를 들어 Prescaler를 84로 지정하게 되면 "84번의 신호를 하나의 신호로 설정하겠다"이다.

그렇다면 84,000,000의 신호를 84로 나누게 되면 1,000,000, 즉 1초에 1,000,000번의 신호를 전달하게 된다.

정확하게는 Prescaler Timer Register는 84로 세팅되어 있고 clock이 들어올 때마다 1씩 감소하여 0이 되는 순간 Count Register(Count priod가 이걸 읽음)에 인터럽트를 발생시킨다.

 

Count Period

Count Period는 Prescaler의 신호를 카운트하는 것인데, 만약 Count Period를 1000으로 설정한다면, 1000번의 신호가 들어왔을 때 PWM의 신호를 발생하겠다는 것이다.

 

정리하자면 1초에 84,000,000의 Clock이 발생하여 Prescaler Timer Register에 인터럽트를 발생시키고,

Prescaler Timer Register는 84번(Prescaler 설정 값)마다 Count Register에 인터럽트를 발생시키고,

Count Register는 1000번(Count Period)마다 1PWM 신호를 발생시킨다.

 

결론적으로 1,000번의 PWM이 발생하면 1초로 판단한다.

 

2. 설명

TIM1을 사용할 건데 TIM1은 APB2와 연결되어 있는 것을 볼 수 있다.

 

 

3. 설정

먼저 HAL_Delay는 1ms가 최소단위인데 0.1ms까지 체크하는 Delay 함수를 만들어 보는 것을 목표로 한다.

(확인할 방법은 없다...)

 

클럭은 ST-LINK MCO를 사용하여 [RCC] - [HSE]-"Crystal/Ceramic Resonaotr"를 선택한다.

 

 

 

[Clock Configuration]에서 HSE, PLLCLK를 선택하고 APB2 timer clocks를 확인한다. 84MHz에 맞게 수정해도 되고

84가 아니더라도 윗글에서 [1. 이론]을 이해하였다면 충분히 프로그래밍적으로 수정할 수 있다.

 

CubeMX에서 [Pinout & Configuration] - [Timers] - [TIM1]에서 [Clock Source]를 "Internal Clock"으로 선택하고 [Parameter Settings]에 [Prescaler]를 "8400-1", [Counter Period]를 "1000-1"로 설정한다. 

-1을 하는 이유는 0부터 카운팅 되기 때문이다 

 

TIM1에 대한 global interrupt도 설정하고 "Generate Code"를 클릭한다.

 

 

4. 코드 작성

먼저 "stm32f4xx_hal_tim.h"를 include하고 "HAL_TIM_PeriodElapsedCallback"을 재정의한다.

 

인터럽트가 발생하면 timer_count를 1증가하고,

0.1ms당 1인터럽트이니 timer_count가 10000이 되는 순간 print문을 출력하고 timer_count는 다시 0으로 초기화한다.

 

참고로 나의 경우 dma를 설정하고 지우지 않았는지 오류가 나타나 "stm32fxx_hal_dma.h"를 포함했는데,

오류가 나지 않는다면 삭제해도 무방하다.

#include "stm32f4xx_hal_dma.h"
#include "stm32f4xx_hal_tim.h"

static unsigned short timer_count=0;

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){
    timer_count += 1;
    
    if(timer_count >= 10000){
        printf("timer interrupt!\n");
        timer_count = 0;
    }
}

 

""HAL_TIM_Base_Start_IT" 명령어를 만나면 인터럽트 감지를 시작한다.

인터럽트의 print문이 1초마다 발생하는지 확인하기 위해 HAL_Delay를 이용하여 1초마다 print문을 작성하자

HAL_TIM_Base_Start_IT(&htim1);

while (1)
{
    printf("HEL_Delay 1sec!\n");
    HAL_Delay(1000);
}

 

 

 

 

5. 결과

실행을 해보면 main의 while문과 interrupt callback 함수에서 1초마다 동일하게 print를 하는 것을 볼 수 있다.

혹시나 0.1초마다 print를 찍고 싶다면 포기하자. printf 자체가 시간이 오래 걸리는 작업이기 때문에 불가능하다. 

 

 

다시 한번 피드백 주시면 정말 정말 감사합니다.

반응형

댓글