打印
[STM32F1]

stm32测量信号频率及占空比

[复制链接]
453|5
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
基于stm32f103单片机对信号频率、占空比的测量。
最近开始仪器仪表方面的学习了,计划后期做一个示波器。所以这周就在stm32f103上面做了一个测量频率、占空比的小设计。总体上精度还是比较高的,测量频率量程在35Hz—190KHz。频率可以精确到小数点后四位,占空比测量的精度也比较高,可以到小数点后两位。
说到用stm32测频率,都会想到用定时器的输入捕获模式,只需要一个定时器和一个IO口即可,前几天在论坛上看到还有一种是用两个定时器测频率,一个定时器用来检测信号跳变沿,另外一个用来精准定时,比如说用TIM1检测跳变沿(假设为上升沿),TIM2开一个1s的定时器中断,这个1s就比较准确,在1s内TM1检测到了多少个上升沿改信号的频率就是多少。这种方法我本周二试过,精度比输入捕获模式下的高,而且还比较稳定,缺点是用到了两个定时器,占用的cpu资源较多。考虑到我后面任务需要,定时器可能会不够用,故还是用的输入捕获模式。

使用特权

评论回复
沙发
杨寅辉|  楼主 | 2020-6-27 20:26 | 只看该作者
实验平台:stm32f103zet6
定时器及通道:TIM2的通道2
IO口:PA1
定时器及输入捕获模式的配置:
u8 Edge_Flag;  //高低电平的标志位
u16 Rising,Falling,Rising_Last;   

//定时器2输入捕获中断初始化
void TIM2_Cap_Init()
{
    GPIO_InitTypeDef GPIO_InitStruct;   
    TIM_TimeBaseInitTypeDef TIM_InitStruct;
    TIM_ICInitTypeDef TIM_ICInitStruct;
    NVIC_InitTypeDef NVIC_InitStruct;
   
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
   
    GPIO_InitStruct.GPIO_Pin=GPIO_Pin_1;
    GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING;   //浮空
    GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
    GPIO_Init(GPIOA,&GPIO_InitStruct);
   
    TIM_InitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
    TIM_InitStruct.TIM_CounterMode=TIM_CounterMode_Up;  //向上计数
    TIM_InitStruct.TIM_Period=0xffff;
    TIM_InitStruct.TIM_Prescaler=0;                //分频系数 当分频系数越大时,可测量频率最小值越小
    TIM_TimeBaseInit(TIM2,&TIM_InitStruct);
   
    TIM_ICInitStruct.TIM_Channel=TIM_Channel_2;
    TIM_ICInitStruct.TIM_ICFilter=0x00;         //不滤波
    TIM_ICInitStruct.TIM_ICPolarity=TIM_ICPolarity_Rising;     //第一次是检测上升沿进入中断
    TIM_ICInitStruct.TIM_ICPrescaler=TIM_ICPSC_DIV1;
    TIM_ICInitStruct.TIM_ICSelection=TIM_ICSelection_DirectTI; //直接映射
    TIM_ICInit(TIM2,&TIM_ICInitStruct);
    TIM_ITConfig(TIM2,TIM_IT_CC2,ENABLE);
   
    NVIC_InitStruct.NVIC_IRQChannel=TIM2_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=0x00;    //抢占优先级
    NVIC_InitStruct.NVIC_IRQChannelSubPriority=0x01;          //响应优先级
    NVIC_Init(&NVIC_InitStruct);
   
    TIM_Cmd(TIM2,ENABLE);     //使能
}


使用特权

评论回复
板凳
杨寅辉|  楼主 | 2020-6-27 20:27 | 只看该作者
定时器2的中断服务函数:
void TIM2_IRQHandler(void)
{
    if(TIM_GetITStatus(TIM2,TIM_IT_CC2)!=RESET)    //捕获到上升沿
    {
        if(Edge_Flag==1)
        {
            Rising=TIM2->CCR2;    //第一次检测到下降沿
            TIM_OC2PolarityConfig(TIM2,TIM_ICPolarity_Rising);  //再次改为上升沿触发
            Edge_Flag++;
        }
        else if(Edge_Flag==2)
        {
            Rising_Last=TIM2->CCR2;
            TIM_OC2PolarityConfig(TIM2,TIM_ICPolarity_Rising);
            Edge_Flag=0;     //标志位清0
            TIM_SetCounter(TIM2,0); //定时器清0
        }
        else
        {
            Falling=TIM2->CCR2;     //第一次检测到上升沿
            TIM_OC2PolarityConfig(TIM2,TIM_ICPolarity_Falling);    //将上升沿触发改为下降沿触发
            Edge_Flag++;
        }
    }
    TIM_ClearITPendingBit(TIM2,TIM_IT_CC2);    //清除标志位
}

输入捕获模式下,中断服务函数里面处理的内容要尽量少,所以在记录定时器捕获到值时,直接将TIM2的CCR2寄存器里面的值赋值给相应变量。

使用特权

评论回复
地板
杨寅辉|  楼主 | 2020-6-27 20:27 | 只看该作者
主函数:
int main(void)
{   
    delay_init();    //延时函数初始化      
   
    OLED_Init();  //oled屏幕初始化
    OLED_On();
    OLED_Clear();
   
    TIM2_Cap_Init();   //定时器初始化

    while(1)
    {            
        OLED_ShowString(0,0,"Freq:",16);
        OLED_ShowString(100,0,"Hz",16);
        OLED_ShowNum(50,0,72000000/(Rising_Last-Falling),6,16);
        OLED_ShowString(0,2,"Duty:",16);
        OLED_ShowString(95,2,"%",16);
        OLED_Showdecimal(50,2,(float)(Rising-Falling)/(float)(Rising_Last-Falling)*100,2,3,16);
        delay_ms(100);
    }        
}

变量Rising_Last为第二次检测到上升沿捕获到的值,Falling为第一次捕获到的值,两者之差为定时器计数的数值差,根据TIM_Prescaler=0(即不分频),主频为72M,频率f=72M/(Rising_Last-Falling)。而Rising-Falling为高电平的时间,除以一个周期就是占空比了。

使用特权

评论回复
5
杨寅辉|  楼主 | 2020-6-27 20:28 | 只看该作者
实验现象
频率10K,占空比60%

使用特权

评论回复
6
杨寅辉|  楼主 | 2020-6-27 20:34 | 只看该作者
可见效果还是挺不错的,但是这种办法包括上面讲到的双定时器法都有一个缺点,那就是当信号幅度小于2v时,单片机就检测不到跳变沿。外部还需要硬件对信号进行适当放大。也是前几天,在论坛上看到有人提出一个测频率的叫过零检测法,用ADC读信号电压值,ADC值为0时进行记录,再次为0就相当于经过了半个周期。计算两次ADC为0的时间差,就可以计算出信号的频率,这种方法不会受限于信号幅度大小。原理也比较简单,相关程序我也正在写,等测试没问题后我再发出来和大家分享!

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

39

主题

295

帖子

2

粉丝