一、测周法:通过一个方波的两个上升沿或下降沿触发中断,然后定时器计数,计数的总个 数乘以计数单位时间即该方波的周期,具体可通过单片机输入捕获功能实现,以下为参考代码
//输入捕获初始化函数
void input_frequent_init(void) //采用TIM4的Channel_1通道作为输入捕获通道
{
//声明结构体变量,用来初始化定时器
TIM_TimeBaseInitTypeDef TIM4_TimeBaseInitStructure;
TIM_ICInitTypeDef TIM4_ICInitStructure;
NVIC_InitTypeDef TIM4_NVIC_InitStructure;
/* 开启定时器4时钟 */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);
TIM_ClearITPendingBit(TIM4,TIM_IT_Update|TIM_IT_CC1); //清除捕获和中断标志位
TIM4_TimeBaseInitStructure.TIM_Period = 0xffff; //设定计数器自动重装值(设置为最大)
TIM4_TimeBaseInitStructure.TIM_Prescaler = 1; //设置分频系数
TIM4_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
TIM4_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM4,&TIM4_TimeBaseInitStructure);//根据结构体参量初始化定时器
TIM4_ICInitStructure.TIM_Channel = TIM_Channel_1; //选择输入捕获的输入端,IC1映射到TI1上
TIM4_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //设置为上升沿捕获
TIM4_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1上
TIM4_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //配置输入分频,不分频
TIM4_ICInitStructure.TIM_ICFilter = 0x00; //IC1F=0000 配置输入滤波器,此处不滤波
TIM_ICInit(TIM4, &TIM4_ICInitStructure); //初始化TIM4通道1
//中断分组初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
TIM4_NVIC_InitStructure.NVIC_IRQChannel=TIM4_IRQn; //打开TIM4的全局中断
TIM4_NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1; //抢占优先级配置为1
TIM4_NVIC_InitStructure.NVIC_IRQChannelSubPriority=1; //响应优先级配置为1
TIM4_NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能
NVIC_Init(&TIM4_NVIC_InitStructure); //初始化中断
TIM_Cmd(TIM4,ENABLE); //使能中断
TIM_ITConfig(TIM4, TIM_IT_Update|TIM_IT_CC1, ENABLE ); //使能捕获和更新中断
}
需要注意的是,如果所测信号中存在尖峰干扰信号,则
TIM4_ICInitStructure.TIM_ICFilter = 0x00; //IC1F=0000 配置输入滤波器,此处不滤波
这一行应根据干扰信号的高电平时间来赋予合适的滤波器的值,具体计算方法参考芯片手册或自行百度。
void TIM4_IRQHandler() //输入捕获中断函数
{
static u8 state; //存储捕获状态,state=0表示未捕获到第一个上升沿,state=1表示已经捕获到第一个上升沿
static u32 TIM4CH1_CAPTURE; //存储TIM4计数寄存器溢出次数
u32 timecount; //存储总的计数次数
if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET) //发生计数器溢出更新中断
{
TIM_ClearITPendingBit(TIM4, TIM_IT_CC1|TIM_IT_Update); //清除中断标志位
if(state==1) //在捕获到第一个上升沿后
TIM4CH1_CAPTURE++; //溢出次数加一
}
if(TIM_GetITStatus(TIM4,TIM_IT_CC1)!=RESET) //产生输入捕获中断
{
TIM_ClearITPendingBit(TIM4, TIM_IT_CC1|TIM_IT_Update); //清除中断标志位
if(state==0) //未捕获到第一个上升沿
{
state=1; //置1
TIM_SetCounter(TIM4,0); //将计数器清零
}
else if(state==1) //已经捕获到第一个上升沿
{
state=0; //置0
timecount=TIM_GetCapture1(TIM4)+TIM4CH1_CAPTURE*65536; //计算两个上升沿之间的总计数
TIM4CH1_CAPTURE=0; //清零溢出次数
TIM_SetCounter(TIM4,0); //清零计数器
frequent_input=36000000.0/timecount; //计算频率
}
}
}
注意:根据所测频率大致范围来配置定时器(可提高测量精度)
该方法可精确测量较低频率,本人测试1k以下精确度高达0.1%,但随着频率的增加,误差也越来越大,故测低频时推荐此方法
接下来的程序还可测量占空比,思路是先设置为上升沿捕获,然后设置为下降沿捕获,在设置为上升沿捕获,根据两次捕获中计数次数算出占空比duty=捕获高电平时间/(捕获高电平时间+捕获低电平时间)
定时器输入捕获配置同上(改用TIM5,TIM分频系数改为143,TIM_Prescaler = 143),
不在重复,直接看中断函数
void TIM5_IRQHandler()
{
if((TIM5CH1_CAPTURE_STA&0X80)==0)//还未成功捕获
{
if (TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET)
{
if(TIM5CH1_CAPTURE_STA&0X20) //已经捕获到高电平
{
if((TIM5CH1_CAPTURE_STA&0x1f)==0x1f)//高电平时间太长了
{
TIM5CH1_CAPTURE_STA|=0x80; //标记成功捕获一次
}
else
{
TIM5CH1_CAPTURE_STA++; //溢出次数加1
}
}
}
}
if (TIM_GetITStatus(TIM5, TIM_IT_CC4) != RESET) //发生捕获事件
{
if((TIM5CH1_CAPTURE_STA&0X20)&&(!(TIM5CH1_CAPTURE_STA&0X40))) //已经捕获到上升沿
{
TIM5CH1_CAPTURE_STA|=0X40; //标记成功捕获一次下降沿
TIM5CH1_CAPTURE_VAL1=TIM_GetCapture4(TIM5)+(TIM5CH1_CAPTURE_STA&0X1f)*65536; //获得TIM5捕获通道一的捕获值(对应高电平时间)
TIM_SetCounter(TIM5,0); //清零计数器
TIM_OC4PolarityConfig(TIM5,TIM_ICPolarity_Rising); //设置为上升沿捕获
TIM5CH1_CAPTURE_STA&=0Xe0; //溢出次数清零
}
else if((TIM5CH1_CAPTURE_STA&0X20)==0) //未捕获到上升沿
{
TIM5CH1_CAPTURE_STA=0; //清零标志位及溢出次数
TIM5CH1_CAPTURE_VAL1=0; //清零高电平计数
TIM5CH1_CAPTURE_VAL2=0; //清零低电平计数
TIM_SetCounter(TIM5,0); //清零TIM5计数寄存器
TIM5CH1_CAPTURE_STA|=0X20; //置标志位
TIM_OC4PolarityConfig(TIM5,TIM_ICPolarity_Falling); //设置为下降沿捕获
}
else if((TIM5CH1_CAPTURE_STA&0X40)&&(!(TIM5CH1_CAPTURE_STA&0X80))) //已经捕获到下降沿
{
TIM5CH1_CAPTURE_VAL2=TIM_GetCapture4(TIM5)+(TIM5CH1_CAPTURE_STA&0X1f)*65536;//低电平计数(对应低电平时间)
TIM5CH1_CAPTURE_STA|=0X80; //置标志位
}
}
TIM_ClearITPendingBit(TIM5, TIM_IT_CC4|TIM_IT_Update); //清中断标志位
}
主函数
u8 TIM5CH1_CAPTURE_STA; //
u16 TIM5CH1_CAPTURE_VAL1;//
u16 TIM5CH1_CAPTURE_VAL2;//
float frequent;
float duty;
int main()
{
input_duty_init();
while(1)
{
if((TIM5CH1_CAPTURE_STA&0x80)) //
{
duty=(float)TIM5CH1_CAPTURE_VAL1/(TIM5CH1_CAPTURE_VAL1+TIM5CH1_CAPTURE_VAL2 );//计算占空比
frequent+=500000.0/(TIM5CH1_CAPTURE_VAL1+TIM5CH1_CAPTURE_VAL2 );//计算频率
TIM5CH1_CAPTURE_STA=0; // 清零标志位
}
}
}
这个程序中变量TIM5CH1_CAPTURE_STA的高三位作为输入捕获状态的标志位,具体每一位的作用不在详细解释,凭借自学能力完全可以解决。该方法测量误差同样在频率比较低时精确度很高,但随着频率的增大误差也越来越大。