STM32输出1-500KHz任意整数频率脉冲,代码时间空间优化实现误差最小频率输出。
提示:此文章只是分析了一种优化STM32发送脉冲减少误差的方法实现,由于本人水平有限,该方法并不是最优解,但确是一种比较容易理解的实现方法。
STM32输出1-500KHz任意整数频率脉冲,代码时间空间优化实现误差最小频率输出。
- 前言
- 一、问题及简化后的数学模型
- 二、解决方法分析
- 三、最终结果
前言
在使用单片机发送脉冲时,往往要求发送范围比较广的任意频率的脉冲,在STM32当中实现指定频率脉冲的发送时,需要计算预分频和重装载值,但是有些频率,可以由多个预分频和重装载值计算得出,有些频率无法通过预分频和重装载值计算得出,只能计算出与该频率误差最小的频率进行代替,并且由频率反推计算预分频和重装载值需要消耗CPU较多资源,如果提前将每个频率对应最小误差的预分频值和重装载值计算出来,确实可以减少输出频率的误差,并降低CPU的资源,但是往往实际应用过程当中需要输出的频率范围较广,这样得到的预分频值和重装载值将会非常庞大,本文章就此提出一种优化空间时间性能且便于理解,方便实现的方法。
提示:以下是本篇文章正文内容
一、问题及简化后的数学模型
在定时器时间基准固定为最大72MHz时,控制STM32输出脉冲的周期、频率取决于PSC(预分频)和ARR(重装载),有等式:PSC * ARR * F = 72 000 000,当在输入F确定时,可得等式:PSC * ARR = 72 000 000 / F,即PSC * ARR = 确定值。但是PSC和ARR在32单片机当中都是16位的寄存器,所以也就有了限制条件:0 < PSC < 65535 、 0 < ARR < 65535 。所以求PSC和ARR也就化简为一道数学题。
已知0<psc<65535,0<arr<65535,0< f <72 * 1000 * 1000,在 psc 、 arr 和 f 均为整数,且 f 已定的前提下,求 psc 和 arr 的数值使得 psc * arr * f = 72 * 1000 * 1000 的误差最小。
二、解决方法分析
在我们确定F的情况下,PSC * ARR = 72 000 000 / F 等同于PSC * ARR = T,T = 72 000 000 / F。由于PSC和ARR的限制条件(小于65536的自然数),导致PSC * ARR不一定等于T(比如T为大于65535的质数),但是,我们应该要减少由PSC和ARR得到的频率与所需求频率的误差,下面就是我的思想:
首先判断PSC * ARR = T等式能否直接成立,通过T对范围内所有不同的PSC相除,判断是否余数为0,如果为0,说明存在两个整数相乘可以得到T,其中除于PSC后得到的值为ARR,但是同时需要注意ARR也有限制条件,只有满足:T能被在范围内的PSC整除,且整除结果ARR也在范围内,才能得到误差为0的PSC * ARR组合。(使用遍历的方法得到满足 PSC * ARR = T 公式的PSC和ARR)
如果T没有符合条件的PSC * ARR组合,那可以先试试T+1(误差为1/(T+1))是否具有满足 PSC * ARR = T+1 公式的PSC和ARR,如果依然没有的话,再尝试一下T-1(误差为1/(T-1)),仍然没有的话,继续尝试T+2(误差为2/(T+2))、T-2(误差为2/(T-2))、T+3(误差为3/(T+3))…等方案,同时,还需要考虑到T+x确保在0 ~ 65535*65535的范围内(不在此范围内的T是永远得不到满足条件的PSC * ARR组合)。最终在满足这些条件下得出对应的PSC * ARR组合,而由此组合得到的单片机脉冲频率与所需频率误差最小(事实上由频率转成T往往存在小数,本方案忽略小数部分的误差)
同时,也可以测量一下所有频率通过此方法计算出 PSC * ARR 组合所需的最长时间,和由计算出来的 PSC * ARR 组合生成的PWM的周期的最大误差,如下图所示(PWM输出范围为1Hz ~ 100KHz时):(源代码贴在后面)
在Windows平台上的情况:
(因为clock();精度以及运行环境的影响,实际运行速度可能稍微有点偏差,导致每次计算同一频率所花费的时间都不一样,但是不影响我们接下来的测试。)
从图片当中可以看到,当 频率为11 Hz时,计算机最长需要时间0.001s便可以计算出结果
而在STM32F103平台上,我们通过设置断点,测量转换时间最长频率在STM32F103平台下需要的时间:
执行到断点一的时间:0.00018693
执行到断点二的时间:9.00021275
在STM32F103平台上,一次频率转换最长时间为9.00021275 - 0.00018693 = 9.00002582
也就是9s多,对于单片机的来说,根本等不了9s时间只为得到一个误差最小的PSC * ARR~
所以通过MCU自己将所有的频率转换为PSC * ARR组合,方案根本行不通
那还可以采用以下方案:
1.计算机计算好数值通过通信将PSC和ARR两个参传进去
2.单片机自己提前将PSC和ARR保存在存储器当中,需要时在取出来
但也只是将运算时间减少,但是误差依然减少不了。
这相当于,在输出1Hz ~ 100KHz时,频率转换为PSC和ARR最长需要 9.00002582 s,最大误差达到0.138688%之大。
对于第一种方案,需要计算机与单片机一直长期通信连接,通过上位机先算好PSC和ARR,在下发到传入单片机,而对于第二种方式需要先提前在计算机平台计算好所有的PSC和ARR数组,存入单片机当中,单片机执行程序时在需要的时候再取出PSC和ARR。但是第一种方式局限性太大,所以优先考虑第二种方式。
在Windows平台下,我们可以将计算好的PSC和ARR按照C语言数组格式输出到.c文件当中,在数组加上全局,只读变量等关键词,花括号等符号后,便可以直接添加到STM32项目工程当中。
输出程序:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>int main()
{/*定义变量*/double Total_time,Total_timeMax=0.0;clock_t start, finish;double Error, ErrorMax=0.0; //误差unsigned long int num = 0, num_temp = 0, ErrornumMax, Total_timenumMax;unsigned char flag = 0, Symbol_Opt, Number_Offset;unsigned int i, f, ErrorfMax, Total_timefMax;int xx=0;unsigned short Psc; //对应周期下的预分配系数unsigned short Arr; //对应周期下的重装载值unsigned int Cycle; //STM32所有周期保存地址FILE* fp;//建立一个文件操作指针int err;if ((err = fopen_s(&fp, "1.h", "w")) != 0)//以追加的方式建立或打开1.txt,默认位置在你程序的目录下面{printf("无法打开此文件\n"); //如果打不开,就输出打不开exit(0); //终止程序}for (f = 1; f < 100 * 1000; f++)//j是频率:1 ~ 100k(100KHz){//start = clock();num = 72000000/ f; //100K是720num_temp = num;Symbol_Opt = 0;Number_Offset = 1;while (1){flag = 0;for (i = 1; i < 65536; i++)//4294770690{if (((num_temp / i) < 65536) && (num_temp % i == 0))//满足条件说明有可以乘积的数值{//对其进行保存Psc = i;Arr = num_temp / i;Cycle = num_temp;flag = 1;break;}}if (flag == 0)//说明这个数没有了,该对num进行处理了{if ((num <= (4294836225 - Number_Offset)) && (Symbol_Opt == 0))// 65535 * 65535 = 4294836225{num_temp = num + Number_Offset;Symbol_Opt = 1;}else if ((num - Number_Offset > 0) && (Symbol_Opt == 1)){num_temp = num - Number_Offset;Symbol_Opt = 0;Number_Offset++;}}else{break;}}/*输出到文件当中*/fprintf(fp,"0x%X,0x%X,", Psc, Arr);//同输出printf一样,以格式方式输出到文本中xx++;if (xx == 10){xx = 0;fprintf(fp,"\\\n");//同输出printf一样,以格式方式输出到文本中}/*记录计算机计算时间*///finish = clock();//Total_time = (double)(finish - start) / CLOCKS_PER_SEC; //单位换算成秒//if (Total_time > Total_timeMax)//{// Total_timefMax = f;// Total_timeMax = Total_time;// Total_timenumMax = num;//}Error = ((72000000.0 / (Psc * Arr)) - f) / f;//误差=(实际频率-理论频率)/理论频率。//if (Error > ErrorMax)//{// ErrorfMax = f;// ErrorMax = Error;// ErrornumMax = num;//}//printf("f=%-8lu num=%-8lu %8lu = %5lu * %5lu",f, num, Cycle, Psc, Arr);//printf(" 误差:%f%%\n", Error*100.0);}fclose(fp);//关闭流//printf("当mun为%8lu时,%8luHz,最长时间:%f\n", Total_timenumMax, Total_timefMax, Total_timeMax);printf("当mun为%8lu时,频率f为%8luHz时,最大误差:%f%%\n", ErrornumMax, ErrorfMax, ErrorMax*100.0);
/*
当mun为 6545454时,最长时间:0.001000
当mun为 721时,频率f为 99723Hz时,最大误差:0.138688%
*/
(为了方便,我将多个功能整合在上面一套代码当中,需要哪些功能自行分析,屏蔽代码便可实现)
最终结果如下:
经发现,在频率值达到某一频率时,一直满足Psc=1的条件。这是由于频率的提高,周期的减少,使得周期已经可以在65536 * 1/72000000 s之内。也就是PSC * ARR < 65536。
所以为了平衡时间性能和空间性能,我们可以将F大于该特定值时,通过Psc=1求得ARR的数值,小于该特定值的频率通过数组将PSC和ARR存起来。这样,将会大大减少Psc=1数组的空间。
首先求得最先得到Psc=1时的F,求得F结果如下:
所以只需要将1Hz ~ 1098Hz的数组保存下来即可。结果如下:
对比一下两文件大小区别:
由于加上了 const 修饰词,数组将会存储在MCU的 nor flash 当中,数组二维长度为1098,一维长度为2,元素类型为unsigned short:2字节,所以占用空间大小为:109822=4392 Byte,4K左右,连51片内都有这么大空间的Flash,在STM32当中更能存储下这些字节。
在程序当中只需要判断频率大于1098Hz便可以通过快速计算得出ARR和PSC,而在1Hz ~ 1098Hz之间,直接通过计算需要耗费较长时间和CPU资源,所以通过取数组的方式得到ARR和PSC。
代码如下:
void Frequency_Change_PSC_And_ARR(u32 Frequency,u16* psc,u16* arr)
{if(1<=Frequency && Frequency<=100000){if (Frequency<=1098){*psc=ARRPSC[Frequency-1][0];*arr=ARRPSC[Frequency-1][1];}else{*psc=1;*arr=72000000/((*psc)*Frequency);}}else{printf("输入频率不在1~100k之间。\n");}
}
三、最终结果
续~~~增加100K~500KHZ频率
由于摒弃了通过单片机自行计算出PSC和ARR的方案,所以我们目前可以不用考虑“为了得出PSC和ARR占用单片机太多CPU资源”这个问题,但是依然要考虑输出脉冲误差的问题,当输出频率较低时(1Hz ~ 1098Hz),也就是周期较高时,100KHz和500KHz没啥区别,当输出频率较高时,令Psc=1,单片机只需计算ARR,不需要耗费太多CPU资源,便可以节省高频对应的PSC和ARR数组空间。
但是频率的提升,也会增大输出脉冲频率的误差。
如下图:
从中可以看出100KHz脉冲和500KHz脉冲输出频率的误差相差不大,而且小到可以忽略(500KHz脉冲误差0.0069多),(虽然频率误差较小,但是频率大起来了,误差的脉冲数量就比较大)但是实际情况还得考虑对脉冲操作(如:输出方向反转,改变脉冲发送通道)等情况的延时对与输出脉冲的影响,F1系列最高才72MHz的主频,F4系列最高168MHz的主频,但是他们都是16位的定时器,使用F4后,相较于F1的低频(1Hz ~ 1098Hz)需要提前保存的数组10984字节,变成25634字节。
STM32输出1-500KHz任意整数频率脉冲,代码时间空间优化实现误差最小频率输出。相关推荐
- Verilog实现---1/x任意整数分频器通用代码
目录 1.偶数分频 2.奇数分频 3.代码文件说明 4.端口说明 5.测试&波形 6.代码 7.Reference 1.偶数分频 对于占空比为50%,分频系数为N的偶数分频,其核心思想是使用计 ...
- Python代码发现链表中的环并输出环中的第一个元素
Python代码发现链表中的环并输出环中的第一个元素 # Python代码发现链表中的环并输出环中的第一个元素 # Find first node of loop in a linked list # ...
- 咚咚咚————【封装驱动】Si5351A方波信号发生器发送任意(8K-160Mhz)频率程序
咚咚咚----[封装驱动]Si5351A方波信号发生器发送任意[8K-160Mhz]频率程序 (一)效果展示 (二)源码分享 (三)需要改进的地方及不足 (使用阿波罗STM32F7开发板) (一)效果 ...
- matlab x为整数,关于matlab中用什么字符表示任意整数
matlab做除法,怎么取整数? 方法一: floor(a/b);就是舍去小数点. ceil(a/b)就是舍去小数点+1的数. 方法二: fix(x)截尾取整 fix(x)不超过x的最大整数 ceil ...
- java 五个数字_关于java:五个任意整数找出其中第二大的数字
package comxaqf.w02_objectoriented.a_saturday1030; import java.util.Scanner; /** [题6] 6.五个任意整数,找出其中第 ...
- C语言随笔小算法:取出一个任意整数的每一位数值
C语言随笔小算法:取出一个任意整数的每一位数值 代码: #include "stdlib.h" #include "stdio.h"//将val的各位取出来 i ...
- python求1到n的奇数和_编写程序。输入任意整数n,计算1到n的奇数和。C语言编写程序 输入整数N 显...
编写程序.输入任意整数n,计算1到n的奇数和. C语言编写程序 输入整数N 显 www.zhiqu.org 时间: 2020-11-23 解题思路:循环判断1到N的每一个数: 若除以2是结果为整数,也 ...
- Python 输入任意整数,打印输入的数字是几位数
Python 输入任意整数,打印输入的数字是几位数 根据题目,这个比较简单,但有很多人会想的比较复杂,复杂在键盘输入0开始,后面接任意个零都可以,那么,比如,输入00024,这个输入是没有 ...
- 任意整数有几种分解方法 java_整数的分解方法
腾讯 2017春招真题 题目 如下示例: 1:共0种分解方法: 2:共0种分解方法: 3:3=2+1 共1种分解方法: 4:4=3+1=2+1+1 共2种分解方法: 5:5=4+1=3+2=3+1+1 ...
最新文章
- 左神讲算法——二分法及其拓展
- 基类的析构函数不能被继承。_为什么要把C++类中的析构函数声明为虚函数?
- linux开启内部路由转发功能
- 三因素三水平正交表l9_影响多腔导管挤出机头设计的关键因素
- AHK-UMSS框架 (AHK通用修饰键解决方案,任何键都是修饰键)
- 无符号右移负数_关于负数的右移与无符号右移运算小结
- linux下sqlmap安装教程,(转)Sqlmap官网下载与安装教程[windows/linux版本]
- java复制sheet_Java对excel中的sheet进行拷贝
- word2010赠送_我们将赠送两台LulzBot 3D打印机
- app嵌入jsp页面的项目工作量_好程序员Java学习路线分享jsp为什么用的不多了
- opencv打开的图片应用于nn.Conv2d()(二)
- Disruptor内存消息队列简单使用
- 机器学习的训练数据(Training Dataset)、测试数据(Testing Dataset)和验证数据(Validation Dataset)
- MVC通过重写OnActionExecuting获取控制器,方法和域
- php ini set开启方法,php ini_set更改php.ini配置功能_PHP教程
- NS方程求解-NSFnet
- python离线安装环境 解决 ERROR: Could not find a version that satisfies the requirement xxx 以及winError[10061]
- L337. 打家劫舍 III
- 银行对公业务数字化迎来新机遇
- GCC 预处理的宏 (predefined macros)
热门文章
- 【题解】nkoj 9061 通用的0
- AD layout完成后如何对板框进行修改
- PADS Layout制板文件和贴片文件的输出方法
- 大连理工计算机管理专业硕士,2018年大连理工大学硕士研究生招生专业一览表...
- 生产环境慎用 redis 模糊匹配功能!
- 如何做好一份程序员的工作汇报ppt?
- google金山词霸推出挑战有道桌面词典
- mysql数据库引擎转换_[转]MySQL数据库引擎
- c++蛮力法例子_学习方法系列丨贝叶斯学习法,我们几乎每天都在使用的数学工具...
- 圆角按钮css,基于CSS3的一组圆角按钮 - YangJunwei