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是720
		num_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字节。
在这里插入图片描述

  • 14
    点赞
  • 65
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值