找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 3002|回复: 20
收起左侧

请问单片机的浮点的运算要怎么处理?

[复制链接]
ID:961122 发表于 2021-9-5 13:23 | 显示全部楼层 |阅读模式
请问exp()函数输入输出都是浮点数,计算的最终结果要一个整数。
请问要始何做才能得到正确的结果。下面的程序一得出的数值不正确。
公式在下图。
#include <math.h>
#defind  FOSC/2/11059200
unsigned int Int_value;
unsigned int Int_i;
for((Int_i=0; Int_i < 1000; Int_i++)
{
Int_value=(unsigned int)(1600+(5304-1600)/(1+(exp(-5*(Int_i-500)/500)))
}

20160817141658638.jpg
回复

使用道具 举报

ID:161164 发表于 2021-9-5 16:50 | 显示全部楼层
Int_i的类型是无符号
Int_i-500这一步会等于65536-500=65036

应改作
Int_value=(unsigned int)(1600+(5304-1600)/(1+(exp(-5*(((float)Int_i-500)/500)))));
回复

使用道具 举报

ID:624769 发表于 2021-9-5 17:22 | 显示全部楼层
首先, 你要浮点运算的话,变量类型不能用INT
必须要用 float,其次尽可能所有参与浮点运算的变量最好都是 float
最后,不知道你的单片机什么内核的,如果是51内核,最好避免浮点运算。是真的抗不住。不管是最后编译后的代码量,还是对MCU的负荷都是巨大的。
回复

使用道具 举报

ID:961122 发表于 2021-9-5 18:53 | 显示全部楼层
按两位指导改了一下测试程序,数值对了。也确实运算时间太长了。
#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);
}
回复

使用道具 举报

ID:624769 发表于 2021-9-5 21:16 | 显示全部楼层
litingkun 发表于 2021-9-5 18:53
按两位指导改了一下测试程序,数值对了。也确实运算时间太长了。
#include
#include

如果,能用 1K以内的 查表来代替的话,能不用浮点就不用浮点吧。
回复

使用道具 举报

ID:57657 发表于 2021-9-5 21:43 | 显示全部楼层
litingkun 发表于 2021-9-5 18:53
按两位指导改了一下测试程序,数值对了。也确实运算时间太长了。
#include
#include

8位单片机,浮点运算性能较差,具体需要多长时间需要用逻辑分析仪检测。
回复

使用道具 举报

ID:624769 发表于 2021-9-5 21:49 | 显示全部楼层
npn 发表于 2021-9-5 21:43
8位单片机,浮点运算性能较差,具体需要多长时间需要用逻辑分析仪检测。

51内核的话,压根不是差的问题了……,是根本没有浮点运算能力,是用海量的整形来模拟浮点运算。所以,根本抗不住,用这种模拟浮点,把单片机做个计算器也就算了,做其他的东西,压根等不起。
回复

使用道具 举报

ID:961122 发表于 2021-9-5 21:51 | 显示全部楼层
调了两天最后数值调对了,从1600Hz加速到5304H,中间1000个脉冲,估计是0.26S 终果电机一抖一抖用了大概3S才加速完。
回复

使用道具 举报

ID:961122 发表于 2021-9-5 21:52 | 显示全部楼层
单片机STC15W408AS
回复

使用道具 举报

ID:883242 发表于 2021-9-5 23:34 | 显示全部楼层
应该用PC上面的c语言编译器按照你的公式生成一个表格,比你现在的代码不仅快还省Flash空间。
回复

使用道具 举报

ID:401564 发表于 2021-9-5 23:43 | 显示全部楼层
litingkun 发表于 2021-9-5 21:51
调了两天最后数值调对了,从1600Hz加速到5304H,中间1000个脉冲,估计是0.26S 终果电机一抖一抖用了 ...

时间长并不是浮点数的问题
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也不算长呀
回复

使用道具 举报

ID:961122 发表于 2021-9-6 00:20 | 显示全部楼层
Y_G_G 发表于 2021-9-5 23:43
时间长并不是浮点数的问题
8位单片机在浮点运算上的能力是比较弱的,速度会很慢,用时相当的长
但这是对 ...

我用在脉冲输出,运算写在中断里,每个脉冲改一次频率,设了一个灯,如果加速完成,灯就亮。灯亮的时间大概是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);

}
}
回复

使用道具 举报

ID:401564 发表于 2021-9-6 06:16 | 显示全部楼层
litingkun 发表于 2021-9-6 00:20
我用在脉冲输出,运算写在中断里,每个脉冲改一次频率,设了一个灯,如果加速完成,灯就亮。灯亮的时间大 ...

看不到完整的程序,不知道怎么回事
没有看到PCA相关设定,PCA工作在什么模式?
回复

使用道具 举报

ID:961122 发表于 2021-9-6 08:09 | 显示全部楼层
Y_G_G 发表于 2021-9-6 06:16
看不到完整的程序,不知道怎么回事
没有看到PCA相关设定,PCA工作在什么模式?

PCA 高速脉冲输出,系统时钟11059200 不分频。
CCON = 0x00;
CMOD = 0x08;                                //PCA时钟为系统时钟
CL = 0x00;
CH = 0x00;
CCAPM0 = 0x4d;                              //PCA模块0为16位定时器模式并使能脉冲输出
回复

使用道具 举报

ID:401564 发表于 2021-9-6 09:23 | 显示全部楼层
litingkun 发表于 2021-9-6 08:09
PCA 高速脉冲输出,系统时钟11059200 不分频。
CCON = 0x00;
CMOD = 0x08;                            ...

我也是服了,在我遇到的绝大多数人中,用到STC的,一般就是DIY或者学习,所以,我并不觉得一个一个STC的代码有什么保密性可言,你就不能上传一个完整的工程文件?到这问问题,你就应该先考虑到这个要不要保密的问题了

暂时不说这个,就你的程序而言
if (Int_value=3316)    你不得少了一个"="吗?
Int_i在大于Pnode之后是怎么处理的?
我可以认为,你说的3秒,问题肯定不是出现在"浮点数"上,单片机在浮点运算上是很"弱",但并不是绝对的不能用
回复

使用道具 举报

ID:961122 发表于 2021-9-6 18:37 | 显示全部楼层
    先感谢各位前辈指导,这个论坛的人真的很热心。
因只是自学,非商用,程序也没保密一说,只是全贴太多,怕耐心无法支持看完。所以只写了重点。
    附件是程序,用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;                                                                                                                                                                          //启动中断使能                                                                                       
}

回复

使用道具 举报

ID:624769 发表于 2021-9-6 19:06 | 显示全部楼层
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  你不用每次都算吧?可以先算好,来减轻浮点运算时的负担。
回复

使用道具 举报

ID:401564 发表于 2021-9-6 23:13 | 显示全部楼层
litingkun 发表于 2021-9-6 18:37
先感谢各位前辈指导,这个论坛的人真的很热心。
因只是自学,非商用,程序也没保密一说,只是全贴太多 ...

你确定主函数中这个if (Int_value=3316)不改一下吗?
回复

使用道具 举报

ID:883242 发表于 2021-9-7 05:07 | 显示全部楼层
  1. int32_t fxexp(int32_t x) {
  2.         int32_t t,y;

  3.         y=0x00010000;
  4.         t=x-0x58b91;if(t>=0) x=t,y<<=8;
  5.         t=x-0x2c5c8;if(t>=0) x=t,y<<=4;
  6.         t=x-0x162e4;if(t>=0) x=t,y<<=2;
  7.         t=x-0x0b172;if(t>=0) x=t,y<<=1;
  8.         t=x-0x067cd;if(t>=0) x=t,y+=y>>1;
  9.         t=x-0x03920;if(t>=0) x=t,y+=y>>2;
  10.         t=x-0x01e27;if(t>=0) x=t,y+=y>>3;
  11.         t=x-0x00f85;if(t>=0) x=t,y+=y>>4;
  12.         t=x-0x007e1;if(t>=0) x=t,y+=y>>5;
  13.         t=x-0x003f8;if(t>=0) x=t,y+=y>>6;
  14.         t=x-0x001fe;if(t>=0) x=t,y+=y>>7;
  15.         if(x&0x100)               y+=y>>8;
  16.         if(x&0x080)               y+=y>>9;
  17.         if(x&0x040)               y+=y>>10;
  18.         if(x&0x020)               y+=y>>11;
  19.         if(x&0x010)               y+=y>>12;
  20.         if(x&0x008)               y+=y>>13;
  21.         if(x&0x004)               y+=y>>14;
  22.         if(x&0x002)               y+=y>>15;
  23.         if(x&0x001)               y+=y>>16;
  24.         return y;
  25. }
复制代码

给你贴个定点数exp()算法吧,32位,小数点在16位,也就是说0x10000表示1,0x7fffffff表示32768。函数的入口和出口都是这种定点格式。
回复

使用道具 举报

ID:213173 发表于 2021-9-7 08:35 | 显示全部楼层
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++;




回复

使用道具 举报

ID:961122 发表于 2021-9-8 11:59 | 显示全部楼层
Hephaestus 发表于 2021-9-7 05:07
给你贴个定点数exp()算法吧,32位,小数点在16位,也就是说0x10000表示1,0x7fffffff表示32768。函数的入 ...

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表