單片機(jī)
電腦(PC)通過串口向單片機(jī)發(fā)送浮點(diǎn)數(shù)(float、double)的方法version1.0maswellxiao 在實(shí)際應(yīng)用中我們可能會遇到這樣一個(gè)問題,PC機(jī)要向單片機(jī)發(fā)送一個(gè)浮點(diǎn)數(shù),float類型、double類型也好。maswell我曾經(jīng)做過AD轉(zhuǎn)換并發(fā)送數(shù)據(jù)到MATLAB中做FFT、濾波器等數(shù)字信號處理。處理完畢以后maswell我遇到一個(gè)非常棘手的問題……MATLAB數(shù)據(jù)處理完畢以后得到的數(shù)據(jù)類型是小數(shù)類型,就算忽略后幾位精度,最起碼也是一個(gè)float數(shù)據(jù)類型的數(shù),怎么把這個(gè)數(shù)發(fā)送到單片機(jī)呢? 經(jīng)過幾天的折騰。maswell我一不小心就實(shí)現(xiàn)PC通過串口向單片機(jī)發(fā)送浮點(diǎn)小數(shù)。下面把這種方法分享給大家。 首先我們需要一個(gè)預(yù)備知識。也就是float數(shù)據(jù)類型在單片機(jī)中的存儲方式。 float數(shù)據(jù)類型總共占據(jù)32個(gè)位bit,其中第一個(gè)位為數(shù)據(jù)符號(Symbol,在下面簡稱為S)該位表示數(shù)據(jù)的正負(fù)性。接下來8個(gè)位是階碼(Expoent,下面簡稱為E),這8個(gè)位表示浮點(diǎn)數(shù)的小數(shù)點(diǎn)的位置。最后有23位的尾數(shù)(mantissa,M),這23個(gè)位表示數(shù)據(jù)。下面做個(gè)示意圖 1位S 8個(gè)位E23個(gè)位M 例如:十進(jìn)制的數(shù)據(jù)Nfile:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image002.png將之換成二進(jìn)制表示file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image004.pngfile:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image006.pngfile:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image008.pngfile:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image010.png因此file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image012.png并由公式file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image014.png得file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image016.png組合起來就是 S EM0file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image018.pngfile:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image020.png4bit一格隔開 0100 001011110110111010010111100142F6E979因此十進(jìn)制浮點(diǎn)小數(shù)N = 123.456在單片機(jī)里存儲的數(shù)據(jù)為0x42F6E979
至此,我們的思路有了一點(diǎn)了。PC機(jī)向單片機(jī)發(fā)送十六進(jìn)制代碼0x42F6E979就意味著向單片機(jī)發(fā)送浮點(diǎn)小數(shù)123.456了。
下面是MATLAB向單片機(jī)發(fā)送數(shù)據(jù)的一段代碼
function pushbutton7_Callback(hObject, eventdata, handles)
% hObject handle to pushbutton7 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
global s;
global TxDataToMSP430;
TxDataHex = num2hex(single(TxDataToMSP430));
fwrite(s,'dcba','uchar');%協(xié)議密碼
t1 =clock;
while 1
if fread(s,1) == hex2dec('af');
break;
end
if etime(clock , t1) > 1%1秒鐘內(nèi)未收到數(shù)據(jù)則放棄
break ;
end
end
TxDataHexTemp(1) = TxDataHex(1);
TxDataHexTemp(2) = TxDataHex(2);
fwrite(s,base2dec(TxDataHexTemp,16),'uchar');%協(xié)議數(shù)據(jù)
我當(dāng)初做的是一個(gè)GUI界面,函數(shù)名是MATLAB自動(dòng)生成的,我的下位機(jī)用的是MSP430G2553單片機(jī)。編譯環(huán)境CCS5.2
volatile float DisplayData1,DisplayData2,DisplayData3,DisplayData4;
volatile long DisplayData;//接收到的數(shù)據(jù)轉(zhuǎn)換前數(shù)據(jù)
unsigned char TXVolFlag = 0;
static char FrameHeaderFlag = 1;//識別幀頭標(biāo)志位
static char StopTxFlag = 0;//上位機(jī)發(fā)送停止發(fā)送標(biāo)志
unsigned char ReadDataClassFlag = 0x00;//讀數(shù)據(jù)包格式
以上為全局變量。注意DisplayData為long數(shù)據(jù)類型
#pragma vector=USCIAB0RX_VECTOR
__interrupt void USCI0RX_ISR(void)
{
static char k = 0;
if(FrameHeaderFlag)
{
ReadDataClassFlag = IdentifyHeader();
if(ReadDataClassFlag != 0x00)//識別幀頭
FrameHeaderFlag = 0;
}
else//上一次讀到的temp不是0,說明握手成功
{
switch(ReadDataClassFlag)
{
case 0xaf:
case 0xbe:
case 0xcc:
case 0xdb:
switch(k)
{
case 0:
DisplayData = UCA0RXBUF;
DisplayData <<= 8;
k = 1;
break;
case 1:
DisplayData += UCA0RXBUF;
DisplayData <<= 8;
k = 2;
break;
case 2:
DisplayData += UCA0RXBUF;
DisplayData <<= 8;
k = 3;
break;
case 3:
DisplayData += UCA0RXBUF;
k = 0;
switch(ReadDataClassFlag)
{
///////////////////////////////////接收數(shù)據(jù),以下是接收數(shù)據(jù)的控制字
case 0xaf:DisplayData1 = *((float *)&DisplayData);break;
case 0xbe:DisplayData2 = *((float *)&DisplayData);break;
case 0xcc:DisplayData3 = *((float *)&DisplayData);break;
case 0xdb:DisplayData4 = *((float *)&DisplayData);break;
default:;
}
FrameHeaderFlag = 1;//幀數(shù)據(jù)接收完畢,32個(gè)bit數(shù)據(jù)為一幀
break;
default:;
}
break;
case 0xfa://停止發(fā)送的密碼
IdentifyHeader();
break;
default:;
}
}
}
我這個(gè)函數(shù)是MSP430G2553單片機(jī)的接受中斷函數(shù),單片機(jī)串口接收到數(shù)據(jù)后進(jìn)入中斷,首先判斷是不是協(xié)議的幀頭(我這里自行定義了一個(gè)協(xié)議)如果不是幀頭,則是數(shù)據(jù),進(jìn)入數(shù)據(jù)的判斷,由于PC機(jī)發(fā)送的是32位的數(shù)據(jù),而UART只支持接收8位的數(shù)據(jù),故此用了四個(gè)case語句判斷。接收完32位的數(shù)據(jù)以后,問題又來了,我們用來存儲接收來的數(shù)據(jù)是long類型(volatile long DisplayData;)的。而我們需要接收的數(shù)據(jù)是float數(shù)據(jù)類型的。故此還需要一個(gè)轉(zhuǎn)換。其實(shí)這個(gè)轉(zhuǎn)換很簡單。一個(gè)語句就可以了。
DisplayData1 = *((float *)&DisplayData);
首先將DisplayData的地址強(qiáng)制轉(zhuǎn)換為float數(shù)據(jù)類型的地址,然后再對地址取值就可以了。至此,就可以完整地將long數(shù)據(jù)類型轉(zhuǎn)換為float數(shù)據(jù)類型。
那么一整個(gè)過程就可以完整地實(shí)現(xiàn),從PC機(jī)發(fā)送1、0代碼到MSP430單片機(jī)接收為long數(shù)據(jù)類型,再由long數(shù)據(jù)類型強(qiáng)制轉(zhuǎn)換為float數(shù)據(jù)類型。
那么同樣道理,我們可以用同樣的方法將long long數(shù)據(jù)類型轉(zhuǎn)換為double數(shù)據(jù)類型。
我們每次接受一個(gè)float數(shù)據(jù),只需把這個(gè)數(shù)據(jù)從long轉(zhuǎn)換成float就行了,為什么這里要分四種情況
case 0xafisplayData1 = *((float *)&DisplayData);break;
case 0xbeisplayData2 = *((float *)&DisplayData);break;
case 0xccisplayData3 = *((float *)&DisplayData);break;
case 0xdbisplayData4 = *((float *)&DisplayData);break;