完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
为实现测量频率这个功能,采用STM32的定时器功能,大体方案是用2个定时器来实现,
TIM4 定时器负责计数。 TIM2 定时器负责1秒产生一次中断,执行一次脉冲计数采集工作。记录下TIM4的计数值。 实现过程如下 先看下时钟频率 配置定时器TIM2 TIM2开中断 关于定时器4是如何计数的,外部时钟模式走的是TI1_ED 时钟线。这里为啥是65536呢? 是因为这个计数器是16位的。 最大只能到65536-1,溢出以后自动到中断值里面.count_over++ 这样就可以计数很大的次数了。当然不中断也可以,还可以定时器级联。这样就要多占用一个定时器了。 后来发现上面这个是错误的, 不能配置Time4的CH通道,时钟源改成ETR2就可以了。正确的是下面的效果,已验证。 实现代码如下 void HAL_TIM_PeriodElapsedCallback (TIM_HandleTypeDef * htim) { ///每秒定时器 ///定时器2.每1秒触发一次中断, 在中断中读取Time4 的脉冲数 if (htim == &htim2 ) //检查TIM2更新中断发生与否 { //1.读取Count count_i = __HAL_TIM_GET_COUNTER(&htim4); __HAL_TIM_SET_COUNTER(&htim4, 0); //重置定时计数器 //2.加上溢出数.* 36mhz count_i = count_over * 65536 + count_i; count_over = 0; } //TIM4 外部脉冲信号计数溢出,产生中断的时候就会进来。 //脉冲信号经过TIMx_CH1 输入滤波,和边沿检测后,转成TI1F_ED-》TRC时钟信号。然后这个Conte 记录的实际上是霍尔传感器的脉冲数。 ///定时器2.每次触发都是定时器计数超出了最大值,为了保证不丢失计数信息,只需要给溢出标志 count_over++; //if (htim-》 Instance ==TIM4 && __HAL_TIM_GET_FLAG(htim, TIM_FLAG_UPDATE) != RESET) //检查TIM2的中断是否更新中断,因为很多中断都会调用HAL_TIM_IRQHandler ,所以要区分一下 if (htim == &htim4 ) { count_over++; } } 今天发现定时器中断总是进不去。发现了2个问题 一。生成的配置文件有问题 需要点好几次NVIC中断,才能生成合格的代码。 生成的tim.c文件中应该有 HAL_NVIC_EnableIRQ(TIM2_IRQn);才能开启中断。 先确认下生成的代码有没有问题, 如果没有那么需要去勾上Nvic中断 再去掉。生成代码来回折腾几次就出来了。 二 。 定时器开中断没有开启 ,定时器开中断默认是没有开启的, 而且要在初始化代码里面手动初始化中断。 void MyInit(void) { HAL_TIM_Base_Start_IT(&htim2);//开中断才会触发中断函数 HAL_TIM_PeriodElapsedCallback HAL_TIM_Base_Start_IT(&htim4);//开中断才会触发中断函数 HAL_TIM_PeriodElapsedCallback } TIM Update event 计数更新事件的处理函数在HAL库的写法里面变了。 在标准库时代是TIM_IRQHandler 里面写, 后来HAL库发现已经实现了 HAL_TIM_IRQHandler 。 经过查看源代码发现, 在 void HAL_TIM_IRQHandler(TIM_HandleTypeDef *htim)中调用了HAL_TIM_PeriodElapsedCallback. 代码如下,那么只要实现 HAL_TIM_PeriodElapsedCallback方法就可以实现对 TIM Update event 的处理。也就是上面的代码。 void HAL_TIM_IRQHandler(TIM_HandleTypeDef *htim){ 。..。 。..。 /* TIM Update event */ if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_UPDATE) != RESET) { if (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_UPDATE) != RESET) { __HAL_TIM_CLEAR_IT(htim, TIM_IT_UPDATE); #if (USE_HAL_TIM_REGISTER_CALLBACKS == 1) htim-》PeriodElapsedCallback(htim); #else HAL_TIM_PeriodElapsedCallback(htim); #endif /* USE_HAL_TIM_REGISTER_CALLBACKS */ } } 。..。 。..。 } 在后来的调试过程中,发现。了另外两个问题。 第一个是传感器的信号始终得不到, 因为我采用的是3144霍尔传感器, 这种传感器的信号输出引脚是可以直接挂在STM32芯片上Time2的外部始终引脚上的 。 后来在小火箭兄弟的帮助下,找到了问题的原因, 这种传感器要把GPIO mode引脚改成上拉Pull-up. 这样传感器的输出电压才能上来, 因为这个3144霍尔传感器在检测到磁性物体时发出的信号是低电压… 所以在没设置上拉的时候电压达不到,所以就检测不到信号了… (这应该属于低级问题了,我电子和单片机刚入门新手,请谅解), 另外下面这个图里面的Time2 对于的应该是上文的Time4, 但是配置都是一样的,因为我的程序后来Time4的脉冲采集功能放在了Time2 Time2的定时1秒功能放在了Time1, 所以如下所见了。 第二个问题,测得的频率不及时。 这个问题是因为我上面的Time1 配置是按照1秒更新一次频率数据, 这1秒一次的速度对于人来讲还行,但是对于某些机器来讲是不行的, 例如四轴飞行器,1S更新一次数据估计都要坠机了。 所以为了更快的得到实时数据,我改成了100ms更新一次数据。 频率数据的实时性提高了,但是这样又引入了另外一个问题, 如果频率小于10HZ的时候,将无法测量准确。 因为在每100ms采样一次的时候有可能信号还没触发会在下一个100ms采样的时候触发。 … 这样会导致10hz以下的频率无法准确测得。为了解决这个问题,我引入了 循环数组,将最近10次的计数值。存储在一个数组中。 100ms采样1次,1秒钟正好采样10次, 所以我创建了一个长度为10的数组,用来存储每次采样获得的霍尔传感器的脉冲次数。 在每次取数据的时候在求和。就得到了1秒钟的脉冲总数… 这里要说明的是我并没有在100ms一次的中断里面立即计算结果。这样会导致性能方面的一些影响。 所以只是在中断中循环存储数据然后清零而已,取数据的时候再对数组求和。毕竟取数据的速度要低的多次数也少的多。 引入循环池后的代码如下 //频率相关的变量 //int32_t pulse_count=0; #define SAMPLECOUNT 10 //uint32_t count_over=0; uint32_t count_Array[SAMPLECOUNT]; uint8_t count_ArrayIndex=0; //频率相关的变量 //读取转速数据, void HAL_TIM_PeriodElapsedCallback (TIM_HandleTypeDef * htim) { if (htim == &htim2 ) { ///每0.1秒定时器 //1.读取霍尔传感器在0.1秒内收到的脉冲计数Count uint32_t count_i = __HAL_TIM_GET_COUNTER(&htim4); __HAL_TIM_SET_COUNTER(&htim4, 0); //把这个数值清零重新计数 if(count_ArrayIndex 》= SAMPLECOUNT) {count_ArrayIndex = 0 ;} count_Array[count_ArrayIndex] = count_i;//pulse_count count_ArrayIndex ++; } } //在主函数中读取频率函数,将1秒内的10次计数求和即可 int32_t ReadFrequency(void) { int32_t Frequency =0; for(int i=0;i《SAMPLECOUNT;i++){ Frequency += count_Array[i] ; } return Frequency; } 经过测试,此方法非常有效, 可以测得1-10HZ以下的频率, 最高目前测到150KHZ没啥大问题。 而且省去了一个溢出变量和一个判断, 再高的频率我还没有方法测试,因为没有合适的信号发生器…有条件了再测一下。 后来在经过实际比对的时候又发现一个致命的问题, 频率有点误差, 记录如下表: 1khz 误差8hz 2khz 误差15hz 3khz 误差22hz 4khz 误差30hz 5khz 误差38hz 6khz 误差45hz 7khz 误差51hz 8khz 误差60hz 基本上呈线性误差。 经过推测,在150khz的时候应该误差在1150左右, 但是实际上在150khz却只有200左右。 说明, 这个误差跟系统代码或逻辑没有什么关系。 因为如果是系统或代码引起的问题一般呈线性或固定误差。 那么这个误差是哪里来的呢? 经过2个小时的苦思冥想, 终于明白了。 应该是时间问题。这个时间应该是我们的0.1秒定时器的时间不准确导致的。 我们的0.1秒可能就不是标准的0.1秒,有可能比0.1秒多了0.001秒, 10次累积下来就差不多了。所以我就开始调周期定时器的配置,最终改成了下面这个样子 这个小小的修改就把误差从200多降到了非常低的水平。 当然原先的-1是完全不对的。 这里有个小技巧, 64001000和64010000理论上应该是一样的吧。但实际上不一样。 这个地方640个时钟周期计数一次和6400个时钟周期计数一次,差了10倍。 在这段时间里面就有可能越过了好多信号。 所以要想时间精准就要减少预分频数。两者的成绩最后任然是0.1秒即可。 相当于最小的时间片。 时间切得小,就越是精准。 但是下面的计数上限是65536. 不能超过这个数值。 当然还有最佳的数值应该是100个时钟周期计数一次,计数超过64000就是0.1秒。 这样的时间片最小, 定时也更接近真正的0.1秒。 最终就是下面的配置了。 我又产生了另外一个疑问。 如何才能准确的产生绝对的0.1秒呢? 这似乎是不可能的事情。..得和标准的原子时钟做比较。才行了。 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1649 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1566 浏览 1 评论
994 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
694 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1613 浏览 2 评论
1872浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
656浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
526浏览 3评论
543浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
515浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-27 20:07 , Processed in 0.829143 second(s), Total 75, Slave 59 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号