打印
[应用相关]

基于stm32的单片机的数字频率计设计

[复制链接]
1157|19
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
jcky001|  楼主 | 2022-4-21 09:20 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
1.简介:数字频率计主要由四个部分组成:时基电路,整形电路,控制电路和显示电路组成。在一个测量周期过程中,由时基电路产生一标准时间信号控制阀门,调节时基电路中的电阻可产生需要的标准时间信号。信号输入整形电路中,经过整形,输出一方波,通过阀门后,计时器对其计数。当计数完毕,时基电路输出一个上升沿,使锁存器打开,计数器计数结果输入译码器,从而让显示器显示,达到测量频率的目的。文件包括设计电路图,设计源代码与设计报
2.目标:本设计主要完成以下目标:
  1.测量信号输入幅度1V~5V方波,频率为1kHz~10kHz,测量精度1%,信号输入幅度1V~5V三角波,频率为1kHz~10kHz,测量精度1%,信号输入幅度2V~5V正弦波,频率为1kHz~10kHz,测量精度1%;
2.当输入信号幅度大于15V时,具有报警功能,测量结果为数字显示;
3.测量具有串口通信功能;
4.信号输入幅度为1V~10V脉冲,频率为20Hz~1kHz时,测量并计算占空比。
3.硬件设计:
3.硬件设计原理
硬件电路图:

使用特权

评论回复
沙发
jcky001|  楼主 | 2022-4-21 09:23 | 只看该作者
1.测频部分:先对输入信号进行波形转换,通过一个过零比较器将三角波、正弦波转化为方波,之后将将方波输入单片机 口进行波形测试。
2.限幅报警部分:输入信号经过D1变成半波信号,之后经过C3C4C5C6C7进行整流成为幅值直流信号,之后将信号输入电位器进行分压,保证15v电压经过电位器后剩余3.3v电压。最后输出到端口上与3.3v电压进行比较,将比较结果输入单片机 口。
3.单片机供电部分:通过供电模块输出3.3v电压供给stm32单片机。
4.软件设计原理:
硬件部分会将所有波形统一成方波输入。使用TIM3TIM4计时器。TIM3进行波形捕捉。TIM3捕捉波形的上升沿或者下降沿。开始计数,知道捕捉到相对应的上升沿或者下降沿。根据所计数的多少即可计算出波形的占空比。每次捕捉到上升沿且之前为低电平,频率计数加一。TIM4计数器用来计数一秒,当TIM4计数满一秒时,发生TIM4中断,根据TIM3的频率计数,即可计算出输入波形的频率。
5.源代码:(带有详细注释)Main函数:
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "oled.h"
#include "timer.h"
#include "led.h"
#include "key.h"

extern uint16_t  counter;                         //记录捕获状态
extern uint8_t   flag;                                  //记录计时是否满一秒
extern uint32_t  rising_time;                  //高电平持续时间
extern uint32_t  falling_time;                 //低电平持续时间

int main(void)
{        
         delay_init();                                                                                 //延时函数初始化                    
         uart_init(115200);                                                                    //串口一初始化,设置波特率为115200
         LED_Init();                                                                                            //报警LED初始化
         KEY_Init();                                                                                            //比较器输入初始化
         OLED_Init();                                                                                //OLED显示屏初始化
         OLED_Clear();                                                                            //清空显示屏
         OLED_ShowString(30,0, "Cymometer");                           //在屏幕上显示Cymometer
         OLED_ShowString(20,4, "Fre:");
         OLED_ShowString(93,4, "Hz");
         OLED_ShowString(20,6, "Dut:");
         OLED_ShowString(87,6, "%");
         TIM3_Cap_Init(0xffff,72-1);                                         //TIM3初始化 时钟设为1M Hz用于输入捕获
         TIM4_Int_Init(10000-1,72-1);                                               //TIM4初始化 时钟设为1M Hz用于计时1s

         while(1)
         {                 
                   if(flag== 1)                //检测计时是否满1s                                          
                   {
                            flag= 0;
                            if((rising_time!= 0) && (falling_time != 0))     //有信号输入
                            {
                                     printf("Fre:%dHz,     Dut:%1.2f\r\n", counter,(float)rising_time/(rising_time + falling_time));
                                     OLED_ShowNum(53,4, counter, 5, 16);
                                     OLED_ShowNum(71,6, (float)rising_time/(rising_time + falling_time)*100, 2, 16);
                            }
                            else
                            {
                                     printf("Fre:%d           Dut:0\r\n", counter);       //无信号输入
                                     OLED_ShowNum(53,4, counter, 5, 16);
                                     OLED_ShowNum(71,6, 0, 2, 16);
                            }
                            counter= 0;
                            rising_time= 0;
                            falling_time= 0;
                   }
                   if(KEY== 1)                //检测比较器输入是否为高电平
                            LED= 0;             //报警灯亮
                   elseLED = 1;              //报警灯不亮
         }
}

使用特权

评论回复
板凳
jcky001|  楼主 | 2022-4-21 09:25 | 只看该作者
计时器函数:
#include "sys.h"
#include "timer.h"
#include "usart.h"

void TIM3_Cap_Init(uint16_t arr, uint16_tpsc)                                  //TIM3用于输入捕获
{
         GPIO_InitTypeDefGPIO_InitStructure;
         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
         TIM_ICInitTypeDef  TIM3_ICInitStructure;
     NVIC_InitTypeDef NVIC_InitStructure;

         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);     //使能TIM3时钟
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);          //使能GPIOA时钟

         GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_6;                      //选择6号引脚
         GPIO_InitStructure.GPIO_Mode= GPIO_Mode_IPD;                      //输入下拉
         GPIO_Init(GPIOA,&GPIO_InitStructure);                                             //初始化PA6
         GPIO_ResetBits(GPIOA,GPIO_Pin_0);                                                    //PA6下拉

         //初始化定时器3 TIM3  
         TIM_TimeBaseStructure.TIM_Period= arr;                               //设定计数器自动重装值
         TIM_TimeBaseStructure.TIM_Prescaler=psc;                                    //预分频器
         TIM_TimeBaseStructure.TIM_ClockDivision= TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
         TIM_TimeBaseStructure.TIM_CounterMode= TIM_CounterMode_Up;  //TIM向上计数模式
         TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);

         //初始化TIM3输入捕获参数
         TIM3_ICInitStructure.TIM_Channel= TIM_Channel_1;                   //CC1S=01选择输入端 IC1映射到TI1
       TIM3_ICInitStructure.TIM_ICPolarity =TIM_ICPolarity_Rising;      //上升沿捕获
       TIM3_ICInitStructure.TIM_ICSelection =TIM_ICSelection_DirectTI; //映射到TI1
       TIM3_ICInitStructure.TIM_ICPrescaler =TIM_ICPSC_DIV1;   //配置输入分频,不分频
       TIM3_ICInitStructure.TIM_ICFilter = 0x00;                                  //IC1F=0000 配置输入滤波器 不滤波
       TIM_ICInit(TIM3,&TIM3_ICInitStructure);

         //中断分组初始化
         NVIC_InitStructure.NVIC_IRQChannel= TIM3_IRQn;            //TIM3中断
         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority= 0;  //先占优先级0
         NVIC_InitStructure.NVIC_IRQChannelSubPriority= 0;          //从优先级0
         NVIC_InitStructure.NVIC_IRQChannelCmd= ENABLE;           //IRQ通道被使能
         NVIC_Init(&NVIC_InitStructure);

         TIM_ITConfig(TIM3,TIM_IT_CC1,ENABLE);

     TIM_Cmd(TIM3,ENABLE );     //使能定时器3
}

uint8_t  TIM3CH1_CAPTURE_STA;                                           //输入捕获状态
uint16_t counter = 0;                                                           //记录捕获上升沿次数
uint8_t  sec_cnt = 0;                                                           //毫秒计时
uint8_t       flag = 0;                                                                          //计时1s完成标志
uint32_t rising_time;                                                            //高电平时间
uint32_t falling_time;                                                          //低电平时间

//定时器3中断服务程序      
void TIM3_IRQHandler(void)
{
         if(TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET)                            //发生捕获事件
         {        
                   if(TIM3CH1_CAPTURE_STA& 0x80)                                                                 //捕获下降沿(已经捕获到上升沿)
                   {
                            rising_time= TIM_GetCapture1(TIM3);                             //读取高电平时间(us)

                            TIM3CH1_CAPTURE_STA= 0;                                                         //清空
                            TIM_SetCounter(TIM3,0);
                            TIM3CH1_CAPTURE_STA|= 0x40;                                                //标记捕获到了下降沿
                            TIM_OC1PolarityConfig(TIM3,TIM_ICPolarity_Rising);   //设置为上升沿触发
                   }
                   else                                                                                                                  //捕获上升沿
                   {
                            falling_time= TIM_GetCapture1(TIM3);                                     //低电平时间

                            TIM3CH1_CAPTURE_STA= 0;                                                         //清零
                            TIM_SetCounter(TIM3,0);
                            TIM3CH1_CAPTURE_STA|=0X80;                                                            //标记捕获到了上升沿
                            TIM_OC1PolarityConfig(TIM3,TIM_ICPolarity_Falling);  //开始捕获下降沿
                            counter++;                                                                                                     //频率计数加一
                   }
         }

         TIM_ClearITPendingBit(TIM3,TIM_IT_CC1); //清除中断标志位
}



void TIM4_Int_Init(u16 arr,u16 psc)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
         NVIC_InitTypeDefNVIC_InitStructure;

         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE); //时钟使能

         TIM_TimeBaseStructure.TIM_Period= arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
         TIM_TimeBaseStructure.TIM_Prescaler=psc; //设置用来作为TIMx时钟频率除数的预分频值
         TIM_TimeBaseStructure.TIM_ClockDivision= 0; //设置时钟分割:TDTS = Tck_tim
         TIM_TimeBaseStructure.TIM_CounterMode= TIM_CounterMode_Up;  //TIM向上计数模式
         TIM_TimeBaseInit(TIM4,&TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位

         TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE);

         NVIC_InitStructure.NVIC_IRQChannel= TIM4_IRQn;
         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority= 1;  //先占优先级1
         NVIC_InitStructure.NVIC_IRQChannelSubPriority= 0;  //从优先级0
         NVIC_InitStructure.NVIC_IRQChannelCmd= ENABLE; //IRQ通道被使能
         NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器

         TIM_Cmd(TIM4,ENABLE);  

}

void TIM4_IRQHandler(void)                                                               //TIM3中断,10000us中断一次
{
         if(TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)  //检查指定的TIM中断发生与否:TIM 中断源
         {
                   sec_cnt++;                                                                                            
                   if(sec_cnt== 100)                                                                     //计时满1
                   {
                            sec_cnt= 0;
                            flag= 1;
                   }
                   TIM_ClearITPendingBit(TIM4,TIM_IT_Update);           //清除TIMx的中断待处理位:TIM 中断源
         }
}
       LED初始化:
#include "led.h"
#include "sys.h"

void LED_Init(void)
{
         GPIO_InitTypeDef  GPIO_InitStructure;

         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);    //使能GPIOC端口时钟

         GPIO_InitStructure.GPIO_Pin= GPIO_Pin_13;                                    //LED-->PC13 端口配置
         GPIO_InitStructure.GPIO_Mode= GPIO_Mode_Out_PP;                //推挽输出
         GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;                 //IO口速度为50MHz
         GPIO_Init(GPIOC,&GPIO_InitStructure);                                             //根据设定参数初始化GPIOB.5
         GPIO_SetBits(GPIOC,GPIO_Pin_13);
}

使用特权

评论回复
地板
jcky001|  楼主 | 2022-4-21 09:29 | 只看该作者
幅值报警初始化:
#include "key.h"
#include "sys.h"

void KEY_Init(void)
{
        GPIO_InitTypeDef GPIO_InitStructure;

        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//使能PORTA时钟

         GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_5;
         GPIO_InitStructure.GPIO_Mode= GPIO_Mode_IPD; //设置成下拉输入
        GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA5

}

使用特权

评论回复
5
micoccd| | 2022-4-21 09:33 | 只看该作者
有没有源码包啊

使用特权

评论回复
6
skyred| | 2022-4-21 17:22 | 只看该作者
这原理图,感觉也太简单了点

使用特权

评论回复
7
麻花油条| | 2022-4-22 15:11 | 只看该作者
想看看源代码

使用特权

评论回复
8
麻花油条| | 2022-4-22 15:11 | 只看该作者
能提供一份吗

使用特权

评论回复
9
Jacquetry| | 2022-10-5 21:31 | 只看该作者
好简单的电路图

使用特权

评论回复
10
Uriah| | 2022-10-8 13:06 | 只看该作者

对于没有else的场景,使用ifPresent即可

使用特权

评论回复
11
Bblythe| | 2022-10-8 16:05 | 只看该作者

使用Optional简化if判空

使用特权

评论回复
12
Pulitzer| | 2022-10-8 19:04 | 只看该作者

通过对判断条件取反,代码在逻辑表达上会更加清晰

使用特权

评论回复
13
公羊子丹| | 2023-7-1 07:08 | 只看该作者

电压范围称为工作电源电压

使用特权

评论回复
14
公羊子丹| | 2023-7-1 08:01 | 只看该作者

与15号引脚连接的C1称为旁路电容

使用特权

评论回复
15
Wordsworth| | 2023-7-1 09:04 | 只看该作者

时序电路是按时钟信号(CK)的上升沿(信号从L→H的变化)或下降沿(信号从H→L的变化)同步工作的

使用特权

评论回复
16
Clyde011| | 2023-7-1 10:07 | 只看该作者

电源电压处于1.6V到5.5V之间

使用特权

评论回复
17
万图| | 2023-7-1 12:03 | 只看该作者

内部电路工作电圧是通过内部电压调节器调节电源电压得到的

使用特权

评论回复
18
Uriah| | 2023-7-1 13:06 | 只看该作者

单片机的外部都连接有象电池等电源部分

使用特权

评论回复
19
帛灿灿| | 2023-7-1 15:02 | 只看该作者

通常选择0.01μF~0.1μF的陶瓷电容作为旁路电容。

使用特权

评论回复
20
Bblythe| | 2023-7-1 16:05 | 只看该作者

主时钟振荡器主要用作CPU的工作时钟

使用特权

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

本版积分规则

1510

主题

4547

帖子

6

粉丝