![]() |
發布時間: 2021-9-5 13:23
正文摘要:請問exp()函數輸入輸出都是浮點數,計算的最終結果要一個整數。 請問要始何做才能得到正確的結果。下面的程序一得出的數值不正確。 公式在下圖。 #include <math.h> #defind FOSC/2/11059200 un ... |
Hephaestus 發表于 2021-9-7 05:07 ![]() |
litingkun 發表于 2021-9-6 18:37 樓主這個程序的計算方式耗時遠大于脈沖周期,怎么可能正常運行?完全相同的數據重復計算為什么不放在預處理完成?只不過算PCA定時器初值不至要用浮點運算。可以借鑒如下連接中本人回帖的思路處理PCA定時器初值。 http://www.zg4o1577.cn/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 |
![]() |
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))))); |