PWM 은 통신용으로 개발된 기술이었으나 전류,전압 제어용으로 탁월한 방식이어기 때문에 현재는 통신보다는 DC쪽 전력제어나 모터 제어 쪽에 쓰이는 모듈이다.
해당 글에서는 Generic Timer Module (GTM) 의 in-build 된 Timer Output Module 을 사용하여 PWM 신호를 만들어보겠다.
void Driver_Gtm_Init(void)
{
/* disable interrupts */
boolean interruptState = IfxCpu_disableInterrupts();
/** - GTM clocks */
Ifx_GTM *gtm = &MODULE_GTM;
g_GtmTomTimer.info.gtmFreq = IfxGtm_Cmu_getModuleFrequency(gtm);
IfxGtm_enable(gtm);
/* Set the global clock frequencies */
IfxGtm_Cmu_setGclkFrequency(gtm, g_GtmTomTimer.info.gtmFreq);
g_GtmTomTimer.info.gtmGclkFreq = IfxGtm_Cmu_getGclkFrequency(gtm);
/** - Initialise the GTM part */
Driver_Tom0_Init();
/* enable interrupts again */
IfxCpu_restoreInterrupts(interruptState);
IfxGtm_Cmu_enableClocks(gtm, IFXGTM_CMU_CLKEN_FXCLK | IFXGTM_CMU_CLKEN_CLK0);
}
Ifx_GTM *gtm = &MODULE_GTM;
g_GtmTomTimer.info.gtmFreq = IfxGtm_Cmu_getModuleFrequency(gtm);
IfxGtm_enable(gtm);
GTM 모듈을 불러오고
GTM 의 클럭을 가지고 오고 있다.
Cmu 는 Clock management Unit 의 약자이다.

sys_clk로 클럭이 들어오고 있다.

GTM Module 로 fgtm 과 fspb가 들어가는것을 볼 수 있다.
IFX_INLINE float32 IfxScuCcu_getGtmFrequency(void)
{
return IfxScuCcu_getSourceFrequency() / SCU_CCUCON1.B.GTMDIV;
}
IfxGtm_Cmu_getModuleFrequency를 타고 타고 들어가면 결국 fgtn 클럭을 사용하는것을 볼 수 있다. 100Mhz이다.
IfxGtm_enable(gtm);
void IfxGtm_enable(Ifx_GTM *gtm)
{
uint16 psw = IfxScuWdt_getCpuWatchdogPassword();
IfxScuWdt_clearCpuEndinit(psw);
gtm->CLC.B.DISR = 0;
IfxScuWdt_setCpuEndinit(psw);
}
gtm->CLC.B.DISR 레지스터를 0으로 바꾼다.
CLC는 Clock Control register의 약자로

GMT Module을 enable 시켜준다.
/* Set the global clock frequencies */
IfxGtm_Cmu_setGclkFrequency(gtm, g_GtmTomTimer.info.gtmFreq);
g_GtmTomTimer.info.gtmGclkFreq = IfxGtm_Cmu_getGclkFrequency(gtm);
void IfxGtm_Cmu_setGclkFrequency(Ifx_GTM *gtm, float32 frequency)
{
float32 f;
float32 bestDistance = frequency;
float32 fIn = IfxGtm_Cmu_getModuleFrequency(gtm);
uint32 z, n, nBest = 1, zBest = 1;
float32 t;
#if 1
for (z = 1; z < 0xFFFFFF; z++)
{
boolean endLoop = FALSE;
t = fIn / z;
for (n = z; n > 0; n--)
{
float32 distance;
f = t * n;
distance = fabsf(frequency - f);
if (distance < bestDistance)
{
bestDistance = distance;
nBest = n;
zBest = z;
}
if (bestDistance < 0.1)
{
endLoop = TRUE;
break;
}
}
if (endLoop)
{
break;
}
}
uint16 psw = IfxScuWdt_getCpuWatchdogPassword();
IfxScuWdt_clearCpuEndinit(psw);
gtm->CMU.GCLK_NUM.B.GCLK_NUM = zBest;
gtm->CMU.GCLK_NUM.B.GCLK_NUM = zBest; /* write twice to be sure */
gtm->CMU.GCLK_DEN.B.GCLK_DEN = nBest;
IfxScuWdt_setCpuEndinit(psw);
}
float32 IfxGtm_Cmu_getGclkFrequency(Ifx_GTM *gtm)
{
float32 N = gtm->CMU.GCLK_DEN.B.GCLK_DEN;
float32 Z = gtm->CMU.GCLK_NUM.B.GCLK_NUM;
float32 multiplier = N / Z;
return IfxGtm_Cmu_getModuleFrequency(gtm) * multiplier;
}
GTM 의 글로벌 클럭(GCLK) 을 설정하는 함수이다.
GLCK는 GTM 내부의 다양한 서브모듈 (TOM,ATOM 등) 이 사용할 클럭 소스로, 이를 설정하는 코드이다.
static void Driver_Tom0_Init(void)
{
{ /* GTM TOM configuration */
IfxGtm_Tom_Timer_Config timerConfig;
IfxGtm_Tom_Timer_initConfig(&timerConfig, &MODULE_GTM);
timerConfig.base.frequency = 100;
timerConfig.base.isrPriority = 0u;
timerConfig.base.minResolution = (1.0 / timerConfig.base.frequency) / 1000;
timerConfig.base.trigger.enabled = FALSE;
timerConfig.tom = IfxGtm_Tom_0;
timerConfig.timerChannel = IfxGtm_Tom_Ch_0;
timerConfig.clock = IfxGtm_Tom_Ch_ClkSrc_cmuFxclk1;
timerConfig.triggerOut = &IfxGtm_TOM0_0_TOUT76_P15_5_OUT;
timerConfig.base.trigger.outputEnabled = TRUE;
timerConfig.base.trigger.triggerPoint = 0u; /*Default 0 duty*/
timerConfig.base.trigger.risingEdgeAtPeriod = TRUE;
IfxGtm_Tom_Timer_init(&g_GtmTomTimer.drivers.timerOneMs, &timerConfig);
IfxGtm_Tom_Timer_run(&g_GtmTomTimer.drivers.timerOneMs);
}
}
이 모듈도 마찬가지이다.
IfxGtm_Tom_Timer_Config timerConfig;
IfxGtm_Tom_Timer_initConfig(&timerConfig, &MODULE_GTM);
초기 설정값을 불러온다.
timerConfig.base.frequency = 100;
timerConfig.base.isrPriority = 0u;
timerConfig.base.minResolution = (1.0 / timerConfig.base.frequency) / 1000;
timerConfig.base.trigger.enabled = FALSE;
timerConfig.tom = IfxGtm_Tom_0;
timerConfig.timerChannel = IfxGtm_Tom_Ch_0;
timerConfig.clock = IfxGtm_Tom_Ch_ClkSrc_cmuFxclk1;
timerConfig.triggerOut = &IfxGtm_TOM0_0_TOUT76_P15_5_OUT;
timerConfig.base.trigger.outputEnabled = TRUE;
timerConfig.base.trigger.triggerPoint = 0u; /*Default 0 duty*/
timerConfig.base.trigger.risingEdgeAtPeriod = TRUE;
timerConfig.base.frequency를 100으로 설정하였다. timerConfig.base.frequency는 타이머가 1초에 몇번 PWM을 발생시킬지 정하는 변수이다. 즉 100Hz로 주기가 10ms 마다 인터럽트를 발생시킨다.
isrPriority는 0으로 설정하였다. 인터럽트를 비활성화하고 Task에서 PWM을 10ms간격으로 생성하겠다.
minResolution은 타이머가 제어할 수 있는 최소 시간 단위를 설정하는 것이다. 10us 면 타이머는 10us의 간격으로 이벤트를 생성하거나 시간을 제어할 수 있다.
IfxGtm_Tom_Ch_ClkSrc_cmuFxclk1; 내부 클럭 소스를 선택한다.

cmuFxclk1의 값은 sys_clk값(100Mhz)를 Divider에 의해 16으로 나누어지게된 값이다.
즉 내부 클럭은 6,250,000 Hz 이다.
timerConfig.triggerOut = &IfxGtm_TOM0_0_TOUT26_P33_4_OUT;
PWM 아웃풋 핀을 설정한다. P33_4를 아웃풋 핀으로 설정하였다.

void Driver_Gtm_Tom0Test(void)
{
Ifx_TimerValue triggerPoint = 0u;
static float32_t fDuty = 0.5f;
triggerPoint = (uint32_t)(fDuty * (100000000.0f/16.0f/100.0f));
IfxGtm_Tom_Timer_disableUpdate(&g_GtmTomTimer.drivers.timerOneMs);
IfxGtm_Tom_Timer_setTrigger(&g_GtmTomTimer.drivers.timerOneMs, triggerPoint);
IfxGtm_Tom_Timer_applyUpdate(&g_GtmTomTimer.drivers.timerOneMs);
}
PWM 신호의 Duty 및 trigger point를 설정한다.
fDuty가 0.5면 한 사이클에 50%가 HIGH 신호가 되는것을 의미한다. 100%일때 5v를 주게된다면 50%일때는 2.5v를 주게된다.
triggerPoint는 언제 HIGH -> LOW 전환을 하는지 의미한다.
triggerPoint = fDuty * (100000000.0f / 16.0f / 100.0f);
100000000은 타이머의 입력 주파수이다. 100Mhz
16.0은 IfxGtm_Tom_Ch_ClkSrc_cmuFxclk1를 설정하기위한 Divider의 값
100은 timerConfig.base.frequency값으로 타이머 주파수 설정값이다.
정리하자면 최종 입력주파수는 6,250,000hz인데
주기를 10ms로 설정했으니 (timerConfig.base.frequency = 100;) 10ms 동안의 틱수는 62,500이다. 그 중 우리는 50%를 HIGH로 설정했으니 주기가 시작되고 31,250 틱 이후에 LOW로 전환하겠다는 의미이다.