專家支招:單片機中無符號數運算出現的問題
發布時間:2015-10-05 責任編輯:susan
【導讀】在單片機編程中,我們經常會用到一些無符號數與有符號數的混合運算,另外我們所用的單片機很有可能是16位或者8位的,這樣,編程時所用的一些變量的取值範圍會對我們的運算有所限製。
比如說8位的單片機無符號數最大值為255,有符號最大數為127;16位單片機無符號數最大值為65535,有符號數最大值為32767.對於32的單片機來說,因為我們一般所處理的值很少能超過有符號數的最大取值,所以比較少遇到下麵出現的問題.
在一些運算中,我們希望有些數能表示正負,這就得用有符號數,而有些數的取值會超過有符號數的最大值,這時我們就得用無符數來表示.下麵是我編程時遇到的兩個問題(用的是MC9S12XS128處理器,16位的單片機).
變量的聲明如下:
int iError;
unsigned int uiExpectSpeed;
unsigned int uiCurrentSpeed;
語句如下:
iError = (uiExpectSpeed - uiCurrentSpeed)/3; //(1) 第一個語句
在調試的過程中發現這個iError的值有時候會特別大,最後才發現是上麵的這句語句出錯了!然後修改成下麵兩句結果就對了:
iError = uiExpectSpeed - uiCurrentSpeed; //(2)第二個語句
iError = iError/3; //(3)第三個語句
不同類型的數據在進行混合運算時會有一個隱試的類型轉換過程,有符號數與無符號數混合運算,有符號數會被轉換成無符號數後再參加運算.
在上麵的第一個語句中,如果uiExpectSpeed 比uiCurrentSpeed的值大,也就是uiExpectSpeed - uiCurrentSpeed結果為一正值,那不會出現啥問題,但當uiExpectSpeed 比uiCurrentSpeed的值小時就出現問題了,此時uiExpectSpeed - uiCurrentSpeed的臨時結果存放在16位的寄存器中,且最高位1,對於有符號數來說會把這一個位解釋為符號位,1表示負數,而對於無符號來說這個位就表示數值,接著這個臨時的結果除以3後,所得到的結果的最高位變為了0此時該結果會轉換為一個有符合數(不管是有符號數,還是無符號數,最高位為0時,所表示的數值就是一樣的),賦給iError.本應該得到一個負數的,但最終卻得到了一個比較大的正數!在第一個語句中,如果沒有除以3,而是兩個數作差後直接賦給iError則是不會出錯的,雖然uiExpectSpeed - uiCurrentSpeed運算的結果是一個很大的正數(寄存器的最高位為1),但在這個臨時結果賦給iError這個變量時,會先把這個值轉換為一個有符號數賦給iError.其實,在把uiExpectSpeed - uiCurrentSpeed運算的結果賦給iError時是把所有的位原封不動的複製到iErrorr所表示的內存單元中的,隻是我們是以有符號數來解釋這個內存單元中的內容,所以這個很大的正數就變成了一個負數!(數據在處理器內是以補碼表示的,對於數據是正還是負隻是人們的解釋不同而已).所以我就用後麵的兩句替換了第一句,這樣不管uiExpectSpeed - uiCurrentSpeed的差值是正還是負都能得到正確的結果了.
下麵是我在做超聲波測距時遇到的又一個很隱蔽的問題:
unsigned int start; //表示計時開始時計數器的值
unsigned int end; //表示計時結束時計數器的值
unsigned int error;
unsigned int distance; //表示距離
unsigned int time; //表示從計時開始到結束所用的時間
unsigned int remainder;//餘數
start = TCNT;// 計時開始, TCNT為16位的計時器寄存器
..............一段時間後(這段時間小於計時器TCNT從0計數到最大值65535所表示的時間)...........
end = TCNT; //計時結束
error = end - start; //注意,end有可能比start小,但由於都是無符號數,所以最後得到的差值就是這段時間內計數器TCNT的增量.
time = error/625; //單位為ms TCNT每1ms內數值增加625(這個數與TCNT所用的時鍾有關)
distance = 17*time; //單位為cm, 距離為速度乘以時間再除以2就是聲波所傳波的距離
這塊由於是分步計算的,所以會有比較大的誤差(主要是由於error/625後的餘數被丟棄了) 於是我改成如下語句:
start = TCNT;// 計時開始, TCNT為16位的計時器寄存器
..............一段時間後(這段時間小於計時器TCNT從0計數到最大值65535所表示的時間)...........
end = TCNT; //計時結束
error = end - start; //注意,end有可能比start小,但由於都是無符號數,所以最後得到的差值就是這段時間內計數器TCNT的增量.
distance = (17*error)/625; //單位為cm, 將上麵的最後兩句結合成一句,先乘後除就會減小誤差
但改後上麵distance = (17*error)/625; 這句就錯了,因為error的值可能很大,最大可以達到65535,所以17*error結果很有可能會超過65535,但這個處理器是16位的,也就是說這個處理器的數據寄存器為16位,最大的表示數值也就65535,所以17*error大於65535後就會被截斷存入寄存器中.也就是說存入寄存器中的值為(17*error)%65536,當再用這個值除以625時得到的很有可能就是0或者個位數的值,不管怎樣,此時得到的結果都是錯誤的值了!!
結合上麵兩種情況,最後我改成如下:
start = TCNT;// 計時開始, TCNT為16位的計時器寄存器
..............一段時間後(這段時間小於計時器TCNT從0計數到最大值65535所表示的時間)...........
end = TCNT; //計時結束
error = end - start; //注意,end有可能比start小,但由於都是無符號數,所以最後得到的差值就是這段時間內計數器TCNT的增量.
time = error/625; //單位為ms
remainder = error - time*625;//計算上一句中丟棄的餘數,沒有用remainder = error%625,是因為除法很耗時!!
distance = 17*time + (17*remainder + 312)/625; //單位為cm,此處的312(625/2)是考慮到四舍五入的.
特別推薦
- 噪聲中提取真值!瑞盟科技推出MSA2240電流檢測芯片賦能多元高端測量場景
- 10MHz高頻運行!氮矽科技發布集成驅動GaN芯片,助力電源能效再攀新高
- 失真度僅0.002%!力芯微推出超低內阻、超低失真4PST模擬開關
- 一“芯”雙電!聖邦微電子發布雙輸出電源芯片,簡化AFE與音頻設計
- 一機適配萬端:金升陽推出1200W可編程電源,賦能高端裝備製造
技術文章更多>>
- 築基AI4S:摩爾線程全功能GPU加速中國生命科學自主生態
- 一秒檢測,成本降至萬分之一,光引科技把幾十萬的台式光譜儀“搬”到了手腕上
- AI服務器電源機櫃Power Rack HVDC MW級測試方案
- 突破工藝邊界,奎芯科技LPDDR5X IP矽驗證通過,速率達9600Mbps
- 通過直接、準確、自動測量超低範圍的氯殘留來推動反滲透膜保護
技術白皮書下載更多>>
- 車規與基於V2X的車輛協同主動避撞技術展望
- 數字隔離助力新能源汽車安全隔離的新挑戰
- 汽車模塊拋負載的解決方案
- 車用連接器的安全創新應用
- Melexis Actuators Business Unit
- Position / Current Sensors - Triaxis Hall
熱門搜索
微波功率管
微波開關
微波連接器
微波器件
微波三極管
微波振蕩器
微電機
微調電容
微動開關
微蜂窩
位置傳感器
溫度保險絲
溫度傳感器
溫控開關
溫控可控矽
聞泰
穩壓電源
穩壓二極管
穩壓管
無焊端子
無線充電
無線監控
無源濾波器
五金工具
物聯網
顯示模塊
顯微鏡結構
線圈
線繞電位器
線繞電阻




