1.简介:数字频率计主要由四个部分组成:时基电路,整形电路,控制电路和显示电路组成。在一个测量周期过程中,由时基电路产生一标准时间信号控制阀门,调节时基电路中的电阻可产生需要的标准时间信号。信号输入整形电路中,经过整形,输出一方波,通过阀门后,计时器对其计数。当计数完毕,时基电路输出一个上升沿,使锁存器打开,计数器计数结果输入译码器,从而让显示器显示,达到测量频率的目的。文件包括设计电路图,设计源代码与设计报
1.测量信号输入幅度1V~5V方波,频率为1kHz~10kHz,测量精度1%,信号输入幅度1V~5V三角波,频率为1kHz~10kHz,测量精度1%,信号输入幅度2V~5V正弦波,频率为1kHz~10kHz,测量精度1%;
1.测频部分:先对输入信号进行波形转换,通过一个过零比较器将三角波、正弦波转化为方波,之后将将方波输入单片机 口进行波形测试。
2.限幅报警部分:输入信号经过D1变成半波信号,之后经过C3,C4,C5,C6,C7进行整流成为幅值直流信号,之后将信号输入电位器进行分压,保证15v电压经过电位器后剩余3.3v电压。最后输出到端口上与3.3v电压进行比较,将比较结果输入单片机 口。
3.单片机供电部分:通过供电模块输出3.3v电压供给stm32单片机。
4.软件设计原理:硬件部分会将所有波形统一成方波输入。使用TIM3与TIM4计时器。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; //报警灯不亮
}
}
计时器函数:#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);
}
幅值报警初始化:#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
}