Hephaestus 发表于 2021-9-7 05:07 ![]() |
litingkun 发表于 2021-9-6 18:37 楼主这个程序的计算方式耗时远大于脉冲周期,怎么可能正常运行?完全相同的数据重复计算为什么不放在预处理完成?只不过算PCA定时器初值不至要用浮点运算。可以借鉴如下连接中本人回帖的思路处理PCA定时器初值。 http://www.51hei.com/bbs/forum.php?mod=viewthread&tid=211892 利用Excel工作表SUM函数功能,计算脉冲周期制作一个16位数组,直接调用数组赋值。 CCAP0L = Int_v; CCAP0H = Int_v >> 8; i++; |
给你贴个定点数exp()算法吧,32位,小数点在16位,也就是说0x10000表示1,0x7fffffff表示32768。函数的入口和出口都是这种定点格式。 |
litingkun 发表于 2021-9-6 18:37 你确定主函数中这个if (Int_value=3316)不改一下吗? |
litingkun 发表于 2021-9-6 18:37 你问题如果解决了,就不用再贴代码上来了. 运算时间长,是在我意料之中的。 也许有人觉得,浮点运算,对于没有硬件浮点运算器的51单片机没什么影响。但是,实际上影响还是很大的。 如果纯粹的 浮点数的加减乘除,还好一点。但是你的程序里用到了 EXP, 我没记错的话,EXP 是 MATH.H 函数库里的东西。 但凡用到了 MATH.H 我就没舒服过。有一次写代码用到 LOG, 直接把我弄得没脾气了。所以我才提议,能搞出一个 数据表的话, 用查表会好点。后来但凡遇到需要用MATH.H 的 我都尽量的改成查表。顺便提一下,KEIL 自带的函数库,都是比较傻的,如果有能力自己写几个函数库,会效率更高。举个简单例子,你LONG型除以一个CHAR 型,KEIL 的做法是把你的CHAR 型也变成 LONG,然后用加法器的SUBB 来移位减法来做除法,效率之低令人发指,自己写一个函数分段用DIV,效率能提升30倍。 所以,你要优化你的代码的话…… 可以考虑把你的那段公式拆分。能在 CHAR 或者 Short ,LONG完成的部分,先完成。最后再去进行浮点运算,浮点运算后如果后续可以不用浮点了,尽快转成整形,那么,单片机的效率又可以一个提升。 有些死的东西可以提前算好,放到临时变量。比如 FOSC/2/FREQDIV 你不用每次都算吧?可以先算好,来减轻浮点运算时的负担。 |
|
先感谢各位前辈指导,这个论坛的人真的很热心。 因只是自学,非商用,程序也没保密一说,只是全贴太多,怕耐心无法支持看完。所以只写了重点。 附件是程序,用PCA的高速脉冲输出控制步进电机(配控制器的),电机加速启动。加速的参数和方程上文已提到。方程写在脉冲中断里面,中断时计数器赋新值,方程计算下一个值。加速完成LED灯亮起。 但实现运行起来电机咔咔的响。灯亮的时间实际是8s.所以应该是每个脉冲都做浮点运算,运算时间比脉冲发送本身都长导致的。 ![]() 还想请教各位前辈,有什么办法实现脉冲频率按方程变化。如果写一个表在EEPROM里面,想通过按键改变加启变化就不容易了。 //S曲线脉冲输出C文件 //21-9-2 V1.0 //作者:Tony //说明:SYSclk=11059200 FOSC=SYSclk 模块0 // node节点数量大65536 #include "pulse.h" //!!注意点,一定要引入!! WORD Int_value; float Int_i,Pmin,Pmax,Pnode,Pscale; //全局变量 /***************************************************************************** ** 函数名称: PCA_isr() ** 功能描述: PCA脉冲中断 ** 全局变量: Int_i ; Pnode ; Int_value ;Pmin;Pmax;Pscale;Pnode ** 调用模块: 无 ** 输入: 无 ** 输出: 无 ******************************************************************************/ void PCA_isr() interrupt 7 { if ((unsigned int)Int_i < (unsigned int)Pnode ) //脉冲数量小于node数量 加速运行 { CCF0 = 0; //清除中断标示位 CCAP0L = Int_value; CCAP0H = Int_value>> 8; Int_value+= (signed int)(FOSC/2/FREQDIV/(Pmin+(Pmax-Pmin)/(1+exp(-Pscale*(Int_i-Pnode/2)/(Pnode/2))))); Int_i++; } else //最高脉冲匀速运行 { CCF0 = 0; //清除中断标示位 CCAP0L = Int_value; CCAP0H = Int_value>> 8; Int_value+= (signed int)(FOSC/2/FREQDIV/Pmax); P10=0; } } /***************************************************************************** ** 函数名称: ACCStart ** 功能描述: 加速启动函数 ** 全局变量: 无 ** 调用模块: main() ** 输入: fmin:启动脉冲频率,fmax:最高脉冲频率,scale:加速系统,node:脉冲数量 ** 输出: 无 ******************************************************************************/ void ACCStart(WORD fmin ,WORD fmax, BYTE scale, WORD node) { Int_value=0; //初始化脉冲数 if(CR == 1) //启动判断 CR=1退出程序 return; CCON = 0x00; //初始化PCA控制寄存器 PCA定时器停止 清除CF标志 清除模块中断标志 CMOD = 0x08; //PCA时钟为系统时钟 禁止寄存器CCON中CF位的中断 CL = 0x00; //复位PCA寄存器 CH = 0x00; CCAPM0 = 0x4d; //PCA模块0为16位定时器模式并使能脉冲输出 Pmin=fmin; Pmax=fmax; Pscale=scale; Pnode=node; Int_value=FOSC/2/FREQDIV/Pmin; //初始脉冲数赋值 CCAP0L = Int_value; CCAP0H = Int_value >> 8; Int_value+= (signed int)(FOSC/2/FREQDIV/(Pmin+(Pmax-Pmin)/(1+exp(-Pscale*(1-Pnode/2)/(Pnode/2))))); //第2个脉冲数计算 Int_i=2; CR = 1; //启动PCA计时器 EA = 1; //启动中断使能 } |
litingkun 发表于 2021-9-6 08:09 我也是服了,在我遇到的绝大多数人中,用到STC的,一般就是DIY或者学习,所以,我并不觉得一个一个STC的代码有什么保密性可言,你就不能上传一个完整的工程文件?到这问问题,你就应该先考虑到这个要不要保密的问题了 暂时不说这个,就你的程序而言 if (Int_value=3316) 你不得少了一个"="吗? Int_i在大于Pnode之后是怎么处理的? 我可以认为,你说的3秒,问题肯定不是出现在"浮点数"上,单片机在浮点运算上是很"弱",但并不是绝对的不能用 |
Y_G_G 发表于 2021-9-6 06:16 PCA 高速脉冲输出,系统时钟11059200 不分频。 CCON = 0x00; CMOD = 0x08; //PCA时钟为系统时钟 CL = 0x00; CH = 0x00; CCAPM0 = 0x4d; //PCA模块0为16位定时器模式并使能脉冲输出 |
litingkun 发表于 2021-9-6 00:20 看不到完整的程序,不知道怎么回事 没有看到PCA相关设定,PCA工作在什么模式? |
Y_G_G 发表于 2021-9-5 23:43 我用在脉冲输出,运算写在中断里,每个脉冲改一次频率,设了一个灯,如果加速完成,灯就亮。灯亮的时间大概是3秒。 void PCA_isr() interrupt 7 { if ((unsigned int)Int_i < (unsigned int)Pnode ) //脉冲数量小于node数量 加速运行 { CCF0 = 0; //清除中断标示位 CCAP0L = Int_value; CCAP0H = Int_value>> 8; Int_value+= (signed int)(FOSC/2/FREQDIV/(Pmin+(Pmax-Pmin)/(1+exp(-Pscale*(Int_i-Pnode/2)/(Pnode/2))))); Int_i++; } else //最高脉冲匀速运行 { CCF0 = 0; //清除中断标示位 CCAP0L = Int_value; CCAP0H = Int_value>> 8; Int_value+= (signed int)(FOSC/2/FREQDIV/Pmax); } } |
litingkun 发表于 2021-9-5 21:51 时间长并不是浮点数的问题 8位单片机在浮点运算上的能力是比较弱的,速度会很慢,用时相当的长 但这是对于CPU"时间"而言的,并不是针对现实的时间 你这个用时那么长,难不成是一直在计算?就是换了整型运算,也是一样的 (double)(GPS_Buffer1.GPS_Buffer[a+4]-48)/1000+(double)(GPS_Buffer1.GPS_Buffer[a+5]-48)/10000 )/60; 8051进行以上运算用了367个字节长度的代码进行,指令用了200条左右,12M晶振以1T的STC8051单片机运算,用时就是:200*1/12,大概就是16uS,往大了说,就是1mS也不算长呀 |
| 应该用PC上面的c语言编译器按照你的公式生成一个表格,比你现在的代码不仅快还省Flash空间。 |
| 单片机STC15W408AS |
调了两天最后数值调对了,从1600Hz加速到5304H,中间1000个脉冲,估计是0.26S 终果电机一抖一抖用了大概3S才加速完。 |
npn 发表于 2021-9-5 21:43 51内核的话,压根不是差的问题了……,是根本没有浮点运算能力,是用海量的整形来模拟浮点运算。所以,根本抗不住,用这种模拟浮点,把单片机做个计算器也就算了,做其他的东西,压根等不起。 |
litingkun 发表于 2021-9-5 18:53 8位单片机,浮点运算性能较差,具体需要多长时间需要用逻辑分析仪检测。 |
litingkun 发表于 2021-9-5 18:53 如果,能用 1K以内的 查表来代替的话,能不用浮点就不用浮点吧。 |
|
按两位指导改了一下测试程序,数值对了。也确实运算时间太长了。 #include <reg51.h> #include <math.h> #define FOSC 11059200L void main(void) { unsigned int ; float Int_i,scale,fmin,fmax,node; float Int_value; float b; P10=1; Int_i=1; fmin=1600; fmax=5304; scale=4; node=1000; b= exp((-scale*(Int_i-node/2)/(node/2))); Int_value= (unsigned int)(FOSC/2/(fmin+(fmax-fmin)/(1+b))); //if (b>54 && b<55) if (Int_value=3316) P10=0; else P10=1; while(1); } |
|
首先, 你要浮点运算的话,变量类型不能用INT 必须要用 float,其次尽可能所有参与浮点运算的变量最好都是 float 最后,不知道你的单片机什么内核的,如果是51内核,最好避免浮点运算。是真的抗不住。不管是最后编译后的代码量,还是对MCU的负荷都是巨大的。 |
|
Int_i的类型是无符号 Int_i-500这一步会等于65536-500=65036 应改作 Int_value=(unsigned int)(1600+(5304-1600)/(1+(exp(-5*(((float)Int_i-500)/500))))); |