单片机
电脑(PC)通过串口向单片机发送浮点数(float、double)的方法version1.0maswellxiao 在实际应用中我们可能会遇到这样一个问题,PC机要向单片机发送一个浮点数,float类型、double类型也好。maswell我曾经做过AD转换并发送数据到MATLAB中做FFT、滤波器等数字信号处理。处理完毕以后maswell我遇到一个非常棘手的问题……MATLAB数据处理完毕以后得到的数据类型是小数类型,就算忽略后几位精度,最起码也是一个float数据类型的数,怎么把这个数发送到单片机呢? 经过几天的折腾。maswell我一不小心就实现PC通过串口向单片机发送浮点小数。下面把这种方法分享给大家。 首先我们需要一个预备知识。也就是float数据类型在单片机中的存储方式。 float数据类型总共占据32个位bit,其中第一个位为数据符号(Symbol,在下面简称为S)该位表示数据的正负性。接下来8个位是阶码(Expoent,下面简称为E),这8个位表示浮点数的小数点的位置。最后有23位的尾数(mantissa,M),这23个位表示数据。下面做个示意图 1位S 8个位E23个位M 例如:十进制的数据Nfile:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image002.png将之换成二进制表示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因此十进制浮点小数N = 123.456在单片机里存储的数据为0x42F6E979
至此,我们的思路有了一点了。PC机向单片机发送十六进制代码0x42F6E979就意味着向单片机发送浮点小数123.456了。
下面是MATLAB向单片机发送数据的一段代码
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');%协议密码
t1 =clock;
while 1
if fread(s,1) == hex2dec('af');
break;
end
if etime(clock , t1) > 1%1秒钟内未收到数据则放弃
break ;
end
end
TxDataHexTemp(1) = TxDataHex(1);
TxDataHexTemp(2) = TxDataHex(2);
fwrite(s,base2dec(TxDataHexTemp,16),'uchar');%协议数据
我当初做的是一个GUI界面,函数名是MATLAB自动生成的,我的下位机用的是MSP430G2553单片机。编译环境CCS5.2
volatile float DisplayData1,DisplayData2,DisplayData3,DisplayData4;
volatile long DisplayData;//接收到的数据转换前数据
unsigned char TXVolFlag = 0;
static char FrameHeaderFlag = 1;//识别帧头标志位
static char StopTxFlag = 0;//上位机发送停止发送标志
unsigned char ReadDataClassFlag = 0x00;//读数据包格式
以上为全局变量。注意DisplayData为long数据类型
#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)
{
///////////////////////////////////接收数据,以下是接收数据的控制字
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;//帧数据接收完毕,32个bit数据为一帧
break;
default:;
}
break;
case 0xfa://停止发送的密码
IdentifyHeader();
break;
default:;
}
}
}
我这个函数是MSP430G2553单片机的接受中断函数,单片机串口接收到数据后进入中断,首先判断是不是协议的帧头(我这里自行定义了一个协议)如果不是帧头,则是数据,进入数据的判断,由于PC机发送的是32位的数据,而UART只支持接收8位的数据,故此用了四个case语句判断。接收完32位的数据以后,问题又来了,我们用来存储接收来的数据是long类型(volatile long DisplayData;)的。而我们需要接收的数据是float数据类型的。故此还需要一个转换。其实这个转换很简单。一个语句就可以了。
DisplayData1 = *((float *)&DisplayData);
首先将DisplayData的地址强制转换为float数据类型的地址,然后再对地址取值就可以了。至此,就可以完整地将long数据类型转换为float数据类型。
那么一整个过程就可以完整地实现,从PC机发送1、0代码到MSP430单片机接收为long数据类型,再由long数据类型强制转换为float数据类型。
那么同样道理,我们可以用同样的方法将long long数据类型转换为double数据类型。
我们每次接受一个float数据,只需把这个数据从long转换成float就行了,为什么这里要分四种情况
case 0xafisplayData1 = *((float *)&DisplayData);break;
case 0xbeisplayData2 = *((float *)&DisplayData);break;
case 0xccisplayData3 = *((float *)&DisplayData);break;
case 0xdbisplayData4 = *((float *)&DisplayData);break;