用stm32的定时器输出任意频率方波,如果需要的频率不高,用主时钟分频一般就够了。例如需要755Hz的方波,36M/755=47682.119,只要把主时钟47682分频即可得到755.002Hz的输出. (实际主频72M,定时器按Toggle方式输出,这样可以保证方波占空比是50%.)
如果需要的频率比较高呢? 比如558kHz, 72M/64/2=562.5k, 72M/65/2=553.8k, 都还差得远. 如果能做到72M/64.516=558.001kHz, 这还差不多.
这种情况一般需要类似MC12016之类的双模分频器来实现, 后级计数器控制前面的双模分频器在/N和/N+1之间不断切换, 对于上面的情况, 就是后级计数器每计数到516个脉冲就把前级分频比改为65, 继续计数到1000个脉冲再把分频比改回64, 如此往复, 总的效果就是前级计数器64.516分频了. 当然, 这样输出的558kHz实际上是562.5k和553.8k不断切换的结果, 抖动很大, 有些场合能用, 有些场合不适合.
另外,采用stm32的定时器级联, 主定时器的输出可以作为从定时器的输入时钟, 这样只要给从定时器开一个比较匹配中断, 一个更新中断, 在两个中断里来回修改主定时器的ARR值, 不就实现了?
实际算一下, 这么做的问题很大. 当所需要的输出频率较高, 分频比又非常接近整数时, 两个中断之间的时间间隔就太短了, 前一个中断来不及处理完, 这样输出频率肯定不准. 幸好定时器的比较匹配事件和溢出事件都可以分别触发DMA传输, 这样从定时器用两个DMA通道, 分别在比较匹配和更新时写主定时器的ARR即可.
程序如下, TIM4和TIM5分别作为主/从定时器, TIM2用来测量频率. 调用TIMER_SetFreq函数即可设置输出频率. 实测输出几十kHz频率时的效果非常好, 输出3.58MHz仍然能工作, 只是抖动大到没法看了. (如果加上sigma-delta调制, 应该能改善一点, 不过估计很麻烦.)
再就是如果能用分数逼近来凑出所需分频比的小数部分, 应该比简单使用百位或者千位截断好一些. 有空找找分数逼近算法.
#include <math.h>
#include "misc.h"
static struct {
unsigned short period_master_n;
unsigned short period_master_n1;
unsigned short period_slave;
unsigned short comp_slave;
unsigned long counter;
} g = {9, 10, 100, 5, 0}; // 从定时器周期设为100, 越大则输出频率越精确, 但抖动也更严重
void TIMER_SetFreq(unsigned long freq)
{
g.period_master_n = (SystemCoreClock / 2) / freq - 1;
g.period_master_n1 = g.period_master_n + 1;
g.comp_slave = (int)round(
((SystemCoreClock / 2) % freq * g.period_slave * 1.0) / freq);
TIM_SetCompare1(TIM5, g.comp_slave);
}
static void TIMER_MasterConfig(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_Period = g.period_master_n;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
TIM_ARRPreloadConfig(TIM4, ENABLE);
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
TIM_OC1Init(TIM4, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable);
TIM_SelectOutputTrigger(TIM4, TIM_TRGOSource_Update);
TIM_Cmd(TIM4, ENABLE);
}
static void TIMER_SlaveConfig(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_DeInit(TIM5);
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_TimeBaseStructure.TIM_Period = g.period_slave;
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure);
TIM_ARRPreloadConfig(TIM5, ENABLE);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Disable;
TIM_OCInitStructure.TIM_Pulse = g.comp_slave;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
TIM_OC1Init(TIM5, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM5, TIM_OCPreload_Enable);
TIM_SelectMasterSlaveMode(TIM5, TIM_MasterSlaveMode_Enable);
TIM_SelectSlaveMode(TIM5, TIM_SlaveMode_External1); // 从定时器:外部时钟源模式
TIM_SelectInputTrigger(TIM5, TIM_TS_ITR2); // TIM4触发TIM5
TIM_Cmd(TIM5, ENABLE); // TIM5 enable counter
TIM_DMACmd(TIM5, TIM_DMA_Update, ENABLE); // 开启更新和1通道匹配触发DMA传输
TIM_DMACmd(TIM5, TIM_DMA_CC1, ENABLE);
DMA_StructInit(&DMA_InitStructure);
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize = 1;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_PeripheralBaseAddr = (unsigned long)&(TIM4->ARR);
DMA_InitStructure.DMA_MemoryBaseAddr = (unsigned long)&(g.period_master_n1);
DMA_Init(DMA2_Channel2, &DMA_InitStructure);
DMA_Cmd(DMA2_Channel2, ENABLE);
DMA_InitStructure.DMA_MemoryBaseAddr = (unsigned long)&(g.period_master_n);
DMA_Init(DMA2_Channel5, &DMA_InitStructure);
DMA_Cmd(DMA2_Channel5, ENABLE);
}
static void TIMER_FreqMeasConfig(void) // 用T2_ETR来测量频率, 用飞线短接PB6和PA0
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_DeInit(TIM2);
TIM_TimeBaseStructure.TIM_Period = 0xffff;
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF,
TIM_ExtTRGPolarity_NonInverted, 0);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
TIM_SetCounter(TIM2, 0);
TIM_Cmd(TIM2, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_EnableIRQ(TIM2_IRQn);
}
void TIM2_IRQHandler(void) // 进位处理, stm32f103没有24位定时器, 不然就不用这么折腾了
{
if(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) {
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
g.counter += 65536;
}
}
unsigned long TIMER_GetCounter(void) // 读取频率计数和清零
{
unsigned long ret = g.counter + TIM_GetCounter(TIM2);
TIM_SetCounter(TIM2, 0);
g.counter = 0;
return ret;
}
void TIMER_Config(void)
{
TIMER_MasterConfig();
TIMER_SlaveConfig();
TIMER_FreqMeasConfig();
}
|