STM32 타이머 입력 캡처 실험 - PWM 주파수 측정 및 PWMI 모드로 주파수와 듀티비 측정

입력 캡처概述

STM32 타이머의 입력 캡처(Input Capture) 기능은 외부 신호의 주파수와 펄스 폭을 측정하는 데 사용된다. 본 문서에서는 두 가지 실험을 다루는데, 첫 번째는 PWM 신호의 주파수만 측정하는 방법이고 두 번째는 PWMI(PWM Input) 모드를 사용하여 주파수와 듀티비를 동시에 측정하는 방법이다.

입력 캡처 관련 함수

stm32f10x_tim.h 헤더파일에 정의된 입력 캡처 관련 주요 함수들:

함수 원형기능
TIM_ICInit()타이머 입력 캡처 초기화
TIM_ICStructInit()입력 캡처 구조체를 기본값으로 초기화
TIM_PWMIConfig()PWMI 모드로 두 채널을 동시에 구성
TIM_PrescalerConfig()프리스케일러 설정
TIM_SelectInputTrigger()트리거 소스 선택
TIM_SetICxPrescaler()입력 캡처 채널 프리스케일러 설정
TIM_GetCapturex()캡처 값 읽기
TIM_SelectOutputTrigger()출력 트리거 소스 선택
TIM_SelectSlaveMode()슬레이브 모드 선택

실험1 - 입력 캡처로 주파수 측정

회로 연결

신호 입력 핀은 PA6이며, 측정할 PWM 신호는 STM32 자체에서 생성하여 PA0 핀으로 출력한다.

구성 단계

  1. RCC 클럭 활성화 - GPIO와 타이머 클럭 활성화
  2. GPIO 초기화 - 입력 모드로 설정(풀업 또는 플로팅 입력)
  3. 타이머 기본 유닛 설정 - 내부 클럭 소스를 사용하여 CNT 카운터 동작
  4. 입력 캡처 유닛 설정 - 필터, 극성, 채널 연결, 분주기 등 구성
  5. 슬레이브 트리거 소스 선택 - TI1FP1을 트리거로 설정
  6. 슬레이브 모드 설정 - Reset 모드로 설정하여 카운터 재설정
  7. 타이머 활성화

필터와 분주기의 차이점

필터는 신호의 원래 주파수를 변경하지 않고 고주파 노이즈만 제거한다. 예를 들어 1KHz 신호는 필터링 후에도 1KHz로 유지된다. 반면 분주기는 신호 자체를 분주하므로 1KHz 신호를 2분주하면 500Hz가 된다.

소스 코드

PWM.c

#include "stm32f10x.h"
#include "PwmGenerator.h"

void PWM_Init(TIM_TypeDef* TIMx)
{
    if(TIMx == TIM2)
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    TIM_InternalClockConfig(TIMx);
    
    TIM_TimeBaseInitTypeDef TimeBaseInitStruct;
    TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
    TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
    TimeBaseInitStruct.TIM_Period = 100 - 1;
    TimeBaseInitStruct.TIM_Prescaler = 720 - 1;
    TimeBaseInitStruct.TIM_RepetitionCounter = 0;
    TIM_TimeBaseInit(TIMx, &TimeBaseInitStruct);
    
    TIM_OCInitTypeDef TIM_OCInitStruct;
    TIM_OCStructInit(&TIM_OCInitStruct);
    TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStruct.TIM_Pulse = 10;
    TIM_OC1Init(TIMx, &TIM_OCInitStruct);
    
    TIM_Cmd(TIMx, ENABLE);
}

void PWM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare)
{
    TIM_SetCompare1(TIMx, Compare);
}

PWM.h

#ifndef __PWM_GENERATOR_H__
#define __PWM_GENERATOR_H__

void PWM_Init(TIM_TypeDef* TIMx);
void PWM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare);

#endif

IC.c

#include "stm32f10x.h"
#include "InputCapture.h"

void IC_Init(void)
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    TIM_InternalClockConfig(TIM3);
    
    TIM_TimeBaseInitTypeDef TimeBaseInitStruct;
    TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
    TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
    TimeBaseInitStruct.TIM_Period = 65536 - 1;
    TimeBaseInitStruct.TIM_Prescaler = 72 - 1;
    TimeBaseInitStruct.TIM_RepetitionCounter = 0;
    TIM_TimeBaseInit(TIM3, &TimeBaseInitStruct);
    
    TIM_ICInitTypeDef TIM_ICInitStruct;
    TIM_ICStructInit(&TIM_ICInitStruct);
    TIM_ICInitStruct.TIM_Channel = TIM_Channel_1;
    TIM_ICInitStruct.TIM_ICFilter = 0xF;
    TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising;
    TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;
    TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI;
    TIM_ICInit(TIM3, &TIM_ICInitStruct);
    
    TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);
    TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);
    
    TIM_Cmd(TIM3, ENABLE);
}

uint16_t IC_GetFrequency(void)
{
    return 1000000 / (TIM_GetCapture1(TIM3) + 1);
}

IC.h

#ifndef __INPUT_CAPTURE_H__
#define __INPUT_CAPTURE_H__

void IC_Init(void);
uint16_t IC_GetFrequency(void);

#endif

main.c

#include "stm32f10x.h"
#include "oled.h"
#include "PwmGenerator.h"
#include "InputCapture.h"

int main(void)
{
    OLED_Init();
    OLED_ShowString(1, 1, "Frequency:");
    
    PWM_Init(TIM2);
    PWM_SetCompare1(TIM2, 80);
    
    IC_Init();
    
    while(1)
    {
        OLED_ShowNum(2, 1, IC_GetFrequency(), 5);
    }
    
    return 0;
}

실험 결과

논리 분석기로 측정된 결과, 주파수 2000Hz, 듀티비 80%의 PWM 신호가 확인되었다.

실험2 - PWMI 모드로 주파수와 듀티비 측정

PWMI(PWM Input) 모드는 하나의 타이머 채널에서 상승엣지와 하락엣지를 모두 캡처하여 주파수와 듀티비를 동시에 측정할 수 있다.

주요 구현 코드

#include "stm32f10x.h"
#include "InputCapture.h"

void IC_Init(void)
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    TIM_InternalClockConfig(TIM3);
    
    TIM_TimeBaseInitTypeDef TimeBaseInitStruct;
    TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
    TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
    TimeBaseInitStruct.TIM_Period = 65536 - 1;
    TimeBaseInitStruct.TIM_Prescaler = 72 - 1;
    TimeBaseInitStruct.TIM_RepetitionCounter = 0;
    TIM_TimeBaseInit(TIM3, &TimeBaseInitStruct);
    
    TIM_ICInitTypeDef TIM_ICInitStruct;
    TIM_ICStructInit(&TIM_ICInitStruct);
    TIM_ICInitStruct.TIM_Channel = TIM_Channel_1;
    TIM_ICInitStruct.TIM_ICFilter = 0xF;
    TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising;
    TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;
    TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI;
    
    TIM_PWMIConfig(TIM3, &TIM_ICInitStruct);
    
    TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);
    TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);
    
    TIM_Cmd(TIM3, ENABLE);
}

uint16_t IC_GetFrequency(void)
{
    return 1000000 / (TIM_GetCapture1(TIM3) + 1);
}

uint16_t IC_GetDutyCycle(void)
{
    return (TIM_GetCapture2(TIM3) + 1) * 100 / (TIM_GetCapture1(TIM3) + 1);
}

주요 특징

  • TIM_PWMIConfig() 함수를 사용하면 채널1의 구성에 따라 채널2가 자동으로 역극성으로 설정됨
  • 캡처1(CCR1)에서 전체 주기 값이 저장됨
  • 캡처2(CCR2)에서 하이 레벨 시간 값이 저장됨
  • 듀티비 계산식: (CCR2 + 1) / (CCR1 + 1) × 100%

주파수 및 듀티비 계산 원리

타이머 클럭이 1MHz로 설정된 경우(72MHz / 72 = 1MHz), 카운터 값 1은 1μs를 의미한다. 따라서 주파수는 1,000,000 / (CCR1 + 1) Hz로 계산되고, 듀티비는 (CCR2 + 1) / (CCR1 + 1) × 100%로 계산된다.

태그: STM32 TIM PWM Input Capture Frequency Measurement

6월 4일 22:35에 게시됨