STM32测量频率.docx
- 文档编号:12031195
- 上传时间:2023-04-16
- 格式:DOCX
- 页数:31
- 大小:31.21KB
STM32测量频率.docx
《STM32测量频率.docx》由会员分享,可在线阅读,更多相关《STM32测量频率.docx(31页珍藏版)》请在冰豆网上搜索。
STM32测量频率
【教程】使用STM32测量频率和占空比的几种方法(申请置酷!
)
这几天在论坛上面解答了好几个询问STM32测量频率的贴子,觉得这种需求还是存在的(示波器、电机控制等等)。
而简单搜索了一下论坛,这方面的贴子有但是不全。
正好今年参加比赛做过这方面的题目(最后是一等奖嘿嘿),所以把我们当时尝试过的各种方案都列出来,方便以后大家使用,也是作为一个长期在论坛的潜水党对论坛的回报。
PS:
由于我们当时的题目除了测量频率之外,更麻烦的是测量占空比。
而这两个的测量方法联系比较紧密,所以也一并把测量占空比的方法写出来。
因为时间有限,所以并不能把所有思路都一一测试,只是写在下面作为参考,敬请谅解。
使用平台:
官方STM32F429DISCOVERY开发板,180MHz的主频,定时器频率90MHz。
相关题目:
(1)测量脉冲信号频率f_O,频率范围为10Hz~2MHz,测量误差的绝对值不大于0.1%。
(15分)
(2)测量脉冲信号占空比D,测量范围为10%~90%,测量误差的绝对值不大于2%。
(15分)
思路一:
外部中断
思路:
这种方法是很容易想到的,而且对几乎所有MCU都适用(连51都可以)。
方法也很简单,声明一个计数变量TIM_cnt,每次一个上升沿/下降沿就进入一次中断,对TIM_cnt++,然后定时统计即可。
如果需要占空比,那么就另外用一个定时器统计上升沿、下降沿之间的时间即可。
缺点:
缺陷显而易见,当频率提高,将会频繁进入中断,占用大量时间。
而当频率超过100kHz时,中断程序时间甚至将超过脉冲周期,产生巨大误差。
同时更重要的是,想要测量的占空比由于受到中断程序影响,误差将越来越大。
总结:
我们当时第一时间就把这个方案PASS了,没有相关代码(这个代码也很简单)。
不过,该方法在频率较低(10K以下)时,可以拿来测量频率。
在频率更低的情况下,可以拿来测占空比。
思路二:
PWM输入模式
思路:
翻遍ST的参考手册,在定时器当中有这样一种模式:
总结:
我们当时第一时间就把这个方案PASS了,没有相关代码(这个代码也很简单)。
不过,该方法在频率较低(10K以下)时,可以拿来测量频率。
在频率更低的情况下,可以拿来测占空比。
思路二:
PWM输入模式
思路:
翻遍ST的参考手册,在定时器当中有这样一种模式:
简而言之,理论上,通过这种模式,可以用硬件直接测量出频率和占空比。
当时我们发现这一模式时欢欣鼓舞,以为可以一步解决这一问题,代码如下:
1.voidTim2_PWMIC_Init(void)
2.{
3.GPIO_InitTypeDefGPIO_InitStructure;
4.NVIC_InitTypeDefNVIC_InitStructure;
5.TIM_ICInitTypeDefTIM_ICInitStructure;
6./*TIM4clockenable*/
7.RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);
8.
9./*GPIOBclockenable*/
10.RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);
11.
12./*TIM4chennel2configuration:
PB.07*/
13.GPIO_InitStructure.GPIO_Pin=GPIO_Pin_7;
14.GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF;
15.GPIO_InitStructure.GPIO_Speed=GPIO_Speed_100MHz;
16.GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;
17.GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_UP;
18.GPIO_Init(GPIOB,&GPIO_InitStructure);
19.
20./*ConnectTIMpintoAF2*/
21.GPIO_PinAFConfig(GPIOB,GPIO_PinSource7,GPIO_AF_TIM4);
22.
23./*EnabletheTIM4globalInterrupt*/
24.NVIC_InitStructure.NVIC_IRQChannel=TIM4_IRQn;
25.NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
26.NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
27.NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
28.NVIC_Init(&NVIC_InitStructure);
29.
30.TIM_ICInitStructure.TIM_Channel=TIM_Channel_2;
31.TIM_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;
32.TIM_ICInitStructure.TIM_ICSelection=TIM_ICSelection_DirectTI;
33.TIM_ICInitStructure.TIM_ICPrescaler=TIM_ICPSC_DIV1;
34.TIM_ICInitStructure.TIM_ICFilter=0x0;
35.
36.TIM_PWMIConfig(TIM4,&TIM_ICInitStructure);
37.
38./*SelecttheTIM4InputTrigger:
TI2FP2*/
39.TIM_SelectInputTrigger(TIM4,TIM_TS_TI2FP2);
40.
41./*SelecttheslaveMode:
ResetMode*/
42.TIM_SelectSlaveMode(TIM4,TIM_SlaveMode_Reset);
43.TIM_SelectMasterSlaveMode(TIM4,TIM_MasterSlaveMode_Enable);
44.
45./*TIMenablecounter*/
46.TIM_Cmd(TIM4,ENABLE);
47.
48./*EnabletheCC2InterruptRequest*/
49.TIM_ITConfig(TIM4,TIM_IT_CC2,ENABLE);
50.
51.}
52.//中断程序:
53.voidTIM4_IRQHandler(void)
54.{
55./*ClearTIM4Capturecompareinterruptpendingbit*/
56.TIM_ClearITPendingBit(TIM4,TIM_IT_CC1|TIM_IT_CC2);
57.
58./*GettheInputCapturevalue*/
59.IC2Value=TIM_GetCapture2(TIM4);//周期
60.
61.if(IC2Value!
=0)
62.{
63.highval[filter_cnt]=TIM_GetCapture1(TIM4);//高电平周期
64.waveval[filter_cnt]=IC2Value;
65.filter_cnt++;
66.if(filter_cnt>=FILTER_NUM)
67.filter_cnt=0;
68.}
69.else
70.{
71.DutyCycle=0;
72.Frequency=0;
73.}
74.}
75.//主循环:
76.while
(1)
77.{
78.uint32_thighsum=0,wavesum=0,dutysum=0,freqsum=0;
79.LCD_Clear(0);
80.for(i=0;i 81.{ 82.highsum+=highval[i]; 83.wavesum+=waveval; 84.} 85.[/i]delay_ms (1); 86.DutyCycle=highsum*1000/wavesum; 87.Frequency=(SystemCoreClock/2*1000/wavesum); 88.freq=Frequency*2.2118-47.05;//线性补偿 89.sprintf(str,"DUTY: %3d\nFREQ: %.3fKHZ\n",DutyCycle,freq/1000); 90. 91.LCD_ShowString(0,200,str); 92.delay_ms(100); 93.} 但是,经过测量之后发现这种方法测试数据不稳定也不精确,数据不停跳动,且和实际值相差很大。 ST的这些功能经常有这种问题,比如定时器的编码器模式,在0点处频繁正负跳变时有可能会卡死。 这些方法虽然省事,稳定性却不是很好。 经过线性补偿可以一定程度上减少误差(参数在不同情况下不同): freq=Frequency*2.2118-47.05; 这种方法无法实现要求。 所以在这里我并不推荐这种方法。 如果有谁能够有较好的程序,也欢迎发出来。 思路三: 输入捕获 思路: 一般来说,对STM32有一定了解的坛友们在测量频率的问题上往往都会想到利用输入捕获。 首先设定为上升沿触发,当进入中断之后(rising)记录与上次中断(rising_last)之间的间隔(周期,其倒数就是频率)。 再设定为下降沿,进入中断之后与上升沿时刻之差即为高电平时间(falling-rising_last),高电平时间除周期即为占空比 程序如下,注意由于为了减少程序复杂性使用了32位定时器5(计数周期如果是1us时可以计数4294s,否则如果是16位只能计数65ms),如果需要在F1上使用则需要自行处理: 1.//定时器5通道1输入捕获配置 2.//arr: 自动重装值(TIM2,TIM5是32位的! ! ) 3.//psc: 时钟预分频数 4.voidTIM5_CH1_Cap_Init(u32arr,u16psc) 5.{ 6.GPIO_InitTypeDefGPIO_InitStructure; 7.TIM_TimeBaseInitTypeDefTIM_TimeBaseStructure; 8.NVIC_InitTypeDefNVIC_InitStructure; 9. 10. 11.RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);//TIM5时钟使能 12.RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);//使能PORTA时钟 13. 14.GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;//GPIOA0 15.GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF;//复用功能 16.GPIO_InitStructure.GPIO_Speed=GPIO_Speed_100MHz;//速度100MHz 17.GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;//推挽复用输出 18.GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_DOWN;//下拉 19.GPIO_Init(GPIOA,&GPIO_InitStructure);//初始化PA0 20. 21.GPIO_PinAFConfig(GPIOA,GPIO_PinSource0,GPIO_AF_TIM5);//PA0复用位定时器5 22. 23. 24.TIM_TimeBaseStructure.TIM_Prescaler=psc;//定时器分频 25.TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;//向上计数模式 26.TIM_TimeBaseStructure.TIM_Period=arr;//自动重装载值 27.TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; 28. 29.TIM_TimeBaseInit(TIM5,&TIM_TimeBaseStructure); 30. 31. 32.//初始化TIM5输入捕获参数 33.TIM5_ICInitStructure.TIM_Channel=TIM_Channel_1;//CC1S=01选择输入端IC1映射到TI1上 34.TIM5_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;//上升沿捕获 35.TIM5_ICInitStructure.TIM_ICSelection=TIM_ICSelection_DirectTI;//映射到TI1上 36.TIM5_ICInitStructure.TIM_ICPrescaler=TIM_ICPSC_DIV1;//配置输入分频,不分频 37.TIM5_ICInitStructure.TIM_ICFilter=0x00;//IC1F=0000配置输入滤波器不滤波 38.TIM_ICInit(TIM5,&TIM5_ICInitStructure); 39. 40.TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);//允许更新中断,允许CC1IE捕获中断 41. 42.TIM_Cmd(TIM5,ENABLE);//使能定时器5 43. 44. 45.NVIC_InitStructure.NVIC_IRQChannel=TIM5_IRQn; 46.NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;//抢占优先级 47.NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;//子优先级 48.NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;//IRQ通道使能 49.NVIC_Init(&NVIC_InitStructure);//根据指定的参数初始化VIC寄存器、 50. 51. 52.} 53.//捕获状态(对于32位定时器来说,1us计数器加1,溢出时间: 4294秒) 54.//定时器5中断服务程序 55.voidTIM5_IRQHandler(void) 56.{ 57. 58.if(TIM_GetITStatus(TIM5,TIM_IT_CC1)! =RESET)//捕获1发生捕获事件 59.{ 60.if(edge==RESET)//上升沿 61.{ 62.rising=TIM5->CCR1-rising_last; 63.rising_last=TIM5->CCR1; 64.TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling);//CC1P=0设置为上升沿捕获 65.edge=SET; 66.} 67.else 68.{ 69.falling=TIM5->CCR1-rising_last; 70.TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising);//CC1P=0设置为上升沿捕获 71.edge=RESET; 72.} 73.} 74.TIM_ClearITPendingBit(TIM5,TIM_IT_CC1|TIM_IT_Update);//清除中断标志位 75.} 76.主程序: 77.while (1) 78.{ 79.uint32_thighsum=0,wavesum=0,dutysum=0,freqsum=0; 80.LCD_Clear(0); 81. 82.delay_ms (1); 83. 84.sprintf(str,"rise: %3d\nfall: %d\nfall-rise: %d",rising,falling,falling-rising); 85.LCD_ShowString(0,100,str); 86.sprintf(str,"Freq: %.2fHz\nDuty: %.3f\n",90000000.0/rising,(float)falling/(float)rising);//频率、占空比 87.LCD_ShowString(0,200,str); 88.delay_ms(100); 89.} 注意的是,中断程序当中的变量rising,last因为多次修改的缘故,与名称本身含义有所区别,示意如下: 该方法尤其是在中低频(<100kHz)之下精度不错。 缺点: 稍有经验的朋友们应该都能看出来,该方法仍然会带来极高的中断频率。 在高频之下,首先是CPU时间被完全占用,此外,更重要的是,中断程序时间过长往往导致会错过一次或多次中断信号,表现就是测量值在实际值、实际值×2、实际值×3等之间跳动。 实测中,最高频率可以测到约400kHz。 总结: 该方法在低频率(<100kHz)下有着很好的精度,在考虑到其它程序的情况下,建议在10kHz之下使用该方法。 同时,可以参考以下的改进程序减少CPU负载。 改进: 前述问题,限制频率提高的主要因素是过长的中断时间(一般应用情景之下,还有其它程序部分的限制)。 所以进行以下改进: 1. 使用2个通道,一个只测量上升沿,另一个只测量下降沿。 这样可以减少切换触发边沿的延迟,缺点是多用了一个IO口。 2. 使用寄存器,简化程序 最终程序如下: 1./TIM2_CH1->PA5 2.//TIM2_CH2->PB3 3.voidTIM2_CH1_Cap_Init(u32arr,u16psc) 4.{ 5.GPIO_InitTypeDefGPIO_InitStructure; 6.TIM_TimeBaseInitTypeDefTIM_TimeBaseStructure; 7.NVIC_InitTypeDefNVIC_InitStructure; 8.TIM_ICInitTypeDefTIM_ICInitStructure; 9. 10.TIM_DeInit(TIM2); 11.RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//TIM2时钟使能 12.RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOB,ENABLE);//使能PORTA时钟 13. 14.GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;//GPIOA0 15.GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF;//复用功能 16.GPIO_InitStructure.GPIO_Speed=GPIO_Speed_25MHz;//速度100MHz 17.GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;//推挽复用输出 18.GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_DOWN;//下拉 19.GPIO_Init(GPIOA,&GPIO_InitStructure);//初始化PA0 20. 21.GPIO_InitStructure.GPIO_Pin=GPIO_Pin_3;//GPIOA0 22.GPIO_Init(GPIOB,&GPIO_InitStructure);//初始化PA0 23. 24.GPIO_PinAFConfig(GPIOA,GPIO_PinSource5,GPIO_AF_TIM2);//PA0复用位定时器5 25.GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_TIM2);//PA0复用位定时器5 26. 27.TIM_TimeBaseStructure.TIM_Prescaler=psc;//定时器分频 28.TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;//向上计数模式 29.TIM_TimeBaseStructure.TIM_Period=arr;//自动重装载值 30.TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; 31. 32.TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure); 33.//初始化TIM2输入捕获参数 34.TIM_ICInitStructure.TIM_Channel=TIM_Channel_1;//CC1S=01选择输入端IC1映射到TI1上 35.TIM_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;//上升沿捕获 36.TIM_ICInitStructure.TIM_ICSelection=TIM_ICSelection_DirectTI;//映射到TI1上 37.TIM_ICInitStructure.TIM_ICPrescaler=TIM_ICPSC_DIV1;//配置输入分频,不分频
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- STM32 测量 频率