在官方的Application note中,我们可以看到PCNT的过采样的例程,是以32KHz的时钟源作为参考时钟源的。另外也有一个正交编码,虽然使用的是外部信号作为PCNT模块的时钟,但是我们很少会使用正交编码。
另,我们也只有自己用过,才能比较好的掌握这种功能方式。所以,我们还是用我们的STK,来了解一下PCNT是如何用外部信号作为时钟的。
但是有一点注意的是,就是同步的问题。因为如果外部信号是一个低速信号,而我们又需要修改PCNT的工作方式时,同步会占用很长的时间。
因此,如果是低速信号,还是建议使用32768来过采样外部信号的方式,来数脉冲信号。但是如果是高速外部信号,则可以使用该信号既做信号又做时钟。
另外一个注意的地方就是,G系列的PRS不支持PCNT。而TG系列,PCNT甚至可以数从PRS输过来的脉冲信号。详情看LESense的demo。
而配合定时器Timer,也可以数脉冲的频率。
硬件准备:
使用TG STK。 脉冲信号通过Timer 产生,程序里使用了Timer0的CCP0,location3,指向PD1脚。 而PCNT使用PCNT0的S0输入(切记只有S0上的信号才能作为外部时钟)即PD6脚。具体如下图1所示:
外加一颗LED灯(PD7)脚。
另外,想要完成整体的功能,需要将20pin的扩展口的第6脚和低16脚短接。即连接PD1脚和PD6脚。最终效果就是,你可以看到一盏灯每2秒亮灭一次。
软件准备:
没太多复杂的东西,请直接看例程:
#include "efm32.h"
#include "efm32_cmu.h"
#include "efm32_emu.h"
#include "efm32_gpio.h"
#include "efm32_prs.h"
#include "efm32_system.h"
#include "efm32_timer.h"
#include "efm32_chip.h"
#include "efm32_pcnt.h"
/* Define TOP value */
//默认时钟源为14MHz,timer 256分频
//做成一个10Hz的PWM输出
#define TOP 54687
/*****************************************************
注意点:
LFA clock默认选择的是LFRCO做为时钟源。但是LFRCO复位之后,
默认是关闭状态。
而初始化PCNT,将其切换到外部时钟信号的操作,仍需要使用
LFRCO作为clock,因此才加入了
CMU_ClockSelectSet(cmuClock_LFA, cmuSelect_LFRCO)
*****************************************************/
void PCNT_Initial(void)
{
CMU_ClockSelectSet(cmuClock_LFA, cmuSelect_LFRCO);
/* Enabling all necessary clocks */
CMU_ClockEnable(cmuClock_CORELE, true); /* Enable CORELE clock */
CMU_ClockEnable(cmuClock_GPIO, true); /* Enable clock for GPIO module */
CMU_ClockEnable(cmuClock_PCNT0, true); /* Enable clock for PCNT module */
//CMU_PCNTClockExternalSet(0,true);
/* Configure PC4 as intput to drive pulse counter */
GPIO_PinModeSet(gpioPortD, 6, gpioModeInput, 0);
/* Set configuration for pulse counter */
PCNT_Init_TypeDef pcntInit =
{
.mode = pcntModeExtSingle,//pcntModeOvsSingle, // /* clocked by LFACLK */
.counter = 0, /* Set initial value to 0 */
.top = 9, /* Set top to max value */
.negEdge = false, /* positive edges */
.countDown = false, /* up count */
.filter = true, /* filter enabled */
};
/* Initialize Pulse Counter */
PCNT_Init(PCNT0, &pcntInit);
/* Enable PCNT overflow interrupt */
PCNT_IntEnable(PCNT0, 0x2);
/* Enable PCNT1 interrupt vector in NVIC */
NVIC_EnableIRQ(PCNT0_IRQn);
/* Route PCNT1 input to location 0 -> PCNT1_S0IN on PC4 */
PCNT0->ROUTE = PCNT_ROUTE_LOCATION_LOC3;
}
void Timer_Initial(void)
{
/* Enable clock for GPIO module */
CMU_ClockEnable(cmuClock_GPIO, true);
/* Enable clock for TIMER0 module */
CMU_ClockEnable(cmuClock_TIMER0, true);
/* Set CC0 location 3 pin (PD1) as output */
GPIO_PinModeSet(gpioPortD, 1, gpioModePushPull, 0);
/* Select CC channel parameters */
TIMER_InitCC_TypeDef timerCCInit =
{
.eventCtrl = timerEventEveryEdge,
.edge = timerEdgeBoth,
.prsSel = timerPRSSELCh0,
.cufoa = timerOutputActionNone,
.cofoa = timerOutputActionNone,
.cmoa = timerOutputActionToggle,
.mode = timerCCModePWM,
.filter = false,
.prsInput = false,
.coist = false,
.outInvert = false,
};
/* Configure CC channel 0 */
TIMER_InitCC(TIMER0, 0, &timerCCInit);
/* Route CC0 to location 3 (PD1) and enable pin */
TIMER0->ROUTE |= (TIMER_ROUTE_CC0PEN | TIMER_ROUTE_LOCATION_LOC3);
/* Set Top Value */
TIMER_TopSet(TIMER0, TOP/10);
/* Set compare value starting at 0 - it will be incremented in the interrupt handler */
TIMER_CompareBufSet(TIMER0, 0, TOP/20);
/* Select timer parameters */
TIMER_Init_TypeDef timerInit =
{
.enable = true,
.debugRun = true,
.prescale = timerPrescale256,
.clkSel = timerClkSelHFPerClk,
.fallAction = timerInputActionNone,
.riseAction = timerInputActionNone,
.mode = timerModeUp,
.dmaClrAct = false,
.quadModeX4 = false,
.oneShot = false,
.sync = false,
};
/* Configure timer */
TIMER_Init(TIMER0, &timerInit);
}
/**************************************************************************//**
* @brief Main function
* Main is called from __iar_program_start, see assembly startup file
*****************************************************************************/
int main(void)
{
/* Initialize chip */
CHIP_Init();
Timer_Initial();
PCNT_Initial();
GPIO_PinModeSet(gpioPortD, 7, gpioModePushPull, 0);
while(1)
{
/* Go to EM1 */
EMU_EnterEM1();
}
}
void PCNT0_IRQHandler(void)
{
/* Clear PCNT1 overflow interrupt flag */
PCNT_IntClear(PCNT0, 0x2);
GPIO_PinOutToggle(gpioPortD, 7);
}