当前位置:   article > 正文

STM32单片机-输入捕获、FFT测频_stm32信号处理

stm32信号处理
本内容介绍基于STM32F103VET6的一个实际工程中添加采集A相电压信号或B相电流信号频率的功能,分别通过输入捕获与FFT实现,均测试可用。紫色文字是超链接,点击自动跳转至相关博文。持续更新,原创不易!

目录:

一、硬件连接

1、电压信号处理电路仿真

2、单片机连接

二、代码部分

1、通过STM32输入捕获

1)定时器配置   2)定时溢出与输入捕获中断

2、通过FFT实现

1)概述   2)数据采集   3)功能介绍

3、屏显驱动介绍

三、测频法计算频率

1、外部中断统计跳边沿个数

2、定时器外部计数的方法


一、硬件连接

1、电压信号处理电路仿真

图1.1.1 Proteus仿真图(使用教程)

2、单片机连接

主控MCU:STM32F103ZET6(STM32的介绍),LM293输出连接在PB0上检测电压信号的频率,如图1.1.1与图1.2.1所示。

图1.2.1 原理图截图

图1.2.2 管脚功能截图

如图1.2.2所示,注意其中的TIM3_CH2N是PWM捕获比较输出,TIM3_CH3才是输入捕获。

图1.2.3 内部高级控制定时器框图

二、代码部分

这里通过STM32输入捕获或FFT转换两种方式实现频率的测量,在实际工程中都已实现。STM32输入捕获信号幅度小于2V时,单片机检测不到跳变沿,需硬件对信号适当处理(如图1.1.1)。

PB0/ADC8也可用ADC读信号电压值,ADC值为0时进行记录,再次为0就相当于经过了半个周期。计算两次ADC为0的时间差,就可以计算出信号的频率,这种方法不会受限于信号幅度的限制。

1、通过STM32输入捕获

下面的程序采集PB0口(图1.2.1)的电压信号,因频率较低,且要求继电器出口时间小于35mS,采用测周法计算频率。给出主要部分定时器配置与定时器中断程序。因上升沿示波器测试并不陡峭(图1.1.1仿真图也可看出),故取一周波两次下降沿。

注意:后期的处理程序必须捕获到两个下降沿的前提下,才能作相应的处理。采集程序未完成,此时处理会出错。

图2.1.1 示波器信号

1)定时器配置

  1. void adc_TIM_Init(void)
  2. {
  3.    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; //定时器
  4.    GPIO_InitTypeDef GPIO_InitStructure; //端口
  5.    TIM_ICInitTypeDef TIM_ICInitStructure; //输入捕获
  6.    
  7.    //初始化GPIO口
  8.   GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING; //浮空输入模式
  9.    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
  10.    GPIO_Init(GPIOB,&GPIO_InitStructure);
  11.    GPIO_SetBits(GPIOB,GPIO_Pin_0);
  12.    
  13.    //使能时钟
  14.    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); //TIM3
  15.    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO,ENABLE);
  16.    
  17.    //初始化TIM3定时
  18.    TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
  19.    TIM_TimeBaseStructure.TIM_Prescaler = 17; //1MHz计数脉冲 1uS
  20.    TIM_TimeBaseStructure.TIM_Period = 65535;
  21.    TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;
  22.    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
  23.    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
  24.    TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
  25.    
  26.    //初始化TIM3 Channel3输入捕获IC(Input Capture)
  27.    TIM_ICInitStructure.TIM_Channel=TIM_Channel_3;
  28.    TIM_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Falling; //下降沿捕获
  29.    TIM_ICInitStructure.TIM_ICSelection=TIM_ICSelection_DirectTI; //管脚与寄存器一一对应
  30.    TIM_ICInitStructure.TIM_ICPrescaler=TIM_ICPSC_DIV1; //有下降沿就捕获,不分频
  31.    TIM_ICInitStructure.TIM_ICFilter=0x00; //不打开输入捕获滤波器
  32.    TIM_ICInit(TIM3,&TIM_ICInitStructure);
  33.    TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE); //允许定时中断
  34.    TIM_ITConfig(TIM3,TIM_IT_CC3,ENABLE); //允许CC3捕获中断
  35.    TIM_Cmd(TIM3,ENABLE);
  36. …………
  37. }

------------------------------

2)定时溢出与输入捕获中断

  1. void TIM3_IRQHandler(void) //TIM3
  2. {
  3.    static u8 CapStatus = 0;
  4. //捕获状态,CapStatus=0未捕获到第1个下降沿,CapStatus=1捕获到第1个下降沿
  5.    static u8 TIM3_CH3_Capture = 0; //总的计数次数
  6.    u32 FrequencyTemp = 0;
  7.    
  8.    if(TIM_GetITStatus(TIM3,TIM_IT_Update)) //TIM3定时溢出更新中断
  9.    {
  10.       TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //清除中断标志位
  11.       if(CapStatus)
  12.          TIM3_CH3_Capture++;
  13.    }
  14.    
  15.    if(TIM_GetITStatus(TIM3,TIM_IT_CC3)) //RB0输入捕获中断
  16.    {
  17.       TIM_ClearITPendingBit(TIM3,TIM_IT_CC3); //清除中断标志位
  18.       if(!CapStatus)
  19.       {
  20.          CapStatus = 1;
  21.          TIM_SetCounter(TIM3,0); //计数器清零
  22.       }
  23.       else if(CapStatus) //已经捕获到第1个下降沿
  24.       {
  25.          CapStatus = 0;
  26.          FrequencyTemp=TIM_GetCapture3(TIM3)+TIM3_CH3_Capture*65536;//计算两个下降沿总计数
  27.          TIM3_CH3_Capture = 0; //溢出次数清零
  28.          TIM_SetCounter(TIM3,0); //计数器清零
  29.          FrequencyValue = 400000000/FrequencyTemp;//计算频率,比如5000,单位0.01Hz
  30.       }
  31.    }
  32. }

图2.1.2 继保频率设置

图2.1.3 实际测量值

2、通过FFT实现

1)概述

下面是采集PC1口(图1.2.1)的小通道电流信号,计算频率,其固件具ST官方DSP库实现FFT,测试固件移步:FFT(具ST官方DSP库实现)

图2.2.1 实际测量值

------------------------------

2)数据采集

使用STM32F103自带的12位ADC进行数据采集,定时器触发ADC采集,DMA搬运,定时器时间自行设置,采样频率已知。此部分相关内容移步:ADC转换汇总(采样原理、取平均、精度等)

------------------------------

3)功能介绍

通过FFT可以准确测量电压值、电流值;互感器二次值精确到小数点后2位无压力,电流范围大,硬件增加大小通道、程序分别采集;涌流二次谐波含量最多,故可实现二次谐波制动,谐波的相关介绍移步:电力-涌流抑制与谐波

三段式保护移步:继电保护原理1.1-电流、方向保护,反时限移步:继电保护原理1.2-反时限过流保护,重合闸移步:继电保护原理4-自动重合闸第五章、自动重合闸

-----------------

已实现FFT的控制板:

图2.2.2 主控板正面

图2.2.3 主控板反面

-----------------

GSM短信功能介绍移步:SIM7600CE模块(GSM/GPRS)调试

图2.2.4 4G短信试验板

-----------------

GPRS数据功能介绍移步:STM32-GPRS模块连接系统主站,可以采用映翰通工业数据终端做透明传输。

-----------------

以太网功能板,本方案基于W5500,详细的介绍移步:网络知识汇总(基于W5500以太网)

图2.2.5 以太网主控板

图2.2.6 以太网接口板

3、屏显驱动介绍

移步:12864液晶显示原理(C代码)

图2.3.1 英文显示

三、测频法计算频率

网上找的资料,不保证正确性,没有实际测试过,仅供参考。通过在一定时间内检测跳边沿的个数可计算出频率:频率 = 上升沿或下降沿个数/统计时间。

1、外部中断统计跳边沿个数

配置一个定时器每隔一定时间对频率进行计算。部分代码如下。

  1. void exti_init(void)  //外部中断初始化函数
  2. {
  3. GPIO_InitTypeDef GPIO_InitStructure;
  4. EXTI_InitTypeDef EXTI_InitStructure;
  5. NVIC_InitTypeDef NVIC_InitStructure;
  6. RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
  7. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
  8.        
  9. GPIO_InitStructure.GPIO_Pin=GPIO_Pin_2;
  10. GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
  11. GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
  12. GPIO_Init(GPIOC,&GPIO_InitStructure);
  13.  
  14. GPIO_EXTILineConfig(GPIO_PortSourceGPIOC, GPIO_PinSource2);
  15. //选择GPIO引脚用作外部中段线路
  16. //此处一定要记住给端口管脚加上中断外部线路
  17.  
  18. EXTI_InitStructure.EXTI_Line=EXTI_Line2;
  19. EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
  20. EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;  //下降沿进中断
  21. EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  22. EXTI_Init(&EXTI_InitStructure); 
  23. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);  
  24. NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn; //打开EXTI2的全局中断
  25. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //设置优先级
  26. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;   
  27. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;   //使能
  28. NVIC_Init(&NVIC_InitStructure);
  29. }
外部中断函数:
  1. void EXTI2_IRQHandler(void)    
  2. {
  3. if(EXTI_GetITStatus(EXTI_Line2)==SET)
  4. {
  5.     EXTI_ClearITPendingBit(EXTI_Line0);//清中断
  6. if(GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_2)==Bit_RESET)    //确定沿
  7. {
  8. cnt++;
  9. }
  10. }
定时器中断函数:
  1. void TIM3_IRQHandler(void)   
  2. {
  3. frequent = cnt; //定时器设置时间为1s时
  4. cnt = 0//清零计数cnt
  5. TIM_ClearITPendingBit(TIM3,TIM_IT_Update);    //清标志位
  6. }

2、定时器外部计数的方法

采用定时器外部计数的方法,另外一个定时器负责每隔一段时间计算频率,部分代码如下。

  1. void time_init(void)
  2. {
  3. GPIO_InitTypeDef GPIO_InitStructure;
  4. TIM_TimeBaseInitTypeDef TIM2_TimeBaseInitStructure;  
  5. TIM_TimeBaseInitTypeDef TIM3_TimeBaseInitStructure;
  6. NVIC_InitTypeDef NVIC_InitStructure;
  7. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
  8. RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
  9. RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
  10. GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
  11. GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
  12. GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
  13. GPIO_Init(GPIOA,&GPIO_InitStructure);
  14. TIM_ClearITPendingBit(TIM2,TIM_IT_Update);//清除TIM2中断标志位
  15. TIM2_TimeBaseInitStructure.TIM_Period = 0xFFFF;//设置自动重装载值
  16. TIM2_TimeBaseInitStructure.TIM_Prescaler = 0;//设置分频
  17. TIM2_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; 
  18. TIM2_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数
  19. TIM_TimeBaseInit(TIM2,&TIM2_TimeBaseInitStructure);
  20.   
  21. TIM_ETRClockMode1Config(TIM2, TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted, 0x00);  //设置为采用外部时钟计数,可设定滤波参数消除信号干扰
  22. TIM_Cmd(TIM2,ENABLE); 
  23. TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
  24. TIM3_TimeBaseInitStructure.TIM_Period = 999;
  25. TIM3_TimeBaseInitStructure.TIM_Prescaler = 3599;
  26. TIM3_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; 
  27. TIM3_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
  28. TIM_TimeBaseInit(TIM3,&TIM3_TimeBaseInitStructure);
  29. TIM_Cmd(TIM3,ENABLE);
  30. TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE );
  31. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
  32. NVIC_InitStructure.NVIC_IRQChannel=TIM3_IRQn; 
  33. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
  34. NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;  
  35. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  36. NVIC_Init(&NVIC_InitStructure);
  37. }
定时器中断函数:
  1. void TIM3_IRQHandler(void)  
  2. {
  3. static u8 i;
  4. static u32 frequent_sum;
  5. TIM_ClearITPendingBit(TIM3,TIM_IT_Update);   //清中断
  6. if(i<19)
  7. {
  8. cnt += TIM_GetCounter(TIM2);  //,获取计数器的值,累加减少误差
  9. TIM_SetCounter(TIM2,0);    //计数器清零
  10. i++;
  11. }
  12. else
  13. {
  14. cnt += TIM_GetCounter(TIM2);
  15. TIM_SetCounter(TIM2,0);
  16. cnt += cnt*0.000025; //根据实际情况修改系数线性补偿
  17. frequent = cnt;
  18. i = 0;
  19. cnt = 0;
  20. }
  21. }

首先要有一个具体目标,那些所有你认为必须前置的知识,都可以在完成目标的过程中学到。觉得不错,动动发财的小手点个赞哦!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/秋刀鱼在做梦/article/detail/948953
推荐阅读
相关标签
  

闽ICP备14008679号