FW를 개발하다보면 timer를 이용을 하게 된다.
HAL에서 system tick을 이용하면 1ms마다 처리를 할 수 있지만, 그 이하의 분해능이 필요하기 마련이다.
만약 10us간 sleep을 요한다면 다른 처리가 필요하다.
그렇다면 이건 timer를 이용하여 1us간 timer를 구현을 해야한다.
나는 10us의 정밀성이 있는 timer가 필요하여 timer를 이용해야 했으며 그 방법에 대하여 기술을 하려한다.
나는 STM32F765를 사용했으니 참고하시길...
일단 STM32에는 여러가지의 timer가 있다.
advenced-control, general purpose, basic 3종류가 있다. 이부분은 다음에 설명하도록...
일단 제일 잡기능이 없는 basic timer를 사용하도록하자...
그럼 어디가 basic tiemr이냐를 확인하면
tim6, tim7이 basic timer이다.
그러면 다시 정확한 clock을 확인해보자.
아... 간편하다 STM32 cube max에사 위와 같이 설정되어있는걸 확인할 수 있다.
위와 같이 data sheet를 보면 tim6는 ABP1에 물려있으니 108MHz이다.
data sheet를 찾기 귀찮으면
HAL에서 code를 생성하면
#define TIM6 ((TIM_TypeDef *) TIM6_BASE)
라고 나와있다.
TIM6_BASE는
#define TIM6_BASE (APB1PERIPH_BASE + 0x1000UL)
이니 ABP1인것을 확인하고 각자의 분주비의 맞게 자기의 분주비를 확인할 수 있다.
그럼 지금의 예제 코드의 경우 108MHz이다.
나는 10ns의 분해능이 필요했기에 아래와 같은 설정을 했다.
이제 TIM6의 정보를 보자.
Counter register과 Prescaler counter가 보인다. 각자 0부터 시작한다.
108MHz에서 Prescaler counter값을 통해 Counter register값이 증가한다.
Prescaler counter는 0부터 증가하며 한 클럭마다 1씩증가한다.
108MHz이니 1080 - 1을 나누면 100KHz마다 Prescaler counter가 증가하게 된다.
즉 Counter registrer는 100KHz마다 증가하게된다. 즉 counter register는 10us마다 증가를 하게 된다.
Counter register는 10000번증가하면 Update event가 발생하며 0으로 초기화한다.
그럼 10000 - 1을 하면 10Hz마다 Update event가 발생을 하게 된다.
여기까지 이해가 되었다면 코드를 cube max에서 만들도록하자.
static void MX_TIM6_Init(void)
{
TIM_MasterConfigTypeDef sMasterConfig = {0};
htim6.Instance = TIM6;
htim6.Init.Prescaler = 1080 - 1; // 108MHz / 1080 = 100Khz
htim6.Init.CounterMode = TIM_COUNTERMODE_UP;
htim6.Init.Period = 10000 - 1; // 10ns * 10000 = 100ms, 100Khz / 10000 = 10hz
htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim6) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim6, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
}
이러한 코드가 생성이 될것이다.
그러면 인터럽트를 활성화 시키는 코드를 넣고
HAL_TIM_Base_Start_IT(&htim6);
아래와 같은 코드를 추가한다.
void TIM6_DAC_IRQHandler(void)
{
if ((__HAL_TIM_GET_FLAG(&htim6, TIM_FLAG_UPDATE) != RESET) && \
(__HAL_TIM_GET_IT_SOURCE(&htim6, TIM_IT_UPDATE) != RESET))
{
__HAL_TIM_CLEAR_IT(&htim6, TIM_IT_UPDATE);
HAL_GPIO_TogglePin(ST_LED_GPIO_Port, ST_LED_Pin);
bd_timestamp += (10000 - backup_timer);
backup_timer = 0;
}
}
그러면 스코프로 찍으면
5Hz가 나오는 것을 볼 수있다.
그러면 시간이 필요할 때 마다
uint64_t get_timestamp(void)
{
uint64_t tmp_timer = __HAL_TIM_GetCounter(&htim6);
bd_timestamp += (tmp_timer - backup_timer);
backup_timer = tmp_timer;
return server_timestamp + (bd_timestamp * 10U); // server_timestamp is usec
}
와 같이 호출하면 시간을 얻어 올 수있다.
server_timerstamp는 현재 시간값을 얻어올 수 있는 곳에서 다른 통신을 통해 가져오도록...
그리고 다음 장으로 넘어가보자!
'FW' 카테고리의 다른 글
LPF(low pass filter) (0) | 2022.01.27 |
---|---|
stm32 HAL timer(2) (0) | 2021.12.04 |
IAR Heap Stack size 조절 (0) | 2021.09.19 |
Hanning window (0) | 2021.09.19 |
FFT C code 구현 및 excel로 확인 (0) | 2021.08.28 |