這段時間一直在調試DSP6713的Flash燒寫,現在對Flash的燒寫也算心中了如。
那天,非常Happy的發現將閃爍LED燒寫到Flash成功了,然後,就以為一切都OK了……
那天,成功燒寫了一個300KB以上的程式,又認為,這次應該OK了……
那天,寫了個Timer中斷程式,燒寫到Flash,卻死機了……
那天,在RAM上運行很流暢的一個演算法(演算法中調用了CCS的atan函數),在燒寫到Flash後演算法卻死機了……
那天,我開始思考:是什麼情況導致RAM中跑得很Happy的程式燒寫到Flash就運行得如此的不堪——眾多的囧相。
“且行且珍惜”,珍惜這些次發現Bug的機會,因此,我要總結:在RAM中能正常運行,而燒寫到Flash後無法正常啟動並執行一些情況討論。
請檢查中斷向量表
中斷向量表包含了所有中斷的入口,在燒寫Flash的時候,有兩種方式可以保證中斷能正常工作。具體可參見TMS320C6713燒寫Flash的通用方法的第5小節。
請檢查程式中隱形記憶體錯誤
很多情況下,當出現數組越界時,在RAM中的程式都能正常運行,但在燒寫Flash後運行就會出現死機或程式跑飛的現象。
比如定義一個數組,
int x[5];
你使用x[5]=10這樣的語句在RAM程式中是某些時候能正確啟動並執行,在PC上應該也可以。但將這種程式燒寫到Flash之後運行,DSP果斷和你說拜拜!
因此,請謹慎地檢查程式碼中的數組越界和指標操作。在DSP程式中,堅決不使用C庫函數中的malloc函數。如果需要動態分配記憶體的操作,可以自己寫一個,或使用uCOS II或DSP/BIOS等嵌入式作業系統。
請盡量避免使用math.h中的三角及log等函數
也不知道是什麼原因,也可能是我對atan函數的使用方法不正確造成的吧。在我的一個最初的程式中,我是直接這樣計算atan(x)的,
float x,y;...y = atan(x); // x範圍為[0, 1.7]
在RAM中以及在PC中都多次測試過沒有任何問題。
燒寫Flash之後,也不是死機,但程式運行到atan這個函數的時候會卡上很長一段時間,再接著往下運行。
難道是math.h中的atan運算效率太低?但為什麼RAM中就能運行呢?這個還不清楚。
於是想了個招,在要使用三角函數和log等函數的地方都使用查表法替代庫函數,在精度要求高而儲存空間又有限的場合,可使用查表+插值的方式。
下面是改進方法計算atan,
/* table of determine ATAN(x) */const float atan_tb[] = { // 精度(0.020)0.00, 1.15, 2.29, 3.43, 4.57, 5.71 , 6.84, 7.97, 9.09, 10.20,11.31, 12.41, 13.50, 14.57, 15.64, 16.70 , 17.74, 18.78, 19.80, 20.81,21.80, 22.78, 23.75, 24.70, 25.64, 26.57 , 27.47, 28.37, 29.25, 30.11,30.96, 31.80, 32.62, 33.42, 34.22, 34.99 , 35.75, 36.50, 37.23, 37.95,38.66, 39.35, 40.03, 40.70, 41.35, 41.99 , 42.61, 43.23, 43.83, 44.42,45.00, 45.57, 46.12, 46.67, 47.20, 47.73 , 48.24, 48.74, 49.24, 49.72,50.19, 50.66, 51.12, 51.56, 52.00, 52.43 , 52.85, 53.27, 53.67, 54.07,54.46, 54.85, 55.22, 55.59, 55.95, 56.31 , 56.66, 57.00, 57.34, 57.67,57.99, 58.31, 58.63, 58.93, 59.24, 59.53 , 59.83 };y = atan_tb[((uint16_t)(x*100)) >> 1];
建立atan的表可以藉助Matlab。在需要插值的場合,比如,上面atan_tb的精度為0.02,而我們希望在少數的一些場合下使atan在0.01的精度,如果以0.01建表將會使表的資料存放區量擴大1倍,這是我們可以在0.02精度表的基礎上再使用插值的方式。
比如,要計算atan(0.03),我們可以從表中查到atan(0.02)和atan(0.04),如果僅使用線性插值的話,則
atan(0.03) = (atan(0.02) + atan(0.04)) / 2
請檢查程式的邏輯
曾傻傻的寫過一個類似下面的程式,
uint8_t dir; // 低3位進行了編碼,下面的switch進行解碼int dist_switch(int a, int b, int c){ int max_dist; int min_dist; int result = 0; switch (dir) { case 0x00: break; case 0x01: max=a;min=b;break; case 0x02: max=a;min=c;break; case 0x03: max=b;min=a;break; case 0x04: max=b;min=c;break; case 0x05: max=c;min=a;break; case 0x06: max=c;min=b;break; case 0x07: break; default: break; } result = max * 100 / (min + max); return result;}
咋一看,沒有文法問題,switch的break語句也加上了。
問題出就出在:dir低三位進行了編碼,最大編碼個數應該是8。而因為實際中只用到6種情況,switch中對其它的兩種編碼都使用break,問題就出來了,如果我的dir=0x00會怎麼樣?switch語句當然沒問題,問題在下一條語句:
result = max * 100 / (min + max);
dir=0x00沒有對max和min進行任何的賦值,而且其它地方也沒有。因此max和min作為局部變數將會是一個隨機的值,這在RAM中是能夠運行通過的,但燒寫到Flash之後,這種局部變數的不確定性直接回導致Flash宕機。
因此,對於switch以及if...else...的邏輯問題,不能只關注它們所在範圍,請仔細檢查其上下文。
請特別關照一下程式中的除法運算
x=a/b中若b可能為0,這樣的程式燒寫到Flash會直接導致DSP死機的。如果可以的話,盡量將除法運算轉換為移位元運算。
比如,要計算y=x/0.02,一個號的轉換方式就是:
y=(uint32_t)(x*100)/2=((uint32_t)(x*100) >> 1);
還可以更好一點,將*100也使用移位替代,
uint32_t tmp_x = (uint32_t)x;y = ((tmp_x<<6) + (tmp_x<<5) + (tmp_x<<2)) >> 1;
這樣你就再也看不到除法運算了。